# HG changeset patch # User Gary Kramlich # Date 1749098828 18000 # Node ID 50facee54d1db011e4eee704d64d32c6e24a0b75 # Parent b9cf92c8b16ba219fa22f0470d1a735b6e76f224 Remove the retro protocol plugins This removes gadu-gadu, novell, sametime, silc, silc10, and zephyr as they are all being migrated to retro-prpl. Testing Done: Ran `make distcheck` and built the windows installer and verified that both worked as expected and that the uninstaller didn't remove the files. I did have to clean my `win32-install-dir` as that had contained previous builds of the now deleted protocols. Bugs closed: PIDGIN-18098 Reviewed at https://reviews.imfreedom.org/r/4019/ diff -r b9cf92c8b16b -r 50facee54d1d FUZZING --- a/FUZZING Wed Jun 04 23:12:27 2025 -0500 +++ b/FUZZING Wed Jun 04 23:47:08 2025 -0500 @@ -12,7 +12,7 @@ Example: ```bash -$ CC=clang ./configure --enable-fuzzing --disable-cyrus-sasl --disable-gtkui --disable-gstreamer --disable-vv --disable-idn --disable-meanwhile --disable-avahi --disable-libgadu --disable-dbus --disable-libsecret --disable-gnome-keyring --disable-kwallet --disable-plugins +$ CC=clang ./configure --enable-fuzzing --disable-cyrus-sasl --disable-gtkui --disable-gstreamer --disable-vv --disable-idn --disable-avahi --disable-dbus --disable-libsecret --disable-gnome-keyring --disable-kwallet --disable-plugins ``` Now that the build system has been configured, you need to build everything, diff -r b9cf92c8b16b -r 50facee54d1d INSTALL --- a/INSTALL Wed Jun 04 23:12:27 2025 -0500 +++ b/INSTALL Wed Jun 04 23:47:08 2025 -0500 @@ -146,14 +146,12 @@ `--disable-gstreamer' will build without sound support. This applies to *both* Pidgin and Finch. - `--enable-gnutls=yes,no' will enable or disable the use of gnutls for ssl support. Disabling both gnutls and nss will mean you cannot use either MSN or Google Talk. There is no static option for gnutls at this time. + `--enable-gnutls=yes,no' will enable or disable the use of gnutls for ssl support. There is no static option for gnutls at this time. - `--enable-nss=yes,no,static' will enable or disable the use of nss for ssl support. This is the only option for ssl support if you are attempting to compile a static version of Pidgin or Finch. + `--enable-nss=yes,no,static' will enable or disable the use of nss for ssl support. This is the only option for ssl support if you are attempting to compile a static version of Pidgin or Finch. Optional Packages: - `--with-silc-includes=DIR' and `--with-silc-libs=DIR' can be used if your silc libraries are installed to a location not in your path. - `--with-static-prpls' takes a list of comma separated protocols to build in statically (rather than as plugins). Use this with care. `--with-dynamic-prpls' takes a list of comma separated protocols also. If used only those listed will be built. If no protocols are listed with either `--with-static-prpls' or with `--with-dynamic-prpls' then Pidgin and Finch will be effectively useless. diff -r b9cf92c8b16b -r 50facee54d1d Makefile.mingw --- a/Makefile.mingw Wed Jun 04 23:12:27 2025 -0500 +++ b/Makefile.mingw Wed Jun 04 23:47:08 2025 -0500 @@ -86,7 +86,6 @@ libenchant_ispell.dll \ libenchant_myspell.dll \ libgtkspell-0.dll \ - libmeanwhile-1.dll \ libnspr4.dll \ libplc4.dll \ libplds4.dll \ @@ -196,7 +195,6 @@ tar -cf - `find $(PIDGIN_INSTALL_DIR) \( -name '*.dll' -o -name '*.exe' \) \ -not \( -false $(EXTERNAL_DLLS_FIND_EXP) \) -print` \ | tar --strip 2 --xform s/$$/.dbgsym/ -xC $(DEBUG_SYMBOLS_DIR) -f - - cp $(MEANWHILE_TOP)/bin/libmeanwhile-1.dll.unstripped $(DEBUG_SYMBOLS_DIR)/libmeanwhile-1.dll.dbgsym zip -9 -r $(DEBUG_SYMBOLS_DIR).zip $(DEBUG_SYMBOLS_DIR) $(call gpg_sign, $(DEBUG_SYMBOLS_DIR).zip) diff -r b9cf92c8b16b -r 50facee54d1d config.h.mingw --- a/config.h.mingw Wed Jun 04 23:12:27 2025 -0500 +++ b/config.h.mingw Wed Jun 04 23:47:08 2025 -0500 @@ -155,9 +155,6 @@ /* Define if your file defines LC_MESSAGES. */ /* #define HAVE_LC_MESSAGES 1 */ -/* Linked with external libgadu */ -#define HAVE_LIBGADU 1 - /* Define to 1 if you have the `nsl' library (-lnsl). */ /* #define HAVE_LIBNSL 1 */ @@ -245,9 +242,6 @@ /* Define to 1 if you have the header file. */ /* #define HAVE_SIGNAL_H 1 */ -/* Define if we have silcmime.h */ -#define HAVE_SILCMIME_H 1 - /* Define to 1 if you have the header file. */ /* #undef HAVE_SMIME_H */ @@ -385,9 +379,6 @@ /* whether or not we have xsltproc for devhelp index */ /* #define HAVE_XSLTPROC 1 */ -/* Define if external libzephyr should be used. */ -/* #undef LIBZEPHYR_EXT */ - /* Define to the sub-directory in which libtool stores uninstalled libraries. */ /* #define LT_OBJDIR ".libs/" */ @@ -488,12 +479,6 @@ /* Define to 1 if the X Window System is missing or not being used. */ /* #undef X_DISPLAY_MISSING */ -/* Size of an int32. */ -#define ZEPHYR_INT32 long - -/* Define if kerberos should be used in Zephyr. */ -/* #undef ZEPHYR_USES_KERBEROS */ - /* Number of bits in a file offset, on hosts where this is settable. */ /* #define _FILE_OFFSET_BITS 64 */ diff -r b9cf92c8b16b -r 50facee54d1d configure.ac --- a/configure.ac Wed Jun 04 23:12:27 2025 -0500 +++ b/configure.ac Wed Jun 04 23:47:08 2025 -0500 @@ -926,28 +926,6 @@ fi dnl ####################################################################### -dnl # Check for Meanwhile headers (for Sametime) -dnl ####################################################################### -AC_ARG_ENABLE(meanwhile, - [AC_HELP_STRING([--disable-meanwhile], - [compile without meanwhile (required for Sametime support)])], - enable_meanwhile="$enableval", enable_meanwhile="yes") -if test "x$enable_meanwhile" = "xyes"; then - PKG_CHECK_MODULES(MEANWHILE, [meanwhile >= 1.0.0 meanwhile < 2.0.0], [ - have_meanwhile="yes" - ], [ - have_meanwhile="no" - if test "x$force_deps" = "xyes" ; then - AC_MSG_ERROR([ -Meanwhile development headers not found. -Use --disable-meanwhile if you do not need meanwhile (Sametime) support. -]) - fi]) -fi -AC_SUBST(MEANWHILE_CFLAGS) -AC_SUBST(MEANWHILE_LIBS) - -dnl ####################################################################### dnl # Check for Native Avahi headers (for Bonjour) dnl ####################################################################### AC_ARG_ENABLE(avahi, @@ -1000,158 +978,6 @@ AC_SUBST(AVAHI_CFLAGS) AC_SUBST(AVAHI_LIBS) - -dnl ####################################################################### -dnl # Check for SILC client includes and libraries -dnl ####################################################################### -AC_ARG_WITH(silc-includes, [AC_HELP_STRING([--with-silc-includes=DIR], [compile the SILC plugin against includes in DIR])], [ac_silc_includes="$withval"], [ac_silc_includes="no"]) -AC_ARG_WITH(silc-libs, [AC_HELP_STRING([--with-silc-libs=DIR], [compile the SILC plugin against the SILC libs in DIR])], [ac_silc_libs="$withval"], [ac_silc_libs="no"]) -SILC_CFLAGS="" -SILC_LIBS="" -have_silc="no" -if test -n "$with_silc_includes" || test -n "$with_silc_libs"; then - silc_manual_check="yes" -else - silc_manual_check="no" -fi -if test "x$silc_manual_check" = "xno"; then - PKG_CHECK_MODULES(SILC, [silcclient >= 1.1], [ - have_silc="yes" - silcincludes="yes" - silcclient="yes" - ], [ - have_silc="no" - ]) - if test "x$have_silc" = "xno"; then - PKG_CHECK_MODULES(SILC, silcclient, [ - have_silc="yes" - silc10includes="yes" - silc10client="yes" - ], [ - have_silc="no" - ]) - dnl If silcclient.pc wasn't found, check for just silc.pc - if test "x$have_silc" = "xno"; then - PKG_CHECK_MODULES(SILC, silc, [ - have_silc="yes" - silc10includes="yes" - silc10client="yes" - ], [ - have_silc="no" - ]) - fi - fi -else - if test "$ac_silc_includes" != "no"; then - SILC_CFLAGS="-I$ac_silc_includes" - fi - CPPFLAGS_save="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $SILC_CFLAGS" - AC_CHECK_HEADER(silc.h, [silcincludes=yes]) - CPPFLAGS="$CPPFLAGS_save" - - if test "$ac_silc_libs" != "no"; then - SILC_LIBS="-L$ac_silc_libs" - fi - SILC_LIBS="$SILC_LIBS -lsilc -lsilcclient -lpthread $LIBDL" - AC_CHECK_LIB(silcclient, silc_client_init, [silcclient=yes], , $SILC_LIBS) - - if test "x$silcincludes" = "xyes" -a "x$silcclient" = "xyes"; then - have_silc="yes" - else - CPPFLAGS_save="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $SILC_CFLAGS" - AC_CHECK_HEADER(silcincludes.h, [silc10includes=yes]) - CPPFLAGS="$CPPFLAGS_save" - - SILC_LIBS="$SILC_LIBS -lsilc -lsilcclient -lpthread $LIBDL" - AC_CHECK_LIB(silcclient, silc_client_init, [silc10client=yes], , $SILC_LIBS) - if test "x$silc10includes" = "xyes" -a "x$silc10client" = "xyes"; then - have_silc="yes" - fi - fi -fi -AC_SUBST(SILC_LIBS) -AC_SUBST(SILC_CFLAGS) -dnl SILC Toolkit >= 1.0.1 has a new MIME API -if test "x$silcclient" = "xyes"; then - AC_DEFINE(HAVE_SILCMIME_H, 1, [Define if we have silcmime.h]) -elif test "x$silc10client" = "xyes"; then - CPPFLAGS_save="$CPPFLAGS" - CPPFLAGS="$CPPFLAGS $SILC_CFLAGS" - AC_MSG_CHECKING(for silcmime.h) - AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ -#include -#include - ]], [[]])], [ - AC_MSG_RESULT(yes) - AC_DEFINE(HAVE_SILCMIME_H, 1, [Define if we have silcmime.h]) - ], [ - AC_MSG_RESULT(no) - ]) - CPPFLAGS="$CPPFLAGS_save" -fi - -dnl ####################################################################### -dnl # Check for Gadu-Gadu protocol library (libgadu) -dnl ####################################################################### - -PKG_CHECK_MODULES(LIBGADU, [libgadu >= 1.12.0], [have_libgadu=yes], [have_libgadu=no]) - -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([ -libgadu is not compatible with the GPL when compiled with OpenSSL support. - -To compile against system libgadu, please recompile libgadu using: -./configure --with-openssl=no -Then rerun this ./configure - -Falling back to using our own copy of libgadu. - ]) - ]) -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 - 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 - - 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) -AC_SUBST(LIBGADU_CFLAGS) - - AC_ARG_ENABLE(distrib,,,enable_distrib=no) AM_CONDITIONAL(DISTRIB, test "x$enable_distrib" = "xyes") DYNAMIC_PRPLS=all @@ -1161,101 +987,56 @@ fi if test "x$STATIC_PRPLS" = "xall" ; then - STATIC_PRPLS="bonjour gg irc jabber novell sametime silc simple zephyr" -fi -if test "x$have_meanwhile" != "xyes" ; then - STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/sametime//'` + STATIC_PRPLS="bonjour irc jabber simple" fi if test "x$avahiincludes" != "xyes" -o "x$avahilibs" != "xyes"; then STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/bonjour//'` fi -if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then - STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc/silc10/'` -fi -if test "x$silc10includes" != "xyes" -o "x$silc10client" != "xyes"; then - STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc10//'` -fi AC_SUBST(STATIC_PRPLS) STATIC_LINK_LIBS= extern_init= load_proto= for i in $STATIC_PRPLS ; do - dnl Ugly special case for "libsilcpurple.la": - if test "x$i" = "xsilc"; then - STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib${i}purple.la" - elif test "x$i" = "xsilc10"; then - STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/libsilcpurple.la" - else - STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib$i.la" - fi + STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib$i.la" extern_init="$extern_init extern gboolean purple_init_${i}_plugin();" load_proto="$load_proto purple_init_${i}_plugin();" case $i in bonjour) static_bonjour=yes ;; - gg) static_gg=yes ;; irc) static_irc=yes ;; jabber) static_jabber=yes ;; - novell) static_novell=yes ;; - sametime) static_sametime=yes ;; - silc) static_silc=yes ;; - silc10) static_silc=yes ;; simple) static_simple=yes ;; - zephyr) static_zephyr=yes ;; *) echo "Invalid static protocol $i!!" ; exit 1 ;; esac done AM_CONDITIONAL(STATIC_BONJOUR, test "x$static_bonjour" = "xyes") -AM_CONDITIONAL(STATIC_GG, test "x$static_gg" = "xyes") AM_CONDITIONAL(STATIC_IRC, test "x$static_irc" = "xyes") AM_CONDITIONAL(STATIC_JABBER, test "x$static_jabber" = "xyes") -AM_CONDITIONAL(STATIC_NOVELL, test "x$static_novell" = "xyes") -AM_CONDITIONAL(STATIC_SAMETIME, test "x$static_sametime" = "xyes" -a "x$have_meanwhile" = "xyes") -AM_CONDITIONAL(STATIC_SILC, test "x$static_silc" = "xyes" -a "x$have_silc" = "xyes") AM_CONDITIONAL(STATIC_SIMPLE, test "x$static_simple" = "xyes") -AM_CONDITIONAL(STATIC_ZEPHYR, test "x$static_zephyr" = "xyes") AC_SUBST(STATIC_LINK_LIBS) AC_DEFINE_UNQUOTED(STATIC_PROTO_INIT, $extern_init static void static_proto_init(void) { $load_proto }, [Loads static protocol plugin module initialization functions.]) AC_ARG_WITH(dynamic_prpls, [AC_HELP_STRING([--with-dynamic-prpls], [specify which protocols to build dynamically])], [DYNAMIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`]) if test "x$DYNAMIC_PRPLS" = "xall" ; then - DYNAMIC_PRPLS="bonjour gg irc jabber novell sametime silc simple zephyr" -fi -if test "x$have_meanwhile" != "xyes"; then - DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/sametime//'` + DYNAMIC_PRPLS="bonjour irc jabber simple" fi if test "x$avahiincludes" != "xyes" -o "x$avahilibs" != "xyes"; then DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/bonjour//'` fi -if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then - DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc/silc10/'` -fi -if test "x$silc10includes" != "xyes" -o "x$silc10client" != "xyes"; then - DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc10//'` -fi AC_SUBST(DYNAMIC_PRPLS) for i in $DYNAMIC_PRPLS ; do case $i in bonjour) dynamic_bonjour=yes ;; - gg) dynamic_gg=yes ;; irc) dynamic_irc=yes ;; jabber) dynamic_jabber=yes ;; - novell) dynamic_novell=yes ;; null) dynamic_null=yes ;; - sametime) dynamic_sametime=yes ;; - silc) dynamic_silc=yes ;; - silc10) dynamic_silc=yes ;; simple) dynamic_simple=yes ;; - zephyr) dynamic_zephyr=yes ;; *) echo "Invalid dynamic protocol $i!!" ; exit 1 ;; esac done AC_ARG_ENABLE(plugins, [AC_HELP_STRING([--disable-plugins], [compile without plugin support])], , enable_plugins=yes) -AC_ARG_WITH(krb4, [AC_HELP_STRING([--with-krb4=PREFIX], [compile Zephyr plugin with Kerberos 4 support])], kerberos="$withval", kerberos="no") -AC_ARG_WITH(zephyr, [AC_HELP_STRING([--with-zephyr=PREFIX], [compile Zephyr plugin against external libzephyr])], zephyr="$withval", zephyr="no") -AM_CONDITIONAL(EXTERNAL_LIBZEPHYR, test "x$zephyr" != "xno") AC_CHECK_HEADERS(sys/utsname.h) AC_CHECK_FUNC(uname) @@ -1715,7 +1496,7 @@ [enable_nss="$enableval"], [enable_nss="yes"]) -msg_ssl="None. Novell Groupwise and Google Talk will not work without GnuTLS or NSS. OpenSSL is NOT usable!" +msg_ssl="None. OpenSSL is NOT usable!" looked_for_gnutls="no" dnl # dnl # Check for GnuTLS if it's specified. @@ -2126,19 +1907,16 @@ AC_MSG_ERROR([ Neither GnuTLS or NSS SSL development headers found. Use --disable-nss --disable-gnutls if you do not need SSL support. -Novell Groupwise and Google Talk will not work without GnuTLS or NSS. OpenSSL is NOT usable! ]) elif test "x$looked_for_gnutls" = "xyes" -a "x$force_deps" = "xyes" ; then AC_MSG_ERROR([ GnuTLS SSL development headers not found. Use --disable-gnutls if you do not need SSL support. -Novell Groupwise and Google Talk will not work without SSL support. ]) elif test "x$looked_for_nss" = "xyes" -a "x$force_deps" = "xyes" ; then AC_MSG_ERROR([ NSS SSL development headers not found. Use --disable-nss if you do not need SSL support. -Novell Groupwise and Google Talk will not work without SSL support. ]) fi @@ -2326,74 +2104,6 @@ AM_CONDITIONAL(USE_CYRUS_SASL, false) fi -dnl ####################################################################### -dnl # Check for Kerberos (for Zephyr) -dnl ####################################################################### -AC_DEFINE(ZEPHYR_INT32, long, [Size of an int32.]) -AC_SUBST(KRB4_CFLAGS) -AC_SUBST(KRB4_LDFLAGS) -AC_SUBST(KRB4_LIBS) -if test "$kerberos" != "no" ; then - if test "$kerberos" != "yes" ; then - KRB4_CFLAGS="-I${kerberos}/include" - if test -d "$kerberos/include/kerberosIV" ; then - KRB4_CFLAGS="$KRB4_CFLAGS -I${kerberos}/include/kerberosIV" - fi - KRB4_LDFLAGS="-L${kerberos}/lib" - elif test -d /usr/local/include/kerberosIV ; then - KRB4_CFLAGS="-I/usr/local/include/kerberosIV" - elif test -d /usr/include/kerberosIV ; then - KRB4_CFLAGS="-I/usr/include/kerberosIV" - fi - AC_DEFINE(ZEPHYR_USES_KERBEROS, 1, [Define if kerberos should be used in Zephyr.]) - - orig_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $KRB4_LDFLAGS" - AC_CHECK_LIB(krb4, krb_rd_req, - [KRB4_LIBS="-lkrb4 -ldes425 -lkrb5 -lk5crypto -lcom_err"], - [AC_CHECK_LIB(krb, krb_rd_req, - [KRB4_LIBS="-lkrb -ldes"], - [AC_MSG_ERROR([Kerberos 4 libraries not found])], - -ldes)], - -ldes425 -lkrb5 -lk5crypto -lcom_err) - orig_LIBS="$LIBS" - LIBS="$LIBS $KRB4_LIBS" - AC_CHECK_FUNCS(krb_set_key krb_rd_req krb_get_lrealm) - AC_CHECK_FUNCS(krb_get_err_text krb_log) - LIBS="$orig_LIBS" - LDFLAGS="$orig_LDFLAGS" -fi - -dnl ####################################################################### -dnl # Check for external libzephyr -dnl ####################################################################### -AC_SUBST(ZEPHYR_CFLAGS) -AC_SUBST(ZEPHYR_LDFLAGS) -AC_SUBST(ZEPHYR_LIBS) -if test "$zephyr" != "no" ; then - if test "$zephyr" != "yes" ; then - ZEPHYR_CFLAGS="-I${zephyr}/include" - ZEPHYR_LDFLAGS="-L${zephyr}/lib" - elif test -d /usr/athena/include/zephyr ; then - ZEPHYR_CFLAGS="-I/usr/athena/include" - elif test -d /usr/include/zephyr ; then - ZEPHYR_CFLAGS="-I/usr/include" - elif test -d /usr/local/include/zephyr ; then - ZEPHYR_CFLAGS="-I/usr/local/include" - fi - AC_DEFINE(LIBZEPHYR_EXT, 1 , [Define if external libzephyr should be used.]) - AM_CONDITIONAL(EXTERNAL_LIBZEPHYR, test "x$zephyr" != "xno") - orig_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $ZEPHYR_LDFLAGS" - AC_CHECK_LIB(zephyr, ZInitialize, - [ZEPHYR_LIBS="-lzephyr"], - [AC_MSG_ERROR([Zephyr libraries not found])], - -lzephyr) - orig_LIBS="$LIBS" - LIBS="$orig_LIBS" - LDFLAGS="$orig_LDFLAGS" -fi - AC_MSG_CHECKING(for me pot o' gold) AC_MSG_RESULT(no) AC_CHECK_FUNCS(gethostid lrand48 timegm) @@ -2597,16 +2307,10 @@ libpurple/Makefile libpurple/protocols/Makefile libpurple/protocols/bonjour/Makefile - libpurple/protocols/gg/Makefile libpurple/protocols/irc/Makefile libpurple/protocols/jabber/Makefile - libpurple/protocols/novell/Makefile libpurple/protocols/null/Makefile - libpurple/protocols/sametime/Makefile - libpurple/protocols/silc/Makefile - libpurple/protocols/silc10/Makefile libpurple/protocols/simple/Makefile - libpurple/protocols/zephyr/Makefile libpurple/tests/Makefile libpurple/purple.h libpurple/version.h @@ -2646,9 +2350,6 @@ eval eval echo SSL CA certificates directory. : $SSL_CERTIFICATES_DIR fi 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 diff -r b9cf92c8b16b -r 50facee54d1d doc/finch.1.in --- a/doc/finch.1.in Wed Jun 04 23:12:27 2025 -0500 +++ b/doc/finch.1.in Wed Jun 04 23:47:08 2025 -0500 @@ -30,9 +30,8 @@ .SH DESCRIPTION .PP \fBfinch\fR is a console-based modular messaging client based on libpurple -which is capable of connecting to XMPP, IRC, SILC, -Novell GroupWise, Lotus Sametime, Zephyr, Gadu-Gadu, and QQ all at once. It has -many common features found in other clients, as well as many unique features. +which is capable of connecting to IRC and XMPP. It has many common features +found in other clients, as well as many unique features. .SH OPTIONS The following options are provided by \fBfinch\fR using the standard GNU diff -r b9cf92c8b16b -r 50facee54d1d doc/pidgin.1.in --- a/doc/pidgin.1.in Wed Jun 04 23:12:27 2025 -0500 +++ b/doc/pidgin.1.in Wed Jun 04 23:47:08 2025 -0500 @@ -29,9 +29,8 @@ .SH DESCRIPTION .PP \fBpidgin\fR is a graphical modular messaging client based on libpurple -which is capable of connecting to XMPP, IRC, SILC, -Novell GroupWise, Lotus Sametime, Zephyr, Gadu-Gadu, and QQ all at once. It has -many common features found in other clients, as well as many unique features. +which is capable of connecting to IRC and XMPP. It has many common features +found in other clients, as well as many unique features. .PP Pidgin can be extended by plugins written in multiple programming languages and controlled through DBus or \fBpurple-remote\fR. @@ -91,7 +90,7 @@ other protocols, aliases are saved only locally. .TP .B Protocol -A messaging service. XMPP, Zephyr, etc. are protocols. Others may +A messaging service. IRC, XMPP, etc. are protocols. Others may call these "service types," "account types," "services," and so on. .SH BUDDY LIST diff -r b9cf92c8b16b -r 50facee54d1d libpurple/gaim-compat.h --- a/libpurple/gaim-compat.h Wed Jun 04 23:12:27 2025 -0500 +++ b/libpurple/gaim-compat.h Wed Jun 04 23:47:08 2025 -0500 @@ -2304,16 +2304,7 @@ #define gaim_init_ssl_plugin purple_init_ssl_plugin #define gaim_init_ssl_openssl_plugin purple_init_ssl_openssl_plugin #define gaim_init_ssl_gnutls_plugin purple_init_ssl_gnutls_plugin -#define gaim_init_gg_plugin purple_init_gg_plugin #define gaim_init_jabber_plugin purple_init_jabber_plugin -#define gaim_init_sametime_plugin purple_init_sametime_plugin -#define gaim_init_msn_plugin purple_init_msn_plugin -#define gaim_init_novell_plugin purple_init_novell_plugin -#define gaim_init_qq_plugin purple_init_qq_plugin #define gaim_init_simple_plugin purple_init_simple_plugin -#define gaim_init_yahoo_plugin purple_init_yahoo_plugin -#define gaim_init_zephyr_plugin purple_init_zephyr_plugin -#define gaim_init_aim_plugin purple_init_aim_plugin -#define gaim_init_icq_plugin purple_init_icq_plugin #endif /* _GAIM_COMPAT_H_ */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/plugins/psychic.c --- a/libpurple/plugins/psychic.c Wed Jun 04 23:12:27 2025 -0500 +++ b/libpurple/plugins/psychic.c Wed Jun 04 23:47:08 2025 -0500 @@ -20,8 +20,7 @@ #define PLUGIN_NAME N_("Psychic Mode") #define PLUGIN_SUMMARY N_("Psychic mode for incoming conversation") #define PLUGIN_DESC N_("Causes conversation windows to appear as other" \ - " users begin to message you. This works for" \ - " XMPP and Sametime") + " users begin to message you. This works for XMPP") #define PLUGIN_AUTHOR "Christopher O'Brien " diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/Makefile.am --- a/libpurple/protocols/Makefile.am Wed Jun 04 23:12:27 2025 -0500 +++ b/libpurple/protocols/Makefile.am Wed Jun 04 23:47:08 2025 -0500 @@ -1,5 +1,5 @@ EXTRA_DIST = Makefile.mingw -DIST_SUBDIRS = bonjour gg irc jabber novell null sametime silc silc10 simple zephyr +DIST_SUBDIRS = bonjour irc jabber null simple SUBDIRS = $(DYNAMIC_PRPLS) $(STATIC_PRPLS) diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/Makefile.mingw --- a/libpurple/protocols/Makefile.mingw Wed Jun 04 23:12:27 2025 -0500 +++ b/libpurple/protocols/Makefile.mingw Wed Jun 04 23:47:08 2025 -0500 @@ -8,7 +8,7 @@ PIDGIN_TREE_TOP := ../.. include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak -SUBDIRS = gg irc jabber novell null sametime silc simple bonjour +SUBDIRS = irc jabber null simple bonjour .PHONY: all install clean diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/Makefile.am --- a/libpurple/protocols/gg/Makefile.am Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,141 +0,0 @@ -EXTRA_DIST = \ - Makefile.mingw \ - lib/COPYING \ - lib/common.c \ - lib/config.h \ - lib/dcc7.c \ - lib/dcc.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 \ - lib/protobuf-c.c \ - lib/protobuf-c.h \ - lib/protobuf.h \ - lib/protocol.h \ - lib/pubdir50.c \ - lib/pubdir.c \ - lib/resolver.c \ - lib/resolver.h \ - lib/session.h \ - lib/sha1.c \ - lib/strman.h \ - lib/tvbuff.c \ - lib/tvbuff.h \ - lib/tvbuilder.c \ - lib/tvbuilder.h - -pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) - -if ! HAVE_LIBGADU -INTGG_SOURCES = \ - lib/common.c \ - lib/config.h \ - lib/dcc7.c \ - lib/dcc.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 \ - lib/protobuf-c.c \ - lib/protobuf-c.h \ - lib/protobuf.h \ - lib/protocol.h \ - lib/pubdir50.c \ - lib/pubdir.c \ - lib/resolver.c \ - lib/resolver.h \ - lib/session.h \ - lib/sha1.c \ - lib/strman.h \ - 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 - -GGSOURCES = \ - $(INTGG_SOURCES) \ - gg-utils.h \ - gg-utils.c \ - confer.h \ - confer.c \ - search.h \ - search.c \ - buddylist.h \ - buddylist.c \ - gg.h \ - gg.c - -AM_CFLAGS = $(st) - -libgg_la_LDFLAGS = -module -avoid-version - -if STATIC_GG - -st = -DPURPLE_STATIC_PRPL -noinst_LTLIBRARIES = libgg.la -libgg_la_SOURCES = $(GGSOURCES) -libgg_la_CFLAGS = $(AM_CFLAGS) -libgg_la_LIBADD = $(LIBGADU_LIBS) $(INTGG_LIBS) - -else - -st = -pkg_LTLIBRARIES = libgg.la -libgg_la_SOURCES = $(GGSOURCES) -libgg_la_LIBADD = $(GLIB_LIBS) $(LIBGADU_LIBS) $(INTGG_LIBS) - -endif - -AM_CPPFLAGS = \ - -I$(top_srcdir)/libpurple \ - -I$(top_builddir)/libpurple \ - $(LIBGADU_CFLAGS) \ - $(INTGG_CFLAGS) \ - $(GLIB_CFLAGS) \ - $(DEBUG_CFLAGS) diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/Makefile.mingw --- a/libpurple/protocols/gg/Makefile.mingw Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,109 +0,0 @@ -# -# Makefile.mingw -# -# Description: Makefile for win32 (mingw) version of libgg -# - -PIDGIN_TREE_TOP := ../../.. -include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak - -TARGET = libgg -CFLAGS += -DGG_IGNORE_DEPRECATED -TYPE = PLUGIN - -# Static or Plugin... -ifeq ($(TYPE),STATIC) - DEFINES += -DSTATIC - DLL_INSTALL_DIR = $(PURPLE_INSTALL_DIR) -else -ifeq ($(TYPE),PLUGIN) - DLL_INSTALL_DIR = $(PURPLE_INSTALL_PLUGINS_DIR) -endif -endif - -## -## INCLUDE PATHS -## -INCLUDE_PATHS += \ - -I$(PIDGIN_TREE_TOP) \ - -I$(PURPLE_TOP) \ - -I. \ - -I./lib \ - -I$(GTK_TOP)/include \ - -I$(GTK_TOP)/include/glib-2.0 \ - -I$(GTK_TOP)/lib/glib-2.0/include \ - -I$(PURPLE_TOP)/win32 - -LIB_PATHS += -L$(GTK_TOP)/lib \ - -L$(PURPLE_TOP) \ - -## -## SOURCES, OBJECTS -## -C_SRC = \ - lib/common.c \ - lib/dcc7.c \ - lib/dcc.c \ - lib/debug.c \ - lib/deflate.c \ - lib/encoding.c \ - lib/endian.c \ - lib/events.c \ - lib/handlers.c \ - lib/http.c \ - lib/libgadu.c \ - lib/message.c \ - lib/network.c \ - lib/obsolete.c \ - lib/packets.pb-c.c \ - lib/protobuf.c \ - lib/protobuf-c.c \ - lib/pubdir50.c \ - lib/pubdir.c \ - lib/resolver.c \ - lib/sha1.c \ - lib/tvbuff.c \ - lib/tvbuilder.c \ - buddylist.c \ - confer.c \ - gg.c \ - search.c \ - gg-utils.c - -OBJECTS = $(C_SRC:%.c=%.o) - -## -## LIBRARIES -## -LIBS = \ - -lglib-2.0 \ - -lintl \ - -lpurple \ - -lws2_32 \ - -lz - -include $(PIDGIN_COMMON_RULES) - -## -## TARGET DEFINITIONS -## -.PHONY: all install clean - -all: $(TARGET).dll - -install: all $(DLL_INSTALL_DIR) - cp $(TARGET).dll $(DLL_INSTALL_DIR) - -$(OBJECTS): $(PURPLE_CONFIG_H) - -$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS) - $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll - -## -## CLEAN RULES -## -clean: - rm -f $(OBJECTS) - rm -f $(TARGET).dll - -include $(PIDGIN_COMMON_TARGETS) diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/buddylist.c --- a/libpurple/protocols/gg/buddylist.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,188 +0,0 @@ -/** - * @file buddylist.c - * - * purple - * - * Copyright (C) 2005 Bartosz Oler - * - * 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 - -#include "gg.h" -#include "gg-utils.h" -#include "buddylist.h" - -#define F_FIRSTNAME 0 -#define F_LASTNAME 1 -/* #define F_ 2 */ -#define F_NICKNAME 3 -#define F_PHONE 4 -#define F_GROUP 5 -#define F_UIN 6 - -/* void ggp_buddylist_send(PurpleConnection *gc) {{{ */ -void ggp_buddylist_send(PurpleConnection *gc) -{ - GGPInfo *info = gc->proto_data; - PurpleAccount *account = purple_connection_get_account(gc); - GSList *buddies; - uin_t *userlist; - gchar *types; - int i = 0, ret = 0; - int size; - - buddies = purple_find_buddies(account, NULL); - - size = g_slist_length(buddies); - userlist = g_new(uin_t, size); - types = g_new(gchar, size); - - for (buddies = purple_find_buddies(account, NULL); buddies; - buddies = g_slist_delete_link(buddies, buddies), ++i) - { - PurpleBuddy *buddy = buddies->data; - const gchar *name = purple_buddy_get_name(buddy); - - userlist[i] = ggp_str_to_uin(name); - types[i] = GG_USER_NORMAL; - purple_debug_info("gg", "ggp_buddylist_send: adding %d\n", - userlist[i]); - } - - ret = gg_notify_ex(info->session, userlist, types, size); - purple_debug_info("gg", "send: ret=%d; size=%d\n", ret, size); - - if (userlist) { - g_free(userlist); - g_free(types); - } -} -/* }}} */ - -/* void ggp_buddylist_load(PurpleConnection *gc, char *buddylist) {{{ */ -void ggp_buddylist_load(PurpleConnection *gc, char *buddylist) -{ - PurpleBuddy *buddy; - PurpleGroup *group; - gchar **users_tbl; - int i; - char *utf8buddylist = charset_convert(buddylist, "CP1250", "UTF-8"); - - /* Don't limit the number of records in a buddylist. */ - users_tbl = g_strsplit(utf8buddylist, "\r\n", -1); - - for (i = 0; users_tbl[i] != NULL; i++) { - gchar **data_tbl; - gchar *name, *show, *g; - - if (strlen(users_tbl[i]) == 0) - continue; - - data_tbl = g_strsplit(users_tbl[i], ";", 8); - if (ggp_array_size(data_tbl) < 8) { - purple_debug_warning("gg", - "Something is wrong on line %d of the buddylist. Skipping.\n", - i + 1); - continue; - } - - show = data_tbl[F_NICKNAME]; - name = data_tbl[F_UIN]; - if ('\0' == *name || !atol(name)) { - purple_debug_warning("gg", - "Identifier on line %d of the buddylist is not a number. Skipping.\n", - i + 1); - continue; - } - - if ('\0' == *show) { - show = name; - } - - purple_debug_info("gg", "got buddy: name=%s; show=%s\n", name, show); - - if (purple_find_buddy(purple_connection_get_account(gc), name)) { - g_strfreev(data_tbl); - continue; - } - - g = g_strdup("Gadu-Gadu"); - - if ('\0' != *(data_tbl[F_GROUP])) { - /* XXX: Probably buddy should be added to all the groups. */ - /* Hard limit to at most 50 groups */ - gchar **group_tbl = g_strsplit(data_tbl[F_GROUP], ",", 50); - if (ggp_array_size(group_tbl) > 0) { - g_free(g); - g = g_strdup(group_tbl[0]); - } - g_strfreev(group_tbl); - } - - buddy = purple_buddy_new(purple_connection_get_account(gc), name, - strlen(show) ? show : NULL); - - if (!(group = purple_find_group(g))) { - group = purple_group_new(g); - purple_blist_add_group(group, NULL); - } - - purple_blist_add_buddy(buddy, NULL, group, NULL); - g_free(g); - - g_strfreev(data_tbl); - } - g_strfreev(users_tbl); - g_free(utf8buddylist); - - ggp_buddylist_send(gc); -} -/* }}} */ - -/* char *ggp_buddylist_dump(PurpleAccount *account) {{{ */ -char *ggp_buddylist_dump(PurpleAccount *account) -{ - GSList *buddies; - GString *buddylist = g_string_sized_new(1024); - char *ptr; - - for (buddies = purple_find_buddies(account, NULL); buddies; - buddies = g_slist_delete_link(buddies, buddies)) { - PurpleBuddy *buddy = buddies->data; - PurpleGroup *group = purple_buddy_get_group(buddy); - const char *bname = purple_buddy_get_name(buddy); - const char *gname = purple_group_get_name(group); - const char *alias = purple_buddy_get_alias(buddy); - - if (alias == NULL) - alias = bname; - - g_string_append_printf(buddylist, - "%s;%s;%s;%s;%s;%s;%s;%s%s\r\n", - alias, alias, alias, alias, - "", gname, bname, "", ""); - } - - ptr = charset_convert(buddylist->str, "UTF-8", "CP1250"); - g_string_free(buddylist, TRUE); - return ptr; -} -/* }}} */ - - -/* vim: set ts=8 sts=0 sw=8 noet: */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/buddylist.h --- a/libpurple/protocols/gg/buddylist.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ -/** - * @file buddylist.h - * - * purple - * - * Copyright (C) 2005 Bartosz Oler - * - * 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_GG_BUDDYLIST_H -#define _PURPLE_GG_BUDDYLIST_H - -#include "connection.h" -#include "account.h" - -void -ggp_buddylist_send(PurpleConnection *gc); - -/** - * Load buddylist from server into the roster. - * - * @param gc PurpleConnection - * @param buddylist Pointer to the buddylist that will be loaded. - */ -/* void ggp_buddylist_load(PurpleConnection *gc, char *buddylist) {{{ */ -void -ggp_buddylist_load(PurpleConnection *gc, char *buddylist); - -/** - * Get all the buddies in the current account. - * - * @param account Current account. - * - * @return List of buddies. - */ -char * -ggp_buddylist_dump(PurpleAccount *account); - - -#endif /* _PURPLE_GG_BUDDYLIST_H */ - - -/* vim: set ts=8 sts=0 sw=8 noet: */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/confer.c --- a/libpurple/protocols/gg/confer.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,170 +0,0 @@ -/** - * @file confer.c - * - * purple - * - * Copyright (C) 2005 Bartosz Oler - * - * 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 -#include "gg.h" -#include "gg-utils.h" -#include "confer.h" - -/* PurpleConversation *ggp_confer_find_by_name(PurpleConnection *gc, const gchar *name) {{{ */ -PurpleConversation *ggp_confer_find_by_name(PurpleConnection *gc, const gchar *name) -{ - g_return_val_if_fail(gc != NULL, NULL); - g_return_val_if_fail(name != NULL, NULL); - - return purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, name, - purple_connection_get_account(gc)); -} -/* }}} */ - -/* void ggp_confer_participants_add_uin(PurpleConnection *gc, const gchar *chat_name, const uin_t uin) {{{ */ -void ggp_confer_participants_add_uin(PurpleConnection *gc, const gchar *chat_name, - const uin_t uin) -{ - PurpleConversation *conv; - GGPInfo *info = gc->proto_data; - GGPChat *chat; - GList *l; - gchar *str_uin; - - for (l = info->chats; l != NULL; l = l->next) { - chat = l->data; - - if (g_utf8_collate(chat->name, chat_name) != 0) - continue; - - if (g_list_find(chat->participants, GINT_TO_POINTER(uin)) == NULL) { - chat->participants = g_list_append( - chat->participants, GINT_TO_POINTER(uin)); - - str_uin = g_strdup_printf("%lu", (unsigned long int)uin); - conv = ggp_confer_find_by_name(gc, chat_name); - purple_conv_chat_add_user(PURPLE_CONV_CHAT(conv), str_uin, NULL, - PURPLE_CBFLAGS_NONE, TRUE); - - g_free(str_uin); - } - break; - } -} -/* }}} */ - -/* void ggp_confer_participants_add(PurpleConnection *gc, const gchar *chat_name, const uin_t *recipients, int count) {{{ */ -void ggp_confer_participants_add(PurpleConnection *gc, const gchar *chat_name, - const uin_t *recipients, int count) -{ - GGPInfo *info = gc->proto_data; - GList *l; - gchar *str_uin; - - for (l = info->chats; l != NULL; l = l->next) { - GGPChat *chat = l->data; - int i; - - if (g_utf8_collate(chat->name, chat_name) != 0) - continue; - - for (i = 0; i < count; i++) { - PurpleConversation *conv; - - if (g_list_find(chat->participants, - GINT_TO_POINTER(recipients[i])) != NULL) { - continue; - } - - chat->participants = g_list_append(chat->participants, - GINT_TO_POINTER(recipients[i])); - - str_uin = g_strdup_printf("%lu", (unsigned long int)recipients[i]); - conv = ggp_confer_find_by_name(gc, chat_name); - purple_conv_chat_add_user(PURPLE_CONV_CHAT(conv), - str_uin, NULL, - PURPLE_CBFLAGS_NONE, TRUE); - g_free(str_uin); - } - break; - } -} -/* }}} */ - -/* const char *ggp_confer_find_by_participants(PurpleConnection *gc, const uin_t *recipients, int count) {{{ */ -const char *ggp_confer_find_by_participants(PurpleConnection *gc, - const uin_t *recipients, int count) -{ - GGPInfo *info = gc->proto_data; - GGPChat *chat = NULL; - GList *l; - int matches; - - g_return_val_if_fail(info->chats != NULL, NULL); - - for (l = info->chats; l != NULL; l = l->next) { - GList *m; - - chat = l->data; - matches = 0; - - for (m = chat->participants; m != NULL; m = m->next) { - uin_t uin = GPOINTER_TO_INT(m->data); - int i; - - for (i = 0; i < count; i++) - if (uin == recipients[i]) - matches++; - } - - if (matches == count) - break; - - chat = NULL; - } - - if (chat == NULL) - return NULL; - else - return chat->name; -} -/* }}} */ - -/* const char *ggp_confer_add_new(PurpleConnection *gc, const char *name) {{{ */ -const char *ggp_confer_add_new(PurpleConnection *gc, const char *name) -{ - GGPInfo *info = gc->proto_data; - GGPChat *chat; - - chat = g_new0(GGPChat, 1); - - if (name == NULL) - chat->name = g_strdup_printf("conf#%d", info->chats_count++); - else - chat->name = g_strdup(name); - - chat->participants = NULL; - - info->chats = g_list_append(info->chats, chat); - - return chat->name; -} -/* }}} */ - -/* vim: set ts=8 sts=0 sw=8 noet: */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/confer.h --- a/libpurple/protocols/gg/confer.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -/** - * @file confer.h - * - * purple - * - * Copyright (C) 2005 Bartosz Oler - * - * 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_GG_CONFER_H -#define _PURPLE_GG_CONFER_H - -#include "gg.h" - -/** - * Finds a CHAT conversation for the current account with the specified name. - * - * @param gc PurpleConnection instance. - * @param name Name of the conversation. - * - * @return PurpleConversation or NULL if not found. - */ -PurpleConversation * -ggp_confer_find_by_name(PurpleConnection *gc, const gchar *name); - -/** - * Adds the specified UIN to the specified conversation. - * - * @param gc PurpleConnection. - * @param chat_name Name of the conversation. - */ -void -ggp_confer_participants_add_uin(PurpleConnection *gc, const gchar *chat_name, - const uin_t uin); - -/** - * Add the specified UINs to the specified conversation. - * - * @param gc PurpleConnection. - * @param chat_name Name of the conversation. - * @param recipients List of the UINs. - * @param count Number of the UINs. - */ -void -ggp_confer_participants_add(PurpleConnection *gc, const gchar *chat_name, - const uin_t *recipients, int count); - -/** - * Finds a conversation in which all the specified recipients participate. - * - * TODO: This function should be rewritten to better handle situations when - * somebody adds more people to the converation. - * - * @param gc PurpleConnection. - * @param recipients List of the people in the conversation. - * @param count Number of people. - * - * @return Name of the conversation. - */ -const char* -ggp_confer_find_by_participants(PurpleConnection *gc, const uin_t *recipients, - int count); - -/** - * Adds a new conversation to the internal list of conversations. - * If name is NULL then it will be automagically generated. - * - * @param gc PurpleConnection. - * @param name Name of the conversation. - * - * @return Name of the conversation. - */ -const char* -ggp_confer_add_new(PurpleConnection *gc, const char *name); - - -#endif /* _PURPLE_GG_CONFER_H */ - -/* vim: set ts=8 sts=0 sw=8 noet: */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/gg-utils.c --- a/libpurple/protocols/gg/gg-utils.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,147 +0,0 @@ -/** - * @file gg-utils.c - * - * purple - * - * Copyright (C) 2005 Bartosz Oler - * - * 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 "gg-utils.h" - - -/* uin_t ggp_str_to_uin(const char *str) {{{ */ -uin_t ggp_str_to_uin(const char *str) -{ - char *tmp; - long num; - - if (!str) - return 0; - - errno = 0; - num = strtol(str, &tmp, 10); - - if (*str == '\0' || *tmp != '\0') - return 0; - - if ((errno == ERANGE || (num == LONG_MAX || num == LONG_MIN)) -#if (LONG_MAX > UINT_MAX) - || num > (long)UINT_MAX -#endif - || num < 0) - return 0; - - return (uin_t) num; -} -/* }}} */ - -/* unsigned int ggp_array_size(char **array) {{{ */ -unsigned int ggp_array_size(char **array) -{ - unsigned int i; - - for (i = 0; array[i] != NULL && i < UINT_MAX; i++) - {} - - return i; -} -/* }}} */ - -/* char *charset_convert(const gchar *locstr, const char *encsrc, const char *encdst) {{{ */ -char *charset_convert(const gchar *locstr, const char *encsrc, const char *encdst) -{ - gchar *msg; - GError *err = NULL; - - if (locstr == NULL) - return NULL; - - msg = g_convert_with_fallback(locstr, strlen(locstr), encdst, encsrc, - "?", NULL, NULL, &err); - if (err != NULL) { - purple_debug_error("gg", "Error converting from %s to %s: %s\n", - encsrc, encdst, err->message); - g_error_free(err); - } - - /* Just in case? */ - if (msg == NULL) - msg = g_strdup(locstr); - - return msg; -} -/* }}} */ - -/* ggp_get_uin(PurpleAccount *account) {{{ */ -uin_t ggp_get_uin(PurpleAccount *account) -{ - return ggp_str_to_uin(purple_account_get_username(account)); -} -/* }}} */ - -/* char *ggp_buddy_get_name(PurpleConnection *gc, const uin_t uin) {{{ */ -char *ggp_buddy_get_name(PurpleConnection *gc, const uin_t uin) -{ - PurpleBuddy *buddy; - gchar *str_uin; - - str_uin = g_strdup_printf("%lu", (unsigned long int)uin); - - buddy = purple_find_buddy(purple_connection_get_account(gc), str_uin); - if (buddy != NULL) { - g_free(str_uin); - return g_strdup(purple_buddy_get_alias(buddy)); - } else { - return str_uin; - } -} -/* }}} */ - -void ggp_status_fake_to_self(PurpleAccount *account) -{ - PurplePresence *presence; - PurpleStatus *status; - const char *status_id; - const char *msg; - - if (! purple_find_buddy(account, purple_account_get_username(account))) - return; - - presence = purple_account_get_presence(account); - status = purple_presence_get_active_status(presence); - msg = purple_status_get_attr_string(status, "message"); - if (msg && !*msg) - msg = NULL; - - status_id = purple_status_get_id(status); - if (purple_strequal(status_id, "invisible")) { - status_id = "offline"; - } - - if (msg) { - if (strlen(msg) > GG_STATUS_DESCR_MAXSIZE) { - msg = purple_markup_slice(msg, 0, GG_STATUS_DESCR_MAXSIZE); - } - } - purple_prpl_got_user_status(account, purple_account_get_username(account), - status_id, - msg ? "message" : NULL, msg, NULL); -} - - -/* vim: set ts=8 sts=0 sw=8 noet: */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/gg-utils.h --- a/libpurple/protocols/gg/gg-utils.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -/** - * @file gg-utils.h - * - * purple - * - * Copyright (C) 2005 Bartosz Oler - * - * 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_GG_UTILS_H -#define _PURPLE_GG_UTILS_H - -#include "internal.h" - -#include "plugin.h" -#include "version.h" -#include "notify.h" -#include "status.h" -#include "blist.h" -#include "accountopt.h" -#include "debug.h" -#include "util.h" -#include "request.h" - -#include "gg.h" - - -/** - * Convert a base 10 string to a UIN. - * - * @param str The string to convert - * - * @return UIN or 0 if an error occurred. - */ -uin_t -ggp_str_to_uin(const char *str); - -/** - * Calculate size of a NULL-terminated array. - * - * @param array The array. - * - * @return Size of the array. - */ -unsigned int -ggp_array_size(char **array); - -/** - * Convert enconding of a given string. - * - * @param locstr Input string. - * @param encsrc Current encoding of the string. - * @param encdst Target encoding of the string. - * - * @return Converted string (it must be g_free()ed when not used. Or NULL if - * locstr is NULL. - */ -char * -charset_convert(const gchar *locstr, const char *encsrc, const char *encdst); - -/** - * Get UIN of a given account. - * - * @param account Current account. - * - * @return UIN of an account. - */ -uin_t -ggp_get_uin(PurpleAccount *account); - -/** - * Returns the best name of a buddy from the buddylist. - * - * @param gc PurpleConnection instance. - * @param uin UIN of the buddy. - * - * @return Name of the buddy, or UIN converted to string. - */ -char * -ggp_buddy_get_name(PurpleConnection *gc, const uin_t uin); - -/** - * Manages the display of account's status in the buddylist. - * - * @param account Current account. - */ -void -ggp_status_fake_to_self(PurpleAccount *account); - - -#endif /* _PURPLE_GG_UTILS_H */ - -/* vim: set ts=8 sts=0 sw=8 noet: */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/gg.c --- a/libpurple/protocols/gg/gg.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2452 +0,0 @@ -/** - * @file gg.c Gadu-Gadu protocol plugin - * - * purple - * - * Copyright (C) 2005 Bartosz Oler - * - * Some parts of the code are adapted or taken from the previous implementation - * of this plugin written by Arkadiusz Miskiewicz - * Some parts Copyright (C) 2009 Krzysztof Klinikowski - * - * Thanks to Google's Summer of Code Program. - * - * 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 "internal.h" - -#include "plugin.h" -#include "version.h" -#include "notify.h" -#include "status.h" -#include "blist.h" -#include "accountopt.h" -#include "debug.h" -#include "glibcompat.h" -#include "util.h" -#include "request.h" -#include "xmlnode.h" - -#include - -#include "gg.h" -#include "confer.h" -#include "search.h" -#include "buddylist.h" -#include "gg-utils.h" - -#define DISABLE_AVATARS 1 - -static PurplePlugin *my_protocol = NULL; - -/* Prototypes */ -static void ggp_set_status(PurpleAccount *account, PurpleStatus *status); -static int ggp_to_gg_status(PurpleStatus *status, char **msg); - -/* ---------------------------------------------------------------------- */ -/* ----- EXTERNAL CALLBACKS --------------------------------------------- */ -/* ---------------------------------------------------------------------- */ - - -/* ----- HELPERS -------------------------------------------------------- */ - -static PurpleInputCondition -ggp_tcpsocket_inputcond_gg_to_purple(enum gg_check_t check) -{ - PurpleInputCondition cond = 0; - - if (check & GG_CHECK_READ) - cond |= PURPLE_INPUT_READ; - if (check & GG_CHECK_WRITE) - cond |= PURPLE_INPUT_WRITE; - - return cond; -} - -/** - * Set up libgadu's proxy. - * - * @param account Account for which to set up the proxy. - * - * @return Zero if proxy setup is valid, otherwise -1. - */ -static int ggp_setup_proxy(PurpleAccount *account) -{ - PurpleProxyInfo *gpi; - - gpi = purple_proxy_get_setup(account); - - if ((purple_proxy_info_get_type(gpi) != PURPLE_PROXY_NONE) && - (purple_proxy_info_get_host(gpi) == NULL || - purple_proxy_info_get_port(gpi) <= 0)) { - - gg_proxy_enabled = 0; - purple_notify_error(NULL, NULL, _("Invalid proxy settings"), - _("Either the host name or port number specified for your given proxy type is invalid.")); - return -1; - } else if (purple_proxy_info_get_type(gpi) != PURPLE_PROXY_NONE) { - gg_proxy_enabled = 1; - gg_proxy_host = g_strdup(purple_proxy_info_get_host(gpi)); - gg_proxy_port = purple_proxy_info_get_port(gpi); - gg_proxy_username = g_strdup(purple_proxy_info_get_username(gpi)); - gg_proxy_password = g_strdup(purple_proxy_info_get_password(gpi)); - } else { - gg_proxy_enabled = 0; - } - - return 0; -} - -/* }}} */ - -/* ---------------------------------------------------------------------- */ - -static void ggp_callback_buddylist_save_ok(PurpleConnection *gc, const char *filename) -{ - PurpleAccount *account = purple_connection_get_account(gc); - - char *buddylist = ggp_buddylist_dump(account); - - purple_debug_info("gg", "Saving...\n"); - purple_debug_info("gg", "file = %s\n", filename); - - if (buddylist == NULL) { - purple_notify_info(account, _("Save Buddylist..."), - _("Your buddylist is empty, nothing was written to the file."), - NULL); - return; - } - - if(purple_util_write_data_to_file_absolute(filename, buddylist, -1)) { - purple_notify_info(account, _("Save Buddylist..."), - _("Buddylist saved successfully!"), NULL); - } else { - gchar *primary = g_strdup_printf( - _("Couldn't write buddy list for %s to %s"), - purple_account_get_username(account), filename); - purple_notify_error(account, _("Save Buddylist..."), - primary, NULL); - g_free(primary); - } - - g_free(buddylist); -} - -static void ggp_callback_buddylist_load_ok(PurpleConnection *gc, gchar *file) -{ - PurpleAccount *account = purple_connection_get_account(gc); - GError *error = NULL; - char *buddylist = NULL; - gsize length; - - purple_debug_info("gg", "file_name = %s\n", file); - - if (!g_file_get_contents(file, &buddylist, &length, &error)) { - purple_notify_error(account, - _("Couldn't load buddylist"), - _("Couldn't load buddylist"), - error->message); - - purple_debug_error("gg", - "Couldn't load buddylist. file = %s; error = %s\n", - file, error->message); - - g_error_free(error); - - return; - } - - ggp_buddylist_load(gc, buddylist); - g_free(buddylist); - - purple_notify_info(account, - _("Load Buddylist..."), - _("Buddylist loaded successfully!"), NULL); -} -/* }}} */ - -/* - */ -/* static void ggp_action_buddylist_save(PurplePluginAction *action) {{{ */ -static void ggp_action_buddylist_save(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *)action->context; - - purple_request_file(action, _("Save buddylist..."), NULL, TRUE, - G_CALLBACK(ggp_callback_buddylist_save_ok), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); -} - -static void ggp_action_buddylist_load(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *)action->context; - - purple_request_file(action, _("Load buddylist from file..."), NULL, - FALSE, - G_CALLBACK(ggp_callback_buddylist_load_ok), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); -} - -/* ----- PUBLIC DIRECTORY SEARCH ---------------------------------------- */ - -static void ggp_callback_show_next(PurpleConnection *gc, GList *row, gpointer user_data) -{ - GGPInfo *info = gc->proto_data; - GGPSearchForm *form = user_data; - guint32 seq; - - form->page_number++; - - ggp_search_remove(info->searches, form->seq); - purple_debug_info("gg", "ggp_callback_show_next(): Removed seq %u\n", - form->seq); - - seq = ggp_search_start(gc, form); - ggp_search_add(info->searches, seq, form); - purple_debug_info("gg", "ggp_callback_show_next(): Added seq %u\n", - seq); -} - -static void ggp_callback_add_buddy(PurpleConnection *gc, GList *row, gpointer user_data) -{ - purple_blist_request_add_buddy(purple_connection_get_account(gc), - g_list_nth_data(row, 0), NULL, NULL); -} - -static void ggp_callback_im(PurpleConnection *gc, GList *row, gpointer user_data) -{ - PurpleAccount *account; - PurpleConversation *conv; - char *name; - - account = purple_connection_get_account(gc); - - name = g_list_nth_data(row, 0); - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, name); - purple_conversation_present(conv); -} - -static void ggp_callback_find_buddies(PurpleConnection *gc, PurpleRequestFields *fields) -{ - GGPInfo *info = gc->proto_data; - GGPSearchForm *form; - guint32 seq; - - form = ggp_search_form_new(GGP_SEARCH_TYPE_FULL); - - form->user_data = info; - form->lastname = g_strdup( - purple_request_fields_get_string(fields, "lastname")); - form->firstname = g_strdup( - purple_request_fields_get_string(fields, "firstname")); - form->nickname = g_strdup( - purple_request_fields_get_string(fields, "nickname")); - form->city = g_strdup( - purple_request_fields_get_string(fields, "city")); - form->birthyear = g_strdup( - purple_request_fields_get_string(fields, "year")); - - switch (purple_request_fields_get_choice(fields, "gender")) { - case 1: - form->gender = g_strdup(GG_PUBDIR50_GENDER_MALE); - break; - case 2: - form->gender = g_strdup(GG_PUBDIR50_GENDER_FEMALE); - break; - default: - form->gender = NULL; - break; - } - - form->active = purple_request_fields_get_bool(fields, "active") - ? g_strdup(GG_PUBDIR50_ACTIVE_TRUE) : NULL; - - seq = ggp_search_start(gc, form); - ggp_search_add(info->searches, seq, form); - purple_debug_info("gg", "ggp_callback_find_buddies(): Added seq %u\n", - seq); -} - -static void ggp_find_buddies(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *)action->context; - - PurpleRequestFields *fields; - PurpleRequestFieldGroup *group; - PurpleRequestField *field; - - fields = purple_request_fields_new(); - group = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, group); - - field = purple_request_field_string_new("lastname", - _("Last name"), NULL, FALSE); - purple_request_field_string_set_masked(field, FALSE); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_string_new("firstname", - _("First name"), NULL, FALSE); - purple_request_field_string_set_masked(field, FALSE); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_string_new("nickname", - _("Nickname"), NULL, FALSE); - purple_request_field_string_set_masked(field, FALSE); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_string_new("city", - _("City"), NULL, FALSE); - purple_request_field_string_set_masked(field, FALSE); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_string_new("year", - _("Year of birth"), NULL, FALSE); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_choice_new("gender", _("Gender"), 0); - purple_request_field_choice_add(field, _("Male or female")); - purple_request_field_choice_add(field, _("Male")); - purple_request_field_choice_add(field, _("Female")); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_bool_new("active", - _("Only online"), FALSE); - purple_request_field_group_add_field(group, field); - - purple_request_fields(gc, - _("Find buddies"), - _("Find buddies"), - _("Please, enter your search criteria below"), - fields, - _("OK"), G_CALLBACK(ggp_callback_find_buddies), - _("Cancel"), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); -} - -/* ----- CHANGE STATUS BROADCASTING ------------------------------------------------ */ - -static void ggp_action_change_status_broadcasting_ok(PurpleConnection *gc, PurpleRequestFields *fields) -{ - GGPInfo *info = gc->proto_data; - int selected_field; - PurpleAccount *account = purple_connection_get_account(gc); - PurpleStatus *status; - - selected_field = purple_request_fields_get_choice(fields, "status_broadcasting"); - - if (selected_field == 0) - info->status_broadcasting = TRUE; - else - info->status_broadcasting = FALSE; - - status = purple_account_get_active_status(account); - - ggp_set_status(account, status); -} - -static void ggp_action_change_status_broadcasting(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *)action->context; - GGPInfo *info = gc->proto_data; - - PurpleRequestFields *fields; - PurpleRequestFieldGroup *group; - PurpleRequestField *field; - - fields = purple_request_fields_new(); - group = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, group); - - field = purple_request_field_choice_new("status_broadcasting", _("Show status to:"), 0); - purple_request_field_choice_add(field, _("All people")); - purple_request_field_choice_add(field, _("Only buddies")); - purple_request_field_group_add_field(group, field); - - if (info->status_broadcasting) - purple_request_field_choice_set_default_value(field, 0); - else - purple_request_field_choice_set_default_value(field, 1); - - purple_request_fields(gc, - _("Change status broadcasting"), - _("Change status broadcasting"), - _("Please, select who can see your status"), - fields, - _("OK"), G_CALLBACK(ggp_action_change_status_broadcasting_ok), - _("Cancel"), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); -} - -/* ----- CONFERENCES ---------------------------------------------------- */ - -static void ggp_callback_add_to_chat_ok(PurpleBuddy *buddy, PurpleRequestFields *fields) -{ - PurpleConnection *conn; - PurpleRequestField *field; - GList *sel; - - conn = purple_account_get_connection(purple_buddy_get_account(buddy)); - - g_return_if_fail(conn != NULL); - - field = purple_request_fields_get_field(fields, "name"); - sel = purple_request_field_list_get_selected(field); - - if (sel == NULL) { - purple_debug_error("gg", "No chat selected\n"); - return; - } - - ggp_confer_participants_add_uin(conn, sel->data, - ggp_str_to_uin(purple_buddy_get_name(buddy))); -} - -static void ggp_bmenu_add_to_chat(PurpleBlistNode *node, gpointer ignored) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - GGPInfo *info; - - PurpleRequestFields *fields; - PurpleRequestFieldGroup *group; - PurpleRequestField *field; - - GList *l; - gchar *msg; - - buddy = (PurpleBuddy *)node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - info = gc->proto_data; - - fields = purple_request_fields_new(); - group = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, group); - - field = purple_request_field_list_new("name", "Chat name"); - for (l = info->chats; l != NULL; l = l->next) { - GGPChat *chat = l->data; - purple_request_field_list_add(field, chat->name, chat->name); - } - purple_request_field_group_add_field(group, field); - - msg = g_strdup_printf(_("Select a chat for buddy: %s"), - purple_buddy_get_alias(buddy)); - purple_request_fields(gc, - _("Add to chat..."), - _("Add to chat..."), - msg, - fields, - _("Add"), G_CALLBACK(ggp_callback_add_to_chat_ok), - _("Cancel"), NULL, - purple_connection_get_account(gc), NULL, NULL, - buddy); - g_free(msg); -} - -/* ----- BLOCK BUDDIES -------------------------------------------------- */ - -static void ggp_add_deny(PurpleConnection *gc, const char *who) -{ - GGPInfo *info = gc->proto_data; - uin_t uin = ggp_str_to_uin(who); - - purple_debug_info("gg", "ggp_add_deny: %u\n", uin); - - gg_remove_notify_ex(info->session, uin, GG_USER_NORMAL); - gg_add_notify_ex(info->session, uin, GG_USER_BLOCKED); -} - -static void ggp_rem_deny(PurpleConnection *gc, const char *who) -{ - GGPInfo *info = gc->proto_data; - uin_t uin = ggp_str_to_uin(who); - - purple_debug_info("gg", "ggp_rem_deny: %u\n", uin); - - gg_remove_notify_ex(info->session, uin, GG_USER_BLOCKED); - gg_add_notify_ex(info->session, uin, GG_USER_NORMAL); -} - -/* ---------------------------------------------------------------------- */ -/* ----- INTERNAL CALLBACKS --------------------------------------------- */ -/* ---------------------------------------------------------------------- */ - -#if !DISABLE_AVATARS - -struct gg_fetch_avatar_data -{ - PurpleConnection *gc; - gchar *uin; - gchar *avatar_url; -}; - - -static void gg_fetch_avatar_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, - const gchar *data, size_t len, const gchar *error_message) { - struct gg_fetch_avatar_data *d = user_data; - PurpleAccount *account; - PurpleBuddy *buddy; - gpointer buddy_icon_data; - - purple_debug_info("gg", "gg_fetch_avatar_cb: got avatar image for %s\n", - d->uin); - - /* FIXME: This shouldn't be necessary */ - if (!PURPLE_CONNECTION_IS_VALID(d->gc)) { - g_free(d->uin); - g_free(d->avatar_url); - g_free(d); - g_return_if_reached(); - } - - account = purple_connection_get_account(d->gc); - buddy = purple_find_buddy(account, d->uin); - - if (buddy == NULL) - goto out; - - buddy_icon_data = g_memdup2(data, len); - - purple_buddy_icons_set_for_user(account, purple_buddy_get_name(buddy), - buddy_icon_data, len, d->avatar_url); - purple_debug_info("gg", "gg_fetch_avatar_cb: UIN %s should have avatar " - "now\n", d->uin); - -out: - g_free(d->uin); - g_free(d->avatar_url); - g_free(d); -} - -static void gg_get_avatar_url_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, - const gchar *url_text, size_t len, const gchar *error_message) { - struct gg_fetch_avatar_data *data; - PurpleConnection *gc = user_data; - PurpleAccount *account; - PurpleBuddy *buddy; - const char *uin; - const char *is_blank; - const char *checksum; - - gchar *bigavatar = NULL; - xmlnode *xml = NULL; - xmlnode *xmlnode_users; - xmlnode *xmlnode_user; - xmlnode *xmlnode_avatars; - xmlnode *xmlnode_avatar; - xmlnode *xmlnode_bigavatar; - - g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc)); - account = purple_connection_get_account(gc); - - if (error_message != NULL) - purple_debug_error("gg", "gg_get_avatars_cb error: %s\n", error_message); - else if (len > 0 && url_text && *url_text) { - xml = xmlnode_from_str(url_text, -1); - if (xml == NULL) - goto out; - - xmlnode_users = xmlnode_get_child(xml, "users"); - if (xmlnode_users == NULL) - goto out; - - xmlnode_user = xmlnode_get_child(xmlnode_users, "user"); - if (xmlnode_user == NULL) - goto out; - - uin = xmlnode_get_attrib(xmlnode_user, "uin"); - - xmlnode_avatars = xmlnode_get_child(xmlnode_user, "avatars"); - if (xmlnode_avatars == NULL) - goto out; - - xmlnode_avatar = xmlnode_get_child(xmlnode_avatars, "avatar"); - if (xmlnode_avatar == NULL) - goto out; - - xmlnode_bigavatar = xmlnode_get_child(xmlnode_avatar, "originBigAvatar"); - if (xmlnode_bigavatar == NULL) - goto out; - - is_blank = xmlnode_get_attrib(xmlnode_avatar, "blank"); - bigavatar = xmlnode_get_data(xmlnode_bigavatar); - - purple_debug_info("gg", "gg_get_avatar_url_cb: UIN %s, IS_BLANK %s, " - "URL %s\n", - uin ? uin : "(null)", is_blank ? is_blank : "(null)", - bigavatar ? bigavatar : "(null)"); - - if (uin != NULL && bigavatar != NULL) { - buddy = purple_find_buddy(account, uin); - if (buddy == NULL) - goto out; - - checksum = purple_buddy_icons_get_checksum_for_user(buddy); - - if (purple_strequal(is_blank, "1")) { - purple_buddy_icons_set_for_user(account, - purple_buddy_get_name(buddy), NULL, 0, NULL); - } else if (!purple_strequal(checksum, bigavatar)) { - data = g_new0(struct gg_fetch_avatar_data, 1); - data->gc = gc; - data->uin = g_strdup(uin); - data->avatar_url = g_strdup(bigavatar); - - purple_debug_info("gg", "gg_get_avatar_url_cb: " - "requesting avatar for %s\n", uin); - url_data = purple_util_fetch_url_request_len_with_account(account, - bigavatar, TRUE, "Mozilla/4.0 (compatible; MSIE 5.0)", - FALSE, NULL, FALSE, -1, gg_fetch_avatar_cb, data); - } - } - } - -out: - if (xml) - xmlnode_free(xml); - g_free(bigavatar); -} - -#endif - -/** - * Try to update avatar of the buddy. - * - * @param gc PurpleConnection - * @param uin UIN of the buddy. - */ -static void ggp_update_buddy_avatar(PurpleConnection *gc, uin_t uin) -{ -#if DISABLE_AVATARS - purple_debug_warning("gg", "ggp_update_buddy_avatar: disabled, please " - "update to 3.0.0, when available\n"); -#else - gchar *avatarurl; - PurpleUtilFetchUrlData *url_data; - - purple_debug_info("gg", "ggp_update_buddy_avatar(gc, %u)\n", uin); - - avatarurl = g_strdup_printf("http://api.gadu-gadu.pl/avatars/%u/0.xml", uin); - - url_data = purple_util_fetch_url_request_len_with_account( - purple_connection_get_account(gc), avatarurl, TRUE, - "Mozilla/4.0 (compatible; MSIE 5.5)", FALSE, NULL, FALSE, -1, - gg_get_avatar_url_cb, gc); - - g_free(avatarurl); -#endif -} - -/** - * Handle change of the status of the buddy. - * - * @param gc PurpleConnection - * @param uin UIN of the buddy. - * @param status ID of the status. - * @param descr Description. - */ -static void ggp_generic_status_handler(PurpleConnection *gc, uin_t uin, - int status, const char *descr) -{ - gchar *from; - const char *st; - char *status_msg = NULL; - - ggp_update_buddy_avatar(gc, uin); - - from = g_strdup_printf("%u", uin); - - switch (status) { - case GG_STATUS_NOT_AVAIL: - case GG_STATUS_NOT_AVAIL_DESCR: - st = purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE); - break; - case GG_STATUS_FFC: - case GG_STATUS_FFC_DESCR: - st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE); - break; - case GG_STATUS_AVAIL: - case GG_STATUS_AVAIL_DESCR: - st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE); - break; - case GG_STATUS_BUSY: - case GG_STATUS_BUSY_DESCR: - st = purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY); - break; - case GG_STATUS_DND: - case GG_STATUS_DND_DESCR: - st = purple_primitive_get_id_from_type(PURPLE_STATUS_UNAVAILABLE); - break; - case GG_STATUS_BLOCKED: - /* user is blocking us.... */ - st = "blocked"; - break; - default: - st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE); - purple_debug_info("gg", - "GG_EVENT_NOTIFY: Unknown status: %d\n", status); - break; - } - - if (descr != NULL) { - status_msg = g_strdup(descr); - g_strstrip(status_msg); - if (status_msg[0] == '\0') { - g_free(status_msg); - status_msg = NULL; - } - } - - purple_debug_info("gg", "status of %u is %s [%s]\n", uin, st, - status_msg ? status_msg : ""); - if (status_msg == NULL) { - purple_prpl_got_user_status(purple_connection_get_account(gc), - from, st, NULL); - } else { - purple_prpl_got_user_status(purple_connection_get_account(gc), - from, st, "message", status_msg, NULL); - g_free(status_msg); - } - g_free(from); -} - -static void ggp_sr_close_cb(gpointer user_data) -{ - GGPSearchForm *form = user_data; - GGPInfo *info = form->user_data; - - ggp_search_remove(info->searches, form->seq); - purple_debug_info("gg", "ggp_sr_close_cb(): Removed seq %u\n", - form->seq); - ggp_search_form_destroy(form); -} - -/** - * Translate a status' ID to a more user-friendly name. - * - * @param id The ID of the status. - * - * @return The user-friendly name of the status. - */ -static const char *ggp_status_by_id(unsigned int id) -{ - const char *st; - - purple_debug_info("gg", "ggp_status_by_id: %d\n", id); - switch (id) { - case GG_STATUS_NOT_AVAIL: - case GG_STATUS_NOT_AVAIL_DESCR: - st = _("Offline"); - break; - case GG_STATUS_AVAIL: - case GG_STATUS_AVAIL_DESCR: - st = _("Available"); - break; - case GG_STATUS_FFC: - case GG_STATUS_FFC_DESCR: - return _("Chatty"); - case GG_STATUS_DND: - case GG_STATUS_DND_DESCR: - return _("Do Not Disturb"); - case GG_STATUS_BUSY: - case GG_STATUS_BUSY_DESCR: - st = _("Away"); - break; - default: - st = _("Unknown"); - break; - } - - return st; -} - -static void ggp_pubdir_handle_info(PurpleConnection *gc, gg_pubdir50_t req, - GGPSearchForm *form) -{ - PurpleNotifyUserInfo *user_info; - PurpleBuddy *buddy; - char *val, *who; - - user_info = purple_notify_user_info_new(); - - val = ggp_search_get_result(req, 0, GG_PUBDIR50_STATUS); - /* XXX: Use of ggp_str_to_uin() is an ugly hack! */ - purple_notify_user_info_add_pair(user_info, _("Status"), ggp_status_by_id(ggp_str_to_uin(val))); - g_free(val); - - who = ggp_search_get_result(req, 0, GG_PUBDIR50_UIN); - purple_notify_user_info_add_pair(user_info, _("UIN"), who); - - val = ggp_search_get_result(req, 0, GG_PUBDIR50_FIRSTNAME); - purple_notify_user_info_add_pair(user_info, _("First Name"), val); - g_free(val); - - val = ggp_search_get_result(req, 0, GG_PUBDIR50_NICKNAME); - purple_notify_user_info_add_pair(user_info, _("Nickname"), val); - g_free(val); - - val = ggp_search_get_result(req, 0, GG_PUBDIR50_CITY); - purple_notify_user_info_add_pair(user_info, _("City"), val); - g_free(val); - - val = ggp_search_get_result(req, 0, GG_PUBDIR50_BIRTHYEAR); - if (strncmp(val, "0", 1)) { - purple_notify_user_info_add_pair(user_info, _("Birth Year"), val); - } - g_free(val); - - /* - * Include a status message, if exists and buddy is in the blist. - */ - buddy = purple_find_buddy(purple_connection_get_account(gc), who); - if (NULL != buddy) { - PurpleStatus *status; - const char *msg; - char *text; - - status = purple_presence_get_active_status(purple_buddy_get_presence(buddy)); - msg = purple_status_get_attr_string(status, "message"); - - if (msg != NULL) { - text = g_markup_escape_text(msg, -1); - purple_notify_user_info_add_pair(user_info, _("Message"), text); - g_free(text); - } - } - - purple_notify_userinfo(gc, who, user_info, ggp_sr_close_cb, form); - g_free(who); - purple_notify_user_info_destroy(user_info); -} - -static void ggp_pubdir_handle_full(PurpleConnection *gc, gg_pubdir50_t req, - GGPSearchForm *form) -{ - PurpleNotifySearchResults *results; - PurpleNotifySearchColumn *column; - int res_count; - int start; - int i; - - g_return_if_fail(form != NULL); - - res_count = gg_pubdir50_count(req); - res_count = (res_count > PUBDIR_RESULTS_MAX) ? PUBDIR_RESULTS_MAX : res_count; - if (form->page_size == 0) - form->page_size = res_count; - - results = purple_notify_searchresults_new(); - - if (results == NULL) { - purple_debug_error("gg", "ggp_pubdir_reply_handler: " - "Unable to display the search results.\n"); - purple_notify_error(gc, NULL, - _("Unable to display the search results."), - NULL); - if (form->window == NULL) - ggp_sr_close_cb(form); - return; - } - - column = purple_notify_searchresults_column_new(_("UIN")); - purple_notify_searchresults_column_add(results, column); - - column = purple_notify_searchresults_column_new(_("First Name")); - purple_notify_searchresults_column_add(results, column); - - column = purple_notify_searchresults_column_new(_("Nickname")); - purple_notify_searchresults_column_add(results, column); - - column = purple_notify_searchresults_column_new(_("City")); - purple_notify_searchresults_column_add(results, column); - - column = purple_notify_searchresults_column_new(_("Birth Year")); - purple_notify_searchresults_column_add(results, column); - - purple_debug_info("gg", "Going with %d entries\n", res_count); - - start = (int)ggp_str_to_uin(gg_pubdir50_get(req, 0, GG_PUBDIR50_START)); - purple_debug_info("gg", "start = %d\n", start); - - for (i = 0; i < res_count; i++) { - GList *row = NULL; - char *birth = ggp_search_get_result(req, i, GG_PUBDIR50_BIRTHYEAR); - - /* TODO: Status will be displayed as an icon. */ - /* row = g_list_append(row, ggp_search_get_result(req, i, GG_PUBDIR50_STATUS)); */ - row = g_list_append(row, ggp_search_get_result(req, i, - GG_PUBDIR50_UIN)); - row = g_list_append(row, ggp_search_get_result(req, i, - GG_PUBDIR50_FIRSTNAME)); - row = g_list_append(row, ggp_search_get_result(req, i, - GG_PUBDIR50_NICKNAME)); - row = g_list_append(row, ggp_search_get_result(req, i, - GG_PUBDIR50_CITY)); - row = g_list_append(row, - (birth && strncmp(birth, "0", 1)) ? birth : g_strdup("-")); - - purple_notify_searchresults_row_add(results, row); - } - - purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_CONTINUE, - ggp_callback_show_next); - purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_ADD, - ggp_callback_add_buddy); - purple_notify_searchresults_button_add(results, PURPLE_NOTIFY_BUTTON_IM, - ggp_callback_im); - - if (form->window == NULL) { - void *h = purple_notify_searchresults(gc, - _("Gadu-Gadu Public Directory"), - _("Search results"), NULL, results, - (PurpleNotifyCloseCallback)ggp_sr_close_cb, - form); - - if (h == NULL) { - purple_debug_error("gg", "ggp_pubdir_reply_handler: " - "Unable to display the search results.\n"); - purple_notify_error(gc, NULL, - _("Unable to display the search results."), - NULL); - return; - } - - form->window = h; - } else { - purple_notify_searchresults_new_rows(gc, results, form->window); - } -} - -static void ggp_pubdir_reply_handler(PurpleConnection *gc, gg_pubdir50_t req) -{ - GGPInfo *info = gc->proto_data; - GGPSearchForm *form; - int res_count; - guint32 seq; - - seq = gg_pubdir50_seq(req); - form = ggp_search_get(info->searches, seq); - purple_debug_info("gg", - "ggp_pubdir_reply_handler(): seq %u --> form %p\n", seq, form); - /* - * this can happen when user will request more results - * and close the results window before they arrive. - */ - g_return_if_fail(form != NULL); - - res_count = gg_pubdir50_count(req); - if (res_count < 1) { - purple_debug_info("gg", "GG_EVENT_PUBDIR50_SEARCH_REPLY: Nothing found\n"); - purple_notify_error(gc, NULL, - _("No matching users found"), - _("There are no users matching your search criteria.")); - if (form->window == NULL) - ggp_sr_close_cb(form); - return; - } - - switch (form->search_type) { - case GGP_SEARCH_TYPE_INFO: - ggp_pubdir_handle_info(gc, req, form); - break; - case GGP_SEARCH_TYPE_FULL: - ggp_pubdir_handle_full(gc, req, form); - break; - default: - purple_debug_warning("gg", "Unknown search_type!\n"); - break; - } -} - -static void ggp_recv_image_handler(PurpleConnection *gc, const struct gg_event *ev) -{ - gint imgid = 0; - GGPInfo *info = gc->proto_data; - GList *entry = g_list_first(info->pending_richtext_messages); - gchar *handlerid = g_strdup_printf("IMGID_HANDLER-%i", ev->event.image_reply.crc32); - - imgid = purple_imgstore_add_with_id( - g_memdup2(ev->event.image_reply.image, ev->event.image_reply.size), - ev->event.image_reply.size, - ev->event.image_reply.filename); - - purple_debug_info("gg", "ggp_recv_image_handler: got image with crc32: %u\n", ev->event.image_reply.crc32); - - while(entry) { - if (strstr((gchar *)entry->data, handlerid) != NULL) { - gchar **split = g_strsplit((gchar *)entry->data, handlerid, 3); - gchar *text = g_strdup_printf("%s%i%s", split[0], imgid, split[1]); - purple_debug_info("gg", "ggp_recv_image_handler: found message matching crc32: %s\n", (gchar *)entry->data); - g_strfreev(split); - info->pending_richtext_messages = g_list_remove(info->pending_richtext_messages, entry->data); - /* We don't have any more images to download */ - if (strstr(text, "event.image_reply.sender); - serv_got_im(gc, buf, text, PURPLE_MESSAGE_IMAGES, time(NULL)); - g_free(buf); - purple_debug_info("gg", "ggp_recv_image_handler: richtext message: %s\n", text); - g_free(text); - break; - } - info->pending_richtext_messages = g_list_append(info->pending_richtext_messages, text); - break; - } - entry = g_list_next(entry); - } - g_free(handlerid); - - return; -} - - -/** - * Dispatch a message received from a buddy. - * - * @param gc PurpleConnection. - * @param ev Gadu-Gadu event structure. - * - * Image receiving, some code borrowed from Kadu http://www.kadu.net - */ -static void ggp_recv_message_handler(PurpleConnection *gc, const struct gg_event *ev) -{ - GGPInfo *info = gc->proto_data; - PurpleConversation *conv; - gchar *from; - gchar *msg; - gchar *tmp; - - if (ev->event.msg.message == NULL) - { - purple_debug_warning("gg", "ggp_recv_message_handler: NULL as message pointer\n"); - return; - } - - from = g_strdup_printf("%lu", (unsigned long int)ev->event.msg.sender); - - /* - tmp = charset_convert((const char *)ev->event.msg.message, - "CP1250", "UTF-8"); - */ - tmp = g_strdup_printf("%s", ev->event.msg.message); - purple_str_strip_char(tmp, '\r'); - msg = g_markup_escape_text(tmp, -1); - g_free(tmp); - - /* We got richtext message */ - if (ev->event.msg.formats_length) - { - gboolean got_image = FALSE, bold = FALSE, italic = FALSE, under = FALSE; - char *cformats = (char *)ev->event.msg.formats; - char *cformats_end = cformats + ev->event.msg.formats_length; - gint increased_len = 0; - struct gg_msg_richtext_format *actformat; - struct gg_msg_richtext_image *actimage; - GString *message = g_string_new(msg); - gchar *handlerid; - - purple_debug_info("gg", "ggp_recv_message_handler: richtext msg from (%s): %s %i formats\n", from, msg, ev->event.msg.formats_length); - - while (cformats < cformats_end) - { - gint byteoffset; - actformat = (struct gg_msg_richtext_format *)cformats; - cformats += sizeof(struct gg_msg_richtext_format); - byteoffset = g_utf8_offset_to_pointer(message->str, actformat->position + increased_len) - message->str; - - if(actformat->position == 0 && actformat->font == 0) { - purple_debug_warning("gg", "ggp_recv_message_handler: bogus formatting (inc: %i)\n", increased_len); - continue; - } - purple_debug_info("gg", "ggp_recv_message_handler: format at pos: %i, image:%i, bold:%i, italic: %i, under:%i (inc: %i)\n", - actformat->position, - (actformat->font & GG_FONT_IMAGE) != 0, - (actformat->font & GG_FONT_BOLD) != 0, - (actformat->font & GG_FONT_ITALIC) != 0, - (actformat->font & GG_FONT_UNDERLINE) != 0, - increased_len); - - if (actformat->font & GG_FONT_IMAGE) { - got_image = TRUE; - actimage = (struct gg_msg_richtext_image*)(cformats); - cformats += sizeof(struct gg_msg_richtext_image); - purple_debug_info("gg", "ggp_recv_message_handler: image received, size: %d, crc32: %i\n", actimage->size, actimage->crc32); - - /* Checking for errors, image size shouldn't be - * larger than 255.000 bytes */ - if (actimage->size > 255000) { - purple_debug_warning("gg", "ggp_recv_message_handler: received image large than 255 kb\n"); - continue; - } - - gg_image_request(info->session, ev->event.msg.sender, - actimage->size, actimage->crc32); - - handlerid = g_strdup_printf("", actimage->crc32); - g_string_insert(message, byteoffset, handlerid); - increased_len += strlen(handlerid); - g_free(handlerid); - continue; - } - - if (actformat->font & GG_FONT_BOLD) { - if (bold == FALSE) { - g_string_insert(message, byteoffset, ""); - increased_len += 3; - bold = TRUE; - } - } else if (bold) { - g_string_insert(message, byteoffset, ""); - increased_len += 4; - bold = FALSE; - } - - if (actformat->font & GG_FONT_ITALIC) { - if (italic == FALSE) { - g_string_insert(message, byteoffset, ""); - increased_len += 3; - italic = TRUE; - } - } else if (italic) { - g_string_insert(message, byteoffset, ""); - increased_len += 4; - italic = FALSE; - } - - if (actformat->font & GG_FONT_UNDERLINE) { - if (under == FALSE) { - g_string_insert(message, byteoffset, ""); - increased_len += 3; - under = TRUE; - } - } else if (under) { - g_string_insert(message, byteoffset, ""); - increased_len += 4; - under = FALSE; - } - - if (actformat->font & GG_FONT_COLOR) { - cformats += sizeof(struct gg_msg_richtext_color); - } - } - - msg = g_string_free(message, FALSE); - - if (got_image) { - info->pending_richtext_messages = g_list_append(info->pending_richtext_messages, msg); - return; - } - } - - purple_debug_info("gg", "ggp_recv_message_handler: msg from (%s): %s (class = %d; rcpt_count = %d)\n", - from, msg, ev->event.msg.msgclass, - ev->event.msg.recipients_count); - - if (ev->event.msg.recipients_count == 0) { - serv_got_im(gc, from, msg, 0, ev->event.msg.time); - } else { - const char *chat_name; - int chat_id; - char *buddy_name; - - chat_name = ggp_confer_find_by_participants(gc, - ev->event.msg.recipients, - ev->event.msg.recipients_count); - - if (chat_name == NULL) { - chat_name = ggp_confer_add_new(gc, NULL); - serv_got_joined_chat(gc, info->chats_count, chat_name); - - ggp_confer_participants_add_uin(gc, chat_name, - ev->event.msg.sender); - - ggp_confer_participants_add(gc, chat_name, - ev->event.msg.recipients, - ev->event.msg.recipients_count); - } - conv = ggp_confer_find_by_name(gc, chat_name); - chat_id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)); - - buddy_name = ggp_buddy_get_name(gc, ev->event.msg.sender); - serv_got_chat_in(gc, chat_id, buddy_name, - PURPLE_MESSAGE_RECV, msg, ev->event.msg.time); - g_free(buddy_name); - } - g_free(msg); - g_free(from); -} - -static void ggp_send_image_handler(PurpleConnection *gc, const struct gg_event *ev) -{ - GGPInfo *info = gc->proto_data; - PurpleStoredImage *image; - gint imgid = GPOINTER_TO_INT(g_hash_table_lookup(info->pending_images, GINT_TO_POINTER(ev->event.image_request.crc32))); - - purple_debug_info("gg", "ggp_send_image_handler: image request received, crc32: %u, imgid: %d\n", ev->event.image_request.crc32, imgid); - - if(imgid) - { - if((image = purple_imgstore_find_by_id(imgid))) { - gint image_size = purple_imgstore_get_size(image); - gconstpointer image_bin = purple_imgstore_get_data(image); - const char *image_filename = purple_imgstore_get_filename(image); - - purple_debug_info("gg", "ggp_send_image_handler: sending image imgid: %i, crc: %u\n", imgid, ev->event.image_request.crc32); - gg_image_reply(info->session, (unsigned long int)ev->event.image_request.sender, image_filename, image_bin, image_size); - purple_imgstore_unref(image); - } else { - purple_debug_error("gg", "ggp_send_image_handler: image imgid: %i, crc: %u in hash but not found in imgstore!\n", imgid, ev->event.image_request.crc32); - } - g_hash_table_remove(info->pending_images, GINT_TO_POINTER(ev->event.image_request.crc32)); - } -} - -static void ggp_typing_notification_handler(PurpleConnection *gc, uin_t uin, int length) { - gchar *from; - - from = g_strdup_printf("%u", uin); - if (length) - serv_got_typing(gc, from, 0, PURPLE_TYPING); - else - serv_got_typing_stopped(gc, from); - g_free(from); -} - -/** - * Handling of XML events. - * - * @param gc PurpleConnection. - * @param data Raw XML contents. - * - * @see http://toxygen.net/libgadu/protocol/#ch1.13 - */ -static void ggp_xml_event_handler(PurpleConnection *gc, char *data) -{ - xmlnode *xml = NULL; - xmlnode *xmlnode_next_event; - - xml = xmlnode_from_str(data, -1); - if (xml == NULL) - goto out; - - xmlnode_next_event = xmlnode_get_child(xml, "event"); - while (xmlnode_next_event != NULL) - { - xmlnode *xmlnode_current_event = xmlnode_next_event; - - xmlnode *xmlnode_type; - char *event_type_raw; - int event_type = 0; - - xmlnode *xmlnode_sender; - char *event_sender_raw; - uin_t event_sender = 0; - - xmlnode_next_event = xmlnode_get_next_twin(xmlnode_next_event); - - xmlnode_type = xmlnode_get_child(xmlnode_current_event, "type"); - if (xmlnode_type == NULL) - continue; - event_type_raw = xmlnode_get_data(xmlnode_type); - if (event_type_raw != NULL) - event_type = atoi(event_type_raw); - g_free(event_type_raw); - - xmlnode_sender = xmlnode_get_child(xmlnode_current_event, "sender"); - if (xmlnode_sender != NULL) - { - event_sender_raw = xmlnode_get_data(xmlnode_sender); - if (event_sender_raw != NULL) - event_sender = ggp_str_to_uin(event_sender_raw); - g_free(event_sender_raw); - } - - switch (event_type) - { - case 28: /* avatar update */ - purple_debug_info("gg", - "ggp_xml_event_handler: avatar updated (uid: %u)\n", - event_sender); - ggp_update_buddy_avatar(gc, event_sender); - break; - default: - purple_debug_error("gg", - "ggp_xml_event_handler: unsupported event type=%d from=%u\n", - event_type, event_sender); - } - } - - out: - if (xml) - xmlnode_free(xml); -} - -static void ggp_callback_recv(gpointer _gc, gint fd, PurpleInputCondition cond) -{ - PurpleConnection *gc = _gc; - GGPInfo *info = gc->proto_data; - struct gg_event *ev; - int i; - - if (!(ev = gg_watch_fd(info->session))) { - purple_debug_error("gg", - "ggp_callback_recv: gg_watch_fd failed -- CRITICAL!\n"); - purple_connection_error_reason (gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to read from socket")); - return; - } - - purple_input_remove(gc->inpa); - gc->inpa = purple_input_add(info->session->fd, - ggp_tcpsocket_inputcond_gg_to_purple(info->session->check), - ggp_callback_recv, gc); - - switch (ev->type) { - case GG_EVENT_NONE: - /* Nothing happened. */ - break; - case GG_EVENT_MSG: - ggp_recv_message_handler(gc, ev); - break; - case GG_EVENT_ACK: - /* Changing %u to %i fixes compiler warning */ - purple_debug_info("gg", - "ggp_callback_recv: message sent to: %i, delivery status=%d, seq=%d\n", - ev->event.ack.recipient, ev->event.ack.status, - ev->event.ack.seq); - break; - case GG_EVENT_IMAGE_REPLY: - ggp_recv_image_handler(gc, ev); - break; - case GG_EVENT_IMAGE_REQUEST: - ggp_send_image_handler(gc, ev); - break; - case GG_EVENT_NOTIFY: - case GG_EVENT_NOTIFY_DESCR: - { - struct gg_notify_reply *n; - char *descr; - - purple_debug_info("gg", "notify_pre: (%d) status: %d\n", - ev->event.notify->uin, - GG_S(ev->event.notify->status)); - - n = (ev->type == GG_EVENT_NOTIFY) ? ev->event.notify - : ev->event.notify_descr.notify; - - for (; n->uin; n++) { - descr = (ev->type == GG_EVENT_NOTIFY) ? NULL - : ev->event.notify_descr.descr; - - purple_debug_info("gg", - "notify: (%d) status: %d; descr: %s\n", - n->uin, GG_S(n->status), descr ? descr : "(null)"); - - ggp_generic_status_handler(gc, - n->uin, GG_S(n->status), descr); - } - } - break; - case GG_EVENT_NOTIFY60: - for (i = 0; ev->event.notify60[i].uin; i++) { - purple_debug_info("gg", - "notify60: (%d) status=%d; version=%d; descr=%s\n", - ev->event.notify60[i].uin, - GG_S(ev->event.notify60[i].status), - ev->event.notify60[i].version, - ev->event.notify60[i].descr ? ev->event.notify60[i].descr : "(null)"); - - ggp_generic_status_handler(gc, ev->event.notify60[i].uin, - GG_S(ev->event.notify60[i].status), - ev->event.notify60[i].descr); - } - break; - case GG_EVENT_STATUS: - purple_debug_info("gg", "status: (%d) status=%d; descr=%s\n", - ev->event.status.uin, GG_S(ev->event.status.status), - ev->event.status.descr ? ev->event.status.descr : "(null)"); - - ggp_generic_status_handler(gc, ev->event.status.uin, - GG_S(ev->event.status.status), ev->event.status.descr); - break; - case GG_EVENT_STATUS60: - purple_debug_info("gg", - "status60: (%d) status=%d; version=%d; descr=%s\n", - ev->event.status60.uin, GG_S(ev->event.status60.status), - ev->event.status60.version, - ev->event.status60.descr ? ev->event.status60.descr : "(null)"); - - ggp_generic_status_handler(gc, ev->event.status60.uin, - GG_S(ev->event.status60.status), ev->event.status60.descr); - break; - case GG_EVENT_PUBDIR50_SEARCH_REPLY: - ggp_pubdir_reply_handler(gc, ev->event.pubdir50); - break; - case GG_EVENT_TYPING_NOTIFICATION: - ggp_typing_notification_handler(gc, ev->event.typing_notification.uin, - ev->event.typing_notification.length); - break; - case GG_EVENT_XML_EVENT: - purple_debug_info("gg", "GG_EVENT_XML_EVENT\n"); - ggp_xml_event_handler(gc, ev->event.xml_event.data); - break; - default: - purple_debug_error("gg", - "unsupported event type=%d\n", ev->type); - break; - } - - gg_free_event(ev); -} - -static void ggp_async_login_handler(gpointer _gc, gint fd, PurpleInputCondition cond) -{ - PurpleConnection *gc = _gc; - GGPInfo *info; - struct gg_event *ev; - - g_return_if_fail(PURPLE_CONNECTION_IS_VALID(gc)); - - info = gc->proto_data; - - purple_debug_info("gg", "login_handler: session: check = %d; state = %d;\n", - info->session->check, info->session->state); - - switch (info->session->state) { - case GG_STATE_RESOLVING: - purple_debug_info("gg", "GG_STATE_RESOLVING\n"); - break; - case GG_STATE_RESOLVING_GG: - purple_debug_info("gg", "GG_STATE_RESOLVING_GG\n"); - break; - case GG_STATE_CONNECTING_HUB: - purple_debug_info("gg", "GG_STATE_CONNECTING_HUB\n"); - break; - case GG_STATE_READING_DATA: - purple_debug_info("gg", "GG_STATE_READING_DATA\n"); - break; - case GG_STATE_CONNECTING_GG: - purple_debug_info("gg", "GG_STATE_CONNECTING_GG\n"); - break; - case GG_STATE_READING_KEY: - purple_debug_info("gg", "GG_STATE_READING_KEY\n"); - break; - case GG_STATE_READING_REPLY: - purple_debug_info("gg", "GG_STATE_READING_REPLY\n"); - break; - case GG_STATE_TLS_NEGOTIATION: - purple_debug_info("gg", "GG_STATE_TLS_NEGOTIATION\n"); - break; - default: - purple_debug_error("gg", "unknown state = %d\n", - info->session->state); - break; - } - - if (!(ev = gg_watch_fd(info->session))) { - purple_debug_error("gg", "login_handler: gg_watch_fd failed!\n"); - purple_connection_error_reason (gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to read from socket")); - return; - } - purple_debug_info("gg", "login_handler: session->fd = %d\n", info->session->fd); - purple_debug_info("gg", "login_handler: session: check = %d; state = %d;\n", - info->session->check, info->session->state); - - purple_input_remove(gc->inpa); - - /** XXX I think that this shouldn't be done if ev->type is GG_EVENT_CONN_FAILED or GG_EVENT_CONN_SUCCESS -datallah */ - if (info->session->fd >= 0) - gc->inpa = purple_input_add(info->session->fd, - ggp_tcpsocket_inputcond_gg_to_purple(info->session->check), - ggp_async_login_handler, gc); - - switch (ev->type) { - case GG_EVENT_NONE: - /* Nothing happened. */ - purple_debug_info("gg", "GG_EVENT_NONE\n"); - break; - case GG_EVENT_CONN_SUCCESS: - { - purple_debug_info("gg", "GG_EVENT_CONN_SUCCESS\n"); - purple_input_remove(gc->inpa); - gc->inpa = purple_input_add(info->session->fd, - ggp_tcpsocket_inputcond_gg_to_purple(info->session->check), - ggp_callback_recv, gc); - - ggp_buddylist_send(gc); - purple_connection_update_progress(gc, _("Connected"), 1, 2); - purple_connection_set_state(gc, PURPLE_CONNECTED); - } - break; - case GG_EVENT_CONN_FAILED: - purple_input_remove(gc->inpa); - gc->inpa = 0; - purple_connection_error_reason (gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Connection failed")); - break; - case GG_EVENT_MSG: - if (ev->event.msg.sender == 0) - /* system messages are mostly ads */ - purple_debug_info("gg", "System message:\n%s\n", - ev->event.msg.message); - else - purple_debug_warning("gg", "GG_EVENT_MSG: message from user %u " - "unexpected while connecting:\n%s\n", - ev->event.msg.sender, - ev->event.msg.message); - break; - default: - purple_debug_error("gg", "strange event: %d\n", ev->type); - break; - } - - gg_free_event(ev); -} - -/* ---------------------------------------------------------------------- */ -/* ----- PurplePluginProtocolInfo ----------------------------------------- */ -/* ---------------------------------------------------------------------- */ - -static const char *ggp_list_icon(PurpleAccount *account, PurpleBuddy *buddy) -{ - return "gadu-gadu"; -} - -static char *ggp_status_text(PurpleBuddy *b) -{ - PurpleStatus *status; - const char *msg; - char *text; - char *tmp; - - status = purple_presence_get_active_status( - purple_buddy_get_presence(b)); - msg = purple_status_get_attr_string(status, "message"); - - if (msg == NULL) - return NULL; - - tmp = purple_markup_strip_html(msg); - text = g_markup_escape_text(tmp, -1); - g_free(tmp); - - return text; -} - -static void ggp_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) -{ - PurpleStatus *status; - char *text, *tmp; - const char *msg, *name, *alias; - - g_return_if_fail(b != NULL); - - status = purple_presence_get_active_status(purple_buddy_get_presence(b)); - msg = purple_status_get_attr_string(status, "message"); - name = purple_status_get_name(status); - alias = purple_buddy_get_alias(b); - - purple_notify_user_info_add_pair (user_info, _("Alias"), alias); - - if (msg != NULL) { - text = g_markup_escape_text(msg, -1); - if (PURPLE_BUDDY_IS_ONLINE(b)) { - tmp = g_strdup_printf("%s: %s", name, text); - purple_notify_user_info_add_pair(user_info, _("Status"), tmp); - g_free(tmp); - } else { - purple_notify_user_info_add_pair(user_info, _("Message"), text); - } - g_free(text); - /* We don't want to duplicate 'Status: Offline'. */ - } else if (PURPLE_BUDDY_IS_ONLINE(b)) { - purple_notify_user_info_add_pair(user_info, _("Status"), name); - } -} - -static GList *ggp_status_types(PurpleAccount *account) -{ - PurpleStatusType *type; - GList *types = NULL; - - type = purple_status_type_new_with_attrs( - PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE, TRUE, FALSE, - "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), - NULL); - types = g_list_append(types, type); - - /* - * Without this selecting Invisible as own status doesn't - * work. It's not used and not needed to show status of buddies. - */ - type = purple_status_type_new_with_attrs( - PURPLE_STATUS_INVISIBLE, NULL, NULL, TRUE, TRUE, FALSE, - "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), - NULL); - types = g_list_append(types, type); - - type = purple_status_type_new_with_attrs( - PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE, - "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), - NULL); - types = g_list_append(types, type); - - /* - * New statuses for GG 8.0 like PoGGadaj ze mna (not yet because - * libpurple can't support Chatty status) and Nie przeszkadzac - */ - type = purple_status_type_new_with_attrs( - PURPLE_STATUS_UNAVAILABLE, NULL, NULL, TRUE, TRUE, FALSE, - "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), - NULL); - types = g_list_append(types, type); - - /* - * This status is necessary to display guys who are blocking *us*. - */ - type = purple_status_type_new_with_attrs( - PURPLE_STATUS_INVISIBLE, "blocked", _("Blocked"), TRUE, FALSE, FALSE, - "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), NULL); - types = g_list_append(types, type); - - type = purple_status_type_new_with_attrs( - PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE, - "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), - NULL); - types = g_list_append(types, type); - - return types; -} - -static GList *ggp_blist_node_menu(PurpleBlistNode *node) -{ - PurpleMenuAction *act; - GList *m = NULL; - PurpleAccount *account; - GGPInfo *info; - - if (!PURPLE_BLIST_NODE_IS_BUDDY(node)) - return NULL; - - account = purple_buddy_get_account((PurpleBuddy *) node); - info = purple_account_get_connection(account)->proto_data; - if (info->chats) { - act = purple_menu_action_new(_("Add to chat"), - PURPLE_CALLBACK(ggp_bmenu_add_to_chat), - NULL, NULL); - m = g_list_append(m, act); - } - - return m; -} - -static GList *ggp_chat_info(PurpleConnection *gc) -{ - GList *m = NULL; - struct proto_chat_entry *pce; - - pce = g_new0(struct proto_chat_entry, 1); - pce->label = _("Chat _name:"); - pce->identifier = "name"; - pce->required = TRUE; - m = g_list_append(m, pce); - - return m; -} - -static void ggp_login_to(PurpleAccount *account, uint32_t server) -{ - PurpleConnection *gc; - PurplePresence *presence; - PurpleStatus *status; - struct gg_login_params *glp; - GGPInfo *info; - const gchar *encryption_type; - - if (ggp_setup_proxy(account) == -1) - return; - - gc = purple_account_get_connection(account); - glp = g_new0(struct gg_login_params, 1); - info = gc->proto_data; - g_return_if_fail(info); - - /* Probably this should be moved to *_new() function. */ - info->session = NULL; - info->chats = NULL; - info->chats_count = 0; - info->token = NULL; - info->searches = ggp_search_new(); - info->pending_richtext_messages = NULL; - info->pending_images = g_hash_table_new(g_direct_hash, g_direct_equal); - info->status_broadcasting = purple_account_get_bool(account, "status_broadcasting", TRUE); - - glp->uin = ggp_get_uin(account); - glp->password = (char *)purple_account_get_password(account); - glp->image_size = 255; - - presence = purple_account_get_presence(account); - status = purple_presence_get_active_status(presence); - - glp->encoding = GG_ENCODING_UTF8; - glp->protocol_features = (GG_FEATURE_STATUS80|GG_FEATURE_DND_FFC - |GG_FEATURE_TYPING_NOTIFICATION); - - glp->async = 1; - glp->status = ggp_to_gg_status(status, &glp->status_descr); - - encryption_type = purple_account_get_string(account, "encryption", "none"); - purple_debug_info("gg", "Requested encryption type: %s\n", encryption_type); - if (purple_strequal(encryption_type, "opportunistic_tls")) - glp->tls = 1; - else - glp->tls = 0; - purple_debug_info("gg", "TLS enabled: %d\n", glp->tls); - - if (!info->status_broadcasting) - glp->status = glp->status|GG_STATUS_FRIENDS_MASK; - glp->server_addr = server; - - info->session = gg_login(glp); - g_free(glp); - - purple_connection_update_progress(gc, _("Connecting"), 0, 2); - if (info->session == NULL) { - purple_connection_error_reason (gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Connection failed")); - return; - } - gc->inpa = purple_input_add(info->session->fd, - ggp_tcpsocket_inputcond_gg_to_purple(info->session->check), - ggp_async_login_handler, gc); -} - -static void -ggp_login_resolved(GSList *hosts, gpointer _account, const char *error_message) -{ - PurpleAccount *account = _account; - PurpleConnection *gc; - GGPInfo *info; - uint32_t server_addr = 0; - - gc = purple_account_get_connection(account); - info = gc->proto_data; - g_return_if_fail(info); - info->dns_query = NULL; - - while (hosts && (hosts = g_slist_delete_link(hosts, hosts))) { - struct sockaddr *addr = hosts->data; - - if (addr->sa_family == AF_INET && server_addr == 0) { - struct sockaddr_in *addrv4 = (struct sockaddr_in *)addr; - - server_addr = addrv4->sin_addr.s_addr; - } - - g_free(hosts->data); - hosts = g_slist_delete_link(hosts, hosts); - } - - if (server_addr == 0) { - gchar *tmp = g_strdup_printf( - _("Unable to resolve hostname: %s"), error_message); - purple_connection_error_reason(gc, - /* should this be a settings error? */ - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, tmp); - g_free(tmp); - return; - } - - ggp_login_to(account, server_addr); -} - -static void -ggp_login(PurpleAccount *account) -{ - PurpleConnection *gc; - GGPInfo *info; - const char *address; - - gc = purple_account_get_connection(account); - info = g_new0(GGPInfo, 1); - gc->proto_data = info; - - address = purple_account_get_string(account, "gg_server", ""); - if (address == NULL || address[0] == '\0') { - purple_debug_info("gg", "Trying to retrieve address from gg appmsg service\n"); - ggp_login_to(account, 0); - return; - } - - purple_debug_info("gg", "Using gg server given by user (%s)\n", address); - info->dns_query = purple_dnsquery_a_account(account, address, 8074, - ggp_login_resolved, account); -} - -static void ggp_close(PurpleConnection *gc) -{ - if (gc == NULL) { - purple_debug_info("gg", "gc == NULL\n"); - return; - } - - if (gc->proto_data) { - PurpleAccount *account = purple_connection_get_account(gc); - PurpleStatus *status; - GGPInfo *info = gc->proto_data; - - if (info->dns_query) - purple_dnsquery_destroy(info->dns_query); - - status = purple_account_get_active_status(account); - - if (info->session != NULL) { - ggp_set_status(account, status); - gg_logoff(info->session); - gg_free_session(info->session); - } - - purple_account_set_bool(account, "status_broadcasting", info->status_broadcasting); - - /* Immediately close any notifications on this handle since that process depends - * upon the contents of info->searches, which we are about to destroy. - */ - purple_notify_close_with_handle(gc); - - ggp_search_destroy(info->searches); - g_list_free(info->pending_richtext_messages); - g_hash_table_destroy(info->pending_images); - g_free(info); - gc->proto_data = NULL; - } - - if (gc->inpa > 0) - purple_input_remove(gc->inpa); - - purple_debug_info("gg", "Connection closed.\n"); -} - -static int ggp_send_im(PurpleConnection *gc, const char *who, const char *msg, - PurpleMessageFlags flags) -{ - GGPInfo *info = gc->proto_data; - char *tmp, *plain; - int ret = 1; - unsigned char format[1024]; - unsigned int format_length = sizeof(struct gg_msg_richtext); - gint pos = 0; - GData *attribs; - const char *start, *end = NULL, *last; - - if (msg == NULL || *msg == '\0') { - return 0; - } - - last = msg; - - /* Check if the message is richtext */ - /* TODO: Check formatting, too */ - if(purple_markup_find_tag("img", last, &start, &end, &attribs)) { - - GString *string_buffer = g_string_new(NULL); - struct gg_msg_richtext fmt; - - do { - PurpleStoredImage *image; - const char *id; - - /* Add text before the image */ - if(start - last) { - pos = pos + g_utf8_strlen(last, start - last); - g_string_append_len(string_buffer, last, start - last); - } - - if((id = g_datalist_get_data(&attribs, "id")) && (image = purple_imgstore_find_by_id(atoi(id)))) { - struct gg_msg_richtext_format actformat; - struct gg_msg_richtext_image actimage; - gint image_size = purple_imgstore_get_size(image); - gconstpointer image_bin = purple_imgstore_get_data(image); - const char *image_filename = purple_imgstore_get_filename(image); - uint32_t crc32 = gg_crc32(0, image_bin, image_size); - - g_hash_table_insert(info->pending_images, GINT_TO_POINTER(crc32), GINT_TO_POINTER(atoi(id))); - purple_imgstore_ref(image); - purple_debug_info("gg", "ggp_send_im_richtext: got crc: %u for imgid: %i\n", crc32, atoi(id)); - - actformat.font = GG_FONT_IMAGE; - actformat.position = pos; - - actimage.unknown1 = 0x0109; - actimage.size = gg_fix32(image_size); - actimage.crc32 = gg_fix32(crc32); - - if (actimage.size > 255000) { - purple_debug_warning("gg", "ggp_send_im_richtext: image over 255kb!\n"); - } else { - purple_debug_info("gg", "ggp_send_im_richtext: adding images to richtext, size: %i, crc32: %u, name: %s\n", actimage.size, actimage.crc32, image_filename); - - memcpy(format + format_length, &actformat, sizeof(actformat)); - format_length += sizeof(actformat); - memcpy(format + format_length, &actimage, sizeof(actimage)); - format_length += sizeof(actimage); - } - } else { - purple_debug_error("gg", "ggp_send_im_richtext: image not found in the image store!"); - } - - last = end + 1; - g_datalist_clear(&attribs); - - } while(purple_markup_find_tag("img", last, &start, &end, &attribs)); - - /* Add text after the images */ - if(last && *last) { - /* this is currently not used, but might be useful later? */ - /* pos = pos + g_utf8_strlen(last, -1); */ - g_string_append(string_buffer, last); - } - - fmt.flag = 2; - fmt.length = format_length - sizeof(fmt); - memcpy(format, &fmt, sizeof(fmt)); - - purple_debug_info("gg", "ggp_send_im: richtext msg = %s\n", string_buffer->str); - plain = purple_unescape_html(string_buffer->str); - g_string_free(string_buffer, TRUE); - } else { - purple_debug_info("gg", "ggp_send_im: msg = %s\n", msg); - plain = purple_unescape_html(msg); - } - - /* - tmp = charset_convert(plain, "UTF-8", "CP1250"); - */ - tmp = g_strdup_printf("%s", plain); - - if (tmp && (format_length - sizeof(struct gg_msg_richtext))) { - if(gg_send_message_richtext(info->session, GG_CLASS_CHAT, ggp_str_to_uin(who), (unsigned char *)tmp, format, format_length) < 0) { - ret = -1; - } else { - ret = 1; - } - } else if (NULL == tmp || *tmp == 0) { - ret = 0; - } else if (strlen(tmp) > GG_MSG_MAXSIZE) { - ret = -E2BIG; - } else if (gg_send_message(info->session, GG_CLASS_CHAT, - ggp_str_to_uin(who), (unsigned char *)tmp) < 0) { - ret = -1; - } else { - ret = 1; - } - - g_free(plain); - g_free(tmp); - - return ret; -} - -static unsigned int ggp_send_typing(PurpleConnection *gc, const char *name, PurpleTypingState state) -{ - int dummy_length; // we don't send real length of typed message - - if (state == PURPLE_TYPED) // not supported - return 1; - - if (state == PURPLE_TYPING) - dummy_length = (int)g_random_int(); - else // PURPLE_NOT_TYPING - dummy_length = 0; - - gg_typing_notification( - ((GGPInfo*)gc->proto_data)->session, - ggp_str_to_uin(name), - dummy_length); - - return 1; // wait 1 second before another notification -} - -static void ggp_get_info(PurpleConnection *gc, const char *name) -{ - GGPInfo *info = gc->proto_data; - GGPSearchForm *form; - guint32 seq; - - form = ggp_search_form_new(GGP_SEARCH_TYPE_INFO); - - form->user_data = info; - form->uin = g_strdup(name); - - seq = ggp_search_start(gc, form); - ggp_search_add(info->searches, seq, form); - purple_debug_info("gg", "ggp_get_info(): Added seq %u", seq); -} - -static int ggp_to_gg_status(PurpleStatus *status, char **msg) -{ - const char *status_id = purple_status_get_id(status); - int new_status, new_status_descr; - const char *new_msg; - - g_return_val_if_fail(msg != NULL, 0); - - purple_debug_info("gg", "ggp_to_gg_status: Requested status = %s\n", - status_id); - - if (purple_strequal(status_id, "available")) { - new_status = GG_STATUS_AVAIL; - new_status_descr = GG_STATUS_AVAIL_DESCR; - } else if (purple_strequal(status_id, "away")) { - new_status = GG_STATUS_BUSY; - new_status_descr = GG_STATUS_BUSY_DESCR; - } else if (purple_strequal(status_id, "unavailable")) { - new_status = GG_STATUS_DND; - new_status_descr = GG_STATUS_DND_DESCR; - } else if (purple_strequal(status_id, "invisible")) { - new_status = GG_STATUS_INVISIBLE; - new_status_descr = GG_STATUS_INVISIBLE_DESCR; - } else if (purple_strequal(status_id, "offline")) { - new_status = GG_STATUS_NOT_AVAIL; - new_status_descr = GG_STATUS_NOT_AVAIL_DESCR; - } else { - new_status = GG_STATUS_AVAIL; - new_status_descr = GG_STATUS_AVAIL_DESCR; - purple_debug_info("gg", - "ggp_set_status: unknown status requested (status_id=%s)\n", - status_id); - } - - new_msg = purple_status_get_attr_string(status, "message"); - - if(new_msg) { - /* - char *tmp = purple_markup_strip_html(new_msg); - *msg = charset_convert(tmp, "UTF-8", "CP1250"); - g_free(tmp); - */ - *msg = purple_markup_strip_html(new_msg); - - return new_status_descr; - } else { - *msg = NULL; - return new_status; - } -} - -static void ggp_set_status(PurpleAccount *account, PurpleStatus *status) -{ - PurpleConnection *gc; - GGPInfo *info; - int new_status; - char *new_msg = NULL; - - if (!purple_status_is_active(status)) - return; - - gc = purple_account_get_connection(account); - info = gc->proto_data; - - new_status = ggp_to_gg_status(status, &new_msg); - - if (!info->status_broadcasting) - new_status = new_status|GG_STATUS_FRIENDS_MASK; - - if (new_msg == NULL) { - gg_change_status(info->session, new_status); - } else { - gg_change_status_descr(info->session, new_status, new_msg); - g_free(new_msg); - } - - ggp_status_fake_to_self(account); - -} - -static void ggp_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) -{ - PurpleAccount *account; - GGPInfo *info = gc->proto_data; - const gchar *name = purple_buddy_get_name(buddy); - - gg_add_notify(info->session, ggp_str_to_uin(name)); - - account = purple_connection_get_account(gc); - if (purple_strequal(purple_account_get_username(account), name)) { - ggp_status_fake_to_self(account); - } -} - -static void ggp_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, - PurpleGroup *group) -{ - GGPInfo *info = gc->proto_data; - - gg_remove_notify(info->session, ggp_str_to_uin(purple_buddy_get_name(buddy))); -} - -static void ggp_join_chat(PurpleConnection *gc, GHashTable *data) -{ - GGPInfo *info = gc->proto_data; - GGPChat *chat; - char *chat_name; - GList *l; - PurpleConversation *conv; - PurpleAccount *account = purple_connection_get_account(gc); - - chat_name = g_hash_table_lookup(data, "name"); - - if (chat_name == NULL) - return; - - purple_debug_info("gg", "joined %s chat\n", chat_name); - - for (l = info->chats; l != NULL; l = l->next) { - chat = l->data; - - if (chat != NULL && g_utf8_collate(chat->name, chat_name) == 0) { - purple_notify_error(gc, _("Chat error"), - _("This chat name is already in use"), NULL); - return; - } - } - - ggp_confer_add_new(gc, chat_name); - conv = serv_got_joined_chat(gc, info->chats_count, chat_name); - purple_conv_chat_add_user(PURPLE_CONV_CHAT(conv), - purple_account_get_username(account), NULL, - PURPLE_CBFLAGS_NONE, TRUE); -} - -static char *ggp_get_chat_name(GHashTable *data) { - return g_strdup(g_hash_table_lookup(data, "name")); -} - -static int ggp_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags) -{ - PurpleConversation *conv; - GGPInfo *info = gc->proto_data; - GGPChat *chat = NULL; - GList *l; - /* char *msg, *plain; */ - gchar *msg; - uin_t *uins; - int count = 0; - - if ((conv = purple_find_chat(gc, id)) == NULL) - return -EINVAL; - - for (l = info->chats; l != NULL; l = l->next) { - chat = l->data; - - if (g_utf8_collate(chat->name, conv->name) == 0) { - break; - } - - chat = NULL; - } - - if (chat == NULL) { - purple_debug_error("gg", - "ggp_chat_send: Hm... that's strange. No such chat?\n"); - return -EINVAL; - } - - uins = g_new0(uin_t, g_list_length(chat->participants)); - - for (l = chat->participants; l != NULL; l = l->next) { - uin_t uin = GPOINTER_TO_INT(l->data); - - uins[count++] = uin; - } - - /* - plain = purple_unescape_html(message); - msg = charset_convert(plain, "UTF-8", "CP1250"); - g_free(plain); - */ - msg = purple_unescape_html(message); - gg_send_message_confer(info->session, GG_CLASS_CHAT, count, uins, - (unsigned char *)msg); - g_free(msg); - g_free(uins); - - serv_got_chat_in(gc, id, - purple_account_get_username(purple_connection_get_account(gc)), - flags, message, time(NULL)); - - return 0; -} - -static void ggp_keepalive(PurpleConnection *gc) -{ - GGPInfo *info = gc->proto_data; - - /* purple_debug_info("gg", "Keeping connection alive....\n"); */ - - if (gg_ping(info->session) < 0) { - purple_debug_info("gg", "Not connected to the server " - "or gg_session is not correct\n"); - purple_connection_error_reason (gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Not connected to the server")); - } -} - -static GList *ggp_actions(PurplePlugin *plugin, gpointer context) -{ - GList *m = NULL; - PurplePluginAction *act; - - act = purple_plugin_action_new(_("Find buddies..."), - ggp_find_buddies); - m = g_list_append(m, act); - - act = purple_plugin_action_new(_("Change status broadcasting"), - ggp_action_change_status_broadcasting); - m = g_list_append(m, act); - - m = g_list_append(m, NULL); - - act = purple_plugin_action_new(_("Save buddylist to file..."), - ggp_action_buddylist_save); - m = g_list_append(m, act); - - act = purple_plugin_action_new(_("Load buddylist from file..."), - ggp_action_buddylist_load); - m = g_list_append(m, act); - - return m; -} - -static gboolean ggp_offline_message(const PurpleBuddy *buddy) -{ - return TRUE; -} - -static gboolean ggp_load(PurplePlugin *plugin) -{ - purple_debug_info("gg", "Loading Gadu-Gadu protocol plugin with " - "libgadu %s...\n", gg_libgadu_version()); - - gg_is_gpl_compliant(); - - return TRUE; -} - -static PurplePluginProtocolInfo prpl_info = -{ - OPT_PROTO_IM_IMAGE, - NULL, /* user_splits */ - NULL, /* protocol_options */ - {"png", 32, 32, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ - ggp_list_icon, /* list_icon */ - NULL, /* list_emblem */ - ggp_status_text, /* status_text */ - ggp_tooltip_text, /* tooltip_text */ - ggp_status_types, /* status_types */ - ggp_blist_node_menu, /* blist_node_menu */ - ggp_chat_info, /* chat_info */ - NULL, /* chat_info_defaults */ - ggp_login, /* login */ - ggp_close, /* close */ - ggp_send_im, /* send_im */ - NULL, /* set_info */ - ggp_send_typing, /* send_typing */ - ggp_get_info, /* get_info */ - ggp_set_status, /* set_away */ - NULL, /* set_idle */ - NULL, /* change_passwd */ - ggp_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - ggp_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - ggp_add_deny, /* add_deny */ - NULL, /* rem_permit */ - ggp_rem_deny, /* rem_deny */ - NULL, /* set_permit_deny */ - ggp_join_chat, /* join_chat */ - NULL, /* reject_chat */ - ggp_get_chat_name, /* get_chat_name */ - NULL, /* chat_invite */ - NULL, /* chat_leave */ - NULL, /* chat_whisper */ - ggp_chat_send, /* chat_send */ - ggp_keepalive, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - NULL, /* get_cb_away */ - NULL, /* alias_buddy */ - NULL, /* group_buddy */ - NULL, /* rename_group */ - NULL, /* buddy_free */ - NULL, /* convo_closed */ - NULL, /* normalize */ - NULL, /* set_buddy_icon */ - NULL, /* remove_group */ - NULL, /* get_cb_real_name */ - NULL, /* set_chat_topic */ - NULL, /* find_blist_chat */ - NULL, /* roomlist_get_list */ - NULL, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - NULL, /* can_receive_file */ - NULL, /* send_file */ - NULL, /* new_xfer */ - ggp_offline_message, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - NULL, /* send_attention */ - NULL, /* get_attention_types */ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* can_do_media */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - NULL, /* add_buddy_with_invite */ - NULL, /* add_buddies_with_invite */ - NULL, /* get_cb_alias */ - NULL, /* chat_can_receive_file */ - NULL, /* chat_send_file */ -}; - -static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, /* magic */ - PURPLE_MAJOR_VERSION, /* major_version */ - PURPLE_MINOR_VERSION, /* minor_version */ - PURPLE_PLUGIN_PROTOCOL, /* plugin type */ - NULL, /* ui_requirement */ - 0, /* flags */ - NULL, /* dependencies */ - PURPLE_PRIORITY_DEFAULT, /* priority */ - - "prpl-gg", /* id */ - "Gadu-Gadu", /* name */ - DISPLAY_VERSION, /* version */ - - N_("Gadu-Gadu Protocol Plugin"), /* summary */ - N_("Polish popular IM"), /* description */ - "boler@sourceforge.net", /* author */ - PURPLE_WEBSITE, /* homepage */ - - ggp_load, /* load */ - NULL, /* unload */ - NULL, /* destroy */ - - NULL, /* ui_info */ - &prpl_info, /* extra_info */ - NULL, /* prefs_info */ - ggp_actions, /* actions */ - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -purple_gg_debug_handler(int level, const char * format, va_list args) -{ - PurpleDebugLevel purple_level; - char msgbuff[1000]; - int ret; - - /* Don't use glib's printf family, since it might not support - * system-specific formatting modifiers (like %Iu for size on win32). */ - ret = vsnprintf(msgbuff, sizeof(msgbuff) / sizeof(char), format, args); - - if (ret <= 0) { - purple_debug_fatal("gg", - "failed to printf the following message: %s", - format ? format : "(null)\n"); - - return; - } - - /* This is pretty pointless since the GG_DEBUG levels don't correspond to - * the purple ones */ - switch (level) { - case GG_DEBUG_FUNCTION: - purple_level = PURPLE_DEBUG_INFO; - break; - case GG_DEBUG_MISC: - case GG_DEBUG_NET: - case GG_DEBUG_DUMP: - case GG_DEBUG_TRAFFIC: - default: - purple_level = PURPLE_DEBUG_MISC; - break; - } - - purple_debug(purple_level, "gg", "%s", msgbuff); -} - -static void init_plugin(PurplePlugin *plugin) -{ - PurpleAccountOption *option; - GList *encryption_options = NULL; - - option = purple_account_option_string_new(_("Nickname"), - "nick", _("Gadu-Gadu User")); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_string_new(_("GG server"), - "gg_server", ""); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - -#define ADD_VALUE(list, desc, v) { \ - PurpleKeyValuePair *kvp = g_new0(PurpleKeyValuePair, 1); \ - kvp->key = g_strdup((desc)); \ - kvp->value = g_strdup((v)); \ - list = g_list_append(list, kvp); \ -} - - ADD_VALUE(encryption_options, _("Don't use encryption"), "none"); - ADD_VALUE(encryption_options, _("Use encryption if available"), - "opportunistic_tls"); -#if 0 - /* TODO */ - ADD_VALUE(encryption_options, _("Require encryption"), "require_tls"); -#endif - - option = purple_account_option_list_new(_("Connection security"), - "encryption", encryption_options); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - my_protocol = plugin; - - gg_debug_handler = purple_gg_debug_handler; -} - -PURPLE_INIT_PLUGIN(gg, init_plugin, info); - -/* vim: set ts=8 sts=0 sw=8 noet: */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/gg.h --- a/libpurple/protocols/gg/gg.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -/** - * @file gg.h - * - * purple - * - * Copyright (C) 2005 Bartosz Oler - * - * 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_GG_H -#define _PURPLE_GG_H - -#include -#include "internal.h" -#include "dnsquery.h" -#include "search.h" -#include "connection.h" - - -#define PUBDIR_RESULTS_MAX 20 - - -typedef struct -{ - char *name; - GList *participants; - -} GGPChat; - -typedef void (*GGPTokenCallback)(PurpleConnection *); - -typedef struct -{ - char *id; - char *data; - unsigned int size; - - struct gg_http *req; - guint inpa; - - GGPTokenCallback cb; - -} GGPToken; - -typedef struct { - - struct gg_session *session; - GGPToken *token; - GList *chats; - GGPSearches *searches; - int chats_count; - GList *pending_richtext_messages; - GHashTable *pending_images; - gboolean status_broadcasting; //When TRUE status is visible to all, when FALSE status is visible only to friends. - PurpleDnsQueryData *dns_query; -} GGPInfo; - -#endif /* _PURPLE_GG_H */ - -/* vim: set ts=8 sts=0 sw=8 noet: */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/COPYING --- a/libpurple/protocols/gg/lib/COPYING Wed Jun 04 23:12:27 2025 -0500 +++ /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. - - - Copyright (C) - - 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. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/common.c --- a/libpurple/protocols/gg/lib/common.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,882 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2002 Wojtek Kaniewski - * Robert J. Woźny - * - * 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 -#include -#include -#include -#include -#include - -#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: - */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/config.h --- a/libpurple/protocols/gg/lib/config.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -/* Local libgadu configuration file. */ - -#undef printf - -/* libpurple's config */ -#include - -#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 -#ifndef _WIN32 -# 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 . */ -#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 . */ -#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 . */ -#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 diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/dcc.c --- a/libpurple/protocols/gg/lib/dcc.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1410 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2008 Wojtek Kaniewski - * Tomasz Chiliński - * Adam Wysocki - * - * 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 -#include -#include -#include - -#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: - */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/dcc7.c --- a/libpurple/protocols/gg/lib/dcc7.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1657 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2010 Wojtek Kaniewski - * Tomasz Chiliński - * Adam Wysocki - * Bartłomiej Zimoń - * - * Thanks to Jakub Zawadzki - * - * 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 -#include -#include -#include -#include - -#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->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); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/debug.c --- a/libpurple/protocols/gg/lib/debug.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,397 +0,0 @@ -/* - * (C) Copyright 2001-2006 Wojtek Kaniewski - * Robert J. Woźny - * Arkadiusz Miśkiewicz - * Tomasz Chiliński - * Adam Wysocki - * - * 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 -#include -#include -#include -#include - -#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 diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/debug.h --- a/libpurple/protocols/gg/lib/debug.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,27 +0,0 @@ -/* - * (C) Copyright 2009 Wojtek Kaniewski - * - * 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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/deflate.c --- a/libpurple/protocols/gg/lib/deflate.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,231 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2011 Bartosz Brachaczek - * - * 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 -#include - -#include "libgadu.h" -#include "internal.h" -#include "deflate.h" - -#ifdef GG_CONFIG_HAVE_ZLIB -#include -#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; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/deflate.h --- a/libpurple/protocols/gg/lib/deflate.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2011 Bartosz Brachaczek - * - * 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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/encoding.c --- a/libpurple/protocols/gg/lib/encoding.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,272 +0,0 @@ -/* - * (C) Copyright 2008-2009 Jakub Zawadzki - * Wojtek Kaniewski - * - * 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 -#include - -#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; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/encoding.h --- a/libpurple/protocols/gg/lib/encoding.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -/* - * (C) Copyright 2008-2009 Jakub Zawadzki - * Wojtek Kaniewski - * - * 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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/endian.c --- a/libpurple/protocols/gg/lib/endian.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,109 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2010 Wojtek Kaniewski - * Robert J. Woźny - * Arkadiusz Miśkiewicz - * Tomasz Chiliński - * Adam Wysocki - * - * 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 -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/events.c --- a/libpurple/protocols/gg/lib/events.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1901 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2006 Wojtek Kaniewski - * Robert J. Woźny - * Arkadiusz Miśkiewicz - * Adam Wysocki - * - * 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 -#include -#include -#include -#include -#ifdef GG_CONFIG_HAVE_GNUTLS -# include -# include -#endif -#ifdef GG_CONFIG_HAVE_OPENSSL -# include -# include -# include -#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: - */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/fileio.h --- a/libpurple/protocols/gg/lib/fileio.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2011 Bartosz Brachaczek - * - * 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 -#include -#include - -#ifdef _WIN32 -# include -# 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 -# endif -# include -# define gg_file_close close -#endif - -#ifndef S_IWUSR -# define S_IWUSR S_IWRITE -#endif - -#endif /* LIBGADU_FILEIO_H */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/handlers.c --- a/libpurple/protocols/gg/lib/handlers.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2881 +0,0 @@ -/* - * (C) Copyright 2001-2011 Wojtek Kaniewski - * Robert J. Woźny - * Arkadiusz Miśkiewicz - * Tomasz Chiliński - * Adam Wysocki - * - * 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 - -#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 -#include -#include -#include - -/* 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; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/http.c --- a/libpurple/protocols/gg/lib/http.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,600 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2002 Wojtek Kaniewski - * - * 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 -#include -#include -#include - -#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); - if (h->header) { - 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"); - if (h->header) { - 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); - if (h->body) { - 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); - if (h->body) { - 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: - */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/internal.h --- a/libpurple/protocols/gg/lib/internal.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,266 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2009 Jakub Zawadzki - * - * 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 - -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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/libgadu.c --- a/libpurple/protocols/gg/lib/libgadu.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3082 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2010 Wojtek Kaniewski - * Robert J. Woźny - * Arkadiusz Miśkiewicz - * Tomasz Chiliński - * Adam Wysocki - * - * 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 -#include -#include -#include -#include -#ifdef GG_CONFIG_HAVE_GNUTLS -# include -#endif -#ifdef GG_CONFIG_HAVE_OPENSSL -# include -# include -#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: - */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/libgadu.h --- a/libpurple/protocols/gg/lib/libgadu.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2639 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2009 Wojtek Kaniewski - * Robert J. Woźny - * Arkadiusz Miśkiewicz - * Tomasz Chiliński - * Piotr Wysocki - * Dawid Jarosz - * Jakub Zawadzki - * - * 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 -#include -#include - -/** \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 . */ -#undef GG_CONFIG_HAVE_STDINT_H - -/* Defined if uintX_t types are defined in . */ -#undef GG_CONFIG_HAVE_INTTYPES_H - -/* Defined if uintX_t types are defined in . */ -#undef GG_CONFIG_HAVE_SYS_INTTYPES_H - -/* Defined if uintX_t types are defined in . */ -#undef GG_CONFIG_HAVE_SYS_INT_TYPES_H - -/* Defined if uintX_t types are defined in . */ -#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 -#endif - -#ifdef GG_CONFIG_HAVE_STDINT_H -#include -#else -# ifdef GG_CONFIG_HAVE_INTTYPES_H -# include -# else -# ifdef GG_CONFIG_HAVE_SYS_INTTYPES_H -# include -# else -# ifdef GG_CONFIG_HAVE_SYS_INT_TYPES_H -# include -# else -# ifdef GG_CONFIG_HAVE_SYS_TYPES_H -# include -# else - -/* ISO C 9X: 7.18 Integer types */ - -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 -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: - */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/message.c --- a/libpurple/protocols/gg/lib/message.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1016 +0,0 @@ -/* - * (C) Copyright 2001-2010 Wojtek Kaniewski - * - * 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 -#include -#include -#include -#include -#include - -#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[] = ""; - const size_t span_len = 75; - const char img_fmt[] = ""; - 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, "", 4); - - if ((old_attr & GG_FONT_ITALIC) != 0) - gg_append(dst, &len, "", 4); - - if ((old_attr & GG_FONT_BOLD) != 0) - gg_append(dst, &len, "", 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, "", 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, "", 3); - - if ((attr & GG_FONT_ITALIC) != 0) - gg_append(dst, &len, "", 3); - - if ((attr & GG_FONT_UNDERLINE) != 0) - gg_append(dst, &len, "", 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ć . */ - - 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, "&", 5); - break; - case '<': - gg_append(dst, &len, "<", 4); - break; - case '>': - gg_append(dst, &len, ">", 4); - break; - case '\'': - gg_append(dst, &len, "'", 6); - break; - case '\"': - gg_append(dst, &len, """, 6); - break; - case '\n': - gg_append(dst, &len, "
", 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, "
", 4); - - if ((old_attr & GG_FONT_ITALIC) != 0) - gg_append(dst, &len, "
", 4); - - if ((old_attr & GG_FONT_BOLD) != 0) - gg_append(dst, &len, "
", 4); - - if (in_span) - gg_append(dst, &len, "", 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, "> 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, "", 3) == 0) { - in_bold++; - attr_flag |= GG_FONT_BOLD; - } else if (strncmp(tag, "", 4) == 0) { - if (in_bold > 0) { - in_bold--; - if (in_bold == 0) - attr_flag &= ~GG_FONT_BOLD; - } - } else if (strncmp(tag, "", 3) == 0) { - in_italic++; - attr_flag |= GG_FONT_ITALIC; - } else if (strncmp(tag, "", 4) == 0) { - if (in_italic > 0) { - in_italic--; - if (in_italic == 0) - attr_flag &= ~GG_FONT_ITALIC; - } - } else if (strncmp(tag, "", 3) == 0) { - in_underline++; - attr_flag |= GG_FONT_UNDERLINE; - } else if (strncmp(tag, "", 4) == 0) { - if (in_underline > 0) { - in_underline--; - if (in_underline == 0) - attr_flag &= ~GG_FONT_UNDERLINE; - } - } else if (strncmp(tag, " 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, "", 6); - - for (i = 0; i < (size_t)text_len; i++) { - char c = text[i]; - if (c == '<') - gg_append(dst, &dst_len, "<", 4); - else if (c == '>') - gg_append(dst, &dst_len, ">", 4); - else if (c == '&') - gg_append(dst, &dst_len, "&", 5); - else if (c == '"') - gg_append(dst, &dst_len, """, 6); - else if (c == '\'') - gg_append(dst, &dst_len, "'", 6); - else if (c == '\n') - gg_append(dst, &dst_len, "
", 4); - else if (c == '\r') - continue; - else if (c == '\xc2' && text[i + 1] == '\xa0') { - gg_append(dst, &dst_len, " ", 6); - i++; - } else { - if (dst) - dst[dst_len] = c; - dst_len++; - } - } - - gg_append(dst, &dst_len, "
", 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; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/message.h --- a/libpurple/protocols/gg/lib/message.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,62 +0,0 @@ -/* - * (C) Copyright 2009 Wojtek Kaniewski - * - * 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 -#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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/network.c --- a/libpurple/protocols/gg/lib/network.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,310 +0,0 @@ -/* - * (C) Copyright 2011 Wojtek Kaniewski - * - * 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 -#include - -#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 - * 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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/network.h --- a/libpurple/protocols/gg/lib/network.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,125 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2002 Wojtek Kaniewski - * Robert J. Woźny - * - * 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 -# include -# include -# include -# include -/* 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 -# include -# include -# include -# include -# include -# include -# ifndef FIONBIO -# include -# 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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/obsolete.c --- a/libpurple/protocols/gg/lib/obsolete.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,250 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2003 Wojtek Kaniewski - * - * 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 -#include - -#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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/packets.pb-c.c --- a/libpurple/protocols/gg/lib/packets.pb-c.c Wed Jun 04 23:12:27 2025 -0500 +++ /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] */ -}; diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/packets.pb-c.h --- a/libpurple/protocols/gg/lib/packets.pb-c.h Wed Jun 04 23:12:27 2025 -0500 +++ /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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/protobuf-c.c --- a/libpurple/protocols/gg/lib/protobuf-c.c Wed Jun 04 23:12:27 2025 -0500 +++ /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 /* for malloc, free */ -#include /* 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< 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; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/protobuf-c.h --- a/libpurple/protocols/gg/lib/protobuf-c.h Wed Jun 04 23:12:27 2025 -0500 +++ /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 -#include -#include -#include - -#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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/protobuf.c --- a/libpurple/protocols/gg/lib/protobuf.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,153 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2012 Tomek Wasilczyk - * - * 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; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/protobuf.h --- a/libpurple/protocols/gg/lib/protobuf.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2012 Tomek Wasilczyk - * - * 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 -#include -#include - -#include "config.h" -#ifdef GG_CONFIG_HAVE_PROTOBUF_C -#include -#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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/protocol.h --- a/libpurple/protocols/gg/lib/protocol.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,411 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2009-2010 Jakub Zawadzki - * Bartłomiej Zimoń - * Wojtek Kaniewski - * - * 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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/pubdir.c --- a/libpurple/protocols/gg/lib/pubdir.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,888 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2001-2006 Wojtek Kaniewski - * Dawid Jarosz - * Adam Wysocki - * - * 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 -#include -#include -#include - -#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: - */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/pubdir50.c --- a/libpurple/protocols/gg/lib/pubdir50.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,555 +0,0 @@ -/* - * (C) Copyright 2003 Wojtek Kaniewski - * - * 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 -#include -#include -#include - -#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: - */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/resolver.c --- a/libpurple/protocols/gg/lib/resolver.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1132 +0,0 @@ -/* - * (C) Copyright 2001-2009 Wojtek Kaniewski - * Robert J. Woźny - * Arkadiusz Miśkiewicz - * Tomasz Chiliński - * Adam Wysocki - * - * 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 -#include -#include - -#include "strman.h" -#include "network.h" -#include "config.h" -#include "libgadu.h" -#include "resolver.h" -#include "session.h" - -#ifdef GG_CONFIG_HAVE_FORK -#include -#include -#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 - -/** - * \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" — wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor gniazda - * - \c "void **priv_data" — wskaźnik na zmienną, gdzie można umieścić - * wskaźnik do prywatnych danych na potrzeby rozwiązywania nazwy - * - \c "const char *name" — nazwa serwera do rozwiązania - * - * Parametry funkcji zwalniającej zasoby wyglądają następująco: - * - \c "void **priv_data" — wskaźnik na zmienną przechowującą wskaźnik - * do prywatnych danych, należy go ustawić na \c NULL po zakończeniu - * - \c "int force" — 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 -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/resolver.h --- a/libpurple/protocols/gg/lib/resolver.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,28 +0,0 @@ -/* - * (C) Copyright 2008 Wojtek Kaniewski - * - * 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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/session.h --- a/libpurple/protocols/gg/lib/session.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,67 +0,0 @@ -/* - * (C) Copyright 2008-2010 Wojtek Kaniewski - * - * 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 -#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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/sha1.c --- a/libpurple/protocols/gg/lib/sha1.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,382 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2007 Wojtek Kaniewski - * - * Public domain SHA-1 implementation by Steve Reid - * - * 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 -#include - -#include "libgadu.h" -#include "internal.h" -#include "fileio.h" -#include "config.h" - -/** \cond ignore */ - -#ifdef GG_CONFIG_HAVE_OPENSSL - -#include - -#elif defined(GG_CONFIG_HAVE_GNUTLS) - -#include -#include - -#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 -100% Public Domain - -Modified by Wojtek Kaniewski 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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/strman.h --- a/libpurple/protocols/gg/lib/strman.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2011 Bartosz Brachaczek - * - * 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 -#include -#include -#ifndef _MSC_VER -# include -#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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/tvbuff.c --- a/libpurple/protocols/gg/lib/tvbuff.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,607 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2012 Tomek Wasilczyk - * - * 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 -#include - -#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]); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/tvbuff.h --- a/libpurple/protocols/gg/lib/tvbuff.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2012 Tomek Wasilczyk - * - * 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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/tvbuilder.c --- a/libpurple/protocols/gg/lib/tvbuilder.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,426 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2012 Tomek Wasilczyk - * - * 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 -#include - -#include "tvbuilder.h" - -#include "internal.h" -#include "fileio.h" - -#include - -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); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/lib/tvbuilder.h --- a/libpurple/protocols/gg/lib/tvbuilder.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,50 +0,0 @@ -/* $Id$ */ - -/* - * (C) Copyright 2012 Tomek Wasilczyk - * - * 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 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/search.c --- a/libpurple/protocols/gg/search.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,223 +0,0 @@ -/** - * @file search.c - * - * purple - * - * Copyright (C) 2005 Bartosz Oler - * - * 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 - -#include "gg-utils.h" -#include "search.h" - - -/* GGPSearchForm *ggp_search_form_new() {{{ */ -GGPSearchForm *ggp_search_form_new(GGPSearchType st) -{ - GGPSearchForm *form; - - form = g_new0(GGPSearchForm, 1); - - form->search_type = st; - form->window = NULL; - form->user_data = NULL; - form->seq = 0; - form->page_number = 0; - form->page_size = 0; - - form->uin = NULL; - form->lastname = NULL; - form->firstname = NULL; - form->nickname = NULL; - form->city = NULL; - form->birthyear = NULL; - form->gender = NULL; - form->active = NULL; - - return form; -} -/* }}} */ - -/* void ggp_search_form_destroy(GGPSearchForm *form) {{{ */ -void ggp_search_form_destroy(GGPSearchForm *form) -{ - g_return_if_fail(form != NULL); - - form->window = NULL; - form->user_data = NULL; - form->seq = 0; - form->page_number = 0; - form->page_size = 0; - - g_free(form->uin); - g_free(form->lastname); - g_free(form->firstname); - g_free(form->nickname); - g_free(form->city); - g_free(form->birthyear); - g_free(form->gender); - g_free(form->active); - g_free(form); -} -/* }}} */ - -/* void ggp_search_add(GGPSearches *searches, guint32 seq, GGPSearchForm *form) {{{ */ -void ggp_search_add(GGPSearches *searches, guint32 seq, GGPSearchForm *form) -{ - guint32 *tmp; - - g_return_if_fail(searches != NULL); - g_return_if_fail(form != NULL); - - tmp = g_new0(guint32, 1); - *tmp = seq; - form->seq = seq; - - g_hash_table_insert(searches, tmp, form); -} -/* }}} */ - -/* void ggp_search_remove(GGPSearches *searches, guint32 seq) {{{ */ -void ggp_search_remove(GGPSearches *searches, guint32 seq) -{ - g_return_if_fail(searches != NULL); - - g_hash_table_remove(searches, &seq); -} -/* }}} */ - -/* GGPSearchForm *ggp_search_get(GGPSearches *searches, seq) {{{ */ -GGPSearchForm *ggp_search_get(GGPSearches *searches, guint32 seq) -{ - g_return_val_if_fail(searches != NULL, NULL); - - return g_hash_table_lookup(searches, &seq); -} -/* }}} */ - -/* GGPSearches *ggp_search_new() {{{ */ -GGPSearches *ggp_search_new(void) -{ - GGPSearches *searches; - - searches = g_hash_table_new_full(g_int_hash, g_int_equal, - g_free, NULL); - - return searches; -} -/* }}} */ - -/* void ggp_search_destroy(GGPSearches *searches) {{{ */ -void ggp_search_destroy(GGPSearches *searches) -{ - g_return_if_fail(searches != NULL); - - g_hash_table_destroy(searches); -} -/* }}} */ - -/* guint32 ggp_search_start(PurpleConnection *gc, GGPSearchForm *form) {{{ */ -guint32 ggp_search_start(PurpleConnection *gc, GGPSearchForm *form) -{ - GGPInfo *info = gc->proto_data; - gg_pubdir50_t req; - guint seq, offset; - gchar *tmp; - - purple_debug_info("gg", "It's time to perform a search...\n"); - - if ((req = gg_pubdir50_new(GG_PUBDIR50_SEARCH)) == NULL) { - purple_debug_error("gg", - "ggp_bmenu_show_details: Unable to create req variable.\n"); - return 0; - } - - if (form->uin != NULL) { - purple_debug_info("gg", " uin: %s\n", form->uin); - gg_pubdir50_add(req, GG_PUBDIR50_UIN, form->uin); - } else { - if (form->lastname != NULL) { - purple_debug_info("gg", " lastname: %s\n", form->lastname); - gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, form->lastname); - } - - if (form->firstname != NULL) { - purple_debug_info("gg", " firstname: %s\n", form->firstname); - gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, form->firstname); - } - - if (form->nickname != NULL) { - purple_debug_info("gg", " nickname: %s\n", form->nickname); - gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, form->nickname); - } - - if (form->city != NULL) { - purple_debug_info("gg", " city: %s\n", form->city); - gg_pubdir50_add(req, GG_PUBDIR50_CITY, form->city); - } - - if (form->birthyear != NULL) { - purple_debug_info("gg", " birthyear: %s\n", form->birthyear); - gg_pubdir50_add(req, GG_PUBDIR50_BIRTHYEAR, form->birthyear); - } - - if (form->gender != NULL) { - purple_debug_info("gg", " gender: %s\n", form->gender); - gg_pubdir50_add(req, GG_PUBDIR50_GENDER, form->gender); - } - - if (form->active != NULL) { - purple_debug_info("gg", " active: %s\n", form->active); - gg_pubdir50_add(req, GG_PUBDIR50_ACTIVE, form->active); - } - } - - offset = form->page_size * form->page_number; - purple_debug_info("gg", "page number: %u, page size: %u, offset: %u\n", - form->page_number, form->page_size, offset); - tmp = g_strdup_printf("%u", offset); - gg_pubdir50_add(req, GG_PUBDIR50_START, tmp); - g_free(tmp); - - if ((seq = gg_pubdir50(info->session, req)) == 0) { - purple_debug_warning("gg", "ggp_bmenu_show_details: Search failed.\n"); - gg_pubdir50_free(req); - return 0; - } - - purple_debug_info("gg", "search sequence number: %d\n", seq); - gg_pubdir50_free(req); - - return seq; -} -/* }}} */ - -/* char *ggp_search_get_result(gg_pubdir50_t res, int num, const char *field) {{{ */ -char *ggp_search_get_result(gg_pubdir50_t res, int num, const char *field) -{ - char *tmp; - - tmp = g_strdup(gg_pubdir50_get(res, num, field)); - - return (tmp == NULL) ? g_strdup("") : tmp; -} -/* }}} */ - - -/* vim: set ts=8 sts=0 sw=8 noet: */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/gg/search.h --- a/libpurple/protocols/gg/search.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,152 +0,0 @@ -/** - * @file search.h - * - * purple - * - * Copyright (C) 2005 Bartosz Oler - * - * 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_GG_SEARCH_H -#define _PURPLE_GG_SEARCH_H - -#include "connection.h" - -#include -#include "gg.h" - - -typedef enum { - GGP_SEARCH_TYPE_INFO, - GGP_SEARCH_TYPE_FULL - -} GGPSearchType; - -typedef struct { - - char *uin; - char *lastname; - char *firstname; - char *nickname; - char *city; - char *birthyear; - char *gender; - char *active; - - GGPSearchType search_type; - guint32 seq; - guint16 page_number; - guint16 page_size; /* how many contacts fits into one page of results */ - - void *user_data; - void *window; -} GGPSearchForm; - -typedef GHashTable GGPSearches; - - -/** - * Create a new GGPSearchForm structure, and set the fields - * to the sane defaults. - * - * @return Newly allocated GGPSearchForm. - */ -GGPSearchForm * -ggp_search_form_new(GGPSearchType st); - -/** - * Destroy a Search Form. - * - * @param form Search Form to destroy. - */ -void -ggp_search_form_destroy(GGPSearchForm *form); - -/** - * Add a search to the list of searches. - * - * @param searches The list of searches. - * @param seq Search (form) ID number. - * @param form The search form to add. - */ -void -ggp_search_add(GGPSearches *searches, guint32 seq, GGPSearchForm *form); - -/** - * Remove a search from the list. - * - * If you want to destory the search completely also call: - * ggp_search_form_destroy(). - * - * @param searches The list of searches. - * @param seq ID number of the search. - */ -void -ggp_search_remove(GGPSearches *searches, guint32 seq); - -/** - * Return the search with the specified ID. - * - * @param searches The list of searches. - * @param seq ID number of the search. - */ -GGPSearchForm * -ggp_search_get(GGPSearches *searches, guint32 seq); - -/** - * Create a new GGPSearches structure. - * - * @return GGPSearches instance. - */ -GGPSearches * -ggp_search_new(void); - -/** - * Destroy GGPSearches instance. - * - * @param searches GGPSearches instance. - */ -void -ggp_search_destroy(GGPSearches *searches); - -/** - * Initiate a search in the public directory. - * - * @param gc PurpleConnection. - * @param form Filled in GGPSearchForm. - * - * @return Sequence number of a search or 0 if an error occurred. - */ -guint32 -ggp_search_start(PurpleConnection *gc, GGPSearchForm *form); - -/* - * Return converted to the UTF-8 value of the specified field. - * - * @param res Public directory look-up result. - * @param num Id of the record. - * @param fileld Name of the field. - * - * @return UTF-8 encoded value of the field. - */ -char * -ggp_search_get_result(gg_pubdir50_t res, int num, const char *field); - - -#endif /* _PURPLE_GG_SEARCH_H */ - -/* vim: set ts=8 sts=0 sw=8 noet: */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/Makefile.am --- a/libpurple/protocols/novell/Makefile.am Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -EXTRA_DIST = \ - Makefile.mingw - -pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) - -NOVELLSOURCES = \ - nmfield.h \ - nmfield.c \ - nmconn.h \ - nmconn.c \ - nmconference.h \ - nmconference.c \ - nmcontact.h \ - nmcontact.c \ - nmevent.h \ - nmevent.c \ - nmmessage.h \ - nmmessage.c \ - nmrequest.h \ - nmrequest.c \ - nmrtf.h \ - nmrtf.c \ - nmuser.h \ - nmuser.c \ - nmuserrecord.h \ - nmuserrecord.c \ - novell.c - -AM_CFLAGS = $(st) - -libnovell_la_LDFLAGS = -module -avoid-version - -if STATIC_NOVELL - -st = -DPURPLE_STATIC_PRPL -noinst_LTLIBRARIES = libnovell.la -libnovell_la_SOURCES = $(NOVELLSOURCES) -libnovell_la_CFLAGS = $(AM_CFLAGS) - -else - -st = -pkg_LTLIBRARIES = libnovell.la -libnovell_la_SOURCES = $(NOVELLSOURCES) -libnovell_la_LIBADD = $(GLIB_LIBS) - -endif - -AM_CPPFLAGS = \ - -I$(top_srcdir)/libpurple \ - -I$(top_builddir)/libpurple \ - $(DEBUG_CFLAGS) \ - $(GLIB_CFLAGS) diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/Makefile.mingw --- a/libpurple/protocols/novell/Makefile.mingw Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,88 +0,0 @@ -# -# Makefile.mingw -# -# Description: Makefile for win32 (mingw) version of libnovell -# - -PIDGIN_TREE_TOP := ../../.. -include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak - -TARGET = libnovell -TYPE = PLUGIN - -# Static or Plugin... -ifeq ($(TYPE),STATIC) - DEFINES += -DSTATIC - DLL_INSTALL_DIR = $(PURPLE_INSTALL_DIR) -else -ifeq ($(TYPE),PLUGIN) - DLL_INSTALL_DIR = $(PURPLE_INSTALL_PLUGINS_DIR) -endif -endif - -## -## INCLUDE PATHS -## -INCLUDE_PATHS += -I. \ - -I$(GTK_TOP)/include \ - -I$(GTK_TOP)/include/glib-2.0 \ - -I$(GTK_TOP)/lib/glib-2.0/include \ - -I$(PURPLE_TOP) \ - -I$(PURPLE_TOP)/win32 \ - -I$(PIDGIN_TREE_TOP) - -LIB_PATHS += -L$(GTK_TOP)/lib \ - -L$(PURPLE_TOP) - -## -## SOURCES, OBJECTS -## -C_SRC = \ - nmfield.c \ - nmconn.c \ - nmconference.c \ - nmcontact.c \ - nmevent.c \ - nmmessage.c \ - nmrequest.c \ - nmrtf.c \ - nmuser.c \ - nmuserrecord.c \ - novell.c - -OBJECTS = $(C_SRC:%.c=%.o) - -## -## LIBRARIES -## -LIBS = \ - -lglib-2.0 \ - -lws2_32 \ - -lintl \ - -lpurple - -include $(PIDGIN_COMMON_RULES) - -## -## TARGET DEFINITIONS -## -.PHONY: all install clean - -all: $(TARGET).dll - -install: all $(DLL_INSTALL_DIR) - cp $(TARGET).dll $(DLL_INSTALL_DIR) - -$(OBJECTS): $(PURPLE_CONFIG_H) - -$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS) - $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll - -## -## CLEAN RULES -## -clean: - rm -f $(OBJECTS) - rm -f $(TARGET).dll - -include $(PIDGIN_COMMON_TARGETS) diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmconference.c --- a/libpurple/protocols/novell/nmconference.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,232 +0,0 @@ -/* - * nmconference.c - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 -#include "nmconference.h" - -static int conf_count = 0; - -struct _NMConference -{ - - /* The conference identifier */ - char *guid; - - /* The list of participants for the conference */ - GSList *participants; - - /* Flags for the conference */ - guint32 flags; - - /* User defined data */ - gpointer data; - - /* Reference count for this object */ - int ref_count; - -}; - - -/******************************************************************************* - * Conference API -- see header file for comments - ******************************************************************************/ - -NMConference * -nm_create_conference(const char *guid) -{ - NMConference *conf = g_new0(NMConference, 1); - - if (guid) { - conf->guid = g_strdup(guid); - } else { - conf->guid = g_strdup(BLANK_GUID); - } - conf->ref_count = 1; - - purple_debug(PURPLE_DEBUG_INFO, "novell", - "Creating a conference %p, total=%d\n", - conf, conf_count++); - - return conf; -} - -void -nm_release_conference(NMConference * conference) -{ - GSList *node; - - g_return_if_fail(conference != NULL); - - purple_debug(PURPLE_DEBUG_INFO, "novell", - "In release conference %p, refs=%d\n", - conference, conference->ref_count); - if (--conference->ref_count == 0) { - - purple_debug(PURPLE_DEBUG_INFO, "novell", - "Releasing conference %p, total=%d\n", - conference, --conf_count); - - if (conference->guid) - g_free(conference->guid); - - if (conference->participants) { - for (node = conference->participants; node; node = node->next) { - if (node->data) { - NMUserRecord *user_record = node->data; - - nm_release_user_record(user_record); - node->data = NULL; - } - } - - g_slist_free(conference->participants); - } - - g_free(conference); - } -} - -gboolean -nm_conference_is_instantiated(NMConference * conference) -{ - if (conference == NULL) - return FALSE; - - return (strncmp(conference->guid, BLANK_GUID, CONF_GUID_END) != 0); -} - -int -nm_conference_get_participant_count(NMConference * conference) -{ - if (conference == NULL) - return 0; - - return g_slist_length(conference->participants); -} - -NMUserRecord * -nm_conference_get_participant(NMConference * conference, int index) -{ - if (conference == NULL) - return NULL; - - return (NMUserRecord *) g_slist_nth_data(conference->participants, index); -} - -void -nm_conference_add_participant(NMConference * conference, - NMUserRecord * user_record) -{ - if (conference == NULL || user_record == NULL) { - return; - } - - nm_user_record_add_ref(user_record); - conference->participants = g_slist_append(conference->participants, user_record); -} - -void -nm_conference_remove_participant(NMConference * conference, const char *dn) -{ - GSList *node, *element = NULL; - - if (conference == NULL || dn == NULL) { - return; - } - - for (node = conference->participants; node; node = node->next) { - NMUserRecord *user_record = node->data; - - if (user_record) { - if (nm_utf8_str_equal(dn, nm_user_record_get_dn(user_record))) { - element = node; - break; - } - } - } - - if (element) { - nm_release_user_record((NMUserRecord *) element->data); - element->data = NULL; - conference->participants = - g_slist_remove_link(conference->participants, element); - g_slist_free_1(element); - } -} - -void -nm_conference_add_ref(NMConference * conference) -{ - if (conference) - conference->ref_count++; -} - -void -nm_conference_set_flags(NMConference * conference, guint32 flags) -{ - if (conference) { - conference->flags = flags; - } -} - -void -nm_conference_set_guid(NMConference * conference, const char *guid) -{ - if (conference) { - - /* Release memory for old guid */ - if (conference->guid) { - g_free(conference->guid); - } - - /* Set the new guid */ - if (guid) - conference->guid = g_strdup(guid); - else - conference->guid = g_strdup(BLANK_GUID); - } -} - -void -nm_conference_set_data(NMConference * conference, gpointer data) -{ - if (conference == NULL) - return; - - conference->data = data; -} - -gpointer -nm_conference_get_data(NMConference * conference) -{ - if (conference == NULL) - return NULL; - - return conference->data; -} - -const char * -nm_conference_get_guid(NMConference * conference) -{ - if (conference == NULL) - return NULL; - - return conference->guid; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmconference.h --- a/libpurple/protocols/novell/nmconference.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,167 +0,0 @@ -/* - * nmconference.h - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 __NM_CONFERENCE_H__ -#define __NM_CONFERENCE_H__ - -typedef struct _NMConference NMConference; - -#include "nmuserrecord.h" - -/* A blank GUID -- represents an uninstatiated conference */ -#define BLANK_GUID "[00000000-00000000-00000000-0000-0000]" - -/* This is how much of the conference GUIDs to compare when testing - * to see if two conferences are the same. We cannot compare the - * entire GUID because the last part is the session count. - */ -#define CONF_GUID_END 27 - -/** - * Creates an conference object. - * - * The conference should be released by calling - * nm_release_conference - * - * @param guid The GUID for the conference. - * - * @return The new NMConference - */ -NMConference *nm_create_conference(const char *guid); - -/** - * Increments the reference count for the conference. - * - * The reference to the conference should be released - * by calling nm_release_conference - * - * @param conference The conference to reference - */ -void nm_conference_add_ref(NMConference * conference); - -/** - * Releases the resources associated with the conference - * if there are no more references to it, otherwise just - * decrements the reference count. - * - * @param conf The conference to release - * - */ -void nm_release_conference(NMConference * conf); - -/** - * Set the GUID for the conference. - * - * @param conference The conference - * @param guid The new conference GUID - * - */ -void nm_conference_set_guid(NMConference * conference, const char *guid); - -/** - * Return the GUID for the conference. - * - * @param conference The conference - * - * @return The GUID for the conference - */ -const char *nm_conference_get_guid(NMConference * conference); - -/** - * Add a participant to the conference. - * - * @param conference The conference - * @param user_record The user record to add as a participant - * - * @return - */ -void nm_conference_add_participant(NMConference * conference, - NMUserRecord * user_record); - -/** - * Remove a participant to the conference. - * - * @param conference The conference - * @param dn The dn of the participant to remove - * - */ -void nm_conference_remove_participant(NMConference * conference, const char *dn); - -/** - * Return the total number of participants in the conference. - * - * @param conference The conference - * - * @return The number of participants for the conference - * - */ -int nm_conference_get_participant_count(NMConference * conference); - -/** - * Return a participant given an index. - * - * @param conference The conference - * @param index The index of the participant to get - * - * @return The participant or NULL if the index is out of range. - * - */ -NMUserRecord *nm_conference_get_participant(NMConference * conference, int index); - -/** - * Check to see if the conference has been instantiated - * - * @param conference The conference - * - * @return TRUE if the conference has been instantiated, - * FALSE otherwise. - * - */ -gboolean nm_conference_is_instantiated(NMConference * conf); - -/** - * Set the flags for the conference. - * - * @param conference The conference - * @param flags The conference flags. - * - */ -void nm_conference_set_flags(NMConference * conference, guint32 flags); - -/** - * Set the user defined data for the conference. - * - * @param conference The conference - * @param data User defined data - * - */ -void nm_conference_set_data(NMConference * conference, gpointer data); - -/** - * Get the user defined data for the conference. - * - * @param conference The conference - * - * @return The data if it has been set, NULL otherwise. - * - */ -gpointer nm_conference_get_data(NMConference * conference); - -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmconn.c --- a/libpurple/protocols/novell/nmconn.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,703 +0,0 @@ -/* - * nmconn.c - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 -#include -#include -#include -#include -#include -#include "nmconn.h" - -#ifdef _WIN32 -#include -#endif - -#include "util.h" - -#define NO_ESCAPE(ch) ((ch == 0x20) || (ch >= 0x30 && ch <= 0x39) || \ - (ch >= 0x41 && ch <= 0x5a) || (ch >= 0x61 && ch <= 0x7a)) - -/* Read data from conn until the end of a line */ -static NMERR_T -read_line(NMConn * conn, char *buff, int len) -{ - NMERR_T rc = NM_OK; - int total_bytes = 0; - - while ((rc == NM_OK) && (total_bytes < (len - 1))) { - rc = nm_read_all(conn, &buff[total_bytes], 1); - if (rc == NM_OK) { - total_bytes += 1; - if (buff[total_bytes - 1] == '\n') { - break; - } - } - } - buff[total_bytes] = '\0'; - - return rc; -} - -static char * -url_escape_string(char *src) -{ - guint32 escape = 0; - char *p; - char *q; - char *encoded = NULL; - int ch; - - static const char hex_table[16] = "0123456789abcdef"; - - if (src == NULL) { - return NULL; - } - - /* Find number of chars to escape */ - for (p = src; *p != '\0'; p++) { - ch = (guchar) *p; - if (!NO_ESCAPE(ch)) { - escape++; - } - } - - encoded = g_malloc((p - src) + (escape * 2) + 1); - - /* Escape the string */ - for (p = src, q = encoded; *p != '\0'; p++) { - ch = (guchar) * p; - if (NO_ESCAPE(ch)) { - if (ch != 0x20) { - *q = ch; - q++; - } else { - *q = '+'; - q++; - } - } else { - *q = '%'; - q++; - - *q = hex_table[ch >> 4]; - q++; - - *q = hex_table[ch & 15]; - q++; - } - } - *q = '\0'; - - return encoded; -} - -static char * -encode_method(guint8 method) -{ - char *str; - - switch (method) { - case NMFIELD_METHOD_EQUAL: - str = "G"; - break; - case NMFIELD_METHOD_UPDATE: - str = "F"; - break; - case NMFIELD_METHOD_GTE: - str = "E"; - break; - case NMFIELD_METHOD_LTE: - str = "D"; - break; - case NMFIELD_METHOD_NE: - str = "C"; - break; - case NMFIELD_METHOD_EXIST: - str = "B"; - break; - case NMFIELD_METHOD_NOTEXIST: - str = "A"; - break; - case NMFIELD_METHOD_SEARCH: - str = "9"; - break; - case NMFIELD_METHOD_MATCHBEGIN: - str = "8"; - break; - case NMFIELD_METHOD_MATCHEND: - str = "7"; - break; - case NMFIELD_METHOD_NOT_ARRAY: - str = "6"; - break; - case NMFIELD_METHOD_OR_ARRAY: - str = "5"; - break; - case NMFIELD_METHOD_AND_ARRAY: - str = "4"; - break; - case NMFIELD_METHOD_DELETE_ALL: - str = "3"; - break; - case NMFIELD_METHOD_DELETE: - str = "2"; - break; - case NMFIELD_METHOD_ADD: - str = "1"; - break; - default: /* NMFIELD_METHOD_VALID */ - str = "0"; - break; - } - - return str; -} - -NMConn * -nm_create_conn(const char *addr, int port) -{ - NMConn *conn = g_new0(NMConn, 1); - conn->addr = g_strdup(addr); - conn->port = port; - return conn; -} - -void nm_release_conn(NMConn *conn) -{ - if (conn) { - GSList *node; - for (node = conn->requests; node; node = node->next) { - if (node->data) - nm_release_request(node->data); - } - g_slist_free(conn->requests); - conn->requests = NULL; - if (conn->ssl_conn) { - g_free(conn->ssl_conn); - conn->ssl_conn = NULL; - } - g_free(conn->addr); - conn->addr = NULL; - g_free(conn); - } -} - -int -nm_tcp_write(NMConn * conn, const void *buff, int len) -{ - if (conn == NULL || buff == NULL) - return -1; - - if (!conn->use_ssl) - return (write(conn->fd, buff, len)); - else if (conn->ssl_conn && conn->ssl_conn->write) - return (conn->ssl_conn->write(conn->ssl_conn->data, buff, len)); - else - return -1; -} - -int -nm_tcp_read(NMConn * conn, void *buff, int len) -{ - if (conn == NULL || buff == NULL) - return -1; - - if (!conn->use_ssl) - return (read(conn->fd, buff, len)); - else if (conn->ssl_conn && conn->ssl_conn->read) - return ((conn->ssl_conn->read)(conn->ssl_conn->data, buff, len)); - else - return -1; -} - -NMERR_T -nm_read_all(NMConn * conn, char *buff, int len) -{ - NMERR_T rc = NM_OK; - int bytes_left = len; - int bytes_read; - int total_bytes = 0; - int retry = 1000; - - if (conn == NULL || buff == NULL) - return NMERR_BAD_PARM; - - /* Keep reading until buffer is full */ - while (bytes_left) { - bytes_read = nm_tcp_read(conn, &buff[total_bytes], bytes_left); - if (bytes_read > 0) { - bytes_left -= bytes_read; - total_bytes += bytes_read; - } else { - if (errno == EAGAIN) { - if (--retry == 0) { - rc = NMERR_TCP_READ; - break; - } -#ifdef _WIN32 - Sleep(1); -#else - usleep(1000); -#endif - } else { - rc = NMERR_TCP_READ; - break; - } - } - } - return rc; -} - -NMERR_T -nm_read_uint32(NMConn *conn, guint32 *val) -{ - NMERR_T rc = NM_OK; - - rc = nm_read_all(conn, (char *)val, sizeof(*val)); - if (rc == NM_OK) { - *val = GUINT32_FROM_LE(*val); - } - - return rc; -} - -NMERR_T -nm_read_uint16(NMConn *conn, guint16 *val) -{ - NMERR_T rc = NM_OK; - - rc = nm_read_all(conn, (char *)val, sizeof(*val)); - if (rc == NM_OK) { - *val = GUINT16_FROM_LE(*val); - } - - return rc; -} - -NMERR_T -nm_write_fields(NMConn * conn, NMField * fields) -{ - NMERR_T rc = NM_OK; - NMField *field; - char *value = NULL; - char *method = NULL; - char buffer[4096]; - int ret; - int bytes_to_send; - int val = 0; - - if (conn == NULL || fields == NULL) { - return NMERR_BAD_PARM; - } - - /* Format each field as valid "post" data and write it out */ - for (field = fields; (rc == NM_OK) && (field->tag); field++) { - - /* We don't currently handle binary types */ - if (field->method == NMFIELD_METHOD_IGNORE || - field->type == NMFIELD_TYPE_BINARY) { - continue; - } - - /* Write the field tag */ - bytes_to_send = g_snprintf(buffer, sizeof(buffer), "&tag=%s", field->tag); - ret = nm_tcp_write(conn, buffer, bytes_to_send); - if (ret < 0) { - rc = NMERR_TCP_WRITE; - } - - /* Write the field method */ - if (rc == NM_OK) { - method = encode_method(field->method); - bytes_to_send = g_snprintf(buffer, sizeof(buffer), "&cmd=%s", method); - ret = nm_tcp_write(conn, buffer, bytes_to_send); - if (ret < 0) { - rc = NMERR_TCP_WRITE; - } - } - - /* Write the field value */ - if (rc == NM_OK) { - switch (field->type) { - case NMFIELD_TYPE_UTF8: - case NMFIELD_TYPE_DN: - - value = url_escape_string((char *) field->ptr_value); - bytes_to_send = g_snprintf(buffer, sizeof(buffer), - "&val=%s", value); - if (bytes_to_send > (int)sizeof(buffer)) { - ret = nm_tcp_write(conn, buffer, sizeof(buffer)); - } else { - ret = nm_tcp_write(conn, buffer, bytes_to_send); - } - - if (ret < 0) { - rc = NMERR_TCP_WRITE; - } - - g_free(value); - - break; - - case NMFIELD_TYPE_ARRAY: - case NMFIELD_TYPE_MV: - - val = nm_count_fields((NMField *) field->ptr_value); - bytes_to_send = g_snprintf(buffer, sizeof(buffer), - "&val=%u", val); - ret = nm_tcp_write(conn, buffer, bytes_to_send); - if (ret < 0) { - rc = NMERR_TCP_WRITE; - } - - break; - - default: - - bytes_to_send = g_snprintf(buffer, sizeof(buffer), - "&val=%u", field->value); - ret = nm_tcp_write(conn, buffer, bytes_to_send); - if (ret < 0) { - rc = NMERR_TCP_WRITE; - } - - break; - } - } - - /* Write the field type */ - if (rc == NM_OK) { - bytes_to_send = g_snprintf(buffer, sizeof(buffer), - "&type=%u", field->type); - ret = nm_tcp_write(conn, buffer, bytes_to_send); - if (ret < 0) { - rc = NMERR_TCP_WRITE; - } - } - - /* If the field is a sub array then post its fields */ - if (rc == NM_OK && val > 0) { - if (field->type == NMFIELD_TYPE_ARRAY || - field->type == NMFIELD_TYPE_MV) { - - rc = nm_write_fields(conn, (NMField *) field->ptr_value); - - } - } - } - - return rc; -} - -NMERR_T -nm_send_request(NMConn *conn, char *cmd, NMField *fields, - nm_response_cb cb, gpointer data, NMRequest **request) -{ - NMERR_T rc = NM_OK; - char buffer[512]; - int bytes_to_send; - int ret; - NMField *request_fields = NULL; - char *str = NULL; - - if (conn == NULL || cmd == NULL) - return NMERR_BAD_PARM; - - /* Write the post */ - bytes_to_send = g_snprintf(buffer, sizeof(buffer), - "POST /%s HTTP/1.0\r\n", cmd); - ret = nm_tcp_write(conn, buffer, bytes_to_send); - if (ret < 0) { - rc = NMERR_TCP_WRITE; - } - - /* Write headers */ - if (rc == NM_OK) { - if (purple_strequal("login", cmd)) { - bytes_to_send = g_snprintf(buffer, sizeof(buffer), - "Host: %s:%d\r\n\r\n", conn->addr, conn->port); - ret = nm_tcp_write(conn, buffer, bytes_to_send); - if (ret < 0) { - rc = NMERR_TCP_WRITE; - } - } else { - bytes_to_send = g_snprintf(buffer, sizeof(buffer), "\r\n"); - ret = nm_tcp_write(conn, buffer, bytes_to_send); - if (ret < 0) { - rc = NMERR_TCP_WRITE; - } - } - } - - /* Add the transaction id to the request fields */ - if (rc == NM_OK) { - if (fields) - request_fields = nm_copy_field_array(fields); - - str = g_strdup_printf("%d", ++(conn->trans_id)); - request_fields = nm_field_add_pointer(request_fields, NM_A_SZ_TRANSACTION_ID, 0, - NMFIELD_METHOD_VALID, 0, - str, NMFIELD_TYPE_UTF8); - } - - /* Send the request to the server */ - if (rc == NM_OK) { - rc = nm_write_fields(conn, request_fields); - } - - /* Write the CRLF to terminate the data */ - if (rc == NM_OK) { - ret = nm_tcp_write(conn, "\r\n", strlen("\r\n")); - if (ret < 0) { - rc = NMERR_TCP_WRITE; - } - } - - /* Create a request struct, add it to our queue, and return it */ - if (rc == NM_OK) { - NMRequest *new_request = nm_create_request(cmd, conn->trans_id, - time(0), cb, NULL, data); - nm_conn_add_request_item(conn, new_request); - - /* Set the out param if it was sent in, otherwise release the request */ - if (request) - *request = new_request; - else - nm_release_request(new_request); - } - - if (request_fields != NULL) - nm_free_fields(&request_fields); - - return rc; -} - -NMERR_T -nm_read_header(NMConn * conn) -{ - NMERR_T rc = NM_OK; - char buffer[512]; - char *ptr = NULL; - int i; - char rtn_buf[8]; - int rtn_code = 0; - - if (conn == NULL) - return NMERR_BAD_PARM; - - *buffer = '\0'; - rc = read_line(conn, buffer, sizeof(buffer)); - if (rc == NM_OK) { - - /* Find the return code */ - ptr = strchr(buffer, ' '); - if (ptr != NULL) { - ptr++; - - i = 0; - while (isdigit(*ptr) && (i < 3)) { - rtn_buf[i] = *ptr; - i++; - ptr++; - } - rtn_buf[i] = '\0'; - - if (i > 0) - rtn_code = atoi(rtn_buf); - } - } - - /* Finish reading header, in the future we might want to do more processing here */ - /* TODO: handle more general redirects in the future */ - while ((rc == NM_OK) && (!purple_strequal(buffer, "\r\n"))) { - rc = read_line(conn, buffer, sizeof(buffer)); - } - - if (rc == NM_OK && rtn_code == 301) - rc = NMERR_SERVER_REDIRECT; - - return rc; -} - -NMERR_T -nm_read_fields(NMConn * conn, int count, NMField ** fields) -{ - NMERR_T rc = NM_OK; - guint8 type; - guint8 method; - guint32 val; - char tag[64]; - NMField *sub_fields = NULL; - char *str = NULL; - - if (conn == NULL || fields == NULL) - return NMERR_BAD_PARM; - - do { - if (count > 0) { - count--; - } - - /* Read the field type, method, and tag */ - rc = nm_read_all(conn, (char *)&type, sizeof(type)); - if (rc != NM_OK || type == 0) - break; - - rc = nm_read_all(conn, (char *)&method, sizeof(method)); - if (rc != NM_OK) - break; - - rc = nm_read_uint32(conn, &val); - if (rc != NM_OK) - break; - - if (val > sizeof(tag)) { - rc = NMERR_PROTOCOL; - break; - } - - rc = nm_read_all(conn, tag, val); - if (rc != NM_OK) - break; - - if (type == NMFIELD_TYPE_MV || type == NMFIELD_TYPE_ARRAY) { - - /* Read the subarray (first read the number of items in the array) */ - rc = nm_read_uint32(conn, &val); - if (rc != NM_OK) - break; - - if (val > 0) { - rc = nm_read_fields(conn, val, &sub_fields); - if (rc != NM_OK) - break; - } - - *fields = nm_field_add_pointer(*fields, tag, 0, method, - 0, sub_fields, type); - - sub_fields = NULL; - - } else if (type == NMFIELD_TYPE_UTF8 || type == NMFIELD_TYPE_DN) { - - /* Read the string (first read the length) */ - rc = nm_read_uint32(conn, &val); - if (rc != NM_OK) - break; - - if (val >= NMFIELD_MAX_STR_LENGTH) { - rc = NMERR_PROTOCOL; - break; - } - - if (val > 0) { - str = g_new0(char, val + 1); - - rc = nm_read_all(conn, str, val); - if (rc != NM_OK) - break; - - *fields = nm_field_add_pointer(*fields, tag, 0, method, - 0, str, type); - str = NULL; - } - - } else { - - /* Read the numerical value */ - rc = nm_read_uint32(conn, &val); - if (rc != NM_OK) - break; - - *fields = nm_field_add_number(*fields, tag, 0, method, - 0, val, type); - } - - } while ((type != 0) && (count != 0)); - - - if (str != NULL) { - g_free(str); - } - - if (sub_fields != NULL) { - nm_free_fields(&sub_fields); - } - - return rc; -} - -void -nm_conn_add_request_item(NMConn * conn, NMRequest * request) -{ - if (conn == NULL || request == NULL) - return; - - nm_request_add_ref(request); - conn->requests = g_slist_append(conn->requests, request); -} - -void -nm_conn_remove_request_item(NMConn * conn, NMRequest * request) -{ - if (conn == NULL || request == NULL) - return; - - conn->requests = g_slist_remove(conn->requests, request); - nm_release_request(request); -} - -NMRequest * -nm_conn_find_request(NMConn * conn, int trans_id) -{ - NMRequest *req = NULL; - GSList *itr = NULL; - - if (conn == NULL) - return NULL; - - itr = conn->requests; - while (itr) { - req = (NMRequest *) itr->data; - if (req != NULL && nm_request_get_trans_id(req) == trans_id) { - return req; - } - itr = g_slist_next(itr); - } - return NULL; -} - -const char * -nm_conn_get_addr(NMConn * conn) -{ - if (conn == NULL) - return NULL; - else - return conn->addr; -} - -int -nm_conn_get_port(NMConn * conn) -{ - if (conn == NULL) - return -1; - else - return conn->port; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmconn.h --- a/libpurple/protocols/novell/nmconn.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,246 +0,0 @@ -/* - * nmconn.h - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 __NM_CONN_H__ -#define __NM_CONN_H__ - -typedef struct _NMConn NMConn; -typedef struct _NMSSLConn NMSSLConn; - -#include "nmfield.h" -#include "nmuser.h" - -typedef int (*nm_ssl_read_cb) (gpointer ssl_data, void *buff, int len); -typedef int (*nm_ssl_write_cb) (gpointer ssl_data, const void *buff, int len); - -struct _NMConn -{ - - /* The address of the server that we are connecting to. */ - char *addr; - - /* The port that we are connecting to. */ - int port; - - /* The file descriptor of the socket for the connection. */ - int fd; - - /* The transaction counter. */ - int trans_id; - - /* A list of requests currently awaiting a response. */ - GSList *requests; - - /* Are we connected? TRUE if so, FALSE if not. */ - gboolean connected; - - /* Are we running in secure mode? */ - gboolean use_ssl; - - /* Have we been redirected? */ - gboolean redirect; - - /* SSL connection */ - NMSSLConn *ssl_conn; - -}; - -struct _NMSSLConn -{ - - /* Data to pass to the callbacks */ - gpointer data; - - /* Callbacks for reading/writing */ - nm_ssl_read_cb read; - nm_ssl_write_cb write; - -}; - -/** - * Allocate a new NMConn struct - * - * @param The address of the server that we are connecting to. - * @param The port that we are connecting to. - * - * @return A pointer to a newly allocated NMConn struct, should - * be freed by calling nm_release_conn() - */ -NMConn *nm_create_conn(const char *addr, int port); - -/** - * Release an NMConn - * - * @param Pointer to the NMConn to release. - * - */ -void nm_release_conn(NMConn *conn); - -/** - * Write len bytes from the given buffer. - * - * @param conn The connection to write to. - * @param buff The buffer to write from. - * @param len The number of bytes to write. - * - * @return The number of bytes written. - */ -int nm_tcp_write(NMConn * conn, const void *buff, int len); - -/** - * Read at most len bytes into the given buffer. - * - * @param conn The connection to read from. - * @param buff The buffer to write to. - * @param len The maximum number of bytes to read. - * - * @return The number of bytes read. - */ -int nm_tcp_read(NMConn * conn, void *buff, int len); - -/** - * Read exactly len bytes into the given buffer. - * - * @param conn The connection to read from. - * @param buff The buffer to write to. - * @param len The number of bytes to read. - * - * @return NM_OK on success, NMERR_TCP_READ if read fails. - */ -NMERR_T nm_read_all(NMConn * conn, char *buf, int len); - -/** - * Read a 32 bit value and convert it to the host byte order. - * - * @param conn The connection to read from. - * @param val A pointer to unsigned 32 bit integer - * - * @return NM_OK on success, NMERR_TCP_READ if read fails. - */ -NMERR_T -nm_read_uint32(NMConn *conn, guint32 *val); - -/** - * Read a 16 bit value and convert it to the host byte order. - * - * @param conn The connection to read from. - * @param val A pointer to unsigned 16 bit integer - * - * @return NM_OK on success, NMERR_TCP_READ if read fails. - */ -NMERR_T -nm_read_uint16(NMConn *conn, guint16 *val); - -/** - * Dispatch a request to the server. - * - * @param conn The connection. - * @param cmd The request to dispatch. - * @param fields The field list for the request. - * @param cb The response callback for the new request object. - * @param data The user defined data for the request (to be passed to the resp cb). - * @param req The request. Should be freed with nm_release_request. - * - * @return NM_OK on success. - */ -NMERR_T -nm_send_request(NMConn *conn, char *cmd, NMField *fields, - nm_response_cb cb, gpointer data, NMRequest **request); - -/** - * Write out the given field list. - * - * @param conn The connection to write to. - * @param fields The field list to write. - * - * @return NM_OK on success. - */ -NMERR_T nm_write_fields(NMConn * conn, NMField * fields); - -/** - * Read the headers for a response. - * - * @param conn The connection to read from. - * - * @return NM_OK on success. - */ -NMERR_T nm_read_header(NMConn * conn); - -/** - * Read a field list from the connection. - * - * @param conn The connection to read from. - * @param count The maximum number of fields to read (or -1 for no max). - * @param fields The field list. This is an out param. It - * should be freed by calling nm_free_fields - * when finished. - * - * @return NM_OK on success. - */ -NMERR_T nm_read_fields(NMConn * conn, int count, NMField ** fields); - -/** - * Add a request to the connections request list. - * - * @param conn The connection. - * @param request The request to add to the list. - */ -void nm_conn_add_request_item(NMConn * conn, NMRequest * request); - -/** - * Remove a request from the connections list. - * - * @param conn The connection. - * @param request The request to remove from the list. - */ -void nm_conn_remove_request_item(NMConn * conn, NMRequest * request); - -/** - * Find the request with the given transaction id in the connections - * request list. - * - * @param conn The connection. - * @param trans_id The transaction id of the request to return. - * - * @return The request, or NULL if a matching request is not - * found. - */ -NMRequest *nm_conn_find_request(NMConn * conn, int trans_id); - -/** - * Get the server address for the connection. - * - * @param conn The connection. - * - * @return The server address for the connection. - * - */ -const char *nm_conn_get_addr(NMConn * conn); - -/** - * Get the port for the connection. - * - * @param conn The connection. - * - * @return The port that we are connected to. - */ -int nm_conn_get_port(NMConn * conn); - -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmcontact.c --- a/libpurple/protocols/novell/nmcontact.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,973 +0,0 @@ -/* - * nmcontact.c - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 -#include -#include "nmcontact.h" -#include "nmfield.h" -#include "nmuser.h" -#include "util.h" - -struct _NMContact -{ - int id; - int parent_id; - int seq; - char *dn; - char *display_name; - NMUserRecord *user_record; - gpointer data; - int ref_count; -}; - -struct _NMFolder -{ - int id; - int seq; - char *name; - GSList *folders; - GSList *contacts; - int ref_count; -}; - -static int count = 0; - -static void _release_folder_contacts(NMFolder * folder); -static void _release_folder_folders(NMFolder * folder); -static void _add_contacts(NMUser * user, NMFolder * folder, NMField * fields); -static void _add_folders(NMFolder * root, NMField * fields); - -/********************************************************************* - * Contact API - *********************************************************************/ - -NMContact * -nm_create_contact() -{ - NMContact *contact = g_new0(NMContact, 1); - - contact->ref_count = 1; - - purple_debug(PURPLE_DEBUG_INFO, "novell", "Creating contact, total=%d\n", - count++); - - return contact; -} - -/* - * This creates a contact for the contact list. The - * field array that is passed in should be a - * NM_A_FA_CONTACT array. - * - */ -NMContact * -nm_create_contact_from_fields(NMField * fields) -{ - NMContact *contact; - NMField *field; - - if ( fields == NULL || fields->tag == NULL || fields->ptr_value == 0 || - !purple_strequal(fields->tag, NM_A_FA_CONTACT) ) - { - return NULL; - } - - contact = nm_create_contact(); - - if ((field = nm_locate_field(NM_A_SZ_OBJECT_ID, (NMField *) fields->ptr_value))) { - - if (field->ptr_value) - contact->id = atoi((char *) field->ptr_value); - - } - - if ((field = nm_locate_field(NM_A_SZ_PARENT_ID, (NMField *) fields->ptr_value))) { - - if (field->ptr_value) - contact->parent_id = atoi((char *) field->ptr_value); - - } - - if ((field = - nm_locate_field(NM_A_SZ_SEQUENCE_NUMBER, (NMField *) fields->ptr_value))) { - - if (field->ptr_value) - contact->seq = atoi((char *) field->ptr_value); - - } - - if ((field = - nm_locate_field(NM_A_SZ_DISPLAY_NAME, (NMField *) fields->ptr_value))) { - - if (field->ptr_value) - contact->display_name = g_strdup((char *) field->ptr_value); - - } - - if ((field = nm_locate_field(NM_A_SZ_DN, (NMField *) fields->ptr_value))) { - - if (field->ptr_value) - contact->dn = g_strdup((char *) field->ptr_value); - - } - - return contact; -} - -void -nm_contact_update_list_properties(NMContact * contact, NMField * fields) -{ - NMField *field; - - if (contact == NULL || fields == NULL || fields->ptr_value == 0) - return; - - if ((field = nm_locate_field(NM_A_SZ_OBJECT_ID, (NMField *) fields->ptr_value))) { - - if (field->ptr_value) - contact->id = atoi((char *)field->ptr_value); - - } - - if ((field = nm_locate_field(NM_A_SZ_PARENT_ID, (NMField *) fields->ptr_value))) { - - if (field->ptr_value) - contact->parent_id = atoi((char *) field->ptr_value); - - } - - if ((field = - nm_locate_field(NM_A_SZ_SEQUENCE_NUMBER, (NMField *) fields->ptr_value))) { - - if (field->ptr_value) - contact->seq = atoi((char *) field->ptr_value); - - } - - if ((field = - nm_locate_field(NM_A_SZ_DISPLAY_NAME, (NMField *) fields->ptr_value))) { - - if (field->ptr_value) { - if (contact->display_name) - g_free(contact->display_name); - - contact->display_name = g_strdup((char *) field->ptr_value); - } - - } - - if ((field = nm_locate_field(NM_A_SZ_DN, (NMField *) fields->ptr_value))) { - - if (field->ptr_value) { - if (contact->dn) - g_free(contact->dn); - - contact->dn = g_strdup((char *) field->ptr_value); - } - - } -} - -NMField * -nm_contact_to_fields(NMContact * contact) -{ - NMField *fields = NULL; - - if (contact == NULL) - return NULL; - - fields = nm_field_add_pointer(fields, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup_printf("%d", contact->id), NMFIELD_TYPE_UTF8); - - fields = nm_field_add_pointer(fields, NM_A_SZ_PARENT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup_printf("%d", contact->parent_id), NMFIELD_TYPE_UTF8); - - fields = nm_field_add_pointer(fields, NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_METHOD_VALID, 0, - g_strdup_printf("%d", contact->seq), NMFIELD_TYPE_UTF8); - - if (contact->display_name != NULL) { - fields = nm_field_add_pointer(fields, NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(contact->display_name), NMFIELD_TYPE_UTF8); - } - - if (contact->dn != NULL) { - fields = nm_field_add_pointer(fields, NM_A_SZ_DN, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(contact->dn), NMFIELD_TYPE_UTF8); - } - - return fields; -} - -void -nm_contact_add_ref(NMContact * contact) -{ - if (contact) - contact->ref_count++; -} - -void -nm_release_contact(NMContact * contact) -{ - if (contact == NULL) - return; - - if (--(contact->ref_count) == 0) { - - purple_debug(PURPLE_DEBUG_INFO, "novell", - "Releasing contact, total=%d\n", --count); - - if (contact->display_name) { - g_free(contact->display_name); - } - - if (contact->dn) { - g_free(contact->dn); - } - - if (contact->user_record) { - nm_release_user_record(contact->user_record); - } - - g_free(contact); - } - -} - -const char * -nm_contact_get_display_name(NMContact * contact) -{ - if (contact == NULL) - return NULL; - - if (contact->user_record != NULL && contact->display_name == NULL) { - const char *full_name, *lname, *fname, *cn, *display_id; - - full_name = nm_user_record_get_full_name(contact->user_record); - fname = nm_user_record_get_first_name(contact->user_record); - lname = nm_user_record_get_last_name(contact->user_record); - cn = nm_user_record_get_userid(contact->user_record); - display_id = nm_user_record_get_display_id(contact->user_record); - - /* Try to build a display name. */ - if (full_name) { - - contact->display_name = g_strdup(full_name); - - } else if (fname && lname) { - - contact->display_name = g_strdup_printf("%s %s", fname, lname); - - } else { - - /* If auth attribute is set use it */ - if (nm_user_record_get_auth_attr(contact->user_record) && - display_id != NULL) { - - contact->display_name = g_strdup(display_id); - - } else { - - /* Use CN or display id */ - if (cn) { - - contact->display_name = g_strdup(cn); - - } else if (display_id) { - - contact->display_name = g_strdup(display_id); - - } - - } - - } - } - - return contact->display_name; -} - -void -nm_contact_set_display_name(NMContact * contact, const char *display_name) -{ - if (contact == NULL) - return; - - if (contact->display_name) { - g_free(contact->display_name); - contact->display_name = NULL; - } - - if (display_name) - contact->display_name = g_strdup(display_name); -} - -void -nm_contact_set_dn(NMContact * contact, const char *dn) -{ - if (contact == NULL) - return; - - if (contact->dn) { - g_free(contact->dn); - contact->dn = NULL; - } - - if (dn) - contact->dn = g_strdup(dn); -} - -const char * -nm_contact_get_dn(NMContact * contact) -{ - if (contact == NULL) - return NULL; - - return contact->dn; -} - -gpointer -nm_contact_get_data(NMContact * contact) -{ - if (contact == NULL) - return NULL; - - return contact->data; -} - -int -nm_contact_get_id(NMContact * contact) -{ - if (contact == NULL) - return -1; - - return contact->id; -} - -int -nm_contact_get_parent_id(NMContact * contact) -{ - if (contact == NULL) - return -1; - - return contact->parent_id; -} - -void -nm_contact_set_data(NMContact * contact, gpointer data) -{ - if (contact == NULL) - return; - - contact->data = data; -} - -void -nm_contact_set_user_record(NMContact * contact, NMUserRecord * user_record) -{ - if (contact == NULL) - return; - - if (contact->user_record) { - nm_release_user_record(contact->user_record); - } - - nm_user_record_add_ref(user_record); - contact->user_record = user_record; -} - -NMUserRecord * -nm_contact_get_user_record(NMContact * contact) -{ - if (contact == NULL) - return NULL; - - return contact->user_record; -} - -const char * -nm_contact_get_userid(NMContact * contact) -{ - NMUserRecord *user_record; - const char *userid = NULL; - - if (contact == NULL) - return NULL; - - user_record = nm_contact_get_user_record(contact); - if (user_record) { - userid = nm_user_record_get_userid(user_record); - } - - return userid; -} - -const char * -nm_contact_get_display_id(NMContact * contact) -{ - NMUserRecord *user_record; - const char *id = NULL; - - if (contact == NULL) - return NULL; - - user_record = nm_contact_get_user_record(contact); - if (user_record) { - id = nm_user_record_get_display_id(user_record); - } - - return id; -} - - -/********************************************************************* - * Folder API - *********************************************************************/ - -NMFolder * -nm_create_folder(const char *name) -{ - NMFolder *folder = g_new0(NMFolder, 1); - - if (name) - folder->name = g_strdup(name); - - folder->ref_count = 1; - - return folder; -} - -NMFolder * -nm_create_folder_from_fields(NMField * fields) -{ - NMField *field; - NMFolder *folder; - - if (fields == NULL || fields->ptr_value == 0) - return NULL; - - folder = g_new0(NMFolder, 1); - - if ((field = nm_locate_field(NM_A_SZ_OBJECT_ID, (NMField *) fields->ptr_value))) { - - if (field->ptr_value) - folder->id = atoi((char *) field->ptr_value); - } - - if ((field = - nm_locate_field(NM_A_SZ_SEQUENCE_NUMBER, (NMField *) fields->ptr_value))) { - - if (field->ptr_value) - folder->seq = atoi((char *) field->ptr_value); - } - - if ((field = - nm_locate_field(NM_A_SZ_DISPLAY_NAME, (NMField *) fields->ptr_value))) { - - if (field->ptr_value) - folder->name = g_strdup((char *) field->ptr_value); - } - - folder->ref_count = 1; - return folder; -} - -NMField * -nm_folder_to_fields(NMFolder * folder) -{ - NMField *fields = NULL; - - if (folder == NULL) - return NULL; - - fields = nm_field_add_pointer(fields, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup_printf("%d", folder->id), NMFIELD_TYPE_UTF8); - - fields = nm_field_add_pointer(fields, NM_A_SZ_PARENT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup("0"), NMFIELD_TYPE_UTF8); - - fields = nm_field_add_pointer(fields, NM_A_SZ_TYPE, 0, NMFIELD_METHOD_VALID, 0, - g_strdup("1"), NMFIELD_TYPE_UTF8); - - fields = nm_field_add_pointer(fields, NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_METHOD_VALID, 0, - g_strdup_printf("%d", folder->seq), NMFIELD_TYPE_UTF8); - - if (folder->name != NULL) { - fields = nm_field_add_pointer(fields, NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(folder->name), NMFIELD_TYPE_UTF8); - } - - - return fields; -} - -void -nm_folder_update_list_properties(NMFolder * folder, NMField * fields) -{ - NMField *field; - - if (folder == NULL || fields == NULL || fields->ptr_value == 0) - return; - - if ((field = nm_locate_field(NM_A_SZ_OBJECT_ID, (NMField *) fields->ptr_value))) { - - if (field->ptr_value) - folder->id = atoi((char *) field->ptr_value); - - } - - if ((field = - nm_locate_field(NM_A_SZ_SEQUENCE_NUMBER, (NMField *) fields->ptr_value))) { - - if (field->ptr_value) - folder->seq = atoi((char *) field->ptr_value); - - } - - if ((field = - nm_locate_field(NM_A_SZ_DISPLAY_NAME, (NMField *) fields->ptr_value))) { - - if (field->ptr_value) { - if (folder->name) - g_free(folder->name); - - folder->name = g_strdup((char *) field->ptr_value); - } - - } - -} - -void -nm_release_folder(NMFolder * folder) -{ - if (folder == NULL) - return; - - if (--(folder->ref_count) == 0) { - if (folder->name) { - g_free(folder->name); - } - - if (folder->folders) { - _release_folder_folders(folder); - } - - if (folder->contacts) { - _release_folder_contacts(folder); - } - - g_free(folder); - } -} - - -void -nm_folder_add_ref(NMFolder * folder) -{ - if (folder) - folder->ref_count++; -} - -int -nm_folder_get_subfolder_count(NMFolder * folder) -{ - if (folder == NULL) - return 0; - - if (folder->folders) - return g_slist_length(folder->folders); - else - return 0; -} - -NMFolder * -nm_folder_get_subfolder(NMFolder * folder, int index) -{ - if (folder == NULL) - return NULL; - - if (folder->folders) - return (NMFolder *) g_slist_nth_data(folder->folders, index); - else - return NULL; -} - -int -nm_folder_get_contact_count(NMFolder * folder) -{ - if (folder == NULL) - return 0; - - if (folder->contacts != NULL) - return g_slist_length(folder->contacts); - else - return 0; -} - -NMContact * -nm_folder_get_contact(NMFolder * folder, int index) -{ - if (folder == NULL) - return NULL; - - if (folder->contacts) - return (NMContact *) g_slist_nth_data(folder->contacts, index); - else - return NULL; -} - -const char * -nm_folder_get_name(NMFolder * folder) -{ - if (folder == NULL) - return NULL; - - return folder->name; -} - -void -nm_folder_set_name(NMFolder * folder, const char *name) -{ - if (folder == NULL || name == NULL) - return; - - if (folder->name) - g_free(folder->name); - - folder->name = g_strdup(name); -} - -int -nm_folder_get_id(NMFolder * folder) -{ - if (folder == NULL) { - return -1; - } - - return folder->id; -} - -void -nm_folder_add_folder_to_list(NMFolder * root, NMFolder * folder) -{ - GSList *node; - - if (root == NULL || folder == NULL) - return; - - node = root->folders; - while (node) { - if (folder->seq <= ((NMFolder *) node->data)->seq) { - nm_folder_add_ref(folder); - root->folders = g_slist_insert_before(root->folders, node, folder); - break; - } - node = g_slist_next(node); - } - if (node == NULL) { - nm_folder_add_ref(folder); - root->folders = g_slist_append(root->folders, folder); - } -} - -void -nm_folder_remove_contact(NMFolder * folder, NMContact * contact) -{ - GSList *node; - - if (folder == NULL || contact == NULL) - return; - - node = folder->contacts; - while (node) { - if (contact->id == ((NMContact *) (node->data))->id) { - folder->contacts = g_slist_remove(folder->contacts, node->data); - nm_release_contact(contact); - break; - } - node = g_slist_next(node); - } -} - -void -nm_folder_add_contact_to_list(NMFolder * root_folder, NMContact * contact) -{ - GSList *node = NULL; - NMFolder *folder = root_folder; - - if (folder == NULL || contact == NULL) - return; - - /* Find folder to add contact to */ - if (contact->parent_id != 0) { - node = folder->folders; - while (node) { - folder = (NMFolder *) node->data; - if (contact->parent_id == folder->id) { - break; - } - folder = NULL; - node = g_slist_next(node); - } - } - - /* Add contact to list */ - if (folder) { - node = folder->contacts; - while (node) { - if (contact->seq <= ((NMContact *) (node->data))->seq) { - nm_contact_add_ref(contact); - folder->contacts = - g_slist_insert_before(folder->contacts, node, contact); - break; - } - node = g_slist_next(node); - } - - if (node == NULL) { - nm_contact_add_ref(contact); - folder->contacts = g_slist_append(folder->contacts, contact); - } - } -} - -void -nm_folder_add_contacts_and_folders(NMUser * user, NMFolder * root, - NMField * fields) -{ - /* Add the contacts and folders from the field array */ - if (user && root && fields) { - _add_folders(root, fields); - _add_contacts(user, root, fields); - } -} - -gpointer -nm_folder_find_item_by_object_id(NMFolder * root_folder, int object_id) -{ - int cnt, cnt2, i, j; - gpointer item = NULL; - NMFolder *folder; - NMContact *contact; - - if (root_folder == NULL) - return NULL; - - /* Check all contacts for the top level folder */ - cnt = nm_folder_get_contact_count(root_folder); - for (i = 0; i < cnt; i++) { - contact = nm_folder_get_contact(root_folder, i); - if (contact && (contact->id == object_id)) { - item = contact; - break; - } - } - - /* If we haven't found the item yet, check the subfolders */ - if (item == NULL) { - cnt = nm_folder_get_subfolder_count(root_folder); - for (i = 0; (i < cnt) && (item == NULL); i++) { - folder = nm_folder_get_subfolder(root_folder, i); - - /* Check the id of this folder */ - if (folder && (folder->id == object_id)) { - item = folder; - break; - } - - /* Check all contacts for this folder */ - cnt2 = nm_folder_get_contact_count(folder); - for (j = 0; j < cnt2; j++) { - contact = nm_folder_get_contact(folder, j); - if (contact && (contact->id == object_id)) { - item = contact; - break; - } - } - } - } - - return item; -} - -NMContact * -nm_folder_find_contact_by_userid(NMFolder * folder, const char *userid) -{ - int cnt, i; - NMContact *tmp, *contact = NULL; - - if (folder == NULL || userid == NULL) - return NULL; - - cnt = nm_folder_get_contact_count(folder); - for (i = 0; i < cnt; i++) { - tmp = nm_folder_get_contact(folder, i); - if (tmp && nm_utf8_str_equal(nm_contact_get_userid(tmp), userid)) { - contact = tmp; - break; - } - } - - return contact; -} - -NMContact * -nm_folder_find_contact_by_display_id(NMFolder * folder, const char *display_id) -{ - int cnt, i; - NMContact *tmp, *contact = NULL; - - if (folder == NULL || display_id == NULL) - return NULL; - - cnt = nm_folder_get_contact_count(folder); - for (i = 0; i < cnt; i++) { - tmp = nm_folder_get_contact(folder, i); - if (tmp && nm_utf8_str_equal(nm_contact_get_display_id(tmp), display_id)) { - contact = tmp; - break; - } - } - - return contact; -} - -NMContact * -nm_folder_find_contact(NMFolder * folder, const char *dn) -{ - int cnt, i; - NMContact *tmp, *contact = NULL; - - if (folder == NULL || dn == NULL) - return NULL; - - cnt = nm_folder_get_contact_count(folder); - for (i = 0; i < cnt; i++) { - tmp = nm_folder_get_contact(folder, i); - if (tmp && nm_utf8_str_equal(nm_contact_get_dn(tmp), dn)) { - contact = tmp; - break; - } - } - - return contact; -} - - -/********************************************************************* - * Utility functions - *********************************************************************/ - -static void -_release_folder_contacts(NMFolder * folder) -{ - GSList *cnode; - NMContact *contact; - - for (cnode = folder->contacts; cnode; cnode = cnode->next) { - contact = cnode->data; - cnode->data = NULL; - nm_release_contact(contact); - } - - g_slist_free(folder->contacts); - folder->contacts = NULL; -} - -static void -_release_folder_folders(NMFolder * folder) -{ - GSList *fnode; - NMFolder *subfolder; - - if (folder == NULL) - return; - - for (fnode = folder->folders; fnode; fnode = fnode->next) { - subfolder = fnode->data; - fnode->data = NULL; - nm_release_folder(subfolder); - } - - g_slist_free(folder->folders); - folder->folders = NULL; -} - -static void -_add_folders(NMFolder * root, NMField * fields) -{ - NMFolder *folder = NULL; - NMField *locate = NULL; - - locate = nm_locate_field(NM_A_FA_FOLDER, fields); - while (locate != NULL) { - - /* Create a new folder */ - folder = nm_create_folder_from_fields(locate); - - /* Add subfolder to roots folder list */ - nm_folder_add_folder_to_list(root, folder); - - /* Decrement the ref count */ - nm_release_folder(folder); - - /* Find the next folder */ - locate = nm_locate_field(NM_A_FA_FOLDER, locate+1); - - } -} - -static void -_add_contacts(NMUser * user, NMFolder * folder, NMField * fields) -{ - NMContact *contact = NULL; - NMField *locate = NULL, *details; - NMUserRecord *user_record = NULL; - - locate = nm_locate_field(NM_A_FA_CONTACT, fields); - while (locate != NULL) { - - /* Create a new contact from the fields */ - contact = nm_create_contact_from_fields(locate); - - /* Add it to our contact list */ - nm_folder_add_contact_to_list(folder, contact); - - /* Update the contact cache */ - nm_user_add_contact(user, contact); - - /* Update the user record cache */ - if ((details = nm_locate_field(NM_A_FA_USER_DETAILS, - (NMField *) locate->ptr_value))) { - user_record = nm_find_user_record(user, nm_contact_get_dn(contact)); - if (user_record == NULL) { - user_record = nm_create_user_record_from_fields(details); - nm_user_record_set_dn(user_record, nm_contact_get_dn(contact)); - nm_user_add_user_record(user, user_record); - nm_release_user_record(user_record); - } - nm_contact_set_user_record(contact, user_record); - } - - nm_release_contact(contact); - - locate = nm_locate_field(NM_A_FA_CONTACT, locate+1); - } -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmcontact.h --- a/libpurple/protocols/novell/nmcontact.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,425 +0,0 @@ -/* - * nmcontact.h - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 __NM_CONTACT_H__ -#define __NM_CONTACT_H__ - -#include - -typedef struct _NMContact NMContact; -typedef struct _NMContactProperty NMContactProperty; -typedef struct _NMFolder NMFolder; - -#include "nmfield.h" -#include "nmuser.h" - -/** - * Creates a contact - * - * Should be released by calling nm_release_contact - * - * @return The new NMContact - * - */ -NMContact *nm_create_contact(void); - -/** - * Creates a contact from a field array representing the - * contact - * - * Should be released by calling nm_release_contact - * - * @param fields Should be the NM_A_FA_CONTACT for - * the contact - * - * @return The new contact - * - */ -NMContact *nm_create_contact_from_fields(NMField * fields); - -/** - * Add a reference to an existing contact - * - * The reference should be released by calling - * nm_release_contact - * - * @param contact The contact - * - */ -void nm_contact_add_ref(NMContact * contact); - -/** - * Update the contact list properties of the contact (sequence, parent id, etc.) - * - * @param contact The contact to update - * @param fields The fields to update from (should be a NM_A_FA_CONTACT array) - * - */ -void nm_contact_update_list_properties(NMContact * contact, NMField * fields); - -/** - * Release a contact reference - * - * @param contact The contact to release. - * - */ -void nm_release_contact(NMContact * contact); - -/** - * Get the display name of a contact - * - * @param contact The contact - * - * @return The display name of a contact - * - */ -const char *nm_contact_get_display_name(NMContact * contact); - -/** - * Get the DN of a contact - * - * @param contact The contact - * - * @return The DN of the contact - */ -const char *nm_contact_get_dn(NMContact * contact); - -/** - * Set the display name for a contact. This is called - * by nm_send_rename_contact. It should not be called - * directly (it does not change the display name on the - * server side list -- nm_send_rename_conact does). - * - * @param contact The contact - * @param display_name The new display name - * - */ -void nm_contact_set_display_name(NMContact * contact, const char * display_name); - -/** - * Set the DN for the contact - * - * @param contact The contact - * @param dn The new DN for the contact - * - */ -void nm_contact_set_dn(NMContact * contact, const char * dn); - -/** - * Return a field array (NM_A_FA_CONTACT) representing the contact - * - * @param contact The contact - * - * @return A field array representing the contact - */ -NMField *nm_contact_to_fields(NMContact * contact); - -/** - * Set the user record for the contact - * - * @param contact The contact - * @param user_record The user record - * - */ -void nm_contact_set_user_record(NMContact * contact, NMUserRecord * user_record); - -/** - * Get the user record for the contact - * - * @param contact The contact - * - * @return The user record associated with the contact - * - */ -NMUserRecord *nm_contact_get_user_record(NMContact * contact); - -/** - * Get the user defined data for the contact - * - * @param contact The contact - * - * @return The user defined data for the contact - * - */ -gpointer nm_contact_get_data(NMContact * contact); - -/** - * Get the Object ID for the contact - * - * @param contact The contact - * - * @return The ID for the contact - */ -int nm_contact_get_id(NMContact * contact); - -/** - * Get the ID for the folder that the contact is in - * - * @param contact The contact - * - * @return The ID of the folder that contains the contact - * - */ -int nm_contact_get_parent_id(NMContact * contact); - -/** - * Get The userid of the contact. - * - * @param contact The contact - * - * @return The userid of the contact - * - */ -const char *nm_contact_get_userid(NMContact * contact); - -/** - * Get the display id of the contact - * - * @param contact The contact - * - * @return The display id of the contact - */ -const char *nm_contact_get_display_id(NMContact * contact); - -/** - * Set the user defined data for the contact - * - * @param contact The contact - * @param data The user defined data - * - */ -void nm_contact_set_data(NMContact * contact, gpointer data); - -/** - * Create a folder with the given name - * - * @param name The name of the folder - * - * @return The new folder - * - */ -NMFolder *nm_create_folder(const char *name); - -/** - * Create a folder from a NM_A_FA_FOLDER field array - * - * @param fields The NM_A_FA_FOLDER field array - * - * @return The new folder - * - */ -NMFolder *nm_create_folder_from_fields(NMField * fields); - -/** - * Add a reference to an existing folder - * - * The reference should be released by calling - * nm_release_folder - * - * @param folder The folder - * - */ -void nm_folder_add_ref(NMFolder * folder); - -/** - * Release a reference to a folder. - * - * @param folder The folder to release - * - */ -void nm_release_folder(NMFolder * folder); - -/** - * Return the number of subfolders for the given - * folder - * - * @param folder The folder - * - * @return The number of subfolders contained by folder - */ -int nm_folder_get_subfolder_count(NMFolder * folder); - -/** - * Get a subfolder - * - * @param folder The root folder - * @param index The index of the folder to get - * - * @return The subfolder at the given index - * - */ -NMFolder *nm_folder_get_subfolder(NMFolder * folder, int index); - -/** - * Get the number of contacts in the given folder - * - * @param folder The folder - * - * @return The number of contacts contained by folder - * - */ -int nm_folder_get_contact_count(NMFolder * folder); - -/** - * Get a contact in the given folder - * - * @param folder The folder - * @param index The index of the contact to get - * - * @return The contact at the given index - * - */ -NMContact *nm_folder_get_contact(NMFolder * folder, int index); - -/** - * Get the name of the folder - * - * @param folder The folder - * - * @return The name of the folder. - * - */ -const char *nm_folder_get_name(NMFolder * folder); - -/** - * Set the name of a folder. Do not call this directly. - * It does not change the name of the folder in the - * server side contact list. You must call - * nm_send_set_folder_name(). - * - * @param folder The folder - * @param name The new name for the folder - * - */ -void nm_folder_set_name(NMFolder * folder, const char *name); - -/** - * Get Object ID for folder - * - * @param folder The folder - * - * @return The ID of the folder - * - */ -int nm_folder_get_id(NMFolder * folder); - -/** - * Add contacts and folders from fields into root - * - * @param user The logged in user - * @param root The root folder - * @param fields The contact list field array - * - */ -void nm_folder_add_contacts_and_folders(NMUser * user, NMFolder * root, - NMField * fields); -/** - * Add a contact to the contact list. - * - * @param root_folder The root folder of the contact list - * @param contact The contact to add - * - */ -void nm_folder_add_contact_to_list(NMFolder * root_folder, - NMContact * contact); - -/** - * Update the contact list properties of the folder (sequence, parent id, etc.) - * - * @param folder The folder to update - * @param fields The fields to update from (should be a NM_A_FA_FOLDER array) - * - */ -void nm_folder_update_list_properties(NMFolder * folder, NMField * fields); - -/** - * Add folder to the contact list - * - * @param root_folder The root folder of the contact list - * @param folder The folder to add to the contact list - * - */ -void nm_folder_add_folder_to_list(NMFolder * root_folder, NMFolder * folder); - -/** - * Find the object with the given id - * - * @param root_folder The root folder of the contact list - * @param object_id The object id of the object to find - * - * @return The object with object id (either a contact or a folder) - */ -gpointer nm_folder_find_item_by_object_id(NMFolder * root_folder, - int object_id); - -/** - * Remove a contact from the folder - * - * @param folder The folder - * @param contact The contact to remove - * - */ -void nm_folder_remove_contact(NMFolder * folder, NMContact * contact); - -/** - * Find a contact in a folder by DN - * - * @param folder The folder to search - * @param dn The DN of the contact to find - * - * @return The contact if found, NULL otherwise - * - */ -NMContact *nm_folder_find_contact(NMFolder * folder, const char *dn); - -/** - * Find a contact in a folder by userid - * - * @param folder The folder to search - * @param userid The userid of the contact to find - * - * @return The contact if found, NULL otherwise - * - */ -NMContact *nm_folder_find_contact_by_userid(NMFolder * folder, - const char *userid); - -/** - * Find a contact in a folder by display id - * - * @param folder The folder to search - * @param display_id The userid of the contact to find - * - * @return The contact if found, NULL otherwise - * - */ -NMContact * -nm_folder_find_contact_by_display_id(NMFolder * folder, const char *display_id); - -/** - * Return a field array (NM_A_FA_FOLDER) representing the folder - * - * @param folder The folder - * - * @return A field array representing the folder - */ -NMField *nm_folder_to_fields(NMFolder * folder); - -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmevent.c --- a/libpurple/protocols/novell/nmevent.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,939 +0,0 @@ -/* - * nmevent.c - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 -#include -#include -#include "nmevent.h" -#include "nmfield.h" -#include "nmconn.h" -#include "nmuserrecord.h" -#include "nmrtf.h" - -#define MAX_UINT32 0xFFFFFFFF - -struct _NMEvent -{ - - /* Event type */ - int type; - - /* The DN of the event source */ - char *source; - - /* Timestamp of the event */ - guint32 gmt; - - /* Conference to associate with the event */ - NMConference *conference; - - /* User record to associate with the event */ - NMUserRecord *user_record; - - /* Text associated with the event */ - char *text; - - /* Reference count for event structure */ - int ref_count; - -}; - -/* Handle getdetails response and set the new user record into the event */ -static void -_got_user_for_event(NMUser * user, NMERR_T ret_val, - gpointer resp_data, gpointer user_data) -{ - NMUserRecord *user_record; - NMEvent *event; - nm_event_cb cb; - - if (user == NULL) - return; - - user_record = resp_data; - event = user_data; - - if (ret_val == NM_OK) { - if (event && user_record) { - - /* Add the user record to the event structure - * and make the callback. - */ - nm_event_set_user_record(event, user_record); - if ((cb = nm_user_get_event_callback(user))) { - cb(user, event); - } - } - - } else { - /* Cleanup resp_data */ - - } - - /* Clean up */ - if (event) - nm_release_event(event); - -} - -/* Handle getdetails response, set the new user record into the event - * and add the user record as a participant in the conference - */ -static void -_got_user_for_conference(NMUser * user, NMERR_T ret_val, - gpointer resp_data, gpointer user_data) -{ - NMUserRecord *user_record = resp_data; - NMEvent *event = user_data; - NMConference *conference; - nm_event_cb cb; - - if (user == NULL) - return; - - if (event && user_record) { - - conference = nm_event_get_conference(event); - if (conference) { - - /* Add source of event as recip of the conference */ - nm_conference_add_participant(conference, user_record); - - /* Add the user record to the event structure - * and make the callback. - */ - nm_event_set_user_record(event, user_record); - if ((cb = nm_user_get_event_callback(user))) { - cb(user, event); - } - } - } - - if (event) - nm_release_event(event); -} - -/* Read the receive message event, set up the event object, and - * get details for the event source if we don't have them yet. - */ -static NMERR_T -handle_receive_message(NMUser * user, NMEvent * event, gboolean autoreply) -{ - NMConference *conference; - NMUserRecord *user_record; - NMConn *conn; - NMERR_T rc = NM_OK; - guint32 size = 0, flags = 0; - char *msg = NULL; - char *nortf = NULL; - char *guid = NULL; - - conn = nm_user_get_conn(user); - - /* Read the conference guid */ - rc = nm_read_uint32(conn, &size); - if (size > 1000) return NMERR_PROTOCOL; - - if (rc == NM_OK) { - guid = g_new0(char, size + 1); - rc = nm_read_all(conn, guid, size); - } - - /* Read the conference flags */ - if (rc == NM_OK) { - rc = nm_read_uint32(conn, &flags); - } - - /* Read the message text */ - if (rc == NM_OK) { - rc = nm_read_uint32(conn, &size); - if (size > 100000) return NMERR_PROTOCOL; - - if (rc == NM_OK) { - msg = g_new0(char, size + 1); - rc = nm_read_all(conn, msg, size); - - purple_debug(PURPLE_DEBUG_INFO, "novell", "Message is %s\n", msg); - - /* Auto replies are not in RTF format! */ - if (!autoreply) { - NMRtfContext *ctx; - - ctx = nm_rtf_init(); - nortf = nm_rtf_strip_formatting(ctx, msg); - nm_rtf_deinit(ctx); - - purple_debug(PURPLE_DEBUG_INFO, "novell", - "Message without RTF is %s\n", nortf); - - /* Store the event data */ - nm_event_set_text(event, nortf); - - } else { - - /* Store the event data */ - nm_event_set_text(event, msg); - } - } - } - - /* Check to see if we already know about the conference */ - conference = nm_conference_list_find(user, guid); - if (conference) { - - nm_conference_set_flags(conference, flags); - nm_event_set_conference(event, conference); - - /* Add a reference to the user record in our event object */ - user_record = nm_find_user_record(user, nm_event_get_source(event)); - if (user_record) { - nm_event_set_user_record(event, user_record); - } - - } else { - - /* This is a new conference, so create one and add it to our list */ - conference = nm_create_conference(guid); - nm_conference_set_flags(conference, flags); - - /* Add a reference to the conference in the event */ - nm_event_set_conference(event, conference); - - /* Add new conference to the conference list */ - nm_conference_list_add(user, conference); - - /* Check to see if we have details for the event source yet */ - user_record = nm_find_user_record(user, nm_event_get_source(event)); - if (user_record) { - - /* We do so add the user record as a recipient of the conference */ - nm_conference_add_participant(conference, user_record); - - /* Add a reference to the user record in our event object */ - nm_event_set_user_record(event, user_record); - - } else { - - /* Need to go to the server to get details for the user */ - rc = nm_send_get_details(user, nm_event_get_source(event), - _got_user_for_conference, event); - if (rc == NM_OK) - rc = -1; /* Not done processing the event yet! */ - } - - nm_release_conference(conference); - } - - if (msg) - g_free(msg); - - if (nortf) - g_free(nortf); - - if (guid) - g_free(guid); - - return rc; -} - -/* Read the invite event, set up the event object, and - * get details for the event source if we don't have them yet. - */ -static NMERR_T -handle_conference_invite(NMUser * user, NMEvent * event) -{ - NMERR_T rc = NM_OK; - guint32 size = 0; - char *guid = NULL; - char *msg = NULL; - NMConn *conn; - NMUserRecord *user_record; - - conn = nm_user_get_conn(user); - - /* Read the conference guid */ - rc = nm_read_uint32(conn, &size); - if (size > 1000) return NMERR_PROTOCOL; - - if (rc == NM_OK) { - guid = g_new0(char, size + 1); - rc = nm_read_all(conn, guid, size); - } - - /* Read the the message */ - if (rc == NM_OK) { - rc = nm_read_uint32(conn, &size); - if (size > 100000) return NMERR_PROTOCOL; - - if (rc == NM_OK) { - msg = g_new0(char, size + 1); - rc = nm_read_all(conn, msg, size); - } - } - - /* Store the event data */ - if (rc == NM_OK) { - NMConference *conference; - - nm_event_set_text(event, msg); - - conference = nm_conference_list_find(user, guid); - if (conference == NULL) { - conference = nm_create_conference(guid); - - /* Add new conference to the list and the event */ - nm_conference_list_add(user, conference); - nm_event_set_conference(event, conference); - - /* Check to see if we have details for the event source yet */ - user_record = nm_find_user_record(user, nm_event_get_source(event)); - if (user_record) { - - /* Add a reference to the user record in our event object */ - nm_event_set_user_record(event, user_record); - - } else { - - /* Need to go to the server to get details for the user */ - rc = nm_send_get_details(user, nm_event_get_source(event), - _got_user_for_event, event); - if (rc == NM_OK) - rc = -1; /* Not done processing the event yet! */ - } - - nm_release_conference(conference); - - } - } - - if (msg) - g_free(msg); - - if (guid) - g_free(guid); - - return rc; -} - -/* Read the invite notify event, set up the event object, and - * get details for the event source if we don't have them yet. - */ -static NMERR_T -handle_conference_invite_notify(NMUser * user, NMEvent * event) -{ - NMERR_T rc = NM_OK; - guint32 size = 0; - char *guid = NULL; - NMConn *conn; - NMConference *conference; - NMUserRecord *user_record; - - conn = nm_user_get_conn(user); - - /* Read the conference guid */ - rc = nm_read_uint32(conn, &size); - if (size > 1000) return NMERR_PROTOCOL; - - if (rc == NM_OK) { - guid = g_new0(char, size + 1); - rc = nm_read_all(conn, guid, size); - } - - conference = nm_conference_list_find(user, guid); - if (conference) { - nm_event_set_conference(event, conference); - - /* Check to see if we have details for the event source yet */ - user_record = nm_find_user_record(user, nm_event_get_source(event)); - if (user_record) { - - /* Add a reference to the user record in our event object */ - nm_event_set_user_record(event, user_record); - - } else { - - /* Need to go to the server to get details for the user */ - rc = nm_send_get_details(user, nm_event_get_source(event), - _got_user_for_event, event); - if (rc == NM_OK) - rc = -1; /* Not done processing the event yet! */ - } - - } else { - rc = NMERR_CONFERENCE_NOT_FOUND; - } - - - if (guid) - g_free(guid); - - return rc; -} - -/* Read the conference reject event and set up the event object */ -static NMERR_T -handle_conference_reject(NMUser * user, NMEvent * event) -{ - NMERR_T rc = NM_OK; - guint32 size = 0; - char *guid = NULL; - NMConn *conn; - NMConference *conference; - - conn = nm_user_get_conn(user); - - /* Read the conference guid */ - rc = nm_read_uint32(conn, &size); - if (size > 1000) return NMERR_PROTOCOL; - - if (rc == NM_OK) { - guid = g_new0(char, size + 1); - rc = nm_read_all(conn, guid, size); - } - - if (rc == NM_OK) { - conference = nm_conference_list_find(user, guid); - if (conference) { - nm_event_set_conference(event, conference); - } else { - rc = NMERR_CONFERENCE_NOT_FOUND; - } - } - - if (guid) - g_free(guid); - - return rc; -} - -/* Read the conference left event, set up the event object, and - * remove the conference from the list if there are no more - * participants - */ -static NMERR_T -handle_conference_left(NMUser * user, NMEvent * event) -{ - NMERR_T rc = NM_OK; - guint32 size = 0, flags = 0; - char *guid = NULL; - NMConference *conference; - NMConn *conn; - - conn = nm_user_get_conn(user); - - /* Read the conference guid */ - rc = nm_read_uint32(conn, &size); - if (size > 1000) return NMERR_PROTOCOL; - - if (rc == NM_OK) { - guid = g_new0(char, size + 1); - rc = nm_read_all(conn, guid, size); - } - - /* Read the conference flags */ - if (rc == NM_OK) { - rc = nm_read_uint32(conn, &flags); - } - - if (rc == NM_OK) { - conference = nm_conference_list_find(user, guid); - if (conference) { - nm_event_set_conference(event, conference); - nm_conference_set_flags(conference, flags); - - nm_conference_remove_participant(conference, nm_event_get_source(event)); - if (nm_conference_get_participant_count(conference) == 0) { - nm_conference_list_remove(user, conference); - } - - } else { - rc = NMERR_CONFERENCE_NOT_FOUND; - } - } - - if (guid) - g_free(guid); - - return rc; -} - -/* Read the conference closed, set up the event object, and - * remove the conference from the list - */ -static NMERR_T -handle_conference_closed(NMUser * user, NMEvent * event) -{ - NMERR_T rc = NM_OK; - guint32 size = 0; - char *guid = NULL; - NMConference *conference; - NMConn *conn; - - conn = nm_user_get_conn(user); - - /* Read the conference guid */ - rc = nm_read_uint32(conn, &size); - if (size > 1000) return NMERR_PROTOCOL; - - if (rc == NM_OK) { - guid = g_new0(char, size + 1); - rc = nm_read_all(conn, guid, size); - } - - if (rc == NM_OK) { - conference = nm_conference_list_find(user, guid); - if (conference) { - nm_event_set_conference(event, conference); - nm_conference_list_remove(user, conference); - } else { - rc = NMERR_CONFERENCE_NOT_FOUND; - } - } - - if (guid) - g_free(guid); - - return rc; -} - -/* Read the conference joined event, set up the event object, and - * get details for the event source if we don't have them yet. - */ -static NMERR_T -handle_conference_joined(NMUser * user, NMEvent * event) -{ - NMERR_T rc = NM_OK; - guint32 size = 0, flags = 0; - char *guid = NULL; - NMConn *conn; - NMConference *conference; - NMUserRecord *user_record; - - conn = nm_user_get_conn(user); - - /* Read the conference guid */ - rc = nm_read_uint32(conn, &size); - if (size > 1000) return NMERR_PROTOCOL; - - if (rc == NM_OK) { - guid = g_new0(char, size + 1); - rc = nm_read_all(conn, guid, size); - } - - /* Read the conference flags */ - if (rc == NM_OK) { - rc = nm_read_uint32(conn, &flags); - } - - if (rc == NM_OK) { - conference = nm_conference_list_find(user, guid); - if (conference) { - nm_conference_set_flags(conference, flags); - - nm_event_set_conference(event, conference); - - /* Add the new user to the participants list */ - user_record = nm_find_user_record(user, nm_event_get_source(event)); - if (user_record) { - nm_conference_remove_participant(conference, - nm_user_record_get_dn(user_record)); - nm_conference_add_participant(conference, user_record); - } else { - - /* Need to go to the server to get details for the user */ - rc = nm_send_get_details(user, nm_event_get_source(event), - _got_user_for_conference, event); - if (rc == NM_OK) - rc = -1; /* Not done processing the event yet! */ - } - - } else { - rc = NMERR_CONFERENCE_NOT_FOUND; - } - } - - if (guid) - g_free(guid); - - return rc; -} - -/* Read the typing event and set up the event object */ -static NMERR_T -handle_typing(NMUser * user, NMEvent * event) -{ - NMERR_T rc = NM_OK; - guint32 size = 0; - char *guid = NULL; - NMConference *conference; - NMConn *conn; - - conn = nm_user_get_conn(user); - - /* Read the conference guid */ - rc = nm_read_uint32(conn, &size); - if (size > 1000) return NMERR_PROTOCOL; - - if (rc == NM_OK) { - guid = g_new0(char, size + 1); - rc = nm_read_all(conn, guid, size); - } - - if (rc == NM_OK) { - conference = nm_conference_list_find(user, guid); - if (conference) { - nm_event_set_conference(event, conference); - } else { - rc = NMERR_CONFERENCE_NOT_FOUND; - } - } - - if (guid) - g_free(guid); - - return rc; -} - -/* Read the event, set up the event object, and update - * the status in the user record (for the event source) - */ -static NMERR_T -handle_status_change(NMUser * user, NMEvent * event) -{ - NMERR_T rc = NM_OK; - guint16 status; - guint32 size; - char *text = NULL; - NMUserRecord *user_record; - NMConn *conn; - - conn = nm_user_get_conn(user); - - /* Read new status */ - rc = nm_read_uint16(conn, &status); - if (rc == NM_OK) { - - /* Read the status text */ - rc = nm_read_uint32(conn, &size); - if (size > 10000) return NMERR_PROTOCOL; - - if (rc == NM_OK) { - text = g_new0(char, size + 1); - rc = nm_read_all(conn, text, size); - } - } - - if (rc == NM_OK) { - nm_event_set_text(event, text); - - /* Get a reference to the user record and store the new status */ - user_record = nm_find_user_record(user, nm_event_get_source(event)); - if (user_record) { - nm_event_set_user_record(event, user_record); - nm_user_record_set_status(user_record, status, text); - } - } - - if (text) - g_free(text); - - return rc; -} - -/* Read the undeliverable event */ -static NMERR_T -handle_undeliverable_status(NMUser * user, NMEvent * event) -{ - NMERR_T rc = NM_OK; - guint32 size = 0; - char *guid = NULL; - NMConn *conn; - - conn = nm_user_get_conn(user); - - /* Read the conference guid */ - rc = nm_read_uint32(conn, &size); - if (size > 1000) return NMERR_PROTOCOL; - - if (rc == NM_OK) { - guid = g_new0(char, size + 1); - rc = nm_read_all(conn, guid, size); - } - - if (guid) - g_free(guid); - - return rc; -} - -/******************************************************************************* - * Event API -- see header file for comments - ******************************************************************************/ - -NMEvent * -nm_create_event(int type, const char *source, guint32 gmt) -{ - NMEvent *event = g_new0(NMEvent, 1); - - event->type = type; - event->gmt = gmt; - - if (source) - event->source = g_strdup(source); - - event->ref_count = 1; - - return event; -} - -void -nm_release_event(NMEvent * event) -{ - if (event == NULL) { - return; - } - - if (--(event->ref_count) == 0) { - - if (event->source) - g_free(event->source); - - if (event->conference) - nm_release_conference(event->conference); - - if (event->user_record) - nm_release_user_record(event->user_record); - - if (event->text) - g_free(event->text); - - g_free(event); - } -} - - -NMConference * -nm_event_get_conference(NMEvent * event) -{ - if (event) - return event->conference; - else - return NULL; -} - -void -nm_event_set_conference(NMEvent * event, NMConference * conference) -{ - if (event && conference) { - nm_conference_add_ref(conference); - event->conference = conference; - } -} - -NMUserRecord * -nm_event_get_user_record(NMEvent * event) -{ - if (event) - return event->user_record; - else - return NULL; -} - -void -nm_event_set_user_record(NMEvent * event, NMUserRecord * user_record) -{ - if (event && user_record) { - nm_user_record_add_ref(user_record); - event->user_record = user_record; - } -} - -const char * -nm_event_get_text(NMEvent * event) -{ - if (event) - return event->text; - else - return NULL; -} - -void -nm_event_set_text(NMEvent * event, const char *text) -{ - if (event) { - if (text) - event->text = g_strdup(text); - else - event->text = NULL; - } -} - -const char * -nm_event_get_source(NMEvent * event) -{ - if (event) - return event->source; - else - return NULL; -} - -int -nm_event_get_type(NMEvent * event) -{ - if (event) - return event->type; - else - return -1; -} - -time_t -nm_event_get_gmt(NMEvent * event) -{ - if (event) - return event->gmt; - else - return (time_t)-1; -} - -NMERR_T -nm_process_event(NMUser * user, int type) -{ - NMERR_T rc = NM_OK; - guint32 size = 0; - NMEvent *event = NULL; - char *source = NULL; - nm_event_cb cb; - NMConn *conn; - - if (user == NULL) - return NMERR_BAD_PARM; - - if (type < NMEVT_START || type > NMEVT_STOP) - return NMERR_PROTOCOL; - - conn = nm_user_get_conn(user); - - /* Read the event source */ - rc = nm_read_uint32(conn, &size); - if (rc == NM_OK) { - if (size > 1000000) { - /* Size is larger than our 1MB sanity check. Ignore it. */ - rc = NMERR_PROTOCOL; - } else { - source = g_new0(char, size); - - rc = nm_read_all(conn, source, size); - } - } - - /* Read the event data */ - if (rc == NM_OK) { - event = nm_create_event(type, source, time(0)); - - if (event) { - - switch (type) { - case NMEVT_STATUS_CHANGE: - rc = handle_status_change(user, event); - break; - - case NMEVT_RECEIVE_MESSAGE: - rc = handle_receive_message(user, event, FALSE); - break; - - case NMEVT_RECEIVE_AUTOREPLY: - rc = handle_receive_message(user, event, TRUE); - break; - - case NMEVT_USER_TYPING: - case NMEVT_USER_NOT_TYPING: - rc = handle_typing(user, event); - break; - - case NMEVT_CONFERENCE_LEFT: - rc = handle_conference_left(user, event); - break; - - case NMEVT_CONFERENCE_CLOSED: - rc = handle_conference_closed(user, event); - break; - - case NMEVT_CONFERENCE_JOINED: - rc = handle_conference_joined(user, event); - break; - - case NMEVT_CONFERENCE_INVITE: - rc = handle_conference_invite(user, event); - break; - - case NMEVT_CONFERENCE_REJECT: - rc = handle_conference_reject(user, event); - break; - - case NMEVT_CONFERENCE_INVITE_NOTIFY: - rc = handle_conference_invite_notify(user, event); - break; - - case NMEVT_UNDELIVERABLE_STATUS: - rc = handle_undeliverable_status(user, event); - break; - - case NMEVT_INVALID_RECIPIENT: - /* Nothing else to read, just callback */ - break; - - case NMEVT_USER_DISCONNECT: - /* Nothing else to read, just callback */ - break; - - case NMEVT_SERVER_DISCONNECT: - /* Nothing else to read, just callback */ - break; - - case NMEVT_RECEIVE_FILE: - case NMEVT_CONTACT_ADD: - /* Safely ignored for now */ - break; - - default: - purple_debug(PURPLE_DEBUG_INFO, "novell", - "Unknown event %d received.\n", type); - rc = NMERR_PROTOCOL; - break; - } - } - } - - if (rc == (NMERR_T)-1) { - /* -1 means that we are not ready to callback yet. */ - rc = NM_OK; - } else if (rc == NM_OK && (cb = nm_user_get_event_callback(user))) { - - cb(user, event); - } - - /* Cleanup */ - if (event) - nm_release_event(event); - if (source) - g_free(source); - - return rc; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmevent.h --- a/libpurple/protocols/novell/nmevent.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,178 +0,0 @@ -/* - * nmevent.h - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 __NM_EVENT_H__ -#define __NM_EVENT_H__ - -typedef struct _NMEvent NMEvent; - -#include "nmuser.h" -#include - -/** - * Defines for the event types - */ -#define NMEVT_INVALID_RECIPIENT 101 -#define NMEVT_UNDELIVERABLE_STATUS 102 -#define NMEVT_STATUS_CHANGE 103 -#define NMEVT_CONTACT_ADD 104 -#define NMEVT_CONFERENCE_CLOSED 105 -#define NMEVT_CONFERENCE_JOINED 106 -#define NMEVT_CONFERENCE_LEFT 107 -#define NMEVT_RECEIVE_MESSAGE 108 -#define NMEVT_RECEIVE_FILE 109 -#define NMEVT_USER_TYPING 112 -#define NMEVT_USER_NOT_TYPING 113 -#define NMEVT_USER_DISCONNECT 114 -#define NMEVT_SERVER_DISCONNECT 115 -#define NMEVT_CONFERENCE_RENAME 116 -#define NMEVT_CONFERENCE_INVITE 117 -#define NMEVT_CONFERENCE_INVITE_NOTIFY 118 -#define NMEVT_CONFERENCE_REJECT 119 -#define NMEVT_RECEIVE_AUTOREPLY 121 -#define NMEVT_START NMEVT_INVALID_RECIPIENT -#define NMEVT_STOP NMEVT_RECEIVE_AUTOREPLY - -/** - * Process the event. The event will be read, an NMEvent will - * be created, and the event callback will be called. - * - * @param user The main user structure. - * @param type The type of the event to read. - * - * @return NM_OK on success - */ -NMERR_T nm_process_event(NMUser * user, int type); - -/** - * Creates an NMEvent - * - * The NMEvent should be released by calling - * nm_release_event. - * - * @param type The event type, see defines above. - * @param source The DN of the event source. - * @param gmt The time that the event occurred. - * - * @return The new NMEvent - */ -NMEvent *nm_create_event(int type, const char *source, guint32 gmt); - -/** - * Releases an NMEvent - * - * @param event The event to release - * - */ -void nm_release_event(NMEvent * event); - -/** - * Sets the conference object for the given event. - * - * @param event The event. - * @param conference The conference to associate with the event. - * - */ -void nm_event_set_conference(NMEvent * event, NMConference * conference); - -/** - * Returns the conference object associated with the given event. This should not - * be released. If it needs to be kept around call nm_conference_addref(). - * - * @param event The event. - * - * @return The conference associated with the event, or NULL - * if no conference has been set for the event. - */ -NMConference *nm_event_get_conference(NMEvent * event); - -/** - * Sets the NMUserRecord object for the given event. - * The user record represents the event source. - * - * @param event The event. - * @param user_record The user record to associate with the event. - * - */ -void nm_event_set_user_record(NMEvent * event, NMUserRecord * user_record); - -/** - * Returns the NMUserRecord object associated with the given event. - * The user record represents the event source. This should not - * be released. If it needs to be kept around call - * nm_user_record_add_ref(). - * - * @param event The event. - * - * @return The user record associated with the event, or NULL - * if no user record has been set for the event. - */ -NMUserRecord *nm_event_get_user_record(NMEvent * event); - -/** - * Sets the text to associate with the given event. - * - * @param event The event. - * @param text The text to associate with the event. - * - */ -void nm_event_set_text(NMEvent * event, const char *text); - -/** - * Returns the text associated with the given event. - * - * @param event The event. - * - * @return The text associated with the event, or NULL - * if no text has been set for the event. - */ -const char *nm_event_get_text(NMEvent * event); - -/** - * Returns the source of the event (this will be the full DN of the - * event source). - * - * @param event The event. - * - * @return The full DN of the event's source. - */ -const char *nm_event_get_source(NMEvent * event); - -/** - * Returns the type of the event. See the defines above for - * a list of possible event types. - * - * @param event The event. - * - * @return The type of the event. - * - */ -int nm_event_get_type(NMEvent * event); - -/** - * Returns the time that the event took place. - * - * @param event The event. - * - * @return The timestamp for the event. - */ -time_t nm_event_get_gmt(NMEvent * event); - -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmfield.c --- a/libpurple/protocols/novell/nmfield.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,361 +0,0 @@ -/* - * nmfield.c - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 -#include -#include "nmfield.h" - -/* Free a field value and tag */ -static void _free_field(NMField * field); - -/* Free a field value */ -static void _free_field_value(NMField * field); - -/* Make a deep copy of the field */ -static void _copy_field(NMField * dest, NMField * src); - -/* Make a deep copy of the field's value */ -static void _copy_field_value(NMField * dest, NMField * src); - -/* Create a string from a value -- for debugging */ -static char *_value_to_string(NMField * field); - -static NMField * -_add_blank_field(NMField *fields, guint32 count) -{ - guint32 new_len; - - if (fields == NULL) { - fields = g_new0(NMField, 10); - fields->len = 10; - } else { - if (fields->len < count + 2) { - new_len = count + 10; - fields = g_realloc(fields, new_len * sizeof(NMField)); - fields->len = new_len; - } - } - return fields; -} - -NMField * -nm_field_add_number(NMField * fields, const char *tag, guint32 size, guint8 method, - guint8 flags, guint32 value, guint8 type) -{ - guint32 count; - NMField *field; - - count = nm_count_fields(fields); - fields = _add_blank_field(fields, count); - - field = &(fields[count]); - field->tag = g_strdup(tag); - field->size = size; - field->method = method; - field->flags = flags; - field->value = value; - field->type = type; - - /* Null terminate the field array */ - field = &((fields)[count + 1]); - field->tag = NULL; - field->value = 0; - field->ptr_value = NULL; - - return fields; -} - -NMField * -nm_field_add_pointer(NMField * fields, const char *tag, guint32 size, guint8 method, - guint8 flags, gpointer value, guint8 type) -{ - guint32 count; - NMField *field = NULL; - - count = nm_count_fields(fields); - fields = _add_blank_field(fields, count); - - field = &(fields[count]); - field->tag = g_strdup(tag); - field->size = size; - field->method = method; - field->flags = flags; - field->ptr_value = value; - field->type = type; - - /* Null terminate the field array */ - field = &((fields)[count + 1]); - field->tag = NULL; - field->value = 0; - field->ptr_value = NULL; - - return fields; -} - -guint32 -nm_count_fields(NMField * fields) -{ - guint32 count = 0; - - if (fields) { - while (fields->tag != NULL) { - count++; - fields++; - } - } - - return count; -} - -void -nm_free_fields(NMField ** fields) -{ - NMField *field = NULL; - - if ((fields == NULL) || (*fields == NULL)) - return; - - field = *fields; - - while (field->tag != NULL) { - _free_field(field); - field++; - } - - g_free(*fields); - *fields = NULL; -} - - -static void -_free_field(NMField * field) -{ - if (field == NULL) - return; - - _free_field_value(field); - g_free(field->tag); -} - -static void -_free_field_value(NMField * field) -{ - if (field == NULL) - return; - - switch (field->type) { - case NMFIELD_TYPE_BINARY: - case NMFIELD_TYPE_UTF8: - case NMFIELD_TYPE_DN: - g_free(field->ptr_value); - break; - - case NMFIELD_TYPE_ARRAY: - case NMFIELD_TYPE_MV: - nm_free_fields((NMField **)&field->ptr_value); - break; - - default: - break; - } - - field->size = 0; - field->ptr_value = NULL; -} - -NMField * -nm_locate_field(char *tag, NMField * fields) -{ - NMField *ret_fields = NULL; - - if ((fields == NULL) || (tag == NULL)) { - return NULL; - } - - while (fields->tag != NULL) { - if (g_ascii_strcasecmp(fields->tag, tag) == 0) { - ret_fields = fields; - break; - } - fields++; - } - - return ret_fields; -} - -NMField * -nm_copy_field_array(NMField * src) -{ - NMField *ptr = NULL; - NMField *dest = NULL; - int count; - - if (src != NULL) { - count = nm_count_fields(src) + 1; - dest = g_new0(NMField, count); - dest->len = count; - ptr = dest; - while (src->tag != NULL) { - _copy_field(ptr, src); - ptr++; - src++; - } - } - - return dest; -} - -static void -_copy_field(NMField * dest, NMField * src) -{ - dest->type = src->type; - dest->flags = src->flags; - dest->method = src->method; - dest->tag = g_strdup(src->tag); - _copy_field_value(dest, src); -} - -static void -_copy_field_value(NMField * dest, NMField * src) -{ - dest->type = src->type; - switch (dest->type) { - case NMFIELD_TYPE_UTF8: - case NMFIELD_TYPE_DN: - if (src->size == 0 && src->ptr_value != NULL) { - src->size = strlen((char *) src->ptr_value) + 1; - } - /* fall through */ - case NMFIELD_TYPE_BINARY: - if (src->size != 0 && src->ptr_value != NULL) { - dest->ptr_value = g_new0(char, src->size); - memcpy(dest->ptr_value, src->ptr_value, src->size); - } - break; - - case NMFIELD_TYPE_ARRAY: - case NMFIELD_TYPE_MV: - dest->ptr_value = nm_copy_field_array((NMField *)src->ptr_value); - break; - - default: - /* numeric value */ - dest->value = src->value; - break; - } - - dest->size = src->size; -} - -void -nm_remove_field(NMField * field) -{ - NMField *tmp; - guint32 len; - - if ((field != NULL) && (field->tag != NULL)) { - _free_field(field); - - /* Move fields down */ - tmp = field + 1; - while (1) { - /* Don't overwrite the size of the array */ - len = field->len; - - *field = *tmp; - - field->len = len; - - if (tmp->tag == NULL) - break; - - field++; - tmp++; - } - } -} - -void -nm_print_fields(NMField * fields) -{ - char *str = NULL; - NMField *field = fields; - - if (fields == NULL) - return; - - while (field->tag != NULL) { - if (field->type == NMFIELD_TYPE_ARRAY || field->type == NMFIELD_TYPE_MV) { - printf("Subarray START: %s Method = %d\n", field->tag, field->method); - nm_print_fields((NMField *) field->ptr_value); - printf("Subarray END: %s\n", field->tag); - } else { - str = _value_to_string(field); - printf("Tag=%s;Value=%s\n", field->tag, str); - g_free(str); - str = NULL; - } - field++; - } - -} - -static char * -_value_to_string(NMField * field) -{ - char *value = NULL; - - if (field == NULL) - return NULL; - - /* This is a single value attribute */ - if (((field->type == NMFIELD_TYPE_UTF8) || - (field->type == NMFIELD_TYPE_DN)) && (field->ptr_value != NULL)) { - value = g_strdup((const char *) field->ptr_value); - } else if (field->type == NMFIELD_TYPE_BINARY && field->ptr_value != NULL) { - value = g_new0(char, field->size); - memcpy(value, (const char *) field->ptr_value, field->size); - } else if (field->type == NMFIELD_TYPE_BOOL) { - if (field->value) { - value = g_strdup(NM_FIELD_TRUE); - } else { - value = g_strdup(NM_FIELD_FALSE); - } - } else { - switch (field->type) { - case NMFIELD_TYPE_BYTE: - case NMFIELD_TYPE_WORD: - case NMFIELD_TYPE_DWORD: - value = g_strdup_printf("%ld", (long) field->value); - break; - - case NMFIELD_TYPE_UBYTE: - case NMFIELD_TYPE_UWORD: - case NMFIELD_TYPE_UDWORD: - value = g_strdup_printf("%lu", (unsigned long) field->value); - break; - } - } - - if (value == NULL) - value = g_strdup("NULL"); - - return value; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmfield.h --- a/libpurple/protocols/novell/nmfield.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,225 +0,0 @@ -/* - * nmfield.h - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 NMFIELD_H -#define NMFIELD_H - -#include - -typedef struct NMField_t -{ - char *tag; /* Field tag */ - guint8 method; /* Method of the field */ - guint8 flags; /* Flags */ - guint8 type; /* Type of value */ - guint32 size; /* Size of value if binary */ - guint32 value; /* Value of a numeric field */ - gpointer ptr_value; /* Value of a string or sub array field */ - guint32 len; /* Length of the array */ -} NMField; - -/* Field types */ -#define NMFIELD_TYPE_INVALID 0 -#define NMFIELD_TYPE_NUMBER 1 -#define NMFIELD_TYPE_BINARY 2 -#define NMFIELD_TYPE_BYTE 3 -#define NMFIELD_TYPE_UBYTE 4 -#define NMFIELD_TYPE_WORD 5 -#define NMFIELD_TYPE_UWORD 6 -#define NMFIELD_TYPE_DWORD 7 -#define NMFIELD_TYPE_UDWORD 8 -#define NMFIELD_TYPE_ARRAY 9 -#define NMFIELD_TYPE_UTF8 10 -#define NMFIELD_TYPE_BOOL 11 -#define NMFIELD_TYPE_MV 12 -#define NMFIELD_TYPE_DN 13 - -/* Field methods */ -#define NMFIELD_METHOD_VALID 0 -#define NMFIELD_METHOD_IGNORE 1 -#define NMFIELD_METHOD_DELETE 2 -#define NMFIELD_METHOD_DELETE_ALL 3 -#define NMFIELD_METHOD_EQUAL 4 -#define NMFIELD_METHOD_ADD 5 -#define NMFIELD_METHOD_UPDATE 6 -#define NMFIELD_METHOD_GTE 10 -#define NMFIELD_METHOD_LTE 12 -#define NMFIELD_METHOD_NE 14 -#define NMFIELD_METHOD_EXIST 15 -#define NMFIELD_METHOD_NOTEXIST 16 -#define NMFIELD_METHOD_SEARCH 17 -#define NMFIELD_METHOD_MATCHBEGIN 19 -#define NMFIELD_METHOD_MATCHEND 20 -#define NMFIELD_METHOD_NOT_ARRAY 40 -#define NMFIELD_METHOD_OR_ARRAY 41 -#define NMFIELD_METHOD_AND_ARRAY 42 - -/* Attribute Names (field tags) */ -#define NM_A_IP_ADDRESS "nnmIPAddress" -#define NM_A_PORT "nnmPort" -#define NM_A_FA_FOLDER "NM_A_FA_FOLDER" -#define NM_A_FA_CONTACT "NM_A_FA_CONTACT" -#define NM_A_FA_CONVERSATION "NM_A_FA_CONVERSATION" -#define NM_A_FA_MESSAGE "NM_A_FA_MESSAGE" -#define NM_A_FA_CONTACT_LIST "NM_A_FA_CONTACT_LIST" -#define NM_A_FA_RESULTS "NM_A_FA_RESULTS" -#define NM_A_FA_INFO_DISPLAY_ARRAY "NM_A_FA_INFO_DISPLAY_ARRAY" -#define NM_A_FA_USER_DETAILS "NM_A_FA_USER_DETAILS" -#define NM_A_SZ_OBJECT_ID "NM_A_SZ_OBJECT_ID" -#define NM_A_SZ_PARENT_ID "NM_A_SZ_PARENT_ID" -#define NM_A_SZ_SEQUENCE_NUMBER "NM_A_SZ_SEQUENCE_NUMBER" -#define NM_A_SZ_TYPE "NM_A_SZ_TYPE" -#define NM_A_SZ_STATUS "NM_A_SZ_STATUS" -#define NM_A_SZ_STATUS_TEXT "NM_A_SZ_STATUS_TEXT" -#define NM_A_SZ_DN "NM_A_SZ_DN" -#define NM_A_SZ_DISPLAY_NAME "NM_A_SZ_DISPLAY_NAME" -#define NM_A_SZ_USERID "NM_A_SZ_USERID" -#define NM_A_SZ_CREDENTIALS "NM_A_SZ_CREDENTIALS" -#define NM_A_SZ_MESSAGE_BODY "NM_A_SZ_MESSAGE_BODY" -#define NM_A_SZ_MESSAGE_TEXT "NM_A_SZ_MESSAGE_TEXT" -#define NM_A_UD_MESSAGE_TYPE "NM_A_UD_MESSAGE_TYPE" -#define NM_A_FA_PARTICIPANTS "NM_A_FA_PARTICIPANTS" -#define NM_A_FA_INVITES "NM_A_FA_INVITES" -#define NM_A_FA_EVENT "NM_A_FA_EVENT" -#define NM_A_UD_COUNT "NM_A_UD_COUNT" -#define NM_A_UD_DATE "NM_A_UD_DATE" -#define NM_A_UD_EVENT "NM_A_UD_EVENT" -#define NM_A_B_NO_CONTACTS "NM_A_B_NO_CONTACTS" -#define NM_A_B_NO_CUSTOMS "NM_A_B_NO_CUSTOMS" -#define NM_A_B_NO_PRIVACY "NM_A_B_NO_PRIVACY" -#define NM_A_UW_STATUS "NM_A_UW_STATUS" -#define NM_A_UD_OBJECT_ID "NM_A_UD_OBJECT_ID" -#define NM_A_SZ_TRANSACTION_ID "NM_A_SZ_TRANSACTION_ID" -#define NM_A_SZ_RESULT_CODE "NM_A_SZ_RESULT_CODE" -#define NM_A_UD_BUILD "NM_A_UD_BUILD" -#define NM_A_SZ_AUTH_ATTRIBUTE "NM_A_SZ_AUTH_ATTRIBUTE" -#define NM_A_UD_KEEPALIVE "NM_A_UD_KEEPALIVE" -#define NM_A_SZ_USER_AGENT "NM_A_SZ_USER_AGENT" -#define NM_A_BLOCKING "nnmBlocking" -#define NM_A_BLOCKING_DENY_LIST "nnmBlockingDenyList" -#define NM_A_BLOCKING_ALLOW_LIST "nnmBlockingAllowList" -#define NM_A_SZ_BLOCKING_ALLOW_ITEM "NM_A_SZ_BLOCKING_ALLOW_ITEM" -#define NM_A_SZ_BLOCKING_DENY_ITEM "NM_A_SZ_BLOCKING_DENY_ITEM" -#define NM_A_LOCKED_ATTR_LIST "nnmLockedAttrList" - -#define NM_PROTOCOL_VERSION 2 - -#define NM_FIELD_TRUE "1" -#define NM_FIELD_FALSE "0" - -#define NMFIELD_MAX_STR_LENGTH 32768 - -/** - * Count the number of fields - * - * @param fields Field array - * - * @return The number of fields in the array. - * - */ -guint32 nm_count_fields(NMField * fields); - -/** - * Add a field to the field array. The field should be of type NMFIELD_TYPE_UTF8, - * NMFIELD_TYPE_DN, NMFIELD_TYPE_ARRAY, or NMFIELD_TYPE_MV - * - * NOTE: field array that is passed in may be realloc'd so you should use - * the returned field array pointer not the passed in pointer after calling - * this function. - * - * @param fields Field array - * @param tag Tag for the new field - * @param size Size of the field value (if type = binary) - * @param method Field method (see method defines above) - * @param flags Flags for new field - * @param value The value of the field - * @param type The type of the field value - * - * @return Pointer to the updated field array - * - */ -NMField *nm_field_add_pointer(NMField *fields, const char *tag, guint32 size, guint8 method, - guint8 flags, gpointer value, guint8 type); - -/** - * Add a numeric field to the field array. - * - * NOTE: field array that is passed in may be realloc'd so you should use - * the returned field array pointer not the passed in pointer after calling - * this function. - * - * @param fields Field array - * @param tag Tag for the new field - * @param size Size of the field value (if type = binary) - * @param method Field method (see method defines above) - * @param flags Flags for new field - * @param value The value of the field - * @param type The type of the field value - * - * @return Pointer to the updated field array - * - */ -NMField *nm_field_add_number(NMField *fields, const char *tag, guint32 size, guint8 method, - guint8 flags, guint32 value, guint8 type); - -/** - * Recursively free an array of fields and set pointer to NULL. - * - * @param fields Pointer to a field array - * - */ -void nm_free_fields(NMField ** fields); - -/** - * Find first field with given tag in field array. - * - * Note: this will only work for 7-bit ascii tags (which is all that - * we use currently). - * - * @param tag Tag to search for - * @param fields Field array - * - * @return The first matching field, or NULL if no fields match. - * - */ -NMField *nm_locate_field(char *tag, NMField * fields); - -/** - * Make a deep copy of a field array - * - * @param src The array to copy - * - * @return The new (copied) array, which must be freed. - * - */ -NMField *nm_copy_field_array(NMField * src); - -/** - * Remove a field and move other fields up to fill the gap - * - * @param field The field to remove - * - */ -void nm_remove_field(NMField * field); - -/* Print a field array (for debugging purposes) */ -void nm_print_fields(NMField * fields); - -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmmessage.c --- a/libpurple/protocols/novell/nmmessage.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +0,0 @@ -/* - * nmmessage.c - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 "nmmessage.h" - -struct _NMMessage -{ - NMConference *conference; - char *text; - guint32 ref_count; -}; - - -/** Message API **/ - -NMMessage * -nm_create_message(const char *text) -{ - NMMessage *msg = g_new0(NMMessage, 1); - - if (text) - msg->text = g_strdup(text); - - msg->ref_count = 1; - return msg; -} - -void -nm_message_add_ref(NMMessage * msg) -{ - if (msg) - msg->ref_count++; -} - -void -nm_release_message(NMMessage * msg) -{ - if (msg && (--(msg->ref_count) == 0)) { - if (msg->text) - g_free(msg->text); - - if (msg->conference) - nm_release_conference(msg->conference); - - g_free(msg); - } -} - -const char * -nm_message_get_text(NMMessage * msg) -{ - if (msg == NULL) - return NULL; - - return msg->text; -} - -void -nm_message_set_conference(NMMessage * msg, NMConference * conf) -{ - if (msg == NULL || conf == NULL) - return; - - /* Need to ref the conference first so that it doesn't - * get released out from under us - */ - nm_conference_add_ref(conf); - - msg->conference = conf; -} - -NMConference * -nm_message_get_conference(NMMessage * msg) -{ - if (msg == NULL) - return NULL; - - return msg->conference; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmmessage.h --- a/libpurple/protocols/novell/nmmessage.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,83 +0,0 @@ -/* - * nmmessage.h - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 __NM_MESSAGE_H__ -#define __NM_MESSAGE_H__ - -typedef struct _NMMessage NMMessage; - -#include "nmconference.h" - -/** - * Creates a new message. - * - * The returned message should be released by calling - * nm_release_message - * - * @param text The message text - * @return A newly allocated message - */ -NMMessage *nm_create_message(const char *text); - -/** - * Increment the reference count for the message object. - * - * @param msg The message - */ -void nm_message_add_ref(NMMessage * msg); - -/** - * Releases a message. - * - * @param msg The message - */ -void nm_release_message(NMMessage * msg); - -/** - * Returns the message text - * - * @param msg The message - * @return The message text - */ -const char *nm_message_get_text(NMMessage * msg); - -/** - * Sets the conference object for a message - * - * @param msg The message - * @param conf The conference to associate with the message - * @return RVALUE_OK on success - */ -void nm_message_set_conference(NMMessage * msg, NMConference * conf); - -/** - * Returns the conference object associated with the message - * - * Note: this does not increment the reference count for the - * conference and the conference should NOT be released with - * nm_release_conference. If the reference needs to be kept - * around nm_conference_add_ref should be called. - * - * @param msg The message - * @return The conference associated with this message - */ -NMConference *nm_message_get_conference(NMMessage * msg); - -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmrequest.c --- a/libpurple/protocols/novell/nmrequest.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,161 +0,0 @@ -/* - * nmrequest.c - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 "nmrequest.h" - -static int count = 0; - -struct _NMRequest -{ - int trans_id; - char *cmd; - int gmt; - gpointer data; - gpointer user_define; - nm_response_cb callback; - int ref_count; - NMERR_T ret_code; -}; - -NMRequest *nm_create_request(const char *cmd, int trans_id, int gmt, nm_response_cb cb, - gpointer resp_data, gpointer user_define) -{ - NMRequest *req; - - if (cmd == NULL) - return NULL; - - req = g_new0(NMRequest, 1); - req->cmd = g_strdup(cmd); - req->trans_id = trans_id; - req->gmt = gmt; - req->callback = cb; - req->data = resp_data; - req->user_define = user_define; - req->ref_count = 1; - - purple_debug_info("novell", "Creating NMRequest instance, total=%d\n", ++count); - - return req; -} - -void -nm_release_request(NMRequest * req) -{ - if (req && (--req->ref_count == 0)) { - if (req->cmd) - g_free(req->cmd); - g_free(req); - - purple_debug_info("novell", - "Releasing NMRequest instance, total=%d\n", --count); - } - -} - -void -nm_request_add_ref(NMRequest * req) -{ - if (req) - req->ref_count++; -} - -void -nm_request_set_callback(NMRequest * req, nm_response_cb callback) -{ - if (req) - req->callback = callback; -} - -void -nm_request_set_data(NMRequest * req, gpointer data) -{ - if (req) - req->data = data; -} - -void -nm_request_set_user_define(NMRequest * req, gpointer user_define) -{ - if (req) - req->user_define = user_define; -} - -int -nm_request_get_trans_id(NMRequest * req) -{ - if (req) - return req->trans_id; - else - return -1; -} - -const char * -nm_request_get_cmd(NMRequest * req) -{ - if (req == NULL) - return NULL; - - return req->cmd; -} - -gpointer -nm_request_get_data(NMRequest * req) -{ - if (req == NULL) - return NULL; - - return req->data; -} - -gpointer -nm_request_get_user_define(NMRequest * req) -{ - if (req == NULL) - return NULL; - - return req->user_define; -} - -nm_response_cb -nm_request_get_callback(NMRequest * req) -{ - if (req == NULL) - return NULL; - - return req->callback; -} - - -void -nm_request_set_ret_code(NMRequest * req, NMERR_T rc) -{ - if (req) - req->ret_code = rc; -} - -NMERR_T -nm_request_get_ret_code(NMRequest * req) -{ - if (req) - return req->ret_code; - else - return (NMERR_T) - 1; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmrequest.h --- a/libpurple/protocols/novell/nmrequest.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,150 +0,0 @@ -/* - * nmrequest.h - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 __NM_REQUEST_H__ -#define __NM_REQUEST_H__ - -typedef struct _NMRequest NMRequest; - -#include "nmuser.h" - -/** - * Create a new request object. Object must be release with nm_release_object. - * - * @param cmd The request command string (e.g. "login") - * @param trans_id The request transaction id - * @param gmt The time in seconds that the request was created - * - * @return The new request object - */ -NMRequest *nm_create_request(const char *cmd, int trans_id, int gmt, nm_response_cb cb, - gpointer resp_data, gpointer user_define); - -/** - * Release a request object. - * - * @param req The request to release - */ -void nm_release_request(NMRequest * req); - -/** - * Add a new reference to this object. This reference must be released by - * a call to nm_release_object. - * - * @param req The request object - */ -void nm_request_add_ref(NMRequest * req); - -/** - * Set the response callback for this request object. This is the callback - * that will be made when we get a response from the server. - * - * @param req The request object - * @param callback The response callback - * - */ -void nm_request_set_callback(NMRequest * req, nm_response_cb callback); - -/** - * Set the response data. This will be set differently depending on - * the request type (for example to nm_send_get_details will set this - * to be the newly create NMUserRecord object). - * - * @param req The request object - * @param data Pointer to some data - * - */ -void nm_request_set_data(NMRequest * req, gpointer data); - -/** - * Set the user defined data. This is the data that the client - * passes to the various nm_send_* functions. We will pass it - * back when we make the callback. - * - * @param req The request object - * @param user_define Pointer to some data - * - */ -void nm_request_set_user_define(NMRequest * req, gpointer user_define); - -/** - * Set the return code. This is the return code that we received in - * the server response fields. - * - * @param req The request object - * @param rc The return code to set - */ -void nm_request_set_ret_code(NMRequest * req, NMERR_T rc); - -/** - * Get the transaction id for this request. - * - * @param req The request object - * - * @return The transaction id. - */ -int nm_request_get_trans_id(NMRequest * req); - -/** - * Get the command (request type) for this request. - * - * @param req The request object - * - * @return The request cmd - */ -const char *nm_request_get_cmd(NMRequest * req); - -/** - * Get the response data for this request - * - * @param req The request object - * - * @return The response data - */ -gpointer nm_request_get_data(NMRequest * req); - -/** - * Get the user defined data for this request - * - * @param req The request object - * - * @return The user defined data - */ -gpointer nm_request_get_user_define(NMRequest * req); - -/** - * Get the response callback for this request - * - * @param req The request object - * - * @return The response callback - */ -nm_response_cb nm_request_get_callback(NMRequest * req); - -/** - * Get the return code - * - * @param req The request object - * - * @return The return code (from the response fields) - */ -NMERR_T nm_request_get_ret_code(NMRequest * req); - -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmrtf.c --- a/libpurple/protocols/novell/nmrtf.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,830 +0,0 @@ -/* - * nmrtf.c - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 - * - */ - -/* This code was adapted from the sample RTF reader found here: - * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnrtfspec/html/rtfspec.asp - */ - -#include -#include -#include -#include -#include -#include -#include "nmrtf.h" -#include "debug.h" -#include "util.h" - -/* Internal RTF parser error codes */ -#define NMRTF_OK 0 /* Everything's fine! */ -#define NMRTF_STACK_UNDERFLOW 1 /* Unmatched '}' */ -#define NMRTF_STACK_OVERFLOW 2 /* Too many '{' -- memory exhausted */ -#define NMRTF_UNMATCHED_BRACE 3 /* RTF ended during an open group. */ -#define NMRTF_INVALID_HEX 4 /* invalid hex character found in data */ -#define NMRTF_BAD_TABLE 5 /* RTF table (sym or prop) invalid */ -#define NMRTF_ASSERTION 6 /* Assertion failure */ -#define NMRTF_EOF 7 /* End of file reached while reading RTF */ -#define NMRTF_CONVERT_ERROR 8 /* Error converting text */ - -#define NMRTF_MAX_DEPTH 256 - -typedef enum -{ - NMRTF_STATE_NORMAL, - NMRTF_STATE_SKIP, - NMRTF_STATE_FONTTABLE, - NMRTF_STATE_BIN, - NMRTF_STATE_HEX -} NMRtfState; /* Rtf State */ - -/* Property types that we care about */ -typedef enum -{ - NMRTF_PROP_FONT_IDX, - NMRTF_PROP_FONT_CHARSET, - NMRTF_PROP_MAX -} NMRtfProperty; - -typedef enum -{ - NMRTF_SPECIAL_BIN, - NMRTF_SPECIAL_HEX, - NMRTF_SPECIAL_UNICODE, - NMRTF_SPECIAL_SKIP -} NMRtfSpecialKwd; - -typedef enum -{ - NMRTF_DEST_FONTTABLE, - NMRTF_DEST_SKIP -} NMRtfDestinationType; - -typedef enum -{ - NMRTF_KWD_CHAR, - NMRTF_KWD_DEST, - NMRTF_KWD_PROP, - NMRTF_KWD_SPEC -} NMRtfKeywordType; - -typedef struct _NMRTFCharProp -{ - /* All we care about for now is the font. - * bold, italic, underline, etc. should be - * added here - */ - int font_idx; - int font_charset; -} NMRtfCharProp; - -typedef struct _NMRtfStateSave -{ - NMRtfCharProp chp; - NMRtfState rds; - NMRtfState ris; -} NMRtfStateSave; - -typedef struct _NMRtfSymbol -{ - char *keyword; /* RTF keyword */ - int default_val; /* default value to use */ - gboolean pass_default; /* true to use default value from this table */ - NMRtfKeywordType kwd_type; /* the type of the keyword */ - int action; /* property type if the keyword represents a property */ - /* destination type if the keyword represents a destination */ - /* character to print if the keyword represents a character */ -} NMRtfSymbol; - - -typedef struct _NMRtfFont -{ - int number; - char *name; - int charset; -} NMRtfFont; - -/* RTF Context */ -struct _NMRtfContext -{ - NMRtfState rds; /* destination state */ - NMRtfState ris; /* internal state */ - NMRtfCharProp chp; /* current character properties (ie. font, bold, italic, etc.) */ - GSList *font_table; /* the font table */ - GSList *saved; /* saved state stack */ - int param; /* numeric parameter for the current keyword */ - long bytes_to_skip; /* number of bytes to skip (after encountering \bin) */ - int depth; /* how many groups deep are we */ - gboolean skip_unknown; /* if true, skip any unknown destinations (this is set after encountering '\*') */ - char *input; /* input string */ - guchar nextch; /* next char in input */ - gboolean nextch_available; /* nextch value is set */ - GString *ansi; /* Temporary ansi text, will be convert/flushed to the output string */ - GString *output; /* The plain text UTF8 string */ -}; - -static int rtf_parse(NMRtfContext *ctx); -static int rtf_push_state(NMRtfContext *ctx); -static int rtf_pop_state(NMRtfContext *ctx); -static NMRtfFont *rtf_get_font(NMRtfContext *ctx, int index); -static int rtf_get_char(NMRtfContext *ctx, guchar *ch); -static int rtf_unget_char(NMRtfContext *ctx, guchar ch); -static int rtf_flush_data(NMRtfContext *ctx); -static int rtf_parse_keyword(NMRtfContext *ctx); -static int rtf_dispatch_control(NMRtfContext *ctx, char *keyword, int param, gboolean param_set); -static int rtf_dispatch_char(NMRtfContext *ctx, guchar ch); -static int rtf_dispatch_unicode_char(NMRtfContext *ctx, gunichar ch); -static int rtf_print_char(NMRtfContext *ctx, guchar ch); -static int rtf_print_unicode_char(NMRtfContext *ctx, gunichar ch); -static int rtf_change_destination(NMRtfContext *ctx, NMRtfDestinationType dest); -static int rtf_dispatch_special(NMRtfContext *ctx, NMRtfSpecialKwd special); -static int rtf_apply_property(NMRtfContext *ctx, NMRtfProperty prop, int val); - -/* RTF parser tables */ - -/* Keyword descriptions */ -NMRtfSymbol rtf_symbols[] = { - /* keyword, default, pass_default, keyword_type, action */ - {"fonttbl", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_FONTTABLE}, - {"f", 0, FALSE, NMRTF_KWD_PROP, NMRTF_PROP_FONT_IDX}, - {"fcharset", 0, FALSE, NMRTF_KWD_PROP, NMRTF_PROP_FONT_CHARSET}, - {"par", 0, FALSE, NMRTF_KWD_CHAR, 0x0a}, - {"line", 0, FALSE, NMRTF_KWD_CHAR, 0x0a}, - {"\0x0a", 0, FALSE, NMRTF_KWD_CHAR, 0x0a}, - {"\0x0d", 0, FALSE, NMRTF_KWD_CHAR, 0x0a}, - {"tab", 0, FALSE, NMRTF_KWD_CHAR, 0x09}, - {"\r", 0, FALSE, NMRTF_KWD_CHAR, '\r'}, - {"\n", 0, FALSE, NMRTF_KWD_CHAR, '\n'}, - {"ldblquote",0, FALSE, NMRTF_KWD_CHAR, '"'}, - {"rdblquote",0, FALSE, NMRTF_KWD_CHAR, '"'}, - {"{", 0, FALSE, NMRTF_KWD_CHAR, '{'}, - {"}", 0, FALSE, NMRTF_KWD_CHAR, '}'}, - {"\\", 0, FALSE, NMRTF_KWD_CHAR, '\\'}, - {"bin", 0, FALSE, NMRTF_KWD_SPEC, NMRTF_SPECIAL_BIN}, - {"*", 0, FALSE, NMRTF_KWD_SPEC, NMRTF_SPECIAL_SKIP}, - {"'", 0, FALSE, NMRTF_KWD_SPEC, NMRTF_SPECIAL_HEX}, - {"u", 0, FALSE, NMRTF_KWD_SPEC, NMRTF_SPECIAL_UNICODE}, - {"colortbl", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"author", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"buptim", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"comment", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"creatim", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"doccomm", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"footer", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"footerf", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"footerl", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"footerr", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"footnote", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"ftncn", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"ftnsep", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"ftnsepc", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"header", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"headerf", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"headerl", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"headerr", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"info", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"keywords", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"operator", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"pict", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"printim", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"private1", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"revtim", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"rxe", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"stylesheet", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"subject", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"tc", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"title", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"txe", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP}, - {"xe", 0, FALSE, NMRTF_KWD_DEST, NMRTF_DEST_SKIP} -}; -int table_size = sizeof(rtf_symbols) / sizeof(NMRtfSymbol); - -NMRtfContext * -nm_rtf_init() -{ - NMRtfContext *ctx = g_new0(NMRtfContext, 1); - ctx->nextch_available = FALSE; - ctx->ansi = g_string_new(""); - ctx->output = g_string_new(""); - return ctx; -} - -char * -nm_rtf_strip_formatting(NMRtfContext *ctx, const char *input) -{ - int status; - - ctx->input = (char *)input; - status = rtf_parse(ctx); - if (status == NMRTF_OK) - return g_strdup(ctx->output->str); - - purple_debug_info("novell", "RTF parser failed with error code %d\n", status); - return NULL; -} - -void -nm_rtf_deinit(NMRtfContext *ctx) -{ - GSList *node; - NMRtfFont *font; - NMRtfStateSave *save; - - if (ctx) { - for (node = ctx->font_table; node; node = node->next) { - font = node->data; - g_free(font->name); - g_free(font); - node->data = NULL; - } - g_slist_free(ctx->font_table); - for (node = ctx->saved; node; node = node->next) { - save = node->data; - g_free(save); - node->data = NULL; - } - g_slist_free(ctx->saved); - g_string_free(ctx->ansi, TRUE); - g_string_free(ctx->output, TRUE); - g_free(ctx); - } -} - -static const char * -get_current_encoding(NMRtfContext *ctx) -{ - NMRtfFont *font; - - font = rtf_get_font(ctx, ctx->chp.font_idx); - - switch (font->charset) { - case 0: - return "CP1252"; - case 77: - return "MACINTOSH"; - case 78: - return "SJIS"; - case 128: - return "CP932"; - case 129: - return "CP949"; - case 130: - return "CP1361"; - case 134: - return "CP936"; - case 136: - return "CP950"; - case 161: - return "CP1253"; - case 162: - return "CP1254"; - case 163: - return "CP1258"; - case 181: - case 177: - return "CP1255"; - case 178: - case 179: - case 180: - return "CP1256"; - case 186: - return "CP1257"; - case 204: - return "CP1251"; - case 222: - return "CP874"; - case 238: - return "CP1250"; - case 254: - return "CP437"; - default: - purple_debug_info("novell", "Unhandled font charset %d\n", font->charset); - return "CP1252"; - } -} - - -/* - * Add an entry to the font table - */ -static int -rtf_add_font_entry(NMRtfContext *ctx, int number, const char *name, int charset) -{ - NMRtfFont *font = g_new0(NMRtfFont, 1); - - font->number = number; - font->name = g_strdup(name); - font->charset = charset; - - purple_debug_info("novell", "Adding font to table: #%d\t%s\t%d\n", - font->number, font->name, font->charset); - - ctx->font_table = g_slist_append(ctx->font_table, font); - - return NMRTF_OK; -} - -/* - * Return the nth entry in the font table - */ -static NMRtfFont * -rtf_get_font(NMRtfContext *ctx, int nth) -{ - NMRtfFont *font; - - font = g_slist_nth_data(ctx->font_table, nth); - - return font; -} - -/* - * Step 1: - * Isolate RTF keywords and send them to rtf_parse_keyword; - * Push and pop state at the start and end of RTF groups; - * Send text to rtf_dispatch_char for further processing. - */ -static int -rtf_parse(NMRtfContext *ctx) -{ - int status; - guchar ch; - guchar hex_byte = 0; - int hex_count = 2; - int len; - - if (ctx->input == NULL) - return NMRTF_OK; - - while (rtf_get_char(ctx, &ch) == NMRTF_OK) { - if (ctx->depth < 0) - return NMRTF_STACK_UNDERFLOW; - - /* if we're parsing binary data, handle it directly */ - if (ctx->ris == NMRTF_STATE_BIN) { - if ((status = rtf_dispatch_char(ctx, ch)) != NMRTF_OK) - return status; - } else { - switch (ch) { - case '{': - if (ctx->depth > NMRTF_MAX_DEPTH) - return NMRTF_STACK_OVERFLOW; - rtf_flush_data(ctx); - if ((status = rtf_push_state(ctx)) != NMRTF_OK) - return status; - break; - case '}': - rtf_flush_data(ctx); - - /* for some reason there is always an unwanted '\par' at the end */ - if (ctx->rds == NMRTF_STATE_NORMAL) { - len = ctx->output->len; - if (ctx->output->str[len-1] == '\n') - ctx->output = g_string_truncate(ctx->output, len-1); - } - - if ((status = rtf_pop_state(ctx)) != NMRTF_OK) - return status; - - if (ctx->depth < 0) - return NMRTF_STACK_OVERFLOW; - break; - case '\\': - if ((status = rtf_parse_keyword(ctx)) != NMRTF_OK) - return status; - break; - case 0x0d: - case 0x0a: /* cr and lf are noise characters... */ - break; - default: - if (ctx->ris == NMRTF_STATE_NORMAL) { - if ((status = rtf_dispatch_char(ctx, ch)) != NMRTF_OK) - return status; - } else { /* parsing a hex encoded character */ - if (ctx->ris != NMRTF_STATE_HEX) - return NMRTF_ASSERTION; - - hex_byte = hex_byte << 4; - if (isdigit(ch)) - hex_byte += (char) ch - '0'; - else { - if (islower(ch)) { - if (ch < 'a' || ch > 'f') - return NMRTF_INVALID_HEX; - hex_byte += (char) ch - 'a' + 10; - } else { - if (ch < 'A' || ch > 'F') - return NMRTF_INVALID_HEX; - hex_byte += (char) ch - 'A' + 10; - } - } - hex_count--; - if (hex_count == 0) { - if ((status = rtf_dispatch_char(ctx, hex_byte)) != NMRTF_OK) - return status; - hex_count = 2; - hex_byte = 0; - ctx->ris = NMRTF_STATE_NORMAL; - } - } - break; - } - } - } - if (ctx->depth < 0) - return NMRTF_STACK_OVERFLOW; - if (ctx->depth > 0) - return NMRTF_UNMATCHED_BRACE; - return NMRTF_OK; -} - -/* - * Push the current state onto stack - */ -static int -rtf_push_state(NMRtfContext *ctx) -{ - NMRtfStateSave *save = g_new0(NMRtfStateSave, 1); - save->chp = ctx->chp; - save->rds = ctx->rds; - save->ris = ctx->ris; - ctx->saved = g_slist_prepend(ctx->saved, save); - ctx->ris = NMRTF_STATE_NORMAL; - (ctx->depth)++; - return NMRTF_OK; -} - -/* - * Restore the state at the top of the stack - */ -static int -rtf_pop_state(NMRtfContext *ctx) -{ - NMRtfStateSave *save_old; - GSList *link_old; - - if (ctx->saved == NULL) - return NMRTF_STACK_UNDERFLOW; - - save_old = ctx->saved->data; - ctx->chp = save_old->chp; - ctx->rds = save_old->rds; - ctx->ris = save_old->ris; - (ctx->depth)--; - - g_free(save_old); - link_old = ctx->saved; - ctx->saved = g_slist_remove_link(ctx->saved, link_old); - g_slist_free_1(link_old); - return NMRTF_OK; -} - -/* - * Step 2: - * Get a control word (and its associated value) and - * dispatch the control. - */ -static int -rtf_parse_keyword(NMRtfContext *ctx) -{ - int status = NMRTF_OK; - guchar ch; - gboolean param_set = FALSE; - gboolean is_neg = FALSE; - int param = 0; - char keyword[30]; - char parameter[20]; - gsize i; - - keyword[0] = '\0'; - parameter[0] = '\0'; - if ((status = rtf_get_char(ctx, &ch)) != NMRTF_OK) - return status; - - if (!isalpha(ch)) { - /* a control symbol; no delimiter. */ - keyword[0] = (char) ch; - keyword[1] = '\0'; - return rtf_dispatch_control(ctx, keyword, 0, param_set); - } - - /* parse keyword */ - for (i = 0; isalpha(ch) && (i < sizeof(keyword) - 1); rtf_get_char(ctx, &ch)) { - keyword[i] = (char) ch; - i++; - } - keyword[i] = '\0'; - - /* check for '-' indicated a negative parameter value */ - if (ch == '-') { - is_neg = TRUE; - if ((status = rtf_get_char(ctx, &ch)) != NMRTF_OK) - return status; - } - - /* check for numerical param */ - if (isdigit(ch)) { - - param_set = TRUE; - for (i = 0; isdigit(ch) && (i < sizeof(parameter) - 1); rtf_get_char(ctx, &ch)) { - parameter[i] = (char) ch; - i++; - } - parameter[i] = '\0'; - - ctx->param = param = atoi(parameter); - if (is_neg) - ctx->param = param = -param; - } - - /* space after control is optional, put character back if it is not a space */ - if (ch != ' ') - rtf_unget_char(ctx, ch); - - return rtf_dispatch_control(ctx, keyword, param, param_set); -} - -/* - * Route the character to the appropriate destination - */ -static int -rtf_dispatch_char(NMRtfContext *ctx, guchar ch) -{ - if (ctx->ris == NMRTF_STATE_BIN && --(ctx->bytes_to_skip) <= 0) - ctx->ris = NMRTF_STATE_NORMAL; - - switch (ctx->rds) { - case NMRTF_STATE_SKIP: - return NMRTF_OK; - case NMRTF_STATE_NORMAL: - return rtf_print_char(ctx, ch); - case NMRTF_STATE_FONTTABLE: - if (ch == ';') { - rtf_add_font_entry(ctx, ctx->chp.font_idx, - ctx->ansi->str, ctx->chp.font_charset); - g_string_truncate(ctx->ansi, 0); - } - else { - return rtf_print_char(ctx, ch); - } - return NMRTF_OK; - default: - return NMRTF_OK; - } -} - -/* Handle a unicode character */ -static int -rtf_dispatch_unicode_char(NMRtfContext *ctx, gunichar ch) -{ - switch (ctx->rds) { - case NMRTF_STATE_SKIP: - return NMRTF_OK; - case NMRTF_STATE_NORMAL: - case NMRTF_STATE_FONTTABLE: - return rtf_print_unicode_char(ctx, ch); - default: - return NMRTF_OK; - } -} - -/* - * Output a character - */ -static int -rtf_print_char(NMRtfContext *ctx, guchar ch) -{ - - ctx->ansi = g_string_append_c(ctx->ansi, ch); - - return NMRTF_OK; -} - -/* - * Output a unicode character - */ -static int -rtf_print_unicode_char(NMRtfContext *ctx, gunichar ch) -{ - char buf[7]; - int num; - - /* convert and flush the ansi buffer to the utf8 buffer */ - rtf_flush_data(ctx); - - /* convert the unicode character to utf8 and add directly to the output buffer */ - num = g_unichar_to_utf8((gunichar) ch, buf); - buf[num] = 0; - purple_debug_info("novell", "converted unichar 0x%X to utf8 char %s\n", ch, buf); - - ctx->output = g_string_append(ctx->output, buf); - return NMRTF_OK; -} - -/* - * Flush the output text - */ -static int -rtf_flush_data(NMRtfContext *ctx) -{ - int status = NMRTF_OK; - char *conv_data = NULL; - const char *enc = NULL; - GError *gerror = NULL; - - if (ctx->rds == NMRTF_STATE_NORMAL && ctx->ansi->len > 0) { - enc = get_current_encoding(ctx); - conv_data = g_convert(ctx->ansi->str, ctx->ansi->len, "UTF-8", enc, - NULL, NULL, &gerror); - if (conv_data) { - ctx->output = g_string_append(ctx->output, conv_data); - g_free(conv_data); - ctx->ansi = g_string_truncate(ctx->ansi, 0); - } else { - status = NMRTF_CONVERT_ERROR; - purple_debug_info("novell", "failed to convert data! error code = %d msg = %s\n", - gerror->code, gerror->message); - g_free(gerror); - } - } - - return status; -} - -/* - * Handle a property change - */ -static int -rtf_apply_property(NMRtfContext *ctx, NMRtfProperty prop, int val) -{ - if (ctx->rds == NMRTF_STATE_SKIP) /* If we're skipping text, */ - return NMRTF_OK; /* don't do anything. */ - - /* Need to flush any temporary data before a property change*/ - rtf_flush_data(ctx); - - switch (prop) { - case NMRTF_PROP_FONT_IDX: - ctx->chp.font_idx = val; - break; - case NMRTF_PROP_FONT_CHARSET: - ctx->chp.font_charset = val; - break; - default: - return NMRTF_BAD_TABLE; - } - - return NMRTF_OK; -} - -/* - * Step 3. - * Search the table for keyword and evaluate it appropriately. - * - * Inputs: - * keyword: The RTF control to evaluate. - * param: The parameter of the RTF control. - * param_set: TRUE if the control had a parameter; (that is, if param is valid) - * FALSE if it did not. - */ -static int -rtf_dispatch_control(NMRtfContext *ctx, char *keyword, int param, gboolean param_set) -{ - int idx; - - for (idx = 0; idx < table_size; idx++) { - if (purple_strequal(keyword, rtf_symbols[idx].keyword)) - break; - } - - if (idx == table_size) { - if (ctx->skip_unknown) - ctx->rds = NMRTF_STATE_SKIP; - ctx->skip_unknown = FALSE; - return NMRTF_OK; - } - - /* found it! use kwd_type and action to determine what to do with it. */ - ctx->skip_unknown = FALSE; - switch (rtf_symbols[idx].kwd_type) { - case NMRTF_KWD_PROP: - if (rtf_symbols[idx].pass_default || !param_set) - param = rtf_symbols[idx].default_val; - return rtf_apply_property(ctx, rtf_symbols[idx].action, param); - case NMRTF_KWD_CHAR: - return rtf_dispatch_char(ctx, rtf_symbols[idx].action); - case NMRTF_KWD_DEST: - return rtf_change_destination(ctx, rtf_symbols[idx].action); - case NMRTF_KWD_SPEC: - return rtf_dispatch_special(ctx, rtf_symbols[idx].action); - default: - return NMRTF_BAD_TABLE; - } - return NMRTF_BAD_TABLE; -} - -/* - * Change to the destination specified. - */ -static int -rtf_change_destination(NMRtfContext *ctx, NMRtfDestinationType type) -{ - /* if we're skipping text, don't do anything */ - if (ctx->rds == NMRTF_STATE_SKIP) - return NMRTF_OK; - - switch (type) { - case NMRTF_DEST_FONTTABLE: - ctx->rds = NMRTF_STATE_FONTTABLE; - g_string_truncate(ctx->ansi, 0); - break; - default: - ctx->rds = NMRTF_STATE_SKIP; /* when in doubt, skip it... */ - break; - } - return NMRTF_OK; -} - -/* - * Dispatch an RTF control that needs special processing - */ -static int -rtf_dispatch_special(NMRtfContext *ctx, NMRtfSpecialKwd type) -{ - int status = NMRTF_OK; - guchar ch; - - if (ctx->rds == NMRTF_STATE_SKIP && type != NMRTF_SPECIAL_BIN) /* if we're skipping, and it's not */ - return NMRTF_OK; /* the \bin keyword, ignore it. */ - - switch (type) { - case NMRTF_SPECIAL_BIN: - ctx->ris = NMRTF_STATE_BIN; - ctx->bytes_to_skip = ctx->param; - break; - case NMRTF_SPECIAL_SKIP: - ctx->skip_unknown = TRUE; - break; - case NMRTF_SPECIAL_HEX: - ctx->ris = NMRTF_STATE_HEX; - break; - case NMRTF_SPECIAL_UNICODE: - purple_debug_info("novell", "parsing unichar\n"); - status = rtf_dispatch_unicode_char(ctx, ctx->param); - /* Skip next char */ - if (status == NMRTF_OK) - status = rtf_get_char(ctx, &ch); - break; - default: - status = NMRTF_BAD_TABLE; - break; - } - - return status; -} - -/* - * Get the next character from the input stream - */ -static int -rtf_get_char(NMRtfContext *ctx, guchar *ch) -{ - if (ctx->nextch_available) { - *ch = ctx->nextch; - ctx->nextch_available = FALSE; - } else { - *ch = *(ctx->input); - ctx->input++; - } - - if (*ch) - return NMRTF_OK; - else - return NMRTF_EOF; -} - -/* - * Move a character back into the input stream - */ -static int -rtf_unget_char(NMRtfContext *ctx, guchar ch) -{ - ctx->nextch = ch; - ctx->nextch_available = TRUE; - return NMRTF_OK; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmrtf.h --- a/libpurple/protocols/novell/nmrtf.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -/* - * nmrtf.h - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 __NMRTF_H__ -#define __NMRTF_H__ - -typedef struct _NMRtfContext NMRtfContext; - -NMRtfContext *nm_rtf_init(void); -char *nm_rtf_strip_formatting(NMRtfContext *ctx, const char *input); -void nm_rtf_deinit(NMRtfContext *ctx); - -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmuser.c --- a/libpurple/protocols/novell/nmuser.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2137 +0,0 @@ -/* - * nmuser.c - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 "internal.h" -#include -#include "nmfield.h" -#include "nmuser.h" -#include "nmconn.h" -#include "nmcontact.h" -#include "nmuserrecord.h" -#include "util.h" - -/* This is the template that we wrap outgoing messages in, since the other - * GW Messenger clients expect messages to be in RTF. - */ -#define RTF_TEMPLATE "{\\rtf1\\ansi\n"\ - "{\\fonttbl{\\f0\\fnil Unknown;}}\n"\ - "{\\colortbl ;\\red0\\green0\\blue0;}\n"\ - "\\uc1\\cf1\\f0\\fs24 %s\\par\n}" -#define NM_MAX_MESSAGE_SIZE 2048 - -static NMERR_T nm_process_response(NMUser * user); -static void _update_contact_list(NMUser * user, NMField * fields); -static void _handle_multiple_get_details_login_cb(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data); -static char * nm_rtfize_text(char *text); - -/** - * See header for comments on on "public" functions - */ - -NMUser * -nm_initialize_user(const char *name, const char *server_addr, - int port, gpointer data, nm_event_cb event_callback) -{ - NMUser *user; - if (name == NULL || server_addr == NULL || event_callback == NULL) - return NULL; - - user = g_new0(NMUser, 1); - - - - user->contacts = - g_hash_table_new_full(g_str_hash, nm_utf8_str_equal, - g_free, (GDestroyNotify) nm_release_contact); - - user->user_records = - g_hash_table_new_full(g_str_hash, nm_utf8_str_equal, g_free, - (GDestroyNotify) nm_release_user_record); - - user->display_id_to_dn = g_hash_table_new_full(g_str_hash, nm_utf8_str_equal, - g_free, g_free); - - user->name = g_strdup(name); - user->conn = nm_create_conn(server_addr, port); - user->conn->addr = g_strdup(server_addr); - user->conn->port = port; - user->evt_callback = event_callback; - user->client_data = data; - - return user; -} - - -void -nm_deinitialize_user(NMUser * user) -{ - nm_release_conn(user->conn); - - if (user->contacts) { - g_hash_table_destroy(user->contacts); - } - - if (user->user_records) { - g_hash_table_destroy(user->user_records); - } - - if (user->display_id_to_dn) { - g_hash_table_destroy(user->display_id_to_dn); - } - - if (user->name) { - g_free(user->name); - } - - if (user->user_record) { - nm_release_user_record(user->user_record); - } - - nm_conference_list_free(user); - nm_destroy_contact_list(user); - - g_free(user); -} - -NMERR_T -nm_send_login(NMUser * user, const char *pwd, const char *my_addr, - const char *user_agent, nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - - if (user == NULL || pwd == NULL || user_agent == NULL) { - return NMERR_BAD_PARM; - } - - fields = nm_field_add_pointer(fields, NM_A_SZ_USERID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(user->name), NMFIELD_TYPE_UTF8); - - fields = nm_field_add_pointer(fields, NM_A_SZ_CREDENTIALS, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(pwd), NMFIELD_TYPE_UTF8); - - fields = nm_field_add_pointer(fields, NM_A_SZ_USER_AGENT, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(user_agent), NMFIELD_TYPE_UTF8); - - fields = nm_field_add_number(fields, NM_A_UD_BUILD, 0, NMFIELD_METHOD_VALID, 0, - NM_PROTOCOL_VERSION, NMFIELD_TYPE_UDWORD); - if (my_addr) { - fields = nm_field_add_pointer(fields, NM_A_IP_ADDRESS, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(my_addr), NMFIELD_TYPE_UTF8); - } - - /* Send the login */ - rc = nm_send_request(user->conn, "login", fields, callback, data, NULL); - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_set_status(NMUser * user, int status, const char *text, - const char *auto_resp, nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - - if (user == NULL) - return NMERR_BAD_PARM; - - /* Add the status */ - fields = nm_field_add_pointer(fields, NM_A_SZ_STATUS, 0, NMFIELD_METHOD_VALID, 0, - g_strdup_printf("%d", status), NMFIELD_TYPE_UTF8); - - /* Add the status text and auto reply text if there is any */ - if (text) { - fields = nm_field_add_pointer(fields, NM_A_SZ_STATUS_TEXT, 0, - NMFIELD_METHOD_VALID, 0, g_strdup(text), - NMFIELD_TYPE_UTF8); - } - - if (auto_resp) { - fields = nm_field_add_pointer(fields, NM_A_SZ_MESSAGE_BODY, 0, - NMFIELD_METHOD_VALID, 0, g_strdup(auto_resp), - NMFIELD_TYPE_UTF8); - } - - rc = nm_send_request(user->conn, "setstatus", fields, callback, data, NULL); - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_multiple_get_details(NMUser * user, GSList *names, - nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - GSList *node; - - if (user == NULL || names == NULL) - return NMERR_BAD_PARM; - - /* Add in DN or display id */ - for (node = names; node; node = node->next) { - fields = nm_field_add_pointer(fields, NM_A_SZ_USERID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(node->data), NMFIELD_TYPE_UTF8); - } - - rc = nm_send_request(user->conn, "getdetails", fields, callback, data, NULL); - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_get_details(NMUser * user, const char *name, - nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - - if (user == NULL || name == NULL) - return NMERR_BAD_PARM; - - /* Add in DN or display id */ - if (strstr("=", name)) { - fields = nm_field_add_pointer(fields, NM_A_SZ_DN, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(name), NMFIELD_TYPE_DN); - } else { - - const char *dn = nm_lookup_dn(user, name); - if (dn) { - fields = nm_field_add_pointer(fields, NM_A_SZ_DN, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(name), NMFIELD_TYPE_DN); - } else { - fields = - nm_field_add_pointer(fields, NM_A_SZ_USERID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(name), NMFIELD_TYPE_UTF8); - } - - } - - rc = nm_send_request(user->conn, "getdetails", fields, callback, data, NULL); - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_create_conference(NMUser * user, NMConference * conference, - nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - NMField *tmp = NULL; - NMField *field = NULL; - NMRequest *req = NULL; - int count, i; - - if (user == NULL || conference == NULL) - return NMERR_BAD_PARM; - - /* Add in a blank guid */ - tmp = nm_field_add_pointer(tmp, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(BLANK_GUID), NMFIELD_TYPE_UTF8); - - fields = nm_field_add_pointer(fields, NM_A_FA_CONVERSATION, 0, - NMFIELD_METHOD_VALID, 0, tmp, - NMFIELD_TYPE_ARRAY); - tmp = NULL; - - - /* Add participants in */ - count = nm_conference_get_participant_count(conference); - for (i = 0; i < count; i++) { - NMUserRecord *user_record = nm_conference_get_participant(conference, i); - - if (user_record) { - fields = nm_field_add_pointer(fields, NM_A_SZ_DN, - 0, NMFIELD_METHOD_VALID, 0, - g_strdup(nm_user_record_get_dn(user_record)), - NMFIELD_TYPE_DN); - } - } - - /* Add our user in */ - field = nm_locate_field(NM_A_SZ_DN, user->fields); - if (field) { - fields = nm_field_add_pointer(fields, NM_A_SZ_DN, - 0, NMFIELD_METHOD_VALID, 0, - g_strdup((char *) field->ptr_value), - NMFIELD_TYPE_DN); - } - - rc = nm_send_request(user->conn, "createconf", fields, callback, data, &req); - if (rc == NM_OK && req) { - nm_conference_add_ref(conference); - nm_request_set_data(req, conference); - } - - if (req) - nm_release_request(req); - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_leave_conference(NMUser * user, NMConference * conference, - nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - NMField *tmp = NULL; - NMRequest *req = NULL; - - if (user == NULL || conference == NULL) - return NMERR_BAD_PARM; - - /* Add in the conference guid */ - tmp = nm_field_add_pointer(tmp, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(nm_conference_get_guid(conference)), - NMFIELD_TYPE_UTF8); - - fields = nm_field_add_pointer(fields, NM_A_FA_CONVERSATION, 0, - NMFIELD_METHOD_VALID, 0, tmp, - NMFIELD_TYPE_ARRAY); - tmp = NULL; - - /* Send the request to the server */ - rc = nm_send_request(user->conn, "leaveconf", fields, callback, data, &req); - if (rc == NM_OK && req) - nm_request_set_data(req, conference); - - if (req) - nm_release_request(req); - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_join_conference(NMUser * user, NMConference * conference, - nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL, *tmp = NULL; - NMRequest *req = NULL; - - if (user == NULL || conference == NULL) - return NMERR_BAD_PARM; - - /* Add in the conference guid */ - tmp = nm_field_add_pointer(tmp, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(nm_conference_get_guid(conference)), - NMFIELD_TYPE_UTF8); - - fields = nm_field_add_pointer(fields, NM_A_FA_CONVERSATION, 0, - NMFIELD_METHOD_VALID, 0, tmp, - NMFIELD_TYPE_ARRAY); - tmp = NULL; - - /* Send the request to the server */ - rc = nm_send_request(user->conn, "joinconf", fields, callback, data, &req); - if (rc == NM_OK && req) - nm_request_set_data(req, conference); - - if (req) - nm_release_request(req); - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_reject_conference(NMUser * user, NMConference * conference, - nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - NMField *tmp = NULL; - NMRequest *req = NULL; - - if (user == NULL || conference == NULL) - return NMERR_BAD_PARM; - - /* Add in the conference guid */ - tmp = nm_field_add_pointer(tmp, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(nm_conference_get_guid(conference)), - NMFIELD_TYPE_UTF8); - - fields = nm_field_add_pointer(fields, NM_A_FA_CONVERSATION, 0, - NMFIELD_METHOD_VALID, 0, tmp, - NMFIELD_TYPE_ARRAY); - tmp = NULL; - - /* Send the request to the server */ - rc = nm_send_request(user->conn, "rejectconf", fields, callback, data, &req); - if (rc == NM_OK && req) - nm_request_set_data(req, conference); - - if (req) - nm_release_request(req); - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_conference_invite(NMUser *user, NMConference *conference, NMUserRecord *user_record, - const char *message, nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - NMField *tmp = NULL; - NMRequest *req = NULL; - - if (user == NULL || conference == NULL || user_record == NULL) - return NMERR_BAD_PARM; - - /* Add in the conference guid */ - tmp = nm_field_add_pointer(tmp, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(nm_conference_get_guid(conference)), - NMFIELD_TYPE_UTF8); - - fields = nm_field_add_pointer(fields, NM_A_FA_CONVERSATION, 0, - NMFIELD_METHOD_VALID, 0, tmp, - NMFIELD_TYPE_ARRAY); - tmp = NULL; - - /* Add in DN of user to invite */ - fields = nm_field_add_pointer(fields, NM_A_SZ_DN, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(nm_user_record_get_dn(user_record)), - NMFIELD_TYPE_DN); - - /* Add the invite message if there is one */ - if (message) - fields = nm_field_add_pointer(fields, NM_A_SZ_MESSAGE_BODY, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(message), NMFIELD_TYPE_UTF8); - - /* Send the request to the server */ - rc = nm_send_request(user->conn, "sendinvite", fields, callback, data, &req); - if (rc == NM_OK && req) - nm_request_set_data(req, conference); - - if (req) - nm_release_request(req); - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_message(NMUser * user, NMMessage * message, nm_response_cb callback) -{ - NMERR_T rc = NM_OK; - char *text, *rtfized; - NMField *fields = NULL, *tmp = NULL; - NMConference *conf; - NMUserRecord *user_record; - int count, i; - - if (user == NULL || message == NULL) { - return NMERR_BAD_PARM; - } - - conf = nm_message_get_conference(message); - if (!nm_conference_is_instantiated(conf)) { - rc = NMERR_CONFERENCE_NOT_INSTANTIATED; - } else { - - tmp = nm_field_add_pointer(tmp, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(nm_conference_get_guid(conf)), - NMFIELD_TYPE_UTF8); - - fields = - nm_field_add_pointer(fields, NM_A_FA_CONVERSATION, 0, NMFIELD_METHOD_VALID, 0, - tmp, NMFIELD_TYPE_ARRAY); - tmp = NULL; - - /* Add RTF and plain text versions of the message */ - text = g_strdup(nm_message_get_text(message)); - - /* Truncate if necessary */ - if (strlen(text) > NM_MAX_MESSAGE_SIZE) - text[NM_MAX_MESSAGE_SIZE] = 0; - - rtfized = nm_rtfize_text(text); - - purple_debug_info("novell", "message text is: %s\n", text); - purple_debug_info("novell", "message rtf is: %s\n", rtfized); - - tmp = nm_field_add_pointer(tmp, NM_A_SZ_MESSAGE_BODY, 0, NMFIELD_METHOD_VALID, 0, - rtfized, NMFIELD_TYPE_UTF8); - - tmp = nm_field_add_number(tmp, NM_A_UD_MESSAGE_TYPE, 0, NMFIELD_METHOD_VALID, 0, - 0, NMFIELD_TYPE_UDWORD); - - tmp = nm_field_add_pointer(tmp, NM_A_SZ_MESSAGE_TEXT, 0, NMFIELD_METHOD_VALID, 0, - text, NMFIELD_TYPE_UTF8); - - fields = nm_field_add_pointer(fields, NM_A_FA_MESSAGE, 0, NMFIELD_METHOD_VALID, 0, - tmp, NMFIELD_TYPE_ARRAY); - tmp = NULL; - - /* Add participants */ - count = nm_conference_get_participant_count(conf); - for (i = 0; i < count; i++) { - user_record = nm_conference_get_participant(conf, i); - if (user_record) { - fields = - nm_field_add_pointer(fields, NM_A_SZ_DN, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(nm_user_record_get_dn(user_record)), - NMFIELD_TYPE_DN); - } - } - - /* Send the request */ - rc = nm_send_request(user->conn, "sendmessage", fields, callback, NULL, NULL); - } - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_typing(NMUser * user, NMConference * conf, - gboolean typing, nm_response_cb callback) -{ - NMERR_T rc = NM_OK; - char *str = NULL; - NMField *fields = NULL, *tmp = NULL; - - if (user == NULL || conf == NULL) { - return NMERR_BAD_PARM; - } - - if (!nm_conference_is_instantiated(conf)) { - rc = NMERR_CONFERENCE_NOT_INSTANTIATED; - } else { - /* Add the conference GUID */ - tmp = nm_field_add_pointer(tmp, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(nm_conference_get_guid(conf)), - NMFIELD_TYPE_UTF8); - - /* Add typing type */ - str = g_strdup_printf("%d", - (typing ? NMEVT_USER_TYPING : - NMEVT_USER_NOT_TYPING)); - - tmp = nm_field_add_pointer(tmp, NM_A_SZ_TYPE, 0, NMFIELD_METHOD_VALID, 0, - str, NMFIELD_TYPE_UTF8); - - fields = - nm_field_add_pointer(fields, NM_A_FA_CONVERSATION, 0, NMFIELD_METHOD_VALID, 0, - tmp, NMFIELD_TYPE_ARRAY); - tmp = NULL; - - rc = nm_send_request(user->conn, "sendtyping", fields, callback, NULL, NULL); - } - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_create_contact(NMUser * user, NMFolder * folder, - NMContact * contact, nm_response_cb callback, - gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - NMRequest *req = NULL; - const char *name = NULL; - const char *display_name = NULL; - - if (user == NULL || folder == NULL || contact == NULL) { - return NMERR_BAD_PARM; - } - - /* Add parent ID */ - fields = nm_field_add_pointer(fields, NM_A_SZ_PARENT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup_printf("%d", nm_folder_get_id(folder)), - NMFIELD_TYPE_UTF8); - - /* Check to see if userid is current user and return an error? */ - - /* Check to see if contact already exists and return an error? */ - - /* Add userid or dn */ - name = nm_contact_get_dn(contact); - if (name == NULL) - return NMERR_BAD_PARM; - - if (strstr("=", name)) { - fields = nm_field_add_pointer(fields, NM_A_SZ_DN, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(name), NMFIELD_TYPE_DN); - } else { - fields = nm_field_add_pointer(fields, NM_A_SZ_USERID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(name), NMFIELD_TYPE_UTF8); - } - - /* Add display name */ - display_name = nm_contact_get_display_name(contact); - if (display_name) - fields = nm_field_add_pointer(fields, NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(display_name), NMFIELD_TYPE_UTF8); - - /* Dispatch the request */ - rc = nm_send_request(user->conn, "createcontact", fields, callback, data, &req); - if (rc == NM_OK && req) - nm_request_set_data(req, contact); - - if (req) - nm_release_request(req); - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_remove_contact(NMUser * user, NMFolder * folder, - NMContact * contact, nm_response_cb callback, - gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - NMRequest *req = NULL; - - if (user == NULL || folder == NULL || contact == NULL) { - return NMERR_BAD_PARM; - } - - /* Add parent id */ - fields = nm_field_add_pointer(fields, NM_A_SZ_PARENT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup_printf("%d", nm_folder_get_id(folder)), - NMFIELD_TYPE_UTF8); - - /* Add object id */ - fields = nm_field_add_pointer(fields, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup_printf("%d", nm_contact_get_id(contact)), - NMFIELD_TYPE_UTF8); - - /* Dispatch the request */ - rc = nm_send_request(user->conn, "deletecontact", fields, callback, data, &req); - if (rc == NM_OK && req) - nm_request_set_data(req, contact); - - if (req) - nm_release_request(req); - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_create_folder(NMUser * user, const char *name, - nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - NMRequest *req = NULL; - - if (user == NULL || name == NULL) { - return NMERR_BAD_PARM; - } - - /* Add parent ID */ - fields = nm_field_add_pointer(fields, NM_A_SZ_PARENT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup("0"), NMFIELD_TYPE_UTF8); - - /* Add name of the folder to add */ - fields = - nm_field_add_pointer(fields, NM_A_SZ_DISPLAY_NAME, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(name), NMFIELD_TYPE_UTF8); - - /* Add sequence, for now just put it at the bottom */ - fields = - nm_field_add_pointer(fields, NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_METHOD_VALID, 0, - g_strdup("-1"), NMFIELD_TYPE_UTF8); - - /* Dispatch the request */ - rc = nm_send_request(user->conn, "createfolder", fields, callback, data, &req); - if (rc == NM_OK && req) - nm_request_set_data(req, g_strdup(name)); - - if (req) - nm_release_request(req); - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_remove_folder(NMUser * user, NMFolder * folder, - nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - NMRequest *req = NULL; - - if (user == NULL || folder == NULL) { - return NMERR_BAD_PARM; - } - - /* Add the object id */ - fields = nm_field_add_pointer(fields, NM_A_SZ_OBJECT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup_printf("%d", nm_folder_get_id(folder)), - NMFIELD_TYPE_UTF8); - - /* Dispatch the request */ - rc = nm_send_request(user->conn, "deletecontact", fields, callback, data, &req); - if (rc == NM_OK && req) - nm_request_set_data(req, folder); - - if (req) - nm_release_request(req); - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_get_status(NMUser * user, NMUserRecord * user_record, - nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - NMRequest *req = NULL; - const char *dn; - - if (user == NULL || user_record == NULL) - return NMERR_BAD_PARM; - - /* Add DN to field list */ - dn = nm_user_record_get_dn(user_record); - if (dn == NULL) - return (NMERR_T) -1; - - fields = nm_field_add_pointer(fields, NM_A_SZ_DN, 0, NMFIELD_METHOD_VALID, 0, - g_strdup(dn), NMFIELD_TYPE_UTF8); - - /* Dispatch the request */ - rc = nm_send_request(user->conn, "getstatus", fields, callback, data, &req); - if (rc == NM_OK && req) - nm_request_set_data(req, user_record); - - if (req) - nm_release_request(req); - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_rename_contact(NMUser * user, NMContact * contact, - const char *new_name, nm_response_cb callback, - gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *field = NULL, *fields = NULL, *list = NULL; - NMRequest *req = NULL; - - if (user == NULL || contact == NULL || new_name == NULL) - return NMERR_BAD_PARM; - - /* Create field list for current contact */ - field = nm_contact_to_fields(contact); - if (field) { - - fields = - nm_field_add_pointer(fields, NM_A_FA_CONTACT, 0, NMFIELD_METHOD_DELETE, 0, - field, NMFIELD_TYPE_ARRAY); - field = NULL; - - /* Update the contacts display name locally */ - nm_contact_set_display_name(contact, new_name); - - /* Create field list for updated contact */ - field = nm_contact_to_fields(contact); - if (field) { - fields = - nm_field_add_pointer(fields, NM_A_FA_CONTACT, 0, NMFIELD_METHOD_ADD, 0, - field, NMFIELD_TYPE_ARRAY); - field = NULL; - - /* Package it up */ - list = - nm_field_add_pointer(list, NM_A_FA_CONTACT_LIST, 0, NMFIELD_METHOD_VALID, - 0, fields, NMFIELD_TYPE_ARRAY); - fields = NULL; - - rc = nm_send_request(user->conn, "updateitem", list, callback, data, &req); - if (rc == NM_OK && req) - nm_request_set_data(req, contact); - } - } - - if (req) - nm_release_request(req); - - if (list) - nm_free_fields(&list); - - return rc; -} - -NMERR_T -nm_send_rename_folder(NMUser * user, NMFolder * folder, const char *new_name, - nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *field = NULL, *fields = NULL, *list = NULL; - NMRequest *req = NULL; - - if (user == NULL || folder == NULL || new_name == NULL) - return NMERR_BAD_PARM; - - /* Make sure folder does not already exist!? */ - if (nm_find_folder(user, new_name)) - return NMERR_FOLDER_EXISTS; - - /* Create field list for current folder */ - field = nm_folder_to_fields(folder); - if (field) { - - fields = nm_field_add_pointer(fields, NM_A_FA_FOLDER, 0, NMFIELD_METHOD_DELETE, 0, - field, NMFIELD_TYPE_ARRAY); - field = NULL; - - /* Update the folders display name locally */ - nm_folder_set_name(folder, new_name); - - /* Create field list for updated folder */ - field = nm_folder_to_fields(folder); - if (field) { - fields = nm_field_add_pointer(fields, NM_A_FA_FOLDER, 0, NMFIELD_METHOD_ADD, 0, - field, NMFIELD_TYPE_ARRAY); - field = NULL; - - /* Package it up */ - list = nm_field_add_pointer(list, NM_A_FA_CONTACT_LIST, 0, NMFIELD_METHOD_VALID, - 0, fields, NMFIELD_TYPE_ARRAY); - fields = NULL; - - rc = nm_send_request(user->conn, "updateitem", list, callback, data, &req); - if (rc == NM_OK && req) - nm_request_set_data(req, folder); - } - } - - if (req) - nm_release_request(req); - - if (list) - nm_free_fields(&list); - - return rc; -} - -NMERR_T -nm_send_move_contact(NMUser * user, NMContact * contact, NMFolder * folder, - nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *field = NULL, *fields = NULL, *list = NULL; - NMRequest *req = NULL; - - if (user == NULL || contact == NULL || folder == NULL) - return NMERR_BAD_PARM; - - /* Create field list for the contact */ - field = nm_contact_to_fields(contact); - if (field) { - - fields = nm_field_add_pointer(fields, NM_A_FA_CONTACT, 0, NMFIELD_METHOD_DELETE, 0, - field, NMFIELD_TYPE_ARRAY); - field = NULL; - - /* Wrap the contact up and add it to the request field list */ - list = nm_field_add_pointer(list, NM_A_FA_CONTACT_LIST, 0, NMFIELD_METHOD_VALID, 0, - fields, NMFIELD_TYPE_ARRAY); - fields = NULL; - - /* Add sequence number */ - list = nm_field_add_pointer(list, NM_A_SZ_SEQUENCE_NUMBER, 0, NMFIELD_METHOD_VALID, - 0, g_strdup("-1"), NMFIELD_TYPE_UTF8); - - /* Add parent ID */ - list = nm_field_add_pointer(list, NM_A_SZ_PARENT_ID, 0, NMFIELD_METHOD_VALID, 0, - g_strdup_printf("%d", nm_folder_get_id(folder)), - NMFIELD_TYPE_UTF8); - - /* Dispatch the request */ - rc = nm_send_request(user->conn, "movecontact", list, callback, data, &req); - if (rc == NM_OK && req) - nm_request_set_data(req, contact); - - } - - if (req) - nm_release_request(req); - - if (list) - nm_free_fields(&list); - - return rc; -} - - -NMERR_T -nm_send_create_privacy_item(NMUser *user, const char *who, gboolean allow_list, - nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - const char *tag; - - if (user == NULL || who == NULL) - return NMERR_BAD_PARM; - - if (allow_list) - tag = NM_A_SZ_BLOCKING_ALLOW_ITEM; - else - tag = NM_A_SZ_BLOCKING_DENY_ITEM; - - fields = nm_field_add_pointer(fields, tag, 0, NMFIELD_METHOD_ADD, 0, - g_strdup(who), NMFIELD_TYPE_UTF8); - - rc = nm_send_request(user->conn, "createblock", fields, callback, data, NULL); - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_remove_privacy_item(NMUser *user, const char *dn, gboolean allow_list, - nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - const char *tag; - GSList **list_ptr, *node; - - if (user == NULL || dn == NULL) - return NMERR_BAD_PARM; - - if (allow_list) { - tag = NM_A_BLOCKING_ALLOW_LIST; - list_ptr = &user->allow_list; - } else { - tag = NM_A_BLOCKING_DENY_LIST; - list_ptr = &user->deny_list; - } - - /* Remove item from the cached list */ - if ((node = g_slist_find_custom(*list_ptr, dn, (GCompareFunc)purple_utf8_strcasecmp))) { - *list_ptr = g_slist_remove_link(*list_ptr, node); - g_slist_free_1(node); - } - - fields = nm_field_add_pointer(fields, tag, 0, NMFIELD_METHOD_DELETE, 0, - g_strdup(dn), NMFIELD_TYPE_DN); - - rc = nm_send_request(user->conn, "updateblocks", fields, callback, data, NULL); - - nm_free_fields(&fields); - return rc; - -} - -NMERR_T -nm_send_set_privacy_default(NMUser *user, gboolean default_deny, - nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - - if (user == NULL) - return NMERR_BAD_PARM; - - fields = nm_field_add_pointer(fields, NM_A_BLOCKING, 0, NMFIELD_METHOD_UPDATE, 0, - (default_deny ? g_strdup("1") : g_strdup("0")), - NMFIELD_TYPE_UTF8); - - rc = nm_send_request(user->conn, "updateblocks", fields, callback, data, NULL); - - nm_free_fields(&fields); - return rc; -} - -NMERR_T -nm_send_keepalive(NMUser *user, nm_response_cb callback, gpointer data) -{ - NMERR_T rc = NM_OK; - - if (user == NULL) - return NMERR_BAD_PARM; - - rc = nm_send_request(user->conn, "ping", NULL, callback, data, NULL); - - return rc; -} - -NMERR_T -nm_process_new_data(NMUser * user) -{ - NMConn *conn; - NMERR_T rc = NM_OK; - guint32 val; - - if (user == NULL) - return NMERR_BAD_PARM; - - conn = user->conn; - - /* Check to see if this is an event or a response */ - rc = nm_read_all(conn, (char *) &val, sizeof(val)); - if (rc == NM_OK) { - if (strncmp((char *) &val, "HTTP", strlen("HTTP")) == 0) - rc = nm_process_response(user); - else - rc = nm_process_event(user, GUINT32_FROM_LE(val)); - - } else { - if (errno == EAGAIN) - rc = NM_OK; - else - rc = NMERR_PROTOCOL; - } - - return rc; -} - -NMConference * -nm_find_conversation(NMUser * user, const char *who) -{ - NMConference *conference = NULL; - NMConference *tmp; - GSList *cnode; - - if (user && user->conferences) { - for (cnode = user->conferences; cnode; cnode = cnode->next) { - tmp = cnode->data; - if (nm_conference_get_participant_count(tmp) == 1) { - NMUserRecord *ur = nm_conference_get_participant(tmp, 0); - - if (ur) { - if (nm_utf8_str_equal(nm_user_record_get_dn(ur), who)) { - conference = tmp; - break; - } - } - } - } - } - - return conference; -} - -void -nm_conference_list_add(NMUser * user, NMConference * conf) -{ - if (user == NULL || conf == NULL) - return; - - nm_conference_add_ref(conf); - user->conferences = g_slist_append(user->conferences, conf); -} - -void -nm_conference_list_remove(NMUser * user, NMConference * conf) -{ - if (user == NULL || conf == NULL) - return; - - if (g_slist_find(user->conferences, conf)) { - user->conferences = g_slist_remove(user->conferences, conf); - nm_release_conference(conf); - } -} - -void -nm_conference_list_free(NMUser * user) -{ - GSList *cnode; - NMConference *conference; - - if (user == NULL) - return; - - if (user->conferences) { - for (cnode = user->conferences; cnode; cnode = cnode->next) { - conference = cnode->data; - cnode->data = NULL; - nm_release_conference(conference); - } - - g_slist_free(user->conferences); - user->conferences = NULL; - } -} - -NMConference * -nm_conference_list_find(NMUser * user, const char *guid) -{ - GSList *cnode; - NMConference *conference = NULL, *tmp; - - if (user == NULL || guid == NULL) - return NULL; - - if (user->conferences) { - for (cnode = user->conferences; cnode; cnode = cnode->next) { - tmp = cnode->data; - if (nm_are_guids_equal(nm_conference_get_guid(tmp), guid)) { - conference = tmp; - break; - } - } - } - - return conference; -} - -gboolean -nm_are_guids_equal(const char *guid1, const char *guid2) -{ - if (guid1 == NULL || guid2 == NULL) - return FALSE; - - return (strncmp(guid1, guid2, CONF_GUID_END) == 0); -} - -void -nm_user_add_contact(NMUser * user, NMContact * contact) -{ - if (user == NULL || contact == NULL) - return; - - nm_contact_add_ref(contact); - - g_hash_table_insert(user->contacts, - g_utf8_strdown(nm_contact_get_dn(contact), -1), contact); -} - -void -nm_user_add_user_record(NMUser * user, NMUserRecord * user_record) -{ - const char *display_id; - const char *dn; - - if (!user || !user_record) - return; - - display_id = nm_user_record_get_display_id(user_record); - dn = nm_user_record_get_dn(user_record); - - if (!dn || !display_id) - return; - - nm_user_record_add_ref(user_record); - - g_hash_table_insert(user->user_records, - g_utf8_strdown(dn, -1), - user_record); - - g_hash_table_insert(user->display_id_to_dn, - g_utf8_strdown(display_id, -1), - g_utf8_strdown(dn, -1)); -} - -nm_event_cb -nm_user_get_event_callback(NMUser * user) -{ - if (user == NULL) - return NULL; - - return user->evt_callback; -} - -NMConn * -nm_user_get_conn(NMUser * user) -{ - if (user == NULL) - return NULL; - - return user->conn; -} - -NMERR_T -nm_create_contact_list(NMUser * user) -{ - NMERR_T rc = NM_OK; - NMField *locate = NULL; - - if (user == NULL || user->fields == NULL) { - return NMERR_BAD_PARM; - } - - /* Create the root folder */ - user->root_folder = nm_create_folder(""); - - /* Find the contact list in the login fields */ - locate = nm_locate_field(NM_A_FA_CONTACT_LIST, user->fields); - if (locate != NULL) { - - /* Add the folders and then the contacts */ - nm_folder_add_contacts_and_folders(user, user->root_folder, - (NMField *) (locate->ptr_value)); - - } - - return rc; -} - -gboolean nm_user_is_privacy_locked(NMUser *user) -{ - if (user) { - return user->privacy_locked; - } - - return FALSE; -} - -static gboolean -_create_privacy_list(NMUser * user, NMRequest *request) -{ - NMField *locate = NULL; - GSList *need_details = NULL; - - /* Are the privacy settings locked */ - locate = nm_locate_field(NM_A_LOCKED_ATTR_LIST, user->fields); - if (locate && locate->ptr_value) { - if (locate->type == NMFIELD_TYPE_UTF8 && - (purple_utf8_strcasecmp(locate->ptr_value, NM_A_BLOCKING) == 0)) { - user->privacy_locked = TRUE; - } else if (locate->type == NMFIELD_TYPE_MV || - locate->type == NMFIELD_TYPE_ARRAY) { - NMField *tmp = (NMField *)locate->ptr_value; - while (tmp && tmp->tag) { - if (purple_utf8_strcasecmp(tmp->ptr_value, NM_A_BLOCKING) == 0) { - user->privacy_locked = TRUE; - break; - } - tmp++; - } - } - } - - /* Set default deny flag */ - locate = nm_locate_field(NM_A_BLOCKING, user->fields); - if (locate && locate->ptr_value) { - user->default_deny = atoi((char *)locate->ptr_value); - } - - /* Read internal blocking allow list */ - locate = nm_locate_field(NM_A_BLOCKING_ALLOW_LIST, user->fields); - if (locate && locate->ptr_value) { - - if (locate->type == NMFIELD_TYPE_MV) { - locate = (NMField *)locate->ptr_value; - for (; locate->tag != NULL; locate++) { - if (locate->ptr_value) { - - user->allow_list = g_slist_append(user->allow_list, (char *)locate->ptr_value); - - if (nm_find_user_record(user, (char *)locate->ptr_value) == NULL) - need_details = g_slist_append(need_details, (char *)locate->ptr_value); - - } - } - } else { - - user->allow_list = g_slist_append(user->allow_list, (char *)locate->ptr_value); - - if (nm_find_user_record(user, (char *)locate->ptr_value) == NULL) - need_details = g_slist_append(need_details, (char *)locate->ptr_value); - - } - } - - /* Read internal blocking deny list */ - locate = nm_locate_field(NM_A_BLOCKING_DENY_LIST, user->fields); - if (locate && locate->ptr_value) { - - if (locate->type == NMFIELD_TYPE_MV) { - locate = (NMField *)locate->ptr_value; - for (; locate->tag != NULL; locate++) { - if (locate->ptr_value) { - - user->deny_list = g_slist_append(user->deny_list, (char *)locate->ptr_value); - - if (nm_find_user_record(user, (char *)locate->ptr_value) == NULL) - need_details = g_slist_append(need_details, (char *)locate->ptr_value); - - } - } - } else { - - user->deny_list = g_slist_append(user->deny_list, (char *)locate->ptr_value); - - if (nm_find_user_record(user, (char *)locate->ptr_value) == NULL) - need_details = g_slist_append(need_details, (char *)locate->ptr_value); - - } - } - - if (need_details) { - - nm_request_add_ref(request); - nm_send_multiple_get_details(user, need_details, - _handle_multiple_get_details_login_cb, request); - - return FALSE; - } - - return TRUE; -} - -void -nm_destroy_contact_list(NMUser * user) -{ - if (user == NULL) - return; - - if (user->root_folder) { - nm_release_folder(user->root_folder); - user->root_folder = NULL; - } -} - -NMFolder * -nm_get_root_folder(NMUser * user) -{ - if (user == NULL) - return NULL; - - if (user->root_folder == NULL) - nm_create_contact_list(user); - - return user->root_folder; -} - -NMContact * -nm_find_contact(NMUser * user, const char *name) -{ - char *str; - const char *dn = NULL; - NMContact *contact = NULL; - - if (user == NULL || name == NULL) - return NULL; - - str = g_utf8_strdown(name, -1); - if (strstr(str, "=")) { - dn = str; - } else { - /* Assume that we have a display id instead of a dn */ - dn = (const char *) g_hash_table_lookup(user->display_id_to_dn, str); - } - - /* Find contact object in reference table */ - if (dn) { - contact = (NMContact *) g_hash_table_lookup(user->contacts, dn); - } - - g_free(str); - return contact; -} - -GList * -nm_find_contacts(NMUser * user, const char *dn) -{ - guint32 i, cnt; - NMFolder *folder; - NMContact *contact; - GList *contacts = NULL; - - if (user == NULL || dn == NULL) - return NULL; - - /* Check for contact at the root */ - contact = nm_folder_find_contact(user->root_folder, dn); - if (contact) { - contacts = g_list_append(contacts, contact); - contact = NULL; - } - - /* Check for contact in each subfolder */ - cnt = nm_folder_get_subfolder_count(user->root_folder); - for (i = 0; i < cnt; i++) { - folder = nm_folder_get_subfolder(user->root_folder, i); - contact = nm_folder_find_contact(folder, dn); - if (contact) { - contacts = g_list_append(contacts, contact); - contact = NULL; - } - } - - return contacts; -} - -NMUserRecord * -nm_find_user_record(NMUser * user, const char *name) -{ - char *str = NULL; - const char *dn = NULL; - NMUserRecord *user_record = NULL; - - if (user == NULL || name == NULL) - return NULL; - - str = g_utf8_strdown(name, -1); - if (strstr(str, "=")) { - dn = str; - } else { - /* Assume that we have a display id instead of a dn */ - dn = (const char *) g_hash_table_lookup(user->display_id_to_dn, str); - } - - /* Find user record in reference table */ - if (dn) { - user_record = - (NMUserRecord *) g_hash_table_lookup(user->user_records, dn); - } - - g_free(str); - return user_record; -} - -const char * -nm_lookup_dn(NMUser * user, const char *display_id) -{ - const char *dn; - char *lower; - - if (user == NULL || display_id == NULL) - return NULL; - - lower = g_utf8_strdown(display_id, -1); - dn = g_hash_table_lookup(user->display_id_to_dn, lower); - g_free(lower); - - return dn; -} - -NMFolder * -nm_find_folder(NMUser * user, const char *name) -{ - NMFolder *folder = NULL, *temp; - int i, num_folders; - const char *tname = NULL; - - if (user == NULL || name == NULL) - return NULL; - - if (*name == '\0') - return user->root_folder; - - num_folders = nm_folder_get_subfolder_count(user->root_folder); - for (i = 0; i < num_folders; i++) { - temp = nm_folder_get_subfolder(user->root_folder, i); - tname = nm_folder_get_name(temp); - if (tname && purple_strequal(tname, name)) { - folder = temp; - break; - } - } - - return folder; -} - -NMFolder * -nm_find_folder_by_id(NMUser * user, int object_id) -{ - NMFolder *folder = NULL, *temp; - int i, num_folders; - - if (user == NULL) - return NULL; - - if (object_id == 0) - return user->root_folder; - - num_folders = nm_folder_get_subfolder_count(user->root_folder); - for (i = 0; i < num_folders; i++) { - temp = nm_folder_get_subfolder(user->root_folder, i); - if (nm_folder_get_id(temp) == object_id) { - folder = temp; - break; - } - } - - return folder; -} - -static void -_handle_multiple_get_details_login_cb(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - nm_response_cb cb; - NMRequest *request = user_data; - - if (user == NULL || request == NULL) - return; - - if ((cb = nm_request_get_callback(request))) { - cb(user, ret_code, nm_request_get_data(request), - nm_request_get_user_define(request)); - nm_release_request(request); - } -} - -static void -_handle_multiple_get_details_joinconf_cb(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - NMRequest *request = user_data; - NMUserRecord *user_record = resp_data; - NMConference *conference; - GSList *list, *node; - - if (user == NULL || resp_data == NULL || user_data == NULL) - return; - - conference = nm_request_get_data(request); - list = nm_request_get_user_define(request); - - if (ret_code == 0 && conference && list) { - - /* Add the user to the conference */ - nm_conference_add_participant(conference, user_record); - - /* Find the user in the list and remove it */ - for (node = list; node; node = node->next) { - if (nm_utf8_str_equal(nm_user_record_get_dn(user_record), - (const char *) node->data)) { - g_free(node->data); - list = g_slist_remove_link(list, node); - nm_request_set_user_define(request, list); - break; - } - } - - /* Time to callback? */ - if (list == NULL) { - nm_response_cb cb = nm_request_get_callback(request); - - if (cb) { - cb(user, 0, conference, conference); - } - nm_release_request(request); - } - } -} - -static NMERR_T -nm_call_handler(NMUser * user, NMRequest * request, NMField * fields) -{ - NMERR_T rc = NM_OK, ret_code = NM_OK; - NMConference *conf = NULL; - NMUserRecord *user_record = NULL; - NMField *locate = NULL; - NMField *field = NULL; - const char *cmd; - nm_response_cb cb; - gboolean done = TRUE; - - if (user == NULL || request == NULL || fields == NULL) - return NMERR_BAD_PARM; - - /* Get the return code */ - field = nm_locate_field(NM_A_SZ_RESULT_CODE, fields); - if (field) { - ret_code = atoi((char *) field->ptr_value); - } else { - ret_code = NMERR_PROTOCOL; - } - - cmd = nm_request_get_cmd(request); - if (ret_code == NM_OK && cmd != NULL) { - - if (purple_strequal("login", cmd)) { - - user->user_record = nm_create_user_record_from_fields(fields); - - /* Save the users fields */ - user->fields = nm_copy_field_array(fields); - - nm_create_contact_list(user); - done = _create_privacy_list(user, request); - - } else if (purple_strequal("setstatus", cmd)) { - - /* Nothing to do */ - - } else if (purple_strequal("createconf", cmd)) { - - conf = (NMConference *) nm_request_get_data(request); - - /* get the convo guid */ - locate = nm_locate_field(NM_A_FA_CONVERSATION, fields); - if (locate) { - field = - nm_locate_field(NM_A_SZ_OBJECT_ID, (NMField *) fields->ptr_value); - if (field) { - nm_conference_set_guid(conf, (char *) field->ptr_value); - } - } - - nm_conference_list_add(user, conf); - nm_release_conference(conf); - - } else if (purple_strequal("leaveconf", cmd)) { - - conf = (NMConference *) nm_request_get_data(request); - nm_conference_list_remove(user, conf); - - } else if (purple_strequal("joinconf", cmd)) { - GSList *list = NULL, *node; - - conf = nm_request_get_data(request); - - locate = nm_locate_field(NM_A_FA_CONTACT_LIST, fields); - if (locate && locate->ptr_value != 0) { - - field = (NMField *) locate->ptr_value; - while ((field = nm_locate_field(NM_A_SZ_DN, field))) { - if (field && field->ptr_value != 0) { - - if (nm_utf8_str_equal - (nm_user_record_get_dn(user->user_record), - (const char *) field->ptr_value)) { - field++; - continue; - } - - user_record = - nm_find_user_record(user, - (const char *) field->ptr_value); - if (user_record == NULL) { - list = - g_slist_append(list, - g_strdup((char *) field->ptr_value)); - } else { - nm_conference_add_participant(conf, user_record); - } - } - field++; - } - - if (list != NULL) { - - done = FALSE; - nm_request_set_user_define(request, list); - nm_request_add_ref(request); - for (node = list; node; node = node->next) { - - nm_send_get_details(user, (const char *) node->data, - _handle_multiple_get_details_joinconf_cb, - request); - } - } - } - - } else if (purple_strequal("getdetails", cmd)) { - - locate = nm_locate_field(NM_A_FA_RESULTS, fields); - while (locate && locate->ptr_value != 0) { - - user_record = nm_create_user_record_from_fields(locate); - if (user_record) { - NMUserRecord *tmp; - - tmp = - nm_find_user_record(user, - nm_user_record_get_dn(user_record)); - if (tmp) { - - /* Update the existing user record */ - nm_user_record_copy(tmp, user_record); - nm_release_user_record(user_record); - user_record = tmp; - - } else { - nm_user_add_user_record(user, user_record); - nm_release_user_record(user_record); - } - - /* Response data is new user record */ - nm_request_set_data(request, (gpointer) user_record); - } - - locate = nm_locate_field(NM_A_FA_RESULTS, locate+1); - } - - } else if (purple_strequal("createfolder", cmd)) { - - _update_contact_list(user, fields); - - } else if (purple_strequal("createcontact", cmd)) { - - _update_contact_list(user, fields); - - locate = - nm_locate_field(NM_A_SZ_OBJECT_ID, (NMField *) fields->ptr_value); - if (locate) { - - NMContact *new_contact = - nm_folder_find_item_by_object_id(user->root_folder, - atoi((char *)locate->ptr_value)); - - if (new_contact) { - - /* Add the contact to our cache */ - nm_user_add_contact(user, new_contact); - - /* Set the contact as the response data */ - nm_request_set_data(request, (gpointer) new_contact); - - } - - } - - } else if (purple_strequal("deletecontact", cmd)) { - - _update_contact_list(user, fields); - - } else if (purple_strequal("movecontact", cmd)) { - - _update_contact_list(user, fields); - - } else if (purple_strequal("getstatus", cmd)) { - - locate = nm_locate_field(NM_A_SZ_STATUS, fields); - if (locate) { - nm_user_record_set_status((NMUserRecord *) - nm_request_get_data(request), - atoi((char *) locate->ptr_value), NULL); - } - - } else if (purple_strequal("updateitem", cmd)) { - - /* Nothing extra to do here */ - - } else if (purple_strequal("createblock", cmd)) { - if ((locate = nm_locate_field(NM_A_BLOCKING_DENY_LIST, fields))) { - if (locate->ptr_value) { - user->deny_list = g_slist_append(user->deny_list, g_strdup((char *)locate->ptr_value)); - } - } else if ((locate = nm_locate_field(NM_A_BLOCKING_ALLOW_LIST, fields))) { - if (locate->ptr_value) { - user->allow_list = g_slist_append(user->allow_list, g_strdup((char *)locate->ptr_value)); - } - } - } else if (purple_strequal("updateblocks", cmd)) { - /* nothing to do here */ - } else { - - /* Nothing to do, just print debug message */ - purple_debug(PURPLE_DEBUG_INFO, "novell", - "nm_call_handler(): Unknown request command, %s\n", cmd); - - } - } - - if (done && (cb = nm_request_get_callback(request))) { - - cb(user, ret_code, nm_request_get_data(request), - nm_request_get_user_define(request)); - } - - return rc; -} - -static NMERR_T -nm_process_response(NMUser * user) -{ - NMERR_T rc = NM_OK; - NMField *fields = NULL; - NMField *field = NULL; - NMConn *conn = user->conn; - NMRequest *req = NULL; - - rc = nm_read_header(conn); - if (rc == NM_OK) { - rc = nm_read_fields(conn, -1, &fields); - } - - if (rc == NM_OK) { - field = nm_locate_field(NM_A_SZ_TRANSACTION_ID, fields); - if (field != NULL && field->ptr_value != 0) { - req = nm_conn_find_request(conn, atoi((char *) field->ptr_value)); - if (req != NULL) { - rc = nm_call_handler(user, req, fields); - nm_conn_remove_request_item(conn, req); - } - - } - } - - if (fields) - nm_free_fields(&fields); - - return rc; -} - -/* - * Some utility functions...haven't figured out where - * they belong yet. - */ - -gboolean -nm_utf8_str_equal(gconstpointer str1, gconstpointer str2) -{ - return (purple_utf8_strcasecmp(str1, str2) == 0); -} - -char * -nm_typed_to_dotted(const char *typed) -{ - unsigned i = 0, j = 0; - char *dotted; - - if (typed == NULL) - return NULL; - - dotted = g_new0(char, strlen(typed)); - - do { - - /* replace comma with a dot */ - if (j != 0) { - dotted[j] = '.'; - j++; - } - - /* skip the type */ - while (typed[i] != '\0' && typed[i] != '=') - i++; - - /* verify that we aren't running off the end */ - if (typed[i] == '\0') { - dotted[j] = '\0'; - break; - } - - i++; - - /* copy the object name to context */ - while (typed[i] != '\0' && typed[i] != ',') { - dotted[j] = typed[i]; - j++; - i++; - } - - } while (typed[i] != '\0'); - - return dotted; -} - -const char * -nm_error_to_string(NMERR_T err) -{ - static char *unknown_msg = NULL; - - g_free(unknown_msg); - unknown_msg = NULL; - - switch (err) { - - case NMERR_BAD_PARM: - return _("Required parameters not passed in"); - - case NMERR_TCP_WRITE: - return _("Unable to write to network"); - - case NMERR_TCP_READ: - return _("Unable to read from network"); - - case NMERR_PROTOCOL: - return _("Error communicating with server"); - - case NMERR_CONFERENCE_NOT_FOUND: - case NMERR_CONFERENCE_NOT_FOUND_2: - return _("Conference not found"); - - case NMERR_CONFERENCE_NOT_INSTANTIATED: - return _("Conference does not exist"); - - case NMERR_DUPLICATE_FOLDER: - case NMERR_FOLDER_EXISTS: - return _("A folder with that name already exists"); - - case NMERR_NOT_SUPPORTED: - return _("Not supported"); - - case NMERR_PASSWORD_EXPIRED: - case NMERR_PASSWORD_EXPIRED_2: - return _("Password has expired"); - - case NMERR_PASSWORD_INVALID: - return _("Incorrect password"); - - case NMERR_USER_NOT_FOUND: - return _("User not found"); - - case NMERR_USER_DISABLED: - return _("Account has been disabled"); - - case NMERR_DIRECTORY_FAILURE: - return _("The server could not access the directory"); - - case NMERR_ADMIN_LOCKED: - return _("Your system administrator has disabled this operation"); - - case NMERR_SERVER_BUSY: - return _("The server is unavailable; try again later"); - - case NMERR_DUPLICATE_CONTACT: - return _("Cannot add a contact to the same folder twice"); - - case NMERR_USER_NOT_ALLOWED: - return _("Cannot add yourself"); - - case NMERR_MASTER_ARCHIVE_MISSING: - return _("Master archive is misconfigured"); - - case NMERR_AUTHENTICATION_FAILED: - case NMERR_CREDENTIALS_MISSING: - return _("Incorrect username or password"); - - case NMERR_HOST_NOT_FOUND: - return _("Could not recognize the host of the username you entered"); - - case NMERR_ACCESS_DENIED: - return _("Your account has been disabled because too many incorrect passwords were entered"); - - case NMERR_DUPLICATE_PARTICIPANT: - return _("You cannot add the same person twice to a conversation"); - - case NMERR_TOO_MANY_CONTACTS: - case NMERR_TOO_MANY_FOLDERS: - return _("You have reached your limit for the number of contacts allowed"); - - case NMERR_OBJECT_NOT_FOUND: - return _("You have entered an incorrect username"); - - case NMERR_DIRECTORY_UPDATE: - return _("An error occurred while updating the directory"); - - case NMERR_SERVER_PROTOCOL: - return _("Incompatible protocol version"); - - case NMERR_USER_BLOCKED: - return _("The user has blocked you"); - - case NMERR_EVAL_CONNECTION_LIMIT: - return _("This evaluation version does not allow more than ten users to log in at one time"); - - case NMERR_CONVERSATION_INVITE: - return _("The user is either offline or you are blocked"); - - default: - unknown_msg = g_strdup_printf (_("Unknown error: 0x%X"), err); - - return unknown_msg; - } -} - -static void -_update_contact_list(NMUser * user, NMField * fields) -{ - NMField *list, *cursor, *locate; - gint objid1; - NMContact *contact; - NMFolder *folder; - gpointer item; - - if (user == NULL || fields == NULL) - return; - - /* Is it wrapped in a RESULTS array? */ - if (purple_strequal(fields->tag, NM_A_FA_RESULTS)) { - list = (NMField *) fields->ptr_value; - } else { - list = fields; - } - - /* Update the cached contact list */ - cursor = (NMField *) list->ptr_value; - while (cursor->tag != NULL) { - if ((g_ascii_strcasecmp(cursor->tag, NM_A_FA_CONTACT) == 0) || - (g_ascii_strcasecmp(cursor->tag, NM_A_FA_FOLDER) == 0)) { - - locate = - nm_locate_field(NM_A_SZ_OBJECT_ID, (NMField *) cursor->ptr_value); - if (locate != NULL && locate->ptr_value != 0) { - objid1 = atoi((char *) locate->ptr_value); - item = - nm_folder_find_item_by_object_id(user->root_folder, objid1); - if (item != NULL) { - if (cursor->method == NMFIELD_METHOD_ADD) { - if (g_ascii_strcasecmp(cursor->tag, NM_A_FA_CONTACT) == 0) { - contact = (NMContact *) item; - nm_contact_update_list_properties(contact, cursor); - } else if (g_ascii_strcasecmp(cursor->tag, NM_A_FA_FOLDER) - == 0) { - folder = (NMFolder *) item; - nm_folder_update_list_properties(folder, cursor); - } - } else if (cursor->method == NMFIELD_METHOD_DELETE) { - if (g_ascii_strcasecmp(cursor->tag, NM_A_FA_CONTACT) == 0) { - contact = (NMContact *) item; - folder = - nm_find_folder_by_id(user, - nm_contact_get_parent_id - (contact)); - if (folder) { - nm_folder_remove_contact(folder, contact); - } - } else if (g_ascii_strcasecmp(cursor->tag, NM_A_FA_FOLDER) - == 0) { - /* TODO: write nm_folder_remove_folder */ - /* ignoring for now, should not be a big deal */ -/* folder = (NMFolder *) item;*/ -/* nm_folder_remove_folder(user->root_folder, folder);*/ - } - } - } else { - - if (cursor->method == NMFIELD_METHOD_ADD) { - - /* Not found, so we need to add it */ - if (g_ascii_strcasecmp(cursor->tag, NM_A_FA_CONTACT) == 0) { - - const char *dn = NULL; - - locate = - nm_locate_field(NM_A_SZ_DN, - (NMField *) cursor->ptr_value); - if (locate != NULL && locate->ptr_value != 0) { - dn = (const char *) locate->ptr_value; - if (dn != NULL) { - contact = - nm_create_contact_from_fields(cursor); - if (contact) { - nm_folder_add_contact_to_list(user-> - root_folder, - contact); - nm_release_contact(contact); - } - } - } - } else if (g_ascii_strcasecmp(cursor->tag, NM_A_FA_FOLDER) - == 0) { - folder = nm_create_folder_from_fields(cursor); - nm_folder_add_folder_to_list(user->root_folder, - folder); - nm_release_folder(folder); - } - } - } - } - } - cursor++; - } -} - -static char * -nm_rtfize_text(char *text) -{ - GString *gstr = NULL; - unsigned char *pch; - char *uni_str = NULL, *rtf = NULL; - int bytes; - gunichar uc; - - gstr = g_string_sized_new(strlen(text)*2); - pch = (unsigned char *)text; - while (*pch) { - if ((*pch) <= 0x7F) { - switch (*pch) { - case '{': - case '}': - case '\\': - gstr = g_string_append_c(gstr, '\\'); - gstr = g_string_append_c(gstr, *pch); - break; - case '\n': - gstr = g_string_append(gstr, "\\par "); - break; - default: - gstr = g_string_append_c(gstr, *pch); - break; - } - pch++; - } else { - /* convert the utf-8 character to ucs-4 for rtf encoding */ - if(*pch <= 0xDF) { - uc = ((((gunichar)pch[0]) & 0x001F) << 6) | - (((gunichar)pch[1]) & 0x003F); - bytes = 2; - } else if(*pch <= 0xEF) { - uc = ((((gunichar)pch[0]) & 0x000F) << 12) | - ((((gunichar)pch[1]) & 0x003F) << 6) | - (((gunichar)pch[2]) & 0x003F); - bytes = 3; - } else if (*pch <= 0xF7) { - uc = ((((gunichar)pch[0]) & 0x0007) << 18) | - ((((gunichar)pch[1]) & 0x003F) << 12) | - ((((gunichar)pch[2]) & 0x003F) << 6) | - (((gunichar)pch[3]) & 0x003F); - bytes = 4; - } else if (*pch <= 0xFB) { - uc = ((((gunichar)pch[0]) & 0x0003) << 24) | - ((((gunichar)pch[1]) & 0x003F) << 18) | - ((((gunichar)pch[2]) & 0x003F) << 12) | - ((((gunichar)pch[3]) & 0x003F) << 6) | - (((gunichar)pch[4]) & 0x003F); - bytes = 5; - } else if (*pch <= 0xFD) { - uc = ((((gunichar)pch[0]) & 0x0001) << 30) | - ((((gunichar)pch[1]) & 0x003F) << 24) | - ((((gunichar)pch[2]) & 0x003F) << 18) | - ((((gunichar)pch[3]) & 0x003F) << 12) | - ((((gunichar)pch[4]) & 0x003F) << 6) | - (((gunichar)pch[5]) & 0x003F); - bytes = 6; - } else { - /* should never happen ... bogus utf-8! */ - purple_debug_info("novell", "bogus utf-8 lead byte: 0x%X\n", pch[0]); - uc = 0x003F; - bytes = 1; - } - uni_str = g_strdup_printf("\\u%d?", uc); - purple_debug_info("novell", "unicode escaped char %s\n", uni_str); - gstr = g_string_append(gstr, uni_str); - pch += bytes; - g_free(uni_str); - } - } - - rtf = g_strdup_printf(RTF_TEMPLATE, gstr->str); - g_string_free(gstr, TRUE); - return rtf; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmuser.h --- a/libpurple/protocols/novell/nmuser.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,680 +0,0 @@ -/* - * nmuser.h - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 __NM_USER_H__ -#define __NM_USER_H__ - -#include -#include -#include - -typedef guint32 NMERR_T; -typedef int NMSTATUS_T; - -typedef struct _NMUser NMUser; - -typedef enum -{ - NMREQUEST_TYPE_LOGIN = 0, - NMREQUEST_TYPE_LOGOUT, - NMREQUEST_TYPE_SETSTATUS, - NMREQUEST_TYPE_GETDETAILS, - NMREQUEST_TYPE_CREATECONF, - NMREQUEST_TYPE_SENDMESSAGE, - NMREQUEST_TYPE_JOINCONF, - NMREQUEST_TYPE_LEAVECONF, - NMREQUEST_TYPE_REJECTCONF, - NMREQUEST_TYPE_SENDTYPING, - NMREQUEST_TYPE_CREATECONTACT, - NMREQUEST_TYPE_DELETECONTACT - -} NMRequestType; - -#include "debug.h" -#include "nmmessage.h" -#include "nmconference.h" -#include "nmcontact.h" -#include "nmuserrecord.h" -#include "nmfield.h" -#include "nmevent.h" - -/* Callback typedefs */ -typedef void (*nm_response_cb) (NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data); - -typedef void (*nm_event_cb) (NMUser * user, NMEvent * event); - -#include "nmrequest.h" -#include "nmconn.h" - -/* This represents user that we are currently logged in as */ -struct _NMUser -{ - - char *name; - - NMSTATUS_T status; - - /* A copy of the login response fields */ - NMField *fields; - - /* The user record for this user */ - NMUserRecord *user_record; - - /* Our connection information */ - NMConn *conn; - - /* Our public IP address */ - char *address; - - /* This is the contact list */ - NMFolder *root_folder; - - /* All contacts that we know about hashed by dn */ - GHashTable *contacts; - - /* All user records hashed by dn */ - GHashTable *user_records; - - /* DN lookup */ - GHashTable *display_id_to_dn; - - /* One on one conversations indexed by recipient's dn */ - GSList *conferences; - - guint32 conference_count; - - /* Called when we receive an event */ - nm_event_cb evt_callback; - - /* Privacy settings */ - gboolean privacy_locked; - gboolean default_deny; - GSList *allow_list; - GSList *deny_list; - - /* Pending requests. If we need to go to the server to more info - * before processing a request we will queue it up and process when - * we get a response - */ - GSList *pending_requests; - - /* Pending events. Same as above except for events. */ - GSList *pending_events; - - /* Generic pointer to data needed by the client - * using the API - */ - gpointer client_data; - - /* Have the privacy lists been synched yet */ - gboolean privacy_synched; - - /* Has the contact list been synched */ - gboolean clist_synched; -}; - -#define NM_STATUS_UNKNOWN 0 -#define NM_STATUS_OFFLINE 1 -#define NM_STATUS_AVAILABLE 2 -#define NM_STATUS_BUSY 3 -#define NM_STATUS_AWAY 4 -#define NM_STATUS_AWAY_IDLE 5 -#define NM_STATUS_INVALID 6 - -#define NMERR_BASE 0x2000L -#define NM_OK 0L -#define NMERR_BAD_PARM (NMERR_BASE + 0x0001) -#define NMERR_TCP_WRITE (NMERR_BASE + 0x0002) -#define NMERR_TCP_READ (NMERR_BASE + 0x0003) -#define NMERR_PROTOCOL (NMERR_BASE + 0x0004) -#define NMERR_SERVER_REDIRECT (NMERR_BASE + 0x0005) -#define NMERR_CONFERENCE_NOT_FOUND (NMERR_BASE + 0x0006) -#define NMERR_CONFERENCE_NOT_INSTANTIATED (NMERR_BASE + 0x0007) -#define NMERR_FOLDER_EXISTS (NMERR_BASE + 0x0008) - -/* Errors that are returned from the server */ -#define NMERR_SERVER_BASE 0xD100L -#define NMERR_ACCESS_DENIED (NMERR_SERVER_BASE + 0x0006) -#define NMERR_NOT_SUPPORTED (NMERR_SERVER_BASE + 0x000A) -#define NMERR_PASSWORD_EXPIRED (NMERR_SERVER_BASE + 0x000B) -#define NMERR_PASSWORD_INVALID (NMERR_SERVER_BASE + 0x000C) -#define NMERR_USER_NOT_FOUND (NMERR_SERVER_BASE + 0x000D) -#define NMERR_USER_DISABLED (NMERR_SERVER_BASE + 0x0010) -#define NMERR_DIRECTORY_FAILURE (NMERR_SERVER_BASE + 0x0011) -#define NMERR_HOST_NOT_FOUND (NMERR_SERVER_BASE + 0x0019) -#define NMERR_ADMIN_LOCKED (NMERR_SERVER_BASE + 0x001C) -#define NMERR_DUPLICATE_PARTICIPANT (NMERR_SERVER_BASE + 0x001F) -#define NMERR_SERVER_BUSY (NMERR_SERVER_BASE + 0x0023) -#define NMERR_OBJECT_NOT_FOUND (NMERR_SERVER_BASE + 0x0024) -#define NMERR_DIRECTORY_UPDATE (NMERR_SERVER_BASE + 0x0025) -#define NMERR_DUPLICATE_FOLDER (NMERR_SERVER_BASE + 0x0026) -#define NMERR_DUPLICATE_CONTACT (NMERR_SERVER_BASE + 0x0027) -#define NMERR_USER_NOT_ALLOWED (NMERR_SERVER_BASE + 0x0028) -#define NMERR_TOO_MANY_CONTACTS (NMERR_SERVER_BASE + 0x0029) -#define NMERR_CONFERENCE_NOT_FOUND_2 (NMERR_SERVER_BASE + 0x002B) -#define NMERR_TOO_MANY_FOLDERS (NMERR_SERVER_BASE + 0x002C) -#define NMERR_SERVER_PROTOCOL (NMERR_SERVER_BASE + 0x0030) -#define NMERR_CONVERSATION_INVITE (NMERR_SERVER_BASE + 0x0035) -#define NMERR_USER_BLOCKED (NMERR_SERVER_BASE + 0x0039) -#define NMERR_MASTER_ARCHIVE_MISSING (NMERR_SERVER_BASE + 0x003A) -#define NMERR_PASSWORD_EXPIRED_2 (NMERR_SERVER_BASE + 0x0042) -#define NMERR_CREDENTIALS_MISSING (NMERR_SERVER_BASE + 0x0046) -#define NMERR_AUTHENTICATION_FAILED (NMERR_SERVER_BASE + 0x0049) -#define NMERR_EVAL_CONNECTION_LIMIT (NMERR_SERVER_BASE + 0x004A) - -/** - * Initialize the user that we are going to login to the system as. - * - * @param name The userid of the user - * @param server IP Address of server - * @param port Port to connect to on the server - * @param data Client data to associate with the user - * @param event_callback Function to call when we receive an event - * - * @return The initialized user object. Must be freed by calling - * nm_deinitialize_user - */ -NMUser *nm_initialize_user(const char *name, const char *server, int port, - gpointer data, nm_event_cb event_callback); - - -/** - * Free up resources associated with the user object. - * - * @param user The user to deinitialize - */ -void nm_deinitialize_user(NMUser * user); - -/** - * Send a login request to the server. - * - * The response data sent to the callback will be NULL. - * - * @param user The User to login -- must be initialized - * @param pwd The password of the user - * @param my_addr The address of the client machine - * @param user_agent String describing the client (eg. "Purple/0.76 (Linux; 2.4.20)") - * @param callback Function to call when we get the response from the server - * @param data User defined data to be passed to the callback function - * - * - * @return NM_OK if login is sent successfully, error otherwise. - */ -NMERR_T nm_send_login(NMUser * user, const char *pwd, const char *my_addr, - const char *user_agent, nm_response_cb callback, - gpointer data); - -/** - * Send a set status request to the server. - * - * The response data sent to the callback will be NULL. - * - * @param user The logged in User - * @param dn The DN of the user (if known, or NULL if not known) - * @param address IP Address of server - * @param callback Function to call when we get the response from the server - * - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_set_status(NMUser * user, int status, const char *text, - const char *auto_resp, nm_response_cb callback, - gpointer data); - -/** - * Send a create conference to the server. - * - * The response data sent to the callback will be NULL. - * - * @param user The logged in User - * @param conference Conference to create - * @param add_participants Add participant list on create? - * @param callback Function to call when we get the response from the server - * @param data User defined data to be passed to the callback function - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_create_conference(NMUser * user, NMConference * conference, - nm_response_cb callback, gpointer data); - -/** - * Tell server we have left the conference. - * - * The response data sent to the callback will be NULL. - * - * @param user The logged in User - * @param conference Conference the user is leaving - * @param callback Function to call when we get the response from the server - * @param data User defined data to be passed to the callback function - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_leave_conference(NMUser * user, NMConference * conference, - nm_response_cb callback, gpointer data); - -/** - * Send a join conference request to the server. - * - * The response data sent to the callback will be NULL. - * - * @param user The logged in User - * @param conference Conference the user is joining - * @param callback Function to call when we get the response from the server - * @param data User defined data to be passed to the callback function - * - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_join_conference(NMUser * user, NMConference * conference, - nm_response_cb callback, gpointer data); - -/** - * Send a conference reject request to the server. - * - * The response data sent to the callback will be NULL. - * - * @param user The logged in User - * @param conference Conference the user is rejecting - * @param callback Function to call when we get the response from the server - * @param data User defined data to be passed to the callback function - * - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_reject_conference(NMUser * user, NMConference * conference, - nm_response_cb callback, gpointer data); - - -/** - * Send a conference invitation to the server. - * - * The response data sent to the callback will be NULL. - * - * @param user The logged in User - * @param conference Conference the user is rejecting - * @param user_record The user to invite - * @param message The invite message if there is one, NULL otherwise - * @param callback Function to call when we get the response from the server - * @param data User defined data to be passed to the callback function - * - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_conference_invite(NMUser *user, NMConference *conference, NMUserRecord *user_record, - const char *message, nm_response_cb callback, gpointer data); - -/** - * Get details for a more than one user from the server. - * - * The response data sent to the callback will be an NMUserRecord which should be - * freed with nm_release_user_record - * - * @param user The logged in User - * @param names Link list of user id's or dn's - * @param callback Function to call when we get the response from the server - * @param data User defined data to be passed to the callback function - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_multiple_get_details(NMUser * user, GSList *names, - nm_response_cb callback, gpointer data); - -/** - * Get details for a user from the server. - * - * The response data sent to the callback will be an NMUserRecord which should be - * freed with nm_release_user_record - * - * @param user The logged in User - * @param name Userid or DN of the user to look up - * @param callback Function to call when we get the response from the server - * @param data User defined data to be passed to the callback function - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_get_details(NMUser * user, const char *name, - nm_response_cb callback, gpointer data); - -/** - * Send a message. - * - * The response data sent to the callback will be NULL. - * - * @param user The logged in User - * @param message The message to send. - * @param callback Function to call when we get the response from the server - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_message(NMUser * user, NMMessage * message, - nm_response_cb callback); - -/** - * Sends a typing event to the server. - * - * The response data sent to the callback will be NULL. - * - * @param user The logged in User - * @param conf The conference that corresponds to the typing event - * @param typing TRUE if the user is typing - * FALSE if the user has stopped typing - * @param callback Function to call when we get the response from the server - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_typing(NMUser * user, NMConference * conf, - gboolean typing, nm_response_cb callback); - -/** - * Send a create contact request to the server. - * - * The given folder should already exist on the server. If not, - * the call will fail. - * - * The response data sent to the callback will be a NMContact which should - * be released with nm_release_contact - * - * @param user The logged in User - * @param folder The folder that the contact should be created in - * @param contact The contact to add - * @param callback Function to call when we get the response from the server - * @param data User defined data - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_create_contact(NMUser * user, NMFolder * folder, - NMContact * contact, nm_response_cb callback, - gpointer data); - -/** - * Send a remove contact request to the server. - * - * The response data sent to the callback will be NULL. - * - * @param user The logged in User - * @param folder The folder to remove contact from - * @param contact The contact to remove - * @param callback Function to call when we get the response from the server - * @param data User defined data - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_remove_contact(NMUser * user, NMFolder * folder, - NMContact * contact, nm_response_cb callback, - gpointer data); - -/** - * Send a create folder request to the server. - * - * The response data sent to the callback will be a NMFolder which should be - * released with nm_release_folder - * - * @param user The logged in User - * @param name The name of the folder to create - * @param callback Function to call when we get the response from the server - * @param data User defined data - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_create_folder(NMUser * user, const char *name, - nm_response_cb callback, gpointer data); - -/** - * Send a delete folder request to the server. - * - * The response data sent to the callback will be NULL. - * - * @param user The logged in User - * @param folder The name of the folder to remove - * @param callback Function to call when we get the response from the server - * @param data User defined data - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_remove_folder(NMUser * user, NMFolder * folder, - nm_response_cb callback, gpointer data); - -/** - * Send a rename contact request to the server. - * - * The response data sent to the callback will be NULL. - * - * @param user The logged in User - * @param contact The contact to rename - * @param new_name The new display name for the contact - * @param callback Function to call when we get the response from the server - * @param data User defined data - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_rename_contact(NMUser * user, NMContact * contact, - const char *new_name, nm_response_cb callback, - gpointer data); - -/** - * Send a rename folder request to the server. - * - * The response data sent to the callback will be NULL. - * - * @param user The logged in User - * @param folder The folder to rename - * @param new_name The new name of the folder - * @param callback Function to call when we get the response from the server - * @param data User defined data - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_rename_folder(NMUser * user, NMFolder * folder, - const char *new_name, nm_response_cb callback, - gpointer data); - -/** - * Send a move contact request to the server. - * - * The response data sent to the callback will be NULL. - * - * @param user The logged in User - * @param contact The contact to move - * @param folder The folder to move the contact to - * @param callback Function to call when we get the response from the server - * @param data User defined data - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_move_contact(NMUser * user, NMContact * contact, - NMFolder * folder, nm_response_cb callback, - gpointer data); - -/** - * Send a get status request to the server. - * - * The response data sent to the callback will be a NMUserRecord. - * - * @param user The logged in User - * @param contact The contact to move - * @param folder The folder to move the contact to - * @param callback Function to call when we get the response from the server - * @param data User defined data - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T nm_send_get_status(NMUser * user, NMUserRecord * user_record, - nm_response_cb callback, gpointer data); - -/** - * Send a request to add an item to the allow or deny list. - * - * @param user The logged in User - * @param who The userid or DN of the user to add to list - * @param allow_list TRUE if adding to allow list, FALSE if adding to deny list - * @param callback Function to call when we get the response from the server - * @param data User defined data - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T -nm_send_create_privacy_item(NMUser *user, const char *who, gboolean is_allowed, - nm_response_cb callback, gpointer data); - -/** - * Send a request to remove an item from the allow or deny list. - * - * @param user The logged in User - * @param who The userid or DN of the user to add to list - * @param allow_list TRUE if removing from allow list, FALSE if removing from deny list - * @param callback Function to call when we get the response from the server - * @param data User defined data - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T -nm_send_remove_privacy_item(NMUser *user, const char *dn, gboolean allow_list, - nm_response_cb callback, gpointer data); - -/** - * Send a request to change the default privacy setting to deny all or allow all - * - * @param user The logged in User - * @param default_deny TRUE if default should be changed to deny all - * @param callback Function to call when we get the response from the server - * @param data User defined data - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T -nm_send_set_privacy_default(NMUser *user, gboolean default_deny, - nm_response_cb callback, gpointer data); - -/** - * Send a ping to the server - * - * @param user The logged in User - * @param callback Function to call when we get the response from the server - * @param data User defined data - * - * @return NM_OK if successfully sent, error otherwise - */ -NMERR_T -nm_send_keepalive(NMUser *user, nm_response_cb callback, gpointer data); - -/** - * Reads a response/event from the server and processes it. - * - * @param user The logged in User - */ -NMERR_T nm_process_new_data(NMUser * user); - -/** - * Return the root folder of the contact list - * - * @param user The logged in User - * - * @return Root folder - */ -NMFolder *nm_get_root_folder(NMUser * user); - -/** - * Create the contact list based on the login fields - * - * @param user The logged in User - * - */ -NMERR_T nm_create_contact_list(NMUser * user); - -void nm_destroy_contact_list(NMUser * user); - -void nm_user_add_contact(NMUser * user, NMContact * contact); - -void nm_user_add_user_record(NMUser * user, NMUserRecord * user_record); - -NMContact *nm_find_contact(NMUser * user, const char *dn); - -GList *nm_find_contacts(NMUser * user, const char *dn); - -NMUserRecord *nm_find_user_record(NMUser * user, const char *dn); - -NMFolder *nm_find_folder(NMUser * user, const char *name); - -NMFolder *nm_find_folder_by_id(NMUser * user, int object_id); - -NMConference *nm_find_conversation(NMUser * user, const char *who); - -void nm_conference_list_add(NMUser * user, NMConference * conf); - -void nm_conference_list_remove(NMUser * user, NMConference * conf); - -void nm_conference_list_free(NMUser * user); - -NMConference *nm_conference_list_find(NMUser * user, const char *guid); - -const char *nm_lookup_dn(NMUser * user, const char *display_id); - -nm_event_cb nm_user_get_event_callback(NMUser * user); - -NMConn *nm_user_get_conn(NMUser * user); - -gboolean nm_user_is_privacy_locked(NMUser *user); - -/** Some utility functions **/ - -/** - * Check to see if the conference GUIDs are equivalent. - * - * @param guid1 First guid to compare - * @param guid2 Second guid to compare - * - * @return TRUE if conference GUIDs are equivalent, FALSE otherwise. - * - */ -gboolean nm_are_guids_equal(const char *guid1, const char *guid2); - -/** - * Compare UTF8 strings for equality only (case insensitive) - * - * @param guid1 First string to compare - * @param guid2 Second string to compare - * - * @return TRUE if strings are equal, FALSE otherwise - * - */ -gboolean nm_utf8_str_equal(gconstpointer str1, gconstpointer str2); - -/** - * Convert a fully typed LDAP DN to dotted, untype notation - * e.g. cn=mike,o=novell -> mike.novell - * - * @param typed Fully typed dn - * - * @return Dotted equivalent of typed (must be freed). - * - */ -char *nm_typed_to_dotted(const char *typed); - -/** - * Return a string representation of the error code. - * - * @param error NMERR_T to convert to string - * - * @return String representation. - */ -const char *nm_error_to_string (NMERR_T err); - -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmuserrecord.c --- a/libpurple/protocols/novell/nmuserrecord.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,561 +0,0 @@ -/* - * nmuserrecord.c - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 -#include -#include "nmuserrecord.h" -#include "nmfield.h" -#include "nmuser.h" - -struct _NMUserRecord -{ - NMSTATUS_T status; - char *status_text; - char *dn; - char *cn; - char *display_id; - char *fname; - char *lname; - char *full_name; - NMField *fields; - gboolean auth_attr; - gpointer data; - int ref_count; -}; - -struct _NMProperty -{ - char *tag; - char *value; -}; - -static int count = 0; - -/* API functions */ - -NMUserRecord * -nm_create_user_record() -{ - NMUserRecord *user_record = g_new0(NMUserRecord, 1); - - user_record->ref_count = 1; - - purple_debug(PURPLE_DEBUG_INFO, "novell", "Creating user_record, total=%d\n", - count++); - - return user_record; -} - -static char * -_get_attribute_value(NMField *field) -{ - char *value = NULL; - - if (field->ptr_value == NULL) - return NULL; - - if (field->type == NMFIELD_TYPE_UTF8 || field->type == NMFIELD_TYPE_DN) { - - value = (char *)field->ptr_value; - - } else if (field->type == NMFIELD_TYPE_MV) { - - /* Need to handle multi-valued returns, for now - * just pick the first value and return it - */ - NMField *tmp = (NMField *)field->ptr_value; - if ((tmp != NULL) && - ((tmp->type == NMFIELD_TYPE_UTF8) || - (tmp->type == NMFIELD_TYPE_DN))) { - - value = (char *)tmp->ptr_value; - - } else { - return NULL; - } - - } else { - return NULL; - } - - return g_strdup(value); -} -/* - * This creates a user_record for the reference list the - * field array that is passed in should be a - * NM_A_FA_USER_DETAILS array. - */ -NMUserRecord * -nm_create_user_record_from_fields(NMField * details) -{ - NMUserRecord *user_record; - NMField *field, *fields = details; - - if (details == NULL) { - return NULL; - } - - if (details->type == NMFIELD_TYPE_ARRAY) { - if (details->ptr_value == NULL) - return NULL; - fields = (NMField *) details->ptr_value; - } - - user_record = nm_create_user_record(); - - if ((field = nm_locate_field(NM_A_SZ_AUTH_ATTRIBUTE, fields))) { - - if (field->ptr_value) { - user_record->display_id = _get_attribute_value(field); - user_record->auth_attr = TRUE; - } - } - - if ((field = nm_locate_field(NM_A_SZ_DN, fields))) { - - if (field->ptr_value) { - user_record->dn = _get_attribute_value(field); - } - } - - if ((field = nm_locate_field("CN", fields))) { - - if (field->ptr_value) { - user_record->cn = _get_attribute_value(field); - } - } - - if ((field = nm_locate_field("Given Name", fields))) { - - if (field->ptr_value) { - user_record->fname = _get_attribute_value(field); - } - } - - if ((field = nm_locate_field("Surname", fields))) { - - if (field->ptr_value) { - user_record->lname = _get_attribute_value(field); - } - } - - if ((field = nm_locate_field("Full Name", fields))) { - - if (field->ptr_value) { - user_record->full_name = _get_attribute_value(field); - } - } - - if ((field = nm_locate_field(NM_A_SZ_STATUS, fields))) { - - if (field->ptr_value) - user_record->status = atoi((char *) field->ptr_value); - - } - - if ((field = nm_locate_field(NM_A_SZ_MESSAGE_BODY, fields))) { - - if (field->ptr_value) - user_record->status_text = g_strdup((char *) field->ptr_value); - - } - - user_record->fields = nm_copy_field_array(fields); - - return user_record; -} - -void -nm_user_record_copy(NMUserRecord * dest, NMUserRecord * src) -{ - if (dest == NULL || src == NULL) - return; - - dest->status = src->status; - - /* Copy status text */ - if (dest->status_text) { - g_free(dest->status_text); - dest->status_text = NULL; - } - - if (src->status_text) - dest->status_text = g_strdup(src->status_text); - - /* Copy DN */ - if (dest->dn) { - g_free(dest->dn); - dest->dn = NULL; - } - - if (src->dn) - dest->dn = g_strdup(src->dn); - - /* Copy CN */ - if (dest->cn) { - g_free(dest->cn); - dest->cn = NULL; - } - - if (src->cn) - dest->cn = g_strdup(src->cn); - - /* Copy display id */ - if (dest->display_id) { - g_free(dest->display_id); - dest->display_id = NULL; - } - - if (src->display_id) - dest->display_id = g_strdup(src->display_id); - - /* Copy first name */ - if (dest->fname) { - g_free(dest->fname); - dest->fname = NULL; - } - - if (src->fname) - dest->fname = g_strdup(src->fname); - - /* Copy last name */ - if (dest->lname) { - g_free(dest->lname); - dest->lname = NULL; - } - - if (src->lname) - dest->lname = g_strdup(src->lname); - - /* Copy full name */ - if (dest->full_name) { - g_free(dest->full_name); - dest->full_name = NULL; - } - - if (src->full_name) - dest->full_name = g_strdup(src->full_name); - - /* Copy fields */ - if (src->fields) { - - if (dest->fields) { - nm_free_fields(&dest->fields); - } - - dest->fields = nm_copy_field_array(src->fields); - } - - /* Copy data */ - dest->data = src->data; -} - -void -nm_user_record_add_ref(NMUserRecord * user_record) -{ - if (user_record) - user_record->ref_count++; -} - -void -nm_release_user_record(NMUserRecord * user_record) -{ - if (--(user_record->ref_count) == 0) { - - purple_debug(PURPLE_DEBUG_INFO, "novell", - "Releasing user_record, total=%d\n", --count); - - if (user_record->dn) { - g_free(user_record->dn); - } - - if (user_record->cn) { - g_free(user_record->cn); - } - - if (user_record->display_id) { - g_free(user_record->display_id); - } - - if (user_record->fname) { - g_free(user_record->fname); - } - - if (user_record->lname) { - g_free(user_record->lname); - } - - if (user_record->full_name) { - g_free(user_record->full_name); - } - - if (user_record->status_text) { - g_free(user_record->status_text); - } - - nm_free_fields(&user_record->fields); - - g_free(user_record); - } -} - -/* UserRecord API */ - -NMSTATUS_T -nm_user_record_get_status(NMUserRecord * user_record) -{ - if (user_record == NULL) - return (NMSTATUS_T) - 1; - - return user_record->status; - -} - -const char * -nm_user_record_get_status_text(NMUserRecord * user_record) -{ - if (user_record == NULL) - return NULL; - - return user_record->status_text; -} - -void -nm_user_record_set_dn(NMUserRecord * user_record, const char *dn) -{ - if (user_record != NULL && dn != NULL) { - if (user_record->dn) - g_free(user_record->dn); - - user_record->dn = g_strdup(dn); - } -} - -const char * -nm_user_record_get_dn(NMUserRecord * user_record) -{ - if (user_record == NULL) - return NULL; - - return user_record->dn; -} - -void -nm_user_record_set_userid(NMUserRecord * user_record, const char *userid) -{ - if (user_record != NULL && userid != NULL) { - if (user_record->cn) - g_free(user_record->cn); - - user_record->cn = g_strdup(userid); - } -} - -const char * -nm_user_record_get_userid(NMUserRecord * user_record) -{ - if (user_record == NULL) - return NULL; - - return user_record->cn; -} - -void -nm_user_record_set_display_id(NMUserRecord * user_record, const char *display_id) -{ - if (user_record != NULL && display_id != NULL) { - if (user_record->display_id) - g_free(user_record->display_id); - - user_record->display_id = g_strdup(display_id); - } -} - -const char * -nm_user_record_get_display_id(NMUserRecord * user_record) -{ - if (user_record == NULL) - return NULL; - - if (user_record->display_id == NULL) { - user_record->display_id = nm_typed_to_dotted(user_record->dn); - } - - return user_record->display_id; -} - -const char * -nm_user_record_get_full_name(NMUserRecord * user_record) -{ - if (user_record == NULL) - return NULL; - - if (user_record->full_name == NULL) { - if (user_record->fname && user_record->lname) { - user_record->full_name = g_strdup_printf("%s %s", - user_record->fname, - user_record->lname); - - } - } - - return user_record->full_name; -} - -const char * -nm_user_record_get_first_name(NMUserRecord * user_record) -{ - if (user_record == NULL) - return NULL; - - return user_record->fname; - -} - -const char * -nm_user_record_get_last_name(NMUserRecord * user_record) -{ - if (user_record == NULL) - return NULL; - - return user_record->lname; -} - -gpointer -nm_user_record_get_data(NMUserRecord * user_record) -{ - if (user_record == NULL) - return NULL; - - return user_record->data; -} - -void -nm_user_record_set_data(NMUserRecord * user_record, gpointer data) -{ - if (user_record == NULL) - return; - - user_record->data = data; -} - -void -nm_user_record_set_status(NMUserRecord * user_record, - int status, const char *text) -{ - if (user_record == NULL) - return; - - user_record->status = status; - - if (user_record->status_text) { - g_free(user_record->status_text); - user_record->status_text = NULL; - } - - if (text) - user_record->status_text = g_strdup(text); -} - -gboolean -nm_user_record_get_auth_attr(NMUserRecord *user_record) -{ - if (user_record == NULL) - return FALSE; - - return user_record->auth_attr; -} - -int -nm_user_record_get_property_count(NMUserRecord * user_record) -{ - NMField *locate, *fields; - - int count = 0; - - if (user_record && user_record->fields) { - locate = nm_locate_field(NM_A_FA_INFO_DISPLAY_ARRAY, - (NMField *) user_record->fields); - if (locate && (fields = (NMField *) (locate->ptr_value))) { - count = (int) nm_count_fields(fields); - } - } - return count; -} - -NMProperty * -nm_user_record_get_property(NMUserRecord * user_record, int index) -{ - NMProperty *property = NULL; - NMField *field = NULL, *fields, *locate; - - if (user_record && user_record->fields) { - locate = nm_locate_field(NM_A_FA_INFO_DISPLAY_ARRAY, - (NMField *) user_record->fields); - if (locate && (fields = (NMField *) (locate->ptr_value))) { - int max = nm_count_fields(fields); - - if (index < max) { - if (user_record) { - field = &fields[index]; - if (field && field->tag && field->ptr_value) { - property = g_new0(NMProperty, 1); - property->tag = g_strdup(field->tag); - property->value = _get_attribute_value(field); - } - } - } - } - } - - return property; -} - -void -nm_release_property(NMProperty * property) -{ - if (property) { - if (property->tag) - g_free(property->tag); - - if (property->value) - g_free(property->value); - - g_free(property); - } -} - -const char * -nm_property_get_tag(NMProperty * property) -{ - if (property) - return property->tag; - else - return NULL; -} - -const char * -nm_property_get_value(NMProperty * property) -{ - if (property) - return property->value; - else - return NULL; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/nmuserrecord.h --- a/libpurple/protocols/novell/nmuserrecord.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,273 +0,0 @@ -/* - * nmuserrecord.h - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 __NM_USER_RECORD_H__ -#define __NM_USER_RECORD_H__ - -#include - -typedef struct _NMUserRecord NMUserRecord; -typedef struct _NMProperty NMProperty; - -#include "nmfield.h" -#include "nmuser.h" - -/** - * Creates an NMUserRecord - * - * The NMUserRecord should be released by calling - * nm_release_user_record - * - * @return The new user record - * - */ -NMUserRecord *nm_create_user_record(void); - -/** - * Creates an NMUserRecord - * - * The NMUserRecord should be released by calling - * nm_release_user_record - * - * @param details Should be a NM_A_FA_USER_DETAILS - * - * - * @return The new user record - * - */ -NMUserRecord *nm_create_user_record_from_fields(NMField * details); - -/** - * Add a reference to an existing user_record - * - * The reference should be released by calling - * nm_release_user_record - * - * @param user_record The contact to addref - * - */ -void nm_user_record_add_ref(NMUserRecord * user_record); - -/** - * Release a reference to the user record - * - * @param user_record The user record - * - */ -void nm_release_user_record(NMUserRecord * user_record); - -/** - * Set the status for the user record - * - * @param user_record The user record - * @param status The status for the user - * @param text The status text for the user - * - */ -void nm_user_record_set_status(NMUserRecord * user_record, NMSTATUS_T status, - const char *text); - -/** - * Get the status for the user record - * - * @param user_record The user record - * - * @return The status for the user record - */ -NMSTATUS_T nm_user_record_get_status(NMUserRecord * user_record); - -/** - * Get the status text for the user record - * - * @param user_record The user record - * - * @return The status text if there is any, NULL otherwise - * - */ -const char *nm_user_record_get_status_text(NMUserRecord * user_record); - -/** - * Set the DN for the user record - * - * @param user_record The user record - * @param dn The new DN for the user record - * - */ -void nm_user_record_set_dn(NMUserRecord * user_record, const char *dn); - -/** - * Get the DN for the user record - * - * @param user_record The user record - * - * @return The DN for the user record - */ -const char *nm_user_record_get_dn(NMUserRecord * user_record); - -/** - * Set the user id for the - * - * @param user_record The user record - * @param userid The userid (CN) for the user record - * - */ -void nm_user_record_set_userid(NMUserRecord * user_record, const char *userid); - -/** - * Get the user id for the user record - * - * @param user_record The user record - * - * @return The user id for the user record - */ -const char *nm_user_record_get_userid(NMUserRecord * user_record); - -/** - * Set the display id for the user record - * - * @param user_record The user record - * @param display_id The new display id for the user - * - */ -void nm_user_record_set_display_id(NMUserRecord * user_record, - const char *display_id); - -/** - * Get the display id for the user record - * - * @param user_record The user record - * - * @return The display id for the user record - */ -const char *nm_user_record_get_display_id(NMUserRecord * user_record); - -/** - * Return whether or not the display id is an auth attribute or not. - * - * @param user_record The user record - * - * @return TRUE if display_id is an auth attribute, FALSE otherwise. - */ -gboolean -nm_user_record_get_auth_attr(NMUserRecord *user_record); - -/** - * Get the full name for the user record - * - * @param user_record The user record - * - * @return The full name for the user - */ -const char *nm_user_record_get_full_name(NMUserRecord * user_record); - -/** - * Get the first name for the user record - * - * @param user_record The user record - * - * @return The first name for the user - */ -const char *nm_user_record_get_first_name(NMUserRecord * user_record); - -/** - * Get the last name for the user record - * - * @param user_record The user record - * - * @return The last name for the user - */ -const char *nm_user_record_get_last_name(NMUserRecord * user_record); - -/** - * Set the user defined data for the user record - * - * @param user_record The user record - * @param data The user defined data for the user record - * - */ -void nm_user_record_set_data(NMUserRecord * user_record, gpointer data); - -/** - * Get the user defined data for the user record - * - * @param user_record The user record - * - * @return The user defined data for the user record - */ -gpointer nm_user_record_get_data(NMUserRecord * user_record); - -/** - * Get the property count for the user record - * - * @param user_record The user record - * - * @return The number of information properties for the user record - * - */ -int nm_user_record_get_property_count(NMUserRecord * user_record); - -/** - * Get an info property for the user record. The property must be released - * by calling nm_release_property() - * - * @param user_record The user record - * @param index The index of the property to get (zero based) - * - * @return The property - */ -NMProperty *nm_user_record_get_property(NMUserRecord * user_record, int index); - -/** - * Release a property object - * - * @param property The property - * - */ -void nm_release_property(NMProperty * property); - -/** - * Get the tag for the property - * - * @param property The property - * - * @return The tag of the property (i.e. "Email Address") - */ -const char *nm_property_get_tag(NMProperty * property); - -/** - * Get the value for the property - * - * @param property The property - * - * @return The value of the property (i.e. "nobody@nowhere.com") - */ -const char *nm_property_get_value(NMProperty * property); - -/** - * Copy a user record (deep copy). The dest user record must have already been - * created (nm_create_user_record) - * - * @param dest The destination of the copy - * @param src The source of the copy - * - */ -void nm_user_record_copy(NMUserRecord * dest, NMUserRecord * src); - -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/novell/novell.c --- a/libpurple/protocols/novell/novell.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3589 +0,0 @@ -/* - * novell.c - * - * Copyright (c) 2004 Novell, Inc. All Rights Reserved. - * - * 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; version 2 of the License. - * - * 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 "internal.h" -#include "accountopt.h" -#include "debug.h" -#include "prpl.h" -#include "server.h" -#include "nmuser.h" -#include "notify.h" -#include "util.h" -#include "sslconn.h" -#include "request.h" -#include "network.h" -#include "privacy.h" -#include "status.h" -#include "version.h" - -#define DEFAULT_PORT 8300 -#define NOVELL_CONNECT_STEPS 4 -#define NM_ROOT_FOLDER_NAME "GroupWise Messenger" - -#define NOVELL_STATUS_TYPE_AVAILABLE "available" -#define NOVELL_STATUS_TYPE_AWAY "away" -#define NOVELL_STATUS_TYPE_BUSY "busy" -#define NOVELL_STATUS_TYPE_OFFLINE "offline" -#define NOVELL_STATUS_TYPE_IDLE "idle" -#define NOVELL_STATUS_TYPE_APPEAR_OFFLINE "appearoffline" - -static PurplePlugin *my_protocol = NULL; - -static gboolean -_is_disconnect_error(NMERR_T err); - -static gboolean -_check_for_disconnect(NMUser * user, NMERR_T err); - -static void -_send_message(NMUser * user, NMMessage * message); - -static void -_update_buddy_status(NMUser *user, PurpleBuddy * buddy, int status, int gmt); - -static void -_remove_purple_buddies(NMUser * user); - -static void -_add_contacts_to_purple_blist(NMUser * user, NMFolder * folder); - -static void -_add_purple_buddies(NMUser * user); - -static void -_sync_contact_list(NMUser *user); - -static void -_sync_privacy_lists(NMUser *user); - -static void -_show_info(PurpleConnection * gc, NMUserRecord * user_record, char * name); - -const char * -_get_conference_name(int id); - -/******************************************************************************* - * Response callbacks - *******************************************************************************/ - -/* Handle login response */ -static void -_login_resp_cb(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - PurpleConnection *gc; - const char *alias; - NMERR_T rc; - - if (user == NULL) - return; - - gc = purple_account_get_connection(user->client_data); - if (gc == NULL) - return; - - if (ret_code == NM_OK) { - - /* Set alias for user if not set (use Full Name) */ - alias = purple_account_get_alias(user->client_data); - if (alias == NULL || *alias == '\0') { - alias = nm_user_record_get_full_name(user->user_record); - - if (alias) - purple_account_set_alias(user->client_data, alias); - } - - /* Tell Purple that we are connected */ - purple_connection_set_state(gc, PURPLE_CONNECTED); - - _sync_contact_list(user); - - rc = nm_send_set_status(user, NM_STATUS_AVAILABLE, NULL, NULL, NULL, - NULL); - _check_for_disconnect(user, rc); - - } else { - PurpleConnectionError reason; - char *err = g_strdup_printf(_("Unable to login: %s"), - nm_error_to_string (ret_code)); - - switch (ret_code) { - case NMERR_AUTHENTICATION_FAILED: - case NMERR_CREDENTIALS_MISSING: - case NMERR_PASSWORD_INVALID: - /* Don't attempt to auto-reconnect if our - * password was invalid. - */ - if (!purple_account_get_remember_password(gc->account)) - purple_account_set_password(gc->account, NULL); - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - break; - default: - /* FIXME: There are other reasons login could fail */ - reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - } - - purple_connection_error_reason(gc, reason, err); - g_free(err); - } -} - -/* Handle getstatus response*/ -static void -_get_status_resp_cb(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - PurpleBuddy *buddy; - GSList *buddies; - GSList *bnode; - NMUserRecord *user_record = (NMUserRecord *) resp_data; - int status; - - if (user == NULL || user_record == NULL) - return; - - if (ret_code == NM_OK) { - - /* Find all Purple buddies and update their statuses */ - const char *name = nm_user_record_get_display_id(user_record); - - if (name) { - buddies = purple_find_buddies((PurpleAccount *) user->client_data, name); - for (bnode = buddies; bnode; bnode = bnode->next) { - buddy = (PurpleBuddy *) bnode->data; - if (buddy) { - status = nm_user_record_get_status(user_record); - _update_buddy_status(user, buddy, status, time(0)); - } - } - g_slist_free(buddies); - } - - } else { - - purple_debug(PURPLE_DEBUG_INFO, "novell", - "_get_status_resp_cb(): rc = 0x%X\n", ret_code); - - } -} - -/* Show an error if the rename failed */ -static void -_rename_contact_resp_cb(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - if (ret_code != NM_OK) { - purple_debug(PURPLE_DEBUG_INFO, "novell", - "_rename_contact_resp_cb(): rc = 0x%X\n", ret_code); - } -} - -/* Handle the getdetails response and send the message */ -static void -_get_details_resp_send_msg(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - PurpleConversation *gconv; - PurpleConnection *gc; - NMUserRecord *user_record = NULL; - NMContact *cntct = NULL; - NMConference *conf; - NMMessage *msg = user_data; - const char *dn = NULL; - const char *name; - - if (user == NULL || msg == NULL) - return; - - if (ret_code == NM_OK) { - user_record = (NMUserRecord *) resp_data; - if (user_record) { - - /* Set the title for the conversation */ - /* XXX - Should this be PURPLE_CONV_TYPE_IM? */ - gconv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, - nm_user_record_get_display_id(user_record), - (PurpleAccount *) user->client_data); - if (gconv) { - - dn = nm_user_record_get_dn(user_record); - if (dn) { - cntct = nm_find_contact(user, dn); - } - - if (cntct) { - purple_conversation_set_title(gconv, - nm_contact_get_display_name(cntct)); - } else { - - /* Not in the contact list, try to user full name */ - name = (char *) nm_user_record_get_full_name(user_record); - if (name) - purple_conversation_set_title(gconv, name); - } - } - - /* Add the user record to particpant list */ - conf = nm_message_get_conference(msg); - if (conf) { - nm_conference_add_participant(conf, user_record); - _send_message(user, msg); - } - } - - } else { - - gc = purple_account_get_connection(user->client_data); - if (gc != NULL) { - char *err = g_strdup_printf(_("Unable to send message." - " Could not get details for user (%s)."), - nm_error_to_string (ret_code)); - - purple_notify_error(gc, NULL, err, NULL); - g_free(err); - } - - if (msg) - nm_release_message(msg); - } -} - -/* Set up the new PurpleBuddy based on the response from getdetails */ -static void -_get_details_resp_setup_buddy(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - NMUserRecord *user_record; - NMContact *contact; - PurpleBuddy *buddy; - const char *alias; - NMERR_T rc = NM_OK; - - if (user == NULL || resp_data == NULL || user_data == NULL) - return; - - contact = user_data; - - if (ret_code == NM_OK) { - user_record = resp_data; - - buddy = nm_contact_get_data(contact); - - nm_contact_set_user_record(contact, user_record); - - /* Set the display id */ - purple_blist_rename_buddy(buddy, - nm_user_record_get_display_id(user_record)); - - alias = purple_buddy_get_alias(buddy); - if (alias == NULL || *alias == '\0' || purple_strequal(alias, purple_buddy_get_name(buddy))) { - purple_blist_alias_buddy(buddy, - nm_user_record_get_full_name(user_record)); - - /* Tell the server about the new display name */ - rc = nm_send_rename_contact(user, contact, - nm_user_record_get_full_name(user_record), - NULL, NULL); - _check_for_disconnect(user, rc); - - } - - - /* Get initial status for the buddy */ - rc = nm_send_get_status(user, resp_data, _get_status_resp_cb, NULL); - _check_for_disconnect(user, rc); - -/* nm_release_contact(contact);*/ - - } - - if (contact) - nm_release_contact(contact); -} - -/* Add the new contact into the PurpleBuddy list */ -static void -_create_contact_resp_cb(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - NMContact *tmp_contact = (NMContact *) user_data; - NMContact *new_contact = NULL; - NMFolder *folder = NULL; - PurpleGroup *group; - PurpleBuddy *buddy; - const char *folder_name = NULL; - NMERR_T rc = NM_OK; - - if (user == NULL) - return; - - if (ret_code == NM_OK) { - - new_contact = (NMContact *) resp_data; - if (new_contact == NULL || tmp_contact == NULL) - return; - - /* Get the userid and folder name for the new contact */ - folder = nm_find_folder_by_id(user, - nm_contact_get_parent_id(new_contact)); - if (folder) { - folder_name = nm_folder_get_name(folder); - } - - if (folder_name == NULL || *folder_name == '\0') - folder_name = NM_ROOT_FOLDER_NAME; - - /* Re-add the buddy now that we got the okay from the server */ - if (folder_name && (group = purple_find_group(folder_name))) { - - const char *alias = nm_contact_get_display_name(tmp_contact); - const char *display_id = nm_contact_get_display_id(new_contact); - - if (display_id == NULL) - display_id = nm_contact_get_dn(new_contact); - - if (alias && !purple_strequal(alias, display_id)) { - - /* The user requested an alias, tell the server about it. */ - rc = nm_send_rename_contact(user, new_contact, alias, - _rename_contact_resp_cb, NULL); - _check_for_disconnect(user, rc); - - } else { - - alias = ""; - - } - - /* Add it to the purple buddy list if it is not there */ - buddy = purple_find_buddy_in_group(user->client_data, display_id, group); - if (buddy == NULL) { - buddy = purple_buddy_new(user->client_data, display_id, alias); - purple_blist_add_buddy(buddy, NULL, group, NULL); - } - - /* Save the new buddy as part of the contact object */ - nm_contact_set_data(new_contact, (gpointer) buddy); - - /* We need details for the user before we can setup the - * new Purple buddy. We always call this because the - * 'createcontact' response fields do not always contain - * everything that we need. - */ - nm_contact_add_ref(new_contact); - - rc = nm_send_get_details(user, nm_contact_get_dn(new_contact), - _get_details_resp_setup_buddy, new_contact); - _check_for_disconnect(user, rc); - - } - - } else { - PurpleConnection *gc = purple_account_get_connection(user->client_data); - const char *name = nm_contact_get_dn(tmp_contact); - char *err; - - err = - g_strdup_printf(_("Unable to add %s to your buddy list (%s)."), - name, nm_error_to_string (ret_code)); - purple_notify_error(gc, NULL, err, NULL); - g_free(err); - - } - - if (tmp_contact) - nm_release_contact(tmp_contact); -} - -/* Show an error if we failed to send the message */ -static void -_send_message_resp_cb(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - PurpleConnection *gc; - char *err = NULL; - - if (user == NULL) - return; - - if (ret_code != NM_OK) { - gc = purple_account_get_connection(user->client_data); - - /* TODO: Improve this! message to who or for what conference? */ - err = g_strdup_printf(_("Unable to send message (%s)."), - nm_error_to_string (ret_code)); - purple_notify_error(gc, NULL, err, NULL); - g_free(err); - } -} - -/* Show an error if the remove failed */ -static void -_remove_contact_resp_cb(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - if (ret_code != NM_OK) { - /* TODO: Display an error? */ - - purple_debug(PURPLE_DEBUG_INFO, "novell", - "_remove_contact_resp_cb(): rc = 0x%x\n", ret_code); - } -} - -/* Show an error if the remove failed */ -static void -_remove_folder_resp_cb(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - if (ret_code != NM_OK) { - /* TODO: Display an error? */ - - purple_debug(PURPLE_DEBUG_INFO, "novell", - "_remove_folder_resp_cb(): rc = 0x%x\n", ret_code); - } -} - -/* Show an error if the move failed */ -static void -_move_contact_resp_cb(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - if (ret_code != NM_OK) { - /* TODO: Display an error? */ - - purple_debug(PURPLE_DEBUG_INFO, "novell", - "_move_contact_resp_cb(): rc = 0x%x\n", ret_code); - } -} - -/* Show an error if the rename failed */ -static void -_rename_folder_resp_cb(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - if (ret_code != NM_OK) { - /* TODO: Display an error? */ - - purple_debug(PURPLE_DEBUG_INFO, "novell", - "_rename_folder_resp_cb(): rc = 0x%x\n", ret_code); - } -} - -static void -_sendinvite_resp_cb(NMUser *user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - char *err; - PurpleConnection *gc; - - if (user == NULL) - return; - - if (ret_code != NM_OK) { - gc = purple_account_get_connection(user->client_data); - err = g_strdup_printf(_("Unable to invite user (%s)."), nm_error_to_string(ret_code)); - purple_notify_error(gc, NULL, err, NULL); - g_free(err); - - purple_debug(PURPLE_DEBUG_INFO, "novell", - "_sendinvite_resp_cb(): rc = 0x%x\n", ret_code); - } - -} - -/* If the createconf was successful attempt to send the message, - * otherwise display an error message to the user. - */ -static void -_createconf_resp_send_msg(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - NMConference *conf; - NMMessage *msg = user_data; - - if (user == NULL || msg == NULL) - return; - - if (ret_code == NM_OK) { - _send_message(user, msg); - } else { - - if ((conf = nm_message_get_conference(msg))) { - - PurpleConnection *gc = purple_account_get_connection(user->client_data); - const char *name = NULL; - char *err; - NMUserRecord *ur; - - ur = nm_conference_get_participant(conf, 0); - if (ur) - name = nm_user_record_get_userid(ur); - - if (name) - err = g_strdup_printf(_("Unable to send message to %s." - " Could not create the conference (%s)."), - name, - nm_error_to_string (ret_code)); - else - err = g_strdup_printf(_("Unable to send message." - " Could not create the conference (%s)."), - nm_error_to_string (ret_code)); - - purple_notify_error(gc, NULL, err, NULL); - g_free(err); - } - - if (msg) - nm_release_message(msg); - } -} - -/* Move contact to newly created folder */ -static void -_create_folder_resp_move_contact(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - NMContact *contact = user_data; - NMFolder *new_folder; - char *folder_name = resp_data; - NMERR_T rc = NM_OK; - - if (user == NULL || folder_name == NULL || contact == NULL) { - - if (folder_name) - g_free(folder_name); - - return; - } - - if (ret_code == NM_OK || ret_code == NMERR_DUPLICATE_FOLDER) { - new_folder = nm_find_folder(user, folder_name); - if (new_folder) { - - /* Tell the server to move the contact to the new folder */ -/* rc = nm_send_move_contact(user, contact, new_folder, - _move_contact_resp_cb, NULL); */ - - rc = nm_send_create_contact(user, new_folder, contact, - NULL, NULL); - - _check_for_disconnect(user, rc); - - } - } else { - PurpleConnection *gc = purple_account_get_connection(user->client_data); - char *err = g_strdup_printf(_("Unable to move user %s" - " to folder %s in the server side list." - " Error while creating folder (%s)."), - nm_contact_get_dn(contact), - folder_name, - nm_error_to_string (ret_code)); - - purple_notify_error(gc, NULL, err, NULL); - g_free(err); - } - - if (folder_name) - g_free(folder_name); -} - -/* Add contact to newly create folder */ -static void -_create_folder_resp_add_contact(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - NMContact *contact = (NMContact *) user_data; - NMFolder *folder; - char *folder_name = (char *) resp_data; - NMERR_T rc = NM_OK; - - if (user == NULL || folder_name == NULL || contact == NULL) { - - if (contact) - nm_release_contact(contact); - - if (folder_name) - g_free(folder_name); - - return; - } - - if (ret_code == NM_OK || ret_code == NMERR_DUPLICATE_FOLDER) { - folder = nm_find_folder(user, folder_name); - if (folder) { - - rc = nm_send_create_contact(user, folder, contact, - _create_contact_resp_cb, contact); - _check_for_disconnect(user, rc); - } - } else { - PurpleConnection *gc = purple_account_get_connection(user->client_data); - const char *name = nm_contact_get_dn(contact); - char *err = - g_strdup_printf(_("Unable to add %s to your buddy list." - " Error creating folder in server side list (%s)."), - name, nm_error_to_string (ret_code)); - - purple_notify_error(gc, NULL, err, NULL); - - nm_release_contact(contact); - g_free(err); - } - - g_free(folder_name); -} - -static void -_join_conf_resp_cb(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - PurpleConversation *chat; - PurpleConnection *gc; - NMUserRecord *ur; - NMConference *conference = user_data; - const char *name, *conf_name; - int i, count; - - if (user == NULL || conference == NULL) - return; - - gc = purple_account_get_connection(user->client_data); - - if (ret_code == NM_OK) { - conf_name = _get_conference_name(++user->conference_count); - chat = serv_got_joined_chat(gc, user->conference_count, conf_name); - if (chat) { - - nm_conference_set_data(conference, (gpointer) chat); - - count = nm_conference_get_participant_count(conference); - for (i = 0; i < count; i++) { - ur = nm_conference_get_participant(conference, i); - if (ur) { - name = nm_user_record_get_display_id(ur); - purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat), name, NULL, - PURPLE_CBFLAGS_NONE, TRUE); - } - } - } - } -} - -/* Show info returned by getdetails */ -static void -_get_details_resp_show_info(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - PurpleConnection *gc; - NMUserRecord *user_record; - char *name; - char *err; - - if (user == NULL) - return; - - name = user_data; - - if (ret_code == NM_OK) { - user_record = (NMUserRecord *) resp_data; - if (user_record) { - _show_info(purple_account_get_connection(user->client_data), - user_record, g_strdup(name)); - } - } else { - gc = purple_account_get_connection(user->client_data); - err = - g_strdup_printf(_("Could not get details for user %s (%s)."), - name, nm_error_to_string (ret_code)); - purple_notify_error(gc, NULL, err, NULL); - g_free(err); - } - - if (name) - g_free(name); -} - -/* Handle get details response add to privacy list */ -static void -_get_details_resp_add_privacy_item(NMUser *user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - PurpleConnection *gc; - NMUserRecord *user_record = resp_data; - char *err; - gboolean allowed = GPOINTER_TO_INT(user_data); - const char *display_id; - - if (user == NULL) - return; - - gc = purple_account_get_connection(user->client_data); - display_id = nm_user_record_get_display_id(user_record); - - if (ret_code == NM_OK) { - - if (allowed) { - - if (!g_slist_find_custom(gc->account->permit, - display_id, (GCompareFunc)purple_utf8_strcasecmp)) { - purple_privacy_permit_add(gc->account, display_id, TRUE); - } - - } else { - - if (!g_slist_find_custom(gc->account->permit, - display_id, (GCompareFunc)purple_utf8_strcasecmp)) { - purple_privacy_deny_add(gc->account, display_id, TRUE); - } - } - - } else { - - err = g_strdup_printf(_("Unable to add user to privacy list (%s)."), - nm_error_to_string(ret_code)); - purple_notify_error(gc, NULL, err, NULL); - g_free(err); - - } -} - -/* Handle response to create privacy item request */ -static void -_create_privacy_item_deny_resp_cb(NMUser *user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - PurpleConnection *gc; - NMUserRecord *user_record; - char *who = user_data; - char *err; - NMERR_T rc = NM_OK; - const char *display_id = NULL; - - if (user == NULL) - return; - - gc = purple_account_get_connection(user->client_data); - - if (ret_code == NM_OK) { - - user_record = nm_find_user_record(user, who); - if (user_record) - display_id = nm_user_record_get_display_id(user_record); - - if (display_id) { - - if (!g_slist_find_custom(gc->account->deny, - display_id, (GCompareFunc)purple_utf8_strcasecmp)) { - - purple_privacy_deny_add(gc->account, display_id, TRUE); - } - - } else { - rc = nm_send_get_details(user, who, - _get_details_resp_add_privacy_item, - (gpointer)FALSE); - _check_for_disconnect(user, rc); - } - } else { - - err = g_strdup_printf(_("Unable to add %s to deny list (%s)."), - who, nm_error_to_string(ret_code)); - purple_notify_error(gc, NULL, err, NULL); - g_free(err); - - } - - if (who) - g_free(who); - -} - -/* Handle response to create privacy item request */ -static void -_create_privacy_item_permit_resp_cb(NMUser *user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - PurpleConnection *gc; - NMUserRecord *user_record; - char *who = user_data; - char *err; - NMERR_T rc = NM_OK; - const char *display_id = NULL; - - if (user == NULL) - return; - - gc = purple_account_get_connection(user->client_data); - - if (ret_code == NM_OK) { - - user_record = nm_find_user_record(user, who); - if (user_record) - display_id = nm_user_record_get_display_id(user_record); - - if (display_id) { - - if (!g_slist_find_custom(gc->account->permit, - display_id, - (GCompareFunc)purple_utf8_strcasecmp)) { - - purple_privacy_permit_add(gc->account, display_id, TRUE); - } - - } else { - rc = nm_send_get_details(user, who, - _get_details_resp_add_privacy_item, - (gpointer)TRUE); - _check_for_disconnect(user, rc); - } - - } else { - - err = g_strdup_printf(_("Unable to add %s to permit list (%s)."), who, - nm_error_to_string(ret_code)); - purple_notify_error(gc, NULL, err, NULL); - g_free(err); - - } - - if (who) - g_free(who); -} - -static void -_get_details_send_privacy_create(NMUser *user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - NMERR_T rc = NM_OK; - PurpleConnection *gc; - NMUserRecord *user_record = resp_data; - char *err; - gboolean allowed = GPOINTER_TO_INT(user_data); - const char *dn, *display_id; - - if (user == NULL) - return; - - gc = purple_account_get_connection(user->client_data); - dn = nm_user_record_get_dn(user_record); - display_id = nm_user_record_get_display_id(user_record); - - if (ret_code == NM_OK) { - - if (allowed) { - rc = nm_send_create_privacy_item(user, dn, TRUE, - _create_privacy_item_permit_resp_cb, - g_strdup(display_id)); - _check_for_disconnect(user, rc); - - } else { - rc = nm_send_create_privacy_item(user, dn, FALSE, - _create_privacy_item_deny_resp_cb, - g_strdup(display_id)); - _check_for_disconnect(user, rc); - } - - } else { - - err = g_strdup_printf(_("Unable to add user to privacy list (%s)."), - nm_error_to_string(ret_code)); - purple_notify_error(gc, NULL, err, NULL); - g_free(err); - - } -} - -static void -_remove_privacy_item_resp_cb(NMUser *user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - PurpleConnection *gc; - char *who = user_data; - char *err; - - if (user == NULL) - return; - - if (ret_code != NM_OK) { - - gc = purple_account_get_connection(user->client_data); - err = g_strdup_printf(_("Unable to remove %s from privacy list (%s)."), who, - nm_error_to_string(ret_code)); - purple_notify_error(gc, NULL, err, NULL); - g_free(err); - } - - if (who) - g_free(who); -} - -static void -_set_privacy_default_resp_cb(NMUser *user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - PurpleConnection *gc; - char *err; - - if (user == NULL) - return; - - if (ret_code != NM_OK) { - - gc = purple_account_get_connection(user->client_data); - err = g_strdup_printf(_("Unable to change server side privacy settings (%s)."), - nm_error_to_string(ret_code)); - purple_notify_error(gc, NULL, err, NULL); - g_free(err); - - } -} - -/* Handle get details response add to privacy list */ -static void -_get_details_resp_send_invite(NMUser *user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - NMERR_T rc = NM_OK; - PurpleConnection *gc; - NMUserRecord *user_record = resp_data; - char *err; - GSList *cnode; - NMConference *conference; - gpointer chat; - long id = (long) user_data; - - if (user == NULL) - return; - - gc = purple_account_get_connection(user->client_data); - - if (ret_code == NM_OK) { - - for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) { - conference = cnode->data; - if (conference && (chat = nm_conference_get_data(conference))) { - if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(chat)) == id) { - rc = nm_send_conference_invite(user, conference, user_record, - NULL, _sendinvite_resp_cb, NULL); - _check_for_disconnect(user, rc); - break; - } - } - } - - } else { - - err = g_strdup_printf(_("Unable to invite user (%s)."), nm_error_to_string(ret_code)); - purple_notify_error(gc, NULL, err, NULL); - g_free(err); - - } -} - -static void -_createconf_resp_send_invite(NMUser * user, NMERR_T ret_code, - gpointer resp_data, gpointer user_data) -{ - NMERR_T rc = NM_OK; - NMConference *conference = resp_data; - NMUserRecord *user_record = user_data; - PurpleConnection *gc; - char *err; - - if (user == NULL) - return; - - - - if (ret_code == NM_OK) { - rc = nm_send_conference_invite(user, conference, user_record, - NULL, _sendinvite_resp_cb, NULL); - _check_for_disconnect(user, rc); - } else { - err = g_strdup_printf(_("Unable to create conference (%s)."), nm_error_to_string(ret_code)); - gc = purple_account_get_connection(user->client_data); - purple_notify_error(gc, NULL, err, NULL); - g_free(err); - } -} - -/******************************************************************************* - * Helper functions - ******************************************************************************/ - -static char * -_user_agent_string(void) -{ - -#if !defined(_WIN32) - - const char *sysname = ""; - const char *release = ""; - struct utsname u; - - if (uname(&u) == 0) { - sysname = u.sysname; - release = u.release; - } else { - sysname = "Linux"; - release = "Unknown"; - } - - return g_strdup_printf("Purple/%s (%s; %s)", VERSION, sysname, release); - -#else - - const char *sysname = ""; - OSVERSIONINFO os_info; - SYSTEM_INFO sys_info; - - os_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - GetVersionEx(&os_info); - GetSystemInfo(&sys_info); - - if (os_info.dwPlatformId == VER_PLATFORM_WIN32_NT) { - switch (os_info.dwMajorVersion) { - case 3: - case 4: - sysname = "Windows NT"; - break; - case 5: - switch (os_info.dwMinorVersion) { - case 0: - sysname = "Windows 2000"; - break; - case 1: - sysname = "Windows XP"; - break; - case 2: - sysname = "Windows Server 2003"; - break; - default: - sysname = "Windows"; - break; - } - break; - default: - sysname = "Windows"; - break; - } - - } else if (os_info.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) { - switch (os_info.dwMinorVersion) { - case 0: - sysname = "Windows 95"; - break; - case 10: - sysname = "Windows 98"; - break; - case 90: - sysname = "Windows ME"; - break; - default: - sysname = "Windows"; - break; - } - } else { - sysname = "Windows"; - } - - return g_strdup_printf("Purple/%s (%s; %ld.%ld)", VERSION, sysname, - os_info.dwMajorVersion, os_info.dwMinorVersion); - -#endif - - -} - -static gboolean -_is_disconnect_error(NMERR_T err) -{ - return (err == NMERR_TCP_WRITE || - err == NMERR_TCP_READ || err == NMERR_PROTOCOL); -} - -static gboolean -_check_for_disconnect(NMUser * user, NMERR_T err) -{ - PurpleConnection *gc = purple_account_get_connection(user->client_data); - - if (_is_disconnect_error(err)) { - - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Error communicating with server. Closing connection.")); - return TRUE; - - } - - return FALSE; -} - -/* Check to see if the conference is instantiated, if so send the message. - * If not send the create conference -- the response handler for the createconf - * will call this function again. - */ -static void -_send_message(NMUser * user, NMMessage * message) -{ - NMConference *conf; - NMERR_T rc = NM_OK; - - conf = nm_message_get_conference(message); - if (conf) { - /* We have a conference make sure that the - server knows about it already. */ - if (nm_conference_is_instantiated(conf)) { - - /* We have everything that we need...finally! */ - rc = nm_send_message(user, message, _send_message_resp_cb); - _check_for_disconnect(user, rc); - - nm_release_message(message); - - } else { - rc = nm_send_create_conference(user, conf, _createconf_resp_send_msg, message); - _check_for_disconnect(user, rc); - } - } -} - -/* - * Update the status of the given buddy in the Purple buddy list - */ -static void -_update_buddy_status(NMUser *user, PurpleBuddy * buddy, int novellstatus, int gmt) -{ - PurpleAccount *account; - const char *status_id; - const char *text = NULL; - const char *dn; - const char *name; - int idle = 0; - - account = purple_buddy_get_account(buddy); - name = purple_buddy_get_name(buddy); - - switch (novellstatus) { - case NM_STATUS_AVAILABLE: - status_id = NOVELL_STATUS_TYPE_AVAILABLE; - break; - case NM_STATUS_AWAY: - status_id = NOVELL_STATUS_TYPE_AWAY; - break; - case NM_STATUS_BUSY: - status_id = NOVELL_STATUS_TYPE_BUSY; - break; - case NM_STATUS_OFFLINE: - status_id = NOVELL_STATUS_TYPE_OFFLINE; - break; - case NM_STATUS_AWAY_IDLE: - status_id = NOVELL_STATUS_TYPE_AWAY; - idle = gmt; - break; - default: - status_id = NOVELL_STATUS_TYPE_OFFLINE; - break; - } - - /* Get status text for the user */ - dn = nm_lookup_dn(user, name); - if (dn) { - NMUserRecord *user_record = nm_find_user_record(user, dn); - if (user_record) { - text = nm_user_record_get_status_text(user_record); - } - } - - purple_prpl_got_user_status(account, name, status_id, - "message", text, NULL); - purple_prpl_got_user_idle(account, name, - (novellstatus == NM_STATUS_AWAY_IDLE), idle); -} - -/* Iterate through the cached Purple buddy list and remove buddies - * that are not in the server side list. - */ -static void -_remove_purple_buddies(NMUser *user) -{ - PurpleBlistNode *gnode; - PurpleBlistNode *cnode; - PurpleBlistNode *bnode; - PurpleGroup *group; - PurpleBuddy *buddy; - GSList *rem_list = NULL; - GSList *l; - NMFolder *folder = NULL; - const char *gname = NULL; - - for (gnode = purple_blist_get_root(); gnode; - gnode = purple_blist_node_get_sibling_next(gnode)) { - if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) - continue; - group = (PurpleGroup *) gnode; - gname = purple_group_get_name(group); - for (cnode = purple_blist_node_get_first_child(gnode); - cnode; - cnode = purple_blist_node_get_sibling_next(cnode)) { - if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) - continue; - for (bnode = purple_blist_node_get_first_child(cnode); - bnode; - bnode = purple_blist_node_get_sibling_next(bnode)) { - if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) - continue; - buddy = (PurpleBuddy *) bnode; - if (purple_buddy_get_account(buddy) == user->client_data) { - if (purple_strequal(gname, NM_ROOT_FOLDER_NAME)) - gname = ""; - folder = nm_find_folder(user, gname); - if (folder == NULL || - !nm_folder_find_contact_by_display_id(folder, purple_buddy_get_name(buddy))) { - rem_list = g_slist_append(rem_list, buddy); - } - } - } - } - } - - if (rem_list) { - for (l = rem_list; l; l = l->next) { - purple_blist_remove_buddy(l->data); - } - g_slist_free(rem_list); - } -} - -/* Add all of the contacts in the given folder to the Purple buddy list */ -static void -_add_contacts_to_purple_blist(NMUser * user, NMFolder * folder) -{ - NMUserRecord *user_record = NULL; - NMContact *contact = NULL; - PurpleBuddy *buddy = NULL; - PurpleGroup *group; - NMERR_T cnt = 0, i; - const char *name = NULL; - const char *fname = NULL; - int status = 0; - - /* If this is the root folder give it a name. Purple does not have the concept of - * a root folder. - */ - fname = nm_folder_get_name(folder); - if (fname == NULL || *fname == '\0') { - fname = NM_ROOT_FOLDER_NAME; - } - - /* Does the Purple group exist already? */ - group = purple_find_group(fname); - if (group == NULL) { - group = purple_group_new(fname); - purple_blist_add_group(group, NULL); - } - - /* Get each contact for this folder */ - cnt = nm_folder_get_contact_count(folder); - for (i = 0; i < cnt; i++) { - contact = nm_folder_get_contact(folder, i); - if (contact) { - - name = nm_contact_get_display_id(contact); - if (name) { - - buddy = purple_find_buddy_in_group(user->client_data, name, group); - if (buddy == NULL) { - /* Add it to the purple buddy list */ - buddy = purple_buddy_new(user->client_data, - name, - nm_contact_get_display_name(contact)); - - purple_blist_add_buddy(buddy, NULL, group, NULL); - } - - /* Set the initial status for the buddy */ - user_record = nm_contact_get_user_record(contact); - if (user_record) { - status = nm_user_record_get_status(user_record); - } - _update_buddy_status(user, buddy, status, time(0)); - - /* Save the new buddy as part of the contact object */ - nm_contact_set_data(contact, (gpointer) buddy); - } - - } else { - /* NULL contact. This should not happen, but - * let's break out of the loop. - */ - break; - } - } -} - -/* Add all of the server side contacts to the Purple buddy list. */ -static void -_add_purple_buddies(NMUser * user) -{ - int cnt = 0, i; - NMFolder *root_folder = NULL; - NMFolder *folder = NULL; - - root_folder = nm_get_root_folder(user); - if (root_folder) { - - /* Add sub-folders and contacts to sub-folders... - * iterate throught the sub-folders in reverse order - * because Purple adds the folders to the front -- so we - * want to add the first folder last - */ - cnt = nm_folder_get_subfolder_count(root_folder); - for (i = cnt-1; i >= 0; i--) { - folder = nm_folder_get_subfolder(root_folder, i); - if (folder) { - _add_contacts_to_purple_blist(user, folder); - } - } - - /* Add contacts for the root folder */ - _add_contacts_to_purple_blist(user, root_folder); - } -} - -static void -_sync_contact_list(NMUser *user) -{ - /* Remove all buddies from the local list that are - * not in the server side list and add all buddies - * from the server side list that are not in - * the local list - */ - _remove_purple_buddies(user); - _add_purple_buddies(user); - user->clist_synched = TRUE; -} - -static void -_sync_privacy_lists(NMUser *user) -{ - GSList *node = NULL, *rem_list = NULL; - PurpleConnection *gc; - const char *name, *dn; - NMUserRecord *user_record; - - if (user == NULL) - return; - - gc = purple_account_get_connection(user->client_data); - if (gc == NULL) - return; - - /* Set the Purple privacy setting */ - if (user->default_deny) { - if (user->allow_list == NULL) { - gc->account->perm_deny = PURPLE_PRIVACY_DENY_ALL; - } else { - gc->account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS; - } - } else { - if (user->deny_list == NULL) { - gc->account->perm_deny = PURPLE_PRIVACY_ALLOW_ALL; - } else { - gc->account->perm_deny = PURPLE_PRIVACY_DENY_USERS; - } - } - - /* Add stuff */ - for (node = user->allow_list; node; node = node->next) { - user_record = nm_find_user_record(user, (char *)node->data); - if (user_record) - name = nm_user_record_get_display_id(user_record); - else - name =(char *)node->data; - - if (!g_slist_find_custom(gc->account->permit, - name, (GCompareFunc)purple_utf8_strcasecmp)) { - purple_privacy_permit_add(gc->account, name , TRUE); - } - } - - for (node = user->deny_list; node; node = node->next) { - user_record = nm_find_user_record(user, (char *)node->data); - if (user_record) - name = nm_user_record_get_display_id(user_record); - else - name =(char *)node->data; - - if (!g_slist_find_custom(gc->account->deny, - name, (GCompareFunc)purple_utf8_strcasecmp)) { - purple_privacy_deny_add(gc->account, name, TRUE); - } - } - - - /* Remove stuff */ - for (node = gc->account->permit; node; node = node->next) { - dn = nm_lookup_dn(user, (char *)node->data); - if (dn != NULL && - !g_slist_find_custom(user->allow_list, - dn, (GCompareFunc)purple_utf8_strcasecmp)) { - rem_list = g_slist_append(rem_list, node->data); - } - } - - if (rem_list) { - for (node = rem_list; node; node = node->next) { - purple_privacy_permit_remove(gc->account, (char *)node->data, TRUE); - } - g_slist_free(rem_list); - rem_list = NULL; - } - - for (node = gc->account->deny; node; node = node->next) { - dn = nm_lookup_dn(user, (char *)node->data); - if (dn != NULL && - !g_slist_find_custom(user->deny_list, - dn, (GCompareFunc)purple_utf8_strcasecmp)) { - rem_list = g_slist_append(rem_list, node->data); - } - } - - if (rem_list) { - for (node = rem_list; node; node = node->next) { - purple_privacy_deny_remove(gc->account, (char *)node->data, TRUE); - } - g_slist_free(rem_list); - } -} - - /* Map known property tags to user-friendly strings */ -static const char * -_map_property_tag(const char *tag) -{ - if (tag == NULL) return NULL; - - if (purple_strequal(tag, "telephoneNumber")) - return _("Telephone Number"); - else if (purple_strequal(tag, "L")) - return _("Location"); - else if (purple_strequal(tag, "OU")) - return _("Department"); - else if (purple_strequal(tag, "personalTitle")) - return _("Personal Title"); - else if (purple_strequal(tag, "Title")) - return _("Job Title"); - else if (purple_strequal(tag, "mailstop")) - return _("Mailstop"); - else if (purple_strequal(tag, "Internet EMail Address")) - return _("Email Address"); - else - return tag; -} - -/* Display a dialog box showing the properties for the given user record */ -static void -_show_info(PurpleConnection * gc, NMUserRecord * user_record, char * name) -{ - PurpleNotifyUserInfo *user_info = purple_notify_user_info_new(); - int count, i; - NMProperty *property; - const char *tag, *value; - - tag = _("User ID"); - value = nm_user_record_get_userid(user_record); - if (value) { - purple_notify_user_info_add_pair(user_info, tag, value); - } - -/* tag = _("DN"); - value = nm_user_record_get_dn(user_record); - if (value) { - purple_notify_user_info_add_pair(user_info, tag, value); - } -*/ - - tag = _("Full name"); - value = nm_user_record_get_full_name(user_record); - if (value) { - purple_notify_user_info_add_pair(user_info, tag, value); - } - - count = nm_user_record_get_property_count(user_record); - for (i = 0; i < count; i++) { - property = nm_user_record_get_property(user_record, i); - if (property) { - tag = _map_property_tag(nm_property_get_tag(property)); - value = nm_property_get_value(property); - if (tag && value) { - purple_notify_user_info_add_pair(user_info, tag, value); - } - nm_release_property(property); - } - } - - purple_notify_userinfo(gc, name, user_info, NULL, NULL); - purple_notify_user_info_destroy(user_info); - - g_free(name); -} - -/* Send a join conference, the first item in the parms list is the - * NMUser object and the second item is the conference to join. - * This callback is passed to purple_request_action when we ask the - * user if they want to join the conference. - */ -static void -_join_conference_cb(GSList * parms) -{ - NMUser *user; - NMConference *conference; - NMERR_T rc = NM_OK; - - if (parms == NULL || g_slist_length(parms) != 2) - return; - - user = g_slist_nth_data(parms, 0); - conference = g_slist_nth_data(parms, 1); - - if (user && conference) { - rc = nm_send_join_conference(user, conference, - _join_conf_resp_cb, conference); - _check_for_disconnect(user, rc); - } - - g_slist_free(parms); -} - -/* Send a reject conference, the first item in the parms list is the - * NMUser object and the second item is the conference to reject. - * This callback is passed to purple_request_action when we ask the - * user if they want to joing the conference. - */ -static void -_reject_conference_cb(GSList * parms) -{ - NMUser *user; - NMConference *conference; - NMERR_T rc = NM_OK; - - if (parms == NULL || g_slist_length(parms) != 2) - return; - - user = g_slist_nth_data(parms, 0); - conference = g_slist_nth_data(parms, 1); - - if (user && conference) { - rc = nm_send_reject_conference(user, conference, NULL, NULL); - _check_for_disconnect(user, rc); - } - - g_slist_free(parms); -} - -static void -_initiate_conference_cb(PurpleBlistNode *node, gpointer ignored) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - - NMUser *user; - const char *conf_name; - PurpleConversation *chat = NULL; - NMUserRecord *user_record; - NMConference *conference; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - - user = gc->proto_data; - if (user == NULL) - return; - - /* We should already have a userrecord for the buddy */ - user_record = nm_find_user_record(user, purple_buddy_get_name(buddy)); - if (user_record == NULL) - return; - - conf_name = _get_conference_name(++user->conference_count); - chat = serv_got_joined_chat(gc, user->conference_count, conf_name); - if (chat) { - - conference = nm_create_conference(NULL); - nm_conference_set_data(conference, (gpointer) chat); - nm_send_create_conference(user, conference, _createconf_resp_send_invite, user_record); - nm_release_conference(conference); - } -} - -const char * -_get_conference_name(int id) -{ - static char *name = NULL; - - if (name) - g_free(name); - - name = g_strdup_printf(_("GroupWise Conference %d"), id); - - return name; -} - -static void -_show_privacy_locked_error(PurpleConnection *gc, NMUser *user) -{ - char *err; - - err = g_strdup_printf(_("Unable to change server side privacy settings (%s)."), - nm_error_to_string(NMERR_ADMIN_LOCKED)); - purple_notify_error(gc, NULL, err, NULL); - g_free(err); -} - -/******************************************************************************* - * Connect and recv callbacks - ******************************************************************************/ - -static void -novell_ssl_connect_error(PurpleSslConnection * gsc, - PurpleSslErrorType error, gpointer data) -{ - PurpleConnection *gc; - NMUser *user; - - gc = data; - user = gc->proto_data; - user->conn->ssl_conn->data = NULL; - - purple_connection_ssl_error (gc, error); -} - -static void -novell_ssl_recv_cb(gpointer data, PurpleSslConnection * gsc, - PurpleInputCondition condition) -{ - PurpleConnection *gc = data; - NMUser *user; - NMERR_T rc; - - if (gc == NULL) - return; - - user = gc->proto_data; - if (user == NULL) - return; - - rc = nm_process_new_data(user); - if (rc != NM_OK) { - - if (_is_disconnect_error(rc)) { - - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Error communicating with server. Closing connection.")); - } else { - purple_debug(PURPLE_DEBUG_INFO, "novell", - "Error processing event or response (%d).\n", rc); - } - } -} - -static void -novell_ssl_connected_cb(gpointer data, PurpleSslConnection * gsc, - PurpleInputCondition cond) -{ - PurpleConnection *gc = data; - NMUser *user; - NMConn *conn; - NMERR_T rc = 0; - const char *pwd = NULL; - const char *my_addr = NULL; - char *ua = NULL; - - if (gc == NULL || gsc == NULL) - return; - - user = gc->proto_data; - if ((user == NULL) || (conn = user->conn) == NULL) - return; - - purple_connection_update_progress(gc, _("Authenticating..."), - 2, NOVELL_CONNECT_STEPS); - - my_addr = purple_network_get_my_ip(gsc->fd); - pwd = purple_connection_get_password(gc); - ua = _user_agent_string(); - - rc = nm_send_login(user, pwd, my_addr, ua, _login_resp_cb, NULL); - if (rc == NM_OK) { - conn->connected = TRUE; - purple_ssl_input_add(gsc, novell_ssl_recv_cb, gc); - } else { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect")); - } - - purple_connection_update_progress(gc, _("Waiting for response..."), - 3, NOVELL_CONNECT_STEPS); - - g_free(ua); -} - -/******************************************************************************* - * Event callback and event handlers - ******************************************************************************/ - -static void -_evt_receive_message(NMUser * user, NMEvent * event) -{ - NMUserRecord *user_record = NULL; - NMContact *contact = NULL; - PurpleConversation *gconv; - NMConference *conference; - PurpleMessageFlags flags; - char *text = NULL; - - text = g_markup_escape_text(nm_event_get_text(event), -1); - - conference = nm_event_get_conference(event); - if (conference) { - - PurpleConversation *chat = nm_conference_get_data(conference); - - /* Is this a single person 'conversation' or a conference? */ - if (chat == NULL && nm_conference_get_participant_count(conference) == 1) { - - user_record = nm_find_user_record(user, nm_event_get_source(event)); - if (user_record) { - - flags = 0; - if (nm_event_get_type(event) == NMEVT_RECEIVE_AUTOREPLY) - flags |= PURPLE_MESSAGE_AUTO_RESP; - - serv_got_im(purple_account_get_connection(user->client_data), - nm_user_record_get_display_id(user_record), - text, flags, - nm_event_get_gmt(event)); - - gconv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, - nm_user_record_get_display_id(user_record), - (PurpleAccount *) user->client_data); - if (gconv) { - - contact = nm_find_contact(user, nm_event_get_source(event)); - if (contact) { - - purple_conversation_set_title( - gconv, nm_contact_get_display_name(contact)); - - - } else { - - const char *name = - nm_user_record_get_full_name(user_record); - - if (name == NULL) - name = nm_user_record_get_userid(user_record); - - purple_conversation_set_title(gconv, name); - } - - } - - } else { - /* this should not happen, see the event code. - * the event code will get the contact details from - * the server if it does not have them before calling - * the event callback. - */ - } - - } else if (chat) { - - /* get the contact for send if we have one */ - NMContact *contact = nm_find_contact(user, - nm_event_get_source(event)); - - /* get the user record for the sender */ - user_record = nm_find_user_record(user, nm_event_get_source(event)); - if (user_record) { - const char *name = nm_contact_get_display_name(contact); - - if (name == NULL) { - name = nm_user_record_get_full_name(user_record); - if (name == NULL) - name = nm_user_record_get_display_id(user_record); - } - - serv_got_chat_in(purple_account_get_connection(user->client_data), - purple_conv_chat_get_id(PURPLE_CONV_CHAT(chat)), - name, 0, text, nm_event_get_gmt(event)); - } - } - } - - g_free(text); -} - -static void -_evt_conference_left(NMUser * user, NMEvent * event) -{ - PurpleConversation *chat; - NMConference *conference; - - conference = nm_event_get_conference(event); - if (conference) { - chat = nm_conference_get_data(conference); - if (chat) { - NMUserRecord *ur = nm_find_user_record(user, - nm_event_get_source(event)); - - if (ur) - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(chat), - nm_user_record_get_display_id(ur), - NULL); - } - } -} - -static void -_evt_conference_invite_notify(NMUser * user, NMEvent * event) -{ - PurpleConversation *gconv; - NMConference *conference; - NMUserRecord *user_record = NULL; - char *str = NULL; - - user_record = nm_find_user_record(user, nm_event_get_source(event)); - conference = nm_event_get_conference(event); - if (user_record && conference) { - gconv = nm_conference_get_data(conference); - str = g_strdup_printf(_("%s has been invited to this conversation."), - nm_user_record_get_display_id(user_record)); - purple_conversation_write(gconv, NULL, str, - PURPLE_MESSAGE_SYSTEM, time(NULL)); - g_free(str); - } -} - -static void -_evt_conference_invite(NMUser * user, NMEvent * event) -{ - NMUserRecord *ur; - PurpleConnection *gc; - GSList *parms = NULL; - const char *title = NULL; - const char *secondary = NULL; - const char *name = NULL; - char *primary = NULL; - time_t gmt; - - ur = nm_find_user_record(user, nm_event_get_source(event)); - if (ur) - name = nm_user_record_get_full_name(ur); - - if (name == NULL) - name = nm_event_get_source(event); - - gmt = nm_event_get_gmt(event); - title = _("Invitation to Conversation"); - primary = g_strdup_printf(_("Invitation from: %s\n\nSent: %s"), - name, purple_date_format_full(localtime(&gmt))); - secondary = _("Would you like to join the conversation?"); - - /* Set up parms list for the callbacks - * We need to send the NMUser object and - * the NMConference object to the callbacks - */ - parms = NULL; - parms = g_slist_append(parms, user); - parms = g_slist_append(parms, nm_event_get_conference(event)); - - /* Prompt the user */ - /* TODO: Would it be better to use serv_got_chat_invite() here? */ - gc = purple_account_get_connection(user->client_data); - purple_request_action(gc, title, primary, secondary, - PURPLE_DEFAULT_ACTION_NONE, - purple_connection_get_account(gc), name, NULL, - parms, 2, - _("Yes"), G_CALLBACK(_join_conference_cb), - _("No"), G_CALLBACK(_reject_conference_cb)); - - g_free(primary); -} - - -static void -_evt_conference_joined(NMUser * user, NMEvent * event) -{ - PurpleConversation *chat = NULL; - PurpleConnection *gc; - NMConference *conference = NULL; - NMUserRecord *ur = NULL; - const char *name; - const char *conf_name; - - gc = purple_account_get_connection(user->client_data); - if (gc == NULL) - return; - - conference = nm_event_get_conference(event); - if (conference) { - chat = nm_conference_get_data(conference); - if (nm_conference_get_participant_count(conference) == 2 && chat == NULL) { - ur = nm_conference_get_participant(conference, 0); - if (ur) { - conf_name = _get_conference_name(++user->conference_count); - chat = - serv_got_joined_chat(gc, user->conference_count, conf_name); - if (chat) { - - nm_conference_set_data(conference, (gpointer) chat); - - name = nm_user_record_get_display_id(ur); - purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat), name, NULL, - PURPLE_CBFLAGS_NONE, TRUE); - - } - } - } - - if (chat != NULL) { - ur = nm_find_user_record(user, nm_event_get_source(event)); - if (ur) { - name = nm_user_record_get_display_id(ur); - if (!purple_conv_chat_find_user(PURPLE_CONV_CHAT(chat), name)) { - purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat), name, NULL, - PURPLE_CBFLAGS_NONE, TRUE); - } - } - } - } -} - -static void -_evt_status_change(NMUser * user, NMEvent * event) -{ - PurpleBuddy *buddy = NULL; - GSList *buddies; - GSList *bnode; - NMUserRecord *user_record; - const char *display_id; - int status; - - user_record = nm_event_get_user_record(event); - if (user_record) { - - /* Retrieve new status */ - status = nm_user_record_get_status(user_record); - - /* Update status for buddy in all folders */ - display_id = nm_user_record_get_display_id(user_record); - buddies = purple_find_buddies(user->client_data, display_id); - for (bnode = buddies; bnode; bnode = bnode->next) { - buddy = (PurpleBuddy *) bnode->data; - if (buddy) { - _update_buddy_status(user, buddy, status, nm_event_get_gmt(event)); - } - } - - g_slist_free(buddies); - - } -} - -static void -_evt_user_disconnect(NMUser * user, NMEvent * event) -{ - PurpleConnection *gc; - PurpleAccount *account = user->client_data; - - gc = purple_account_get_connection(account); - if (gc) - { - if (!purple_account_get_remember_password(account)) - purple_account_set_password(account, NULL); - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NAME_IN_USE, - _("You have signed on from another location")); - } -} - -static void -_evt_user_typing(NMUser * user, NMEvent * event) -{ - PurpleConnection *gc; - NMUserRecord *user_record = NULL; - - gc = purple_account_get_connection((PurpleAccount *) user->client_data); - if (gc) { - user_record = nm_find_user_record(user, nm_event_get_source(event)); - if (user_record) { - serv_got_typing(gc, nm_user_record_get_display_id(user_record), - 30, PURPLE_TYPING); - } - } -} - -static void -_evt_user_not_typing(NMUser * user, NMEvent * event) -{ - PurpleConnection *gc; - NMUserRecord *user_record; - - gc = purple_account_get_connection((PurpleAccount *) user->client_data); - if (gc) { - user_record = nm_find_user_record(user, nm_event_get_source(event)); - if (user_record) { - serv_got_typing_stopped(gc, - nm_user_record_get_display_id(user_record)); - } - } -} - -static void -_evt_undeliverable_status(NMUser * user, NMEvent * event) -{ - NMUserRecord *ur; - PurpleConversation *gconv; - char *str; - - ur = nm_find_user_record(user, nm_event_get_source(event)); - if (ur) { - /* XXX - Should this be PURPLE_CONV_TYPE_IM? */ - gconv = - purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, - nm_user_record_get_display_id(ur), - user->client_data); - if (gconv) { - const char *name = nm_user_record_get_full_name(ur); - - if (name == NULL) { - name = nm_user_record_get_display_id(ur); - } - str = g_strdup_printf(_("%s appears to be offline and did not receive" - " the message that you just sent."), name); - purple_conversation_write(gconv, NULL, str, - PURPLE_MESSAGE_SYSTEM, time(NULL)); - g_free(str); - } - } -} - -static void -_event_callback(NMUser * user, NMEvent * event) -{ - if (user == NULL || event == NULL) - return; - - switch (nm_event_get_type(event)) { - case NMEVT_STATUS_CHANGE: - _evt_status_change(user, event); - break; - case NMEVT_RECEIVE_AUTOREPLY: - case NMEVT_RECEIVE_MESSAGE: - _evt_receive_message(user, event); - break; - case NMEVT_USER_DISCONNECT: - _evt_user_disconnect(user, event); - break; - case NMEVT_USER_TYPING: - _evt_user_typing(user, event); - break; - case NMEVT_USER_NOT_TYPING: - _evt_user_not_typing(user, event); - break; - case NMEVT_SERVER_DISCONNECT: - /* Nothing to do? */ - break; - case NMEVT_INVALID_RECIPIENT: - break; - case NMEVT_UNDELIVERABLE_STATUS: - _evt_undeliverable_status(user, event); - break; - case NMEVT_CONFERENCE_INVITE_NOTIFY: - /* Someone else has been invited to join a - * conference that we are currently a part of - */ - _evt_conference_invite_notify(user, event); - break; - case NMEVT_CONFERENCE_INVITE: - /* We have been invited to join a conference */ - _evt_conference_invite(user, event); - break; - case NMEVT_CONFERENCE_JOINED: - /* Some one has joined a conference that we - * are a part of - */ - _evt_conference_joined(user, event); - break; - case NMEVT_CONFERENCE_LEFT: - /* Someone else has left a conference that we - * are currently a part of - */ - _evt_conference_left(user, event); - break; - default: - purple_debug(PURPLE_DEBUG_INFO, "novell", - "_event_callback(): unhandled event, %d\n", - nm_event_get_type(event)); - break; - } -} - -/******************************************************************************* - * Prpl Ops - ******************************************************************************/ - -static void -novell_login(PurpleAccount * account) -{ - PurpleConnection *gc; - NMUser *user = NULL; - const char *server; - const char *name; - int port; - - if (account == NULL) - return; - - gc = purple_account_get_connection(account); - if (gc == NULL) - return; - - server = purple_account_get_string(account, "server", NULL); - if (server == NULL || *server == '\0') { - - /* TODO: Would be nice to prompt if not set! - * purple_request_fields(gc, _("Server Address"),...); - */ - - /* ...but for now just error out with a nice message. */ - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, - _("Unable to connect to server. Please enter the " - "address of the server to which you wish to connect.")); - return; - } - - port = purple_account_get_int(account, "port", DEFAULT_PORT); - name = purple_account_get_username(account); - - user = nm_initialize_user(name, server, port, account, _event_callback); - if (user && user->conn) { - /* save user */ - gc->proto_data = user; - - /* connect to the server */ - purple_connection_update_progress(gc, _("Connecting"), - 1, NOVELL_CONNECT_STEPS); - - user->conn->use_ssl = TRUE; - - user->conn->ssl_conn = g_new0(NMSSLConn, 1); - user->conn->ssl_conn->read = (nm_ssl_read_cb) purple_ssl_read; - user->conn->ssl_conn->write = (nm_ssl_write_cb) purple_ssl_write; - - user->conn->ssl_conn->data = purple_ssl_connect(user->client_data, - user->conn->addr, user->conn->port, - novell_ssl_connected_cb, novell_ssl_connect_error, gc); - if (user->conn->ssl_conn->data == NULL) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, - _("SSL support unavailable")); - } - } -} - -static void -novell_close(PurpleConnection * gc) -{ - NMUser *user; - NMConn *conn; - - if (gc == NULL) - return; - - user = gc->proto_data; - if (user) { - conn = user->conn; - if (conn && conn->ssl_conn) { - purple_ssl_close(user->conn->ssl_conn->data); - } - nm_deinitialize_user(user); - } - gc->proto_data = NULL; -} - -static int -novell_send_im(PurpleConnection * gc, const char *name, - const char *message_body, PurpleMessageFlags flags) -{ - NMUserRecord *user_record = NULL; - NMConference *conf = NULL; - NMMessage *message; - NMUser *user; - const char *dn = NULL; - char *plain; - gboolean done = TRUE, created_conf = FALSE; - NMERR_T rc = NM_OK; - - if (gc == NULL || name == NULL || - message_body == NULL || *message_body == '\0') - return 0; - - user = gc->proto_data; - if (user == NULL) - return 0; - - /* Create a new message */ - plain = purple_unescape_html(message_body); - message = nm_create_message(plain); - g_free(plain); - - /* Need to get the DN for the buddy so we can look up the convo */ - dn = nm_lookup_dn(user, name); - - /* Do we already know about the sender? */ - user_record = nm_find_user_record(user, dn); - if (user_record) { - - /* Do we already have an instantiated conference? */ - conf = nm_find_conversation(user, dn); - if (conf == NULL) { - - /* If not, create a blank conference */ - conf = nm_create_conference(NULL); - created_conf = TRUE; - - nm_conference_add_participant(conf, user_record); - } - - nm_message_set_conference(message, conf); - - /* Make sure conference is instantiated */ - if (!nm_conference_is_instantiated(conf)) { - - /* It is not, so send the createconf. We will - * have to finish sending the message when we - * get the response with the new conference guid. - */ - rc = nm_send_create_conference(user, conf, _createconf_resp_send_msg, message); - _check_for_disconnect(user, rc); - - done = FALSE; - } - - } else { - - /* If we don't have details for the user, then we don't have - * a conference yet. So create one and send the getdetails - * to the server. We will have to finish sending the message - * when we get the response from the server. - */ - conf = nm_create_conference(NULL); - created_conf = TRUE; - - nm_message_set_conference(message, conf); - - rc = nm_send_get_details(user, name, _get_details_resp_send_msg, message); - _check_for_disconnect(user, rc); - - done = FALSE; - } - - if (done) { - - /* Did we find everything we needed? */ - rc = nm_send_message(user, message, _send_message_resp_cb); - _check_for_disconnect(user, rc); - - nm_release_message(message); - } - - if (created_conf && conf) - nm_release_conference(conf); - - return 1; -} - -static unsigned int -novell_send_typing(PurpleConnection * gc, const char *name, PurpleTypingState state) -{ - NMConference *conf = NULL; - NMUser *user; - const char *dn = NULL; - NMERR_T rc = NM_OK; - - if (gc == NULL || name == NULL) - return 0; - - user = gc->proto_data; - if (user == NULL) - return 0; - - /* Need to get the DN for the buddy so we can look up the convo */ - dn = nm_lookup_dn(user, name); - if (dn) { - - /* Now find the conference in our list */ - conf = nm_find_conversation(user, dn); - if (conf) { - - rc = nm_send_typing(user, conf, - ((state == PURPLE_TYPING) ? TRUE : FALSE), NULL); - _check_for_disconnect(user, rc); - - } - - } - - return 0; -} - -static void -novell_convo_closed(PurpleConnection * gc, const char *who) -{ - NMUser *user; - NMConference *conf; - const char *dn; - NMERR_T rc = NM_OK; - - if (gc == NULL || who == NULL) - return; - - user = gc->proto_data; - if (user && (dn = nm_lookup_dn(user, who))) { - conf = nm_find_conversation(user, dn); - if (conf) { - rc = nm_send_leave_conference(user, conf, NULL, NULL); - _check_for_disconnect(user, rc); - } - } -} - -static void -novell_chat_leave(PurpleConnection * gc, int id) -{ - NMConference *conference; - NMUser *user; - PurpleConversation *chat; - GSList *cnode; - NMERR_T rc = NM_OK; - - if (gc == NULL) - return; - - user = gc->proto_data; - if (user == NULL) - return; - - for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) { - conference = cnode->data; - if (conference && (chat = nm_conference_get_data(conference))) { - if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(chat)) == id) { - rc = nm_send_leave_conference(user, conference, NULL, NULL); - _check_for_disconnect(user, rc); - break; - } - } - } - - serv_got_chat_left(gc, id); -} - -static void -novell_chat_invite(PurpleConnection *gc, int id, - const char *message, const char *who) -{ - NMConference *conference; - NMUser *user; - PurpleConversation *chat; - GSList *cnode; - NMERR_T rc = NM_OK; - NMUserRecord *user_record = NULL; - - if (gc == NULL) - return; - - user = gc->proto_data; - if (user == NULL) - return; - - user_record = nm_find_user_record(user, who); - if (user_record == NULL) { - rc = nm_send_get_details(user, who, _get_details_resp_send_invite, GINT_TO_POINTER(id)); - _check_for_disconnect(user, rc); - return; - } - - for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) { - conference = cnode->data; - if (conference && (chat = nm_conference_get_data(conference))) { - if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(chat)) == id) { - rc = nm_send_conference_invite(user, conference, user_record, - message, _sendinvite_resp_cb, NULL); - _check_for_disconnect(user, rc); - break; - } - } - } -} - -static int -novell_chat_send(PurpleConnection * gc, int id, const char *text, PurpleMessageFlags flags) -{ - NMConference *conference; - PurpleConversation *chat; - GSList *cnode; - NMMessage *message; - NMUser *user; - NMERR_T rc = NM_OK; - const char *name; - char *str, *plain; - - if (gc == NULL || text == NULL) - return -1; - - user = gc->proto_data; - if (user == NULL) - return -1; - - plain = purple_unescape_html(text); - message = nm_create_message(plain); - g_free(plain); - - for (cnode = user->conferences; cnode != NULL; cnode = cnode->next) { - conference = cnode->data; - if (conference && (chat = nm_conference_get_data(conference))) { - if (purple_conv_chat_get_id(PURPLE_CONV_CHAT(chat)) == id) { - - nm_message_set_conference(message, conference); - - /* check to see if the conference is instatiated yet */ - if (!nm_conference_is_instantiated(conference)) { - nm_message_add_ref(message); - nm_send_create_conference(user, conference, _createconf_resp_send_msg, message); - } else { - rc = nm_send_message(user, message, _send_message_resp_cb); - } - - nm_release_message(message); - - if (!_check_for_disconnect(user, rc)) { - - /* Use the account alias if it is set */ - name = purple_account_get_alias(user->client_data); - if (name == NULL || *name == '\0') { - - /* If there is no account alias, try full name */ - name = nm_user_record_get_full_name(user->user_record); - if (name == NULL || *name == '\0') { - - /* Fall back to the username that we are signed in with */ - name = purple_account_get_username(user->client_data); - } - } - - serv_got_chat_in(gc, id, name, flags, text, time(NULL)); - return 0; - } else - return -1; - - } - } - } - - - /* The conference was not found, must be closed */ - chat = purple_find_chat(gc, id); - if (chat) { - str = g_strdup(_("This conference has been closed." - " No more messages can be sent.")); - purple_conversation_write(chat, NULL, str, PURPLE_MESSAGE_SYSTEM, time(NULL)); - g_free(str); - } - - if (message) - nm_release_message(message); - - return -1; -} - -static void -novell_add_buddy(PurpleConnection * gc, PurpleBuddy *buddy, PurpleGroup * group) -{ - NMFolder *folder = NULL; - NMContact *contact; - NMUser *user; - NMERR_T rc = NM_OK; - const char *alias, *gname, *bname; - - if (gc == NULL || buddy == NULL || group == NULL) - return; - - user = (NMUser *) purple_connection_get_protocol_data(gc); - if (user == NULL) - return; - - /* If we haven't synched the contact list yet, ignore - * the add_buddy calls. Server side list is the master. - */ - if (!user->clist_synched) - return; - - /* Don't re-add a buddy that is already on our contact list */ - if (nm_find_user_record(user, purple_buddy_get_name(buddy)) != NULL) - return; - - contact = nm_create_contact(); - nm_contact_set_dn(contact, purple_buddy_get_name(buddy)); - - /* Remove the PurpleBuddy (we will add it back after adding it - * to the server side list). Save the alias if there is one. - */ - alias = purple_buddy_get_alias(buddy); - bname = purple_buddy_get_name(buddy); - if (alias && !purple_strequal(alias, bname)) - nm_contact_set_display_name(contact, alias); - - purple_blist_remove_buddy(buddy); - buddy = NULL; - - gname = purple_group_get_name(group); - if (purple_strequal(gname, NM_ROOT_FOLDER_NAME)) { - gname = ""; - } - - folder = nm_find_folder(user, gname); - if (folder) { - - /* We have everything that we need, so send the createcontact */ - rc = nm_send_create_contact(user, folder, contact, - _create_contact_resp_cb, contact); - - } else { - - /* Need to create the folder before we can add the contact */ - rc = nm_send_create_folder(user, gname, - _create_folder_resp_add_contact, contact); - } - - _check_for_disconnect(user, rc); - -} - -static void -novell_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) -{ - NMContact *contact; - NMFolder *folder; - NMUser *user; - const char *dn, *gname; - NMERR_T rc = NM_OK; - - if (gc == NULL || buddy == NULL || group == NULL) - return; - - user = (NMUser *) gc->proto_data; - if (user && (dn = nm_lookup_dn(user, purple_buddy_get_name(buddy)))) { - gname = purple_group_get_name(group); - if (purple_strequal(gname, NM_ROOT_FOLDER_NAME)) { - gname = ""; - } - folder = nm_find_folder(user, gname); - if (folder) { - contact = nm_folder_find_contact(folder, dn); - if (contact) { - - /* Remove the buddy from the contact */ - nm_contact_set_data(contact, NULL); - - /* Tell the server to remove the contact */ - rc = nm_send_remove_contact(user, folder, contact, - _remove_contact_resp_cb, NULL); - _check_for_disconnect(user, rc); - } - } - } -} - -static void -novell_remove_group(PurpleConnection * gc, PurpleGroup *group) -{ - NMUser *user; - NMERR_T rc = NM_OK; - - if (gc == NULL || group == NULL) - return; - - user = (NMUser *) gc->proto_data; - if (user) { - NMFolder *folder = nm_find_folder(user, purple_group_get_name(group)); - - if (folder) { - rc = nm_send_remove_folder(user, folder, - _remove_folder_resp_cb, NULL); - _check_for_disconnect(user, rc); - } - } -} - -static void -novell_alias_buddy(PurpleConnection * gc, const char *name, const char *alias) -{ - NMContact *contact; - NMUser *user; - GList *contacts = NULL; - GList *cnode = NULL; - const char *dn = NULL, *fname = NULL; - NMERR_T rc = NM_OK; - - if (gc == NULL || name == NULL || alias == NULL) - return; - - user = (NMUser *) gc->proto_data; - if (user && (dn = nm_lookup_dn(user, name))) { - - /* Alias all of instances of the contact */ - contacts = nm_find_contacts(user, dn); - for (cnode = contacts; cnode != NULL; cnode = cnode->next) { - contact = (NMContact *) cnode->data; - if (contact) { - PurpleGroup *group = NULL; - PurpleBuddy *buddy; - NMFolder *folder; - - /* Alias the Purple buddy? */ - folder = nm_find_folder_by_id(user, - nm_contact_get_parent_id(contact)); - if (folder) { - fname = nm_folder_get_name(folder); - if (*fname == '\0') { - fname = NM_ROOT_FOLDER_NAME; - } - group = purple_find_group(fname); - } - - if (group) { - const char *balias; - buddy = purple_find_buddy_in_group(user->client_data, - name, group); - balias = buddy ? purple_buddy_get_local_buddy_alias(buddy) : NULL; - if (balias && !purple_strequal(balias, alias)) - purple_blist_alias_buddy(buddy, alias); - } - - /* Tell the server to alias the contact */ - rc = nm_send_rename_contact(user, contact, alias, - _rename_contact_resp_cb, NULL); - _check_for_disconnect(user, rc); - } - } - if (contacts) - g_list_free(contacts); - } -} - -static void -novell_group_buddy(PurpleConnection * gc, - const char *name, const char *old_group_name, - const char *new_group_name) -{ - NMFolder *old_folder; - NMFolder *new_folder; - NMContact *contact; - NMUser *user; - const char *dn; - NMERR_T rc = NM_OK; - - if (gc == NULL || name == NULL || - old_group_name == NULL || new_group_name == NULL) - return; - - user = (NMUser *) gc->proto_data; - if (user && (dn = nm_lookup_dn(user, name))) { - - /* Find the old folder */ - if (purple_strequal(old_group_name, NM_ROOT_FOLDER_NAME)) { - old_folder = nm_get_root_folder(user); - if (nm_folder_find_contact(old_folder, dn) == NULL) - old_folder = nm_find_folder(user, old_group_name); - } else { - old_folder = nm_find_folder(user, old_group_name); - } - - if (old_folder && (contact = nm_folder_find_contact(old_folder, dn))) { - - /* Find the new folder */ - new_folder = nm_find_folder(user, new_group_name); - if (new_folder == NULL) { - if (purple_strequal(new_group_name, NM_ROOT_FOLDER_NAME)) - new_folder = nm_get_root_folder(user); - } - - if (new_folder) { - - /* Tell the server to move the contact to the new folder */ - rc = nm_send_move_contact(user, contact, new_folder, - _move_contact_resp_cb, NULL); - - } else { - - nm_contact_add_ref(contact); - - /* Remove the old contact first */ - nm_send_remove_contact(user, old_folder, contact, - _remove_contact_resp_cb, NULL); - - /* New folder does not exist yet, so create it */ - rc = nm_send_create_folder(user, new_group_name, - _create_folder_resp_move_contact, - contact); - } - - _check_for_disconnect(user, rc); - } - } -} - -static void -novell_rename_group(PurpleConnection * gc, const char *old_name, - PurpleGroup *group, GList *moved_buddies) -{ - NMERR_T rc = NM_OK; - NMFolder *folder; - NMUser *user; - - if (gc == NULL || old_name == NULL || group == NULL || moved_buddies == NULL) { - return; - } - - user = gc->proto_data; - if (user) { - const char *gname = purple_group_get_name(group); - /* Does new folder exist already? */ - if (nm_find_folder(user, gname)) { - /* purple_blist_rename_group() adds the buddies - * to the new group and removes the old group... - * so there is nothing more to do here. - */ - return; - } - - if (purple_strequal(old_name, NM_ROOT_FOLDER_NAME)) { - /* Can't rename the root folder ... need to revisit this */ - return; - } - - folder = nm_find_folder(user, old_name); - if (folder) { - rc = nm_send_rename_folder(user, folder, gname, - _rename_folder_resp_cb, NULL); - _check_for_disconnect(user, rc); - } - } -} - -static const char * -novell_list_icon(PurpleAccount * account, PurpleBuddy * buddy) -{ - return "novell"; -} - -static void -novell_tooltip_text(PurpleBuddy * buddy, PurpleNotifyUserInfo * user_info, gboolean full) -{ - NMUserRecord *user_record = NULL; - PurpleConnection *gc; - NMUser *user; - int status = 0; - const char *status_str = NULL; - const char *text = NULL; - - if (buddy == NULL) - return; - - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - if (gc == NULL || (user = gc->proto_data) == NULL) - return; - - if (PURPLE_BUDDY_IS_ONLINE(buddy)) { - user_record = nm_find_user_record(user, purple_buddy_get_name(buddy)); - if (user_record) { - status = nm_user_record_get_status(user_record); - text = nm_user_record_get_status_text(user_record); - /* No custom text, so default it ... */ - switch (status) { - case NM_STATUS_AVAILABLE: - status_str = _("Available"); - break; - case NM_STATUS_AWAY: - status_str = _("Away"); - break; - case NM_STATUS_BUSY: - status_str = _("Busy"); - break; - case NM_STATUS_AWAY_IDLE: - status_str = _("Idle"); - break; - case NM_STATUS_OFFLINE: - status_str = _("Offline"); - break; - default: - status_str = _("Unknown"); - break; - } - - purple_notify_user_info_add_pair(user_info, _("Status"), status_str); - - if (text) - purple_notify_user_info_add_pair(user_info, _("Message"), text); - } - } -} - -static void -novell_set_idle(PurpleConnection * gc, int time) -{ - NMUser *user; - NMERR_T rc = NM_OK; - const char *id = NULL; - PurpleStatus *status = NULL; - - if (gc == NULL) - return; - - user = gc->proto_data; - if (user == NULL) - return; - - status = purple_account_get_active_status(purple_connection_get_account(gc)); - id = purple_status_get_id(status); - - /* Only go idle if active status is available */ - if (purple_strequal(id, NOVELL_STATUS_TYPE_AVAILABLE)) { - if (time > 0) { - rc = nm_send_set_status(user, NM_STATUS_AWAY_IDLE, NULL, NULL, NULL, NULL); - } else { - rc = nm_send_set_status(user, NM_STATUS_AVAILABLE, NULL, NULL, NULL, NULL); - } - } - - _check_for_disconnect(user, rc); -} - -static void -novell_get_info(PurpleConnection * gc, const char *name) -{ - NMUserRecord *user_record; - NMUser *user; - NMERR_T rc; - - if (gc == NULL || name == NULL) - return; - - user = (NMUser *) gc->proto_data; - if (user) { - - user_record = nm_find_user_record(user, name); - if (user_record) { - _show_info(gc, user_record, g_strdup(name)); - - } else { - rc = nm_send_get_details(user, name, - _get_details_resp_show_info, g_strdup(name)); - - _check_for_disconnect(user, rc); - - } - - } -} - -static char * -novell_status_text(PurpleBuddy * buddy) -{ - const char *text = NULL; - const char *dn = NULL; - PurpleAccount *account; - - account = buddy ? purple_buddy_get_account(buddy) : NULL; - if (buddy && account) { - PurpleConnection *gc = purple_account_get_connection(account); - - if (gc && gc->proto_data) { - NMUser *user = gc->proto_data; - - dn = nm_lookup_dn(user, purple_buddy_get_name(buddy)); - if (dn) { - NMUserRecord *user_record = nm_find_user_record(user, dn); - - if (user_record) { - text = nm_user_record_get_status_text(user_record); - if (text) - return g_strdup(text); - } - } - } - } - - return NULL; -} - -static GList * -novell_status_types(PurpleAccount *account) -{ - GList *status_types = NULL; - PurpleStatusType *type; - - g_return_val_if_fail(account != NULL, NULL); - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, NOVELL_STATUS_TYPE_AVAILABLE, - NULL, TRUE, TRUE, FALSE, - "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), - NULL); - status_types = g_list_append(status_types, type); - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, NOVELL_STATUS_TYPE_AWAY, - NULL, TRUE, TRUE, FALSE, - "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), - NULL); - status_types = g_list_append(status_types, type); - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, NOVELL_STATUS_TYPE_BUSY, - _("Busy"), TRUE, TRUE, FALSE, - "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), - NULL); - status_types = g_list_append(status_types, type); - - type = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE, NOVELL_STATUS_TYPE_APPEAR_OFFLINE, - NULL, TRUE, TRUE, FALSE); - status_types = g_list_append(status_types, type); - - type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE, TRUE, FALSE); - status_types = g_list_append(status_types, type); - - return status_types; -} - -static void -novell_set_status(PurpleAccount *account, PurpleStatus *status) -{ - PurpleConnection *gc; - gboolean connected; - PurplePresence *presence; - PurpleStatusType *type; - PurpleStatusPrimitive primitive; - NMUser *user; - NMSTATUS_T novellstatus = NM_STATUS_AVAILABLE; - NMERR_T rc = NM_OK; - const char *msg = NULL; - char *text = NULL; - - connected = purple_account_is_connected(account); - presence = purple_status_get_presence(status); - type = purple_status_get_type(status); - primitive = purple_status_type_get_primitive(type); - - /* - * We don't have any independent statuses, so we don't need to - * do anything when a status is deactivated (because another - * status is about to be activated). - */ - if (!purple_status_is_active(status)) - return; - - if (!connected) - return; - - gc = purple_account_get_connection(account); - user = gc->proto_data; - if (user == NULL) - return; - - if (primitive == PURPLE_STATUS_AVAILABLE) { - novellstatus = NM_STATUS_AVAILABLE; - } else if (primitive == PURPLE_STATUS_AWAY) { - novellstatus = NM_STATUS_AWAY; - } else if (primitive == PURPLE_STATUS_UNAVAILABLE) { - novellstatus = NM_STATUS_BUSY; - } else if (primitive == PURPLE_STATUS_INVISIBLE) { - novellstatus = NM_STATUS_OFFLINE; - } else if (purple_presence_is_idle(presence)) { - novellstatus = NM_STATUS_AWAY_IDLE; - } else { - novellstatus = NM_STATUS_AVAILABLE; - } - - if (primitive == PURPLE_STATUS_AWAY || primitive == PURPLE_STATUS_AVAILABLE || - primitive == PURPLE_STATUS_UNAVAILABLE) { - msg = purple_status_get_attr_string(status, "message"); - text = g_strdup(msg); - - if (primitive == PURPLE_STATUS_AVAILABLE) - msg = NULL; /* no auto replies for online status */ - - /* Don't want newlines in status text */ - purple_util_chrreplace(text, '\n', ' '); - } - - rc = nm_send_set_status(user, novellstatus, text, msg, NULL, NULL); - _check_for_disconnect(user, rc); - - if (text) - g_free(text); -} - -static void -novell_add_permit(PurpleConnection *gc, const char *who) -{ - NMUser *user; - NMERR_T rc = NM_OK; - const char *name = who; - - if (gc == NULL || who == NULL) - return; - - user = gc->proto_data; - if (user == NULL) - return; - - /* Remove first -- we will add it back in when we get - * the okay from the server - */ - purple_privacy_permit_remove(gc->account, who, TRUE); - - if (nm_user_is_privacy_locked(user)) { - _show_privacy_locked_error(gc, user); - _sync_privacy_lists(user); - return; - } - - /* Work around for problem with un-typed, dotted contexts */ - if (strchr(who, '.')) { - const char *dn = nm_lookup_dn(user, who); - if (dn == NULL) { - rc = nm_send_get_details(user, who, _get_details_send_privacy_create, - (gpointer)TRUE); - _check_for_disconnect(user, rc); - return; - } else { - name = dn; - } - } - - rc = nm_send_create_privacy_item(user, name, TRUE, - _create_privacy_item_permit_resp_cb, - g_strdup(who)); - _check_for_disconnect(user, rc); -} - -static void -novell_add_deny(PurpleConnection *gc, const char *who) -{ - NMUser *user; - NMERR_T rc = NM_OK; - const char *name = who; - - if (gc == NULL || who == NULL) - return; - - user = gc->proto_data; - if (user == NULL) - return; - - /* Remove first -- we will add it back in when we get - * the okay from the server - */ - purple_privacy_deny_remove(gc->account, who, TRUE); - - if (nm_user_is_privacy_locked(user)) { - _show_privacy_locked_error(gc, user); - _sync_privacy_lists(user); - return; - } - - /* Work around for problem with un-typed, dotted contexts */ - if (strchr(who, '.')) { - const char *dn = nm_lookup_dn(user, who); - if (dn == NULL) { - rc = nm_send_get_details(user, who, _get_details_send_privacy_create, - (gpointer)FALSE); - _check_for_disconnect(user, rc); - return; - } else { - name = dn; - } - } - - rc = nm_send_create_privacy_item(user, name, FALSE, - _create_privacy_item_deny_resp_cb, - g_strdup(who)); - _check_for_disconnect(user, rc); -} - -static void -novell_rem_permit(PurpleConnection *gc, const char *who) -{ - NMUser *user; - NMERR_T rc = NM_OK; - const char *dn = NULL; - - if (gc == NULL || who == NULL) - return; - - user = gc->proto_data; - if (user == NULL) - return; - - if (nm_user_is_privacy_locked(user)) { - _show_privacy_locked_error(gc, user); - _sync_privacy_lists(user); - return; - } - - dn = nm_lookup_dn(user, who); - if (dn == NULL) - dn = who; - - rc = nm_send_remove_privacy_item(user, dn, TRUE, - _remove_privacy_item_resp_cb, - g_strdup(who)); - _check_for_disconnect(user, rc); -} - -static void -novell_rem_deny(PurpleConnection *gc, const char *who) -{ - NMUser *user; - NMERR_T rc = NM_OK; - const char *dn = NULL; - - if (gc == NULL || who == NULL) - return; - - user = gc->proto_data; - if (user == NULL) - return; - - if (nm_user_is_privacy_locked(user)) { - _show_privacy_locked_error(gc, user); - _sync_privacy_lists(user); - return; - } - - dn = nm_lookup_dn(user, who); - if (dn == NULL) - dn = who; - - rc = nm_send_remove_privacy_item(user, dn, FALSE, - _remove_privacy_item_resp_cb, - g_strdup(who)); - _check_for_disconnect(user, rc); -} - -static void -novell_set_permit_deny(PurpleConnection *gc) -{ - NMERR_T rc = NM_OK; - const char *dn, *name = NULL; - NMUserRecord *user_record = NULL; - GSList *node = NULL, *copy = NULL; - NMUser *user; - int i, j, num_contacts, num_folders; - NMContact *contact; - NMFolder *folder = NULL; - - if (gc == NULL) - return; - - user = gc->proto_data; - if (user == NULL) - return; - - if (user->privacy_synched == FALSE) { - _sync_privacy_lists(user); - user->privacy_synched = TRUE; - return; - } - - if (nm_user_is_privacy_locked(user)) { - _show_privacy_locked_error(gc, user); - _sync_privacy_lists(user); - return; - } - - switch (gc->account->perm_deny) { - - case PURPLE_PRIVACY_ALLOW_ALL: - rc = nm_send_set_privacy_default(user, FALSE, - _set_privacy_default_resp_cb, NULL); - _check_for_disconnect(user, rc); - - /* clear server side deny list */ - if (rc == NM_OK) { - copy = g_slist_copy(user->deny_list); - for (node = copy; node && node->data; node = node->next) { - rc = nm_send_remove_privacy_item(user, (const char *)node->data, - FALSE, NULL, NULL); - if (_check_for_disconnect(user, rc)) - break; - } - g_slist_free(copy); - g_slist_free(user->deny_list); - user->deny_list = NULL; - } - break; - - case PURPLE_PRIVACY_DENY_ALL: - rc = nm_send_set_privacy_default(user, TRUE, - _set_privacy_default_resp_cb, NULL); - _check_for_disconnect(user, rc); - - /* clear server side allow list */ - if (rc == NM_OK) { - copy = g_slist_copy(user->allow_list); - for (node = copy; node && node->data; node = node->next) { - rc = nm_send_remove_privacy_item(user, (const char *)node->data, - TRUE, NULL, NULL); - if (_check_for_disconnect(user, rc)) - break; - } - g_slist_free(copy); - g_slist_free(user->allow_list); - user->allow_list = NULL; - } - break; - - case PURPLE_PRIVACY_ALLOW_USERS: - - rc = nm_send_set_privacy_default(user, TRUE, - _set_privacy_default_resp_cb, NULL); - _check_for_disconnect(user, rc); - - /* sync allow lists */ - if (rc == NM_OK) { - - for (node = user->allow_list; node; node = node->next) { - user_record = nm_find_user_record(user, (char *)node->data); - if (user_record) { - name = nm_user_record_get_display_id(user_record); - - if (!g_slist_find_custom(gc->account->permit, - name, (GCompareFunc)purple_utf8_strcasecmp)) { - purple_privacy_permit_add(gc->account, name , TRUE); - } - } - } - - for (node = gc->account->permit; node; node = node->next) { - dn = nm_lookup_dn(user, (char *)node->data); - if (dn) { - - if (!g_slist_find_custom(user->allow_list, - dn, (GCompareFunc)purple_utf8_strcasecmp)) { - rc = nm_send_create_privacy_item(user, dn, TRUE, - _create_privacy_item_deny_resp_cb, - g_strdup(dn)); - if (_check_for_disconnect(user, rc)) - return; - } - } else { - purple_privacy_permit_remove(gc->account, (char *)node->data, TRUE); - } - } - } - break; - - case PURPLE_PRIVACY_DENY_USERS: - - /* set to default allow */ - rc = nm_send_set_privacy_default(user, FALSE, - _set_privacy_default_resp_cb, NULL); - _check_for_disconnect(user, rc); - - /* sync deny lists */ - if (rc == NM_OK) { - - for (node = user->deny_list; node; node = node->next) { - user_record = nm_find_user_record(user, (char *)node->data); - if (user_record) { - name = nm_user_record_get_display_id(user_record); - - if (!g_slist_find_custom(gc->account->deny, - name, (GCompareFunc)purple_utf8_strcasecmp)) { - purple_privacy_deny_add(gc->account, name , TRUE); - } - } - } - - for (node = gc->account->deny; node; node = node->next) { - - name = NULL; - dn = nm_lookup_dn(user, (char *)node->data); - if (dn) { - user_record = nm_find_user_record(user, dn); - name = nm_user_record_get_display_id(user_record); - - if (!g_slist_find_custom(user->deny_list, - dn, (GCompareFunc)purple_utf8_strcasecmp)) { - rc = nm_send_create_privacy_item(user, dn, FALSE, - _create_privacy_item_deny_resp_cb, - g_strdup(name)); - if (_check_for_disconnect(user, rc)) - return; - } - } else { - purple_privacy_deny_remove(gc->account, (char *)node->data, TRUE); - } - } - - } - break; - - case PURPLE_PRIVACY_ALLOW_BUDDYLIST: - - /* remove users from allow list that are not in buddy list */ - copy = g_slist_copy(user->allow_list); - for (node = copy; node && node->data; node = node->next) { - if (!nm_find_contacts(user, node->data)) { - rc = nm_send_remove_privacy_item(user, (const char *)node->data, - TRUE, NULL, NULL); - if (_check_for_disconnect(user, rc)) - return; - } - } - g_slist_free(copy); - - /* add all buddies to allow list */ - num_contacts = nm_folder_get_contact_count(user->root_folder); - for (i = 0; i < num_contacts; i++) { - contact = nm_folder_get_contact(user->root_folder, i); - dn = nm_contact_get_dn(contact); - if (dn && !g_slist_find_custom(user->allow_list, - dn, (GCompareFunc)purple_utf8_strcasecmp)) - { - rc = nm_send_create_privacy_item(user, dn, TRUE, - _create_privacy_item_deny_resp_cb, - g_strdup(dn)); - if (_check_for_disconnect(user, rc)) - return; - } - - } - - num_folders = nm_folder_get_subfolder_count(user->root_folder); - for (i = 0; i < num_folders; i++) { - folder = nm_folder_get_subfolder(user->root_folder, i); - num_contacts = nm_folder_get_contact_count(folder); - for (j = 0; j < num_contacts; j++) { - contact = nm_folder_get_contact(folder, j); - dn = nm_contact_get_dn(contact); - if (dn && !g_slist_find_custom(user->allow_list, - dn, (GCompareFunc)purple_utf8_strcasecmp)) - { - rc = nm_send_create_privacy_item(user, dn, TRUE, - _create_privacy_item_deny_resp_cb, - g_strdup(dn)); - if (_check_for_disconnect(user, rc)) - return; - } - } - } - - /* set to default deny */ - rc = nm_send_set_privacy_default(user, TRUE, - _set_privacy_default_resp_cb, NULL); - if (_check_for_disconnect(user, rc)) - break; - - break; - } -} - -static GList * -novell_blist_node_menu(PurpleBlistNode *node) -{ - GList *list = NULL; - PurpleMenuAction *act; - - if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { - act = purple_menu_action_new(_("Initiate _Chat"), - PURPLE_CALLBACK(_initiate_conference_cb), - NULL, NULL); - list = g_list_append(list, act); - } - - return list; -} - -static void -novell_keepalive(PurpleConnection *gc) -{ - NMUser *user; - NMERR_T rc = NM_OK; - - if (gc == NULL) - return; - - user = gc->proto_data; - if (user == NULL) - return; - - rc = nm_send_keepalive(user, NULL, NULL); - _check_for_disconnect(user, rc); -} - -static PurplePluginProtocolInfo prpl_info = { - 0, - NULL, /* user_splits */ - NULL, /* protocol_options */ - NO_BUDDY_ICONS, /* icon_spec */ - novell_list_icon, /* list_icon */ - NULL, /* list_emblems */ - novell_status_text, /* status_text */ - novell_tooltip_text, /* tooltip_text */ - novell_status_types, /* status_types */ - novell_blist_node_menu, /* blist_node_menu */ - NULL, /* chat_info */ - NULL, /* chat_info_defaults */ - novell_login, /* login */ - novell_close, /* close */ - novell_send_im, /* send_im */ - NULL, /* set_info */ - novell_send_typing, /* send_typing */ - novell_get_info, /* get_info */ - novell_set_status, /* set_status */ - novell_set_idle, /* set_idle */ - NULL, /* change_passwd */ - novell_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - novell_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - novell_add_permit, /* add_permit */ - novell_add_deny, /* add_deny */ - novell_rem_permit, /* rem_permit */ - novell_rem_deny, /* rem_deny */ - novell_set_permit_deny, /* set_permit_deny */ - NULL, /* join_chat */ - NULL, /* reject_chat */ - NULL, /* get_chat_name */ - novell_chat_invite, /* chat_invite */ - novell_chat_leave, /* chat_leave */ - NULL, /* chat_whisper */ - novell_chat_send, /* chat_send */ - novell_keepalive, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - NULL, /* get_cb_away */ - novell_alias_buddy, /* alias_buddy */ - novell_group_buddy, /* group_buddy */ - novell_rename_group, /* rename_group */ - NULL, /* buddy_free */ - novell_convo_closed, /* convo_closed */ - purple_normalize_nocase, /* normalize */ - NULL, /* set_buddy_icon */ - novell_remove_group, /* remove_group */ - NULL, /* get_cb_real_name */ - NULL, /* set_chat_topic */ - NULL, /* find_blist_chat */ - NULL, /* roomlist_get_list */ - NULL, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - NULL, /* can_receive_file */ - NULL, /* send_file */ - NULL, /* new_xfer */ - NULL, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - NULL, /* send_attention */ - NULL, /* get_attention_types */ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - NULL, /* add_buddy_with_invite */ - NULL, /* add_buddies_with_invite */ - NULL, /* get_cb_alias */ - NULL, /* chat_can_receive_file */ - NULL, /* chat_send_file */ -}; - -static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - "prpl-novell", /**< id */ - "GroupWise", /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Novell GroupWise Messenger Protocol Plugin"), - /** description */ - N_("Novell GroupWise Messenger Protocol Plugin"), - NULL, /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - NULL, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, - NULL, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin * plugin) -{ - PurpleAccountOption *option; - - option = purple_account_option_string_new(_("Server address"), "server", NULL); - prpl_info.protocol_options = - g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_int_new(_("Server port"), "port", DEFAULT_PORT); - prpl_info.protocol_options = - g_list_append(prpl_info.protocol_options, option); - - my_protocol = plugin; -} - -PURPLE_INIT_PLUGIN(novell, init_plugin, info); diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/sametime/Makefile.am --- a/libpurple/protocols/sametime/Makefile.am Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -EXTRA_DIST = \ - Makefile.mingw - -pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) - -noinst_HEADERS = sametime.h - -SAMETIMESOURCES = sametime.c - -AM_CFLAGS = \ - $(st) - -if STATIC_SAMETIME - -st = -DPURPLE_STATIC_PRPL -noinst_LTLIBRARIES = libsametime.la -libsametime_la_CFLAGS = $(AM_CFLAGS) - -else - -st = -pkg_LTLIBRARIES = libsametime.la - -endif - -libsametime_la_SOURCES = $(SAMETIMESOURCES) -libsametime_la_LDFLAGS = -module -avoid-version -libsametime_la_LIBADD = $(GLIB_LIBS) $(MEANWHILE_LIBS) - -AM_CPPFLAGS = \ - -I$(top_srcdir)/libpurple \ - -I$(top_builddir)/libpurple \ - $(DEBUG_CFLAGS) \ - $(GLIB_CFLAGS) \ - $(MEANWHILE_CFLAGS) \ - -DG_LOG_DOMAIN=\"sametime\" - diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/sametime/Makefile.mingw --- a/libpurple/protocols/sametime/Makefile.mingw Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,85 +0,0 @@ -# -# Makefile.mingw -# -# Description: Makefile for win32 (mingw) version of libsametime -# - -PIDGIN_TREE_TOP := ../../.. -include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak - -TARGET = libsametime -NEEDED_DLLS = $(MEANWHILE_TOP)/bin/libmeanwhile-1.dll -TYPE = PLUGIN - -# Static or Plugin... -ifeq ($(TYPE),STATIC) - DEFINES += -DSTATIC - DLL_INSTALL_DIR = $(PURPLE_INSTALL_DIR) -else - ifeq ($(TYPE),PLUGIN) - DLL_INSTALL_DIR = $(PURPLE_INSTALL_PLUGINS_DIR) - endif -endif - -CFLAGS += -DG_LOG_DOMAIN=\"sametime\" - -## -## INCLUDE PATHS -## -INCLUDE_PATHS += -I. \ - -I$(MEANWHILE_TOP)/include/meanwhile \ - -I$(GTK_TOP)/include \ - -I$(GTK_TOP)/include/glib-2.0 \ - -I$(GTK_TOP)/lib/glib-2.0/include \ - -I$(PURPLE_TOP) \ - -I$(PURPLE_TOP)/win32 \ - -I$(PIDGIN_TREE_TOP) - -LIB_PATHS += -L$(GTK_TOP)/lib \ - -L$(MEANWHILE_TOP)/lib \ - -L$(PURPLE_TOP) - -## -## SOURCES, OBJECTS -## -C_SRC = sametime.c - -OBJECTS = $(C_SRC:%.c=%.o) - -## -## LIBRARIES -## -LIBS = \ - -lglib-2.0 \ - -lintl \ - -lws2_32 \ - -lmeanwhile \ - -lpurple - -include $(PIDGIN_COMMON_RULES) - -## -## TARGET DEFINITIONS -## -.PHONY: all install clean - -all: $(TARGET).dll - -install: all $(DLL_INSTALL_DIR) $(PURPLE_INSTALL_DIR) - cp $(TARGET).dll $(DLL_INSTALL_DIR) - cp $(NEEDED_DLLS) $(PURPLE_INSTALL_DIR) - -$(OBJECTS): $(PURPLE_CONFIG_H) - -$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS) - $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll - -## -## CLEAN RULES -## - -clean: - rm -f $(OBJECTS) - rm -f $(TARGET).dll - -include $(PIDGIN_COMMON_TARGETS) diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/sametime/sametime.c --- a/libpurple/protocols/sametime/sametime.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5795 +0,0 @@ - -/* - Meanwhile Protocol Plugin for Purple - Adds Lotus Sametime support to Purple using the Meanwhile library - - Copyright (C) 2004 Christopher (siege) O'Brien - - 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 "internal.h" - -/* system includes */ -#include -#include - -/* glib includes */ -#include - -/* purple includes */ -#include "account.h" -#include "accountopt.h" -#include "circbuffer.h" -#include "conversation.h" -#include "debug.h" -#include "ft.h" -#include "imgstore.h" -#include "mime.h" -#include "notify.h" -#include "plugin.h" -#include "privacy.h" -#include "prpl.h" -#include "request.h" -#include "util.h" -#include "version.h" - -/* meanwhile includes */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* plugin includes */ -#include "sametime.h" - - -/* considering that there's no display of this information for prpls, - I don't know why I even bother providing these. Oh valiant reader, - I do it all for you. */ -/* scratch that, I just added it to the prpl options panel */ -#define PLUGIN_ID "prpl-meanwhile" -#define PLUGIN_NAME "Sametime" -#define PLUGIN_SUMMARY "Sametime Protocol Plugin" -#define PLUGIN_DESC "Open implementation of a Lotus Sametime client" -#define PLUGIN_AUTHOR "Christopher (siege) O'Brien " -#define PLUGIN_HOMEPAGE "http://meanwhile.sourceforge.net/" - - -/* plugin preference names */ -#define MW_PRPL_OPT_BASE "/plugins/prpl/meanwhile" -#define MW_PRPL_OPT_BLIST_ACTION MW_PRPL_OPT_BASE "/blist_action" -#define MW_PRPL_OPT_PSYCHIC MW_PRPL_OPT_BASE "/psychic" -#define MW_PRPL_OPT_FORCE_LOGIN MW_PRPL_OPT_BASE "/force_login" -#define MW_PRPL_OPT_SAVE_DYNAMIC MW_PRPL_OPT_BASE "/save_dynamic" - - -/* stages of connecting-ness */ -#define MW_CONNECT_STEPS 11 - - -/* stages of conciousness */ -#define MW_STATE_OFFLINE "offline" -#define MW_STATE_ACTIVE "active" -#define MW_STATE_AWAY "away" -#define MW_STATE_BUSY "dnd" -#define MW_STATE_MESSAGE "message" -#define MW_STATE_ENLIGHTENED "buddha" - - -/* keys to get/set chat information */ -#define CHAT_KEY_CREATOR "chat.creator" -#define CHAT_KEY_NAME "chat.name" -#define CHAT_KEY_TOPIC "chat.topic" -#define CHAT_KEY_INVITE "chat.invite" -#define CHAT_KEY_IS_PLACE "chat.is_place" - - -/* key for associating a mwLoginType with a buddy */ -#define BUDDY_KEY_CLIENT "meanwhile.client" - -/* store the remote alias so that we can re-create it easily */ -#define BUDDY_KEY_NAME "meanwhile.shortname" - -/* enum mwSametimeUserType */ -#define BUDDY_KEY_TYPE "meanwhile.type" - - -/* key for the real group name for a meanwhile group */ -#define GROUP_KEY_NAME "meanwhile.group" - -/* enum mwSametimeGroupType */ -#define GROUP_KEY_TYPE "meanwhile.type" - -/* NAB group owning account */ -#define GROUP_KEY_OWNER "meanwhile.account" - -/* key gtk blist uses to indicate a collapsed group */ -#define GROUP_KEY_COLLAPSED "collapsed" - - -/* verification replacement */ -#define mwSession_NO_SECRET "meanwhile.no_secret" - - -/* keys to get/set purple plugin information */ -#define MW_KEY_HOST "server" -#define MW_KEY_PORT "port" -#define MW_KEY_FORCE "force_login" -#define MW_KEY_FAKE_IT "fake_client_id" -#define MW_KEY_CLIENT "client_id_val" -#define MW_KEY_MAJOR "client_major" -#define MW_KEY_MINOR "client_minor" - - -/** number of seconds from the first blist change before a save to the - storage service occurs. */ -#define BLIST_SAVE_SECONDS 15 - - -/** the possible buddy list storage settings */ -enum blist_choice { - blist_choice_LOCAL = 1, /**< local only */ - blist_choice_MERGE = 2, /**< merge from server */ - blist_choice_STORE = 3, /**< merge from and save to server */ - blist_choice_SYNCH = 4 /**< sync with server */ -}; - - -/** the default blist storage option */ -#define BLIST_CHOICE_DEFAULT blist_choice_SYNCH - - -/* testing for the above */ -#define BLIST_PREF_IS(n) (purple_prefs_get_int(MW_PRPL_OPT_BLIST_ACTION)==(n)) -#define BLIST_PREF_IS_LOCAL() BLIST_PREF_IS(blist_choice_LOCAL) -#define BLIST_PREF_IS_MERGE() BLIST_PREF_IS(blist_choice_MERGE) -#define BLIST_PREF_IS_STORE() BLIST_PREF_IS(blist_choice_STORE) -#define BLIST_PREF_IS_SYNCH() BLIST_PREF_IS(blist_choice_SYNCH) - - -/* debugging output */ -#define DEBUG_ERROR(...) purple_debug_error(G_LOG_DOMAIN, __VA_ARGS__) -#define DEBUG_INFO(...) purple_debug_info(G_LOG_DOMAIN, __VA_ARGS__) -#define DEBUG_MISC(...) purple_debug_misc(G_LOG_DOMAIN, __VA_ARGS__) -#define DEBUG_WARN(...) purple_debug_warning(G_LOG_DOMAIN, __VA_ARGS__) - - -/** ensure non-null strings */ -#ifndef NSTR -# define NSTR(str) ((str)? (str): "(null)") -#endif - - -/** calibrates distinct secure channel nomenclature */ -static const unsigned char no_secret[] = { - 0x2d, 0x2d, 0x20, 0x73, 0x69, 0x65, 0x67, 0x65, - 0x20, 0x6c, 0x6f, 0x76, 0x65, 0x73, 0x20, 0x6a, - 0x65, 0x6e, 0x6e, 0x69, 0x20, 0x61, 0x6e, 0x64, - 0x20, 0x7a, 0x6f, 0x65, 0x20, 0x2d, 0x2d, 0x00, -}; - - -/** handler IDs from g_log_set_handler in mw_plugin_init */ -static guint log_handler[2] = { 0, 0 }; - - -/** the purple plugin data. - available as gc->proto_data and mwSession_getClientData */ -struct mwPurplePluginData { - struct mwSession *session; - - struct mwServiceAware *srvc_aware; - struct mwServiceConference *srvc_conf; - struct mwServiceFileTransfer *srvc_ft; - struct mwServiceIm *srvc_im; - struct mwServicePlace *srvc_place; - struct mwServiceResolve *srvc_resolve; - struct mwServiceStorage *srvc_store; - - /** map of PurpleGroup:mwAwareList and mwAwareList:PurpleGroup */ - GHashTable *group_list_map; - - /** event id for the buddy list save callback */ - guint save_event; - - /** socket fd */ - int socket; - gint outpa; /* like inpa, but the other way */ - - /** circular buffer for outgoing data */ - PurpleCircBuffer *sock_buf; - - PurpleConnection *gc; -}; - - -typedef struct { - PurpleBuddy *buddy; - PurpleGroup *group; -} BuddyAddData; - - -/* blist and aware functions */ - -static void blist_export(PurpleConnection *gc, struct mwSametimeList *stlist); - -static void blist_store(struct mwPurplePluginData *pd); - -static void blist_schedule(struct mwPurplePluginData *pd); - -static void blist_merge(PurpleConnection *gc, struct mwSametimeList *stlist); - -static void blist_sync(PurpleConnection *gc, struct mwSametimeList *stlist); - -static gboolean buddy_is_external(PurpleBuddy *b); - -static void buddy_add(struct mwPurplePluginData *pd, PurpleBuddy *buddy); - -static PurpleBuddy * -buddy_ensure(PurpleConnection *gc, PurpleGroup *group, - struct mwSametimeUser *stuser); - -static void group_add(struct mwPurplePluginData *pd, PurpleGroup *group); - -static PurpleGroup * -group_ensure(PurpleConnection *gc, struct mwSametimeGroup *stgroup); - -static struct mwAwareList * -list_ensure(struct mwPurplePluginData *pd, PurpleGroup *group); - - -/* session functions */ - -static struct mwSession * -gc_to_session(PurpleConnection *gc); - -static PurpleConnection *session_to_gc(struct mwSession *session); - - -/* conference functions */ - -static struct mwConference * -conf_find_by_id(struct mwPurplePluginData *pd, int id); - - -/* conversation functions */ - -struct convo_msg { - enum mwImSendType type; - gpointer data; - GDestroyNotify clear; -}; - - -struct convo_data { - struct mwConversation *conv; - GList *queue; /**< outgoing message queue, list of convo_msg */ -}; - -static void convo_data_new(struct mwConversation *conv); - -static void convo_data_free(struct convo_data *conv); - -static void convo_features(struct mwConversation *conv); - -static PurpleConversation *convo_get_gconv(struct mwConversation *conv); - - -/* name and id */ - -struct named_id { - char *id; - char *name; -}; - - -/* connection functions */ - -static void connect_cb(gpointer data, gint source, const gchar *error_message); - - -/* ----- session ------ */ - - -/** resolves a mwSession from a PurpleConnection */ -static struct mwSession *gc_to_session(PurpleConnection *gc) { - struct mwPurplePluginData *pd; - - g_return_val_if_fail(gc != NULL, NULL); - - pd = gc->proto_data; - g_return_val_if_fail(pd != NULL, NULL); - - return pd->session; -} - - -/** resolves a PurpleConnection from a mwSession */ -static PurpleConnection *session_to_gc(struct mwSession *session) { - struct mwPurplePluginData *pd; - - g_return_val_if_fail(session != NULL, NULL); - - pd = mwSession_getClientData(session); - g_return_val_if_fail(pd != NULL, NULL); - - return pd->gc; -} - - -static void write_cb(gpointer data, gint source, PurpleInputCondition cond) { - struct mwPurplePluginData *pd = data; - PurpleCircBuffer *circ = pd->sock_buf; - gsize avail; - int ret; - - DEBUG_INFO("write_cb\n"); - - g_return_if_fail(circ != NULL); - - avail = purple_circ_buffer_get_max_read(circ); - if(BUF_LONG < avail) avail = BUF_LONG; - - while(avail) { - ret = write(pd->socket, circ->outptr, avail); - - if(ret <= 0) - break; - - purple_circ_buffer_mark_read(circ, ret); - avail = purple_circ_buffer_get_max_read(circ); - if(BUF_LONG < avail) avail = BUF_LONG; - } - - if(! avail) { - purple_input_remove(pd->outpa); - pd->outpa = 0; - } -} - - -static int mw_session_io_write(struct mwSession *session, - const guchar *buf, gsize len) { - struct mwPurplePluginData *pd; - gssize ret = 0; - int err = 0; - - pd = mwSession_getClientData(session); - - /* socket was already closed. */ - if(pd->socket == 0) - return 1; - - if(pd->outpa) { - DEBUG_INFO("already pending INPUT_WRITE, buffering\n"); - purple_circ_buffer_append(pd->sock_buf, buf, len); - return 0; - } - - while(len) { - ret = write(pd->socket, buf, (len > BUF_LEN)? BUF_LEN: len); - - if(ret <= 0) - break; - - len -= ret; - buf += ret; - } - - if(ret <= 0) - err = errno; - - if(err == EAGAIN) { - /* append remainder to circular buffer */ - DEBUG_INFO("EAGAIN\n"); - purple_circ_buffer_append(pd->sock_buf, buf, len); - pd->outpa = purple_input_add(pd->socket, PURPLE_INPUT_WRITE, write_cb, pd); - - } else if(len > 0) { - gchar *tmp = g_strdup_printf(_("Lost connection with server: %s"), - g_strerror(errno)); - DEBUG_ERROR("write returned %" G_GSSIZE_FORMAT ", %" G_GSIZE_FORMAT - " bytes left unwritten\n", ret, len); - purple_connection_error_reason(pd->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - tmp); - g_free(tmp); - -#if 0 - close(pd->socket); - pd->socket = 0; -#endif - - return -1; - } - - return 0; -} - - -static void mw_session_io_close(struct mwSession *session) { - struct mwPurplePluginData *pd; - PurpleConnection *gc; - - pd = mwSession_getClientData(session); - g_return_if_fail(pd != NULL); - - gc = pd->gc; - - if(pd->outpa) { - purple_input_remove(pd->outpa); - pd->outpa = 0; - } - - if(pd->socket) { - close(pd->socket); - pd->socket = 0; - } - - if(gc->inpa) { - purple_input_remove(gc->inpa); - gc->inpa = 0; - } -} - - -static void mw_session_clear(struct mwSession *session) { - ; /* nothing for now */ -} - - -/* ----- aware list ----- */ - - -static void blist_resolve_alias_cb(struct mwServiceResolve *srvc, - guint32 id, guint32 code, GList *results, - gpointer data) { - struct mwResolveResult *result; - struct mwResolveMatch *match; - - g_return_if_fail(results != NULL); - - result = results->data; - g_return_if_fail(result != NULL); - g_return_if_fail(result->matches != NULL); - - match = result->matches->data; - g_return_if_fail(match != NULL); - - purple_blist_server_alias_buddy(data, match->name); - purple_blist_node_set_string(data, BUDDY_KEY_NAME, match->name); -} - - -static void mw_aware_list_on_aware(struct mwAwareList *list, - struct mwAwareSnapshot *aware) { - - PurpleConnection *gc; - PurpleAccount *acct; - - struct mwPurplePluginData *pd; - guint32 idle; - guint stat; - const char *id; - const char *status = MW_STATE_ACTIVE; - - gc = mwAwareList_getClientData(list); - acct = purple_connection_get_account(gc); - - pd = gc->proto_data; - idle = aware->status.time; - stat = aware->status.status; - id = aware->id.user; - - if(idle) { - guint32 idle_len; /*< how long a client has been idle */ - guint32 ugly_idle_len; /*< how long a broken client has been idle */ - - DEBUG_INFO("%s has idle value 0x%x\n", NSTR(id), idle); - - idle_len = time(NULL) - idle; - ugly_idle_len = ((time(NULL) * 1000) - idle) / 1000; - - if(idle > ugly_idle_len) - ugly_idle_len = 0; - else - ugly_idle_len = (ugly_idle_len - idle) / 1000; - - /* - what's the deal here? Well, good clients are smart enough to - publish their idle time by using an attribute to indicate that - they went idle at some time UTC, in seconds since epoch. Bad - clients use milliseconds since epoch. So we're going to compute - the idle time for either method, then figure out the lower of - the two and use that. Blame the ST 7.5 development team for - this. - */ - - DEBUG_INFO("idle time: %u, ugly idle time: %u\n", idle_len, ugly_idle_len); - -#if 1 - if(idle_len <= ugly_idle_len) { - ; /* DEBUG_INFO("sane idle value, let's use it\n"); */ - } else { - idle = time(NULL) - ugly_idle_len; - } - -#else - if(idle < 0 || idle > time(NULL)) { - DEBUG_INFO("hiding a messy idle value 0x%x\n", NSTR(id), idle); - idle = -1; - } -#endif - } - - switch(stat) { - case mwStatus_ACTIVE: - status = MW_STATE_ACTIVE; - idle = 0; - break; - - case mwStatus_IDLE: - if(! idle) idle = -1; - break; - - case mwStatus_AWAY: - status = MW_STATE_AWAY; - break; - - case mwStatus_BUSY: - status = MW_STATE_BUSY; - break; - } - - /* NAB group members */ - if(aware->group) { - PurpleGroup *group; - PurpleBuddy *buddy; - PurpleBlistNode *bnode; - - group = g_hash_table_lookup(pd->group_list_map, list); - buddy = purple_find_buddy_in_group(acct, id, group); - bnode = (PurpleBlistNode *) buddy; - - if(! buddy) { - struct mwServiceResolve *srvc; - GList *query; - - buddy = purple_buddy_new(acct, id, NULL); - purple_blist_add_buddy(buddy, NULL, group, NULL); - - bnode = (PurpleBlistNode *) buddy; - - srvc = pd->srvc_resolve; - query = g_list_append(NULL, (char *) id); - - mwServiceResolve_resolve(srvc, query, mwResolveFlag_USERS, - blist_resolve_alias_cb, buddy, NULL); - g_list_free(query); - } - - purple_blist_node_set_int(bnode, BUDDY_KEY_TYPE, mwSametimeUser_NORMAL); - } - - if(aware->online) { - purple_prpl_got_user_status(acct, id, status, NULL); - purple_prpl_got_user_idle(acct, id, !!idle, (time_t) idle); - - } else { - purple_prpl_got_user_status(acct, id, MW_STATE_OFFLINE, NULL); - } -} - - -static void mw_aware_list_on_attrib(struct mwAwareList *list, - struct mwAwareIdBlock *id, - struct mwAwareAttribute *attrib) { - - ; /* nothing. We'll get attribute data as we need it */ -} - - -static void mw_aware_list_clear(struct mwAwareList *list) { - ; /* nothing for now */ -} - - -static struct mwAwareListHandler mw_aware_list_handler = { - mw_aware_list_on_aware, - mw_aware_list_on_attrib, - mw_aware_list_clear, -}; - - -/** Ensures that an Aware List is associated with the given group, and - returns that list. */ -static struct mwAwareList * -list_ensure(struct mwPurplePluginData *pd, PurpleGroup *group) { - - struct mwAwareList *list; - - g_return_val_if_fail(pd != NULL, NULL); - g_return_val_if_fail(group != NULL, NULL); - - list = g_hash_table_lookup(pd->group_list_map, group); - if(! list) { - list = mwAwareList_new(pd->srvc_aware, &mw_aware_list_handler); - mwAwareList_setClientData(list, pd->gc, NULL); - - mwAwareList_watchAttributes(list, - mwAttribute_AV_PREFS_SET, - mwAttribute_MICROPHONE, - mwAttribute_SPEAKERS, - mwAttribute_VIDEO_CAMERA, - mwAttribute_FILE_TRANSFER, - NULL); - - g_hash_table_replace(pd->group_list_map, group, list); - g_hash_table_insert(pd->group_list_map, list, group); - } - - return list; -} - - -static void blist_export(PurpleConnection *gc, struct mwSametimeList *stlist) { - /* - find the account for this connection - - iterate through the buddy list - - add each buddy matching this account to the stlist - */ - - PurpleAccount *acct; - PurpleBlistNode *gn, *cn, *bn; - PurpleGroup *grp; - PurpleBuddy *bdy; - - struct mwSametimeGroup *stg = NULL; - struct mwIdBlock idb = { NULL, NULL }; - - acct = purple_connection_get_account(gc); - g_return_if_fail(acct != NULL); - - for(gn = purple_blist_get_root(); gn; - gn = purple_blist_node_get_sibling_next(gn)) { - const char *owner; - const char *gname; - enum mwSametimeGroupType gtype; - gboolean gopen; - - if(! PURPLE_BLIST_NODE_IS_GROUP(gn)) continue; - grp = (PurpleGroup *) gn; - - /* the group's type (normal or dynamic) */ - gtype = purple_blist_node_get_int(gn, GROUP_KEY_TYPE); - if(! gtype) gtype = mwSametimeGroup_NORMAL; - - /* if it's a normal group with none of our people in it, skip it */ - if(gtype == mwSametimeGroup_NORMAL && !purple_group_on_account(grp, acct)) - continue; - - /* if the group has an owner and we're not it, skip it */ - owner = purple_blist_node_get_string(gn, GROUP_KEY_OWNER); - if(owner && !purple_strequal(owner, purple_account_get_username(acct))) - continue; - - /* the group's actual name may be different from the purple group's - name. Find whichever is there */ - gname = purple_blist_node_get_string(gn, GROUP_KEY_NAME); - if(! gname) gname = purple_group_get_name(grp); - - /* we save this, but never actually honor it */ - gopen = ! purple_blist_node_get_bool(gn, GROUP_KEY_COLLAPSED); - - stg = mwSametimeGroup_new(stlist, gtype, gname); - mwSametimeGroup_setAlias(stg, purple_group_get_name(grp)); - mwSametimeGroup_setOpen(stg, gopen); - - /* don't attempt to put buddies in a dynamic group, it breaks - other clients */ - if(gtype == mwSametimeGroup_DYNAMIC) - continue; - - for(cn = purple_blist_node_get_first_child(gn); - cn; - cn = purple_blist_node_get_sibling_next(cn)) { - if(! PURPLE_BLIST_NODE_IS_CONTACT(cn)) continue; - - for(bn = purple_blist_node_get_first_child(cn); - bn; - bn = purple_blist_node_get_sibling_next(bn)) { - if(! PURPLE_BLIST_NODE_IS_BUDDY(bn)) continue; - if(! PURPLE_BLIST_NODE_SHOULD_SAVE(bn)) continue; - - bdy = (PurpleBuddy *) bn; - - if(purple_buddy_get_account(bdy) == acct) { - struct mwSametimeUser *stu; - enum mwSametimeUserType utype; - - idb.user = (char *)purple_buddy_get_name(bdy); - - utype = purple_blist_node_get_int(bn, BUDDY_KEY_TYPE); - if(! utype) utype = mwSametimeUser_NORMAL; - - stu = mwSametimeUser_new(stg, utype, &idb); - mwSametimeUser_setShortName(stu, purple_buddy_get_server_alias(bdy)); - mwSametimeUser_setAlias(stu, purple_buddy_get_local_buddy_alias(bdy)); - } - } - } - } -} - - -static void blist_store(struct mwPurplePluginData *pd) { - - struct mwSametimeList *stlist; - struct mwServiceStorage *srvc; - struct mwStorageUnit *unit; - - PurpleConnection *gc; - - struct mwPutBuffer *b; - struct mwOpaque *o; - - g_return_if_fail(pd != NULL); - - srvc = pd->srvc_store; - g_return_if_fail(srvc != NULL); - - gc = pd->gc; - - if(BLIST_PREF_IS_LOCAL() || BLIST_PREF_IS_MERGE()) { - DEBUG_INFO("preferences indicate not to save remote blist\n"); - return; - - } else if(MW_SERVICE_IS_DEAD(srvc)) { - DEBUG_INFO("aborting save of blist: storage service is not alive\n"); - return; - - } else if(BLIST_PREF_IS_STORE() || BLIST_PREF_IS_SYNCH()) { - DEBUG_INFO("saving remote blist\n"); - - } else { - g_return_if_reached(); - } - - /* create and export to a list object */ - stlist = mwSametimeList_new(); - blist_export(gc, stlist); - - /* write it to a buffer */ - b = mwPutBuffer_new(); - mwSametimeList_put(b, stlist); - mwSametimeList_free(stlist); - - /* put the buffer contents into a storage unit */ - unit = mwStorageUnit_new(mwStore_AWARE_LIST); - o = mwStorageUnit_asOpaque(unit); - mwPutBuffer_finalize(o, b); - - /* save the storage unit to the service */ - mwServiceStorage_save(srvc, unit, NULL, NULL, NULL); -} - - -static gboolean blist_save_cb(gpointer data) { - struct mwPurplePluginData *pd = data; - - blist_store(pd); - pd->save_event = 0; - return FALSE; -} - - -/** schedules the buddy list to be saved to the server */ -static void blist_schedule(struct mwPurplePluginData *pd) { - if(pd->save_event) return; - - pd->save_event = purple_timeout_add_seconds(BLIST_SAVE_SECONDS, - blist_save_cb, pd); -} - - -static gboolean buddy_is_external(PurpleBuddy *b) { - g_return_val_if_fail(b != NULL, FALSE); - return purple_str_has_prefix(purple_buddy_get_name(b), "@E "); -} - - -/** Actually add a buddy to the aware service, and schedule the buddy - list to be saved to the server */ -static void buddy_add(struct mwPurplePluginData *pd, - PurpleBuddy *buddy) { - - struct mwAwareIdBlock idb = { mwAware_USER, (char *) purple_buddy_get_name(buddy), NULL }; - struct mwAwareList *list; - - PurpleGroup *group; - GList *add; - - add = g_list_prepend(NULL, &idb); - - group = purple_buddy_get_group(buddy); - list = list_ensure(pd, group); - - if(mwAwareList_addAware(list, add)) { - purple_blist_remove_buddy(buddy); - } - - blist_schedule(pd); - - g_list_free(add); -} - - -/** ensure that a PurpleBuddy exists in the group with data - appropriately matching the st user entry from the st list */ -static PurpleBuddy *buddy_ensure(PurpleConnection *gc, PurpleGroup *group, - struct mwSametimeUser *stuser) { - - struct mwPurplePluginData *pd = gc->proto_data; - PurpleBuddy *buddy; - PurpleAccount *acct = purple_connection_get_account(gc); - - const char *id = mwSametimeUser_getUser(stuser); - const char *name = mwSametimeUser_getShortName(stuser); - const char *alias = mwSametimeUser_getAlias(stuser); - enum mwSametimeUserType type = mwSametimeUser_getType(stuser); - - g_return_val_if_fail(id != NULL, NULL); - g_return_val_if_fail(strlen(id) > 0, NULL); - - buddy = purple_find_buddy_in_group(acct, id, group); - if(! buddy) { - buddy = purple_buddy_new(acct, id, alias); - - purple_blist_add_buddy(buddy, NULL, group, NULL); - buddy_add(pd, buddy); - } - - purple_blist_alias_buddy(buddy, alias); - purple_blist_server_alias_buddy(buddy, name); - purple_blist_node_set_string((PurpleBlistNode *) buddy, BUDDY_KEY_NAME, name); - purple_blist_node_set_int((PurpleBlistNode *) buddy, BUDDY_KEY_TYPE, type); - - return buddy; -} - - -/** add aware watch for a dynamic group */ -static void group_add(struct mwPurplePluginData *pd, - PurpleGroup *group) { - - struct mwAwareIdBlock idb = { mwAware_GROUP, NULL, NULL }; - struct mwAwareList *list; - const char *n; - GList *add; - - n = purple_blist_node_get_string((PurpleBlistNode *) group, GROUP_KEY_NAME); - if(! n) n = purple_group_get_name(group); - - idb.user = (char *) n; - add = g_list_prepend(NULL, &idb); - - list = list_ensure(pd, group); - mwAwareList_addAware(list, add); - g_list_free(add); -} - - -/** ensure that a PurpleGroup exists in the blist with data - appropriately matching the st group entry from the st list */ -static PurpleGroup *group_ensure(PurpleConnection *gc, - struct mwSametimeGroup *stgroup) { - PurpleAccount *acct; - PurpleGroup *group = NULL; - PurpleBuddyList *blist; - PurpleBlistNode *gn; - const char *name, *alias, *owner; - enum mwSametimeGroupType type; - - acct = purple_connection_get_account(gc); - owner = purple_account_get_username(acct); - - blist = purple_get_blist(); - g_return_val_if_fail(blist != NULL, NULL); - - name = mwSametimeGroup_getName(stgroup); - alias = mwSametimeGroup_getAlias(stgroup); - type = mwSametimeGroup_getType(stgroup); - - if (!name) { - DEBUG_WARN("Can't ensure a null group\n"); - return NULL; - } - - if (!name) { - DEBUG_WARN("Can't ensure a null group\n"); - return NULL; - } - - DEBUG_INFO("attempting to ensure group %s, called %s\n", - NSTR(name), NSTR(alias)); - - /* first attempt at finding the group, by the name key */ - for(gn = purple_blist_get_root(); gn; - gn = purple_blist_node_get_sibling_next(gn)) { - const char *n, *o; - if(! PURPLE_BLIST_NODE_IS_GROUP(gn)) continue; - n = purple_blist_node_get_string(gn, GROUP_KEY_NAME); - o = purple_blist_node_get_string(gn, GROUP_KEY_OWNER); - - DEBUG_INFO("found group named %s, owned by %s\n", NSTR(n), NSTR(o)); - - if(n && purple_strequal(n, name)) { - if(!o || purple_strequal(o, owner)) { - DEBUG_INFO("that'll work\n"); - group = (PurpleGroup *) gn; - break; - } - } - } - - /* try again, by alias */ - if(! group) { - DEBUG_INFO("searching for group by alias %s\n", NSTR(alias)); - group = purple_find_group(alias); - } - - /* oh well, no such group. Let's create it! */ - if(! group) { - DEBUG_INFO("creating group\n"); - group = purple_group_new(alias); - purple_blist_add_group(group, NULL); - } - - gn = (PurpleBlistNode *) group; - purple_blist_node_set_string(gn, GROUP_KEY_NAME, name); - purple_blist_node_set_int(gn, GROUP_KEY_TYPE, type); - - if(type == mwSametimeGroup_DYNAMIC) { - purple_blist_node_set_string(gn, GROUP_KEY_OWNER, owner); - group_add(gc->proto_data, group); - } - - return group; -} - - -/** merge the entries from a st list into the purple blist */ -static void blist_merge(PurpleConnection *gc, struct mwSametimeList *stlist) { - struct mwSametimeGroup *stgroup; - struct mwSametimeUser *stuser; - - PurpleGroup *group; - - GList *gl, *gtl, *ul, *utl; - - gl = gtl = mwSametimeList_getGroups(stlist); - for(; gl; gl = gl->next) { - - stgroup = (struct mwSametimeGroup *) gl->data; - group = group_ensure(gc, stgroup); - - ul = utl = mwSametimeGroup_getUsers(stgroup); - for(; ul; ul = ul->next) { - - stuser = (struct mwSametimeUser *) ul->data; - buddy_ensure(gc, group, stuser); - } - g_list_free(utl); - } - g_list_free(gtl); -} - - -/** remove all buddies on account from group. If del is TRUE and group - is left empty, remove group as well */ -static void group_clear(PurpleGroup *group, PurpleAccount *acct, gboolean del) { - PurpleConnection *gc; - GList *prune = NULL; - PurpleBlistNode *gn, *cn, *bn; - - g_return_if_fail(group != NULL); - - DEBUG_INFO("clearing members from pruned group %s\n", NSTR(purple_group_get_name(group))); - - gc = purple_account_get_connection(acct); - g_return_if_fail(gc != NULL); - - gn = (PurpleBlistNode *) group; - - for(cn = purple_blist_node_get_first_child(gn); - cn; - cn = purple_blist_node_get_sibling_next(cn)) { - if(! PURPLE_BLIST_NODE_IS_CONTACT(cn)) continue; - - for(bn = purple_blist_node_get_first_child(cn); - bn; - bn = purple_blist_node_get_sibling_next(bn)) { - PurpleBuddy *gb = (PurpleBuddy *) bn; - - if(! PURPLE_BLIST_NODE_IS_BUDDY(bn)) continue; - - if(purple_buddy_get_account(gb) == acct) { - DEBUG_INFO("clearing %s from group\n", NSTR(purple_buddy_get_name(gb))); - prune = g_list_prepend(prune, gb); - } - } - } - - /* quickly unsubscribe from presence for the entire group */ - purple_account_remove_group(acct, group); - - /* remove blist entries that need to go */ - while(prune) { - purple_blist_remove_buddy(prune->data); - prune = g_list_delete_link(prune, prune); - } - DEBUG_INFO("cleared buddies\n"); - - /* optionally remove group from blist */ - if(del && !purple_blist_get_group_size(group, TRUE)) { - DEBUG_INFO("removing empty group\n"); - purple_blist_remove_group(group); - } -} - - -/** prune out group members that shouldn't be there */ -static void group_prune(PurpleConnection *gc, PurpleGroup *group, - struct mwSametimeGroup *stgroup) { - - PurpleAccount *acct; - PurpleBlistNode *gn, *cn, *bn; - - GHashTable *stusers; - GList *prune = NULL; - GList *ul, *utl; - - g_return_if_fail(group != NULL); - - DEBUG_INFO("pruning membership of group %s\n", NSTR(purple_group_get_name(group))); - - acct = purple_connection_get_account(gc); - g_return_if_fail(acct != NULL); - - stusers = g_hash_table_new(g_str_hash, g_str_equal); - - /* build a hash table for quick lookup while pruning the group - contents */ - utl = mwSametimeGroup_getUsers(stgroup); - for(ul = utl; ul; ul = ul->next) { - const char *id = mwSametimeUser_getUser(ul->data); - g_hash_table_insert(stusers, (char *) id, ul->data); - DEBUG_INFO("server copy has %s\n", NSTR(id)); - } - g_list_free(utl); - - gn = (PurpleBlistNode *) group; - - for(cn = purple_blist_node_get_first_child(gn); - cn; - cn = purple_blist_node_get_sibling_next(cn)) { - if(! PURPLE_BLIST_NODE_IS_CONTACT(cn)) continue; - - for(bn = purple_blist_node_get_first_child(cn); - bn; - bn = purple_blist_node_get_sibling_next(bn)) { - PurpleBuddy *gb = (PurpleBuddy *) bn; - - if(! PURPLE_BLIST_NODE_IS_BUDDY(bn)) continue; - - /* if the account is correct and they're not in our table, mark - them for pruning */ - if(purple_buddy_get_account(gb) == acct && !g_hash_table_lookup(stusers, purple_buddy_get_name(gb))) { - DEBUG_INFO("marking %s for pruning\n", NSTR(purple_buddy_get_name(gb))); - prune = g_list_prepend(prune, gb); - } - } - } - DEBUG_INFO("done marking\n"); - - g_hash_table_destroy(stusers); - - if(prune) { - purple_account_remove_buddies(acct, prune, NULL); - while(prune) { - purple_blist_remove_buddy(prune->data); - prune = g_list_delete_link(prune, prune); - } - } -} - - -/** synch the entries from a st list into the purple blist, removing any - existing buddies that aren't in the st list */ -static void blist_sync(PurpleConnection *gc, struct mwSametimeList *stlist) { - - PurpleAccount *acct; - PurpleBuddyList *blist; - PurpleBlistNode *gn; - - GHashTable *stgroups; - GList *g_prune = NULL; - - GList *gl, *gtl; - - const char *acct_n; - - DEBUG_INFO("synchronizing local buddy list from server list\n"); - - acct = purple_connection_get_account(gc); - g_return_if_fail(acct != NULL); - - acct_n = purple_account_get_username(acct); - - blist = purple_get_blist(); - g_return_if_fail(blist != NULL); - - /* build a hash table for quick lookup while pruning the local - list, mapping group name to group structure */ - stgroups = g_hash_table_new(g_str_hash, g_str_equal); - - gtl = mwSametimeList_getGroups(stlist); - for(gl = gtl; gl; gl = gl->next) { - const char *name = mwSametimeGroup_getName(gl->data); - g_hash_table_insert(stgroups, (char *) name, gl->data); - } - g_list_free(gtl); - - /* find all groups which should be pruned from the local list */ - for(gn = purple_blist_get_root(); gn; - gn = purple_blist_node_get_sibling_next(gn)) { - PurpleGroup *grp = (PurpleGroup *) gn; - const char *gname, *owner; - struct mwSametimeGroup *stgrp; - - if(! PURPLE_BLIST_NODE_IS_GROUP(gn)) continue; - - /* group not belonging to this account */ - if(! purple_group_on_account(grp, acct)) - continue; - - /* dynamic group belonging to this account. don't prune contents */ - owner = purple_blist_node_get_string(gn, GROUP_KEY_OWNER); - if(owner && purple_strequal(owner, acct_n)) - continue; - - /* we actually are synching by this key as opposed to the group - title, which can be different things in the st list */ - gname = purple_blist_node_get_string(gn, GROUP_KEY_NAME); - if(! gname) gname = purple_group_get_name(grp); - - stgrp = g_hash_table_lookup(stgroups, gname); - if(! stgrp) { - /* remove the whole group */ - DEBUG_INFO("marking group %s for pruning\n", purple_group_get_name(grp)); - g_prune = g_list_prepend(g_prune, grp); - - } else { - /* synch the group contents */ - group_prune(gc, grp, stgrp); - } - } - DEBUG_INFO("done marking groups\n"); - - /* don't need this anymore */ - g_hash_table_destroy(stgroups); - - /* prune all marked groups */ - while(g_prune) { - PurpleGroup *grp = g_prune->data; - PurpleBlistNode *gn = (PurpleBlistNode *) grp; - const char *owner; - gboolean del = TRUE; - - owner = purple_blist_node_get_string(gn, GROUP_KEY_OWNER); - if(owner && !purple_strequal(owner, acct_n)) { - /* it's a specialty group belonging to another account with some - of our members in it, so don't fully delete it */ - del = FALSE; - } - - group_clear(g_prune->data, acct, del); - g_prune = g_list_delete_link(g_prune, g_prune); - } - - /* done with the pruning, let's merge in the additions */ - blist_merge(gc, stlist); -} - - -/** callback passed to the storage service when it's told to load the - st list */ -static void fetch_blist_cb(struct mwServiceStorage *srvc, - guint32 result, struct mwStorageUnit *item, - gpointer data) { - - struct mwPurplePluginData *pd = data; - struct mwSametimeList *stlist; - - struct mwGetBuffer *b; - - g_return_if_fail(result == ERR_SUCCESS); - - /* check our preferences for loading */ - if(BLIST_PREF_IS_LOCAL()) { - DEBUG_INFO("preferences indicate not to load remote buddy list\n"); - return; - } - - b = mwGetBuffer_wrap(mwStorageUnit_asOpaque(item)); - - stlist = mwSametimeList_new(); - mwSametimeList_get(b, stlist); - - /* merge or synch depending on preferences */ - if(BLIST_PREF_IS_MERGE() || BLIST_PREF_IS_STORE()) { - blist_merge(pd->gc, stlist); - - } else if(BLIST_PREF_IS_SYNCH()) { - blist_sync(pd->gc, stlist); - } - - mwSametimeList_free(stlist); - mwGetBuffer_free(b); -} - - -/** signal triggered when a conversation is opened in Purple */ -static void conversation_created_cb(PurpleConversation *g_conv, - struct mwPurplePluginData *pd) { - - /* we need to tell the IM service to negotiate features for the - conversation right away, otherwise it'll wait until the first - message is sent before offering NotesBuddy features. Therefore - whenever Purple creates a conversation, we'll immediately open the - channel to the other side and figure out what the target can - handle. Unfortunately, this makes us vulnerable to Psychic Mode, - whereas a more lazy negotiation based on the first message - would not */ - - PurpleConnection *gc; - struct mwIdBlock who = { 0, 0 }; - struct mwConversation *conv; - - gc = purple_conversation_get_gc(g_conv); - if(pd->gc != gc) - return; /* not ours */ - - if(purple_conversation_get_type(g_conv) != PURPLE_CONV_TYPE_IM) - return; /* wrong type */ - - who.user = (char *) purple_conversation_get_name(g_conv); - conv = mwServiceIm_getConversation(pd->srvc_im, &who); - - convo_features(conv); - - if(mwConversation_isClosed(conv)) - mwConversation_open(conv); -} - - -static void blist_menu_nab(PurpleBlistNode *node, gpointer data) { - struct mwPurplePluginData *pd = data; - PurpleConnection *gc; - - PurpleGroup *group = (PurpleGroup *) node; - - GString *str; - char *tmp; - const char *gname; - - g_return_if_fail(pd != NULL); - - gc = pd->gc; - g_return_if_fail(gc != NULL); - - g_return_if_fail(PURPLE_BLIST_NODE_IS_GROUP(node)); - - str = g_string_new(NULL); - - tmp = (char *) purple_blist_node_get_string(node, GROUP_KEY_NAME); - gname = purple_group_get_name(group); - - g_string_append_printf(str, _("Group Title: %s
"), gname); - g_string_append_printf(str, _("Notes Group ID: %s
"), tmp); - - tmp = g_strdup_printf(_("Info for Group %s"), gname); - - purple_notify_formatted(gc, tmp, _("Notes Address Book Information"), - NULL, str->str, NULL, NULL); - - g_free(tmp); - g_string_free(str, TRUE); -} - - -/** The normal blist menu prpl function doesn't get called for groups, - so we use the blist-node-extended-menu signal to trigger this - handler */ -static void blist_node_menu_cb(PurpleBlistNode *node, - GList **menu, struct mwPurplePluginData *pd) { - const char *owner; - PurpleAccount *acct; - PurpleMenuAction *act; - - /* we only want groups */ - if(! PURPLE_BLIST_NODE_IS_GROUP(node)) return; - - acct = purple_connection_get_account(pd->gc); - g_return_if_fail(acct != NULL); - - /* better make sure we're connected */ - if(! purple_account_is_connected(acct)) return; - -#if 0 - /* if there's anyone in the group for this acct, offer to invite - them all to a conference */ - if(purple_group_on_account(group, acct)) { - act = purple_menu_action_new(_("Invite Group to Conference..."), - PURPLE_CALLBACK(blist_menu_group_invite), - pd, NULL); - *menu = g_list_append(*menu, NULL); - } -#endif - - /* check if it's a NAB group for this account */ - owner = purple_blist_node_get_string(node, GROUP_KEY_OWNER); - if(owner && purple_strequal(owner, purple_account_get_username(acct))) { - act = purple_menu_action_new(_("Get Notes Address Book Info"), - PURPLE_CALLBACK(blist_menu_nab), pd, NULL); - *menu = g_list_append(*menu, act); - } -} - - -/* lifted this from oldstatus, since HEAD doesn't do this at login - anymore. */ -static void blist_init(PurpleAccount *acct) { - PurpleBlistNode *gnode, *cnode, *bnode; - GList *add_buds = NULL; - - for(gnode = purple_blist_get_root(); gnode; - gnode = purple_blist_node_get_sibling_next(gnode)) { - if(! PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue; - - for(cnode = purple_blist_node_get_first_child(gnode); - cnode; - cnode = purple_blist_node_get_sibling_next(cnode)) { - if(! PURPLE_BLIST_NODE_IS_CONTACT(cnode)) - continue; - for(bnode = purple_blist_node_get_first_child(cnode); - bnode; - bnode = purple_blist_node_get_sibling_next(bnode)) { - PurpleBuddy *b; - if(!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) - continue; - - b = (PurpleBuddy *)bnode; - if(purple_buddy_get_account(b) == acct) { - add_buds = g_list_append(add_buds, b); - } - } - } - } - - if(add_buds) { - purple_account_add_buddies(acct, add_buds); - g_list_free(add_buds); - } -} - - -/** Last thing to happen from a started session */ -static void services_starting(struct mwPurplePluginData *pd) { - - PurpleConnection *gc; - PurpleAccount *acct; - struct mwStorageUnit *unit; - PurpleBlistNode *l; - - gc = pd->gc; - acct = purple_connection_get_account(gc); - - /* grab the buddy list from the server */ - unit = mwStorageUnit_new(mwStore_AWARE_LIST); - mwServiceStorage_load(pd->srvc_store, unit, fetch_blist_cb, pd, NULL); - - /* find all the NAB groups and subscribe to them */ - for(l = purple_blist_get_root(); l; - l = purple_blist_node_get_sibling_next(l)) { - PurpleGroup *group = (PurpleGroup *) l; - enum mwSametimeGroupType gt; - const char *owner; - - if(! PURPLE_BLIST_NODE_IS_GROUP(l)) continue; - - /* if the group is ownerless, or has an owner and we're not it, - skip it */ - owner = purple_blist_node_get_string(l, GROUP_KEY_OWNER); - if(!owner || !purple_strequal(owner, purple_account_get_username(acct))) - continue; - - gt = purple_blist_node_get_int(l, GROUP_KEY_TYPE); - if(gt == mwSametimeGroup_DYNAMIC) - group_add(pd, group); - } - - /* set the aware attributes */ - /* indicate we understand what AV prefs are, but don't support any */ - mwServiceAware_setAttributeBoolean(pd->srvc_aware, - mwAttribute_AV_PREFS_SET, TRUE); - mwServiceAware_unsetAttribute(pd->srvc_aware, mwAttribute_MICROPHONE); - mwServiceAware_unsetAttribute(pd->srvc_aware, mwAttribute_SPEAKERS); - mwServiceAware_unsetAttribute(pd->srvc_aware, mwAttribute_VIDEO_CAMERA); - - /* ... but we can do file transfers! */ - mwServiceAware_setAttributeBoolean(pd->srvc_aware, - mwAttribute_FILE_TRANSFER, TRUE); - - blist_init(acct); -} - - -static void session_loginRedirect(struct mwSession *session, - const char *host) { - struct mwPurplePluginData *pd; - PurpleConnection *gc; - PurpleAccount *account; - guint port; - const char *current_host; - - pd = mwSession_getClientData(session); - gc = pd->gc; - account = purple_connection_get_account(gc); - port = purple_account_get_int(account, MW_KEY_PORT, MW_PLUGIN_DEFAULT_PORT); - current_host = purple_account_get_string(account, MW_KEY_HOST, - MW_PLUGIN_DEFAULT_HOST); - - if(purple_account_get_bool(account, MW_KEY_FORCE, FALSE) || - !host || purple_strequal(current_host, host) || - (purple_proxy_connect(gc, account, host, port, connect_cb, pd) == NULL)) { - - /* if we're configured to force logins, or if we're being - redirected to the already configured host, or if we couldn't - connect to the new host, we'll force the login instead */ - - mwSession_forceLogin(session); - } -} - - -static void mw_prpl_set_status(PurpleAccount *acct, PurpleStatus *status); - - -/** called from mw_session_stateChange when the session's state is - mwSession_STARTED. Any finalizing of start-up stuff should go - here */ -static void session_started(struct mwPurplePluginData *pd) { - PurpleStatus *status; - PurpleAccount *acct; - - /* set out initial status */ - acct = purple_connection_get_account(pd->gc); - status = purple_account_get_active_status(acct); - mw_prpl_set_status(acct, status); - - /* start watching for new conversations */ - purple_signal_connect(purple_conversations_get_handle(), - "conversation-created", pd, - PURPLE_CALLBACK(conversation_created_cb), pd); - - /* watch for group extended menu items */ - purple_signal_connect(purple_blist_get_handle(), - "blist-node-extended-menu", pd, - PURPLE_CALLBACK(blist_node_menu_cb), pd); - - /* use our services to do neat things */ - services_starting(pd); -} - - -static void session_stopping(struct mwPurplePluginData *pd) { - /* stop watching the signals from session_started */ - purple_signals_disconnect_by_handle(pd); -} - - -static void mw_session_stateChange(struct mwSession *session, - enum mwSessionState state, - gpointer info) { - struct mwPurplePluginData *pd; - PurpleConnection *gc; - const char *msg = NULL; - - pd = mwSession_getClientData(session); - gc = pd->gc; - - switch(state) { - case mwSession_STARTING: - msg = _("Sending Handshake"); - purple_connection_update_progress(gc, msg, 2, MW_CONNECT_STEPS); - break; - - case mwSession_HANDSHAKE: - msg = _("Waiting for Handshake Acknowledgement"); - purple_connection_update_progress(gc, msg, 3, MW_CONNECT_STEPS); - break; - - case mwSession_HANDSHAKE_ACK: - msg = _("Handshake Acknowledged, Sending Login"); - purple_connection_update_progress(gc, msg, 4, MW_CONNECT_STEPS); - break; - - case mwSession_LOGIN: - msg = _("Waiting for Login Acknowledgement"); - purple_connection_update_progress(gc, msg, 5, MW_CONNECT_STEPS); - break; - - case mwSession_LOGIN_REDIR: - msg = _("Login Redirected"); - purple_connection_update_progress(gc, msg, 6, MW_CONNECT_STEPS); - session_loginRedirect(session, info); - break; - - case mwSession_LOGIN_CONT: - msg = _("Forcing Login"); - purple_connection_update_progress(gc, msg, 7, MW_CONNECT_STEPS); - break; - - case mwSession_LOGIN_ACK: - msg = _("Login Acknowledged"); - purple_connection_update_progress(gc, msg, 8, MW_CONNECT_STEPS); - break; - - case mwSession_STARTED: - msg = _("Starting Services"); - purple_connection_update_progress(gc, msg, 9, MW_CONNECT_STEPS); - - session_started(pd); - - msg = _("Connected"); - purple_connection_update_progress(gc, msg, 10, MW_CONNECT_STEPS); - purple_connection_set_state(gc, PURPLE_CONNECTED); - break; - - case mwSession_STOPPING: - - session_stopping(pd); - - if(GPOINTER_TO_UINT(info) & ERR_FAILURE) { - char *err = mwError(GPOINTER_TO_UINT(info)); - PurpleConnectionError reason; - switch (GPOINTER_TO_UINT(info)) { - case VERSION_MISMATCH: - reason = PURPLE_CONNECTION_ERROR_OTHER_ERROR; - break; - - case USER_RESTRICTED: - case INCORRECT_LOGIN: - case USER_UNREGISTERED: - case GUEST_IN_USE: - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - break; - - case ENCRYPT_MISMATCH: - case ERR_ENCRYPT_NO_SUPPORT: - case ERR_NO_COMMON_ENCRYPT: - reason = PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR; - break; - - case VERIFICATION_DOWN: - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE; - break; - - case MULTI_SERVER_LOGIN: - case MULTI_SERVER_LOGIN2: - reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE; - break; - - default: - reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - } - purple_connection_error_reason(gc, reason, err); - g_free(err); - } - break; - - case mwSession_STOPPED: - break; - - case mwSession_UNKNOWN: - default: - DEBUG_WARN("session in unknown state\n"); - } -} - - -static void mw_session_setPrivacyInfo(struct mwSession *session) { - struct mwPurplePluginData *pd; - PurpleConnection *gc; - PurpleAccount *acct; - struct mwPrivacyInfo *privacy; - GSList *l, **ll; - guint count; - - DEBUG_INFO("privacy information set from server\n"); - - g_return_if_fail(session != NULL); - - pd = mwSession_getClientData(session); - g_return_if_fail(pd != NULL); - - gc = pd->gc; - g_return_if_fail(gc != NULL); - - acct = purple_connection_get_account(gc); - g_return_if_fail(acct != NULL); - - privacy = mwSession_getPrivacyInfo(session); - count = privacy->count; - - ll = (privacy->deny)? &acct->deny: &acct->permit; - for(l = *ll; l; l = l->next) g_free(l->data); - g_slist_free(*ll); - l = *ll = NULL; - - while(count--) { - struct mwUserItem *u = privacy->users + count; - l = g_slist_prepend(l, g_strdup(u->id)); - } - *ll = l; -} - - -static void mw_session_setUserStatus(struct mwSession *session) { - struct mwPurplePluginData *pd; - PurpleConnection *gc; - struct mwAwareIdBlock idb = { mwAware_USER, NULL, NULL }; - struct mwUserStatus *stat; - - g_return_if_fail(session != NULL); - - pd = mwSession_getClientData(session); - g_return_if_fail(pd != NULL); - - gc = pd->gc; - g_return_if_fail(gc != NULL); - - idb.user = mwSession_getProperty(session, mwSession_AUTH_USER_ID); - stat = mwSession_getUserStatus(session); - - /* trigger an update of our own status if we're in the buddy list */ - mwServiceAware_setStatus(pd->srvc_aware, &idb, stat); -} - - -static void mw_session_admin(struct mwSession *session, - const char *text) { - PurpleConnection *gc; - PurpleAccount *acct; - const char *host; - const char *msg; - char *prim; - - gc = session_to_gc(session); - g_return_if_fail(gc != NULL); - - acct = purple_connection_get_account(gc); - g_return_if_fail(acct != NULL); - - host = purple_account_get_string(acct, MW_KEY_HOST, NULL); - - msg = _("A Sametime administrator has issued the following announcement" - " on server %s"); - prim = g_strdup_printf(msg, NSTR(host)); - - purple_notify_message(gc, PURPLE_NOTIFY_MSG_INFO, - _("Sametime Administrator Announcement"), - prim, text, NULL, NULL); - - g_free(prim); -} - - -/** called from read_cb, attempts to read available data from sock and - pass it to the session, passing back the return code from the read - call for handling in read_cb */ -static int read_recv(struct mwSession *session, int sock) { - guchar buf[BUF_LEN]; - int len; - - len = read(sock, buf, BUF_LEN); - if(len > 0) { - mwSession_recv(session, buf, len); - } - - return len; -} - - -/** callback triggered from purple_input_add, watches the socked for - available data to be processed by the session */ -static void read_cb(gpointer data, gint source, PurpleInputCondition cond) { - struct mwPurplePluginData *pd = data; - int ret = 0, err = 0; - - g_return_if_fail(pd != NULL); - - ret = read_recv(pd->session, pd->socket); - - /* normal operation ends here */ - if(ret > 0) return; - - /* fetch the global error value */ - err = errno; - - /* read problem occurred if we're here, so we'll need to take care of - it and clean up internal state */ - - if(pd->socket) { - close(pd->socket); - pd->socket = 0; - } - - if(pd->gc->inpa) { - purple_input_remove(pd->gc->inpa); - pd->gc->inpa = 0; - } - - if(! ret) { - DEBUG_INFO("connection reset\n"); - purple_connection_error_reason(pd->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Server closed the connection")); - - } else if(ret < 0) { - const gchar *err_str = g_strerror(err); - char *msg = NULL; - - DEBUG_INFO("error in read callback: %s\n", err_str); - - msg = g_strdup_printf(_("Lost connection with server: %s"), err_str); - purple_connection_error_reason(pd->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - msg); - g_free(msg); - } -} - - -/** Callback passed to purple_proxy_connect when an account is logged - in, and if the session logging in receives a redirect message */ -static void connect_cb(gpointer data, gint source, const gchar *error_message) { - - struct mwPurplePluginData *pd = data; - PurpleConnection *gc = pd->gc; - - if(source < 0) { - /* connection failed */ - - if(pd->socket) { - /* this is a redirect connect, force login on existing socket */ - mwSession_forceLogin(pd->session); - - } else { - /* this is a regular connect, error out */ - gchar *tmp = g_strdup_printf(_("Unable to connect: %s"), - error_message); - purple_connection_error_reason(pd->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - tmp); - g_free(tmp); - } - - return; - } - - if(pd->socket) { - /* stop any existing login attempt */ - mwSession_stop(pd->session, ERR_SUCCESS); - } - - pd->socket = source; - gc->inpa = purple_input_add(source, PURPLE_INPUT_READ, - read_cb, pd); - - mwSession_start(pd->session); -} - - -static void mw_session_announce(struct mwSession *s, - struct mwLoginInfo *from, - gboolean may_reply, - const char *text) { - struct mwPurplePluginData *pd; - PurpleAccount *acct; - PurpleConversation *conv; - PurpleBuddy *buddy; - char *who = from->user_id; - char *msg; - - pd = mwSession_getClientData(s); - acct = purple_connection_get_account(pd->gc); - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, who, acct); - if(! conv) conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, who); - - buddy = purple_find_buddy(acct, who); - if(buddy) who = (char *) purple_buddy_get_contact_alias(buddy); - - who = g_strdup_printf(_("Announcement from %s"), who); - msg = purple_markup_linkify(text); - - purple_conversation_write(conv, who, msg ? msg : "", PURPLE_MESSAGE_RECV, time(NULL)); - g_free(who); - g_free(msg); -} - - -static struct mwSessionHandler mw_session_handler = { - mw_session_io_write, - mw_session_io_close, - mw_session_clear, - mw_session_stateChange, - mw_session_setPrivacyInfo, - mw_session_setUserStatus, - mw_session_admin, - mw_session_announce, -}; - - -static void mw_aware_on_attrib(struct mwServiceAware *srvc, - struct mwAwareAttribute *attrib) { - - ; /** @todo handle server attributes. There may be some stuff we - actually want to look for, but I'm not aware of anything right - now.*/ -} - - -static void mw_aware_clear(struct mwServiceAware *srvc) { - ; /* nothing for now */ -} - - -static struct mwAwareHandler mw_aware_handler = { - mw_aware_on_attrib, - mw_aware_clear, -}; - - -static struct mwServiceAware *mw_srvc_aware_new(struct mwSession *s) { - struct mwServiceAware *srvc; - srvc = mwServiceAware_new(s, &mw_aware_handler); - return srvc; -}; - - -static void mw_conf_invited(struct mwConference *conf, - struct mwLoginInfo *inviter, - const char *invitation) { - - struct mwServiceConference *srvc; - struct mwSession *session; - struct mwPurplePluginData *pd; - PurpleConnection *gc; - - char *c_inviter, *c_name, *c_topic, *c_invitation; - GHashTable *ht; - - srvc = mwConference_getService(conf); - session = mwService_getSession(MW_SERVICE(srvc)); - pd = mwSession_getClientData(session); - gc = pd->gc; - - ht = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); - - c_inviter = g_strdup(inviter->user_id); - g_hash_table_insert(ht, CHAT_KEY_CREATOR, c_inviter); - - c_name = g_strdup(mwConference_getName(conf)); - g_hash_table_insert(ht, CHAT_KEY_NAME, c_name); - - c_topic = g_strdup(mwConference_getTitle(conf)); - g_hash_table_insert(ht, CHAT_KEY_TOPIC, c_topic); - - c_invitation = g_strdup(invitation); - g_hash_table_insert(ht, CHAT_KEY_INVITE, c_invitation); - - DEBUG_INFO("received invitation from '%s' to join ('%s','%s'): '%s'\n", - NSTR(c_inviter), NSTR(c_name), - NSTR(c_topic), NSTR(c_invitation)); - - if(! c_topic) c_topic = "(no title)"; - if(! c_invitation) c_invitation = "(no message)"; - serv_got_chat_invite(gc, c_topic, c_inviter, c_invitation, ht); -} - - -/* The following mess helps us relate a mwConference to a PurpleConvChat - in the various forms by which either may be indicated */ - -#define CONF_TO_ID(conf) (GPOINTER_TO_INT(conf)) -#define ID_TO_CONF(pd, id) (conf_find_by_id((pd), (id))) - -#define CHAT_TO_ID(chat) (purple_conv_chat_get_id(chat)) -#define ID_TO_CHAT(id) (purple_find_chat(id)) - -#define CHAT_TO_CONF(pd, chat) (ID_TO_CONF((pd), CHAT_TO_ID(chat))) -#define CONF_TO_CHAT(conf) (ID_TO_CHAT(CONF_TO_ID(conf))) - - -static struct mwConference * -conf_find_by_id(struct mwPurplePluginData *pd, int id) { - - struct mwServiceConference *srvc = pd->srvc_conf; - struct mwConference *conf = NULL; - GList *l, *ll; - - ll = mwServiceConference_getConferences(srvc); - for(l = ll; l; l = l->next) { - struct mwConference *c = l->data; - PurpleConvChat *h = mwConference_getClientData(c); - - if(CHAT_TO_ID(h) == id) { - conf = c; - break; - } - } - g_list_free(ll); - - return conf; -} - - -static void mw_conf_opened(struct mwConference *conf, GList *members) { - struct mwServiceConference *srvc; - struct mwSession *session; - struct mwPurplePluginData *pd; - PurpleConnection *gc; - PurpleConversation *g_conf; - - const char *n = mwConference_getName(conf); - const char *t = mwConference_getTitle(conf); - - DEBUG_INFO("conf %s opened, %u initial members\n", - NSTR(n), g_list_length(members)); - - srvc = mwConference_getService(conf); - session = mwService_getSession(MW_SERVICE(srvc)); - pd = mwSession_getClientData(session); - gc = pd->gc; - - if(! t) t = "(no title)"; - g_conf = serv_got_joined_chat(gc, CONF_TO_ID(conf), t); - - mwConference_setClientData(conf, PURPLE_CONV_CHAT(g_conf), NULL); - - for(; members; members = members->next) { - struct mwLoginInfo *peer = members->data; - purple_conv_chat_add_user(PURPLE_CONV_CHAT(g_conf), peer->user_id, - NULL, PURPLE_CBFLAGS_NONE, FALSE); - } -} - - -static void mw_conf_closed(struct mwConference *conf, guint32 reason) { - struct mwServiceConference *srvc; - struct mwSession *session; - struct mwPurplePluginData *pd; - PurpleConnection *gc; - - const char *n = mwConference_getName(conf); - char *msg = mwError(reason); - - DEBUG_INFO("conf %s closed, 0x%08x\n", NSTR(n), reason); - - srvc = mwConference_getService(conf); - session = mwService_getSession(MW_SERVICE(srvc)); - pd = mwSession_getClientData(session); - gc = pd->gc; - - serv_got_chat_left(gc, CONF_TO_ID(conf)); - - purple_notify_error(gc, _("Conference Closed"), NULL, msg); - g_free(msg); -} - - -static void mw_conf_peer_joined(struct mwConference *conf, - struct mwLoginInfo *peer) { - - PurpleConvChat *g_conf; - - const char *n = mwConference_getName(conf); - - DEBUG_INFO("%s joined conf %s\n", NSTR(peer->user_id), NSTR(n)); - - g_conf = mwConference_getClientData(conf); - g_return_if_fail(g_conf != NULL); - - purple_conv_chat_add_user(g_conf, peer->user_id, - NULL, PURPLE_CBFLAGS_NONE, TRUE); -} - - -static void mw_conf_peer_parted(struct mwConference *conf, - struct mwLoginInfo *peer) { - - PurpleConvChat *g_conf; - - const char *n = mwConference_getName(conf); - - DEBUG_INFO("%s left conf %s\n", NSTR(peer->user_id), NSTR(n)); - - g_conf = mwConference_getClientData(conf); - g_return_if_fail(g_conf != NULL); - - purple_conv_chat_remove_user(g_conf, peer->user_id, NULL); -} - - -static void mw_conf_text(struct mwConference *conf, - struct mwLoginInfo *who, const char *text) { - - struct mwServiceConference *srvc; - struct mwSession *session; - struct mwPurplePluginData *pd; - PurpleConnection *gc; - char *esc; - - if(! text) return; - - srvc = mwConference_getService(conf); - session = mwService_getSession(MW_SERVICE(srvc)); - pd = mwSession_getClientData(session); - gc = pd->gc; - - esc = g_markup_escape_text(text, -1); - serv_got_chat_in(gc, CONF_TO_ID(conf), who->user_id, 0, esc, time(NULL)); - g_free(esc); -} - - -static void mw_conf_typing(struct mwConference *conf, - struct mwLoginInfo *who, gboolean typing) { - - /* purple really has no good way to expose this to the user. */ - - const char *n = mwConference_getName(conf); - const char *w = who->user_id; - - if(typing) { - DEBUG_INFO("%s in conf %s: \n", NSTR(w), NSTR(n)); - - } else { - DEBUG_INFO("%s in conf %s: \n", NSTR(w), NSTR(n)); - } -} - - -static void mw_conf_clear(struct mwServiceConference *srvc) { - ; -} - - -static struct mwConferenceHandler mw_conference_handler = { - mw_conf_invited, - mw_conf_opened, - mw_conf_closed, - mw_conf_peer_joined, - mw_conf_peer_parted, - mw_conf_text, - mw_conf_typing, - mw_conf_clear, -}; - - -static struct mwServiceConference *mw_srvc_conf_new(struct mwSession *s) { - struct mwServiceConference *srvc; - srvc = mwServiceConference_new(s, &mw_conference_handler); - return srvc; -} - - -/** size of an outgoing file transfer chunk */ -#define MW_FT_LEN (BUF_LONG * 2) - - -static void ft_incoming_cancel(PurpleXfer *xfer) { - /* incoming transfer rejected or cancelled in-progress */ - struct mwFileTransfer *ft = xfer->data; - if(ft) mwFileTransfer_reject(ft); -} - - -static void ft_incoming_init(PurpleXfer *xfer) { - /* incoming transfer accepted */ - - /* - accept the mwFileTransfer - - open/create the local FILE "wb" - - stick the FILE's fp in xfer->dest_fp - */ - - struct mwFileTransfer *ft; - FILE *fp; - - ft = xfer->data; - - fp = g_fopen(xfer->local_filename, "wb"); - if(! fp) { - mwFileTransfer_cancel(ft); - return; - } - - xfer->dest_fp = fp; - mwFileTransfer_accept(ft); -} - - -static void mw_ft_offered(struct mwFileTransfer *ft) { - /* - - create a purple ft object - - offer it - */ - - struct mwServiceFileTransfer *srvc; - struct mwSession *session; - struct mwPurplePluginData *pd; - PurpleConnection *gc; - PurpleAccount *acct; - const char *who; - PurpleXfer *xfer; - - /* @todo add some safety checks */ - srvc = mwFileTransfer_getService(ft); - session = mwService_getSession(MW_SERVICE(srvc)); - pd = mwSession_getClientData(session); - gc = pd->gc; - acct = purple_connection_get_account(gc); - - who = mwFileTransfer_getUser(ft)->user; - - DEBUG_INFO("file transfer %p offered\n", ft); - DEBUG_INFO(" from: %s\n", NSTR(who)); - DEBUG_INFO(" file: %s\n", NSTR(mwFileTransfer_getFileName(ft))); - DEBUG_INFO(" size: %u\n", mwFileTransfer_getFileSize(ft)); - DEBUG_INFO(" text: %s\n", NSTR(mwFileTransfer_getMessage(ft))); - - xfer = purple_xfer_new(acct, PURPLE_XFER_RECEIVE, who); - if (xfer) - { - purple_xfer_ref(xfer); - mwFileTransfer_setClientData(ft, xfer, (GDestroyNotify) purple_xfer_unref); - xfer->data = ft; - - purple_xfer_set_init_fnc(xfer, ft_incoming_init); - purple_xfer_set_cancel_recv_fnc(xfer, ft_incoming_cancel); - purple_xfer_set_request_denied_fnc(xfer, ft_incoming_cancel); - - purple_xfer_set_filename(xfer, mwFileTransfer_getFileName(ft)); - purple_xfer_set_size(xfer, mwFileTransfer_getFileSize(ft)); - purple_xfer_set_message(xfer, mwFileTransfer_getMessage(ft)); - - purple_xfer_request(xfer); - } -} - - -static void ft_send(struct mwFileTransfer *ft, FILE *fp) { - guchar buf[MW_FT_LEN]; - struct mwOpaque o = { MW_FT_LEN, buf }; - guint32 rem; - PurpleXfer *xfer; - - xfer = mwFileTransfer_getClientData(ft); - - rem = mwFileTransfer_getRemaining(ft); - if(rem < MW_FT_LEN) o.len = rem; - - if (fread(buf, (size_t)o.len, 1, fp) == 1) { - - /* calculate progress and display it */ - xfer->bytes_sent += o.len; - xfer->bytes_remaining -= o.len; - purple_xfer_update_progress(xfer); - - mwFileTransfer_send(ft, &o); - - } else { - int err = errno; - DEBUG_WARN("problem reading from file %s: %s\n", - NSTR(mwFileTransfer_getFileName(ft)), g_strerror(err)); - - mwFileTransfer_cancel(ft); - } -} - - -static void mw_ft_opened(struct mwFileTransfer *ft) { - /* - - get purple ft from client data in ft - - set the state to active - */ - - PurpleXfer *xfer; - - xfer = mwFileTransfer_getClientData(ft); - - if(! xfer) { - mwFileTransfer_cancel(ft); - mwFileTransfer_free(ft); - g_return_if_reached(); - } - - if(purple_xfer_get_type(xfer) == PURPLE_XFER_SEND) { - xfer->dest_fp = g_fopen(xfer->local_filename, "rb"); - if (xfer->dest_fp) - ft_send(ft, xfer->dest_fp); - } -} - - -static void mw_ft_closed(struct mwFileTransfer *ft, guint32 code) { - /* - - get purple ft from client data in ft - - indicate rejection/cancelation/completion - - free the file transfer itself - */ - - PurpleXfer *xfer; - - xfer = mwFileTransfer_getClientData(ft); - if(xfer) { - xfer->data = NULL; - - if(! mwFileTransfer_getRemaining(ft)) { - purple_xfer_set_completed(xfer, TRUE); - purple_xfer_end(xfer); - - } else if(mwFileTransfer_isCancelLocal(ft)) { - /* calling purple_xfer_cancel_local is redundant, since that's - probably what triggered this function to be called */ - ; - - } else if(mwFileTransfer_isCancelRemote(ft)) { - /* steal the reference for the xfer */ - mwFileTransfer_setClientData(ft, NULL, NULL); - purple_xfer_cancel_remote(xfer); - - /* drop the stolen reference */ - purple_xfer_unref(xfer); - return; - } - } - - mwFileTransfer_free(ft); -} - - -static void mw_ft_recv(struct mwFileTransfer *ft, - struct mwOpaque *data) { - /* - - get purple ft from client data in ft - - update transfered percentage - - if done, destroy the ft, disassociate from purple ft - */ - - PurpleXfer *xfer; - FILE *fp; - size_t wc; - - xfer = mwFileTransfer_getClientData(ft); - g_return_if_fail(xfer != NULL); - - fp = xfer->dest_fp; - g_return_if_fail(fp != NULL); - - /* we must collect and save our precious data */ - wc = fwrite(data->data, 1, data->len, fp); - if (wc != data->len) { - DEBUG_ERROR("failed to write data\n"); - purple_xfer_cancel_local(xfer); - return; - } - - /* update the progress */ - xfer->bytes_sent += data->len; - xfer->bytes_remaining -= data->len; - purple_xfer_update_progress(xfer); - - /* let the other side know we got it, and to send some more */ - mwFileTransfer_ack(ft); -} - - -static void mw_ft_ack(struct mwFileTransfer *ft) { - PurpleXfer *xfer; - - xfer = mwFileTransfer_getClientData(ft); - g_return_if_fail(xfer != NULL); - g_return_if_fail(xfer->watcher == 0); - - if(! mwFileTransfer_getRemaining(ft)) { - purple_xfer_set_completed(xfer, TRUE); - purple_xfer_end(xfer); - - } else if(mwFileTransfer_isOpen(ft)) { - ft_send(ft, xfer->dest_fp); - } -} - - -static void mw_ft_clear(struct mwServiceFileTransfer *srvc) { - ; -} - - -static struct mwFileTransferHandler mw_ft_handler = { - mw_ft_offered, - mw_ft_opened, - mw_ft_closed, - mw_ft_recv, - mw_ft_ack, - mw_ft_clear, -}; - - -static struct mwServiceFileTransfer *mw_srvc_ft_new(struct mwSession *s) { - struct mwServiceFileTransfer *srvc; - GHashTable *ft_map; - - ft_map = g_hash_table_new(g_direct_hash, g_direct_equal); - - srvc = mwServiceFileTransfer_new(s, &mw_ft_handler); - mwService_setClientData(MW_SERVICE(srvc), ft_map, - (GDestroyNotify) g_hash_table_destroy); - - return srvc; -} - - -static void convo_data_free(struct convo_data *cd) { - GList *l; - - /* clean the queue */ - for(l = cd->queue; l; l = g_list_delete_link(l, l)) { - struct convo_msg *m = l->data; - if(m->clear) m->clear(m->data); - g_free(m); - } - - g_free(cd); -} - - -/** allocates a convo_data structure and associates it with the - conversation in the client data slot */ -static void convo_data_new(struct mwConversation *conv) { - struct convo_data *cd; - - g_return_if_fail(conv != NULL); - - if(mwConversation_getClientData(conv)) - return; - - cd = g_new0(struct convo_data, 1); - cd->conv = conv; - - mwConversation_setClientData(conv, cd, (GDestroyNotify) convo_data_free); -} - - -static PurpleConversation *convo_get_gconv(struct mwConversation *conv) { - struct mwServiceIm *srvc; - struct mwSession *session; - struct mwPurplePluginData *pd; - PurpleConnection *gc; - PurpleAccount *acct; - - struct mwIdBlock *idb; - - srvc = mwConversation_getService(conv); - session = mwService_getSession(MW_SERVICE(srvc)); - pd = mwSession_getClientData(session); - gc = pd->gc; - acct = purple_connection_get_account(gc); - - idb = mwConversation_getTarget(conv); - - return purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, - idb->user, acct); -} - - -static void convo_queue(struct mwConversation *conv, - enum mwImSendType type, gconstpointer data) { - - struct convo_data *cd; - struct convo_msg *m; - - convo_data_new(conv); - cd = mwConversation_getClientData(conv); - - m = g_new0(struct convo_msg, 1); - m->type = type; - - switch(type) { - case mwImSend_PLAIN: - m->data = g_strdup(data); - m->clear = g_free; - break; - - case mwImSend_TYPING: - default: - m->data = (gpointer) data; - m->clear = NULL; - } - - cd->queue = g_list_append(cd->queue, m); -} - - -/* Does what it takes to get an error displayed for a conversation */ -static void convo_error(struct mwConversation *conv, guint32 err) { - PurpleConversation *gconv; - char *tmp, *text; - struct mwIdBlock *idb; - - idb = mwConversation_getTarget(conv); - - tmp = mwError(err); - text = g_strconcat(_("Unable to send message: "), tmp, NULL); - - gconv = convo_get_gconv(conv); - if(gconv && !purple_conv_present_error(idb->user, gconv->account, text)) { - - g_free(text); - text = g_strdup_printf(_("Unable to send message to %s:"), - (idb->user)? idb->user: "(unknown)"); - purple_notify_error(purple_account_get_connection(gconv->account), - NULL, text, tmp); - } - - g_free(tmp); - g_free(text); -} - - -static void convo_queue_send(struct mwConversation *conv) { - struct convo_data *cd; - GList *l; - - cd = mwConversation_getClientData(conv); - - for(l = cd->queue; l; l = g_list_delete_link(l, l)) { - struct convo_msg *m = l->data; - - mwConversation_send(conv, m->type, m->data); - - if(m->clear) m->clear(m->data); - g_free(m); - } - - cd->queue = NULL; -} - - -/** called when a mw conversation leaves a purple conversation to - inform the purple conversation that it's unsafe to offer any *cool* - features. */ -static void convo_nofeatures(struct mwConversation *conv) { - PurpleConversation *gconv; - PurpleConnection *gc; - - gconv = convo_get_gconv(conv); - if(! gconv) return; - - gc = purple_conversation_get_gc(gconv); - if(! gc) return; - - purple_conversation_set_features(gconv, gc->flags); -} - - -/** called when a mw conversation and purple conversation come together, - to inform the purple conversation of what features to offer the - user */ -static void convo_features(struct mwConversation *conv) { - PurpleConversation *gconv; - PurpleConnectionFlags feat; - - gconv = convo_get_gconv(conv); - if(! gconv) return; - - feat = purple_conversation_get_features(gconv); - - if(mwConversation_isOpen(conv)) { - if(mwConversation_supports(conv, mwImSend_HTML)) { - feat |= PURPLE_CONNECTION_HTML; - } else { - feat &= ~PURPLE_CONNECTION_HTML; - } - - if(mwConversation_supports(conv, mwImSend_MIME)) { - feat &= ~PURPLE_CONNECTION_NO_IMAGES; - } else { - feat |= PURPLE_CONNECTION_NO_IMAGES; - } - - DEBUG_INFO("conversation features set to 0x%04x\n", feat); - purple_conversation_set_features(gconv, feat); - - } else { - convo_nofeatures(conv); - } -} - - -static void mw_conversation_opened(struct mwConversation *conv) { - struct mwServiceIm *srvc; - struct mwSession *session; - struct mwPurplePluginData *pd; - PurpleConnection *gc; - PurpleAccount *acct; - - struct convo_dat *cd; - - srvc = mwConversation_getService(conv); - session = mwService_getSession(MW_SERVICE(srvc)); - pd = mwSession_getClientData(session); - gc = pd->gc; - acct = purple_connection_get_account(gc); - - /* set up the queue */ - cd = mwConversation_getClientData(conv); - if(cd) { - convo_queue_send(conv); - - if(! convo_get_gconv(conv)) { - mwConversation_free(conv); - return; - } - - } else { - convo_data_new(conv); - } - - { /* record the client key for the buddy */ - PurpleBuddy *buddy; - struct mwLoginInfo *info; - info = mwConversation_getTargetInfo(conv); - - buddy = purple_find_buddy(acct, info->user_id); - if(buddy) { - purple_blist_node_set_int((PurpleBlistNode *) buddy, - BUDDY_KEY_CLIENT, info->type); - } - } - - convo_features(conv); -} - - -static void mw_conversation_closed(struct mwConversation *conv, - guint32 reason) { - - struct convo_data *cd; - - g_return_if_fail(conv != NULL); - - /* if there's an error code and a non-typing message in the queue, - print an error message to the conversation */ - cd = mwConversation_getClientData(conv); - if(reason && cd && cd->queue) { - GList *l; - for(l = cd->queue; l; l = l->next) { - struct convo_msg *m = l->data; - if(m->type != mwImSend_TYPING) { - convo_error(conv, reason); - break; - } - } - } - -#if 0 - /* don't do this, to prevent the occasional weird sending of - formatted messages as plaintext when the other end closes the - conversation after we've begun composing the message */ - convo_nofeatures(conv); -#endif - - mwConversation_removeClientData(conv); -} - - -static void im_recv_text(struct mwConversation *conv, - struct mwPurplePluginData *pd, - const char *msg) { - - struct mwIdBlock *idb; - char *txt, *esc; - const char *t; - - idb = mwConversation_getTarget(conv); - - txt = purple_utf8_try_convert(msg); - t = txt? txt: msg; - - esc = g_markup_escape_text(t, -1); - serv_got_im(pd->gc, idb->user, esc, 0, time(NULL)); - g_free(esc); - - g_free(txt); -} - - -static void im_recv_typing(struct mwConversation *conv, - struct mwPurplePluginData *pd, - gboolean typing) { - - struct mwIdBlock *idb; - idb = mwConversation_getTarget(conv); - - serv_got_typing(pd->gc, idb->user, 0, - typing? PURPLE_TYPING: PURPLE_NOT_TYPING); -} - - -static void im_recv_html(struct mwConversation *conv, - struct mwPurplePluginData *pd, - const char *msg) { - struct mwIdBlock *idb; - char *t1, *t2; - const char *t; - - idb = mwConversation_getTarget(conv); - - /* ensure we're receiving UTF8 */ - t1 = purple_utf8_try_convert(msg); - t = t1? t1: msg; - - /* convert entities to UTF8 so they'll log correctly */ - t2 = purple_utf8_ncr_decode(t); - t = t2? t2: t; - - serv_got_im(pd->gc, idb->user, t, 0, time(NULL)); - - g_free(t1); - g_free(t2); -} - - -static void im_recv_subj(struct mwConversation *conv, - struct mwPurplePluginData *pd, - const char *subj) { - - /** @todo somehow indicate receipt of a conversation subject. It - would also be nice if we added a /topic command for the - protocol */ - ; -} - - -/** generate "cid:908@20582notesbuddy" from "<908@20582notesbuddy>" */ -static char *make_cid(const char *cid) { - gsize n; - char *c, *d; - - g_return_val_if_fail(cid != NULL, NULL); - - n = strlen(cid); - g_return_val_if_fail(n > 2, NULL); - - c = g_strndup(cid+1, n-2); - d = g_strdup_printf("cid:%s", c); - - g_free(c); - return d; -} - - -static void im_recv_mime(struct mwConversation *conv, - struct mwPurplePluginData *pd, - const char *data) { - - GHashTable *img_by_cid; - GList *images; - - GString *str; - - PurpleMimeDocument *doc; - GList *parts; - - img_by_cid = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); - images = NULL; - - /* don't want the contained string to ever be NULL */ - str = g_string_new(""); - - doc = purple_mime_document_parse(data); - - /* handle all the MIME parts */ - parts = purple_mime_document_get_parts(doc); - for(; parts; parts = parts->next) { - PurpleMimePart *part = parts->data; - const char *type; - - type = purple_mime_part_get_field(part, "content-type"); - DEBUG_INFO("MIME part Content-Type: %s\n", NSTR(type)); - - if(! type) { - ; /* feh */ - - } else if(purple_str_has_prefix(type, "image")) { - /* put images into the image store */ - - guchar *d_dat; - gsize d_len; - char *cid; - int img; - - /* obtain and unencode the data */ - purple_mime_part_get_data_decoded(part, &d_dat, &d_len); - - /* look up the content id */ - cid = (char *) purple_mime_part_get_field(part, "Content-ID"); - cid = make_cid(cid); - - /* add image to the purple image store */ - img = purple_imgstore_add_with_id(d_dat, d_len, cid); - - /* map the cid to the image store identifier */ - g_hash_table_insert(img_by_cid, cid, GINT_TO_POINTER(img)); - - /* recall the image for dereferencing later */ - images = g_list_append(images, GINT_TO_POINTER(img)); - - } else if(purple_str_has_prefix(type, "text")) { - - /* concatenate all the text parts together */ - guchar *data; - gsize len; - - purple_mime_part_get_data_decoded(part, &data, &len); - g_string_append(str, (const char *)data); - g_free(data); - } - } - - purple_mime_document_free(doc); - - /* @todo should put this in its own function */ - { /* replace each IMG tag's SRC attribute with an ID attribute. This - actually modifies the contents of str */ - GData *attribs; - char *start, *end; - char *tmp = str->str; - - while(*tmp && purple_markup_find_tag("img", tmp, (const char **) &start, - (const char **) &end, &attribs)) { - - char *alt, *align, *border, *src; - int img = 0; - - alt = g_datalist_get_data(&attribs, "alt"); - align = g_datalist_get_data(&attribs, "align"); - border = g_datalist_get_data(&attribs, "border"); - src = g_datalist_get_data(&attribs, "src"); - - if(src) - img = GPOINTER_TO_INT(g_hash_table_lookup(img_by_cid, src)); - - if(img) { - GString *atstr; - gsize len = (end - start); - gsize mov; - - atstr = g_string_new(""); - if(alt) g_string_append_printf(atstr, " alt=\"%s\"", alt); - if(align) g_string_append_printf(atstr, " align=\"%s\"", align); - if(border) g_string_append_printf(atstr, " border=\"%s\"", border); - - mov = g_snprintf(start, len, "str, img); - while(mov < len) start[mov++] = ' '; - - g_string_free(atstr, TRUE); - } - - g_datalist_clear(&attribs); - tmp = end + 1; - } - } - - im_recv_html(conv, pd, str->str); - - g_string_free(str, TRUE); - - /* clean up the cid table */ - g_hash_table_destroy(img_by_cid); - - /* dereference all the imgages */ - while(images) { - purple_imgstore_unref_by_id(GPOINTER_TO_INT(images->data)); - images = g_list_delete_link(images, images); - } -} - - -static void mw_conversation_recv(struct mwConversation *conv, - enum mwImSendType type, - gconstpointer msg) { - struct mwServiceIm *srvc; - struct mwSession *session; - struct mwPurplePluginData *pd; - - srvc = mwConversation_getService(conv); - session = mwService_getSession(MW_SERVICE(srvc)); - pd = mwSession_getClientData(session); - - switch(type) { - case mwImSend_PLAIN: - im_recv_text(conv, pd, msg); - break; - - case mwImSend_TYPING: - im_recv_typing(conv, pd, !! msg); - break; - - case mwImSend_HTML: - im_recv_html(conv, pd, msg); - break; - - case mwImSend_SUBJECT: - im_recv_subj(conv, pd, msg); - break; - - case mwImSend_MIME: - im_recv_mime(conv, pd, msg); - break; - - default: - DEBUG_INFO("conversation received strange type, 0x%04x\n", type); - ; /* erm... */ - } -} - - -static void mw_place_invite(struct mwConversation *conv, - const char *message, - const char *title, const char *name) { - struct mwServiceIm *srvc; - struct mwSession *session; - struct mwPurplePluginData *pd; - - struct mwIdBlock *idb; - GHashTable *ht; - - srvc = mwConversation_getService(conv); - session = mwService_getSession(MW_SERVICE(srvc)); - pd = mwSession_getClientData(session); - - idb = mwConversation_getTarget(conv); - - ht = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); - g_hash_table_insert(ht, CHAT_KEY_CREATOR, g_strdup(idb->user)); - g_hash_table_insert(ht, CHAT_KEY_NAME, g_strdup(name)); - g_hash_table_insert(ht, CHAT_KEY_TOPIC, g_strdup(title)); - g_hash_table_insert(ht, CHAT_KEY_INVITE, g_strdup(message)); - g_hash_table_insert(ht, CHAT_KEY_IS_PLACE, g_strdup("")); /* ugh */ - - if(! title) title = "(no title)"; - if(! message) message = "(no message)"; - serv_got_chat_invite(pd->gc, title, idb->user, message, ht); - - mwConversation_close(conv, ERR_SUCCESS); - mwConversation_free(conv); -} - - -static void mw_im_clear(struct mwServiceIm *srvc) { - ; -} - - -static struct mwImHandler mw_im_handler = { - mw_conversation_opened, - mw_conversation_closed, - mw_conversation_recv, - mw_place_invite, - mw_im_clear, -}; - - -static struct mwServiceIm *mw_srvc_im_new(struct mwSession *s) { - struct mwServiceIm *srvc; - srvc = mwServiceIm_new(s, &mw_im_handler); - mwServiceIm_setClientType(srvc, mwImClient_NOTESBUDDY); - return srvc; -} - - -/* The following helps us relate a mwPlace to a PurpleConvChat in the - various forms by which either may be indicated. Uses some of - the similar macros from the conference service above */ - -#define PLACE_TO_ID(place) (GPOINTER_TO_INT(place)) -#define ID_TO_PLACE(pd, id) (place_find_by_id((pd), (id))) - -#define CHAT_TO_PLACE(pd, chat) (ID_TO_PLACE((pd), CHAT_TO_ID(chat))) -#define PLACE_TO_CHAT(place) (ID_TO_CHAT(PLACE_TO_ID(place))) - - -static struct mwPlace * -place_find_by_id(struct mwPurplePluginData *pd, int id) { - struct mwServicePlace *srvc = pd->srvc_place; - struct mwPlace *place = NULL; - GList *l; - - l = (GList *) mwServicePlace_getPlaces(srvc); - for(; l; l = l->next) { - struct mwPlace *p = l->data; - PurpleConvChat *h = PURPLE_CONV_CHAT(mwPlace_getClientData(p)); - - if(CHAT_TO_ID(h) == id) { - place = p; - break; - } - } - - return place; -} - - -static void mw_place_opened(struct mwPlace *place) { - struct mwServicePlace *srvc; - struct mwSession *session; - struct mwPurplePluginData *pd; - PurpleConnection *gc; - PurpleConversation *gconf; - - GList *members, *l; - - const char *n = mwPlace_getName(place); - const char *t = mwPlace_getTitle(place); - - srvc = mwPlace_getService(place); - session = mwService_getSession(MW_SERVICE(srvc)); - pd = mwSession_getClientData(session); - gc = pd->gc; - - members = mwPlace_getMembers(place); - - DEBUG_INFO("place %s opened, %u initial members\n", - NSTR(n), g_list_length(members)); - - if(! t) t = "(no title)"; - gconf = serv_got_joined_chat(gc, PLACE_TO_ID(place), t); - - mwPlace_setClientData(place, gconf, NULL); - - for(l = members; l; l = l->next) { - struct mwIdBlock *idb = l->data; - purple_conv_chat_add_user(PURPLE_CONV_CHAT(gconf), idb->user, - NULL, PURPLE_CBFLAGS_NONE, FALSE); - } - g_list_free(members); -} - - -static void mw_place_closed(struct mwPlace *place, guint32 code) { - struct mwServicePlace *srvc; - struct mwSession *session; - struct mwPurplePluginData *pd; - PurpleConnection *gc; - - const char *n = mwPlace_getName(place); - char *msg = mwError(code); - - DEBUG_INFO("place %s closed, 0x%08x\n", NSTR(n), code); - - srvc = mwPlace_getService(place); - session = mwService_getSession(MW_SERVICE(srvc)); - pd = mwSession_getClientData(session); - gc = pd->gc; - - serv_got_chat_left(gc, PLACE_TO_ID(place)); - - purple_notify_error(gc, _("Place Closed"), NULL, msg); - g_free(msg); -} - - -static void mw_place_peerJoined(struct mwPlace *place, - const struct mwIdBlock *peer) { - PurpleConversation *gconf; - - const char *n = mwPlace_getName(place); - - DEBUG_INFO("%s joined place %s\n", NSTR(peer->user), NSTR(n)); - - gconf = mwPlace_getClientData(place); - g_return_if_fail(gconf != NULL); - - purple_conv_chat_add_user(PURPLE_CONV_CHAT(gconf), peer->user, - NULL, PURPLE_CBFLAGS_NONE, TRUE); -} - - -static void mw_place_peerParted(struct mwPlace *place, - const struct mwIdBlock *peer) { - PurpleConversation *gconf; - - const char *n = mwPlace_getName(place); - - DEBUG_INFO("%s left place %s\n", NSTR(peer->user), NSTR(n)); - - gconf = mwPlace_getClientData(place); - g_return_if_fail(gconf != NULL); - - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(gconf), peer->user, NULL); -} - - -static void mw_place_peerSetAttribute(struct mwPlace *place, - const struct mwIdBlock *peer, - guint32 attr, struct mwOpaque *o) { - ; -} - - -static void mw_place_peerUnsetAttribute(struct mwPlace *place, - const struct mwIdBlock *peer, - guint32 attr) { - ; -} - - -static void mw_place_message(struct mwPlace *place, - const struct mwIdBlock *who, - const char *msg) { - struct mwServicePlace *srvc; - struct mwSession *session; - struct mwPurplePluginData *pd; - PurpleConnection *gc; - char *esc; - - if(! msg) return; - - srvc = mwPlace_getService(place); - session = mwService_getSession(MW_SERVICE(srvc)); - pd = mwSession_getClientData(session); - gc = pd->gc; - - esc = g_markup_escape_text(msg, -1); - serv_got_chat_in(gc, PLACE_TO_ID(place), who->user, 0, esc, time(NULL)); - g_free(esc); -} - - -static void mw_place_clear(struct mwServicePlace *srvc) { - ; -} - - -static struct mwPlaceHandler mw_place_handler = { - mw_place_opened, - mw_place_closed, - mw_place_peerJoined, - mw_place_peerParted, - mw_place_peerSetAttribute, - mw_place_peerUnsetAttribute, - mw_place_message, - mw_place_clear, -}; - - -static struct mwServicePlace *mw_srvc_place_new(struct mwSession *s) { - struct mwServicePlace *srvc; - srvc = mwServicePlace_new(s, &mw_place_handler); - return srvc; -} - - -static struct mwServiceResolve *mw_srvc_resolve_new(struct mwSession *s) { - struct mwServiceResolve *srvc; - srvc = mwServiceResolve_new(s); - return srvc; -} - - -static struct mwServiceStorage *mw_srvc_store_new(struct mwSession *s) { - struct mwServiceStorage *srvc; - srvc = mwServiceStorage_new(s); - return srvc; -} - - -/** allocate and associate a mwPurplePluginData with a PurpleConnection */ -static struct mwPurplePluginData *mwPurplePluginData_new(PurpleConnection *gc) { - struct mwPurplePluginData *pd; - - g_return_val_if_fail(gc != NULL, NULL); - - pd = g_new0(struct mwPurplePluginData, 1); - pd->gc = gc; - pd->session = mwSession_new(&mw_session_handler); - pd->srvc_aware = mw_srvc_aware_new(pd->session); - pd->srvc_conf = mw_srvc_conf_new(pd->session); - pd->srvc_ft = mw_srvc_ft_new(pd->session); - pd->srvc_im = mw_srvc_im_new(pd->session); - pd->srvc_place = mw_srvc_place_new(pd->session); - pd->srvc_resolve = mw_srvc_resolve_new(pd->session); - pd->srvc_store = mw_srvc_store_new(pd->session); - pd->group_list_map = g_hash_table_new(g_direct_hash, g_direct_equal); - pd->sock_buf = purple_circ_buffer_new(0); - - mwSession_addService(pd->session, MW_SERVICE(pd->srvc_aware)); - mwSession_addService(pd->session, MW_SERVICE(pd->srvc_conf)); - mwSession_addService(pd->session, MW_SERVICE(pd->srvc_ft)); - mwSession_addService(pd->session, MW_SERVICE(pd->srvc_im)); - mwSession_addService(pd->session, MW_SERVICE(pd->srvc_place)); - mwSession_addService(pd->session, MW_SERVICE(pd->srvc_resolve)); - mwSession_addService(pd->session, MW_SERVICE(pd->srvc_store)); - - mwSession_addCipher(pd->session, mwCipher_new_RC2_40(pd->session)); - mwSession_addCipher(pd->session, mwCipher_new_RC2_128(pd->session)); - - mwSession_setClientData(pd->session, pd, NULL); - gc->proto_data = pd; - - return pd; -} - - -static void mwPurplePluginData_free(struct mwPurplePluginData *pd) { - g_return_if_fail(pd != NULL); - - pd->gc->proto_data = NULL; - - mwSession_removeService(pd->session, mwService_AWARE); - mwSession_removeService(pd->session, mwService_CONFERENCE); - mwSession_removeService(pd->session, mwService_FILE_TRANSFER); - mwSession_removeService(pd->session, mwService_IM); - mwSession_removeService(pd->session, mwService_PLACE); - mwSession_removeService(pd->session, mwService_RESOLVE); - mwSession_removeService(pd->session, mwService_STORAGE); - - mwService_free(MW_SERVICE(pd->srvc_aware)); - mwService_free(MW_SERVICE(pd->srvc_conf)); - mwService_free(MW_SERVICE(pd->srvc_ft)); - mwService_free(MW_SERVICE(pd->srvc_im)); - mwService_free(MW_SERVICE(pd->srvc_place)); - mwService_free(MW_SERVICE(pd->srvc_resolve)); - mwService_free(MW_SERVICE(pd->srvc_store)); - - mwCipher_free(mwSession_getCipher(pd->session, mwCipher_RC2_40)); - mwCipher_free(mwSession_getCipher(pd->session, mwCipher_RC2_128)); - - mwSession_free(pd->session); - - g_hash_table_destroy(pd->group_list_map); - purple_circ_buffer_destroy(pd->sock_buf); - - g_free(pd); -} - - -static const char *mw_prpl_list_icon(PurpleAccount *a, PurpleBuddy *b) { - /* my little green dude is a chopped up version of the aim running - guy. First, cut off the head and store someplace safe. Then, - take the left-half side of the body and throw it away. Make a - copy of the remaining body, and flip it horizontally. Now attach - the two pieces into an X shape, and drop the head back on the - top, being careful to center it. Then, just change the color - saturation to bring the red down a bit, and voila! */ - - /* then, throw all of that away and use sodipodi to make a new - icon. You know, LIKE A REAL MAN. */ - - return "meanwhile"; -} - - -static const char* mw_prpl_list_emblem(PurpleBuddy *b) -{ - if(buddy_is_external(b)) - return "external"; - - return NULL; -} - - -static char *mw_prpl_status_text(PurpleBuddy *b) { - PurpleConnection *gc; - struct mwPurplePluginData *pd; - struct mwAwareIdBlock t = { mwAware_USER, (char *)purple_buddy_get_name(b), NULL }; - const char *ret = NULL; - - if ((gc = purple_account_get_connection(purple_buddy_get_account(b))) - && (pd = gc->proto_data)) - ret = mwServiceAware_getText(pd->srvc_aware, &t); - - return (ret && g_utf8_validate(ret, -1, NULL)) ? g_markup_escape_text(ret, -1): NULL; -} - - -static const char *status_text(PurpleBuddy *b) { - PurplePresence *presence; - PurpleStatus *status; - - presence = purple_buddy_get_presence(b); - status = purple_presence_get_active_status(presence); - - return purple_status_get_name(status); -} - - -static gboolean user_supports(struct mwServiceAware *srvc, - const char *who, guint32 feature) { - - const struct mwAwareAttribute *attr; - struct mwAwareIdBlock idb = { mwAware_USER, (char *) who, NULL }; - - attr = mwServiceAware_getAttribute(srvc, &idb, feature); - return (attr != NULL) && mwAwareAttribute_asBoolean(attr); -} - - -static char *user_supports_text(struct mwServiceAware *srvc, const char *who) { - const char *feat[] = {NULL, NULL, NULL, NULL, NULL}; - const char **f = feat; - - if(user_supports(srvc, who, mwAttribute_AV_PREFS_SET)) { - gboolean mic, speak, video; - - mic = user_supports(srvc, who, mwAttribute_MICROPHONE); - speak = user_supports(srvc, who, mwAttribute_SPEAKERS); - video = user_supports(srvc, who, mwAttribute_VIDEO_CAMERA); - - if(mic) *f++ = _("Microphone"); - if(speak) *f++ = _("Speakers"); - if(video) *f++ = _("Video Camera"); - } - - if(user_supports(srvc, who, mwAttribute_FILE_TRANSFER)) - *f++ = _("File Transfer"); - - return (*feat)? g_strjoinv(", ", (char **)feat): NULL; - /* jenni loves siege */ -} - - -static void mw_prpl_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) { - PurpleConnection *gc; - struct mwPurplePluginData *pd = NULL; - struct mwAwareIdBlock idb = { mwAware_USER, (char *)purple_buddy_get_name(b), NULL }; - - const char *message = NULL; - const char *status; - char *tmp; - - if ((gc = purple_account_get_connection(purple_buddy_get_account(b))) - && (pd = gc->proto_data)) - message = mwServiceAware_getText(pd->srvc_aware, &idb); - - status = status_text(b); - - if(message != NULL && g_utf8_validate(message, -1, NULL) && purple_utf8_strcasecmp(status, message)) { - tmp = g_markup_escape_text(message, -1); - purple_notify_user_info_add_pair(user_info, status, tmp); - g_free(tmp); - - } else { - purple_notify_user_info_add_pair(user_info, _("Status"), status); - } - - if(full && pd != NULL) { - tmp = user_supports_text(pd->srvc_aware, purple_buddy_get_name(b)); - if(tmp) { - purple_notify_user_info_add_pair(user_info, _("Supports"), tmp); - g_free(tmp); - } - - if(buddy_is_external(b)) { - purple_notify_user_info_add_pair(user_info, NULL, _("External User")); - } - } -} - -static GList *mw_prpl_status_types(PurpleAccount *acct) -{ - GList *types = NULL; - PurpleStatusType *type; - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, - MW_STATE_ACTIVE, NULL, TRUE, TRUE, FALSE, - MW_STATE_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING), - NULL); - types = g_list_append(types, type); - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, - MW_STATE_AWAY, NULL, TRUE, TRUE, FALSE, - MW_STATE_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING), - NULL); - types = g_list_append(types, type); - - type = purple_status_type_new_with_attrs(PURPLE_STATUS_UNAVAILABLE, - MW_STATE_BUSY, _("Do Not Disturb"), TRUE, TRUE, FALSE, - MW_STATE_MESSAGE, _("Message"), purple_value_new(PURPLE_TYPE_STRING), - NULL); - types = g_list_append(types, type); - - type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, - MW_STATE_OFFLINE, NULL, TRUE, TRUE, FALSE); - types = g_list_append(types, type); - - return types; -} - - -static void conf_create_prompt_cancel(PurpleBuddy *buddy, - PurpleRequestFields *fields) { - ; /* nothing to do */ -} - - -static void conf_create_prompt_join(PurpleBuddy *buddy, - PurpleRequestFields *fields) { - PurpleAccount *acct; - PurpleConnection *gc; - struct mwPurplePluginData *pd; - struct mwServiceConference *srvc; - - PurpleRequestField *f; - - const char *topic, *invite; - struct mwConference *conf; - struct mwIdBlock idb = { NULL, NULL }; - - acct = purple_buddy_get_account(buddy); - gc = purple_account_get_connection(acct); - pd = gc->proto_data; - srvc = pd->srvc_conf; - - f = purple_request_fields_get_field(fields, CHAT_KEY_TOPIC); - topic = purple_request_field_string_get_value(f); - - f = purple_request_fields_get_field(fields, CHAT_KEY_INVITE); - invite = purple_request_field_string_get_value(f); - - conf = mwConference_new(srvc, topic); - mwConference_open(conf); - - idb.user = (char *)purple_buddy_get_name(buddy); - mwConference_invite(conf, &idb, invite); -} - - -static void blist_menu_conf_create(PurpleBuddy *buddy, const char *msg) { - - PurpleRequestFields *fields; - PurpleRequestFieldGroup *g; - PurpleRequestField *f; - - PurpleAccount *acct; - PurpleConnection *gc; - - const char *msgA; - const char *msgB; - char *msg1; - - g_return_if_fail(buddy != NULL); - - acct = purple_buddy_get_account(buddy); - g_return_if_fail(acct != NULL); - - gc = purple_account_get_connection(acct); - g_return_if_fail(gc != NULL); - - fields = purple_request_fields_new(); - - g = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, g); - - f = purple_request_field_string_new(CHAT_KEY_TOPIC, _("Topic"), NULL, FALSE); - purple_request_field_group_add_field(g, f); - - f = purple_request_field_string_new(CHAT_KEY_INVITE, _("Message"), msg, FALSE); - purple_request_field_group_add_field(g, f); - - msgA = _("Create conference with user"); - msgB = _("Please enter a topic for the new conference, and an invitation" - " message to be sent to %s"); - msg1 = g_strdup_printf(msgB, purple_buddy_get_name(buddy)); - - purple_request_fields(gc, _("New Conference"), - msgA, msg1, fields, - _("Create"), G_CALLBACK(conf_create_prompt_join), - _("Cancel"), G_CALLBACK(conf_create_prompt_cancel), - acct, purple_buddy_get_name(buddy), NULL, - buddy); - g_free(msg1); -} - - -static void conf_select_prompt_cancel(PurpleBuddy *buddy, - PurpleRequestFields *fields) { - ; -} - - -static void conf_select_prompt_invite(PurpleBuddy *buddy, - PurpleRequestFields *fields) { - PurpleRequestField *f; - GList *l; - const char *msg; - - f = purple_request_fields_get_field(fields, CHAT_KEY_INVITE); - msg = purple_request_field_string_get_value(f); - - f = purple_request_fields_get_field(fields, "conf"); - l = purple_request_field_list_get_selected(f); - - if(l) { - gpointer d = purple_request_field_list_get_data(f, l->data); - - if(GPOINTER_TO_INT(d) == 0x01) { - blist_menu_conf_create(buddy, msg); - - } else { - struct mwIdBlock idb = { (char *)purple_buddy_get_name(buddy), NULL }; - mwConference_invite(d, &idb, msg); - } - } -} - - -static void blist_menu_conf_list(PurpleBuddy *buddy, - GList *confs) { - - PurpleRequestFields *fields; - PurpleRequestFieldGroup *g; - PurpleRequestField *f; - - PurpleAccount *acct; - PurpleConnection *gc; - - const char *msgA; - const char *msgB; - char *msg; - - acct = purple_buddy_get_account(buddy); - g_return_if_fail(acct != NULL); - - gc = purple_account_get_connection(acct); - g_return_if_fail(gc != NULL); - - fields = purple_request_fields_new(); - - g = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, g); - - f = purple_request_field_list_new("conf", _("Available Conferences")); - purple_request_field_list_set_multi_select(f, FALSE); - for(; confs; confs = confs->next) { - struct mwConference *c = confs->data; - purple_request_field_list_add_icon(f, mwConference_getTitle(c), NULL, c); - } - purple_request_field_list_add_icon(f, _("Create New Conference..."), - NULL, GINT_TO_POINTER(0x01)); - purple_request_field_group_add_field(g, f); - - f = purple_request_field_string_new(CHAT_KEY_INVITE, "Message", NULL, FALSE); - purple_request_field_group_add_field(g, f); - - msgA = _("Invite user to a conference"); - msgB = _("Select a conference from the list below to send an invite to" - " user %s. Select \"Create New Conference\" if you'd like to" - " create a new conference to invite this user to."); - msg = g_strdup_printf(msgB, purple_buddy_get_name(buddy)); - - purple_request_fields(gc, _("Invite to Conference"), - msgA, msg, fields, - _("Invite"), G_CALLBACK(conf_select_prompt_invite), - _("Cancel"), G_CALLBACK(conf_select_prompt_cancel), - acct, purple_buddy_get_name(buddy), NULL, - buddy); - g_free(msg); -} - - -static void blist_menu_conf(PurpleBlistNode *node, gpointer data) { - PurpleBuddy *buddy = (PurpleBuddy *) node; - PurpleAccount *acct; - PurpleConnection *gc; - struct mwPurplePluginData *pd; - GList *l; - - g_return_if_fail(node != NULL); - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - acct = purple_buddy_get_account(buddy); - g_return_if_fail(acct != NULL); - - gc = purple_account_get_connection(acct); - g_return_if_fail(gc != NULL); - - pd = gc->proto_data; - g_return_if_fail(pd != NULL); - - /* - - get a list of all conferences on this session - - if none, prompt to create one, and invite buddy to it - - else, prompt to select a conference or create one - */ - - l = mwServiceConference_getConferences(pd->srvc_conf); - if(l) { - blist_menu_conf_list(buddy, l); - g_list_free(l); - - } else { - blist_menu_conf_create(buddy, NULL); - } -} - - -#if 0 -static void blist_menu_announce(PurpleBlistNode *node, gpointer data) { - PurpleBuddy *buddy = (PurpleBuddy *) node; - PurpleAccount *acct; - PurpleConnection *gc; - struct mwPurplePluginData *pd; - struct mwSession *session; - char *rcpt_name; - GList *rcpt; - - g_return_if_fail(node != NULL); - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - acct = buddy->account; - g_return_if_fail(acct != NULL); - - gc = purple_account_get_connection(acct); - g_return_if_fail(gc != NULL); - - pd = gc->proto_data; - g_return_if_fail(pd != NULL); - - rcpt_name = g_strdup_printf("@U %s", buddy->name); - rcpt = g_list_prepend(NULL, rcpt_name); - - session = pd->session; - mwSession_sendAnnounce(session, FALSE, - "This is a TEST announcement. Please ignore.", - rcpt); - - g_list_free(rcpt); - g_free(rcpt_name); -} -#endif - - -static GList *mw_prpl_blist_node_menu(PurpleBlistNode *node) { - GList *l = NULL; - PurpleMenuAction *act; - - if(! PURPLE_BLIST_NODE_IS_BUDDY(node)) - return l; - - l = g_list_append(l, NULL); - - act = purple_menu_action_new(_("Invite to Conference..."), - PURPLE_CALLBACK(blist_menu_conf), NULL, NULL); - l = g_list_append(l, act); - -#if 0 - act = purple_menu_action_new(_("Send TEST Announcement"), - PURPLE_CALLBACK(blist_menu_announce), NULL, NULL); - l = g_list_append(l, act); -#endif - - /** note: this never gets called for a PurpleGroup, have to use the - blist-node-extended-menu signal for that. The function - blist_node_menu_cb is assigned to this signal in the function - services_starting */ - - return l; -} - - -static GList *mw_prpl_chat_info(PurpleConnection *gc) { - GList *l = NULL; - struct proto_chat_entry *pce; - - pce = g_new0(struct proto_chat_entry, 1); - pce->label = _("Topic:"); - pce->identifier = CHAT_KEY_TOPIC; - l = g_list_append(l, pce); - - return l; -} - - -static GHashTable *mw_prpl_chat_info_defaults(PurpleConnection *gc, - const char *name) { - GHashTable *table; - - g_return_val_if_fail(gc != NULL, NULL); - - table = g_hash_table_new_full(g_str_hash, g_str_equal, - NULL, g_free); - - g_hash_table_insert(table, CHAT_KEY_NAME, g_strdup(name)); - g_hash_table_insert(table, CHAT_KEY_INVITE, NULL); - - return table; -} - - -static void mw_prpl_login(PurpleAccount *acct); - - -static void mw_prpl_login(PurpleAccount *account) { - PurpleConnection *gc; - struct mwPurplePluginData *pd; - - char *user, *pass, *host; - guint port; - - gc = purple_account_get_connection(account); - pd = mwPurplePluginData_new(gc); - - /* while we do support images, the default is to not offer it */ - gc->flags |= PURPLE_CONNECTION_NO_IMAGES; - - user = g_strdup(purple_account_get_username(account)); - - host = strrchr(user, ':'); - if(host) { - /* annoying user split from 1.2.0, need to undo it */ - *host++ = '\0'; - purple_account_set_string(account, MW_KEY_HOST, host); - purple_account_set_username(account, user); - - } else { - host = (char *) purple_account_get_string(account, MW_KEY_HOST, - MW_PLUGIN_DEFAULT_HOST); - } - - if(! host || ! *host) { - /* somehow, we don't have a host to connect to. Well, we need one - to actually continue, so let's ask the user directly. */ - g_free(user); - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_INVALID_SETTINGS, - _("A server is required to connect this account")); - return; - } - - pass = g_strdup(purple_account_get_password(account)); - port = purple_account_get_int(account, MW_KEY_PORT, MW_PLUGIN_DEFAULT_PORT); - - DEBUG_INFO("user: '%s'\n", user); - DEBUG_INFO("host: '%s'\n", host); - DEBUG_INFO("port: %u\n", port); - - mwSession_setProperty(pd->session, mwSession_NO_SECRET, - (char *) no_secret, NULL); - mwSession_setProperty(pd->session, mwSession_AUTH_USER_ID, user, g_free); - mwSession_setProperty(pd->session, mwSession_AUTH_PASSWORD, pass, g_free); - - if(purple_account_get_bool(account, MW_KEY_FAKE_IT, FALSE)) { - guint client, major, minor; - - /* if we're faking the login, let's also fake the version we're - reporting. Let's also allow the actual values to be specified */ - - client = purple_account_get_int(account, MW_KEY_CLIENT, mwLogin_BINARY); - major = purple_account_get_int(account, MW_KEY_MAJOR, 0x001e); - minor = purple_account_get_int(account, MW_KEY_MINOR, 0x196f); - - DEBUG_INFO("client id: 0x%04x\n", client); - DEBUG_INFO("client major: 0x%04x\n", major); - DEBUG_INFO("client minor: 0x%04x\n", minor); - - mwSession_setProperty(pd->session, mwSession_CLIENT_TYPE_ID, - GUINT_TO_POINTER(client), NULL); - - mwSession_setProperty(pd->session, mwSession_CLIENT_VER_MAJOR, - GUINT_TO_POINTER(major), NULL); - - mwSession_setProperty(pd->session, mwSession_CLIENT_VER_MINOR, - GUINT_TO_POINTER(minor), NULL); - } - - purple_connection_update_progress(gc, _("Connecting"), 1, MW_CONNECT_STEPS); - - if (purple_proxy_connect(gc, account, host, port, connect_cb, pd) == NULL) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect")); - } -} - - -static void mw_prpl_close(PurpleConnection *gc) { - struct mwPurplePluginData *pd; - - g_return_if_fail(gc != NULL); - - pd = gc->proto_data; - g_return_if_fail(pd != NULL); - - /* get rid of the blist save timeout */ - if(pd->save_event) { - purple_timeout_remove(pd->save_event); - pd->save_event = 0; - blist_store(pd); - } - - /* stop the session */ - mwSession_stop(pd->session, 0x00); - - /* no longer necessary */ - gc->proto_data = NULL; - - /* stop watching the socket */ - if(gc->inpa) { - purple_input_remove(gc->inpa); - gc->inpa = 0; - } - - /* clean up the rest */ - mwPurplePluginData_free(pd); -} - - -static int mw_rand(void) { - static int seed = 0; - - /* for diversity, not security. don't touch */ - srand(time(NULL) ^ seed); - seed = rand(); - - return seed; -} - - -/** generates a random-ish content id string */ -static char *im_mime_content_id(void) { - return g_strdup_printf("%03x@%05xmeanwhile", - mw_rand() & 0xfff, mw_rand() & 0xfffff); -} - - -/** generates a multipart/related content type with a random-ish - boundary value */ -static char *im_mime_content_type(void) { - return g_strdup_printf("multipart/related; boundary=related_MW%03x_%04x", - mw_rand() & 0xfff, mw_rand() & 0xffff); -} - - -/** determine content type from extension. Not so happy about this, - but I don't want to actually write image type detection */ -static char *im_mime_img_content_type(PurpleStoredImage *img) { - const char *fn = purple_imgstore_get_filename(img); - const char *ct = NULL; - - ct = strrchr(fn, '.'); - if(! ct) { - ct = "image"; - - } else if(purple_strequal(".png", ct)) { - ct = "image/png"; - - } else if(purple_strequal(".jpg", ct)) { - ct = "image/jpeg"; - - } else if(purple_strequal(".jpeg", ct)) { - ct = "image/jpeg"; - - } else if(purple_strequal(".gif", ct)) { - ct = "image/gif"; - - } else { - ct = "image"; - } - - return g_strdup_printf("%s; name=\"%s\"", ct, fn); -} - - -static char *im_mime_img_content_disp(PurpleStoredImage *img) { - const char *fn = purple_imgstore_get_filename(img); - return g_strdup_printf("attachment; filename=\"%s\"", fn); -} - - -/** turn an IM with embedded images into a multi-part mime document */ -static char *im_mime_convert(PurpleConnection *gc, - struct mwConversation *conv, - const char *message) { - GString *str; - PurpleMimeDocument *doc; - PurpleMimePart *part; - - GData *attr; - char *tmp, *start, *end; - - str = g_string_new(NULL); - - doc = purple_mime_document_new(); - - purple_mime_document_set_field(doc, "Mime-Version", "1.0"); - purple_mime_document_set_field(doc, "Content-Disposition", "inline"); - - tmp = im_mime_content_type(); - purple_mime_document_set_field(doc, "Content-Type", tmp); - g_free(tmp); - - tmp = (char *) message; - while(*tmp && purple_markup_find_tag("img", tmp, (const char **) &start, - (const char **) &end, &attr)) { - char *id; - PurpleStoredImage *img = NULL; - - gsize len = (start - tmp); - - /* append the in-between-tags text */ - if(len) g_string_append_len(str, tmp, len); - - /* find the imgstore data by the id tag */ - id = g_datalist_get_data(&attr, "id"); - if(id && *id) - img = purple_imgstore_find_by_id(atoi(id)); - - if(img) { - char *cid; - gpointer data; - size_t size; - - part = purple_mime_part_new(doc); - - data = im_mime_img_content_disp(img); - purple_mime_part_set_field(part, "Content-Disposition", data); - g_free(data); - - data = im_mime_img_content_type(img); - purple_mime_part_set_field(part, "Content-Type", data); - g_free(data); - - cid = im_mime_content_id(); - data = g_strdup_printf("<%s>", cid); - purple_mime_part_set_field(part, "Content-ID", data); - g_free(data); - - purple_mime_part_set_field(part, "Content-transfer-encoding", "base64"); - - /* obtain and base64 encode the image data, and put it in the - mime part */ - size = purple_imgstore_get_size(img); - data = purple_base64_encode(purple_imgstore_get_data(img), (gsize) size); - purple_mime_part_set_data(part, data); - g_free(data); - - /* append the modified tag */ - g_string_append_printf(str, "", cid); - g_free(cid); - - } else { - /* append the literal image tag, since we couldn't find a - relative imgstore object */ - gsize len = (end - start) + 1; - g_string_append_len(str, start, len); - } - - g_datalist_clear(&attr); - tmp = end + 1; - } - - /* append left-overs */ - g_string_append(str, tmp); - - /* add the text/html part */ - part = purple_mime_part_new(doc); - purple_mime_part_set_field(part, "Content-Disposition", "inline"); - - tmp = purple_utf8_ncr_encode(str->str); - purple_mime_part_set_field(part, "Content-Type", "text/html"); - purple_mime_part_set_field(part, "Content-Transfer-Encoding", "7bit"); - purple_mime_part_set_data(part, tmp); - g_free(tmp); - - g_string_free(str, TRUE); - - str = g_string_new(NULL); - purple_mime_document_write(doc, str); - return g_string_free(str, FALSE); -} - - -static int mw_prpl_send_im(PurpleConnection *gc, - const char *name, - const char *message, - PurpleMessageFlags flags) { - - struct mwPurplePluginData *pd; - struct mwIdBlock who = { (char *) name, NULL }; - struct mwConversation *conv; - - g_return_val_if_fail(gc != NULL, 0); - pd = gc->proto_data; - - g_return_val_if_fail(pd != NULL, 0); - - conv = mwServiceIm_getConversation(pd->srvc_im, &who); - - /* this detection of features to determine how to send the message - (plain, html, or mime) is flawed because the other end of the - conversation could close their channel at any time, rendering any - existing formatting in an outgoing message innapropriate. The end - result is that it may be possible that the other side of the - conversation will receive a plaintext message with html contents, - which is bad. I'm not sure how to fix this correctly. */ - - if(strstr(message, "proto_data; - - g_return_val_if_fail(pd != NULL, 0); - - conv = mwServiceIm_getConversation(pd->srvc_im, &who); - - if(mwConversation_isOpen(conv)) { - mwConversation_send(conv, mwImSend_TYPING, t); - - } else if((state == PURPLE_TYPING) || (state == PURPLE_TYPED)) { - /* only open a channel for sending typing notification, not for - when typing has stopped. There's no point in re-opening a - channel just to tell someone that this side isn't typing. */ - - convo_queue(conv, mwImSend_TYPING, t); - - if(! mwConversation_isPending(conv)) { - mwConversation_open(conv); - } - } - - return 0; -} - - -static const char *mw_client_name(guint16 type) { - switch(type) { - case mwLogin_LIB: - return "Lotus Binary Library"; - - case mwLogin_JAVA_WEB: - return "Lotus Java Client Applet"; - - case mwLogin_BINARY: - return "Lotus Sametime Connect"; - - case mwLogin_JAVA_APP: - return "Lotus Java Client Application"; - - case mwLogin_LINKS: - return "Lotus Sametime Links"; - - case mwLogin_NOTES_6_5: - case mwLogin_NOTES_6_5_3: - case mwLogin_NOTES_7_0_beta: - case mwLogin_NOTES_7_0: - return "Lotus Notes Client"; - - case mwLogin_ICT: - case mwLogin_ICT_1_7_8_2: - case mwLogin_ICT_SIP: - return "IBM Community Tools"; - - case mwLogin_NOTESBUDDY_4_14: - case mwLogin_NOTESBUDDY_4_15: - case mwLogin_NOTESBUDDY_4_16: - return "Alphaworks NotesBuddy"; - - case 0x1305: - case 0x1306: - case 0x1307: - return "Lotus Sametime Connect 7.5"; - - case mwLogin_SANITY: - return "Sanity"; - - case mwLogin_ST_PERL: - return "ST-Send-Message"; - - case mwLogin_TRILLIAN: - case mwLogin_TRILLIAN_IBM: - return "Trillian"; - - case mwLogin_MEANWHILE: - return "Meanwhile"; - - default: - return NULL; - } -} - - -static void mw_prpl_get_info(PurpleConnection *gc, const char *who) { - - struct mwAwareIdBlock idb = { mwAware_USER, (char *) who, NULL }; - - struct mwPurplePluginData *pd; - PurpleAccount *acct; - PurpleBuddy *b; - PurpleNotifyUserInfo *user_info; - char *tmp; - const char *tmp2; - - g_return_if_fail(who != NULL); - g_return_if_fail(*who != '\0'); - - pd = gc->proto_data; - - acct = purple_connection_get_account(gc); - b = purple_find_buddy(acct, who); - user_info = purple_notify_user_info_new(); - - if(purple_str_has_prefix(who, "@E ")) { - purple_notify_user_info_add_pair(user_info, _("External User"), NULL); - } - - purple_notify_user_info_add_pair(user_info, _("User ID"), who); - - if(b) { - guint32 type; - - if(purple_buddy_get_server_alias(b)) { - purple_notify_user_info_add_pair(user_info, _("Full Name"), purple_buddy_get_server_alias(b)); - } - - type = purple_blist_node_get_int((PurpleBlistNode *) b, BUDDY_KEY_CLIENT); - if(type) { - tmp = g_strdup(mw_client_name(type)); - if (!tmp) - tmp = g_strdup_printf(_("Unknown (0x%04x)
"), type); - - purple_notify_user_info_add_pair(user_info, _("Last Known Client"), tmp); - - g_free(tmp); - } - } - - tmp = user_supports_text(pd->srvc_aware, who); - if(tmp) { - purple_notify_user_info_add_pair(user_info, _("Supports"), tmp); - g_free(tmp); - } - - if(b) { - purple_notify_user_info_add_pair(user_info, _("Status"), status_text(b)); - - /* XXX Is this adding a status message in its own section rather than with the "Status" label? */ - tmp2 = mwServiceAware_getText(pd->srvc_aware, &idb); - if(tmp2 && g_utf8_validate(tmp2, -1, NULL)) { - tmp = g_markup_escape_text(tmp2, -1); - purple_notify_user_info_add_section_break(user_info); - purple_notify_user_info_add_pair(user_info, NULL, tmp); - g_free(tmp); - } - } - - /* @todo emit a signal to allow a plugin to override the display of - this notification, so that it can create its own */ - - purple_notify_userinfo(gc, who, user_info, NULL, NULL); - purple_notify_user_info_destroy(user_info); -} - - -static void mw_prpl_set_status(PurpleAccount *acct, PurpleStatus *status) { - PurpleConnection *gc; - const char *state; - char *message = NULL; - struct mwSession *session; - struct mwUserStatus stat; - - g_return_if_fail(acct != NULL); - gc = purple_account_get_connection(acct); - - state = purple_status_get_id(status); - - DEBUG_INFO("Set status to %s\n", purple_status_get_name(status)); - - g_return_if_fail(gc != NULL); - - session = gc_to_session(gc); - g_return_if_fail(session != NULL); - - /* get a working copy of the current status */ - mwUserStatus_clone(&stat, mwSession_getUserStatus(session)); - - /* determine the state */ - if(purple_strequal(state, MW_STATE_ACTIVE)) { - stat.status = mwStatus_ACTIVE; - - } else if(purple_strequal(state, MW_STATE_AWAY)) { - stat.status = mwStatus_AWAY; - - } else if(purple_strequal(state, MW_STATE_BUSY)) { - stat.status = mwStatus_BUSY; - } - - /* determine the message */ - message = (char *) purple_status_get_attr_string(status, MW_STATE_MESSAGE); - - if(message) { - /* all the possible non-NULL values of message up to this point - are const, so we don't need to free them */ - message = purple_markup_strip_html(message); - } - - /* out with the old */ - g_free(stat.desc); - - /* in with the new */ - stat.desc = (char *) message; - - mwSession_setUserStatus(session, &stat); - mwUserStatus_clear(&stat); -} - - -static void mw_prpl_set_idle(PurpleConnection *gc, int t) { - struct mwSession *session; - struct mwUserStatus stat; - - - session = gc_to_session(gc); - g_return_if_fail(session != NULL); - - mwUserStatus_clone(&stat, mwSession_getUserStatus(session)); - - if(t) { - time_t now = time(NULL); - stat.time = now - t; - - } else { - stat.time = 0; - } - - if(t > 0 && stat.status == mwStatus_ACTIVE) { - /* we were active and went idle, so change the status to IDLE. */ - stat.status = mwStatus_IDLE; - - } else if(t == 0 && stat.status == mwStatus_IDLE) { - /* we only become idle automatically, so change back to ACTIVE */ - stat.status = mwStatus_ACTIVE; - } - - mwSession_setUserStatus(session, &stat); - mwUserStatus_clear(&stat); -} - - -static void notify_im(PurpleConnection *gc, GList *row, void *user_data) { - PurpleAccount *acct; - PurpleConversation *conv; - char *id; - - acct = purple_connection_get_account(gc); - id = g_list_nth_data(row, 1); - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, id, acct); - if(! conv) conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, id); - purple_conversation_present(conv); -} - - -static void notify_add(PurpleConnection *gc, GList *row, void *user_data) { - BuddyAddData *data = user_data; - const char *group_name = NULL; - - if (data && data->group) { - group_name = purple_group_get_name(data->group); - } - - purple_blist_request_add_buddy(purple_connection_get_account(gc), - g_list_nth_data(row, 1), group_name, - g_list_nth_data(row, 0)); -} - - -static void notify_close(gpointer data) { - if (data) { - g_free(data); - } -} - - -static void multi_resolved_query(struct mwResolveResult *result, - PurpleConnection *gc, gpointer data) { - GList *l; - const char *msgA; - const char *msgB; - char *msg; - - PurpleNotifySearchResults *sres; - PurpleNotifySearchColumn *scol; - - sres = purple_notify_searchresults_new(); - - scol = purple_notify_searchresults_column_new(_("User Name")); - purple_notify_searchresults_column_add(sres, scol); - - scol = purple_notify_searchresults_column_new(_("Sametime ID")); - purple_notify_searchresults_column_add(sres, scol); - - purple_notify_searchresults_button_add(sres, PURPLE_NOTIFY_BUTTON_IM, - notify_im); - - purple_notify_searchresults_button_add(sres, PURPLE_NOTIFY_BUTTON_ADD, - notify_add); - - for(l = result->matches; l; l = l->next) { - struct mwResolveMatch *match = l->data; - GList *row = NULL; - - DEBUG_INFO("multi resolve: %s, %s\n", - NSTR(match->id), NSTR(match->name)); - - if(!match->id || !match->name) - continue; - - row = g_list_append(row, g_strdup(match->name)); - row = g_list_append(row, g_strdup(match->id)); - purple_notify_searchresults_row_add(sres, row); - } - - msgA = _("An ambiguous user ID was entered"); - msgB = _("The identifier '%s' may possibly refer to any of the following" - " users. Please select the correct user from the list below to" - " add them to your buddy list."); - msg = g_strdup_printf(msgB, result->name); - - purple_notify_searchresults(gc, _("Select User"), - msgA, msg, sres, notify_close, data); - - g_free(msg); -} - - -static void add_buddy_resolved(struct mwServiceResolve *srvc, - guint32 id, guint32 code, GList *results, - gpointer b) { - - struct mwResolveResult *res = NULL; - BuddyAddData *data = b; - PurpleBuddy *buddy = NULL; - PurpleConnection *gc; - struct mwPurplePluginData *pd; - - g_return_if_fail(data != NULL); - - buddy = data->buddy; - - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - pd = gc->proto_data; - - if(results) - res = results->data; - - if(!code && res && res->matches) { - if(!res->matches->next) { - struct mwResolveMatch *match = res->matches->data; - - /* only one? that might be the right one! */ - if(!purple_strequal(res->name, match->id)) { - /* uh oh, the single result isn't identical to the search - term, better safe then sorry, so let's make sure it's who - the user meant to add */ - purple_blist_remove_buddy(buddy); - multi_resolved_query(res, gc, data); - - } else { - - /* same person, set the server alias */ - purple_blist_server_alias_buddy(buddy, match->name); - purple_blist_node_set_string((PurpleBlistNode *) buddy, - BUDDY_KEY_NAME, match->name); - - /* subscribe to awareness */ - buddy_add(pd, buddy); - - blist_schedule(pd); - - g_free(data); - } - - } else { - /* prompt user if more than one match was returned */ - purple_blist_remove_buddy(buddy); - multi_resolved_query(res, gc, data); - } - - return; - } - -#if 0 - /* fall-through indicates that we couldn't find a matching user in - the resolve service (ether error or zero results), so we remove - this buddy */ - - /* note: I can't really think of a good reason to alter the buddy - list in any way. There has been at least one report where the - resolve service isn't returning correct results anyway, so let's - just leave them in the list. I'm just going to if0 this section - out unless I can think of a very good reason to do this. -siege */ - - DEBUG_INFO("no such buddy in community\n"); - purple_blist_remove_buddy(buddy); - blist_schedule(pd); - - if(res && res->name) { - /* compose and display an error message */ - const char *msgA; - const char *msgB; - char *msg; - - msgA = _("Unable to add user: user not found"); - - msgB = _("The identifier '%s' did not match any users in your" - " Sametime community. This entry has been removed from" - " your buddy list."); - msg = g_strdup_printf(msgB, NSTR(res->name)); - - purple_notify_error(gc, _("Unable to add user"), msgA, msg); - - g_free(msg); - } -#endif -} - - -static void mw_prpl_add_buddy(PurpleConnection *gc, - PurpleBuddy *buddy, - PurpleGroup *group) { - - struct mwPurplePluginData *pd = gc->proto_data; - struct mwServiceResolve *srvc; - GList *query; - enum mwResolveFlag flags; - guint32 req; - BuddyAddData *data; - - /* catch external buddies. They won't be in the resolve service */ - if(buddy_is_external(buddy)) { - buddy_add(pd, buddy); - return; - } - - data = g_new0(BuddyAddData, 1); - data->buddy = buddy; - data->group = group; - - srvc = pd->srvc_resolve; - - query = g_list_prepend(NULL, (char *)purple_buddy_get_name(buddy)); - flags = mwResolveFlag_FIRST | mwResolveFlag_USERS; - - req = mwServiceResolve_resolve(srvc, query, flags, add_buddy_resolved, - data, NULL); - g_list_free(query); - - if(req == SEARCH_ERROR) { - purple_blist_remove_buddy(buddy); - blist_schedule(pd); - } -} - - -static void foreach_add_buddies(PurpleGroup *group, GList *buddies, - struct mwPurplePluginData *pd) { - struct mwAwareList *list; - - list = list_ensure(pd, group); - mwAwareList_addAware(list, buddies); - g_list_free(buddies); -} - - -static void mw_prpl_add_buddies(PurpleConnection *gc, - GList *buddies, - GList *groups) { - - struct mwPurplePluginData *pd; - GHashTable *group_sets; - struct mwAwareIdBlock *idbs, *idb; - - pd = gc->proto_data; - - /* map PurpleGroup:GList of mwAwareIdBlock */ - group_sets = g_hash_table_new(g_direct_hash, g_direct_equal); - - /* bunch of mwAwareIdBlock allocated at once, free'd at once */ - idb = idbs = g_new(struct mwAwareIdBlock, g_list_length(buddies)); - - /* first pass collects mwAwareIdBlock lists for each group */ - for(; buddies; buddies = buddies->next) { - PurpleBuddy *b = buddies->data; - PurpleGroup *g; - const char *fn; - GList *l; - - /* nab the saved server alias and stick it on the buddy */ - fn = purple_blist_node_get_string((PurpleBlistNode *) b, BUDDY_KEY_NAME); - purple_blist_server_alias_buddy(b, fn); - - /* convert PurpleBuddy into a mwAwareIdBlock */ - idb->type = mwAware_USER; - idb->user = (char *) purple_buddy_get_name(b); - idb->community = NULL; - - /* put idb into the list associated with the buddy's group */ - g = purple_buddy_get_group(b); - l = g_hash_table_lookup(group_sets, g); - l = g_list_prepend(l, idb++); - g_hash_table_insert(group_sets, g, l); - } - - /* each group's buddies get added in one shot, and schedule the blist - for saving */ - g_hash_table_foreach(group_sets, (GHFunc) foreach_add_buddies, pd); - blist_schedule(pd); - - /* cleanup */ - g_hash_table_destroy(group_sets); - g_free(idbs); -} - - -static void mw_prpl_remove_buddy(PurpleConnection *gc, - PurpleBuddy *buddy, PurpleGroup *group) { - - struct mwPurplePluginData *pd; - struct mwAwareIdBlock idb = { mwAware_USER, (char *)purple_buddy_get_name(buddy), NULL }; - struct mwAwareList *list; - - GList *rem = g_list_prepend(NULL, &idb); - - pd = gc->proto_data; - group = purple_buddy_get_group(buddy); - list = list_ensure(pd, group); - - mwAwareList_removeAware(list, rem); - blist_schedule(pd); - - g_list_free(rem); -} - - -static void privacy_fill(struct mwPrivacyInfo *priv, - GSList *members) { - - struct mwUserItem *u; - guint count; - - count = g_slist_length(members); - DEBUG_INFO("privacy_fill: %u members\n", count); - - priv->count = count; - priv->users = g_new0(struct mwUserItem, count); - - while(count--) { - u = priv->users + count; - u->id = members->data; - members = members->next; - } -} - - -static void mw_prpl_set_permit_deny(PurpleConnection *gc) { - PurpleAccount *acct; - struct mwPurplePluginData *pd; - struct mwSession *session; - - struct mwPrivacyInfo privacy = { - FALSE, /* deny */ - 0, /* count */ - NULL, /* users */ - }; - - g_return_if_fail(gc != NULL); - - acct = purple_connection_get_account(gc); - g_return_if_fail(acct != NULL); - - pd = gc->proto_data; - g_return_if_fail(pd != NULL); - - session = pd->session; - g_return_if_fail(session != NULL); - - switch(acct->perm_deny) { - case PURPLE_PRIVACY_DENY_USERS: - DEBUG_INFO("PURPLE_PRIVACY_DENY_USERS\n"); - privacy_fill(&privacy, acct->deny); - privacy.deny = TRUE; - break; - - case PURPLE_PRIVACY_ALLOW_ALL: - DEBUG_INFO("PURPLE_PRIVACY_ALLOW_ALL\n"); - privacy.deny = TRUE; - break; - - case PURPLE_PRIVACY_ALLOW_USERS: - DEBUG_INFO("PURPLE_PRIVACY_ALLOW_USERS\n"); - privacy_fill(&privacy, acct->permit); - privacy.deny = FALSE; - break; - - case PURPLE_PRIVACY_DENY_ALL: - DEBUG_INFO("PURPLE_PRIVACY_DENY_ALL\n"); - privacy.deny = FALSE; - break; - - default: - DEBUG_INFO("acct->perm_deny is 0x%x\n", acct->perm_deny); - return; - } - - mwSession_setPrivacyInfo(session, &privacy); - g_free(privacy.users); -} - - -static void mw_prpl_add_permit(PurpleConnection *gc, const char *name) { - mw_prpl_set_permit_deny(gc); -} - - -static void mw_prpl_add_deny(PurpleConnection *gc, const char *name) { - mw_prpl_set_permit_deny(gc); -} - - -static void mw_prpl_rem_permit(PurpleConnection *gc, const char *name) { - mw_prpl_set_permit_deny(gc); -} - - -static void mw_prpl_rem_deny(PurpleConnection *gc, const char *name) { - mw_prpl_set_permit_deny(gc); -} - - -static struct mwConference *conf_find(struct mwServiceConference *srvc, - const char *name) { - GList *l, *ll; - struct mwConference *conf = NULL; - - ll = mwServiceConference_getConferences(srvc); - for(l = ll; l; l = l->next) { - struct mwConference *c = l->data; - if(purple_strequal(name, mwConference_getName(c))) { - conf = c; - break; - } - } - g_list_free(ll); - - return conf; -} - - -static void mw_prpl_join_chat(PurpleConnection *gc, - GHashTable *components) { - - struct mwPurplePluginData *pd; - char *c, *t; - - pd = gc->proto_data; - - c = g_hash_table_lookup(components, CHAT_KEY_NAME); - t = g_hash_table_lookup(components, CHAT_KEY_TOPIC); - - if(g_hash_table_lookup(components, CHAT_KEY_IS_PLACE)) { - /* use place service */ - struct mwServicePlace *srvc; - struct mwPlace *place = NULL; - - srvc = pd->srvc_place; - place = mwPlace_new(srvc, c, t); - mwPlace_open(place); - - } else { - /* use conference service */ - struct mwServiceConference *srvc; - struct mwConference *conf = NULL; - - srvc = pd->srvc_conf; - if(c) conf = conf_find(srvc, c); - - if(conf) { - DEBUG_INFO("accepting conference invitation\n"); - mwConference_accept(conf); - - } else { - DEBUG_INFO("creating new conference\n"); - conf = mwConference_new(srvc, t); - mwConference_open(conf); - } - } -} - - -static void mw_prpl_reject_chat(PurpleConnection *gc, - GHashTable *components) { - - struct mwPurplePluginData *pd; - struct mwServiceConference *srvc; - char *c; - - pd = gc->proto_data; - srvc = pd->srvc_conf; - - if(g_hash_table_lookup(components, CHAT_KEY_IS_PLACE)) { - ; /* nothing needs doing */ - - } else { - /* reject conference */ - c = g_hash_table_lookup(components, CHAT_KEY_NAME); - if(c) { - struct mwConference *conf = conf_find(srvc, c); - if(conf) mwConference_reject(conf, ERR_SUCCESS, "Declined"); - } - } -} - - -static char *mw_prpl_get_chat_name(GHashTable *components) { - return g_hash_table_lookup(components, CHAT_KEY_NAME); -} - - -static void mw_prpl_chat_invite(PurpleConnection *gc, - int id, - const char *invitation, - const char *who) { - - struct mwPurplePluginData *pd; - struct mwConference *conf; - struct mwPlace *place; - struct mwIdBlock idb = { (char *) who, NULL }; - - pd = gc->proto_data; - g_return_if_fail(pd != NULL); - - conf = ID_TO_CONF(pd, id); - - if(conf) { - mwConference_invite(conf, &idb, invitation); - return; - } - - place = ID_TO_PLACE(pd, id); - g_return_if_fail(place != NULL); - - /* @todo: use the IM service for invitation */ - mwPlace_legacyInvite(place, &idb, invitation); -} - - -static void mw_prpl_chat_leave(PurpleConnection *gc, - int id) { - - struct mwPurplePluginData *pd; - struct mwConference *conf; - - pd = gc->proto_data; - - g_return_if_fail(pd != NULL); - conf = ID_TO_CONF(pd, id); - - if(conf) { - mwConference_destroy(conf, ERR_SUCCESS, "Leaving"); - - } else { - struct mwPlace *place = ID_TO_PLACE(pd, id); - g_return_if_fail(place != NULL); - - mwPlace_destroy(place, ERR_SUCCESS); - } -} - - -static void mw_prpl_chat_whisper(PurpleConnection *gc, - int id, - const char *who, - const char *message) { - - mw_prpl_send_im(gc, who, message, 0); -} - - -static int mw_prpl_chat_send(PurpleConnection *gc, - int id, - const char *message, - PurpleMessageFlags flags) { - - struct mwPurplePluginData *pd; - struct mwConference *conf; - char *msg; - int ret; - - pd = gc->proto_data; - - g_return_val_if_fail(pd != NULL, 0); - conf = ID_TO_CONF(pd, id); - - msg = purple_markup_strip_html(message); - - if(conf) { - ret = ! mwConference_sendText(conf, msg); - - } else { - struct mwPlace *place = ID_TO_PLACE(pd, id); - g_return_val_if_fail(place != NULL, 0); - - ret = ! mwPlace_sendText(place, msg); - } - - g_free(msg); - return ret; -} - - -static void mw_prpl_keepalive(PurpleConnection *gc) { - struct mwSession *session; - - g_return_if_fail(gc != NULL); - - session = gc_to_session(gc); - g_return_if_fail(session != NULL); - - mwSession_sendKeepalive(session); -} - - -static void mw_prpl_alias_buddy(PurpleConnection *gc, - const char *who, - const char *alias) { - - struct mwPurplePluginData *pd = gc->proto_data; - g_return_if_fail(pd != NULL); - - /* it's a change to the buddy list, so we've gotta reflect that in - the server copy */ - - blist_schedule(pd); -} - - -static void mw_prpl_group_buddy(PurpleConnection *gc, - const char *who, - const char *old_group, - const char *new_group) { - - struct mwAwareIdBlock idb = { mwAware_USER, (char *) who, NULL }; - GList *gl = g_list_prepend(NULL, &idb); - - struct mwPurplePluginData *pd = gc->proto_data; - PurpleGroup *group; - struct mwAwareList *list; - - /* add who to new_group's aware list */ - group = purple_find_group(new_group); - list = list_ensure(pd, group); - mwAwareList_addAware(list, gl); - - /* remove who from old_group's aware list */ - group = purple_find_group(old_group); - list = list_ensure(pd, group); - mwAwareList_removeAware(list, gl); - - g_list_free(gl); - - /* schedule the changes to be saved */ - blist_schedule(pd); -} - - -static void mw_prpl_rename_group(PurpleConnection *gc, - const char *old, - PurpleGroup *group, - GList *buddies) { - - struct mwPurplePluginData *pd = gc->proto_data; - g_return_if_fail(pd != NULL); - - /* it's a change in the buddy list, so we've gotta reflect that in - the server copy. Also, having this function should prevent all - those buddies from being removed and re-added. We don't really - give a crap what the group is named in Purple other than to record - that as the group name/alias */ - - blist_schedule(pd); -} - - -static void mw_prpl_buddy_free(PurpleBuddy *buddy) { - /* I don't think we have any cleanup for buddies yet */ - ; -} - - -static void mw_prpl_convo_closed(PurpleConnection *gc, const char *who) { - struct mwPurplePluginData *pd = gc->proto_data; - struct mwServiceIm *srvc; - struct mwConversation *conv; - struct mwIdBlock idb = { (char *) who, NULL }; - - g_return_if_fail(pd != NULL); - - srvc = pd->srvc_im; - g_return_if_fail(srvc != NULL); - - conv = mwServiceIm_findConversation(srvc, &idb); - if(! conv) return; - - if(mwConversation_isOpen(conv)) - mwConversation_free(conv); -} - - -static const char *mw_prpl_normalize(const PurpleAccount *account, - const char *id) { - - /* code elsewhere assumes that the return value points to different - memory than the passed value, but it won't free the normalized - data. wtf? */ - - static char buf[BUF_LEN]; - g_strlcpy(buf, id, sizeof(buf)); - return buf; -} - - -static void mw_prpl_remove_group(PurpleConnection *gc, PurpleGroup *group) { - struct mwPurplePluginData *pd; - struct mwAwareList *list; - - pd = gc->proto_data; - g_return_if_fail(pd != NULL); - g_return_if_fail(pd->group_list_map != NULL); - - list = g_hash_table_lookup(pd->group_list_map, group); - - if(list) { - g_hash_table_remove(pd->group_list_map, list); - g_hash_table_remove(pd->group_list_map, group); - mwAwareList_free(list); - - blist_schedule(pd); - } -} - - -static gboolean mw_prpl_can_receive_file(PurpleConnection *gc, - const char *who) { - struct mwPurplePluginData *pd; - struct mwServiceAware *srvc; - PurpleAccount *acct; - - g_return_val_if_fail(gc != NULL, FALSE); - - pd = gc->proto_data; - g_return_val_if_fail(pd != NULL, FALSE); - - srvc = pd->srvc_aware; - g_return_val_if_fail(srvc != NULL, FALSE); - - acct = purple_connection_get_account(gc); - g_return_val_if_fail(acct != NULL, FALSE); - - return purple_find_buddy(acct, who) && - user_supports(srvc, who, mwAttribute_FILE_TRANSFER); -} - - -static void ft_outgoing_init(PurpleXfer *xfer) { - PurpleAccount *acct; - PurpleConnection *gc; - - struct mwPurplePluginData *pd; - struct mwServiceFileTransfer *srvc; - struct mwFileTransfer *ft; - - const char *filename; - gsize filesize; - FILE *fp; - - struct mwIdBlock idb = { NULL, NULL }; - - DEBUG_INFO("ft_outgoing_init\n"); - - acct = purple_xfer_get_account(xfer); - gc = purple_account_get_connection(acct); - pd = gc->proto_data; - srvc = pd->srvc_ft; - - filename = purple_xfer_get_local_filename(xfer); - filesize = purple_xfer_get_size(xfer); - idb.user = xfer->who; - - purple_xfer_update_progress(xfer); - - /* test that we can actually send the file */ - fp = g_fopen(filename, "rb"); - if(! fp) { - char *msg = g_strdup_printf(_("Error reading file %s: \n%s\n"), - filename, g_strerror(errno)); - purple_xfer_error(purple_xfer_get_type(xfer), acct, xfer->who, msg); - g_free(msg); - return; - } - fclose(fp); - - { - char *tmp = strrchr(filename, G_DIR_SEPARATOR); - if(tmp++) filename = tmp; - } - - ft = mwFileTransfer_new(srvc, &idb, NULL, filename, filesize); - - purple_xfer_ref(xfer); - mwFileTransfer_setClientData(ft, xfer, (GDestroyNotify) purple_xfer_unref); - xfer->data = ft; - - mwFileTransfer_offer(ft); -} - - -static void ft_outgoing_cancel(PurpleXfer *xfer) { - struct mwFileTransfer *ft = xfer->data; - - DEBUG_INFO("ft_outgoing_cancel called\n"); - - if(ft) mwFileTransfer_cancel(ft); -} - - -static PurpleXfer *mw_prpl_new_xfer(PurpleConnection *gc, const char *who) { - PurpleAccount *acct; - PurpleXfer *xfer; - - acct = purple_connection_get_account(gc); - - xfer = purple_xfer_new(acct, PURPLE_XFER_SEND, who); - if (xfer) - { - purple_xfer_set_init_fnc(xfer, ft_outgoing_init); - purple_xfer_set_cancel_send_fnc(xfer, ft_outgoing_cancel); - } - - return xfer; -} - -static void mw_prpl_send_file(PurpleConnection *gc, - const char *who, const char *file) { - - PurpleXfer *xfer = mw_prpl_new_xfer(gc, who); - - if(file) { - DEBUG_INFO("file != NULL\n"); - purple_xfer_request_accepted(xfer, file); - - } else { - DEBUG_INFO("file == NULL\n"); - purple_xfer_request(xfer); - } -} - - -static PurplePluginProtocolInfo mw_prpl_info = { - OPT_PROTO_IM_IMAGE, - NULL, /*< set in mw_plugin_init */ - NULL, /*< set in mw_plugin_init */ - NO_BUDDY_ICONS, - mw_prpl_list_icon, - mw_prpl_list_emblem, - mw_prpl_status_text, - mw_prpl_tooltip_text, - mw_prpl_status_types, - mw_prpl_blist_node_menu, - mw_prpl_chat_info, - mw_prpl_chat_info_defaults, - mw_prpl_login, - mw_prpl_close, - mw_prpl_send_im, - NULL, - mw_prpl_send_typing, - mw_prpl_get_info, - mw_prpl_set_status, - mw_prpl_set_idle, - NULL, - mw_prpl_add_buddy, - mw_prpl_add_buddies, - mw_prpl_remove_buddy, - NULL, - mw_prpl_add_permit, - mw_prpl_add_deny, - mw_prpl_rem_permit, - mw_prpl_rem_deny, - mw_prpl_set_permit_deny, - mw_prpl_join_chat, - mw_prpl_reject_chat, - mw_prpl_get_chat_name, - mw_prpl_chat_invite, - mw_prpl_chat_leave, - mw_prpl_chat_whisper, - mw_prpl_chat_send, - mw_prpl_keepalive, - NULL, - NULL, - NULL, - mw_prpl_alias_buddy, - mw_prpl_group_buddy, - mw_prpl_rename_group, - mw_prpl_buddy_free, - mw_prpl_convo_closed, - mw_prpl_normalize, - NULL, - mw_prpl_remove_group, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - mw_prpl_can_receive_file, - mw_prpl_send_file, - mw_prpl_new_xfer, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - sizeof(PurplePluginProtocolInfo), - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, /* get_cb_alias */ - NULL, /* chat_can_receive_file */ - NULL, /* chat_send_file */ -}; - - -static PurplePluginPrefFrame * -mw_plugin_get_plugin_pref_frame(PurplePlugin *plugin) { - PurplePluginPrefFrame *frame; - PurplePluginPref *pref; - - frame = purple_plugin_pref_frame_new(); - - pref = purple_plugin_pref_new_with_label(_("Remotely Stored Buddy List")); - purple_plugin_pref_frame_add(frame, pref); - - - pref = purple_plugin_pref_new_with_name(MW_PRPL_OPT_BLIST_ACTION); - purple_plugin_pref_set_label(pref, _("Buddy List Storage Mode")); - - purple_plugin_pref_set_type(pref, PURPLE_PLUGIN_PREF_CHOICE); - purple_plugin_pref_add_choice(pref, _("Local Buddy List Only"), - GINT_TO_POINTER(blist_choice_LOCAL)); - purple_plugin_pref_add_choice(pref, _("Merge List from Server"), - GINT_TO_POINTER(blist_choice_MERGE)); - purple_plugin_pref_add_choice(pref, _("Merge and Save List to Server"), - GINT_TO_POINTER(blist_choice_STORE)); - purple_plugin_pref_add_choice(pref, _("Synchronize List with Server"), - GINT_TO_POINTER(blist_choice_SYNCH)); - - purple_plugin_pref_frame_add(frame, pref); - - return frame; -} - - -static PurplePluginUiInfo mw_plugin_ui_info = { - mw_plugin_get_plugin_pref_frame, - 0, /* page_num */ - NULL, /* frame */ - NULL, - NULL, - NULL, - NULL -}; - - -static void st_import_action_cb(PurpleConnection *gc, char *filename) { - struct mwSametimeList *l; - - FILE *file; - char buf[BUF_LEN]; - size_t len; - - GString *str; - - file = g_fopen(filename, "r"); - g_return_if_fail(file != NULL); - - str = g_string_new(NULL); - while( (len = fread(buf, 1, BUF_LEN, file)) ) { - g_string_append_len(str, buf, len); - } - - fclose(file); - - l = mwSametimeList_load(str->str); - g_string_free(str, TRUE); - - blist_merge(gc, l); - mwSametimeList_free(l); -} - - -/** prompts for a file to import blist from */ -static void st_import_action(PurplePluginAction *act) { - PurpleConnection *gc; - PurpleAccount *account; - char *title; - - gc = act->context; - account = purple_connection_get_account(gc); - title = g_strdup_printf(_("Import Sametime List for Account %s"), - purple_account_get_username(account)); - - purple_request_file(gc, title, NULL, FALSE, - G_CALLBACK(st_import_action_cb), NULL, - account, NULL, NULL, - gc); - - g_free(title); -} - - -static void st_export_action_cb(PurpleConnection *gc, char *filename) { - struct mwSametimeList *l; - char *str; - FILE *file; - - file = g_fopen(filename, "w"); - g_return_if_fail(file != NULL); - - l = mwSametimeList_new(); - blist_export(gc, l); - str = mwSametimeList_store(l); - mwSametimeList_free(l); - - fprintf(file, "%s", str); - fclose(file); - - g_free(str); -} - - -/** prompts for a file to export blist to */ -static void st_export_action(PurplePluginAction *act) { - PurpleConnection *gc; - PurpleAccount *account; - char *title; - - gc = act->context; - account = purple_connection_get_account(gc); - title = g_strdup_printf(_("Export Sametime List for Account %s"), - purple_account_get_username(account)); - - purple_request_file(gc, title, NULL, TRUE, - G_CALLBACK(st_export_action_cb), NULL, - account, NULL, NULL, - gc); - - g_free(title); -} - - -static void remote_group_multi_cleanup(gpointer ignore, - PurpleRequestFields *fields) { - - PurpleRequestField *f; - GList *l; - - f = purple_request_fields_get_field(fields, "group"); - l = purple_request_field_list_get_items(f); - - for(; l; l = l->next) { - const char *i = l->data; - struct named_id *res; - - res = purple_request_field_list_get_data(f, i); - - g_free(res->id); - g_free(res->name); - g_free(res); - } -} - - -static void remote_group_done(struct mwPurplePluginData *pd, - const char *id, const char *name) { - PurpleConnection *gc; - PurpleAccount *acct; - PurpleGroup *group; - PurpleBlistNode *gn; - const char *owner; - - g_return_if_fail(pd != NULL); - - gc = pd->gc; - acct = purple_connection_get_account(gc); - - /* collision checking */ - group = purple_find_group(name); - if(group) { - const char *msgA; - const char *msgB; - char *msg; - - msgA = _("Unable to add group: group exists"); - msgB = _("A group named '%s' already exists in your buddy list."); - msg = g_strdup_printf(msgB, name); - - purple_notify_error(gc, _("Unable to add group"), msgA, msg); - - g_free(msg); - return; - } - - group = purple_group_new(name); - gn = (PurpleBlistNode *) group; - - owner = purple_account_get_username(acct); - - purple_blist_node_set_string(gn, GROUP_KEY_NAME, id); - purple_blist_node_set_int(gn, GROUP_KEY_TYPE, mwSametimeGroup_DYNAMIC); - purple_blist_node_set_string(gn, GROUP_KEY_OWNER, owner); - purple_blist_add_group(group, NULL); - - group_add(pd, group); - blist_schedule(pd); -} - - -static void remote_group_multi_cb(struct mwPurplePluginData *pd, - PurpleRequestFields *fields) { - PurpleRequestField *f; - GList *l; - - f = purple_request_fields_get_field(fields, "group"); - l = purple_request_field_list_get_selected(f); - - if(l) { - const char *i = l->data; - struct named_id *res; - - res = purple_request_field_list_get_data(f, i); - remote_group_done(pd, res->id, res->name); - } - - remote_group_multi_cleanup(NULL, fields); -} - - -static void remote_group_multi(struct mwResolveResult *result, - struct mwPurplePluginData *pd) { - - PurpleRequestFields *fields; - PurpleRequestFieldGroup *g; - PurpleRequestField *f; - GList *l; - const char *msgA; - const char *msgB; - char *msg; - - PurpleConnection *gc = pd->gc; - - fields = purple_request_fields_new(); - - g = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, g); - - f = purple_request_field_list_new("group", _("Possible Matches")); - purple_request_field_list_set_multi_select(f, FALSE); - purple_request_field_set_required(f, TRUE); - - for(l = result->matches; l; l = l->next) { - struct mwResolveMatch *match = l->data; - struct named_id *res = g_new0(struct named_id, 1); - - res->id = g_strdup(match->id); - res->name = g_strdup(match->name); - - purple_request_field_list_add_icon(f, res->name, NULL, res); - } - - purple_request_field_group_add_field(g, f); - - msgA = _("Notes Address Book group results"); - msgB = _("The identifier '%s' may possibly refer to any of the following" - " Notes Address Book groups. Please select the correct group from" - " the list below to add it to your buddy list."); - msg = g_strdup_printf(msgB, result->name); - - purple_request_fields(gc, _("Select Notes Address Book"), - msgA, msg, fields, - _("Add Group"), G_CALLBACK(remote_group_multi_cb), - _("Cancel"), G_CALLBACK(remote_group_multi_cleanup), - purple_connection_get_account(gc), result->name, NULL, - pd); - - g_free(msg); -} - - -static void remote_group_resolved(struct mwServiceResolve *srvc, - guint32 id, guint32 code, GList *results, - gpointer b) { - - struct mwResolveResult *res = NULL; - struct mwSession *session; - struct mwPurplePluginData *pd; - PurpleConnection *gc; - - session = mwService_getSession(MW_SERVICE(srvc)); - g_return_if_fail(session != NULL); - - pd = mwSession_getClientData(session); - g_return_if_fail(pd != NULL); - - gc = pd->gc; - g_return_if_fail(gc != NULL); - - if(!code && results) { - res = results->data; - - if(res->matches) { - remote_group_multi(res, pd); - return; - } - } - - if(res && res->name) { - const char *msgA; - const char *msgB; - char *msg; - - msgA = _("Unable to add group: group not found"); - - msgB = _("The identifier '%s' did not match any Notes Address Book" - " groups in your Sametime community."); - msg = g_strdup_printf(msgB, res->name); - - purple_notify_error(gc, _("Unable to add group"), msgA, msg); - - g_free(msg); - } -} - - -static void remote_group_action_cb(PurpleConnection *gc, const char *name) { - struct mwPurplePluginData *pd; - struct mwServiceResolve *srvc; - GList *query; - enum mwResolveFlag flags; - guint32 req; - - pd = gc->proto_data; - srvc = pd->srvc_resolve; - - query = g_list_prepend(NULL, (char *) name); - flags = mwResolveFlag_FIRST | mwResolveFlag_GROUPS; - - req = mwServiceResolve_resolve(srvc, query, flags, remote_group_resolved, - NULL, NULL); - g_list_free(query); - - if(req == SEARCH_ERROR) { - /** @todo display error */ - } -} - - -static void remote_group_action(PurplePluginAction *act) { - PurpleConnection *gc; - const char *msgA; - const char *msgB; - - gc = act->context; - - msgA = _("Notes Address Book Group"); - msgB = _("Enter the name of a Notes Address Book group in the field below" - " to add the group and its members to your buddy list."); - - purple_request_input(gc, _("Add Group"), msgA, msgB, NULL, - FALSE, FALSE, NULL, - _("Add"), G_CALLBACK(remote_group_action_cb), - _("Cancel"), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); -} - - -static void search_notify(struct mwResolveResult *result, - PurpleConnection *gc) { - GList *l; - const char *msgA; - const char *msgB; - char *msg1; - char *msg2; - - PurpleNotifySearchResults *sres; - PurpleNotifySearchColumn *scol; - - sres = purple_notify_searchresults_new(); - - scol = purple_notify_searchresults_column_new(_("User Name")); - purple_notify_searchresults_column_add(sres, scol); - - scol = purple_notify_searchresults_column_new(_("Sametime ID")); - purple_notify_searchresults_column_add(sres, scol); - - purple_notify_searchresults_button_add(sres, PURPLE_NOTIFY_BUTTON_IM, - notify_im); - - purple_notify_searchresults_button_add(sres, PURPLE_NOTIFY_BUTTON_ADD, - notify_add); - - for(l = result->matches; l; l = l->next) { - struct mwResolveMatch *match = l->data; - GList *row = NULL; - - if(!match->id || !match->name) - continue; - - row = g_list_append(row, g_strdup(match->name)); - row = g_list_append(row, g_strdup(match->id)); - purple_notify_searchresults_row_add(sres, row); - } - - msgA = _("Search results for '%s'"); - msgB = _("The identifier '%s' may possibly refer to any of the following" - " users. You may add these users to your buddy list or send them" - " messages with the action buttons below."); - - msg1 = g_strdup_printf(msgA, result->name); - msg2 = g_strdup_printf(msgB, result->name); - - purple_notify_searchresults(gc, _("Search Results"), - msg1, msg2, sres, notify_close, NULL); - - g_free(msg1); - g_free(msg2); -} - - -static void search_resolved(struct mwServiceResolve *srvc, - guint32 id, guint32 code, GList *results, - gpointer b) { - - PurpleConnection *gc = b; - struct mwResolveResult *res = NULL; - - if(results) res = results->data; - - if(!code && res && res->matches) { - search_notify(res, gc); - - } else { - const char *msgA; - const char *msgB; - char *msg; - - msgA = _("No matches"); - msgB = _("The identifier '%s' did not match any users in your" - " Sametime community."); - msg = g_strdup_printf(msgB, (res && res->name) ? NSTR(res->name) : ""); - - purple_notify_error(gc, _("No Matches"), msgA, msg); - - g_free(msg); - } -} - - -static void search_action_cb(PurpleConnection *gc, const char *name) { - struct mwPurplePluginData *pd; - struct mwServiceResolve *srvc; - GList *query; - enum mwResolveFlag flags; - guint32 req; - - pd = gc->proto_data; - srvc = pd->srvc_resolve; - - query = g_list_prepend(NULL, (char *) name); - flags = mwResolveFlag_FIRST | mwResolveFlag_USERS; - - req = mwServiceResolve_resolve(srvc, query, flags, search_resolved, - gc, NULL); - g_list_free(query); - - if(req == SEARCH_ERROR) { - /** @todo display error */ - } -} - - -static void search_action(PurplePluginAction *act) { - PurpleConnection *gc; - const char *msgA; - const char *msgB; - - gc = act->context; - - msgA = _("Search for a user"); - msgB = _("Enter a name or partial ID in the field below to search" - " for matching users in your Sametime community."); - - purple_request_input(gc, _("User Search"), msgA, msgB, NULL, - FALSE, FALSE, NULL, - _("Search"), G_CALLBACK(search_action_cb), - _("Cancel"), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); -} - - -static GList *mw_plugin_actions(PurplePlugin *plugin, gpointer context) { - PurplePluginAction *act; - GList *l = NULL; - - act = purple_plugin_action_new(_("Import Sametime List..."), - st_import_action); - l = g_list_append(l, act); - - act = purple_plugin_action_new(_("Export Sametime List..."), - st_export_action); - l = g_list_append(l, act); - - act = purple_plugin_action_new(_("Add Notes Address Book Group..."), - remote_group_action); - l = g_list_append(l, act); - - act = purple_plugin_action_new(_("User Search..."), - search_action); - l = g_list_append(l, act); - - return l; -} - - -static gboolean mw_plugin_load(PurplePlugin *plugin) { - return TRUE; -} - - -static gboolean mw_plugin_unload(PurplePlugin *plugin) { - return TRUE; -} - - -static void mw_plugin_destroy(PurplePlugin *plugin) { - g_log_remove_handler(G_LOG_DOMAIN, log_handler[0]); - g_log_remove_handler("meanwhile", log_handler[1]); -} - -static PurplePluginInfo mw_plugin_info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - PLUGIN_ID, /**< id */ - PLUGIN_NAME, /**< name */ - DISPLAY_VERSION, /**< version */ - PLUGIN_SUMMARY, /**< summary */ - PLUGIN_DESC, /**< description */ - PLUGIN_AUTHOR, /**< author */ - PLUGIN_HOMEPAGE, /**< homepage */ - - mw_plugin_load, /**< load */ - mw_plugin_unload, /**< unload */ - mw_plugin_destroy, /**< destroy */ - - NULL, /**< ui_info */ - &mw_prpl_info, /**< extra_info */ - &mw_plugin_ui_info, /**< prefs_info */ - mw_plugin_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - - -static void mw_log_handler(const gchar *domain, GLogLevelFlags flags, - const gchar *msg, gpointer data) { - - if(! (msg && *msg)) return; - - /* handle g_log requests via purple's built-in debug logging */ - if(flags & G_LOG_LEVEL_ERROR) { - purple_debug_error(domain, "%s\n", msg); - - } else if(flags & G_LOG_LEVEL_WARNING) { - purple_debug_warning(domain, "%s\n", msg); - - } else { - purple_debug_info(domain, "%s\n", msg); - } -} - - -static void mw_plugin_init(PurplePlugin *plugin) { - PurpleAccountUserSplit *split; - PurpleAccountOption *opt; - GList *l = NULL; - - GLogLevelFlags logflags = - G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION; - - /* set up the preferences */ - purple_prefs_add_none(MW_PRPL_OPT_BASE); - purple_prefs_add_int(MW_PRPL_OPT_BLIST_ACTION, BLIST_CHOICE_DEFAULT); - - /* set up account ID as user:server */ - split = purple_account_user_split_new(_("Server"), - MW_PLUGIN_DEFAULT_HOST, ':'); - mw_prpl_info.user_splits = g_list_append(mw_prpl_info.user_splits, split); - - /* remove dead preferences */ - purple_prefs_remove(MW_PRPL_OPT_PSYCHIC); - purple_prefs_remove(MW_PRPL_OPT_SAVE_DYNAMIC); - - /* port to connect to */ - opt = purple_account_option_int_new(_("Port"), MW_KEY_PORT, - MW_PLUGIN_DEFAULT_PORT); - l = g_list_append(l, opt); - - { /* copy the old force login setting from prefs if it's - there. Don't delete the preference, since there may be more - than one account that wants to check for it. */ - gboolean b = FALSE; - const char *label = _("Force login (ignore server redirects)"); - - if(purple_prefs_exists(MW_PRPL_OPT_FORCE_LOGIN)) - b = purple_prefs_get_bool(MW_PRPL_OPT_FORCE_LOGIN); - - opt = purple_account_option_bool_new(label, MW_KEY_FORCE, b); - l = g_list_append(l, opt); - } - - /* pretend to be Sametime Connect */ - opt = purple_account_option_bool_new(_("Hide client identity"), - MW_KEY_FAKE_IT, FALSE); - l = g_list_append(l, opt); - - mw_prpl_info.protocol_options = l; - l = NULL; - - /* forward all our g_log messages to purple. Generally all the logging - calls are using purple_log directly, but the g_return macros will - get caught here */ - log_handler[0] = g_log_set_handler(G_LOG_DOMAIN, logflags, - mw_log_handler, NULL); - - /* redirect meanwhile's logging to purple's */ - log_handler[1] = g_log_set_handler("meanwhile", logflags, - mw_log_handler, NULL); -} - - -PURPLE_INIT_PLUGIN(sametime, mw_plugin_init, mw_plugin_info); -/* The End. */ - diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/sametime/sametime.h --- a/libpurple/protocols/sametime/sametime.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,26 +0,0 @@ - - -/* CFLAGS trumps configure values */ - - -/** default host for the purple plugin. You can specialize a build to - default to your server by supplying this at compile time */ -#ifndef MW_PLUGIN_DEFAULT_HOST -#define MW_PLUGIN_DEFAULT_HOST "" -#endif -/* "" */ - - -/** default port for the purple plugin. You can specialize a build to - default to your server by supplying this at compile time */ -#ifndef MW_PLUGIN_DEFAULT_PORT -#define MW_PLUGIN_DEFAULT_PORT 1533 -#endif -/* 1533 */ - - -/** default encoding for the purple plugin.*/ -#ifndef MW_PLUGIN_DEFAULT_ENCODING -#define MW_PLUGIN_DEFAULT_ENCODING "ISO-8859-1" -#endif -/* ISO-8859-1 */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc/Makefile.am --- a/libpurple/protocols/silc/Makefile.am Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -EXTRA_DIST = \ - Makefile.mingw \ - README \ - TODO - -pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) - -SILCSOURCES = \ - buddy.c \ - chat.c \ - ft.c \ - ops.c \ - pk.c \ - silc.c \ - silcpurple.h \ - util.c \ - wb.c \ - wb.h - -AM_CFLAGS = $(st) - -libsilcpurple_la_LDFLAGS = -module -avoid-version - -if STATIC_SILC - -st = -DPURPLE_STATIC_PRPL $(SILC_CFLAGS) -noinst_LTLIBRARIES = libsilcpurple.la -libsilcpurple_la_SOURCES = $(SILCSOURCES) -libsilcpurple_la_CFLAGS = $(AM_CFLAGS) -libsilcpurple_la_LIBADD = $(SILC_LIBS) - -else - -st = $(SILC_CFLAGS) -pkg_LTLIBRARIES = libsilcpurple.la -libsilcpurple_la_SOURCES = $(SILCSOURCES) -libsilcpurple_la_LIBADD = $(GLIB_LIBS) $(SILC_LIBS) - -endif - -AM_CPPFLAGS = \ - -I$(top_srcdir)/libpurple \ - -I$(top_builddir)/libpurple \ - $(DEBUG_CFLAGS) \ - $(GLIB_CFLAGS) \ - $(SILC_CFLAGS) diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc/Makefile.mingw --- a/libpurple/protocols/silc/Makefile.mingw Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -# -# Makefile.mingw -# -# Description: Makefile for win32 (mingw) version of libsilc protocol plugin -# - -PIDGIN_TREE_TOP := ../../.. -include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak - -DEFINES := $(subst -DWIN32_LEAN_AND_MEAN,,$(DEFINES)) - -TARGET = libsilc -NEEDED_DLLS = $(SILC_TOOLKIT)/bin/libsilc-1-1-4.dll \ - $(SILC_TOOLKIT)/bin/libsilcclient-1-1-4.dll -TYPE = PLUGIN - -# Static or Plugin... -ifeq ($(TYPE),STATIC) - DEFINES += -DSTATIC - DLL_INSTALL_DIR = $(PURPLE_INSTALL_DIR) -else -ifeq ($(TYPE),PLUGIN) - DLL_INSTALL_DIR = $(PURPLE_INSTALL_PLUGINS_DIR) -endif -endif - -## -## INCLUDE PATHS -## -INCLUDE_PATHS += -I. \ - -I$(GTK_TOP)/include \ - -I$(GTK_TOP)/include/glib-2.0 \ - -I$(GTK_TOP)/lib/glib-2.0/include \ - -I$(PURPLE_TOP) \ - -I$(PURPLE_TOP)/win32 \ - -I$(PIDGIN_TREE_TOP) \ - -I$(SILC_TOOLKIT)/include - -LIB_PATHS += -L$(GTK_TOP)/lib \ - -L$(PURPLE_TOP) \ - -L$(SILC_TOOLKIT)/lib - -## -## SOURCES, OBJECTS -## -C_SRC = silc.c \ - buddy.c \ - chat.c \ - ft.c \ - ops.c \ - pk.c \ - util.c \ - wb.c - -OBJECTS = $(C_SRC:%.c=%.o) - -## -## LIBRARIES -## -LIBS = \ - -lglib-2.0 \ - -lws2_32 \ - -lintl \ - -lpurple \ - -lsilc \ - -lsilcclient - -include $(PIDGIN_COMMON_RULES) - -## -## TARGET DEFINITIONS -## -.PHONY: all install clean - -all: $(TARGET).dll - -install: all $(DLL_INSTALL_DIR) $(PURPLE_INSTALL_DIR) - cp $(TARGET).dll $(DLL_INSTALL_DIR) - cp $(NEEDED_DLLS) $(PURPLE_INSTALL_DIR) - -$(OBJECTS): $(PURPLE_CONFIG_H) - -$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS) - $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -Wl,--image-base,0x74000000 -o $(TARGET).dll - -## -## CLEAN RULES -## -clean: - rm -f $(OBJECTS) - rm -f $(TARGET).dll - -include $(PIDGIN_COMMON_TARGETS) diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc/README --- a/libpurple/protocols/silc/README Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -SILC Purple Plugin -================== - -This is the Purple protocol plugin of the protocol called Secure Internet -Live Conferencing (SILC). The implementation will use the SILC Toolkit, -freely available from the http://silcnet.org/ site, for the actual SILC -protocol implementation. - -To include SILC into Purple, one needs to first compile and install -the SILC Toolkit. It is done as follows: - - ./configure - make - make install - -This will compile shared libraries of the SILC Toolkit. If the --prefix -is not given to ./configure, the binaries are installed into the -/usr/local/silc directory. - -Once the Toolkit is installed one needs to tell Purple's ./configure -script where the SILC Toolkit is located. It is done as simply as: - - ./configure - -if pkg-config is installed in your system. If it is isn't it's done as: - - ./configure --with-silc-libs=/path/to/silc/lib - --with-silc-includes=/path/to/silc/include - -If the SILC Toolkit cannot be found then the SILC protocol plugin will -not be compiled. diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc/TODO --- a/libpurple/protocols/silc/TODO Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,6 +0,0 @@ -Features TODO (maybe) -===================== - -Preferences - - Add joined channels to buddy list automatically (during - session) diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc/buddy.c --- a/libpurple/protocols/silc/buddy.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1731 +0,0 @@ -/* - - silcpurple_buddy.c - - Author: Pekka Riikonen - - Copyright (C) 2004 - 2007 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#include "internal.h" -#include "silc.h" -#include "silcclient.h" -#include "silcpurple.h" -#include "wb.h" - -#include "glibcompat.h" - -/***************************** Key Agreement *********************************/ - -static void -silcpurple_buddy_keyagr(PurpleBlistNode *node, gpointer data); - -static void -silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name, - gboolean force_local); - -typedef struct { - char *nick; - PurpleConnection *gc; -} *SilcPurpleResolve; - -static void -silcpurple_buddy_keyagr_resolved(SilcClient client, - SilcClientConnection conn, - SilcStatus status, - SilcDList clients, - void *context) -{ - PurpleConnection *gc = client->application; - SilcPurpleResolve r = context; - char tmp[256]; - - if (!clients) { - g_snprintf(tmp, sizeof(tmp), - _("User %s is not present in the network"), r->nick); - purple_notify_error(gc, _("Key Agreement"), - _("Cannot perform the key agreement"), tmp); - g_free(r->nick); - silc_free(r); - return; - } - - silcpurple_buddy_keyagr_do(gc, r->nick, FALSE); - g_free(r->nick); - silc_free(r); -} - -static void -silcpurple_buddy_keyagr_cb(SilcClient client, - SilcClientConnection conn, - SilcClientEntry client_entry, - SilcKeyAgreementStatus status, - SilcSKEKeyMaterial key, - void *context) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - - if (!sg->conn) - return; - - switch (status) { - case SILC_KEY_AGREEMENT_OK: - { - PurpleConversation *convo; - char tmp[128]; - - /* Set the private key for this client */ - silc_client_del_private_message_key(client, conn, client_entry); - silc_client_add_private_message_key_ske(client, conn, client_entry, - NULL, NULL, key); - silc_ske_free_key_material(key); - - - /* Open IM window */ - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, - client_entry->nickname, sg->account); - if (convo) { - /* we don't have windows in the core anymore...but we may want to - * provide some method for asking the UI to show the window - purple_conv_window_show(purple_conversation_get_window(convo)); - */ - } else { - convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, sg->account, - client_entry->nickname); - } - g_snprintf(tmp, sizeof(tmp), "%s [private key]", client_entry->nickname); - purple_conversation_set_title(convo, tmp); - } - break; - - case SILC_KEY_AGREEMENT_ERROR: - purple_notify_error(gc, _("Key Agreement"), - _("Error occurred during key agreement"), NULL); - break; - - case SILC_KEY_AGREEMENT_FAILURE: - purple_notify_error(gc, _("Key Agreement"), _("Key Agreement failed"), NULL); - break; - - case SILC_KEY_AGREEMENT_TIMEOUT: - purple_notify_error(gc, _("Key Agreement"), - _("Timeout during key agreement"), NULL); - break; - - case SILC_KEY_AGREEMENT_ABORTED: - purple_notify_error(gc, _("Key Agreement"), - _("Key agreement was aborted"), NULL); - break; - - case SILC_KEY_AGREEMENT_ALREADY_STARTED: - purple_notify_error(gc, _("Key Agreement"), - _("Key agreement is already started"), NULL); - break; - - case SILC_KEY_AGREEMENT_SELF_DENIED: - purple_notify_error(gc, _("Key Agreement"), - _("Key agreement cannot be started with yourself"), - NULL); - break; - - default: - break; - } -} - -static void -silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name, - gboolean force_local) -{ - SilcPurple sg = gc->proto_data; - SilcDList clients; - SilcClientEntry client_entry; - SilcClientConnectionParams params; - char *local_ip = NULL, *remote_ip = NULL; - gboolean local = TRUE; - SilcSocket sock; - - if (!sg->conn || !name) - return; - - /* Find client entry */ - clients = silc_client_get_clients_local(sg->client, sg->conn, name, - FALSE); - if (!clients) { - /* Resolve unknown user */ - SilcPurpleResolve r = silc_calloc(1, sizeof(*r)); - if (!r) - return; - r->nick = g_strdup(name); - r->gc = gc; - silc_client_get_clients(sg->client, sg->conn, name, NULL, - silcpurple_buddy_keyagr_resolved, r); - return; - } - - silc_socket_stream_get_info(silc_packet_stream_get_stream(sg->conn->stream), - &sock, NULL, NULL, NULL); - - /* Resolve the local IP from the outgoing socket connection. We resolve - it to check whether we have a private range IP address or public IP - address. If we have public then we will assume that we are not behind - NAT and will provide automatically the point of connection to the - agreement. If we have private range address we assume that we are - behind NAT and we let the responder provide the point of connection. - - The algorithm also checks the remote IP address of server connection. - If it is private range address and we have private range address we - assume that we are chatting in LAN and will provide the point of - connection. - - Naturally this algorithm does not always get things right. */ - - if (silc_net_check_local_by_sock(sock, NULL, &local_ip)) { - /* Check if the IP is private */ - if (!force_local && silcpurple_ip_is_private(local_ip)) { - local = FALSE; - - /* Local IP is private, resolve the remote server IP to see whether - we are talking to Internet or just on LAN. */ - if (silc_net_check_host_by_sock(sock, NULL, - &remote_ip)) - if (silcpurple_ip_is_private(remote_ip)) - /* We assume we are in LAN. Let's provide - the connection point. */ - local = TRUE; - } - } - - if (force_local) - local = TRUE; - - if (local && !local_ip) - local_ip = silc_net_localip(); - - silc_dlist_start(clients); - client_entry = silc_dlist_get(clients); - - memset(¶ms, 0, sizeof(params)); - params.timeout_secs = 60; - if (local) - /* Provide connection point */ - params.local_ip = local_ip; - - /* Send the key agreement request */ - silc_client_send_key_agreement(sg->client, sg->conn, client_entry, - ¶ms, sg->public_key, - sg->private_key, - silcpurple_buddy_keyagr_cb, NULL); - - silc_free(local_ip); - silc_free(remote_ip); - silc_client_list_free(sg->client, sg->conn, clients); -} - -typedef struct { - SilcClient client; - SilcClientConnection conn; - SilcClientID client_id; - char *hostname; - SilcUInt16 port; -} *SilcPurpleKeyAgrAsk; - -static void -silcpurple_buddy_keyagr_request_cb(SilcPurpleKeyAgrAsk a, gint id) -{ - SilcClientEntry client_entry; - SilcClientConnectionParams params; - - if (id != 1) - goto out; - - /* Get the client entry. */ - client_entry = silc_client_get_client_by_id(a->client, a->conn, - &a->client_id); - if (!client_entry) { - purple_notify_error(a->client->application, _("Key Agreement"), - _("The remote user is not present in the network any more"), - NULL); - goto out; - } - - /* If the hostname was provided by the requestor perform the key agreement - now. Otherwise, we will send him a request to connect to us. */ - if (a->hostname) { - memset(¶ms, 0, sizeof(params)); - params.timeout_secs = 60; - silc_client_perform_key_agreement(a->client, a->conn, - client_entry, ¶ms, - a->conn->public_key, - a->conn->private_key, - a->hostname, a->port, - silcpurple_buddy_keyagr_cb, NULL); - } else { - /* Send request. Force us as the point of connection since requestor - did not provide the point of connection. */ - silcpurple_buddy_keyagr_do(a->client->application, - client_entry->nickname, TRUE); - } - - out: - g_free(a->hostname); - silc_free(a); -} - -void silcpurple_buddy_keyagr_request(SilcClient client, - SilcClientConnection conn, - SilcClientEntry client_entry, - const char *hostname, SilcUInt16 port, - SilcUInt16 protocol) -{ - char tmp[128], tmp2[128]; - SilcPurpleKeyAgrAsk a; - PurpleConnection *gc = client->application; - - /* For now Pidgin don't support UDP key agreement */ - if (protocol == 1) - return; - - g_snprintf(tmp, sizeof(tmp), - _("Key agreement request received from %s. Would you like to " - "perform the key agreement?"), client_entry->nickname); - if (hostname) - g_snprintf(tmp2, sizeof(tmp2), - _("The remote user is waiting key agreement on:\n" - "Remote host: %s\nRemote port: %d"), hostname, port); - - a = silc_calloc(1, sizeof(*a)); - if (!a) - return; - a->client = client; - a->conn = conn; - a->client_id = client_entry->id; - if (hostname) - a->hostname = g_strdup(hostname); - a->port = port; - - purple_request_action(client->application, _("Key Agreement Request"), tmp, - hostname ? tmp2 : NULL, 1, gc->account, client_entry->nickname, - NULL, a, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb), - _("No"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb)); -} - -static void -silcpurple_buddy_keyagr(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *buddy; - PurpleAccount *account; - - buddy = (PurpleBuddy *)node; - account = purple_buddy_get_account(buddy); - silcpurple_buddy_keyagr_do(purple_account_get_connection(account), - purple_buddy_get_name(buddy), FALSE); -} - - -/**************************** Static IM Key **********************************/ - -static void -silcpurple_buddy_resetkey(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *b; - PurpleConnection *gc; - SilcPurple sg; - SilcDList clients; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - b = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(b)); - sg = gc->proto_data; - - /* Find client entry */ - clients = silc_client_get_clients_local(sg->client, sg->conn, - purple_buddy_get_name(b), FALSE); - if (!clients) - return; - - silc_dlist_start(clients); - silc_client_del_private_message_key(sg->client, sg->conn, - silc_dlist_get(clients)); - silc_client_list_free(sg->client, sg->conn, clients); -} - -typedef struct { - SilcClient client; - SilcClientConnection conn; - SilcClientID client_id; -} *SilcPurplePrivkey; - -static void -silcpurple_buddy_privkey(PurpleConnection *gc, const char *name); - -static void -silcpurple_buddy_privkey_cb(SilcPurplePrivkey p, const char *passphrase) -{ - SilcClientEntry client_entry; - - if (!passphrase || !(*passphrase)) { - silc_free(p); - return; - } - - /* Get the client entry. */ - client_entry = silc_client_get_client_by_id(p->client, p->conn, - &p->client_id); - if (!client_entry) { - purple_notify_error(p->client->application, _("IM With Password"), - _("The remote user is not present in the network any more"), - NULL); - silc_free(p); - return; - } - - /* Set the private message key */ - silc_client_del_private_message_key(p->client, p->conn, - client_entry); - silc_client_add_private_message_key(p->client, p->conn, - client_entry, NULL, NULL, - (unsigned char *)passphrase, - strlen(passphrase)); - silc_free(p); -} - -static void -silcpurple_buddy_privkey_resolved(SilcClient client, - SilcClientConnection conn, - SilcStatus status, - SilcDList clients, - void *context) -{ - char tmp[256]; - - if (!clients) { - g_snprintf(tmp, sizeof(tmp), - _("User %s is not present in the network"), - (const char *)context); - purple_notify_error(client->application, _("IM With Password"), - _("Cannot set IM key"), tmp); - g_free(context); - return; - } - - silcpurple_buddy_privkey(client->application, context); - g_free(context); -} - -static void -silcpurple_buddy_privkey(PurpleConnection *gc, const char *name) -{ - SilcPurple sg = gc->proto_data; - SilcPurplePrivkey p; - SilcDList clients; - SilcClientEntry client_entry; - - if (!name) - return; - - /* Find client entry */ - clients = silc_client_get_clients_local(sg->client, sg->conn, - name, FALSE); - if (!clients) { - silc_client_get_clients(sg->client, sg->conn, name, NULL, - silcpurple_buddy_privkey_resolved, - g_strdup(name)); - return; - } - - silc_dlist_start(clients); - client_entry = silc_dlist_get(clients); - - p = silc_calloc(1, sizeof(*p)); - if (!p) - return; - p->client = sg->client; - p->conn = sg->conn; - p->client_id = client_entry->id; - purple_request_input(gc, _("IM With Password"), NULL, - _("Set IM Password"), NULL, FALSE, TRUE, NULL, - _("OK"), G_CALLBACK(silcpurple_buddy_privkey_cb), - _("Cancel"), G_CALLBACK(silcpurple_buddy_privkey_cb), - gc->account, NULL, NULL, p); - - silc_client_list_free(sg->client, sg->conn, clients); -} - -static void -silcpurple_buddy_privkey_menu(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - - silcpurple_buddy_privkey(gc, purple_buddy_get_name(buddy)); -} - - -/**************************** Get Public Key *********************************/ - -typedef struct { - SilcClient client; - SilcClientConnection conn; - SilcClientID client_id; -} *SilcPurpleBuddyGetkey; - -static void -silcpurple_buddy_getkey(PurpleConnection *gc, const char *name); - -static SilcBool -silcpurple_buddy_getkey_cb(SilcClient client, SilcClientConnection conn, - SilcCommand command, SilcStatus status, - SilcStatus error, void *context, va_list ap) -{ - SilcClientEntry client_entry; - SilcPurpleBuddyGetkey g = context; - - if (status != SILC_STATUS_OK) { - purple_notify_error(g->client->application, _("Get Public Key"), - _("The remote user is not present in the network any more"), - NULL); - silc_free(g); - return FALSE; - } - - /* Get the client entry. */ - client_entry = silc_client_get_client_by_id(g->client, g->conn, - &g->client_id); - if (!client_entry) { - purple_notify_error(g->client->application, _("Get Public Key"), - _("The remote user is not present in the network any more"), - NULL); - silc_free(g); - return FALSE; - } - - if (!client_entry->public_key) { - silc_free(g); - return FALSE; - } - - /* Now verify the public key */ - silcpurple_verify_public_key(g->client, g->conn, client_entry->nickname, - SILC_CONN_CLIENT, client_entry->public_key, - NULL, NULL); - silc_free(g); - return TRUE; -} - -static void -silcpurple_buddy_getkey_resolved(SilcClient client, - SilcClientConnection conn, - SilcStatus status, - SilcDList clients, - void *context) -{ - char tmp[256]; - - if (!clients) { - g_snprintf(tmp, sizeof(tmp), - _("User %s is not present in the network"), - (const char *)context); - purple_notify_error(client->application, _("Get Public Key"), - _("Cannot fetch the public key"), tmp); - g_free(context); - return; - } - - silcpurple_buddy_getkey(client->application, context); - g_free(context); -} - -static void -silcpurple_buddy_getkey(PurpleConnection *gc, const char *name) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcClientEntry client_entry; - SilcDList clients; - SilcPurpleBuddyGetkey g; - SilcUInt16 cmd_ident; - - if (!name) - return; - - /* Find client entry */ - clients = silc_client_get_clients_local(client, conn, name, FALSE); - if (!clients) { - silc_client_get_clients(client, conn, name, NULL, - silcpurple_buddy_getkey_resolved, - g_strdup(name)); - return; - } - - silc_dlist_start(clients); - client_entry = silc_dlist_get(clients); - - /* Call GETKEY */ - g = silc_calloc(1, sizeof(*g)); - if (!g) - return; - g->client = client; - g->conn = conn; - g->client_id = client_entry->id; - cmd_ident = silc_client_command_call(client, conn, NULL, "GETKEY", - client_entry->nickname, NULL); - silc_client_command_pending(conn, SILC_COMMAND_GETKEY, cmd_ident, - silcpurple_buddy_getkey_cb, g); - silc_client_list_free(client, conn, clients); -} - -static void -silcpurple_buddy_getkey_menu(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(buddy)); - - silcpurple_buddy_getkey(gc, purple_buddy_get_name(buddy)); -} - -static void -silcpurple_buddy_showkey(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *b; - PurpleConnection *gc; - SilcPurple sg; - SilcPublicKey public_key; - const char *pkfile; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - b = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(b)); - sg = gc->proto_data; - - pkfile = purple_blist_node_get_string(node, "public-key"); - if (!silc_pkcs_load_public_key(pkfile, &public_key)) { - purple_notify_error(gc, - _("Show Public Key"), - _("Could not load public key"), NULL); - return; - } - - silcpurple_show_public_key(sg, purple_buddy_get_name(b), public_key, NULL, NULL); - silc_pkcs_public_key_free(public_key); -} - - -/**************************** Buddy routines *********************************/ - -/* The buddies are implemented by using the WHOIS and WATCH commands that - can be used to search users by their public key. Since nicknames aren't - unique in SILC we cannot trust the buddy list using their nickname. We - associate public keys to buddies and use those to search and watch - in the network. - - The problem is that Purple does not return PurpleBuddy contexts to the - callbacks but the buddy names. Naturally, this is not going to work - with SILC. But, for now, we have to do what we can... */ - -typedef struct { - SilcClient client; - SilcClientConnection conn; - SilcClientID client_id; - PurpleBuddy *b; - unsigned char *offline_pk; - SilcUInt32 offline_pk_len; - SilcPublicKey public_key; - unsigned int offline : 1; - unsigned int pubkey_search : 1; - unsigned int init : 1; -} *SilcPurpleBuddyRes; - -static void -silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id); -static void -silcpurple_add_buddy_resolved(SilcClient client, - SilcClientConnection conn, - SilcStatus status, - SilcDList clients, - void *context); - -void silcpurple_get_info(PurpleConnection *gc, const char *who) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcClientEntry client_entry; - PurpleBuddy *b; - const char *filename, *nick = who; - char tmp[256]; - - if (!who) - return; - if (strlen(who) > 1 && who[0] == '@') - nick = who + 1; - if (strlen(who) > 1 && who[0] == '*') - nick = who + 1; - if (strlen(who) > 2 && who[0] == '*' && who[1] == '@') - nick = who + 2; - - b = purple_find_buddy(gc->account, nick); - if (b) { - /* See if we have this buddy's public key. If we do use that - to search the details. */ - gpointer proto_data; - filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key"); - if (filename) { - /* Call WHOIS. The user info is displayed in the WHOIS - command reply. */ - silc_client_command_call(client, conn, NULL, "WHOIS", - "-details", "-pubkey", filename, NULL); - return; - } - - if (!(proto_data = purple_buddy_get_protocol_data(b))) { - g_snprintf(tmp, sizeof(tmp), - _("User %s is not present in the network"), purple_buddy_get_name(b)); - purple_notify_error(gc, _("User Information"), - _("Cannot get user information"), tmp); - return; - } - - client_entry = silc_client_get_client_by_id(client, conn, proto_data); - if (client_entry) { - /* Call WHOIS. The user info is displayed in the WHOIS - command reply. */ - silc_client_command_call(client, conn, NULL, "WHOIS", - client_entry->nickname, "-details", NULL); - } - } else { - /* Call WHOIS just with nickname. */ - silc_client_command_call(client, conn, NULL, "WHOIS", nick, NULL); - } -} - -static void -silcpurple_add_buddy_pk_no(SilcPurpleBuddyRes r) -{ - char tmp[512]; - g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not trusted"), - purple_buddy_get_name(r->b)); - purple_notify_error(r->client->application, _("Add Buddy"), tmp, - _("You cannot receive buddy notifications until you " - "import his/her public key. You can use the Get Public Key " - "command to get the public key.")); - purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); -} - -static void -silcpurple_add_buddy_save(SilcBool success, void *context) -{ - SilcPurpleBuddyRes r = context; - PurpleBuddy *b = r->b; - SilcClientEntry client_entry; - SilcAttributePayload attr; - SilcAttribute attribute; - SilcVCardStruct vcard; - SilcMime message = NULL, extension = NULL; - SilcMime usericon = NULL; - SilcAttributeObjPk serverpk, usersign, serversign; - gboolean usign_success = TRUE, ssign_success = TRUE; - char filename[512], filename2[512], *fingerprint = NULL, *tmp; - SilcUInt32 len; - SilcHash hash; - gsize i; - - if (!success) { - /* The user did not trust the public key. */ - silcpurple_add_buddy_pk_no(r); - silc_free(r->offline_pk); - if (r->public_key) - silc_pkcs_public_key_free(r->public_key); - silc_free(r); - return; - } - - if (r->offline) { - /* User is offline. Associate the imported public key with - this user. */ - fingerprint = silc_hash_fingerprint(NULL, r->offline_pk, - r->offline_pk_len); - for (i = 0; i < strlen(fingerprint); i++) - if (fingerprint[i] == ' ') - fingerprint[i] = '_'; - g_snprintf(filename, sizeof(filename) - 1, - "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub", - silcpurple_silcdir(), fingerprint); - purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename); - purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); - silc_free(fingerprint); - silc_free(r->offline_pk); - if (r->public_key) - silc_pkcs_public_key_free(r->public_key); - silc_free(r); - return; - } - - /* Get the client entry. */ - client_entry = silc_client_get_client_by_id(r->client, r->conn, - &r->client_id); - if (!client_entry) { - silc_free(r->offline_pk); - silc_pkcs_public_key_free(r->public_key); - if (r->public_key) - silc_pkcs_public_key_free(r->public_key); - silc_free(r); - return; - } - - memset(&vcard, 0, sizeof(vcard)); - memset(&serverpk, 0, sizeof(serverpk)); - memset(&usersign, 0, sizeof(usersign)); - memset(&serversign, 0, sizeof(serversign)); - - /* Now that we have the public key and we trust it now we - save the attributes of the buddy and update its status. */ - - if (client_entry->attrs) { - silc_dlist_start(client_entry->attrs); - while ((attr = silc_dlist_get(client_entry->attrs)) - != SILC_LIST_END) { - attribute = silc_attribute_get_attribute(attr); - - switch (attribute) { - case SILC_ATTRIBUTE_USER_INFO: - if (!silc_attribute_get_object(attr, (void *)&vcard, - sizeof(vcard))) - continue; - break; - - case SILC_ATTRIBUTE_STATUS_MESSAGE: - message = silc_mime_alloc(); - if (!silc_attribute_get_object(attr, (void *)message, - sizeof(*message))) - continue; - break; - - case SILC_ATTRIBUTE_EXTENSION: - extension = silc_mime_alloc(); - if (!silc_attribute_get_object(attr, (void *)extension, - sizeof(*extension))) - continue; - break; - - case SILC_ATTRIBUTE_USER_ICON: - usericon = silc_mime_alloc(); - if (!silc_attribute_get_object(attr, (void *)usericon, - sizeof(*usericon))) - continue; - break; - - case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY: - if (serverpk.type) - continue; - if (!silc_attribute_get_object(attr, (void *)&serverpk, - sizeof(serverpk))) - continue; - break; - - case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE: - if (usersign.data) - continue; - if (!silc_attribute_get_object(attr, (void *)&usersign, - sizeof(usersign))) - continue; - break; - - case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE: - if (serversign.data) - continue; - if (!silc_attribute_get_object(attr, (void *)&serversign, - sizeof(serversign))) - continue; - break; - - default: - break; - } - } - } - - /* Verify the attribute signatures */ - silc_hash_alloc((const unsigned char *)"sha1", &hash); - - if (usersign.data) { - unsigned char *verifyd; - SilcUInt32 verify_len; - - verifyd = silc_attribute_get_verify_data(client_entry->attrs, - FALSE, &verify_len); - if (verifyd && !silc_pkcs_verify(client_entry->public_key, - usersign.data, - usersign.data_len, - verifyd, verify_len, hash)) - usign_success = FALSE; - silc_free(verifyd); - } - - if (serversign.data) { - SilcPublicKey public_key; - SilcPKCSType type = 0; - unsigned char *verifyd; - SilcUInt32 verify_len; - - if (purple_strequal(serverpk.type, "silc-rsa")) - type = SILC_PKCS_SILC; - else if (purple_strequal(serverpk.type, "ssh-rsa")) - type = SILC_PKCS_SSH2; - else if (purple_strequal(serverpk.type, "x509v3-sign-rsa")) - type = SILC_PKCS_X509V3; - else if (purple_strequal(serverpk.type, "pgp-sign-rsa")) - type = SILC_PKCS_OPENPGP; - - if (silc_pkcs_public_key_alloc(type, serverpk.data, - serverpk.data_len, - &public_key)) { - verifyd = silc_attribute_get_verify_data(client_entry->attrs, - TRUE, &verify_len); - if (verifyd && !silc_pkcs_verify(public_key, - serversign.data, - serversign.data_len, - verifyd, verify_len, - hash)) - ssign_success = FALSE; - silc_pkcs_public_key_free(public_key); - silc_free(verifyd); - } - } - - fingerprint = silc_fingerprint(client_entry->fingerprint, 20); - for (i = 0; i < strlen(fingerprint); i++) - if (fingerprint[i] == ' ') - fingerprint[i] = '_'; - - if (usign_success || ssign_success) { - struct passwd *pw; - - memset(filename2, 0, sizeof(filename2)); - - /* Filename for dir */ - tmp = fingerprint + strlen(fingerprint) - 9; - g_snprintf(filename, sizeof(filename) - 1, - "%s" G_DIR_SEPARATOR_S "friends" G_DIR_SEPARATOR_S "%s", - silcpurple_silcdir(), tmp); - - pw = getpwuid(getuid()); - if (!pw) - return; - - /* Create dir if it doesn't exist */ - if (pw->pw_uid == geteuid()) { - if (g_mkdir(filename, 0755) != 0 && errno != EEXIST) - return; - } - - /* Save VCard */ - g_snprintf(filename2, sizeof(filename2) - 1, - "%s" G_DIR_SEPARATOR_S "vcard", filename); - if (vcard.full_name) { - tmp = (char *)silc_vcard_encode(&vcard, &len); - silc_file_writefile(filename2, tmp, len); - silc_free(tmp); - } - - /* Save status message */ - if (message) { - memset(filename2, 0, sizeof(filename2)); - g_snprintf(filename2, sizeof(filename2) - 1, - "%s" G_DIR_SEPARATOR_S "status_message.mime", - filename); - tmp = (char *)silc_mime_get_data(message, &len); - silc_file_writefile(filename2, tmp, len); - silc_mime_free(message); - } - - /* Save extension data */ - if (extension) { - memset(filename2, 0, sizeof(filename2)); - g_snprintf(filename2, sizeof(filename2) - 1, - "%s" G_DIR_SEPARATOR_S "extension.mime", - filename); - tmp = (char *)silc_mime_get_data(extension, &len); - silc_file_writefile(filename2, tmp, len); - silc_mime_free(extension); - } - - /* Save user icon */ - if (usericon) { - const char *type = silc_mime_get_field(usericon, "Content-Type"); - if (type && - (purple_strequal(type, "image/jpeg") || - purple_strequal(type, "image/gif") || - purple_strequal(type, "image/bmp") || - purple_strequal(type, "image/png"))) { - const unsigned char *data; - SilcUInt32 data_len; - data = silc_mime_get_data(usericon, &data_len); - if (data) { - /* TODO: Check if SILC gives us something to use as the checksum instead */ - purple_buddy_icons_set_for_user(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), g_memdup2(data, data_len), data_len, NULL); - } - } - silc_mime_free(usericon); - } - } - - /* Save the public key path to buddy properties, as it is used - to identify the buddy in the network (and not the nickname). */ - memset(filename, 0, sizeof(filename)); - g_snprintf(filename, sizeof(filename) - 1, - "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub", - silcpurple_silcdir(), fingerprint); - purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename); - - /* Update online status */ - purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL); - - /* Finally, start watching this user so we receive its status - changes from the server */ - g_snprintf(filename2, sizeof(filename2) - 1, "+%s", filename); - silc_client_command_call(r->client, r->conn, NULL, "WATCH", "-pubkey", - filename2, NULL); - - silc_hash_free(hash); - silc_free(fingerprint); - silc_free(r->offline_pk); - if (r->public_key) - silc_pkcs_public_key_free(r->public_key); - silc_free(r); -} - -static void -silcpurple_add_buddy_ask_import(void *user_data, const char *name) -{ - SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data; - - /* Load the public key */ - if (!silc_pkcs_load_public_key(name, &r->public_key)) { - silcpurple_add_buddy_ask_pk_cb(r, 0); - purple_notify_error(r->client->application, - _("Add Buddy"), _("Could not load public key"), NULL); - return; - } - - /* Now verify the public key */ - r->offline_pk = silc_pkcs_public_key_encode(r->public_key, &r->offline_pk_len); - silcpurple_verify_public_key(r->client, r->conn, purple_buddy_get_name(r->b), - SILC_CONN_CLIENT, r->public_key, - silcpurple_add_buddy_save, r); -} - -static void -silcpurple_add_buddy_ask_pk_cancel(void *user_data, const char *name) -{ - SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data; - - /* The user did not import public key. The buddy is unusable. */ - silcpurple_add_buddy_pk_no(r); - silc_free(r); -} - -static void -silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id) -{ - if (id != 0) { - /* The user did not import public key. The buddy is unusable. */ - silcpurple_add_buddy_pk_no(r); - silc_free(r); - return; - } - - /* Open file selector to select the public key. */ - purple_request_file(r->client->application, _("Open..."), NULL, FALSE, - G_CALLBACK(silcpurple_add_buddy_ask_import), - G_CALLBACK(silcpurple_add_buddy_ask_pk_cancel), - purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r); - -} - -static void -silcpurple_add_buddy_ask_pk(SilcPurpleBuddyRes r) -{ - char tmp[512]; - g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not present in the network"), - purple_buddy_get_name(r->b)); - purple_request_action(r->client->application, _("Add Buddy"), tmp, - _("To add the buddy you must import his/her public key. " - "Press Import to import a public key."), 0, - purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r, 2, - _("Cancel"), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb), - _("_Import..."), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb)); -} - -static SilcBool -silcpurple_add_buddy_getkey_cb(SilcClient client, SilcClientConnection conn, - SilcCommand command, SilcStatus status, - SilcStatus error, void *context, va_list ap) -{ - SilcPurpleBuddyRes r = context; - SilcClientEntry client_entry; - - if (status != SILC_STATUS_OK) { - /* The buddy is offline/nonexistent. We will require user - to associate a public key with the buddy or the buddy - cannot be added. */ - r->offline = TRUE; - silcpurple_add_buddy_ask_pk(r); - return FALSE; - } - - /* Get the client entry. */ - client_entry = silc_client_get_client_by_id(r->client, r->conn, - &r->client_id); - if (!client_entry || !client_entry->public_key) { - /* The buddy is offline/nonexistent. We will require user - to associate a public key with the buddy or the buddy - cannot be added. */ - r->offline = TRUE; - silcpurple_add_buddy_ask_pk(r); - return FALSE; - } - - /* Now verify the public key */ - silcpurple_verify_public_key(r->client, r->conn, client_entry->nickname, - SILC_CONN_CLIENT, client_entry->public_key, - silcpurple_add_buddy_save, r); - return TRUE; -} - -static void -silcpurple_add_buddy_select_cb(SilcPurpleBuddyRes r, PurpleRequestFields *fields) -{ - PurpleRequestField *f; - GList *list; - SilcClientEntry client_entry; - SilcDList clients; - - f = purple_request_fields_get_field(fields, "list"); - list = purple_request_field_list_get_selected(f); - if (!list) { - /* The user did not select any user. */ - silcpurple_add_buddy_pk_no(r); - silc_free(r); - return; - } - - client_entry = purple_request_field_list_get_data(f, list->data); - clients = silc_dlist_init(); - silc_dlist_add(clients, client_entry); - silcpurple_add_buddy_resolved(r->client, r->conn, SILC_STATUS_OK, - clients, r); - silc_dlist_uninit(clients); -} - -static void -silcpurple_add_buddy_select_cancel(SilcPurpleBuddyRes r, PurpleRequestFields *fields) -{ - /* The user did not select any user. */ - silcpurple_add_buddy_pk_no(r); - silc_free(r); -} - -static void -silcpurple_add_buddy_select(SilcPurpleBuddyRes r, SilcDList clients) -{ - PurpleRequestFields *fields; - PurpleRequestFieldGroup *g; - PurpleRequestField *f; - char tmp[512], tmp2[128]; - char *fingerprint; - SilcClientEntry client_entry; - - fields = purple_request_fields_new(); - g = purple_request_field_group_new(NULL); - f = purple_request_field_list_new("list", NULL); - purple_request_field_group_add_field(g, f); - purple_request_field_list_set_multi_select(f, FALSE); - purple_request_fields_add_group(fields, g); - - silc_dlist_start(clients); - while ((client_entry = silc_dlist_get(clients))) { - fingerprint = NULL; - if (*client_entry->fingerprint) { - fingerprint = silc_fingerprint(client_entry->fingerprint, 20); - g_snprintf(tmp2, sizeof(tmp2), "\n%s", fingerprint); - } - g_snprintf(tmp, sizeof(tmp), "%s - %s (%s@%s)%s", - client_entry->realname, client_entry->nickname, - client_entry->username, *client_entry->hostname ? - client_entry->hostname : "", - fingerprint ? tmp2 : ""); - purple_request_field_list_add_icon(f, tmp, NULL, client_entry); - silc_free(fingerprint); - } - - purple_request_fields(r->client->application, _("Add Buddy"), - _("Select correct user"), - r->pubkey_search - ? _("More than one user was found with the same public key. Select " - "the correct user from the list to add to the buddy list.") - : _("More than one user was found with the same name. Select " - "the correct user from the list to add to the buddy list."), - fields, - _("OK"), G_CALLBACK(silcpurple_add_buddy_select_cb), - _("Cancel"), G_CALLBACK(silcpurple_add_buddy_select_cancel), - purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r); -} - -static void -silcpurple_add_buddy_resolved(SilcClient client, - SilcClientConnection conn, - SilcStatus status, - SilcDList clients, - void *context) -{ - SilcPurpleBuddyRes r = context; - PurpleBuddy *b = r->b; - SilcAttributePayload pub; - SilcAttributeObjPk userpk; - const char *filename; - SilcClientEntry client_entry = NULL; - SilcUInt16 cmd_ident; - const char *name; - - filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key"); - - /* If the buddy is offline/nonexistent, we will require user - to associate a public key with the buddy or the buddy - cannot be added. */ - if (!clients) { - if (r->init) { - silc_free(r); - return; - } - - r->offline = TRUE; - /* If the user has already associated a public key, try loading it - * before prompting the user to load it again */ - if (filename != NULL) - silcpurple_add_buddy_ask_import(r, filename); - else - silcpurple_add_buddy_ask_pk(r); - return; - } - - /* If more than one client was found with nickname, we need to verify - from user which one is the correct. */ - if (silc_dlist_count(clients) > 1 && !r->pubkey_search) { - if (r->init) { - silc_free(r); - return; - } - - silcpurple_add_buddy_select(r, clients); - return; - } - - silc_dlist_start(clients); - client_entry = silc_dlist_get(clients); - - name = purple_buddy_get_name(b); - - /* If we searched using public keys and more than one entry was found - the same person is logged on multiple times. */ - if (silc_dlist_count(clients) > 1 && r->pubkey_search && name) { - if (r->init) { - /* Find the entry that closest matches to the - buddy nickname. */ - SilcClientEntry entry; - silc_dlist_start(clients); - while ((entry = silc_dlist_get(clients))) { - if (!g_ascii_strncasecmp(name, entry->nickname, - strlen(name))) { - client_entry = entry; - break; - } - } - } else { - /* Verify from user which one is correct */ - silcpurple_add_buddy_select(r, clients); - return; - } - } - - /* The client was found. Now get its public key and verify - that before adding the buddy. */ - memset(&userpk, 0, sizeof(userpk)); - purple_buddy_set_protocol_data(b, silc_memdup(&client_entry->id, sizeof(client_entry->id))); - r->client_id = client_entry->id; - - /* Get the public key from attributes, if not present then - resolve it with GETKEY unless we have it cached already. */ - if (client_entry->attrs && !client_entry->public_key) { - pub = silcpurple_get_attr(client_entry->attrs, - SILC_ATTRIBUTE_USER_PUBLIC_KEY); - if (!pub || !silc_attribute_get_object(pub, (void *)&userpk, - sizeof(userpk))) { - /* Get public key with GETKEY */ - cmd_ident = - silc_client_command_call(client, conn, NULL, - "GETKEY", client_entry->nickname, NULL); - silc_client_command_pending(conn, SILC_COMMAND_GETKEY, - cmd_ident, - silcpurple_add_buddy_getkey_cb, - r); - return; - } - if (!silc_pkcs_public_key_alloc(SILC_PKCS_SILC, - userpk.data, userpk.data_len, - &client_entry->public_key)) - return; - silc_free(userpk.data); - } else if (filename && !client_entry->public_key) { - if (!silc_pkcs_load_public_key(filename, &client_entry->public_key)) { - /* Get public key with GETKEY */ - cmd_ident = - silc_client_command_call(client, conn, NULL, - "GETKEY", client_entry->nickname, NULL); - silc_client_command_pending(conn, SILC_COMMAND_GETKEY, - cmd_ident, - silcpurple_add_buddy_getkey_cb, - r); - return; - } - } else if (!client_entry->public_key) { - /* Get public key with GETKEY */ - cmd_ident = - silc_client_command_call(client, conn, NULL, - "GETKEY", client_entry->nickname, NULL); - silc_client_command_pending(conn, SILC_COMMAND_GETKEY, - cmd_ident, - silcpurple_add_buddy_getkey_cb, - r); - return; - } - - /* We have the public key, verify it. */ - silcpurple_verify_public_key(client, conn, client_entry->nickname, - SILC_CONN_CLIENT, - client_entry->public_key, - silcpurple_add_buddy_save, r); -} - -static void -silcpurple_add_buddy_i(PurpleConnection *gc, PurpleBuddy *b, gboolean init) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcPurpleBuddyRes r; - SilcBuffer attrs; - const char *filename, *name = purple_buddy_get_name(b); - - r = silc_calloc(1, sizeof(*r)); - if (!r) - return; - r->client = client; - r->conn = conn; - r->b = b; - r->init = init; - - /* See if we have this buddy's public key. If we do use that - to search the details. */ - filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key"); - if (filename) { - SilcPublicKey public_key; - SilcAttributeObjPk userpk; - - if (!silc_pkcs_load_public_key(filename, &public_key)) - return; - - /* Get all attributes, and use the public key to search user */ - name = NULL; - attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO, - SILC_ATTRIBUTE_SERVICE, - SILC_ATTRIBUTE_STATUS_MOOD, - SILC_ATTRIBUTE_STATUS_FREETEXT, - SILC_ATTRIBUTE_STATUS_MESSAGE, - SILC_ATTRIBUTE_PREFERRED_LANGUAGE, - SILC_ATTRIBUTE_PREFERRED_CONTACT, - SILC_ATTRIBUTE_TIMEZONE, - SILC_ATTRIBUTE_GEOLOCATION, - SILC_ATTRIBUTE_USER_ICON, - SILC_ATTRIBUTE_DEVICE_INFO, 0); - userpk.type = "silc-rsa"; - userpk.data = silc_pkcs_public_key_encode(public_key, &userpk.data_len); - attrs = silc_attribute_payload_encode(attrs, - SILC_ATTRIBUTE_USER_PUBLIC_KEY, - SILC_ATTRIBUTE_FLAG_VALID, - &userpk, sizeof(userpk)); - silc_free(userpk.data); - silc_pkcs_public_key_free(public_key); - r->pubkey_search = TRUE; - } else { - /* Get all attributes */ - attrs = silc_client_attributes_request(0); - } - - /* Resolve */ - silc_client_get_clients_whois(client, conn, name, NULL, attrs, - silcpurple_add_buddy_resolved, r); - silc_buffer_free(attrs); -} - -void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) -{ - /* Don't add if the buddy is already on the list. - * - * SILC doesn't have groups, so we also don't need to do anything - * for a move. */ - if (purple_buddy_get_protocol_data(buddy) == NULL) - silcpurple_add_buddy_i(gc, buddy, FALSE); -} - -void silcpurple_send_buddylist(PurpleConnection *gc) -{ - GSList *buddies; - PurpleAccount *account; - - account = purple_connection_get_account(gc); - - for (buddies = purple_find_buddies(account, NULL); buddies; - buddies = g_slist_delete_link(buddies, buddies)) - { - PurpleBuddy *buddy = buddies->data; - silcpurple_add_buddy_i(gc, buddy, TRUE); - } -} - -void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, - PurpleGroup *group) -{ - silc_free(purple_buddy_get_protocol_data(buddy)); -} - -void silcpurple_idle_set(PurpleConnection *gc, int idle) - -{ - SilcPurple sg; - SilcClient client; - SilcClientConnection conn; - SilcAttributeObjService service; - const char *server; - int port; - - sg = gc->proto_data; - if (sg == NULL) - return; - - client = sg->client; - if (client == NULL) - return; - - conn = sg->conn; - if (conn == NULL) - return; - - server = purple_account_get_string(sg->account, "server", - "silc.silcnet.org"); - port = purple_account_get_int(sg->account, "port", 706), - - memset(&service, 0, sizeof(service)); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_SERVICE, NULL); - service.port = port; - g_snprintf(service.address, sizeof(service.address), "%s", server); - service.idle = idle; - silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_SERVICE, - &service, sizeof(service)); -} - -char *silcpurple_status_text(PurpleBuddy *b) -{ - PurpleAccount *account = purple_buddy_get_account(b); - PurpleConnection *gc = purple_account_get_connection(account); - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcClientID *client_id = purple_buddy_get_protocol_data(b); - SilcClientEntry client_entry; - SilcAttributePayload attr; - SilcAttributeMood mood = 0; - - /* Get the client entry. */ - client_entry = silc_client_get_client_by_id(client, conn, client_id); - if (!client_entry) - return NULL; - - /* If user is online, we show the mood status, if available. - If user is offline or away that status is indicated. */ - - if (client_entry->mode & SILC_UMODE_DETACHED) - return g_strdup(_("Detached")); - if (client_entry->mode & SILC_UMODE_GONE) - return g_strdup(_("Away")); - if (client_entry->mode & SILC_UMODE_INDISPOSED) - return g_strdup(_("Indisposed")); - if (client_entry->mode & SILC_UMODE_BUSY) - return g_strdup(_("Busy")); - if (client_entry->mode & SILC_UMODE_PAGE) - return g_strdup(_("Wake Me Up")); - if (client_entry->mode & SILC_UMODE_HYPER) - return g_strdup(_("Hyper Active")); - if (client_entry->mode & SILC_UMODE_ROBOT) - return g_strdup(_("Robot")); - - attr = silcpurple_get_attr(client_entry->attrs, SILC_ATTRIBUTE_STATUS_MOOD); - if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) { - /* The mood is a bit mask, so we could show multiple moods, - but let's show only one for now. */ - if (mood & SILC_ATTRIBUTE_MOOD_HAPPY) - return g_strdup(_("Happy")); - if (mood & SILC_ATTRIBUTE_MOOD_SAD) - return g_strdup(_("Sad")); - if (mood & SILC_ATTRIBUTE_MOOD_ANGRY) - return g_strdup(_("Angry")); - if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS) - return g_strdup(_("Jealous")); - if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED) - return g_strdup(_("Ashamed")); - if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE) - return g_strdup(_("Invincible")); - if (mood & SILC_ATTRIBUTE_MOOD_INLOVE) - return g_strdup(_("In Love")); - if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY) - return g_strdup(_("Sleepy")); - if (mood & SILC_ATTRIBUTE_MOOD_BORED) - return g_strdup(_("Bored")); - if (mood & SILC_ATTRIBUTE_MOOD_EXCITED) - return g_strdup(_("Excited")); - if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS) - return g_strdup(_("Anxious")); - } - - return NULL; -} - -void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) -{ - PurpleAccount *account = purple_buddy_get_account(b); - PurpleConnection *gc = purple_account_get_connection(account); - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcClientID *client_id = purple_buddy_get_protocol_data(b); - SilcClientEntry client_entry; - char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr; - char tmp[256]; - - /* Get the client entry. */ - client_entry = silc_client_get_client_by_id(client, conn, client_id); - if (!client_entry) - return; - - purple_notify_user_info_add_pair(user_info, _("Nickname"), - client_entry->nickname); - g_snprintf(tmp, sizeof(tmp), "%s@%s", client_entry->username, client_entry->hostname); - purple_notify_user_info_add_pair(user_info, _("Username"), tmp); - if (client_entry->mode) { - memset(tmp, 0, sizeof(tmp)); - silcpurple_get_umode_string(client_entry->mode, - tmp, sizeof(tmp) - strlen(tmp)); - purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp); - } - - silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr); - - if (statusstr) { - purple_notify_user_info_add_pair(user_info, _("Message"), statusstr); - g_free(statusstr); - } - - if (full) { - if (moodstr) { - purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr); - g_free(moodstr); - } - - if (contactstr) { - purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr); - g_free(contactstr); - } - - if (langstr) { - purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr); - g_free(langstr); - } - - if (devicestr) { - purple_notify_user_info_add_pair(user_info, _("Device"), devicestr); - g_free(devicestr); - } - - if (tzstr) { - purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr); - g_free(tzstr); - } - - if (geostr) { - purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr); - g_free(geostr); - } - } -} - -static void -silcpurple_buddy_kill(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *b; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - b = (PurpleBuddy *) node; - gc = purple_account_get_connection(purple_buddy_get_account(b)); - sg = gc->proto_data; - - /* Call KILL */ - silc_client_command_call(sg->client, sg->conn, NULL, "KILL", - purple_buddy_get_name(b), "Killed by operator", NULL); -} - -typedef struct { - SilcPurple sg; - SilcClientEntry client_entry; -} *SilcPurpleBuddyWb; - -static void -silcpurple_buddy_wb(PurpleBlistNode *node, gpointer data) -{ - SilcPurpleBuddyWb wb = data; - silcpurple_wb_init(wb->sg, wb->client_entry); - silc_free(wb); -} - -GList *silcpurple_buddy_menu(PurpleBuddy *buddy) -{ - PurpleAccount *account = purple_buddy_get_account(buddy); - PurpleConnection *gc = purple_account_get_connection(account); - SilcPurple sg = gc->proto_data; - SilcClientConnection conn = sg->conn; - const char *pkfile = NULL; - SilcClientEntry client_entry = NULL; - PurpleMenuAction *act; - GList *m = NULL; - SilcPurpleBuddyWb wb; - - pkfile = purple_blist_node_get_string((PurpleBlistNode *) buddy, "public-key"); - client_entry = silc_client_get_client_by_id(sg->client, - sg->conn, - purple_buddy_get_protocol_data(buddy)); - - if (client_entry && - silc_client_private_message_key_is_set(sg->client, - sg->conn, client_entry)) { - act = purple_menu_action_new(_("Reset IM Key"), - PURPLE_CALLBACK(silcpurple_buddy_resetkey), - NULL, NULL); - m = g_list_append(m, act); - } else { - act = purple_menu_action_new(_("IM with Key Exchange"), - PURPLE_CALLBACK(silcpurple_buddy_keyagr), - NULL, NULL); - m = g_list_append(m, act); - - act = purple_menu_action_new(_("IM with Password"), - PURPLE_CALLBACK(silcpurple_buddy_privkey_menu), - NULL, NULL); - m = g_list_append(m, act); - } - - if (pkfile) { - act = purple_menu_action_new(_("Show Public Key"), - PURPLE_CALLBACK(silcpurple_buddy_showkey), - NULL, NULL); - m = g_list_append(m, act); - - } else { - act = purple_menu_action_new(_("Get Public Key..."), - PURPLE_CALLBACK(silcpurple_buddy_getkey_menu), - NULL, NULL); - m = g_list_append(m, act); - } - - if (conn && conn->local_entry->mode & SILC_UMODE_ROUTER_OPERATOR) { - act = purple_menu_action_new(_("Kill User"), - PURPLE_CALLBACK(silcpurple_buddy_kill), - NULL, NULL); - m = g_list_append(m, act); - } - - if (client_entry) { - wb = silc_calloc(1, sizeof(*wb)); - wb->sg = sg; - wb->client_entry = client_entry; - act = purple_menu_action_new(_("Draw On Whiteboard"), - PURPLE_CALLBACK(silcpurple_buddy_wb), - (void *)wb, NULL); - m = g_list_append(m, act); - } - return m; -} - -void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcMime mime; - char type[32]; - const char *t; - - /* Remove */ - if (!img) { - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_USER_ICON, NULL); - return; - } - - /* Add */ - mime = silc_mime_alloc(); - if (!mime) - return; - - t = purple_imgstore_get_extension(img); - if (!t || purple_strequal(t, "icon")) { - silc_mime_free(mime); - return; - } - if (purple_strequal(t, "jpg")) - t = "jpeg"; - g_snprintf(type, sizeof(type), "image/%s", t); - silc_mime_add_field(mime, "Content-Type", type); - silc_mime_add_data(mime, purple_imgstore_get_data(img), purple_imgstore_get_size(img)); - - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_USER_ICON, mime, sizeof(*mime)); - - silc_mime_free(mime); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc/chat.c --- a/libpurple/protocols/silc/chat.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1435 +0,0 @@ -/* - - silcpurple_chat.c - - Author: Pekka Riikonen - - Copyright (C) 2004 - 2007 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#include "internal.h" -#include "silc.h" -#include "silcclient.h" -#include "silcpurple.h" -#include "wb.h" - -/***************************** Channel Routines ******************************/ - -GList *silcpurple_chat_info(PurpleConnection *gc) -{ - GList *ci = NULL; - struct proto_chat_entry *pce; - - pce = g_new0(struct proto_chat_entry, 1); - pce->label = _("_Channel:"); - pce->identifier = "channel"; - pce->required = TRUE; - ci = g_list_append(ci, pce); - - pce = g_new0(struct proto_chat_entry, 1); - pce->label = _("_Passphrase:"); - pce->identifier = "passphrase"; - pce->secret = TRUE; - ci = g_list_append(ci, pce); - - return ci; -} - -GHashTable *silcpurple_chat_info_defaults(PurpleConnection *gc, const char *chat_name) -{ - GHashTable *defaults; - - defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); - - if (chat_name != NULL) - g_hash_table_insert(defaults, "channel", g_strdup(chat_name)); - - return defaults; -} - -static void -silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components); - -static void -silcpurple_chat_getinfo_res(SilcClient client, - SilcClientConnection conn, - SilcStatus status, - SilcDList channels, - void *context) -{ - GHashTable *components = context; - PurpleConnection *gc = client->application; - const char *chname; - char tmp[256]; - - chname = g_hash_table_lookup(components, "channel"); - if (!chname) - return; - - if (!channels) { - g_snprintf(tmp, sizeof(tmp), - _("Channel %s does not exist in the network"), chname); - purple_notify_error(gc, _("Channel Information"), - _("Cannot get channel information"), tmp); - return; - } - - silcpurple_chat_getinfo(gc, components); -} - - -static void -silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components) -{ - SilcPurple sg = gc->proto_data; - const char *chname; - char tmp[256], *tmp2; - GString *s; - SilcChannelEntry channel; - SilcHashTableList htl; - SilcChannelUser chu; - - if (!components) - return; - - chname = g_hash_table_lookup(components, "channel"); - if (!chname) - return; - channel = silc_client_get_channel(sg->client, sg->conn, - (char *)chname); - if (!channel) { - silc_client_get_channel_resolve(sg->client, sg->conn, - (char *)chname, - silcpurple_chat_getinfo_res, - components); - return; - } - - s = g_string_new(""); - tmp2 = g_markup_escape_text(channel->channel_name, -1); - g_string_append_printf(s, _("Channel Name: %s"), tmp2); - g_free(tmp2); - if (channel->user_list && silc_hash_table_count(channel->user_list)) - g_string_append_printf(s, _("
User Count: %d"), - (int)silc_hash_table_count(channel->user_list)); - - silc_hash_table_list(channel->user_list, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) { - tmp2 = g_markup_escape_text(chu->client->nickname, -1); - g_string_append_printf(s, _("
Channel Founder: %s"), - tmp2); - g_free(tmp2); - break; - } - } - silc_hash_table_list_reset(&htl); - - if (channel->cipher) - g_string_append_printf(s, _("
Channel Cipher: %s"), - channel->cipher); - - if (channel->hmac) - /* Definition of HMAC: http://en.wikipedia.org/wiki/HMAC */ - g_string_append_printf(s, _("
Channel HMAC: %s"), - channel->hmac); - - if (channel->topic) { - tmp2 = g_markup_escape_text(channel->topic, -1); - g_string_append_printf(s, _("
Channel Topic:
%s"), tmp2); - g_free(tmp2); - } - - if (channel->mode) { - g_string_append(s, _("
Channel Modes: ")); - silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp)); - g_string_append(s, tmp); - } - - if (channel->founder_key) { - char *fingerprint, *babbleprint; - unsigned char *pk; - SilcUInt32 pk_len; - pk = silc_pkcs_public_key_encode(channel->founder_key, &pk_len); - if (pk) { - fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); - - g_string_append_printf(s, _("
Founder Key Fingerprint:
%s"), fingerprint); - g_string_append_printf(s, _("
Founder Key Babbleprint:
%s"), babbleprint); - - silc_free(fingerprint); - silc_free(babbleprint); - silc_free(pk); - } - } - - purple_notify_formatted(gc, NULL, _("Channel Information"), NULL, s->str, NULL, NULL); - g_string_free(s, TRUE); -} - - -static void -silcpurple_chat_getinfo_menu(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat = (PurpleChat *)node; - PurpleAccount *account = purple_chat_get_account(chat); - silcpurple_chat_getinfo(purple_account_get_connection(account), - purple_chat_get_components(chat)); -} - - -#if 0 /* XXX For now these are not implemented. We need better - listview dialog from Purple for these. */ -/************************** Channel Invite List ******************************/ - -static void -silcpurple_chat_invitelist(PurpleBlistNode *node, gpointer data); -{ - -} - - -/**************************** Channel Ban List *******************************/ - -static void -silcpurple_chat_banlist(PurpleBlistNode *node, gpointer data); -{ - -} -#endif - - -/************************* Channel Authentication ****************************/ - -typedef struct { - SilcPurple sg; - SilcChannelEntry channel; - PurpleChat *c; - SilcDList pubkeys; -} *SilcPurpleChauth; - -static void -silcpurple_chat_chpk_add(void *user_data, const char *name) -{ - SilcPurpleChauth sgc = (SilcPurpleChauth)user_data; - SilcPurple sg = sgc->sg; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcPublicKey public_key; - SilcBuffer chpks, pk, chidp; - unsigned char mode[4]; - SilcUInt32 m; - - /* Load the public key */ - if (!silc_pkcs_load_public_key(name, &public_key)) { - silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys); - silc_dlist_uninit(sgc->pubkeys); - silc_free(sgc); - purple_notify_error(client->application, - _("Add Channel Public Key"), - _("Could not load public key"), NULL); - return; - } - - pk = silc_public_key_payload_encode(public_key); - chpks = silc_buffer_alloc_size(2); - SILC_PUT16_MSB(1, chpks->head); - chpks = silc_argument_payload_encode_one(chpks, pk->data, - silc_buffer_len(pk), 0x00); - silc_buffer_free(pk); - - m = sgc->channel->mode; - m |= SILC_CHANNEL_MODE_CHANNEL_AUTH; - - /* Send CMODE */ - SILC_PUT32_MSB(m, mode); - chidp = silc_id_payload_encode(&sgc->channel->id, SILC_ID_CHANNEL); - silc_client_command_send(client, conn, SILC_COMMAND_CMODE, - silcpurple_command_reply, NULL, 3, - 1, chidp->data, silc_buffer_len(chidp), - 2, mode, sizeof(mode), - 9, chpks->data, silc_buffer_len(chpks)); - silc_buffer_free(chpks); - silc_buffer_free(chidp); - if (sgc->pubkeys) { - silc_dlist_start(sgc->pubkeys); - while ((public_key = silc_dlist_get(sgc->pubkeys))) - silc_pkcs_public_key_free(public_key); - silc_dlist_uninit(sgc->pubkeys); - } - silc_free(sgc); -} - -static void -silcpurple_chat_chpk_cancel(void *user_data, const char *name) -{ - SilcPurpleChauth sgc = (SilcPurpleChauth)user_data; - SilcPublicKey public_key; - - silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys); - - if (sgc->pubkeys) { - silc_dlist_start(sgc->pubkeys); - while ((public_key = silc_dlist_get(sgc->pubkeys))) - silc_pkcs_public_key_free(public_key); - silc_dlist_uninit(sgc->pubkeys); - } - silc_free(sgc); -} - -static void -silcpurple_chat_chpk_cb(SilcPurpleChauth sgc, PurpleRequestFields *fields) -{ - SilcPurple sg = sgc->sg; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - PurpleRequestField *f; - GList *list; - SilcPublicKey public_key; - SilcBuffer chpks, pk, chidp; - SilcUInt16 c = 0, ct; - unsigned char mode[4]; - SilcUInt32 m; - - f = purple_request_fields_get_field(fields, "list"); - if (!purple_request_field_list_get_selected(f)) { - /* Add new public key */ - purple_request_file(sg->gc, _("Open Public Key..."), NULL, FALSE, - G_CALLBACK(silcpurple_chat_chpk_add), - G_CALLBACK(silcpurple_chat_chpk_cancel), - purple_connection_get_account(sg->gc), NULL, NULL, sgc); - return; - } - - list = purple_request_field_list_get_items(f); - chpks = silc_buffer_alloc_size(2); - - for (ct = 0; list; list = list->next, ct++) { - public_key = purple_request_field_list_get_data(f, list->data); - if (purple_request_field_list_is_selected(f, list->data)) { - /* Delete this public key */ - pk = silc_public_key_payload_encode(public_key); - chpks = silc_argument_payload_encode_one(chpks, pk->data, - silc_buffer_len(pk), 0x01); - silc_buffer_free(pk); - c++; - } - } - if (!c) { - silc_buffer_free(chpks); - return; - } - SILC_PUT16_MSB(c, chpks->head); - - m = sgc->channel->mode; - if (ct == c) - m &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH; - - /* Send CMODE */ - SILC_PUT32_MSB(m, mode); - chidp = silc_id_payload_encode(&sgc->channel->id, SILC_ID_CHANNEL); - silc_client_command_send(client, conn, SILC_COMMAND_CMODE, - silcpurple_command_reply, NULL, 3, - 1, chidp->data, silc_buffer_len(chidp), - 2, mode, sizeof(mode), - 9, chpks->data, silc_buffer_len(chpks)); - silc_buffer_free(chpks); - silc_buffer_free(chidp); - if (sgc->pubkeys) { - silc_dlist_start(sgc->pubkeys); - while ((public_key = silc_dlist_get(sgc->pubkeys))) - silc_pkcs_public_key_free(public_key); - silc_dlist_uninit(sgc->pubkeys); - } - silc_free(sgc); -} - -static void -silcpurple_chat_chauth_ok(SilcPurpleChauth sgc, PurpleRequestFields *fields) -{ - SilcPurple sg = sgc->sg; - PurpleRequestField *f; - SilcPublicKey public_key; - const char *curpass, *val; - int set; - - f = purple_request_fields_get_field(fields, "passphrase"); - val = purple_request_field_string_get_value(f); - curpass = purple_blist_node_get_string((PurpleBlistNode *)sgc->c, "passphrase"); - - if (!val && curpass) - set = 0; - else if (val && !curpass) - set = 1; - else if (val && curpass && !purple_strequal(val, curpass)) - set = 1; - else - set = -1; - - if (set == 1) { - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - sgc->channel->channel_name, "+a", val, NULL); - purple_blist_node_set_string((PurpleBlistNode *)sgc->c, "passphrase", val); - } else if (set == 0) { - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - sgc->channel->channel_name, "-a", NULL); - purple_blist_node_remove_setting((PurpleBlistNode *)sgc->c, "passphrase"); - } - - if (sgc->pubkeys) { - silc_dlist_start(sgc->pubkeys); - while ((public_key = silc_dlist_get(sgc->pubkeys))) - silc_pkcs_public_key_free(public_key); - silc_dlist_uninit(sgc->pubkeys); - } - silc_free(sgc); -} - -void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel, - SilcDList channel_pubkeys) -{ - SilcPublicKey public_key; - SilcSILCPublicKey silc_pubkey; - unsigned char *pk; - SilcUInt32 pk_len; - char *fingerprint, *babbleprint; - SilcPublicKeyIdentifier ident; - char tmp2[1024], t[512]; - PurpleRequestFields *fields; - PurpleRequestFieldGroup *g; - PurpleRequestField *f; - SilcPurpleChauth sgc; - const char *curpass = NULL; - - sgc = silc_calloc(1, sizeof(*sgc)); - if (!sgc) - return; - sgc->sg = sg; - sgc->channel = channel; - - fields = purple_request_fields_new(); - - if (sgc->c) - curpass = purple_blist_node_get_string((PurpleBlistNode *)sgc->c, "passphrase"); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_string_new("passphrase", _("Channel Passphrase"), - curpass, FALSE); - purple_request_field_string_set_masked(f, TRUE); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_label_new("l1", _("Channel Public Keys List")); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - g_snprintf(t, sizeof(t), - _("Channel authentication is used to secure the channel from " - "unauthorized access. The authentication may be based on " - "passphrase and digital signatures. If passphrase is set, it " - "is required to be able to join. If channel public keys are set " - "then only users whose public keys are listed are able to join.")); - - if (!channel_pubkeys || !silc_dlist_count(channel_pubkeys)) { - f = purple_request_field_list_new("list", NULL); - purple_request_field_group_add_field(g, f); - purple_request_fields(sg->gc, _("Channel Authentication"), - _("Channel Authentication"), t, fields, - _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb), - _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok), - purple_connection_get_account(sg->gc), NULL, NULL, sgc); - if (channel_pubkeys) - silc_dlist_uninit(channel_pubkeys); - return; - } - sgc->pubkeys = channel_pubkeys; - - g = purple_request_field_group_new(NULL); - f = purple_request_field_list_new("list", NULL); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - silc_dlist_start(channel_pubkeys); - while ((public_key = silc_dlist_get(channel_pubkeys))) { - pk = silc_pkcs_public_key_encode(public_key, &pk_len); - if (!pk) - continue; - fingerprint = silc_hash_fingerprint(NULL, pk + 4, pk_len - 4); - babbleprint = silc_hash_babbleprint(NULL, pk + 4, pk_len - 4); - - silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key); - ident = &silc_pubkey->identifier; - - g_snprintf(tmp2, sizeof(tmp2), "%s\n %s\n %s", - ident->realname ? ident->realname : ident->username ? - ident->username : "", fingerprint, babbleprint); - purple_request_field_list_add_icon(f, tmp2, NULL, public_key); - - silc_free(fingerprint); - silc_free(babbleprint); - } - - purple_request_field_list_set_multi_select(f, FALSE); - purple_request_fields(sg->gc, _("Channel Authentication"), - _("Channel Authentication"), t, fields, - _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb), - _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok), - purple_connection_get_account(sg->gc), NULL, NULL, sgc); -} - -static void -silcpurple_chat_chauth(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(purple_chat_get_account(chat)); - sg = gc->proto_data; - - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - g_hash_table_lookup(purple_chat_get_components(chat), "channel"), - "+C", NULL); -} - - -/************************** Channel Private Groups **************************/ - -/* Private groups are "virtual" channels. They are groups inside a channel. - This is implemented by using channel private keys. By knowing a channel - private key user becomes part of that group and is able to talk on that - group. Other users, on the same channel, won't be able to see the - messages of that group. It is possible to have multiple groups inside - a channel - and thus having multiple private keys on the channel. */ - -typedef struct { - SilcPurple sg; - PurpleChat *c; - const char *channel; -} *SilcPurpleCharPrv; - -static void -silcpurple_chat_prv_add(SilcPurpleCharPrv p, PurpleRequestFields *fields) -{ - SilcPurple sg = p->sg; - char tmp[512]; - PurpleRequestField *f; - const char *name, *passphrase, *alias; - GHashTable *comp; - PurpleGroup *g; - PurpleChat *cn; - - f = purple_request_fields_get_field(fields, "name"); - name = purple_request_field_string_get_value(f); - if (!name) { - silc_free(p); - return; - } - f = purple_request_fields_get_field(fields, "passphrase"); - passphrase = purple_request_field_string_get_value(f); - f = purple_request_fields_get_field(fields, "alias"); - alias = purple_request_field_string_get_value(f); - - /* Add private group to buddy list */ - g_snprintf(tmp, sizeof(tmp), "%s [Private Group]", name); - comp = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); - g_hash_table_replace(comp, "channel", g_strdup(tmp)); - g_hash_table_replace(comp, "passphrase", g_strdup(passphrase)); - - cn = purple_chat_new(sg->account, alias, comp); - g = purple_chat_get_group(p->c); - purple_blist_add_chat(cn, g, (PurpleBlistNode *)p->c); - - /* Associate to a real channel */ - purple_blist_node_set_string((PurpleBlistNode *)cn, "parentch", p->channel); - - /* Join the group */ - silcpurple_chat_join(sg->gc, comp); - - silc_free(p); -} - -static void -silcpurple_chat_prv_cancel(SilcPurpleCharPrv p, PurpleRequestFields *fields) -{ - silc_free(p); -} - -static void -silcpurple_chat_prv(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - SilcPurpleCharPrv p; - PurpleRequestFields *fields; - PurpleRequestFieldGroup *g; - PurpleRequestField *f; - char tmp[512]; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(purple_chat_get_account(chat)); - sg = gc->proto_data; - - p = silc_calloc(1, sizeof(*p)); - if (!p) - return; - p->sg = sg; - - p->channel = g_hash_table_lookup(purple_chat_get_components(chat), "channel"); - p->c = purple_blist_find_chat(sg->account, p->channel); - - fields = purple_request_fields_new(); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_string_new("name", _("Group Name"), - NULL, FALSE); - purple_request_field_group_add_field(g, f); - - f = purple_request_field_string_new("passphrase", _("Passphrase"), - NULL, FALSE); - purple_request_field_string_set_masked(f, TRUE); - purple_request_field_group_add_field(g, f); - - f = purple_request_field_string_new("alias", _("Alias"), - NULL, FALSE); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - g_snprintf(tmp, sizeof(tmp), - _("Please enter the %s channel private group name and passphrase."), - p->channel); - purple_request_fields(gc, _("Add Channel Private Group"), NULL, tmp, fields, - _("Add"), G_CALLBACK(silcpurple_chat_prv_add), - _("Cancel"), G_CALLBACK(silcpurple_chat_prv_cancel), - purple_connection_get_account(gc), NULL, NULL, p); -} - - -/****************************** Channel Modes ********************************/ - -static void -silcpurple_chat_permanent_reset(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(purple_chat_get_account(chat)); - sg = gc->proto_data; - - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - g_hash_table_lookup(purple_chat_get_components(chat), "channel"), - "-f", NULL); -} - -static void -silcpurple_chat_permanent(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - const char *channel; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(purple_chat_get_account(chat)); - sg = gc->proto_data; - - if (!sg->conn) - return; - - /* XXX we should have ability to define which founder - key to use. Now we use the user's own public key - (default key). */ - - /* Call CMODE */ - channel = g_hash_table_lookup(purple_chat_get_components(chat), "channel"); - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", channel, - "+f", NULL); -} - -typedef struct { - SilcPurple sg; - char *channel; -} *SilcPurpleChatInput; - -static void -silcpurple_chat_ulimit_cb(SilcPurpleChatInput s, const char *limit) -{ - SilcChannelEntry channel; - guint ulimit = 0; - - channel = silc_client_get_channel(s->sg->client, s->sg->conn, - (char *)s->channel); - if (!channel) - return; - if (limit) - ulimit = strtoul(limit, NULL, 10); - - if (!limit || !(*limit) || *limit == '0') { - if (limit && ulimit == channel->user_limit) { - g_free(s->channel); - silc_free(s); - return; - } - silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE", - s->channel, "-l", NULL); - - g_free(s->channel); - silc_free(s); - return; - } - - if (ulimit == channel->user_limit) { - g_free(s->channel); - silc_free(s); - return; - } - - /* Call CMODE */ - silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE", - s->channel, "+l", limit, NULL); - - g_free(s->channel); - silc_free(s); -} - -static void -silcpurple_chat_ulimit(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - SilcPurpleChatInput s; - SilcChannelEntry channel; - char *ch; - char tmp[32]; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(purple_chat_get_account(chat)); - sg = gc->proto_data; - - if (!sg->conn) - return; - - ch = g_strdup(g_hash_table_lookup(purple_chat_get_components(chat), "channel")); - channel = silc_client_get_channel(sg->client, sg->conn, (char *)ch); - if (!channel) - return; - - s = silc_calloc(1, sizeof(*s)); - if (!s) - return; - s->channel = ch; - s->sg = sg; - g_snprintf(tmp, sizeof(tmp), "%d", (int)channel->user_limit); - purple_request_input(gc, _("User Limit"), NULL, - _("Set user limit on channel. Set to zero to reset user limit."), - tmp, FALSE, FALSE, NULL, - _("OK"), G_CALLBACK(silcpurple_chat_ulimit_cb), - _("Cancel"), G_CALLBACK(silcpurple_chat_ulimit_cb), - purple_connection_get_account(gc), NULL, NULL, s); -} - -static void -silcpurple_chat_resettopic(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(purple_chat_get_account(chat)); - sg = gc->proto_data; - - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - g_hash_table_lookup(purple_chat_get_components(chat), "channel"), - "-t", NULL); -} - -static void -silcpurple_chat_settopic(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(purple_chat_get_account(chat)); - sg = gc->proto_data; - - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - g_hash_table_lookup(purple_chat_get_components(chat), "channel"), - "+t", NULL); -} - -static void -silcpurple_chat_resetprivate(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(purple_chat_get_account(chat)); - sg = gc->proto_data; - - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - g_hash_table_lookup(purple_chat_get_components(chat), "channel"), - "-p", NULL); -} - -static void -silcpurple_chat_setprivate(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(purple_chat_get_account(chat)); - sg = gc->proto_data; - - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - g_hash_table_lookup(purple_chat_get_components(chat), "channel"), - "+p", NULL); -} - -static void -silcpurple_chat_resetsecret(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(purple_chat_get_account(chat)); - sg = gc->proto_data; - - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - g_hash_table_lookup(purple_chat_get_components(chat), "channel"), - "-s", NULL); -} - -static void -silcpurple_chat_setsecret(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(purple_chat_get_account(chat)); - sg = gc->proto_data; - - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - g_hash_table_lookup(purple_chat_get_components(chat), "channel"), - "+s", NULL); -} - -typedef struct { - SilcPurple sg; - SilcChannelEntry channel; -} *SilcPurpleChatWb; - -static void -silcpurple_chat_wb(PurpleBlistNode *node, gpointer data) -{ - SilcPurpleChatWb wb = data; - silcpurple_wb_init_ch(wb->sg, wb->channel); - silc_free(wb); -} - -GList *silcpurple_chat_menu(PurpleChat *chat) -{ - GHashTable *components = purple_chat_get_components(chat); - PurpleConnection *gc = purple_account_get_connection(purple_chat_get_account(chat)); - SilcPurple sg = gc->proto_data; - SilcClientConnection conn = sg->conn; - const char *chname = NULL; - SilcChannelEntry channel = NULL; - SilcChannelUser chu = NULL; - SilcUInt32 mode = 0; - - GList *m = NULL; - PurpleMenuAction *act; - - if (components) - chname = g_hash_table_lookup(components, "channel"); - if (!chname) - return NULL; - channel = silc_client_get_channel(sg->client, sg->conn, - (char *)chname); - if (channel) { - chu = silc_client_on_channel(channel, conn->local_entry); - if (chu) - mode = chu->mode; - } - - if (strstr(chname, "[Private Group]")) - return NULL; - - act = purple_menu_action_new(_("Get Info"), - PURPLE_CALLBACK(silcpurple_chat_getinfo_menu), - NULL, NULL); - m = g_list_append(m, act); - -#if 0 /* XXX For now these are not implemented. We need better - listview dialog from Purple for these. */ - if (mode & SILC_CHANNEL_UMODE_CHANOP) { - act = purple_menu_action_new(_("Invite List"), - PURPLE_CALLBACK(silcpurple_chat_invitelist), - NULL, NULL); - m = g_list_append(m, act); - - act = purple_menu_action_new(_("Ban List"), - PURPLE_CALLBACK(silcpurple_chat_banlist), - NULL, NULL); - m = g_list_append(m, act); - } -#endif - - if (chu) { - act = purple_menu_action_new(_("Add Private Group"), - PURPLE_CALLBACK(silcpurple_chat_prv), - NULL, NULL); - m = g_list_append(m, act); - } - - if (chu && mode & SILC_CHANNEL_UMODE_CHANFO) { - act = purple_menu_action_new(_("Channel Authentication"), - PURPLE_CALLBACK(silcpurple_chat_chauth), - NULL, NULL); - m = g_list_append(m, act); - - if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) { - act = purple_menu_action_new(_("Reset Permanent"), - PURPLE_CALLBACK(silcpurple_chat_permanent_reset), - NULL, NULL); - m = g_list_append(m, act); - } else { - act = purple_menu_action_new(_("Set Permanent"), - PURPLE_CALLBACK(silcpurple_chat_permanent), - NULL, NULL); - m = g_list_append(m, act); - } - } - - if (chu && mode & SILC_CHANNEL_UMODE_CHANOP) { - act = purple_menu_action_new(_("Set User Limit"), - PURPLE_CALLBACK(silcpurple_chat_ulimit), - NULL, NULL); - m = g_list_append(m, act); - - if (channel->mode & SILC_CHANNEL_MODE_TOPIC) { - act = purple_menu_action_new(_("Reset Topic Restriction"), - PURPLE_CALLBACK(silcpurple_chat_resettopic), - NULL, NULL); - m = g_list_append(m, act); - } else { - act = purple_menu_action_new(_("Set Topic Restriction"), - PURPLE_CALLBACK(silcpurple_chat_settopic), - NULL, NULL); - m = g_list_append(m, act); - } - - if (channel->mode & SILC_CHANNEL_MODE_PRIVATE) { - act = purple_menu_action_new(_("Reset Private Channel"), - PURPLE_CALLBACK(silcpurple_chat_resetprivate), - NULL, NULL); - m = g_list_append(m, act); - } else { - act = purple_menu_action_new(_("Set Private Channel"), - PURPLE_CALLBACK(silcpurple_chat_setprivate), - NULL, NULL); - m = g_list_append(m, act); - } - - if (channel->mode & SILC_CHANNEL_MODE_SECRET) { - act = purple_menu_action_new(_("Reset Secret Channel"), - PURPLE_CALLBACK(silcpurple_chat_resetsecret), - NULL, NULL); - m = g_list_append(m, act); - } else { - act = purple_menu_action_new(_("Set Secret Channel"), - PURPLE_CALLBACK(silcpurple_chat_setsecret), - NULL, NULL); - m = g_list_append(m, act); - } - } - - if (chu && channel) { - SilcPurpleChatWb wb; - wb = silc_calloc(1, sizeof(*wb)); - wb->sg = sg; - wb->channel = channel; - act = purple_menu_action_new(_("Draw On Whiteboard"), - PURPLE_CALLBACK(silcpurple_chat_wb), - (void *)wb, NULL); - m = g_list_append(m, act); - } - - return m; -} - - -/******************************* Joining Etc. ********************************/ - -char *silcpurple_get_chat_name(GHashTable *data) -{ - return g_strdup(g_hash_table_lookup(data, "channel")); -} - -void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - const char *channel, *passphrase, *parentch; - - if (!conn) - return; - - channel = g_hash_table_lookup(data, "channel"); - passphrase = g_hash_table_lookup(data, "passphrase"); - - /* Check if we are joining a private group. Handle it - purely locally as it's not a real channel */ - if (strstr(channel, "[Private Group]")) { - SilcChannelEntry channel_entry; - SilcChannelPrivateKey key; - PurpleChat *c; - SilcPurplePrvgrp grp; - - c = purple_blist_find_chat(sg->account, channel); - parentch = purple_blist_node_get_string((PurpleBlistNode *)c, "parentch"); - if (!parentch) - return; - - channel_entry = silc_client_get_channel(sg->client, sg->conn, - (char *)parentch); - if (!channel_entry || - !silc_client_on_channel(channel_entry, sg->conn->local_entry)) { - char tmp[512]; - g_snprintf(tmp, sizeof(tmp), - _("You have to join the %s channel before you are " - "able to join the private group"), parentch); - purple_notify_error(gc, _("Join Private Group"), - _("Cannot join private group"), tmp); - return; - } - - /* Add channel private key */ - if (!silc_client_add_channel_private_key(client, conn, - channel_entry, channel, - NULL, NULL, - (unsigned char *)passphrase, - strlen(passphrase), &key)) - return; - - /* Join the group */ - grp = silc_calloc(1, sizeof(*grp)); - if (!grp) - return; - grp->id = ++sg->channel_ids + SILCPURPLE_PRVGRP; - grp->chid = SILC_PTR_TO_32(channel_entry->context); - grp->parentch = parentch; - grp->channel = channel; - grp->key = key; - sg->grps = g_list_append(sg->grps, grp); - serv_got_joined_chat(gc, grp->id, channel); - return; - } - - /* XXX We should have other properties here as well: - 1. whether to try to authenticate to the channel - 1a. with default key, - 1b. with specific key. - 2. whether to try to authenticate to become founder. - 2a. with default key, - 2b. with specific key. - - Since now such variety is not possible in the join dialog - we always use -founder and -auth options, which try to - do both 1 and 2 with default keys. */ - - /* Call JOIN */ - if ((passphrase != NULL) && (*passphrase != '\0')) - silc_client_command_call(client, conn, NULL, "JOIN", - channel, passphrase, "-auth", "-founder", NULL); - else - silc_client_command_call(client, conn, NULL, "JOIN", - channel, "-auth", "-founder", NULL); -} - -void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg, - const char *name) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcHashTableList htl; - SilcChannelUser chu; - gboolean found = FALSE; - - if (!conn) - return; - - /* See if we are inviting on a private group. Invite - to the actual channel */ - if (id > SILCPURPLE_PRVGRP) { - GList *l; - SilcPurplePrvgrp prv; - - for (l = sg->grps; l; l = l->next) - if (((SilcPurplePrvgrp)l->data)->id == (gulong)id) - break; - if (!l) - return; - prv = l->data; - id = prv->chid; - } - - /* Find channel by id */ - silc_hash_table_list(conn->local_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) { - found = TRUE; - break; - } - } - silc_hash_table_list_reset(&htl); - if (!found) - return; - - /* Call INVITE */ - silc_client_command_call(client, conn, NULL, "INVITE", - chu->channel->channel_name, - name, NULL); -} - -void silcpurple_chat_leave(PurpleConnection *gc, int id) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcHashTableList htl; - SilcChannelUser chu; - gboolean found = FALSE; - GList *l; - SilcPurplePrvgrp prv; - - if (!conn) - return; - - /* See if we are leaving a private group */ - if (id > SILCPURPLE_PRVGRP) { - SilcChannelEntry channel; - - for (l = sg->grps; l; l = l->next) - if (((SilcPurplePrvgrp)l->data)->id == (gulong)id) - break; - if (!l) - return; - prv = l->data; - channel = silc_client_get_channel(sg->client, sg->conn, - (char *)prv->parentch); - if (!channel) - return; - silc_client_del_channel_private_key(client, conn, - channel, prv->key); - silc_free(prv); - sg->grps = g_list_remove(sg->grps, prv); - serv_got_chat_left(gc, id); - return; - } - - /* Find channel by id */ - silc_hash_table_list(conn->local_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) { - found = TRUE; - break; - } - } - silc_hash_table_list_reset(&htl); - if (!found) - return; - - /* Call LEAVE */ - silc_client_command_call(client, conn, NULL, "LEAVE", - chu->channel->channel_name, NULL); - - serv_got_chat_left(gc, id); - - /* Leave from private groups on this channel as well */ - for (l = sg->grps; l; l = l->next) - if (((SilcPurplePrvgrp)l->data)->chid == (gulong)id) { - prv = l->data; - silc_client_del_channel_private_key(client, conn, - chu->channel, - prv->key); - serv_got_chat_left(gc, prv->id); - silc_free(prv); - sg->grps = g_list_remove(sg->grps, prv); - if (!sg->grps) - break; - } -} - -int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg, - PurpleMessageFlags msgflags) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcHashTableList htl; - SilcChannelUser chu; - SilcChannelEntry channel = NULL; - SilcChannelPrivateKey key = NULL; - SilcMessageFlags flags; - int ret = 0; - char *msg2, *tmp; - gboolean found = FALSE; - gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE); - SilcDList list; - - if (!msg || !conn) - return 0; - - flags = SILC_MESSAGE_FLAG_UTF8; - - tmp = msg2 = purple_unescape_html(msg); - - if (!g_ascii_strncasecmp(msg2, "/me ", 4)) - { - msg2 += 4; - if (!*msg2) { - g_free(tmp); - return 0; - } - flags |= SILC_MESSAGE_FLAG_ACTION; - } else if (strlen(msg) > 1 && msg[0] == '/') { - if (!silc_client_command_call(client, conn, msg + 1)) - purple_notify_error(gc, _("Call Command"), _("Cannot call command"), - _("Unknown command")); - g_free(tmp); - return 0; - } - - - if (sign) - flags |= SILC_MESSAGE_FLAG_SIGNED; - - /* Get the channel private key if we are sending on - private group */ - if (id > SILCPURPLE_PRVGRP) { - GList *l; - SilcPurplePrvgrp prv; - - for (l = sg->grps; l; l = l->next) - if (((SilcPurplePrvgrp)l->data)->id == (gulong)id) - break; - if (!l) { - g_free(tmp); - return 0; - } - prv = l->data; - channel = silc_client_get_channel(sg->client, sg->conn, - (char *)prv->parentch); - if (!channel) { - g_free(tmp); - return 0; - } - key = prv->key; - } - - if (!channel) { - /* Find channel by id */ - silc_hash_table_list(conn->local_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) { - found = TRUE; - break; - } - } - silc_hash_table_list_reset(&htl); - if (!found) { - g_free(tmp); - return 0; - } - channel = chu->channel; - } - - /* Check for images */ - if (msgflags & PURPLE_MESSAGE_IMAGES) { - list = silcpurple_image_message(msg, &flags); - if (list) { - /* Send one or more MIME message. If more than one, they - are MIME fragments due to over large message */ - SilcBuffer buf; - - silc_dlist_start(list); - while ((buf = silc_dlist_get(list)) != SILC_LIST_END) - ret = - silc_client_send_channel_message(client, conn, - channel, key, - flags, sg->sha1hash, - buf->data, - silc_buffer_len(buf)); - silc_mime_partial_free(list); - g_free(tmp); - - if (ret) - serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg, time(NULL)); - return ret; - } - } - - /* Send channel message */ - ret = silc_client_send_channel_message(client, conn, channel, key, - flags, sg->sha1hash, - (unsigned char *)msg2, - strlen(msg2)); - if (ret) { - serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg, - time(NULL)); - } - g_free(tmp); - - return ret; -} - -void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcHashTableList htl; - SilcChannelUser chu; - gboolean found = FALSE; - - if (!conn) - return; - - /* See if setting topic on private group. Set it - on the actual channel */ - if (id > SILCPURPLE_PRVGRP) { - GList *l; - SilcPurplePrvgrp prv; - - for (l = sg->grps; l; l = l->next) - if (((SilcPurplePrvgrp)l->data)->id == (gulong)id) - break; - if (!l) - return; - prv = l->data; - id = prv->chid; - } - - /* Find channel by id */ - silc_hash_table_list(conn->local_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - if (SILC_PTR_TO_32(chu->channel->context) == (gulong)id ) { - found = TRUE; - break; - } - } - silc_hash_table_list_reset(&htl); - if (!found) - return; - - /* Call TOPIC */ - silc_client_command_call(client, conn, NULL, "TOPIC", - chu->channel->channel_name, topic, NULL); -} - -PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - GList *fields = NULL; - PurpleRoomlistField *f; - - if (!conn) - return NULL; - - if (sg->roomlist) - purple_roomlist_unref(sg->roomlist); - - sg->roomlist_cancelled = FALSE; - - sg->roomlist = purple_roomlist_new(purple_connection_get_account(gc)); - f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", "channel", TRUE); - fields = g_list_append(fields, f); - f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_INT, - _("Users"), "users", FALSE); - fields = g_list_append(fields, f); - f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, - _("Topic"), "topic", FALSE); - fields = g_list_append(fields, f); - purple_roomlist_set_fields(sg->roomlist, fields); - - /* Call LIST */ - silc_client_command_call(client, conn, "LIST"); - - purple_roomlist_set_in_progress(sg->roomlist, TRUE); - - return sg->roomlist; -} - -void silcpurple_roomlist_cancel(PurpleRoomlist *list) -{ - PurpleConnection *gc = purple_account_get_connection(list->account); - SilcPurple sg; - - if (!gc) - return; - sg = gc->proto_data; - - purple_roomlist_set_in_progress(list, FALSE); - if (sg->roomlist == list) { - purple_roomlist_unref(sg->roomlist); - sg->roomlist = NULL; - sg->roomlist_cancelled = TRUE; - } -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc/ft.c --- a/libpurple/protocols/silc/ft.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,486 +0,0 @@ -/* - - silcpurple_ft.c - - Author: Pekka Riikonen - - Copyright (C) 2004 - 2007 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#include "internal.h" -#include "silc.h" -#include "silcclient.h" -#include "silcpurple.h" - -/****************************** File Transfer ********************************/ - -/* This implements the secure file transfer protocol (SFTP) using the SILC - SFTP library implementation. The API we use from the SILC Toolkit is the - SILC Client file transfer API, as it provides a simple file transfer we - need in this case. We could use the SILC SFTP API directly, but it would - be an overkill since we'd effectively re-implement the file transfer what - the SILC Client's file transfer API already provides. - - From Purple we do NOT use the FT API to do the transfer as it is very limiting. - In fact it does not suite to file transfers like SFTP at all. For example, - it assumes that read operations are synchronous what they are not in SFTP. - It also assumes that the file transfer socket is to be handled by the Purple - eventloop, and this naturally is something we don't want to do in case of - SILC Toolkit. The FT API suites well to purely stream based file transfers - like HTTP GET and similar. - - For this reason, we directly access the Purple GKT FT API and hack the FT - API to merely provide the user interface experience and all the magic - is done in the SILC Toolkit. Ie. we update the statistics information in - the FT API for user interface, and that's it. A bit dirty but until the - FT API gets better this is the way to go. Good thing that FT API allowed - us to do this. */ - -typedef struct { - SilcPurple sg; - SilcClientEntry client_entry; - SilcUInt32 session_id; - char *hostname; - SilcUInt16 port; - PurpleXfer *xfer; - - SilcClientFileName completion; - void *completion_context; -} *SilcPurpleXfer; - -static void -silcpurple_ftp_monitor(SilcClient client, - SilcClientConnection conn, - SilcClientMonitorStatus status, - SilcClientFileError error, - SilcUInt64 offset, - SilcUInt64 filesize, - SilcClientEntry client_entry, - SilcUInt32 session_id, - const char *filepath, - void *context) -{ - SilcPurpleXfer xfer = context; - PurpleConnection *gc = xfer->sg->gc; - char tmp[256]; - - if (status == SILC_CLIENT_FILE_MONITOR_CLOSED) { - /* All started sessions terminate here */ - xfer->xfer->data = NULL; - purple_xfer_unref(xfer->xfer); - silc_free(xfer); - return; - } - - if (status == SILC_CLIENT_FILE_MONITOR_DISCONNECT) { - purple_notify_error(gc, _("Secure File Transfer"), - _("Error during file transfer"), - _("Remote disconnected")); - xfer->xfer->status = PURPLE_XFER_STATUS_CANCEL_REMOTE; - purple_xfer_update_progress(xfer->xfer); - silc_client_file_close(client, conn, session_id); - return; - } - - if (status == SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT) - return; - - if (status == SILC_CLIENT_FILE_MONITOR_ERROR) { - if (error == SILC_CLIENT_FILE_NO_SUCH_FILE) { - g_snprintf(tmp, sizeof(tmp), "No such file %s", - filepath ? filepath : "[N/A]"); - purple_notify_error(gc, _("Secure File Transfer"), - _("Error during file transfer"), tmp); - } else if (error == SILC_CLIENT_FILE_PERMISSION_DENIED) { - purple_notify_error(gc, _("Secure File Transfer"), - _("Error during file transfer"), - _("Permission denied")); - } else if (error == SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED) { - purple_notify_error(gc, _("Secure File Transfer"), - _("Error during file transfer"), - _("Key agreement failed")); - } else if (error == SILC_CLIENT_FILE_TIMEOUT) { - purple_notify_error(gc, _("Secure File Transfer"), - _("Error during file transfer"), - _("Connection timed out")); - } else if (error == SILC_CLIENT_FILE_CONNECT_FAILED) { - purple_notify_error(gc, _("Secure File Transfer"), - _("Error during file transfer"), - _("Creating connection failed")); - } else if (error == SILC_CLIENT_FILE_UNKNOWN_SESSION) { - purple_notify_error(gc, _("Secure File Transfer"), - _("Error during file transfer"), - _("File transfer session does not exist")); - } - xfer->xfer->status = PURPLE_XFER_STATUS_CANCEL_REMOTE; - purple_xfer_update_progress(xfer->xfer); - silc_client_file_close(client, conn, session_id); - return; - } - - /* Update file transfer UI */ - if (!offset && filesize) - purple_xfer_set_size(xfer->xfer, filesize); - if (offset && filesize) { - xfer->xfer->bytes_sent = offset; - xfer->xfer->bytes_remaining = filesize - offset; - } - purple_xfer_update_progress(xfer->xfer); - - if (status == SILC_CLIENT_FILE_MONITOR_SEND || - status == SILC_CLIENT_FILE_MONITOR_RECEIVE) { - if (offset == filesize) { - /* Download finished */ - purple_xfer_set_completed(xfer->xfer, TRUE); - silc_client_file_close(client, conn, session_id); - } - } -} - -static void -silcpurple_ftp_cancel(PurpleXfer *x) -{ - SilcPurpleXfer xfer = x->data; - - if (!xfer) - return; - - xfer->xfer->status = PURPLE_XFER_STATUS_CANCEL_LOCAL; - purple_xfer_update_progress(xfer->xfer); - silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id); -} - -static void -silcpurple_ftp_ask_name_cancel(PurpleXfer *x) -{ - SilcPurpleXfer xfer = x->data; - - if (!xfer) - return; - - /* Cancel the transmission */ - xfer->completion(NULL, xfer->completion_context); - silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id); -} - -static void -silcpurple_ftp_ask_name_ok(PurpleXfer *x) -{ - SilcPurpleXfer xfer = x->data; - const char *name; - - if (!xfer) - return; - - name = purple_xfer_get_local_filename(x); - g_unlink(name); - xfer->completion(name, xfer->completion_context); -} - -static void -silcpurple_ftp_ask_name(SilcClient client, - SilcClientConnection conn, - SilcUInt32 session_id, - const char *remote_filename, - SilcClientFileName completion, - void *completion_context, - void *context) -{ - SilcPurpleXfer xfer = context; - - xfer->completion = completion; - xfer->completion_context = completion_context; - - purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_ask_name_ok); - purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_ask_name_cancel); - - /* Request to save the file */ - purple_xfer_set_filename(xfer->xfer, remote_filename); - purple_xfer_request(xfer->xfer); -} - -static void -silcpurple_ftp_request_result(PurpleXfer *x) -{ - SilcPurpleXfer xfer = x->data; - SilcClientFileError status; - PurpleConnection *gc = xfer->sg->gc; - SilcClientConnectionParams params; - gboolean local = xfer->hostname ? FALSE : TRUE; - char *local_ip = NULL, *remote_ip = NULL; - SilcSocket sock; - - if (purple_xfer_get_status(x) != PURPLE_XFER_STATUS_ACCEPTED) - return; - - silc_socket_stream_get_info(silc_packet_stream_get_stream(xfer->sg->conn->stream), - &sock, NULL, NULL, NULL); - - if (local) { - /* Do the same magic what we do with key agreement (see silcpurple_buddy.c) - to see if we are behind NAT. */ - if (silc_net_check_local_by_sock(sock, NULL, &local_ip)) { - /* Check if the IP is private */ - if (silcpurple_ip_is_private(local_ip)) { - local = TRUE; - /* Local IP is private, resolve the remote server IP to see whether - we are talking to Internet or just on LAN. */ - if (silc_net_check_host_by_sock(sock, NULL, - &remote_ip)) - if (silcpurple_ip_is_private(remote_ip)) - /* We assume we are in LAN. Let's provide the connection point. */ - local = TRUE; - } - } - - if (local && !local_ip) - local_ip = silc_net_localip(); - } - - memset(¶ms, 0, sizeof(params)); - params.timeout_secs = 60; - if (local) - /* Provide connection point */ - params.local_ip = local_ip; - - /* Start the file transfer */ - status = silc_client_file_receive(xfer->sg->client, xfer->sg->conn, - ¶ms, xfer->sg->public_key, - xfer->sg->private_key, - silcpurple_ftp_monitor, xfer, - NULL, xfer->session_id, - silcpurple_ftp_ask_name, xfer); - switch (status) { - case SILC_CLIENT_FILE_OK: - silc_free(local_ip); - silc_free(remote_ip); - return; - break; - - case SILC_CLIENT_FILE_UNKNOWN_SESSION: - purple_notify_error(gc, _("Secure File Transfer"), - _("No file transfer session active"), NULL); - break; - - case SILC_CLIENT_FILE_ALREADY_STARTED: - purple_notify_error(gc, _("Secure File Transfer"), - _("File transfer already started"), NULL); - break; - - case SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED: - purple_notify_error(gc, _("Secure File Transfer"), - _("Could not perform key agreement for file transfer"), - NULL); - break; - - default: - purple_notify_error(gc, _("Secure File Transfer"), - _("Could not start the file transfer"), NULL); - break; - } - - /* Error */ - purple_xfer_unref(xfer->xfer); - g_free(xfer->hostname); - silc_free(xfer); - silc_free(local_ip); - silc_free(remote_ip); -} - -static void -silcpurple_ftp_request_denied(PurpleXfer *x) -{ - -} - -void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn, - SilcClientEntry client_entry, SilcUInt32 session_id, - const char *hostname, SilcUInt16 port) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - SilcPurpleXfer xfer; - - xfer = silc_calloc(1, sizeof(*xfer)); - if (!xfer) { - silc_client_file_close(sg->client, sg->conn, session_id); - return; - } - - xfer->sg = sg; - xfer->client_entry = client_entry; - xfer->session_id = session_id; - xfer->hostname = g_strdup(hostname); - xfer->port = port; - xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_RECEIVE, - xfer->client_entry->nickname); - if (!xfer->xfer) { - silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id); - g_free(xfer->hostname); - silc_free(xfer); - return; - } - purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_request_result); - purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_request_denied); - purple_xfer_set_cancel_recv_fnc(xfer->xfer, silcpurple_ftp_cancel); - xfer->xfer->remote_ip = g_strdup(hostname); - xfer->xfer->remote_port = port; - xfer->xfer->data = xfer; - - /* File transfer request */ - purple_xfer_request(xfer->xfer); -} - -static void -silcpurple_ftp_send_cancel(PurpleXfer *x) -{ - SilcPurpleXfer xfer = x->data; - - if (!xfer) - return; - - /* This call will free all resources */ - silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id); -} - -static void -silcpurple_ftp_send(PurpleXfer *x) -{ - SilcPurpleXfer xfer = x->data; - const char *name; - char *local_ip = NULL, *remote_ip = NULL; - gboolean local = TRUE; - SilcClientConnectionParams params; - SilcSocket sock; - - if (!xfer) - return; - - name = purple_xfer_get_local_filename(x); - - silc_socket_stream_get_info(silc_packet_stream_get_stream(xfer->sg->conn->stream), - &sock, NULL, NULL, NULL); - - /* Do the same magic what we do with key agreement (see silcpurple_buddy.c) - to see if we are behind NAT. */ - if (silc_net_check_local_by_sock(sock, NULL, &local_ip)) { - /* Check if the IP is private */ - if (silcpurple_ip_is_private(local_ip)) { - local = FALSE; - /* Local IP is private, resolve the remote server IP to see whether - we are talking to Internet or just on LAN. */ - if (silc_net_check_host_by_sock(sock, NULL, - &remote_ip)) - if (silcpurple_ip_is_private(remote_ip)) - /* We assume we are in LAN. Let's provide the connection point. */ - local = TRUE; - } - } - - if (local && !local_ip) - local_ip = silc_net_localip(); - - memset(¶ms, 0, sizeof(params)); - params.timeout_secs = 60; - if (local) - /* Provide connection point */ - params.local_ip = local_ip; - - /* Send the file */ - silc_client_file_send(xfer->sg->client, xfer->sg->conn, - xfer->client_entry, ¶ms, - xfer->sg->public_key, xfer->sg->private_key, - silcpurple_ftp_monitor, xfer, - name, &xfer->session_id); - - silc_free(local_ip); - silc_free(remote_ip); -} - -static void -silcpurple_ftp_send_file_resolved(SilcClient client, - SilcClientConnection conn, - SilcStatus status, - SilcDList clients, - void *context) -{ - PurpleConnection *gc = client->application; - char tmp[256]; - - if (!clients) { - g_snprintf(tmp, sizeof(tmp), - _("User %s is not present in the network"), - (const char *)context); - purple_notify_error(gc, _("Secure File Transfer"), - _("Cannot send file"), tmp); - g_free(context); - return; - } - - silcpurple_ftp_send_file(client->application, (const char *)context, NULL); - g_free(context); -} - -PurpleXfer *silcpurple_ftp_new_xfer(PurpleConnection *gc, const char *name) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcDList clients; - SilcPurpleXfer xfer; - - g_return_val_if_fail(name != NULL, NULL); - - /* Find client entry */ - clients = silc_client_get_clients_local(client, conn, name, FALSE); - if (!clients) { - silc_client_get_clients(client, conn, name, NULL, - silcpurple_ftp_send_file_resolved, - g_strdup(name)); - return NULL; - } - silc_dlist_start(clients); - - xfer = silc_calloc(1, sizeof(*xfer)); - g_return_val_if_fail(xfer != NULL, NULL); - - xfer->sg = sg; - xfer->client_entry = silc_dlist_get(clients); - xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_SEND, - xfer->client_entry->nickname); - if (!xfer->xfer) { - silc_free(xfer); - return NULL; - } - purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_send); - purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_request_denied); - purple_xfer_set_cancel_send_fnc(xfer->xfer, silcpurple_ftp_send_cancel); - xfer->xfer->data = xfer; - - silc_free(clients); - - return xfer->xfer; -} - -void silcpurple_ftp_send_file(PurpleConnection *gc, const char *name, const char *file) -{ - PurpleXfer *xfer = silcpurple_ftp_new_xfer(gc, name); - - g_return_if_fail(xfer != NULL); - - /* Choose file to send */ - if (file) - purple_xfer_request_accepted(xfer, file); - else - purple_xfer_request(xfer); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc/ops.c --- a/libpurple/protocols/silc/ops.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1884 +0,0 @@ -/* - - silcpurple_ops.c - - Author: Pekka Riikonen - - Copyright (C) 2004 - 2007 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#include "internal.h" -#include "silc.h" -#include "silcclient.h" -#include "silcpurple.h" -#include "imgstore.h" -#include "wb.h" - -#include "glibcompat.h" - -static void -silc_channel_message(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcChannelEntry channel, - SilcMessagePayload payload, - SilcChannelPrivateKey key, SilcMessageFlags flags, - const unsigned char *message, - SilcUInt32 message_len); -static void -silc_private_message(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcMessagePayload payload, - SilcMessageFlags flags, const unsigned char *message, - SilcUInt32 message_len); -static void -silc_ask_passphrase(SilcClient client, SilcClientConnection conn, - SilcAskPassphrase completion, void *context); - -/* Message sent to the application by library. `conn' associates the - message to a specific connection. `conn', however, may be NULL. - The `type' indicates the type of the message sent by the library. - The application can for example filter the message according the - type. */ - -void silc_say(SilcClient client, SilcClientConnection conn, - SilcClientMessageType type, char *msg, ...) -{ - char tmp[256]; - va_list va; - PurpleConnection *gc = NULL; - PurpleConnectionError reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - - va_start(va, msg); - silc_vsnprintf(tmp, sizeof(tmp), msg, va); - va_end(va); - - if (type != SILC_CLIENT_MESSAGE_ERROR) { - purple_debug_misc("silc", "silc_say (%d) %s\n", type, tmp); - return; - } - - purple_debug_error("silc", "silc_say error: %s\n", tmp); - - if (purple_strequal(tmp, "Authentication failed")) - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - - if (client != NULL) - gc = client->application; - - if (gc != NULL) - purple_connection_error_reason(gc, reason, tmp); - else - purple_notify_error(NULL, _("Error"), _("Error occurred"), tmp); -} - -/* Processes incoming MIME message. Can be private message or channel - message. Returns TRUE if the message `mime' was displayed. */ - -static SilcBool -silcpurple_mime_message(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcChannelEntry channel, - SilcMessagePayload payload, SilcChannelPrivateKey key, - SilcMessageFlags flags, SilcMime mime, - gboolean recursive) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - const char *type; - const unsigned char *data; - SilcUInt32 data_len; - PurpleMessageFlags cflags = 0; - PurpleConversation *convo = NULL; - SilcBool ret = FALSE; - - if (!mime) - return FALSE; - - /* Check for fragmented MIME message */ - if (silc_mime_is_partial(mime)) { - if (!sg->mimeass) - sg->mimeass = silc_mime_assembler_alloc(); - - /* Defragment */ - mime = silc_mime_assemble(sg->mimeass, mime); - if (!mime) - /* More fragments to come */ - return FALSE; - - /* Process the complete message */ - return silcpurple_mime_message(client, conn, sender, channel, - payload, key, flags, mime, - FALSE); - } - - /* Check for multipart message */ - if (silc_mime_is_multipart(mime)) { - SilcMime p; - const char *mtype; - SilcDList parts = silc_mime_get_multiparts(mime, &mtype); - - if (purple_strequal(mtype, "mixed")) { - /* Contains multiple messages */ - silc_dlist_start(parts); - while ((p = silc_dlist_get(parts)) != SILC_LIST_END) { - /* Recursively process parts */ - ret = silcpurple_mime_message(client, conn, sender, channel, - payload, key, flags, p, TRUE); - } - } - - if (purple_strequal(mtype, "alternative")) { - /* Same message in alternative formats. Kopete sends - these. Go in order from last to first. */ - silc_dlist_end(parts); - while ((p = silc_dlist_get(parts)) != SILC_LIST_END) { - /* Go through the alternatives and display the first - one we support. */ - if (silcpurple_mime_message(client, conn, sender, channel, - payload, key, flags, p, TRUE)) { - ret = TRUE; - break; - } - } - } - - goto out; - } - - /* Get content type and MIME data */ - type = silc_mime_get_field(mime, "Content-Type"); - if (!type) - goto out; - data = silc_mime_get_data(mime, &data_len); - if (!data) - goto out; - - /* Process according to content type */ - - /* Plain text */ - if (strstr(type, "text/plain")) { - /* Default is UTF-8, don't check for other charsets */ - if (!strstr(type, "utf-8")) - goto out; - - if (channel) - silc_channel_message(client, conn, sender, channel, - payload, key, - SILC_MESSAGE_FLAG_UTF8, data, - data_len); - else - silc_private_message(client, conn, sender, payload, - SILC_MESSAGE_FLAG_UTF8, data, - data_len); - ret = TRUE; - goto out; - } - - /* Image */ - if (strstr(type, "image/png") || - strstr(type, "image/jpeg") || - strstr(type, "image/gif") || - strstr(type, "image/tiff")) { - char tmp[32]; - int imgid; - - /* Get channel convo (if message is for channel) */ - if (key && channel) { - GList *l; - SilcPurplePrvgrp prv; - - for (l = sg->grps; l; l = l->next) - if (((SilcPurplePrvgrp)l->data)->key == key) { - prv = l->data; - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - prv->channel, sg->account); - break; - } - } - if (channel && !convo) - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (channel && !convo) - goto out; - - imgid = purple_imgstore_add_with_id(g_memdup2(data, data_len), data_len, ""); - if (imgid) { - cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV; - g_snprintf(tmp, sizeof(tmp), "", imgid); - - if (channel) - serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)), - sender->nickname, cflags, - tmp, time(NULL)); - else - serv_got_im(gc, sender->nickname, - tmp, cflags, time(NULL)); - - purple_imgstore_unref_by_id(imgid); - ret = TRUE; - } - goto out; - } - - /* Whiteboard message */ - if (strstr(type, "application/x-wb") && - !purple_account_get_bool(sg->account, "block-wb", FALSE)) { - if (channel) - silcpurple_wb_receive_ch(client, conn, sender, channel, - payload, flags, data, data_len); - else - silcpurple_wb_receive(client, conn, sender, payload, - flags, data, data_len); - ret = TRUE; - goto out; - } - - out: - if (!recursive) - silc_mime_free(mime); - return ret; -} - -/* Message for a channel. The `sender' is the sender of the message - The `channel' is the channel. The `message' is the message. Note - that `message' maybe NULL. The `flags' indicates message flags - and it is used to determine how the message can be interpreted - (like it may tell the message is multimedia message). */ - -static void -silc_channel_message(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcChannelEntry channel, - SilcMessagePayload payload, - SilcChannelPrivateKey key, SilcMessageFlags flags, - const unsigned char *message, - SilcUInt32 message_len) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - PurpleConversation *convo = NULL; - char *msg, *tmp; - - if (!message) - return; - - if (key) { - GList *l; - SilcPurplePrvgrp prv; - - for (l = sg->grps; l; l = l->next) - if (((SilcPurplePrvgrp)l->data)->key == key) { - prv = l->data; - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - prv->channel, sg->account); - break; - } - } - if (!convo) - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) - return; - - if (flags & SILC_MESSAGE_FLAG_SIGNED && - purple_account_get_bool(sg->account, "sign-verify", FALSE)) { - /* XXX */ - } - - if (flags & SILC_MESSAGE_FLAG_DATA) { - /* Process MIME message */ - SilcMime mime; - mime = silc_mime_decode(NULL, message, message_len); - silcpurple_mime_message(client, conn, sender, channel, payload, - key, flags, mime, FALSE); - return; - } - - if (flags & SILC_MESSAGE_FLAG_ACTION) { - msg = g_strdup_printf("/me %s", - (const char *)message); - if (!msg) - return; - - tmp = g_markup_escape_text(msg, -1); - /* Send to Purple */ - serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)), - sender->nickname, 0, tmp, time(NULL)); - g_free(tmp); - g_free(msg); - return; - } - - if (flags & SILC_MESSAGE_FLAG_NOTICE) { - msg = g_strdup_printf("(notice) %s %s", - sender->nickname, (const char *)message); - if (!msg) - return; - - /* Send to Purple */ - purple_conversation_write(convo, NULL, (const char *)msg, - PURPLE_MESSAGE_SYSTEM, time(NULL)); - g_free(msg); - return; - } - - if (flags & SILC_MESSAGE_FLAG_UTF8) { - const char *msg = (const char *)message; - char *salvaged = NULL; - if (!g_utf8_validate((const char *)message, -1, NULL)) { - salvaged = purple_utf8_salvage((const char *)message); - msg = salvaged; - } - tmp = g_markup_escape_text(msg, -1); - /* Send to Purple */ - serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)), - sender->nickname, 0, tmp, time(NULL)); - g_free(salvaged); - g_free(tmp); - } -} - - -/* Private message to the client. The `sender' is the sender of the - message. The message is `message'and maybe NULL. The `flags' - indicates message flags and it is used to determine how the message - can be interpreted (like it may tell the message is multimedia - message). */ - -static void -silc_private_message(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcMessagePayload payload, - SilcMessageFlags flags, const unsigned char *message, - SilcUInt32 message_len) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - PurpleConversation *convo; - char *msg, *tmp; - - if (!message) - return; - - /* XXX - Should this be PURPLE_CONV_TYPE_IM? */ - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, - sender->nickname, sg->account); - - if (flags & SILC_MESSAGE_FLAG_SIGNED && - purple_account_get_bool(sg->account, "sign-verify", FALSE)) { - /* XXX */ - } - - if (flags & SILC_MESSAGE_FLAG_DATA) { - /* Process MIME message */ - SilcMime mime; - mime = silc_mime_decode(NULL, message, message_len); - silcpurple_mime_message(client, conn, sender, NULL, payload, - NULL, flags, mime, FALSE); - return; - } - - if (flags & SILC_MESSAGE_FLAG_ACTION && convo) { - msg = g_strdup_printf("/me %s", - (const char *)message); - if (!msg) - return; - - /* Send to Purple */ - tmp = g_markup_escape_text(msg, -1); - serv_got_im(gc, sender->nickname, tmp, 0, time(NULL)); - g_free(msg); - g_free(tmp); - return; - } - - if (flags & SILC_MESSAGE_FLAG_NOTICE && convo) { - msg = g_strdup_printf("(notice) %s %s", - sender->nickname, (const char *)message); - if (!msg) - return; - - /* Send to Purple */ - purple_conversation_write(convo, NULL, (const char *)msg, - PURPLE_MESSAGE_SYSTEM, time(NULL)); - g_free(msg); - return; - } - - if (flags & SILC_MESSAGE_FLAG_UTF8) { - const char *msg = (const char *)message; - char *salvaged = NULL; - if (!g_utf8_validate((const char *)message, -1, NULL)) { - salvaged = purple_utf8_salvage((const char *)message); - msg = salvaged; - } - tmp = g_markup_escape_text(msg, -1); - /* Send to Purple */ - serv_got_im(gc, sender->nickname, tmp, 0, time(NULL)); - g_free(salvaged); - g_free(tmp); - } -} - - -/* Notify message to the client. The notify arguments are sent in the - same order as servers sends them. The arguments are same as received - from the server except for ID's. If ID is received application receives - the corresponding entry to the ID. For example, if Client ID is received - application receives SilcClientEntry. Also, if the notify type is - for channel the channel entry is sent to application (even if server - does not send it because client library gets the channel entry from - the Channel ID in the packet's header). */ - -static void -silc_notify(SilcClient client, SilcClientConnection conn, - SilcNotifyType type, ...) -{ - va_list va; - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - PurpleAccount *account = purple_connection_get_account(gc); - PurpleConversation *convo; - SilcClientEntry client_entry, client_entry2; - SilcChannelEntry channel; - SilcServerEntry server_entry; - SilcIdType idtype; - void *entry; - SilcUInt32 mode; - SilcHashTableList htl; - SilcChannelUser chu; - char buf[512], buf2[512], *tmp, *name; - SilcNotifyType notify; - PurpleBuddy *b; - SilcDList list; - - va_start(va, type); - memset(buf, 0, sizeof(buf)); - - switch (type) { - - case SILC_NOTIFY_TYPE_NONE: - break; - - case SILC_NOTIFY_TYPE_INVITE: - { - GHashTable *components; - (void)va_arg(va, SilcChannelEntry); - name = va_arg(va, char *); - client_entry = va_arg(va, SilcClientEntry); - - components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - g_hash_table_insert(components, g_strdup("channel"), g_strdup(name)); - serv_got_chat_invite(gc, name, client_entry->nickname, NULL, components); - } - break; - - case SILC_NOTIFY_TYPE_JOIN: - client_entry = va_arg(va, SilcClientEntry); - channel = va_arg(va, SilcChannelEntry); - - /* If we joined channel, do nothing */ - if (client_entry == conn->local_entry) - break; - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) - break; - - /* Join user to channel */ - g_snprintf(buf, sizeof(buf), "%s@%s", - client_entry->username, client_entry->hostname); - purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo), - client_entry->nickname, buf, PURPLE_CBFLAGS_NONE, TRUE); - - break; - - case SILC_NOTIFY_TYPE_LEAVE: - client_entry = va_arg(va, SilcClientEntry); - channel = va_arg(va, SilcChannelEntry); - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) - break; - - /* Remove user from channel */ - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), - client_entry->nickname, NULL); - - break; - - case SILC_NOTIFY_TYPE_SIGNOFF: - client_entry = va_arg(va, SilcClientEntry); - tmp = va_arg(va, char *); - - /* Remove from all channels */ - silc_hash_table_list(client_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - chu->channel->channel_name, sg->account); - if (!convo) - continue; - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), - client_entry->nickname, - tmp); - } - silc_hash_table_list_reset(&htl); - - break; - - case SILC_NOTIFY_TYPE_TOPIC_SET: - { - char *esc, *tmp2; - idtype = va_arg(va, int); - entry = va_arg(va, void *); - tmp = va_arg(va, char *); - channel = va_arg(va, SilcChannelEntry); - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) - break; - - if (!tmp) - break; - - esc = g_markup_escape_text(tmp, -1); - tmp2 = purple_markup_linkify(esc); - g_free(esc); - - if (idtype == SILC_ID_CLIENT) { - client_entry = (SilcClientEntry)entry; - g_snprintf(buf, sizeof(buf), - _("%s has changed the topic of %s to: %s"), - client_entry->nickname, channel->channel_name, tmp2); - purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); - purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), - client_entry->nickname, tmp); - } else if (idtype == SILC_ID_SERVER) { - server_entry = (SilcServerEntry)entry; - g_snprintf(buf, sizeof(buf), - _("%s has changed the topic of %s to: %s"), - server_entry->server_name, channel->channel_name, tmp2); - purple_conv_chat_write(PURPLE_CONV_CHAT(convo), server_entry->server_name, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); - purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), - server_entry->server_name, tmp); - } else if (idtype == SILC_ID_CHANNEL) { - channel = (SilcChannelEntry)entry; - g_snprintf(buf, sizeof(buf), - _("%s has changed the topic of %s to: %s"), - channel->channel_name, channel->channel_name, tmp2); - purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); - purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), - channel->channel_name, tmp); - } else { - purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, tmp); - } - - g_free(tmp2); - - break; - - } - case SILC_NOTIFY_TYPE_NICK_CHANGE: - client_entry = va_arg(va, SilcClientEntry); - tmp = va_arg(va, char *); /* Old nick */ - name = va_arg(va, char *); /* New nick */ - - if (purple_strequal(tmp, name)) - break; - - /* Change nick on all channels */ - silc_hash_table_list(client_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - chu->channel->channel_name, sg->account); - if (!convo) - continue; - if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(convo), client_entry->nickname)) - purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo), - tmp, name); - } - silc_hash_table_list_reset(&htl); - - break; - - case SILC_NOTIFY_TYPE_CMODE_CHANGE: - idtype = va_arg(va, int); - entry = va_arg(va, void *); - mode = va_arg(va, SilcUInt32); - (void)va_arg(va, char *); - (void)va_arg(va, char *); - (void)va_arg(va, char *); - (void)va_arg(va, SilcPublicKey); - (void)va_arg(va, SilcDList); - channel = va_arg(va, SilcChannelEntry); - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) - break; - - if (idtype == SILC_ID_CLIENT) - name = ((SilcClientEntry)entry)->nickname; - else if (idtype == SILC_ID_SERVER) - name = ((SilcServerEntry)entry)->server_name; - else - name = ((SilcChannelEntry)entry)->channel_name; - if (!name) - break; - - if (mode) { - silcpurple_get_chmode_string(mode, buf2, sizeof(buf2)); - g_snprintf(buf, sizeof(buf), - _("%s set channel %s modes to: %s"), name, - channel->channel_name, buf2); - } else { - g_snprintf(buf, sizeof(buf), - _("%s removed all channel %s modes"), name, - channel->channel_name); - } - purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); - break; - - case SILC_NOTIFY_TYPE_CUMODE_CHANGE: - { - PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE; - idtype = va_arg(va, int); - entry = va_arg(va, void *); - mode = va_arg(va, SilcUInt32); - client_entry2 = va_arg(va, SilcClientEntry); - channel = va_arg(va, SilcChannelEntry); - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) - break; - - if (idtype == SILC_ID_CLIENT) - name = ((SilcClientEntry)entry)->nickname; - else if (idtype == SILC_ID_SERVER) - name = ((SilcServerEntry)entry)->server_name; - else - name = ((SilcChannelEntry)entry)->channel_name; - if (!name) - break; - - if (mode) { - silcpurple_get_chumode_string(mode, buf2, sizeof(buf2)); - g_snprintf(buf, sizeof(buf), - _("%s set %s's modes to: %s"), name, - client_entry2->nickname, buf2); - if (mode & SILC_CHANNEL_UMODE_CHANFO) - flags |= PURPLE_CBFLAGS_FOUNDER; - if (mode & SILC_CHANNEL_UMODE_CHANOP) - flags |= PURPLE_CBFLAGS_OP; - } else { - g_snprintf(buf, sizeof(buf), - _("%s removed all %s's modes"), name, - client_entry2->nickname); - } - purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); - purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(convo), client_entry2->nickname, flags); - break; - } - - case SILC_NOTIFY_TYPE_MOTD: - tmp = va_arg(va, char *); - silc_free(sg->motd); - sg->motd = silc_memdup(tmp, strlen(tmp)); - break; - - case SILC_NOTIFY_TYPE_KICKED: - client_entry = va_arg(va, SilcClientEntry); - tmp = va_arg(va, char *); - client_entry2 = va_arg(va, SilcClientEntry); - channel = va_arg(va, SilcChannelEntry); - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) - break; - - if (client_entry == conn->local_entry) { - /* Remove us from channel */ - g_snprintf(buf, sizeof(buf), - _("You have been kicked off %s by %s (%s)"), - channel->channel_name, client_entry2->nickname, - tmp ? tmp : ""); - purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); - serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo))); - } else { - /* Remove user from channel */ - g_snprintf(buf, sizeof(buf), _("Kicked by %s (%s)"), - client_entry2->nickname, tmp ? tmp : ""); - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), - client_entry->nickname, - buf); - } - - break; - - case SILC_NOTIFY_TYPE_KILLED: - client_entry = va_arg(va, SilcClientEntry); - tmp = va_arg(va, char *); - idtype = va_arg(va, int); - entry = va_arg(va, SilcClientEntry); - - if (client_entry == conn->local_entry) { - if (idtype == SILC_ID_CLIENT) { - client_entry2 = (SilcClientEntry)entry; - g_snprintf(buf, sizeof(buf), - _("You have been killed by %s (%s)"), - client_entry2->nickname, tmp ? tmp : ""); - } else if (idtype == SILC_ID_SERVER) { - server_entry = (SilcServerEntry)entry; - g_snprintf(buf, sizeof(buf), - _("You have been killed by %s (%s)"), - server_entry->server_name, tmp ? tmp : ""); - } else if (idtype == SILC_ID_CHANNEL) { - channel = (SilcChannelEntry)entry; - g_snprintf(buf, sizeof(buf), - _("You have been killed by %s (%s)"), - channel->channel_name, tmp ? tmp : ""); - } - - /* Remove us from all channels */ - silc_hash_table_list(client_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - chu->channel->channel_name, sg->account); - if (!convo) - continue; - purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); - serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo))); - } - silc_hash_table_list_reset(&htl); - - } else { - if (idtype == SILC_ID_CLIENT) { - client_entry2 = (SilcClientEntry)entry; - g_snprintf(buf, sizeof(buf), - _("Killed by %s (%s)"), - client_entry2->nickname, tmp ? tmp : ""); - } else if (idtype == SILC_ID_SERVER) { - server_entry = (SilcServerEntry)entry; - g_snprintf(buf, sizeof(buf), - _("Killed by %s (%s)"), - server_entry->server_name, tmp ? tmp : ""); - } else if (idtype == SILC_ID_CHANNEL) { - channel = (SilcChannelEntry)entry; - g_snprintf(buf, sizeof(buf), - _("Killed by %s (%s)"), - channel->channel_name, tmp ? tmp : ""); - } - - /* Remove user from all channels */ - silc_hash_table_list(client_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - chu->channel->channel_name, sg->account); - if (!convo) - continue; - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), - client_entry->nickname, tmp); - } - silc_hash_table_list_reset(&htl); - } - - break; - - case SILC_NOTIFY_TYPE_CHANNEL_CHANGE: - break; - - case SILC_NOTIFY_TYPE_SERVER_SIGNOFF: - (void)va_arg(va, void *); - list = va_arg(va, SilcDList); - - silc_dlist_start(list); - while ((client_entry = silc_dlist_get(list))) { - /* Remove from all channels */ - silc_hash_table_list(client_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - chu->channel->channel_name, sg->account); - if (!convo) - continue; - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), - client_entry->nickname, - _("Server signoff")); - } - silc_hash_table_list_reset(&htl); - } - break; - - case SILC_NOTIFY_TYPE_ERROR: - { - SilcStatus error = va_arg(va, int); - purple_notify_error(gc, "Error Notify", - silc_get_status_message(error), - NULL); - } - break; - - case SILC_NOTIFY_TYPE_WATCH: - { - SilcPublicKey public_key; - unsigned char *pk; - SilcUInt32 pk_len; - char *fingerprint; - - client_entry = va_arg(va, SilcClientEntry); - (void)va_arg(va, char *); - mode = va_arg(va, SilcUInt32); - notify = va_arg(va, int); - public_key = va_arg(va, SilcPublicKey); - - b = NULL; - if (public_key) { - GSList *buddies; - const char *f; - gsize i; - - pk = silc_pkcs_public_key_encode(public_key, &pk_len); - if (!pk) - break; - fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - for (i = 0; i < strlen(fingerprint); i++) - if (fingerprint[i] == ' ') - fingerprint[i] = '_'; - g_snprintf(buf, sizeof(buf) - 1, - "%s" G_DIR_SEPARATOR_S "clientkeys" - G_DIR_SEPARATOR_S "clientkey_%s.pub", - silcpurple_silcdir(), fingerprint); - silc_free(fingerprint); - silc_free(pk); - - /* Find buddy by associated public key */ - for (buddies = purple_find_buddies(account, NULL); buddies; - buddies = g_slist_delete_link(buddies, buddies)) { - b = buddies->data; - f = purple_blist_node_get_string(PURPLE_BLIST_NODE(b), "public-key"); - if (purple_strequal(f, buf)) - goto cont; - b = NULL; - } - } - cont: - if (!b) { - /* Find buddy by nickname */ - b = purple_find_buddy(sg->account, client_entry->nickname); - if (!b) { - purple_debug_warning("silc", "WATCH for %s, unknown buddy\n", - client_entry->nickname); - break; - } - } - - silc_free(purple_buddy_get_protocol_data(b)); - purple_buddy_set_protocol_data(b, silc_memdup(&client_entry->id, - sizeof(client_entry->id))); - if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) { - break; - } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) { - /* See if client was away and is now present */ - if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED | - SILC_UMODE_BUSY | SILC_UMODE_PAGE | - SILC_UMODE_DETACHED)) && - (client_entry->mode & SILC_UMODE_GONE || - client_entry->mode & SILC_UMODE_INDISPOSED || - client_entry->mode & SILC_UMODE_BUSY || - client_entry->mode & SILC_UMODE_PAGE || - client_entry->mode & SILC_UMODE_DETACHED)) { - client_entry->mode = mode; - purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL); - } - else if ((mode & SILC_UMODE_GONE) || - (mode & SILC_UMODE_INDISPOSED) || - (mode & SILC_UMODE_BUSY) || - (mode & SILC_UMODE_PAGE) || - (mode & SILC_UMODE_DETACHED)) { - client_entry->mode = mode; - purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); - } - } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF || - notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF || - notify == SILC_NOTIFY_TYPE_KILLED) { - client_entry->mode = mode; - purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); - } else if (notify == SILC_NOTIFY_TYPE_NONE) { - client_entry->mode = mode; - purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL); - } - } - break; - - default: - purple_debug_info("silc", "Unhandled notification: %d\n", type); - break; - } - - va_end(va); -} - - -/* Command handler. This function is called always after application has - called a command. It will be called to indicate that the command - was processed. It will also be called if error occurs while processing - the command. The `success' indicates whether the command was sent - or if error occurred. The `status' indicates the actual error. - The `argc' and `argv' are the command line arguments sent to the - command by application. Note that, this is not reply to the command - from server, this is merely and indication to application that the - command was processed. */ - -static void -silc_command(SilcClient client, SilcClientConnection conn, - SilcBool success, SilcCommand command, SilcStatus status, - SilcUInt32 argc, unsigned char **argv) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - - switch (command) { - - case SILC_COMMAND_CMODE: - if (argc == 3 && purple_strequal((char *)argv[2], "+C")) - sg->chpk = TRUE; - else - sg->chpk = FALSE; - break; - - default: - break; - } -} - -#if 0 -static void -silcpurple_whois_more(SilcClientEntry client_entry, gint id) -{ - SilcAttributePayload attr; - SilcAttribute attribute; - GString *s; - SilcVCardStruct vcard; - int i; - - if (id != 0) - return; - - memset(&vcard, 0, sizeof(vcard)); - - s = g_string_new(""); - - silc_dlist_start(client_entry->attrs); - while ((attr = silc_dlist_get(client_entry->attrs)) != SILC_LIST_END) { - attribute = silc_attribute_get_attribute(attr); - switch (attribute) { - - case SILC_ATTRIBUTE_USER_INFO: - if (!silc_attribute_get_object(attr, (void *)&vcard, - sizeof(vcard))) - continue; - g_string_append_printf(s, "%s:\n\n", _("Personal Information")); - if (vcard.full_name) - g_string_append_printf(s, "%s:\t\t%s\n", - _("Full Name"), - vcard.full_name); - if (vcard.first_name) - g_string_append_printf(s, "%s:\t%s\n", - _("First Name"), - vcard.first_name); - if (vcard.middle_names) - g_string_append_printf(s, "%s:\t%s\n", - _("Middle Name"), - vcard.middle_names); - if (vcard.family_name) - g_string_append_printf(s, "%s:\t%s\n", - _("Family Name"), - vcard.family_name); - if (vcard.nickname) - g_string_append_printf(s, "%s:\t\t%s\n", - _("Nickname"), - vcard.nickname); - if (vcard.bday) - g_string_append_printf(s, "%s:\t\t%s\n", - _("Birth Day"), - vcard.bday); - if (vcard.title) - g_string_append_printf(s, "%s:\t\t%s\n", - _("Job Title"), - vcard.title); - if (vcard.role) - g_string_append_printf(s, "%s:\t\t%s\n", - _("Job Role"), - vcard.role); - if (vcard.org_name) - g_string_append_printf(s, "%s:\t%s\n", - _("Organization"), - vcard.org_name); - if (vcard.org_unit) - g_string_append_printf(s, "%s:\t\t%s\n", - _("Unit"), - vcard.org_unit); - if (vcard.url) - g_string_append_printf(s, "%s:\t%s\n", - _("Homepage"), - vcard.url); - if (vcard.label) - g_string_append_printf(s, "%s:\t%s\n", - _("Address"), - vcard.label); - for (i = 0; i < vcard.num_tels; i++) { - if (vcard.tels[i].telnum) - g_string_append_printf(s, "%s:\t\t\t%s\n", - _("Phone"), - vcard.tels[i].telnum); - } - for (i = 0; i < vcard.num_emails; i++) { - if (vcard.emails[i].address) - g_string_append_printf(s, "%s:\t\t%s\n", - _("Email"), - vcard.emails[i].address); - } - if (vcard.note) - g_string_append_printf(s, "\n%s:\t\t%s\n", - _("Note"), - vcard.note); - break; - } - } - - purple_notify_info(NULL, _("User Information"), _("User Information"), - s->str); - g_string_free(s, TRUE); -} -#endif - - -/* Command reply handler. Delivers a reply to command that was sent - earlier. The `conn' is the associated client connection. The `command' - indicates the command reply type. If the `status' other than - SILC_STATUS_OK an error occurred. In this case the `error' will indicate - the error. It is possible to receive list of command replies and list - of errors. In this case the `status' will indicate it is an list entry - (the `status' is SILC_STATUS_LIST_START, SILC_STATUS_LIST_ITEM and/or - SILC_STATUS_LIST_END). - - The arguments received in `ap' are command specific. See a separate - documentation in the Toolkit Reference Manual for the command reply - arguments. */ - -static void -silc_command_reply(SilcClient client, SilcClientConnection conn, - SilcCommand command, SilcStatus status, - SilcStatus error, va_list ap) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - PurpleConversation *convo; - - switch (command) { - case SILC_COMMAND_JOIN: - { - SilcChannelEntry channel; - PurpleConversation *convo; - SilcHashTableList *user_list; - SilcChannelUser chu; - GList *users = NULL, *flags = NULL; - char tmp[256], *topic; - - if (status != SILC_STATUS_OK) { - purple_notify_error(gc, _("Join Chat"), _("Cannot join channel"), - silc_get_status_message(error)); - return; - } - - (void)va_arg(ap, char *); - channel = va_arg(ap, SilcChannelEntry); - (void)va_arg(ap, SilcUInt32); - user_list = va_arg(ap, SilcHashTableList *); - topic = va_arg(ap, char *); - - /* Add channel to Purple */ - channel->context = SILC_32_TO_PTR(++sg->channel_ids); - serv_got_joined_chat(gc, sg->channel_ids, channel->channel_name); - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) - return; - - /* Add all users to channel */ - while (silc_hash_table_get(user_list, NULL, (void *)&chu)) { - PurpleConvChatBuddyFlags f = PURPLE_CBFLAGS_NONE; - chu->context = SILC_32_TO_PTR(sg->channel_ids); - - if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) - f |= PURPLE_CBFLAGS_FOUNDER; - if (chu->mode & SILC_CHANNEL_UMODE_CHANOP) - f |= PURPLE_CBFLAGS_OP; - users = g_list_append(users, chu->client->nickname); - flags = g_list_append(flags, GINT_TO_POINTER(f)); - - if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) { - if (chu->client == conn->local_entry) - g_snprintf(tmp, sizeof(tmp), - _("You are channel founder on %s"), - channel->channel_name); - else - g_snprintf(tmp, sizeof(tmp), - _("Channel founder on %s is %s"), - channel->channel_name, chu->client->nickname); - - purple_conversation_write(convo, NULL, tmp, - PURPLE_MESSAGE_SYSTEM, time(NULL)); - } - } - - purple_conv_chat_add_users(PURPLE_CONV_CHAT(convo), users, NULL, flags, FALSE); - g_list_free(users); - g_list_free(flags); - - /* Set topic */ - if (topic) - purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, topic); - - /* Set nick */ - purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), conn->local_entry->nickname); - } - break; - - case SILC_COMMAND_LEAVE: - break; - - case SILC_COMMAND_USERS: - break; - - case SILC_COMMAND_WHOIS: - { - SilcUInt32 *user_modes; - SilcDList channels; - SilcClientEntry client_entry; - char tmp[1024], *tmp2; - char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr; - PurpleNotifyUserInfo *user_info; - - if (status != SILC_STATUS_OK) { - purple_notify_error(gc, _("User Information"), - _("Cannot get user information"), - silc_get_status_message(error)); - break; - } - - client_entry = va_arg(ap, SilcClientEntry); - (void)va_arg(ap, char *); - (void)va_arg(ap, char *); - (void)va_arg(ap, char *); - channels = va_arg(ap, SilcDList); - (void)va_arg(ap, SilcUInt32); - va_arg(ap, SilcUInt32); /* idle */ - (void)va_arg(ap, unsigned char *); - user_modes = va_arg(ap, SilcUInt32 *); - - user_info = purple_notify_user_info_new(); - tmp2 = g_markup_escape_text(client_entry->nickname, -1); - purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp2); - g_free(tmp2); - if (client_entry->realname) { - tmp2 = g_markup_escape_text(client_entry->realname, -1); - purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp2); - g_free(tmp2); - } - tmp2 = g_markup_escape_text(client_entry->username, -1); - if (*client_entry->hostname) { - gchar *tmp3; - tmp3 = g_strdup_printf("%s@%s", tmp2, client_entry->hostname); - purple_notify_user_info_add_pair(user_info, _("Username"), tmp3); - g_free(tmp3); - } else - purple_notify_user_info_add_pair(user_info, _("Username"), tmp2); - g_free(tmp2); - - if (client_entry->mode) { - memset(tmp, 0, sizeof(tmp)); - silcpurple_get_umode_string(client_entry->mode, - tmp, sizeof(tmp) - strlen(tmp)); - purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp); - } - - silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr); - if (moodstr) { - purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr); - g_free(moodstr); - } - - if (statusstr) { - tmp2 = g_markup_escape_text(statusstr, -1); - purple_notify_user_info_add_pair(user_info, _("Status Text"), tmp2); - g_free(statusstr); - g_free(tmp2); - } - - if (contactstr) { - purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr); - g_free(contactstr); - } - - if (langstr) { - purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr); - g_free(langstr); - } - - if (devicestr) { - purple_notify_user_info_add_pair(user_info, _("Device"), devicestr); - g_free(devicestr); - } - - if (tzstr) { - purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr); - g_free(tzstr); - } - - if (geostr) { - purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr); - g_free(geostr); - } - - if (*client_entry->server) - purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server); - - if (channels && user_modes) { - SilcChannelPayload entry; - int i = 0; - - memset(tmp, 0, sizeof(tmp)); - silc_dlist_start(channels); - while ((entry = silc_dlist_get(channels))) { - SilcUInt32 name_len; - char *m = silc_client_chumode_char(user_modes[i++]); - char *name = (char *)silc_channel_get_name(entry, &name_len); - if (m) - silc_strncat(tmp, sizeof(tmp) - 1, m, strlen(m)); - silc_strncat(tmp, sizeof(tmp) - 1, name, name_len); - silc_strncat(tmp, sizeof(tmp) - 1, " ", 1); - silc_free(m); - } - tmp2 = g_markup_escape_text(tmp, -1); - purple_notify_user_info_add_pair(user_info, _("Currently on"), tmp2); - g_free(tmp2); - } - - if (client_entry->public_key) { - char *fingerprint, *babbleprint; - unsigned char *pk; - SilcUInt32 pk_len; - pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len); - if (pk) { - fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); - purple_notify_user_info_add_pair(user_info, _("Public Key Fingerprint"), fingerprint); - purple_notify_user_info_add_pair(user_info, _("Public Key Babbleprint"), babbleprint); - silc_free(fingerprint); - silc_free(babbleprint); - silc_free(pk); - } - } - -#if 0 /* XXX for now, let's not show attrs here */ - if (client_entry->attrs) - purple_request_action(gc, _("User Information"), - _("User Information"), - buf, 1, client_entry, 2, - _("OK"), G_CALLBACK(silcpurple_whois_more), - _("_More..."), G_CALLBACK(silcpurple_whois_more), gc->account, NULL, NULL); - else -#endif /* 0 */ - purple_notify_userinfo(gc, client_entry->nickname, user_info, NULL, NULL); - purple_notify_user_info_destroy(user_info); - } - break; - - case SILC_COMMAND_WHOWAS: - { - SilcClientEntry client_entry; - char *nickname, *realname, *username, *tmp; - PurpleNotifyUserInfo *user_info; - - if (status != SILC_STATUS_OK) { - purple_notify_error(gc, _("User Information"), - _("Cannot get user information"), - silc_get_status_message(error)); - break; - } - - client_entry = va_arg(ap, SilcClientEntry); - nickname = va_arg(ap, char *); - username = va_arg(ap, char *); - realname = va_arg(ap, char *); - if (!nickname) - break; - - user_info = purple_notify_user_info_new(); - tmp = g_markup_escape_text(nickname, -1); - purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp); - g_free(tmp); - if (realname) { - tmp = g_markup_escape_text(realname, -1); - purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp); - g_free(tmp); - } - if (username) { - tmp = g_markup_escape_text(username, -1); - if (client_entry && *client_entry->hostname) { - gchar *tmp3; - tmp3 = g_strdup_printf("%s@%s", tmp, client_entry->hostname); - purple_notify_user_info_add_pair(user_info, _("Username"), tmp3); - g_free(tmp3); - } else - purple_notify_user_info_add_pair(user_info, _("Username"), tmp); - g_free(tmp); - } - if (client_entry && *client_entry->server) - purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server); - - - if (client_entry && client_entry->public_key) { - char *fingerprint, *babbleprint; - unsigned char *pk; - SilcUInt32 pk_len; - pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len); - if (pk) { - fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); - purple_notify_user_info_add_pair(user_info, _("Public Key Fingerprint"), fingerprint); - purple_notify_user_info_add_pair(user_info, _("Public Key Babbleprint"), babbleprint); - silc_free(fingerprint); - silc_free(babbleprint); - silc_free(pk); - } - } - - purple_notify_userinfo(gc, nickname, user_info, NULL, NULL); - purple_notify_user_info_destroy(user_info); - } - break; - - case SILC_COMMAND_DETACH: - { - const char *file; - SilcBuffer detach_data; - - if (status != SILC_STATUS_OK) { - purple_notify_error(gc, _("Detach From Server"), _("Cannot detach"), - silc_get_status_message(error)); - return; - } - - detach_data = va_arg(ap, SilcBuffer); - - /* Save the detachment data to file. */ - file = silcpurple_session_file(purple_account_get_username(sg->account)); - g_unlink(file); - silc_file_writefile(file, (const char *)silc_buffer_data(detach_data), - silc_buffer_len(detach_data)); - } - break; - - case SILC_COMMAND_TOPIC: - { - SilcChannelEntry channel; - - if (status != SILC_STATUS_OK) { - purple_notify_error(gc, _("Topic"), _("Cannot set topic"), - silc_get_status_message(error)); - return; - } - - channel = va_arg(ap, SilcChannelEntry); - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) { - purple_debug_error("silc", "Got a topic for %s, which doesn't exist\n", - channel->channel_name); - break; - } - - /* Set topic */ - if (channel->topic) - purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, channel->topic); - } - break; - - case SILC_COMMAND_NICK: - { - SilcClientEntry local_entry; - SilcHashTableList htl; - SilcChannelUser chu; - const char *oldnick, *newnick; - - if (status != SILC_STATUS_OK) { - purple_notify_error(gc, _("Nick"), _("Failed to change nickname"), - silc_get_status_message(error)); - return; - } - - local_entry = va_arg(ap, SilcClientEntry); - newnick = va_arg(ap, char *); - - /* Change nick on all channels */ - silc_hash_table_list(local_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - chu->channel->channel_name, sg->account); - if (!convo) - continue; - oldnick = purple_conv_chat_get_nick(PURPLE_CONV_CHAT(convo)); - if (!purple_strequal(oldnick, purple_normalize(purple_conversation_get_account(convo), newnick))) { - purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo), - oldnick, newnick); - purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), newnick); - } - } - silc_hash_table_list_reset(&htl); - - purple_connection_set_display_name(gc, newnick); - } - break; - - case SILC_COMMAND_LIST: - { - char *topic, *name; - int usercount; - PurpleRoomlistRoom *room; - - if (sg->roomlist_cancelled) - break; - - if (error != SILC_STATUS_OK) { - purple_notify_error(gc, _("Error"), _("Error retrieving room list"), - silc_get_status_message(error)); - purple_roomlist_set_in_progress(sg->roomlist, FALSE); - purple_roomlist_unref(sg->roomlist); - sg->roomlist = NULL; - return; - } - - (void)va_arg(ap, SilcChannelEntry); - name = va_arg(ap, char *); - if (!name) { - purple_notify_error(gc, _("Roomlist"), _("Cannot get room list"), - _("Network is empty")); - purple_roomlist_set_in_progress(sg->roomlist, FALSE); - purple_roomlist_unref(sg->roomlist); - sg->roomlist = NULL; - return; - } - topic = va_arg(ap, char *); - usercount = va_arg(ap, int); - - room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, name, NULL); - purple_roomlist_room_add_field(sg->roomlist, room, name); - purple_roomlist_room_add_field(sg->roomlist, room, - SILC_32_TO_PTR(usercount)); - purple_roomlist_room_add_field(sg->roomlist, room, - topic ? topic : ""); - purple_roomlist_room_add(sg->roomlist, room); - - if (status == SILC_STATUS_LIST_END || - status == SILC_STATUS_OK) { - purple_roomlist_set_in_progress(sg->roomlist, FALSE); - purple_roomlist_unref(sg->roomlist); - sg->roomlist = NULL; - } - } - break; - - case SILC_COMMAND_GETKEY: - { - SilcPublicKey public_key; - - if (status != SILC_STATUS_OK) { - purple_notify_error(gc, _("Get Public Key"), - _("Cannot fetch the public key"), - silc_get_status_message(error)); - return; - } - - (void)va_arg(ap, SilcUInt32); - (void)va_arg(ap, void *); - public_key = va_arg(ap, SilcPublicKey); - - if (!public_key) - purple_notify_error(gc, _("Get Public Key"), - _("Cannot fetch the public key"), - _("No public key was received")); - } - break; - - case SILC_COMMAND_INFO: - { - - char *server_name; - char *server_info; - char tmp[256]; - - if (status != SILC_STATUS_OK) { - purple_notify_error(gc, _("Server Information"), - _("Cannot get server information"), - silc_get_status_message(error)); - return; - } - - (void)va_arg(ap, SilcServerEntry); - server_name = va_arg(ap, char *); - server_info = va_arg(ap, char *); - - if (server_name && server_info) { - g_snprintf(tmp, sizeof(tmp), "Server: %s\n%s", - server_name, server_info); - purple_notify_info(gc, NULL, _("Server Information"), tmp); - } - } - break; - - case SILC_COMMAND_STATS: - { - SilcClientStats *stats; - char *msg; - - if (status != SILC_STATUS_OK) { - purple_notify_error(gc, _("Server Statistics"), - _("Cannot get server statistics"), - silc_get_status_message(error)); - return; - } - - stats = va_arg(ap, SilcClientStats *); - - msg = g_strdup_printf(_("Local server start time: %s\n" - "Local server uptime: %s\n" - "Local server clients: %d\n" - "Local server channels: %d\n" - "Local server operators: %d\n" - "Local router operators: %d\n" - "Local cell clients: %d\n" - "Local cell channels: %d\n" - "Local cell servers: %d\n" - "Total clients: %d\n" - "Total channels: %d\n" - "Total servers: %d\n" - "Total routers: %d\n" - "Total server operators: %d\n" - "Total router operators: %d\n"), - silc_time_string(stats->starttime), - purple_str_seconds_to_string((int)stats->uptime), - (int)stats->my_clients, - (int)stats->my_channels, - (int)stats->my_server_ops, - (int)stats->my_router_ops, - (int)stats->cell_clients, - (int)stats->cell_channels, - (int)stats->cell_servers, - (int)stats->clients, - (int)stats->channels, - (int)stats->servers, - (int)stats->routers, - (int)stats->server_ops, - (int)stats->router_ops); - - purple_notify_info(gc, NULL, - _("Network Statistics"), msg); - g_free(msg); - } - break; - - case SILC_COMMAND_PING: - { - if (status != SILC_STATUS_OK) { - purple_notify_error(gc, _("Ping"), _("Ping failed"), - silc_get_status_message(error)); - return; - } - - purple_notify_info(gc, _("Ping"), _("Ping reply received from server"), - NULL); - } - break; - - case SILC_COMMAND_KILL: - if (status != SILC_STATUS_OK) { - purple_notify_error(gc, _("Kill User"), - _("Could not kill user"), - silc_get_status_message(error)); - return; - } - break; - - case SILC_COMMAND_CMODE: - { - SilcChannelEntry channel_entry; - SilcDList channel_pubkeys, list; - SilcArgumentDecodedList e; - - if (status != SILC_STATUS_OK) - return; - - channel_entry = va_arg(ap, SilcChannelEntry); - (void)va_arg(ap, SilcUInt32); - (void)va_arg(ap, SilcPublicKey); - channel_pubkeys = va_arg(ap, SilcDList); - - if (!sg->chpk) - break; - - list = silc_dlist_init(); - - if (channel_pubkeys) { - silc_dlist_start(channel_pubkeys); - while ((e = silc_dlist_get(channel_pubkeys))) { - if (e->arg_type == 0x00 || - e->arg_type == 0x03) - silc_dlist_add(list, silc_pkcs_public_key_copy(e->argument)); - } - } - silcpurple_chat_chauth_show(sg, channel_entry, list); - } - break; - - case SILC_COMMAND_WATCH: - if (status != SILC_STATUS_OK) { - purple_notify_error(gc, _("WATCH"), _("Cannot watch user"), - silc_get_status_message(error)); - return; - } - break; - - default: - if (status == SILC_STATUS_OK) - purple_debug_info("silc", "Unhandled command: %d (succeeded)\n", command); - else - purple_debug_info("silc", "Unhandled command: %d (failed: %s)\n", command, - silc_get_status_message(error)); - break; - } -} - -/* Generic command reply callback for silc_client_command_send. Simply - calls the default command_reply client operation callback */ - -SilcBool silcpurple_command_reply(SilcClient client, SilcClientConnection conn, - SilcCommand command, SilcStatus status, - SilcStatus error, void *context, va_list ap) -{ - silc_command_reply(client, conn, command, status, error, ap); - return TRUE; -} - - -typedef struct { - union { - SilcAskPassphrase ask_pass; - SilcGetAuthMeth get_auth; - } u; - void *context; -} *SilcPurpleAskPassphrase; - -static void -silc_ask_auth_password_cb(const unsigned char *passphrase, - SilcUInt32 passphrase_len, void *context) -{ - SilcPurpleAskPassphrase internal = context; - - if (!passphrase || !(*passphrase)) - internal->u.get_auth(SILC_AUTH_NONE, NULL, 0, internal->context); - else - internal->u.get_auth(SILC_AUTH_PASSWORD, - (unsigned char *)passphrase, - passphrase_len, internal->context); - silc_free(internal); -} - -/* Find authentication method and authentication data by hostname and - port. The hostname may be IP address as well. The `auth_method' is - the authentication method the remote connection requires. It is - however possible that remote accepts also some other authentication - method. Application should use the method that may have been - configured for this connection. If none has been configured it should - use the required `auth_method'. If the `auth_method' is - SILC_AUTH_NONE, server does not require any authentication or the - required authentication method is not known. The `completion' - callback must be called to deliver the chosen authentication method - and data. The `conn' may be NULL. */ - -static void -silc_get_auth_method(SilcClient client, SilcClientConnection conn, - char *hostname, SilcUInt16 port, - SilcAuthMethod auth_method, - SilcGetAuthMeth completion, void *context) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - SilcPurpleAskPassphrase internal; - const char *password; - - /* Progress */ - if (sg->resuming) - purple_connection_update_progress(gc, _("Resuming session"), 4, 5); - else - purple_connection_update_progress(gc, _("Authenticating connection"), 4, 5); - - /* Check configuration if we have this connection configured. */ - if (auth_method == SILC_AUTH_PUBLIC_KEY && - purple_account_get_bool(sg->account, "pubkey-auth", FALSE)) { - completion(SILC_AUTH_PUBLIC_KEY, NULL, 0, context); - return; - } - if (auth_method == SILC_AUTH_PASSWORD) { - password = purple_connection_get_password(gc); - if (password && *password) { - completion(SILC_AUTH_PASSWORD, (unsigned char *)password, strlen(password), context); - return; - } - - /* Ask password from user */ - internal = silc_calloc(1, sizeof(*internal)); - if (!internal) - return; - internal->u.get_auth = completion; - internal->context = context; - silc_ask_passphrase(client, conn, silc_ask_auth_password_cb, - internal); - return; - } - - completion(SILC_AUTH_NONE, NULL, 0, context); -} - - -/* Called to verify received public key. The `conn_type' indicates which - entity (server or client) has sent the public key. If user decides to - trust the key the application may save the key as trusted public key for - later use. The `completion' must be called after the public key has - been verified. */ - -static void -silc_verify_public_key(SilcClient client, SilcClientConnection conn, - SilcConnectionType conn_type, - SilcPublicKey public_key, - SilcVerifyPublicKey completion, void *context) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - - if (!sg->conn && (conn_type == SILC_CONN_SERVER || - conn_type == SILC_CONN_ROUTER)) { - /* Progress */ - if (sg->resuming) - purple_connection_update_progress(gc, _("Resuming session"), 3, 5); - else - purple_connection_update_progress(gc, _("Verifying server public key"), - 3, 5); - } - - /* Verify public key */ - silcpurple_verify_public_key(client, conn, NULL, conn_type, - public_key, completion, context); -} - -static void -silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal, const char *passphrase) -{ - if (!passphrase || !(*passphrase)) - internal->u.ask_pass(NULL, 0, internal->context); - else - internal->u.ask_pass((unsigned char *)passphrase, - strlen(passphrase), internal->context); - silc_free(internal); -} - -/* Ask (interact, that is) a passphrase from user. The passphrase is - returned to the library by calling the `completion' callback with - the `context'. The returned passphrase SHOULD be in UTF-8 encoded, - if not then the library will attempt to encode. */ - -static void -silc_ask_passphrase(SilcClient client, SilcClientConnection conn, - SilcAskPassphrase completion, void *context) -{ - PurpleConnection *gc = client->application; - SilcPurpleAskPassphrase internal = silc_calloc(1, sizeof(*internal)); - - if (!internal) - return; - internal->u.ask_pass = completion; - internal->context = context; - purple_request_input(gc, _("Passphrase"), NULL, - _("Passphrase required"), NULL, FALSE, TRUE, NULL, - _("OK"), G_CALLBACK(silc_ask_passphrase_cb), - _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb), - purple_connection_get_account(gc), NULL, NULL, internal); -} - - -/* Called to indicate that incoming key agreement request has been - received. If the application wants to perform key agreement it may - call silc_client_perform_key_agreement to initiate key agreement or - silc_client_send_key_agreement to provide connection point to the - remote client in case the `hostname' is NULL. If key agreement is - not desired this request can be ignored. The `protocol' is either - value 0 for TCP or value 1 for UDP. */ - -static void -silc_key_agreement(SilcClient client, SilcClientConnection conn, - SilcClientEntry client_entry, - const char *hostname, SilcUInt16 protocol, - SilcUInt16 port) -{ - silcpurple_buddy_keyagr_request(client, conn, client_entry, - hostname, port, protocol); -} - - -/* Notifies application that file transfer protocol session is being - requested by the remote client indicated by the `client_entry' from - the `hostname' and `port'. The `session_id' is the file transfer - session and it can be used to either accept or reject the file - transfer request, by calling the silc_client_file_receive or - silc_client_file_close, respectively. */ - -static void -silc_ftp(SilcClient client, SilcClientConnection conn, - SilcClientEntry client_entry, SilcUInt32 session_id, - const char *hostname, SilcUInt16 port) -{ - silcpurple_ftp_request(client, conn, client_entry, session_id, - hostname, port); -} - -SilcClientOperations ops = { - silc_say, - silc_channel_message, - silc_private_message, - silc_notify, - silc_command, - silc_command_reply, - silc_get_auth_method, - silc_verify_public_key, - silc_ask_passphrase, - silc_key_agreement, - silc_ftp -}; diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc/pk.c --- a/libpurple/protocols/silc/pk.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,274 +0,0 @@ -/* - - silcpurple_pk.c - - Author: Pekka Riikonen - - Copyright (C) 2004 - 2007 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#include "internal.h" -#include "silc.h" -#include "silcclient.h" -#include "silcpurple.h" - -/************************* Public Key Verification ***************************/ - -typedef struct { - SilcClient client; - SilcClientConnection conn; - char *filename; - char *entity; - char *entity_name; - char *fingerprint; - char *babbleprint; - SilcPublicKey public_key; - SilcVerifyPublicKey completion; - void *context; - gboolean changed; -} *PublicKeyVerify; - -static void silcpurple_verify_ask(const char *entity, - const char *fingerprint, - const char *babbleprint, - PublicKeyVerify verify); - -static void silcpurple_verify_cb(PublicKeyVerify verify, gint id) -{ - if (id != 2) { - if (verify->completion) - verify->completion(FALSE, verify->context); - } else { - if (verify->completion) - verify->completion(TRUE, verify->context); - - /* Save the key for future checking */ - silc_pkcs_save_public_key(verify->filename, verify->public_key, - SILC_PKCS_FILE_BASE64); - } - - g_free(verify->filename); - g_free(verify->entity); - g_free(verify->entity_name); - silc_free(verify->fingerprint); - silc_free(verify->babbleprint); - silc_pkcs_public_key_free(verify->public_key); - silc_free(verify); -} - -static void silcpurple_verify_details_cb(PublicKeyVerify verify) -{ - /* What a hack. We have to display the accept dialog _again_ - because Purple closes the dialog after you press the button. Purple - should have option for the dialogs whether the buttons close them - or not. */ - silcpurple_verify_ask(verify->entity, verify->fingerprint, - verify->babbleprint, verify); -} - -static void silcpurple_verify_details(PublicKeyVerify verify, gint id) -{ - PurpleConnection *gc = verify->client->application; - SilcPurple sg = gc->proto_data; - - silcpurple_show_public_key(sg, verify->entity_name, verify->public_key, - G_CALLBACK(silcpurple_verify_details_cb), - verify); -} - -static void silcpurple_verify_ask(const char *entity, - const char *fingerprint, - const char *babbleprint, - PublicKeyVerify verify) -{ - PurpleConnection *gc = verify->client->application; - char tmp[256], tmp2[256]; - - if (verify->changed) { - g_snprintf(tmp, sizeof(tmp), - _("Received %s's public key. Your local copy does not match this " - "key. Would you still like to accept this public key?"), - entity); - } else { - g_snprintf(tmp, sizeof(tmp), - _("Received %s's public key. Would you like to accept this " - "public key?"), entity); - } - g_snprintf(tmp2, sizeof(tmp2), - _("Fingerprint and babbleprint for the %s key are:\n\n" - "%s\n%s\n"), entity, fingerprint, babbleprint); - - purple_request_action(gc, _("Verify Public Key"), tmp, tmp2, - PURPLE_DEFAULT_ACTION_NONE, - purple_connection_get_account(gc), entity, NULL, verify, 3, - _("Yes"), G_CALLBACK(silcpurple_verify_cb), - _("No"), G_CALLBACK(silcpurple_verify_cb), - _("_View..."), G_CALLBACK(silcpurple_verify_details)); -} - -void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn, - const char *name, SilcConnectionType conn_type, - SilcPublicKey public_key, - SilcVerifyPublicKey completion, void *context) -{ - PurpleConnection *gc = client->application; - gsize i; - char file[256], filename[256], filename2[256], *ipf, *hostf = NULL; - char *fingerprint, *babbleprint; - struct passwd *pw; - struct stat st; - char *entity = ((conn_type == SILC_CONN_SERVER || - conn_type == SILC_CONN_ROUTER) ? - "server" : "client"); - PublicKeyVerify verify; - const char *ip, *hostname; - SilcUInt16 port; - unsigned char *pk; - SilcUInt32 pk_len; - - if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) { - purple_notify_error(gc, _("Verify Public Key"), - _("Unsupported public key type"), NULL); - if (completion) - completion(FALSE, context); - return; - } - - pw = getpwuid(getuid()); - if (!pw) { - if (completion) - completion(FALSE, context); - return; - } - - memset(filename, 0, sizeof(filename)); - memset(filename2, 0, sizeof(filename2)); - memset(file, 0, sizeof(file)); - - silc_socket_stream_get_info(silc_packet_stream_get_stream(conn->stream), - NULL, &hostname, &ip, &port); - - pk = silc_pkcs_public_key_encode(public_key, &pk_len); - if (!pk) { - if (completion) - completion(FALSE, context); - return; - } - - if (conn_type == SILC_CONN_SERVER || - conn_type == SILC_CONN_ROUTER) { - if (!name) { - g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, - ip, port); - g_snprintf(filename, sizeof(filename) - 1, - "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", - silcpurple_silcdir(), entity, file); - - g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, - hostname, port); - g_snprintf(filename2, sizeof(filename2) - 1, - "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", - silcpurple_silcdir(), entity, file); - - ipf = filename; - hostf = filename2; - } else { - g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, - name, port); - g_snprintf(filename, sizeof(filename) - 1, - "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", - silcpurple_silcdir(), entity, file); - - ipf = filename; - } - } else { - /* Replace all whitespaces with `_'. */ - fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - for (i = 0; i < strlen(fingerprint); i++) - if (fingerprint[i] == ' ') - fingerprint[i] = '_'; - - g_snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint); - g_snprintf(filename, sizeof(filename) - 1, - "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", - silcpurple_silcdir(), entity, file); - silc_free(fingerprint); - - ipf = filename; - } - - verify = silc_calloc(1, sizeof(*verify)); - if (!verify) - return; - verify->client = client; - verify->conn = conn; - verify->filename = g_strdup(ipf); - verify->entity = g_strdup(entity); - verify->entity_name = (conn_type != SILC_CONN_CLIENT ? - (name ? g_strdup(name) : g_strdup(hostname)) - : NULL); - verify->public_key = silc_pkcs_public_key_copy(public_key); - verify->completion = completion; - verify->context = context; - fingerprint = verify->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - babbleprint = verify->babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); - - /* Check whether this key already exists */ - if (g_stat(ipf, &st) < 0 && (!hostf || g_stat(hostf, &st) < 0)) { - /* Key does not exist, ask user to verify the key and save it */ - silcpurple_verify_ask(name ? name : entity, - fingerprint, babbleprint, verify); - return; - } else { - /* The key already exists, verify it. */ - SilcPublicKey public_key; - unsigned char *encpk; - SilcUInt32 encpk_len; - - /* Load the key file, try for both IP filename and hostname filename */ - if (!silc_pkcs_load_public_key(ipf, &public_key) && - (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key)))) { - silcpurple_verify_ask(name ? name : entity, - fingerprint, babbleprint, verify); - return; - } - - /* Encode the key data */ - encpk = silc_pkcs_public_key_encode(public_key, &encpk_len); - if (!encpk) { - silcpurple_verify_ask(name ? name : entity, - fingerprint, babbleprint, verify); - return; - } - - /* Compare the keys */ - if (memcmp(encpk, pk, encpk_len)) { - /* Ask user to verify the key and save it */ - verify->changed = TRUE; - silcpurple_verify_ask(name ? name : entity, - fingerprint, babbleprint, verify); - return; - } - - /* Local copy matched */ - if (completion) - completion(TRUE, context); - g_free(verify->filename); - g_free(verify->entity); - g_free(verify->entity_name); - silc_free(verify->fingerprint); - silc_free(verify->babbleprint); - silc_pkcs_public_key_free(verify->public_key); - silc_free(verify); - } -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc/silc.c --- a/libpurple/protocols/silc/silc.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2263 +0,0 @@ -/* - - silcpurple.c - - Author: Pekka Riikonen - - Copyright (C) 2004 - 2007 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#include "internal.h" -#include "silc.h" -#include "silcclient.h" -#include "silcpurple.h" -#include "version.h" -#include "wb.h" -#include "core.h" - -extern SilcClientOperations ops; -static PurplePlugin *silc_plugin = NULL; - -/* Error log message callback */ - -static SilcBool silcpurple_log_error(SilcLogType type, char *message, - void *context) -{ - silc_say(NULL, NULL, SILC_CLIENT_MESSAGE_ERROR, message); - return TRUE; -} - -static const char * -silcpurple_list_icon(PurpleAccount *a, PurpleBuddy *b) -{ - return (const char *)"silc"; -} - -static GList * -silcpurple_away_states(PurpleAccount *account) -{ - PurpleStatusType *type; - GList *types = NULL; - - type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, TRUE, TRUE, FALSE); - types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), TRUE, TRUE, FALSE); - types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, TRUE, TRUE, FALSE); - types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), TRUE, TRUE, FALSE); - types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), TRUE, TRUE, FALSE); - types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), TRUE, TRUE, FALSE); - types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, TRUE, TRUE, FALSE); - types = g_list_append(types, type); - - return types; -} - -static void -silcpurple_set_status(PurpleAccount *account, PurpleStatus *status) -{ - PurpleConnection *gc = purple_account_get_connection(account); - SilcPurple sg = NULL; - SilcUInt32 mode; - SilcBuffer idp; - unsigned char mb[4]; - const char *state; - - if (gc != NULL) - sg = gc->proto_data; - - if (status == NULL) - return; - - state = purple_status_get_id(status); - - if (state == NULL) - return; - - if ((sg == NULL) || (sg->conn == NULL)) - return; - - mode = sg->conn->local_entry->mode; - mode &= ~(SILC_UMODE_GONE | - SILC_UMODE_HYPER | - SILC_UMODE_BUSY | - SILC_UMODE_INDISPOSED | - SILC_UMODE_PAGE); - - if (purple_strequal(state, "hyper")) - mode |= SILC_UMODE_HYPER; - else if (purple_strequal(state, "away")) - mode |= SILC_UMODE_GONE; - else if (purple_strequal(state, "busy")) - mode |= SILC_UMODE_BUSY; - else if (purple_strequal(state, "indisposed")) - mode |= SILC_UMODE_INDISPOSED; - else if (purple_strequal(state, "page")) - mode |= SILC_UMODE_PAGE; - - /* Send UMODE */ - idp = silc_id_payload_encode(sg->conn->local_id, SILC_ID_CLIENT); - SILC_PUT32_MSB(mode, mb); - silc_client_command_send(sg->client, sg->conn, SILC_COMMAND_UMODE, - silcpurple_command_reply, NULL, 2, - 1, idp->data, silc_buffer_len(idp), - 2, mb, sizeof(mb)); - silc_buffer_free(idp); -} - - -/*************************** Connection Routines *****************************/ - -static void -silcpurple_keepalive(PurpleConnection *gc) -{ - SilcPurple sg = gc->proto_data; - silc_packet_send(sg->conn->stream, SILC_PACKET_HEARTBEAT, 0, - NULL, 0); -} - -#if __SILC_TOOLKIT_VERSION < SILC_VERSION(1,1,1) -static gboolean -silcpurple_scheduler(gpointer *context) -{ - SilcClient client = (SilcClient)context; - silc_client_run_one(client); - return TRUE; -} -#else -typedef struct { - SilcPurple sg; - SilcUInt32 fd; - guint tag; -} *SilcPurpleTask; - -/* A timeout occurred. Call SILC scheduler. */ - -static gboolean -silcpurple_scheduler_timeout(gpointer context) -{ - SilcPurpleTask task = (SilcPurpleTask)context; - silc_client_run_one(task->sg->client); - silc_dlist_del(task->sg->tasks, task); - silc_free(task); - return FALSE; -} - -/* An fd task event occurred. Call SILC scheduler. */ - -static void -silcpurple_scheduler_fd(gpointer data, gint fd, PurpleInputCondition cond) -{ - SilcClient client = (SilcClient)data; - silc_client_run_one(client); -} - -/* SILC Scheduler notify callback. This is called whenever task is added to - or deleted from SILC scheduler. It's also called when fd task events - change. Here we add same tasks to glib's main loop. */ - -static void -silcpurple_scheduler(SilcSchedule schedule, - SilcBool added, SilcTask task, - SilcBool fd_task, SilcUInt32 fd, - SilcTaskEvent event, - long seconds, long useconds, - void *context) -{ - SilcClient client = (SilcClient)context; - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - SilcPurpleTask ptask = NULL; - - if (added) { - if (fd_task) { - /* Add fd or change fd events */ - PurpleInputCondition e = 0; - - silc_dlist_start(sg->tasks); - while ((ptask = silc_dlist_get(sg->tasks))) - if (ptask->fd == fd) { - purple_input_remove(ptask->tag); - break; - } - - if (event & SILC_TASK_READ) - e |= PURPLE_INPUT_READ; - if (event & SILC_TASK_WRITE) - e |= PURPLE_INPUT_WRITE; - - if (e) { - if (!ptask) { - ptask = silc_calloc(1, sizeof(*ptask)); - ptask->fd = fd; - silc_dlist_add(sg->tasks, ptask); - } - ptask->tag = purple_input_add(fd, e, silcpurple_scheduler_fd, - client); - } else if (ptask) { - silc_dlist_del(sg->tasks, ptask); - silc_free(ptask); - } - } else { - /* Add timeout */ - ptask = silc_calloc(1, sizeof(*ptask)); - ptask->sg = sg; - ptask->tag = purple_timeout_add((seconds * 1000) + - (useconds / 1000), - silcpurple_scheduler_timeout, - ptask); - silc_dlist_add(sg->tasks, ptask); - } - } else { - if (fd_task) { - /* Remove fd */ - silc_dlist_start(sg->tasks); - while ((ptask = silc_dlist_get(sg->tasks))) - if (ptask->fd == fd) { - purple_input_remove(ptask->tag); - silc_dlist_del(sg->tasks, ptask); - silc_free(ptask); - break; - } - } - } -} -#endif /* __SILC_TOOLKIT_VERSION */ - -static void -silcpurple_connect_cb(SilcClient client, SilcClientConnection conn, - SilcClientConnectionStatus status, SilcStatus error, - const char *message, void *context) -{ - PurpleConnection *gc = context; - SilcPurple sg; - SilcUInt32 mask; - char tz[16]; - PurpleStoredImage *img; -#ifdef HAVE_SYS_UTSNAME_H - struct utsname u; -#endif - - sg = gc->proto_data; - - switch (status) { - case SILC_CLIENT_CONN_SUCCESS: - case SILC_CLIENT_CONN_SUCCESS_RESUME: - sg->conn = conn; - - /* Connection created successfully */ - purple_connection_set_state(gc, PURPLE_CONNECTED); - - /* Send the server our buddy list */ - silcpurple_send_buddylist(gc); - - g_unlink(silcpurple_session_file(purple_account_get_username(sg->account))); - - /* Send any UMODEs configured for account */ - if (purple_account_get_bool(sg->account, "block-ims", FALSE)) { - silc_client_command_call(sg->client, sg->conn, NULL, - "UMODE", "+P", NULL); - } - - /* Set default attributes */ - mask = SILC_ATTRIBUTE_MOOD_NORMAL; - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_STATUS_MOOD, - SILC_32_TO_PTR(mask), - sizeof(SilcUInt32)); - mask = SILC_ATTRIBUTE_CONTACT_CHAT; - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_PREFERRED_CONTACT, - SILC_32_TO_PTR(mask), - sizeof(SilcUInt32)); -#ifdef HAVE_SYS_UTSNAME_H - if (!uname(&u)) { - SilcAttributeObjDevice dev; - memset(&dev, 0, sizeof(dev)); - dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER; - dev.version = u.release; - dev.model = u.sysname; - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_DEVICE_INFO, - (void *)&dev, sizeof(dev)); - } -#endif - silc_timezone(tz, sizeof(tz)); - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_TIMEZONE, - (void *)tz, strlen(tz)); - - /* Set our buddy icon */ - img = purple_buddy_icons_find_account_icon(sg->account); - silcpurple_buddy_set_icon(gc, img); - purple_imgstore_unref(img); - - return; - break; - - case SILC_CLIENT_CONN_DISCONNECTED: - /* Disconnected */ - if (sg->resuming && !sg->detaching) - g_unlink(silcpurple_session_file(purple_account_get_username(sg->account))); - - /* Close the connection */ - if (!sg->detaching) - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Disconnected by server")); - else - /* TODO: Does this work correctly? Maybe we need to set wants_to_die? */ - purple_account_disconnect(purple_connection_get_account(gc)); - break; - - case SILC_CLIENT_CONN_ERROR: - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Error connecting to SILC Server")); - g_unlink(silcpurple_session_file(purple_account_get_username(sg->account))); - break; - - case SILC_CLIENT_CONN_ERROR_KE: - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, - _("Key Exchange failed")); - break; - - case SILC_CLIENT_CONN_ERROR_AUTH: - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, - _("Authentication failed")); - break; - - case SILC_CLIENT_CONN_ERROR_RESUME: - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("Resuming detached session failed. " - "Press Reconnect to create new connection.")); - g_unlink(silcpurple_session_file(purple_account_get_username(sg->account))); - break; - - case SILC_CLIENT_CONN_ERROR_TIMEOUT: - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Connection timed out")); - break; - } - - /* Error */ - sg->conn = NULL; -} - -static void -silcpurple_stream_created(SilcSocketStreamStatus status, SilcStream stream, - void *context) -{ - PurpleConnection *gc = context; - SilcPurple sg; - SilcClient client; - SilcClientConnectionParams params; - const char *dfile; - - sg = gc->proto_data; - - if (status != SILC_SOCKET_OK) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Connection failed")); - silc_pkcs_public_key_free(sg->public_key); - silc_pkcs_private_key_free(sg->private_key); - silc_free(sg); - gc->proto_data = NULL; - return; - } - - client = sg->client; - - /* Get session detachment data, if available */ - memset(¶ms, 0, sizeof(params)); - dfile = silcpurple_session_file(purple_account_get_username(sg->account)); - params.detach_data = (unsigned char *)silc_file_readfile(dfile, ¶ms.detach_data_len); - if (params.detach_data) - params.detach_data[params.detach_data_len] = 0; - params.ignore_requested_attributes = FALSE; - params.pfs = purple_account_get_bool(sg->account, "pfs", FALSE); - - /* Progress */ - if (params.detach_data) { - purple_connection_update_progress(gc, _("Resuming session"), 2, 5); - sg->resuming = TRUE; - } else { - purple_connection_update_progress(gc, _("Performing key exchange"), 2, 5); - } - - /* Perform SILC Key Exchange. */ - silc_client_key_exchange(client, ¶ms, sg->public_key, - sg->private_key, stream, SILC_CONN_SERVER, - silcpurple_connect_cb, gc); - - silc_free(params.detach_data); -} - -static void -silcpurple_login_connected(gpointer data, gint source, const gchar *error_message) -{ - PurpleConnection *gc = data; - SilcPurple sg; - - g_return_if_fail(gc != NULL); - - sg = gc->proto_data; - - if (source < 0) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Connection failed")); - silc_pkcs_public_key_free(sg->public_key); - silc_pkcs_private_key_free(sg->private_key); - silc_free(sg); - gc->proto_data = NULL; - return; - } - - silc_hash_alloc((unsigned char *)"sha1", &sg->sha1hash); - - /* Wrap socket to TCP stream */ - silc_socket_tcp_stream_create(source, TRUE, FALSE, - sg->client->schedule, - silcpurple_stream_created, gc); -} - -static void silcpurple_continue_running(SilcPurple sg) -{ - PurpleConnection *gc = sg->gc; - PurpleAccount *account = purple_connection_get_account(gc); - - /* Connect to the SILC server */ - if (purple_proxy_connect(gc, account, - purple_account_get_string(account, "server", - "silc.silcnet.org"), - purple_account_get_int(account, "port", 706), - silcpurple_login_connected, gc) == NULL) - { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect")); - gc->proto_data = NULL; - silc_free(sg); - return; - } -} - -static void silcpurple_got_password_cb(PurpleConnection *gc, PurpleRequestFields *fields) -{ - SilcPurple sg = (SilcPurple)gc->proto_data; - PurpleAccount *account = purple_connection_get_account(gc); - char pkd[256], prd[256]; - const char *password; - gboolean remember; - - /* The password prompt dialog doesn't get disposed if the account disconnects */ - if (!PURPLE_CONNECTION_IS_VALID(gc)) - return; - - password = purple_request_fields_get_string(fields, "password"); - remember = purple_request_fields_get_bool(fields, "remember"); - - if (!password || !*password) - { - purple_notify_error(gc, NULL, _("Password is required to sign on."), NULL); - gc->proto_data = NULL; - silc_free(sg); - return; - } - - if (remember) - purple_account_set_remember_password(account, TRUE); - - purple_account_set_password(account, password); - - /* Load SILC key pair */ - g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir()); - g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir()); - if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd), - (char *)purple_account_get_string(account, "private-key", prd), - password, - &sg->public_key, &sg->private_key)) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("Unable to load SILC key pair")); - gc->proto_data = NULL; - silc_free(sg); - return; - } - silcpurple_continue_running(sg); -} - -static void silcpurple_no_password_cb(PurpleConnection *gc, PurpleRequestFields *fields) -{ - SilcPurple sg; - /* The password prompt dialog doesn't get disposed if the account disconnects */ - if (!PURPLE_CONNECTION_IS_VALID(gc)) - return; - sg = gc->proto_data; - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("Unable to load SILC key pair")); - gc->proto_data = NULL; - silc_free(sg); -} - -static void silcpurple_running(SilcClient client, void *context) -{ - SilcPurple sg = context; - PurpleConnection *gc = sg->gc; - PurpleAccount *account = purple_connection_get_account(gc); - char pkd[256], prd[256]; - - - /* Progress */ - purple_connection_update_progress(gc, _("Connecting to SILC Server"), 1, 5); - - /* Load SILC key pair */ - g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir()); - g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir()); - if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd), - (char *)purple_account_get_string(account, "private-key", prd), - (gc->password == NULL) ? "" : gc->password, - &sg->public_key, &sg->private_key)) { - if (!purple_account_get_password(account)) { - purple_account_request_password(account, G_CALLBACK(silcpurple_got_password_cb), - G_CALLBACK(silcpurple_no_password_cb), gc); - return; - } - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("Unable to load SILC key pair")); - gc->proto_data = NULL; - silc_free(sg); - return; - } - silcpurple_continue_running(sg); -} - -static void -silcpurple_login(PurpleAccount *account) -{ - SilcClient client; - PurpleConnection *gc; - SilcPurple sg; - SilcClientParams params; - const char *cipher, *hmac; - char *username, *hostname, *realname, **up; - int i; - - gc = account->gc; - if (!gc) - return; - gc->proto_data = NULL; - - memset(¶ms, 0, sizeof(params)); - strcat(params.nickname_format, "%n#a"); - - /* Allocate SILC client */ - client = silc_client_alloc(&ops, ¶ms, gc, NULL); - if (!client) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("Out of memory")); - return; - } - - /* Get username, real name and local hostname for SILC library */ - if (!purple_account_get_username(account)) - purple_account_set_username(account, silc_get_username()); - - username = (char *)purple_account_get_username(account); - up = g_strsplit(username, "@", 2); - username = g_strdup(up[0]); - g_strfreev(up); - - if (!purple_account_get_user_info(account)) { - purple_account_set_user_info(account, silc_get_real_name()); - if (!purple_account_get_user_info(account)) - purple_account_set_user_info(account, - "John T. Noname"); - } - realname = (char *)purple_account_get_user_info(account); - hostname = silc_net_localhost(); - - purple_connection_set_display_name(gc, username); - - /* Register requested cipher and HMAC */ - cipher = purple_account_get_string(account, "cipher", - SILC_DEFAULT_CIPHER); - for (i = 0; silc_default_ciphers[i].name; i++) - if (purple_strequal(silc_default_ciphers[i].name, cipher)) { - silc_cipher_register(&(silc_default_ciphers[i])); - break; - } - hmac = purple_account_get_string(account, "hmac", SILC_DEFAULT_HMAC); - for (i = 0; silc_default_hmacs[i].name; i++) - if (purple_strequal(silc_default_hmacs[i].name, hmac)) { - silc_hmac_register(&(silc_default_hmacs[i])); - break; - } - - sg = silc_calloc(1, sizeof(*sg)); - if (!sg) - return; - sg->client = client; - sg->gc = gc; - sg->account = account; - gc->proto_data = sg; - - /* Init SILC client */ - if (!silc_client_init(client, username, hostname, realname, - silcpurple_running, sg)) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("Unable to initialize SILC protocol")); - gc->proto_data = NULL; - silc_free(sg); - silc_free(hostname); - g_free(username); - return; - } - silc_free(hostname); - g_free(username); - - /* Check the ~/.silc dir and create it, and new key pair if necessary. */ - if (!silcpurple_check_silc_dir(gc)) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("Error loading SILC key pair")); - gc->proto_data = NULL; - silc_free(sg); - return; - } - -#if __SILC_TOOLKIT_VERSION < SILC_VERSION(1,1,1) - /* Schedule SILC using Glib's event loop */ - sg->scheduler = purple_timeout_add(300, (GSourceFunc)silcpurple_scheduler, client); -#else - /* Run SILC scheduler */ - sg->tasks = silc_dlist_init(); - silc_schedule_set_notify(client->schedule, silcpurple_scheduler, - client); - silc_client_run_one(client); -#endif /* __SILC_TOOLKIT_VERSION */ -} - -static int -silcpurple_close_final(gpointer *context) -{ - SilcPurple sg = (SilcPurple)context; - - purple_debug_info("silc", "Finalizing SilcPurple %p\n", sg); - - silc_client_stop(sg->client, NULL, NULL); - silc_client_free(sg->client); - if (sg->sha1hash) - silc_hash_free(sg->sha1hash); - if (sg->mimeass) - silc_mime_assembler_free(sg->mimeass); - silc_free(sg); - return 0; -} - -static void -silcpurple_close(PurpleConnection *gc) -{ - SilcPurple sg = gc->proto_data; -#if __SILC_TOOLKIT_VERSION >= SILC_VERSION(1,1,1) - SilcPurpleTask task; -#endif /* __SILC_TOOLKIT_VERSION */ - GHashTable *ui_info; - const char *ui_name = NULL, *ui_website = NULL; - char *quit_msg; - - g_return_if_fail(sg != NULL); - - ui_info = purple_core_get_ui_info(); - - if(ui_info) { - ui_name = g_hash_table_lookup(ui_info, "name"); - ui_website = g_hash_table_lookup(ui_info, "website"); - } - - if(!ui_name || !ui_website) { - ui_name = "Pidgin"; - ui_website = PURPLE_WEBSITE; - } - quit_msg = g_strdup_printf(_("Download %s: %s"), - ui_name, ui_website); - - /* Send QUIT */ - silc_client_command_call(sg->client, sg->conn, NULL, - "QUIT", quit_msg, - NULL); - g_free(quit_msg); - - if (sg->conn) - silc_client_close_connection(sg->client, sg->conn); - -#if __SILC_TOOLKIT_VERSION >= SILC_VERSION(1,1,1) - if (sg->conn) - silc_client_run_one(sg->client); - silc_schedule_set_notify(sg->client->schedule, NULL, NULL); - - silc_dlist_start(sg->tasks); - while ((task = silc_dlist_get(sg->tasks))) { - purple_input_remove(task->tag); - silc_free(task); - } - silc_dlist_uninit(sg->tasks); -#endif /* __SILC_TOOLKIT_VERSION */ - - purple_timeout_remove(sg->scheduler); - - purple_debug_info("silc", "Scheduling destruction of SilcPurple %p\n", sg); - purple_timeout_add(1, (GSourceFunc)silcpurple_close_final, sg); -} - - -/****************************** Protocol Actions *****************************/ - -static void -silcpurple_attrs_cancel(PurpleConnection *gc, PurpleRequestFields *fields) -{ - /* Nothing */ -} - -static void -silcpurple_attrs_cb(PurpleConnection *gc, PurpleRequestFields *fields) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - PurpleRequestField *f; - char *tmp; - SilcUInt32 tmp_len, mask; - SilcAttributeObjService service; - SilcAttributeObjDevice dev; - SilcVCardStruct vcard; - const char *val; - - sg = gc->proto_data; - if (!sg) - return; - - memset(&service, 0, sizeof(service)); - memset(&dev, 0, sizeof(dev)); - memset(&vcard, 0, sizeof(vcard)); - - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_USER_INFO, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_SERVICE, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_STATUS_MOOD, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_STATUS_FREETEXT, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_STATUS_MESSAGE, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_PREFERRED_LANGUAGE, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_PREFERRED_CONTACT, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_TIMEZONE, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_GEOLOCATION, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_DEVICE_INFO, NULL); - - /* Set mood */ - mask = 0; - f = purple_request_fields_get_field(fields, "mood_normal"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_NORMAL; - f = purple_request_fields_get_field(fields, "mood_happy"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_HAPPY; - f = purple_request_fields_get_field(fields, "mood_sad"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_SAD; - f = purple_request_fields_get_field(fields, "mood_angry"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_ANGRY; - f = purple_request_fields_get_field(fields, "mood_jealous"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_JEALOUS; - f = purple_request_fields_get_field(fields, "mood_ashamed"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_ASHAMED; - f = purple_request_fields_get_field(fields, "mood_invincible"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_INVINCIBLE; - f = purple_request_fields_get_field(fields, "mood_inlove"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_INLOVE; - f = purple_request_fields_get_field(fields, "mood_sleepy"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_SLEEPY; - f = purple_request_fields_get_field(fields, "mood_bored"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_BORED; - f = purple_request_fields_get_field(fields, "mood_excited"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_EXCITED; - f = purple_request_fields_get_field(fields, "mood_anxious"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_ANXIOUS; - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_STATUS_MOOD, - SILC_32_TO_PTR(mask), - sizeof(SilcUInt32)); - - /* Set preferred contact */ - mask = 0; - f = purple_request_fields_get_field(fields, "contact_chat"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_CONTACT_CHAT; - f = purple_request_fields_get_field(fields, "contact_email"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_CONTACT_EMAIL; - f = purple_request_fields_get_field(fields, "contact_call"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_CONTACT_CALL; - f = purple_request_fields_get_field(fields, "contact_sms"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_CONTACT_SMS; - f = purple_request_fields_get_field(fields, "contact_mms"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_CONTACT_MMS; - f = purple_request_fields_get_field(fields, "contact_video"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_CONTACT_VIDEO; - if (mask) - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_PREFERRED_CONTACT, - SILC_32_TO_PTR(mask), - sizeof(SilcUInt32)); - - /* Set status text */ - val = NULL; - f = purple_request_fields_get_field(fields, "status_text"); - if (f) - val = purple_request_field_string_get_value(f); - if (val && *val) - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_STATUS_FREETEXT, - (void *)val, strlen(val)); - - /* Set vcard */ - val = NULL; - f = purple_request_fields_get_field(fields, "vcard"); - if (f) - val = purple_request_field_string_get_value(f); - if (val && *val) { - purple_account_set_string(sg->account, "vcard", val); - tmp = silc_file_readfile(val, &tmp_len); - if (tmp) { - tmp[tmp_len] = 0; - if (silc_vcard_decode((unsigned char *)tmp, tmp_len, &vcard)) - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_USER_INFO, - (void *)&vcard, - sizeof(vcard)); - } - silc_vcard_free(&vcard); - silc_free(tmp); - } else { - purple_account_set_string(sg->account, "vcard", ""); - } - -#ifdef HAVE_SYS_UTSNAME_H - /* Set device info */ - f = purple_request_fields_get_field(fields, "device"); - if (f && purple_request_field_bool_get_value(f)) { - struct utsname u; - if (!uname(&u)) { - dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER; - dev.version = u.release; - dev.model = u.sysname; - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_DEVICE_INFO, - (void *)&dev, sizeof(dev)); - } - } -#endif - - /* Set timezone */ - val = NULL; - f = purple_request_fields_get_field(fields, "timezone"); - if (f) - val = purple_request_field_string_get_value(f); - if (val && *val) - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_TIMEZONE, - (void *)val, strlen(val)); -} - -static void -silcpurple_attrs(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - PurpleRequestFields *fields; - PurpleRequestFieldGroup *g; - PurpleRequestField *f; - SilcHashTable attrs; - SilcAttributePayload attr; - gboolean mnormal = TRUE, mhappy = FALSE, msad = FALSE, - mangry = FALSE, mjealous = FALSE, mashamed = FALSE, - minvincible = FALSE, minlove = FALSE, msleepy = FALSE, - mbored = FALSE, mexcited = FALSE, manxious = FALSE; - gboolean cemail = FALSE, ccall = FALSE, csms = FALSE, - cmms = FALSE, cchat = TRUE, cvideo = FALSE; - gboolean device = TRUE; - char status[1024], tz[16]; - - sg = gc->proto_data; - if (!sg) - return; - - memset(status, 0, sizeof(status)); - - attrs = silc_client_attributes_get(client, conn); - if (attrs) { - if (silc_hash_table_find(attrs, - SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_MOOD), - NULL, (void *)&attr)) { - SilcUInt32 mood = 0; - silc_attribute_get_object(attr, &mood, sizeof(mood)); - mnormal = !mood; - mhappy = (mood & SILC_ATTRIBUTE_MOOD_HAPPY); - msad = (mood & SILC_ATTRIBUTE_MOOD_SAD); - mangry = (mood & SILC_ATTRIBUTE_MOOD_ANGRY); - mjealous = (mood & SILC_ATTRIBUTE_MOOD_JEALOUS); - mashamed = (mood & SILC_ATTRIBUTE_MOOD_ASHAMED); - minvincible = (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE); - minlove = (mood & SILC_ATTRIBUTE_MOOD_INLOVE); - msleepy = (mood & SILC_ATTRIBUTE_MOOD_SLEEPY); - mbored = (mood & SILC_ATTRIBUTE_MOOD_BORED); - mexcited = (mood & SILC_ATTRIBUTE_MOOD_EXCITED); - manxious = (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS); - } - - if (silc_hash_table_find(attrs, - SILC_32_TO_PTR(SILC_ATTRIBUTE_PREFERRED_CONTACT), - NULL, (void *)&attr)) { - SilcUInt32 contact = 0; - silc_attribute_get_object(attr, &contact, sizeof(contact)); - cemail = (contact & SILC_ATTRIBUTE_CONTACT_EMAIL); - ccall = (contact & SILC_ATTRIBUTE_CONTACT_CALL); - csms = (contact & SILC_ATTRIBUTE_CONTACT_SMS); - cmms = (contact & SILC_ATTRIBUTE_CONTACT_MMS); - cchat = (contact & SILC_ATTRIBUTE_CONTACT_CHAT); - cvideo = (contact & SILC_ATTRIBUTE_CONTACT_VIDEO); - } - - if (silc_hash_table_find(attrs, - SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_FREETEXT), - NULL, (void *)&attr)) - silc_attribute_get_object(attr, &status, sizeof(status)); - - if (!silc_hash_table_find(attrs, - SILC_32_TO_PTR(SILC_ATTRIBUTE_DEVICE_INFO), - NULL, (void *)&attr)) - device = FALSE; - } - - fields = purple_request_fields_new(); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_label_new("l3", _("Your Current Mood")); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_normal", _("Normal"), mnormal); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_happy", _("Happy"), mhappy); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_sad", _("Sad"), msad); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_angry", _("Angry"), mangry); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_jealous", _("Jealous"), mjealous); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_ashamed", _("Ashamed"), mashamed); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_invincible", _("Invincible"), minvincible); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_inlove", _("In love"), minlove); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_sleepy", _("Sleepy"), msleepy); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_bored", _("Bored"), mbored); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_excited", _("Excited"), mexcited); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_anxious", _("Anxious"), manxious); - purple_request_field_group_add_field(g, f); - - f = purple_request_field_label_new("l4", _("\nYour Preferred Contact Methods")); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("contact_chat", _("Chat"), cchat); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("contact_email", _("Email"), cemail); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("contact_call", _("Phone"), ccall); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("contact_sms", _("SMS"), csms); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("contact_mms", _("MMS"), cmms); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("contact_video", _("Video conferencing"), cvideo); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_string_new("status_text", _("Your Current Status"), - status[0] ? status : NULL, TRUE); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - g = purple_request_field_group_new(NULL); -#if 0 - f = purple_request_field_label_new("l2", _("Online Services")); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("services", - _("Let others see what services you are using"), - TRUE); - purple_request_field_group_add_field(g, f); -#endif -#ifdef HAVE_SYS_UTSNAME_H - f = purple_request_field_bool_new("device", - _("Let others see what computer you are using"), - device); - purple_request_field_group_add_field(g, f); -#endif - purple_request_fields_add_group(fields, g); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_string_new("vcard", _("Your VCard File"), - purple_account_get_string(sg->account, "vcard", ""), - FALSE); - purple_request_field_group_add_field(g, f); - - silc_timezone(tz, sizeof(tz)); - f = purple_request_field_string_new("timezone", _("Timezone (UTC)"), tz, FALSE); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - purple_request_fields(gc, _("User Online Status Attributes"), - _("User Online Status Attributes"), - _("You can let other users see your online status information " - "and your personal information. Please fill the information " - "you would like other users to see about yourself."), - fields, - _("OK"), G_CALLBACK(silcpurple_attrs_cb), - _("Cancel"), G_CALLBACK(silcpurple_attrs_cancel), - gc->account, NULL, NULL, gc); -} - -static void -silcpurple_detach(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - SilcPurple sg; - - if (!gc) - return; - sg = gc->proto_data; - if (!sg) - return; - - /* Call DETACH */ - silc_client_command_call(sg->client, sg->conn, "DETACH"); - sg->detaching = TRUE; -} - -static void -silcpurple_view_motd(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - SilcPurple sg; - char *tmp; - - if (!gc) - return; - sg = gc->proto_data; - if (!sg) - return; - - if (!sg->motd) { - purple_notify_error( - gc, _("Message of the Day"), _("No Message of the Day available"), - _("There is no Message of the Day associated with this connection")); - return; - } - - tmp = g_markup_escape_text(sg->motd, -1); - purple_notify_formatted(gc, NULL, _("Message of the Day"), NULL, - tmp, NULL, NULL); - g_free(tmp); -} - -static void -silcpurple_create_keypair_cancel(PurpleConnection *gc, PurpleRequestFields *fields) -{ - /* Nothing */ -} - -static void -silcpurple_create_keypair_cb(PurpleConnection *gc, PurpleRequestFields *fields) -{ - SilcPurple sg; - PurpleRequestField *f; - const char *val, *pkfile = NULL, *prfile = NULL; - const char *pass1 = NULL, *pass2 = NULL, *un = NULL, *hn = NULL; - const char *rn = NULL, *e = NULL, *o = NULL, *c = NULL; - char *identifier; - int keylen = SILCPURPLE_DEF_PKCS_LEN; - SilcPublicKey public_key; - - sg = gc->proto_data; - if (!sg) - return; - - val = NULL; - f = purple_request_fields_get_field(fields, "pass1"); - if (f) - val = purple_request_field_string_get_value(f); - if (val && *val) - pass1 = val; - else - pass1 = ""; - val = NULL; - f = purple_request_fields_get_field(fields, "pass2"); - if (f) - val = purple_request_field_string_get_value(f); - if (val && *val) - pass2 = val; - else - pass2 = ""; - - if (!purple_strequal(pass1, pass2)) { - purple_notify_error( - gc, _("Create New SILC Key Pair"), _("Passphrases do not match"), NULL); - return; - } - - val = NULL; - f = purple_request_fields_get_field(fields, "key"); - if (f) - val = purple_request_field_string_get_value(f); - if (val && *val) - keylen = atoi(val); - f = purple_request_fields_get_field(fields, "pkfile"); - if (f) - pkfile = purple_request_field_string_get_value(f); - f = purple_request_fields_get_field(fields, "prfile"); - if (f) - prfile = purple_request_field_string_get_value(f); - - f = purple_request_fields_get_field(fields, "un"); - if (f) - un = purple_request_field_string_get_value(f); - f = purple_request_fields_get_field(fields, "hn"); - if (f) - hn = purple_request_field_string_get_value(f); - f = purple_request_fields_get_field(fields, "rn"); - if (f) - rn = purple_request_field_string_get_value(f); - f = purple_request_fields_get_field(fields, "e"); - if (f) - e = purple_request_field_string_get_value(f); - f = purple_request_fields_get_field(fields, "o"); - if (f) - o = purple_request_field_string_get_value(f); - f = purple_request_fields_get_field(fields, "c"); - if (f) - c = purple_request_field_string_get_value(f); - - identifier = silc_pkcs_silc_encode_identifier((char *)un, (char *)hn, - (char *)rn, (char *)e, - (char *)o, (char *)c, - NULL); - - /* Create the key pair */ - if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS, keylen, pkfile, prfile, - identifier, pass1, &public_key, NULL, - FALSE)) { - purple_notify_error( - gc, _("Create New SILC Key Pair"), _("Key Pair Generation failed"), NULL); - return; - } - - silcpurple_show_public_key(sg, NULL, public_key, NULL, NULL); - - silc_pkcs_public_key_free(public_key); - silc_free(identifier); -} - -static void -silcpurple_create_keypair(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - SilcPurple sg = gc->proto_data; - PurpleRequestFields *fields; - PurpleRequestFieldGroup *g; - PurpleRequestField *f; - const char *username, *realname; - char *hostname, **u; - char tmp[256], pkd[256], pkd2[256], prd[256], prd2[256]; - - username = purple_account_get_username(sg->account); - u = g_strsplit(username, "@", 2); - username = u[0]; - realname = purple_account_get_user_info(sg->account); - hostname = silc_net_localhost(); - g_snprintf(tmp, sizeof(tmp), "%s@%s", username, hostname); - - g_snprintf(pkd2, sizeof(pkd2), "%s" G_DIR_SEPARATOR_S"public_key.pub", silcpurple_silcdir()); - g_snprintf(prd2, sizeof(prd2), "%s" G_DIR_SEPARATOR_S"private_key.prv", silcpurple_silcdir()); - g_snprintf(pkd, sizeof(pkd) - 1, "%s", - purple_account_get_string(gc->account, "public-key", pkd2)); - g_snprintf(prd, sizeof(prd) - 1, "%s", - purple_account_get_string(gc->account, "private-key", prd2)); - - fields = purple_request_fields_new(); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_string_new("key", _("Key length"), "2048", FALSE); - purple_request_field_group_add_field(g, f); - f = purple_request_field_string_new("pkfile", _("Public key file"), pkd, FALSE); - purple_request_field_group_add_field(g, f); - f = purple_request_field_string_new("prfile", _("Private key file"), prd, FALSE); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_string_new("un", _("Username"), username ? username : "", FALSE); - purple_request_field_group_add_field(g, f); - f = purple_request_field_string_new("hn", _("Hostname"), hostname ? hostname : "", FALSE); - purple_request_field_group_add_field(g, f); - f = purple_request_field_string_new("rn", _("Real name"), realname ? realname : "", FALSE); - purple_request_field_group_add_field(g, f); - f = purple_request_field_string_new("e", _("Email"), tmp, FALSE); - purple_request_field_group_add_field(g, f); - f = purple_request_field_string_new("o", _("Organization"), "", FALSE); - purple_request_field_group_add_field(g, f); - f = purple_request_field_string_new("c", _("Country"), "", FALSE); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_string_new("pass1", _("Passphrase"), "", FALSE); - purple_request_field_string_set_masked(f, TRUE); - purple_request_field_group_add_field(g, f); - f = purple_request_field_string_new("pass2", _("Passphrase (retype)"), "", FALSE); - purple_request_field_string_set_masked(f, TRUE); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - purple_request_fields(gc, _("Create New SILC Key Pair"), - _("Create New SILC Key Pair"), NULL, fields, - _("Generate Key Pair"), G_CALLBACK(silcpurple_create_keypair_cb), - _("Cancel"), G_CALLBACK(silcpurple_create_keypair_cancel), - gc->account, NULL, NULL, gc); - - g_strfreev(u); - silc_free(hostname); -} - -static void -silcpurple_change_pass(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - purple_account_request_change_password(purple_connection_get_account(gc)); -} - -static void -silcpurple_change_passwd(PurpleConnection *gc, const char *old, const char *new) -{ - char prd[256]; - g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.pub", silcpurple_silcdir()); - silc_change_private_key_passphrase(purple_account_get_string(gc->account, - "private-key", - prd), old ? old : "", new ? new : ""); -} - -static void -silcpurple_show_set_info(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - purple_account_request_change_user_info(purple_connection_get_account(gc)); -} - -static void -silcpurple_set_info(PurpleConnection *gc, const char *text) -{ -} - -static GList * -silcpurple_actions(PurplePlugin *plugin, gpointer context) -{ - GList *list = NULL; - PurplePluginAction *act; - - act = purple_plugin_action_new(_("Online Status"), - silcpurple_attrs); - list = g_list_append(list, act); - - act = purple_plugin_action_new(_("Detach From Server"), - silcpurple_detach); - list = g_list_append(list, act); - - act = purple_plugin_action_new(_("View Message of the Day"), - silcpurple_view_motd); - list = g_list_append(list, act); - - act = purple_plugin_action_new(_("Create SILC Key Pair..."), - silcpurple_create_keypair); - list = g_list_append(list, act); - - act = purple_plugin_action_new(_("Change Password..."), - silcpurple_change_pass); - list = g_list_append(list, act); - - act = purple_plugin_action_new(_("Set User Info..."), - silcpurple_show_set_info); - list = g_list_append(list, act); - - return list; -} - - -/******************************* IM Routines *********************************/ - -typedef struct { - char *nick; - char *message; - SilcUInt32 message_len; - SilcMessageFlags flags; - PurpleMessageFlags gflags; -} *SilcPurpleIM; - -static void -silcpurple_send_im_resolved(SilcClient client, - SilcClientConnection conn, - SilcStatus status, - SilcDList clients, - void *context) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - SilcPurpleIM im = context; - PurpleConversation *convo; - char tmp[256]; - SilcClientEntry client_entry; - SilcDList list; - gboolean free_list = FALSE; - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->nick, - sg->account); - if (!convo) - return; - - if (!clients) - goto err; - - if (silc_dlist_count(clients) > 1) { - /* Find the correct one. The im->nick might be a formatted nick - so this will find the correct one. */ - clients = silc_client_get_clients_local(client, conn, - im->nick, FALSE); - if (!clients) - goto err; - - free_list = TRUE; - } - - silc_dlist_start(clients); - client_entry = silc_dlist_get(clients); - - /* Check for images */ - if (im->gflags & PURPLE_MESSAGE_IMAGES) { - list = silcpurple_image_message(im->message, &im->flags); - if (list) { - /* Send one or more MIME message. If more than one, they - are MIME fragments due to over large message */ - SilcBuffer buf; - - silc_dlist_start(list); - while ((buf = silc_dlist_get(list)) != SILC_LIST_END) - silc_client_send_private_message(client, conn, - client_entry, im->flags, sg->sha1hash, - buf->data, - silc_buffer_len(buf)); - silc_mime_partial_free(list); - purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname, - im->message, 0, time(NULL)); - goto out; - } - } - - /* Send the message */ - silc_client_send_private_message(client, conn, client_entry, im->flags, - sg->sha1hash, (unsigned char *)im->message, im->message_len); - purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname, - im->message, 0, time(NULL)); - goto out; - - err: - g_snprintf(tmp, sizeof(tmp), - _("User %s is not present in the network"), im->nick); - purple_conversation_write(convo, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL)); - - out: - if (free_list) { - silc_client_list_free(client, conn, clients); - } - g_free(im->nick); - g_free(im->message); - silc_free(im); -} - -static int -silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message, - PurpleMessageFlags flags) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcDList clients; - SilcClientEntry client_entry; - SilcMessageFlags mflags; - char *msg, *tmp; - int ret = 0; - gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE); - SilcDList list; - - if (!who || !message) - return 0; - - mflags = SILC_MESSAGE_FLAG_UTF8; - - tmp = msg = purple_unescape_html(message); - - if (!g_ascii_strncasecmp(msg, "/me ", 4)) { - msg += 4; - if (!*msg) { - g_free(tmp); - return 0; - } - mflags |= SILC_MESSAGE_FLAG_ACTION; - } else if (strlen(msg) > 1 && msg[0] == '/') { - if (!silc_client_command_call(client, conn, msg + 1)) - purple_notify_error(gc, _("Call Command"), - _("Cannot call command"), - _("Unknown command")); - g_free(tmp); - return 0; - } - - if (sign) - mflags |= SILC_MESSAGE_FLAG_SIGNED; - - /* Find client entry */ - clients = silc_client_get_clients_local(client, conn, who, FALSE); - if (!clients) { - /* Resolve unknown user */ - SilcPurpleIM im = silc_calloc(1, sizeof(*im)); - if (!im) { - g_free(tmp); - return 0; - } - im->nick = g_strdup(who); - im->message = g_strdup(message); - im->message_len = strlen(im->message); - im->flags = mflags; - im->gflags = flags; - silc_client_get_clients(client, conn, who, NULL, - silcpurple_send_im_resolved, im); - g_free(tmp); - return 0; - } - - silc_dlist_start(clients); - client_entry = silc_dlist_get(clients); - - /* Check for images */ - if (flags & PURPLE_MESSAGE_IMAGES) { - list = silcpurple_image_message(message, &mflags); - if (list) { - /* Send one or more MIME message. If more than one, they - are MIME fragments due to over large message */ - SilcBuffer buf; - - silc_dlist_start(list); - while ((buf = silc_dlist_get(list)) != SILC_LIST_END) - ret = - silc_client_send_private_message(client, conn, - client_entry, mflags, sg->sha1hash, - buf->data, - silc_buffer_len(buf)); - silc_mime_partial_free(list); - g_free(tmp); - silc_client_list_free(client, conn, clients); - return ret; - } - } - - /* Send private message directly */ - ret = silc_client_send_private_message(client, conn, client_entry, - mflags, sg->sha1hash, - (unsigned char *)msg, - strlen(msg)); - - g_free(tmp); - silc_client_list_free(client, conn, clients); - return ret; -} - - -static GList *silcpurple_blist_node_menu(PurpleBlistNode *node) { - /* split this single menu building function back into the two - original: one for buddies and one for chats */ - if(PURPLE_BLIST_NODE_IS_CHAT(node)) { - return silcpurple_chat_menu((PurpleChat *) node); - } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { - return silcpurple_buddy_menu((PurpleBuddy *) node); - } else { - g_return_val_if_reached(NULL); - } -} - -/********************************* Commands **********************************/ - -static PurpleCmdRet silcpurple_cmd_chat_part(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - PurpleConversation *convo = conv; - int id = 0; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - if(args && args[0]) - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[0], - gc->account); - - if (convo != NULL) - id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)); - - if (id == 0) - return PURPLE_CMD_RET_FAILED; - - silcpurple_chat_leave(gc, id); - - return PURPLE_CMD_RET_OK; - -} - -static PurpleCmdRet silcpurple_cmd_chat_topic(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - int id = 0; - char *buf, *tmp, *tmp2; - const char *topic; - - gc = purple_conversation_get_gc(conv); - id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)); - - if (gc == NULL || id == 0) - return PURPLE_CMD_RET_FAILED; - - if (!args || !args[0]) { - topic = purple_conv_chat_get_topic (PURPLE_CONV_CHAT(conv)); - if (topic) { - tmp = g_markup_escape_text(topic, -1); - tmp2 = purple_markup_linkify(tmp); - buf = g_strdup_printf(_("current topic is: %s"), tmp2); - g_free(tmp); - g_free(tmp2); - } else - buf = g_strdup(_("No topic is set")); - purple_conv_chat_write(PURPLE_CONV_CHAT(conv), gc->account->username, buf, - PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL)); - g_free(buf); - - } - - if (args && args[0] && (strlen(args[0]) > 255)) { - *error = g_strdup(_("Topic too long")); - return PURPLE_CMD_RET_FAILED; - } - - silcpurple_chat_set_topic(gc, id, args ? args[0] : NULL); - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_chat_join(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - GHashTable *comp; - - if(!args || !args[0]) - return PURPLE_CMD_RET_FAILED; - - comp = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); - - g_hash_table_replace(comp, "channel", args[0]); - if(args[1]) - g_hash_table_replace(comp, "passphrase", args[1]); - - silcpurple_chat_join(purple_conversation_get_gc(conv), comp); - - g_hash_table_destroy(comp); - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_chat_list(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - gc = purple_conversation_get_gc(conv); - purple_roomlist_show_with_account(purple_connection_get_account(gc)); - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_whois(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - silcpurple_get_info(gc, args[0]); - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_msg(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - int ret; - PurpleConnection *gc; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - ret = silcpurple_send_im(gc, args[0], args[1], PURPLE_MESSAGE_SEND); - - if (ret) - return PURPLE_CMD_RET_OK; - else - return PURPLE_CMD_RET_FAILED; -} - -static PurpleCmdRet silcpurple_cmd_query(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - int ret = 1; - PurpleConversation *convo; - PurpleConnection *gc; - PurpleAccount *account; - - if (!args || !args[0]) { - *error = g_strdup(_("You must specify a nick")); - return PURPLE_CMD_RET_FAILED; - } - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - account = purple_connection_get_account(gc); - - convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, args[0]); - - if (args[1]) { - ret = silcpurple_send_im(gc, args[0], args[1], PURPLE_MESSAGE_SEND); - purple_conv_im_write(PURPLE_CONV_IM(convo), purple_connection_get_display_name(gc), - args[1], PURPLE_MESSAGE_SEND, time(NULL)); - } - - if (ret) - return PURPLE_CMD_RET_OK; - else - return PURPLE_CMD_RET_FAILED; -} - -static PurpleCmdRet silcpurple_cmd_motd(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - SilcPurple sg; - char *tmp; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - sg = gc->proto_data; - - if (sg == NULL) - return PURPLE_CMD_RET_FAILED; - - if (!sg->motd) { - *error = g_strdup(_("There is no Message of the Day associated with this connection")); - return PURPLE_CMD_RET_FAILED; - } - - tmp = g_markup_escape_text(sg->motd, -1); - purple_notify_formatted(gc, NULL, _("Message of the Day"), NULL, - tmp, NULL, NULL); - g_free(tmp); - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_detach(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - SilcPurple sg; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - sg = gc->proto_data; - - if (sg == NULL) - return PURPLE_CMD_RET_FAILED; - - silc_client_command_call(sg->client, sg->conn, "DETACH"); - sg->detaching = TRUE; - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_cmode(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - SilcPurple sg; - SilcChannelEntry channel; - char *silccmd, *silcargs, *msg, tmp[256]; - const char *chname; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL || !args || gc->proto_data == NULL) - return PURPLE_CMD_RET_FAILED; - - sg = gc->proto_data; - - if (args[0]) - chname = args[0]; - else - chname = purple_conversation_get_name(conv); - - if (!args[1]) { - channel = silc_client_get_channel(sg->client, sg->conn, - (char *)chname); - if (!channel) { - *error = g_strdup_printf(_("channel %s not found"), chname); - return PURPLE_CMD_RET_FAILED; - } - if (channel->mode) { - silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp)); - msg = g_strdup_printf(_("channel modes for %s: %s"), chname, tmp); - } else { - msg = g_strdup_printf(_("no channel modes are set on %s"), chname); - } - purple_conv_chat_write(PURPLE_CONV_CHAT(conv), "", - msg, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL)); - g_free(msg); - return PURPLE_CMD_RET_OK; - } - - silcargs = g_strjoinv(" ", args); - silccmd = g_strconcat(cmd, " ", silcargs, NULL); - g_free(silcargs); - if (!silc_client_command_call(sg->client, sg->conn, silccmd)) { - g_free(silccmd); - *error = g_strdup_printf(_("Failed to set cmodes for %s"), args[0]); - return PURPLE_CMD_RET_FAILED; - } - g_free(silccmd); - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_generic(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - SilcPurple sg; - char *silccmd, *silcargs; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - sg = gc->proto_data; - - if (sg == NULL) - return PURPLE_CMD_RET_FAILED; - - silcargs = g_strjoinv(" ", args); - silccmd = g_strconcat(cmd, " ", args ? silcargs : NULL, NULL); - g_free(silcargs); - if (!silc_client_command_call(sg->client, sg->conn, silccmd)) { - g_free(silccmd); - *error = g_strdup_printf(_("Unknown command: %s, (may be a client bug)"), cmd); - return PURPLE_CMD_RET_FAILED; - } - g_free(silccmd); - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_quit(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - SilcPurple sg; - GHashTable *ui_info; - const char *ui_name = NULL, *ui_website = NULL; - char *quit_msg; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - sg = gc->proto_data; - - if (sg == NULL) - return PURPLE_CMD_RET_FAILED; - - ui_info = purple_core_get_ui_info(); - - if(ui_info) { - ui_name = g_hash_table_lookup(ui_info, "name"); - ui_website = g_hash_table_lookup(ui_info, "website"); - } - - if(!ui_name || !ui_website) { - ui_name = "Pidgin"; - ui_website = PURPLE_WEBSITE; - } - quit_msg = g_strdup_printf(_("Download %s: %s"), - ui_name, ui_website); - - silc_client_command_call(sg->client, sg->conn, NULL, - "QUIT", (args && args[0]) ? args[0] : quit_msg, NULL); - g_free(quit_msg); - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_call(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - SilcPurple sg; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - sg = gc->proto_data; - - if (sg == NULL) - return PURPLE_CMD_RET_FAILED; - - if (!silc_client_command_call(sg->client, sg->conn, args[0])) { - *error = g_strdup_printf(_("Unknown command: %s"), args[0]); - return PURPLE_CMD_RET_FAILED; - } - - return PURPLE_CMD_RET_OK; -} - - -/************************** Plugin Initialization ****************************/ - -static void -silcpurple_register_commands(void) -{ - purple_cmd_register("part", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | - PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, - "prpl-silc", silcpurple_cmd_chat_part, _("part [channel]: Leave the chat"), NULL); - purple_cmd_register("leave", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | - PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, - "prpl-silc", silcpurple_cmd_chat_part, _("leave [channel]: Leave the chat"), NULL); - purple_cmd_register("topic", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", - silcpurple_cmd_chat_topic, _("topic [<new topic>]: View or change the topic"), NULL); - purple_cmd_register("join", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | - PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, - "prpl-silc", silcpurple_cmd_chat_join, - _("join <channel> [<password>]: Join a chat on this network"), NULL); - purple_cmd_register("list", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", - silcpurple_cmd_chat_list, _("list: List channels on this network"), NULL); - purple_cmd_register("whois", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", - silcpurple_cmd_whois, _("whois <nick>: View nick's information"), NULL); - purple_cmd_register("msg", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_msg, - _("msg <nick> <message>: Send a private message to a user"), NULL); - purple_cmd_register("query", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_query, - _("query <nick> [<message>]: Send a private message to a user"), NULL); - purple_cmd_register("motd", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_motd, - _("motd: View the server's Message Of The Day"), NULL); - purple_cmd_register("detach", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_detach, - _("detach: Detach this session"), NULL); - purple_cmd_register("quit", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_quit, - _("quit [message]: Disconnect from the server, with an optional message"), NULL); - purple_cmd_register("call", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_call, - _("call <command>: Call any silc client command"), NULL); - /* These below just get passed through for the silc client library to deal - * with */ - purple_cmd_register("kill", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, - _("kill <nick> [-pubkey|<reason>]: Kill nick"), NULL); - purple_cmd_register("nick", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_generic, - _("nick <newnick>: Change your nickname"), NULL); - purple_cmd_register("whowas", "ww", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, - _("whowas <nick>: View nick's information"), NULL); - purple_cmd_register("cmode", "wws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_cmode, - _("cmode <channel> [+|-<modes>] [arguments]: Change or display channel modes"), NULL); - purple_cmd_register("cumode", "wws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, - _("cumode <channel> +|-<modes> <nick>: Change nick's modes on channel"), NULL); - purple_cmd_register("umode", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_generic, - _("umode <usermodes>: Set your modes in the network"), NULL); - purple_cmd_register("oper", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_generic, - _("oper <nick> [-pubkey]: Get server operator privileges"), NULL); - purple_cmd_register("invite", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, - _("invite <channel> [-|+]<nick>: invite nick or add/remove from channel invite list"), NULL); - purple_cmd_register("kick", "wws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, - _("kick <channel> <nick> [comment]: Kick client from channel"), NULL); - purple_cmd_register("info", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, - _("info [server]: View server administrative details"), NULL); - purple_cmd_register("ban", "ww", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, - _("ban [<channel> +|-<nick>]: Ban client from channel"), NULL); - purple_cmd_register("getkey", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_generic, - _("getkey <nick|server>: Retrieve client's or server's public key"), NULL); - purple_cmd_register("stats", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_generic, - _("stats: View server and network statistics"), NULL); - purple_cmd_register("ping", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_generic, - _("ping: Send PING to the connected server"), NULL); -#if 0 /* Purple doesn't handle these yet */ - purple_cmd_register("users", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_users, - _("users <channel>: List users in channel")); - purple_cmd_register("names", "ww", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_names, - _("names [-count|-ops|-halfops|-voices|-normal] <channel(s)>: List specific users in channel(s)")); -#endif -} - -static PurpleWhiteboardPrplOps silcpurple_wb_ops = -{ - silcpurple_wb_start, - silcpurple_wb_end, - silcpurple_wb_get_dimensions, - silcpurple_wb_set_dimensions, - silcpurple_wb_get_brush, - silcpurple_wb_set_brush, - silcpurple_wb_send, - silcpurple_wb_clear, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginProtocolInfo prpl_info = -{ - OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | - OPT_PROTO_PASSWORD_OPTIONAL | OPT_PROTO_IM_IMAGE | - OPT_PROTO_SLASH_COMMANDS_NATIVE, - NULL, /* user_splits */ - NULL, /* protocol_options */ - {"jpeg,gif,png,bmp", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ - silcpurple_list_icon, /* list_icon */ - NULL, /* list_emblems */ - silcpurple_status_text, /* status_text */ - silcpurple_tooltip_text, /* tooltip_text */ - silcpurple_away_states, /* away_states */ - silcpurple_blist_node_menu, /* blist_node_menu */ - silcpurple_chat_info, /* chat_info */ - silcpurple_chat_info_defaults, /* chat_info_defaults */ - silcpurple_login, /* login */ - silcpurple_close, /* close */ - silcpurple_send_im, /* send_im */ - silcpurple_set_info, /* set_info */ - NULL, /* send_typing */ - silcpurple_get_info, /* get_info */ - silcpurple_set_status, /* set_status */ - silcpurple_idle_set, /* set_idle */ - silcpurple_change_passwd, /* change_passwd */ - silcpurple_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - silcpurple_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - NULL, /* add_deny */ - NULL, /* rem_permit */ - NULL, /* rem_deny */ - NULL, /* set_permit_deny */ - silcpurple_chat_join, /* join_chat */ - NULL, /* reject_chat */ - silcpurple_get_chat_name, /* get_chat_name */ - silcpurple_chat_invite, /* chat_invite */ - silcpurple_chat_leave, /* chat_leave */ - NULL, /* chat_whisper */ - silcpurple_chat_send, /* chat_send */ - silcpurple_keepalive, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - NULL, /* get_cb_away */ - NULL, /* alias_buddy */ - NULL, /* group_buddy */ - NULL, /* rename_group */ - NULL, /* buddy_free */ - NULL, /* convo_closed */ - NULL, /* normalize */ - silcpurple_buddy_set_icon, /* set_buddy_icon */ - NULL, /* remove_group */ - NULL, /* get_cb_real_name */ - silcpurple_chat_set_topic, /* set_chat_topic */ - NULL, /* find_blist_chat */ - silcpurple_roomlist_get_list, /* roomlist_get_list */ - silcpurple_roomlist_cancel, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - NULL, /* can_receive_file */ - silcpurple_ftp_send_file, /* send_file */ - silcpurple_ftp_new_xfer, /* new_xfer */ - NULL, /* offline_message */ - &silcpurple_wb_ops, /* whiteboard_prpl_ops */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - NULL, /* send_attention */ - NULL, /* get_attention_types */ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - NULL, /* add_buddy_with_invite */ - NULL, /* add_buddies_with_invite */ - NULL, /* get_cb_alias */ - NULL, /* chat_can_receive_file */ - NULL, /* chat_send_file */ -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-silc", /**< id */ - "SILC", /**< name */ - "1.1", /**< version */ - /** summary */ - N_("SILC Protocol Plugin"), - /** description */ - N_("Secure Internet Live Conferencing (SILC) Protocol"), - "Pekka Riikonen", /**< author */ - "http://silcnet.org/", /**< homepage */ - - NULL, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, /**< prefs_info */ - silcpurple_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -#if 0 -static SilcBool silcpurple_debug_cb(char *file, char *function, int line, - char *message, void *context) -{ - purple_debug_info("SILC", "%s:%d:%s - %s\n", file ? file : "(null)", line, function ? function : "(null)", message ? message : "(null)"); - return TRUE; -} -#endif - -static void -init_plugin(PurplePlugin *plugin) -{ - PurpleAccountOption *option; - PurpleAccountUserSplit *split; - char tmp[256]; - int i; - PurpleKeyValuePair *kvp; - GList *list = NULL; - - silc_plugin = plugin; - - split = purple_account_user_split_new(_("Network"), "silcnet.org", '@'); - prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); - - /* Account options */ - option = purple_account_option_string_new(_("Connect server"), - "server", - "silc.silcnet.org"); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_int_new(_("Port"), "port", 706); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir()); - option = purple_account_option_string_new(_("Public Key file"), - "public-key", tmp); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir()); - option = purple_account_option_string_new(_("Private Key file"), - "private-key", tmp); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - for (i = 0; silc_default_ciphers[i].name; i++) { - kvp = g_new0(PurpleKeyValuePair, 1); - kvp->key = g_strdup(silc_default_ciphers[i].name); - kvp->value = g_strdup(silc_default_ciphers[i].name); - list = g_list_append(list, kvp); - } - option = purple_account_option_list_new(_("Cipher"), "cipher", list); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - list = NULL; - for (i = 0; silc_default_hmacs[i].name; i++) { - kvp = g_new0(PurpleKeyValuePair, 1); - kvp->key = g_strdup(silc_default_hmacs[i].name); - kvp->value = g_strdup(silc_default_hmacs[i].name); - list = g_list_append(list, kvp); - } - option = purple_account_option_list_new(_("HMAC"), "hmac", list); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_bool_new(_("Use Perfect Forward Secrecy"), - "pfs", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_bool_new(_("Public key authentication"), - "pubkey-auth", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_bool_new(_("Block IMs without Key Exchange"), - "block-ims", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_bool_new(_("Block messages to whiteboard"), - "block-wb", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_bool_new(_("Automatically open whiteboard"), - "open-wb", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_bool_new(_("Digitally sign and verify all messages"), - "sign-verify", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - purple_prefs_remove("/plugins/prpl/silc"); - - silc_log_set_callback(SILC_LOG_ERROR, silcpurple_log_error, NULL); - silcpurple_register_commands(); - -#if 0 -silc_log_debug(TRUE); -silc_log_set_debug_string("*client*"); -silc_log_quick(TRUE); -silc_log_set_debug_callbacks(silcpurple_debug_cb, NULL, NULL, NULL); -#endif - -} - -PURPLE_INIT_PLUGIN(silc, init_plugin, info); diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc/silcpurple.h --- a/libpurple/protocols/silc/silcpurple.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,176 +0,0 @@ -/* - - silcpurple.h - - Author: Pekka Riikonen - - Copyright (C) 2004 - 2007 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#ifndef SILCPURPLE_H -#define SILCPURPLE_H - -/* Purple includes */ -#include "internal.h" -#include "account.h" -#include "accountopt.h" -#include "cmds.h" -#include "conversation.h" -#include "debug.h" -#include "ft.h" -#include "notify.h" -#include "prpl.h" -#include "request.h" -#include "roomlist.h" -#include "server.h" -#include "util.h" - -#undef SILC_VERSION -#define SILC_VERSION(a, b, c) (((a) << 24) + ((b) << 16) + ((c) << 8)) - -/* Default public and private key file names */ -#define SILCPURPLE_PUBLIC_KEY_NAME "public_key.pub" -#define SILCPURPLE_PRIVATE_KEY_NAME "private_key.prv" - -/* Default settings for creating key pair */ -#define SILCPURPLE_DEF_PKCS "rsa" -#define SILCPURPLE_DEF_PKCS_LEN 2048 - -#define SILCPURPLE_PRVGRP 0x001fffff - -/* Status IDs */ -#define SILCPURPLE_STATUS_ID_OFFLINE "offline" -#define SILCPURPLE_STATUS_ID_AVAILABLE "available" -#define SILCPURPLE_STATUS_ID_HYPER "hyper" -#define SILCPURPLE_STATUS_ID_AWAY "away" -#define SILCPURPLE_STATUS_ID_BUSY "busy" -#define SILCPURPLE_STATUS_ID_INDISPOSED "indisposed" -#define SILCPURPLE_STATUS_ID_PAGE "page" - -typedef struct { - unsigned long id; - const char *channel; - unsigned long chid; - const char *parentch; - SilcChannelPrivateKey key; -} *SilcPurplePrvgrp; - -/* The SILC Purple plugin context */ -typedef struct SilcPurpleStruct { - SilcClient client; - SilcClientConnection conn; - SilcPublicKey public_key; - SilcPrivateKey private_key; - SilcHash sha1hash; - - SilcDList tasks; - guint scheduler; - PurpleConnection *gc; - PurpleAccount *account; - unsigned long channel_ids; - GList *grps; - - char *motd; - PurpleRoomlist *roomlist; - SilcMimeAssembler mimeass; - unsigned int detaching : 1; - unsigned int resuming : 1; - unsigned int roomlist_cancelled : 1; - unsigned int chpk : 1; -} *SilcPurple; - - -void silc_say(SilcClient client, SilcClientConnection conn, - SilcClientMessageType type, char *msg, ...); -SilcBool silcpurple_command_reply(SilcClient client, SilcClientConnection conn, - SilcCommand command, SilcStatus status, - SilcStatus error, void *context, va_list ap); -gboolean silcpurple_check_silc_dir(PurpleConnection *gc); -const char *silcpurple_silcdir(void); -const char *silcpurple_session_file(const char *account); -void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn, - const char *name, - SilcConnectionType conn_type, - SilcPublicKey public_key, - SilcVerifyPublicKey completion, - void *context); -GList *silcpurple_buddy_menu(PurpleBuddy *buddy); -void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); -void silcpurple_send_buddylist(PurpleConnection *gc); -void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); -void silcpurple_buddy_keyagr_request(SilcClient client, - SilcClientConnection conn, - SilcClientEntry client_entry, - const char *hostname, SilcUInt16 port, - SilcUInt16 protocol); -void silcpurple_idle_set(PurpleConnection *gc, int idle); -void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full); -char *silcpurple_status_text(PurpleBuddy *b); -gboolean silcpurple_ip_is_private(const char *ip); -void silcpurple_ftp_send_file(PurpleConnection *gc, const char *name, const char *file); -PurpleXfer *silcpurple_ftp_new_xfer(PurpleConnection *gc, const char *name); -void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn, - SilcClientEntry client_entry, SilcUInt32 session_id, - const char *hostname, SilcUInt16 port); -void silcpurple_show_public_key(SilcPurple sg, - const char *name, SilcPublicKey public_key, - GCallback callback, void *context); -void silcpurple_get_info(PurpleConnection *gc, const char *who); -SilcAttributePayload -silcpurple_get_attr(SilcDList attrs, SilcAttribute attribute); -void silcpurple_get_umode_string(SilcUInt32 mode, char *buf, - SilcUInt32 buf_size); -void silcpurple_get_chmode_string(SilcUInt32 mode, char *buf, - SilcUInt32 buf_size); -void silcpurple_get_chumode_string(SilcUInt32 mode, char *buf, - SilcUInt32 buf_size); -GList *silcpurple_chat_info(PurpleConnection *gc); -GHashTable *silcpurple_chat_info_defaults(PurpleConnection *gc, const char *chat_name); -GList *silcpurple_chat_menu(PurpleChat *); -void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data); -char *silcpurple_get_chat_name(GHashTable *data); -void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg, - const char *name); -void silcpurple_chat_leave(PurpleConnection *gc, int id); -int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg, PurpleMessageFlags flags); -void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic); -PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc); -void silcpurple_roomlist_cancel(PurpleRoomlist *list); -void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel, - SilcDList channel_pubkeys); -void silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr, - char **contactstr, char **langstr, char **devicestr, - char **tzstr, char **geostr); -void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img); -char *silcpurple_file2mime(const char *filename); -SilcDList silcpurple_image_message(const char *msg, SilcMessageFlags *mflags); - -#ifdef _WIN32 -typedef int uid_t; - -struct passwd { - char *pw_name; /* user name */ - char *pw_passwd; /* user password */ - int pw_uid; /* user id */ - int pw_gid; /* group id */ - char *pw_gecos; /* real name */ - char *pw_dir; /* home directory */ - char *pw_shell; /* shell program */ -}; - -struct passwd *getpwuid(int uid); -int getuid(void); -int geteuid(void); -#endif - -#endif /* SILCPURPLE_H */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc/util.c --- a/libpurple/protocols/silc/util.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,728 +0,0 @@ -/* - - silcpurple_util.c - - Author: Pekka Riikonen - - Copyright (C) 2004 - 2007 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#include "internal.h" -#include "silc.h" -#include "silcclient.h" -#include "silcpurple.h" -#include "imgstore.h" - -/**************************** Utility Routines *******************************/ - -static char str[256], str2[256]; - -const char *silcpurple_silcdir(void) -{ - const char *hd = purple_home_dir(); - memset(str, 0, sizeof(str)); - g_snprintf(str, sizeof(str) - 1, "%s" G_DIR_SEPARATOR_S ".silc", hd ? hd : "/tmp"); - return (const char *)str; -} - -const char *silcpurple_session_file(const char *account) -{ - memset(str2, 0, sizeof(str2)); - g_snprintf(str2, sizeof(str2) - 1, "%s" G_DIR_SEPARATOR_S "%s_session", - silcpurple_silcdir(), account); - return (const char *)str2; -} - -gboolean silcpurple_ip_is_private(const char *ip) -{ - if (silc_net_is_ip4(ip)) { - if (!strncmp(ip, "10.", 3)) { - return TRUE; - } else if (!strncmp(ip, "172.", 4) && strlen(ip) > 6) { - char tmp[3]; - int s; - memset(tmp, 0, sizeof(tmp)); - strncpy(tmp, ip + 4, 2); - s = atoi(tmp); - if (s >= 16 && s <= 31) - return TRUE; - } else if (!strncmp(ip, "192.168.", 8)) { - return TRUE; - } - } - - return FALSE; -} - -/* This checks stats for various SILC files and directories. First it - checks if ~/.silc directory exist and is owned by the correct user. If - it doesn't exist, it will create the directory. After that it checks if - user's Public and Private key files exists and creates them if needed. */ - -gboolean silcpurple_check_silc_dir(PurpleConnection *gc) -{ - char filename[256], file_public_key[256], file_private_key[256]; - char servfilename[256], clientfilename[256], friendsfilename[256]; - char pkd[256], prd[256]; - struct stat st; - struct passwd *pw; - int fd; - - pw = getpwuid(getuid()); - if (!pw) { - purple_debug_error("silc", "silc: %s\n", g_strerror(errno)); - return FALSE; - } - - g_snprintf(filename, sizeof(filename) - 1, "%s", silcpurple_silcdir()); - g_snprintf(servfilename, sizeof(servfilename) - 1, "%s" G_DIR_SEPARATOR_S "serverkeys", - silcpurple_silcdir()); - g_snprintf(clientfilename, sizeof(clientfilename) - 1, "%s" G_DIR_SEPARATOR_S "clientkeys", - silcpurple_silcdir()); - g_snprintf(friendsfilename, sizeof(friendsfilename) - 1, "%s" G_DIR_SEPARATOR_S "friends", - silcpurple_silcdir()); - - if (pw->pw_uid != geteuid()) { - purple_debug_error("silc", "Couldn't create directories due to wrong uid!\n"); - return FALSE; - } - - /* - * Check ~/.silc directory - */ - if (g_mkdir(filename, 0755) != 0 && errno != EEXIST) { - purple_debug_error("silc", "Couldn't create '%s' directory\n", filename); - return FALSE; - } - -#ifndef _WIN32 - if ((g_stat(filename, &st)) == -1) { - purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n", filename, g_strerror(errno)); - return FALSE; - } else { - /* Check the owner of the dir */ - if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { - purple_debug_error("silc", "You don't seem to own '%s' directory\n", - filename); - return FALSE; - } - } -#endif - - /* - * Check ~./silc/serverkeys directory - */ - if (g_mkdir(servfilename, 0755) != 0 && errno != EEXIST) { - purple_debug_error("silc", "Couldn't create '%s' directory\n", servfilename); - return FALSE; - } - - /* - * Check ~./silc/clientkeys directory - */ - if (g_mkdir(clientfilename, 0755) != 0 && errno != EEXIST) { - purple_debug_error("silc", "Couldn't create '%s' directory\n", clientfilename); - return FALSE; - } - - /* - * Check ~./silc/friends directory - */ - if (g_mkdir(friendsfilename, 0755) != 0 && errno != EEXIST) { - purple_debug_error("silc", "Couldn't create '%s' directory\n", friendsfilename); - return FALSE; - } - - /* - * Check Public and Private keys - */ - g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir()); - g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir()); - g_snprintf(file_public_key, sizeof(file_public_key) - 1, "%s", - purple_account_get_string(gc->account, "public-key", pkd)); - g_snprintf(file_private_key, sizeof(file_public_key) - 1, "%s", - purple_account_get_string(gc->account, "private-key", prd)); - - if ((g_stat(file_public_key, &st)) == -1) { - /* If file doesn't exist */ - if (errno == ENOENT) { - purple_connection_update_progress(gc, _("Creating SILC key pair..."), 1, 5); - if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS, - SILCPURPLE_DEF_PKCS_LEN, - file_public_key, - file_private_key, NULL, - (gc->password == NULL) - ? "" : gc->password, - NULL, NULL, FALSE)) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("Unable to create SILC key pair")); - return FALSE; - } - - if ((g_stat(file_public_key, &st)) == -1) { - purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n", - file_public_key, g_strerror(errno)); - return FALSE; - } - } else { - purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n", - file_public_key, g_strerror(errno)); - return FALSE; - } - } - -#ifndef _WIN32 - /* Check the owner of the public key */ - if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { - purple_debug_error("silc", "You don't seem to own your public key!?\n"); - return FALSE; - } -#endif - - if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) { - if ((fstat(fd, &st)) == -1) { - purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n", - file_private_key, g_strerror(errno)); - close(fd); - return FALSE; - } - } else { - /* If file doesn't exist */ - if (errno == ENOENT) { - purple_connection_update_progress(gc, _("Creating SILC key pair..."), 1, 5); - if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS, - SILCPURPLE_DEF_PKCS_LEN, - file_public_key, - file_private_key, NULL, - (gc->password == NULL) - ? "" : gc->password, - NULL, NULL, FALSE)) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("Unable to create SILC key pair")); - return FALSE; - } - - if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) { - if ((fstat(fd, &st)) == -1) { - purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n", - file_private_key, g_strerror(errno)); - close(fd); - return FALSE; - } - } else { - purple_debug_error("silc", "Couldn't open '%s' " - "private key, error: %s\n", - file_private_key, g_strerror(errno)); - return FALSE; - } - } else { - purple_debug_error("silc", "Couldn't open '%s' private key, error: %s\n", - file_private_key, g_strerror(errno)); - return FALSE; - } - } - -#ifndef _WIN32 - /* Check the owner of the private key */ - if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { - purple_debug_error("silc", "You don't seem to own your private key!?\n"); - if (fd != -1) - close(fd); - return FALSE; - } - - /* Check the permissions for the private key */ - if ((st.st_mode & 0777) != 0600) { - purple_debug_warning("silc", "Wrong permissions in your private key file `%s'!\n" - "Trying to change them ...\n", file_private_key); - if ((fd == -1) || (fchmod(fd, S_IRUSR | S_IWUSR)) == -1) { - purple_debug_error("silc", - "Failed to change permissions for private key file!\n" - "Permissions for your private key file must be 0600.\n"); - if (fd != -1) - close(fd); - return FALSE; - } - purple_debug_warning("silc", "Done.\n\n"); - } -#endif - - if (fd != -1) - close(fd); - - return TRUE; -} - -#ifdef _WIN32 -struct passwd *getpwuid(uid_t uid) { - struct passwd *pwd = calloc(1, sizeof(struct passwd)); - return pwd; -} - -uid_t getuid() { - return 0; -} - -uid_t geteuid() { - return 0; -} -#endif - -void silcpurple_show_public_key(SilcPurple sg, - const char *name, SilcPublicKey public_key, - GCallback callback, void *context) -{ - SilcPublicKeyIdentifier ident; - SilcSILCPublicKey silc_pubkey; - char *fingerprint, *babbleprint; - unsigned char *pk; - SilcUInt32 pk_len, key_len = 0; - GString *s; - - /* We support showing only SILC public keys for now */ - if (silc_pkcs_get_type(public_key) != SILC_PKCS_SILC) - return; - - silc_pubkey = silc_pkcs_get_context(SILC_PKCS_SILC, public_key); - ident = &silc_pubkey->identifier; - key_len = silc_pkcs_public_key_get_len(public_key); - - pk = silc_pkcs_public_key_encode(public_key, &pk_len); - if (!pk) - return; - fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); - if (!fingerprint || !babbleprint) - return; - - s = g_string_new(""); - if (ident->realname) - /* Hint for translators: Please check the tabulator width here and in - the next strings (short strings: 2 tabs, longer strings 1 tab, - sum: 3 tabs or 24 characters) */ - g_string_append_printf(s, _("Real Name: \t%s\n"), ident->realname); - if (ident->username) - g_string_append_printf(s, _("User Name: \t%s\n"), ident->username); - if (ident->email) - g_string_append_printf(s, _("Email: \t\t%s\n"), ident->email); - if (ident->host) - g_string_append_printf(s, _("Host Name: \t%s\n"), ident->host); - if (ident->org) - g_string_append_printf(s, _("Organization: \t%s\n"), ident->org); - if (ident->country) - g_string_append_printf(s, _("Country: \t%s\n"), ident->country); - g_string_append_printf(s, _("Algorithm: \t%s\n"), silc_pubkey->pkcs->name); - g_string_append_printf(s, _("Key Length: \t%d bits\n"), (int)key_len); - if (ident->version) - g_string_append_printf(s, _("Version: \t%s\n"), ident->version); - g_string_append_printf(s, "\n"); - g_string_append_printf(s, _("Public Key Fingerprint:\n%s\n\n"), fingerprint); - g_string_append_printf(s, _("Public Key Babbleprint:\n%s"), babbleprint); - - purple_request_action(sg->gc, _("Public Key Information"), - _("Public Key Information"), - s->str, 0, purple_connection_get_account(sg->gc), - NULL, NULL, context, 1, _("Close"), callback); - - g_string_free(s, TRUE); - silc_free(fingerprint); - silc_free(babbleprint); - silc_free(pk); -} - -SilcAttributePayload -silcpurple_get_attr(SilcDList attrs, SilcAttribute attribute) -{ - SilcAttributePayload attr = NULL; - - if (!attrs) - return NULL; - - silc_dlist_start(attrs); - while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) - if (attribute == silc_attribute_get_attribute(attr)) - break; - - return attr; -} - -void silcpurple_get_umode_string(SilcUInt32 mode, char *buf, - SilcUInt32 buf_size) -{ - memset(buf, 0, buf_size); - if ((mode & SILC_UMODE_SERVER_OPERATOR) || - (mode & SILC_UMODE_ROUTER_OPERATOR)) { - strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ? - "[server operator] " : - (mode & SILC_UMODE_ROUTER_OPERATOR) ? - "[SILC operator] " : "[unknown mode] "); - } - if (mode & SILC_UMODE_GONE) - strcat(buf, "[away] "); - if (mode & SILC_UMODE_INDISPOSED) - strcat(buf, "[indisposed] "); - if (mode & SILC_UMODE_BUSY) - strcat(buf, "[busy] "); - if (mode & SILC_UMODE_PAGE) - strcat(buf, "[wake me up] "); - if (mode & SILC_UMODE_HYPER) - strcat(buf, "[hyperactive] "); - if (mode & SILC_UMODE_ROBOT) - strcat(buf, "[robot] "); - if (mode & SILC_UMODE_ANONYMOUS) - strcat(buf, "[anonymous] "); - if (mode & SILC_UMODE_BLOCK_PRIVMSG) - strcat(buf, "[blocks private messages] "); - if (mode & SILC_UMODE_DETACHED) - strcat(buf, "[detached] "); - if (mode & SILC_UMODE_REJECT_WATCHING) - strcat(buf, "[rejects watching] "); - if (mode & SILC_UMODE_BLOCK_INVITE) - strcat(buf, "[blocks invites] "); - g_strchomp(buf); -} - -void silcpurple_get_chmode_string(SilcUInt32 mode, char *buf, - SilcUInt32 buf_size) -{ - memset(buf, 0, buf_size); - if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) - strcat(buf, "[permanent] "); - if (mode & SILC_CHANNEL_MODE_PRIVATE) - strcat(buf, "[private] "); - if (mode & SILC_CHANNEL_MODE_SECRET) - strcat(buf, "[secret] "); - if (mode & SILC_CHANNEL_MODE_PRIVKEY) - strcat(buf, "[private key] "); - if (mode & SILC_CHANNEL_MODE_INVITE) - strcat(buf, "[invite only] "); - if (mode & SILC_CHANNEL_MODE_TOPIC) - strcat(buf, "[topic restricted] "); - if (mode & SILC_CHANNEL_MODE_ULIMIT) - strcat(buf, "[user count limit] "); - if (mode & SILC_CHANNEL_MODE_PASSPHRASE) - strcat(buf, "[passphrase auth] "); - if (mode & SILC_CHANNEL_MODE_CHANNEL_AUTH) - strcat(buf, "[public key auth] "); - if (mode & SILC_CHANNEL_MODE_SILENCE_USERS) - strcat(buf, "[users silenced] "); - if (mode & SILC_CHANNEL_MODE_SILENCE_OPERS) - strcat(buf, "[operators silenced] "); - g_strchomp(buf); -} - -void silcpurple_get_chumode_string(SilcUInt32 mode, char *buf, - SilcUInt32 buf_size) -{ - memset(buf, 0, buf_size); - if (mode & SILC_CHANNEL_UMODE_CHANFO) - strcat(buf, "[founder] "); - if (mode & SILC_CHANNEL_UMODE_CHANOP) - strcat(buf, "[operator] "); - if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES) - strcat(buf, "[blocks messages] "); - if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS) - strcat(buf, "[blocks user messages] "); - if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS) - strcat(buf, "[blocks robot messages] "); - if (mode & SILC_CHANNEL_UMODE_QUIET) - strcat(buf, "[quieted] "); - g_strchomp(buf); -} - -void -silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr, - char **contactstr, char **langstr, char **devicestr, - char **tzstr, char **geostr) -{ - SilcAttributePayload attr; - SilcAttributeMood mood = 0; - SilcAttributeContact contact; - SilcAttributeObjDevice device; - SilcAttributeObjGeo geo; - - char tmp[1024]; - GString *s; - - *moodstr = NULL; - *statusstr = NULL; - *contactstr = NULL; - *langstr = NULL; - *devicestr = NULL; - *tzstr = NULL; - *geostr = NULL; - - if (!attrs) - return; - - s = g_string_new(""); - attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_STATUS_MOOD); - if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) { - if (mood & SILC_ATTRIBUTE_MOOD_HAPPY) - g_string_append_printf(s, "[%s] ", _("Happy")); - if (mood & SILC_ATTRIBUTE_MOOD_SAD) - g_string_append_printf(s, "[%s] ", _("Sad")); - if (mood & SILC_ATTRIBUTE_MOOD_ANGRY) - g_string_append_printf(s, "[%s] ", _("Angry")); - if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS) - g_string_append_printf(s, "[%s] ", _("Jealous")); - if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED) - g_string_append_printf(s, "[%s] ", _("Ashamed")); - if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE) - g_string_append_printf(s, "[%s] ", _("Invincible")); - if (mood & SILC_ATTRIBUTE_MOOD_INLOVE) - g_string_append_printf(s, "[%s] ", _("In Love")); - if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY) - g_string_append_printf(s, "[%s] ", _("Sleepy")); - if (mood & SILC_ATTRIBUTE_MOOD_BORED) - g_string_append_printf(s, "[%s] ", _("Bored")); - if (mood & SILC_ATTRIBUTE_MOOD_EXCITED) - g_string_append_printf(s, "[%s] ", _("Excited")); - if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS) - g_string_append_printf(s, "[%s] ", _("Anxious")); - } - if (strlen(s->str)) { - *moodstr = g_string_free(s, FALSE); - g_strchomp(*moodstr); - } else - g_string_free(s, TRUE); - - attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_STATUS_FREETEXT); - memset(tmp, 0, sizeof(tmp)); - if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp))) - *statusstr = g_strdup(tmp); - - s = g_string_new(""); - attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_PREFERRED_CONTACT); - if (attr && silc_attribute_get_object(attr, &contact, sizeof(contact))) { - if (contact & SILC_ATTRIBUTE_CONTACT_CHAT) - g_string_append_printf(s, "[%s] ", _("Chat")); - if (contact & SILC_ATTRIBUTE_CONTACT_EMAIL) - g_string_append_printf(s, "[%s] ", _("Email")); - if (contact & SILC_ATTRIBUTE_CONTACT_CALL) - g_string_append_printf(s, "[%s] ", _("Phone")); - if (contact & SILC_ATTRIBUTE_CONTACT_PAGE) - g_string_append_printf(s, "[%s] ", _("Paging")); - if (contact & SILC_ATTRIBUTE_CONTACT_SMS) - g_string_append_printf(s, "[%s] ", _("SMS")); - if (contact & SILC_ATTRIBUTE_CONTACT_MMS) - g_string_append_printf(s, "[%s] ", _("MMS")); - if (contact & SILC_ATTRIBUTE_CONTACT_VIDEO) - g_string_append_printf(s, "[%s] ", _("Video Conferencing")); - } - if (strlen(s->str)) { - *contactstr = g_string_free(s, FALSE); - g_strchomp(*contactstr); - } else - g_string_free(s, TRUE); - - attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_PREFERRED_LANGUAGE); - memset(tmp, 0, sizeof(tmp)); - if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp))) - *langstr = g_strdup(tmp); - - s = g_string_new(""); - attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_DEVICE_INFO); - memset(&device, 0, sizeof(device)); - if (attr && silc_attribute_get_object(attr, &device, sizeof(device))) { - if (device.type == SILC_ATTRIBUTE_DEVICE_COMPUTER) - g_string_append_printf(s, "%s: ", _("Computer")); - if (device.type == SILC_ATTRIBUTE_DEVICE_MOBILE_PHONE) - g_string_append_printf(s, "%s: ", _("Mobile Phone")); - if (device.type == SILC_ATTRIBUTE_DEVICE_PDA) - g_string_append_printf(s, "%s: ", _("PDA")); - if (device.type == SILC_ATTRIBUTE_DEVICE_TERMINAL) - g_string_append_printf(s, "%s: ", _("Terminal")); - g_string_append_printf(s, "%s %s %s %s", - device.manufacturer ? device.manufacturer : "", - device.version ? device.version : "", - device.model ? device.model : "", - device.language ? device.language : ""); - } - if (strlen(s->str)) - *devicestr = g_string_free(s, FALSE); - else - g_string_free(s, TRUE); - - attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_TIMEZONE); - memset(tmp, 0, sizeof(tmp)); - if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp))) - *tzstr = g_strdup(tmp); - - attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_GEOLOCATION); - memset(&geo, 0, sizeof(geo)); - if (attr && silc_attribute_get_object(attr, &geo, sizeof(geo))) - *geostr = g_strdup_printf("%s %s %s (%s)", - geo.longitude ? geo.longitude : "", - geo.latitude ? geo.latitude : "", - geo.altitude ? geo.altitude : "", - geo.accuracy ? geo.accuracy : ""); -} - -/* Returns MIME type of filetype */ - -char *silcpurple_file2mime(const char *filename) -{ - const char *ct; - - ct = strrchr(filename, '.'); - if (!ct) - return NULL; - else if (!g_ascii_strcasecmp(".png", ct)) - return g_strdup("image/png"); - else if (!g_ascii_strcasecmp(".jpg", ct)) - return g_strdup("image/jpeg"); - else if (!g_ascii_strcasecmp(".jpeg", ct)) - return g_strdup("image/jpeg"); - else if (!g_ascii_strcasecmp(".gif", ct)) - return g_strdup("image/gif"); - else if (!g_ascii_strcasecmp(".tiff", ct)) - return g_strdup("image/tiff"); - - return NULL; -} - -/* Checks if message has images, and assembles MIME message if it has. - If only one image is present, creates simple MIME image message. If - there are multiple images and/or text with images multipart MIME - message is created. */ - -SilcDList silcpurple_image_message(const char *msg, SilcMessageFlags *mflags) -{ - SilcMime mime = NULL, p; - SilcDList list, parts = NULL; - const char *start, *end, *last; - GData *attribs; - char *type; - gboolean images = FALSE; - - last = msg; - while (last && *last && purple_markup_find_tag("img", last, &start, - &end, &attribs)) { - PurpleStoredImage *image = NULL; - const char *id; - - /* Check if there is text before image */ - if (start - last) { - char *text, *tmp; - p = silc_mime_alloc(); - - /* Add content type */ - silc_mime_add_field(p, "Content-Type", - "text/plain; charset=utf-8"); - - tmp = g_strndup(last, start - last); - text = purple_unescape_html(tmp); - g_free(tmp); - - /* Add text */ - silc_mime_add_data(p, (const unsigned char *)text, strlen(text)); - g_free(text); - - if (!parts) - parts = silc_dlist_init(); - silc_dlist_add(parts, p); - } - - id = g_datalist_get_data(&attribs, "id"); - if (id && (image = purple_imgstore_find_by_id(atoi(id)))) { - unsigned long imglen = purple_imgstore_get_size(image); - gconstpointer img = purple_imgstore_get_data(image); - - p = silc_mime_alloc(); - - /* Add content type */ - type = silcpurple_file2mime(purple_imgstore_get_filename(image)); - if (!type) { - g_datalist_clear(&attribs); - last = end + 1; - continue; - } - silc_mime_add_field(p, "Content-Type", type); - g_free(type); - - /* Add content transfer encoding */ - silc_mime_add_field(p, "Content-Transfer-Encoding", "binary"); - - /* Add image data */ - silc_mime_add_data(p, img, imglen); - - if (!parts) - parts = silc_dlist_init(); - silc_dlist_add(parts, p); - images = TRUE; - } - - g_datalist_clear(&attribs); - - /* Continue after tag */ - last = end + 1; - } - - /* Check for text after the image(s) */ - if (images && last && *last) { - char *tmp = purple_unescape_html(last); - p = silc_mime_alloc(); - - /* Add content type */ - silc_mime_add_field(p, "Content-Type", - "text/plain; charset=utf-8"); - - /* Add text */ - silc_mime_add_data(p, (const unsigned char *)tmp, strlen(tmp)); - g_free(tmp); - - if (!parts) - parts = silc_dlist_init(); - silc_dlist_add(parts, p); - } - - /* If there weren't any images, don't return anything. */ - if (!images) { - if (parts) - silc_dlist_uninit(parts); - return NULL; - } - - if (silc_dlist_count(parts) > 1) { - /* Multipart MIME message */ - char b[32]; - mime = silc_mime_alloc(); - silc_mime_add_field(mime, "MIME-Version", "1.0"); - g_snprintf(b, sizeof(b), "b%4X%4X", - (unsigned int)time(NULL), - silc_dlist_count(parts)); - silc_mime_set_multipart(mime, "mixed", b); - silc_dlist_start(parts); - while ((p = silc_dlist_get(parts)) != SILC_LIST_END) - silc_mime_add_multipart(mime, p); - } else { - /* Simple MIME message */ - silc_dlist_start(parts); - mime = silc_dlist_get(parts); - silc_mime_add_field(mime, "MIME-Version", "1.0"); - } - - *mflags &= ~SILC_MESSAGE_FLAG_UTF8; - *mflags |= SILC_MESSAGE_FLAG_DATA; - - /* Encode message. Fragment if it is too large */ - list = silc_mime_encode_partial(mime, 0xfc00); - - silc_dlist_uninit(parts); - - /* Added multiparts gets freed here */ - silc_mime_free(mime); - - return list; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc/wb.c --- a/libpurple/protocols/silc/wb.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,520 +0,0 @@ -/* - - wb.c - - Author: Pekka Riikonen - - Copyright (C) 2005 - 2007 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#include "internal.h" -#include "silc.h" -#include "silcclient.h" -#include "silcpurple.h" -#include "wb.h" - -/* - SILC Whiteboard packet: - - 1 byte command - 2 bytes width - 2 bytes height - 4 bytes brush color - 2 bytes brush size - n bytes data - - Data: - - 4 bytes x - 4 bytes y - - Commands: - - 0x01 draw - 0x02 clear - - MIME: - - MIME-Version: 1.0 - Content-Type: application/x-wb - Content-Transfer-Encoding: binary - -*/ - -#define SILCPURPLE_WB_MIME "MIME-Version: 1.0\r\nContent-Type: application/x-wb\r\nContent-Transfer-Encoding: binary\r\n\r\n" -#define SILCPURPLE_WB_HEADER strlen(SILCPURPLE_WB_MIME) + 11 - -#define SILCPURPLE_WB_WIDTH 500 -#define SILCPURPLE_WB_HEIGHT 400 -#define SILCPURPLE_WB_WIDTH_MAX 1024 -#define SILCPURPLE_WB_HEIGHT_MAX 1024 - -/* Commands */ -typedef enum { - SILCPURPLE_WB_DRAW = 0x01, - SILCPURPLE_WB_CLEAR = 0x02, -} SilcPurpleWbCommand; - -/* Brush size */ -typedef enum { - SILCPURPLE_WB_BRUSH_SMALL = 2, - SILCPURPLE_WB_BRUSH_MEDIUM = 5, - SILCPURPLE_WB_BRUSH_LARGE = 10, -} SilcPurpleWbBrushSize; - -/* Brush color (XXX Purple should provide default colors) */ -typedef enum { - SILCPURPLE_WB_COLOR_BLACK = 0, - SILCPURPLE_WB_COLOR_RED = 13369344, - SILCPURPLE_WB_COLOR_GREEN = 52224, - SILCPURPLE_WB_COLOR_BLUE = 204, - SILCPURPLE_WB_COLOR_YELLOW = 15658496, - SILCPURPLE_WB_COLOR_ORANGE = 16737792, - SILCPURPLE_WB_COLOR_CYAN = 52428, - SILCPURPLE_WB_COLOR_VIOLET = 5381277, - SILCPURPLE_WB_COLOR_PURPLE = 13369548, - SILCPURPLE_WB_COLOR_TAN = 12093547, - SILCPURPLE_WB_COLOR_BROWN = 5256485, - SILCPURPLE_WB_COLOR_GREY = 11184810, - SILCPURPLE_WB_COLOR_WHITE = 16777215, -} SilcPurpleWbColor; - -typedef struct { - int type; /* 0 = buddy, 1 = channel */ - union { - SilcClientEntry client; - SilcChannelEntry channel; - } u; - int width; - int height; - int brush_size; - int brush_color; -} *SilcPurpleWb; - -/* Initialize whiteboard */ - -PurpleWhiteboard *silcpurple_wb_init(SilcPurple sg, SilcClientEntry client_entry) -{ - PurpleWhiteboard *wb; - SilcPurpleWb wbs; - - wb = purple_whiteboard_get_session(sg->account, client_entry->nickname); - if (!wb) - wb = purple_whiteboard_create(sg->account, client_entry->nickname, 0); - if (!wb) - return NULL; - - if (!wb->proto_data) { - wbs = silc_calloc(1, sizeof(*wbs)); - if (!wbs) - return NULL; - wbs->type = 0; - wbs->u.client = client_entry; - wbs->width = SILCPURPLE_WB_WIDTH; - wbs->height = SILCPURPLE_WB_HEIGHT; - wbs->brush_size = SILCPURPLE_WB_BRUSH_SMALL; - wbs->brush_color = SILCPURPLE_WB_COLOR_BLACK; - wb->proto_data = wbs; - - /* Start the whiteboard */ - purple_whiteboard_start(wb); - purple_whiteboard_clear(wb); - } - - return wb; -} - -PurpleWhiteboard *silcpurple_wb_init_ch(SilcPurple sg, SilcChannelEntry channel) -{ - PurpleWhiteboard *wb; - SilcPurpleWb wbs; - - wb = purple_whiteboard_get_session(sg->account, channel->channel_name); - if (!wb) - wb = purple_whiteboard_create(sg->account, channel->channel_name, 0); - if (!wb) - return NULL; - - if (!wb->proto_data) { - wbs = silc_calloc(1, sizeof(*wbs)); - if (!wbs) - return NULL; - wbs->type = 1; - wbs->u.channel = channel; - wbs->width = SILCPURPLE_WB_WIDTH; - wbs->height = SILCPURPLE_WB_HEIGHT; - wbs->brush_size = SILCPURPLE_WB_BRUSH_SMALL; - wbs->brush_color = SILCPURPLE_WB_COLOR_BLACK; - wb->proto_data = wbs; - - /* Start the whiteboard */ - purple_whiteboard_start(wb); - purple_whiteboard_clear(wb); - } - - return wb; -} - -static void -silcpurple_wb_parse(SilcPurpleWb wbs, PurpleWhiteboard *wb, - unsigned char *message, SilcUInt32 message_len) -{ - SilcUInt8 command; - SilcUInt16 width, height, brush_size; - SilcUInt32 brush_color, x, y, dx, dy; - SilcBufferStruct buf; - int ret; - - /* Parse the packet */ - silc_buffer_set(&buf, message, message_len); - ret = silc_buffer_unformat(&buf, - SILC_STR_UI_CHAR(&command), - SILC_STR_UI_SHORT(&width), - SILC_STR_UI_SHORT(&height), - SILC_STR_UI_INT(&brush_color), - SILC_STR_UI_SHORT(&brush_size), - SILC_STR_END); - if (ret < 0) - return; - silc_buffer_pull(&buf, ret); - - /* Update whiteboard if its dimensions changed */ - if (width != wbs->width || height != wbs->height) - silcpurple_wb_set_dimensions(wb, width, height); - - if (command == SILCPURPLE_WB_DRAW) { - /* Parse data and draw it */ - ret = silc_buffer_unformat(&buf, - SILC_STR_UI_INT(&dx), - SILC_STR_UI_INT(&dy), - SILC_STR_END); - if (ret < 0) - return; - silc_buffer_pull(&buf, 8); - x = dx; - y = dy; - while (silc_buffer_len(&buf) > 0) { - ret = silc_buffer_unformat(&buf, - SILC_STR_UI_INT(&dx), - SILC_STR_UI_INT(&dy), - SILC_STR_END); - if (ret < 0) - return; - silc_buffer_pull(&buf, 8); - - purple_whiteboard_draw_line(wb, x, y, x + dx, y + dy, - brush_color, brush_size); - x += dx; - y += dy; - } - } - - if (command == SILCPURPLE_WB_CLEAR) - purple_whiteboard_clear(wb); -} - -typedef struct { - unsigned char *message; - SilcUInt32 message_len; - SilcPurple sg; - SilcClientEntry sender; - SilcChannelEntry channel; -} *SilcPurpleWbRequest; - -static void -silcpurple_wb_request_cb(SilcPurpleWbRequest req, gint id) -{ - PurpleWhiteboard *wb; - - if (id != 1) - goto out; - - if (!req->channel) - wb = silcpurple_wb_init(req->sg, req->sender); - else - wb = silcpurple_wb_init_ch(req->sg, req->channel); - - silcpurple_wb_parse(wb->proto_data, wb, req->message, req->message_len); - - out: - silc_free(req->message); - silc_free(req); -} - -static void -silcpurple_wb_request(SilcClient client, const unsigned char *message, - SilcUInt32 message_len, SilcClientEntry sender, - SilcChannelEntry channel) -{ - char tmp[256]; - SilcPurpleWbRequest req; - PurpleConnection *gc; - SilcPurple sg; - - gc = client->application; - sg = gc->proto_data; - - /* Open whiteboard automatically if requested */ - if (purple_account_get_bool(sg->account, "open-wb", FALSE)) { - PurpleWhiteboard *wb; - - if (!channel) - wb = silcpurple_wb_init(sg, sender); - else - wb = silcpurple_wb_init_ch(sg, channel); - - silcpurple_wb_parse(wb->proto_data, wb, - (unsigned char *)message, - message_len); - return; - } - - /* Close any previous unaccepted requests */ - purple_request_close_with_handle(sender); - - if (!channel) { - g_snprintf(tmp, sizeof(tmp), - _("%s sent message to whiteboard. Would you like " - "to open the whiteboard?"), sender->nickname); - } else { - g_snprintf(tmp, sizeof(tmp), - _("%s sent message to whiteboard on %s channel. " - "Would you like to open the whiteboard?"), - sender->nickname, channel->channel_name); - } - - req = silc_calloc(1, sizeof(*req)); - if (!req) - return; - req->message = silc_memdup(message, message_len); - req->message_len = message_len; - req->sender = sender; - req->channel = channel; - req->sg = sg; - - purple_request_action(gc, _("Whiteboard"), tmp, NULL, 1, - sg->account, sender->nickname, NULL, req, 2, - _("Yes"), G_CALLBACK(silcpurple_wb_request_cb), - _("No"), G_CALLBACK(silcpurple_wb_request_cb)); -} - -/* Process incoming whiteboard message */ - -void silcpurple_wb_receive(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcMessagePayload payload, - SilcMessageFlags flags, const unsigned char *message, - SilcUInt32 message_len) -{ - SilcPurple sg; - PurpleConnection *gc; - PurpleWhiteboard *wb; - SilcPurpleWb wbs; - - gc = client->application; - sg = gc->proto_data; - - wb = purple_whiteboard_get_session(sg->account, sender->nickname); - if (!wb) { - /* Ask user if they want to open the whiteboard */ - silcpurple_wb_request(client, message, message_len, - sender, NULL); - return; - } - - wbs = wb->proto_data; - silcpurple_wb_parse(wbs, wb, (unsigned char *)message, message_len); -} - -/* Process incoming whiteboard message on channel */ - -void silcpurple_wb_receive_ch(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcChannelEntry channel, - SilcMessagePayload payload, - SilcMessageFlags flags, - const unsigned char *message, - SilcUInt32 message_len) -{ - SilcPurple sg; - PurpleConnection *gc; - PurpleWhiteboard *wb; - SilcPurpleWb wbs; - - gc = client->application; - sg = gc->proto_data; - - wb = purple_whiteboard_get_session(sg->account, channel->channel_name); - if (!wb) { - /* Ask user if they want to open the whiteboard */ - silcpurple_wb_request(client, message, message_len, - sender, channel); - return; - } - - wbs = wb->proto_data; - silcpurple_wb_parse(wbs, wb, (unsigned char *)message, message_len); -} - -/* Send whiteboard message */ - -void silcpurple_wb_send(PurpleWhiteboard *wb, GList *draw_list) -{ - SilcPurpleWb wbs = wb->proto_data; - SilcBuffer packet; - GList *list; - int len; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(draw_list); - gc = purple_account_get_connection(wb->account); - g_return_if_fail(gc); - sg = gc->proto_data; - g_return_if_fail(sg); - - len = SILCPURPLE_WB_HEADER; - for (list = draw_list; list; list = list->next) - len += 4; - - packet = silc_buffer_alloc_size(len); - if (!packet) - return; - - /* Assmeble packet */ - silc_buffer_format(packet, - SILC_STR_UI32_STRING(SILCPURPLE_WB_MIME), - SILC_STR_UI_CHAR(SILCPURPLE_WB_DRAW), - SILC_STR_UI_SHORT(wbs->width), - SILC_STR_UI_SHORT(wbs->height), - SILC_STR_UI_INT(wbs->brush_color), - SILC_STR_UI_SHORT(wbs->brush_size), - SILC_STR_END); - silc_buffer_pull(packet, SILCPURPLE_WB_HEADER); - for (list = draw_list; list; list = list->next) { - silc_buffer_format(packet, - SILC_STR_UI_INT(GPOINTER_TO_INT(list->data)), - SILC_STR_END); - silc_buffer_pull(packet, 4); - } - - /* Send the message */ - if (wbs->type == 0) { - /* Private message */ - silc_client_send_private_message(sg->client, sg->conn, - wbs->u.client, - SILC_MESSAGE_FLAG_DATA, NULL, - packet->head, len); - } else if (wbs->type == 1) { - /* Channel message. Channel private keys are not supported. */ - silc_client_send_channel_message(sg->client, sg->conn, - wbs->u.channel, NULL, - SILC_MESSAGE_FLAG_DATA, NULL, - packet->head, len); - } - - silc_buffer_free(packet); -} - -/* Purple Whiteboard operations */ - -void silcpurple_wb_start(PurpleWhiteboard *wb) -{ - /* Nothing here. Everything is in initialization */ -} - -void silcpurple_wb_end(PurpleWhiteboard *wb) -{ - silc_free(wb->proto_data); - wb->proto_data = NULL; -} - -void silcpurple_wb_get_dimensions(const PurpleWhiteboard *wb, int *width, int *height) -{ - SilcPurpleWb wbs = wb->proto_data; - *width = wbs->width; - *height = wbs->height; -} - -void silcpurple_wb_set_dimensions(PurpleWhiteboard *wb, int width, int height) -{ - SilcPurpleWb wbs = wb->proto_data; - wbs->width = width > SILCPURPLE_WB_WIDTH_MAX ? SILCPURPLE_WB_WIDTH_MAX : - width; - wbs->height = height > SILCPURPLE_WB_HEIGHT_MAX ? SILCPURPLE_WB_HEIGHT_MAX : - height; - - /* Update whiteboard */ - purple_whiteboard_set_dimensions(wb, wbs->width, wbs->height); -} - -void silcpurple_wb_get_brush(const PurpleWhiteboard *wb, int *size, int *color) -{ - SilcPurpleWb wbs = wb->proto_data; - *size = wbs->brush_size; - *color = wbs->brush_color; -} - -void silcpurple_wb_set_brush(PurpleWhiteboard *wb, int size, int color) -{ - SilcPurpleWb wbs = wb->proto_data; - wbs->brush_size = size; - wbs->brush_color = color; - - /* Update whiteboard */ - purple_whiteboard_set_brush(wb, size, color); -} - -void silcpurple_wb_clear(PurpleWhiteboard *wb) -{ - SilcPurpleWb wbs = wb->proto_data; - SilcBuffer packet; - int len; - PurpleConnection *gc; - SilcPurple sg; - - gc = purple_account_get_connection(wb->account); - g_return_if_fail(gc); - sg = gc->proto_data; - g_return_if_fail(sg); - - len = SILCPURPLE_WB_HEADER; - packet = silc_buffer_alloc_size(len); - if (!packet) - return; - - /* Assmeble packet */ - silc_buffer_format(packet, - SILC_STR_UI32_STRING(SILCPURPLE_WB_MIME), - SILC_STR_UI_CHAR(SILCPURPLE_WB_CLEAR), - SILC_STR_UI_SHORT(wbs->width), - SILC_STR_UI_SHORT(wbs->height), - SILC_STR_UI_INT(wbs->brush_color), - SILC_STR_UI_SHORT(wbs->brush_size), - SILC_STR_END); - - /* Send the message */ - if (wbs->type == 0) { - /* Private message */ - silc_client_send_private_message(sg->client, sg->conn, - wbs->u.client, - SILC_MESSAGE_FLAG_DATA, NULL, - packet->head, len); - } else if (wbs->type == 1) { - /* Channel message */ - silc_client_send_channel_message(sg->client, sg->conn, - wbs->u.channel, NULL, - SILC_MESSAGE_FLAG_DATA, NULL, - packet->head, len); - } - - silc_buffer_free(packet); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc/wb.h --- a/libpurple/protocols/silc/wb.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - - silcpurple.h - - Author: Pekka Riikonen - - Copyright (C) 2005 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#ifndef SILCPURPLE_WB_H -#define SILCPURPLE_WB_H - -#include "silcpurple.h" -#include "whiteboard.h" - -PurpleWhiteboard * -silcpurple_wb_init(SilcPurple sg, SilcClientEntry client_entry); -PurpleWhiteboard * -silcpurple_wb_init_ch(SilcPurple sg, SilcChannelEntry channel); -void silcpurple_wb_receive(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcMessagePayload payload, - SilcMessageFlags flags, const unsigned char *message, - SilcUInt32 message_len); -void silcpurple_wb_receive_ch(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcChannelEntry channel, - SilcMessagePayload payload, - SilcMessageFlags flags, - const unsigned char *message, - SilcUInt32 message_len); -void silcpurple_wb_start(PurpleWhiteboard *wb); -void silcpurple_wb_end(PurpleWhiteboard *wb); -void silcpurple_wb_get_dimensions(const PurpleWhiteboard *wb, int *width, int *height); -void silcpurple_wb_set_dimensions(PurpleWhiteboard *wb, int width, int height); -void silcpurple_wb_get_brush(const PurpleWhiteboard *wb, int *size, int *color); -void silcpurple_wb_set_brush(PurpleWhiteboard *wb, int size, int color); -void silcpurple_wb_send(PurpleWhiteboard *wb, GList *draw_list); -void silcpurple_wb_clear(PurpleWhiteboard *wb); - -#endif /* SILCPURPLE_WB_H */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc10/Makefile.am --- a/libpurple/protocols/silc10/Makefile.am Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -EXTRA_DIST = \ - Makefile.mingw \ - README \ - TODO - -pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) - -SILCSOURCES = silc.c silcpurple.h buddy.c chat.c ft.c ops.c pk.c util.c wb.c wb.h - -AM_CFLAGS = $(st) - -libsilcpurple_la_LDFLAGS = -module -avoid-version - -if STATIC_SILC - -st = -DPURPLE_STATIC_PRPL $(SILC_CFLAGS) -noinst_LTLIBRARIES = libsilcpurple.la -libsilcpurple_la_SOURCES = $(SILCSOURCES) -libsilcpurple_la_CFLAGS = $(AM_CFLAGS) -libsilcpurple_la_LIBADD = $(SILC_LIBS) - -else - -st = $(SILC_CFLAGS) -pkg_LTLIBRARIES = libsilcpurple.la -libsilcpurple_la_SOURCES = $(SILCSOURCES) -libsilcpurple_la_LIBADD = $(GLIB_LIBS) $(SILC_LIBS) - -endif - -AM_CPPFLAGS = \ - -I$(top_srcdir)/libpurple \ - -I$(top_builddir)/libpurple \ - $(DEBUG_CFLAGS) \ - $(GLIB_CFLAGS) \ - $(SILC_CFLAGS) diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc10/Makefile.mingw --- a/libpurple/protocols/silc10/Makefile.mingw Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,93 +0,0 @@ -# -# Makefile.mingw -# -# Description: Makefile for win32 (mingw) version of libsilc protocol plugin -# - -PIDGIN_TREE_TOP := ../../.. -include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak - -DEFINES := $(subst -DWIN32_LEAN_AND_MEAN,,$(DEFINES)) - -TARGET = libsilc -NEEDED_DLLS = $(SILC_TOOLKIT)/lib/silc.dll \ - $(SILC_TOOLKIT)/lib/silcclient.dll -TYPE = PLUGIN - -# Static or Plugin... -ifeq ($(TYPE),STATIC) - DEFINES += -DSTATIC - DLL_INSTALL_DIR = $(PURPLE_INSTALL_DIR) -else -ifeq ($(TYPE),PLUGIN) - DLL_INSTALL_DIR = $(PURPLE_INSTALL_PLUGINS_DIR) -endif -endif - -## -## INCLUDE PATHS -## -INCLUDE_PATHS += -I. \ - -I$(GTK_TOP)/include \ - -I$(GTK_TOP)/include/glib-2.0 \ - -I$(GTK_TOP)/lib/glib-2.0/include \ - -I$(PURPLE_TOP) \ - -I$(PURPLE_TOP)/win32 \ - -I$(PIDGIN_TREE_TOP) \ - -I$(SILC_TOOLKIT)/include - -LIB_PATHS += -L$(GTK_TOP)/lib \ - -L$(PURPLE_TOP) \ - -L$(SILC_TOOLKIT)/lib - -## -## SOURCES, OBJECTS -## -C_SRC = silc.c \ - buddy.c \ - chat.c \ - ft.c \ - ops.c \ - pk.c \ - util.c \ - wb.c - -OBJECTS = $(C_SRC:%.c=%.o) - -## -## LIBRARIES -## -LIBS = \ - -lglib-2.0 \ - -lws2_32 \ - -lintl \ - -lpurple \ - -lsilc \ - -lsilcclient - -include $(PIDGIN_COMMON_RULES) - -## -## TARGET DEFINITIONS -## -.PHONY: all install clean - -all: $(TARGET).dll - -install: all $(DLL_INSTALL_DIR) $(PURPLE_INSTALL_DIR) - cp $(TARGET).dll $(DLL_INSTALL_DIR) - cp $(NEEDED_DLLS) $(PURPLE_INSTALL_DIR) - -$(OBJECTS): $(PURPLE_CONFIG_H) - -$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS) - $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -Wl,--image-base,0x64000000 -o $(TARGET).dll - -## -## CLEAN RULES -## -clean: - rm -f $(OBJECTS) - rm -f $(TARGET).dll - -include $(PIDGIN_COMMON_TARGETS) diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc10/README --- a/libpurple/protocols/silc10/README Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -SILC Purple Plugin -================== - -This is the Purple protocol plugin of the protocol called Secure Internet -Live Conferencing (SILC). The implementation will use the SILC Toolkit, -freely available from the http://silcnet.org/ site, for the actual SILC -protocol implementation. - -To include SILC into Purple, one needs to first compile and install -the SILC Toolkit. It is done as follows: - - ./configure --enable-shared - make - make install - -This will compile shared libraries of the SILC Toolkit. If the --prefix -is not given to ./configure, the binaries are installed into the -/usr/local/silc directory. - -Once the Toolkit is installed one needs to tell Purple's ./configure -script where the SILC Toolkit is located. It is done as simply as: - - ./configure - -if pkg-config is installed in your system. If it is isn't it's done as: - - ./configure --with-silc-libs=/path/to/silc/lib - --with-silc-includes=/path/to/silc/include - -If the SILC Toolkit cannot be found then the SILC protocol plugin will -not be compiled. diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc10/TODO --- a/libpurple/protocols/silc10/TODO Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,8 +0,0 @@ -Features TODO (maybe) -===================== - -Preferences - - Add joined channels to buddy list automatically (during - session) - - Add joined channels to buddy list automatically permanently - diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc10/buddy.c --- a/libpurple/protocols/silc10/buddy.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1750 +0,0 @@ -/* - - silcpurple_buddy.c - - Author: Pekka Riikonen - - Copyright (C) 2004 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#include "silcincludes.h" -#include "silcclient.h" -#include "silcpurple.h" -#include "wb.h" - -#include "glibcompat.h" - -/***************************** Key Agreement *********************************/ - -static void -silcpurple_buddy_keyagr(PurpleBlistNode *node, gpointer data); - -static void -silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name, - gboolean force_local); - -typedef struct { - char *nick; - PurpleConnection *gc; -} *SilcPurpleResolve; - -static void -silcpurple_buddy_keyagr_resolved(SilcClient client, - SilcClientConnection conn, - SilcClientEntry *clients, - SilcUInt32 clients_count, - void *context) -{ - PurpleConnection *gc = client->application; - SilcPurpleResolve r = context; - char tmp[256]; - - if (!clients) { - g_snprintf(tmp, sizeof(tmp), - _("User %s is not present in the network"), r->nick); - purple_notify_error(gc, _("Key Agreement"), - _("Cannot perform the key agreement"), tmp); - silc_free(r->nick); - silc_free(r); - return; - } - - silcpurple_buddy_keyagr_do(gc, r->nick, FALSE); - silc_free(r->nick); - silc_free(r); -} - -typedef struct { - gboolean responder; -} *SilcPurpleKeyAgr; - -static void -silcpurple_buddy_keyagr_cb(SilcClient client, - SilcClientConnection conn, - SilcClientEntry client_entry, - SilcKeyAgreementStatus status, - SilcSKEKeyMaterial *key, - void *context) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - SilcPurpleKeyAgr a = context; - - if (!sg->conn) - return; - - switch (status) { - case SILC_KEY_AGREEMENT_OK: - { - PurpleConversation *convo; - char tmp[128]; - - /* Set the private key for this client */ - silc_client_del_private_message_key(client, conn, client_entry); - silc_client_add_private_message_key_ske(client, conn, client_entry, - NULL, NULL, key, a->responder); - silc_ske_free_key_material(key); - - - /* Open IM window */ - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, - client_entry->nickname, sg->account); - if (convo) { - /* we don't have windows in the core anymore...but we may want to - * provide some method for asking the UI to show the window - purple_conv_window_show(purple_conversation_get_window(convo)); - */ - } else { - convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, sg->account, - client_entry->nickname); - } - g_snprintf(tmp, sizeof(tmp), "%s [private key]", client_entry->nickname); - purple_conversation_set_title(convo, tmp); - } - break; - - case SILC_KEY_AGREEMENT_ERROR: - purple_notify_error(gc, _("Key Agreement"), - _("Error occurred during key agreement"), NULL); - break; - - case SILC_KEY_AGREEMENT_FAILURE: - purple_notify_error(gc, _("Key Agreement"), _("Key Agreement failed"), NULL); - break; - - case SILC_KEY_AGREEMENT_TIMEOUT: - purple_notify_error(gc, _("Key Agreement"), - _("Timeout during key agreement"), NULL); - break; - - case SILC_KEY_AGREEMENT_ABORTED: - purple_notify_error(gc, _("Key Agreement"), - _("Key agreement was aborted"), NULL); - break; - - case SILC_KEY_AGREEMENT_ALREADY_STARTED: - purple_notify_error(gc, _("Key Agreement"), - _("Key agreement is already started"), NULL); - break; - - case SILC_KEY_AGREEMENT_SELF_DENIED: - purple_notify_error(gc, _("Key Agreement"), - _("Key agreement cannot be started with yourself"), - NULL); - break; - - default: - break; - } - - silc_free(a); -} - -static void -silcpurple_buddy_keyagr_do(PurpleConnection *gc, const char *name, - gboolean force_local) -{ - SilcPurple sg = gc->proto_data; - SilcClientEntry *clients; - SilcUInt32 clients_count; - char *local_ip = NULL, *remote_ip = NULL; - gboolean local = TRUE; - char *nickname; - SilcPurpleKeyAgr a; - - if (!sg->conn || !name) - return; - - if (!silc_parse_userfqdn(name, &nickname, NULL)) - return; - - /* Find client entry */ - clients = silc_client_get_clients_local(sg->client, sg->conn, nickname, name, - &clients_count); - if (!clients) { - /* Resolve unknown user */ - SilcPurpleResolve r = silc_calloc(1, sizeof(*r)); - if (!r) - return; - r->nick = g_strdup(name); - r->gc = gc; - silc_client_get_clients(sg->client, sg->conn, nickname, NULL, - silcpurple_buddy_keyagr_resolved, r); - silc_free(nickname); - return; - } - - /* Resolve the local IP from the outgoing socket connection. We resolve - it to check whether we have a private range IP address or public IP - address. If we have public then we will assume that we are not behind - NAT and will provide automatically the point of connection to the - agreement. If we have private range address we assume that we are - behind NAT and we let the responder provide the point of connection. - - The algorithm also checks the remote IP address of server connection. - If it is private range address and we have private range address we - assume that we are chatting in LAN and will provide the point of - connection. - - Naturally this algorithm does not always get things right. */ - - if (silc_net_check_local_by_sock(sg->conn->sock->sock, NULL, &local_ip)) { - /* Check if the IP is private */ - if (!force_local && silcpurple_ip_is_private(local_ip)) { - local = FALSE; - - /* Local IP is private, resolve the remote server IP to see whether - we are talking to Internet or just on LAN. */ - if (silc_net_check_host_by_sock(sg->conn->sock->sock, NULL, - &remote_ip)) - if (silcpurple_ip_is_private(remote_ip)) - /* We assume we are in LAN. Let's provide - the connection point. */ - local = TRUE; - } - } - - if (force_local) - local = TRUE; - - if (local && !local_ip) - local_ip = silc_net_localip(); - - a = silc_calloc(1, sizeof(*a)); - if (!a) - return; - a->responder = local; - - /* Send the key agreement request */ - silc_client_send_key_agreement(sg->client, sg->conn, clients[0], - local ? local_ip : NULL, NULL, 0, 60, - silcpurple_buddy_keyagr_cb, a); - - silc_free(local_ip); - silc_free(remote_ip); - silc_free(clients); -} - -typedef struct { - SilcClient client; - SilcClientConnection conn; - SilcClientID client_id; - char *hostname; - SilcUInt16 port; -} *SilcPurpleKeyAgrAsk; - -static void -silcpurple_buddy_keyagr_request_cb(SilcPurpleKeyAgrAsk a, gint id) -{ - SilcPurpleKeyAgr ai; - SilcClientEntry client_entry; - - if (id != 1) - goto out; - - /* Get the client entry. */ - client_entry = silc_client_get_client_by_id(a->client, a->conn, - &a->client_id); - if (!client_entry) { - purple_notify_error(a->client->application, _("Key Agreement"), - _("The remote user is not present in the network any more"), - NULL); - goto out; - } - - /* If the hostname was provided by the requestor perform the key agreement - now. Otherwise, we will send him a request to connect to us. */ - if (a->hostname) { - ai = silc_calloc(1, sizeof(*ai)); - if (!ai) - goto out; - ai->responder = FALSE; - silc_client_perform_key_agreement(a->client, a->conn, client_entry, - a->hostname, a->port, - silcpurple_buddy_keyagr_cb, ai); - } else { - /* Send request. Force us as the point of connection since requestor - did not provide the point of connection. */ - silcpurple_buddy_keyagr_do(a->client->application, - client_entry->nickname, TRUE); - } - - out: - silc_free(a->hostname); - silc_free(a); -} - -void silcpurple_buddy_keyagr_request(SilcClient client, - SilcClientConnection conn, - SilcClientEntry client_entry, - const char *hostname, SilcUInt16 port) -{ - char tmp[128], tmp2[128]; - SilcPurpleKeyAgrAsk a; - PurpleConnection *gc = client->application; - - g_snprintf(tmp, sizeof(tmp), - _("Key agreement request received from %s. Would you like to " - "perform the key agreement?"), client_entry->nickname); - if (hostname) - g_snprintf(tmp2, sizeof(tmp2), - _("The remote user is waiting key agreement on:\n" - "Remote host: %s\nRemote port: %d"), hostname, port); - - a = silc_calloc(1, sizeof(*a)); - if (!a) - return; - a->client = client; - a->conn = conn; - a->client_id = *client_entry->id; - if (hostname) - a->hostname = strdup(hostname); - a->port = port; - - purple_request_action(client->application, _("Key Agreement Request"), tmp, - hostname ? tmp2 : NULL, 1, gc->account, client_entry->nickname, - NULL, a, 2, _("Yes"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb), - _("No"), G_CALLBACK(silcpurple_buddy_keyagr_request_cb)); -} - -static void -silcpurple_buddy_keyagr(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *buddy; - - buddy = (PurpleBuddy *)node; - silcpurple_buddy_keyagr_do(buddy->account->gc, buddy->name, FALSE); -} - - -/**************************** Static IM Key **********************************/ - -static void -silcpurple_buddy_resetkey(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *b; - PurpleConnection *gc; - SilcPurple sg; - char *nickname; - SilcClientEntry *clients; - SilcUInt32 clients_count; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - b = (PurpleBuddy *) node; - gc = purple_account_get_connection(b->account); - sg = gc->proto_data; - - if (!silc_parse_userfqdn(b->name, &nickname, NULL)) - return; - - /* Find client entry */ - clients = silc_client_get_clients_local(sg->client, sg->conn, - nickname, b->name, - &clients_count); - if (!clients) { - silc_free(nickname); - return; - } - - clients[0]->prv_resp = FALSE; - silc_client_del_private_message_key(sg->client, sg->conn, - clients[0]); - silc_free(clients); - silc_free(nickname); -} - -typedef struct { - SilcClient client; - SilcClientConnection conn; - SilcClientID client_id; -} *SilcPurplePrivkey; - -static void -silcpurple_buddy_privkey(PurpleConnection *gc, const char *name); - -static void -silcpurple_buddy_privkey_cb(SilcPurplePrivkey p, const char *passphrase) -{ - SilcClientEntry client_entry; - - if (!passphrase || !(*passphrase)) { - silc_free(p); - return; - } - - /* Get the client entry. */ - client_entry = silc_client_get_client_by_id(p->client, p->conn, - &p->client_id); - if (!client_entry) { - purple_notify_error(p->client->application, _("IM With Password"), - _("The remote user is not present in the network any more"), - NULL); - silc_free(p); - return; - } - - /* Set the private message key */ - silc_client_del_private_message_key(p->client, p->conn, - client_entry); - silc_client_add_private_message_key(p->client, p->conn, - client_entry, NULL, NULL, - (unsigned char *)passphrase, - strlen(passphrase), FALSE, - client_entry->prv_resp); - if (!client_entry->prv_resp) - silc_client_send_private_message_key_request(p->client, - p->conn, - client_entry); - silc_free(p); -} - -static void -silcpurple_buddy_privkey_resolved(SilcClient client, - SilcClientConnection conn, - SilcClientEntry *clients, - SilcUInt32 clients_count, - void *context) -{ - char tmp[256]; - - if (!clients) { - g_snprintf(tmp, sizeof(tmp), - _("User %s is not present in the network"), - (const char *)context); - purple_notify_error(client->application, _("IM With Password"), - _("Cannot set IM key"), tmp); - g_free(context); - return; - } - - silcpurple_buddy_privkey(client->application, context); - silc_free(context); -} - -static void -silcpurple_buddy_privkey(PurpleConnection *gc, const char *name) -{ - SilcPurple sg = gc->proto_data; - char *nickname; - SilcPurplePrivkey p; - SilcClientEntry *clients; - SilcUInt32 clients_count; - - if (!name) - return; - if (!silc_parse_userfqdn(name, &nickname, NULL)) - return; - - /* Find client entry */ - clients = silc_client_get_clients_local(sg->client, sg->conn, - nickname, name, - &clients_count); - if (!clients) { - silc_client_get_clients(sg->client, sg->conn, nickname, NULL, - silcpurple_buddy_privkey_resolved, - g_strdup(name)); - silc_free(nickname); - return; - } - - p = silc_calloc(1, sizeof(*p)); - if (!p) - return; - p->client = sg->client; - p->conn = sg->conn; - p->client_id = *clients[0]->id; - purple_request_input(gc, _("IM With Password"), NULL, - _("Set IM Password"), NULL, FALSE, TRUE, NULL, - _("OK"), G_CALLBACK(silcpurple_buddy_privkey_cb), - _("Cancel"), G_CALLBACK(silcpurple_buddy_privkey_cb), - gc->account, NULL, NULL, p); - - silc_free(clients); - silc_free(nickname); -} - -static void -silcpurple_buddy_privkey_menu(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(buddy->account); - - silcpurple_buddy_privkey(gc, buddy->name); -} - - -/**************************** Get Public Key *********************************/ - -typedef struct { - SilcClient client; - SilcClientConnection conn; - SilcClientID client_id; -} *SilcPurpleBuddyGetkey; - -static void -silcpurple_buddy_getkey(PurpleConnection *gc, const char *name); - -static void -silcpurple_buddy_getkey_cb(SilcPurpleBuddyGetkey g, - SilcClientCommandReplyContext cmd) -{ - SilcClientEntry client_entry; - unsigned char *pk; - SilcUInt32 pk_len; - - /* Get the client entry. */ - client_entry = silc_client_get_client_by_id(g->client, g->conn, - &g->client_id); - if (!client_entry) { - purple_notify_error(g->client->application, _("Get Public Key"), - _("The remote user is not present in the network any more"), - NULL); - silc_free(g); - return; - } - - if (!client_entry->public_key) { - silc_free(g); - return; - } - - /* Now verify the public key */ - pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len); - silcpurple_verify_public_key(g->client, g->conn, client_entry->nickname, - SILC_SOCKET_TYPE_CLIENT, - pk, pk_len, SILC_SKE_PK_TYPE_SILC, - NULL, NULL); - silc_free(pk); - silc_free(g); -} - -static void -silcpurple_buddy_getkey_resolved(SilcClient client, - SilcClientConnection conn, - SilcClientEntry *clients, - SilcUInt32 clients_count, - void *context) -{ - char tmp[256]; - - if (!clients) { - g_snprintf(tmp, sizeof(tmp), - _("User %s is not present in the network"), - (const char *)context); - purple_notify_error(client->application, _("Get Public Key"), - _("Cannot fetch the public key"), tmp); - g_free(context); - return; - } - - silcpurple_buddy_getkey(client->application, context); - silc_free(context); -} - -static void -silcpurple_buddy_getkey(PurpleConnection *gc, const char *name) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcClientEntry *clients; - SilcUInt32 clients_count; - SilcPurpleBuddyGetkey g; - char *nickname; - - if (!name) - return; - - if (!silc_parse_userfqdn(name, &nickname, NULL)) - return; - - /* Find client entry */ - clients = silc_client_get_clients_local(client, conn, nickname, name, - &clients_count); - if (!clients) { - silc_client_get_clients(client, conn, nickname, NULL, - silcpurple_buddy_getkey_resolved, - g_strdup(name)); - silc_free(nickname); - return; - } - - /* Call GETKEY */ - g = silc_calloc(1, sizeof(*g)); - if (!g) - return; - g->client = client; - g->conn = conn; - g->client_id = *clients[0]->id; - silc_client_command_call(client, conn, NULL, "GETKEY", - clients[0]->nickname, NULL); - silc_client_command_pending(conn, SILC_COMMAND_GETKEY, - conn->cmd_ident, - (SilcCommandCb)silcpurple_buddy_getkey_cb, g); - silc_free(clients); - silc_free(nickname); -} - -static void -silcpurple_buddy_getkey_menu(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(buddy->account); - - silcpurple_buddy_getkey(gc, buddy->name); -} - -static void -silcpurple_buddy_showkey(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *b; - PurpleConnection *gc; - SilcPurple sg; - SilcPublicKey public_key; - const char *pkfile; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - b = (PurpleBuddy *) node; - gc = purple_account_get_connection(b->account); - sg = gc->proto_data; - - pkfile = purple_blist_node_get_string(node, "public-key"); - if (!silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_PEM) && - !silc_pkcs_load_public_key(pkfile, &public_key, SILC_PKCS_FILE_BIN)) { - purple_notify_error(gc, - _("Show Public Key"), - _("Could not load public key"), NULL); - return; - } - - silcpurple_show_public_key(sg, b->name, public_key, NULL, NULL); - silc_pkcs_public_key_free(public_key); -} - - -/**************************** Buddy routines *********************************/ - -/* The buddies are implemented by using the WHOIS and WATCH commands that - can be used to search users by their public key. Since nicknames aren't - unique in SILC we cannot trust the buddy list using their nickname. We - associate public keys to buddies and use those to search and watch - in the network. - - The problem is that Purple does not return PurpleBuddy contexts to the - callbacks but the buddy names. Naturally, this is not going to work - with SILC. But, for now, we have to do what we can... */ - -typedef struct { - SilcClient client; - SilcClientConnection conn; - SilcClientID client_id; - PurpleBuddy *b; - unsigned char *offline_pk; - SilcUInt32 offline_pk_len; - unsigned int offline : 1; - unsigned int pubkey_search : 1; - unsigned int init : 1; -} *SilcPurpleBuddyRes; - -static void -silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id); -static void -silcpurple_add_buddy_resolved(SilcClient client, - SilcClientConnection conn, - SilcClientEntry *clients, - SilcUInt32 clients_count, - void *context); - -void silcpurple_get_info(PurpleConnection *gc, const char *who) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcClientEntry client_entry; - PurpleBuddy *b; - const char *filename, *nick = who; - char tmp[256]; - - if (!who) - return; - if (strlen(who) > 1 && who[0] == '@') - nick = who + 1; - if (strlen(who) > 1 && who[0] == '*') - nick = who + 1; - if (strlen(who) > 2 && who[0] == '*' && who[1] == '@') - nick = who + 2; - - b = purple_find_buddy(gc->account, nick); - if (b) { - /* See if we have this buddy's public key. If we do use that - to search the details. */ - filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key"); - if (filename) { - /* Call WHOIS. The user info is displayed in the WHOIS - command reply. */ - silc_client_command_call(client, conn, NULL, "WHOIS", - "-details", "-pubkey", filename, NULL); - return; - } - - if (!b->proto_data) { - g_snprintf(tmp, sizeof(tmp), - _("User %s is not present in the network"), b->name); - purple_notify_error(gc, _("User Information"), - _("Cannot get user information"), tmp); - return; - } - - client_entry = silc_client_get_client_by_id(client, conn, b->proto_data); - if (client_entry) { - /* Call WHOIS. The user info is displayed in the WHOIS - command reply. */ - silc_client_command_call(client, conn, NULL, "WHOIS", - client_entry->nickname, "-details", NULL); - } - } else { - /* Call WHOIS just with nickname. */ - silc_client_command_call(client, conn, NULL, "WHOIS", nick, NULL); - } -} - -static void -silcpurple_add_buddy_pk_no(SilcPurpleBuddyRes r) -{ - char tmp[512]; - g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not trusted"), - r->b->name); - purple_notify_error(r->client->application, _("Add Buddy"), tmp, - _("You cannot receive buddy notifications until you " - "import his/her public key. You can use the Get Public Key " - "command to get the public key.")); - purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); -} - -static void -silcpurple_add_buddy_save(bool success, void *context) -{ - SilcPurpleBuddyRes r = context; - PurpleBuddy *b = r->b; - SilcClient client = r->client; - SilcClientEntry client_entry; - SilcAttributePayload attr; - SilcAttribute attribute; - SilcVCardStruct vcard; - SilcAttributeObjMime message, extension; -#ifdef SILC_ATTRIBUTE_USER_ICON - SilcAttributeObjMime usericon; -#endif - SilcAttributeObjPk serverpk, usersign, serversign; - gboolean usign_success = TRUE, ssign_success = TRUE; - char filename[512], filename2[512], *fingerprint = NULL, *tmp; - SilcUInt32 len; - int i; - - if (!success) { - /* The user did not trust the public key. */ - silcpurple_add_buddy_pk_no(r); - silc_free(r); - return; - } - - if (r->offline) { - /* User is offline. Associate the imported public key with - this user. */ - fingerprint = silc_hash_fingerprint(NULL, r->offline_pk, - r->offline_pk_len); - for (i = 0; i < strlen(fingerprint); i++) - if (fingerprint[i] == ' ') - fingerprint[i] = '_'; - g_snprintf(filename, sizeof(filename) - 1, - "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub", - silcpurple_silcdir(), fingerprint); - purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename); - purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); - silc_free(fingerprint); - silc_free(r->offline_pk); - silc_free(r); - return; - } - - /* Get the client entry. */ - client_entry = silc_client_get_client_by_id(r->client, r->conn, - &r->client_id); - if (!client_entry) { - silc_free(r); - return; - } - - memset(&vcard, 0, sizeof(vcard)); - memset(&message, 0, sizeof(message)); - memset(&extension, 0, sizeof(extension)); -#ifdef SILC_ATTRIBUTE_USER_ICON - memset(&usericon, 0, sizeof(usericon)); -#endif - memset(&serverpk, 0, sizeof(serverpk)); - memset(&usersign, 0, sizeof(usersign)); - memset(&serversign, 0, sizeof(serversign)); - - /* Now that we have the public key and we trust it now we - save the attributes of the buddy and update its status. */ - - if (client_entry->attrs) { - silc_dlist_start(client_entry->attrs); - while ((attr = silc_dlist_get(client_entry->attrs)) - != SILC_LIST_END) { - attribute = silc_attribute_get_attribute(attr); - - switch (attribute) { - case SILC_ATTRIBUTE_USER_INFO: - if (!silc_attribute_get_object(attr, (void *)&vcard, - sizeof(vcard))) - continue; - break; - - case SILC_ATTRIBUTE_STATUS_MESSAGE: - if (!silc_attribute_get_object(attr, (void *)&message, - sizeof(message))) - continue; - break; - - case SILC_ATTRIBUTE_EXTENSION: - if (!silc_attribute_get_object(attr, (void *)&extension, - sizeof(extension))) - continue; - break; - -#ifdef SILC_ATTRIBUTE_USER_ICON - case SILC_ATTRIBUTE_USER_ICON: - if (!silc_attribute_get_object(attr, (void *)&usericon, - sizeof(usericon))) - continue; - break; -#endif - - case SILC_ATTRIBUTE_SERVER_PUBLIC_KEY: - if (serverpk.type) - continue; - if (!silc_attribute_get_object(attr, (void *)&serverpk, - sizeof(serverpk))) - continue; - break; - - case SILC_ATTRIBUTE_USER_DIGITAL_SIGNATURE: - if (usersign.data) - continue; - if (!silc_attribute_get_object(attr, (void *)&usersign, - sizeof(usersign))) - continue; - break; - - case SILC_ATTRIBUTE_SERVER_DIGITAL_SIGNATURE: - if (serversign.data) - continue; - if (!silc_attribute_get_object(attr, (void *)&serversign, - sizeof(serversign))) - continue; - break; - - default: - break; - } - } - } - - /* Verify the attribute signatures */ - - if (usersign.data) { - SilcPKCS pkcs; - unsigned char *verifyd; - SilcUInt32 verify_len; - - silc_pkcs_alloc((unsigned char*)"rsa", &pkcs); - verifyd = silc_attribute_get_verify_data(client_entry->attrs, - FALSE, &verify_len); - if (verifyd && silc_pkcs_public_key_set(pkcs, client_entry->public_key)){ - if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash, - usersign.data, - usersign.data_len, - verifyd, verify_len)) - usign_success = FALSE; - } - silc_free(verifyd); - } - - if (serversign.data && purple_strequal(serverpk.type, "silc-rsa")) { - SilcPublicKey public_key; - SilcPKCS pkcs; - unsigned char *verifyd; - SilcUInt32 verify_len; - - if (silc_pkcs_public_key_decode(serverpk.data, serverpk.data_len, - &public_key)) { - silc_pkcs_alloc((unsigned char *)"rsa", &pkcs); - verifyd = silc_attribute_get_verify_data(client_entry->attrs, - TRUE, &verify_len); - if (verifyd && silc_pkcs_public_key_set(pkcs, public_key)) { - if (!silc_pkcs_verify_with_hash(pkcs, client->sha1hash, - serversign.data, - serversign.data_len, - verifyd, verify_len)) - ssign_success = FALSE; - } - silc_pkcs_public_key_free(public_key); - silc_free(verifyd); - } - } - - fingerprint = silc_fingerprint(client_entry->fingerprint, - client_entry->fingerprint_len); - for (i = 0; i < strlen(fingerprint); i++) - if (fingerprint[i] == ' ') - fingerprint[i] = '_'; - - if (usign_success || ssign_success) { - struct passwd *pw; - struct stat st; - - memset(filename2, 0, sizeof(filename2)); - - /* Filename for dir */ - tmp = fingerprint + strlen(fingerprint) - 9; - g_snprintf(filename, sizeof(filename) - 1, - "%s" G_DIR_SEPARATOR_S "friends" G_DIR_SEPARATOR_S "%s", - silcpurple_silcdir(), tmp); - - pw = getpwuid(getuid()); - if (!pw) - return; - - /* Create dir if it doesn't exist */ - if ((g_stat(filename, &st)) == -1) { - if (errno == ENOENT) { - if (pw->pw_uid == geteuid()) { - int ret = g_mkdir(filename, 0755); - if (ret < 0) - return; - } - } - } - - /* Save VCard */ - g_snprintf(filename2, sizeof(filename2) - 1, - "%s" G_DIR_SEPARATOR_S "vcard", filename); - if (vcard.full_name) { - tmp = (char *)silc_vcard_encode(&vcard, &len); - silc_file_writefile(filename2, tmp, len); - silc_free(tmp); - } - - /* Save status message */ - if (message.mime) { - memset(filename2, 0, sizeof(filename2)); - g_snprintf(filename2, sizeof(filename2) - 1, - "%s" G_DIR_SEPARATOR_S "status_message.mime", - filename); - silc_file_writefile(filename2, (char *)message.mime, - message.mime_len); - } - - /* Save extension data */ - if (extension.mime) { - memset(filename2, 0, sizeof(filename2)); - g_snprintf(filename2, sizeof(filename2) - 1, - "%s" G_DIR_SEPARATOR_S "extension.mime", - filename); - silc_file_writefile(filename2, (char *)extension.mime, - extension.mime_len); - } - -#ifdef SILC_ATTRIBUTE_USER_ICON - /* Save user icon */ - if (usericon.mime) { - SilcMime m = silc_mime_decode(usericon.mime, - usericon.mime_len); - if (m) { - const char *type = silc_mime_get_field(m, "Content-Type"); - if (purple_strequal(type, "image/jpeg") || - purple_strequal(type, "image/gif") || - purple_strequal(type, "image/bmp") || - purple_strequal(type, "image/png")) { - const unsigned char *data; - SilcUInt32 data_len; - data = silc_mime_get_data(m, &data_len); - if (data) { - /* TODO: Check if SILC gives us something to use as the checksum instead */ - purple_buddy_icons_set_for_user(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), g_memdup2(data, data_len), data_len, NULL); - } - } - silc_mime_free(m); - } - } -#endif - } - - /* Save the public key path to buddy properties, as it is used - to identify the buddy in the network (and not the nickname). */ - memset(filename, 0, sizeof(filename)); - g_snprintf(filename, sizeof(filename) - 1, - "%s" G_DIR_SEPARATOR_S "clientkeys" G_DIR_SEPARATOR_S "clientkey_%s.pub", - silcpurple_silcdir(), fingerprint); - purple_blist_node_set_string((PurpleBlistNode *)b, "public-key", filename); - - /* Update online status */ - purple_prpl_got_user_status(purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL); - - /* Finally, start watching this user so we receive its status - changes from the server */ - g_snprintf(filename2, sizeof(filename2) - 1, "+%s", filename); - silc_client_command_call(r->client, r->conn, NULL, "WATCH", "-pubkey", - filename2, NULL); - - silc_free(fingerprint); - silc_free(r); -} - -static void -silcpurple_add_buddy_ask_import(void *user_data, const char *name) -{ - SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data; - SilcPublicKey public_key; - - /* Load the public key */ - if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) && - !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) { - silcpurple_add_buddy_ask_pk_cb(r, 0); - purple_notify_error(r->client->application, - _("Add Buddy"), _("Could not load public key"), NULL); - return; - } - - /* Now verify the public key */ - r->offline_pk = silc_pkcs_public_key_encode(public_key, &r->offline_pk_len); - silcpurple_verify_public_key(r->client, r->conn, r->b->name, - SILC_SOCKET_TYPE_CLIENT, - r->offline_pk, r->offline_pk_len, - SILC_SKE_PK_TYPE_SILC, - silcpurple_add_buddy_save, r); -} - -static void -silcpurple_add_buddy_ask_pk_cancel(void *user_data, const char *name) -{ - SilcPurpleBuddyRes r = (SilcPurpleBuddyRes)user_data; - - /* The user did not import public key. The buddy is unusable. */ - silcpurple_add_buddy_pk_no(r); - silc_free(r); -} - -static void -silcpurple_add_buddy_ask_pk_cb(SilcPurpleBuddyRes r, gint id) -{ - if (id != 0) { - /* The user did not import public key. The buddy is unusable. */ - silcpurple_add_buddy_pk_no(r); - silc_free(r); - return; - } - - /* Open file selector to select the public key. */ - purple_request_file(r->client->application, _("Open..."), NULL, FALSE, - G_CALLBACK(silcpurple_add_buddy_ask_import), - G_CALLBACK(silcpurple_add_buddy_ask_pk_cancel), - purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r); - -} - -static void -silcpurple_add_buddy_ask_pk(SilcPurpleBuddyRes r) -{ - char tmp[512]; - g_snprintf(tmp, sizeof(tmp), _("The %s buddy is not present in the network"), - r->b->name); - purple_request_action(r->client->application, _("Add Buddy"), tmp, - _("To add the buddy you must import his/her public key. " - "Press Import to import a public key."), 0, - purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r, 2, - _("Cancel"), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb), - _("_Import..."), G_CALLBACK(silcpurple_add_buddy_ask_pk_cb)); -} - -static void -silcpurple_add_buddy_getkey_cb(SilcPurpleBuddyRes r, - SilcClientCommandReplyContext cmd) -{ - SilcClientEntry client_entry; - unsigned char *pk; - SilcUInt32 pk_len; - - /* Get the client entry. */ - client_entry = silc_client_get_client_by_id(r->client, r->conn, - &r->client_id); - if (!client_entry || !client_entry->public_key) { - /* The buddy is offline/nonexistent. We will require user - to associate a public key with the buddy or the buddy - cannot be added. */ - r->offline = TRUE; - silcpurple_add_buddy_ask_pk(r); - return; - } - - /* Now verify the public key */ - pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len); - silcpurple_verify_public_key(r->client, r->conn, client_entry->nickname, - SILC_SOCKET_TYPE_CLIENT, - pk, pk_len, SILC_SKE_PK_TYPE_SILC, - silcpurple_add_buddy_save, r); - silc_free(pk); -} - -static void -silcpurple_add_buddy_select_cb(SilcPurpleBuddyRes r, PurpleRequestFields *fields) -{ - PurpleRequestField *f; - GList *list; - SilcClientEntry client_entry; - - f = purple_request_fields_get_field(fields, "list"); - list = purple_request_field_list_get_selected(f); - if (!list) { - /* The user did not select any user. */ - silcpurple_add_buddy_pk_no(r); - silc_free(r); - return; - } - - client_entry = purple_request_field_list_get_data(f, list->data); - silcpurple_add_buddy_resolved(r->client, r->conn, &client_entry, 1, r); -} - -static void -silcpurple_add_buddy_select_cancel(SilcPurpleBuddyRes r, PurpleRequestFields *fields) -{ - /* The user did not select any user. */ - silcpurple_add_buddy_pk_no(r); - silc_free(r); -} - -static void -silcpurple_add_buddy_select(SilcPurpleBuddyRes r, - SilcClientEntry *clients, - SilcUInt32 clients_count) -{ - PurpleRequestFields *fields; - PurpleRequestFieldGroup *g; - PurpleRequestField *f; - char tmp[512], tmp2[128]; - int i; - char *fingerprint; - - fields = purple_request_fields_new(); - g = purple_request_field_group_new(NULL); - f = purple_request_field_list_new("list", NULL); - purple_request_field_group_add_field(g, f); - purple_request_field_list_set_multi_select(f, FALSE); - purple_request_fields_add_group(fields, g); - - for (i = 0; i < clients_count; i++) { - fingerprint = NULL; - if (clients[i]->fingerprint) { - fingerprint = silc_fingerprint(clients[i]->fingerprint, - clients[i]->fingerprint_len); - g_snprintf(tmp2, sizeof(tmp2), "\n%s", fingerprint); - } - g_snprintf(tmp, sizeof(tmp), "%s - %s (%s@%s)%s", - clients[i]->realname, clients[i]->nickname, - clients[i]->username, clients[i]->hostname ? - clients[i]->hostname : "", - fingerprint ? tmp2 : ""); - purple_request_field_list_add_icon(f, tmp, NULL, clients[i]); - silc_free(fingerprint); - } - - purple_request_fields(r->client->application, _("Add Buddy"), - _("Select correct user"), - r->pubkey_search - ? _("More than one user was found with the same public key. Select " - "the correct user from the list to add to the buddy list.") - : _("More than one user was found with the same name. Select " - "the correct user from the list to add to the buddy list."), - fields, - _("OK"), G_CALLBACK(silcpurple_add_buddy_select_cb), - _("Cancel"), G_CALLBACK(silcpurple_add_buddy_select_cancel), - purple_buddy_get_account(r->b), purple_buddy_get_name(r->b), NULL, r); -} - -static void -silcpurple_add_buddy_resolved(SilcClient client, - SilcClientConnection conn, - SilcClientEntry *clients, - SilcUInt32 clients_count, - void *context) -{ - SilcPurpleBuddyRes r = context; - PurpleBuddy *b = r->b; - SilcAttributePayload pub; - SilcAttributeObjPk userpk; - unsigned char *pk; - SilcUInt32 pk_len; - const char *filename; - - filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key"); - - /* If the buddy is offline/nonexistent, we will require user - to associate a public key with the buddy or the buddy - cannot be added. */ - if (!clients_count) { - if (r->init) { - silc_free(r); - return; - } - - r->offline = TRUE; - /* If the user has already associated a public key, try loading it - * before prompting the user to load it again */ - if (filename != NULL) - silcpurple_add_buddy_ask_import(r, filename); - else - silcpurple_add_buddy_ask_pk(r); - return; - } - - /* If more than one client was found with nickname, we need to verify - from user which one is the correct. */ - if (clients_count > 1 && !r->pubkey_search) { - if (r->init) { - silc_free(r); - return; - } - - silcpurple_add_buddy_select(r, clients, clients_count); - return; - } - - /* If we searched using public keys and more than one entry was found - the same person is logged on multiple times. */ - if (clients_count > 1 && r->pubkey_search && b->name) { - if (r->init) { - /* Find the entry that closest matches to the - buddy nickname. */ - int i; - for (i = 0; i < clients_count; i++) { - if (!g_ascii_strncasecmp(b->name, clients[i]->nickname, - strlen(b->name))) { - clients[0] = clients[i]; - break; - } - } - } else { - /* Verify from user which one is correct */ - silcpurple_add_buddy_select(r, clients, clients_count); - return; - } - } - - /* The client was found. Now get its public key and verify - that before adding the buddy. */ - memset(&userpk, 0, sizeof(userpk)); - b->proto_data = silc_memdup(clients[0]->id, sizeof(*clients[0]->id)); - r->client_id = *clients[0]->id; - - /* Get the public key from attributes, if not present then - resolve it with GETKEY unless we have it cached already. */ - if (clients[0]->attrs && !clients[0]->public_key) { - pub = silcpurple_get_attr(clients[0]->attrs, - SILC_ATTRIBUTE_USER_PUBLIC_KEY); - if (!pub || !silc_attribute_get_object(pub, (void *)&userpk, - sizeof(userpk))) { - /* Get public key with GETKEY */ - silc_client_command_call(client, conn, NULL, - "GETKEY", clients[0]->nickname, NULL); - silc_client_command_pending(conn, SILC_COMMAND_GETKEY, - conn->cmd_ident, - (SilcCommandCb)silcpurple_add_buddy_getkey_cb, - r); - return; - } - if (!silc_pkcs_public_key_decode(userpk.data, userpk.data_len, - &clients[0]->public_key)) - return; - silc_free(userpk.data); - } else if (filename && !clients[0]->public_key) { - if (!silc_pkcs_load_public_key(filename, &clients[0]->public_key, - SILC_PKCS_FILE_PEM) && - !silc_pkcs_load_public_key(filename, &clients[0]->public_key, - SILC_PKCS_FILE_BIN)) { - /* Get public key with GETKEY */ - silc_client_command_call(client, conn, NULL, - "GETKEY", clients[0]->nickname, NULL); - silc_client_command_pending(conn, SILC_COMMAND_GETKEY, - conn->cmd_ident, - (SilcCommandCb)silcpurple_add_buddy_getkey_cb, - r); - return; - } - } else if (!clients[0]->public_key) { - /* Get public key with GETKEY */ - silc_client_command_call(client, conn, NULL, - "GETKEY", clients[0]->nickname, NULL); - silc_client_command_pending(conn, SILC_COMMAND_GETKEY, - conn->cmd_ident, - (SilcCommandCb)silcpurple_add_buddy_getkey_cb, - r); - return; - } - - /* We have the public key, verify it. */ - pk = silc_pkcs_public_key_encode(clients[0]->public_key, &pk_len); - silcpurple_verify_public_key(client, conn, clients[0]->nickname, - SILC_SOCKET_TYPE_CLIENT, - pk, pk_len, SILC_SKE_PK_TYPE_SILC, - silcpurple_add_buddy_save, r); - silc_free(pk); -} - -static void -silcpurple_add_buddy_i(PurpleConnection *gc, PurpleBuddy *b, gboolean init) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcPurpleBuddyRes r; - SilcBuffer attrs; - const char *filename, *name = b->name; - - r = silc_calloc(1, sizeof(*r)); - if (!r) - return; - r->client = client; - r->conn = conn; - r->b = b; - r->init = init; - - /* See if we have this buddy's public key. If we do use that - to search the details. */ - filename = purple_blist_node_get_string((PurpleBlistNode *)b, "public-key"); - if (filename) { - SilcPublicKey public_key; - SilcAttributeObjPk userpk; - - if (!silc_pkcs_load_public_key(filename, &public_key, - SILC_PKCS_FILE_PEM) && - !silc_pkcs_load_public_key(filename, &public_key, - SILC_PKCS_FILE_BIN)) - return; - - /* Get all attributes, and use the public key to search user */ - name = NULL; - attrs = silc_client_attributes_request(SILC_ATTRIBUTE_USER_INFO, - SILC_ATTRIBUTE_SERVICE, - SILC_ATTRIBUTE_STATUS_MOOD, - SILC_ATTRIBUTE_STATUS_FREETEXT, - SILC_ATTRIBUTE_STATUS_MESSAGE, - SILC_ATTRIBUTE_PREFERRED_LANGUAGE, - SILC_ATTRIBUTE_PREFERRED_CONTACT, - SILC_ATTRIBUTE_TIMEZONE, - SILC_ATTRIBUTE_GEOLOCATION, -#ifdef SILC_ATTRIBUTE_USER_ICON - SILC_ATTRIBUTE_USER_ICON, -#endif - SILC_ATTRIBUTE_DEVICE_INFO, 0); - userpk.type = "silc-rsa"; - userpk.data = silc_pkcs_public_key_encode(public_key, &userpk.data_len); - attrs = silc_attribute_payload_encode(attrs, - SILC_ATTRIBUTE_USER_PUBLIC_KEY, - SILC_ATTRIBUTE_FLAG_VALID, - &userpk, sizeof(userpk)); - silc_free(userpk.data); - silc_pkcs_public_key_free(public_key); - r->pubkey_search = TRUE; - } else { - /* Get all attributes */ - attrs = silc_client_attributes_request(0); - } - - /* Resolve */ - silc_client_get_clients_whois(client, conn, name, NULL, attrs, - silcpurple_add_buddy_resolved, r); - silc_buffer_free(attrs); -} - -void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) -{ - /* Don't add if the buddy is already on the list. - * - * SILC doesn't have groups, so we don't need to do anything - * for a move. */ - if (purple_buddy_get_protocol_data(buddy) == NULL) - silcpurple_add_buddy_i(gc, buddy, FALSE); -} - -void silcpurple_send_buddylist(PurpleConnection *gc) -{ - PurpleBuddyList *blist; - PurpleBlistNode *gnode, *cnode, *bnode; - PurpleBuddy *buddy; - PurpleAccount *account; - - account = purple_connection_get_account(gc); - - if ((blist = purple_get_blist()) != NULL) - { - for (gnode = blist->root; gnode != NULL; gnode = gnode->next) - { - if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) - continue; - for (cnode = gnode->child; cnode != NULL; cnode = cnode->next) - { - if (!PURPLE_BLIST_NODE_IS_CONTACT(cnode)) - continue; - for (bnode = cnode->child; bnode != NULL; bnode = bnode->next) - { - if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) - continue; - buddy = (PurpleBuddy *)bnode; - if (purple_buddy_get_account(buddy) == account) - silcpurple_add_buddy_i(gc, buddy, TRUE); - } - } - } - } -} - -void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, - PurpleGroup *group) -{ - silc_free(buddy->proto_data); -} - -void silcpurple_idle_set(PurpleConnection *gc, int idle) - -{ - SilcPurple sg; - SilcClient client; - SilcClientConnection conn; - SilcAttributeObjService service; - const char *server; - int port; - - sg = gc->proto_data; - if (sg == NULL) - return; - - client = sg->client; - if (client == NULL) - return; - - conn = sg->conn; - if (conn == NULL) - return; - - server = purple_account_get_string(sg->account, "server", - "silc.silcnet.org"); - port = purple_account_get_int(sg->account, "port", 706), - - memset(&service, 0, sizeof(service)); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_SERVICE, NULL); - service.port = port; - g_snprintf(service.address, sizeof(service.address), "%s", server); - service.idle = idle; - silc_client_attribute_add(client, conn, SILC_ATTRIBUTE_SERVICE, - &service, sizeof(service)); -} - -char *silcpurple_status_text(PurpleBuddy *b) -{ - SilcPurple sg = b->account->gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcClientID *client_id = b->proto_data; - SilcClientEntry client_entry; - SilcAttributePayload attr; - SilcAttributeMood mood = 0; - - /* Get the client entry. */ - client_entry = silc_client_get_client_by_id(client, conn, client_id); - if (!client_entry) - return NULL; - - /* If user is online, we show the mood status, if available. - If user is offline or away that status is indicated. */ - - if (client_entry->mode & SILC_UMODE_DETACHED) - return g_strdup(_("Detached")); - if (client_entry->mode & SILC_UMODE_GONE) - return g_strdup(_("Away")); - if (client_entry->mode & SILC_UMODE_INDISPOSED) - return g_strdup(_("Indisposed")); - if (client_entry->mode & SILC_UMODE_BUSY) - return g_strdup(_("Busy")); - if (client_entry->mode & SILC_UMODE_PAGE) - return g_strdup(_("Wake Me Up")); - if (client_entry->mode & SILC_UMODE_HYPER) - return g_strdup(_("Hyper Active")); - if (client_entry->mode & SILC_UMODE_ROBOT) - return g_strdup(_("Robot")); - - attr = silcpurple_get_attr(client_entry->attrs, SILC_ATTRIBUTE_STATUS_MOOD); - if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) { - /* The mood is a bit mask, so we could show multiple moods, - but let's show only one for now. */ - if (mood & SILC_ATTRIBUTE_MOOD_HAPPY) - return g_strdup(_("Happy")); - if (mood & SILC_ATTRIBUTE_MOOD_SAD) - return g_strdup(_("Sad")); - if (mood & SILC_ATTRIBUTE_MOOD_ANGRY) - return g_strdup(_("Angry")); - if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS) - return g_strdup(_("Jealous")); - if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED) - return g_strdup(_("Ashamed")); - if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE) - return g_strdup(_("Invincible")); - if (mood & SILC_ATTRIBUTE_MOOD_INLOVE) - return g_strdup(_("In Love")); - if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY) - return g_strdup(_("Sleepy")); - if (mood & SILC_ATTRIBUTE_MOOD_BORED) - return g_strdup(_("Bored")); - if (mood & SILC_ATTRIBUTE_MOOD_EXCITED) - return g_strdup(_("Excited")); - if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS) - return g_strdup(_("Anxious")); - } - - return NULL; -} - -void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full) -{ - SilcPurple sg = b->account->gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcClientID *client_id = b->proto_data; - SilcClientEntry client_entry; - char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr; - char tmp[256]; - - /* Get the client entry. */ - client_entry = silc_client_get_client_by_id(client, conn, client_id); - if (!client_entry) - return; - - if (client_entry->nickname) - purple_notify_user_info_add_pair(user_info, _("Nickname"), - client_entry->nickname); - if (client_entry->username && client_entry->hostname) { - g_snprintf(tmp, sizeof(tmp), "%s@%s", client_entry->username, client_entry->hostname); - purple_notify_user_info_add_pair(user_info, _("Username"), tmp); - } - if (client_entry->mode) { - memset(tmp, 0, sizeof(tmp)); - silcpurple_get_umode_string(client_entry->mode, - tmp, sizeof(tmp) - strlen(tmp)); - purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp); - } - - silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr); - - if (statusstr) { - purple_notify_user_info_add_pair(user_info, _("Message"), statusstr); - g_free(statusstr); - } - - if (full) { - if (moodstr) { - purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr); - g_free(moodstr); - } - - if (contactstr) { - purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr); - g_free(contactstr); - } - - if (langstr) { - purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr); - g_free(langstr); - } - - if (devicestr) { - purple_notify_user_info_add_pair(user_info, _("Device"), devicestr); - g_free(devicestr); - } - - if (tzstr) { - purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr); - g_free(tzstr); - } - - if (geostr) { - purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr); - g_free(geostr); - } - } -} - -static void -silcpurple_buddy_kill(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *b; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - b = (PurpleBuddy *) node; - gc = purple_account_get_connection(b->account); - sg = gc->proto_data; - - /* Call KILL */ - silc_client_command_call(sg->client, sg->conn, NULL, "KILL", - b->name, "Killed by operator", NULL); -} - -typedef struct { - SilcPurple sg; - SilcClientEntry client_entry; -} *SilcPurpleBuddyWb; - -static void -silcpurple_buddy_wb(PurpleBlistNode *node, gpointer data) -{ - SilcPurpleBuddyWb wb = data; - silcpurple_wb_init(wb->sg, wb->client_entry); - silc_free(wb); -} - -GList *silcpurple_buddy_menu(PurpleBuddy *buddy) -{ - PurpleConnection *gc = purple_account_get_connection(buddy->account); - SilcPurple sg = gc->proto_data; - SilcClientConnection conn = sg->conn; - const char *pkfile = NULL; - SilcClientEntry client_entry = NULL; - PurpleMenuAction *act; - GList *m = NULL; - SilcPurpleBuddyWb wb; - - pkfile = purple_blist_node_get_string((PurpleBlistNode *) buddy, "public-key"); - client_entry = silc_client_get_client_by_id(sg->client, - sg->conn, - buddy->proto_data); - - if (client_entry && client_entry->send_key) { - act = purple_menu_action_new(_("Reset IM Key"), - PURPLE_CALLBACK(silcpurple_buddy_resetkey), - NULL, NULL); - m = g_list_append(m, act); - - } else { - act = purple_menu_action_new(_("IM with Key Exchange"), - PURPLE_CALLBACK(silcpurple_buddy_keyagr), - NULL, NULL); - m = g_list_append(m, act); - - act = purple_menu_action_new(_("IM with Password"), - PURPLE_CALLBACK(silcpurple_buddy_privkey_menu), - NULL, NULL); - m = g_list_append(m, act); - } - - if (pkfile) { - act = purple_menu_action_new(_("Show Public Key"), - PURPLE_CALLBACK(silcpurple_buddy_showkey), - NULL, NULL); - m = g_list_append(m, act); - - } else { - act = purple_menu_action_new(_("Get Public Key..."), - PURPLE_CALLBACK(silcpurple_buddy_getkey_menu), - NULL, NULL); - m = g_list_append(m, act); - } - - if (conn && conn->local_entry->mode & SILC_UMODE_ROUTER_OPERATOR) { - act = purple_menu_action_new(_("Kill User"), - PURPLE_CALLBACK(silcpurple_buddy_kill), - NULL, NULL); - m = g_list_append(m, act); - } - - if (client_entry) { - wb = silc_calloc(1, sizeof(*wb)); - wb->sg = sg; - wb->client_entry = client_entry; - act = purple_menu_action_new(_("Draw On Whiteboard"), - PURPLE_CALLBACK(silcpurple_buddy_wb), - (void *)wb, NULL); - m = g_list_append(m, act); - } - return m; -} - -#ifdef SILC_ATTRIBUTE_USER_ICON -void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcMime mime; - char type[32]; - unsigned char *icon; - const char *t; - SilcAttributeObjMime obj; - - /* Remove */ - if (!img) { - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_USER_ICON, NULL); - return; - } - - /* Add */ - mime = silc_mime_alloc(); - if (!mime) - return; - - t = purple_imgstore_get_extension(img); - if (!t || purple_strequal(t, "icon")) { - silc_mime_free(mime); - return; - } - if (purple_strequal(t, "jpg")) - t = "jpeg"; - g_snprintf(type, sizeof(type), "image/%s", t); - silc_mime_add_field(mime, "Content-Type", type); - silc_mime_add_data(mime, purple_imgstore_get_data(img), purple_imgstore_get_size(img)); - - obj.mime = icon = silc_mime_encode(mime, &obj.mime_len); - if (obj.mime) - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_USER_ICON, &obj, sizeof(obj)); - - silc_free(icon); - silc_mime_free(mime); -} -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc10/chat.c --- a/libpurple/protocols/silc10/chat.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1456 +0,0 @@ -/* - - silcpurple_chat.c - - Author: Pekka Riikonen - - Copyright (C) 2004 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#include "silcincludes.h" -#include "silcclient.h" -#include "silcpurple.h" -#include "wb.h" - -/***************************** Channel Routines ******************************/ - -GList *silcpurple_chat_info(PurpleConnection *gc) -{ - GList *ci = NULL; - struct proto_chat_entry *pce; - - pce = g_new0(struct proto_chat_entry, 1); - pce->label = _("_Channel:"); - pce->identifier = "channel"; - pce->required = TRUE; - ci = g_list_append(ci, pce); - - pce = g_new0(struct proto_chat_entry, 1); - pce->label = _("_Passphrase:"); - pce->identifier = "passphrase"; - pce->secret = TRUE; - ci = g_list_append(ci, pce); - - return ci; -} - -GHashTable *silcpurple_chat_info_defaults(PurpleConnection *gc, const char *chat_name) -{ - GHashTable *defaults; - - defaults = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, g_free); - - if (chat_name != NULL) - g_hash_table_insert(defaults, "channel", g_strdup(chat_name)); - - return defaults; -} - -static void -silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components); - -static void -silcpurple_chat_getinfo_res(SilcClient client, - SilcClientConnection conn, - SilcChannelEntry *channels, - SilcUInt32 channels_count, - void *context) -{ - GHashTable *components = context; - PurpleConnection *gc = client->application; - const char *chname; - char tmp[256]; - - chname = g_hash_table_lookup(components, "channel"); - if (!chname) - return; - - if (!channels) { - g_snprintf(tmp, sizeof(tmp), - _("Channel %s does not exist in the network"), chname); - purple_notify_error(gc, _("Channel Information"), - _("Cannot get channel information"), tmp); - return; - } - - silcpurple_chat_getinfo(gc, components); -} - - -static void -silcpurple_chat_getinfo(PurpleConnection *gc, GHashTable *components) -{ - SilcPurple sg = gc->proto_data; - const char *chname; - char *buf, tmp[256], *tmp2; - GString *s; - SilcChannelEntry channel; - SilcHashTableList htl; - SilcChannelUser chu; - - if (!components) - return; - - chname = g_hash_table_lookup(components, "channel"); - if (!chname) - return; - channel = silc_client_get_channel(sg->client, sg->conn, - (char *)chname); - if (!channel) { - silc_client_get_channel_resolve(sg->client, sg->conn, - (char *)chname, - silcpurple_chat_getinfo_res, - components); - return; - } - - s = g_string_new(""); - tmp2 = g_markup_escape_text(channel->channel_name, -1); - g_string_append_printf(s, _("Channel Name: %s"), tmp2); - g_free(tmp2); - if (channel->user_list && silc_hash_table_count(channel->user_list)) - g_string_append_printf(s, _("
User Count: %d"), - (int)silc_hash_table_count(channel->user_list)); - - silc_hash_table_list(channel->user_list, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) { - tmp2 = g_markup_escape_text(chu->client->nickname, -1); - g_string_append_printf(s, _("
Channel Founder: %s"), - tmp2); - g_free(tmp2); - break; - } - } - silc_hash_table_list_reset(&htl); - - if (channel->channel_key) - g_string_append_printf(s, _("
Channel Cipher: %s"), - silc_cipher_get_name(channel->channel_key)); - if (channel->hmac) - /* Definition of HMAC: http://en.wikipedia.org/wiki/HMAC */ - g_string_append_printf(s, _("
Channel HMAC: %s"), - silc_hmac_get_name(channel->hmac)); - - if (channel->topic) { - tmp2 = g_markup_escape_text(channel->topic, -1); - g_string_append_printf(s, _("
Channel Topic:
%s"), tmp2); - g_free(tmp2); - } - - if (channel->mode) { - g_string_append_printf(s, _("
Channel Modes: ")); - silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp)); - g_string_append(s, tmp); - } - - if (channel->founder_key) { - char *fingerprint, *babbleprint; - unsigned char *pk; - SilcUInt32 pk_len; - pk = silc_pkcs_public_key_encode(channel->founder_key, &pk_len); - fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); - - g_string_append_printf(s, _("
Founder Key Fingerprint:
%s"), fingerprint); - g_string_append_printf(s, _("
Founder Key Babbleprint:
%s"), babbleprint); - - silc_free(fingerprint); - silc_free(babbleprint); - silc_free(pk); - } - - buf = g_string_free(s, FALSE); - purple_notify_formatted(gc, NULL, _("Channel Information"), NULL, buf, NULL, NULL); - g_free(buf); -} - - -static void -silcpurple_chat_getinfo_menu(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat = (PurpleChat *)node; - silcpurple_chat_getinfo(chat->account->gc, chat->components); -} - - -#if 0 /* XXX For now these are not implemented. We need better - listview dialog from Purple for these. */ -/************************** Channel Invite List ******************************/ - -static void -silcpurple_chat_invitelist(PurpleBlistNode *node, gpointer data); -{ - -} - - -/**************************** Channel Ban List *******************************/ - -static void -silcpurple_chat_banlist(PurpleBlistNode *node, gpointer data); -{ - -} -#endif - - -/************************* Channel Authentication ****************************/ - -typedef struct { - SilcPurple sg; - SilcChannelEntry channel; - PurpleChat *c; - SilcBuffer pubkeys; -} *SilcPurpleChauth; - -static void -silcpurple_chat_chpk_add(void *user_data, const char *name) -{ - SilcPurpleChauth sgc = (SilcPurpleChauth)user_data; - SilcPurple sg = sgc->sg; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcPublicKey public_key; - SilcBuffer chpks, pk, chidp; - unsigned char mode[4]; - SilcUInt32 m; - - /* Load the public key */ - if (!silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_PEM) && - !silc_pkcs_load_public_key(name, &public_key, SILC_PKCS_FILE_BIN)) { - silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys); - silc_buffer_free(sgc->pubkeys); - silc_free(sgc); - purple_notify_error(client->application, - _("Add Channel Public Key"), - _("Could not load public key"), NULL); - return; - } - - pk = silc_pkcs_public_key_payload_encode(public_key); - chpks = silc_buffer_alloc_size(2); - SILC_PUT16_MSB(1, chpks->head); - chpks = silc_argument_payload_encode_one(chpks, pk->data, - pk->len, 0x00); - silc_buffer_free(pk); - - m = sgc->channel->mode; - m |= SILC_CHANNEL_MODE_CHANNEL_AUTH; - - /* Send CMODE */ - SILC_PUT32_MSB(m, mode); - chidp = silc_id_payload_encode(sgc->channel->id, SILC_ID_CHANNEL); - silc_client_command_send(client, conn, SILC_COMMAND_CMODE, - ++conn->cmd_ident, 3, - 1, chidp->data, chidp->len, - 2, mode, sizeof(mode), - 9, chpks->data, chpks->len); - silc_buffer_free(chpks); - silc_buffer_free(chidp); - silc_buffer_free(sgc->pubkeys); - silc_free(sgc); -} - -static void -silcpurple_chat_chpk_cancel(void *user_data, const char *name) -{ - SilcPurpleChauth sgc = (SilcPurpleChauth)user_data; - silcpurple_chat_chauth_show(sgc->sg, sgc->channel, sgc->pubkeys); - silc_buffer_free(sgc->pubkeys); - silc_free(sgc); -} - -static void -silcpurple_chat_chpk_cb(SilcPurpleChauth sgc, PurpleRequestFields *fields) -{ - SilcPurple sg = sgc->sg; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - PurpleRequestField *f; - GList *list; - SilcPublicKey public_key; - SilcBuffer chpks, pk, chidp; - SilcUInt16 c = 0, ct; - unsigned char mode[4]; - SilcUInt32 m; - - f = purple_request_fields_get_field(fields, "list"); - if (!purple_request_field_list_get_selected(f)) { - /* Add new public key */ - purple_request_file(sg->gc, _("Open Public Key..."), NULL, FALSE, - G_CALLBACK(silcpurple_chat_chpk_add), - G_CALLBACK(silcpurple_chat_chpk_cancel), - purple_connection_get_account(sg->gc), NULL, NULL, sgc); - return; - } - - list = purple_request_field_list_get_items(f); - chpks = silc_buffer_alloc_size(2); - - for (ct = 0; list; list = list->next, ct++) { - public_key = purple_request_field_list_get_data(f, list->data); - if (purple_request_field_list_is_selected(f, list->data)) { - /* Delete this public key */ - pk = silc_pkcs_public_key_payload_encode(public_key); - chpks = silc_argument_payload_encode_one(chpks, pk->data, - pk->len, 0x01); - silc_buffer_free(pk); - c++; - } - silc_pkcs_public_key_free(public_key); - } - if (!c) { - silc_buffer_free(chpks); - return; - } - SILC_PUT16_MSB(c, chpks->head); - - m = sgc->channel->mode; - if (ct == c) - m &= ~SILC_CHANNEL_MODE_CHANNEL_AUTH; - - /* Send CMODE */ - SILC_PUT32_MSB(m, mode); - chidp = silc_id_payload_encode(sgc->channel->id, SILC_ID_CHANNEL); - silc_client_command_send(client, conn, SILC_COMMAND_CMODE, - ++conn->cmd_ident, 3, - 1, chidp->data, chidp->len, - 2, mode, sizeof(mode), - 9, chpks->data, chpks->len); - silc_buffer_free(chpks); - silc_buffer_free(chidp); - silc_buffer_free(sgc->pubkeys); - silc_free(sgc); -} - -static void -silcpurple_chat_chauth_ok(SilcPurpleChauth sgc, PurpleRequestFields *fields) -{ - SilcPurple sg = sgc->sg; - PurpleRequestField *f; - const char *curpass, *val; - int set; - - f = purple_request_fields_get_field(fields, "passphrase"); - val = purple_request_field_string_get_value(f); - curpass = purple_blist_node_get_string((PurpleBlistNode *)sgc->c, "passphrase"); - - if (!val && curpass) - set = 0; - else if (val && !curpass) - set = 1; - else if (val && curpass && !purple_strequal(val, curpass)) - set = 1; - else - set = -1; - - if (set == 1) { - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - sgc->channel->channel_name, "+a", val, NULL); - purple_blist_node_set_string((PurpleBlistNode *)sgc->c, "passphrase", val); - } else if (set == 0) { - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - sgc->channel->channel_name, "-a", NULL); - purple_blist_node_remove_setting((PurpleBlistNode *)sgc->c, "passphrase"); - } - - silc_buffer_free(sgc->pubkeys); - silc_free(sgc); -} - -void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel, - SilcBuffer channel_pubkeys) -{ - SilcUInt16 argc; - SilcArgumentPayload chpks; - unsigned char *pk; - SilcUInt32 pk_len, type; - char *fingerprint, *babbleprint; - SilcPublicKey pubkey; - SilcPublicKeyIdentifier ident; - char tmp2[1024], t[512]; - PurpleRequestFields *fields; - PurpleRequestFieldGroup *g; - PurpleRequestField *f; - SilcPurpleChauth sgc; - const char *curpass = NULL; - - sgc = silc_calloc(1, sizeof(*sgc)); - if (!sgc) - return; - sgc->sg = sg; - sgc->channel = channel; - - fields = purple_request_fields_new(); - - if (sgc->c) - curpass = purple_blist_node_get_string((PurpleBlistNode *)sgc->c, "passphrase"); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_string_new("passphrase", _("Channel Passphrase"), - curpass, FALSE); - purple_request_field_string_set_masked(f, TRUE); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_label_new("l1", _("Channel Public Keys List")); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - g_snprintf(t, sizeof(t), - _("Channel authentication is used to secure the channel from " - "unauthorized access. The authentication may be based on " - "passphrase and digital signatures. If passphrase is set, it " - "is required to be able to join. If channel public keys are set " - "then only users whose public keys are listed are able to join.")); - - if (!channel_pubkeys) { - f = purple_request_field_list_new("list", NULL); - purple_request_field_group_add_field(g, f); - purple_request_fields(sg->gc, _("Channel Authentication"), - _("Channel Authentication"), t, fields, - _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb), - _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok), - purple_connection_get_account(sg->gc), NULL, NULL, sgc); - return; - } - sgc->pubkeys = silc_buffer_copy(channel_pubkeys); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_list_new("list", NULL); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - SILC_GET16_MSB(argc, channel_pubkeys->data); - chpks = silc_argument_payload_parse(channel_pubkeys->data + 2, - channel_pubkeys->len - 2, argc); - if (!chpks) - return; - - pk = silc_argument_get_first_arg(chpks, &type, &pk_len); - while (pk) { - fingerprint = silc_hash_fingerprint(NULL, pk + 4, pk_len - 4); - babbleprint = silc_hash_babbleprint(NULL, pk + 4, pk_len - 4); - silc_pkcs_public_key_payload_decode(pk, pk_len, &pubkey); - ident = silc_pkcs_decode_identifier(pubkey->identifier); - - g_snprintf(tmp2, sizeof(tmp2), "%s\n %s\n %s", - ident->realname ? ident->realname : ident->username ? - ident->username : "", fingerprint, babbleprint); - purple_request_field_list_add_icon(f, tmp2, NULL, pubkey); - - silc_free(fingerprint); - silc_free(babbleprint); - silc_pkcs_free_identifier(ident); - pk = silc_argument_get_next_arg(chpks, &type, &pk_len); - } - - purple_request_field_list_set_multi_select(f, FALSE); - purple_request_fields(sg->gc, _("Channel Authentication"), - _("Channel Authentication"), t, fields, - _("Add / Remove"), G_CALLBACK(silcpurple_chat_chpk_cb), - _("OK"), G_CALLBACK(silcpurple_chat_chauth_ok), - purple_connection_get_account(sg->gc), NULL, NULL, sgc); - - silc_argument_payload_free(chpks); -} - -static void -silcpurple_chat_chauth(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(chat->account); - sg = gc->proto_data; - - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - g_hash_table_lookup(chat->components, "channel"), - "+C", NULL); -} - - -/************************** Channel Private Groups **************************/ - -/* Private groups are "virtual" channels. They are groups inside a channel. - This is implemented by using channel private keys. By knowing a channel - private key user becomes part of that group and is able to talk on that - group. Other users, on the same channel, won't be able to see the - messages of that group. It is possible to have multiple groups inside - a channel - and thus having multiple private keys on the channel. */ - -typedef struct { - SilcPurple sg; - PurpleChat *c; - const char *channel; -} *SilcPurpleCharPrv; - -static void -silcpurple_chat_prv_add(SilcPurpleCharPrv p, PurpleRequestFields *fields) -{ - SilcPurple sg = p->sg; - char tmp[512]; - PurpleRequestField *f; - const char *name, *passphrase, *alias; - GHashTable *comp; - PurpleGroup *g; - PurpleChat *cn; - - f = purple_request_fields_get_field(fields, "name"); - name = purple_request_field_string_get_value(f); - if (!name) { - silc_free(p); - return; - } - f = purple_request_fields_get_field(fields, "passphrase"); - passphrase = purple_request_field_string_get_value(f); - f = purple_request_fields_get_field(fields, "alias"); - alias = purple_request_field_string_get_value(f); - - /* Add private group to buddy list */ - g_snprintf(tmp, sizeof(tmp), "%s [Private Group]", name); - comp = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - g_hash_table_replace(comp, g_strdup("channel"), g_strdup(tmp)); - g_hash_table_replace(comp, g_strdup("passphrase"), g_strdup(passphrase)); - - cn = purple_chat_new(sg->account, alias, comp); - g = (PurpleGroup *)p->c->node.parent; - purple_blist_add_chat(cn, g, (PurpleBlistNode *)p->c); - - /* Associate to a real channel */ - purple_blist_node_set_string((PurpleBlistNode *)cn, "parentch", p->channel); - - /* Join the group */ - silcpurple_chat_join(sg->gc, comp); - - silc_free(p); -} - -static void -silcpurple_chat_prv_cancel(SilcPurpleCharPrv p, PurpleRequestFields *fields) -{ - silc_free(p); -} - -static void -silcpurple_chat_prv(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - SilcPurpleCharPrv p; - PurpleRequestFields *fields; - PurpleRequestFieldGroup *g; - PurpleRequestField *f; - char tmp[512]; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(chat->account); - sg = gc->proto_data; - - p = silc_calloc(1, sizeof(*p)); - if (!p) - return; - p->sg = sg; - - p->channel = g_hash_table_lookup(chat->components, "channel"); - p->c = purple_blist_find_chat(sg->account, p->channel); - - fields = purple_request_fields_new(); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_string_new("name", _("Group Name"), - NULL, FALSE); - purple_request_field_group_add_field(g, f); - - f = purple_request_field_string_new("passphrase", _("Passphrase"), - NULL, FALSE); - purple_request_field_string_set_masked(f, TRUE); - purple_request_field_group_add_field(g, f); - - f = purple_request_field_string_new("alias", _("Alias"), - NULL, FALSE); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - g_snprintf(tmp, sizeof(tmp), - _("Please enter the %s channel private group name and passphrase."), - p->channel); - purple_request_fields(gc, _("Add Channel Private Group"), NULL, tmp, fields, - _("Add"), G_CALLBACK(silcpurple_chat_prv_add), - _("Cancel"), G_CALLBACK(silcpurple_chat_prv_cancel), - purple_connection_get_account(gc), NULL, NULL, p); -} - - -/****************************** Channel Modes ********************************/ - -static void -silcpurple_chat_permanent_reset(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(chat->account); - sg = gc->proto_data; - - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - g_hash_table_lookup(chat->components, "channel"), - "-f", NULL); -} - -static void -silcpurple_chat_permanent(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - const char *channel; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(chat->account); - sg = gc->proto_data; - - if (!sg->conn) - return; - - /* XXX we should have ability to define which founder - key to use. Now we use the user's own public key - (default key). */ - - /* Call CMODE */ - channel = g_hash_table_lookup(chat->components, "channel"); - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", channel, - "+f", NULL); -} - -typedef struct { - SilcPurple sg; - char *channel; -} *SilcPurpleChatInput; - -static void -silcpurple_chat_ulimit_cb(SilcPurpleChatInput s, const char *limit) -{ - SilcChannelEntry channel; - int ulimit = 0; - - channel = silc_client_get_channel(s->sg->client, s->sg->conn, - (char *)s->channel); - if (!channel) - return; - if (limit) - ulimit = atoi(limit); - - if (!limit || !(*limit) || *limit == '0') { - if (limit && ulimit == channel->user_limit) { - silc_free(s); - return; - } - silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE", - s->channel, "-l", NULL); - - silc_free(s); - return; - } - - if (ulimit == channel->user_limit) { - silc_free(s); - return; - } - - /* Call CMODE */ - silc_client_command_call(s->sg->client, s->sg->conn, NULL, "CMODE", - s->channel, "+l", limit, NULL); - - silc_free(s); -} - -static void -silcpurple_chat_ulimit(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - SilcPurpleChatInput s; - SilcChannelEntry channel; - char *ch; - char tmp[32]; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(chat->account); - sg = gc->proto_data; - - if (!sg->conn) - return; - - ch = g_strdup(g_hash_table_lookup(chat->components, "channel")); - channel = silc_client_get_channel(sg->client, sg->conn, (char *)ch); - if (!channel) - return; - - s = silc_calloc(1, sizeof(*s)); - if (!s) - return; - s->channel = ch; - s->sg = sg; - g_snprintf(tmp, sizeof(tmp), "%d", (int)channel->user_limit); - purple_request_input(gc, _("User Limit"), NULL, - _("Set user limit on channel. Set to zero to reset user limit."), - tmp, FALSE, FALSE, NULL, - _("OK"), G_CALLBACK(silcpurple_chat_ulimit_cb), - _("Cancel"), G_CALLBACK(silcpurple_chat_ulimit_cb), - purple_connection_get_account(gc), NULL, NULL, s); -} - -static void -silcpurple_chat_resettopic(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(chat->account); - sg = gc->proto_data; - - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - g_hash_table_lookup(chat->components, "channel"), - "-t", NULL); -} - -static void -silcpurple_chat_settopic(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(chat->account); - sg = gc->proto_data; - - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - g_hash_table_lookup(chat->components, "channel"), - "+t", NULL); -} - -static void -silcpurple_chat_resetprivate(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(chat->account); - sg = gc->proto_data; - - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - g_hash_table_lookup(chat->components, "channel"), - "-p", NULL); -} - -static void -silcpurple_chat_setprivate(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(chat->account); - sg = gc->proto_data; - - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - g_hash_table_lookup(chat->components, "channel"), - "+p", NULL); -} - -static void -silcpurple_chat_resetsecret(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(chat->account); - sg = gc->proto_data; - - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - g_hash_table_lookup(chat->components, "channel"), - "-s", NULL); -} - -static void -silcpurple_chat_setsecret(PurpleBlistNode *node, gpointer data) -{ - PurpleChat *chat; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_CHAT(node)); - - chat = (PurpleChat *) node; - gc = purple_account_get_connection(chat->account); - sg = gc->proto_data; - - silc_client_command_call(sg->client, sg->conn, NULL, "CMODE", - g_hash_table_lookup(chat->components, "channel"), - "+s", NULL); -} - -typedef struct { - SilcPurple sg; - SilcChannelEntry channel; -} *SilcPurpleChatWb; - -static void -silcpurple_chat_wb(PurpleBlistNode *node, gpointer data) -{ - SilcPurpleChatWb wb = data; - silcpurple_wb_init_ch(wb->sg, wb->channel); - silc_free(wb); -} - -GList *silcpurple_chat_menu(PurpleChat *chat) -{ - GHashTable *components = chat->components; - PurpleConnection *gc = purple_account_get_connection(chat->account); - SilcPurple sg = gc->proto_data; - SilcClientConnection conn = sg->conn; - const char *chname = NULL; - SilcChannelEntry channel = NULL; - SilcChannelUser chu = NULL; - SilcUInt32 mode = 0; - - GList *m = NULL; - PurpleMenuAction *act; - - if (components) - chname = g_hash_table_lookup(components, "channel"); - if (chname) - channel = silc_client_get_channel(sg->client, sg->conn, - (char *)chname); - if (channel) { - chu = silc_client_on_channel(channel, conn->local_entry); - if (chu) - mode = chu->mode; - } - - if (strstr(chname, "[Private Group]")) - return NULL; - - act = purple_menu_action_new(_("Get Info"), - PURPLE_CALLBACK(silcpurple_chat_getinfo_menu), - NULL, NULL); - m = g_list_append(m, act); - -#if 0 /* XXX For now these are not implemented. We need better - listview dialog from Purple for these. */ - if (mode & SILC_CHANNEL_UMODE_CHANOP) { - act = purple_menu_action_new(_("Invite List"), - PURPLE_CALLBACK(silcpurple_chat_invitelist), - NULL, NULL); - m = g_list_append(m, act); - - act = purple_menu_action_new(_("Ban List"), - PURPLE_CALLBACK(silcpurple_chat_banlist), - NULL, NULL); - m = g_list_append(m, act); - } -#endif - - if (chu) { - act = purple_menu_action_new(_("Add Private Group"), - PURPLE_CALLBACK(silcpurple_chat_prv), - NULL, NULL); - m = g_list_append(m, act); - } - - if (mode & SILC_CHANNEL_UMODE_CHANFO) { - act = purple_menu_action_new(_("Channel Authentication"), - PURPLE_CALLBACK(silcpurple_chat_chauth), - NULL, NULL); - m = g_list_append(m, act); - - if (channel->mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) { - act = purple_menu_action_new(_("Reset Permanent"), - PURPLE_CALLBACK(silcpurple_chat_permanent_reset), - NULL, NULL); - m = g_list_append(m, act); - } else { - act = purple_menu_action_new(_("Set Permanent"), - PURPLE_CALLBACK(silcpurple_chat_permanent), - NULL, NULL); - m = g_list_append(m, act); - } - } - - if (mode & SILC_CHANNEL_UMODE_CHANOP) { - act = purple_menu_action_new(_("Set User Limit"), - PURPLE_CALLBACK(silcpurple_chat_ulimit), - NULL, NULL); - m = g_list_append(m, act); - - if (channel->mode & SILC_CHANNEL_MODE_TOPIC) { - act = purple_menu_action_new(_("Reset Topic Restriction"), - PURPLE_CALLBACK(silcpurple_chat_resettopic), - NULL, NULL); - m = g_list_append(m, act); - } else { - act = purple_menu_action_new(_("Set Topic Restriction"), - PURPLE_CALLBACK(silcpurple_chat_settopic), - NULL, NULL); - m = g_list_append(m, act); - } - - if (channel->mode & SILC_CHANNEL_MODE_PRIVATE) { - act = purple_menu_action_new(_("Reset Private Channel"), - PURPLE_CALLBACK(silcpurple_chat_resetprivate), - NULL, NULL); - m = g_list_append(m, act); - } else { - act = purple_menu_action_new(_("Set Private Channel"), - PURPLE_CALLBACK(silcpurple_chat_setprivate), - NULL, NULL); - m = g_list_append(m, act); - } - - if (channel->mode & SILC_CHANNEL_MODE_SECRET) { - act = purple_menu_action_new(_("Reset Secret Channel"), - PURPLE_CALLBACK(silcpurple_chat_resetsecret), - NULL, NULL); - m = g_list_append(m, act); - } else { - act = purple_menu_action_new(_("Set Secret Channel"), - PURPLE_CALLBACK(silcpurple_chat_setsecret), - NULL, NULL); - m = g_list_append(m, act); - } - } - - if (channel) { - SilcPurpleChatWb wb; - wb = silc_calloc(1, sizeof(*wb)); - wb->sg = sg; - wb->channel = channel; - act = purple_menu_action_new(_("Draw On Whiteboard"), - PURPLE_CALLBACK(silcpurple_chat_wb), - (void *)wb, NULL); - m = g_list_append(m, act); - } - - return m; -} - - -/******************************* Joining Etc. ********************************/ - -void silcpurple_chat_join_done(SilcClient client, - SilcClientConnection conn, - SilcClientEntry *clients, - SilcUInt32 clients_count, - void *context) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - SilcChannelEntry channel = context; - PurpleConversation *convo; - SilcUInt32 retry = SILC_PTR_TO_32(channel->context); - SilcHashTableList htl; - SilcChannelUser chu; - GList *users = NULL, *flags = NULL; - char tmp[256]; - - if (!clients && retry < 1) { - /* Resolving users failed, try again. */ - channel->context = SILC_32_TO_PTR(retry + 1); - silc_client_get_clients_by_channel(client, conn, channel, - silcpurple_chat_join_done, channel); - return; - } - - /* Add channel to Purple */ - channel->context = SILC_32_TO_PTR(++sg->channel_ids); - serv_got_joined_chat(gc, sg->channel_ids, channel->channel_name); - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) - return; - - /* Add all users to channel */ - silc_hash_table_list(channel->user_list, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - PurpleConvChatBuddyFlags f = PURPLE_CBFLAGS_NONE; - if (!chu->client->nickname) - continue; - chu->context = SILC_32_TO_PTR(sg->channel_ids); - - if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) - f |= PURPLE_CBFLAGS_FOUNDER; - if (chu->mode & SILC_CHANNEL_UMODE_CHANOP) - f |= PURPLE_CBFLAGS_OP; - users = g_list_append(users, g_strdup(chu->client->nickname)); - flags = g_list_append(flags, GINT_TO_POINTER(f)); - - if (chu->mode & SILC_CHANNEL_UMODE_CHANFO) { - if (chu->client == conn->local_entry) - g_snprintf(tmp, sizeof(tmp), - _("You are channel founder on %s"), - channel->channel_name); - else - g_snprintf(tmp, sizeof(tmp), - _("Channel founder on %s is %s"), - channel->channel_name, chu->client->nickname); - - purple_conversation_write(convo, NULL, tmp, - PURPLE_MESSAGE_SYSTEM, time(NULL)); - - } - } - silc_hash_table_list_reset(&htl); - - purple_conv_chat_add_users(PURPLE_CONV_CHAT(convo), users, NULL, flags, FALSE); - g_list_free(users); - g_list_free(flags); - - /* Set topic */ - if (channel->topic) - purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, channel->topic); - - /* Set nick */ - purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), conn->local_entry->nickname); -} - -char *silcpurple_get_chat_name(GHashTable *data) -{ - return g_strdup(g_hash_table_lookup(data, "channel")); -} - -void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - const char *channel, *passphrase, *parentch; - - if (!conn) - return; - - channel = g_hash_table_lookup(data, "channel"); - passphrase = g_hash_table_lookup(data, "passphrase"); - - /* Check if we are joining a private group. Handle it - purely locally as it's not a real channel */ - if (strstr(channel, "[Private Group]")) { - SilcChannelEntry channel_entry; - SilcChannelPrivateKey key; - PurpleChat *c; - SilcPurplePrvgrp grp; - - c = purple_blist_find_chat(sg->account, channel); - parentch = purple_blist_node_get_string((PurpleBlistNode *)c, "parentch"); - if (!parentch) - return; - - channel_entry = silc_client_get_channel(sg->client, sg->conn, - (char *)parentch); - if (!channel_entry || - !silc_client_on_channel(channel_entry, sg->conn->local_entry)) { - char tmp[512]; - g_snprintf(tmp, sizeof(tmp), - _("You have to join the %s channel before you are " - "able to join the private group"), parentch); - purple_notify_error(gc, _("Join Private Group"), - _("Cannot join private group"), tmp); - return; - } - - /* Add channel private key */ - if (!silc_client_add_channel_private_key(client, conn, - channel_entry, channel, - NULL, NULL, - (unsigned char *)passphrase, - strlen(passphrase), &key)) - return; - - /* Join the group */ - grp = silc_calloc(1, sizeof(*grp)); - if (!grp) - return; - grp->id = ++sg->channel_ids + SILCPURPLE_PRVGRP; - grp->chid = SILC_PTR_TO_32(channel_entry->context); - grp->parentch = parentch; - grp->channel = channel; - grp->key = key; - sg->grps = g_list_append(sg->grps, grp); - serv_got_joined_chat(gc, grp->id, channel); - return; - } - - /* XXX We should have other properties here as well: - 1. whether to try to authenticate to the channel - 1a. with default key, - 1b. with specific key. - 2. whether to try to authenticate to become founder. - 2a. with default key, - 2b. with specific key. - - Since now such variety is not possible in the join dialog - we always use -founder and -auth options, which try to - do both 1 and 2 with default keys. */ - - /* Call JOIN */ - if ((passphrase != NULL) && (*passphrase != '\0')) - silc_client_command_call(client, conn, NULL, "JOIN", - channel, passphrase, "-auth", "-founder", NULL); - else - silc_client_command_call(client, conn, NULL, "JOIN", - channel, "-auth", "-founder", NULL); -} - -void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg, - const char *name) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcHashTableList htl; - SilcChannelUser chu; - gboolean found = FALSE; - - if (!conn) - return; - - /* See if we are inviting on a private group. Invite - to the actual channel */ - if (id > SILCPURPLE_PRVGRP) { - GList *l; - SilcPurplePrvgrp prv; - - for (l = sg->grps; l; l = l->next) - if (((SilcPurplePrvgrp)l->data)->id == id) - break; - if (!l) - return; - prv = l->data; - id = prv->chid; - } - - /* Find channel by id */ - silc_hash_table_list(conn->local_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - if (SILC_PTR_TO_32(chu->channel->context) == id ) { - found = TRUE; - break; - } - } - silc_hash_table_list_reset(&htl); - if (!found) - return; - - /* Call INVITE */ - silc_client_command_call(client, conn, NULL, "INVITE", - chu->channel->channel_name, - name, NULL); -} - -void silcpurple_chat_leave(PurpleConnection *gc, int id) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcHashTableList htl; - SilcChannelUser chu; - gboolean found = FALSE; - GList *l; - SilcPurplePrvgrp prv; - - if (!conn) - return; - - /* See if we are leaving a private group */ - if (id > SILCPURPLE_PRVGRP) { - SilcChannelEntry channel; - - for (l = sg->grps; l; l = l->next) - if (((SilcPurplePrvgrp)l->data)->id == id) - break; - if (!l) - return; - prv = l->data; - channel = silc_client_get_channel(sg->client, sg->conn, - (char *)prv->parentch); - if (!channel) - return; - silc_client_del_channel_private_key(client, conn, - channel, prv->key); - silc_free(prv); - sg->grps = g_list_remove(sg->grps, prv); - serv_got_chat_left(gc, id); - return; - } - - /* Find channel by id */ - silc_hash_table_list(conn->local_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - if (SILC_PTR_TO_32(chu->channel->context) == id ) { - found = TRUE; - break; - } - } - silc_hash_table_list_reset(&htl); - if (!found) - return; - - /* Call LEAVE */ - silc_client_command_call(client, conn, NULL, "LEAVE", - chu->channel->channel_name, NULL); - - serv_got_chat_left(gc, id); - - /* Leave from private groups on this channel as well */ - for (l = sg->grps; l; l = l->next) - if (((SilcPurplePrvgrp)l->data)->chid == id) { - prv = l->data; - silc_client_del_channel_private_key(client, conn, - chu->channel, - prv->key); - serv_got_chat_left(gc, prv->id); - silc_free(prv); - sg->grps = g_list_remove(sg->grps, prv); - if (!sg->grps) - break; - } -} - -int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg, PurpleMessageFlags msgflags) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcHashTableList htl; - SilcChannelUser chu; - SilcChannelEntry channel = NULL; - SilcChannelPrivateKey key = NULL; - SilcUInt32 flags; - int ret; - char *msg2, *tmp; - gboolean found = FALSE; - gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE); - - if (!msg || !conn) - return 0; - - flags = SILC_MESSAGE_FLAG_UTF8; - - tmp = msg2 = purple_unescape_html(msg); - - if (!g_ascii_strncasecmp(msg2, "/me ", 4)) - { - msg2 += 4; - if (!*msg2) { - g_free(tmp); - return 0; - } - flags |= SILC_MESSAGE_FLAG_ACTION; - } else if (strlen(msg) > 1 && msg[0] == '/') { - if (!silc_client_command_call(client, conn, msg + 1)) - purple_notify_error(gc, _("Call Command"), _("Cannot call command"), - _("Unknown command")); - g_free(tmp); - return 0; - } - - - if (sign) - flags |= SILC_MESSAGE_FLAG_SIGNED; - - /* Get the channel private key if we are sending on - private group */ - if (id > SILCPURPLE_PRVGRP) { - GList *l; - SilcPurplePrvgrp prv; - - for (l = sg->grps; l; l = l->next) - if (((SilcPurplePrvgrp)l->data)->id == id) - break; - if (!l) { - g_free(tmp); - return 0; - } - prv = l->data; - channel = silc_client_get_channel(sg->client, sg->conn, - (char *)prv->parentch); - if (!channel) { - g_free(tmp); - return 0; - } - key = prv->key; - } - - if (!channel) { - /* Find channel by id */ - silc_hash_table_list(conn->local_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - if (SILC_PTR_TO_32(chu->channel->context) == id ) { - found = TRUE; - break; - } - } - silc_hash_table_list_reset(&htl); - if (!found) { - g_free(tmp); - return 0; - } - channel = chu->channel; - } - - /* Send channel message */ - ret = silc_client_send_channel_message(client, conn, channel, key, - flags, (unsigned char *)msg2, - strlen(msg2), TRUE); - if (ret) { - serv_got_chat_in(gc, id, purple_connection_get_display_name(gc), msgflags, msg, - time(NULL)); - } - g_free(tmp); - - return ret; -} - -void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcHashTableList htl; - SilcChannelUser chu; - gboolean found = FALSE; - - if (!conn) - return; - - /* See if setting topic on private group. Set it - on the actual channel */ - if (id > SILCPURPLE_PRVGRP) { - GList *l; - SilcPurplePrvgrp prv; - - for (l = sg->grps; l; l = l->next) - if (((SilcPurplePrvgrp)l->data)->id == id) - break; - if (!l) - return; - prv = l->data; - id = prv->chid; - } - - /* Find channel by id */ - silc_hash_table_list(conn->local_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - if (SILC_PTR_TO_32(chu->channel->context) == id ) { - found = TRUE; - break; - } - } - silc_hash_table_list_reset(&htl); - if (!found) - return; - - /* Call TOPIC */ - silc_client_command_call(client, conn, NULL, "TOPIC", - chu->channel->channel_name, topic, NULL); -} - -PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - GList *fields = NULL; - PurpleRoomlistField *f; - - if (!conn) - return NULL; - - if (sg->roomlist) - purple_roomlist_unref(sg->roomlist); - - sg->roomlist_cancelled = FALSE; - - sg->roomlist = purple_roomlist_new(purple_connection_get_account(gc)); - f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, "", "channel", TRUE); - fields = g_list_append(fields, f); - f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_INT, - _("Users"), "users", FALSE); - fields = g_list_append(fields, f); - f = purple_roomlist_field_new(PURPLE_ROOMLIST_FIELD_STRING, - _("Topic"), "topic", FALSE); - fields = g_list_append(fields, f); - purple_roomlist_set_fields(sg->roomlist, fields); - - /* Call LIST */ - silc_client_command_call(client, conn, "LIST"); - - purple_roomlist_set_in_progress(sg->roomlist, TRUE); - - return sg->roomlist; -} - -void silcpurple_roomlist_cancel(PurpleRoomlist *list) -{ - PurpleConnection *gc = purple_account_get_connection(list->account); - SilcPurple sg; - - if (!gc) - return; - sg = gc->proto_data; - - purple_roomlist_set_in_progress(list, FALSE); - if (sg->roomlist == list) { - purple_roomlist_unref(sg->roomlist); - sg->roomlist = NULL; - sg->roomlist_cancelled = TRUE; - } -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc10/ft.c --- a/libpurple/protocols/silc10/ft.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,412 +0,0 @@ -/* - - silcpurple_ft.c - - Author: Pekka Riikonen - - Copyright (C) 2004 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#include "silcincludes.h" -#include "silcclient.h" -#include "silcpurple.h" - -/****************************** File Transfer ********************************/ - -/* This implements the secure file transfer protocol (SFTP) using the SILC - SFTP library implementation. The API we use from the SILC Toolkit is the - SILC Client file transfer API, as it provides a simple file transfer we - need in this case. We could use the SILC SFTP API directly, but it would - be an overkill since we'd effectively re-implement the file transfer what - the SILC Client's file transfer API already provides. - - From Purple we do NOT use the FT API to do the transfer as it is very limiting. - In fact it does not suite to file transfers like SFTP at all. For example, - it assumes that read operations are synchronous what they are not in SFTP. - It also assumes that the file transfer socket is to be handled by the Purple - eventloop, and this naturally is something we don't want to do in case of - SILC Toolkit. The FT API suites well to purely stream based file transfers - like HTTP GET and similar. - - For this reason, we directly access the Purple GKT FT API and hack the FT - API to merely provide the user interface experience and all the magic - is done in the SILC Toolkit. Ie. we update the statistics information in - the FT API for user interface, and that's it. A bit dirty but until the - FT API gets better this is the way to go. Good thing that FT API allowed - us to do this. */ - -typedef struct { - SilcPurple sg; - SilcClientEntry client_entry; - SilcUInt32 session_id; - char *hostname; - SilcUInt16 port; - PurpleXfer *xfer; - - SilcClientFileName completion; - void *completion_context; -} *SilcPurpleXfer; - -static void -silcpurple_ftp_monitor(SilcClient client, - SilcClientConnection conn, - SilcClientMonitorStatus status, - SilcClientFileError error, - SilcUInt64 offset, - SilcUInt64 filesize, - SilcClientEntry client_entry, - SilcUInt32 session_id, - const char *filepath, - void *context) -{ - SilcPurpleXfer xfer = context; - PurpleConnection *gc = xfer->sg->gc; - char tmp[256]; - - if (status == SILC_CLIENT_FILE_MONITOR_CLOSED) { - purple_xfer_unref(xfer->xfer); - silc_free(xfer); - return; - } - - if (status == SILC_CLIENT_FILE_MONITOR_KEY_AGREEMENT) - return; - - if (status == SILC_CLIENT_FILE_MONITOR_ERROR) { - if (error == SILC_CLIENT_FILE_NO_SUCH_FILE) { - g_snprintf(tmp, sizeof(tmp), "No such file %s", - filepath ? filepath : "[N/A]"); - purple_notify_error(gc, _("Secure File Transfer"), - _("Error during file transfer"), tmp); - } else if (error == SILC_CLIENT_FILE_PERMISSION_DENIED) { - purple_notify_error(gc, _("Secure File Transfer"), - _("Error during file transfer"), - _("Permission denied")); - } else if (error == SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED) { - purple_notify_error(gc, _("Secure File Transfer"), - _("Error during file transfer"), - _("Key agreement failed")); - } else if (error == SILC_CLIENT_FILE_UNKNOWN_SESSION) { - purple_notify_error(gc, _("Secure File Transfer"), - _("Error during file transfer"), - _("File transfer session does not exist")); - } else { - purple_notify_error(gc, _("Secure File Transfer"), - _("Error during file transfer"), NULL); - } - silc_client_file_close(client, conn, session_id); - purple_xfer_unref(xfer->xfer); - silc_free(xfer); - return; - } - - /* Update file transfer UI */ - if (!offset && filesize) - purple_xfer_set_size(xfer->xfer, filesize); - if (offset && filesize) { - xfer->xfer->bytes_sent = offset; - xfer->xfer->bytes_remaining = filesize - offset; - } - purple_xfer_update_progress(xfer->xfer); - - if (status == SILC_CLIENT_FILE_MONITOR_SEND || - status == SILC_CLIENT_FILE_MONITOR_RECEIVE) { - if (offset == filesize) { - /* Download finished */ - purple_xfer_set_completed(xfer->xfer, TRUE); - silc_client_file_close(client, conn, session_id); - } - } -} - -static void -silcpurple_ftp_cancel(PurpleXfer *x) -{ - SilcPurpleXfer xfer = x->data; - xfer->xfer->status = PURPLE_XFER_STATUS_CANCEL_LOCAL; - purple_xfer_update_progress(xfer->xfer); - silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id); -} - -static void -silcpurple_ftp_ask_name_cancel(PurpleXfer *x) -{ - SilcPurpleXfer xfer = x->data; - - /* Cancel the transmission */ - xfer->completion(NULL, xfer->completion_context); - silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id); -} - -static void -silcpurple_ftp_ask_name_ok(PurpleXfer *x) -{ - SilcPurpleXfer xfer = x->data; - const char *name; - - name = purple_xfer_get_local_filename(x); - g_unlink(name); - xfer->completion(name, xfer->completion_context); -} - -static void -silcpurple_ftp_ask_name(SilcClient client, - SilcClientConnection conn, - SilcUInt32 session_id, - const char *remote_filename, - SilcClientFileName completion, - void *completion_context, - void *context) -{ - SilcPurpleXfer xfer = context; - - xfer->completion = completion; - xfer->completion_context = completion_context; - - purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_ask_name_ok); - purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_ask_name_cancel); - - /* Request to save the file */ - purple_xfer_set_filename(xfer->xfer, remote_filename); - purple_xfer_request(xfer->xfer); -} - -static void -silcpurple_ftp_request_result(PurpleXfer *x) -{ - SilcPurpleXfer xfer = x->data; - SilcClientFileError status; - PurpleConnection *gc = xfer->sg->gc; - - if (purple_xfer_get_status(x) != PURPLE_XFER_STATUS_ACCEPTED) - return; - - /* Start the file transfer */ - status = silc_client_file_receive(xfer->sg->client, xfer->sg->conn, - silcpurple_ftp_monitor, xfer, - NULL, xfer->session_id, - silcpurple_ftp_ask_name, xfer); - switch (status) { - case SILC_CLIENT_FILE_OK: - return; - break; - - case SILC_CLIENT_FILE_UNKNOWN_SESSION: - purple_notify_error(gc, _("Secure File Transfer"), - _("No file transfer session active"), NULL); - break; - - case SILC_CLIENT_FILE_ALREADY_STARTED: - purple_notify_error(gc, _("Secure File Transfer"), - _("File transfer already started"), NULL); - break; - - case SILC_CLIENT_FILE_KEY_AGREEMENT_FAILED: - purple_notify_error(gc, _("Secure File Transfer"), - _("Could not perform key agreement for file transfer"), - NULL); - break; - - default: - purple_notify_error(gc, _("Secure File Transfer"), - _("Could not start the file transfer"), NULL); - break; - } - - /* Error */ - purple_xfer_unref(xfer->xfer); - g_free(xfer->hostname); - silc_free(xfer); -} - -static void -silcpurple_ftp_request_denied(PurpleXfer *x) -{ - -} - -void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn, - SilcClientEntry client_entry, SilcUInt32 session_id, - const char *hostname, SilcUInt16 port) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - SilcPurpleXfer xfer; - - xfer = silc_calloc(1, sizeof(*xfer)); - if (!xfer) { - silc_client_file_close(sg->client, sg->conn, session_id); - return; - } - - xfer->sg = sg; - xfer->client_entry = client_entry; - xfer->session_id = session_id; - xfer->hostname = g_strdup(hostname); - xfer->port = port; - xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_RECEIVE, - xfer->client_entry->nickname); - if (!xfer->xfer) { - silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id); - g_free(xfer->hostname); - silc_free(xfer); - return; - } - purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_request_result); - purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_request_denied); - purple_xfer_set_cancel_recv_fnc(xfer->xfer, silcpurple_ftp_cancel); - xfer->xfer->remote_ip = g_strdup(hostname); - xfer->xfer->remote_port = port; - xfer->xfer->data = xfer; - - /* File transfer request */ - purple_xfer_request(xfer->xfer); -} - -static void -silcpurple_ftp_send_cancel(PurpleXfer *x) -{ - SilcPurpleXfer xfer = x->data; - silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id); - purple_xfer_unref(xfer->xfer); - g_free(xfer->hostname); - silc_free(xfer); -} - -static void -silcpurple_ftp_send(PurpleXfer *x) -{ - SilcPurpleXfer xfer = x->data; - const char *name; - char *local_ip = NULL, *remote_ip = NULL; - gboolean local = TRUE; - - name = purple_xfer_get_local_filename(x); - - /* Do the same magic what we do with key agreement (see silcpurple_buddy.c) - to see if we are behind NAT. */ - if (silc_net_check_local_by_sock(xfer->sg->conn->sock->sock, - NULL, &local_ip)) { - /* Check if the IP is private */ - if (silcpurple_ip_is_private(local_ip)) { - local = FALSE; - /* Local IP is private, resolve the remote server IP to see whether - we are talking to Internet or just on LAN. */ - if (silc_net_check_host_by_sock(xfer->sg->conn->sock->sock, NULL, - &remote_ip)) - if (silcpurple_ip_is_private(remote_ip)) - /* We assume we are in LAN. Let's provide the connection point. */ - local = TRUE; - } - } - - if (local && !local_ip) - local_ip = silc_net_localip(); - - /* Send the file */ - silc_client_file_send(xfer->sg->client, xfer->sg->conn, - silcpurple_ftp_monitor, xfer, - local_ip, 0, !local, xfer->client_entry, - name, &xfer->session_id); - - silc_free(local_ip); - silc_free(remote_ip); -} - -static void -silcpurple_ftp_send_file_resolved(SilcClient client, - SilcClientConnection conn, - SilcClientEntry *clients, - SilcUInt32 clients_count, - void *context) -{ - PurpleConnection *gc = client->application; - char tmp[256]; - - if (!clients) { - g_snprintf(tmp, sizeof(tmp), - _("User %s is not present in the network"), - (const char *)context); - purple_notify_error(gc, _("Secure File Transfer"), - _("Cannot send file"), tmp); - silc_free(context); - return; - } - - silcpurple_ftp_send_file(client->application, (const char *)context, NULL); - silc_free(context); -} - -PurpleXfer *silcpurple_ftp_new_xfer(PurpleConnection *gc, const char *name) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcClientEntry *clients; - SilcUInt32 clients_count; - SilcPurpleXfer xfer; - char *nickname; - - g_return_val_if_fail(name != NULL, NULL); - - if (!silc_parse_userfqdn(name, &nickname, NULL)) - return NULL; - - /* Find client entry */ - clients = silc_client_get_clients_local(client, conn, nickname, name, - &clients_count); - if (!clients) { - silc_client_get_clients(client, conn, nickname, NULL, - silcpurple_ftp_send_file_resolved, - strdup(name)); - silc_free(nickname); - return NULL; - } - - xfer = silc_calloc(1, sizeof(*xfer)); - - g_return_val_if_fail(xfer != NULL, NULL); - - xfer->sg = sg; - xfer->client_entry = clients[0]; - xfer->xfer = purple_xfer_new(xfer->sg->account, PURPLE_XFER_SEND, - xfer->client_entry->nickname); - if (!xfer->xfer) { - silc_client_file_close(xfer->sg->client, xfer->sg->conn, xfer->session_id); - g_free(xfer->hostname); - silc_free(xfer); - return NULL; - } - purple_xfer_set_init_fnc(xfer->xfer, silcpurple_ftp_send); - purple_xfer_set_request_denied_fnc(xfer->xfer, silcpurple_ftp_request_denied); - purple_xfer_set_cancel_send_fnc(xfer->xfer, silcpurple_ftp_send_cancel); - xfer->xfer->data = xfer; - - silc_free(clients); - silc_free(nickname); - - return xfer->xfer; -} - -void silcpurple_ftp_send_file(PurpleConnection *gc, const char *name, const char *file) -{ - PurpleXfer *xfer = silcpurple_ftp_new_xfer(gc, name); - - g_return_if_fail(xfer != NULL); - - /* Choose file to send */ - if (file) - purple_xfer_request_accepted(xfer, file); - else - purple_xfer_request(xfer); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc10/ops.c --- a/libpurple/protocols/silc10/ops.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2065 +0,0 @@ -/* - - silcpurple_ops.c - - Author: Pekka Riikonen - - Copyright (C) 2004 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#include "silcincludes.h" -#include "silcclient.h" -#include "silcpurple.h" -#include "imgstore.h" -#include "wb.h" - -#include "glibcompat.h" - -static void -silc_channel_message(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcChannelEntry channel, - SilcMessagePayload payload, SilcChannelPrivateKey key, - SilcMessageFlags flags, const unsigned char *message, - SilcUInt32 message_len); -static void -silc_private_message(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcMessagePayload payload, - SilcMessageFlags flags, const unsigned char *message, - SilcUInt32 message_len); - -/* Message sent to the application by library. `conn' associates the - message to a specific connection. `conn', however, may be NULL. - The `type' indicates the type of the message sent by the library. - The application can for example filter the message according the - type. */ - -static void -silc_say(SilcClient client, SilcClientConnection conn, - SilcClientMessageType type, char *msg, ...) -{ - /* Nothing */ -} - -#ifdef HAVE_SILCMIME_H -/* Processes incoming MIME message. Can be private message or channel - message. */ - -static void -silcpurple_mime_message(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcChannelEntry channel, - SilcMessagePayload payload, SilcChannelPrivateKey key, - SilcMessageFlags flags, SilcMime mime, - gboolean recursive) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - const char *type; - const unsigned char *data; - SilcUInt32 data_len; - PurpleMessageFlags cflags = 0; - PurpleConversation *convo = NULL; - - if (!mime) - return; - - /* Check for fragmented MIME message */ - if (silc_mime_is_partial(mime)) { - if (!sg->mimeass) - sg->mimeass = silc_mime_assembler_alloc(); - - /* Defragment */ - mime = silc_mime_assemble(sg->mimeass, mime); - if (!mime) - /* More fragments to come */ - return; - - /* Process the complete message */ - silcpurple_mime_message(client, conn, sender, channel, - payload, key, flags, mime, FALSE); - return; - } - - /* Check for multipart message */ - if (silc_mime_is_multipart(mime)) { - SilcMime p; - const char *mtype; - SilcDList parts = silc_mime_get_multiparts(mime, &mtype); - - /* Only "mixed" type supported */ - if (!purple_strequal(mtype, "mixed")) - goto out; - - silc_dlist_start(parts); - while ((p = silc_dlist_get(parts)) != SILC_LIST_END) { - /* Recursively process parts */ - silcpurple_mime_message(client, conn, sender, channel, - payload, key, flags, p, TRUE); - } - goto out; - } - - /* Get content type and MIME data */ - type = silc_mime_get_field(mime, "Content-Type"); - if (!type) - goto out; - data = silc_mime_get_data(mime, &data_len); - if (!data) - goto out; - - /* Process according to content type */ - - /* Plain text */ - if (strstr(type, "text/plain")) { - /* Default is UTF-8, don't check for other charsets */ - if (!strstr(type, "utf-8")) - goto out; - - if (channel) - silc_channel_message(client, conn, sender, channel, - payload, key, - SILC_MESSAGE_FLAG_UTF8, data, - data_len); - else - silc_private_message(client, conn, sender, payload, - SILC_MESSAGE_FLAG_UTF8, data, - data_len); - goto out; - } - - /* Image */ - if (strstr(type, "image/png") || - strstr(type, "image/jpeg") || - strstr(type, "image/gif") || - strstr(type, "image/tiff")) { - char tmp[32]; - int imgid; - - /* Get channel convo (if message is for channel) */ - if (key && channel) { - GList *l; - SilcPurplePrvgrp prv; - - for (l = sg->grps; l; l = l->next) - if (((SilcPurplePrvgrp)l->data)->key == key) { - prv = l->data; - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - prv->channel, sg->account); - break; - } - } - if (channel && !convo) - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (channel && !convo) - goto out; - - imgid = purple_imgstore_add_with_id(g_memdup2(data, data_len), data_len, ""); - if (imgid) { - cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV; - g_snprintf(tmp, sizeof(tmp), "", imgid); - - if (channel) - serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)), - sender->nickname ? - sender->nickname : - "", cflags, - tmp, time(NULL)); - else - serv_got_im(gc, sender->nickname ? - sender->nickname : "", - tmp, cflags, time(NULL)); - - purple_imgstore_unref_by_id(imgid); - cflags = 0; - } - goto out; - } - - /* Whiteboard message */ - if (strstr(type, "application/x-wb") && - !purple_account_get_bool(sg->account, "block-wb", FALSE)) { - if (channel) - silcpurple_wb_receive_ch(client, conn, sender, channel, - payload, flags, data, data_len); - else - silcpurple_wb_receive(client, conn, sender, payload, - flags, data, data_len); - goto out; - } - - out: - if (!recursive) - silc_mime_free(mime); -} -#endif /* HAVE_SILCMIME_H */ - -/* Message for a channel. The `sender' is the sender of the message - The `channel' is the channel. The `message' is the message. Note - that `message' maybe NULL. The `flags' indicates message flags - and it is used to determine how the message can be interpreted - (like it may tell the message is multimedia message). */ - -static void -silc_channel_message(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcChannelEntry channel, - SilcMessagePayload payload, SilcChannelPrivateKey key, - SilcMessageFlags flags, const unsigned char *message, - SilcUInt32 message_len) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - PurpleConversation *convo = NULL; - char *msg, *tmp; - - if (!message) - return; - - if (key) { - GList *l; - SilcPurplePrvgrp prv; - - for (l = sg->grps; l; l = l->next) - if (((SilcPurplePrvgrp)l->data)->key == key) { - prv = l->data; - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - prv->channel, sg->account); - break; - } - } - if (!convo) - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) - return; - - if (flags & SILC_MESSAGE_FLAG_SIGNED && - purple_account_get_bool(sg->account, "sign-verify", FALSE)) { - /* XXX */ - } - - if (flags & SILC_MESSAGE_FLAG_DATA) { - /* Process MIME message */ -#ifdef HAVE_SILCMIME_H - SilcMime mime; - mime = silc_mime_decode(message, message_len); - silcpurple_mime_message(client, conn, sender, channel, payload, - key, flags, mime, FALSE); -#else - char type[128], enc[128]; - unsigned char *data; - SilcUInt32 data_len; - - memset(type, 0, sizeof(type)); - memset(enc, 0, sizeof(enc)); - - if (!silc_mime_parse(message, message_len, NULL, 0, - type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data, - &data_len)) - return; - - if (purple_strequal(type, "application/x-wb") && - purple_strequal(enc, "binary") && - !purple_account_get_bool(sg->account, "block-wb", FALSE)) - silcpurple_wb_receive_ch(client, conn, sender, channel, - payload, flags, data, data_len); -#endif - return; - } - - if (flags & SILC_MESSAGE_FLAG_ACTION) { - msg = g_strdup_printf("/me %s", - (const char *)message); - if (!msg) - return; - - tmp = g_markup_escape_text(msg, -1); - /* Send to Purple */ - serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)), - sender->nickname ? - sender->nickname : "", 0, - tmp, time(NULL)); - g_free(tmp); - g_free(msg); - return; - } - - if (flags & SILC_MESSAGE_FLAG_NOTICE) { - msg = g_strdup_printf("(notice) %s %s", - sender->nickname ? - sender->nickname : "", - (const char *)message); - if (!msg) - return; - - /* Send to Purple */ - purple_conversation_write(convo, NULL, (const char *)msg, - PURPLE_MESSAGE_SYSTEM, time(NULL)); - g_free(msg); - return; - } - - if (flags & SILC_MESSAGE_FLAG_UTF8) { - tmp = g_markup_escape_text((const char *)message, -1); - /* Send to Purple */ - serv_got_chat_in(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)), - sender->nickname ? - sender->nickname : "", 0, - tmp, time(NULL)); - g_free(tmp); - } -} - - -/* Private message to the client. The `sender' is the sender of the - message. The message is `message'and maybe NULL. The `flags' - indicates message flags and it is used to determine how the message - can be interpreted (like it may tell the message is multimedia - message). */ - -static void -silc_private_message(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcMessagePayload payload, - SilcMessageFlags flags, const unsigned char *message, - SilcUInt32 message_len) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - PurpleConversation *convo = NULL; - char *msg, *tmp; - - if (!message) - return; - - if (sender->nickname) - /* XXX - Should this be PURPLE_CONV_TYPE_IM? */ - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, - sender->nickname, sg->account); - - if (flags & SILC_MESSAGE_FLAG_SIGNED && - purple_account_get_bool(sg->account, "sign-verify", FALSE)) { - /* XXX */ - } - - if (flags & SILC_MESSAGE_FLAG_DATA) { -#ifdef HAVE_SILCMIME_H - /* Process MIME message */ - SilcMime mime; - mime = silc_mime_decode(message, message_len); - silcpurple_mime_message(client, conn, sender, NULL, payload, - NULL, flags, mime, FALSE); -#else - char type[128], enc[128]; - unsigned char *data; - SilcUInt32 data_len; - - memset(type, 0, sizeof(type)); - memset(enc, 0, sizeof(enc)); - - if (!silc_mime_parse(message, message_len, NULL, 0, - type, sizeof(type) - 1, enc, sizeof(enc) - 1, &data, - &data_len)) - return; - - if (purple_strequal(type, "application/x-wb") && - purple_strequal(enc, "binary") && - !purple_account_get_bool(sg->account, "block-wb", FALSE)) - silcpurple_wb_receive(client, conn, sender, payload, - flags, data, data_len); -#endif - return; - } - - if (flags & SILC_MESSAGE_FLAG_ACTION && convo) { - msg = g_strdup_printf("/me %s", - (const char *)message); - if (!msg) - return; - - tmp = g_markup_escape_text(msg, -1); - /* Send to Purple */ - serv_got_im(gc, sender->nickname ? - sender->nickname : "", - tmp, 0, time(NULL)); - g_free(msg); - g_free(tmp); - return; - } - - if (flags & SILC_MESSAGE_FLAG_NOTICE && convo) { - msg = g_strdup_printf("(notice) %s %s", - sender->nickname ? - sender->nickname : "", - (const char *)message); - if (!msg) - return; - - /* Send to Purple */ - purple_conversation_write(convo, NULL, (const char *)msg, - PURPLE_MESSAGE_SYSTEM, time(NULL)); - g_free(msg); - return; - } - - if (flags & SILC_MESSAGE_FLAG_UTF8) { - tmp = g_markup_escape_text((const char *)message, -1); - /* Send to Purple */ - serv_got_im(gc, sender->nickname ? - sender->nickname : "", - tmp, 0, time(NULL)); - g_free(tmp); - } -} - - -/* Notify message to the client. The notify arguments are sent in the - same order as servers sends them. The arguments are same as received - from the server except for ID's. If ID is received application receives - the corresponding entry to the ID. For example, if Client ID is received - application receives SilcClientEntry. Also, if the notify type is - for channel the channel entry is sent to application (even if server - does not send it because client library gets the channel entry from - the Channel ID in the packet's header). */ - -static void -silc_notify(SilcClient client, SilcClientConnection conn, - SilcNotifyType type, ...) -{ - va_list va; - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - PurpleConversation *convo; - SilcClientEntry client_entry, client_entry2; - SilcChannelEntry channel; - SilcServerEntry server_entry; - SilcIdType idtype; - void *entry; - SilcUInt32 mode; - SilcHashTableList htl; - SilcChannelUser chu; - char buf[512], buf2[512], *tmp, *name; - SilcNotifyType notify; - PurpleBuddy *b; - int i; - - va_start(va, type); - memset(buf, 0, sizeof(buf)); - - switch (type) { - - case SILC_NOTIFY_TYPE_NONE: - break; - - case SILC_NOTIFY_TYPE_INVITE: - { - GHashTable *components; - va_arg(va, SilcChannelEntry); - name = va_arg(va, char *); - client_entry = va_arg(va, SilcClientEntry); - - components = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - g_hash_table_insert(components, strdup("channel"), strdup(name)); - serv_got_chat_invite(gc, name, client_entry->nickname, NULL, components); - } - break; - - case SILC_NOTIFY_TYPE_JOIN: - client_entry = va_arg(va, SilcClientEntry); - channel = va_arg(va, SilcChannelEntry); - - /* If we joined channel, do nothing */ - if (client_entry == conn->local_entry) - break; - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) - break; - - /* Join user to channel */ - g_snprintf(buf, sizeof(buf), "%s@%s", - client_entry->username, client_entry->hostname); - purple_conv_chat_add_user(PURPLE_CONV_CHAT(convo), - g_strdup(client_entry->nickname), buf, PURPLE_CBFLAGS_NONE, TRUE); - - break; - - case SILC_NOTIFY_TYPE_LEAVE: - client_entry = va_arg(va, SilcClientEntry); - channel = va_arg(va, SilcChannelEntry); - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) - break; - - /* Remove user from channel */ - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), - client_entry->nickname, NULL); - - break; - - case SILC_NOTIFY_TYPE_SIGNOFF: - client_entry = va_arg(va, SilcClientEntry); - tmp = va_arg(va, char *); - - if (!client_entry->nickname) - break; - - /* Remove from all channels */ - silc_hash_table_list(client_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - chu->channel->channel_name, sg->account); - if (!convo) - continue; - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), - client_entry->nickname, - tmp); - } - silc_hash_table_list_reset(&htl); - - break; - - case SILC_NOTIFY_TYPE_TOPIC_SET: - { - char *esc, *tmp2; - idtype = va_arg(va, int); - entry = va_arg(va, void *); - tmp = va_arg(va, char *); - channel = va_arg(va, SilcChannelEntry); - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) - break; - - if (!tmp) - break; - - esc = g_markup_escape_text(tmp, -1); - tmp2 = purple_markup_linkify(esc); - g_free(esc); - - if (idtype == SILC_ID_CLIENT) { - client_entry = (SilcClientEntry)entry; - g_snprintf(buf, sizeof(buf), - _("%s has changed the topic of %s to: %s"), - client_entry->nickname, channel->channel_name, tmp2); - purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); - purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), - client_entry->nickname, tmp); - } else if (idtype == SILC_ID_SERVER) { - server_entry = (SilcServerEntry)entry; - g_snprintf(buf, sizeof(buf), - _("%s has changed the topic of %s to: %s"), - server_entry->server_name, channel->channel_name, tmp2); - purple_conv_chat_write(PURPLE_CONV_CHAT(convo), server_entry->server_name, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); - purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), - server_entry->server_name, tmp); - } else if (idtype == SILC_ID_CHANNEL) { - channel = (SilcChannelEntry)entry; - g_snprintf(buf, sizeof(buf), - _("%s has changed the topic of %s to: %s"), - channel->channel_name, channel->channel_name, tmp2); - purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); - purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), - channel->channel_name, tmp); - } else { - purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, tmp); - } - - g_free(tmp2); - - break; - - } - case SILC_NOTIFY_TYPE_NICK_CHANGE: - client_entry = va_arg(va, SilcClientEntry); - client_entry2 = va_arg(va, SilcClientEntry); - - if (purple_strequal(client_entry->nickname, client_entry2->nickname)) - break; - - /* Change nick on all channels */ - silc_hash_table_list(client_entry2->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - chu->channel->channel_name, sg->account); - if (!convo) - continue; - if (purple_conv_chat_find_user(PURPLE_CONV_CHAT(convo), client_entry->nickname)) - purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo), - client_entry->nickname, - client_entry2->nickname); - } - silc_hash_table_list_reset(&htl); - - break; - - case SILC_NOTIFY_TYPE_CMODE_CHANGE: - idtype = va_arg(va, int); - entry = va_arg(va, void *); - mode = va_arg(va, SilcUInt32); - (void)va_arg(va, char *); - (void)va_arg(va, char *); - (void)va_arg(va, char *); - (void)va_arg(va, SilcPublicKey); - (void)va_arg(va, SilcBuffer); - channel = va_arg(va, SilcChannelEntry); - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) - break; - - if (idtype == SILC_ID_CLIENT) - name = ((SilcClientEntry)entry)->nickname; - else if (idtype == SILC_ID_SERVER) - name = ((SilcServerEntry)entry)->server_name; - else - name = ((SilcChannelEntry)entry)->channel_name; - if (!name) - break; - - if (mode) { - silcpurple_get_chmode_string(mode, buf2, sizeof(buf2)); - g_snprintf(buf, sizeof(buf), - _("%s set channel %s modes to: %s"), name, - channel->channel_name, buf2); - } else { - g_snprintf(buf, sizeof(buf), - _("%s removed all channel %s modes"), name, - channel->channel_name); - } - purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); - break; - - case SILC_NOTIFY_TYPE_CUMODE_CHANGE: - { - PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE; - idtype = va_arg(va, int); - entry = va_arg(va, void *); - mode = va_arg(va, SilcUInt32); - client_entry2 = va_arg(va, SilcClientEntry); - channel = va_arg(va, SilcChannelEntry); - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) - break; - - if (idtype == SILC_ID_CLIENT) - name = ((SilcClientEntry)entry)->nickname; - else if (idtype == SILC_ID_SERVER) - name = ((SilcServerEntry)entry)->server_name; - else - name = ((SilcChannelEntry)entry)->channel_name; - if (!name) - break; - - if (mode) { - silcpurple_get_chumode_string(mode, buf2, sizeof(buf2)); - g_snprintf(buf, sizeof(buf), - _("%s set %s's modes to: %s"), name, - client_entry2->nickname, buf2); - if (mode & SILC_CHANNEL_UMODE_CHANFO) - flags |= PURPLE_CBFLAGS_FOUNDER; - if (mode & SILC_CHANNEL_UMODE_CHANOP) - flags |= PURPLE_CBFLAGS_OP; - } else { - g_snprintf(buf, sizeof(buf), - _("%s removed all %s's modes"), name, - client_entry2->nickname); - } - purple_conv_chat_write(PURPLE_CONV_CHAT(convo), channel->channel_name, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); - purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(convo), client_entry2->nickname, flags); - break; - } - - case SILC_NOTIFY_TYPE_MOTD: - tmp = va_arg(va, char *); - silc_free(sg->motd); - sg->motd = silc_memdup(tmp, strlen(tmp)); - break; - - case SILC_NOTIFY_TYPE_KICKED: - client_entry = va_arg(va, SilcClientEntry); - tmp = va_arg(va, char *); - client_entry2 = va_arg(va, SilcClientEntry); - channel = va_arg(va, SilcChannelEntry); - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) - break; - - if (client_entry == conn->local_entry) { - /* Remove us from channel */ - g_snprintf(buf, sizeof(buf), - _("You have been kicked off %s by %s (%s)"), - channel->channel_name, client_entry2->nickname, - tmp ? tmp : ""); - purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); - serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo))); - } else { - /* Remove user from channel */ - g_snprintf(buf, sizeof(buf), _("Kicked by %s (%s)"), - client_entry2->nickname, tmp ? tmp : ""); - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), - client_entry->nickname, - buf); - } - - break; - - case SILC_NOTIFY_TYPE_KILLED: - client_entry = va_arg(va, SilcClientEntry); - tmp = va_arg(va, char *); - idtype = va_arg(va, int); - entry = va_arg(va, SilcClientEntry); - - if (!client_entry->nickname) - break; - - if (client_entry == conn->local_entry) { - if (idtype == SILC_ID_CLIENT) { - client_entry2 = (SilcClientEntry)entry; - g_snprintf(buf, sizeof(buf), - _("You have been killed by %s (%s)"), - client_entry2->nickname, tmp ? tmp : ""); - } else if (idtype == SILC_ID_SERVER) { - server_entry = (SilcServerEntry)entry; - g_snprintf(buf, sizeof(buf), - _("You have been killed by %s (%s)"), - server_entry->server_name, tmp ? tmp : ""); - } else if (idtype == SILC_ID_CHANNEL) { - channel = (SilcChannelEntry)entry; - g_snprintf(buf, sizeof(buf), - _("You have been killed by %s (%s)"), - channel->channel_name, tmp ? tmp : ""); - } - - /* Remove us from all channels */ - silc_hash_table_list(client_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - chu->channel->channel_name, sg->account); - if (!convo) - continue; - purple_conv_chat_write(PURPLE_CONV_CHAT(convo), client_entry->nickname, - buf, PURPLE_MESSAGE_SYSTEM, time(NULL)); - serv_got_chat_left(gc, purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo))); - } - silc_hash_table_list_reset(&htl); - - } else { - if (idtype == SILC_ID_CLIENT) { - client_entry2 = (SilcClientEntry)entry; - g_snprintf(buf, sizeof(buf), - _("Killed by %s (%s)"), - client_entry2->nickname, tmp ? tmp : ""); - } else if (idtype == SILC_ID_SERVER) { - server_entry = (SilcServerEntry)entry; - g_snprintf(buf, sizeof(buf), - _("Killed by %s (%s)"), - server_entry->server_name, tmp ? tmp : ""); - } else if (idtype == SILC_ID_CHANNEL) { - channel = (SilcChannelEntry)entry; - g_snprintf(buf, sizeof(buf), - _("Killed by %s (%s)"), - channel->channel_name, tmp ? tmp : ""); - } - - /* Remove user from all channels */ - silc_hash_table_list(client_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - chu->channel->channel_name, sg->account); - if (!convo) - continue; - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), - client_entry->nickname, tmp); - } - silc_hash_table_list_reset(&htl); - } - - break; - - case SILC_NOTIFY_TYPE_CHANNEL_CHANGE: - break; - - case SILC_NOTIFY_TYPE_SERVER_SIGNOFF: - { - int i; - SilcClientEntry *clients; - SilcUInt32 clients_count; - - (void)va_arg(va, void *); - clients = va_arg(va, SilcClientEntry *); - clients_count = va_arg(va, SilcUInt32); - - for (i = 0; i < clients_count; i++) { - if (!clients[i]->nickname) - break; - - /* Remove from all channels */ - silc_hash_table_list(clients[i]->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - convo = - purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - chu->channel->channel_name, sg->account); - if (!convo) - continue; - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(convo), - clients[i]->nickname, - _("Server signoff")); - } - silc_hash_table_list_reset(&htl); - } - } - break; - - case SILC_NOTIFY_TYPE_ERROR: - { - SilcStatus error = va_arg(va, int); - purple_notify_error(gc, "Error Notify", - silc_get_status_message(error), - NULL); - } - break; - - case SILC_NOTIFY_TYPE_WATCH: - { - SilcPublicKey public_key; - unsigned char *pk; - SilcUInt32 pk_len; - char *fingerprint; - - client_entry = va_arg(va, SilcClientEntry); - (void)va_arg(va, char *); - mode = va_arg(va, SilcUInt32); - notify = va_arg(va, int); - public_key = va_arg(va, SilcPublicKey); - - b = NULL; - if (public_key) { - PurpleBlistNode *gnode, *cnode, *bnode; - const char *f; - - pk = silc_pkcs_public_key_encode(public_key, &pk_len); - if (!pk) - break; - fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - for (i = 0; i < strlen(fingerprint); i++) - if (fingerprint[i] == ' ') - fingerprint[i] = '_'; - g_snprintf(buf, sizeof(buf) - 1, - "%s" G_DIR_SEPARATOR_S "clientkeys" - G_DIR_SEPARATOR_S "clientkey_%s.pub", - silcpurple_silcdir(), fingerprint); - silc_free(fingerprint); - silc_free(pk); - - /* Find buddy by associated public key */ - for (gnode = purple_get_blist()->root; gnode; - gnode = gnode->next) { - if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) - continue; - for (cnode = gnode->child; cnode; cnode = cnode->next) { - if( !PURPLE_BLIST_NODE_IS_CONTACT(cnode)) - continue; - for (bnode = cnode->child; bnode; - bnode = bnode->next) { - if (!PURPLE_BLIST_NODE_IS_BUDDY(bnode)) - continue; - b = (PurpleBuddy *)bnode; - if (b->account != gc->account) - continue; - f = purple_blist_node_get_string(bnode, "public-key"); - if (f && purple_strequal(f, buf)) - goto cont; - b = NULL; - } - } - } - } - cont: - if (!b) { - /* Find buddy by nickname */ - b = purple_find_buddy(sg->account, client_entry->nickname); - if (!b) { - purple_debug_warning("silc", "WATCH for %s, unknown buddy\n", - client_entry->nickname); - break; - } - } - - silc_free(b->proto_data); - b->proto_data = silc_memdup(client_entry->id, - sizeof(*client_entry->id)); - if (notify == SILC_NOTIFY_TYPE_NICK_CHANGE) { - break; - } else if (notify == SILC_NOTIFY_TYPE_UMODE_CHANGE) { - /* See if client was away and is now present */ - if (!(mode & (SILC_UMODE_GONE | SILC_UMODE_INDISPOSED | - SILC_UMODE_BUSY | SILC_UMODE_PAGE | - SILC_UMODE_DETACHED)) && - (client_entry->mode & SILC_UMODE_GONE || - client_entry->mode & SILC_UMODE_INDISPOSED || - client_entry->mode & SILC_UMODE_BUSY || - client_entry->mode & SILC_UMODE_PAGE || - client_entry->mode & SILC_UMODE_DETACHED)) { - client_entry->mode = mode; - purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL); - } - else if ((mode & SILC_UMODE_GONE) || - (mode & SILC_UMODE_INDISPOSED) || - (mode & SILC_UMODE_BUSY) || - (mode & SILC_UMODE_PAGE) || - (mode & SILC_UMODE_DETACHED)) { - client_entry->mode = mode; - purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); - } - } else if (notify == SILC_NOTIFY_TYPE_SIGNOFF || - notify == SILC_NOTIFY_TYPE_SERVER_SIGNOFF || - notify == SILC_NOTIFY_TYPE_KILLED) { - client_entry->mode = mode; - purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_OFFLINE, NULL); - } else if (notify == SILC_NOTIFY_TYPE_NONE) { - client_entry->mode = mode; - purple_prpl_got_user_status(purple_buddy_get_account(b), purple_buddy_get_name(b), SILCPURPLE_STATUS_ID_AVAILABLE, NULL); - } - } - break; - - default: - purple_debug_info("silc", "Unhandled notification: %d\n", type); - break; - } - - va_end(va); -} - - -/* Command handler. This function is called always in the command function. - If error occurs it will be called as well. `conn' is the associated - client connection. `cmd_context' is the command context that was - originally sent to the command. `success' is FALSE if error occurred - during command. `command' is the command being processed. It must be - noted that this is not reply from server. This is merely called just - after application has called the command. Just to tell application - that the command really was processed. */ - -static void -silc_command(SilcClient client, SilcClientConnection conn, - SilcClientCommandContext cmd_context, bool success, - SilcCommand command, SilcStatus status) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - - switch (command) { - - case SILC_COMMAND_CMODE: - if (cmd_context->argc == 3 && - purple_strequal((char *)cmd_context->argv[2], "+C")) - sg->chpk = TRUE; - else - sg->chpk = FALSE; - break; - - default: - break; - } -} - -#if 0 -static void -silcpurple_whois_more(SilcClientEntry client_entry, gint id) -{ - SilcAttributePayload attr; - SilcAttribute attribute; - char *buf; - GString *s; - SilcVCardStruct vcard; - int i; - - if (id != 0) - return; - - memset(&vcard, 0, sizeof(vcard)); - - s = g_string_new(""); - - silc_dlist_start(client_entry->attrs); - while ((attr = silc_dlist_get(client_entry->attrs)) != SILC_LIST_END) { - attribute = silc_attribute_get_attribute(attr); - switch (attribute) { - - case SILC_ATTRIBUTE_USER_INFO: - if (!silc_attribute_get_object(attr, (void *)&vcard, - sizeof(vcard))) - continue; - g_string_append_printf(s, "%s:\n\n", _("Personal Information")); - if (vcard.full_name) - g_string_append_printf(s, "%s:\t\t%s\n", - _("Full Name"), - vcard.full_name); - if (vcard.first_name) - g_string_append_printf(s, "%s:\t%s\n", - _("First Name"), - vcard.first_name); - if (vcard.middle_names) - g_string_append_printf(s, "%s:\t%s\n", - _("Middle Name"), - vcard.middle_names); - if (vcard.family_name) - g_string_append_printf(s, "%s:\t%s\n", - _("Family Name"), - vcard.family_name); - if (vcard.nickname) - g_string_append_printf(s, "%s:\t\t%s\n", - _("Nickname"), - vcard.nickname); - if (vcard.bday) - g_string_append_printf(s, "%s:\t\t%s\n", - _("Birth Day"), - vcard.bday); - if (vcard.title) - g_string_append_printf(s, "%s:\t\t%s\n", - _("Job Title"), - vcard.title); - if (vcard.role) - g_string_append_printf(s, "%s:\t\t%s\n", - _("Job Role"), - vcard.role); - if (vcard.org_name) - g_string_append_printf(s, "%s:\t%s\n", - _("Organization"), - vcard.org_name); - if (vcard.org_unit) - g_string_append_printf(s, "%s:\t\t%s\n", - _("Unit"), - vcard.org_unit); - if (vcard.url) - g_string_append_printf(s, "%s:\t%s\n", - _("Homepage"), - vcard.url); - if (vcard.label) - g_string_append_printf(s, "%s:\t%s\n", - _("Address"), - vcard.label); - for (i = 0; i < vcard.num_tels; i++) { - if (vcard.tels[i].telnum) - g_string_append_printf(s, "%s:\t\t\t%s\n", - _("Phone"), - vcard.tels[i].telnum); - } - for (i = 0; i < vcard.num_emails; i++) { - if (vcard.emails[i].address) - g_string_append_printf(s, "%s:\t\t%s\n", - _("Email"), - vcard.emails[i].address); - } - if (vcard.note) - g_string_append_printf(s, "\n%s:\t\t%s\n", - _("Note"), - vcard.note); - break; - } - } - - buf = g_string_free(s, FALSE); - purple_notify_info(NULL, _("User Information"), _("User Information"), - buf); - g_free(buf); -} -#endif - -/* Command reply handler. This function is called always in the command reply - function. If error occurs it will be called as well. Normal scenario - is that it will be called after the received command data has been parsed - and processed. The function is used to pass the received command data to - the application. - - `conn' is the associated client connection. `cmd_payload' is the command - payload data received from server and it can be ignored. It is provided - if the application would like to re-parse the received command data, - however, it must be noted that the data is parsed already by the library - thus the payload can be ignored. `success' is FALSE if error occurred. - In this case arguments are not sent to the application. The `status' is - the command reply status server returned. The `command' is the command - reply being processed. The function has variable argument list and each - command defines the number and type of arguments it passes to the - application (on error they are not sent). */ - -static void -silc_command_reply(SilcClient client, SilcClientConnection conn, - SilcCommandPayload cmd_payload, bool success, - SilcCommand command, SilcStatus status, ...) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - PurpleConversation *convo; - va_list vp; - - va_start(vp, status); - - switch (command) { - case SILC_COMMAND_JOIN: - { - SilcChannelEntry channel_entry; - - if (!success) { - purple_notify_error(gc, _("Join Chat"), _("Cannot join channel"), - silc_get_status_message(status)); - return; - } - - (void)va_arg(vp, char *); - channel_entry = va_arg(vp, SilcChannelEntry); - - /* Resolve users on channel */ - silc_client_get_clients_by_channel(client, conn, channel_entry, - silcpurple_chat_join_done, - channel_entry); - } - break; - - case SILC_COMMAND_LEAVE: - break; - - case SILC_COMMAND_USERS: - break; - - case SILC_COMMAND_WHOIS: - { - SilcUInt32 idle, mode; - SilcBuffer channels, user_modes; - SilcClientEntry client_entry; - char tmp[1024], *tmp2; - char *moodstr, *statusstr, *contactstr, *langstr, *devicestr, *tzstr, *geostr; - PurpleNotifyUserInfo *user_info; - - if (!success) { - purple_notify_error(gc, _("User Information"), - _("Cannot get user information"), - silc_get_status_message(status)); - break; - } - - client_entry = va_arg(vp, SilcClientEntry); - if (!client_entry->nickname) - break; - (void)va_arg(vp, char *); - (void)va_arg(vp, char *); - (void)va_arg(vp, char *); - channels = va_arg(vp, SilcBuffer); - mode = va_arg(vp, SilcUInt32); - idle = va_arg(vp, SilcUInt32); - (void)va_arg(vp, unsigned char *); - user_modes = va_arg(vp, SilcBuffer); - - user_info = purple_notify_user_info_new(); - tmp2 = g_markup_escape_text(client_entry->nickname, -1); - purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp2); - g_free(tmp2); - if (client_entry->realname) { - tmp2 = g_markup_escape_text(client_entry->realname, -1); - purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp2); - g_free(tmp2); - } - if (client_entry->username) { - tmp2 = g_markup_escape_text(client_entry->username, -1); - if (client_entry->hostname) { - gchar *tmp3; - tmp3 = g_strdup_printf("%s@%s", tmp2, client_entry->hostname); - purple_notify_user_info_add_pair(user_info, _("Username"), tmp3); - g_free(tmp3); - } else - purple_notify_user_info_add_pair(user_info, _("Username"), tmp2); - g_free(tmp2); - } - - if (client_entry->mode) { - memset(tmp, 0, sizeof(tmp)); - silcpurple_get_umode_string(client_entry->mode, - tmp, sizeof(tmp) - strlen(tmp)); - purple_notify_user_info_add_pair(user_info, _("User Modes"), tmp); - } - - silcpurple_parse_attrs(client_entry->attrs, &moodstr, &statusstr, &contactstr, &langstr, &devicestr, &tzstr, &geostr); - if (moodstr) { - purple_notify_user_info_add_pair(user_info, _("Mood"), moodstr); - g_free(moodstr); - } - - if (statusstr) { - tmp2 = g_markup_escape_text(statusstr, -1); - purple_notify_user_info_add_pair(user_info, _("Status Text"), tmp2); - g_free(statusstr); - g_free(tmp2); - } - - if (contactstr) { - purple_notify_user_info_add_pair(user_info, _("Preferred Contact"), contactstr); - g_free(contactstr); - } - - if (langstr) { - purple_notify_user_info_add_pair(user_info, _("Preferred Language"), langstr); - g_free(langstr); - } - - if (devicestr) { - purple_notify_user_info_add_pair(user_info, _("Device"), devicestr); - g_free(devicestr); - } - - if (tzstr) { - purple_notify_user_info_add_pair(user_info, _("Timezone"), tzstr); - g_free(tzstr); - } - - if (geostr) { - purple_notify_user_info_add_pair(user_info, _("Geolocation"), geostr); - g_free(geostr); - } - - if (client_entry->server) - purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server); - - if (channels && user_modes) { - SilcUInt32 *umodes; - SilcDList list = - silc_channel_payload_parse_list(channels->data, - channels->len); - if (list && silc_get_mode_list(user_modes, - silc_dlist_count(list), - &umodes)) { - SilcChannelPayload entry; - int i = 0; - - memset(tmp, 0, sizeof(tmp)); - silc_dlist_start(list); - while ((entry = silc_dlist_get(list)) - != SILC_LIST_END) { - SilcUInt32 name_len; - char *m = silc_client_chumode_char(umodes[i++]); - char *name = (char *)silc_channel_get_name(entry, &name_len); - if (m) - silc_strncat(tmp, sizeof(tmp) - 1, m, strlen(m)); - silc_strncat(tmp, sizeof(tmp) - 1, name, name_len); - silc_strncat(tmp, sizeof(tmp) - 1, " ", 1); - silc_free(m); - - } - tmp2 = g_markup_escape_text(tmp, -1); - purple_notify_user_info_add_pair(user_info, _("Currently on"), tmp2); - g_free(tmp2); - silc_free(umodes); - } - } - - if (client_entry->public_key) { - char *fingerprint, *babbleprint; - unsigned char *pk; - SilcUInt32 pk_len; - pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len); - fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); - purple_notify_user_info_add_pair(user_info, _("Public Key Fingerprint"), fingerprint); - purple_notify_user_info_add_pair(user_info, _("Public Key Babbleprint"), babbleprint); - silc_free(fingerprint); - silc_free(babbleprint); - silc_free(pk); - } - -#if 0 /* XXX for now, let's not show attrs here */ - if (client_entry->attrs) - purple_request_action(gc, _("User Information"), - _("User Information"), - buf, 1, client_entry, 2, - _("OK"), G_CALLBACK(silcpurple_whois_more), - _("_More..."), G_CALLBACK(silcpurple_whois_more), gc->account, NULL, NULL); - else -#endif - purple_notify_userinfo(gc, client_entry->nickname, user_info, NULL, NULL); - purple_notify_user_info_destroy(user_info); - } - break; - - case SILC_COMMAND_WHOWAS: - { - SilcClientEntry client_entry; - char *nickname, *realname, *username, *tmp; - PurpleNotifyUserInfo *user_info; - - if (!success) { - purple_notify_error(gc, _("User Information"), - _("Cannot get user information"), - silc_get_status_message(status)); - break; - } - - client_entry = va_arg(vp, SilcClientEntry); - nickname = va_arg(vp, char *); - username = va_arg(vp, char *); - realname = va_arg(vp, char *); - if (!nickname) - break; - - user_info = purple_notify_user_info_new(); - tmp = g_markup_escape_text(nickname, -1); - purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp); - g_free(tmp); - if (realname) { - tmp = g_markup_escape_text(realname, -1); - purple_notify_user_info_add_pair(user_info, _("Real Name"), tmp); - g_free(tmp); - } - if (username) { - tmp = g_markup_escape_text(username, -1); - if (client_entry && client_entry->hostname) { - gchar *tmp3; - tmp3 = g_strdup_printf("%s@%s", tmp, client_entry->hostname); - purple_notify_user_info_add_pair(user_info, _("Username"), tmp3); - g_free(tmp3); - } else - purple_notify_user_info_add_pair(user_info, _("Username"), tmp); - g_free(tmp); - } - if (client_entry && client_entry->server) - purple_notify_user_info_add_pair(user_info, _("Server"), client_entry->server); - - - if (client_entry && client_entry->public_key) { - char *fingerprint, *babbleprint; - unsigned char *pk; - SilcUInt32 pk_len; - pk = silc_pkcs_public_key_encode(client_entry->public_key, &pk_len); - fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); - purple_notify_user_info_add_pair(user_info, _("Public Key Fingerprint"), fingerprint); - purple_notify_user_info_add_pair(user_info, _("Public Key Babbleprint"), babbleprint); - silc_free(fingerprint); - silc_free(babbleprint); - silc_free(pk); - } - - purple_notify_userinfo(gc, nickname, user_info, NULL, NULL); - purple_notify_user_info_destroy(user_info); - } - break; - - case SILC_COMMAND_DETACH: - if (!success) { - purple_notify_error(gc, _("Detach From Server"), _("Cannot detach"), - silc_get_status_message(status)); - return; - } - break; - - case SILC_COMMAND_TOPIC: - { - SilcChannelEntry channel; - - if (!success) { - purple_notify_error(gc, _("Topic"), _("Cannot set topic"), - silc_get_status_message(status)); - return; - } - - channel = va_arg(vp, SilcChannelEntry); - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - channel->channel_name, sg->account); - if (!convo) { - purple_debug_error("silc", "Got a topic for %s, which doesn't exist\n", - channel->channel_name); - break; - } - - /* Set topic */ - if (channel->topic) - purple_conv_chat_set_topic(PURPLE_CONV_CHAT(convo), NULL, channel->topic); - } - break; - - case SILC_COMMAND_NICK: - { - /* I don't think we should need to do this because the server should - * be sending a SILC_NOTIFY_TYPE_NICK_CHANGE when we change our own - * nick, but it isn't, so we deal with it here instead. Stu. */ - SilcClientEntry local_entry; - SilcHashTableList htl; - SilcChannelUser chu; - const char *oldnick; - - if (!success) { - purple_notify_error(gc, _("Nick"), _("Failed to change nickname"), - silc_get_status_message(status)); - return; - } - - local_entry = va_arg(vp, SilcClientEntry); - - /* Change nick on all channels */ - silc_hash_table_list(local_entry->channels, &htl); - while (silc_hash_table_get(&htl, NULL, (void *)&chu)) { - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - chu->channel->channel_name, sg->account); - if (!convo) - continue; - oldnick = purple_conv_chat_get_nick(PURPLE_CONV_CHAT(convo)); - if (!purple_strequal(oldnick, purple_normalize(purple_conversation_get_account(convo), local_entry->nickname))) { - purple_conv_chat_rename_user(PURPLE_CONV_CHAT(convo), - oldnick, local_entry->nickname); - purple_conv_chat_set_nick(PURPLE_CONV_CHAT(convo), local_entry->nickname); - } - } - silc_hash_table_list_reset(&htl); - - purple_connection_set_display_name(gc, local_entry->nickname); - } - break; - - case SILC_COMMAND_LIST: - { - char *topic, *name; - int usercount; - PurpleRoomlistRoom *room; - - if (sg->roomlist_cancelled) - break; - - if (!success) { - purple_notify_error(gc, _("Error"), _("Error retrieving room list"), - silc_get_status_message(status)); - purple_roomlist_set_in_progress(sg->roomlist, FALSE); - purple_roomlist_unref(sg->roomlist); - sg->roomlist = NULL; - return; - } - - (void)va_arg(vp, SilcChannelEntry); - name = va_arg(vp, char *); - if (!name) { - purple_notify_error(gc, _("Roomlist"), _("Cannot get room list"), - silc_get_status_message(status)); - purple_roomlist_set_in_progress(sg->roomlist, FALSE); - purple_roomlist_unref(sg->roomlist); - sg->roomlist = NULL; - return; - } - topic = va_arg(vp, char *); - usercount = va_arg(vp, int); - - room = purple_roomlist_room_new(PURPLE_ROOMLIST_ROOMTYPE_ROOM, name, NULL); - purple_roomlist_room_add_field(sg->roomlist, room, name); - purple_roomlist_room_add_field(sg->roomlist, room, - SILC_32_TO_PTR(usercount)); - purple_roomlist_room_add_field(sg->roomlist, room, - topic ? topic : ""); - purple_roomlist_room_add(sg->roomlist, room); - - if (status == SILC_STATUS_LIST_END || - status == SILC_STATUS_OK) { - purple_roomlist_set_in_progress(sg->roomlist, FALSE); - purple_roomlist_unref(sg->roomlist); - sg->roomlist = NULL; - } - } - break; - - case SILC_COMMAND_GETKEY: - { - SilcPublicKey public_key; - - if (!success) { - purple_notify_error(gc, _("Get Public Key"), - _("Cannot fetch the public key"), - silc_get_status_message(status)); - return; - } - - (void)va_arg(vp, SilcUInt32); - (void)va_arg(vp, void *); - public_key = va_arg(vp, SilcPublicKey); - - if (!public_key) - purple_notify_error(gc, _("Get Public Key"), - _("Cannot fetch the public key"), - _("No public key was received")); - } - break; - - case SILC_COMMAND_INFO: - { - - char *server_name; - char *server_info; - char tmp[256]; - - if (!success) { - purple_notify_error(gc, _("Server Information"), - _("Cannot get server information"), - silc_get_status_message(status)); - return; - } - - (void)va_arg(vp, SilcServerEntry); - server_name = va_arg(vp, char *); - server_info = va_arg(vp, char *); - - if (server_name && server_info) { - g_snprintf(tmp, sizeof(tmp), "Server: %s\n%s", - server_name, server_info); - purple_notify_info(gc, NULL, _("Server Information"), tmp); - } - } - break; - - case SILC_COMMAND_STATS: - { - SilcUInt32 starttime, uptime, my_clients, my_channels, my_server_ops, - my_router_ops, cell_clients, cell_channels, cell_servers, - clients, channels, servers, routers, server_ops, router_ops; - SilcUInt32 buffer_length; - SilcBufferStruct buf; - - unsigned char *server_stats; - char *msg; - - if (!success) { - purple_notify_error(gc, _("Server Statistics"), - _("Cannot get server statistics"), - silc_get_status_message(status)); - return; - } - - server_stats = va_arg(vp, unsigned char *); - buffer_length = va_arg(vp, SilcUInt32); - if (!server_stats || !buffer_length) { - purple_notify_error(gc, _("Server Statistics"), - _("No server statistics available"), NULL); - break; - } - silc_buffer_set(&buf, server_stats, buffer_length); - silc_buffer_unformat(&buf, - SILC_STR_UI_INT(&starttime), - SILC_STR_UI_INT(&uptime), - SILC_STR_UI_INT(&my_clients), - SILC_STR_UI_INT(&my_channels), - SILC_STR_UI_INT(&my_server_ops), - SILC_STR_UI_INT(&my_router_ops), - SILC_STR_UI_INT(&cell_clients), - SILC_STR_UI_INT(&cell_channels), - SILC_STR_UI_INT(&cell_servers), - SILC_STR_UI_INT(&clients), - SILC_STR_UI_INT(&channels), - SILC_STR_UI_INT(&servers), - SILC_STR_UI_INT(&routers), - SILC_STR_UI_INT(&server_ops), - SILC_STR_UI_INT(&router_ops), - SILC_STR_END); - - msg = g_strdup_printf(_("Local server start time: %s\n" - "Local server uptime: %s\n" - "Local server clients: %d\n" - "Local server channels: %d\n" - "Local server operators: %d\n" - "Local router operators: %d\n" - "Local cell clients: %d\n" - "Local cell channels: %d\n" - "Local cell servers: %d\n" - "Total clients: %d\n" - "Total channels: %d\n" - "Total servers: %d\n" - "Total routers: %d\n" - "Total server operators: %d\n" - "Total router operators: %d\n"), - silc_get_time(starttime), - purple_str_seconds_to_string((int)uptime), - (int)my_clients, (int)my_channels, (int)my_server_ops, (int)my_router_ops, - (int)cell_clients, (int)cell_channels, (int)cell_servers, - (int)clients, (int)channels, (int)servers, (int)routers, - (int)server_ops, (int)router_ops); - - purple_notify_info(gc, NULL, - _("Network Statistics"), msg); - g_free(msg); - } - break; - - case SILC_COMMAND_PING: - { - if (!success) { - purple_notify_error(gc, _("Ping"), _("Ping failed"), - silc_get_status_message(status)); - return; - } - - purple_notify_info(gc, _("Ping"), _("Ping reply received from server"), - NULL); - } - break; - - case SILC_COMMAND_KILL: - if (!success) { - purple_notify_error(gc, _("Kill User"), - _("Could not kill user"), - silc_get_status_message(status)); - return; - } - break; - - case SILC_COMMAND_CMODE: - { - SilcChannelEntry channel_entry; - SilcBuffer channel_pubkeys; - - if (!success) - return; - - channel_entry = va_arg(vp, SilcChannelEntry); - (void)va_arg(vp, SilcUInt32); - (void)va_arg(vp, SilcPublicKey); - channel_pubkeys = va_arg(vp, SilcBuffer); - - if (sg->chpk) - silcpurple_chat_chauth_show(sg, channel_entry, channel_pubkeys); - } - break; - - default: - if (success) - purple_debug_info("silc", "Unhandled command: %d (succeeded)\n", command); - else - purple_debug_info("silc", "Unhandled command: %d (failed: %s)\n", command, - silc_get_status_message(status)); - break; - } - - va_end(vp); -} - - -/* Called to indicate that connection was either successfully established - or connecting failed. This is also the first time application receives - the SilcClientConnection object which it should save somewhere. - If the `success' is FALSE the application must always call the function - silc_client_close_connection. */ - -static void -silc_connected(SilcClient client, SilcClientConnection conn, - SilcClientConnectionStatus status) -{ - PurpleConnection *gc = client->application; - SilcPurple sg; - - if (gc == NULL) { - silc_client_close_connection(client, conn); - return; - } - sg = gc->proto_data; - - switch (status) { - case SILC_CLIENT_CONN_SUCCESS: - case SILC_CLIENT_CONN_SUCCESS_RESUME: - purple_connection_set_state(gc, PURPLE_CONNECTED); - - /* Send the server our buddy list */ - silcpurple_send_buddylist(gc); - - g_unlink(silcpurple_session_file(purple_account_get_username(sg->account))); - - /* Send any UMODEs configured for account */ - if (purple_account_get_bool(sg->account, "block-ims", FALSE)) { - silc_client_command_call(sg->client, sg->conn, NULL, - "UMODE", "+P", NULL); - } - - return; - break; - case SILC_CLIENT_CONN_ERROR: - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Error during connecting to SILC Server")); - g_unlink(silcpurple_session_file(purple_account_get_username(sg->account))); - break; - - case SILC_CLIENT_CONN_ERROR_KE: - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_ENCRYPTION_ERROR, - _("Key Exchange failed")); - break; - - case SILC_CLIENT_CONN_ERROR_AUTH: - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, - _("Authentication failed")); - break; - - case SILC_CLIENT_CONN_ERROR_RESUME: - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("Resuming detached session failed. " - "Press Reconnect to create new connection.")); - g_unlink(silcpurple_session_file(purple_account_get_username(sg->account))); - break; - - case SILC_CLIENT_CONN_ERROR_TIMEOUT: - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Connection timed out")); - break; - } - - /* Error */ - sg->conn = NULL; - silc_client_close_connection(client, conn); -} - - -/* Called to indicate that connection was disconnected to the server. - The `status' may tell the reason of the disconnection, and if the - `message' is non-NULL it may include the disconnection message - received from server. */ - -static void -silc_disconnected(SilcClient client, SilcClientConnection conn, - SilcStatus status, const char *message) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - - if (sg->resuming && !sg->detaching) - g_unlink(silcpurple_session_file(purple_account_get_username(sg->account))); - - sg->conn = NULL; - - /* Close the connection */ - if (!sg->detaching) - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Disconnected by server")); - else - /* TODO: Does this work correctly? Maybe we need to set wants_to_die? */ - purple_account_disconnect(purple_connection_get_account(gc)); -} - - -typedef struct { - SilcGetAuthMeth completion; - void *context; -} *SilcPurpleGetAuthMethod; - -/* Callback called when we've received the authentication method information - from the server after we've requested it. */ - -static void silc_get_auth_method_callback(SilcClient client, - SilcClientConnection conn, - SilcAuthMethod auth_meth, - void *context) -{ - SilcPurpleGetAuthMethod internal = context; - - switch (auth_meth) { - case SILC_AUTH_NONE: - /* No authentication required. */ - (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context); - break; - - case SILC_AUTH_PASSWORD: - /* By returning NULL here the library will ask the passphrase from us - by calling the silc_ask_passphrase. */ - (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context); - break; - - case SILC_AUTH_PUBLIC_KEY: - /* Do not get the authentication data now, the library will generate - it using our default key, if we do not provide it here. */ - (*internal->completion)(TRUE, auth_meth, NULL, 0, internal->context); - break; - } - - silc_free(internal); -} - -/* Find authentication method and authentication data by hostname and - port. The hostname may be IP address as well. When the authentication - method has been resolved the `completion' callback with the found - authentication method and authentication data is called. The `conn' - may be NULL. */ - -static void -silc_get_auth_method(SilcClient client, SilcClientConnection conn, - char *hostname, SilcUInt16 port, - SilcGetAuthMeth completion, void *context) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - SilcPurpleGetAuthMethod internal; - const char *password; - - /* Progress */ - if (sg->resuming) - purple_connection_update_progress(gc, _("Resuming session"), 4, 5); - else - purple_connection_update_progress(gc, _("Authenticating connection"), 4, 5); - - /* Check configuration if we have this connection configured. If we - have then return that data immediately, as it's faster way. */ - if (purple_account_get_bool(sg->account, "pubkey-auth", FALSE)) { - completion(TRUE, SILC_AUTH_PUBLIC_KEY, NULL, 0, context); - return; - } - password = purple_connection_get_password(gc); - if (password && *password) { - completion(TRUE, SILC_AUTH_PASSWORD, (unsigned char *)password, strlen(password), context); - return; - } - - /* Resolve the authentication method from server, as we may not know it. */ - internal = silc_calloc(1, sizeof(*internal)); - if (!internal) - return; - internal->completion = completion; - internal->context = context; - silc_client_request_authentication_method(client, conn, - silc_get_auth_method_callback, - internal); -} - - -/* Verifies received public key. The `conn_type' indicates which entity - (server, client etc.) has sent the public key. If user decides to trust - the application may save the key as trusted public key for later - use. The `completion' must be called after the public key has been - verified. */ - -static void -silc_verify_public_key(SilcClient client, SilcClientConnection conn, - SilcSocketType conn_type, unsigned char *pk, - SilcUInt32 pk_len, SilcSKEPKType pk_type, - SilcVerifyPublicKey completion, void *context) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - - if (!sg->conn && (conn_type == SILC_SOCKET_TYPE_SERVER || - conn_type == SILC_SOCKET_TYPE_ROUTER)) { - /* Progress */ - if (sg->resuming) - purple_connection_update_progress(gc, _("Resuming session"), 3, 5); - else - purple_connection_update_progress(gc, _("Verifying server public key"), - 3, 5); - } - - /* Verify public key */ - silcpurple_verify_public_key(client, conn, NULL, conn_type, pk, - pk_len, pk_type, completion, context); -} - -typedef struct { - SilcAskPassphrase completion; - void *context; -} *SilcPurpleAskPassphrase; - -static void -silc_ask_passphrase_cb(SilcPurpleAskPassphrase internal, const char *passphrase) -{ - if (!passphrase || !(*passphrase)) - internal->completion(NULL, 0, internal->context); - else - internal->completion((unsigned char *)passphrase, - strlen(passphrase), internal->context); - silc_free(internal); -} - -/* Ask (interact, that is) a passphrase from user. The passphrase is - returned to the library by calling the `completion' callback with - the `context'. The returned passphrase SHOULD be in UTF-8 encoded, - if not then the library will attempt to encode. */ - -static void -silc_ask_passphrase(SilcClient client, SilcClientConnection conn, - SilcAskPassphrase completion, void *context) -{ - PurpleConnection *gc = client->application; - SilcPurpleAskPassphrase internal = silc_calloc(1, sizeof(*internal)); - - if (!internal) - return; - internal->completion = completion; - internal->context = context; - purple_request_input(gc, _("Passphrase"), NULL, - _("Passphrase required"), NULL, FALSE, TRUE, NULL, - _("OK"), G_CALLBACK(silc_ask_passphrase_cb), - _("Cancel"), G_CALLBACK(silc_ask_passphrase_cb), - purple_connection_get_account(gc), NULL, NULL, internal); -} - - -/* Notifies application that failure packet was received. This is called - if there is some protocol active in the client. The `protocol' is the - protocol context. The `failure' is opaque pointer to the failure - indication. Note, that the `failure' is protocol dependant and - application must explicitly cast it to correct type. Usually `failure' - is 32 bit failure type (see protocol specs for all protocol failure - types). */ - -static void -silc_failure(SilcClient client, SilcClientConnection conn, - SilcProtocol protocol, void *failure) -{ - PurpleConnection *gc = client->application; - char buf[128]; - - memset(buf, 0, sizeof(buf)); - - if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_KEY_EXCHANGE) { - SilcSKEStatus status = (SilcSKEStatus)SILC_PTR_TO_32(failure); - - if (status == SILC_SKE_STATUS_BAD_VERSION) - g_snprintf(buf, sizeof(buf), - _("Failure: Version mismatch, upgrade your client")); - if (status == SILC_SKE_STATUS_UNSUPPORTED_PUBLIC_KEY) - g_snprintf(buf, sizeof(buf), - _("Failure: Remote does not trust/support your public key")); - if (status == SILC_SKE_STATUS_UNKNOWN_GROUP) - g_snprintf(buf, sizeof(buf), - _("Failure: Remote does not support proposed KE group")); - if (status == SILC_SKE_STATUS_UNKNOWN_CIPHER) - g_snprintf(buf, sizeof(buf), - _("Failure: Remote does not support proposed cipher")); - if (status == SILC_SKE_STATUS_UNKNOWN_PKCS) - g_snprintf(buf, sizeof(buf), - _("Failure: Remote does not support proposed PKCS")); - if (status == SILC_SKE_STATUS_UNKNOWN_HASH_FUNCTION) - g_snprintf(buf, sizeof(buf), - _("Failure: Remote does not support proposed hash function")); - if (status == SILC_SKE_STATUS_UNKNOWN_HMAC) - g_snprintf(buf, sizeof(buf), - _("Failure: Remote does not support proposed HMAC")); - if (status == SILC_SKE_STATUS_INCORRECT_SIGNATURE) - g_snprintf(buf, sizeof(buf), _("Failure: Incorrect signature")); - if (status == SILC_SKE_STATUS_INVALID_COOKIE) - g_snprintf(buf, sizeof(buf), _("Failure: Invalid cookie")); - - /* Show the error on the progress bar. A more generic error message - is going to be showed to user after this in the silc_connected. */ - purple_connection_update_progress(gc, buf, 2, 5); - } - - if (protocol->protocol->type == SILC_PROTOCOL_CLIENT_CONNECTION_AUTH) { - SilcUInt32 err = SILC_PTR_TO_32(failure); - - if (err == SILC_AUTH_FAILED) - g_snprintf(buf, sizeof(buf), _("Failure: Authentication failed")); - - /* Show the error on the progress bar. A more generic error message - is going to be showed to user after this in the silc_connected. */ - purple_connection_update_progress(gc, buf, 4, 5); - } -} - -/* Asks whether the user would like to perform the key agreement protocol. - This is called after we have received an key agreement packet or an - reply to our key agreement packet. This returns TRUE if the user wants - the library to perform the key agreement protocol and FALSE if it is not - desired (application may start it later by calling the function - silc_client_perform_key_agreement). If TRUE is returned also the - `completion' and `context' arguments must be set by the application. */ - -static bool -silc_key_agreement(SilcClient client, SilcClientConnection conn, - SilcClientEntry client_entry, const char *hostname, - SilcUInt16 port, SilcKeyAgreementCallback *completion, - void **context) -{ - silcpurple_buddy_keyagr_request(client, conn, client_entry, hostname, port); - *completion = NULL; - *context = NULL; - return FALSE; -} - - -/* Notifies application that file transfer protocol session is being - requested by the remote client indicated by the `client_entry' from - the `hostname' and `port'. The `session_id' is the file transfer - session and it can be used to either accept or reject the file - transfer request, by calling the silc_client_file_receive or - silc_client_file_close, respectively. */ - -static void -silc_ftp(SilcClient client, SilcClientConnection conn, - SilcClientEntry client_entry, SilcUInt32 session_id, - const char *hostname, SilcUInt16 port) -{ - silcpurple_ftp_request(client, conn, client_entry, session_id, - hostname, port); -} - - -/* Delivers SILC session detachment data indicated by `detach_data' to the - application. If application has issued SILC_COMMAND_DETACH command - the client session in the SILC network is not quit. The client remains - in the network but is detached. The detachment data may be used later - to resume the session in the SILC Network. The appliation is - responsible of saving the `detach_data', to for example in a file. - - The detachment data can be given as argument to the functions - silc_client_connect_to_server, or silc_client_add_connection when - creating connection to remote server, inside SilcClientConnectionParams - structure. If it is provided the client library will attempt to resume - the session in the network. After the connection is created - successfully, the application is responsible of setting the user - interface for user into the same state it was before detaching (showing - same channels, channel modes, etc). It can do this by fetching the - information (like joined channels) from the client library. */ - -static void -silc_detach(SilcClient client, SilcClientConnection conn, - const unsigned char *detach_data, SilcUInt32 detach_data_len) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - const char *file; - - /* Save the detachment data to file. */ - file = silcpurple_session_file(purple_account_get_username(sg->account)); - g_unlink(file); - silc_file_writefile(file, (char *)detach_data, detach_data_len); -} - -SilcClientOperations ops = { - silc_say, - silc_channel_message, - silc_private_message, - silc_notify, - silc_command, - silc_command_reply, - silc_connected, - silc_disconnected, - silc_get_auth_method, - silc_verify_public_key, - silc_ask_passphrase, - silc_failure, - silc_key_agreement, - silc_ftp, - silc_detach -}; diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc10/pk.c --- a/libpurple/protocols/silc10/pk.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,274 +0,0 @@ -/* - - silcpurple_pk.c - - Author: Pekka Riikonen - - Copyright (C) 2004 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#include "silcincludes.h" -#include "silcclient.h" -#include "silcpurple.h" - -/************************* Public Key Verification ***************************/ - -typedef struct { - SilcClient client; - SilcClientConnection conn; - char *filename; - char *entity; - char *entity_name; - char *fingerprint; - char *babbleprint; - unsigned char *pk; - SilcUInt32 pk_len; - SilcSKEPKType pk_type; - SilcVerifyPublicKey completion; - void *context; - gboolean changed; -} *PublicKeyVerify; - -static void silcpurple_verify_ask(const char *entity, - const char *fingerprint, - const char *babbleprint, - PublicKeyVerify verify); - -static void silcpurple_verify_cb(PublicKeyVerify verify, gint id) -{ - if (id != 2) { - if (verify->completion) - verify->completion(FALSE, verify->context); - } else { - if (verify->completion) - verify->completion(TRUE, verify->context); - - /* Save the key for future checking */ - silc_pkcs_save_public_key_data(verify->filename, verify->pk, - verify->pk_len, SILC_PKCS_FILE_PEM); - } - - silc_free(verify->filename); - silc_free(verify->entity); - silc_free(verify->entity_name); - silc_free(verify->fingerprint); - silc_free(verify->babbleprint); - silc_free(verify->pk); - silc_free(verify); -} - -static void silcpurple_verify_details_cb(PublicKeyVerify verify) -{ - /* What a hack. We have to display the accept dialog _again_ - because Purple closes the dialog after you press the button. Purple - should have option for the dialogs whether the buttons close them - or not. */ - silcpurple_verify_ask(verify->entity, verify->fingerprint, - verify->babbleprint, verify); -} - -static void silcpurple_verify_details(PublicKeyVerify verify, gint id) -{ - SilcPublicKey public_key; - PurpleConnection *gc = verify->client->application; - SilcPurple sg = gc->proto_data; - - silc_pkcs_public_key_decode(verify->pk, verify->pk_len, - &public_key); - silcpurple_show_public_key(sg, verify->entity_name, public_key, - G_CALLBACK(silcpurple_verify_details_cb), - verify); - silc_pkcs_public_key_free(public_key); -} - -static void silcpurple_verify_ask(const char *entity, - const char *fingerprint, - const char *babbleprint, - PublicKeyVerify verify) -{ - PurpleConnection *gc = verify->client->application; - char tmp[256], tmp2[256]; - - if (verify->changed) { - g_snprintf(tmp, sizeof(tmp), - _("Received %s's public key. Your local copy does not match this " - "key. Would you still like to accept this public key?"), - entity); - } else { - g_snprintf(tmp, sizeof(tmp), - _("Received %s's public key. Would you like to accept this " - "public key?"), entity); - } - g_snprintf(tmp2, sizeof(tmp2), - _("Fingerprint and babbleprint for the %s key are:\n\n" - "%s\n%s\n"), entity, fingerprint, babbleprint); - - purple_request_action(gc, _("Verify Public Key"), tmp, tmp2, - PURPLE_DEFAULT_ACTION_NONE, - purple_connection_get_account(gc), entity, NULL, verify, 3, - _("Yes"), G_CALLBACK(silcpurple_verify_cb), - _("No"), G_CALLBACK(silcpurple_verify_cb), - _("_View..."), G_CALLBACK(silcpurple_verify_details)); -} - -void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn, - const char *name, SilcSocketType conn_type, - unsigned char *pk, SilcUInt32 pk_len, - SilcSKEPKType pk_type, - SilcVerifyPublicKey completion, void *context) -{ - PurpleConnection *gc = client->application; - int i; - char file[256], filename[256], filename2[256], *ipf, *hostf = NULL; - char *fingerprint, *babbleprint; - struct passwd *pw; - struct stat st; - char *entity = ((conn_type == SILC_SOCKET_TYPE_SERVER || - conn_type == SILC_SOCKET_TYPE_ROUTER) ? - "server" : "client"); - PublicKeyVerify verify; - - if (pk_type != SILC_SKE_PK_TYPE_SILC) { - purple_notify_error(gc, _("Verify Public Key"), - _("Unsupported public key type"), NULL); - if (completion) - completion(FALSE, context); - return; - } - - pw = getpwuid(getuid()); - if (!pw) { - if (completion) - completion(FALSE, context); - return; - } - - memset(filename, 0, sizeof(filename)); - memset(filename2, 0, sizeof(filename2)); - memset(file, 0, sizeof(file)); - - if (conn_type == SILC_SOCKET_TYPE_SERVER || - conn_type == SILC_SOCKET_TYPE_ROUTER) { - if (!name) { - g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, - conn->sock->ip, conn->sock->port); - g_snprintf(filename, sizeof(filename) - 1, - "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", - silcpurple_silcdir(), entity, file); - - g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, - conn->sock->hostname, conn->sock->port); - g_snprintf(filename2, sizeof(filename2) - 1, - "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", - silcpurple_silcdir(), entity, file); - - ipf = filename; - hostf = filename2; - } else { - g_snprintf(file, sizeof(file) - 1, "%skey_%s_%d.pub", entity, - name, conn->sock->port); - g_snprintf(filename, sizeof(filename) - 1, - "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", - silcpurple_silcdir(), entity, file); - - ipf = filename; - } - } else { - /* Replace all whitespaces with `_'. */ - fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - for (i = 0; i < strlen(fingerprint); i++) - if (fingerprint[i] == ' ') - fingerprint[i] = '_'; - - g_snprintf(file, sizeof(file) - 1, "%skey_%s.pub", entity, fingerprint); - g_snprintf(filename, sizeof(filename) - 1, - "%s" G_DIR_SEPARATOR_S "%skeys" G_DIR_SEPARATOR_S "%s", - silcpurple_silcdir(), entity, file); - silc_free(fingerprint); - - ipf = filename; - } - - verify = silc_calloc(1, sizeof(*verify)); - if (!verify) - return; - verify->client = client; - verify->conn = conn; - verify->filename = strdup(ipf); - verify->entity = strdup(entity); - verify->entity_name = (conn_type != SILC_SOCKET_TYPE_CLIENT ? - (name ? strdup(name) : strdup(conn->sock->hostname)) - : NULL); - verify->pk = silc_memdup(pk, pk_len); - verify->pk_len = pk_len; - verify->pk_type = pk_type; - verify->completion = completion; - verify->context = context; - fingerprint = verify->fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - babbleprint = verify->babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); - - /* Check whether this key already exists */ - if (g_stat(ipf, &st) < 0 && (!hostf || g_stat(hostf, &st) < 0)) { - /* Key does not exist, ask user to verify the key and save it */ - silcpurple_verify_ask(name ? name : entity, - fingerprint, babbleprint, verify); - return; - } else { - /* The key already exists, verify it. */ - SilcPublicKey public_key; - unsigned char *encpk; - SilcUInt32 encpk_len; - - /* Load the key file, try for both IP filename and hostname filename */ - if (!silc_pkcs_load_public_key(ipf, &public_key, - SILC_PKCS_FILE_PEM) && - !silc_pkcs_load_public_key(ipf, &public_key, - SILC_PKCS_FILE_BIN) && - (!hostf || (!silc_pkcs_load_public_key(hostf, &public_key, - SILC_PKCS_FILE_PEM) && - !silc_pkcs_load_public_key(hostf, &public_key, - SILC_PKCS_FILE_BIN)))) { - silcpurple_verify_ask(name ? name : entity, - fingerprint, babbleprint, verify); - return; - } - - /* Encode the key data */ - encpk = silc_pkcs_public_key_encode(public_key, &encpk_len); - if (!encpk) { - silcpurple_verify_ask(name ? name : entity, - fingerprint, babbleprint, verify); - return; - } - - /* Compare the keys */ - if (memcmp(encpk, pk, encpk_len)) { - /* Ask user to verify the key and save it */ - verify->changed = TRUE; - silcpurple_verify_ask(name ? name : entity, - fingerprint, babbleprint, verify); - return; - } - - /* Local copy matched */ - if (completion) - completion(TRUE, context); - silc_free(verify->filename); - silc_free(verify->entity); - silc_free(verify->entity_name); - silc_free(verify->pk); - silc_free(verify->fingerprint); - silc_free(verify->babbleprint); - silc_free(verify); - } -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc10/silc.c --- a/libpurple/protocols/silc10/silc.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1968 +0,0 @@ -/* - - silcpurple.c - - Author: Pekka Riikonen - - Copyright (C) 2004 - 2005 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#include "silcincludes.h" -#include "silcclient.h" -#include "silcpurple.h" -#include "version.h" -#include "wb.h" -#include "core.h" - -extern SilcClientOperations ops; -static PurplePlugin *silc_plugin = NULL; - -static const char * -silcpurple_list_icon(PurpleAccount *a, PurpleBuddy *b) -{ - return (const char *)"silc"; -} - -static GList * -silcpurple_away_states(PurpleAccount *account) -{ - PurpleStatusType *type; - GList *types = NULL; - - type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_AVAILABLE, NULL, TRUE, TRUE, FALSE); - types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, SILCPURPLE_STATUS_ID_HYPER, _("Hyper Active"), TRUE, TRUE, FALSE); - types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_AWAY, NULL, TRUE, TRUE, FALSE); - types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, SILCPURPLE_STATUS_ID_BUSY, _("Busy"), TRUE, TRUE, FALSE); - types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_INDISPOSED, _("Indisposed"), TRUE, TRUE, FALSE); - types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AWAY, SILCPURPLE_STATUS_ID_PAGE, _("Wake Me Up"), TRUE, TRUE, FALSE); - types = g_list_append(types, type); - type = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, SILCPURPLE_STATUS_ID_OFFLINE, NULL, TRUE, TRUE, FALSE); - types = g_list_append(types, type); - - return types; -} - -static void -silcpurple_set_status(PurpleAccount *account, PurpleStatus *status) -{ - PurpleConnection *gc = purple_account_get_connection(account); - SilcPurple sg = NULL; - SilcUInt32 mode; - SilcBuffer idp; - unsigned char mb[4]; - const char *state; - - if (gc != NULL) - sg = gc->proto_data; - - if (status == NULL) - return; - - state = purple_status_get_id(status); - - if (state == NULL) - return; - - if ((sg == NULL) || (sg->conn == NULL)) - return; - - mode = sg->conn->local_entry->mode; - mode &= ~(SILC_UMODE_GONE | - SILC_UMODE_HYPER | - SILC_UMODE_BUSY | - SILC_UMODE_INDISPOSED | - SILC_UMODE_PAGE); - - if (purple_strequal(state, "hyper")) - mode |= SILC_UMODE_HYPER; - else if (purple_strequal(state, "away")) - mode |= SILC_UMODE_GONE; - else if (purple_strequal(state, "busy")) - mode |= SILC_UMODE_BUSY; - else if (purple_strequal(state, "indisposed")) - mode |= SILC_UMODE_INDISPOSED; - else if (purple_strequal(state, "page")) - mode |= SILC_UMODE_PAGE; - - /* Send UMODE */ - idp = silc_id_payload_encode(sg->conn->local_id, SILC_ID_CLIENT); - SILC_PUT32_MSB(mode, mb); - silc_client_command_send(sg->client, sg->conn, SILC_COMMAND_UMODE, - ++sg->conn->cmd_ident, 2, - 1, idp->data, idp->len, - 2, mb, sizeof(mb)); - silc_buffer_free(idp); -} - - -/*************************** Connection Routines *****************************/ - -static void -silcpurple_keepalive(PurpleConnection *gc) -{ - SilcPurple sg = gc->proto_data; - silc_client_send_packet(sg->client, sg->conn, SILC_PACKET_HEARTBEAT, - NULL, 0); -} - -static gboolean -silcpurple_scheduler(gpointer *context) -{ - SilcPurple sg = (SilcPurple)context; - silc_client_run_one(sg->client); - return TRUE; -} - -static void -silcpurple_nickname_parse(const char *nickname, - char **ret_nickname) -{ - silc_parse_userfqdn(nickname, ret_nickname, NULL); -} - -static void -silcpurple_login_connected(gpointer data, gint source, const gchar *error_message) -{ - PurpleConnection *gc = data; - SilcPurple sg; - SilcClient client; - SilcClientConnection conn; - PurpleAccount *account; - SilcClientConnectionParams params; - SilcUInt32 mask; - const char *dfile, *tmp; -#ifdef SILC_ATTRIBUTE_USER_ICON - PurpleStoredImage *img; -#endif -#ifdef HAVE_SYS_UTSNAME_H - struct utsname u; -#endif - - - g_return_if_fail(gc != NULL); - - sg = gc->proto_data; - - if (source < 0) { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Connection failed")); - return; - } - - client = sg->client; - account = sg->account; - - /* Get session detachment data, if available */ - memset(¶ms, 0, sizeof(params)); - dfile = silcpurple_session_file(purple_account_get_username(sg->account)); - params.detach_data = (unsigned char *)silc_file_readfile(dfile, ¶ms.detach_data_len); - if (params.detach_data) - params.detach_data[params.detach_data_len] = 0; - - /* Add connection to SILC client library */ - conn = silc_client_add_connection( - sg->client, ¶ms, - (char *)purple_account_get_string(account, "server", - "silc.silcnet.org"), - purple_account_get_int(account, "port", 706), sg); - if (!conn) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to initialize SILC Client connection")); - gc->proto_data = NULL; - return; - } - sg->conn = conn; - - /* Progress */ - if (params.detach_data) { - purple_connection_update_progress(gc, _("Resuming session"), 2, 5); - sg->resuming = TRUE; - } else { - purple_connection_update_progress(gc, _("Performing key exchange"), 2, 5); - } - - /* Perform SILC Key Exchange. The "silc_connected" will be called - eventually. */ - silc_client_start_key_exchange(sg->client, sg->conn, source); - - /* Set default attributes */ - mask = SILC_ATTRIBUTE_MOOD_NORMAL; - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_STATUS_MOOD, - SILC_32_TO_PTR(mask), - sizeof(SilcUInt32)); - mask = SILC_ATTRIBUTE_CONTACT_CHAT; - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_PREFERRED_CONTACT, - SILC_32_TO_PTR(mask), - sizeof(SilcUInt32)); -#ifdef HAVE_SYS_UTSNAME_H - if (!uname(&u)) { - SilcAttributeObjDevice dev; - memset(&dev, 0, sizeof(dev)); - dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER; - dev.version = u.release; - dev.model = u.sysname; - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_DEVICE_INFO, - (void *)&dev, sizeof(dev)); - } -#endif -#ifdef _WIN32 - tmp = _tzname[0]; -#else - tmp = tzname[0]; -#endif - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_TIMEZONE, - (void *)tmp, strlen(tmp)); - -#ifdef SILC_ATTRIBUTE_USER_ICON - /* Set our buddy icon */ - img = purple_buddy_icons_find_account_icon(account); - silcpurple_buddy_set_icon(gc, img); - purple_imgstore_unref(img); -#endif - - silc_free(params.detach_data); -} - -static void -silcpurple_login(PurpleAccount *account) -{ - SilcPurple sg; - SilcClient client; - SilcClientParams params; - PurpleConnection *gc; - char pkd[256], prd[256]; - const char *cipher, *hmac; - char *realname; - int i; - - gc = account->gc; - if (!gc) - return; - gc->proto_data = NULL; - - memset(¶ms, 0, sizeof(params)); - strcat(params.nickname_format, "%n@%h%a"); - params.nickname_parse = silcpurple_nickname_parse; - params.ignore_requested_attributes = FALSE; - - /* Allocate SILC client */ - client = silc_client_alloc(&ops, ¶ms, gc, NULL); - if (!client) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("Out of memory")); - return; - } - - /* Get username, real name and local hostname for SILC library */ - if (purple_account_get_username(account)) { - const char *u = purple_account_get_username(account); - char **up = g_strsplit(u, "@", 2); - client->username = strdup(up[0]); - g_strfreev(up); - } else { - client->username = silc_get_username(); - purple_account_set_username(account, client->username); - } - realname = silc_get_real_name(); - if (purple_account_get_user_info(account)) { - client->realname = strdup(purple_account_get_user_info(account)); - free(realname); - } else if ((silc_get_real_name() != NULL) && (*realname != '\0')) { - client->realname = realname; - purple_account_set_user_info(account, client->realname); - } else { - free(realname); - client->realname = strdup(_("John Noname")); - } - client->hostname = silc_net_localhost(); - - purple_connection_set_display_name(gc, client->username); - - /* Register requested cipher and HMAC */ - cipher = purple_account_get_string(account, "cipher", SILC_DEFAULT_CIPHER); - for (i = 0; silc_default_ciphers[i].name; i++) - if (purple_strequal(silc_default_ciphers[i].name, cipher)) { - silc_cipher_register(&(silc_default_ciphers[i])); - break; - } - hmac = purple_account_get_string(account, "hmac", SILC_DEFAULT_HMAC); - for (i = 0; silc_default_hmacs[i].name; i++) - if (purple_strequal(silc_default_hmacs[i].name, hmac)) { - silc_hmac_register(&(silc_default_hmacs[i])); - break; - } - - /* Init SILC client */ - if (!silc_client_init(client)) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("Unable to initialize SILC protocol")); - return; - } - - /* Check the ~/.silc dir and create it, and new key pair if necessary. */ - if (!silcpurple_check_silc_dir(gc)) { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - _("Error loading SILC key pair")); - return; - } - - /* Progress */ - purple_connection_update_progress(gc, _("Connecting to SILC Server"), 1, 5); - - /* Load SILC key pair */ - g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir()); - g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir()); - if (!silc_load_key_pair((char *)purple_account_get_string(account, "public-key", pkd), - (char *)purple_account_get_string(account, "private-key", prd), - (gc->password == NULL) ? "" : gc->password, &client->pkcs, - &client->public_key, &client->private_key)) { - g_snprintf(pkd, sizeof(pkd), _("Unable to load SILC key pair: %s"), g_strerror(errno)); - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, - pkd); - return; - } - - sg = silc_calloc(1, sizeof(*sg)); - if (!sg) - return; - memset(sg, 0, sizeof(*sg)); - sg->client = client; - sg->gc = gc; - sg->account = account; - gc->proto_data = sg; - - /* Connect to the SILC server */ - if (purple_proxy_connect(gc, account, - purple_account_get_string(account, "server", - "silc.silcnet.org"), - purple_account_get_int(account, "port", 706), - silcpurple_login_connected, gc) == NULL) - { - purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to create connection")); - return; - } - - /* Schedule SILC using Glib's event loop */ - sg->scheduler = purple_timeout_add(300, (GSourceFunc)silcpurple_scheduler, sg); -} - -static int -silcpurple_close_final(gpointer *context) -{ - SilcPurple sg = (SilcPurple)context; - silc_client_stop(sg->client); - silc_client_free(sg->client); -#ifdef HAVE_SILCMIME_H - if (sg->mimeass) - silc_mime_assembler_free(sg->mimeass); -#endif - silc_free(sg); - return 0; -} - -static void -silcpurple_close(PurpleConnection *gc) -{ - SilcPurple sg = gc->proto_data; - GHashTable *ui_info; - const char *ui_name = NULL, *ui_website = NULL; - char *quit_msg; - - g_return_if_fail(sg != NULL); - - ui_info = purple_core_get_ui_info(); - - if(ui_info) { - ui_name = g_hash_table_lookup(ui_info, "name"); - ui_website = g_hash_table_lookup(ui_info, "website"); - } - - if(!ui_name || !ui_website) { - ui_name = "Pidgin"; - ui_website = PURPLE_WEBSITE; - } - quit_msg = g_strdup_printf(_("Download %s: %s"), - ui_name, ui_website); - - /* Send QUIT */ - silc_client_command_call(sg->client, sg->conn, NULL, - "QUIT", quit_msg, NULL); - g_free(quit_msg); - - if (sg->conn) - silc_client_close_connection(sg->client, sg->conn); - - purple_timeout_remove(sg->scheduler); - purple_timeout_add(1, (GSourceFunc)silcpurple_close_final, sg); -} - - -/****************************** Protocol Actions *****************************/ - -static void -silcpurple_attrs_cancel(PurpleConnection *gc, PurpleRequestFields *fields) -{ - /* Nothing */ -} - -static void -silcpurple_attrs_cb(PurpleConnection *gc, PurpleRequestFields *fields) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - PurpleRequestField *f; - char *tmp; - SilcUInt32 tmp_len, mask; - SilcAttributeObjService service; - SilcAttributeObjDevice dev; - SilcVCardStruct vcard; - const char *val; - - sg = gc->proto_data; - if (!sg) - return; - - memset(&service, 0, sizeof(service)); - memset(&dev, 0, sizeof(dev)); - memset(&vcard, 0, sizeof(vcard)); - - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_USER_INFO, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_SERVICE, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_STATUS_MOOD, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_STATUS_FREETEXT, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_STATUS_MESSAGE, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_PREFERRED_LANGUAGE, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_PREFERRED_CONTACT, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_TIMEZONE, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_GEOLOCATION, NULL); - silc_client_attribute_del(client, conn, - SILC_ATTRIBUTE_DEVICE_INFO, NULL); - - /* Set mood */ - mask = 0; - f = purple_request_fields_get_field(fields, "mood_normal"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_NORMAL; - f = purple_request_fields_get_field(fields, "mood_happy"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_HAPPY; - f = purple_request_fields_get_field(fields, "mood_sad"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_SAD; - f = purple_request_fields_get_field(fields, "mood_angry"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_ANGRY; - f = purple_request_fields_get_field(fields, "mood_jealous"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_JEALOUS; - f = purple_request_fields_get_field(fields, "mood_ashamed"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_ASHAMED; - f = purple_request_fields_get_field(fields, "mood_invincible"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_INVINCIBLE; - f = purple_request_fields_get_field(fields, "mood_inlove"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_INLOVE; - f = purple_request_fields_get_field(fields, "mood_sleepy"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_SLEEPY; - f = purple_request_fields_get_field(fields, "mood_bored"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_BORED; - f = purple_request_fields_get_field(fields, "mood_excited"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_EXCITED; - f = purple_request_fields_get_field(fields, "mood_anxious"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_MOOD_ANXIOUS; - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_STATUS_MOOD, - SILC_32_TO_PTR(mask), - sizeof(SilcUInt32)); - - /* Set preferred contact */ - mask = 0; - f = purple_request_fields_get_field(fields, "contact_chat"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_CONTACT_CHAT; - f = purple_request_fields_get_field(fields, "contact_email"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_CONTACT_EMAIL; - f = purple_request_fields_get_field(fields, "contact_call"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_CONTACT_CALL; - f = purple_request_fields_get_field(fields, "contact_sms"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_CONTACT_SMS; - f = purple_request_fields_get_field(fields, "contact_mms"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_CONTACT_MMS; - f = purple_request_fields_get_field(fields, "contact_video"); - if (f && purple_request_field_bool_get_value(f)) - mask |= SILC_ATTRIBUTE_CONTACT_VIDEO; - if (mask) - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_PREFERRED_CONTACT, - SILC_32_TO_PTR(mask), - sizeof(SilcUInt32)); - - /* Set status text */ - val = NULL; - f = purple_request_fields_get_field(fields, "status_text"); - if (f) - val = purple_request_field_string_get_value(f); - if (val && *val) - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_STATUS_FREETEXT, - (void *)val, strlen(val)); - - /* Set vcard */ - val = NULL; - f = purple_request_fields_get_field(fields, "vcard"); - if (f) - val = purple_request_field_string_get_value(f); - if (val && *val) { - purple_account_set_string(sg->account, "vcard", val); - tmp = silc_file_readfile(val, &tmp_len); - if (tmp) { - tmp[tmp_len] = 0; - if (silc_vcard_decode((unsigned char *)tmp, tmp_len, &vcard)) - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_USER_INFO, - (void *)&vcard, - sizeof(vcard)); - } - silc_vcard_free(&vcard); - silc_free(tmp); - } else { - purple_account_set_string(sg->account, "vcard", ""); - } - -#ifdef HAVE_SYS_UTSNAME_H - /* Set device info */ - f = purple_request_fields_get_field(fields, "device"); - if (f && purple_request_field_bool_get_value(f)) { - struct utsname u; - if (!uname(&u)) { - dev.type = SILC_ATTRIBUTE_DEVICE_COMPUTER; - dev.version = u.release; - dev.model = u.sysname; - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_DEVICE_INFO, - (void *)&dev, sizeof(dev)); - } - } -#endif - - /* Set timezone */ - val = NULL; - f = purple_request_fields_get_field(fields, "timezone"); - if (f) - val = purple_request_field_string_get_value(f); - if (val && *val) - silc_client_attribute_add(client, conn, - SILC_ATTRIBUTE_TIMEZONE, - (void *)val, strlen(val)); -} - -static void -silcpurple_attrs(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - PurpleRequestFields *fields; - PurpleRequestFieldGroup *g; - PurpleRequestField *f; - SilcHashTable attrs; - SilcAttributePayload attr; - gboolean mnormal = TRUE, mhappy = FALSE, msad = FALSE, - mangry = FALSE, mjealous = FALSE, mashamed = FALSE, - minvincible = FALSE, minlove = FALSE, msleepy = FALSE, - mbored = FALSE, mexcited = FALSE, manxious = FALSE; - gboolean cemail = FALSE, ccall = FALSE, csms = FALSE, - cmms = FALSE, cchat = TRUE, cvideo = FALSE; - gboolean device = TRUE; - char status[1024]; - - sg = gc->proto_data; - if (!sg) - return; - - memset(status, 0, sizeof(status)); - - attrs = silc_client_attributes_get(client, conn); - if (attrs) { - if (silc_hash_table_find(attrs, - SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_MOOD), - NULL, (void *)&attr)) { - SilcUInt32 mood = 0; - silc_attribute_get_object(attr, &mood, sizeof(mood)); - mnormal = !mood; - mhappy = (mood & SILC_ATTRIBUTE_MOOD_HAPPY); - msad = (mood & SILC_ATTRIBUTE_MOOD_SAD); - mangry = (mood & SILC_ATTRIBUTE_MOOD_ANGRY); - mjealous = (mood & SILC_ATTRIBUTE_MOOD_JEALOUS); - mashamed = (mood & SILC_ATTRIBUTE_MOOD_ASHAMED); - minvincible = (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE); - minlove = (mood & SILC_ATTRIBUTE_MOOD_INLOVE); - msleepy = (mood & SILC_ATTRIBUTE_MOOD_SLEEPY); - mbored = (mood & SILC_ATTRIBUTE_MOOD_BORED); - mexcited = (mood & SILC_ATTRIBUTE_MOOD_EXCITED); - manxious = (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS); - } - - if (silc_hash_table_find(attrs, - SILC_32_TO_PTR(SILC_ATTRIBUTE_PREFERRED_CONTACT), - NULL, (void *)&attr)) { - SilcUInt32 contact = 0; - silc_attribute_get_object(attr, &contact, sizeof(contact)); - cemail = (contact & SILC_ATTRIBUTE_CONTACT_EMAIL); - ccall = (contact & SILC_ATTRIBUTE_CONTACT_CALL); - csms = (contact & SILC_ATTRIBUTE_CONTACT_SMS); - cmms = (contact & SILC_ATTRIBUTE_CONTACT_MMS); - cchat = (contact & SILC_ATTRIBUTE_CONTACT_CHAT); - cvideo = (contact & SILC_ATTRIBUTE_CONTACT_VIDEO); - } - - if (silc_hash_table_find(attrs, - SILC_32_TO_PTR(SILC_ATTRIBUTE_STATUS_FREETEXT), - NULL, (void *)&attr)) - silc_attribute_get_object(attr, &status, sizeof(status)); - - if (!silc_hash_table_find(attrs, - SILC_32_TO_PTR(SILC_ATTRIBUTE_DEVICE_INFO), - NULL, (void *)&attr)) - device = FALSE; - } - - fields = purple_request_fields_new(); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_label_new("l3", _("Your Current Mood")); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_normal", _("Normal"), mnormal); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_happy", _("Happy"), mhappy); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_sad", _("Sad"), msad); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_angry", _("Angry"), mangry); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_jealous", _("Jealous"), mjealous); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_ashamed", _("Ashamed"), mashamed); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_invincible", _("Invincible"), minvincible); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_inlove", _("In love"), minlove); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_sleepy", _("Sleepy"), msleepy); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_bored", _("Bored"), mbored); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_excited", _("Excited"), mexcited); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("mood_anxious", _("Anxious"), manxious); - purple_request_field_group_add_field(g, f); - - f = purple_request_field_label_new("l4", _("\nYour Preferred Contact Methods")); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("contact_chat", _("Chat"), cchat); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("contact_email", _("Email"), cemail); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("contact_call", _("Phone"), ccall); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("contact_sms", _("SMS"), csms); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("contact_mms", _("MMS"), cmms); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("contact_video", _("Video conferencing"), cvideo); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_string_new("status_text", _("Your Current Status"), - status[0] ? status : NULL, TRUE); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - g = purple_request_field_group_new(NULL); -#if 0 - f = purple_request_field_label_new("l2", _("Online Services")); - purple_request_field_group_add_field(g, f); - f = purple_request_field_bool_new("services", - _("Let others see what services you are using"), - TRUE); - purple_request_field_group_add_field(g, f); -#endif -#ifdef HAVE_SYS_UTSNAME_H - f = purple_request_field_bool_new("device", - _("Let others see what computer you are using"), - device); - purple_request_field_group_add_field(g, f); -#endif - purple_request_fields_add_group(fields, g); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_string_new("vcard", _("Your VCard File"), - purple_account_get_string(sg->account, "vcard", ""), - FALSE); - purple_request_field_group_add_field(g, f); -#ifdef _WIN32 - f = purple_request_field_string_new("timezone", _("Timezone"), _tzname[0], FALSE); -#else - f = purple_request_field_string_new("timezone", _("Timezone"), tzname[0], FALSE); -#endif - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - purple_request_fields(gc, _("User Online Status Attributes"), - _("User Online Status Attributes"), - _("You can let other users see your online status information " - "and your personal information. Please fill the information " - "you would like other users to see about yourself."), - fields, - _("OK"), G_CALLBACK(silcpurple_attrs_cb), - _("Cancel"), G_CALLBACK(silcpurple_attrs_cancel), - gc->account, NULL, NULL, gc); -} - -static void -silcpurple_detach(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - SilcPurple sg; - - if (!gc) - return; - sg = gc->proto_data; - if (!sg) - return; - - /* Call DETACH */ - silc_client_command_call(sg->client, sg->conn, "DETACH"); - sg->detaching = TRUE; -} - -static void -silcpurple_view_motd(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - SilcPurple sg; - char *tmp; - - if (!gc) - return; - sg = gc->proto_data; - if (!sg) - return; - - if (!sg->motd) { - purple_notify_error( - gc, _("Message of the Day"), _("No Message of the Day available"), - _("There is no Message of the Day associated with this connection")); - return; - } - - tmp = g_markup_escape_text(sg->motd, -1); - purple_notify_formatted(gc, NULL, _("Message of the Day"), NULL, - tmp, NULL, NULL); - g_free(tmp); -} - -static void -silcpurple_create_keypair_cancel(PurpleConnection *gc, PurpleRequestFields *fields) -{ - /* Nothing */ -} - -static void -silcpurple_create_keypair_cb(PurpleConnection *gc, PurpleRequestFields *fields) -{ - SilcPurple sg = gc->proto_data; - PurpleRequestField *f; - const char *val, *pkfile = NULL, *prfile = NULL; - const char *pass1 = NULL, *pass2 = NULL, *un = NULL, *hn = NULL; - const char *rn = NULL, *e = NULL, *o = NULL, *c = NULL; - char *identifier; - int keylen = SILCPURPLE_DEF_PKCS_LEN; - SilcPublicKey public_key; - - sg = gc->proto_data; - if (!sg) - return; - - val = NULL; - f = purple_request_fields_get_field(fields, "pass1"); - if (f) - val = purple_request_field_string_get_value(f); - if (val && *val) - pass1 = val; - else - pass1 = ""; - val = NULL; - f = purple_request_fields_get_field(fields, "pass2"); - if (f) - val = purple_request_field_string_get_value(f); - if (val && *val) - pass2 = val; - else - pass2 = ""; - - if (!purple_strequal(pass1, pass2)) { - purple_notify_error( - gc, _("Create New SILC Key Pair"), _("Passphrases do not match"), NULL); - return; - } - - val = NULL; - f = purple_request_fields_get_field(fields, "key"); - if (f) - val = purple_request_field_string_get_value(f); - if (val && *val) - keylen = atoi(val); - f = purple_request_fields_get_field(fields, "pkfile"); - if (f) - pkfile = purple_request_field_string_get_value(f); - f = purple_request_fields_get_field(fields, "prfile"); - if (f) - prfile = purple_request_field_string_get_value(f); - - f = purple_request_fields_get_field(fields, "un"); - if (f) - un = purple_request_field_string_get_value(f); - f = purple_request_fields_get_field(fields, "hn"); - if (f) - hn = purple_request_field_string_get_value(f); - f = purple_request_fields_get_field(fields, "rn"); - if (f) - rn = purple_request_field_string_get_value(f); - f = purple_request_fields_get_field(fields, "e"); - if (f) - e = purple_request_field_string_get_value(f); - f = purple_request_fields_get_field(fields, "o"); - if (f) - o = purple_request_field_string_get_value(f); - f = purple_request_fields_get_field(fields, "c"); - if (f) - c = purple_request_field_string_get_value(f); - - identifier = silc_pkcs_encode_identifier((char *)un, (char *)hn, - (char *)rn, (char *)e, (char *)o, (char *)c); - - /* Create the key pair */ - if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS, keylen, pkfile, prfile, - identifier, pass1, NULL, &public_key, NULL, - FALSE)) { - purple_notify_error( - gc, _("Create New SILC Key Pair"), _("Key Pair Generation failed"), NULL); - return; - } - - silcpurple_show_public_key(sg, NULL, public_key, NULL, NULL); - - silc_pkcs_public_key_free(public_key); - silc_free(identifier); -} - -static void -silcpurple_create_keypair(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - SilcPurple sg = gc->proto_data; - PurpleRequestFields *fields; - PurpleRequestFieldGroup *g; - PurpleRequestField *f; - const char *username, *realname; - char *hostname, **u; - char tmp[256], pkd[256], pkd2[256], prd[256], prd2[256]; - - username = purple_account_get_username(sg->account); - u = g_strsplit(username, "@", 2); - username = u[0]; - realname = purple_account_get_user_info(sg->account); - hostname = silc_net_localhost(); - g_snprintf(tmp, sizeof(tmp), "%s@%s", username, hostname); - - g_snprintf(pkd2, sizeof(pkd2), "%s" G_DIR_SEPARATOR_S"public_key.pub", silcpurple_silcdir()); - g_snprintf(prd2, sizeof(prd2), "%s" G_DIR_SEPARATOR_S"private_key.prv", silcpurple_silcdir()); - g_snprintf(pkd, sizeof(pkd) - 1, "%s", - purple_account_get_string(gc->account, "public-key", pkd2)); - g_snprintf(prd, sizeof(prd) - 1, "%s", - purple_account_get_string(gc->account, "private-key", prd2)); - - fields = purple_request_fields_new(); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_string_new("key", _("Key length"), "2048", FALSE); - purple_request_field_group_add_field(g, f); - f = purple_request_field_string_new("pkfile", _("Public key file"), pkd, FALSE); - purple_request_field_group_add_field(g, f); - f = purple_request_field_string_new("prfile", _("Private key file"), prd, FALSE); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_string_new("un", _("Username"), username ? username : "", FALSE); - purple_request_field_group_add_field(g, f); - f = purple_request_field_string_new("hn", _("Hostname"), hostname ? hostname : "", FALSE); - purple_request_field_group_add_field(g, f); - f = purple_request_field_string_new("rn", _("Real name"), realname ? realname : "", FALSE); - purple_request_field_group_add_field(g, f); - f = purple_request_field_string_new("e", _("Email"), tmp, FALSE); - purple_request_field_group_add_field(g, f); - f = purple_request_field_string_new("o", _("Organization"), "", FALSE); - purple_request_field_group_add_field(g, f); - f = purple_request_field_string_new("c", _("Country"), "", FALSE); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - g = purple_request_field_group_new(NULL); - f = purple_request_field_string_new("pass1", _("Passphrase"), "", FALSE); - purple_request_field_string_set_masked(f, TRUE); - purple_request_field_group_add_field(g, f); - f = purple_request_field_string_new("pass2", _("Passphrase (retype)"), "", FALSE); - purple_request_field_string_set_masked(f, TRUE); - purple_request_field_group_add_field(g, f); - purple_request_fields_add_group(fields, g); - - purple_request_fields(gc, _("Create New SILC Key Pair"), - _("Create New SILC Key Pair"), NULL, fields, - _("Generate Key Pair"), G_CALLBACK(silcpurple_create_keypair_cb), - _("Cancel"), G_CALLBACK(silcpurple_create_keypair_cancel), - gc->account, NULL, NULL, gc); - - g_strfreev(u); - silc_free(hostname); -} - -static void -silcpurple_change_pass(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - purple_account_request_change_password(purple_connection_get_account(gc)); -} - -static void -silcpurple_change_passwd(PurpleConnection *gc, const char *old, const char *new) -{ - char prd[256]; - g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.pub", silcpurple_silcdir()); - silc_change_private_key_passphrase(purple_account_get_string(gc->account, - "private-key", - prd), old ? old : "", new ? new : ""); -} - -static void -silcpurple_show_set_info(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - purple_account_request_change_user_info(purple_connection_get_account(gc)); -} - -static void -silcpurple_set_info(PurpleConnection *gc, const char *text) -{ -} - -static GList * -silcpurple_actions(PurplePlugin *plugin, gpointer context) -{ - GList *list = NULL; - PurplePluginAction *act; - - act = purple_plugin_action_new(_("Online Status"), - silcpurple_attrs); - list = g_list_append(list, act); - - act = purple_plugin_action_new(_("Detach From Server"), - silcpurple_detach); - list = g_list_append(list, act); - - act = purple_plugin_action_new(_("View Message of the Day"), - silcpurple_view_motd); - list = g_list_append(list, act); - - act = purple_plugin_action_new(_("Create SILC Key Pair..."), - silcpurple_create_keypair); - list = g_list_append(list, act); - - act = purple_plugin_action_new(_("Change Password..."), - silcpurple_change_pass); - list = g_list_append(list, act); - - act = purple_plugin_action_new(_("Set User Info..."), - silcpurple_show_set_info); - list = g_list_append(list, act); - - return list; -} - - -/******************************* IM Routines *********************************/ - -typedef struct { - char *nick; - char *message; - SilcUInt32 message_len; - SilcMessageFlags flags; - PurpleMessageFlags gflags; -} *SilcPurpleIM; - -static void -silcpurple_send_im_resolved(SilcClient client, - SilcClientConnection conn, - SilcClientEntry *clients, - SilcUInt32 clients_count, - void *context) -{ - PurpleConnection *gc = client->application; - SilcPurple sg = gc->proto_data; - SilcPurpleIM im = context; - PurpleConversation *convo; - char tmp[256], *nickname = NULL; - SilcClientEntry client_entry; -#ifdef HAVE_SILCMIME_H - SilcDList list; -#endif - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, im->nick, - sg->account); - if (!convo) - return; - - if (!clients) - goto err; - - if (clients_count > 1) { - silc_parse_userfqdn(im->nick, &nickname, NULL); - - /* Find the correct one. The im->nick might be a formatted nick - so this will find the correct one. */ - clients = silc_client_get_clients_local(client, conn, - nickname, im->nick, - &clients_count); - if (!clients) - goto err; - client_entry = clients[0]; - silc_free(clients); - } else { - client_entry = clients[0]; - } - -#ifdef HAVE_SILCMIME_H - /* Check for images */ - if (im->gflags & PURPLE_MESSAGE_IMAGES) { - list = silcpurple_image_message(im->message, (SilcUInt32 *)&im->flags); - if (list) { - /* Send one or more MIME message. If more than one, they - are MIME fragments due to over large message */ - SilcBuffer buf; - - silc_dlist_start(list); - while ((buf = silc_dlist_get(list)) != SILC_LIST_END) - silc_client_send_private_message(client, conn, - client_entry, im->flags, - buf->data, buf->len, - TRUE); - silc_mime_partial_free(list); - purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname, - im->message, 0, time(NULL)); - goto out; - } - } -#endif - - /* Send the message */ - silc_client_send_private_message(client, conn, client_entry, im->flags, - (unsigned char *)im->message, im->message_len, TRUE); - purple_conv_im_write(PURPLE_CONV_IM(convo), conn->local_entry->nickname, - im->message, 0, time(NULL)); - goto out; - - err: - g_snprintf(tmp, sizeof(tmp), - _("User %s is not present in the network"), im->nick); - purple_conversation_write(convo, NULL, tmp, PURPLE_MESSAGE_SYSTEM, time(NULL)); - - out: - g_free(im->nick); - g_free(im->message); - silc_free(im); - silc_free(nickname); -} - -static int -silcpurple_send_im(PurpleConnection *gc, const char *who, const char *message, - PurpleMessageFlags flags) -{ - SilcPurple sg = gc->proto_data; - SilcClient client = sg->client; - SilcClientConnection conn = sg->conn; - SilcClientEntry *clients; - SilcUInt32 clients_count, mflags; - char *nickname, *msg, *tmp; - int ret = 0; - gboolean sign = purple_account_get_bool(sg->account, "sign-verify", FALSE); -#ifdef HAVE_SILCMIME_H - SilcDList list; -#endif - - if (!who || !message) - return 0; - - mflags = SILC_MESSAGE_FLAG_UTF8; - - tmp = msg = purple_unescape_html(message); - - if (!g_ascii_strncasecmp(msg, "/me ", 4)) { - msg += 4; - if (!*msg) { - g_free(tmp); - return 0; - } - mflags |= SILC_MESSAGE_FLAG_ACTION; - } else if (strlen(msg) > 1 && msg[0] == '/') { - if (!silc_client_command_call(client, conn, msg + 1)) - purple_notify_error(gc, _("Call Command"), _("Cannot call command"), - _("Unknown command")); - g_free(tmp); - return 0; - } - - - if (!silc_parse_userfqdn(who, &nickname, NULL)) { - g_free(tmp); - return 0; - } - - if (sign) - mflags |= SILC_MESSAGE_FLAG_SIGNED; - - /* Find client entry */ - clients = silc_client_get_clients_local(client, conn, nickname, who, - &clients_count); - if (!clients) { - /* Resolve unknown user */ - SilcPurpleIM im = silc_calloc(1, sizeof(*im)); - if (!im) { - g_free(tmp); - return 0; - } - im->nick = g_strdup(who); - im->message = g_strdup(message); - im->message_len = strlen(im->message); - im->flags = mflags; - im->gflags = flags; - silc_client_get_clients(client, conn, nickname, NULL, - silcpurple_send_im_resolved, im); - silc_free(nickname); - g_free(tmp); - return 0; - } - -#ifdef HAVE_SILCMIME_H - /* Check for images */ - if (flags & PURPLE_MESSAGE_IMAGES) { - list = silcpurple_image_message(message, &mflags); - if (list) { - /* Send one or more MIME message. If more than one, they - are MIME fragments due to over large message */ - SilcBuffer buf; - - silc_dlist_start(list); - while ((buf = silc_dlist_get(list)) != SILC_LIST_END) - ret = - silc_client_send_private_message(client, conn, - clients[0], mflags, - buf->data, buf->len, - TRUE); - silc_mime_partial_free(list); - g_free(tmp); - silc_free(nickname); - silc_free(clients); - return ret; - } - } -#endif - - /* Send private message directly */ - ret = silc_client_send_private_message(client, conn, clients[0], - mflags, - (unsigned char *)msg, - strlen(msg), TRUE); - - g_free(tmp); - silc_free(nickname); - silc_free(clients); - return ret; -} - - -static GList *silcpurple_blist_node_menu(PurpleBlistNode *node) { - /* split this single menu building function back into the two - original: one for buddies and one for chats */ - - if(PURPLE_BLIST_NODE_IS_CHAT(node)) { - return silcpurple_chat_menu((PurpleChat *) node); - } else if(PURPLE_BLIST_NODE_IS_BUDDY(node)) { - return silcpurple_buddy_menu((PurpleBuddy *) node); - } else { - g_return_val_if_reached(NULL); - } -} - -/********************************* Commands **********************************/ - -static PurpleCmdRet silcpurple_cmd_chat_part(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - PurpleConversation *convo = conv; - int id = 0; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - if(args && args[0]) - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[0], - gc->account); - - if (convo != NULL) - id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(convo)); - - if (id == 0) - return PURPLE_CMD_RET_FAILED; - - silcpurple_chat_leave(gc, id); - - return PURPLE_CMD_RET_OK; - -} - -static PurpleCmdRet silcpurple_cmd_chat_topic(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - int id = 0; - char *buf, *tmp, *tmp2; - const char *topic; - - gc = purple_conversation_get_gc(conv); - id = purple_conv_chat_get_id(PURPLE_CONV_CHAT(conv)); - - if (gc == NULL || id == 0) - return PURPLE_CMD_RET_FAILED; - - if (!args || !args[0]) { - topic = purple_conv_chat_get_topic (PURPLE_CONV_CHAT(conv)); - if (topic) { - tmp = g_markup_escape_text(topic, -1); - tmp2 = purple_markup_linkify(tmp); - buf = g_strdup_printf(_("current topic is: %s"), tmp2); - g_free(tmp); - g_free(tmp2); - } else - buf = g_strdup(_("No topic is set")); - purple_conv_chat_write(PURPLE_CONV_CHAT(conv), gc->account->username, buf, - PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL)); - g_free(buf); - - } - - if (args && args[0] && (strlen(args[0]) > 255)) { - *error = g_strdup(_("Topic too long")); - return PURPLE_CMD_RET_FAILED; - } - - silcpurple_chat_set_topic(gc, id, args ? args[0] : NULL); - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_chat_join(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - GHashTable *comp; - - if(!args || !args[0]) - return PURPLE_CMD_RET_FAILED; - - comp = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); - - g_hash_table_replace(comp, "channel", args[0]); - if(args[1]) - g_hash_table_replace(comp, "passphrase", args[1]); - - silcpurple_chat_join(purple_conversation_get_gc(conv), comp); - - g_hash_table_destroy(comp); - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_chat_list(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - gc = purple_conversation_get_gc(conv); - purple_roomlist_show_with_account(purple_connection_get_account(gc)); - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_whois(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - silcpurple_get_info(gc, args[0]); - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_msg(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - int ret; - PurpleConnection *gc; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - ret = silcpurple_send_im(gc, args[0], args[1], PURPLE_MESSAGE_SEND); - - if (ret) - return PURPLE_CMD_RET_OK; - else - return PURPLE_CMD_RET_FAILED; -} - -static PurpleCmdRet silcpurple_cmd_query(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - int ret = 1; - PurpleConversation *convo; - PurpleConnection *gc; - PurpleAccount *account; - - if (!args || !args[0]) { - *error = g_strdup(_("You must specify a nick")); - return PURPLE_CMD_RET_FAILED; - } - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - account = purple_connection_get_account(gc); - - convo = purple_conversation_new(PURPLE_CONV_TYPE_IM, account, args[0]); - - if (args[1]) { - ret = silcpurple_send_im(gc, args[0], args[1], PURPLE_MESSAGE_SEND); - purple_conv_im_write(PURPLE_CONV_IM(convo), purple_connection_get_display_name(gc), - args[1], PURPLE_MESSAGE_SEND, time(NULL)); - } - - if (ret) - return PURPLE_CMD_RET_OK; - else - return PURPLE_CMD_RET_FAILED; -} - -static PurpleCmdRet silcpurple_cmd_motd(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - SilcPurple sg; - char *tmp; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - sg = gc->proto_data; - - if (sg == NULL) - return PURPLE_CMD_RET_FAILED; - - if (!sg->motd) { - *error = g_strdup(_("There is no Message of the Day associated with this connection")); - return PURPLE_CMD_RET_FAILED; - } - - tmp = g_markup_escape_text(sg->motd, -1); - purple_notify_formatted(gc, NULL, _("Message of the Day"), NULL, - tmp, NULL, NULL); - g_free(tmp); - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_detach(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - SilcPurple sg; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - sg = gc->proto_data; - - if (sg == NULL) - return PURPLE_CMD_RET_FAILED; - - silc_client_command_call(sg->client, sg->conn, "DETACH"); - sg->detaching = TRUE; - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_cmode(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - SilcPurple sg; - SilcChannelEntry channel; - char *silccmd, *silcargs, *msg, tmp[256]; - const char *chname; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL || !args || gc->proto_data == NULL) - return PURPLE_CMD_RET_FAILED; - - sg = gc->proto_data; - - if (args[0]) - chname = args[0]; - else - chname = purple_conversation_get_name(conv); - - if (!args[1]) { - channel = silc_client_get_channel(sg->client, sg->conn, - (char *)chname); - if (!channel) { - *error = g_strdup_printf(_("channel %s not found"), chname); - return PURPLE_CMD_RET_FAILED; - } - if (channel->mode) { - silcpurple_get_chmode_string(channel->mode, tmp, sizeof(tmp)); - msg = g_strdup_printf(_("channel modes for %s: %s"), chname, tmp); - } else { - msg = g_strdup_printf(_("no channel modes are set on %s"), chname); - } - purple_conv_chat_write(PURPLE_CONV_CHAT(conv), "", - msg, PURPLE_MESSAGE_SYSTEM|PURPLE_MESSAGE_NO_LOG, time(NULL)); - g_free(msg); - return PURPLE_CMD_RET_OK; - } - - silcargs = g_strjoinv(" ", args); - silccmd = g_strconcat(cmd, " ", args ? silcargs : NULL, NULL); - g_free(silcargs); - if (!silc_client_command_call(sg->client, sg->conn, silccmd)) { - g_free(silccmd); - *error = g_strdup_printf(_("Failed to set cmodes for %s"), args[0]); - return PURPLE_CMD_RET_FAILED; - } - g_free(silccmd); - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_generic(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - SilcPurple sg; - char *silccmd, *silcargs; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - sg = gc->proto_data; - - if (sg == NULL) - return PURPLE_CMD_RET_FAILED; - - silcargs = g_strjoinv(" ", args); - silccmd = g_strconcat(cmd, " ", args ? silcargs : NULL, NULL); - g_free(silcargs); - if (!silc_client_command_call(sg->client, sg->conn, silccmd)) { - g_free(silccmd); - *error = g_strdup_printf(_("Unknown command: %s, (may be a client bug)"), cmd); - return PURPLE_CMD_RET_FAILED; - } - g_free(silccmd); - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_quit(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - SilcPurple sg; - GHashTable *ui_info; - const char *ui_name = NULL, *ui_website = NULL; - char *quit_msg; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - sg = gc->proto_data; - - if (sg == NULL) - return PURPLE_CMD_RET_FAILED; - - ui_info = purple_core_get_ui_info(); - - if(ui_info) { - ui_name = g_hash_table_lookup(ui_info, "name"); - ui_website = g_hash_table_lookup(ui_info, "website"); - } - - if(!ui_name || !ui_website) { - ui_name = "Pidgin"; - ui_website = PURPLE_WEBSITE; - } - quit_msg = g_strdup_printf(_("Download %s: %s"), - ui_name, ui_website); - - silc_client_command_call(sg->client, sg->conn, NULL, - "QUIT", (args && args[0]) ? args[0] : quit_msg, NULL); - g_free(quit_msg); - - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet silcpurple_cmd_call(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - PurpleConnection *gc; - SilcPurple sg; - - gc = purple_conversation_get_gc(conv); - - if (gc == NULL) - return PURPLE_CMD_RET_FAILED; - - sg = gc->proto_data; - - if (sg == NULL) - return PURPLE_CMD_RET_FAILED; - - if (!silc_client_command_call(sg->client, sg->conn, args[0])) { - *error = g_strdup_printf(_("Unknown command: %s"), args[0]); - return PURPLE_CMD_RET_FAILED; - } - - return PURPLE_CMD_RET_OK; -} - - -/************************** Plugin Initialization ****************************/ - -static void -silcpurple_register_commands(void) -{ - purple_cmd_register("part", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | - PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, - "prpl-silc", silcpurple_cmd_chat_part, _("part [channel]: Leave the chat"), NULL); - purple_cmd_register("leave", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | - PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, - "prpl-silc", silcpurple_cmd_chat_part, _("leave [channel]: Leave the chat"), NULL); - purple_cmd_register("topic", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", - silcpurple_cmd_chat_topic, _("topic [<new topic>]: View or change the topic"), NULL); - purple_cmd_register("join", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | - PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, - "prpl-silc", silcpurple_cmd_chat_join, - _("join <channel> [<password>]: Join a chat on this network"), NULL); - purple_cmd_register("list", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", - silcpurple_cmd_chat_list, _("list: List channels on this network"), NULL); - purple_cmd_register("whois", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", - silcpurple_cmd_whois, _("whois <nick>: View nick's information"), NULL); - purple_cmd_register("msg", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_msg, - _("msg <nick> <message>: Send a private message to a user"), NULL); - purple_cmd_register("query", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_query, - _("query <nick> [<message>]: Send a private message to a user"), NULL); - purple_cmd_register("motd", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_motd, - _("motd: View the server's Message Of The Day"), NULL); - purple_cmd_register("detach", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_detach, - _("detach: Detach this session"), NULL); - purple_cmd_register("quit", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_quit, - _("quit [message]: Disconnect from the server, with an optional message"), NULL); - purple_cmd_register("call", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_call, - _("call <command>: Call any silc client command"), NULL); - /* These below just get passed through for the silc client library to deal - * with */ - purple_cmd_register("kill", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, - _("kill <nick> [-pubkey|<reason>]: Kill nick"), NULL); - purple_cmd_register("nick", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_generic, - _("nick <newnick>: Change your nickname"), NULL); - purple_cmd_register("whowas", "ww", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, - _("whowas <nick>: View nick's information"), NULL); - purple_cmd_register("cmode", "wws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_cmode, - _("cmode <channel> [+|-<modes>] [arguments]: Change or display channel modes"), NULL); - purple_cmd_register("cumode", "wws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, - _("cumode <channel> +|-<modes> <nick>: Change nick's modes on channel"), NULL); - purple_cmd_register("umode", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_generic, - _("umode <usermodes>: Set your modes in the network"), NULL); - purple_cmd_register("oper", "s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_generic, - _("oper <nick> [-pubkey]: Get server operator privileges"), NULL); - purple_cmd_register("invite", "ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, - _("invite <channel> [-|+]<nick>: invite nick or add/remove from channel invite list"), NULL); - purple_cmd_register("kick", "wws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, - _("kick <channel> <nick> [comment]: Kick client from channel"), NULL); - purple_cmd_register("info", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, - _("info [server]: View server administrative details"), NULL); - purple_cmd_register("ban", "ww", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_generic, - _("ban [<channel> +|-<nick>]: Ban client from channel"), NULL); - purple_cmd_register("getkey", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_generic, - _("getkey <nick|server>: Retrieve client's or server's public key"), NULL); - purple_cmd_register("stats", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_generic, - _("stats: View server and network statistics"), NULL); - purple_cmd_register("ping", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_generic, - _("ping: Send PING to the connected server"), NULL); -#if 0 /* Purple doesn't handle these yet */ - purple_cmd_register("users", "w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-silc", silcpurple_cmd_users, - _("users <channel>: List users in channel")); - purple_cmd_register("names", "ww", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | - PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-silc", silcpurple_cmd_names, - _("names [-count|-ops|-halfops|-voices|-normal] <channel(s)>: List specific users in channel(s)")); -#endif -} - -static PurpleWhiteboardPrplOps silcpurple_wb_ops = -{ - silcpurple_wb_start, - silcpurple_wb_end, - silcpurple_wb_get_dimensions, - silcpurple_wb_set_dimensions, - silcpurple_wb_get_brush, - silcpurple_wb_set_brush, - silcpurple_wb_send, - silcpurple_wb_clear, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static PurplePluginProtocolInfo prpl_info = -{ -#ifdef HAVE_SILCMIME_H - OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | - OPT_PROTO_PASSWORD_OPTIONAL | OPT_PROTO_IM_IMAGE | - OPT_PROTO_SLASH_COMMANDS_NATIVE, -#else - OPT_PROTO_CHAT_TOPIC | OPT_PROTO_UNIQUE_CHATNAME | - OPT_PROTO_PASSWORD_OPTIONAL | - OPT_PROTO_SLASH_COMMANDS_NATIVE, -#endif - NULL, /* user_splits */ - NULL, /* protocol_options */ -#ifdef SILC_ATTRIBUTE_USER_ICON - {"jpeg,gif,png,bmp", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ -#else - NO_BUDDY_ICONS, -#endif - silcpurple_list_icon, /* list_icon */ - NULL, /* list_emblems */ - silcpurple_status_text, /* status_text */ - silcpurple_tooltip_text, /* tooltip_text */ - silcpurple_away_states, /* away_states */ - silcpurple_blist_node_menu, /* blist_node_menu */ - silcpurple_chat_info, /* chat_info */ - silcpurple_chat_info_defaults,/* chat_info_defaults */ - silcpurple_login, /* login */ - silcpurple_close, /* close */ - silcpurple_send_im, /* send_im */ - silcpurple_set_info, /* set_info */ - NULL, /* send_typing */ - silcpurple_get_info, /* get_info */ - silcpurple_set_status, /* set_status */ - silcpurple_idle_set, /* set_idle */ - silcpurple_change_passwd, /* change_passwd */ - silcpurple_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - silcpurple_remove_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - NULL, /* add_deny */ - NULL, /* rem_permit */ - NULL, /* rem_deny */ - NULL, /* set_permit_deny */ - silcpurple_chat_join, /* join_chat */ - NULL, /* reject_chat */ - silcpurple_get_chat_name, /* get_chat_name */ - silcpurple_chat_invite, /* chat_invite */ - silcpurple_chat_leave, /* chat_leave */ - NULL, /* chat_whisper */ - silcpurple_chat_send, /* chat_send */ - silcpurple_keepalive, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - NULL, /* get_cb_away */ - NULL, /* alias_buddy */ - NULL, /* group_buddy */ - NULL, /* rename_group */ - NULL, /* buddy_free */ - NULL, /* convo_closed */ - NULL, /* normalize */ -#ifdef SILC_ATTRIBUTE_USER_ICON - silcpurple_buddy_set_icon, /* set_buddy_icon */ -#else - NULL, -#endif - NULL, /* remove_group */ - NULL, /* get_cb_real_name */ - silcpurple_chat_set_topic, /* set_chat_topic */ - NULL, /* find_blist_chat */ - silcpurple_roomlist_get_list, /* roomlist_get_list */ - silcpurple_roomlist_cancel, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - NULL, /* can_receive_file */ - silcpurple_ftp_send_file, /* send_file */ - silcpurple_ftp_new_xfer, /* new_xfer */ - NULL, /* offline_message */ - &silcpurple_wb_ops, /* whiteboard_prpl_ops */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - NULL, /* send_attention */ - NULL, /* get_attention_types */ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - NULL, /* add_buddy_with_invite */ - NULL, /* add_buddies_with_invite */ - NULL, /* get_cb_alias */ - NULL, /* chat_can_receive_file */ - NULL, /* chat_send_file */ -}; - -static PurplePluginInfo info = -{ - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-silc", /**< id */ - "SILC", /**< name */ - "1.0", /**< version */ - /** summary */ - N_("SILC Protocol Plugin"), - /** description */ - N_("Secure Internet Live Conferencing (SILC) Protocol"), - "Pekka Riikonen", /**< author */ - "http://silcnet.org/", /**< homepage */ - - NULL, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, /**< prefs_info */ - silcpurple_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ - PurpleAccountOption *option; - PurpleAccountUserSplit *split; - char tmp[256]; - int i; - PurpleKeyValuePair *kvp; - GList *list = NULL; - - silc_plugin = plugin; - - split = purple_account_user_split_new(_("Network"), "silcnet.org", '@'); - prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); - - /* Account options */ - option = purple_account_option_string_new(_("Connect server"), - "server", - "silc.silcnet.org"); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_int_new(_("Port"), "port", 706); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir()); - option = purple_account_option_string_new(_("Public Key file"), - "public-key", tmp); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - g_snprintf(tmp, sizeof(tmp), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir()); - option = purple_account_option_string_new(_("Private Key file"), - "private-key", tmp); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - for (i = 0; silc_default_ciphers[i].name; i++) { - kvp = g_new0(PurpleKeyValuePair, 1); - kvp->key = g_strdup(silc_default_ciphers[i].name); - kvp->value = g_strdup(silc_default_ciphers[i].name); - list = g_list_append(list, kvp); - } - option = purple_account_option_list_new(_("Cipher"), "cipher", list); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - list = NULL; - for (i = 0; silc_default_hmacs[i].name; i++) { - kvp = g_new0(PurpleKeyValuePair, 1); - kvp->key = g_strdup(silc_default_hmacs[i].name); - kvp->value = g_strdup(silc_default_hmacs[i].name); - list = g_list_append(list, kvp); - } - option = purple_account_option_list_new(_("HMAC"), "hmac", list); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_bool_new(_("Public key authentication"), - "pubkey-auth", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_bool_new(_("Block IMs without Key Exchange"), - "block-ims", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_bool_new(_("Block messages to whiteboard"), - "block-wb", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_bool_new(_("Automatically open whiteboard"), - "open-wb", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - option = purple_account_option_bool_new(_("Digitally sign and verify all messages"), - "sign-verify", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - purple_prefs_remove("/plugins/prpl/silc"); - - silcpurple_register_commands(); - -#ifdef _WIN32 - silc_net_win32_init(); -#endif -} - -PURPLE_INIT_PLUGIN(silc10, init_plugin, info); diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc10/silcpurple.h --- a/libpurple/protocols/silc10/silcpurple.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,173 +0,0 @@ -/* - - silcpurple.h - - Author: Pekka Riikonen - - Copyright (C) 2004 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#ifndef SILCPURPLE_H -#define SILCPURPLE_H - -/* Purple includes */ -#include "internal.h" -#include "account.h" -#include "accountopt.h" -#include "cmds.h" -#include "conversation.h" -#include "debug.h" -#include "ft.h" -#include "notify.h" -#include "prpl.h" -#include "request.h" -#include "roomlist.h" -#include "server.h" -#include "util.h" - -/* Default public and private key file names */ -#define SILCPURPLE_PUBLIC_KEY_NAME "public_key.pub" -#define SILCPURPLE_PRIVATE_KEY_NAME "private_key.prv" - -/* Default settings for creating key pair */ -#define SILCPURPLE_DEF_PKCS "rsa" -#define SILCPURPLE_DEF_PKCS_LEN 2048 - -#define SILCPURPLE_PRVGRP 0x001fffff - -/* Status IDs */ -#define SILCPURPLE_STATUS_ID_OFFLINE "offline" -#define SILCPURPLE_STATUS_ID_AVAILABLE "available" -#define SILCPURPLE_STATUS_ID_HYPER "hyper" -#define SILCPURPLE_STATUS_ID_AWAY "away" -#define SILCPURPLE_STATUS_ID_BUSY "busy" -#define SILCPURPLE_STATUS_ID_INDISPOSED "indisposed" -#define SILCPURPLE_STATUS_ID_PAGE "page" - -typedef struct { - unsigned long id; - const char *channel; - unsigned long chid; - const char *parentch; - SilcChannelPrivateKey key; -} *SilcPurplePrvgrp; - -/* The SILC Purple plugin context */ -typedef struct SilcPurpleStruct { - SilcClient client; - SilcClientConnection conn; - - guint scheduler; - PurpleConnection *gc; - PurpleAccount *account; - unsigned long channel_ids; - GList *grps; - - char *motd; - PurpleRoomlist *roomlist; -#ifdef HAVE_SILCMIME_H - SilcMimeAssembler mimeass; -#endif - unsigned int detaching : 1; - unsigned int resuming : 1; - unsigned int roomlist_cancelled : 1; - unsigned int chpk : 1; -} *SilcPurple; - - -gboolean silcpurple_check_silc_dir(PurpleConnection *gc); -void silcpurple_chat_join_done(SilcClient client, - SilcClientConnection conn, - SilcClientEntry *clients, - SilcUInt32 clients_count, - void *context); -const char *silcpurple_silcdir(void); -const char *silcpurple_session_file(const char *account); -void silcpurple_verify_public_key(SilcClient client, SilcClientConnection conn, - const char *name, SilcSocketType conn_type, - unsigned char *pk, SilcUInt32 pk_len, - SilcSKEPKType pk_type, - SilcVerifyPublicKey completion, void *context); -GList *silcpurple_buddy_menu(PurpleBuddy *buddy); -void silcpurple_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); -void silcpurple_send_buddylist(PurpleConnection *gc); -void silcpurple_remove_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group); -void silcpurple_buddy_keyagr_request(SilcClient client, - SilcClientConnection conn, - SilcClientEntry client_entry, - const char *hostname, SilcUInt16 port); -void silcpurple_idle_set(PurpleConnection *gc, int idle); -void silcpurple_tooltip_text(PurpleBuddy *b, PurpleNotifyUserInfo *user_info, gboolean full); -char *silcpurple_status_text(PurpleBuddy *b); -gboolean silcpurple_ip_is_private(const char *ip); -void silcpurple_ftp_send_file(PurpleConnection *gc, const char *name, const char *file); -PurpleXfer *silcpurple_ftp_new_xfer(PurpleConnection *gc, const char *name); -void silcpurple_ftp_request(SilcClient client, SilcClientConnection conn, - SilcClientEntry client_entry, SilcUInt32 session_id, - const char *hostname, SilcUInt16 port); -void silcpurple_show_public_key(SilcPurple sg, - const char *name, SilcPublicKey public_key, - GCallback callback, void *context); -void silcpurple_get_info(PurpleConnection *gc, const char *who); -SilcAttributePayload -silcpurple_get_attr(SilcDList attrs, SilcAttribute attribute); -void silcpurple_get_umode_string(SilcUInt32 mode, char *buf, - SilcUInt32 buf_size); -void silcpurple_get_chmode_string(SilcUInt32 mode, char *buf, - SilcUInt32 buf_size); -void silcpurple_get_chumode_string(SilcUInt32 mode, char *buf, - SilcUInt32 buf_size); -GList *silcpurple_chat_info(PurpleConnection *gc); -GHashTable *silcpurple_chat_info_defaults(PurpleConnection *gc, const char *chat_name); -GList *silcpurple_chat_menu(PurpleChat *); -void silcpurple_chat_join(PurpleConnection *gc, GHashTable *data); -char *silcpurple_get_chat_name(GHashTable *data); -void silcpurple_chat_invite(PurpleConnection *gc, int id, const char *msg, - const char *name); -void silcpurple_chat_leave(PurpleConnection *gc, int id); -int silcpurple_chat_send(PurpleConnection *gc, int id, const char *msg, PurpleMessageFlags flags); -void silcpurple_chat_set_topic(PurpleConnection *gc, int id, const char *topic); -PurpleRoomlist *silcpurple_roomlist_get_list(PurpleConnection *gc); -void silcpurple_roomlist_cancel(PurpleRoomlist *list); -void silcpurple_chat_chauth_show(SilcPurple sg, SilcChannelEntry channel, - SilcBuffer channel_pubkeys); -void silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr, - char **contactstr, char **langstr, char **devicestr, - char **tzstr, char **geostr); -#ifdef SILC_ATTRIBUTE_USER_ICON -void silcpurple_buddy_set_icon(PurpleConnection *gc, PurpleStoredImage *img); -#endif -#ifdef HAVE_SILCMIME_H -char *silcpurple_file2mime(const char *filename); -SilcDList silcpurple_image_message(const char *msg, SilcUInt32 *mflags); -#endif - -#ifdef _WIN32 -typedef int uid_t; - -struct passwd { - char *pw_name; /* user name */ - char *pw_passwd; /* user password */ - int pw_uid; /* user id */ - int pw_gid; /* group id */ - char *pw_gecos; /* real name */ - char *pw_dir; /* home directory */ - char *pw_shell; /* shell program */ -}; - -struct passwd *getpwuid(int uid); -int getuid(void); -int geteuid(void); -#endif - -#endif /* SILCPURPLE_H */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc10/util.c --- a/libpurple/protocols/silc10/util.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,774 +0,0 @@ -/* - - silcpurple_util.c - - Author: Pekka Riikonen - - Copyright (C) 2004 - 2005 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#include "silcincludes.h" -#include "silcclient.h" -#include "silcpurple.h" -#include "imgstore.h" - -/**************************** Utility Routines *******************************/ - -static char str[256], str2[256]; - -const char *silcpurple_silcdir(void) -{ - const char *hd = purple_home_dir(); - memset(str, 0, sizeof(str)); - g_snprintf(str, sizeof(str) - 1, "%s" G_DIR_SEPARATOR_S ".silc", hd ? hd : "/tmp"); - return (const char *)str; -} - -const char *silcpurple_session_file(const char *account) -{ - memset(str2, 0, sizeof(str2)); - g_snprintf(str2, sizeof(str2) - 1, "%s" G_DIR_SEPARATOR_S "%s_session", - silcpurple_silcdir(), account); - return (const char *)str2; -} - -gboolean silcpurple_ip_is_private(const char *ip) -{ - if (silc_net_is_ip4(ip)) { - if (!strncmp(ip, "10.", 3)) { - return TRUE; - } else if (!strncmp(ip, "172.", 4) && strlen(ip) > 6) { - char tmp[3]; - int s; - memset(tmp, 0, sizeof(tmp)); - strncpy(tmp, ip + 4, 2); - s = atoi(tmp); - if (s >= 16 && s <= 31) - return TRUE; - } else if (!strncmp(ip, "192.168.", 8)) { - return TRUE; - } - } - - return FALSE; -} - -/* This checks stats for various SILC files and directories. First it - checks if ~/.silc directory exist and is owned by the correct user. If - it doesn't exist, it will create the directory. After that it checks if - user's Public and Private key files exists and creates them if needed. */ - -gboolean silcpurple_check_silc_dir(PurpleConnection *gc) -{ - char filename[256], file_public_key[256], file_private_key[256]; - char servfilename[256], clientfilename[256], friendsfilename[256]; - char pkd[256], prd[256]; - struct stat st; - struct passwd *pw; - int fd; - - pw = getpwuid(getuid()); - if (!pw) { - purple_debug_error("silc", "silc: %s\n", g_strerror(errno)); - return FALSE; - } - - g_snprintf(filename, sizeof(filename) - 1, "%s", silcpurple_silcdir()); - g_snprintf(servfilename, sizeof(servfilename) - 1, "%s" G_DIR_SEPARATOR_S "serverkeys", - silcpurple_silcdir()); - g_snprintf(clientfilename, sizeof(clientfilename) - 1, "%s" G_DIR_SEPARATOR_S "clientkeys", - silcpurple_silcdir()); - g_snprintf(friendsfilename, sizeof(friendsfilename) - 1, "%s" G_DIR_SEPARATOR_S "friends", - silcpurple_silcdir()); - - /* - * Check ~/.silc directory - */ - if ((g_stat(filename, &st)) == -1) { - /* If dir doesn't exist */ - if (errno == ENOENT) { - if (pw->pw_uid == geteuid()) { - if ((g_mkdir(filename, 0755)) == -1) { - purple_debug_error("silc", "Couldn't create '%s' directory\n", filename); - return FALSE; - } - } else { - purple_debug_error("silc", "Couldn't create '%s' directory due to a wrong uid!\n", - filename); - return FALSE; - } - } else { - purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n", filename, g_strerror(errno)); - return FALSE; - } - } else { -#ifndef _WIN32 - /* Check the owner of the dir */ - if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { - purple_debug_error("silc", "You don't seem to own '%s' directory\n", - filename); - return FALSE; - } -#endif - } - - /* - * Check ~./silc/serverkeys directory - */ - if ((g_stat(servfilename, &st)) == -1) { - /* If dir doesn't exist */ - if (errno == ENOENT) { - if (pw->pw_uid == geteuid()) { - if ((g_mkdir(servfilename, 0755)) == -1) { - purple_debug_error("silc", "Couldn't create '%s' directory\n", servfilename); - return FALSE; - } - } else { - purple_debug_error("silc", "Couldn't create '%s' directory due to a wrong uid!\n", - servfilename); - return FALSE; - } - } else { - purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n", - servfilename, g_strerror(errno)); - return FALSE; - } - } - - /* - * Check ~./silc/clientkeys directory - */ - if ((g_stat(clientfilename, &st)) == -1) { - /* If dir doesn't exist */ - if (errno == ENOENT) { - if (pw->pw_uid == geteuid()) { - if ((g_mkdir(clientfilename, 0755)) == -1) { - purple_debug_error("silc", "Couldn't create '%s' directory\n", clientfilename); - return FALSE; - } - } else { - purple_debug_error("silc", "Couldn't create '%s' directory due to a wrong uid!\n", - clientfilename); - return FALSE; - } - } else { - purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n", - clientfilename, g_strerror(errno)); - return FALSE; - } - } - - /* - * Check ~./silc/friends directory - */ - if ((g_stat(friendsfilename, &st)) == -1) { - /* If dir doesn't exist */ - if (errno == ENOENT) { - if (pw->pw_uid == geteuid()) { - if ((g_mkdir(friendsfilename, 0755)) == -1) { - purple_debug_error("silc", "Couldn't create '%s' directory\n", friendsfilename); - return FALSE; - } - } else { - purple_debug_error("silc", "Couldn't create '%s' directory due to a wrong uid!\n", - friendsfilename); - return FALSE; - } - } else { - purple_debug_error("silc", "Couldn't stat '%s' directory, error: %s\n", - friendsfilename, g_strerror(errno)); - return FALSE; - } - } - - /* - * Check Public and Private keys - */ - g_snprintf(pkd, sizeof(pkd), "%s" G_DIR_SEPARATOR_S "public_key.pub", silcpurple_silcdir()); - g_snprintf(prd, sizeof(prd), "%s" G_DIR_SEPARATOR_S "private_key.prv", silcpurple_silcdir()); - g_snprintf(file_public_key, sizeof(file_public_key) - 1, "%s", - purple_account_get_string(gc->account, "public-key", pkd)); - g_snprintf(file_private_key, sizeof(file_public_key) - 1, "%s", - purple_account_get_string(gc->account, "private-key", prd)); - - if ((g_stat(file_public_key, &st)) == -1) { - /* If file doesn't exist */ - if (errno == ENOENT) { - purple_connection_update_progress(gc, _("Creating SILC key pair..."), 1, 5); - if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS, - SILCPURPLE_DEF_PKCS_LEN, - file_public_key, file_private_key, NULL, - (gc->password == NULL) ? "" : gc->password, - NULL, NULL, NULL, FALSE)) { - purple_debug_error("silc", "Couldn't create key pair\n"); - return FALSE; - } - - if ((g_stat(file_public_key, &st)) == -1) { - purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n", - file_public_key, g_strerror(errno)); - return FALSE; - } - } else { - purple_debug_error("silc", "Couldn't stat '%s' public key, error: %s\n", - file_public_key, g_strerror(errno)); - return FALSE; - } - } - -#ifndef _WIN32 - /* Check the owner of the public key */ - if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { - purple_debug_error("silc", "You don't seem to own your public key!?\n"); - return FALSE; - } -#endif - - if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) { - if ((fstat(fd, &st)) == -1) { - purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n", - file_private_key, g_strerror(errno)); - close(fd); - return FALSE; - } - } else if ((g_stat(file_private_key, &st)) == -1) { - /* If file doesn't exist */ - if (errno == ENOENT) { - purple_connection_update_progress(gc, _("Creating SILC key pair..."), 1, 5); - if (!silc_create_key_pair(SILCPURPLE_DEF_PKCS, - SILCPURPLE_DEF_PKCS_LEN, - file_public_key, file_private_key, NULL, - (gc->password == NULL) ? "" : gc->password, - NULL, NULL, NULL, FALSE)) { - purple_debug_error("silc", "Couldn't create key pair\n"); - return FALSE; - } - - if ((fd = g_open(file_private_key, O_RDONLY, 0)) != -1) { - if ((fstat(fd, &st)) == -1) { - purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n", - file_private_key, g_strerror(errno)); - close(fd); - return FALSE; - } - } - /* This shouldn't really happen because silc_create_key_pair() - * will set the permissions */ - else if ((g_stat(file_private_key, &st)) == -1) { - purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n", - file_private_key, g_strerror(errno)); - return FALSE; - } - } else { - purple_debug_error("silc", "Couldn't stat '%s' private key, error: %s\n", - file_private_key, g_strerror(errno)); - return FALSE; - } - } - -#ifndef _WIN32 - /* Check the owner of the private key */ - if (st.st_uid != 0 && st.st_uid != pw->pw_uid) { - purple_debug_error("silc", "You don't seem to own your private key!?\n"); - if (fd != -1) - close(fd); - return FALSE; - } - - /* Check the permissions for the private key */ - if ((st.st_mode & 0777) != 0600) { - purple_debug_warning("silc", "Wrong permissions in your private key file `%s'!\n" - "Trying to change them ...\n", file_private_key); - if ((fd == -1) || (fchmod(fd, S_IRUSR | S_IWUSR)) == -1) { - purple_debug_error("silc", - "Failed to change permissions for private key file!\n" - "Permissions for your private key file must be 0600.\n"); - if (fd != -1) - close(fd); - return FALSE; - } - purple_debug_warning("silc", "Done.\n\n"); - } -#endif - - if (fd != -1) - close(fd); - - return TRUE; -} - -#ifdef _WIN32 -struct passwd *getpwuid(uid_t uid) { - struct passwd *pwd = calloc(1, sizeof(struct passwd)); - return pwd; -} - -uid_t getuid() { - return 0; -} - -uid_t geteuid() { - return 0; -} -#endif - -void silcpurple_show_public_key(SilcPurple sg, - const char *name, SilcPublicKey public_key, - GCallback callback, void *context) -{ - SilcPublicKeyIdentifier ident; - SilcPKCS pkcs; - char *fingerprint, *babbleprint; - unsigned char *pk; - SilcUInt32 pk_len, key_len = 0; - GString *s; - char *buf; - - ident = silc_pkcs_decode_identifier(public_key->identifier); - if (!ident) - return; - - pk = silc_pkcs_public_key_encode(public_key, &pk_len); - fingerprint = silc_hash_fingerprint(NULL, pk, pk_len); - babbleprint = silc_hash_babbleprint(NULL, pk, pk_len); - - if (silc_pkcs_alloc((unsigned char *)public_key->name, &pkcs)) { - key_len = silc_pkcs_public_key_set(pkcs, public_key); - silc_pkcs_free(pkcs); - } - - s = g_string_new(""); - if (ident->realname) - /* Hint for translators: Please check the tabulator width here and in - the next strings (short strings: 2 tabs, longer strings 1 tab, - sum: 3 tabs or 24 characters) */ - g_string_append_printf(s, _("Real Name: \t%s\n"), ident->realname); - if (ident->username) - g_string_append_printf(s, _("User Name: \t%s\n"), ident->username); - if (ident->email) - g_string_append_printf(s, _("Email: \t\t%s\n"), ident->email); - if (ident->host) - g_string_append_printf(s, _("Host Name: \t%s\n"), ident->host); - if (ident->org) - g_string_append_printf(s, _("Organization: \t%s\n"), ident->org); - if (ident->country) - g_string_append_printf(s, _("Country: \t%s\n"), ident->country); - g_string_append_printf(s, _("Algorithm: \t%s\n"), public_key->name); - g_string_append_printf(s, _("Key Length: \t%d bits\n"), (int)key_len); - g_string_append_printf(s, "\n"); - g_string_append_printf(s, _("Public Key Fingerprint:\n%s\n\n"), fingerprint); - g_string_append_printf(s, _("Public Key Babbleprint:\n%s"), babbleprint); - - buf = g_string_free(s, FALSE); - - purple_request_action(sg->gc, _("Public Key Information"), - _("Public Key Information"), - buf, 0, purple_connection_get_account(sg->gc), - NULL, NULL, context, 1, _("Close"), callback); - - g_free(buf); - silc_free(fingerprint); - silc_free(babbleprint); - silc_free(pk); - silc_pkcs_free_identifier(ident); -} - -SilcAttributePayload -silcpurple_get_attr(SilcDList attrs, SilcAttribute attribute) -{ - SilcAttributePayload attr = NULL; - - if (!attrs) - return NULL; - - silc_dlist_start(attrs); - while ((attr = silc_dlist_get(attrs)) != SILC_LIST_END) - if (attribute == silc_attribute_get_attribute(attr)) - break; - - return attr; -} - -void silcpurple_get_umode_string(SilcUInt32 mode, char *buf, - SilcUInt32 buf_size) -{ - memset(buf, 0, buf_size); - if ((mode & SILC_UMODE_SERVER_OPERATOR) || - (mode & SILC_UMODE_ROUTER_OPERATOR)) { - strcat(buf, (mode & SILC_UMODE_SERVER_OPERATOR) ? - "[server operator] " : - (mode & SILC_UMODE_ROUTER_OPERATOR) ? - "[SILC operator] " : "[unknown mode] "); - } - if (mode & SILC_UMODE_GONE) - strcat(buf, "[away] "); - if (mode & SILC_UMODE_INDISPOSED) - strcat(buf, "[indisposed] "); - if (mode & SILC_UMODE_BUSY) - strcat(buf, "[busy] "); - if (mode & SILC_UMODE_PAGE) - strcat(buf, "[wake me up] "); - if (mode & SILC_UMODE_HYPER) - strcat(buf, "[hyperactive] "); - if (mode & SILC_UMODE_ROBOT) - strcat(buf, "[robot] "); - if (mode & SILC_UMODE_ANONYMOUS) - strcat(buf, "[anonymous] "); - if (mode & SILC_UMODE_BLOCK_PRIVMSG) - strcat(buf, "[blocks private messages] "); - if (mode & SILC_UMODE_DETACHED) - strcat(buf, "[detached] "); - if (mode & SILC_UMODE_REJECT_WATCHING) - strcat(buf, "[rejects watching] "); - if (mode & SILC_UMODE_BLOCK_INVITE) - strcat(buf, "[blocks invites] "); - g_strchomp(buf); -} - -void silcpurple_get_chmode_string(SilcUInt32 mode, char *buf, - SilcUInt32 buf_size) -{ - memset(buf, 0, buf_size); - if (mode & SILC_CHANNEL_MODE_FOUNDER_AUTH) - strcat(buf, "[permanent] "); - if (mode & SILC_CHANNEL_MODE_PRIVATE) - strcat(buf, "[private] "); - if (mode & SILC_CHANNEL_MODE_SECRET) - strcat(buf, "[secret] "); - if (mode & SILC_CHANNEL_MODE_PRIVKEY) - strcat(buf, "[private key] "); - if (mode & SILC_CHANNEL_MODE_INVITE) - strcat(buf, "[invite only] "); - if (mode & SILC_CHANNEL_MODE_TOPIC) - strcat(buf, "[topic restricted] "); - if (mode & SILC_CHANNEL_MODE_ULIMIT) - strcat(buf, "[user count limit] "); - if (mode & SILC_CHANNEL_MODE_PASSPHRASE) - strcat(buf, "[passphrase auth] "); - if (mode & SILC_CHANNEL_MODE_CHANNEL_AUTH) - strcat(buf, "[public key auth] "); - if (mode & SILC_CHANNEL_MODE_SILENCE_USERS) - strcat(buf, "[users silenced] "); - if (mode & SILC_CHANNEL_MODE_SILENCE_OPERS) - strcat(buf, "[operators silenced] "); - g_strchomp(buf); -} - -void silcpurple_get_chumode_string(SilcUInt32 mode, char *buf, - SilcUInt32 buf_size) -{ - memset(buf, 0, buf_size); - if (mode & SILC_CHANNEL_UMODE_CHANFO) - strcat(buf, "[founder] "); - if (mode & SILC_CHANNEL_UMODE_CHANOP) - strcat(buf, "[operator] "); - if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES) - strcat(buf, "[blocks messages] "); - if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_USERS) - strcat(buf, "[blocks user messages] "); - if (mode & SILC_CHANNEL_UMODE_BLOCK_MESSAGES_ROBOTS) - strcat(buf, "[blocks robot messages] "); - if (mode & SILC_CHANNEL_UMODE_QUIET) - strcat(buf, "[quieted] "); - g_strchomp(buf); -} - -void -silcpurple_parse_attrs(SilcDList attrs, char **moodstr, char **statusstr, - char **contactstr, char **langstr, char **devicestr, - char **tzstr, char **geostr) -{ - SilcAttributePayload attr; - SilcAttributeMood mood = 0; - SilcAttributeContact contact; - SilcAttributeObjDevice device; - SilcAttributeObjGeo geo; - - char tmp[1024]; - GString *s; - - *moodstr = NULL; - *statusstr = NULL; - *contactstr = NULL; - *langstr = NULL; - *devicestr = NULL; - *tzstr = NULL; - *geostr = NULL; - - if (!attrs) - return; - - s = g_string_new(""); - attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_STATUS_MOOD); - if (attr && silc_attribute_get_object(attr, &mood, sizeof(mood))) { - if (mood & SILC_ATTRIBUTE_MOOD_HAPPY) - g_string_append_printf(s, "[%s] ", _("Happy")); - if (mood & SILC_ATTRIBUTE_MOOD_SAD) - g_string_append_printf(s, "[%s] ", _("Sad")); - if (mood & SILC_ATTRIBUTE_MOOD_ANGRY) - g_string_append_printf(s, "[%s] ", _("Angry")); - if (mood & SILC_ATTRIBUTE_MOOD_JEALOUS) - g_string_append_printf(s, "[%s] ", _("Jealous")); - if (mood & SILC_ATTRIBUTE_MOOD_ASHAMED) - g_string_append_printf(s, "[%s] ", _("Ashamed")); - if (mood & SILC_ATTRIBUTE_MOOD_INVINCIBLE) - g_string_append_printf(s, "[%s] ", _("Invincible")); - if (mood & SILC_ATTRIBUTE_MOOD_INLOVE) - g_string_append_printf(s, "[%s] ", _("In Love")); - if (mood & SILC_ATTRIBUTE_MOOD_SLEEPY) - g_string_append_printf(s, "[%s] ", _("Sleepy")); - if (mood & SILC_ATTRIBUTE_MOOD_BORED) - g_string_append_printf(s, "[%s] ", _("Bored")); - if (mood & SILC_ATTRIBUTE_MOOD_EXCITED) - g_string_append_printf(s, "[%s] ", _("Excited")); - if (mood & SILC_ATTRIBUTE_MOOD_ANXIOUS) - g_string_append_printf(s, "[%s] ", _("Anxious")); - } - if (s->len != 0) { - *moodstr = g_strchomp(g_string_free(s, FALSE)); - } else { - g_string_free(s, TRUE); - } - - attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_STATUS_FREETEXT); - memset(tmp, 0, sizeof(tmp)); - if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp))) - *statusstr = g_strdup(tmp); - - s = g_string_new(""); - attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_PREFERRED_CONTACT); - if (attr && silc_attribute_get_object(attr, &contact, sizeof(contact))) { - if (contact & SILC_ATTRIBUTE_CONTACT_CHAT) - g_string_append_printf(s, "[%s] ", _("Chat")); - if (contact & SILC_ATTRIBUTE_CONTACT_EMAIL) - g_string_append_printf(s, "[%s] ", _("Email")); - if (contact & SILC_ATTRIBUTE_CONTACT_CALL) - g_string_append_printf(s, "[%s] ", _("Phone")); - if (contact & SILC_ATTRIBUTE_CONTACT_PAGE) - g_string_append_printf(s, "[%s] ", _("Paging")); - if (contact & SILC_ATTRIBUTE_CONTACT_SMS) - g_string_append_printf(s, "[%s] ", _("SMS")); - if (contact & SILC_ATTRIBUTE_CONTACT_MMS) - g_string_append_printf(s, "[%s] ", _("MMS")); - if (contact & SILC_ATTRIBUTE_CONTACT_VIDEO) - g_string_append_printf(s, "[%s] ", _("Video Conferencing")); - } - if (s->len != 0) { - *contactstr = g_strchomp(g_string_free(s, FALSE)); - } else { - g_string_free(s, TRUE); - } - - attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_PREFERRED_LANGUAGE); - memset(tmp, 0, sizeof(tmp)); - if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp))) - *langstr = g_strdup(tmp); - - s = g_string_new(""); - attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_DEVICE_INFO); - memset(&device, 0, sizeof(device)); - if (attr && silc_attribute_get_object(attr, &device, sizeof(device))) { - if (device.type == SILC_ATTRIBUTE_DEVICE_COMPUTER) - g_string_append_printf(s, "%s: ", _("Computer")); - if (device.type == SILC_ATTRIBUTE_DEVICE_MOBILE_PHONE) - g_string_append_printf(s, "%s: ", _("Mobile Phone")); - if (device.type == SILC_ATTRIBUTE_DEVICE_PDA) - g_string_append_printf(s, "%s: ", _("PDA")); - if (device.type == SILC_ATTRIBUTE_DEVICE_TERMINAL) - g_string_append_printf(s, "%s: ", _("Terminal")); - g_string_append_printf(s, "%s %s %s %s", - device.manufacturer ? device.manufacturer : "", - device.version ? device.version : "", - device.model ? device.model : "", - device.language ? device.language : ""); - } - if (s->len != 0) { - *devicestr = g_string_free(s, FALSE); - } else { - g_string_free(s, TRUE); - } - - attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_TIMEZONE); - memset(tmp, 0, sizeof(tmp)); - if (attr && silc_attribute_get_object(attr, tmp, sizeof(tmp))) - *tzstr = g_strdup(tmp); - - attr = silcpurple_get_attr(attrs, SILC_ATTRIBUTE_GEOLOCATION); - memset(&geo, 0, sizeof(geo)); - if (attr && silc_attribute_get_object(attr, &geo, sizeof(geo))) - *geostr = g_strdup_printf("%s %s %s (%s)", - geo.longitude ? geo.longitude : "", - geo.latitude ? geo.latitude : "", - geo.altitude ? geo.altitude : "", - geo.accuracy ? geo.accuracy : ""); -} - -#ifdef HAVE_SILCMIME_H -/* Returns MIME type of filetype */ - -char *silcpurple_file2mime(const char *filename) -{ - const char *ct; - - ct = strrchr(filename, '.'); - if (!ct) - return NULL; - else if (!g_ascii_strcasecmp(".png", ct)) - return strdup("image/png"); - else if (!g_ascii_strcasecmp(".jpg", ct)) - return strdup("image/jpeg"); - else if (!g_ascii_strcasecmp(".jpeg", ct)) - return strdup("image/jpeg"); - else if (!g_ascii_strcasecmp(".gif", ct)) - return strdup("image/gif"); - else if (!g_ascii_strcasecmp(".tiff", ct)) - return strdup("image/tiff"); - - return NULL; -} - -/* Checks if message has images, and assembles MIME message if it has. - If only one image is present, creates simple MIME image message. If - there are multiple images and/or text with images multipart MIME - message is created. */ - -SilcDList silcpurple_image_message(const char *msg, SilcUInt32 *mflags) -{ - SilcMime mime = NULL, p; - SilcDList list, parts = NULL; - const char *start, *end, *last; - GData *attribs; - char *type; - gboolean images = FALSE; - - last = msg; - while (last && *last && purple_markup_find_tag("img", last, &start, - &end, &attribs)) { - PurpleStoredImage *image = NULL; - const char *id; - - /* Check if there is text before image */ - if (start - last) { - char *text, *tmp; - p = silc_mime_alloc(); - - /* Add content type */ - silc_mime_add_field(p, "Content-Type", - "text/plain; charset=utf-8"); - - tmp = g_strndup(last, start - last); - text = purple_unescape_html(tmp); - g_free(tmp); - /* Add text */ - silc_mime_add_data(p, (unsigned char *)text, strlen(text)); - g_free(text); - - if (!parts) - parts = silc_dlist_init(); - silc_dlist_add(parts, p); - } - - id = g_datalist_get_data(&attribs, "id"); - if (id && (image = purple_imgstore_find_by_id(atoi(id)))) { - unsigned long imglen = purple_imgstore_get_size(image); - gconstpointer img = purple_imgstore_get_data(image); - - p = silc_mime_alloc(); - - /* Add content type */ - type = silcpurple_file2mime(purple_imgstore_get_filename(image)); - if (!type) { - g_datalist_clear(&attribs); - last = end + 1; - continue; - } - silc_mime_add_field(p, "Content-Type", type); - silc_free(type); - - /* Add content transfer encoding */ - silc_mime_add_field(p, "Content-Transfer-Encoding", "binary"); - - /* Add image data */ - silc_mime_add_data(p, img, imglen); - - if (!parts) - parts = silc_dlist_init(); - silc_dlist_add(parts, p); - images = TRUE; - } - - g_datalist_clear(&attribs); - - /* Continue after tag */ - last = end + 1; - } - - /* Check for text after the image(s) */ - if (images && last && *last) { - char *tmp = purple_unescape_html(last); - p = silc_mime_alloc(); - - /* Add content type */ - silc_mime_add_field(p, "Content-Type", - "text/plain; charset=utf-8"); - - /* Add text */ - silc_mime_add_data(p, (unsigned char *)tmp, strlen(tmp)); - g_free(tmp); - - if (!parts) - parts = silc_dlist_init(); - silc_dlist_add(parts, p); - } - - /* If there weren't any images, don't return anything. */ - if (!images) { - if (parts) - silc_dlist_uninit(parts); - return NULL; - } - - if (silc_dlist_count(parts) > 1) { - /* Multipart MIME message */ - char b[32]; - mime = silc_mime_alloc(); - silc_mime_add_field(mime, "MIME-Version", "1.0"); - g_snprintf(b, sizeof(b), "b%4X%4X", - (unsigned int)time(NULL), - silc_dlist_count(parts)); - silc_mime_set_multipart(mime, "mixed", b); - silc_dlist_start(parts); - while ((p = silc_dlist_get(parts)) != SILC_LIST_END) - silc_mime_add_multipart(mime, p); - } else { - /* Simple MIME message */ - silc_dlist_start(parts); - mime = silc_dlist_get(parts); - silc_mime_add_field(mime, "MIME-Version", "1.0"); - } - - *mflags &= ~SILC_MESSAGE_FLAG_UTF8; - *mflags |= SILC_MESSAGE_FLAG_DATA; - - /* Encode message. Fragment if it is too large */ - list = silc_mime_encode_partial(mime, 0xfc00); - - silc_dlist_uninit(parts); - - /* Added multiparts gets freed here */ - silc_mime_free(mime); - - return list; -} - -#endif /* HAVE_SILCMIME_H */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc10/wb.c --- a/libpurple/protocols/silc10/wb.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,520 +0,0 @@ -/* - - wb.c - - Author: Pekka Riikonen - - Copyright (C) 2005 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#include "silcincludes.h" -#include "silcclient.h" -#include "silcpurple.h" -#include "wb.h" - -/* - SILC Whiteboard packet: - - 1 byte command - 2 bytes width - 2 bytes height - 4 bytes brush color - 2 bytes brush size - n bytes data - - Data: - - 4 bytes x - 4 bytes y - - Commands: - - 0x01 draw - 0x02 clear - - MIME: - - MIME-Version: 1.0 - Content-Type: application/x-wb - Content-Transfer-Encoding: binary - -*/ - -#define SILCPURPLE_WB_MIME "MIME-Version: 1.0\r\nContent-Type: application/x-wb\r\nContent-Transfer-Encoding: binary\r\n\r\n" -#define SILCPURPLE_WB_HEADER strlen(SILCPURPLE_WB_MIME) + 11 - -#define SILCPURPLE_WB_WIDTH 500 -#define SILCPURPLE_WB_HEIGHT 400 -#define SILCPURPLE_WB_WIDTH_MAX 1024 -#define SILCPURPLE_WB_HEIGHT_MAX 1024 - -/* Commands */ -typedef enum { - SILCPURPLE_WB_DRAW = 0x01, - SILCPURPLE_WB_CLEAR = 0x02 -} SilcPurpleWbCommand; - -/* Brush size */ -typedef enum { - SILCPURPLE_WB_BRUSH_SMALL = 2, - SILCPURPLE_WB_BRUSH_MEDIUM = 5, - SILCPURPLE_WB_BRUSH_LARGE = 10 -} SilcPurpleWbBrushSize; - -/* Brush color (XXX Purple should provide default colors) */ -typedef enum { - SILCPURPLE_WB_COLOR_BLACK = 0, - SILCPURPLE_WB_COLOR_RED = 13369344, - SILCPURPLE_WB_COLOR_GREEN = 52224, - SILCPURPLE_WB_COLOR_BLUE = 204, - SILCPURPLE_WB_COLOR_YELLOW = 15658496, - SILCPURPLE_WB_COLOR_ORANGE = 16737792, - SILCPURPLE_WB_COLOR_CYAN = 52428, - SILCPURPLE_WB_COLOR_VIOLET = 5381277, - SILCPURPLE_WB_COLOR_PURPLE = 13369548, - SILCPURPLE_WB_COLOR_TAN = 12093547, - SILCPURPLE_WB_COLOR_BROWN = 5256485, - SILCPURPLE_WB_COLOR_GREY = 11184810, - SILCPURPLE_WB_COLOR_WHITE = 16777215 -} SilcPurpleWbColor; - -typedef struct { - int type; /* 0 = buddy, 1 = channel */ - union { - SilcClientEntry client; - SilcChannelEntry channel; - } u; - int width; - int height; - int brush_size; - int brush_color; -} *SilcPurpleWb; - -/* Initialize whiteboard */ - -PurpleWhiteboard *silcpurple_wb_init(SilcPurple sg, SilcClientEntry client_entry) -{ - SilcClientConnection conn; - PurpleWhiteboard *wb; - SilcPurpleWb wbs; - - conn = sg->conn; - wb = purple_whiteboard_get_session(sg->account, client_entry->nickname); - if (!wb) - wb = purple_whiteboard_create(sg->account, client_entry->nickname, 0); - if (!wb) - return NULL; - - if (!wb->proto_data) { - wbs = silc_calloc(1, sizeof(*wbs)); - if (!wbs) - return NULL; - wbs->type = 0; - wbs->u.client = client_entry; - wbs->width = SILCPURPLE_WB_WIDTH; - wbs->height = SILCPURPLE_WB_HEIGHT; - wbs->brush_size = SILCPURPLE_WB_BRUSH_SMALL; - wbs->brush_color = SILCPURPLE_WB_COLOR_BLACK; - wb->proto_data = wbs; - - /* Start the whiteboard */ - purple_whiteboard_start(wb); - purple_whiteboard_clear(wb); - } - - return wb; -} - -PurpleWhiteboard *silcpurple_wb_init_ch(SilcPurple sg, SilcChannelEntry channel) -{ - PurpleWhiteboard *wb; - SilcPurpleWb wbs; - - wb = purple_whiteboard_get_session(sg->account, channel->channel_name); - if (!wb) - wb = purple_whiteboard_create(sg->account, channel->channel_name, 0); - if (!wb) - return NULL; - - if (!wb->proto_data) { - wbs = silc_calloc(1, sizeof(*wbs)); - if (!wbs) - return NULL; - wbs->type = 1; - wbs->u.channel = channel; - wbs->width = SILCPURPLE_WB_WIDTH; - wbs->height = SILCPURPLE_WB_HEIGHT; - wbs->brush_size = SILCPURPLE_WB_BRUSH_SMALL; - wbs->brush_color = SILCPURPLE_WB_COLOR_BLACK; - wb->proto_data = wbs; - - /* Start the whiteboard */ - purple_whiteboard_start(wb); - purple_whiteboard_clear(wb); - } - - return wb; -} - -static void -silcpurple_wb_parse(SilcPurpleWb wbs, PurpleWhiteboard *wb, - unsigned char *message, SilcUInt32 message_len) -{ - SilcUInt8 command; - SilcUInt16 width, height, brush_size; - SilcUInt32 brush_color, x, y, dx, dy; - SilcBufferStruct buf; - int ret; - - /* Parse the packet */ - silc_buffer_set(&buf, message, message_len); - ret = silc_buffer_unformat(&buf, - SILC_STR_UI_CHAR(&command), - SILC_STR_UI_SHORT(&width), - SILC_STR_UI_SHORT(&height), - SILC_STR_UI_INT(&brush_color), - SILC_STR_UI_SHORT(&brush_size), - SILC_STR_END); - if (ret < 0) - return; - silc_buffer_pull(&buf, ret); - - /* Update whiteboard if its dimensions changed */ - if (width != wbs->width || height != wbs->height) - silcpurple_wb_set_dimensions(wb, width, height); - - if (command == SILCPURPLE_WB_DRAW) { - /* Parse data and draw it */ - ret = silc_buffer_unformat(&buf, - SILC_STR_UI_INT(&dx), - SILC_STR_UI_INT(&dy), - SILC_STR_END); - if (ret < 0) - return; - silc_buffer_pull(&buf, 8); - x = dx; - y = dy; - while (buf.len > 0) { - ret = silc_buffer_unformat(&buf, - SILC_STR_UI_INT(&dx), - SILC_STR_UI_INT(&dy), - SILC_STR_END); - if (ret < 0) - return; - silc_buffer_pull(&buf, 8); - - purple_whiteboard_draw_line(wb, x, y, x + dx, y + dy, - brush_color, brush_size); - x += dx; - y += dy; - } - } - - if (command == SILCPURPLE_WB_CLEAR) - purple_whiteboard_clear(wb); -} - -typedef struct { - unsigned char *message; - SilcUInt32 message_len; - SilcPurple sg; - SilcClientEntry sender; - SilcChannelEntry channel; -} *SilcPurpleWbRequest; - -static void -silcpurple_wb_request_cb(SilcPurpleWbRequest req, gint id) -{ - PurpleWhiteboard *wb; - - if (id != 1) - goto out; - - if (!req->channel) - wb = silcpurple_wb_init(req->sg, req->sender); - else - wb = silcpurple_wb_init_ch(req->sg, req->channel); - - silcpurple_wb_parse(wb->proto_data, wb, req->message, req->message_len); - - out: - silc_free(req->message); - silc_free(req); -} - -static void -silcpurple_wb_request(SilcClient client, const unsigned char *message, - SilcUInt32 message_len, SilcClientEntry sender, - SilcChannelEntry channel) -{ - char tmp[128]; - SilcPurpleWbRequest req; - PurpleConnection *gc; - SilcPurple sg; - - gc = client->application; - sg = gc->proto_data; - - /* Open whiteboard automatically if requested */ - if (purple_account_get_bool(sg->account, "open-wb", FALSE)) { - PurpleWhiteboard *wb; - - if (!channel) - wb = silcpurple_wb_init(sg, sender); - else - wb = silcpurple_wb_init_ch(sg, channel); - - silcpurple_wb_parse(wb->proto_data, wb, (unsigned char *)message, - message_len); - return; - } - - /* Close any previous unaccepted requests */ - purple_request_close_with_handle(sender); - - if (!channel) { - g_snprintf(tmp, sizeof(tmp), - _("%s sent message to whiteboard. Would you like " - "to open the whiteboard?"), sender->nickname); - } else { - g_snprintf(tmp, sizeof(tmp), - _("%s sent message to whiteboard on %s channel. " - "Would you like to open the whiteboard?"), - sender->nickname, channel->channel_name); - } - - req = silc_calloc(1, sizeof(*req)); - if (!req) - return; - req->message = silc_memdup(message, message_len); - req->message_len = message_len; - req->sender = sender; - req->channel = channel; - req->sg = sg; - - purple_request_action(sender, _("Whiteboard"), tmp, NULL, 1, - sg->account, sender->nickname, NULL, req, 2, - _("Yes"), G_CALLBACK(silcpurple_wb_request_cb), - _("No"), G_CALLBACK(silcpurple_wb_request_cb)); -} - -/* Process incoming whiteboard message */ - -void silcpurple_wb_receive(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcMessagePayload payload, - SilcMessageFlags flags, const unsigned char *message, - SilcUInt32 message_len) -{ - SilcPurple sg; - PurpleConnection *gc; - PurpleWhiteboard *wb; - SilcPurpleWb wbs; - - gc = client->application; - sg = gc->proto_data; - - wb = purple_whiteboard_get_session(sg->account, sender->nickname); - if (!wb) { - /* Ask user if they want to open the whiteboard */ - silcpurple_wb_request(client, message, message_len, - sender, NULL); - return; - } - - wbs = wb->proto_data; - silcpurple_wb_parse(wbs, wb, (unsigned char *)message, message_len); -} - -/* Process incoming whiteboard message on channel */ - -void silcpurple_wb_receive_ch(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcChannelEntry channel, - SilcMessagePayload payload, - SilcMessageFlags flags, - const unsigned char *message, - SilcUInt32 message_len) -{ - SilcPurple sg; - PurpleConnection *gc; - PurpleWhiteboard *wb; - SilcPurpleWb wbs; - - gc = client->application; - sg = gc->proto_data; - - wb = purple_whiteboard_get_session(sg->account, channel->channel_name); - if (!wb) { - /* Ask user if they want to open the whiteboard */ - silcpurple_wb_request(client, message, message_len, - sender, channel); - return; - } - - wbs = wb->proto_data; - silcpurple_wb_parse(wbs, wb, (unsigned char *)message, message_len); -} - -/* Send whiteboard message */ - -void silcpurple_wb_send(PurpleWhiteboard *wb, GList *draw_list) -{ - SilcPurpleWb wbs = wb->proto_data; - SilcBuffer packet; - GList *list; - int len; - PurpleConnection *gc; - SilcPurple sg; - - g_return_if_fail(draw_list); - gc = purple_account_get_connection(wb->account); - g_return_if_fail(gc); - sg = gc->proto_data; - g_return_if_fail(sg); - - len = SILCPURPLE_WB_HEADER; - for (list = draw_list; list; list = list->next) - len += 4; - - packet = silc_buffer_alloc_size(len); - if (!packet) - return; - - /* Assmeble packet */ - silc_buffer_format(packet, - SILC_STR_UI32_STRING(SILCPURPLE_WB_MIME), - SILC_STR_UI_CHAR(SILCPURPLE_WB_DRAW), - SILC_STR_UI_SHORT(wbs->width), - SILC_STR_UI_SHORT(wbs->height), - SILC_STR_UI_INT(wbs->brush_color), - SILC_STR_UI_SHORT(wbs->brush_size), - SILC_STR_END); - silc_buffer_pull(packet, SILCPURPLE_WB_HEADER); - for (list = draw_list; list; list = list->next) { - silc_buffer_format(packet, - SILC_STR_UI_INT(GPOINTER_TO_INT(list->data)), - SILC_STR_END); - silc_buffer_pull(packet, 4); - } - - /* Send the message */ - if (wbs->type == 0) { - /* Private message */ - silc_client_send_private_message(sg->client, sg->conn, - wbs->u.client, - SILC_MESSAGE_FLAG_DATA, - packet->head, len, TRUE); - } else if (wbs->type == 1) { - /* Channel message. Channel private keys are not supported. */ - silc_client_send_channel_message(sg->client, sg->conn, - wbs->u.channel, NULL, - SILC_MESSAGE_FLAG_DATA, - packet->head, len, TRUE); - } - - silc_buffer_free(packet); -} - -/* Purple Whiteboard operations */ - -void silcpurple_wb_start(PurpleWhiteboard *wb) -{ - /* Nothing here. Everything is in initialization */ -} - -void silcpurple_wb_end(PurpleWhiteboard *wb) -{ - silc_free(wb->proto_data); - wb->proto_data = NULL; -} - -void silcpurple_wb_get_dimensions(const PurpleWhiteboard *wb, int *width, int *height) -{ - SilcPurpleWb wbs = wb->proto_data; - *width = wbs->width; - *height = wbs->height; -} - -void silcpurple_wb_set_dimensions(PurpleWhiteboard *wb, int width, int height) -{ - SilcPurpleWb wbs = wb->proto_data; - wbs->width = width > SILCPURPLE_WB_WIDTH_MAX ? SILCPURPLE_WB_WIDTH_MAX : - width; - wbs->height = height > SILCPURPLE_WB_HEIGHT_MAX ? SILCPURPLE_WB_HEIGHT_MAX : - height; - - /* Update whiteboard */ - purple_whiteboard_set_dimensions(wb, wbs->width, wbs->height); -} - -void silcpurple_wb_get_brush(const PurpleWhiteboard *wb, int *size, int *color) -{ - SilcPurpleWb wbs = wb->proto_data; - *size = wbs->brush_size; - *color = wbs->brush_color; -} - -void silcpurple_wb_set_brush(PurpleWhiteboard *wb, int size, int color) -{ - SilcPurpleWb wbs = wb->proto_data; - wbs->brush_size = size; - wbs->brush_color = color; - - /* Update whiteboard */ - purple_whiteboard_set_brush(wb, size, color); -} - -void silcpurple_wb_clear(PurpleWhiteboard *wb) -{ - SilcPurpleWb wbs = wb->proto_data; - SilcBuffer packet; - int len; - PurpleConnection *gc; - SilcPurple sg; - - gc = purple_account_get_connection(wb->account); - g_return_if_fail(gc); - sg = gc->proto_data; - g_return_if_fail(sg); - - len = SILCPURPLE_WB_HEADER; - packet = silc_buffer_alloc_size(len); - if (!packet) - return; - - /* Assmeble packet */ - silc_buffer_format(packet, - SILC_STR_UI32_STRING(SILCPURPLE_WB_MIME), - SILC_STR_UI_CHAR(SILCPURPLE_WB_CLEAR), - SILC_STR_UI_SHORT(wbs->width), - SILC_STR_UI_SHORT(wbs->height), - SILC_STR_UI_INT(wbs->brush_color), - SILC_STR_UI_SHORT(wbs->brush_size), - SILC_STR_END); - - /* Send the message */ - if (wbs->type == 0) { - /* Private message */ - silc_client_send_private_message(sg->client, sg->conn, - wbs->u.client, - SILC_MESSAGE_FLAG_DATA, - packet->head, len, TRUE); - } else if (wbs->type == 1) { - /* Channel message */ - silc_client_send_channel_message(sg->client, sg->conn, - wbs->u.channel, NULL, - SILC_MESSAGE_FLAG_DATA, - packet->head, len, TRUE); - } - - silc_buffer_free(packet); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/silc10/wb.h --- a/libpurple/protocols/silc10/wb.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* - - silcpurple.h - - Author: Pekka Riikonen - - Copyright (C) 2005 Pekka Riikonen - - 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; version 2 of the License. - - 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. - -*/ - -#ifndef SILCPURPLE_WB_H -#define SILCPURPLE_WB_H - -#include "silcpurple.h" -#include "whiteboard.h" - -PurpleWhiteboard * -silcpurple_wb_init(SilcPurple sg, SilcClientEntry client_entry); -PurpleWhiteboard * -silcpurple_wb_init_ch(SilcPurple sg, SilcChannelEntry channel); -void silcpurple_wb_receive(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcMessagePayload payload, - SilcMessageFlags flags, const unsigned char *message, - SilcUInt32 message_len); -void silcpurple_wb_receive_ch(SilcClient client, SilcClientConnection conn, - SilcClientEntry sender, SilcChannelEntry channel, - SilcMessagePayload payload, - SilcMessageFlags flags, - const unsigned char *message, - SilcUInt32 message_len); -void silcpurple_wb_start(PurpleWhiteboard *wb); -void silcpurple_wb_end(PurpleWhiteboard *wb); -void silcpurple_wb_get_dimensions(const PurpleWhiteboard *wb, int *width, int *height); -void silcpurple_wb_set_dimensions(PurpleWhiteboard *wb, int width, int height); -void silcpurple_wb_get_brush(const PurpleWhiteboard *wb, int *size, int *color); -void silcpurple_wb_set_brush(PurpleWhiteboard *wb, int size, int color); -void silcpurple_wb_send(PurpleWhiteboard *wb, GList *draw_list); -void silcpurple_wb_clear(PurpleWhiteboard *wb); - -#endif /* SILCPURPLE_WB_H */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/Makefile.am --- a/libpurple/protocols/zephyr/Makefile.am Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,109 +0,0 @@ -pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) - -ZEPHYRSOURCES = \ - ZAsyncLocate.c \ - ZCkAuth.c \ - ZCkIfNot.c \ - ZClosePort.c \ - ZCmpUID.c \ - ZCmpUIDP.c \ - ZFlsLocs.c \ - ZFlsSubs.c \ - ZFmtAuth.c \ - ZFmtList.c \ - ZFmtNotice.c \ - ZFmtRaw.c \ - ZFmtRawLst.c \ - ZFmtSmRLst.c \ - ZFmtSmRaw.c \ - ZFreeNot.c \ - ZGetLocs.c \ - ZGetSender.c \ - ZGetSubs.c \ - ZGetWGPort.c \ - ZIfNotice.c \ - ZInit.c \ - ZLocations.c \ - ZMakeAscii.c \ - ZMkAuth.c \ - ZNewLocU.c \ - ZOpenPort.c \ - ZParseNot.c \ - ZPeekIfNot.c \ - ZPeekNot.c \ - ZPeekPkt.c \ - ZPending.c \ - ZReadAscii.c \ - ZRecvNot.c \ - ZRecvPkt.c \ - ZRetSubs.c \ - ZSendList.c \ - ZSendNot.c \ - ZSendPkt.c \ - ZSendRLst.c \ - ZSendRaw.c \ - ZSetDest.c \ - ZSetFD.c \ - ZSetSrv.c \ - ZSubs.c \ - ZVariables.c \ - ZWait4Not.c \ - ZhmStat.c \ - Zinternal.c \ - com_err.h \ - error_message.c \ - error_table.h \ - et_name.c \ - init_et.c \ - internal.h \ - mit-copyright.h \ - mit-sipb-copyright.h \ - sysdep.h \ - zephyr_err.c \ - zephyr_err.h \ - zephyr_internal.h \ - zephyr.c - -ZEPHYRSOURCESEXT = zephyr.c - -AM_CFLAGS = $(st) - -ZEPHYRLIBS = $(KRB4_LDFLAGS) $(KRB4_LIBS) - -ZEPHYRLIBSEXT = $(ZEPHYR_LDFLAGS) $(ZEPHYR_LIBS) - -libzephyr_la_LDFLAGS = -module -avoid-version - -if STATIC_ZEPHYR - -st = -DPURPLE_STATIC_PRPL -Dlint -noinst_LTLIBRARIES = libzephyr.la -libzephyr_la_SOURCES = $(ZEPHYRSOURCES) -libzephyr_la_CFLAGS = $(AM_CFLAGS) -libzephyr_la_LIBADD = $(ZEPHYRLIBS) - -else - -st = -Dlint -pkg_LTLIBRARIES = libzephyr.la - -if EXTERNAL_LIBZEPHYR -libzephyr_la_SOURCES = $(ZEPHYRSOURCESEXT) -libzephyr_la_LIBADD = $(GLIB_LIBS) $(ZEPHYRLIBSEXT) -else -libzephyr_la_SOURCES = $(ZEPHYRSOURCES) -libzephyr_la_LIBADD = $(GLIB_LIBS) $(ZEPHYRLIBS) -endif - - -endif - -AM_CPPFLAGS = \ - -I$(top_srcdir) \ - -I$(top_srcdir)/libpurple \ - -I$(top_builddir)/libpurple \ - -I$(top_srcdir)/libpurple/protocols \ - -DCONFDIR=\"$(sysconfdir)\" \ - $(GLIB_CFLAGS) \ - $(KRB4_CFLAGS) \ - $(DEBUG_CFLAGS) diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/Makefile.mingw --- a/libpurple/protocols/zephyr/Makefile.mingw Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,149 +0,0 @@ -# -# Makefile.mingw -# -# Description: Makefile for win32 (mingw) version of libzephyr -# - -PIDGIN_TREE_TOP := ../../.. -include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak - -TARGET = libzephyr -KRB4_TOP := $(WIN32_DEV_TOP)/kfw-2.6.3-final.fixed -USE_KRB4 := false -DEFINES += -DWIN32 -DKRB5_SYSTYPES__ -TYPE = PLUGIN - -ifeq ($(USE_KRB4),true) - DEFINES += -DZEPHYR_USES_KERBEROS -endif - -# Static or Plugin... -ifeq ($(TYPE),STATIC) - DEFINES += -DSTATIC - DLL_INSTALL_DIR = $(PURPLE_INSTALL_DIR) -else -ifeq ($(TYPE),PLUGIN) - DLL_INSTALL_DIR = $(PURPLE_INSTALL_PLUGINS_DIR) -endif -endif - -## -## INCLUDE PATHS -## -INCLUDE_PATHS += -I$(ZEPHYR_ROOT) \ - -I$(GTK_TOP)/include \ - -I$(GTK_TOP)/include/glib-2.0 \ - -I$(GTK_TOP)/lib/glib-2.0/include \ - -I$(PURPLE_TOP) \ - -I$(PURPLE_TOP)/win32 \ - -I$(PIDGIN_TREE_TOP) - -ifeq ($(USE_KRB4), true) - INCLUDE_PATHS += -I$(KRB4_TOP)/inc/krb4 -endif - -LIB_PATHS += -L$(GTK_TOP)/lib \ - -L$(PURPLE_TOP) - -ifeq ($(USE_KRB4), true) - LIB_PATHS+=-L$(KRB4_TOP)/lib/i386 -endif - -## -## SOURCES, OBJECTS -## -C_SRC = ZAsyncLocate.c \ - ZCkAuth.c \ - ZCkIfNot.c \ - ZClosePort.c \ - ZCmpUID.c \ - ZCmpUIDP.c \ - ZFlsLocs.c \ - ZFlsSubs.c \ - ZFmtAuth.c \ - ZFmtList.c \ - ZFmtNotice.c \ - ZFmtRaw.c \ - ZFmtRawLst.c \ - ZFmtSmRLst.c \ - ZFmtSmRaw.c \ - ZFreeNot.c \ - ZGetLocs.c \ - ZGetSender.c \ - ZGetSubs.c \ - ZGetWGPort.c \ - ZIfNotice.c \ - ZInit.c \ - ZLocations.c \ - ZMakeAscii.c \ - ZMkAuth.c \ - ZNewLocU.c \ - ZOpenPort.c \ - ZParseNot.c \ - ZPeekIfNot.c \ - ZPeekNot.c \ - ZPeekPkt.c \ - ZPending.c \ - ZReadAscii.c \ - ZRecvNot.c \ - ZRecvPkt.c \ - ZRetSubs.c \ - ZSendList.c \ - ZSendNot.c \ - ZSendPkt.c \ - ZSendRLst.c \ - ZSendRaw.c \ - ZSetDest.c \ - ZSetFD.c \ - ZSetSrv.c \ - ZSubs.c \ - ZVariables.c \ - ZWait4Not.c \ - ZhmStat.c \ - Zinternal.c \ - error_message.c \ - et_name.c \ - init_et.c \ - zephyr_err.c \ - zephyr.c - -OBJECTS = $(C_SRC:%.c=%.o) - -## -## LIBRARIES -## -LIBS = \ - -lglib-2.0 \ - -lws2_32 \ - -lintl \ - -lpurple - -ifeq ($(USE_KRB4),true) - LIBS+= -lkrbv4w32 -endif - -include $(PIDGIN_COMMON_RULES) - -## -## TARGET DEFINITIONS -## -.PHONY: all install clean - -all: $(TARGET).dll - -install: all $(DLL_INSTALL_DIR) - cp $(TARGET).dll $(DLL_INSTALL_DIR) - -$(OBJECTS): $(PURPLE_CONFIG_H) - -$(TARGET).dll: $(PURPLE_DLL).a $(OBJECTS) - $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -Wl,--enable-stdcall-fixup -o $(TARGET).dll - -## -## CLEAN RULES -## -clean: - rm -f $(OBJECTS) - rm -f $(TARGET).dll - -include $(PIDGIN_COMMON_TARGETS) diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZAsyncLocate.c --- a/libpurple/protocols/zephyr/ZAsyncLocate.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,165 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for asynchronous location functions. - * - * Created by: Marc Horowitz - * - * Copyright (c) 1990,1991 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" -#include "util.h" - -Code_t -ZRequestLocations(const char *user, ZAsyncLocateData_t *zald, - ZNotice_Kind_t kind, /* UNSAFE, UNACKED, or ACKED */ - Z_AuthProc auth) -{ - int retval; - ZNotice_t notice; - size_t userlen, versionlen; - - if (ZGetFD() < 0) - if ((retval = ZOpenPort((unsigned short *)0)) != ZERR_NONE) - return (retval); - - (void) memset((char *)¬ice, 0, sizeof(notice)); - notice.z_kind = kind; - notice.z_port = __Zephyr_port; - notice.z_class = LOCATE_CLASS; - notice.z_class_inst = user; - notice.z_opcode = LOCATE_LOCATE; - notice.z_sender = 0; - notice.z_recipient = ""; - notice.z_default_format = ""; - notice.z_message_len = 0; - - if ((retval = ZSendNotice(¬ice, auth)) != ZERR_NONE) - return(retval); - - userlen = strlen(user) + 1; - versionlen = strlen(notice.z_version) + 1; - if ((zald->user = (char *) malloc(userlen)) == NULL) { - return(ENOMEM); - } - if ((zald->version = (char *) malloc(versionlen)) == NULL) { - free(zald->user); - return(ENOMEM); - } - zald->uid = notice.z_multiuid; - g_strlcpy(zald->user,user,userlen); - g_strlcpy(zald->version,notice.z_version,versionlen); - - return(ZERR_NONE); -} - -Code_t -ZParseLocations(ZNotice_t *notice, ZAsyncLocateData_t *zald, int *nlocs, - char **user) -{ - char *ptr, *end; - int i; - - ZFlushLocations(); /* This never fails (this function is part of the - library, so it is allowed to know this). */ - - /* non-matching protocol version numbers means the - server is probably an older version--must punt */ - - if (zald && !purple_strequal(notice->z_version, zald->version)) - return(ZERR_VERS); - - if (notice->z_kind == SERVNAK) - return (ZERR_SERVNAK); - - /* flag ACKs as special */ - if (notice->z_kind == SERVACK && - purple_strequal(notice->z_opcode, LOCATE_LOCATE)) { - *nlocs = -1; - return(ZERR_NONE); - } - - if (notice->z_kind != ACKED) - return (ZERR_INTERNAL); - - end = notice->z_message+notice->z_message_len; - - __locate_num = 0; - - for (ptr=notice->z_message;ptrz_message, i=0; i<__locate_num; i++) { - unsigned int len; - - len = strlen (ptr) + 1; - __locate_list[i].host = (char *) malloc(len); - if (!__locate_list[i].host) - return (ENOMEM); - g_strlcpy(__locate_list[i].host, ptr,len); - ptr += len; - - len = strlen (ptr) + 1; - __locate_list[i].time = (char *) malloc(len); - if (!__locate_list[i].time) - return (ENOMEM); - g_strlcpy(__locate_list[i].time, ptr,len); - ptr += len; - - len = strlen (ptr) + 1; - __locate_list[i].tty = (char *) malloc(len); - if (!__locate_list[i].tty) - return (ENOMEM); - g_strlcpy(__locate_list[i].tty, ptr,len); - ptr += len; - } - - __locate_next = 0; - *nlocs = __locate_num; - if (user) { - size_t len; - if (zald) { - len = strlen(zald->user) + 1; - if ((*user = (char *) malloc(len)) == NULL) - return(ENOMEM); - g_strlcpy(*user,zald->user,len); - } else { - len = strlen(notice->z_class_inst) + 1; - if ((*user = (char *) malloc(len)) == NULL) - return(ENOMEM); - g_strlcpy(*user,notice->z_class_inst,len); - } - } - return (ZERR_NONE); -} - -int -ZCompareALDPred(ZNotice_t *notice, void *zald) -{ - return(ZCompareUID(&(notice->z_multiuid), - &(((ZAsyncLocateData_t *) zald)->uid))); -} - -void -ZFreeALD(ZAsyncLocateData_t *zald) -{ - if (!zald) return; - - if (zald->user) free(zald->user); - if (zald->version) free(zald->version); - (void) memset(zald, 0, sizeof(*zald)); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZCkAuth.c --- a/libpurple/protocols/zephyr/ZCkAuth.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,54 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZCheckAuthentication function. - * - * Created by: Robert French - * - * Copyright (c) 1987,1991 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -/* Check authentication of the notice. - If it looks authentic but fails the Kerberos check, return -1. - If it looks authentic and passes the Kerberos check, return 1. - If it doesn't look authentic, return 0 - - When not using Kerberos, return true if the notice claims to be authentic. - Only used by clients; the server uses its own routine. - */ -Code_t -ZCheckAuthentication(ZNotice_t *notice, struct sockaddr_in *from) -{ -#ifdef ZEPHYR_USES_KERBEROS - int result; - ZChecksum_t our_checksum; - CREDENTIALS cred; - - /* If the value is already known, return it. */ - if (notice->z_checked_auth != ZAUTH_UNSET) - return (notice->z_checked_auth); - - if (!notice->z_auth) - return (ZAUTH_NO); - - if ((result = krb_get_cred(SERVER_SERVICE, SERVER_INSTANCE, - __Zephyr_realm, &cred)) != 0) - return (ZAUTH_NO); - -#ifdef NOENCRYPTION - our_checksum = 0; -#else - our_checksum = des_quad_cksum(notice->z_packet, NULL, - notice->z_default_format+ - strlen(notice->z_default_format)+1- - notice->z_packet, 0, (C_Block *)cred.session); -#endif - /* if mismatched checksum, then the packet was corrupted */ - return ((our_checksum == notice->z_checksum) ? ZAUTH_YES : ZAUTH_FAILED); - -#else - return (notice->z_auth ? ZAUTH_YES : ZAUTH_NO); -#endif -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZCkIfNot.c --- a/libpurple/protocols/zephyr/ZCkIfNot.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZCheckIfNotice function. - * - * Created by: Robert French - * - * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -Code_t -ZCheckIfNotice(ZNotice_t *notice, struct sockaddr_in *from, - register int (*predicate) __P((ZNotice_t *, void *)), void *args) -{ - ZNotice_t tmpnotice; - Code_t retval; - register char *buffer; - register struct _Z_InputQ *qptr; - - if ((retval = Z_ReadEnqueue()) != ZERR_NONE) - return (retval); - - qptr = Z_GetFirstComplete(); - - while (qptr) { - if ((retval = ZParseNotice(qptr->packet, qptr->packet_len, - &tmpnotice)) != ZERR_NONE) - return (retval); - if ((*predicate)(&tmpnotice, args)) { - if (!(buffer = (char *) malloc((unsigned) qptr->packet_len))) - return (ENOMEM); - (void) memcpy(buffer, qptr->packet, qptr->packet_len); - if (from) - *from = qptr->from; - if ((retval = ZParseNotice(buffer, qptr->packet_len, - notice)) != ZERR_NONE) { - free(buffer); - return (retval); - } - Z_RemQueue(qptr); - return (ZERR_NONE); - } - qptr = Z_GetNextComplete(qptr); - } - - return (ZERR_NONOTICE); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZClosePort.c --- a/libpurple/protocols/zephyr/ZClosePort.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZClosePort function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -Code_t -ZClosePort(void) -{ - if (__Zephyr_fd >= 0 && __Zephyr_open) - (void) close(__Zephyr_fd); - - __Zephyr_fd = -1; - __Zephyr_open = 0; - - return (ZERR_NONE); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZCmpUID.c --- a/libpurple/protocols/zephyr/ZCmpUID.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,17 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZCompareUID function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -int -ZCompareUID(ZUnique_Id_t *uid1, ZUnique_Id_t *uid2) -{ - return (!memcmp((char *)uid1, (char *)uid2, sizeof (*uid1))); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZCmpUIDP.c --- a/libpurple/protocols/zephyr/ZCmpUIDP.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZCompareUIDPred function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -int -ZCompareUIDPred(ZNotice_t *notice, void *uid) -{ - return (ZCompareUID(¬ice->z_uid, (ZUnique_Id_t *) uid)); -} - -int -ZCompareMultiUIDPred(ZNotice_t *notice, void *uid) -{ - return (ZCompareUID(¬ice->z_multiuid, (ZUnique_Id_t *) uid)); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZFlsLocs.c --- a/libpurple/protocols/zephyr/ZFlsLocs.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZFlushLocations function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -Code_t -ZFlushLocations(void) -{ - int i; - - if (!__locate_list) - return (ZERR_NONE); - - for (i=0;i<__locate_num;i++) { - free(__locate_list[i].host); - free(__locate_list[i].time); - free(__locate_list[i].tty); - } - - free((char *)__locate_list); - - __locate_list = 0; - __locate_num = 0; - - return (ZERR_NONE); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZFlsSubs.c --- a/libpurple/protocols/zephyr/ZFlsSubs.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZFlushSubscriptions function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -Code_t -ZFlushSubscriptions(void) -{ - register int i; - - if (!__subscriptions_list) - return (ZERR_NONE); - - for (i=0;i<__subscriptions_num;i++) { - free(__subscriptions_list[i].zsub_class); - free(__subscriptions_list[i].zsub_classinst); - free(__subscriptions_list[i].zsub_recipient); - } - - free((char *)__subscriptions_list); - - __subscriptions_list = 0; - __subscriptions_num = 0; - - return (ZERR_NONE); -} - diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZFmtAuth.c --- a/libpurple/protocols/zephyr/ZFmtAuth.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,55 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZFormatAuthenticNotice function. - * - * Created by: Robert French - - * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -#ifdef ZEPHYR_USES_KERBEROS -Code_t -ZFormatAuthenticNotice(ZNotice_t *notice, register char *buffer, - register int buffer_len, int *len, C_Block session) -{ - ZNotice_t newnotice; - char *ptr; - int retval, hdrlen; - - newnotice = *notice; - newnotice.z_auth = 1; - newnotice.z_authent_len = 0; - newnotice.z_ascii_authent = ""; - - if ((retval = Z_FormatRawHeader(&newnotice, buffer, buffer_len, - &hdrlen, &ptr, NULL)) != ZERR_NONE) - return (retval); - -#ifdef NOENCRYPTION - newnotice.z_checksum = 0; -#else - newnotice.z_checksum = - (ZChecksum_t)des_quad_cksum(buffer, NULL, ptr - buffer, 0, (C_Block*)session); -#endif - if ((retval = Z_FormatRawHeader(&newnotice, buffer, buffer_len, - &hdrlen, NULL, NULL)) != ZERR_NONE) - return (retval); - - ptr = buffer+hdrlen; - - if (newnotice.z_message_len+hdrlen > buffer_len) - return (ZERR_PKTLEN); - - (void) memcpy(ptr, newnotice.z_message, newnotice.z_message_len); - - *len = hdrlen+newnotice.z_message_len; - - if (*len > Z_MAXPKTLEN) - return (ZERR_PKTLEN); - - return (ZERR_NONE); -} -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZFmtList.c --- a/libpurple/protocols/zephyr/ZFmtList.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZFormatNoticeList function. - * - * Created by: Robert French - * - * Copyright (c) 1987,1991 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -Code_t -ZFormatNoticeList(ZNotice_t *notice, register char **list, int nitems, - char **buffer, int *ret_len, Z_AuthProc cert_routine) -{ - char header[Z_MAXHEADERLEN]; - register int i; - int hdrlen, size; - char *ptr; - Code_t retval; - - if ((retval = Z_FormatHeader(notice, header, sizeof(header), &hdrlen, - cert_routine)) != ZERR_NONE) - return (retval); - - size = 0; - for (i=0;iz_message_len; - - /* Length can never be zero, don't have to worry about malloc(0). */ - if (!(*buffer = (char *) malloc((unsigned)*ret_len))) - return (ENOMEM); - - (void) memcpy(*buffer, header, hdrlen); - (void) memcpy(*buffer+hdrlen, notice->z_message, notice->z_message_len); - - return (ZERR_NONE); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZFmtRaw.c --- a/libpurple/protocols/zephyr/ZFmtRaw.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,34 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZFormatRawNotice function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -Code_t -ZFormatRawNotice(register ZNotice_t *notice, char **buffer, int *ret_len) -{ - char header[Z_MAXHEADERLEN]; - int hdrlen; - Code_t retval; - - if ((retval = Z_FormatRawHeader(notice, header, sizeof(header), - &hdrlen, NULL, NULL)) != ZERR_NONE) - return (retval); - - *ret_len = hdrlen+notice->z_message_len; - - /* *ret_len is never 0, don't have to worry about malloc(0) */ - if (!(*buffer = (char *) malloc((unsigned) *ret_len))) - return (ENOMEM); - - (void) memcpy(*buffer, header, hdrlen); - (void) memcpy(*buffer+hdrlen, notice->z_message, notice->z_message_len); - - return (ZERR_NONE); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZFmtRawLst.c --- a/libpurple/protocols/zephyr/ZFmtRawLst.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZFormatRawNoticeList function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -Code_t -ZFormatRawNoticeList(ZNotice_t *notice, char *list[], int nitems, char **buffer, - int *ret_len) -{ - char header[Z_MAXHEADERLEN]; - int hdrlen, i, size; - char *ptr; - Code_t retval; - - if ((retval = Z_FormatRawHeader(notice, header, sizeof(header), - &hdrlen, NULL, NULL)) != ZERR_NONE) - return (retval); - - size = 0; - for (i=0;i Z_MAXPKTLEN) - return (ZERR_PKTLEN); - - ptr = buffer+hdrlen; - - for (;nitems;nitems--, list++) { - i = strlen(*list)+1; - (void) memcpy(ptr, *list, i); - ptr += i; - } - - return (ZERR_NONE); -} -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZFmtSmRaw.c --- a/libpurple/protocols/zephyr/ZFmtSmRaw.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,31 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZFormatSmallRawNotice function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -Code_t -ZFormatSmallRawNotice(ZNotice_t *notice, ZPacket_t buffer, int *ret_len) -{ - Code_t retval; - int hdrlen; - - if ((retval = Z_FormatRawHeader(notice, buffer, Z_MAXHEADERLEN, - &hdrlen, NULL, NULL)) != ZERR_NONE) - return (retval); - - *ret_len = hdrlen+notice->z_message_len; - - if (*ret_len > Z_MAXPKTLEN) - return (ZERR_PKTLEN); - - (void) memcpy(buffer+hdrlen, notice->z_message, notice->z_message_len); - - return (ZERR_NONE); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZFreeNot.c --- a/libpurple/protocols/zephyr/ZFreeNot.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZFreeNotice function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -Code_t -ZFreeNotice(ZNotice_t *notice) -{ - free(notice->z_packet); - return 0; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZGetLocs.c --- a/libpurple/protocols/zephyr/ZGetLocs.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZGetLocations function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -#define min(a,b) ((a)<(b)?(a):(b)) - -/* Prototype for -Wmissing-prototypes */ -Code_t ZGetLocations(ZLocations_t *location, int *numlocs); - -Code_t ZGetLocations(ZLocations_t *location, int *numlocs) -{ - int i; - - if (!__locate_list) - return (ZERR_NOLOCATIONS); - - if (__locate_next == __locate_num) - return (ZERR_NOMORELOCS); - - for (i=0;i -#endif - -char * -ZGetSender(void) -{ - struct passwd *pw; -#ifdef ZEPHYR_USES_KERBEROS - char pname[ANAME_SZ]; - char pinst[INST_SZ]; - char prealm[REALM_SZ]; - static char sender[ANAME_SZ+INST_SZ+REALM_SZ+3] = ""; - long int kerror; -#else - static char sender[128] = ""; -#endif - -#ifdef WIN32 - unsigned long sender_size = sizeof(sender) - 1; -#endif - -#ifdef ZEPHYR_USES_KERBEROS - if ((kerror = krb_get_tf_fullname((char *)TKT_FILE, pname, pinst, prealm)) == KSUCCESS) - { - sprintf(sender, "%s%s%s@%s", pname, (pinst[0] ? "." : ""), pinst, prealm); - return sender; - } -#endif - -#ifdef WIN32 - GetUserName(sender, &sender_size); -#else - /* XXX a uid_t is a u_short (now), but getpwuid - * wants an int. AARGH! */ - pw = getpwuid((int) getuid()); - if (!pw) - return ("unknown"); - sprintf(sender, "%s@%s", pw->pw_name, __Zephyr_realm); -#endif - return sender; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZGetSubs.c --- a/libpurple/protocols/zephyr/ZGetSubs.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZGetSubscriptions function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -#define min(a,b) ((a)<(b)?(a):(b)) - -/* Prototype for -Wmissing-prototypes */ -Code_t ZGetSubscriptions(ZSubscription_t *subscription, int *numsubs); - -Code_t ZGetSubscriptions(ZSubscription_t *subscription, int *numsubs) -{ - int i; - - if (!__subscriptions_list) - return (ZERR_NOSUBSCRIPTIONS); - - if (__subscriptions_next == __subscriptions_num) - return (ZERR_NOMORESUBSCRIPTIONS); - - for (i=0;ipacket, qptr->packet_len, - &tmpnotice)) != ZERR_NONE) - return (retval); - if ((*predicate)(&tmpnotice, args)) { - if (!(buffer = (char *) malloc((unsigned) qptr->packet_len))) - return (ENOMEM); - (void) memcpy(buffer, qptr->packet, qptr->packet_len); - if (from) - *from = qptr->from; - if ((retval = ZParseNotice(buffer, qptr->packet_len, - notice)) != ZERR_NONE) { - free(buffer); - return (retval); - } - Z_RemQueue(qptr); - return (ZERR_NONE); - } - qptr = Z_GetNextComplete(qptr); - } - if ((retval = Z_ReadWait()) != ZERR_NONE) - return (retval); - qptr = Z_GetFirstComplete(); /* need to look over all of - the queued messages, in case - a fragment has been reassembled */ - } -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZInit.c --- a/libpurple/protocols/zephyr/ZInit.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,155 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZInitialize function. - * - * Created by: Robert French - * - * Copyright (c) 1987, 1991 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#ifdef ZEPHYR_USES_KERBEROS -#ifdef WIN32 - -#else -#include -#endif -#endif - -#include "internal.h" - -#ifdef WIN32 -#include -#else -#include -#endif - - -#ifndef INADDR_NONE -#define INADDR_NONE 0xffffffff -#endif - -Code_t -ZInitialize(void) -{ - struct servent *hmserv; - struct hostent *hostent; - char addr[4], hostname[MAXHOSTNAMELEN]; - struct in_addr servaddr; - struct sockaddr_in sin; - int s; - socklen_t sinsize = sizeof(sin); - Code_t code; - ZNotice_t notice; -#ifdef ZEPHYR_USES_KERBEROS - char *krealm = NULL; - int krbval; - char d1[ANAME_SZ], d2[INST_SZ]; - - /* initialize_krb_error_table(); */ -#endif - - initialize_zeph_error_table(); - - (void) memset((char *)&__HM_addr, 0, sizeof(__HM_addr)); - - __HM_addr.sin_family = AF_INET; - - /* Set up local loopback address for HostManager */ - addr[0] = 127; - addr[1] = 0; - addr[2] = 0; - addr[3] = 1; - - hmserv = (struct servent *)getservbyname(HM_SVCNAME, "udp"); - __HM_addr.sin_port = (hmserv) ? hmserv->s_port : HM_SVC_FALLBACK; - - (void) memcpy((char *)&__HM_addr.sin_addr, addr, 4); - - __HM_set = 0; - - /* Initialize the input queue */ - __Q_Tail = NULL; - __Q_Head = NULL; - - /* if the application is a server, there might not be a zhm. The - code will fall back to something which might not be "right", - but this is is ok, since none of the servers call krb_rd_req. */ - - servaddr.s_addr = INADDR_NONE; - if (! __Zephyr_server) { - if ((code = ZOpenPort(NULL)) != ZERR_NONE) - return(code); - - if ((code = ZhmStat(NULL, ¬ice)) != ZERR_NONE) - return(code); - - ZClosePort(); - - /* the first field, which is NUL-terminated, is the server name. - If this code ever support a multiplexing zhm, this will have to - be made smarter, and probably per-message */ - -#ifdef ZEPHYR_USES_KERBEROS - krealm = krb_realmofhost(notice.z_message); -#endif - hostent = gethostbyname(notice.z_message); - if (hostent && hostent->h_addrtype == AF_INET) - memcpy(&servaddr, hostent->h_addr, sizeof(servaddr)); - - ZFreeNotice(¬ice); - } - -#ifdef ZEPHYR_USES_KERBEROS - if (krealm) { - g_strlcpy(__Zephyr_realm, krealm, REALM_SZ); - } else if ((krb_get_tf_fullname(TKT_FILE, d1, d2, __Zephyr_realm) - != KSUCCESS) && - ((krbval = krb_get_lrealm(__Zephyr_realm, 1)) != KSUCCESS)) { - return (krbval); - } -#else - g_strlcpy(__Zephyr_realm, "local-realm", REALM_SZ); -#endif - - __My_addr.s_addr = INADDR_NONE; - if (servaddr.s_addr != INADDR_NONE) { - /* Try to get the local interface address by connecting a UDP - * socket to the server address and getting the local address. - * Some broken operating systems (e.g. Solaris 2.0-2.5) yield - * INADDR_ANY (zero), so we have to check for that. */ - s = socket(AF_INET, SOCK_DGRAM, 0); - if (s != -1) { - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - memcpy(&sin.sin_addr, &servaddr, sizeof(servaddr)); - sin.sin_port = HM_SRV_SVC_FALLBACK; - if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) == 0 - && getsockname(s, (struct sockaddr *) &sin, &sinsize) == 0 - && sin.sin_addr.s_addr != 0) - memcpy(&__My_addr, &sin.sin_addr, sizeof(__My_addr)); - close(s); - } - } - if (__My_addr.s_addr == INADDR_NONE) { - /* We couldn't figure out the local interface address by the - * above method. Try by resolving the local hostname. (This - * is a pretty broken thing to do, and unfortunately what we - * always do on server machines.) */ - if (gethostname(hostname, sizeof(hostname)) == 0) { - hostent = gethostbyname(hostname); - if (hostent && hostent->h_addrtype == AF_INET) - memcpy(&__My_addr, hostent->h_addr, sizeof(__My_addr)); - } - } - /* If the above methods failed, zero out __My_addr so things will - * sort of kind of work. */ - if (__My_addr.s_addr == INADDR_NONE) - __My_addr.s_addr = 0; - - /* Get the sender so we can cache it */ - (void) ZGetSender(); - - return (ZERR_NONE); -} - diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZLocations.c --- a/libpurple/protocols/zephyr/ZLocations.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,161 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZSetLocation, ZUnsetLocation, and - * ZFlushMyLocations functions. - * - * Created by: Robert French - * - * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" -#include "util.h" - -#ifndef WIN32 -#include -#endif - -#include -#include - -Code_t -ZSetLocation(char *exposure) -{ - return (Z_SendLocation(LOGIN_CLASS, exposure, ZAUTH, - "$sender logged in to $1 on $3 at $2")); -} - -Code_t -ZUnsetLocation(void) -{ - return (Z_SendLocation(LOGIN_CLASS, LOGIN_USER_LOGOUT, ZNOAUTH, - "$sender logged out of $1 on $3 at $2")); -} - -Code_t -ZFlushMyLocations(void) -{ - return (Z_SendLocation(LOGIN_CLASS, LOGIN_USER_FLUSH, ZAUTH, "")); -} - -static char host[MAXHOSTNAMELEN]; -static char *mytty = NULL; -static int reenter = 0; - -Code_t -Z_SendLocation(char *class, char *opcode, Z_AuthProc auth, char *format) -{ - int retval; - time_t ourtime; - ZNotice_t notice, retnotice; - char *bptr[3]; -#ifndef X_DISPLAY_MISSING - char *display; -#endif -#ifndef WIN32 - char *ttyp; - char *p; -#endif - struct hostent *hent; - short wg_port = ZGetWGPort(); - - (void) memset((char *)¬ice, 0, sizeof(notice)); - notice.z_kind = ACKED; - notice.z_port = (unsigned short) ((wg_port == -1) ? 0 : wg_port); - notice.z_class = class; - notice.z_class_inst = ZGetSender(); - notice.z_opcode = opcode; - notice.z_sender = 0; - notice.z_recipient = ""; - notice.z_num_other_fields = 0; - notice.z_default_format = format; - - /* - keep track of what we said before so that we can be consistent - when changing location information. - This is done mainly for the sake of the WindowGram client. - */ - - if (!reenter) { - if (gethostname(host, MAXHOSTNAMELEN) < 0) - return (errno); - - hent = gethostbyname(host); - if (hent) { - (void) strncpy(host, hent->h_name, sizeof(host)); - host[sizeof(host) - 1] = '\0'; - } -#ifndef X_DISPLAY_MISSING - if ((display = getenv("DISPLAY")) && *display) { - mytty = g_strdup(display); - } else { -#endif -#ifdef WIN32 - mytty = g_strdup("WinPurple"); -#else - ttyp = ttyname(0); - if (ttyp && *ttyp) { - p = strchr(ttyp + 1, '/'); - mytty = g_strdup((p) ? p + 1 : ttyp); - } else { - mytty = g_strdup("unknown"); - } -#endif -#ifndef X_DISPLAY_MISSING - } -#endif - reenter = 1; - } - - ourtime = time((time_t *)0); - bptr[0] = host; - bptr[1] = ctime(&ourtime); - bptr[1][strlen(bptr[1])-1] = '\0'; - bptr[2] = mytty; - - if ((retval = ZSendList(¬ice, bptr, 3, auth)) != ZERR_NONE) - return (retval); - - retval = Z_WaitForNotice (&retnotice, ZCompareUIDPred, ¬ice.z_uid, - SRV_TIMEOUT); - if (retval != ZERR_NONE) - return retval; - - if (retnotice.z_kind == SERVNAK) { - if (!retnotice.z_message_len) { - ZFreeNotice(&retnotice); - return (ZERR_SERVNAK); - } - if (purple_strequal(retnotice.z_message, ZSRVACK_NOTSENT)) { - ZFreeNotice(&retnotice); - return (ZERR_AUTHFAIL); - } - if (purple_strequal(retnotice.z_message, ZSRVACK_FAIL)) { - ZFreeNotice(&retnotice); - return (ZERR_LOGINFAIL); - } - ZFreeNotice(&retnotice); - return (ZERR_SERVNAK); - } - - if (retnotice.z_kind != SERVACK) { - ZFreeNotice(&retnotice); - return (ZERR_INTERNAL); - } - - if (!retnotice.z_message_len) { - ZFreeNotice(&retnotice); - return (ZERR_INTERNAL); - } - - if (!purple_strequal(retnotice.z_message, ZSRVACK_SENT) && - !purple_strequal(retnotice.z_message, ZSRVACK_NOTSENT)) { - ZFreeNotice(&retnotice); - return (ZERR_INTERNAL); - } - - ZFreeNotice(&retnotice); - - return (ZERR_NONE); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZMakeAscii.c --- a/libpurple/protocols/zephyr/ZMakeAscii.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZMakeAscii function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -static char *itox_chars = "0123456789ABCDEF"; - -Code_t -ZMakeAscii(register char *ptr, int len, unsigned char *field, int num) -{ - int i; - - for (i=0;i> 4)]; - *ptr++ = itox_chars[(int) (field[i] & 0xf)]; - len -= 2; - } - - *ptr = '\0'; - return ZERR_NONE; -} - -Code_t -ZMakeAscii32(register char *ptr, int len, unsigned long value) -{ - if (len < 11) - return ZERR_FIELDLEN; - *ptr++ = '0'; - *ptr++ = 'x'; - *ptr++ = itox_chars[(value >> 28) & 0xf]; - *ptr++ = itox_chars[(value >> 24) & 0xf]; - *ptr++ = itox_chars[(value >> 20) & 0xf]; - *ptr++ = itox_chars[(value >> 16) & 0xf]; - *ptr++ = itox_chars[(value >> 12) & 0xf]; - *ptr++ = itox_chars[(value >> 8) & 0xf]; - *ptr++ = itox_chars[(value >> 4) & 0xf]; - *ptr++ = itox_chars[(value >> 0) & 0xf]; - *ptr = 0; - return ZERR_NONE; -} - -Code_t -ZMakeAscii16(register char *ptr, int len, unsigned int value) -{ - if (len < 7) - return ZERR_FIELDLEN; - *ptr++ = '0'; - *ptr++ = 'x'; - *ptr++ = itox_chars[(value >> 12) & 0xf]; - *ptr++ = itox_chars[(value >> 8) & 0xf]; - *ptr++ = itox_chars[(value >> 4) & 0xf]; - *ptr++ = itox_chars[(value >> 0) & 0xf]; - *ptr = 0; - return ZERR_NONE; -} - diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZMkAuth.c --- a/libpurple/protocols/zephyr/ZMkAuth.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZMakeAuthentication function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -#ifndef ERROR_TABLE_BASE_krb -#define ERROR_TABLE_BASE_krb (39525376L) -#endif - -#ifdef ZEPHYR_USES_KERBEROS -#ifdef WIN32 - -#else -#include -#endif -static long last_authent_time = 0L; -static KTEXT_ST last_authent; -#endif - -#if 0 -Code_t ZResetAuthentication () { -#ifdef ZEPHYR_USES_KERBEROS - last_authent_time = 0L; -#endif - return ZERR_NONE; -} -#endif - -Code_t -ZMakeAuthentication(register ZNotice_t *notice, char *buffer, int buffer_len, - int *len) -{ -#ifdef ZEPHYR_USES_KERBEROS - int result; - time_t now; - KTEXT_ST authent; - char *cstart, *cend; - ZChecksum_t checksum; - CREDENTIALS cred; - extern unsigned long des_quad_cksum(); - - now = time(0); - if (last_authent_time == 0 || (now - last_authent_time > 120)) { - result = krb_mk_req(&authent, SERVER_SERVICE, - SERVER_INSTANCE, __Zephyr_realm, 0); - if (result != MK_AP_OK) { - last_authent_time = 0; - return (result+ERROR_TABLE_BASE_krb); - } - last_authent_time = now; - last_authent = authent; - } - else { - authent = last_authent; - } - notice->z_auth = 1; - notice->z_authent_len = authent.length; - notice->z_ascii_authent = (char *)malloc((unsigned)authent.length*3); - /* zero length authent is an error, so malloc(0) is not a problem */ - if (!notice->z_ascii_authent) - return (ENOMEM); - if ((result = ZMakeAscii(notice->z_ascii_authent, - authent.length*3, - authent.dat, - authent.length)) != ZERR_NONE) { - free(notice->z_ascii_authent); - return (result); - } - result = Z_FormatRawHeader(notice, buffer, buffer_len, len, &cstart, - &cend); - free(notice->z_ascii_authent); - notice->z_authent_len = 0; - if (result) - return(result); - - /* Compute a checksum over the header and message. */ - if ((result = krb_get_cred(SERVER_SERVICE, SERVER_INSTANCE, - __Zephyr_realm, &cred)) != 0) - return result; - checksum = des_quad_cksum(buffer, NULL, cstart - buffer, 0, (C_Block *)cred.session); - checksum ^= des_quad_cksum(cend, NULL, buffer + *len - cend, 0, - (C_Block *)cred.session); - checksum ^= des_quad_cksum(notice->z_message, NULL, notice->z_message_len, - 0, (C_Block *)cred.session); - notice->z_checksum = checksum; - ZMakeAscii32(cstart, buffer + buffer_len - cstart, checksum); - - return (ZERR_NONE); -#else - notice->z_checksum = 0; - notice->z_auth = 1; - notice->z_authent_len = 0; - notice->z_ascii_authent = ""; - return (Z_FormatRawHeader(notice, buffer, buffer_len, len, NULL, NULL)); -#endif -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZNewLocU.c --- a/libpurple/protocols/zephyr/ZNewLocU.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,40 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZNewLocateUser function. - * - * Created by: Robert French - * - * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -Code_t -ZLocateUser(char *user, int *nlocs, Z_AuthProc auth) -{ - Code_t retval; - ZNotice_t notice; - ZAsyncLocateData_t zald; - - (void) ZFlushLocations(); /* ZFlushLocations never fails (the library - is allowed to know this). */ - - if ((retval = ZRequestLocations(user, &zald, UNACKED, auth)) != ZERR_NONE) - return(retval); - - retval = Z_WaitForNotice (¬ice, ZCompareALDPred, &zald, SRV_TIMEOUT); - if (retval == ZERR_NONOTICE) - return ETIMEDOUT; - if (retval != ZERR_NONE) - return retval; - - if ((retval = ZParseLocations(¬ice, &zald, nlocs, NULL)) != ZERR_NONE) { - ZFreeNotice(¬ice); - return(retval); - } - - ZFreeNotice(¬ice); - ZFreeALD(&zald); - return(ZERR_NONE); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZOpenPort.c --- a/libpurple/protocols/zephyr/ZOpenPort.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZOpenPort function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" -#include "debug.h" -#ifdef WIN32 -#include -#else -#include -#endif - -Code_t -ZOpenPort(unsigned short *port) -{ - struct sockaddr_in bindin; - socklen_t len; - - (void) ZClosePort(); - memset(&bindin, 0, sizeof(bindin)); - - if ((__Zephyr_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { - __Zephyr_fd = -1; - return (errno); - } - -#ifdef SO_BSDCOMPAT - { - int on = 1; - - if (setsockopt(__Zephyr_fd, SOL_SOCKET, SO_BSDCOMPAT, - (char *)&on, sizeof(on)) != 0) - { - purple_debug_warning("zephyr", "couldn't setsockopt\n"); - } - } -#endif - - bindin.sin_family = AF_INET; - - if (port && *port) - bindin.sin_port = *port; - else - bindin.sin_port = 0; - - bindin.sin_addr.s_addr = INADDR_ANY; - - if (bind(__Zephyr_fd, (struct sockaddr *)&bindin, sizeof(bindin)) < 0) { - if (errno == EADDRINUSE && port && *port) - return (ZERR_PORTINUSE); - else - return (errno); - } - - if (!bindin.sin_port) { - len = sizeof(bindin); - if (getsockname(__Zephyr_fd, (struct sockaddr *)&bindin, &len)) - return (errno); - } - - __Zephyr_port = bindin.sin_port; - __Zephyr_open = 1; - - if (port) - *port = bindin.sin_port; - - return (ZERR_NONE); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZParseNot.c --- a/libpurple/protocols/zephyr/ZParseNot.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,285 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZParseNotice function. - * - * Created by: Robert French - * - * Copyright (c) 1987,1991 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -/* Assume that strlen is efficient on this machine... */ -#define next_field(ptr) ptr += strlen (ptr) + 1 - -#if defined (__GNUC__) && defined (__vax__) -#undef next_field -static __inline__ char * Istrend (char *str) { - /* - * This should be faster on VAX models outside the 2 series. Don't - * use it if you are using MicroVAX 2 servers. If you are using a - * VS2 server, use something like - * #define next_field(ptr) while(*ptr++) - * instead of this code. - * - * This requires use of GCC to get the optimized code, but - * everybody uses GCC, don't they? :-) - */ - register char *str2 asm ("r1"); - /* Assumes that no field is longer than 64K.... */ - asm ("locc $0,$65535,(%1)" : "=r" (str2) : "r" (str) : "r0"); - return str2; -} -#define next_field(ptr) ptr = Istrend (ptr) + 1 -#endif - -#ifdef mips -#undef next_field -/* - * The compiler doesn't optimize this macro as well as it does the - * following function. - */ -#define next_fieldXXX(ptr) do{register unsigned c1,c2;c1= *ptr; \ - while((ptr++,c2= *ptr,c1)&&(ptr++,c1= *ptr,c2));}while(0) -static char *next_field_1 (s) char *s; { - /* - * Calling overhead is still present, but this routine is faster - * than strlen, and doesn't bother with some of the other math - * that we'd just have to undo later anyways. - */ - register unsigned c1 = *s, c2; - while (1) { - s++; c2 = *s; if (c1 == 0) break; - s++; c1 = *s; if (c2 == 0) break; - s++; c2 = *s; if (c1 == 0) break; - s++; c1 = *s; if (c2 == 0) break; - } - return s; -} -#define next_field(ptr) ptr=next_field_1(ptr) -#endif - -Code_t -ZParseNotice(char *buffer, int len, ZNotice_t *notice) -{ - char *ptr, *end; - unsigned long temp; - int maj, numfields, i; - -#ifdef __LINE__ - int lineno; - /* Note: This definition of BAD eliminates lint and compiler - * complains about the "while (0)", but require that the macro not - * be used as the "then" part of an "if" statement that also has - * an "else" clause. - */ -#define BAD_PACKET {lineno=__LINE__;goto badpkt;} - /* This one gets lint/compiler complaints. */ -/*#define BAD do{lineno=__LINE__;goto badpkt;}while(0)*/ -#else -#define BAD_PACKET goto badpkt -#endif - - (void) memset((char *)notice, 0, sizeof(ZNotice_t)); - - ptr = buffer; - end = buffer+len; - - notice->z_packet = buffer; - - notice->z_version = ptr; - if (strncmp(ptr, ZVERSIONHDR, sizeof(ZVERSIONHDR) - 1)) - return (ZERR_VERS); - ptr += sizeof(ZVERSIONHDR) - 1; - if (!*ptr) { -#ifdef Z_DEBUG - Z_debug ("ZParseNotice: null version string"); -#endif - return ZERR_BADPKT; - } - maj = atoi(ptr); - if (maj != ZVERSIONMAJOR) - return (ZERR_VERS); - next_field (ptr); - - if (ZReadAscii32(ptr, end-ptr, &temp) == ZERR_BADFIELD) - BAD_PACKET; - numfields = temp; - next_field (ptr); - - /*XXX 3 */ - numfields -= 2; /* numfields, version, and checksum */ - if (numfields < 0) { -#ifdef __LINE__ - lineno = __LINE__; - badpkt: -#ifdef Z_DEBUG - Z_debug ("ZParseNotice: bad packet from %s/%d (line %d)", - inet_ntoa (notice->z_uid.zuid_addr.s_addr), - notice->z_port, lineno); -#else - /* We won't use lineno anywhere else, so let's silence a warning. */ - (void)lineno; -#endif -#else - badpkt: -#ifdef Z_DEBUG - Z_debug ("ZParseNotice: bad packet from %s/%d", - inet_ntoa (notice->z_uid.zuid_addr.s_addr), - notice->z_port); -#endif -#endif - return ZERR_BADPKT; - } - - if (numfields) { - if (ZReadAscii32(ptr, end-ptr, &temp) == ZERR_BADFIELD) - BAD_PACKET; - notice->z_kind = temp; - numfields--; - next_field (ptr); - } - else - BAD_PACKET; - - if (numfields) { - if (ZReadAscii(ptr, end-ptr, (unsigned char *)¬ice->z_uid, - sizeof(ZUnique_Id_t)) == ZERR_BADFIELD) - BAD_PACKET; - notice->z_time.tv_sec = ntohl((unsigned long) notice->z_uid.tv.tv_sec); - notice->z_time.tv_usec = ntohl((unsigned long) notice->z_uid.tv.tv_usec); - numfields--; - next_field (ptr); - } - else - BAD_PACKET; - - if (numfields) { - if (ZReadAscii16(ptr, end-ptr, ¬ice->z_port) == ZERR_BADFIELD) - BAD_PACKET; - notice->z_port = htons(notice->z_port); - numfields--; - next_field (ptr); - } - else - BAD_PACKET; - - if (numfields) { - if (ZReadAscii32(ptr, end-ptr, &temp) == ZERR_BADFIELD) - BAD_PACKET; - notice->z_auth = temp; - numfields--; - next_field (ptr); - } - else - BAD_PACKET; - notice->z_checked_auth = ZAUTH_UNSET; - - if (numfields) { - if (ZReadAscii32(ptr, end-ptr, &temp) == ZERR_BADFIELD) - BAD_PACKET; - notice->z_authent_len = temp; - numfields--; - next_field (ptr); - } - else - BAD_PACKET; - - if (numfields) { - notice->z_ascii_authent = ptr; - numfields--; - next_field (ptr); - } - else - BAD_PACKET; - - if (numfields) { - notice->z_class = ptr; - numfields--; - next_field (ptr); - } - else - notice->z_class = ""; - - if (numfields) { - notice->z_class_inst = ptr; - numfields--; - next_field (ptr); - } - else - notice->z_class_inst = ""; - - if (numfields) { - notice->z_opcode = ptr; - numfields--; - next_field (ptr); - } - else - notice->z_opcode = ""; - - if (numfields) { - notice->z_sender = ptr; - numfields--; - next_field (ptr); - } - else - notice->z_sender = ""; - - if (numfields) { - notice->z_recipient = ptr; - numfields--; - next_field (ptr); - } - else - notice->z_recipient = ""; - - if (numfields) { - notice->z_default_format = ptr; - numfields--; - next_field (ptr); - } - else - notice->z_default_format = ""; - -/*XXX*/ - if (ZReadAscii32(ptr, end-ptr, &temp) == ZERR_BADFIELD) - BAD_PACKET; - notice->z_checksum = temp; - numfields--; - next_field (ptr); - - if (numfields) { - notice->z_multinotice = ptr; - numfields--; - next_field (ptr); - } - else - notice->z_multinotice = ""; - - if (numfields) { - if (ZReadAscii(ptr, end-ptr, (unsigned char *)¬ice->z_multiuid, - sizeof(ZUnique_Id_t)) == ZERR_BADFIELD) - BAD_PACKET; - notice->z_time.tv_sec = ntohl((unsigned long) notice->z_multiuid.tv.tv_sec); - notice->z_time.tv_usec = ntohl((unsigned long) notice->z_multiuid.tv.tv_usec); - numfields--; - next_field (ptr); - } - else - notice->z_multiuid = notice->z_uid; - - for (i=0;iz_other_fields[i] = ptr; - next_field (ptr); - } - notice->z_num_other_fields = i; - - for (i=0;iz_message = (void *)ptr; - notice->z_message_len = len-(ptr-buffer); - - return (ZERR_NONE); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZPeekIfNot.c --- a/libpurple/protocols/zephyr/ZPeekIfNot.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZPeekIfNotice function. - * - * Created by: Robert French - * - * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -#if 0 -Code_t ZPeekIfNotice(notice, from, predicate, args) - ZNotice_t *notice; - struct sockaddr_in *from; - int (*predicate)(); - char *args; -{ - ZNotice_t tmpnotice; - Code_t retval; - char *buffer; - struct _Z_InputQ *qptr; - - if ((retval = Z_WaitForComplete()) != ZERR_NONE) - return (retval); - - for (;;) { - qptr = Z_GetFirstComplete(); - while (qptr) { - if ((retval = ZParseNotice(qptr->packet, qptr->packet_len, - &tmpnotice)) != ZERR_NONE) - return (retval); - if ((*predicate)(&tmpnotice, args)) { - if (!(buffer = (char *) malloc((unsigned) qptr->packet_len))) - return (ENOMEM); - (void) memcpy(buffer, qptr->packet, qptr->packet_len); - if (from) - *from = qptr->from; - if ((retval = ZParseNotice(buffer, qptr->packet_len, - notice)) != ZERR_NONE) { - free(buffer); - return (retval); - } - return (ZERR_NONE); - } - qptr = Z_GetNextComplete(qptr); - } - if ((retval = Z_ReadWait()) != ZERR_NONE) - return (retval); - } -} -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZPeekNot.c --- a/libpurple/protocols/zephyr/ZPeekNot.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for ZPeekNotice function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -Code_t -ZPeekNotice(ZNotice_t *notice, struct sockaddr_in *from) -{ - char *buffer; - int len; - Code_t retval; - - if ((retval = ZPeekPacket(&buffer, &len, from)) != ZERR_NONE) - return (retval); - - return (ZParseNotice(buffer, len, notice)); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZPeekPkt.c --- a/libpurple/protocols/zephyr/ZPeekPkt.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for ZPeekPacket function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -Code_t -ZPeekPacket(char **buffer, int *ret_len, struct sockaddr_in *from) -{ - Code_t retval; - struct _Z_InputQ *nextq; - - if ((retval = Z_WaitForComplete()) != ZERR_NONE) - return (retval); - - nextq =Z_GetFirstComplete(); - - *ret_len = nextq->packet_len; - - if (!(*buffer = (char *) malloc((unsigned) *ret_len))) - return (ENOMEM); - - (void) memcpy(*buffer, nextq->packet, *ret_len); - - if (from) - *from = nextq->from; - - return (ZERR_NONE); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZPending.c --- a/libpurple/protocols/zephyr/ZPending.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZPending function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -int -ZPending(void) -{ - int retval; - - if (ZGetFD() < 0) { - errno = ZERR_NOPORT; - return (-1); - } - - if ((retval = Z_ReadEnqueue()) != ZERR_NONE) { - errno = retval; - return (-1); - } - - return(ZQLength()); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZReadAscii.c --- a/libpurple/protocols/zephyr/ZReadAscii.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZReadAscii function. - * - * Created by: Robert French - * - * Copyright (c) 1987, 1990 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -#define Z_cnvt_xtoi(c) ((temp=(c)-'0'),(temp<10)?(int)temp:((temp-='A'-'9'-1),(temp<16)?(int)temp:-1)) - -Code_t -ZReadAscii(char *ptr, int len, unsigned char *field, int num) -{ - int i; - unsigned int hexbyte; - register int c1, c2; - register unsigned int temp; - - for (i=0;ipacket_len; - - if (!(buffer = (char *) malloc((unsigned) len))) - return (ENOMEM); - - if (from) - *from = nextq->from; - - (void) memcpy(buffer, nextq->packet, len); - - auth = nextq->auth; - Z_RemQueue(nextq); - - if ((retval = ZParseNotice(buffer, len, notice)) != ZERR_NONE) - return (retval); - notice->z_checked_auth = auth; - return ZERR_NONE; -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZRecvPkt.c --- a/libpurple/protocols/zephyr/ZRecvPkt.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,36 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for ZReceivePacket function. - * - * Created by: Robert French - * - * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -Code_t -ZReceivePacket(ZPacket_t buffer, int *ret_len, struct sockaddr_in *from) -{ - Code_t retval; - struct _Z_InputQ *nextq; - - if ((retval = Z_WaitForComplete()) != ZERR_NONE) - return (retval); - - nextq = Z_GetFirstComplete(); - - *ret_len = nextq->packet_len; - if (*ret_len > Z_MAXPKTLEN) - return (ZERR_PKTLEN); - - (void) memcpy(buffer, nextq->packet, *ret_len); - - if (from) - *from = nextq->from; - - Z_RemQueue(nextq); - - return (ZERR_NONE); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZRetSubs.c --- a/libpurple/protocols/zephyr/ZRetSubs.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,184 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZRetrieveSubscriptions and - * ZRetrieveDefaultSubscriptions functions. - * - * Created by: Robert French - * - * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" -#include "util.h" - -static Code_t Z_RetSubs(ZNotice_t *notice, int *nsubs, Z_AuthProc auth_routine); - -/* Need STDC definition when possible for unsigned short argument. */ -#ifdef __STDC__ -Code_t ZRetrieveSubscriptions(unsigned short port, int *nsubs) -#else -Code_t ZRetrieveSubscriptions(port,nsubs) - unsigned short port; - int *nsubs; -#endif -{ - int retval; - ZNotice_t notice; - char asciiport[50]; - - if (!port) /* use default port */ - port = __Zephyr_port; - - retval = ZMakeAscii16(asciiport, sizeof(asciiport), ntohs(port)); - if (retval != ZERR_NONE) - return (retval); - - (void) memset((char *)¬ice, 0, sizeof(notice)); - notice.z_message = asciiport; - notice.z_message_len = strlen(asciiport)+1; - notice.z_opcode = CLIENT_GIMMESUBS; - - return(Z_RetSubs(¬ice, nsubs, ZAUTH)); -} - -#if 0 -Code_t ZRetrieveDefaultSubscriptions(nsubs) - int *nsubs; -{ - ZNotice_t notice; - - (void) memset((char *)¬ice, 0, sizeof(notice)); - notice.z_message = (char *) 0; - notice.z_message_len = 0; - notice.z_opcode = CLIENT_GIMMEDEFS; - - return(Z_RetSubs(¬ice, nsubs, ZNOAUTH)); - -} -#endif - -static Code_t -Z_RetSubs(register ZNotice_t *notice, int *nsubs, Z_AuthProc auth_routine) -{ - register int i; - int retval,nrecv,gimmeack; - ZNotice_t retnotice; - char *ptr,*end,*ptr2; - - retval = ZFlushSubscriptions(); - - if (retval != ZERR_NONE && retval != ZERR_NOSUBSCRIPTIONS) - return (retval); - - if (ZGetFD() < 0) - if ((retval = ZOpenPort((unsigned short *)0)) != ZERR_NONE) - return (retval); - - notice->z_kind = ACKED; - notice->z_port = __Zephyr_port; - notice->z_class = ZEPHYR_CTL_CLASS; - notice->z_class_inst = ZEPHYR_CTL_CLIENT; - notice->z_sender = 0; - notice->z_recipient = ""; - notice->z_default_format = ""; - - if ((retval = ZSendNotice(notice,auth_routine)) != ZERR_NONE) - return (retval); - - nrecv = 0; - gimmeack = 0; - __subscriptions_list = (ZSubscription_t *) 0; - - while (!nrecv || !gimmeack) { - retval = Z_WaitForNotice (&retnotice, ZCompareMultiUIDPred, - ¬ice->z_multiuid, SRV_TIMEOUT); - if (retval == ZERR_NONOTICE) - return ETIMEDOUT; - else if (retval != ZERR_NONE) - return retval; - - if (retnotice.z_kind == SERVNAK) { - ZFreeNotice(&retnotice); - return (ZERR_SERVNAK); - } - /* non-matching protocol version numbers means the - server is probably an older version--must punt */ - if (!purple_strequal(notice->z_version,retnotice.z_version)) { - ZFreeNotice(&retnotice); - return(ZERR_VERS); - } - if (retnotice.z_kind == SERVACK && - purple_strequal(retnotice.z_opcode,notice->z_opcode)) { - ZFreeNotice(&retnotice); - gimmeack = 1; - continue; - } - - if (retnotice.z_kind != ACKED) { - ZFreeNotice(&retnotice); - return (ZERR_INTERNAL); - } - - nrecv++; - - end = retnotice.z_message+retnotice.z_message_len; - - __subscriptions_num = 0; - for (ptr=retnotice.z_message;ptr -#else -#include -#endif - -static int wait_for_hmack(ZNotice_t *notice, void *uid); - -Code_t -ZSendPacket(char *packet, int len, int waitforack) -{ - Code_t retval; - struct sockaddr_in dest; - ZNotice_t notice, acknotice; - - if (!packet || len < 0) - return (ZERR_ILLVAL); - - if (len > Z_MAXPKTLEN) - return (ZERR_PKTLEN); - - if (ZGetFD() < 0) - if ((retval = ZOpenPort((unsigned short *)0)) != ZERR_NONE) - return (retval); - - dest = ZGetDestAddr(); - - if (sendto(ZGetFD(), packet, len, 0, (struct sockaddr *)&dest, - sizeof(dest)) < 0) - return (errno); - - if (!waitforack) - return (ZERR_NONE); - - if ((retval = ZParseNotice(packet, len, ¬ice)) != ZERR_NONE) - return (retval); - - retval = Z_WaitForNotice (&acknotice, wait_for_hmack, ¬ice.z_uid, - HM_TIMEOUT); - if (retval == ETIMEDOUT) - return ZERR_HMDEAD; - if (retval == ZERR_NONE) - ZFreeNotice (&acknotice); - return retval; -} - -static int wait_for_hmack(ZNotice_t *notice, void *uid) -{ - return (notice->z_kind == HMACK && ZCompareUID(¬ice->z_uid, (ZUnique_Id_t *)uid)); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZSendRLst.c --- a/libpurple/protocols/zephyr/ZSendRLst.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZSendRawList function. - * - * Created by: John T. Kohl - * - * Copyright (c) 1988 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -#if 0 -Code_t ZSendRawList(notice, list, nitems) - ZNotice_t *notice; - char *list[]; - int nitems; -{ - return(ZSrvSendRawList(notice, list, nitems, Z_XmitFragment)); -} - -Code_t ZSrvSendRawList(notice, list, nitems, send_routine) - ZNotice_t *notice; - char *list[]; - int nitems; - Code_t (*send_routine)(); -{ - Code_t retval; - ZNotice_t newnotice; - char *buffer; - int len; - - if ((retval = ZFormatRawNoticeList(notice, list, nitems, &buffer, - &len)) != ZERR_NONE) - return (retval); - - if ((retval = ZParseNotice(buffer, len, &newnotice)) != ZERR_NONE) - return (retval); - - retval = Z_SendFragmentedNotice(&newnotice, len, NULL, send_routine); - - free(buffer); - - return (retval); -} -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZSendRaw.c --- a/libpurple/protocols/zephyr/ZSendRaw.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZSendRawNotice function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -#if 0 -Code_t ZSendRawNotice(notice) - ZNotice_t *notice; -{ - Code_t retval; - ZNotice_t newnotice; - char *buffer; - int len; - - if ((retval = ZFormatRawNotice(notice, &buffer, &len)) != - ZERR_NONE) - return (retval); - - if ((retval = ZParseNotice(buffer, len, &newnotice)) != ZERR_NONE) - return (retval); - - retval = Z_SendFragmentedNotice(&newnotice, len, NULL, Z_XmitFragment); - - free(buffer); - - return (retval); -} -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZSetDest.c --- a/libpurple/protocols/zephyr/ZSetDest.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,21 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZSetDestAddr function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -Code_t -ZSetDestAddr(struct sockaddr_in *addr) -{ - __HM_addr = *addr; - - __HM_set = 1; - - return (ZERR_NONE); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZSetFD.c --- a/libpurple/protocols/zephyr/ZSetFD.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,22 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZSetFD function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -Code_t -ZSetFD(int fd) -{ - (void) ZClosePort(); - - __Zephyr_fd = fd; - __Zephyr_open = 0; - - return (ZERR_NONE); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZSetSrv.c --- a/libpurple/protocols/zephyr/ZSetSrv.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZSetServerState function. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -Code_t -ZSetServerState(int state) -{ - __Zephyr_server = state; - - return (ZERR_NONE); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZSubs.c --- a/libpurple/protocols/zephyr/ZSubs.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,180 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZSubscribeTo, ZUnsubscribeTo, and - * ZCancelSubscriptions functions. - * - * Created by: Robert French - * - * Copyright (c) 1987,1988 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -static Code_t Z_Subscriptions __P((register ZSubscription_t *sublist, - int nitems, unsigned int port, - char *opcode, int authit)); -static Code_t subscr_sendoff __P((ZNotice_t *notice, char **lyst, int num, - int authit)); - -Code_t -ZSubscribeTo(ZSubscription_t *sublist, int nitems, unsigned int port) -{ - return (Z_Subscriptions(sublist, nitems, port, CLIENT_SUBSCRIBE, 1)); -} - -Code_t -ZSubscribeToSansDefaults(ZSubscription_t *sublist, int nitems, - unsigned int port) -{ - return (Z_Subscriptions(sublist, nitems, port, CLIENT_SUBSCRIBE_NODEFS, - 1)); -} - -Code_t -ZUnsubscribeTo(ZSubscription_t *sublist, int nitems, unsigned int port) -{ - return (Z_Subscriptions(sublist, nitems, port, CLIENT_UNSUBSCRIBE, 1)); -} - -Code_t -ZCancelSubscriptions(unsigned int port) -{ - return (Z_Subscriptions((ZSubscription_t *)0, 0, port, - CLIENT_CANCELSUB, 0)); -} - -/* - * This routine must do its own fragmentation. Subscriptions must - * not be broken across packet boundaries, or else the server will - * mis-interpret them. - */ - -static Code_t -Z_Subscriptions(register ZSubscription_t *sublist, int nitems, - unsigned int port, char *opcode, int authit) -{ - register int i, j; - int retval; - ZNotice_t notice; - char header[Z_MAXHEADERLEN]; - char **list; - char *recip; - int hdrlen; - int size_avail = Z_MAXPKTLEN-Z_FRAGFUDGE; /* space avail for data, - adjusted below */ - int size, start, numok; - - /* nitems = 0 means cancel all subscriptions; still need to allocate a */ - /* array for one item so we can cancel, however. */ - - list = (char **)malloc((unsigned)((nitems==0)?1:nitems)*3*sizeof(char *)); - if (!list) - return (ENOMEM); - - (void) memset((char *)¬ice, 0, sizeof(notice)); - notice.z_kind = ACKED; - notice.z_port = port; - notice.z_class = ZEPHYR_CTL_CLASS; - notice.z_class_inst = ZEPHYR_CTL_CLIENT; - notice.z_opcode = opcode; - notice.z_sender = 0; - notice.z_recipient = ""; - notice.z_default_format = ""; - notice.z_message_len = 0; - - /* format the header to figure out how long it is */ - retval = Z_FormatHeader(¬ice, header, sizeof(header), &hdrlen, ZAUTH); - if (retval != ZERR_NONE && !authit) - retval = Z_FormatHeader(¬ice, header, sizeof(header), - &hdrlen, ZNOAUTH); - if (retval != ZERR_NONE) { - free((char *)list); - return(retval); - } - - /* compute amount of room left */ - size_avail -= hdrlen; - size = size_avail; - - /* assemble subs into an array of pointers */ - for (i=0;iz_uid)) != - ZERR_NONE) - return (retval); - if (retnotice.z_kind == SERVNAK) { - ZFreeNotice(&retnotice); - return (ZERR_SERVNAK); - } - if (retnotice.z_kind != SERVACK) { - ZFreeNotice(&retnotice); - return (ZERR_INTERNAL); - } - ZFreeNotice(&retnotice); - return (ZERR_NONE); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZVariables.c --- a/libpurple/protocols/zephyr/ZVariables.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,220 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the ZGetVariable, ZSetVariable, and ZUnsetVariable - * functions. - * - * Created by: Robert French - * - * Copyright (c) 1987 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "libpurple/internal.h" -#include "internal.h" -#include "util.h" - -#include -#ifndef WIN32 -#include -#endif - -static char *get_localvarfile __P((void)); -static char *get_varval __P((char *fn, char *val)); -static int varline __P((char *bfr, char *var)); - -char *ZGetVariable(var) - char *var; -{ - char *varfile, *ret; - - if ((varfile = get_localvarfile()) == NULL) - return ((char *)0); - - ret = get_varval(varfile, var); - g_free(varfile); - if (ret != ZERR_NONE) - return ret; - -#ifdef WIN32 - varfile = g_strdup("C:\\zephyr\\zephyr.var"); -#else - varfile = g_strdup_printf("%s/zephyr.vars", CONFDIR); -#endif - ret = get_varval(varfile, var); - g_free(varfile); - - return ret; -} - -Code_t -ZSetVariable(char *var, char *value) -{ - int written; - FILE *fpin, *fpout; - char *varfile, *varfilebackup, varbfr[512]; - - written = 0; - - if ((varfile = get_localvarfile()) == NULL) - return (ZERR_INTERNAL); - - varfilebackup = g_strconcat(varfile, ".backup", NULL); - - if (!(fpout = fopen(varfilebackup, "w"))) { - g_free(varfile); - g_free(varfilebackup); - return (errno); - } - if ((fpin = fopen(varfile, "r")) != NULL) { - while (fgets(varbfr, sizeof varbfr, fpin) != (char *) 0) { - if (varbfr[strlen(varbfr)-1] < ' ') - varbfr[strlen(varbfr)-1] = '\0'; - if (varline(varbfr, var)) { - fprintf(fpout, "%s = %s\n", var, value); - written = 1; - } - else - fprintf(fpout, "%s\n", varbfr); - } - (void) fclose(fpin); /* don't care about errs on input */ - } - if (!written) - fprintf(fpout, "%s = %s\n", var, value); - if (fclose(fpout) == EOF) { - g_free(varfilebackup); - g_free(varfile); - return(EIO); /* can't rely on errno */ - } - if (rename(varfilebackup, varfile)) { - g_free(varfilebackup); - g_free(varfile); - return (errno); - } - g_free(varfilebackup); - g_free(varfile); - return (ZERR_NONE); -} - -Code_t -ZUnsetVariable(char *var) -{ - FILE *fpin, *fpout; - char *varfile, *varfilebackup, varbfr[512]; - - if ((varfile = get_localvarfile()) == NULL) - return (ZERR_INTERNAL); - - varfilebackup = g_strconcat(varfile, ".backup", NULL); - - if (!(fpout = fopen(varfilebackup, "w"))) { - g_free(varfile); - g_free(varfilebackup); - return (errno); - } - if ((fpin = fopen(varfile, "r")) != NULL) { - while (fgets(varbfr, sizeof varbfr, fpin) != (char *) 0) { - if (varbfr[strlen(varbfr)-1] < ' ') - varbfr[strlen(varbfr)-1] = '\0'; - if (!varline(varbfr, var)) - fprintf(fpout, "%s\n", varbfr); - } - (void) fclose(fpin); /* don't care about read close errs */ - } - if (fclose(fpout) == EOF) { - g_free(varfilebackup); - g_free(varfile); - return(EIO); /* errno isn't reliable */ - } - if (rename(varfilebackup, varfile)) { - g_free(varfilebackup); - g_free(varfile); - return (errno); - } - g_free(varfilebackup); - g_free(varfile); - return (ZERR_NONE); -} - -static char *get_localvarfile(void) -{ - const char *base; -#ifndef WIN32 - struct passwd *pwd; - base = purple_home_dir(); -#else - base = getenv("HOME"); - if (!base) - base = getenv("HOMEPATH"); - if (!base) - base = "C:\\"; -#endif - if (!base) { -#ifndef WIN32 - if (!(pwd = getpwuid((int) getuid()))) { - fprintf(stderr, "Zephyr internal failure: Can't find your entry in /etc/passwd\n"); - return NULL; - } - base = pwd->pw_dir; -#endif - } - - return g_strconcat(base, "/.zephyr.vars", NULL); -} - -static char *get_varval(fn, var) - char *fn; - char *var; -{ - FILE *fp; - static char varbfr[512]; - int i; - - fp = fopen(fn, "r"); - if (!fp) - return ((char *)0); - - while (fgets(varbfr, sizeof varbfr, fp) != (char *) 0) { - if (varbfr[strlen(varbfr)-1] < ' ') - varbfr[strlen(varbfr)-1] = '\0'; - if (!(i = varline(varbfr, var))) - continue; - (void) fclose(fp); /* open read-only, don't care */ - return (varbfr+i); - } - (void) fclose(fp); /* open read-only, don't care */ - return ((char *)0); -} - -/* If the variable in the line bfr[] is the same as var, return index to - the variable value, else return 0. */ -static int varline(bfr, var) - char *bfr; - char *var; -{ - register char *cp; - - - if (!bfr[0] || bfr[0] == '#') /* comment or null line */ - return (0); - - cp = bfr; - while (*cp && !isspace(*cp) && (*cp != '=')) - cp++; - -#ifndef WIN32 -#define max(a,b) ((a > b) ? (a) : (b)) -#endif - - if (g_ascii_strncasecmp(bfr, var, max(strlen(var), (gsize)(cp - bfr)))) - return(0); /* var is not the var in - bfr ==> no match */ - - cp = strchr(bfr, '='); - if (!cp) - return(0); - cp++; - while (*cp && isspace(*cp)) /* space up to variable value */ - cp++; - - return (cp - bfr); /* return index */ -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZWait4Not.c --- a/libpurple/protocols/zephyr/ZWait4Not.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,78 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains the ZCheckIfNotice/select loop used for waiting for - * a notice, with a timeout. - * - * Copyright (c) 1991 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -#ifdef WIN32 -#include - -#ifndef ZEPHYR_USES_KERBEROS -static int gettimeofday(struct timeval* tv, struct timezone* tz){ - union { - long long ns100; /*time since 1 Jan 1601 in 100ns units */ - FILETIME ft; - } _now; - - GetSystemTimeAsFileTime( &(_now.ft) ); - tv->tv_usec=(long)((_now.ns100 / 10LL) % 1000000LL ); - tv->tv_sec= (long)((_now.ns100-(116444736000000000LL))/10000000LL); - return 0; - } -#endif - -#else -#include -#endif - -Code_t -Z_WaitForNotice(ZNotice_t *notice, int (*pred) __P((ZNotice_t *, void *)), void *arg, - int timeout) -{ - Code_t retval; - struct timeval tv, t0; - fd_set fdmask; - int i, fd; - - retval = ZCheckIfNotice (notice, (struct sockaddr_in *) 0, pred, - (char *) arg); - if (retval == ZERR_NONE) - return ZERR_NONE; - if (retval != ZERR_NONOTICE) - return retval; - - fd = ZGetFD (); - FD_ZERO (&fdmask); - tv.tv_sec = timeout; - tv.tv_usec = 0; - gettimeofday (&t0, (struct timezone *)NULL); - t0.tv_sec += timeout; - while (1) { - FD_SET (fd, &fdmask); - i = select (fd + 1, &fdmask, (fd_set *) 0, (fd_set *) 0, &tv); - if (i == 0) - return ETIMEDOUT; - if (i < 0 && errno != EINTR) - return errno; - if (i > 0) { - retval = ZCheckIfNotice (notice, (struct sockaddr_in *) 0, pred, - (char *) arg); - if (retval != ZERR_NONOTICE) /* includes ZERR_NONE */ - return retval; - } - gettimeofday (&tv, (struct timezone *) NULL); - tv.tv_usec = t0.tv_usec - tv.tv_usec; - if (tv.tv_usec < 0) { - tv.tv_usec += 1000000; - tv.tv_sec = t0.tv_sec - tv.tv_sec - 1; - } - else - tv.tv_sec = t0.tv_sec - tv.tv_sec; - } - /*NOTREACHED*/ -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/ZhmStat.c --- a/libpurple/protocols/zephyr/ZhmStat.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains the ZhmStat() function. - * - * Created by: Marc Horowitz - * - * Copyright (c) 1996 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" - -#ifdef WIN32 -#include -#else -#include -#endif - -#ifndef INADDR_LOOPBACK -#define INADDR_LOOPBACK 0x7f000001 -#endif - -Code_t -ZhmStat(struct in_addr *hostaddr, ZNotice_t *notice) -{ - struct servent *sp; - struct sockaddr_in sin; - ZNotice_t req; - Code_t code; - struct timeval tv; - fd_set readers; - - (void) memset((char *)&sin, 0, sizeof(struct sockaddr_in)); - - sp = getservbyname(HM_SVCNAME, "udp"); - - sin.sin_port = (sp) ? sp->s_port : HM_SVC_FALLBACK; - sin.sin_family = AF_INET; - - if (hostaddr) - sin.sin_addr = *hostaddr; - else - sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - (void) memset((char *)&req, 0, sizeof(req)); - req.z_kind = STAT; - req.z_port = 0; - req.z_class = HM_STAT_CLASS; - req.z_class_inst = HM_STAT_CLIENT; - req.z_opcode = HM_GIMMESTATS; - req.z_sender = ""; - req.z_recipient = ""; - req.z_default_format = ""; - req.z_message_len = 0; - - if ((code = ZSetDestAddr(&sin)) != ZERR_NONE) - return(code); - - if ((code = ZSendNotice(&req, ZNOAUTH)) != ZERR_NONE) - return(code); - - /* Wait up to ten seconds for a response. */ - FD_ZERO(&readers); - FD_SET(ZGetFD(), &readers); - tv.tv_sec = 10; - tv.tv_usec = 0; - code = select(ZGetFD() + 1, &readers, NULL, NULL, &tv); - if (code < 0 && errno != EINTR) - return(errno); - if (code == 0 || (code < 0 && errno == EINTR) || ZPending() == 0) - return(ZERR_HMDEAD); - - return(ZReceiveNotice(notice, (struct sockaddr_in *) 0)); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/Zinternal.c --- a/libpurple/protocols/zephyr/Zinternal.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,994 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains source for the internal Zephyr routines. - * - * Created by: Robert French - * - * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of - * Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#include "internal.h" -#ifdef WIN32 -#include - -#ifndef ZEPHYR_USES_KERBEROS - int gettimeofday(struct timeval* p, struct timezone* tz ){ - union { - long long ns100; /*time since 1 Jan 1601 in 100ns units */ - FILETIME ft; - } _now; - - GetSystemTimeAsFileTime( &(_now.ft) ); - p->tv_usec=(long)((_now.ns100 / 10LL) % 1000000LL ); - p->tv_sec= (long)((_now.ns100-(116444736000000000LL))/10000000LL); - return 0; - } -#endif - -#else -#include -#include -#endif - -int __Zephyr_fd = -1; -int __Zephyr_open; -int __Zephyr_port = -1; -struct in_addr __My_addr; -int __Q_CompleteLength; -int __Q_Size; -struct _Z_InputQ *__Q_Head, *__Q_Tail; -struct sockaddr_in __HM_addr; -struct sockaddr_in __HM_addr_real; -int __HM_set; -int __Zephyr_server; -ZLocations_t *__locate_list; -int __locate_num; -int __locate_next; -ZSubscription_t *__subscriptions_list; -int __subscriptions_num; -int __subscriptions_next; -int Z_discarded_packets = 0; - -#ifdef ZEPHYR_USES_KERBEROS -C_Block __Zephyr_session; -#endif -char __Zephyr_realm[REALM_SZ]; - -#ifdef Z_DEBUG -void (*__Z_debug_print) __P((const char *fmt, va_list args, void *closure)); -void *__Z_debug_print_closure; -#endif - -#define min(a,b) ((a)<(b)?(a):(b)) - -static int Z_AddField __P((char **ptr, const char *field, char *end)); -static int find_or_insert_uid __P((ZUnique_Id_t *uid, ZNotice_Kind_t kind)); - -/* Find or insert uid in the old uids buffer. The buffer is a sorted - * circular queue. We make the assumption that most packets arrive in - * order, so we can usually search for a uid or insert it into the buffer - * by looking back just a few entries from the end. Since this code is - * only executed by the client, the implementation isn't microoptimized. */ -static int -find_or_insert_uid(ZUnique_Id_t *uid, ZNotice_Kind_t kind) -{ - static struct _filter { - ZUnique_Id_t uid; - ZNotice_Kind_t kind; - time_t t; - } *buffer; - static long size; - static long start; - static long num; - - time_t now; - struct _filter *new; - long i, j, new_size; - int result; - - /* Initialize the uid buffer if it hasn't been done already. */ - if (!buffer) { - size = Z_INITFILTERSIZE; - buffer = (struct _filter *) malloc(size * sizeof(*buffer)); - if (!buffer) - return 0; - } - - /* Age the uid buffer, discarding any uids older than the clock skew. */ - time(&now); - while (num && (now - buffer[start % size].t) > CLOCK_SKEW) - start++, num--; - start %= size; - - /* Make room for a new uid, since we'll probably have to insert one. */ - if (num == size) { - new_size = size * 2 + 2; - new = (struct _filter *) malloc(new_size * sizeof(*new)); - if (!new) - return 0; - for (i = 0; i < num; i++) - new[i] = buffer[(start + i) % size]; - free(buffer); - buffer = new; - size = new_size; - start = 0; - } - - /* Search for this uid in the buffer, starting from the end. */ - for (i = start + num - 1; i >= start; i--) { - result = memcmp(uid, &buffer[i % size].uid, sizeof(*uid)); - if (result == 0 && buffer[i % size].kind == kind) - return 1; - if (result > 0) - break; - } - - /* We didn't find it; insert the uid into the buffer after i. */ - i++; - for (j = start + num; j > i; j--) - buffer[j % size] = buffer[(j - 1) % size]; - buffer[i % size].uid = *uid; - buffer[i % size].kind = kind; - buffer[i % size].t = now; - num++; - - return 0; -} - - -/* Return 1 if there is a packet waiting, 0 otherwise */ - -static int Z_PacketWaiting(void) -{ - struct timeval tv; - fd_set read; - - tv.tv_sec = tv.tv_usec = 0; - FD_ZERO(&read); - FD_SET(ZGetFD(), &read); - return (select(ZGetFD() + 1, &read, NULL, NULL, &tv)); -} - - -/* Wait for a complete notice to become available */ - -Code_t Z_WaitForComplete(void) -{ - Code_t retval; - - if (__Q_CompleteLength) - return (Z_ReadEnqueue()); - - while (!__Q_CompleteLength) - if ((retval = Z_ReadWait()) != ZERR_NONE) - return (retval); - - return (ZERR_NONE); -} - - -/* Read any available packets and enqueue them */ - -Code_t -Z_ReadEnqueue(void) -{ - Code_t retval; - - if (ZGetFD() < 0) - return (ZERR_NOPORT); - - while (Z_PacketWaiting()) - if ((retval = Z_ReadWait()) != ZERR_NONE) - return (retval); - - return (ZERR_NONE); -} - - -/* - * Search the queue for a notice with the proper multiuid - remove any - * notices that haven't been touched in a while - */ - -static struct _Z_InputQ *Z_SearchQueue(ZUnique_Id_t *uid, ZNotice_Kind_t kind) -{ - register struct _Z_InputQ *qptr; - struct _Z_InputQ *next; - struct timeval tv; - - (void) gettimeofday(&tv, (struct timezone *)0); - - qptr = __Q_Head; - - while (qptr) { - if (ZCompareUID(uid, &qptr->uid) && qptr->kind == kind) - return (qptr); - next = qptr->next; - if (qptr->timep && ((time_t)qptr->timep+Z_NOTICETIMELIMIT < tv.tv_sec)) - Z_RemQueue(qptr); - qptr = next; - } - return (NULL); -} - -/* - * Now we delve into really convoluted queue handling and - * fragmentation reassembly algorithms and other stuff you probably - * don't want to look at... - * - * This routine does NOT guarantee a complete packet will be ready when it - * returns. - */ - -Code_t -Z_ReadWait(void) -{ - register struct _Z_InputQ *qptr; - ZNotice_t notice; - ZPacket_t packet; - struct sockaddr_in olddest, from; - int packet_len, zvlen, part, partof; - socklen_t from_len; - char *slash; - Code_t retval; - fd_set fds; - struct timeval tv; - - if (ZGetFD() < 0) - return (ZERR_NOPORT); - - FD_ZERO(&fds); - FD_SET(ZGetFD(), &fds); - tv.tv_sec = 60; - tv.tv_usec = 0; - - if (select(ZGetFD() + 1, &fds, NULL, NULL, &tv) < 0) - return (errno); - if (!FD_ISSET(ZGetFD(), &fds)) - return ETIMEDOUT; - - from_len = sizeof(struct sockaddr_in); - - packet_len = recvfrom(ZGetFD(), packet, sizeof(packet) - 1, 0, - (struct sockaddr *)&from, &from_len); - - if (packet_len < 0) - return (errno); - - if (!packet_len) - return (ZERR_EOF); - - packet[packet_len] = '\0'; - - /* Ignore obviously non-Zephyr packets. */ - zvlen = sizeof(ZVERSIONHDR) - 1; - if (packet_len < zvlen || memcmp(packet, ZVERSIONHDR, zvlen) != 0) { - Z_discarded_packets++; - return (ZERR_NONE); - } - - /* Parse the notice */ - if ((retval = ZParseNotice(packet, packet_len, ¬ice)) != ZERR_NONE) - return (retval); - - /* - * If we're not a server and the notice is of an appropriate kind, - * send back a CLIENTACK to whoever sent it to say we got it. - */ - if (!__Zephyr_server) { - if (notice.z_kind != HMACK && notice.z_kind != SERVACK && - notice.z_kind != SERVNAK && notice.z_kind != CLIENTACK) { - ZNotice_t tmpnotice; - ZPacket_t pkt; - int len; - - tmpnotice = notice; - tmpnotice.z_kind = CLIENTACK; - tmpnotice.z_message_len = 0; - olddest = __HM_addr; - __HM_addr = from; - if ((retval = ZFormatSmallRawNotice(&tmpnotice, pkt, &len)) - != ZERR_NONE) - return(retval); - if ((retval = ZSendPacket(pkt, len, 0)) != ZERR_NONE) - return (retval); - __HM_addr = olddest; - } - if (find_or_insert_uid(¬ice.z_uid, notice.z_kind)) - return(ZERR_NONE); - - /* Check authentication on the notice. */ - notice.z_checked_auth = ZCheckAuthentication(¬ice, &from); - } - - - /* - * Parse apart the z_multinotice field - if the field is blank for - * some reason, assume this packet stands by itself. - */ - slash = strchr(notice.z_multinotice, '/'); - if (slash) { - part = atoi(notice.z_multinotice); - partof = atoi(slash+1); - if (part > partof || partof == 0) { - part = 0; - partof = notice.z_message_len; - } - } - else { - part = 0; - partof = notice.z_message_len; - } - - /* Too big a packet...just ignore it! */ - if (partof > Z_MAXNOTICESIZE) - return (ZERR_NONE); - - /* - * If we aren't a server and we can find a notice in the queue - * with the same multiuid field, insert the current fragment as - * appropriate. - */ - switch (notice.z_kind) { - case SERVACK: - case SERVNAK: - /* The SERVACK and SERVNAK replies shouldn't be reassembled - (they have no parts). Instead, we should hold on to the reply - ONLY if it's the first part of a fragmented message, i.e. - multi_uid == uid. This allows programs to wait for the uid - of the first packet, and get a response when that notice - arrives. Acknowledgements of the other fragments are discarded - (XXX we assume here that they all carry the same information - regarding failure/success) - */ - if (!__Zephyr_server && - !ZCompareUID(¬ice.z_multiuid, ¬ice.z_uid)) - /* they're not the same... throw away this packet. */ - return(ZERR_NONE); - /* fall thru & process it */ - default: - /* for HMACK types, we assume no packet loss (local loopback - connections). The other types can be fragmented and MUST - run through this code. */ - if (!__Zephyr_server && (qptr = Z_SearchQueue(¬ice.z_multiuid, - notice.z_kind))) { - /* - * If this is the first fragment, and we haven't already - * gotten a first fragment, grab the header from it. - */ - if (part == 0 && !qptr->header) { - qptr->header_len = packet_len-notice.z_message_len; - qptr->header = (char *) malloc((unsigned) qptr->header_len); - if (!qptr->header) - return (ENOMEM); - (void) memcpy(qptr->header, packet, qptr->header_len); - } - return (Z_AddNoticeToEntry(qptr, ¬ice, part)); - } - } - - /* - * We'll have to create a new entry...make sure the queue isn't - * going to get too big. - */ - if (__Q_Size+(__Zephyr_server ? notice.z_message_len : partof) > Z_MAXQUEUESIZE) - return (ZERR_NONE); - - /* - * This is a notice we haven't heard of, so create a new queue - * entry for it and zero it out. - */ - qptr = (struct _Z_InputQ *)malloc(sizeof(struct _Z_InputQ)); - if (!qptr) - return (ENOMEM); - (void) memset((char *)qptr, 0, sizeof(struct _Z_InputQ)); - - /* Insert the entry at the end of the queue */ - qptr->next = NULL; - qptr->prev = __Q_Tail; - if (__Q_Tail) - __Q_Tail->next = qptr; - __Q_Tail = qptr; - - if (!__Q_Head) - __Q_Head = qptr; - - - /* Copy the from field, multiuid, kind, and checked authentication. */ - qptr->from = from; - qptr->uid = notice.z_multiuid; - qptr->kind = notice.z_kind; - qptr->auth = notice.z_checked_auth; - - /* - * If this is the first part of the notice, we take the header - * from it. We only take it if this is the first fragment so that - * the Unique ID's will be predictable. - * - * If a Zephyr Server, we always take the header. - */ - if (__Zephyr_server || part == 0) { - qptr->header_len = packet_len-notice.z_message_len; - qptr->header = (char *) malloc((unsigned) qptr->header_len); - if (!qptr->header) - return ENOMEM; - (void) memcpy(qptr->header, packet, qptr->header_len); - } - - /* - * If this is not a fragmented notice, then don't bother with a - * hole list. - * If we are a Zephyr server, all notices are treated as complete. - */ - if (__Zephyr_server || (part == 0 && notice.z_message_len == partof)) { - __Q_CompleteLength++; - qptr->holelist = (struct _Z_Hole *) 0; - qptr->complete = 1; - /* allocate a msg buf for this piece */ - if (notice.z_message_len == 0) - qptr->msg = 0; - else if (!(qptr->msg = (char *) malloc((unsigned) notice.z_message_len))) - return(ENOMEM); - else - (void) memcpy(qptr->msg, notice.z_message, notice.z_message_len); - qptr->msg_len = notice.z_message_len; - __Q_Size += notice.z_message_len; - qptr->packet_len = qptr->header_len+qptr->msg_len; - if (!(qptr->packet = (char *) malloc((unsigned) qptr->packet_len))) - return (ENOMEM); - (void) memcpy(qptr->packet, qptr->header, qptr->header_len); - if(qptr->msg) - (void) memcpy(qptr->packet+qptr->header_len, qptr->msg, - qptr->msg_len); - return (ZERR_NONE); - } - - /* - * We know how long the message is going to be (this is better - * than IP fragmentation...), so go ahead and allocate it all. - */ - if (!(qptr->msg = (char *) malloc((unsigned) partof)) && partof) - return (ENOMEM); - qptr->msg_len = partof; - __Q_Size += partof; - - /* - * Well, it's a fragmented notice...allocate a hole list and - * initialize it to the full packet size. Then insert the - * current fragment. - */ - if (!(qptr->holelist = (struct _Z_Hole *) - malloc(sizeof(struct _Z_Hole)))) - return (ENOMEM); - qptr->holelist->next = (struct _Z_Hole *) 0; - qptr->holelist->first = 0; - qptr->holelist->last = partof-1; - return (Z_AddNoticeToEntry(qptr, ¬ice, part)); -} - - -/* Fragment management routines - compliments, more or less, of RFC815 */ - -Code_t -Z_AddNoticeToEntry(struct _Z_InputQ *qptr, ZNotice_t *notice, int part) -{ - int last, oldfirst, oldlast; - struct _Z_Hole *hole, *lasthole; - struct timeval tv; - - /* Incorporate this notice's checked authentication. */ - if (notice->z_checked_auth == ZAUTH_FAILED) - qptr->auth = ZAUTH_FAILED; - else if (notice->z_checked_auth == ZAUTH_NO && qptr->auth != ZAUTH_FAILED) - qptr->auth = ZAUTH_NO; - - (void) gettimeofday(&tv, (struct timezone *)0); - qptr->timep = tv.tv_sec; - - last = part+notice->z_message_len-1; - - hole = qptr->holelist; - lasthole = (struct _Z_Hole *) 0; - - /* copy in the message body */ - (void) memcpy(qptr->msg+part, notice->z_message, notice->z_message_len); - - /* Search for a hole that overlaps with the current fragment */ - while (hole) { - if (part <= hole->last && last >= hole->first) - break; - lasthole = hole; - hole = hole->next; - } - - /* If we found one, delete it and reconstruct a new hole */ - if (hole) { - oldfirst = hole->first; - oldlast = hole->last; - if (lasthole) - lasthole->next = hole->next; - else - qptr->holelist = hole->next; - free((char *)hole); - /* - * Now create a new hole that is the original hole without the - * current fragment. - */ - if (part > oldfirst) { - /* Search for the end of the hole list */ - hole = qptr->holelist; - lasthole = (struct _Z_Hole *) 0; - while (hole) { - lasthole = hole; - hole = hole->next; - } - if (lasthole) { - struct _Z_InputQ *inputq = malloc(sizeof(struct _Z_InputQ)); - if (!inputq) - return (ENOMEM); - lasthole->next = (struct _Z_Hole *)inputq; - hole = lasthole->next; - } - else { - struct _Z_InputQ *inputq = malloc(sizeof(struct _Z_InputQ)); - if (!inputq) - return (ENOMEM); - qptr->holelist = (struct _Z_Hole *)inputq; - hole = qptr->holelist; - } - hole->next = NULL; - hole->first = oldfirst; - hole->last = part-1; - } - if (last < oldlast) { - /* Search for the end of the hole list */ - hole = qptr->holelist; - lasthole = (struct _Z_Hole *) 0; - while (hole) { - lasthole = hole; - hole = hole->next; - } - if (lasthole) { - struct _Z_InputQ *inputq = malloc(sizeof(struct _Z_InputQ)); - if (!inputq) - return (ENOMEM); - lasthole->next = (struct _Z_Hole *)inputq; - hole = lasthole->next; - } - else { - struct _Z_InputQ *inputq = malloc(sizeof(struct _Z_InputQ)); - if (!inputq) - return (ENOMEM); - qptr->holelist = (struct _Z_Hole *)inputq; - hole = qptr->holelist; - } - hole->next = (struct _Z_Hole *) 0; - hole->first = last+1; - hole->last = oldlast; - } - } - - if (!qptr->holelist) { - if (!qptr->complete) - __Q_CompleteLength++; - qptr->complete = 1; - qptr->timep = 0; /* don't time out anymore */ - qptr->packet_len = qptr->header_len+qptr->msg_len; - if (!(qptr->packet = (char *) malloc((unsigned) qptr->packet_len))) - return (ENOMEM); - (void) memcpy(qptr->packet, qptr->header, qptr->header_len); - (void) memcpy(qptr->packet+qptr->header_len, qptr->msg, - qptr->msg_len); - } - - return (ZERR_NONE); -} - -Code_t -Z_FormatHeader(ZNotice_t *notice, char *buffer, int buffer_len, int *len, - Z_AuthProc cert_routine) -{ - Code_t retval; - static char version[BUFSIZ]; /* default init should be all \0 */ - struct sockaddr_in name; - socklen_t namelen = sizeof(name); - - if (!notice->z_sender) - notice->z_sender = ZGetSender(); - - if (notice->z_port == 0) { - if (ZGetFD() < 0) { - retval = ZOpenPort((unsigned short *)0); - if (retval != ZERR_NONE) - return (retval); - } - retval = getsockname(ZGetFD(), (struct sockaddr *) &name, &namelen); - if (retval != 0) - return (retval); - notice->z_port = name.sin_port; - } - - notice->z_multinotice = ""; - - (void) gettimeofday(¬ice->z_uid.tv, (struct timezone *)0); - notice->z_uid.tv.tv_sec = htonl((unsigned long) notice->z_uid.tv.tv_sec); - notice->z_uid.tv.tv_usec = htonl((unsigned long) notice->z_uid.tv.tv_usec); - - (void) memcpy(¬ice->z_uid.zuid_addr, &__My_addr, sizeof(__My_addr)); - - notice->z_multiuid = notice->z_uid; - - if (!version[0]) - (void) sprintf(version, "%s%d.%d", ZVERSIONHDR, ZVERSIONMAJOR, - ZVERSIONMINOR); - notice->z_version = version; - - return Z_FormatAuthHeader(notice, buffer, buffer_len, len, cert_routine); -} - -Code_t -Z_FormatAuthHeader(ZNotice_t *notice, char *buffer, int buffer_len, int *len, - Z_AuthProc cert_routine) -{ - if (!cert_routine) { - notice->z_auth = 0; - notice->z_authent_len = 0; - notice->z_ascii_authent = ""; - notice->z_checksum = 0; - return (Z_FormatRawHeader(notice, buffer, buffer_len, - len, NULL, NULL)); - } - - return ((*cert_routine)(notice, buffer, buffer_len, len)); -} - -Code_t -Z_FormatRawHeader(ZNotice_t *notice, char *buffer, gsize buffer_len, int *len, - char **cstart, char **cend) -{ - char newrecip[BUFSIZ]; - char *ptr, *end; - int i; - - if (!notice->z_class) - notice->z_class = ""; - - if (!notice->z_class_inst) - notice->z_class_inst = ""; - - if (!notice->z_opcode) - notice->z_opcode = ""; - - if (!notice->z_recipient) - notice->z_recipient = ""; - - if (!notice->z_default_format) - notice->z_default_format = ""; - - ptr = buffer; - end = buffer+buffer_len; - - if (buffer_len < strlen(notice->z_version)+1) - return (ZERR_HEADERLEN); - - g_strlcpy(ptr, notice->z_version, buffer_len); - ptr += strlen(ptr)+1; - - if (ZMakeAscii32(ptr, end-ptr, Z_NUMFIELDS + notice->z_num_other_fields) - == ZERR_FIELDLEN) - return (ZERR_HEADERLEN); - ptr += strlen(ptr)+1; - - if (ZMakeAscii32(ptr, end-ptr, notice->z_kind) == ZERR_FIELDLEN) - return (ZERR_HEADERLEN); - ptr += strlen(ptr)+1; - - if (ZMakeAscii(ptr, end-ptr, (unsigned char *)¬ice->z_uid, - sizeof(ZUnique_Id_t)) == ZERR_FIELDLEN) - return (ZERR_HEADERLEN); - ptr += strlen(ptr)+1; - - if (ZMakeAscii16(ptr, end-ptr, ntohs(notice->z_port)) == ZERR_FIELDLEN) - return (ZERR_HEADERLEN); - ptr += strlen(ptr)+1; - - if (ZMakeAscii32(ptr, end-ptr, notice->z_auth) == ZERR_FIELDLEN) - return (ZERR_HEADERLEN); - ptr += strlen(ptr)+1; - - if (ZMakeAscii32(ptr, end-ptr, notice->z_authent_len) == ZERR_FIELDLEN) - return (ZERR_HEADERLEN); - ptr += strlen(ptr)+1; - - if (Z_AddField(&ptr, notice->z_ascii_authent, end)) - return (ZERR_HEADERLEN); - if (Z_AddField(&ptr, notice->z_class, end)) - return (ZERR_HEADERLEN); - if (Z_AddField(&ptr, notice->z_class_inst, end)) - return (ZERR_HEADERLEN); - if (Z_AddField(&ptr, notice->z_opcode, end)) - return (ZERR_HEADERLEN); - if (Z_AddField(&ptr, notice->z_sender, end)) - return (ZERR_HEADERLEN); - if (strchr(notice->z_recipient, '@') || !*notice->z_recipient) { - if (Z_AddField(&ptr, notice->z_recipient, end)) - return (ZERR_HEADERLEN); - } - else { - if (strlen(notice->z_recipient) + strlen(__Zephyr_realm) + 2 > - sizeof(newrecip)) - return (ZERR_HEADERLEN); - (void) sprintf(newrecip, "%s@%s", notice->z_recipient, __Zephyr_realm); - if (Z_AddField(&ptr, newrecip, end)) - return (ZERR_HEADERLEN); - } - if (Z_AddField(&ptr, notice->z_default_format, end)) - return (ZERR_HEADERLEN); - - /* copy back the end pointer location for crypto checksum */ - if (cstart) - *cstart = ptr; - if (ZMakeAscii32(ptr, end-ptr, notice->z_checksum) == ZERR_FIELDLEN) - return (ZERR_HEADERLEN); - ptr += strlen(ptr)+1; - if (cend) - *cend = ptr; - - if (Z_AddField(&ptr, notice->z_multinotice, end)) - return (ZERR_HEADERLEN); - - if (ZMakeAscii(ptr, end-ptr, (unsigned char *)¬ice->z_multiuid, - sizeof(ZUnique_Id_t)) == ZERR_FIELDLEN) - return (ZERR_HEADERLEN); - ptr += strlen(ptr)+1; - - for (i=0;iz_num_other_fields;i++) - if (Z_AddField(&ptr, notice->z_other_fields[i], end)) - return (ZERR_HEADERLEN); - - *len = ptr-buffer; - - return (ZERR_NONE); -} - -static int -Z_AddField(char **ptr, const char *field, char *end) -{ - register int len; - - len = field ? strlen (field) + 1 : 1; - - if (*ptr+len > end) - return 1; - if (field) - strcpy(*ptr, field); - else - **ptr = '\0'; - *ptr += len; - - return 0; -} - -struct _Z_InputQ * -Z_GetFirstComplete(void) -{ - struct _Z_InputQ *qptr; - - qptr = __Q_Head; - - while (qptr) { - if (qptr->complete) - return (qptr); - qptr = qptr->next; - } - - return ((struct _Z_InputQ *)0); -} - -struct _Z_InputQ * -Z_GetNextComplete(struct _Z_InputQ *qptr) -{ - qptr = qptr->next; - while (qptr) { - if (qptr->complete) - return (qptr); - qptr = qptr->next; - } - - return ((struct _Z_InputQ *)0); -} - -void -Z_RemQueue(struct _Z_InputQ *qptr) -{ - struct _Z_Hole *hole, *nexthole; - - if (qptr->complete) - __Q_CompleteLength--; - - __Q_Size -= qptr->msg_len; - - if (qptr->header) - free(qptr->header); - if (qptr->msg) - free(qptr->msg); - if (qptr->packet) - free(qptr->packet); - - hole = qptr->holelist; - while (hole) { - nexthole = hole->next; - free((char *)hole); - hole = nexthole; - } - - if (qptr == __Q_Head && __Q_Head == __Q_Tail) { - free ((char *)qptr); - __Q_Head = (struct _Z_InputQ *)0; - __Q_Tail = (struct _Z_InputQ *)0; - return; - } - - if (qptr == __Q_Head) { - __Q_Head = qptr->next; - __Q_Head->prev = (struct _Z_InputQ *)0; - free ((char *)qptr); - return; - } - if (qptr == __Q_Tail) { - __Q_Tail = qptr->prev; - __Q_Tail->next = (struct _Z_InputQ *)0; - free ((char *)qptr); - return; - } - qptr->prev->next = qptr->next; - qptr->next->prev = qptr->prev; - free ((char *)qptr); - return; -} - -Code_t -Z_SendFragmentedNotice(ZNotice_t *notice, int len, Z_AuthProc cert_func, - Z_SendProc send_func) -{ - ZNotice_t partnotice; - ZPacket_t buffer; - char multi[64]; - int offset, hdrsize, fragsize, ret_len, message_len, waitforack; - Code_t retval; - - hdrsize = len-notice->z_message_len; - fragsize = Z_MAXPKTLEN-hdrsize-Z_FRAGFUDGE; - - offset = 0; - - waitforack = ((notice->z_kind == UNACKED || notice->z_kind == ACKED) - && !__Zephyr_server); - - partnotice = *notice; - - while (offset < notice->z_message_len || !notice->z_message_len) { - (void) sprintf(multi, "%d/%d", offset, notice->z_message_len); - partnotice.z_multinotice = multi; - if (offset > 0) { - (void) gettimeofday(&partnotice.z_uid.tv, - (struct timezone *)0); - partnotice.z_uid.tv.tv_sec = - htonl((unsigned long) partnotice.z_uid.tv.tv_sec); - partnotice.z_uid.tv.tv_usec = - htonl((unsigned long) partnotice.z_uid.tv.tv_usec); - (void) memcpy((char *)&partnotice.z_uid.zuid_addr, &__My_addr, - sizeof(__My_addr)); - } - message_len = min(notice->z_message_len-offset, fragsize); - partnotice.z_message = (char*)notice->z_message+offset; - partnotice.z_message_len = message_len; - if ((retval = Z_FormatAuthHeader(&partnotice, buffer, Z_MAXHEADERLEN, - &ret_len, cert_func)) != ZERR_NONE) { - return (retval); - } - memcpy(buffer + ret_len, partnotice.z_message, message_len); - if ((retval = (*send_func)(&partnotice, buffer, ret_len+message_len, - waitforack)) != ZERR_NONE) { - return (retval); - } - offset += fragsize; - if (!notice->z_message_len) - break; - } - - return (ZERR_NONE); -} - -/*ARGSUSED*/ -Code_t -Z_XmitFragment(ZNotice_t *notice, char *buf, int len, int wait) -{ - return(ZSendPacket(buf, len, wait)); -} - -#ifdef Z_DEBUG -/* For debugging printing */ -const char *const ZNoticeKinds[] = { - "UNSAFE", "UNACKED", "ACKED", "HMACK", "HMCTL", "SERVACK", "SERVNAK", - "CLIENTACK", "STAT" -}; -#endif - -#ifdef Z_DEBUG - -#undef Z_debug -#ifdef HAVE_STDARG_H -void Z_debug (const char *format, ...) -{ - va_list pvar; - if (!__Z_debug_print) - return; - va_start (pvar, format); - (*__Z_debug_print) (format, pvar, __Z_debug_print_closure); - va_end (pvar); -} -#else /* stdarg */ -void Z_debug (va_alist) va_dcl -{ - va_list pvar; - char *format; - if (!__Z_debug_print) - return; - va_start (pvar); - format = va_arg (pvar, char *); - (*__Z_debug_print) (format, pvar, __Z_debug_print_closure); - va_end (pvar); -} -#endif - -void -Z_debug_stderr(const char *format, va_list args, void *closure) -{ -#ifdef HAVE_VPRINTF - vfprintf (stderr, format, args); -#else - _doprnt (format, args, stderr); -#endif - putc ('\n', stderr); -} - -#undef ZGetFD -int -ZGetFD(void) -{ - return __Zephyr_fd; -} - -#undef ZQLength -int -ZQLength(void) -{ - return __Q_CompleteLength; -} - -#undef ZGetDestAddr -struct sockaddr_in -ZGetDestAddr(void) -{ - return __HM_addr; -} - -#undef ZGetRealm -Zconst char * -ZGetRealm(void) -{ - return __Zephyr_realm; -} - -#undef ZSetDebug -void -ZSetDebug(void (*proc) __P((const char *, va_list, void *)), char *arg) -{ - __Z_debug_print = proc; - __Z_debug_print_closure = arg; -} -#endif /* Z_DEBUG */ - diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/com_err.h --- a/libpurple/protocols/zephyr/com_err.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,38 +0,0 @@ -/* - * Header file for common error description library. - * - * Copyright 1988, Student Information Processing Board of the - * Massachusetts Institute of Technology. - * - * For copyright and distribution info, see the documentation supplied - * with this package. - */ - -#ifndef __COM_ERR_H -#define __COM_ERR_H - -#define COM_ERR_BUF_LEN 25 - -/* Use __STDC__ to guess whether we can use stdarg, prototypes, and const. - * This is a public header file, so autoconf can't help us here. */ -#ifdef __STDC__ -# include -# define ETP(x) x -# define ETCONST const -#else -# define ETP(x) () -# define ETCONST -#endif - -typedef void (*error_handler_t) ETP((ETCONST char *, long, ETCONST char *, - va_list)); -extern error_handler_t com_err_hook; -void com_err ETP((ETCONST char *, long, ETCONST char *, ...)); -ETCONST char *error_message ETP((long)); -ETCONST char *error_message_r ETP((long, char *)); -error_handler_t set_com_err_hook ETP((error_handler_t)); -error_handler_t reset_com_err_hook ETP((void)); - -#undef ETP - -#endif /* ! defined(__COM_ERR_H) */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/error_message.c --- a/libpurple/protocols/zephyr/error_message.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,66 +0,0 @@ -/* - * Copyright 1987 by the Student Information Processing Board - * of the Massachusetts Institute of Technology - * - * For copyright info, see "mit-sipb-copyright.h". - */ - -#include "error_table.h" -#include "com_err.h" -#include - -char *error_table_name_r __P((int, char *)); - -struct et_list * _et_list = (struct et_list *) NULL; - -const char * error_message (code) -long code; -{ - static char buf[COM_ERR_BUF_LEN]; - - return(error_message_r(code, buf)); -} - -const char * error_message_r (code, buf) -long code; -char *buf; -{ - int offset; - struct et_list *et; - int table_num; - int started = 0; - char *cp, namebuf[6]; - - offset = code & ((1<next) { - if (et->table->base == table_num) { - /* This is the right table */ - if (et->table->n_msgs <= offset) - break; - return(et->table->msgs[offset]); - } - } - - strcpy (buf, "Unknown code "); - if (table_num) { - strcat (buf, error_table_name_r (table_num, namebuf)); - strcat (buf, " "); - } - for (cp = buf; *cp; cp++) - ; - if (offset >= 100) { - *cp++ = '0' + offset / 100; - offset %= 100; - started++; - } - if (started || offset >= 10) { - *cp++ = '0' + offset / 10; - offset %= 10; - } - *cp++ = '0' + offset; - *cp = '\0'; - return(buf); -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/error_table.h --- a/libpurple/protocols/zephyr/error_table.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,25 +0,0 @@ -/* - * Copyright 1988 by the Student Information Processing Board of the - * Massachusetts Institute of Technology. - * - * For copyright info, see mit-sipb-copyright.h. - */ - -#ifndef _ET_H -struct error_table { - char const * const * msgs; - long base; - int n_msgs; -}; -struct et_list { - struct et_list *next; - const struct error_table *table; -}; -extern struct et_list * _et_list; - -#define ERRCODE_RANGE 8 /* # of bits to shift table number */ -#define BITS_PER_CHAR 6 /* # bits to shift per character in name */ - -const char *error_table_name(void); -#define _ET_H -#endif diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/et_name.c --- a/libpurple/protocols/zephyr/et_name.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -/* - * Copyright 1987 by MIT Student Information Processing Board - * - * For copyright info, see mit-sipb-copyright.h. - */ - -#include - - -#define ERRCODE_RANGE 8 /* # of bits to shift table number */ -#define BITS_PER_CHAR 6 /* # bits to shift per character in name */ - - -static const char char_set[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"; - -/* Prototypes for -Wmissing-prototypes */ -const char * error_table_name(int num); -const char * error_table_name_r(int num, char *buf); - -const char * error_table_name_r(int num, char *buf) -{ - int ch; - int i; - char *p; - - /* num = aa aaa abb bbb bcc ccc cdd ddd d?? ??? ??? */ - p = buf; - num >>= ERRCODE_RANGE; - /* num = ?? ??? ??? aaa aaa bbb bbb ccc ccc ddd ddd */ - num &= 077777777; - /* num = 00 000 000 aaa aaa bbb bbb ccc ccc ddd ddd */ - for (i = 4; i >= 0; i--) { - ch = (num >> BITS_PER_CHAR * i) & ((1 << BITS_PER_CHAR) - 1); - if (ch != 0) - *p++ = char_set[ch-1]; - } - *p = '\0'; - return(buf); -} - -const char * error_table_name(int num) -{ - static char buf[6]; - - return(error_table_name_r(num, buf)); -} - diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/init_et.c --- a/libpurple/protocols/zephyr/init_et.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,11 +0,0 @@ -/* - * Copyright 1986, 1987, 1988 by MIT Information Systems and - * the MIT Student Information Processing Board. - * - * For copyright info, see mit-sipb-copyright.h. - */ - -#include - -extern struct et_list * _et_list; - diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/internal.h --- a/libpurple/protocols/zephyr/internal.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,122 +0,0 @@ -#ifndef __INTERNAL_H__ -#define __INTERNAL_H__ - -#include - -#ifdef LIBZEPHYR_EXT -#include -#else -#include -#endif - -#ifndef WIN32 -#include -#endif - - - -#ifdef WIN32 - -#ifndef MAXHOSTNAMELEN -#define MAXHOSTNAMELEN 512 -#endif - -#define ETIMEDOUT WSAETIMEDOUT -#define EADDRINUSE WSAEADDRINUSE -#else /* !WIN32 */ - -#ifndef MAXHOSTNAMELEN -#define MAXHOSTNAMELEN 4096 -#endif - -#endif - -#ifdef ZEPHYR_USES_HESIOD -#include -#endif - -#ifndef ZEPHYR_USES_KERBEROS -#define REALM_SZ MAXHOSTNAMELEN -#define INST_SZ 0 /* no instances w/o Kerberos */ -#define ANAME_SZ 9 /* size of a username + null */ -#define CLOCK_SKEW 300 /* max time to cache packet ids */ -#endif - -#define SERVER_SVC_FALLBACK htons((unsigned short) 2103) -#define HM_SVC_FALLBACK htons((unsigned short) 2104) -#define HM_SRV_SVC_FALLBACK htons((unsigned short) 2105) - -#define ZAUTH_CKSUM_FAILED (-2) /* Used only by server. */ -#define ZAUTH_UNSET (-3) /* Internal to client library. */ -#define Z_MAXFRAGS 500 /* Max number of packet fragments */ -#define Z_MAXNOTICESIZE 400000 /* Max size of incoming notice */ -#define Z_MAXQUEUESIZE 1500000 /* Max size of input queue notices */ -#define Z_FRAGFUDGE 13 /* Room to for multinotice field */ -#define Z_NOTICETIMELIMIT 30 /* Time to wait for fragments */ -#define Z_INITFILTERSIZE 30 /* Starting size of uid filter */ - -struct _Z_Hole { - struct _Z_Hole *next; - int first; - int last; -}; - -struct _Z_InputQ { - struct _Z_InputQ *next; - struct _Z_InputQ *prev; - ZNotice_Kind_t kind; - unsigned ZEPHYR_INT32 timep; - int packet_len; - char *packet; - int complete; - struct sockaddr_in from; - struct _Z_Hole *holelist; - ZUnique_Id_t uid; - int auth; - int header_len; - char *header; - int msg_len; - char *msg; -}; - -extern struct _Z_InputQ *__Q_Head, *__Q_Tail; - -extern int __Zephyr_open; /* 0 if FD opened, 1 otherwise */ -extern int __HM_set; /* 0 if dest addr set, 1 otherwise */ -extern int __Zephyr_server; /* 0 if normal client, 1 if server or zhm */ - -extern ZLocations_t *__locate_list; -extern int __locate_num; -extern int __locate_next; - -extern ZSubscription_t *__subscriptions_list; -extern int __subscriptions_num; -extern int __subscriptions_next; - -extern int __Zephyr_port; /* Port number */ -extern struct in_addr __My_addr; - -typedef Code_t (*Z_SendProc) __P((ZNotice_t *, char *, int, int)); - -struct _Z_InputQ *Z_GetFirstComplete __P((void)); -struct _Z_InputQ *Z_GetNextComplete __P((struct _Z_InputQ *)); -Code_t Z_XmitFragment __P((ZNotice_t*, char *,int,int)); -void Z_RemQueue __P((struct _Z_InputQ *)); -Code_t Z_AddNoticeToEntry __P((struct _Z_InputQ*, ZNotice_t*, int)); -Code_t Z_FormatAuthHeader __P((ZNotice_t *, char *, int, int *, Z_AuthProc)); -Code_t Z_FormatHeader __P((ZNotice_t *, char *, int, int *, Z_AuthProc)); -Code_t Z_FormatRawHeader __P((ZNotice_t *, char*, gsize, - int*, char **, char **)); -Code_t Z_ReadEnqueue __P((void)); -Code_t Z_ReadWait __P((void)); -Code_t Z_SendLocation __P((char*, char*, Z_AuthProc, char*)); -Code_t Z_SendFragmentedNotice __P((ZNotice_t *notice, int len, - Z_AuthProc cert_func, - Z_SendProc send_func)); -Code_t Z_WaitForComplete __P((void)); -Code_t Z_WaitForNotice __P((ZNotice_t *notice, - int (*pred) __P((ZNotice_t *, void *)), void *arg, - int timeout)); - -#endif /* __INTERNAL_H__ */ - diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/mit-copyright.h --- a/libpurple/protocols/zephyr/mit-copyright.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,24 +0,0 @@ -/* - -Copyright 1987,1988 by the Massachusetts Institute of Technology - -All rights reserved. - -Permission to use, copy, modify, and distribute this software and its -documentation for any purpose and without fee is hereby granted, -provided that the above copyright notice appear in all copies and that -both that copyright notice and this permission notice appear in -supporting documentation, and that the name of the Massachusetts -Institute of Technology (M.I.T.) not be used in advertising or publicity -pertaining to distribution of the software without specific, written -prior permission. - -M.I.T. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING -ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL -M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR -ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, -ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS -SOFTWARE. - -*/ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/mit-sipb-copyright.h --- a/libpurple/protocols/zephyr/mit-sipb-copyright.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,19 +0,0 @@ -/* - -Copyright 1987, 1989 by the Student Information Processing Board - of the Massachusetts Institute of Technology - -Permission to use, copy, modify, and distribute this software -and its documentation for any purpose and without fee is -hereby granted, provided that the above copyright notice -appear in all copies and that both that copyright notice and -this permission notice appear in supporting documentation, -and that the names of M.I.T. and the M.I.T. S.I.P.B. not be -used in advertising or publicity pertaining to distribution -of the software without specific, written prior permission. -M.I.T. and the M.I.T. S.I.P.B. make no representations about -the suitability of this software for any purpose. It is -provided "as is" without express or implied warranty. - -*/ - diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/sysdep.h --- a/libpurple/protocols/zephyr/sysdep.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,202 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains system-dependent header code. - * - * Created by: Greg Hudson - * - * Copyright (c) 1988,1991 by the Massachusetts Institute of Technology. - * For copying and distribution information, see the file - * "mit-copyright.h". - */ - -#ifndef __SYSDEP_H__ -#define __SYSDEP_H__ - -#include -#include -#include -#include -#include -#include -#ifndef WIN32 -#include -#endif - -#include -#include -#include -#include - -#if defined(STDC_HEADERS) || defined(HAVE_STDLIB_H) -# include -#else -# ifdef HAVE_MALLOC_H -# include -# else -char *malloc(), *realloc(void); -# endif -char *getenv(), *strerror(), *ctime(), *strcpy(void); -time_t time(void); -ZEPHYR_INT32 random(void); -#endif - -#ifndef HAVE_RANDOM -#ifdef HAVE_LRAND48 -#define random lrand48 -#define srandom srand48 -#else -#define random rand -#define srandom srand -#endif -#endif - -#ifndef HAVE_STRERROR -extern char *sys_errlist[]; -# define strerror(x) (sys_errlist[(x)]) -#endif - -/* Strings. */ -#if defined(STDC_HEADERS) || defined(HAVE_STRING_H) -# include -#else -# ifndef HAVE_STRCHR -# define strchr index -# define strrchr rindex -# endif -char *strchr(), *strrchr(void); -# ifndef HAVE_MEMCPY -# define memcpy(d, s, n) bcopy ((s), (d), (n)) -# define memcmp bcmp -# endif -# ifndef HAVE_MEMMOVE -# define memmove(d, s, n) bcopy ((s), (d), (n)) -# endif -#endif - -/* Exit status handling and wait(). */ -#ifdef HAVE_SYS_WAIT_H -# include -#endif -#ifndef WEXITSTATUS -# define WEXITSTATUS(stat_val) ((unsigned)(stat_val) >> 8) -#endif -#ifndef WIFEXITED -# define WIFEXITED(stat_val) (((stat_val) & 255) == 0) -#endif - -#ifdef HAVE_SYS_CDEFS_H -#include -#endif - -/* Because we have public header files (and our prototypes need to agree with - * those header files, use __STDC__ to guess whether the compiler can handle - * stdarg, const, and prototypes. */ -#ifdef __STDC__ -# include -# define VA_START(ap, last) va_start(ap, last) -# ifndef __P -# define __P(x) x -# endif -#else -# include -# define VA_START(ap, last) va_start(ap) -# define const -# ifndef __P -# define __P(x) () -# endif -#endif - -/* openlog(). */ -#ifdef LOG_AUTH -/* A decent syslog */ -#define OPENLOG(str, opts, facility) openlog(str, opts, facility) -#else -/* Probably a 4.2-type syslog */ -#define OPENLOG(str, opts, facility) openlog(str, opts) -#endif - -#ifdef HAVE_FCNTL_H -# include -#endif - -#ifdef HAVE_PATHS_H -# include -# define TEMP_DIRECTORY _PATH_VARTMP -#else -# define TEMP_DIRECTORY FOUND_TMP -#endif - -#ifdef HAVE_UNISTD_H -# include -#else -# ifdef HAVE_SYS_FILE_H -# include -# endif -uid_t getuid(void); -char *ttyname(void); -#ifdef HAVE_GETHOSTID -ZEPHYR_INT32 gethostid(void); -#endif -#endif - -#ifndef STDIN_FILENO -#define STDIN_FILENO 0 -#define STDOUT_FILENO 1 -#define STDERR_FILENO 2 -#endif - -#ifdef HAVE_TERMIOS_H -# include -#else -# ifdef HAVE_SYS_FILIO_H -# include -# else -# ifdef HAVE_SGTTY_H -# include -# endif -# ifdef HAVE_SYS_IOCTL_H -# include -# endif -# endif -#endif - -/* Kerberos compatibility. */ -#ifdef ZEPHYR_USES_KERBEROS -# include -#ifdef WIN32 - -#else -# include -#endif /* WIN32 */ -# include -#ifndef WIN32 -# ifndef HAVE_KRB_GET_ERR_TEXT -# define krb_get_err_text(n) krb_err_txt[n] -# endif -#endif /* WIN32 */ -# ifndef HAVE_KRB_LOG -# define krb_log log -# endif -#endif /* ZEPHYR_USES_KERBEROS */ - -#ifdef HAVE_SYS_UIO_H -# include -#endif - -#ifdef HAVE_SYS_UTSNAME_H -# include -#endif - -#ifdef HAVE_SYS_SELECT_H -# include -#endif - -#ifdef HAVE_SYS_MSGBUF_H -#include -#endif - -#ifndef MSG_BSIZE -#define MSG_BSIZE BUFSIZ -#endif - -#endif /* __SYSDEP_H__ */ - diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/zephyr.c --- a/libpurple/protocols/zephyr/zephyr.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3006 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * purple - * - * Copyright (C) 1998-2001, Mark Spencer - * Some code borrowed from GtkZephyr, by - * Jag/Sean Dilda / - * http://gtkzephyr.linuxpower.org/ - * - * Some code borrowed from kzephyr, by - * Chris Colohan - * - * 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 "libpurple/internal.h" - -#include "accountopt.h" -#include "debug.h" -#include "notify.h" -#include "prpl.h" -#include "server.h" -#include "util.h" -#include "cmds.h" -#include "privacy.h" -#include "version.h" - -#include "internal.h" - -#include - -#define ZEPHYR_FALLBACK_CHARSET "ISO-8859-1" - -/* these are deliberately high, since most people don't send multiple "PING"s */ -#define ZEPHYR_TYPING_SEND_TIMEOUT 15 -#define ZEPHYR_TYPING_RECV_TIMEOUT 10 -#define ZEPHYR_FD_READ 0 -#define ZEPHYR_FD_WRITE 1 - -extern Code_t ZGetLocations(ZLocations_t *, int *); -extern Code_t ZSetLocation(char *); -extern Code_t ZUnsetLocation(void); -extern Code_t ZGetSubscriptions(ZSubscription_t *, int*); -extern char __Zephyr_realm[]; -typedef struct _zframe zframe; -typedef struct _zephyr_triple zephyr_triple; -typedef struct _zephyr_account zephyr_account; -typedef struct _parse_tree parse_tree; - -typedef enum { - PURPLE_ZEPHYR_NONE, /* Non-kerberized ZEPH0.2 */ - PURPLE_ZEPHYR_KRB4, /* ZEPH0.2 w/ KRB4 support */ - PURPLE_ZEPHYR_TZC, /* tzc executable proxy */ - PURPLE_ZEPHYR_INTERGALACTIC_KRB4 /* Kerberized ZEPH0.3 */ -} zephyr_connection_type; - -struct _zephyr_account { - PurpleAccount* account; - char *username; - char *realm; - char *encoding; - char* galaxy; /* not yet useful */ - char* krbtkfile; /* not yet useful */ - guint32 nottimer; - guint32 loctimer; - GList *pending_zloc_names; - GSList *subscrips; - int last_id; - unsigned short port; - char ourhost[HOST_NAME_MAX + 1]; - char ourhostcanon[HOST_NAME_MAX + 1]; - zephyr_connection_type connection_type; - int totzc[2]; - int fromtzc[2]; - char *exposure; - pid_t tzc_pid; - gchar *away; -}; - -#define MAXCHILDREN 20 - -struct _parse_tree { - gchar* contents; - parse_tree *children[MAXCHILDREN]; - int num_children; -}; - -parse_tree null_parse_tree = { - "", - {NULL}, - 0, -}; - -#define use_none(zephyr) ((zephyr->connection_type == PURPLE_ZEPHYR_NONE)?1:0) -#define use_krb4(zephyr) ((zephyr->connection_type == PURPLE_ZEPHYR_KRB4)?1:0) -#define use_tzc(zephyr) ((zephyr->connection_type == PURPLE_ZEPHYR_TZC)?1:0) - -#define use_zeph02(zephyr) ( (zephyr->connection_type == PURPLE_ZEPHYR_NONE)?1: ((zephyr->connection_type == PURPLE_ZEPHYR_KRB4)?1:0)) - -/* struct I need for zephyr_to_html */ -struct _zframe { - /* true for everything but @color, since inside the parens of that one is - * the color. */ - gboolean has_closer; - /* @i, @b, etc. */ - const char *env; - /* }=1, ]=2, )=4, >=8 */ - int closer_mask; - /* }, ], ), > */ - char *closer; - /* , , , etc. */ - const char *closing; - /* text including the opening html thingie. */ - GString *text; - /* href for links */ - gboolean is_href; - GString *href; - struct _zframe *enclosing; -}; - -struct _zephyr_triple { - char *class; - char *instance; - char *recipient; - char *name; - gboolean open; - int id; -}; - -#define z_call(func) if (func != ZERR_NONE)\ - return; -#define z_call_r(func) if (func != ZERR_NONE)\ - return TRUE; - -#define z_call_s(func, err) if (func != ZERR_NONE) {\ - purple_connection_error(gc, err);\ - return;\ - } - -#ifdef WIN32 -extern const char *username; -#endif - -static Code_t zephyr_subscribe_to(zephyr_account* zephyr, char* class, char *instance, char *recipient, char* galaxy) { - size_t result; - Code_t ret_val = -1; - - if (use_tzc(zephyr)) { - /* ((tzcfodder . subscribe) ("class" "instance" "recipient")) */ - gchar *zsubstr = g_strdup_printf("((tzcfodder . subscribe) (\"%s\" \"%s\" \"%s\"))\n",class,instance,recipient); - size_t len = strlen(zsubstr); - result = write(zephyr->totzc[ZEPHYR_FD_WRITE],zsubstr,len); - if (result != len) { - purple_debug_error("zephyr", "Unable to write a message: %s\n", g_strerror(errno)); - } else { - ret_val = ZERR_NONE; - } - g_free(zsubstr); - } - else { - if (use_zeph02(zephyr)) { - ZSubscription_t sub; - sub.zsub_class = class; - sub.zsub_classinst = instance; - sub.zsub_recipient = recipient; - ret_val = ZSubscribeTo(&sub,1,0); - } - } - return ret_val; -} - -char *local_zephyr_normalize(zephyr_account* zephyr,const char *); -static void zephyr_chat_set_topic(PurpleConnection * gc, int id, const char *topic); -char* zephyr_tzc_deescape_str(const char *message); - -static char *zephyr_strip_local_realm(zephyr_account* zephyr,const char* user){ - /* - Takes in a username of the form username or username@realm - and returns: - username, if there is no realm, or the realm is the local realm - or: - username@realm if there is a realm and it is foreign - */ - char *tmp = g_strdup(user); - char *at = strchr(tmp,'@'); - if (at && !g_ascii_strcasecmp(at+1,zephyr->realm)) { - /* We're passed in a username of the form user@users-realm */ - char* tmp2; - *at = '\0'; - tmp2 = g_strdup(tmp); - g_free(tmp); - return tmp2; - } - else { - /* We're passed in a username of the form user or user@foreign-realm */ - return tmp; - } -} - -/* this is so bad, and if Zephyr weren't so fucked up to begin with I - * wouldn't do this. but it is so i will. */ - -/* just for debugging */ -static void handle_unknown(ZNotice_t *notice) -{ - purple_debug_error("zephyr","z_packet: %s\n", notice->z_packet); - purple_debug_error("zephyr","z_version: %s\n", notice->z_version); - purple_debug_error("zephyr","z_kind: %d\n", (int)(notice->z_kind)); - purple_debug_error("zephyr","z_class: %s\n", notice->z_class); - purple_debug_error("zephyr","z_class_inst: %s\n", notice->z_class_inst); - purple_debug_error("zephyr","z_opcode: %s\n", notice->z_opcode); - purple_debug_error("zephyr","z_sender: %s\n", notice->z_sender); - purple_debug_error("zephyr","z_recipient: %s\n", notice->z_recipient); - purple_debug_error("zephyr","z_message: %s\n", notice->z_message); - purple_debug_error("zephyr","z_message_len: %d\n", notice->z_message_len); -} - - -static zephyr_triple *new_triple(zephyr_account *zephyr,const char *c, const char *i, const char *r) -{ - zephyr_triple *zt; - - zt = g_new0(zephyr_triple, 1); - zt->class = g_strdup(c); - zt->instance = g_strdup(i); - zt->recipient = g_strdup(r); - zt->name = g_strdup_printf("%s,%s,%s", c, i?i:"", r?r:""); - zt->id = ++(zephyr->last_id); - zt->open = FALSE; - return zt; -} - -static void free_triple(zephyr_triple * zt) -{ - g_free(zt->class); - g_free(zt->instance); - g_free(zt->recipient); - g_free(zt->name); - g_free(zt); -} - -/* returns true if zt1 is a subset of zt2. This function is used to - determine whether a zephyr sent to zt1 should be placed in the chat - with triple zt2 - - zt1 is a subset of zt2 - iff. the classnames are identical ignoring case - AND. the instance names are identical (ignoring case), or zt2->instance is *. - AND. the recipient names are identical -*/ - -static gboolean triple_subset(zephyr_triple * zt1, zephyr_triple * zt2) -{ - - if (!zt2) { - purple_debug_error("zephyr","zt2 doesn't exist\n"); - return FALSE; - } - if (!zt1) { - purple_debug_error("zephyr","zt1 doesn't exist\n"); - return FALSE; - } - if (!(zt1->class)) { - purple_debug_error("zephyr","zt1c doesn't exist\n"); - return FALSE; - } - if (!(zt1->instance)) { - purple_debug_error("zephyr","zt1i doesn't exist\n"); - return FALSE; - } - if (!(zt1->recipient)) { - purple_debug_error("zephyr","zt1r doesn't exist\n"); - return FALSE; - } - if (!(zt2->class)) { - purple_debug_error("zephyr","zt2c doesn't exist\n"); - return FALSE; - } - if (!(zt2->recipient)) { - purple_debug_error("zephyr","zt2r doesn't exist\n"); - return FALSE; - } - if (!(zt2->instance)) { - purple_debug_error("zephyr","zt2i doesn't exist\n"); - return FALSE; - } - - if (g_ascii_strcasecmp(zt2->class, zt1->class)) { - return FALSE; - } - if (g_ascii_strcasecmp(zt2->instance, zt1->instance) && g_ascii_strcasecmp(zt2->instance, "*")) { - return FALSE; - } - if (g_ascii_strcasecmp(zt2->recipient, zt1->recipient)) { - return FALSE; - } - purple_debug_info("zephyr","<%s,%s,%s> is in <%s,%s,%s>\n",zt1->class,zt1->instance,zt1->recipient,zt2->class,zt2->instance,zt2->recipient); - return TRUE; -} - -static zephyr_triple *find_sub_by_triple(zephyr_account *zephyr,zephyr_triple * zt) -{ - zephyr_triple *curr_t; - GSList *curr = zephyr->subscrips; - - while (curr) { - curr_t = curr->data; - if (triple_subset(zt, curr_t)) - return curr_t; - curr = curr->next; - } - return NULL; -} - -static zephyr_triple *find_sub_by_id(zephyr_account *zephyr,int id) -{ - zephyr_triple *zt; - GSList *curr = zephyr->subscrips; - - while (curr) { - zt = curr->data; - if (zt->id == id) - return zt; - curr = curr->next; - } - return NULL; -} - -/* - Converts strings to utf-8 if necessary using user specified encoding -*/ - -static gchar *zephyr_recv_convert(PurpleConnection *gc, gchar *string) -{ - gchar *utf8; - GError *err = NULL; - zephyr_account *zephyr = gc->proto_data; - if (g_utf8_validate(string, -1, NULL)) { - return g_strdup(string); - } else { - utf8 = g_convert(string, -1, "UTF-8", zephyr->encoding, NULL, NULL, &err); - if (err) { - purple_debug_error("zephyr", "recv conversion error: %s\n", err->message); - utf8 = g_strdup(_("(There was an error converting this message. Check the 'Encoding' option in the Account Editor)")); - g_error_free(err); - } - - return utf8; - } -} - -/* This parses HTML formatting (put out by one of the gtkimhtml widgets - And converts it to zephyr formatting. - It currently deals properly with ,
, , , , - It ignores - It does - @small - 3 or 4 @medium() - 5,6, or 7 @large() - " or just "description" as appropriate -*/ - -static char *html_to_zephyr(const char *message) -{ - zframe *frames, *new_f; - char *ret; - - if (*message == '\0') - return g_strdup(""); - - frames = g_new(zframe, 1); - frames->text = g_string_new(""); - frames->href = NULL; - frames->is_href = FALSE; - frames->enclosing = NULL; - frames->closing = NULL; - frames->env = ""; - frames->has_closer = FALSE; - frames->closer_mask = 15; - - purple_debug_info("zephyr","html received %s\n",message); - while (*message) { - if (frames->closing && !g_ascii_strncasecmp(message, frames->closing, strlen(frames->closing))) { - zframe *popped; - message += strlen(frames->closing); - popped = frames; - frames = frames->enclosing; - if (popped->is_href) { - frames->href = popped->text; - } else { - g_string_append(frames->text, popped->env); - if (popped->has_closer) { - g_string_append_c(frames->text, - (popped->closer_mask & 1) ? '{' : - (popped->closer_mask & 2) ? '[' : - (popped->closer_mask & 4) ? '(' : - '<'); - } - g_string_append(frames->text, popped->text->str); - if (popped->href) - { - int text_len = strlen(popped->text->str), href_len = strlen(popped->href->str); - if (!((text_len == href_len && !strncmp(popped->href->str, popped->text->str, text_len)) || - (7 + text_len == href_len && !strncmp(popped->href->str, "http://", 7) && - !strncmp(popped->href->str + 7, popped->text->str, text_len)) || - (7 + text_len == href_len && !strncmp(popped->href->str, "mailto:", 7) && - !strncmp(popped->href->str + 7, popped->text->str, text_len)))) { - g_string_append(frames->text, " <"); - g_string_append(frames->text, popped->href->str); - if (popped->closer_mask & ~8) { - g_string_append_c(frames->text, '>'); - popped->closer_mask &= ~8; - } else { - g_string_append(frames->text, "@{>}"); - } - } - g_string_free(popped->href, TRUE); - } - if (popped->has_closer) { - g_string_append_c(frames->text, - (popped->closer_mask & 1) ? '}' : - (popped->closer_mask & 2) ? ']' : - (popped->closer_mask & 4) ? ')' : - '>'); - } - if (!popped->has_closer) - frames->closer_mask = popped->closer_mask; - g_string_free(popped->text, TRUE); - } - g_free(popped); - } else if (*message == '<') { - if (!g_ascii_strncasecmp(message + 1, "i>", 2)) { - new_f = g_new(zframe, 1); - new_f->enclosing = frames; - new_f->text = g_string_new(""); - new_f->href = NULL; - new_f->is_href = FALSE; - new_f->closing = ""; - new_f->env = "@i"; - new_f->has_closer = TRUE; - new_f->closer_mask = 15; - frames = new_f; - message += 3; - } else if (!g_ascii_strncasecmp(message + 1, "b>", 2)) { - new_f = g_new(zframe, 1); - new_f->enclosing = frames; - new_f->text = g_string_new(""); - new_f->href = NULL; - new_f->is_href = FALSE; - new_f->closing = "
"; - new_f->env = "@b"; - new_f->has_closer = TRUE; - new_f->closer_mask = 15; - frames = new_f; - message += 3; - } else if (!g_ascii_strncasecmp(message + 1, "br>", 3)) { - g_string_append_c(frames->text, '\n'); - message += 4; - } else if (!g_ascii_strncasecmp(message + 1, "a href=\"", 8)) { - message += 9; - new_f = g_new(zframe, 1); - new_f->enclosing = frames; - new_f->text = g_string_new(""); - new_f->href = NULL; - new_f->is_href = FALSE; - new_f->closing = ""; - new_f->env = ""; - new_f->has_closer = FALSE; - new_f->closer_mask = frames->closer_mask; - frames = new_f; - new_f = g_new(zframe, 1); - new_f->enclosing = frames; - new_f->text = g_string_new(""); - new_f->href = NULL; - new_f->is_href = TRUE; - new_f->closing = "\">"; - new_f->has_closer = FALSE; - new_f->closer_mask = frames->closer_mask; - frames = new_f; - } else if (!g_ascii_strncasecmp(message + 1, "font", 4)) { - new_f = g_new(zframe, 1); - new_f->enclosing = frames; - new_f->text = g_string_new(""); - new_f->href = NULL; - new_f->is_href = FALSE; - new_f->closing = ""; - new_f->has_closer = TRUE; - new_f->closer_mask = 15; - message += 5; - while (*message == ' ') - message++; - if (!g_ascii_strncasecmp(message, "color=\"", 7)) { - message += 7; - new_f->env = "@"; - frames = new_f; - new_f = g_new(zframe, 1); - new_f->enclosing = frames; - new_f->env = "@color"; - new_f->text = g_string_new(""); - new_f->href = NULL; - new_f->is_href = FALSE; - new_f->closing = "\">"; - new_f->has_closer = TRUE; - new_f->closer_mask = 15; - } else if (!g_ascii_strncasecmp(message, "face=\"", 6)) { - message += 6; - new_f->env = "@"; - frames = new_f; - new_f = g_new(zframe, 1); - new_f->enclosing = frames; - new_f->env = "@font"; - new_f->text = g_string_new(""); - new_f->href = NULL; - new_f->is_href = FALSE; - new_f->closing = "\">"; - new_f->has_closer = TRUE; - new_f->closer_mask = 15; - } else if (!g_ascii_strncasecmp(message, "size=\"", 6)) { - message += 6; - if ((*message == '1') || (*message == '2')) { - new_f->env = "@small"; - } else if ((*message == '3') - || (*message == '4')) { - new_f->env = "@medium"; - } else if ((*message == '5') - || (*message == '6') - || (*message == '7')) { - new_f->env = "@large"; - } else { - new_f->env = ""; - new_f->has_closer = FALSE; - new_f->closer_mask = frames->closer_mask; - } - message += 3; - } else { - /* Drop all unrecognized/misparsed font tags */ - new_f->env = ""; - new_f->has_closer = FALSE; - new_f->closer_mask = frames->closer_mask; - while (g_ascii_strncasecmp(message, "\">", 2) != 0) { - message++; - } - if (*message != '\0') - message += 2; - } - frames = new_f; - } else { - /* Catch all for all unrecognized/misparsed tage */ - g_string_append_c(frames->text, *message++); - } - } else if (*message == '@') { - g_string_append(frames->text, "@@"); - message++; - } else if (*message == '}') { - if (frames->closer_mask & ~1) { - frames->closer_mask &= ~1; - g_string_append_c(frames->text, *message++); - } else { - g_string_append(frames->text, "@[}]"); - message++; - } - } else if (*message == ']') { - if (frames->closer_mask & ~2) { - frames->closer_mask &= ~2; - g_string_append_c(frames->text, *message++); - } else { - g_string_append(frames->text, "@{]}"); - message++; - } - } else if (*message == ')') { - if (frames->closer_mask & ~4) { - frames->closer_mask &= ~4; - g_string_append_c(frames->text, *message++); - } else { - g_string_append(frames->text, "@{)}"); - message++; - } - } else if (!g_ascii_strncasecmp(message, ">", 4)) { - if (frames->closer_mask & ~8) { - frames->closer_mask &= ~8; - g_string_append_c(frames->text, *message++); - } else { - g_string_append(frames->text, "@{>}"); - message += 4; - } - } else { - g_string_append_c(frames->text, *message++); - } - } - ret = g_string_free(frames->text, FALSE); - g_free(frames); - purple_debug_info("zephyr","zephyr outputted %s\n",ret); - return ret; -} - -/* this parses zephyr formatting and converts it to html. For example, if - * you pass in "@{@color(blue)@i(hello)}" you should get out - * "hello". */ -static char *zephyr_to_html(const char *message) -{ - zframe *frames, *curr; - char *ret; - - frames = g_new(zframe, 1); - frames->text = g_string_new(""); - frames->enclosing = NULL; - frames->closing = ""; - frames->has_closer = FALSE; - frames->closer = NULL; - - while (*message) { - if (*message == '@' && message[1] == '@') { - g_string_append(frames->text, "@"); - message += 2; - } else if (*message == '@') { - int end; - for (end = 1; message[end] && (isalnum(message[end]) || message[end] == '_'); end++); - if (message[end] && - (message[end] == '{' || message[end] == '[' || message[end] == '(' || - !g_ascii_strncasecmp(message + end, "<", 4))) { - zframe *new_f; - char *buf; - buf = g_new0(char, end); - g_snprintf(buf, end, "%s", message + 1); - message += end; - new_f = g_new(zframe, 1); - new_f->enclosing = frames; - new_f->has_closer = TRUE; - new_f->closer = (*message == '{' ? "}" : - *message == '[' ? "]" : - *message == '(' ? ")" : - ">"); - message += (*message == '&' ? 4 : 1); - if (!g_ascii_strcasecmp(buf, "italic") || !g_ascii_strcasecmp(buf, "i")) { - new_f->text = g_string_new(""); - new_f->closing = ""; - } else if (!g_ascii_strcasecmp(buf, "small")) { - new_f->text = g_string_new(""); - new_f->closing = ""; - } else if (!g_ascii_strcasecmp(buf, "medium")) { - new_f->text = g_string_new(""); - new_f->closing = ""; - } else if (!g_ascii_strcasecmp(buf, "large")) { - new_f->text = g_string_new(""); - new_f->closing = ""; - } else if (!g_ascii_strcasecmp(buf, "bold") - || !g_ascii_strcasecmp(buf, "b")) { - new_f->text = g_string_new(""); - new_f->closing = ""; - } else if (!g_ascii_strcasecmp(buf, "font")) { - zframe *extra_f; - extra_f = g_new(zframe, 1); - extra_f->enclosing = frames; - new_f->enclosing = extra_f; - extra_f->text = g_string_new(""); - extra_f->has_closer = FALSE; - extra_f->closer = frames->closer; - extra_f->closing = ""; - new_f->text = g_string_new("closing = "\">"; - } else if (!g_ascii_strcasecmp(buf, "color")) { - zframe *extra_f; - extra_f = g_new(zframe, 1); - extra_f->enclosing = frames; - new_f->enclosing = extra_f; - extra_f->text = g_string_new(""); - extra_f->has_closer = FALSE; - extra_f->closer = frames->closer; - extra_f->closing = ""; - new_f->text = g_string_new("closing = "\">"; - } else { - new_f->text = g_string_new(""); - new_f->closing = ""; - } - frames = new_f; - } else { - /* Not a formatting tag, add the character as normal. */ - g_string_append_c(frames->text, *message++); - } - } else if (frames->closer && !g_ascii_strncasecmp(message, frames->closer, strlen(frames->closer))) { - zframe *popped; - gboolean last_had_closer; - - message += strlen(frames->closer); - if (frames->enclosing) { - do { - popped = frames; - frames = frames->enclosing; - g_string_append(frames->text, popped->text->str); - g_string_append(frames->text, popped->closing); - g_string_free(popped->text, TRUE); - last_had_closer = popped->has_closer; - g_free(popped); - } while (frames->enclosing && !last_had_closer); - } else { - g_string_append_c(frames->text, *message); - } - } else if (*message == '\n') { - g_string_append(frames->text, "
"); - message++; - } else { - g_string_append_c(frames->text, *message++); - } - } - /* go through all the stuff that they didn't close */ - while (frames->enclosing) { - curr = frames; - g_string_append(frames->enclosing->text, frames->text->str); - g_string_append(frames->enclosing->text, frames->closing); - g_string_free(frames->text, TRUE); - frames = frames->enclosing; - g_free(curr); - } - ret = g_string_free(frames->text, FALSE); - g_free(frames); - return ret; -} - -static gboolean pending_zloc(zephyr_account *zephyr, const char *who) -{ - GList *curr; - - for (curr = zephyr->pending_zloc_names; curr != NULL; curr = curr->next) { - char* normalized_who = local_zephyr_normalize(zephyr,who); - if (!g_ascii_strcasecmp(normalized_who, (char *)curr->data)) { - g_free(curr->data); - zephyr->pending_zloc_names = g_list_delete_link(zephyr->pending_zloc_names, curr); - return TRUE; - } - } - return FALSE; -} - -/* Called when the server notifies us a message couldn't get sent */ - -static void message_failed(PurpleConnection *gc, ZNotice_t *notice, struct sockaddr_in from) -{ - if (g_ascii_strcasecmp(notice->z_class, "message")) { - gchar* chat_failed = g_strdup_printf( - _("Unable to send to chat %s,%s,%s"), - notice->z_class, notice->z_class_inst, - notice->z_recipient); - purple_notify_error(gc,"",chat_failed,NULL); - g_free(chat_failed); - } else { - purple_notify_error(gc, notice->z_recipient, - _("User is offline"), NULL); - } -} - -static void handle_message(PurpleConnection *gc, ZNotice_t *notice_p) -{ - ZNotice_t notice; - zephyr_account* zephyr = gc->proto_data; - - memcpy(¬ice, notice_p, sizeof(notice)); /* TODO - use pointer? */ - - if (!g_ascii_strcasecmp(notice.z_class, LOGIN_CLASS)) { - /* well, we'll be updating in 20 seconds anyway, might as well ignore this. */ - } else if (!g_ascii_strcasecmp(notice.z_class, LOCATE_CLASS)) { - if (!g_ascii_strcasecmp(notice.z_opcode, LOCATE_LOCATE)) { - int nlocs; - char *user; - PurpleBuddy *b; - const char *bname; - - /* XXX add real error reporting */ - if (ZParseLocations(¬ice, NULL, &nlocs, &user) != ZERR_NONE) - return; - - if ((b = purple_find_buddy(gc->account, user)) == NULL) { - char* stripped_user = zephyr_strip_local_realm(zephyr,user); - b = purple_find_buddy(gc->account,stripped_user); - g_free(stripped_user); - } - - bname = b ? purple_buddy_get_name(b) : NULL; - if ((b && pending_zloc(zephyr,bname)) || pending_zloc(zephyr,user)) { - ZLocations_t locs; - int one = 1; - PurpleNotifyUserInfo *user_info = purple_notify_user_info_new(); - char *tmp; - const char *balias; - - purple_notify_user_info_add_pair(user_info, _("User"), (b ? bname : user)); - balias = purple_buddy_get_local_buddy_alias(b); - if (b && balias) - purple_notify_user_info_add_pair(user_info, _("Alias"), balias); - - if (!nlocs) { - purple_notify_user_info_add_pair(user_info, NULL, _("Hidden or not logged-in")); - } - for (; nlocs > 0; nlocs--) { - /* XXX add real error reporting */ - - ZGetLocations(&locs, &one); - tmp = g_strdup_printf(_("
At %s since %s"), locs.host, locs.time); - purple_notify_user_info_add_pair(user_info, _("Location"), tmp); - g_free(tmp); - } - purple_notify_userinfo(gc, (b ? bname : user), - user_info, NULL, NULL); - purple_notify_user_info_destroy(user_info); - } else { - if (nlocs>0) - purple_prpl_got_user_status(gc->account, b ? bname : user, "available", NULL); - else - purple_prpl_got_user_status(gc->account, b ? bname : user, "offline", NULL); - } - - g_free(user); - } - } else { - char *buf, *buf2, *buf3; - char *send_inst; - PurpleConversation *gconv1; - PurpleConvChat *gcc; - char *ptr = (char *) notice.z_message + (strlen(notice.z_message) + 1); - int len; - char *stripped_sender; - int signature_length = strlen(notice.z_message); - PurpleMessageFlags flags = 0; - gchar *tmpescape; - - /* Need to deal with 0 length messages to handle typing notification (OPCODE) ping messages */ - /* One field zephyrs would have caused purple to crash */ - if ( (notice.z_message_len == 0) || (signature_length >= notice.z_message_len - 1)) { - len = 0; - purple_debug_info("zephyr","message_size %d %d %d\n",len,notice.z_message_len,signature_length); - buf3 = g_strdup(""); - - } else { - len = notice.z_message_len - ( signature_length +1); - purple_debug_info("zephyr","message_size %d %d %d\n",len,notice.z_message_len,signature_length); - buf = g_malloc(len + 1); - g_snprintf(buf, len + 1, "%s", ptr); - g_strchomp(buf); - tmpescape = g_markup_escape_text(buf, -1); - g_free(buf); - buf2 = zephyr_to_html(tmpescape); - buf3 = zephyr_recv_convert(gc, buf2); - g_free(buf2); - g_free(tmpescape); - } - - stripped_sender = zephyr_strip_local_realm(zephyr,notice.z_sender); - - if (!g_ascii_strcasecmp(notice.z_class, "MESSAGE") && !g_ascii_strcasecmp(notice.z_class_inst, "PERSONAL") - && !g_ascii_strcasecmp(notice.z_recipient,zephyr->username)) { - if (!g_ascii_strcasecmp(notice.z_message, "Automated reply:")) - flags |= PURPLE_MESSAGE_AUTO_RESP; - - if (!g_ascii_strcasecmp(notice.z_opcode,"PING")) - serv_got_typing(gc,stripped_sender,ZEPHYR_TYPING_RECV_TIMEOUT, PURPLE_TYPING); - else - serv_got_im(gc, stripped_sender, buf3, flags, time(NULL)); - - } else { - zephyr_triple *zt1, *zt2; - gchar *send_inst_utf8; - zephyr_account *zephyr = gc->proto_data; - zt1 = new_triple(zephyr,notice.z_class, notice.z_class_inst, notice.z_recipient); - zt2 = find_sub_by_triple(zephyr,zt1); - if (!zt2) { - /* This is a server supplied subscription */ - zephyr->subscrips = g_slist_append(zephyr->subscrips, new_triple(zephyr,zt1->class,zt1->instance,zt1->recipient)); - zt2 = find_sub_by_triple(zephyr,zt1); - } - - if (!zt2->open) { - zt2->open = TRUE; - serv_got_joined_chat(gc, zt2->id, zt2->name); - zephyr_chat_set_topic(gc,zt2->id,notice.z_class_inst); - } - - if (!g_ascii_strcasecmp(notice.z_class_inst,"PERSONAL")) - send_inst_utf8 = g_strdup(stripped_sender); - else { - send_inst = g_strdup_printf("[%s] %s",notice.z_class_inst,stripped_sender); - send_inst_utf8 = zephyr_recv_convert(gc,send_inst); - g_free(send_inst); - if (!send_inst_utf8) { - purple_debug_error("zephyr","Failed to convert instance for sender %s.\n", stripped_sender); - send_inst_utf8 = g_strdup(stripped_sender); - } - } - - gconv1 = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, - zt2->name, gc->account); - gcc = purple_conversation_get_chat_data(gconv1); -#ifndef INET_ADDRSTRLEN -#define INET_ADDRSTRLEN 16 -#endif - if (!purple_conv_chat_find_user(gcc, stripped_sender)) { - gchar ipaddr[INET_ADDRSTRLEN]; -#ifdef HAVE_INET_NTOP - inet_ntop(AF_INET, ¬ice.z_sender_addr.s_addr, ipaddr, sizeof(ipaddr)); -#else - memcpy(ipaddr,inet_ntoa(notice.z_sender_addr),sizeof(ipaddr)); -#endif - purple_conv_chat_add_user(gcc, stripped_sender, ipaddr, PURPLE_CBFLAGS_NONE, TRUE); - } - serv_got_chat_in(gc, zt2->id, send_inst_utf8, 0, buf3, time(NULL)); - g_free(send_inst_utf8); - - free_triple(zt1); - } - g_free(stripped_sender); - g_free(buf3); - } -} - -static int free_parse_tree(parse_tree* tree) { - if (!tree) { - return 0; - } - else { - int i; - for(i=0;inum_children;i++){ - if (tree->children[i]) { - free_parse_tree(tree->children[i]); - g_free(tree->children[i]); - } - } - if ((tree != &null_parse_tree) && (tree->contents != NULL)) - g_free(tree->contents); - - } - return 0; -} - -static parse_tree *tree_child(parse_tree* tree,int index) { - if (index < tree->num_children) { - return tree->children[index]; - } else { - return &null_parse_tree; - } -} - -static parse_tree *find_node(parse_tree* ptree,gchar* key) -{ - gchar* tc; - - if (!ptree || ! key) - return &null_parse_tree; - - tc = tree_child(ptree,0)->contents; - - /* g_strcasecmp() is deprecated. What is the encoding here??? */ - if (ptree->num_children > 0 && tc && !g_ascii_strcasecmp(tc, key)) { - return ptree; - } else { - parse_tree *result = &null_parse_tree; - int i; - for(i = 0; i < ptree->num_children; i++) { - result = find_node(ptree->children[i],key); - if(result != &null_parse_tree) { - break; - } - } - return result; - } -} - -static parse_tree *parse_buffer(gchar* source, gboolean do_parse) { - - parse_tree *ptree = g_new0(parse_tree,1); - ptree->contents = NULL; - ptree->num_children=0; - if (do_parse) { - unsigned int p = 0; - while(p < strlen(source)) { - unsigned int end; - gchar *newstr; - - /* Eat white space: */ - if(g_ascii_isspace(source[p]) || source[p] == '\001') { - p++; - continue; - } - - /* Skip comments */ - if(source[p] == ';') { - while(source[p] != '\n' && p < strlen(source)) { - p++; - } - continue; - } - - if(source[p] == '(') { - int nesting = 0; - gboolean in_quote = FALSE; - gboolean escape_next = FALSE; - p++; - end = p; - while(!(source[end] == ')' && nesting == 0 && !in_quote) && end < strlen(source)) { - if(!escape_next) { - if(source[end] == '\\') { - escape_next = TRUE; - } - if(!in_quote) { - if(source[end] == '(') { - nesting++; - } - if(source[end] == ')') { - nesting--; - } - } - if(source[end] == '"') { - in_quote = !in_quote; - } - } else { - escape_next = FALSE; - } - end++; - } - do_parse = TRUE; - - } else { - gchar end_char; - if(source[p] == '"') { - end_char = '"'; - p++; - } else { - end_char = ' '; - } - do_parse = FALSE; - - end = p; - while(source[end] != end_char && end < strlen(source)) { - if(source[end] == '\\') - end++; - end++; - } - } - newstr = g_new0(gchar, end+1-p); - strncpy(newstr,source+p,end-p); - if (ptree->num_children < MAXCHILDREN) { - /* In case we surpass maxchildren, ignore this */ - ptree->children[ptree->num_children++] = parse_buffer( newstr, do_parse); - } else { - purple_debug_error("zephyr","too many children in tzc output. skipping\n"); - } - g_free(newstr); - p = end + 1; - } - return ptree; - } else { - /* XXX does this have to be strdup'd */ - ptree->contents = g_strdup(source); - return ptree; - } -} - -static parse_tree *read_from_tzc(zephyr_account* zephyr){ - struct timeval tv; - fd_set rfds; - int bufsize = 2048; - char *buf = (char *)calloc(bufsize, 1); - char *bufcur = buf; - int selected = 0; - parse_tree *incoming_msg; - - FD_ZERO(&rfds); - FD_SET(zephyr->fromtzc[ZEPHYR_FD_READ], &rfds); - tv.tv_sec = 0; - tv.tv_usec = 0; - incoming_msg=NULL; - - while (select(zephyr->fromtzc[ZEPHYR_FD_READ] + 1, &rfds, NULL, NULL, &tv)) { - selected = 1; - if (read(zephyr->fromtzc[ZEPHYR_FD_READ], bufcur, 1) != 1) { - purple_debug_error("zephyr", "couldn't read\n"); - purple_connection_error(purple_account_get_connection(zephyr->account), "couldn't read"); - free(buf); - return NULL; - } - bufcur++; - if ((bufcur - buf) > (bufsize - 1)) { - if ((buf = realloc(buf, bufsize * 2)) == NULL) { - purple_debug_error("zephyr","Ran out of memory\n"); - exit(-1); - } else { - bufcur = buf + bufsize; - bufsize *= 2; - } - } - } - *bufcur = '\0'; - - if (selected) { - incoming_msg = parse_buffer(buf,TRUE); - } - free(buf); - return incoming_msg; -} - -static gint check_notify_tzc(gpointer data) -{ - PurpleConnection *gc = (PurpleConnection *)data; - zephyr_account* zephyr = gc->proto_data; - parse_tree *newparsetree = read_from_tzc(zephyr); - if (newparsetree != NULL) { - gchar *spewtype; - if ( (spewtype = tree_child(find_node(newparsetree,"tzcspew"),2)->contents) ) { - if (!g_ascii_strncasecmp(spewtype,"message",7)) { - ZNotice_t notice; - parse_tree *msgnode = tree_child(find_node(newparsetree,"message"),2); - parse_tree *bodynode = tree_child(msgnode,1); - /* char *zsig = g_strdup(" "); */ /* purple doesn't care about zsigs */ - char *msg = zephyr_tzc_deescape_str(bodynode->contents); - size_t bufsize = strlen(msg) + 3; - char *buf = g_new0(char,bufsize); - g_snprintf(buf,1+strlen(msg)+2," %c%s",'\0',msg); - memset((char *)¬ice, 0, sizeof(notice)); - notice.z_kind = ACKED; - notice.z_port = 0; - notice.z_opcode = tree_child(find_node(newparsetree,"opcode"),2)->contents; - notice.z_class = zephyr_tzc_deescape_str(tree_child(find_node(newparsetree,"class"),2)->contents); - notice.z_class_inst = tree_child(find_node(newparsetree,"instance"),2)->contents; - notice.z_recipient = local_zephyr_normalize(zephyr,tree_child(find_node(newparsetree,"recipient"),2)->contents); - notice.z_sender = local_zephyr_normalize(zephyr,tree_child(find_node(newparsetree,"sender"),2)->contents); - notice.z_default_format = "Class $class, Instance $instance:\n" "To: @bold($recipient) at $time $date\n" "From: @bold($1) <$sender>\n\n$2"; - notice.z_message_len = strlen(msg) + 3; - notice.z_message = buf; - handle_message(gc, ¬ice); - g_free(msg); - /* g_free(zsig); */ - g_free(buf); - /* free_parse_tree(msgnode); - free_parse_tree(bodynode); - g_free(msg); - g_free(zsig); - g_free(buf); - */ - } - else if (!g_ascii_strncasecmp(spewtype,"zlocation",9)) { - /* check_loc or zephyr_zloc respectively */ - /* XXX fix */ - char *user; - PurpleBuddy *b; - const char *bname; - int nlocs = 0; - parse_tree *locations; - gchar *locval; - user = tree_child(find_node(newparsetree,"user"),2)->contents; - - if ((b = purple_find_buddy(gc->account, user)) == NULL) { - gchar *stripped_user = zephyr_strip_local_realm(zephyr,user); - b = purple_find_buddy(gc->account, stripped_user); - g_free(stripped_user); - } - locations = find_node(newparsetree,"locations"); - locval = tree_child(tree_child(tree_child(tree_child(locations,2),0),0),2)->contents; - - if (!locval || !g_ascii_strcasecmp(locval," ") || (strlen(locval) == 0)) { - nlocs = 0; - } else { - nlocs = 1; - } - - bname = b ? purple_buddy_get_name(b) : NULL; - if ((b && pending_zloc(zephyr,bname)) || pending_zloc(zephyr,user) || pending_zloc(zephyr,local_zephyr_normalize(zephyr,user))){ - PurpleNotifyUserInfo *user_info = purple_notify_user_info_new(); - char *tmp; - const char *balias; - - purple_notify_user_info_add_pair(user_info, _("User"), (b ? bname : user)); - - balias = b ? purple_buddy_get_local_buddy_alias(b) : NULL; - if (balias) - purple_notify_user_info_add_pair(user_info, _("Alias"), balias); - - if (!nlocs) { - purple_notify_user_info_add_pair(user_info, NULL, _("Hidden or not logged-in")); - } else { - tmp = g_strdup_printf(_("
At %s since %s"), - tree_child(tree_child(tree_child(tree_child(locations,2),0),0),2)->contents, - tree_child(tree_child(tree_child(tree_child(locations,2),0),2),2)->contents); - purple_notify_user_info_add_pair(user_info, _("Location"), tmp); - g_free(tmp); - } - - purple_notify_userinfo(gc, b ? bname : user, - user_info, NULL, NULL); - purple_notify_user_info_destroy(user_info); - } else { - if (nlocs>0) - purple_prpl_got_user_status(gc->account, b ? bname : user, "available", NULL); - else - purple_prpl_got_user_status(gc->account, b ? bname : user, "offline", NULL); - } - } - else if (!g_ascii_strncasecmp(spewtype,"subscribed",10)) { - } - else if (!g_ascii_strncasecmp(spewtype,"start",5)) { - } - else if (!g_ascii_strncasecmp(spewtype,"error",5)) { - /* XXX handle */ - } - } - } - - free_parse_tree(newparsetree); - g_free(newparsetree); - return TRUE; -} - -static gint check_notify_zeph02(gpointer data) -{ - /* XXX add real error reporting */ - PurpleConnection *gc = (PurpleConnection*) data; - while (ZPending()) { - ZNotice_t notice; - struct sockaddr_in from; - /* XXX add real error reporting */ - - z_call_r(ZReceiveNotice(¬ice, &from)); - - switch (notice.z_kind) { - case UNSAFE: - case UNACKED: - case ACKED: - handle_message(gc, ¬ice); - break; - case SERVACK: - if (!(g_ascii_strcasecmp(notice.z_message, ZSRVACK_NOTSENT))) { - message_failed(gc, ¬ice, from); - } - break; - case CLIENTACK: - purple_debug_error("zephyr", "Client ack received\n"); - handle_unknown(¬ice); /* XXX: is it really unknown? */ - break; - default: - /* we'll just ignore things for now */ - handle_unknown(¬ice); - purple_debug_error("zephyr", "Unhandled notice.\n"); - break; - } - /* XXX add real error reporting */ - ZFreeNotice(¬ice); - } - - return TRUE; -} - -#ifdef WIN32 - -static gint check_loc(gpointer data) -{ - GSList *buddies; - ZLocations_t locations; - PurpleConnection *gc = data; - zephyr_account *zephyr = gc->proto_data; - PurpleAccount *account = purple_connection_get_account(gc); - int numlocs; - int one = 1; - - for (buddies = purple_find_buddies(account, NULL); buddies; - buddies = g_slist_delete_link(buddies, buddies)) { - PurpleBuddy *b = buddies->data; - char *chk; - const char *bname = purple_buddy_get_name(b); - chk = local_zephyr_normalize(bname); - ZLocateUser(chk,&numlocs, ZAUTH); - if (numlocs) { - int i; - for(i=0;iproto_data; - PurpleAccount *account = purple_connection_get_account(gc); - - if (use_zeph02(zephyr)) { - ald.user = NULL; - memset(&(ald.uid), 0, sizeof(ZUnique_Id_t)); - ald.version = NULL; - } - - for (buddies = purple_find_buddies(account, NULL); buddies; - buddies = g_slist_delete_link(buddies, buddies)) { - PurpleBuddy *b = buddies->data; - - const char *chk; - const char *name = purple_buddy_get_name(b); - - chk = local_zephyr_normalize(zephyr,name); - purple_debug_info("zephyr","chk: %s b->name %s\n",chk,name); - /* XXX add real error reporting */ - /* doesn't matter if this fails or not; we'll just move on to the next one */ - if (use_zeph02(zephyr)) { -#ifdef WIN32 - int numlocs; - int one=1; - ZLocateUser(chk,&numlocs,ZAUTH); - if (numlocs) { - int i; - for(i=0;i0) - purple_prpl_got_user_status(account,name,"available",NULL); - else - purple_prpl_got_user_status(account,name,"offline",NULL); - } - } -#else - ZRequestLocations(chk, &ald, UNACKED, ZAUTH); - g_free(ald.user); - g_free(ald.version); -#endif /* WIN32 */ - } else - if (use_tzc(zephyr)) { - gchar *zlocstr = g_strdup_printf("((tzcfodder . zlocate) \"%s\")\n",chk); - size_t len = strlen(zlocstr); - size_t result = write(zephyr->totzc[ZEPHYR_FD_WRITE],zlocstr,len); - if (result != len) { - purple_debug_error("zephyr", "Unable to write a message: %s\n", g_strerror(errno)); - } - g_free(zlocstr); - } - } - - return TRUE; -} - -#endif /* WIN32 */ - -static char *get_exposure_level(void) -{ - /* XXX add real error reporting */ - char *exposure = ZGetVariable("exposure"); - - if (!exposure) - return EXPOSE_REALMVIS; - if (!g_ascii_strcasecmp(exposure, EXPOSE_NONE)) - return EXPOSE_NONE; - if (!g_ascii_strcasecmp(exposure, EXPOSE_OPSTAFF)) - return EXPOSE_OPSTAFF; - if (!g_ascii_strcasecmp(exposure, EXPOSE_REALMANN)) - return EXPOSE_REALMANN; - if (!g_ascii_strcasecmp(exposure, EXPOSE_NETVIS)) - return EXPOSE_NETVIS; - if (!g_ascii_strcasecmp(exposure, EXPOSE_NETANN)) - return EXPOSE_NETANN; - return EXPOSE_REALMVIS; -} - -static void strip_comments(char *str) -{ - char *tmp = strchr(str, '#'); - - if (tmp) - *tmp = '\0'; - g_strchug(str); - g_strchomp(str); -} - -static void zephyr_inithosts(zephyr_account *zephyr) -{ - /* XXX This code may not be Win32 clean */ - struct hostent *hent; - - if (gethostname(zephyr->ourhost, sizeof(zephyr->ourhost)) == -1) { - purple_debug_error("zephyr", "unable to retrieve hostname, %%host%% and %%canon%% will be wrong in subscriptions and have been set to unknown\n"); - g_strlcpy(zephyr->ourhost, "unknown", sizeof(zephyr->ourhost)); - g_strlcpy(zephyr->ourhostcanon, "unknown", sizeof(zephyr->ourhostcanon)); - return; - } - - if (!(hent = gethostbyname(zephyr->ourhost))) { - purple_debug_error("zephyr", "unable to resolve hostname, %%canon%% will be wrong in subscriptions.and has been set to the value of %%host%%, %s\n",zephyr->ourhost); - g_strlcpy(zephyr->ourhostcanon, zephyr->ourhost, sizeof(zephyr->ourhostcanon)); - return; - } - - g_strlcpy(zephyr->ourhostcanon, hent->h_name, sizeof(zephyr->ourhostcanon)); - - return; -} - -static void process_zsubs(zephyr_account *zephyr) -{ - /* Loads zephyr chats "(subscriptions) from ~/.zephyr.subs, and - registers (subscribes to) them on the server */ - - /* XXX deal with unsubscriptions */ - /* XXX deal with punts */ - - FILE *f; - gchar *fname; - gchar buff[BUFSIZ]; - - fname = g_strdup_printf("%s/.zephyr.subs", purple_home_dir()); - f = g_fopen(fname, "r"); - if (f) { - char **triple; - char *recip; - char *z_class; - char *z_instance; - char *z_galaxy = NULL; - - while (fgets(buff, BUFSIZ, f)) { - strip_comments(buff); - if (buff[0]) { - triple = g_strsplit(buff, ",", 3); - if (triple[0] && triple[1]) { - char *tmp = g_strdup_printf("%s", zephyr->username); - char *atptr; - - if (triple[2] == NULL) { - recip = g_malloc0(1); - } else if (!g_ascii_strcasecmp(triple[2], "%me%")) { - recip = g_strdup_printf("%s", zephyr->username); - } else if (!g_ascii_strcasecmp(triple[2], "*")) { - /* wildcard - * form of class,instance,* */ - recip = g_malloc0(1); - } else if (!g_ascii_strcasecmp(triple[2], tmp)) { - /* form of class,instance,aatharuv@ATHENA.MIT.EDU */ - recip = g_strdup(triple[2]); - } else if ((atptr = strchr(triple[2], '@')) != NULL) { - /* form of class,instance,*@ANDREW.CMU.EDU - * class,instance,@ANDREW.CMU.EDU - * If realm is local realm, blank recipient, else - * @REALM-NAME - */ - char *realmat = g_strdup_printf("@%s",zephyr->realm); - - if (!g_ascii_strcasecmp(atptr, realmat)) - recip = g_malloc0(1); - else - recip = g_strdup(atptr); - g_free(realmat); - } else { - recip = g_strdup(triple[2]); - } - g_free(tmp); - - if (!g_ascii_strcasecmp(triple[0],"%host%")) { - z_class = g_strdup(zephyr->ourhost); - } else if (!g_ascii_strcasecmp(triple[0],"%canon%")) { - z_class = g_strdup(zephyr->ourhostcanon); - } else { - z_class = g_strdup(triple[0]); - } - - if (!g_ascii_strcasecmp(triple[1],"%host%")) { - z_instance = g_strdup(zephyr->ourhost); - } else if (!g_ascii_strcasecmp(triple[1],"%canon%")) { - z_instance = g_strdup(zephyr->ourhostcanon); - } else { - z_instance = g_strdup(triple[1]); - } - - /* There should be some sort of error report listing classes that couldn't be subbed to. - Not important right now though */ - - if (zephyr_subscribe_to(zephyr,z_class, z_instance, recip,z_galaxy) != ZERR_NONE) { - - purple_debug_error("zephyr", "Couldn't subscribe to %s, %s, %s\n", z_class,z_instance,recip); - } - - zephyr->subscrips = g_slist_append(zephyr->subscrips, new_triple(zephyr,z_class,z_instance,recip)); - /* g_hash_table_destroy(sub_hash_table); */ - g_free(z_instance); - g_free(z_class); - g_free(recip); - } - g_strfreev(triple); - } - } - fclose(f); - } -} - -static void process_anyone(PurpleConnection *gc) -{ - zephyr_account *zephyr = purple_connection_get_protocol_data(gc); - FILE *fd; - gchar buff[BUFSIZ], *filename; - PurpleGroup *g; - PurpleBuddy *b; - - if (!(g = purple_find_group(_("Anyone")))) { - g = purple_group_new(_("Anyone")); - purple_blist_add_group(g, NULL); - } - - filename = g_strconcat(purple_home_dir(), "/.anyone", NULL); - if ((fd = g_fopen(filename, "r")) != NULL) { - while (fgets(buff, BUFSIZ, fd)) { - strip_comments(buff); - if (buff[0]) { - if (!purple_find_buddy(gc->account, buff)) { - char *stripped_user = zephyr_strip_local_realm(zephyr,buff); - purple_debug_info("zephyr","stripped_user %s\n",stripped_user); - if (!purple_find_buddy(gc->account,stripped_user)){ - b = purple_buddy_new(gc->account, stripped_user, NULL); - purple_blist_add_buddy(b, NULL, g, NULL); - } - g_free(stripped_user); - } - } - } - fclose(fd); - } - g_free(filename); -} - -static char* normalize_zephyr_exposure(const char* exposure) { - char *exp2 = g_strstrip(g_ascii_strup(exposure,-1)); - - if (!exp2) - return EXPOSE_REALMVIS; - if (!g_ascii_strcasecmp(exp2, EXPOSE_NONE)) - return EXPOSE_NONE; - if (!g_ascii_strcasecmp(exp2, EXPOSE_OPSTAFF)) - return EXPOSE_OPSTAFF; - if (!g_ascii_strcasecmp(exp2, EXPOSE_REALMANN)) - return EXPOSE_REALMANN; - if (!g_ascii_strcasecmp(exp2, EXPOSE_NETVIS)) - return EXPOSE_NETVIS; - if (!g_ascii_strcasecmp(exp2, EXPOSE_NETANN)) - return EXPOSE_NETANN; - return EXPOSE_REALMVIS; -} - -static void zephyr_login(PurpleAccount * account) -{ - PurpleConnection *gc; - zephyr_account *zephyr; - gboolean read_anyone; - gboolean read_zsubs; - gchar *exposure; - - gc = purple_account_get_connection(account); - read_anyone = purple_account_get_bool(gc->account,"read_anyone",TRUE); - read_zsubs = purple_account_get_bool(gc->account,"read_zsubs",TRUE); - exposure = (gchar *)purple_account_get_string(gc->account, "exposure_level", EXPOSE_REALMVIS); - -#ifdef WIN32 - username = purple_account_get_username(account); -#endif - gc->flags |= PURPLE_CONNECTION_AUTO_RESP | PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_NO_BGCOLOR | PURPLE_CONNECTION_NO_URLDESC; - gc->proto_data = zephyr=g_new0(zephyr_account,1); - - zephyr->account = account; - - /* Make sure that the exposure (visibility) is set to a sane value */ - zephyr->exposure=g_strdup(normalize_zephyr_exposure(exposure)); - - if (purple_account_get_bool(gc->account,"use_tzc",0)) { - zephyr->connection_type = PURPLE_ZEPHYR_TZC; - } else { - zephyr->connection_type = PURPLE_ZEPHYR_KRB4; - } - - zephyr->encoding = (char *)purple_account_get_string(gc->account, "encoding", ZEPHYR_FALLBACK_CHARSET); - purple_connection_update_progress(gc, _("Connecting"), 0, 8); - - /* XXX z_call_s should actually try to report the com_err determined error */ - if (use_tzc(zephyr)) { - pid_t pid; - /* purple_connection_error(gc,"tzc not supported yet"); */ - if ((pipe(zephyr->totzc) != 0) || (pipe(zephyr->fromtzc) != 0)) { - purple_debug_error("zephyr", "pipe creation failed. killing\n"); - exit(-1); - } - - pid = fork(); - - if (pid == -1) { - purple_debug_error("zephyr", "forking failed\n"); - exit(-1); - } - if (pid == 0) { - unsigned int i=0; - gboolean found_ps = FALSE; - gchar ** tzc_cmd_array = g_strsplit(purple_account_get_string(gc->account,"tzc_command","/usr/bin/tzc -e %s")," ",0); - if (close(1) == -1) { - exit(-1); - } - if (dup2(zephyr->fromtzc[1], 1) == -1) { - exit(-1); - } - if (close(zephyr->fromtzc[1]) == -1) { - exit(-1); - } - if (close(0) == -1) { - exit(-1); - } - if (dup2(zephyr->totzc[0], 0) == -1) { - exit(-1); - } - if (close(zephyr->totzc[0]) == -1) { - exit(-1); - } - /* tzc_command should really be of the form - path/to/tzc -e %s - or - ssh username@hostname pathtotzc -e %s - -- this should not require a password, and ideally should be kerberized ssh -- - or - fsh username@hostname pathtotzc -e %s - */ - while(tzc_cmd_array[i] != NULL){ - if (!g_ascii_strncasecmp(tzc_cmd_array[i],"%s",2)) { - /* fprintf(stderr,"replacing %%s with %s\n",zephyr->exposure); */ - tzc_cmd_array[i] = g_strdup(zephyr->exposure); - found_ps = TRUE; - - } else { - /* fprintf(stderr,"keeping %s\n",tzc_cmd_array[i]); */ - } - i++; - } - - if (!found_ps) { - exit(-1); - } - - execvp(tzc_cmd_array[0], tzc_cmd_array); - exit(-1); - } - else { - fd_set rfds; - int bufsize = 2048; - char *buf = (char *)calloc(bufsize, 1); - char *bufcur = buf; - struct timeval tv; - char *ptr; - int parenlevel=0; - char* tempstr; - int tempstridx; - int select_status; - - zephyr->tzc_pid = pid; - /* wait till we have data to read from ssh */ - FD_ZERO(&rfds); - FD_SET(zephyr->fromtzc[ZEPHYR_FD_READ], &rfds); - - tv.tv_sec = 10; - tv.tv_usec = 0; - - purple_debug_info("zephyr", "about to read from tzc\n"); - - if (waitpid(pid, NULL, WNOHANG) == 0) { /* Only select if tzc is still running */ - purple_debug_info("zephyr", "about to read from tzc\n"); - select_status = select(zephyr->fromtzc[ZEPHYR_FD_READ] + 1, &rfds, NULL, NULL, NULL); - } - else { - purple_debug_info("zephyr", "tzc exited early\n"); - select_status = -1; - } - - FD_ZERO(&rfds); - FD_SET(zephyr->fromtzc[ZEPHYR_FD_READ], &rfds); - while (select_status > 0 && - select(zephyr->fromtzc[ZEPHYR_FD_READ] + 1, &rfds, NULL, NULL, &tv) > 0) { - if (read(zephyr->fromtzc[ZEPHYR_FD_READ], bufcur, 1) != 1) { - purple_debug_error("zephyr", "couldn't read\n"); - purple_connection_error(gc, "couldn't read"); - free(buf); - return; - } - bufcur++; - if ((bufcur - buf) > (bufsize - 1)) { - if ((buf = realloc(buf, bufsize * 2)) == NULL) { - exit(-1); - } else { - bufcur = buf + bufsize; - bufsize *= 2; - } - } - FD_ZERO(&rfds); - FD_SET(zephyr->fromtzc[ZEPHYR_FD_READ], &rfds); - tv.tv_sec = 10; - tv.tv_usec = 0; - - } - /* fprintf(stderr, "read from tzc\n"); */ - *bufcur = '\0'; - ptr = buf; - - /* ignore all tzcoutput till we've received the first (*/ - while (ptr < bufcur && (*ptr !='(')) { - ptr++; - } - if (ptr >=bufcur) { - purple_connection_error(gc,"invalid output by tzc (or bad parsing code)"); - free(buf); - return; - } - - while(ptr < bufcur) { - if (*ptr == '(') { - parenlevel++; - } - else if (*ptr == ')') { - parenlevel--; - } - purple_debug_info("zephyr","tzc parenlevel is %d\n",parenlevel); - switch (parenlevel) { - case 0: - break; - case 1: - /* Search for next beginning (, or for the ending */ - ptr++; - while((*ptr != '(') && (*ptr != ')') && (ptr = bufcur) - purple_debug_error("zephyr","tzc parsing error\n"); - break; - case 2: - /* You are probably at - (foo . bar ) or (foo . "bar") or (foo . chars) or (foo . numbers) or (foo . () ) - Parse all the data between the first and last f, and move past ) - */ - tempstr = g_malloc0(20000); - tempstridx=0; - while(parenlevel >1) { - ptr++; - if (*ptr == '(') - parenlevel++; - if (*ptr == ')') - parenlevel--; - if (parenlevel > 1) { - tempstr[tempstridx++]=*ptr; - } else { - ptr++; - } - } - purple_debug_info("zephyr","tempstr parsed\n"); - /* tempstr should now be a tempstridx length string containing all characters - from that after the first ( to the one before the last paren ). */ - /* We should have the following possible lisp strings but we don't care - (tzcspew . start) (version . "something") (pid . number)*/ - /* We care about 'zephyrid . "username@REALM.NAME"' and 'exposure . "SOMETHING"' */ - tempstridx=0; - if (!g_ascii_strncasecmp(tempstr,"zephyrid",8)) { - gchar* username = g_malloc0(100); - int username_idx=0; - char *realm; - purple_debug_info("zephyr","zephyrid found\n"); - tempstridx+=8; - while(tempstr[tempstridx] !='"' && tempstridx < 20000) - tempstridx++; - tempstridx++; - while(tempstr[tempstridx] !='"' && tempstridx < 20000) - username[username_idx++]=tempstr[tempstridx++]; - - zephyr->username = g_strdup_printf("%s",username); - if ((realm = strchr(username,'@'))) - zephyr->realm = g_strdup_printf("%s",realm+1); - else { - realm = (gchar *)purple_account_get_string(gc->account,"realm",""); - if (!*realm) { - realm = "local-realm"; - } - zephyr->realm = g_strdup(realm); - g_strlcpy(__Zephyr_realm, (const char*)zephyr->realm, REALM_SZ-1); - } - /* else { - zephyr->realm = g_strdup("local-realm"); - }*/ - - g_free(username); - } else { - purple_debug_info("zephyr", "something that's not zephyr id found %s\n",tempstr); - } - - /* We don't care about anything else yet */ - g_free(tempstr); - break; - default: - purple_debug_info("zephyr","parenlevel is not 1 or 2\n"); - /* This shouldn't be happening */ - break; - } - if (parenlevel==0) - break; - } /* while (ptr < bufcur) */ - purple_debug_info("zephyr", "tzc startup done\n"); - free(buf); - } - } - else if ( use_zeph02(zephyr)) { - gchar* realm; - z_call_s(ZInitialize(), "Couldn't initialize zephyr"); - z_call_s(ZOpenPort(&(zephyr->port)), "Couldn't open port"); - z_call_s(ZSetLocation((char *)zephyr->exposure), "Couldn't set location"); - - realm = (gchar *)purple_account_get_string(gc->account,"realm",""); - if (!*realm) { - realm = ZGetRealm(); - } - zephyr->realm = g_strdup(realm); - g_strlcpy(__Zephyr_realm, (const char*)zephyr->realm, REALM_SZ-1); - zephyr->username = g_strdup(ZGetSender()); - - /* zephyr->realm = g_strdup(ZGetRealm()); */ - purple_debug_info("zephyr","realm: %s\n",zephyr->realm); - } - else { - purple_connection_error(gc,"Only ZEPH0.2 supported currently"); - return; - } - purple_debug_info("zephyr","does it get here\n"); - purple_debug_info("zephyr"," realm: %s username:%s\n", zephyr->realm, zephyr->username); - - /* For now */ - zephyr->galaxy = NULL; - zephyr->krbtkfile = NULL; - zephyr_inithosts(zephyr); - - if (zephyr_subscribe_to(zephyr,"MESSAGE","PERSONAL",zephyr->username,NULL) != ZERR_NONE) { - /* XXX don't translate this yet. It could be written better */ - /* XXX error messages could be handled with more detail */ - purple_notify_error(account->gc, NULL, - "Unable to subscribe to messages", "Unable to subscribe to initial messages"); - return; - } - - purple_connection_set_state(gc, PURPLE_CONNECTED); - - if (read_anyone) - process_anyone(gc); - if (read_zsubs) - process_zsubs(zephyr); - - if (use_zeph02(zephyr)) { - zephyr->nottimer = purple_timeout_add(100, check_notify_zeph02, gc); - } else if (use_tzc(zephyr)) { - zephyr->nottimer = purple_timeout_add(100, check_notify_tzc, gc); - } - zephyr->loctimer = purple_timeout_add_seconds(20, check_loc, gc); - -} - -static void write_zsubs(zephyr_account *zephyr) -{ - /* Exports subscription (chat) list back to - * .zephyr.subs - * XXX deal with %host%, %canon%, unsubscriptions, and negative subscriptions (punts?) - */ - - GSList *s = zephyr->subscrips; - zephyr_triple *zt; - FILE *fd; - char *fname; - - char **triple; - - fname = g_strdup_printf("%s/.zephyr.subs", purple_home_dir()); - fd = g_fopen(fname, "w"); - - if (!fd) { - g_free(fname); - return; - } - - while (s) { - char *zclass, *zinst, *zrecip; - zt = s->data; - triple = g_strsplit(zt->name, ",", 3); - - /* deal with classes */ - if (!g_ascii_strcasecmp(triple[0],zephyr->ourhost)) { - zclass = g_strdup("%host%"); - } else if (!g_ascii_strcasecmp(triple[0],zephyr->ourhostcanon)) { - zclass = g_strdup("%canon%"); - } else { - zclass = g_strdup(triple[0]); - } - - /* deal with instances */ - - if (!g_ascii_strcasecmp(triple[1],zephyr->ourhost)) { - zinst = g_strdup("%host%"); - } else if (!g_ascii_strcasecmp(triple[1],zephyr->ourhostcanon)) { - zinst = g_strdup("%canon%");; - } else { - zinst = g_strdup(triple[1]); - } - - /* deal with recipients */ - if (triple[2] == NULL) { - zrecip = g_strdup("*"); - } else if (!g_ascii_strcasecmp(triple[2],"")){ - zrecip = g_strdup("*"); - } else if (!g_ascii_strcasecmp(triple[2], zephyr->username)) { - zrecip = g_strdup("%me%"); - } else { - zrecip = g_strdup(triple[2]); - } - - fprintf(fd, "%s,%s,%s\n",zclass,zinst,zrecip); - - g_free(zclass); - g_free(zinst); - g_free(zrecip); - g_free(triple); - s = s->next; - } - g_free(fname); - fclose(fd); -} - -static void write_anyone(zephyr_account *zephyr) -{ - GSList *buddies; - char *fname; - FILE *fd; - PurpleAccount *account; - fname = g_strdup_printf("%s/.anyone", purple_home_dir()); - fd = g_fopen(fname, "w"); - if (!fd) { - g_free(fname); - return; - } - - account = zephyr->account; - for (buddies = purple_find_buddies(account, NULL); buddies; - buddies = g_slist_delete_link(buddies, buddies)) { - PurpleBuddy *b = buddies->data; - gchar *stripped_user = zephyr_strip_local_realm(zephyr, purple_buddy_get_name(b)); - fprintf(fd, "%s\n", stripped_user); - g_free(stripped_user); - } - - fclose(fd); - g_free(fname); -} - -static void zephyr_close(PurpleConnection * gc) -{ - GList *l; - GSList *s; - zephyr_account *zephyr = gc->proto_data; - pid_t tzc_pid = zephyr->tzc_pid; - - l = zephyr->pending_zloc_names; - while (l) { - g_free((char *)l->data); - l = l->next; - } - g_list_free(zephyr->pending_zloc_names); - - if (purple_account_get_bool(gc->account, "write_anyone", FALSE)) - write_anyone(zephyr); - - if (purple_account_get_bool(gc->account, "write_zsubs", FALSE)) - write_zsubs(zephyr); - - s = zephyr->subscrips; - while (s) { - free_triple((zephyr_triple *) s->data); - s = s->next; - } - g_slist_free(zephyr->subscrips); - - if (zephyr->nottimer) - purple_timeout_remove(zephyr->nottimer); - zephyr->nottimer = 0; - if (zephyr->loctimer) - purple_timeout_remove(zephyr->loctimer); - zephyr->loctimer = 0; - gc = NULL; - if (use_zeph02(zephyr)) { - z_call(ZCancelSubscriptions(0)); - z_call(ZUnsetLocation()); - z_call(ZClosePort()); - } else { - /* assume tzc */ - if (kill(tzc_pid,SIGTERM) == -1) { - int err=errno; - if (err==EINVAL) { - purple_debug_error("zephyr","An invalid signal was specified when killing tzc\n"); - } - else if (err==ESRCH) { - purple_debug_error("zephyr","Tzc's pid didn't exist while killing tzc\n"); - } - else if (err==EPERM) { - purple_debug_error("zephyr","purple didn't have permission to kill tzc\n"); - } - else { - purple_debug_error("zephyr","miscellaneous error while attempting to close tzc\n"); - } - } - } -} - -static int zephyr_send_message(zephyr_account *zephyr,char* zclass, char* instance, char* recipient, const char *im, - const char *sig, char *opcode) ; - -static const char * zephyr_get_signature(void) -{ - /* XXX add zephyr error reporting */ - const char * sig =ZGetVariable("zwrite-signature"); - if (!sig) { - sig = g_get_real_name(); - } - return sig; -} - -static int zephyr_chat_send(PurpleConnection * gc, int id, const char *im, PurpleMessageFlags flags) -{ - zephyr_triple *zt; - const char *sig; - PurpleConversation *gconv1; - PurpleConvChat *gcc; - char *inst; - char *recipient; - zephyr_account *zephyr = gc->proto_data; - - zt = find_sub_by_id(zephyr,id); - if (!zt) - /* this should never happen. */ - return -EINVAL; - - sig = zephyr_get_signature(); - - gconv1 = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, zt->name, - gc->account); - gcc = purple_conversation_get_chat_data(gconv1); - - if (!(inst = (char *)purple_conv_chat_get_topic(gcc))) - inst = g_strdup("PERSONAL"); - - if (!g_ascii_strcasecmp(zt->recipient, "*")) - recipient = local_zephyr_normalize(zephyr,""); - else - recipient = local_zephyr_normalize(zephyr,zt->recipient); - - zephyr_send_message(zephyr,zt->class,inst,recipient,im,sig,""); - return 0; -} - - -static int zephyr_send_im(PurpleConnection * gc, const char *who, const char *im, PurpleMessageFlags flags) -{ - const char *sig; - zephyr_account *zephyr = gc->proto_data; - if (flags & PURPLE_MESSAGE_AUTO_RESP) - sig = "Automated reply:"; - else { - sig = zephyr_get_signature(); - } - zephyr_send_message(zephyr,"MESSAGE","PERSONAL",local_zephyr_normalize(zephyr,who),im,sig,""); - - return 1; -} - -/* Munge the outgoing zephyr so that any quotes or backslashes are - escaped and do not confuse tzc: */ - -static char* zephyr_tzc_escape_msg(const char *message) -{ - gsize pos = 0, pos2 = 0; - char *newmsg; - - if (message && (strlen(message) > 0)) { - newmsg = g_new0(char,1+strlen(message)*2); - while(pos < strlen(message)) { - if (message[pos]=='\\') { - newmsg[pos2]='\\'; - newmsg[pos2+1]='\\'; - pos2+=2; - } - else if (message[pos]=='"') { - newmsg[pos2]='\\'; - newmsg[pos2+1]='"'; - pos2+=2; - } - else { - newmsg[pos2] = message[pos]; - pos2++; - } - pos++; - } - } else { - newmsg = g_strdup(""); - } - /* fprintf(stderr,"newmsg %s message %s\n",newmsg,message); */ - return newmsg; -} - -char* zephyr_tzc_deescape_str(const char *message) -{ - gsize pos = 0, pos2 = 0; - char *newmsg; - - if (message && (strlen(message) > 0)) { - newmsg = g_new0(char,strlen(message)+1); - while(pos < strlen(message)) { - if (message[pos]=='\\') { - pos++; - } - newmsg[pos2] = message[pos]; - pos++;pos2++; - } - newmsg[pos2]='\0'; - } else { - newmsg = g_strdup(""); - } - - return newmsg; -} - -static int zephyr_send_message(zephyr_account *zephyr,char* zclass, char* instance, char* recipient, const char *im, - const char *sig, char *opcode) -{ - - /* (From the tzc source) - * emacs sends something of the form: - * ((class . "MESSAGE") - * (auth . t) - * (recipients ("PERSONAL" . "bovik") ("test" . "")) - * (sender . "bovik") - * (message . ("Harry Bovik" "my zgram")) - * ) - */ - char *html_buf; - char *html_buf2; - html_buf = html_to_zephyr(im); - html_buf2 = purple_unescape_html(html_buf); - - if(use_tzc(zephyr)) { - size_t len; - size_t result; - char* zsendstr; - /* CMU cclub tzc doesn't grok opcodes for now */ - char* tzc_sig = zephyr_tzc_escape_msg(sig); - char *tzc_body = zephyr_tzc_escape_msg(html_buf2); - zsendstr = g_strdup_printf("((tzcfodder . send) (class . \"%s\") (auth . t) (recipients (\"%s\" . \"%s\")) (message . (\"%s\" \"%s\")) ) \n", - zclass, instance, recipient, tzc_sig, tzc_body); - /* fprintf(stderr,"zsendstr = %s\n",zsendstr); */ - len = strlen(zsendstr); - result = write(zephyr->totzc[ZEPHYR_FD_WRITE], zsendstr, len); - if (result != len) { - g_free(zsendstr); - g_free(html_buf2); - g_free(html_buf); - return errno; - } - g_free(zsendstr); - } else if (use_zeph02(zephyr)) { - ZNotice_t notice; - char *buf = g_strdup_printf("%s%c%s", sig, '\0', html_buf2); - memset((char *)¬ice, 0, sizeof(notice)); - - notice.z_kind = ACKED; - notice.z_port = 0; - notice.z_opcode = ""; - notice.z_class = zclass; - notice.z_class_inst = instance; - notice.z_recipient = recipient; - notice.z_sender = 0; - notice.z_default_format = "Class $class, Instance $instance:\n" "To: @bold($recipient) at $time $date\n" "From: @bold($1) <$sender>\n\n$2"; - notice.z_message_len = strlen(html_buf2) + strlen(sig) + 2; - notice.z_message = buf; - notice.z_opcode = g_strdup(opcode); - purple_debug_info("zephyr","About to send notice\n"); - if (ZSendNotice(¬ice, ZAUTH) != ZERR_NONE) { - /* XXX handle errors here */ - g_free(buf); - g_free(html_buf2); - g_free(html_buf); - return 0; - } - purple_debug_info("zephyr","notice sent\n"); - g_free(buf); - } - - g_free(html_buf2); - g_free(html_buf); - - return 1; -} - -char *local_zephyr_normalize(zephyr_account *zephyr,const char *orig) -{ - /* - Basically the inverse of zephyr_strip_local_realm - */ - char* buf; - - if (!g_ascii_strcasecmp(orig, "")) { - return g_strdup(""); - } - - if (strchr(orig,'@')) { - buf = g_strdup_printf("%s",orig); - } else { - buf = g_strdup_printf("%s@%s",orig,zephyr->realm); - } - return buf; -} - -static const char *zephyr_normalize(const PurpleAccount *account, const char *who) -{ - static char buf[BUF_LEN]; - PurpleConnection *gc; - char *tmp; - - gc = purple_account_get_connection(account); - if (gc == NULL) - return NULL; - - tmp = local_zephyr_normalize(gc->proto_data, who); - - if (strlen(tmp) >= sizeof(buf)) { - g_free(tmp); - return NULL; - } - - g_strlcpy(buf, tmp, sizeof(buf)); - g_free(tmp); - - return buf; -} - -static void zephyr_zloc(PurpleConnection *gc, const char *who) -{ - ZAsyncLocateData_t ald; - zephyr_account *zephyr = gc->proto_data; - gchar* normalized_who = local_zephyr_normalize(zephyr,who); - - if (use_zeph02(zephyr)) { - if (ZRequestLocations(normalized_who, &ald, UNACKED, ZAUTH) == ZERR_NONE) { - zephyr->pending_zloc_names = g_list_append(zephyr->pending_zloc_names, - g_strdup(normalized_who)); - } else { - /* XXX deal with errors somehow */ - } - } else if (use_tzc(zephyr)) { - size_t len; - size_t result; - char* zlocstr = g_strdup_printf("((tzcfodder . zlocate) \"%s\")\n",normalized_who); - zephyr->pending_zloc_names = g_list_append(zephyr->pending_zloc_names, g_strdup(normalized_who)); - len = strlen(zlocstr); - result = write(zephyr->totzc[ZEPHYR_FD_WRITE],zlocstr,len); - if (result != len) { - purple_debug_error("zephyr", "Unable to write a message: %s\n", g_strerror(errno)); - } - g_free(zlocstr); - } -} - -static void zephyr_set_status(PurpleAccount *account, PurpleStatus *status) { - size_t len; - size_t result; - zephyr_account *zephyr = purple_account_get_connection(account)->proto_data; - PurpleStatusPrimitive primitive = purple_status_type_get_primitive(purple_status_get_type(status)); - - if (zephyr->away) { - g_free(zephyr->away); - zephyr->away=NULL; - } - - if (primitive == PURPLE_STATUS_AWAY) { - zephyr->away = g_strdup(purple_status_get_attr_string(status,"message")); - } - else if (primitive == PURPLE_STATUS_AVAILABLE) { - if (use_zeph02(zephyr)) { - ZSetLocation(zephyr->exposure); - } - else { - char *zexpstr = g_strdup_printf("((tzcfodder . set-location) (hostname . \"%s\") (exposure . \"%s\"))\n",zephyr->ourhost,zephyr->exposure); - len = strlen(zexpstr); - result = write(zephyr->totzc[ZEPHYR_FD_WRITE],zexpstr,len); - if (result != len) { - purple_debug_error("zephyr", "Unable to write message: %s\n", g_strerror(errno)); - } - g_free(zexpstr); - } - } - else if (primitive == PURPLE_STATUS_INVISIBLE) { - /* XXX handle errors */ - if (use_zeph02(zephyr)) { - ZSetLocation(EXPOSE_OPSTAFF); - } else { - char *zexpstr = g_strdup_printf("((tzcfodder . set-location) (hostname . \"%s\") (exposure . \"%s\"))\n",zephyr->ourhost,EXPOSE_OPSTAFF); - len = strlen(zexpstr); - result = write(zephyr->totzc[ZEPHYR_FD_WRITE],zexpstr,len); - if (result != len) { - purple_debug_error("zephyr", "Unable to write message: %s\n", g_strerror(errno)); - } - g_free(zexpstr); - } - } -} - -static GList *zephyr_status_types(PurpleAccount *account) -{ - PurpleStatusType *type; - GList *types = NULL; - - /* zephyr has several exposures - NONE (where you are hidden, and zephyrs to you are in practice silently dropped -- yes this is wrong) - OPSTAFF "hidden" - REALM-VISIBLE visible to people in local realm - REALM-ANNOUNCED REALM-VISIBLE+ plus your logins/logouts are announced to - NET-VISIBLE REALM-ANNOUNCED, plus visible to people in foreign realm - NET-ANNOUNCED NET-VISIBLE, plus logins/logouts are announced to - - Online will set the user to the exposure they have in their options (defaulting to REALM-VISIBLE), - Hidden, will set the user's exposure to OPSTAFF - - Away won't change their exposure but will set an auto away message (for IMs only) - */ - - type = purple_status_type_new(PURPLE_STATUS_AVAILABLE, NULL, NULL, TRUE); - types = g_list_append(types,type); - - type = purple_status_type_new(PURPLE_STATUS_INVISIBLE, NULL, NULL, TRUE); - types = g_list_append(types,type); - - type = purple_status_type_new_with_attrs( - PURPLE_STATUS_AWAY, NULL, NULL, TRUE, TRUE, FALSE, - "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), - NULL); - types = g_list_append(types, type); - - type = purple_status_type_new(PURPLE_STATUS_OFFLINE, NULL, NULL, TRUE); - types = g_list_append(types,type); - - return types; -} - -static GList *zephyr_chat_info(PurpleConnection * gc) -{ - GList *m = NULL; - struct proto_chat_entry *pce; - - pce = g_new0(struct proto_chat_entry, 1); - - pce->label = _("_Class:"); - pce->identifier = "class"; - m = g_list_append(m, pce); - - pce = g_new0(struct proto_chat_entry, 1); - - pce->label = _("_Instance:"); - pce->identifier = "instance"; - m = g_list_append(m, pce); - - pce = g_new0(struct proto_chat_entry, 1); - - pce->label = _("_Recipient:"); - pce->identifier = "recipient"; - m = g_list_append(m, pce); - - return m; -} - -/* Called when the server notifies us a message couldn't get sent */ - -static void zephyr_subscribe_failed(PurpleConnection *gc,char * z_class, char *z_instance, char * z_recipient, char* z_galaxy) -{ - gchar* subscribe_failed = g_strdup_printf(_("Attempt to subscribe to %s,%s,%s failed"), z_class, z_instance,z_recipient); - purple_notify_error(gc,"", subscribe_failed, NULL); - g_free(subscribe_failed); -} - -static char *zephyr_get_chat_name(GHashTable *data) { - gchar* zclass = g_hash_table_lookup(data,"class"); - gchar* inst = g_hash_table_lookup(data,"instance"); - gchar* recipient = g_hash_table_lookup(data, "recipient"); - if (!zclass) /* This should never happen */ - zclass = ""; - if (!inst) - inst = "*"; - if (!recipient) - recipient = ""; - return g_strdup_printf("%s,%s,%s",zclass,inst,recipient); -} - - -static void zephyr_join_chat(PurpleConnection * gc, GHashTable * data) -{ - /* ZSubscription_t sub; */ - zephyr_triple *zt1, *zt2; - const char *classname; - const char *instname; - const char *recip; - zephyr_account *zephyr=gc->proto_data; - classname = g_hash_table_lookup(data, "class"); - instname = g_hash_table_lookup(data, "instance"); - recip = g_hash_table_lookup(data, "recipient"); - - - if (!classname) - return; - - if (!g_ascii_strcasecmp(classname,"%host%")) - classname = g_strdup(zephyr->ourhost); - if (!g_ascii_strcasecmp(classname,"%canon%")) - classname = g_strdup(zephyr->ourhostcanon); - - if (!instname || !strlen(instname)) - instname = "*"; - - if (!g_ascii_strcasecmp(instname,"%host%")) - instname = g_strdup(zephyr->ourhost); - if (!g_ascii_strcasecmp(instname,"%canon%")) - instname = g_strdup(zephyr->ourhostcanon); - - if (!recip || (*recip == '*')) - recip = ""; - if (!g_ascii_strcasecmp(recip, "%me%")) - recip = zephyr->username; - - zt1 = new_triple(zephyr,classname, instname, recip); - zt2 = find_sub_by_triple(zephyr,zt1); - if (zt2) { - free_triple(zt1); - if (!zt2->open) { - if (!g_ascii_strcasecmp(instname,"*")) - instname = "PERSONAL"; - serv_got_joined_chat(gc, zt2->id, zt2->name); - zephyr_chat_set_topic(gc,zt2->id,instname); - zt2->open = TRUE; - } - return; - } - - /* sub.zsub_class = zt1->class; - sub.zsub_classinst = zt1->instance; - sub.zsub_recipient = zt1->recipient; */ - - if (zephyr_subscribe_to(zephyr,zt1->class,zt1->instance,zt1->recipient,NULL) != ZERR_NONE) { - /* XXX output better subscription information */ - zephyr_subscribe_failed(gc,zt1->class,zt1->instance,zt1->recipient,NULL); - free_triple(zt1); - return; - } - - zephyr->subscrips = g_slist_append(zephyr->subscrips, zt1); - zt1->open = TRUE; - serv_got_joined_chat(gc, zt1->id, zt1->name); - if (!g_ascii_strcasecmp(instname,"*")) - instname = "PERSONAL"; - zephyr_chat_set_topic(gc,zt1->id,instname); -} - -static void zephyr_chat_leave(PurpleConnection * gc, int id) -{ - zephyr_triple *zt; - zephyr_account *zephyr = gc->proto_data; - zt = find_sub_by_id(zephyr,id); - - if (zt) { - zt->open = FALSE; - zt->id = ++(zephyr->last_id); - } -} - -static PurpleChat *zephyr_find_blist_chat(PurpleAccount *account, const char *name) -{ - PurpleBlistNode *gnode, *cnode; - - /* XXX needs to be %host%,%canon%, and %me% clean */ - for(gnode = purple_blist_get_root(); gnode; - gnode = purple_blist_node_get_sibling_next(gnode)) { - for(cnode = purple_blist_node_get_first_child(gnode); - cnode; - cnode = purple_blist_node_get_sibling_next(cnode)) { - PurpleChat *chat = (PurpleChat*)cnode; - char *zclass, *inst, *recip; - char** triple; - GHashTable *components; - if(!PURPLE_BLIST_NODE_IS_CHAT(cnode)) - continue; - if(purple_chat_get_account(chat) != account) - continue; - components = purple_chat_get_components(chat); - if(!(zclass = g_hash_table_lookup(components, "class"))) - continue; - if(!(inst = g_hash_table_lookup(components, "instance"))) - inst = g_strdup(""); - if(!(recip = g_hash_table_lookup(components, "recipient"))) - recip = g_strdup(""); - /* purple_debug_info("zephyr","in zephyr_find_blist_chat name: %s\n",name?name:""); */ - triple = g_strsplit(name,",",3); - if (!g_ascii_strcasecmp(triple[0],zclass) && !g_ascii_strcasecmp(triple[1],inst) && !g_ascii_strcasecmp(triple[2],recip)) - return chat; - - } - } - return NULL; -} -static const char *zephyr_list_icon(PurpleAccount * a, PurpleBuddy * b) -{ - return "zephyr"; -} - -static unsigned int zephyr_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state) { - gchar *recipient; - zephyr_account *zephyr = gc->proto_data; - if (use_tzc(zephyr)) - return 0; - - if (state == PURPLE_NOT_TYPING) - return 0; - - /* XXX We probably should care if this fails. Or maybe we don't want to */ - if (!who) { - purple_debug_info("zephyr", "who is null\n"); - recipient = local_zephyr_normalize(zephyr,""); - } else { - char *comma = strrchr(who, ','); - /* Don't ping broadcast (chat) recipients */ - /* The strrchr case finds a realm-stripped broadcast subscription - e.g. comma is the last character in the string */ - if (comma && ( (*(comma+1) == '\0') || (*(comma+1) == '@'))) - return 0; - - recipient = local_zephyr_normalize(zephyr,who); - } - - purple_debug_info("zephyr","about to send typing notification to %s\n",recipient); - zephyr_send_message(zephyr,"MESSAGE","PERSONAL",recipient,"","","PING"); - purple_debug_info("zephyr","sent typing notification\n"); - - /* - * TODO: Is this correct? It means we will call - * serv_send_typing(gc, who, PURPLE_TYPING) once every 15 seconds - * until the Purple user stops typing. - */ - return ZEPHYR_TYPING_SEND_TIMEOUT; -} - - - -static void zephyr_chat_set_topic(PurpleConnection * gc, int id, const char *topic) -{ - zephyr_triple *zt; - PurpleConversation *gconv; - PurpleConvChat *gcc; - gchar *topic_utf8; - zephyr_account* zephyr = gc->proto_data; - char *sender = (char *)zephyr->username; - - zt = find_sub_by_id(zephyr,id); - /* find_sub_by_id can return NULL */ - if (!zt) - return; - gconv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, zt->name, - gc->account); - gcc = purple_conversation_get_chat_data(gconv); - - topic_utf8 = zephyr_recv_convert(gc,(gchar *)topic); - purple_conv_chat_set_topic(gcc,sender,topic_utf8); - g_free(topic_utf8); - return; -} - -/* commands */ - -static PurpleCmdRet zephyr_purple_cmd_msg(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - char *recipient; - zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data; - if (!g_ascii_strcasecmp(args[0],"*")) - return PURPLE_CMD_RET_FAILED; /* "*" is not a valid argument */ - else - recipient = local_zephyr_normalize(zephyr,args[0]); - - if (strlen(recipient) < 1) - return PURPLE_CMD_RET_FAILED; /* a null recipient is a chat message, not an IM */ - - if (zephyr_send_message(zephyr,"MESSAGE","PERSONAL",recipient,args[1],zephyr_get_signature(),"")) - return PURPLE_CMD_RET_OK; - else - return PURPLE_CMD_RET_FAILED; -} - -static PurpleCmdRet zephyr_purple_cmd_zlocate(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - zephyr_zloc(purple_conversation_get_gc(conv),args[0]); - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet zephyr_purple_cmd_instance(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - /* Currently it sets the instance with leading spaces and - * all. This might not be the best thing to do, though having - * one word isn't ideal either. */ - - PurpleConvChat *gcc = purple_conversation_get_chat_data(conv); - int id = gcc->id; - const char* instance = args[0]; - zephyr_chat_set_topic(purple_conversation_get_gc(conv),id,instance); - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet zephyr_purple_cmd_joinchat_cir(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - /* Join a new zephyr chat */ - GHashTable *triple = g_hash_table_new(NULL,NULL); - g_hash_table_insert(triple,"class",args[0]); - g_hash_table_insert(triple,"instance",args[1]); - g_hash_table_insert(triple,"recipient",args[2]); - zephyr_join_chat(purple_conversation_get_gc(conv),triple); - return PURPLE_CMD_RET_OK; -} - -static PurpleCmdRet zephyr_purple_cmd_zi(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - /* args = instance, message */ - zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data; - if ( zephyr_send_message(zephyr,"message",args[0],"",args[1],zephyr_get_signature(),"")) - return PURPLE_CMD_RET_OK; - else - return PURPLE_CMD_RET_FAILED; -} - -static PurpleCmdRet zephyr_purple_cmd_zci(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - /* args = class, instance, message */ - zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data; - if ( zephyr_send_message(zephyr,args[0],args[1],"",args[2],zephyr_get_signature(),"")) - return PURPLE_CMD_RET_OK; - else - return PURPLE_CMD_RET_FAILED; -} - -static PurpleCmdRet zephyr_purple_cmd_zcir(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - /* args = class, instance, recipient, message */ - zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data; - if ( zephyr_send_message(zephyr,args[0],args[1],args[2],args[3],zephyr_get_signature(),"")) - return PURPLE_CMD_RET_OK; - else - return PURPLE_CMD_RET_FAILED; -} - -static PurpleCmdRet zephyr_purple_cmd_zir(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - /* args = instance, recipient, message */ - zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data; - if ( zephyr_send_message(zephyr,"message",args[0],args[1],args[2],zephyr_get_signature(),"")) - return PURPLE_CMD_RET_OK; - else - return PURPLE_CMD_RET_FAILED; -} - -static PurpleCmdRet zephyr_purple_cmd_zc(PurpleConversation *conv, - const char *cmd, char **args, char **error, void *data) -{ - /* args = class, message */ - zephyr_account *zephyr = purple_conversation_get_gc(conv)->proto_data; - if ( zephyr_send_message(zephyr,args[0],"PERSONAL","",args[1],zephyr_get_signature(),"")) - return PURPLE_CMD_RET_OK; - else - return PURPLE_CMD_RET_FAILED; -} - -static void zephyr_register_slash_commands(void) -{ - - purple_cmd_register("msg","ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-zephyr", - zephyr_purple_cmd_msg, _("msg <nick> <message>: Send a private message to a user"), NULL); - - purple_cmd_register("zlocate","w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-zephyr", - zephyr_purple_cmd_zlocate, _("zlocate <nick>: Locate user"), NULL); - - purple_cmd_register("zl","w", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-zephyr", - zephyr_purple_cmd_zlocate, _("zl <nick>: Locate user"), NULL); - - purple_cmd_register("instance","s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-zephyr", - zephyr_purple_cmd_instance, _("instance <instance>: Set the instance to be used on this class"), NULL); - - purple_cmd_register("inst","s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-zephyr", - zephyr_purple_cmd_instance, _("inst <instance>: Set the instance to be used on this class"), NULL); - - purple_cmd_register("topic","s", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-zephyr", - zephyr_purple_cmd_instance, _("topic <instance>: Set the instance to be used on this class"), NULL); - - purple_cmd_register("sub", "www", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-zephyr", - zephyr_purple_cmd_joinchat_cir, - _("sub <class> <instance> <recipient>: Join a new chat"), NULL); - - purple_cmd_register("zi","ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-zephyr", - zephyr_purple_cmd_zi, _("zi <instance>: Send a message to <message,instance,*>"), NULL); - - purple_cmd_register("zci","wws",PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-zephyr", - zephyr_purple_cmd_zci, - _("zci <class> <instance>: Send a message to <class,instance,*>"), NULL); - - purple_cmd_register("zcir","wwws",PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-zephyr", - zephyr_purple_cmd_zcir, - _("zcir <class> <instance> <recipient>: Send a message to <class,instance,recipient>"), NULL); - - purple_cmd_register("zir","wws",PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-zephyr", - zephyr_purple_cmd_zir, - _("zir <instance> <recipient>: Send a message to <MESSAGE,instance,recipient>"), NULL); - - purple_cmd_register("zc","ws", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-zephyr", - zephyr_purple_cmd_zc, _("zc <class>: Send a message to <class,PERSONAL,*>"), NULL); - -} - - -static int zephyr_resubscribe(PurpleConnection *gc) -{ - /* Resubscribe to the in-memory list of subscriptions and also - unsubscriptions*/ - zephyr_account *zephyr = gc->proto_data; - GSList *s = zephyr->subscrips; - zephyr_triple *zt; - while (s) { - zt = s->data; - /* XXX We really should care if this fails */ - zephyr_subscribe_to(zephyr,zt->class,zt->instance,zt->recipient,NULL); - s = s->next; - } - /* XXX handle unsubscriptions */ - return 1; -} - - -static void zephyr_action_resubscribe(PurplePluginAction *action) -{ - - PurpleConnection *gc = (PurpleConnection *) action->context; - zephyr_resubscribe(gc); -} - - -static void zephyr_action_get_subs_from_server(PurplePluginAction *action) -{ - PurpleConnection *gc = (PurpleConnection *) action->context; - zephyr_account *zephyr = gc->proto_data; - int retval, nsubs, one,i; - ZSubscription_t subs; - if (use_zeph02(zephyr)) { - GString* subout = g_string_new("Subscription list
"); - - if (zephyr->port == 0) { - purple_debug_error("zephyr", "error while retrieving port\n"); - return; - } - if ((retval = ZRetrieveSubscriptions(zephyr->port,&nsubs)) != ZERR_NONE) { - /* XXX better error handling */ - purple_debug_error("zephyr", "error while retrieving subscriptions from server\n"); - return; - } - for(i=0;i", - subs.zsub_class, subs.zsub_classinst, - subs.zsub_recipient); - } - - if (retval == ZERR_NONE) { - gchar *title = g_strdup_printf("Server subscriptions for %s", zephyr->username); - purple_notify_formatted(gc, title, title, NULL, subout->str, NULL, NULL); - g_free(title); - } else { - /* XXX better error handling */ - purple_debug_error("zephyr", "error while retrieving individual subscription\n"); - } - } else { - /* XXX fix */ - purple_notify_error(gc,"","tzc doesn't support this action",NULL); - } -} - - -static GList *zephyr_actions(PurplePlugin *plugin, gpointer context) -{ - GList *list = NULL; - PurplePluginAction *act = NULL; - - act = purple_plugin_action_new(_("Resubscribe"), zephyr_action_resubscribe); - list = g_list_append(list, act); - - act = purple_plugin_action_new(_("Retrieve subscriptions from server"), zephyr_action_get_subs_from_server); - list = g_list_append(list,act); - - return list; -} - -static PurplePlugin *my_protocol = NULL; - -static PurplePluginProtocolInfo prpl_info = { - OPT_PROTO_CHAT_TOPIC | OPT_PROTO_NO_PASSWORD, - NULL, /* ??? user_splits */ - NULL, /* ??? protocol_options */ - NO_BUDDY_ICONS, - zephyr_list_icon, - NULL, /* ??? list_emblems */ - NULL, /* ??? status_text */ - NULL, /* ??? tooltip_text */ - zephyr_status_types, /* status_types */ - NULL, /* ??? blist_node_menu - probably all useful actions are already handled*/ - zephyr_chat_info, /* chat_info */ - NULL, /* chat_info_defaults */ - zephyr_login, /* login */ - zephyr_close, /* close */ - zephyr_send_im, /* send_im */ - NULL, /* XXX set info (Location?) */ - zephyr_send_typing, /* send_typing */ - zephyr_zloc, /* get_info */ - zephyr_set_status, /* set_status */ - NULL, /* ??? set idle */ - NULL, /* change password */ - NULL, /* add_buddy */ - NULL, /* add_buddies */ - NULL, /* remove_buddy */ - NULL, /* remove_buddies */ - NULL, /* add_permit */ - NULL, /* add_deny */ - NULL, /* remove_permit */ - NULL, /* remove_deny */ - NULL, /* set_permit_deny */ - zephyr_join_chat, /* join_chat */ - NULL, /* reject_chat -- No chat invites*/ - zephyr_get_chat_name, /* get_chat_name */ - NULL, /* chat_invite -- No chat invites*/ - zephyr_chat_leave, /* chat_leave */ - NULL, /* chat_whisper -- No "whispering"*/ - zephyr_chat_send, /* chat_send */ - NULL, /* keepalive -- Not necessary*/ - NULL, /* register_user -- Not supported*/ - NULL, /* XXX get_cb_info */ - NULL, /* get_cb_away */ - NULL, /* alias_buddy */ - NULL, /* group_buddy */ - NULL, /* rename_group */ - NULL, /* buddy_free */ - NULL, /* convo_closed */ - zephyr_normalize, /* normalize */ - NULL, /* XXX set_buddy_icon */ - NULL, /* remove_group */ - NULL, /* XXX get_cb_real_name */ - zephyr_chat_set_topic, /* set_chat_topic */ - zephyr_find_blist_chat, /* find_blist_chat */ - NULL, /* roomlist_get_list */ - NULL, /* roomlist_cancel */ - NULL, /* roomlist_expand_category */ - NULL, /* can_receive_file */ - NULL, /* send_file */ - NULL, /* new_xfer */ - NULL, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - - NULL, - NULL, - NULL, - sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL, /* get_account_text_table */ - NULL, /* initate_media */ - NULL, /* get_media_caps */ - NULL, /* get_moods */ - NULL, /* set_public_alias */ - NULL, /* get_public_alias */ - NULL, /* add_buddy_with_invite */ - NULL, /* add_buddies_with_invite */ - NULL, /* get_cb_alias */ - NULL, /* chat_can_receive_file */ - NULL, /* chat_send_file */ -}; - -static PurplePluginInfo info = { - PURPLE_PLUGIN_MAGIC, - PURPLE_MAJOR_VERSION, - PURPLE_MINOR_VERSION, - PURPLE_PLUGIN_PROTOCOL, /**< type */ - NULL, /**< ui_requirement */ - 0, /**< flags */ - NULL, /**< dependencies */ - PURPLE_PRIORITY_DEFAULT, /**< priority */ - - "prpl-zephyr", /**< id */ - "Zephyr", /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("Zephyr Protocol Plugin"), - /** description */ - N_("Zephyr Protocol Plugin"), - NULL, /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - NULL, /**< load */ - NULL, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, - zephyr_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void init_plugin(PurplePlugin * plugin) -{ - PurpleAccountOption *option; - char *tmp = get_exposure_level(); - - option = purple_account_option_bool_new(_("Use tzc"), "use_tzc", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_string_new(_("tzc command"), "tzc_command", "/usr/bin/tzc -e %s"); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_bool_new(_("Export to .anyone"), "write_anyone", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_bool_new(_("Export to .zephyr.subs"), "write_zsubs", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_bool_new(_("Import from .anyone"), "read_anyone", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_bool_new(_("Import from .zephyr.subs"), "read_zsubs", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_string_new(_("Realm"), "realm", ""); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_string_new(_("Exposure"), "exposure_level", tmp?tmp: EXPOSE_REALMVIS); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - option = purple_account_option_string_new(_("Encoding"), "encoding", ZEPHYR_FALLBACK_CHARSET); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); - - my_protocol = plugin; - zephyr_register_slash_commands(); -} - -PURPLE_INIT_PLUGIN(zephyr, init_plugin, info); diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/zephyr_err.c --- a/libpurple/protocols/zephyr/zephyr_err.c Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,58 +0,0 @@ -#include "zephyr_err.h" - -#ifdef __STDC__ -#define NOARGS void -#else -#define NOARGS -#define const -#endif - -static const char * const text[] = { - "Packet too long or buffer too small", - "Notice header too large", - "Illegal value in notice", - "Can't get host manager port", - "Can't assign port", - "Bad packet format", - "Incompatible version numbers", - "No port opened", - "No notices match criteria", - "Input queue too long", - "Hostmanager not responding", - "Internal error", - "No previous call to ZLocateUser", - "No more locations available", - "Field too long for buffer", - "Improperly formatted field", - "SERVNAK received", - "Server could not verify authentication", - "Not logged-in", - "No previous call to ZRetrieveSubscriptions", - "No more subscriptions available", - "Too many subscriptions to transmit", - "End of file detected during read", - 0 -}; - -struct error_table { - char const * const * msgs; - long base; - int n_msgs; -}; -struct et_list { - struct et_list *next; - const struct error_table * table; -}; -extern struct et_list *_et_list; - -static const struct error_table et = { text, -772103680L, 23 }; - -static struct et_list link = { 0, 0 }; - -void initialize_zeph_error_table (NOARGS) { - if (!link.table) { - link.next = _et_list; - link.table = &et; - _et_list = &link; - } -} diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/zephyr_err.h --- a/libpurple/protocols/zephyr/zephyr_err.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ -/* - * zephyr_err.h: - * This file is automatically generated; please do not edit it. - */ -#define ZERR_PKTLEN (-772103680L) -#define ZERR_HEADERLEN (-772103679L) -#define ZERR_ILLVAL (-772103678L) -#define ZERR_HMPORT (-772103677L) -#define ZERR_PORTINUSE (-772103676L) -#define ZERR_BADPKT (-772103675L) -#define ZERR_VERS (-772103674L) -#define ZERR_NOPORT (-772103673L) -#define ZERR_NONOTICE (-772103672L) -#define ZERR_QLEN (-772103671L) -#define ZERR_HMDEAD (-772103670L) -#define ZERR_INTERNAL (-772103669L) -#define ZERR_NOLOCATIONS (-772103668L) -#define ZERR_NOMORELOCS (-772103667L) -#define ZERR_FIELDLEN (-772103666L) -#define ZERR_BADFIELD (-772103665L) -#define ZERR_SERVNAK (-772103664L) -#define ZERR_AUTHFAIL (-772103663L) -#define ZERR_LOGINFAIL (-772103662L) -#define ZERR_NOSUBSCRIPTIONS (-772103661L) -#define ZERR_NOMORESUBSCRIPTIONS (-772103660L) -#define ZERR_TOOMANYSUBS (-772103659L) -#define ZERR_EOF (-772103658L) -void initialize_zeph_error_table (void); -#define ERROR_TABLE_BASE_zeph (-772103680L) - -/* for compatibility with older versions... */ -#define init_zeph_err_tbl initialize_zeph_error_table -#define zeph_err_base ERROR_TABLE_BASE_zeph diff -r b9cf92c8b16b -r 50facee54d1d libpurple/protocols/zephyr/zephyr_internal.h --- a/libpurple/protocols/zephyr/zephyr_internal.h Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,340 +0,0 @@ -/* This file is part of the Project Athena Zephyr Notification System. - * It contains global definitions - * - * Created by: Robert French - * - * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of - * Technology. For copying and distribution information, see the - * file "mit-copyright.h". - */ - -#ifndef __ZEPHYR_H__ -#define __ZEPHYR_H__ - -#include - -#include - -#include -#include - -#include - -#ifndef IPPROTO_MAX /* Make sure not already included */ -#ifndef WIN32 -#include -#endif -#endif - -/* Use __STDC__ to guess whether we can use stdarg, prototypes, and const. - * This is a public header file, so autoconf can't help us here. */ -#ifdef __STDC__ -# include -# define ZP(x) x -# define ZCONST const -#else -# define ZP(x) () -# define ZCONST -#endif - -#ifdef WIN32 -/* this really should be uint32_t */ -/*typedef unsigned int in_addr_t; -struct in_addr -{ - in_addr_t s_addr; -}; */ -#include -#endif - -/* Service names */ -#define HM_SVCNAME "zephyr-hm" -#define HM_SRV_SVCNAME "zephyr-hm-srv" -#define SERVER_SVCNAME "zephyr-clt" -#define SERVER_SERVICE "zephyr" -#define SERVER_INSTANCE "zephyr" - -#define ZVERSIONHDR "ZEPH" -#define ZVERSIONMAJOR 0 -#define ZVERSIONMINOR 2 - -#define Z_MAXPKTLEN 1024 -#define Z_MAXHEADERLEN 800 -#define Z_MAXOTHERFIELDS 10 /* Max unknown fields in ZNotice_t */ -#define Z_NUMFIELDS 17 - -/* Authentication levels returned by ZCheckAuthentication */ -#define ZAUTH_FAILED (-1) -#define ZAUTH_YES 1 -#define ZAUTH_NO 0 - -typedef char ZPacket_t[Z_MAXPKTLEN]; - -/* Packet type */ -typedef enum { - UNSAFE, UNACKED, ACKED, HMACK, HMCTL, SERVACK, SERVNAK, CLIENTACK, STAT -} ZNotice_Kind_t; -extern ZCONST char *ZNoticeKinds[9]; - -/* Unique ID format */ -typedef struct _ZUnique_Id_t { - struct in_addr zuid_addr; - struct timeval tv; -} ZUnique_Id_t; - -/* Checksum */ -typedef unsigned long ZChecksum_t; - -/* Notice definition */ -typedef struct _ZNotice_t { - char *z_packet; - char *z_version; - ZNotice_Kind_t z_kind; - ZUnique_Id_t z_uid; -#define z_sender_addr z_uid.zuid_addr - struct timeval z_time; - unsigned short z_port; - int z_auth; - int z_checked_auth; - int z_authent_len; - char *z_ascii_authent; - char *z_class; - const char *z_class_inst; - char *z_opcode; - char *z_sender; - const char *z_recipient; - char *z_default_format; - char *z_multinotice; - ZUnique_Id_t z_multiuid; - ZChecksum_t z_checksum; - int z_num_other_fields; - char *z_other_fields[Z_MAXOTHERFIELDS]; - caddr_t z_message; - int z_message_len; -} ZNotice_t; - -/* Subscription structure */ -typedef struct _ZSubscriptions_t { - char *zsub_recipient; - char *zsub_class; - char *zsub_classinst; -} ZSubscription_t; - -/* Function return code */ -typedef int Code_t; - -/* Locations structure */ -typedef struct _ZLocations_t { - char *host; - char *time; - char *tty; -} ZLocations_t; - -typedef struct _ZAsyncLocateData_t { - char *user; - ZUnique_Id_t uid; - char *version; -} ZAsyncLocateData_t; - -/* for ZSetDebug */ -#ifdef Z_DEBUG -void (*__Z_debug_print) ZP((ZCONST char *fmt, va_list args, void *closure)); -void *__Z_debug_print_closure; -#endif - -int ZCompareUIDPred ZP((ZNotice_t *, void *)); -int ZCompareMultiUIDPred ZP((ZNotice_t *, void *)); - -/* Defines for ZFormatNotice, et al. */ -typedef Code_t (*Z_AuthProc) ZP((ZNotice_t*, char *, int, int *)); -typedef Code_t (*Z_SendProc) ZP((ZNotice_t *, char *, int, int)); -Code_t ZMakeAuthentication ZP((ZNotice_t*, char *,int, int*)); - -char *ZGetSender ZP((void)); -char *ZGetVariable ZP((char *)); -Code_t ZSetVariable ZP((char *var, char *value)); -Code_t ZUnsetVariable ZP((char *var)); -int ZGetWGPort ZP((void)); -Code_t ZSetDestAddr ZP((struct sockaddr_in *)); -Code_t ZFormatNoticeList ZP((ZNotice_t*, char**, int, - char **, int*, Z_AuthProc)); -Code_t ZParseNotice ZP((char*, int, ZNotice_t *)); -Code_t ZReadAscii ZP((char*, int, unsigned char*, int)); -Code_t ZReadAscii32 ZP((char *, int, unsigned long *)); -Code_t ZReadAscii16 ZP((char *, int, unsigned short *)); -Code_t ZSendPacket ZP((char*, int, int)); -Code_t ZSendList ZP((ZNotice_t*, char *[], int, Z_AuthProc)); -Code_t ZSrvSendList ZP((ZNotice_t*, char*[], int, Z_AuthProc, Z_SendProc)); -Code_t ZSendNotice ZP((ZNotice_t *, Z_AuthProc)); -Code_t ZSrvSendNotice ZP((ZNotice_t*, Z_AuthProc, Z_SendProc)); -Code_t ZFormatNotice ZP((ZNotice_t*, char**, int*, Z_AuthProc)); -Code_t ZFormatSmallNotice ZP((ZNotice_t*, ZPacket_t, int*, Z_AuthProc)); -Code_t ZFormatRawNoticeList ZP((ZNotice_t *notice, char *list[], int nitems, - char **buffer, int *ret_len)); -Code_t ZLocateUser ZP((char *, int *, Z_AuthProc)); -Code_t ZRequestLocations ZP((const char *, ZAsyncLocateData_t *, - ZNotice_Kind_t, Z_AuthProc)); -Code_t ZhmStat ZP((struct in_addr *, ZNotice_t *)); -Code_t ZInitialize ZP((void)); -Code_t ZSetServerState ZP((int)); -Code_t ZSetFD ZP((int)); -Code_t ZFormatSmallRawNotice ZP((ZNotice_t*, ZPacket_t, int*)); -int ZCompareUID ZP((ZUnique_Id_t*, ZUnique_Id_t*)); -Code_t ZMakeAscii ZP((char*, int, unsigned char*, int)); -Code_t ZMakeAscii32 ZP((char *, int, unsigned long)); -Code_t ZMakeAscii16 ZP((char *, int, unsigned int)); -Code_t ZReceivePacket ZP((ZPacket_t, int*, struct sockaddr_in*)); -Code_t ZCheckAuthentication ZP((ZNotice_t*, struct sockaddr_in*)); -Code_t ZSetLocation ZP((char *exposure)); -Code_t ZUnsetLocation ZP((void)); -Code_t ZFlushMyLocations ZP((void)); -Code_t ZFormatRawNotice ZP((ZNotice_t *, char**, int *)); -Code_t ZRetrieveSubscriptions ZP((unsigned short, int*)); -Code_t ZOpenPort ZP((unsigned short *port)); -Code_t ZClosePort ZP((void)); -Code_t ZFlushLocations ZP((void)); -Code_t ZFlushSubscriptions ZP((void)); -Code_t ZFreeNotice ZP((ZNotice_t *notice)); -Code_t ZParseLocations ZP((register ZNotice_t *notice, - register ZAsyncLocateData_t *zald, int *nlocs, - char **user)); -int ZCompareALDPred ZP((ZNotice_t *notice, void *zald)); -void ZFreeALD ZP((register ZAsyncLocateData_t *zald)); -Code_t ZCheckIfNotice ZP((ZNotice_t *notice, struct sockaddr_in *from, - register int (*predicate) ZP((ZNotice_t *,void *)), - void *args)); -Code_t ZPeekPacket ZP((char **buffer, int *ret_len, - struct sockaddr_in *from)); -Code_t ZPeekNotice ZP((ZNotice_t *notice, struct sockaddr_in *from)); -Code_t ZIfNotice ZP((ZNotice_t *notice, struct sockaddr_in *from, - int (*predicate) ZP((ZNotice_t *, void *)), void *args)); -Code_t ZSubscribeTo ZP((ZSubscription_t *sublist, int nitems, - unsigned int port)); -Code_t ZSubscribeToSansDefaults ZP((ZSubscription_t *sublist, int nitems, - unsigned int port)); -Code_t ZUnsubscribeTo ZP((ZSubscription_t *sublist, int nitems, - unsigned int port)); -Code_t ZCancelSubscriptions ZP((unsigned int port)); -int ZPending ZP((void)); -Code_t ZReceiveNotice ZP((ZNotice_t *notice, struct sockaddr_in *from)); -#ifdef Z_DEBUG -void Z_debug ZP((ZCONST char *, ...)); -#endif - -#undef ZP - -/* Compatibility */ -#define ZNewLocateUser ZLocateUser - -/* Macros to retrieve Zephyr library values. */ -extern int __Zephyr_fd; -extern int __Q_CompleteLength; -extern struct sockaddr_in __HM_addr; -extern char __Zephyr_realm[]; -#define ZGetFD() __Zephyr_fd -#define ZQLength() __Q_CompleteLength -#define ZGetDestAddr() __HM_addr -#define ZGetRealm() __Zephyr_realm - -#ifdef Z_DEBUG -void ZSetDebug ZP((void (*)(ZCONST char *, va_list, void *), void *)); -#define ZSetDebug(proc,closure) (__Z_debug_print=(proc), \ - __Z_debug_print_closure=(closure), \ - (void) 0) -#else -#define ZSetDebug(proc,closure) -#endif - -/* Maximum queue length */ -#define Z_MAXQLEN 30 - -/* Successful function return */ -#define ZERR_NONE 0 - -/* Hostmanager wait time (in secs) */ -#define HM_TIMEOUT 1 - -/* Server wait time (in secs) */ -#define SRV_TIMEOUT 30 - -#define ZAUTH (ZMakeAuthentication) -#define ZNOAUTH ((Z_AuthProc)0) - -/* Packet strings */ -#define ZSRVACK_SENT "SENT" /* SERVACK codes */ -#define ZSRVACK_NOTSENT "LOST" -#define ZSRVACK_FAIL "FAIL" - -/* Server internal class */ -#define ZEPHYR_ADMIN_CLASS "ZEPHYR_ADMIN" /* Class */ - -/* Control codes sent to a server */ -#define ZEPHYR_CTL_CLASS "ZEPHYR_CTL" /* Class */ - -#define ZEPHYR_CTL_CLIENT "CLIENT" /* Inst: From client */ -#define CLIENT_SUBSCRIBE "SUBSCRIBE" /* Opcode: Subscribe */ -#define CLIENT_SUBSCRIBE_NODEFS "SUBSCRIBE_NODEFS" /* Opcode: Subscribe */ -#define CLIENT_UNSUBSCRIBE "UNSUBSCRIBE" /* Opcode: Unsubsubscribe */ -#define CLIENT_CANCELSUB "CLEARSUB" /* Opcode: Clear all subs */ -#define CLIENT_GIMMESUBS "GIMME" /* Opcode: Give me subs */ -#define CLIENT_GIMMEDEFS "GIMMEDEFS" /* Opcode: Give me default - * subscriptions */ - -#define ZEPHYR_CTL_HM "HM" /* Inst: From HM */ -#define HM_BOOT "BOOT" /* Opcode: Boot msg */ -#define HM_FLUSH "FLUSH" /* Opcode: Flush me */ -#define HM_DETACH "DETACH" /* Opcode: Detach me */ -#define HM_ATTACH "ATTACH" /* Opcode: Attach me */ - -/* Control codes send to a HostManager */ -#define HM_CTL_CLASS "HM_CTL" /* Class */ - -#define HM_CTL_SERVER "SERVER" /* Inst: From server */ -#define SERVER_SHUTDOWN "SHUTDOWN" /* Opcode: Server shutdown */ -#define SERVER_PING "PING" /* Opcode: PING */ - -#define HM_CTL_CLIENT "CLIENT" /* Inst: From client */ -#define CLIENT_FLUSH "FLUSH" /* Opcode: Send flush to srv */ -#define CLIENT_NEW_SERVER "NEWSERV" /* Opcode: Find new server */ - -/* HM Statistics */ -#define HM_STAT_CLASS "HM_STAT" /* Class */ - -#define HM_STAT_CLIENT "HMST_CLIENT" /* Inst: From client */ -#define HM_GIMMESTATS "GIMMESTATS" /* Opcode: get stats */ - -/* Login class messages */ -#define LOGIN_CLASS "LOGIN" /* Class */ - -/* Class Instance is principal of user who is logging in or logging out */ - -#define EXPOSE_NONE "NONE" /* Opcode: Not visible */ -#define EXPOSE_OPSTAFF "OPSTAFF" /* Opcode: Opstaff visible */ -#define EXPOSE_REALMVIS "REALM-VISIBLE" /* Opcode: Realm visible */ -#define EXPOSE_REALMANN "REALM-ANNOUNCED"/* Opcode: Realm announced */ -#define EXPOSE_NETVIS "NET-VISIBLE" /* Opcode: Net visible */ -#define EXPOSE_NETANN "NET-ANNOUNCED" /* Opcode: Net announced */ -#define LOGIN_USER_LOGIN "USER_LOGIN" /* Opcode: user login - (from server) */ -#define LOGIN_USER_LOGOUT "USER_LOGOUT" /* Opcode: User logout */ -#define LOGIN_USER_FLUSH "USER_FLUSH" /* Opcode: flush all locs */ - -/* Locate class messages */ -#define LOCATE_CLASS "USER_LOCATE" /* Class */ - -#define LOCATE_HIDE "USER_HIDE" /* Opcode: Hide me */ -#define LOCATE_UNHIDE "USER_UNHIDE" /* Opcode: Unhide me */ - -/* Class Instance is principal of user to locate */ -#define LOCATE_LOCATE "LOCATE" /* Opcode: Locate user */ - -/* WG_CTL class messages */ -#define WG_CTL_CLASS "WG_CTL" /* Class */ - -#define WG_CTL_USER "USER" /* Inst: User request */ -#define USER_REREAD "REREAD" /* Opcode: Reread desc file */ -#define USER_SHUTDOWN "SHUTDOWN" /* Opcode: Go catatonic */ -#define USER_STARTUP "STARTUP" /* Opcode: Come out of it */ -#define USER_EXIT "EXIT" /* Opcode: Exit the client */ - -#endif /* __ZEPHYR_H__ */ diff -r b9cf92c8b16b -r 50facee54d1d libpurple/win32/global.mak --- a/libpurple/win32/global.mak Wed Jun 04 23:12:27 2025 -0500 +++ b/libpurple/win32/global.mak Wed Jun 04 23:47:08 2025 -0500 @@ -19,10 +19,8 @@ GTK_TOP ?= $(WIN32_DEV_TOP)/gtk_2_0-2.14 GTK_BIN ?= $(GTK_TOP)/bin LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.9.2_daa1 -MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2_daa3 NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.24-nspr-4.12 PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl-5.20.1.1 -SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.12 GSTREAMER_TOP ?= $(WIN32_DEV_TOP)/gstreamer-0.10.13 GCC_SSP_TOP ?= $(shell dirname $(shell which $(CC))) CYRUS_SASL_TOP ?= $(WIN32_DEV_TOP)/cyrus-sasl-2.1.26_daa1 diff -r b9cf92c8b16b -r 50facee54d1d pidgin.apspec.in --- a/pidgin.apspec.in Wed Jun 04 23:12:27 2025 -0500 +++ b/pidgin.apspec.in Wed Jun 04 23:47:08 2025 -0500 @@ -14,8 +14,7 @@ [Description] Pidgin allows you to talk to anyone using a variety of messaging protocols, -including IRC, XMPP, -Gadu-Gadu, and Zephyr. These protocols are implemented using a +including IRC and XMPP. These protocols are implemented using a modular, easy to use design. To use a protocol, just add an account using the account editor. diff -r b9cf92c8b16b -r 50facee54d1d pidgin.spec.in --- a/pidgin.spec.in Wed Jun 04 23:12:27 2025 -0500 +++ b/pidgin.spec.in Wed Jun 04 23:47:08 2025 -0500 @@ -34,10 +34,8 @@ %{!?_without_startupnotification:BuildRequires: startup-notification-devel} %{?_with_avahi:BuildRequires: avahi-glib-devel} %{!?_without_gtkspell:BuildRequires: gtkspell-devel} -%{?_with_meanwhile:BuildRequires: meanwhile-devel} %{?_with_mono:BuildRequires: mono-devel} %{?_with_sasl:BuildRequires: cyrus-sasl-devel >= 2} -%{!?_without_silc:BuildRequires: /usr/include/silc/silcclient.h} %{!?_without_tcl:BuildRequires: tcl, tk, /usr/include/tcl.h} %{!?_without_text:BuildRequires: ncurses-devel} %{!?_without_nm:BuildRequires: NetworkManager-devel} @@ -131,13 +129,6 @@ Requires: libpurple >= %{apiver} %endif -%if 0%{?_with_meanwhile:1} -%package -n libpurple-meanwhile -Summary: Lotus Sametime plugin for Pidgin using the Meanwhile library -Group: Applications/Internet -Requires: libpurple >= %{apiver} -%endif - %if 0%{?_with_mono:1} %package -n libpurple-mono Summary: Mono .NET plugin support for Pidgin @@ -161,11 +152,9 @@ %description Pidgin allows you to talk to anyone using a variety of messaging -protocols including XMPP, Bonjour, Gadu-Gadu, -IRC, Novell Groupwise, QQ, Lotus Sametime, SILC, Simple and -Zephyr. These protocols are implemented using a modular, easy to -use design. To use a protocol, just add an account using the -account editor. +protocols including IRC and XMPP. These protocols are implemented +using a modular, easy to use design. To use a protocol, just add +an account using the account editor. Pidgin supports many common features of other clients, as well as many unique features, such as perl scripting, TCL scripting and C plugins. @@ -179,9 +168,7 @@ libpurple contains the core IM support for IM clients such as Pidgin and Finch. -libpurple supports a variety of messaging protocols including -XMPP, Bonjour, Gadu-Gadu, IRC, Novell Groupwise, QQ, -Lotus Sametime, SILC, Simple and Zephyr. +libpurple supports a variety of messaging protocols including IRC and XMPP. %description -n libpurple-devel The libpurple-devel package contains the header files, developer @@ -193,11 +180,6 @@ Bonjour plugin for Pidgin. %endif -%if 0%{?_with_meanwhile:1} -%description -n libpurple-meanwhile -Lotus Sametime plugin for Pidgin using the Meanwhile library. -%endif - %if 0%{?_with_mono:1} %description -n libpurple-mono Mono plugin loader for Pidgin. This package will allow you to write or @@ -233,7 +215,6 @@ %{!?_with_vv:--disable-vv} \ %{!?_with_dbus:--disable-dbus} \ %{!?_with_avahi:--disable-avahi} \ - %{!?_with_meanwhile:--disable-meanwhile} \ %{?_without_gstreamer:--disable-gstreamer} \ %{?_without_gtkspell:--disable-gtkspell} \ %{?_without_nm:--disable-nm} \ @@ -268,19 +249,11 @@ rm -f $RPM_BUILD_ROOT%{_libdir}/purple-2/libbonjour.so %endif -%if 0%{!?_with_meanwhile:1} -rm -f $RPM_BUILD_ROOT%{_libdir}/purple-2/libsametime.so -%endif - %if 0%{!?_with_mono:1} rm -f $RPM_BUILD_ROOT%{_libdir}/purple-2/mono.so rm -f $RPM_BUILD_ROOT%{_libdir}/purple-2/*.dll %endif -%if 0%{?_without_silc:1} -rm -f $RPM_BUILD_ROOT%{_libdir}/purple-2/libsilcpurple.so -%endif - %if 0%{?_without_tcl:1} rm -f $RPM_BUILD_ROOT%{_libdir}/purple-2/tcl.so %endif @@ -295,7 +268,6 @@ find $RPM_BUILD_ROOT%{_libdir}/purple-2 -xtype f -print | \ sed "s@^$RPM_BUILD_ROOT@@g" | \ grep -v /libbonjour.so | \ - grep -v /libsametime.so | \ grep -v /mono.so | \ grep -v ".dll$" > %{name}-%{version}-purpleplugins @@ -431,13 +403,6 @@ %{_libdir}/purple-2/libbonjour.* %endif -%if 0%{?_with_meanwhile:1} -%files -n libpurple-meanwhile -%defattr(-, root, root) - -%{_libdir}/purple-2/libsametime.* -%endif - %if 0%{?_with_mono:1} %files -n libpurple-mono %defattr(-, root, root) diff -r b9cf92c8b16b -r 50facee54d1d pidgin/data/pidgin.desktop.in.in --- a/pidgin/data/pidgin.desktop.in.in Wed Jun 04 23:12:27 2025 -0500 +++ b/pidgin/data/pidgin.desktop.in.in Wed Jun 04 23:47:08 2025 -0500 @@ -4,7 +4,7 @@ _X-GNOME-FullName=Pidgin Internet Messenger _Comment=Chat over IM. Supports XMPP, IRC, and more # TRANSLATORS: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! -_Keywords=chat;talk;im;message;bonjour;gadu-gadu;irc;groupwise;jabber;sametime;silc;simple;xmpp;zephyr +_Keywords=chat;talk;im;message;bonjour;irc;jabber;simple;xmpp; Exec=pidgin Icon=pidgin StartupNotify=true diff -r b9cf92c8b16b -r 50facee54d1d pidgin/gtkdialogs.c --- a/pidgin/gtkdialogs.c Wed Jun 04 23:12:27 2025 -0500 +++ b/pidgin/gtkdialogs.c Wed Jun 04 23:47:08 2025 -0500 @@ -642,12 +642,6 @@ #endif #endif -#ifdef HAVE_LIBGADU - g_string_append(str, " Gadu-Gadu library (libgadu): External
"); -#else - g_string_append(str, " Gadu-Gadu library (libgadu): Internal
"); -#endif - #ifdef USE_GTKSPELL g_string_append(str, " GtkSpell: Enabled
"); #else @@ -732,17 +726,6 @@ g_string_append(str, " XScreenSaver: Disabled
"); #endif -#ifdef LIBZEPHYR_EXT - g_string_append(str, " Zephyr library (libzephyr): External
"); -#else - g_string_append(str, " Zephyr library (libzephyr): Internal
"); -#endif - -#ifdef ZEPHYR_USES_KERBEROS - g_string_append(str, " Zephyr uses Kerberos: Yes
"); -#else - g_string_append(str, " Zephyr uses Kerberos: No
"); -#endif #endif /* End of not to be translated section */ diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/Makefile.am --- a/pidgin/pixmaps/Makefile.am Wed Jun 04 23:12:27 2025 -0500 +++ b/pidgin/pixmaps/Makefile.am Wed Jun 04 23:47:08 2025 -0500 @@ -202,27 +202,17 @@ PROTOCOLS_16_SCALABLE = \ protocols/16/scalable/bonjour.svg \ - protocols/16/scalable/gadu-gadu.svg \ - protocols/16/scalable/novell.svg \ protocols/16/scalable/irc.svg \ protocols/16/scalable/jabber.svg \ - protocols/16/scalable/meanwhile.svg \ - protocols/16/scalable/silc.svg \ - protocols/16/scalable/simple.svg \ - protocols/16/scalable/zephyr.svg + protocols/16/scalable/simple.svg PROTOCOLS_16 = \ protocols/16/bonjour.png \ protocols/16/facebook.png \ - protocols/16/gadu-gadu.png \ protocols/16/google-talk.png \ - protocols/16/novell.png \ protocols/16/irc.png \ protocols/16/jabber.png \ - protocols/16/meanwhile.png \ - protocols/16/silc.png \ - protocols/16/simple.png \ - protocols/16/zephyr.png + protocols/16/simple.png ICONS_16_SCALABLE = \ icons/hicolor/16x16/apps/scalable/pidgin.svg @@ -247,51 +237,31 @@ PROTOCOLS_22_SCALABLE = \ protocols/22/scalable/bonjour.svg \ - protocols/22/scalable/gadu-gadu.svg \ - protocols/22/scalable/novell.svg \ protocols/22/scalable/irc.svg \ protocols/22/scalable/jabber.svg \ - protocols/22/scalable/meanwhile.svg \ - protocols/22/scalable/silc.svg \ - protocols/22/scalable/simple.svg \ - protocols/22/scalable/zephyr.svg + protocols/22/scalable/simple.svg PROTOCOLS_22 = \ protocols/22/bonjour.png \ protocols/22/facebook.png \ - protocols/22/gadu-gadu.png \ protocols/22/google-talk.png \ - protocols/22/novell.png \ protocols/22/irc.png \ protocols/22/jabber.png \ - protocols/22/meanwhile.png \ - protocols/22/silc.png \ - protocols/22/simple.png \ - protocols/22/zephyr.png + protocols/22/simple.png PROTOCOLS_48 = \ protocols/48/bonjour.png \ protocols/48/facebook.png \ - protocols/48/gadu-gadu.png \ - protocols/48/novell.png \ protocols/48/irc.png \ protocols/48/jabber.png \ - protocols/48/meanwhile.png \ - protocols/48/silc.png \ - protocols/48/simple.png \ - protocols/48/zephyr.png + protocols/48/simple.png PROTOCOLS_SCALABLE = \ protocols/scalable/bonjour.svg \ - protocols/scalable/gadu-gadu.svg \ protocols/scalable/google-talk.svg \ - protocols/scalable/novell.svg \ protocols/scalable/irc.svg \ protocols/scalable/jabber.svg \ - protocols/scalable/meanwhile.svg \ - protocols/scalable/silc.svg \ - protocols/scalable/simple.svg \ - protocols/scalable/zephyr.svg + protocols/scalable/simple.svg STATUS_11 = \ status/11/available.png \ diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/16/gadu-gadu.png Binary file pidgin/pixmaps/protocols/16/gadu-gadu.png has changed diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/16/meanwhile.png Binary file pidgin/pixmaps/protocols/16/meanwhile.png has changed diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/16/novell.png Binary file pidgin/pixmaps/protocols/16/novell.png has changed diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/16/scalable/gadu-gadu.svg --- a/pidgin/pixmaps/protocols/16/scalable/gadu-gadu.svg Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,365 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/16/scalable/meanwhile.svg --- a/pidgin/pixmaps/protocols/16/scalable/meanwhile.svg Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,218 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/16/scalable/novell.svg --- a/pidgin/pixmaps/protocols/16/scalable/novell.svg Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,163 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/16/scalable/silc.svg --- a/pidgin/pixmaps/protocols/16/scalable/silc.svg Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,173 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/16/scalable/zephyr.svg --- a/pidgin/pixmaps/protocols/16/scalable/zephyr.svg Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,189 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/16/silc.png Binary file pidgin/pixmaps/protocols/16/silc.png has changed diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/16/zephyr.png Binary file pidgin/pixmaps/protocols/16/zephyr.png has changed diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/22/gadu-gadu.png Binary file pidgin/pixmaps/protocols/22/gadu-gadu.png has changed diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/22/meanwhile.png Binary file pidgin/pixmaps/protocols/22/meanwhile.png has changed diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/22/novell.png Binary file pidgin/pixmaps/protocols/22/novell.png has changed diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/22/scalable/gadu-gadu.svg --- a/pidgin/pixmaps/protocols/22/scalable/gadu-gadu.svg Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,206 +0,0 @@ - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/22/scalable/meanwhile.svg --- a/pidgin/pixmaps/protocols/22/scalable/meanwhile.svg Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,172 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/22/scalable/novell.svg --- a/pidgin/pixmaps/protocols/22/scalable/novell.svg Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/22/scalable/silc.svg --- a/pidgin/pixmaps/protocols/22/scalable/silc.svg Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,139 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/22/scalable/zephyr.svg --- a/pidgin/pixmaps/protocols/22/scalable/zephyr.svg Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,212 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/22/silc.png Binary file pidgin/pixmaps/protocols/22/silc.png has changed diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/22/zephyr.png Binary file pidgin/pixmaps/protocols/22/zephyr.png has changed diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/48/gadu-gadu.png Binary file pidgin/pixmaps/protocols/48/gadu-gadu.png has changed diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/48/meanwhile.png Binary file pidgin/pixmaps/protocols/48/meanwhile.png has changed diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/48/novell.png Binary file pidgin/pixmaps/protocols/48/novell.png has changed diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/48/silc.png Binary file pidgin/pixmaps/protocols/48/silc.png has changed diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/48/zephyr.png Binary file pidgin/pixmaps/protocols/48/zephyr.png has changed diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/scalable/gadu-gadu.svg --- a/pidgin/pixmaps/protocols/scalable/gadu-gadu.svg Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,215 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/scalable/meanwhile.svg --- a/pidgin/pixmaps/protocols/scalable/meanwhile.svg Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,174 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/scalable/novell.svg --- a/pidgin/pixmaps/protocols/scalable/novell.svg Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,167 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/scalable/silc.svg --- a/pidgin/pixmaps/protocols/scalable/silc.svg Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,153 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - diff -r b9cf92c8b16b -r 50facee54d1d pidgin/pixmaps/protocols/scalable/zephyr.svg --- a/pidgin/pixmaps/protocols/scalable/zephyr.svg Wed Jun 04 23:12:27 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,141 +0,0 @@ - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - diff -r b9cf92c8b16b -r 50facee54d1d pidgin/plugins/disco/xmppdisco.c --- a/pidgin/plugins/disco/xmppdisco.c Wed Jun 04 23:12:27 2025 -0500 +++ b/pidgin/plugins/disco/xmppdisco.c Wed Jun 04 23:47:08 2025 -0500 @@ -250,8 +250,6 @@ const char *from; const char *to; } disco_type_mappings[] = { - { "gadu-gadu", "gadu-gadu" }, /* the prpl is prpl-gg, but list_icon returns "gadu-gadu" */ - { "sametime", "meanwhile" }, { "xmpp", "jabber" }, /* prpl-jabber (mentioned in case the prpl is renamed so this line will match) */ { NULL, NULL } }; diff -r b9cf92c8b16b -r 50facee54d1d pidgin/win32/nsis/pidgin-installer.nsi --- a/pidgin/win32/nsis/pidgin-installer.nsi Wed Jun 04 23:12:27 2025 -0500 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Wed Jun 04 23:47:08 2025 -0500 @@ -500,8 +500,6 @@ ; Remove any URI handlers ; I can't think of an easy way to maintain a list in a single place - Push "aim" - Call un.UnregisterURIHandler Push "xmpp" Call un.UnregisterURIHandler @@ -549,17 +547,9 @@ Delete "$INSTDIR\plugins\iconaway.dll" Delete "$INSTDIR\plugins\idle.dll" Delete "$INSTDIR\plugins\joinpart.dll" - Delete "$INSTDIR\plugins\libaim.dll" Delete "$INSTDIR\plugins\libbonjour.dll" - Delete "$INSTDIR\plugins\libgg.dll" - Delete "$INSTDIR\plugins\libicq.dll" Delete "$INSTDIR\plugins\libirc.dll" - Delete "$INSTDIR\plugins\libnapster.dll" - Delete "$INSTDIR\plugins\libnovell.dll" - Delete "$INSTDIR\plugins\libsametime.dll" - Delete "$INSTDIR\plugins\libsilc.dll" Delete "$INSTDIR\plugins\libsimple.dll" - Delete "$INSTDIR\plugins\libtoc.dll" Delete "$INSTDIR\plugins\libxmpp.dll" Delete "$INSTDIR\plugins\log_reader.dll" Delete "$INSTDIR\plugins\markerline.dll" @@ -619,18 +609,13 @@ Delete "$INSTDIR\libgcc_s_dw2-1.dll" Delete "$INSTDIR\libjabber.dll" Delete "$INSTDIR\libnspr4.dll" - Delete "$INSTDIR\libmeanwhile-1.dll" - Delete "$INSTDIR\liboscar.dll" Delete "$INSTDIR\libplc4.dll" Delete "$INSTDIR\libplds4.dll" Delete "$INSTDIR\libpurple.dll" Delete "$INSTDIR\libsasl2-3.dll" - Delete "$INSTDIR\libsilc-1-1-4.dll" - Delete "$INSTDIR\libsilcclient-1-1-4.dll" Delete "$INSTDIR\libssp-0.dll" Delete "$INSTDIR\libwinpthread-1.dll" Delete "$INSTDIR\libxml2-2.dll" - Delete "$INSTDIR\libymsg.dll" Delete "$INSTDIR\nss3.dll" Delete "$INSTDIR\nssutil3.dll" Delete "$INSTDIR\pidgin.dll" diff -r b9cf92c8b16b -r 50facee54d1d po/POTFILES.in --- a/po/POTFILES.in Wed Jun 04 23:12:27 2025 -0500 +++ b/po/POTFILES.in Wed Jun 04 23:47:08 2025 -0500 @@ -67,7 +67,6 @@ libpurple/protocols/bonjour/bonjour.h libpurple/protocols/bonjour/jabber.c libpurple/protocols/bonjour/mdns_dns_sd.c -libpurple/protocols/gg/gg.c libpurple/protocols/irc/cmds.c libpurple/protocols/irc/dcc_send.c libpurple/protocols/irc/irc.c @@ -94,27 +93,7 @@ libpurple/protocols/jabber/usermood.c libpurple/protocols/jabber/usernick.c libpurple/protocols/jabber/xdata.c -libpurple/protocols/novell/nmuser.c -libpurple/protocols/novell/novell.c -libpurple/protocols/sametime/sametime.c -libpurple/protocols/silc/buddy.c -libpurple/protocols/silc/chat.c -libpurple/protocols/silc/ft.c -libpurple/protocols/silc/ops.c -libpurple/protocols/silc/pk.c -libpurple/protocols/silc/silc.c -libpurple/protocols/silc/util.c -libpurple/protocols/silc/wb.c -libpurple/protocols/silc10/buddy.c -libpurple/protocols/silc10/chat.c -libpurple/protocols/silc10/ft.c -libpurple/protocols/silc10/ops.c -libpurple/protocols/silc10/pk.c -libpurple/protocols/silc10/silc.c -libpurple/protocols/silc10/util.c -libpurple/protocols/silc10/wb.c libpurple/protocols/simple/simple.c -libpurple/protocols/zephyr/zephyr.c libpurple/proxy.c libpurple/prpl.c libpurple/request.h