pidgin/pidgin

Merged pidgin/main into default

2016-10-30, Arkadiy Illarionov
bea4cc95b40f
Merged pidgin/main into default
  • +5 -3
    Makefile.am
  • +12 -2
    bitbucket-pipelines.yml
  • +55 -60
    configure.ac
  • +0 -1
    doc/reference/libpurple/libpurple-docs.xml
  • +0 -1
    doc/reference/protocols/facebook/Makefile.am
  • +21 -4
    libpurple/Makefile.am
  • +0 -1
    libpurple/Makefile.mingw
  • +0 -1
    libpurple/connection.c
  • +0 -2
    libpurple/core.c
  • +22 -0
    libpurple/dbus-define-api.h
  • +22 -0
    libpurple/dbus-maybe.h
  • +22 -0
    libpurple/dbus-useful.c
  • +22 -0
    libpurple/dbus-useful.h
  • +174 -59
    libpurple/http.c
  • +0 -25
    libpurple/internal.h
  • +119 -97
    libpurple/plugins/keyrings/secretservice.c
  • +2 -1
    libpurple/protocols/bonjour/Makefile.am
  • +1 -1
    libpurple/protocols/facebook/Makefile.am
  • +2 -2
    libpurple/protocols/facebook/facebook.c
  • +193 -135
    libpurple/protocols/facebook/mqtt.c
  • +0 -17
    libpurple/protocols/facebook/mqtt.h
  • +1 -57
    libpurple/protocols/gg/Makefile.am
  • +0 -504
    libpurple/protocols/gg/lib/COPYING
  • +0 -882
    libpurple/protocols/gg/lib/common.c
  • +0 -93
    libpurple/protocols/gg/lib/config.h
  • +0 -1410
    libpurple/protocols/gg/lib/dcc.c
  • +0 -1660
    libpurple/protocols/gg/lib/dcc7.c
  • +0 -397
    libpurple/protocols/gg/lib/debug.c
  • +0 -27
    libpurple/protocols/gg/lib/debug.h
  • +0 -231
    libpurple/protocols/gg/lib/deflate.c
  • +0 -29
    libpurple/protocols/gg/lib/deflate.h
  • +0 -272
    libpurple/protocols/gg/lib/encoding.c
  • +0 -28
    libpurple/protocols/gg/lib/encoding.h
  • +0 -109
    libpurple/protocols/gg/lib/endian.c
  • +0 -1901
    libpurple/protocols/gg/lib/events.c
  • +0 -63
    libpurple/protocols/gg/lib/fileio.h
  • +0 -2881
    libpurple/protocols/gg/lib/handlers.c
  • +0 -592
    libpurple/protocols/gg/lib/http.c
  • +0 -266
    libpurple/protocols/gg/lib/internal.h
  • +0 -3082
    libpurple/protocols/gg/lib/libgadu.c
  • +0 -2639
    libpurple/protocols/gg/lib/libgadu.h
  • +0 -1016
    libpurple/protocols/gg/lib/message.c
  • +0 -62
    libpurple/protocols/gg/lib/message.h
  • +0 -310
    libpurple/protocols/gg/lib/network.c
  • +0 -125
    libpurple/protocols/gg/lib/network.h
  • +0 -250
    libpurple/protocols/gg/lib/obsolete.c
  • +0 -2603
    libpurple/protocols/gg/lib/packets.pb-c.c
  • +0 -766
    libpurple/protocols/gg/lib/packets.pb-c.h
  • +0 -3288
    libpurple/protocols/gg/lib/protobuf-c.c
  • +0 -1078
    libpurple/protocols/gg/lib/protobuf-c.h
  • +0 -153
    libpurple/protocols/gg/lib/protobuf.c
  • +0 -70
    libpurple/protocols/gg/lib/protobuf.h
  • +0 -411
    libpurple/protocols/gg/lib/protocol.h
  • +0 -888
    libpurple/protocols/gg/lib/pubdir.c
  • +0 -555
    libpurple/protocols/gg/lib/pubdir50.c
  • +0 -1132
    libpurple/protocols/gg/lib/resolver.c
  • +0 -28
    libpurple/protocols/gg/lib/resolver.h
  • +0 -67
    libpurple/protocols/gg/lib/session.h
  • +0 -382
    libpurple/protocols/gg/lib/sha1.c
  • +0 -43
    libpurple/protocols/gg/lib/strman.h
  • +0 -607
    libpurple/protocols/gg/lib/tvbuff.c
  • +0 -59
    libpurple/protocols/gg/lib/tvbuff.h
  • +0 -426
    libpurple/protocols/gg/lib/tvbuilder.c
  • +0 -50
    libpurple/protocols/gg/lib/tvbuilder.h
  • +160 -30
    libpurple/protocols/gg/tcpsocket.c
  • +11 -0
    libpurple/protocols/irc/msgs.c
  • +1 -7
    libpurple/protocols/jabber/Makefile.am
  • +0 -2
    libpurple/protocols/jabber/Makefile.mingw
  • +0 -21
    libpurple/protocols/jabber/iq.c
  • +0 -141
    libpurple/protocols/jabber/win32/posix.uname.c
  • +0 -24
    libpurple/protocols/jabber/win32/utsname.h
  • +1 -0
    libpurple/protocols/yahoo/Makefile.am
  • +22 -0
    libpurple/purple-client-example.c
  • +22 -0
    libpurple/purple-client.c
  • +22 -0
    libpurple/purple-client.h
  • +10 -6
    libpurple/purple-gio.c
  • +0 -410
    libpurple/purple-socket.c
  • +0 -217
    libpurple/purple-socket.h
  • +22 -0
    libpurple/tests/test_des.c
  • +22 -0
    libpurple/tests/test_des3.c
  • +22 -0
    libpurple/tests/test_hmac.c
  • +22 -0
    libpurple/tests/test_md4.c
  • +22 -0
    libpurple/tests/test_md5.c
  • +22 -0
    libpurple/tests/test_sha1.c
  • +22 -0
    libpurple/tests/test_sha256.c
  • +22 -0
    libpurple/tests/test_util.c
  • +22 -0
    libpurple/tests/test_xmlnode.c
  • +2 -2
    pidgin/data/pidgin-3-uninstalled.pc.in
  • +15 -39
    pidgin/gtkutils.c
  • +1 -8
    pidgin/gtkwebviewtoolbar.c
  • +26 -1
    pidgin/libpidgin.c
  • +0 -1
    po/POTFILES.in
  • +1 -0
    po/POTFILES.skip
  • --- a/Makefile.am Wed Oct 26 10:17:10 2016 +0000
    +++ b/Makefile.am Sun Oct 30 12:03:44 2016 +0300
    @@ -14,6 +14,8 @@
    m4macros/introspection.m4 \
    package_revision.h \
    po/Makefile.mingw \
    + tap-driver.sh \
    + tap-test \
    valgrind-suppressions
    noinst_HEADERS = config.h package_revision.h
    @@ -27,6 +29,9 @@
    $(AM_V_at)if [ ! -f "$(top_builddir)libpurple/libpurple.la" ]; then \
    $(MAKE) -C libpurple libpurple.la; \
    fi
    + $(AM_V_at)if [ ! -f "$(top_builddir)libpurple/protocols/facebook/libfacebook.la" ]; then \
    + $(MAKE) -C libpurple/protocols/facebook libfacebook.la; \
    + fi
    if ENABLE_GNT
    $(AM_V_at)if [ ! -f "$(top_builddir)finch/libgnt/libgnt.la" ]; then \
    $(MAKE) -C finch/libgnt libgnt.la; \
    @@ -46,9 +51,6 @@
    dist-hook:
    rm $(distdir)/config.h
    -distcheck-hook: libpurple/plugins/perl/common/Purple.pm pidgin/plugins/perl/common/Pidgin.pm
    -# cp libpurple/plugins/perl/common/Gaim.pm $(distdir)/libpurple/plugins/perl/common
    -
    commit-check:
    (cd po ; intltool-update -m 2>&1 | grep -v '^mismatched quotes.*\.py$$' | sed "s|current directory|po directory|" | grep . ; if [ $$? = 0 ] ; then exit 1 ; else exit 0 ; fi)
    --- a/bitbucket-pipelines.yml Wed Oct 26 10:17:10 2016 +0000
    +++ b/bitbucket-pipelines.yml Sun Oct 30 12:03:44 2016 +0300
    @@ -5,7 +5,17 @@
    - step:
    script:
    - set -ex
    - - ./autogen.sh --disable-kwallet --enable-debug --enable-gtk-doc
    + - ./autogen.sh --enable-debug --enable-gtk-doc
    - make -s -j$(nproc)
    - - make -s -j$(nproc) check
    + - make -s -j$(nproc) distcheck
    + branches:
    + release-2.x.y:
    + - step:
    + image: pidgin/release-builder:release-2.x.y
    + script:
    + - set -ex
    + - ./autogen.sh --enable-debug
    + - make -s -j$(nproc)
    + - make -s -j$(nproc) check
    + - make distcheck
    --- a/configure.ac Wed Oct 26 10:17:10 2016 +0000
    +++ b/configure.ac Sun Oct 30 12:03:44 2016 +0300
    @@ -155,9 +155,9 @@
    AC_SUBST([PLUGIN_LDFLAGS])
    dnl Define *_LIBS
    -PURPLE_LIBS="\$(top_builddir)/libpurple/libpurple.la \$(GLIB_LIBS)"
    -PIDGIN_LIBS="\$(top_builddir)/pidgin/libpidgin.la \$(GTK_LIBS)"
    -FINCH_LIBS="\$(top_builddir)/finch/libfinch.la \$(top_builddir)/finch/libgnt/libgnt.la \$(GLIB_LIBS)"
    +PURPLE_LIBS="\$(top_builddir)/libpurple/libpurple.la \$(GLIB_LIBS) \$(GPLUGIN_LIBS)"
    +PIDGIN_LIBS="\$(top_builddir)/pidgin/libpidgin.la \$(GTK_LIBS) \$(PURPLE_LIBS) \$(WEBKIT_LIBS)"
    +FINCH_LIBS="\$(top_builddir)/finch/libfinch.la \$(top_builddir)/finch/libgnt/libgnt.la \$(PURPLE_LIBS)"
    AC_SUBST(PURPLE_LIBS)
    AC_SUBST(PIDGIN_LIBS)
    AC_SUBST(FINCH_LIBS)
    @@ -1181,58 +1181,38 @@
    dnl #######################################################################
    dnl # Check for Gadu-Gadu protocol library (libgadu)
    dnl #######################################################################
    -
    -PKG_CHECK_MODULES(LIBGADU, [libgadu >= 1.12.0], [have_libgadu=yes], [have_libgadu=no])
    +AC_ARG_ENABLE(libgadu,
    + [AS_HELP_STRING([--disable-libgadu],
    + [compile without libgadu (required for GaduGadu support)])],
    + enable_libgadu="$enableval", enable_libgadu="yes")
    -if test "x$have_libgadu" = "xyes"; then
    - AC_CHECK_LIB(gadu, gg_is_gpl_compliant, , [
    - LIBGADU_LIBS=""
    - LIBGADU_CFLAGS=""
    - have_libgadu=no
    - AC_MSG_WARN([
    +if test "x$enable_libgadu" = "xyes"; then
    + PKG_CHECK_MODULES(LIBGADU, [libgadu >= 1.12.0], [
    + have_libgadu=yes
    + AC_CHECK_LIB(gadu, gg_is_gpl_compliant, , [
    + LIBGADU_LIBS=""
    + LIBGADU_CFLAGS=""
    + have_libgadu=no
    + if test "x$force_deps" = "xyes" ; then
    + AC_MSG_WARN([
    libgadu is not compatible with the GPL when compiled with OpenSSL support.
    -To compile against system libgadu, please recompile libgadu using:
    +To link against libgadu, please recompile it using:
    ./configure --with-openssl=no
    Then rerun this ./configure
    -
    -Falling back to using our own copy of libgadu.
    - ])
    + ])
    + fi
    + ], [$LIBGADU_LIBS])
    + ], [
    + have_libgadu=no
    ])
    -fi
    -
    -AM_CONDITIONAL(HAVE_LIBGADU, test "x$have_libgadu" = "xyes")
    -if test "x$have_libgadu" = "xyes"; then
    - AC_DEFINE(HAVE_LIBGADU, 1, [Linked with external libgadu])
    -else
    - AC_CHECK_LIB(gnutls, gnutls_certificate_set_x509_system_trust, [gg_have_gnutls_csxst=yes], [gg_have_gnutls_csxst=no])
    - gg_gnutls_sts=""
    - if test "x$gg_have_gnutls_csxst" = "xno"; then
    - for i in /etc/ssl/ca-bundle.pem \
    - /etc/ssl/certs/ca-certificates.crt \
    - /etc/pki/tls/cert.pem \
    - /usr/local/share/certs/ca-root-nss.crt \
    - /etc/ssl/cert.pem
    - do
    - if test -e $i; then
    - gg_gnutls_sts="$i"
    - break
    - fi
    - done
    + if test "x$have_libgadu" != "xyes" -a "x$force_deps" = "xyes" ; then
    + AC_MSG_ERROR([
    +Libgadu development headers not found.
    +Use --disable-libgadu if you do not need GG (GaduGadu) support.
    +])
    fi
    -
    - if test "x$gg_have_gnutls_csxst" = "xyes"; then
    - AC_DEFINE(HAVE_GNUTLS_CERTIFICATE_SET_X509_SYSTEM_TRUST, 1, [gnutls contains the gnutls_certificate_set_x509_system_trust function])
    - fi
    - if test "x$gg_gnutls_sts" != "x"; then
    - AC_DEFINE_UNQUOTED(GG_CONFIG_GNUTLS_SYSTEM_TRUST_STORE, ["$gg_gnutls_sts"], [use the given file as GnuTLS default trust store])
    - fi
    -
    - dnl # redundant - only here to stay compatible with libgadu upstream
    - PKG_CHECK_MODULES([GNUTLS_2_10], [gnutls >= 2.10.0], [
    - AC_DEFINE([HAVE_GNUTLS_2_10], [], [Defined if GnuTLS >= 2.10.0 is available.])
    - ],:)
    fi
    AC_SUBST(LIBGADU_LIBS)
    @@ -1259,6 +1239,9 @@
    if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then
    STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc//'`
    fi
    +if test "x$have_libgadu" != "xyes" ; then
    + STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/gg//'`
    +fi
    if test "x$is_win32" = "xyes" ; then
    STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/zephyr//'`
    fi
    @@ -1333,6 +1316,9 @@
    if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then
    DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc//'`
    fi
    +if test "x$have_libgadu" != "xyes"; then
    + DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/gg//'`
    +fi
    if test "x$is_win32" = "xyes" ; then
    DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/zephyr//'`
    fi
    @@ -1536,19 +1522,29 @@
    dnl # Check for Secret Service headers
    dnl #######################################################################
    -# disabled - see secretservice.c
    -#AC_ARG_ENABLE(libsecret, [AC_HELP_STRING([--disable-libsecret], [enable Secret Service support])], enable_secret_service=no, enable_secret_service=yes)
    +AC_ARG_ENABLE(libsecret,
    + [AC_HELP_STRING([--disable-libsecret], [enable Secret Service support])],
    + enable_secret_service="$enableval", enable_secret_service="$is_not_win32")
    -#if test "x$enable_secret_service" = "xyes" ; then
    -# PKG_CHECK_MODULES(SECRETSERVICE, [libsecret-1], [
    -# AC_SUBST(SECRETSERVICE_CFLAGS)
    -# AC_SUBST(SECRETSERVICE_LIBS)
    -# AC_DEFINE(HAVE_SECRETSERVICE, 1, [Define if we have Secret Service.])
    -# ])
    -#fi
    +if test "x$enable_secret_service" = "xyes" ; then
    + PKG_CHECK_MODULES(SECRETSERVICE, [libsecret-1], [
    + AC_SUBST(SECRETSERVICE_CFLAGS)
    + AC_SUBST(SECRETSERVICE_LIBS)
    + AC_DEFINE(HAVE_SECRETSERVICE, 1, [Define if we have Secret Service.])
    + ], [
    + AC_MSG_RESULT(no)
    + enable_secret_service="no"
    + if test "x$force_deps" = "xyes" ; then
    + AC_MSG_ERROR([
    +Libsecret development headers not found
    +Use --disable-libsecret if you do not need it.
    +])
    + fi
    + ])
    -#AM_CONDITIONAL(ENABLE_SECRETSERVICE, test "x$enable_secret_service" = "xyes")
    -AM_CONDITIONAL(ENABLE_SECRETSERVICE, test "x1" = "x2")
    +fi
    +
    +AM_CONDITIONAL(ENABLE_SECRETSERVICE, test "x$enable_secret_service" = "xyes")
    dnl #######################################################################
    dnl # Check for GNOME Keyring headers
    @@ -2254,7 +2250,6 @@
    echo Build with Cyrus SASL support. : $enable_cyrus_sasl
    echo Use kerberos 4 with zephyr.... : $kerberos
    echo Use external libzephyr........ : $zephyr
    -echo Use external libgadu.......... : $have_libgadu
    echo Install pixmaps............... : $enable_pixmaps
    echo Old tray icon compatibility... : $enable_traycompat
    echo Install translations.......... : $enable_i18n
    @@ -2269,7 +2264,7 @@
    echo
    echo Build with GNOME Keyring...... : $enable_gnome_keyring
    echo Build with KWallet............ : $enable_kwallet
    -#echo Build with Secret Service..... : $enable_secret_service
    +echo Build with Secret Service..... : $enable_secret_service
    echo
    echo Build with plugin support..... : $enable_plugins
    echo Enable Introspection...........: $enable_introspection
    --- a/doc/reference/libpurple/libpurple-docs.xml Wed Oct 26 10:17:10 2016 +0000
    +++ b/doc/reference/libpurple/libpurple-docs.xml Sun Oct 30 12:03:44 2016 +0300
    @@ -51,7 +51,6 @@
    <xi:include href="xml/eventloop.xml" />
    <xi:include href="xml/group.xml" />
    <xi:include href="xml/xfer.xml" />
    - <xi:include href="xml/purple-socket.xml" />
    <xi:include href="xml/http.xml" />
    <xi:include href="xml/idle.xml" />
    <xi:include href="xml/keyring.xml" />
    --- a/doc/reference/protocols/facebook/Makefile.am Wed Oct 26 10:17:10 2016 +0000
    +++ b/doc/reference/protocols/facebook/Makefile.am Sun Oct 30 12:03:44 2016 +0300
    @@ -106,7 +106,6 @@
    # Other files to distribute
    # e.g. EXTRA_DIST += version.xml.in
    -EXTRA_DIST += version.xml.in
    # Files not to distribute
    # for --rebuild-types in $(SCAN_OPTIONS), e.g. $(DOC_MODULE).types
    --- a/libpurple/Makefile.am Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/Makefile.am Sun Oct 30 12:03:44 2016 +0300
    @@ -94,7 +94,6 @@
    protocol.c \
    protocols.c \
    purple-gio.c \
    - purple-socket.c \
    queuedoutputstream.c \
    request.c \
    request-datasheet.c \
    @@ -179,7 +178,6 @@
    protocol.h \
    protocols.h \
    purple-gio.h \
    - purple-socket.h \
    queuedoutputstream.h \
    request.h \
    request-datasheet.h \
    @@ -284,11 +282,16 @@
    $(AM_V_GEN)echo "#include \"marshallers.h\"" > marshallers.c
    $(AM_V_at)$(GLIB_GENMARSHAL) --prefix=purple_smarshal $(srcdir)/marshallers.list --body >> marshallers.c
    +# glib-mkenums doesn't handle VPATHs well, sed is used here to fix the
    +# include paths and documentation.
    +# Escaping possible . in $(srcdir) to \. for sed pattern.
    enums.h: $(srcdir)/enums.h.in $(purple_enumheaders)
    $(AM_V_GEN)$(GLIB_MKENUMS) --template $(srcdir)/enums.h.in $(purple_enumheaders) > $@
    + sed -i 's,$(subst .,\.,$(srcdir))/,,g' $@
    enums.c: $(srcdir)/enums.c.in $(purple_enumheaders)
    $(AM_V_GEN)$(GLIB_MKENUMS) --template $(srcdir)/enums.c.in $(purple_enumheaders) > $@
    + sed -i 's,$(subst .,\.,$(srcdir))/,,g' $@
    CLEANFILES = \
    enums.c \
    @@ -296,6 +299,9 @@
    marshallers.c \
    marshallers.h
    +DISTCLEANFILES = \
    + data/purple-url-handler.desktop
    +
    if ENABLE_DBUS
    CLEANFILES += \
    @@ -320,10 +326,14 @@
    prefs.h presence.h roomlist.h savedstatuses.h smiley.h smiley-list.h \
    status.h server.h util.h xmlnode.h protocol.h protocols.h
    +# Filter out enums.h and marshallers.h as they don't actually generate
    +# anything. This way making from a VPATH and distcheck doesn't fail due
    +# to not being able to find the enums.h or marshallers.h files if
    +# they've already been generated in the $(srcdir).
    purple_build_coreheaders = $(addprefix $(srcdir)/, $(purple_coreheaders)) \
    $(addprefix $(srcdir)/ciphers/, $(purple_cipherheaders)) \
    $(addprefix $(srcdir)/media/, $(purple_mediaheaders)) \
    - $(purple_builtheaders)
    + $(filter-out: enums.h marshallers.h, $(purple_builtheaders))
    dbus_build_exported = $(addprefix $(srcdir)/, $(dbus_exported))
    # We should probably make this better
    dbus_signals = $(addprefix $(srcdir)/, $(purple_coresources)) \
    @@ -344,7 +354,10 @@
    dbus-server.$(OBJEXT): dbus-bindings.c dbus-signals.c dbus-types.c dbus-types.h
    dbus-server.lo: dbus-bindings.c dbus-signals.c dbus-types.c dbus-types.h
    -$(libpurple_la_OBJECTS): dbus-types.h
    +
    +# Declare these as dependencies so they're built even if `make distcheck`
    +# is run immediately after configuring.
    +$(libpurple_la_OBJECTS): dbus-types.h enums.h marshallers.h
    # libpurple-client
    @@ -395,6 +408,10 @@
    BUILT_SOURCES = $(purple_builtheaders) $(purple_builtsources)
    +# Declare these as dependencies so they're built even if `make distcheck`
    +# is run immediately after configuring.
    +$(libpurple_la_OBJECTS): enums.h marshallers.h
    +
    endif
    lib_LTLIBRARIES = libpurple.la $(libpurple_client_lib)
    --- a/libpurple/Makefile.mingw Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/Makefile.mingw Sun Oct 30 12:03:44 2016 +0300
    @@ -115,7 +115,6 @@
    proxy.c \
    protocol.c \
    protocols.c \
    - purple-socket.c \
    request.c \
    request-datasheet.c \
    roomlist.c \
    --- a/libpurple/connection.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/connection.c Sun Oct 30 12:03:44 2016 +0300
    @@ -845,7 +845,6 @@
    }
    purple_http_conn_cancel_all(gc);
    - _purple_socket_cancel_with_connection(gc);
    purple_proxy_connect_cancel_with_handle(gc);
    connections = g_list_remove(connections, gc);
    --- a/libpurple/core.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/core.c Sun Oct 30 12:03:44 2016 +0300
    @@ -190,7 +190,6 @@
    purple_log_init();
    purple_network_init();
    purple_pounces_init();
    - _purple_socket_init();
    purple_proxy_init();
    purple_sound_init();
    purple_stun_init();
    @@ -254,7 +253,6 @@
    purple_theme_manager_uninit();
    purple_xfers_uninit();
    purple_proxy_uninit();
    - _purple_socket_uninit();
    _purple_image_store_uninit();
    purple_network_uninit();
    --- a/libpurple/dbus-define-api.h Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/dbus-define-api.h Sun Oct 30 12:03:44 2016 +0300
    @@ -1,3 +1,25 @@
    +/*
    + * purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    #ifndef __GI_SCANNER__ /* hide this file from g-ir-scanner */
    #error "This file is not a valid C code and is not intended to be compiled."
    --- a/libpurple/dbus-maybe.h Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/dbus-maybe.h Sun Oct 30 12:03:44 2016 +0300
    @@ -1,3 +1,25 @@
    +/*
    + * purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    #ifndef _PURPLE_DBUS_MAYBE_H_
    #define _PURPLE_DBUS_MAYBE_H_
    /**
    --- a/libpurple/dbus-useful.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/dbus-useful.c Sun Oct 30 12:03:44 2016 +0300
    @@ -1,3 +1,25 @@
    +/*
    + * purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    #include <string.h>
    #include <glib.h>
    --- a/libpurple/dbus-useful.h Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/dbus-useful.h Sun Oct 30 12:03:44 2016 +0300
    @@ -1,3 +1,25 @@
    +/*
    + * purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    #ifndef _PURPLE_DBUS_USEFUL_H_
    #define _PURPLE_DBUS_USEFUL_H_
    /**
    --- a/libpurple/http.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/http.c Sun Oct 30 12:03:44 2016 +0300
    @@ -28,7 +28,7 @@
    #include "debug.h"
    #include "ntlm.h"
    #include "proxy.h"
    -#include "purple-socket.h"
    +#include "purple-gio.h"
    #include <zlib.h>
    #ifndef z_const
    @@ -57,9 +57,15 @@
    typedef struct _PurpleHttpGzStream PurpleHttpGzStream;
    +typedef void (*PurpleHttpSocketConnectCb)(PurpleHttpSocket *hs,
    + const gchar *error, gpointer _hc);
    +
    struct _PurpleHttpSocket
    {
    - PurpleSocket *ps;
    + GSocketConnection *conn;
    + GCancellable *cancellable;
    + guint input_source;
    + guint output_source;
    gboolean is_busy;
    guint use_count;
    @@ -175,7 +181,7 @@
    struct _PurpleHttpKeepaliveRequest
    {
    PurpleConnection *gc;
    - PurpleSocketConnectCb cb;
    + PurpleHttpSocketConnectCb cb;
    gpointer user_data;
    PurpleHttpKeepaliveHost *host;
    @@ -247,7 +253,7 @@
    static PurpleHttpKeepaliveRequest *
    purple_http_keepalive_pool_request(PurpleHttpKeepalivePool *pool,
    PurpleConnection *gc, const gchar *host, int port, gboolean is_ssl,
    - PurpleSocketConnectCb cb, gpointer user_data);
    + PurpleHttpSocketConnectCb cb, gpointer user_data);
    static void
    purple_http_keepalive_pool_request_cancel(PurpleHttpKeepaliveRequest *req);
    static void
    @@ -463,23 +469,69 @@
    return g_strdup_printf("%c:%s:%d", (is_ssl ? 'S' : 'R'), host, port);
    }
    +static void
    +purple_http_socket_connect_new_cb(GObject *source, GAsyncResult *res,
    + gpointer user_data)
    +{
    + PurpleHttpSocket *hs = user_data;
    + GSocketConnection *conn;
    + PurpleHttpSocketConnectCb cb;
    + gpointer cb_data;
    + GError *error = NULL;
    +
    + conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
    + res, &error);
    +
    + cb = g_object_steal_data(source, "cb");
    + cb_data = g_object_steal_data(source, "cb_data");
    +
    + if (conn == NULL) {
    + if (!g_error_matches(error,
    + G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    + cb(hs, error->message, cb_data);
    + }
    +
    + g_clear_error(&error);
    + return;
    + }
    +
    + hs->conn = conn;
    +
    + cb(hs, NULL, cb_data);
    +}
    +
    static PurpleHttpSocket *
    purple_http_socket_connect_new(PurpleConnection *gc, const gchar *host,
    - int port, gboolean is_ssl, PurpleSocketConnectCb cb, gpointer user_data)
    + int port, gboolean is_ssl,
    + PurpleHttpSocketConnectCb cb, gpointer user_data)
    {
    - PurpleHttpSocket *hs = g_new0(PurpleHttpSocket, 1);
    -
    - hs->ps = purple_socket_new(gc);
    - purple_socket_set_data(hs->ps, "hs", hs);
    - purple_socket_set_tls(hs->ps, is_ssl);
    - purple_socket_set_host(hs->ps, host);
    - purple_socket_set_port(hs->ps, port);
    - if (!purple_socket_connect(hs->ps, cb, user_data)) {
    - purple_socket_destroy(hs->ps);
    - g_free(hs);
    + PurpleHttpSocket *hs;
    + GSocketClient *client;
    + GError *error = NULL;
    +
    + client = purple_gio_socket_client_new(
    + purple_connection_get_account(gc), &error);
    +
    + if (client == NULL) {
    + purple_debug_error("http", "Error connecting to '%s:%d': %s",
    + host, port, error->message);
    + g_clear_error(&error);
    return NULL;
    }
    + hs = g_new0(PurpleHttpSocket, 1);
    + hs->cancellable = g_cancellable_new();
    +
    + g_socket_client_set_tls(client, is_ssl);
    + g_object_set_data(G_OBJECT(client), "cb", cb);
    + g_object_set_data(G_OBJECT(client), "cb_data", user_data);
    +
    + g_socket_client_connect_to_host_async(client,
    + host, port, hs->cancellable,
    + purple_http_socket_connect_new_cb, hs);
    +
    + g_object_unref(client);
    +
    if (purple_debug_is_verbose())
    purple_debug_misc("http", "new socket created: %p\n", hs);
    @@ -495,7 +547,26 @@
    if (purple_debug_is_verbose())
    purple_debug_misc("http", "destroying socket: %p\n", hs);
    - purple_socket_destroy(hs->ps);
    + if (hs->input_source > 0) {
    + g_source_remove(hs->input_source);
    + hs->input_source = 0;
    + }
    +
    + if (hs->output_source > 0) {
    + g_source_remove(hs->output_source);
    + hs->output_source = 0;
    + }
    +
    + if (hs->cancellable != NULL) {
    + g_cancellable_cancel(hs->cancellable);
    + g_clear_object(&hs->cancellable);
    + }
    +
    + if (hs->conn != NULL) {
    + purple_gio_graceful_close(G_IO_STREAM(hs->conn), NULL, NULL);
    + g_clear_object(&hs->conn);
    + }
    +
    g_free(hs);
    }
    @@ -682,10 +753,9 @@
    gboolean is_graceful);
    static void _purple_http_gen_headers(PurpleHttpConnection *hc);
    -static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc, gint fd);
    -static void _purple_http_recv(gpointer _hc, gint fd,
    - PurpleInputCondition cond);
    -static void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond);
    +static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc);
    +static gboolean _purple_http_recv(GObject *source, gpointer _hc);
    +static gboolean _purple_http_send(GObject *source, gpointer _hc);
    /* closes current connection (if exists), estabilishes one and proceeds with
    * request */
    @@ -1077,21 +1147,33 @@
    return _purple_http_recv_body_data(hc, buf, len);
    }
    -static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc, gint fd)
    +static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc)
    {
    int len;
    gchar buf[4096];
    gboolean got_anything;
    -
    - len = purple_socket_read(hc->socket->ps, (guchar*)buf, sizeof(buf));
    + GError *error = NULL;
    +
    + len = g_pollable_input_stream_read_nonblocking(
    + G_POLLABLE_INPUT_STREAM(
    + g_io_stream_get_input_stream(
    + G_IO_STREAM(hc->socket->conn))),
    + buf, sizeof(buf), hc->socket->cancellable,
    + &error);
    got_anything = (len > 0);
    - if (len < 0 && errno == EAGAIN)
    + if (len < 0 && (g_error_matches(error,
    + G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) ||
    + g_error_matches(error,
    + G_IO_ERROR, G_IO_ERROR_CANCELLED))) {
    + g_clear_error(&error);
    return FALSE;
    + }
    if (len < 0) {
    _purple_http_error(hc, _("Error reading from %s: %s"),
    - hc->url->host, g_strerror(errno));
    + hc->url->host, error->message);
    + g_clear_error(&error);
    return FALSE;
    }
    @@ -1270,11 +1352,13 @@
    return got_anything;
    }
    -static void _purple_http_recv(gpointer _hc, gint fd, PurpleInputCondition cond)
    +static gboolean _purple_http_recv(GObject *source, gpointer _hc)
    {
    PurpleHttpConnection *hc = _hc;
    - while (_purple_http_recv_loopbody(hc, fd));
    + while (_purple_http_recv_loopbody(hc));
    +
    + return G_SOURCE_CONTINUE;
    }
    static void _purple_http_send_got_data(PurpleHttpConnection *hc,
    @@ -1305,17 +1389,19 @@
    hc->request->contents_length = estimated_length;
    }
    -static void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond)
    +static gboolean _purple_http_send(GObject *source, gpointer _hc)
    {
    PurpleHttpConnection *hc = _hc;
    int written, write_len;
    const gchar *write_from;
    gboolean writing_headers;
    + GError *error = NULL;
    + GSource *gsource;
    /* Waiting for data. This could be written more efficiently, by removing
    * (and later, adding) hs->inpa. */
    if (hc->contents_reader_requested)
    - return;
    + return G_SOURCE_CONTINUE;
    _purple_http_gen_headers(hc);
    @@ -1328,7 +1414,7 @@
    hc->request_header_written;
    } else if (hc->request->contents_reader) {
    if (hc->contents_reader_requested)
    - return; /* waiting for data */
    + return G_SOURCE_CONTINUE; /* waiting for data */
    if (!hc->contents_reader_buffer)
    hc->contents_reader_buffer = g_string_new("");
    if (hc->contents_reader_buffer->len == 0) {
    @@ -1341,7 +1427,7 @@
    PURPLE_HTTP_MAX_READ_BUFFER_LEN,
    hc->request->contents_reader_data,
    _purple_http_send_got_data);
    - return;
    + return G_SOURCE_CONTINUE;
    }
    write_from = hc->contents_reader_buffer->str;
    write_len = hc->contents_reader_buffer->len;
    @@ -1356,12 +1442,21 @@
    purple_debug_warning("http", "Nothing to write\n");
    written = 0;
    } else {
    - written = purple_socket_write(hc->socket->ps,
    - (const guchar*)write_from, write_len);
    + written = g_pollable_output_stream_write_nonblocking(
    + G_POLLABLE_OUTPUT_STREAM(
    + g_io_stream_get_output_stream(
    + G_IO_STREAM(hc->socket->conn))),
    + write_from, write_len, hc->socket->cancellable,
    + &error);
    }
    - if (written < 0 && errno == EAGAIN)
    - return;
    + if (written < 0 && (g_error_matches(error,
    + G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) ||
    + g_error_matches(error,
    + G_IO_ERROR, G_IO_ERROR_CANCELLED))) {
    + g_clear_error(&error);
    + return G_SOURCE_CONTINUE;
    + }
    if (written < 0) {
    if (hc->request_header_written == 0 &&
    @@ -1370,21 +1465,22 @@
    purple_debug_info("http", "Keep-alive connection "
    "expired (when writing), retrying...\n");
    purple_http_conn_retry(hc);
    - return;
    + } else {
    + _purple_http_error(hc, _("Error writing to %s: %s"),
    + hc->url->host, error->message);
    }
    - _purple_http_error(hc, _("Error writing to %s: %s"),
    - hc->url->host, g_strerror(errno));
    - return;
    + g_clear_error(&error);
    + return G_SOURCE_CONTINUE;
    }
    if (writing_headers) {
    hc->request_header_written += written;
    purple_http_conn_notify_progress_watcher(hc);
    if (hc->request_header_written < hc->request_header->len)
    - return;
    + return G_SOURCE_CONTINUE;
    if (hc->request->contents_length > 0)
    - return;
    + return G_SOURCE_CONTINUE;
    } else {
    hc->request_contents_written += written;
    purple_http_conn_notify_progress_watcher(hc);
    @@ -1394,14 +1490,24 @@
    hc->request_contents_written <
    (guint)hc->request->contents_length)
    {
    - return;
    + return G_SOURCE_CONTINUE;
    }
    }
    /* request is completely written, let's read the response */
    hc->is_reading = TRUE;
    - purple_socket_watch(hc->socket->ps, PURPLE_INPUT_READ,
    - _purple_http_recv, hc);
    + gsource = g_pollable_input_stream_create_source(
    + G_POLLABLE_INPUT_STREAM(
    + g_io_stream_get_input_stream(
    + G_IO_STREAM(hc->socket->conn))),
    + NULL);
    + g_source_set_callback(gsource,
    + (GSourceFunc)_purple_http_recv, hc, NULL);
    + hc->socket->input_source = g_source_attach(gsource, NULL);
    + g_source_unref(gsource);
    +
    + hc->socket->output_source = 0;
    + return G_SOURCE_REMOVE;
    }
    static void _purple_http_disconnect(PurpleHttpConnection *hc,
    @@ -1426,13 +1532,10 @@
    }
    static void
    -_purple_http_connected(PurpleSocket *ps, const gchar *error, gpointer _hc)
    +_purple_http_connected(PurpleHttpSocket *hs, const gchar *error, gpointer _hc)
    {
    - PurpleHttpSocket *hs = NULL;
    PurpleHttpConnection *hc = _hc;
    -
    - if (ps != NULL)
    - hs = purple_socket_get_data(ps, "hs");
    + GSource *source;
    hc->socket_request = NULL;
    hc->socket = hs;
    @@ -1443,7 +1546,14 @@
    return;
    }
    - purple_socket_watch(ps, PURPLE_INPUT_WRITE, _purple_http_send, hc);
    + source = g_pollable_output_stream_create_source(
    + G_POLLABLE_OUTPUT_STREAM(
    + g_io_stream_get_output_stream(G_IO_STREAM(hs->conn))),
    + NULL);
    + g_source_set_callback(source,
    + (GSourceFunc)_purple_http_send, hc, NULL);
    + hc->socket->output_source = g_source_attach(source, NULL);
    + g_source_unref(source);
    }
    static gboolean _purple_http_reconnect(PurpleHttpConnection *hc)
    @@ -2155,7 +2265,7 @@
    static PurpleHttpKeepaliveRequest *
    purple_http_keepalive_pool_request(PurpleHttpKeepalivePool *pool,
    PurpleConnection *gc, const gchar *host, int port, gboolean is_ssl,
    - PurpleSocketConnectCb cb, gpointer user_data)
    + PurpleHttpSocketConnectCb cb, gpointer user_data)
    {
    PurpleHttpKeepaliveRequest *req;
    PurpleHttpKeepaliveHost *kahost;
    @@ -2198,19 +2308,15 @@
    }
    static void
    -_purple_http_keepalive_socket_connected(PurpleSocket *ps,
    +_purple_http_keepalive_socket_connected(PurpleHttpSocket *hs,
    const gchar *error, gpointer _req)
    {
    - PurpleHttpSocket *hs = NULL;
    PurpleHttpKeepaliveRequest *req = _req;
    - if (ps != NULL)
    - hs = purple_socket_get_data(ps, "hs");
    -
    if (hs != NULL)
    hs->use_count++;
    - req->cb(ps, error, req->user_data);
    + req->cb(hs, error, req->user_data);
    g_free(req);
    }
    @@ -2266,7 +2372,7 @@
    purple_http_keepalive_host_process_queue(host);
    - req->cb(hs->ps, NULL, req->user_data);
    + req->cb(hs, NULL, req->user_data);
    g_free(req);
    return FALSE;
    @@ -2337,7 +2443,16 @@
    if (purple_debug_is_verbose())
    purple_debug_misc("http", "releasing a socket: %p\n", hs);
    - purple_socket_watch(hs->ps, 0, NULL, NULL);
    + if (hs->input_source > 0) {
    + g_source_remove(hs->input_source);
    + hs->input_source = 0;
    + }
    +
    + if (hs->output_source > 0) {
    + g_source_remove(hs->output_source);
    + hs->output_source = 0;
    + }
    +
    hs->is_busy = FALSE;
    host = hs->host;
    --- a/libpurple/internal.h Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/internal.h Sun Oct 30 12:03:44 2016 +0300
    @@ -360,31 +360,6 @@
    _purple_fstat(int fd, GStatBuf *st);
    /**
    - * _purple_socket_cancel_with_connection:
    - * @gc The connection.
    - *
    - * Cancels all #PurpleSocket instances bound with @gc.
    - */
    -void
    -_purple_socket_cancel_with_connection(PurpleConnection *gc);
    -
    -/**
    - * _purple_socket_init: (skip)
    - *
    - * Initializes the #PurpleSocket subsystem.
    - */
    -void
    -_purple_socket_init(void);
    -
    -/**
    - * _purple_socket_uninit: (skip)
    - *
    - * Uninitializes the #PurpleSocket subsystem.
    - */
    -void
    -_purple_socket_uninit(void);
    -
    -/**
    * _purple_message_init: (skip)
    *
    * Initializes the #PurpleMessage subsystem.
    --- a/libpurple/plugins/keyrings/secretservice.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/plugins/keyrings/secretservice.c Sun Oct 30 12:03:44 2016 +0300
    @@ -21,17 +21,15 @@
    * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    */
    -#error "This keyring needs some more work (see TODO)"
    -
    /* TODO
    *
    - * This keyring needs some more work, so it will be disabled until its quality
    - * was raised. Some of the pain points:
    - * - throws a lot of g_warnings
    - * - it doesn't notify about some backend faults (like access denied), some of
    - * them are not handled at all
    - * - it could use libsecret's Complete API
    - * - code formatting could be better
    + * This keyring now works (at the time of this writing), but there are
    + * some inconvenient edge cases. When looking up passwords, libsecret
    + * doesn't error if the keyring is locked. Therefore, it appears to
    + * this plugin that there's no stored password. libpurple seems to
    + * handle this as gracefully as possible, but it's still inconvenient.
    + * This plugin could possibly be ported to use libsecret's "Complete API"
    + * to resolve this if desired.
    */
    #include "internal.h"
    @@ -53,6 +51,7 @@
    #define SECRETSERVICE_DOMAIN (g_quark_from_static_string(SECRETSERVICE_ID))
    static PurpleKeyring *keyring_handler = NULL;
    +static GCancellable *keyring_cancellable = NULL;
    static const SecretSchema purple_schema = {
    "im.pidgin.Purple", SECRET_SCHEMA_NONE,
    @@ -78,6 +77,62 @@
    /* Keyring interface */
    /***********************************************/
    static void
    +ss_g_error_to_keyring_error(GError **error, PurpleAccount *account)
    +{
    + GError *old_error;
    + GError *new_error = NULL;
    +
    + g_return_if_fail(error != NULL);
    +
    + old_error = *error;
    +
    + if (g_error_matches(old_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    + new_error = g_error_new(PURPLE_KEYRING_ERROR,
    + PURPLE_KEYRING_ERROR_CANCELLED,
    + _("Operation cancelled."));
    + } else if (g_error_matches(old_error, G_DBUS_ERROR,
    + G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND) ||
    + g_error_matches(old_error, G_DBUS_ERROR,
    + G_DBUS_ERROR_IO_ERROR)) {
    + purple_debug_info("keyring-libsecret",
    + "Failed to communicate with Secret "
    + "Service (account: %s (%s)).\n",
    + purple_account_get_username(account),
    + purple_account_get_protocol_id(account));
    + new_error = g_error_new(PURPLE_KEYRING_ERROR,
    + PURPLE_KEYRING_ERROR_BACKENDFAIL,
    + "Failed to communicate with Secret "
    + "Service (account: %s).",
    + purple_account_get_username(account));
    + } else if (g_error_matches(old_error, SECRET_ERROR,
    + SECRET_ERROR_IS_LOCKED)) {
    + purple_debug_info("keyring-libsecret",
    + "Secret Service is locked (account: %s (%s)).\n",
    + purple_account_get_username(account),
    + purple_account_get_protocol_id(account));
    + new_error = g_error_new(PURPLE_KEYRING_ERROR,
    + PURPLE_KEYRING_ERROR_ACCESSDENIED,
    + "Secret Service is locked (account: %s).",
    + purple_account_get_username(account));
    + } else {
    + purple_debug_error("keyring-libsecret",
    + "Unknown error (account: %s (%s), "
    + "domain: %s, code: %d): %s.\n",
    + purple_account_get_username(account),
    + purple_account_get_protocol_id(account),
    + g_quark_to_string(old_error->domain),
    + old_error->code, old_error->message);
    + new_error = g_error_new(PURPLE_KEYRING_ERROR,
    + PURPLE_KEYRING_ERROR_BACKENDFAIL,
    + "Unknown error (account: %s).",
    + purple_account_get_username(account));
    + }
    +
    + g_clear_error(error);
    + g_propagate_error(error, new_error);
    +}
    +
    +static void
    ss_read_continue(GObject *object, GAsyncResult *result, gpointer data)
    {
    InfoStorage *storage = data;
    @@ -89,53 +144,19 @@
    password = secret_password_lookup_finish(result, &error);
    if (error != NULL) {
    - int code = error->code;
    -
    - switch (code) {
    - case G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND:
    - case G_DBUS_ERROR_IO_ERROR:
    - error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_BACKENDFAIL,
    - "Failed to communicate with Secret "
    - "Service (account : %s).",
    - purple_account_get_username(account));
    - if (cb != NULL)
    - cb(account, NULL, error, storage->user_data);
    - g_error_free(error);
    - break;
    -
    - default:
    - purple_debug_error("keyring-libsecret",
    - "Unknown error (account: %s (%s), "
    - "domain: %s, code: %d): %s.\n",
    - purple_account_get_username(account),
    - purple_account_get_protocol_id(account),
    - g_quark_to_string(error->domain), code,
    - error->message);
    - error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_BACKENDFAIL,
    - "Unknown error (account : %s).",
    - purple_account_get_username(account));
    - if (cb != NULL)
    - cb(account, NULL, error, storage->user_data);
    - g_error_free(error);
    - break;
    - }
    -
    + ss_g_error_to_keyring_error(&error, account);
    } else if (password == NULL) {
    error = g_error_new(PURPLE_KEYRING_ERROR,
    PURPLE_KEYRING_ERROR_NOPASSWORD,
    "No password found for account: %s",
    purple_account_get_username(account));
    - if (cb != NULL)
    - cb(account, NULL, error, storage->user_data);
    - g_error_free(error);
    -
    - } else {
    - if (cb != NULL)
    - cb(account, password, NULL, storage->user_data);
    }
    + if (cb != NULL) {
    + cb(account, password, error, storage->user_data);
    + }
    +
    + g_clear_error(&error);
    g_free(storage);
    }
    @@ -148,74 +169,57 @@
    storage->cb = cb;
    storage->user_data = data;
    - secret_password_lookup(&purple_schema, NULL, ss_read_continue, storage,
    + secret_password_lookup(&purple_schema, keyring_cancellable,
    + ss_read_continue, storage,
    "user", purple_account_get_username(account),
    "protocol", purple_account_get_protocol_id(account), NULL);
    }
    static void
    -ss_save_continue(GObject *object, GAsyncResult *result, gpointer data)
    +ss_save_continue(GError *error, gpointer data)
    {
    InfoStorage *storage = data;
    PurpleKeyringSaveCallback cb;
    - GError *error = NULL;
    PurpleAccount *account;
    account = storage->account;
    cb = storage->cb;
    - secret_password_store_finish(result, &error);
    -
    if (error != NULL) {
    - int code = error->code;
    -
    - switch (code) {
    - case G_DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND:
    - case G_DBUS_ERROR_IO_ERROR:
    - purple_debug_info("keyring-libsecret",
    - "Failed to communicate with Secret "
    - "Service (account : %s (%s)).\n",
    - purple_account_get_username(account),
    - purple_account_get_protocol_id(account));
    - error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_BACKENDFAIL,
    - "Failed to communicate with Secret Service (account : %s).",
    - purple_account_get_username(account));
    - if (cb != NULL)
    - cb(account, error, storage->user_data);
    - g_error_free(error);
    - break;
    -
    - default:
    - purple_debug_error("keyring-libsecret",
    - "Unknown error (account: %s (%s), "
    - "domain: %s, code: %d): %s.\n",
    - purple_account_get_username(account),
    - purple_account_get_protocol_id(account),
    - g_quark_to_string(error->domain), code,
    - error->message);
    - error = g_error_new(PURPLE_KEYRING_ERROR,
    - PURPLE_KEYRING_ERROR_BACKENDFAIL,
    - "Unknown error (account : %s).",
    - purple_account_get_username(account));
    - if (cb != NULL)
    - cb(account, error, storage->user_data);
    - g_error_free(error);
    - break;
    - }
    -
    + ss_g_error_to_keyring_error(&error, account);
    } else {
    purple_debug_info("keyring-libsecret", "Password for %s updated.\n",
    purple_account_get_username(account));
    -
    - if (cb != NULL)
    - cb(account, NULL, storage->user_data);
    }
    + if (cb != NULL)
    + cb(account, error, storage->user_data);
    +
    + g_clear_error(&error);
    g_free(storage);
    }
    static void
    +ss_store_continue(GObject *object, GAsyncResult *result, gpointer data)
    +{
    + GError *error = NULL;
    +
    + secret_password_store_finish(result, &error);
    +
    + ss_save_continue(error, data);
    +}
    +
    +static void
    +ss_clear_continue(GObject *object, GAsyncResult *result, gpointer data)
    +{
    + GError *error = NULL;
    +
    + secret_password_clear_finish(result, &error);
    +
    + ss_save_continue(error, data);
    +}
    +
    +static void
    ss_save(PurpleAccount *account,
    const gchar *password,
    PurpleKeyringSaveCallback cb,
    @@ -237,7 +241,8 @@
    label = g_strdup_printf(_("Pidgin IM password for account %s"), username);
    secret_password_store(&purple_schema, SECRET_COLLECTION_DEFAULT,
    - label, password, NULL, ss_save_continue, storage,
    + label, password, keyring_cancellable,
    + ss_store_continue, storage,
    "user", username,
    "protocol", purple_account_get_protocol_id(account),
    NULL);
    @@ -249,27 +254,42 @@
    purple_account_get_username(account),
    purple_account_get_protocol_id(account));
    - secret_password_clear(&purple_schema, NULL, ss_save_continue,
    - storage, "user", purple_account_get_username(account),
    + secret_password_clear(&purple_schema, keyring_cancellable,
    + ss_clear_continue, storage,
    + "user", purple_account_get_username(account),
    "protocol", purple_account_get_protocol_id(account),
    NULL);
    }
    }
    static void
    +ss_cancel(void)
    +{
    + g_cancellable_cancel(keyring_cancellable);
    +
    + /* Swap out cancelled cancellable for new one for further operations */
    + g_clear_object(&keyring_cancellable);
    + keyring_cancellable = g_cancellable_new();
    +}
    +
    +static void
    ss_close(void)
    {
    + ss_cancel();
    }
    static gboolean
    ss_init(GError **error)
    {
    + keyring_cancellable = g_cancellable_new();
    +
    keyring_handler = purple_keyring_new();
    purple_keyring_set_name(keyring_handler, _(SECRETSERVICE_NAME));
    purple_keyring_set_id(keyring_handler, SECRETSERVICE_ID);
    purple_keyring_set_read_password(keyring_handler, ss_read);
    purple_keyring_set_save_password(keyring_handler, ss_save);
    + purple_keyring_set_cancel_requests(keyring_handler, ss_cancel);
    purple_keyring_set_close_keyring(keyring_handler, ss_close);
    purple_keyring_register(keyring_handler);
    @@ -284,6 +304,8 @@
    purple_keyring_unregister(keyring_handler);
    purple_keyring_free(keyring_handler);
    keyring_handler = NULL;
    +
    + g_clear_object(&keyring_cancellable);
    }
    /***********************************************/
    --- a/libpurple/protocols/bonjour/Makefile.am Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/protocols/bonjour/Makefile.am Sun Oct 30 12:03:44 2016 +0300
    @@ -1,5 +1,6 @@
    EXTRA_DIST = \
    mdns_avahi.c \
    + mdns_dns_sd.c \
    dns_sd_proxy.c \
    dns_sd_proxy.h \
    Makefile.mingw
    @@ -23,7 +24,7 @@
    bonjour_ft.h
    if IS_WIN32
    -BONJOURSOURCES += dns_sd_proxy.c
    +BONJOURSOURCES += dns_sd_proxy.c mdns_dns_sd.c
    else
    BONJOURSOURCES += mdns_avahi.c
    endif
    --- a/libpurple/protocols/facebook/Makefile.am Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/protocols/facebook/Makefile.am Sun Oct 30 12:03:44 2016 +0300
    @@ -52,7 +52,7 @@
    st =
    pkg_LTLIBRARIES = libfacebook.la
    libfacebook_la_SOURCES = $(FACEBOOKSOURCES)
    -libfacebook_la_LIBADD = @PURPLE_LIBS@
    +libfacebook_la_LIBADD = @PURPLE_LIBS@ $(JSON_LIBS)
    endif
    --- a/libpurple/protocols/facebook/facebook.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/protocols/facebook/facebook.c Sun Oct 30 12:03:44 2016 +0300
    @@ -332,8 +332,8 @@
    gc = fb_data_get_connection(fata);
    - if (error->domain == FB_MQTT_SSL_ERROR) {
    - purple_connection_ssl_error(gc, error->code);
    + if (error->domain == G_IO_ERROR) {
    + purple_connection_g_error(gc, error);
    return;
    }
    --- a/libpurple/protocols/facebook/mqtt.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/protocols/facebook/mqtt.c Sun Oct 30 12:03:44 2016 +0300
    @@ -28,7 +28,8 @@
    #include "account.h"
    #include "eventloop.h"
    #include "glibcompat.h"
    -#include "sslconn.h"
    +#include "purple-gio.h"
    +#include "queuedoutputstream.h"
    #include "marshal.h"
    #include "mqtt.h"
    @@ -37,17 +38,17 @@
    struct _FbMqttPrivate
    {
    PurpleConnection *gc;
    - PurpleSslConnection *gsc;
    + GIOStream *conn;
    + GBufferedInputStream *input;
    + PurpleQueuedOutputStream *output;
    + GCancellable *cancellable;
    gboolean connected;
    guint16 mid;
    GByteArray *rbuf;
    - GByteArray *wbuf;
    gsize remz;
    gint tev;
    - gint rev;
    - gint wev;
    };
    struct _FbMqttMessagePrivate
    @@ -65,6 +66,8 @@
    G_DEFINE_TYPE(FbMqtt, fb_mqtt, G_TYPE_OBJECT);
    G_DEFINE_TYPE(FbMqttMessage, fb_mqtt_message, G_TYPE_OBJECT);
    +static void fb_mqtt_read_packet(FbMqtt *mqtt);
    +
    static void
    fb_mqtt_dispose(GObject *obj)
    {
    @@ -73,7 +76,6 @@
    fb_mqtt_close(mqtt);
    g_byte_array_free(priv->rbuf, TRUE);
    - g_byte_array_free(priv->wbuf, TRUE);
    }
    static void
    @@ -161,7 +163,6 @@
    mqtt->priv = priv;
    priv->rbuf = g_byte_array_new();
    - priv->wbuf = g_byte_array_new();
    }
    static void
    @@ -205,18 +206,6 @@
    return q;
    }
    -GQuark
    -fb_mqtt_ssl_error_quark(void)
    -{
    - static GQuark q = 0;
    -
    - if (G_UNLIKELY(q == 0)) {
    - q = g_quark_from_static_string("fb-mqtt-ssl-error-quark");
    - }
    -
    - return q;
    -}
    -
    FbMqtt *
    fb_mqtt_new(PurpleConnection *gc)
    {
    @@ -240,33 +229,47 @@
    g_return_if_fail(FB_IS_MQTT(mqtt));
    priv = mqtt->priv;
    - if (priv->wev > 0) {
    - purple_input_remove(priv->wev);
    - priv->wev = 0;
    - }
    -
    - if (priv->rev > 0) {
    - purple_input_remove(priv->rev);
    - priv->rev = 0;
    - }
    -
    if (priv->tev > 0) {
    purple_timeout_remove(priv->tev);
    priv->tev = 0;
    }
    - if (priv->gsc != NULL) {
    - purple_ssl_close(priv->gsc);
    - priv->gsc = NULL;
    + if (priv->cancellable != NULL) {
    + g_cancellable_cancel(priv->cancellable);
    + g_clear_object(&priv->cancellable);
    }
    - if (priv->wbuf->len > 0) {
    - fb_util_debug_warning("Closing with unwritten data");
    + if (priv->conn != NULL) {
    + purple_gio_graceful_close(priv->conn,
    + G_INPUT_STREAM(priv->input),
    + G_OUTPUT_STREAM(priv->output));
    + g_clear_object(&priv->input);
    + g_clear_object(&priv->output);
    + g_clear_object(&priv->conn);
    }
    priv->connected = FALSE;
    g_byte_array_set_size(priv->rbuf, 0);
    - g_byte_array_set_size(priv->wbuf, 0);
    +}
    +
    +static void
    +fb_mqtt_take_error(FbMqtt *mqtt, GError *err, const gchar *prefix)
    +{
    + if (g_error_matches(err, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    + /* Return as cancelled means the connection is closing */
    + g_error_free(err);
    + return;
    + }
    +
    + /* Now we can check for programming errors */
    + g_return_if_fail(FB_IS_MQTT(mqtt));
    +
    + if (prefix != NULL) {
    + g_prefix_error(&err, "%s: ", prefix);
    + }
    +
    + g_signal_emit_by_name(mqtt, "error", err);
    + g_error_free(err);
    }
    void
    @@ -344,74 +347,130 @@
    }
    static void
    -fb_mqtt_cb_read(gpointer data, gint fd, PurpleInputCondition cond)
    +fb_mqtt_cb_fill(GObject *source, GAsyncResult *res, gpointer data)
    +{
    + GBufferedInputStream *input = G_BUFFERED_INPUT_STREAM(source);
    + FbMqtt *mqtt = data;
    + gssize ret;
    + GError *err = NULL;
    +
    + ret = g_buffered_input_stream_fill_finish(input, res, &err);
    +
    + if (ret < 1) {
    + if (ret == 0) {
    + err = g_error_new_literal(G_IO_ERROR,
    + G_IO_ERROR_CONNECTION_CLOSED,
    + _("Connection closed"));
    + }
    +
    + fb_mqtt_take_error(mqtt, err, _("Failed to read fixed header"));
    + return;
    + }
    +
    + fb_mqtt_read_packet(mqtt);
    +}
    +
    +static void
    +fb_mqtt_cb_read_packet(GObject *source, GAsyncResult *res, gpointer data)
    {
    FbMqtt *mqtt = data;
    + FbMqttPrivate *priv;
    + gssize ret;
    FbMqttMessage *msg;
    - FbMqttPrivate *priv = mqtt->priv;
    - gint res;
    - guint mult;
    - guint8 buf[1024];
    - guint8 byte;
    - gsize size;
    - gssize rize;
    + GError *err = NULL;
    - if (priv->remz < 1) {
    - /* Reset the read buffer */
    - g_byte_array_set_size(priv->rbuf, 0);
    + ret = g_input_stream_read_finish(G_INPUT_STREAM(source), res, &err);
    - res = purple_ssl_read(priv->gsc, &byte, sizeof byte);
    - g_byte_array_append(priv->rbuf, &byte, sizeof byte);
    -
    - if (res != sizeof byte) {
    - fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL,
    - _("Failed to read fixed header"));
    - return;
    + if (ret < 1) {
    + if (ret == 0) {
    + err = g_error_new_literal(G_IO_ERROR,
    + G_IO_ERROR_CONNECTION_CLOSED,
    + _("Connection closed"));
    }
    - mult = 1;
    -
    - do {
    - res = purple_ssl_read(priv->gsc, &byte, sizeof byte);
    - g_byte_array_append(priv->rbuf, &byte, sizeof byte);
    + fb_mqtt_take_error(mqtt, err, _("Failed to read packet data"));
    + return;
    + }
    - if (res != sizeof byte) {
    - fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL,
    - _("Failed to read packet size"));
    - return;
    - }
    -
    - priv->remz += (byte & 127) * mult;
    - mult *= 128;
    - } while ((byte & 128) != 0);
    - }
    + priv = mqtt->priv;
    + priv->remz -= ret;
    if (priv->remz > 0) {
    - size = MIN(priv->remz, sizeof buf);
    - rize = purple_ssl_read(priv->gsc, buf, size);
    + g_input_stream_read_async(G_INPUT_STREAM(source),
    + priv->rbuf->data +
    + priv->rbuf->len - priv->remz, priv->remz,
    + G_PRIORITY_DEFAULT, priv->cancellable,
    + fb_mqtt_cb_read_packet, mqtt);
    + return;
    + }
    - if (rize < 1) {
    - fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL,
    - _("Failed to read packet data"));
    - return;
    - }
    + msg = fb_mqtt_message_new_bytes(priv->rbuf);
    - g_byte_array_append(priv->rbuf, buf, rize);
    - priv->remz -= rize;
    + if (G_UNLIKELY(msg == NULL)) {
    + fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL,
    + _("Failed to parse message"));
    + return;
    }
    - if (priv->remz < 1) {
    - msg = fb_mqtt_message_new_bytes(priv->rbuf);
    - priv->remz = 0;
    + fb_mqtt_read(mqtt, msg);
    + g_object_unref(msg);
    +
    + /* Read another packet if connection wasn't reset in fb_mqtt_read() */
    + if (fb_mqtt_connected(mqtt, FALSE)) {
    + fb_mqtt_read_packet(mqtt);
    + }
    +}
    - if (G_UNLIKELY(msg == NULL)) {
    - fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL,
    - _("Failed to parse message"));
    - return;
    +static void
    +fb_mqtt_read_packet(FbMqtt *mqtt)
    +{
    + FbMqttPrivate *priv = mqtt->priv;
    + const guint8 const *buf;
    + gsize count = 0;
    + gsize pos;
    + guint mult = 1;
    + guint8 byte;
    + gsize size = 0;
    +
    + buf = g_buffered_input_stream_peek_buffer(priv->input, &count);
    +
    + /* Start at 1 to skip the first byte */
    + pos = 1;
    +
    + do {
    + if (pos >= count) {
    + /* Not enough data yet, try again later */
    + size = 0;
    + break;
    }
    - fb_mqtt_read(mqtt, msg);
    - g_object_unref(msg);
    + byte = *(buf + pos++);
    +
    + size += (byte & 127) * mult;
    + mult *= 128;
    + } while ((byte & 128) != 0);
    +
    + if (size > 0) {
    + /* Add header to size */
    + size += pos;
    +
    + g_byte_array_set_size(priv->rbuf, size);
    + priv->remz = size;
    +
    + /* TODO: Use g_input_stream_read_all_async() when available. */
    + /* TODO: Alternately, it would be nice to let the
    + * FbMqttMessage directly use the GBufferedInputStream
    + * buffer instead of copying it, provided it's consumed
    + * before the next read.
    + */
    + g_input_stream_read_async(G_INPUT_STREAM(priv->input),
    + priv->rbuf->data, priv->rbuf->len,
    + G_PRIORITY_DEFAULT, priv->cancellable,
    + fb_mqtt_cb_read_packet, mqtt);
    + } else {
    + g_buffered_input_stream_fill_async(priv->input, -1,
    + G_PRIORITY_DEFAULT, priv->cancellable,
    + fb_mqtt_cb_fill, mqtt);
    }
    }
    @@ -519,27 +578,16 @@
    }
    static void
    -fb_mqtt_cb_write(gpointer data, gint fd, PurpleInputCondition cond)
    +fb_mqtt_cb_flush(GObject *source, GAsyncResult *res, gpointer data)
    {
    FbMqtt *mqtt = data;
    - FbMqttPrivate *priv = mqtt->priv;
    - gssize wize;
    + GError *err = NULL;
    - wize = purple_ssl_write(priv->gsc, priv->wbuf->data, priv->wbuf->len);
    -
    - if (wize < 0) {
    - fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL,
    - _("Failed to write data"));
    + if (!g_output_stream_flush_finish(G_OUTPUT_STREAM(source),
    + res, &err)) {
    + fb_mqtt_take_error(mqtt, err, _("Failed to write data"));
    return;
    }
    -
    - if (wize > 0) {
    - g_byte_array_remove_range(priv->wbuf, 0, wize);
    - }
    -
    - if (priv->wbuf->len < 1) {
    - priv->wev = 0;
    - }
    }
    void
    @@ -548,6 +596,7 @@
    const GByteArray *bytes;
    FbMqttMessagePrivate *mriv;
    FbMqttPrivate *priv;
    + GBytes *gbytes;
    g_return_if_fail(FB_IS_MQTT(mqtt));
    g_return_if_fail(FB_IS_MQTT_MESSAGE(msg));
    @@ -566,46 +615,46 @@
    "Writing %d (flags: 0x%0X)",
    mriv->type, mriv->flags);
    - g_byte_array_append(priv->wbuf, bytes->data, bytes->len);
    - fb_mqtt_cb_write(mqtt, priv->gsc->fd, PURPLE_INPUT_WRITE);
    + /* TODO: Would be nice to refactor this to not require copying bytes */
    + gbytes = g_bytes_new(bytes->data, bytes->len);
    + purple_queued_output_stream_push_bytes(priv->output, gbytes);
    + g_bytes_unref(gbytes);
    - if (priv->wev > 0) {
    - priv->wev = purple_input_add(priv->gsc->fd,
    - PURPLE_INPUT_WRITE,
    - fb_mqtt_cb_write, mqtt);
    + if (!g_output_stream_has_pending(G_OUTPUT_STREAM(priv->output))) {
    + g_output_stream_flush_async(G_OUTPUT_STREAM(priv->output),
    + G_PRIORITY_DEFAULT, priv->cancellable,
    + fb_mqtt_cb_flush, mqtt);
    }
    }
    static void
    -fb_mqtt_cb_open(gpointer data, PurpleSslConnection *ssl,
    - PurpleInputCondition cond)
    +fb_mqtt_cb_open(GObject *source, GAsyncResult *res, gpointer data)
    {
    FbMqtt *mqtt = data;
    - FbMqttPrivate *priv = mqtt->priv;
    + FbMqttPrivate *priv;
    + GSocketConnection *conn;
    + GError *err = NULL;
    +
    + conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
    + res, &err);
    +
    + if (conn == NULL) {
    + fb_mqtt_take_error(mqtt, err, NULL);
    + return;
    + }
    fb_mqtt_timeout_clear(mqtt);
    - priv->rev = purple_input_add(priv->gsc->fd, PURPLE_INPUT_READ,
    - fb_mqtt_cb_read, mqtt);
    - g_signal_emit_by_name(mqtt, "open");
    -}
    -static void
    -fb_mqtt_cb_open_error(PurpleSslConnection *ssl, PurpleSslErrorType error,
    - gpointer data)
    -{
    - const gchar *str;
    - FbMqtt *mqtt = data;
    - FbMqttPrivate *priv = mqtt->priv;
    - GError *err;
    + priv = mqtt->priv;
    + priv->conn = G_IO_STREAM(conn);
    + priv->input = G_BUFFERED_INPUT_STREAM(g_buffered_input_stream_new(
    + g_io_stream_get_input_stream(priv->conn)));
    + priv->output = purple_queued_output_stream_new(
    + g_io_stream_get_output_stream(priv->conn));
    - str = purple_ssl_strerror(error);
    - err = g_error_new_literal(FB_MQTT_SSL_ERROR, error, str);
    + fb_mqtt_read_packet(mqtt);
    - /* Do not call purple_ssl_close() from the error_func */
    - priv->gsc = NULL;
    -
    - g_signal_emit_by_name(mqtt, "error", err);
    - g_error_free(err);
    + g_signal_emit_by_name(mqtt, "open");
    }
    void
    @@ -613,20 +662,29 @@
    {
    FbMqttPrivate *priv;
    PurpleAccount *acc;
    + GSocketClient *client;
    + GError *err = NULL;
    g_return_if_fail(FB_IS_MQTT(mqtt));
    priv = mqtt->priv;
    acc = purple_connection_get_account(priv->gc);
    fb_mqtt_close(mqtt);
    - priv->gsc = purple_ssl_connect(acc, host, port, fb_mqtt_cb_open,
    - fb_mqtt_cb_open_error, mqtt);
    +
    + client = purple_gio_socket_client_new(acc, &err);
    - if (priv->gsc == NULL) {
    - fb_mqtt_cb_open_error(NULL, 0, mqtt);
    + if (client == NULL) {
    + fb_mqtt_take_error(mqtt, err, NULL);
    return;
    }
    + priv->cancellable = g_cancellable_new();
    +
    + g_socket_client_set_tls(client, TRUE);
    + g_socket_client_connect_to_host_async(client, host, port,
    + priv->cancellable, fb_mqtt_cb_open, mqtt);
    + g_object_unref(client);
    +
    fb_mqtt_timeout(mqtt);
    }
    @@ -662,7 +720,7 @@
    g_return_val_if_fail(FB_IS_MQTT(mqtt), FALSE);
    priv = mqtt->priv;
    - connected = (priv->gsc != NULL) && priv->connected;
    + connected = (priv->conn != NULL) && priv->connected;
    if (!connected && error) {
    fb_mqtt_error(mqtt, FB_MQTT_ERROR_GENERAL,
    --- a/libpurple/protocols/facebook/mqtt.h Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/protocols/facebook/mqtt.h Sun Oct 30 12:03:44 2016 +0300
    @@ -107,13 +107,6 @@
    */
    #define FB_MQTT_ERROR fb_mqtt_error_quark()
    -/**
    - * FB_MQTT_SSL_ERROR:
    - *
    - * The #GQuark of the domain of MQTT SSL errors.
    - */
    -#define FB_MQTT_SSL_ERROR fb_mqtt_ssl_error_quark()
    -
    typedef struct _FbMqtt FbMqtt;
    typedef struct _FbMqttClass FbMqttClass;
    typedef struct _FbMqttPrivate FbMqttPrivate;
    @@ -298,16 +291,6 @@
    fb_mqtt_error_quark(void);
    /**
    - * fb_mqtt_ssl_error_quark:
    - *
    - * Gets the #GQuark of the domain of MQTT SSL errors.
    - *
    - * Returns: The #GQuark of the domain.
    - */
    -GQuark
    -fb_mqtt_ssl_error_quark(void);
    -
    -/**
    * fb_mqtt_new:
    * @gc: The #PurpleConnection.
    *
    --- a/libpurple/protocols/gg/Makefile.am Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/protocols/gg/Makefile.am Sun Oct 30 12:03:44 2016 +0300
    @@ -3,64 +3,9 @@
    EXTRA_DIST = \
    account.c \
    account.h \
    - Makefile.mingw \
    - lib/COPYING
    -
    -if ! HAVE_LIBGADU
    -INTGG_SOURCES = \
    - lib/common.c \
    - lib/config.h \
    - lib/dcc.c \
    - lib/dcc7.c \
    - lib/debug.c \
    - lib/debug.h \
    - lib/deflate.c \
    - lib/deflate.h \
    - lib/encoding.c \
    - lib/encoding.h \
    - lib/endian.c \
    - lib/events.c \
    - lib/fileio.h \
    - lib/handlers.c \
    - lib/http.c \
    - lib/internal.h \
    - lib/libgadu.c \
    - lib/libgadu.h \
    - lib/message.c \
    - lib/message.h \
    - lib/network.c \
    - lib/network.h \
    - lib/obsolete.c \
    - lib/packets.pb-c.c \
    - lib/packets.pb-c.h \
    - lib/protobuf-c.c \
    - lib/protobuf-c.h \
    - lib/protobuf.c \
    - lib/protobuf.h \
    - lib/protocol.h \
    - lib/pubdir.c \
    - lib/pubdir50.c \
    - lib/resolver.c \
    - lib/resolver.h \
    - lib/session.h \
    - lib/strman.h \
    - lib/sha1.c \
    - lib/tvbuff.c \
    - lib/tvbuff.h \
    - lib/tvbuilder.c \
    - lib/tvbuilder.h
    -
    -INTGG_LIBS = $(ZLIB_LIBS) $(GNUTLS_LIBS)
    -INTGG_CFLAGS = \
    - -I$(top_srcdir)/libpurple/protocols/gg/lib \
    - $(ZLIB_CFLAGS) \
    - $(GNUTLS_CFLAGS) \
    - -DGG_IGNORE_DEPRECATED
    -
    -endif
    + Makefile.mingw
    GGSOURCES = \
    - $(INTGG_SOURCES) \
    avatar.c \
    avatar.h \
    blist.c \
    @@ -137,7 +82,6 @@
    -I$(top_srcdir)/libpurple \
    -I$(top_builddir)/libpurple \
    $(LIBGADU_CFLAGS) \
    - $(INTGG_CFLAGS) \
    $(GLIB_CFLAGS) \
    $(JSON_CFLAGS) \
    $(GPLUGIN_CFLAGS) \
    --- a/libpurple/protocols/gg/lib/COPYING Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,504 +0,0 @@
    - GNU LESSER GENERAL PUBLIC LICENSE
    - Version 2.1, February 1999
    -
    - Copyright (C) 1991, 1999 Free Software Foundation, Inc.
    - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    - Everyone is permitted to copy and distribute verbatim copies
    - of this license document, but changing it is not allowed.
    -
    -[This is the first released version of the Lesser GPL. It also counts
    - as the successor of the GNU Library Public License, version 2, hence
    - the version number 2.1.]
    -
    - Preamble
    -
    - The licenses for most software are designed to take away your
    -freedom to share and change it. By contrast, the GNU General Public
    -Licenses are intended to guarantee your freedom to share and change
    -free software--to make sure the software is free for all its users.
    -
    - This license, the Lesser General Public License, applies to some
    -specially designated software packages--typically libraries--of the
    -Free Software Foundation and other authors who decide to use it. You
    -can use it too, but we suggest you first think carefully about whether
    -this license or the ordinary General Public License is the better
    -strategy to use in any particular case, based on the explanations below.
    -
    - When we speak of free software, we are referring to freedom of use,
    -not price. Our General Public Licenses are designed to make sure that
    -you have the freedom to distribute copies of free software (and charge
    -for this service if you wish); that you receive source code or can get
    -it if you want it; that you can change the software and use pieces of
    -it in new free programs; and that you are informed that you can do
    -these things.
    -
    - To protect your rights, we need to make restrictions that forbid
    -distributors to deny you these rights or to ask you to surrender these
    -rights. These restrictions translate to certain responsibilities for
    -you if you distribute copies of the library or if you modify it.
    -
    - For example, if you distribute copies of the library, whether gratis
    -or for a fee, you must give the recipients all the rights that we gave
    -you. You must make sure that they, too, receive or can get the source
    -code. If you link other code with the library, you must provide
    -complete object files to the recipients, so that they can relink them
    -with the library after making changes to the library and recompiling
    -it. And you must show them these terms so they know their rights.
    -
    - We protect your rights with a two-step method: (1) we copyright the
    -library, and (2) we offer you this license, which gives you legal
    -permission to copy, distribute and/or modify the library.
    -
    - To protect each distributor, we want to make it very clear that
    -there is no warranty for the free library. Also, if the library is
    -modified by someone else and passed on, the recipients should know
    -that what they have is not the original version, so that the original
    -author's reputation will not be affected by problems that might be
    -introduced by others.
    -
    - Finally, software patents pose a constant threat to the existence of
    -any free program. We wish to make sure that a company cannot
    -effectively restrict the users of a free program by obtaining a
    -restrictive license from a patent holder. Therefore, we insist that
    -any patent license obtained for a version of the library must be
    -consistent with the full freedom of use specified in this license.
    -
    - Most GNU software, including some libraries, is covered by the
    -ordinary GNU General Public License. This license, the GNU Lesser
    -General Public License, applies to certain designated libraries, and
    -is quite different from the ordinary General Public License. We use
    -this license for certain libraries in order to permit linking those
    -libraries into non-free programs.
    -
    - When a program is linked with a library, whether statically or using
    -a shared library, the combination of the two is legally speaking a
    -combined work, a derivative of the original library. The ordinary
    -General Public License therefore permits such linking only if the
    -entire combination fits its criteria of freedom. The Lesser General
    -Public License permits more lax criteria for linking other code with
    -the library.
    -
    - We call this license the "Lesser" General Public License because it
    -does Less to protect the user's freedom than the ordinary General
    -Public License. It also provides other free software developers Less
    -of an advantage over competing non-free programs. These disadvantages
    -are the reason we use the ordinary General Public License for many
    -libraries. However, the Lesser license provides advantages in certain
    -special circumstances.
    -
    - For example, on rare occasions, there may be a special need to
    -encourage the widest possible use of a certain library, so that it becomes
    -a de-facto standard. To achieve this, non-free programs must be
    -allowed to use the library. A more frequent case is that a free
    -library does the same job as widely used non-free libraries. In this
    -case, there is little to gain by limiting the free library to free
    -software only, so we use the Lesser General Public License.
    -
    - In other cases, permission to use a particular library in non-free
    -programs enables a greater number of people to use a large body of
    -free software. For example, permission to use the GNU C Library in
    -non-free programs enables many more people to use the whole GNU
    -operating system, as well as its variant, the GNU/Linux operating
    -system.
    -
    - Although the Lesser General Public License is Less protective of the
    -users' freedom, it does ensure that the user of a program that is
    -linked with the Library has the freedom and the wherewithal to run
    -that program using a modified version of the Library.
    -
    - The precise terms and conditions for copying, distribution and
    -modification follow. Pay close attention to the difference between a
    -"work based on the library" and a "work that uses the library". The
    -former contains code derived from the library, whereas the latter must
    -be combined with the library in order to run.
    -
    - GNU LESSER GENERAL PUBLIC LICENSE
    - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
    -
    - 0. This License Agreement applies to any software library or other
    -program which contains a notice placed by the copyright holder or
    -other authorized party saying it may be distributed under the terms of
    -this Lesser General Public License (also called "this License").
    -Each licensee is addressed as "you".
    -
    - A "library" means a collection of software functions and/or data
    -prepared so as to be conveniently linked with application programs
    -(which use some of those functions and data) to form executables.
    -
    - The "Library", below, refers to any such software library or work
    -which has been distributed under these terms. A "work based on the
    -Library" means either the Library or any derivative work under
    -copyright law: that is to say, a work containing the Library or a
    -portion of it, either verbatim or with modifications and/or translated
    -straightforwardly into another language. (Hereinafter, translation is
    -included without limitation in the term "modification".)
    -
    - "Source code" for a work means the preferred form of the work for
    -making modifications to it. For a library, complete source code means
    -all the source code for all modules it contains, plus any associated
    -interface definition files, plus the scripts used to control compilation
    -and installation of the library.
    -
    - Activities other than copying, distribution and modification are not
    -covered by this License; they are outside its scope. The act of
    -running a program using the Library is not restricted, and output from
    -such a program is covered only if its contents constitute a work based
    -on the Library (independent of the use of the Library in a tool for
    -writing it). Whether that is true depends on what the Library does
    -and what the program that uses the Library does.
    -
    - 1. You may copy and distribute verbatim copies of the Library's
    -complete source code as you receive it, in any medium, provided that
    -you conspicuously and appropriately publish on each copy an
    -appropriate copyright notice and disclaimer of warranty; keep intact
    -all the notices that refer to this License and to the absence of any
    -warranty; and distribute a copy of this License along with the
    -Library.
    -
    - You may charge a fee for the physical act of transferring a copy,
    -and you may at your option offer warranty protection in exchange for a
    -fee.
    -
    - 2. You may modify your copy or copies of the Library or any portion
    -of it, thus forming a work based on the Library, and copy and
    -distribute such modifications or work under the terms of Section 1
    -above, provided that you also meet all of these conditions:
    -
    - a) The modified work must itself be a software library.
    -
    - b) You must cause the files modified to carry prominent notices
    - stating that you changed the files and the date of any change.
    -
    - c) You must cause the whole of the work to be licensed at no
    - charge to all third parties under the terms of this License.
    -
    - d) If a facility in the modified Library refers to a function or a
    - table of data to be supplied by an application program that uses
    - the facility, other than as an argument passed when the facility
    - is invoked, then you must make a good faith effort to ensure that,
    - in the event an application does not supply such function or
    - table, the facility still operates, and performs whatever part of
    - its purpose remains meaningful.
    -
    - (For example, a function in a library to compute square roots has
    - a purpose that is entirely well-defined independent of the
    - application. Therefore, Subsection 2d requires that any
    - application-supplied function or table used by this function must
    - be optional: if the application does not supply it, the square
    - root function must still compute square roots.)
    -
    -These requirements apply to the modified work as a whole. If
    -identifiable sections of that work are not derived from the Library,
    -and can be reasonably considered independent and separate works in
    -themselves, then this License, and its terms, do not apply to those
    -sections when you distribute them as separate works. But when you
    -distribute the same sections as part of a whole which is a work based
    -on the Library, the distribution of the whole must be on the terms of
    -this License, whose permissions for other licensees extend to the
    -entire whole, and thus to each and every part regardless of who wrote
    -it.
    -
    -Thus, it is not the intent of this section to claim rights or contest
    -your rights to work written entirely by you; rather, the intent is to
    -exercise the right to control the distribution of derivative or
    -collective works based on the Library.
    -
    -In addition, mere aggregation of another work not based on the Library
    -with the Library (or with a work based on the Library) on a volume of
    -a storage or distribution medium does not bring the other work under
    -the scope of this License.
    -
    - 3. You may opt to apply the terms of the ordinary GNU General Public
    -License instead of this License to a given copy of the Library. To do
    -this, you must alter all the notices that refer to this License, so
    -that they refer to the ordinary GNU General Public License, version 2,
    -instead of to this License. (If a newer version than version 2 of the
    -ordinary GNU General Public License has appeared, then you can specify
    -that version instead if you wish.) Do not make any other change in
    -these notices.
    -
    - Once this change is made in a given copy, it is irreversible for
    -that copy, so the ordinary GNU General Public License applies to all
    -subsequent copies and derivative works made from that copy.
    -
    - This option is useful when you wish to copy part of the code of
    -the Library into a program that is not a library.
    -
    - 4. You may copy and distribute the Library (or a portion or
    -derivative of it, under Section 2) in object code or executable form
    -under the terms of Sections 1 and 2 above provided that you accompany
    -it with the complete corresponding machine-readable source code, which
    -must be distributed under the terms of Sections 1 and 2 above on a
    -medium customarily used for software interchange.
    -
    - If distribution of object code is made by offering access to copy
    -from a designated place, then offering equivalent access to copy the
    -source code from the same place satisfies the requirement to
    -distribute the source code, even though third parties are not
    -compelled to copy the source along with the object code.
    -
    - 5. A program that contains no derivative of any portion of the
    -Library, but is designed to work with the Library by being compiled or
    -linked with it, is called a "work that uses the Library". Such a
    -work, in isolation, is not a derivative work of the Library, and
    -therefore falls outside the scope of this License.
    -
    - However, linking a "work that uses the Library" with the Library
    -creates an executable that is a derivative of the Library (because it
    -contains portions of the Library), rather than a "work that uses the
    -library". The executable is therefore covered by this License.
    -Section 6 states terms for distribution of such executables.
    -
    - When a "work that uses the Library" uses material from a header file
    -that is part of the Library, the object code for the work may be a
    -derivative work of the Library even though the source code is not.
    -Whether this is true is especially significant if the work can be
    -linked without the Library, or if the work is itself a library. The
    -threshold for this to be true is not precisely defined by law.
    -
    - If such an object file uses only numerical parameters, data
    -structure layouts and accessors, and small macros and small inline
    -functions (ten lines or less in length), then the use of the object
    -file is unrestricted, regardless of whether it is legally a derivative
    -work. (Executables containing this object code plus portions of the
    -Library will still fall under Section 6.)
    -
    - Otherwise, if the work is a derivative of the Library, you may
    -distribute the object code for the work under the terms of Section 6.
    -Any executables containing that work also fall under Section 6,
    -whether or not they are linked directly with the Library itself.
    -
    - 6. As an exception to the Sections above, you may also combine or
    -link a "work that uses the Library" with the Library to produce a
    -work containing portions of the Library, and distribute that work
    -under terms of your choice, provided that the terms permit
    -modification of the work for the customer's own use and reverse
    -engineering for debugging such modifications.
    -
    - You must give prominent notice with each copy of the work that the
    -Library is used in it and that the Library and its use are covered by
    -this License. You must supply a copy of this License. If the work
    -during execution displays copyright notices, you must include the
    -copyright notice for the Library among them, as well as a reference
    -directing the user to the copy of this License. Also, you must do one
    -of these things:
    -
    - a) Accompany the work with the complete corresponding
    - machine-readable source code for the Library including whatever
    - changes were used in the work (which must be distributed under
    - Sections 1 and 2 above); and, if the work is an executable linked
    - with the Library, with the complete machine-readable "work that
    - uses the Library", as object code and/or source code, so that the
    - user can modify the Library and then relink to produce a modified
    - executable containing the modified Library. (It is understood
    - that the user who changes the contents of definitions files in the
    - Library will not necessarily be able to recompile the application
    - to use the modified definitions.)
    -
    - b) Use a suitable shared library mechanism for linking with the
    - Library. A suitable mechanism is one that (1) uses at run time a
    - copy of the library already present on the user's computer system,
    - rather than copying library functions into the executable, and (2)
    - will operate properly with a modified version of the library, if
    - the user installs one, as long as the modified version is
    - interface-compatible with the version that the work was made with.
    -
    - c) Accompany the work with a written offer, valid for at
    - least three years, to give the same user the materials
    - specified in Subsection 6a, above, for a charge no more
    - than the cost of performing this distribution.
    -
    - d) If distribution of the work is made by offering access to copy
    - from a designated place, offer equivalent access to copy the above
    - specified materials from the same place.
    -
    - e) Verify that the user has already received a copy of these
    - materials or that you have already sent this user a copy.
    -
    - For an executable, the required form of the "work that uses the
    -Library" must include any data and utility programs needed for
    -reproducing the executable from it. However, as a special exception,
    -the materials to be distributed need not include anything that is
    -normally distributed (in either source or binary form) with the major
    -components (compiler, kernel, and so on) of the operating system on
    -which the executable runs, unless that component itself accompanies
    -the executable.
    -
    - It may happen that this requirement contradicts the license
    -restrictions of other proprietary libraries that do not normally
    -accompany the operating system. Such a contradiction means you cannot
    -use both them and the Library together in an executable that you
    -distribute.
    -
    - 7. You may place library facilities that are a work based on the
    -Library side-by-side in a single library together with other library
    -facilities not covered by this License, and distribute such a combined
    -library, provided that the separate distribution of the work based on
    -the Library and of the other library facilities is otherwise
    -permitted, and provided that you do these two things:
    -
    - a) Accompany the combined library with a copy of the same work
    - based on the Library, uncombined with any other library
    - facilities. This must be distributed under the terms of the
    - Sections above.
    -
    - b) Give prominent notice with the combined library of the fact
    - that part of it is a work based on the Library, and explaining
    - where to find the accompanying uncombined form of the same work.
    -
    - 8. You may not copy, modify, sublicense, link with, or distribute
    -the Library except as expressly provided under this License. Any
    -attempt otherwise to copy, modify, sublicense, link with, or
    -distribute the Library is void, and will automatically terminate your
    -rights under this License. However, parties who have received copies,
    -or rights, from you under this License will not have their licenses
    -terminated so long as such parties remain in full compliance.
    -
    - 9. You are not required to accept this License, since you have not
    -signed it. However, nothing else grants you permission to modify or
    -distribute the Library or its derivative works. These actions are
    -prohibited by law if you do not accept this License. Therefore, by
    -modifying or distributing the Library (or any work based on the
    -Library), you indicate your acceptance of this License to do so, and
    -all its terms and conditions for copying, distributing or modifying
    -the Library or works based on it.
    -
    - 10. Each time you redistribute the Library (or any work based on the
    -Library), the recipient automatically receives a license from the
    -original licensor to copy, distribute, link with or modify the Library
    -subject to these terms and conditions. You may not impose any further
    -restrictions on the recipients' exercise of the rights granted herein.
    -You are not responsible for enforcing compliance by third parties with
    -this License.
    -
    - 11. If, as a consequence of a court judgment or allegation of patent
    -infringement or for any other reason (not limited to patent issues),
    -conditions are imposed on you (whether by court order, agreement or
    -otherwise) that contradict the conditions of this License, they do not
    -excuse you from the conditions of this License. If you cannot
    -distribute so as to satisfy simultaneously your obligations under this
    -License and any other pertinent obligations, then as a consequence you
    -may not distribute the Library at all. For example, if a patent
    -license would not permit royalty-free redistribution of the Library by
    -all those who receive copies directly or indirectly through you, then
    -the only way you could satisfy both it and this License would be to
    -refrain entirely from distribution of the Library.
    -
    -If any portion of this section is held invalid or unenforceable under any
    -particular circumstance, the balance of the section is intended to apply,
    -and the section as a whole is intended to apply in other circumstances.
    -
    -It is not the purpose of this section to induce you to infringe any
    -patents or other property right claims or to contest validity of any
    -such claims; this section has the sole purpose of protecting the
    -integrity of the free software distribution system which is
    -implemented by public license practices. Many people have made
    -generous contributions to the wide range of software distributed
    -through that system in reliance on consistent application of that
    -system; it is up to the author/donor to decide if he or she is willing
    -to distribute software through any other system and a licensee cannot
    -impose that choice.
    -
    -This section is intended to make thoroughly clear what is believed to
    -be a consequence of the rest of this License.
    -
    - 12. If the distribution and/or use of the Library is restricted in
    -certain countries either by patents or by copyrighted interfaces, the
    -original copyright holder who places the Library under this License may add
    -an explicit geographical distribution limitation excluding those countries,
    -so that distribution is permitted only in or among countries not thus
    -excluded. In such case, this License incorporates the limitation as if
    -written in the body of this License.
    -
    - 13. The Free Software Foundation may publish revised and/or new
    -versions of the Lesser General Public License from time to time.
    -Such new versions will be similar in spirit to the present version,
    -but may differ in detail to address new problems or concerns.
    -
    -Each version is given a distinguishing version number. If the Library
    -specifies a version number of this License which applies to it and
    -"any later version", you have the option of following the terms and
    -conditions either of that version or of any later version published by
    -the Free Software Foundation. If the Library does not specify a
    -license version number, you may choose any version ever published by
    -the Free Software Foundation.
    -
    - 14. If you wish to incorporate parts of the Library into other free
    -programs whose distribution conditions are incompatible with these,
    -write to the author to ask for permission. For software which is
    -copyrighted by the Free Software Foundation, write to the Free
    -Software Foundation; we sometimes make exceptions for this. Our
    -decision will be guided by the two goals of preserving the free status
    -of all derivatives of our free software and of promoting the sharing
    -and reuse of software generally.
    -
    - NO WARRANTY
    -
    - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
    -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
    -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
    -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
    -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
    -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
    -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
    -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
    -
    - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
    -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
    -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
    -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
    -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
    -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
    -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
    -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
    -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
    -DAMAGES.
    -
    - END OF TERMS AND CONDITIONS
    -
    - How to Apply These Terms to Your New Libraries
    -
    - If you develop a new library, and you want it to be of the greatest
    -possible use to the public, we recommend making it free software that
    -everyone can redistribute and change. You can do so by permitting
    -redistribution under these terms (or, alternatively, under the terms of the
    -ordinary General Public License).
    -
    - To apply these terms, attach the following notices to the library. It is
    -safest to attach them to the start of each source file to most effectively
    -convey the exclusion of warranty; and each file should have at least the
    -"copyright" line and a pointer to where the full notice is found.
    -
    - <one line to give the library's name and a brief idea of what it does.>
    - Copyright (C) <year> <name of author>
    -
    - This library is free software; you can redistribute it and/or
    - modify it under the terms of the GNU Lesser General Public
    - License as published by the Free Software Foundation; either
    - version 2.1 of the License, or (at your option) any later version.
    -
    - This library 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
    - Lesser General Public License for more details.
    -
    - You should have received a copy of the GNU Lesser General Public
    - License along with this library; if not, write to the Free Software
    - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    -
    -Also add information on how to contact you by electronic and paper mail.
    -
    -You should also get your employer (if you work as a programmer) or your
    -school, if any, to sign a "copyright disclaimer" for the library, if
    -necessary. Here is a sample; alter the names:
    -
    - Yoyodyne, Inc., hereby disclaims all copyright interest in the
    - library `Frob' (a library for tweaking knobs) written by James Random Hacker.
    -
    - <signature of Ty Coon>, 1 April 1990
    - Ty Coon, President of Vice
    -
    -That's all there is to it!
    -
    -
    --- a/libpurple/protocols/gg/lib/common.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,882 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
    - * Robert J. Woźny <speedy@ziew.org>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file common.c
    - *
    - * \brief Funkcje wykorzystywane przez różne moduły biblioteki
    - */
    -
    -#include "network.h"
    -#include "strman.h"
    -#include "fileio.h"
    -
    -#include <errno.h>
    -#include <stdarg.h>
    -#include <stdlib.h>
    -#include <string.h>
    -#include <ctype.h>
    -#include <time.h>
    -
    -#include "config.h"
    -#include "libgadu.h"
    -#include "internal.h"
    -
    -#ifndef GG_CONFIG_HAVE_VA_COPY
    -# ifdef GG_CONFIG_HAVE___VA_COPY
    -# define va_copy(dest, src) __va_copy((dest), (src))
    -# else
    -/* Taka wersja va_copy() działa poprawnie tylko na platformach, które
    - * va_copy() de facto wcale nie potrzebują, np. MSVC. Definicja tylko dla
    - * przejrzystości kodu. */
    -# define va_copy(dest, src) (dest) = (src)
    -# endif
    -#endif
    -
    -/**
    - * \internal Odpowiednik funkcji \c vsprintf alokujący miejsce na wynik.
    - *
    - * Funkcja korzysta z funkcji \c vsnprintf, sprawdzając czy dostępna funkcja
    - * systemowa jest zgodna ze standardem C99 czy wcześniejszymi.
    - *
    - * \param format Format wiadomości (zgodny z \c printf)
    - * \param ap Lista argumentów (zgodna z \c printf)
    - *
    - * \return Zaalokowany bufor lub NULL, jeśli zabrakło pamięci.
    - *
    - * \ingroup helper
    - */
    -char *gg_vsaprintf(const char *format, va_list ap)
    -{
    - int size;
    - char *buf = NULL;
    - va_list aq;
    -
    -#if !defined(GG_CONFIG_HAVE_C99_VSNPRINTF) && !defined(HAVE__VSCPRINTF)
    - {
    - int res = 0;
    - char *tmp;
    -
    - size = 128;
    - do {
    - if (res > size) {
    - /* Jednak zachowanie zgodne z C99. */
    - size = res + 1;
    - } else {
    - size *= 2;
    - }
    -
    - if (!(tmp = realloc(buf, size))) {
    - free(buf);
    - return NULL;
    - }
    -
    - buf = tmp;
    - va_copy(aq, ap);
    - res = vsnprintf(buf, size, format, aq);
    - va_end(aq);
    - } while (res >= size || res < 0);
    - }
    -#else
    - va_copy(aq, ap);
    -
    -# ifdef HAVE__VSCPRINTF
    - size = _vscprintf(format, aq) + 1;
    -# else
    - {
    - char tmp[2];
    -
    - /* libce Solarisa przy buforze NULL zawsze zwracają -1, więc
    - * musimy podać coś istniejącego jako cel printf()owania. */
    - size = vsnprintf(tmp, sizeof(tmp), format, aq) + 1;
    - }
    -# endif
    - va_end(aq);
    - if (!(buf = malloc(size)))
    - return NULL;
    -
    - vsnprintf(buf, size, format, ap);
    -#endif
    -
    - return buf;
    -}
    -
    -/**
    - * \internal Odpowiednik funkcji \c sprintf alokujący miejsce na wynik.
    - *
    - * Funkcja korzysta z funkcji \c vsnprintf, sprawdzając czy dostępna funkcja
    - * systemowa jest zgodna ze standardem C99 czy wcześniejszymi.
    - *
    - * \param format Format wiadomości (zgodny z \c printf)
    - *
    - * \return Zaalokowany bufor lub NULL, jeśli zabrakło pamięci.
    - *
    - * \ingroup helper
    - */
    -char *gg_saprintf(const char *format, ...)
    -{
    - va_list ap;
    - char *res;
    -
    - va_start(ap, format);
    - res = gg_vsaprintf(format, ap);
    - va_end(ap);
    -
    - return res;
    -}
    -
    -/**
    - * \internal Pobiera linię tekstu z bufora.
    - *
    - * Funkcja niszczy bufor źródłowy bezpowrotnie, dzieląc go na kolejne ciągi
    - * znaków i obcina znaki końca linii.
    - *
    - * \param ptr Wskaźnik do zmiennej, która przechowuje aktualne położenie
    - * w analizowanym buforze
    - *
    - * \note Funkcja nie jest już używana. Pozostała dla zachowania ABI.
    - *
    - * \return Wskaźnik do kolejnej linii tekstu lub NULL, jeśli to już koniec
    - * bufora.
    - */
    -char *gg_get_line(char **ptr)
    -{
    - char *foo, *res;
    -
    - if (!ptr || !*ptr || !strcmp(*ptr, ""))
    - return NULL;
    -
    - res = *ptr;
    -
    - if (!(foo = strchr(*ptr, '\n')))
    - *ptr += strlen(*ptr);
    - else {
    - size_t len;
    - *ptr = foo + 1;
    - *foo = 0;
    -
    - len = strlen(res);
    -
    - if (len > 1 && res[len - 1] == '\r')
    - res[len - 1] = 0;
    - }
    -
    - return res;
    -}
    -
    -/**
    - * \internal Czyta linię tekstu z gniazda.
    - *
    - * Funkcja czyta tekst znak po znaku, więc nie jest efektywna, ale dzięki
    - * brakowi buforowania, nie koliduje z innymi funkcjami odczytu.
    - *
    - * \note W przypadku zakończenia połączenia przez drugą stronę, ostatnia
    - * linia nie jest zwracana.
    - *
    - * \param sock Deskryptor gniazda
    - * \param buf Wskaźnik do bufora
    - * \param length Długość bufora
    - *
    - * \return Zwraca wskaźnik na koniec odebranej linii jeśli się powiodło,
    - * lub \c NULL w przypadku błędu.
    - */
    -char *gg_read_line(int sock, char *buf, int length)
    -{
    - int ret;
    -
    - if (!buf || length < 0)
    - return NULL;
    -
    - for (; length > 1; buf++, length--) {
    - do {
    - if ((ret = recv(sock, buf, 1, 0)) == -1 &&
    - errno != EINTR && errno != EAGAIN)
    - {
    - gg_debug(GG_DEBUG_MISC, "// gg_read_line() "
    - "error on read (errno=%d, %s)\n",
    - errno, strerror(errno));
    - *buf = 0;
    - return NULL;
    - } else if (ret == 0) {
    - gg_debug(GG_DEBUG_MISC, "// gg_read_line() "
    - "eof reached\n");
    - *buf = 0;
    - return NULL;
    - }
    - } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
    -
    - if (*buf == '\n') {
    - buf++;
    - break;
    - }
    - }
    -
    - *buf = 0;
    - return buf;
    -}
    -
    -/**
    - * \internal Nawiązuje połączenie TCP.
    - *
    - * \param addr Wskaźnik na strukturę \c in_addr z adresem serwera
    - * \param port Port serwera
    - * \param async Flaga asynchronicznego połączenia
    - *
    - * \return Deskryptor gniazda lub -1 w przypadku błędu
    - *
    - * \ingroup helper
    - */
    -int gg_connect(void *addr, int port, int async)
    -{
    - int sock, errno2;
    - struct sockaddr_in sin;
    - struct in_addr *a = addr;
    - struct sockaddr_in myaddr;
    -
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n",
    - inet_ntoa(*a), port, async);
    -
    - if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_connect() socket() failed "
    - "(errno=%d, %s)\n", errno, strerror(errno));
    - return -1;
    - }
    -
    - memset(&myaddr, 0, sizeof(myaddr));
    - myaddr.sin_family = AF_INET;
    -
    - myaddr.sin_addr.s_addr = gg_local_ip;
    -
    - if (bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_connect() bind() failed "
    - "(errno=%d, %s)\n", errno, strerror(errno));
    - errno2 = errno;
    - close(sock);
    - errno = errno2;
    - return -1;
    - }
    -
    - if (async) {
    - if (!gg_fd_set_nonblocking(sock)) {
    - gg_debug(GG_DEBUG_MISC, "// gg_connect() can't set "
    - "nonblocking (errno=%d, %s)\n",
    - errno, strerror(errno));
    - errno2 = errno;
    - close(sock);
    - errno = errno2;
    - return -1;
    - }
    - }
    -
    - memset(&sin, 0, sizeof(sin));
    - sin.sin_port = htons(port);
    - sin.sin_family = AF_INET;
    - sin.sin_addr.s_addr = a->s_addr;
    -
    - if (connect(sock, (struct sockaddr*) &sin, sizeof(sin)) == -1) {
    - if (errno && (!async || errno != EINPROGRESS)) {
    - gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() "
    - "failed (errno=%d, %s)\n",
    - errno, strerror(errno));
    - errno2 = errno;
    - close(sock);
    - errno = errno2;
    - return -1;
    - }
    - gg_debug(GG_DEBUG_MISC,
    - "// gg_connect() connect() in progress\n");
    - }
    -
    - return sock;
    -}
    -
    -/**
    - * \internal Usuwa znaki końca linii.
    - *
    - * Funkcja działa bezpośrednio na buforze.
    - *
    - * \param line Bufor z tekstem
    - *
    - * \ingroup helper
    - */
    -void gg_chomp(char *line)
    -{
    - int len;
    -
    - if (!line)
    - return;
    -
    - len = strlen(line);
    -
    - if (len > 0 && line[len - 1] == '\n')
    - line[--len] = 0;
    - if (len > 0 && line[len - 1] == '\r')
    - line[--len] = 0;
    -}
    -
    -/**
    - * \internal Koduje ciąg znaków do postacji adresu HTTP.
    - *
    - * Zamienia znaki niedrukowalne, spoza ASCII i mające specjalne znaczenie
    - * dla protokołu HTTP na encje postaci \c %XX, gdzie \c XX jest szesnastkową
    - * wartością znaku.
    - *
    - * \param str Ciąg znaków do zakodowania
    - *
    - * \return Zaalokowany bufor lub \c NULL w przypadku błędu.
    - *
    - * \ingroup helper
    - */
    -char *gg_urlencode(const char *str)
    -{
    - char *q, *buf;
    - const char hex[] = "0123456789abcdef";
    - const char *p;
    - unsigned int size = 0;
    -
    - if (!str)
    - str = "";
    -
    - for (p = str; *p; p++, size++) {
    - if (!((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') ||
    - (*p >= '0' && *p <= '9') || *p == ' ') || (*p == '@') ||
    - (*p == '.') || (*p == '-'))
    - {
    - size += 2;
    - }
    - }
    -
    - if (!(buf = malloc(size + 1)))
    - return NULL;
    -
    - for (p = str, q = buf; *p; p++, q++) {
    - if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') ||
    - (*p >= '0' && *p <= '9') || (*p == '@') ||
    - (*p == '.') || (*p == '-'))
    - {
    - *q = *p;
    - } else {
    - if (*p == ' ')
    - *q = '+';
    - else {
    - *q++ = '%';
    - *q++ = hex[*p >> 4 & 15];
    - *q = hex[*p & 15];
    - }
    - }
    - }
    -
    - *q = 0;
    -
    - return buf;
    -}
    -
    -/**
    - * \internal Wyznacza skrót dla usług HTTP.
    - *
    - * Funkcja jest wykorzystywana do wyznaczania skrótu adresu e-mail, hasła
    - * i innych wartości przekazywanych jako parametry usług HTTP.
    - *
    - * W parametrze \c format należy umieścić znaki określające postać kolejnych
    - * parametrów: \c 's' jeśli parametr jest ciągiem znaków, \c 'u' jeśli jest
    - * liczbą.
    - *
    - * \param format Format kolejnych parametrów (niezgodny z \c printf)
    - *
    - * \return Wartość skrótu
    - */
    -int gg_http_hash(const char *format, ...)
    -{
    - unsigned int a, c, i, j;
    - va_list ap;
    - int b = -1;
    -
    - va_start(ap, format);
    -
    - for (j = 0; j < strlen(format); j++) {
    - const char *arg;
    - char buf[16];
    -
    - if (format[j] == 'u') {
    - snprintf(buf, sizeof(buf), "%d", va_arg(ap, uin_t));
    - arg = buf;
    - } else {
    - if (!(arg = va_arg(ap, char*)))
    - arg = "";
    - }
    -
    - i = 0;
    - while ((c = (unsigned char) arg[i++]) != 0) {
    - a = (c ^ b) + (c << 8);
    - b = (a >> 24) | (a << 8);
    - }
    - }
    -
    - va_end(ap);
    -
    - return (b < 0 ? -b : b);
    -}
    -
    -/**
    - * \internal Zestaw znaków kodowania base64.
    - */
    -static char gg_base64_charset[] =
    - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    -
    -/**
    - * \internal Koduje ciąg znaków do base64.
    - *
    - * Wynik funkcji należy zwolnić za pomocą \c free.
    - *
    - * \param buf Bufor z danami do zakodowania
    - *
    - * \return Zaalokowany bufor z zakodowanymi danymi
    - *
    - * \ingroup helper
    - */
    -char *gg_base64_encode(const char *buf)
    -{
    - char *out, *res;
    - unsigned int i = 0, j = 0, k = 0, len = strlen(buf);
    -
    - res = out = malloc((len / 3 + 1) * 4 + 2);
    -
    - if (!res)
    - return NULL;
    -
    - while (j <= len) {
    - switch (i % 4) {
    - case 0:
    - k = (buf[j] & 252) >> 2;
    - break;
    - case 1:
    - if (j < len)
    - k = ((buf[j] & 3) << 4) | ((buf[j + 1] & 240) >> 4);
    - else
    - k = (buf[j] & 3) << 4;
    -
    - j++;
    - break;
    - case 2:
    - if (j < len)
    - k = ((buf[j] & 15) << 2) | ((buf[j + 1] & 192) >> 6);
    - else
    - k = (buf[j] & 15) << 2;
    -
    - j++;
    - break;
    - case 3:
    - k = buf[j++] & 63;
    - break;
    - }
    - *out++ = gg_base64_charset[k];
    - i++;
    - }
    -
    - if (i % 4)
    - for (j = 0; j < 4 - (i % 4); j++, out++)
    - *out = '=';
    -
    - *out = 0;
    -
    - return res;
    -}
    -
    -/**
    - * \internal Dekoduje ciąg znaków zapisany w base64.
    - *
    - * Wynik funkcji należy zwolnić za pomocą \c free.
    - *
    - * \param buf Bufor źródłowy z danymi do zdekodowania
    - *
    - * \return Zaalokowany bufor ze zdekodowanymi danymi
    - *
    - * \ingroup helper
    - */
    -char *gg_base64_decode(const char *buf)
    -{
    - char *res, *save, *foo, val;
    - const char *end;
    - unsigned int idx = 0;
    -
    - if (!buf)
    - return NULL;
    -
    - save = res = calloc(1, (strlen(buf) / 4 + 1) * 3 + 2);
    -
    - if (!save)
    - return NULL;
    -
    - end = buf + strlen(buf);
    -
    - while (*buf && buf < end) {
    - if (*buf == '\r' || *buf == '\n') {
    - buf++;
    - continue;
    - }
    - if (!(foo = memchr(gg_base64_charset, *buf, sizeof(gg_base64_charset))))
    - foo = gg_base64_charset;
    - val = (int)(foo - gg_base64_charset);
    - buf++;
    - switch (idx) {
    - case 0:
    - *res |= val << 2;
    - break;
    - case 1:
    - *res++ |= val >> 4;
    - *res |= val << 4;
    - break;
    - case 2:
    - *res++ |= val >> 2;
    - *res |= val << 6;
    - break;
    - case 3:
    - *res++ |= val;
    - break;
    - }
    - idx++;
    - idx %= 4;
    - }
    - *res = 0;
    -
    - return save;
    -}
    -
    -/**
    - * \internal Tworzy nagłówek autoryzacji serwera pośredniczącego.
    - *
    - * Dane pobiera ze zmiennych globalnych \c gg_proxy_username i
    - * \c gg_proxy_password.
    - *
    - * \return Zaalokowany bufor z tekstem lub NULL, jeśli serwer pośredniczący
    - * nie jest używany lub nie wymaga autoryzacji.
    - */
    -char *gg_proxy_auth(void)
    -{
    - char *tmp, *enc, *out;
    - unsigned int tmp_size;
    -
    - if (!gg_proxy_enabled || !gg_proxy_username || !gg_proxy_password)
    - return NULL;
    -
    - tmp_size = strlen(gg_proxy_username) + strlen(gg_proxy_password) + 2;
    - tmp = malloc(tmp_size);
    - if (!tmp)
    - return NULL;
    -
    - snprintf(tmp, tmp_size, "%s:%s", gg_proxy_username, gg_proxy_password);
    -
    - enc = gg_base64_encode(tmp);
    - if (!enc) {
    - free(tmp);
    - return NULL;
    - }
    -
    - free(tmp);
    -
    - out = malloc(strlen(enc) + 40);
    - if (!out) {
    - free(enc);
    - return NULL;
    - }
    -
    - snprintf(out, strlen(enc) + 40, "Proxy-Authorization: Basic %s\r\n", enc);
    -
    - free(enc);
    -
    - return out;
    -}
    -
    -/**
    - * \internal Tablica pomocnicza do wyznaczania sumy kontrolnej.
    - */
    -static const uint32_t gg_crc32_table[256] =
    -{
    - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
    - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
    - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
    - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
    - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
    - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
    - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
    - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
    - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
    - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
    - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
    - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
    - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
    - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
    - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
    - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
    - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
    - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
    - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
    - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
    - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
    - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
    - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
    - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
    - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
    - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
    - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
    - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
    - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
    - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
    - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
    - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
    - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
    - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
    - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
    - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
    - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
    - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
    - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
    - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
    - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
    - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
    - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
    - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
    - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
    - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
    - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
    - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
    - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
    - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
    - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
    - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
    - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
    - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
    - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
    - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
    - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
    - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
    - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
    - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
    - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
    - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
    - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
    - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
    -};
    -
    -/**
    - * Wyznacza sumę kontrolną CRC32.
    - *
    - * \param crc Suma kontrola poprzedniego bloku danych lub 0 jeśli liczona
    - * jest suma kontrolna pierwszego bloku
    - * \param buf Bufor danych
    - * \param len Długość bufora danych
    - *
    - * \return Suma kontrolna.
    - */
    -uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len)
    -{
    - if (buf == NULL || len < 0)
    - return crc;
    -
    - crc ^= 0xffffffffL;
    -
    - while (len--)
    - crc = (crc >> 8) ^ gg_crc32_table[(crc ^ *buf++) & 0xff];
    -
    - return crc ^ 0xffffffffL;
    -}
    -
    -/**
    - * \internal Parsuje identyfikator użytkownika.
    - *
    - * \param str Ciąg tekstowy, zawierający identyfikator
    - * \param len Długość identyfikatora
    - *
    - * \return Identyfikator, lub 0, jeżeli nie udało się odczytać
    - */
    -uin_t gg_str_to_uin(const char *str, int len)
    -{
    - char buff[11];
    - char *endptr;
    - uin_t uin;
    -
    - if (len < 0)
    - len = strlen(str);
    - if (len > 10)
    - return 0;
    - memcpy(buff, str, len);
    - buff[len] = '\0';
    -
    - errno = 0;
    - uin = strtoul(buff, &endptr, 10);
    - if (errno == ERANGE || endptr[0] != '\0')
    - return 0;
    -
    - return uin;
    -}
    -
    -/**
    - * Szuka informacji o konferencji o podanym identyfikatorze.
    - *
    - * \param sess Struktura sesji
    - * \param id Identyfikator konferencji
    - *
    - * \return Struktura z informacjami o konferencji
    - */
    -gg_chat_list_t *gg_chat_find(struct gg_session *sess, uint64_t id)
    -{
    - gg_chat_list_t *chat_list = sess->private_data->chat_list;
    -
    - while (chat_list != NULL) {
    - if (chat_list->id == id)
    - return chat_list;
    - chat_list = chat_list->next;
    - }
    -
    - return NULL;
    -}
    -
    -/**
    - * \internal Aktualizuje informacje o konferencji.
    - *
    - * \param sess Struktura sesji
    - * \param id Identyfikator konferencji
    - * \param version Wersja informacji o konferencji
    - * \param participants Lista uczestników konferencji
    - * \param participants_count Ilość uczestników konferencji
    - *
    - * \return Wartość równa 0, jeżeli zakończono powodzeniem
    - */
    -int gg_chat_update(struct gg_session *sess, uint64_t id, uint32_t version,
    - const uin_t *participants, unsigned int participants_count)
    -{
    - gg_chat_list_t *chat;
    - uin_t *participants_new;
    -
    - if (participants_count >= ~(unsigned int)0 / sizeof(uin_t))
    - return -1;
    -
    - chat = gg_chat_find(sess, id);
    -
    - if (!chat) {
    - chat = malloc(sizeof(gg_chat_list_t));
    -
    - if (!chat)
    - return -1;
    -
    - memset(chat, 0, sizeof(gg_chat_list_t));
    - chat->id = id;
    - chat->next = sess->private_data->chat_list;
    - sess->private_data->chat_list = chat;
    - }
    -
    - participants_new = realloc(chat->participants,
    - sizeof(uin_t) * participants_count);
    -
    - if (participants_new == NULL)
    - return -1;
    -
    - chat->version = version;
    - chat->participants = participants_new;
    - chat->participants_count = participants_count;
    - memcpy(chat->participants, participants,
    - sizeof(uin_t) * participants_count);
    -
    - return 0;
    -}
    -
    -void gg_connection_failure(struct gg_session *gs, struct gg_event *ge,
    - enum gg_failure_t failure)
    -{
    - gg_close(gs);
    -
    - if (ge != NULL) {
    - ge->type = GG_EVENT_CONN_FAILED;
    - ge->event.failure = failure;
    - }
    - gs->state = GG_STATE_IDLE;
    -}
    -
    -time_t gg_server_time(struct gg_session *gs)
    -{
    - time_t now = time(NULL);
    -
    - if (gs == NULL || gs->private_data == NULL) {
    - gg_debug_session(gs, GG_DEBUG_ERROR, "time diff data is not "
    - "accessible\n");
    - return now;
    - }
    -
    - return now + gs->private_data->time_diff;
    -}
    -
    -void gg_strarr_free(char **strarr)
    -{
    - char **it;
    -
    - if (strarr == NULL)
    - return;
    -
    - for (it = strarr; *it != NULL; it++)
    - free(*it);
    - free(strarr);
    -}
    -
    -char ** gg_strarr_dup(char **strarr)
    -{
    - size_t i, len, size;
    - char **it, **out;
    -
    - if (strarr == NULL)
    - return NULL;
    -
    - len = 0;
    - for (it = strarr; *it != NULL; it++)
    - len++;
    -
    - size = (len + 1) * sizeof(char*);
    - out = malloc(size);
    -
    - if (out == NULL) {
    - gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR, "// gg_strarr_dup() "
    - "not enough memory for the array\n");
    - return NULL;
    - }
    - memset(out, 0, size);
    -
    - for (i = 0; i < len; i++) {
    - out[i] = strdup(strarr[i]);
    - if (out[i] == NULL) {
    - gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "// gg_strarr_dup() "
    - "not enough memory for the array element\n");
    - gg_strarr_free(out);
    - return NULL;
    - }
    - }
    -
    - return out;
    -}
    -
    -/*
    - * Local variables:
    - * c-indentation-style: k&r
    - * c-basic-offset: 8
    - * indent-tabs-mode: notnil
    - * End:
    - *
    - * vim: shiftwidth=8:
    - */
    --- a/libpurple/protocols/gg/lib/config.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,93 +0,0 @@
    -/* Local libgadu configuration file. */
    -
    -#undef printf
    -
    -/* libpurple's config */
    -#include <config.h>
    -
    -#define GG_LIBGADU_VERSION "1.12.1"
    -
    -/* Defined if libgadu was compiled for bigendian machine. */
    -#undef GG_CONFIG_BIGENDIAN
    -#ifdef WORDS_BIGENDIAN
    -# define GG_CONFIG_BIGENDIAN
    -#endif
    -
    -/* Defined if this machine has gethostbyname_r(). */
    -#undef GG_CONFIG_HAVE_GETHOSTBYNAME_R
    -
    -/* Define to 1 if you have the `_exit' function. */
    -#define HAVE__EXIT 1
    -
    -/* Defined if libgadu was compiled and linked with fork support. */
    -#undef GG_CONFIG_HAVE_FORK
    -#ifndef _WIN32
    -# define GG_CONFIG_HAVE_FORK
    -#endif
    -
    -/* Defined if libgadu was compiled and linked with pthread support. */
    -/* We don't use pthreads - they may not be safe. */
    -#undef GG_CONFIG_HAVE_PTHREAD
    -
    -/* Defined if this machine has C99-compiliant vsnprintf(). */
    -#undef HAVE_C99_VSNPRINTF
    -#if !defined(_WIN32) || (defined(__MINGW64_VERSION_MAJOR) && __MINGW64_VERSION_MAJOR >= 3)
    -# define HAVE_C99_VSNPRINTF
    -#endif
    -
    -/* Defined if this machine has va_copy(). */
    -#define GG_CONFIG_HAVE_VA_COPY
    -
    -/* Defined if this machine has __va_copy(). */
    -#define GG_CONFIG_HAVE___VA_COPY
    -
    -/* Defined if this machine supports long long. */
    -#undef GG_CONFIG_HAVE_LONG_LONG
    -#ifdef HAVE_LONG_LONG
    -# define GG_CONFIG_HAVE_LONG_LONG
    -#endif
    -
    -/* Defined if libgadu was compiled and linked with GnuTLS support. */
    -#undef GG_CONFIG_HAVE_GNUTLS
    -#if defined(HAVE_GNUTLS) && defined(HAVE_GNUTLS_2_10)
    -# define GG_CONFIG_HAVE_GNUTLS
    -#endif
    -
    -/* Defined if libgadu was compiled and linked with OpenSSL support. */
    -/* OpenSSL cannot be used with libpurple due to licence type. */
    -#undef GG_CONFIG_HAVE_OPENSSL
    -
    -/* Defined if libgadu was compiled and linked with zlib support. */
    -#define GG_CONFIG_HAVE_ZLIB
    -
    -/* Defined if uintX_t types are defined in <stdint.h>. */
    -#undef GG_CONFIG_HAVE_STDINT_H
    -#ifdef HAVE_STDINT_H
    -# define GG_CONFIG_HAVE_STDINT_H
    -#endif
    -
    -/* Defined if uintX_t types are defined in <inttypes.h>. */
    -#undef GG_CONFIG_HAVE_INTTYPES_H
    -#ifdef HAVE_INTTYPES_H
    -# define GG_CONFIG_HAVE_INTTYPES_H
    -#endif
    -
    -/* Defined if uintX_t types are defined in <sys/types.h>. */
    -#undef GG_CONFIG_HAVE_SYS_TYPES_H
    -#ifdef HAVE_SYS_TYPES_H
    -# define GG_CONFIG_HAVE_SYS_TYPES_H
    -#endif
    -
    -/* Defined if this machine has uint64_t. */
    -#define GG_CONFIG_HAVE_UINT64_T
    -
    -/* Defined if libgadu is GPL compliant (was not linked with OpenSSL or any
    - * other non-GPL compliant library support). */
    -#define GG_CONFIG_IS_GPL_COMPLIANT
    -
    -/* Defined if libgadu uses system defalt trusted CAs. */
    -#define GG_CONFIG_SSL_SYSTEM_TRUST
    -
    -/* Defined if libgadu is GPL compliant (was not linked with OpenSSL or any
    - other non-GPL compliant library support). */
    -#define GG_CONFIG_IS_GPL_COMPLIANT
    --- a/libpurple/protocols/gg/lib/dcc.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1410 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka@irc.pl>
    - * Tomasz Chiliński <chilek@chilan.com>
    - * Adam Wysocki <gophi@ekg.chmurka.net>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file dcc.c
    - *
    - * \brief Obsługa połączeń bezpośrednich do wersji Gadu-Gadu 6.x
    - */
    -
    -#include "fileio.h"
    -#include "network.h"
    -
    -#include <ctype.h>
    -#include <errno.h>
    -#include <string.h>
    -#include <stdlib.h>
    -
    -#include "libgadu.h"
    -#include "debug.h"
    -#include "internal.h"
    -
    -/**
    - * \internal Przekazuje zawartość pakietu do odpluskwiania.
    - *
    - * \param prefix Prefiks informacji
    - * \param fd Deskryptor gniazda
    - * \param buf Bufor z danumi
    - * \param size Rozmiar bufora z danymi
    - */
    -static void gg_dcc_debug_data(const char *prefix, int fd, const void *buf, unsigned int size)
    -{
    - gg_debug(GG_DEBUG_MISC, "++ gg_dcc %s (fd=%d,len=%d)", prefix, fd, size);
    - gg_debug_dump(NULL, GG_DEBUG_DUMP, buf, size);
    - gg_debug(GG_DEBUG_MISC, "\n");
    -}
    -
    -/**
    - * Wysyła żądanie zwrotnego połączenia bezpośredniego.
    - *
    - * Funkcję wykorzystuje się, jeśli nie ma możliwości połączenia się z odbiorcą
    - * pliku lub rozmowy głosowej. Po otrzymaniu żądania druga strona spróbuje
    - * nawiązać zwrotne połączenie bezpośrednie z nadawcą.
    - * gg_dcc_request()
    - *
    - * \param sess Struktura sesji
    - * \param uin Numer odbiorcy
    - *
    - * \return Patrz \c gg_send_message_ctcp()
    - *
    - * \ingroup dcc6
    - */
    -int gg_dcc_request(struct gg_session *sess, uin_t uin)
    -{
    - return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (const unsigned char*) "\002", 1);
    -}
    -
    -/**
    - * \internal Zamienia znacznik czasu w postaci uniksowej na format API WIN32.
    - *
    - * \note Funkcja działa jedynie gdy kompilator obsługuje typ danych
    - * \c long \c long.
    - *
    - * \param ut Czas w postaci uniksowej
    - * \param ft Czas w postaci API WIN32
    - */
    -static void gg_dcc_fill_filetime(uint32_t ut, uint32_t *ft)
    -{
    - uint64_t tmp;
    -
    - tmp = ut;
    - tmp += 11644473600LL;
    - tmp *= 10000000LL;
    -
    - tmp = gg_fix64(tmp);
    -
    - memcpy(ft, &tmp, sizeof(tmp));
    -}
    -
    -/**
    - * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku.
    - *
    - * \note Większą funkcjonalność zapewnia funkcja \c gg_dcc_fill_file_info2().
    - *
    - * \param d Struktura połączenia
    - * \param filename Nazwa pliku
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup dcc6
    - */
    -int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename)
    -{
    - return gg_dcc_fill_file_info2(d, filename, filename);
    -}
    -
    -/**
    - * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku.
    - *
    - * \param d Struktura połączenia
    - * \param filename Nazwa pliku zapisywana w strukturze
    - * \param local_filename Nazwa pliku w lokalnym systemie plików
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup dcc6
    - */
    -int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename)
    -{
    - struct stat st;
    - const char *name, *ext, *p;
    - unsigned char *q;
    - int i, j;
    -
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_fill_file_info2(%p, \"%s\", \"%s\");\n", d, filename, local_filename);
    -
    - if (!d || d->type != GG_SESSION_DCC_SEND) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() invalid arguments\n");
    - errno = EINVAL;
    - return -1;
    - }
    -
    - if ((d->file_fd = open(local_filename, O_RDONLY)) == -1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() open() failed (%s)\n", strerror(errno));
    - return -1;
    - }
    -
    - if (fstat(d->file_fd, &st) == -1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() "
    - "fstat() failed (%s)\n", strerror(errno));
    - close(d->file_fd);
    - d->file_fd = -1;
    - return -1;
    - }
    -
    - if ((st.st_mode & S_IFDIR)) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() that's a directory\n");
    - errno = EINVAL;
    - close(d->file_fd);
    - d->file_fd = -1;
    - return -1;
    - }
    -
    - memset(&d->file_info, 0, sizeof(d->file_info));
    -
    - if (!(st.st_mode & S_IWUSR))
    - d->file_info.mode |= gg_fix32(GG_DCC_FILEATTR_READONLY);
    -
    - gg_dcc_fill_filetime(st.st_atime, d->file_info.atime);
    - gg_dcc_fill_filetime(st.st_mtime, d->file_info.mtime);
    - gg_dcc_fill_filetime(st.st_ctime, d->file_info.ctime);
    -
    - d->file_info.size = gg_fix32(st.st_size);
    - d->file_info.mode = gg_fix32(0x20); /* FILE_ATTRIBUTE_ARCHIVE */
    -
    - if (!(name = strrchr(filename, '/')))
    - name = filename;
    - else
    - name++;
    -
    - if (!(ext = strrchr(name, '.')))
    - ext = name + strlen(name);
    -
    - for (i = 0, p = name; i < 8 && p < ext; i++, p++)
    - d->file_info.short_filename[i] = toupper(name[i]);
    -
    - if (i == 8 && p < ext) {
    - d->file_info.short_filename[6] = '~';
    - d->file_info.short_filename[7] = '1';
    - }
    -
    - if (strlen(ext) > 0) {
    - for (j = 0; *ext && j < 4; j++, p++)
    - d->file_info.short_filename[i + j] = toupper(ext[j]);
    - }
    -
    - for (q = d->file_info.short_filename; *q; q++) {
    - if (*q == 185) {
    - *q = 165;
    - } else if (*q == 230) {
    - *q = 198;
    - } else if (*q == 234) {
    - *q = 202;
    - } else if (*q == 179) {
    - *q = 163;
    - } else if (*q == 241) {
    - *q = 209;
    - } else if (*q == 243) {
    - *q = 211;
    - } else if (*q == 156) {
    - *q = 140;
    - } else if (*q == 159) {
    - *q = 143;
    - } else if (*q == 191) {
    - *q = 175;
    - }
    - }
    -
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() short name \"%s\","
    - " dos name \"%s\"\n", name, d->file_info.short_filename);
    - strncpy((char*) d->file_info.filename, name, sizeof(d->file_info.filename) - 1);
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Rozpoczyna połączenie bezpośrednie z danym klientem.
    - *
    - * \param ip Adres IP odbiorcy
    - * \param port Port odbiorcy
    - * \param my_uin Własny numer
    - * \param peer_uin Numer odbiorcy
    - * \param type Rodzaj połączenia (\c GG_SESSION_DCC_SEND lub \c GG_SESSION_DCC_GET)
    - *
    - * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
    - */
    -static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin, int type)
    -{
    - struct gg_dcc *d = NULL;
    - struct in_addr addr;
    -
    - addr.s_addr = ip;
    -
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_transfer(%s, %d, %u, %u, "
    - "%s);\n", inet_ntoa(addr), port, my_uin, peer_uin,
    - (type == GG_SESSION_DCC_SEND) ? "SEND" : "GET");
    -
    - if (!ip || ip == INADDR_NONE || !port || !my_uin || !peer_uin) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() invalid arguments\n");
    - errno = EINVAL;
    - return NULL;
    - }
    -
    - if (!(d = (void*) calloc(1, sizeof(*d)))) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() not enough memory\n");
    - return NULL;
    - }
    -
    - d->check = GG_CHECK_WRITE;
    - d->state = GG_STATE_CONNECTING;
    - d->type = type;
    - d->timeout = GG_DEFAULT_TIMEOUT;
    - d->file_fd = -1;
    - d->active = 1;
    - d->fd = -1;
    - d->uin = my_uin;
    - d->peer_uin = peer_uin;
    -
    - if ((d->fd = gg_connect(&addr, port, 1)) == -1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() connection failed\n");
    - free(d);
    - return NULL;
    - }
    -
    - return d;
    -}
    -
    -/**
    - * Rozpoczyna odbieranie pliku przez zwrotne połączenie bezpośrednie.
    - *
    - * \param ip Adres IP nadawcy
    - * \param port Port nadawcy
    - * \param my_uin Własny numer
    - * \param peer_uin Numer nadawcy
    - *
    - * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
    - *
    - * \ingroup dcc6
    - */
    -struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_get_file() handing over to gg_dcc_transfer()\n");
    -
    - return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_GET);
    -}
    -
    -/**
    - * Rozpoczyna wysyłanie pliku.
    - *
    - * \param ip Adres IP odbiorcy
    - * \param port Port odbiorcy
    - * \param my_uin Własny numer
    - * \param peer_uin Numer odbiorcy
    - *
    - * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
    - *
    - * \ingroup dcc6
    - */
    -struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_send_file() handing over to gg_dcc_transfer()\n");
    -
    - return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_SEND);
    -}
    -
    -/**
    - * Rozpoczyna połączenie głosowe.
    - *
    - * \param ip Adres IP odbiorcy
    - * \param port Port odbiorcy
    - * \param my_uin Własny numer
    - * \param peer_uin Numer odbiorcy
    - *
    - * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
    - *
    - * \ingroup dcc6
    - */
    -struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_chat() handing over to gg_dcc_transfer()\n");
    -
    - return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_VOICE);
    -}
    -
    -/**
    - * Ustawia typ przychodzącego połączenia bezpośredniego.
    - *
    - * Funkcję należy wywołać po otrzymaniu zdarzenia \c GG_EVENT_DCC_CALLBACK.
    - *
    - * \param d Struktura połączenia
    - * \param type Rodzaj połączenia (\c GG_SESSION_DCC_SEND lub
    - * \c GG_SESSION_DCC_VOICE)
    - *
    - * \ingroup dcc6
    - */
    -void gg_dcc_set_type(struct gg_dcc *d, int type)
    -{
    - d->type = type;
    - d->state = (type == GG_SESSION_DCC_SEND) ? GG_STATE_SENDING_FILE_INFO : GG_STATE_SENDING_VOICE_REQUEST;
    -}
    -
    -/**
    - * \internal Funkcja zwrotna połączenia bezpośredniego.
    - *
    - * Pole \c callback struktury \c gg_dcc zawiera wskaźnik do tej funkcji.
    - * Wywołuje ona \c gg_dcc_watch_fd() i zachowuje wynik w polu \c event.
    - *
    - * \note Funkcjonalność funkcji zwrotnej nie jest już wspierana.
    - *
    - * \param d Struktura połączenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -static int gg_dcc_callback(struct gg_dcc *d)
    -{
    - struct gg_event *e = gg_dcc_watch_fd(d);
    -
    - d->event = e;
    -
    - return (e != NULL) ? 0 : -1;
    -}
    -
    -/**
    - * Tworzy gniazdo nasłuchujące dla połączeń bezpośrednich.
    - *
    - * Funkcja przywiązuje gniazdo do pierwszego wolnego portu TCP.
    - *
    - * \param uin Własny numer
    - * \param port Preferowany port (jeśli równy 0 lub -1, próbuje się domyślnego)
    - *
    - * \note Ze względu na możliwość podania wartości -1 do parametru będącego
    - * 16-bitową liczbą bez znaku, port 65535 nie jest dostępny.
    - *
    - * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
    - *
    - * \ingroup dcc6
    - */
    -struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port)
    -{
    - struct gg_dcc *c;
    - int sock, bound = 0, errno2;
    -
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_create_dcc_socket(%d, %d);\n", uin, port);
    -
    - if (!uin) {
    - gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() invalid arguments\n");
    - errno = EINVAL;
    - return NULL;
    - }
    -
    - if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() can't create socket (%s)\n", strerror(errno));
    - return NULL;
    - }
    -
    - if (port == 0 || port == (uint16_t)-1)
    - port = GG_DEFAULT_DCC_PORT;
    -
    - while (!bound) {
    - struct sockaddr_in sin;
    -
    - memset(&sin, 0, sizeof(sin));
    - sin.sin_family = AF_INET;
    - sin.sin_addr.s_addr = INADDR_ANY;
    - sin.sin_port = htons(port);
    -
    - gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() trying port %d\n", port);
    - if (!bind(sock, (struct sockaddr*) &sin, sizeof(sin)))
    - bound = 1;
    - else {
    - if (++port == 65535) {
    - gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() no free port found\n");
    - close(sock);
    - return NULL;
    - }
    - }
    - }
    -
    - if (listen(sock, 10)) {
    - gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() unable to listen (%s)\n", strerror(errno));
    - errno2 = errno;
    - close(sock);
    - errno = errno2;
    - return NULL;
    - }
    -
    - gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() bound to port %d\n", port);
    -
    - if (!(c = malloc(sizeof(*c)))) {
    - gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() not enough memory for struct\n");
    - close(sock);
    - return NULL;
    - }
    - memset(c, 0, sizeof(*c));
    -
    - c->port = c->id = port;
    - c->fd = sock;
    - c->file_fd = -1;
    - c->type = GG_SESSION_DCC_SOCKET;
    - c->uin = uin;
    - c->timeout = -1;
    - c->state = GG_STATE_LISTENING;
    - c->check = GG_CHECK_READ;
    - c->callback = gg_dcc_callback;
    - c->destroy = gg_dcc_free;
    -
    - return c;
    -}
    -
    -/**
    - * Wysyła ramkę danych połączenia głosowego.
    - *
    - * \param d Struktura połączenia
    - * \param buf Bufor z danymi
    - * \param length Długość bufora z danymi
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup dcc6
    - */
    -int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length)
    -{
    - struct packet_s {
    - uint8_t type;
    - uint32_t length;
    - } GG_PACKED;
    - struct packet_s packet;
    -
    - gg_debug(GG_DEBUG_FUNCTION, "++ gg_dcc_voice_send(%p, %p, %d);\n", d, buf, length);
    - if (!d || !buf || length < 0 || d->type != GG_SESSION_DCC_VOICE) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() invalid argument\n");
    - errno = EINVAL;
    - return -1;
    - }
    -
    - packet.type = 0x03; /* XXX */
    - packet.length = gg_fix32(length);
    -
    - if (send(d->fd, &packet, sizeof(packet), 0) < (signed)sizeof(packet)) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() send() failed\n");
    - return -1;
    - }
    - gg_dcc_debug_data("write", d->fd, &packet, sizeof(packet));
    -
    - if (send(d->fd, buf, length, 0) < length) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() send() failed\n");
    - return -1;
    - }
    - gg_dcc_debug_data("write", d->fd, buf, length);
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Odbiera dane z połączenia bezpośredniego z obsługą błędów.
    - *
    - * \param fd Deskryptor gniazda
    - * \param buf Bufor na dane
    - * \param size Rozmiar bufora na dane
    - */
    -#define gg_dcc_read(fd, buf, size) \
    -{ \
    - int _tmp = recv(fd, buf, size, 0); \
    - \
    - if (_tmp < (int) size) { \
    - if (_tmp == -1) { \
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed " \
    - "(errno=%d, %s)\n", errno, strerror(errno)); \
    - } else if (_tmp == 0) { \
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed, " \
    - "connection broken\n"); \
    - } else { \
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() failed " \
    - "(%d bytes, %" GG_SIZE_FMT " needed)\n", \
    - _tmp, size); \
    - } \
    - e->type = GG_EVENT_DCC_ERROR; \
    - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \
    - return e; \
    - } \
    - gg_dcc_debug_data("read", fd, buf, size); \
    -}
    -
    -/**
    - * \internal Wysyła dane do połączenia bezpośredniego z obsługą błędów.
    - *
    - * \param fd Deskryptor gniazda
    - * \param buf Bufor z danymi
    - * \param size Rozmiar bufora z danymi
    - */
    -#define gg_dcc_write(fd, buf, size) \
    -{ \
    - int write_res; \
    - gg_dcc_debug_data("write", fd, buf, size); \
    - write_res = send(fd, buf, size, 0); \
    - if (write_res < (int) size) { \
    - if (write_res == -1) { \
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() send() " \
    - "failed (errno=%d, %s)\n", errno, strerror(errno)); \
    - } else { \
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() send() " \
    - "failed (%" GG_SIZE_FMT " needed, %d done)\n", \
    - size, write_res); \
    - } \
    - e->type = GG_EVENT_DCC_ERROR; \
    - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \
    - return e; \
    - } \
    -}
    -
    -/**
    - * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
    - *
    - * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
    - * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
    - * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free.
    - *
    - * \param h Struktura połączenia
    - *
    - * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
    - *
    - * \ingroup dcc6
    - */
    -struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
    -{
    - struct gg_event *e;
    - int foo;
    -
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_watch_fd(%p);\n", h);
    -
    - if (!h || (h->type != GG_SESSION_DCC &&
    - h->type != GG_SESSION_DCC_SOCKET &&
    - h->type != GG_SESSION_DCC_SEND &&
    - h->type != GG_SESSION_DCC_GET &&
    - h->type != GG_SESSION_DCC_VOICE))
    - {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid argument\n");
    - errno = EINVAL;
    - return NULL;
    - }
    -
    - if (!(e = (void*) calloc(1, sizeof(*e)))) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory\n");
    - return NULL;
    - }
    -
    - e->type = GG_EVENT_NONE;
    -
    - if (h->type == GG_SESSION_DCC_SOCKET) {
    - struct sockaddr_in sin;
    - struct gg_dcc *c;
    - int fd;
    - socklen_t sin_len = sizeof(sin);
    -
    - if ((fd = accept(h->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't "
    - "accept() new connection (errno=%d, %s)\n",
    - errno, strerror(errno));
    - return e;
    - }
    -
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() new direct "
    - "connection from %s:%d\n", inet_ntoa(sin.sin_addr),
    - htons(sin.sin_port));
    -
    - if (!gg_fd_set_nonblocking(fd)) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't set"
    - " nonblocking (errno=%d, %s)\n",
    - errno, strerror(errno));
    - close(fd);
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
    - return e;
    - }
    -
    - if (!(c = (void*) calloc(1, sizeof(*c)))) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory for client data\n");
    -
    - free(e);
    - close(fd);
    - return NULL;
    - }
    -
    - c->fd = fd;
    - c->check = GG_CHECK_READ;
    - c->state = GG_STATE_READING_UIN_1;
    - c->type = GG_SESSION_DCC;
    - c->timeout = GG_DEFAULT_TIMEOUT;
    - c->file_fd = -1;
    - c->remote_addr = sin.sin_addr.s_addr;
    - c->remote_port = ntohs(sin.sin_port);
    -
    - e->type = GG_EVENT_DCC_NEW;
    - e->event.dcc_new = c;
    -
    - return e;
    - } else {
    - struct gg_dcc_tiny_packet tiny_pkt;
    - struct gg_dcc_small_packet small_pkt;
    - struct gg_dcc_big_packet big_pkt;
    - int size, tmp, res;
    - unsigned int utmp;
    - socklen_t res_size = sizeof(res);
    - char buf[1024], ack[] = "UDAG";
    - void *tmp_buf;
    -
    - struct gg_dcc_file_info_packet {
    - struct gg_dcc_big_packet big;
    - struct gg_file_info file_info;
    - } GG_PACKED;
    - struct gg_dcc_file_info_packet file_info_packet;
    -
    - switch (h->state) {
    - case GG_STATE_READING_UIN_1:
    - case GG_STATE_READING_UIN_2:
    - {
    - uin_t uin;
    -
    - gg_debug(GG_DEBUG_MISC,
    - "// gg_dcc_watch_fd() GG_READING_UIN_%d\n",
    - (h->state == GG_STATE_READING_UIN_1) ? 1 : 2);
    -
    - gg_dcc_read(h->fd, &uin, sizeof(uin));
    -
    - if (h->state == GG_STATE_READING_UIN_1) {
    - h->state = GG_STATE_READING_UIN_2;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    - h->peer_uin = gg_fix32(uin);
    - } else {
    - h->state = GG_STATE_SENDING_ACK;
    - h->check = GG_CHECK_WRITE;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    - h->uin = gg_fix32(uin);
    - e->type = GG_EVENT_DCC_CLIENT_ACCEPT;
    - }
    -
    - return e;
    - }
    -
    - case GG_STATE_SENDING_ACK:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n");
    -
    - gg_dcc_write(h->fd, ack, (size_t)4);
    -
    - h->state = GG_STATE_READING_TYPE;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    -
    - return e;
    -
    - case GG_STATE_READING_TYPE:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_TYPE\n");
    -
    - gg_dcc_read(h->fd, &small_pkt, sizeof(small_pkt));
    -
    - small_pkt.type = gg_fix32(small_pkt.type);
    -
    - switch (small_pkt.type) {
    - case 0x0003: /* XXX */
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() callback\n");
    - h->type = GG_SESSION_DCC_SEND;
    - h->state = GG_STATE_SENDING_FILE_INFO;
    - h->check = GG_CHECK_WRITE;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    -
    - e->type = GG_EVENT_DCC_CALLBACK;
    -
    - break;
    -
    - case 0x0002: /* XXX */
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() dialin\n");
    - h->type = GG_SESSION_DCC_GET;
    - h->state = GG_STATE_READING_REQUEST;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    - h->incoming = 1;
    -
    - break;
    -
    - default:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc type "
    - "(%.4x) from %u\n", small_pkt.type, h->peer_uin);
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
    - }
    -
    - return e;
    -
    - case GG_STATE_READING_REQUEST:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_REQUEST\n");
    -
    - gg_dcc_read(h->fd, &small_pkt, sizeof(small_pkt));
    -
    - small_pkt.type = gg_fix32(small_pkt.type);
    -
    - switch (small_pkt.type) {
    - case 0x0001: /* XXX */
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() file transfer request\n");
    - h->state = GG_STATE_READING_FILE_INFO;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    - break;
    -
    - case 0x0003: /* XXX */
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() voice chat request\n");
    - h->state = GG_STATE_SENDING_VOICE_ACK;
    - h->check = GG_CHECK_WRITE;
    - h->timeout = GG_DCC_TIMEOUT_VOICE_ACK;
    - h->type = GG_SESSION_DCC_VOICE;
    - e->type = GG_EVENT_DCC_NEED_VOICE_ACK;
    -
    - break;
    -
    - default:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown "
    - "dcc request (%.4x) from %u\n",
    - small_pkt.type, h->peer_uin);
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
    - }
    -
    - return e;
    -
    - case GG_STATE_READING_FILE_INFO:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_INFO\n");
    -
    - gg_dcc_read(h->fd, &file_info_packet, sizeof(file_info_packet));
    -
    - memcpy(&h->file_info, &file_info_packet.file_info, sizeof(h->file_info));
    -
    - h->file_info.mode = gg_fix32(h->file_info.mode);
    - h->file_info.size = gg_fix32(h->file_info.size);
    -
    - h->state = GG_STATE_SENDING_FILE_ACK;
    - h->check = GG_CHECK_WRITE;
    - h->timeout = GG_DCC_TIMEOUT_FILE_ACK;
    -
    - e->type = GG_EVENT_DCC_NEED_FILE_ACK;
    -
    - return e;
    -
    - case GG_STATE_SENDING_FILE_ACK:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_ACK\n");
    -
    - big_pkt.type = gg_fix32(0x0006); /* XXX */
    - big_pkt.dunno1 = gg_fix32(h->offset);
    - big_pkt.dunno2 = 0;
    -
    - gg_dcc_write(h->fd, &big_pkt, sizeof(big_pkt));
    -
    - h->state = GG_STATE_READING_FILE_HEADER;
    - h->chunk_size = sizeof(big_pkt);
    - h->chunk_offset = 0;
    - h->chunk_buf = NULL;
    - tmp_buf = malloc(sizeof(big_pkt));
    - if (!tmp_buf) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n");
    - free(e);
    - return NULL;
    - }
    - h->chunk_buf = tmp_buf;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    -
    - return e;
    -
    - case GG_STATE_SENDING_VOICE_ACK:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_ACK\n");
    -
    - tiny_pkt.type = 0x01; /* XXX */
    -
    - gg_dcc_write(h->fd, &tiny_pkt, sizeof(tiny_pkt));
    -
    - h->state = GG_STATE_READING_VOICE_HEADER;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    -
    - h->offset = 0;
    -
    - return e;
    -
    - case GG_STATE_READING_FILE_HEADER:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_HEADER\n");
    -
    - tmp = recv(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset, 0);
    -
    - if (tmp == -1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() "
    - "failed (errno=%d, %s)\n", errno, strerror(errno));
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_NET;
    - return e;
    - }
    -
    - gg_dcc_debug_data("read", h->fd,
    - h->chunk_buf + h->chunk_offset,
    - h->chunk_size - h->chunk_offset);
    -
    - h->chunk_offset += tmp;
    -
    - if (h->chunk_offset < h->chunk_size)
    - return e;
    -
    - memcpy(&big_pkt, h->chunk_buf, sizeof(big_pkt));
    - free(h->chunk_buf);
    - h->chunk_buf = NULL;
    -
    - big_pkt.type = gg_fix32(big_pkt.type);
    - h->chunk_size = gg_fix32(big_pkt.dunno1);
    - h->chunk_offset = 0;
    -
    - if (big_pkt.type == 0x0005) { /* XXX */
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() transfer refused\n");
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_REFUSED;
    - return e;
    - }
    -
    - if (h->chunk_size == 0) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() empty chunk, EOF\n");
    - e->type = GG_EVENT_DCC_DONE;
    - return e;
    - }
    -
    - h->state = GG_STATE_GETTING_FILE;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    - h->established = 1;
    -
    - return e;
    -
    - case GG_STATE_READING_VOICE_HEADER:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_HEADER\n");
    -
    - gg_dcc_read(h->fd, &tiny_pkt, sizeof(tiny_pkt));
    -
    - switch (tiny_pkt.type) {
    - case 0x03: /* XXX */
    - h->state = GG_STATE_READING_VOICE_SIZE;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    - h->established = 1;
    - break;
    - case 0x04: /* XXX */
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() "
    - "peer breaking connection\n");
    - /* XXX zwracać odpowiedni event */
    - default:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() "
    - "unknown request (%.2x)\n", tiny_pkt.type);
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
    - }
    -
    - return e;
    -
    - case GG_STATE_READING_VOICE_SIZE:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_SIZE\n");
    -
    - gg_dcc_read(h->fd, &small_pkt, sizeof(small_pkt));
    -
    - small_pkt.type = gg_fix32(small_pkt.type);
    -
    - if (small_pkt.type < 16 || small_pkt.type > sizeof(buf)) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() "
    - "invalid voice frame size (%d)\n", small_pkt.type);
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_NET;
    -
    - return e;
    - }
    -
    - h->chunk_size = small_pkt.type;
    - h->chunk_offset = 0;
    -
    - if (!(h->voice_buf = malloc(h->chunk_size))) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory for voice frame\n");
    - free(e);
    - return NULL;
    - }
    -
    - h->state = GG_STATE_READING_VOICE_DATA;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    -
    - return e;
    -
    - case GG_STATE_READING_VOICE_DATA:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_DATA\n");
    -
    - tmp = recv(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset, 0);
    - if (tmp < 1) {
    - if (tmp == -1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() "
    - "recv() failed (errno=%d, %s)\n",
    - errno, strerror(errno));
    - } else {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() "
    - "recv() failed, connection broken\n");
    - }
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_NET;
    - return e;
    - }
    -
    - gg_dcc_debug_data("read", h->fd, h->voice_buf + h->chunk_offset, tmp);
    -
    - h->chunk_offset += tmp;
    -
    - if (h->chunk_offset >= h->chunk_size) {
    - e->type = GG_EVENT_DCC_VOICE_DATA;
    - e->event.dcc_voice_data.data = (unsigned char*) h->voice_buf;
    - e->event.dcc_voice_data.length = h->chunk_size;
    - h->state = GG_STATE_READING_VOICE_HEADER;
    - h->voice_buf = NULL;
    - }
    -
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    -
    - return e;
    -
    - case GG_STATE_CONNECTING:
    - {
    - uin_t uins[2];
    -
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_CONNECTING\n");
    -
    - res = 0;
    - if ((foo = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size)) || res) {
    - gg_debug(GG_DEBUG_MISC,
    - "// gg_dcc_watch_fd() connection failed "
    - "(fd=%d,errno=%d(%s),foo=%d,res=%d(%s))\n",
    - h->fd, errno, strerror(errno), foo, res, strerror(res));
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
    - return e;
    - }
    -
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connected, sending uins\n");
    -
    - uins[0] = gg_fix32(h->uin);
    - uins[1] = gg_fix32(h->peer_uin);
    -
    - gg_dcc_write(h->fd, uins, sizeof(uins));
    -
    - h->state = GG_STATE_READING_ACK;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    -
    - return e;
    - }
    -
    - case GG_STATE_READING_ACK:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_ACK\n");
    -
    - gg_dcc_read(h->fd, buf, (size_t)4);
    -
    - if (strncmp(buf, ack, 4)) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n");
    -
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
    - return e;
    - }
    -
    - h->check = GG_CHECK_WRITE;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    - h->state = GG_STATE_SENDING_REQUEST;
    -
    - return e;
    -
    - case GG_STATE_SENDING_VOICE_REQUEST:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_REQUEST\n");
    -
    - small_pkt.type = gg_fix32(0x0003);
    -
    - gg_dcc_write(h->fd, &small_pkt, sizeof(small_pkt));
    -
    - h->state = GG_STATE_READING_VOICE_ACK;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    -
    - return e;
    -
    - case GG_STATE_SENDING_REQUEST:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_REQUEST\n");
    -
    - small_pkt.type = (h->type == GG_SESSION_DCC_GET) ?
    - gg_fix32(0x0003) : gg_fix32(0x0002); /* XXX */
    -
    - gg_dcc_write(h->fd, &small_pkt, sizeof(small_pkt));
    -
    - switch (h->type) {
    - case GG_SESSION_DCC_GET:
    - h->state = GG_STATE_READING_REQUEST;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    - break;
    -
    - case GG_SESSION_DCC_SEND:
    - h->state = GG_STATE_SENDING_FILE_INFO;
    - h->check = GG_CHECK_WRITE;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    -
    - if (h->file_fd == -1)
    - e->type = GG_EVENT_DCC_NEED_FILE_INFO;
    - break;
    -
    - case GG_SESSION_DCC_VOICE:
    - h->state = GG_STATE_SENDING_VOICE_REQUEST;
    - h->check = GG_CHECK_WRITE;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    - break;
    - }
    -
    - return e;
    -
    - case GG_STATE_SENDING_FILE_INFO:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n");
    -
    - if (h->file_fd == -1) {
    - e->type = GG_EVENT_DCC_NEED_FILE_INFO;
    - return e;
    - }
    -
    - small_pkt.type = gg_fix32(0x0001); /* XXX */
    -
    - gg_dcc_write(h->fd, &small_pkt, sizeof(small_pkt));
    -
    - file_info_packet.big.type = gg_fix32(0x0003); /* XXX */
    - file_info_packet.big.dunno1 = 0;
    - file_info_packet.big.dunno2 = 0;
    -
    - memcpy(&file_info_packet.file_info, &h->file_info, sizeof(h->file_info));
    -
    - /* zostają teraz u nas, więc odwracamy z powrotem */
    - h->file_info.size = gg_fix32(h->file_info.size);
    - h->file_info.mode = gg_fix32(h->file_info.mode);
    -
    - gg_dcc_write(h->fd, &file_info_packet, sizeof(file_info_packet));
    -
    - h->state = GG_STATE_READING_FILE_ACK;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DCC_TIMEOUT_FILE_ACK;
    -
    - return e;
    -
    - case GG_STATE_READING_FILE_ACK:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_ACK\n");
    -
    - gg_dcc_read(h->fd, &big_pkt, sizeof(big_pkt));
    -
    - /* XXX sprawdzać wynik */
    - h->offset = gg_fix32(big_pkt.dunno1);
    -
    - h->state = GG_STATE_SENDING_FILE_HEADER;
    - h->check = GG_CHECK_WRITE;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    -
    - e->type = GG_EVENT_DCC_ACK;
    -
    - return e;
    -
    - case GG_STATE_READING_VOICE_ACK:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_ACK\n");
    -
    - gg_dcc_read(h->fd, &tiny_pkt, sizeof(tiny_pkt));
    -
    - if (tiny_pkt.type != 0x01) {
    - gg_debug(GG_DEBUG_MISC, "// invalid "
    - "reply (%.2x), connection "
    - "refused\n", tiny_pkt.type);
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_REFUSED;
    - return e;
    - }
    -
    - h->state = GG_STATE_READING_VOICE_HEADER;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    -
    - e->type = GG_EVENT_DCC_ACK;
    -
    - return e;
    -
    - case GG_STATE_SENDING_FILE_HEADER:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_HEADER\n");
    -
    - h->chunk_offset = 0;
    -
    - if ((h->chunk_size = h->file_info.size - h->offset) > 4096) {
    - h->chunk_size = 4096;
    - big_pkt.type = gg_fix32(0x0003); /* XXX */
    - } else
    - big_pkt.type = gg_fix32(0x0002); /* XXX */
    -
    - big_pkt.dunno1 = gg_fix32(h->chunk_size);
    - big_pkt.dunno2 = 0;
    -
    - gg_dcc_write(h->fd, &big_pkt, sizeof(big_pkt));
    -
    - h->state = GG_STATE_SENDING_FILE;
    - h->check = GG_CHECK_WRITE;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    - h->established = 1;
    -
    - return e;
    -
    - case GG_STATE_SENDING_FILE:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE\n");
    -
    - if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf))
    - utmp = sizeof(buf);
    -
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() "
    - "offset=%d, size=%d\n",
    - h->offset, h->file_info.size);
    -
    - /* koniec pliku? */
    - if (h->file_info.size == 0) {
    - gg_debug(GG_DEBUG_MISC,
    - "// gg_dcc_watch_fd() read()"
    - "reached eof on empty file\n");
    - e->type = GG_EVENT_DCC_DONE;
    -
    - return e;
    - }
    -
    - if (h->offset >= h->file_info.size) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n");
    - e->type = GG_EVENT_DCC_DONE;
    - return e;
    - }
    -
    - if (lseek(h->file_fd, h->offset, SEEK_SET) != (off_t)h->offset) {
    - gg_debug(GG_DEBUG_MISC,
    - "// gg_dcc_watch_fd() lseek() "
    - "failed. (errno=%d, %s)\n",
    - errno, strerror(errno));
    -
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_FILE;
    -
    - return e;
    - }
    -
    - size = read(h->file_fd, buf, utmp);
    -
    - /* błąd */
    - if (size == -1) {
    - gg_debug(GG_DEBUG_MISC,
    - "// gg_dcc_watch_fd() read() "
    - "failed. (errno=%d, %s)\n",
    - errno, strerror(errno));
    -
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_FILE;
    -
    - return e;
    - }
    -
    - /* koniec pliku? */
    - if (size == 0) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n");
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_EOF;
    -
    - return e;
    - }
    -
    - /* jeśli wczytaliśmy więcej, utnijmy. */
    - if (h->offset + size > h->file_info.size) {
    - gg_debug(GG_DEBUG_MISC,
    - "// gg_dcc_watch_fd() read() "
    - "too much (read=%d, ofs=%d, "
    - "size=%d)\n", size, h->offset,
    - h->file_info.size);
    - size = h->file_info.size - h->offset;
    -
    - if (size < 1) {
    - gg_debug(GG_DEBUG_MISC,
    - "// gg_dcc_watch_fd() "
    - "reached EOF after cutting\n");
    - e->type = GG_EVENT_DCC_DONE;
    - return e;
    - }
    - }
    -
    - tmp = send(h->fd, buf, size, 0);
    -
    - if (tmp == -1) {
    - gg_debug(GG_DEBUG_MISC,
    - "// gg_dcc_watch_fd() send() "
    - "failed (%s)\n", strerror(errno));
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_NET;
    - return e;
    - }
    -
    - if (tmp == 0) {
    - gg_debug(GG_DEBUG_MISC,
    - "// gg_dcc_watch_fd() send() "
    - "failed (connection reset)\n");
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_NET;
    - return e;
    - }
    -
    - h->offset += tmp;
    -
    - if (h->offset >= h->file_info.size) {
    - e->type = GG_EVENT_DCC_DONE;
    - return e;
    - }
    -
    - h->chunk_offset += tmp;
    -
    - if (h->chunk_offset >= h->chunk_size) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n");
    - h->state = GG_STATE_SENDING_FILE_HEADER;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    - } else {
    - h->state = GG_STATE_SENDING_FILE;
    - h->timeout = GG_DCC_TIMEOUT_SEND;
    - }
    -
    - h->check = GG_CHECK_WRITE;
    -
    - return e;
    -
    - case GG_STATE_GETTING_FILE:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_GETTING_FILE\n");
    -
    - if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf))
    - utmp = sizeof(buf);
    -
    - if (h->offset >= h->file_info.size) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n");
    - e->type = GG_EVENT_DCC_DONE;
    - return e;
    - }
    -
    - size = recv(h->fd, buf, utmp, 0);
    -
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() "
    - "ofs=%d, size=%d, recv()=%d\n",
    - h->offset, h->file_info.size, size);
    -
    - /* błąd */
    - if (size == -1) {
    - gg_debug(GG_DEBUG_MISC,
    - "// gg_dcc_watch_fd() recv() "
    - "failed. (errno=%d, %s)\n",
    - errno, strerror(errno));
    -
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_NET;
    -
    - return e;
    - }
    -
    - /* koniec? */
    - if (size == 0) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() recv() reached eof\n");
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_EOF;
    -
    - return e;
    - }
    -
    - tmp = write(h->file_fd, buf, size);
    -
    - if (tmp == -1 || tmp < size) {
    - gg_debug(GG_DEBUG_MISC,
    - "// gg_dcc_watch_fd() write() "
    - "failed (%d:fd=%d:res=%d:%s)\n",
    - tmp, h->file_fd, size,
    - strerror(errno));
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_NET;
    - return e;
    - }
    -
    - h->offset += size;
    -
    - if (h->offset >= h->file_info.size) {
    - e->type = GG_EVENT_DCC_DONE;
    - return e;
    - }
    -
    - h->chunk_offset += size;
    -
    - if (h->chunk_offset >= h->chunk_size) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n");
    - h->state = GG_STATE_READING_FILE_HEADER;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    - h->chunk_offset = 0;
    - h->chunk_size = sizeof(big_pkt);
    - h->chunk_buf = NULL;
    - tmp_buf = malloc(sizeof(big_pkt));
    - if (!tmp_buf) {
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n");
    - free(e);
    - return NULL;
    - }
    - h->chunk_buf = tmp_buf;
    - } else {
    - h->state = GG_STATE_GETTING_FILE;
    - h->timeout = GG_DCC_TIMEOUT_GET;
    - }
    -
    - h->check = GG_CHECK_READ;
    -
    - return e;
    -
    - default:
    - gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_???\n");
    - e->type = GG_EVENT_DCC_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
    -
    - return e;
    - }
    - }
    -
    - return e;
    -}
    -
    -/**
    - * Zwalnia zasoby używane przez połączenie bezpośrednie.
    - *
    - * \param d Struktura połączenia
    - *
    - * \ingroup dcc6
    - */
    -void gg_dcc_free(struct gg_dcc *d)
    -{
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_free(%p);\n", d);
    -
    - if (!d)
    - return;
    -
    - if (d->fd != -1)
    - close(d->fd);
    -
    - if (d->file_fd != -1)
    - gg_file_close(d->file_fd);
    -
    - free(d->chunk_buf);
    - free(d);
    -}
    -
    -/*
    - * Local variables:
    - * c-indentation-style: k&r
    - * c-basic-offset: 8
    - * indent-tabs-mode: notnil
    - * End:
    - *
    - * vim: shiftwidth=8:
    - */
    --- a/libpurple/protocols/gg/lib/dcc7.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1660 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
    - * Tomasz Chiliński <chilek@chilan.com>
    - * Adam Wysocki <gophi@ekg.chmurka.net>
    - * Bartłomiej Zimoń <uzi18@o2.pl>
    - *
    - * Thanks to Jakub Zawadzki <darkjames@darkjames.ath.cx>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110,
    - * USA.
    - */
    -
    -/**
    - * \file dcc7.c
    - *
    - * \brief Obsługa połączeń bezpośrednich od wersji Gadu-Gadu 7.x
    - */
    -
    -#include "fileio.h"
    -#include "network.h"
    -#include "strman.h"
    -
    -#include <ctype.h>
    -#include <errno.h>
    -#include <string.h>
    -#include <stdlib.h>
    -#include <time.h>
    -
    -#include "libgadu.h"
    -#include "protocol.h"
    -#include "resolver.h"
    -#include "internal.h"
    -#include "debug.h"
    -
    -#ifdef _MSC_VER
    -# define gg_debug_dcc(dcc, level, fmt, ...) \
    - gg_debug_session(((dcc) != NULL) ? (dcc)->sess : NULL, level, fmt, __VA_ARGS__)
    -#else
    -# define gg_debug_dcc(dcc, level, fmt...) \
    - gg_debug_session(((dcc) != NULL) ? (dcc)->sess : NULL, level, fmt)
    -#endif
    -
    -#define gg_debug_dump_dcc(dcc, level, buf, len) \
    - gg_debug_dump(((dcc) != NULL) ? (dcc)->sess : NULL, level, buf, len)
    -
    -/**
    - * \internal Dodaje połączenie bezpośrednie do sesji.
    - *
    - * \param sess Struktura sesji
    - * \param dcc Struktura połączenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -static int gg_dcc7_session_add(struct gg_session *sess, struct gg_dcc7 *dcc)
    -{
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_add(%p, %p)\n", sess, dcc);
    -
    - if (!sess || !dcc || dcc->next) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_add() invalid parameters\n");
    - errno = EINVAL;
    - return -1;
    - }
    -
    - dcc->next = sess->dcc7_list;
    - sess->dcc7_list = dcc;
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Usuwa połączenie bezpośrednie z sesji.
    - *
    - * \param sess Struktura sesji
    - * \param dcc Struktura połączenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -static int gg_dcc7_session_remove(struct gg_session *sess, struct gg_dcc7 *dcc)
    -{
    - struct gg_dcc7 *tmp;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_remove(%p, %p)\n", sess, dcc);
    -
    - if (sess == NULL || dcc == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n");
    - errno = EINVAL;
    - return -1;
    - }
    -
    - if (sess->dcc7_list == dcc) {
    - sess->dcc7_list = dcc->next;
    - dcc->next = NULL;
    - return 0;
    - }
    -
    - for (tmp = sess->dcc7_list; tmp != NULL; tmp = tmp->next) {
    - if (tmp->next == dcc) {
    - tmp->next = dcc->next;
    - dcc->next = NULL;
    - return 0;
    - }
    - }
    -
    - errno = ENOENT;
    - return -1;
    -}
    -
    -/**
    - * \internal Zwraca strukturę połączenia o danym identyfikatorze.
    - *
    - * \param sess Struktura sesji
    - * \param id Identyfikator połączenia
    - * \param uin Numer nadawcy lub odbiorcy
    - *
    - * \return Struktura połączenia lub \c NULL jeśli nie znaleziono
    - */
    -static struct gg_dcc7 *gg_dcc7_session_find(struct gg_session *sess, gg_dcc7_id_t id, uin_t uin)
    -{
    - struct gg_dcc7 *tmp;
    - int empty;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_find(%p, ..., %d)\n", sess, (int) uin);
    -
    - empty = !memcmp(&id, "\0\0\0\0\0\0\0\0", 8);
    -
    - for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
    - if (empty) {
    - if (tmp->peer_uin == uin && tmp->state == GG_STATE_WAITING_FOR_ACCEPT)
    - return tmp;
    - } else {
    - if (!memcmp(&tmp->cid, &id, sizeof(id)))
    - return tmp;
    - }
    - }
    -
    - return NULL;
    -}
    -
    -/**
    - * \internal Rozpoczyna proces pobierania adresu
    - *
    - * \param dcc Struktura połączenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -static int gg_dcc7_get_relay_addr(struct gg_dcc7 *dcc)
    -{
    - gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_get_relay_addr(%p)\n", dcc);
    -
    - if (dcc == NULL || dcc->sess == NULL) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() invalid parameters\n");
    - errno = EINVAL;
    - return -1;
    - }
    -
    - if (dcc->sess->resolver_start(&dcc->fd, &dcc->resolver, GG_RELAY_HOST) == -1) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() "
    - "resolving failed (errno=%d, %s)\n",
    - errno, strerror(errno));
    - return -1;
    - }
    -
    - dcc->state = GG_STATE_RESOLVING_RELAY;
    - dcc->check = GG_CHECK_READ;
    - dcc->timeout = GG_DEFAULT_TIMEOUT;
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Nawiązuje połączenie bezpośrednie
    - *
    - * \param dcc Struktura połączenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -static int gg_dcc7_connect(struct gg_dcc7 *dcc)
    -{
    - gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p)\n", dcc);
    -
    - if (dcc == NULL) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_connect() invalid parameters\n");
    - errno = EINVAL;
    - return -1;
    - }
    -
    - if ((dcc->fd = gg_connect(&dcc->remote_addr, dcc->remote_port, 1)) == -1) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_connect() connection failed\n");
    - return -1;
    - }
    -
    - dcc->state = GG_STATE_CONNECTING;
    - dcc->check = GG_CHECK_WRITE;
    - dcc->timeout = GG_DCC7_TIMEOUT_CONNECT;
    - dcc->soft_timeout = 1;
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Tworzy gniazdo nasłuchujące dla połączenia bezpośredniego
    - *
    - * \param dcc Struktura połączenia
    - * \param addr Preferowany adres (jeśli równy 0, nasłuchujemy na wszystkich interfejsach)
    - * \param port Preferowany port (jeśli równy 0, nasłuchujemy na losowym)
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -static int gg_dcc7_listen(struct gg_dcc7 *dcc, uint32_t addr, uint16_t port)
    -{
    - struct sockaddr_in sin;
    - socklen_t sin_len = sizeof(sin);
    - int errsv;
    - int fd;
    -
    - gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen(%p, %d)\n", dcc, port);
    -
    - if (!dcc) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() invalid parameters\n");
    - errno = EINVAL;
    - return -1;
    - }
    -
    - if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() can't create socket (%s)\n", strerror(errno));
    - return -1;
    - }
    -
    - memset(&sin, 0, sizeof(sin));
    - sin.sin_family = AF_INET;
    - sin.sin_addr.s_addr = addr;
    - sin.sin_port = htons(port);
    -
    - if (bind(fd, (struct sockaddr*) &sin, sizeof(sin)) == -1) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to"
    - " bind to %s:%d\n", inet_ntoa(sin.sin_addr), port);
    - goto fail;
    - }
    -
    - if (port == 0 && getsockname(fd, (struct sockaddr*) &sin, &sin_len) == -1) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to bind to port %d\n", port);
    - goto fail;
    - }
    -
    - if (listen(fd, 1)) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to listen (%s)\n", strerror(errno));
    - goto fail;
    - }
    -
    - dcc->fd = fd;
    - dcc->local_addr = sin.sin_addr.s_addr;
    - dcc->local_port = ntohs(sin.sin_port);
    -
    - dcc->state = GG_STATE_LISTENING;
    - dcc->check = GG_CHECK_READ;
    - dcc->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
    -
    - return 0;
    -
    -fail:
    - errsv = errno;
    - close(fd);
    - errno = errsv;
    - return -1;
    -}
    -
    -/**
    - * \internal Tworzy gniazdo nasłuchujące i wysyła jego parametry
    - *
    - * \param dcc Struktura połączenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc)
    -{
    - struct gg_dcc7_info pkt;
    - uint16_t external_port;
    - uint32_t external_addr;
    - struct in_addr addr;
    -
    - gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc);
    -
    - if (gg_dcc7_listen(dcc, dcc->sess->client_addr, dcc->sess->client_port) == -1)
    - return -1;
    -
    - if (dcc->sess->external_port != 0)
    - external_port = dcc->sess->external_port;
    - else
    - external_port = dcc->local_port;
    -
    - if (dcc->sess->external_addr != 0)
    - external_addr = dcc->sess->external_addr;
    - else
    - external_addr = dcc->local_addr;
    -
    - addr.s_addr = external_addr;
    -
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// dcc7_listen_and_send_info() "
    - "sending IP address %s and port %d\n",
    - inet_ntoa(addr), external_port);
    -
    - memset(&pkt, 0, sizeof(pkt));
    - pkt.uin = gg_fix32(dcc->peer_uin);
    - pkt.type = GG_DCC7_TYPE_P2P;
    - pkt.id = dcc->cid;
    - snprintf((char*) pkt.info, sizeof(pkt.info), "%s %d", inet_ntoa(addr), external_port);
    - snprintf((char*) pkt.hash, sizeof(pkt.hash), "%u", external_addr + external_port * rand());
    -
    - return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt, sizeof(pkt), NULL);
    -}
    -
    -/**
    - * \internal Odwraca połączenie po nieudanym connect()
    - *
    - * \param dcc Struktura połączenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -static int gg_dcc7_reverse_connect(struct gg_dcc7 *dcc)
    -{
    - gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reverse_connect(%p)\n", dcc);
    -
    - if (dcc == NULL)
    - return -1;
    -
    - if (dcc->reverse) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() already reverse connection\n");
    - return -1;
    - }
    -
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() timeout, trying reverse connection\n");
    - close(dcc->fd);
    - dcc->fd = -1;
    - dcc->reverse = 1;
    -
    - return gg_dcc7_listen_and_send_info(dcc);
    -}
    -
    -/**
    - * \internal Wysyła do serwera żądanie nadania identyfikatora sesji
    - *
    - * \param sess Struktura sesji
    - * \param type Rodzaj połączenia (\c GG_DCC7_TYPE_FILE lub \c GG_DCC7_TYPE_VOICE)
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -static int gg_dcc7_request_id(struct gg_session *sess, uint32_t type)
    -{
    - struct gg_dcc7_id_request pkt;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_request_id(%p, %d)\n", sess, type);
    -
    - if (!sess) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid parameters\n");
    - errno = EFAULT;
    - return -1;
    - }
    -
    - if (sess->state != GG_STATE_CONNECTED) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() not connected\n");
    - errno = ENOTCONN;
    - return -1;
    - }
    -
    - if (type != GG_DCC7_TYPE_VOICE && type != GG_DCC7_TYPE_FILE) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid transfer type (%d)\n", type);
    - errno = EINVAL;
    - return -1;
    - }
    -
    - memset(&pkt, 0, sizeof(pkt));
    - pkt.type = gg_fix32(type);
    -
    - return gg_send_packet(sess, GG_DCC7_ID_REQUEST, &pkt, sizeof(pkt), NULL);
    -}
    -
    -/**
    - * \internal Rozpoczyna wysyłanie pliku.
    - *
    - * Funkcja jest wykorzystywana przez \c gg_dcc7_send_file() oraz
    - * \c gg_dcc_send_file_fd().
    - *
    - * \param sess Struktura sesji
    - * \param rcpt Numer odbiorcy
    - * \param fd Deskryptor pliku
    - * \param size Rozmiar pliku
    - * \param filename1250 Nazwa pliku w kodowaniu CP-1250
    - * \param hash Skrót SHA-1 pliku
    - * \param seek Flaga mówiąca, czy można używać lseek()
    - *
    - * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
    - *
    - * \ingroup dcc7
    - */
    -static struct gg_dcc7 *gg_dcc7_send_file_common(struct gg_session *sess,
    - uin_t rcpt, int fd, size_t size, const char *filename1250,
    - const char *hash, int seek)
    -{
    - struct gg_dcc7 *dcc = NULL;
    -
    - if (!sess || !rcpt || !filename1250 || !hash || fd == -1) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() invalid parameters\n");
    - errno = EINVAL;
    - goto fail;
    - }
    -
    - if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() not enough memory\n");
    - goto fail;
    - }
    -
    - if (gg_dcc7_request_id(sess, GG_DCC7_TYPE_FILE) == -1)
    - goto fail;
    -
    - memset(dcc, 0, sizeof(struct gg_dcc7));
    - dcc->type = GG_SESSION_DCC7_SEND;
    - dcc->dcc_type = GG_DCC7_TYPE_FILE;
    - dcc->state = GG_STATE_REQUESTING_ID;
    - dcc->timeout = GG_DEFAULT_TIMEOUT;
    - dcc->sess = sess;
    - dcc->fd = -1;
    - dcc->uin = sess->uin;
    - dcc->peer_uin = rcpt;
    - dcc->file_fd = fd;
    - dcc->size = size;
    - dcc->seek = seek;
    -
    - strncpy((char*) dcc->filename, filename1250, GG_DCC7_FILENAME_LEN);
    - dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
    -
    - memcpy(dcc->hash, hash, GG_DCC7_HASH_LEN);
    -
    - if (gg_dcc7_session_add(sess, dcc) == -1)
    - goto fail;
    -
    - return dcc;
    -
    -fail:
    - free(dcc);
    - return NULL;
    -}
    -
    -/**
    - * Rozpoczyna wysyłanie pliku o danej nazwie.
    - *
    - * \param sess Struktura sesji
    - * \param rcpt Numer odbiorcy
    - * \param filename Nazwa pliku w lokalnym systemie plików
    - * \param filename1250 Nazwa pliku w kodowaniu CP-1250
    - * \param hash Skrót SHA-1 pliku (lub \c NULL jeśli ma być wyznaczony)
    - *
    - * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
    - *
    - * \ingroup dcc7
    - */
    -struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt,
    - const char *filename, const char *filename1250, const char *hash)
    -{
    - struct gg_dcc7 *dcc = NULL;
    - const char *tmp;
    - char hash_buf[GG_DCC7_HASH_LEN];
    - struct stat st;
    - int fd = -1;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file(%p, %d,"
    - " \"%s\", %p)\n", sess, rcpt, filename, hash);
    -
    - if (!sess || !rcpt || !filename) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() invalid parameters\n");
    - errno = EINVAL;
    - goto fail;
    - }
    -
    - if (!filename1250)
    - filename1250 = filename;
    -
    - if ((fd = open(filename, O_RDONLY)) == -1) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() open() failed (%s)\n", strerror(errno));
    - goto fail;
    - }
    -
    - if (fstat(fd, &st) == -1) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() "
    - "fstat() failed (%s)\n", strerror(errno));
    - goto fail;
    - }
    -
    - if ((st.st_mode & S_IFDIR)) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() that's a directory\n");
    - errno = EINVAL;
    - goto fail;
    - }
    -
    - if (!hash) {
    - if (gg_file_hash_sha1(fd, (uint8_t*) hash_buf) == -1)
    - goto fail;
    -
    - hash = hash_buf;
    - }
    -
    - if ((tmp = strrchr(filename1250, '/')))
    - filename1250 = tmp + 1;
    -
    - if (!(dcc = gg_dcc7_send_file_common(sess, rcpt, fd, st.st_size, filename1250, hash, 1)))
    - goto fail;
    -
    - return dcc;
    -
    -fail:
    - if (fd != -1) {
    - int errsv = errno;
    - gg_file_close(fd);
    - errno = errsv;
    - }
    -
    - free(dcc);
    - return NULL;
    -}
    -
    -/**
    - * \internal Rozpoczyna wysyłanie pliku o danym deskryptorze.
    - *
    - * \note Wysyłanie pliku nie będzie działać poprawnie, jeśli deskryptor
    - * źródłowy jest w trybie nieblokującym i w pewnym momencie zabraknie danych.
    - *
    - * \param sess Struktura sesji
    - * \param rcpt Numer odbiorcy
    - * \param fd Deskryptor pliku
    - * \param size Rozmiar pliku
    - * \param filename1250 Nazwa pliku w kodowaniu CP-1250
    - * \param hash Skrót SHA-1 pliku
    - *
    - * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu
    - *
    - * \ingroup dcc7
    - */
    -struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt,
    - int fd, size_t size, const char *filename1250, const char *hash)
    -{
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file_fd(%p, "
    - "%d, %d, %" GG_SIZE_FMT ", \"%s\", %p)\n",
    - sess, rcpt, fd, size, filename1250, hash);
    -
    - return gg_dcc7_send_file_common(sess, rcpt, fd, size, filename1250, hash, 0);
    -}
    -
    -
    -/**
    - * Potwierdza chęć odebrania pliku.
    - *
    - * \param dcc Struktura połączenia
    - * \param offset Początkowy offset przy wznawianiu przesyłania pliku
    - *
    - * \note Biblioteka nie zmienia położenia w odbieranych plikach. Jeśli offset
    - * początkowy jest różny od zera, należy ustawić go funkcją \c lseek() lub
    - * podobną.
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup dcc7
    - */
    -int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset)
    -{
    - struct gg_dcc7_accept pkt;
    -
    - gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_accept(%p, %d)\n", dcc, offset);
    -
    - if (!dcc || !dcc->sess) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_accept() invalid parameters\n");
    - errno = EFAULT;
    - return -1;
    - }
    -
    - memset(&pkt, 0, sizeof(pkt));
    - pkt.uin = gg_fix32(dcc->peer_uin);
    - pkt.id = dcc->cid;
    - pkt.offset = gg_fix32(offset);
    -
    - if (gg_send_packet(dcc->sess, GG_DCC7_ACCEPT, &pkt, sizeof(pkt), NULL) == -1)
    - return -1;
    -
    - dcc->offset = offset;
    -
    - return gg_dcc7_listen_and_send_info(dcc);
    -}
    -
    -/**
    - * Odrzuca próbę przesłania pliku.
    - *
    - * \param dcc Struktura połączenia
    - * \param reason Powód odrzucenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup dcc7
    - */
    -int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason)
    -{
    - struct gg_dcc7_reject pkt;
    -
    - gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reject(%p, %d)\n", dcc, reason);
    -
    - if (!dcc || !dcc->sess) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reject() invalid parameters\n");
    - errno = EFAULT;
    - return -1;
    - }
    -
    - memset(&pkt, 0, sizeof(pkt));
    - pkt.uin = gg_fix32(dcc->peer_uin);
    - pkt.id = dcc->cid;
    - pkt.reason = gg_fix32(reason);
    -
    - return gg_send_packet(dcc->sess, GG_DCC7_REJECT, &pkt, sizeof(pkt), NULL);
    -}
    -
    -/**
    - * \internal Obsługuje pakiet identyfikatora połączenia bezpośredniego.
    - *
    - * \param sess Struktura sesji
    - * \param e Struktura zdarzenia
    - * \param payload Treść pakietu
    - * \param len Długość pakietu
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
    -{
    - const struct gg_dcc7_id_reply *p = payload;
    - struct gg_dcc7 *tmp;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_id(%p, %p, %p, %d)\n", sess, e, payload, len);
    -
    - for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// checking dcc %p, "
    - "state %d, type %d\n", tmp, tmp->state, tmp->dcc_type);
    -
    - if (tmp->state != GG_STATE_REQUESTING_ID || tmp->dcc_type != (int) gg_fix32(p->type))
    - continue;
    -
    - tmp->cid = p->id;
    -
    - switch (tmp->dcc_type) {
    - case GG_DCC7_TYPE_FILE:
    - {
    - struct gg_dcc7_new s;
    -
    - memset(&s, 0, sizeof(s));
    - s.id = tmp->cid;
    - s.type = gg_fix32(GG_DCC7_TYPE_FILE);
    - s.uin_from = gg_fix32(tmp->uin);
    - s.uin_to = gg_fix32(tmp->peer_uin);
    - s.size = gg_fix32(tmp->size);
    -
    - /* Uwaga: To nie jest ciąg kończony zerem.
    - * Note: This is not a null-terminated string. */
    - GG_STATIC_ASSERT(
    - sizeof(s.filename) == sizeof(tmp->filename) - 1,
    - filename_sizes_does_not_match);
    - memcpy((char*)s.filename, (char*)tmp->filename, sizeof(s.filename));
    -
    - tmp->state = GG_STATE_WAITING_FOR_ACCEPT;
    - tmp->timeout = GG_DCC7_TIMEOUT_FILE_ACK;
    -
    - return gg_send_packet(sess, GG_DCC7_NEW, &s, sizeof(s), NULL);
    - }
    - }
    - }
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet akceptacji połączenia bezpośredniego.
    - *
    - * \param sess Struktura sesji
    - * \param e Struktura zdarzenia
    - * \param payload Treść pakietu
    - * \param len Długość pakietu
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
    -{
    - const struct gg_dcc7_accept *p = payload;
    - struct gg_dcc7 *dcc;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_accept(%p, %p, %p, %d)\n", sess, e, payload, len);
    -
    - if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() unknown dcc session\n");
    - /* XXX wysłać reject? */
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
    - return 0;
    - }
    -
    - if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() invalid state\n");
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
    - return 0;
    - }
    -
    - /* XXX czy dla odwrotnego połączenia powinniśmy wywołać już zdarzenie GG_DCC7_ACCEPT? */
    -
    - dcc->offset = gg_fix32(p->offset);
    - dcc->state = GG_STATE_WAITING_FOR_INFO;
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet informacji o połączeniu bezpośrednim.
    - *
    - * \param sess Struktura sesji
    - * \param e Struktura zdarzenia
    - * \param payload Treść pakietu
    - * \param len Długość pakietu
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
    -{
    - const struct gg_dcc7_info *p = payload;
    - struct gg_dcc7 *dcc;
    - char *tmp;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_info(%p, %p, %p, %d)\n", sess, e, payload, len);
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "// gg_dcc7_handle_info() "
    - "received address: %s, hash: %s\n", p->info, p->hash);
    -
    - if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unknown dcc session\n");
    - return 0;
    - }
    -
    - if (dcc->state == GG_STATE_CONNECTED) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() state is already connected\n");
    - return 0;
    - }
    -
    - switch (p->type)
    - {
    - case GG_DCC7_TYPE_P2P:
    - if ((dcc->remote_addr = inet_addr(p->info)) == INADDR_NONE) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP address\n");
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
    - return 0;
    - }
    -
    - if (!(tmp = strchr(p->info, ' ')) || !(dcc->remote_port = atoi(tmp + 1))) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP port\n");
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
    - return 0;
    - }
    -
    - if (dcc->state == GG_STATE_WAITING_FOR_INFO) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_dcc7_handle_info() waiting for info "
    - "so send one\n");
    - gg_dcc7_listen_and_send_info(dcc);
    - e->type = GG_EVENT_DCC7_PENDING;
    - e->event.dcc7_pending.dcc7 = dcc;
    - return 0;
    - }
    -
    - break;
    -
    - case GG_DCC7_TYPE_SERVER:
    - if (!(tmp = strstr(p->info, "GG"))) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unknown info packet\n");
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
    - return 0;
    - }
    -
    -#if defined(HAVE__STRTOUI64) || defined(HAVE_STRTOULL)
    - {
    - uint64_t cid;
    -
    -# ifdef HAVE__STRTOUI64
    - cid = _strtoui64(tmp + 2, NULL, 0);
    -# else
    - cid = strtoull(tmp + 2, NULL, 0);
    -# endif
    -
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_dcc7_handle_info() info.str=%s, "
    - "info.id=%llu, sess.id=%llu\n", tmp + 2, cid,
    - *((unsigned long long*) &dcc->cid));
    -
    - cid = gg_fix64(cid);
    -
    - if (memcmp(&dcc->cid, &cid, sizeof(cid)) != 0) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid session id\n");
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
    - return 0;
    - }
    - }
    -#else
    - (void)tmp;
    -#endif
    -
    - if (gg_dcc7_get_relay_addr(dcc) == -1) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unable to retrieve relay address\n");
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
    - return 0;
    - }
    -
    - /* XXX wysyłać dopiero jeśli uda się połączyć z serwerem? */
    -
    - gg_send_packet(dcc->sess, GG_DCC7_INFO, payload, len, NULL);
    -
    - return 0;
    -
    - default:
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info()"
    - " unhandled transfer type (%d)\n", p->type);
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
    - return 0;
    - }
    -
    -#if 0
    - /* jeśli nadal czekamy na połączenie przychodzące, a druga strona nie
    - * daje rady i oferuje namiary na siebie, bierzemy co dają.
    - */
    - if (dcc->state != GG_STATE_WAITING_FOR_INFO && (dcc->state != GG_STATE_LISTENING || dcc->reverse)) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid state\n");
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
    - return 0;
    - }
    -#endif
    -
    - if (dcc->state == GG_STATE_LISTENING) {
    - close(dcc->fd);
    - dcc->fd = -1;
    - dcc->reverse = 1;
    - }
    -
    - if (dcc->type == GG_SESSION_DCC7_SEND) {
    - e->type = GG_EVENT_DCC7_ACCEPT;
    - e->event.dcc7_accept.dcc7 = dcc;
    - e->event.dcc7_accept.type = gg_fix32(p->type);
    - e->event.dcc7_accept.remote_ip = dcc->remote_addr;
    - e->event.dcc7_accept.remote_port = dcc->remote_port;
    - } else {
    - e->type = GG_EVENT_DCC7_PENDING;
    - e->event.dcc7_pending.dcc7 = dcc;
    - }
    -
    - if (gg_dcc7_connect(dcc) == -1) {
    - if (gg_dcc7_reverse_connect(dcc) == -1) {
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc7_error = GG_ERROR_DCC7_NET;
    - return 0;
    - }
    - }
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet odrzucenia połączenia bezpośredniego.
    - *
    - * \param sess Struktura sesji
    - * \param e Struktura zdarzenia
    - * \param payload Treść pakietu
    - * \param len Długość pakietu
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
    -{
    - const struct gg_dcc7_reject *p = payload;
    - struct gg_dcc7 *dcc;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_reject(%p, %p, %p, %d)\n", sess, e, payload, len);
    -
    - if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() unknown dcc session\n");
    - return 0;
    - }
    -
    - if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() invalid state\n");
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
    - return 0;
    - }
    -
    - e->type = GG_EVENT_DCC7_REJECT;
    - e->event.dcc7_reject.dcc7 = dcc;
    - e->event.dcc7_reject.reason = gg_fix32(p->reason);
    -
    - /* XXX ustawić state na rejected? */
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet nowego połączenia bezpośredniego.
    - *
    - * \param sess Struktura sesji
    - * \param e Struktura zdarzenia
    - * \param payload Treść pakietu
    - * \param len Długość pakietu
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, const void *payload, int len)
    -{
    - const struct gg_dcc7_new *p = payload;
    - struct gg_dcc7 *dcc;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_new(%p, %p, %p, %d)\n", sess, e, payload, len);
    -
    - switch (gg_fix32(p->type)) {
    - case GG_DCC7_TYPE_FILE:
    - if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() not enough memory\n");
    - return -1;
    - }
    -
    - memset(dcc, 0, sizeof(struct gg_dcc7));
    - dcc->type = GG_SESSION_DCC7_GET;
    - dcc->dcc_type = GG_DCC7_TYPE_FILE;
    - dcc->fd = -1;
    - dcc->file_fd = -1;
    - dcc->uin = sess->uin;
    - dcc->peer_uin = gg_fix32(p->uin_from);
    - dcc->cid = p->id;
    - dcc->sess = sess;
    -
    - if (gg_dcc7_session_add(sess, dcc) == -1) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_dcc7_handle_new() unable to "
    - "add to session\n");
    - gg_dcc7_free(dcc);
    - return -1;
    - }
    -
    - dcc->size = gg_fix32(p->size);
    - strncpy((char*) dcc->filename, (char*) p->filename, GG_DCC7_FILENAME_LEN);
    - dcc->filename[GG_DCC7_FILENAME_LEN] = 0;
    - memcpy(dcc->hash, p->hash, GG_DCC7_HASH_LEN);
    -
    - e->type = GG_EVENT_DCC7_NEW;
    - e->event.dcc7_new = dcc;
    -
    - break;
    -
    - case GG_DCC7_TYPE_VOICE:
    - if (!(dcc = malloc(sizeof(struct gg_dcc7)))) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_packet() not enough memory\n");
    - return -1;
    - }
    -
    - memset(dcc, 0, sizeof(struct gg_dcc7));
    -
    - dcc->type = GG_SESSION_DCC7_VOICE;
    - dcc->dcc_type = GG_DCC7_TYPE_VOICE;
    - dcc->fd = -1;
    - dcc->file_fd = -1;
    - dcc->uin = sess->uin;
    - dcc->peer_uin = gg_fix32(p->uin_from);
    - dcc->cid = p->id;
    - dcc->sess = sess;
    -
    - if (gg_dcc7_session_add(sess, dcc) == -1) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_dcc7_handle_new() unable to add "
    - "to session\n");
    - gg_dcc7_free(dcc);
    - return -1;
    - }
    -
    - e->type = GG_EVENT_DCC7_NEW;
    - e->event.dcc7_new = dcc;
    -
    - break;
    -
    - default:
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_dcc7_handle_new() unknown dcc type (%d) "
    - "from %u\n", gg_fix32(p->type),
    - gg_fix32(p->uin_from));
    -
    - break;
    - }
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Ustawia odpowiednie stany wewnętrzne w zależności od rodzaju
    - * połączenia.
    - *
    - * \param dcc Struktura połączenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu.
    - */
    -static int gg_dcc7_postauth_fixup(struct gg_dcc7 *dcc)
    -{
    - gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_postauth_fixup(%p)\n", dcc);
    -
    - if (!dcc) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_postauth_fixup() invalid parameters\n");
    - errno = EINVAL;
    - return -1;
    - }
    -
    - switch (dcc->type) {
    - case GG_SESSION_DCC7_GET:
    - dcc->state = GG_STATE_GETTING_FILE;
    - dcc->check = GG_CHECK_READ;
    - return 0;
    -
    - case GG_SESSION_DCC7_SEND:
    - dcc->state = GG_STATE_SENDING_FILE;
    - dcc->check = GG_CHECK_WRITE;
    - return 0;
    -
    - case GG_SESSION_DCC7_VOICE:
    - dcc->state = GG_STATE_READING_VOICE_DATA;
    - dcc->check = GG_CHECK_READ;
    - return 0;
    - }
    -
    - errno = EINVAL;
    -
    - return -1;
    -}
    -
    -/**
    - * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
    - *
    - * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
    - * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
    - * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free().
    - *
    - * \param dcc Struktura połączenia
    - *
    - * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
    - *
    - * \ingroup dcc7
    - */
    -struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc)
    -{
    - struct gg_event *e;
    -
    - gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_watch_fd(%p)\n", dcc);
    -
    - if (!dcc || (dcc->type != GG_SESSION_DCC7_SEND &&
    - dcc->type != GG_SESSION_DCC7_GET &&
    - dcc->type != GG_SESSION_DCC7_VOICE))
    - {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid parameters\n");
    - errno = EINVAL;
    - return NULL;
    - }
    -
    - if (!(e = malloc(sizeof(struct gg_event)))) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory\n");
    - return NULL;
    - }
    -
    - memset(e, 0, sizeof(struct gg_event));
    - e->type = GG_EVENT_NONE;
    -
    - switch (dcc->state) {
    - case GG_STATE_LISTENING:
    - {
    - struct sockaddr_in sin;
    - int fd;
    - socklen_t sin_len = sizeof(sin);
    -
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_LISTENING\n");
    -
    - if ((fd = accept(dcc->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() accept() failed "
    - "(%s)\n", strerror(errno));
    - return e;
    - }
    -
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd()"
    - " connection from %s:%d\n",
    - inet_ntoa(sin.sin_addr), htons(sin.sin_port));
    -
    - if (!gg_fd_set_nonblocking(fd)) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() can't set "
    - "nonblocking (%s)\n", strerror(errno));
    - close(fd);
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
    - return e;
    - }
    -
    - close(dcc->fd);
    - dcc->fd = fd;
    -
    - dcc->state = GG_STATE_READING_ID;
    - dcc->check = GG_CHECK_READ;
    - dcc->timeout = GG_DEFAULT_TIMEOUT;
    - dcc->incoming = 1;
    -
    - dcc->remote_port = ntohs(sin.sin_port);
    - dcc->remote_addr = sin.sin_addr.s_addr;
    -
    - e->type = GG_EVENT_DCC7_CONNECTED;
    - e->event.dcc7_connected.dcc7 = dcc;
    -
    - return e;
    - }
    -
    - case GG_STATE_CONNECTING:
    - {
    - int res = 0, error = 0;
    - socklen_t error_size = sizeof(error);
    -
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n");
    -
    - dcc->soft_timeout = 0;
    -
    - if (dcc->timeout == 0)
    - error = ETIMEDOUT;
    -
    - if (error || (res = getsockopt(dcc->fd, SOL_SOCKET,
    - SO_ERROR, &error, &error_size)) == -1 ||
    - error != 0)
    - {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() connection "
    - "failed (%s)\n", (res == -1) ?
    - strerror(errno) : strerror(error));
    -
    - if (dcc->relay) {
    - for (dcc->relay_index++;
    - dcc->relay_index < dcc->relay_count;
    - dcc->relay_index++)
    - {
    - dcc->remote_addr = dcc->relay_list[dcc->relay_index].addr;
    - dcc->remote_port = dcc->relay_list[dcc->relay_index].port;
    -
    - if (gg_dcc7_connect(dcc) == 0)
    - break;
    - }
    -
    - if (dcc->relay_index >= dcc->relay_count) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() "
    - "no relay available\n");
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_RELAY;
    - return e;
    - }
    - } else {
    - if (gg_dcc7_reverse_connect(dcc) != -1) {
    - e->type = GG_EVENT_DCC7_PENDING;
    - e->event.dcc7_pending.dcc7 = dcc;
    - } else {
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_NET;
    - }
    -
    - return e;
    - }
    - }
    -
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connected, sending id\n");
    -
    - dcc->state = GG_STATE_SENDING_ID;
    - dcc->check = GG_CHECK_WRITE;
    - dcc->timeout = GG_DEFAULT_TIMEOUT;
    - dcc->incoming = 0;
    -
    - return e;
    - }
    -
    - case GG_STATE_READING_ID:
    - {
    - int res;
    -
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n");
    -
    - if (!dcc->relay) {
    - struct gg_dcc7_welcome_p2p welcome, welcome_ok;
    - welcome_ok.id = dcc->cid;
    -
    - if ((res = recv(dcc->fd, &welcome, sizeof(welcome), 0)) != sizeof(welcome)) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() recv() "
    - "failed (%d, %s)\n", res,
    - strerror(errno));
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
    - return e;
    - }
    -
    - if (memcmp(&welcome, &welcome_ok, sizeof(welcome))) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
    - return e;
    - }
    - } else {
    - struct gg_dcc7_welcome_server welcome, welcome_ok;
    - welcome_ok.magic = GG_DCC7_WELCOME_SERVER;
    - welcome_ok.id = dcc->cid;
    -
    - if ((res = recv(dcc->fd, &welcome, sizeof(welcome), 0)) != sizeof(welcome)) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() recv() "
    - "failed (%d, %s)\n",
    - res, strerror(errno));
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
    - return e;
    - }
    -
    - if (memcmp(&welcome, &welcome_ok, sizeof(welcome)) != 0) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
    - return e;
    - }
    - }
    -
    - if (dcc->incoming) {
    - dcc->state = GG_STATE_SENDING_ID;
    - dcc->check = GG_CHECK_WRITE;
    - dcc->timeout = GG_DEFAULT_TIMEOUT;
    - } else {
    - gg_dcc7_postauth_fixup(dcc);
    - dcc->timeout = GG_DEFAULT_TIMEOUT;
    - }
    -
    - return e;
    - }
    -
    - case GG_STATE_SENDING_ID:
    - {
    - int res;
    -
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n");
    -
    - if (!dcc->relay) {
    - struct gg_dcc7_welcome_p2p welcome;
    -
    - welcome.id = dcc->cid;
    -
    - if ((res = send(dcc->fd, &welcome, sizeof(welcome), 0)) != sizeof(welcome)) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() send() "
    - "failed (%d, %s)\n",
    - res, strerror(errno));
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
    - return e;
    - }
    - } else {
    - struct gg_dcc7_welcome_server welcome;
    -
    - welcome.magic = gg_fix32(GG_DCC7_WELCOME_SERVER);
    - welcome.id = dcc->cid;
    -
    - if ((res = send(dcc->fd, &welcome, sizeof(welcome), 0)) != sizeof(welcome)) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() send() "
    - "failed (%d, %s)\n", res,
    - strerror(errno));
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
    - return e;
    - }
    - }
    -
    - if (dcc->incoming) {
    - gg_dcc7_postauth_fixup(dcc);
    - dcc->timeout = GG_DEFAULT_TIMEOUT;
    - } else {
    - dcc->state = GG_STATE_READING_ID;
    - dcc->check = GG_CHECK_READ;
    - dcc->timeout = GG_DEFAULT_TIMEOUT;
    - }
    -
    - return e;
    - }
    -
    - case GG_STATE_SENDING_FILE:
    - {
    - char buf[1024];
    - size_t chunk;
    - int res;
    -
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd()"
    - " GG_STATE_SENDING_FILE (offset=%d, size=%d)\n",
    - dcc->offset, dcc->size);
    -
    - if (dcc->offset >= dcc->size) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() offset >= size, finished\n");
    - e->type = GG_EVENT_DCC7_DONE;
    - e->event.dcc7_done.dcc7 = dcc;
    - return e;
    - }
    -
    - if (dcc->seek && lseek(dcc->file_fd, dcc->offset, SEEK_SET) == (off_t) -1) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() lseek() failed "
    - "(%s)\n", strerror(errno));
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_FILE;
    - return e;
    - }
    -
    - if ((chunk = dcc->size - dcc->offset) > sizeof(buf))
    - chunk = sizeof(buf);
    -
    - if ((res = read(dcc->file_fd, buf, chunk)) < 1) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() read() failed "
    - "(res=%d, %s)\n", res, strerror(errno));
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_FILE : GG_ERROR_DCC7_EOF;
    - return e;
    - }
    -
    - if ((res = send(dcc->fd, buf, res, 0)) == -1) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() send() failed "
    - "(%s)\n", strerror(errno));
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_NET;
    - return e;
    - }
    -
    - dcc->offset += res;
    -
    - if (dcc->offset >= dcc->size) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
    - e->type = GG_EVENT_DCC7_DONE;
    - e->event.dcc7_done.dcc7 = dcc;
    - return e;
    - }
    -
    - dcc->state = GG_STATE_SENDING_FILE;
    - dcc->check = GG_CHECK_WRITE;
    - dcc->timeout = GG_DCC7_TIMEOUT_SEND;
    -
    - return e;
    - }
    -
    - case GG_STATE_GETTING_FILE:
    - {
    - char buf[1024];
    - int res, wres;
    -
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd()"
    - " GG_STATE_GETTING_FILE (offset=%d, size=%d)\n",
    - dcc->offset, dcc->size);
    -
    - if (dcc->offset >= dcc->size) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
    - e->type = GG_EVENT_DCC7_DONE;
    - e->event.dcc7_done.dcc7 = dcc;
    - return e;
    - }
    -
    - if ((res = recv(dcc->fd, buf, sizeof(buf), 0)) < 1) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() recv() failed "
    - "(fd=%d, res=%d, %s)\n", dcc->fd, res,
    - strerror(errno));
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_NET : GG_ERROR_DCC7_EOF;
    - return e;
    - }
    -
    - /* XXX zapisywać do skutku? */
    -
    - if ((wres = write(dcc->file_fd, buf, res)) < res) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() write() failed "
    - "(fd=%d, res=%d, %s)\n", dcc->file_fd,
    - wres, strerror(errno));
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_FILE;
    - return e;
    - }
    -
    - dcc->offset += res;
    -
    - if (dcc->offset >= dcc->size) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
    - e->type = GG_EVENT_DCC7_DONE;
    - e->event.dcc7_done.dcc7 = dcc;
    - return e;
    - }
    -
    - dcc->state = GG_STATE_GETTING_FILE;
    - dcc->check = GG_CHECK_READ;
    - dcc->timeout = GG_DCC7_TIMEOUT_GET;
    -
    - return e;
    - }
    -
    - case GG_STATE_RESOLVING_RELAY:
    - {
    - struct in_addr addr;
    - int res;
    -
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_RESOLVING_RELAY\n");
    -
    - do {
    - res = gg_resolver_recv(dcc->fd, &addr, sizeof(addr));
    - } while (res == -1 && errno == EINTR);
    -
    - dcc->sess->resolver_cleanup(&dcc->resolver, 0);
    -
    - if (res != sizeof(addr) || addr.s_addr == INADDR_NONE) {
    - int errno_save = errno;
    -
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolving failed\n");
    - close(dcc->fd);
    - dcc->fd = -1;
    - errno = errno_save;
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_RELAY;
    - return e;
    - }
    -
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd()"
    - " resolved, connecting to %s:%d\n",
    - inet_ntoa(addr), GG_RELAY_PORT);
    -
    - if ((dcc->fd = gg_connect(&addr, GG_RELAY_PORT, 1)) == -1) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() connection "
    - "failed (errno=%d, %s), critical\n",
    - errno, strerror(errno));
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_RELAY;
    - return e;
    - }
    -
    - dcc->state = GG_STATE_CONNECTING_RELAY;
    - dcc->check = GG_CHECK_WRITE;
    - dcc->timeout = GG_DEFAULT_TIMEOUT;
    -
    - e->type = GG_EVENT_DCC7_PENDING;
    - e->event.dcc7_pending.dcc7 = dcc;
    -
    - return e;
    - }
    -
    - case GG_STATE_CONNECTING_RELAY:
    - {
    - int res;
    - socklen_t res_size = sizeof(res);
    - struct gg_dcc7_relay_req pkt;
    -
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING_RELAY\n");
    -
    - if (getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) != 0 || res != 0) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() connection "
    - "failed (errno=%d, %s)\n",
    - res, strerror(res));
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_RELAY;
    - return e;
    - }
    -
    - memset(&pkt, 0, sizeof(pkt));
    - pkt.magic = gg_fix32(GG_DCC7_RELAY_REQUEST);
    - pkt.len = gg_fix32(sizeof(pkt));
    - pkt.id = dcc->cid;
    - pkt.type = gg_fix16(GG_DCC7_RELAY_TYPE_SERVER);
    - pkt.dunno1 = gg_fix16(GG_DCC7_RELAY_DUNNO1);
    -
    - gg_debug_dcc(dcc, GG_DEBUG_DUMP, "// gg_dcc7_watch_fd()"
    - " send pkt(0x%.2x)\n", gg_fix32(pkt.magic));
    - gg_debug_dump_dcc(dcc, GG_DEBUG_DUMP, (const char*) &pkt, sizeof(pkt));
    -
    - if ((res = send(dcc->fd, &pkt, sizeof(pkt), 0)) != sizeof(pkt)) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() sending failed\n");
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_RELAY;
    - return e;
    - }
    -
    - dcc->state = GG_STATE_READING_RELAY;
    - dcc->check = GG_CHECK_READ;
    - dcc->timeout = GG_DEFAULT_TIMEOUT;
    -
    - return e;
    - }
    -
    - case GG_STATE_READING_RELAY:
    - {
    - char buf[256];
    - struct gg_dcc7_relay_reply *pkt;
    - struct gg_dcc7_relay_reply_server srv;
    - size_t max_relay_count = (sizeof(buf) - sizeof(*pkt)) / sizeof(srv);
    - int res;
    - int i;
    -
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_RELAY\n");
    -
    - if ((res = recv(dcc->fd, buf, sizeof(buf), 0)) < (int) sizeof(*pkt)) {
    - if (res == 0)
    - errno = ECONNRESET;
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() recv() failed "
    - "(%d, %s)\n", res, strerror(errno));
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_RELAY;
    - return e;
    - }
    -
    - pkt = (struct gg_dcc7_relay_reply*) buf;
    -
    - if (gg_fix32(pkt->magic) != GG_DCC7_RELAY_REPLY ||
    - gg_fix32(pkt->rcount) < 1 ||
    - gg_fix32(pkt->rcount) > 256 ||
    - gg_fix32(pkt->len) < sizeof(*pkt) +
    - gg_fix32(pkt->rcount) * sizeof(srv))
    - {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_wathc_fd() invalid reply\n");
    - errno = EINVAL;
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_RELAY;
    - return e;
    - }
    -
    - gg_debug_dcc(dcc, GG_DEBUG_DUMP,
    - "// gg_dcc7_get_relay() read pkt(0x%.2x)\n",
    - gg_fix32(pkt->magic));
    - gg_debug_dump_dcc(dcc, GG_DEBUG_DUMP, buf, res);
    -
    - free(dcc->relay_list);
    -
    - dcc->relay_index = 0;
    - dcc->relay_count = gg_fix32(pkt->rcount);
    -
    - if (dcc->relay_count > 0xffff ||
    - (size_t)dcc->relay_count > max_relay_count)
    - {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// gg_dcc7_watch_fd() relay_count out "
    - "of bounds (%d)\n", dcc->relay_count);
    - dcc->relay_count = 0;
    - free(e);
    - return NULL;
    - }
    -
    - dcc->relay_list = malloc(dcc->relay_count * sizeof(gg_dcc7_relay_t));
    -
    - if (dcc->relay_list == NULL) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory\n");
    - dcc->relay_count = 0;
    - free(e);
    - return NULL;
    - }
    -
    - for (i = 0; i < dcc->relay_count; i++) {
    - struct in_addr addr;
    -
    - memcpy(&srv, buf + sizeof(*pkt) + i * sizeof(srv), sizeof(srv));
    - dcc->relay_list[i].addr = srv.addr;
    - dcc->relay_list[i].port = gg_fix16(srv.port);
    - dcc->relay_list[i].family = srv.family;
    -
    - addr.s_addr = srv.addr;
    - gg_debug_dcc(dcc, GG_DEBUG_MISC,
    - "// %s %d %d\n", inet_ntoa(addr),
    - gg_fix16(srv.port), srv.family);
    - }
    -
    - dcc->relay = 1;
    -
    - for (; dcc->relay_index < dcc->relay_count; dcc->relay_index++) {
    - dcc->remote_addr = dcc->relay_list[dcc->relay_index].addr;
    - dcc->remote_port = dcc->relay_list[dcc->relay_index].port;
    -
    - if (gg_dcc7_connect(dcc) == 0)
    - break;
    - }
    -
    - if (dcc->relay_index >= dcc->relay_count) {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available\n");
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_RELAY;
    - return e;
    - }
    -
    - return e;
    - }
    -
    - default:
    - {
    - gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n");
    - e->type = GG_EVENT_DCC7_ERROR;
    - e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE;
    -
    - return e;
    - }
    - }
    -
    - return e;
    -}
    -
    -/**
    - * Zwalnia zasoby używane przez połączenie bezpośrednie.
    - *
    - * \param dcc Struktura połączenia
    - *
    - * \ingroup dcc7
    - */
    -void gg_dcc7_free(struct gg_dcc7 *dcc)
    -{
    - gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p)\n", dcc);
    -
    - if (!dcc)
    - return;
    -
    - if (dcc->fd != -1)
    - close(dcc->fd);
    -
    - if (dcc->file_fd != -1)
    - gg_file_close(dcc->file_fd);
    -
    - if (dcc->sess)
    - gg_dcc7_session_remove(dcc->sess, dcc);
    -
    - free(dcc->relay_list);
    -
    - free(dcc);
    -}
    --- a/libpurple/protocols/gg/lib/debug.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,397 +0,0 @@
    -/*
    - * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
    - * Robert J. Woźny <speedy@ziew.org>
    - * Arkadiusz Miśkiewicz <arekm@pld-linux.org>
    - * Tomasz Chiliński <chilek@chilan.com>
    - * Adam Wysocki <gophi@ekg.chmurka.net>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file debug.c
    - *
    - * \brief Funkcje odpluskwiania
    - */
    -#include <sys/types.h>
    -#include <errno.h>
    -#include <stdarg.h>
    -#include <stdio.h>
    -#include <string.h>
    -
    -#include "libgadu.h"
    -#include "debug.h"
    -
    -/**
    - * Poziom rejestracji informacji odpluskwiających. Zmienna jest maską bitową
    - * składającą się ze stałych \c GG_DEBUG_...
    - *
    - * \ingroup debug
    - */
    -int gg_debug_level = 0;
    -
    -/**
    - * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno
    - * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, są równe
    - * \c NULL, informacje są wysyłane do standardowego wyjścia błędu (\c stderr).
    - *
    - * \param level Poziom rejestracji
    - * \param format Format wiadomości (zgodny z \c printf)
    - * \param ap Lista argumentów (zgodna z \c printf)
    - *
    - * \note Funkcja jest przesłaniana przez \c gg_debug_handler_session.
    - *
    - * \ingroup debug
    - */
    -void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL;
    -
    -/**
    - * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno
    - * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, są równe
    - * \c NULL, informacje są wysyłane do standardowego wyjścia błędu.
    - *
    - * \param sess Sesja której dotyczy informacja lub \c NULL
    - * \param level Poziom rejestracji
    - * \param format Format wiadomości (zgodny z \c printf)
    - * \param ap Lista argumentów (zgodna z \c printf)
    - *
    - * \note Funkcja przesłania przez \c gg_debug_handler_session.
    - *
    - * \ingroup debug
    - */
    -void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap) = NULL;
    -
    -/**
    - * Plik, do którego będą przekazywane informacje odpluskwiania.
    - *
    - * Funkcja \c gg_debug() i pochodne mogą być przechwytywane przez aplikację
    - * korzystającą z biblioteki, by wyświetlić je na żądanie użytkownika lub
    - * zapisać do późniejszej analizy. Jeśli nie określono pliku, wybrane
    - * informacje będą wysyłane do standardowego wyjścia błędu (\c stderr).
    - *
    - * \ingroup debug
    - */
    -FILE *gg_debug_file = NULL;
    -
    -#ifndef GG_DEBUG_DISABLE
    -
    -/**
    - * \internal Przekazuje informacje odpluskwiania do odpowiedniej funkcji.
    - *
    - * Jeśli aplikacja ustawiła odpowiednią funkcję obsługi w
    - * \c gg_debug_handler_session lub \c gg_debug_handler, jest ona wywoływana.
    - * W przeciwnym wypadku wynik jest wysyłany do standardowego wyjścia błędu.
    - *
    - * \param sess Struktura sesji (może być \c NULL)
    - * \param level Poziom informacji
    - * \param format Format wiadomości (zgodny z \c printf)
    - * \param ap Lista argumentów (zgodna z \c printf)
    - */
    -void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap)
    -{
    - if (gg_debug_handler_session != NULL)
    - (*gg_debug_handler_session)(sess, level, format, ap);
    - else if (gg_debug_handler != NULL)
    - (*gg_debug_handler)(level, format, ap);
    - else if ((gg_debug_level & level) != 0)
    - vfprintf((gg_debug_file) ? gg_debug_file : stderr, format, ap);
    -}
    -
    -
    -/**
    - * \internal Przekazuje informację odpluskawiania.
    - *
    - * \param level Poziom wiadomości
    - * \param format Format wiadomości (zgodny z \c printf)
    - *
    - * \ingroup debug
    - */
    -void gg_debug(int level, const char *format, ...)
    -{
    - va_list ap;
    - int old_errno = errno;
    -
    - va_start(ap, format);
    - gg_debug_common(NULL, level, format, ap);
    - va_end(ap);
    - errno = old_errno;
    -}
    -
    -/**
    - * \internal Przekazuje informację odpluskwiania związaną z sesją.
    - *
    - * \param gs Struktura sesji
    - * \param level Poziom wiadomości
    - * \param format Format wiadomości (zgodny z \c printf)
    - *
    - * \ingroup debug
    - */
    -void gg_debug_session(struct gg_session *gs, int level, const char *format, ...)
    -{
    - va_list ap;
    - int old_errno = errno;
    -
    - va_start(ap, format);
    - gg_debug_common(gs, level, format, ap);
    - va_end(ap);
    - errno = old_errno;
    -}
    -
    -/**
    - * \internal Przekazuje zrzut bufora do odpluskwiania.
    - *
    - * \param gs Struktura sesji
    - * \param level Poziom wiadomości
    - * \param buf Bufor danych
    - * \param len Długość bufora danych
    - *
    - * \ingroup debug
    - */
    -void gg_debug_dump(struct gg_session *gs, int level, const char *buf, size_t len)
    -{
    - char line[80];
    - unsigned int i, j;
    -
    - for (i = 0; i < len; i += 16) {
    - int ofs;
    -
    - sprintf(line, "%.4x: ", i);
    - ofs = 6;
    -
    - for (j = 0; j < 16; j++) {
    - if (i + j < len)
    - sprintf(line + ofs, " %02x", (unsigned char) buf[i + j]);
    - else
    - sprintf(line + ofs, " ");
    -
    - ofs += 3;
    - }
    -
    - sprintf(line + ofs, " ");
    - ofs += 2;
    -
    - for (j = 0; j < 16; j++) {
    - unsigned char ch;
    -
    - if (i + j < len) {
    - ch = buf[i + j];
    -
    - if (ch < 32 || ch > 126)
    - ch = '.';
    - } else {
    - ch = ' ';
    - }
    -
    - line[ofs++] = ch;
    - }
    -
    - line[ofs++] = '\n';
    - line[ofs++] = 0;
    -
    - gg_debug_session(gs, level, "%s", line);
    - }
    -}
    -
    -/**
    - * \internal Zwraca ciąg z nazwą podanego stanu sesji.
    - *
    - * \param state Stan sesji.
    - *
    - * \return Ciąg z nazwą stanu
    - *
    - * \ingroup debug
    - */
    -const char *gg_debug_state(enum gg_state_t state)
    -{
    - switch (state) {
    -#define GG_DEBUG_STATE(x) case x: return #x;
    - GG_DEBUG_STATE(GG_STATE_IDLE)
    - GG_DEBUG_STATE(GG_STATE_RESOLVING)
    - GG_DEBUG_STATE(GG_STATE_CONNECTING)
    - GG_DEBUG_STATE(GG_STATE_READING_DATA)
    - GG_DEBUG_STATE(GG_STATE_ERROR)
    - GG_DEBUG_STATE(GG_STATE_CONNECTING_HUB)
    - GG_DEBUG_STATE(GG_STATE_CONNECTING_GG)
    - GG_DEBUG_STATE(GG_STATE_READING_KEY)
    - GG_DEBUG_STATE(GG_STATE_READING_REPLY)
    - GG_DEBUG_STATE(GG_STATE_CONNECTED)
    - GG_DEBUG_STATE(GG_STATE_SENDING_QUERY)
    - GG_DEBUG_STATE(GG_STATE_READING_HEADER)
    - GG_DEBUG_STATE(GG_STATE_PARSING)
    - GG_DEBUG_STATE(GG_STATE_DONE)
    - GG_DEBUG_STATE(GG_STATE_LISTENING)
    - GG_DEBUG_STATE(GG_STATE_READING_UIN_1)
    - GG_DEBUG_STATE(GG_STATE_READING_UIN_2)
    - GG_DEBUG_STATE(GG_STATE_SENDING_ACK)
    - GG_DEBUG_STATE(GG_STATE_READING_ACK)
    - GG_DEBUG_STATE(GG_STATE_READING_REQUEST)
    - GG_DEBUG_STATE(GG_STATE_SENDING_REQUEST)
    - GG_DEBUG_STATE(GG_STATE_SENDING_FILE_INFO)
    - GG_DEBUG_STATE(GG_STATE_READING_PRE_FILE_INFO)
    - GG_DEBUG_STATE(GG_STATE_READING_FILE_INFO)
    - GG_DEBUG_STATE(GG_STATE_SENDING_FILE_ACK)
    - GG_DEBUG_STATE(GG_STATE_READING_FILE_ACK)
    - GG_DEBUG_STATE(GG_STATE_SENDING_FILE_HEADER)
    - GG_DEBUG_STATE(GG_STATE_READING_FILE_HEADER)
    - GG_DEBUG_STATE(GG_STATE_GETTING_FILE)
    - GG_DEBUG_STATE(GG_STATE_SENDING_FILE)
    - GG_DEBUG_STATE(GG_STATE_READING_VOICE_ACK)
    - GG_DEBUG_STATE(GG_STATE_READING_VOICE_HEADER)
    - GG_DEBUG_STATE(GG_STATE_READING_VOICE_SIZE)
    - GG_DEBUG_STATE(GG_STATE_READING_VOICE_DATA)
    - GG_DEBUG_STATE(GG_STATE_SENDING_VOICE_ACK)
    - GG_DEBUG_STATE(GG_STATE_SENDING_VOICE_REQUEST)
    - GG_DEBUG_STATE(GG_STATE_READING_TYPE)
    - GG_DEBUG_STATE(GG_STATE_TLS_NEGOTIATION)
    - GG_DEBUG_STATE(GG_STATE_REQUESTING_ID)
    - GG_DEBUG_STATE(GG_STATE_WAITING_FOR_ACCEPT)
    - GG_DEBUG_STATE(GG_STATE_WAITING_FOR_INFO)
    - GG_DEBUG_STATE(GG_STATE_READING_ID)
    - GG_DEBUG_STATE(GG_STATE_SENDING_ID)
    - GG_DEBUG_STATE(GG_STATE_RESOLVING_GG)
    - GG_DEBUG_STATE(GG_STATE_RESOLVING_RELAY)
    - GG_DEBUG_STATE(GG_STATE_CONNECTING_RELAY)
    - GG_DEBUG_STATE(GG_STATE_READING_RELAY)
    - GG_DEBUG_STATE(GG_STATE_DISCONNECTING)
    - GG_DEBUG_STATE(GG_STATE_CONNECT_HUB)
    - GG_DEBUG_STATE(GG_STATE_CONNECT_PROXY_HUB)
    - GG_DEBUG_STATE(GG_STATE_CONNECT_GG)
    - GG_DEBUG_STATE(GG_STATE_CONNECT_PROXY_GG)
    - GG_DEBUG_STATE(GG_STATE_CONNECTING_PROXY_HUB)
    - GG_DEBUG_STATE(GG_STATE_CONNECTING_PROXY_GG)
    - GG_DEBUG_STATE(GG_STATE_RESOLVE_HUB_SYNC)
    - GG_DEBUG_STATE(GG_STATE_RESOLVE_HUB_ASYNC)
    - GG_DEBUG_STATE(GG_STATE_RESOLVE_PROXY_HUB_SYNC)
    - GG_DEBUG_STATE(GG_STATE_RESOLVE_PROXY_HUB_ASYNC)
    - GG_DEBUG_STATE(GG_STATE_RESOLVE_PROXY_GG_SYNC)
    - GG_DEBUG_STATE(GG_STATE_RESOLVE_PROXY_GG_ASYNC)
    - GG_DEBUG_STATE(GG_STATE_RESOLVE_GG_SYNC)
    - GG_DEBUG_STATE(GG_STATE_RESOLVE_GG_ASYNC)
    - GG_DEBUG_STATE(GG_STATE_RESOLVING_HUB)
    - GG_DEBUG_STATE(GG_STATE_RESOLVING_PROXY_HUB)
    - GG_DEBUG_STATE(GG_STATE_RESOLVING_PROXY_GG)
    - GG_DEBUG_STATE(GG_STATE_SEND_HUB)
    - GG_DEBUG_STATE(GG_STATE_SEND_PROXY_HUB)
    - GG_DEBUG_STATE(GG_STATE_SEND_PROXY_GG)
    - GG_DEBUG_STATE(GG_STATE_SENDING_HUB)
    - GG_DEBUG_STATE(GG_STATE_SENDING_PROXY_HUB)
    - GG_DEBUG_STATE(GG_STATE_SENDING_PROXY_GG)
    - GG_DEBUG_STATE(GG_STATE_READING_HUB)
    - GG_DEBUG_STATE(GG_STATE_READING_PROXY_HUB)
    - GG_DEBUG_STATE(GG_STATE_READING_PROXY_GG)
    -#undef GG_DEBUG_STATE
    -
    - /* Celowo nie ma default, żeby kompilator wyłapał brakujące stany */
    - }
    -
    - return NULL;
    -}
    -
    -/**
    - * \internal Zwraca ciąg z nazwą podanego zdarzenia.
    - *
    - * \param event Zdarzenie.
    - *
    - * \return Ciąg z nazwą zdarzenia
    - *
    - * \ingroup debug
    - */
    -const char *gg_debug_event(enum gg_event_t event)
    -{
    - switch (event) {
    -#define GG_DEBUG_EVENT(x) case x: return #x;
    - GG_DEBUG_EVENT(GG_EVENT_NONE)
    - GG_DEBUG_EVENT(GG_EVENT_MSG)
    - GG_DEBUG_EVENT(GG_EVENT_NOTIFY)
    - GG_DEBUG_EVENT(GG_EVENT_NOTIFY_DESCR)
    - GG_DEBUG_EVENT(GG_EVENT_STATUS)
    - GG_DEBUG_EVENT(GG_EVENT_ACK)
    - GG_DEBUG_EVENT(GG_EVENT_PONG)
    - GG_DEBUG_EVENT(GG_EVENT_CONN_FAILED)
    - GG_DEBUG_EVENT(GG_EVENT_CONN_SUCCESS)
    - GG_DEBUG_EVENT(GG_EVENT_DISCONNECT)
    - GG_DEBUG_EVENT(GG_EVENT_DCC_NEW)
    - GG_DEBUG_EVENT(GG_EVENT_DCC_ERROR)
    - GG_DEBUG_EVENT(GG_EVENT_DCC_DONE)
    - GG_DEBUG_EVENT(GG_EVENT_DCC_CLIENT_ACCEPT)
    - GG_DEBUG_EVENT(GG_EVENT_DCC_CALLBACK)
    - GG_DEBUG_EVENT(GG_EVENT_DCC_NEED_FILE_INFO)
    - GG_DEBUG_EVENT(GG_EVENT_DCC_NEED_FILE_ACK)
    - GG_DEBUG_EVENT(GG_EVENT_DCC_NEED_VOICE_ACK)
    - GG_DEBUG_EVENT(GG_EVENT_DCC_VOICE_DATA)
    - GG_DEBUG_EVENT(GG_EVENT_PUBDIR50_SEARCH_REPLY)
    - GG_DEBUG_EVENT(GG_EVENT_PUBDIR50_READ)
    - GG_DEBUG_EVENT(GG_EVENT_PUBDIR50_WRITE)
    - GG_DEBUG_EVENT(GG_EVENT_STATUS60)
    - GG_DEBUG_EVENT(GG_EVENT_NOTIFY60)
    - GG_DEBUG_EVENT(GG_EVENT_USERLIST)
    - GG_DEBUG_EVENT(GG_EVENT_IMAGE_REQUEST)
    - GG_DEBUG_EVENT(GG_EVENT_IMAGE_REPLY)
    - GG_DEBUG_EVENT(GG_EVENT_DCC_ACK)
    - GG_DEBUG_EVENT(GG_EVENT_DCC7_NEW)
    - GG_DEBUG_EVENT(GG_EVENT_DCC7_ACCEPT)
    - GG_DEBUG_EVENT(GG_EVENT_DCC7_REJECT)
    - GG_DEBUG_EVENT(GG_EVENT_DCC7_CONNECTED)
    - GG_DEBUG_EVENT(GG_EVENT_DCC7_ERROR)
    - GG_DEBUG_EVENT(GG_EVENT_DCC7_DONE)
    - GG_DEBUG_EVENT(GG_EVENT_DCC7_PENDING)
    - GG_DEBUG_EVENT(GG_EVENT_XML_EVENT)
    - GG_DEBUG_EVENT(GG_EVENT_JSON_EVENT)
    - GG_DEBUG_EVENT(GG_EVENT_ACK110)
    - GG_DEBUG_EVENT(GG_EVENT_DISCONNECT_ACK)
    - GG_DEBUG_EVENT(GG_EVENT_TYPING_NOTIFICATION)
    - GG_DEBUG_EVENT(GG_EVENT_USER_DATA)
    - GG_DEBUG_EVENT(GG_EVENT_MULTILOGON_MSG)
    - GG_DEBUG_EVENT(GG_EVENT_MULTILOGON_INFO)
    - GG_DEBUG_EVENT(GG_EVENT_USERLIST100_VERSION)
    - GG_DEBUG_EVENT(GG_EVENT_USERLIST100_REPLY)
    - GG_DEBUG_EVENT(GG_EVENT_IMTOKEN)
    - GG_DEBUG_EVENT(GG_EVENT_PONG110)
    - GG_DEBUG_EVENT(GG_EVENT_CHAT_INFO)
    - GG_DEBUG_EVENT(GG_EVENT_CHAT_INFO_GOT_ALL)
    - GG_DEBUG_EVENT(GG_EVENT_CHAT_INFO_UPDATE)
    - GG_DEBUG_EVENT(GG_EVENT_CHAT_CREATED)
    - GG_DEBUG_EVENT(GG_EVENT_CHAT_INVITE_ACK)
    -#undef GG_DEBUG_EVENT
    -
    - /* Celowo nie ma default, żeby kompilator wyłapał brakujące zdarzenia */
    - }
    -
    - return NULL;
    -}
    -
    -#else
    -
    -#undef gg_debug_common
    -void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap)
    -{
    -}
    -
    -#undef gg_debug
    -void gg_debug(int level, const char *format, ...)
    -{
    -}
    -
    -#undef gg_debug_session
    -void gg_debug_session(struct gg_session *gs, int level, const char *format, ...)
    -{
    -}
    -
    -#undef gg_debug_dump
    -void gg_debug_dump(struct gg_session *gs, int level, const char *buf, size_t len)
    -{
    -}
    -
    -#endif
    --- a/libpurple/protocols/gg/lib/debug.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,27 +0,0 @@
    -/*
    - * (C) Copyright 2009 Wojtek Kaniewski <wojtekka@irc.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -#ifndef LIBGADU_DEBUG_H
    -#define LIBGADU_DEBUG_H
    -
    -#include "libgadu.h"
    -
    -void gg_debug_dump(struct gg_session *sess, int level, const char *buf, size_t len);
    -void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap);
    -
    -#endif /* LIBGADU_DEBUG_H */
    --- a/libpurple/protocols/gg/lib/deflate.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,231 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2011 Bartosz Brachaczek <b.brachaczek@gmail.com>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file deflate.c
    - *
    - * \brief Funkcje kompresji Deflate
    - */
    -
    -#include <stdlib.h>
    -#include <string.h>
    -
    -#include "libgadu.h"
    -#include "internal.h"
    -#include "deflate.h"
    -
    -#ifdef GG_CONFIG_HAVE_ZLIB
    -#include <zlib.h>
    -#endif
    -
    -/**
    - * \internal Kompresuje dane wejściowe algorytmem Deflate z najwyższym
    - * stopniem kompresji, tak samo jak oryginalny klient.
    - *
    - * Wynik funkcji należy zwolnić za pomocą \c free.
    - *
    - * \param in Ciąg znaków do skompresowania, zakończony \c \\0
    - * \param out_lenp Wskaźnik na zmienną, do której zostanie zapisana
    - * długość bufora wynikowego
    - *
    - * \return Skompresowany ciąg znaków lub \c NULL w przypadku niepowodzenia.
    - */
    -unsigned char *gg_deflate(const char *in, size_t *out_lenp)
    -{
    -#ifdef GG_CONFIG_HAVE_ZLIB
    - int ret;
    - z_stream strm;
    - unsigned char *out, *out2;
    - size_t out_len;
    -
    - if (in == NULL || out_lenp == NULL)
    - return NULL;
    -
    - strm.zalloc = Z_NULL;
    - strm.zfree = Z_NULL;
    - strm.opaque = Z_NULL;
    - strm.avail_in = strlen(in);
    - strm.next_in = (unsigned char*) in;
    -
    - ret = deflateInit(&strm, Z_BEST_COMPRESSION);
    - if (ret != Z_OK) {
    - gg_debug(GG_DEBUG_MISC, "// gg_deflate() deflateInit() failed (%d)\n", ret);
    - return NULL;
    - }
    -
    - out_len = deflateBound(&strm, strm.avail_in);
    - out = malloc(out_len);
    -
    - if (out == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_deflate() not enough memory for "
    - "output data (%" GG_SIZE_FMT ")\n", out_len);
    - goto fail;
    - }
    -
    - strm.avail_out = out_len;
    - strm.next_out = out;
    -
    - for (;;) {
    - ret = deflate(&strm, Z_FINISH);
    -
    - if (ret == Z_STREAM_END)
    - break;
    -
    - /* raczej nie powinno się zdarzyć przy Z_FINISH i out_len == deflateBound(),
    - * ale dokumentacja zlib nie wyklucza takiej możliwości */
    - if (ret == Z_OK) {
    - out_len *= 2;
    - out2 = realloc(out, out_len);
    -
    - if (out2 == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_deflate() not "
    - "enough memory for output data (%"
    - GG_SIZE_FMT ")\n", out_len);
    - goto fail;
    - }
    -
    - out = out2;
    -
    - strm.avail_out = out_len / 2;
    - strm.next_out = out + out_len / 2;
    - } else {
    - gg_debug(GG_DEBUG_MISC, "// gg_deflate() deflate() "
    - "failed (ret=%d, msg=%s)\n", ret,
    - strm.msg != NULL ? strm.msg :
    - "no error message provided");
    - goto fail;
    - }
    - }
    -
    - out_len = strm.total_out;
    - out2 = realloc(out, out_len);
    -
    - if (out2 == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_deflate() not enough memory for "
    - "output data (%" GG_SIZE_FMT ")\n", out_len);
    - goto fail;
    - }
    -
    - *out_lenp = out_len;
    - deflateEnd(&strm);
    -
    - return out2;
    -
    -fail:
    - *out_lenp = 0;
    - deflateEnd(&strm);
    - free(out);
    -#endif
    - return NULL;
    -}
    -
    -/**
    - * \internal Dekompresuje dane wejściowe w formacie Deflate.
    - *
    - * Wynik funkcji należy zwolnić za pomocą \c free.
    - *
    - * \param in Bufor danych skompresowanych algorytmem Deflate
    - * \param length Długość bufora wejściowego
    - *
    - * \note Dokleja \c \\0 na końcu bufora wynikowego.
    - *
    - * \return Zdekompresowany ciąg znaków, zakończony \c \\0,
    - * lub \c NULL w przypadku niepowodzenia.
    - */
    -char *gg_inflate(const unsigned char *in, size_t length)
    -{
    -#ifdef GG_CONFIG_HAVE_ZLIB
    - int ret;
    - z_stream strm;
    - char *out = NULL, *out2;
    - size_t out_len = 1024;
    - int first = 1;
    -
    - if (in == NULL)
    - return NULL;
    -
    - strm.zalloc = Z_NULL;
    - strm.zfree = Z_NULL;
    - strm.opaque = Z_NULL;
    - strm.avail_in = length;
    - strm.next_in = (unsigned char*) in;
    -
    - ret = inflateInit(&strm);
    - if (ret != Z_OK) {
    - gg_debug(GG_DEBUG_MISC, "// gg_inflate() inflateInit() failed (%d)\n", ret);
    - return NULL;
    - }
    -
    - do {
    - out_len *= 2;
    - out2 = realloc(out, out_len);
    -
    - if (out2 == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_inflate() not enough "
    - "memory for output data (%" GG_SIZE_FMT ")\n", out_len);
    - goto fail;
    - }
    -
    - out = out2;
    -
    - if (first) {
    - strm.avail_out = out_len;
    - strm.next_out = (unsigned char*) out;
    - } else {
    - strm.avail_out = out_len / 2;
    - strm.next_out = (unsigned char*) out + out_len / 2;
    - }
    -
    - ret = inflate(&strm, Z_NO_FLUSH);
    -
    - if (ret != Z_OK && ret != Z_STREAM_END) {
    - gg_debug(GG_DEBUG_MISC, "// gg_inflate() inflate() "
    - "failed (ret=%d, msg=%s)\n", ret,
    - strm.msg != NULL ? strm.msg :
    - "no error message provided");
    - goto fail;
    - }
    -
    - first = 0;
    - } while (ret != Z_STREAM_END);
    -
    - /* rezerwujemy ostatni znak na NULL-a */
    - out_len = strm.total_out + 1;
    - out2 = realloc(out, out_len);
    -
    - if (out2 == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_inflate() not enough memory for "
    - "output data (%" GG_SIZE_FMT ")\n", out_len);
    - goto fail;
    - }
    -
    - out = out2;
    - out[out_len - 1] = '\0';
    -
    - inflateEnd(&strm);
    -
    - return out;
    -
    -fail:
    - inflateEnd(&strm);
    - free(out);
    -#endif
    - return NULL;
    -}
    --- a/libpurple/protocols/gg/lib/deflate.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,29 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2011 Bartosz Brachaczek <b.brachaczek@gmail.com>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -#ifndef LIBGADU_DEFLATE_H
    -#define LIBGADU_DEFLATE_H
    -
    -#include "libgadu.h"
    -
    -unsigned char *gg_deflate(const char *in, size_t *out_lenp);
    -char *gg_inflate(const unsigned char *in, size_t length);
    -
    -#endif /* LIBGADU_DEFLATE_H */
    --- a/libpurple/protocols/gg/lib/encoding.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,272 +0,0 @@
    -/*
    - * (C) Copyright 2008-2009 Jakub Zawadzki <darkjames@darkjames.ath.cx>
    - * Wojtek Kaniewski <wojtekka@irc.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -#include "strman.h"
    -#include <stdlib.h>
    -#include <errno.h>
    -
    -#include "libgadu.h"
    -#include "encoding.h"
    -
    -/**
    - * \file encoding.c
    - *
    - * \brief Funkcje konwersji kodowania tekstu
    - */
    -
    -/**
    - * \internal Tablica konwersji CP1250 na Unikod.
    - */
    -static const uint16_t table_cp1250[] =
    -{
    - 0x20ac, '?', 0x201a, '?', 0x201e, 0x2026, 0x2020, 0x2021,
    - '?', 0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179,
    - '?', 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
    - '?', 0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a,
    - 0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7,
    - 0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b,
    - 0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7,
    - 0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c,
    - 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7,
    - 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e,
    - 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7,
    - 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df,
    - 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7,
    - 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f,
    - 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7,
    - 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9,
    -};
    -
    -/**
    - * \internal Zamienia tekst kodowany CP1250 na UTF-8.
    - *
    - * \param src Tekst źródłowy w CP1250.
    - * \param src_length Długość ciągu źródłowego (nigdy ujemna).
    - * \param dst_length Długość ciągu docelowego (jeśli -1, nieograniczona).
    - *
    - * \return Zaalokowany bufor z tekstem w UTF-8.
    - */
    -static char *gg_encoding_convert_cp1250_utf8(const char *src, int src_length, int dst_length)
    -{
    - int i, j, len;
    - char *result = NULL;
    -
    - for (i = 0, len = 0; (src[i] != 0) && (i < src_length); i++) {
    - uint16_t uc;
    -
    - if ((unsigned char) src[i] < 0x80)
    - uc = (unsigned char) src[i];
    - else
    - uc = table_cp1250[(unsigned char) src[i] - 128];
    -
    - if (uc < 0x80)
    - len += 1;
    - else if (uc < 0x800)
    - len += 2;
    - else
    - len += 3;
    - }
    -
    - if ((dst_length != -1) && (len > dst_length))
    - len = dst_length;
    -
    - result = malloc(len + 1);
    -
    - if (result == NULL)
    - return NULL;
    -
    - for (i = 0, j = 0; (src[i] != 0) && (i < src_length) && (j < len); i++) {
    - uint16_t uc;
    -
    - if ((unsigned char) src[i] < 0x80)
    - uc = (unsigned char) src[i];
    - else
    - uc = table_cp1250[(unsigned char) src[i] - 128];
    -
    - if (uc < 0x80)
    - result[j++] = (char) uc;
    - else if (uc < 0x800) {
    - if (j + 1 > len)
    - break;
    - result[j++] = 0xc0 | ((uc >> 6) & 0x1f);
    - result[j++] = 0x80 | (uc & 0x3f);
    - } else {
    - if (j + 2 > len)
    - break;
    - result[j++] = 0xe0 | ((uc >> 12) & 0x1f);
    - result[j++] = 0x80 | ((uc >> 6) & 0x3f);
    - result[j++] = 0x80 | (uc & 0x3f);
    - }
    - }
    -
    - result[j] = 0;
    -
    - return result;
    -}
    -
    -/**
    - * \internal Zamienia tekst kodowany UTF-8 na CP1250.
    - *
    - * \param src Tekst źródłowy w UTF-8.
    - * \param src_length Długość ciągu źródłowego (nigdy ujemna).
    - * \param dst_length Długość ciągu docelowego (jeśli -1, nieograniczona).
    - *
    - * \return Zaalokowany bufor z tekstem w CP1250.
    - */
    -static char *gg_encoding_convert_utf8_cp1250(const char *src, int src_length, int dst_length)
    -{
    - char *result;
    - int i, j, len, uc_left = 0;
    - uint32_t uc = 0, uc_min = 0;
    -
    - for (i = 0, len = 0; (src[i] != 0) && (i < src_length); i++) {
    - if ((src[i] & 0xc0) != 0x80)
    - len++;
    - }
    -
    - if ((dst_length != -1) && (len > dst_length))
    - len = dst_length;
    -
    - result = malloc(len + 1);
    -
    - if (result == NULL)
    - return NULL;
    -
    - for (i = 0, j = 0; (src[i] != 0) && (i < src_length) && (j < len); i++) {
    - if ((unsigned char) src[i] >= 0xf5) {
    - if (uc_left != 0)
    - result[j++] = '?';
    - /* Restricted sequences */
    - result[j++] = '?';
    - uc_left = 0;
    - } else if ((src[i] & 0xf8) == 0xf0) {
    - if (uc_left != 0)
    - result[j++] = '?';
    - uc = src[i] & 0x07;
    - uc_left = 3;
    - uc_min = 0x10000;
    - } else if ((src[i] & 0xf0) == 0xe0) {
    - if (uc_left != 0)
    - result[j++] = '?';
    - uc = src[i] & 0x0f;
    - uc_left = 2;
    - uc_min = 0x800;
    - } else if ((src[i] & 0xe0) == 0xc0) {
    - if (uc_left != 0)
    - result[j++] = '?';
    - uc = src[i] & 0x1f;
    - uc_left = 1;
    - uc_min = 0x80;
    - } else if ((src[i] & 0xc0) == 0x80) {
    - if (uc_left > 0) {
    - uc <<= 6;
    - uc |= src[i] & 0x3f;
    - uc_left--;
    -
    - if (uc_left == 0) {
    - int valid = 0;
    - int k;
    -
    - if (uc >= uc_min) {
    - for (k = 0; k < 128; k++) {
    - if (uc == table_cp1250[k]) {
    - result[j++] = k + 128;
    - valid = 1;
    - break;
    - }
    - }
    - }
    -
    - if (!valid && uc != 0xfeff) /* Byte Order Mark */
    - result[j++] = '?';
    - }
    - }
    - } else {
    - if (uc_left != 0) {
    - result[j++] = '?';
    - uc_left = 0;
    - }
    - result[j++] = src[i];
    - }
    - }
    -
    - if ((uc_left != 0) && (src[i] == 0))
    - result[j++] = '?';
    -
    - result[j] = 0;
    -
    - return result;
    -}
    -
    -/**
    - * \internal Zamienia kodowanie tekstu.
    - *
    - * \param src Tekst źródłowy.
    - * \param src_encoding Kodowanie tekstu źródłowego.
    - * \param dst_encoding Kodowanie tekstu docelowego.
    - * \param src_length Długość ciągu źródłowego w bajtach (jeśli -1, zostanie obliczona na podstawie zawartości \p src).
    - * \param dst_length Długość ciągu docelowego w bajtach (jeśli -1, nieograniczona).
    - *
    - * \return Zaalokowany bufor z tekstem w kodowaniu docelowym.
    - */
    -char *gg_encoding_convert(const char *src, gg_encoding_t src_encoding,
    - gg_encoding_t dst_encoding, int src_length, int dst_length)
    -{
    - char *result;
    -
    - if (src == NULL) {
    - errno = EINVAL;
    - return NULL;
    - }
    -
    - /* specjalny przypadek obsługiwany ekspresowo */
    - if ((dst_encoding == src_encoding) && (dst_length == -1) && (src_length == -1))
    - return strdup(src);
    -
    - if (src_length == -1)
    - src_length = strlen(src);
    -
    - if (dst_encoding == src_encoding) {
    - int len;
    -
    - if (dst_length == -1)
    - len = src_length;
    - else
    - len = (src_length < dst_length) ? src_length : dst_length;
    -
    - result = malloc(len + 1);
    -
    - if (result == NULL)
    - return NULL;
    -
    - strncpy(result, src, len);
    - result[len] = 0;
    -
    - return result;
    - }
    -
    - if (dst_encoding == GG_ENCODING_CP1250 && src_encoding == GG_ENCODING_UTF8)
    - return gg_encoding_convert_utf8_cp1250(src, src_length, dst_length);
    -
    - if (dst_encoding == GG_ENCODING_UTF8 && src_encoding == GG_ENCODING_CP1250)
    - return gg_encoding_convert_cp1250_utf8(src, src_length, dst_length);
    -
    - errno = EINVAL;
    - return NULL;
    -}
    --- a/libpurple/protocols/gg/lib/encoding.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,28 +0,0 @@
    -/*
    - * (C) Copyright 2008-2009 Jakub Zawadzki <darkjames@darkjames.ath.cx>
    - * Wojtek Kaniewski <wojtekka@irc.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -#ifndef LIBGADU_ENCODING_H
    -#define LIBGADU_ENCODING_H
    -
    -#include "libgadu.h"
    -
    -char *gg_encoding_convert(const char *src, gg_encoding_t src_encoding,
    - gg_encoding_t dst_encoding, int src_length, int dst_length);
    -
    -#endif /* LIBGADU_SESSION_H */
    --- a/libpurple/protocols/gg/lib/endian.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,109 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
    - * Robert J. Woźny <speedy@ziew.org>
    - * Arkadiusz Miśkiewicz <arekm@pld-linux.org>
    - * Tomasz Chiliński <chilek@chilan.com>
    - * Adam Wysocki <gophi@ekg.chmurka.net>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file endian.c
    - *
    - * \brief Konwersja między różnymi kolejnościami bajtów
    - */
    -
    -#include "libgadu.h"
    -#include "internal.h"
    -
    -/**
    - * \internal Zamienia kolejność bajtów w 64-bitowym słowie.
    - *
    - * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach
    - * big-endianowych odwraca kolejność bajtów w słowie.
    - *
    - * \param x Liczba do zamiany
    - *
    - * \return Liczba z odpowiednią kolejnością bajtów
    - *
    - * \ingroup helper
    - */
    -uint64_t gg_fix64(uint64_t x)
    -{
    -#ifndef GG_CONFIG_BIGENDIAN
    - return x;
    -#else
    - return (uint64_t)
    - (((x & (uint64_t) 0x00000000000000ffULL) << 56) |
    - ((x & (uint64_t) 0x000000000000ff00ULL) << 40) |
    - ((x & (uint64_t) 0x0000000000ff0000ULL) << 24) |
    - ((x & (uint64_t) 0x00000000ff000000ULL) << 8) |
    - ((x & (uint64_t) 0x000000ff00000000ULL) >> 8) |
    - ((x & (uint64_t) 0x0000ff0000000000ULL) >> 24) |
    - ((x & (uint64_t) 0x00ff000000000000ULL) >> 40) |
    - ((x & (uint64_t) 0xff00000000000000ULL) >> 56));
    -#endif
    -}
    -
    -/**
    - * \internal Zamienia kolejność bajtów w 32-bitowym słowie.
    - *
    - * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach
    - * big-endianowych odwraca kolejność bajtów w słowie.
    - *
    - * \param x Liczba do zamiany
    - *
    - * \return Liczba z odpowiednią kolejnością bajtów
    - *
    - * \ingroup helper
    - */
    -uint32_t gg_fix32(uint32_t x)
    -{
    -#ifndef GG_CONFIG_BIGENDIAN
    - return x;
    -#else
    - return (uint32_t)
    - (((x & (uint32_t) 0x000000ffU) << 24) |
    - ((x & (uint32_t) 0x0000ff00U) << 8) |
    - ((x & (uint32_t) 0x00ff0000U) >> 8) |
    - ((x & (uint32_t) 0xff000000U) >> 24));
    -#endif
    -}
    -
    -/**
    - * \internal Zamienia kolejność bajtów w 16-bitowym słowie.
    - *
    - * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach
    - * big-endianowych zamienia kolejność bajtów w słowie.
    - *
    - * \param x Liczba do zamiany
    - *
    - * \return Liczba z odpowiednią kolejnością bajtów
    - *
    - * \ingroup helper
    - */
    -uint16_t gg_fix16(uint16_t x)
    -{
    -#ifndef GG_CONFIG_BIGENDIAN
    - return x;
    -#else
    - return (uint16_t)
    - (((x & (uint16_t) 0x00ffU) << 8) |
    - ((x & (uint16_t) 0xff00U) >> 8));
    -#endif
    -}
    --- a/libpurple/protocols/gg/lib/events.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1901 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
    - * Robert J. Woźny <speedy@ziew.org>
    - * Arkadiusz Miśkiewicz <arekm@pld-linux.org>
    - * Adam Wysocki <gophi@ekg.chmurka.net>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file events.c
    - *
    - * \brief Obsługa zdarzeń
    - *
    - * \todo Poprawna obsługa gg_proxy_http_only
    - */
    -
    -#include "strman.h"
    -#include "network.h"
    -
    -#include "libgadu.h"
    -#include "protocol.h"
    -#include "internal.h"
    -#include "encoding.h"
    -#include "debug.h"
    -#include "session.h"
    -#include "resolver.h"
    -#include "config.h"
    -
    -#include <errno.h>
    -#include <string.h>
    -#include <stdlib.h>
    -#include <time.h>
    -#include <ctype.h>
    -#ifdef GG_CONFIG_HAVE_GNUTLS
    -# include <gnutls/gnutls.h>
    -# include <gnutls/x509.h>
    -#endif
    -#ifdef GG_CONFIG_HAVE_OPENSSL
    -# include <openssl/err.h>
    -# include <openssl/x509.h>
    -# include <openssl/rand.h>
    -#endif
    -
    -/**
    - * Zwalnia pamięć zajmowaną przez informację o zdarzeniu.
    - *
    - * Funkcję należy wywoływać za każdym razem gdy funkcja biblioteki zwróci
    - * strukturę \c gg_event.
    - *
    - * \param e Struktura zdarzenia
    - *
    - * \ingroup events
    - */
    -void gg_event_free(struct gg_event *e)
    -{
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e);
    -
    - if (!e)
    - return;
    -
    - switch (e->type) {
    - case GG_EVENT_MSG:
    - case GG_EVENT_MULTILOGON_MSG:
    - free(e->event.msg.message);
    - free(e->event.msg.formats);
    - free(e->event.msg.recipients);
    - free(e->event.msg.xhtml_message);
    - break;
    -
    - case GG_EVENT_NOTIFY:
    - free(e->event.notify);
    - break;
    -
    - case GG_EVENT_NOTIFY60:
    - {
    - int i;
    -
    - for (i = 0; e->event.notify60[i].uin; i++)
    - free(e->event.notify60[i].descr);
    -
    - free(e->event.notify60);
    -
    - break;
    - }
    -
    - case GG_EVENT_STATUS60:
    - free(e->event.status60.descr);
    - break;
    -
    - case GG_EVENT_STATUS:
    - free(e->event.status.descr);
    - break;
    -
    - case GG_EVENT_NOTIFY_DESCR:
    - free(e->event.notify_descr.notify);
    - free(e->event.notify_descr.descr);
    - break;
    -
    - case GG_EVENT_DCC_VOICE_DATA:
    - free(e->event.dcc_voice_data.data);
    - break;
    -
    - case GG_EVENT_PUBDIR50_SEARCH_REPLY:
    - case GG_EVENT_PUBDIR50_READ:
    - case GG_EVENT_PUBDIR50_WRITE:
    - gg_pubdir50_free(e->event.pubdir50);
    - break;
    -
    - case GG_EVENT_USERLIST:
    - free(e->event.userlist.reply);
    - break;
    -
    - case GG_EVENT_IMAGE_REPLY:
    - free(e->event.image_reply.filename);
    - free(e->event.image_reply.image);
    - break;
    -
    - case GG_EVENT_XML_EVENT:
    - free(e->event.xml_event.data);
    - break;
    -
    - case GG_EVENT_JSON_EVENT:
    - free(e->event.json_event.data);
    - free(e->event.json_event.type);
    - break;
    -
    - case GG_EVENT_USER_DATA:
    - {
    - unsigned int i, j;
    -
    - for (i = 0; i < e->event.user_data.user_count; i++) {
    - for (j = 0; j < e->event.user_data.users[i].attr_count; j++) {
    - free(e->event.user_data.users[i].attrs[j].key);
    - free(e->event.user_data.users[i].attrs[j].value);
    - }
    -
    - free(e->event.user_data.users[i].attrs);
    - }
    -
    - free(e->event.user_data.users);
    -
    - break;
    - }
    -
    - case GG_EVENT_MULTILOGON_INFO:
    - {
    - int i;
    -
    - for (i = 0; i < e->event.multilogon_info.count; i++)
    - free(e->event.multilogon_info.sessions[i].name);
    -
    - free(e->event.multilogon_info.sessions);
    -
    - break;
    - }
    -
    - case GG_EVENT_USERLIST100_REPLY:
    - free(e->event.userlist100_reply.reply);
    - break;
    -
    - case GG_EVENT_IMTOKEN:
    - free(e->event.imtoken.imtoken);
    - break;
    -
    - case GG_EVENT_CHAT_INFO:
    - free(e->event.chat_info.participants);
    - break;
    - }
    -
    - free(e);
    -}
    -
    -/** \cond internal */
    -
    -/**
    - * \internal Usuwa obrazek z kolejki do wysłania.
    - *
    - * \param s Struktura sesji
    - * \param q Struktura obrazka
    - * \param freeq Flaga zwolnienia elementu kolejki
    - *
    - * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
    - */
    -int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq)
    -{
    - if (!s || !q) {
    - errno = EFAULT;
    - return -1;
    - }
    -
    - if (s->images == q)
    - s->images = q->next;
    - else {
    - struct gg_image_queue *qq;
    -
    - for (qq = s->images; qq; qq = qq->next) {
    - if (qq->next == q) {
    - qq->next = q->next;
    - break;
    - }
    - }
    - }
    -
    - if (freeq) {
    - free(q->image);
    - free(q->filename);
    - free(q);
    - }
    -
    - return 0;
    -}
    -
    -/** \endcond */
    -
    -/**
    - * \internal Inicjalizuje struktury SSL.
    - *
    - * \param gs Struktura sesji
    - *
    - * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
    - */
    -int gg_session_init_ssl(struct gg_session *gs)
    -{
    -#ifdef GG_CONFIG_HAVE_GNUTLS
    - gg_session_gnutls_t *tmp;
    -
    - tmp = (gg_session_gnutls_t*) gs->ssl;
    -
    - if (tmp == NULL) {
    - tmp = malloc(sizeof(gg_session_gnutls_t));
    -
    - if (tmp == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_session_connect() out of memory for GnuTLS session\n");
    - return -1;
    - }
    -
    - memset(tmp, 0, sizeof(gg_session_gnutls_t));
    -
    - gs->ssl = tmp;
    -
    - gnutls_global_init();
    - gnutls_certificate_allocate_credentials(&tmp->xcred);
    -#ifdef GG_CONFIG_SSL_SYSTEM_TRUST
    -#ifdef HAVE_GNUTLS_CERTIFICATE_SET_X509_SYSTEM_TRUST
    - gnutls_certificate_set_x509_system_trust(tmp->xcred);
    -#else
    - gnutls_certificate_set_x509_trust_file(tmp->xcred,
    - GG_CONFIG_GNUTLS_SYSTEM_TRUST_STORE,
    - GNUTLS_X509_FMT_PEM);
    -#endif
    -#endif
    - } else {
    - gnutls_deinit(tmp->session);
    - }
    -
    - gnutls_init(&tmp->session, GNUTLS_CLIENT);
    - gnutls_set_default_priority(tmp->session);
    - gnutls_credentials_set(tmp->session, GNUTLS_CRD_CERTIFICATE, tmp->xcred);
    - gnutls_transport_set_ptr(tmp->session, (gnutls_transport_ptr_t) (intptr_t) gs->fd);
    -#endif
    -
    -#ifdef GG_CONFIG_HAVE_OPENSSL
    - char buf[1024];
    -
    - OpenSSL_add_ssl_algorithms();
    -
    - if (!RAND_status()) {
    - char rdata[1024];
    - struct {
    - time_t time;
    - void *ptr;
    - } rstruct;
    -
    - time(&rstruct.time);
    - rstruct.ptr = (void *) &rstruct;
    -
    - RAND_seed((void *) rdata, sizeof(rdata));
    - RAND_seed((void *) &rstruct, sizeof(rstruct));
    - }
    -
    - if (gs->ssl_ctx == NULL) {
    - gs->ssl_ctx = SSL_CTX_new(SSLv3_client_method());
    -
    - if (gs->ssl_ctx == NULL) {
    - ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
    - gg_debug(GG_DEBUG_MISC, "// gg_session_connect() SSL_CTX_new() failed: %s\n", buf);
    - return -1;
    - }
    -
    - SSL_CTX_set_verify(gs->ssl_ctx, SSL_VERIFY_NONE, NULL);
    -#ifdef GG_CONFIG_SSL_SYSTEM_TRUST
    - SSL_CTX_set_default_verify_paths(gs->ssl_ctx);
    -#endif
    - }
    -
    - if (gs->ssl != NULL)
    - SSL_free(gs->ssl);
    -
    - gs->ssl = SSL_new(gs->ssl_ctx);
    -
    - if (gs->ssl == NULL) {
    - ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
    - gg_debug(GG_DEBUG_MISC, "// gg_session_connect() SSL_new() failed: %s\n", buf);
    - return -1;
    - }
    -
    - SSL_set_fd(gs->ssl, gs->fd);
    -#endif
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Funkcja próbuje wysłać dane zakolejkowane do wysyłki.
    - *
    - * \param sess Struktura sesji
    - *
    - * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
    - */
    -static int gg_send_queued_data(struct gg_session *sess)
    -{
    - int res;
    -
    - if (sess->send_buf == NULL || sess->send_left == 0)
    - return 0;
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending %d bytes of queued data\n", sess->send_left);
    -
    - res = send(sess->fd, sess->send_buf, sess->send_left, 0);
    -
    - if (res == -1) {
    - if (errno == EAGAIN || errno == EINTR) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
    - " non-critical send error (errno=%d, %s)\n",
    - errno, strerror(errno));
    -
    - return 0;
    - }
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() send() "
    - "failed (errno=%d, %s)\n", errno, strerror(errno));
    -
    - return -1;
    - }
    -
    - if (res == sess->send_left) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent all queued data\n");
    - free(sess->send_buf);
    - sess->send_buf = NULL;
    - sess->send_left = 0;
    - } else if (res > 0) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent %d"
    - " bytes of queued data, %d bytes left\n",
    - res, sess->send_left - res);
    -
    - memmove(sess->send_buf, sess->send_buf + res, sess->send_left - res);
    - sess->send_left -= res;
    - }
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Sprawdza wynik połączenia asynchronicznego.
    - * \param gs Struktura sesji
    - * \param res_ptr Wskaźnik na kod błędu
    - * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
    - */
    -static int gg_async_connect_failed(struct gg_session *gs, int *res_ptr)
    -{
    - int res = 0;
    - socklen_t res_size = sizeof(res);
    -
    - if (!gs->async)
    - return 0;
    -
    - if (gs->timeout == 0) {
    - *res_ptr = ETIMEDOUT;
    - return 1;
    - }
    -
    - if (getsockopt(gs->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) == -1) {
    - *res_ptr = errno;
    - return 1;
    - }
    -
    - if (res != 0) {
    - *res_ptr = res;
    - return 1;
    - }
    -
    - *res_ptr = 0;
    -
    - return 0;
    -}
    -
    -typedef enum
    -{
    - GG_ACTION_WAIT,
    - GG_ACTION_NEXT,
    - GG_ACTION_FAIL
    -} gg_action_t;
    -
    -typedef gg_action_t (*gg_state_handler_t)(struct gg_session *gs,
    - struct gg_event *ge, enum gg_state_t next_state,
    - enum gg_state_t alt_state, enum gg_state_t alt2_state);
    -
    -typedef struct
    -{
    - enum gg_state_t state;
    - gg_state_handler_t handler;
    - enum gg_state_t next_state;
    - enum gg_state_t alt_state;
    - enum gg_state_t alt2_state;
    -} gg_state_transition_t;
    -
    -/* zwraca:
    - * -1 w przypadku błędu
    - * 0 jeżeli nie ma ustawionego specjalnego managera gniazdek
    - * 1 w przypadku powodzenia
    - */
    -static int gg_handle_resolve_custom(struct gg_session *sess, enum gg_state_t next_state)
    -{
    - struct gg_session_private *p = sess->private_data;
    - int is_tls = 0;
    - int port;
    -
    - if (p->socket_manager_type == GG_SOCKET_MANAGER_TYPE_INTERNAL)
    - return 0;
    -
    - if (p->socket_manager.connect_cb == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "// gg_handle_resolve_custom() socket_manager.connect "
    - "callback is empty\n");
    - return -1;
    - }
    -
    - if (p->socket_handle != NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "// gg_handle_resolve_custom() socket_handle is not "
    - "NULL\n");
    - return -1;
    - }
    -
    - port = sess->connect_port[sess->connect_index];
    - if (next_state == GG_STATE_SEND_HUB)
    - port = GG_APPMSG_PORT;
    -
    - if (sess->ssl_flag != GG_SSL_DISABLED &&
    - next_state == GG_STATE_READING_KEY)
    - {
    - /* XXX: w tej chwili nie ma możliwości łączenia się do HUBa po
    - * SSL, ale może będzie w przyszłości */
    - is_tls = 1;
    - }
    -
    - if (is_tls && p->socket_manager_type == GG_SOCKET_MANAGER_TYPE_TCP) {
    - is_tls = 0;
    - next_state = GG_STATE_TLS_NEGOTIATION;
    - }
    -
    - if (port <= 0) {
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "// gg_handle_resolve_custom() port <= 0\n");
    - return -1;
    - }
    -
    - p->socket_failure = 0;
    - p->socket_next_state = next_state;
    - p->socket_handle = p->socket_manager.connect_cb(
    - p->socket_manager.cb_data, sess->resolver_host, port, is_tls,
    - sess->async, sess);
    -
    - if (p->socket_failure != 0) {
    - if (p->socket_handle != NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_WARNING,
    - "// gg_handle_resolve_custom() handle should be"
    - " empty on error\n");
    - }
    - return -1;
    - }
    -
    - if (p->socket_handle == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "// gg_handle_resolve_custom() returned empty "
    - "handle\n");
    - return -1;
    - }
    -
    - return 1;
    -}
    -
    -static gg_action_t gg_handle_resolve_sync(struct gg_session *sess,
    - struct gg_event *e, enum gg_state_t next_state,
    - enum gg_state_t alt_state, enum gg_state_t alt2_state)
    -{
    - struct in_addr addr;
    - int res;
    -
    - res = gg_handle_resolve_custom(sess, alt_state);
    - if (res == 1)
    - return GG_ACTION_NEXT;
    - else if (res == -1)
    - return GG_ACTION_FAIL;
    -
    - addr.s_addr = inet_addr(sess->resolver_host);
    -
    - if (addr.s_addr == INADDR_NONE) {
    - struct in_addr *addr_list = NULL;
    - unsigned int addr_count;
    -
    - if (gg_gethostbyname_real(sess->resolver_host, &addr_list, &addr_count, 0) == -1) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
    - " host %s not found\n", sess->resolver_host);
    - e->event.failure = GG_FAILURE_RESOLVING;
    - free(addr_list);
    - return GG_ACTION_FAIL;
    - }
    -
    - sess->resolver_result = addr_list;
    - sess->resolver_count = addr_count;
    - sess->resolver_index = 0;
    - } else {
    - sess->resolver_result = malloc(sizeof(struct in_addr));
    -
    - if (sess->resolver_result == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory\n");
    - return GG_ACTION_FAIL;
    - }
    -
    - sess->resolver_result[0].s_addr = addr.s_addr;
    - sess->resolver_count = 1;
    - sess->resolver_index = 0;
    - }
    -
    - sess->state = next_state;
    -
    - return GG_ACTION_NEXT;
    -}
    -
    -static gg_action_t gg_handle_resolve_async(struct gg_session *sess,
    - struct gg_event *e, enum gg_state_t next_state,
    - enum gg_state_t alt_state, enum gg_state_t alt2_state)
    -{
    - int res;
    -
    - res = gg_handle_resolve_custom(sess, alt_state);
    - if (res == 1)
    - return GG_ACTION_WAIT;
    - else if (res == -1)
    - return GG_ACTION_FAIL;
    -
    - if (sess->resolver_start(&sess->fd, &sess->resolver, sess->resolver_host) == -1) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
    - "resolving failed (errno=%d, %s)\n",
    - errno, strerror(errno));
    - e->event.failure = GG_FAILURE_RESOLVING;
    - return GG_ACTION_FAIL;
    - }
    -
    - sess->state = next_state;
    - sess->check = GG_CHECK_READ;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    -
    - return GG_ACTION_WAIT;
    -}
    -
    -static gg_action_t gg_handle_resolving(struct gg_session *sess,
    - struct gg_event *e, enum gg_state_t next_state,
    - enum gg_state_t alt_state, enum gg_state_t alt2_state)
    -{
    - char buf[256];
    - int count = -1;
    - int res;
    - unsigned int i;
    - struct in_addr *addrs;
    -
    - res = gg_resolver_recv(sess->fd, buf, sizeof(buf));
    -
    - if (res == -1 && (errno == EAGAIN || errno == EINTR)) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
    - "non-critical error (errno=%d, %s)\n",
    - errno, strerror(errno));
    - return GG_ACTION_WAIT;
    - }
    -
    - sess->resolver_cleanup(&sess->resolver, 0);
    -
    - if (res == -1) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() read "
    - "error (errno=%d, %s)\n", errno, strerror(errno));
    - e->event.failure = GG_FAILURE_RESOLVING;
    - return GG_ACTION_FAIL;
    - }
    -
    - if (res > 0) {
    - char *tmp;
    -
    - tmp = realloc(sess->recv_buf, sess->recv_done + res);
    -
    - if (tmp == NULL)
    - return GG_ACTION_FAIL;
    -
    - sess->recv_buf = tmp;
    - memcpy(sess->recv_buf + sess->recv_done, buf, res);
    - sess->recv_done += res;
    - }
    -
    - /* Sprawdź, czy mamy listę zakończoną INADDR_NONE */
    -
    - addrs = (struct in_addr *)(void *)sess->recv_buf;
    -
    - for (i = 0; i < sess->recv_done / sizeof(struct in_addr); i++) {
    - if (addrs[i].s_addr == INADDR_NONE) {
    - count = i;
    - break;
    - }
    - }
    -
    - /* Nie znaleziono hosta */
    -
    - if (count == 0) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() host not found\n");
    - e->event.failure = GG_FAILURE_RESOLVING;
    - return GG_ACTION_FAIL;
    - }
    -
    - /* Nie mamy pełnej listy, ale połączenie zerwane */
    -
    - if (res == 0 && count == -1) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection broken\n");
    - e->event.failure = GG_FAILURE_RESOLVING;
    - return GG_ACTION_FAIL;
    - }
    -
    - /* Nie mamy pełnej listy, normalna sytuacja */
    -
    - if (count == -1)
    - return GG_ACTION_WAIT;
    -
    -#ifndef GG_DEBUG_DISABLE
    - if ((gg_debug_level & GG_DEBUG_DUMP) && (count > 0)) {
    - char *list;
    - size_t len;
    -
    - len = 0;
    -
    - for (i = 0; i < (unsigned int) count; i++) {
    - if (i > 0)
    - len += 2;
    -
    - len += strlen(inet_ntoa(addrs[i]));
    - }
    -
    - list = malloc(len + 1);
    -
    - if (list == NULL)
    - return GG_ACTION_FAIL;
    -
    - list[0] = 0;
    -
    - for (i = 0; i < (unsigned int) count; i++) {
    - if (i > 0)
    - strcat(list, ", ");
    -
    - strcat(list, inet_ntoa(addrs[i]));
    - }
    -
    - gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() resolved: %s\n", list);
    -
    - free(list);
    - }
    -#endif
    -
    - gg_close(sess);
    -
    - sess->state = next_state;
    - sess->resolver_result = addrs;
    - sess->resolver_count = count;
    - sess->resolver_index = 0;
    - sess->recv_buf = NULL;
    - sess->recv_done = 0;
    -
    - return GG_ACTION_NEXT;
    -}
    -
    -static gg_action_t gg_handle_connect(struct gg_session *sess,
    - struct gg_event *e, enum gg_state_t next_state,
    - enum gg_state_t alt_state, enum gg_state_t alt2_state)
    -{
    - struct in_addr addr;
    - int port;
    -
    - if (sess->resolver_index >= sess->resolver_count) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of addresses to connect to\n");
    - e->event.failure = GG_FAILURE_CONNECTING;
    - return GG_ACTION_FAIL;
    - }
    -
    - addr = sess->resolver_result[sess->resolver_index];
    -
    - if (sess->state == GG_STATE_CONNECT_HUB) {
    - sess->hub_addr = addr.s_addr;
    - port = GG_APPMSG_PORT;
    - } else {
    - sess->proxy_addr = addr.s_addr;
    - port = sess->proxy_port;
    - }
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connecting to %s:%d\n", inet_ntoa(addr), port);
    -
    - sess->fd = gg_connect(&addr, port, sess->async);
    -
    - if (sess->fd == -1) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
    - "connection failed (errno=%d, %s)\n",
    - errno, strerror(errno));
    - sess->resolver_index++;
    - return GG_ACTION_NEXT;
    - }
    -
    - sess->state = next_state;
    - sess->check = GG_CHECK_WRITE;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    - sess->soft_timeout = 1;
    -
    - return GG_ACTION_WAIT;
    -}
    -
    -static gg_action_t gg_handle_connecting(struct gg_session *sess,
    - struct gg_event *e, enum gg_state_t next_state,
    - enum gg_state_t alt_state, enum gg_state_t alt2_state)
    -{
    - int res;
    -
    - sess->soft_timeout = 0;
    -
    - if (gg_async_connect_failed(sess, &res)) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
    - "connection failed (errno=%d, %s)\n",
    - res, strerror(res));
    - gg_close(sess);
    - sess->resolver_index++;
    - sess->state = alt_state;
    - } else {
    - /* Z proxy zwykle łączymy się dwa razy, więc nie zwalniamy
    - * adresów IP po pierwszym połączeniu. */
    - if (sess->state != GG_STATE_CONNECTING_PROXY_HUB) {
    - free(sess->resolver_result);
    - sess->resolver_result = NULL;
    - }
    -
    - sess->state = next_state;
    - }
    -
    - return GG_ACTION_NEXT;
    -}
    -
    -static gg_action_t gg_handle_connect_gg(struct gg_session *sess,
    - struct gg_event *e, enum gg_state_t next_state,
    - enum gg_state_t alt_state, enum gg_state_t alt2_state)
    -{
    - struct in_addr addr;
    - uint16_t port;
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "resolver_index=%d, "
    - "connect_index=%d, connect_port={%d,%d}\n",
    - sess->resolver_index, sess->connect_index,
    - sess->connect_port[0], sess->connect_port[1]);
    -
    - if ((unsigned int) sess->connect_index >=
    - sizeof(sess->connect_port) / sizeof(sess->connect_port[0]) ||
    - sess->connect_port[sess->connect_index] == 0)
    - {
    - sess->connect_index = 0;
    - sess->resolver_index++;
    - if (sess->resolver_index >= sess->resolver_count) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of addresses to connect to\n");
    - e->event.failure = GG_FAILURE_CONNECTING;
    - return GG_ACTION_FAIL;
    - }
    - }
    -
    - addr = sess->resolver_result[sess->resolver_index];
    - port = sess->connect_port[sess->connect_index];
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connecting to %s:%d\n", inet_ntoa(addr), port);
    -
    - sess->server_addr = addr.s_addr;
    - sess->fd = gg_connect(&addr, port, sess->async);
    -
    - if (sess->fd == -1) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
    - "connection failed (errno=%d, %s)\n",
    - errno, strerror(errno));
    - sess->connect_index++;
    - return GG_ACTION_NEXT;
    - }
    -
    - sess->state = next_state;
    - sess->check = GG_CHECK_WRITE;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    - sess->soft_timeout = 1;
    -
    - return GG_ACTION_WAIT;
    -}
    -
    -static gg_action_t gg_handle_connecting_gg(struct gg_session *sess,
    - struct gg_event *e, enum gg_state_t next_state,
    - enum gg_state_t alt_state, enum gg_state_t alt2_state)
    -{
    - int res;
    -
    - sess->soft_timeout = 0;
    -
    - /* jeśli wystąpił błąd podczas łączenia się... */
    - if (gg_async_connect_failed(sess, &res)) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
    - "connection failed (errno=%d, %s)\n",
    - res, strerror(res));
    - gg_close(sess);
    - sess->connect_index++;
    - sess->state = alt_state;
    - return GG_ACTION_NEXT;
    - }
    -
    - free(sess->resolver_result);
    - sess->resolver_result = NULL;
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
    -
    - if (sess->ssl_flag != GG_SSL_DISABLED) {
    - if (gg_session_init_ssl(sess) == -1) {
    - e->event.failure = GG_FAILURE_TLS;
    - return GG_ACTION_FAIL;
    - }
    -
    - sess->state = alt2_state;
    - sess->check = GG_CHECK_WRITE;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    -
    - return GG_ACTION_NEXT;
    - } else {
    - sess->state = next_state;
    - sess->check = GG_CHECK_READ;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    -
    - return GG_ACTION_WAIT;
    - }
    -}
    -
    -static gg_action_t gg_handle_send_hub(struct gg_session *sess,
    - struct gg_event *e, enum gg_state_t next_state,
    - enum gg_state_t alt_state, enum gg_state_t alt2_state)
    -{
    - char *req, *client, *auth;
    - const char *host;
    - int res;
    - int proxy;
    - size_t req_len;
    -
    - if (sess->client_version != NULL && isdigit(sess->client_version[0]))
    - client = gg_urlencode(sess->client_version);
    - else if (sess->protocol_version <= GG_PROTOCOL_VERSION_100)
    - client = gg_urlencode(GG_DEFAULT_CLIENT_VERSION_100);
    - else /* sess->protocol_version >= GG_PROTOCOL_VERSION_110 */
    - client = gg_urlencode(GG_DEFAULT_CLIENT_VERSION_110);
    -
    - if (client == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
    - return GG_ACTION_FAIL;
    - }
    -
    - if (sess->proxy_addr && sess->proxy_port) {
    - host = "http://" GG_APPMSG_HOST;
    - proxy = 1;
    - } else {
    - host = "";
    - proxy = 0;
    - }
    -
    - auth = gg_proxy_auth();
    -
    - if (sess->ssl_flag != GG_SSL_DISABLED) {
    - req = gg_saprintf
    - ("GET %s/appsvc/appmsg_ver10.asp?fmnumber=%u&fmt=2&"
    - "lastmsg=%d&version=%s&age=2&gender=1 HTTP/1.0\r\n"
    - "Connection: close\r\n"
    - "Host: " GG_APPMSG_HOST "\r\n"
    - "%s"
    - "\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
    - } else {
    - req = gg_saprintf
    - ("GET %s/appsvc/appmsg_ver8.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s HTTP/1.0\r\n"
    - "Host: " GG_APPMSG_HOST "\r\n"
    - "%s"
    - "\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
    - }
    -
    - free(auth);
    - free(client);
    -
    - if (req == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory\n");
    - e->event.failure = GG_FAILURE_PROXY;
    - return GG_ACTION_FAIL;
    - }
    -
    - req_len = strlen(req);
    -
    - gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// sending http query:\n%s", req);
    -
    - res = send(sess->fd, req, req_len, 0);
    -
    - free(req);
    -
    - if (res == -1 && errno != EINTR && errno != EAGAIN) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
    - e->event.failure = (!proxy) ? GG_FAILURE_HUB : GG_FAILURE_PROXY;
    - return GG_ACTION_FAIL;
    - }
    -
    - if ((size_t) res < req_len) {
    - sess->state = alt_state;
    - sess->check = GG_CHECK_WRITE;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    - } else {
    - sess->state = next_state;
    - sess->check = GG_CHECK_READ;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    - }
    -
    - return GG_ACTION_WAIT;
    -}
    -
    -static gg_action_t gg_handle_sending_hub_proxy(struct gg_session *sess,
    - struct gg_event *e, enum gg_state_t next_state,
    - enum gg_state_t alt_state, enum gg_state_t alt2_state)
    -{
    - if (gg_send_queued_data(sess) == -1) {
    - e->event.failure = GG_FAILURE_WRITING;
    - return GG_ACTION_FAIL;
    - }
    -
    - if (sess->send_left > 0)
    - return GG_ACTION_WAIT;
    -
    - sess->state = next_state;
    - sess->check = GG_CHECK_READ;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    -
    - return GG_ACTION_WAIT;
    -}
    -
    -static gg_action_t gg_handle_reading_hub_proxy(struct gg_session *sess,
    - struct gg_event *e, enum gg_state_t next_state,
    - enum gg_state_t alt_state, enum gg_state_t alt2_state)
    -{
    - char buf[1024], *tmp, host[129];
    - int port = GG_DEFAULT_PORT;
    - int reply;
    - const char *body;
    - struct in_addr addr;
    - int res;
    - char **host_white;
    - char *host_white_default[] = GG_DEFAULT_HOST_WHITE_LIST;
    -
    - res = recv(sess->fd, buf, sizeof(buf), 0);
    -
    - if (res == -1 && (errno == EAGAIN || errno == EINTR)) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
    - "non-critical recv error (errno=%d, %s)\n",
    - errno, strerror(errno));
    - return GG_ACTION_WAIT;
    - }
    -
    - if (res == -1) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() recv "
    - "error (errno=%d, %s)\n", errno, strerror(errno));
    - e->event.failure = GG_FAILURE_CONNECTING;
    - return GG_ACTION_FAIL;
    - }
    -
    - if (res != 0) {
    - tmp = realloc(sess->recv_buf, sess->recv_done + res + 1);
    -
    - if (tmp == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for http reply\n");
    - return GG_ACTION_FAIL;
    - }
    -
    - sess->recv_buf = tmp;
    - memcpy(sess->recv_buf + sess->recv_done, buf, res);
    - sess->recv_done += res;
    - sess->recv_buf[sess->recv_done] = 0;
    - }
    -
    - if (res == 0 && sess->recv_buf == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection closed\n");
    - e->event.failure = GG_FAILURE_CONNECTING;
    - return GG_ACTION_FAIL;
    - }
    -
    - if (res != 0)
    - return GG_ACTION_WAIT;
    -
    - gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// received http reply:\n%s", sess->recv_buf);
    -
    - res = sscanf(sess->recv_buf, "HTTP/1.%*d %3d ", &reply);
    -
    - /* sprawdzamy, czy wszystko w porządku. */
    - if (res != 1 || reply != 200) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n");
    - e->event.failure = GG_FAILURE_CONNECTING;
    - return GG_ACTION_FAIL;
    - }
    -
    - /* szukamy początku treści */
    - body = strstr(sess->recv_buf, "\r\n\r\n");
    -
    - if (body == NULL) {
    - body = strstr(sess->recv_buf, "\n\n");
    -
    - if (body == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't find body\n");
    - e->event.failure = GG_FAILURE_CONNECTING;
    - return GG_ACTION_FAIL;
    - } else {
    - body += 2;
    - }
    - } else {
    - body += 4;
    - }
    -
    - /* 17591 0 91.197.13.71:8074 91.197.13.71 */
    - res = sscanf(body, "%d %*d %128s", &reply, host);
    -
    - if (res != 2) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid hub reply, connection failed\n");
    - e->event.failure = GG_FAILURE_CONNECTING;
    - return GG_ACTION_FAIL;
    - }
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "reply=%d, host=\"%s\"\n", reply, host);
    -
    - /* jeśli pierwsza liczba w linii nie jest równa zeru,
    - * oznacza to, że mamy wiadomość systemową. */
    - if (reply != 0) {
    - tmp = strchr(body, '\n');
    -
    - if (tmp != NULL) {
    - e->type = GG_EVENT_MSG;
    - e->event.msg.msgclass = reply;
    - e->event.msg.sender = 0;
    - e->event.msg.message = (unsigned char*) strdup(tmp + 1);
    -
    - if (e->event.msg.message == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_watch_fd() not enough memory "
    - "for system message\n");
    - return GG_ACTION_FAIL;
    - }
    - }
    - }
    -
    - gg_close(sess);
    -
    - tmp = strchr(host, ':');
    -
    - if (tmp != NULL) {
    - *tmp = 0;
    - port = atoi(tmp + 1);
    - }
    -
    - if (strcmp(host, "notoperating") == 0) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n");
    - e->event.failure = GG_FAILURE_UNAVAILABLE;
    - return GG_ACTION_FAIL;
    - }
    -
    - addr.s_addr = inet_addr(host);
    - if (addr.s_addr == INADDR_NONE)
    - addr.s_addr = 0;
    - sess->server_addr = addr.s_addr;
    -
    - free(sess->recv_buf);
    - sess->recv_buf = NULL;
    - sess->recv_done = 0;
    -
    - if (sess->state != GG_STATE_READING_PROXY_HUB) {
    - if (sess->port == 0) {
    - sess->connect_port[0] = port;
    - sess->connect_port[1] = (port != GG_HTTPS_PORT) ? GG_HTTPS_PORT : 0;
    - } else {
    - sess->connect_port[0] = sess->port;
    - sess->connect_port[1] = 0;
    - }
    - } else {
    - sess->connect_port[0] = (sess->port == 0) ? GG_HTTPS_PORT : sess->port;
    - sess->connect_port[1] = 0;
    - }
    -
    - free(sess->connect_host);
    - sess->connect_host = strdup(host);
    -
    - if (sess->connect_host == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory\n");
    - return GG_ACTION_FAIL;
    - }
    -
    - host_white = sess->private_data->host_white_list;
    - if (!host_white)
    - host_white = host_white_default;
    -
    - if (sess->ssl_flag == GG_SSL_REQUIRED && host_white[0] != NULL) {
    - int host_ok = 0;
    - char **it;
    - int host_len;
    -
    - host_len = strlen(sess->connect_host);
    -
    - for (it = host_white; *it != NULL; it++) {
    - const char *white = *it;
    - int white_len, dom_offset;
    -
    - white_len = strlen(white);
    - if (white_len > host_len)
    - continue;
    -
    - dom_offset = host_len - white_len;
    - if (strncasecmp(sess->connect_host + dom_offset, white,
    - white_len) != 0)
    - {
    - continue;
    - }
    -
    - if (white_len < host_len) {
    - if (sess->connect_host[dom_offset - 1] != '.')
    - continue;
    - }
    -
    - host_ok = 1;
    - break;
    - }
    -
    - if (!host_ok) {
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "// gg_watch_fd() the HUB server returned "
    - "a host that is not trusted (%s)\n",
    - sess->connect_host);
    - e->event.failure = GG_FAILURE_TLS;
    - return GG_ACTION_FAIL;
    - }
    - }
    -
    - if (sess->state == GG_STATE_READING_HUB)
    - sess->resolver_host = sess->connect_host;
    -
    - /* Jeśli łączymy się przez proxy, zacznijmy od początku listy */
    - sess->resolver_index = 0;
    -
    - sess->state = (sess->async) ? next_state : alt_state;
    -
    - return GG_ACTION_NEXT;
    -}
    -
    -static gg_action_t gg_handle_send_proxy_gg(struct gg_session *sess,
    - struct gg_event *e, enum gg_state_t next_state,
    - enum gg_state_t alt_state, enum gg_state_t alt2_state)
    -{
    - char *req, *auth;
    - size_t req_len;
    - int res;
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() %s\n", gg_debug_state(sess->state));
    -
    - if (sess->connect_index > 1 || sess->connect_port[sess->connect_index] == 0) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of connection candidates\n");
    - e->event.failure = GG_FAILURE_CONNECTING;
    - return GG_ACTION_FAIL;
    - }
    -
    - auth = gg_proxy_auth();
    -
    - req = gg_saprintf("CONNECT %s:%d HTTP/1.0\r\n%s\r\n",
    - sess->connect_host, sess->connect_port[sess->connect_index],
    - (auth) ? auth : "");
    -
    - free(auth);
    -
    - sess->connect_index++;
    -
    - if (req == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory\n");
    - e->event.failure = GG_FAILURE_PROXY;
    - return GG_ACTION_FAIL;
    - }
    -
    - req_len = strlen(req);
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n%s", req);
    -
    - res = send(sess->fd, req, req_len, 0);
    -
    - free(req);
    -
    - if (res == -1 && errno != EINTR && errno != EAGAIN) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n");
    - e->event.failure = GG_FAILURE_PROXY;
    - return GG_ACTION_FAIL;
    - }
    -
    - if ((size_t) res < req_len) {
    - sess->state = alt_state;
    - sess->check = GG_CHECK_WRITE;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    - } else {
    - sess->state = next_state;
    - sess->check = GG_CHECK_READ;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    - }
    -
    - return GG_ACTION_WAIT;
    -}
    -
    -static gg_action_t gg_handle_tls_negotiation(struct gg_session *sess,
    - struct gg_event *e, enum gg_state_t next_state,
    - enum gg_state_t alt_state, enum gg_state_t alt2_state)
    -{
    -#if defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL)
    - int valid_hostname = 0;
    -#endif
    -
    -#ifdef GG_CONFIG_HAVE_GNUTLS
    - unsigned int status;
    - int res;
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
    -
    - for (;;) {
    - res = gnutls_handshake(GG_SESSION_GNUTLS(sess));
    -
    - if (res == GNUTLS_E_AGAIN) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake GNUTLS_E_AGAIN\n");
    -
    - if (gnutls_record_get_direction(GG_SESSION_GNUTLS(sess)) == 0)
    - sess->check = GG_CHECK_READ;
    - else
    - sess->check = GG_CHECK_WRITE;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    - return GG_ACTION_WAIT;
    - }
    -
    - if (res == GNUTLS_E_INTERRUPTED) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS handshake GNUTLS_E_INTERRUPTED\n");
    - continue;
    - }
    -
    - if (res != GNUTLS_E_SUCCESS) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
    - " TLS handshake error: %d, %s\n",
    - res, gnutls_strerror(res));
    - e->event.failure = GG_FAILURE_TLS;
    - return GG_ACTION_FAIL;
    - }
    -
    - break;
    - }
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n");
    - gg_debug_session(sess, GG_DEBUG_MISC, "// cipher: VERS-%s:%s:%s:%s:COMP-%s\n",
    - gnutls_protocol_get_name(gnutls_protocol_get_version(GG_SESSION_GNUTLS(sess))),
    - gnutls_cipher_get_name(gnutls_cipher_get(GG_SESSION_GNUTLS(sess))),
    - gnutls_kx_get_name(gnutls_kx_get(GG_SESSION_GNUTLS(sess))),
    - gnutls_mac_get_name(gnutls_mac_get(GG_SESSION_GNUTLS(sess))),
    - gnutls_compression_get_name(gnutls_compression_get(GG_SESSION_GNUTLS(sess))));
    -
    - if (gnutls_certificate_type_get(GG_SESSION_GNUTLS(sess)) == GNUTLS_CRT_X509) {
    - unsigned int peer_count;
    - const gnutls_datum_t *peers;
    - gnutls_x509_crt_t cert;
    -
    - if (gnutls_x509_crt_init(&cert) == 0) {
    - peers = gnutls_certificate_get_peers(GG_SESSION_GNUTLS(sess), &peer_count);
    -
    - if (peers != NULL) {
    - char buf[256];
    - size_t size;
    -
    - if (gnutls_x509_crt_import(cert, &peers[0], GNUTLS_X509_FMT_DER) == 0) {
    - size = sizeof(buf);
    - gnutls_x509_crt_get_dn(cert, buf, &size);
    - gg_debug_session(sess, GG_DEBUG_MISC, "// cert subject: %s\n", buf);
    - size = sizeof(buf);
    - gnutls_x509_crt_get_issuer_dn(cert, buf, &size);
    - gg_debug_session(sess, GG_DEBUG_MISC, "// cert issuer: %s\n", buf);
    -
    - if (gnutls_x509_crt_check_hostname(cert, sess->connect_host) != 0)
    - valid_hostname = 1;
    - }
    - }
    -
    - gnutls_x509_crt_deinit(cert);
    - }
    - }
    -
    - res = gnutls_certificate_verify_peers2(GG_SESSION_GNUTLS(sess), &status);
    -
    - if (res != 0 || status != 0) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "//   WARNING! unable to"
    - " verify peer certificate: 0x%x, %d, %s\n", status, res,
    - gnutls_strerror(res));
    -
    - if (sess->ssl_flag == GG_SSL_REQUIRED) {
    - e->event.failure = GG_FAILURE_TLS;
    - return GG_ACTION_FAIL;
    - }
    - } else {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// verified peer certificate\n");
    - }
    -
    -
    -#elif defined GG_CONFIG_HAVE_OPENSSL
    -
    - X509 *peer;
    - int res;
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() %s\n", gg_debug_state(sess->state));
    -
    - res = SSL_connect(GG_SESSION_OPENSSL(sess));
    -
    - if (res <= 0) {
    - int err;
    -
    - err = SSL_get_error(GG_SESSION_OPENSSL(sess), res);
    -
    - if (res == 0) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n");
    - e->event.failure = GG_FAILURE_TLS;
    - return GG_ACTION_FAIL;
    - }
    -
    - if (err == SSL_ERROR_WANT_READ) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n");
    -
    - sess->check = GG_CHECK_READ;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    - return GG_ACTION_WAIT;
    - } else if (err == SSL_ERROR_WANT_WRITE) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n");
    -
    - sess->check = GG_CHECK_WRITE;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    - return GG_ACTION_WAIT;
    - } else {
    - char buf[256];
    -
    - ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf);
    -
    - e->event.failure = GG_FAILURE_TLS;
    - return GG_ACTION_FAIL;
    - }
    - }
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation"
    - " succeded:\n// cipher: %s\n",
    - SSL_get_cipher_name(GG_SESSION_OPENSSL(sess)));
    -
    - peer = SSL_get_peer_certificate(GG_SESSION_OPENSSL(sess));
    -
    - if (peer == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n");
    -
    - if (sess->ssl_flag == GG_SSL_REQUIRED) {
    - e->event.failure = GG_FAILURE_TLS;
    - return GG_ACTION_FAIL;
    - }
    - } else {
    - char buf[256];
    - long res;
    -
    - X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf));
    - gg_debug_session(sess, GG_DEBUG_MISC, "// cert subject: %s\n", buf);
    -
    - X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf));
    - gg_debug_session(sess, GG_DEBUG_MISC, "// cert issuer: %s\n", buf);
    -
    - res = SSL_get_verify_result(GG_SESSION_OPENSSL(sess));
    -
    - if (res != X509_V_OK) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "//   WARNING! "
    - "unable to verify peer certificate! "
    - "res=%ld\n", res);
    -
    - if (sess->ssl_flag == GG_SSL_REQUIRED) {
    - e->event.failure = GG_FAILURE_TLS;
    - return GG_ACTION_FAIL;
    - }
    - } else {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// verified peer certificate\n");
    - }
    -
    - if (X509_NAME_get_text_by_NID(X509_get_subject_name(peer), NID_commonName, buf, sizeof(buf)) == -1)
    - buf[0] = 0;
    -
    - /* Obsługa certyfikatów z wieloznacznikiem */
    - if (strchr(buf, '*') == buf && strchr(buf + 1, '*') == NULL) {
    - char *tmp;
    -
    - tmp = strchr(sess->connect_host, '.');
    -
    - if (tmp != NULL)
    - valid_hostname = (strcasecmp(tmp, buf + 1) == 0);
    - } else {
    - valid_hostname = (strcasecmp(sess->connect_host, buf) == 0);
    - }
    - }
    -
    -#else
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() no SSL support\n");
    - e->event.failure = GG_FAILURE_TLS;
    - return GG_ACTION_FAIL;
    -
    -#endif
    -
    -#if defined(GG_CONFIG_HAVE_GNUTLS) || defined(GG_CONFIG_HAVE_OPENSSL)
    - if (!valid_hostname) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "//   WARNING! unable to verify hostname\n");
    -
    - if (sess->ssl_flag == GG_SSL_REQUIRED) {
    - e->event.failure = GG_FAILURE_TLS;
    - return GG_ACTION_FAIL;
    - }
    - }
    -
    - sess->state = next_state;
    - sess->check = GG_CHECK_READ;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    -
    - return GG_ACTION_WAIT;
    -#endif
    -}
    -
    -static gg_action_t gg_handle_reading_proxy_gg(struct gg_session *sess,
    - struct gg_event *e, enum gg_state_t next_state,
    - enum gg_state_t alt_state, enum gg_state_t alt2_state)
    -{
    - char buf[256];
    - int res;
    - int reply;
    - char *body;
    -
    - res = recv(sess->fd, buf, sizeof(buf), 0);
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "recv() = %d\n", res);
    -
    - if (res == -1 && (errno == EAGAIN || errno == EINTR)) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
    - "non-critical recv error (errno=%d, %s)\n",
    - errno, strerror(errno));
    - return GG_ACTION_WAIT;
    - }
    -
    - if (res == -1) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() recv "
    - "error (errno=%d, %s)\n", errno, strerror(errno));
    - e->event.failure = GG_FAILURE_CONNECTING;
    - return GG_ACTION_FAIL;
    - }
    -
    - if (res != 0) {
    - char *tmp;
    -
    - tmp = realloc(sess->recv_buf, sess->recv_done + res + 1);
    -
    - if (tmp == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for http reply\n");
    - return GG_ACTION_FAIL;
    - }
    -
    - sess->recv_buf = tmp;
    - memcpy(sess->recv_buf + sess->recv_done, buf, res);
    - sess->recv_done += res;
    - sess->recv_buf[sess->recv_done] = 0;
    - }
    -
    - if (res == 0 && sess->recv_buf == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection closed\n");
    - e->event.failure = GG_FAILURE_CONNECTING;
    - return GG_ACTION_FAIL;
    - }
    -
    - /* szukamy początku treści */
    - body = strstr(sess->recv_buf, "\r\n\r\n");
    -
    - if (body == NULL) {
    - body = strstr(sess->recv_buf, "\n\n");
    -
    - if (body == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't find body\n");
    - e->event.failure = GG_FAILURE_CONNECTING;
    - return GG_ACTION_FAIL;
    - } else {
    - body += 2;
    - }
    - } else {
    - body += 4;
    - }
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// found body!\n");
    -
    - gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// received proxy reply:\n%s\n", sess->recv_buf);
    -
    - res = sscanf(sess->recv_buf, "HTTP/1.%*d %3d ", &reply);
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "res = %d, reply = %d\n", res, reply);
    -
    - /* sprawdzamy, czy wszystko w porządku. */
    - if (res != 1 || reply != 200) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n");
    - e->event.failure = GG_FAILURE_CONNECTING;
    - return GG_ACTION_FAIL;
    - }
    -
    - if (sess->ssl_flag != GG_SSL_DISABLED) {
    - if (gg_session_init_ssl(sess) == -1) {
    - e->event.failure = GG_FAILURE_TLS;
    - return GG_ACTION_FAIL;
    - }
    -
    - /* Teoretycznie SSL jest inicjowany przez klienta, więc serwer
    - * nie powinien niczego wysłać. */
    - if (sess->recv_buf + sess->recv_done > body) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() unexpected SSL data\n");
    - e->event.failure = GG_FAILURE_TLS;
    - return GG_ACTION_FAIL;
    - }
    -
    - free(sess->recv_buf);
    - sess->recv_buf = NULL;
    - sess->recv_done = 0;
    -
    - sess->state = alt_state;
    - sess->check = GG_CHECK_WRITE;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    -
    - return GG_ACTION_WAIT;
    - }
    -
    - sess->state = next_state;
    - sess->check = GG_CHECK_READ;
    - sess->timeout = GG_DEFAULT_TIMEOUT; /* Pierwszy pakiet musi przyjść */
    -
    - /* Jeśli zbuforowaliśmy za dużo, przeanalizuj */
    -
    - if (sess->recv_buf + sess->recv_done > body) {
    - sess->recv_done = sess->recv_done - (body - sess->recv_buf);
    - memmove(sess->recv_buf, body, sess->recv_done);
    - sess->state = alt2_state;
    - return GG_ACTION_NEXT;
    - } else {
    - free(sess->recv_buf);
    - sess->recv_buf = NULL;
    - sess->recv_done = 0;
    - }
    -
    - return GG_ACTION_WAIT;
    -}
    -
    -static gg_action_t gg_handle_connected(struct gg_session *sess,
    - struct gg_event *e, enum gg_state_t next_state,
    - enum gg_state_t alt_state, enum gg_state_t alt2_state)
    -{
    -#if 0
    - char buf[1024];
    - int res;
    -
    - if (gg_send_queued_data(sess) == -1)
    - return GG_ACTION_FAIL;
    -
    - res = gg_read(sess, buf, sizeof(buf));
    -
    - if (res == -1 && (errno == EAGAIN || errno == EINTR)) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() "
    - "non-critical read error (errno=%d, %s)\n",
    - errno, strerror(errno));
    - return GG_ACTION_WAIT;
    - }
    -
    - if (res == -1 || res == 0) {
    - if (res == -1) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
    - " read error (errno=%d, %s)\n",
    - errno, strerror(errno));
    - } else {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
    - " connection closed\n");
    - }
    -
    - if (sess->state == GG_STATE_DISCONNECTING && res == 0) {
    - e->type = GG_EVENT_DISCONNECT_ACK;
    - } else if (sess->state == GG_STATE_READING_KEY) {
    - e->event.failure = GG_FAILURE_INVALID;
    - return GG_ACTION_FAIL;
    - }
    -
    - return GG_ACTION_FAIL;
    - }
    -
    - gg_debug_dump(sess, GG_DEBUG_DUMP, buf, res);
    -
    - if (gg_session_handle_data(sess, buf, res, e) == -1)
    - return GG_ACTION_FAIL;
    -
    - if (sess->send_buf != NULL)
    - sess->check |= GG_CHECK_WRITE;
    -
    - return GG_ACTION_WAIT;
    -#else
    - struct gg_header *gh;
    -
    - if (gg_send_queued_data(sess) == -1)
    - return GG_ACTION_FAIL;
    -
    - gh = gg_recv_packet(sess);
    -
    - if (gh == NULL) {
    - if (sess->state == GG_STATE_DISCONNECTING) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection broken expectedly\n");
    - e->type = GG_EVENT_DISCONNECT_ACK;
    - return GG_ACTION_WAIT;
    - }
    -
    - if (errno != EAGAIN) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd()"
    - " gg_recv_packet failed (errno=%d, %s)\n",
    - errno, strerror(errno));
    - return GG_ACTION_FAIL;
    - }
    - } else {
    - if (gg_session_handle_packet(sess, gh->type,
    - (const char *) gh + sizeof(struct gg_header),
    - gh->length, e) == -1)
    - {
    - free(gh);
    - return GG_ACTION_FAIL;
    - }
    -
    - free(gh);
    - }
    -
    - sess->check = GG_CHECK_READ;
    -
    - if (sess->send_buf != NULL)
    - sess->check |= GG_CHECK_WRITE;
    -
    - return GG_ACTION_WAIT;
    -#endif
    -}
    -
    -static gg_action_t gg_handle_error(struct gg_session *sess, struct gg_event *e,
    - enum gg_state_t next_state, enum gg_state_t alt_state,
    - enum gg_state_t alt2_state)
    -{
    - struct gg_session_private *p = sess->private_data;
    -
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR, "// gg_handle_error() failure=%d\n", p->socket_failure);
    -
    - e->event.failure = p->socket_failure;
    -
    - return GG_ACTION_FAIL;
    -}
    -
    -static const gg_state_transition_t handlers[] =
    -{
    - /* style:maxlinelength:start-ignore */
    - { GG_STATE_RESOLVE_HUB_SYNC, gg_handle_resolve_sync, GG_STATE_CONNECT_HUB, GG_STATE_SEND_HUB, 0 },
    - { GG_STATE_RESOLVE_GG_SYNC, gg_handle_resolve_sync, GG_STATE_CONNECT_GG, GG_STATE_READING_KEY, 0 },
    - { GG_STATE_RESOLVE_PROXY_HUB_SYNC, gg_handle_resolve_sync, GG_STATE_CONNECT_PROXY_HUB, GG_STATE_SEND_PROXY_HUB, 0 },
    - { GG_STATE_RESOLVE_PROXY_GG_SYNC, gg_handle_resolve_sync, GG_STATE_CONNECT_PROXY_GG, GG_STATE_SEND_PROXY_GG, 0 },
    -
    - { GG_STATE_RESOLVE_HUB_ASYNC, gg_handle_resolve_async, GG_STATE_RESOLVING_HUB, GG_STATE_SEND_HUB, 0 },
    - { GG_STATE_RESOLVE_GG_ASYNC, gg_handle_resolve_async, GG_STATE_RESOLVING_GG, GG_STATE_READING_KEY, 0 },
    - { GG_STATE_RESOLVE_PROXY_HUB_ASYNC, gg_handle_resolve_async, GG_STATE_RESOLVING_PROXY_HUB, GG_STATE_SEND_PROXY_HUB, 0 },
    - { GG_STATE_RESOLVE_PROXY_GG_ASYNC, gg_handle_resolve_async, GG_STATE_RESOLVING_PROXY_GG, GG_STATE_SEND_PROXY_GG, 0 },
    -
    - { GG_STATE_RESOLVING_HUB, gg_handle_resolving, GG_STATE_CONNECT_HUB, 0, 0 },
    - { GG_STATE_RESOLVING_GG, gg_handle_resolving, GG_STATE_CONNECT_GG, 0, 0 },
    - { GG_STATE_RESOLVING_PROXY_HUB, gg_handle_resolving, GG_STATE_CONNECT_PROXY_HUB, 0, 0 },
    - { GG_STATE_RESOLVING_PROXY_GG, gg_handle_resolving, GG_STATE_CONNECT_PROXY_GG, 0, 0 },
    -
    - { GG_STATE_CONNECT_HUB, gg_handle_connect, GG_STATE_CONNECTING_HUB, 0, 0 },
    - { GG_STATE_CONNECT_PROXY_HUB, gg_handle_connect, GG_STATE_CONNECTING_PROXY_HUB, 0, 0 },
    - { GG_STATE_CONNECT_PROXY_GG, gg_handle_connect, GG_STATE_CONNECTING_PROXY_GG, 0, 0 },
    -
    - { GG_STATE_CONNECT_GG, gg_handle_connect_gg, GG_STATE_CONNECTING_GG, 0, 0 },
    -
    - { GG_STATE_CONNECTING_HUB, gg_handle_connecting, GG_STATE_SEND_HUB, GG_STATE_CONNECT_HUB, 0 },
    - { GG_STATE_CONNECTING_PROXY_HUB, gg_handle_connecting, GG_STATE_SEND_PROXY_HUB, GG_STATE_CONNECT_PROXY_HUB, 0 },
    - { GG_STATE_CONNECTING_PROXY_GG, gg_handle_connecting, GG_STATE_SEND_PROXY_GG, GG_STATE_CONNECT_PROXY_GG, 0 },
    -
    - { GG_STATE_CONNECTING_GG, gg_handle_connecting_gg, GG_STATE_READING_KEY, GG_STATE_CONNECT_GG, GG_STATE_TLS_NEGOTIATION },
    -
    - { GG_STATE_SEND_HUB, gg_handle_send_hub, GG_STATE_READING_HUB, GG_STATE_SENDING_HUB, 0 },
    - { GG_STATE_SEND_PROXY_HUB, gg_handle_send_hub, GG_STATE_READING_PROXY_HUB, GG_STATE_SENDING_PROXY_HUB, 0 },
    -
    - { GG_STATE_SEND_PROXY_GG, gg_handle_send_proxy_gg, GG_STATE_READING_PROXY_GG, GG_STATE_SENDING_PROXY_GG, 0 },
    -
    - { GG_STATE_SENDING_HUB, gg_handle_sending_hub_proxy, GG_STATE_READING_HUB, 0, 0 },
    - { GG_STATE_SENDING_PROXY_HUB, gg_handle_sending_hub_proxy, GG_STATE_READING_PROXY_HUB, 0, 0 },
    - { GG_STATE_SENDING_PROXY_GG, gg_handle_sending_hub_proxy, GG_STATE_READING_PROXY_GG, 0, 0 },
    -
    - { GG_STATE_READING_HUB, gg_handle_reading_hub_proxy, GG_STATE_RESOLVE_GG_ASYNC, GG_STATE_RESOLVE_GG_SYNC, 0 },
    - { GG_STATE_READING_PROXY_HUB, gg_handle_reading_hub_proxy, GG_STATE_CONNECT_PROXY_GG, GG_STATE_CONNECT_PROXY_GG, 0 },
    -
    - { GG_STATE_READING_PROXY_GG, gg_handle_reading_proxy_gg, GG_STATE_READING_KEY, GG_STATE_TLS_NEGOTIATION, GG_STATE_READING_KEY },
    -
    - { GG_STATE_TLS_NEGOTIATION, gg_handle_tls_negotiation, GG_STATE_READING_KEY, 0, 0 },
    -
    - { GG_STATE_READING_KEY, gg_handle_connected, 0, 0, 0 },
    - { GG_STATE_READING_REPLY, gg_handle_connected, 0, 0, 0 },
    - { GG_STATE_CONNECTED, gg_handle_connected, 0, 0, 0 },
    - { GG_STATE_DISCONNECTING, gg_handle_connected, 0, 0, 0 },
    - { GG_STATE_ERROR, gg_handle_error, 0, 0, 0 },
    - /* style:maxlinelength:end-ignore */
    -};
    -
    -struct gg_event *gg_eventqueue_add(struct gg_session *sess)
    -{
    - struct gg_event *ge;
    - gg_eventqueue_t *queue_el, *it;
    -
    - queue_el = gg_new0(sizeof(gg_eventqueue_t));
    - ge = gg_new0(sizeof(struct gg_event));
    -
    - if (queue_el == NULL || ge == NULL) {
    - free(queue_el);
    - free(ge);
    - return NULL;
    - }
    -
    - ge->type = GG_EVENT_NONE;
    -
    - queue_el->event = ge;
    - if (sess->private_data->event_queue == NULL)
    - sess->private_data->event_queue = queue_el;
    - else {
    - it = sess->private_data->event_queue;
    - while (it->next != NULL)
    - it = it->next;
    - it->next = queue_el;
    - }
    -
    - return ge;
    -}
    -
    -/**
    - * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze sesji.
    - *
    - * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
    - * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
    - * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free().
    - *
    - * \param sess Struktura sesji
    - *
    - * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
    - *
    - * \ingroup events
    - */
    -struct gg_event *gg_watch_fd(struct gg_session *sess)
    -{
    - struct gg_event *ge;
    - struct gg_session_private *priv;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess);
    -
    - if (sess == NULL) {
    - errno = EFAULT;
    - return NULL;
    - }
    -
    - priv = sess->private_data;
    -
    - if (priv->event_queue != NULL) {
    - gg_eventqueue_t *next;
    -
    - ge = priv->event_queue->event;
    - next = priv->event_queue->next;
    - free(priv->event_queue);
    - priv->event_queue = next;
    -
    - if (next == NULL) {
    - sess->check = priv->check_after_queue;
    - sess->fd = priv->fd_after_queue;
    - }
    - return ge;
    - }
    -
    - ge = malloc(sizeof(struct gg_event));
    -
    - if (ge == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n");
    - return NULL;
    - }
    -
    - memset(ge, 0, sizeof(struct gg_event));
    -
    - ge->type = GG_EVENT_NONE;
    -
    - for (;;) {
    - unsigned int i, found = 0;
    - gg_action_t res;
    -
    - res = GG_ACTION_FAIL;
    -
    - for (i = 0; i < sizeof(handlers) / sizeof(handlers[0]); i++) {
    - if (handlers[i].state == (enum gg_state_t) sess->state) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_watch_fd() %s\n",
    - gg_debug_state(sess->state));
    - res = (*handlers[i].handler)(sess, ge,
    - handlers[i].next_state,
    - handlers[i].alt_state,
    - handlers[i].alt2_state);
    - found = 1;
    - break;
    - }
    - }
    -
    - if (!found) {
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "// gg_watch_fd() invalid state %s\n",
    - gg_debug_state(sess->state));
    - ge->event.failure = GG_FAILURE_INTERNAL;
    - }
    -
    - if (!sess->async && ge->type == GG_EVENT_NONE && res == GG_ACTION_WAIT)
    - res = GG_ACTION_NEXT;
    -
    - switch (res) {
    - case GG_ACTION_WAIT:
    - if (priv->event_queue != NULL) {
    - priv->fd_after_queue = sess->fd;
    - priv->check_after_queue = sess->check;
    - /* wymuszamy ponowne wywołanie gg_watch_fd */
    - sess->fd = gg_get_dummy_fd(sess);
    - if (sess->fd < 0)
    - sess->fd = priv->fd_after_queue;
    - sess->check = GG_CHECK_READ | GG_CHECK_WRITE;
    - }
    - return ge;
    -
    - case GG_ACTION_NEXT:
    - continue;
    -
    - case GG_ACTION_FAIL:
    - sess->state = GG_STATE_IDLE;
    -
    - gg_close(sess);
    -
    - if (ge->event.failure != 0) {
    - ge->type = GG_EVENT_CONN_FAILED;
    - } else {
    - free(ge);
    - ge = NULL;
    - }
    -
    - return ge;
    -
    - /* Celowo nie ma default */
    - }
    - }
    -}
    -
    -/*
    - * Local variables:
    - * c-indentation-style: k&r
    - * c-basic-offset: 8
    - * indent-tabs-mode: notnil
    - * End:
    - *
    - * vim: shiftwidth=8:
    - */
    --- a/libpurple/protocols/gg/lib/fileio.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,63 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2011 Bartosz Brachaczek <b.brachaczek@gmail.com>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file fileio.h
    - *
    - * \brief Makra zapewniające kompatybilność API do obsługi operacji na plikach na różnych systemach
    - */
    -
    -#ifndef LIBGADU_FILEIO_H
    -#define LIBGADU_FILEIO_H
    -
    -#include <sys/types.h>
    -#include <sys/stat.h>
    -#include <fcntl.h>
    -
    -#ifdef _WIN32
    -# include <io.h>
    -# define gg_file_close _close
    -# undef lseek
    -# define lseek _lseek
    -# undef open
    -# define open _open
    -# undef read
    -# define read _read
    -# undef stat
    -# define stat _stat
    -# undef fstat
    -# define fstat _fstat
    -# undef write
    -# define write _write
    -# define S_IRWXO 0
    -# define S_IRWXG 0
    -#else
    -# ifdef sun
    -# include <sys/filio.h>
    -# endif
    -# include <unistd.h>
    -# define gg_file_close close
    -#endif
    -
    -#ifndef S_IWUSR
    -# define S_IWUSR S_IWRITE
    -#endif
    -
    -#endif /* LIBGADU_FILEIO_H */
    --- a/libpurple/protocols/gg/lib/handlers.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,2881 +0,0 @@
    -/*
    - * (C) Copyright 2001-2011 Wojtek Kaniewski <wojtekka@irc.pl>
    - * Robert J. Woźny <speedy@ziew.org>
    - * Arkadiusz Miśkiewicz <arekm@pld-linux.org>
    - * Tomasz Chiliński <chilek@chilan.com>
    - * Adam Wysocki <gophi@ekg.chmurka.net>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file handlers.c
    - *
    - * \brief Funkcje obsługi przychodzących pakietów
    - */
    -
    -#include <ctype.h>
    -
    -#include "fileio.h"
    -#include "network.h"
    -#include "strman.h"
    -#include "libgadu.h"
    -#include "resolver.h"
    -#include "session.h"
    -#include "protocol.h"
    -#include "encoding.h"
    -#include "message.h"
    -#include "internal.h"
    -#include "deflate.h"
    -#include "tvbuff.h"
    -#include "protobuf.h"
    -#include "packets.pb-c.h"
    -
    -#include <errno.h>
    -#include <stdlib.h>
    -#include <string.h>
    -#include <time.h>
    -
    -/* Ograniczenie długości listy kontaktów
    - * z pakietów GG_USERLIST_REPLY do 10MB. */
    -#define GG_USERLIST_REPLY_MAX_LENGTH 10485760
    -
    -/**
    - * \internal Struktura opisująca funkcję obsługi pakietu.
    - */
    -typedef struct {
    - /* Typ pakietu */
    - uint32_t type;
    - /* Stan w którym pakiet jest obsługiwany */
    - enum gg_state_t state;
    - /* Minimalny rozmiar danych pakietu */
    - size_t min_length;
    - /* Funkcja obsługująca pakiet. Patrz gg_session_handle_packet(). */
    - int (*handler)(struct gg_session *, uint32_t, const char *, size_t, struct gg_event *);
    -} gg_packet_handler_t;
    -
    -static int gg_ack_110(struct gg_session *gs, GG110Ack__Type type, uint32_t seq, struct gg_event *ge)
    -{
    - GG110Ack msg = GG110_ACK__INIT;
    -
    - msg.type = type;
    - msg.seq = seq;
    -
    - if (!GG_PROTOBUF_SEND(gs, ge, GG_ACK110, gg110_ack, msg))
    - return -1;
    - return 0;
    -}
    -
    -static void gg_sync_time(struct gg_session *gs, time_t server_time)
    -{
    - time_t local_time = time(NULL);
    - int time_diff = server_time - local_time;
    -
    - if (gs->private_data->time_diff == time_diff)
    - return;
    -
    - gs->private_data->time_diff = time_diff;
    - gg_debug_session(gs, GG_DEBUG_MISC | GG_DEBUG_VERBOSE,
    - "// time synchronized (diff = %d)\n", time_diff);
    -}
    -
    -static int gg_session_handle_welcome_110(struct gg_session *gs, uint32_t seed,
    - struct gg_event *ge)
    -{
    - GG105Login msg = GG105_LOGIN__INIT;
    - char client_str[1000];
    - uint8_t hash[64];
    - const char *client_name = GG11_VERSION;
    - const char *client_version = GG_DEFAULT_CLIENT_VERSION_110;
    - const char *client_target = GG11_TARGET;
    - uint8_t dummy4[4] = {0, 0, 0, 0};
    -
    - if (gs->hash_type != GG_LOGIN_HASH_SHA1) {
    - gg_debug_session(gs, GG_DEBUG_ERROR, "// Unsupported hash type "
    - "for this protocol version\n");
    - gg_connection_failure(gs, ge, GG_FAILURE_INTERNAL);
    - return -1;
    - }
    -
    - if (gg_login_hash_sha1_2(gs->password, seed, hash) == -1) {
    - gg_debug_session(gs, GG_DEBUG_ERROR, "// gg_watch_fd() "
    - "gg_login_hash_sha1_2() failed, "
    - "probably out of memory\n");
    - gg_connection_failure(gs, ge, GG_FAILURE_INTERNAL);
    - return -1;
    - }
    -
    - if (gs->client_version != NULL && !isdigit(gs->client_version[0])) {
    - client_name = "";
    - client_target = "";
    - }
    - if (gs->client_version != NULL)
    - client_version = gs->client_version;
    - snprintf(client_str, sizeof(client_str), "%s%s%s",
    - client_name, client_version, client_target);
    - client_str[sizeof(client_str) - 1] = '\0';
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() "
    - "sending GG_LOGIN105 packet\n");
    -
    - msg.lang = GG8_LANG;
    - gg_protobuf_set_uin(&msg.uin, gs->uin, NULL);
    - msg.hash.len = 20;
    - msg.hash.data = hash;
    - msg.client = client_str;
    -
    - /* flagi gg8 są różne od tych dla gg11 */
    - msg.initial_status = gs->initial_status ?
    - (gs->initial_status & 0xFF) : GG_STATUS_AVAIL;
    -
    - if (gs->initial_descr != NULL) {
    - msg.initial_descr = gs->initial_descr;
    - }
    -
    - /* GG11.0
    - msg.supported_features = "avatar,StatusComments,gg_account_sdp,"
    - "edisc,bot,fanpage,pubdir,botCaps"; */
    - /* GG11.2 */
    - msg.supported_features = "avatar,StatusComments,ggaccount,edisc,"
    - "music_shared,bot,fanpage,pubdir,botCaps,gifts,Gift";
    -
    - msg.dummy4.len = sizeof(dummy4);
    - msg.dummy4.data = dummy4;
    -
    - msg.has_dummy7 = 1;
    - msg.has_dummy8 = 1;
    - msg.has_dummy10 = 1;
    -
    - if (!GG_PROTOBUF_SEND(gs, ge, GG_LOGIN105, gg105_login, msg))
    - return -1;
    -
    - gs->state = GG_STATE_READING_REPLY;
    - gs->check = GG_CHECK_READ;
    - return 0;
    -}
    -
    -static int gg_session_handle_login110_ok(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - GG110LoginOK *msg = gg110_login_ok__unpack(NULL, len, (uint8_t*)ptr);
    -
    - if (!GG_PROTOBUF_VALID(gs, "GG110LoginOK", msg))
    - return -1;
    -
    - gg_protobuf_expected(gs, "GG110LoginOK.dummy1", msg->dummy1, 1);
    - gg_sync_time(gs, msg->server_time);
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// login110_ok: "
    - "uin=%u, dummyhash=%s\n", msg->uin, msg->dummyhash);
    -
    - gg110_login_ok__free_unpacked(msg, NULL);
    -
    - ge->type = GG_EVENT_CONN_SUCCESS;
    - gs->state = GG_STATE_CONNECTED;
    - gs->check = GG_CHECK_READ;
    - gs->timeout = -1;
    - gs->status = (gs->initial_status) ? gs->initial_status : GG_STATUS_AVAIL;
    -#if 0
    - free(gs->status_descr);
    - gs->status_descr = gs->initial_descr;
    -#else
    - free(gs->initial_descr);
    -#endif
    - gs->initial_descr = NULL;
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_WELCOME.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_welcome(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - const struct gg_welcome *w;
    - int ret;
    - uint8_t hash_buf[64];
    - uint32_t local_ip;
    - struct sockaddr_in sin;
    - socklen_t sin_len = sizeof(sin);
    - uint32_t seed;
    -
    - struct gg_login80 l80;
    - const char *client_name, *version, *descr;
    - uint32_t client_name_len, version_len, descr_len;
    -
    - if (len < sizeof(struct gg_welcome)) {
    - ge->type = GG_EVENT_CONN_FAILED;
    - ge->event.failure = GG_FAILURE_INVALID;
    - gs->state = GG_STATE_IDLE;
    - gg_close(gs);
    - return 0;
    - }
    -
    - w = (const struct gg_welcome*) ptr;
    - seed = gg_fix32(w->key);
    -
    - if (gs->protocol_version >= GG_PROTOCOL_VERSION_110)
    - return gg_session_handle_welcome_110(gs, seed, ge);
    -
    - memset(hash_buf, 0, sizeof(hash_buf));
    -
    - switch (gs->hash_type) {
    - case GG_LOGIN_HASH_GG32:
    - {
    - uint32_t hash;
    -
    - hash = gg_fix32(gg_login_hash((unsigned char*) gs->password, seed));
    - gg_debug_session(gs, GG_DEBUG_DUMP, "// gg_watch_fd() "
    - "challenge %.4x --> GG32 hash %.8x\n",
    - seed, hash);
    - memcpy(hash_buf, &hash, sizeof(hash));
    -
    - break;
    - }
    -
    - case GG_LOGIN_HASH_SHA1:
    - {
    -#ifndef GG_DEBUG_DISABLE
    - char tmp[41];
    - int i;
    -#endif
    -
    - if (gg_login_hash_sha1_2(gs->password, seed, hash_buf) == -1) {
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_watch_fd() gg_login_hash_sha1_2()"
    - " failed, probably out of memory\n");
    - gg_close(gs);
    - ge->type = GG_EVENT_CONN_FAILED;
    - ge->event.failure = GG_FAILURE_INTERNAL;
    - gs->state = GG_STATE_IDLE;
    - return -1;
    - }
    -
    -#ifndef GG_DEBUG_DISABLE
    - for (i = 0; i < 40; i += 2)
    - snprintf(tmp + i, sizeof(tmp) - i, "%02x", hash_buf[i / 2]);
    -
    - gg_debug_session(gs, GG_DEBUG_DUMP, "// gg_watch_fd() "
    - "challenge %.4x --> SHA1 hash: %s\n",
    - seed, tmp);
    -#endif
    -
    - break;
    - }
    -
    - default:
    - break;
    - }
    -
    -#if 0
    - if (gs->password != NULL && (gs->flags & (1 << GG_SESSION_FLAG_CLEAR_PASSWORD))) {
    - memset(gs->password, 0, strlen(gs->password));
    - free(gs->password);
    - gs->password = NULL;
    - }
    -#endif
    -
    - if (!getsockname(gs->fd, (struct sockaddr*) &sin, &sin_len)) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() "
    - "detected address to %s\n", inet_ntoa(sin.sin_addr));
    - local_ip = sin.sin_addr.s_addr;
    - } else {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n");
    - local_ip = 0;
    - }
    -
    - if (gs->external_addr == 0)
    - gs->external_addr = local_ip;
    -
    - memset(&l80, 0, sizeof(l80));
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() sending GG_LOGIN80 packet\n");
    - l80.uin = gg_fix32(gs->uin);
    - memcpy(l80.language, GG8_LANG, sizeof(l80.language));
    - l80.hash_type = gs->hash_type;
    - memcpy(l80.hash, hash_buf, sizeof(l80.hash));
    - l80.status = gg_fix32(gs->initial_status ? gs->initial_status : GG_STATUS_AVAIL);
    - l80.flags = gg_fix32(gs->status_flags);
    - l80.features = gg_fix32(gs->protocol_features);
    - l80.image_size = gs->image_size;
    - l80.dunno2 = 0x64;
    -
    - if (gs->client_version != NULL && !isdigit(gs->client_version[0])) {
    - client_name = "";
    - client_name_len = 0;
    - } else {
    - client_name = GG8_VERSION;
    - client_name_len = strlen(GG8_VERSION);
    - }
    -
    - version = (gs->client_version != NULL) ? gs->client_version : GG_DEFAULT_CLIENT_VERSION_100;
    - version_len = gg_fix32(client_name_len + strlen(version));
    -
    - descr = (gs->initial_descr != NULL) ? gs->initial_descr : "";
    - descr_len = (gs->initial_descr != NULL) ? gg_fix32(strlen(gs->initial_descr)) : 0;
    -
    - ret = gg_send_packet(gs,
    - GG_LOGIN80,
    - &l80, sizeof(l80),
    - &version_len, sizeof(version_len),
    - client_name, client_name_len,
    - version, strlen(version),
    - &descr_len, sizeof(descr_len),
    - descr, strlen(descr),
    - NULL);
    -
    - if (ret == -1) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() "
    - "sending packet failed. (errno=%d, %s)\n",
    - errno, strerror(errno));
    - gg_close(gs);
    - ge->type = GG_EVENT_CONN_FAILED;
    - ge->event.failure = GG_FAILURE_WRITING;
    - gs->state = GG_STATE_IDLE;
    - return -1;
    - }
    -
    - gs->state = GG_STATE_READING_REPLY;
    - gs->check = GG_CHECK_READ;
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_LOGIN_OK.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_login_ok(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n");
    - ge->type = GG_EVENT_CONN_SUCCESS;
    - gs->state = GG_STATE_CONNECTED;
    - gs->check = GG_CHECK_READ;
    - gs->timeout = -1;
    - gs->status = (gs->initial_status) ? gs->initial_status : GG_STATUS_AVAIL;
    -#if 0
    - free(gs->status_descr);
    - gs->status_descr = gs->initial_descr;
    -#else
    - free(gs->initial_descr);
    -#endif
    - gs->initial_descr = NULL;
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_LOGIN_FAILED.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_login_failed(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - if (type != GG_DISCONNECTING)
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() login failed\n");
    - else
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n");
    - ge->type = GG_EVENT_CONN_FAILED;
    - ge->event.failure = (type != GG_DISCONNECTING) ? GG_FAILURE_PASSWORD : GG_FAILURE_INTRUDER;
    - gs->state = GG_STATE_IDLE;
    - gg_close(gs);
    - errno = EACCES;
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_SEND_MSG_ACK.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_send_msg_ack(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - struct gg_session_private *p = gs->private_data;
    - const struct gg_send_msg_ack *s = (const struct gg_send_msg_ack*) ptr;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n");
    -
    - ge->type = GG_EVENT_ACK;
    - ge->event.ack.status = gg_fix32(s->status);
    - ge->event.ack.recipient = gg_fix32(s->recipient);
    - ge->event.ack.seq = gg_fix32(s->seq);
    -
    - if (ge->event.ack.seq == 0 && p->imgout_waiting_ack > 0)
    - p->imgout_waiting_ack--;
    - gg_image_sendout(gs);
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_SEND_MSG_ACK110.
    - */
    -static int gg_session_handle_send_msg_ack_110(struct gg_session *gs,
    - uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
    -{
    - struct gg_session_private *p = gs->private_data;
    - GG110MessageAck *msg = gg110_message_ack__unpack(NULL, len, (uint8_t*)ptr);
    - size_t i;
    -
    - if (!GG_PROTOBUF_VALID(gs, "GG110MessageAck", msg))
    - return -1;
    -
    - if (msg->dummy1 == 0x4000) {
    - /* zaobserwowane w EKG rev2856, po wywołaniu check_conn, czyli
    - * gg_image_request(sess, uin, 0, time(NULL));
    - */
    - gg_debug_session(gs, GG_DEBUG_MISC | GG_DEBUG_WARNING,
    - "// gg_session_handle_send_msg_ack_110() magic dummy1 "
    - "value 0x4000\n");
    - } else if (msg->dummy1 != 0) {
    - gg_debug_session(gs, GG_DEBUG_MISC | GG_DEBUG_WARNING,
    - "// gg_session_handle_send_msg_ack_110() unknown dummy1 "
    - "value: %x\n", msg->dummy1);
    - }
    -
    - gg_debug_session(gs, GG_DEBUG_VERBOSE,
    - "// gg_session_handle_send_msg_ack_110() "
    - "%s=%016" PRIx64 " %s=%016" PRIx64 "\n",
    - msg->has_msg_id ? "msg_id" : "0", msg->msg_id,
    - msg->has_conv_id ? "conv_id" : "0", msg->conv_id);
    -
    - for (i = 0; i < msg->n_links; i++) {
    - GG110MessageAckLink *link = msg->links[i];
    - if (!GG_PROTOBUF_VALID(gs, "GG110MessageAckLink", link))
    - continue;
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_session_handle_send_msg_ack_110() "
    - "got link (id=%" PRIx64 ") \"%s\"\n", link->id, link->url);
    - }
    -
    - ge->type = GG_EVENT_ACK110;
    - ge->event.ack110.msg_type = msg->msg_type;
    - ge->event.ack110.seq = msg->seq;
    - ge->event.ack110.time = msg->time;
    -
    - gg_compat_message_ack(gs, msg->seq);
    -
    - gg110_message_ack__free_unpacked(msg, NULL);
    -
    - if (msg->seq == 0 && p->imgout_waiting_ack > 0)
    - p->imgout_waiting_ack--;
    - gg_image_sendout(gs);
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_PONG.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_pong(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n");
    -
    - ge->type = GG_EVENT_PONG;
    -
    - gs->last_pong = time(NULL);
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_DISCONNECTING.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_disconnecting(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n");
    -
    - ge->type = GG_EVENT_DISCONNECT;
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_DISCONNECT_ACK.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_disconnect_ack(struct gg_session *gs,
    - uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
    -{
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received logoff acknowledge\n");
    -
    - ge->type = GG_EVENT_DISCONNECT_ACK;
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiety GG_XML_EVENT i GG_XML_ACTION.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_xml_event(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received XML event\n");
    -
    - ge->type = GG_EVENT_XML_EVENT;
    - ge->event.xml_event.data = malloc(len + 1);
    -
    - if (ge->event.xml_event.data == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
    - return -1;
    - }
    -
    - memcpy(ge->event.xml_event.data, ptr, len);
    - ge->event.xml_event.data[len] = 0;
    -
    - return 0;
    -}
    -
    -static int gg_session_handle_event_110(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - GG110Event *msg = gg110_event__unpack(NULL, len, (uint8_t*)ptr);
    - int succ = 1;
    -
    - if (!GG_PROTOBUF_VALID(gs, "GG110Event", msg))
    - return -1;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_event_110: "
    - "received GG11 event (type=%d, id=%" PRIx64 ")\n", msg->type, msg->id);
    -
    - if (msg->type == GG110_EVENT__TYPE__XML) {
    - ge->type = GG_EVENT_XML_EVENT;
    - ge->event.xml_event.data = strdup(msg->data);
    - succ = succ && (ge->event.xml_event.data != NULL);
    - } else if (msg->type == GG110_EVENT__TYPE__JSON) {
    - ge->type = GG_EVENT_JSON_EVENT;
    - ge->event.json_event.data = strdup(msg->data);
    - succ = succ && (ge->event.json_event.data != NULL);
    - ge->event.json_event.type = strdup(msg->subtype);
    - succ = succ && (ge->event.json_event.type != NULL);
    - } else {
    - gg_debug_session(gs, GG_DEBUG_WARNING,
    - "// gg_session_handle_event_110: "
    - "unsupported GG11 event type: %d\n", msg->type);
    - succ = 0;
    - }
    -
    - if (gg_ack_110(gs, GG110_ACK__TYPE__MPA, msg->seq, ge) != 0) {
    - succ = 0;
    - }
    -
    - gg110_event__free_unpacked(msg, NULL);
    -
    - return succ ? 0 : -1;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_PUBDIR50_REPLY.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_pubdir50_reply(struct gg_session *gs,
    - uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
    -{
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n");
    -
    - return gg_pubdir50_handle_reply_sess(gs, ge, ptr, len);
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_USERLIST_REPLY.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_userlist_reply(struct gg_session *gs,
    - uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
    -{
    - char reply_type;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n");
    -
    - reply_type = ptr[0];
    -
    - /* jeśli odpowiedź na eksport, wywołaj zdarzenie tylko
    - * gdy otrzymano wszystkie odpowiedzi */
    - if (reply_type == GG_USERLIST_PUT_REPLY || reply_type == GG_USERLIST_PUT_MORE_REPLY) {
    - if (--gs->userlist_blocks)
    - return 0;
    -
    - reply_type = GG_USERLIST_PUT_REPLY;
    - }
    -
    - if (len > 1) {
    - unsigned int reply_len = (gs->userlist_reply != NULL) ? strlen(gs->userlist_reply) : 0;
    - char *tmp;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "userlist_reply=%p, len=%"
    - GG_SIZE_FMT "\n", gs->userlist_reply, len);
    -
    - if (reply_len + len > GG_USERLIST_REPLY_MAX_LENGTH) {
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_session_handle_userlist_reply() "
    - "too many userlist replies\n");
    - return -1;
    - }
    -
    - tmp = realloc(gs->userlist_reply, reply_len + len);
    -
    - if (tmp == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
    - return -1;
    - }
    -
    - gs->userlist_reply = tmp;
    - memcpy(gs->userlist_reply + reply_len, ptr + 1, len - 1);
    - gs->userlist_reply[reply_len + len - 1] = 0;
    - }
    -
    - if (reply_type == GG_USERLIST_GET_MORE_REPLY)
    - return 0;
    -
    - ge->type = GG_EVENT_USERLIST;
    - ge->event.userlist.type = reply_type;
    - ge->event.userlist.reply = gs->userlist_reply;
    -
    - gs->userlist_reply = NULL;
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_DCC7_ID_REPLY.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_dcc7_id_reply(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 id packet\n");
    -
    - return gg_dcc7_handle_id(gs, ge, ptr, len);
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_DCC7_ACCEPT.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_dcc7_accept(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 accept\n");
    -
    - return gg_dcc7_handle_accept(gs, ge, ptr, len);
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_DCC7_NEW.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_dcc7_new(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 request\n");
    -
    - return gg_dcc7_handle_new(gs, ge, ptr, len);
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_DCC7_REJECT.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_dcc7_reject(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 reject\n");
    -
    - return gg_dcc7_handle_reject(gs, ge, ptr, len);
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_DCC7_INFO.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_dcc7_info(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 info\n");
    -
    - return gg_dcc7_handle_info(gs, ge, ptr, len);
    -}
    -
    -/**
    - * \internal Analizuje przychodzący pakiet z obrazkiem.
    - *
    - * \param e Struktura zdarzenia
    - * \param p Bufor z danymi
    - * \param len Długość bufora
    - * \param sess Struktura sesji
    - * \param sender Numer nadawcy
    - * \param type Typ pakietu (NIE typ GG_MSG_OPTION_IMAGE_*)
    - */
    -static void gg_image_queue_parse(struct gg_event *e, const char *p,
    - unsigned int len, struct gg_session *sess, uin_t sender,
    - uint32_t type)
    -{
    - const struct gg_msg_image_reply *i = (const void*) p;
    - struct gg_image_queue *q, *qq;
    -
    - gg_debug_session(sess, GG_DEBUG_VERBOSE,
    - "// gg_image_queue_parse(%p, %p, %d, %p, %u, %d)\n",
    - e, p, len, sess, sender, type);
    -
    - if (!p || !sess || !e) {
    - errno = EFAULT;
    - return;
    - }
    -
    - if (i->flag == GG_MSG_OPTION_IMAGE_REQUEST) {
    - e->type = GG_EVENT_IMAGE_REQUEST;
    - e->event.image_request.sender = sender;
    - e->event.image_reply.size = i->size;
    - e->event.image_request.crc32 = i->crc32;
    - return;
    - }
    -
    - /* znajdź dany obrazek w kolejce danej sesji */
    -
    - for (qq = sess->images, q = NULL; qq; qq = qq->next) {
    - if (sender == qq->sender && i->size == qq->size && i->crc32 == qq->crc32) {
    - q = qq;
    - break;
    - }
    - }
    -
    - if (!q) {
    - gg_debug_session(sess, GG_DEBUG_WARNING,
    - "// gg_image_queue_parse() unknown image from %d, "
    - "size=%d, crc32=%.8x\n", sender, i->size, i->crc32);
    - return;
    - }
    -
    - if (q->packet_type == 0)
    - q->packet_type = type;
    - if (q->packet_type != type)
    - return;
    -
    - if (i->flag == GG_MSG_OPTION_IMAGE_REPLY) {
    - q->done = 0;
    -
    - len -= sizeof(struct gg_msg_image_reply);
    - p += sizeof(struct gg_msg_image_reply);
    -
    - if (memchr(p, 0, len) == NULL) {
    - gg_debug_session(sess, GG_DEBUG_ERROR,
    - "// gg_image_queue_parse() malformed packet "
    - "from %d, unlimited filename\n", sender);
    - return;
    - }
    -
    - if (!(q->filename = strdup(p))) {
    - gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_image_queue_parse() out of memory\n");
    - return;
    - }
    -
    - len -= strlen(p) + 1;
    - p += strlen(p) + 1;
    - } else if (i->flag == GG_MSG_OPTION_IMAGE_REPLY_MORE) {
    - len -= sizeof(struct gg_msg_image_reply);
    - p += sizeof(struct gg_msg_image_reply);
    - } else {
    - gg_debug_session(sess, GG_DEBUG_WARNING, "// gg_image_queue_parse() unexpected flag\n");
    - return;
    - }
    -
    - if (q->done + len > q->size) {
    - gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_image_queue_parse() got too much\n");
    - len = q->size - q->done;
    - }
    -
    - memcpy(q->image + q->done, p, len);
    - q->done += len;
    -
    - gg_debug_session(sess, GG_DEBUG_VERBOSE,
    - "// gg_image_queue_parse() got image part (done: %d of %d)\n",
    - q->done, q->size);
    -
    - /* jeśli skończono odbierać obrazek, wygeneruj zdarzenie */
    -
    - if (q->done >= q->size) {
    - gg_debug_session(sess, GG_DEBUG_VERBOSE,
    - "// gg_image_queue_parse() image ready\n");
    -
    - e->type = GG_EVENT_IMAGE_REPLY;
    - e->event.image_reply.sender = sender;
    - e->event.image_reply.size = q->size;
    - e->event.image_reply.crc32 = q->crc32;
    - e->event.image_reply.filename = q->filename;
    - e->event.image_reply.image = q->image;
    -
    - gg_image_queue_remove(sess, q, 0);
    -
    - free(q);
    - }
    -}
    -
    -/**
    - * \internal Analizuje informacje rozszerzone wiadomości.
    - *
    - * \param sess Struktura sesji.
    - * \param e Struktura zdarzenia.
    - * \param sender Numer nadawcy.
    - * \param p Wskaźnik na dane rozszerzone.
    - * \param packet_end Wskaźnik na koniec pakietu.
    - * \param packet_type Typ pakietu, w którym otrzymaliśmy wiadomość.
    - *
    - * \return 0 jeśli się powiodło, -1 jeśli wiadomość obsłużono i wynik ma
    - * zostać przekazany aplikacji, -2 jeśli wystąpił błąd ogólny, -3 jeśli
    - * wiadomość jest niepoprawna.
    - */
    -static int gg_handle_recv_msg_options(struct gg_session *sess,
    - struct gg_event *e, uin_t sender, const char *p, const char *packet_end,
    - uint32_t packet_type)
    -{
    - while (p < packet_end) {
    - switch (*p) {
    - case GG_MSG_OPTION_CONFERENCE:
    - {
    - const struct gg_msg_recipients *m = (const void*) p;
    - uint32_t i, count;
    -
    - p += sizeof(*m);
    -
    - if (p > packet_end) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg_options()"
    - " packet out of bounds (1)\n");
    - goto malformed;
    - }
    -
    - count = gg_fix32(m->count);
    -
    - if (p + count * sizeof(uin_t) > packet_end ||
    - p + count * sizeof(uin_t) < p ||
    - count > 0xffff)
    - {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg_options()"
    - " packet out of bounds (1.5)\n");
    - goto malformed;
    - }
    -
    - if (e->event.msg.recipients != NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg_options()"
    - " e->event.msg.recipients already exist\n");
    - goto malformed;
    - }
    -
    - e->event.msg.recipients = malloc(count * sizeof(uin_t));
    -
    - if (e->event.msg.recipients == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg_options()"
    - " not enough memory for recipients data\n");
    - goto fail;
    - }
    -
    - memcpy(e->event.msg.recipients, p, count * sizeof(uin_t));
    - p += count * sizeof(uin_t);
    -
    - for (i = 0; i < count; i++)
    - e->event.msg.recipients[i] = gg_fix32(e->event.msg.recipients[i]);
    -
    - e->event.msg.recipients_count = count;
    -
    - break;
    - }
    -
    - case GG_MSG_OPTION_ATTRIBUTES:
    - {
    - uint16_t len;
    - char *buf;
    -
    - if (p + 3 > packet_end) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg_options()"
    - " packet out of bounds (2)\n");
    - goto malformed;
    - }
    -
    - memcpy(&len, p + 1, sizeof(uint16_t));
    - len = gg_fix16(len);
    -
    - if (e->event.msg.formats != NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg_options()"
    - " e->event.msg.formats already exist\n");
    - goto malformed;
    - }
    -
    - buf = malloc(len);
    -
    - if (buf == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg_options()"
    - " not enough memory for richtext data\n");
    - goto fail;
    - }
    -
    - p += 3;
    -
    - if (p + len > packet_end) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg_options()"
    - " packet out of bounds (3)\n");
    - free(buf);
    - goto malformed;
    - }
    -
    - memcpy(buf, p, len);
    -
    - e->event.msg.formats = buf;
    - e->event.msg.formats_length = len;
    -
    - p += len;
    -
    - break;
    - }
    -
    - case GG_MSG_OPTION_IMAGE_REQUEST:
    - {
    - const struct gg_msg_image_request *i = (const void*) p;
    -
    - if (p + sizeof(*i) > packet_end) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg() "
    - "packet out of bounds (3)\n");
    - goto malformed;
    - }
    -
    - if (e->event.msg.formats != NULL || e->event.msg.recipients != NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg_options()"
    - " mixed options (1)\n");
    - goto malformed;
    - }
    -
    - e->event.image_request.sender = sender;
    - e->event.image_request.size = gg_fix32(i->size);
    - e->event.image_request.crc32 = gg_fix32(i->crc32);
    -
    - e->type = GG_EVENT_IMAGE_REQUEST;
    -
    - goto handled;
    - }
    -
    - case GG_MSG_OPTION_IMAGE_REPLY:
    - case GG_MSG_OPTION_IMAGE_REPLY_MORE:
    - {
    - struct gg_msg_image_reply *rep = (void*) p;
    -
    - if (e->event.msg.formats != NULL || e->event.msg.recipients != NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg_options() "
    - "mixed options (2)\n");
    - goto malformed;
    - }
    -
    - if (p + sizeof(struct gg_msg_image_reply) == packet_end) {
    -
    - /* pusta odpowiedź - klient po drugiej stronie nie ma żądanego obrazka */
    -
    - e->type = GG_EVENT_IMAGE_REPLY;
    - e->event.image_reply.sender = sender;
    - e->event.image_reply.size = 0;
    - e->event.image_reply.crc32 = gg_fix32(rep->crc32);
    - e->event.image_reply.filename = NULL;
    - e->event.image_reply.image = NULL;
    - goto handled;
    -
    - } else if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) {
    -
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg() "
    - "packet out of bounds (4)\n");
    - goto malformed;
    - }
    -
    - rep->size = gg_fix32(rep->size);
    - rep->crc32 = gg_fix32(rep->crc32);
    - gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, sender, packet_type);
    -
    - goto handled;
    - }
    -
    - default:
    - {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg() "
    - "unknown payload 0x%.2x\n", *p);
    - p = packet_end;
    - }
    - }
    - }
    -
    - return 0;
    -
    -handled:
    - return -1;
    -
    -fail:
    - return -2;
    -
    -malformed:
    - return -3;
    -}
    -
    -/**
    - * \internal Wysyła potwierdzenie odebrania wiadomości.
    - *
    - * \param gs Struktura sesji
    - * \param seq Numer sekwencyjny odebranej wiadomości
    - *
    - * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
    - */
    -static int gg_session_send_msg_ack(struct gg_session *gs, uint32_t seq)
    -{
    - struct gg_recv_msg_ack pkt;
    -
    - gg_debug_session(gs, GG_DEBUG_FUNCTION, "** gg_session_send_msg_ack(%p);\n", gs);
    -
    - if ((gs->protocol_features & GG_FEATURE_MSG_ACK) == 0)
    - return 0;
    -
    - /* Kiedyś zdawało nam się, że mamy wysyłać liczbę odebranych
    - * wiadomości, ale okazało się, że numer sekwencyjny. */
    - gs->recv_msg_count++;
    -
    - pkt.seq = gg_fix32(seq);
    -
    - return gg_send_packet(gs, GG_RECV_MSG_ACK, &pkt, sizeof(pkt), NULL);
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_RECV_MSG.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_recv_msg(struct gg_session *sess, uint32_t type,
    - const char *packet, size_t length, struct gg_event *e)
    -{
    - const struct gg_recv_msg *r = (const struct gg_recv_msg*) packet;
    - const char *payload = packet + sizeof(struct gg_recv_msg);
    - const char *payload_end = packet + length;
    - size_t len;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %"
    - GG_SIZE_FMT ", %p);\n", packet, length, e);
    -
    - if (sess == NULL)
    - goto fail;
    -
    - if ((r->seq == 0) && (r->msgclass == 0)) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n");
    - goto malformed;
    - }
    -
    - /* jednobajtowa wiadomość o treści \x02 to żądanie połączenia DCC */
    - if (*payload == GG_MSG_CALLBACK && payload == payload_end - 1) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n");
    - length = 1;
    - } else {
    - const char *options;
    -
    - options = memchr(payload, 0, (size_t) (payload_end - payload));
    -
    - if (options == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg() malformed packet, "
    - "message out of bounds (0)\n");
    - goto malformed;
    - }
    -
    - length = (size_t) (options - payload);
    -
    - switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), options + 1, payload_end, type)) {
    - case -1: /* handled */
    - gg_session_send_msg_ack(sess, gg_fix32(r->seq));
    - return 0;
    -
    - case -2: /* failed */
    - goto fail;
    -
    - case -3: /* malformed */
    - goto malformed;
    - }
    - }
    -
    - e->type = GG_EVENT_MSG;
    - e->event.msg.msgclass = gg_fix32(r->msgclass);
    - e->event.msg.sender = gg_fix32(r->sender);
    - e->event.msg.time = gg_fix32(r->time);
    - e->event.msg.seq = gg_fix32(r->seq);
    -
    - e->event.msg.message = (unsigned char*)gg_encoding_convert(payload,
    - GG_ENCODING_CP1250, sess->encoding, length, -1);
    - if (e->event.msg.message == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_session_handle_recv_msg() out of memory\n");
    - goto fail;
    - }
    -
    - len = gg_message_text_to_html(NULL, (char*)e->event.msg.message,
    - sess->encoding, e->event.msg.formats,
    - e->event.msg.formats_length);
    - e->event.msg.xhtml_message = malloc(len + 1);
    -
    - if (e->event.msg.xhtml_message == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_session_handle_recv_msg() out of memory\n");
    - goto fail;
    - }
    -
    - gg_message_text_to_html(e->event.msg.xhtml_message,
    - (char*)e->event.msg.message, sess->encoding,
    - e->event.msg.formats, e->event.msg.formats_length);
    -
    - gg_session_send_msg_ack(sess, gg_fix32(r->seq));
    - return 0;
    -
    -fail:
    - free(e->event.msg.message);
    - free(e->event.msg.xhtml_message);
    - free(e->event.msg.recipients);
    - free(e->event.msg.formats);
    - return -1;
    -
    -malformed:
    - e->type = GG_EVENT_NONE;
    - free(e->event.msg.message);
    - free(e->event.msg.xhtml_message);
    - free(e->event.msg.recipients);
    - free(e->event.msg.formats);
    - gg_session_send_msg_ack(sess, gg_fix32(r->seq));
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_RECV_MSG80.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_recv_msg_80(struct gg_session *sess, uint32_t type,
    - const char *packet, size_t length, struct gg_event *e)
    -{
    - const struct gg_recv_msg80 *r = (const struct gg_recv_msg80*) packet;
    - uint32_t offset_plain;
    - uint32_t offset_attr;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION,
    - "** gg_handle_recv_msg80(%p, %" GG_SIZE_FMT ", %p);\n",
    - packet, length, e);
    -
    - if (sess == NULL)
    - goto fail;
    -
    - if (r->seq == 0 && r->msgclass == 0) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() oops, silently ignoring the bait\n");
    - goto malformed;
    - }
    -
    - offset_plain = gg_fix32(r->offset_plain);
    - offset_attr = gg_fix32(r->offset_attr);
    -
    - if (offset_plain < sizeof(struct gg_recv_msg80) || offset_plain >= length) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg80() malformed packet, "
    - "message out of bounds (0)\n");
    - goto malformed;
    - }
    -
    - if (offset_attr < sizeof(struct gg_recv_msg80) || offset_attr > length) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg80() malformed packet, "
    - "attr out of bounds (1)\n");
    - offset_attr = 0; /* nie parsuj attr. */
    - }
    -
    - /* Normalna sytuacja, więc nie podpada pod powyższy warunek. */
    - if (offset_attr == length)
    - offset_attr = 0;
    -
    - if (memchr(packet + offset_plain, 0, length - offset_plain) == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg80() malformed packet, "
    - "message out of bounds (2)\n");
    - goto malformed;
    - }
    -
    - if (offset_plain > sizeof(struct gg_recv_msg80) && memchr(packet +
    - sizeof(struct gg_recv_msg80), 0, offset_plain -
    - sizeof(struct gg_recv_msg80)) == NULL)
    - {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_handle_recv_msg80() malformed packet, "
    - "message out of bounds (3)\n");
    - goto malformed;
    - }
    -
    - e->type = (type != GG_RECV_OWN_MSG) ? GG_EVENT_MSG : GG_EVENT_MULTILOGON_MSG;
    - e->event.msg.msgclass = gg_fix32(r->msgclass);
    - e->event.msg.sender = gg_fix32(r->sender);
    - e->event.msg.time = gg_fix32(r->time);
    - e->event.msg.seq = gg_fix32(r->seq);
    -
    - if (offset_attr != 0) {
    - switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender),
    - packet + offset_attr, packet + length, type))
    - {
    - case -1: /* handled */
    - gg_session_send_msg_ack(sess, gg_fix32(r->seq));
    - return 0;
    -
    - case -2: /* failed */
    - goto fail;
    -
    - case -3: /* malformed */
    - goto malformed;
    - }
    - }
    -
    - if (sess->encoding == GG_ENCODING_CP1250) {
    - e->event.msg.message = (unsigned char*) strdup(packet + offset_plain);
    -
    - if (e->event.msg.message == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_session_handle_recv_msg_80() out of memory\n");
    - goto fail;
    - }
    - } else {
    - if (offset_plain > sizeof(struct gg_recv_msg80)) {
    - size_t len, fmt_len;
    -
    - len = gg_message_html_to_text(NULL, NULL, &fmt_len,
    - packet + sizeof(struct gg_recv_msg80),
    - GG_ENCODING_UTF8);
    - e->event.msg.message = malloc(len + 1);
    -
    - if (e->event.msg.message == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_session_handle_recv_msg_80() "
    - "out of memory\n");
    - goto fail;
    - }
    -
    - free(e->event.msg.formats);
    - e->event.msg.formats_length = fmt_len;
    - e->event.msg.formats = malloc(fmt_len);
    -
    - if (e->event.msg.formats == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_session_handle_recv_msg_80() "
    - "out of memory\n");
    - goto fail;
    - }
    -
    - gg_message_html_to_text((char*)e->event.msg.message,
    - e->event.msg.formats, NULL,
    - packet + sizeof(struct gg_recv_msg80),
    - GG_ENCODING_UTF8);
    - } else {
    - e->event.msg.message = (unsigned char*)gg_encoding_convert(
    - packet + offset_plain, GG_ENCODING_CP1250,
    - sess->encoding, -1, -1);
    -
    - if (e->event.msg.message == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_session_handle_recv_msg_80() "
    - "out of memory\n");
    - goto fail;
    - }
    - }
    - }
    -
    - if (offset_plain > sizeof(struct gg_recv_msg80)) {
    - e->event.msg.xhtml_message = gg_encoding_convert(
    - packet + sizeof(struct gg_recv_msg80), GG_ENCODING_UTF8,
    - sess->encoding, -1, -1);
    -
    - if (e->event.msg.xhtml_message == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_session_handle_recv_msg_80() out of memory\n");
    - goto fail;
    - }
    - } else {
    - size_t len;
    -
    - len = gg_message_text_to_html(NULL,
    - (char*)e->event.msg.message, sess->encoding,
    - e->event.msg.formats, e->event.msg.formats_length);
    - e->event.msg.xhtml_message = malloc(len + 1);
    -
    - if (e->event.msg.xhtml_message == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_session_handle_recv_msg_80() out of memory\n");
    - goto fail;
    - }
    -
    - gg_message_text_to_html(e->event.msg.xhtml_message,
    - (char*)e->event.msg.message, sess->encoding,
    - e->event.msg.formats, e->event.msg.formats_length);
    - }
    -
    - gg_session_send_msg_ack(sess, gg_fix32(r->seq));
    - return 0;
    -
    -fail:
    - free(e->event.msg.message);
    - free(e->event.msg.xhtml_message);
    - free(e->event.msg.recipients);
    - free(e->event.msg.formats);
    - return -1;
    -
    -malformed:
    - e->type = GG_EVENT_NONE;
    - free(e->event.msg.message);
    - free(e->event.msg.xhtml_message);
    - free(e->event.msg.recipients);
    - free(e->event.msg.formats);
    - gg_session_send_msg_ack(sess, gg_fix32(r->seq));
    - return 0;
    -}
    -
    -static int gg_session_handle_recv_msg_110(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - GG110RecvMessage *msg = gg110_recv_message__unpack(NULL, len, (uint8_t*)ptr);
    - uint8_t ack_type;
    - uin_t sender = 0;
    - uint32_t seq;
    - int succ = 1;
    - struct gg_event_msg *ev = &ge->event.msg;
    -
    - gg_debug_session(gs, GG_DEBUG_FUNCTION,
    - "** gg_session_handle_recv_msg_110(%p, %" GG_SIZE_FMT
    - ", %p);\n", ptr, len, ge);
    -
    - if (!GG_PROTOBUF_VALID(gs, "GG110RecvMessage", msg))
    - return -1;
    -
    - seq = msg->seq;
    - if (type == GG_CHAT_RECV_MSG || type == GG_CHAT_RECV_OWN_MSG)
    - ack_type = GG110_ACK__TYPE__CHAT;
    - else
    - ack_type = GG110_ACK__TYPE__MSG;
    -
    - if (msg->has_msg_id || msg->has_conv_id) {
    - msg->msg_id = msg->has_msg_id ? msg->msg_id : 0;
    - msg->conv_id = msg->has_conv_id ? msg->conv_id : 0;
    - gg_debug_session(gs, GG_DEBUG_VERBOSE,
    - "// gg_session_handle_recv_msg_110() "
    - "msg_id=%016" PRIx64 " conv_id=%016" PRIx64 "\n",
    - msg->msg_id, msg->conv_id);
    - }
    -
    - if (msg->has_sender)
    - sender = gg_protobuf_get_uin(msg->sender);
    - else if (type == GG_CHAT_RECV_OWN_MSG)
    - sender = gs->uin;
    -
    - if (msg->has_data && msg->msg_plain[0] == '\0') {
    - if (msg->data.len < sizeof(struct gg_msg_image_reply)) {
    - gg_debug_session(gs, GG_DEBUG_ERROR,
    - "// gg_session_handle_recv_msg_110() "
    - "packet too small (%" GG_SIZE_FMT " < %"
    - GG_SIZE_FMT ")\n", msg->data.len,
    - sizeof(struct gg_msg_image_reply));
    - } else {
    - gg_image_queue_parse(ge, (char *)msg->data.data,
    - msg->data.len, gs, sender, type);
    - }
    - gg110_recv_message__free_unpacked(msg, NULL);
    - return gg_ack_110(gs, GG110_ACK__TYPE__MSG, seq, ge);
    - }
    -
    - if (type == GG_RECV_OWN_MSG110 || type == GG_CHAT_RECV_OWN_MSG)
    - ge->type = GG_EVENT_MULTILOGON_MSG;
    - else
    - ge->type = GG_EVENT_MSG;
    - ev->msgclass = GG_CLASS_CHAT;
    - ev->seq = seq;
    - ev->sender = sender;
    - ev->flags = msg->flags;
    - ev->seq = seq;
    - ev->time = msg->time;
    -
    - if (abs(msg->time - gg_server_time(gs)) > 2)
    - ev->msgclass |= GG_CLASS_QUEUED;
    -
    - ev->message = NULL;
    - if (msg->msg_plain[0] != '\0') {
    - ev->message = (unsigned char*)gg_encoding_convert(
    - msg->msg_plain, GG_ENCODING_UTF8, gs->encoding, -1, -1);
    - succ = succ && (ev->message != NULL);
    - }
    - ev->xhtml_message = NULL;
    - if (msg->msg_xhtml != NULL) {
    - ev->xhtml_message = gg_encoding_convert(
    - msg->msg_xhtml, GG_ENCODING_UTF8, gs->encoding, -1, -1);
    - succ = succ && (ev->xhtml_message != NULL);
    - }
    -
    - /* wiadomości wysłane z mobilnego gg nie posiadają wersji xhtml */
    - if (ev->message == NULL && ev->xhtml_message == NULL) {
    - ev->message = (unsigned char*)strdup("");
    - succ = succ && (ev->message != NULL);
    - } else if (ev->message == NULL) {
    - ev->message = (unsigned char*)gg_message_html_to_text_110(
    - ev->xhtml_message);
    - succ = succ && (ev->message != NULL);
    - } else if (ev->xhtml_message == NULL) {
    - ev->xhtml_message = gg_message_text_to_html_110(
    - (char*)ev->message, -1);
    - succ = succ && (ev->xhtml_message != NULL);
    - }
    -
    - /* otrzymywane tylko od gg <= 10.5 */
    - ev->formats = NULL;
    - ev->formats_length = 0;
    - if (msg->has_data && succ) {
    - ev->formats_length = msg->data.len;
    - ev->formats = malloc(msg->data.len);
    - if (ev->formats == NULL)
    - succ = 0;
    - else
    - memcpy(ev->formats, msg->data.data, msg->data.len);
    - }
    -
    - if (msg->has_chat_id && succ) {
    - gg_chat_list_t *chat;
    -
    - ev->chat_id = msg->chat_id;
    -
    - chat = gg_chat_find(gs, msg->chat_id);
    - if (chat) {
    - size_t rcpt_size = chat->participants_count *
    - sizeof(uin_t);
    - ev->recipients = malloc(rcpt_size);
    - ev->recipients_count = chat->participants_count;
    - if (ev->recipients == NULL)
    - succ = 0;
    - else {
    - memcpy(ev->recipients, chat->participants,
    - rcpt_size);
    - }
    - }
    - }
    -
    - gg110_recv_message__free_unpacked(msg, NULL);
    -
    - if (gg_ack_110(gs, ack_type, seq, ge) != 0)
    - succ = 0;
    -
    - if (succ)
    - return 0;
    - else {
    - free(ev->message);
    - free(ev->xhtml_message);
    - free(ev->formats);
    - free(ev->recipients);
    - return -1;
    - }
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_STATUS.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_status(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - const struct gg_status *s = (const void*) ptr;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
    -
    - ge->type = GG_EVENT_STATUS;
    - ge->event.status.uin = gg_fix32(s->uin);
    - ge->event.status.status = gg_fix32(s->status);
    - ge->event.status.descr = NULL;
    -
    - if (len > sizeof(*s)) {
    - ge->event.status.descr = gg_encoding_convert(ptr + sizeof(*s),
    - GG_ENCODING_CP1250, gs->encoding, len - sizeof(*s), -1);
    -
    - if (ge->event.status.descr == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
    - return -1;
    - }
    - }
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiety GG_STATUS60, GG_STATUS77 i GG_STATUS80BETA.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_status_60_77_80beta(struct gg_session *gs,
    - uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
    -{
    - const struct gg_status60 *s60 = (const void*) ptr;
    - const struct gg_status77 *s77 = (const void*) ptr;
    - size_t struct_len;
    - uint32_t uin;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
    -
    - ge->type = GG_EVENT_STATUS60;
    - ge->event.status60.descr = NULL;
    - ge->event.status60.time = 0;
    -
    - if (type == GG_STATUS60) {
    - uin = gg_fix32(s60->uin);
    - ge->event.status60.status = s60->status;
    - ge->event.status60.remote_ip = s60->remote_ip;
    - ge->event.status60.remote_port = gg_fix16(s60->remote_port);
    - ge->event.status60.version = s60->version;
    - ge->event.status60.image_size = s60->image_size;
    - struct_len = sizeof(*s60);
    - } else {
    - uin = gg_fix32(s77->uin);
    - ge->event.status60.status = s77->status;
    - ge->event.status60.remote_ip = s77->remote_ip;
    - ge->event.status60.remote_port = gg_fix16(s77->remote_port);
    - ge->event.status60.version = s77->version;
    - ge->event.status60.image_size = s77->image_size;
    - struct_len = sizeof(*s77);
    - }
    -
    - ge->event.status60.uin = uin & 0x00ffffff;
    -
    - if (uin & 0x40000000)
    - ge->event.status60.version |= GG_HAS_AUDIO_MASK;
    - if (uin & 0x20000000)
    - ge->event.status60.version |= GG_HAS_AUDIO7_MASK;
    - if (uin & 0x08000000)
    - ge->event.status60.version |= GG_ERA_OMNIX_MASK;
    -
    - if (len > struct_len) {
    - size_t descr_len;
    -
    - descr_len = len - struct_len;
    -
    - ge->event.status60.descr = gg_encoding_convert(ptr + struct_len,
    - (type == GG_STATUS80BETA) ? GG_ENCODING_UTF8 : GG_ENCODING_CP1250,
    - gs->encoding, descr_len, -1);
    -
    - if (ge->event.status60.descr == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
    - return -1;
    - }
    -
    - if (descr_len > 4 && ptr[len - 5] == 0) {
    - uint32_t t;
    - memcpy(&t, ptr + len - 4, sizeof(uint32_t));
    - ge->event.status60.time = gg_fix32(t);
    - }
    - }
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_NOTIFY_REPLY.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_notify_reply(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - const struct gg_notify_reply *n = (const void*) ptr;
    - char *descr;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
    -
    - if (gg_fix32(n->status) == GG_STATUS_BUSY_DESCR ||
    - gg_fix32(n->status) == GG_STATUS_NOT_AVAIL_DESCR ||
    - gg_fix32(n->status) == GG_STATUS_AVAIL_DESCR)
    - {
    - size_t descr_len;
    -
    - ge->type = GG_EVENT_NOTIFY_DESCR;
    -
    - if (!(ge->event.notify_descr.notify = (void*) malloc(sizeof(*n) * 2))) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
    - return -1;
    - }
    - ge->event.notify_descr.notify[1].uin = 0;
    - memcpy(ge->event.notify_descr.notify, ptr, sizeof(*n));
    - ge->event.notify_descr.notify[0].uin = gg_fix32(ge->event.notify_descr.notify[0].uin);
    - ge->event.notify_descr.notify[0].status = gg_fix32(ge->event.notify_descr.notify[0].status);
    - ge->event.notify_descr.notify[0].remote_port = gg_fix16(ge->event.notify_descr.notify[0].remote_port);
    - ge->event.notify_descr.notify[0].version = gg_fix32(ge->event.notify_descr.notify[0].version);
    -
    - descr_len = len - sizeof(*n);
    -
    - descr = gg_encoding_convert(ptr + sizeof(*n), GG_ENCODING_CP1250, gs->encoding, descr_len, -1);
    -
    - if (descr == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
    - return -1;
    - }
    -
    - ge->event.notify_descr.descr = descr;
    -
    - } else {
    - unsigned int i, count;
    -
    - ge->type = GG_EVENT_NOTIFY;
    -
    - if (!(ge->event.notify = (void*) malloc(len + 2 * sizeof(*n)))) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
    - return -1;
    - }
    -
    - memcpy(ge->event.notify, ptr, len);
    - count = len / sizeof(*n);
    - ge->event.notify[count].uin = 0;
    -
    - for (i = 0; i < count; i++) {
    - ge->event.notify[i].uin = gg_fix32(ge->event.notify[i].uin);
    - ge->event.notify[i].status = gg_fix32(ge->event.notify[i].status);
    - ge->event.notify[i].remote_port = gg_fix16(ge->event.notify[i].remote_port);
    - ge->event.notify[i].version = gg_fix32(ge->event.notify[i].version);
    - }
    - }
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_STATUS80.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_status_80(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - const struct gg_notify_reply80 *n = (const void*) ptr;
    - size_t descr_len;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n");
    -
    - ge->type = GG_EVENT_STATUS60;
    - ge->event.status60.uin = gg_fix32(n->uin);
    - ge->event.status60.status = gg_fix32(n->status);
    - ge->event.status60.remote_ip = n->remote_ip;
    - ge->event.status60.remote_port = gg_fix16(n->remote_port);
    - ge->event.status60.version = 0;
    - ge->event.status60.image_size = n->image_size;
    - ge->event.status60.descr = NULL;
    - ge->event.status60.time = 0;
    -
    - descr_len = gg_fix32(n->descr_len);
    -
    - if (descr_len != 0 && sizeof(struct gg_notify_reply80) + descr_len <= len) {
    - ge->event.status60.descr = gg_encoding_convert(
    - (const char*) n + sizeof(struct gg_notify_reply80),
    - GG_ENCODING_UTF8, gs->encoding, descr_len, -1);
    -
    - if (ge->event.status60.descr == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
    - return -1;
    - }
    -
    - /* XXX czas */
    - }
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_NOTIFY_REPLY80.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_notify_reply_80(struct gg_session *gs,
    - uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
    -{
    - const struct gg_notify_reply80 *n = (const void*) ptr;
    - unsigned int length = len, i = 0;
    -
    - /* TODO: najpierw przeanalizować strukturę i określić
    - * liczbę rekordów, żeby obyć się bez realloc()
    - */
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
    -
    - ge->type = GG_EVENT_NOTIFY60;
    - ge->event.notify60 = malloc(sizeof(*ge->event.notify60));
    -
    - if (!ge->event.notify60) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
    - return -1;
    - }
    -
    - ge->event.notify60[0].uin = 0;
    -
    - while (length >= sizeof(struct gg_notify_reply80)) {
    - uin_t uin = gg_fix32(n->uin);
    - int descr_len;
    - void *tmp;
    -
    - ge->event.notify60[i].uin = uin;
    - ge->event.notify60[i].status = gg_fix32(n->status);
    - ge->event.notify60[i].remote_ip = n->remote_ip;
    - ge->event.notify60[i].remote_port = gg_fix16(n->remote_port);
    - ge->event.notify60[i].version = 0;
    - ge->event.notify60[i].image_size = n->image_size;
    - ge->event.notify60[i].descr = NULL;
    - ge->event.notify60[i].time = 0;
    -
    - descr_len = gg_fix32(n->descr_len);
    -
    - if (descr_len != 0) {
    - if (sizeof(struct gg_notify_reply80) + descr_len <= length) {
    - ge->event.notify60[i].descr = gg_encoding_convert(
    - (const char*) n + sizeof(struct gg_notify_reply80),
    - GG_ENCODING_UTF8, gs->encoding, descr_len, -1);
    -
    - if (ge->event.notify60[i].descr == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_watch_fd_connected() "
    - "out of memory\n");
    - return -1;
    - }
    -
    - /* XXX czas */
    -
    - length -= sizeof(struct gg_notify_reply80) + descr_len;
    - n = (const void*) ((const char*) n + sizeof(struct gg_notify_reply80) + descr_len);
    - } else {
    - length = 0;
    - }
    -
    - } else {
    - length -= sizeof(struct gg_notify_reply80);
    - n = (const void*) ((const char*) n + sizeof(struct gg_notify_reply80));
    - }
    -
    - if (!(tmp = realloc(ge->event.notify60, (i + 2) * sizeof(*ge->event.notify60)))) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
    - free(ge->event.notify60);
    - return -1;
    - }
    -
    - ge->event.notify60 = tmp;
    - ge->event.notify60[++i].uin = 0;
    - }
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiety GG_NOTIFY_REPLY77 i GG_NOTIFY_REPLY80BETA.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_notify_reply_77_80beta(struct gg_session *gs,
    - uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
    -{
    - const struct gg_notify_reply77 *n = (const void*) ptr;
    - unsigned int length = len, i = 0;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
    -
    - ge->type = GG_EVENT_NOTIFY60;
    - ge->event.notify60 = malloc(sizeof(*ge->event.notify60));
    -
    - if (ge->event.notify60 == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
    - return -1;
    - }
    -
    - ge->event.notify60[0].uin = 0;
    -
    - while (length >= sizeof(struct gg_notify_reply77)) {
    - uin_t uin = gg_fix32(n->uin);
    - void *tmp;
    -
    - ge->event.notify60[i].uin = uin & 0x00ffffff;
    - ge->event.notify60[i].status = n->status;
    - ge->event.notify60[i].remote_ip = n->remote_ip;
    - ge->event.notify60[i].remote_port = gg_fix16(n->remote_port);
    - ge->event.notify60[i].version = n->version;
    - ge->event.notify60[i].image_size = n->image_size;
    - ge->event.notify60[i].descr = NULL;
    - ge->event.notify60[i].time = 0;
    -
    - if (uin & 0x40000000)
    - ge->event.notify60[i].version |= GG_HAS_AUDIO_MASK;
    - if (uin & 0x20000000)
    - ge->event.notify60[i].version |= GG_HAS_AUDIO7_MASK;
    - if (uin & 0x08000000)
    - ge->event.notify60[i].version |= GG_ERA_OMNIX_MASK;
    -
    - if (GG_S_D(n->status)) {
    - unsigned char descr_len = *((const char*) n + sizeof(struct gg_notify_reply77));
    -
    - if (sizeof(struct gg_notify_reply77) + descr_len <= length) {
    - ge->event.notify60[i].descr = gg_encoding_convert(
    - (const char*) n + sizeof(struct gg_notify_reply77) + 1,
    - (type == GG_NOTIFY_REPLY80BETA) ? GG_ENCODING_UTF8 : GG_ENCODING_CP1250,
    - gs->encoding, descr_len, -1);
    -
    - if (ge->event.notify60[i].descr == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_watch_fd_connected() "
    - "out of memory\n");
    - return -1;
    - }
    -
    - /* XXX czas */
    -
    - length -= sizeof(struct gg_notify_reply77) + descr_len + 1;
    - n = (const void*) ((const char*) n + sizeof(struct gg_notify_reply77) + descr_len + 1);
    - } else {
    - length = 0;
    - }
    -
    - } else {
    - length -= sizeof(struct gg_notify_reply77);
    - n = (const void*) ((const char*) n + sizeof(struct gg_notify_reply77));
    - }
    -
    - if (!(tmp = realloc(ge->event.notify60, (i + 2) * sizeof(*ge->event.notify60)))) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
    - free(ge->event.notify60);
    - return -1;
    - }
    -
    - ge->event.notify60 = tmp;
    - ge->event.notify60[++i].uin = 0;
    - }
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_NOTIFY_REPLY60.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_notify_reply_60(struct gg_session *gs,
    - uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
    -{
    - const struct gg_notify_reply60 *n = (const void*) ptr;
    - unsigned int length = len, i = 0;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
    -
    - ge->type = GG_EVENT_NOTIFY60;
    - ge->event.notify60 = malloc(sizeof(*ge->event.notify60));
    -
    - if (ge->event.notify60 == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
    - return -1;
    - }
    -
    - ge->event.notify60[0].uin = 0;
    -
    - while (length >= sizeof(struct gg_notify_reply60)) {
    - uin_t uin = gg_fix32(n->uin);
    - void *tmp;
    -
    - ge->event.notify60[i].uin = uin & 0x00ffffff;
    - ge->event.notify60[i].status = n->status;
    - ge->event.notify60[i].remote_ip = n->remote_ip;
    - ge->event.notify60[i].remote_port = gg_fix16(n->remote_port);
    - ge->event.notify60[i].version = n->version;
    - ge->event.notify60[i].image_size = n->image_size;
    - ge->event.notify60[i].descr = NULL;
    - ge->event.notify60[i].time = 0;
    -
    - if (uin & 0x40000000)
    - ge->event.notify60[i].version |= GG_HAS_AUDIO_MASK;
    - if (uin & 0x08000000)
    - ge->event.notify60[i].version |= GG_ERA_OMNIX_MASK;
    -
    - if (GG_S_D(n->status)) {
    - unsigned char descr_len = *((const char*) n + sizeof(struct gg_notify_reply60));
    -
    - if (sizeof(struct gg_notify_reply60) + descr_len <= length) {
    - char *descr;
    -
    - descr = gg_encoding_convert((const char*) n +
    - sizeof(struct gg_notify_reply60) + 1,
    - GG_ENCODING_CP1250, gs->encoding,
    - descr_len, -1);
    -
    - if (descr == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_watch_fd_connected() "
    - "out of memory\n");
    - return -1;
    - }
    -
    - ge->event.notify60[i].descr = descr;
    -
    - /* XXX czas */
    -
    - length -= sizeof(struct gg_notify_reply60) + descr_len + 1;
    - n = (const void*) ((const char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1);
    - } else {
    - length = 0;
    - }
    -
    - } else {
    - length -= sizeof(struct gg_notify_reply60);
    - n = (const void*) ((const char*) n + sizeof(struct gg_notify_reply60));
    - }
    -
    - if (!(tmp = realloc(ge->event.notify60, (i + 2) * sizeof(*ge->event.notify60)))) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() out of memory\n");
    - free(ge->event.notify60);
    - return -1;
    - }
    -
    - ge->event.notify60 = tmp;
    - ge->event.notify60[++i].uin = 0;
    - }
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_USER_DATA.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_user_data(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - struct gg_user_data d;
    - const char *p = (const char*) ptr;
    - const char *packet_end = (const char*) ptr + len;
    - struct gg_event_user_data_user *users;
    - unsigned int i, j;
    - int res = 0;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received user data\n");
    -
    - ge->event.user_data.user_count = 0;
    - ge->event.user_data.users = NULL;
    -
    - if (ptr + sizeof(d) > packet_end)
    - goto malformed;
    -
    - memcpy(&d, p, sizeof(d));
    - p += sizeof(d);
    -
    - d.type = gg_fix32(d.type);
    - d.user_count = gg_fix32(d.user_count);
    -
    - if (d.user_count > 0xffff) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (1)\n");
    - goto malformed;
    - }
    -
    - if (d.user_count > 0) {
    - users = calloc(d.user_count, sizeof(struct gg_event_user_data_user));
    -
    - if (users == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_session_handle_user_data() out of memory"
    - " (%d*%" GG_SIZE_FMT ")\n", d.user_count,
    - sizeof(struct gg_event_user_data_user));
    - goto fail;
    - }
    - } else {
    - users = NULL;
    - }
    -
    - ge->type = GG_EVENT_USER_DATA;
    - ge->event.user_data.type = d.type;
    - ge->event.user_data.user_count = d.user_count;
    - ge->event.user_data.users = users;
    -
    - gg_debug_session(gs, GG_DEBUG_DUMP, "type=%d, count=%d\n", d.type, d.user_count);
    -
    - for (i = 0; i < d.user_count; i++) {
    - struct gg_user_data_user u;
    - struct gg_event_user_data_attr *attrs;
    -
    - if (p + sizeof(u) > packet_end) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (2)\n");
    - goto malformed;
    - }
    -
    - memcpy(&u, p, sizeof(u));
    - p += sizeof(u);
    -
    - u.uin = gg_fix32(u.uin);
    - u.attr_count = gg_fix32(u.attr_count);
    -
    - if (u.attr_count > 0xffff) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_user_data() malformed packet (2)\n");
    - goto malformed;
    - }
    -
    - if (u.attr_count > 0) {
    - attrs = calloc(u.attr_count, sizeof(struct gg_event_user_data_attr));
    -
    - if (attrs == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_session_handle_user_data() "
    - "out of memory (%d*%" GG_SIZE_FMT
    - ")\n", u.attr_count,
    - sizeof(struct gg_event_user_data_attr));
    - goto fail;
    - }
    - } else {
    - attrs = NULL;
    - }
    -
    - users[i].uin = u.uin;
    - users[i].attr_count = u.attr_count;
    - users[i].attrs = attrs;
    -
    - gg_debug_session(gs, GG_DEBUG_DUMP, " uin=%d, count=%d\n", u.uin, u.attr_count);
    -
    - for (j = 0; j < u.attr_count; j++) {
    - uint32_t key_size;
    - uint32_t attr_type;
    - uint32_t value_size;
    - char *key;
    - char *value;
    -
    - if (p + sizeof(key_size) > packet_end) {
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_session_handle_user_data()"
    - "malformed packet (3)\n");
    - goto malformed;
    - }
    -
    - memcpy(&key_size, p, sizeof(key_size));
    - p += sizeof(key_size);
    -
    - key_size = gg_fix32(key_size);
    -
    - if (key_size > 0xffff || p + key_size > packet_end) {
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_session_handle_user_data() "
    - "malformed packet (3)\n");
    - goto malformed;
    - }
    -
    - key = malloc(key_size + 1);
    -
    - if (key == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_session_handle_user_data() "
    - "out of memory (%d)\n", key_size + 1);
    - goto fail;
    - }
    -
    - memcpy(key, p, key_size);
    - p += key_size;
    -
    - key[key_size] = 0;
    -
    - attrs[j].key = key;
    -
    - if (p + sizeof(attr_type) + sizeof(value_size) > packet_end) {
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_session_handle_user_data() "
    - "malformed packet (4)\n");
    - goto malformed;
    - }
    -
    - memcpy(&attr_type, p, sizeof(attr_type));
    - p += sizeof(attr_type);
    - memcpy(&value_size, p, sizeof(value_size));
    - p += sizeof(value_size);
    -
    - attrs[j].type = gg_fix32(attr_type);
    - value_size = gg_fix32(value_size);
    -
    - if (value_size > 0xffff || p + value_size > packet_end) {
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_session_handle_user_data() "
    - "malformed packet (5)\n");
    - goto malformed;
    - }
    -
    - value = malloc(value_size + 1);
    -
    - if (value == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_session_handle_user_data() "
    - "out of memory (%d)\n", value_size + 1);
    - goto fail;
    - }
    -
    - memcpy(value, p, value_size);
    - p += value_size;
    -
    - value[value_size] = 0;
    -
    - attrs[j].value = value;
    -
    - gg_debug_session(gs, GG_DEBUG_DUMP, " key=\"%s\", "
    - "type=%d, value=\"%s\"\n", key, attr_type, value);
    - }
    - }
    -
    - return 0;
    -
    -fail:
    - res = -1;
    -
    -malformed:
    - ge->type = GG_EVENT_NONE;
    -
    - for (i = 0; i < ge->event.user_data.user_count; i++) {
    - for (j = 0; j < ge->event.user_data.users[i].attr_count; j++) {
    - free(ge->event.user_data.users[i].attrs[j].key);
    - free(ge->event.user_data.users[i].attrs[j].value);
    - }
    -
    - free(ge->event.user_data.users[i].attrs);
    - }
    -
    - free(ge->event.user_data.users);
    -
    - return res;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_TYPING_NOTIFICATION.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_typing_notification(struct gg_session *gs,
    - uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
    -{
    - const struct gg_typing_notification *n = (const void*) ptr;
    - uin_t uin;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received typing notification\n");
    -
    - memcpy(&uin, &n->uin, sizeof(uin_t));
    -
    - ge->type = GG_EVENT_TYPING_NOTIFICATION;
    - ge->event.typing_notification.uin = gg_fix32(uin);
    - ge->event.typing_notification.length = gg_fix16(n->length);
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_MULTILOGON_INFO.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_multilogon_info(struct gg_session *gs,
    - uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
    -{
    - const char *packet_end = (const char*) ptr + len;
    - const struct gg_multilogon_info *info = (const struct gg_multilogon_info*) ptr;
    - const char *p = (const char*) ptr + sizeof(*info);
    - struct gg_multilogon_session *sessions = NULL;
    - size_t count;
    - size_t i;
    - int res = 0;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received multilogon info\n");
    -
    - count = gg_fix32(info->count);
    -
    - if (count > 0xffff) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (1)\n");
    - goto malformed;
    - }
    -
    - sessions = calloc(count, sizeof(struct gg_multilogon_session));
    -
    - if (sessions == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// "
    - "gg_handle_multilogon_info() out of memory (%"
    - GG_SIZE_FMT "*%" GG_SIZE_FMT ")\n",
    - count, sizeof(struct gg_multilogon_session));
    - return -1;
    - }
    -
    - ge->type = GG_EVENT_MULTILOGON_INFO;
    - ge->event.multilogon_info.count = count;
    - ge->event.multilogon_info.sessions = sessions;
    -
    - for (i = 0; i < count; i++) {
    - struct gg_multilogon_info_item item;
    - size_t name_size;
    -
    - if (p + sizeof(item) > packet_end) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (2)\n");
    - goto malformed;
    - }
    -
    - memcpy(&item, p, sizeof(item));
    -
    - sessions[i].id = item.conn_id;
    - sessions[i].remote_addr = item.addr;
    - sessions[i].status_flags = gg_fix32(item.flags);
    - sessions[i].protocol_features = gg_fix32(item.features);
    - sessions[i].logon_time = gg_fix32(item.logon_time);
    -
    - p += sizeof(item);
    -
    - name_size = gg_fix32(item.name_size);
    -
    - if (name_size > 0xffff || p + name_size > packet_end) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (3)\n");
    - goto malformed;
    - }
    -
    - sessions[i].name = malloc(name_size + 1);
    -
    - if (sessions[i].name == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_handle_multilogon_info() out of "
    - "memory (%" GG_SIZE_FMT ")\n", name_size);
    - goto fail;
    - }
    -
    - memcpy(sessions[i].name, p, name_size);
    - sessions[i].name[name_size] = 0;
    -
    - p += name_size;
    - }
    -
    - return 0;
    -
    -fail:
    - res = -1;
    -
    -malformed:
    - ge->type = GG_EVENT_NONE;
    -
    - for (i = 0; (int) i < ge->event.multilogon_info.count; i++)
    - free(ge->event.multilogon_info.sessions[i].name);
    -
    - free(ge->event.multilogon_info.sessions);
    -
    - return res;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_USERLIST100_VERSION.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_userlist_100_version(struct gg_session *gs,
    - uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
    -{
    - const struct gg_userlist100_version *version = (const struct gg_userlist100_version*) ptr;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist 100 version\n");
    -
    - ge->type = GG_EVENT_USERLIST100_VERSION;
    - ge->event.userlist100_version.version = gg_fix32(version->version);
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Obsługuje pakiet GG_USERLIST100_REPLY.
    - *
    - * Patrz gg_packet_handler_t
    - */
    -static int gg_session_handle_userlist_100_reply(struct gg_session *gs,
    - uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
    -{
    - const struct gg_userlist100_reply *reply = (const struct gg_userlist100_reply*) ptr;
    - char *data = NULL;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist 100 reply\n");
    -
    - if (len > sizeof(*reply)) {
    - data = gg_inflate((const unsigned char*) ptr + sizeof(*reply), len - sizeof(*reply));
    -
    - if (data == NULL) {
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_handle_userlist_100_reply() gg_inflate() failed\n");
    - return -1;
    - }
    - }
    -
    - ge->type = GG_EVENT_USERLIST100_REPLY;
    - ge->event.userlist100_reply.type = reply->type;
    - ge->event.userlist100_reply.version = gg_fix32(reply->version);
    - ge->event.userlist100_reply.format_type = reply->format_type;
    - ge->event.userlist100_reply.reply = data;
    -
    - return 0;
    -}
    -
    -static int gg_session_handle_imtoken(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - GG110Imtoken *msg = gg110_imtoken__unpack(NULL, len, (uint8_t*)ptr);
    - char *imtoken = NULL;
    - int succ = 1;
    -
    - if (!GG_PROTOBUF_VALID(gs, "GG110Imtoken", msg))
    - return -1;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() "
    - "received imtoken\n");
    -
    - if (msg->imtoken[0] != '\0') {
    - imtoken = strdup(msg->imtoken);
    - succ = succ && (imtoken != NULL);
    - }
    -
    - gg110_imtoken__free_unpacked(msg, NULL);
    -
    - ge->type = GG_EVENT_IMTOKEN;
    - ge->event.imtoken.imtoken = imtoken;
    -
    - return succ ? 0 : -1;
    -}
    -
    -static int gg_session_handle_pong_110(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - GG110Pong *msg = gg110_pong__unpack(NULL, len, (uint8_t*)ptr);
    -
    - if (!GG_PROTOBUF_VALID(gs, "GG110Pong", msg))
    - return -1;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_watch_fd_connected() "
    - "received pong110\n");
    -
    - ge->type = GG_EVENT_PONG110;
    - ge->event.pong110.time = msg->server_time;
    -
    - gg_sync_time(gs, msg->server_time);
    -
    - gg110_pong__free_unpacked(msg, NULL);
    -
    - return 0;
    -}
    -
    -static int gg_session_handle_chat_info(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - gg_tvbuff_t *tvb;
    - uint32_t i;
    -
    - uint64_t id;
    - uint32_t version;
    - uint32_t dummy1;
    - uint32_t participants_count;
    - uin_t *participants = NULL;
    -
    - tvb = gg_tvbuff_new(ptr, len);
    -
    - id = gg_tvbuff_read_uint64(tvb);
    - gg_tvbuff_expected_uint32(tvb, 0); /* unknown */
    - version = gg_tvbuff_read_uint32(tvb);
    - dummy1 = gg_tvbuff_read_uint32(tvb);
    - if (gg_tvbuff_is_valid(tvb) && dummy1 == 1) {
    - uint32_t name_length;
    -
    - name_length = gg_tvbuff_read_uint32(tvb);
    - gg_tvbuff_skip(tvb, name_length);
    -
    - gg_tvbuff_expected_uint32(tvb, 0); /* unknown */
    - gg_tvbuff_expected_uint32(tvb, 2); /* unknown */
    - }
    - participants_count = gg_tvbuff_read_uint32(tvb);
    - if (id == 0 && participants_count > 0) {
    - gg_debug_session(gs, GG_DEBUG_MISC | GG_DEBUG_WARNING,
    - "// gg_session_handle_chat_info() terminating packet "
    - "shouldn't contain participants\n");
    - participants_count = 0;
    - }
    -
    - if (participants_count > 0) {
    - participants = malloc(sizeof(uin_t) * participants_count);
    - if (participants == NULL) {
    - gg_tvbuff_close(tvb);
    - return -1;
    - }
    - }
    -
    - for (i = 0; i < participants_count && gg_tvbuff_is_valid(tvb); i++) {
    - participants[i] = gg_tvbuff_read_uint32(tvb);
    - gg_tvbuff_read_uint32(tvb); /* 0x1e lub 0x18 */
    - }
    -
    - if (!gg_tvbuff_close(tvb)) {
    - free(participants);
    - return -1;
    - }
    -
    - if (id == 0) {
    - ge->type = GG_EVENT_CHAT_INFO_GOT_ALL;
    - return 0;
    - }
    -
    - if (0 != gg_chat_update(gs, id, version, participants,
    - participants_count))
    - {
    - free(participants);
    - return -1;
    - }
    -
    - ge->type = GG_EVENT_CHAT_INFO;
    - ge->event.chat_info.id = id;
    - ge->event.chat_info.version = version;
    - ge->event.chat_info.participants_count = participants_count;
    - ge->event.chat_info.participants = participants;
    -
    - return 0;
    -}
    -
    -static int gg_session_handle_chat_info_update(struct gg_session *gs,
    - uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
    -{
    - GG110ChatInfoUpdate *msg = gg110_chat_info_update__unpack(NULL, len, (uint8_t*)ptr);
    - gg_chat_list_t *chat;
    - uin_t participant;
    -
    - if (!GG_PROTOBUF_VALID(gs, "GG110ChatInfoUpdate", msg))
    - return -1;
    -
    - gg_debug_session(gs, GG_DEBUG_VERBOSE,
    - "// gg_session_handle_chat_info_update() "
    - "msg_id=%016" PRIx64 " conv_id=%016" PRIx64 "\n",
    - msg->msg_id, msg->conv_id);
    -
    - ge->type = GG_EVENT_CHAT_INFO_UPDATE;
    - ge->event.chat_info_update.id = msg->chat_id;
    - ge->event.chat_info_update.type = msg->update_type;
    - ge->event.chat_info_update.participant = participant = gg_protobuf_get_uin(msg->participant);
    - ge->event.chat_info_update.inviter = gg_protobuf_get_uin(msg->inviter);
    - ge->event.chat_info_update.version = msg->version;
    - ge->event.chat_info_update.time = msg->time;
    -
    - chat = gg_chat_find(gs, msg->chat_id);
    - if (!chat) {
    - gg110_chat_info_update__free_unpacked(msg, NULL);
    - return 0;
    - }
    -
    - chat->version = msg->version;
    - if (msg->update_type == GG_CHAT_INFO_UPDATE_ENTERED) {
    - uin_t *old_part = chat->participants;
    - chat->participants = realloc(chat->participants,
    - sizeof(uin_t) * chat->participants_count);
    - if (chat->participants == NULL) {
    - chat->participants = old_part;
    - gg_debug_session(gs, GG_DEBUG_ERROR,
    - "// gg_session_handle_chat_info_update() "
    - "out of memory (count=%u)\n",
    - chat->participants_count);
    - return -1;
    - }
    - chat->participants_count++;
    - chat->participants[chat->participants_count - 1] = participant;
    - } else if (msg->update_type == GG_CHAT_INFO_UPDATE_EXITED) {
    - uint32_t idx;
    - for (idx = 0; idx < chat->participants_count; idx++)
    - if (chat->participants[idx] == participant)
    - break;
    - if (chat->participants_count > 1 &&
    - idx < chat->participants_count)
    - chat->participants[idx] = chat->participants[chat->participants_count - 1];
    - if (idx < chat->participants_count) {
    - chat->participants_count--;
    - if (chat->participants_count == 0) {
    - free(chat->participants);
    - chat->participants = NULL;
    - } else {
    - chat->participants = realloc(chat->participants,
    - sizeof(uin_t)*chat->participants_count);
    - }
    - }
    - }
    -
    - gg110_chat_info_update__free_unpacked(msg, NULL);
    - return 0;
    -}
    -
    -static int gg_session_handle_chat_created(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - const struct gg_chat_created *p = (const struct gg_chat_created *)ptr;
    -
    - if (0 != gg_chat_update(gs, gg_fix64(p->id), 0, &gs->uin, 1))
    - return -1;
    -
    - ge->type = GG_EVENT_CHAT_CREATED;
    - ge->event.chat_created.id = gg_fix64(p->id);
    - ge->event.chat_created.seq = gg_fix32(p->seq);
    - return 0;
    -}
    -
    -static int gg_session_handle_chat_invite_ack(struct gg_session *gs,
    - uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
    -{
    - const struct gg_chat_invite_ack *p =
    - (const struct gg_chat_invite_ack *)ptr;
    -
    - ge->type = GG_EVENT_CHAT_INVITE_ACK;
    - ge->event.chat_invite_ack.id = gg_fix64(p->id);
    - ge->event.chat_invite_ack.seq = gg_fix32(p->seq);
    -
    - return 0;
    -}
    -
    -static int gg_session_handle_chat_left(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - const struct gg_chat_left *p = (const struct gg_chat_left *)ptr;
    -
    - ge->type = GG_EVENT_CHAT_INFO_UPDATE;
    - ge->event.chat_info_update.id = gg_fix64(p->id);
    - ge->event.chat_info_update.type = GG_CHAT_INFO_UPDATE_EXITED;
    - /* Właściwie, to nie wiadomo, czy to jest "osoba wychodząca", czy
    - * "osoba wyrzucająca nas" z konferencji. */
    - ge->event.chat_info_update.participant = gg_fix32(p->uin);
    - ge->event.chat_info_update.inviter = gg_fix32(p->uin);
    - ge->event.chat_info_update.version = 0;
    - ge->event.chat_info_update.time = time(NULL);
    -
    - return 0;
    -}
    -
    -static int gg_session_handle_options(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - GG110Options *msg = gg110_options__unpack(NULL, len, (uint8_t*)ptr);
    - size_t i;
    -
    - if (!GG_PROTOBUF_VALID(gs, "GG110Options", msg))
    - return -1;
    -
    - gg_protobuf_expected(gs, "GG110Options.dummy1", msg->dummy1, 0);
    -
    - for (i = 0; i < msg->n_options; i++) {
    - ProtobufKVP *kvp = msg->options[i];
    - if (!GG_PROTOBUF_VALID(gs, "ProtobufKVP", kvp))
    - continue;
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_session_handle_options[%s] = \"%s\"\n",
    - kvp->key, kvp->value);
    - }
    -
    - gg110_options__free_unpacked(msg, NULL);
    -
    - return 0;
    -}
    -
    -static int gg_session_handle_access_info(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - GG110AccessInfo *msg = gg110_access_info__unpack(NULL, len, (uint8_t*)ptr);
    -
    - if (!GG_PROTOBUF_VALID(gs, "GG110AccessInfo", msg))
    - return -1;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_session_handle_access_info: dummy[%02x, %02x], "
    - "last[message=%u, file_transfer=%u, conference_ch=%u]\n",
    - msg->dummy1, msg->dummy2, msg->last_message,
    - msg->last_file_transfer, msg->last_conference_ch);
    -
    - gg110_access_info__free_unpacked(msg, NULL);
    -
    - return 0;
    -}
    -
    -/* ten pakiet jest odbierany tylko, jeżeli przy logowaniu użyliśmy identyfikatora typu 0x01 */
    -static int gg_session_handle_uin_info(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - gg_tvbuff_t *tvb;
    - char *uin1 = NULL, *uin2 = NULL;
    -
    - tvb = gg_tvbuff_new(ptr, len);
    -
    - gg_tvbuff_expected_uint32(tvb, 1); /* unknown */
    - gg_tvbuff_expected_uint32(tvb, 2); /* unknown */
    -
    - /* podstawowy identyfikator (numer GG) */
    - gg_tvbuff_expected_uint8(tvb, 0);
    - gg_tvbuff_read_str_dup(tvb, &uin1);
    -
    - /* identyfikator użyty przy logowaniu (numer GG lub email) */
    - gg_tvbuff_expected_uint8(tvb, 1);
    - gg_tvbuff_read_str_dup(tvb, &uin2);
    -
    - if (!gg_tvbuff_close(tvb)) {
    - free(uin1);
    - free(uin2);
    - return -1;
    - }
    -
    - gg_debug_session(gs, GG_DEBUG_MISC, "// gg_session_handle_uin_info: "
    - "uin1=\"%s\", uin2=\"%s\"\n", uin1, uin2);
    -
    - free(uin1);
    - free(uin2);
    -
    - return 0;
    -}
    -
    -static int gg_session_handle_transfer_info(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - GG112TransferInfo *msg = gg112_transfer_info__unpack(NULL, len, (uint8_t*)ptr);
    - int succ = 1;
    - size_t i;
    - uin_t peer = 0, sender = 0;
    -
    - if (!GG_PROTOBUF_VALID(gs, "GG112TransferInfo", msg))
    - return -1;
    -
    - /* see packets.proto */
    - if (msg->dummy1 != 5 && msg->dummy1 != 6) {
    - gg_debug_session(gs, GG_DEBUG_MISC | GG_DEBUG_WARNING,
    - "// gg_session_handle_transfer_info: "
    - "unknown dummy1 value: %d\n", msg->dummy1);
    - }
    -
    - if (GG_PROTOBUF_VALID(gs, "GG112TransferInfoUin", msg->peer)) {
    - gg_protobuf_expected(gs, "GG112TransferInfoUin.dummy1",
    - msg->peer->dummy1, 1);
    - peer = gg_protobuf_get_uin(msg->peer->uin);
    - }
    - if (GG_PROTOBUF_VALID(gs, "GG112TransferInfoUin", msg->sender)) {
    - gg_protobuf_expected(gs, "GG112TransferInfoUin.dummy1",
    - msg->sender->dummy1, 1);
    - sender = gg_protobuf_get_uin(msg->sender->uin);
    - }
    -
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_session_handle_transfer_info: dummy1=%#x, time=%u, "
    - "sender=%u, peer=%u, msg_id=%#016" PRIx64 ", "
    - "conv_id=%#016" PRIx64 "\n",
    - msg->dummy1, msg->time, sender, peer, msg->msg_id,
    - msg->conv_id);
    -
    - for (i = 0; i < msg->n_data; i++) {
    - ProtobufKVP *kvp = msg->data[i];
    - if (!GG_PROTOBUF_VALID(gs, "ProtobufKVP", kvp))
    - continue;
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_session_handle_transfer_info[%s] = \"%s\"\n",
    - kvp->key, kvp->value);
    - }
    -
    - if (msg->file && GG_PROTOBUF_VALID(gs, "GG112TransferInfoFile", msg->file)) {
    - GG112TransferInfoFile *file = msg->file;
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_session_handle_transfer_info file: "
    - "type=\"%s\", content_type=\"%s\", filename=\"%s\", "
    - "filesize=%u, msg_id=%#016" PRIx64 " url=\"%s\"\n",
    - file->type, file->content_type, file->filename,
    - file->filesize, file->msg_id, file->url);
    - }
    -
    - succ = (gg_ack_110(gs, GG110_ACK__TYPE__TRANSFER_INFO,
    - msg->seq, ge) == 0);
    -
    - gg112_transfer_info__free_unpacked(msg, NULL);
    -
    - return succ ? 0 : -1;
    -}
    -
    -static int gg_session_handle_magic_notification(struct gg_session *gs, uint32_t type,
    - const char *ptr, size_t len, struct gg_event *ge)
    -{
    - GG110MagicNotification *msg = gg110_magic_notification__unpack(NULL, len, (uint8_t*)ptr);
    - int succ = 1;
    -
    - if (!GG_PROTOBUF_VALID(gs, "GG110MagicNotification", msg))
    - return -1;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC,
    - "// gg_session_handle_magic_notification \n");
    -
    - gg_protobuf_expected(gs, "GG110MagicNotification.dummy1", msg->dummy1, 2);
    - gg_protobuf_expected(gs, "GG110MagicNotification.dummy2", msg->dummy2, 1);
    - gg_protobuf_expected(gs, "GG110MagicNotification.dummy3", msg->dummy3, 1);
    -
    - succ = (gg_ack_110(gs, GG110_ACK__TYPE__MAGIC_NOTIFICATION, msg->seq, ge) == 0);
    -
    - gg110_magic_notification__free_unpacked(msg, NULL);
    -
    - return succ ? 0 : -1;
    -}
    -
    -/**
    - * \internal Tablica obsługiwanych pakietów
    - */
    -static const gg_packet_handler_t handlers[] =
    -{
    - /* style:maxlinelength:start-ignore */
    - { GG_WELCOME, GG_STATE_READING_KEY, 0, gg_session_handle_welcome },
    - { GG_LOGIN_OK, GG_STATE_READING_REPLY, 0, gg_session_handle_login_ok },
    - { GG_LOGIN80_OK, GG_STATE_READING_REPLY, 0, gg_session_handle_login_ok },
    - { GG_LOGIN110_OK, GG_STATE_READING_REPLY, 0, gg_session_handle_login110_ok },
    - { GG_NEED_EMAIL, GG_STATE_READING_REPLY, 0, gg_session_handle_login_ok },
    - { GG_LOGIN_FAILED, GG_STATE_READING_REPLY, 0, gg_session_handle_login_failed },
    - { GG_LOGIN80_FAILED, GG_STATE_READING_REPLY, 0, gg_session_handle_login_failed },
    - { GG_SEND_MSG_ACK, GG_STATE_CONNECTED, sizeof(struct gg_send_msg_ack), gg_session_handle_send_msg_ack },
    - { GG_SEND_MSG_ACK110, GG_STATE_CONNECTED, 0, gg_session_handle_send_msg_ack_110 },
    - { GG_PONG, GG_STATE_CONNECTED, 0, gg_session_handle_pong },
    - { GG_DISCONNECTING, GG_STATE_CONNECTED, 0, gg_session_handle_disconnecting },
    - { GG_DISCONNECT_ACK, GG_STATE_DISCONNECTING, 0, gg_session_handle_disconnect_ack },
    - { GG_XML_EVENT, GG_STATE_CONNECTED, 0, gg_session_handle_xml_event },
    - { GG_EVENT110, GG_STATE_CONNECTED, 0, gg_session_handle_event_110 },
    - { GG_PUBDIR50_REPLY, GG_STATE_CONNECTED, 0, gg_session_handle_pubdir50_reply },
    - { GG_USERLIST_REPLY, GG_STATE_CONNECTED, sizeof(char), gg_session_handle_userlist_reply },
    - { GG_DCC7_ID_REPLY, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_id_reply), gg_session_handle_dcc7_id_reply },
    - { GG_DCC7_ACCEPT, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_accept), gg_session_handle_dcc7_accept },
    - { GG_DCC7_NEW, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_new), gg_session_handle_dcc7_new },
    - { GG_DCC7_REJECT, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_reject), gg_session_handle_dcc7_reject },
    - { GG_DCC7_INFO, GG_STATE_CONNECTED, sizeof(struct gg_dcc7_info), gg_session_handle_dcc7_info },
    - { GG_RECV_MSG, GG_STATE_CONNECTED, sizeof(struct gg_recv_msg), gg_session_handle_recv_msg },
    - { GG_RECV_MSG80, GG_STATE_CONNECTED, sizeof(struct gg_recv_msg80), gg_session_handle_recv_msg_80 },
    - { GG_RECV_MSG110, GG_STATE_CONNECTED, 0, gg_session_handle_recv_msg_110 },
    - { GG_RECV_OWN_MSG110, GG_STATE_CONNECTED, 0, gg_session_handle_recv_msg_110 },
    - { GG_STATUS, GG_STATE_CONNECTED, sizeof(struct gg_status), gg_session_handle_status },
    - { GG_STATUS60, GG_STATE_CONNECTED, sizeof(struct gg_status60), gg_session_handle_status_60_77_80beta },
    - { GG_STATUS77, GG_STATE_CONNECTED, sizeof(struct gg_status77), gg_session_handle_status_60_77_80beta },
    - { GG_STATUS80BETA, GG_STATE_CONNECTED, sizeof(struct gg_status77), gg_session_handle_status_60_77_80beta },
    - { GG_STATUS80, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply80), gg_session_handle_status_80 },
    - { GG_NOTIFY_REPLY, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply), gg_session_handle_notify_reply },
    - { GG_NOTIFY_REPLY60, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply60), gg_session_handle_notify_reply_60 },
    - { GG_NOTIFY_REPLY77, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply77), gg_session_handle_notify_reply_77_80beta },
    - { GG_NOTIFY_REPLY80BETA, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply77), gg_session_handle_notify_reply_77_80beta },
    - { GG_NOTIFY_REPLY80, GG_STATE_CONNECTED, sizeof(struct gg_notify_reply80), gg_session_handle_notify_reply_80 },
    - { GG_USER_DATA, GG_STATE_CONNECTED, sizeof(struct gg_user_data), gg_session_handle_user_data },
    - { GG_TYPING_NOTIFICATION, GG_STATE_CONNECTED, sizeof(struct gg_typing_notification), gg_session_handle_typing_notification },
    - { GG_MULTILOGON_INFO, GG_STATE_CONNECTED, sizeof(struct gg_multilogon_info), gg_session_handle_multilogon_info },
    - { GG_XML_ACTION, GG_STATE_CONNECTED, 0, gg_session_handle_xml_event },
    - { GG_RECV_OWN_MSG, GG_STATE_CONNECTED, sizeof(struct gg_recv_msg80), gg_session_handle_recv_msg_80 },
    - { GG_USERLIST100_VERSION, GG_STATE_CONNECTED, sizeof(struct gg_userlist100_version), gg_session_handle_userlist_100_version },
    - { GG_USERLIST100_REPLY, GG_STATE_CONNECTED, sizeof(struct gg_userlist100_reply), gg_session_handle_userlist_100_reply },
    - { GG_IMTOKEN, GG_STATE_CONNECTED, 0, gg_session_handle_imtoken },
    - { GG_PONG110, GG_STATE_CONNECTED, 0, gg_session_handle_pong_110 },
    - { GG_CHAT_INFO, GG_STATE_CONNECTED, 0, gg_session_handle_chat_info },
    - { GG_CHAT_INFO_UPDATE, GG_STATE_CONNECTED, 0, gg_session_handle_chat_info_update },
    - { GG_CHAT_CREATED, GG_STATE_CONNECTED, sizeof(struct gg_chat_created), gg_session_handle_chat_created },
    - { GG_CHAT_INVITE_ACK, GG_STATE_CONNECTED, sizeof(struct gg_chat_invite_ack), gg_session_handle_chat_invite_ack },
    - { GG_CHAT_RECV_MSG, GG_STATE_CONNECTED, 0, gg_session_handle_recv_msg_110 },
    - { GG_CHAT_RECV_OWN_MSG, GG_STATE_CONNECTED, 0, gg_session_handle_recv_msg_110 },
    - { GG_CHAT_LEFT, GG_STATE_CONNECTED, sizeof(struct gg_chat_left), gg_session_handle_chat_left },
    - { GG_OPTIONS, GG_STATE_CONNECTED, 0, gg_session_handle_options },
    - { GG_ACCESS_INFO, GG_STATE_CONNECTED, 0, gg_session_handle_access_info },
    - { GG_UIN_INFO, GG_STATE_CONNECTED, 0, gg_session_handle_uin_info },
    - { GG_TRANSFER_INFO, GG_STATE_CONNECTED, 0, gg_session_handle_transfer_info },
    - { GG_MAGIC_NOTIFICATION, GG_STATE_CONNECTED, 0, gg_session_handle_magic_notification }
    - /* style:maxlinelength:end-ignore */
    -};
    -
    -/**
    - * \internal Obsługuje przychodzący pakiet danych.
    - *
    - * \param gs Struktura sesji
    - * \param type Typ pakietu
    - * \param ptr Wskaźnik do bufora pakietu
    - * \param len Długość bufora pakietu
    - * \param[out] ge Struktura zdarzenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -int gg_session_handle_packet(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge)
    -{
    - unsigned int i;
    -
    - gg_debug_session(gs, GG_DEBUG_FUNCTION,
    - "// gg_session_handle_packet(%d, %p, %" GG_SIZE_FMT ")\n",
    - type, ptr, len);
    -
    - gs->last_event = time(NULL);
    -
    -#if 0
    - if ((gs->flags & (1 << GG_SESSION_FLAG_RAW_PACKET)) != 0) {
    - char *tmp;
    -
    - tmp = malloc(len);
    -
    - if (tmp == NULL) {
    - gg_debug_session(gs, GG_DEBUG_ERROR,
    - "// gg_session_handle_packet() out of memory "
    - "(%d bytes)\n", len);
    - return -1;
    - }
    -
    - memcpy(tmp, ptr, len);
    -
    - ge->type = GG_EVENT_RAW_PACKET;
    - ge->event.raw_packet.type = type;
    - ge->event.raw_packet.length = len;
    - ge->event.raw_packet.data = tmp;
    -
    - return 0;
    - }
    -#endif
    -
    - for (i = 0; i < sizeof(handlers) / sizeof(handlers[0]); i++) {
    - if (handlers[i].type != 0 && handlers[i].type != type)
    - continue;
    -
    - if (handlers[i].state != 0 && handlers[i].state != (enum gg_state_t) gs->state) {
    - gg_debug_session(gs, GG_DEBUG_WARNING,
    - "// gg_session_handle_packet() packet 0x%02x "
    - "unexpected in state %d\n", type, gs->state);
    - continue;
    - }
    -
    - if (len < handlers[i].min_length) {
    - gg_debug_session(gs, GG_DEBUG_ERROR,
    - "// gg_session_handle_packet() packet 0x%02x "
    - "too short (%" GG_SIZE_FMT " bytes)\n",
    - type, len);
    - continue;
    - }
    -
    - return (*handlers[i].handler)(gs, type, ptr, len, ge);
    - }
    -
    - gg_debug_session(gs, GG_DEBUG_WARNING, "// gg_session_handle_packet() "
    - "unhandled packet 0x%02x, len %" GG_SIZE_FMT ", state %d\n",
    - type, len, gs->state);
    -
    - return 0;
    -}
    --- a/libpurple/protocols/gg/lib/http.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,592 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file http.c
    - *
    - * \brief Obsługa połączeń HTTP
    - */
    -
    -#include "strman.h"
    -#include "network.h"
    -#include "libgadu.h"
    -#include "resolver.h"
    -#include "internal.h"
    -
    -#include <ctype.h>
    -#include <errno.h>
    -#include <stdlib.h>
    -#include <string.h>
    -
    -#define GG_HTTP_MAX_LENGTH 1000000000
    -
    -/**
    - * Rozpoczyna połączenie HTTP.
    - *
    - * Funkcja przeprowadza połączenie HTTP przy połączeniu synchronicznym,
    - * zwracając wynik w polach struktury \c gg_http, lub błąd, gdy sesja się
    - * nie powiedzie.
    - *
    - * Przy połączeniu asynchronicznym, funkcja rozpoczyna połączenie, a dalsze
    - * etapy będą przeprowadzane po wykryciu zmian (\c watch) na obserwowanym
    - * deskryptorze (\c fd) i wywołaniu funkcji \c gg_http_watch_fd().
    - *
    - * Po zakończeniu, należy zwolnić strukturę za pomocą funkcji
    - * \c gg_http_free(). Połączenie asynchroniczne można zatrzymać w każdej
    - * chwili za pomocą \c gg_http_stop().
    - *
    - * \param hostname Adres serwera
    - * \param port Port serwera
    - * \param async Flaga asynchronicznego połączenia
    - * \param method Metoda HTTP
    - * \param path Ścieżka do zasobu (musi być poprzedzona znakiem '/')
    - * \param header Nagłówek zapytania plus ewentualne dane dla POST
    - *
    - * \return Zaalokowana struktura \c gg_http lub NULL, jeśli wystąpił błąd.
    - *
    - * \ingroup http
    - */
    -struct gg_http *gg_http_connect(const char *hostname, int port, int async,
    - const char *method, const char *path, const char *header)
    -{
    - struct gg_http *h;
    -
    - if (!hostname || !port || !method || !path || !header) {
    - gg_debug(GG_DEBUG_MISC, "// gg_http_connect() invalid arguments\n");
    - errno = EFAULT;
    - return NULL;
    - }
    -
    - if (!(h = malloc(sizeof(*h))))
    - return NULL;
    - memset(h, 0, sizeof(*h));
    -
    - h->async = async;
    - h->port = port;
    - h->fd = -1;
    - h->type = GG_SESSION_HTTP;
    -
    - gg_http_set_resolver(h, GG_RESOLVER_DEFAULT);
    -
    - if (gg_proxy_enabled) {
    - char *auth = gg_proxy_auth();
    -
    - h->query = gg_saprintf("%s http://%s:%d%s HTTP/1.0\r\n%s%s",
    - method, hostname, port, path, (auth) ? auth :
    - "", header);
    - hostname = gg_proxy_host;
    - h->port = port = gg_proxy_port;
    - free(auth);
    -
    - } else {
    - h->query = gg_saprintf("%s %s HTTP/1.0\r\n%s",
    - method, path, header);
    - }
    -
    - if (h->query == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_http_connect() not enough memory for query\n");
    - free(h);
    - errno = ENOMEM;
    - return NULL;
    - }
    -
    - gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h->query);
    -
    - if (async) {
    - if (h->resolver_start(&h->fd, &h->resolver, hostname) == -1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver failed\n");
    - gg_http_free(h);
    - errno = ENOENT;
    - return NULL;
    - }
    -
    - gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver = %p\n", h->resolver);
    -
    - h->state = GG_STATE_RESOLVING;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    - } else {
    - struct in_addr *addr_list = NULL;
    - unsigned int addr_count;
    -
    - if (gg_gethostbyname_real(hostname, &addr_list, &addr_count, 0) == -1 || addr_count == 0) {
    - gg_debug(GG_DEBUG_MISC, "// gg_http_connect() host not found\n");
    - gg_http_free(h);
    - free(addr_list);
    - errno = ENOENT;
    - return NULL;
    - }
    -
    - h->fd = gg_connect(&addr_list[0], port, 0);
    -
    - if (h->fd == -1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_http_connect() "
    - "connection failed (errno=%d, %s)\n",
    - errno, strerror(errno));
    - gg_http_free(h);
    - free(addr_list);
    - return NULL;
    - }
    -
    - free(addr_list);
    -
    - h->state = GG_STATE_CONNECTING;
    -
    - while (h->state != GG_STATE_ERROR && h->state != GG_STATE_PARSING) {
    - if (gg_http_watch_fd(h) == -1)
    - break;
    - }
    -
    - if (h->state != GG_STATE_PARSING) {
    - gg_debug(GG_DEBUG_MISC, "// gg_http_connect() some strange error\n");
    - gg_http_free(h);
    - return NULL;
    - }
    - }
    -
    - h->callback = gg_http_watch_fd;
    - h->destroy = gg_http_free;
    -
    - return h;
    -}
    -
    -#ifndef DOXYGEN
    -
    -#define gg_http_error(x) \
    - if (h->fd > -1) \
    - close(h->fd); \
    - h->fd = -1; \
    - h->state = GG_STATE_ERROR; \
    - h->error = x; \
    - return 0;
    -
    -#endif /* DOXYGEN */
    -
    -/**
    - * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
    - *
    - * Operacja będzie zakończona, gdy pole \c state będzie równe
    - * \c GG_STATE_PARSING. W tym miejscu działanie przejmuje zwykle funkcja
    - * korzystająca z \c gg_http_watch_fd(). W przypadku błędu połączenia,
    - * pole \c state będzie równe \c GG_STATE_ERROR, a kod błędu znajdzie się
    - * w polu \c error.
    - *
    - * \param h Struktura połączenia
    - *
    - * \return \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup http
    - */
    -int gg_http_watch_fd(struct gg_http *h)
    -{
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_http_watch_fd(%p);\n", h);
    -
    - if (h == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_http_watch_fd() invalid arguments\n");
    - errno = EFAULT;
    - return -1;
    - }
    -
    - if (h->state == GG_STATE_RESOLVING) {
    - struct in_addr addr;
    - int res;
    -
    - gg_debug(GG_DEBUG_MISC, "=> http, resolving done\n");
    -
    - do {
    - res = gg_resolver_recv(h->fd, &addr, sizeof(addr));
    - } while (res == -1 && errno == EINTR);
    -
    - h->resolver_cleanup(&h->resolver, 0);
    -
    - if (res != sizeof(addr) || addr.s_addr == INADDR_NONE) {
    - gg_debug(GG_DEBUG_MISC, "=> http, resolver thread failed\n");
    - gg_http_error(GG_ERROR_RESOLVING);
    - }
    -
    - close(h->fd);
    - h->fd = -1;
    -
    - gg_debug(GG_DEBUG_MISC, "=> http, connecting to %s:%d\n", inet_ntoa(addr), h->port);
    -
    - h->fd = gg_connect(&addr, h->port, h->async);
    -
    - if (h->fd == -1) {
    - gg_debug(GG_DEBUG_MISC, "=> http, connection failed (errno=%d, %s)\n", errno, strerror(errno));
    - gg_http_error(GG_ERROR_CONNECTING);
    - }
    -
    - h->state = GG_STATE_CONNECTING;
    - h->check = GG_CHECK_WRITE;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    -
    - return 0;
    - }
    -
    - if (h->state == GG_STATE_CONNECTING) {
    - int res = 0;
    - socklen_t res_size = sizeof(res);
    -
    - if (h->async && (getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
    - gg_debug(GG_DEBUG_MISC, "=> http, async connection "
    - "failed (errno=%d, %s)\n", (res) ? res : errno,
    - strerror((res) ? res : errno));
    - close(h->fd);
    - h->fd = -1;
    - h->state = GG_STATE_ERROR;
    - h->error = GG_ERROR_CONNECTING;
    - if (res)
    - errno = res;
    - return 0;
    - }
    -
    - gg_debug(GG_DEBUG_MISC, "=> http, connected, sending request\n");
    -
    - h->state = GG_STATE_SENDING_QUERY;
    - }
    -
    - if (h->state == GG_STATE_SENDING_QUERY) {
    - int res;
    -
    - res = send(h->fd, h->query, strlen(h->query), 0);
    -
    - if (res == -1 && errno != EINTR && errno != EAGAIN) {
    - gg_debug(GG_DEBUG_MISC, "=> http, send() failed "
    - "(len=%" GG_SIZE_FMT ", res=%d, errno=%d)\n",
    - strlen(h->query), res, errno);
    - gg_http_error(GG_ERROR_WRITING);
    - }
    -
    - if (res == -1) {
    - gg_debug(GG_DEBUG_MISC, "=> http, non-critical send "
    - "error (errno=%d, %s)\n",
    - errno, strerror(errno));
    - return 0;
    - }
    -
    - if ((size_t) res < strlen(h->query)) {
    - gg_debug(GG_DEBUG_MISC, "=> http, partial header sent "
    - "(led=%" GG_SIZE_FMT ", sent=%d)\n",
    - strlen(h->query), res);
    -
    - memmove(h->query, h->query + res, strlen(h->query) - res + 1);
    - h->state = GG_STATE_SENDING_QUERY;
    - h->check = GG_CHECK_WRITE;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    - } else {
    - gg_debug(GG_DEBUG_MISC, "=> http, request sent (len=%"
    - GG_SIZE_FMT ")\n", strlen(h->query));
    - free(h->query);
    - h->query = NULL;
    -
    - h->state = GG_STATE_READING_HEADER;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    - }
    -
    - return 0;
    - }
    -
    - if (h->state == GG_STATE_READING_HEADER) {
    - char buf[1024], *tmp;
    - int res;
    -
    - res = recv(h->fd, buf, sizeof(buf), 0);
    -
    - if (res == -1 && errno != EINTR && errno != EAGAIN) {
    - gg_debug(GG_DEBUG_MISC, "=> http, reading header failed (errno=%d)\n", errno);
    - free(h->header);
    - h->header = NULL;
    - gg_http_error(GG_ERROR_READING);
    - }
    -
    - if (res == -1) {
    - gg_debug(GG_DEBUG_MISC, "=> http, non-critical recv "
    - "error (errno=%d, %s)\n",
    - errno, strerror(errno));
    - return 0;
    - }
    -
    - if (res == 0) {
    - gg_debug(GG_DEBUG_MISC, "=> http, connection reset by peer\n");
    - free(h->header);
    - h->header = NULL;
    - gg_http_error(GG_ERROR_READING);
    - }
    -
    - gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of header\n", res);
    -
    - tmp = realloc(h->header, h->header_size + res + 1);
    -
    - if (tmp == NULL) {
    - gg_debug(GG_DEBUG_MISC, "=> http, not enough memory for header\n");
    - free(h->header);
    - h->header = NULL;
    - gg_http_error(GG_ERROR_READING);
    - }
    -
    - h->header = tmp;
    -
    - memcpy(h->header + h->header_size, buf, res);
    - h->header_size += res;
    -
    - gg_debug(GG_DEBUG_MISC, "=> http, header_buf=%p, header_size=%d\n", h->header, h->header_size);
    -
    - h->header[h->header_size] = 0;
    -
    - if ((tmp = strstr(h->header, "\r\n\r\n")) || (tmp = strstr(h->header, "\n\n"))) {
    - int sep_len = (*tmp == '\r') ? 4 : 2;
    - unsigned int left;
    - char *line;
    -
    - left = h->header_size - ((size_t)(tmp) - (size_t)(h->header) + sep_len);
    -
    - gg_debug(GG_DEBUG_MISC, "=> http, got all header "
    - "(%d bytes, %d left)\n",
    - h->header_size - left, left);
    -
    - /* HTTP/1.1 200 OK */
    - if (strlen(h->header) < 16 || strncmp(h->header + 9, "200", 3)) {
    - gg_debug(GG_DEBUG_MISC,
    - "=> -----BEGIN-HTTP-HEADER-----\n%s\n"
    - "=> -----END-HTTP-HEADER-----\n",
    - h->header);
    -
    - gg_debug(GG_DEBUG_MISC, "=> http, didn't get 200 OK -- no results\n");
    - free(h->header);
    - h->header = NULL;
    - gg_http_error(GG_ERROR_CONNECTING);
    - }
    -
    - h->body_size = 0;
    - line = h->header;
    - *tmp = 0;
    -
    - gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----"
    - "\n%s\n=> -----END-HTTP-HEADER-----\n",
    - h->header);
    -
    - while (line) {
    - if (!strncasecmp(line, "Content-length: ", 16)) {
    - h->body_size = atoi(line + 16);
    - }
    - line = strchr(line, '\n');
    - if (line)
    - line++;
    - }
    -
    - if (h->body_size <= 0) {
    - gg_debug(GG_DEBUG_MISC, "=> http, content-length not found\n");
    - h->body_size = left;
    - }
    -
    - if (h->body_size > GG_HTTP_MAX_LENGTH) {
    - gg_debug(GG_DEBUG_MISC, "=> http, content-length too big\n");
    - h->body_size = GG_HTTP_MAX_LENGTH;
    - }
    -
    - if (left > h->body_size) {
    - gg_debug(GG_DEBUG_MISC, "=> http, oversized "
    - "reply (%d bytes needed, "
    - "%d bytes left)\n", h->body_size, left);
    - h->body_size = left;
    - }
    -
    - gg_debug(GG_DEBUG_MISC, "=> http, body_size=%d\n", h->body_size);
    -
    - if (!(h->body = malloc(h->body_size + 1))) {
    - gg_debug(GG_DEBUG_MISC, "=> http, not enough "
    - "memory (%d bytes for body_buf)\n",
    - h->body_size + 1);
    - free(h->header);
    - h->header = NULL;
    - gg_http_error(GG_ERROR_READING);
    - }
    -
    - if (left) {
    - memcpy(h->body, tmp + sep_len, left);
    - h->body_done = left;
    - }
    -
    - h->body[left] = 0;
    -
    - h->state = GG_STATE_READING_DATA;
    - h->check = GG_CHECK_READ;
    - h->timeout = GG_DEFAULT_TIMEOUT;
    - }
    -
    - return 0;
    - }
    -
    - if (h->state == GG_STATE_READING_DATA) {
    - char buf[1024];
    - int res;
    -
    - res = recv(h->fd, buf, sizeof(buf), 0);
    -
    - if (res == -1 && errno != EINTR && errno != EAGAIN) {
    - gg_debug(GG_DEBUG_MISC, "=> http, reading body failed (errno=%d)\n", errno);
    - free(h->body);
    - h->body = NULL;
    - gg_http_error(GG_ERROR_READING);
    - }
    -
    - if (res == -1) {
    - gg_debug(GG_DEBUG_MISC, "=> http, non-critical "
    - "recv error (errno=%d, %s)\n",
    - errno, strerror(errno));
    - return 0;
    - }
    -
    - if (res == 0) {
    - if (h->body_done >= h->body_size) {
    - gg_debug(GG_DEBUG_MISC, "=> http, we're done, closing socket\n");
    - h->state = GG_STATE_PARSING;
    - close(h->fd);
    - h->fd = -1;
    - } else {
    - gg_debug(GG_DEBUG_MISC, "=> http, "
    - "connection closed while reading "
    - "(have %d, need %d)\n",
    - h->body_done, h->body_size);
    - free(h->body);
    - h->body = NULL;
    - gg_http_error(GG_ERROR_READING);
    - }
    -
    - return 0;
    - }
    -
    - gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of body\n", res);
    -
    - if (h->body_done + res > h->body_size) {
    - char *tmp;
    -
    - gg_debug(GG_DEBUG_MISC, "=> http, too much data "
    - "(%d bytes, %d needed), enlarging buffer\n",
    - h->body_done + res, h->body_size);
    -
    - if (!(tmp = realloc(h->body, h->body_done + res + 1))) {
    - gg_debug(GG_DEBUG_MISC, "=> http, not enough "
    - "memory for data (%d needed)\n",
    - h->body_done + res + 1);
    - free(h->body);
    - h->body = NULL;
    - gg_http_error(GG_ERROR_READING);
    - }
    -
    - h->body = tmp;
    - h->body_size = h->body_done + res;
    - }
    -
    - h->body[h->body_done + res] = 0;
    - memcpy(h->body + h->body_done, buf, res);
    - h->body_done += res;
    -
    - gg_debug(GG_DEBUG_MISC, "=> body_done=%d, body_size=%d\n", h->body_done, h->body_size);
    -
    - return 0;
    - }
    -
    - if (h->fd != -1)
    - close(h->fd);
    -
    - h->fd = -1;
    - h->state = GG_STATE_ERROR;
    - h->error = 0;
    -
    - return -1;
    -}
    -
    -/**
    - * Kończy asynchroniczne połączenie HTTP.
    - *
    - * Po zatrzymaniu należy zwolnić zasoby funkcją \c gg_http_free().
    - *
    - * \param h Struktura połączenia
    - *
    - * \ingroup http
    - */
    -void gg_http_stop(struct gg_http *h)
    -{
    - if (!h)
    - return;
    -
    - if (h->state == GG_STATE_ERROR || h->state == GG_STATE_DONE)
    - return;
    -
    - h->resolver_cleanup(&h->resolver, 1);
    -
    - if (h->fd != -1) {
    - close(h->fd);
    - h->fd = -1;
    - }
    -}
    -
    -/**
    - * \internal Zwalnia pola struktury \c gg_http.
    - *
    - * Funkcja zwalnia same pola, nie zwalnia struktury.
    - *
    - * \param h Struktura połączenia
    - */
    -void gg_http_free_fields(struct gg_http *h)
    -{
    - if (h == NULL)
    - return;
    -
    - free(h->body);
    - h->body = NULL;
    -
    - free(h->query);
    - h->query = NULL;
    -
    - free(h->header);
    - h->header = NULL;
    -}
    -
    -/**
    - * Zwalnia zasoby po połączeniu HTTP.
    - *
    - * Jeśli połączenie nie zostało jeszcze zakończone, jest przerywane.
    - *
    - * \param h Struktura połączenia
    - *
    - * \ingroup http
    - */
    -void gg_http_free(struct gg_http *h)
    -{
    - if (h == NULL)
    - return;
    -
    - gg_http_stop(h);
    - gg_http_free_fields(h);
    - free(h);
    -}
    -
    -/*
    - * Local variables:
    - * c-indentation-style: k&r
    - * c-basic-offset: 8
    - * indent-tabs-mode: notnil
    - * End:
    - *
    - * vim: shiftwidth=8:
    - */
    --- a/libpurple/protocols/gg/lib/internal.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,266 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -#ifndef LIBGADU_INTERNAL_H
    -#define LIBGADU_INTERNAL_H
    -
    -#include "libgadu.h"
    -
    -#define GG_DEFAULT_CLIENT_VERSION_100 "10.1.0.11070"
    -#define GG_DEFAULT_CLIENT_VERSION_110 "11.3.45.10771"
    -
    -#ifdef _WIN32
    -# ifdef __COVERITY__
    -# define GG_SIZE_FMT "lu"
    -# define _GG_INT64_MODIFIER "ll"
    -# undef PRIu64
    -# undef PRIx64
    -# undef PRId64
    -# else
    -# define GG_SIZE_FMT "Iu"
    -# define _GG_INT64_MODIFIER "I64"
    -# endif
    -#elif defined(_LP64)
    -# define GG_SIZE_FMT "zu"
    -# define _GG_INT64_MODIFIER "l"
    -#else
    -# define GG_SIZE_FMT "zu"
    -# define _GG_INT64_MODIFIER "ll"
    -#endif
    -
    -#ifndef PRIu64
    -# define PRIu64 _GG_INT64_MODIFIER "u"
    -#endif
    -#ifndef PRIx64
    -# define PRIx64 _GG_INT64_MODIFIER "x"
    -#endif
    -#ifndef PRId64
    -# define PRId64 _GG_INT64_MODIFIER "d"
    -#endif
    -
    -#define GG_LOGIN_PARAMS_HAS_FIELD(glp, member) \
    - (offsetof(struct gg_login_params, member) < (glp)->struct_size || \
    - offsetof(struct gg_login_params, member) <= offsetof(struct gg_login_params, struct_size))
    -
    -#ifdef __GNUC__
    -# define GG_UNUSED __attribute__ ((unused))
    -# define GG_NORETURN __attribute__ ((noreturn))
    -# define GG_CDECL __attribute__ ((__cdecl__))
    -#else
    -# define GG_UNUSED
    -# define GG_NORETURN
    -# define GG_CDECL
    -#endif
    -
    -#define GG_STATIC_ASSERT(condition, message) \
    - { typedef char static_assertion_failed_ ## message \
    - [(condition) ? 1 : -1]; static_assertion_failed_ ## message dummy; \
    - (void)dummy; }
    -
    -#define GG_IMGOUT_WAITING_MAX 4
    -
    -struct gg_dcc7_relay {
    - uint32_t addr;
    - uint16_t port;
    - uint8_t family;
    -};
    -
    -typedef struct _gg_chat_list gg_chat_list_t;
    -struct _gg_chat_list {
    - uint64_t id;
    - uint32_t version;
    - uint32_t participants_count;
    - uin_t *participants;
    -
    - gg_chat_list_t *next;
    -};
    -
    -typedef struct _gg_msg_list gg_msg_list_t;
    -struct _gg_msg_list {
    - int seq;
    - uin_t *recipients;
    - size_t recipients_count;
    -
    - gg_msg_list_t *next;
    -};
    -
    -typedef struct _gg_eventqueue gg_eventqueue_t;
    -struct _gg_eventqueue {
    - struct gg_event *event;
    -
    - gg_eventqueue_t *next;
    -};
    -
    -typedef struct _gg_imgout_queue_t gg_imgout_queue_t;
    -struct _gg_imgout_queue_t {
    - struct gg_send_msg msg_hdr;
    - char buf[1910];
    - size_t buf_len;
    -
    - gg_imgout_queue_t *next;
    -};
    -
    -struct gg_session_private {
    - gg_compat_t compatibility;
    -
    - gg_chat_list_t *chat_list;
    - gg_msg_list_t *sent_messages;
    -
    - gg_eventqueue_t *event_queue;
    - int check_after_queue;
    - int fd_after_queue;
    -
    - gg_imgout_queue_t *imgout_queue;
    - int imgout_waiting_ack;
    -
    - gg_socket_manager_type_t socket_manager_type;
    - gg_socket_manager_t socket_manager;
    - void *socket_handle;
    - int socket_next_state;
    - int socket_is_external;
    - enum gg_failure_t socket_failure;
    -
    - int time_diff;
    -
    - int dummyfds_created;
    - int dummyfds[2];
    -
    - char **host_white_list;
    -};
    -
    -typedef enum
    -{
    - GG_COMPAT_FEATURE_ACK_EVENT,
    - GG_COMPAT_FEATURE_LEGACY_CONFER
    -} gg_compat_feature_t;
    -
    -typedef struct gg_dcc7_relay gg_dcc7_relay_t;
    -
    -void * gg_new0(size_t size);
    -int gg_required_proto(struct gg_session *gs, int protocol_version);
    -int gg_get_dummy_fd(struct gg_session *sess);
    -
    -int gg_compat_feature_is_enabled(struct gg_session *sess, gg_compat_feature_t feature);
    -
    -int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length);
    -
    -int gg_resolve(int *fd, int *pid, const char *hostname);
    -int gg_resolve_pthread(int *fd, void **resolver, const char *hostname);
    -void gg_resolve_pthread_cleanup(void *resolver, int kill);
    -
    -int gg_login_hash_sha1_2(const char *password, uint32_t seed, uint8_t *result);
    -
    -int gg_chat_update(struct gg_session *sess, uint64_t id, uint32_t version,
    - const uin_t *participants, unsigned int participants_count);
    -gg_chat_list_t *gg_chat_find(struct gg_session *sess, uint64_t id);
    -
    -uin_t gg_str_to_uin(const char *str, int len);
    -
    -uint64_t gg_fix64(uint64_t x);
    -void gg_connection_failure(struct gg_session *gs, struct gg_event *ge,
    - enum gg_failure_t failure);
    -
    -time_t gg_server_time(struct gg_session *gs);
    -
    -int gg_session_init_ssl(struct gg_session *gs);
    -void gg_close(struct gg_session *gs);
    -
    -struct gg_event *gg_eventqueue_add(struct gg_session *sess);
    -
    -void gg_compat_message_ack(struct gg_session *sess, int seq);
    -
    -void gg_image_sendout(struct gg_session *sess);
    -
    -void gg_strarr_free(char **strarr);
    -char ** gg_strarr_dup(char **strarr);
    -
    -#ifdef _WIN32
    -
    -#include <windows.h>
    -
    -typedef struct {
    - void (*fnc)();
    -#ifdef _WIN64
    - uint8_t trap[12];
    - uint8_t original[12];
    -#else
    - uint8_t trap[7];
    - uint8_t original[7];
    -#endif
    -} gg_win32_hook_data_t;
    -
    -#define gg_win32_hook(orig_func, hook_func, data) \
    - gg_win32_hook_f((void (*)())(orig_func), (void (*)())(hook_func), (data))
    -
    -static inline void
    -gg_win32_hook_f(void (*orig_func)(), void (*hook_func)(), gg_win32_hook_data_t *data)
    -{
    - DWORD dPermission;
    - uint8_t trap[] = {
    -#ifdef _WIN64
    - 0x48, 0xb8, 0, 0, 0, 0, 0, 0, 0, 0, /* mov rax, uint64_t */
    - 0xff, 0xe0 /* jmp rax */
    -#else
    - 0xB8, 0, 0, 0, 0, /* mov eax, uint32_t */
    - 0xff, 0xe0 /* jmp eax */
    -#endif
    - };
    -
    -#ifdef _WIN64
    - uint64_t addr = (uint64_t)hook_func;
    - memcpy(&trap[2], &addr, sizeof(addr));
    -#else
    - uint32_t addr = (uint32_t)hook_func;
    - memcpy(&trap[1], &addr, sizeof(addr));
    -#endif
    -
    - VirtualProtect(orig_func, sizeof(trap),
    - PAGE_EXECUTE_READWRITE, &dPermission);
    - if (data != NULL) {
    - data->fnc = orig_func;
    - memcpy(data->trap, trap, sizeof(trap));
    - memcpy(data->original, orig_func, sizeof(trap));
    - }
    - memcpy(orig_func, trap, sizeof(trap));
    - VirtualProtect(orig_func, sizeof(trap),
    - dPermission, &dPermission);
    -}
    -
    -static inline void
    -gg_win32_hook_set_enabled(gg_win32_hook_data_t *data, int enabled)
    -{
    - DWORD dPermission;
    - uint8_t *src;
    -
    - if (enabled)
    - src = data->trap;
    - else
    - src = data->original;
    -
    - VirtualProtect(data->fnc, sizeof(data->trap),
    - PAGE_EXECUTE_READWRITE, &dPermission);
    - memcpy(data->fnc, src, sizeof(data->trap));
    - VirtualProtect(data->fnc, sizeof(data->trap),
    - dPermission, &dPermission);
    -}
    -
    -#endif /* _WIN32 */
    -
    -#endif /* LIBGADU_INTERNAL_H */
    --- a/libpurple/protocols/gg/lib/libgadu.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,3082 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
    - * Robert J. Woźny <speedy@ziew.org>
    - * Arkadiusz Miśkiewicz <arekm@pld-linux.org>
    - * Tomasz Chiliński <chilek@chilan.com>
    - * Adam Wysocki <gophi@ekg.chmurka.net>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file libgadu.c
    - *
    - * \brief Główny moduł biblioteki
    - */
    -
    -#include "strman.h"
    -#include "network.h"
    -#include "fileio.h"
    -
    -#include "libgadu.h"
    -#include "protocol.h"
    -#include "resolver.h"
    -#include "internal.h"
    -#include "encoding.h"
    -#include "debug.h"
    -#include "session.h"
    -#include "message.h"
    -#include "deflate.h"
    -#include "tvbuilder.h"
    -#include "protobuf.h"
    -#include "packets.pb-c.h"
    -
    -#include <errno.h>
    -#include <stdarg.h>
    -#include <stdlib.h>
    -#include <string.h>
    -#include <time.h>
    -#ifdef GG_CONFIG_HAVE_GNUTLS
    -# include <gnutls/gnutls.h>
    -#endif
    -#ifdef GG_CONFIG_HAVE_OPENSSL
    -# include <openssl/err.h>
    -# include <openssl/rand.h>
    -#endif
    -
    -/**
    - * Port gniazda nasłuchującego dla połączeń bezpośrednich.
    - *
    - * \ingroup ip
    - */
    -int gg_dcc_port = 0;
    -
    -/**
    - * Adres IP gniazda nasłuchującego dla połączeń bezpośrednich.
    - *
    - * \ingroup ip
    - */
    -unsigned long gg_dcc_ip = 0;
    -
    -/**
    - * Adres lokalnego interfejsu IP, z którego wywoływane są wszystkie połączenia.
    - *
    - * \ingroup ip
    - */
    -unsigned long gg_local_ip = 0;
    -
    -/**
    - * Flaga włączenia połączeń przez serwer pośredniczący.
    - *
    - * \ingroup proxy
    - */
    -int gg_proxy_enabled = 0;
    -
    -/**
    - * Adres serwera pośredniczącego.
    - *
    - * \ingroup proxy
    - */
    -char *gg_proxy_host = NULL;
    -
    -/**
    - * Port serwera pośredniczącego.
    - *
    - * \ingroup proxy
    - */
    -int gg_proxy_port = 0;
    -
    -/**
    - * Flaga używania serwera pośredniczącego jedynie dla usług HTTP.
    - *
    - * \ingroup proxy
    - */
    -int gg_proxy_http_only = 0;
    -
    -/**
    - * Nazwa użytkownika do autoryzacji serwera pośredniczącego.
    - *
    - * \ingroup proxy
    - */
    -char *gg_proxy_username = NULL;
    -
    -/**
    - * Hasło użytkownika do autoryzacji serwera pośredniczącego.
    - *
    - * \ingroup proxy
    - */
    -char *gg_proxy_password = NULL;
    -
    -#ifndef DOXYGEN
    -
    -#ifndef lint
    -static char rcsid[] GG_UNUSED = "$Id$";
    -#endif
    -
    -#endif /* DOXYGEN */
    -
    -static void gg_compat_message_sent(struct gg_session *sess, int seq, size_t recipients_count, uin_t *recipients);
    -static void gg_compat_message_cleanup(struct gg_session *sess);
    -
    -#ifdef GG_CONFIG_IS_GPL_COMPLIANT
    -/**
    - * Symbol zdefiniowany tylko dla libgadu zgodnego z licencją GPL.
    - *
    - * Zwracana wartość nie jest istotna, a ponadto może się zmienić w przyszłych
    - * wersjach biblioteki. Istotne jest tylko wywołanie tej funkcji w kodzie, który
    - * ma być zgodny z GPL, aby wymusić jej istnienie.
    - *
    - * \return Wartość 1.
    - *
    - * \ingroup version
    - */
    -int gg_is_gpl_compliant(void)
    -{
    - return 1;
    -}
    -#endif
    -
    -/**
    - * Zwraca wersję biblioteki.
    - *
    - * \return Wskaźnik na statyczny bufor z wersją biblioteki.
    - *
    - * \ingroup version
    - */
    -const char *gg_libgadu_version(void)
    -{
    - return GG_LIBGADU_VERSION;
    -}
    -
    -void * gg_new0(size_t size)
    -{
    - void *ptr;
    -
    - ptr = malloc(size);
    - if (ptr == NULL) {
    - gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "//gg_new0(%" GG_SIZE_FMT
    - ") not enough memory\n", size);
    - return NULL;
    - }
    -
    - memset(ptr, 0, size);
    - return ptr;
    -}
    -
    -int
    -gg_required_proto(struct gg_session *gs, int protocol_version)
    -{
    - if (gs->protocol_version >= protocol_version)
    - return 1;
    -
    - gg_debug_session(gs, GG_DEBUG_MISC | GG_DEBUG_ERROR, "// requested "
    - "feature requires protocol %#02x, but %#02x is selected\n",
    - protocol_version, gs->protocol_version);
    - return 0;
    -}
    -
    -int gg_get_dummy_fd(struct gg_session *sess)
    -{
    - struct gg_session_private *p = sess->private_data;
    -
    - if (p->dummyfds_created)
    - return p->dummyfds[0];
    -
    - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, p->dummyfds) == -1) {
    - gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR, "// gg_get_dummy_fd() "
    - "unable to create pipes (errno=%d, %s)\n",
    - errno, strerror(errno));
    - return -1;
    - }
    -
    - p->dummyfds_created = 1;
    - return p->dummyfds[0];
    -}
    -
    -/**
    - * \internal Liczy skrót z hasła i ziarna.
    - *
    - * \param password Hasło
    - * \param seed Ziarno podane przez serwer
    - *
    - * \return Wartość skrótu
    - */
    -unsigned int gg_login_hash(const unsigned char *password, unsigned int seed)
    -{
    - unsigned int x, y, z;
    -
    - y = seed;
    -
    - for (x = 0; *password; password++) {
    - x = (x & 0xffffff00) | *password;
    - y ^= x;
    - y += x;
    - x <<= 8;
    - y ^= x;
    - x <<= 8;
    - y -= x;
    - x <<= 8;
    - y ^= x;
    -
    - z = y & 0x1F;
    - y = (y << z) | (y >> (32 - z));
    - }
    -
    - return y;
    -}
    -
    -/**
    - * \internal Odbiera od serwera dane binarne.
    - *
    - * Funkcja odbiera dane od serwera zajmując się SSL/TLS w razie konieczności.
    - * Obsługuje EINTR, więc użytkownik nie musi się przejmować przerwanymi
    - * wywołaniami systemowymi.
    - *
    - * \param sess Struktura sesji
    - * \param buf Bufor na danymi
    - * \param length Długość bufora
    - *
    - * \return To samo co funkcja systemowa \c read
    - */
    -int gg_read(struct gg_session *sess, char *buf, int length)
    -{
    - struct gg_session_private *p = sess->private_data;
    - int res;
    -
    -#ifdef GG_CONFIG_HAVE_GNUTLS
    - if (sess->ssl != NULL) {
    - for (;;) {
    - res = gnutls_record_recv(GG_SESSION_GNUTLS(sess), buf, length);
    -
    - if (res < 0) {
    - if (res == GNUTLS_E_AGAIN)
    - errno = EAGAIN;
    - else if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED)
    - continue;
    - else
    - errno = EINVAL;
    -
    - return -1;
    - }
    -
    - return res;
    - }
    - }
    -#endif
    -
    -#ifdef GG_CONFIG_HAVE_OPENSSL
    - if (sess->ssl != NULL) {
    - for (;;) {
    - int err;
    -
    - res = SSL_read(sess->ssl, buf, length);
    -
    - if (res < 0) {
    - err = SSL_get_error(sess->ssl, res);
    -
    - if (err == SSL_ERROR_SYSCALL && errno == EINTR)
    - continue;
    -
    - if (err == SSL_ERROR_WANT_READ)
    - errno = EAGAIN;
    - else if (err != SSL_ERROR_SYSCALL)
    - errno = EINVAL;
    -
    - return -1;
    - }
    -
    - return res;
    - }
    - }
    -#endif
    -
    - if (p->socket_handle != NULL) {
    - if (p->socket_manager.read_cb == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "// gg_read() socket_manager.read callback is "
    - "empty\n");
    - errno = EINVAL;
    - return -1;
    - }
    -
    - do {
    - res = p->socket_manager.read_cb(
    - p->socket_manager.cb_data, p->socket_handle,
    - (unsigned char*)buf, length);
    - } while (res < 0 && errno == EINTR);
    -
    - if (res < 0) {
    - if (errno == EAGAIN)
    - return -1;
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "// gg_read() unexpected errno=%d\n", errno);
    - errno = EINVAL;
    - }
    - return res;
    - }
    -
    - for (;;) {
    - res = recv(sess->fd, buf, length, 0);
    -
    - if (res == -1 && errno == EINTR)
    - continue;
    -
    - return res;
    - }
    -}
    -
    -/**
    - * \internal Wysyła do serwera dane binarne.
    - *
    - * Funkcja wysyła dane do serwera zajmując się SSL/TLS w razie konieczności.
    - * Obsługuje EINTR, więc użytkownik nie musi się przejmować przerwanymi
    - * wywołaniami systemowymi.
    - *
    - * \note Funkcja nie zajmuje się buforowaniem wysyłanych danych (patrz
    - * gg_write()).
    - *
    - * \param sess Struktura sesji
    - * \param buf Bufor z danymi
    - * \param length Długość bufora
    - *
    - * \return To samo co funkcja systemowa \c write
    - */
    -static int gg_write_common(struct gg_session *sess, const char *buf, int length)
    -{
    - struct gg_session_private *p = sess->private_data;
    - int res;
    -
    -#ifdef GG_CONFIG_HAVE_GNUTLS
    - if (sess->ssl != NULL) {
    - for (;;) {
    - res = gnutls_record_send(GG_SESSION_GNUTLS(sess), buf, length);
    -
    - if (res < 0) {
    - if (!gnutls_error_is_fatal(res) || res == GNUTLS_E_INTERRUPTED)
    - continue;
    -
    - if (res == GNUTLS_E_AGAIN)
    - errno = EAGAIN;
    - else
    - errno = EINVAL;
    -
    - return -1;
    - }
    -
    - return res;
    - }
    - }
    -#endif
    -
    -#ifdef GG_CONFIG_HAVE_OPENSSL
    - if (sess->ssl != NULL) {
    - for (;;) {
    - int err;
    -
    - res = SSL_write(sess->ssl, buf, length);
    -
    - if (res < 0) {
    - err = SSL_get_error(sess->ssl, res);
    -
    - if (err == SSL_ERROR_SYSCALL && errno == EINTR)
    - continue;
    -
    - if (err == SSL_ERROR_WANT_WRITE)
    - errno = EAGAIN;
    - else if (err != SSL_ERROR_SYSCALL)
    - errno = EINVAL;
    -
    - return -1;
    - }
    -
    - return res;
    - }
    - }
    -#endif
    -
    - if (p->socket_handle != NULL) {
    - if (p->socket_manager.write_cb == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "// gg_write_common() socket_manager.write "
    - "callback is empty\n");
    - errno = EINVAL;
    - return -1;
    - }
    -
    - do {
    - res = p->socket_manager.write_cb(
    - p->socket_manager.cb_data, p->socket_handle,
    - (const unsigned char*)buf, length);
    - } while (res < 0 && errno == EINTR);
    -
    - if (res < 0) {
    - if (errno == EAGAIN)
    - return -1;
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "// gg_read() unexpected errno=%d\n", errno);
    - errno = EINVAL;
    - }
    -
    - return res;
    - }
    -
    - for (;;) {
    - res = send(sess->fd, buf, length, 0);
    -
    - if (res == -1 && errno == EINTR)
    - continue;
    -
    - return res;
    - }
    -}
    -
    -/**
    - * \internal Wysyła do serwera dane binarne.
    - *
    - * Funkcja wysyła dane do serwera zajmując się TLS w razie konieczności.
    - *
    - * \param sess Struktura sesji
    - * \param buf Bufor z danymi
    - * \param length Długość bufora
    - *
    - * \return To samo co funkcja systemowa \c write
    - */
    -int gg_write(struct gg_session *sess, const char *buf, int length)
    -{
    - int res = 0;
    -
    - if (!sess->async) {
    - int written = 0;
    -
    - while (written < length) {
    - res = gg_write_common(sess, buf + written, length - written);
    -
    - if (res == -1)
    - return -1;
    -
    - written += res;
    - res = written;
    - }
    - } else {
    - if (sess->send_buf == NULL) {
    - res = gg_write_common(sess, buf, length);
    -
    - if (res == -1 && errno == EAGAIN)
    - res = 0;
    - if (res == -1)
    - return -1;
    - }
    -
    - if (res < length) {
    - char *tmp;
    -
    - if (!(tmp = realloc(sess->send_buf, sess->send_left + length - res))) {
    - errno = ENOMEM;
    - return -1;
    - }
    -
    - sess->send_buf = tmp;
    -
    - memcpy(sess->send_buf + sess->send_left, buf + res, length - res);
    -
    - sess->send_left += length - res;
    - }
    - }
    -
    - return res;
    -}
    -
    -void gg_close(struct gg_session *sess)
    -{
    - struct gg_session_private *p = sess->private_data;
    - int errno_copy;
    -
    - errno_copy = errno;
    -
    - if (!p->socket_is_external) {
    - if (sess->fd != -1)
    - close(sess->fd);
    - } else {
    - assert(p->socket_manager_type !=
    - GG_SOCKET_MANAGER_TYPE_INTERNAL);
    - if (p->socket_handle != NULL) {
    - p->socket_manager.close_cb(p->socket_manager.cb_data,
    - p->socket_handle);
    - }
    - p->socket_is_external = 0;
    - }
    - sess->fd = -1;
    - p->socket_handle = NULL;
    -
    - while (p->event_queue) {
    - gg_eventqueue_t *next = p->event_queue->next;
    - gg_event_free(p->event_queue->event);
    - free(p->event_queue);
    - p->event_queue = next;
    - }
    -
    - while (p->imgout_queue) {
    - gg_imgout_queue_t *next = p->imgout_queue->next;
    - free(p->imgout_queue);
    - p->imgout_queue = next;
    - }
    -
    - if (p->dummyfds_created) {
    - close(p->dummyfds[0]);
    - close(p->dummyfds[1]);
    - p->dummyfds_created = 0;
    - }
    -
    - gg_compat_message_cleanup(sess);
    -
    - errno = errno_copy;
    -}
    -
    -/**
    - * \internal Odbiera pakiet od serwera.
    - *
    - * Funkcja odczytuje nagłówek pakietu, a następnie jego zawartość i zwraca
    - * w zaalokowanym buforze.
    - *
    - * Przy połączeniach asynchronicznych, funkcja może nie być w stanie
    - * skompletować całego pakietu -- w takim przypadku zwróci \c NULL, a kodem błędu
    - * będzie \c EAGAIN.
    - *
    - * \param sess Struktura sesji
    - *
    - * \return Wskaźnik do zaalokowanego bufora
    - */
    -void *gg_recv_packet(struct gg_session *sess)
    -{
    - struct gg_header *gh;
    - char *packet;
    - int res;
    - size_t len;
    - uint32_t ghlen = 0;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
    -
    - if (sess == NULL) {
    - errno = EFAULT;
    - return NULL;
    - }
    -
    - for (;;) {
    - if (sess->recv_buf == NULL && sess->recv_done == 0) {
    - sess->recv_buf = malloc(sizeof(struct gg_header) + 1);
    -
    - if (sess->recv_buf == NULL) {
    - gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() out of memory\n");
    - return NULL;
    - }
    - }
    -
    - gh = (struct gg_header*) sess->recv_buf;
    -
    - if ((size_t) sess->recv_done < sizeof(struct gg_header)) {
    - len = sizeof(struct gg_header) - sess->recv_done;
    - gg_debug_session(sess, GG_DEBUG_NET,
    - "// gg_recv_packet() header: %d done, "
    - "%" GG_SIZE_FMT " to go\n",
    - sess->recv_done, len);
    - } else {
    - ghlen = gh ? gg_fix32(gh->length) : 0;
    -
    - if (ghlen > 65535) {
    - gg_debug_session(sess, GG_DEBUG_ERROR,
    - "// gg_recv_packet() invalid packet "
    - "length (%d)\n", ghlen);
    - errno = ERANGE;
    - goto fail;
    - }
    -
    - if ((size_t) sess->recv_done >= sizeof(struct gg_header) + ghlen) {
    - gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() and that's it\n");
    - break;
    - }
    -
    - len = sizeof(struct gg_header) + ghlen - sess->recv_done;
    -
    - gg_debug_session(sess, GG_DEBUG_NET,
    - "// gg_recv_packet() payload: %d done, "
    - "%u length, %" GG_SIZE_FMT " to go\n",
    - sess->recv_done, ghlen, len);
    - }
    -
    - res = gg_read(sess, sess->recv_buf + sess->recv_done, len);
    -
    - if (res == 0) {
    - errno = ECONNRESET;
    - gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() connection broken\n");
    - goto fail;
    - }
    -
    - if (res == -1 && errno == EAGAIN) {
    - gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() resource temporarily unavailable\n");
    - goto eagain;
    - }
    -
    - if (res == -1) {
    - gg_debug_session(sess, GG_DEBUG_ERROR,
    - "// gg_recv_packet() read failed: errno=%d, "
    - "%s\n", errno, strerror(errno));
    - goto fail;
    - }
    -
    - gg_debug_session(sess, GG_DEBUG_NET, "// gg_recv_packet() read %d bytes\n", res);
    -
    - if (sess->recv_done + res == sizeof(struct gg_header)) {
    - char *tmp;
    - ghlen = gh ? gg_fix32(gh->length) : 0;
    -
    - gg_debug_session(sess, GG_DEBUG_NET,
    - "// gg_recv_packet() header complete, "
    - "payload %d bytes\n", ghlen);
    -
    - if (ghlen == 0)
    - break;
    -
    - if (ghlen > 65535) {
    - gg_debug_session(sess, GG_DEBUG_ERROR,
    - "// gg_recv_packet() invalid packet "
    - "length (%d)\n", ghlen);
    - errno = ERANGE;
    - goto fail;
    - }
    -
    - tmp = realloc(sess->recv_buf, sizeof(struct gg_header) + ghlen + 1);
    -
    - if (tmp == NULL) {
    - gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_recv_packet() out of memory\n");
    - goto fail;
    - }
    -
    - sess->recv_buf = tmp;
    - }
    -
    - sess->recv_done += res;
    - }
    -
    - packet = sess->recv_buf;
    - sess->recv_buf = NULL;
    - sess->recv_done = 0;
    -
    - if (gh == NULL)
    - goto fail;
    -
    - /* Czasami zakładamy, że teksty w pakietach są zakończone zerem */
    - packet[sizeof(struct gg_header) + ghlen] = 0;
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet(type=0x%.2x, "
    - "length=%d)\n", gg_fix32(gh->type), ghlen);
    - gg_debug_dump(sess, GG_DEBUG_DUMP, packet, sizeof(struct gg_header) + ghlen);
    -
    - gh->type = gg_fix32(gh->type);
    - gh->length = ghlen;
    -
    - return packet;
    -
    -fail:
    - free(sess->recv_buf);
    - sess->recv_buf = NULL;
    - sess->recv_done = 0;
    -
    -eagain:
    - return NULL;
    -}
    -
    -/**
    - * \internal Wysyła pakiet do serwera.
    - *
    - * Funkcja konstruuje pakiet do wysłania z dowolnej liczby fragmentów. Jeśli
    - * rozmiar pakietu jest za duży, by móc go wysłać za jednym razem, pozostała
    - * część zostanie zakolejkowana i wysłana, gdy będzie to możliwe.
    - *
    - * \param sess Struktura sesji
    - * \param type Rodzaj pakietu
    - * \param ... Lista kolejnych części pakietu (wskaźnik na bufor i długość
    - * typu \c int) zakończona \c NULL
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -int gg_send_packet(struct gg_session *sess, int type, ...)
    -{
    - struct gg_header *h;
    - char *tmp;
    - unsigned int tmp_length;
    - void *payload;
    - unsigned int payload_length;
    - va_list ap;
    - int res;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...);\n", sess, type);
    -
    - tmp_length = sizeof(struct gg_header);
    -
    - if (!(tmp = malloc(tmp_length))) {
    - gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() not enough memory for packet header\n");
    - return -1;
    - }
    -
    - va_start(ap, type);
    -
    - payload = va_arg(ap, void *);
    -
    - while (payload) {
    - char *tmp2;
    -
    - payload_length = va_arg(ap, unsigned int);
    -
    - if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) {
    - gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() not enough memory for payload\n");
    - free(tmp);
    - va_end(ap);
    - return -1;
    - }
    -
    - tmp = tmp2;
    -
    - memcpy(tmp + tmp_length, payload, payload_length);
    - tmp_length += payload_length;
    -
    - payload = va_arg(ap, void *);
    - }
    -
    - va_end(ap);
    -
    - h = (struct gg_header*) tmp;
    - h->type = gg_fix32(type);
    - h->length = gg_fix32(tmp_length - sizeof(struct gg_header));
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet(type=0x%.2x, "
    - "length=%d)\n", gg_fix32(h->type), gg_fix32(h->length));
    - gg_debug_dump(sess, GG_DEBUG_DUMP, tmp, tmp_length);
    -
    - res = gg_write(sess, tmp, tmp_length);
    -
    - free(tmp);
    -
    - if (res == -1) {
    - gg_debug_session(sess, GG_DEBUG_ERROR, "// gg_send_packet() "
    - "write() failed. res = %d, errno = %d (%s)\n",
    - res, errno, strerror(errno));
    - return -1;
    - }
    -
    - if (sess->async) {
    - gg_debug_session(sess, GG_DEBUG_NET, "// gg_send_packet() "
    - "partial write(), %d sent, %d left, %d total left\n",
    - res, tmp_length - res, sess->send_left);
    - }
    -
    - if (sess->send_buf)
    - sess->check |= GG_CHECK_WRITE;
    -
    - return 0;
    -}
    -
    -/**
    - * \internal Funkcja zwrotna sesji.
    - *
    - * Pole \c callback struktury \c gg_session zawiera wskaźnik do tej funkcji.
    - * Wywołuje ona \c gg_watch_fd i zachowuje wynik w polu \c event.
    - *
    - * \note Korzystanie z tej funkcjonalności nie jest już zalecane.
    - *
    - * \param sess Struktura sesji
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -static int gg_session_callback(struct gg_session *sess)
    -{
    - if (!sess) {
    - errno = EFAULT;
    - return -1;
    - }
    -
    - return ((sess->event = gg_watch_fd(sess)) != NULL) ? 0 : -1;
    -}
    -
    -/**
    - * Łączy się z serwerem Gadu-Gadu.
    - *
    - * Przy połączeniu synchronicznym funkcja zakończy działanie po nawiązaniu
    - * połączenia lub gdy wystąpi błąd. Po udanym połączeniu należy wywoływać
    - * funkcję \c gg_watch_fd(), która odbiera informacje od serwera i zwraca
    - * informacje o zdarzeniach.
    - *
    - * Przy połączeniu asynchronicznym funkcja rozpocznie procedurę połączenia
    - * i zwróci zaalokowaną strukturę. Pole \c fd struktury \c gg_session zawiera
    - * deskryptor, który należy obserwować funkcją \c select, \c poll lub za
    - * pomocą mechanizmów użytej pętli zdarzeń (Glib, Qt itp.). Pole \c check
    - * jest maską bitową mówiącą, czy biblioteka chce być informowana o możliwości
    - * odczytu danych (\c GG_CHECK_READ) czy zapisu danych (\c GG_CHECK_WRITE).
    - * Po zaobserwowaniu zmian na deskryptorze należy wywołać funkcję
    - * \c gg_watch_fd(). Podczas korzystania z połączeń asynchronicznych, w trakcie
    - * połączenia może zostać stworzony dodatkowy proces rozwiązujący nazwę
    - * serwera -- z tego powodu program musi poprawnie obsłużyć sygnał SIGCHLD.
    - *
    - * \note Po nawiązaniu połączenia z serwerem należy wysłać listę kontaktów
    - * za pomocą funkcji \c gg_notify() lub \c gg_notify_ex().
    - *
    - * \note Funkcja zwróci błąd ENOSYS jeśli połączenie SSL było wymagane, ale
    - * obsługa SSL nie jest wkompilowana.
    - *
    - * \param p Struktura opisująca parametry połączenia. Wymagane pola: uin,
    - * password, async.
    - *
    - * \return Wskaźnik do zaalokowanej struktury sesji \c gg_session lub NULL
    - * w przypadku błędu.
    - *
    - * \ingroup login
    - */
    -struct gg_session *gg_login(const struct gg_login_params *p)
    -{
    - struct gg_session *sess = NULL;
    - struct gg_session_private *sess_private = NULL;
    -
    - if (p == NULL) {
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p);\n", p);
    - errno = EFAULT;
    - return NULL;
    - }
    -
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p: [uin=%u, async=%d, ...]);\n", p, p->uin, p->async);
    -
    - sess = malloc(sizeof(struct gg_session));
    -
    - if (sess == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session data\n");
    - goto fail;
    - }
    -
    - memset(sess, 0, sizeof(struct gg_session));
    - sess->fd = -1;
    -
    - sess_private = malloc(sizeof(struct gg_session_private));
    -
    - if (sess_private == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session private data\n");
    - goto fail;
    - }
    -
    - memset(sess_private, 0, sizeof(struct gg_session_private));
    - sess->private_data = sess_private;
    -
    - if (p->password == NULL || p->uin == 0) {
    - gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. uin and password needed\n");
    - errno = EFAULT;
    - goto fail;
    - }
    -
    - if (!(sess->password = strdup(p->password))) {
    - gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for password\n");
    - goto fail;
    - }
    -
    - if (p->hash_type < 0 || p->hash_type > GG_LOGIN_HASH_SHA1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unknown hash type (%d)\n", p->hash_type);
    - errno = EFAULT;
    - goto fail;
    - }
    -
    - sess->uin = p->uin;
    - sess->state = GG_STATE_RESOLVING;
    - sess->check = GG_CHECK_READ;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    - sess->async = p->async;
    - sess->type = GG_SESSION_GG;
    - sess->initial_status = p->status;
    - sess->callback = gg_session_callback;
    - sess->destroy = gg_free_session;
    - sess->port = p->server_port;
    - sess->server_addr = p->server_addr;
    - sess->external_port = p->external_port;
    - sess->external_addr = p->external_addr;
    - sess->client_addr = p->client_addr;
    - sess->client_port = p->client_port;
    -
    - if (GG_LOGIN_PARAMS_HAS_FIELD(p, compatibility))
    - sess_private->compatibility = p->compatibility;
    -
    - if (GG_LOGIN_PARAMS_HAS_FIELD(p, connect_host) && p->connect_host != NULL) {
    - int port = 0;
    - char *colon;
    -
    - sess->connect_host = strdup(p->connect_host);
    - if (sess->connect_host == NULL)
    - goto fail;
    -
    - colon = strchr(sess->connect_host, ':');
    - if (colon != NULL) {
    - colon[0] = '\0';
    - port = atoi(colon + 1);
    - }
    - if (port > 0)
    - sess->port = port;
    - }
    -
    - if (GG_LOGIN_PARAMS_HAS_FIELD(p, socket_manager_type) &&
    - GG_LOGIN_PARAMS_HAS_FIELD(p, socket_manager) &&
    - p->socket_manager_type != GG_SOCKET_MANAGER_TYPE_INTERNAL)
    - {
    - if ((unsigned int)p->socket_manager_type >
    - GG_SOCKET_MANAGER_TYPE_TLS)
    - {
    - gg_debug(GG_DEBUG_MISC | GG_DEBUG_ERROR, "// gg_login()"
    - " invalid arguments. unknown socket manager "
    - "type (%d)\n", p->socket_manager_type);
    - errno = EFAULT;
    - goto fail;
    - } else {
    - sess_private->socket_manager_type =
    - p->socket_manager_type;
    - memcpy(&sess_private->socket_manager,
    - &p->socket_manager,
    - sizeof(gg_socket_manager_t));
    - }
    - } else {
    - sess_private->socket_manager_type =
    - GG_SOCKET_MANAGER_TYPE_INTERNAL;
    - }
    -
    - if (GG_LOGIN_PARAMS_HAS_FIELD(p, host_white_list) &&
    - p->host_white_list != NULL)
    - {
    - sess_private->host_white_list =
    - gg_strarr_dup(p->host_white_list);
    - if (sess_private->host_white_list == NULL)
    - goto fail;
    - }
    -
    - if (p->protocol_features == 0) {
    - sess->protocol_features = GG_FEATURE_MSG80 |
    - GG_FEATURE_STATUS80 | GG_FEATURE_DND_FFC |
    - GG_FEATURE_IMAGE_DESCR | GG_FEATURE_UNKNOWN_100 |
    - GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK |
    - GG_FEATURE_TYPING_NOTIFICATION;
    - } else {
    - sess->protocol_features = (p->protocol_features & ~(GG_FEATURE_STATUS77 | GG_FEATURE_MSG77));
    -
    - if (!(p->protocol_features & GG_FEATURE_STATUS77))
    - sess->protocol_features |= GG_FEATURE_STATUS80;
    -
    - if (!(p->protocol_features & GG_FEATURE_MSG77))
    - sess->protocol_features |= GG_FEATURE_MSG80;
    - }
    -
    - if (!(sess->status_flags = p->status_flags))
    - sess->status_flags = GG_STATUS_FLAG_UNKNOWN | GG_STATUS_FLAG_SPAM;
    -
    - if (!p->protocol_version)
    - sess->protocol_version = GG_DEFAULT_PROTOCOL_VERSION;
    - else if (p->protocol_version < 0x2e) {
    - gg_debug(GG_DEBUG_MISC, "// gg_login() libgadu no longer support protocol < 0x2e\n");
    - sess->protocol_version = 0x2e;
    - } else
    - sess->protocol_version = p->protocol_version;
    -
    - if (p->client_version && strcmp(p->client_version, "-") != 0)
    - sess->client_version = strdup(p->client_version);
    - sess->last_sysmsg = p->last_sysmsg;
    - sess->image_size = p->image_size;
    - sess->pid = -1;
    - sess->encoding = p->encoding;
    -
    - if (gg_session_set_resolver(sess, p->resolver) == -1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. "
    - "unsupported resolver type (%d)\n", p->resolver);
    - errno = EFAULT;
    - goto fail;
    - }
    -
    - if (p->status_descr) {
    - sess->initial_descr = gg_encoding_convert(p->status_descr, p->encoding, GG_ENCODING_UTF8, -1, -1);
    -
    - if (!sess->initial_descr) {
    - gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n");
    - goto fail;
    - }
    -
    - /* XXX pamiętać, żeby nie ciąć w środku znaku utf-8 */
    -
    - if (strlen(sess->initial_descr) > GG_STATUS_DESCR_MAXSIZE)
    - sess->initial_descr[GG_STATUS_DESCR_MAXSIZE] = 0;
    - }
    -
    - if (p->tls != GG_SSL_DISABLED) {
    -#if !defined(GG_CONFIG_HAVE_GNUTLS) && !defined(GG_CONFIG_HAVE_OPENSSL)
    - gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n");
    -
    - if (p->tls == GG_SSL_REQUIRED) {
    - errno = ENOSYS;
    - goto fail;
    - }
    -#else
    - sess->ssl_flag = p->tls;
    -#endif
    - }
    -
    - if (p->hash_type)
    - sess->hash_type = p->hash_type;
    - else
    - sess->hash_type = GG_LOGIN_HASH_SHA1;
    -
    - if (sess->server_addr == 0 && sess->connect_host == NULL) {
    - if (gg_proxy_enabled) {
    - sess->resolver_host = gg_proxy_host;
    - sess->proxy_port = gg_proxy_port;
    - sess->state = (sess->async) ?
    - GG_STATE_RESOLVE_PROXY_HUB_ASYNC :
    - GG_STATE_RESOLVE_PROXY_HUB_SYNC;
    - } else {
    - sess->resolver_host = GG_APPMSG_HOST;
    - sess->proxy_port = 0;
    - sess->state = (sess->async) ? GG_STATE_RESOLVE_HUB_ASYNC : GG_STATE_RESOLVE_HUB_SYNC;
    - }
    - } else {
    - if (sess->connect_host != NULL)
    - sess->server_addr = 0;
    - else {
    - /* XXX inet_ntoa i wielowątkowość */
    - sess->connect_host = strdup(inet_ntoa(*(struct in_addr*) &sess->server_addr));
    - if (sess->connect_host == NULL)
    - goto fail;
    - }
    - sess->connect_index = 0;
    -
    - if (gg_proxy_enabled) {
    - sess->resolver_host = gg_proxy_host;
    - sess->proxy_port = gg_proxy_port;
    - if (sess->port == 0)
    - sess->connect_port[0] = GG_HTTPS_PORT;
    - else
    - sess->connect_port[0] = sess->port;
    - sess->connect_port[1] = 0;
    - sess->state = (sess->async) ? GG_STATE_RESOLVE_PROXY_GG_ASYNC : GG_STATE_RESOLVE_PROXY_GG_SYNC;
    - } else {
    - sess->resolver_host = sess->connect_host;
    - if (sess->port == 0) {
    - if (sess->ssl_flag == GG_SSL_DISABLED) {
    - sess->connect_port[0] = GG_DEFAULT_PORT;
    - sess->connect_port[1] = GG_HTTPS_PORT;
    - } else {
    - sess->connect_port[0] = GG_HTTPS_PORT;
    - sess->connect_port[1] = 0;
    - }
    - } else {
    - sess->connect_port[0] = sess->port;
    - sess->connect_port[1] = 0;
    - }
    - sess->state = (sess->async) ? GG_STATE_RESOLVE_GG_ASYNC : GG_STATE_RESOLVE_GG_SYNC;
    - }
    - }
    -
    - /* XXX inaczej gg_watch_fd() wyjdzie z timeoutem */
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    -
    - if (!sess->async) {
    - while (!GG_SESSION_IS_CONNECTED(sess)) {
    - struct gg_event *ge;
    -
    - ge = gg_watch_fd(sess);
    -
    - if (ge == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_session_connect() critical error in gg_watch_fd()\n");
    - goto fail;
    - }
    -
    - if (ge->type == GG_EVENT_CONN_FAILED) {
    - errno = EACCES;
    - gg_debug(GG_DEBUG_MISC, "// gg_session_connect() could not login\n");
    - gg_event_free(ge);
    - goto fail;
    - }
    -
    - gg_event_free(ge);
    - }
    - } else {
    - struct gg_event *ge;
    -
    - ge = gg_watch_fd(sess);
    -
    - if (ge == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_session_connect() critical error in gg_watch_fd()\n");
    - goto fail;
    - }
    -
    - gg_event_free(ge);
    - }
    -
    - return sess;
    -
    -fail:
    - gg_free_session(sess);
    -
    - return NULL;
    -}
    -
    -/**
    - * Wysyła do serwera pakiet utrzymania połączenia.
    - *
    - * Klient powinien regularnie co minutę wysyłać pakiet utrzymania połączenia,
    - * inaczej serwer uzna, że klient stracił łączność z siecią i zerwie
    - * połączenie.
    - *
    - * \param sess Struktura sesji
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup login
    - */
    -int gg_ping(struct gg_session *sess)
    -{
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess);
    -
    - if (!sess) {
    - errno = EFAULT;
    - return -1;
    - }
    -
    - if (sess->state != GG_STATE_CONNECTED) {
    - errno = ENOTCONN;
    - return -1;
    - }
    -
    - return gg_send_packet(sess, GG_PING, NULL);
    -}
    -
    -/**
    - * Kończy połączenie z serwerem.
    - *
    - * Funkcja nie zwalnia zasobów, więc po jej wywołaniu należy użyć
    - * \c gg_free_session(). Jeśli chce się ustawić opis niedostępności, należy
    - * wcześniej wywołać funkcję \c gg_change_status_descr() lub
    - * \c gg_change_status_descr_time().
    - *
    - * \note Jeśli w buforze nadawczym połączenia z serwerem znajdują się jeszcze
    - * dane (np. z powodu strat pakietów na łączu), prawdopodobnie zostaną one
    - * utracone przy zrywaniu połączenia. Aby mieć pewność, że opis statusu
    - * zostanie zachowany, należy ustawić stan \c GG_STATUS_NOT_AVAIL_DESCR
    - * za pomocą funkcji \c gg_change_status_descr() i poczekać na zdarzenie
    - * \c GG_EVENT_DISCONNECT_ACK.
    - *
    - * \param sess Struktura sesji
    - *
    - * \ingroup login
    - */
    -void gg_logoff(struct gg_session *sess)
    -{
    - if (!sess)
    - return;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess);
    -
    -#ifdef GG_CONFIG_HAVE_GNUTLS
    - if (sess->ssl != NULL)
    - gnutls_bye(GG_SESSION_GNUTLS(sess), GNUTLS_SHUT_RDWR);
    -#endif
    -
    -#ifdef GG_CONFIG_HAVE_OPENSSL
    - if (sess->ssl != NULL)
    - SSL_shutdown(sess->ssl);
    -#endif
    -
    - sess->resolver_cleanup(&sess->resolver, 1);
    -
    - gg_close(sess);
    -
    - if (sess->send_buf) {
    - free(sess->send_buf);
    - sess->send_buf = NULL;
    - sess->send_left = 0;
    - }
    -}
    -
    -/**
    - * Zwalnia zasoby używane przez połączenie z serwerem. Funkcję należy wywołać
    - * po zamknięciu połączenia z serwerem, by nie doprowadzić do wycieku zasobów
    - * systemowych.
    - *
    - * \param sess Struktura sesji
    - *
    - * \ingroup login
    - */
    -void gg_free_session(struct gg_session *sess)
    -{
    - struct gg_dcc7 *dcc;
    - gg_chat_list_t *chat;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_free_session(%p);\n", sess);
    -
    - if (sess == NULL)
    - return;
    -
    - /* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */
    -
    - free(sess->resolver_result);
    - free(sess->connect_host);
    - free(sess->password);
    - free(sess->initial_descr);
    - free(sess->client_version);
    - free(sess->header_buf);
    - free(sess->recv_buf);
    -
    -#ifdef GG_CONFIG_HAVE_GNUTLS
    - if (sess->ssl != NULL) {
    - gg_session_gnutls_t *tmp;
    -
    - tmp = (gg_session_gnutls_t*) sess->ssl;
    - gnutls_deinit(tmp->session);
    - gnutls_certificate_free_credentials(tmp->xcred);
    - gnutls_global_deinit();
    - free(sess->ssl);
    - }
    -#endif
    -
    -#ifdef GG_CONFIG_HAVE_OPENSSL
    - if (sess->ssl)
    - SSL_free(sess->ssl);
    -
    - if (sess->ssl_ctx)
    - SSL_CTX_free(sess->ssl_ctx);
    -#endif
    -
    - if (sess->resolver_cleanup != NULL)
    - sess->resolver_cleanup(&sess->resolver, 1);
    -
    - gg_close(sess);
    -
    - while (sess->images) {
    - struct gg_image_queue *next = sess->images->next;
    -
    - gg_image_queue_remove(sess, sess->images, 1);
    -
    - /* a fix for false-positive NULL-dereference */
    - sess->images = next;
    - }
    -
    - free(sess->send_buf);
    -
    - for (dcc = sess->dcc7_list; dcc; dcc = dcc->next)
    - dcc->sess = NULL;
    -
    - chat = sess->private_data->chat_list;
    - while (chat != NULL) {
    - gg_chat_list_t *next = chat->next;
    - free(chat->participants);
    - free(chat);
    - chat = next;
    - }
    -
    - gg_strarr_free(sess->private_data->host_white_list);
    -
    - free(sess->private_data);
    -
    - free(sess);
    -}
    -
    -/**
    - * Zmienia status użytkownika.
    - *
    - * \param sess Struktura sesji
    - * \param status Nowy status użytkownika
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup status
    - */
    -int gg_change_status(struct gg_session *sess, int status)
    -{
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status);
    -
    - return gg_change_status_descr(sess, status, NULL);
    -}
    -
    -/**
    - * Zmienia status użytkownika na status opisowy.
    - *
    - * \param sess Struktura sesji
    - * \param status Nowy status użytkownika
    - * \param descr Opis statusu użytkownika (lub \c NULL)
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup status
    - */
    -int gg_change_status_descr(struct gg_session *sess, int status, const char *descr)
    -{
    - struct gg_new_status80 p;
    - char *gen_descr = NULL;
    - int descr_len = 0;
    - int descr_null_len = 0;
    - int res;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);
    -
    - if (!sess) {
    - errno = EFAULT;
    - return -1;
    - }
    -
    - if (sess->state != GG_STATE_CONNECTED) {
    - errno = ENOTCONN;
    - return -1;
    - }
    -
    - sess->status = status;
    -
    - if (descr != NULL && sess->encoding != GG_ENCODING_UTF8) {
    - descr = gen_descr = gg_encoding_convert(descr, GG_ENCODING_CP1250, GG_ENCODING_UTF8, -1, -1);
    -
    - if (!gen_descr)
    - return -1;
    - }
    -
    - if (descr) {
    - descr_len = strlen(descr);
    -
    - if (descr_len > GG_STATUS_DESCR_MAXSIZE)
    - descr_len = GG_STATUS_DESCR_MAXSIZE;
    -
    - /* XXX pamiętać o tym, żeby nie ucinać w środku znaku utf-8 */
    - } else {
    - descr = "";
    - }
    -
    - p.status = gg_fix32(status);
    - p.flags = gg_fix32(sess->status_flags);
    - p.description_size = gg_fix32(descr_len);
    -
    - if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) {
    - p.flags = gg_fix32(0x00000014);
    - descr_null_len = 1;
    - }
    -
    - res = gg_send_packet(sess, GG_NEW_STATUS80,
    - &p, sizeof(p), descr, descr_len,
    - "\x00", descr_null_len, NULL);
    -
    - free(gen_descr);
    -
    - if (GG_S_NA(status)) {
    - sess->state = GG_STATE_DISCONNECTING;
    - sess->timeout = GG_TIMEOUT_DISCONNECT;
    - }
    -
    - return res;
    -}
    -
    -/**
    - * Zmienia status użytkownika na status opisowy z podanym czasem powrotu.
    - *
    - * \param sess Struktura sesji
    - * \param status Nowy status użytkownika
    - * \param descr Opis statusu użytkownika
    - * \param ts Czas powrotu w postaci uniksowego znacznika czasu
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup status
    - */
    -int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int ts)
    -{
    - gg_debug_session(sess, GG_DEBUG_FUNCTION,
    - "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n",
    - sess, status, descr, ts);
    -
    - return gg_change_status_descr(sess, status, descr);
    -}
    -
    -/**
    - * Funkcja zmieniająca flagi statusu.
    - *
    - * \param sess Struktura sesji
    - * \param flags Nowe flagi statusu
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \note Aby zmiany weszły w życie, należy ponownie ustawić status za pomocą
    - * funkcji z rodziny \c gg_change_status().
    - *
    - * \ingroup status
    - */
    -int gg_change_status_flags(struct gg_session *sess, int flags)
    -{
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_flags(%p, 0x%08x);\n", sess, flags);
    -
    - if (sess == NULL) {
    - errno = EFAULT;
    - return -1;
    - }
    -
    - sess->status_flags = flags;
    -
    - return 0;
    -}
    -
    -#ifndef DOXYGEN
    -
    -static int gg_send_message_110(struct gg_session *sess,
    - uin_t recipient, uint64_t chat_id,
    - const char *message, int is_html)
    -{
    - GG110SendMessage msg = GG110_SEND_MESSAGE__INIT;
    - int packet_type = recipient ? GG_SEND_MSG110 : GG_CHAT_SEND_MSG;
    - int seq;
    - char *html_message_gen = NULL, *plain_message_gen = NULL;
    - const char *html_message, *plain_message;
    - int succ = 1;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION,
    - "** gg_send_message_110(%p, %u, %" PRIu64 ", %p, %d);\n",
    - sess, recipient, chat_id, message, is_html);
    -
    - if (message == NULL)
    - return -1;
    -
    - if ((recipient == 0) == (chat_id == 0))
    - return -1;
    -
    - if (is_html) {
    - html_message = message;
    -
    - if (sess->encoding != GG_ENCODING_UTF8) {
    - html_message = html_message_gen = gg_encoding_convert(
    - html_message, sess->encoding, GG_ENCODING_UTF8,
    - -1, -1);
    - if (html_message_gen == NULL)
    - return -1;
    - }
    -
    - plain_message = plain_message_gen =
    - gg_message_html_to_text_110(html_message);
    - if (plain_message_gen == NULL) {
    - free(html_message_gen);
    - return -1;
    - }
    - } else {
    - plain_message = message;
    -
    - if (sess->encoding != GG_ENCODING_UTF8) {
    - plain_message = plain_message_gen = gg_encoding_convert(
    - plain_message, sess->encoding, GG_ENCODING_UTF8,
    - -1, -1);
    - if (plain_message_gen == NULL)
    - return -1;
    - }
    -
    - html_message = html_message_gen =
    - gg_message_text_to_html_110(plain_message, -1);
    - if (html_message_gen == NULL) {
    - free(plain_message_gen);
    - return -1;
    - }
    - }
    -
    - seq = ++sess->seq;
    -
    - if (recipient) {
    - msg.has_recipient = 1;
    - gg_protobuf_set_uin(&msg.recipient, recipient, NULL);
    - }
    -
    - msg.seq = seq;
    -
    - /* rzutujemy z const, ale msg i tak nie będzie modyfikowany */
    - msg.msg_plain = (char*)plain_message;
    - msg.msg_xhtml = (char*)html_message;
    -
    - if (chat_id) {
    - msg.dummy3 = "";
    - msg.has_chat_id = 1;
    - msg.chat_id = chat_id;
    - }
    -
    - if (!GG_PROTOBUF_SEND(sess, NULL, packet_type, gg110_send_message, msg))
    - succ = 0;
    -
    - free(html_message_gen);
    - free(plain_message_gen);
    -
    - return succ ? seq : -1;
    -}
    -
    -static char *
    -gg_message_legacy_text_to_html(const char *src, gg_encoding_t encoding,
    - const unsigned char *format, size_t format_len)
    -{
    - size_t len;
    - char *dst;
    -
    - if (format == NULL || format_len <= 3) {
    - format = NULL;
    - format_len = 0;
    - } else {
    - format += 3;
    - format_len -= 3;
    - }
    -
    - len = gg_message_text_to_html(NULL, src, encoding, format, format_len);
    -
    - dst = malloc(len + 1);
    - if (dst == NULL)
    - return NULL;
    -
    - gg_message_text_to_html(dst, src, encoding, format, format_len);
    -
    - return dst;
    -}
    -
    -/**
    - * \internal Wysyła wiadomość.
    - *
    - * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
    - * do potwierdzenia.
    - *
    - * \param sess Struktura sesji
    - * \param msgclass Klasa wiadomości
    - * \param recipients_count Liczba adresatów
    - * \param recipients Wskaźnik do tablicy z numerami adresatów
    - * \param message Treść wiadomości
    - * \param format Informacje o formatowaniu
    - * \param formatlen Długość informacji o formatowaniu
    - * \param html_message Treść wiadomości HTML
    - *
    - * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
    - *
    - * \ingroup messages
    - */
    -static int gg_send_message_common(struct gg_session *sess, int msgclass,
    - int recipients_count, uin_t *recipients, const unsigned char *message,
    - const unsigned char *format, int formatlen,
    - const unsigned char *html_message)
    -{
    - struct gg_send_msg80 s80;
    - const char *cp_msg = NULL, *utf_html_msg = NULL;
    - char *recoded_msg = NULL, *recoded_html_msg = NULL;
    - unsigned char *generated_format = NULL;
    - int seq_no = -1;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_common("
    - "%p, %d, %d, %p, %p, %p, %d, %p);\n", sess, msgclass,
    - recipients_count, recipients, message, format,
    - formatlen, html_message);
    -
    - if (!sess) {
    - errno = EFAULT;
    - return -1;
    - }
    -
    - if (sess->state != GG_STATE_CONNECTED) {
    - errno = ENOTCONN;
    - return -1;
    - }
    -
    - if ((message == NULL && html_message == NULL) ||
    - recipients_count <= 0 || recipients_count > 0xffff ||
    - recipients == NULL || (format == NULL && formatlen != 0))
    - {
    - errno = EINVAL;
    - return -1;
    - }
    -
    - if (sess->protocol_version >= GG_PROTOCOL_VERSION_110 &&
    - recipients_count == 1)
    - {
    - int is_html = (html_message != NULL);
    - char *formatted_msg = NULL;
    -
    - if (formatlen > 3 && !is_html) {
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_WARNING,
    - "// gg_send_message_common() using legacy "
    - "formatting with new protocol\n");
    - formatted_msg = gg_message_legacy_text_to_html(
    - (const char *)message, sess->encoding,
    - format, formatlen);
    - if (formatted_msg == NULL)
    - goto cleanup;
    - html_message = (unsigned char*)formatted_msg;
    - is_html = 1;
    - }
    -
    - seq_no = gg_send_message_110(sess, recipients[0], 0,
    - (const char*)(is_html ? html_message : message),
    - is_html);
    - goto cleanup;
    - }
    -
    - if (sess->protocol_version >= GG_PROTOCOL_VERSION_110 &&
    - !gg_compat_feature_is_enabled(sess, GG_COMPAT_FEATURE_LEGACY_CONFER))
    - {
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "// gg_send_message_common() legacy conferences disabled\n");
    - errno = EINVAL;
    - return -1;
    - }
    -
    - if (message == NULL) {
    - char *tmp_msg;
    - size_t len, fmt_len;
    - uint16_t fixed_fmt_len;
    -
    - len = gg_message_html_to_text(NULL, NULL, &fmt_len, (const char*) html_message, sess->encoding);
    -
    - tmp_msg = malloc(len + 1);
    -
    - if (tmp_msg == NULL)
    - goto cleanup;
    -
    - if (fmt_len != 0) {
    - generated_format = malloc(fmt_len + 3);
    -
    - if (generated_format == NULL) {
    - free(tmp_msg);
    - goto cleanup;
    - }
    -
    - generated_format[0] = '\x02';
    - fixed_fmt_len = gg_fix16(fmt_len);
    - memcpy(generated_format + 1, &fixed_fmt_len, sizeof(fixed_fmt_len));
    - gg_message_html_to_text(tmp_msg, generated_format + 3,
    - NULL, (const char*)html_message, sess->encoding);
    -
    - format = generated_format;
    - formatlen = fmt_len + 3;
    - } else {
    - gg_message_html_to_text(tmp_msg, NULL, NULL, (const char*) html_message, sess->encoding);
    -
    - format = NULL;
    - formatlen = 0;
    - }
    -
    - if (sess->encoding != GG_ENCODING_CP1250) {
    - cp_msg = recoded_msg = gg_encoding_convert(tmp_msg, sess->encoding, GG_ENCODING_CP1250, -1, -1);
    - free(tmp_msg);
    -
    - if (cp_msg == NULL)
    - goto cleanup;
    - } else {
    - cp_msg = recoded_msg = tmp_msg;
    - }
    - } else {
    - if (sess->encoding != GG_ENCODING_CP1250) {
    - cp_msg = recoded_msg = gg_encoding_convert(
    - (const char*)message, sess->encoding,
    - GG_ENCODING_CP1250, -1, -1);
    -
    - if (cp_msg == NULL)
    - goto cleanup;
    - } else {
    - cp_msg = (const char*) message;
    - }
    - }
    -
    - if (html_message == NULL) {
    - char *formatted_msg;
    -
    - formatted_msg = gg_message_legacy_text_to_html(
    - (const char*)message, sess->encoding, format, formatlen);
    - if (formatted_msg == NULL)
    - goto cleanup;
    -
    - if (sess->encoding == GG_ENCODING_UTF8) {
    - utf_html_msg = recoded_html_msg = formatted_msg;
    - } else {
    - utf_html_msg = recoded_html_msg = gg_encoding_convert(
    - formatted_msg, sess->encoding,
    - GG_ENCODING_UTF8, -1, -1);
    - free(formatted_msg);
    -
    - if (utf_html_msg == NULL)
    - goto cleanup;
    - }
    - } else {
    - if (sess->encoding == GG_ENCODING_UTF8) {
    - utf_html_msg = (const char*) html_message;
    - } else {
    - utf_html_msg = recoded_html_msg = gg_encoding_convert(
    - (const char*)html_message, sess->encoding,
    - GG_ENCODING_UTF8, -1, -1);
    -
    - if (utf_html_msg == NULL)
    - goto cleanup;
    - }
    - }
    -
    - /* Drobne odchylenie od protokołu. Jeśli wysyłamy kilka
    - * wiadomości w ciągu jednej sekundy, zwiększamy poprzednią
    - * wartość, żeby każda wiadomość miała unikalny numer.
    - */
    -
    - seq_no = time(NULL);
    -
    - if (seq_no <= sess->seq)
    - seq_no = sess->seq + 1;
    -
    - sess->seq = seq_no;
    -
    - s80.seq = gg_fix32(seq_no);
    - s80.msgclass = gg_fix32(msgclass);
    - s80.offset_plain = gg_fix32(sizeof(s80) + strlen(utf_html_msg) + 1);
    - s80.offset_attr = gg_fix32(sizeof(s80) + strlen(utf_html_msg) + 1 + strlen(cp_msg) + 1);
    -
    - if (recipients_count > 1) {
    - struct gg_msg_recipients r;
    - int i, j, k;
    - uin_t *recps;
    -
    - r.flag = GG_MSG_OPTION_CONFERENCE;
    - r.count = gg_fix32(recipients_count - 1);
    -
    - recps = malloc(sizeof(uin_t) * (recipients_count - 1));
    -
    - if (!recps) {
    - seq_no = -1;
    - goto cleanup;
    - }
    -
    - for (i = 0; i < recipients_count; i++) {
    - for (j = 0, k = 0; j < recipients_count; j++) {
    - if (j != i) {
    - recps[k] = gg_fix32(recipients[j]);
    - k++;
    - }
    - }
    -
    - s80.recipient = gg_fix32(recipients[i]);
    -
    - if (gg_send_packet(sess, GG_SEND_MSG80, &s80,
    - sizeof(s80), utf_html_msg,
    - strlen(utf_html_msg) + 1, cp_msg,
    - strlen(cp_msg) + 1, &r, sizeof(r), recps,
    - (recipients_count - 1) * sizeof(uin_t), format,
    - formatlen, NULL) == -1)
    - {
    - seq_no = -1;
    - }
    - }
    -
    - free(recps);
    - } else {
    - s80.recipient = gg_fix32(recipients[0]);
    -
    - if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80),
    - utf_html_msg, strlen(utf_html_msg) + 1, cp_msg,
    - strlen(cp_msg) + 1, format, formatlen, NULL) == -1)
    - {
    - seq_no = -1;
    - }
    - }
    -
    -cleanup:
    - free(recoded_msg);
    - free(recoded_html_msg);
    - free(generated_format);
    -
    - if (seq_no >= 0)
    - gg_compat_message_sent(sess, seq_no, recipients_count, recipients);
    -
    - return seq_no;
    -}
    -
    -#endif /* DOXYGEN */
    -
    -/**
    - * Wysyła wiadomość do użytkownika.
    - *
    - * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
    - * do potwierdzenia.
    - *
    - * \param sess Struktura sesji
    - * \param msgclass Klasa wiadomości
    - * \param recipient Numer adresata
    - * \param message Treść wiadomości
    - *
    - * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
    - *
    - * \ingroup messages
    - */
    -int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message)
    -{
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, "
    - "%u, %p)\n", sess, msgclass, recipient, message);
    -
    - if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) {
    - int seq_no;
    -
    - seq_no = gg_send_message_110(sess, recipient, 0, (const char*)message, 0);
    -
    - if (seq_no >= 0)
    - gg_compat_message_sent(sess, seq_no, 1, &recipient);
    -
    - return seq_no;
    - }
    -
    - return gg_send_message_common(sess, msgclass, 1, &recipient, message,
    - (const unsigned char*)"\x02\x06\x00\x00\x00\x08\x00\x00\x00",
    - 9, NULL);
    -}
    -
    -/**
    - * Wysyła wiadomość formatowaną.
    - *
    - * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
    - * do potwierdzenia.
    - *
    - * \param sess Struktura sesji
    - * \param msgclass Klasa wiadomości
    - * \param recipient Numer adresata
    - * \param message Treść wiadomości
    - * \param format Informacje o formatowaniu
    - * \param formatlen Długość informacji o formatowaniu
    - *
    - * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
    - *
    - * \ingroup messages
    - */
    -int gg_send_message_richtext(struct gg_session *sess, int msgclass,
    - uin_t recipient, const unsigned char *message,
    - const unsigned char *format, int formatlen)
    -{
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_richtext("
    - "%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient,
    - message, format, formatlen);
    -
    - return gg_send_message_common(sess, msgclass, 1, &recipient, message, format, formatlen, NULL);
    -}
    -
    -/**
    - * Wysyła formatowaną wiadomość HTML.
    - *
    - * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
    - * do potwierdzenia.
    - *
    - * \param sess Struktura sesji
    - * \param msgclass Klasa wiadomości
    - * \param recipient Numer adresata
    - * \param html_message Treść wiadomości HTML
    - *
    - * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
    - *
    - * \ingroup messages
    - */
    -int gg_send_message_html(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *html_message)
    -{
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_html(%p, "
    - "%d, %u, %p);\n", sess, msgclass, recipient, html_message);
    -
    - return gg_send_message_common(sess, msgclass, 1, &recipient, NULL, NULL, 0, html_message);
    -}
    -
    -/**
    - * Wysyła wiadomość w ramach konferencji.
    - *
    - * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
    - * do potwierdzenia.
    - *
    - * \param sess Struktura sesji
    - * \param msgclass Klasa wiadomości
    - * \param recipients_count Liczba adresatów
    - * \param recipients Wskaźnik do tablicy z numerami adresatów
    - * \param message Treść wiadomości
    - *
    - * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
    - *
    - * \ingroup messages
    - */
    -int gg_send_message_confer(struct gg_session *sess, int msgclass,
    - int recipients_count, uin_t *recipients, const unsigned char *message)
    -{
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer("
    - "%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count,
    - recipients, message);
    -
    - return gg_send_message_common(sess, msgclass, recipients_count,
    - recipients, message,
    - (const unsigned char*)"\x02\x06\x00\x00\x00\x08\x00\x00\x00",
    - 9, NULL);
    -}
    -
    -/**
    - * Wysyła wiadomość formatowaną w ramach konferencji.
    - *
    - * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
    - * do potwierdzenia.
    - *
    - * \param sess Struktura sesji
    - * \param msgclass Klasa wiadomości
    - * \param recipients_count Liczba adresatów
    - * \param recipients Wskaźnik do tablicy z numerami adresatów
    - * \param message Treść wiadomości
    - * \param format Informacje o formatowaniu
    - * \param formatlen Długość informacji o formatowaniu
    - *
    - * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
    - *
    - * \ingroup messages
    - */
    -int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass,
    - int recipients_count, uin_t *recipients, const unsigned char *message,
    - const unsigned char *format, int formatlen)
    -{
    - gg_debug_session(sess, GG_DEBUG_FUNCTION,
    - "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, "
    - "%d);\n", sess, msgclass, recipients_count, recipients, message,
    - format, formatlen);
    -
    - return gg_send_message_common(sess, msgclass, recipients_count, recipients, message, format, formatlen, NULL);
    -}
    -
    -/**
    - * Wysyła formatowaną wiadomość HTML w ramach konferencji.
    - *
    - * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
    - * do potwierdzenia.
    - *
    - * \param sess Struktura sesji
    - * \param msgclass Klasa wiadomości
    - * \param recipients_count Liczba adresatów
    - * \param recipients Wskaźnik do tablicy z numerami adresatów
    - * \param html_message Treść wiadomości HTML
    - *
    - * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
    - *
    - * \ingroup messages
    - */
    -int gg_send_message_confer_html(struct gg_session *sess, int msgclass,
    - int recipients_count, uin_t *recipients,
    - const unsigned char *html_message)
    -{
    - gg_debug_session(sess, GG_DEBUG_FUNCTION,
    - "** gg_send_message_confer_html(%p, %d, %d, %p, %p);\n", sess,
    - msgclass, recipients_count, recipients, html_message);
    -
    - return gg_send_message_common(sess, msgclass, recipients_count, recipients, NULL, NULL, 0, html_message);
    -}
    -
    -/**
    - * Wysyła wiadomość binarną przeznaczoną dla klienta.
    - *
    - * Wiadomości między klientami przesyła się np. w celu wywołania zwrotnego
    - * połączenia bezpośredniego. Funkcja zwraca losowy numer sekwencyjny,
    - * który można zignorować albo wykorzystać do potwierdzenia.
    - *
    - * \param sess Struktura sesji
    - * \param msgclass Klasa wiadomości
    - * \param recipient Numer adresata
    - * \param message Treść wiadomości
    - * \param message_len Długość wiadomości
    - *
    - * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
    - *
    - * \ingroup messages
    - */
    -int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient,
    - const unsigned char *message, int message_len)
    -{
    - struct gg_send_msg s;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, "
    - "%d, %u, ...);\n", sess, msgclass, recipient);
    -
    - if (!sess) {
    - errno = EFAULT;
    - return -1;
    - }
    -
    - if (sess->state != GG_STATE_CONNECTED) {
    - errno = ENOTCONN;
    - return -1;
    - }
    -
    - s.recipient = gg_fix32(recipient);
    - s.seq = gg_fix32(0);
    - s.msgclass = gg_fix32(msgclass);
    -
    - return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL);
    -}
    -
    -/**
    - * Wysyła żądanie obrazka o podanych parametrach.
    - *
    - * Wiadomości obrazkowe nie zawierają samych obrazków, a tylko ich rozmiary
    - * i sumy kontrolne. Odbiorca najpierw szuka obrazków w swojej pamięci
    - * podręcznej i dopiero gdy ich nie znajdzie, wysyła żądanie do nadawcy.
    - * Wynik zostanie przekazany zdarzeniem \c GG_EVENT_IMAGE_REPLY.
    - *
    - * \param sess Struktura sesji
    - * \param recipient Numer adresata
    - * \param size Rozmiar obrazka w bajtach
    - * \param crc32 Suma kontrola obrazka
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup messages
    - */
    -int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32)
    -{
    - struct gg_send_msg s;
    - struct gg_msg_image_request r;
    - char dummy = 0;
    - int res;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, "
    - "%u, 0x%.4x);\n", sess, recipient, size, crc32);
    -
    - if (!sess) {
    - errno = EFAULT;
    - return -1;
    - }
    -
    - if (sess->state != GG_STATE_CONNECTED) {
    - errno = ENOTCONN;
    - return -1;
    - }
    -
    - if (size < 0) {
    - errno = EINVAL;
    - return -1;
    - }
    -
    - s.recipient = gg_fix32(recipient);
    - s.seq = gg_fix32(0);
    - s.msgclass = gg_fix32(GG_CLASS_MSG);
    -
    - r.flag = GG_MSG_OPTION_IMAGE_REQUEST;
    - r.size = gg_fix32(size);
    - r.crc32 = gg_fix32(crc32);
    -
    - res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), &dummy, 1, &r, sizeof(r), NULL);
    -
    - if (!res) {
    - struct gg_image_queue *q = malloc(sizeof(*q));
    - char *buf;
    -
    - if (!q) {
    - gg_debug_session(sess, GG_DEBUG_MISC,
    - "// gg_image_request() not enough memory for "
    - "image queue\n");
    - return -1;
    - }
    -
    - buf = malloc(size);
    - if (size && !buf) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n");
    - free(q);
    - return -1;
    - }
    -
    - memset(q, 0, sizeof(*q));
    -
    - q->sender = recipient;
    - q->size = size;
    - q->crc32 = crc32;
    - q->image = buf;
    -
    - if (!sess->images)
    - sess->images = q;
    - else {
    - struct gg_image_queue *qq;
    -
    - for (qq = sess->images; qq->next; qq = qq->next);
    -
    - qq->next = q;
    - }
    - }
    -
    - return res;
    -}
    -
    -/**
    - * Wysyła żądany obrazek.
    - *
    - * \param sess Struktura sesji
    - * \param recipient Numer adresata
    - * \param filename Nazwa pliku
    - * \param image Bufor z obrazkiem
    - * \param size Rozmiar obrazka
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup messages
    - */
    -int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size)
    -{
    - struct gg_session_private *p;
    - struct gg_msg_image_reply *r;
    - struct gg_send_msg s;
    - const char *tmp;
    - char buf[1910];
    - gg_imgout_queue_t *queue = NULL, *queue_end = NULL;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, "
    - "\"%s\", %p, %d);\n", sess, recipient, filename, image, size);
    -
    - if (!sess || !filename || !image) {
    - errno = EFAULT;
    - return -1;
    - }
    -
    - p = sess->private_data;
    -
    - if (sess->state != GG_STATE_CONNECTED) {
    - errno = ENOTCONN;
    - return -1;
    - }
    -
    - if (size < 0) {
    - errno = EINVAL;
    - return -1;
    - }
    -
    - /* wytnij ścieżki, zostaw tylko nazwę pliku */
    - while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\')))
    - filename = tmp + 1;
    -
    - if (strlen(filename) < 1 || strlen(filename) > 1024) {
    - errno = EINVAL;
    - return -1;
    - }
    -
    - s.recipient = gg_fix32(recipient);
    - s.seq = gg_fix32(0);
    - s.msgclass = gg_fix32(GG_CLASS_MSG);
    -
    - buf[0] = 0;
    - r = (void*) &buf[1];
    -
    - r->flag = GG_MSG_OPTION_IMAGE_REPLY;
    - r->size = gg_fix32(size);
    - r->crc32 = gg_fix32(gg_crc32(0, (const unsigned char*) image, size));
    -
    - while (size > 0) {
    - gg_imgout_queue_t *it;
    - size_t buflen, chunklen;
    -
    - /* \0 + struct gg_msg_image_reply */
    - buflen = sizeof(struct gg_msg_image_reply) + 1;
    -
    - /* w pierwszym kawałku jest nazwa pliku */
    - if (r->flag == GG_MSG_OPTION_IMAGE_REPLY) {
    - strcpy(buf + buflen, filename);
    - buflen += strlen(filename) + 1;
    - }
    -
    - chunklen = ((size_t) size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : (size_t) size;
    -
    - memcpy(buf + buflen, image, chunklen);
    - size -= chunklen;
    - image += chunklen;
    -
    - it = gg_new0(sizeof(gg_imgout_queue_t));
    - if (!it)
    - break;
    - if (queue_end) {
    - queue_end->next = it;
    - queue_end = it;
    - } else {
    - queue = queue_end = it;
    - }
    -
    - memcpy(&it->msg_hdr, &s, sizeof(s));
    - memcpy(it->buf, buf, buflen + chunklen);
    - it->buf_len = buflen + chunklen;
    -
    - r->flag = GG_MSG_OPTION_IMAGE_REPLY_MORE;
    - }
    -
    - if (p->imgout_queue) {
    - queue_end = p->imgout_queue;
    - while (queue_end->next)
    - queue_end = queue_end->next;
    - queue_end->next = queue;
    - } else {
    - p->imgout_queue = queue;
    - }
    - gg_image_sendout(sess);
    -
    - return 0;
    -}
    -
    -void gg_image_sendout(struct gg_session *sess)
    -{
    - struct gg_session_private *p = sess->private_data;
    -
    - while (p->imgout_waiting_ack < GG_IMGOUT_WAITING_MAX && p->imgout_queue) {
    - gg_imgout_queue_t *it = p->imgout_queue;
    - int res;
    -
    - p->imgout_queue = p->imgout_queue->next;
    - p->imgout_waiting_ack++;
    -
    - res = gg_send_packet(sess, GG_SEND_MSG,
    - &it->msg_hdr, sizeof(it->msg_hdr),
    - it->buf, it->buf_len,
    - NULL);
    -
    - free(it);
    -
    - if (res == -1)
    - break;
    - }
    -}
    -
    -static int gg_notify105_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
    -{
    - int i = 0;
    -
    - if (!userlist || !count)
    - return gg_send_packet(sess, GG_NOTIFY105_LIST_EMPTY, NULL);
    -
    - while (i < count) {
    - gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL);
    - gg_tvbuilder_expected_size(tvb, 2100);
    -
    - while (i < count) {
    - size_t prev_size = gg_tvbuilder_get_size(tvb);
    - gg_tvbuilder_write_uin(tvb, userlist[i]);
    - gg_tvbuilder_write_uint8(tvb,
    - (types == NULL) ? GG_USER_NORMAL : types[i]);
    -
    - /* Oryginalny klient wysyła maksymalnie 2048 bajtów
    - * danych w każdym pakiecie tego typu.
    - */
    - if (gg_tvbuilder_get_size(tvb) > 2048) {
    - gg_tvbuilder_strip(tvb, prev_size);
    - break;
    - }
    - i++;
    - }
    -
    - if (!gg_tvbuilder_send(tvb, (i < count) ?
    - GG_NOTIFY105_FIRST : GG_NOTIFY105_LAST))
    - {
    - return -1;
    - }
    - }
    -
    - return 0;
    -}
    -
    -/**
    - * Wysyła do serwera listę kontaktów.
    - *
    - * Funkcja informuje serwer o liście kontaktów, których statusy będą
    - * obserwowane lub kontaktów, które bedą blokowane. Dla każdego z \c count
    - * kontaktów tablica \c userlist zawiera numer, a tablica \c types rodzaj
    - * kontaktu (\c GG_USER_NORMAL, \c GG_USER_OFFLINE, \c GG_USER_BLOCKED).
    - *
    - * Listę kontaktów należy \b zawsze wysyłać po połączeniu, nawet jeśli
    - * jest pusta.
    - *
    - * \param sess Struktura sesji
    - * \param userlist Wskaźnik do tablicy numerów kontaktów
    - * \param types Wskaźnik do tablicy rodzajów kontaktów. Jeżeli NULL, wszystkie kontakty są typu GG_USER_NORMAL.
    - * \param count Liczba kontaktów
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup contacts
    - */
    -int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
    -{
    - struct gg_notify *n;
    - int i, res = 0;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count);
    -
    - if (!sess) {
    - errno = EFAULT;
    - return -1;
    - }
    -
    - if (sess->state != GG_STATE_CONNECTED) {
    - errno = ENOTCONN;
    - return -1;
    - }
    -
    - if (sess->protocol_version >= GG_PROTOCOL_VERSION_110)
    - return gg_notify105_ex(sess, userlist, types, count);
    -
    - if (!userlist || !count)
    - return gg_send_packet(sess, GG_LIST_EMPTY, NULL);
    -
    - while (count > 0) {
    - int part_count, packet_type;
    -
    - if (count > 400) {
    - part_count = 400;
    - packet_type = GG_NOTIFY_FIRST;
    - } else {
    - part_count = count;
    - packet_type = GG_NOTIFY_LAST;
    - }
    -
    - if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count)))
    - return -1;
    -
    - for (i = 0; i < part_count; i++) {
    - n[i].uin = gg_fix32(userlist[i]);
    - if (types == NULL)
    - n[i].dunno1 = GG_USER_NORMAL;
    - else
    - n[i].dunno1 = types[i];
    - }
    -
    - if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) {
    - free(n);
    - res = -1;
    - break;
    - }
    -
    - count -= part_count;
    - userlist += part_count;
    - if (types != NULL)
    - types += part_count;
    -
    - free(n);
    - }
    -
    - return res;
    -}
    -
    -/**
    - * Wysyła do serwera listę kontaktów.
    - *
    - * Funkcja jest odpowiednikiem \c gg_notify_ex(), gdzie wszystkie kontakty
    - * są rodzaju \c GG_USER_NORMAL.
    - *
    - * \param sess Struktura sesji
    - * \param userlist Wskaźnik do tablicy numerów kontaktów
    - * \param count Liczba kontaktów
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup contacts
    - */
    -int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
    -{
    - return gg_notify_ex(sess, userlist, NULL, count);
    -}
    -
    -/**
    - * Dodaje kontakt.
    - *
    - * Dodaje do listy kontaktów dany numer w trakcie połączenia. Aby zmienić
    - * rodzaj kontaktu (np. z normalnego na zablokowany), należy najpierw usunąć
    - * poprzedni rodzaj, ponieważ serwer operuje na maskach bitowych.
    - *
    - * \param sess Struktura sesji
    - * \param uin Numer kontaktu
    - * \param type Rodzaj kontaktu
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup contacts
    - */
    -int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type)
    -{
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type);
    -
    - if (!sess) {
    - errno = EFAULT;
    - return -1;
    - }
    -
    - if (sess->state != GG_STATE_CONNECTED) {
    - errno = ENOTCONN;
    - return -1;
    - }
    -
    - if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) {
    - gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL);
    - gg_tvbuilder_expected_size(tvb, 16);
    -
    - gg_tvbuilder_write_uin(tvb, uin);
    - gg_tvbuilder_write_uint8(tvb, type);
    -
    - if (!gg_tvbuilder_send(tvb, GG_ADD_NOTIFY105))
    - return -1;
    - return 0;
    - } else {
    - struct gg_add_remove a;
    -
    - a.uin = gg_fix32(uin);
    - a.dunno1 = type;
    -
    - return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL);
    - }
    -}
    -
    -/**
    - * Dodaje kontakt.
    - *
    - * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
    - * kontaktów to \c GG_USER_NORMAL.
    - *
    - * \param sess Struktura sesji
    - * \param uin Numer kontaktu
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup contacts
    - */
    -int gg_add_notify(struct gg_session *sess, uin_t uin)
    -{
    - return gg_add_notify_ex(sess, uin, GG_USER_NORMAL);
    -}
    -
    -/**
    - * Usuwa kontakt.
    - *
    - * Usuwa z listy kontaktów dany numer w trakcie połączenia.
    - *
    - * \param sess Struktura sesji
    - * \param uin Numer kontaktu
    - * \param type Rodzaj kontaktu
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup contacts
    - */
    -int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type)
    -{
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type);
    -
    - if (!sess) {
    - errno = EFAULT;
    - return -1;
    - }
    -
    - if (sess->state != GG_STATE_CONNECTED) {
    - errno = ENOTCONN;
    - return -1;
    - }
    -
    - if (sess->protocol_version >= GG_PROTOCOL_VERSION_110) {
    - gg_tvbuilder_t *tvb = gg_tvbuilder_new(sess, NULL);
    - gg_tvbuilder_expected_size(tvb, 16);
    -
    - gg_tvbuilder_write_uin(tvb, uin);
    - gg_tvbuilder_write_uint8(tvb, type);
    -
    - if (!gg_tvbuilder_send(tvb, GG_REMOVE_NOTIFY105))
    - return -1;
    - return 0;
    - } else {
    - struct gg_add_remove a;
    -
    - a.uin = gg_fix32(uin);
    - a.dunno1 = type;
    -
    - return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL);
    - }
    -}
    -
    -/**
    - * Usuwa kontakt.
    - *
    - * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
    - * kontaktów to \c GG_USER_NORMAL.
    - *
    - * \param sess Struktura sesji
    - * \param uin Numer kontaktu
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup contacts
    - */
    -int gg_remove_notify(struct gg_session *sess, uin_t uin)
    -{
    - return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL);
    -}
    -
    -/**
    - * Wysyła do serwera zapytanie dotyczące listy kontaktów.
    - *
    - * Funkcja służy do importu lub eksportu listy kontaktów do serwera.
    - * W odróżnieniu od funkcji \c gg_notify(), ta lista kontaktów jest przez
    - * serwer jedynie przechowywana i nie ma wpływu na połączenie. Format
    - * listy kontaktów jest ignorowany przez serwer, ale ze względu na
    - * kompatybilność z innymi klientami, należy przechowywać dane w tym samym
    - * formacie co oryginalny klient Gadu-Gadu.
    - *
    - * Program nie musi się przejmować fragmentacją listy kontaktów wynikającą
    - * z protokołu -- wysyła i odbiera kompletną listę.
    - *
    - * \param sess Struktura sesji
    - * \param type Rodzaj zapytania
    - * \param request Treść zapytania (może być równe NULL)
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup importexport
    - */
    -int gg_userlist_request(struct gg_session *sess, char type, const char *request)
    -{
    - int len;
    -
    - if (!sess) {
    - errno = EFAULT;
    - return -1;
    - }
    -
    - if (sess->state != GG_STATE_CONNECTED) {
    - errno = ENOTCONN;
    - return -1;
    - }
    -
    - if (!request) {
    - sess->userlist_blocks = 1;
    - return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL);
    - }
    -
    - len = strlen(request);
    -
    - sess->userlist_blocks = 0;
    -
    - while (len > 2047) {
    - sess->userlist_blocks++;
    -
    - if (gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, 2047, NULL) == -1)
    - return -1;
    -
    - if (type == GG_USERLIST_PUT)
    - type = GG_USERLIST_PUT_MORE;
    -
    - request += 2047;
    - len -= 2047;
    - }
    -
    - sess->userlist_blocks++;
    -
    - return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL);
    -}
    -
    -/**
    - * Wysyła do serwera zapytanie dotyczące listy kontaktów (10.0).
    - *
    - * Funkcja służy do importu lub eksportu listy kontaktów do serwera.
    - * W odróżnieniu od funkcji \c gg_notify(), ta lista kontaktów jest przez
    - * serwer jedynie przechowywana i nie ma wpływu na połączenie. Format
    - * listy kontaktów jest jednak weryfikowany przez serwer, który stara się
    - * synchronizować listę kontaktów zapisaną w formatach GG 7.0 oraz GG 10.0.
    - * Serwer przyjmuje listy kontaktów przysłane w formacie niezgodnym z podanym
    - * jako \c format_type, ale nie zachowuje ich, a przesłanie takiej listy jest
    - * równoznaczne z usunięciem listy kontaktów.
    - *
    - * Program nie musi się przejmować kompresją listy kontaktów zgodną
    - * z protokołem -- wysyła i odbiera kompletną listę zapisaną czystym tekstem.
    - *
    - * \param sess Struktura sesji
    - * \param type Rodzaj zapytania
    - * \param version Numer ostatniej znanej programowi wersji listy kontaktów lub 0
    - * \param format_type Typ formatu listy kontaktów
    - * \param request Treść zapytania (może być równe NULL)
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup importexport
    - */
    -int gg_userlist100_request(struct gg_session *sess, char type,
    - unsigned int version, char format_type, const char *request)
    -{
    - struct gg_userlist100_request pkt;
    - unsigned char *zrequest;
    - size_t zrequest_len;
    - int ret;
    -
    - if (!sess) {
    - errno = EFAULT;
    - return -1;
    - }
    -
    - if (sess->state != GG_STATE_CONNECTED) {
    - errno = ENOTCONN;
    - return -1;
    - }
    -
    - pkt.type = type;
    - pkt.version = gg_fix32(version);
    - pkt.format_type = format_type;
    - pkt.unknown1 = 0x01;
    -
    - if (request == NULL)
    - return gg_send_packet(sess, GG_USERLIST100_REQUEST, &pkt, sizeof(pkt), NULL);
    -
    - zrequest = gg_deflate(request, &zrequest_len);
    -
    - if (zrequest == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_userlist100_request() gg_deflate() failed\n");
    - return -1;
    - }
    -
    - ret = gg_send_packet(sess, GG_USERLIST100_REQUEST, &pkt, sizeof(pkt), zrequest, zrequest_len, NULL);
    -
    - free(zrequest);
    -
    - return ret;
    -}
    -
    -/**
    - * Informuje rozmówcę o pisaniu wiadomości.
    - *
    - * \param sess Struktura sesji
    - * \param recipient Numer adresata
    - * \param length Długość wiadomości lub 0 jeśli jest pusta
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup messages
    - */
    -int gg_typing_notification(struct gg_session *sess, uin_t recipient, int length){
    - struct gg_typing_notification pkt;
    - uin_t uin;
    -
    - pkt.length = gg_fix16(length);
    - uin = gg_fix32(recipient);
    - memcpy(&pkt.uin, &uin, sizeof(uin_t));
    -
    - return gg_send_packet(sess, GG_TYPING_NOTIFICATION, &pkt, sizeof(pkt), NULL);
    -}
    -
    -/**
    - * Rozłącza inną sesję multilogowania.
    - *
    - * \param gs Struktura sesji
    - * \param conn_id Sesja do rozłączenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup login
    - */
    -int gg_multilogon_disconnect(struct gg_session *gs, gg_multilogon_id_t conn_id)
    -{
    - struct gg_multilogon_disconnect pkt;
    -
    - pkt.conn_id = conn_id;
    -
    - return gg_send_packet(gs, GG_MULTILOGON_DISCONNECT, &pkt, sizeof(pkt), NULL);
    -}
    -
    -/**
    - * Tworzy nową konferencję (11.0).
    - *
    - * \param gs Struktura sesji
    - *
    - * \return Numer sekwencyjny (ten sam, co w \c gg_event_chat_created), lub -1
    - * w przypadku błędu
    - *
    - * \ingroup chat
    - */
    -int gg_chat_create(struct gg_session *gs)
    -{
    - struct gg_chat_create pkt;
    - int seq;
    -
    - if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110))
    - return -1;
    -
    - seq = ++gs->seq;
    -
    - pkt.seq = gg_fix32(seq);
    - pkt.dummy = 0;
    -
    - if (gg_send_packet(gs, GG_CHAT_CREATE, &pkt, sizeof(pkt), NULL) == -1)
    - return -1;
    -
    - return seq;
    -}
    -
    -/**
    - * Zaprasza nowych użytkowników do konferencji (11.0).
    - *
    - * \param gs Struktura sesji
    - * \param id Identyfikator konferencji
    - * \param participants Lista użytkowników do zaproszenia
    - * \param participants_count Liczba użytkowników
    - *
    - * \return Numer sekwencyjny w przypadku powodzenia (ten sam, co w
    - * \c gg_event_chat_invite_ack), lub -1 w przypadku błędu
    - *
    - * \ingroup chat
    - */
    -int gg_chat_invite(struct gg_session *gs, uint64_t id, uin_t *participants,
    - unsigned int participants_count)
    -{
    - struct gg_chat_invite pkt;
    - int seq, ret;
    - unsigned int i;
    - struct gg_chat_participant
    - {
    - uint32_t uin;
    - uint32_t dummy;
    - } GG_PACKED;
    - struct gg_chat_participant *participants_list;
    - size_t participants_list_size;
    -
    - if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110))
    - return -1;
    -
    - if (participants_count == 0 || participants_count >=
    - ~(unsigned int)0 / sizeof(struct gg_chat_participant))
    - {
    - return -1;
    - }
    -
    - participants_list_size = sizeof(struct gg_chat_participant) *
    - participants_count;
    - participants_list = malloc(participants_list_size);
    - if (participants_list == NULL)
    - return -1;
    -
    - seq = ++gs->seq;
    - pkt.id = gg_fix64(id);
    - pkt.seq = gg_fix32(seq);
    - pkt.participants_count = gg_fix32(participants_count);
    -
    - for (i = 0; i < participants_count; i++) {
    - participants_list[i].uin = gg_fix32(participants[i]);
    - participants_list[i].dummy = gg_fix32(0x1e);
    - }
    -
    - ret = gg_send_packet(gs, GG_CHAT_INVITE,
    - &pkt, sizeof(pkt),
    - participants_list, participants_list_size,
    - NULL);
    - free(participants_list);
    -
    - if (ret == -1)
    - return -1;
    - return seq;
    -}
    -
    -/**
    - * Opuszcza konferencję (11.0).
    - *
    - * \param gs Struktura sesji
    - * \param id Identyfikator konferencji
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup chat
    - */
    -int gg_chat_leave(struct gg_session *gs, uint64_t id)
    -{
    - struct gg_chat_leave pkt;
    - int seq;
    -
    - if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110))
    - return -1;
    -
    - seq = ++gs->seq;
    - pkt.id = gg_fix64(id);
    - pkt.seq = gg_fix32(seq);
    -
    - if (gg_send_packet(gs, GG_CHAT_LEAVE, &pkt, sizeof(pkt), NULL) == -1)
    - return -1;
    -
    - return seq;
    -}
    -
    -/**
    - * Wysyła wiadomość w ramach konferencji (11.0).
    - *
    - * \param gs Struktura sesji
    - * \param id Identyfikator konferencji
    - * \param message Wiadomość
    - * \param is_html 1, jeżeli wiadomość jest zapisana jako HTML, 0 w p.p.
    - *
    - * \return Numer sekwencyjny (taki sam, jak w \c gg_event_chat_send_msg_ack)
    - * jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup chat
    - */
    -int gg_chat_send_message(struct gg_session *gs, uint64_t id, const char *message, int is_html)
    -{
    - if (!gg_required_proto(gs, GG_PROTOCOL_VERSION_110))
    - return -1;
    -
    - return gg_send_message_110(gs, 0, id, message, is_html);
    -}
    -
    -/* @} */
    -
    -/**
    - * Sprawdza czy biblioteka obsługuje daną funkcję.
    - *
    - * \param feature Identyfikator funkcji.
    - *
    - * \return Wartość niezerowa jeśli funkcja jest obsłgiwana.
    - *
    - * \ingroup version
    - */
    -int gg_libgadu_check_feature(gg_libgadu_feature_t feature)
    -{
    - switch (feature)
    - {
    - case GG_LIBGADU_FEATURE_SSL:
    -#if defined(GG_CONFIG_HAVE_OPENSSL) || defined(GG_CONFIG_HAVE_GNUTLS)
    - return 1;
    -#else
    - return 0;
    -#endif
    -
    - case GG_LIBGADU_FEATURE_PTHREAD:
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - return 1;
    -#else
    - return 0;
    -#endif
    -
    - case GG_LIBGADU_FEATURE_USERLIST100:
    -#ifdef GG_CONFIG_HAVE_ZLIB
    - return 1;
    -#else
    - return 0;
    -#endif
    -
    - /* Celowo nie ma default, żeby kompilator wyłapał brakujące funkcje */
    -
    - }
    -
    - return 0;
    -}
    -
    -static void gg_socket_manager_error(struct gg_session *sess, enum gg_failure_t failure)
    -{
    - int pipes[2];
    - uint8_t dummy = 0;
    - struct gg_session_private *p = sess->private_data;
    -
    - p->socket_failure = failure;
    -
    - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_socket_manager_error() unable to"
    - " create pipes (errno=%d, %s)\n", errno,
    - strerror(errno));
    - return;
    - }
    -
    - p->socket_is_external = 0;
    - sess->fd = pipes[1];
    - sess->check = GG_CHECK_READ;
    - sess->state = GG_STATE_ERROR;
    - if (send(pipes[0], &dummy, sizeof(dummy), 0) != sizeof(dummy)) {
    - gg_debug(GG_DEBUG_MISC, "// gg_socket_manager_error() unable to"
    - " send via pipe (errno=%d, %s)\n", errno,
    - strerror(errno));
    - return;
    - }
    - close(pipes[0]);
    -}
    -
    -int gg_compat_feature_is_enabled(struct gg_session *sess, gg_compat_feature_t feature)
    -{
    - gg_compat_t level;
    -
    - if (sess == NULL)
    - return 0;
    -
    - level = sess->private_data->compatibility;
    -
    - switch (feature) {
    - case GG_COMPAT_FEATURE_ACK_EVENT:
    - case GG_COMPAT_FEATURE_LEGACY_CONFER:
    - return (level < GG_COMPAT_1_12_0);
    - }
    -
    - return 0;
    -}
    -
    -static gg_msg_list_t * gg_compat_find_sent_message(struct gg_session *sess, int seq, int remove)
    -{
    - struct gg_session_private *p = sess->private_data;
    - gg_msg_list_t *it, *previous = NULL;
    -
    - for (it = p->sent_messages; it; it = it->next) {
    - if (it->seq == seq)
    - break;
    - else
    - previous = it;
    - }
    -
    - if (remove && it) {
    - if (previous == NULL)
    - p->sent_messages = it->next;
    - else
    - previous->next = it->next;
    - }
    -
    - return it;
    -}
    -
    -static void gg_compat_message_cleanup(struct gg_session *sess)
    -{
    - struct gg_session_private *p = sess->private_data;
    -
    - while (p->sent_messages) {
    - gg_msg_list_t *next = p->sent_messages->next;
    - free(p->sent_messages->recipients);
    - free(p->sent_messages);
    - p->sent_messages = next;
    - }
    -}
    -
    -static void gg_compat_message_sent(struct gg_session *sess, int seq, size_t recipients_count, uin_t *recipients)
    -{
    - struct gg_session_private *p = sess->private_data;
    - gg_msg_list_t *sm;
    - uin_t *new_recipients;
    - size_t old_count, i;
    -
    - if (sess->protocol_version < GG_PROTOCOL_VERSION_110)
    - return;
    -
    - if (!gg_compat_feature_is_enabled(sess, GG_COMPAT_FEATURE_ACK_EVENT))
    - return;
    -
    - sm = gg_compat_find_sent_message(sess, seq, 0);
    - if (!sm) {
    - sm = gg_new0(sizeof(gg_msg_list_t));
    - if (!sm)
    - return;
    - sm->next = p->sent_messages;
    - p->sent_messages = sm;
    - }
    -
    - sm->seq = seq;
    - old_count = sm->recipients_count;
    - sm->recipients_count += recipients_count;
    -
    - new_recipients = realloc(sm->recipients, sizeof(uin_t) * sm->recipients_count);
    - if (new_recipients == NULL) {
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "// gg_compat_message_sent() not enough memory\n");
    - return;
    - }
    - sm->recipients = new_recipients;
    -
    - for (i = 0; i < recipients_count; i++)
    - sm->recipients[old_count + i] = recipients[i];
    -}
    -
    -void gg_compat_message_ack(struct gg_session *sess, int seq)
    -{
    - gg_msg_list_t *sm;
    - size_t i;
    -
    - if (sess->protocol_version < GG_PROTOCOL_VERSION_110)
    - return;
    -
    - if (!gg_compat_feature_is_enabled(sess, GG_COMPAT_FEATURE_ACK_EVENT))
    - return;
    -
    - sm = gg_compat_find_sent_message(sess, seq, 1);
    - if (!sm)
    - return;
    -
    - for (i = 0; i < sm->recipients_count; i++) {
    - struct gg_event *qev;
    -
    - qev = gg_eventqueue_add(sess);
    -
    - qev->type = GG_EVENT_ACK;
    - qev->event.ack.status = GG_ACK_DELIVERED;
    - qev->event.ack.recipient = sm->recipients[i];
    - qev->event.ack.seq = seq;
    - }
    -
    - free(sm->recipients);
    - free(sm);
    -}
    -
    -/**
    - * Odbiera nowo utworzone gniazdo TCP/TLS.
    - *
    - * Po wywołaniu tej funkcji należy zacząć obserwować deskryptor sesji (nawet
    - * w przypadku niepowodzenia).
    - *
    - * Jeżeli gniazdo nie zostanie obsłużone, należy je zniszczyć.
    - *
    - * \param handle Uchwyt gniazda
    - * \param priv Dane prywatne biblioteki libgadu
    - * \param fd Deskryptor nowo utworzonego gniazda, lub -1 w przypadku błędu
    - *
    - * \return Wartość różna od zera, jeżeli gniazdo zostało obsłużone, 0 w przeciwnym przypadku
    - *
    - * \ingroup socketmanager
    - */
    -int gg_socket_manager_connected(void *handle, void *priv, int fd)
    -{
    - struct gg_session *sess = priv;
    - struct gg_session_private *p = sess->private_data;
    -
    - if (p->socket_handle != handle) {
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "// gg_socket_manager_connected() invalid handle\n");
    - return 0;
    - }
    -
    - sess->fd = -1;
    -
    - if (fd < 0) {
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "// gg_socket_manager_connected() connection error\n");
    - p->socket_handle = NULL;
    - gg_socket_manager_error(sess, GG_FAILURE_CONNECTING);
    - return 0;
    - }
    -
    - if (p->socket_next_state == GG_STATE_TLS_NEGOTIATION) {
    - if (gg_session_init_ssl(sess) == -1) {
    - gg_debug_session(sess, GG_DEBUG_MISC | GG_DEBUG_ERROR,
    - "// gg_socket_manager_connected() couldn't "
    - "initialize ssl\n");
    - p->socket_handle = NULL;
    - gg_socket_manager_error(sess, GG_FAILURE_TLS);
    - return 0;
    - }
    - }
    -
    - p->socket_is_external = 1;
    - sess->fd = fd;
    - sess->timeout = GG_DEFAULT_TIMEOUT;
    - sess->state = p->socket_next_state;
    -
    - gg_debug_session(sess, GG_DEBUG_MISC, "// next state=%s\n",
    - gg_debug_state(p->socket_next_state));
    -
    - if (p->socket_next_state == GG_STATE_READING_KEY)
    - sess->check = GG_CHECK_READ;
    - else
    - sess->check = GG_CHECK_WRITE;
    -
    - return 1;
    -}
    -
    -/*
    - * Local variables:
    - * c-indentation-style: k&r
    - * c-basic-offset: 8
    - * indent-tabs-mode: notnil
    - * End:
    - *
    - * vim: shiftwidth=8:
    - */
    --- a/libpurple/protocols/gg/lib/libgadu.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,2639 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl>
    - * Robert J. Woźny <speedy@ziew.org>
    - * Arkadiusz Miśkiewicz <arekm@pld-linux.org>
    - * Tomasz Chiliński <chilek@chilan.com>
    - * Piotr Wysocki <wysek@linux.bydg.org>
    - * Dawid Jarosz <dawjar@poczta.onet.pl>
    - * Jakub Zawadzki <darkjames@darkjames.ath.cx>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file libgadu.h
    - *
    - * \brief Główny plik nagłówkowy biblioteki
    - */
    -
    -#ifndef LIBGADU_LIBGADU_H
    -#define LIBGADU_LIBGADU_H
    -
    -#ifdef _WIN32
    -#pragma pack(push, 1)
    -#endif
    -
    -#ifdef __cplusplus
    -extern "C" {
    -#endif
    -
    -#include <sys/types.h>
    -#include <stdio.h>
    -#include <stdarg.h>
    -
    -/** \cond ignore */
    -
    -/* Defined if libgadu was compiled for bigendian machine. */
    -#undef GG_CONFIG_BIGENDIAN
    -
    -/* Defined if this machine has gethostbyname_r(). */
    -#undef GG_CONFIG_HAVE_GETHOSTBYNAME_R
    -
    -/* Defined if libgadu was compiled and linked with fork support. */
    -#undef GG_CONFIG_HAVE_FORK
    -
    -/* Defined if libgadu was compiled and linked with pthread support. */
    -#undef GG_CONFIG_HAVE_PTHREAD
    -
    -/* Defined if pthread resolver is the default one. */
    -#undef GG_CONFIG_PTHREAD_DEFAULT
    -
    -/* Defined if this machine has C99-compiliant vsnprintf(). */
    -#undef GG_CONFIG_HAVE_C99_VSNPRINTF
    -
    -/* Defined if this machine has va_copy(). */
    -#undef GG_CONFIG_HAVE_VA_COPY
    -
    -/* Defined if this machine has __va_copy(). */
    -#undef GG_CONFIG_HAVE___VA_COPY
    -
    -/* Defined if this machine supports long long. */
    -#undef GG_CONFIG_HAVE_LONG_LONG
    -
    -/* Defined if libgadu was compiled and linked with GnuTLS support. */
    -#undef GG_CONFIG_HAVE_GNUTLS
    -
    -/* Defined if libgadu was compiled and linked with OpenSSL support. */
    -#undef GG_CONFIG_HAVE_OPENSSL
    -
    -/* Defined if libgadu was compiled and linked with zlib support. */
    -#undef GG_CONFIG_HAVE_ZLIB
    -
    -/* Defined if uintX_t types are defined in <stdint.h>. */
    -#undef GG_CONFIG_HAVE_STDINT_H
    -
    -/* Defined if uintX_t types are defined in <inttypes.h>. */
    -#undef GG_CONFIG_HAVE_INTTYPES_H
    -
    -/* Defined if uintX_t types are defined in <sys/inttypes.h>. */
    -#undef GG_CONFIG_HAVE_SYS_INTTYPES_H
    -
    -/* Defined if uintX_t types are defined in <sys/int_types.h>. */
    -#undef GG_CONFIG_HAVE_SYS_INT_TYPES_H
    -
    -/* Defined if uintX_t types are defined in <sys/types.h>. */
    -#undef GG_CONFIG_HAVE_SYS_TYPES_H
    -
    -/* Defined if this machine has uint64_t. */
    -#undef GG_CONFIG_HAVE_UINT64_T
    -
    -/* Defined if libgadu is GPL compliant (was not linked with OpenSSL or any
    - other non-GPL compliant library support). */
    -#undef GG_CONFIG_IS_GPL_COMPLIANT
    -
    -#include "config.h"
    -
    -#ifdef GG_CONFIG_HAVE_OPENSSL
    -#include <openssl/ssl.h>
    -#endif
    -
    -#ifdef GG_CONFIG_HAVE_STDINT_H
    -#include <stdint.h>
    -#else
    -# ifdef GG_CONFIG_HAVE_INTTYPES_H
    -# include <inttypes.h>
    -# else
    -# ifdef GG_CONFIG_HAVE_SYS_INTTYPES_H
    -# include <sys/inttypes.h>
    -# else
    -# ifdef GG_CONFIG_HAVE_SYS_INT_TYPES_H
    -# include <sys/int_types.h>
    -# else
    -# ifdef GG_CONFIG_HAVE_SYS_TYPES_H
    -# include <sys/types.h>
    -# else
    -
    -/* ISO C 9X: 7.18 Integer types <stdint.h> */
    -
    -typedef unsigned char uint8_t;
    -typedef unsigned short uint16_t;
    -typedef unsigned int uint32_t;
    -
    -# endif
    -# endif
    -# endif
    -# endif
    -#endif
    -
    -#ifndef GG_CONFIG_HAVE_UINT64_T
    -typedef unsigned long long uint64_t;
    -#endif
    -
    -#ifdef _MSC_VER
    -#include <BaseTsd.h>
    -typedef SSIZE_T ssize_t;
    -#endif
    -
    -#if defined(__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4))
    -# define GG_GNUC_PRINTF(format_idx, arg_idx) \
    - __attribute__((format (printf, (format_idx), (arg_idx))))
    -#else
    -# define GG_GNUC_PRINTF(format_idx, arg_idx)
    -#endif
    -
    -/** \endcond */
    -
    -/**
    - * Numer Gadu-Gadu.
    - */
    -typedef uint32_t uin_t;
    -
    -/**
    - * Identyfikator połączenia bezpośredniego Gadu-Gadu 7.x.
    - */
    -typedef struct {
    - uint8_t id[8];
    -} gg_dcc7_id_t;
    -
    -/**
    - * Identyfikator sesji multilogowania.
    - */
    -typedef struct {
    - uint8_t id[8];
    -} gg_multilogon_id_t;
    -
    -/**
    - * Makro deklarujące pola wspólne dla struktur sesji.
    - */
    -#define gg_common_head(x) \
    - int fd; /**< Obserwowany deskryptor */ \
    - int check; /**< Informacja o żądaniu odczytu/zapisu (patrz \ref gg_check_t) */ \
    - int state; /**< Aktualny stan połączenia (patrz \ref gg_state_t) */ \
    - int error; /**< Kod błędu dla \c GG_STATE_ERROR (patrz \ref gg_error_t) */ \
    - int type; /**< Rodzaj sesji (patrz \ref gg_session_t) */ \
    - int id; /**< Identyfikator sesji */ \
    - int timeout; /**< Czas pozostały do zakończenia stanu */ \
    - int (*callback)(x*); /**< Funkcja zwrotna */ \
    - void (*destroy)(x*); /**< Funkcja zwalniania zasobów */
    -
    -/**
    - * Struktura wspólna dla wszystkich sesji i połączeń. Pozwala na proste
    - * rzutowanie niezależne od rodzaju połączenia.
    - */
    -struct gg_common {
    - gg_common_head(struct gg_common)
    -};
    -
    -struct gg_image_queue;
    -
    -struct gg_dcc7;
    -
    -struct gg_dcc7_relay;
    -
    -struct gg_session_private;
    -
    -/**
    - * Sposób rozwiązywania nazw serwerów.
    - */
    -typedef enum {
    - GG_RESOLVER_DEFAULT = 0, /**< Domyślny sposób rozwiązywania nazw (jeden z poniższych) */
    - GG_RESOLVER_FORK, /**< Rozwiązywanie nazw bazujące na procesach */
    - GG_RESOLVER_PTHREAD, /**< Rozwiązywanie nazw bazujące na wątkach */
    - GG_RESOLVER_CUSTOM, /**< Funkcje rozwiązywania nazw dostarczone przed aplikację */
    - GG_RESOLVER_WIN32, /**< Rozwiązywanie nazw bazujące na wątkach Win32 */
    - GG_RESOLVER_INVALID = -1 /**< Nieprawidłowy sposób rozwiązywania nazw (wynik \c gg_session_get_resolver) */
    -} gg_resolver_t;
    -
    -/**
    - * Rodzaj kodowania znaków.
    - */
    -typedef enum {
    - GG_ENCODING_CP1250 = 0, /**< Kodowanie CP1250 */
    - GG_ENCODING_UTF8, /**< Kodowanie UTF-8 */
    - GG_ENCODING_INVALID = -1 /**< Nieprawidłowe kodowanie */
    -} gg_encoding_t;
    -
    -/**
    - * Stopień kompatybilności ze starymi wersjami API.
    - */
    -typedef enum {
    - GG_COMPAT_LEGACY = 0, /**< Całkowita kompatybilność (nie wyłącza żadnych funkcji) */
    - GG_COMPAT_1_12_0 = 1 /**< Wyłącza: dostarczanie eventów GG_EVENT_ACK, stary format konferencji */
    -} gg_compat_t;
    -
    -/**
    - * Flaga połączenia szyfrowanego.
    - *
    - * \ingroup login
    - */
    -typedef enum {
    - GG_SSL_DISABLED = 0, /**< Połączenie SSL wyłączone */
    - GG_SSL_ENABLED, /**< Połączenie SSL włączone gdy dostępne. Błędny certyfikat serwera nie powoduje odrzucenia połączenia. */
    - GG_SSL_REQUIRED /**< Połączenie SSL wymagane. Błędny certyfikat serwera powoduje odrzucenie połączenia. */
    -} gg_ssl_t;
    -
    -/**
    - * Sesja Gadu-Gadu.
    - *
    - * Tworzona przez funkcję \c gg_login(), zwalniana przez \c gg_free_session().
    - *
    - * \ingroup login
    - */
    -struct gg_session {
    - gg_common_head(struct gg_session)
    -
    - int async; /**< Flaga połączenia asynchronicznego */
    - int pid; /**< Numer procesu rozwiązującego nazwę serwera */
    - int port; /**< Port serwera */
    - int seq; /**< Numer sekwencyjny ostatniej wiadomości */
    - int last_pong; /**< Czas otrzymania ostatniej ramki utrzymaniowej */
    - int last_event; /**< Czas otrzymania ostatniego pakietu */
    -
    - struct gg_event *event; /**< Zdarzenie po wywołaniu \c callback */
    -
    - uint32_t proxy_addr; /**< Adres serwera pośredniczącego */
    - uint16_t proxy_port; /**< Port serwera pośredniczącego */
    -
    - uint32_t hub_addr; /**< Adres huba po rozwiązaniu nazwy */
    - uint32_t server_addr; /**< Adres serwera otrzymany od huba */
    -
    - uint32_t client_addr; /**< Adres gniazda dla połączeń bezpośrednich */
    - uint16_t client_port; /**< Port gniazda dla połączeń bezpośrednich */
    -
    - uint32_t external_addr; /**< Publiczny adres dla połączeń bezpośrednich */
    - uint16_t external_port; /**< Publiczny port dla połączeń bezpośrednich */
    -
    - uin_t uin; /**< Własny numer Gadu-Gadu */
    - char *password; /**< Hasło (zwalniane po użyciu) */
    -
    - int initial_status; /**< Początkowy status */
    - int status; /**< Aktualny status */
    -
    - char *recv_buf; /**< Bufor na odbierane pakiety. Wskaźnik zawsze maksymalnie wyrównany, tak jak w wyniku działania \c malloc(). */
    - int recv_done; /**< Liczba wczytanych bajtów pakietu */
    - int recv_left; /**< Liczba pozostałych do wczytania bajtów pakietu */
    -
    - int protocol_version; /**< Wersja protokołu (bez flag) */
    - char *client_version; /**< Wersja klienta */
    - int last_sysmsg; /**< Numer ostatniej wiadomości systemowej */
    -
    - char *initial_descr; /**< Początkowy opis statusu */
    -
    - void *resolver; /**< Dane prywatne procesu lub wątku rozwiązującego nazwę serwera */
    -
    -#ifndef DOXYGEN
    - char *header_buf; /**< Bufor na początek nagłówka pakietu (nieaktualne) */
    - unsigned int header_done; /**< Liczba wczytanych bajtów nagłówka pakietu (nieaktualne) */
    -#endif
    -
    -#ifdef GG_CONFIG_HAVE_OPENSSL
    - SSL *ssl; /**< Struktura TLS */
    - SSL_CTX *ssl_ctx; /**< Kontekst sesji TLS */
    -#else
    - void *ssl; /**< Struktura TLS */
    - void *ssl_ctx; /**< Kontekst sesji TLS */
    -#endif
    -
    - int image_size; /**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
    -
    - char *userlist_reply; /**< Bufor z odbieraną listą kontaktów */
    -
    - int userlist_blocks; /**< Liczba części listy kontaktów */
    -
    - struct gg_image_queue *images; /**< Lista wczytywanych obrazków */
    -
    - int hash_type; /**< Rodzaj funkcji skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1) */
    -
    - char *send_buf; /**< Bufor z danymi do wysłania */
    - int send_left; /**< Liczba bajtów do wysłania */
    -
    - struct gg_dcc7 *dcc7_list; /**< Lista połączeń bezpośrednich skojarzonych z sesją */
    -
    - int soft_timeout; /**< Flaga mówiąca, że po przekroczeniu \c timeout należy wywołać \c gg_watch_fd() */
    -
    - int protocol_flags; /**< Flagi protokołu */
    -
    - gg_encoding_t encoding; /**< Rodzaj kodowania znaków */
    -
    - gg_resolver_t resolver_type; /**< Sposób rozwiązywania nazw serwerów */
    - int (*resolver_start)(int *fd, void **private_data, const char *hostname); /**< Funkcja rozpoczynająca rozwiązywanie nazwy */
    - void (*resolver_cleanup)(void **private_data, int force); /**< Funkcja zwalniająca zasoby po rozwiązaniu nazwy */
    -
    - int protocol_features; /**< Opcje protokołu */
    - int status_flags; /**< Flagi statusu */
    - int recv_msg_count; /**< Liczba odebranych wiadomości */
    -
    - const char *resolver_host; /**< Nazwa do rozwiązania */
    - struct in_addr *resolver_result; /**< Wynik rozwiązywania nazwy */
    - unsigned int resolver_index; /**< Indeks aktualnie obsługiwanego wyniku rozwiązywania nazwy */
    - unsigned int resolver_count; /**< Liczba wyników rozwiązywania nazwy */
    -
    - uint16_t connect_port[2]; /**< Lista portów do połączenia */
    - unsigned int connect_index; /**< Indeks aktualnie obsługiwanego portu */
    -
    - char *connect_host; /**< Adres serwera Gadu-Gadu, z którym się łączymy */
    - gg_ssl_t ssl_flag; /**< Flaga połączenia szyfrowanego */
    -
    - struct gg_session_private *private_data; /**< Prywatne dane sesji, nie udostępnione w API */
    -};
    -
    -/**
    - * Połączenie HTTP.
    - *
    - * Tworzone przez \c gg_http_connect(), zwalniane przez \c gg_http_free().
    - *
    - * \ingroup http
    - */
    -struct gg_http {
    - gg_common_head(struct gg_http)
    -
    - int async; /**< Flaga połączenia asynchronicznego */
    - int pid; /**< Identyfikator procesu rozwiązującego nazwę serwera */
    - int port; /**< Port */
    -
    - char *query; /**< Zapytanie HTTP */
    - char *header; /**< Odebrany nagłówek */
    - int header_size; /**< Rozmiar wczytanego nagłówka */
    - char *body; /**< Odebrana strona */
    - unsigned int body_size; /**< Rozmiar strony */
    -
    - void *data; /**< Dane prywatne usługi HTTP */
    -
    - char *user_data; /**< Dane prywatne użytkownika (nie są zwalniane) */
    -
    - void *resolver; /**< Dane prywatne procesu lub wątku rozwiązującego nazwę */
    -
    - unsigned int body_done; /**< Liczba odebranych bajtów strony */
    -
    - gg_resolver_t resolver_type; /**< Sposób rozwiązywania nazw serwerów */
    - int (*resolver_start)(int *fd, void **private_data, const char *hostname); /**< Funkcja rozpoczynająca rozwiązywanie nazwy */
    - void (*resolver_cleanup)(void **private_data, int force); /**< Funkcja zwalniająca zasoby po rozwiązaniu nazwy */
    -};
    -
    -/** \cond ignore */
    -
    -#ifdef __GNUC__
    -#define GG_PACKED __attribute__ ((packed))
    -#ifndef GG_IGNORE_DEPRECATED
    -#define GG_DEPRECATED __attribute__ ((deprecated))
    -#else
    -#define GG_DEPRECATED
    -#endif
    -#else
    -#define GG_PACKED
    -#define GG_DEPRECATED
    -#endif
    -
    -/** \endcond */
    -
    -#define GG_MAX_PATH 276 /**< Maksymalny rozmiar nazwy pliku w strukturze \c gg_file_info */
    -
    -/**
    - * Odpowiednik struktury WIN32_FIND_DATA z API WIN32.
    - *
    - * Wykorzystywana przy połączeniach bezpośrednich do wersji Gadu-Gadu 6.x.
    - */
    -struct gg_file_info {
    - uint32_t mode; /**< dwFileAttributes */
    - uint32_t ctime[2]; /**< ftCreationTime */
    - uint32_t atime[2]; /**< ftLastAccessTime */
    - uint32_t mtime[2]; /**< ftLastWriteTime */
    - uint32_t size_hi; /**< nFileSizeHigh */
    - uint32_t size; /**< nFileSizeLow */
    - uint32_t reserved0; /**< dwReserved0 */
    - uint32_t reserved1; /**< dwReserved1 */
    - unsigned char filename[GG_MAX_PATH - 14]; /**< cFileName */
    - unsigned char short_filename[14]; /**< cAlternateFileName */
    -} /** \cond ignore */ GG_PACKED /** \endcond */;
    -
    -/**
    - * Połączenie bezpośrednie do wersji Gadu-Gadu 6.x.
    - *
    - * Tworzone przez \c gg_dcc_socket_create(), \c gg_dcc_get_file(),
    - * \c gg_dcc_send_file() lub \c gg_dcc_voice_chat(), zwalniane przez
    - * \c gg_dcc_free().
    - *
    - * \ingroup dcc6
    - */
    -struct gg_dcc {
    - gg_common_head(struct gg_dcc)
    -
    - struct gg_event *event; /**< Zdarzenie po wywołaniu \c callback */
    -
    - int active; /**< Flaga połączenia aktywnego (nieużywana) */
    - int port; /**< Port gniazda nasłuchującego */
    - uin_t uin; /**< Własny numer Gadu-Gadu */
    - uin_t peer_uin; /**< Numer Gadu-Gadu drugiej strony połączenia */
    - int file_fd; /**< deskryptor pliku */
    - unsigned int offset; /**< Położenie w pliku */
    - unsigned int chunk_size;
    - /**< Rozmiar kawałka pliku */
    - unsigned int chunk_offset;
    - /**< Położenie w aktualnym kawałku pliku */
    - struct gg_file_info file_info;
    - /**< Informacje o pliku */
    - int established; /**< Flaga ustanowienia połączenia */
    - char *voice_buf; /**< Bufor na pakiet połączenia głosowego */
    - int incoming; /**< Flaga połączenia przychodzącego */
    - char *chunk_buf; /**< Bufor na fragment danych */
    - uint32_t remote_addr; /**< Adres drugiej strony */
    - uint16_t remote_port; /**< Port drugiej strony */
    -};
    -
    -#define GG_DCC7_HASH_LEN 20 /**< Maksymalny rozmiar skrótu pliku w połączeniach bezpośrenich */
    -#define GG_DCC7_FILENAME_LEN 255 /**< Maksymalny rozmiar nazwy pliku w połączeniach bezpośrednich */
    -#define GG_DCC7_INFO_LEN 32 /**< Maksymalny rozmiar informacji o połączeniach bezpośrednich */
    -#define GG_DCC7_INFO_HASH_LEN 32 /**< Maksymalny rozmiar skrótu ip informacji o połączeniach bezpośrednich */
    -
    -/**
    - * Połączenie bezpośrednie od wersji Gadu-Gadu 7.x.
    - *
    - * \ingroup dcc7
    - */
    -struct gg_dcc7 {
    - gg_common_head(struct gg_dcc7)
    -
    - gg_dcc7_id_t cid; /**< Identyfikator połączenia */
    -
    - struct gg_event *event; /**< Struktura zdarzenia */
    -
    - uin_t uin; /**< Własny numer Gadu-Gadu */
    - uin_t peer_uin; /**< Numer Gadu-Gadu drugiej strony połączenia */
    -
    - int file_fd; /**< Deskryptor przesyłanego pliku */
    - unsigned int offset; /**< Aktualne położenie w przesyłanym pliku */
    - unsigned int size; /**< Rozmiar przesyłanego pliku */
    - unsigned char filename[GG_DCC7_FILENAME_LEN + 1];
    - /**< Nazwa przesyłanego pliku */
    - unsigned char hash[GG_DCC7_HASH_LEN];
    - /**< Skrót SHA1 przesyłanego pliku */
    -
    - int dcc_type; /**< Rodzaj połączenia bezpośredniego */
    - int established; /**< Flaga ustanowienia połączenia */
    - int incoming; /**< Flaga połączenia przychodzącego */
    - int reverse; /**< Flaga połączenia zwrotnego */
    -
    - uint32_t local_addr; /**< Adres lokalny */
    - uint16_t local_port; /**< Port lokalny */
    -
    - uint32_t remote_addr; /**< Adres drugiej strony */
    - uint16_t remote_port; /**< Port drugiej strony */
    -
    - struct gg_session *sess;
    - /**< Sesja do której przypisano połączenie */
    - struct gg_dcc7 *next; /**< Następne połączenie w liście */
    -
    - int soft_timeout; /**< Flaga mówiąca, że po przekroczeniu \c timeout należy wywołać \c gg_dcc7_watch_fd() */
    - int seek; /**< Flaga mówiąca, że można zmieniać położenie w wysyłanym pliku */
    -
    - void *resolver; /**< Dane prywatne procesu lub wątku rozwiązującego nazwę serwera */
    -
    - int relay; /**< Flaga mówiąca, że laczymy sie przez serwer */
    - int relay_index; /**< Numer serwera pośredniczącego, do którego się łączymy */
    - int relay_count; /**< Rozmiar listy serwerów pośredniczących */
    - struct gg_dcc7_relay *relay_list; /**< Lista serwerów pośredniczących */
    -};
    -
    -/**
    - * Rodzaj sesji.
    - */
    -enum gg_session_t {
    - GG_SESSION_GG = 1, /**< Połączenie z serwerem Gadu-Gadu */
    - GG_SESSION_HTTP, /**< Połączenie HTTP */
    - GG_SESSION_SEARCH, /**< Wyszukiwanie w katalogu publicznym (nieaktualne) */
    - GG_SESSION_REGISTER, /**< Rejestracja nowego konta */
    - GG_SESSION_REMIND, /**< Przypominanie hasła */
    - GG_SESSION_PASSWD, /**< Zmiana hasła */
    - GG_SESSION_CHANGE, /**< Zmiana informacji w katalogu publicznym (nieaktualne) */
    - GG_SESSION_DCC, /**< Połączenie bezpośrednie (do wersji 6.x) */
    - GG_SESSION_DCC_SOCKET, /**< Gniazdo nasłuchujące (do wersji 6.x) */
    - GG_SESSION_DCC_SEND, /**< Wysyłanie pliku (do wersji 6.x) */
    - GG_SESSION_DCC_GET, /**< Odbieranie pliku (do wersji 6.x) */
    - GG_SESSION_DCC_VOICE, /**< Rozmowa głosowa (do wersji 6.x) */
    - GG_SESSION_USERLIST_GET, /**< Import listy kontaktów z serwera (nieaktualne) */
    - GG_SESSION_USERLIST_PUT, /**< Eksport listy kontaktów do serwera (nieaktualne) */
    - GG_SESSION_UNREGISTER, /**< Usuwanie konta */
    - GG_SESSION_USERLIST_REMOVE, /**< Usuwanie listy kontaktów z serwera (nieaktualne) */
    - GG_SESSION_TOKEN, /**< Pobieranie tokenu */
    - GG_SESSION_DCC7_SOCKET, /**< Gniazdo nasłuchujące (od wersji 7.x) */
    - GG_SESSION_DCC7_SEND, /**< Wysyłanie pliku (od wersji 7.x) */
    - GG_SESSION_DCC7_GET, /**< Odbieranie pliku (od wersji 7.x) */
    - GG_SESSION_DCC7_VOICE, /**< Rozmowa głosowa (od wersji 7.x) */
    -
    - GG_SESSION_USER0 = 256, /**< Rodzaj zadeklarowany dla użytkownika */
    - GG_SESSION_USER1, /**< Rodzaj zadeklarowany dla użytkownika */
    - GG_SESSION_USER2, /**< Rodzaj zadeklarowany dla użytkownika */
    - GG_SESSION_USER3, /**< Rodzaj zadeklarowany dla użytkownika */
    - GG_SESSION_USER4, /**< Rodzaj zadeklarowany dla użytkownika */
    - GG_SESSION_USER5, /**< Rodzaj zadeklarowany dla użytkownika */
    - GG_SESSION_USER6, /**< Rodzaj zadeklarowany dla użytkownika */
    - GG_SESSION_USER7 /**< Rodzaj zadeklarowany dla użytkownika */
    -};
    -
    -/**
    - * Aktualny stan sesji.
    - */
    -enum gg_state_t {
    - /* wspólne */
    - GG_STATE_IDLE = 0, /**< Nie dzieje się nic */
    - GG_STATE_RESOLVING, /**< Oczekiwanie na rozwiązanie nazwy serwera */
    - GG_STATE_CONNECTING, /**< Oczekiwanie na połączenie */
    - GG_STATE_READING_DATA, /**< Oczekiwanie na dane */
    - GG_STATE_ERROR, /**< Kod błędu w polu \c error */
    -
    - /* gg_session */
    - GG_STATE_CONNECTING_HUB, /**< Oczekiwanie na połączenie z hubem */
    - GG_STATE_CONNECTING_GG, /**< Oczekiwanie na połączenie z serwerem */
    - GG_STATE_READING_KEY, /**< Oczekiwanie na klucz */
    - GG_STATE_READING_REPLY, /**< Oczekiwanie na odpowiedź serwera */
    - GG_STATE_CONNECTED, /**< Połączono z serwerem */
    -
    - /* gg_http */
    - GG_STATE_SENDING_QUERY, /**< Wysłano zapytanie HTTP */
    - GG_STATE_READING_HEADER, /**< Oczekiwanie na nagłówek HTTP */
    - GG_STATE_PARSING, /**< Przetwarzanie danych */
    - GG_STATE_DONE, /**< Połączenie zakończone */
    -
    - /* gg_dcc */
    - GG_STATE_LISTENING, /* czeka na połączenia */
    - GG_STATE_READING_UIN_1, /* czeka na uin peera */
    - GG_STATE_READING_UIN_2, /* czeka na swój uin */
    - GG_STATE_SENDING_ACK, /* wysyła potwierdzenie dcc */
    - GG_STATE_READING_ACK, /* czeka na potwierdzenie dcc */
    - GG_STATE_READING_REQUEST, /* czeka na komendę */
    - GG_STATE_SENDING_REQUEST, /* wysyła komendę */
    - GG_STATE_SENDING_FILE_INFO, /* wysyła informacje o pliku */
    - GG_STATE_READING_PRE_FILE_INFO, /* czeka na pakiet przed file_info */
    - GG_STATE_READING_FILE_INFO, /* czeka na informacje o pliku */
    - GG_STATE_SENDING_FILE_ACK, /* wysyła potwierdzenie pliku */
    - GG_STATE_READING_FILE_ACK, /* czeka na potwierdzenie pliku */
    - GG_STATE_SENDING_FILE_HEADER, /* wysyła nagłówek pliku */
    - GG_STATE_READING_FILE_HEADER, /* czeka na nagłówek */
    - GG_STATE_GETTING_FILE, /* odbiera plik */
    - GG_STATE_SENDING_FILE, /* wysyła plik */
    - GG_STATE_READING_VOICE_ACK, /* czeka na potwierdzenie voip */
    - GG_STATE_READING_VOICE_HEADER, /* czeka na rodzaj bloku voip */
    - GG_STATE_READING_VOICE_SIZE, /* czeka na rozmiar bloku voip */
    - GG_STATE_READING_VOICE_DATA, /* czeka na dane voip */
    - GG_STATE_SENDING_VOICE_ACK, /* wysyła potwierdzenie voip */
    - GG_STATE_SENDING_VOICE_REQUEST, /* wysyła żądanie voip */
    - GG_STATE_READING_TYPE, /* czeka na typ połączenia */
    -
    - /* nowe. bez sensu jest to API. */
    - GG_STATE_TLS_NEGOTIATION, /**< Negocjacja połączenia szyfrowanego */
    -
    - GG_STATE_REQUESTING_ID, /**< Oczekiwanie na nadanie identyfikatora połączenia bezpośredniego */
    - GG_STATE_WAITING_FOR_ACCEPT, /**< Oczekiwanie na potwierdzenie lub odrzucenie połączenia bezpośredniego */
    - GG_STATE_WAITING_FOR_INFO, /**< Oczekiwanie na informacje o połączeniu bezpośrednim */
    -
    - GG_STATE_READING_ID, /**< Odebranie identyfikatora połączenia bezpośredniego */
    - GG_STATE_SENDING_ID, /**< Wysłano identyfikator połączenia bezpośredniego */
    - GG_STATE_RESOLVING_GG, /**< Oczekiwanie na rozwiązanie nazwy serwera Gadu-Gadu */
    -
    - GG_STATE_RESOLVING_RELAY, /**< Oczekiwanie na rozwiązanie nazwy serwera pośredniczącego */
    - GG_STATE_CONNECTING_RELAY, /**< Oczekiwanie na połączenie z serwerem pośredniczącym */
    - GG_STATE_READING_RELAY, /**< Odbieranie danych */
    -
    - GG_STATE_DISCONNECTING, /**< Oczekiwanie na potwierdzenie rozłączenia */
    -
    - GG_STATE_CONNECT_HUB, /**< Nawiązanie połączenia z hubem */
    - GG_STATE_CONNECT_PROXY_HUB,
    - GG_STATE_CONNECT_GG, /**< Nawiązanie połączenia z serwerem */
    - GG_STATE_CONNECT_PROXY_GG,
    - GG_STATE_CONNECTING_PROXY_HUB,
    - GG_STATE_CONNECTING_PROXY_GG,
    - GG_STATE_RESOLVE_HUB_SYNC,
    - GG_STATE_RESOLVE_HUB_ASYNC,
    - GG_STATE_RESOLVE_PROXY_HUB_SYNC,
    - GG_STATE_RESOLVE_PROXY_HUB_ASYNC,
    - GG_STATE_RESOLVE_PROXY_GG_SYNC,
    - GG_STATE_RESOLVE_PROXY_GG_ASYNC,
    - GG_STATE_RESOLVE_GG_SYNC,
    - GG_STATE_RESOLVE_GG_ASYNC,
    - GG_STATE_RESOLVING_HUB,
    - GG_STATE_RESOLVING_PROXY_HUB,
    - GG_STATE_RESOLVING_PROXY_GG,
    - GG_STATE_SEND_HUB,
    - GG_STATE_SEND_PROXY_HUB,
    - GG_STATE_SEND_PROXY_GG,
    - GG_STATE_SENDING_HUB,
    - GG_STATE_SENDING_PROXY_HUB,
    - GG_STATE_SENDING_PROXY_GG,
    - GG_STATE_READING_HUB,
    - GG_STATE_READING_PROXY_HUB,
    - GG_STATE_READING_PROXY_GG,
    -};
    -
    -/**
    - * Informacja o tym, czy biblioteka chce zapisywać i/lub czytać
    - * z deskryptora. Maska bitowa.
    - *
    - * \ingroup events
    - */
    -enum gg_check_t {
    - GG_CHECK_NONE = 0, /**< Nie sprawdzaj niczego */
    - GG_CHECK_WRITE = 1, /**< Sprawdź możliwość zapisu */
    - GG_CHECK_READ = 2 /**< Sprawdź możliwość odczytu */
    -};
    -
    -/**
    - * Metody nawiązywania połączeń TCP/TLS.
    - *
    - * \ingroup socketmanager
    - */
    -typedef enum {
    - GG_SOCKET_MANAGER_TYPE_INTERNAL = 0, /**< Wewnętrzna obsługa gniazd (domyślne). */
    - GG_SOCKET_MANAGER_TYPE_TCP, /**< Dostarczona przez aplikację - tylko obsługa TCP. */
    - GG_SOCKET_MANAGER_TYPE_TLS /**< Dostarczona przez aplikację - obsługa zarówno TCP, jak i TLS. */
    -} gg_socket_manager_type_t;
    -
    -/**
    - * Funkcja dostarczona przez aplikację, tworząca nowe gniazdo TCP/TLS.
    - *
    - * Po nawiązaniu połączenia aplikacja musi wywołać gg_socket_manager_connected.
    - * Jeżeli połączenie jest asynchroniczne, wywołanie musi nastąpić po wyjściu z
    - * kontekstu tej funkcji. Dla połączeń synchronicznych z kolei, musi nastąpić
    - * jeszcze przed wyjściem z kontekstu.
    - *
    - * \param cb_data Dane prywatne aplikacji
    - * \param host Nazwa hosta
    - * \param port Numer portu
    - * \param is_tls Flaga określająca, czy ma zostać nawiązane połączenie TLS
    - * \param is_async Flaga określająca połączenie asynchroniczne (patrz szczegóły powyżej)
    - * \param priv Dane prywatne biblioteki libgadu (do przekazania do gg_socket_manager_connected)
    - *
    - * \return Uchwyt gniazda
    - *
    - * \ingroup socketmanager
    - */
    -typedef void* (*gg_socket_manager_connect_cb_t)(void *cb_data, const char *host, int port, int is_tls, int is_async, void *priv);
    -
    -/**
    - * Niszczy gniazdo i zwalnia wszystkie powiązane z nim zasoby.
    - *
    - * \param cb_data Dane prywatne aplikacji
    - * \param handle Uchwyt gniazda
    - *
    - * \ingroup socketmanager
    - */
    -typedef void (*gg_socket_manager_close_cb_t)(void *cb_data, void *handle);
    -
    -/**
    - * Odbiera z gniazda dane binarne.
    - *
    - * Funkcja powinna zajmować się obsługą TLS, jeżeli gniazdo jest w takim trybie.
    - *
    - * \param cb_data Dane prywatne aplikacji
    - * \param handle Uchwyt gniazda
    - * \param buffer Bufor do zapisu danych
    - * \param bufsize Rozmiar bufora
    - *
    - * \return Ilość zapisanych danych, lub -1 (oraz ustawiony errno) w przypadku niepowodzenia
    - *
    - * \ingroup socketmanager
    - */
    -typedef ssize_t (*gg_socket_manager_read_cb_t)(void *cb_data, void *handle, unsigned char *buffer, size_t bufsize);
    -
    -/**
    - * Wysyła przez gniazdo dane binarne.
    - *
    - * Funkcja powinna zajmować się obsługą TLS, jeżeli gniazdo jest w takim trybie.
    - *
    - * \param cb_data Dane prywatne aplikacji
    - * \param handle Uchwyt gniazda
    - * \param data Dane do wysłania
    - * \param length Rozmiar danych
    - *
    - * \return Ilość wysłanych danych, lub -1 (oraz ustawiony errno) w przypadku niepowodzenia
    - *
    - * \ingroup socketmanager
    - */
    -typedef ssize_t (*gg_socket_manager_write_cb_t)(void *cb_data, void *handle, const unsigned char *data, size_t length);
    -
    -/**
    - * Struktura opisująca funkcje zarządzające gniazdami, jeżeli aplikacja sama je
    - * obsługuje.
    - *
    - * \ingroup socketmanager
    - */
    -typedef struct {
    - void *cb_data; /**< Dane prywatne aplikacji */
    - gg_socket_manager_connect_cb_t connect_cb; /**< Funkcja tworząca nowe gniazdo */
    - gg_socket_manager_close_cb_t close_cb; /**< Funkcja niszcząca gniazdo */
    - gg_socket_manager_read_cb_t read_cb; /**< Funkcja odczytująca dane z gniazda */
    - gg_socket_manager_write_cb_t write_cb; /**< Funkcja wysyłająca dane przez gniazdo */
    -#ifndef DOXYGEN
    - void *reserved1;
    - void *reserved2;
    - void *reserved3;
    - void *reserved4;
    -#endif
    -} gg_socket_manager_t;
    -
    -int gg_socket_manager_connected(void *handle, void *priv, int fd);
    -
    -/**
    - * Parametry połączenia z serwerem Gadu-Gadu. Parametry zostały przeniesione
    - * do struktury, by uniknąć zmian API po rozszerzeniu protokołu i dodaniu
    - * kolejnych opcji połączenia. Część parametrów, które nie są już aktualne
    - * lub nie mają znaczenia, została usunięta z dokumentacji.
    - *
    - * \ingroup login
    - */
    -struct gg_login_params {
    - uin_t uin; /**< Numer Gadu-Gadu */
    - char *password; /**< Hasło */
    - int async; /**< Flaga asynchronicznego połączenia (domyślnie nie) */
    - int status; /**< Początkowy status użytkownika (domyślnie \c GG_STATUS_AVAIL) */
    - char *status_descr; /**< Początkowy opis użytkownika (domyślnie brak) */
    - uint32_t server_addr; /**< Adres serwera Gadu-Gadu (domyślnie pobierany automatycznie) */
    - uint16_t server_port; /**< Port serwera Gadu-Gadu (domyślnie pobierany automatycznie) */
    - uint32_t client_addr; /**< Adres połączeń bezpośrednich (domyślnie dobierany automatycznie) */
    - uint16_t client_port; /**< Port połączeń bezpośrednich (domyślnie dobierany automatycznie) */
    - int protocol_version; /**< Wersja protokołu wysyłana do serwera (domyślnie najnowsza obsługiwana) */
    - char *client_version; /**< Wersja klienta wysyłana do serwera (domyślnie najnowsza znana) */
    - int has_audio; /**< Flaga obsługi połączeń głosowych */
    - int last_sysmsg; /**< Numer ostatnio odebranej wiadomości systemowej */
    - uint32_t external_addr; /**< Adres publiczny dla połączeń bezpośrednich (domyślnie dobierany automatycznie) */
    - uint16_t external_port; /**< Port publiczny dla połączeń bezpośrednich (domyślnie dobierany automatycznie) */
    - int tls; /**< Flaga połączenia szyfrowanego (patrz \ref gg_ssl_t) */
    - int image_size; /**< Maksymalny rozmiar obsługiwanych obrazków w kilobajtach */
    -#ifndef DOXYGEN
    - int era_omnix; /**< Flaga udawania klienta Era Omnix (nieaktualna) */
    -#endif
    - int hash_type; /**< Rodzaj skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1, domyślnie SHA1) */
    - gg_encoding_t encoding; /**< Rodzaj kodowania używanego w sesji (domyślnie CP1250) */
    - gg_resolver_t resolver; /**< Sposób rozwiązywania nazw (patrz \ref build-resolver) */
    - int protocol_features; /**< Opcje protokołu (flagi GG_FEATURE_*). */
    - int status_flags; /**< Flagi statusu (flagi GG_STATUS_FLAG_*, patrz \ref status). */
    -
    - unsigned int struct_size; /**< Rozmiar struktury. To pole powinno być inicjowane wartością sizeof(struct gg_login_params) - w przeciwnym przypadku pola za nim nie będą obsługiwane. Pozwala na rozszerzanie struktury bez łamania ABI. */
    -
    - gg_compat_t compatibility; /**< Stopień kompatybilności ze starym API. */
    -
    - char *connect_host; /**< Nazwa hosta (oraz opcjonalnie port, podany po dwukropku) serwera Gadu-Gadu (domyślnie pobierany automatycznie) (patrz pole struct_size). */
    -
    - gg_socket_manager_type_t socket_manager_type; /**< Wybrana metoda nawiązywania połączeń TCP/TLS (domyślnie wewnętrzna) */
    - gg_socket_manager_t socket_manager; /**< Jeżeli wybrano metodę zewnętrzną - konfiguracja jej */
    -
    - char **host_white_list; /**< Lista zakończona wskaźnikiem NULL, domen akceptowanych w odpowiedziach od huba (domyślnie wszystkie do tej pory znane). Używane tylko przy GG_SSL_REQUIRED. Pusta lista wyłącza sprawdzanie. */
    -};
    -
    -#ifdef GG_CONFIG_IS_GPL_COMPLIANT
    -int gg_is_gpl_compliant(void);
    -#endif
    -struct gg_session *gg_login(const struct gg_login_params *p);
    -void gg_free_session(struct gg_session *sess);
    -void gg_logoff(struct gg_session *sess);
    -int gg_change_status(struct gg_session *sess, int status);
    -int gg_change_status_descr(struct gg_session *sess, int status, const char *descr);
    -int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time);
    -int gg_change_status_flags(struct gg_session *sess, int flags);
    -int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message);
    -int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen);
    -int gg_send_message_html(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *html_message);
    -int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message);
    -int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen);
    -int gg_send_message_confer_html(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *html_message);
    -int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len);
    -int gg_ping(struct gg_session *sess);
    -int gg_userlist_request(struct gg_session *sess, char type, const char *request);
    -int gg_userlist100_request(struct gg_session *sess, char type, unsigned int version, char format_type, const char *request);
    -int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32);
    -int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size);
    -int gg_typing_notification(struct gg_session *sess, uin_t recipient, int length);
    -
    -uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len);
    -
    -int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type);
    -gg_resolver_t gg_session_get_resolver(struct gg_session *gs);
    -int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int));
    -
    -int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type);
    -gg_resolver_t gg_http_get_resolver(struct gg_http *gh);
    -int gg_http_set_custom_resolver(struct gg_http *gh, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int));
    -
    -int gg_global_set_resolver(gg_resolver_t type);
    -gg_resolver_t gg_global_get_resolver(void);
    -int gg_global_set_custom_resolver(int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int));
    -
    -int gg_multilogon_disconnect(struct gg_session *gs, gg_multilogon_id_t conn_id);
    -
    -int gg_chat_create(struct gg_session *gs);
    -int gg_chat_invite(struct gg_session *gs, uint64_t id, uin_t *participants, unsigned int participants_count);
    -int gg_chat_leave(struct gg_session *gs, uint64_t id);
    -int gg_chat_send_message(struct gg_session *gs, uint64_t id, const char *message, int is_html);
    -
    -/**
    - * Rodzaj zdarzenia.
    - *
    - * \ingroup events
    - */
    -enum gg_event_t {
    - GG_EVENT_NONE = 0, /**< Nie wydarzyło się nic wartego uwagi */
    - GG_EVENT_MSG, /**< \brief Otrzymano wiadomość. Przekazuje również wiadomości systemowe od numeru 0. */
    - GG_EVENT_NOTIFY, /**< \brief Informacja o statusach osób z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. Ostatni element tablicy zawiera uin równy 0, a pozostałe pola są niezainicjowane. */
    - GG_EVENT_NOTIFY_DESCR, /**< \brief Informacja o statusie opisowym osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */
    - GG_EVENT_STATUS, /**< \brief Zmiana statusu osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */
    - GG_EVENT_ACK, /**< Potwierdzenie doręczenia wiadomości */
    - GG_EVENT_PONG, /**< \brief Utrzymanie połączenia. Obecnie serwer nie wysyła już do klienta ramek utrzymania połączenia, polega wyłącznie na wysyłaniu ramek przez klienta. */
    - GG_EVENT_CONN_FAILED, /**< \brief Nie udało się połączyć */
    - GG_EVENT_CONN_SUCCESS, /**< \brief Połączono z serwerem. Pierwszą rzeczą, jaką należy zrobić jest wysłanie listy kontaktów. */
    - GG_EVENT_DISCONNECT, /**< \brief Serwer zrywa połączenie. Zdarza się, gdy równolegle do serwera podłączy się druga sesja i trzeba zerwać połączenie z pierwszą. */
    -
    - GG_EVENT_DCC_NEW, /**< Nowe połączenie bezpośrednie (6.x) */
    - GG_EVENT_DCC_ERROR, /**< Błąd połączenia bezpośredniego (6.x) */
    - GG_EVENT_DCC_DONE, /**< Zakończono połączenie bezpośrednie (6.x) */
    - GG_EVENT_DCC_CLIENT_ACCEPT, /**< Moment akceptacji klienta w połączeniu bezpośrednim (6.x) */
    - GG_EVENT_DCC_CALLBACK, /**< Zwrotne połączenie bezpośrednie (6.x) */
    - GG_EVENT_DCC_NEED_FILE_INFO, /**< Należy wypełnić \c file_info dla połączenia bezpośredniego (6.x) */
    - GG_EVENT_DCC_NEED_FILE_ACK, /**< Czeka na potwierdzenie pliku w połączeniu bezpośrednim (6.x) */
    - GG_EVENT_DCC_NEED_VOICE_ACK, /**< Czeka na potwierdzenie rozmowy w połączeniu bezpośrednim (6.x) */
    - GG_EVENT_DCC_VOICE_DATA, /**< Dane bezpośredniego połączenia głosowego (6.x) */
    -
    - GG_EVENT_PUBDIR50_SEARCH_REPLY, /**< Odpowiedź katalogu publicznego */
    - GG_EVENT_PUBDIR50_READ, /**< Odczytano własne dane z katalogu publicznego */
    - GG_EVENT_PUBDIR50_WRITE, /**< Zmieniono własne dane w katalogu publicznym */
    -
    - GG_EVENT_STATUS60, /**< Zmiana statusu osoby z listy kontaktów */
    - GG_EVENT_NOTIFY60, /**< Informacja o statusach osób z listy kontaktów. Ostatni element tablicy zawiera uin równy 0, a pozostałe pola są niezainicjowane. */
    - GG_EVENT_USERLIST, /**< Wynik importu lub eksportu listy kontaktów */
    - GG_EVENT_IMAGE_REQUEST, /**< Żądanie przesłania obrazka z wiadomości */
    - GG_EVENT_IMAGE_REPLY, /**< Przysłano obrazek z wiadomości */
    - GG_EVENT_DCC_ACK, /**< Potwierdzenie transmisji w połączeniu bezpośrednim (6.x) */
    -
    - GG_EVENT_DCC7_NEW, /**< Nowe połączenie bezpośrednie (7.x) */
    - GG_EVENT_DCC7_ACCEPT, /**< Zaakceptowano połączenie bezpośrednie (7.x), nowy deskryptor */
    - GG_EVENT_DCC7_REJECT, /**< Odrzucono połączenie bezpośrednie (7.x) */
    - GG_EVENT_DCC7_CONNECTED, /**< Zestawiono połączenie bezpośrednie (7.x), nowy deskryptor */
    - GG_EVENT_DCC7_ERROR, /**< Błąd połączenia bezpośredniego (7.x) */
    - GG_EVENT_DCC7_DONE, /**< Zakończono połączenie bezpośrednie (7.x) */
    - GG_EVENT_DCC7_PENDING, /**< Trwa próba połączenia bezpośredniego (7.x), nowy deskryptor */
    -
    - GG_EVENT_XML_EVENT, /**< Otrzymano komunikat systemowy (7.7) */
    - GG_EVENT_DISCONNECT_ACK, /**< \brief Potwierdzenie zakończenia sesji. Informuje o tym, że zmiana stanu na niedostępny z opisem dotarła do serwera i można zakończyć połączenie TCP. */
    - GG_EVENT_TYPING_NOTIFICATION, /**< Powiadomienie o pisaniu */
    - GG_EVENT_USER_DATA, /**< Informacja o kontaktach */
    - GG_EVENT_MULTILOGON_MSG, /**< Wiadomość wysłana z innej sesji multilogowania */
    - GG_EVENT_MULTILOGON_INFO, /**< Informacja o innych sesjach multilogowania */
    -
    - GG_EVENT_USERLIST100_VERSION, /**< Otrzymano numer wersji listy kontaktów na serwerze (10.0) */
    - GG_EVENT_USERLIST100_REPLY, /**< Wynik importu lub eksportu listy kontaktów (10.0) */
    -
    - GG_EVENT_IMTOKEN, /**< Otrzymano ciąg IMTOKEN (11.0) */
    - GG_EVENT_PONG110, /**< \brief Utrzymanie połączenia (11.0). Może służyć do synchronizacji czasu z serwerem. */
    - GG_EVENT_JSON_EVENT, /**< Otrzymano komunikat systemowy (11.0) */
    - GG_EVENT_ACK110, /**< Potwierdzenie wysłania wiadomości (11.0) */
    -
    - GG_EVENT_CHAT_INFO, /**< Otrzymano informację o konferencji (11.0). */
    - GG_EVENT_CHAT_INFO_GOT_ALL, /**< \brief Informacje o wszystkich konferencjach zostały już wysłane (11.0). Otrzymywany po ostatnim pakiecie \c GG_EVENT_CHAT_INFO */
    - GG_EVENT_CHAT_INFO_UPDATE, /**< \brief Aktualizacja informacji o konferencji (11.0). Dodanie, usunięcie jednego z uczestników. */
    - GG_EVENT_CHAT_CREATED, /**< Potwierdzenie utworzenia konferencji (11.0) */
    - GG_EVENT_CHAT_INVITE_ACK, /**< Potwierdzenie wysłania zaproszenia do konferencji (11.0) */
    -};
    -
    -#define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY
    -
    -/**
    - * Powód nieudanego połączenia.
    - */
    -enum gg_failure_t {
    - GG_FAILURE_RESOLVING = 1, /**< Nie znaleziono serwera */
    - GG_FAILURE_CONNECTING, /**< Błąd połączenia */
    - GG_FAILURE_INVALID, /**< Serwer zwrócił nieprawidłowe dane */
    - GG_FAILURE_READING, /**< Zerwano połączenie podczas odczytu */
    - GG_FAILURE_WRITING, /**< Zerwano połączenie podczas zapisu */
    - GG_FAILURE_PASSWORD, /**< Nieprawidłowe hasło */
    - GG_FAILURE_404, /**< Nieużywane */
    - GG_FAILURE_TLS, /**< Błąd negocjacji szyfrowanego połączenia */
    - GG_FAILURE_NEED_EMAIL, /**< Serwer rozłączył nas z prośbą o zmianę adresu e-mail */
    - GG_FAILURE_INTRUDER, /**< Zbyt wiele prób połączenia z nieprawidłowym hasłem */
    - GG_FAILURE_UNAVAILABLE, /**< Serwery są wyłączone */
    - GG_FAILURE_PROXY, /**< Błąd serwera pośredniczącego */
    - GG_FAILURE_HUB, /**< Błąd połączenia z hubem */
    - GG_FAILURE_INTERNAL, /**< Błąd wewnętrzny */
    -};
    -
    -/**
    - * Kod błędu danej operacji.
    - *
    - * Nie zawiera przesadnie szczegółowych informacji o powodach błędów, by nie
    - * komplikować ich obsługi. Jeśli wymagana jest większa dokładność, należy
    - * sprawdzić zawartość zmiennej systemowej \c errno.
    - */
    -enum gg_error_t {
    - GG_ERROR_RESOLVING = 1, /**< Nie znaleziono hosta */
    - GG_ERROR_CONNECTING, /**< Błąd połączenia */
    - GG_ERROR_READING, /**< Błąd odczytu/odbierania */
    - GG_ERROR_WRITING, /**< Błąd zapisu/wysyłania */
    -
    - GG_ERROR_DCC_HANDSHAKE, /**< Błąd negocjacji */
    - GG_ERROR_DCC_FILE, /**< Błąd odczytu/zapisu pliku */
    - GG_ERROR_DCC_EOF, /**< Przedwczesny koniec pliku */
    - GG_ERROR_DCC_NET, /**< Błąd wysyłania/odbierania */
    - GG_ERROR_DCC_REFUSED, /**< Połączenie odrzucone */
    -
    - GG_ERROR_DCC7_HANDSHAKE, /**< Błąd negocjacji */
    - GG_ERROR_DCC7_FILE, /**< Błąd odczytu/zapisu pliku */
    - GG_ERROR_DCC7_EOF, /**< Przedwczesny koniec pliku */
    - GG_ERROR_DCC7_NET, /**< Błąd wysyłania/odbierania */
    - GG_ERROR_DCC7_REFUSED, /**< Połączenie odrzucone */
    - GG_ERROR_DCC7_RELAY, /**< Problem z serwerem pośredniczącym */
    -};
    -
    -/**
    - * Pole zapytania lub odpowiedzi katalogu publicznego.
    - */
    -struct gg_pubdir50_entry {
    - int num; /**< Numer wyniku */
    - char *field; /**< Nazwa pola */
    - char *value; /**< Wartość pola */
    -} /* GG_DEPRECATED */;
    -
    -/**
    - * Zapytanie lub odpowiedź katalogu publicznego.
    - *
    - * Patrz \c gg_pubdir50_t.
    - */
    -struct gg_pubdir50_s {
    - int count; /**< Liczba wyników odpowiedzi */
    - uin_t next; /**< Numer początkowy następnego zapytania */
    - int type; /**< Rodzaj zapytania */
    - uint32_t seq; /**< Numer sekwencyjny */
    - struct gg_pubdir50_entry *entries; /**< Pola zapytania lub odpowiedzi */
    - int entries_count; /**< Liczba pól */
    -} /* GG_DEPRECATED */;
    -
    -/**
    - * Zapytanie lub odpowiedź katalogu publicznego.
    - *
    - * Do pól nie należy się odwoływać bezpośrednio -- wszystkie niezbędne
    - * informacje są dostępne za pomocą funkcji \c gg_pubdir50_*
    - */
    -typedef struct gg_pubdir50_s *gg_pubdir50_t;
    -
    -/**
    - * Opis zdarzeń \c GG_EVENT_MSG i \c GG_EVENT_MULTILOGON_MSG.
    - */
    -struct gg_event_msg {
    - uin_t sender; /**< Numer nadawcy/odbiorcy */
    - int msgclass; /**< Klasa wiadomości */
    -#ifndef _WIN32
    - time_t time; /**< Czas nadania */
    -#else
    - uint32_t time; /**< Czas nadania */
    -#endif
    - unsigned char *message; /**< Treść wiadomości */
    -
    - int recipients_count; /**< Liczba odbiorców konferencji */
    - uin_t *recipients; /**< Odbiorcy konferencji */
    -
    - int formats_length; /**< Długość informacji o formatowaniu tekstu */
    - void *formats; /**< Informacje o formatowaniu tekstu */
    - uint32_t seq; /**< Numer sekwencyjny wiadomości */
    -
    - char *xhtml_message; /**< Treść wiadomości w formacie XHTML */
    -
    - uint64_t chat_id; /**< Identyfikator konferencji lub 0, jeżeli jest to zwykła wiadomość (11.0) */
    - uint64_t flags; /**< Flagi wiadomości (11.0) */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_NOTIFY_DESCR.
    - */
    -struct gg_event_notify_descr {
    - struct gg_notify_reply *notify; /**< Informacje o liście kontaktów */
    - char *descr; /**< Opis status */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_STATUS.
    - */
    -struct gg_event_status {
    - uin_t uin; /**< Numer Gadu-Gadu */
    - uint32_t status; /**< Nowy status */
    - char *descr; /**< Opis */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_STATUS60.
    - */
    -struct gg_event_status60 {
    - uin_t uin; /**< Numer Gadu-Gadu */
    - int status; /**< Nowy status */
    - uint32_t remote_ip; /**< Adres IP dla połączeń bezpośrednich */
    - uint16_t remote_port; /**< Port dla połączeń bezpośrednich */
    - int version; /**< Wersja protokołu */
    - int image_size; /**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
    - char *descr; /**< Opis statusu */
    -#ifndef _WIN32
    - time_t time; /**< Czas powrotu */
    -#else
    - uint32_t time; /**< Czas powrotu */
    -#endif
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_NOTIFY_REPLY60.
    - */
    -struct gg_event_notify60 {
    - uin_t uin; /**< Numer Gadu-Gadu. W ostatnim elemencie jest równy 0, a pozostałe pola są niezainicjowane. */
    - int status; /**< Nowy status */
    - uint32_t remote_ip; /**< Adres IP dla połączeń bezpośrednich */
    - uint16_t remote_port; /**< Port dla połączeń bezpośrednich */
    - int version; /**< Wersja protokołu */
    - int image_size; /**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
    - char *descr; /**< Opis statusu */
    -#ifndef _WIN32
    - time_t time; /**< Czas powrotu */
    -#else
    - uint32_t time; /**< Czas powrotu */
    -#endif
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_ACK.
    - */
    -struct gg_event_ack {
    - uin_t recipient; /**< Numer odbiorcy */
    - int status; /**< Status doręczenia */
    - int seq; /**< Numer sekwencyjny wiadomości */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_ACK110.
    - */
    -struct gg_event_ack110 {
    - uint8_t msg_type; /**< Rodzaj wiadomości (0x01 - zwykła, 0x02 - konferencja) */
    - uint32_t seq; /**< Numer sekwencyjny */
    - uint32_t time; /**< Czas zdarzenia */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_USERLIST.
    - */
    -struct gg_event_userlist {
    - char type; /**< Rodzaj odpowiedzi */
    - char *reply; /**< Treść odpowiedzi */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_DCC_VOICE_DATA.
    - */
    -struct gg_event_dcc_voice_data {
    - uint8_t *data; /**< Dane dźwiękowe */
    - int length; /**< Rozmiar danych dźwiękowych */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_IMAGE_REQUEST.
    - */
    -struct gg_event_image_request {
    - uin_t sender; /**< Nadawca żądania */
    - uint32_t size; /**< Rozmiar obrazka */
    - uint32_t crc32; /**< Suma kontrolna CRC32 */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_IMAGE_REPLY.
    - */
    -struct gg_event_image_reply {
    - uin_t sender; /**< Nadawca obrazka */
    - uint32_t size; /**< Rozmiar obrazka */
    - uint32_t crc32; /**< Suma kontrolna CRC32 */
    - char *filename; /**< Nazwa pliku */
    - char *image; /**< Bufor z obrazkiem */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_XML_EVENT.
    - */
    -struct gg_event_xml_event {
    - char *data; /**< Bufor z komunikatem */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_JSON_EVENT.
    - */
    -struct gg_event_json_event {
    - char *data; /**< Bufor z komunikatem */
    - char *type; /**< Bufor z typem komunikatu */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_DCC7_CONNECTED.
    - */
    -struct gg_event_dcc7_connected {
    - struct gg_dcc7 *dcc7; /**< Struktura połączenia */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_DCC7_PENDING.
    - */
    -struct gg_event_dcc7_pending {
    - struct gg_dcc7 *dcc7; /**< Struktura połączenia */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_DCC7_REJECT.
    - */
    -struct gg_event_dcc7_reject {
    - struct gg_dcc7 *dcc7; /**< Struktura połączenia */
    - int reason; /**< powód odrzucenia */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_DCC7_ACCEPT.
    - */
    -struct gg_event_dcc7_accept {
    - struct gg_dcc7 *dcc7; /**< Struktura połączenia */
    - int type; /**< Sposób połączenia (P2P, przez serwer) */
    - uint32_t remote_ip; /**< Adres zdalnego klienta */
    - uint16_t remote_port; /**< Port zdalnego klienta */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_DCC7_DONE.
    - */
    -struct gg_event_dcc7_done {
    - struct gg_dcc7 *dcc7; /**< Struktura połączenia */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_TYPING_NOTIFICATION.
    - */
    -struct gg_event_typing_notification {
    - uin_t uin; /**< Numer rozmówcy */
    - int length; /**< Długość tekstu */
    -};
    -
    -/**
    - * Atrybut użytkownika.
    - */
    -struct gg_event_user_data_attr {
    - int type; /**< Typ atrybutu */
    - char *key; /**< Klucz */
    - char *value; /**< Wartość */
    -};
    -
    -/**
    - * Struktura opisująca kontakt w zdarzeniu GG_EVENT_USER_DATA.
    - */
    -struct gg_event_user_data_user {
    - uin_t uin; /**< Numer kontaktu */
    - size_t attr_count; /**< Liczba atrybutów */
    - struct gg_event_user_data_attr *attrs; /**< Lista atrybutów */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_USER_DATA.
    - */
    -struct gg_event_user_data {
    - int type; /**< Rodzaj informacji o kontaktach */
    - size_t user_count; /**< Liczba kontaktów */
    - struct gg_event_user_data_user *users; /**< Lista kontaktów */
    -};
    -
    -/**
    - * Struktura opisująca sesję multilogowania.
    - */
    -struct gg_multilogon_session {
    - gg_multilogon_id_t id; /**< Identyfikator sesji */
    - char *name; /**< Nazwa sesji (podana w \c gg_login_params.client_version) */
    - uint32_t remote_addr; /**< Adres sesji */
    - int status_flags; /**< Flagi statusu sesji */
    - int protocol_features; /**< Opcje protokolu sesji */
    -#ifndef _WIN32
    - time_t logon_time; /**< Czas zalogowania */
    -#else
    - uint32_t logon_time; /**< Czas zalogowania */
    -#endif
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_MULTILOGON_INFO.
    - */
    -struct gg_event_multilogon_info {
    - int count; /**< Liczba sesji */
    - struct gg_multilogon_session *sessions; /** Lista sesji */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_USERLIST100_VERSION.
    - */
    -struct gg_event_userlist100_version {
    - uint32_t version; /**< Numer wersji listy kontaktów na serwerze */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_USERLIST100_REPLY.
    - */
    -struct gg_event_userlist100_reply {
    - char type; /**< Rodzaj odpowiedzi */
    - uint32_t version; /**< Aktualna wersja listy kontaktów na serwerze */
    - char format_type; /**< Typ formatu listy kontaktów (żądany w \c gg_userlist100_request.format_type) */
    - char *reply; /**< Treść listy kontaktów w przesyłanej wersji i formacie */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_IMTOKEN.
    - */
    -struct gg_event_imtoken {
    - char *imtoken; /**< Wartość IMTOKEN */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_PONG110.
    - */
    -struct gg_event_pong110 {
    -#ifndef _WIN32
    - time_t time; /**< Aktualny czas na serwerze */
    -#else
    - uint32_t time; /**< Aktualny czas na serwerze */
    -#endif
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_CHAT_INFO.
    - */
    -struct gg_event_chat_info {
    - uint64_t id; /**< Identyfikator konferencji */
    - uint32_t version; /**< Wersja informacji o konferencji */
    - uint32_t participants_count; /**< Ilość uczestników */
    - uin_t *participants; /**< Lista uczestników */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_CHAT_INFO_UPDATE.
    - */
    -struct gg_event_chat_info_update {
    - uint64_t id; /**< Identyfikator konferencji */
    - uint32_t type; /**< Typ aktualizacji (\c GG_CHAT_INFO_UPDATE_*) */
    - uin_t participant; /**< Uczestnik, którego dotyczy aktualizacja */
    - uin_t inviter; /**< Uczestnik inicjujący aktualizację (zapraszający) */
    - uint32_t version; /**< Wersja informacji o konferencji */
    - uint32_t time; /**< Czas zdarzenia */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_CHAT_CREATED.
    - */
    -struct gg_event_chat_created {
    - uint64_t id; /**< Identyfikator konferencji */
    - uint32_t seq; /**< Numer sekwencyjny */
    -};
    -
    -/**
    - * Opis zdarzenia \c GG_EVENT_CHAT_INVITE_ACK.
    - */
    -struct gg_event_chat_invite_ack {
    - uint64_t id; /**< Identyfikator konferencji */
    - uint32_t seq; /**< Numer sekwencyjny */
    -};
    -
    -/**
    - * Unia wszystkich zdarzeń zwracanych przez funkcje \c gg_watch_fd(),
    - * \c gg_dcc_watch_fd() i \c gg_dcc7_watch_fd().
    - *
    - * \ingroup events
    - */
    -union gg_event_union {
    - enum gg_failure_t failure; /**< Błąd połączenia (\c GG_EVENT_CONN_FAILED) */
    - struct gg_notify_reply *notify; /**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY) */
    - struct gg_event_notify_descr notify_descr; /**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY_DESCR) */
    - struct gg_event_status status; /**< Zmiana statusu kontaktów (\c GG_EVENT_STATUS) */
    - struct gg_event_status60 status60; /**< Zmiana statusu kontaktów (\c GG_EVENT_STATUS60) */
    - struct gg_event_notify60 *notify60; /**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY60) */
    - struct gg_event_msg msg; /**< Otrzymano wiadomość (\c GG_EVENT_MSG) */
    - struct gg_event_ack ack; /**< Potwierdzenie wiadomości (\c GG_EVENT_ACK) */
    - struct gg_event_ack110 ack110; /**< Potwierdzenie wysłania wiadomości (11.0) (\c GG_EVENT_ACK110) */
    - struct gg_event_image_request image_request; /**< Żądanie wysłania obrazka (\c GG_EVENT_IMAGE_REQUEST) */
    - struct gg_event_image_reply image_reply; /**< Odpowiedź z obrazkiem (\c GG_EVENT_IMAGE_REPLY) */
    - struct gg_event_userlist userlist; /**< Odpowiedź listy kontaktów (\c GG_EVENT_USERLIST) */
    - gg_pubdir50_t pubdir50; /**< Odpowiedź katalogu publicznego (\c GG_EVENT_PUBDIR50_*) */
    - struct gg_event_xml_event xml_event; /**< Zdarzenie systemowe (\c GG_EVENT_XML_EVENT) */
    - struct gg_event_json_event json_event; /**< Zdarzenie systemowe (\c GG_EVENT_JSON_EVENT) */
    - struct gg_dcc *dcc_new; /**< Nowe połączenie bezpośrednie (\c GG_EVENT_DCC_NEW) */
    - enum gg_error_t dcc_error; /**< Błąd połączenia bezpośredniego (\c GG_EVENT_DCC_ERROR) */
    - struct gg_event_dcc_voice_data dcc_voice_data; /**< Dane połączenia głosowego (\c GG_EVENT_DCC_VOICE_DATA) */
    - struct gg_dcc7 *dcc7_new; /**< Nowe połączenie bezpośrednie (\c GG_EVENT_DCC7_NEW) */
    - enum gg_error_t dcc7_error; /**< Błąd połączenia bezpośredniego (\c GG_EVENT_DCC7_ERROR) */
    - struct gg_event_dcc7_connected dcc7_connected; /**< Informacja o zestawieniu połączenia bezpośredniego (\c GG_EVENT_DCC7_CONNECTED) */
    - struct gg_event_dcc7_pending dcc7_pending; /**< Trwa próba połączenia bezpośredniego (\c GG_EVENT_DCC7_PENDING) */
    - struct gg_event_dcc7_reject dcc7_reject; /**< Odrzucono połączenia bezpośredniego (\c GG_EVENT_DCC7_REJECT) */
    - struct gg_event_dcc7_accept dcc7_accept; /**< Zaakceptowano połączenie bezpośrednie (\c GG_EVENT_DCC7_ACCEPT) */
    - struct gg_event_dcc7_done dcc7_done; /**< Zakończono połączenie bezpośrednie (\c GG_EVENT_DCC7_DONE) */
    - struct gg_event_typing_notification typing_notification; /**< Powiadomienie o pisaniu */
    - struct gg_event_user_data user_data; /**< Informacje o kontaktach */
    - struct gg_event_msg multilogon_msg; /**< Inna sesja wysłała wiadomość (\c GG_EVENT_MULTILOGON_MSG) */
    - struct gg_event_multilogon_info multilogon_info; /**< Informacja o innych sesjach multilogowania (\c GG_EVENT_MULTILOGON_INFO) */
    - struct gg_event_userlist100_version userlist100_version; /**< Informacja o numerze wersji listy kontaktów na serwerze (\c GG_EVENT_USERLIST100_VERSION) */
    - struct gg_event_userlist100_reply userlist100_reply; /**< Odpowiedź listy kontaktów (10.0) (\c GG_EVENT_USERLIST100_REPLY) */
    - struct gg_event_imtoken imtoken; /**< Ciąg IMTOKEN (11.0) (\c GG_EVENT_IMTOKEN) */
    - struct gg_event_pong110 pong110; /**< Utrzymanie połączenia (11.0) (\c GG_EVENT_PONG110) */
    - struct gg_event_chat_info chat_info; /**< Informacje o konferencji (11.0) (\c GG_EVENT_CHAT_INFO) */
    - struct gg_event_chat_info_update chat_info_update; /**< Aktualizacja informacji o konferencji (11.0) (\c GG_EVENT_CHAT_INFO_UPDATE) */
    - struct gg_event_chat_created chat_created; /**< Potwierdzenie utworzenia konferencji (11.0) (\c GG_EVENT_CHAT_CREATED) */
    - struct gg_event_chat_invite_ack chat_invite_ack; /**< Potwierdzenie wysłania zaproszenia do konferencji (11.0) (\c GG_EVENT_CHAT_INVITE_ACK) */
    -};
    -
    -/**
    - * Opis zdarzenia.
    - *
    - * Zwracany przez funkcje \c gg_watch_fd(), \c gg_dcc_watch_fd()
    - * i \c gg_dcc7_watch_fd(). Po przeanalizowaniu należy zwolnić
    - * za pomocą \c gg_event_free().
    - *
    - * \ingroup events
    - */
    -struct gg_event {
    - int type; /**< Rodzaj zdarzenia */
    - union gg_event_union event; /**< Informacja o zdarzeniu */
    -};
    -
    -struct gg_event *gg_watch_fd(struct gg_session *sess);
    -void gg_event_free(struct gg_event *e);
    -
    -int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count);
    -int gg_notify(struct gg_session *sess, uin_t *userlist, int count);
    -int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type);
    -int gg_add_notify(struct gg_session *sess, uin_t uin);
    -int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type);
    -int gg_remove_notify(struct gg_session *sess, uin_t uin);
    -
    -struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header);
    -int gg_http_watch_fd(struct gg_http *h);
    -void gg_http_stop(struct gg_http *h);
    -void gg_http_free(struct gg_http *h);
    -
    -uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req);
    -gg_pubdir50_t gg_pubdir50_new(int type);
    -int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value);
    -int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq);
    -const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field);
    -int gg_pubdir50_type(gg_pubdir50_t res);
    -int gg_pubdir50_count(gg_pubdir50_t res);
    -uin_t gg_pubdir50_next(gg_pubdir50_t res);
    -uint32_t gg_pubdir50_seq(gg_pubdir50_t res);
    -void gg_pubdir50_free(gg_pubdir50_t res);
    -
    -#ifndef DOXYGEN
    -
    -#define GG_PUBDIR50_UIN "FmNumber"
    -#define GG_PUBDIR50_STATUS "FmStatus"
    -#define GG_PUBDIR50_FIRSTNAME "firstname"
    -#define GG_PUBDIR50_LASTNAME "lastname"
    -#define GG_PUBDIR50_NICKNAME "nickname"
    -#define GG_PUBDIR50_BIRTHYEAR "birthyear"
    -#define GG_PUBDIR50_CITY "city"
    -#define GG_PUBDIR50_GENDER "gender"
    -#define GG_PUBDIR50_GENDER_FEMALE "1"
    -#define GG_PUBDIR50_GENDER_MALE "2"
    -#define GG_PUBDIR50_GENDER_SET_FEMALE "2"
    -#define GG_PUBDIR50_GENDER_SET_MALE "1"
    -#define GG_PUBDIR50_ACTIVE "ActiveOnly"
    -#define GG_PUBDIR50_ACTIVE_TRUE "1"
    -#define GG_PUBDIR50_START "fmstart"
    -#define GG_PUBDIR50_FAMILYNAME "familyname"
    -#define GG_PUBDIR50_FAMILYCITY "familycity"
    -
    -#else
    -
    -/**
    - * \ingroup pubdir50
    - *
    - * Rodzaj pola zapytania.
    - */
    -enum {
    - GG_PUBDIR50_UIN, /**< Numer Gadu-Gadu */
    - GG_PUBDIR50_STATUS, /**< Status (tylko wynik wyszukiwania) */
    - GG_PUBDIR50_FIRSTNAME, /**< Imię */
    - GG_PUBDIR50_LASTNAME, /**< Nazwisko */
    - GG_PUBDIR50_NICKNAME, /**< Pseudonim */
    - GG_PUBDIR50_BIRTHYEAR, /**< Rok urodzenia lub przedział lat oddzielony spacją */
    - GG_PUBDIR50_CITY, /**< Miejscowość */
    - GG_PUBDIR50_GENDER, /**< Płeć */
    - GG_PUBDIR50_ACTIVE, /**< Osoba dostępna (tylko wyszukiwanie) */
    - GG_PUBDIR50_START, /**< Numer początkowy wyszukiwania (tylko wyszukiwanie) */
    - GG_PUBDIR50_FAMILYNAME, /**< Nazwisko rodowe (tylko wysyłanie informacji o sobie) */
    - GG_PUBDIR50_FAMILYCITY, /**< Miejscowość pochodzenia (tylko wysyłanie informacji o sobie) */
    -};
    -
    -/**
    - * \ingroup pubdir50
    - *
    - * Wartość pola GG_PUBDIR50_GENDER przy wyszukiwaniu. Brak pola oznacza dowolną płeć.
    - */
    -enum {
    - GG_PUBDIR50_GENDER_FEMALE, /**< Kobieta */
    - GG_PUBDIR50_GENDER_MALE, /**< Mężczyzna */
    -};
    -
    -/**
    - * \ingroup pubdir50
    - *
    - * Wartość pola GG_PUBDIR50_GENDER przy wysyłaniu informacji o sobie.
    - */
    -enum {
    - GG_PUBDIR50_GENDER_SET_FEMALE, /**< Kobieta */
    - GG_PUBDIR50_GENDER_SET_MALE, /**< Mężczyzna */
    -};
    -
    -/**
    - * \ingroup pubdir50
    - *
    - * Wartość pola GG_PUBDIR50_ACTIVE.
    - */
    -enum {
    - GG_PUBDIR50_ACTIVE_TRUE, /**< Wyszukaj tylko osoby dostępne */
    -};
    -
    -#endif /* DOXYGEN */
    -
    -/**
    - * Powód błędu operacji na katalogu publicznym.
    - *
    - * \ingroup http
    - */
    -typedef enum {
    - GG_PUBDIR_ERROR_NONE = 0, /**< Brak błędu */
    - GG_PUBDIR_ERROR_OTHER, /**< Nieznany błąd */
    - GG_PUBDIR_ERROR_TOKEN, /**< Błędny token */
    - GG_PUBDIR_ERROR_OLD_PASSWORD, /**< Niepoprawne stare hasło */
    - GG_PUBDIR_ERROR_NEW_PASSWORD, /**< Niepoprawne nowe hasło */
    -} gg_pubdir_error_t;
    -
    -/**
    - * Wynik operacji na katalogu publicznym.
    - *
    - * \ingroup http
    - */
    -struct gg_pubdir {
    - int success; /**< Flaga powodzenia operacji */
    - uin_t uin; /**< Otrzymany numer lub 0 w przypadku błędu */
    - gg_pubdir_error_t error; /**< Powód błędu */
    -};
    -
    -int gg_pubdir_watch_fd(struct gg_http *f);
    -void gg_pubdir_free(struct gg_http *f);
    -
    -/**
    - * Token autoryzacji niektórych operacji HTTP.
    - *
    - * \ingroup token
    - */
    -struct gg_token {
    - int width; /**< Szerokość obrazka */
    - int height; /**< Wysokość obrazka */
    - int length; /**< Liczba znaków w tokenie */
    - char *tokenid; /**< Identyfikator tokenu */
    -};
    -
    -struct gg_http *gg_token(int async);
    -int gg_token_watch_fd(struct gg_http *h);
    -void gg_token_free(struct gg_http *h);
    -
    -struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async);
    -#ifndef DOXYGEN
    -#define gg_register_watch_fd gg_pubdir_watch_fd
    -#define gg_register_free gg_pubdir_free
    -#endif
    -
    -struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async);
    -#ifndef DOXYGEN
    -#define gg_unregister_watch_fd gg_pubdir_watch_fd
    -#define gg_unregister_free gg_pubdir_free
    -#endif
    -
    -struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async);
    -#ifndef DOXYGEN
    -#define gg_remind_passwd_watch_fd gg_pubdir_watch_fd
    -#define gg_remind_passwd_free gg_pubdir_free
    -#endif
    -
    -struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async);
    -#ifndef DOXYGEN
    -#define gg_change_passwd_watch_fd gg_pubdir_watch_fd
    -#define gg_change_passwd_free gg_pubdir_free
    -#endif
    -
    -extern int gg_dcc_port;
    -extern unsigned long gg_dcc_ip;
    -
    -int gg_dcc_request(struct gg_session *sess, uin_t uin);
    -
    -struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
    -struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
    -struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
    -void gg_dcc_set_type(struct gg_dcc *d, int type);
    -int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename);
    -int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename);
    -int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length);
    -
    -#define GG_DCC_VOICE_FRAME_LENGTH 195 /**< Rozmiar pakietu głosowego przed wersją Gadu-Gadu 5.0.5 */
    -#define GG_DCC_VOICE_FRAME_LENGTH_505 326 /**< Rozmiar pakietu głosowego od wersji Gadu-Gadu 5.0.5 */
    -
    -struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port);
    -#ifndef DOXYGEN
    -#define gg_dcc_socket_free gg_dcc_free
    -#define gg_dcc_socket_watch_fd gg_dcc_watch_fd
    -#endif
    -
    -struct gg_event *gg_dcc_watch_fd(struct gg_dcc *d);
    -
    -void gg_dcc_free(struct gg_dcc *c);
    -
    -struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *d);
    -struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash);
    -struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash);
    -int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset);
    -int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason);
    -void gg_dcc7_free(struct gg_dcc7 *d);
    -
    -extern int gg_debug_level;
    -
    -extern void (*gg_debug_handler)(int level, const char *format, va_list ap);
    -extern void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap);
    -
    -extern FILE *gg_debug_file;
    -
    -/**
    - * \ingroup debug
    - * @{
    - */
    -#define GG_DEBUG_NET 1 /**< Rejestracja zdarzeń związanych z siecią */
    -#define GG_DEBUG_TRAFFIC 2 /**< Rejestracja ruchu sieciowego */
    -#define GG_DEBUG_DUMP 4 /**< Rejestracja zawartości pakietów */
    -#define GG_DEBUG_FUNCTION 8 /**< Rejestracja wywołań funkcji */
    -#define GG_DEBUG_MISC 16 /**< Rejestracja różnych informacji */
    -#define GG_DEBUG_VERBOSE 32 /**< Rejestracja informacji szczegółowych */
    -#define GG_DEBUG_WARNING 64 /**< Rejestracja ostrzeżeń */
    -#define GG_DEBUG_ERROR 128 /**< Rejestracja błędów krytycznych */
    -/** @} */
    -
    -const char *gg_debug_state(enum gg_state_t state);
    -const char *gg_debug_event(enum gg_event_t event);
    -
    -#ifdef GG_DEBUG_DISABLE
    -#define gg_debug(...) do { } while (0)
    -#define gg_debug_session(...) do { } while (0)
    -#else
    -void gg_debug(int level, const char *format, ...) GG_GNUC_PRINTF(2, 3);
    -void gg_debug_session(struct gg_session *sess, int level, const char *format, ...) GG_GNUC_PRINTF(3, 4);
    -#endif
    -
    -const char *gg_libgadu_version(void);
    -
    -/**
    - * Lista funkcji biblioteki, które zależą od zewnętrznych bibliotek.
    - *
    - * \ingroup version
    - */
    -typedef enum {
    - GG_LIBGADU_FEATURE_SSL, /**< Biblioteka obsługuje połączenia szyfrowane */
    - GG_LIBGADU_FEATURE_PTHREAD, /**< Biblioteka obsługuje rozwiązywanie nazw za pomocą wątków */
    - GG_LIBGADU_FEATURE_USERLIST100, /**< Biblioteka obsługuje listę kontaktów zgodną z Gadu-Gadu 10 */
    -} gg_libgadu_feature_t;
    -
    -int gg_libgadu_check_feature(gg_libgadu_feature_t feature);
    -
    -extern int gg_proxy_enabled;
    -extern char *gg_proxy_host;
    -extern int gg_proxy_port;
    -extern char *gg_proxy_username;
    -extern char *gg_proxy_password;
    -extern int gg_proxy_http_only;
    -
    -extern unsigned long gg_local_ip;
    -
    -#define GG_LOGIN_HASH_GG32 0x01 /**< Algorytm Gadu-Gadu */
    -#define GG_LOGIN_HASH_SHA1 0x02 /**< Algorytm SHA1 */
    -
    -#ifndef DOXYGEN
    -
    -#define GG_PUBDIR50_WRITE 0x01
    -#define GG_PUBDIR50_READ 0x02
    -#define GG_PUBDIR50_SEARCH 0x03
    -#define GG_PUBDIR50_SEARCH_REQUEST GG_PUBDIR50_SEARCH
    -#define GG_PUBDIR50_SEARCH_REPLY 0x05
    -
    -#else
    -
    -/**
    - * \ingroup pubdir50
    - *
    - * Rodzaj zapytania lub odpowiedzi katalogu publicznego.
    - */
    -enum {
    - GG_PUBDIR50_WRITE, /**< Wysłanie do serwera informacji o sobie */
    - GG_PUBDIR50_READ, /**< Pobranie z serwera informacji o sobie */
    - GG_PUBDIR50_SEARCH, /**< Wyszukiwanie w katalogu publicznym */
    - GG_PUBDIR50_SEARCH_REPLY, /**< Wynik wyszukiwania w katalogu publicznym */
    -};
    -
    -#endif /* DOXYGEN */
    -
    -/** \cond obsolete */
    -
    -#define gg_free_event gg_event_free
    -#define gg_free_http gg_http_free
    -#define gg_free_pubdir gg_pubdir_free
    -#define gg_free_register gg_pubdir_free
    -#define gg_free_remind_passwd gg_pubdir_free
    -#define gg_free_dcc gg_dcc_free
    -#define gg_free_change_passwd gg_pubdir_free
    -
    -struct gg_search_request {
    - int active;
    - unsigned int start;
    - char *nickname;
    - char *first_name;
    - char *last_name;
    - char *city;
    - int gender;
    - int min_birth;
    - int max_birth;
    - char *email;
    - char *phone;
    - uin_t uin;
    -} /* GG_DEPRECATED */;
    -
    -struct gg_search {
    - int count;
    - struct gg_search_result *results;
    -} GG_DEPRECATED;
    -
    -struct gg_search_result {
    - uin_t uin;
    - char *first_name;
    - char *last_name;
    - char *nickname;
    - int born;
    - int gender;
    - char *city;
    - int active;
    -} GG_DEPRECATED;
    -
    -#define GG_GENDER_NONE 0
    -#define GG_GENDER_FEMALE 1
    -#define GG_GENDER_MALE 2
    -
    -struct gg_http *gg_search(const struct gg_search_request *r, int async) GG_DEPRECATED;
    -int gg_search_watch_fd(struct gg_http *f) GG_DEPRECATED;
    -void gg_free_search(struct gg_http *f) GG_DEPRECATED;
    -#define gg_search_free gg_free_search
    -
    -const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start) GG_DEPRECATED;
    -const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start) GG_DEPRECATED;
    -const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start) GG_DEPRECATED;
    -const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start) GG_DEPRECATED;
    -void gg_search_request_free(struct gg_search_request *r) GG_DEPRECATED;
    -
    -struct gg_http *gg_register(const char *email, const char *password, int async) GG_DEPRECATED;
    -struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async) GG_DEPRECATED;
    -
    -struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async) GG_DEPRECATED;
    -struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async) GG_DEPRECATED;
    -
    -struct gg_http *gg_remind_passwd(uin_t uin, int async) GG_DEPRECATED;
    -struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async) GG_DEPRECATED;
    -
    -struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async) GG_DEPRECATED;
    -struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async) GG_DEPRECATED;
    -struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async) GG_DEPRECATED;
    -
    -struct gg_change_info_request {
    - char *first_name;
    - char *last_name;
    - char *nickname;
    - char *email;
    - int born;
    - int gender;
    - char *city;
    -} /* GG_DEPRECATED */;
    -
    -struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city) GG_DEPRECATED;
    -void gg_change_info_request_free(struct gg_change_info_request *r) GG_DEPRECATED;
    -
    -struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async) GG_DEPRECATED;
    -#define gg_change_pubdir_watch_fd gg_pubdir_watch_fd
    -#define gg_change_pubdir_free gg_pubdir_free
    -#define gg_free_change_pubdir gg_pubdir_free
    -
    -struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async) GG_DEPRECATED;
    -int gg_userlist_get_watch_fd(struct gg_http *f) GG_DEPRECATED;
    -void gg_userlist_get_free(struct gg_http *f) GG_DEPRECATED;
    -
    -struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async) GG_DEPRECATED;
    -int gg_userlist_put_watch_fd(struct gg_http *f) GG_DEPRECATED;
    -void gg_userlist_put_free(struct gg_http *f) GG_DEPRECATED;
    -
    -struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async) GG_DEPRECATED;
    -int gg_userlist_remove_watch_fd(struct gg_http *f) GG_DEPRECATED;
    -void gg_userlist_remove_free(struct gg_http *f) GG_DEPRECATED;
    -
    -int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length) GG_DEPRECATED;
    -
    -/** \endcond */
    -
    -int gg_file_hash_sha1(int fd, uint8_t *result) GG_DEPRECATED;
    -
    -char *gg_saprintf(const char *format, ...) GG_GNUC_PRINTF(1, 2) GG_DEPRECATED;
    -char *gg_vsaprintf(const char *format, va_list ap) GG_DEPRECATED;
    -
    -#define gg_alloc_sprintf gg_saprintf
    -
    -char *gg_get_line(char **ptr) GG_DEPRECATED;
    -
    -int gg_connect(void *addr, int port, int async) GG_DEPRECATED;
    -struct in_addr *gg_gethostbyname(const char *hostname) GG_DEPRECATED;
    -char *gg_read_line(int sock, char *buf, int length) GG_DEPRECATED;
    -void gg_chomp(char *line) GG_DEPRECATED;
    -char *gg_urlencode(const char *str) GG_DEPRECATED;
    -int gg_http_hash(const char *format, ...) GG_DEPRECATED;
    -void gg_http_free_fields(struct gg_http *h) GG_DEPRECATED;
    -int gg_read(struct gg_session *sess, char *buf, int length) GG_DEPRECATED;
    -int gg_write(struct gg_session *sess, const char *buf, int length) GG_DEPRECATED;
    -void *gg_recv_packet(struct gg_session *sess) GG_DEPRECATED;
    -int gg_send_packet(struct gg_session *sess, int type, ...) GG_DEPRECATED;
    -unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) GG_DEPRECATED;
    -void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result) GG_DEPRECATED;
    -uint32_t gg_fix32(uint32_t x);
    -uint16_t gg_fix16(uint16_t x);
    -#define fix16 gg_fix16
    -#define fix32 gg_fix32
    -char *gg_proxy_auth(void) GG_DEPRECATED;
    -char *gg_base64_encode(const char *buf) GG_DEPRECATED;
    -char *gg_base64_decode(const char *buf) GG_DEPRECATED;
    -int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) GG_DEPRECATED;
    -
    -/**
    - * Kolejka odbieranych obrazków.
    - */
    -struct gg_image_queue {
    - uin_t sender; /**< Nadawca obrazka */
    - uint32_t size; /**< Rozmiar obrazka */
    - uint32_t crc32; /**< Suma kontrolna CRC32 */
    - char *filename; /**< Nazwa pliku */
    - char *image; /**< Bufor z odebranymi danymi */
    - uint32_t done; /**< Rozmiar odebranych danych */
    -
    - struct gg_image_queue *next; /**< Kolejny element listy */
    -
    - uint32_t packet_type; /**< \brief Rodzaj odbieranych pakietów. W niektórych przypadkach (przy multilogowaniu) serwer wysyła nam dwie kopie obrazka jako dwa różne typy pakietów */
    -} GG_DEPRECATED;
    -
    -int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, const void *payload, int len) GG_DEPRECATED;
    -int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, const void *payload, int len) GG_DEPRECATED;
    -int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, const void *payload, int len) GG_DEPRECATED;
    -int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, const void *payload, int len) GG_DEPRECATED;
    -int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, const void *payload, int len) GG_DEPRECATED;
    -
    -#define GG_APPMSG_HOST "appmsg.gadu-gadu.pl"
    -#define GG_APPMSG_PORT 80
    -#define GG_PUBDIR_HOST "pubdir.gadu-gadu.pl"
    -#define GG_PUBDIR_PORT 80
    -#define GG_REGISTER_HOST "register.gadu-gadu.pl"
    -#define GG_REGISTER_PORT 80
    -#define GG_REMIND_HOST "retr.gadu-gadu.pl"
    -#define GG_REMIND_PORT 80
    -#define GG_RELAY_HOST "relay.gadu-gadu.pl"
    -#define GG_RELAY_PORT 80
    -
    -#define GG_DEFAULT_PORT 8074
    -#define GG_HTTPS_PORT 443
    -#define GG_HTTP_USERAGENT "Mozilla/4.7 [en] (Win98; I)"
    -
    -#define GG_PROTOCOL_VERSION_100 0x2e
    -#define GG_PROTOCOL_VERSION_110 0x40
    -
    -/* GG_DEPRECATED */
    -#define GG_DEFAULT_CLIENT_VERSION "-"
    -
    -#define GG_DEFAULT_PROTOCOL_VERSION GG_PROTOCOL_VERSION_110
    -#define GG_DEFAULT_TIMEOUT 30
    -#define GG_HAS_AUDIO_MASK 0x40000000
    -#define GG_HAS_AUDIO7_MASK 0x20000000
    -#define GG_ERA_OMNIX_MASK 0x04000000
    -#undef GG_LIBGADU_VERSION
    -
    -#ifndef DOXYGEN
    -
    -#define GG_FEATURE_MSG77 0x0001
    -#define GG_FEATURE_STATUS77 0x0002
    -#define GG_FEATURE_UNKNOWN_4 0x0004
    -#define GG_FEATURE_UNKNOWN_8 0x0008
    -#define GG_FEATURE_DND_FFC 0x0010
    -#define GG_FEATURE_IMAGE_DESCR 0x0020
    -#define GG_FEATURE_UNKNOWN_40 0x0040
    -#define GG_FEATURE_UNKNOWN_80 0x0080
    -#define GG_FEATURE_UNKNOWN_100 0x0100
    -#define GG_FEATURE_USER_DATA 0x0200
    -#define GG_FEATURE_MSG_ACK 0x0400
    -#define GG_FEATURE_UNKNOWN_800 0x0800
    -#define GG_FEATURE_UNKNOWN_1000 0x1000
    -#define GG_FEATURE_TYPING_NOTIFICATION 0x2000
    -#define GG_FEATURE_MULTILOGON 0x4000
    -
    -/* Poniższe makra zostały zachowane dla zgodności API */
    -#define GG_FEATURE_MSG80 0
    -#define GG_FEATURE_STATUS80 0
    -#define GG_FEATURE_STATUS80BETA 0
    -
    -#define GG_FEATURE_ALL (GG_FEATURE_MSG80 | GG_FEATURE_STATUS80 | GG_FEATURE_DND_FFC | GG_FEATURE_IMAGE_DESCR | GG_FEATURE_UNKNOWN_100 | GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK | GG_FEATURE_TYPING_NOTIFICATION)
    -
    -#else
    -
    -/**
    - * \ingroup login
    - *
    - * Flagi opcji protokołu.
    - */
    -enum {
    - GG_FEATURE_MSG77, /**< Klient życzy sobie otrzymywać wiadomości zgodnie z protokołem 7.7 */
    - GG_FEATURE_STATUS77, /**< Klient życzy sobie otrzymywać zmiany stanu zgodnie z protokołem 7.7 */
    - GG_FEATURE_DND_FFC, /**< Klient obsługuje statusy "nie przeszkadzać" i "poGGadaj ze mną" */
    - GG_FEATURE_IMAGE_DESCR, /**< Klient obsługuje opisy graficzne oraz flagę \c GG_STATUS80_DESCR_MASK */
    -};
    -
    -
    -#endif
    -
    -#define GG_DEFAULT_DCC_PORT 1550
    -
    -struct gg_header {
    - uint32_t type; /* typ pakietu */
    - uint32_t length; /* długość reszty pakietu */
    -} GG_PACKED;
    -
    -#define GG_WELCOME 0x0001
    -#define GG_NEED_EMAIL 0x0014
    -
    -struct gg_welcome {
    - uint32_t key; /* klucz szyfrowania hasła */
    -} GG_PACKED;
    -
    -#define GG_LOGIN 0x000c
    -
    -struct gg_login {
    - uint32_t uin; /* mój numerek */
    - uint32_t hash; /* hash hasła */
    - uint32_t status; /* status na dzień dobry */
    - uint32_t version; /* moja wersja klienta */
    - uint32_t local_ip; /* mój adres ip */
    - uint16_t local_port; /* port, na którym słucham */
    -} GG_PACKED;
    -
    -#define GG_LOGIN_EXT 0x0013
    -
    -struct gg_login_ext {
    - uint32_t uin; /* mój numerek */
    - uint32_t hash; /* hash hasła */
    - uint32_t status; /* status na dzień dobry */
    - uint32_t version; /* moja wersja klienta */
    - uint32_t local_ip; /* mój adres ip */
    - uint16_t local_port; /* port, na którym słucham */
    - uint32_t external_ip; /* zewnętrzny adres ip */
    - uint16_t external_port; /* zewnętrzny port */
    -} GG_PACKED;
    -
    -#define GG_LOGIN60 0x0015
    -
    -struct gg_login60 {
    - uint32_t uin; /* mój numerek */
    - uint32_t hash; /* hash hasła */
    - uint32_t status; /* status na dzień dobry */
    - uint32_t version; /* moja wersja klienta */
    - uint8_t dunno1; /* 0x00 */
    - uint32_t local_ip; /* mój adres ip */
    - uint16_t local_port; /* port, na którym słucham */
    - uint32_t external_ip; /* zewnętrzny adres ip */
    - uint16_t external_port; /* zewnętrzny port */
    - uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
    - uint8_t dunno2; /* 0xbe */
    -} GG_PACKED;
    -
    -#define GG_LOGIN70 0x0019
    -
    -struct gg_login70 {
    - uint32_t uin; /* mój numerek */
    - uint8_t hash_type; /* rodzaj hashowania hasła */
    - uint8_t hash[64]; /* hash hasła dopełniony zerami */
    - uint32_t status; /* status na dzień dobry */
    - uint32_t version; /* moja wersja klienta */
    - uint8_t dunno1; /* 0x00 */
    - uint32_t local_ip; /* mój adres ip */
    - uint16_t local_port; /* port, na którym słucham */
    - uint32_t external_ip; /* zewnętrzny adres ip (???) */
    - uint16_t external_port; /* zewnętrzny port (???) */
    - uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
    - uint8_t dunno2; /* 0xbe */
    -} GG_PACKED;
    -
    -#define GG_LOGIN_OK 0x0003
    -
    -#define GG_LOGIN_FAILED 0x0009
    -
    -#define GG_PUBDIR50_REQUEST 0x0014
    -
    -struct gg_pubdir50_request {
    - uint8_t type; /* GG_PUBDIR50_* */
    - uint32_t seq; /* czas wysłania zapytania */
    -} GG_PACKED;
    -
    -#define GG_PUBDIR50_REPLY 0x000e
    -
    -struct gg_pubdir50_reply {
    - uint8_t type; /* GG_PUBDIR50_* */
    - uint32_t seq; /* czas wysłania zapytania */
    -} GG_PACKED;
    -
    -#define GG_NEW_STATUS 0x0002
    -
    -#ifndef DOXYGEN
    -
    -#define GG_STATUS_NOT_AVAIL 0x0001
    -#define GG_STATUS_NOT_AVAIL_DESCR 0x0015
    -#define GG_STATUS_FFC 0x0017
    -#define GG_STATUS_FFC_DESCR 0x0018
    -#define GG_STATUS_AVAIL 0x0002
    -#define GG_STATUS_AVAIL_DESCR 0x0004
    -#define GG_STATUS_BUSY 0x0003
    -#define GG_STATUS_BUSY_DESCR 0x0005
    -#define GG_STATUS_DND 0x0021
    -#define GG_STATUS_DND_DESCR 0x0022
    -#define GG_STATUS_INVISIBLE 0x0014
    -#define GG_STATUS_INVISIBLE_DESCR 0x0016
    -#define GG_STATUS_BLOCKED 0x0006
    -
    -#define GG_STATUS_GGPLUS 0x0020
    -#define GG_STATUS_NOT_SET 0x0023
    -#define GG_STATUS_UNKNOWN 0x0025
    -
    -#define GG_STATUS_IMAGE_MASK 0x0100
    -#define GG_STATUS_DESCR_MASK 0x4000
    -#define GG_STATUS_FRIENDS_MASK 0x8000
    -
    -#define GG_STATUS_FLAG_UNKNOWN 0x00000001
    -#define GG_STATUS_FLAG_VIDEO 0x00000002
    -#define GG_STATUS_FLAG_INHERIT 0x00000020
    -#define GG_STATUS_FLAG_MOBILE 0x00100000
    -#define GG_STATUS_FLAG_SPAM 0x00800000
    -
    -#else
    -
    -/**
    - * Rodzaje statusów użytkownika.
    - *
    - * \ingroup status
    - */
    -enum {
    - GG_STATUS_NOT_AVAIL, /**< Niedostępny */
    - GG_STATUS_NOT_AVAIL_DESCR, /**< Niedostępny z opisem */
    - GG_STATUS_FFC, /**< PoGGadaj ze mną */
    - GG_STATUS_FFC_DESCR, /**< PoGGadaj ze mną z opisem */
    - GG_STATUS_AVAIL, /**< Dostępny */
    - GG_STATUS_AVAIL_DESCR, /**< Dostępny z opisem */
    - GG_STATUS_BUSY, /**< Zajęty */
    - GG_STATUS_BUSY_DESCR, /**< Zajęty z opisem */
    - GG_STATUS_DND, /**< Nie przeszkadzać */
    - GG_STATUS_DND_DESCR, /**< Nie przeszakdzać z opisem */
    - GG_STATUS_INVISIBLE, /**< Niewidoczny (tylko własny status) */
    - GG_STATUS_INVISIBLE_DESCR, /**< Niewidoczny z opisem (tylko własny status) */
    - GG_STATUS_BLOCKED, /**< Zablokowany (tylko status innych) */
    - GG_STATUS_GGPLUS, /**< Status "Korzystam z GG Plus" */
    - GG_STATUS_NOT_SET, /**< Status nie ustawiony (przy logowaniu się do sieci) */
    - GG_STATUS_IMAGE_MASK, /**< Flaga bitowa oznaczająca opis graficzny (tylko jeśli wybrano \c GG_FEATURE_IMAGE_DESCR) */
    - GG_STATUS_DESCR_MASK, /**< Flaga bitowa oznaczająca status z opisem (tylko jeśli wybrano \c GG_FEATURE_IMAGE_DESCR) */
    - GG_STATUS_FRIENDS_MASK, /**< Flaga bitowa dostępności tylko dla znajomych */
    -};
    -
    -/**
    - * Rodzaje statusów użytkownika. Mapa bitowa.
    - *
    - * \ingroup status
    - */
    -enum {
    - GG_STATUS_FLAG_UNKNOWN, /**< Przeznaczenie nieznane, ale występuje zawsze */
    - GG_STATUS_FLAG_VIDEO, /**< Klient obsługuje wideorozmowy */
    - GG_STATUS_FLAG_INHERIT, /**< Synchronizacja statusu do innych klientów (przy logowaniu się do sieci) */
    - GG_STATUS_FLAG_MOBILE, /**< Klient mobilny (ikona telefonu komórkowego) */
    - GG_STATUS_FLAG_SPAM, /**< Klient chce otrzymywać linki od nieznajomych */
    -};
    -
    -#endif /* DOXYGEN */
    -
    -/**
    - * \ingroup status
    - *
    - * Flaga bitowa dostepnosci informujaca ze mozemy voipowac
    - */
    -
    -#define GG_STATUS_VOICE_MASK 0x20000 /**< czy ma wlaczone audio (7.7) */
    -
    -/**
    - * \ingroup status
    - *
    - * Maksymalna długośc opisu.
    - */
    -#define GG_STATUS_DESCR_MAXSIZE 255
    -#define GG_STATUS_DESCR_MAXSIZE_PRE_8_0 70
    -
    -#define GG_STATUS_MASK 0xff
    -
    -/* GG_S_F() tryb tylko dla znajomych */
    -#define GG_S_F(x) (((x) & GG_STATUS_FRIENDS_MASK) != 0)
    -
    -/* GG_S() stan bez uwzględnienia dodatkowych flag */
    -#define GG_S(x) ((x) & GG_STATUS_MASK)
    -
    -
    -/* GG_S_FF() chętny do rozmowy */
    -#define GG_S_FF(x) (GG_S(x) == GG_STATUS_FFC || GG_S(x) == GG_STATUS_FFC_DESCR)
    -
    -/* GG_S_AV() dostępny */
    -#define GG_S_AV(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR)
    -
    -/* GG_S_AW() zaraz wracam */
    -#define GG_S_AW(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR)
    -
    -/* GG_S_DD() nie przeszkadzać */
    -#define GG_S_DD(x) (GG_S(x) == GG_STATUS_DND || GG_S(x) == GG_STATUS_DND_DESCR)
    -
    -/* GG_S_NA() niedostępny */
    -#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR)
    -
    -/* GG_S_I() niewidoczny */
    -#define GG_S_I(x) (GG_S(x) == GG_STATUS_INVISIBLE || GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
    -
    -
    -/* GG_S_A() dostępny lub chętny do rozmowy */
    -#define GG_S_A(x) (GG_S_FF(x) || GG_S_AV(x))
    -
    -/* GG_S_B() zajęty lub nie przeszkadzać */
    -#define GG_S_B(x) (GG_S_AW(x) || GG_S_DD(x))
    -
    -
    -/* GG_S_D() stan opisowy */
    -#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || \
    - GG_S(x) == GG_STATUS_FFC_DESCR || \
    - GG_S(x) == GG_STATUS_AVAIL_DESCR || \
    - GG_S(x) == GG_STATUS_BUSY_DESCR || \
    - GG_S(x) == GG_STATUS_DND_DESCR || \
    - GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
    -
    -/* GG_S_BL() blokowany lub blokujący */
    -#define GG_S_BL(x) (GG_S(x) == GG_STATUS_BLOCKED)
    -
    -/**
    - * Zmiana statusu (pakiet \c GG_NEW_STATUS i \c GG_NEW_STATUS80BETA)
    - */
    -struct gg_new_status {
    - uint32_t status; /**< Nowy status */
    -} GG_PACKED;
    -
    -#define GG_NOTIFY_FIRST 0x000f
    -#define GG_NOTIFY_LAST 0x0010
    -
    -#define GG_NOTIFY 0x0010
    -
    -struct gg_notify {
    - uint32_t uin; /* numerek danej osoby */
    - uint8_t dunno1; /* rodzaj wpisu w liście */
    -} GG_PACKED;
    -
    -#ifndef DOXYGEN
    -
    -#define GG_USER_OFFLINE 0x01
    -#define GG_USER_NORMAL 0x03
    -#define GG_USER_BLOCKED 0x04
    -
    -#else
    -
    -/**
    - * \ingroup contacts
    - *
    - * Rodzaj kontaktu.
    - */
    -enum {
    - GG_USER_NORMAL, /**< Zwykły kontakt */
    - GG_USER_BLOCKED, /**< Zablokowany */
    - GG_USER_OFFLINE, /**< Niewidoczny dla kontaktu */
    -};
    -
    -#endif /* DOXYGEN */
    -
    -#define GG_LIST_EMPTY 0x0012
    -
    -#define GG_NOTIFY_REPLY 0x000c /* tak, to samo co GG_LOGIN */
    -
    -struct gg_notify_reply {
    - uint32_t uin; /* numerek */
    - uint32_t status; /* status danej osoby */
    - uint32_t remote_ip; /* adres ip delikwenta */
    - uint16_t remote_port; /* port, na którym słucha klient */
    - uint32_t version; /* wersja klienta */
    - uint16_t dunno2; /* znowu port? */
    -} GG_PACKED;
    -
    -#define GG_NOTIFY_REPLY60 0x0011
    -
    -struct gg_notify_reply60 {
    - uint32_t uin; /* numerek plus flagi w MSB */
    - uint8_t status; /* status danej osoby */
    - uint32_t remote_ip; /* adres ip delikwenta */
    - uint16_t remote_port; /* port, na którym słucha klient */
    - uint8_t version; /* wersja klienta */
    - uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
    - uint8_t dunno1; /* 0x00 */
    -} GG_PACKED;
    -
    -#define GG_STATUS60 0x000f
    -
    -struct gg_status60 {
    - uint32_t uin; /* numerek plus flagi w MSB */
    - uint8_t status; /* status danej osoby */
    - uint32_t remote_ip; /* adres ip delikwenta */
    - uint16_t remote_port; /* port, na którym słucha klient */
    - uint8_t version; /* wersja klienta */
    - uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
    - uint8_t dunno1; /* 0x00 */
    -} GG_PACKED;
    -
    -#define GG_NOTIFY_REPLY77 0x0018
    -
    -struct gg_notify_reply77 {
    - uint32_t uin; /* numerek plus flagi w MSB */
    - uint8_t status; /* status danej osoby */
    - uint32_t remote_ip; /* adres ip delikwenta */
    - uint16_t remote_port; /* port, na którym słucha klient */
    - uint8_t version; /* wersja klienta */
    - uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
    - uint8_t dunno1; /* 0x00 */
    - uint32_t dunno2; /* ? */
    -} GG_PACKED;
    -
    -#define GG_STATUS77 0x0017
    -
    -struct gg_status77 {
    - uint32_t uin; /* numerek plus flagi w MSB */
    - uint8_t status; /* status danej osoby */
    - uint32_t remote_ip; /* adres ip delikwenta */
    - uint16_t remote_port; /* port, na którym słucha klient */
    - uint8_t version; /* wersja klienta */
    - uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
    - uint8_t dunno1; /* 0x00 */
    - uint32_t dunno2; /* ? */
    -} GG_PACKED;
    -
    -#define GG_ADD_NOTIFY 0x000d
    -#define GG_REMOVE_NOTIFY 0x000e
    -
    -struct gg_add_remove {
    - uint32_t uin; /* numerek */
    - uint8_t dunno1; /* bitmapa */
    -} GG_PACKED;
    -
    -#define GG_STATUS 0x0002
    -
    -struct gg_status {
    - uint32_t uin; /* numerek */
    - uint32_t status; /* nowy stan */
    -} GG_PACKED;
    -
    -#define GG_SEND_MSG 0x000b
    -
    -#ifndef DOXYGEN
    -
    -#define GG_CLASS_QUEUED 0x0001
    -#define GG_CLASS_OFFLINE GG_CLASS_QUEUED
    -#define GG_CLASS_MSG 0x0004
    -#define GG_CLASS_CHAT 0x0008
    -#define GG_CLASS_CTCP 0x0010
    -#define GG_CLASS_ACK 0x0020
    -#define GG_CLASS_EXT GG_CLASS_ACK /**< Dla kompatybilności wstecz */
    -
    -#else
    -
    -/**
    - * Klasy wiadomości. Wartości są maskami bitowymi, które w większości
    - * przypadków można łączyć (połączenie \c GG_CLASS_MSG i \c GG_CLASS_CHAT
    - * nie ma sensu).
    - *
    - * \ingroup messages
    - */
    -enum {
    - GG_CLASS_MSG, /**< Wiadomość ma pojawić się w osobnym oknie */
    - GG_CLASS_CHAT, /**< Wiadomość ma pojawić się w oknie rozmowy */
    - GG_CLASS_CTCP, /**< Wiadomość przeznaczona dla klienta Gadu-Gadu */
    - GG_CLASS_ACK, /**< Klient nie życzy sobie potwierdzenia */
    - GG_CLASS_QUEUED, /**< Wiadomość zakolejkowana na serwerze (tylko przy odbieraniu) */
    -};
    -
    -#endif /* DOXYGEN */
    -
    -/**
    - * Maksymalna długość wiadomości.
    - *
    - * \ingroup messages
    - */
    -#define GG_MSG_MAXSIZE 1989
    -
    -struct gg_send_msg {
    - uint32_t recipient;
    - uint32_t seq;
    - uint32_t msgclass;
    -} GG_PACKED;
    -
    -struct gg_msg_richtext {
    - uint8_t flag;
    - uint16_t length;
    -} GG_PACKED;
    -
    -/**
    - * Struktura opisująca formatowanie tekstu. W zależności od wartości pola
    - * \c font, zaraz za tą strukturą może wystąpić \c gg_msg_richtext_color
    - * lub \c gg_msg_richtext_image.
    - *
    - * \ingroup messages
    - */
    -struct gg_msg_richtext_format {
    - uint16_t position; /**< Początkowy znak formatowania (liczony od 0) */
    - uint8_t font; /**< Atrybuty formatowania */
    -} GG_PACKED;
    -
    -#ifndef DOXYGEN
    -
    -#define GG_FONT_BOLD 0x01
    -#define GG_FONT_ITALIC 0x02
    -#define GG_FONT_UNDERLINE 0x04
    -#define GG_FONT_COLOR 0x08
    -#define GG_FONT_IMAGE 0x80
    -
    -#else
    -
    -/**
    - * Atrybuty formatowania wiadomości.
    - *
    - * \ingroup messages
    - */
    -enum {
    - GG_FONT_BOLD,
    - GG_FONT_ITALIC,
    - GG_FONT_UNDERLINE,
    - GG_FONT_COLOR,
    - GG_FONT_IMAGE
    -};
    -
    -#endif /* DOXYGEN */
    -
    -/**
    - * Struktura opisującą kolor tekstu dla atrybutu \c GG_FONT_COLOR.
    - *
    - * \ingroup messages
    - */
    -struct gg_msg_richtext_color {
    - uint8_t red; /**< Składowa czerwona koloru */
    - uint8_t green; /**< Składowa zielona koloru */
    - uint8_t blue; /**< Składowa niebieska koloru */
    -} GG_PACKED;
    -
    -/**
    - * Strukturya opisująca obrazek wstawiony do wiadomości dla atrubutu
    - * \c GG_FONT_IMAGE.
    - *
    - * \ingroup messages
    - */
    -struct gg_msg_richtext_image {
    - uint16_t unknown1; /**< Nieznane pole o wartości 0x0109 */
    - uint32_t size; /**< Rozmiar obrazka */
    - uint32_t crc32; /**< Suma kontrolna CRC32 obrazka */
    -} GG_PACKED;
    -
    -struct gg_msg_recipients {
    - uint8_t flag;
    - uint32_t count;
    -} GG_PACKED;
    -
    -struct gg_msg_image_request {
    - uint8_t flag;
    - uint32_t size;
    - uint32_t crc32;
    -} GG_PACKED;
    -
    -struct gg_msg_image_reply {
    - uint8_t flag;
    - uint32_t size;
    - uint32_t crc32;
    - /* char filename[]; */
    - /* char image[]; */
    -} GG_PACKED;
    -
    -#define GG_SEND_MSG_ACK 0x0005
    -
    -#ifndef DOXYGEN
    -
    -#define GG_ACK_BLOCKED 0x0001
    -#define GG_ACK_DELIVERED 0x0002
    -#define GG_ACK_QUEUED 0x0003
    -#define GG_ACK_MBOXFULL 0x0004
    -#define GG_ACK_NOT_DELIVERED 0x0006
    -
    -#else
    -
    -/**
    - * Status doręczenia wiadomości.
    - *
    - * \ingroup messages
    - */
    -enum
    -{
    - GG_ACK_DELIVERED, /**< Wiadomość dostarczono. */
    - GG_ACK_QUEUED, /**< Wiadomość zakolejkowano z powodu niedostępności odbiorcy. */
    - GG_ACK_BLOCKED, /**< Wiadomość zablokowana przez serwer (spam, świąteczne ograniczenia itd.) */
    - GG_ACK_MBOXFULL, /**< Wiadomości nie dostarczono z powodu zapełnionej kolejki wiadomości odbiorcy. */
    - GG_ACK_NOT_DELIVERED /**< Wiadomości nie dostarczono (tylko dla \c GG_CLASS_CTCP). */
    -};
    -
    -#endif /* DOXYGEN */
    -
    -struct gg_send_msg_ack {
    - uint32_t status;
    - uint32_t recipient;
    - uint32_t seq;
    -} GG_PACKED;
    -
    -#define GG_RECV_MSG 0x000a
    -
    -struct gg_recv_msg {
    - uint32_t sender;
    - uint32_t seq;
    - uint32_t time;
    - uint32_t msgclass;
    -} GG_PACKED;
    -
    -#define GG_PING 0x0008
    -
    -#define GG_PONG 0x0007
    -
    -#define GG_DISCONNECTING 0x000b
    -
    -#define GG_USERLIST_REQUEST 0x0016
    -
    -#define GG_XML_EVENT 0x0027
    -
    -#ifndef DOXYGEN
    -
    -#define GG_USERLIST_PUT 0x00
    -#define GG_USERLIST_PUT_MORE 0x01
    -#define GG_USERLIST_GET 0x02
    -
    -#else
    -
    -/**
    - * \ingroup importexport
    - *
    - * Rodzaj zapytania.
    - */
    -enum {
    - GG_USERLIST_PUT, /**< Eksport listy kontaktów. */
    - GG_USERLIST_GET, /**< Import listy kontaktów. */
    -};
    -
    -#endif /* DOXYGEN */
    -
    -struct gg_userlist_request {
    - uint8_t type;
    -} GG_PACKED;
    -
    -#define GG_USERLIST_REPLY 0x0010
    -
    -#ifndef DOXYGEN
    -
    -#define GG_USERLIST_PUT_REPLY 0x00
    -#define GG_USERLIST_PUT_MORE_REPLY 0x02
    -#define GG_USERLIST_GET_REPLY 0x06
    -#define GG_USERLIST_GET_MORE_REPLY 0x04
    -
    -#else
    -
    -/**
    - * \ingroup importexport
    - *
    - * Rodzaj odpowiedzi.
    - */
    -enum {
    - GG_USERLIST_PUT_REPLY, /**< Wyeksportowano listy kontaktów. */
    - GG_USERLIST_GET_REPLY, /**< Zaimportowano listę kontaktów. */
    -};
    -
    -#endif /* DOXYGEN */
    -
    -struct gg_userlist_reply {
    - uint8_t type;
    -} GG_PACKED;
    -
    -#ifndef DOXYGEN
    -
    -#define GG_USERLIST100_PUT 0x00
    -#define GG_USERLIST100_GET 0x02
    -
    -#else
    -
    -/**
    - * \ingroup importexport
    - *
    - * Rodzaj zapytania (10.0).
    - */
    -enum {
    - GG_USERLIST100_PUT, /**< Eksport listy kontaktów. */
    - GG_USERLIST100_GET, /**< Import listy kontaktów. */
    -};
    -
    -#endif /* DOXYGEN */
    -
    -#ifndef DOXYGEN
    -
    -#define GG_USERLIST100_FORMAT_TYPE_NONE 0x00
    -#define GG_USERLIST100_FORMAT_TYPE_GG70 0x01
    -#define GG_USERLIST100_FORMAT_TYPE_GG100 0x02
    -
    -#else
    -
    -/**
    - * \ingroup importexport
    - *
    - * Typ formatu listy kontaktów (10.0).
    - */
    -enum {
    - GG_USERLIST100_FORMAT_TYPE_NONE, /**< Brak treści listy kontaktów. */
    - GG_USERLIST100_FORMAT_TYPE_GG70, /**< Format listy kontaktów zgodny z Gadu-Gadu 7.0. */
    - GG_USERLIST100_FORMAT_TYPE_GG100, /**< Format listy kontaktów zgodny z Gadu-Gadu 10.0. */
    -};
    -
    -#endif /* DOXYGEN */
    -
    -#ifndef DOXYGEN
    -
    -#define GG_USERLIST100_REPLY_LIST 0x00
    -#define GG_USERLIST100_REPLY_UPTODATE 0x01
    -#define GG_USERLIST100_REPLY_ACK 0x10
    -#define GG_USERLIST100_REPLY_REJECT 0x12
    -
    -#else
    -
    -/**
    - * \ingroup importexport
    - *
    - * Typ odpowiedzi listy kontaktów (10.0).
    - */
    -enum {
    - GG_USERLIST100_REPLY_LIST, /**< W odpowiedzi znajduje się aktualna lista kontaktów na serwerze. */
    - GG_USERLIST100_REPLY_UPTODATE, /**< Komunikat o tym, że lista kontaktów jest już zsynchronizowana. */
    - GG_USERLIST100_REPLY_ACK, /**< Potwierdzenie odebrania nowej wersji listy kontaktów. W polu \c gg_userlist100_reply.version znajduje się numer nowej wersji listy kontaktów. */
    - GG_USERLIST100_REPLY_REJECT, /**< Odmowa przyjęcia nowej wersji listy kontaktów. W polu \c gg_userlist100_reply.version znajduje się numer wersji listy kontaktów aktualnie przechowywanej przez serwer. */
    -};
    -
    -#endif /* DOXYGEN */
    -
    -struct gg_dcc_tiny_packet {
    - uint8_t type; /* rodzaj pakietu */
    -} GG_PACKED;
    -
    -struct gg_dcc_small_packet {
    - uint32_t type; /* rodzaj pakietu */
    -} GG_PACKED;
    -
    -struct gg_dcc_big_packet {
    - uint32_t type; /* rodzaj pakietu */
    - uint32_t dunno1; /* niewiadoma */
    - uint32_t dunno2; /* niewiadoma */
    -} GG_PACKED;
    -
    -/*
    - * póki co, nie znamy dokładnie protokołu. nie wiemy, co czemu odpowiada.
    - * nazwy są niepoważne i tymczasowe.
    - */
    -#define GG_DCC_WANT_FILE 0x0003 /* peer chce plik */
    -#define GG_DCC_HAVE_FILE 0x0001 /* więc mu damy */
    -#define GG_DCC_HAVE_FILEINFO 0x0003 /* niech ma informacje o pliku */
    -#define GG_DCC_GIMME_FILE 0x0006 /* peer jest pewny */
    -#define GG_DCC_CATCH_FILE 0x0002 /* wysyłamy plik */
    -
    -#define GG_DCC_FILEATTR_READONLY 0x0020
    -
    -#define GG_DCC_TIMEOUT_SEND 1800 /* 30 minut */
    -#define GG_DCC_TIMEOUT_GET 1800 /* 30 minut */
    -#define GG_DCC_TIMEOUT_FILE_ACK 300 /* 5 minut */
    -#define GG_DCC_TIMEOUT_VOICE_ACK 300 /* 5 minut */
    -
    -#define GG_DCC7_INFO 0x1f
    -
    -struct gg_dcc7_info {
    - uint32_t uin; /* numer nadawcy */
    - uint32_t type; /* sposób połączenia */
    - gg_dcc7_id_t id; /* identyfikator połączenia */
    - char info[GG_DCC7_INFO_LEN]; /* informacje o połączeniu "ip port" */
    - char hash[GG_DCC7_INFO_HASH_LEN];/* skrót "ip" */
    -} GG_PACKED;
    -
    -#define GG_DCC7_NEW 0x20
    -
    -struct gg_dcc7_new {
    - gg_dcc7_id_t id; /* identyfikator połączenia */
    - uint32_t uin_from; /* numer nadawcy */
    - uint32_t uin_to; /* numer odbiorcy */
    - uint32_t type; /* rodzaj transmisji */
    - unsigned char filename[GG_DCC7_FILENAME_LEN]; /* nazwa pliku */
    - uint32_t size; /* rozmiar pliku */
    - uint32_t size_hi; /* rozmiar pliku (starsze bajty) */
    - unsigned char hash[GG_DCC7_HASH_LEN]; /* hash SHA1 */
    -} GG_PACKED;
    -
    -#define GG_DCC7_ACCEPT 0x21
    -
    -struct gg_dcc7_accept {
    - uint32_t uin; /* numer przyjmującego połączenie */
    - gg_dcc7_id_t id; /* identyfikator połączenia */
    - uint32_t offset; /* offset przy wznawianiu transmisji */
    - uint32_t dunno1; /* 0x00000000 */
    -} GG_PACKED;
    -
    -/* XXX API */
    -#define GG_DCC7_TYPE_P2P 0x00000001 /**< Połączenie bezpośrednie */
    -#define GG_DCC7_TYPE_SERVER 0x00000002 /**< Połączenie przez serwer */
    -
    -#define GG_DCC7_REJECT 0x22
    -
    -struct gg_dcc7_reject {
    - uint32_t uin; /**< Numer odrzucającego połączenie */
    - gg_dcc7_id_t id; /**< Identyfikator połączenia */
    - uint32_t reason; /**< Powód rozłączenia */
    -} GG_PACKED;
    -
    -/* XXX API */
    -#define GG_DCC7_REJECT_BUSY 0x00000001 /**< Połączenie bezpośrednie już trwa, nie umiem obsłużyć więcej */
    -#define GG_DCC7_REJECT_USER 0x00000002 /**< Użytkownik odrzucił połączenie */
    -#define GG_DCC7_REJECT_VERSION 0x00000006 /**< Druga strona ma wersję klienta nieobsługującą połączeń bezpośrednich tego typu */
    -
    -#define GG_DCC7_ID_REQUEST 0x23
    -
    -struct gg_dcc7_id_request {
    - uint32_t type; /**< Rodzaj tranmisji */
    -} GG_PACKED;
    -
    -/* XXX API */
    -#define GG_DCC7_TYPE_VOICE 0x00000001 /**< Transmisja głosu */
    -#define GG_DCC7_TYPE_FILE 0x00000004 /**< transmisja pliku */
    -
    -#define GG_DCC7_ID_REPLY 0x23
    -
    -struct gg_dcc7_id_reply {
    - uint32_t type; /** Rodzaj transmisji */
    - gg_dcc7_id_t id; /** Przyznany identyfikator */
    -} GG_PACKED;
    -
    -#define GG_DCC7_DUNNO1 0x24
    -
    -#define GG_DCC7_TIMEOUT_CONNECT 10 /* 10 sekund */
    -#define GG_DCC7_TIMEOUT_SEND 1800 /* 30 minut */
    -#define GG_DCC7_TIMEOUT_GET 1800 /* 30 minut */
    -#define GG_DCC7_TIMEOUT_FILE_ACK 300 /* 5 minut */
    -#define GG_DCC7_TIMEOUT_VOICE_ACK 300 /* 5 minut */
    -
    -#define GG_CHAT_INFO_UPDATE_ENTERED 0x01
    -#define GG_CHAT_INFO_UPDATE_EXITED 0x03
    -
    -#ifdef __cplusplus
    -}
    -#endif
    -
    -#ifdef _WIN32
    -#pragma pack(pop)
    -#endif
    -
    -#endif /* LIBGADU_LIBGADU_H */
    -
    -/*
    - * Local variables:
    - * c-indentation-style: k&r
    - * c-basic-offset: 8
    - * indent-tabs-mode: notnil
    - * End:
    - *
    - * vim: shiftwidth=8:
    - */
    --- a/libpurple/protocols/gg/lib/message.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1016 +0,0 @@
    -/*
    - * (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file message.c
    - *
    - * \brief Obsługa wiadomości
    - *
    - * Plik zawiera funkcje dotyczące obsługi "klasy" gg_message_t, które
    - * w przyszłości zostaną dołączone do API. Obecnie używane są funkcje
    - * konwersji między tekstem z atrybutami i HTML.
    - */
    -
    -#include <stdlib.h>
    -#include <string.h>
    -#include <errno.h>
    -#include <limits.h>
    -#include <ctype.h>
    -#include <assert.h>
    -
    -#include "message.h"
    -
    -#if 0
    -
    -gg_message_t *gg_message_new(void)
    -{
    - gg_message_t *gm;
    -
    - gm = malloc(sizeof(gg_message_t));
    -
    - if (gm == NULL)
    - return NULL;
    -
    - memset(gm, 0, sizeof(gg_message_t));
    -
    - gm->msgclass = GG_CLASS_CHAT;
    - gm->seq = (uint32_t) -1;
    -
    - return gm;
    -}
    -
    -int gg_message_init(gg_message_t *gm, int msgclass, int seq, uin_t *recipients,
    - size_t recipient_count, char *text, char *html, char *attributes,
    - size_t attributes_length, int auto_convert)
    -{
    - GG_MESSAGE_CHECK(gm, -1);
    -
    - memset(gm, 0, sizeof(gg_message_t));
    - gm->recipients = recipients;
    - gm->recipient_count = recipient_count;
    - gm->text = text;
    - gm->html = html;
    - gm->attributes = attributes;
    - gm->attributes_length = attributes_length;
    - gm->msgclass = msgclass;
    - gm->seq = seq;
    - gm->auto_convert = auto_convert;
    -
    - return 0;
    -}
    -
    -void gg_message_free(gg_message_t *gm)
    -{
    - if (gm == NULL) {
    - errno = EINVAL;
    - return;
    - }
    -
    - free(gm->text);
    - free(gm->text_converted);
    - free(gm->html);
    - free(gm->html_converted);
    - free(gm->recipients);
    - free(gm->attributes);
    -
    - free(gm);
    -}
    -
    -int gg_message_set_auto_convert(gg_message_t *gm, int auto_convert)
    -{
    - GG_MESSAGE_CHECK(gm, -1);
    -
    - gm->auto_convert = !!auto_convert;
    -
    - if (!gm->auto_convert) {
    - free(gm->text_converted);
    - free(gm->html_converted);
    - gm->text_converted = NULL;
    - gm->html_converted = NULL;
    - }
    -
    - return 0;
    -}
    -
    -int gg_message_get_auto_convert(gg_message_t *gm)
    -{
    - GG_MESSAGE_CHECK(gm, -1);
    -
    - return gm->auto_convert;
    -}
    -
    -int gg_message_set_recipients(gg_message_t *gm, const uin_t *recipients, size_t recipient_count)
    -{
    - GG_MESSAGE_CHECK(gm, -1);
    -
    - if (recipient_count >= INT_MAX / sizeof(uin_t)) {
    - errno = EINVAL;
    - return -1;
    - }
    -
    - if ((recipients == NULL) || (recipient_count == 0)) {
    - free(gm->recipients);
    - gm->recipients = NULL;
    - gm->recipient_count = 0;
    - } else {
    - uin_t *tmp;
    -
    - tmp = realloc(gm->recipients, recipient_count * sizeof(uin_t));
    -
    - if (tmp == NULL)
    - return -1;
    -
    - memcpy(tmp, recipients, recipient_count * sizeof(uin_t));
    -
    - gm->recipients = tmp;
    - gm->recipient_count = recipient_count;
    - }
    -
    - return 0;
    -}
    -
    -int gg_message_set_recipient(gg_message_t *gm, uin_t recipient)
    -{
    - return gg_message_set_recipients(gm, &recipient, 1);
    -}
    -
    -int gg_message_get_recipients(gg_message_t *gm, const uin_t **recipients, size_t *recipient_count)
    -{
    - GG_MESSAGE_CHECK(gm, -1);
    -
    - if (recipients != NULL)
    - *recipients = gm->recipients;
    -
    - if (recipient_count != NULL)
    - *recipient_count = gm->recipient_count;
    -
    - return 0;
    -}
    -
    -uin_t gg_message_get_recipient(gg_message_t *gm)
    -{
    - GG_MESSAGE_CHECK(gm, (uin_t) -1);
    -
    - if ((gm->recipients == NULL) || (gm->recipient_count < 1)) {
    - /* errno = XXX; */
    - return (uin_t) -1;
    - }
    -
    - return gm->recipients[0];
    -}
    -
    -int gg_message_set_class(gg_message_t *gm, uint32_t msgclass)
    -{
    - GG_MESSAGE_CHECK(gm, -1);
    -
    - gm->msgclass = msgclass;
    -
    - return 0;
    -}
    -
    -uint32_t gg_message_get_class(gg_message_t *gm)
    -{
    - GG_MESSAGE_CHECK(gm, (uint32_t) -1);
    -
    - return gm->msgclass;
    -}
    -
    -int gg_message_set_seq(gg_message_t *gm, uint32_t seq)
    -{
    - GG_MESSAGE_CHECK(gm, -1);
    -
    - gm->seq = seq;
    -
    - return 0;
    -}
    -
    -uint32_t gg_message_get_seq(gg_message_t *gm)
    -{
    - GG_MESSAGE_CHECK(gm, (uint32_t) -1);
    -
    - return gm->seq;
    -}
    -
    -int gg_message_set_text(gg_message_t *gm, const char *text)
    -{
    - GG_MESSAGE_CHECK(gm, -1);
    -
    - if (text == NULL) {
    - free(gm->text);
    - gm->text = NULL;
    - } else {
    - char *tmp;
    -
    - tmp = strdup(text);
    -
    - if (tmp == NULL)
    - return -1;
    -
    - free(gm->text);
    - gm->text = tmp;
    - }
    -
    - free(gm->html_converted);
    - gm->html_converted = NULL;
    -
    - return 0;
    -}
    -
    -const char *gg_message_get_text(gg_message_t *gm)
    -{
    - GG_MESSAGE_CHECK(gm, NULL);
    -
    - if (gm->text_converted != NULL)
    - return gm->text_converted;
    -
    - if (gm->text == NULL && gm->html != NULL && gm->auto_convert) {
    - size_t len;
    -
    - free(gm->text_converted);
    -
    - len = gg_message_html_to_text(NULL, gm->html);
    -
    - gm->text_converted = malloc(len + 1);
    -
    - if (gm->text_converted == NULL)
    - return NULL;
    -
    - gg_message_html_to_text(gm->text_converted, gm->html);
    -
    - return gm->text_converted;
    - }
    -
    - return gm->text;
    -}
    -
    -int gg_message_set_html(gg_message_t *gm, const char *html)
    -{
    - GG_MESSAGE_CHECK(gm, -1);
    -
    - if (html == NULL) {
    - free(gm->html);
    - gm->html = NULL;
    - } else {
    - char *tmp;
    -
    - tmp = strdup(html);
    -
    - if (tmp == NULL)
    - return -1;
    -
    - free(gm->html);
    - gm->html = tmp;
    - }
    -
    - free(gm->text_converted);
    - gm->text_converted = NULL;
    -
    - return 0;
    -}
    -
    -const char *gg_message_get_html(gg_message_t *gm)
    -{
    - GG_MESSAGE_CHECK(gm, NULL);
    -
    - if (gm->html_converted != NULL)
    - return gm->html_converted;
    -
    - if (gm->html == NULL && gm->text != NULL && gm->auto_convert) {
    - size_t len;
    -
    - free(gm->html_converted);
    -
    - len = gg_message_text_to_html(NULL, gm->text, GG_ENCODING_UTF8, gm->attributes, gm->attributes_length);
    -
    - gm->html_converted = malloc(len + 1);
    -
    - if (gm->html_converted == NULL)
    - return NULL;
    -
    - gg_message_text_to_html(gm->html_converted, gm->text,
    - GG_ENCODING_UTF8, gm->attributes, gm->attributes_length);
    -
    - return gm->html_converted;
    - }
    -
    - return gm->html;
    -}
    -
    -int gg_message_set_attributes(gg_message_t *gm, const char *attributes, size_t length)
    -{
    - GG_MESSAGE_CHECK(gm, -1);
    -
    - if (length > 0xfffd) {
    - /* errno = XXX; */
    - return -1;
    - }
    -
    - if ((attributes == NULL) || (length == 0)) {
    - free(gm->attributes);
    - gm->attributes = NULL;
    - gm->attributes_length = 0;
    - } else {
    - char *tmp;
    -
    - tmp = realloc(gm->attributes, length);
    -
    - if (tmp == NULL)
    - return -1;
    -
    - gm->attributes = tmp;
    - gm->attributes_length = length;
    - }
    -
    - free(gm->html_converted);
    - gm->html_converted = NULL;
    -
    - return 0;
    -}
    -
    -int gg_message_get_attributes(gg_message_t *gm, const char **attributes, size_t *attributes_length)
    -{
    - GG_MESSAGE_CHECK(gm, -1);
    -
    - if (attributes != NULL)
    - *attributes = gm->attributes;
    -
    - if (attributes_length != NULL)
    - *attributes_length = gm->attributes_length;
    -
    - return 0;
    -}
    -
    -#endif
    -
    -/**
    - * \internal Dodaje tekst na koniec bufora.
    - *
    - * \param dst Wskaźnik na bufor roboczy
    - * \param pos Wskaźnik na aktualne położenie w buforze roboczym
    - * \param src Dodawany tekst
    - * \param len Długość dodawanego tekstu
    - */
    -static void gg_append(char *dst, size_t *pos, const void *src, size_t len)
    -{
    - if (dst != NULL)
    - memcpy(&dst[*pos], src, len);
    -
    - *pos += len;
    -}
    -
    -/**
    - * \internal Zamienia tekst z formatowaniem Gadu-Gadu na HTML.
    - *
    - * \param dst Bufor wynikowy (może być \c NULL)
    - * \param src Tekst źródłowy
    - * \param encoding Kodowanie tekstu źródłowego oraz wynikowego
    - * \param format Atrybuty tekstu źródłowego
    - * \param format_len Długość bloku atrybutów tekstu źródłowego
    - *
    - * \note Wynikowy tekst nie jest idealnym kodem HTML, ponieważ ma jak
    - * dokładniej odzwierciedlać to, co wygenerowałby oryginalny klient.
    - *
    - * \note Dokleja \c \\0 na końcu bufora wynikowego.
    - *
    - * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
    - */
    -size_t gg_message_text_to_html(char *dst, const char *src,
    - gg_encoding_t encoding, const unsigned char *format, size_t format_len)
    -{
    - const char span_fmt[] = "<span style=\"color:#%02x%02x%02x; font-family:'MS Shell Dlg 2'; font-size:9pt; \">";
    - const size_t span_len = 75;
    - const char img_fmt[] = "<img name=\"%02x%02x%02x%02x%02x%02x%02x%02x\">";
    - const size_t img_len = 29;
    - size_t char_pos = 0;
    - unsigned char old_attr = 0;
    - const unsigned char default_color[] = {'\x00', '\x00', '\x00'};
    - const unsigned char *old_color = NULL;
    - int in_span = 0;
    - unsigned int i;
    - size_t len = 0;
    -
    - if (format == NULL)
    - format_len = 0;
    -
    - /* Pętla przechodzi też przez kończące \0, żeby móc dokleić obrazek
    - * na końcu tekstu. */
    -
    - for (i = 0; ; i++) {
    - int in_char = 0;
    - size_t format_idx = 0;
    -
    - /* Sprawdź, czy bajt jest kontynuacją znaku UTF-8. */
    - if (encoding == GG_ENCODING_UTF8 && (src[i] & 0xc0) == 0x80)
    - in_char = 1;
    -
    - /* GG_FONT_IMAGE powinno dotyczyć tylko jednego znaku, więc czyścimy stary atrybut */
    -
    - if (!in_char && (old_attr & GG_FONT_IMAGE) != 0)
    - old_attr &= ~GG_FONT_IMAGE;
    -
    - /* Analizuj wszystkie atrybuty dotyczące aktualnego znaku. */
    - for (;;) {
    - unsigned char attr;
    - size_t attr_pos;
    -
    - /* Nie wstawiamy niczego wewnątrz wielobajtowego znaku UTF-8. */
    - if (in_char)
    - break;
    -
    - if (format_idx + 3 > format_len)
    - break;
    -
    - /* (format_idx + 3 <= format_len) && (format_idx > 0)
    - * 3 < format_len
    - * 0 != format_len
    - * format != NULL
    - */
    - assert(format != NULL);
    -
    - attr_pos = format[format_idx] | (format[format_idx + 1] << 8);
    - attr = format[format_idx + 2];
    -
    - /* Nie doklejaj atrybutów na końcu, co najwyżej obrazki. */
    -
    - if (src[i] == 0)
    - attr &= ~(GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR);
    -
    - format_idx += 3;
    -
    - if (attr_pos != char_pos) {
    - if ((attr & GG_FONT_COLOR) != 0)
    - format_idx += 3;
    - if ((attr & GG_FONT_IMAGE) != 0)
    - format_idx += 10;
    -
    - continue;
    - }
    -
    - if ((old_attr & GG_FONT_UNDERLINE) != 0)
    - gg_append(dst, &len, "</u>", 4);
    -
    - if ((old_attr & GG_FONT_ITALIC) != 0)
    - gg_append(dst, &len, "</i>", 4);
    -
    - if ((old_attr & GG_FONT_BOLD) != 0)
    - gg_append(dst, &len, "</b>", 4);
    -
    - if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0) {
    - const unsigned char *color;
    -
    - if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) {
    - color = &format[format_idx];
    - format_idx += 3;
    - } else {
    - color = default_color;
    - }
    -
    - if (old_color == NULL || memcmp(color, old_color, 3) != 0) {
    - if (in_span) {
    - gg_append(dst, &len, "</span>", 7);
    - in_span = 0;
    - }
    -
    - if (src[i] != 0) {
    - if (dst != NULL)
    - sprintf(&dst[len], span_fmt, color[0], color[1], color[2]);
    -
    - len += span_len;
    - in_span = 1;
    - old_color = color;
    - }
    - }
    - }
    -
    - if ((attr & GG_FONT_BOLD) != 0)
    - gg_append(dst, &len, "<b>", 3);
    -
    - if ((attr & GG_FONT_ITALIC) != 0)
    - gg_append(dst, &len, "<i>", 3);
    -
    - if ((attr & GG_FONT_UNDERLINE) != 0)
    - gg_append(dst, &len, "<u>", 3);
    -
    - if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) {
    - if (dst != NULL) {
    - sprintf(&dst[len], img_fmt,
    - format[format_idx + 9],
    - format[format_idx + 8],
    - format[format_idx + 7],
    - format[format_idx + 6],
    - format[format_idx + 5],
    - format[format_idx + 4],
    - format[format_idx + 3],
    - format[format_idx + 2]);
    - }
    -
    - len += img_len;
    - format_idx += 10;
    - }
    -
    - old_attr = attr;
    - }
    -
    - if (src[i] == 0)
    - break;
    -
    - /* Znaki oznaczone jako GG_FONT_IMAGE nie są częścią wiadomości. */
    -
    - if ((old_attr & GG_FONT_IMAGE) != 0) {
    - if (!in_char)
    - char_pos++;
    -
    - continue;
    - }
    -
    - /* Jesteśmy na początku tekstu i choć nie było atrybutów dla pierwszego
    - * znaku, ponieważ tekst nie jest pusty, trzeba otworzyć <span>. */
    -
    - if (!in_span) {
    - if (dst != NULL)
    - sprintf(&dst[len], span_fmt, default_color[0], default_color[1], default_color[2]);
    -
    - len += span_len;
    - in_span = 1;
    - old_color = default_color;
    - }
    -
    - /* Doklej znak zachowując htmlowe escapowanie. */
    -
    - switch (src[i]) {
    - case '&':
    - gg_append(dst, &len, "&amp;", 5);
    - break;
    - case '<':
    - gg_append(dst, &len, "&lt;", 4);
    - break;
    - case '>':
    - gg_append(dst, &len, "&gt;", 4);
    - break;
    - case '\'':
    - gg_append(dst, &len, "&apos;", 6);
    - break;
    - case '\"':
    - gg_append(dst, &len, "&quot;", 6);
    - break;
    - case '\n':
    - gg_append(dst, &len, "<br>", 4);
    - break;
    - case '\r':
    - break;
    - default:
    - if (dst != NULL)
    - dst[len] = src[i];
    - len++;
    - }
    -
    - if (!in_char)
    - char_pos++;
    - }
    -
    - /* Zamknij tagi. */
    -
    - if ((old_attr & GG_FONT_UNDERLINE) != 0)
    - gg_append(dst, &len, "</u>", 4);
    -
    - if ((old_attr & GG_FONT_ITALIC) != 0)
    - gg_append(dst, &len, "</i>", 4);
    -
    - if ((old_attr & GG_FONT_BOLD) != 0)
    - gg_append(dst, &len, "</b>", 4);
    -
    - if (in_span)
    - gg_append(dst, &len, "</span>", 7);
    -
    - if (dst != NULL)
    - dst[len] = 0;
    -
    - return len;
    -}
    -
    -/**
    - * \internal Dokleja nowe atrybuty formatowania, jeśli konieczne, oraz inkrementuje pozycję znaku w tekście.
    - *
    - * \param pos Wskaźnik na zmienną przechowującą pozycję znaku w tekście
    - * \param attr_flag Aktualna flaga atrybutu formatowania
    - * \param old_attr_flag Wskaźnik na poprzednią flagę atrybutu formatowania
    - * \param color Wskaźnik na tablicę z aktualnym kolorem RGB (jeśli \p attr_flag
    - * nie zawiera flagi \c GG_FONT_COLOR, ignorowane)
    - * \param old_color Wskaźnik na tablicę z poprzednim kolorem RGB
    - * \param imgs_size Rozmiar atrybutów formatowania obrazków znajdujących się
    - * obecnie w tablicy atrybutów formatowania, w bajtach
    - * \param format Wskaźnik na wskaźnik do tablicy atrybutów formatowania
    - * \param format_len Wskaźnik na zmienną zawierającą długość tablicy atrybutów
    - * formatowania, w bajtach (może być \c NULL)
    - */
    -static void gg_after_append_formatted_char(uint16_t *pos,
    - unsigned char attr_flag, unsigned char *old_attr_flag,
    - const unsigned char *color, unsigned char *old_color, size_t imgs_size,
    - unsigned char **format, size_t *format_len)
    -{
    - const size_t color_size = 3;
    - int has_color = 0;
    -
    - if ((attr_flag & GG_FONT_COLOR) != 0)
    - has_color = 1;
    -
    - if (*old_attr_flag != attr_flag || (has_color && memcmp(old_color, color, color_size) != 0)) {
    - size_t attr_size = sizeof(*pos) + sizeof(attr_flag) + (has_color ? color_size : 0);
    -
    - if (*format != NULL) {
    - /* Staramy się naśladować oryginalnego klienta i atrybuty obrazków trzymamy na końcu */
    -
    - *format -= imgs_size;
    - memmove(*format + attr_size, *format, imgs_size);
    -
    - **format = (unsigned char) (*pos & (uint16_t) 0x00ffU);
    - *format += 1;
    - **format = (unsigned char) ((*pos & (uint16_t) 0xff00U) >> 8);
    - *format += 1;
    -
    - **format = attr_flag;
    - *format += 1;
    -
    - if (has_color) {
    - memcpy(*format, color, color_size);
    - *format += color_size;
    - }
    -
    - *format += imgs_size;
    - }
    -
    - if (format_len != NULL)
    - *format_len += attr_size;
    -
    - *old_attr_flag = attr_flag;
    - if (has_color)
    - memcpy(old_color, color, color_size);
    - }
    -
    - *pos += 1;
    -}
    -
    -/**
    - * \internal Zamienia tekst w formacie HTML na czysty tekst.
    - *
    - * \param dst Bufor wynikowy (może być \c NULL)
    - * \param format Bufor wynikowy z atrybutami formatowania (może być \c NULL)
    - * \param format_len Wskaźnik na zmienną, do której zostanie zapisana potrzebna
    - * wielkość bufora wynikowego z atrybutami formatowania,
    - * w bajtach (może być \c NULL)
    - * \param html Tekst źródłowy
    - * \param encoding Kodowanie tekstu źródłowego oraz wynikowego
    - *
    - * \note Dokleja \c \\0 na końcu bufora wynikowego.
    - *
    - * \return Długość bufora wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
    - */
    -size_t gg_message_html_to_text(char *dst, unsigned char *format,
    - size_t *format_len, const char *html, gg_encoding_t encoding)
    -{
    - const char *src, *entity = NULL, *tag = NULL;
    - int in_tag = 0, in_entity = 0, in_bold = 0, in_italic = 0, in_underline = 0;
    - unsigned char color[3] = { 0 }, old_color[3] = { 0 };
    - unsigned char attr_flag = 0, old_attr_flag = 0;
    - uint16_t pos = 0;
    - size_t len = 0, imgs_size = 0;
    -
    - if (format_len != NULL)
    - *format_len = 0;
    -
    - for (src = html; *src != 0; src++) {
    - if (in_entity && !(isalnum(*src) || *src == '#' || *src == ';')) {
    - int first = 1;
    - size_t i, append_len = src - entity;
    -
    - gg_append(dst, &len, entity, append_len);
    - for (i = 0; i < append_len; i++) {
    - if (encoding != GG_ENCODING_UTF8 || (entity[i] & 0xc0) != 0x80) {
    - if (first) {
    - gg_after_append_formatted_char(&pos, attr_flag,
    - &old_attr_flag, color, old_color, imgs_size,
    - &format, format_len);
    - first = 0;
    - } else {
    - pos++;
    - }
    - }
    - }
    -
    - in_entity = 0;
    - }
    -
    - if (*src == '<') {
    - tag = src;
    - in_tag = 1;
    - continue;
    - }
    -
    - if (in_tag && (*src == '>')) {
    - if (strncmp(tag, "<br", 3) == 0) {
    - if (dst != NULL)
    - dst[len] = '\n';
    - len++;
    -
    - gg_after_append_formatted_char(&pos, attr_flag,
    - &old_attr_flag, color, old_color,
    - imgs_size, &format, format_len);
    - } else if (strncmp(tag, "<img name=\"", 11) == 0 || strncmp(tag, "<img name=\'", 11) == 0) {
    - tag += 11;
    -
    - /* 17 bo jeszcze cudzysłów musi być zamknięty */
    - if (tag + 17 <= src) {
    - int i, ok = 1;
    -
    - for (i = 0; i < 16; i++) {
    - if (!isxdigit(tag[i])) {
    - ok = 0;
    - break;
    - }
    - }
    -
    - if (ok) {
    - unsigned char img_attr[13];
    -
    - if (format != NULL) {
    - char buf[3] = { 0 };
    -
    - img_attr[0] = (unsigned char) (pos & (uint16_t) 0x00ffU);
    - img_attr[1] = (unsigned char) ((pos & (uint16_t) 0xff00U) >> 8);
    - img_attr[2] = GG_FONT_IMAGE;
    - img_attr[3] = '\x09';
    - img_attr[4] = '\x01';
    - for (i = 0; i < 16; i += 2) {
    - buf[0] = tag[i];
    - buf[1] = tag[i + 1];
    - /* buf[2] to '\0' */
    - img_attr[12 - i / 2] =
    - (unsigned char)strtoul(buf, NULL, 16);
    - }
    -
    - memcpy(format, img_attr, sizeof(img_attr));
    - format += sizeof(img_attr);
    - }
    -
    - if (format_len != NULL)
    - *format_len += sizeof(img_attr);
    - imgs_size += sizeof(img_attr);
    -
    - if (dst != NULL) {
    - if (encoding == GG_ENCODING_UTF8)
    - dst[len++] = '\xc2';
    - dst[len++] = '\xa0';
    - } else {
    - len += 2;
    - }
    -
    - /* Nie używamy tutaj gg_after_append_formatted_char().
    - * Po pierwsze to praktycznie niczego by nie
    - * zmieniło, a po drugie nie wszystkim klientom
    - * mogłaby się spodobać redefinicja atrybutów
    - * formatowania dla jednego znaku (bo np. najpierw
    - * byśmy zdefiniowali bolda od znaku 10, a potem
    - * by się okazało, że znak 10 to obrazek).
    - */
    -
    - pos++;
    -
    - /* Resetujemy atrybuty, aby je w razie czego
    - * redefiniować od następnego znaku, co by sobie
    - * nikt przypadkiem nie pomyślał, że GG_FONT_IMAGE
    - * dotyczy więcej, niż jednego znaku.
    - * Tak samo robi oryginalny klient.
    - */
    -
    - old_attr_flag = -1;
    - }
    - }
    - } else if (strncmp(tag, "<b>", 3) == 0) {
    - in_bold++;
    - attr_flag |= GG_FONT_BOLD;
    - } else if (strncmp(tag, "</b>", 4) == 0) {
    - if (in_bold > 0) {
    - in_bold--;
    - if (in_bold == 0)
    - attr_flag &= ~GG_FONT_BOLD;
    - }
    - } else if (strncmp(tag, "<i>", 3) == 0) {
    - in_italic++;
    - attr_flag |= GG_FONT_ITALIC;
    - } else if (strncmp(tag, "</i>", 4) == 0) {
    - if (in_italic > 0) {
    - in_italic--;
    - if (in_italic == 0)
    - attr_flag &= ~GG_FONT_ITALIC;
    - }
    - } else if (strncmp(tag, "<u>", 3) == 0) {
    - in_underline++;
    - attr_flag |= GG_FONT_UNDERLINE;
    - } else if (strncmp(tag, "</u>", 4) == 0) {
    - if (in_underline > 0) {
    - in_underline--;
    - if (in_underline == 0)
    - attr_flag &= ~GG_FONT_UNDERLINE;
    - }
    - } else if (strncmp(tag, "<span ", 6) == 0) {
    - for (tag += 6; tag < src - 8; tag++) {
    - if (*tag == '\"' || *tag == '\'' || *tag == ' ') {
    - if (strncmp(tag + 1, "color:#", 7) == 0) {
    - int i, ok = 1;
    - char buf[3] = { 0 };
    -
    - tag += 8;
    - if (tag + 6 > src)
    - break;
    -
    - for (i = 0; i < 6; i++) {
    - if (!isxdigit(tag[i])) {
    - ok = 0;
    - break;
    - }
    - }
    -
    - if (!ok)
    - break;
    -
    - for (i = 0; i < 6; i += 2) {
    - buf[0] = tag[i];
    - buf[1] = tag[i + 1];
    - /* buf[2] to '\0' */
    - color[i / 2] = (unsigned char) strtoul(buf, NULL, 16);
    - }
    -
    - attr_flag |= GG_FONT_COLOR;
    - }
    - }
    - }
    - } else if (strncmp(tag, "</span", 6) == 0) {
    - /* Można by trzymać kolory na stosie i tutaj
    - * przywracać poprzedni, ale to raczej zbędne */
    -
    - attr_flag &= ~GG_FONT_COLOR;
    - }
    -
    - tag = NULL;
    - in_tag = 0;
    - continue;
    - }
    -
    - if (in_tag)
    - continue;
    -
    - if (*src == '&') {
    - in_entity = 1;
    - entity = src;
    - continue;
    - }
    -
    - if (in_entity && *src == ';') {
    - in_entity = 0;
    -
    - if (dst != NULL) {
    - if (strncmp(entity, "&lt;", 4) == 0)
    - dst[len++] = '<';
    - else if (strncmp(entity, "&gt;", 4) == 0)
    - dst[len++] = '>';
    - else if (strncmp(entity, "&quot;", 6) == 0)
    - dst[len++] = '"';
    - else if (strncmp(entity, "&apos;", 6) == 0)
    - dst[len++] = '\'';
    - else if (strncmp(entity, "&amp;", 5) == 0)
    - dst[len++] = '&';
    - else if (strncmp(entity, "&nbsp;", 6) == 0) {
    - if (encoding == GG_ENCODING_UTF8)
    - dst[len++] = '\xc2';
    - dst[len++] = '\xa0';
    - } else
    - dst[len++] = '?';
    - } else {
    - if (strncmp(entity, "&nbsp;", 6) == 0)
    - len += 2;
    - else
    - len++;
    - }
    -
    - gg_after_append_formatted_char(&pos, attr_flag,
    - &old_attr_flag, color, old_color, imgs_size,
    - &format, format_len);
    -
    - continue;
    - }
    -
    - if (in_entity && !(isalnum(*src) || *src == '#'))
    - in_entity = 0;
    -
    - if (in_entity)
    - continue;
    -
    - if (dst != NULL)
    - dst[len] = *src;
    - len++;
    -
    - if (encoding != GG_ENCODING_UTF8 || (*src & 0xc0) != 0x80) {
    - gg_after_append_formatted_char(&pos, attr_flag,
    - &old_attr_flag, color, old_color, imgs_size,
    - &format, format_len);
    - }
    - }
    -
    - if (dst != NULL)
    - dst[len] = '\0';
    -
    - return len;
    -}
    -
    -static size_t gg_message_html_to_text_110_buff(char *dst, const char *html)
    -{
    - return gg_message_html_to_text(dst, NULL, NULL, html, GG_ENCODING_UTF8);
    -}
    -
    -static size_t gg_message_text_to_html_110_buff(char *dst, const char *text,
    - ssize_t text_len)
    -{
    - size_t i, dst_len;
    -
    - if (text_len == -1)
    - text_len = strlen(text);
    - dst_len = 0;
    -
    - gg_append(dst, &dst_len, "<span>", 6);
    -
    - for (i = 0; i < (size_t)text_len; i++) {
    - char c = text[i];
    - if (c == '<')
    - gg_append(dst, &dst_len, "&lt;", 4);
    - else if (c == '>')
    - gg_append(dst, &dst_len, "&gt;", 4);
    - else if (c == '&')
    - gg_append(dst, &dst_len, "&amp;", 5);
    - else if (c == '"')
    - gg_append(dst, &dst_len, "&quot;", 6);
    - else if (c == '\'')
    - gg_append(dst, &dst_len, "&apos;", 6);
    - else if (c == '\n')
    - gg_append(dst, &dst_len, "<br>", 4);
    - else if (c == '\r')
    - continue;
    - else if (c == '\xc2' && text[i + 1] == '\xa0') {
    - gg_append(dst, &dst_len, "&nbsp;", 6);
    - i++;
    - } else {
    - if (dst)
    - dst[dst_len] = c;
    - dst_len++;
    - }
    - }
    -
    - gg_append(dst, &dst_len, "</span>", 7);
    -
    - if (dst)
    - dst[dst_len] = '\0';
    -
    - return dst_len;
    -}
    -
    -char *gg_message_html_to_text_110(const char *html)
    -{
    - size_t dst_len;
    - char *dst;
    -
    - dst_len = gg_message_html_to_text_110_buff(NULL, html) + 1;
    - dst = malloc(dst_len);
    - if (!dst)
    - return NULL;
    - gg_message_html_to_text_110_buff(dst, html);
    -
    - return dst;
    -}
    -
    -char *gg_message_text_to_html_110(const char *text, ssize_t text_len)
    -{
    - size_t dst_len;
    - char *dst;
    -
    - dst_len = gg_message_text_to_html_110_buff(NULL, text, text_len) + 1;
    - dst = malloc(dst_len);
    - if (!dst)
    - return NULL;
    - gg_message_text_to_html_110_buff(dst, text, text_len);
    -
    - return dst;
    -}
    --- a/libpurple/protocols/gg/lib/message.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,62 +0,0 @@
    -/*
    - * (C) Copyright 2009 Wojtek Kaniewski <wojtekka@irc.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -#ifndef LIBGADU_MESSAGE_H
    -#define LIBGADU_MESSAGE_H
    -
    -#include <sys/types.h>
    -#include "libgadu.h"
    -
    -#if 0
    -
    -struct gg_message {
    - uin_t *recipients;
    - size_t recipient_count;
    - char *text;
    - char *html;
    - char *attributes;
    - size_t attributes_length;
    - uint32_t msgclass;
    - uint32_t seq;
    -
    - int auto_convert;
    - char *text_converted;
    - char *html_converted;
    -};
    -
    -#define GG_MESSAGE_CHECK(gm, result) \
    - if ((gm) == NULL) { \
    - errno = EINVAL; \
    - return (result); \
    - }
    -
    -int gg_message_init(gg_message_t *gm, int msgclass, int seq, uin_t *recipients,
    - size_t recipient_count, char *text, char *xhtml, char *attributes,
    - size_t attributes_length, int auto_convert);
    -
    -#endif
    -
    -size_t gg_message_html_to_text(char *dst, unsigned char *format,
    - size_t *format_len, const char *html, gg_encoding_t encoding);
    -size_t gg_message_text_to_html(char *dst, const char *src,
    - gg_encoding_t encoding, const unsigned char *format, size_t format_len);
    -
    -char * gg_message_html_to_text_110(const char *html);
    -char * gg_message_text_to_html_110(const char *text, ssize_t text_len);
    -
    -#endif /* LIBGADU_MESSAGE_H */
    --- a/libpurple/protocols/gg/lib/network.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,310 +0,0 @@
    -/*
    - * (C) Copyright 2011 Wojtek Kaniewski <wojtekka@irc.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -#include "network.h"
    -#include <stdlib.h>
    -#include <string.h>
    -
    -#ifdef _WIN32
    -
    -/* Code losely based on sockerpair implementation by Nathan C. Meyrs.
    - * The original copyright notice follows: */
    -
    -/* socketpair.c
    - * Copyright 2007, 2010 by Nathan C. Myers <ncm@cantrip.org>
    - * This code is Free Software. It may be copied freely, in original or
    - * modified form, subject only to the restrictions that (1) the author is
    - * relieved from all responsibilities for any use for any purpose, and (2)
    - * this copyright notice must be retained, unchanged, in its entirety. If
    - * for any reason the author might be held responsible for any consequences
    - * of copying or use, license is withheld.
    - */
    -
    -int gg_win32_socketpair(int sv[2])
    -{
    - struct sockaddr_in sin;
    - socklen_t sin_len = sizeof(sin);
    - int server = -1;
    - int tmp = 1;
    - int errno_copy;
    -
    - server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    -
    - sv[0] = -1;
    - sv[1] = -1;
    -
    - if (server == -1)
    - goto fail;
    -
    - memset(&sin, 0, sizeof(sin));
    - sin.sin_family = AF_INET;
    - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    - sin.sin_port = 0;
    -
    - if (setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &tmp, sizeof(tmp)) == -1)
    - goto fail;
    -
    - if (bind(server, (struct sockaddr*) &sin, sin_len) == -1)
    - goto fail;
    -
    - if (listen(server, 1) == -1)
    - goto fail;
    -
    - if (getsockname(server, (struct sockaddr*) &sin, &sin_len) == -1)
    - goto fail;
    -
    - sin.sin_family = AF_INET;
    - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    -
    - sv[0] = socket(AF_INET, SOCK_STREAM, 0);
    -
    - if (sv[0] == -1)
    - goto fail;
    -
    - if (connect(sv[0], (struct sockaddr*) &sin, sin_len) == -1)
    - goto fail;
    -
    - sv[1] = accept(server, NULL, NULL);
    -
    - if (sv[1] == -1)
    - goto fail;
    -
    - close(server);
    -
    - return 0;
    -
    -fail:
    - errno_copy = errno;
    - close(server);
    - close(sv[0]);
    - close(sv[1]);
    - errno = errno_copy;
    -
    - return -1;
    -}
    -
    -static int gg_win32_map_wsa_error_to_errno(int wsaewouldblock_map)
    -{
    - int wsa_error;
    -
    - wsa_error = WSAGetLastError();
    -
    - /* Tutaj powinny być tłumaczone wszystkie typy błędów sprawdzane przez
    - * kod libgadu. Dla spójność są również tłumaczone typy błędów ustawiane
    - * przez libgadu.
    - * Ponadto gdyby okazało się, że jakaś aplikacja na Win32 chce móc
    - * polegać jeszcze na innych wartościach errno, można tutaj dodać
    - * ich tłumaczenie. Najpierw jednak zawsze trzeba porównać dokumentacje,
    - * aby upewnić się co do poprawności tłumaczenia (patrz WSAEWOULDBLOCK,
    - * które można tłumaczyć na EWOULDBLOCK lub EAGAIN, a nawet na
    - * EINPROGRESS w przypadku connect()).
    - */
    - switch (wsa_error)
    - {
    - /* Typy błędów sprawdzane przez libgadu. */
    - case WSAEINTR:
    - return EINTR;
    - case WSAEWOULDBLOCK:
    - return wsaewouldblock_map;
    - /* Typy błędów ustawiane przez libgadu. */
    - case WSAECONNRESET:
    - return ECONNRESET;
    - case WSAEFAULT:
    - return EFAULT;
    - case WSAEINVAL:
    - return EINVAL;
    - case WSAENOTCONN:
    - return ENOTCONN;
    - case WSAETIMEDOUT:
    - return ETIMEDOUT;
    - default:
    - /* Najlepiej zwrócić oryginalny kod błędu. I tak będzie co najwyżej
    - * wyświetlony w komunikacie debugowym, a tym sposobem będzie łatwiej
    - * dojść przyczyny problemu. */
    - return wsa_error;
    - }
    -}
    -
    -#undef accept
    -int gg_win32_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
    -{
    - int res;
    -
    - res = accept(sockfd, addr, addrlen);
    -
    - if (res == -1)
    - errno = gg_win32_map_wsa_error_to_errno(EAGAIN);
    -
    - return res;
    -}
    -
    -#undef bind
    -int gg_win32_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
    -{
    - int res;
    -
    - res = bind(sockfd, addr, addrlen);
    -
    - if (res == -1)
    - errno = gg_win32_map_wsa_error_to_errno(EAGAIN);
    -
    - return res;
    -}
    -
    -int gg_win32_close(int sockfd)
    -{
    - int res;
    -
    - res = closesocket(sockfd);
    -
    - if (res == -1)
    - errno = gg_win32_map_wsa_error_to_errno(EAGAIN);
    -
    - return res;
    -}
    -
    -#undef connect
    -int gg_win32_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
    -{
    - int res;
    -
    - res = connect(sockfd, addr, addrlen);
    -
    - if (res == -1)
    - errno = gg_win32_map_wsa_error_to_errno(EINPROGRESS);
    -
    - return res;
    -}
    -
    -#undef gethostbyname
    -struct hostent *gg_win32_gethostbyname(const char *name)
    -{
    - struct hostent *res;
    -
    - res = gethostbyname(name);
    -
    - if (res == NULL)
    - errno = gg_win32_map_wsa_error_to_errno(EAGAIN);
    -
    - return res;
    -}
    -
    -#undef getsockname
    -int gg_win32_getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
    -{
    - int res;
    -
    - res = getsockname(sockfd, addr, addrlen);
    -
    - if (res == -1)
    - errno = gg_win32_map_wsa_error_to_errno(EAGAIN);
    -
    - return res;
    -}
    -
    -#undef getsockopt
    -int gg_win32_getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen)
    -{
    - int res;
    -
    - res = getsockopt(sockfd, level, optname, optval, optlen);
    -
    - if (res == -1)
    - errno = gg_win32_map_wsa_error_to_errno(EAGAIN);
    -
    - return res;
    -}
    -
    -int gg_win32_ioctl(int d, int request, int *argp)
    -{
    - int res;
    -
    - res = ioctlsocket(d, request, (u_long *)argp);
    -
    - if (res == -1)
    - errno = gg_win32_map_wsa_error_to_errno(EAGAIN);
    -
    - return res;
    -}
    -
    -#undef listen
    -int gg_win32_listen(int sockfd, int backlog)
    -{
    - int res;
    -
    - res = listen(sockfd, backlog);
    -
    - if (res == -1)
    - errno = gg_win32_map_wsa_error_to_errno(EAGAIN);
    -
    - return res;
    -}
    -
    -#undef recv
    -int gg_win32_recv(int sockfd, void *buf, size_t len, int flags)
    -{
    - int res;
    -
    - res = recv(sockfd, buf, len, flags);
    -
    - if (res == -1)
    - errno = gg_win32_map_wsa_error_to_errno(EAGAIN);
    -
    - return res;
    -}
    -
    -#undef send
    -int gg_win32_send(int sockfd, const void *buf, size_t len, int flags)
    -{
    - int res;
    -
    - res = send(sockfd, buf, len, flags);
    -
    - if (res == -1)
    - errno = gg_win32_map_wsa_error_to_errno(EAGAIN);
    -
    - return res;
    -}
    -
    -#undef setsockopt
    -int gg_win32_setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)
    -{
    - int res;
    -
    - res = setsockopt(sockfd, level, optname, optval, optlen);
    -
    - if (res == -1)
    - errno = gg_win32_map_wsa_error_to_errno(EAGAIN);
    -
    - return res;
    -}
    -
    -#undef socket
    -int gg_win32_socket(int domain, int type, int protocol)
    -{
    - int res;
    -
    - res = socket(domain, type, protocol);
    -
    - if (res == -1)
    - errno = gg_win32_map_wsa_error_to_errno(EAGAIN);
    -
    - return res;
    -}
    -
    -#endif /* _WIN32 */
    --- a/libpurple/protocols/gg/lib/network.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,125 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
    - * Robert J. Woźny <speedy@ziew.org>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file network.h
    - *
    - * \brief Makra zapewniające kompatybilność API do obsługi sieci na różnych systemach
    - */
    -
    -#ifndef LIBGADU_NETWORK_H
    -#define LIBGADU_NETWORK_H
    -
    -#ifdef _WIN32
    -# include <ws2tcpip.h>
    -# include <winsock2.h>
    -# include <stdlib.h>
    -# include <stdio.h>
    -# include <errno.h>
    -/* Obecnie na Win32 tylko MSVC definiuje te typy błędów. Na wypadek, gdyby
    - * jednak Cygwin bądź MinGW zaczęły je definiować, używamy bardziej ogólnych
    - * ifdefów. */
    -# ifndef ECONNRESET
    -# define ECONNRESET WSAECONNRESET
    -# endif
    -# ifndef EINPROGRESS
    -# define EINPROGRESS WSAEINPROGRESS
    -# endif
    -# ifndef ENOTCONN
    -# define ENOTCONN WSAENOTCONN
    -# endif
    -# ifndef ETIMEDOUT
    -# define ETIMEDOUT WSAETIMEDOUT
    -# endif
    -# define accept gg_win32_accept
    -# define bind gg_win32_bind
    -# define close gg_win32_close
    -# define connect gg_win32_connect
    -# define gethostbyname gg_win32_gethostbyname
    -# define getsockname gg_win32_getsockname
    -# define getsockopt gg_win32_getsockopt
    -# define ioctl gg_win32_ioctl
    -# define listen gg_win32_listen
    -# define recv gg_win32_recv
    -# define send gg_win32_send
    -# define setsockopt gg_win32_setsockopt
    -# define socket gg_win32_socket
    -# define socketpair(a, b, c, d) gg_win32_socketpair(d)
    -int gg_win32_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    -int gg_win32_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    -int gg_win32_close(int sockfd);
    -int gg_win32_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
    -struct hostent *gg_win32_gethostbyname(const char *name);
    -int gg_win32_getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    -int gg_win32_getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
    -int gg_win32_ioctl(int d, int request, int *argp);
    -int gg_win32_listen(int sockfd, int backlog);
    -int gg_win32_recv(int sockfd, void *buf, size_t len, int flags);
    -int gg_win32_send(int sockfd, const void *buf, size_t len, int flags);
    -int gg_win32_setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
    -int gg_win32_socket(int domain, int type, int protocol);
    -int gg_win32_socketpair(int sv[2]);
    -
    -static inline void gg_win32_init_network(void)
    -{
    - WSADATA wsaData;
    -
    - if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
    - perror("WSAStartup");
    - exit(1);
    - }
    -}
    -
    -#else
    -# include <sys/ioctl.h>
    -# include <sys/types.h>
    -# include <sys/socket.h>
    -# include <netinet/in.h>
    -# include <arpa/inet.h>
    -# include <netdb.h>
    -# include <unistd.h>
    -# ifndef FIONBIO
    -# include <fcntl.h>
    -# endif
    -#endif
    -
    -#ifndef INADDR_NONE
    -# define INADDR_NONE ((in_addr_t) 0xffffffff)
    -#endif
    -
    -#ifndef AF_LOCAL
    -# define AF_LOCAL AF_UNIX
    -#endif
    -
    -static inline int gg_fd_set_nonblocking(int fd)
    -{
    - int success;
    -#ifdef FIONBIO
    - int one = 1;
    - success = (ioctl(fd, FIONBIO, &one) == 0);
    -#else
    - success = (fcntl(fd, F_SETFL, O_NONBLOCK) == 0);
    -#endif
    -
    - return success;
    -}
    -
    -#endif /* LIBGADU_NETWORK_H */
    --- a/libpurple/protocols/gg/lib/obsolete.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,250 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file obsolete.c
    - *
    - * \brief Nieaktualne funkcje
    - *
    - * Plik zawiera definicje funkcji, które są już nieaktualne ze względu
    - * na zmiany w protokole. Programy konsolidowane ze starszych wersjami
    - * bibliotek powinny nadal mieć możliwość działania, mimo ograniczonej
    - * funkcjonalności.
    - */
    -
    -/** \cond obsolete */
    -
    -#include <errno.h>
    -#include <string.h>
    -
    -#include "libgadu.h"
    -#include "internal.h"
    -
    -struct gg_http *gg_userlist_get(uin_t uin, const char *passwd, int async)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_userlist_get() is obsolete. use gg_userlist_request() instead!\n");
    - errno = EINVAL;
    - return NULL;
    -}
    -
    -int gg_userlist_get_watch_fd(struct gg_http *h)
    -{
    - errno = EINVAL;
    - return -1;
    -}
    -
    -void gg_userlist_get_free(struct gg_http *h)
    -{
    -
    -}
    -
    -struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_userlist_put() is obsolete. use gg_userlist_request() instead!\n");
    - errno = EINVAL;
    - return NULL;
    -}
    -
    -int gg_userlist_put_watch_fd(struct gg_http *h)
    -{
    - errno = EINVAL;
    - return -1;
    -}
    -
    -void gg_userlist_put_free(struct gg_http *h)
    -{
    -
    -}
    -
    -struct gg_http *gg_userlist_remove(uin_t uin, const char *passwd, int async)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_userlist_remove() is obsolete. use gg_userlist_request() instead!\n");
    - errno = EINVAL;
    - return NULL;
    -}
    -
    -int gg_userlist_remove_watch_fd(struct gg_http *h)
    -{
    - errno = EINVAL;
    - return -1;
    -}
    -
    -void gg_userlist_remove_free(struct gg_http *h)
    -{
    -
    -}
    -
    -struct gg_http *gg_search(const struct gg_search_request *r, int async)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_search() is obsolete. use gg_search50() instead!\n");
    - errno = EINVAL;
    - return NULL;
    -}
    -
    -int gg_search_watch_fd(struct gg_http *h)
    -{
    - errno = EINVAL;
    - return -1;
    -}
    -
    -void gg_search_free(struct gg_http *h)
    -{
    -
    -}
    -
    -const struct gg_search_request *gg_search_request_mode_0(char *nickname,
    - char *first_name, char *last_name, char *city, int gender,
    - int min_birth, int max_birth, int active, int start)
    -{
    - return NULL;
    -}
    -
    -const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start)
    -{
    - return NULL;
    -}
    -
    -const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start)
    -{
    - return NULL;
    -}
    -
    -const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start)
    -{
    - return NULL;
    -}
    -
    -void gg_search_request_free(struct gg_search_request *r)
    -{
    -
    -}
    -
    -struct gg_http *gg_register(const char *email, const char *password, int async)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_register() is obsolete. use gg_register3() instead!\n");
    - errno = EINVAL;
    - return NULL;
    -}
    -
    -struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_register2() is obsolete. use gg_register3() instead!\n");
    - errno = EINVAL;
    - return NULL;
    -}
    -
    -struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_unregister() is obsolete. use gg_unregister3() instead!\n");
    - errno = EINVAL;
    - return NULL;
    -}
    -
    -struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_unregister2() is obsolete. use gg_unregister3() instead!\n");
    - errno = EINVAL;
    - return NULL;
    -}
    -
    -
    -struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_change_passwd() is obsolete. use gg_change_passwd4() instead!\n");
    - errno = EINVAL;
    - return NULL;
    -}
    -
    -struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd,
    - const char *newpasswd, const char *email, const char *newemail,
    - int async)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_change_passwd2() is obsolete. use gg_change_passwd4() instead!\n");
    - errno = EINVAL;
    - return NULL;
    -}
    -
    -struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_change_passwd3() is obsolete. use gg_change_passwd4() instead!\n");
    - errno = EINVAL;
    - return NULL;
    -}
    -
    -struct gg_http *gg_remind_passwd(uin_t uin, int async)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_remind_passwd() is obsolete. use gg_remind_passwd3() instead!\n");
    - errno = EINVAL;
    - return NULL;
    -}
    -
    -struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_remind_passwd2() is obsolete. use gg_remind_passwd3() instead!\n");
    - errno = EINVAL;
    - return NULL;
    -}
    -
    -struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async)
    -{
    - gg_debug(GG_DEBUG_MISC, "// gg_change_info() is obsolete. use gg_pubdir50() instead\n");
    - errno = EINVAL;
    - return NULL;
    -}
    -
    -struct gg_change_info_request *gg_change_info_request_new(
    - const char *first_name, const char *last_name, const char *nickname,
    - const char *email, int born, int gender, const char *city)
    -{
    - return NULL;
    -}
    -
    -void gg_change_info_request_free(struct gg_change_info_request *r)
    -{
    -
    -}
    -
    -int gg_resolve(int *fd, int *pid, const char *hostname)
    -{
    - return -1;
    -}
    -
    -void gg_resolve_pthread_cleanup(void *arg, int kill)
    -{
    -
    -}
    -
    -int gg_resolve_pthread(int *fd, void **resolver, const char *hostname)
    -{
    - return -1;
    -}
    -
    -int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
    -{
    - return -1;
    -}
    -
    -void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result)
    -{
    - if (gg_login_hash_sha1_2(password, seed, result) != 0)
    - memset(result, 0, 20);
    -}
    -
    -/** \endcond */
    --- a/libpurple/protocols/gg/lib/packets.pb-c.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,2603 +0,0 @@
    -/* Generated by the protocol buffer compiler. DO NOT EDIT! */
    -/* Generated from: packets.proto */
    -
    -/* Do not generate deprecated warnings for self */
    -#ifndef PROTOBUF_C__NO_DEPRECATED
    -#define PROTOBUF_C__NO_DEPRECATED
    -#endif
    -
    -#include "packets.pb-c.h"
    -void gg110_login_ok__init
    - (GG110LoginOK *message)
    -{
    - static GG110LoginOK init_value = GG110_LOGIN_OK__INIT;
    - *message = init_value;
    -}
    -size_t gg110_login_ok__get_packed_size
    - (const GG110LoginOK *message)
    -{
    - assert(message->base.descriptor == &gg110_login_ok__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg110_login_ok__pack
    - (const GG110LoginOK *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg110_login_ok__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg110_login_ok__pack_to_buffer
    - (const GG110LoginOK *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg110_login_ok__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG110LoginOK *
    - gg110_login_ok__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG110LoginOK *)
    - protobuf_c_message_unpack (&gg110_login_ok__descriptor,
    - allocator, len, data);
    -}
    -void gg110_login_ok__free_unpacked
    - (GG110LoginOK *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg110_login_ok__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void gg110_pong__init
    - (GG110Pong *message)
    -{
    - static GG110Pong init_value = GG110_PONG__INIT;
    - *message = init_value;
    -}
    -size_t gg110_pong__get_packed_size
    - (const GG110Pong *message)
    -{
    - assert(message->base.descriptor == &gg110_pong__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg110_pong__pack
    - (const GG110Pong *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg110_pong__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg110_pong__pack_to_buffer
    - (const GG110Pong *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg110_pong__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG110Pong *
    - gg110_pong__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG110Pong *)
    - protobuf_c_message_unpack (&gg110_pong__descriptor,
    - allocator, len, data);
    -}
    -void gg110_pong__free_unpacked
    - (GG110Pong *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg110_pong__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void gg110_ack__init
    - (GG110Ack *message)
    -{
    - static GG110Ack init_value = GG110_ACK__INIT;
    - *message = init_value;
    -}
    -size_t gg110_ack__get_packed_size
    - (const GG110Ack *message)
    -{
    - assert(message->base.descriptor == &gg110_ack__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg110_ack__pack
    - (const GG110Ack *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg110_ack__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg110_ack__pack_to_buffer
    - (const GG110Ack *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg110_ack__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG110Ack *
    - gg110_ack__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG110Ack *)
    - protobuf_c_message_unpack (&gg110_ack__descriptor,
    - allocator, len, data);
    -}
    -void gg110_ack__free_unpacked
    - (GG110Ack *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg110_ack__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void gg105_login__init
    - (GG105Login *message)
    -{
    - static GG105Login init_value = GG105_LOGIN__INIT;
    - *message = init_value;
    -}
    -size_t gg105_login__get_packed_size
    - (const GG105Login *message)
    -{
    - assert(message->base.descriptor == &gg105_login__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg105_login__pack
    - (const GG105Login *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg105_login__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg105_login__pack_to_buffer
    - (const GG105Login *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg105_login__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG105Login *
    - gg105_login__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG105Login *)
    - protobuf_c_message_unpack (&gg105_login__descriptor,
    - allocator, len, data);
    -}
    -void gg105_login__free_unpacked
    - (GG105Login *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg105_login__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void gg110_message_ack_link__init
    - (GG110MessageAckLink *message)
    -{
    - static GG110MessageAckLink init_value = GG110_MESSAGE_ACK_LINK__INIT;
    - *message = init_value;
    -}
    -size_t gg110_message_ack_link__get_packed_size
    - (const GG110MessageAckLink *message)
    -{
    - assert(message->base.descriptor == &gg110_message_ack_link__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg110_message_ack_link__pack
    - (const GG110MessageAckLink *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg110_message_ack_link__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg110_message_ack_link__pack_to_buffer
    - (const GG110MessageAckLink *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg110_message_ack_link__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG110MessageAckLink *
    - gg110_message_ack_link__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG110MessageAckLink *)
    - protobuf_c_message_unpack (&gg110_message_ack_link__descriptor,
    - allocator, len, data);
    -}
    -void gg110_message_ack_link__free_unpacked
    - (GG110MessageAckLink *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg110_message_ack_link__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void gg110_message_ack__init
    - (GG110MessageAck *message)
    -{
    - static GG110MessageAck init_value = GG110_MESSAGE_ACK__INIT;
    - *message = init_value;
    -}
    -size_t gg110_message_ack__get_packed_size
    - (const GG110MessageAck *message)
    -{
    - assert(message->base.descriptor == &gg110_message_ack__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg110_message_ack__pack
    - (const GG110MessageAck *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg110_message_ack__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg110_message_ack__pack_to_buffer
    - (const GG110MessageAck *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg110_message_ack__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG110MessageAck *
    - gg110_message_ack__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG110MessageAck *)
    - protobuf_c_message_unpack (&gg110_message_ack__descriptor,
    - allocator, len, data);
    -}
    -void gg110_message_ack__free_unpacked
    - (GG110MessageAck *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg110_message_ack__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void gg110_event__init
    - (GG110Event *message)
    -{
    - static GG110Event init_value = GG110_EVENT__INIT;
    - *message = init_value;
    -}
    -size_t gg110_event__get_packed_size
    - (const GG110Event *message)
    -{
    - assert(message->base.descriptor == &gg110_event__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg110_event__pack
    - (const GG110Event *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg110_event__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg110_event__pack_to_buffer
    - (const GG110Event *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg110_event__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG110Event *
    - gg110_event__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG110Event *)
    - protobuf_c_message_unpack (&gg110_event__descriptor,
    - allocator, len, data);
    -}
    -void gg110_event__free_unpacked
    - (GG110Event *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg110_event__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void gg110_recv_message__init
    - (GG110RecvMessage *message)
    -{
    - static GG110RecvMessage init_value = GG110_RECV_MESSAGE__INIT;
    - *message = init_value;
    -}
    -size_t gg110_recv_message__get_packed_size
    - (const GG110RecvMessage *message)
    -{
    - assert(message->base.descriptor == &gg110_recv_message__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg110_recv_message__pack
    - (const GG110RecvMessage *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg110_recv_message__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg110_recv_message__pack_to_buffer
    - (const GG110RecvMessage *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg110_recv_message__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG110RecvMessage *
    - gg110_recv_message__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG110RecvMessage *)
    - protobuf_c_message_unpack (&gg110_recv_message__descriptor,
    - allocator, len, data);
    -}
    -void gg110_recv_message__free_unpacked
    - (GG110RecvMessage *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg110_recv_message__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void gg110_send_message__init
    - (GG110SendMessage *message)
    -{
    - static GG110SendMessage init_value = GG110_SEND_MESSAGE__INIT;
    - *message = init_value;
    -}
    -size_t gg110_send_message__get_packed_size
    - (const GG110SendMessage *message)
    -{
    - assert(message->base.descriptor == &gg110_send_message__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg110_send_message__pack
    - (const GG110SendMessage *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg110_send_message__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg110_send_message__pack_to_buffer
    - (const GG110SendMessage *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg110_send_message__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG110SendMessage *
    - gg110_send_message__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG110SendMessage *)
    - protobuf_c_message_unpack (&gg110_send_message__descriptor,
    - allocator, len, data);
    -}
    -void gg110_send_message__free_unpacked
    - (GG110SendMessage *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg110_send_message__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void gg110_imtoken__init
    - (GG110Imtoken *message)
    -{
    - static GG110Imtoken init_value = GG110_IMTOKEN__INIT;
    - *message = init_value;
    -}
    -size_t gg110_imtoken__get_packed_size
    - (const GG110Imtoken *message)
    -{
    - assert(message->base.descriptor == &gg110_imtoken__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg110_imtoken__pack
    - (const GG110Imtoken *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg110_imtoken__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg110_imtoken__pack_to_buffer
    - (const GG110Imtoken *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg110_imtoken__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG110Imtoken *
    - gg110_imtoken__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG110Imtoken *)
    - protobuf_c_message_unpack (&gg110_imtoken__descriptor,
    - allocator, len, data);
    -}
    -void gg110_imtoken__free_unpacked
    - (GG110Imtoken *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg110_imtoken__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void gg110_chat_info_update__init
    - (GG110ChatInfoUpdate *message)
    -{
    - static GG110ChatInfoUpdate init_value = GG110_CHAT_INFO_UPDATE__INIT;
    - *message = init_value;
    -}
    -size_t gg110_chat_info_update__get_packed_size
    - (const GG110ChatInfoUpdate *message)
    -{
    - assert(message->base.descriptor == &gg110_chat_info_update__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg110_chat_info_update__pack
    - (const GG110ChatInfoUpdate *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg110_chat_info_update__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg110_chat_info_update__pack_to_buffer
    - (const GG110ChatInfoUpdate *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg110_chat_info_update__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG110ChatInfoUpdate *
    - gg110_chat_info_update__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG110ChatInfoUpdate *)
    - protobuf_c_message_unpack (&gg110_chat_info_update__descriptor,
    - allocator, len, data);
    -}
    -void gg110_chat_info_update__free_unpacked
    - (GG110ChatInfoUpdate *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg110_chat_info_update__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void protobuf_kvp__init
    - (ProtobufKVP *message)
    -{
    - static ProtobufKVP init_value = PROTOBUF_KVP__INIT;
    - *message = init_value;
    -}
    -size_t protobuf_kvp__get_packed_size
    - (const ProtobufKVP *message)
    -{
    - assert(message->base.descriptor == &protobuf_kvp__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t protobuf_kvp__pack
    - (const ProtobufKVP *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &protobuf_kvp__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t protobuf_kvp__pack_to_buffer
    - (const ProtobufKVP *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &protobuf_kvp__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -ProtobufKVP *
    - protobuf_kvp__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (ProtobufKVP *)
    - protobuf_c_message_unpack (&protobuf_kvp__descriptor,
    - allocator, len, data);
    -}
    -void protobuf_kvp__free_unpacked
    - (ProtobufKVP *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &protobuf_kvp__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void gg110_options__init
    - (GG110Options *message)
    -{
    - static GG110Options init_value = GG110_OPTIONS__INIT;
    - *message = init_value;
    -}
    -size_t gg110_options__get_packed_size
    - (const GG110Options *message)
    -{
    - assert(message->base.descriptor == &gg110_options__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg110_options__pack
    - (const GG110Options *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg110_options__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg110_options__pack_to_buffer
    - (const GG110Options *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg110_options__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG110Options *
    - gg110_options__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG110Options *)
    - protobuf_c_message_unpack (&gg110_options__descriptor,
    - allocator, len, data);
    -}
    -void gg110_options__free_unpacked
    - (GG110Options *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg110_options__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void gg110_access_info__init
    - (GG110AccessInfo *message)
    -{
    - static GG110AccessInfo init_value = GG110_ACCESS_INFO__INIT;
    - *message = init_value;
    -}
    -size_t gg110_access_info__get_packed_size
    - (const GG110AccessInfo *message)
    -{
    - assert(message->base.descriptor == &gg110_access_info__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg110_access_info__pack
    - (const GG110AccessInfo *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg110_access_info__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg110_access_info__pack_to_buffer
    - (const GG110AccessInfo *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg110_access_info__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG110AccessInfo *
    - gg110_access_info__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG110AccessInfo *)
    - protobuf_c_message_unpack (&gg110_access_info__descriptor,
    - allocator, len, data);
    -}
    -void gg110_access_info__free_unpacked
    - (GG110AccessInfo *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg110_access_info__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void gg112_transfer_info_uin__init
    - (GG112TransferInfoUin *message)
    -{
    - static GG112TransferInfoUin init_value = GG112_TRANSFER_INFO_UIN__INIT;
    - *message = init_value;
    -}
    -size_t gg112_transfer_info_uin__get_packed_size
    - (const GG112TransferInfoUin *message)
    -{
    - assert(message->base.descriptor == &gg112_transfer_info_uin__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg112_transfer_info_uin__pack
    - (const GG112TransferInfoUin *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg112_transfer_info_uin__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg112_transfer_info_uin__pack_to_buffer
    - (const GG112TransferInfoUin *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg112_transfer_info_uin__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG112TransferInfoUin *
    - gg112_transfer_info_uin__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG112TransferInfoUin *)
    - protobuf_c_message_unpack (&gg112_transfer_info_uin__descriptor,
    - allocator, len, data);
    -}
    -void gg112_transfer_info_uin__free_unpacked
    - (GG112TransferInfoUin *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg112_transfer_info_uin__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void gg112_transfer_info_file__init
    - (GG112TransferInfoFile *message)
    -{
    - static GG112TransferInfoFile init_value = GG112_TRANSFER_INFO_FILE__INIT;
    - *message = init_value;
    -}
    -size_t gg112_transfer_info_file__get_packed_size
    - (const GG112TransferInfoFile *message)
    -{
    - assert(message->base.descriptor == &gg112_transfer_info_file__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg112_transfer_info_file__pack
    - (const GG112TransferInfoFile *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg112_transfer_info_file__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg112_transfer_info_file__pack_to_buffer
    - (const GG112TransferInfoFile *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg112_transfer_info_file__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG112TransferInfoFile *
    - gg112_transfer_info_file__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG112TransferInfoFile *)
    - protobuf_c_message_unpack (&gg112_transfer_info_file__descriptor,
    - allocator, len, data);
    -}
    -void gg112_transfer_info_file__free_unpacked
    - (GG112TransferInfoFile *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg112_transfer_info_file__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void gg112_transfer_info__init
    - (GG112TransferInfo *message)
    -{
    - static GG112TransferInfo init_value = GG112_TRANSFER_INFO__INIT;
    - *message = init_value;
    -}
    -size_t gg112_transfer_info__get_packed_size
    - (const GG112TransferInfo *message)
    -{
    - assert(message->base.descriptor == &gg112_transfer_info__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg112_transfer_info__pack
    - (const GG112TransferInfo *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg112_transfer_info__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg112_transfer_info__pack_to_buffer
    - (const GG112TransferInfo *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg112_transfer_info__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG112TransferInfo *
    - gg112_transfer_info__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG112TransferInfo *)
    - protobuf_c_message_unpack (&gg112_transfer_info__descriptor,
    - allocator, len, data);
    -}
    -void gg112_transfer_info__free_unpacked
    - (GG112TransferInfo *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg112_transfer_info__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -void gg110_magic_notification__init
    - (GG110MagicNotification *message)
    -{
    - static GG110MagicNotification init_value = GG110_MAGIC_NOTIFICATION__INIT;
    - *message = init_value;
    -}
    -size_t gg110_magic_notification__get_packed_size
    - (const GG110MagicNotification *message)
    -{
    - assert(message->base.descriptor == &gg110_magic_notification__descriptor);
    - return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));
    -}
    -size_t gg110_magic_notification__pack
    - (const GG110MagicNotification *message,
    - uint8_t *out)
    -{
    - assert(message->base.descriptor == &gg110_magic_notification__descriptor);
    - return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);
    -}
    -size_t gg110_magic_notification__pack_to_buffer
    - (const GG110MagicNotification *message,
    - ProtobufCBuffer *buffer)
    -{
    - assert(message->base.descriptor == &gg110_magic_notification__descriptor);
    - return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);
    -}
    -GG110MagicNotification *
    - gg110_magic_notification__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data)
    -{
    - return (GG110MagicNotification *)
    - protobuf_c_message_unpack (&gg110_magic_notification__descriptor,
    - allocator, len, data);
    -}
    -void gg110_magic_notification__free_unpacked
    - (GG110MagicNotification *message,
    - ProtobufCAllocator *allocator)
    -{
    - assert(message->base.descriptor == &gg110_magic_notification__descriptor);
    - protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);
    -}
    -static const int32_t gg110_login_ok__dummy1__default_value = 1;
    -static const ProtobufCFieldDescriptor gg110_login_ok__field_descriptors[4] =
    -{
    - {
    - "dummy1",
    - 1,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_INT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110LoginOK, dummy1),
    - NULL,
    - &gg110_login_ok__dummy1__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummyhash",
    - 2,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG110LoginOK, dummyhash),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "uin",
    - 3,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110LoginOK, uin),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "server_time",
    - 4,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED32,
    - 0, /* quantifier_offset */
    - offsetof(GG110LoginOK, server_time),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg110_login_ok__field_indices_by_name[] = {
    - 0, /* field[0] = dummy1 */
    - 1, /* field[1] = dummyhash */
    - 3, /* field[3] = server_time */
    - 2, /* field[2] = uin */
    -};
    -static const ProtobufCIntRange gg110_login_ok__number_ranges[1 + 1] =
    -{
    - { 1, 0 },
    - { 0, 4 }
    -};
    -const ProtobufCMessageDescriptor gg110_login_ok__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG110LoginOK",
    - "GG110LoginOK",
    - "GG110LoginOK",
    - "",
    - sizeof(GG110LoginOK),
    - 4,
    - gg110_login_ok__field_descriptors,
    - gg110_login_ok__field_indices_by_name,
    - 1, gg110_login_ok__number_ranges,
    - (ProtobufCMessageInit) gg110_login_ok__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -static const ProtobufCFieldDescriptor gg110_pong__field_descriptors[1] =
    -{
    - {
    - "server_time",
    - 1,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED32,
    - 0, /* quantifier_offset */
    - offsetof(GG110Pong, server_time),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg110_pong__field_indices_by_name[] = {
    - 0, /* field[0] = server_time */
    -};
    -static const ProtobufCIntRange gg110_pong__number_ranges[1 + 1] =
    -{
    - { 1, 0 },
    - { 0, 1 }
    -};
    -const ProtobufCMessageDescriptor gg110_pong__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG110Pong",
    - "GG110Pong",
    - "GG110Pong",
    - "",
    - sizeof(GG110Pong),
    - 1,
    - gg110_pong__field_descriptors,
    - gg110_pong__field_indices_by_name,
    - 1, gg110_pong__number_ranges,
    - (ProtobufCMessageInit) gg110_pong__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -const ProtobufCEnumValue gg110_ack__type__enum_values_by_number[6] =
    -{
    - { "MSG", "GG110_ACK__TYPE__MSG", 1 },
    - { "CHAT", "GG110_ACK__TYPE__CHAT", 2 },
    - { "CHAT_INFO", "GG110_ACK__TYPE__CHAT_INFO", 3 },
    - { "MAGIC_NOTIFICATION", "GG110_ACK__TYPE__MAGIC_NOTIFICATION", 5 },
    - { "MPA", "GG110_ACK__TYPE__MPA", 6 },
    - { "TRANSFER_INFO", "GG110_ACK__TYPE__TRANSFER_INFO", 7 },
    -};
    -static const ProtobufCIntRange gg110_ack__type__value_ranges[] = {
    -{1, 0},{5, 3},{0, 6}
    -};
    -const ProtobufCEnumValueIndex gg110_ack__type__enum_values_by_name[6] =
    -{
    - { "CHAT", 1 },
    - { "CHAT_INFO", 2 },
    - { "MAGIC_NOTIFICATION", 3 },
    - { "MPA", 4 },
    - { "MSG", 0 },
    - { "TRANSFER_INFO", 5 },
    -};
    -const ProtobufCEnumDescriptor gg110_ack__type__descriptor =
    -{
    - PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
    - "GG110Ack.Type",
    - "Type",
    - "GG110Ack__Type",
    - "",
    - 6,
    - gg110_ack__type__enum_values_by_number,
    - 6,
    - gg110_ack__type__enum_values_by_name,
    - 2,
    - gg110_ack__type__value_ranges,
    - NULL,NULL,NULL,NULL /* reserved[1234] */
    -};
    -static const uint32_t gg110_ack__dummy1__default_value = 1u;
    -static const ProtobufCFieldDescriptor gg110_ack__field_descriptors[3] =
    -{
    - {
    - "type",
    - 1,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_ENUM,
    - 0, /* quantifier_offset */
    - offsetof(GG110Ack, type),
    - &gg110_ack__type__descriptor,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "seq",
    - 2,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110Ack, seq),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy1",
    - 3,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110Ack, dummy1),
    - NULL,
    - &gg110_ack__dummy1__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg110_ack__field_indices_by_name[] = {
    - 2, /* field[2] = dummy1 */
    - 1, /* field[1] = seq */
    - 0, /* field[0] = type */
    -};
    -static const ProtobufCIntRange gg110_ack__number_ranges[1 + 1] =
    -{
    - { 1, 0 },
    - { 0, 3 }
    -};
    -const ProtobufCMessageDescriptor gg110_ack__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG110Ack",
    - "GG110Ack",
    - "GG110Ack",
    - "",
    - sizeof(GG110Ack),
    - 3,
    - gg110_ack__field_descriptors,
    - gg110_ack__field_indices_by_name,
    - 1, gg110_ack__number_ranges,
    - (ProtobufCMessageInit) gg110_ack__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -char gg105_login__initial_descr__default_value[] = "";
    -static const uint32_t gg105_login__initial_status__default_value = 8227u;
    -static const int32_t gg105_login__dummy1__default_value = 4;
    -static const uint32_t gg105_login__dummy2__default_value = 65994615u;
    -static const uint32_t gg105_login__dummy3__default_value = 198164u;
    -static const int32_t gg105_login__dummy5__default_value = 255;
    -static const int32_t gg105_login__dummy6__default_value = 100;
    -static const uint32_t gg105_login__dummy7__default_value = 127u;
    -static const int32_t gg105_login__dummy8__default_value = 0;
    -static const uint32_t gg105_login__dummy10__default_value = 0u;
    -static const ProtobufCFieldDescriptor gg105_login__field_descriptors[16] =
    -{
    - {
    - "lang",
    - 1,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG105Login, lang),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "uin",
    - 2,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_BYTES,
    - 0, /* quantifier_offset */
    - offsetof(GG105Login, uin),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "hash",
    - 3,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_BYTES,
    - 0, /* quantifier_offset */
    - offsetof(GG105Login, hash),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy1",
    - 4,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_INT32,
    - 0, /* quantifier_offset */
    - offsetof(GG105Login, dummy1),
    - NULL,
    - &gg105_login__dummy1__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy2",
    - 5,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED32,
    - 0, /* quantifier_offset */
    - offsetof(GG105Login, dummy2),
    - NULL,
    - &gg105_login__dummy2__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy3",
    - 6,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED32,
    - 0, /* quantifier_offset */
    - offsetof(GG105Login, dummy3),
    - NULL,
    - &gg105_login__dummy3__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "client",
    - 7,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG105Login, client),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "initial_status",
    - 8,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED32,
    - 0, /* quantifier_offset */
    - offsetof(GG105Login, initial_status),
    - NULL,
    - &gg105_login__initial_status__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "initial_descr",
    - 9,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG105Login, initial_descr),
    - NULL,
    - &gg105_login__initial_descr__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy4",
    - 10,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_BYTES,
    - 0, /* quantifier_offset */
    - offsetof(GG105Login, dummy4),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "supported_features",
    - 11,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG105Login, supported_features),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy5",
    - 12,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_INT32,
    - 0, /* quantifier_offset */
    - offsetof(GG105Login, dummy5),
    - NULL,
    - &gg105_login__dummy5__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy6",
    - 13,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_INT32,
    - 0, /* quantifier_offset */
    - offsetof(GG105Login, dummy6),
    - NULL,
    - &gg105_login__dummy6__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy7",
    - 14,
    - PROTOBUF_C_LABEL_OPTIONAL,
    - PROTOBUF_C_TYPE_FIXED32,
    - offsetof(GG105Login, has_dummy7),
    - offsetof(GG105Login, dummy7),
    - NULL,
    - &gg105_login__dummy7__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy8",
    - 15,
    - PROTOBUF_C_LABEL_OPTIONAL,
    - PROTOBUF_C_TYPE_INT32,
    - offsetof(GG105Login, has_dummy8),
    - offsetof(GG105Login, dummy8),
    - NULL,
    - &gg105_login__dummy8__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy10",
    - 17,
    - PROTOBUF_C_LABEL_OPTIONAL,
    - PROTOBUF_C_TYPE_UINT32,
    - offsetof(GG105Login, has_dummy10),
    - offsetof(GG105Login, dummy10),
    - NULL,
    - &gg105_login__dummy10__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg105_login__field_indices_by_name[] = {
    - 6, /* field[6] = client */
    - 3, /* field[3] = dummy1 */
    - 15, /* field[15] = dummy10 */
    - 4, /* field[4] = dummy2 */
    - 5, /* field[5] = dummy3 */
    - 9, /* field[9] = dummy4 */
    - 11, /* field[11] = dummy5 */
    - 12, /* field[12] = dummy6 */
    - 13, /* field[13] = dummy7 */
    - 14, /* field[14] = dummy8 */
    - 2, /* field[2] = hash */
    - 8, /* field[8] = initial_descr */
    - 7, /* field[7] = initial_status */
    - 0, /* field[0] = lang */
    - 10, /* field[10] = supported_features */
    - 1, /* field[1] = uin */
    -};
    -static const ProtobufCIntRange gg105_login__number_ranges[2 + 1] =
    -{
    - { 1, 0 },
    - { 17, 15 },
    - { 0, 16 }
    -};
    -const ProtobufCMessageDescriptor gg105_login__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG105Login",
    - "GG105Login",
    - "GG105Login",
    - "",
    - sizeof(GG105Login),
    - 16,
    - gg105_login__field_descriptors,
    - gg105_login__field_indices_by_name,
    - 2, gg105_login__number_ranges,
    - (ProtobufCMessageInit) gg105_login__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -static const ProtobufCFieldDescriptor gg110_message_ack_link__field_descriptors[2] =
    -{
    - {
    - "id",
    - 1,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED64,
    - 0, /* quantifier_offset */
    - offsetof(GG110MessageAckLink, id),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "url",
    - 2,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG110MessageAckLink, url),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg110_message_ack_link__field_indices_by_name[] = {
    - 0, /* field[0] = id */
    - 1, /* field[1] = url */
    -};
    -static const ProtobufCIntRange gg110_message_ack_link__number_ranges[1 + 1] =
    -{
    - { 1, 0 },
    - { 0, 2 }
    -};
    -const ProtobufCMessageDescriptor gg110_message_ack_link__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG110MessageAckLink",
    - "GG110MessageAckLink",
    - "GG110MessageAckLink",
    - "",
    - sizeof(GG110MessageAckLink),
    - 2,
    - gg110_message_ack_link__field_descriptors,
    - gg110_message_ack_link__field_indices_by_name,
    - 1, gg110_message_ack_link__number_ranges,
    - (ProtobufCMessageInit) gg110_message_ack_link__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -static const uint32_t gg110_message_ack__dummy1__default_value = 0u;
    -static const ProtobufCFieldDescriptor gg110_message_ack__field_descriptors[7] =
    -{
    - {
    - "msg_type",
    - 1,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110MessageAck, msg_type),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "seq",
    - 2,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110MessageAck, seq),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "time",
    - 3,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED32,
    - 0, /* quantifier_offset */
    - offsetof(GG110MessageAck, time),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "msg_id",
    - 4,
    - PROTOBUF_C_LABEL_OPTIONAL,
    - PROTOBUF_C_TYPE_FIXED64,
    - offsetof(GG110MessageAck, has_msg_id),
    - offsetof(GG110MessageAck, msg_id),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "conv_id",
    - 5,
    - PROTOBUF_C_LABEL_OPTIONAL,
    - PROTOBUF_C_TYPE_FIXED64,
    - offsetof(GG110MessageAck, has_conv_id),
    - offsetof(GG110MessageAck, conv_id),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "links",
    - 6,
    - PROTOBUF_C_LABEL_REPEATED,
    - PROTOBUF_C_TYPE_MESSAGE,
    - offsetof(GG110MessageAck, n_links),
    - offsetof(GG110MessageAck, links),
    - &gg110_message_ack_link__descriptor,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy1",
    - 7,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110MessageAck, dummy1),
    - NULL,
    - &gg110_message_ack__dummy1__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg110_message_ack__field_indices_by_name[] = {
    - 4, /* field[4] = conv_id */
    - 6, /* field[6] = dummy1 */
    - 5, /* field[5] = links */
    - 3, /* field[3] = msg_id */
    - 0, /* field[0] = msg_type */
    - 1, /* field[1] = seq */
    - 2, /* field[2] = time */
    -};
    -static const ProtobufCIntRange gg110_message_ack__number_ranges[1 + 1] =
    -{
    - { 1, 0 },
    - { 0, 7 }
    -};
    -const ProtobufCMessageDescriptor gg110_message_ack__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG110MessageAck",
    - "GG110MessageAck",
    - "GG110MessageAck",
    - "",
    - sizeof(GG110MessageAck),
    - 7,
    - gg110_message_ack__field_descriptors,
    - gg110_message_ack__field_indices_by_name,
    - 1, gg110_message_ack__number_ranges,
    - (ProtobufCMessageInit) gg110_message_ack__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -const ProtobufCEnumValue gg110_event__type__enum_values_by_number[2] =
    -{
    - { "XML", "GG110_EVENT__TYPE__XML", 0 },
    - { "JSON", "GG110_EVENT__TYPE__JSON", 2 },
    -};
    -static const ProtobufCIntRange gg110_event__type__value_ranges[] = {
    -{0, 0},{2, 1},{0, 2}
    -};
    -const ProtobufCEnumValueIndex gg110_event__type__enum_values_by_name[2] =
    -{
    - { "JSON", 1 },
    - { "XML", 0 },
    -};
    -const ProtobufCEnumDescriptor gg110_event__type__descriptor =
    -{
    - PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,
    - "GG110Event.Type",
    - "Type",
    - "GG110Event__Type",
    - "",
    - 2,
    - gg110_event__type__enum_values_by_number,
    - 2,
    - gg110_event__type__enum_values_by_name,
    - 2,
    - gg110_event__type__value_ranges,
    - NULL,NULL,NULL,NULL /* reserved[1234] */
    -};
    -static const ProtobufCFieldDescriptor gg110_event__field_descriptors[5] =
    -{
    - {
    - "type",
    - 1,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_ENUM,
    - 0, /* quantifier_offset */
    - offsetof(GG110Event, type),
    - &gg110_event__type__descriptor,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "seq",
    - 2,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110Event, seq),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "data",
    - 3,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG110Event, data),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "subtype",
    - 4,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG110Event, subtype),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "id",
    - 5,
    - PROTOBUF_C_LABEL_OPTIONAL,
    - PROTOBUF_C_TYPE_UINT64,
    - offsetof(GG110Event, has_id),
    - offsetof(GG110Event, id),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg110_event__field_indices_by_name[] = {
    - 2, /* field[2] = data */
    - 4, /* field[4] = id */
    - 1, /* field[1] = seq */
    - 3, /* field[3] = subtype */
    - 0, /* field[0] = type */
    -};
    -static const ProtobufCIntRange gg110_event__number_ranges[1 + 1] =
    -{
    - { 1, 0 },
    - { 0, 5 }
    -};
    -const ProtobufCMessageDescriptor gg110_event__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG110Event",
    - "GG110Event",
    - "GG110Event",
    - "",
    - sizeof(GG110Event),
    - 5,
    - gg110_event__field_descriptors,
    - gg110_event__field_indices_by_name,
    - 1, gg110_event__number_ranges,
    - (ProtobufCMessageInit) gg110_event__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -char gg110_recv_message__msg_plain__default_value[] = "";
    -static const ProtobufCFieldDescriptor gg110_recv_message__field_descriptors[10] =
    -{
    - {
    - "sender",
    - 1,
    - PROTOBUF_C_LABEL_OPTIONAL,
    - PROTOBUF_C_TYPE_BYTES,
    - offsetof(GG110RecvMessage, has_sender),
    - offsetof(GG110RecvMessage, sender),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "flags",
    - 2,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110RecvMessage, flags),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "seq",
    - 3,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110RecvMessage, seq),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "time",
    - 4,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED32,
    - 0, /* quantifier_offset */
    - offsetof(GG110RecvMessage, time),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "msg_plain",
    - 5,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG110RecvMessage, msg_plain),
    - NULL,
    - &gg110_recv_message__msg_plain__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "msg_xhtml",
    - 6,
    - PROTOBUF_C_LABEL_OPTIONAL,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG110RecvMessage, msg_xhtml),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "data",
    - 7,
    - PROTOBUF_C_LABEL_OPTIONAL,
    - PROTOBUF_C_TYPE_BYTES,
    - offsetof(GG110RecvMessage, has_data),
    - offsetof(GG110RecvMessage, data),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "msg_id",
    - 9,
    - PROTOBUF_C_LABEL_OPTIONAL,
    - PROTOBUF_C_TYPE_FIXED64,
    - offsetof(GG110RecvMessage, has_msg_id),
    - offsetof(GG110RecvMessage, msg_id),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "chat_id",
    - 10,
    - PROTOBUF_C_LABEL_OPTIONAL,
    - PROTOBUF_C_TYPE_FIXED64,
    - offsetof(GG110RecvMessage, has_chat_id),
    - offsetof(GG110RecvMessage, chat_id),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "conv_id",
    - 11,
    - PROTOBUF_C_LABEL_OPTIONAL,
    - PROTOBUF_C_TYPE_FIXED64,
    - offsetof(GG110RecvMessage, has_conv_id),
    - offsetof(GG110RecvMessage, conv_id),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg110_recv_message__field_indices_by_name[] = {
    - 8, /* field[8] = chat_id */
    - 9, /* field[9] = conv_id */
    - 6, /* field[6] = data */
    - 1, /* field[1] = flags */
    - 7, /* field[7] = msg_id */
    - 4, /* field[4] = msg_plain */
    - 5, /* field[5] = msg_xhtml */
    - 0, /* field[0] = sender */
    - 2, /* field[2] = seq */
    - 3, /* field[3] = time */
    -};
    -static const ProtobufCIntRange gg110_recv_message__number_ranges[2 + 1] =
    -{
    - { 1, 0 },
    - { 9, 7 },
    - { 0, 10 }
    -};
    -const ProtobufCMessageDescriptor gg110_recv_message__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG110RecvMessage",
    - "GG110RecvMessage",
    - "GG110RecvMessage",
    - "",
    - sizeof(GG110RecvMessage),
    - 10,
    - gg110_recv_message__field_descriptors,
    - gg110_recv_message__field_indices_by_name,
    - 2, gg110_recv_message__number_ranges,
    - (ProtobufCMessageInit) gg110_recv_message__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -char gg110_send_message__dummy3__default_value[] = "";
    -static const uint32_t gg110_send_message__dummy1__default_value = 8u;
    -static const ProtobufCFieldDescriptor gg110_send_message__field_descriptors[7] =
    -{
    - {
    - "recipient",
    - 1,
    - PROTOBUF_C_LABEL_OPTIONAL,
    - PROTOBUF_C_TYPE_BYTES,
    - offsetof(GG110SendMessage, has_recipient),
    - offsetof(GG110SendMessage, recipient),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy1",
    - 2,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110SendMessage, dummy1),
    - NULL,
    - &gg110_send_message__dummy1__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "seq",
    - 3,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110SendMessage, seq),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "msg_plain",
    - 5,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG110SendMessage, msg_plain),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "msg_xhtml",
    - 6,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG110SendMessage, msg_xhtml),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy3",
    - 7,
    - PROTOBUF_C_LABEL_OPTIONAL,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG110SendMessage, dummy3),
    - NULL,
    - &gg110_send_message__dummy3__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "chat_id",
    - 10,
    - PROTOBUF_C_LABEL_OPTIONAL,
    - PROTOBUF_C_TYPE_FIXED64,
    - offsetof(GG110SendMessage, has_chat_id),
    - offsetof(GG110SendMessage, chat_id),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg110_send_message__field_indices_by_name[] = {
    - 6, /* field[6] = chat_id */
    - 1, /* field[1] = dummy1 */
    - 5, /* field[5] = dummy3 */
    - 3, /* field[3] = msg_plain */
    - 4, /* field[4] = msg_xhtml */
    - 0, /* field[0] = recipient */
    - 2, /* field[2] = seq */
    -};
    -static const ProtobufCIntRange gg110_send_message__number_ranges[3 + 1] =
    -{
    - { 1, 0 },
    - { 5, 3 },
    - { 10, 6 },
    - { 0, 7 }
    -};
    -const ProtobufCMessageDescriptor gg110_send_message__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG110SendMessage",
    - "GG110SendMessage",
    - "GG110SendMessage",
    - "",
    - sizeof(GG110SendMessage),
    - 7,
    - gg110_send_message__field_descriptors,
    - gg110_send_message__field_indices_by_name,
    - 3, gg110_send_message__number_ranges,
    - (ProtobufCMessageInit) gg110_send_message__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -static const ProtobufCFieldDescriptor gg110_imtoken__field_descriptors[1] =
    -{
    - {
    - "imtoken",
    - 1,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG110Imtoken, imtoken),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg110_imtoken__field_indices_by_name[] = {
    - 0, /* field[0] = imtoken */
    -};
    -static const ProtobufCIntRange gg110_imtoken__number_ranges[1 + 1] =
    -{
    - { 1, 0 },
    - { 0, 1 }
    -};
    -const ProtobufCMessageDescriptor gg110_imtoken__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG110Imtoken",
    - "GG110Imtoken",
    - "GG110Imtoken",
    - "",
    - sizeof(GG110Imtoken),
    - 1,
    - gg110_imtoken__field_descriptors,
    - gg110_imtoken__field_indices_by_name,
    - 1, gg110_imtoken__number_ranges,
    - (ProtobufCMessageInit) gg110_imtoken__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -static const ProtobufCFieldDescriptor gg110_chat_info_update__field_descriptors[10] =
    -{
    - {
    - "participant",
    - 1,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_BYTES,
    - 0, /* quantifier_offset */
    - offsetof(GG110ChatInfoUpdate, participant),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "inviter",
    - 2,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_BYTES,
    - 0, /* quantifier_offset */
    - offsetof(GG110ChatInfoUpdate, inviter),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "update_type",
    - 3,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED32,
    - 0, /* quantifier_offset */
    - offsetof(GG110ChatInfoUpdate, update_type),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "time",
    - 4,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED32,
    - 0, /* quantifier_offset */
    - offsetof(GG110ChatInfoUpdate, time),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy1",
    - 5,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED32,
    - 0, /* quantifier_offset */
    - offsetof(GG110ChatInfoUpdate, dummy1),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "version",
    - 6,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110ChatInfoUpdate, version),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy2",
    - 7,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110ChatInfoUpdate, dummy2),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "msg_id",
    - 9,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED64,
    - 0, /* quantifier_offset */
    - offsetof(GG110ChatInfoUpdate, msg_id),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "chat_id",
    - 10,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED64,
    - 0, /* quantifier_offset */
    - offsetof(GG110ChatInfoUpdate, chat_id),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "conv_id",
    - 11,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED64,
    - 0, /* quantifier_offset */
    - offsetof(GG110ChatInfoUpdate, conv_id),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg110_chat_info_update__field_indices_by_name[] = {
    - 8, /* field[8] = chat_id */
    - 9, /* field[9] = conv_id */
    - 4, /* field[4] = dummy1 */
    - 6, /* field[6] = dummy2 */
    - 1, /* field[1] = inviter */
    - 7, /* field[7] = msg_id */
    - 0, /* field[0] = participant */
    - 3, /* field[3] = time */
    - 2, /* field[2] = update_type */
    - 5, /* field[5] = version */
    -};
    -static const ProtobufCIntRange gg110_chat_info_update__number_ranges[2 + 1] =
    -{
    - { 1, 0 },
    - { 9, 7 },
    - { 0, 10 }
    -};
    -const ProtobufCMessageDescriptor gg110_chat_info_update__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG110ChatInfoUpdate",
    - "GG110ChatInfoUpdate",
    - "GG110ChatInfoUpdate",
    - "",
    - sizeof(GG110ChatInfoUpdate),
    - 10,
    - gg110_chat_info_update__field_descriptors,
    - gg110_chat_info_update__field_indices_by_name,
    - 2, gg110_chat_info_update__number_ranges,
    - (ProtobufCMessageInit) gg110_chat_info_update__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -static const ProtobufCFieldDescriptor protobuf_kvp__field_descriptors[2] =
    -{
    - {
    - "key",
    - 1,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(ProtobufKVP, key),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "value",
    - 2,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(ProtobufKVP, value),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned protobuf_kvp__field_indices_by_name[] = {
    - 0, /* field[0] = key */
    - 1, /* field[1] = value */
    -};
    -static const ProtobufCIntRange protobuf_kvp__number_ranges[1 + 1] =
    -{
    - { 1, 0 },
    - { 0, 2 }
    -};
    -const ProtobufCMessageDescriptor protobuf_kvp__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "ProtobufKVP",
    - "ProtobufKVP",
    - "ProtobufKVP",
    - "",
    - sizeof(ProtobufKVP),
    - 2,
    - protobuf_kvp__field_descriptors,
    - protobuf_kvp__field_indices_by_name,
    - 1, protobuf_kvp__number_ranges,
    - (ProtobufCMessageInit) protobuf_kvp__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -static const uint32_t gg110_options__dummy1__default_value = 0u;
    -static const ProtobufCFieldDescriptor gg110_options__field_descriptors[2] =
    -{
    - {
    - "options",
    - 1,
    - PROTOBUF_C_LABEL_REPEATED,
    - PROTOBUF_C_TYPE_MESSAGE,
    - offsetof(GG110Options, n_options),
    - offsetof(GG110Options, options),
    - &protobuf_kvp__descriptor,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy1",
    - 2,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110Options, dummy1),
    - NULL,
    - &gg110_options__dummy1__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg110_options__field_indices_by_name[] = {
    - 1, /* field[1] = dummy1 */
    - 0, /* field[0] = options */
    -};
    -static const ProtobufCIntRange gg110_options__number_ranges[1 + 1] =
    -{
    - { 1, 0 },
    - { 0, 2 }
    -};
    -const ProtobufCMessageDescriptor gg110_options__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG110Options",
    - "GG110Options",
    - "GG110Options",
    - "",
    - sizeof(GG110Options),
    - 2,
    - gg110_options__field_descriptors,
    - gg110_options__field_indices_by_name,
    - 1, gg110_options__number_ranges,
    - (ProtobufCMessageInit) gg110_options__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -static const uint32_t gg110_access_info__dummy1__default_value = 1u;
    -static const ProtobufCFieldDescriptor gg110_access_info__field_descriptors[5] =
    -{
    - {
    - "dummy1",
    - 1,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110AccessInfo, dummy1),
    - NULL,
    - &gg110_access_info__dummy1__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy2",
    - 2,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110AccessInfo, dummy2),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "last_message",
    - 3,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110AccessInfo, last_message),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "last_file_transfer",
    - 4,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110AccessInfo, last_file_transfer),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "last_conference_ch",
    - 5,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110AccessInfo, last_conference_ch),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg110_access_info__field_indices_by_name[] = {
    - 0, /* field[0] = dummy1 */
    - 1, /* field[1] = dummy2 */
    - 4, /* field[4] = last_conference_ch */
    - 3, /* field[3] = last_file_transfer */
    - 2, /* field[2] = last_message */
    -};
    -static const ProtobufCIntRange gg110_access_info__number_ranges[1 + 1] =
    -{
    - { 1, 0 },
    - { 0, 5 }
    -};
    -const ProtobufCMessageDescriptor gg110_access_info__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG110AccessInfo",
    - "GG110AccessInfo",
    - "GG110AccessInfo",
    - "",
    - sizeof(GG110AccessInfo),
    - 5,
    - gg110_access_info__field_descriptors,
    - gg110_access_info__field_indices_by_name,
    - 1, gg110_access_info__number_ranges,
    - (ProtobufCMessageInit) gg110_access_info__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -static const uint32_t gg112_transfer_info_uin__dummy1__default_value = 1u;
    -static const ProtobufCFieldDescriptor gg112_transfer_info_uin__field_descriptors[2] =
    -{
    - {
    - "dummy1",
    - 1,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG112TransferInfoUin, dummy1),
    - NULL,
    - &gg112_transfer_info_uin__dummy1__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "uin",
    - 2,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_BYTES,
    - 0, /* quantifier_offset */
    - offsetof(GG112TransferInfoUin, uin),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg112_transfer_info_uin__field_indices_by_name[] = {
    - 0, /* field[0] = dummy1 */
    - 1, /* field[1] = uin */
    -};
    -static const ProtobufCIntRange gg112_transfer_info_uin__number_ranges[1 + 1] =
    -{
    - { 1, 0 },
    - { 0, 2 }
    -};
    -const ProtobufCMessageDescriptor gg112_transfer_info_uin__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG112TransferInfoUin",
    - "GG112TransferInfoUin",
    - "GG112TransferInfoUin",
    - "",
    - sizeof(GG112TransferInfoUin),
    - 2,
    - gg112_transfer_info_uin__field_descriptors,
    - gg112_transfer_info_uin__field_indices_by_name,
    - 1, gg112_transfer_info_uin__number_ranges,
    - (ProtobufCMessageInit) gg112_transfer_info_uin__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -char gg112_transfer_info_file__type__default_value[] = "other";
    -static const ProtobufCFieldDescriptor gg112_transfer_info_file__field_descriptors[6] =
    -{
    - {
    - "type",
    - 1,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG112TransferInfoFile, type),
    - NULL,
    - &gg112_transfer_info_file__type__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "url",
    - 2,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG112TransferInfoFile, url),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "content_type",
    - 6,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG112TransferInfoFile, content_type),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "filename",
    - 7,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG112TransferInfoFile, filename),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "filesize",
    - 8,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG112TransferInfoFile, filesize),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "msg_id",
    - 1001,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED64,
    - 0, /* quantifier_offset */
    - offsetof(GG112TransferInfoFile, msg_id),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg112_transfer_info_file__field_indices_by_name[] = {
    - 2, /* field[2] = content_type */
    - 3, /* field[3] = filename */
    - 4, /* field[4] = filesize */
    - 5, /* field[5] = msg_id */
    - 0, /* field[0] = type */
    - 1, /* field[1] = url */
    -};
    -static const ProtobufCIntRange gg112_transfer_info_file__number_ranges[3 + 1] =
    -{
    - { 1, 0 },
    - { 6, 2 },
    - { 1001, 5 },
    - { 0, 6 }
    -};
    -const ProtobufCMessageDescriptor gg112_transfer_info_file__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG112TransferInfoFile",
    - "GG112TransferInfoFile",
    - "GG112TransferInfoFile",
    - "",
    - sizeof(GG112TransferInfoFile),
    - 6,
    - gg112_transfer_info_file__field_descriptors,
    - gg112_transfer_info_file__field_indices_by_name,
    - 3, gg112_transfer_info_file__number_ranges,
    - (ProtobufCMessageInit) gg112_transfer_info_file__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -static const ProtobufCFieldDescriptor gg112_transfer_info__field_descriptors[9] =
    -{
    - {
    - "dummy1",
    - 1,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG112TransferInfo, dummy1),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "peer",
    - 2,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_MESSAGE,
    - 0, /* quantifier_offset */
    - offsetof(GG112TransferInfo, peer),
    - &gg112_transfer_info_uin__descriptor,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "time",
    - 3,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED32,
    - 0, /* quantifier_offset */
    - offsetof(GG112TransferInfo, time),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "sender",
    - 4,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_MESSAGE,
    - 0, /* quantifier_offset */
    - offsetof(GG112TransferInfo, sender),
    - &gg112_transfer_info_uin__descriptor,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "data",
    - 5,
    - PROTOBUF_C_LABEL_REPEATED,
    - PROTOBUF_C_TYPE_MESSAGE,
    - offsetof(GG112TransferInfo, n_data),
    - offsetof(GG112TransferInfo, data),
    - &protobuf_kvp__descriptor,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "file",
    - 6,
    - PROTOBUF_C_LABEL_OPTIONAL,
    - PROTOBUF_C_TYPE_MESSAGE,
    - 0, /* quantifier_offset */
    - offsetof(GG112TransferInfo, file),
    - &gg112_transfer_info_file__descriptor,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "seq",
    - 7,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_UINT32,
    - 0, /* quantifier_offset */
    - offsetof(GG112TransferInfo, seq),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "msg_id",
    - 1001,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED64,
    - 0, /* quantifier_offset */
    - offsetof(GG112TransferInfo, msg_id),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "conv_id",
    - 1002,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_FIXED64,
    - 0, /* quantifier_offset */
    - offsetof(GG112TransferInfo, conv_id),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg112_transfer_info__field_indices_by_name[] = {
    - 8, /* field[8] = conv_id */
    - 4, /* field[4] = data */
    - 0, /* field[0] = dummy1 */
    - 5, /* field[5] = file */
    - 7, /* field[7] = msg_id */
    - 1, /* field[1] = peer */
    - 3, /* field[3] = sender */
    - 6, /* field[6] = seq */
    - 2, /* field[2] = time */
    -};
    -static const ProtobufCIntRange gg112_transfer_info__number_ranges[2 + 1] =
    -{
    - { 1, 0 },
    - { 1001, 7 },
    - { 0, 9 }
    -};
    -const ProtobufCMessageDescriptor gg112_transfer_info__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG112TransferInfo",
    - "GG112TransferInfo",
    - "GG112TransferInfo",
    - "",
    - sizeof(GG112TransferInfo),
    - 9,
    - gg112_transfer_info__field_descriptors,
    - gg112_transfer_info__field_indices_by_name,
    - 2, gg112_transfer_info__number_ranges,
    - (ProtobufCMessageInit) gg112_transfer_info__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    -char gg110_magic_notification__dummy4__default_value[] = "";
    -static const int32_t gg110_magic_notification__dummy1__default_value = 2;
    -static const int32_t gg110_magic_notification__dummy2__default_value = 1;
    -static const int32_t gg110_magic_notification__dummy3__default_value = 1;
    -static const ProtobufCFieldDescriptor gg110_magic_notification__field_descriptors[6] =
    -{
    - {
    - "dummy1",
    - 1,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_INT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110MagicNotification, dummy1),
    - NULL,
    - &gg110_magic_notification__dummy1__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "seq",
    - 2,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_INT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110MagicNotification, seq),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy2",
    - 3,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_INT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110MagicNotification, dummy2),
    - NULL,
    - &gg110_magic_notification__dummy2__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy3",
    - 4,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_INT32,
    - 0, /* quantifier_offset */
    - offsetof(GG110MagicNotification, dummy3),
    - NULL,
    - &gg110_magic_notification__dummy3__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "uin",
    - 5,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_BYTES,
    - 0, /* quantifier_offset */
    - offsetof(GG110MagicNotification, uin),
    - NULL,
    - NULL,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    - {
    - "dummy4",
    - 6,
    - PROTOBUF_C_LABEL_REQUIRED,
    - PROTOBUF_C_TYPE_STRING,
    - 0, /* quantifier_offset */
    - offsetof(GG110MagicNotification, dummy4),
    - NULL,
    - &gg110_magic_notification__dummy4__default_value,
    - 0, /* flags */
    - 0,NULL,NULL /* reserved1,reserved2, etc */
    - },
    -};
    -static const unsigned gg110_magic_notification__field_indices_by_name[] = {
    - 0, /* field[0] = dummy1 */
    - 2, /* field[2] = dummy2 */
    - 3, /* field[3] = dummy3 */
    - 5, /* field[5] = dummy4 */
    - 1, /* field[1] = seq */
    - 4, /* field[4] = uin */
    -};
    -static const ProtobufCIntRange gg110_magic_notification__number_ranges[1 + 1] =
    -{
    - { 1, 0 },
    - { 0, 6 }
    -};
    -const ProtobufCMessageDescriptor gg110_magic_notification__descriptor =
    -{
    - PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,
    - "GG110MagicNotification",
    - "GG110MagicNotification",
    - "GG110MagicNotification",
    - "",
    - sizeof(GG110MagicNotification),
    - 6,
    - gg110_magic_notification__field_descriptors,
    - gg110_magic_notification__field_indices_by_name,
    - 1, gg110_magic_notification__number_ranges,
    - (ProtobufCMessageInit) gg110_magic_notification__init,
    - NULL,NULL,NULL /* reserved[123] */
    -};
    --- a/libpurple/protocols/gg/lib/packets.pb-c.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,766 +0,0 @@
    -/* Generated by the protocol buffer compiler. DO NOT EDIT! */
    -/* Generated from: packets.proto */
    -
    -#ifndef PROTOBUF_C_packets_2eproto__INCLUDED
    -#define PROTOBUF_C_packets_2eproto__INCLUDED
    -
    -#include "protobuf.h"
    -
    -PROTOBUF_C__BEGIN_DECLS
    -
    -#if PROTOBUF_C_VERSION_NUMBER < 1000000
    -# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.
    -#elif 1000002 < PROTOBUF_C_MIN_COMPILER_VERSION
    -# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.
    -#endif
    -
    -
    -typedef struct _GG110LoginOK GG110LoginOK;
    -typedef struct _GG110Pong GG110Pong;
    -typedef struct _GG110Ack GG110Ack;
    -typedef struct _GG105Login GG105Login;
    -typedef struct _GG110MessageAckLink GG110MessageAckLink;
    -typedef struct _GG110MessageAck GG110MessageAck;
    -typedef struct _GG110Event GG110Event;
    -typedef struct _GG110RecvMessage GG110RecvMessage;
    -typedef struct _GG110SendMessage GG110SendMessage;
    -typedef struct _GG110Imtoken GG110Imtoken;
    -typedef struct _GG110ChatInfoUpdate GG110ChatInfoUpdate;
    -typedef struct _ProtobufKVP ProtobufKVP;
    -typedef struct _GG110Options GG110Options;
    -typedef struct _GG110AccessInfo GG110AccessInfo;
    -typedef struct _GG112TransferInfoUin GG112TransferInfoUin;
    -typedef struct _GG112TransferInfoFile GG112TransferInfoFile;
    -typedef struct _GG112TransferInfo GG112TransferInfo;
    -typedef struct _GG110MagicNotification GG110MagicNotification;
    -
    -
    -/* --- enums --- */
    -
    -typedef enum _GG110Ack__Type {
    - GG110_ACK__TYPE__MSG = 1,
    - GG110_ACK__TYPE__CHAT = 2,
    - GG110_ACK__TYPE__CHAT_INFO = 3,
    - GG110_ACK__TYPE__MAGIC_NOTIFICATION = 5,
    - GG110_ACK__TYPE__MPA = 6,
    - GG110_ACK__TYPE__TRANSFER_INFO = 7
    - PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(GG110_ACK__TYPE)
    -} GG110Ack__Type;
    -typedef enum _GG110Event__Type {
    - GG110_EVENT__TYPE__XML = 0,
    - GG110_EVENT__TYPE__JSON = 2
    - PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(GG110_EVENT__TYPE)
    -} GG110Event__Type;
    -
    -/* --- messages --- */
    -
    -struct _GG110LoginOK
    -{
    - ProtobufCMessage base;
    - int32_t dummy1;
    - char *dummyhash;
    - uint32_t uin;
    - uint32_t server_time;
    -};
    -#define GG110_LOGIN_OK__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg110_login_ok__descriptor) \
    - , 1, NULL, 0, 0 }
    -
    -
    -struct _GG110Pong
    -{
    - ProtobufCMessage base;
    - uint32_t server_time;
    -};
    -#define GG110_PONG__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg110_pong__descriptor) \
    - , 0 }
    -
    -
    -struct _GG110Ack
    -{
    - ProtobufCMessage base;
    - GG110Ack__Type type;
    - uint32_t seq;
    - uint32_t dummy1;
    -};
    -#define GG110_ACK__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg110_ack__descriptor) \
    - , 0, 0, 1u }
    -
    -
    -struct _GG105Login
    -{
    - ProtobufCMessage base;
    - char *lang;
    - ProtobufCBinaryData uin;
    - ProtobufCBinaryData hash;
    - char *client;
    - uint32_t initial_status;
    - char *initial_descr;
    - char *supported_features;
    - int32_t dummy1;
    - uint32_t dummy2;
    - uint32_t dummy3;
    - ProtobufCBinaryData dummy4;
    - int32_t dummy5;
    - int32_t dummy6;
    - protobuf_c_boolean has_dummy7;
    - uint32_t dummy7;
    - protobuf_c_boolean has_dummy8;
    - int32_t dummy8;
    - protobuf_c_boolean has_dummy10;
    - uint32_t dummy10;
    -};
    -extern char gg105_login__initial_descr__default_value[];
    -#define GG105_LOGIN__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg105_login__descriptor) \
    - , NULL, {0,NULL}, {0,NULL}, NULL, 8227u, gg105_login__initial_descr__default_value, NULL, 4, 65994615u, 198164u, {0,NULL}, 255, 100, 0,127u, 0,0, 0,0u }
    -
    -
    -struct _GG110MessageAckLink
    -{
    - ProtobufCMessage base;
    - uint64_t id;
    - char *url;
    -};
    -#define GG110_MESSAGE_ACK_LINK__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg110_message_ack_link__descriptor) \
    - , 0, NULL }
    -
    -
    -struct _GG110MessageAck
    -{
    - ProtobufCMessage base;
    - uint32_t msg_type;
    - uint32_t seq;
    - uint32_t time;
    - protobuf_c_boolean has_msg_id;
    - uint64_t msg_id;
    - protobuf_c_boolean has_conv_id;
    - uint64_t conv_id;
    - size_t n_links;
    - GG110MessageAckLink **links;
    - uint32_t dummy1;
    -};
    -#define GG110_MESSAGE_ACK__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg110_message_ack__descriptor) \
    - , 0, 0, 0, 0,0, 0,0, 0,NULL, 0u }
    -
    -
    -struct _GG110Event
    -{
    - ProtobufCMessage base;
    - GG110Event__Type type;
    - uint32_t seq;
    - char *data;
    - char *subtype;
    - protobuf_c_boolean has_id;
    - uint64_t id;
    -};
    -#define GG110_EVENT__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg110_event__descriptor) \
    - , 0, 0, NULL, NULL, 0,0 }
    -
    -
    -struct _GG110RecvMessage
    -{
    - ProtobufCMessage base;
    - protobuf_c_boolean has_sender;
    - ProtobufCBinaryData sender;
    - uint32_t flags;
    - uint32_t seq;
    - uint32_t time;
    - char *msg_plain;
    - char *msg_xhtml;
    - protobuf_c_boolean has_data;
    - ProtobufCBinaryData data;
    - protobuf_c_boolean has_msg_id;
    - uint64_t msg_id;
    - protobuf_c_boolean has_chat_id;
    - uint64_t chat_id;
    - protobuf_c_boolean has_conv_id;
    - uint64_t conv_id;
    -};
    -extern char gg110_recv_message__msg_plain__default_value[];
    -#define GG110_RECV_MESSAGE__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg110_recv_message__descriptor) \
    - , 0,{0,NULL}, 0, 0, 0, gg110_recv_message__msg_plain__default_value, NULL, 0,{0,NULL}, 0,0, 0,0, 0,0 }
    -
    -
    -struct _GG110SendMessage
    -{
    - ProtobufCMessage base;
    - protobuf_c_boolean has_recipient;
    - ProtobufCBinaryData recipient;
    - uint32_t dummy1;
    - uint32_t seq;
    - char *msg_plain;
    - char *msg_xhtml;
    - char *dummy3;
    - protobuf_c_boolean has_chat_id;
    - uint64_t chat_id;
    -};
    -extern char gg110_send_message__dummy3__default_value[];
    -#define GG110_SEND_MESSAGE__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg110_send_message__descriptor) \
    - , 0,{0,NULL}, 8u, 0, NULL, NULL, gg110_send_message__dummy3__default_value, 0,0 }
    -
    -
    -struct _GG110Imtoken
    -{
    - ProtobufCMessage base;
    - char *imtoken;
    -};
    -#define GG110_IMTOKEN__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg110_imtoken__descriptor) \
    - , NULL }
    -
    -
    -struct _GG110ChatInfoUpdate
    -{
    - ProtobufCMessage base;
    - ProtobufCBinaryData participant;
    - ProtobufCBinaryData inviter;
    - uint32_t update_type;
    - uint32_t time;
    - uint32_t dummy1;
    - uint32_t version;
    - uint32_t dummy2;
    - uint64_t msg_id;
    - uint64_t chat_id;
    - uint64_t conv_id;
    -};
    -#define GG110_CHAT_INFO_UPDATE__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg110_chat_info_update__descriptor) \
    - , {0,NULL}, {0,NULL}, 0, 0, 0, 0, 0, 0, 0, 0 }
    -
    -
    -struct _ProtobufKVP
    -{
    - ProtobufCMessage base;
    - char *key;
    - char *value;
    -};
    -#define PROTOBUF_KVP__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&protobuf_kvp__descriptor) \
    - , NULL, NULL }
    -
    -
    -struct _GG110Options
    -{
    - ProtobufCMessage base;
    - size_t n_options;
    - ProtobufKVP **options;
    - uint32_t dummy1;
    -};
    -#define GG110_OPTIONS__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg110_options__descriptor) \
    - , 0,NULL, 0u }
    -
    -
    -struct _GG110AccessInfo
    -{
    - ProtobufCMessage base;
    - uint32_t dummy1;
    - uint32_t dummy2;
    - uint32_t last_message;
    - uint32_t last_file_transfer;
    - uint32_t last_conference_ch;
    -};
    -#define GG110_ACCESS_INFO__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg110_access_info__descriptor) \
    - , 1u, 0, 0, 0, 0 }
    -
    -
    -struct _GG112TransferInfoUin
    -{
    - ProtobufCMessage base;
    - uint32_t dummy1;
    - ProtobufCBinaryData uin;
    -};
    -#define GG112_TRANSFER_INFO_UIN__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg112_transfer_info_uin__descriptor) \
    - , 1u, {0,NULL} }
    -
    -
    -struct _GG112TransferInfoFile
    -{
    - ProtobufCMessage base;
    - char *type;
    - char *url;
    - char *content_type;
    - char *filename;
    - uint32_t filesize;
    - uint64_t msg_id;
    -};
    -extern char gg112_transfer_info_file__type__default_value[];
    -#define GG112_TRANSFER_INFO_FILE__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg112_transfer_info_file__descriptor) \
    - , gg112_transfer_info_file__type__default_value, NULL, NULL, NULL, 0, 0 }
    -
    -
    -struct _GG112TransferInfo
    -{
    - ProtobufCMessage base;
    - uint32_t dummy1;
    - GG112TransferInfoUin *peer;
    - GG112TransferInfoUin *sender;
    - uint32_t time;
    - size_t n_data;
    - ProtobufKVP **data;
    - GG112TransferInfoFile *file;
    - uint32_t seq;
    - uint64_t msg_id;
    - uint64_t conv_id;
    -};
    -#define GG112_TRANSFER_INFO__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg112_transfer_info__descriptor) \
    - , 0, NULL, NULL, 0, 0,NULL, NULL, 0, 0, 0 }
    -
    -
    -struct _GG110MagicNotification
    -{
    - ProtobufCMessage base;
    - int32_t dummy1;
    - int32_t seq;
    - int32_t dummy2;
    - int32_t dummy3;
    - ProtobufCBinaryData uin;
    - char *dummy4;
    -};
    -extern char gg110_magic_notification__dummy4__default_value[];
    -#define GG110_MAGIC_NOTIFICATION__INIT \
    - { PROTOBUF_C_MESSAGE_INIT (&gg110_magic_notification__descriptor) \
    - , 2, 0, 1, 1, {0,NULL}, gg110_magic_notification__dummy4__default_value }
    -
    -
    -/* GG110LoginOK methods */
    -void gg110_login_ok__init
    - (GG110LoginOK *message);
    -size_t gg110_login_ok__get_packed_size
    - (const GG110LoginOK *message);
    -size_t gg110_login_ok__pack
    - (const GG110LoginOK *message,
    - uint8_t *out);
    -size_t gg110_login_ok__pack_to_buffer
    - (const GG110LoginOK *message,
    - ProtobufCBuffer *buffer);
    -GG110LoginOK *
    - gg110_login_ok__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg110_login_ok__free_unpacked
    - (GG110LoginOK *message,
    - ProtobufCAllocator *allocator);
    -/* GG110Pong methods */
    -void gg110_pong__init
    - (GG110Pong *message);
    -size_t gg110_pong__get_packed_size
    - (const GG110Pong *message);
    -size_t gg110_pong__pack
    - (const GG110Pong *message,
    - uint8_t *out);
    -size_t gg110_pong__pack_to_buffer
    - (const GG110Pong *message,
    - ProtobufCBuffer *buffer);
    -GG110Pong *
    - gg110_pong__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg110_pong__free_unpacked
    - (GG110Pong *message,
    - ProtobufCAllocator *allocator);
    -/* GG110Ack methods */
    -void gg110_ack__init
    - (GG110Ack *message);
    -size_t gg110_ack__get_packed_size
    - (const GG110Ack *message);
    -size_t gg110_ack__pack
    - (const GG110Ack *message,
    - uint8_t *out);
    -size_t gg110_ack__pack_to_buffer
    - (const GG110Ack *message,
    - ProtobufCBuffer *buffer);
    -GG110Ack *
    - gg110_ack__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg110_ack__free_unpacked
    - (GG110Ack *message,
    - ProtobufCAllocator *allocator);
    -/* GG105Login methods */
    -void gg105_login__init
    - (GG105Login *message);
    -size_t gg105_login__get_packed_size
    - (const GG105Login *message);
    -size_t gg105_login__pack
    - (const GG105Login *message,
    - uint8_t *out);
    -size_t gg105_login__pack_to_buffer
    - (const GG105Login *message,
    - ProtobufCBuffer *buffer);
    -GG105Login *
    - gg105_login__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg105_login__free_unpacked
    - (GG105Login *message,
    - ProtobufCAllocator *allocator);
    -/* GG110MessageAckLink methods */
    -void gg110_message_ack_link__init
    - (GG110MessageAckLink *message);
    -size_t gg110_message_ack_link__get_packed_size
    - (const GG110MessageAckLink *message);
    -size_t gg110_message_ack_link__pack
    - (const GG110MessageAckLink *message,
    - uint8_t *out);
    -size_t gg110_message_ack_link__pack_to_buffer
    - (const GG110MessageAckLink *message,
    - ProtobufCBuffer *buffer);
    -GG110MessageAckLink *
    - gg110_message_ack_link__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg110_message_ack_link__free_unpacked
    - (GG110MessageAckLink *message,
    - ProtobufCAllocator *allocator);
    -/* GG110MessageAck methods */
    -void gg110_message_ack__init
    - (GG110MessageAck *message);
    -size_t gg110_message_ack__get_packed_size
    - (const GG110MessageAck *message);
    -size_t gg110_message_ack__pack
    - (const GG110MessageAck *message,
    - uint8_t *out);
    -size_t gg110_message_ack__pack_to_buffer
    - (const GG110MessageAck *message,
    - ProtobufCBuffer *buffer);
    -GG110MessageAck *
    - gg110_message_ack__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg110_message_ack__free_unpacked
    - (GG110MessageAck *message,
    - ProtobufCAllocator *allocator);
    -/* GG110Event methods */
    -void gg110_event__init
    - (GG110Event *message);
    -size_t gg110_event__get_packed_size
    - (const GG110Event *message);
    -size_t gg110_event__pack
    - (const GG110Event *message,
    - uint8_t *out);
    -size_t gg110_event__pack_to_buffer
    - (const GG110Event *message,
    - ProtobufCBuffer *buffer);
    -GG110Event *
    - gg110_event__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg110_event__free_unpacked
    - (GG110Event *message,
    - ProtobufCAllocator *allocator);
    -/* GG110RecvMessage methods */
    -void gg110_recv_message__init
    - (GG110RecvMessage *message);
    -size_t gg110_recv_message__get_packed_size
    - (const GG110RecvMessage *message);
    -size_t gg110_recv_message__pack
    - (const GG110RecvMessage *message,
    - uint8_t *out);
    -size_t gg110_recv_message__pack_to_buffer
    - (const GG110RecvMessage *message,
    - ProtobufCBuffer *buffer);
    -GG110RecvMessage *
    - gg110_recv_message__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg110_recv_message__free_unpacked
    - (GG110RecvMessage *message,
    - ProtobufCAllocator *allocator);
    -/* GG110SendMessage methods */
    -void gg110_send_message__init
    - (GG110SendMessage *message);
    -size_t gg110_send_message__get_packed_size
    - (const GG110SendMessage *message);
    -size_t gg110_send_message__pack
    - (const GG110SendMessage *message,
    - uint8_t *out);
    -size_t gg110_send_message__pack_to_buffer
    - (const GG110SendMessage *message,
    - ProtobufCBuffer *buffer);
    -GG110SendMessage *
    - gg110_send_message__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg110_send_message__free_unpacked
    - (GG110SendMessage *message,
    - ProtobufCAllocator *allocator);
    -/* GG110Imtoken methods */
    -void gg110_imtoken__init
    - (GG110Imtoken *message);
    -size_t gg110_imtoken__get_packed_size
    - (const GG110Imtoken *message);
    -size_t gg110_imtoken__pack
    - (const GG110Imtoken *message,
    - uint8_t *out);
    -size_t gg110_imtoken__pack_to_buffer
    - (const GG110Imtoken *message,
    - ProtobufCBuffer *buffer);
    -GG110Imtoken *
    - gg110_imtoken__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg110_imtoken__free_unpacked
    - (GG110Imtoken *message,
    - ProtobufCAllocator *allocator);
    -/* GG110ChatInfoUpdate methods */
    -void gg110_chat_info_update__init
    - (GG110ChatInfoUpdate *message);
    -size_t gg110_chat_info_update__get_packed_size
    - (const GG110ChatInfoUpdate *message);
    -size_t gg110_chat_info_update__pack
    - (const GG110ChatInfoUpdate *message,
    - uint8_t *out);
    -size_t gg110_chat_info_update__pack_to_buffer
    - (const GG110ChatInfoUpdate *message,
    - ProtobufCBuffer *buffer);
    -GG110ChatInfoUpdate *
    - gg110_chat_info_update__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg110_chat_info_update__free_unpacked
    - (GG110ChatInfoUpdate *message,
    - ProtobufCAllocator *allocator);
    -/* ProtobufKVP methods */
    -void protobuf_kvp__init
    - (ProtobufKVP *message);
    -size_t protobuf_kvp__get_packed_size
    - (const ProtobufKVP *message);
    -size_t protobuf_kvp__pack
    - (const ProtobufKVP *message,
    - uint8_t *out);
    -size_t protobuf_kvp__pack_to_buffer
    - (const ProtobufKVP *message,
    - ProtobufCBuffer *buffer);
    -ProtobufKVP *
    - protobuf_kvp__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void protobuf_kvp__free_unpacked
    - (ProtobufKVP *message,
    - ProtobufCAllocator *allocator);
    -/* GG110Options methods */
    -void gg110_options__init
    - (GG110Options *message);
    -size_t gg110_options__get_packed_size
    - (const GG110Options *message);
    -size_t gg110_options__pack
    - (const GG110Options *message,
    - uint8_t *out);
    -size_t gg110_options__pack_to_buffer
    - (const GG110Options *message,
    - ProtobufCBuffer *buffer);
    -GG110Options *
    - gg110_options__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg110_options__free_unpacked
    - (GG110Options *message,
    - ProtobufCAllocator *allocator);
    -/* GG110AccessInfo methods */
    -void gg110_access_info__init
    - (GG110AccessInfo *message);
    -size_t gg110_access_info__get_packed_size
    - (const GG110AccessInfo *message);
    -size_t gg110_access_info__pack
    - (const GG110AccessInfo *message,
    - uint8_t *out);
    -size_t gg110_access_info__pack_to_buffer
    - (const GG110AccessInfo *message,
    - ProtobufCBuffer *buffer);
    -GG110AccessInfo *
    - gg110_access_info__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg110_access_info__free_unpacked
    - (GG110AccessInfo *message,
    - ProtobufCAllocator *allocator);
    -/* GG112TransferInfoUin methods */
    -void gg112_transfer_info_uin__init
    - (GG112TransferInfoUin *message);
    -size_t gg112_transfer_info_uin__get_packed_size
    - (const GG112TransferInfoUin *message);
    -size_t gg112_transfer_info_uin__pack
    - (const GG112TransferInfoUin *message,
    - uint8_t *out);
    -size_t gg112_transfer_info_uin__pack_to_buffer
    - (const GG112TransferInfoUin *message,
    - ProtobufCBuffer *buffer);
    -GG112TransferInfoUin *
    - gg112_transfer_info_uin__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg112_transfer_info_uin__free_unpacked
    - (GG112TransferInfoUin *message,
    - ProtobufCAllocator *allocator);
    -/* GG112TransferInfoFile methods */
    -void gg112_transfer_info_file__init
    - (GG112TransferInfoFile *message);
    -size_t gg112_transfer_info_file__get_packed_size
    - (const GG112TransferInfoFile *message);
    -size_t gg112_transfer_info_file__pack
    - (const GG112TransferInfoFile *message,
    - uint8_t *out);
    -size_t gg112_transfer_info_file__pack_to_buffer
    - (const GG112TransferInfoFile *message,
    - ProtobufCBuffer *buffer);
    -GG112TransferInfoFile *
    - gg112_transfer_info_file__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg112_transfer_info_file__free_unpacked
    - (GG112TransferInfoFile *message,
    - ProtobufCAllocator *allocator);
    -/* GG112TransferInfo methods */
    -void gg112_transfer_info__init
    - (GG112TransferInfo *message);
    -size_t gg112_transfer_info__get_packed_size
    - (const GG112TransferInfo *message);
    -size_t gg112_transfer_info__pack
    - (const GG112TransferInfo *message,
    - uint8_t *out);
    -size_t gg112_transfer_info__pack_to_buffer
    - (const GG112TransferInfo *message,
    - ProtobufCBuffer *buffer);
    -GG112TransferInfo *
    - gg112_transfer_info__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg112_transfer_info__free_unpacked
    - (GG112TransferInfo *message,
    - ProtobufCAllocator *allocator);
    -/* GG110MagicNotification methods */
    -void gg110_magic_notification__init
    - (GG110MagicNotification *message);
    -size_t gg110_magic_notification__get_packed_size
    - (const GG110MagicNotification *message);
    -size_t gg110_magic_notification__pack
    - (const GG110MagicNotification *message,
    - uint8_t *out);
    -size_t gg110_magic_notification__pack_to_buffer
    - (const GG110MagicNotification *message,
    - ProtobufCBuffer *buffer);
    -GG110MagicNotification *
    - gg110_magic_notification__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -void gg110_magic_notification__free_unpacked
    - (GG110MagicNotification *message,
    - ProtobufCAllocator *allocator);
    -/* --- per-message closures --- */
    -
    -typedef void (*GG110LoginOK_Closure)
    - (const GG110LoginOK *message,
    - void *closure_data);
    -typedef void (*GG110Pong_Closure)
    - (const GG110Pong *message,
    - void *closure_data);
    -typedef void (*GG110Ack_Closure)
    - (const GG110Ack *message,
    - void *closure_data);
    -typedef void (*GG105Login_Closure)
    - (const GG105Login *message,
    - void *closure_data);
    -typedef void (*GG110MessageAckLink_Closure)
    - (const GG110MessageAckLink *message,
    - void *closure_data);
    -typedef void (*GG110MessageAck_Closure)
    - (const GG110MessageAck *message,
    - void *closure_data);
    -typedef void (*GG110Event_Closure)
    - (const GG110Event *message,
    - void *closure_data);
    -typedef void (*GG110RecvMessage_Closure)
    - (const GG110RecvMessage *message,
    - void *closure_data);
    -typedef void (*GG110SendMessage_Closure)
    - (const GG110SendMessage *message,
    - void *closure_data);
    -typedef void (*GG110Imtoken_Closure)
    - (const GG110Imtoken *message,
    - void *closure_data);
    -typedef void (*GG110ChatInfoUpdate_Closure)
    - (const GG110ChatInfoUpdate *message,
    - void *closure_data);
    -typedef void (*ProtobufKVP_Closure)
    - (const ProtobufKVP *message,
    - void *closure_data);
    -typedef void (*GG110Options_Closure)
    - (const GG110Options *message,
    - void *closure_data);
    -typedef void (*GG110AccessInfo_Closure)
    - (const GG110AccessInfo *message,
    - void *closure_data);
    -typedef void (*GG112TransferInfoUin_Closure)
    - (const GG112TransferInfoUin *message,
    - void *closure_data);
    -typedef void (*GG112TransferInfoFile_Closure)
    - (const GG112TransferInfoFile *message,
    - void *closure_data);
    -typedef void (*GG112TransferInfo_Closure)
    - (const GG112TransferInfo *message,
    - void *closure_data);
    -typedef void (*GG110MagicNotification_Closure)
    - (const GG110MagicNotification *message,
    - void *closure_data);
    -
    -/* --- services --- */
    -
    -
    -/* --- descriptors --- */
    -
    -extern const ProtobufCMessageDescriptor gg110_login_ok__descriptor;
    -extern const ProtobufCMessageDescriptor gg110_pong__descriptor;
    -extern const ProtobufCMessageDescriptor gg110_ack__descriptor;
    -extern const ProtobufCEnumDescriptor gg110_ack__type__descriptor;
    -extern const ProtobufCMessageDescriptor gg105_login__descriptor;
    -extern const ProtobufCMessageDescriptor gg110_message_ack_link__descriptor;
    -extern const ProtobufCMessageDescriptor gg110_message_ack__descriptor;
    -extern const ProtobufCMessageDescriptor gg110_event__descriptor;
    -extern const ProtobufCEnumDescriptor gg110_event__type__descriptor;
    -extern const ProtobufCMessageDescriptor gg110_recv_message__descriptor;
    -extern const ProtobufCMessageDescriptor gg110_send_message__descriptor;
    -extern const ProtobufCMessageDescriptor gg110_imtoken__descriptor;
    -extern const ProtobufCMessageDescriptor gg110_chat_info_update__descriptor;
    -extern const ProtobufCMessageDescriptor protobuf_kvp__descriptor;
    -extern const ProtobufCMessageDescriptor gg110_options__descriptor;
    -extern const ProtobufCMessageDescriptor gg110_access_info__descriptor;
    -extern const ProtobufCMessageDescriptor gg112_transfer_info_uin__descriptor;
    -extern const ProtobufCMessageDescriptor gg112_transfer_info_file__descriptor;
    -extern const ProtobufCMessageDescriptor gg112_transfer_info__descriptor;
    -extern const ProtobufCMessageDescriptor gg110_magic_notification__descriptor;
    -
    -PROTOBUF_C__END_DECLS
    -
    -
    -#endif /* PROTOBUF_C_packets_2eproto__INCLUDED */
    --- a/libpurple/protocols/gg/lib/protobuf-c.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,3288 +0,0 @@
    -/*
    - * Copyright (c) 2008-2014, Dave Benson and the protobuf-c authors.
    - * All rights reserved.
    - *
    - * Redistribution and use in source and binary forms, with or without
    - * modification, are permitted provided that the following conditions are
    - * met:
    - *
    - * * Redistributions of source code must retain the above copyright
    - * notice, this list of conditions and the following disclaimer.
    - *
    - * * Redistributions in binary form must reproduce the above
    - * copyright notice, this list of conditions and the following disclaimer
    - * in the documentation and/or other materials provided with the
    - * distribution.
    - *
    - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    - */
    -
    -/*! \file
    - * Support library for `protoc-c` generated code.
    - *
    - * This file implements the public API used by the code generated
    - * by `protoc-c`.
    - *
    - * \authors Dave Benson and the protobuf-c authors
    - *
    - * \copyright 2008-2014. Licensed under the terms of the [BSD-2-Clause] license.
    - */
    -
    -/**
    - * \todo 64-BIT OPTIMIZATION: certain implementations use 32-bit math
    - * even on 64-bit platforms (uint64_size, uint64_pack, parse_uint64).
    - *
    - * \todo Use size_t consistently.
    - */
    -
    -#include <stdlib.h> /* for malloc, free */
    -#include <string.h> /* for strcmp, strlen, memcpy, memmove, memset */
    -
    -/* Pull WORDS_BIGENDIAN etc */
    -#include "config.h"
    -
    -#include "protobuf-c.h"
    -
    -#define TRUE 1
    -#define FALSE 0
    -
    -#define PROTOBUF_C__ASSERT_NOT_REACHED() assert(0)
    -
    -/* Workaround for Microsoft compilers. */
    -#ifdef _MSC_VER
    -# define inline __inline
    -#endif
    -
    -/**
    - * \defgroup internal Internal functions and macros
    - *
    - * These are not exported by the library but are useful to developers working
    - * on `libprotobuf-c` itself.
    - */
    -
    -/**
    - * \defgroup macros Utility macros for manipulating structures
    - *
    - * Macros and constants used to manipulate the base "classes" generated by
    - * `protobuf-c`. They also define limits and check correctness.
    - *
    - * \ingroup internal
    - * @{
    - */
    -
    -/** The maximum length of a 64-bit integer in varint encoding. */
    -#define MAX_UINT64_ENCODED_SIZE 10
    -
    -#ifndef PROTOBUF_C_UNPACK_ERROR
    -# define PROTOBUF_C_UNPACK_ERROR(...)
    -#endif
    -
    -/**
    - * Internal `ProtobufCMessage` manipulation macro.
    - *
    - * Base macro for manipulating a `ProtobufCMessage`. Used by STRUCT_MEMBER() and
    - * STRUCT_MEMBER_PTR().
    - */
    -#define STRUCT_MEMBER_P(struct_p, struct_offset) \
    - ((void *) ((uint8_t *) (struct_p) + (struct_offset)))
    -
    -/**
    - * Return field in a `ProtobufCMessage` based on offset.
    - *
    - * Take a pointer to a `ProtobufCMessage` and find the field at the offset.
    - * Cast it to the passed type.
    - */
    -#define STRUCT_MEMBER(member_type, struct_p, struct_offset) \
    - (*(member_type *) STRUCT_MEMBER_P((struct_p), (struct_offset)))
    -
    -/**
    - * Return field in a `ProtobufCMessage` based on offset.
    - *
    - * Take a pointer to a `ProtobufCMessage` and find the field at the offset. Cast
    - * it to a pointer to the passed type.
    - */
    -#define STRUCT_MEMBER_PTR(member_type, struct_p, struct_offset) \
    - ((member_type *) STRUCT_MEMBER_P((struct_p), (struct_offset)))
    -
    -/* Assertions for magic numbers. */
    -
    -#define ASSERT_IS_ENUM_DESCRIPTOR(desc) \
    - assert((desc)->magic == PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC)
    -
    -#define ASSERT_IS_MESSAGE_DESCRIPTOR(desc) \
    - assert((desc)->magic == PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC)
    -
    -#define ASSERT_IS_MESSAGE(message) \
    - ASSERT_IS_MESSAGE_DESCRIPTOR((message)->descriptor)
    -
    -#define ASSERT_IS_SERVICE_DESCRIPTOR(desc) \
    - assert((desc)->magic == PROTOBUF_C__SERVICE_DESCRIPTOR_MAGIC)
    -
    -/**@}*/
    -
    -/* --- version --- */
    -
    -const char *
    -protobuf_c_version(void)
    -{
    - return PROTOBUF_C_VERSION;
    -}
    -
    -uint32_t
    -protobuf_c_version_number(void)
    -{
    - return PROTOBUF_C_VERSION_NUMBER;
    -}
    -
    -/* --- allocator --- */
    -
    -static void *
    -system_alloc(void *allocator_data, size_t size)
    -{
    - return malloc(size);
    -}
    -
    -static void
    -system_free(void *allocator_data, void *data)
    -{
    - free(data);
    -}
    -
    -static inline void *
    -do_alloc(ProtobufCAllocator *allocator, size_t size)
    -{
    - return allocator->alloc(allocator->allocator_data, size);
    -}
    -
    -static inline void
    -do_free(ProtobufCAllocator *allocator, void *data)
    -{
    - if (data != NULL)
    - allocator->free(allocator->allocator_data, data);
    -}
    -
    -/*
    - * This allocator uses the system's malloc() and free(). It is the default
    - * allocator used if NULL is passed as the ProtobufCAllocator to an exported
    - * function.
    - */
    -static ProtobufCAllocator protobuf_c__allocator = {
    - .alloc = &system_alloc,
    - .free = &system_free,
    - .allocator_data = NULL,
    -};
    -
    -/* === buffer-simple === */
    -
    -void
    -protobuf_c_buffer_simple_append(ProtobufCBuffer *buffer,
    - size_t len, const uint8_t *data)
    -{
    - ProtobufCBufferSimple *simp = (ProtobufCBufferSimple *) buffer;
    - size_t new_len = simp->len + len;
    -
    - if (new_len > simp->alloced) {
    - ProtobufCAllocator *allocator = simp->allocator;
    - size_t new_alloced = simp->alloced * 2;
    - uint8_t *new_data;
    -
    - if (allocator == NULL)
    - allocator = &protobuf_c__allocator;
    - while (new_alloced < new_len)
    - new_alloced += new_alloced;
    - new_data = do_alloc(allocator, new_alloced);
    - if (!new_data)
    - return;
    - memcpy(new_data, simp->data, simp->len);
    - if (simp->must_free_data)
    - do_free(allocator, simp->data);
    - else
    - simp->must_free_data = TRUE;
    - simp->data = new_data;
    - simp->alloced = new_alloced;
    - }
    - memcpy(simp->data + simp->len, data, len);
    - simp->len = new_len;
    -}
    -
    -/**
    - * \defgroup packedsz protobuf_c_message_get_packed_size() implementation
    - *
    - * Routines mainly used by protobuf_c_message_get_packed_size().
    - *
    - * \ingroup internal
    - * @{
    - */
    -
    -/**
    - * Return the number of bytes required to store the tag for the field. Includes
    - * 3 bits for the wire-type, and a single bit that denotes the end-of-tag.
    - *
    - * \param number
    - * Field tag to encode.
    - * \return
    - * Number of bytes required.
    - */
    -static inline size_t
    -get_tag_size(unsigned number)
    -{
    - if (number < (1 << 4)) {
    - return 1;
    - } else if (number < (1 << 11)) {
    - return 2;
    - } else if (number < (1 << 18)) {
    - return 3;
    - } else if (number < (1 << 25)) {
    - return 4;
    - } else {
    - return 5;
    - }
    -}
    -
    -/**
    - * Return the number of bytes required to store a variable-length unsigned
    - * 32-bit integer in base-128 varint encoding.
    - *
    - * \param v
    - * Value to encode.
    - * \return
    - * Number of bytes required.
    - */
    -static inline size_t
    -uint32_size(uint32_t v)
    -{
    - if (v < (1 << 7)) {
    - return 1;
    - } else if (v < (1 << 14)) {
    - return 2;
    - } else if (v < (1 << 21)) {
    - return 3;
    - } else if (v < (1 << 28)) {
    - return 4;
    - } else {
    - return 5;
    - }
    -}
    -
    -/**
    - * Return the number of bytes required to store a variable-length signed 32-bit
    - * integer in base-128 varint encoding.
    - *
    - * \param v
    - * Value to encode.
    - * \return
    - * Number of bytes required.
    - */
    -static inline size_t
    -int32_size(int32_t v)
    -{
    - if (v < 0) {
    - return 10;
    - } else if (v < (1 << 7)) {
    - return 1;
    - } else if (v < (1 << 14)) {
    - return 2;
    - } else if (v < (1 << 21)) {
    - return 3;
    - } else if (v < (1 << 28)) {
    - return 4;
    - } else {
    - return 5;
    - }
    -}
    -
    -/**
    - * Return the ZigZag-encoded 32-bit unsigned integer form of a 32-bit signed
    - * integer.
    - *
    - * \param v
    - * Value to encode.
    - * \return
    - * ZigZag encoded integer.
    - */
    -static inline uint32_t
    -zigzag32(int32_t v)
    -{
    - if (v < 0)
    - return ((uint32_t) (-v)) * 2 - 1;
    - else
    - return v * 2;
    -}
    -
    -/**
    - * Return the number of bytes required to store a signed 32-bit integer,
    - * converted to an unsigned 32-bit integer with ZigZag encoding, using base-128
    - * varint encoding.
    - *
    - * \param v
    - * Value to encode.
    - * \return
    - * Number of bytes required.
    - */
    -static inline size_t
    -sint32_size(int32_t v)
    -{
    - return uint32_size(zigzag32(v));
    -}
    -
    -/**
    - * Return the number of bytes required to store a 64-bit unsigned integer in
    - * base-128 varint encoding.
    - *
    - * \param v
    - * Value to encode.
    - * \return
    - * Number of bytes required.
    - */
    -static inline size_t
    -uint64_size(uint64_t v)
    -{
    - uint32_t upper_v = (uint32_t) (v >> 32);
    -
    - if (upper_v == 0) {
    - return uint32_size((uint32_t) v);
    - } else if (upper_v < (1 << 3)) {
    - return 5;
    - } else if (upper_v < (1 << 10)) {
    - return 6;
    - } else if (upper_v < (1 << 17)) {
    - return 7;
    - } else if (upper_v < (1 << 24)) {
    - return 8;
    - } else if (upper_v < (1U << 31)) {
    - return 9;
    - } else {
    - return 10;
    - }
    -}
    -
    -/**
    - * Return the ZigZag-encoded 64-bit unsigned integer form of a 64-bit signed
    - * integer.
    - *
    - * \param v
    - * Value to encode.
    - * \return
    - * ZigZag encoded integer.
    - */
    -static inline uint64_t
    -zigzag64(int64_t v)
    -{
    - if (v < 0)
    - return ((uint64_t) (-v)) * 2 - 1;
    - else
    - return v * 2;
    -}
    -
    -/**
    - * Return the number of bytes required to store a signed 64-bit integer,
    - * converted to an unsigned 64-bit integer with ZigZag encoding, using base-128
    - * varint encoding.
    - *
    - * \param v
    - * Value to encode.
    - * \return
    - * Number of bytes required.
    - */
    -static inline size_t
    -sint64_size(int64_t v)
    -{
    - return uint64_size(zigzag64(v));
    -}
    -
    -/**
    - * Calculate the serialized size of a single required message field, including
    - * the space needed by the preceding tag.
    - *
    - * \param field
    - * Field descriptor for member.
    - * \param member
    - * Field to encode.
    - * \return
    - * Number of bytes required.
    - */
    -static size_t
    -required_field_get_packed_size(const ProtobufCFieldDescriptor *field,
    - const void *member)
    -{
    - size_t rv = get_tag_size(field->id);
    -
    - switch (field->type) {
    - case PROTOBUF_C_TYPE_SINT32:
    - return rv + sint32_size(*(const int32_t *) member);
    - case PROTOBUF_C_TYPE_INT32:
    - return rv + int32_size(*(const uint32_t *) member);
    - case PROTOBUF_C_TYPE_UINT32:
    - return rv + uint32_size(*(const uint32_t *) member);
    - case PROTOBUF_C_TYPE_SINT64:
    - return rv + sint64_size(*(const int64_t *) member);
    - case PROTOBUF_C_TYPE_INT64:
    - case PROTOBUF_C_TYPE_UINT64:
    - return rv + uint64_size(*(const uint64_t *) member);
    - case PROTOBUF_C_TYPE_SFIXED32:
    - case PROTOBUF_C_TYPE_FIXED32:
    - return rv + 4;
    - case PROTOBUF_C_TYPE_SFIXED64:
    - case PROTOBUF_C_TYPE_FIXED64:
    - return rv + 8;
    - case PROTOBUF_C_TYPE_BOOL:
    - return rv + 1;
    - case PROTOBUF_C_TYPE_FLOAT:
    - return rv + 4;
    - case PROTOBUF_C_TYPE_DOUBLE:
    - return rv + 8;
    - case PROTOBUF_C_TYPE_ENUM:
    - /* \todo Is this correct for negative-valued enums? */
    - return rv + uint32_size(*(const uint32_t *) member);
    - case PROTOBUF_C_TYPE_STRING: {
    - const char *str = *(char * const *) member;
    - size_t len = str ? strlen(str) : 0;
    - return rv + uint32_size(len) + len;
    - }
    - case PROTOBUF_C_TYPE_BYTES: {
    - size_t len = ((const ProtobufCBinaryData *) member)->len;
    - return rv + uint32_size(len) + len;
    - }
    - case PROTOBUF_C_TYPE_MESSAGE: {
    - const ProtobufCMessage *msg = *(ProtobufCMessage * const *) member;
    - size_t subrv = msg ? protobuf_c_message_get_packed_size(msg) : 0;
    - return rv + uint32_size(subrv) + subrv;
    - }
    - }
    - PROTOBUF_C__ASSERT_NOT_REACHED();
    - return 0;
    -}
    -
    -/**
    - * Calculate the serialized size of a single optional message field, including
    - * the space needed by the preceding tag. Returns 0 if the optional field isn't
    - * set.
    - *
    - * \param field
    - * Field descriptor for member.
    - * \param has
    - * True if the field exists, false if not.
    - * \param member
    - * Field to encode.
    - * \return
    - * Number of bytes required.
    - */
    -static size_t
    -optional_field_get_packed_size(const ProtobufCFieldDescriptor *field,
    - const protobuf_c_boolean *has,
    - const void *member)
    -{
    - if (field->type == PROTOBUF_C_TYPE_MESSAGE ||
    - field->type == PROTOBUF_C_TYPE_STRING)
    - {
    - const void *ptr = *(const void * const *) member;
    - if (ptr == NULL || ptr == field->default_value)
    - return 0;
    - } else {
    - if (!*has)
    - return 0;
    - }
    - return required_field_get_packed_size(field, member);
    -}
    -
    -/**
    - * Calculate the serialized size of repeated message fields, which may consist
    - * of any number of values (including 0). Includes the space needed by the
    - * preceding tags (as needed).
    - *
    - * \param field
    - * Field descriptor for member.
    - * \param count
    - * Number of repeated field members.
    - * \param member
    - * Field to encode.
    - * \return
    - * Number of bytes required.
    - */
    -static size_t
    -repeated_field_get_packed_size(const ProtobufCFieldDescriptor *field,
    - size_t count, const void *member)
    -{
    - size_t header_size;
    - size_t rv = 0;
    - unsigned i;
    - void *array = *(void * const *) member;
    -
    - if (count == 0)
    - return 0;
    - header_size = get_tag_size(field->id);
    - if (0 == (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED))
    - header_size *= count;
    -
    - switch (field->type) {
    - case PROTOBUF_C_TYPE_SINT32:
    - for (i = 0; i < count; i++)
    - rv += sint32_size(((int32_t *) array)[i]);
    - break;
    - case PROTOBUF_C_TYPE_INT32:
    - for (i = 0; i < count; i++)
    - rv += int32_size(((uint32_t *) array)[i]);
    - break;
    - case PROTOBUF_C_TYPE_UINT32:
    - case PROTOBUF_C_TYPE_ENUM:
    - for (i = 0; i < count; i++)
    - rv += uint32_size(((uint32_t *) array)[i]);
    - break;
    - case PROTOBUF_C_TYPE_SINT64:
    - for (i = 0; i < count; i++)
    - rv += sint64_size(((int64_t *) array)[i]);
    - break;
    - case PROTOBUF_C_TYPE_INT64:
    - case PROTOBUF_C_TYPE_UINT64:
    - for (i = 0; i < count; i++)
    - rv += uint64_size(((uint64_t *) array)[i]);
    - break;
    - case PROTOBUF_C_TYPE_SFIXED32:
    - case PROTOBUF_C_TYPE_FIXED32:
    - case PROTOBUF_C_TYPE_FLOAT:
    - rv += 4 * count;
    - break;
    - case PROTOBUF_C_TYPE_SFIXED64:
    - case PROTOBUF_C_TYPE_FIXED64:
    - case PROTOBUF_C_TYPE_DOUBLE:
    - rv += 8 * count;
    - break;
    - case PROTOBUF_C_TYPE_BOOL:
    - rv += count;
    - break;
    - case PROTOBUF_C_TYPE_STRING:
    - for (i = 0; i < count; i++) {
    - size_t len = strlen(((char **) array)[i]);
    - rv += uint32_size(len) + len;
    - }
    - break;
    - case PROTOBUF_C_TYPE_BYTES:
    - for (i = 0; i < count; i++) {
    - size_t len = ((ProtobufCBinaryData *) array)[i].len;
    - rv += uint32_size(len) + len;
    - }
    - break;
    - case PROTOBUF_C_TYPE_MESSAGE:
    - for (i = 0; i < count; i++) {
    - size_t len = protobuf_c_message_get_packed_size(
    - ((ProtobufCMessage **) array)[i]);
    - rv += uint32_size(len) + len;
    - }
    - break;
    - }
    -
    - if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED))
    - header_size += uint32_size(rv);
    - return header_size + rv;
    -}
    -
    -/**
    - * Calculate the serialized size of an unknown field, i.e. one that is passed
    - * through mostly uninterpreted. This is required for forward compatibility if
    - * new fields are added to the message descriptor.
    - *
    - * \param field
    - * Unknown field type.
    - * \return
    - * Number of bytes required.
    - */
    -static inline size_t
    -unknown_field_get_packed_size(const ProtobufCMessageUnknownField *field)
    -{
    - return get_tag_size(field->tag) + field->len;
    -}
    -
    -/**@}*/
    -
    -/*
    - * Calculate the serialized size of the message.
    - */
    -size_t protobuf_c_message_get_packed_size(const ProtobufCMessage *message)
    -{
    - unsigned i;
    - size_t rv = 0;
    -
    - ASSERT_IS_MESSAGE(message);
    - for (i = 0; i < message->descriptor->n_fields; i++) {
    - const ProtobufCFieldDescriptor *field =
    - message->descriptor->fields + i;
    - const void *member =
    - ((const char *) message) + field->offset;
    - const void *qmember =
    - ((const char *) message) + field->quantifier_offset;
    -
    - if (field->label == PROTOBUF_C_LABEL_REQUIRED) {
    - rv += required_field_get_packed_size(field, member);
    - } else if (field->label == PROTOBUF_C_LABEL_OPTIONAL) {
    - rv += optional_field_get_packed_size(field, qmember, member);
    - } else {
    - rv += repeated_field_get_packed_size(
    - field,
    - *(const size_t *) qmember,
    - member
    - );
    - }
    - }
    - for (i = 0; i < message->n_unknown_fields; i++)
    - rv += unknown_field_get_packed_size(&message->unknown_fields[i]);
    - return rv;
    -}
    -
    -/**
    - * \defgroup pack protobuf_c_message_pack() implementation
    - *
    - * Routines mainly used by protobuf_c_message_pack().
    - *
    - * \ingroup internal
    - * @{
    - */
    -
    -/**
    - * Pack an unsigned 32-bit integer in base-128 varint encoding and return the
    - * number of bytes written, which must be 5 or less.
    - *
    - * \param value
    - * Value to encode.
    - * \param[out] out
    - * Packed value.
    - * \return
    - * Number of bytes written to `out`.
    - */
    -static inline size_t
    -uint32_pack(uint32_t value, uint8_t *out)
    -{
    - unsigned rv = 0;
    -
    - if (value >= 0x80) {
    - out[rv++] = value | 0x80;
    - value >>= 7;
    - if (value >= 0x80) {
    - out[rv++] = value | 0x80;
    - value >>= 7;
    - if (value >= 0x80) {
    - out[rv++] = value | 0x80;
    - value >>= 7;
    - if (value >= 0x80) {
    - out[rv++] = value | 0x80;
    - value >>= 7;
    - }
    - }
    - }
    - }
    - /* assert: value<128 */
    - out[rv++] = value;
    - return rv;
    -}
    -
    -/**
    - * Pack a signed 32-bit integer and return the number of bytes written.
    - * Negative numbers are encoded as two's complement 64-bit integers.
    - *
    - * \param value
    - * Value to encode.
    - * \param[out] out
    - * Packed value.
    - * \return
    - * Number of bytes written to `out`.
    - */
    -static inline size_t
    -int32_pack(int32_t value, uint8_t *out)
    -{
    - if (value < 0) {
    - out[0] = value | 0x80;
    - out[1] = (value >> 7) | 0x80;
    - out[2] = (value >> 14) | 0x80;
    - out[3] = (value >> 21) | 0x80;
    - out[4] = (value >> 28) | 0x80;
    - out[5] = out[6] = out[7] = out[8] = 0xff;
    - out[9] = 0x01;
    - return 10;
    - } else {
    - return uint32_pack(value, out);
    - }
    -}
    -
    -/**
    - * Pack a signed 32-bit integer using ZigZag encoding and return the number of
    - * bytes written.
    - *
    - * \param value
    - * Value to encode.
    - * \param[out] out
    - * Packed value.
    - * \return
    - * Number of bytes written to `out`.
    - */
    -static inline size_t
    -sint32_pack(int32_t value, uint8_t *out)
    -{
    - return uint32_pack(zigzag32(value), out);
    -}
    -
    -/**
    - * Pack a 64-bit unsigned integer using base-128 varint encoding and return the
    - * number of bytes written.
    - *
    - * \param value
    - * Value to encode.
    - * \param[out] out
    - * Packed value.
    - * \return
    - * Number of bytes written to `out`.
    - */
    -static size_t
    -uint64_pack(uint64_t value, uint8_t *out)
    -{
    - uint32_t hi = (uint32_t) (value >> 32);
    - uint32_t lo = (uint32_t) value;
    - unsigned rv;
    -
    - if (hi == 0)
    - return uint32_pack((uint32_t) lo, out);
    - out[0] = (lo) | 0x80;
    - out[1] = (lo >> 7) | 0x80;
    - out[2] = (lo >> 14) | 0x80;
    - out[3] = (lo >> 21) | 0x80;
    - if (hi < 8) {
    - out[4] = (hi << 4) | (lo >> 28);
    - return 5;
    - } else {
    - out[4] = ((hi & 7) << 4) | (lo >> 28) | 0x80;
    - hi >>= 3;
    - }
    - rv = 5;
    - while (hi >= 128) {
    - out[rv++] = hi | 0x80;
    - hi >>= 7;
    - }
    - out[rv++] = hi;
    - return rv;
    -}
    -
    -/**
    - * Pack a 64-bit signed integer in ZigZag encoding and return the number of
    - * bytes written.
    - *
    - * \param value
    - * Value to encode.
    - * \param[out] out
    - * Packed value.
    - * \return
    - * Number of bytes written to `out`.
    - */
    -static inline size_t
    -sint64_pack(int64_t value, uint8_t *out)
    -{
    - return uint64_pack(zigzag64(value), out);
    -}
    -
    -/**
    - * Pack a 32-bit quantity in little-endian byte order. Used for protobuf wire
    - * types fixed32, sfixed32, float. Similar to "htole32".
    - *
    - * \param value
    - * Value to encode.
    - * \param[out] out
    - * Packed value.
    - * \return
    - * Number of bytes written to `out`.
    - */
    -static inline size_t
    -fixed32_pack(uint32_t value, void *out)
    -{
    -#if !defined(WORDS_BIGENDIAN)
    - memcpy(out, &value, 4);
    -#else
    - uint8_t *buf = out;
    -
    - buf[0] = value;
    - buf[1] = value >> 8;
    - buf[2] = value >> 16;
    - buf[3] = value >> 24;
    -#endif
    - return 4;
    -}
    -
    -/**
    - * Pack a 64-bit quantity in little-endian byte order. Used for protobuf wire
    - * types fixed64, sfixed64, double. Similar to "htole64".
    - *
    - * \todo The big-endian impl is really only good for 32-bit machines, a 64-bit
    - * version would be appreciated, plus a way to decide to use 64-bit math where
    - * convenient.
    - *
    - * \param value
    - * Value to encode.
    - * \param[out] out
    - * Packed value.
    - * \return
    - * Number of bytes written to `out`.
    - */
    -static inline size_t
    -fixed64_pack(uint64_t value, void *out)
    -{
    -#if !defined(WORDS_BIGENDIAN)
    - memcpy(out, &value, 8);
    -#else
    - fixed32_pack(value, out);
    - fixed32_pack(value >> 32, ((char *) out) + 4);
    -#endif
    - return 8;
    -}
    -
    -/**
    - * Pack a boolean value as an integer and return the number of bytes written.
    - *
    - * \todo Perhaps on some platforms *out = !!value would be a better impl, b/c
    - * that is idiomatic C++ in some STL implementations.
    - *
    - * \param value
    - * Value to encode.
    - * \param[out] out
    - * Packed value.
    - * \return
    - * Number of bytes written to `out`.
    - */
    -static inline size_t
    -boolean_pack(protobuf_c_boolean value, uint8_t *out)
    -{
    - *out = value ? TRUE : FALSE;
    - return 1;
    -}
    -
    -/**
    - * Pack a NUL-terminated C string and return the number of bytes written. The
    - * output includes a length delimiter.
    - *
    - * The NULL pointer is treated as an empty string. This isn't really necessary,
    - * but it allows people to leave required strings blank. (See Issue #13 in the
    - * bug tracker for a little more explanation).
    - *
    - * \param str
    - * String to encode.
    - * \param[out] out
    - * Packed value.
    - * \return
    - * Number of bytes written to `out`.
    - */
    -static inline size_t
    -string_pack(const char *str, uint8_t *out)
    -{
    - if (str == NULL) {
    - out[0] = 0;
    - return 1;
    - } else {
    - size_t len = strlen(str);
    - size_t rv = uint32_pack(len, out);
    - memcpy(out + rv, str, len);
    - return rv + len;
    - }
    -}
    -
    -/**
    - * Pack a ProtobufCBinaryData and return the number of bytes written. The output
    - * includes a length delimiter.
    - *
    - * \param bd
    - * ProtobufCBinaryData to encode.
    - * \param[out] out
    - * Packed value.
    - * \return
    - * Number of bytes written to `out`.
    - */
    -static inline size_t
    -binary_data_pack(const ProtobufCBinaryData *bd, uint8_t *out)
    -{
    - size_t len = bd->len;
    - size_t rv = uint32_pack(len, out);
    - memcpy(out + rv, bd->data, len);
    - return rv + len;
    -}
    -
    -/**
    - * Pack a ProtobufCMessage and return the number of bytes written. The output
    - * includes a length delimiter.
    - *
    - * \param message
    - * ProtobufCMessage object to pack.
    - * \param[out] out
    - * Packed message.
    - * \return
    - * Number of bytes written to `out`.
    - */
    -static inline size_t
    -prefixed_message_pack(const ProtobufCMessage *message, uint8_t *out)
    -{
    - if (message == NULL) {
    - out[0] = 0;
    - return 1;
    - } else {
    - size_t rv = protobuf_c_message_pack(message, out + 1);
    - uint32_t rv_packed_size = uint32_size(rv);
    - if (rv_packed_size != 1)
    - memmove(out + rv_packed_size, out + 1, rv);
    - return uint32_pack(rv, out) + rv;
    - }
    -}
    -
    -/**
    - * Pack a field tag.
    - *
    - * Wire-type will be added in required_field_pack().
    - *
    - * \todo Just call uint64_pack on 64-bit platforms.
    - *
    - * \param id
    - * Tag value to encode.
    - * \param[out] out
    - * Packed value.
    - * \return
    - * Number of bytes written to `out`.
    - */
    -static size_t
    -tag_pack(uint32_t id, uint8_t *out)
    -{
    - if (id < (1 << (32 - 3)))
    - return uint32_pack(id << 3, out);
    - else
    - return uint64_pack(((uint64_t) id) << 3, out);
    -}
    -
    -/**
    - * Pack a required field and return the number of bytes written.
    - *
    - * \param field
    - * Field descriptor.
    - * \param member
    - * The field member.
    - * \param[out] out
    - * Packed value.
    - * \return
    - * Number of bytes written to `out`.
    - */
    -static size_t
    -required_field_pack(const ProtobufCFieldDescriptor *field,
    - const void *member, uint8_t *out)
    -{
    - size_t rv = tag_pack(field->id, out);
    -
    - switch (field->type) {
    - case PROTOBUF_C_TYPE_SINT32:
    - out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
    - return rv + sint32_pack(*(const int32_t *) member, out + rv);
    - case PROTOBUF_C_TYPE_INT32:
    - out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
    - return rv + int32_pack(*(const uint32_t *) member, out + rv);
    - case PROTOBUF_C_TYPE_UINT32:
    - case PROTOBUF_C_TYPE_ENUM:
    - out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
    - return rv + uint32_pack(*(const uint32_t *) member, out + rv);
    - case PROTOBUF_C_TYPE_SINT64:
    - out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
    - return rv + sint64_pack(*(const int64_t *) member, out + rv);
    - case PROTOBUF_C_TYPE_INT64:
    - case PROTOBUF_C_TYPE_UINT64:
    - out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
    - return rv + uint64_pack(*(const uint64_t *) member, out + rv);
    - case PROTOBUF_C_TYPE_SFIXED32:
    - case PROTOBUF_C_TYPE_FIXED32:
    - case PROTOBUF_C_TYPE_FLOAT:
    - out[0] |= PROTOBUF_C_WIRE_TYPE_32BIT;
    - return rv + fixed32_pack(*(const uint32_t *) member, out + rv);
    - case PROTOBUF_C_TYPE_SFIXED64:
    - case PROTOBUF_C_TYPE_FIXED64:
    - case PROTOBUF_C_TYPE_DOUBLE:
    - out[0] |= PROTOBUF_C_WIRE_TYPE_64BIT;
    - return rv + fixed64_pack(*(const uint64_t *) member, out + rv);
    - case PROTOBUF_C_TYPE_BOOL:
    - out[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
    - return rv + boolean_pack(*(const protobuf_c_boolean *) member, out + rv);
    - case PROTOBUF_C_TYPE_STRING:
    - out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
    - return rv + string_pack(*(char *const *) member, out + rv);
    - case PROTOBUF_C_TYPE_BYTES:
    - out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
    - return rv + binary_data_pack((const ProtobufCBinaryData *) member, out + rv);
    - case PROTOBUF_C_TYPE_MESSAGE:
    - out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
    - return rv + prefixed_message_pack(*(ProtobufCMessage * const *) member, out + rv);
    - }
    - PROTOBUF_C__ASSERT_NOT_REACHED();
    - return 0;
    -}
    -
    -/**
    - * Pack an optional field and return the number of bytes written.
    - *
    - * \param field
    - * Field descriptor.
    - * \param has
    - * Whether the field is set.
    - * \param member
    - * The field member.
    - * \param[out] out
    - * Packed value.
    - * \return
    - * Number of bytes written to `out`.
    - */
    -static size_t
    -optional_field_pack(const ProtobufCFieldDescriptor *field,
    - const protobuf_c_boolean *has,
    - const void *member, uint8_t *out)
    -{
    - if (field->type == PROTOBUF_C_TYPE_MESSAGE ||
    - field->type == PROTOBUF_C_TYPE_STRING)
    - {
    - const void *ptr = *(const void * const *) member;
    - if (ptr == NULL || ptr == field->default_value)
    - return 0;
    - } else {
    - if (!*has)
    - return 0;
    - }
    - return required_field_pack(field, member, out);
    -}
    -
    -/**
    - * Given a field type, return the in-memory size.
    - *
    - * \todo Implement as a table lookup.
    - *
    - * \param type
    - * Field type.
    - * \return
    - * Size of the field.
    - */
    -static inline size_t
    -sizeof_elt_in_repeated_array(ProtobufCType type)
    -{
    - switch (type) {
    - case PROTOBUF_C_TYPE_SINT32:
    - case PROTOBUF_C_TYPE_INT32:
    - case PROTOBUF_C_TYPE_UINT32:
    - case PROTOBUF_C_TYPE_SFIXED32:
    - case PROTOBUF_C_TYPE_FIXED32:
    - case PROTOBUF_C_TYPE_FLOAT:
    - case PROTOBUF_C_TYPE_ENUM:
    - return 4;
    - case PROTOBUF_C_TYPE_SINT64:
    - case PROTOBUF_C_TYPE_INT64:
    - case PROTOBUF_C_TYPE_UINT64:
    - case PROTOBUF_C_TYPE_SFIXED64:
    - case PROTOBUF_C_TYPE_FIXED64:
    - case PROTOBUF_C_TYPE_DOUBLE:
    - return 8;
    - case PROTOBUF_C_TYPE_BOOL:
    - return sizeof(protobuf_c_boolean);
    - case PROTOBUF_C_TYPE_STRING:
    - case PROTOBUF_C_TYPE_MESSAGE:
    - return sizeof(void *);
    - case PROTOBUF_C_TYPE_BYTES:
    - return sizeof(ProtobufCBinaryData);
    - }
    - PROTOBUF_C__ASSERT_NOT_REACHED();
    - return 0;
    -}
    -
    -/**
    - * Pack an array of 32-bit quantities.
    - *
    - * \param[out] out
    - * Destination.
    - * \param[in] in
    - * Source.
    - * \param[in] n
    - * Number of elements in the source array.
    - */
    -static void
    -copy_to_little_endian_32(void *out, const void *in, const unsigned n)
    -{
    -#if !defined(WORDS_BIGENDIAN)
    - memcpy(out, in, n * 4);
    -#else
    - unsigned i;
    - const uint32_t *ini = in;
    - for (i = 0; i < n; i++)
    - fixed32_pack(ini[i], (uint32_t *) out + i);
    -#endif
    -}
    -
    -/**
    - * Pack an array of 64-bit quantities.
    - *
    - * \param[out] out
    - * Destination.
    - * \param[in] in
    - * Source.
    - * \param[in] n
    - * Number of elements in the source array.
    - */
    -static void
    -copy_to_little_endian_64(void *out, const void *in, const unsigned n)
    -{
    -#if !defined(WORDS_BIGENDIAN)
    - memcpy(out, in, n * 8);
    -#else
    - unsigned i;
    - const uint64_t *ini = in;
    - for (i = 0; i < n; i++)
    - fixed64_pack(ini[i], (uint64_t *) out + i);
    -#endif
    -}
    -
    -/**
    - * Get the minimum number of bytes required to pack a field value of a
    - * particular type.
    - *
    - * \param type
    - * Field type.
    - * \return
    - * Number of bytes.
    - */
    -static unsigned
    -get_type_min_size(ProtobufCType type)
    -{
    - if (type == PROTOBUF_C_TYPE_SFIXED32 ||
    - type == PROTOBUF_C_TYPE_FIXED32 ||
    - type == PROTOBUF_C_TYPE_FLOAT)
    - {
    - return 4;
    - }
    - if (type == PROTOBUF_C_TYPE_SFIXED64 ||
    - type == PROTOBUF_C_TYPE_FIXED64 ||
    - type == PROTOBUF_C_TYPE_DOUBLE)
    - {
    - return 8;
    - }
    - return 1;
    -}
    -
    -/**
    - * Packs the elements of a repeated field and returns the serialised field and
    - * its length.
    - *
    - * \param field
    - * Field descriptor.
    - * \param count
    - * Number of elements in the repeated field array.
    - * \param member
    - * Pointer to the elements for this repeated field.
    - * \param[out] out
    - * Serialised representation of the repeated field.
    - * \return
    - * Number of bytes serialised to `out`.
    - */
    -static size_t
    -repeated_field_pack(const ProtobufCFieldDescriptor *field,
    - size_t count, const void *member, uint8_t *out)
    -{
    - void *array = *(void * const *) member;
    - unsigned i;
    -
    - if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED)) {
    - unsigned header_len;
    - unsigned len_start;
    - unsigned min_length;
    - unsigned payload_len;
    - unsigned length_size_min;
    - unsigned actual_length_size;
    - uint8_t *payload_at;
    -
    - if (count == 0)
    - return 0;
    - header_len = tag_pack(field->id, out);
    - out[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
    - len_start = header_len;
    - min_length = get_type_min_size(field->type) * count;
    - length_size_min = uint32_size(min_length);
    - header_len += length_size_min;
    - payload_at = out + header_len;
    -
    - switch (field->type) {
    - case PROTOBUF_C_TYPE_SFIXED32:
    - case PROTOBUF_C_TYPE_FIXED32:
    - case PROTOBUF_C_TYPE_FLOAT:
    - copy_to_little_endian_32(payload_at, array, count);
    - payload_at += count * 4;
    - break;
    - case PROTOBUF_C_TYPE_SFIXED64:
    - case PROTOBUF_C_TYPE_FIXED64:
    - case PROTOBUF_C_TYPE_DOUBLE:
    - copy_to_little_endian_64(payload_at, array, count);
    - payload_at += count * 8;
    - break;
    - case PROTOBUF_C_TYPE_INT32: {
    - const int32_t *arr = (const int32_t *) array;
    - for (i = 0; i < count; i++)
    - payload_at += int32_pack(arr[i], payload_at);
    - break;
    - }
    - case PROTOBUF_C_TYPE_SINT32: {
    - const int32_t *arr = (const int32_t *) array;
    - for (i = 0; i < count; i++)
    - payload_at += sint32_pack(arr[i], payload_at);
    - break;
    - }
    - case PROTOBUF_C_TYPE_SINT64: {
    - const int64_t *arr = (const int64_t *) array;
    - for (i = 0; i < count; i++)
    - payload_at += sint64_pack(arr[i], payload_at);
    - break;
    - }
    - case PROTOBUF_C_TYPE_ENUM:
    - case PROTOBUF_C_TYPE_UINT32: {
    - const uint32_t *arr = (const uint32_t *) array;
    - for (i = 0; i < count; i++)
    - payload_at += uint32_pack(arr[i], payload_at);
    - break;
    - }
    - case PROTOBUF_C_TYPE_INT64:
    - case PROTOBUF_C_TYPE_UINT64: {
    - const uint64_t *arr = (const uint64_t *) array;
    - for (i = 0; i < count; i++)
    - payload_at += uint64_pack(arr[i], payload_at);
    - break;
    - }
    - case PROTOBUF_C_TYPE_BOOL: {
    - const protobuf_c_boolean *arr = (const protobuf_c_boolean *) array;
    - for (i = 0; i < count; i++)
    - payload_at += boolean_pack(arr[i], payload_at);
    - break;
    - }
    - default:
    - PROTOBUF_C__ASSERT_NOT_REACHED();
    - }
    -
    - payload_len = payload_at - (out + header_len);
    - actual_length_size = uint32_size(payload_len);
    - if (length_size_min != actual_length_size) {
    - assert(actual_length_size == length_size_min + 1);
    - memmove(out + header_len + 1, out + header_len,
    - payload_len);
    - header_len++;
    - }
    - uint32_pack(payload_len, out + len_start);
    - return header_len + payload_len;
    - } else {
    - /* not "packed" cased */
    - /* CONSIDER: optimize this case a bit (by putting the loop inside the switch) */
    - size_t rv = 0;
    - unsigned siz = sizeof_elt_in_repeated_array(field->type);
    -
    - for (i = 0; i < count; i++) {
    - rv += required_field_pack(field, array, out + rv);
    - array = (char *)array + siz;
    - }
    - return rv;
    - }
    -}
    -
    -static size_t
    -unknown_field_pack(const ProtobufCMessageUnknownField *field, uint8_t *out)
    -{
    - size_t rv = tag_pack(field->tag, out);
    - out[0] |= field->wire_type;
    - memcpy(out + rv, field->data, field->len);
    - return rv + field->len;
    -}
    -
    -/**@}*/
    -
    -size_t
    -protobuf_c_message_pack(const ProtobufCMessage *message, uint8_t *out)
    -{
    - unsigned i;
    - size_t rv = 0;
    -
    - ASSERT_IS_MESSAGE(message);
    - for (i = 0; i < message->descriptor->n_fields; i++) {
    - const ProtobufCFieldDescriptor *field =
    - message->descriptor->fields + i;
    - const void *member = ((const char *) message) + field->offset;
    -
    - /*
    - * It doesn't hurt to compute qmember (a pointer to the
    - * quantifier field of the structure), but the pointer is only
    - * valid if the field is:
    - * - a repeated field, or
    - * - an optional field that isn't a pointer type
    - * (Meaning: not a message or a string).
    - */
    - const void *qmember =
    - ((const char *) message) + field->quantifier_offset;
    -
    - if (field->label == PROTOBUF_C_LABEL_REQUIRED) {
    - rv += required_field_pack(field, member, out + rv);
    - } else if (field->label == PROTOBUF_C_LABEL_OPTIONAL) {
    - /*
    - * Note that qmember is bogus for strings and messages,
    - * but it isn't used.
    - */
    - rv += optional_field_pack(field, qmember, member, out + rv);
    - } else {
    - rv += repeated_field_pack(field, *(const size_t *) qmember,
    - member, out + rv);
    - }
    - }
    - for (i = 0; i < message->n_unknown_fields; i++)
    - rv += unknown_field_pack(&message->unknown_fields[i], out + rv);
    - return rv;
    -}
    -
    -/**
    - * \defgroup packbuf protobuf_c_message_pack_to_buffer() implementation
    - *
    - * Routines mainly used by protobuf_c_message_pack_to_buffer().
    - *
    - * \ingroup internal
    - * @{
    - */
    -
    -/**
    - * Pack a required field to a virtual buffer.
    - *
    - * \param field
    - * Field descriptor.
    - * \param member
    - * The element to be packed.
    - * \param[out] buffer
    - * Virtual buffer to append data to.
    - * \return
    - * Number of bytes packed.
    - */
    -static size_t
    -required_field_pack_to_buffer(const ProtobufCFieldDescriptor *field,
    - const void *member, ProtobufCBuffer *buffer)
    -{
    - size_t rv;
    - uint8_t scratch[MAX_UINT64_ENCODED_SIZE * 2];
    -
    - rv = tag_pack(field->id, scratch);
    - switch (field->type) {
    - case PROTOBUF_C_TYPE_SINT32:
    - scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
    - rv += sint32_pack(*(const int32_t *) member, scratch + rv);
    - buffer->append(buffer, rv, scratch);
    - break;
    - case PROTOBUF_C_TYPE_INT32:
    - scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
    - rv += int32_pack(*(const uint32_t *) member, scratch + rv);
    - buffer->append(buffer, rv, scratch);
    - break;
    - case PROTOBUF_C_TYPE_UINT32:
    - case PROTOBUF_C_TYPE_ENUM:
    - scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
    - rv += uint32_pack(*(const uint32_t *) member, scratch + rv);
    - buffer->append(buffer, rv, scratch);
    - break;
    - case PROTOBUF_C_TYPE_SINT64:
    - scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
    - rv += sint64_pack(*(const int64_t *) member, scratch + rv);
    - buffer->append(buffer, rv, scratch);
    - break;
    - case PROTOBUF_C_TYPE_INT64:
    - case PROTOBUF_C_TYPE_UINT64:
    - scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
    - rv += uint64_pack(*(const uint64_t *) member, scratch + rv);
    - buffer->append(buffer, rv, scratch);
    - break;
    - case PROTOBUF_C_TYPE_SFIXED32:
    - case PROTOBUF_C_TYPE_FIXED32:
    - case PROTOBUF_C_TYPE_FLOAT:
    - scratch[0] |= PROTOBUF_C_WIRE_TYPE_32BIT;
    - rv += fixed32_pack(*(const uint32_t *) member, scratch + rv);
    - buffer->append(buffer, rv, scratch);
    - break;
    - case PROTOBUF_C_TYPE_SFIXED64:
    - case PROTOBUF_C_TYPE_FIXED64:
    - case PROTOBUF_C_TYPE_DOUBLE:
    - scratch[0] |= PROTOBUF_C_WIRE_TYPE_64BIT;
    - rv += fixed64_pack(*(const uint64_t *) member, scratch + rv);
    - buffer->append(buffer, rv, scratch);
    - break;
    - case PROTOBUF_C_TYPE_BOOL:
    - scratch[0] |= PROTOBUF_C_WIRE_TYPE_VARINT;
    - rv += boolean_pack(*(const protobuf_c_boolean *) member, scratch + rv);
    - buffer->append(buffer, rv, scratch);
    - break;
    - case PROTOBUF_C_TYPE_STRING: {
    - const char *str = *(char *const *) member;
    - size_t sublen = str ? strlen(str) : 0;
    -
    - scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
    - rv += uint32_pack(sublen, scratch + rv);
    - buffer->append(buffer, rv, scratch);
    - buffer->append(buffer, sublen, (const uint8_t *) str);
    - rv += sublen;
    - break;
    - }
    - case PROTOBUF_C_TYPE_BYTES: {
    - const ProtobufCBinaryData *bd = ((const ProtobufCBinaryData *) member);
    - size_t sublen = bd->len;
    -
    - scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
    - rv += uint32_pack(sublen, scratch + rv);
    - buffer->append(buffer, rv, scratch);
    - buffer->append(buffer, sublen, bd->data);
    - rv += sublen;
    - break;
    - }
    - case PROTOBUF_C_TYPE_MESSAGE: {
    - uint8_t simple_buffer_scratch[256];
    - size_t sublen;
    - const ProtobufCMessage *msg = *(ProtobufCMessage * const *) member;
    - ProtobufCBufferSimple simple_buffer =
    - PROTOBUF_C_BUFFER_SIMPLE_INIT(simple_buffer_scratch);
    -
    - scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
    - if (msg == NULL)
    - sublen = 0;
    - else
    - sublen = protobuf_c_message_pack_to_buffer(msg, &simple_buffer.base);
    - rv += uint32_pack(sublen, scratch + rv);
    - buffer->append(buffer, rv, scratch);
    - buffer->append(buffer, sublen, simple_buffer.data);
    - rv += sublen;
    - PROTOBUF_C_BUFFER_SIMPLE_CLEAR(&simple_buffer);
    - break;
    - }
    - default:
    - PROTOBUF_C__ASSERT_NOT_REACHED();
    - }
    - return rv;
    -}
    -
    -/**
    - * Pack an optional field to a buffer.
    - *
    - * \param field
    - * Field descriptor.
    - * \param has
    - * Whether the field is set.
    - * \param member
    - * The element to be packed.
    - * \param[out] buffer
    - * Virtual buffer to append data to.
    - * \return
    - * Number of bytes serialised to `buffer`.
    - */
    -static size_t
    -optional_field_pack_to_buffer(const ProtobufCFieldDescriptor *field,
    - const protobuf_c_boolean *has,
    - const void *member, ProtobufCBuffer *buffer)
    -{
    - if (field->type == PROTOBUF_C_TYPE_MESSAGE ||
    - field->type == PROTOBUF_C_TYPE_STRING)
    - {
    - const void *ptr = *(const void *const *) member;
    - if (ptr == NULL || ptr == field->default_value)
    - return 0;
    - } else {
    - if (!*has)
    - return 0;
    - }
    - return required_field_pack_to_buffer(field, member, buffer);
    -}
    -
    -/**
    - * Get the packed size of an array of same field type.
    - *
    - * \param field
    - * Field descriptor.
    - * \param count
    - * Number of elements of this type.
    - * \param array
    - * The elements to get the size of.
    - * \return
    - * Number of bytes required.
    - */
    -static size_t
    -get_packed_payload_length(const ProtobufCFieldDescriptor *field,
    - unsigned count, const void *array)
    -{
    - unsigned rv = 0;
    - unsigned i;
    -
    - switch (field->type) {
    - case PROTOBUF_C_TYPE_SFIXED32:
    - case PROTOBUF_C_TYPE_FIXED32:
    - case PROTOBUF_C_TYPE_FLOAT:
    - return count * 4;
    - case PROTOBUF_C_TYPE_SFIXED64:
    - case PROTOBUF_C_TYPE_FIXED64:
    - case PROTOBUF_C_TYPE_DOUBLE:
    - return count * 8;
    - case PROTOBUF_C_TYPE_INT32: {
    - const int32_t *arr = (const int32_t *) array;
    - for (i = 0; i < count; i++)
    - rv += int32_size(arr[i]);
    - break;
    - }
    - case PROTOBUF_C_TYPE_SINT32: {
    - const int32_t *arr = (const int32_t *) array;
    - for (i = 0; i < count; i++)
    - rv += sint32_size(arr[i]);
    - break;
    - }
    - case PROTOBUF_C_TYPE_ENUM:
    - case PROTOBUF_C_TYPE_UINT32: {
    - const uint32_t *arr = (const uint32_t *) array;
    - for (i = 0; i < count; i++)
    - rv += uint32_size(arr[i]);
    - break;
    - }
    - case PROTOBUF_C_TYPE_SINT64: {
    - const int64_t *arr = (const int64_t *) array;
    - for (i = 0; i < count; i++)
    - rv += sint64_size(arr[i]);
    - break;
    - }
    - case PROTOBUF_C_TYPE_INT64:
    - case PROTOBUF_C_TYPE_UINT64: {
    - const uint64_t *arr = (const uint64_t *) array;
    - for (i = 0; i < count; i++)
    - rv += uint64_size(arr[i]);
    - break;
    - }
    - case PROTOBUF_C_TYPE_BOOL:
    - return count;
    - default:
    - PROTOBUF_C__ASSERT_NOT_REACHED();
    - }
    - return rv;
    -}
    -
    -/**
    - * Pack an array of same field type to a virtual buffer.
    - *
    - * \param field
    - * Field descriptor.
    - * \param count
    - * Number of elements of this type.
    - * \param array
    - * The elements to get the size of.
    - * \param[out] buffer
    - * Virtual buffer to append data to.
    - * \return
    - * Number of bytes packed.
    - */
    -static size_t
    -pack_buffer_packed_payload(const ProtobufCFieldDescriptor *field,
    - unsigned count, const void *array,
    - ProtobufCBuffer *buffer)
    -{
    - uint8_t scratch[16];
    - size_t rv = 0;
    - unsigned i;
    -
    - switch (field->type) {
    - case PROTOBUF_C_TYPE_SFIXED32:
    - case PROTOBUF_C_TYPE_FIXED32:
    - case PROTOBUF_C_TYPE_FLOAT:
    -#if !defined(WORDS_BIGENDIAN)
    - rv = count * 4;
    - goto no_packing_needed;
    -#else
    - for (i = 0; i < count; i++) {
    - unsigned len = fixed32_pack(((uint32_t *) array)[i], scratch);
    - buffer->append(buffer, len, scratch);
    - rv += len;
    - }
    - break;
    -#endif
    - case PROTOBUF_C_TYPE_SFIXED64:
    - case PROTOBUF_C_TYPE_FIXED64:
    - case PROTOBUF_C_TYPE_DOUBLE:
    -#if !defined(WORDS_BIGENDIAN)
    - rv = count * 8;
    - goto no_packing_needed;
    -#else
    - for (i = 0; i < count; i++) {
    - unsigned len = fixed64_pack(((uint64_t *) array)[i], scratch);
    - buffer->append(buffer, len, scratch);
    - rv += len;
    - }
    - break;
    -#endif
    - case PROTOBUF_C_TYPE_INT32:
    - for (i = 0; i < count; i++) {
    - unsigned len = int32_pack(((int32_t *) array)[i], scratch);
    - buffer->append(buffer, len, scratch);
    - rv += len;
    - }
    - break;
    - case PROTOBUF_C_TYPE_SINT32:
    - for (i = 0; i < count; i++) {
    - unsigned len = sint32_pack(((int32_t *) array)[i], scratch);
    - buffer->append(buffer, len, scratch);
    - rv += len;
    - }
    - break;
    - case PROTOBUF_C_TYPE_ENUM:
    - case PROTOBUF_C_TYPE_UINT32:
    - for (i = 0; i < count; i++) {
    - unsigned len = uint32_pack(((uint32_t *) array)[i], scratch);
    - buffer->append(buffer, len, scratch);
    - rv += len;
    - }
    - break;
    - case PROTOBUF_C_TYPE_SINT64:
    - for (i = 0; i < count; i++) {
    - unsigned len = sint64_pack(((int64_t *) array)[i], scratch);
    - buffer->append(buffer, len, scratch);
    - rv += len;
    - }
    - break;
    - case PROTOBUF_C_TYPE_INT64:
    - case PROTOBUF_C_TYPE_UINT64:
    - for (i = 0; i < count; i++) {
    - unsigned len = uint64_pack(((uint64_t *) array)[i], scratch);
    - buffer->append(buffer, len, scratch);
    - rv += len;
    - }
    - break;
    - case PROTOBUF_C_TYPE_BOOL:
    - for (i = 0; i < count; i++) {
    - unsigned len = boolean_pack(((protobuf_c_boolean *) array)[i], scratch);
    - buffer->append(buffer, len, scratch);
    - rv += len;
    - }
    - return count;
    - default:
    - PROTOBUF_C__ASSERT_NOT_REACHED();
    - }
    - return rv;
    -
    -#if !defined(WORDS_BIGENDIAN)
    -no_packing_needed:
    - buffer->append(buffer, rv, array);
    - return rv;
    -#endif
    -}
    -
    -static size_t
    -repeated_field_pack_to_buffer(const ProtobufCFieldDescriptor *field,
    - unsigned count, const void *member,
    - ProtobufCBuffer *buffer)
    -{
    - char *array = *(char * const *) member;
    -
    - if (count == 0)
    - return 0;
    - if (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED)) {
    - uint8_t scratch[MAX_UINT64_ENCODED_SIZE * 2];
    - size_t rv = tag_pack(field->id, scratch);
    - size_t payload_len = get_packed_payload_length(field, count, array);
    - size_t tmp;
    -
    - scratch[0] |= PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED;
    - rv += uint32_pack(payload_len, scratch + rv);
    - buffer->append(buffer, rv, scratch);
    - tmp = pack_buffer_packed_payload(field, count, array, buffer);
    - assert(tmp == payload_len);
    - return rv + payload_len;
    - } else {
    - size_t siz;
    - unsigned i;
    - /* CONSIDER: optimize this case a bit (by putting the loop inside the switch) */
    - unsigned rv = 0;
    -
    - siz = sizeof_elt_in_repeated_array(field->type);
    - for (i = 0; i < count; i++) {
    - rv += required_field_pack_to_buffer(field, array, buffer);
    - array = ((char*)array) + siz;
    - }
    - return rv;
    - }
    -}
    -
    -static size_t
    -unknown_field_pack_to_buffer(const ProtobufCMessageUnknownField *field,
    - ProtobufCBuffer *buffer)
    -{
    - uint8_t header[MAX_UINT64_ENCODED_SIZE];
    - size_t rv = tag_pack(field->tag, header);
    -
    - header[0] |= field->wire_type;
    - buffer->append(buffer, rv, header);
    - buffer->append(buffer, field->len, field->data);
    - return rv + field->len;
    -}
    -
    -/**@}*/
    -
    -size_t
    -protobuf_c_message_pack_to_buffer(const ProtobufCMessage *message,
    - ProtobufCBuffer *buffer)
    -{
    - unsigned i;
    - size_t rv = 0;
    -
    - ASSERT_IS_MESSAGE(message);
    - for (i = 0; i < message->descriptor->n_fields; i++) {
    - const ProtobufCFieldDescriptor *field =
    - message->descriptor->fields + i;
    - const void *member =
    - ((const char *) message) + field->offset;
    - const void *qmember =
    - ((const char *) message) + field->quantifier_offset;
    -
    - if (field->label == PROTOBUF_C_LABEL_REQUIRED) {
    - rv += required_field_pack_to_buffer(field, member, buffer);
    - } else if (field->label == PROTOBUF_C_LABEL_OPTIONAL) {
    - rv += optional_field_pack_to_buffer(
    - field,
    - qmember,
    - member,
    - buffer
    - );
    - } else {
    - rv += repeated_field_pack_to_buffer(
    - field,
    - *(const size_t *) qmember,
    - member,
    - buffer
    - );
    - }
    - }
    - for (i = 0; i < message->n_unknown_fields; i++)
    - rv += unknown_field_pack_to_buffer(&message->unknown_fields[i], buffer);
    -
    - return rv;
    -}
    -
    -/**
    - * \defgroup unpack unpacking implementation
    - *
    - * Routines mainly used by the unpacking functions.
    - *
    - * \ingroup internal
    - * @{
    - */
    -
    -static inline int
    -int_range_lookup(unsigned n_ranges, const ProtobufCIntRange *ranges, int value)
    -{
    - unsigned n;
    - unsigned start;
    -
    - if (n_ranges == 0)
    - return -1;
    - start = 0;
    - n = n_ranges;
    - while (n > 1) {
    - unsigned mid = start + n / 2;
    -
    - if (value < ranges[mid].start_value) {
    - n = mid - start;
    - } else if (value >= ranges[mid].start_value +
    - (int) (ranges[mid + 1].orig_index -
    - ranges[mid].orig_index))
    - {
    - unsigned new_start = mid + 1;
    - n = start + n - new_start;
    - start = new_start;
    - } else
    - return (value - ranges[mid].start_value) +
    - ranges[mid].orig_index;
    - }
    - if (n > 0) {
    - unsigned start_orig_index = ranges[start].orig_index;
    - unsigned range_size =
    - ranges[start + 1].orig_index - start_orig_index;
    -
    - if (ranges[start].start_value <= value &&
    - value < (int) (ranges[start].start_value + range_size))
    - {
    - return (value - ranges[start].start_value) +
    - start_orig_index;
    - }
    - }
    - return -1;
    -}
    -
    -static size_t
    -parse_tag_and_wiretype(size_t len,
    - const uint8_t *data,
    - uint32_t *tag_out,
    - ProtobufCWireType *wiretype_out)
    -{
    - unsigned max_rv = len > 5 ? 5 : len;
    - uint32_t tag = (data[0] & 0x7f) >> 3;
    - unsigned shift = 4;
    - unsigned rv;
    -
    - *wiretype_out = data[0] & 7;
    - if ((data[0] & 0x80) == 0) {
    - *tag_out = tag;
    - return 1;
    - }
    - for (rv = 1; rv < max_rv; rv++) {
    - if (data[rv] & 0x80) {
    - tag |= (data[rv] & 0x7f) << shift;
    - shift += 7;
    - } else {
    - tag |= data[rv] << shift;
    - *tag_out = tag;
    - return rv + 1;
    - }
    - }
    - return 0; /* error: bad header */
    -}
    -
    -/* sizeof(ScannedMember) must be <= (1<<BOUND_SIZEOF_SCANNED_MEMBER_LOG2) */
    -#define BOUND_SIZEOF_SCANNED_MEMBER_LOG2 5
    -typedef struct _ScannedMember ScannedMember;
    -/** Field as it's being read. */
    -struct _ScannedMember {
    - uint32_t tag; /**< Field tag. */
    - uint8_t wire_type; /**< Field type. */
    - uint8_t length_prefix_len; /**< Prefix length. */
    - const ProtobufCFieldDescriptor *field; /**< Field descriptor. */
    - size_t len; /**< Field length. */
    - const uint8_t *data; /**< Pointer to field data. */
    -};
    -
    -static inline uint32_t
    -scan_length_prefixed_data(size_t len, const uint8_t *data,
    - size_t *prefix_len_out)
    -{
    - unsigned hdr_max = len < 5 ? len : 5;
    - unsigned hdr_len;
    - uint32_t val = 0;
    - unsigned i;
    - unsigned shift = 0;
    -
    - for (i = 0; i < hdr_max; i++) {
    - val |= (data[i] & 0x7f) << shift;
    - shift += 7;
    - if ((data[i] & 0x80) == 0)
    - break;
    - }
    - if (i == hdr_max) {
    - PROTOBUF_C_UNPACK_ERROR("error parsing length for length-prefixed data");
    - return 0;
    - }
    - hdr_len = i + 1;
    - *prefix_len_out = hdr_len;
    - if (hdr_len + val > len) {
    - PROTOBUF_C_UNPACK_ERROR("data too short after length-prefix of %u", val);
    - return 0;
    - }
    - return hdr_len + val;
    -}
    -
    -static size_t
    -max_b128_numbers(size_t len, const uint8_t *data)
    -{
    - size_t rv = 0;
    - while (len--)
    - if ((*data++ & 0x80) == 0)
    - ++rv;
    - return rv;
    -}
    -
    -/**@}*/
    -
    -/**
    - * Merge earlier message into a latter message.
    - *
    - * For numeric types and strings, if the same value appears multiple
    - * times, the parser accepts the last value it sees. For embedded
    - * message fields, the parser merges multiple instances of the same
    - * field. That is, all singular scalar fields in the latter instance
    - * replace those in the former, singular embedded messages are merged,
    - * and repeated fields are concatenated.
    - *
    - * The earlier message should be freed after calling this function, as
    - * some of its fields may have been reused and changed to their default
    - * values during the merge.
    - */
    -static protobuf_c_boolean
    -merge_messages(ProtobufCMessage *earlier_msg,
    - ProtobufCMessage *latter_msg,
    - ProtobufCAllocator *allocator)
    -{
    - unsigned i;
    - const ProtobufCFieldDescriptor *fields =
    - earlier_msg->descriptor->fields;
    - for (i = 0; i < latter_msg->descriptor->n_fields; i++) {
    - if (fields[i].label == PROTOBUF_C_LABEL_REPEATED) {
    - size_t *n_earlier =
    - STRUCT_MEMBER_PTR(size_t, earlier_msg,
    - fields[i].quantifier_offset);
    - uint8_t **p_earlier =
    - STRUCT_MEMBER_PTR(uint8_t *, earlier_msg,
    - fields[i].offset);
    - size_t *n_latter =
    - STRUCT_MEMBER_PTR(size_t, latter_msg,
    - fields[i].quantifier_offset);
    - uint8_t **p_latter =
    - STRUCT_MEMBER_PTR(uint8_t *, latter_msg,
    - fields[i].offset);
    -
    - if (*n_earlier > 0) {
    - if (*n_latter > 0) {
    - /* Concatenate the repeated field */
    - size_t el_size =
    - sizeof_elt_in_repeated_array(fields[i].type);
    - uint8_t *new_field;
    -
    - new_field = do_alloc(allocator,
    - (*n_earlier + *n_latter) * el_size);
    - if (!new_field)
    - return FALSE;
    -
    - memcpy(new_field, *p_earlier,
    - *n_earlier * el_size);
    - memcpy(new_field +
    - *n_earlier * el_size,
    - *p_latter,
    - *n_latter * el_size);
    -
    - do_free(allocator, *p_latter);
    - do_free(allocator, *p_earlier);
    - *p_latter = new_field;
    - *n_latter = *n_earlier + *n_latter;
    - } else {
    - /* Zero copy the repeated field from the earlier message */
    - *n_latter = *n_earlier;
    - *p_latter = *p_earlier;
    - }
    - /* Make sure the field does not get double freed */
    - *n_earlier = 0;
    - *p_earlier = 0;
    - }
    - } else if (fields[i].type == PROTOBUF_C_TYPE_MESSAGE) {
    - ProtobufCMessage **em =
    - STRUCT_MEMBER_PTR(ProtobufCMessage *,
    - earlier_msg,
    - fields[i].offset);
    - ProtobufCMessage **lm =
    - STRUCT_MEMBER_PTR(ProtobufCMessage *,
    - latter_msg,
    - fields[i].offset);
    - if (*em != NULL) {
    - if (*lm != NULL) {
    - if (!merge_messages
    - (*em, *lm, allocator))
    - return FALSE;
    - } else {
    - /* Zero copy the optional message */
    - assert(fields[i].label ==
    - PROTOBUF_C_LABEL_OPTIONAL);
    - *lm = *em;
    - *em = NULL;
    - }
    - }
    - } else if (fields[i].label == PROTOBUF_C_LABEL_OPTIONAL) {
    - size_t el_size = 0;
    - protobuf_c_boolean need_to_merge = FALSE;
    - void *earlier_elem =
    - STRUCT_MEMBER_P(earlier_msg, fields[i].offset);
    - void *latter_elem =
    - STRUCT_MEMBER_P(latter_msg, fields[i].offset);
    - const void *def_val = fields[i].default_value;
    -
    - switch (fields[i].type) {
    - case PROTOBUF_C_TYPE_BYTES: {
    - uint8_t *e_data =
    - ((ProtobufCBinaryData *) earlier_elem)->data;
    - uint8_t *l_data =
    - ((ProtobufCBinaryData *) latter_elem)->data;
    - const ProtobufCBinaryData *d_bd =
    - (ProtobufCBinaryData *) def_val;
    -
    - el_size = sizeof(ProtobufCBinaryData);
    - need_to_merge =
    - (e_data != NULL &&
    - (d_bd != NULL &&
    - e_data != d_bd->data)) &&
    - (l_data == NULL ||
    - (d_bd != NULL &&
    - l_data == d_bd->data));
    - break;
    - }
    - case PROTOBUF_C_TYPE_STRING: {
    - char *e_str = *(char **) earlier_elem;
    - char *l_str = *(char **) latter_elem;
    - const char *d_str = def_val;
    -
    - el_size = sizeof(char *);
    - need_to_merge = e_str != d_str && l_str == d_str;
    - break;
    - }
    - default: {
    - el_size = sizeof_elt_in_repeated_array(fields[i].type);
    -
    - need_to_merge =
    - STRUCT_MEMBER(protobuf_c_boolean,
    - earlier_msg,
    - fields[i].quantifier_offset) &&
    - !STRUCT_MEMBER(protobuf_c_boolean,
    - latter_msg,
    - fields[i].quantifier_offset);
    - break;
    - }
    - }
    -
    - if (need_to_merge) {
    - memcpy(latter_elem, earlier_elem, el_size);
    - /*
    - * Reset the element from the old message to 0
    - * to make sure earlier message deallocation
    - * doesn't corrupt zero-copied data in the new
    - * message, earlier message will be freed after
    - * this function is called anyway
    - */
    - memset(earlier_elem, 0, el_size);
    -
    - if (fields[i].quantifier_offset != 0) {
    - /* Set the has field, if applicable */
    - STRUCT_MEMBER(protobuf_c_boolean,
    - latter_msg,
    - fields[i].
    - quantifier_offset) = TRUE;
    - STRUCT_MEMBER(protobuf_c_boolean,
    - earlier_msg,
    - fields[i].
    - quantifier_offset) = FALSE;
    - }
    - }
    - }
    - }
    - return TRUE;
    -}
    -
    -/**
    - * Count packed elements.
    - *
    - * Given a raw slab of packed-repeated values, determine the number of
    - * elements. This function detects certain kinds of errors but not
    - * others; the remaining error checking is done by
    - * parse_packed_repeated_member().
    - */
    -static protobuf_c_boolean
    -count_packed_elements(ProtobufCType type,
    - size_t len, const uint8_t *data, size_t *count_out)
    -{
    - switch (type) {
    - case PROTOBUF_C_TYPE_SFIXED32:
    - case PROTOBUF_C_TYPE_FIXED32:
    - case PROTOBUF_C_TYPE_FLOAT:
    - if (len % 4 != 0) {
    - PROTOBUF_C_UNPACK_ERROR("length must be a multiple of 4 for fixed-length 32-bit types");
    - return FALSE;
    - }
    - *count_out = len / 4;
    - return TRUE;
    - case PROTOBUF_C_TYPE_SFIXED64:
    - case PROTOBUF_C_TYPE_FIXED64:
    - case PROTOBUF_C_TYPE_DOUBLE:
    - if (len % 8 != 0) {
    - PROTOBUF_C_UNPACK_ERROR("length must be a multiple of 8 for fixed-length 64-bit types");
    - return FALSE;
    - }
    - *count_out = len / 8;
    - return TRUE;
    - case PROTOBUF_C_TYPE_INT32:
    - case PROTOBUF_C_TYPE_SINT32:
    - case PROTOBUF_C_TYPE_ENUM:
    - case PROTOBUF_C_TYPE_UINT32:
    - case PROTOBUF_C_TYPE_INT64:
    - case PROTOBUF_C_TYPE_SINT64:
    - case PROTOBUF_C_TYPE_UINT64:
    - *count_out = max_b128_numbers(len, data);
    - return TRUE;
    - case PROTOBUF_C_TYPE_BOOL:
    - *count_out = len;
    - return TRUE;
    - case PROTOBUF_C_TYPE_STRING:
    - case PROTOBUF_C_TYPE_BYTES:
    - case PROTOBUF_C_TYPE_MESSAGE:
    - default:
    - PROTOBUF_C_UNPACK_ERROR("bad protobuf-c type %u for packed-repeated", type);
    - return FALSE;
    - }
    -}
    -
    -static inline uint32_t
    -parse_uint32(unsigned len, const uint8_t *data)
    -{
    - uint32_t rv = data[0] & 0x7f;
    - if (len > 1) {
    - rv |= ((uint32_t) (data[1] & 0x7f) << 7);
    - if (len > 2) {
    - rv |= ((uint32_t) (data[2] & 0x7f) << 14);
    - if (len > 3) {
    - rv |= ((uint32_t) (data[3] & 0x7f) << 21);
    - if (len > 4)
    - rv |= ((uint32_t) (data[4]) << 28);
    - }
    - }
    - }
    - return rv;
    -}
    -
    -static inline uint32_t
    -parse_int32(unsigned len, const uint8_t *data)
    -{
    - return parse_uint32(len, data);
    -}
    -
    -static inline int32_t
    -unzigzag32(uint32_t v)
    -{
    - if (v & 1)
    - return -(v >> 1) - 1;
    - else
    - return v >> 1;
    -}
    -
    -static inline uint32_t
    -parse_fixed_uint32(const uint8_t *data)
    -{
    -#if !defined(WORDS_BIGENDIAN)
    - uint32_t t;
    - memcpy(&t, data, 4);
    - return t;
    -#else
    - return data[0] |
    - ((uint32_t) (data[1]) << 8) |
    - ((uint32_t) (data[2]) << 16) |
    - ((uint32_t) (data[3]) << 24);
    -#endif
    -}
    -
    -static uint64_t
    -parse_uint64(unsigned len, const uint8_t *data)
    -{
    - unsigned shift, i;
    - uint64_t rv;
    -
    - if (len < 5)
    - return parse_uint32(len, data);
    - rv = ((uint64_t) (data[0] & 0x7f)) |
    - ((uint64_t) (data[1] & 0x7f) << 7) |
    - ((uint64_t) (data[2] & 0x7f) << 14) |
    - ((uint64_t) (data[3] & 0x7f) << 21);
    - shift = 28;
    - for (i = 4; i < len; i++) {
    - rv |= (((uint64_t) (data[i] & 0x7f)) << shift);
    - shift += 7;
    - }
    - return rv;
    -}
    -
    -static inline int64_t
    -unzigzag64(uint64_t v)
    -{
    - if (v & 1)
    - return -(v >> 1) - 1;
    - else
    - return v >> 1;
    -}
    -
    -static inline uint64_t
    -parse_fixed_uint64(const uint8_t *data)
    -{
    -#if !defined(WORDS_BIGENDIAN)
    - uint64_t t;
    - memcpy(&t, data, 8);
    - return t;
    -#else
    - return (uint64_t) parse_fixed_uint32(data) |
    - (((uint64_t) parse_fixed_uint32(data + 4)) << 32);
    -#endif
    -}
    -
    -static protobuf_c_boolean
    -parse_boolean(unsigned len, const uint8_t *data)
    -{
    - unsigned i;
    - for (i = 0; i < len; i++)
    - if (data[i] & 0x7f)
    - return TRUE;
    - return FALSE;
    -}
    -
    -static protobuf_c_boolean
    -parse_required_member(ScannedMember *scanned_member,
    - void *member,
    - ProtobufCAllocator *allocator,
    - protobuf_c_boolean maybe_clear)
    -{
    - unsigned len = scanned_member->len;
    - const uint8_t *data = scanned_member->data;
    - ProtobufCWireType wire_type = scanned_member->wire_type;
    -
    - switch (scanned_member->field->type) {
    - case PROTOBUF_C_TYPE_INT32:
    - if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT)
    - return FALSE;
    - *(uint32_t *) member = parse_int32(len, data);
    - return TRUE;
    - case PROTOBUF_C_TYPE_UINT32:
    - if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT)
    - return FALSE;
    - *(uint32_t *) member = parse_uint32(len, data);
    - return TRUE;
    - case PROTOBUF_C_TYPE_SINT32:
    - if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT)
    - return FALSE;
    - *(int32_t *) member = unzigzag32(parse_uint32(len, data));
    - return TRUE;
    - case PROTOBUF_C_TYPE_SFIXED32:
    - case PROTOBUF_C_TYPE_FIXED32:
    - case PROTOBUF_C_TYPE_FLOAT:
    - if (wire_type != PROTOBUF_C_WIRE_TYPE_32BIT)
    - return FALSE;
    - *(uint32_t *) member = parse_fixed_uint32(data);
    - return TRUE;
    - case PROTOBUF_C_TYPE_INT64:
    - case PROTOBUF_C_TYPE_UINT64:
    - if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT)
    - return FALSE;
    - *(uint64_t *) member = parse_uint64(len, data);
    - return TRUE;
    - case PROTOBUF_C_TYPE_SINT64:
    - if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT)
    - return FALSE;
    - *(int64_t *) member = unzigzag64(parse_uint64(len, data));
    - return TRUE;
    - case PROTOBUF_C_TYPE_SFIXED64:
    - case PROTOBUF_C_TYPE_FIXED64:
    - case PROTOBUF_C_TYPE_DOUBLE:
    - if (wire_type != PROTOBUF_C_WIRE_TYPE_64BIT)
    - return FALSE;
    - *(uint64_t *) member = parse_fixed_uint64(data);
    - return TRUE;
    - case PROTOBUF_C_TYPE_BOOL:
    - *(protobuf_c_boolean *) member = parse_boolean(len, data);
    - return TRUE;
    - case PROTOBUF_C_TYPE_ENUM:
    - if (wire_type != PROTOBUF_C_WIRE_TYPE_VARINT)
    - return FALSE;
    - *(uint32_t *) member = parse_uint32(len, data);
    - return TRUE;
    - case PROTOBUF_C_TYPE_STRING: {
    - char **pstr = member;
    - unsigned pref_len = scanned_member->length_prefix_len;
    -
    - if (wire_type != PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED)
    - return FALSE;
    -
    - if (maybe_clear && *pstr != NULL) {
    - const char *def = scanned_member->field->default_value;
    - if (*pstr != NULL && *pstr != def)
    - do_free(allocator, *pstr);
    - }
    - *pstr = do_alloc(allocator, len - pref_len + 1);
    - if (*pstr == NULL)
    - return FALSE;
    - memcpy(*pstr, data + pref_len, len - pref_len);
    - (*pstr)[len - pref_len] = 0;
    - return TRUE;
    - }
    - case PROTOBUF_C_TYPE_BYTES: {
    - ProtobufCBinaryData *bd = member;
    - const ProtobufCBinaryData *def_bd;
    - unsigned pref_len = scanned_member->length_prefix_len;
    -
    - if (wire_type != PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED)
    - return FALSE;
    -
    - def_bd = scanned_member->field->default_value;
    - if (maybe_clear &&
    - bd->data != NULL &&
    - (def_bd == NULL || bd->data != def_bd->data))
    - {
    - do_free(allocator, bd->data);
    - }
    - if (len - pref_len > 0) {
    - bd->data = do_alloc(allocator, len - pref_len);
    - if (bd->data == NULL)
    - return FALSE;
    - memcpy(bd->data, data + pref_len, len - pref_len);
    - } else {
    - bd->data = NULL;
    - }
    - bd->len = len - pref_len;
    - return TRUE;
    - }
    - case PROTOBUF_C_TYPE_MESSAGE: {
    - ProtobufCMessage **pmessage = member;
    - ProtobufCMessage *subm;
    - const ProtobufCMessage *def_mess;
    - protobuf_c_boolean merge_successful = TRUE;
    - unsigned pref_len = scanned_member->length_prefix_len;
    -
    - if (wire_type != PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED)
    - return FALSE;
    -
    - def_mess = scanned_member->field->default_value;
    - subm = protobuf_c_message_unpack(scanned_member->field->descriptor,
    - allocator,
    - len - pref_len,
    - data + pref_len);
    -
    - if (maybe_clear &&
    - *pmessage != NULL &&
    - *pmessage != def_mess)
    - {
    - if (subm != NULL)
    - merge_successful = merge_messages(*pmessage, subm, allocator);
    - /* Delete the previous message */
    - protobuf_c_message_free_unpacked(*pmessage, allocator);
    - }
    - *pmessage = subm;
    - if (subm == NULL || !merge_successful)
    - return FALSE;
    - return TRUE;
    - }
    - }
    - return FALSE;
    -}
    -
    -static protobuf_c_boolean
    -parse_optional_member(ScannedMember *scanned_member,
    - void *member,
    - ProtobufCMessage *message,
    - ProtobufCAllocator *allocator)
    -{
    - if (!parse_required_member(scanned_member, member, allocator, TRUE))
    - return FALSE;
    - if (scanned_member->field->quantifier_offset != 0)
    - STRUCT_MEMBER(protobuf_c_boolean,
    - message,
    - scanned_member->field->quantifier_offset) = TRUE;
    - return TRUE;
    -}
    -
    -static protobuf_c_boolean
    -parse_repeated_member(ScannedMember *scanned_member,
    - void *member,
    - ProtobufCMessage *message,
    - ProtobufCAllocator *allocator)
    -{
    - const ProtobufCFieldDescriptor *field = scanned_member->field;
    - size_t *p_n = STRUCT_MEMBER_PTR(size_t, message, field->quantifier_offset);
    - size_t siz = sizeof_elt_in_repeated_array(field->type);
    - char *array = *(char **) member;
    -
    - if (!parse_required_member(scanned_member, array + siz * (*p_n),
    - allocator, FALSE))
    - {
    - return FALSE;
    - }
    - *p_n += 1;
    - return TRUE;
    -}
    -
    -static unsigned
    -scan_varint(unsigned len, const uint8_t *data)
    -{
    - unsigned i;
    - if (len > 10)
    - len = 10;
    - for (i = 0; i < len; i++)
    - if ((data[i] & 0x80) == 0)
    - break;
    - if (i == len)
    - return 0;
    - return i + 1;
    -}
    -
    -static protobuf_c_boolean
    -parse_packed_repeated_member(ScannedMember *scanned_member,
    - void *member,
    - ProtobufCMessage *message)
    -{
    - const ProtobufCFieldDescriptor *field = scanned_member->field;
    - size_t *p_n = STRUCT_MEMBER_PTR(size_t, message, field->quantifier_offset);
    - size_t siz = sizeof_elt_in_repeated_array(field->type);
    - void *array = *(char **) member + siz * (*p_n);
    - const uint8_t *at = scanned_member->data + scanned_member->length_prefix_len;
    - size_t rem = scanned_member->len - scanned_member->length_prefix_len;
    - size_t count = 0;
    - unsigned i;
    -
    - switch (field->type) {
    - case PROTOBUF_C_TYPE_SFIXED32:
    - case PROTOBUF_C_TYPE_FIXED32:
    - case PROTOBUF_C_TYPE_FLOAT:
    - count = (scanned_member->len - scanned_member->length_prefix_len) / 4;
    -#if !defined(WORDS_BIGENDIAN)
    - goto no_unpacking_needed;
    -#else
    - for (i = 0; i < count; i++) {
    - ((uint32_t *) array)[i] = parse_fixed_uint32(at);
    - at += 4;
    - }
    - break;
    -#endif
    - case PROTOBUF_C_TYPE_SFIXED64:
    - case PROTOBUF_C_TYPE_FIXED64:
    - case PROTOBUF_C_TYPE_DOUBLE:
    - count = (scanned_member->len - scanned_member->length_prefix_len) / 8;
    -#if !defined(WORDS_BIGENDIAN)
    - goto no_unpacking_needed;
    -#else
    - for (i = 0; i < count; i++) {
    - ((uint64_t *) array)[i] = parse_fixed_uint64(at);
    - at += 8;
    - }
    - break;
    -#endif
    - case PROTOBUF_C_TYPE_INT32:
    - while (rem > 0) {
    - unsigned s = scan_varint(rem, at);
    - if (s == 0) {
    - PROTOBUF_C_UNPACK_ERROR("bad packed-repeated int32 value");
    - return FALSE;
    - }
    - ((int32_t *) array)[count++] = parse_int32(s, at);
    - at += s;
    - rem -= s;
    - }
    - break;
    - case PROTOBUF_C_TYPE_SINT32:
    - while (rem > 0) {
    - unsigned s = scan_varint(rem, at);
    - if (s == 0) {
    - PROTOBUF_C_UNPACK_ERROR("bad packed-repeated sint32 value");
    - return FALSE;
    - }
    - ((int32_t *) array)[count++] = unzigzag32(parse_uint32(s, at));
    - at += s;
    - rem -= s;
    - }
    - break;
    - case PROTOBUF_C_TYPE_ENUM:
    - case PROTOBUF_C_TYPE_UINT32:
    - while (rem > 0) {
    - unsigned s = scan_varint(rem, at);
    - if (s == 0) {
    - PROTOBUF_C_UNPACK_ERROR("bad packed-repeated enum or uint32 value");
    - return FALSE;
    - }
    - ((uint32_t *) array)[count++] = parse_uint32(s, at);
    - at += s;
    - rem -= s;
    - }
    - break;
    -
    - case PROTOBUF_C_TYPE_SINT64:
    - while (rem > 0) {
    - unsigned s = scan_varint(rem, at);
    - if (s == 0) {
    - PROTOBUF_C_UNPACK_ERROR("bad packed-repeated sint64 value");
    - return FALSE;
    - }
    - ((int64_t *) array)[count++] = unzigzag64(parse_uint64(s, at));
    - at += s;
    - rem -= s;
    - }
    - break;
    - case PROTOBUF_C_TYPE_INT64:
    - case PROTOBUF_C_TYPE_UINT64:
    - while (rem > 0) {
    - unsigned s = scan_varint(rem, at);
    - if (s == 0) {
    - PROTOBUF_C_UNPACK_ERROR("bad packed-repeated int64/uint64 value");
    - return FALSE;
    - }
    - ((int64_t *) array)[count++] = parse_uint64(s, at);
    - at += s;
    - rem -= s;
    - }
    - break;
    - case PROTOBUF_C_TYPE_BOOL:
    - count = rem;
    - for (i = 0; i < count; i++) {
    - if (at[i] > 1) {
    - PROTOBUF_C_UNPACK_ERROR("bad packed-repeated boolean value");
    - return FALSE;
    - }
    - ((protobuf_c_boolean *) array)[i] = at[i];
    - }
    - break;
    - default:
    - PROTOBUF_C__ASSERT_NOT_REACHED();
    - }
    - *p_n += count;
    - return TRUE;
    -
    -#if !defined(WORDS_BIGENDIAN)
    -no_unpacking_needed:
    - memcpy(array, at, count * siz);
    - *p_n += count;
    - return TRUE;
    -#endif
    -}
    -
    -static protobuf_c_boolean
    -is_packable_type(ProtobufCType type)
    -{
    - return
    - type != PROTOBUF_C_TYPE_STRING &&
    - type != PROTOBUF_C_TYPE_BYTES &&
    - type != PROTOBUF_C_TYPE_MESSAGE;
    -}
    -
    -static protobuf_c_boolean
    -parse_member(ScannedMember *scanned_member,
    - ProtobufCMessage *message,
    - ProtobufCAllocator *allocator)
    -{
    - const ProtobufCFieldDescriptor *field = scanned_member->field;
    - void *member;
    -
    - if (field == NULL) {
    - ProtobufCMessageUnknownField *ufield =
    - message->unknown_fields +
    - (message->n_unknown_fields++);
    - ufield->tag = scanned_member->tag;
    - ufield->wire_type = scanned_member->wire_type;
    - ufield->len = scanned_member->len;
    - ufield->data = do_alloc(allocator, scanned_member->len);
    - if (ufield->data == NULL)
    - return FALSE;
    - memcpy(ufield->data, scanned_member->data, ufield->len);
    - return TRUE;
    - }
    - member = (char *) message + field->offset;
    - switch (field->label) {
    - case PROTOBUF_C_LABEL_REQUIRED:
    - return parse_required_member(scanned_member, member,
    - allocator, TRUE);
    - case PROTOBUF_C_LABEL_OPTIONAL:
    - return parse_optional_member(scanned_member, member,
    - message, allocator);
    - case PROTOBUF_C_LABEL_REPEATED:
    - if (scanned_member->wire_type ==
    - PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED &&
    - (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED) ||
    - is_packable_type(field->type)))
    - {
    - return parse_packed_repeated_member(scanned_member,
    - member, message);
    - } else {
    - return parse_repeated_member(scanned_member,
    - member, message,
    - allocator);
    - }
    - }
    - PROTOBUF_C__ASSERT_NOT_REACHED();
    - return 0;
    -}
    -
    -/**
    - * Initialise messages generated by old code.
    - *
    - * This function is used if desc->message_init == NULL (which occurs
    - * for old code, and which would be useful to support allocating
    - * descriptors dynamically).
    - */
    -static void
    -message_init_generic(const ProtobufCMessageDescriptor *desc,
    - ProtobufCMessage *message)
    -{
    - unsigned i;
    -
    - memset(message, 0, desc->sizeof_message);
    - message->descriptor = desc;
    - for (i = 0; i < desc->n_fields; i++) {
    - if (desc->fields[i].default_value != NULL &&
    - desc->fields[i].label != PROTOBUF_C_LABEL_REPEATED)
    - {
    - void *field =
    - STRUCT_MEMBER_P(message, desc->fields[i].offset);
    - const void *dv = desc->fields[i].default_value;
    -
    - switch (desc->fields[i].type) {
    - case PROTOBUF_C_TYPE_INT32:
    - case PROTOBUF_C_TYPE_SINT32:
    - case PROTOBUF_C_TYPE_SFIXED32:
    - case PROTOBUF_C_TYPE_UINT32:
    - case PROTOBUF_C_TYPE_FIXED32:
    - case PROTOBUF_C_TYPE_FLOAT:
    - case PROTOBUF_C_TYPE_ENUM:
    - memcpy(field, dv, 4);
    - break;
    - case PROTOBUF_C_TYPE_INT64:
    - case PROTOBUF_C_TYPE_SINT64:
    - case PROTOBUF_C_TYPE_SFIXED64:
    - case PROTOBUF_C_TYPE_UINT64:
    - case PROTOBUF_C_TYPE_FIXED64:
    - case PROTOBUF_C_TYPE_DOUBLE:
    - memcpy(field, dv, 8);
    - break;
    - case PROTOBUF_C_TYPE_BOOL:
    - memcpy(field, dv, sizeof(protobuf_c_boolean));
    - break;
    - case PROTOBUF_C_TYPE_BYTES:
    - memcpy(field, dv, sizeof(ProtobufCBinaryData));
    - break;
    -
    - case PROTOBUF_C_TYPE_STRING:
    - case PROTOBUF_C_TYPE_MESSAGE:
    - /*
    - * The next line essentially implements a cast
    - * from const, which is totally unavoidable.
    - */
    - *(const void **) field = dv;
    - break;
    - }
    - }
    - }
    -}
    -
    -/**@}*/
    -
    -/*
    - * ScannedMember slabs (an unpacking implementation detail). Before doing real
    - * unpacking, we first scan through the elements to see how many there are (for
    - * repeated fields), and which field to use (for non-repeated fields given
    - * twice).
    - *
    - * In order to avoid allocations for small messages, we keep a stack-allocated
    - * slab of ScannedMembers of size FIRST_SCANNED_MEMBER_SLAB_SIZE (16). After we
    - * fill that up, we allocate each slab twice as large as the previous one.
    - */
    -#define FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2 4
    -
    -/*
    - * The number of slabs, including the stack-allocated ones; choose the number so
    - * that we would overflow if we needed a slab larger than provided.
    - */
    -#define MAX_SCANNED_MEMBER_SLAB \
    - (sizeof(unsigned int)*8 - 1 \
    - - BOUND_SIZEOF_SCANNED_MEMBER_LOG2 \
    - - FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2)
    -
    -#define REQUIRED_FIELD_BITMAP_SET(index) \
    - (required_fields_bitmap[(index)/8] |= (1<<((index)%8)))
    -
    -#define REQUIRED_FIELD_BITMAP_IS_SET(index) \
    - (required_fields_bitmap[(index)/8] & (1<<((index)%8)))
    -
    -ProtobufCMessage *
    -protobuf_c_message_unpack(const ProtobufCMessageDescriptor *desc,
    - ProtobufCAllocator *allocator,
    - size_t len, const uint8_t *data)
    -{
    - ProtobufCMessage *rv;
    - size_t rem = len;
    - const uint8_t *at = data;
    - const ProtobufCFieldDescriptor *last_field = desc->fields + 0;
    - ScannedMember first_member_slab[1 <<
    - FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2];
    -
    - /*
    - * scanned_member_slabs[i] is an array of arrays of ScannedMember.
    - * The first slab (scanned_member_slabs[0] is just a pointer to
    - * first_member_slab), above. All subsequent slabs will be allocated
    - * using the allocator.
    - */
    - ScannedMember *scanned_member_slabs[MAX_SCANNED_MEMBER_SLAB + 1];
    - unsigned which_slab = 0; /* the slab we are currently populating */
    - unsigned in_slab_index = 0; /* number of members in the slab */
    - size_t n_unknown = 0;
    - unsigned f;
    - unsigned j;
    - unsigned i_slab;
    - unsigned last_field_index = 0;
    - unsigned required_fields_bitmap_len;
    - unsigned char required_fields_bitmap_stack[16];
    - unsigned char *required_fields_bitmap = required_fields_bitmap_stack;
    - protobuf_c_boolean required_fields_bitmap_alloced = FALSE;
    -
    - ASSERT_IS_MESSAGE_DESCRIPTOR(desc);
    -
    - if (allocator == NULL)
    - allocator = &protobuf_c__allocator;
    -
    - rv = do_alloc(allocator, desc->sizeof_message);
    - if (!rv)
    - return (NULL);
    - scanned_member_slabs[0] = first_member_slab;
    -
    - required_fields_bitmap_len = (desc->n_fields + 7) / 8;
    - if (required_fields_bitmap_len > sizeof(required_fields_bitmap_stack)) {
    - required_fields_bitmap = do_alloc(allocator, required_fields_bitmap_len);
    - if (!required_fields_bitmap) {
    - do_free(allocator, rv);
    - return (NULL);
    - }
    - required_fields_bitmap_alloced = TRUE;
    - }
    - memset(required_fields_bitmap, 0, required_fields_bitmap_len);
    -
    - /*
    - * Generated code always defines "message_init". However, we provide a
    - * fallback for (1) users of old protobuf-c generated-code that do not
    - * provide the function, and (2) descriptors constructed from some other
    - * source (most likely, direct construction from the .proto file).
    - */
    - if (desc->message_init != NULL)
    - protobuf_c_message_init(desc, rv);
    - else
    - message_init_generic(desc, rv);
    -
    - while (rem > 0) {
    - uint32_t tag;
    - ProtobufCWireType wire_type;
    - size_t used = parse_tag_and_wiretype(rem, at, &tag, &wire_type);
    - const ProtobufCFieldDescriptor *field;
    - ScannedMember tmp;
    -
    - memset(&tmp, 0, sizeof(ScannedMember));
    -
    - if (used == 0) {
    - PROTOBUF_C_UNPACK_ERROR("error parsing tag/wiretype at offset %u",
    - (unsigned) (at - data));
    - goto error_cleanup_during_scan;
    - }
    - /*
    - * \todo Consider optimizing for field[1].id == tag, if field[1]
    - * exists!
    - */
    - if (last_field == NULL || last_field->id != tag) {
    - /* lookup field */
    - int field_index =
    - int_range_lookup(desc->n_field_ranges,
    - desc->field_ranges,
    - tag);
    - if (field_index < 0) {
    - field = NULL;
    - n_unknown++;
    - } else {
    - field = desc->fields + field_index;
    - last_field = field;
    - last_field_index = field_index;
    - }
    - } else {
    - field = last_field;
    - }
    -
    - if (field != NULL && field->label == PROTOBUF_C_LABEL_REQUIRED)
    - REQUIRED_FIELD_BITMAP_SET(last_field_index);
    -
    - at += used;
    - rem -= used;
    - tmp.tag = tag;
    - tmp.wire_type = wire_type;
    - tmp.field = field;
    - tmp.data = at;
    - tmp.length_prefix_len = 0;
    -
    - switch (wire_type) {
    - case PROTOBUF_C_WIRE_TYPE_VARINT: {
    - unsigned max_len = rem < 10 ? rem : 10;
    - unsigned i;
    -
    - for (i = 0; i < max_len; i++)
    - if ((at[i] & 0x80) == 0)
    - break;
    - if (i == max_len) {
    - PROTOBUF_C_UNPACK_ERROR("unterminated varint at offset %u",
    - (unsigned) (at - data));
    - goto error_cleanup_during_scan;
    - }
    - tmp.len = i + 1;
    - break;
    - }
    - case PROTOBUF_C_WIRE_TYPE_64BIT:
    - if (rem < 8) {
    - PROTOBUF_C_UNPACK_ERROR("too short after 64bit wiretype at offset %u",
    - (unsigned) (at - data));
    - goto error_cleanup_during_scan;
    - }
    - tmp.len = 8;
    - break;
    - case PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED: {
    - size_t pref_len;
    -
    - tmp.len = scan_length_prefixed_data(rem, at, &pref_len);
    - if (tmp.len == 0) {
    - /* NOTE: scan_length_prefixed_data calls UNPACK_ERROR */
    - goto error_cleanup_during_scan;
    - }
    - tmp.length_prefix_len = pref_len;
    - break;
    - }
    - case PROTOBUF_C_WIRE_TYPE_32BIT:
    - if (rem < 4) {
    - PROTOBUF_C_UNPACK_ERROR("too short after 32bit wiretype at offset %u",
    - (unsigned) (at - data));
    - goto error_cleanup_during_scan;
    - }
    - tmp.len = 4;
    - break;
    - default:
    - PROTOBUF_C_UNPACK_ERROR("unsupported tag %u at offset %u",
    - wire_type, (unsigned) (at - data));
    - goto error_cleanup_during_scan;
    - }
    -
    - if (in_slab_index == (1U <<
    - (which_slab + FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2)))
    - {
    - size_t size;
    -
    - in_slab_index = 0;
    - if (which_slab == MAX_SCANNED_MEMBER_SLAB) {
    - PROTOBUF_C_UNPACK_ERROR("too many fields");
    - goto error_cleanup_during_scan;
    - }
    - which_slab++;
    - size = sizeof(ScannedMember)
    - << (which_slab + FIRST_SCANNED_MEMBER_SLAB_SIZE_LOG2);
    - scanned_member_slabs[which_slab] = do_alloc(allocator, size);
    - if (scanned_member_slabs[which_slab] == NULL)
    - goto error_cleanup_during_scan;
    - }
    - scanned_member_slabs[which_slab][in_slab_index++] = tmp;
    -
    - if (field != NULL && field->label == PROTOBUF_C_LABEL_REPEATED) {
    - size_t *n = STRUCT_MEMBER_PTR(size_t, rv,
    - field->quantifier_offset);
    - if (wire_type == PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED &&
    - (0 != (field->flags & PROTOBUF_C_FIELD_FLAG_PACKED) ||
    - is_packable_type(field->type)))
    - {
    - size_t count;
    - if (!count_packed_elements(field->type,
    - tmp.len -
    - tmp.length_prefix_len,
    - tmp.data +
    - tmp.length_prefix_len,
    - &count))
    - {
    - PROTOBUF_C_UNPACK_ERROR("counting packed elements");
    - goto error_cleanup_during_scan;
    - }
    - *n += count;
    - } else {
    - *n += 1;
    - }
    - }
    -
    - at += tmp.len;
    - rem -= tmp.len;
    - }
    -
    - /* allocate space for repeated fields, also check that all required fields have been set */
    - for (f = 0; f < desc->n_fields; f++) {
    - const ProtobufCFieldDescriptor *field = desc->fields + f;
    - if (field->label == PROTOBUF_C_LABEL_REPEATED) {
    - size_t siz =
    - sizeof_elt_in_repeated_array(field->type);
    - size_t *n_ptr =
    - STRUCT_MEMBER_PTR(size_t, rv,
    - field->quantifier_offset);
    - if (*n_ptr != 0) {
    - unsigned n = *n_ptr;
    - void *a;
    - *n_ptr = 0;
    - assert(rv->descriptor != NULL);
    -#define CLEAR_REMAINING_N_PTRS() \
    - for(f++;f < desc->n_fields; f++) \
    - { \
    - field = desc->fields + f; \
    - if (field->label == PROTOBUF_C_LABEL_REPEATED) \
    - STRUCT_MEMBER (size_t, rv, field->quantifier_offset) = 0; \
    - }
    - a = do_alloc(allocator, siz * n);
    - if (!a) {
    - CLEAR_REMAINING_N_PTRS();
    - goto error_cleanup;
    - }
    - STRUCT_MEMBER(void *, rv, field->offset) = a;
    - }
    - } else if (field->label == PROTOBUF_C_LABEL_REQUIRED) {
    - if (field->default_value == NULL &&
    - !REQUIRED_FIELD_BITMAP_IS_SET(f))
    - {
    - CLEAR_REMAINING_N_PTRS();
    - PROTOBUF_C_UNPACK_ERROR("message '%s': missing required field '%s'",
    - desc->name, field->name);
    - goto error_cleanup;
    - }
    - }
    - }
    -#undef CLEAR_REMAINING_N_PTRS
    -
    - /* allocate space for unknown fields */
    - if (n_unknown) {
    - rv->unknown_fields = do_alloc(allocator,
    - n_unknown * sizeof(ProtobufCMessageUnknownField));
    - if (rv->unknown_fields == NULL)
    - goto error_cleanup;
    - }
    -
    - /* do real parsing */
    - for (i_slab = 0; i_slab <= which_slab; i_slab++) {
    - unsigned max = (i_slab == which_slab) ?
    - in_slab_index : (1U << (i_slab + 4));
    - ScannedMember *slab = scanned_member_slabs[i_slab];
    - unsigned j;
    -
    - for (j = 0; j < max; j++) {
    - if (!parse_member(slab + j, rv, allocator)) {
    - PROTOBUF_C_UNPACK_ERROR("error parsing member %s of %s",
    - slab->field ? slab->field->name : "*unknown-field*",
    - desc->name);
    - goto error_cleanup;
    - }
    - }
    - }
    -
    - /* cleanup */
    - for (j = 1; j <= which_slab; j++)
    - do_free(allocator, scanned_member_slabs[j]);
    - if (required_fields_bitmap_alloced)
    - do_free(allocator, required_fields_bitmap);
    - return rv;
    -
    -error_cleanup:
    - protobuf_c_message_free_unpacked(rv, allocator);
    - for (j = 1; j <= which_slab; j++)
    - do_free(allocator, scanned_member_slabs[j]);
    - if (required_fields_bitmap_alloced)
    - do_free(allocator, required_fields_bitmap);
    - return NULL;
    -
    -error_cleanup_during_scan:
    - do_free(allocator, rv);
    - for (j = 1; j <= which_slab; j++)
    - do_free(allocator, scanned_member_slabs[j]);
    - if (required_fields_bitmap_alloced)
    - do_free(allocator, required_fields_bitmap);
    - return NULL;
    -}
    -
    -void
    -protobuf_c_message_free_unpacked(ProtobufCMessage *message,
    - ProtobufCAllocator *allocator)
    -{
    - const ProtobufCMessageDescriptor *desc = message->descriptor;
    - unsigned f;
    -
    - ASSERT_IS_MESSAGE(message);
    - if (allocator == NULL)
    - allocator = &protobuf_c__allocator;
    - message->descriptor = NULL;
    - for (f = 0; f < desc->n_fields; f++) {
    - if (desc->fields[f].label == PROTOBUF_C_LABEL_REPEATED) {
    - size_t n = STRUCT_MEMBER(size_t,
    - message,
    - desc->fields[f].quantifier_offset);
    - void *arr = STRUCT_MEMBER(void *,
    - message,
    - desc->fields[f].offset);
    -
    - if (desc->fields[f].type == PROTOBUF_C_TYPE_STRING) {
    - unsigned i;
    - for (i = 0; i < n; i++)
    - do_free(allocator, ((char **) arr)[i]);
    - } else if (desc->fields[f].type == PROTOBUF_C_TYPE_BYTES) {
    - unsigned i;
    - for (i = 0; i < n; i++)
    - do_free(allocator, ((ProtobufCBinaryData *) arr)[i].data);
    - } else if (desc->fields[f].type == PROTOBUF_C_TYPE_MESSAGE) {
    - unsigned i;
    - for (i = 0; i < n; i++)
    - protobuf_c_message_free_unpacked(
    - ((ProtobufCMessage **) arr)[i],
    - allocator
    - );
    - }
    - if (arr != NULL)
    - do_free(allocator, arr);
    - } else if (desc->fields[f].type == PROTOBUF_C_TYPE_STRING) {
    - char *str = STRUCT_MEMBER(char *, message,
    - desc->fields[f].offset);
    -
    - if (str && str != desc->fields[f].default_value)
    - do_free(allocator, str);
    - } else if (desc->fields[f].type == PROTOBUF_C_TYPE_BYTES) {
    - void *data = STRUCT_MEMBER(ProtobufCBinaryData, message,
    - desc->fields[f].offset).data;
    - const ProtobufCBinaryData *default_bd;
    -
    - default_bd = desc->fields[f].default_value;
    - if (data != NULL &&
    - (default_bd == NULL ||
    - default_bd->data != data))
    - {
    - do_free(allocator, data);
    - }
    - } else if (desc->fields[f].type == PROTOBUF_C_TYPE_MESSAGE) {
    - ProtobufCMessage *sm;
    -
    - sm = STRUCT_MEMBER(ProtobufCMessage *, message,
    - desc->fields[f].offset);
    - if (sm && sm != desc->fields[f].default_value)
    - protobuf_c_message_free_unpacked(sm, allocator);
    - }
    - }
    -
    - for (f = 0; f < message->n_unknown_fields; f++)
    - do_free(allocator, message->unknown_fields[f].data);
    - if (message->unknown_fields != NULL)
    - do_free(allocator, message->unknown_fields);
    -
    - do_free(allocator, message);
    -}
    -
    -void
    -protobuf_c_message_init(const ProtobufCMessageDescriptor * descriptor,
    - void *message)
    -{
    - descriptor->message_init((ProtobufCMessage *) (message));
    -}
    -
    -protobuf_c_boolean
    -protobuf_c_message_check(const ProtobufCMessage *message)
    -{
    - unsigned i;
    -
    - if (!message ||
    - !message->descriptor ||
    - message->descriptor->magic != PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC)
    - {
    - return FALSE;
    - }
    -
    - for (i = 0; i < message->descriptor->n_fields; i++) {
    - const ProtobufCFieldDescriptor *f = message->descriptor->fields + i;
    - ProtobufCType type = f->type;
    - ProtobufCLabel label = f->label;
    - void *field = STRUCT_MEMBER_P (message, f->offset);
    -
    - if (label == PROTOBUF_C_LABEL_REPEATED) {
    - size_t *quantity = STRUCT_MEMBER_P (message, f->quantifier_offset);
    -
    - if (*quantity > 0 && *(void **) field == NULL) {
    - return FALSE;
    - }
    -
    - if (type == PROTOBUF_C_TYPE_MESSAGE) {
    - ProtobufCMessage **submessage = *(ProtobufCMessage ***) field;
    - unsigned j;
    - for (j = 0; j < *quantity; j++) {
    - if (!protobuf_c_message_check(submessage[j]))
    - return FALSE;
    - }
    - } else if (type == PROTOBUF_C_TYPE_STRING) {
    - char **string = *(char ***) field;
    - unsigned j;
    - for (j = 0; j < *quantity; j++) {
    - if (!string[j])
    - return FALSE;
    - }
    - } else if (type == PROTOBUF_C_TYPE_BYTES) {
    - ProtobufCBinaryData *bd = *(ProtobufCBinaryData **) field;
    - unsigned j;
    - for (j = 0; j < *quantity; j++) {
    - if (bd[j].len > 0 && bd[j].data == NULL)
    - return FALSE;
    - }
    - }
    -
    - } else { /* PROTOBUF_C_LABEL_REQUIRED or PROTOBUF_C_LABEL_OPTIONAL */
    -
    - if (type == PROTOBUF_C_TYPE_MESSAGE) {
    - ProtobufCMessage *submessage = *(ProtobufCMessage **) field;
    - if (label == PROTOBUF_C_LABEL_REQUIRED || submessage != NULL) {
    - if (!protobuf_c_message_check(submessage))
    - return FALSE;
    - }
    - } else if (type == PROTOBUF_C_TYPE_STRING) {
    - char *string = *(char **) field;
    - if (label == PROTOBUF_C_LABEL_REQUIRED && string == NULL)
    - return FALSE;
    - } else if (type == PROTOBUF_C_TYPE_BYTES) {
    - protobuf_c_boolean *has = STRUCT_MEMBER_P (message, f->quantifier_offset);
    - ProtobufCBinaryData *bd = field;
    - if (label == PROTOBUF_C_LABEL_REQUIRED || *has == TRUE) {
    - if (bd->len > 0 && bd->data == NULL)
    - return FALSE;
    - }
    - }
    - }
    - }
    -
    - return TRUE;
    -}
    -
    -/* === services === */
    -
    -typedef void (*GenericHandler) (void *service,
    - const ProtobufCMessage *input,
    - ProtobufCClosure closure,
    - void *closure_data);
    -void
    -protobuf_c_service_invoke_internal(ProtobufCService *service,
    - unsigned method_index,
    - const ProtobufCMessage *input,
    - ProtobufCClosure closure,
    - void *closure_data)
    -{
    - GenericHandler *handlers;
    - GenericHandler handler;
    -
    - /*
    - * Verify that method_index is within range. If this fails, you are
    - * likely invoking a newly added method on an old service. (Although
    - * other memory corruption bugs can cause this assertion too.)
    - */
    - assert(method_index < service->descriptor->n_methods);
    -
    - /*
    - * Get the array of virtual methods (which are enumerated by the
    - * generated code).
    - */
    - handlers = (GenericHandler *) (service + 1);
    -
    - /*
    - * Get our method and invoke it.
    - * \todo Seems like handler == NULL is a situation that needs handling.
    - */
    - handler = handlers[method_index];
    - (*handler)(service, input, closure, closure_data);
    -}
    -
    -void
    -protobuf_c_service_generated_init(ProtobufCService *service,
    - const ProtobufCServiceDescriptor *descriptor,
    - ProtobufCServiceDestroy destroy)
    -{
    - ASSERT_IS_SERVICE_DESCRIPTOR(descriptor);
    - service->descriptor = descriptor;
    - service->destroy = destroy;
    - service->invoke = protobuf_c_service_invoke_internal;
    - memset(service + 1, 0, descriptor->n_methods * sizeof(GenericHandler));
    -}
    -
    -void protobuf_c_service_destroy(ProtobufCService *service)
    -{
    - service->destroy(service);
    -}
    -
    -/* --- querying the descriptors --- */
    -
    -const ProtobufCEnumValue *
    -protobuf_c_enum_descriptor_get_value_by_name(const ProtobufCEnumDescriptor *desc,
    - const char *name)
    -{
    - unsigned start = 0;
    - unsigned count = desc->n_value_names;
    -
    - while (count > 1) {
    - unsigned mid = start + count / 2;
    - int rv = strcmp(desc->values_by_name[mid].name, name);
    - if (rv == 0)
    - return desc->values + desc->values_by_name[mid].index;
    - else if (rv < 0) {
    - count = start + count - (mid + 1);
    - start = mid + 1;
    - } else
    - count = mid - start;
    - }
    - if (count == 0)
    - return NULL;
    - if (strcmp(desc->values_by_name[start].name, name) == 0)
    - return desc->values + desc->values_by_name[start].index;
    - return NULL;
    -}
    -
    -const ProtobufCEnumValue *
    -protobuf_c_enum_descriptor_get_value(const ProtobufCEnumDescriptor *desc,
    - int value)
    -{
    - int rv = int_range_lookup(desc->n_value_ranges, desc->value_ranges, value);
    - if (rv < 0)
    - return NULL;
    - return desc->values + rv;
    -}
    -
    -const ProtobufCFieldDescriptor *
    -protobuf_c_message_descriptor_get_field_by_name(const ProtobufCMessageDescriptor *desc,
    - const char *name)
    -{
    - unsigned start = 0;
    - unsigned count = desc->n_fields;
    - const ProtobufCFieldDescriptor *field;
    -
    - while (count > 1) {
    - unsigned mid = start + count / 2;
    - int rv;
    - field = desc->fields + desc->fields_sorted_by_name[mid];
    - rv = strcmp(field->name, name);
    - if (rv == 0)
    - return field;
    - else if (rv < 0) {
    - count = start + count - (mid + 1);
    - start = mid + 1;
    - } else
    - count = mid - start;
    - }
    - if (count == 0)
    - return NULL;
    - field = desc->fields + desc->fields_sorted_by_name[start];
    - if (strcmp(field->name, name) == 0)
    - return field;
    - return NULL;
    -}
    -
    -const ProtobufCFieldDescriptor *
    -protobuf_c_message_descriptor_get_field(const ProtobufCMessageDescriptor *desc,
    - unsigned value)
    -{
    - int rv = int_range_lookup(desc->n_field_ranges,desc->field_ranges, value);
    - if (rv < 0)
    - return NULL;
    - return desc->fields + rv;
    -}
    -
    -const ProtobufCMethodDescriptor *
    -protobuf_c_service_descriptor_get_method_by_name(const ProtobufCServiceDescriptor *desc,
    - const char *name)
    -{
    - unsigned start = 0;
    - unsigned count = desc->n_methods;
    -
    - while (count > 1) {
    - unsigned mid = start + count / 2;
    - unsigned mid_index = desc->method_indices_by_name[mid];
    - const char *mid_name = desc->methods[mid_index].name;
    - int rv = strcmp(mid_name, name);
    -
    - if (rv == 0)
    - return desc->methods + desc->method_indices_by_name[mid];
    - if (rv < 0) {
    - count = start + count - (mid + 1);
    - start = mid + 1;
    - } else {
    - count = mid - start;
    - }
    - }
    - if (count == 0)
    - return NULL;
    - if (strcmp(desc->methods[desc->method_indices_by_name[start]].name, name) == 0)
    - return desc->methods + desc->method_indices_by_name[start];
    - return NULL;
    -}
    --- a/libpurple/protocols/gg/lib/protobuf-c.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1078 +0,0 @@
    -/*
    - * Copyright (c) 2008-2014, Dave Benson and the protobuf-c authors.
    - * All rights reserved.
    - *
    - * Redistribution and use in source and binary forms, with or without
    - * modification, are permitted provided that the following conditions are
    - * met:
    - *
    - * * Redistributions of source code must retain the above copyright
    - * notice, this list of conditions and the following disclaimer.
    - *
    - * * Redistributions in binary form must reproduce the above
    - * copyright notice, this list of conditions and the following disclaimer
    - * in the documentation and/or other materials provided with the
    - * distribution.
    - *
    - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    - */
    -
    -/*! \file
    - * \mainpage Introduction
    - *
    - * This is [protobuf-c], a C implementation of [Protocol Buffers].
    - *
    - * This file defines the public API for the `libprotobuf-c` support library.
    - * This API includes interfaces that can be used directly by client code as well
    - * as the interfaces used by the code generated by the `protoc-c` compiler.
    - *
    - * The `libprotobuf-c` support library performs the actual serialization and
    - * deserialization of Protocol Buffers messages. It interacts with structures,
    - * definitions, and metadata generated by the `protoc-c` compiler from .proto
    - * files.
    - *
    - * \authors Dave Benson and the `protobuf-c` authors.
    - *
    - * \copyright 2008-2014. Licensed under the terms of the [BSD-2-Clause] license.
    - *
    - * [protobuf-c]: https://github.com/protobuf-c/protobuf-c
    - * [Protocol Buffers]: https://developers.google.com/protocol-buffers/
    - * [BSD-2-Clause]: http://opensource.org/licenses/BSD-2-Clause
    - *
    - * \page gencode Generated Code
    - *
    - * For each enum, we generate a C enum. For each message, we generate a C
    - * structure which can be cast to a `ProtobufCMessage`.
    - *
    - * For each enum and message, we generate a descriptor object that allows us to
    - * implement a kind of reflection on the structures.
    - *
    - * First, some naming conventions:
    - *
    - * - The name of the type for enums and messages and services is camel case
    - * (meaning WordsAreCrammedTogether) except that double underscores are used
    - * to delimit scopes. For example, the following `.proto` file:
    - *
    -~~~{.proto}
    - package foo.bar;
    - message BazBah {
    - optional int32 val = 1;
    - }
    -~~~
    - *
    - * would generate a C type `Foo__Bar__BazBah`.
    - *
    - * - Identifiers for functions and globals are all lowercase, with camel case
    - * words separated by single underscores. For example, one of the function
    - * prototypes generated by `protoc-c` for the above example:
    - *
    -~~~{.c}
    -Foo__Bar__BazBah *
    - foo__bar__baz_bah__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -~~~
    - *
    - * - Identifiers for enum values contain an uppercase prefix which embeds the
    - * package name and the enum type name.
    - *
    - * - A double underscore is used to separate further components of identifier
    - * names.
    - *
    - * For example, in the name of the unpack function above, the package name
    - * `foo.bar` has become `foo__bar`, the message name BazBah has become
    - * `baz_bah`, and the method name is `unpack`. These are all joined with double
    - * underscores to form the C identifier `foo__bar__baz_bah__unpack`.
    - *
    - * We also generate descriptor objects for messages and enums. These are
    - * declared in the `.pb-c.h` files:
    - *
    -~~~{.c}
    -extern const ProtobufCMessageDescriptor foo__bar__baz_bah__descriptor;
    -~~~
    - *
    - * The message structures all begin with `ProtobufCMessageDescriptor *` which is
    - * sufficient to allow them to be cast to `ProtobufCMessage`.
    - *
    - * For each message defined in a `.proto` file, we generate a number of
    - * functions. Each function name contains a prefix based on the package name and
    - * message name in order to make it a unique C identifier.
    - *
    - * - `unpack()`. Unpacks data for a particular message format. Note that the
    - * `allocator` parameter is usually `NULL` to indicate that the system's
    - * `malloc()` and `free()` functions should be used for dynamically allocating
    - * memory.
    - *
    -~~~{.c}
    -Foo__Bar__BazBah *
    - foo__bar__baz_bah__unpack
    - (ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -~~~
    - *
    - * - `free_unpacked()`. Frees a message object obtained with the `unpack()`
    - * method.
    - *
    -~~~{.c}
    -void foo__bar__baz_bah__free_unpacked
    - (Foo__Bar__BazBah *message,
    - ProtobufCAllocator *allocator);
    -~~~
    - *
    - * - `get_packed_size()`. Calculates the length in bytes of the serialized
    - * representation of the message object.
    - *
    -~~~{.c}
    -size_t foo__bar__baz_bah__get_packed_size
    - (const Foo__Bar__BazBah *message);
    -~~~
    - *
    - * - `pack()`. Pack a message object into a preallocated buffer. Assumes that
    - * the buffer is large enough. (Use `get_packed_size()` first.)
    - *
    -~~~{.c}
    -size_t foo__bar__baz_bah__pack
    - (const Foo__Bar__BazBah *message,
    - uint8_t *out);
    -~~~
    - *
    - * - `pack_to_buffer()`. Packs a message into a "virtual buffer". This is an
    - * object which defines an "append bytes" callback to consume data as it is
    - * serialized.
    - *
    -~~~{.c}
    -size_t foo__bar__baz_bah__pack_to_buffer
    - (const Foo__Bar__BazBah *message,
    - ProtobufCBuffer *buffer);
    -~~~
    - *
    - * \page pack Packing and unpacking messages
    - *
    - * To pack a message, first compute the packed size of the message with
    - * protobuf_c_message_get_packed_size(), then allocate a buffer of at least
    - * that size, then call protobuf_c_message_pack().
    - *
    - * Alternatively, a message can be serialized without calculating the final size
    - * first. Use the protobuf_c_message_pack_to_buffer() function and provide a
    - * ProtobufCBuffer object which implements an "append" method that consumes
    - * data.
    - *
    - * To unpack a message, call the protobuf_c_message_unpack() function. The
    - * result can be cast to an object of the type that matches the descriptor for
    - * the message.
    - *
    - * The result of unpacking a message should be freed with
    - * protobuf_c_message_free_unpacked().
    - */
    -
    -#ifndef PROTOBUF_C_H
    -#define PROTOBUF_C_H
    -
    -#include <assert.h>
    -#include <limits.h>
    -#include <stddef.h>
    -#include <stdint.h>
    -
    -#ifdef __cplusplus
    -# define PROTOBUF_C__BEGIN_DECLS extern "C" {
    -# define PROTOBUF_C__END_DECLS }
    -#else
    -# define PROTOBUF_C__BEGIN_DECLS
    -# define PROTOBUF_C__END_DECLS
    -#endif
    -
    -PROTOBUF_C__BEGIN_DECLS
    -
    -#if defined(_WIN32) && defined(PROTOBUF_C_USE_SHARED_LIB)
    -# ifdef PROTOBUF_C_EXPORT
    -# define PROTOBUF_C__API __declspec(dllexport)
    -# else
    -# define PROTOBUF_C__API __declspec(dllimport)
    -# endif
    -#else
    -# define PROTOBUF_C__API
    -#endif
    -
    -#if !defined(PROTOBUF_C__NO_DEPRECATED) && \
    - ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
    -# define PROTOBUF_C__DEPRECATED __attribute__((__deprecated__))
    -#else
    -# define PROTOBUF_C__DEPRECATED
    -#endif
    -
    -#ifndef PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE
    - #define PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(enum_name) \
    - , _##enum_name##_IS_INT_SIZE = INT_MAX
    -#endif
    -
    -#define PROTOBUF_C__SERVICE_DESCRIPTOR_MAGIC 0x14159bc3
    -#define PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC 0x28aaeef9
    -#define PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC 0x114315af
    -
    -/**
    - * \defgroup api Public API
    - *
    - * This is the public API for `libprotobuf-c`. These interfaces are stable and
    - * subject to Semantic Versioning guarantees.
    - *
    - * @{
    - */
    -
    -/**
    - * Values for the `flags` word in `ProtobufCFieldDescriptor`.
    - */
    -typedef enum {
    - /** Set if the field is repeated and marked with the `packed` option. */
    - PROTOBUF_C_FIELD_FLAG_PACKED = (1 << 0),
    -
    - /** Set if the field is marked with the `deprecated` option. */
    - PROTOBUF_C_FIELD_FLAG_DEPRECATED = (1 << 1),
    -} ProtobufCFieldFlag;
    -
    -/**
    - * Message field rules.
    - *
    - * \see [Defining A Message Type] in the Protocol Buffers documentation.
    - *
    - * [Defining A Message Type]:
    - * https://developers.google.com/protocol-buffers/docs/proto#simple
    - */
    -typedef enum {
    - /** A well-formed message must have exactly one of this field. */
    - PROTOBUF_C_LABEL_REQUIRED,
    -
    - /**
    - * A well-formed message can have zero or one of this field (but not
    - * more than one).
    - */
    - PROTOBUF_C_LABEL_OPTIONAL,
    -
    - /**
    - * This field can be repeated any number of times (including zero) in a
    - * well-formed message. The order of the repeated values will be
    - * preserved.
    - */
    - PROTOBUF_C_LABEL_REPEATED,
    -} ProtobufCLabel;
    -
    -/**
    - * Field value types.
    - *
    - * \see [Scalar Value Types] in the Protocol Buffers documentation.
    - *
    - * [Scalar Value Types]:
    - * https://developers.google.com/protocol-buffers/docs/proto#scalar
    - */
    -typedef enum {
    - PROTOBUF_C_TYPE_INT32, /**< int32 */
    - PROTOBUF_C_TYPE_SINT32, /**< signed int32 */
    - PROTOBUF_C_TYPE_SFIXED32, /**< signed int32 (4 bytes) */
    - PROTOBUF_C_TYPE_INT64, /**< int64 */
    - PROTOBUF_C_TYPE_SINT64, /**< signed int64 */
    - PROTOBUF_C_TYPE_SFIXED64, /**< signed int64 (8 bytes) */
    - PROTOBUF_C_TYPE_UINT32, /**< unsigned int32 */
    - PROTOBUF_C_TYPE_FIXED32, /**< unsigned int32 (4 bytes) */
    - PROTOBUF_C_TYPE_UINT64, /**< unsigned int64 */
    - PROTOBUF_C_TYPE_FIXED64, /**< unsigned int64 (8 bytes) */
    - PROTOBUF_C_TYPE_FLOAT, /**< float */
    - PROTOBUF_C_TYPE_DOUBLE, /**< double */
    - PROTOBUF_C_TYPE_BOOL, /**< boolean */
    - PROTOBUF_C_TYPE_ENUM, /**< enumerated type */
    - PROTOBUF_C_TYPE_STRING, /**< UTF-8 or ASCII string */
    - PROTOBUF_C_TYPE_BYTES, /**< arbitrary byte sequence */
    - PROTOBUF_C_TYPE_MESSAGE, /**< nested message */
    -} ProtobufCType;
    -
    -/**
    - * Field wire types.
    - *
    - * \see [Message Structure] in the Protocol Buffers documentation.
    - *
    - * [Message Structure]:
    - * https://developers.google.com/protocol-buffers/docs/encoding#structure
    - */
    -typedef enum {
    - PROTOBUF_C_WIRE_TYPE_VARINT = 0,
    - PROTOBUF_C_WIRE_TYPE_64BIT = 1,
    - PROTOBUF_C_WIRE_TYPE_LENGTH_PREFIXED = 2,
    - /* "Start group" and "end group" wire types are unsupported. */
    - PROTOBUF_C_WIRE_TYPE_32BIT = 5,
    -} ProtobufCWireType;
    -
    -struct ProtobufCAllocator;
    -struct ProtobufCBinaryData;
    -struct ProtobufCBuffer;
    -struct ProtobufCBufferSimple;
    -struct ProtobufCEnumDescriptor;
    -struct ProtobufCEnumValue;
    -struct ProtobufCEnumValueIndex;
    -struct ProtobufCFieldDescriptor;
    -struct ProtobufCIntRange;
    -struct ProtobufCMessage;
    -struct ProtobufCMessageDescriptor;
    -struct ProtobufCMessageUnknownField;
    -struct ProtobufCMethodDescriptor;
    -struct ProtobufCService;
    -struct ProtobufCServiceDescriptor;
    -
    -typedef struct ProtobufCAllocator ProtobufCAllocator;
    -typedef struct ProtobufCBinaryData ProtobufCBinaryData;
    -typedef struct ProtobufCBuffer ProtobufCBuffer;
    -typedef struct ProtobufCBufferSimple ProtobufCBufferSimple;
    -typedef struct ProtobufCEnumDescriptor ProtobufCEnumDescriptor;
    -typedef struct ProtobufCEnumValue ProtobufCEnumValue;
    -typedef struct ProtobufCEnumValueIndex ProtobufCEnumValueIndex;
    -typedef struct ProtobufCFieldDescriptor ProtobufCFieldDescriptor;
    -typedef struct ProtobufCIntRange ProtobufCIntRange;
    -typedef struct ProtobufCMessage ProtobufCMessage;
    -typedef struct ProtobufCMessageDescriptor ProtobufCMessageDescriptor;
    -typedef struct ProtobufCMessageUnknownField ProtobufCMessageUnknownField;
    -typedef struct ProtobufCMethodDescriptor ProtobufCMethodDescriptor;
    -typedef struct ProtobufCService ProtobufCService;
    -typedef struct ProtobufCServiceDescriptor ProtobufCServiceDescriptor;
    -
    -/** Boolean type. */
    -typedef int protobuf_c_boolean;
    -
    -typedef void (*ProtobufCClosure)(const ProtobufCMessage *, void *closure_data);
    -typedef void (*ProtobufCMessageInit)(ProtobufCMessage *);
    -typedef void (*ProtobufCServiceDestroy)(ProtobufCService *);
    -
    -/**
    - * Structure for defining a custom memory allocator.
    - */
    -struct ProtobufCAllocator {
    - /** Function to allocate memory. */
    - void *(*alloc)(void *allocator_data, size_t size);
    -
    - /** Function to free memory. */
    - void (*free)(void *allocator_data, void *pointer);
    -
    - /** Opaque pointer passed to `alloc` and `free` functions. */
    - void *allocator_data;
    -};
    -
    -/**
    - * Structure for the protobuf `bytes` scalar type.
    - *
    - * The data contained in a `ProtobufCBinaryData` is an arbitrary sequence of
    - * bytes. It may contain embedded `NUL` characters and is not required to be
    - * `NUL`-terminated.
    - */
    -struct ProtobufCBinaryData {
    - size_t len; /**< Number of bytes in the `data` field. */
    - uint8_t *data; /**< Data bytes. */
    -};
    -
    -/**
    - * Structure for defining a virtual append-only buffer. Used by
    - * protobuf_c_message_pack_to_buffer() to abstract the consumption of serialized
    - * bytes.
    - *
    - * `ProtobufCBuffer` "subclasses" may be defined on the stack. For example, to
    - * write to a `FILE` object:
    - *
    -~~~{.c}
    -typedef struct {
    - ProtobufCBuffer base;
    - FILE *fp;
    -} BufferAppendToFile;
    -
    -static void
    -my_buffer_file_append(ProtobufCBuffer *buffer,
    - size_t len,
    - const uint8_t *data)
    -{
    - BufferAppendToFile *file_buf = (BufferAppendToFile *) buffer;
    - fwrite(data, len, 1, file_buf->fp); // XXX: No error handling!
    -}
    -~~~
    - *
    - * To use this new type of ProtobufCBuffer, it could be called as follows:
    - *
    -~~~{.c}
    -...
    -BufferAppendToFile tmp = {0};
    -tmp.base.append = my_buffer_file_append;
    -tmp.fp = fp;
    -protobuf_c_message_pack_to_buffer(&message, &tmp);
    -...
    -~~~
    - */
    -struct ProtobufCBuffer {
    - /** Append function. Consumes the `len` bytes stored at `data`. */
    - void (*append)(ProtobufCBuffer *buffer,
    - size_t len,
    - const uint8_t *data);
    -};
    -
    -/**
    - * Simple buffer "subclass" of `ProtobufCBuffer`.
    - *
    - * A `ProtobufCBufferSimple` object is declared on the stack and uses a
    - * scratch buffer provided by the user for the initial allocation. It performs
    - * exponential resizing, using dynamically allocated memory. A
    - * `ProtobufCBufferSimple` object can be created and used as follows:
    - *
    -~~~{.c}
    -uint8_t pad[128];
    -ProtobufCBufferSimple simple = PROTOBUF_C_BUFFER_SIMPLE_INIT(pad);
    -ProtobufCBuffer *buffer = (ProtobufCBuffer *) &simple;
    -~~~
    - *
    - * `buffer` can now be used with `protobuf_c_message_pack_to_buffer()`. Once a
    - * message has been serialized to a `ProtobufCBufferSimple` object, the
    - * serialized data bytes can be accessed from the `.data` field.
    - *
    - * To free the memory allocated by a `ProtobufCBufferSimple` object, if any,
    - * call PROTOBUF_C_BUFFER_SIMPLE_CLEAR() on the object, for example:
    - *
    -~~~{.c}
    -PROTOBUF_C_BUFFER_SIMPLE_CLEAR(&simple);
    -~~~
    - *
    - * \see PROTOBUF_C_BUFFER_SIMPLE_INIT
    - * \see PROTOBUF_C_BUFFER_SIMPLE_CLEAR
    - */
    -struct ProtobufCBufferSimple {
    - /** "Base class". */
    - ProtobufCBuffer base;
    - /** Number of bytes allocated in `data`. */
    - size_t alloced;
    - /** Number of bytes currently stored in `data`. */
    - size_t len;
    - /** Data bytes. */
    - uint8_t *data;
    - /** Whether `data` must be freed. */
    - protobuf_c_boolean must_free_data;
    - /** Allocator to use. May be NULL to indicate the system allocator. */
    - ProtobufCAllocator *allocator;
    -};
    -
    -/**
    - * Describes an enumeration as a whole, with all of its values.
    - */
    -struct ProtobufCEnumDescriptor {
    - /** Magic value checked to ensure that the API is used correctly. */
    - uint32_t magic;
    -
    - /** The qualified name (e.g., "namespace.Type"). */
    - const char *name;
    - /** The unqualified name as given in the .proto file (e.g., "Type"). */
    - const char *short_name;
    - /** Identifier used in generated C code. */
    - const char *c_name;
    - /** The dot-separated namespace. */
    - const char *package_name;
    -
    - /** Number elements in `values`. */
    - unsigned n_values;
    - /** Array of distinct values, sorted by numeric value. */
    - const ProtobufCEnumValue *values;
    -
    - /** Number of elements in `values_by_name`. */
    - unsigned n_value_names;
    - /** Array of named values, including aliases, sorted by name. */
    - const ProtobufCEnumValueIndex *values_by_name;
    -
    - /** Number of elements in `value_ranges`. */
    - unsigned n_value_ranges;
    - /** Value ranges, for faster lookups by numeric value. */
    - const ProtobufCIntRange *value_ranges;
    -
    - /** Reserved for future use. */
    - void *reserved1;
    - /** Reserved for future use. */
    - void *reserved2;
    - /** Reserved for future use. */
    - void *reserved3;
    - /** Reserved for future use. */
    - void *reserved4;
    -};
    -
    -/**
    - * Represents a single value of an enumeration.
    - */
    -struct ProtobufCEnumValue {
    - /** The string identifying this value in the .proto file. */
    - const char *name;
    -
    - /** The string identifying this value in generated C code. */
    - const char *c_name;
    -
    - /** The numeric value assigned in the .proto file. */
    - int value;
    -};
    -
    -/**
    - * Used by `ProtobufCEnumDescriptor` to look up enum values.
    - */
    -struct ProtobufCEnumValueIndex {
    - /** Name of the enum value. */
    - const char *name;
    - /** Index into values[] array. */
    - unsigned index;
    -};
    -
    -/**
    - * Describes a single field in a message.
    - */
    -struct ProtobufCFieldDescriptor {
    - /** Name of the field as given in the .proto file. */
    - const char *name;
    -
    - /** Tag value of the field as given in the .proto file. */
    - uint32_t id;
    -
    - /** Whether the field is `REQUIRED`, `OPTIONAL`, or `REPEATED`. */
    - ProtobufCLabel label;
    -
    - /** The type of the field. */
    - ProtobufCType type;
    -
    - /**
    - * The offset in bytes of the message's C structure's quantifier field
    - * (the `has_MEMBER` field for optional members or the `n_MEMBER` field
    - * for repeated members.
    - */
    - unsigned quantifier_offset;
    -
    - /**
    - * The offset in bytes into the message's C structure for the member
    - * itself.
    - */
    - unsigned offset;
    -
    - /**
    - * A type-specific descriptor.
    - *
    - * If `type` is `PROTOBUF_C_TYPE_ENUM`, then `descriptor` points to the
    - * corresponding `ProtobufCEnumDescriptor`.
    - *
    - * If `type` is `PROTOBUF_C_TYPE_MESSAGE`, then `descriptor` points to
    - * the corresponding `ProtobufCMessageDescriptor`.
    - *
    - * Otherwise this field is NULL.
    - */
    - const void *descriptor; /* for MESSAGE and ENUM types */
    -
    - /** The default value for this field, if defined. May be NULL. */
    - const void *default_value;
    -
    - /**
    - * A flag word. Zero or more of the bits defined in the
    - * `ProtobufCFieldFlag` enum may be set.
    - */
    - uint32_t flags;
    -
    - /** Reserved for future use. */
    - unsigned reserved_flags;
    - /** Reserved for future use. */
    - void *reserved2;
    - /** Reserved for future use. */
    - void *reserved3;
    -};
    -
    -/**
    - * Helper structure for optimizing int => index lookups in the case
    - * where the keys are mostly consecutive values, as they presumably are for
    - * enums and fields.
    - *
    - * The data structures requires that the values in the original array are
    - * sorted.
    - */
    -struct ProtobufCIntRange {
    - int start_value;
    - unsigned orig_index;
    - /*
    - * NOTE: the number of values in the range can be inferred by looking
    - * at the next element's orig_index. A dummy element is added to make
    - * this simple.
    - */
    -};
    -
    -/**
    - * An instance of a message.
    - *
    - * `ProtobufCMessage` is a light-weight "base class" for all messages.
    - *
    - * In particular, `ProtobufCMessage` doesn't have any allocation policy
    - * associated with it. That's because it's common to create `ProtobufCMessage`
    - * objects on the stack. In fact, that's what we recommend for sending messages.
    - * If the object is allocated from the stack, you can't really have a memory
    - * leak.
    - *
    - * This means that calls to functions like protobuf_c_message_unpack() which
    - * return a `ProtobufCMessage` must be paired with a call to a free function,
    - * like protobuf_c_message_free_unpacked().
    - */
    -struct ProtobufCMessage {
    - /** The descriptor for this message type. */
    - const ProtobufCMessageDescriptor *descriptor;
    - /** The number of elements in `unknown_fields`. */
    - unsigned n_unknown_fields;
    - /** The fields that weren't recognized by the parser. */
    - ProtobufCMessageUnknownField *unknown_fields;
    -};
    -
    -/**
    - * Describes a message.
    - */
    -struct ProtobufCMessageDescriptor {
    - /** Magic value checked to ensure that the API is used correctly. */
    - uint32_t magic;
    -
    - /** The qualified name (e.g., "namespace.Type"). */
    - const char *name;
    - /** The unqualified name as given in the .proto file (e.g., "Type"). */
    - const char *short_name;
    - /** Identifier used in generated C code. */
    - const char *c_name;
    - /** The dot-separated namespace. */
    - const char *package_name;
    -
    - /**
    - * Size in bytes of the C structure representing an instance of this
    - * type of message.
    - */
    - size_t sizeof_message;
    -
    - /** Number of elements in `fields`. */
    - unsigned n_fields;
    - /** Field descriptors, sorted by tag number. */
    - const ProtobufCFieldDescriptor *fields;
    - /** Used for looking up fields by name. */
    - const unsigned *fields_sorted_by_name;
    -
    - /** Number of elements in `field_ranges`. */
    - unsigned n_field_ranges;
    - /** Used for looking up fields by id. */
    - const ProtobufCIntRange *field_ranges;
    -
    - /** Message initialisation function. */
    - ProtobufCMessageInit message_init;
    -
    - /** Reserved for future use. */
    - void *reserved1;
    - /** Reserved for future use. */
    - void *reserved2;
    - /** Reserved for future use. */
    - void *reserved3;
    -};
    -
    -/**
    - * An unknown message field.
    - */
    -struct ProtobufCMessageUnknownField {
    - /** The tag number. */
    - uint32_t tag;
    - /** The wire type of the field. */
    - ProtobufCWireType wire_type;
    - /** Number of bytes in `data`. */
    - size_t len;
    - /** Field data. */
    - uint8_t *data;
    -};
    -
    -/**
    - * Method descriptor.
    - */
    -struct ProtobufCMethodDescriptor {
    - /** Method name. */
    - const char *name;
    - /** Input message descriptor. */
    - const ProtobufCMessageDescriptor *input;
    - /** Output message descriptor. */
    - const ProtobufCMessageDescriptor *output;
    -};
    -
    -/**
    - * Service.
    - */
    -struct ProtobufCService {
    - /** Service descriptor. */
    - const ProtobufCServiceDescriptor *descriptor;
    - /** Function to invoke the service. */
    - void (*invoke)(ProtobufCService *service,
    - unsigned method_index,
    - const ProtobufCMessage *input,
    - ProtobufCClosure closure,
    - void *closure_data);
    - /** Function to destroy the service. */
    - void (*destroy)(ProtobufCService *service);
    -};
    -
    -/**
    - * Service descriptor.
    - */
    -struct ProtobufCServiceDescriptor {
    - /** Magic value checked to ensure that the API is used correctly. */
    - uint32_t magic;
    -
    - /** Service name. */
    - const char *name;
    - /** Short version of service name. */
    - const char *short_name;
    - /** C identifier for the service name. */
    - const char *c_name;
    - /** Package name. */
    - const char *package;
    - /** Number of elements in `methods`. */
    - unsigned n_methods;
    - /** Method descriptors, in the order defined in the .proto file. */
    - const ProtobufCMethodDescriptor *methods;
    - /** Sort index of methods. */
    - const unsigned *method_indices_by_name;
    -};
    -
    -/**
    - * Get the version of the protobuf-c library. Note that this is the version of
    - * the library linked against, not the version of the headers compiled against.
    - *
    - * \return A string containing the version number of protobuf-c.
    - */
    -PROTOBUF_C__API
    -const char *
    -protobuf_c_version(void);
    -
    -/**
    - * Get the version of the protobuf-c library. Note that this is the version of
    - * the library linked against, not the version of the headers compiled against.
    - *
    - * \return A 32 bit unsigned integer containing the version number of
    - * protobuf-c, represented in base-10 as (MAJOR*1E6) + (MINOR*1E3) + PATCH.
    - */
    -PROTOBUF_C__API
    -uint32_t
    -protobuf_c_version_number(void);
    -
    -/**
    - * The version of the protobuf-c headers, represented as a string using the same
    - * format as protobuf_c_version().
    - */
    -#define PROTOBUF_C_VERSION "1.0.2"
    -
    -/**
    - * The version of the protobuf-c headers, represented as an integer using the
    - * same format as protobuf_c_version_number().
    - */
    -#define PROTOBUF_C_VERSION_NUMBER 1000002
    -
    -/**
    - * The minimum protoc-c version which works with the current version of the
    - * protobuf-c headers.
    - */
    -#define PROTOBUF_C_MIN_COMPILER_VERSION 1000000
    -
    -/**
    - * Look up a `ProtobufCEnumValue` from a `ProtobufCEnumDescriptor` by name.
    - *
    - * \param desc
    - * The `ProtobufCEnumDescriptor` object.
    - * \param name
    - * The `name` field from the corresponding `ProtobufCEnumValue` object to
    - * match.
    - * \return
    - * A `ProtobufCEnumValue` object.
    - * \retval NULL
    - * If not found.
    - */
    -PROTOBUF_C__API
    -const ProtobufCEnumValue *
    -protobuf_c_enum_descriptor_get_value_by_name(
    - const ProtobufCEnumDescriptor *desc,
    - const char *name);
    -
    -/**
    - * Look up a `ProtobufCEnumValue` from a `ProtobufCEnumDescriptor` by numeric
    - * value.
    - *
    - * \param desc
    - * The `ProtobufCEnumDescriptor` object.
    - * \param value
    - * The `value` field from the corresponding `ProtobufCEnumValue` object to
    - * match.
    - *
    - * \return
    - * A `ProtobufCEnumValue` object.
    - * \retval NULL
    - * If not found.
    - */
    -PROTOBUF_C__API
    -const ProtobufCEnumValue *
    -protobuf_c_enum_descriptor_get_value(
    - const ProtobufCEnumDescriptor *desc,
    - int value);
    -
    -/**
    - * Look up a `ProtobufCFieldDescriptor` from a `ProtobufCMessageDescriptor` by
    - * the name of the field.
    - *
    - * \param desc
    - * The `ProtobufCMessageDescriptor` object.
    - * \param name
    - * The name of the field.
    - * \return
    - * A `ProtobufCFieldDescriptor` object.
    - * \retval NULL
    - * If not found.
    - */
    -PROTOBUF_C__API
    -const ProtobufCFieldDescriptor *
    -protobuf_c_message_descriptor_get_field_by_name(
    - const ProtobufCMessageDescriptor *desc,
    - const char *name);
    -
    -/**
    - * Look up a `ProtobufCFieldDescriptor` from a `ProtobufCMessageDescriptor` by
    - * the tag value of the field.
    - *
    - * \param desc
    - * The `ProtobufCMessageDescriptor` object.
    - * \param value
    - * The tag value of the field.
    - * \return
    - * A `ProtobufCFieldDescriptor` object.
    - * \retval NULL
    - * If not found.
    - */
    -PROTOBUF_C__API
    -const ProtobufCFieldDescriptor *
    -protobuf_c_message_descriptor_get_field(
    - const ProtobufCMessageDescriptor *desc,
    - unsigned value);
    -
    -/**
    - * Determine the number of bytes required to store the serialised message.
    - *
    - * \param message
    - * The message object to serialise.
    - * \return
    - * Number of bytes.
    - */
    -PROTOBUF_C__API
    -size_t
    -protobuf_c_message_get_packed_size(const ProtobufCMessage *message);
    -
    -/**
    - * Serialise a message from its in-memory representation.
    - *
    - * This function stores the serialised bytes of the message in a pre-allocated
    - * buffer.
    - *
    - * \param message
    - * The message object to serialise.
    - * \param[out] out
    - * Buffer to store the bytes of the serialised message. This buffer must
    - * have enough space to store the packed message. Use
    - * protobuf_c_message_get_packed_size() to determine the number of bytes
    - * required.
    - * \return
    - * Number of bytes stored in `out`.
    - */
    -PROTOBUF_C__API
    -size_t
    -protobuf_c_message_pack(const ProtobufCMessage *message, uint8_t *out);
    -
    -/**
    - * Serialise a message from its in-memory representation to a virtual buffer.
    - *
    - * This function calls the `append` method of a `ProtobufCBuffer` object to
    - * consume the bytes generated by the serialiser.
    - *
    - * \param message
    - * The message object to serialise.
    - * \param buffer
    - * The virtual buffer object.
    - * \return
    - * Number of bytes passed to the virtual buffer.
    - */
    -PROTOBUF_C__API
    -size_t
    -protobuf_c_message_pack_to_buffer(
    - const ProtobufCMessage *message,
    - ProtobufCBuffer *buffer);
    -
    -/**
    - * Unpack a serialised message into an in-memory representation.
    - *
    - * \param descriptor
    - * The message descriptor.
    - * \param allocator
    - * `ProtobufCAllocator` to use for memory allocation. May be NULL to
    - * specify the default allocator.
    - * \param len
    - * Length in bytes of the serialised message.
    - * \param data
    - * Pointer to the serialised message.
    - * \return
    - * An unpacked message object.
    - * \retval NULL
    - * If an error occurred during unpacking.
    - */
    -PROTOBUF_C__API
    -ProtobufCMessage *
    -protobuf_c_message_unpack(
    - const ProtobufCMessageDescriptor *descriptor,
    - ProtobufCAllocator *allocator,
    - size_t len,
    - const uint8_t *data);
    -
    -/**
    - * Free an unpacked message object.
    - *
    - * This function should be used to deallocate the memory used by a call to
    - * protobuf_c_message_unpack().
    - *
    - * \param message
    - * The message object to free.
    - * \param allocator
    - * `ProtobufCAllocator` to use for memory deallocation. May be NULL to
    - * specify the default allocator.
    - */
    -PROTOBUF_C__API
    -void
    -protobuf_c_message_free_unpacked(
    - ProtobufCMessage *message,
    - ProtobufCAllocator *allocator);
    -
    -/**
    - * Check the validity of a message object.
    - *
    - * Makes sure all required fields (`PROTOBUF_C_LABEL_REQUIRED`) are present.
    - * Recursively checks nested messages.
    - *
    - * \retval TRUE
    - * Message is valid.
    - * \retval FALSE
    - * Message is invalid.
    - */
    -PROTOBUF_C__API
    -protobuf_c_boolean
    -protobuf_c_message_check(const ProtobufCMessage *);
    -
    -/** Message initialiser. */
    -#define PROTOBUF_C_MESSAGE_INIT(descriptor) { descriptor, 0, NULL }
    -
    -/**
    - * Initialise a message object from a message descriptor.
    - *
    - * \param descriptor
    - * Message descriptor.
    - * \param message
    - * Allocated block of memory of size `descriptor->sizeof_message`.
    - */
    -PROTOBUF_C__API
    -void
    -protobuf_c_message_init(
    - const ProtobufCMessageDescriptor *descriptor,
    - void *message);
    -
    -/**
    - * Free a service.
    - *
    - * \param service
    - * The service object to free.
    - */
    -PROTOBUF_C__API
    -void
    -protobuf_c_service_destroy(ProtobufCService *service);
    -
    -/**
    - * Look up a `ProtobufCMethodDescriptor` by name.
    - *
    - * \param desc
    - * Service descriptor.
    - * \param name
    - * Name of the method.
    - *
    - * \return
    - * A `ProtobufCMethodDescriptor` object.
    - * \retval NULL
    - * If not found.
    - */
    -PROTOBUF_C__API
    -const ProtobufCMethodDescriptor *
    -protobuf_c_service_descriptor_get_method_by_name(
    - const ProtobufCServiceDescriptor *desc,
    - const char *name);
    -
    -/**
    - * Initialise a `ProtobufCBufferSimple` object.
    - */
    -#define PROTOBUF_C_BUFFER_SIMPLE_INIT(array_of_bytes) \
    -{ \
    - { protobuf_c_buffer_simple_append }, \
    - sizeof(array_of_bytes), \
    - 0, \
    - (array_of_bytes), \
    - 0, \
    - NULL \
    -}
    -
    -/**
    - * Clear a `ProtobufCBufferSimple` object, freeing any allocated memory.
    - */
    -#define PROTOBUF_C_BUFFER_SIMPLE_CLEAR(simp_buf) \
    -do { \
    - if ((simp_buf)->must_free_data) { \
    - if ((simp_buf)->allocator != NULL) \
    - (simp_buf)->allocator->free( \
    - (simp_buf)->allocator, \
    - (simp_buf)->data); \
    - else \
    - free((simp_buf)->data); \
    - } \
    -} while (0)
    -
    -/**
    - * The `append` method for `ProtobufCBufferSimple`.
    - *
    - * \param buffer
    - * The buffer object to append to. Must actually be a
    - * `ProtobufCBufferSimple` object.
    - * \param len
    - * Number of bytes in `data`.
    - * \param data
    - * Data to append.
    - */
    -PROTOBUF_C__API
    -void
    -protobuf_c_buffer_simple_append(
    - ProtobufCBuffer *buffer,
    - size_t len,
    - const unsigned char *data);
    -
    -PROTOBUF_C__API
    -void
    -protobuf_c_service_generated_init(
    - ProtobufCService *service,
    - const ProtobufCServiceDescriptor *descriptor,
    - ProtobufCServiceDestroy destroy);
    -
    -PROTOBUF_C__API
    -void
    -protobuf_c_service_invoke_internal(
    - ProtobufCService *service,
    - unsigned method_index,
    - const ProtobufCMessage *input,
    - ProtobufCClosure closure,
    - void *closure_data);
    -
    -/**@}*/
    -
    -PROTOBUF_C__END_DECLS
    -
    -#endif /* PROTOBUF_C_H */
    --- a/libpurple/protocols/gg/lib/protobuf.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,153 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2012 Tomek Wasilczyk <www.wasilczyk.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file protobuf.c
    - *
    - * \brief Funkcje pomocnicze do obsługi formatu protocol buffers
    - */
    -
    -#include "protobuf.h"
    -
    -#define GG_PROTOBUFF_UIN_MAXLEN 15
    -struct _gg_protobuf_uin_buff
    -{
    - char data[GG_PROTOBUFF_UIN_MAXLEN + 1 + 2];
    -};
    -
    -void gg_protobuf_expected(struct gg_session *gs, const char *field_name,
    - uint32_t value, uint32_t expected)
    -{
    - if (value == expected) {
    - return;
    - }
    - gg_debug_session(gs, GG_DEBUG_WARNING, "// gg_packet: field %s was "
    - "expected to be %#x, but its value was %#x\n",
    - field_name, expected, value);
    -}
    -
    -int gg_protobuf_valid_chknull(struct gg_session *gs, const char *msg_name,
    - int isNull)
    -{
    - if (isNull) {
    - gg_debug_session(gs, GG_DEBUG_ERROR, "// gg_protobuf: couldn't "
    - "unpack %s message\n", msg_name);
    - }
    - return !isNull;
    -}
    -
    -int gg_protobuf_valid_chkunknown(struct gg_session *gs, const char *msg_name,
    - ProtobufCMessage *base)
    -{
    - if (base->n_unknown_fields > 0) {
    - gg_debug_session(gs, GG_DEBUG_WARNING, "// gg_protobuf: message"
    - " %s had %d unknown field(s)\n",
    - msg_name, base->n_unknown_fields);
    - }
    - return 1;
    -}
    -
    -int gg_protobuf_send_ex(struct gg_session *gs, struct gg_event *ge, int type,
    - void *msg, gg_protobuf_size_cb_t size_cb,
    - gg_protobuf_pack_cb_t pack_cb)
    -{
    - void *buffer;
    - size_t len;
    - int succ = 1;
    - enum gg_failure_t failure;
    -
    - len = size_cb(msg);
    - buffer = malloc(len);
    - if (buffer == NULL) {
    - gg_debug_session(gs, GG_DEBUG_ERROR, "// gg_protobuf_send: out "
    - "of memory - tried to allocate %" GG_SIZE_FMT
    - " bytes for %#x packet\n", len, type);
    - succ = 0;
    - failure = GG_FAILURE_INTERNAL;
    - } else {
    - pack_cb(msg, buffer);
    - succ = (-1 != gg_send_packet(gs, type, buffer, len, NULL));
    - free(buffer);
    - buffer = NULL;
    - if (!succ) {
    - failure = GG_FAILURE_WRITING;
    - gg_debug_session(gs, GG_DEBUG_ERROR,
    - "// gg_protobuf_send: sending packet %#x "
    - "failed. (errno=%d, %s)\n", type, errno,
    - strerror(errno));
    - }
    - }
    -
    - if (!succ) {
    - gg_connection_failure(gs, ge, failure);
    - }
    -
    - return succ;
    -}
    -
    -void gg_protobuf_set_uin(ProtobufCBinaryData *dst, uin_t uin, gg_protobuf_uin_buff_t *buff)
    -{
    - char *uin_str;
    - int uin_len;
    - static gg_protobuf_uin_buff_t static_buffer;
    -
    - if (buff == NULL) {
    - buff = &static_buffer;
    - }
    -
    - uin_str = buff->data + 2;
    - uin_len = snprintf(uin_str, GG_PROTOBUFF_UIN_MAXLEN + 1, "%u", uin);
    -
    - buff->data[0] = 0x01; /* magic: 0x00 lub 0x01 */
    - buff->data[1] = uin_len;
    -
    - dst->len = uin_len + 2;
    - dst->data = (uint8_t*)&buff->data;
    -}
    -
    -uin_t gg_protobuf_get_uin(ProtobufCBinaryData uin_data)
    -{
    - uint8_t magic;
    - size_t uin_len;
    - const char *uin_str;
    - uin_t uin;
    -
    - magic = (uin_data.len > 0) ? uin_data.data[0] : 0;
    - uin_len = (uin_data.len > 1) ? uin_data.data[1] : 0;
    -
    - if (uin_data.len != uin_len + 2 || uin_len > 10) {
    - gg_debug(GG_DEBUG_ERROR, "// gg_protobuf_get_uin: "
    - "invalid length\n");
    - return 0;
    - }
    - if (magic != 0) {
    - gg_debug(GG_DEBUG_WARNING, "// gg_protobuf_get_uin: "
    - "unexpected magic value=%#x\n", magic);
    - }
    -
    - uin_str = (char*)(uin_data.data + 2);
    - uin = gg_str_to_uin(uin_str, uin_len);
    -
    - if (uin == 0) {
    - gg_debug(GG_DEBUG_ERROR, "// gg_protobuf_get_uin: "
    - "invalid uin\n");
    - }
    - return uin;
    -}
    --- a/libpurple/protocols/gg/lib/protobuf.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,70 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2012 Tomek Wasilczyk <www.wasilczyk.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -#ifndef LIBGADU_PROTOBUF_H
    -#define LIBGADU_PROTOBUF_H
    -
    -#include <stdlib.h>
    -#include <errno.h>
    -#include <string.h>
    -
    -#include "config.h"
    -#ifdef GG_CONFIG_HAVE_PROTOBUF_C
    -#include <protobuf-c/protobuf-c.h>
    -#else
    -#include "protobuf-c.h"
    -#endif
    -
    -#include "internal.h"
    -#include "fileio.h"
    -
    -typedef size_t (*gg_protobuf_size_cb_t)(const void *message);
    -typedef size_t (*gg_protobuf_pack_cb_t)(const void *message, uint8_t *out);
    -
    -typedef struct _gg_protobuf_uin_buff gg_protobuf_uin_buff_t;
    -
    -/* Ostatni warunek (msg != NULL) jest tylko po to, żeby uciszyć analizę
    - * statyczną (zawiera się w pierwszym). */
    -#define GG_PROTOBUF_VALID(gs, name, msg) \
    - (gg_protobuf_valid_chknull(gs, name, msg == NULL) && \
    - gg_protobuf_valid_chkunknown(gs, name, &msg->base) && \
    - msg != NULL)
    -
    -#define GG_PROTOBUF_SEND(gs, ge, packet_type, msg_type, msg) \
    - gg_protobuf_send_ex(gs, ge, packet_type, &msg, \
    - (gg_protobuf_size_cb_t) msg_type ## __get_packed_size, \
    - (gg_protobuf_pack_cb_t) msg_type ## __pack)
    -
    -void gg_protobuf_expected(struct gg_session *gs, const char *field_name,
    - uint32_t value, uint32_t expected);
    -
    -int gg_protobuf_valid_chknull(struct gg_session *gs, const char *msg_name,
    - int isNull);
    -int gg_protobuf_valid_chkunknown(struct gg_session *gs, const char *msg_name,
    - ProtobufCMessage *base);
    -
    -int gg_protobuf_send_ex(struct gg_session *gs, struct gg_event *ge, int type,
    - void *msg, gg_protobuf_size_cb_t size_cb,
    - gg_protobuf_pack_cb_t pack_cb);
    -
    -void gg_protobuf_set_uin(ProtobufCBinaryData *dst, uin_t uin, gg_protobuf_uin_buff_t *buff);
    -uin_t gg_protobuf_get_uin(ProtobufCBinaryData uin_data);
    -
    -#endif /* LIBGADU_PROTOBUF_H */
    --- a/libpurple/protocols/gg/lib/protocol.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,411 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2009-2010 Jakub Zawadzki <darkjames@darkjames.ath.cx>
    - * Bartłomiej Zimoń <uzi18@o2.pl>
    - * Wojtek Kaniewski <wojtekka@irc.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -#ifndef LIBGADU_PROTOCOL_H
    -#define LIBGADU_PROTOCOL_H
    -
    -#include "libgadu.h"
    -
    -#ifdef _WIN32
    -#pragma pack(push, 1)
    -#endif
    -
    -#define GG_LOGIN80BETA 0x0029
    -
    -#define GG_LOGIN80 0x0031
    -
    -#define GG_LOGIN105 0x0083
    -
    -#undef GG_FEATURE_STATUS80BETA
    -#undef GG_FEATURE_MSG80
    -#undef GG_FEATURE_STATUS80
    -#define GG_FEATURE_STATUS80BETA 0x01
    -#define GG_FEATURE_MSG80 0x02
    -#define GG_FEATURE_STATUS80 0x05
    -
    -#define GG_DEFAULT_HOST_WHITE_LIST { "gadu-gadu.pl", "gg.pl", NULL }
    -
    -#define GG8_LANG "pl"
    -#define GG8_VERSION "Gadu-Gadu Client Build "
    -
    -#define GG11_VERSION "GG-Phoenix/"
    -#define GG11_TARGET " (BUILD;WINNT_x86-msvc;rv:11.0,pl;release;standard) (OS;Windows;Windows NT 6.1)"
    -
    -struct gg_login80 {
    - uint32_t uin; /* mój numerek */
    - uint8_t language[2]; /* język: GG8_LANG */
    - uint8_t hash_type; /* rodzaj hashowania hasła */
    - uint8_t hash[64]; /* hash hasła dopełniony zerami */
    - uint32_t status; /* status na dzień dobry */
    - uint32_t flags; /* flagi (przeznaczenie nieznane) */
    - uint32_t features; /* opcje protokołu (GG8_FEATURES) */
    - uint32_t local_ip; /* mój adres ip */
    - uint16_t local_port; /* port, na którym słucham */
    - uint32_t external_ip; /* zewnętrzny adres ip (???) */
    - uint16_t external_port; /* zewnętrzny port (???) */
    - uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
    - uint8_t dunno2; /* 0x64 */
    -} GG_PACKED;
    -
    -#define GG_LOGIN_HASH_TYPE_INVALID 0x0016
    -
    -#define GG_LOGIN80_OK 0x0035
    -
    -#define GG_LOGIN110_OK 0x009d
    -
    -/**
    - * Logowanie powiodło się (pakiet \c GG_LOGIN80_OK)
    - */
    -struct gg_login80_ok {
    - uint32_t unknown1; /* 0x00000001 */
    -} GG_PACKED;
    -
    -/**
    - * Logowanie nie powiodło się (pakiet \c GG_LOGIN80_FAILED)
    - */
    -#define GG_LOGIN80_FAILED 0x0043
    -
    -struct gg_login80_failed {
    - uint32_t unknown1; /* 0x00000001 */
    -} GG_PACKED;
    -
    -#define GG_NEW_STATUS80BETA 0x0028
    -
    -#define GG_NEW_STATUS80 0x0038
    -
    -/**
    - * Zmiana stanu (pakiet \c GG_NEW_STATUS80)
    - */
    -struct gg_new_status80 {
    - uint32_t status; /**< Nowy status */
    - uint32_t flags; /**< flagi (nieznane przeznaczenie) */
    - uint32_t description_size; /**< rozmiar opisu */
    -} GG_PACKED;
    -
    -#define GG_NEW_STATUS105 0x0063
    -
    -#define GG_STATUS80BETA 0x002a
    -#define GG_NOTIFY_REPLY80BETA 0x002b
    -
    -#define GG_STATUS80 0x0036
    -#define GG_NOTIFY_REPLY80 0x0037
    -
    -struct gg_notify_reply80 {
    - uint32_t uin; /* numerek plus flagi w najstarszym bajcie */
    - uint32_t status; /* status danej osoby */
    - uint32_t features; /* opcje protokołu */
    - uint32_t remote_ip; /* adres IP bezpośrednich połączeń */
    - uint16_t remote_port; /* port bezpośrednich połączeń */
    - uint8_t image_size; /* maksymalny rozmiar obrazków w KB */
    - uint8_t unknown1; /* 0x00 */
    - uint32_t flags; /* flagi połączenia */
    - uint32_t descr_len; /* rozmiar opisu */
    -} GG_PACKED;
    -
    -#define GG_SEND_MSG80 0x002d
    -
    -struct gg_send_msg80 {
    - uint32_t recipient;
    - uint32_t seq;
    - uint32_t msgclass;
    - uint32_t offset_plain;
    - uint32_t offset_attr;
    -} GG_PACKED;
    -
    -#define GG_RECV_MSG80 0x002e
    -
    -struct gg_recv_msg80 {
    - uint32_t sender;
    - uint32_t seq;
    - uint32_t time;
    - uint32_t msgclass;
    - uint32_t offset_plain;
    - uint32_t offset_attr;
    -} GG_PACKED;
    -
    -#define GG_DISCONNECT_ACK 0x000d
    -
    -#define GG_RECV_MSG_ACK 0x0046
    -
    -struct gg_recv_msg_ack {
    - uint32_t seq;
    -} GG_PACKED;
    -
    -#define GG_USER_DATA 0x0044
    -
    -struct gg_user_data {
    - uint32_t type;
    - uint32_t user_count;
    -} GG_PACKED;
    -
    -struct gg_user_data_user {
    - uint32_t uin;
    - uint32_t attr_count;
    -} GG_PACKED;
    -
    -#define GG_TYPING_NOTIFICATION 0x0059
    -
    -struct gg_typing_notification {
    - uint16_t length;
    - uint32_t uin;
    -} GG_PACKED;
    -
    -#define GG_XML_ACTION 0x002c
    -
    -#define GG_RECV_OWN_MSG 0x005a
    -
    -#define GG_MULTILOGON_INFO 0x005b
    -
    -struct gg_multilogon_info {
    - uint32_t count;
    -} GG_PACKED;
    -
    -struct gg_multilogon_info_item {
    - uint32_t addr;
    - uint32_t flags;
    - uint32_t features;
    - uint32_t logon_time;
    - gg_multilogon_id_t conn_id;
    - uint32_t unknown1;
    - uint32_t name_size;
    -} GG_PACKED;
    -
    -#define GG_MULTILOGON_DISCONNECT 0x0062
    -
    -struct gg_multilogon_disconnect {
    - gg_multilogon_id_t conn_id;
    -} GG_PACKED;
    -
    -#define GG_MSG_CALLBACK 0x02 /**< Żądanie zwrotnego połączenia bezpośredniego */
    -
    -#define GG_MSG_OPTION_CONFERENCE 0x01
    -#define GG_MSG_OPTION_ATTRIBUTES 0x02
    -#define GG_MSG_OPTION_IMAGE_REQUEST 0x04
    -#define GG_MSG_OPTION_IMAGE_REPLY 0x05
    -#define GG_MSG_OPTION_IMAGE_REPLY_MORE 0x06
    -
    -#define GG_DCC7_ABORT 0x0025
    -
    -struct gg_dcc7_abort {
    - gg_dcc7_id_t id; /* identyfikator połączenia */
    - uint32_t uin_from; /* numer nadawcy */
    - uint32_t uin_to; /* numer odbiorcy */
    -} GG_PACKED;
    -
    -#define GG_DCC7_ABORTED 0x0025
    -
    -struct gg_dcc7_aborted {
    - gg_dcc7_id_t id; /* identyfikator połączenia */
    -} GG_PACKED;
    -
    -#define GG_DCC7_VOICE_RETRIES 0x11 /* 17 powtorzen */
    -
    -#define GG_DCC7_RESERVED1 0xdeadc0de
    -#define GG_DCC7_RESERVED2 0xdeadbeaf
    -
    -struct gg_dcc7_voice_auth {
    - uint8_t type; /* 0x00 -> wysylanie ID
    - * 0x01 -> potwierdzenie ID
    - */
    - gg_dcc7_id_t id; /* identyfikator połączenia */
    - uint32_t reserved1; /* GG_DCC7_RESERVED1 */
    - uint32_t reserved2; /* GG_DCC7_RESERVED2 */
    -} GG_PACKED;
    -
    -/* Wyciszony mikrofon. Ten pakiet jest wysylany co 1s (jesli chcemy podtrzymac
    - * polaczenie).
    - */
    -struct gg_dcc7_voice_nodata {
    - uint8_t type; /* 0x02 */
    - gg_dcc7_id_t id; /* identyfikator połączenia */
    - uint32_t reserved1; /* GG_DCC7_RESERVED1 */
    - uint32_t reserved2; /* GG_DCC7_RESERVED2 */
    -} GG_PACKED;
    -
    -struct gg_dcc7_voice_data {
    - uint8_t type; /* 0x03 */
    - uint32_t did; /* XXX: co ile zwieksza sie u nas id pakietu [uzywac 0x28] */
    - uint32_t len; /* rozmiar strukturki - 1 (sizeof(type)) */
    - uint32_t packet_id; /* numerek pakietu */
    - uint32_t datalen; /* rozmiar danych */
    - /* char data[]; */ /* ramki: albo gsm, albo speex, albo melp, albo inne. */
    -} GG_PACKED;
    -
    -struct gg_dcc7_voice_init {
    - uint8_t type; /* 0x04 */
    - uint32_t id; /* nr kroku [0x1 - 0x5] */
    - uint32_t protocol; /* XXX: wersja protokolu (0x29, 0x2a, 0x2b) */
    - uint32_t len; /* rozmiar sizeof(protocol)+sizeof(len)+
    - * sizeof(data) = 0x08 + sizeof(data) */
    - /* char data[]; */ /* reszta danych */
    -} GG_PACKED;
    -
    -struct gg_dcc7_voice_init_confirm {
    - uint8_t type; /* 0x05 */
    - uint32_t id; /* id tego co potwierdzamy [0x1 - 0x5] */
    -} GG_PACKED;
    -
    -#define GG_DCC7_RELAY_TYPE_SERVER 0x01 /* adres serwera, na który spytać o proxy */
    -#define GG_DCC7_RELAY_TYPE_PROXY 0x08 /* adresy proxy, na które sie łączyć */
    -
    -#define GG_DCC7_RELAY_DUNNO1 0x02
    -
    -#define GG_DCC7_RELAY_REQUEST 0x0a
    -
    -struct gg_dcc7_relay_req {
    - uint32_t magic; /* 0x0a */
    - uint32_t len; /* długość całego pakietu */
    - gg_dcc7_id_t id; /* identyfikator połączenia */
    - uint16_t type; /* typ zapytania */
    - uint16_t dunno1; /* 0x02 */
    -} GG_PACKED;
    -
    -#define GG_DCC7_RELAY_REPLY_RCOUNT 0x02
    -
    -#define GG_DCC7_RELAY_REPLY 0x0b
    -
    -struct gg_dcc7_relay_reply {
    - uint32_t magic; /* 0x0b */
    - uint32_t len; /* długość całego pakietu */
    - uint32_t rcount; /* ilość serwerów */
    -} GG_PACKED;
    -
    -struct gg_dcc7_relay_reply_server {
    - uint32_t addr; /* adres ip serwera */
    - uint16_t port; /* port serwera */
    - uint8_t family; /* rodzina adresów (na końcu?!) AF_INET=2 */
    -} GG_PACKED;
    -
    -#define GG_DCC7_WELCOME_SERVER 0xc0debabe
    -
    -struct gg_dcc7_welcome_server {
    - uint32_t magic; /* 0xc0debabe */
    - gg_dcc7_id_t id; /* identyfikator połączenia */
    -} GG_PACKED;
    -
    -struct gg_dcc7_welcome_p2p {
    - gg_dcc7_id_t id; /* identyfikator połączenia */
    -} GG_PACKED;
    -
    -#define GG_TIMEOUT_DISCONNECT 5 /**< Maksymalny czas oczekiwania na rozłączenie */
    -
    -#define GG_USERLIST100_VERSION 0x5c
    -
    -struct gg_userlist100_version {
    - uint32_t version; /* numer wersji listy kontaktów */
    -} GG_PACKED;
    -
    -#define GG_USERLIST100_REQUEST 0x0040
    -
    -struct gg_userlist100_request {
    - uint8_t type; /* rodzaj żądania */
    - uint32_t version; /* numer ostatniej znanej wersji listy kontaktów bądź 0 */
    - uint8_t format_type; /* rodzaj żądanego typu formatu listy kontaktów */
    - uint8_t unknown1; /* 0x01 */
    - /* char request[]; */
    -} GG_PACKED;
    -
    -#define GG_USERLIST100_REPLY 0x41
    -
    -struct gg_userlist100_reply {
    - uint8_t type; /* rodzaj odpowiedzi */
    - uint32_t version; /* numer wersji listy kontaktów aktualnie przechowywanej przez serwer */
    - uint8_t format_type; /* rodzaj przesyłanego typu formatu listy kontaktów */
    - uint8_t unknown1; /* 0x01 */
    - /* char reply[]; */
    -} GG_PACKED;
    -
    -struct gg_chat_create {
    - uint32_t seq;
    - uint32_t dummy;
    -} GG_PACKED;
    -
    -struct gg_chat_invite {
    - uint64_t id;
    - uint32_t seq;
    - uint32_t participants_count;
    - /* struct {
    - uint32_t uin;
    - uint32_t dummy; (0x1e)
    - } participants[]; */
    -} GG_PACKED;
    -
    -struct gg_chat_leave {
    - uint64_t id;
    - uint32_t seq;
    -} GG_PACKED;
    -
    -struct gg_chat_created {
    - uint64_t id;
    - uint32_t seq;
    -} GG_PACKED;
    -
    -struct gg_chat_invite_ack {
    - uint64_t id;
    - uint32_t seq;
    - uint32_t unknown1; /* 0x00 */
    - uint32_t unknown2; /* 0x10 */
    -} GG_PACKED;
    -
    -struct gg_chat_left {
    - uint64_t id;
    - uint32_t uin;
    -} GG_PACKED;
    -
    -#define GG_ADD_NOTIFY105 0x007b
    -#define GG_REMOVE_NOTIFY105 0x007c
    -#define GG_EVENT110 0x0084
    -#define GG_IMTOKEN 0x008c
    -#define GG_ACCESS_INFO 0x008f
    -#define GG_NOTIFY105_FIRST 0x0077
    -#define GG_NOTIFY105_LAST 0x0078
    -#define GG_NOTIFY105_LIST_EMPTY 0x0079
    -#define GG_PONG110 0x00a1
    -#define GG_OPTIONS 0x009b
    -
    -#define GG_SEND_MSG110 0x007d
    -#define GG_RECV_MSG110 0x007e
    -#define GG_RECV_OWN_MSG110 0x0082
    -#define GG_ACK110 0x0086
    -#define GG_SEND_MSG_ACK110 0x0087
    -
    -#define GG_CHAT_INFO 0x0093
    -#define GG_CHAT_INFO_UPDATE 0x009e
    -#define GG_CHAT_CREATED 0x0045
    -#define GG_CHAT_INVITE_ACK 0x0047
    -#define GG_CHAT_RECV_MSG 0x0088
    -#define GG_CHAT_RECV_OWN_MSG 0x008e
    -#define GG_CHAT_CREATE 0x0047
    -#define GG_CHAT_INVITE 0x0090
    -#define GG_CHAT_LEAVE 0x0052
    -#define GG_CHAT_LEFT 0x0066
    -#define GG_CHAT_SEND_MSG 0x008d
    -
    -#define GG_UIN_INFO 0x007a
    -#define GG_TRANSFER_INFO 0x00a0
    -#define GG_MAGIC_NOTIFICATION 0x009f
    -
    -#ifdef _WIN32
    -#pragma pack(pop)
    -#endif
    -
    -#endif /* LIBGADU_PROTOCOL_H */
    --- a/libpurple/protocols/gg/lib/pubdir.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,888 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
    - * Dawid Jarosz <dawjar@poczta.onet.pl>
    - * Adam Wysocki <gophi@ekg.chmurka.net>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file pubdir.c
    - *
    - * \brief Obsługa katalogu publicznego
    - */
    -
    -#include "network.h"
    -#include <ctype.h>
    -#include <errno.h>
    -#include <stdlib.h>
    -#include <string.h>
    -
    -#include "libgadu.h"
    -
    -/**
    - * Rejestruje nowego użytkownika.
    - *
    - * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
    - *
    - * \param email Adres e-mail
    - * \param password Hasło
    - * \param tokenid Identyfikator tokenu
    - * \param tokenval Zawartość tokenu
    - * \param async Flaga połączenia asynchronicznego
    - *
    - * \return Struktura \c gg_http lub \c NULL w przypadku błędu
    - *
    - * \ingroup register
    - */
    -struct gg_http *gg_register3(const char *email, const char *password,
    - const char *tokenid, const char *tokenval, int async)
    -{
    - struct gg_http *h;
    - char *__pwd, *__email, *__tokenid, *__tokenval, *form, *query;
    -
    - if (!email || !password || !tokenid || !tokenval) {
    - gg_debug(GG_DEBUG_MISC, "=> register, NULL parameter\n");
    - errno = EFAULT;
    - return NULL;
    - }
    -
    - __pwd = gg_urlencode(password);
    - __email = gg_urlencode(email);
    - __tokenid = gg_urlencode(tokenid);
    - __tokenval = gg_urlencode(tokenval);
    -
    - if (!__pwd || !__email || !__tokenid || !__tokenval) {
    - gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for form fields\n");
    - free(__pwd);
    - free(__email);
    - free(__tokenid);
    - free(__tokenval);
    - return NULL;
    - }
    -
    - form = gg_saprintf("pwd=%s&email=%s&tokenid=%s&tokenval=%s&code=%u",
    - __pwd, __email, __tokenid, __tokenval,
    - gg_http_hash("ss", email, password));
    -
    - free(__pwd);
    - free(__email);
    - free(__tokenid);
    - free(__tokenval);
    -
    - if (!form) {
    - gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for form query\n");
    - return NULL;
    - }
    -
    - gg_debug(GG_DEBUG_MISC, "=> register, %s\n", form);
    -
    - query = gg_saprintf(
    - "Host: " GG_REGISTER_HOST "\r\n"
    - "Content-Type: application/x-www-form-urlencoded\r\n"
    - "User-Agent: " GG_HTTP_USERAGENT "\r\n"
    - "Content-Length: %d\r\n"
    - "Pragma: no-cache\r\n"
    - "\r\n"
    - "%s",
    - (int) strlen(form), form);
    -
    - free(form);
    -
    - if (!query) {
    - gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for query\n");
    - return NULL;
    - }
    -
    - if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async,
    - "POST", "/appsvc/fmregister3.asp", query)))
    - {
    - gg_debug(GG_DEBUG_MISC, "=> register, gg_http_connect() failed mysteriously\n");
    - free(query);
    - return NULL;
    - }
    -
    - h->type = GG_SESSION_REGISTER;
    -
    - free(query);
    -
    - h->callback = gg_pubdir_watch_fd;
    - h->destroy = gg_pubdir_free;
    -
    - if (!async)
    - gg_pubdir_watch_fd(h);
    -
    - return h;
    -}
    -
    -#ifdef DOXYGEN
    -
    -/**
    - * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
    - *
    - * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
    - * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
    - * znajdzie się w polu \c error.
    - *
    - * \note W rzeczywistości funkcja jest makrem rozwijanym do
    - * \c gg_pubdir_watch_fd().
    - *
    - * \param h Struktura połączenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup register
    - */
    -int gg_register_watch_fd(struct gg_httpd *h)
    -{
    - return gg_pubdir_watch_fd(h);
    -}
    -
    -/**
    - * Zwalnia zasoby po operacji.
    - *
    - * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
    - *
    - * \param h Struktura połączenia
    - *
    - * \ingroup register
    - */
    -void gg_register_free(struct gg_http *h)
    -{
    - return gg_pubdir_free(h);
    -}
    -
    -#endif /* DOXYGEN */
    -
    -/**
    - * Usuwa użytkownika.
    - *
    - * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
    - *
    - * \param uin Numer Gadu-Gadu
    - * \param password Hasło
    - * \param tokenid Identyfikator tokenu
    - * \param tokenval Zawartość tokenu
    - * \param async Flaga połączenia asynchronicznego
    - *
    - * \return Struktura \c gg_http lub \c NULL w przypadku błędu
    - *
    - * \ingroup unregister
    - */
    -struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async)
    -{
    - struct gg_http *h;
    - char *__fmpwd, *__pwd, *__tokenid, *__tokenval, *form, *query;
    -
    - if (!password || !tokenid || !tokenval) {
    - gg_debug(GG_DEBUG_MISC, "=> unregister, NULL parameter\n");
    - errno = EFAULT;
    - return NULL;
    - }
    -
    - __pwd = gg_saprintf("%d", rand());
    - __fmpwd = gg_urlencode(password);
    - __tokenid = gg_urlencode(tokenid);
    - __tokenval = gg_urlencode(tokenval);
    -
    - if (!__fmpwd || !__pwd || !__tokenid || !__tokenval) {
    - gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for form fields\n");
    - free(__pwd);
    - free(__fmpwd);
    - free(__tokenid);
    - free(__tokenval);
    - return NULL;
    - }
    -
    - form = gg_saprintf("fmnumber=%d&fmpwd=%s&delete=1&pwd=%s&"
    - "email=deletedaccount@gadu-gadu.pl&tokenid=%s&tokenval=%s&"
    - "code=%u", uin, __fmpwd, __pwd, __tokenid, __tokenval,
    - gg_http_hash("ss", "deletedaccount@gadu-gadu.pl", __pwd));
    -
    - free(__fmpwd);
    - free(__pwd);
    - free(__tokenid);
    - free(__tokenval);
    -
    - if (!form) {
    - gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for form query\n");
    - return NULL;
    - }
    -
    - gg_debug(GG_DEBUG_MISC, "=> unregister, %s\n", form);
    -
    - query = gg_saprintf(
    - "Host: " GG_REGISTER_HOST "\r\n"
    - "Content-Type: application/x-www-form-urlencoded\r\n"
    - "User-Agent: " GG_HTTP_USERAGENT "\r\n"
    - "Content-Length: %d\r\n"
    - "Pragma: no-cache\r\n"
    - "\r\n"
    - "%s",
    - (int) strlen(form), form);
    -
    - free(form);
    -
    - if (!query) {
    - gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for query\n");
    - return NULL;
    - }
    -
    - if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async,
    - "POST", "/appsvc/fmregister3.asp", query)))
    - {
    - gg_debug(GG_DEBUG_MISC, "=> unregister, gg_http_connect() failed mysteriously\n");
    - free(query);
    - return NULL;
    - }
    -
    - h->type = GG_SESSION_UNREGISTER;
    -
    - free(query);
    -
    - h->callback = gg_pubdir_watch_fd;
    - h->destroy = gg_pubdir_free;
    -
    - if (!async)
    - gg_pubdir_watch_fd(h);
    -
    - return h;
    -}
    -
    -#ifdef DOXYGEN
    -
    -/**
    - * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
    - *
    - * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
    - * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
    - * znajdzie się w polu \c error.
    - *
    - * \note W rzeczywistości funkcja jest makrem rozwijanym do
    - * \c gg_pubdir_watch_fd().
    - *
    - * \param h Struktura połączenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup unregister
    - */
    -int gg_unregister_watch_fd(struct gg_httpd *h)
    -{
    - return gg_pubdir_watch_fd(h);
    -}
    -
    -/**
    - * Zwalnia zasoby po operacji.
    - *
    - * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
    - *
    - * \param h Struktura połączenia
    - *
    - * \ingroup unregister
    - */
    -void gg_unregister_free(struct gg_http *h)
    -{
    - return gg_pubdir_free(h);
    -}
    -
    -#endif /* DOXYGEN */
    -
    -/**
    - * Zmienia hasło użytkownika.
    - *
    - * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
    - *
    - * \param uin Numer Gadu-Gadu
    - * \param email Adres e-mail
    - * \param passwd Obecne hasło
    - * \param newpasswd Nowe hasło
    - * \param tokenid Identyfikator tokenu
    - * \param tokenval Zawartość tokenu
    - * \param async Flaga połączenia asynchronicznego
    - *
    - * \return Struktura \c gg_http lub \c NULL w przypadku błędu
    - *
    - * \ingroup passwd
    - */
    -struct gg_http *gg_change_passwd4(uin_t uin, const char *email,
    - const char *passwd, const char *newpasswd, const char *tokenid,
    - const char *tokenval, int async)
    -{
    - struct gg_http *h;
    - char *form, *query, *__email, *__fmpwd, *__pwd, *__tokenid, *__tokenval;
    -
    - if (!uin || !email || !passwd || !newpasswd || !tokenid || !tokenval) {
    - gg_debug(GG_DEBUG_MISC, "=> change, NULL parameter\n");
    - errno = EFAULT;
    - return NULL;
    - }
    -
    - __fmpwd = gg_urlencode(passwd);
    - __pwd = gg_urlencode(newpasswd);
    - __email = gg_urlencode(email);
    - __tokenid = gg_urlencode(tokenid);
    - __tokenval = gg_urlencode(tokenval);
    -
    - if (!__fmpwd || !__pwd || !__email || !__tokenid || !__tokenval) {
    - gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for form fields\n");
    - free(__fmpwd);
    - free(__pwd);
    - free(__email);
    - free(__tokenid);
    - free(__tokenval);
    - return NULL;
    - }
    -
    - if (!(form = gg_saprintf("fmnumber=%d&fmpwd=%s&pwd=%s&email=%s&"
    - "tokenid=%s&tokenval=%s&code=%u", uin, __fmpwd, __pwd, __email,
    - __tokenid, __tokenval, gg_http_hash("ss", email, newpasswd))))
    - {
    - gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for form fields\n");
    - free(__fmpwd);
    - free(__pwd);
    - free(__email);
    - free(__tokenid);
    - free(__tokenval);
    -
    - return NULL;
    - }
    -
    - free(__fmpwd);
    - free(__pwd);
    - free(__email);
    - free(__tokenid);
    - free(__tokenval);
    -
    - gg_debug(GG_DEBUG_MISC, "=> change, %s\n", form);
    -
    - query = gg_saprintf(
    - "Host: " GG_REGISTER_HOST "\r\n"
    - "Content-Type: application/x-www-form-urlencoded\r\n"
    - "User-Agent: " GG_HTTP_USERAGENT "\r\n"
    - "Content-Length: %d\r\n"
    - "Pragma: no-cache\r\n"
    - "\r\n"
    - "%s",
    - (int) strlen(form), form);
    -
    - free(form);
    -
    - if (!query) {
    - gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for query\n");
    - return NULL;
    - }
    -
    - if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async,
    - "POST", "/appsvc/fmregister3.asp", query)))
    - {
    - gg_debug(GG_DEBUG_MISC, "=> change, gg_http_connect() failed mysteriously\n");
    - free(query);
    - return NULL;
    - }
    -
    - h->type = GG_SESSION_PASSWD;
    -
    - free(query);
    -
    - h->callback = gg_pubdir_watch_fd;
    - h->destroy = gg_pubdir_free;
    -
    - if (!async)
    - gg_pubdir_watch_fd(h);
    -
    - return h;
    -}
    -
    -#ifdef DOXYGEN
    -
    -/**
    - * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
    - *
    - * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
    - * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
    - * znajdzie się w polu \c error.
    - *
    - * \note W rzeczywistości funkcja jest makrem rozwijanym do
    - * \c gg_pubdir_watch_fd().
    - *
    - * \param h Struktura połączenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup passwd
    - */
    -int gg_change_passwd_watch_fd(struct gg_httpd *h)
    -{
    - return gg_pubdir_watch_fd(h);
    -}
    -
    -/**
    - * Zwalnia zasoby po operacji.
    - *
    - * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
    - *
    - * \param h Struktura połączenia
    - *
    - * \ingroup passwd
    - */
    -void gg_change_passwd_free(struct gg_http *h)
    -{
    - return gg_pubdir_free(h);
    -}
    -
    -#endif /* DOXYGEN */
    -
    -/**
    - * Wysyła hasło użytkownika na e-mail.
    - *
    - * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
    - *
    - * \param uin Numer Gadu-Gadu
    - * \param email Adres e-mail (podany przy rejestracji)
    - * \param tokenid Identyfikator tokenu
    - * \param tokenval Zawartość tokenu
    - * \param async Flaga połączenia asynchronicznego
    - *
    - * \return Struktura \c gg_http lub \c NULL w przypadku błędu
    - *
    - * \ingroup remind
    - */
    -struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async)
    -{
    - struct gg_http *h;
    - char *form, *query, *__tokenid, *__tokenval, *__email;
    -
    - if (!tokenid || !tokenval || !email) {
    - gg_debug(GG_DEBUG_MISC, "=> remind, NULL parameter\n");
    - errno = EFAULT;
    - return NULL;
    - }
    -
    - __tokenid = gg_urlencode(tokenid);
    - __tokenval = gg_urlencode(tokenval);
    - __email = gg_urlencode(email);
    -
    - if (!__tokenid || !__tokenval || !__email) {
    - gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for form fields\n");
    - free(__tokenid);
    - free(__tokenval);
    - free(__email);
    - return NULL;
    - }
    -
    - if (!(form = gg_saprintf("userid=%d&code=%u&tokenid=%s&tokenval=%s&"
    - "email=%s", uin, gg_http_hash("u", uin), __tokenid, __tokenval,
    - __email)))
    - {
    - gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for form fields\n");
    - free(__tokenid);
    - free(__tokenval);
    - free(__email);
    - return NULL;
    - }
    -
    - free(__tokenid);
    - free(__tokenval);
    - free(__email);
    -
    - gg_debug(GG_DEBUG_MISC, "=> remind, %s\n", form);
    -
    - query = gg_saprintf(
    - "Host: " GG_REMIND_HOST "\r\n"
    - "Content-Type: application/x-www-form-urlencoded\r\n"
    - "User-Agent: " GG_HTTP_USERAGENT "\r\n"
    - "Content-Length: %d\r\n"
    - "Pragma: no-cache\r\n"
    - "\r\n"
    - "%s",
    - (int) strlen(form), form);
    -
    - free(form);
    -
    - if (!query) {
    - gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for query\n");
    - return NULL;
    - }
    -
    - if (!(h = gg_http_connect(GG_REMIND_HOST, GG_REMIND_PORT, async, "POST", "/appsvc/fmsendpwd3.asp", query))) {
    - gg_debug(GG_DEBUG_MISC, "=> remind, gg_http_connect() failed mysteriously\n");
    - free(query);
    - return NULL;
    - }
    -
    - h->type = GG_SESSION_REMIND;
    -
    - free(query);
    -
    - h->callback = gg_pubdir_watch_fd;
    - h->destroy = gg_pubdir_free;
    -
    - if (!async)
    - gg_pubdir_watch_fd(h);
    -
    - return h;
    -}
    -
    -#ifdef DOXYGEN
    -
    -/**
    - * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
    - *
    - * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
    - * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
    - * znajdzie się w polu \c error.
    - *
    - * \note W rzeczywistości funkcja jest makrem rozwijanym do
    - * \c gg_pubdir_watch_fd().
    - *
    - * \param h Struktura połączenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup remind
    - */
    -int gg_remind_watch_fd(struct gg_httpd *h)
    -{
    - return gg_pubdir_watch_fd(h);
    -}
    -
    -/**
    - * Zwalnia zasoby po operacji.
    - *
    - * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
    - *
    - * \param h Struktura połączenia
    - *
    - * \ingroup remind
    - */
    -void gg_remind_free(struct gg_http *h)
    -{
    - return gg_pubdir_free(h);
    -}
    -
    -#endif /* DOXYGEN */
    -
    -/**
    - * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
    - *
    - * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
    - * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
    - * znajdzie się w polu \c error.
    - *
    - * \param h Struktura połączenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -int gg_pubdir_watch_fd(struct gg_http *h)
    -{
    - struct gg_pubdir *p;
    - char *tmp;
    -
    - if (!h) {
    - errno = EFAULT;
    - return -1;
    - }
    -
    - if (h->state == GG_STATE_ERROR) {
    - gg_debug(GG_DEBUG_MISC, "=> pubdir, watch_fd issued on failed session\n");
    - errno = EINVAL;
    - return -1;
    - }
    -
    - if (h->state != GG_STATE_PARSING) {
    - if (gg_http_watch_fd(h) == -1) {
    - gg_debug(GG_DEBUG_MISC, "=> pubdir, http failure\n");
    - errno = EINVAL;
    - return -1;
    - }
    - }
    -
    - if (h->state != GG_STATE_PARSING)
    - return 0;
    -
    - h->state = GG_STATE_DONE;
    -
    - if (!(h->data = p = malloc(sizeof(struct gg_pubdir)))) {
    - gg_debug(GG_DEBUG_MISC, "=> pubdir, not enough memory for results\n");
    - return -1;
    - }
    -
    - p->success = 0;
    - p->uin = 0;
    -
    - gg_debug(GG_DEBUG_MISC, "=> pubdir, let's parse \"%s\"\n", h->body);
    -
    - if ((tmp = strstr(h->body, "Tokens okregisterreply_packet.reg.dwUserId="))) {
    - p->success = 1;
    - p->uin = strtol(tmp + sizeof("Tokens okregisterreply_packet.reg.dwUserId=") - 1, NULL, 0);
    - p->error = GG_PUBDIR_ERROR_NONE;
    - gg_debug(GG_DEBUG_MISC, "=> pubdir, success (okregisterreply, uin=%d)\n", p->uin);
    - } else if ((tmp = strstr(h->body, "success")) || (tmp = strstr(h->body, "results"))) {
    - p->success = 1;
    - if (tmp[7] == ':')
    - p->uin = strtol(tmp + 8, NULL, 0);
    - p->error = GG_PUBDIR_ERROR_NONE;
    - gg_debug(GG_DEBUG_MISC, "=> pubdir, success (uin=%d)\n", p->uin);
    - } else if (strncmp(h->body, "error1", 6) == 0 || strncmp(h->body, "error3", 6) == 0) {
    - p->error = GG_PUBDIR_ERROR_NEW_PASSWORD;
    - gg_debug(GG_DEBUG_MISC, "=> pubdir, invalid new password\n");
    - } else if (strncmp(h->body, "not authenticated", 17) == 0) {
    - p->error = GG_PUBDIR_ERROR_OLD_PASSWORD;
    - gg_debug(GG_DEBUG_MISC, "=> pubdir, invalid old password\n");
    - } else if (strncmp(h->body, "bad_tokenval", 12) == 0) {
    - p->error = GG_PUBDIR_ERROR_TOKEN;
    - gg_debug(GG_DEBUG_MISC, "=> pubdir, invalid token\n");
    - } else {
    - p->error = GG_PUBDIR_ERROR_OTHER;
    - gg_debug(GG_DEBUG_MISC, "=> pubdir, unknown error\n");
    - }
    -
    - return 0;
    -}
    -
    -/**
    - * Zwalnia zasoby po operacji na katalogu publicznym.
    - *
    - * \param h Struktura połączenia
    - */
    -void gg_pubdir_free(struct gg_http *h)
    -{
    - if (!h)
    - return;
    -
    - free(h->data);
    - gg_http_free(h);
    -}
    -
    -/**
    - * Pobiera token do autoryzacji operacji na katalogu publicznym.
    - *
    - * Token jest niezbędny do tworzenia nowego i usuwania użytkownika,
    - * zmiany hasła itd.
    - *
    - * \param async Flaga połączenia asynchronicznego
    - *
    - * \return Struktura \c gg_http lub \c NULL w przypadku błędu
    - *
    - * \ingroup token
    - */
    -struct gg_http *gg_token(int async)
    -{
    - struct gg_http *h;
    - const char *query;
    -
    - query = "Host: " GG_REGISTER_HOST "\r\n"
    - "Content-Type: application/x-www-form-urlencoded\r\n"
    - "User-Agent: " GG_HTTP_USERAGENT "\r\n"
    - "Content-Length: 0\r\n"
    - "Pragma: no-cache\r\n"
    - "\r\n";
    -
    - if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/regtoken.asp", query))) {
    - gg_debug(GG_DEBUG_MISC, "=> token, gg_http_connect() failed mysteriously\n");
    - return NULL;
    - }
    -
    - h->type = GG_SESSION_TOKEN;
    -
    - h->callback = gg_token_watch_fd;
    - h->destroy = gg_token_free;
    -
    - if (!async)
    - gg_token_watch_fd(h);
    -
    - return h;
    -}
    -
    -/**
    - * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
    - *
    - * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
    - * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
    - * znajdzie się w polu \c error.
    - *
    - * \param h Struktura połączenia
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup token
    - */
    -int gg_token_watch_fd(struct gg_http *h)
    -{
    - if (!h) {
    - errno = EFAULT;
    - return -1;
    - }
    -
    - if (h->state == GG_STATE_ERROR) {
    - gg_debug(GG_DEBUG_MISC, "=> token, watch_fd issued on failed session\n");
    - errno = EINVAL;
    - return -1;
    - }
    -
    - if (h->state != GG_STATE_PARSING) {
    - if (gg_http_watch_fd(h) == -1) {
    - gg_debug(GG_DEBUG_MISC, "=> token, http failure\n");
    - errno = EINVAL;
    - return -1;
    - }
    - }
    -
    - if (h->state != GG_STATE_PARSING)
    - return 0;
    -
    - /* jeśli h->data jest puste, to ściągaliśmy tokenid i url do niego,
    - * ale jeśli coś tam jest, to znaczy, że mamy drugi etap polegający
    - * na pobieraniu tokenu. */
    - if (!h->data) {
    - int width, height, length;
    - char *url = NULL, *tokenid = NULL, *path, *headers;
    - const char *host;
    - struct gg_http *h2;
    - struct gg_token *t;
    - size_t results_len;
    -
    - gg_debug(GG_DEBUG_MISC, "=> token body \"%s\"\n", h->body);
    -
    - results_len = h->body ? strlen(h->body) : 0;
    -
    - if (h->body && (!(url = malloc(results_len)) || !(tokenid = malloc(results_len)))) {
    - gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for results\n");
    - free(url);
    - return -1;
    - }
    -
    - if (!h->body || sscanf(h->body, "%d %d %d\r\n%s\r\n%s", &width, &height, &length, tokenid, url) != 5) {
    - gg_debug(GG_DEBUG_MISC, "=> token, parsing failed\n");
    - free(url);
    - free(tokenid);
    - errno = EINVAL;
    - return -1;
    - }
    -
    - /* dostaliśmy tokenid i wszystkie niezbędne informacje,
    - * więc pobierzmy obrazek z tokenem */
    -
    - if (strncmp(url, "http://", 7)) {
    - path = gg_saprintf("%s?tokenid=%s", url, tokenid);
    - host = GG_REGISTER_HOST;
    - } else {
    - char *slash = strchr(url + 7, '/');
    -
    - if (slash) {
    - path = gg_saprintf("%s?tokenid=%s", slash, tokenid);
    - *slash = 0;
    - host = url + 7;
    - } else {
    - gg_debug(GG_DEBUG_MISC, "=> token, url parsing failed\n");
    - free(url);
    - free(tokenid);
    - errno = EINVAL;
    - return -1;
    - }
    - }
    -
    - if (!path) {
    - gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token url\n");
    - free(url);
    - free(tokenid);
    - return -1;
    - }
    -
    - if (!(headers = gg_saprintf("Host: %s\r\nUser-Agent: " GG_HTTP_USERAGENT "\r\n\r\n", host))) {
    - gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token url\n");
    - free(path);
    - free(url);
    - free(tokenid);
    - return -1;
    - }
    -
    - if (!(h2 = gg_http_connect(host, GG_REGISTER_PORT, h->async, "GET", path, headers))) {
    - gg_debug(GG_DEBUG_MISC, "=> token, gg_http_connect() failed mysteriously\n");
    - free(headers);
    - free(url);
    - free(path);
    - free(tokenid);
    - return -1;
    - }
    -
    - free(headers);
    - free(path);
    - free(url);
    -
    - gg_http_free_fields(h);
    -
    - memcpy(h, h2, sizeof(struct gg_http));
    - free(h2);
    -
    - h->type = GG_SESSION_TOKEN;
    -
    - h->callback = gg_token_watch_fd;
    - h->destroy = gg_token_free;
    -
    - if (!h->async)
    - gg_token_watch_fd(h);
    -
    - if (!(h->data = t = malloc(sizeof(struct gg_token)))) {
    - gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token data\n");
    - free(tokenid);
    - return -1;
    - }
    -
    - t->width = width;
    - t->height = height;
    - t->length = length;
    - t->tokenid = tokenid;
    - } else {
    - /* obrazek mamy w h->body */
    - h->state = GG_STATE_DONE;
    - }
    -
    - return 0;
    -}
    -
    -/**
    - * Zwalnia zasoby po operacji pobierania tokenu.
    - *
    - * \param h Struktura połączenia
    - *
    - * \ingroup token
    - */
    -void gg_token_free(struct gg_http *h)
    -{
    - struct gg_token *t;
    -
    - if (!h)
    - return;
    -
    - if ((t = h->data))
    - free(t->tokenid);
    -
    - free(h->data);
    - gg_http_free(h);
    -}
    -
    -/*
    - * Local variables:
    - * c-indentation-style: k&r
    - * c-basic-offset: 8
    - * indent-tabs-mode: notnil
    - * End:
    - *
    - * vim: shiftwidth=8:
    - */
    --- a/libpurple/protocols/gg/lib/pubdir50.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,555 +0,0 @@
    -/*
    - * (C) Copyright 2003 Wojtek Kaniewski <wojtekka@irc.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file pubdir50.c
    - *
    - * \brief Obsługa katalogu publicznego od wersji Gadu-Gadu 5.x
    - *
    - * \todo Zoptymalizować konwersję CP1250<->UTF8. Obecnie robiona jest
    - * testowa konwersja, żeby poznać długość tekstu wynikowego.
    - */
    -
    -#include "network.h"
    -#include "strman.h"
    -#include <errno.h>
    -#include <stdlib.h>
    -#include <string.h>
    -#include <time.h>
    -
    -#include "libgadu.h"
    -#include "internal.h"
    -#include "encoding.h"
    -
    -/**
    - * Tworzy nowe zapytanie katalogu publicznego.
    - *
    - * \param type Rodzaj zapytania
    - *
    - * \return Zmienna \c gg_pubdir50_t lub \c NULL w przypadku błędu.
    - *
    - * \ingroup pubdir50
    - */
    -gg_pubdir50_t gg_pubdir50_new(int type)
    -{
    - gg_pubdir50_t res = malloc(sizeof(struct gg_pubdir50_s));
    -
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_new(%d);\n", type);
    -
    - if (!res) {
    - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_new() out of memory\n");
    - return NULL;
    - }
    -
    - memset(res, 0, sizeof(struct gg_pubdir50_s));
    -
    - res->type = type;
    -
    - return res;
    -}
    -
    -/**
    - * \internal Dodaje lub zastępuje pole zapytania lub odpowiedzi katalogu
    - * publicznego.
    - *
    - * \param req Zapytanie lub odpowiedź
    - * \param num Numer wyniku odpowiedzi (0 dla zapytania)
    - * \param field Nazwa pola
    - * \param value Wartość pola
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -static int gg_pubdir50_add_n(gg_pubdir50_t req, int num, const char *field, const char *value)
    -{
    - struct gg_pubdir50_entry *tmp = NULL, *entry;
    - char *dupfield, *dupvalue;
    - int i;
    -
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_add_n(%p, %d, \"%s\", \"%s\");\n", req, num, field, value);
    -
    - if (!(dupvalue = strdup(value))) {
    - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
    - return -1;
    - }
    -
    - for (i = 0; i < req->entries_count; i++) {
    - if (req->entries[i].num != num || strcmp(req->entries[i].field, field))
    - continue;
    -
    - free(req->entries[i].value);
    - req->entries[i].value = dupvalue;
    -
    - return 0;
    - }
    -
    - if (!(dupfield = strdup(field))) {
    - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
    - free(dupvalue);
    - return -1;
    - }
    -
    - if (!(tmp = realloc(req->entries, sizeof(struct gg_pubdir50_entry) * (req->entries_count + 1)))) {
    - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
    - free(dupfield);
    - free(dupvalue);
    - return -1;
    - }
    -
    - req->entries = tmp;
    -
    - entry = &req->entries[req->entries_count];
    - entry->num = num;
    - entry->field = dupfield;
    - entry->value = dupvalue;
    -
    - req->entries_count++;
    -
    - return 0;
    -}
    -
    -/**
    - * Dodaje pole zapytania.
    - *
    - * \param req Zapytanie
    - * \param field Nazwa pola
    - * \param value Wartość pola
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup pubdir50
    - */
    -int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value)
    -{
    - return gg_pubdir50_add_n(req, 0, field, value);
    -}
    -
    -/**
    - * Ustawia numer sekwencyjny zapytania.
    - *
    - * \param req Zapytanie
    - * \param seq Numer sekwencyjny
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - *
    - * \ingroup pubdir50
    - */
    -int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq)
    -{
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_seq_set(%p, %d);\n", req, seq);
    -
    - if (!req) {
    - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_seq_set() invalid arguments\n");
    - errno = EFAULT;
    - return -1;
    - }
    -
    - req->seq = seq;
    -
    - return 0;
    -}
    -
    -/**
    - * Zwalnia zasoby po zapytaniu lub odpowiedzi katalogu publicznego.
    - *
    - * \param s Zapytanie lub odpowiedź
    - *
    - * \ingroup pubdir50
    - */
    -void gg_pubdir50_free(gg_pubdir50_t s)
    -{
    - int i;
    -
    - if (!s)
    - return;
    -
    - for (i = 0; i < s->entries_count; i++) {
    - free(s->entries[i].field);
    - free(s->entries[i].value);
    - }
    -
    - free(s->entries);
    - free(s);
    -}
    -
    -/**
    - * Wysyła zapytanie katalogu publicznego do serwera.
    - *
    - * \param sess Struktura sesji
    - * \param req Zapytanie
    - *
    - * \return Numer sekwencyjny zapytania lub 0 w przypadku błędu
    - *
    - * \ingroup pubdir50
    - */
    -uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
    -{
    - int i, size = 5;
    - uint32_t res;
    - char *buf, *p;
    - struct gg_pubdir50_request *r;
    -
    - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req);
    -
    - if (!sess || !req) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n");
    - errno = EFAULT;
    - return 0;
    - }
    -
    - if (sess->state != GG_STATE_CONNECTED) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() not connected\n");
    - errno = ENOTCONN;
    - return 0;
    - }
    -
    - for (i = 0; i < req->entries_count; i++) {
    - /* wyszukiwanie bierze tylko pierwszy wpis */
    - if (req->entries[i].num)
    - continue;
    -
    - if (sess->encoding == GG_ENCODING_CP1250) {
    - size += strlen(req->entries[i].field) + 1;
    - size += strlen(req->entries[i].value) + 1;
    - } else {
    - char *tmp;
    -
    - /* XXX \todo zoptymalizować */
    - tmp = gg_encoding_convert(req->entries[i].field, sess->encoding, GG_ENCODING_CP1250, -1, -1);
    -
    - if (tmp == NULL)
    - return -1;
    -
    - size += strlen(tmp) + 1;
    -
    - free(tmp);
    -
    - /* XXX \todo zoptymalizować */
    - tmp = gg_encoding_convert(req->entries[i].value, sess->encoding, GG_ENCODING_CP1250, -1, -1);
    -
    - if (tmp == NULL)
    - return -1;
    -
    - size += strlen(tmp) + 1;
    -
    - free(tmp);
    - }
    - }
    -
    - if (!(buf = malloc(size))) {
    - gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size);
    - return 0;
    - }
    -
    - if (!req->seq)
    - req->seq = time(NULL);
    -
    - res = req->seq;
    -
    - r = (struct gg_pubdir50_request*) buf;
    - r->type = req->type;
    - r->seq = gg_fix32(req->seq);
    -
    - for (i = 0, p = buf + 5; i < req->entries_count; i++) {
    - if (req->entries[i].num)
    - continue;
    -
    - if (sess->encoding == GG_ENCODING_CP1250) {
    - strcpy(p, req->entries[i].field);
    - p += strlen(p) + 1;
    -
    - strcpy(p, req->entries[i].value);
    - p += strlen(p) + 1;
    - } else {
    - char *tmp;
    -
    - /* XXX \todo zoptymalizować */
    - tmp = gg_encoding_convert(req->entries[i].field, sess->encoding, GG_ENCODING_CP1250, -1, -1);
    -
    - if (tmp == NULL) {
    - free(buf);
    - return -1;
    - }
    -
    - strcpy(p, tmp);
    - p += strlen(tmp) + 1;
    - free(tmp);
    -
    - /* XXX \todo zoptymalizować */
    - tmp = gg_encoding_convert(req->entries[i].value, sess->encoding, GG_ENCODING_CP1250, -1, -1);
    -
    -
    - if (tmp == NULL) {
    - free(buf);
    - return -1;
    - }
    -
    - strcpy(p, tmp);
    - p += strlen(tmp) + 1;
    - free(tmp);
    - }
    - }
    -
    - if (gg_send_packet(sess, GG_PUBDIR50_REQUEST, buf, size, NULL, 0) == -1)
    - res = 0;
    -
    - free(buf);
    -
    - return res;
    -}
    -
    -/*
    - * \internal Analizuje przychodzący pakiet odpowiedzi i zapisuje wynik
    - * w strukturze \c gg_event.
    - *
    - * \param sess Struktura sesji
    - * \param e Struktura zdarzenia
    - * \param packet Pakiet odpowiedzi
    - * \param length Długość pakietu odpowiedzi
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length)
    -{
    - const char *end = packet + length, *p;
    - const struct gg_pubdir50_reply *r = (const struct gg_pubdir50_reply*) packet;
    - gg_pubdir50_t res;
    - int num = 0;
    -
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply_sess(%p, %p, %p, %d);\n", sess, e, packet, length);
    -
    - if (!sess || !e || !packet) {
    - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() invalid arguments\n");
    - errno = EFAULT;
    - return -1;
    - }
    -
    - if (length < 5) {
    - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() packet too short\n");
    - errno = EINVAL;
    - return -1;
    - }
    -
    - if (!(res = gg_pubdir50_new(r->type))) {
    - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() unable to allocate reply\n");
    - return -1;
    - }
    -
    - e->event.pubdir50 = res;
    -
    - res->seq = gg_fix32(r->seq);
    -
    - switch (res->type) {
    - case GG_PUBDIR50_READ:
    - e->type = GG_EVENT_PUBDIR50_READ;
    - break;
    -
    - case GG_PUBDIR50_WRITE:
    - e->type = GG_EVENT_PUBDIR50_WRITE;
    - break;
    -
    - default:
    - e->type = GG_EVENT_PUBDIR50_SEARCH_REPLY;
    - break;
    - }
    -
    - /* brak wyników? */
    - if (length == 5)
    - return 0;
    -
    - /* pomiń początek odpowiedzi */
    - p = packet + 5;
    -
    - while (p < end) {
    - const char *field, *value;
    -
    - field = p;
    -
    - /* sprawdź, czy nie mamy podziału na kolejne pole */
    - if (!*field) {
    - num++;
    - field++;
    - }
    -
    - value = NULL;
    -
    - for (p = field; p < end; p++) {
    - /* jeśli mamy koniec tekstu... */
    - if (!*p) {
    - /* ...i jeszcze nie mieliśmy wartości pola to
    - * wiemy, że po tym zerze jest wartość... */
    - if (!value)
    - value = p + 1;
    - else
    - /* ...w przeciwym wypadku koniec
    - * wartości i możemy wychodzić
    - * grzecznie z pętli */
    - break;
    - }
    - }
    -
    - /* sprawdźmy, czy pole nie wychodzi poza pakiet, żeby nie
    - * mieć segfaultów, jeśli serwer przestanie zakańczać pakietów
    - * przez \0 */
    -
    - if (p == end) {
    - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() premature end of packet\n");
    - goto failure;
    - }
    -
    - p++;
    -
    - /* jeśli dostaliśmy namier na następne wyniki, to znaczy że
    - * mamy koniec wyników i nie jest to kolejna osoba. */
    - if (!strcasecmp(field, "nextstart")) {
    - res->next = value ? atoi(value) : 0;
    - num--;
    - } else {
    - if (sess->encoding == GG_ENCODING_CP1250) {
    - if (gg_pubdir50_add_n(res, num, field, value) == -1)
    - goto failure;
    - } else {
    - char *tmp;
    -
    - tmp = gg_encoding_convert(value, GG_ENCODING_CP1250, sess->encoding, -1, -1);
    -
    - if (tmp == NULL)
    - goto failure;
    -
    - if (gg_pubdir50_add_n(res, num, field, tmp) == -1) {
    - free(tmp);
    - goto failure;
    - }
    -
    - free(tmp);
    - }
    - }
    - }
    -
    - res->count = num + 1;
    -
    - return 0;
    -
    -failure:
    - gg_pubdir50_free(res);
    - return -1;
    -}
    -
    -/**
    - * Pobiera pole z odpowiedzi katalogu publicznego.
    - *
    - * \param res Odpowiedź
    - * \param num Numer wyniku odpowiedzi
    - * \param field Nazwa pola (wielkość liter nie ma znaczenia)
    - *
    - * \return Wartość pola lub \c NULL jeśli nie znaleziono
    - *
    - * \ingroup pubdir50
    - */
    -const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field)
    -{
    - char *value = NULL;
    - int i;
    -
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_get(%p, %d, \"%s\");\n", res, num, field);
    -
    - if (!res || num < 0 || !field) {
    - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_get() invalid arguments\n");
    - errno = EINVAL;
    - return NULL;
    - }
    -
    - for (i = 0; i < res->entries_count; i++) {
    - if (res->entries[i].num == num && !strcasecmp(res->entries[i].field, field)) {
    - value = res->entries[i].value;
    - break;
    - }
    - }
    -
    - return value;
    -}
    -
    -/**
    - * Zwraca liczbę wyników odpowiedzi.
    - *
    - * \param res Odpowiedź
    - *
    - * \return Liczba wyników lub -1 w przypadku błędu
    - *
    - * \ingroup pubdir50
    - */
    -int gg_pubdir50_count(gg_pubdir50_t res)
    -{
    - return (!res) ? -1 : res->count;
    -}
    -
    -/**
    - * Zwraca rodzaj zapytania lub odpowiedzi.
    - *
    - * \param res Zapytanie lub odpowiedź
    - *
    - * \return Rodzaj lub -1 w przypadku błędu
    - *
    - * \ingroup pubdir50
    - */
    -int gg_pubdir50_type(gg_pubdir50_t res)
    -{
    - return (!res) ? -1 : res->type;
    -}
    -
    -/**
    - * Zwraca numer, od którego należy rozpocząc kolejne wyszukiwanie.
    - *
    - * Dłuższe odpowiedzi katalogu publicznego są wysyłane przez serwer
    - * w mniejszych paczkach. Po otrzymaniu odpowiedzi, jeśli numer kolejnego
    - * wyszukiwania jest większy od zera, dalsze wyniki można otrzymać przez
    - * wywołanie kolejnego zapytania z określonym numerem początkowym.
    - *
    - * \param res Odpowiedź
    - *
    - * \return Numer lub -1 w przypadku błędu
    - *
    - * \ingroup pubdir50
    - */
    -uin_t gg_pubdir50_next(gg_pubdir50_t res)
    -{
    - return (!res) ? (unsigned) -1 : res->next;
    -}
    -
    -/**
    - * Zwraca numer sekwencyjny zapytania lub odpowiedzi.
    - *
    - * \param res Zapytanie lub odpowiedź
    - *
    - * \return Numer sekwencyjny lub -1 w przypadku błędu
    - *
    - * \ingroup pubdir50
    - */
    -uint32_t gg_pubdir50_seq(gg_pubdir50_t res)
    -{
    - return (!res) ? (unsigned) -1 : res->seq;
    -}
    -
    -/*
    - * Local variables:
    - * c-indentation-style: k&r
    - * c-basic-offset: 8
    - * indent-tabs-mode: notnil
    - * End:
    - *
    - * vim: shiftwidth=8:
    - */
    --- a/libpurple/protocols/gg/lib/resolver.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,1132 +0,0 @@
    -/*
    - * (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl>
    - * Robert J. Woźny <speedy@ziew.org>
    - * Arkadiusz Miśkiewicz <arekm@pld-linux.org>
    - * Tomasz Chiliński <chilek@chilan.com>
    - * Adam Wysocki <gophi@ekg.chmurka.net>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file resolver.c
    - *
    - * \brief Funkcje rozwiązywania nazw
    - */
    -
    -#include <errno.h>
    -#include <stdlib.h>
    -#include <string.h>
    -
    -#include "strman.h"
    -#include "network.h"
    -#include "config.h"
    -#include "libgadu.h"
    -#include "resolver.h"
    -#include "session.h"
    -
    -#ifdef GG_CONFIG_HAVE_FORK
    -#include <sys/wait.h>
    -#include <signal.h>
    -#endif
    -
    -/** Sposób rozwiązywania nazw serwerów */
    -static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT;
    -
    -/** Funkcja rozpoczynająca rozwiązywanie nazwy */
    -static int (*gg_global_resolver_start)(int *fd, void **private_data, const char *hostname);
    -
    -/** Funkcja zwalniająca zasoby po rozwiązaniu nazwy */
    -static void (*gg_global_resolver_cleanup)(void **private_data, int force);
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    -
    -#include <pthread.h>
    -
    -/**
    - * \internal Funkcja pomocnicza zwalniająca zasoby po rozwiązywaniu nazwy
    - * w wątku.
    - *
    - * \note Funkcja nie powinna być statyczna, ponieważ zostanie potraktowana
    - * jako inline i kompilator może "zoptymalizować" jej wywołanie w funkcji
    - * pthread_cleanup_pop().
    - *
    - * \param data Wskaźnik na wskaźnik bufora zaalokowanego w wątku
    - */
    -void gg_resolver_cleaner(void *data)
    -{
    - void **buf_ptr = (void **) data;
    -
    - if (buf_ptr != NULL) {
    - free(*buf_ptr);
    - *buf_ptr = NULL;
    - }
    -}
    -
    -#endif /* GG_CONFIG_HAVE_PTHREAD */
    -
    -/**
    - * \internal Odpowiednik \c gethostbyname zapewniający współbieżność.
    - *
    - * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli
    - * nie, to zwykłej \c gethostbyname. Wynikiem jest tablica adresów zakończona
    - * wartością INADDR_NONE, którą należy zwolnić po użyciu.
    - *
    - * \param hostname Nazwa serwera
    - * \param result Wskaźnik na wskaźnik z tablicą adresów zakończoną INADDR_NONE
    - * \param count Wskaźnik na zmienną, do ktorej zapisze się liczbę wyników
    - * \param pthread Flaga blokowania unicestwiania wątku podczas alokacji pamięci
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -int gg_gethostbyname_real(const char *hostname, struct in_addr **result, unsigned int *count, int pthread)
    -{
    -#ifdef GG_CONFIG_HAVE_GETHOSTBYNAME_R
    - char *buf = NULL;
    - char *new_buf = NULL;
    - struct hostent he;
    - struct hostent *he_ptr = NULL;
    - size_t buf_len = 1024;
    - int res = -1;
    - int h_errnop;
    - int ret = 0;
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - int old_state;
    -#endif
    -
    - if (result == NULL) {
    - errno = EINVAL;
    - return -1;
    - }
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - pthread_cleanup_push(gg_resolver_cleaner, &buf);
    -
    - if (pthread)
    - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
    -#endif
    -
    - buf = malloc(buf_len);
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - if (pthread)
    - pthread_setcancelstate(old_state, NULL);
    -#endif
    -
    - if (buf != NULL) {
    - while (1) {
    -#ifndef sun
    - ret = gethostbyname_r(hostname, &he, buf, buf_len, &he_ptr, &h_errnop);
    - if (ret != ERANGE)
    - break;
    -#else
    - he_ptr = gethostbyname_r(hostname, &he, buf, buf_len, &h_errnop);
    - if (he_ptr != NULL || errno != ERANGE)
    - break;
    -#endif
    -
    - buf_len *= 2;
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - if (pthread)
    - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
    -#endif
    -
    - new_buf = realloc(buf, buf_len);
    -
    - if (new_buf != NULL)
    - buf = new_buf;
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - if (pthread)
    - pthread_setcancelstate(old_state, NULL);
    -#endif
    -
    - if (new_buf == NULL) {
    - ret = ENOMEM;
    - break;
    - }
    - }
    -
    - if (ret == 0 && he_ptr != NULL && he_ptr->h_addr_list[0] != NULL) {
    - int i;
    -
    - /* Policz liczbę adresów */
    -
    - for (i = 0; he_ptr->h_addr_list[i] != NULL; i++);
    -
    - /* Zaalokuj */
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - if (pthread)
    - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
    -#endif
    -
    - *result = malloc((i + 1) * sizeof(struct in_addr));
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - if (pthread)
    - pthread_setcancelstate(old_state, NULL);
    -#endif
    -
    - if (*result != NULL) {
    - /* Kopiuj */
    -
    - for (i = 0; he_ptr->h_addr_list[i] != NULL; i++)
    - memcpy(&((*result)[i]), he_ptr->h_addr_list[i], sizeof(struct in_addr));
    -
    - (*result)[i].s_addr = INADDR_NONE;
    -
    - *count = i;
    -
    - res = 0;
    - } else
    - res = -1;
    - }
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - if (pthread)
    - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
    -#endif
    -
    - free(buf);
    - buf = NULL;
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - if (pthread)
    - pthread_setcancelstate(old_state, NULL);
    -#endif
    - }
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - pthread_cleanup_pop(0);
    -#endif
    -
    - return res;
    -#else /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */
    - struct hostent *he;
    - int i;
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - int old_state;
    -#endif
    -
    - if (result == NULL || count == NULL) {
    - errno = EINVAL;
    - return -1;
    - }
    -
    - he = gethostbyname(hostname);
    -
    - if (he == NULL || he->h_addr_list[0] == NULL)
    - return -1;
    -
    - /* Policz liczbę adresów */
    -
    - for (i = 0; he->h_addr_list[i] != NULL; i++);
    -
    - /* Zaalokuj */
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - if (pthread)
    - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
    -#endif
    -
    - *result = malloc((i + 1) * sizeof(struct in_addr));
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - if (pthread)
    - pthread_setcancelstate(old_state, NULL);
    -#endif
    -
    - if (*result == NULL)
    - return -1;
    -
    - /* Kopiuj */
    -
    - for (i = 0; he->h_addr_list[i] != NULL; i++)
    - memcpy(&((*result)[i]), he->h_addr_list[i], sizeof(struct in_addr));
    -
    - (*result)[i].s_addr = INADDR_NONE;
    -
    - *count = i;
    -
    - return 0;
    -#endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */
    -}
    -
    -/**
    - * \internal Rozwiązuje nazwę i zapisuje wynik do podanego gniazda.
    - *
    - * \note Użycie logowania w tej funkcji może mieć negatywny wpływ na
    - * aplikacje jednowątkowe korzystające.
    - *
    - * \param fd Deskryptor gniazda
    - * \param hostname Nazwa serwera
    - * \param pthread Flaga blokowania unicestwiania wątku podczas alokacji pamięci
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -static int gg_resolver_run(int fd, const char *hostname, int pthread)
    -{
    - struct in_addr addr_ip[2], *addr_list = NULL;
    - unsigned int addr_count;
    - int res = 0;
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - int old_state;
    -#endif
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - pthread_cleanup_push(gg_resolver_cleaner, &addr_list);
    -#endif
    -
    - if ((addr_ip[0].s_addr = inet_addr(hostname)) == INADDR_NONE) {
    - if (gg_gethostbyname_real(hostname, &addr_list, &addr_count, pthread) == -1) {
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - if (pthread)
    - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
    -#endif
    -
    - free(addr_list);
    - addr_list = NULL;
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - if (pthread)
    - pthread_setcancelstate(old_state, NULL);
    -#endif
    -
    - addr_count = 0;
    - /* addr_ip[0] już zawiera INADDR_NONE */
    - }
    - } else {
    - addr_ip[1].s_addr = INADDR_NONE;
    - addr_count = 1;
    - }
    -
    - if (send(fd, addr_list != NULL ? addr_list : addr_ip,
    - (addr_count + 1) * sizeof(struct in_addr), 0) !=
    - (int)((addr_count + 1) * sizeof(struct in_addr)))
    - {
    - res = -1;
    - }
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - if (pthread)
    - pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state);
    -#endif
    -
    - free(addr_list);
    - addr_list = NULL;
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - if (pthread)
    - pthread_setcancelstate(old_state, NULL);
    -
    - pthread_cleanup_pop(0);
    -#endif
    -
    - return res;
    -}
    -
    -/**
    - * \internal Odpowiednik \c gethostbyname zapewniający współbieżność.
    - *
    - * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli
    - * nie, to zwykłej \c gethostbyname. Funkcja służy do zachowania zgodności
    - * ABI i służy do pobierania tylko pierwszego adresu -- pozostałe mogą
    - * zostać zignorowane przez aplikację.
    - *
    - * \param hostname Nazwa serwera
    - *
    - * \return Zaalokowana struktura \c in_addr lub NULL w przypadku błędu.
    - */
    -struct in_addr *gg_gethostbyname(const char *hostname)
    -{
    - struct in_addr *result;
    - unsigned int count;
    -
    - if (gg_gethostbyname_real(hostname, &result, &count, 0) == -1)
    - return NULL;
    -
    - return result;
    -}
    -
    -#ifdef GG_CONFIG_HAVE_FORK
    -
    -/**
    - * \internal Struktura przekazywana do wątku rozwiązującego nazwę.
    - */
    -struct gg_resolver_fork_data {
    - int pid; /*< Identyfikator procesu */
    -};
    -
    -/**
    - * \internal Rozwiązuje nazwę serwera w osobnym procesie.
    - *
    - * Połączenia asynchroniczne nie mogą blokować procesu w trakcie rozwiązywania
    - * nazwy serwera. W tym celu tworzona jest para gniazd, nowy proces i dopiero
    - * w nim przeprowadzane jest rozwiązywanie nazwy. Deskryptor gniazda do odczytu
    - * zapisuje się w strukturze sieci i czeka na dane w postaci struktury
    - * \c in_addr. Jeśli nie znaleziono nazwy, zwracana jest \c INADDR_NONE.
    - *
    - * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda
    - * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik
    - * do numeru procesu potomnego rozwiązującego nazwę
    - * \param hostname Nazwa serwera do rozwiązania
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -static int gg_resolver_fork_start(int *fd, void **priv_data, const char *hostname)
    -{
    - struct gg_resolver_fork_data *data = NULL;
    - int pipes[2], new_errno;
    -
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_fork_start(%p, %p, \"%s\");\n", fd, priv_data, hostname);
    -
    - if (fd == NULL || priv_data == NULL || hostname == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() invalid arguments\n");
    - errno = EFAULT;
    - return -1;
    - }
    -
    - data = malloc(sizeof(struct gg_resolver_fork_data));
    -
    - if (data == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() out of memory for resolver data\n");
    - return -1;
    - }
    -
    - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() unable "
    - "to create pipes (errno=%d, %s)\n",
    - errno, strerror(errno));
    - free(data);
    - return -1;
    - }
    -
    - data->pid = fork();
    -
    - if (data->pid == -1) {
    - new_errno = errno;
    - goto cleanup;
    - }
    -
    - if (data->pid == 0) {
    - int status;
    -
    - close(pipes[0]);
    -
    - status = (gg_resolver_run(pipes[1], hostname, 0) == -1) ? 1 : 0;
    -
    -#ifdef HAVE__EXIT
    - _exit(status);
    -#else
    - exit(status);
    -#endif
    - }
    -
    - close(pipes[1]);
    -
    - gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() %p\n", data);
    -
    - *fd = pipes[0];
    - *priv_data = data;
    -
    - return 0;
    -
    -cleanup:
    - free(data);
    - close(pipes[0]);
    - close(pipes[1]);
    -
    - errno = new_errno;
    -
    - return -1;
    -}
    -
    -/**
    - * \internal Usuwanie zasobów po procesie rozwiązywaniu nazwy.
    - *
    - * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu
    - * zasobów sesji podczas rozwiązywania nazwy.
    - *
    - * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych
    - * danych
    - * \param force Flaga usuwania zasobów przed zakończeniem działania
    - */
    -static void gg_resolver_fork_cleanup(void **priv_data, int force)
    -{
    - struct gg_resolver_fork_data *data;
    -
    - if (priv_data == NULL || *priv_data == NULL)
    - return;
    -
    - data = (struct gg_resolver_fork_data*) *priv_data;
    - *priv_data = NULL;
    -
    - if (force)
    - kill(data->pid, SIGKILL);
    -
    - /* we don't care about child's exit status, just want to clean it up */
    - (void)waitpid(data->pid, NULL, WNOHANG);
    -
    - free(data);
    -}
    -
    -#endif /* GG_CONFIG_HAVE_FORK */
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    -
    -/**
    - * \internal Struktura przekazywana do wątku rozwiązującego nazwę.
    - */
    -struct gg_resolver_pthread_data {
    - pthread_t thread; /*< Identyfikator wątku */
    - char *hostname; /*< Nazwa serwera */
    - int wfd; /*< Deskryptor do zapisu */
    -};
    -
    -/**
    - * \internal Usuwanie zasobów po wątku rozwiązywaniu nazwy.
    - *
    - * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu
    - * zasobów sesji podczas rozwiązywania nazwy.
    - *
    - * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych
    - * danych
    - * \param force Flaga usuwania zasobów przed zakończeniem działania
    - */
    -static void gg_resolver_pthread_cleanup(void **priv_data, int force)
    -{
    - struct gg_resolver_pthread_data *data;
    -
    - if (priv_data == NULL || *priv_data == NULL)
    - return;
    -
    - data = (struct gg_resolver_pthread_data *) *priv_data;
    - *priv_data = NULL;
    -
    - if (force)
    - pthread_cancel(data->thread);
    -
    - pthread_join(data->thread, NULL);
    -
    - close(data->wfd);
    - free(data->hostname);
    - free(data);
    -}
    -
    -/**
    - * \internal Wątek rozwiązujący nazwę.
    - *
    - * \param arg Wskaźnik na strukturę \c gg_resolver_pthread_data
    - */
    -static void *gg_resolver_pthread_thread(void *arg)
    -{
    - struct gg_resolver_pthread_data *data = arg;
    -
    - if (gg_resolver_run(data->wfd, data->hostname, 1) == -1)
    - pthread_exit((void*) -1);
    - else
    - pthread_exit(NULL);
    -
    - return NULL; /* żeby kompilator nie marudził */
    -}
    -
    -/**
    - * \internal Rozwiązuje nazwę serwera w osobnym wątku.
    - *
    - * Funkcja działa analogicznie do \c gg_resolver_fork_start(), z tą różnicą,
    - * że działa na wątkach, nie procesach. Jest dostępna wyłącznie gdy podczas
    - * kompilacji włączono odpowiednią opcję.
    - *
    - * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda
    - * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik
    - * do prywatnych danych wątku rozwiązującego nazwę
    - * \param hostname Nazwa serwera do rozwiązania
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -static int gg_resolver_pthread_start(int *fd, void **priv_data, const char *hostname)
    -{
    - struct gg_resolver_pthread_data *data = NULL;
    - int pipes[2], new_errno;
    -
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_pthread_start(%p, %p, \"%s\");\n", fd, priv_data, hostname);
    -
    - if (fd == NULL || priv_data == NULL || hostname == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() invalid arguments\n");
    - errno = EFAULT;
    - return -1;
    - }
    -
    - data = malloc(sizeof(struct gg_resolver_pthread_data));
    -
    - if (data == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory for resolver data\n");
    - return -1;
    - }
    -
    - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable "
    - "to create pipes (errno=%d, %s)\n",
    - errno, strerror(errno));
    - free(data);
    - return -1;
    - }
    -
    - data->hostname = strdup(hostname);
    -
    - if (data->hostname == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory\n");
    - new_errno = errno;
    - goto cleanup;
    - }
    -
    - data->wfd = pipes[1];
    -
    - if (pthread_create(&data->thread, NULL, gg_resolver_pthread_thread, data)) {
    - gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable to create thread\n");
    - new_errno = errno;
    - goto cleanup;
    - }
    -
    - gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() %p\n", data);
    -
    - *fd = pipes[0];
    - *priv_data = data;
    -
    - return 0;
    -
    -cleanup:
    - if (data != NULL)
    - free(data->hostname);
    -
    - free(data);
    -
    - close(pipes[0]);
    - close(pipes[1]);
    -
    - errno = new_errno;
    -
    - return -1;
    -}
    -
    -#endif /* GG_CONFIG_HAVE_PTHREAD */
    -
    -#ifdef _WIN32
    -
    -/**
    - * \internal Struktura przekazywana do wątku rozwiązującego nazwę.
    - */
    -struct gg_resolver_win32_data {
    - HANDLE thread; /*< Uchwyt wątku */
    - CRITICAL_SECTION mutex; /*< Semafor wątku */
    - char *hostname; /*< Nazwa serwera */
    - int wfd; /*< Deskryptor do zapisu */
    - int orphan; /*< Wątek powinien sam po sobie posprzątać */
    - int finished; /*< Wątek już skończył pracę */
    -};
    -
    -/**
    - * \internal Wątek rozwiązujący nazwę.
    - *
    - * \param arg Wskaźnik na strukturę \c gg_resolver_win32_data
    - */
    -static DWORD WINAPI gg_resolver_win32_thread(void *arg)
    -{
    - struct gg_resolver_win32_data *data = arg;
    - int result, is_orphan;
    -
    - result = gg_resolver_run(data->wfd, data->hostname, 0);
    -
    - EnterCriticalSection(&data->mutex);
    - is_orphan = data->orphan;
    - data->finished = 1;
    - LeaveCriticalSection(&data->mutex);
    -
    - if (is_orphan) {
    - CloseHandle(data->thread);
    - DeleteCriticalSection(&data->mutex);
    - close(data->wfd);
    - free(data->hostname);
    - free(data);
    - }
    -
    - ExitThread(result);
    - return 0; /* żeby kompilator nie marudził */
    -}
    -
    -/**
    - * \internal Rozwiązuje nazwę serwera w osobnym wątku.
    - *
    - * Funkcja działa analogicznie do \c gg_resolver_pthread_start(), z tą różnicą,
    - * że działa na wątkach Win32. Jest dostępna wyłącznie przy kompilacji dla
    - * systemu Windows.
    - *
    - * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda
    - * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik
    - * do prywatnych danych wątku rozwiązującego nazwę
    - * \param hostname Nazwa serwera do rozwiązania
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -static int gg_resolver_win32_start(int *fd, void **priv_data, const char *hostname)
    -{
    - struct gg_resolver_win32_data *data = NULL;
    - int pipes[2], new_errno;
    - CRITICAL_SECTION *mutex = NULL;
    -
    - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_win32_start(%p, %p, \"%s\");\n", fd, priv_data, hostname);
    -
    - if (fd == NULL || priv_data == NULL || hostname == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() invalid arguments\n");
    - errno = EFAULT;
    - return -1;
    - }
    -
    - data = malloc(sizeof(struct gg_resolver_win32_data));
    -
    - if (data == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() out of memory for resolver data\n");
    - return -1;
    - }
    -
    - data->orphan = 0;
    - data->finished = 0;
    -
    - if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pipes) == -1) {
    - gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() unable to "
    - "create pipes (errno=%d, %s)\n",
    - errno, strerror(errno));
    - free(data);
    - return -1;
    - }
    -
    - data->hostname = strdup(hostname);
    -
    - if (data->hostname == NULL) {
    - gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() out of memory\n");
    - new_errno = errno;
    - goto cleanup;
    - }
    -
    - data->wfd = pipes[1];
    -
    - mutex = &data->mutex;
    - InitializeCriticalSection(mutex);
    -
    - data->thread = CreateThread(NULL, 0, gg_resolver_win32_thread, data, 0, NULL);
    - if (!data->thread) {
    - gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() unable to create thread\n");
    - new_errno = errno;
    - goto cleanup;
    - }
    -
    - gg_debug(GG_DEBUG_MISC, "// gg_resolver_win32_start() %p\n", data);
    -
    - *fd = pipes[0];
    - *priv_data = data;
    -
    - return 0;
    -
    -cleanup:
    - if (data) {
    - free(data->hostname);
    - free(data);
    - }
    -
    - close(pipes[0]);
    - close(pipes[1]);
    -
    - if (mutex)
    - DeleteCriticalSection(mutex);
    -
    - errno = new_errno;
    -
    - return -1;
    -}
    -
    -/**
    - * \internal Usuwanie zasobów po wątku rozwiązywaniu nazwy.
    - *
    - * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu
    - * zasobów sesji podczas rozwiązywania nazwy.
    - *
    - * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych
    - * danych
    - * \param force Flaga usuwania zasobów przed zakończeniem działania
    - */
    -static void gg_resolver_win32_cleanup(void **priv_data, int force)
    -{
    - struct gg_resolver_win32_data *data;
    -
    - if (priv_data == NULL || *priv_data == NULL)
    - return;
    -
    - data = (struct gg_resolver_win32_data *) *priv_data;
    - *priv_data = NULL;
    -
    - if (WaitForSingleObject(data->thread, 0) == WAIT_TIMEOUT) {
    - int finished;
    - /* We cannot call TerminateThread here - it doesn't
    - * release critical section locks (see MSDN docs).
    - * if (force) TerminateThread(data->thread, 0);
    - */
    - EnterCriticalSection(&data->mutex);
    - finished = data->finished;
    - if (!finished)
    - data->orphan = 1;
    - LeaveCriticalSection(&data->mutex);
    - if (!finished)
    - return;
    - }
    -
    - CloseHandle(data->thread);
    - DeleteCriticalSection(&data->mutex);
    - close(data->wfd);
    - free(data->hostname);
    - free(data);
    -}
    -
    -#endif /* _WIN32 */
    -
    -/**
    - * Ustawia sposób rozwiązywania nazw w sesji.
    - *
    - * \param gs Struktura sesji
    - * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver)
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type)
    -{
    - GG_SESSION_CHECK(gs, -1);
    -
    - if (type == GG_RESOLVER_DEFAULT) {
    - if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) {
    - gs->resolver_type = gg_global_resolver_type;
    - gs->resolver_start = gg_global_resolver_start;
    - gs->resolver_cleanup = gg_global_resolver_cleanup;
    - return 0;
    - }
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - type = GG_RESOLVER_PTHREAD;
    -#elif defined(_WIN32)
    - type = GG_RESOLVER_WIN32;
    -#elif defined(GG_CONFIG_HAVE_FORK)
    - type = GG_RESOLVER_FORK;
    -#endif
    - }
    -
    - switch (type) {
    -#ifdef GG_CONFIG_HAVE_FORK
    - case GG_RESOLVER_FORK:
    - gs->resolver_type = type;
    - gs->resolver_start = gg_resolver_fork_start;
    - gs->resolver_cleanup = gg_resolver_fork_cleanup;
    - return 0;
    -#endif
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - case GG_RESOLVER_PTHREAD:
    - gs->resolver_type = type;
    - gs->resolver_start = gg_resolver_pthread_start;
    - gs->resolver_cleanup = gg_resolver_pthread_cleanup;
    - return 0;
    -#endif
    -
    -#ifdef _WIN32
    - case GG_RESOLVER_WIN32:
    - gs->resolver_type = type;
    - gs->resolver_start = gg_resolver_win32_start;
    - gs->resolver_cleanup = gg_resolver_win32_cleanup;
    - return 0;
    -#endif
    -
    - default:
    - errno = EINVAL;
    - return -1;
    - }
    -}
    -
    -/**
    - * Zwraca sposób rozwiązywania nazw w sesji.
    - *
    - * \param gs Struktura sesji
    - *
    - * \return Sposób rozwiązywania nazw
    - */
    -gg_resolver_t gg_session_get_resolver(struct gg_session *gs)
    -{
    - GG_SESSION_CHECK(gs, (gg_resolver_t) -1);
    -
    - return gs->resolver_type;
    -}
    -
    -/**
    - * Ustawia własny sposób rozwiązywania nazw w sesji.
    - *
    - * \param gs Struktura sesji
    - * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy
    - * \param resolver_cleanup Funkcja zwalniająca zasoby
    - *
    - * Parametry funkcji rozpoczynającej rozwiązywanie nazwy wyglądają następująco:
    - * - \c "int *fd" &mdash; wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda
    - * - \c "void **priv_data" &mdash; wskaźnik na zmienną, gdzie można umieścić
    - * wskaźnik do prywatnych danych na potrzeby rozwiązywania nazwy
    - * - \c "const char *name" &mdash; nazwa serwera do rozwiązania
    - *
    - * Parametry funkcji zwalniającej zasoby wyglądają następująco:
    - * - \c "void **priv_data" &mdash; wskaźnik na zmienną przechowującą wskaźnik
    - * do prywatnych danych, należy go ustawić na \c NULL po zakończeniu
    - * - \c "int force" &mdash; flaga mówiąca o tym, że zasoby są zwalniane przed
    - * zakończeniem rozwiązywania nazwy, np. z powodu zamknięcia sesji.
    - *
    - * Własny kod rozwiązywania nazwy powinien stworzyć potok, parę gniazd lub
    - * inny deskryptor pozwalający na co najmniej odbiór danych i przekazać go
    - * w parametrze \c fd. Na platformie Windows możliwe jest przekazanie jedynie
    - * deskryptora gniazda. Po zakończeniu rozwiązywania nazwy powinien wysłać
    - * otrzymany adres IP w postaci sieciowej (big-endian) do deskryptora. Jeśli
    - * rozwiązywanie nazwy się nie powiedzie, należy wysłać \c INADDR_NONE.
    - * Następnie zostanie wywołana funkcja zwalniająca zasoby z parametrem
    - * \c force równym \c 0. Gdyby sesja została zakończona przed rozwiązaniem
    - * nazwy, np. za pomocą funkcji \c gg_logoff(), funkcja zwalniająca zasoby
    - * zostanie wywołana z parametrem \c force równym \c 1.
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -int gg_session_set_custom_resolver(struct gg_session *gs,
    - int (*resolver_start)(int*, void**, const char*),
    - void (*resolver_cleanup)(void**, int))
    -{
    - GG_SESSION_CHECK(gs, -1);
    -
    - if (resolver_start == NULL || resolver_cleanup == NULL) {
    - errno = EINVAL;
    - return -1;
    - }
    -
    - gs->resolver_type = GG_RESOLVER_CUSTOM;
    - gs->resolver_start = resolver_start;
    - gs->resolver_cleanup = resolver_cleanup;
    -
    - return 0;
    -}
    -
    -/**
    - * Ustawia sposób rozwiązywania nazw połączenia HTTP.
    - *
    - * \param gh Struktura połączenia
    - * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver)
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type)
    -{
    - if (gh == NULL) {
    - errno = EINVAL;
    - return -1;
    - }
    -
    - if (type == GG_RESOLVER_DEFAULT) {
    - if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) {
    - gh->resolver_type = gg_global_resolver_type;
    - gh->resolver_start = gg_global_resolver_start;
    - gh->resolver_cleanup = gg_global_resolver_cleanup;
    - return 0;
    - }
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - type = GG_RESOLVER_PTHREAD;
    -#elif defined(_WIN32)
    - type = GG_RESOLVER_WIN32;
    -#elif defined(GG_CONFIG_HAVE_FORK)
    - type = GG_RESOLVER_FORK;
    -#endif
    - }
    -
    - switch (type) {
    -#ifdef GG_CONFIG_HAVE_FORK
    - case GG_RESOLVER_FORK:
    - gh->resolver_type = type;
    - gh->resolver_start = gg_resolver_fork_start;
    - gh->resolver_cleanup = gg_resolver_fork_cleanup;
    - return 0;
    -#endif
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - case GG_RESOLVER_PTHREAD:
    - gh->resolver_type = type;
    - gh->resolver_start = gg_resolver_pthread_start;
    - gh->resolver_cleanup = gg_resolver_pthread_cleanup;
    - return 0;
    -#endif
    -
    -#ifdef _WIN32
    - case GG_RESOLVER_WIN32:
    - gh->resolver_type = type;
    - gh->resolver_start = gg_resolver_win32_start;
    - gh->resolver_cleanup = gg_resolver_win32_cleanup;
    - return 0;
    -#endif
    -
    - default:
    - errno = EINVAL;
    - return -1;
    - }
    -}
    -
    -/**
    - * Zwraca sposób rozwiązywania nazw połączenia HTTP.
    - *
    - * \param gh Struktura połączenia
    - *
    - * \return Sposób rozwiązywania nazw
    - */
    -gg_resolver_t gg_http_get_resolver(struct gg_http *gh)
    -{
    - if (gh == NULL) {
    - errno = EINVAL;
    - return GG_RESOLVER_INVALID;
    - }
    -
    - return gh->resolver_type;
    -}
    -
    -/**
    - * Ustawia własny sposób rozwiązywania nazw połączenia HTTP.
    - *
    - * \param gh Struktura sesji
    - * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy
    - * \param resolver_cleanup Funkcja zwalniająca zasoby
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -int gg_http_set_custom_resolver(struct gg_http *gh,
    - int (*resolver_start)(int*, void**, const char*),
    - void (*resolver_cleanup)(void**, int))
    -{
    - if (gh == NULL || resolver_start == NULL || resolver_cleanup == NULL) {
    - errno = EINVAL;
    - return -1;
    - }
    -
    - gh->resolver_type = GG_RESOLVER_CUSTOM;
    - gh->resolver_start = resolver_start;
    - gh->resolver_cleanup = resolver_cleanup;
    -
    - return 0;
    -}
    -
    -/**
    - * Ustawia sposób rozwiązywania nazw globalnie dla biblioteki.
    - *
    - * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver)
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -int gg_global_set_resolver(gg_resolver_t type)
    -{
    - switch (type) {
    - case GG_RESOLVER_DEFAULT:
    - gg_global_resolver_type = type;
    - gg_global_resolver_start = NULL;
    - gg_global_resolver_cleanup = NULL;
    - return 0;
    -
    -#ifdef GG_CONFIG_HAVE_FORK
    - case GG_RESOLVER_FORK:
    - gg_global_resolver_type = type;
    - gg_global_resolver_start = gg_resolver_fork_start;
    - gg_global_resolver_cleanup = gg_resolver_fork_cleanup;
    - return 0;
    -#endif
    -
    -#ifdef GG_CONFIG_HAVE_PTHREAD
    - case GG_RESOLVER_PTHREAD:
    - gg_global_resolver_type = type;
    - gg_global_resolver_start = gg_resolver_pthread_start;
    - gg_global_resolver_cleanup = gg_resolver_pthread_cleanup;
    - return 0;
    -#endif
    -
    -#ifdef _WIN32
    - case GG_RESOLVER_WIN32:
    - gg_global_resolver_type = type;
    - gg_global_resolver_start = gg_resolver_win32_start;
    - gg_global_resolver_cleanup = gg_resolver_win32_cleanup;
    - return 0;
    -#endif
    -
    - default:
    - errno = EINVAL;
    - return -1;
    - }
    -}
    -
    -/**
    - * Zwraca sposób rozwiązywania nazw globalnie dla biblioteki.
    - *
    - * \return Sposób rozwiązywania nazw
    - */
    -gg_resolver_t gg_global_get_resolver(void)
    -{
    - return gg_global_resolver_type;
    -}
    -
    -/**
    - * Ustawia własny sposób rozwiązywania nazw globalnie dla biblioteki.
    - *
    - * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy
    - * \param resolver_cleanup Funkcja zwalniająca zasoby
    - *
    - * Patrz \ref gg_session_set_custom_resolver.
    - *
    - * \return 0 jeśli się powiodło, -1 w przypadku błędu
    - */
    -int gg_global_set_custom_resolver(
    - int (*resolver_start)(int*, void**, const char*),
    - void (*resolver_cleanup)(void**, int))
    -{
    - if (resolver_start == NULL || resolver_cleanup == NULL) {
    - errno = EINVAL;
    - return -1;
    - }
    -
    - gg_global_resolver_type = GG_RESOLVER_CUSTOM;
    - gg_global_resolver_start = resolver_start;
    - gg_global_resolver_cleanup = resolver_cleanup;
    -
    - return 0;
    -}
    -
    -/**
    - * Odczytuje dane z procesu/wątku rozwiązywania nazw.
    - *
    - * \param fd Deskryptor
    - * \param buf Wskaźnik na bufor
    - * \param len Długość bufora
    - *
    - * \return Patrz recv() i read().
    - */
    -int gg_resolver_recv(int fd, void *buf, size_t len)
    -{
    -#ifndef _WIN32
    - return read(fd, buf, len);
    -#else
    - return recv(fd, buf, len, 0);
    -#endif
    -}
    --- a/libpurple/protocols/gg/lib/resolver.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,28 +0,0 @@
    -/*
    - * (C) Copyright 2008 Wojtek Kaniewski <wojtekka@irc.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -#ifndef LIBGADU_RESOLVER_H
    -#define LIBGADU_RESOLVER_H
    -
    -#include "network.h"
    -
    -int gg_gethostbyname_real(const char *hostname, struct in_addr **result, unsigned int *count, int pthread);
    -int gg_resolver_recv(int fd, void *buf, size_t len);
    -void gg_resolver_cleaner(void *data);
    -
    -#endif /* LIBGADU_RESOLVER_H */
    --- a/libpurple/protocols/gg/lib/session.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,67 +0,0 @@
    -/*
    - * (C) Copyright 2008-2010 Wojtek Kaniewski <wojtekka@irc.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -#ifndef LIBGADU_SESSION_H
    -#define LIBGADU_SESSION_H
    -
    -#ifdef GG_CONFIG_HAVE_GNUTLS
    -# include <gnutls/gnutls.h>
    -#endif
    -
    -#define GG_SESSION_CHECK(gs, result) \
    - do { \
    - if ((gs) == NULL) { \
    - errno = EINVAL; \
    - return (result); \
    - } \
    - } while (0)
    -
    -#define GG_SESSION_CHECK_CONNECTED(gs, result) \
    - do { \
    - GG_SESSION_CHECK(gs, result); \
    - \
    - if (!GG_SESSION_IS_CONNECTED(gs)) { \
    - errno = ENOTCONN; \
    - return (result); \
    - } \
    - } while (0)
    -
    -#define GG_SESSION_IS_IDLE(gs) ((gs)->state == GG_STATE_IDLE)
    -#define GG_SESSION_IS_CONNECTING(gs) ((gs)->state != GG_STATE_IDLE && (gs)->state != GG_STATE_CONNECTED)
    -#define GG_SESSION_IS_CONNECTED(gs) ((gs)->state == GG_STATE_CONNECTED)
    -
    -#ifdef GG_CONFIG_HAVE_GNUTLS
    -
    -typedef struct {
    - gnutls_session_t session;
    - gnutls_certificate_credentials_t xcred;
    -} gg_session_gnutls_t;
    -
    -#define GG_SESSION_GNUTLS(gs) ((gg_session_gnutls_t*) (gs)->ssl)->session
    -
    -#endif /* GG_CONFIG_HAVE_GNUTLS */
    -
    -#ifdef GG_CONFIG_HAVE_OPENSSL
    -
    -#define GG_SESSION_OPENSSL(gs) ((SSL*) (gs)->ssl)
    -
    -#endif /* GG_CONFIG_HAVE_OPENSSL */
    -
    -int gg_session_handle_packet(struct gg_session *gs, uint32_t type, const char *ptr, size_t len, struct gg_event *ge);
    -
    -#endif /* LIBGADU_SESSION_H */
    --- a/libpurple/protocols/gg/lib/sha1.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,382 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2007 Wojtek Kaniewski <wojtekka@irc.pl>
    - *
    - * Public domain SHA-1 implementation by Steve Reid <steve@edmweb.com>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file sha1.c
    - *
    - * \brief Funkcje wyznaczania skrótu SHA1
    - */
    -
    -#include <errno.h>
    -#include <string.h>
    -
    -#include "libgadu.h"
    -#include "internal.h"
    -#include "fileio.h"
    -#include "config.h"
    -
    -/** \cond ignore */
    -
    -#ifdef GG_CONFIG_HAVE_OPENSSL
    -
    -#include <openssl/sha.h>
    -
    -#elif defined(GG_CONFIG_HAVE_GNUTLS)
    -
    -#include <gnutls/gnutls.h>
    -#include <gnutls/crypto.h>
    -
    -#define SHA_CTX gnutls_hash_hd_t
    -#define SHA1_Init(ctx) (gnutls_hash_init((ctx), GNUTLS_DIG_SHA1) == 0 ? 1 : 0)
    -#define SHA1_Update(ctx, ptr, len) (gnutls_hash(*(ctx), (ptr), (len)) == 0 ? 1 : 0)
    -#define SHA1_Final(digest, ctx) (gnutls_hash_deinit(*(ctx), (digest)), 1)
    -
    -#else
    -
    -/*
    -SHA-1 in C
    -By Steve Reid <steve@edmweb.com>
    -100% Public Domain
    -
    -Modified by Wojtek Kaniewski <wojtekka@toxygen.net> for compatibility
    -with libgadu and OpenSSL API.
    -
    -Test Vectors (from FIPS PUB 180-1)
    -"abc"
    - A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
    -"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
    - 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
    -A million repetitions of "a"
    - 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
    -*/
    -
    -/* #define LITTLE_ENDIAN * This should be #define'd if true. */
    -/* #define SHA1HANDSOFF * Copies data before messing with it. */
    -
    -typedef struct {
    - uint32_t state[5];
    - uint32_t count[2];
    - unsigned char buffer[64];
    -} SHA_CTX;
    -
    -static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64]);
    -static int SHA1_Init(SHA_CTX* context);
    -static int SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len);
    -static int SHA1_Final(unsigned char digest[20], SHA_CTX* context);
    -
    -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
    -
    -/* blk0() and blk() perform the initial expand. */
    -/* I got the idea of expanding during the round function from SSLeay */
    -#ifndef GG_CONFIG_BIGENDIAN
    -#define blk0(i) (block.l[i] = (rol(block.l[i], 24) & 0xFF00FF00) \
    - |(rol(block.l[i], 8) & 0x00FF00FF))
    -#else
    -#define blk0(i) block.l[i]
    -#endif
    -#define blk(i) (block.l[i&15] = rol(block.l[(i+13)&15]^block.l[(i+8)&15] \
    - ^block.l[(i+2)&15]^block.l[i&15], 1))
    -
    -/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
    -/* style:comma:start-ignore */
    -#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
    -#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
    -#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
    -#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
    -#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
    -/* style:comma:end-ignore */
    -
    -
    -/* Hash a single 512-bit block. This is the core of the algorithm. */
    -
    -static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64])
    -{
    -uint32_t a, b, c, d, e;
    -typedef union {
    - unsigned char c[64];
    - uint32_t l[16];
    -} CHAR64LONG16;
    - CHAR64LONG16 block;
    - memcpy(&block, buffer, sizeof(block));
    - /* Copy context->state[] to working vars */
    - a = state[0];
    - b = state[1];
    - c = state[2];
    - d = state[3];
    - e = state[4];
    - /* 4 rounds of 20 operations each. Loop unrolled. */
    - /* style:comma:start-ignore */
    - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
    - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
    - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
    - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
    - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
    - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
    - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
    - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
    - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
    - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
    - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
    - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
    - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
    - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
    - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
    - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
    - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
    - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
    - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
    - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
    - /* style:comma:end-ignore */
    - /* Add the working vars back into context.state[] */
    - state[0] += a;
    - state[1] += b;
    - state[2] += c;
    - state[3] += d;
    - state[4] += e;
    - /* Wipe variables */
    - memset(&a, 0, sizeof(a));
    - memset(&b, 0, sizeof(b));
    - memset(&c, 0, sizeof(c));
    - memset(&d, 0, sizeof(d));
    - memset(&e, 0, sizeof(e));
    -}
    -
    -
    -/* SHA1_Init - Initialize new context */
    -
    -static int SHA1_Init(SHA_CTX* context)
    -{
    - /* SHA1 initialization constants */
    - context->state[0] = 0x67452301;
    - context->state[1] = 0xEFCDAB89;
    - context->state[2] = 0x98BADCFE;
    - context->state[3] = 0x10325476;
    - context->state[4] = 0xC3D2E1F0;
    - context->count[0] = context->count[1] = 0;
    -
    - return 1;
    -}
    -
    -
    -/* Run your data through this. */
    -
    -static int SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len)
    -{
    -unsigned int i, j;
    -
    - j = (context->count[0] >> 3) & 63;
    - if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
    - context->count[1] += (len >> 29);
    - if ((j + len) > 63) {
    - memcpy(&context->buffer[j], data, (i = 64-j));
    - SHA1_Transform(context->state, context->buffer);
    - for ( ; i + 63 < len; i += 64) {
    - SHA1_Transform(context->state, &data[i]);
    - }
    - j = 0;
    - }
    - else i = 0;
    - memcpy(&context->buffer[j], &data[i], len - i);
    -
    - return 1;
    -}
    -
    -
    -/* Add padding and return the message digest. */
    -
    -static int SHA1_Final(unsigned char digest[20], SHA_CTX* context)
    -{
    -uint32_t i;
    -unsigned char finalcount[8];
    -
    - for (i = 0; i < 8; i++) {
    - finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
    - >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
    - }
    - SHA1_Update(context, (const unsigned char *)"\200", 1);
    - while ((context->count[0] & 504) != 448) {
    - SHA1_Update(context, (const unsigned char *)"\0", 1);
    - }
    - SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
    - for (i = 0; i < 20; i++) {
    - digest[i] = (unsigned char)
    - ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
    - }
    - /* Wipe variables */
    - memset(context->buffer, 0, 64);
    - memset(context->state, 0, 20);
    - memset(context->count, 0, 8);
    - memset(&finalcount, 0, 8);
    -#ifdef SHA1HANDSOFF /* make SHA1_Transform overwrite it's own static vars */
    - SHA1_Transform(context->state, context->buffer);
    -#endif
    -
    - return 1;
    -}
    -
    -#endif /* GG_CONFIG_HAVE_OPENSSL */
    -
    -/** \endcond */
    -
    -/** \cond internal */
    -
    -/**
    - * \internal Liczy skrót SHA1 z ziarna i hasła.
    - *
    - * \param password Hasło
    - * \param seed Ziarno
    - * \param result Bufor na wynik funkcji skrótu (20 bajtów)
    - *
    - * \return 0 lub -1
    - */
    -int gg_login_hash_sha1_2(const char *password, uint32_t seed, uint8_t *result)
    -{
    - SHA_CTX ctx;
    -
    - if (!SHA1_Init(&ctx))
    - return -1;
    -
    - if (!SHA1_Update(&ctx, (const unsigned char*) password, strlen(password)))
    - goto fail;
    -
    - seed = gg_fix32(seed);
    - if (!SHA1_Update(&ctx, (uint8_t*) &seed, 4))
    - goto fail;
    -
    - if (!SHA1_Final(result, &ctx))
    - return -1;
    -
    - return 0;
    -
    -fail:
    - /* Zwolnij zasoby. Tylko GnuTLS przyjęłoby NULL zamiast result, więc przekaż result. */
    - (void)SHA1_Final(result, &ctx);
    - return -1;
    -}
    -
    -/**
    - * \internal Liczy skrót SHA1 z fragmentu pliku.
    - *
    - * \param fd Deskryptor pliku
    - * \param ctx Kontekst SHA-1
    - * \param pos Położenie fragmentu pliku
    - * \param len Długość fragmentu pliku
    - *
    - * \return 0 lub -1
    - */
    -static int gg_file_hash_sha1_part(int fd, SHA_CTX *ctx, off_t pos, size_t len)
    -{
    - unsigned char buf[4096];
    - size_t chunk_len;
    - int res = 0;
    -
    - while (len > 0) {
    - if (lseek(fd, pos, SEEK_SET) == (off_t) -1) {
    - res = -1;
    - break;
    - }
    -
    - chunk_len = len;
    -
    - if (chunk_len > sizeof(buf))
    - chunk_len = sizeof(buf);
    -
    - res = read(fd, buf, chunk_len);
    -
    - if (res == -1 && errno != EINTR)
    - break;
    -
    - if (res == 0)
    - break;
    -
    - if (res != -1) {
    - if (!SHA1_Update(ctx, buf, res)) {
    - res = -1;
    - break;
    - }
    -
    - pos += res;
    - len -= res;
    - }
    - }
    -
    - return res;
    -}
    -
    -/**
    - * \internal Liczy skrót SHA1 z pliku.
    - *
    - * Dla plików poniżej 10MB liczony jest skrót z całego pliku, dla plików
    - * powyżej 10MB liczy się 9 jednomegabajtowych fragmentów.
    - *
    - * \param fd Deskryptor pliku
    - * \param result Bufor na wynik funkcji skrótu (20 bajtów)
    - *
    - * \return 0 lub -1
    - */
    -int gg_file_hash_sha1(int fd, uint8_t *result)
    -{
    - SHA_CTX ctx;
    - off_t pos, len;
    - int res;
    - const size_t part_len = 1048576;
    -
    - if ((pos = lseek(fd, 0, SEEK_CUR)) == (off_t) -1)
    - return -1;
    -
    - if ((len = lseek(fd, 0, SEEK_END)) == (off_t) -1)
    - return -1;
    -
    - if (lseek(fd, 0, SEEK_SET) == (off_t) -1)
    - return -1;
    -
    - if (!SHA1_Init(&ctx))
    - return -1;
    -
    - if (len <= (off_t)part_len * 10) {
    - res = gg_file_hash_sha1_part(fd, &ctx, 0, len);
    - } else {
    - unsigned int i;
    -
    - for (i = 0; i < 9; i++) {
    - off_t part_pos = (len - part_len) / 9 * i;
    -
    - res = gg_file_hash_sha1_part(fd, &ctx, part_pos, part_len);
    -
    - if (res == -1)
    - break;
    - }
    - }
    -
    - if (!SHA1_Final(result, &ctx))
    - return -1;
    -
    - if (res == -1)
    - return -1;
    -
    - if (lseek(fd, pos, SEEK_SET) == (off_t) -1)
    - return -1;
    -
    - return 0;
    -}
    -
    -/** \endcond */
    --- a/libpurple/protocols/gg/lib/strman.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,43 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2011 Bartosz Brachaczek <b.brachaczek@gmail.com>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file strman.h
    - *
    - * \brief Makra zapewniające kompatybilność API do obsługi operacji na stringach na różnych systemach
    - */
    -
    -#ifndef LIBGADU_STRMAN_H
    -#define LIBGADU_STRMAN_H
    -
    -#include <stdio.h>
    -#include <stdarg.h>
    -#include <string.h>
    -#ifndef _MSC_VER
    -# include <strings.h>
    -#else
    -# define snprintf(str, size, format, ...) _snprintf_s(str, size, _TRUNCATE, format, __VA_ARGS__)
    -# define vsnprintf(str, size, format, ap) vsnprintf_s(str, size, _TRUNCATE, format, ap)
    -# define strdup _strdup
    -# define strcasecmp _stricmp
    -# define strncasecmp _strnicmp
    -#endif
    -
    -#endif /* LIBGADU_STRMAN_H */
    --- a/libpurple/protocols/gg/lib/tvbuff.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,607 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2012 Tomek Wasilczyk <www.wasilczyk.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file tvbuff.c
    - *
    - * \brief Bufor wspierający obsługę pakietów typu Type-Value(s)
    - */
    -
    -#include <stdlib.h>
    -#include <string.h>
    -
    -#include "tvbuff.h"
    -
    -#include "internal.h"
    -
    -struct gg_tvbuff
    -{
    - const char *buffer;
    - size_t length;
    - size_t offset;
    - int valid;
    -};
    -
    -/**
    - * \internal Tworzy nową instancję bufora.
    - *
    - * \param buffer Bufor źródłowy; nie może być modyfikowany (w szczególności
    - * zwalniany) przez cały okres korzystania z jego opakowanej wersji.
    - * \param length Długość bufora źródłowego.
    - *
    - * \return Zaalokowane opakowanie bufora - musi być zwolnione przez free lub
    - * gg_tvbuff_close.
    - */
    -gg_tvbuff_t *gg_tvbuff_new(const char *buffer, size_t length)
    -{
    - gg_tvbuff_t *tvb;
    -
    - tvb = malloc(sizeof(gg_tvbuff_t));
    - if (tvb == NULL)
    - return NULL;
    - memset(tvb, 0, sizeof(gg_tvbuff_t));
    -
    - if (buffer == NULL && length > 0) {
    - gg_debug(GG_DEBUG_ERROR, "// gg_tvbuff_new() "
    - "invalid arguments\n");
    - tvb->valid = 0;
    - return tvb;
    - }
    -
    - tvb->buffer = buffer;
    - tvb->length = length;
    - tvb->offset = 0;
    - tvb->valid = 1;
    -
    - return tvb;
    -}
    -
    -/**
    - * \internal Zwalnia opakowanie bufora. Przed zwolnieniem sprawdza, czy
    - * przeczytano go do końca.
    - *
    - * \param tvb Bufor.
    - *
    - * \return Wartość różna od 0, jeżeli bufor tuż przed zwolnieniem był oznaczony
    - * jako prawidłowy
    - */
    -int gg_tvbuff_close(gg_tvbuff_t *tvb)
    -{
    - int valid;
    -
    - gg_tvbuff_expected_eob(tvb);
    - valid = gg_tvbuff_is_valid(tvb);
    - free(tvb);
    -
    - return valid;
    -}
    -
    -/**
    - * \internal Sprawdza, czy wszystkie odczyty z bufora były prawidłowe.
    - *
    - * \param tvb Bufor.
    - *
    - * \return Wartość różna od 0, jeżeli wszystkie odczyty były prawidłowe.
    - */
    -int gg_tvbuff_is_valid(const gg_tvbuff_t *tvb)
    -{
    - if (tvb == NULL)
    - return 0;
    - return tvb->valid;
    -}
    -
    -/**
    - * \internal Zwraca pozostałą do odczytania liczbę bajtów w buforze.
    - *
    - * \param tvb Bufor.
    - *
    - * \return Pozostała liczba bajtów do odczytania.
    - */
    -size_t gg_tvbuff_get_remaining(const gg_tvbuff_t *tvb)
    -{
    - if (!gg_tvbuff_is_valid(tvb))
    - return 0;
    -
    - return tvb->length - tvb->offset;
    -}
    -
    -/**
    - * \internal Sprawdza, czy w buforze pozostała określona liczba bajtów do
    - * odczytania. Jeżeli nie została - oznacza bufor jako nieprawidłowy.
    - *
    - * \param tvb Bufor.
    - * \param length Ilość bajtów do odczytania.
    - *
    - * \return Wartość różna od 0, jeżeli można odczytać podaną liczbę bajtów.
    - */
    -int gg_tvbuff_have_remaining(gg_tvbuff_t *tvb, size_t length)
    -{
    - if (!gg_tvbuff_is_valid(tvb))
    - return 0;
    -
    - if (gg_tvbuff_get_remaining(tvb) >= length)
    - return 1;
    -
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_have_remaining() failed "
    - "(%" GG_SIZE_FMT " < %" GG_SIZE_FMT ")\n",
    - gg_tvbuff_get_remaining(tvb), length);
    - tvb->valid = 0;
    - return 0;
    -}
    -
    -/**
    - * \internal Pomija określoną liczbę bajtów w buforze. Jeżeli w wyniku ich
    - * pominięcia wyjdzie poza zakres, oznacza bufor jako nieprawidłowy.
    - *
    - * \param tvb Bufor
    - * \param amount Liczba bajtów do pominięcia
    - */
    -void gg_tvbuff_skip(gg_tvbuff_t *tvb, size_t amount)
    -{
    - if (!gg_tvbuff_is_valid(tvb))
    - return;
    -
    - if (tvb->offset + amount > tvb->length) {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_skip() failed\n");
    - tvb->valid = 0;
    - return;
    - }
    -
    - tvb->offset += amount;
    -}
    -
    -/**
    - * \internal Cofa się o określoną liczbę bajtów w buforze. Jeżeli cofnie przed
    - * pierwszy znak, oznacza bufor jako nieprawidłowy.
    - *
    - * \param tvb Bufor
    - * \param amount Liczba bajtów do cofnięcia
    - */
    -void gg_tvbuff_rewind(gg_tvbuff_t *tvb, size_t amount)
    -{
    - if (!gg_tvbuff_is_valid(tvb))
    - return;
    -
    - if (tvb->offset < amount) {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_rewind() failed\n");
    - tvb->valid = 0;
    - return;
    - }
    -
    - tvb->offset -= amount;
    -}
    -
    -/**
    - * \internal Sprawdza, czy pod aktualną pozycją w buforze występuje podana
    - * wartość. Jeżeli tak, przesuwa aktualną pozycję do przodu.
    - *
    - * \param tvb Bufor.
    - * \param value Wartość do sprawdzenia
    - *
    - * \return Wartość różna od 0, jeżeli znaleziono podaną wartość.
    - */
    -int gg_tvbuff_match(gg_tvbuff_t *tvb, uint8_t value)
    -{
    - if (!gg_tvbuff_is_valid(tvb))
    - return 0;
    -
    - if (!gg_tvbuff_have_remaining(tvb, 1)) {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_match() failed\n");
    - return 0;
    - }
    -
    - if (tvb->buffer[tvb->offset] != value)
    - return 0;
    -
    - tvb->offset++;
    - return 1;
    -}
    -
    -/**
    - * \internal Odczytuje z bufora liczbę 8-bitową.
    - *
    - * \param tvb Bufor
    - *
    - * \return Odczytana liczba
    - */
    -uint8_t gg_tvbuff_read_uint8(gg_tvbuff_t *tvb)
    -{
    - if (!gg_tvbuff_is_valid(tvb))
    - return 0;
    -
    - if (!gg_tvbuff_have_remaining(tvb, 1)) {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_read_uint8() "
    - "failed at %" GG_SIZE_FMT "\n", tvb->offset);
    - return 0;
    - }
    -
    - return tvb->buffer[tvb->offset++];
    -}
    -
    -/**
    - * \internal Odczytuje z bufora liczbę 32-bitową.
    - *
    - * \param tvb Bufor
    - *
    - * \return Odczytana liczba
    - */
    -uint32_t gg_tvbuff_read_uint32(gg_tvbuff_t *tvb)
    -{
    - uint32_t val;
    -
    - if (!gg_tvbuff_is_valid(tvb))
    - return 0;
    -
    - if (!gg_tvbuff_have_remaining(tvb, 4)) {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_read_uint32() "
    - "failed at %" GG_SIZE_FMT "\n", tvb->offset);
    - return 0;
    - }
    -
    - memcpy(&val, tvb->buffer + tvb->offset, 4);
    - tvb->offset += 4;
    -
    - return gg_fix32(val);
    -}
    -
    -/**
    - * \internal Odczytuje z bufora liczbę 64-bitową.
    - *
    - * \param tvb Bufor
    - *
    - * \return Odczytana liczba
    - */
    -uint64_t gg_tvbuff_read_uint64(gg_tvbuff_t *tvb)
    -{
    - uint64_t val;
    -
    - if (!gg_tvbuff_is_valid(tvb))
    - return 0;
    -
    - if (!gg_tvbuff_have_remaining(tvb, 8)) {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_read_uint64() "
    - "failed at %" GG_SIZE_FMT "\n", tvb->offset);
    - return 0;
    - }
    -
    - memcpy(&val, tvb->buffer + tvb->offset, 8);
    - tvb->offset += 8;
    -
    - return gg_fix64(val);
    -}
    -
    -/**
    - * \internal Odczytuje z bufora skompresowaną liczbę całkowitą.
    - * Liczba taka może być zapisana w buforze na 1-9 bajtach, w zależności
    - * od jej wartości.
    - *
    - * Skompresowana liczba jest zapisywana od najmłodszego bajtu do najstarszego
    - * niezerowego. W każdym bajcie zapisuje się bit sterujący (równy 0, jeżeli jest
    - * to ostatni bajt do przeczytania, lub 1 w p.p.) oraz 7 kolejnych bitów z
    - * kompresowanej liczby.
    - *
    - * Przykładowo, liczby mniejsze od 128 (1000.0000b) są zapisywane dokładnie tak,
    - * jak uint8_t; a np. 12345 (0011.0000.0011.1001b) zostanie zapisana jako 0x60B9
    - * (0110.0000.1011.1001b).
    - *
    - * \param tvb Bufor.
    - *
    - * \return Odczytana liczba.
    - */
    -uint64_t gg_tvbuff_read_packed_uint(gg_tvbuff_t *tvb)
    -{
    - uint64_t val = 0;
    - int i, val_len = 0;
    -
    - if (!gg_tvbuff_is_valid(tvb))
    - return 0;
    -
    - while (gg_tvbuff_have_remaining(tvb, 1)) {
    - val_len++;
    - if (!(gg_tvbuff_read_uint8(tvb) & 0x80))
    - break;
    - }
    -
    - if (!gg_tvbuff_is_valid(tvb)) {
    - gg_debug(GG_DEBUG_WARNING,
    - "// gg_tvbuff_read_packed_uint() failed\n");
    - return 0;
    - }
    -
    - if (val_len > 9) {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_read_packed_uint() "
    - "packed uint size too big: %d\n", val_len);
    - tvb->valid = 0;
    - return 0;
    - }
    -
    - for (i = 1; i <= val_len; i++) {
    - uint64_t old_val = val;
    - val <<= 7;
    - if (old_val != (val >> 7)) {
    - gg_debug(GG_DEBUG_WARNING,
    - "// gg_tvbuff_read_packed_uint() overflow\n");
    - tvb->valid = 0;
    - return 0;
    - }
    - val |= (uint8_t)(tvb->buffer[tvb->offset - i] & ~0x80);
    - }
    -
    - return val;
    -}
    -
    -/**
    - * \internal Odczytuje z bufora podciąg bez kopiowania danych.
    - *
    - * \param tvb Bufor źródłowy
    - * \param length Ilość bajtów do odczytania
    - *
    - * \return Wskaźnik na początek odczytanych danych, lub NULL w przypadku
    - * niepowodzenia
    - */
    -const char *gg_tvbuff_read_buff(gg_tvbuff_t *tvb, size_t length)
    -{
    - const char *buff;
    -
    - if (!gg_tvbuff_is_valid(tvb))
    - return NULL;
    -
    - if (!gg_tvbuff_have_remaining(tvb, length)) {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_get_buff() "
    - "failed at %" GG_SIZE_FMT ":%" GG_SIZE_FMT "\n",
    - tvb->offset, length);
    - return NULL;
    - }
    -
    - buff = tvb->buffer + tvb->offset;
    - tvb->offset += length;
    - return buff;
    -}
    -
    -/**
    - * \internal Odczytuje z bufora podciąg kopiując go do nowego obszaru pamięci.
    - *
    - * \param tvb Bufor źródłowy
    - * \param buffer Bufor docelowy
    - * \param length Ilość bajtów do odczytania
    - */
    -void gg_tvbuff_read_buff_cpy(gg_tvbuff_t *tvb, char *buffer, size_t length)
    -{
    - if (!gg_tvbuff_is_valid(tvb))
    - return;
    -
    - if (!gg_tvbuff_have_remaining(tvb, length)) {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_read_buff() "
    - "failed at %" GG_SIZE_FMT ":%" GG_SIZE_FMT "\n",
    - tvb->offset, length);
    - return;
    - }
    -
    - if (buffer == NULL && length > 0) {
    - gg_debug(GG_DEBUG_ERROR, "// gg_tvbuff_new() "
    - "invalid arguments\n");
    - tvb->valid = 0;
    - return;
    - }
    -
    - memcpy(buffer, tvb->buffer + tvb->offset, length);
    - tvb->offset += length;
    -}
    -
    -/**
    - * \internal Odczytuje z bufora ciąg tekstowy (mogący zawierać dowolne znaki,
    - * również \0) bez kopiowania danych.
    - *
    - * \param tvb Bufor źródłowy
    - * \param length Zmienna, do której zostanie zapisana długość odczytanego ciągu
    - *
    - * \return Wskaźnik na początek odczytanych danych, lub NULL w przypadku
    - * niepowodzenia
    - */
    -const char *gg_tvbuff_read_str(gg_tvbuff_t *tvb, size_t *length)
    -{
    - size_t offset;
    - uint32_t str_len;
    - const char *str;
    -
    - if (!gg_tvbuff_is_valid(tvb))
    - return NULL;
    -
    - offset = tvb->offset;
    - str_len = gg_tvbuff_read_packed_uint(tvb);
    - if (!gg_tvbuff_is_valid(tvb) ||
    - !gg_tvbuff_have_remaining(tvb, str_len))
    - {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_read_str() failed at "
    - "%" GG_SIZE_FMT ":%d\n", offset, str_len);
    - return NULL;
    - }
    -
    - str = gg_tvbuff_read_buff(tvb, str_len);
    -
    - if (!gg_tvbuff_is_valid(tvb))
    - return NULL;
    -
    - if (length != NULL)
    - *length = str_len;
    - if (str_len == 0)
    - return NULL;
    - return str;
    -}
    -
    -/**
    - * \internal Odczytuje z bufora ciąg tekstowy (mogący zawierać dowolne znaki,
    - * również \0) kopiując go do nowego obszaru pamięci. Zwrócony ciąg będzie
    - * zawsze zakończony znakiem \0.
    - *
    - * \param tvb Bufor źródłowy
    - * \param dst Zmienna, do której zostanie zapisany wskaźnik na odczytany ciąg.
    - * Po użyciu, blok ten powinien zostać zwolniony za pomocą \c free()
    - *
    - * \return Wskaźnik na początek odczytanych danych, lub NULL w przypadku
    - * niepowodzenia
    - */
    -void gg_tvbuff_read_str_dup(gg_tvbuff_t *tvb, char **dst)
    -{
    - size_t offset;
    - uint32_t str_len;
    - char *str;
    -
    - if (!gg_tvbuff_is_valid(tvb))
    - return;
    -
    - offset = tvb->offset;
    - str_len = gg_tvbuff_read_packed_uint(tvb);
    - if (!gg_tvbuff_is_valid(tvb) ||
    - !gg_tvbuff_have_remaining(tvb, str_len))
    - {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_read_str_dup() failed "
    - "at %" GG_SIZE_FMT ":%d\n", offset, str_len);
    - return;
    - }
    -
    - str = malloc(str_len + 1);
    - if (str == NULL) {
    - gg_debug(GG_DEBUG_ERROR, "// gg_tvbuff_read_str_dup() "
    - "not enough free memory: %d + 1\n", str_len);
    - tvb->valid = 0;
    - return;
    - }
    -
    - gg_tvbuff_read_buff_cpy(tvb, str, str_len);
    - str[str_len] = '\0';
    -
    - if (*dst != NULL) {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_read_str_dup() "
    - "destination already filled, freeing it...\n");
    - free(*dst);
    - }
    - *dst = str;
    -}
    -
    -/**
    - * \internal Odczytuje z bufora identyfikator użytkownika.
    - *
    - * \param tvb Bufor
    - *
    - * \return Identyfikator użytkownika, lub 0 w przypadku niepowodzenia
    - */
    -uin_t gg_tvbuff_read_uin(gg_tvbuff_t *tvb)
    -{
    - uin_t uin = 0;
    - uint32_t uin_len, full_len;
    - uint8_t uin_type;
    - const char *raw;
    -
    - if (!gg_tvbuff_is_valid(tvb))
    - return 0;
    -
    - full_len = gg_tvbuff_read_packed_uint(tvb);
    - uin_type = gg_tvbuff_read_uint8(tvb);
    - uin_len = gg_tvbuff_read_uint8(tvb);
    -
    - if (!gg_tvbuff_is_valid(tvb))
    - return 0;
    -
    - if (full_len != uin_len + 2 ||
    - uin_type != 0 ||
    - uin_len > 10)
    - {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_read_uin() failed (1)\n");
    - tvb->valid = 0;
    - return 0;
    - }
    -
    - raw = gg_tvbuff_read_buff(tvb, uin_len);
    - if (raw)
    - uin = gg_str_to_uin(raw, uin_len);
    -
    - if (uin == 0) {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_read_uin() failed (2)\n");
    - tvb->valid = 0;
    - return 0;
    - }
    -
    - return uin;
    -}
    -
    -/**
    - * \internal Odczytuje z bufora liczbę 8-bitową i porównuje z podaną. Jeżeli te
    - * się różnią, zostaje wygenerowane ostrzeżenie.
    - *
    - * \param tvb Bufor
    - * \param value Oczekiwana wartość
    - */
    -void gg_tvbuff_expected_uint8(gg_tvbuff_t *tvb, uint8_t value)
    -{
    - uint8_t got;
    - size_t offset;
    -
    - offset = tvb->offset;
    - got = gg_tvbuff_read_uint8(tvb);
    - if (!gg_tvbuff_is_valid(tvb))
    - return;
    -
    - if (got != value)
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_expected_uint8() "
    - "expected %#02x, but %#02x found at %" GG_SIZE_FMT "\n",
    - value, got, offset);
    -}
    -
    -/**
    - * \internal Odczytuje z bufora liczbę 32-bitową i porównuje z podaną. Jeżeli te
    - * się różnią, zostaje wygenerowane ostrzeżenie.
    - *
    - * \param tvb Bufor
    - * \param value Oczekiwana wartość
    - */
    -void gg_tvbuff_expected_uint32(gg_tvbuff_t *tvb, uint32_t value)
    -{
    - uint32_t got;
    - size_t offset;
    -
    - offset = tvb->offset;
    - got = gg_tvbuff_read_uint32(tvb);
    - if (!gg_tvbuff_is_valid(tvb))
    - return;
    -
    - if (got != value)
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_expected_uint32() "
    - "expected %#08x, but %#08x found at %" GG_SIZE_FMT "\n",
    - value, got, offset);
    -}
    -
    -/**
    - * \internal Oczekuje końca bufora. Jeżeli w buforze są jeszcze dane do
    - * przeczytania, generuje ostrzeżenie.
    - *
    - * \param tvb Bufor.
    - */
    -void gg_tvbuff_expected_eob(const gg_tvbuff_t *tvb)
    -{
    - if (!gg_tvbuff_is_valid(tvb))
    - return;
    -
    - if (gg_tvbuff_get_remaining(tvb) != 0)
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuff_expected_eob() "
    - "unexpected %" GG_SIZE_FMT " bytes, first=%#02x\n",
    - gg_tvbuff_get_remaining(tvb),
    - tvb->buffer[tvb->offset]);
    -}
    --- a/libpurple/protocols/gg/lib/tvbuff.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,59 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2012 Tomek Wasilczyk <www.wasilczyk.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -#ifndef LIBGADU_TVBUFF_H
    -#define LIBGADU_TVBUFF_H
    -
    -#include "libgadu.h"
    -
    -typedef struct gg_tvbuff gg_tvbuff_t;
    -
    -gg_tvbuff_t * gg_tvbuff_new(const char *buffer, size_t length);
    -int gg_tvbuff_close(gg_tvbuff_t *tvb);
    -
    -int gg_tvbuff_is_valid(const gg_tvbuff_t *tvb);
    -
    -size_t gg_tvbuff_get_remaining(const gg_tvbuff_t *tvb);
    -
    -int gg_tvbuff_have_remaining(gg_tvbuff_t *tvb, size_t length);
    -
    -void gg_tvbuff_skip(gg_tvbuff_t *tvb, size_t amount);
    -void gg_tvbuff_rewind(gg_tvbuff_t *tvb, size_t amount);
    -
    -int gg_tvbuff_match(gg_tvbuff_t *tvb, uint8_t value);
    -
    -uint8_t gg_tvbuff_read_uint8(gg_tvbuff_t *tvb);
    -uint32_t gg_tvbuff_read_uint32(gg_tvbuff_t *tvb);
    -uint64_t gg_tvbuff_read_uint64(gg_tvbuff_t *tvb);
    -
    -uint64_t gg_tvbuff_read_packed_uint(gg_tvbuff_t *tvb);
    -
    -const char * gg_tvbuff_read_buff(gg_tvbuff_t *tvb, size_t length);
    -void gg_tvbuff_read_buff_cpy(gg_tvbuff_t *tvb, char *buffer, size_t length);
    -const char * gg_tvbuff_read_str(gg_tvbuff_t *tvb, size_t *length);
    -void gg_tvbuff_read_str_dup(gg_tvbuff_t *tvb, char **dst);
    -
    -uin_t gg_tvbuff_read_uin(gg_tvbuff_t *tvb);
    -
    -void gg_tvbuff_expected_uint8(gg_tvbuff_t *tvb, uint8_t value);
    -void gg_tvbuff_expected_uint32(gg_tvbuff_t *tvb, uint32_t value);
    -void gg_tvbuff_expected_eob(const gg_tvbuff_t *tvb);
    -
    -#endif /* LIBGADU_TVBUFF_H */
    --- a/libpurple/protocols/gg/lib/tvbuilder.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,426 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2012 Tomek Wasilczyk <www.wasilczyk.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -/**
    - * \file tvbuilder.c
    - *
    - * \brief Bufor wspierający budowanie pakietów typu Type-Value(s)
    - */
    -
    -#include <stdlib.h>
    -#include <string.h>
    -
    -#include "tvbuilder.h"
    -
    -#include "internal.h"
    -#include "fileio.h"
    -
    -#include <errno.h>
    -
    -struct gg_tvbuilder
    -{
    - char *buffer;
    - size_t length;
    - size_t alloc_length;
    - int valid;
    -
    - struct gg_session *gs;
    - struct gg_event *ge;
    -};
    -
    -static char *gg_tvbuilder_extend(gg_tvbuilder_t *tvb, size_t length);
    -
    -/**
    - * \internal Tworzy nową instancję bufora.
    - *
    - * \param gs Struktura sesji
    - * \param ge Struktura zdarzenia
    - *
    - * \return Zaalokowany bufor - musi być zwolniony przez gg_tvbuilder_free,
    - * gg_tvbuilder_fail lub gg_tvbuilder_send.
    - */
    -gg_tvbuilder_t *gg_tvbuilder_new(struct gg_session *gs, struct gg_event *ge)
    -{
    - gg_tvbuilder_t *tvb;
    -
    - tvb = malloc(sizeof(gg_tvbuilder_t));
    - if (tvb == NULL)
    - return NULL;
    - memset(tvb, 0, sizeof(gg_tvbuilder_t));
    -
    - if (gs == NULL) {
    - gg_debug(GG_DEBUG_ERROR, "// gg_tvbuilder_new() "
    - "invalid arguments\n");
    - tvb->valid = 0;
    - return tvb;
    - }
    -
    - tvb->buffer = NULL;
    - tvb->length = 0;
    - tvb->alloc_length = 0;
    - tvb->valid = 1;
    -
    - tvb->gs = gs;
    - tvb->ge = ge;
    -
    - return tvb;
    -}
    -
    -/**
    - * \internal Zwalnia bufor.
    - *
    - * \param tvb Bufor
    - */
    -void gg_tvbuilder_free(gg_tvbuilder_t *tvb)
    -{
    - if (tvb == NULL)
    - return;
    -
    - free(tvb->buffer);
    - free(tvb);
    -}
    -
    -/**
    - * \internal Zwalnia bufor i generuje błąd połączenia.
    - *
    - * \param tvb Bufor
    - * \param failure Powód błędu
    - */
    -void gg_tvbuilder_fail(gg_tvbuilder_t *tvb, enum gg_failure_t failure)
    -{
    - int errno_copy;
    -
    - if (tvb == NULL) {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuilder_fail() "
    - "NULL tvbuilder\n");
    - return;
    - }
    -
    - errno_copy = errno;
    - close(tvb->gs->fd);
    - tvb->gs->fd = -1;
    - errno = errno_copy;
    -
    - if (tvb->ge) {
    - tvb->ge->type = GG_EVENT_CONN_FAILED;
    - tvb->ge->event.failure = failure;
    - }
    - tvb->gs->state = GG_STATE_IDLE;
    -
    - gg_tvbuilder_free(tvb);
    -}
    -
    -/**
    - * \internal Próbuje wysłać zawartość bufora i go zwalnia.
    - *
    - * \param tvb Bufor
    - * \param type Typ pakietu
    - *
    - * \return 1 jeśli się powiodło, 0 w p.p.
    - */
    -int gg_tvbuilder_send(gg_tvbuilder_t *tvb, int type)
    -{
    - int ret;
    - enum gg_failure_t failure;
    -
    - if (tvb == NULL) {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuilder_send() "
    - "NULL tvbuilder\n");
    - return 0;
    - }
    -
    - if (!gg_tvbuilder_is_valid(tvb)) {
    - gg_debug_session(tvb->gs, GG_DEBUG_ERROR, "// gg_tvbuilder_send() "
    - "invalid buffer\n");
    - ret = -1;
    - failure = GG_FAILURE_INTERNAL;
    - } else {
    - const char *buffer = (tvb->length > 0) ? tvb->buffer : "";
    - ret = gg_send_packet(tvb->gs, type, buffer, tvb->length, NULL);
    - if (ret == -1) {
    - failure = GG_FAILURE_WRITING;
    - gg_debug_session(tvb->gs, GG_DEBUG_ERROR,
    - "// gg_tvbuilder_send() "
    - "sending packet %#x failed. (errno=%d, %s)\n",
    - type, errno, strerror(errno));
    - }
    - }
    -
    - if (ret == -1) {
    - gg_tvbuilder_fail(tvb, failure);
    - return 0;
    - }
    -
    - gg_tvbuilder_free(tvb);
    - return 1;
    -}
    -
    -/**
    - * \internal Sprawdza, czy wszystkie zapisy do bufora były prawidłowe.
    - *
    - * \param tvb Builder.
    - *
    - * \return Wartość różna od 0, jeżeli wszystkie zapisy były prawidłowe.
    - */
    -int gg_tvbuilder_is_valid(const gg_tvbuilder_t *tvb)
    -{
    - if (tvb == NULL)
    - return 0;
    - return tvb->valid;
    -}
    -
    -/**
    - * \internal Sprawdza rozmiar bufora.
    - *
    - * \param tvb Bufor
    - *
    - * \return Rozmiar bufora
    - */
    -size_t gg_tvbuilder_get_size(const gg_tvbuilder_t *tvb)
    -{
    - if (!gg_tvbuilder_is_valid(tvb))
    - return 0;
    -
    - return tvb->length;
    -}
    -
    -/**
    - * \internal Określa oczekiwaną liczbę bajtów, o którą zostanie rozszerzony
    - * bufor.
    - *
    - * Funkcja powoduje jedynie wzrost wydajności poprzez zmniejszenie ilości
    - * realokacji.
    - *
    - * \param tvb Builder.
    - * \param length Oczekiwana liczba bajtów.
    - */
    -void gg_tvbuilder_expected_size(gg_tvbuilder_t *tvb, size_t length)
    -{
    - size_t length_new;
    - char *buff_new;
    -
    - if (!gg_tvbuilder_is_valid(tvb) || length == 0)
    - return;
    -
    - length_new = tvb->length + length;
    -
    - if (length_new <= tvb->alloc_length)
    - return;
    -
    - if (tvb->alloc_length > 0) {
    - gg_debug(GG_DEBUG_MISC, "// gg_tvbuilder_expected_size(%p, %"
    - GG_SIZE_FMT ") realloc from %" GG_SIZE_FMT " to %"
    - GG_SIZE_FMT "\n",
    - tvb, length, tvb->alloc_length, length_new);
    - }
    -
    - buff_new = realloc(tvb->buffer, length_new);
    - if (buff_new != NULL) {
    - tvb->buffer = buff_new;
    - tvb->alloc_length = length_new;
    - return;
    - }
    -
    - gg_debug(GG_DEBUG_ERROR, "// gg_tvbuilder_expected_size(%p, %"
    - GG_SIZE_FMT ") out of memory (new length: %" GG_SIZE_FMT
    - ")\n", tvb, length, length_new);
    - free(tvb->buffer);
    - tvb->buffer = NULL;
    - tvb->length = 0;
    - tvb->alloc_length = 0;
    - tvb->valid = 0;
    -}
    -
    -/**
    - * \internal Poszerza bufor o podaną liczbę bajtów.
    - *
    - * \param tvb Bufor
    - * \param length Liczba bajtów do dodania
    - *
    - * \return Początek nowo dodanego bloku bufora
    - */
    -static char * gg_tvbuilder_extend(gg_tvbuilder_t *tvb, size_t length)
    -{
    - size_t length_old;
    -
    - gg_tvbuilder_expected_size(tvb, length);
    - if (!gg_tvbuilder_is_valid(tvb))
    - return NULL;
    -
    - length_old = tvb->length;
    - tvb->length += length;
    -
    - return tvb->buffer + length_old;
    -}
    -
    -/**
    - * \internal Skraca bufor o podaną liczbę bajtów
    - *
    - * \param tvb Bufor
    - * \param length Ilość bajtów do skrócenia
    - */
    -void gg_tvbuilder_strip(gg_tvbuilder_t *tvb, size_t length)
    -{
    - if (!gg_tvbuilder_is_valid(tvb))
    - return;
    -
    - if (length > tvb->length) {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuilder_strip() "
    - "out of range\n");
    - tvb->valid = 0;
    - return;
    - }
    -
    - tvb->length = length;
    -}
    -
    -/**
    - * \internal Zapisuje do bufora liczbę 8-bitową.
    - *
    - * \param tvb Bufor
    - * \param value Wartość do zapisania
    - */
    -void gg_tvbuilder_write_uint8(gg_tvbuilder_t *tvb, uint8_t value)
    -{
    - gg_tvbuilder_write_buff(tvb, (const char *)&value, 1);
    -}
    -
    -/**
    - * \internal Zapisuje do bufora liczbę 32-bitową.
    - *
    - * \param tvb Bufor
    - * \param value Wartość do zapisania
    - */
    -void gg_tvbuilder_write_uint32(gg_tvbuilder_t *tvb, uint32_t value)
    -{
    - value = gg_fix32(value);
    - gg_tvbuilder_write_buff(tvb, (const char *)&value, 4);
    -}
    -
    -/**
    - * \internal Zapisuje do bufora liczbę 64-bitową.
    - *
    - * \param tvb Bufor
    - * \param value Wartość do zapisania
    - */
    -void gg_tvbuilder_write_uint64(gg_tvbuilder_t *tvb, uint64_t value)
    -{
    - value = gg_fix64(value);
    - gg_tvbuilder_write_buff(tvb, (const char *)&value, 8);
    -}
    -
    -/**
    - * \internal Zapisuje do bufora liczbę 1-9 bajtową.
    - *
    - * \param tvb Bufor
    - * \param value Wartość do zapisania
    - *
    - * \see gg_tvbuff_read_packed_uint
    - */
    -void gg_tvbuilder_write_packed_uint(gg_tvbuilder_t *tvb, uint64_t value)
    -{
    - uint8_t buff[9];
    - uint64_t val_curr;
    - int i, val_len = 0;
    -
    - if (!gg_tvbuilder_is_valid(tvb))
    - return;
    -
    - val_curr = value;
    - while (val_curr > 0) {
    - val_curr >>= 7;
    - val_len++;
    - }
    - if (val_len == 0)
    - val_len = 1;
    -
    - if (val_len > 9) {
    - gg_debug(GG_DEBUG_WARNING, "// gg_tvbuilder_write_packed_uint() "
    - "int size too big (%d): %" PRIu64 "\n", val_len, value);
    - tvb->valid = 0;
    - return;
    - }
    -
    - val_curr = value;
    - for (i = 0; i < val_len; i++) {
    - uint8_t raw = val_curr & 0x7F;
    - val_curr >>= 7;
    - if (i + 1 < val_len)
    - raw |= 0x80;
    - buff[i] = raw;
    - }
    -
    - gg_tvbuilder_write_buff(tvb, (const char*)buff, val_len);
    -}
    -
    -/**
    - * \internal Zapisuje do bufora zawartość innego bufora.
    - *
    - * \param tvb Bufor docelowy
    - * \param buffer Bufor źródłowy
    - * \param length Ilość danych do skopiowania
    - */
    -void gg_tvbuilder_write_buff(gg_tvbuilder_t *tvb, const char *buffer,
    - size_t length)
    -{
    - char *buff = gg_tvbuilder_extend(tvb, length);
    - if (!buff)
    - return;
    -
    - memcpy(buff, buffer, length);
    -}
    -
    -/**
    - * \internal Zapisuje do bufora ciąg tekstowy (mogący zawierać znaki \0).
    - *
    - * \param tvb Bufor docelowy
    - * \param buffer Bufor źródłowy
    - * \param length Długość tekstu, lub -1, jeżeli ma zostać wyliczona
    - * automatycznie (do pierwszego znaku \0)
    - */
    -void gg_tvbuilder_write_str(gg_tvbuilder_t *tvb, const char *buffer,
    - ssize_t length)
    -{
    - if (!gg_tvbuilder_is_valid(tvb))
    - return;
    -
    - if (length == -1)
    - length = strlen(buffer);
    -
    - gg_tvbuilder_write_packed_uint(tvb, length);
    - gg_tvbuilder_write_buff(tvb, buffer, length);
    -}
    -
    -/**
    - * \internal Zapisuje do bufora identyfikator użytkownika.
    - *
    - * \param tvb Bufor
    - * \param uin Identyfikator użytkownika
    - */
    -void gg_tvbuilder_write_uin(gg_tvbuilder_t *tvb, uin_t uin)
    -{
    - char uin_str[16];
    - int uin_len;
    -
    - uin_len = snprintf(uin_str, sizeof(uin_str), "%u", uin);
    -
    - gg_tvbuilder_write_uint8(tvb, 0x00);
    - gg_tvbuilder_write_str(tvb, uin_str, uin_len);
    -}
    --- a/libpurple/protocols/gg/lib/tvbuilder.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,50 +0,0 @@
    -/* $Id$ */
    -
    -/*
    - * (C) Copyright 2012 Tomek Wasilczyk <www.wasilczyk.pl>
    - *
    - * This program is free software; you can redistribute it and/or modify
    - * it under the terms of the GNU Lesser General Public License Version
    - * 2.1 as published by the Free Software Foundation.
    - *
    - * 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 Lesser General Public License for more details.
    - *
    - * You should have received a copy of the GNU Lesser General Public
    - * License along with this program; if not, write to the Free Software
    - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
    - * USA.
    - */
    -
    -#ifndef LIBGADU_TVBUILDER_H
    -#define LIBGADU_TVBUILDER_H
    -
    -#include "libgadu.h"
    -
    -typedef struct gg_tvbuilder gg_tvbuilder_t;
    -
    -gg_tvbuilder_t * gg_tvbuilder_new(struct gg_session *gs, struct gg_event *ge);
    -void gg_tvbuilder_free(gg_tvbuilder_t *tvb);
    -void gg_tvbuilder_fail(gg_tvbuilder_t *tvb, enum gg_failure_t failure);
    -int gg_tvbuilder_send(gg_tvbuilder_t *tvb, int type);
    -
    -int gg_tvbuilder_is_valid(const gg_tvbuilder_t *tvb);
    -
    -size_t gg_tvbuilder_get_size(const gg_tvbuilder_t *tvb);
    -void gg_tvbuilder_expected_size(gg_tvbuilder_t *tvb, size_t length);
    -void gg_tvbuilder_strip(gg_tvbuilder_t *tvb, size_t length);
    -
    -void gg_tvbuilder_write_uint8(gg_tvbuilder_t *tvb, uint8_t value);
    -void gg_tvbuilder_write_uint32(gg_tvbuilder_t *tvb, uint32_t value);
    -void gg_tvbuilder_write_uint64(gg_tvbuilder_t *tvb, uint64_t value);
    -
    -void gg_tvbuilder_write_packed_uint(gg_tvbuilder_t *tvb, uint64_t value);
    -
    -void gg_tvbuilder_write_buff(gg_tvbuilder_t *tvb, const char *buffer, size_t length);
    -void gg_tvbuilder_write_str(gg_tvbuilder_t *tvb, const char *buffer, ssize_t length);
    -
    -void gg_tvbuilder_write_uin(gg_tvbuilder_t *tvb, uin_t uin);
    -
    -#endif /* LIBGADU_TVBUILDER_H */
    --- a/libpurple/protocols/gg/tcpsocket.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/protocols/gg/tcpsocket.c Sun Oct 30 12:03:44 2016 +0300
    @@ -30,32 +30,100 @@
    #include "gg.h"
    #include "debug.h"
    -#include "purple-socket.h"
    +#include "purple-gio.h"
    +
    +typedef struct {
    + GSocketConnection *conn;
    + GCancellable *cancellable;
    + PurpleConnection *gc;
    + gpointer priv_gg;
    +} GGPTcpSocketData;
    +
    +static void
    +ggp_tcp_socket_data_free(GGPTcpSocketData *data)
    +{
    + g_return_if_fail(data != NULL);
    +
    + if (data->cancellable != NULL) {
    + g_cancellable_cancel(data->cancellable);
    + g_clear_object(&data->cancellable);
    + }
    +
    + if (data->conn != NULL) {
    + purple_gio_graceful_close(G_IO_STREAM(data->conn), NULL, NULL);
    + g_clear_object(&data->conn);
    + }
    +
    + g_free(data);
    +}
    static void
    -ggp_tcpsocket_connected(PurpleSocket *ps, const gchar *error, gpointer priv_gg)
    +ggp_tcpsocket_connected(GObject *source, GAsyncResult *res, gpointer user_data)
    {
    - PurpleConnection *gc = purple_socket_get_connection(ps);
    - GGPInfo *info = purple_connection_get_protocol_data(gc);
    + GGPTcpSocketData *data = user_data;
    + GSocketConnection *conn;
    + GSocket *socket;
    int fd = -1;
    + GGPInfo *info;
    + GError *error = NULL;
    +
    + conn = g_socket_client_connect_to_host_finish(G_SOCKET_CLIENT(source),
    + res, &error);
    - PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
    + if (conn == NULL) {
    + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
    + /* The connection was already closed, return now */
    + g_clear_error(&error);
    + return;
    + }
    - if (error == NULL)
    - fd = purple_socket_get_fd(ps);
    + purple_debug_error("gg", "socket failed to connect: %s",
    + error->message);
    + g_clear_error(&error);
    + } else {
    + data->conn = conn;
    - if (!gg_socket_manager_connected(ps, priv_gg, fd)) {
    - purple_debug_error("gg", "socket not handled");
    - purple_socket_destroy(ps);
    + socket = g_socket_connection_get_socket(data->conn);
    +
    + if (socket != NULL) {
    + fd = g_socket_get_fd(socket);
    + }
    }
    - if (info->inpa > 0)
    - purple_input_remove(info->inpa);
    + /* XXX: For some reason if you try to connect and then immediately
    + * disconnect, this gets into a state where ggp_tcpsocket_close()
    + * isn't called. The cancellable is therefore not cancelled, and
    + * the connection is never closed. Guard against that state here.
    + */
    + if (data->gc == NULL ||
    + !g_list_find(purple_connections_get_all(), data->gc)) {
    + purple_debug_error("gg",
    + "disconnected without closing connection: %p",
    + data);
    + ggp_tcp_socket_data_free(data);
    + return;
    + }
    +
    + if (!gg_socket_manager_connected(data, data->priv_gg, fd)) {
    + purple_debug_error("gg", "socket not handled");
    + ggp_tcp_socket_data_free(data);
    + return;
    + }
    +
    + info = purple_connection_get_protocol_data(data->gc);
    +
    + if (info->inpa > 0) {
    + g_source_remove(info->inpa);
    + info->inpa = 0;
    + }
    +
    if (info->session->fd < 0)
    return;
    +
    + /* XXX: This works, but not recommended to use GSocket FDs directly */
    info->inpa = purple_input_add(info->session->fd,
    ggp_tcpsocket_inputcond_gg_to_purple(info->session->check),
    - ggp_async_login_handler, gc);
    + ggp_async_login_handler, data->gc);
    }
    static void*
    @@ -63,7 +131,9 @@
    int is_async, void *priv)
    {
    PurpleConnection *gc = _gc;
    - PurpleSocket *ps;
    + GGPTcpSocketData *data;
    + GSocketClient *client;
    + GError *error = NULL;
    PURPLE_ASSERT_CONNECTION_IS_VALID(gc);
    g_return_val_if_fail(!purple_connection_is_disconnecting(gc), NULL);
    @@ -74,40 +144,100 @@
    purple_debug_misc("gg", "ggp_tcpsocket_connect(%p, %s:%d, %s, %p)",
    gc, host, port, is_tls ? "tls" : "tcp", priv);
    - ps = purple_socket_new(gc);
    - purple_socket_set_tls(ps, is_tls);
    - purple_socket_set_host(ps, host);
    - purple_socket_set_port(ps, port);
    - if (!purple_socket_connect(ps, ggp_tcpsocket_connected, priv)) {
    - purple_socket_destroy(ps);
    + client = purple_gio_socket_client_new(
    + purple_connection_get_account(gc), &error);
    +
    + if (client == NULL) {
    + purple_debug_error("gg", "unable to connect: %s",
    + error->message);
    + g_clear_error(&error);
    return NULL;
    }
    - return ps;
    + g_socket_client_set_tls(client, is_tls);
    +
    + data = g_new0(GGPTcpSocketData, 1);
    + data->cancellable = g_cancellable_new();
    + data->gc = gc;
    + data->priv_gg = priv;
    +
    + g_socket_client_connect_to_host_async(client, host, port,
    + data->cancellable, ggp_tcpsocket_connected, data);
    + g_object_unref(client);
    +
    + return data;
    }
    static void
    -ggp_tcpsocket_close(void *_gc, void *_ps)
    +ggp_tcpsocket_close(void *_gc, void *_data)
    {
    - PurpleSocket *ps = _ps;
    + GGPTcpSocketData *data = _data;
    - purple_socket_destroy(ps);
    + ggp_tcp_socket_data_free(data);
    }
    static ssize_t
    -ggp_tcpsocket_read(void *_gc, void *_ps, unsigned char *buffer, size_t bufsize)
    +ggp_tcpsocket_read(void *_gc, void *_data, unsigned char *buffer, size_t bufsize)
    {
    - PurpleSocket *ps = _ps;
    + GGPTcpSocketData *data = _data;
    + GPollableInputStream *input;
    + gssize ret;
    + GError *error = NULL;
    +
    + if (data->conn == NULL) {
    + return -1;
    + }
    +
    + input = G_POLLABLE_INPUT_STREAM(
    + g_io_stream_get_input_stream(G_IO_STREAM(data->conn)));
    + ret = g_pollable_input_stream_read_nonblocking(input,
    + buffer, bufsize, NULL, &error);
    - return purple_socket_read(ps, buffer, bufsize);
    + if (ret < 0) {
    + if (g_error_matches(error,
    + G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
    + errno = EAGAIN;
    + } else {
    + purple_debug_error("gg", "socket read error: %s",
    + error->message);
    + }
    +
    + g_clear_error(&error);
    + }
    +
    + return ret;
    }
    static ssize_t
    -ggp_tcpsocket_write(void *_gc, void *_ps, const unsigned char *data, size_t len)
    +ggp_tcpsocket_write(void *_gc, void *_data, const unsigned char *data_buf, size_t len)
    {
    - PurpleSocket *ps = _ps;
    + GGPTcpSocketData *data = _data;
    + GPollableOutputStream *output;
    + gssize ret;
    + GError *error = NULL;
    +
    + if (data->conn == NULL) {
    + return -1;
    + }
    +
    + output = G_POLLABLE_OUTPUT_STREAM(
    + g_io_stream_get_output_stream(G_IO_STREAM(data->conn)));
    + ret = g_pollable_output_stream_write_nonblocking(output,
    + data_buf, len, NULL, &error);
    - return purple_socket_write(ps, data, len);
    + if (ret < 0) {
    + if (g_error_matches(error,
    + G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
    + errno = EAGAIN;
    + } else {
    + purple_debug_error("gg", "socket write error: %s",
    + error->message);
    + }
    +
    + g_clear_error(&error);
    + }
    +
    + return ret;
    }
    void
    --- a/libpurple/protocols/irc/msgs.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/protocols/irc/msgs.c Sun Oct 30 12:03:44 2016 +0300
    @@ -1564,6 +1564,8 @@
    int id = 0;
    PurpleConnection *gc = purple_account_get_connection(irc->account);
    const char *mech_list = NULL;
    + char *pos;
    + size_t index;
    if (strncmp(args[2], "sasl ", 6))
    return;
    @@ -1628,6 +1630,15 @@
    }
    irc->sasl_mechs = g_string_new(mech_list);
    + /* Drop EXTERNAL mechanism since we don't support it */
    + if ((pos = strstr(irc->sasl_mechs->str, "EXTERNAL"))) {
    + index = pos - irc->sasl_mechs->str;
    + g_string_erase(irc->sasl_mechs, index, strlen("EXTERNAL"));
    + /* Remove space which separated this mech from the next */
    + if ((irc->sasl_mechs->str)[index] == ' ') {
    + g_string_erase(irc->sasl_mechs, index, 1);
    + }
    + }
    irc_auth_start_cyrus(irc);
    }
    --- a/libpurple/protocols/jabber/Makefile.am Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/protocols/jabber/Makefile.am Sun Oct 30 12:03:44 2016 +0300
    @@ -1,7 +1,5 @@
    EXTRA_DIST = \
    - Makefile.mingw \
    - win32/posix.uname.c \
    - win32/utsname.h
    + Makefile.mingw
    pkgdir = @PURPLE_PLUGINDIR@
    @@ -105,10 +103,6 @@
    JABBERSOURCES += auth_cyrus.c
    endif
    -if IS_WIN32
    -JABBERSOURCES += win32/posix.uname.c
    -endif
    -
    if STATIC_JABBER
    st = -DPURPLE_STATIC_PRPL
    --- a/libpurple/protocols/jabber/Makefile.mingw Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/protocols/jabber/Makefile.mingw Sun Oct 30 12:03:44 2016 +0300
    @@ -24,7 +24,6 @@
    ## INCLUDE PATHS
    ##
    INCLUDE_PATHS += -I. \
    - -I./win32 \
    -I$(GTK_TOP)/include \
    -I$(GTK_TOP)/include/glib-2.0 \
    -I$(GTK_TOP)/lib/glib-2.0/include \
    @@ -95,7 +94,6 @@
    usertune.c \
    xdata.c \
    xmpp.c \
    - win32/posix.uname.c \
    $(VV_SRC)
    OBJECTS = $(C_SRC:%.c=%.o)
    --- a/libpurple/protocols/jabber/iq.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/protocols/jabber/iq.c Sun Oct 30 12:03:44 2016 +0300
    @@ -42,10 +42,6 @@
    #include "data.h"
    #include "ibb.h"
    -#ifdef _WIN32
    -#include "win32/utsname.h"
    -#endif
    -
    static GHashTable *iq_handlers = NULL;
    static GHashTable *signal_iq_handlers = NULL;
    @@ -232,16 +228,6 @@
    if(type == JABBER_IQ_GET) {
    GHashTable *ui_info;
    const char *ui_name = NULL, *ui_version = NULL;
    -#if 0
    - char *os = NULL;
    - if(!purple_prefs_get_bool("/plugins/prpl/jabber/hide_os")) {
    - struct utsname osinfo;
    -
    - uname(&osinfo);
    - os = g_strdup_printf("%s %s %s", osinfo.sysname, osinfo.release,
    - osinfo.machine);
    - }
    -#endif
    iq = jabber_iq_new_query(js, JABBER_IQ_RESULT, "jabber:iq:version");
    if (from)
    @@ -267,13 +253,6 @@
    purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "version"), VERSION, -1);
    }
    -#if 0
    - if(os) {
    - purple_xmlnode_insert_data(purple_xmlnode_new_child(query, "os"), os, -1);
    - g_free(os);
    - }
    -#endif
    -
    jabber_iq_send(iq);
    }
    }
    --- a/libpurple/protocols/jabber/win32/posix.uname.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,141 +0,0 @@
    -/*
    - posix.uname.c - version 1.1
    - Copyright (C) 1999, 2000
    - Earnie Boyd and assigns
    -
    - Fills the utsname structure with the appropriate values.
    -
    - This program is free software; you can redistribute it and/or modify
    - it under the terms of the GNU Lesser General Public License as published
    - by the Free Software Foundation; either version 2.1, 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 PARTICUALR PURPOSE. See the
    - GNU Lesser 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, USA.
    - */
    -
    -/*
    - Send bug reports to Earnie Boyd <earnie_boyd@yahoo.com>
    - */
    -
    -#include "utsname.h"
    -#include <string.h>
    -#include <stdio.h>
    -
    -#include <glib.h>
    -
    -/* ANONYMOUS unions and structs are used from the windows header definitions.
    - These need to be defined for them to work correctly with gcc2.95.2-mingw. */
    -/*#define _ANONYMOUS_STRUCT*/
    -/*#define _ANONYMOUS_UNION*/
    -#include <windows.h>
    -#ifdef __MINGW32__
    -#include <_mingw.h>
    -#endif
    -
    -int
    -jabber_win32_uname( struct utsname *uts )
    -{
    - DWORD sLength;
    - OSVERSIONINFO OS_version;
    - SYSTEM_INFO System_Info;
    -
    -/* XXX Should these be in the global runtime */
    - enum WinOS {Win95, Win98, WinNT, unknown};
    - int MingwOS;
    -
    - memset( uts, 0, sizeof ( *uts ) );
    - OS_version.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
    -
    - GetVersionEx ( &OS_version );
    - GetSystemInfo ( &System_Info );
    -
    - g_strlcpy( uts->sysname, "WIN32_" , sizeof(uts->sysname));
    - switch( OS_version.dwPlatformId )
    - {
    - case VER_PLATFORM_WIN32_NT:
    - g_strlcat( uts->sysname, "WinNT", sizeof(uts->sysname) );
    - MingwOS = WinNT;
    - break;
    - case VER_PLATFORM_WIN32_WINDOWS:
    - switch ( OS_version.dwMinorVersion )
    - {
    - case 0:
    - g_strlcat( uts->sysname, "Win95", sizeof(uts->sysname) );
    - MingwOS = Win95;
    - break;
    - case 10:
    - g_strlcat( uts->sysname, "Win98", sizeof(uts->sysname) );
    - MingwOS = Win98;
    - break;
    - default:
    - g_strlcat( uts->sysname, "Win??", sizeof(uts->sysname) );
    - MingwOS = unknown;
    - break;
    - }
    - break;
    - default:
    - g_strlcat( uts->sysname, "Win??", sizeof(uts->sysname) );
    - MingwOS = unknown;
    - break;
    - }
    -
    -#ifdef __MINGW32__
    - sprintf( uts->version, "%i", __MINGW32_MAJOR_VERSION );
    - sprintf( uts->release, "%i", __MINGW32_MINOR_VERSION );
    -#endif
    -
    - switch( System_Info.wProcessorArchitecture )
    - {
    - case PROCESSOR_ARCHITECTURE_PPC:
    - g_strlcpy( uts->machine, "ppc" , sizeof( uts->machine ) );
    - break;
    - case PROCESSOR_ARCHITECTURE_ALPHA:
    - g_strlcpy( uts->machine, "alpha" , sizeof( uts->machine ) );
    - break;
    - case PROCESSOR_ARCHITECTURE_MIPS:
    - g_strlcpy( uts->machine, "mips" , sizeof( uts->machine ) );
    - break;
    - case PROCESSOR_ARCHITECTURE_INTEL:
    - /* dwProcessorType is only valid in Win95 and Win98
    - wProcessorLevel is only valid in WinNT */
    - switch( MingwOS )
    - {
    - case Win95:
    - case Win98:
    - switch( System_Info.dwProcessorType )
    - {
    - case PROCESSOR_INTEL_386:
    - case PROCESSOR_INTEL_486:
    - case PROCESSOR_INTEL_PENTIUM:
    - sprintf( uts->machine, "i%ld", System_Info.dwProcessorType );
    - break;
    - default:
    - g_strlcpy( uts->machine, "i386" , sizeof( uts->machine ) );
    - break;
    - }
    - break;
    - case WinNT:
    - sprintf( uts->machine, "i%d86", System_Info.wProcessorLevel );
    - break;
    - default:
    - g_strlcpy( uts->machine, "unknown" , sizeof( uts->machine ) );
    - break;
    - }
    - break;
    - default:
    - g_strlcpy( uts->machine, "unknown" , sizeof( uts->machine ) );
    - break;
    - }
    -
    - sLength = sizeof ( uts->nodename ) - 1;
    - GetComputerNameA( uts->nodename, &sLength );
    - return 1;
    -}
    -
    --- a/libpurple/protocols/jabber/win32/utsname.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,24 +0,0 @@
    -#ifndef _SYS_UTSNAME_H
    -#define _SYS_UTSNAME_H
    -
    -#ifdef __cplusplus
    -extern "C" {
    -#endif
    -
    -struct utsname
    -{
    - char sysname[20];
    - char nodename[20];
    - char release[20];
    - char version[20];
    - char machine[20];
    -};
    -
    -int jabber_win32_uname (struct utsname *);
    -#define uname(utsname) jabber_win32_uname(utsname)
    -
    -#ifdef __cplusplus
    -}
    -#endif
    -
    -#endif
    --- a/libpurple/protocols/yahoo/Makefile.am Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/protocols/yahoo/Makefile.am Sun Oct 30 12:03:44 2016 +0300
    @@ -1,6 +1,7 @@
    SUBDIRS = tests
    EXTRA_DIST = \
    + yahoojp.c \
    Makefile.mingw
    pkgdir = @PURPLE_PLUGINDIR@
    --- a/libpurple/purple-client-example.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/purple-client-example.c Sun Oct 30 12:03:44 2016 +0300
    @@ -1,3 +1,25 @@
    +/*
    + * purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    #ifndef DBUS_API_SUBJECT_TO_CHANGE
    #define DBUS_API_SUBJECT_TO_CHANGE
    #endif
    --- a/libpurple/purple-client.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/purple-client.c Sun Oct 30 12:03:44 2016 +0300
    @@ -1,3 +1,25 @@
    +/*
    + * purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    #ifndef DBUS_API_SUBJECT_TO_CHANGE
    #define DBUS_API_SUBJECT_TO_CHANGE
    #endif
    --- a/libpurple/purple-client.h Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/purple-client.h Sun Oct 30 12:03:44 2016 +0300
    @@ -1,3 +1,25 @@
    +/*
    + * purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    #ifndef _PURPLE_CLIENT_H_INCLUDED_
    #define _PURPLE_CLIENT_H_INCLUDED_
    --- a/libpurple/purple-gio.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/purple-gio.c Sun Oct 30 12:03:44 2016 +0300
    @@ -48,9 +48,8 @@
    /* Finally can gracefully close */
    - /* Close wrapper input stream, if any */
    - if (data->input != NULL &&
    - !g_input_stream_close(data->input, NULL, &error)) {
    + /* Close input stream, from wrapper or GIOStream */
    + if (!g_input_stream_close(data->input, NULL, &error)) {
    purple_debug_warning("gio",
    "Error closing input stream: %s",
    error->message);
    @@ -59,9 +58,8 @@
    g_clear_object(&data->input);
    - /* Close wrapper output stream, if any */
    - if (data->output != NULL &&
    - !g_output_stream_close(data->output, NULL, &error)) {
    + /* Close output stream, from wrapper or GIOStream */
    + if (!g_output_stream_close(data->output, NULL, &error)) {
    purple_debug_warning("gio",
    "Error closing output stream: %s",
    error->message);
    @@ -97,7 +95,13 @@
    data = g_new(GracefulCloseData, 1);
    data->stream = g_object_ref(stream);
    +
    + if (input == NULL)
    + input = g_io_stream_get_input_stream(stream);
    data->input = g_object_ref(input);
    +
    + if (output == NULL)
    + output = g_io_stream_get_output_stream(stream);
    data->output = g_object_ref(output);
    /* Try gracefully closing the stream synchronously */
    --- a/libpurple/purple-socket.c Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,410 +0,0 @@
    -/* purple
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - */
    -
    -#include "purple-socket.h"
    -
    -#include "internal.h"
    -
    -#include "debug.h"
    -#include "proxy.h"
    -#include "sslconn.h"
    -
    -typedef enum {
    - PURPLE_SOCKET_STATE_DISCONNECTED = 0,
    - PURPLE_SOCKET_STATE_CONNECTING,
    - PURPLE_SOCKET_STATE_CONNECTED,
    - PURPLE_SOCKET_STATE_ERROR
    -} PurpleSocketState;
    -
    -struct _PurpleSocket
    -{
    - PurpleConnection *gc;
    - gchar *host;
    - int port;
    - gboolean is_tls;
    - GHashTable *data;
    -
    - PurpleSocketState state;
    -
    - PurpleSslConnection *tls_connection;
    - PurpleProxyConnectData *raw_connection;
    - int fd;
    - guint inpa;
    -
    - PurpleSocketConnectCb cb;
    - gpointer cb_data;
    -};
    -
    -static GHashTable *handles = NULL;
    -
    -static void
    -handle_add(PurpleSocket *ps)
    -{
    - PurpleConnection *gc = ps->gc;
    - GSList *l;
    -
    - l = g_hash_table_lookup(handles, gc);
    - l = g_slist_prepend(l, ps);
    - g_hash_table_insert(handles, gc, l);
    -}
    -
    -static void
    -handle_remove(PurpleSocket *ps)
    -{
    - PurpleConnection *gc = ps->gc;
    - GSList *l;
    -
    - l = g_hash_table_lookup(handles, gc);
    - l = g_slist_remove(l, ps);
    - g_hash_table_insert(handles, gc, l);
    -}
    -
    -void
    -_purple_socket_init(void)
    -{
    - handles = g_hash_table_new(g_direct_hash, g_direct_equal);
    -}
    -
    -void
    -_purple_socket_uninit(void)
    -{
    - g_hash_table_destroy(handles);
    - handles = NULL;
    -}
    -
    -PurpleSocket *
    -purple_socket_new(PurpleConnection *gc)
    -{
    - PurpleSocket *ps = g_new0(PurpleSocket, 1);
    -
    - ps->gc = gc;
    - ps->fd = -1;
    - ps->port = -1;
    - ps->data = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
    -
    - handle_add(ps);
    -
    - return ps;
    -}
    -
    -PurpleConnection *
    -purple_socket_get_connection(PurpleSocket *ps)
    -{
    - g_return_val_if_fail(ps != NULL, NULL);
    -
    - return ps->gc;
    -}
    -
    -static gboolean
    -purple_socket_check_state(PurpleSocket *ps, PurpleSocketState wanted_state)
    -{
    - g_return_val_if_fail(ps != NULL, FALSE);
    -
    - if (ps->state == wanted_state)
    - return TRUE;
    -
    - purple_debug_error("socket", "invalid state: %d (should be: %d)",
    - ps->state, wanted_state);
    - ps->state = PURPLE_SOCKET_STATE_ERROR;
    - return FALSE;
    -}
    -
    -void
    -purple_socket_set_tls(PurpleSocket *ps, gboolean is_tls)
    -{
    - g_return_if_fail(ps != NULL);
    -
    - if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
    - return;
    -
    - ps->is_tls = is_tls;
    -}
    -
    -void
    -purple_socket_set_host(PurpleSocket *ps, const gchar *host)
    -{
    - g_return_if_fail(ps != NULL);
    -
    - if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
    - return;
    -
    - g_free(ps->host);
    - ps->host = g_strdup(host);
    -}
    -
    -void
    -purple_socket_set_port(PurpleSocket *ps, int port)
    -{
    - g_return_if_fail(ps != NULL);
    - g_return_if_fail(port >= 0);
    - g_return_if_fail(port <= 65535);
    -
    - if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
    - return;
    -
    - ps->port = port;
    -}
    -
    -static void
    -_purple_socket_connected_raw(gpointer _ps, gint fd, const gchar *error_message)
    -{
    - PurpleSocket *ps = _ps;
    -
    - ps->raw_connection = NULL;
    -
    - if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTING)) {
    - if (fd > 0)
    - close(fd);
    - ps->cb(ps, _("Invalid socket state"), ps->cb_data);
    - return;
    - }
    -
    - if (fd <= 0 || error_message != NULL) {
    - if (error_message == NULL)
    - error_message = _("Unknown error");
    - ps->fd = -1;
    - ps->state = PURPLE_SOCKET_STATE_ERROR;
    - ps->cb(ps, error_message, ps->cb_data);
    - return;
    - }
    -
    - ps->state = PURPLE_SOCKET_STATE_CONNECTED;
    - ps->fd = fd;
    - ps->cb(ps, NULL, ps->cb_data);
    -}
    -
    -static void
    -_purple_socket_connected_tls(gpointer _ps, PurpleSslConnection *tls_connection,
    - PurpleInputCondition cond)
    -{
    - PurpleSocket *ps = _ps;
    -
    - if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTING)) {
    - purple_ssl_close(tls_connection);
    - ps->tls_connection = NULL;
    - ps->cb(ps, _("Invalid socket state"), ps->cb_data);
    - return;
    - }
    -
    - if (ps->tls_connection->fd <= 0) {
    - ps->state = PURPLE_SOCKET_STATE_ERROR;
    - purple_ssl_close(tls_connection);
    - ps->tls_connection = NULL;
    - ps->cb(ps, _("Invalid file descriptor"), ps->cb_data);
    - return;
    - }
    -
    - ps->state = PURPLE_SOCKET_STATE_CONNECTED;
    - ps->fd = ps->tls_connection->fd;
    - ps->cb(ps, NULL, ps->cb_data);
    -}
    -
    -static void
    -_purple_socket_connected_tls_error(PurpleSslConnection *ssl_connection,
    - PurpleSslErrorType error, gpointer _ps)
    -{
    - PurpleSocket *ps = _ps;
    -
    - ps->state = PURPLE_SOCKET_STATE_ERROR;
    - ps->tls_connection = NULL;
    - ps->cb(ps, purple_ssl_strerror(error), ps->cb_data);
    -}
    -
    -gboolean
    -purple_socket_connect(PurpleSocket *ps, PurpleSocketConnectCb cb,
    - gpointer user_data)
    -{
    - PurpleAccount *account = NULL;
    -
    - g_return_val_if_fail(ps != NULL, FALSE);
    -
    - if (ps->gc && purple_connection_is_disconnecting(ps->gc)) {
    - purple_debug_error("socket", "connection is being destroyed");
    - ps->state = PURPLE_SOCKET_STATE_ERROR;
    - return FALSE;
    - }
    -
    - if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_DISCONNECTED))
    - return FALSE;
    - ps->state = PURPLE_SOCKET_STATE_CONNECTING;
    -
    - if (ps->host == NULL || ps->port < 0) {
    - purple_debug_error("socket", "Host or port is not specified");
    - ps->state = PURPLE_SOCKET_STATE_ERROR;
    - return FALSE;
    - }
    -
    - if (ps->gc != NULL)
    - account = purple_connection_get_account(ps->gc);
    -
    - ps->cb = cb;
    - ps->cb_data = user_data;
    -
    - if (ps->is_tls) {
    - ps->tls_connection = purple_ssl_connect(account, ps->host,
    - ps->port, _purple_socket_connected_tls,
    - _purple_socket_connected_tls_error, ps);
    - } else {
    - ps->raw_connection = purple_proxy_connect(ps->gc, account,
    - ps->host, ps->port, _purple_socket_connected_raw, ps);
    - }
    -
    - if (ps->tls_connection == NULL &&
    - ps->raw_connection == NULL)
    - {
    - ps->state = PURPLE_SOCKET_STATE_ERROR;
    - return FALSE;
    - }
    -
    - return TRUE;
    -}
    -
    -gssize
    -purple_socket_read(PurpleSocket *ps, guchar *buf, size_t len)
    -{
    - g_return_val_if_fail(ps != NULL, -1);
    - g_return_val_if_fail(buf != NULL, -1);
    -
    - if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
    - return -1;
    -
    - if (ps->is_tls)
    - return purple_ssl_read(ps->tls_connection, buf, len);
    - else
    - return read(ps->fd, buf, len);
    -}
    -
    -gssize
    -purple_socket_write(PurpleSocket *ps, const guchar *buf, size_t len)
    -{
    - g_return_val_if_fail(ps != NULL, -1);
    - g_return_val_if_fail(buf != NULL, -1);
    -
    - if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
    - return -1;
    -
    - if (ps->is_tls)
    - return purple_ssl_write(ps->tls_connection, buf, len);
    - else
    - return write(ps->fd, buf, len);
    -}
    -
    -void
    -purple_socket_watch(PurpleSocket *ps, PurpleInputCondition cond,
    - PurpleInputFunction func, gpointer user_data)
    -{
    - g_return_if_fail(ps != NULL);
    -
    - if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
    - return;
    -
    - if (ps->inpa > 0)
    - purple_input_remove(ps->inpa);
    - ps->inpa = 0;
    -
    - g_return_if_fail(ps->fd > 0);
    -
    - if (func != NULL)
    - ps->inpa = purple_input_add(ps->fd, cond, func, user_data);
    -}
    -
    -int
    -purple_socket_get_fd(PurpleSocket *ps)
    -{
    - g_return_val_if_fail(ps != NULL, -1);
    -
    - if (!purple_socket_check_state(ps, PURPLE_SOCKET_STATE_CONNECTED))
    - return -1;
    -
    - g_return_val_if_fail(ps->fd > 0, -1);
    -
    - return ps->fd;
    -}
    -
    -void
    -purple_socket_set_data(PurpleSocket *ps, const gchar *key, gpointer data)
    -{
    - g_return_if_fail(ps != NULL);
    - g_return_if_fail(key != NULL);
    -
    - if (data == NULL)
    - g_hash_table_remove(ps->data, key);
    - else
    - g_hash_table_insert(ps->data, g_strdup(key), data);
    -}
    -
    -gpointer
    -purple_socket_get_data(PurpleSocket *ps, const gchar *key)
    -{
    - g_return_val_if_fail(ps != NULL, NULL);
    - g_return_val_if_fail(key != NULL, NULL);
    -
    - return g_hash_table_lookup(ps->data, key);
    -}
    -
    -static void
    -purple_socket_cancel(PurpleSocket *ps)
    -{
    - if (ps->inpa > 0)
    - purple_input_remove(ps->inpa);
    - ps->inpa = 0;
    -
    - if (ps->tls_connection != NULL) {
    - purple_ssl_close(ps->tls_connection);
    - ps->fd = -1;
    - }
    - ps->tls_connection = NULL;
    -
    - if (ps->raw_connection != NULL)
    - purple_proxy_connect_cancel(ps->raw_connection);
    - ps->raw_connection = NULL;
    -
    - if (ps->fd > 0)
    - close(ps->fd);
    - ps->fd = 0;
    -}
    -
    -void
    -purple_socket_destroy(PurpleSocket *ps)
    -{
    - if (ps == NULL)
    - return;
    -
    - handle_remove(ps);
    -
    - purple_socket_cancel(ps);
    -
    - g_free(ps->host);
    - g_hash_table_destroy(ps->data);
    - g_free(ps);
    -}
    -
    -void
    -_purple_socket_cancel_with_connection(PurpleConnection *gc)
    -{
    - GSList *it;
    -
    - it = g_hash_table_lookup(handles, gc);
    - for (; it; it = g_slist_next(it)) {
    - PurpleSocket *ps = it->data;
    - purple_socket_cancel(ps);
    - }
    -}
    --- a/libpurple/purple-socket.h Wed Oct 26 10:17:10 2016 +0000
    +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
    @@ -1,217 +0,0 @@
    -/* purple
    - *
    - * Purple is the legal property of its developers, whose names are too numerous
    - * to list here. Please refer to the COPYRIGHT file distributed with this
    - * source distribution.
    - *
    - * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    - */
    -
    -#ifndef _PURPLE_SOCKET_H_
    -#define _PURPLE_SOCKET_H_
    -/**
    - * SECTION:purple-socket
    - * @section_id: libpurple-purple-socket
    - * @short_description: <filename>purple-socket.h</filename>
    - * @title: Generic Sockets
    - */
    -
    -#include "connection.h"
    -
    -/**
    - * PurpleSocket:
    - *
    - * A structure holding all resources needed for the TCP connection.
    - */
    -typedef struct _PurpleSocket PurpleSocket;
    -
    -/**
    - * PurpleSocketConnectCb:
    - * @ps: The socket.
    - * @error: Error message, or NULL if connection was successful.
    - * @user_data: The user data passed with callback function.
    - *
    - * A callback fired after (successfully or not) establishing a connection.
    - */
    -typedef void (*PurpleSocketConnectCb)(PurpleSocket *ps, const gchar *error,
    - gpointer user_data);
    -
    -/**
    - * purple_socket_new:
    - * @gc: The connection for which the socket is needed, or NULL.
    - *
    - * Creates new, disconnected socket.
    - *
    - * Passing a PurpleConnection allows for proper proxy handling.
    - *
    - * Returns: The new socket struct.
    - */
    -PurpleSocket *
    -purple_socket_new(PurpleConnection *gc);
    -
    -/**
    - * purple_socket_get_connection:
    - * @ps: The socket.
    - *
    - * Gets PurpleConnection tied with specified socket.
    - *
    - * Returns: The PurpleConnection object.
    - */
    -PurpleConnection *
    -purple_socket_get_connection(PurpleSocket *ps);
    -
    -/**
    - * purple_socket_set_tls:
    - * @ps: The socket.
    - * @is_tls: TRUE, if TLS should be handled transparently, FALSE otherwise.
    - *
    - * Determines, if socket should handle TLS.
    - */
    -void
    -purple_socket_set_tls(PurpleSocket *ps, gboolean is_tls);
    -
    -/**
    - * purple_socket_set_host:
    - * @ps: The socket.
    - * @host: The connection host.
    - *
    - * Sets connection host.
    - */
    -void
    -purple_socket_set_host(PurpleSocket *ps, const gchar *host);
    -
    -/**
    - * purple_socket_set_port:
    - * @ps: The socket.
    - * @port: The connection port.
    - *
    - * Sets connection port.
    - */
    -void
    -purple_socket_set_port(PurpleSocket *ps, int port);
    -
    -/**
    - * purple_socket_connect:
    - * @ps: The socket.
    - * @cb: The function to call after establishing a connection, or on
    - * error.
    - * @user_data: The user data to be passed to callback function.
    - *
    - * Establishes a connection.
    - *
    - * Returns: TRUE on success (this doesn't mean it's connected yet), FALSE
    - * otherwise.
    - */
    -gboolean
    -purple_socket_connect(PurpleSocket *ps, PurpleSocketConnectCb cb,
    - gpointer user_data);
    -
    -/**
    - * purple_socket_read:
    - * @ps: The socket.
    - * @buf: The buffer to write data to.
    - * @len: The buffer size.
    - *
    - * Reads incoming data from socket.
    - *
    - * This function deals with TLS, if the socket is configured to do it.
    - *
    - * Returns: Amount of data written, or -1 on error (errno will be also be set).
    - */
    -gssize
    -purple_socket_read(PurpleSocket *ps, guchar *buf, size_t len);
    -
    -/**
    - * purple_socket_write:
    - * @ps: The socket.
    - * @buf: The buffer to read data from.
    - * @len: The amount of data to read and send.
    - *
    - * Sends data through socket.
    - *
    - * This function deals with TLS, if the socket is configured to do it.
    - *
    - * Returns: Amount of data sent, or -1 on error (errno will albo be set).
    - */
    -gssize
    -purple_socket_write(PurpleSocket *ps, const guchar *buf, size_t len);
    -
    -/**
    - * purple_socket_watch:
    - * @ps: The socket.
    - * @cond: The condition type.
    - * @func: The callback function for data, or NULL to remove any
    - * existing callbacks.
    - * @user_data: The user data to be passed to callback function.
    - *
    - * Adds an input handler for the socket.
    - *
    - * If the specified socket had input handler already registered, it will be
    - * removed. To remove any input handlers, pass an NULL handler function.
    - */
    -void
    -purple_socket_watch(PurpleSocket *ps, PurpleInputCondition cond,
    - PurpleInputFunction func, gpointer user_data);
    -
    -/**
    - * purple_socket_get_fd:
    - * @ps: The socket
    - *
    - * Gets underlying file descriptor for socket.
    - *
    - * It's not meant to read/write data (use purple_socket_read/
    - * purple_socket_write), rather for watching for changes with select().
    - *
    - * Returns: The file descriptor, or -1 on error.
    - */
    -int
    -purple_socket_get_fd(PurpleSocket *ps);
    -
    -/**
    - * purple_socket_set_data:
    - * @ps: The socket.
    - * @key: The unique key.
    - * @data: The data to assign, or NULL to remove.
    - *
    - * Sets extra data for a socket.
    - */
    -void
    -purple_socket_set_data(PurpleSocket *ps, const gchar *key, gpointer data);
    -
    -/**
    - * purple_socket_get_data:
    - * @ps: The socket.
    - * @key: The unqiue key.
    - *
    - * Returns extra data in a socket.
    - *
    - * Returns: The data associated with the key.
    - */
    -gpointer
    -purple_socket_get_data(PurpleSocket *ps, const gchar *key);
    -
    -/**
    - * purple_socket_destroy:
    - * @ps: The socket.
    - *
    - * Destroys the socket, closes connection and frees all resources.
    - *
    - * If file descriptor for the socket was extracted with purple_socket_get_fd and
    - * added to event loop, it have to be removed prior this.
    - */
    -void
    -purple_socket_destroy(PurpleSocket *ps);
    -
    -#endif /* _PURPLE_SOCKET_H_ */
    --- a/libpurple/tests/test_des.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/tests/test_des.c Sun Oct 30 12:03:44 2016 +0300
    @@ -1,3 +1,25 @@
    +/*
    + * purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    #include <glib.h>
    #include <purple.h>
    --- a/libpurple/tests/test_des3.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/tests/test_des3.c Sun Oct 30 12:03:44 2016 +0300
    @@ -1,3 +1,25 @@
    +/*
    + * purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    #include <glib.h>
    #include <purple.h>
    --- a/libpurple/tests/test_hmac.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/tests/test_hmac.c Sun Oct 30 12:03:44 2016 +0300
    @@ -1,3 +1,25 @@
    +/*
    + * purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    #include <glib.h>
    #include <purple.h>
    --- a/libpurple/tests/test_md4.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/tests/test_md4.c Sun Oct 30 12:03:44 2016 +0300
    @@ -1,3 +1,25 @@
    +/*
    + * purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    #include <glib.h>
    #include <purple.h>
    --- a/libpurple/tests/test_md5.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/tests/test_md5.c Sun Oct 30 12:03:44 2016 +0300
    @@ -1,3 +1,25 @@
    +/*
    + * purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    #include <glib.h>
    #include <purple.h>
    --- a/libpurple/tests/test_sha1.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/tests/test_sha1.c Sun Oct 30 12:03:44 2016 +0300
    @@ -1,3 +1,25 @@
    +/*
    + * purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    #include <glib.h>
    #include <purple.h>
    --- a/libpurple/tests/test_sha256.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/tests/test_sha256.c Sun Oct 30 12:03:44 2016 +0300
    @@ -1,3 +1,25 @@
    +/*
    + * purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    #include <glib.h>
    #include <purple.h>
    --- a/libpurple/tests/test_util.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/tests/test_util.c Sun Oct 30 12:03:44 2016 +0300
    @@ -1,3 +1,25 @@
    +/*
    + * purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    #include <glib.h>
    #include "../util.h"
    --- a/libpurple/tests/test_xmlnode.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/libpurple/tests/test_xmlnode.c Sun Oct 30 12:03:44 2016 +0300
    @@ -1,3 +1,25 @@
    +/*
    + * purple
    + *
    + * Purple is the legal property of its developers, whose names are too numerous
    + * to list here. Please refer to the COPYRIGHT file distributed with this
    + * source distribution.
    + *
    + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
    + *
    + */
    #include <glib.h>
    #include "../xmlnode.h"
    --- a/pidgin/data/pidgin-3-uninstalled.pc.in Wed Oct 26 10:17:10 2016 +0000
    +++ b/pidgin/data/pidgin-3-uninstalled.pc.in Sun Oct 30 12:03:44 2016 +0300
    @@ -15,7 +15,7 @@
    plugindir=${libdir}/pidgin
    Name: Pidgin
    -Description: Pidgin is a GTK2-based instant messenger application.
    +Description: Pidgin is a GTK3-based instant messenger application.
    Version: @VERSION@
    -Requires: gtk+-2.0 purple-3
    +Requires: gtk+-3.0 webkitgtk-3.0 purple-3
    Cflags: -I${abs_top_srcdir}
    --- a/pidgin/gtkutils.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/pidgin/gtkutils.c Sun Oct 30 12:03:44 2016 +0300
    @@ -1189,20 +1189,12 @@
    GdkRectangle monitor;
    gint monitor_num;
    gint space_left, space_right, space_above, space_below;
    - gint needed_width;
    - gint needed_height;
    - gint xthickness;
    - gint ythickness;
    gboolean rtl;
    g_return_if_fail(GTK_IS_MENU(menu));
    widget = GTK_WIDGET(menu);
    screen = gtk_widget_get_screen(widget);
    - context = gtk_widget_get_style_context(widget);
    - gtk_style_context_get(context, gtk_style_context_get_state(context),
    - "xthickness", &xthickness,
    - "ythickness", &ythickness, NULL);
    rtl = (gtk_widget_get_direction(widget) == GTK_TEXT_DIR_RTL);
    /*
    @@ -1245,24 +1237,14 @@
    /* position horizontally */
    - /* the amount of space we need to position the menu. Note the
    - * menu is offset "xthickness" pixels
    - */
    - needed_width = requisition.width - xthickness;
    -
    - if (needed_width <= space_left ||
    - needed_width <= space_right)
    + if (requisition.width <= space_left ||
    + requisition.width <= space_right)
    {
    - if ((rtl && needed_width <= space_left) ||
    - (!rtl && needed_width > space_right))
    + if ((rtl && requisition.width <= space_left) ||
    + (!rtl && requisition.width > space_right))
    {
    /* position left */
    - *x = *x + xthickness - requisition.width + 1;
    - }
    - else
    - {
    - /* position right */
    - *x = *x - xthickness;
    + *x = *x - requisition.width + 1;
    }
    /* x is clamped on-screen further down */
    @@ -1301,20 +1283,19 @@
    /* Position vertically. The algorithm is the same as above, but
    * simpler because we don't have to take RTL into account.
    */
    - needed_height = requisition.height - ythickness;
    -
    - if (needed_height <= space_above ||
    - needed_height <= space_below)
    +
    + if (requisition.height <= space_above ||
    + requisition.height <= space_below)
    {
    - if (needed_height <= space_below)
    - *y = *y - ythickness;
    - else
    - *y = *y + ythickness - requisition.height + 1;
    + if (requisition.height > space_below) {
    + *y = *y - requisition.height + 1;
    + }
    *y = CLAMP (*y, monitor.y,
    monitor.y + monitor.height - requisition.height);
    }
    - else if (needed_height > space_below && needed_height > space_above)
    + else if (requisition.height > space_below &&
    + requisition.height > space_above)
    {
    if (space_below >= space_above)
    *y = monitor.y + monitor.height - requisition.height;
    @@ -1340,18 +1321,13 @@
    GtkTreePath *path;
    GtkTreeViewColumn *col;
    GdkRectangle rect;
    - GtkStyleContext *context;
    - gint ythickness;
    -
    - context = gtk_widget_get_style_context(GTK_WIDGET(menu));
    - gtk_style_context_get(context, gtk_style_context_get_state(context),
    - "ythickness", &ythickness, NULL);
    +
    gdk_window_get_origin (gtk_widget_get_window(widget), x, y);
    gtk_tree_view_get_cursor (tv, &path, &col);
    gtk_tree_view_get_cell_area (tv, path, col, &rect);
    *x += rect.x+rect.width;
    - *y += rect.y+rect.height+ythickness;
    + *y += rect.y + rect.height;
    pidgin_menu_position_func_helper(menu, x, y, push_in, data);
    }
    --- a/pidgin/gtkwebviewtoolbar.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/pidgin/gtkwebviewtoolbar.c Sun Oct 30 12:03:44 2016 +0300
    @@ -1197,17 +1197,10 @@
    gpointer data)
    {
    GtkWidget *widget = GTK_WIDGET(data);
    - GtkRequisition menu_req;
    GtkAllocation allocation;
    - GtkStyleContext *context;
    - gint ythickness;
    int savy;
    - context = gtk_widget_get_style_context(widget);
    - gtk_style_context_get(context, gtk_style_context_get_state(context),
    - "ythickness", &ythickness, NULL);
    gtk_widget_get_allocation(widget, &allocation);
    - gtk_widget_get_preferred_size(GTK_WIDGET(menu), NULL, &menu_req);
    gdk_window_get_origin(gtk_widget_get_window(widget), x, y);
    *x += allocation.x;
    *y += allocation.y + allocation.height;
    @@ -1215,7 +1208,7 @@
    pidgin_menu_position_func_helper(menu, x, y, push_in, data);
    - if (savy > *y + ythickness + 1)
    + if (savy > *y + 1)
    *y -= allocation.height;
    }
    --- a/pidgin/libpidgin.c Wed Oct 26 10:17:10 2016 +0000
    +++ b/pidgin/libpidgin.c Sun Oct 30 12:03:44 2016 +0300
    @@ -70,10 +70,13 @@
    #include "pidginstock.h"
    #include "gtkwhiteboard.h"
    +#ifndef _WIN32
    #include <signal.h>
    +#endif
    #include <getopt.h>
    +#ifndef _WIN32
    /*
    * Lists of signals we wish to catch and those we wish to ignore.
    @@ -92,6 +95,7 @@
    SIGPIPE,
    -1
    };
    +#endif /* !_WIN32 */
    static void
    dologin_named(const char *name)
    @@ -121,6 +125,7 @@
    }
    }
    +#ifndef _WIN32
    static char *segfault_message;
    static int signal_sockets[2];
    @@ -189,6 +194,7 @@
    return TRUE;
    }
    +#endif /* !_WIN32 */
    static int
    ui_main(void)
    @@ -427,21 +433,23 @@
    GtkCssProvider *provider;
    GdkScreen *screen;
    GList *accounts;
    +#ifndef _WIN32
    int sig_indx; /* for setting up signal catching */
    sigset_t sigset;
    char errmsg[BUFSIZ];
    GIOChannel *signal_channel;
    GIOStatus signal_status;
    guint signal_channel_watcher;
    - GError *error;
    #ifndef DEBUG
    char *segfault_message_tmp;
    #endif /* DEBUG */
    +#endif /* !_WIN32 */
    int opt;
    gboolean gui_check;
    gboolean debug_enabled, debug_colored;
    GList *active_accounts;
    GStatBuf st;
    + GError *error;
    struct option long_options[] = {
    {"config", required_argument, NULL, 'c'},
    @@ -474,6 +482,8 @@
    /* Locale initialization is not complete here. See gtk_init_check() */
    setlocale(LC_ALL, "");
    +#ifndef _WIN32
    +
    #ifndef DEBUG
    /* We translate this here in case the crash breaks gettext. */
    segfault_message_tmp = g_strdup_printf(_(
    @@ -574,6 +584,7 @@
    snprintf(errmsg, sizeof(errmsg), "Warning: couldn't unblock signals");
    perror(errmsg);
    }
    +#endif /* !_WIN32 */
    /* scan command-line options */
    opterr = 1;
    @@ -626,7 +637,9 @@
    case '?': /* show terse help */
    default:
    show_usage(argv[0], TRUE);
    +#ifndef _WIN32
    g_free(segfault_message);
    +#endif
    return 0;
    break;
    }
    @@ -635,14 +648,18 @@
    /* show help message */
    if (opt_help) {
    show_usage(argv[0], FALSE);
    +#ifndef _WIN32
    g_free(segfault_message);
    +#endif
    return 0;
    }
    /* show version message */
    if (opt_version) {
    printf("%s %s (libpurple %s)\n", PIDGIN_NAME, DISPLAY_VERSION,
    purple_core_get_version());
    +#ifndef _WIN32
    g_free(segfault_message);
    +#endif
    return 0;
    }
    @@ -677,7 +694,9 @@
    printf("%s %s\n", PIDGIN_NAME, DISPLAY_VERSION);
    g_warning("cannot open display: %s", display ? display : "unset");
    +#ifndef _WIN32
    g_free(segfault_message);
    +#endif
    return 1;
    }
    @@ -711,7 +730,9 @@
    fprintf(stderr,
    "Initialization of the libpurple core failed. Dumping core.\n"
    "Please report this!\n");
    +#ifndef _WIN32
    g_free(segfault_message);
    +#endif
    abort();
    }
    @@ -737,7 +758,9 @@
    gdk_notify_startup_complete();
    purple_core_quit();
    g_printerr(_("Exiting because another libpurple client is already running.\n"));
    +#ifndef _WIN32
    g_free(segfault_message);
    +#endif
    return 0;
    }
    @@ -822,10 +845,12 @@
    gtk_main();
    +#ifndef _WIN32
    g_free(segfault_message);
    g_source_remove(signal_channel_watcher);
    close(signal_sockets[0]);
    close(signal_sockets[1]);
    +#endif
    #ifdef _WIN32
    winpidgin_cleanup();
    --- a/po/POTFILES.in Wed Oct 26 10:17:10 2016 +0000
    +++ b/po/POTFILES.in Sun Oct 30 12:03:44 2016 +0300
    @@ -191,7 +191,6 @@
    libpurple/protocols/yahoo/ymsg.c
    libpurple/protocols/zephyr/zephyr.c
    libpurple/proxy.c
    -libpurple/purple-socket.c
    libpurple/request.c
    libpurple/request.h
    libpurple/savedstatuses.c
    --- a/po/POTFILES.skip Wed Oct 26 10:17:10 2016 +0000
    +++ b/po/POTFILES.skip Sun Oct 30 12:03:44 2016 +0300
    @@ -69,6 +69,7 @@
    pidgin/plugins/perl/common/GtkThemes.c
    pidgin/plugins/perl/common/GtkUtils.c
    pidgin/plugins/perl/common/Pidgin.c
    +sub/libpurple/data/purple-url-handler.desktop.in
    sub/libpurple/plugins/perl/common/Account.c
    sub/libpurple/plugins/perl/common/AccountOpts.c
    sub/libpurple/plugins/perl/common/BuddyIcon.c