Mercurial > pidgin.yaz
changeset 29995:2292d8896b0b
merged with im.pidgin.pidgin
author | Yoshiki Yazawa <yaz@honeyplanet.jp> |
---|---|
date | Tue, 16 Mar 2010 12:07:06 +0900 |
parents | 10c2702ecfff (current diff) 6ca9aa923250 (diff) |
children | f988f25259c7 |
files | configure.ac libpurple/protocols/gg/lib/http.c libpurple/protocols/gg/lib/pubdir50.c libpurple/protocols/irc/irc.c libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/message.c libpurple/protocols/msnp9/Makefile.am libpurple/protocols/msnp9/Makefile.mingw libpurple/protocols/msnp9/cmdproc.c libpurple/protocols/msnp9/cmdproc.h libpurple/protocols/msnp9/command.c libpurple/protocols/msnp9/command.h libpurple/protocols/msnp9/dialog.c libpurple/protocols/msnp9/dialog.h libpurple/protocols/msnp9/directconn.c libpurple/protocols/msnp9/directconn.h libpurple/protocols/msnp9/error.c libpurple/protocols/msnp9/error.h libpurple/protocols/msnp9/group.c libpurple/protocols/msnp9/group.h libpurple/protocols/msnp9/history.c libpurple/protocols/msnp9/history.h libpurple/protocols/msnp9/httpconn.c libpurple/protocols/msnp9/httpconn.h libpurple/protocols/msnp9/msg.c libpurple/protocols/msnp9/msg.h libpurple/protocols/msnp9/msn-utils.c libpurple/protocols/msnp9/msn-utils.h libpurple/protocols/msnp9/msn.c libpurple/protocols/msnp9/msn.h libpurple/protocols/msnp9/nexus.c libpurple/protocols/msnp9/nexus.h libpurple/protocols/msnp9/notification.c libpurple/protocols/msnp9/notification.h libpurple/protocols/msnp9/object.c libpurple/protocols/msnp9/object.h libpurple/protocols/msnp9/page.c libpurple/protocols/msnp9/page.h libpurple/protocols/msnp9/servconn.c libpurple/protocols/msnp9/servconn.h libpurple/protocols/msnp9/session.c libpurple/protocols/msnp9/session.h libpurple/protocols/msnp9/slp.c libpurple/protocols/msnp9/slp.h libpurple/protocols/msnp9/slpcall.c libpurple/protocols/msnp9/slpcall.h libpurple/protocols/msnp9/slplink.c libpurple/protocols/msnp9/slplink.h libpurple/protocols/msnp9/slpmsg.c libpurple/protocols/msnp9/slpmsg.h libpurple/protocols/msnp9/slpsession.c libpurple/protocols/msnp9/slpsession.h libpurple/protocols/msnp9/state.c libpurple/protocols/msnp9/state.h libpurple/protocols/msnp9/switchboard.c libpurple/protocols/msnp9/switchboard.h libpurple/protocols/msnp9/sync.c libpurple/protocols/msnp9/sync.h libpurple/protocols/msnp9/table.c libpurple/protocols/msnp9/table.h libpurple/protocols/msnp9/transaction.c libpurple/protocols/msnp9/transaction.h libpurple/protocols/msnp9/user.c libpurple/protocols/msnp9/user.h libpurple/protocols/msnp9/userlist.c libpurple/protocols/msnp9/userlist.h libpurple/protocols/oscar/oscar.c libpurple/protocols/yahoo/libymsg.c pidgin/gtkblist.c pidgin/gtkimhtml.c |
diffstat | 157 files changed, 9857 insertions(+), 23770 deletions(-) [+] |
line wrap: on
line diff
--- a/ChangeLog Thu Mar 04 15:19:39 2010 +0900 +++ b/ChangeLog Tue Mar 16 12:07:06 2010 +0900 @@ -20,10 +20,39 @@ * The Recent Log Activity sort method for the Buddy List now distinguishes between no activity and a small amount of activity in the distant past. (Greg McNew) + * Added a menu set mood globally for all mood-supporting accounts + (currently XMPP and ICQ). Bonjour: * Added support for IPv6. (Thanks to T_X for testing) + Gadu-Gadu: + * Updated our bundled libgadu to 1.9.0-rc2 (many thanks to Krzysztof + Klinikowski for the work and testing put in here!) + * Minimum requirement for external libgadu is now also 1.9.0-rc2. + + ICQ: + * X-Status (Custom ICQ status icon) support. Since most of the icons + available reflect moods, this is labeled "Set Mood" on the Accounts->ICQ + Account menu. (Andrew Ivanov, Tomテ。ナ。 Kebert, Yuriy Yevgrafov, and trac + users bob007, salieff, and nops) + * Allow setting and displaying icons between 1x1 and 100x100 pixels. + Previously only icons between 48x48 and 52x64 were allowed. + + MSN: + * Support for version 9 of the MSN protocol has been removed. This + version is no longer supported on the servers. + + XMPP: + * Direct messages to a specific resource only upon receipt of a message + with content (as opposed to a typing notification, etc). (Thanks to + rjoly for testing) + + Yahoo: + * Attempt to better handle transparent proxies interfering with HTTP-based + login. + * Fix handling of P2P packets, thus fixing the loss of some messages. + version 2.6.6 (02/18/2010): libpurple: * Fix 'make check' on OS X. (David Fang)
--- a/ChangeLog.API Thu Mar 04 15:19:39 2010 +0900 +++ b/ChangeLog.API Tue Mar 16 12:07:06 2010 +0900 @@ -23,6 +23,7 @@ * ui-caps-changed media manager signal * sent-attention conversation signal * got-attention conversation signal + * PurpleMood struct in status.h Pidgin: Added:
--- a/ChangeLog.win32 Thu Mar 04 15:19:39 2010 +0900 +++ b/ChangeLog.win32 Tue Mar 16 12:07:06 2010 +0900 @@ -1,10 +1,14 @@ version 2.7.0 (??/??/????): + * Updated GTK+ to 2.16.6 + * Private GTK+ Runtime now used (GTK+ Installer no longer supported) * Minimum required GTK+ version increased to 2.14.0 - * Private GTK+ Runtime now used (GTK+ Installer no longer supported) * Win9x no longer supported. * Crash Report files (pidgin.RPT) are now generated in the ~/.purple directory instead of the installation directory. * NSS SSL Library upgraded to 3.12.5 (thanks to Berke Viktor) + * GtkSpell upgraded to 2.0.16, changing the spellchecking backend to + enchant. This means that myspell, hunspell (OpenOffice) and existing + aspell dictionaries can be used. version 2.6.6 (02/18/2010): * Installer translations for: Norwegian nynorsk
--- a/Makefile.mingw Thu Mar 04 15:19:39 2010 +0900 +++ b/Makefile.mingw Tue Mar 16 12:07:06 2010 +0900 @@ -31,7 +31,7 @@ exit; \ }' VERSION) -GTK_INSTALL_VERSION = 2.14.7.0 +GTK_INSTALL_VERSION = 2.16.6.0 STRIPPED_RELEASE_DIR = $(PIDGIN_TREE_TOP)/pidgin-$(PIDGIN_VERSION)-win32bin DEBUG_SYMBOLS_DIR = $(PIDGIN_TREE_TOP)/pidgin-$(PIDGIN_VERSION)-dbgsym @@ -41,11 +41,13 @@ # should be included in this list so they don't get stripped EXTERNAL_DLLS = \ comerr32.dll \ + exchndl.dll \ freebl3.dll \ gssapi32.dll \ k5sprt32.dll \ krb5_32.dll \ - libgtkspell.dll \ + libenchant.dll \ + libgtkspell-0.dll \ libmeanwhile-1.dll \ libnspr4.dll \ libplc4.dll \ @@ -88,29 +90,48 @@ endif $(MAKE) -C share/ca-certs -f $(MINGW_MAKEFILE) install $(MAKE) -C share/sounds -f $(MINGW_MAKEFILE) install + mkdir -p $(PIDGIN_INSTALL_DIR)/spellcheck + cp $(GTKSPELL_TOP)/bin/libgtkspell-0.dll $(PIDGIN_INSTALL_DIR)/spellcheck + cp $(ENCHANT_TOP)/bin/libenchant.dll $(PIDGIN_INSTALL_DIR)/spellcheck + cp -R $(ENCHANT_TOP)/lib $(PIDGIN_INSTALL_DIR)/spellcheck + cp $(WIN32_DEV_TOP)/pidgin-inst-deps-20100223/exchndl.dll $(PIDGIN_INSTALL_DIR) pidgin/win32/nsis/gtk-runtime-$(GTK_BUNDLE_VERSION).zip: pidgin/win32/nsis/generate_gtk_zip.sh `pwd` -generate_installer_includes: create_release_install_dir pidgin/win32/nsis/gtk-runtime-$(GTK_BUNDLE_VERSION).zip - rm -f pidgin/win32/nsis/pidgin-translations.nsh +generate_installer_includes: create_release_install_dir pidgin/win32/nsis/gtk-runtime-$(GTK_BUNDLE_VERSION).zip debug_symbols_zip + rm -f pidgin/win32/nsis/pidgin-translations.nsh pidgin/win32/nsis/pidgin-spellcheck.nsh pidgin/win32/nsis/pidgin-spellcheck-preselect.nsh find $(STRIPPED_RELEASE_DIR)/locale -maxdepth 1 -mindepth 1 \ -exec basename {} ';' \ | sed -e s/^/\!insertmacro\ LANG_SECTION\ \"/ -e s/$$/\"/ \ > pidgin/win32/nsis/pidgin-translations.nsh + #Convert the available.lst lines to "!insertmacro SPELLCHECK_SECTION lang lang_name lang_file" + sed -e "/^#/d" -e "s/^[^,]\{1,\},[^,]\{1,\},/\"/" \ + -e "s/,/\"\ \"/" -e "s/,/\"\ \"/" -e "s/[\ \t]*$$/\"/" \ + -e "s/^/\!insertmacro\ SPELLCHECK_SECTION\ /" \ + pidgin/win32/nsis/available.lst \ + > pidgin/win32/nsis/pidgin-spellcheck.nsh + #Convert the lines to "!insertmacro CHECK_SPELLCHECK_SECTION lang" + iconv -f latin1 -t utf-8 pidgin/win32/nsis/pidgin-spellcheck.nsh | \ + sed -e "s/SPELLCHECK_SECTION/CHECK_SPELLCHECK_SECTION/" \ + -e "s/ \"[^\"]*\"\ \"[^\"]*\"[\t\ ]*$$//" | \ + iconv -f utf-8 -t latin1 \ + > pidgin/win32/nsis/pidgin-spellcheck-preselect.nsh create_release_install_dir: install rm -rf $(STRIPPED_RELEASE_DIR) - cp -R $(PIDGIN_INSTALL_DIR) $(STRIPPED_RELEASE_DIR) + mkdir $(STRIPPED_RELEASE_DIR) + tar -cf - $(PIDGIN_INSTALL_DIR) --exclude=Gtk --exclude=spellcheck/share \ + | tar --strip 2 -xC $(STRIPPED_RELEASE_DIR) -f - find $(STRIPPED_RELEASE_DIR) \( -name '*.dll' -o -name '*.exe' \) \ -not \( -false $(EXTERNAL_DLLS_FIND_EXP) \) \ -exec $(STRIP) --strip-unneeded {} ';' -installer: create_release_install_dir generate_installer_includes +installer: generate_installer_includes $(MAKENSIS) $(MAKENSISOPT)V3 $(MAKENSISOPT)DPIDGIN_VERSION="$(PIDGIN_VERSION)" $(MAKENSISOPT)DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" $(MAKENSISOPT)DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" $(MAKENSISOPT)DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION).exe ./ -installer_offline: create_release_install_dir generate_installer_includes debug_symbols_zip +installer_offline: generate_installer_includes $(MAKENSIS) $(MAKENSISOPT)V3 $(MAKENSISOPT)DPIDGIN_VERSION="$(PIDGIN_VERSION)" $(MAKENSISOPT)DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" $(MAKENSISOPT)DOFFLINE_INSTALLER $(MAKENSISOPT)DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" $(MAKENSISOPT)DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION)-offline.exe ./
--- a/configure.ac Thu Mar 04 15:19:39 2010 +0900 +++ b/configure.ac Tue Mar 16 12:07:06 2010 +0900 @@ -72,8 +72,8 @@ ]) fi -AC_CANONICAL_SYSTEM -AM_CONFIG_HEADER(config.h) +AC_CANONICAL_HOST +AC_CONFIG_HEADERS([config.h]) AM_INIT_AUTOMAKE([1.9 -Wno-portability dist-bzip2]) PURPLE_MAJOR_VERSION=purple_major_version @@ -152,7 +152,7 @@ if test x$MSGFMT = xno -o x$MSGFMT$GMSGFMT$INTLTOOL_MSGFMT = x then - AC_ERROR([ + AC_MSG_ERROR([ The msgfmt command is required to build libpurple. If it is installed on your system, ensure that it is in your path. If it is not, install @@ -168,9 +168,6 @@ ]) fi -dnl we don't use autobreak on cygwin!! -dnl AC_CYGWIN - dnl Checks for header files. AC_HEADER_STDC AC_HEADER_SYS_WAIT @@ -198,11 +195,11 @@ dnl Check for inet_aton AC_CHECK_FUNC(inet_aton, , [AC_CHECK_LIB(resolv, inet_aton, , - [AC_ERROR(inet_aton not found)])]) + [AC_MSG_ERROR([inet_aton not found])])]) AC_CHECK_LIB(resolv, __res_query) AC_CHECK_LIB(nsl, gethostent) AC_CHECK_FUNC(socket, , - [AC_CHECK_LIB(socket, socket, , [AC_ERROR([socket not found])])]) + [AC_CHECK_LIB(socket, socket, , [AC_MSG_ERROR([socket not found])])]) dnl If all goes well, by this point the previous two checks will have dnl pulled in -lsocket and -lnsl if we need them. AC_CHECK_FUNC(getaddrinfo, @@ -214,19 +211,18 @@ AC_CHECK_FUNCS(getifaddrs) dnl Check for socklen_t (in Unix98) AC_MSG_CHECKING(for socklen_t) -AC_TRY_COMPILE([ +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <sys/types.h> #include <sys/socket.h> socklen_t x; -], [], -[ +]], [[]])], [ AC_MSG_RESULT(yes) ], [ - AC_TRY_COMPILE([ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <sys/types.h> #include <sys/socket.h> int accept(int, struct sockaddr *, size_t *); - ], [], [ + ]], [[]])], [ AC_MSG_RESULT(size_t) AC_DEFINE(socklen_t, size_t, [socklen_t size]) ], [ @@ -249,7 +245,7 @@ AC_CHECK_FUNC(dlopen, LIBDL="", [AC_CHECK_LIB(dl, dlopen, LIBDL="-ldl")]) AC_MSG_CHECKING(for fileno()) -AC_TRY_RUN([ +AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include <stdio.h> int main(int argc, char *argv[]) @@ -260,7 +256,7 @@ return !(fd > 0); } -], [ +]])], [ AC_MSG_RESULT(yes) AC_DEFINE([HAVE_FILENO], [1], [Define to 1 if your stdio has int fileno(FILE *).]) @@ -273,7 +269,7 @@ ]) AC_MSG_CHECKING(for the %z format string in strftime()) -AC_TRY_RUN([ +AC_RUN_IFELSE([AC_LANG_SOURCE([[ #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #endif @@ -297,7 +293,7 @@ (buf[4] >= '0' && buf[4] <= '9') ); } -], [ +]])], [ AC_MSG_RESULT(yes) AC_DEFINE([HAVE_STRFTIME_Z_FORMAT], [1], [Define to 1 if you have a strftime() that supports the %z format string.]) @@ -307,8 +303,7 @@ # Fallback for Cross Compiling... # This will enable the compatibility code. AC_MSG_RESULT(no) -] -) +]) dnl ####################################################################### dnl # Check for GLib 2.12 (required) @@ -458,10 +453,10 @@ if test "x$XSS_LIBS" != "x"; then oldCPPFLAGS="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $x_incpath_add" - AC_TRY_COMPILE([ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <X11/Xlib.h> #include <X11/extensions/scrnsaver.h> - ], [], [], [enable_screensaver=no]) + ]], [[]])], [], [enable_screensaver=no]) CPPFLAGS="$oldCPPFLAGS" else enable_screensaver=no @@ -648,14 +643,14 @@ f="$location/ncurses.h" AC_CHECK_HEADER($f,[ AC_MSG_CHECKING([if $f supports wide characters]) - AC_TRY_COMPILE([ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #define _XOPEN_SOURCE_EXTENDED #include <$f> - ], [ + ]], [[ #ifndef get_wch # error get_wch not found! #endif - ], [ + ]])], [ dir=$location if test x"$dir" != x"." ; then GNT_CFLAGS="-I$dir/" @@ -997,10 +992,10 @@ CPPFLAGS_save="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $SILC_CFLAGS" AC_MSG_CHECKING(for silcmime.h) - AC_TRY_COMPILE([ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <silcincludes.h> #include <silcmime.h> - ], [], [ + ]], [[]])], [ AC_MSG_RESULT(yes) AC_DEFINE(HAVE_SILCMIME_H, 1, [Define if we have silcmime.h]) ], [ @@ -1022,7 +1017,7 @@ gadu_manual_check="no" fi if test "x$gadu_manual_check" = "xno"; then - PKG_CHECK_MODULES(GADU, libgadu, [ + PKG_CHECK_MODULES(GADU, [libgadu >= 1.9.0-rc2], [ gadu_includes="yes" gadu_libs="yes" ], [ @@ -1049,14 +1044,33 @@ AC_MSG_CHECKING(for libgadu GPL compatibility) CPPFLAGS_save="$CPPFLAGS" CPPFLAGS="$CPPFLAGS $GADU_CFLAGS" - AC_TRY_COMPILE([#include <libgadu.h>], [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <libgadu.h>]], [[ #if defined(__GG_LIBGADU_HAVE_OPENSSL) || defined(GG_CONFIG_HAVE_OPENSSL) #error "libgadu is not compatible with the GPL when compiled with OpenSSL support." #endif - ], [ - AC_MSG_RESULT(yes) - AC_DEFINE([HAVE_LIBGADU], [1], - [Define to 1 if you have libgadu.]) + ]])], [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <libgadu.h>]], [[ +#if GG_DEFAULT_PROTOCOL_VERSION < 0x2e +#error "Your libgadu version is too old. libpurple requires 1.9.0-rc2 or higher." +#endif + ]])], [ + AC_MSG_RESULT(yes) + AC_DEFINE([HAVE_LIBGADU], [1], + [Define to 1 if you have libgadu.]) + ], [ + AC_MSG_RESULT(no) + echo + echo + echo "Your supplied copy of libgadu is too old." + echo "Install version 1.9.0-rc2 or newer." + echo "Then rerun this ./configure" + echo + echo "Falling back to using our own copy of libgadu" + echo + GADU_LIBS="" + GADU_CFLAGS="" + gadu_libs=no + ]) ], [ AC_MSG_RESULT(no) echo @@ -1077,12 +1091,13 @@ AM_CONDITIONAL(USE_INTERNAL_LIBGADU, test "x$gadu_libs" != "xyes") +if test "x$gadu_libs" = "x"; then + gadu_libs=no +fi + AC_SUBST(GADU_LIBS) AC_SUBST(GADU_CFLAGS) -# change the next line to not make MSNP15 the default (s/disable/enable/; s/yes/no/;) -AC_ARG_ENABLE(msnp15,[AC_HELP_STRING([--disable-msnp15], [Disable the newer MSNP15 protocol])],enable_msnp15=$enableval,enable_msnp15=yes) - AC_ARG_ENABLE(distrib,,,enable_distrib=no) AM_CONDITIONAL(DISTRIB, test "x$enable_distrib" = "xyes") DYNAMIC_PRPLS=all @@ -1100,9 +1115,6 @@ if test "x$avahiincludes" != "xyes" -o "x$avahilibs" != "xyes"; then STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/bonjour//'` fi -if test "x$enable_msnp15" != "xyes" ; then - STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/msn/msnp9/'` -fi if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then STATIC_PRPLS=`echo $STATIC_PRPLS | $sedpath 's/silc/silc10/'` fi @@ -1133,8 +1145,6 @@ 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" - elif test "x$i" = "xmsnp9"; then - STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/libmsn.la" else STATIC_LINK_LIBS="$STATIC_LINK_LIBS \$(top_builddir)/libpurple/protocols/$i/lib$i.la" fi @@ -1147,7 +1157,6 @@ irc) static_irc=yes ;; jabber) static_jabber=yes ;; msn) static_msn=yes ;; - msnp9) static_msn=yes ;; myspace) static_myspace=yes ;; mxit) static_mxit=yes ;; novell) static_novell=yes ;; @@ -1193,9 +1202,6 @@ if test "x$avahiincludes" != "xyes" -o "x$avahilibs" != "xyes"; then DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/bonjour//'` fi -if test "x$enable_msnp15" != "xyes" ; then - DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/msn/msnp9/'` -fi if test "x$silcincludes" != "xyes" -o "x$silcclient" != "xyes"; then DYNAMIC_PRPLS=`echo $DYNAMIC_PRPLS | $sedpath 's/silc/silc10/'` fi @@ -1210,7 +1216,6 @@ irc) dynamic_irc=yes ;; jabber) dynamic_jabber=yes ;; msn) dynamic_msn=yes ;; - msnp9) dynamic_msn=yes ;; myspace) dynamic_myspace=yes ;; mxit) dynamic_mxit=yes ;; novell) dynamic_novell=yes ;; @@ -1281,9 +1286,9 @@ orig_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS $newflag" AC_MSG_CHECKING(for $newflag option to gcc) - AC_TRY_COMPILE([], [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[ int main() {return 0;} - ], [ + ]])], [ AC_MSG_RESULT(yes) CFLAGS="$orig_CFLAGS" DEBUG_CFLAGS="$DEBUG_CFLAGS $newflag" @@ -1295,7 +1300,7 @@ if test "x$enable_fortify" = "xyes"; then AC_MSG_CHECKING(for FORTIFY_SOURCE support) - AC_TRY_COMPILE([#include <features.h>], [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <features.h>]], [[ int main() { #if !(__GNUC_PREREQ (4, 1) \ || (defined __GNUC_RH_RELEASE__ && __GNUC_PREREQ (4, 0)) \ @@ -1307,7 +1312,7 @@ #endif return 0; } - ], [ + ]])], [ AC_MSG_RESULT(yes) DEBUG_CFLAGS="$DEBUG_CFLAGS -Wp,-D_FORTIFY_SOURCE=2" ], [ @@ -1481,12 +1486,15 @@ PY_MAJOR=`$pythonpath -c 'import sys ; print sys.version[0:2]'` changequote([, ])dnl if test -f $PY_PREFIX/include/python$PY_VERSION/Python.h -a "$PY_MAJOR" = "2."; then + AC_MSG_RESULT() AC_CHECK_LIB(pthread, pthread_create, ) AC_CHECK_LIB(util, openpty, ) AC_CHECK_LIB(db, dbopen, ) PY_LIBS="-L$PY_EXEC_PREFIX/lib/python$PY_VERSION/config -lpython$PY_VERSION" PY_CFLAGS="-I$PY_PREFIX/include/python$PY_VERSION" AC_DEFINE(USE_PYTHON, [1], [Define if python headers are available.]) + dnl Because the above AC_CHECK_LIB get in the way... + AC_MSG_CHECKING(for Python compile flags) AC_MSG_RESULT(ok) else AC_MSG_RESULT([Can't find Python.h]) @@ -2144,6 +2152,7 @@ if test -f $dir/tclConfig.sh; then TCLCONFIG=$dir/tclConfig.sh AC_MSG_RESULT([yes ($TCLCONFIG)]) + break fi done if test "$TCLCONFIG" = "no"; then @@ -2169,8 +2178,8 @@ CPPFLAGS="$CPPFLAGS $TCL_INCLUDE_SPEC -I$TCL_PREFIX/include" oldLIBS=$LIBS LIBS="$LIBS $TCL_LIB_SPEC" - AC_TRY_LINK([#include <tcl.h>], - [Tcl_Interp *interp=NULL; Tcl_Init(interp)], + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <tcl.h>]], + [[Tcl_Interp *interp=NULL; Tcl_Init(interp)]])], [AC_MSG_RESULT([yes]);enable_tcl=yes], [AC_MSG_RESULT([no]);enable_tcl=no]) CPPFLAGS="$oldCPPFLAGS" @@ -2214,6 +2223,7 @@ if test -f $dir/tkConfig.sh; then TKCONFIG=$dir/tkConfig.sh AC_MSG_RESULT([yes ($TKCONFIG)]) + break fi done if test "$TKCONFIG" = "no"; then @@ -2233,8 +2243,8 @@ CPPFLAGS="$CPPFLAGS $TCL_CFLAGS" oldLIBS=$LIBS LIBS="$LIBS $TCL_LIB_SPEC $TK_LIB_SPEC" - AC_TRY_LINK([#include <tk.h>], - [Tcl_Interp *interp=NULL; Tcl_Init(interp); Tk_Init(interp);], + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include <tk.h>]], + [[Tcl_Interp *interp=NULL; Tcl_Init(interp); Tk_Init(interp);]])], [AC_MSG_RESULT([yes]);enable_tk=yes], [AC_MSG_RESULT([no]);enable_tk=no]) CPPFLAGS="$oldCPPFLAGS" @@ -2286,7 +2296,7 @@ SASL_LIBS=-"lsasl2" ], [ AM_CONDITIONAL(USE_CYRUS_SASL, false) - AC_ERROR(Cyrus SASL library not found) + AC_MSG_ERROR([Cyrus SASL library not found]) ]) else AM_CONDITIONAL(USE_CYRUS_SASL, false) @@ -2319,7 +2329,7 @@ [KRB4_LIBS="-lkrb4 -ldes425 -lkrb5 -lk5crypto -lcom_err"], [AC_CHECK_LIB(krb, krb_rd_req, [KRB4_LIBS="-lkrb -ldes"], - [AC_ERROR(Kerberos 4 libraries not found)], + [AC_MSG_ERROR([Kerberos 4 libraries not found])], -ldes)], -ldes425 -lkrb5 -lk5crypto -lcom_err) orig_LIBS="$LIBS" @@ -2353,7 +2363,7 @@ LDFLAGS="$LDFLAGS $ZEPHYR_LDFLAGS" AC_CHECK_LIB(zephyr, ZInitialize, [ZEPHYR_LIBS="-lzephyr"], - [AC_ERROR(Zephyr libraries not found)], + [AC_MSG_ERROR([Zephyr libraries not found])], -lzephyr) orig_LIBS="$LIBS" LIBS="$orig_LIBS" @@ -2384,16 +2394,40 @@ AC_VAR_TIMEZONE_EXTERNALS AC_CACHE_CHECK(for tm_gmtoff in struct tm, ac_cv_struct_tm_gmtoff, - AC_TRY_COMPILE([ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ #include <time.h> - ], [ + ]], [[ struct tm tm; tm.tm_gmtoff = 1; - ], ac_cv_struct_tm_gmtoff=yes, ac_cv_struct_tm_gmtoff=no)) + ]])], [ac_cv_struct_tm_gmtoff=yes], [ac_cv_struct_tm_gmtoff=no])) if test $ac_cv_struct_tm_gmtoff = yes; then AC_DEFINE(HAVE_TM_GMTOFF, 1, [Define if you have a tm_gmtoff member in struct tm]) fi +AC_CACHE_CHECK([whether va_lists can be copied by value], ac_cv_va_val_copy,[ + AC_RUN_IFELSE([AC_LANG_SOURCE([[#include <stdarg.h> +#include <stdlib.h> + void f (int i, ...) { + va_list args1, args2; + va_start (args1, i); + args2 = args1; + if (va_arg (args2, int) != 42 || va_arg (args1, int) != 42) + exit (1); + va_end (args1); va_end (args2); + } + int main() { + f (0, 42); + return 0; + }]])], + [ac_cv_va_val_copy=yes], + [ac_cv_va_val_copy=no], + [ac_cv_va_val_copy=yes]) +]) + +if test "x$ac_cv_va_val_copy" = "xno"; then + AC_DEFINE(VA_COPY_AS_ARRAY, 1, ['va_lists' cannot be copied as values]) +fi + dnl ####################################################################### dnl # Check for check dnl ####################################################################### @@ -2482,7 +2516,7 @@ AM_CONDITIONAL(PURPLE_AVAILABLE, true) -AC_OUTPUT([Makefile +AC_CONFIG_FILES([Makefile Doxyfile doc/Makefile doc/pidgin.1 @@ -2528,7 +2562,6 @@ libpurple/protocols/irc/Makefile libpurple/protocols/jabber/Makefile libpurple/protocols/msn/Makefile - libpurple/protocols/msnp9/Makefile libpurple/protocols/myspace/Makefile libpurple/protocols/mxit/Makefile libpurple/protocols/novell/Makefile @@ -2555,6 +2588,7 @@ po/Makefile.in pidgin.spec ]) +AC_OUTPUT echo echo $PACKAGE $VERSION @@ -2583,6 +2617,7 @@ 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.......... : $gadu_libs echo Install pixmaps............... : $enable_pixmaps echo Install translations.......... : $enable_i18n echo Has you....................... : yes
--- a/libpurple/connection.h Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/connection.h Tue Mar 16 12:07:06 2010 +0900 @@ -44,8 +44,9 @@ PURPLE_CONNECTION_NO_FONTSIZE = 0x0020, /**< Connection does not send/receive font sizes */ PURPLE_CONNECTION_NO_URLDESC = 0x0040, /**< Connection does not support descriptions with links */ PURPLE_CONNECTION_NO_IMAGES = 0x0080, /**< Connection does not support sending of images */ - PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY = 0x0100 /**< Connection supports sending and receiving custom smileys */ - + PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY = 0x0100, /**< Connection supports sending and receiving custom smileys */ + PURPLE_CONNECTION_SUPPORT_MOODS = 0x0200, /**< Connection supports setting moods */ + PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES = 0x0400 /**< Connection supports setting a message on moods */ } PurpleConnectionFlags; typedef enum
--- a/libpurple/ft.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/ft.c Tue Mar 16 12:07:06 2010 +0900 @@ -579,7 +579,7 @@ type = purple_xfer_get_type(xfer); account = purple_xfer_get_account(xfer); - purple_debug_misc("xfer", "request accepted for %p\n", xfer); + purple_debug_misc("xfer", "request accepted for %p\n", xfer); if (!filename && type == PURPLE_XFER_RECEIVE) { xfer->status = PURPLE_XFER_STATUS_ACCEPTED; @@ -1088,6 +1088,7 @@ size_t result = 0; size_t s = MIN(purple_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size); PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer); + gboolean read = TRUE; /* this is so the prpl can keep the connection open if it needs to for some odd reason. */ @@ -1099,47 +1100,41 @@ return; } - if (ui_ops && ui_ops->ui_read) { - gssize tmp = ui_ops->ui_read(xfer, &buffer, s); - if (tmp == 0) { - /* - * The UI claimed it was ready, but didn't have any data for - * us... It will call purple_xfer_ui_ready when ready, which - * sets back up this watcher. - */ - if (xfer->watcher != 0) { - purple_input_remove(xfer->watcher); - xfer->watcher = 0; + if (priv->buffer) { + if (priv->buffer->len < s) { + s -= priv->buffer->len; + read = TRUE; + } else { + read = FALSE; + } + } + + if (read) { + if (ui_ops && ui_ops->ui_read) { + gssize tmp = ui_ops->ui_read(xfer, &buffer, s); + if (tmp == 0) { + /* + * The UI claimed it was ready, but didn't have any data for + * us... It will call purple_xfer_ui_ready when ready, which + * sets back up this watcher. + */ + if (xfer->watcher != 0) { + purple_input_remove(xfer->watcher); + xfer->watcher = 0; + } + + /* Need to indicate the prpl is still ready... */ + priv->ready |= PURPLE_XFER_READY_PRPL; + + g_return_if_reached(); + } else if (tmp < 0) { + purple_debug_error("filetransfer", "Unable to read whole buffer.\n"); + purple_xfer_cancel_local(xfer); + return; } - /* Need to indicate the prpl is still ready... */ - priv->ready |= PURPLE_XFER_READY_PRPL; - - /* - * if we requested 0 bytes it's only normal that end up here - * we shouldn't return as we still have something to - * write in priv->buffer - */ - if (s != 0) - g_return_if_reached(); - } else if (tmp < 0) { - purple_debug_error("filetransfer", "Unable to read whole buffer.\n"); - purple_xfer_cancel_local(xfer); - return; - } - - result = tmp; - } else { - gboolean read = TRUE; - if (priv->buffer) { - if (priv->buffer->len < s) { - s -= priv->buffer->len; - read = TRUE; - } else { - read = FALSE; - } - } - if (read) { + result = tmp; + } else { buffer = g_malloc(s); result = fread(buffer, 1, s, xfer->dest_fp); if (result != s) { @@ -1150,14 +1145,14 @@ } } } - + if (priv->buffer) { priv->buffer = g_byte_array_append(priv->buffer, buffer, result); g_free(buffer); buffer = priv->buffer->data; result = priv->buffer->len; } - + r = purple_xfer_write(xfer, buffer, result); if (r == -1) { @@ -1172,12 +1167,12 @@ */ purple_xfer_increase_buffer_size(xfer); } else { - if (ui_ops && ui_ops->data_not_sent) - ui_ops->data_not_sent(xfer, buffer + r, result -r); + if (ui_ops && ui_ops->data_not_sent) + ui_ops->data_not_sent(xfer, buffer + r, result - r); } if (priv->buffer) { - /* + /* * Remove what we wrote * If we wrote the whole buffer the byte array will be empty * Otherwise we'll kee what wasn't sent for next time.
--- a/libpurple/plugins/perl/common/Makefile.mingw Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/plugins/perl/common/Makefile.mingw Tue Mar 16 12:07:06 2010 +0900 @@ -13,7 +13,6 @@ TARGET = Purple AUTOSPLIT = lib/auto/Purple/autosplit.ix -EXTUTILS ?= C:/perl/lib/ExtUtils PERL_PLUGIN_TOP := .. ##
--- a/libpurple/plugins/perl/perl-common.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/plugins/perl/perl-common.c Tue Mar 16 12:07:06 2010 +0900 @@ -472,74 +472,74 @@ } SV * -purple_perl_sv_from_vargs(const PurpleValue *value, va_list args, void ***copy_arg) +purple_perl_sv_from_vargs(const PurpleValue *value, va_list *args, void ***copy_arg) { if (purple_value_is_outgoing(value)) { switch (purple_value_get_type(value)) { case PURPLE_TYPE_SUBTYPE: - if ((*copy_arg = va_arg(args, void **)) == NULL) + if ((*copy_arg = va_arg(*args, void **)) == NULL) return &PL_sv_undef; return purple_perl_sv_from_subtype(value, *(void **)*copy_arg); case PURPLE_TYPE_BOOLEAN: - if ((*copy_arg = (void *)va_arg(args, gboolean *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, gboolean *)) == NULL) return &PL_sv_undef; return newSViv(*(gboolean *)*copy_arg); case PURPLE_TYPE_INT: - if ((*copy_arg = (void *)va_arg(args, int *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, int *)) == NULL) return &PL_sv_undef; return newSViv(*(int *)*copy_arg); case PURPLE_TYPE_UINT: - if ((*copy_arg = (void *)va_arg(args, unsigned int *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, unsigned int *)) == NULL) return &PL_sv_undef; return newSVuv(*(unsigned int *)*copy_arg); case PURPLE_TYPE_LONG: - if ((*copy_arg = (void *)va_arg(args, long *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, long *)) == NULL) return &PL_sv_undef; return newSViv(*(long *)*copy_arg); case PURPLE_TYPE_ULONG: - if ((*copy_arg = (void *)va_arg(args, + if ((*copy_arg = (void *)va_arg(*args, unsigned long *)) == NULL) return &PL_sv_undef; return newSVuv(*(unsigned long *)*copy_arg); case PURPLE_TYPE_INT64: - if ((*copy_arg = (void *)va_arg(args, gint64 *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, gint64 *)) == NULL) return &PL_sv_undef; return newSViv(*(gint64 *)*copy_arg); case PURPLE_TYPE_UINT64: - if ((*copy_arg = (void *)va_arg(args, guint64 *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, guint64 *)) == NULL) return &PL_sv_undef; return newSVuv(*(guint64 *)*copy_arg); case PURPLE_TYPE_STRING: - if ((*copy_arg = (void *)va_arg(args, char **)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, char **)) == NULL) return &PL_sv_undef; return newSVGChar(*(char **)*copy_arg); case PURPLE_TYPE_POINTER: - if ((*copy_arg = va_arg(args, void **)) == NULL) + if ((*copy_arg = va_arg(*args, void **)) == NULL) return &PL_sv_undef; return newSViv((IV)*(void **)*copy_arg); case PURPLE_TYPE_BOXED: /* Uh.. I dunno. Try this? */ - if ((*copy_arg = va_arg(args, void **)) == NULL) + if ((*copy_arg = va_arg(*args, void **)) == NULL) return &PL_sv_undef; return sv_2mortal(purple_perl_bless_object( @@ -553,40 +553,40 @@ } else { switch (purple_value_get_type(value)) { case PURPLE_TYPE_SUBTYPE: - if ((*copy_arg = va_arg(args, void *)) == NULL) + if ((*copy_arg = va_arg(*args, void *)) == NULL) return &PL_sv_undef; return purple_perl_sv_from_subtype(value, *copy_arg); case PURPLE_TYPE_BOOLEAN: - *copy_arg = GINT_TO_POINTER( va_arg(args, gboolean) ); + *copy_arg = GINT_TO_POINTER( va_arg(*args, gboolean) ); return newSViv((gboolean)GPOINTER_TO_INT(*copy_arg)); case PURPLE_TYPE_INT: - *copy_arg = GINT_TO_POINTER( va_arg(args, int) ); + *copy_arg = GINT_TO_POINTER( va_arg(*args, int) ); return newSViv(GPOINTER_TO_INT(*copy_arg)); case PURPLE_TYPE_UINT: - *copy_arg = GUINT_TO_POINTER(va_arg(args, unsigned int)); + *copy_arg = GUINT_TO_POINTER(va_arg(*args, unsigned int)); return newSVuv(GPOINTER_TO_UINT(*copy_arg)); case PURPLE_TYPE_LONG: - *copy_arg = (void *)va_arg(args, long); + *copy_arg = (void *)va_arg(*args, long); return newSViv((long)*copy_arg); case PURPLE_TYPE_ULONG: - *copy_arg = (void *)va_arg(args, unsigned long); + *copy_arg = (void *)va_arg(*args, unsigned long); return newSVuv((unsigned long)*copy_arg); case PURPLE_TYPE_INT64: #if 0 /* XXX This yells and complains. */ - *copy_arg = va_arg(args, gint64); + *copy_arg = va_arg(*args, gint64); return newSViv(*copy_arg); #endif @@ -595,27 +595,27 @@ case PURPLE_TYPE_UINT64: /* XXX This also yells and complains. */ #if 0 - *copy_arg = (void *)va_arg(args, guint64); + *copy_arg = (void *)va_arg(*args, guint64); return newSVuv(*copy_arg); #endif break; case PURPLE_TYPE_STRING: - if ((*copy_arg = (void *)va_arg(args, char *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, char *)) == NULL) return &PL_sv_undef; return newSVGChar((char *)*copy_arg); case PURPLE_TYPE_POINTER: - if ((*copy_arg = (void *)va_arg(args, void *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL) return &PL_sv_undef; return newSViv((IV)*copy_arg); case PURPLE_TYPE_BOXED: /* Uh.. I dunno. Try this? */ - if ((*copy_arg = (void *)va_arg(args, void *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL) return &PL_sv_undef; return sv_2mortal(purple_perl_bless_object(*copy_arg,
--- a/libpurple/plugins/perl/perl-common.h Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/plugins/perl/perl-common.h Tue Mar 16 12:07:06 2010 +0900 @@ -66,7 +66,7 @@ #endif void *purple_perl_data_from_sv(PurpleValue *value, SV *sv); -SV *purple_perl_sv_from_vargs(const PurpleValue *value, va_list args, +SV *purple_perl_sv_from_vargs(const PurpleValue *value, va_list *args, void ***copy_arg); SV *purple_perl_sv_from_fun(PurplePlugin *plugin, SV *callback); #endif /* _PURPLE_PERL_COMMON_H_ */
--- a/libpurple/plugins/perl/perl-handlers.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/plugins/perl/perl-handlers.c Tue Mar 16 12:07:06 2010 +0900 @@ -298,7 +298,11 @@ for (i = 0; i < value_count; i++) { sv_args[i] = purple_perl_sv_from_vargs(values[i], - args, +#ifdef VA_COPY_AS_ARRAY + (va_list*)args, +#else + (va_list*)&args, +#endif ©_args[i]); XPUSHs(sv_args[i]);
--- a/libpurple/protocols/Makefile.am Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/Makefile.am Tue Mar 16 12:07:06 2010 +0900 @@ -1,5 +1,5 @@ EXTRA_DIST = Makefile.mingw -DIST_SUBDIRS = bonjour gg irc jabber msn msnp9 myspace mxit novell null oscar qq sametime silc silc10 simple yahoo zephyr +DIST_SUBDIRS = bonjour gg irc jabber msn myspace mxit novell null oscar qq sametime silc silc10 simple yahoo zephyr SUBDIRS = $(DYNAMIC_PRPLS) $(STATIC_PRPLS)
--- a/libpurple/protocols/gg/Makefile.am Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/gg/Makefile.am Tue Mar 16 12:07:06 2010 +0900 @@ -4,14 +4,19 @@ lib/compat.h \ lib/COPYING \ lib/dcc.c \ + lib/dcc7.c \ lib/events.c \ lib/http.c \ lib/libgadu.c \ lib/libgadu-config.h \ lib/libgadu.h \ lib/obsolete.c \ + lib/protocol.h \ + lib/pubdir.c \ lib/pubdir50.c \ - lib/pubdir.c + lib/resolver.c \ + lib/resolver.h \ + lib/sha1.c pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) @@ -20,16 +25,21 @@ lib/common.c \ lib/compat.h \ lib/dcc.c \ + lib/dcc7.c \ lib/events.c \ lib/http.c \ lib/libgadu.c \ lib/libgadu-config.h \ lib/libgadu.h \ lib/obsolete.c \ + lib/protocol.h \ + lib/pubdir.c \ lib/pubdir50.c \ - lib/pubdir.c + lib/resolver.c \ + lib/resolver.h \ + lib/sha1.c -INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib +INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib -DGG_IGNORE_DEPRECATED endif GGSOURCES = \
--- a/libpurple/protocols/gg/Makefile.mingw Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/gg/Makefile.mingw Tue Mar 16 12:07:06 2010 +0900 @@ -8,7 +8,7 @@ include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak TARGET = libgg -CFLAGS += -include win32dep.h +CFLAGS += -include win32dep.h -DGG_IGNORE_DEPRECATED TYPE = PLUGIN # Static or Plugin... @@ -41,12 +41,16 @@ ## C_SRC = \ lib/common.c \ + lib/dcc.c \ + lib/dcc7.c \ lib/events.c \ lib/http.c \ lib/libgadu.c \ lib/obsolete.c \ lib/pubdir.c \ lib/pubdir50.c \ + lib/resolver.c \ + lib/sha1.c \ buddylist.c \ confer.c \ gg.c \
--- a/libpurple/protocols/gg/buddylist.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/gg/buddylist.c Tue Mar 16 12:07:06 2010 +0900 @@ -82,7 +82,7 @@ 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);
--- a/libpurple/protocols/gg/gg.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/gg/gg.c Tue Mar 16 12:07:06 2010 +0900 @@ -1000,8 +1000,8 @@ int status, const char *descr) { gchar *from; + gchar *msg; const char *st; - gchar *msg; gchar *avatarurl; PurpleUtilFetchUrlData *url_data; @@ -1018,29 +1018,37 @@ switch (status) { case GG_STATUS_NOT_AVAIL: case GG_STATUS_NOT_AVAIL_DESCR: - st = "offline"; + 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 = "available"; + st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE); break; case GG_STATUS_BUSY: case GG_STATUS_BUSY_DESCR: - st = "away"; + 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); case GG_STATUS_BLOCKED: /* user is blocking us.... */ st = "blocked"; break; default: - st = "available"; + st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE); purple_debug_info("gg", "GG_EVENT_NOTIFY: Unknown status: %d\n", status); break; } purple_debug_info("gg", "st = %s\n", st); - msg = charset_convert(descr, "CP1250", "UTF-8"); + //msg = charset_convert(descr, "CP1250", "UTF-8"); + msg = g_strdup_printf("%s", descr); purple_prpl_got_user_status(purple_connection_get_account(gc), from, st, "message", msg, NULL); g_free(from); @@ -1078,6 +1086,12 @@ 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"); @@ -1347,8 +1361,11 @@ 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); @@ -1562,7 +1579,7 @@ purple_debug_info("gg", "notify_pre: (%d) status: %d\n", ev->event.notify->uin, - ev->event.notify->status); + GG_S(ev->event.notify->status)); n = (ev->type == GG_EVENT_NOTIFY) ? ev->event.notify : ev->event.notify_descr.notify; @@ -1573,17 +1590,17 @@ purple_debug_info("gg", "notify: (%d) status: %d; descr: %s\n", - n->uin, n->status, descr ? descr : "(null)"); + n->uin, GG_S(n->status), descr ? descr : "(null)"); ggp_generic_status_handler(gc, - n->uin, n->status, descr); + n->uin, GG_S(n->status), descr); } } break; case GG_EVENT_NOTIFY60: purple_debug_info("gg", "notify60_pre: (%d) status=%d; version=%d; descr=%s\n", - ev->event.notify60->uin, ev->event.notify60->status, + ev->event.notify60->uin, GG_S(ev->event.notify60->status), ev->event.notify60->version, ev->event.notify60->descr ? ev->event.notify60->descr : "(null)"); @@ -1602,7 +1619,7 @@ break; case GG_EVENT_STATUS: purple_debug_info("gg", "status: (%d) status=%d; descr=%s\n", - ev->event.status.uin, ev->event.status.status, + 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, @@ -1611,12 +1628,12 @@ case GG_EVENT_STATUS60: purple_debug_info("gg", "status60: (%d) status=%d; version=%d; descr=%s\n", - ev->event.status60.uin, ev->event.status60.status, + 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, - ev->event.status60.status, ev->event.status60.descr); + GG_S(ev->event.status60.status), ev->event.status60.descr); break; case GG_EVENT_USERLIST: if (ev->event.userlist.type == GG_USERLIST_GET_REPLY) { @@ -1829,6 +1846,16 @@ 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*. */ @@ -1930,6 +1957,9 @@ 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); + glp->async = 1; glp->status = ggp_to_gg_status(status, &glp->status_descr); glp->tls = 0; @@ -2100,8 +2130,11 @@ 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; @@ -2160,6 +2193,9 @@ } else if (strcmp(status_id, "away") == 0) { new_status = GG_STATUS_BUSY; new_status_descr = GG_STATUS_BUSY_DESCR; + } else if (strcmp(status_id, "unavailable") == 0) { + new_status = GG_STATUS_DND; + new_status_descr = GG_STATUS_DND_DESCR; } else if (strcmp(status_id, "invisible") == 0) { new_status = GG_STATUS_INVISIBLE; new_status_descr = GG_STATUS_INVISIBLE_DESCR; @@ -2177,9 +2213,12 @@ 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 { @@ -2279,7 +2318,8 @@ GGPInfo *info = gc->proto_data; GGPChat *chat = NULL; GList *l; - char *msg, *plain; + /* char *msg, *plain; */ + gchar *msg; uin_t *uins; int count = 0; @@ -2310,9 +2350,12 @@ 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);
--- a/libpurple/protocols/gg/lib/common.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/gg/lib/common.c Tue Mar 16 12:07:06 2010 +0900 @@ -1,8 +1,8 @@ -/* $Id: common.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: common.c 878 2009-11-16 23:48:19Z wojtekka $ */ /* * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Woシny <speedy@ziew.org> + * Robert J. Woナコny <speedy@ziew.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -15,109 +15,180 @@ * * 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 Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ +/* + * Funkcje konwersji miト囘zy UTF-8 i CP1250 sト oparte o kod biblioteki iconv. + * Informacje o prawach autorskich oryginalnego kodu zamieszczono poniナシej: + * + * Copyright (C) 1999-2001, 2004 Free Software Foundation, Inc. + * This file is part of the GNU LIBICONV Library. + * + * The GNU LIBICONV Library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * The GNU LIBICONV 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the GNU LIBICONV Library; see the file COPYING.LIB. + * If not, write to the Free Software Foundation, Inc., 51 Franklin Street, + * Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * \file common.c + * + * \brief Funkcje wykorzystywane przez rテウナシne moduナZ biblioteki + */ + #include "libgadu.h" +#include "libgadu-internal.h" #ifndef _WIN32 -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#ifdef sun -# include <sys/filio.h> -#endif +# include <sys/types.h> +# include <sys/ioctl.h> +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# ifdef sun +# include <sys/filio.h> +# endif #endif #include <errno.h> #include <fcntl.h> + #ifndef _WIN32 -#include <netdb.h> +# include <netdb.h> #endif + #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +/** + * Plik, do ktテウrego bト囘ト przekazywane informacje odpluskwiania. + * + * Funkcja \c gg_debug() i pochodne mogト byト przechwytywane przez aplikacjト + * korzystajトcト z biblioteki, by wyナ孩ietliト je na ナシトdanie uナシytkownika lub + * zapisaト do pテウナコniejszej analizy. Jeナ嬪i nie okreナ嬪ono pliku, wybrane + * informacje bト囘ト wysyナBne do standardowego wyjナ嫩ia bナて囘u (\c stderr). + * + * \ingroup debug + */ FILE *gg_debug_file = NULL; #ifndef GG_DEBUG_DISABLE -/* - * gg_debug() // funkcja wewn黎rzna +/** + * \internal Przekazuje informacje odpluskwiania do odpowiedniej funkcji. + * + * Jeナ嬪i aplikacja ustawiナB odpowiedniト funkcjト obsナVgi w + * \c gg_debug_handler_session lub \c gg_debug_handler, jest ona wywoナZwana. + * W przeciwnym wypadku wynik jest wysyナBny do standardowego wyjナ嫩ia bナて囘u. * - * wyカwietla komunikat o danym poziomie, o ile uソytkownik sobie tego ソyczy. + * \param sess Struktura sesji (moナシe byト \c NULL) + * \param level Poziom informacji + * \param format Format wiadomoナ嫩i (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) + (*gg_debug_handler_session)(sess, level, format, ap); + else if (gg_debug_handler) + (*gg_debug_handler)(level, format, ap); + else if (gg_debug_level & level) + vfprintf(gg_debug_file ? gg_debug_file : stderr, format, ap); +} + + +/** + * \internal Przekazuje informacjト odpluskawiania. * - * - level - poziom wiadomoカci - * - format... - treカ wiadomoカci (kompatybilna z printf()) + * \param level Poziom wiadomoナ嫩i + * \param format Format wiadomoナ嫩i (zgodny z \c printf) + * + * \ingroup debug */ void gg_debug(int level, const char *format, ...) { va_list ap; int old_errno = errno; - - if (gg_debug_handler) { - va_start(ap, format); - (*gg_debug_handler)(level, format, ap); - va_end(ap); + va_start(ap, format); + gg_debug_common(NULL, level, format, ap); + va_end(ap); + errno = old_errno; +} - goto cleanup; - } - - if ((gg_debug_level & level)) { - va_start(ap, format); - vfprintf((gg_debug_file) ? gg_debug_file : stderr, format, ap); - va_end(ap); - } - -cleanup: +/** + * \internal Przekazuje informacjト odpluskwiania zwiトzanト z sesjト. + * + * \param sess Struktura sesji + * \param level Poziom wiadomoナ嫩i + * \param format Format wiadomoナ嫩i (zgodny z \c printf) + * + * \ingroup debug + */ +void gg_debug_session(struct gg_session *sess, int level, const char *format, ...) +{ + va_list ap; + int old_errno = errno; + va_start(ap, format); + gg_debug_common(sess, level, format, ap); + va_end(ap); errno = old_errno; } #endif -/* - * gg_vsaprintf() // funkcja pomocnicza +/** + * \internal Odpowiednik funkcji \c vsprintf alokujトcy miejsce na wynik. + * + * Funkcja korzysta z funkcji \c vsnprintf, sprawdzajトc czy dostト冪na funkcja + * systemowa jest zgodna ze standardem C99 czy wczeナ嬾iejszymi. * - * robi dokウadnie to samo, co vsprintf(), tyle ソe alokuje sobie wczeカniej - * miejsce na dane. powinno dziaウa na tych maszynach, ktre majア funkcj - * vsnprintf() zgodnア z C99, jak i na wczeカniejszych. + * \param format Format wiadomoナ嫩i (zgodny z \c printf) + * \param ap Lista argumentテウw (zgodna z \c printf) * - * - format - opis wyカwietlanego tekstu jak dla printf() - * - ap - lista argumentw dla printf() + * \return Zaalokowany bufor lub NULL, jeナ嬪i zabrakナP pamiト冂i. * - * zaalokowany bufor, ktry naleソy pシniej zwolni, lub NULL - * jeカli nie udaウo si wykona zadania. + * \ingroup helper */ char *gg_vsaprintf(const char *format, va_list ap) { int size = 0; const char *start; char *buf = NULL; - -#ifdef __GG_LIBGADU_HAVE_VA_COPY + +#ifdef GG_CONFIG_HAVE_VA_COPY va_list aq; va_copy(aq, ap); #else -# ifdef __GG_LIBGADU_HAVE___VA_COPY +# ifdef GG_CONFIG_HAVE___VA_COPY va_list aq; __va_copy(aq, ap); # endif #endif - start = format; + start = format; -#ifndef __GG_LIBGADU_HAVE_C99_VSNPRINTF +#ifndef GG_CONFIG_HAVE_C99_VSNPRINTF { int res; char *tmp; - + size = 128; do { size *= 2; @@ -132,9 +203,9 @@ #else { char tmp[2]; - - /* libce Solarisa przy buforze NULL zawsze zwracajア -1, wi鹹 - * musimy poda coカ istniejアcego jako cel printf()owania. */ + + /* libce Solarisa przy buforze NULL zawsze zwracajト -1, wiト冂 + * musimy podaト coナ istniejトcego jako cel printf()owania. */ size = vsnprintf(tmp, sizeof(tmp), format, ap); if (!(buf = malloc(size + 1))) return NULL; @@ -142,33 +213,33 @@ #endif format = start; - -#ifdef __GG_LIBGADU_HAVE_VA_COPY + +#ifdef GG_CONFIG_HAVE_VA_COPY vsnprintf(buf, size + 1, format, aq); va_end(aq); #else -# ifdef __GG_LIBGADU_HAVE___VA_COPY +# ifdef GG_CONFIG_HAVE___VA_COPY vsnprintf(buf, size + 1, format, aq); va_end(aq); # else vsnprintf(buf, size + 1, format, ap); # endif #endif - + return buf; } -/* - * gg_saprintf() // funkcja pomocnicza +/** + * \internal Odpowiednik funkcji \c sprintf alokujトcy miejsce na wynik. + * + * Funkcja korzysta z funkcji \c vsnprintf, sprawdzajトc czy dostト冪na funkcja + * systemowa jest zgodna ze standardem C99 czy wczeナ嬾iejszymi. * - * robi dokウadnie to samo, co sprintf(), tyle ソe alokuje sobie wczeカniej - * miejsce na dane. powinno dziaウa na tych maszynach, ktre majア funkcj - * vsnprintf() zgodnア z C99, jak i na wczeカniejszych. + * \param format Format wiadomoナ嫩i (zgodny z \c printf) * - * - format... - treカ taka sama jak w funkcji printf() + * \return Zaalokowany bufor lub NULL, jeナ嬪i zabrakナP pamiト冂i. * - * zaalokowany bufor, ktry naleソy pシniej zwolni, lub NULL - * jeカli nie udaウo si wykona zadania. + * \ingroup helper */ char *gg_saprintf(const char *format, ...) { @@ -182,18 +253,17 @@ return res; } -/* - * gg_get_line() // funkcja pomocnicza - * - * podaje kolejnア lini z bufora tekstowego. niszczy go bezpowrotnie, dzielアc - * na kolejne stringi. zdarza si, nie ma potrzeby pisania funkcji dublujアcej - * bufor ソeby tylko mie nieruszone dane wejカciowe, skoro i tak nie b鹽ア nam - * poシniej potrzebne. obcina `\r\n'. - * - * - ptr - wskaシnik do zmiennej, ktra przechowuje aktualnア pozycj - * w przemiatanym buforze - * - * wskaシnik do kolejnej linii tekstu lub NULL, jeカli to juソ koniec bufora. +/** + * \internal Pobiera liniト tekstu z bufora. + * + * Funkcja niszczy bufor ナコrテウdナPwy 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ナPナシenie + * w analizowanym buforze + * + * \return Wskaナコnik do kolejnej linii tekstu lub NULL, jeナ嬪i to juナシ koniec + * bufora. */ char *gg_get_line(char **ptr) { @@ -207,99 +277,30 @@ if (!(foo = strchr(*ptr, '\n'))) *ptr += strlen(*ptr); else { + size_t len; *ptr = foo + 1; *foo = 0; - if (strlen(res) > 1 && res[strlen(res) - 1] == '\r') - res[strlen(res) - 1] = 0; + + len = strlen(res); + + if (len > 1 && res[len - 1] == '\r') + res[len - 1] = 0; } return res; } -/* - * gg_connect() // funkcja pomocnicza +/** + * \internal Czyta liniト tekstu z gniazda. * - * ウアczy si z serwerem. pierwszy argument jest typu (void *), ソeby nie - * musie niczego inkludowa w libgadu.h i nie psu jakiカ gウupich zaleソnoカci - * na dziwnych systemach. - * - * - addr - adres serwera (struct in_addr *) - * - port - port serwera - * - async - asynchroniczne poウアczenie + * Funkcja czyta tekst znak po znaku, wiト冂 nie jest efektywna, ale dziト冖i + * brakowi buforowania, nie koliduje z innymi funkcjami odczytu. * - * deskryptor gniazda lub -1 w przypadku bウ鹽u (kod bウ鹽u w zmiennej errno). - */ -int gg_connect(void *addr, int port, int async) -{ - int sock, one = 1, 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)); - return -1; - } - -#ifdef ASSIGN_SOCKETS_TO_THREADS - gg_win32_thread_socket(0, sock); -#endif - - if (async) { -#ifdef FIONBIO - if (ioctl(sock, FIONBIO, &one) == -1) { -#else - if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { -#endif - gg_debug(GG_DEBUG_MISC, "// gg_connect() ioctl() failed (errno=%d, %s)\n", errno, strerror(errno)); - errno2 = errno; - close(sock); - errno = errno2; - return -1; - } - } - - 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; -} - -/* - * gg_read_line() // funkcja pomocnicza + * \param sock Deskryptor gniazda + * \param buf Wskaナコnik do bufora + * \param length DナVgoナ崙 bufora * - * czyta jednア lini tekstu z gniazda. - * - * - sock - deskryptor gniazda - * - buf - wskaシnik do bufora - * - length - dウugoカ bufora - * - * jeカli trafi na bウアd odczytu lub podano nieprawidウowe parametry, zwraca NULL. - * inaczej zwraca buf. + * \return Zwraca \c buf jeナ嬪i siト powiodナP, lub \c NULL w przypadku bナて囘u. */ char *gg_read_line(int sock, char *buf, int length) { @@ -331,38 +332,116 @@ return buf; } -/* - * gg_chomp() // funkcja pomocnicza +/** + * \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ナて囘u * - * ucina "\r\n" lub "\n" z koca linii. + * \ingroup helper + */ +int gg_connect(void *addr, int port, int async) +{ + int sock, one = 1, 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; + } + +#ifdef ASSIGN_SOCKETS_TO_THREADS + gg_win32_thread_socket(0, sock); +#endif + + if (async) { +#ifdef FIONBIO + if (ioctl(sock, FIONBIO, &one) == -1) { +#else + if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { +#endif + gg_debug(GG_DEBUG_MISC, "// gg_connect() ioctl() failed (errno=%d, %s)\n", errno, strerror(errno)); + errno2 = errno; + close(sock); + errno = errno2; + return -1; + } + } + + 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. * - * - line - linia do przyci鹹ia + * Funkcja dziaナB bezpoナ孑ednio 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; } -/* - * gg_urlencode() // funkcja wewn黎rzna +/** + * \internal Koduje ciトg znakテウw do postacji adresu HTTP. * - * zamienia podany tekst na ciアg znakw do formularza http. przydaje si - * przy rソnych usウugach katalogu publicznego. + * Zamienia znaki niedrukowalne, spoza ASCII i majトce specjalne znaczenie + * dla protokoナV HTTP na encje postaci \c %XX, gdzie \c XX jest szesnastkowト + * wartoナ嫩iト znaku. + * + * \param str Ciトg znakテウw do zakodowania * - * - str - ciアg znakw do zakodowania + * \return Zaalokowany bufor lub \c NULL w przypadku bナて囘u. * - * zaalokowany bufor, ktry naleソy pシniej zwolni albo NULL - * w przypadku bウ鹽u. + * \ingroup helper */ char *gg_urlencode(const char *str) { @@ -400,16 +479,19 @@ return buf; } -/* - * gg_http_hash() // funkcja wewn黎rzna +/** + * \internal Wyznacza skrテウt dla usナVg HTTP. * - * funkcja liczアca hash dla adresu e-mail, hasウa i paru innych. + * Funkcja jest wykorzystywana do wyznaczania skrテウtu adresu e-mail, hasナB + * i innych wartoナ嫩i przekazywanych jako parametry usナVg HTTP. * - * - format... - format kolejnych parametrw ('s' jeカli dany parametr jest - * ciアgiem znakw lub 'u' jeカli numerem GG) + * W parametrze \c format naleナシy umieナ嫩iト znaki okreナ嬪ajトce postaト kolejnych + * parametrテウw: \c 's' jeナ嬪i parametr jest ciトgiem znakテウw, \c 'u' jeナ嬪i jest + * liczbト. * - * hash wykorzystywany przy rejestracji i wszelkich manipulacjach wウasnego - * wpisu w katalogu publicznym. + * \param format Format kolejnych parametrテウw (niezgodny z \c printf) + * + * \return Wartoナ崙 skrテウtu */ int gg_http_hash(const char *format, ...) { @@ -428,7 +510,7 @@ } else { if (!(arg = va_arg(ap, char*))) arg = ""; - } + } i = 0; while ((c = (unsigned char) arg[i++]) != 0) { @@ -442,95 +524,6 @@ return (b < 0 ? -b : b); } -/* - * gg_gethostbyname() // funkcja pomocnicza - * - * odpowiednik gethostbyname() troszczアcy si o wspウbieソnoカ, gdy mamy do - * dyspozycji funkcj gethostbyname_r(). - * - * - hostname - nazwa serwera - * - * zwraca wskaシnik na struktur in_addr, ktrア naleソy zwolni. - */ -struct in_addr *gg_gethostbyname(const char *hostname) -{ - struct in_addr *addr = NULL; - -#ifdef HAVE_GETHOSTBYNAME_R - char *tmpbuf = NULL, *buf = NULL; - struct hostent *hp = NULL, *hp2 = NULL; - int h_errnop, ret; - size_t buflen = 1024; - int new_errno; - - new_errno = ENOMEM; - - if (!(addr = malloc(sizeof(struct in_addr)))) - goto cleanup; - - if (!(hp = calloc(1, sizeof(*hp)))) - goto cleanup; - - if (!(buf = malloc(buflen))) - goto cleanup; - - tmpbuf = buf; - - while ((ret = gethostbyname_r(hostname, hp, buf, buflen, &hp2, &h_errnop)) == ERANGE) { - buflen *= 2; - - if (!(tmpbuf = realloc(buf, buflen))) - break; - - buf = tmpbuf; - } - - if (ret) - new_errno = h_errnop; - - if (ret || !hp2 || !tmpbuf) - goto cleanup; - - memcpy(addr, hp->h_addr, sizeof(struct in_addr)); - - free(buf); - free(hp); - - return addr; - -cleanup: - errno = new_errno; - - if (addr) - free(addr); - if (hp) - free(hp); - if (buf) - free(buf); - - return NULL; -#else - struct hostent *hp; - - if (!(addr = malloc(sizeof(struct in_addr)))) { - goto cleanup; - } - - if (!(hp = gethostbyname(hostname))) - goto cleanup; - - memcpy(addr, hp->h_addr, sizeof(struct in_addr)); - - return addr; - -cleanup: - if (addr) - free(addr); - - return NULL; -#endif -} - #ifdef ASSIGN_SOCKETS_TO_THREADS typedef struct gg_win32_thread { @@ -541,23 +534,22 @@ struct gg_win32_thread *gg_win32_threads = 0; -/* - * gg_win32_thread_socket() // funkcja pomocnicza, tylko dla win32 +/** + * \internal Zwraca deskryptor gniazda, ktテウre byナP ostatnio tworzone dla wトtku. * - * zwraca deskryptor gniazda, ktre byウo ostatnio tworzone dla wアtku - * o podanym identyfikatorze. + * Jeナ嬪i na win32 przy poナてczeniach synchronicznych zapamiト冲amy w jakim + * wトtku uruchomiliナ嬶y funkcjト, ktテウra siト z czymkolwiek ナてczy, to z osobnego + * wトtku moナシemy anulowaト poナてczenie poprzez \c gg_win32_thread_socket(watek,-1) * - * jeカli na win32 przy poウアczeniach synchronicznych zapami黎amy w jakim - * wアtku uruchomiliカmy funkcj, ktra si z czymkolwiek ウアczy, to z osobnego - * wアtku moソemy anulowa poウアczenie poprzez gg_win32_thread_socket(watek, -1); - * - * - thread_id - id wアtku. jeカli jest rwne 0, brany jest aktualny wアtek, - * jeカli rwne -1, usuwa wpis o podanym sockecie. - * - socket - deskryptor gniazda. jeカli rwne 0, zwraca deskryptor gniazda - * dla podanego wアtku, jeカli rwne -1, usuwa wpis, jeカli coカ - * innego, ustawia dla podanego wアtku dany numer deskryptora. + * \param thread_id Identyfikator wトtku (jeナ嬪i jest rテウwne 0, brany jest + * aktualny wトtek, jeナ嬪i rテウwne -1, usuwa wpis dotyczトcy + * danego gniazda sockecie) + * \param socket Deskryptor gniazda (jeナ嬪i rテウwne 0, zwraca deskryptor gniazda + * dla podanego wトtku, jeナ嬪i rテウwne -1, usuwa wpis, jeナ嬪i coナ + * innego, ustawia dla podanego wトtku dany numer deskryptora) * - * jeカli socket jest rwne 0, zwraca deskryptor gniazda dla podanego wアtku. + * \return Jeナ嬪i socket jest rテウwne 0, zwraca deskryptor gniazda dla podanego + * wトtku. */ int gg_win32_thread_socket(int thread_id, int socket) { @@ -567,7 +559,7 @@ if (!thread_id) thread_id = GetCurrentThreadId(); - + while (wsk) { if ((thread_id == -1 && wsk->socket == socket) || wsk->id == thread_id) { if (close) { @@ -593,7 +585,7 @@ closesocket(socket); if (close || !socket) return 0; - + /* Dodaje nowy element */ wsk = malloc(sizeof(gg_win32_thread)); wsk->id = thread_id; @@ -606,28 +598,33 @@ #endif /* ASSIGN_SOCKETS_TO_THREADS */ +/** + * \internal Zestaw znakテウw kodowania base64. + */ static char gg_base64_charset[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -/* - * gg_base64_encode() +/** + * \internal Koduje ciトg znakテウw do base64. + * + * Wynik funkcji naleナシy zwolniト za pomocト \c free. * - * zapisuje ciアg znakw w base64. + * \param buf Bufor z danami do zakodowania * - * - buf - ciアg znakw. + * \return Zaalokowany bufor z zakodowanymi danymi * - * zaalokowany bufor. + * \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: @@ -660,20 +657,22 @@ if (i % 4) for (j = 0; j < 4 - (i % 4); j++, out++) *out = '='; - + *out = 0; - + return res; } -/* - * gg_base64_decode() +/** + * \internal Dekoduje ciトg znakテウw zapisany w base64. + * + * Wynik funkcji naleナシy zwolniト za pomocト \c free. * - * dekoduje ciアg znakw z base64. + * \param buf Bufor ナコrテウdナPwy z danymi do zdekodowania * - * - buf - ciアg znakw. + * \return Zaalokowany bufor ze zdekodowanymi danymi * - * zaalokowany bufor. + * \ingroup helper */ char *gg_base64_decode(const char *buf) { @@ -683,7 +682,7 @@ if (!buf) return NULL; - + save = res = calloc(1, (strlen(buf) / 4 + 1) * 3 + 2); if (!save) @@ -720,23 +719,24 @@ index %= 4; } *res = 0; - + return save; } -/* - * gg_proxy_auth() // funkcja wewn黎rzna +/** + * \internal Tworzy nagナづウwek autoryzacji serwera poナ孑edniczトcego. * - * tworzy nagウwek autoryzacji dla proxy. - * - * zaalokowany tekst lub NULL, jeカli proxy nie jest wウアczone lub nie wymaga - * autoryzacji. + * Dane pobiera ze zmiennych globalnych \c gg_proxy_username i + * \c gg_proxy_password. + * + * \return Zaalokowany bufor z tekstem lub NULL, jeナ嬪i serwer poナ孑edniczトcy + * nie jest uナシywany lub nie wymaga autoryzacji. */ char *gg_proxy_auth() { char *tmp, *enc, *out; unsigned int tmp_size; - + if (!gg_proxy_enabled || !gg_proxy_username || !gg_proxy_password) return NULL; @@ -749,14 +749,14 @@ free(tmp); return NULL; } - + free(tmp); if (!(out = malloc(strlen(enc) + 40))) { free(enc); return NULL; } - + snprintf(out, strlen(enc) + 40, "Proxy-Authorization: Basic %s\r\n", enc); free(enc); @@ -764,13 +764,21 @@ return out; } +/** + * \internal Tablica pomocnicza do wyznaczania sumy kontrolnej. + */ static uint32_t gg_crc32_table[256]; + +/** + * \internal Flaga wypeナOienia tablicy pomocniczej do wyznaczania sumy + * kontrolnej. + */ static int gg_crc32_initialized = 0; -/* - * gg_crc32_make_table() // funkcja wewn黎rzna +/** + * \internal Tworzy tablicト pomocniczト do wyznaczania sumy kontrolnej. */ -static void gg_crc32_make_table() +static void gg_crc32_make_table(void) { uint32_t h = 1; unsigned int i, j; @@ -787,16 +795,15 @@ gg_crc32_initialized = 1; } -/* - * gg_crc32() - * - * wyznacza sum kontrolnア CRC32 danego bloku danych. +/** + * Wyznacza sumト kontrolnト CRC32. * - * - crc - suma kontrola poprzedniego bloku danych lub 0 jeカli pierwszy - * - buf - bufor danych - * - size - iloカ danych + * \param crc Suma kontrola poprzedniego bloku danych lub 0 jeナ嬪i liczona + * jest suma kontrolna pierwszego bloku + * \param buf Bufor danych + * \param len DナVgoナ崙 bufora danych * - * suma kontrolna CRC32. + * \return Suma kontrolna. */ uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len) { @@ -814,6 +821,186 @@ return crc ^ 0xffffffffL; } +/** + * \internal Tablica konwersji miト囘zy CP1250 a UTF-8. + */ +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 b Tekst ナコrテウdナPwy w CP1250. + * + * \return Zaalokowany bufor z tekstem w UTF-8. + */ +char *gg_cp_to_utf8(const char *b) +{ + unsigned char *buf = (unsigned char *) b; + char *newbuf; + int newlen = 0; + int i, j; + + for (i = 0; buf[i]; i++) { + uint16_t znak = (buf[i] < 0x80) ? buf[i] : table_cp1250[buf[i]-0x80]; + + if (znak < 0x80) newlen += 1; + else if (znak < 0x800) newlen += 2; + else newlen += 3; + } + + if (!(newbuf = malloc(newlen+1))) { + gg_debug(GG_DEBUG_MISC, "// gg_cp_to_utf8() not enough memory\n"); + return NULL; + } + + for (i = 0, j = 0; buf[i]; i++) { + uint16_t znak = (buf[i] < 0x80) ? buf[i] : table_cp1250[buf[i]-0x80]; + int count; + + if (znak < 0x80) count = 1; + else if (znak < 0x800) count = 2; + else count = 3; + + switch (count) { + case 3: newbuf[j+2] = 0x80 | (znak & 0x3f); znak = znak >> 6; znak |= 0x800; + case 2: newbuf[j+1] = 0x80 | (znak & 0x3f); znak = znak >> 6; znak |= 0xc0; + case 1: newbuf[j] = znak; + } + j += count; + } + newbuf[j] = '\0'; + + return newbuf; +} + +/** + * \internal Dekoduje jeden znak UTF-8. + * + * \note Funkcja nie jest kompletnト implementacjト UTF-8, a wersjト uproszczonト + * do potrzeb kodowania CP1250. + * + * \param s Tekst ナコrテウdナPwy. + * \param n DナVgoナ崙 tekstu ナコrテウdナPwego. + * \param ch Wskaナコnik na wynik dekodowania. + * + * \return DナVgoナ崙 zdekodowanej sekwencji w bajtach lub wartoナ崙 mniejsza + * od zera w przypadku bナて囘u. + */ +static int gg_utf8_helper(unsigned char *s, int n, uint16_t *ch) +{ + unsigned char c = s[0]; + + if (c < 0x80) { + *ch = c; + return 1; + } + + if (c < 0xc2) + return -1; + + if (c < 0xe0) { + if (n < 2) + return -2; + if (!((s[1] ^ 0x80) < 0x40)) + return -1; + *ch = ((uint16_t) (c & 0x1f) << 6) | (uint16_t) (s[1] ^ 0x80); + return 2; + } + + if (c < 0xf0) { + if (n < 3) + return -2; + if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 && (c >= 0xe1 || s[1] >= 0xa0))) + return -1; + *ch = ((uint16_t) (c & 0x0f) << 12) | ((uint16_t) (s[1] ^ 0x80) << 6) | (uint16_t) (s[2] ^ 0x80); + return 3; + } + + return -1; +} + +/** + * \internal Zamienia tekst kodowany UTF-8 na CP1250. + * + * \param b Tekst ナコrテウdナPwy w UTF-8. + * + * \return Zaalokowany bufor z tekstem w CP1250. + */ +char *gg_utf8_to_cp(const char *b) +{ + unsigned char *buf = (unsigned char *) b; + char *newbuf; + int newlen = 0; + int len; + int i, j; + + len = strlen(b); + + for (i = 0; i < len; newlen++) { + uint16_t discard; + int ret; + + ret = gg_utf8_helper(&buf[i], len - i, &discard); + + if (ret > 0) + i += ret; + else + i++; + } + + if (!(newbuf = malloc(newlen+1))) { + gg_debug(GG_DEBUG_MISC, "// gg_utf8_to_cp() not enough memory\n"); + return NULL; + } + + for (i = 0, j = 0; buf[i]; j++) { + uint16_t znak; + int ret, k; + + ret = gg_utf8_helper(&buf[i], len - i, &znak); + + if (ret > 0) { + i += ret; + } else { + znak = '?'; + i++; + } + + if (znak < 0x80) { + newbuf[j] = znak; + continue; + } + + newbuf[j] = '?'; + + for (k = 0; k < (sizeof(table_cp1250)/sizeof(table_cp1250[0])); k++) { + if (table_cp1250[k] == znak) { + newbuf[j] = (0x80 | k); + break; + } + } + } + newbuf[j] = '\0'; + + return newbuf; +} /* * Local variables:
--- a/libpurple/protocols/gg/lib/compat.h Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/gg/lib/compat.h Tue Mar 16 12:07:06 2010 +0900 @@ -1,8 +1,8 @@ -/* $Id: compat.h 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: compat.h 506 2008-01-14 22:15:05Z wojtekka $ */ /* * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Woシny <speedy@ziew.org> + * Robert J. Woナコny <speedy@ziew.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -15,10 +15,16 @@ * * 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 Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ +/** + * \file compat.h + * + * \brief Makra zapewniajトce kompatybilnoナ崙 API na rテウナシnych systemach + */ + #ifndef __COMPAT_H #define __COMPAT_H
--- a/libpurple/protocols/gg/lib/dcc.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/gg/lib/dcc.c Tue Mar 16 12:07:06 2010 +0900 @@ -1,8 +1,9 @@ -/* $Id: dcc.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: dcc.c 711 2009-04-16 00:52:47Z darkjames $ */ /* - * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> - * Tomasz Chiliski <chilek@chilan.com> + * (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka@irc.pl> + * Tomasz Chiliナгki <chilek@chilan.com> + * Adam Wysocki <gophi@ekg.chmurka.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -15,22 +16,28 @@ * * 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 Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ +/** + * \file dcc.c + * + * \brief ObsナVga poナてczeナ bezpoナ孑ednich do wersji Gadu-Gadu 6.x + */ #include "libgadu.h" #include <sys/types.h> #include <sys/stat.h> + #ifndef _WIN32 -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#ifdef sun -# include <sys/filio.h> -#endif +# include <sys/ioctl.h> +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# ifdef sun +# include <sys/filio.h> +# endif #endif #include <ctype.h> @@ -43,67 +50,71 @@ #include <unistd.h> #include "compat.h" + #ifndef GG_DEBUG_DISABLE -/* - * gg_dcc_debug_data() // funkcja wewn黎rzna + +/** + * \internal Przekazuje zawartoナ崙 pakietu do odpluskwiania. * - * wyカwietla zrzut pakietu w hexie. - * - * - prefix - prefiks zrzutu pakietu - * - fd - deskryptor gniazda - * - buf - bufor z danymi - * - size - rozmiar danych + * \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) { unsigned int i; - + gg_debug(GG_DEBUG_MISC, "++ gg_dcc %s (fd=%d,len=%d)", prefix, fd, size); - + for (i = 0; i < size; i++) gg_debug(GG_DEBUG_MISC, " %.2x", ((unsigned char*) buf)[i]); - + gg_debug(GG_DEBUG_MISC, "\n"); } #else #define gg_dcc_debug_data(a,b,c,d) do { } while (0) #endif -/* +/** + * WysyナB ナシトdanie zwrotnego poナてczenia bezpoナ孑edniego. + * + * Funkcjト wykorzystuje siト, jeナ嬪i nie ma moナシliwoナ嫩i poナてczenia siト z odbiorcト + * pliku lub rozmowy gナPsowej. Po otrzymaniu ナシトdania druga strona sprテウbuje + * nawiトzaト zwrotne poナてczenie bezpoナ孑ednie z nadawcト. * gg_dcc_request() * - * wysyウa informacj o tym, ソe dany klient powinien si z nami poウアczy. - * wykorzystywane, kiedy druga strona, ktrej chcemy coカ wysウa jest za - * maskaradア. + * \param sess Struktura sesji + * \param uin Numer odbiorcy * - * - sess - struktura opisujアca sesj GG - * - uin - numerek odbiorcy + * \return Patrz \c gg_send_message_ctcp() * - * patrz 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); + return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (unsigned char*) "\002", 1); } -/* - * gg_dcc_fill_filetime() // funkcja wewn黎rzna +/** + * \internal Zamienia znacznik czasu w postaci uniksowej na format API WIN32. * - * zamienia czas w postaci unixowej na windowsowy. + * \note Funkcja dziaナB jedynie gdy kompilator obsナVguje typ danych + * \c long \c long. * - * - unix - czas w postaci unixowej - * - filetime - czas w postaci windowsowej + * \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) { -#ifdef __GG_LIBGADU_HAVE_LONG_LONG +#ifdef GG_CONFIG_HAVE_LONG_LONG unsigned long long tmp; tmp = ut; tmp += 11644473600LL; tmp *= 10000000LL; -#ifndef __GG_LIBGADU_BIGENDIAN +#ifndef GG_CONFIG_BIGENDIAN ft[0] = (uint32_t) tmp; ft[1] = (uint32_t) (tmp >> 32); #else @@ -114,31 +125,33 @@ #endif } -/* - * gg_dcc_fill_file_info() +/** + * WypeナOia pola struktury \c gg_dcc niezbト囘ne do wysナBnia pliku. * - * wypeウnia pola struct gg_dcc niezb鹽ne do wysウania pliku. + * \note Wiト冖szト funkcjonalnoナ崙 zapewnia funkcja \c gg_dcc_fill_file_info2(). * - * - d - struktura opisujアca poウアczenie DCC - * - filename - nazwa pliku + * \param d Struktura poナてczenia + * \param filename Nazwa pliku * - * 0, -1. + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup dcc6 */ int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename) { return gg_dcc_fill_file_info2(d, filename, filename); } -/* - * gg_dcc_fill_file_info2() - * - * wypeウnia pola struct gg_dcc niezb鹽ne do wysウania pliku. +/** + * WypeナOia pola struktury \c gg_dcc niezbト囘ne do wysナBnia pliku. * - * - d - struktura opisujアca poウアczenie DCC - * - filename - nazwa pliku - * - local_filename - nazwa na lokalnym systemie plikw + * \param d Struktura poナてczenia + * \param filename Nazwa pliku zapisywana w strukturze + * \param local_filename Nazwa pliku w lokalnym systemie plikテウw * - * 0, -1. + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup dcc6 */ int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename) { @@ -225,25 +238,23 @@ *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); + strncpy((char*) d->file_info.filename, name, sizeof(d->file_info.filename) - 1); return 0; } -/* - * gg_dcc_transfer() // funkcja wewn黎rzna - * - * inicjuje proces wymiany pliku z danym klientem. +/** + * \internal Rozpoczyna poナてczenie bezpoナ孑ednie z danym klientem. * - * - ip - adres ip odbiorcy - * - port - port odbiorcy - * - my_uin - wウasny numer - * - peer_uin - numer obiorcy - * - type - rodzaj wymiany (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_GET) + * \param ip Adres IP odbiorcy + * \param port Port odbiorcy + * \param my_uin WナBsny numer + * \param peer_uin Numer odbiorcy + * \param type Rodzaj poナてczenia (\c GG_SESSION_DCC_SEND lub \c GG_SESSION_DCC_GET) * - * zaalokowana struct gg_dcc lub NULL jeカli wystアpiウ bウアd. + * \return Struktura \c gg_dcc lub \c NULL w przypadku bナて囘u */ static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin, int type) { @@ -251,9 +262,9 @@ struct in_addr addr; addr.s_addr = ip; - + gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_transfer(%s, %d, %ld, %ld, %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; @@ -284,18 +295,17 @@ return d; } -/* - * gg_dcc_get_file() - * - * inicjuje proces odbierania pliku od danego klienta, gdy ten wysウaウ do - * nas ソアdanie poウアczenia. +/** + * Rozpoczyna odbieranie pliku przez zwrotne poナてczenie bezpoナ孑ednie. * - * - ip - adres ip odbiorcy - * - port - port odbiorcy - * - my_uin - wウasny numer - * - peer_uin - numer obiorcy + * \param ip Adres IP nadawcy + * \param port Port nadawcy + * \param my_uin WナBsny numer + * \param peer_uin Numer nadawcy * - * zaalokowana struct gg_dcc lub NULL jeカli wystアpiウ bウアd. + * \return Struktura \c gg_dcc lub \c NULL w przypadku bナて囘u + * + * \ingroup dcc6 */ struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) { @@ -304,17 +314,17 @@ return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_GET); } -/* - * gg_dcc_send_file() - * - * inicjuje proces wysyウania pliku do danego klienta. +/** + * Rozpoczyna wysyナBnie pliku. * - * - ip - adres ip odbiorcy - * - port - port odbiorcy - * - my_uin - wウasny numer - * - peer_uin - numer obiorcy + * \param ip Adres IP odbiorcy + * \param port Port odbiorcy + * \param my_uin WナBsny numer + * \param peer_uin Numer odbiorcy * - * zaalokowana struct gg_dcc lub NULL jeカli wystアpiウ bウアd. + * \return Struktura \c gg_dcc lub \c NULL w przypadku bナて囘u + * + * \ingroup dcc6 */ struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) { @@ -323,17 +333,17 @@ return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_SEND); } -/* - * gg_dcc_voice_chat() - * - * prbuje nawiアza poウアczenie gウosowe. +/** + * Rozpoczyna poナてczenie gナPsowe. * - * - ip - adres ip odbiorcy - * - port - port odbiorcy - * - my_uin - wウasny numer - * - peer_uin - numer obiorcy + * \param ip Adres IP odbiorcy + * \param port Port odbiorcy + * \param my_uin WナBsny numer + * \param peer_uin Numer odbiorcy * - * zaalokowana struct gg_dcc lub NULL jeカli wystアpiウ bウアd. + * \return Struktura \c gg_dcc lub \c NULL w przypadku bナて囘u + * + * \ingroup dcc6 */ struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) { @@ -342,30 +352,34 @@ return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_VOICE); } -/* - * gg_dcc_set_type() +/** + * Ustawia typ przychodzトcego poナてczenia bezpoナ孑edniego. + * + * Funkcjト naleナシy wywoナBト po otrzymaniu zdarzenia \c GG_EVENT_DCC_CALLBACK. * - * po zdarzeniu GG_EVENT_DCC_CALLBACK naleソy ustawi typ poウアczenia za - * pomocア tej funkcji. + * \param d Struktura poナてczenia + * \param type Rodzaj poナてczenia (\c GG_SESSION_DCC_SEND lub + * \c GG_SESSION_DCC_VOICE) * - * - d - struktura opisujアca poウアczenie - * - type - typ poウアczenia (GG_SESSION_DCC_SEND lub 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; } - -/* - * gg_dcc_callback() // funkcja wewn黎rzna + +/** + * \internal Funkcja zwrotna poナてczenia bezpoナ孑edniego. * - * wywoウywana z struct gg_dcc->callback, odpala gg_dcc_watch_fd i umieszcza - * rezultat w struct gg_dcc->event. + * Pole \c callback struktury \c gg_dcc zawiera wskaナコnik do tej funkcji. + * WywoナVje ona \c gg_watch_fd() i zachowuje wynik w polu \c event. * - * - d - structura opisujアca poウアczenie + * \note Funkcjonalnoナ崙 funkcjo zwrotnej nie jest juナシ wspierana. * - * 0, -1. + * \param d Struktura poナてczenia + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u */ static int gg_dcc_callback(struct gg_dcc *d) { @@ -376,25 +390,26 @@ return (e != NULL) ? 0 : -1; } -/* - * gg_dcc_socket_create() +/** + * Tworzy gniazdo nasナVchujトce dla poナてczeナ bezpoナ孑ednich. * - * tworzy gniazdo dla bezpoカredniej komunikacji mi鹽zy klientami. + * Funkcja przywiトzuje gniazdo do pierwszego wolnego portu TCP. * - * - uin - wウasny numer - * - port - preferowany port, jeカli rwny 0 lub -1, prbuje domyカlnego + * \param uin WナBsny numer + * \param port Preferowany port (jeナ嬪i rテウwny 0 lub -1, prテウbuje siト domyナ嬪nego) * - * zaalokowana struct gg_dcc, ktrア poシniej naleソy zwolni funkcjア - * gg_dcc_free(), albo NULL jeカli wystアpiウ bウアd. + * \return Struktura \c gg_dcc lub \c NULL w przypadku bナて囘u + * + * \ingroup dcc6 */ struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port) { struct gg_dcc *c; struct sockaddr_in sin; 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; @@ -408,12 +423,12 @@ if (!port) port = GG_DEFAULT_DCC_PORT; - + while (!bound) { 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; @@ -433,7 +448,7 @@ errno = errno2; return NULL; } - + gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() bound to port %d\n", port); if (!(c = malloc(sizeof(*c)))) { @@ -452,20 +467,20 @@ c->check = GG_CHECK_READ; c->callback = gg_dcc_callback; c->destroy = gg_dcc_free; - + return c; } -/* - * gg_dcc_voice_send() - * - * wysyウa ramk danych dla rozmowy gウosowej. +/** + * WysyナB ramkト danych poナてczenia gナPsowego. * - * - d - struktura opisujアca poウアczenie dcc - * - buf - bufor z danymi - * - length - rozmiar ramki + * \param d Struktura poナてczenia + * \param buf Bufor z danymi + * \param length DナVgoナ崙 bufora z danymi * - * 0, -1. + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup dcc6 */ int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length) { @@ -500,7 +515,14 @@ return 0; } -#define gg_read(fd, buf, size) \ +/** + * \internal Odbiera dane z poナてczenia bezpoナ孑edniego z obsナVgト bナて囘テウ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 = read(fd, buf, size); \ \ @@ -517,9 +539,16 @@ return e; \ } \ gg_dcc_debug_data("read", fd, buf, size); \ -} +} -#define gg_write(fd, buf, size) \ +/** + * \internal WysyナB dane do poナてczenia bezpoナ孑edniego z obsナVgト bナて囘テウw. + * + * \param fd Deskryptor gniazda + * \param buf Bufor z danymi + * \param size Rozmiar bufora z danymi + */ +#define gg_dcc_write(fd, buf, size) \ { \ int tmp; \ gg_dcc_debug_data("write", fd, buf, size); \ @@ -536,14 +565,18 @@ } \ } -/* - * gg_dcc_watch_fd() +/** + * Funkcja wywoナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia. * - * funkcja, ktrア naleソy wywoウa, gdy coカ si zmieni na gg_dcc->fd. + * Funkcja zwraca strukturト zdarzenia \c gg_event. Jeナ嬪i rodzaj zdarzenia + * to \c GG_EVENT_NONE, nie wydarzyナP siト jeszcze nic wartego odnotowania. + * Strukturト zdarzenia naleナシy zwolniト funkcja \c gg_event_free. * - * - h - struktura zwrcona przez gg_create_dcc_socket() + * \param h Struktura poナてczenia * - * zaalokowana struct gg_event lub NULL, jeカli zabrakウo pami鹹i na niア. + * \return Struktura zdarzenia lub \c NULL jeナ嬪i wystトpiナ bナてd + * + * \ingroup dcc6 */ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h) { @@ -551,7 +584,7 @@ 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; @@ -568,10 +601,9 @@ if (h->type == GG_SESSION_DCC_SOCKET) { struct sockaddr_in sin; struct gg_dcc *c; - int fd; - socklen_t sin_len = sizeof(sin); - int one = 1; - + int fd, one = 1; + unsigned int 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; @@ -607,7 +639,7 @@ 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; @@ -617,8 +649,7 @@ struct gg_dcc_small_packet small; struct gg_dcc_big_packet big; int size, tmp, res; - socklen_t res_size = sizeof(res); - unsigned int utmp; + unsigned int utmp, res_size = sizeof(res); char buf[1024], ack[] = "UDAG"; struct gg_dcc_file_info_packet { @@ -634,8 +665,8 @@ 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_read(h->fd, &uin, sizeof(uin)); + + gg_dcc_read(h->fd, &uin, sizeof(uin)); if (h->state == GG_STATE_READING_UIN_1) { h->state = GG_STATE_READING_UIN_2; @@ -656,7 +687,7 @@ case GG_STATE_SENDING_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n"); - gg_write(h->fd, ack, 4); + gg_dcc_write(h->fd, ack, 4); h->state = GG_STATE_READING_TYPE; h->check = GG_CHECK_READ; @@ -666,8 +697,8 @@ case GG_STATE_READING_TYPE: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_TYPE\n"); - - gg_read(h->fd, &small, sizeof(small)); + + gg_dcc_read(h->fd, &small, sizeof(small)); small.type = gg_fix32(small.type); @@ -680,7 +711,7 @@ h->timeout = GG_DEFAULT_TIMEOUT; e->type = GG_EVENT_DCC_CALLBACK; - + break; case 0x0002: /* XXX */ @@ -703,8 +734,8 @@ case GG_STATE_READING_REQUEST: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_REQUEST\n"); - - gg_read(h->fd, &small, sizeof(small)); + + gg_dcc_read(h->fd, &small, sizeof(small)); small.type = gg_fix32(small.type); @@ -715,7 +746,7 @@ 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; @@ -725,22 +756,22 @@ e->type = GG_EVENT_DCC_NEED_VOICE_ACK; break; - + default: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc request (%.4x) from %ld\n", small.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_read(h->fd, &file_info_packet, sizeof(file_info_packet)); + + 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); @@ -749,17 +780,17 @@ 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.type = gg_fix32(0x0006); /* XXX */ big.dunno1 = gg_fix32(h->offset); big.dunno2 = 0; - gg_write(h->fd, &big, sizeof(big)); + gg_dcc_write(h->fd, &big, sizeof(big)); h->state = GG_STATE_READING_FILE_HEADER; h->chunk_size = sizeof(big); @@ -773,25 +804,25 @@ 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.type = 0x01; /* XXX */ - gg_write(h->fd, &tiny, sizeof(tiny)); + gg_dcc_write(h->fd, &tiny, sizeof(tiny)); 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 = read(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset); if (tmp == -1) { @@ -802,7 +833,7 @@ } 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) @@ -823,7 +854,7 @@ return e; } - if (h->chunk_size == 0) { + 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; @@ -833,13 +864,13 @@ 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_read(h->fd, &tiny, sizeof(tiny)); + + gg_dcc_read(h->fd, &tiny, sizeof(tiny)); switch (tiny.type) { case 0x03: /* XXX */ @@ -850,19 +881,19 @@ break; case 0x04: /* XXX */ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() peer breaking connection\n"); - /* XXX zwraca odpowiedni event */ + /* XXX zwracaト odpowiedni event */ default: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown request (%.2x)\n", tiny.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_read(h->fd, &small, sizeof(small)); + + gg_dcc_read(h->fd, &small, sizeof(small)); small.type = gg_fix32(small.type); @@ -870,7 +901,7 @@ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid voice frame size (%d)\n", small.type); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_NET; - + return e; } @@ -879,18 +910,19 @@ 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 = read(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset); if (tmp < 1) { if (tmp == -1) { @@ -909,7 +941,7 @@ if (h->chunk_offset >= h->chunk_size) { e->type = GG_EVENT_DCC_VOICE_DATA; - e->event.dcc_voice_data.data = h->voice_buf; + 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; @@ -917,7 +949,7 @@ h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; - + return e; case GG_STATE_CONNECTING: @@ -925,7 +957,7 @@ 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)); @@ -935,23 +967,23 @@ } 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_write(h->fd, uins, sizeof(uins)); - + 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_read(h->fd, buf, 4); + + gg_dcc_read(h->fd, buf, 4); if (strncmp(buf, ack, 4)) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n"); @@ -964,29 +996,29 @@ 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.type = gg_fix32(0x0003); - - gg_write(h->fd, &small, sizeof(small)); + + gg_dcc_write(h->fd, &small, sizeof(small)); 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.type = (h->type == GG_SESSION_DCC_GET) ? gg_fix32(0x0003) : gg_fix32(0x0002); /* XXX */ - - gg_write(h->fd, &small, sizeof(small)); - + + gg_dcc_write(h->fd, &small, sizeof(small)); + switch (h->type) { case GG_SESSION_DCC_GET: h->state = GG_STATE_READING_REQUEST; @@ -1002,7 +1034,7 @@ 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; @@ -1011,7 +1043,7 @@ } return e; - + case GG_STATE_SENDING_FILE_INFO: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n"); @@ -1021,8 +1053,8 @@ } small.type = gg_fix32(0x0001); /* XXX */ - - gg_write(h->fd, &small, sizeof(small)); + + gg_dcc_write(h->fd, &small, sizeof(small)); file_info_packet.big.type = gg_fix32(0x0003); /* XXX */ file_info_packet.big.dunno1 = 0; @@ -1030,26 +1062,26 @@ memcpy(&file_info_packet.file_info, &h->file_info, sizeof(h->file_info)); - /* zostajア teraz u nas, wi鹹 odwracamy z powrotem */ + /* zostajト teraz u nas, wiト冂 odwracamy z powrotem */ h->file_info.size = gg_fix32(h->file_info.size); h->file_info.mode = gg_fix32(h->file_info.mode); - - gg_write(h->fd, &file_info_packet, sizeof(file_info_packet)); + + 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_read(h->fd, &big, sizeof(big)); + + gg_dcc_read(h->fd, &big, sizeof(big)); - /* XXX sprawdza wynik */ + /* XXX sprawdzaト wynik */ h->offset = gg_fix32(big.dunno1); - + h->state = GG_STATE_SENDING_FILE_HEADER; h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; @@ -1060,8 +1092,8 @@ case GG_STATE_READING_VOICE_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_ACK\n"); - - gg_read(h->fd, &tiny, sizeof(tiny)); + + gg_dcc_read(h->fd, &tiny, sizeof(tiny)); if (tiny.type != 0x01) { gg_debug(GG_DEBUG_MISC, "// invalid reply (%.2x), connection refused\n", tiny.type); @@ -1075,14 +1107,14 @@ 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.type = gg_fix32(0x0003); /* XXX */ @@ -1091,8 +1123,8 @@ big.dunno1 = gg_fix32(h->chunk_size); big.dunno2 = 0; - - gg_write(h->fd, &big, sizeof(big)); + + gg_dcc_write(h->fd, &big, sizeof(big)); h->state = GG_STATE_SENDING_FILE; h->check = GG_CHECK_WRITE; @@ -1100,13 +1132,13 @@ 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? */ @@ -1117,11 +1149,17 @@ 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; + } + lseek(h->file_fd, h->offset, SEEK_SET); size = read(h->file_fd, buf, utmp); - /* bウアd */ + /* bナてd */ if (size == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno)); @@ -1139,8 +1177,8 @@ return e; } - - /* jeカli wczytaliカmy wi鹹ej, utnijmy. */ + + /* jeナ嬪i wczytaliナ嬶y wiト冂ej, 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; @@ -1161,15 +1199,22 @@ return e; } - h->offset += size; - + if (tmp == 0) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() 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 += size; - + + 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; @@ -1178,22 +1223,28 @@ 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 = read(h->fd, buf, utmp); gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() ofs=%d, size=%d, read()=%d\n", h->offset, h->file_info.size, size); - - /* bウアd */ + + /* bナてd */ if (size == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno)); @@ -1211,9 +1262,9 @@ 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; @@ -1222,14 +1273,14 @@ } 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; @@ -1245,11 +1296,11 @@ 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; @@ -1258,35 +1309,28 @@ return e; } } - + return e; } -#undef gg_read -#undef gg_write - -/* - * gg_dcc_free() +/** + * Zwalnia zasoby uナシywane przez poナてczenie bezpoナ孑ednie. * - * zwalnia pami po strukturze poウアczenia dcc. + * \param d Struktura poナてczenia * - * - d - zwalniana struktura + * \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->chunk_buf) { - free(d->chunk_buf); - d->chunk_buf = NULL; - } - + free(d->chunk_buf); free(d); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/dcc7.c Tue Mar 16 12:07:06 2010 +0900 @@ -0,0 +1,1217 @@ +/* $Id: dcc7.c 711 2009-04-16 00:52:47Z darkjames $ */ + +/* + * (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka@irc.pl> + * Tomasz Chiliナгki <chilek@chilan.com> + * Adam Wysocki <gophi@ekg.chmurka.net> + * + * Thanks to Jakub Zawadzki <darkjames@darkjames.ath.cx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, + * USA. + */ + +/** + * \file dcc7.c + * + * \brief ObsナVga poナてczeナ bezpoナ孑ednich od wersji Gadu-Gadu 7.x + */ + +#include "libgadu.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#ifndef _WIN32 +# include <sys/ioctl.h> +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# ifdef sun +# include <sys/filio.h> +# endif +#endif + +#include <time.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "compat.h" + +#define gg_debug_dcc(dcc, fmt...) \ + gg_debug_session((dcc) ? (dcc)->sess : NULL, fmt) + +/** + * \internal Dodaje poナてczenie bezpoナ孑ednie do sesji. + * + * \param sess Struktura sesji + * \param dcc Struktura poナてczenia + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +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_remove() invalid parameters\n"); + errno = EINVAL; + return -1; + } + + dcc->next = sess->dcc7_list; + sess->dcc7_list = dcc; + + return 0; +} + +/** + * \internal Usuwa poナてczenie bezpoナ孑ednie z sesji. + * + * \param sess Struktura sesji + * \param dcc Struktura poナてczenia + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +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 || !dcc) { + 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; tmp = tmp->next) { + if (tmp->next == dcc) { + tmp = 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ナ嬪i 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 Nawiトzuje poナてczenie bezpoナ孑ednie + * + * \param sess Struktura sesji + * \param dcc Struktura poナてczenia + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +static int gg_dcc7_connect(struct gg_session *sess, struct gg_dcc7 *dcc) +{ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p, %p)\n", sess, dcc); + + if (!sess || !dcc) { + gg_debug_session(sess, 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_session(sess, 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ナVchujトce dla poナてczenia bezpoナ孑edniego + * + * \param dcc Struktura poナてczenia + * \param port Preferowany port (jeナ嬪i rテウwny 0 lub -1, prテウbuje siト domyナ嬪nego) + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +static int gg_dcc7_listen(struct gg_dcc7 *dcc, uint16_t port) +{ + struct sockaddr_in sin; + 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; + } + + // XXX losowaト porty? + + if (!port) + port = GG_DEFAULT_DCC_PORT; + + while (1) { + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = htons(port); + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() trying port %d\n", port); + + if (!bind(fd, (struct sockaddr*) &sin, sizeof(sin))) + break; + + if (port++ == 65535) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() no free port found\n"); + close(fd); + errno = ENOENT; + return -1; + } + } + + if (listen(fd, 1)) { + int errsv = errno; + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to listen (%s)\n", strerror(errno)); + close(fd); + errno = errsv; + return -1; + } + + dcc->fd = fd; + dcc->local_port = port; + + dcc->state = GG_STATE_LISTENING; + dcc->check = GG_CHECK_READ; + dcc->timeout = GG_DCC7_TIMEOUT_FILE_ACK; + + return 0; +} + +/** + * \internal Tworzy gniazdo nasナVchujトce i wysyナB jego parametry + * + * \param dcc Struktura poナてczenia + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc) +{ + struct gg_dcc7_info pkt; + + gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc); + + // XXX daト moナシliwoナ崙 konfiguracji? + + dcc->local_addr = dcc->sess->client_addr; + + if (gg_dcc7_listen(dcc, 0) == -1) + return -1; + + 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(*((struct in_addr*) &dcc->local_addr)), dcc->local_port); + + 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +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ナB 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +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ナBnie 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ナて囘u + * + * \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 - 1); + 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ナBnie 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ナ嬪i ma byト wyznaczony) + * + * \return Struktura \c gg_dcc7 lub \c NULL w przypadku bナて囘u + * + * \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 (stat(filename, &st) == -1) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() stat() 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 ((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 (!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; + close(fd); + errno = errsv; + } + + free(dcc); + return NULL; +} + +/** + * \internal Rozpoczyna wysyナBnie pliku o danym deskryptorze. + * + * \note WysyナBnie pliku nie bト囘zie dziaナBト poprawnie, jeナ嬪i deskryptor + * ナコrテウdナPwy 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ナて囘u + * + * \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, %u, \"%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ナBnia pliku + * + * \note Biblioteka nie zmienia poナPナシenia w odbieranych plikach. Jeナ嬪i offset + * poczトtkowy jest rテウナシny od zera, naleナシy ustawiト go funkcjト \c lseek() lub + * podobnト. + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \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ナBnia pliku. + * + * \param dcc Struktura poナてczenia + * \param reason Powテウd odrzucenia + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \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ナVguje pakiet identyfikatora poナてczenia bezpoナ孑edniego. + * + * \param sess Struktura sesji + * \param e Struktura zdarzenia + * \param payload Treナ崙 pakietu + * \param len DナVgoナ崙 pakietu + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len) +{ + 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 != 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); + + strncpy((char*) s.filename, (char*) tmp->filename, GG_DCC7_FILENAME_LEN); + + 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ナVguje pakiet akceptacji poナてczenia bezpoナ孑edniego. + * + * \param sess Struktura sesji + * \param e Struktura zdarzenia + * \param payload Treナ崙 pakietu + * \param len DナVgoナ崙 pakietu + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len) +{ + 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ナBト 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ナ嬶y wywoナBト juナシ zdarzenie GG_DCC7_ACCEPT? + + dcc->offset = gg_fix32(p->offset); + dcc->state = GG_STATE_WAITING_FOR_INFO; + + return 0; +} + +/** + * \internal ObsナVguje pakiet informacji o poナてczeniu bezpoナ孑ednim. + * + * \param sess Struktura sesji + * \param e Struktura zdarzenia + * \param payload Treナ崙 pakietu + * \param len DナVgoナ崙 pakietu + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len) +{ + 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); + + 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 (p->type != GG_DCC7_TYPE_P2P) { + 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 ((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; + } + + // jeナ嬪i 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; + } + + 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; + } + + if (gg_dcc7_connect(sess, 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ナVguje pakiet odrzucenia poナてczenia bezpoナ孑edniego. + * + * \param sess Struktura sesji + * \param e Struktura zdarzenia + * \param payload Treナ崙 pakietu + * \param len DナVgoナ崙 pakietu + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len) +{ + 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ナVguje pakiet nowego poナてczenia bezpoナ孑edniego. + * + * \param sess Struktura sesji + * \param e Struktura zdarzenia + * \param payload Treナ崙 pakietu + * \param len DナVgoナ崙 pakietu + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len) +{ + 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 - 1); + 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 %ld\n", gg_fix32(p->type), gg_fix32(p->uin_from)); + + break; + } + + return 0; +} + +/** + * \internal Ustawia odpowiednie stany wewnト冲rzne w zaleナシnoナ嫩i od rodzaju + * poナてczenia. + * + * \param dcc Struktura poナてczenia + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u. + */ +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ナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia. + * + * Funkcja zwraca strukturト zdarzenia \c gg_event. Jeナ嬪i rodzaj zdarzenia + * to \c GG_EVENT_NONE, nie wydarzyナP 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ナ嬪i 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, one = 1; + 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)); + +#ifdef FIONBIO + if (ioctl(fd, FIONBIO, &one) == -1) { +#else + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { +#endif + 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; + unsigned int 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 (gg_dcc7_reverse_connect(dcc) != -1) { + e->type = GG_EVENT_DCC7_PENDING; + } 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: + { + gg_dcc7_id_t id; + int res; + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n"); + + if ((res = read(dcc->fd, &id, sizeof(id))) != sizeof(id)) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() 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(&id, &dcc->cid, sizeof(id))) { + 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 ((res = write(dcc->fd, &dcc->cid, sizeof(dcc->cid))) != sizeof(dcc->cid)) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)", 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]; + int chunk, 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; + 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 = write(dcc->fd, buf, res)) == -1) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() 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; + 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; + return e; + } + + if ((res = read(dcc->fd, buf, sizeof(buf))) < 1) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() 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; + return e; + } + + dcc->state = GG_STATE_GETTING_FILE; + dcc->check = GG_CHECK_READ; + dcc->timeout = GG_DCC7_TIMEOUT_GET; + + 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ナ孑ednie. + * + * \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) + close(dcc->file_fd); + + if (dcc->sess) + gg_dcc7_session_remove(dcc->sess, dcc); + + free(dcc); +} +
--- a/libpurple/protocols/gg/lib/events.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/gg/lib/events.c Tue Mar 16 12:07:06 2010 +0900 @@ -1,9 +1,10 @@ -/* $Id: events.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: events.c 855 2009-10-12 21:42:51Z wojtekka $ */ /* - * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Woシny <speedy@ziew.org> - * Arkadiusz Miカkiewicz <arekm@pld-linux.org> + * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> + * Robert J. Woナコny <speedy@ziew.org> + * Arkadiusz Miナ嫐iewicz <arekm@pld-linux.org> + * Adam Wysocki <gophi@ekg.chmurka.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -16,71 +17,78 @@ * * 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 Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ +/** + * \file events.c + * + * \brief ObsナVga zdarzeナ + */ + #include "libgadu.h" +#include "libgadu-internal.h" #include <sys/types.h> + #ifndef _WIN32 -#include <sys/wait.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> +# include <sys/ioctl.h> +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> #endif -#include "libgadu-config.h" +#include "compat.h" +#include "protocol.h" #include <errno.h> -#ifdef __GG_LIBGADU_HAVE_PTHREAD -# include <pthread.h> -#endif #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <unistd.h> -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#include <ctype.h> +#ifdef GG_CONFIG_HAVE_OPENSSL # include <openssl/err.h> # include <openssl/x509.h> #endif -#include "compat.h" - -/* - * gg_event_free() +/** + * Zwalnia pamiト卞 zajmowanト przez informacjト o zdarzeniu. * - * zwalnia pami zajmowanア przez informacj o zdarzeniu. + * Funkcjト naleナシy wywoナZwaト za kaナシdym razem gdy funkcja biblioteki zwrテウci + * strukturト \c gg_event. * - * - e - wskaシnik do informacji o zdarzeniu + * \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: free(e->event.msg.message); free(e->event.msg.formats); free(e->event.msg.recipients); 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; @@ -89,7 +97,7 @@ case GG_EVENT_STATUS60: free(e->event.status60.descr); break; - + case GG_EVENT_STATUS: free(e->event.status.descr); break; @@ -112,26 +120,30 @@ 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; } free(e); } -/* - * gg_image_queue_remove() - * - * usuwa z kolejki dany wpis. +/** \cond internal */ + +/** + * \internal Usuwa obrazek z kolejki do wysナBnia. * - * - s - sesja - * - q - kolejka - * - freeq - czy zwolni kolejk + * \param s Struktura sesji + * \param q Struktura obrazka + * \param freeq Flaga zwolnienia elementu kolejki * - * 0/-1 + * \return 0 jeナ嬪i siト powiodナP, -1 jeナ嬪i wystトpiナ bナてd */ int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) { @@ -162,13 +174,14 @@ return 0; } -/* - * gg_image_queue_parse() // funkcja wewn黎rzna +/** + * \internal Analizuje przychodzトcy pakiet z obrazkiem. * - * parsuje przychodzアcy pakiet z obrazkiem. - * - * - e - opis zdarzenia - * - + * \param e Struktura zdarzenia + * \param p Bufor z danymi + * \param len DナVgoナ崙 bufora + * \param sess Struktura sesji + * \param sender Numer nadawcy */ static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len, struct gg_session *sess, uin_t sender) { @@ -180,8 +193,8 @@ return; } - /* znajdシ dany obrazek w kolejce danej sesji */ - + /* 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; @@ -190,34 +203,23 @@ } if (!q) { - gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32); return; } if (p[0] == 0x05) { - unsigned int i, ok = 0; - q->done = 0; len -= sizeof(struct gg_msg_image_reply); p += sizeof(struct gg_msg_image_reply); - /* sprawdシ, czy mamy tekst zakoczony \0 */ - - for (i = 0; i < len; i++) { - if (!p[i]) { - ok = 1; - break; - } - } - - if (!ok) { - gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender); + if (memchr(p, 0, len) == NULL) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender); return; } if (!(q->filename = strdup(p))) { - gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n"); return; } @@ -230,11 +232,11 @@ if (q->done + len > q->size) len = q->size - q->done; - + memcpy(q->image + q->done, p, len); q->done += len; - /* jeカli skoczono odbiera obrazek, wygeneruj zdarzenie */ + /* jeナ嬪i skoナczono odbieraト obrazek, wygeneruj zdarzenie */ if (q->done >= q->size) { e->type = GG_EVENT_IMAGE_REPLY; @@ -250,78 +252,55 @@ } } -/* - * gg_handle_recv_msg() // funkcja wewn黎rzna - * - * obsウuguje pakiet z przychodzアcア wiadomoカciア, rozbijajアc go na dodatkowe - * struktury (konferencje, kolorki) w razie potrzeby. +/** + * \internal Analizuje informacje rozszerzone wiadomoナ嫩i. + * + * \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. * - * - h - nagウwek pakietu - * - e - opis zdarzenia - * - * 0, -1. + * \return 0 jeナ嬪i siト powiodナP, -1 jeナ嬪i wiadomoナ崙 obsナVナシono i wynik ma + * zostaト przekazany aplikacji, -2 jeナ嬪i wystトpiナ bナてd ogテウlny, -3 jeナ嬪i + * wiadomoナ崙 jest niepoprawna. */ -static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess) +static int gg_handle_recv_msg_options(struct gg_session *sess, struct gg_event *e, uin_t sender, char *p, char *packet_end) { - struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header)); - char *p, *packet_end = (char*) r + h->length; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e); - - if (!r->seq && !r->msgclass) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n"); - e->type = GG_EVENT_NONE; - return 0; - } - - for (p = (char*) r + sizeof(*r); *p; p++) { - if (*p == 0x02 && p == packet_end - 1) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n"); - break; - } - if (p >= packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n"); - goto malformed; - } - } - - p++; - - /* przeanalizuj dodatkowe opcje */ while (p < packet_end) { switch (*p) { case 0x01: /* konferencja */ { struct gg_msg_recipients *m = (void*) p; uint32_t i, count; - + p += sizeof(*m); - + if (p > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() 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(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n"); goto malformed; } - + if (!(e->event.msg.recipients = (void*) malloc(count * sizeof(uin_t)))) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n"); goto fail; } - + for (i = 0; i < count; i++, p += sizeof(uint32_t)) { uint32_t u; memcpy(&u, p, sizeof(uint32_t)); e->event.msg.recipients[i] = gg_fix32(u); } - + e->event.msg.recipients_count = count; - + break; } @@ -329,9 +308,9 @@ { uint16_t len; char *buf; - + if (p + 3 > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n"); goto malformed; } @@ -339,18 +318,18 @@ len = gg_fix16(len); if (!(buf = malloc(len))) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n"); goto fail; } p += 3; if (p + len > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); free(buf); goto malformed; } - + memcpy(buf, p, len); e->event.msg.formats = buf; @@ -366,17 +345,17 @@ struct gg_msg_image_request *i = (void*) p; if (p + sizeof(*i) > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); goto malformed; } - e->event.image_request.sender = gg_fix32(r->sender); + 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; - return 0; + goto handled; } case 0x05: /* image_reply */ @@ -386,75 +365,347 @@ if (p + sizeof(struct gg_msg_image_reply) == packet_end) { - /* pusta odpowiedシ - klient po drugiej stronie nie ma ソアdanego obrazka */ + /* pusta odpowiedナコ - klient po drugiej stronie nie ma ナシトdanego obrazka */ e->type = GG_EVENT_IMAGE_REPLY; - e->event.image_reply.sender = gg_fix32(r->sender); + 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; - return 0; + goto handled; } else if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n"); + 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, gg_fix32(r->sender)); - - return 0; + gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, sender); + + goto handled; } default: { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p); + 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 Analizuje przychodzトcy pakiet z wiadomoナ嫩iト. + * + * Rozbija pakiet na poszczegテウlne skナBdniki -- tekst, informacje + * o konferencjach, formatowani itd. + * + * \param h Wskaナコnik do odebranego pakietu + * \param e Struktura zdarzenia + * \param sess Struktura sesji + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess) +{ + struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header)); + char *p, *packet_end = (char*) r + h->length; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e); + + if (!r->seq && !r->msgclass) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n"); + e->type = GG_EVENT_NONE; + return 0; + } + + /* znajdナコ \0 */ + for (p = (char*) r + sizeof(*r); ; p++) { + if (p >= packet_end) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n"); + goto malformed; + } + + if (*p == 0x02 && p == packet_end - 1) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n"); + break; + } + + if (!*p) + break; + } + + p++; + + switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), p, packet_end)) { + case -1: // handled + 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.message = (unsigned char *)strdup((char*) r + sizeof(*r)); + e->event.msg.seq = gg_fix32(r->seq); + e->event.msg.message = (unsigned char*) strdup((char*) r + sizeof(*r)); return 0; malformed: e->type = GG_EVENT_NONE; - + free(e->event.msg.message); free(e->event.msg.recipients); free(e->event.msg.formats); return 0; fail: + free(e->event.msg.message); free(e->event.msg.recipients); free(e->event.msg.formats); return -1; } -/* - * gg_watch_fd_connected() // funkcja wewn黎rzna +/** + * \internal Zamienia tekst w formacie HTML na czysty tekst. + * + * \param dst Bufor wynikowy (moナシe byト \c NULL) + * \param html Tekst ナコrテウdナPwy + * + * \note Dokleja \c \\0 na koナcu bufora wynikowego. * - * patrzy na gniazdo, odbiera pakiet i wypeウnia struktur zdarzenia. + * \return DナVgoナ崙 tekstu wynikowego bez \c \\0 (nawet jeナ嬪i \c dst to \c NULL). + */ +static int gg_convert_from_html(char *dst, const char *html) +{ + const char *src, *entity, *tag; + int len, in_tag, in_entity; + + len = 0; + in_tag = 0; + tag = NULL; + in_entity = 0; + entity = NULL; + + for (src = html; *src != 0; src++) { + if (*src == '<') { + tag = src; + in_tag = 1; + continue; + } + + if (in_tag && (*src == '>')) { + if (strncmp(tag, "<br", 3) == 0) { + if (dst != NULL) + dst[len] = '\n'; + len++; + } + in_tag = 0; + continue; + } + + if (in_tag) + continue; + + if (*src == '&') { + in_entity = 1; + entity = src; + continue; + } + + if (in_entity && *src == ';') { + in_entity = 0; + if (dst != NULL) { + if (strncmp(entity, "<", 4) == 0) + dst[len] = '<'; + else if (strncmp(entity, ">", 4) == 0) + dst[len] = '>'; + else if (strncmp(entity, """, 6) == 0) + dst[len] = '"'; + else if (strncmp(entity, "'", 6) == 0) + dst[len] = '\''; + else if (strncmp(entity, "&", 5) == 0) + dst[len] = '&'; + else + dst[len] = '?'; + } + len++; + continue; + } + + if (in_entity && !(isalnum(*src) || *src == '#')) + in_entity = 0; + + if (in_entity) + continue; + + if (dst != NULL) + dst[len] = *src; + + len++; + } + + if (dst != NULL) + dst[len] = 0; + + return len; +} + +/** + * \internal Analizuje przychodzトcy pakiet z wiadomoナ嫩iト protokoナV Gadu-Gadu 8.0. + * + * Rozbija pakiet na poszczegテウlne skナBdniki -- tekst, informacje + * o konferencjach, formatowani itd. + * + * \param h Wskaナコnik do odebranego pakietu + * \param e Struktura zdarzenia + * \param sess Struktura sesji * - * - sess - struktura opisujアca sesj - * - e - opis zdarzenia + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +static int gg_handle_recv_msg80(struct gg_header *h, struct gg_event *e, struct gg_session *sess) +{ + char *packet = (char*) h + sizeof(struct gg_header); + struct gg_recv_msg80 *r = (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, %p);\n", h, e); + + if (!r->seq && !r->msgclass) { + 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 >= h->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 > h->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. */ + /* goto ignore; */ + } + + /* Normalna sytuacja, wiト冂 nie podpada pod powyナシszy warunek. */ + if (offset_attr == h->length) + offset_attr = 0; + + if (memchr(packet + offset_plain, 0, h->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 = 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); + + if (sess->encoding == GG_ENCODING_CP1250) { + e->event.msg.message = (unsigned char*) strdup(packet + offset_plain); + } else { + if (offset_plain > sizeof(struct gg_recv_msg80)) { + int len; + + len = gg_convert_from_html(NULL, packet + sizeof(struct gg_recv_msg80)); + + e->event.msg.message = malloc(len + 1); + + if (e->event.msg.message == NULL) + goto fail; + + gg_convert_from_html((char*) e->event.msg.message, packet + sizeof(struct gg_recv_msg80)); + } else { + e->event.msg.message = (unsigned char*) gg_cp_to_utf8(packet + offset_plain); + } + } + + if (offset_plain > sizeof(struct gg_recv_msg80)) { + if (sess->encoding == GG_ENCODING_UTF8) + e->event.msg.xhtml_message = strdup(packet + sizeof(struct gg_recv_msg80)); + else + e->event.msg.xhtml_message = gg_utf8_to_cp(packet + sizeof(struct gg_recv_msg80)); + } else { + e->event.msg.xhtml_message = NULL; + } + + if (offset_attr != 0) { + switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), packet + offset_attr, packet + h->length)) { + case -1: // handled + return 0; + + case -2: // failed + goto fail; + + case -3: // malformed + goto malformed; + } + } + + 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); + return 0; +} + +/** + * \internal Odbiera pakiet od serwera. * - * 0, -1. + * Analizuje pakiet i wypeナOia strukturト zdarzenia. + * + * \param sess Struktura sesji + * \param e Struktura zdarzenia + * + * \return 0 jeナ嬪i siト powiodナP, -1 jeナ嬪i wystトpiナ bナてd */ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e) { struct gg_header *h = NULL; char *p; - gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e); + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e); if (!sess) { errno = EFAULT; @@ -462,76 +713,86 @@ } if (!(h = gg_recv_packet(sess))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail; } p = (char*) h + sizeof(struct gg_header); - + switch (h->type) { case GG_RECV_MSG: { if (h->length >= sizeof(struct gg_recv_msg)) if (gg_handle_recv_msg(h, e, sess)) goto fail; - + break; } + case GG_RECV_MSG80: + { + if (h->length >= sizeof(struct gg_recv_msg80)) + if (gg_handle_recv_msg80(h, e, sess)) + goto fail; + + break; + } + + case GG_NOTIFY_REPLY: { struct gg_notify_reply *n = (void*) p; unsigned int count, i; char *tmp; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); if (h->length < sizeof(*n)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n"); errno = EINVAL; goto fail; } 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) { e->type = GG_EVENT_NOTIFY_DESCR; - + if (!(e->event.notify_descr.notify = (void*) malloc(sizeof(*n) * 2))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } e->event.notify_descr.notify[1].uin = 0; memcpy(e->event.notify_descr.notify, p, sizeof(*n)); e->event.notify_descr.notify[0].uin = gg_fix32(e->event.notify_descr.notify[0].uin); e->event.notify_descr.notify[0].status = gg_fix32(e->event.notify_descr.notify[0].status); - e->event.notify_descr.notify[0].remote_ip = e->event.notify_descr.notify[0].remote_ip; e->event.notify_descr.notify[0].remote_port = gg_fix16(e->event.notify_descr.notify[0].remote_port); + e->event.notify_descr.notify[0].version = gg_fix32(e->event.notify_descr.notify[0].version); count = h->length - sizeof(*n); if (!(tmp = malloc(count + 1))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } memcpy(tmp, p + sizeof(*n), count); tmp[count] = 0; e->event.notify_descr.descr = tmp; - + } else { e->type = GG_EVENT_NOTIFY; - + if (!(e->event.notify = (void*) malloc(h->length + 2 * sizeof(*n)))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } - + memcpy(e->event.notify, p, h->length); count = h->length / sizeof(*n); e->event.notify[count].uin = 0; - + for (i = 0; i < count; i++) { e->event.notify[i].uin = gg_fix32(e->event.notify[i].uin); e->event.notify[i].status = gg_fix32(e->event.notify[i].status); - e->event.notify[i].remote_ip = e->event.notify[i].remote_ip; e->event.notify[i].remote_port = gg_fix16(e->event.notify[i].remote_port); + e->event.notify[i].version = gg_fix32(e->event.notify[i].version); } } @@ -542,7 +803,7 @@ { struct gg_status *s = (void*) p; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); if (h->length >= sizeof(*s)) { e->type = GG_EVENT_STATUS; @@ -564,23 +825,176 @@ break; } - case GG_NOTIFY_REPLY60: + case GG_NOTIFY_REPLY77: + case GG_NOTIFY_REPLY80BETA: { - struct gg_notify_reply60 *n = (void*) p; + struct gg_notify_reply77 *n = (void*) p; unsigned int length = h->length, i = 0; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); e->type = GG_EVENT_NOTIFY60; e->event.notify60 = malloc(sizeof(*e->event.notify60)); if (!e->event.notify60) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } e->event.notify60[0].uin = 0; - + + while (length >= sizeof(struct gg_notify_reply77)) { + uin_t uin = gg_fix32(n->uin); + char *tmp; + + e->event.notify60[i].uin = uin & 0x00ffffff; + e->event.notify60[i].status = n->status; + e->event.notify60[i].remote_ip = n->remote_ip; + e->event.notify60[i].remote_port = gg_fix16(n->remote_port); + e->event.notify60[i].version = n->version; + e->event.notify60[i].image_size = n->image_size; + e->event.notify60[i].descr = NULL; + e->event.notify60[i].time = 0; + + if (uin & 0x40000000) + e->event.notify60[i].version |= GG_HAS_AUDIO_MASK; + if (uin & 0x20000000) + e->event.notify60[i].version |= GG_HAS_AUDIO7_MASK; + if (uin & 0x08000000) + e->event.notify60[i].version |= GG_ERA_OMNIX_MASK; + + if (GG_S_D(n->status)) { + unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply77)); + + if (sizeof(struct gg_notify_reply77) + descr_len <= length) { + char *descr; + + if (!(descr = malloc(descr_len + 1))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + + memcpy(descr, (char*) n + sizeof(struct gg_notify_reply77) + 1, descr_len); + descr[descr_len] = 0; + + if (h->type == GG_NOTIFY_REPLY80BETA && sess->encoding != GG_ENCODING_UTF8) { + char *cp_descr = gg_utf8_to_cp(descr); + + if (!cp_descr) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + free(descr); + goto fail; + } + + free(descr); + descr = cp_descr; + } + + e->event.notify60[i].descr = descr; + + /* XXX czas */ + + length -= sizeof(struct gg_notify_reply77) + descr_len + 1; + n = (void*) ((char*) n + sizeof(struct gg_notify_reply77) + descr_len + 1); + } else { + length = 0; + } + + } else { + length -= sizeof(struct gg_notify_reply77); + n = (void*) ((char*) n + sizeof(struct gg_notify_reply77)); + } + + if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + free(e->event.notify60); + goto fail; + } + + e->event.notify60 = (void*) tmp; + e->event.notify60[++i].uin = 0; + } + + break; + } + + case GG_STATUS77: + case GG_STATUS80BETA: + { + struct gg_status77 *s = (void*) p; + uint32_t uin; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + + if (h->length < sizeof(*s)) + break; + + uin = gg_fix32(s->uin); + + e->type = GG_EVENT_STATUS60; + e->event.status60.uin = uin & 0x00ffffff; + e->event.status60.status = s->status; + e->event.status60.remote_ip = s->remote_ip; + e->event.status60.remote_port = gg_fix16(s->remote_port); + e->event.status60.version = s->version; + e->event.status60.image_size = s->image_size; + e->event.status60.descr = NULL; + e->event.status60.time = 0; + + if (uin & 0x40000000) + e->event.status60.version |= GG_HAS_AUDIO_MASK; + if (uin & 0x20000000) + e->event.status60.version |= GG_HAS_AUDIO7_MASK; + if (uin & 0x08000000) + e->event.status60.version |= GG_ERA_OMNIX_MASK; + + if (h->length > sizeof(*s)) { + int len = h->length - sizeof(*s); + char *buf = malloc(len + 1); + + /* XXX, jesli malloc() sie nie uda to robic tak samo jak przy GG_NOTIFY_REPLY* ? + * - goto fail; (?) + */ + if (buf) { + memcpy(buf, (char*) p + sizeof(*s), len); + buf[len] = 0; + + if (h->type == GG_STATUS80BETA && sess->encoding != GG_ENCODING_UTF8) { + char *cp_buf = gg_utf8_to_cp(buf); + free(buf); + buf = cp_buf; + } + } + + e->event.status60.descr = buf; + + if (len > 4 && p[h->length - 5] == 0) { + uint32_t t; + memcpy(&t, p + h->length - 4, sizeof(uint32_t)); + e->event.status60.time = gg_fix32(t); + } + } + + break; + } + + case GG_NOTIFY_REPLY60: + { + struct gg_notify_reply60 *n = (void*) p; + unsigned int length = h->length, i = 0; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + + e->type = GG_EVENT_NOTIFY60; + e->event.notify60 = malloc(sizeof(*e->event.notify60)); + + if (!e->event.notify60) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + + e->event.notify60[0].uin = 0; + while (length >= sizeof(struct gg_notify_reply60)) { uin_t uin = gg_fix32(n->uin); char *tmp; @@ -602,9 +1016,9 @@ if (GG_S_D(n->status)) { unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60)); - if (descr_len < length) { + if (sizeof(struct gg_notify_reply60) + descr_len <= length) { if (!(e->event.notify60[i].descr = malloc(descr_len + 1))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } @@ -612,17 +1026,20 @@ e->event.notify60[i].descr[descr_len] = 0; /* XXX czas */ + + length -= sizeof(struct gg_notify_reply60) + descr_len + 1; + n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1); + } else { + length = 0; } - - length -= sizeof(struct gg_notify_reply60) + descr_len + 1; - n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1); + } else { length -= sizeof(struct gg_notify_reply60); n = (void*) ((char*) n + sizeof(struct gg_notify_reply60)); } if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); free(e->event.notify60); goto fail; } @@ -633,13 +1050,13 @@ break; } - + case GG_STATUS60: { struct gg_status60 *s = (void*) p; uint32_t uin; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); if (h->length < sizeof(*s)) break; @@ -682,11 +1099,132 @@ break; } + case GG_STATUS80: + { + struct gg_notify_reply80 *s = (void*) p; + uint32_t descr_len; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + + if (h->length < sizeof(*s)) + break; + + e->type = GG_EVENT_STATUS60; + e->event.status60.uin = gg_fix32(s->uin); + e->event.status60.status = gg_fix32(s->status); + e->event.status60.remote_ip = s->remote_ip; + e->event.status60.remote_port = gg_fix16(s->remote_port); + e->event.status60.image_size = s->image_size; + e->event.status60.descr = NULL; + e->event.status60.version = 0x00; /* not-supported */ + e->event.status60.time = 0; /* not-supported */ + + descr_len = gg_fix32(s->descr_len); + + if (descr_len > 0 && h->length-sizeof(*s) >= descr_len) { + char *buf = malloc(descr_len + 1); + + if (buf) { + memcpy(buf, (char*) p + sizeof(*s), descr_len); + buf[descr_len] = 0; + + if (sess->encoding != GG_ENCODING_UTF8) { + char *cp_buf = gg_utf8_to_cp(buf); + free(buf); + buf = cp_buf; + } + } + + e->event.status60.descr = buf; + } + break; + } + + case GG_NOTIFY_REPLY80: + { + struct gg_notify_reply80 *n = (void*) p; + int length = h->length, i = 0; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + + e->type = GG_EVENT_NOTIFY60; + e->event.notify60 = malloc(sizeof(*e->event.notify60)); + if (!e->event.notify60) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + + e->event.notify60[0].uin = 0; + + while (length >= sizeof(struct gg_notify_reply80)) { + uint32_t descr_len; + char *tmp; + + e->event.notify60[i].uin = gg_fix32(n->uin); + e->event.notify60[i].status = gg_fix32(n->status); + e->event.notify60[i].remote_ip = n->remote_ip; + e->event.notify60[i].remote_port= gg_fix16(n->remote_port); + e->event.notify60[i].image_size = n->image_size; + e->event.notify60[i].descr = NULL; + e->event.notify60[i].version = 0x00; /* not-supported */ + e->event.notify60[i].time = 0; /* not-supported */ + + + descr_len = gg_fix32(n->descr_len); + + length -= sizeof(struct gg_notify_reply80); + n = (void*) ((char*) n + sizeof(struct gg_notify_reply80)); + + if (descr_len) { + if (length >= descr_len) { + /* XXX, GG_S_D(n->status) */ + char *descr; + + if (!(descr = malloc(descr_len + 1))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + + memcpy(descr, n, descr_len); + descr[descr_len] = 0; + + if (sess->encoding != GG_ENCODING_UTF8) { + char *cp_descr = gg_utf8_to_cp(descr); + + if (!cp_descr) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + free(descr); + goto fail; + } + + free(descr); + descr = cp_descr; + } + e->event.notify60[i].descr = descr; + + length -= descr_len; + n = (void*) ((char*) n + descr_len); + } else + length = 0; + } + + if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + free(e->event.notify60); + goto fail; + } + + e->event.notify60 = (void*) tmp; + e->event.notify60[++i].uin = 0; + } + break; + } + case GG_SEND_MSG_ACK: { struct gg_send_msg_ack *s = (void*) p; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n"); if (h->length < sizeof(*s)) break; @@ -699,9 +1237,9 @@ break; } - case GG_PONG: + case GG_PONG: { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n"); e->type = GG_EVENT_PONG; sess->last_pong = time(NULL); @@ -711,27 +1249,47 @@ case GG_DISCONNECTING: { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n"); e->type = GG_EVENT_DISCONNECT; break; } + case GG_DISCONNECT_ACK: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection acknowledge\n"); + e->type = GG_EVENT_DISCONNECT_ACK; + break; + } + + case GG_XML_EVENT: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received XML event\n"); + e->type = GG_EVENT_XML_EVENT; + if (!(e->event.xml_event.data = (char *) malloc(h->length + 1))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for XML event data\n"); + goto fail; + } + memcpy(e->event.xml_event.data, p, h->length); + e->event.xml_event.data[h->length] = 0; + break; + } + case GG_PUBDIR50_REPLY: { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n"); - if (gg_pubdir50_handle_reply(e, p, h->length) == -1) + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n"); + if (gg_pubdir50_handle_reply_sess(sess, e, p, h->length) == -1) goto fail; break; } case GG_USERLIST_REPLY: { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n"); if (h->length < 1) break; - /* jeカli odpowiedシ na eksport, wywoウaj zdarzenie tylko + /* jeナ嬪i odpowiedナコ na eksport, wywoナBj zdarzenie tylko * gdy otrzymano wszystkie odpowiedzi */ if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) { if (--sess->userlist_blocks) @@ -743,11 +1301,11 @@ if (h->length > 1) { char *tmp; unsigned int len = (sess->userlist_reply) ? strlen(sess->userlist_reply) : 0; - - gg_debug(GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len); - + + gg_debug_session(sess, GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len); + if (!(tmp = realloc(sess->userlist_reply, len + h->length))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n"); free(sess->userlist_reply); sess->userlist_reply = NULL; goto fail; @@ -769,10 +1327,75 @@ break; } + case GG_DCC7_ID_REPLY: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 id packet\n"); + + if (h->length < sizeof(struct gg_dcc7_id_reply)) + break; + + if (gg_dcc7_handle_id(sess, e, p, h->length) == -1) + goto fail; + + break; + } + + case GG_DCC7_ACCEPT: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 accept\n"); + + if (h->length < sizeof(struct gg_dcc7_accept)) + break; + + if (gg_dcc7_handle_accept(sess, e, p, h->length) == -1) + goto fail; + + break; + } + + case GG_DCC7_NEW: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 request\n"); + + if (h->length < sizeof(struct gg_dcc7_new)) + break; + + if (gg_dcc7_handle_new(sess, e, p, h->length) == -1) + goto fail; + + break; + } + + case GG_DCC7_REJECT: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 reject\n"); + + if (h->length < sizeof(struct gg_dcc7_reject)) + break; + + if (gg_dcc7_handle_reject(sess, e, p, h->length) == -1) + goto fail; + + break; + } + + case GG_DCC7_INFO: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 info\n"); + + if (h->length < sizeof(struct gg_dcc7_info)) + break; + + if (gg_dcc7_handle_info(sess, e, p, h->length) == -1) + goto fail; + + break; + } + default: - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type); } - + free(h); return 0; @@ -781,19 +1404,20 @@ return -1; } -/* - * gg_watch_fd() - * - * funkcja, ktrア naleソy wywoウa, gdy coカ si stanie z obserwowanym - * deskryptorem. zwraca klientowi informacj o tym, co si dzieje. +/** \endcond */ + +/** + * Funkcja wywoナZwana po zaobserwowaniu zmian na deskryptorze sesji. * - * - sess - opis sesji + * Funkcja zwraca strukturト zdarzenia \c gg_event. Jeナ嬪i rodzaj zdarzenia + * to \c GG_EVENT_NONE, nie wydarzyナP siト jeszcze nic wartego odnotowania. + * Strukturト zdarzenia naleナシy zwolniト funkcja \c gg_event_free(). * - * wskaシnik do struktury gg_event, ktrア trzeba zwolni pシniej - * za pomocア gg_event_free(). jesli rodzaj zdarzenia jest rwny - * GG_EVENT_NONE, naleソy je zignorowa. jeカli zwrciウo NULL, - * staウo si coカ niedobrego -- albo zabrakウo pami鹹i albo zerwaウo - * poウアczenie. + * \param sess Struktura sesji + * + * \return Struktura zdarzenia lub \c NULL jeナ嬪i wystトpiナ bナてd + * + * \ingroup events */ struct gg_event *gg_watch_fd(struct gg_session *sess) { @@ -802,68 +1426,79 @@ int port = 0; int errno2 = 0; - gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess); - + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess); + if (!sess) { errno = EFAULT; return NULL; } if (!(e = (void*) calloc(1, sizeof(*e)))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n"); return NULL; } e->type = GG_EVENT_NONE; + if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED)) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending %d bytes of queued data\n", sess->send_left); + + res = write(sess->fd, sess->send_buf, sess->send_left); + + if (res == -1 && errno != EAGAIN) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno)); + + if (sess->state == GG_STATE_READING_REPLY) + goto fail_connecting; + else + goto done; + } + + 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; + } + } + switch (sess->state) { case GG_STATE_RESOLVING: { struct in_addr addr; int failed = 0; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n"); if (read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n"); failed = 1; errno2 = errno; } - + close(sess->fd); sess->fd = -1; -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (sess->resolver) { - pthread_cancel(*((pthread_t*) sess->resolver)); - free(sess->resolver); - sess->resolver = NULL; - } -#elif defined _WIN32 - if (sess->resolver) { - HANDLE h = sess->resolver; - TerminateThread(h, 0); - CloseHandle(h); - sess->resolver = NULL; - } -#else - waitpid(sess->pid, NULL, 0); - sess->pid = -1; -#endif + sess->resolver_cleanup(&sess->resolver, 0); if (failed) { errno = errno2; goto fail_resolving; } - /* jeカli jesteカmy w resolverze i mamy ustawiony port - * proxy, znaczy, ソe resolvowaliカmy proxy. zatem + /* jeナ嬪i jesteナ嬶y w resolverze i mamy ustawiony port + * proxy, znaczy, ナシe resolvowaliナ嬶y proxy. zatem * wpiszmy jego adres. */ if (sess->proxy_port) sess->proxy_addr = addr.s_addr; /* zapiszmy sobie adres huba i adres serwera (do - * bezpoカredniego poウアczenia, jeカli hub leソy) + * bezpoナ孑edniego poナてczenia, jeナ嬪i hub leナシy) * z resolvera. */ if (sess->proxy_addr && sess->proxy_port) port = sess->proxy_port; @@ -872,21 +1507,27 @@ port = GG_APPMSG_PORT; } - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port); - - /* ウアczymy si albo z hubem, albo z proxy, zaleソnie - * od tego, co resolvowaliカmy. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port); + + /* ナてczymy siト albo z hubem, albo z proxy, zaleナシnie + * od tego, co resolvowaliナ嬶y. */ if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) { - /* jeカli w trybie asynchronicznym gg_connect() - * zwrci bウアd, nie ma sensu prbowa dalej. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); + /* jeナ嬪i w trybie asynchronicznym gg_connect() + * zwrテウci bナてd, nie ma sensu prテウbowaト dalej. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); goto fail_connecting; } - /* jeカli podano serwer i ウアczmy si przez proxy, - * jest to bezpoカrednie poウアczenie, inaczej jest + /* jeナ嬪i podano serwer i ナてczmy siト przez proxy, + * jest to bezpoナ孑ednie poナてczenie, inaczej jest * do huba. */ - sess->state = (sess->proxy_addr && sess->proxy_port && sess->server_addr) ? GG_STATE_CONNECTING_GG : GG_STATE_CONNECTING_HUB; + + if (sess->proxy_addr && sess->proxy_port && sess->server_addr) { + sess->state = GG_STATE_CONNECTING_GG; + sess->soft_timeout = 1; + } else + sess->state = GG_STATE_CONNECTING_HUB; + sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; @@ -898,42 +1539,25 @@ char buf[1024], *client, *auth; int res = 0; socklen_t res_size = sizeof(res); - const char *host, *appmsg; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n"); - - /* jeカli asynchroniczne, sprawdzamy, czy nie wystアpiウ - * przypadkiem jakiカ bウアd. */ + const char *host; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n"); + + /* jeナ嬪i asynchroniczne, sprawdzamy, czy nie wystトpiナ + * przypadkiem jakiナ bナてd. */ if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { - /* no tak, nie udaウo si poウアczy z proxy. nawet - * nie prbujemy dalej. */ - if (sess->proxy_addr && sess->proxy_port) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); - goto fail_connecting; - } - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s), trying direct connection\n", res, strerror(res)); - close(sess->fd); - - if ((sess->fd = gg_connect(&sess->hub_addr, GG_DEFAULT_PORT, sess->async)) == -1) { - /* przy asynchronicznych, gg_connect() - * zwraca -1 przy bウ鹽ach socket(), - * ioctl(), braku routingu itd. dlatego - * nawet nie prbujemy dalej. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() direct connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); - goto fail_connecting; - } - - sess->state = GG_STATE_CONNECTING_GG; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - break; + if (sess->proxy_addr && sess->proxy_port) + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); + else + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s)\n", res, strerror(res)); + + goto fail_connecting; } - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n"); + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n"); if (!(client = gg_urlencode((sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n"); goto fail_connecting; } @@ -942,41 +1566,43 @@ else host = ""; -#ifdef __GG_LIBGADU_HAVE_OPENSSL - if (sess->ssl) - appmsg = "appmsg3.asp"; - else + auth = gg_proxy_auth(); + +#ifdef GG_CONFIG_HAVE_OPENSSL + if (sess->ssl) { + snprintf(buf, sizeof(buf) - 1, + "GET %s/appsvc/appmsg3.asp?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n" + "Host: " GG_APPMSG_HOST "\r\n" + "User-Agent: " GG_HTTP_USERAGENT "\r\n" + "Pragma: no-cache\r\n" + "%s" + "\r\n", host, sess->uin, client, sess->last_sysmsg, (auth) ? auth : ""); + } else #endif - appmsg = "appmsg2.asp"; - - auth = gg_proxy_auth(); - - snprintf(buf, sizeof(buf) - 1, - "GET %s/appsvc/%s?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n" - "Host: " GG_APPMSG_HOST "\r\n" - "User-Agent: " GG_HTTP_USERAGENT "\r\n" - "Pragma: no-cache\r\n" - "%s" - "\r\n", host, appmsg, sess->uin, client, sess->last_sysmsg, (auth) ? auth : ""); - - if (auth) - free(auth); - + { + snprintf(buf, sizeof(buf) - 1, + "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); - /* zwolnij pami po wersji klienta. */ + /* zwolnij pamiト卞 po wersji klienta. */ if (sess->client_version) { free(sess->client_version); sess->client_version = NULL; } - gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf); - - /* zapytanie jest krtkie, wi鹹 zawsze zmieカci si - * do bufora gniazda. jeカli write() zwrci mniej, - * staウo si coカ zウego. */ + gg_debug_session(sess, GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf); + + /* zapytanie jest krテウtkie, wiト冂 zawsze zmieナ嫩i siト + * do bufora gniazda. jeナ嬪i write() zwrテウci mniej, + * staナP siト coナ zナFgo. */ if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n"); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_WRITING; @@ -999,72 +1625,36 @@ int port = GG_DEFAULT_PORT; struct in_addr addr; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n"); - - /* czytamy lini z gniazda i obcinamy \r\n. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n"); + + /* czytamy liniト z gniazda i obcinamy \r\n. */ gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); - gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf); - - /* sprawdzamy, czy wszystko w porzアdku. */ + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf); + + /* sprawdzamy, czy wszystko w porzトdku. */ if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() that's not what we've expected, trying direct connection\n"); - - close(sess->fd); - - /* jeカli otrzymaliカmy jakieカ dziwne informacje, - * prbujemy si ウアczy z pomini鹹iem huba. */ - if (sess->proxy_addr && sess->proxy_port) { - if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { - /* trudno. nie wyszウo. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail_connecting; - } - - sess->state = GG_STATE_CONNECTING_GG; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - break; - } - - sess->port = GG_DEFAULT_PORT; - - /* ウアczymy si na port 8074 huba. */ - if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); - - sess->port = GG_HTTPS_PORT; - - /* ウアczymy si na port 443. */ - if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail_connecting; - } - } - - sess->state = GG_STATE_CONNECTING_GG; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - break; + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n"); + goto fail_connecting; } - - /* ignorujemy reszt nagウwka. */ + + /* ignorujemy resztト nagナづウwka. */ while (strcmp(buf, "\r\n") && strcmp(buf, "")) gg_read_line(sess->fd, buf, sizeof(buf) - 1); - /* czytamy pierwszア lini danych. */ + /* czytamy pierwszト liniト danych. */ gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); - - /* jeカli pierwsza liczba w linii nie jest rwna zeru, - * oznacza to, ソe mamy wiadomoカ systemowア. */ + + /* jeナ嬪i pierwsza liczba w linii nie jest rテウwna zeru, + * oznacza to, ナシe mamy wiadomoナ崙 systemowト. */ if (atoi(buf)) { char tmp[1024], *foo, *sysmsg_buf = NULL; int len = 0; - + while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) { if (!(foo = realloc(sysmsg_buf, len + strlen(tmp) + 2))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n"); break; } @@ -1074,23 +1664,32 @@ strcpy(sysmsg_buf, tmp); else strcat(sysmsg_buf, tmp); - + len += strlen(tmp); } - + e->type = GG_EVENT_MSG; e->event.msg.msgclass = atoi(buf); e->event.msg.sender = 0; - e->event.msg.message = (unsigned char *)sysmsg_buf; + e->event.msg.message = (unsigned char*) sysmsg_buf; } - + close(sess->fd); - - gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf); + + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf); /* analizujemy otrzymane dane. */ tmp = buf; - + +#ifdef GG_CONFIG_HAVE_OPENSSL + if (!sess->ssl) +#endif + { + while (*tmp && *tmp != ' ') + tmp++; + while (*tmp && *tmp == ' ') + tmp++; + } while (*tmp && *tmp != ' ') tmp++; while (*tmp && *tmp == ' ') @@ -1105,36 +1704,43 @@ port = atoi(tmp + 1); } + if (!strcmp(host, "notoperating")) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno)); + sess->fd = -1; + goto fail_unavailable; + } + addr.s_addr = inet_addr(host); sess->server_addr = addr.s_addr; if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) { - /* jeカli mamy proxy, ウアczymy si z nim. */ + /* jeナ嬪i mamy proxy, ナてczymy siト z nim. */ if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { - /* nie wyszウo? trudno. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); + /* nie wyszナP? trudno. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } - + sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; + sess->soft_timeout = 1; break; } sess->port = port; - /* ウアczymy si z wウaカciwym serwerem. */ + /* ナてczymy siト z wナBナ嫩iwym serwerem. */ if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); sess->port = GG_HTTPS_PORT; - /* nie wyszウo? prbujemy portu 443. */ + /* nie wyszナP? prテウbujemy portu 443. */ if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) { - /* ostatnia deska ratunku zawiodウa? + /* ostatnia deska ratunku zawiodナB? * w takim razie zwijamy manatki. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } } @@ -1142,7 +1748,8 @@ sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; - + sess->soft_timeout = 1; + break; } @@ -1151,14 +1758,16 @@ int res = 0; socklen_t res_size = sizeof(res); - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n"); - - /* jeカli wystアpiウ bウアd podczas ウアczenia si... */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n"); + + sess->soft_timeout = 0; + + /* jeナ嬪i wystトpiナ bナてd podczas ナてczenia siト... */ if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { - /* jeカli nie udaウo si poウアczenie z proxy, - * nie mamy czego prbowa wi鹹ej. */ + /* jeナ嬪i nie udaナP siト poナてczenie z proxy, + * nie mamy czego prテウbowaト wiト冂ej. */ if (sess->proxy_addr && sess->proxy_port) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); goto fail_connecting; } @@ -1170,36 +1779,46 @@ errno = ETIMEDOUT; #endif -#ifdef __GG_LIBGADU_HAVE_OPENSSL - /* jeカli logujemy si po TLS, nie prbujemy - * si ウアczy juソ z niczym innym w przypadku - * bウ鹽u. nie doカ, ソe nie ma sensu, to i - * trzeba by si bawi w tworzenie na nowo +#ifdef GG_CONFIG_HAVE_OPENSSL + /* jeナ嬪i logujemy siト po TLS, nie prテウbujemy + * siト ナてczyト juナシ z niczym innym w przypadku + * bナて囘u. nie doナ崙, ナシe nie ma sensu, to i + * trzeba by siト bawiト w tworzenie na nowo * SSL i SSL_CTX. */ if (sess->ssl) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); goto fail_connecting; } #endif - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res)); + + if (sess->port == GG_HTTPS_PORT) + goto fail_connecting; sess->port = GG_HTTPS_PORT; - /* prbujemy na port 443. */ + /* prテウbujemy na port 443. */ if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } + + sess->state = GG_STATE_CONNECTING_GG; + sess->check = GG_CHECK_WRITE; + sess->timeout = GG_DEFAULT_TIMEOUT; + sess->soft_timeout = 1; + + break; } - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected\n"); - + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n"); + if (gg_proxy_http_only) sess->proxy_port = 0; - /* jeカli mamy proxy, wyカlijmy zapytanie. */ + /* jeナ嬪i mamy proxy, wyナ嬪ijmy zapytanie. */ if (sess->proxy_addr && sess->proxy_port) { char buf[100], *auth = gg_proxy_auth(); struct in_addr addr; @@ -1211,20 +1830,22 @@ snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port); - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n// %s", buf); - - /* wysyウamy zapytanie. jest ono na tyle krtkie, - * ソe musi si zmieカci w buforze gniazda. jeカli - * write() zawiedzie, staウo si coカ zウego. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n// %s", buf); + + /* wysyナBmy zapytanie. jest ono na tyle krテウtkie, + * ナシe musi siト zmieナ嫩iト w buforze gniazda. jeナ嬪i + * write() zawiedzie, staナP siト coナ zナFgo. */ if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + free(auth); goto fail_connecting; } if (auth) { - gg_debug(GG_DEBUG_MISC, "// %s", auth); + gg_debug_session(sess, GG_DEBUG_MISC, "// %s", auth); if (write(sess->fd, auth, strlen(auth)) < (signed)strlen(auth)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + free(auth); goto fail_connecting; } @@ -1232,12 +1853,12 @@ } if (write(sess->fd, "\r\n", 2) < 2) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); goto fail_connecting; } } -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL if (sess->ssl) { SSL_set_fd(sess->ssl, sess->fd); @@ -1256,19 +1877,19 @@ break; } -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL case GG_STATE_TLS_NEGOTIATION: { int res; X509 *peer; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n"); if ((res = SSL_connect(sess->ssl)) <= 0) { int err = SSL_get_error(sess->ssl, res); if (res == 0) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n"); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_TLS; @@ -1277,9 +1898,9 @@ sess->fd = -1; break; } - + if (err == SSL_ERROR_WANT_READ) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n"); sess->state = GG_STATE_TLS_NEGOTIATION; sess->check = GG_CHECK_READ; @@ -1287,7 +1908,7 @@ break; } else if (err == SSL_ERROR_WANT_WRITE) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n"); sess->state = GG_STATE_TLS_NEGOTIATION; sess->check = GG_CHECK_WRITE; @@ -1299,8 +1920,8 @@ ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf); - + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf); + e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_TLS; sess->state = GG_STATE_IDLE; @@ -1310,20 +1931,20 @@ } } - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n// cipher: %s\n", SSL_get_cipher_name(sess->ssl)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n// cipher: %s\n", SSL_get_cipher_name(sess->ssl)); peer = SSL_get_peer_certificate(sess->ssl); if (!peer) - gg_debug(GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n"); else { char buf[1024]; X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf)); - gg_debug(GG_DEBUG_MISC, "// cert subject: %s\n", 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(GG_DEBUG_MISC, "// cert issuer: %s\n", buf); + gg_debug_session(sess, GG_DEBUG_MISC, "// cert issuer: %s\n", buf); } sess->state = GG_STATE_READING_KEY; @@ -1336,45 +1957,48 @@ case GG_STATE_READING_KEY: { - struct gg_header *h; + struct gg_header *h; struct gg_welcome *w; - struct gg_login60 l; - unsigned int hash; - char *password = sess->password; + unsigned char *password = (unsigned char*) sess->password; int ret; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n"); - - memset(&l, 0, sizeof(l)); - l.dunno2 = 0xbe; - - /* XXX bardzo, bardzo, bardzo gウupi pomysウ na pozbycie - * si tekstu wrzucanego przez proxy. */ + uint8_t login_hash[64]; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n"); + + memset(login_hash, 0, sizeof(login_hash)); + + /* XXX bardzo, bardzo, bardzo gナVpi pomysナ na pozbycie + * siト tekstu wrzucanego przez proxy. */ if (sess->proxy_addr && sess->proxy_port) { char buf[100]; strcpy(buf, ""); gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n// %s\n", buf); - + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n// %s\n", buf); + while (strcmp(buf, "")) { gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); if (strcmp(buf, "")) - gg_debug(GG_DEBUG_MISC, "// %s\n", buf); + gg_debug_session(sess, GG_DEBUG_MISC, "// %s\n", buf); } /* XXX niech czeka jeszcze raz w tej samej - * fazie. gウupio, ale dziaウa. */ + * fazie. gナVpio, ale dziaナB. */ sess->proxy_port = 0; - + break; } /* czytaj pierwszy pakiet. */ if (!(h = gg_recv_packet(sess))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); + if (errno == EAGAIN) { + sess->check = GG_CHECK_READ; + break; + } + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_READING; @@ -1387,7 +2011,7 @@ } if (h->type != GG_WELCOME) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n"); free(h); close(sess->fd); sess->fd = -1; @@ -1397,61 +2021,127 @@ sess->state = GG_STATE_IDLE; break; } - + w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header)); w->key = gg_fix32(w->key); - hash = gg_login_hash((unsigned char *)password, w->key); - - gg_debug(GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> hash %.8x\n", w->key, hash); - + switch (sess->hash_type) { + case GG_LOGIN_HASH_GG32: + { + unsigned int hash; + + hash = gg_fix32(gg_login_hash(password, w->key)); + gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> GG32 hash %.8x\n", w->key, hash); + memcpy(login_hash, &hash, sizeof(hash)); + + break; + } + + case GG_LOGIN_HASH_SHA1: + { + char tmp[41]; + int i; + + gg_login_hash_sha1((char*) password, w->key, login_hash); + for (i = 0; i < 40; i += 2) + snprintf(tmp + i, sizeof(tmp) - i, "%02x", login_hash[i / 2]); + + gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> SHA1 hash: %s\n", w->key, tmp); + + break; + } + } + free(h); - free(sess->password); sess->password = NULL; - { - struct in_addr dcc_ip; - dcc_ip.s_addr = gg_dcc_ip; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() gg_dcc_ip = %s\n", inet_ntoa(dcc_ip)); - } - if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) { struct sockaddr_in sin; socklen_t sin_len = sizeof(sin); - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n"); if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr)); - l.local_ip = sin.sin_addr.s_addr; + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr)); + sess->client_addr = sin.sin_addr.s_addr; } else { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n"); - l.local_ip = 0; + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n"); + sess->client_addr = 0; } - } else - l.local_ip = gg_dcc_ip; - - l.uin = gg_fix32(sess->uin); - l.hash = gg_fix32(hash); - l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); - l.version = gg_fix32(sess->protocol_version); - l.local_port = gg_fix16(gg_dcc_port); - l.image_size = sess->image_size; - - if (sess->external_addr && sess->external_port > 1023) { - l.external_ip = sess->external_addr; - l.external_port = gg_fix16(sess->external_port); + } else + sess->client_addr = gg_dcc_ip; + + if (sess->protocol_version >= 0x2e) { + struct gg_login80 l; + + uint32_t tmp_version_len = gg_fix32(strlen(GG8_VERSION)); + uint32_t tmp_descr_len = gg_fix32((sess->initial_descr) ? strlen(sess->initial_descr) : 0); + + memset(&l, 0, sizeof(l)); + l.uin = gg_fix32(sess->uin); + memcpy(l.language, GG8_LANG, sizeof(l.language)); + l.hash_type = sess->hash_type; + memcpy(l.hash, login_hash, sizeof(login_hash)); + l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); + l.flags = gg_fix32(0x00800001); + l.features = gg_fix32(sess->protocol_features); + l.image_size = sess->image_size; + l.dunno2 = 0x64; + + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80 packet\n"); + ret = gg_send_packet(sess, GG_LOGIN80, + &l, sizeof(l), + &tmp_version_len, sizeof(uint32_t), GG8_VERSION, strlen(GG8_VERSION), + &tmp_descr_len, sizeof(uint32_t), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, + NULL); + + } else if (sess->protocol_version == 0x2d) { + struct gg_login70 l; + + memset(&l, 0, sizeof(l)); + l.uin = gg_fix32(sess->uin); + l.local_ip = (sess->external_addr) ? sess->external_addr : sess->client_addr; + l.local_port = gg_fix16((sess->external_port > 1023) ? sess->external_port : gg_dcc_port); + l.hash_type = sess->hash_type; + memcpy(l.hash, login_hash, sizeof(login_hash)); + l.image_size = sess->image_size; + l.dunno2 = 0x64; + l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); + l.version = gg_fix32(sess->protocol_version | sess->protocol_flags); + + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80BETA packet\n"); + ret = gg_send_packet(sess, GG_LOGIN80BETA, + &l, sizeof(l), + sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, + (sess->initial_descr) ? "\0" : NULL, (sess->initial_descr) ? 1 : 0, + NULL); + } else { + struct gg_login70 l; + + memset(&l, 0, sizeof(l)); + l.local_ip = (sess->external_addr) ? sess->external_addr : sess->client_addr; + l.uin = gg_fix32(sess->uin); + l.local_port = gg_fix16((sess->external_port > 1023) ? sess->external_port : gg_dcc_port); + l.hash_type = sess->hash_type; + memcpy(l.hash, login_hash, sizeof(login_hash)); + l.image_size = sess->image_size; + l.dunno2 = 0xbe; + l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); + l.version = gg_fix32(sess->protocol_version | sess->protocol_flags); + + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN70 packet\n"); + ret = gg_send_packet(sess, GG_LOGIN70, + &l, sizeof(l), + sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, + NULL); } - gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN60 packet\n"); - ret = gg_send_packet(sess, GG_LOGIN60, &l, sizeof(l), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, NULL); - free(sess->initial_descr); sess->initial_descr = NULL; if (ret == -1) { - gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno)); errno2 = errno; close(sess->fd); errno = errno2; @@ -1461,8 +2151,9 @@ sess->state = GG_STATE_IDLE; break; } - + sess->state = GG_STATE_READING_REPLY; + sess->check = GG_CHECK_READ; break; } @@ -1471,10 +2162,15 @@ { struct gg_header *h; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n"); if (!(h = gg_recv_packet(sess))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); + if (errno == EAGAIN) { + sess->check = GG_CHECK_READ; + break; + } + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_READING; sess->state = GG_STATE_IDLE; @@ -1484,11 +2180,12 @@ sess->fd = -1; break; } - - if (h->type == GG_LOGIN_OK) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n"); + + if (h->type == GG_LOGIN_OK || h->type == GG_NEED_EMAIL || h->type == GG_LOGIN80_OK) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n"); e->type = GG_EVENT_CONN_SUCCESS; sess->state = GG_STATE_CONNECTED; + sess->check = GG_CHECK_READ; sess->timeout = -1; sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL; free(h); @@ -1496,15 +2193,15 @@ } if (h->type == GG_LOGIN_FAILED) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login failed\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login failed\n"); e->event.failure = GG_FAILURE_PASSWORD; errno = EACCES; - } else if (h->type == GG_NEED_EMAIL) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() email change needed\n"); - e->event.failure = GG_FAILURE_NEED_EMAIL; + } else if (h->type == GG_DISCONNECTING) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n"); + e->event.failure = GG_FAILURE_INTRUDER; errno = EACCES; } else { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n"); e->event.failure = GG_FAILURE_INVALID; errno = EINVAL; } @@ -1522,13 +2219,13 @@ case GG_STATE_CONNECTED: { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n"); sess->last_event = time(NULL); - + if ((res = gg_watch_fd_connected(sess, e)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno)); if (errno == EAGAIN) { e->type = GG_EVENT_NONE; @@ -1536,6 +2233,9 @@ } else res = -1; } + + sess->check = GG_CHECK_READ; + break; } } @@ -1544,10 +2244,13 @@ if (res == -1) { free(e); e = NULL; + } else { + if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED)) + sess->check |= GG_CHECK_WRITE; } return e; - + fail_connecting: if (sess->fd != -1) { errno2 = errno; @@ -1565,6 +2268,12 @@ e->event.failure = GG_FAILURE_RESOLVING; sess->state = GG_STATE_IDLE; goto done; + +fail_unavailable: + e->type = GG_EVENT_CONN_FAILED; + e->event.failure = GG_FAILURE_UNAVAILABLE; + sess->state = GG_STATE_IDLE; + goto done; } /*
--- a/libpurple/protocols/gg/lib/http.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/gg/lib/http.c Tue Mar 16 12:07:06 2010 +0900 @@ -1,4 +1,4 @@ -/* $Id: http.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: http.c 833 2009-10-01 20:48:01Z wojtekka $ */ /* * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> @@ -14,52 +14,68 @@ * * 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 Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ +/** + * \file http.c + * + * \brief ObsナVga poナてczeナ HTTP + */ + #include "libgadu.h" #include <sys/types.h> + #ifndef _WIN32 -#include <sys/wait.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> #endif -#include "libgadu-config.h" +#include "compat.h" +#include "resolver.h" #include <ctype.h> #include <errno.h> + #ifndef _WIN32 -#include <netdb.h> +# include <netdb.h> #endif -#ifdef __GG_LIBGADU_HAVE_PTHREAD -# include <pthread.h> -#endif + +#include <signal.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include "compat.h" - -/* - * gg_http_connect() // funkcja pomocnicza +/** + * Rozpoczyna poナてczenie HTTP. * - * rozpoczyna poウアczenie po 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ト囘ト przeprowadzane po wykryciu zmian (\c watch) na obserwowanym + * deskryptorze (\c fd) i wywoナBniu funkcji \c gg_http_watch_fd(). * - * - hostname - adres serwera - * - port - port serwera - * - async - asynchroniczne poウアczenie - * - method - metoda http (GET, POST, cokolwiek) - * - path - カcieソka do zasobu (musi by poprzedzona ,,/'') - * - header - nagウwek zapytania plus ewentualne dane dla POST + * 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(). * - * zaalokowana struct gg_http, ktrア poシniej naleソy - * zwolni funkcjア gg_http_free(), albo NULL jeカli wystアpiウ bウアd. + * \param hostname Adres serwera + * \param port Port serwera + * \param async Flaga asynchronicznego poナてczenia + * \param method Metoda HTTP + * \param path ナ喞ieナシ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ナ嬪i 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) { @@ -70,7 +86,7 @@ errno = EFAULT; return NULL; } - + if (!(h = malloc(sizeof(*h)))) return NULL; memset(h, 0, sizeof(*h)); @@ -80,6 +96,8 @@ 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(); @@ -88,9 +106,8 @@ "", header); hostname = gg_proxy_host; h->port = port = gg_proxy_port; + free(auth); - if (auth) - free(auth); } else { h->query = gg_saprintf("%s %s HTTP/1.0\r\n%s", method, path, header); @@ -102,17 +119,11 @@ errno = ENOMEM; return NULL; } - + gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h->query); if (async) { -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (gg_resolve_pthread(&h->fd, &h->resolver, hostname)) { -#elif defined _WIN32 - if (gg_resolve_win32thread(&h->fd, &h->resolver, hostname)) { -#else - if (gg_resolve(&h->fd, &h->pid, hostname)) { -#endif + 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; @@ -125,19 +136,16 @@ h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; } else { - struct in_addr *hn, a; + struct in_addr addr; - if (!(hn = gg_gethostbyname(hostname))) { + if (gg_gethostbyname_real(hostname, &addr, 0) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_http_connect() host not found\n"); gg_http_free(h); errno = ENOENT; return NULL; - } else { - a.s_addr = hn->s_addr; - free(hn); } - if (!(h->fd = gg_connect(&a, port, 0)) == -1) { + if ((h->fd = gg_connect(&addr, port, 0)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_http_connect() connection failed (errno=%d, %s)\n", errno, strerror(errno)); gg_http_free(h); return NULL; @@ -159,10 +167,12 @@ h->callback = gg_http_watch_fd; h->destroy = gg_http_free; - + return h; } +#ifndef DOXYGEN + #define gg_http_error(x) \ close(h->fd); \ h->fd = -1; \ @@ -170,17 +180,22 @@ h->error = x; \ return 0; -/* - * gg_http_watch_fd() - * - * przy asynchronicznej obsウudze HTTP funkcj tア naleソy wywoウa, jeカli - * zmieniウo si coカ na obserwowanym deskryptorze. +#endif /* DOXYGEN */ + +/** + * Funkcja wywoナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia. * - * - h - struktura opisujアca poウアczenie + * Operacja bト囘zie zakoナczona, gdy pole \c state bト囘zie rテウwne + * \c GG_STATE_PARSING. W tym miejscu dziaナBnie przejmuje zwykle funkcja + * korzystajトca z \c gg_http_watch_fd(). W przypadku bナて囘u poナてczenia, + * pole \c state bト囘zie rテウwne \c GG_STATE_ERROR, a kod bナて囘u znajdzie siト + * w polu \c error. * - * jeカli wszystko poszウo dobrze to 0, inaczej -1. poウアczenie b鹽zie - * zakoczone, jeカli h->state == GG_STATE_PARSING. jeカli wystアpi jakiカ - * bウアd, to b鹽zie tam GG_STATE_ERROR i odpowiedni kod bウ鹽u w h->error. + * \param h Struktura poナてczenia + * + * \return \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup http */ int gg_http_watch_fd(struct gg_http *h) { @@ -205,22 +220,7 @@ close(h->fd); h->fd = -1; -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (h->resolver) { - pthread_cancel(*((pthread_t *) h->resolver)); - free(h->resolver); - h->resolver = NULL; - } -#elif defined _WIN32 - if (h->resolver) { - HANDLE hnd = h->resolver; - TerminateThread(hnd, 0); - CloseHandle(hnd); - h->resolver = NULL; - } -#else - waitpid(h->pid, NULL, 0); -#endif + h->resolver_cleanup(&h->resolver, 0); gg_debug(GG_DEBUG_MISC, "=> http, connecting to %s:%d\n", inet_ntoa(a), h->port); @@ -264,7 +264,7 @@ gg_http_error(GG_ERROR_WRITING); } - if (res < 0 || (size_t)res < strlen(h->query)) { + if (res < strlen(h->query)) { gg_debug(GG_DEBUG_MISC, "=> http, partial header sent (led=%d, sent=%d)\n", strlen(h->query), res); memmove(h->query, h->query + res, strlen(h->query) - res + 1); @@ -346,7 +346,7 @@ 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) { @@ -449,7 +449,7 @@ return 0; } - + if (h->fd != -1) close(h->fd); @@ -460,14 +460,14 @@ return -1; } -#undef gg_http_error - -/* - * gg_http_stop() +/** + * Koナczy asynchroniczne poナてczenie HTTP. * - * jeカli poウアczenie jest w trakcie, przerywa je. nie zwalnia h->data. - * - * - h - struktura opisujアca poウアczenie + * 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) { @@ -477,15 +477,20 @@ if (h->state == GG_STATE_ERROR || h->state == GG_STATE_DONE) return; - if (h->fd != -1) + if (h->fd != -1) { close(h->fd); - h->fd = -1; + h->fd = -1; + } + + h->resolver_cleanup(&h->resolver, 1); } -/* - * gg_http_free_fields() // funkcja wewn黎rzna +/** + * \internal Zwalnia pola struktury \c gg_http. * - * zwalnia pola struct gg_http, ale nie zwalnia samej struktury. + * Funkcja zwalnia same pola, nie zwalnia struktury. + * + * \param h Struktura poナてczenia */ void gg_http_free_fields(struct gg_http *h) { @@ -501,19 +506,21 @@ free(h->query); h->query = NULL; } - + if (h->header) { free(h->header); h->header = NULL; } } -/* - * gg_http_free() +/** + * Zwalnia zasoby po poナてczeniu HTTP. * - * prbuje zamknア poウアczenie i zwalnia pami po nim. + * Jeナ嬪i poナてczenie nie zostaナP jeszcze zakoナczone, jest przerywane. * - * - h - struktura, ktrア naleソy zlikwidowa + * \param h Struktura poナてczenia + * + * \ingroup http */ void gg_http_free(struct gg_http *h) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/libgadu-internal.h Tue Mar 16 12:07:06 2010 +0900 @@ -0,0 +1,30 @@ +/* $Id$ */ + +/* + * (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef LIBGADU_INTERNAL_H +#define LIBGADU_INTERNAL_H + +#include "libgadu.h" + +char *gg_cp_to_utf8(const char *b); +char *gg_utf8_to_cp(const char *b); +int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length); + +#endif /* LIBGADU_INTERNAL_H */
--- a/libpurple/protocols/gg/lib/libgadu.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/gg/lib/libgadu.c Tue Mar 16 12:07:06 2010 +0900 @@ -1,10 +1,11 @@ -/* $Id: libgadu.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: libgadu.c 878 2009-11-16 23:48:19Z wojtekka $ */ /* - * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Woシny <speedy@ziew.org> - * Arkadiusz Miカkiewicz <arekm@pld-linux.org> - * Tomasz Chiliski <chilek@chilan.com> + * (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl> + * Robert J. Woナコny <speedy@ziew.org> + * Arkadiusz Miナ嫐iewicz <arekm@pld-linux.org> + * Tomasz Chiliナгki <chilek@chilan.com> + * Adam Wysocki <gophi@ekg.chmurka.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -17,227 +18,200 @@ * * 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 Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ +/** + * \file libgadu.c + * + * \brief Gナづウwny moduナ biblioteki + */ + #include "libgadu.h" +#include "libgadu-config.h" +#include "libgadu-internal.h" #include <sys/types.h> -#ifndef _WIN32 -#include <sys/wait.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#ifdef sun -# include <sys/filio.h> -#endif + +#ifdef _WIN32 +# include <io.h> +# include <fcntl.h> +# include <errno.h> +# define SHUT_RDWR SD_BOTH #else -#include <io.h> -#include <fcntl.h> -#include <errno.h> -#define SHUT_RDWR SD_BOTH +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# ifdef sun +# include <sys/filio.h> +# endif #endif -#include "libgadu-config.h" +#include "compat.h" +#include "protocol.h" +#include "resolver.h" -#include <errno.h> #ifndef _WIN32 -#include <netdb.h> +# include <errno.h> /* on Win32 this is included above */ +# include <netdb.h> #endif -#ifdef __GG_LIBGADU_HAVE_PTHREAD -# include <pthread.h> -#endif + #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <signal.h> +#include <time.h> #include <unistd.h> -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL # include <openssl/err.h> # include <openssl/rand.h> #endif -#include "compat.h" +#define GG_LIBGADU_VERSION "1.9.0-rc2" + +/** + * Poziom rejestracji informacji odpluskwiajトcych. Zmienna jest maskト bitowト + * skナBdajトcト siト ze staナZch \c GG_DEBUG_... + * + * \ingroup debug + */ +int gg_debug_level = 0; -int gg_debug_level = 0; +/** + * Funkcja, do ktテウrej sト przekazywane informacje odpluskwiajトce. Jeナ嬪i zarテウwno + * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, sト rテウwne + * \c NULL, informacje sト wysyナBne do standardowego wyjナ嫩ia bナて囘u (\c stderr). + * + * \param level Poziom rejestracji + * \param format Format wiadomoナ嫩i (zgodny z \c printf) + * \param ap Lista argumentテウw (zgodna z \c printf) + * + * \note Funkcja jest przesナBniana 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ナ嬪i zarテウwno + * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, sト rテウwne + * \c NULL, informacje sト wysyナBne do standardowego wyjナ嫩ia bナて囘u. + * + * \param sess Sesja ktテウrej dotyczy informacja lub \c NULL + * \param level Poziom rejestracji + * \param format Format wiadomoナ嫩i (zgodny z \c printf) + * \param ap Lista argumentテウw (zgodna z \c printf) + * + * \note Funkcja przesナBnia 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; + +/** + * Port gniazda nasナVchujトcego dla poナてczeナ bezpoナ孑ednich. + * + * \ingroup ip + */ int gg_dcc_port = 0; + +/** + * Adres IP gniazda nasナVchujトcego dla poナてczeナ bezpoナ孑ednich. + * + * \ingroup ip + */ unsigned long gg_dcc_ip = 0; +/** + * Adres lokalnego interfejsu IP, z ktテウrego wywoナZwane sト wszystkie poナてczenia. + * + * \ingroup ip + */ unsigned long gg_local_ip = 0; -/* - * zmienne opisujアce parametry proxy http. + +/** + * Flaga wナてczenia poナてczeナ przez serwer poナ孑edniczトcy. + * + * \ingroup proxy + */ +int gg_proxy_enabled = 0; + +/** + * Adres serwera poナ孑edniczトcego. + * + * \ingroup proxy */ char *gg_proxy_host = NULL; + +/** + * Port serwera poナ孑edniczトcego. + * + * \ingroup proxy + */ int gg_proxy_port = 0; -int gg_proxy_enabled = 0; + +/** + * Flaga uナシywania serwera poナ孑edniczトcego jedynie dla usナVg HTTP. + * + * \ingroup proxy + */ int gg_proxy_http_only = 0; + +/** + * Nazwa uナシytkownika do autoryzacji serwera poナ孑edniczトcego. + * + * \ingroup proxy + */ char *gg_proxy_username = NULL; + +/** + * HasナP uナシytkownika do autoryzacji serwera poナ孑edniczトcego. + * + * \ingroup proxy + */ char *gg_proxy_password = NULL; -#ifndef lint +#ifndef DOXYGEN + +#ifndef lint static char rcsid[] #ifdef __GNUC__ __attribute__ ((unused)) #endif -= "$Id: libgadu.c 16856 2006-08-19 01:13:25Z evands $"; -#endif - -#ifdef _WIN32 -/** - * Deal with the fact that you can't select() on a win32 file fd. - * This makes it practically impossible to tie into purple's event loop. - * - * -This is thanks to Tor Lillqvist. - * XXX - Move this to where the rest of the the win32 compatiblity stuff goes when we push the changes back to libgadu. - */ -static int -socket_pipe (int *fds) -{ - SOCKET temp, socket1 = -1, socket2 = -1; - struct sockaddr_in saddr; - int len; - u_long arg; - fd_set read_set, write_set; - struct timeval tv; - - temp = socket(AF_INET, SOCK_STREAM, 0); - - if (temp == INVALID_SOCKET) { - goto out0; - } - - arg = 1; - if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) { - goto out0; - } - - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = 0; - saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) { - goto out0; - } - - if (listen(temp, 1) == SOCKET_ERROR) { - goto out0; - } - - len = sizeof(saddr); - if (getsockname(temp, (struct sockaddr *)&saddr, &len)) { - goto out0; - } - - socket1 = socket(AF_INET, SOCK_STREAM, 0); - - if (socket1 == INVALID_SOCKET) { - goto out0; - } - - arg = 1; - if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { - goto out1; - } - - if (connect(socket1, (struct sockaddr *)&saddr, len) != SOCKET_ERROR || - WSAGetLastError() != WSAEWOULDBLOCK) { - goto out1; - } - - FD_ZERO(&read_set); - FD_SET(temp, &read_set); - - tv.tv_sec = 0; - tv.tv_usec = 0; - - if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) { - goto out1; - } - - if (!FD_ISSET(temp, &read_set)) { - goto out1; - } - - socket2 = accept(temp, (struct sockaddr *) &saddr, &len); - if (socket2 == INVALID_SOCKET) { - goto out1; - } - - FD_ZERO(&write_set); - FD_SET(socket1, &write_set); - - tv.tv_sec = 0; - tv.tv_usec = 0; - - if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) { - goto out2; - } - - if (!FD_ISSET(socket1, &write_set)) { - goto out2; - } - - arg = 0; - if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { - goto out2; - } - - arg = 0; - if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) { - goto out2; - } - - fds[0] = socket1; - fds[1] = socket2; - - closesocket (temp); - - return 0; - -out2: - closesocket (socket2); -out1: - closesocket (socket1); -out0: - closesocket (temp); - errno = EIO; /* XXX */ - - return -1; -} += "$Id: libgadu.c 878 2009-11-16 23:48:19Z wojtekka $"; #endif -/* - * gg_libgadu_version() +#endif /* DOXYGEN */ + +/** + * Zwraca wersjト biblioteki. * - * zwraca wersj libgadu. + * \return Wskaナコnik na statyczny bufor z wersjト biblioteki. * - * - brak - * - * wersja libgadu. + * \ingroup version */ const char *gg_libgadu_version() { return GG_LIBGADU_VERSION; } -/* - * gg_fix32() +/** + * \internal Zamienia kolejnoナ崙 bajtテウw w 32-bitowym sナPwie. + * + * Ze wzglト囘u na little-endianowoナ崙 protokoナV Gadu-Gadu, na maszynach + * big-endianowych odwraca kolejnoナ崙 bajtテウw w sナPwie. * - * zamienia kolejnoカ bajtw w liczbie 32-bitowej tak, by odpowiadaウa - * kolejnoカci bajtw w protokole GG. ze wzgl鹽u na LE-owoカ serwera, - * zamienia tylko na maszynach BE-wych. + * \param x Liczba do zamiany * - * - x - liczba do zamiany + * \return Liczba z odpowiedniト kolejnoナ嫩iト bajtテウw * - * liczba z odpowiedniア kolejnoカciア bajtw. + * \ingroup helper */ uint32_t gg_fix32(uint32_t x) { -#ifndef __GG_LIBGADU_BIGENDIAN +#ifndef GG_CONFIG_BIGENDIAN return x; #else return (uint32_t) @@ -248,20 +222,21 @@ #endif } -/* - * gg_fix16() +/** + * \internal Zamienia kolejnoナ崙 bajtテウw w 16-bitowym sナPwie. + * + * Ze wzglト囘u na little-endianowoナ崙 protokoナV Gadu-Gadu, na maszynach + * big-endianowych zamienia kolejnoナ崙 bajtテウw w sナPwie. * - * zamienia kolejnoカ bajtw w liczbie 16-bitowej tak, by odpowiadaウa - * kolejnoカci bajtw w protokole GG. ze wzgl鹽u na LE-owoカ serwera, - * zamienia tylko na maszynach BE-wych. + * \param x Liczba do zamiany * - * - x - liczba do zamiany + * \return Liczba z odpowiedniト kolejnoナ嫩iト bajtテウw * - * liczba z odpowiedniア kolejnoカciア bajtw. + * \ingroup helper */ uint16_t gg_fix16(uint16_t x) { -#ifndef __GG_LIBGADU_BIGENDIAN +#ifndef GG_CONFIG_BIGENDIAN return x; #else return (uint16_t) @@ -270,15 +245,13 @@ #endif } -/* - * gg_login_hash() // funkcja wewn黎rzna - * - * liczy hash z hasウa i danego seeda. - * - * - password - hasウo do hashowania - * - seed - wartoカ podana przez serwer +/** + * \internal Liczy skrテウt z hasナB i ziarna. * - * hash. + * \param password HasナP + * \param seed Ziarno podane przez serwer + * + * \return Wartoナ崙 skrテウtu */ unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) { @@ -304,313 +277,22 @@ return y; } -#ifndef _WIN32 - -/* - * gg_resolve() // funkcja wewn黎rzna - * - * tworzy potok, forkuje si i w drugim procesie zaczyna resolvowa - * podanego hosta. zapisuje w sesji deskryptor potoku. jeカli coカ tam - * b鹽zie gotowego, znaczy, ソe moソna wczyta struct in_addr. jeカli - * nie znajdzie, zwraca INADDR_NONE. - * - * - fd - wskaシnik gdzie wrzuci deskryptor - * - pid - gdzie wrzuci pid procesu potomnego - * - hostname - nazwa hosta do zresolvowania +/** + * \internal Odbiera od serwera dane binarne. * - * 0, -1. - */ -int gg_resolve(int *fd, int *pid, const char *hostname) -{ - int pipes[2], res; - struct in_addr a; - int errno2; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve(%p, %p, \"%s\");\n", fd, pid, hostname); - - if (!fd || !pid) { - errno = EFAULT; - return -1; - } - - if (pipe(pipes) == -1) - return -1; - - if ((res = fork()) == -1) { - errno2 = errno; - close(pipes[0]); - close(pipes[1]); - errno = errno2; - return -1; - } - - if (!res) { - if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) { - struct in_addr *hn; - - if (!(hn = gg_gethostbyname(hostname))) - a.s_addr = INADDR_NONE; - else { - a.s_addr = hn->s_addr; - free(hn); - } - } - - write(pipes[1], &a, sizeof(a)); - - _exit(0); - } - - close(pipes[1]); - - *fd = pipes[0]; - *pid = res; - - return 0; -} -#endif - -#ifdef __GG_LIBGADU_HAVE_PTHREAD - -struct gg_resolve_pthread_data { - char *hostname; - int fd; -}; - -static void *gg_resolve_pthread_thread(void *arg) -{ - struct gg_resolve_pthread_data *d = arg; - struct in_addr a; - - pthread_detach(pthread_self()); - - if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { - struct in_addr *hn; - - if (!(hn = gg_gethostbyname(d->hostname))) - a.s_addr = INADDR_NONE; - else { - a.s_addr = hn->s_addr; - free(hn); - } - } - - write(d->fd, &a, sizeof(a)); - close(d->fd); - - free(d->hostname); - d->hostname = NULL; - - free(d); - - pthread_exit(NULL); - - return NULL; /* ソeby kompilator nie marudziウ */ -} - -/* - * gg_resolve_pthread() // funkcja wewn黎rzna - * - * tworzy potok, nowy wアtek i w nim zaczyna resolvowa podanego hosta. - * zapisuje w sesji deskryptor potoku. jeカli coカ tam b鹽zie gotowego, - * znaczy, ソe moソna wczyta struct in_addr. jeカli nie znajdzie, zwraca - * INADDR_NONE. - * - * - fd - wskaシnik do zmiennej przechowujアcej desktyptor resolvera - * - resolver - wskaシnik do wskaシnika resolvera - * - hostname - nazwa hosta do zresolvowania + * Funkcja odbiera dane od serwera zajmujトc siト TLS w razie koniecznoナ嫩i. * - * 0, -1. - */ -int gg_resolve_pthread(int *fd, void **resolver, const char *hostname) -{ - struct gg_resolve_pthread_data *d = NULL; - pthread_t *tmp; - int pipes[2], new_errno; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_pthread(%p, %p, \"%s\");\n", fd, resolver, hostname); - - if (!resolver || !fd || !hostname) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() invalid arguments\n"); - errno = EFAULT; - return -1; - } - - if (!(tmp = malloc(sizeof(pthread_t)))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory for pthread id\n"); - return -1; - } - - if (pipe(pipes) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); - free(tmp); - return -1; - } - - if (!(d = malloc(sizeof(*d)))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n"); - new_errno = errno; - goto cleanup; - } - - d->hostname = NULL; - - if (!(d->hostname = strdup(hostname))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n"); - new_errno = errno; - goto cleanup; - } - - d->fd = pipes[1]; - - if (pthread_create(tmp, NULL, gg_resolve_pthread_thread, d)) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_phread() unable to create thread\n"); - new_errno = errno; - goto cleanup; - } - - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() %p\n", tmp); - - *resolver = tmp; - - *fd = pipes[0]; - - return 0; - -cleanup: - if (d) { - free(d->hostname); - free(d); - } - - close(pipes[0]); - close(pipes[1]); - - free(tmp); - - errno = new_errno; - - return -1; -} - -#elif defined _WIN32 - -struct gg_resolve_win32thread_data { - char *hostname; - int fd; -}; - -static DWORD WINAPI gg_resolve_win32thread_thread(LPVOID arg) -{ - struct gg_resolve_win32thread_data *d = arg; - struct in_addr a; - - if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { - struct in_addr *hn; - - if (!(hn = gg_gethostbyname(d->hostname))) - a.s_addr = INADDR_NONE; - else { - a.s_addr = hn->s_addr; - free(hn); - } - } - - write(d->fd, &a, sizeof(a)); - close(d->fd); - - free(d->hostname); - d->hostname = NULL; - - free(d); - - return 0; -} - - -int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname) -{ - struct gg_resolve_win32thread_data *d = NULL; - HANDLE h; - DWORD dwTId; - int pipes[2], new_errno; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_win32thread(%p, %p, \"%s\");\n", fd, resolver, hostname); - - if (!resolver || !fd || !hostname) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() invalid arguments\n"); - errno = EFAULT; - return -1; - } - - if (socket_pipe(pipes) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); - return -1; - } - - if (!(d = malloc(sizeof(*d)))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); - new_errno = GetLastError(); - goto cleanup; - } - - d->hostname = NULL; - - if (!(d->hostname = strdup(hostname))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); - new_errno = GetLastError(); - goto cleanup; - } - - d->fd = pipes[1]; - - h = CreateThread(NULL, 0, gg_resolve_win32thread_thread, - d, 0, &dwTId); - - if (h == NULL) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create thread\n"); - new_errno = GetLastError(); - goto cleanup; - } - - *resolver = h; - *fd = pipes[0]; - - return 0; - -cleanup: - if (d) { - free(d->hostname); - free(d); - } - - close(pipes[0]); - close(pipes[1]); - - errno = new_errno; - - return -1; - -} -#endif - -/* - * gg_read() // funkcja pomocnicza + * \param sess Struktura sesji + * \param buf Bufor na danymi + * \param length DナVgoナ崙 bufora * - * czyta z gniazda okreカlonア iloカ bajtw. bierze pod uwag, czy mamy - * poウアczenie zwykウe czy TLS. - * - * - sess - sesja, - * - buf - bufor, - * - length - iloカ bajtw, - * - * takie same wartoカci jak read(). + * \return To samo co funkcja systemowa \c read */ int gg_read(struct gg_session *sess, char *buf, int length) { int res; -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL if (sess->ssl) { int err; @@ -631,23 +313,22 @@ return res; } -/* - * gg_write() // funkcja pomocnicza +/** + * \internal WysyナB do serwera dane binarne. * - * zapisuje do gniazda okreカlonア iloカ bajtw. bierze pod uwag, czy mamy - * poウアczenie zwykウe czy TLS. + * Funkcja wysyナB dane do serwera zajmujトc siト TLS w razie koniecznoナ嫩i. * - * - sess - sesja, - * - buf - bufor, - * - length - iloカ bajtw, + * \param sess Struktura sesji + * \param buf Bufor z danymi + * \param length DナVgoナ崙 bufora * - * takie same wartoカci jak write(). + * \return To samo co funkcja systemowa \c write */ int gg_write(struct gg_session *sess, const char *buf, int length) { int res = 0; -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL if (sess->ssl) { int err; @@ -664,19 +345,48 @@ } else #endif { - int written = 0; - - while (written < length) { - res = write(sess->fd, buf + written, length - written); + if (!sess->async) { + int written = 0; + + while (written < length) { + res = write(sess->fd, buf + written, length - written); + + if (res == -1) { + if (errno != EINTR) + break; + + continue; + } + + written += res; + res = written; + } + } else { + if (!sess->send_buf) + res = write(sess->fd, buf, length); + else + res = 0; if (res == -1) { - if (errno == EAGAIN) - continue; - else - break; - } else { - written += res; - res = written; + if (errno != EAGAIN) + return res; + + res = 0; + } + + 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; } } } @@ -684,17 +394,19 @@ return res; } -/* - * gg_recv_packet() // funkcja wewn黎rzna +/** + * \internal Odbiera pakiet od serwera. * - * odbiera jeden pakiet i zwraca wskaシnik do niego. pami po nim - * naleソy zwolni za pomocア free(). + * Funkcja odczytuje nagナづウwek pakietu, a nastト冪nie jego zawartoナ崙 i zwraca + * w zaalokowanym buforze. * - * - sess - opis sesji + * Przy poナてczeniach asynchronicznych, funkcja moナシe nie byト w stanie + * skompletowaト caナFgo pakietu -- w takim przypadku zwrテウci -1, a kodem bナて囘u + * bト囘zie \c EAGAIN. * - * w przypadku bウ鹽u NULL, kod bウ鹽u w errno. naleソy zwrci uwag, ソe gdy - * poウアczenie jest nieblokujアce, a kod bウ鹽u wynosi EAGAIN, nie udaウo si - * odczyta caウego pakietu i nie naleソy tego traktowa jako bウアd. + * \param sess Struktura sesji + * + * \return Wskaナコnik do zaalokowanego bufora */ void *gg_recv_packet(struct gg_session *sess) { @@ -702,8 +414,8 @@ char *buf = NULL; int ret = 0, offset, size = 0; - gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess); - + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess); + if (!sess) { errno = EFAULT; return NULL; @@ -712,7 +424,7 @@ if (sess->recv_left < 1) { if (sess->header_buf) { memcpy(&h, sess->header_buf, sess->header_done); - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv: resuming last read (%d bytes left)\n", sizeof(h) - sess->header_done); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv: resuming last read (%d bytes left)\n", sizeof(h) - sess->header_done); free(sess->header_buf); sess->header_buf = NULL; } else @@ -721,34 +433,36 @@ while (sess->header_done < sizeof(h)) { ret = gg_read(sess, (char*) &h + sess->header_done, sizeof(h) - sess->header_done); - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret); if (!ret) { errno = ECONNRESET; - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n"); return NULL; } if (ret == -1) { if (errno == EINTR) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n"); continue; } if (errno == EAGAIN) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n"); if (!(sess->header_buf = malloc(sess->header_done))) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n"); return NULL; } memcpy(sess->header_buf, &h, sess->header_done); + errno = EAGAIN; + return NULL; } - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno)); return NULL; } @@ -761,22 +475,22 @@ h.length = gg_fix32(h.length); } else memcpy(&h, sess->recv_buf, sizeof(h)); - - /* jakieカ sensowne limity na rozmiar pakietu */ + + /* jakieナ sensowne limity na rozmiar pakietu */ if (h.length > 65535) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() invalid packet length (%d)\n", h.length); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() invalid packet length (%d)\n", h.length); errno = ERANGE; return NULL; } if (sess->recv_left > 0) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n"); size = sess->recv_left; offset = sess->recv_done; buf = sess->recv_buf; } else { if (!(buf = malloc(sizeof(h) + h.length + 1))) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n"); return NULL; } @@ -788,24 +502,23 @@ while (size > 0) { ret = gg_read(sess, buf + sizeof(h) + offset, size); - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret); if (!ret) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n"); - free(buf); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n"); errno = ECONNRESET; return NULL; } if (ret > -1 && ret <= size) { offset += ret; size -= ret; - } else if (ret == -1) { + } else if (ret == -1) { int errno2 = errno; - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno)); errno = errno2; if (errno == EAGAIN) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size); sess->recv_buf = buf; sess->recv_left = size; sess->recv_done = offset; @@ -823,49 +536,45 @@ if ((gg_debug_level & GG_DEBUG_DUMP)) { unsigned int i; - gg_debug(GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type); - for (i = 0; i < sizeof(h) + h.length; i++) - gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]); - gg_debug(GG_DEBUG_DUMP, "\n"); + gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type); + for (i = 0; i < sizeof(h) + h.length; i++) + gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]); + gg_debug_session(sess, GG_DEBUG_DUMP, "\n"); } return buf; } -/* - * gg_send_packet() // funkcja wewn黎rzna +/** + * \internal WysyナB pakiet do serwera. * - * konstruuje pakiet i wysyウa go do serwera. + * Funkcja konstruuje pakiet do wysナBnia z dowolnej liczby fragmentテウw. Jeナ嬪i + * rozmiar pakietu jest za duナシy, by mテウc go wysナBト za jednym razem, pozostaナB + * czト卩崙 zostanie zakolejkowana i wysナBna, gdy bト囘zie to moナシliwe. * - * - sock - deskryptor gniazda - * - type - typ pakietu - * - payload_1 - pierwsza czカ pakietu - * - payload_length_1 - dウugoカ pierwszej czカci - * - payload_2 - druga czカ pakietu - * - payload_length_2 - dウugoカ drugiej czカci - * - ... - kolejne czカci pakietu i ich dウugoカci - * - NULL - kocowym parametr (konieczny!) + * \param sess Struktura sesji + * \param type Rodzaj pakietu + * \param ... Lista kolejnych czト卩嫩i pakietu (wskaナコnik na bufor i dナVgoナ崙 + * typu \c int) zakoナczona \c NULL * - * jeカli si powiodウo, zwraca 0, w przypadku bウ鹽u -1. jeカli errno == ENOMEM, - * zabrakウo pami鹹i. inaczej byウ bウアd przy wysyウaniu pakietu. dla errno == 0 - * nie wysウano caウego pakietu. + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u */ int gg_send_packet(struct gg_session *sess, int type, ...) { struct gg_header *h; char *tmp; - int tmp_length; + unsigned int tmp_length; void *payload; unsigned int payload_length; va_list ap; int res; - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...)\n", sess, type); + 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(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n"); return -1; } @@ -879,14 +588,14 @@ payload_length = va_arg(ap, unsigned int); if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) { - gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// 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; @@ -902,52 +611,81 @@ if ((gg_debug_level & GG_DEBUG_DUMP)) { int i; - gg_debug(GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type)); + gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type)); for (i = 0; i < tmp_length; ++i) - gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]); - gg_debug(GG_DEBUG_DUMP, "\n"); + gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]); + gg_debug_session(sess, GG_DEBUG_DUMP, "\n"); } - - if ((res = gg_write(sess, tmp, tmp_length)) < tmp_length) { - gg_debug(GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno)); - free(tmp); + + res = gg_write(sess, tmp, tmp_length); + + free(tmp); + + if (res == -1) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno)); return -1; } - - free(tmp); + + if (sess->async) + gg_debug_session(sess, GG_DEBUG_MISC, "// 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; } -/* - * gg_session_callback() // funkcja wewn黎rzna +/** + * \internal Funkcja zwrotna sesji. + * + * Pole \c callback struktury \c gg_session zawiera wskaナコnik do tej funkcji. + * WywoナVje ona \c gg_watch_fd i zachowuje wynik w polu \c event. + * + * \note Korzystanie z tej funkcjonalnoナ嫩i nie jest juナシ zalecane. * - * wywoウywany z gg_session->callback, wykonuje gg_watch_fd() i pakuje - * do gg_session->event jego wynik. + * \param sess Struktura sesji + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u */ -static int gg_session_callback(struct gg_session *s) +static int gg_session_callback(struct gg_session *sess) { - if (!s) { + if (!sess) { errno = EFAULT; return -1; } - return ((s->event = gg_watch_fd(s)) != NULL) ? 0 : -1; + return ((sess->event = gg_watch_fd(sess)) != NULL) ? 0 : -1; } -/* - * gg_login() +/** + * ナトczy siト z serwerem Gadu-Gadu. * - * rozpoczyna procedur ウアczenia si z serwerem. reszt obsウuguje si przez - * gg_watch_fd(). + * Przy poナてczeniu synchronicznym funkcja zakoナczy dziaナBnie po nawiトzaniu + * poナてczenia lub gdy wystトpi bナてd. Po udanym poナてczeniu naleナシy wywoナZwaト + * funkcjト \c gg_watch_fd(), ktテウra odbiera informacje od serwera i zwraca + * informacje o zdarzeniach. * - * UWAGA! program musi obsウuソy SIGCHLD, jeカli ウアczy si asynchronicznie, - * ソeby poprawnie zamknア proces resolvera. + * 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ト冲li zdarzeナ (Glib, Qt itp.). Pole \c check + * jest maskト bitowト mテウwiトcト, czy biblioteka chce byト informowana o moナシliwoナ嫩i + * odczytu danych (\c GG_CHECK_READ) czy zapisu danych (\c GG_CHECK_WRITE). + * Po zaobserwowaniu zmian na deskryptorze naleナシy wywoナBト 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ナVナシyト sygnaナ SIGCHLD. * - * - p - struktura opisujアca poczアtkowy stan. wymagane pola: uin, - * password + * \note Po nawiトzaniu poナてczenia z serwerem naleナシy wysナBト listト kontaktテウw + * za pomocト funkcji \c gg_notify() lub \c gg_notify_ex(). * - * w przypadku bウ鹽u NULL, jeカli idzie dobrze (async) albo poszウo - * dobrze (sync), zwrci wskaシnik do zaalokowanej struct gg_session. + * \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ナて囘u. + * + * \ingroup login */ struct gg_session *gg_login(const struct gg_login_params *p) { @@ -981,8 +719,9 @@ goto fail; } - if (p->status_descr && !(sess->initial_descr = strdup(p->status_descr))) { - gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n"); + 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; } @@ -999,18 +738,59 @@ sess->server_addr = p->server_addr; sess->external_port = p->external_port; sess->external_addr = p->external_addr; + + 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; + sess->protocol_version = (p->protocol_version) ? p->protocol_version : GG_DEFAULT_PROTOCOL_VERSION; + if (p->era_omnix) - sess->protocol_version |= GG_ERA_OMNIX_MASK; + sess->protocol_flags |= GG_ERA_OMNIX_MASK; if (p->has_audio) - sess->protocol_version |= GG_HAS_AUDIO_MASK; + sess->protocol_flags |= GG_HAS_AUDIO_MASK; sess->client_version = (p->client_version) ? strdup(p->client_version) : NULL; 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) { + int max_length; + + if (sess->protocol_version >= 0x2d) + max_length = GG_STATUS_DESCR_MAXSIZE; + else + max_length = GG_STATUS_DESCR_MAXSIZE_PRE_8_0; + + if (sess->protocol_version >= 0x2d && p->encoding != GG_ENCODING_UTF8) + sess->initial_descr = gg_cp_to_utf8(p->status_descr); + else + sess->initial_descr = strdup(p->status_descr); + + if (!sess->initial_descr) { + gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n"); + goto fail; + } + + // XXX pamiト冲aト, ナシeby nie ciトト w ナ孑odku znaku utf-8 + + if (strlen(sess->initial_descr) > max_length) + sess->initial_descr[max_length] = 0; + } if (p->tls == 1) { -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL char buf[1024]; OpenSSL_add_ssl_algorithms(); @@ -1023,7 +803,7 @@ } rstruct; time(&rstruct.time); - rstruct.ptr = (void *) &rstruct; + rstruct.ptr = (void *) &rstruct; RAND_seed((void *) rdata, sizeof(rdata)); RAND_seed((void *) &rstruct, sizeof(rstruct)); @@ -1050,7 +830,7 @@ gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n"); #endif } - + if (gg_proxy_enabled) { hostname = gg_proxy_host; sess->proxy_port = port = gg_proxy_port; @@ -1059,37 +839,50 @@ port = GG_APPMSG_PORT; } - if (!p->async) { - struct in_addr a; + if (p->hash_type) + sess->hash_type = p->hash_type; + else + sess->hash_type = GG_LOGIN_HASH_SHA1; - if (!p->server_addr || !p->server_port) { - if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) { - struct in_addr *hn; - - if (!(hn = gg_gethostbyname(hostname))) { + if (!p->async) { + struct in_addr addr; + + if (!sess->server_addr) { + if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) { + if (gg_gethostbyname_real(hostname, &addr, 0) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_login() host \"%s\" not found\n", hostname); goto fail; - } else { - a.s_addr = hn->s_addr; - free(hn); } } } else { - a.s_addr = p->server_addr; - port = p->server_port; + addr.s_addr = sess->server_addr; + port = sess->port; } - sess->hub_addr = a.s_addr; + sess->hub_addr = addr.s_addr; if (gg_proxy_enabled) - sess->proxy_addr = a.s_addr; + sess->proxy_addr = addr.s_addr; + + if ((sess->fd = gg_connect(&addr, port, 0)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + + /* nie wyszナP? prテウbujemy portu 443. */ + if (sess->server_addr) { + sess->port = GG_HTTPS_PORT; - if ((sess->fd = gg_connect(&a, port, 0)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail; + if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, 0)) == -1) { + /* ostatnia deska ratunku zawiodナB? + * w takim razie zwijamy manatki. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + goto fail; + } + } else { + goto fail; + } } - if (p->server_addr && p->server_port) + if (sess->server_addr) sess->state = GG_STATE_CONNECTING_GG; else sess->state = GG_STATE_CONNECTING_HUB; @@ -1114,15 +907,9 @@ return sess; } - + if (!sess->server_addr || gg_proxy_enabled) { -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (gg_resolve_pthread(&sess->fd, &sess->resolver, hostname)) { -#elif defined _WIN32 - if (gg_resolve_win32thread(&sess->fd, &sess->resolver, hostname)) { -#else - if (gg_resolve(&sess->fd, &sess->pid, hostname)) { -#endif + if (sess->resolver_start(&sess->fd, &sess->resolver, hostname) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail; } @@ -1133,49 +920,121 @@ } sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; + sess->soft_timeout = 1; } return sess; fail: if (sess) { - if (sess->password) - free(sess->password); - if (sess->initial_descr) - free(sess->initial_descr); + free(sess->password); + free(sess->initial_descr); free(sess); } - + return NULL; } -/* - * gg_free_session() +/** + * WysyナB do serwera pakiet utrzymania poナてczenia. + * + * Klient powinien regularnie co minutト wysyナBト pakiet utrzymania poナてczenia, + * inaczej serwer uzna, ナシe klient straciナ ナてcznoナ崙 z sieciト i zerwie + * poナてczenie. + * + * \param sess Struktura sesji + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u * - * prbuje zamknア poウアczenia i zwalnia pami zajmowanア przez sesj. + * \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. * - * - sess - opis sesji + * Funkcja nie zwalnia zasobテウw, wiト冂 po jej wywoナBniu naleナシy uナシyト + * \c gg_free_session(). Jeナ嬪i chce siト ustawiト opis niedostト冪noナ嫩i, naleナシy + * wczeナ嬾iej wywoナBト funkcjト \c gg_change_status_descr() lub + * \c gg_change_status_descr_time(). + * + * \note Jeナ嬪i 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. + * + * \param sess Struktura sesji + * + * \ingroup login */ -void gg_free_session(struct gg_session *sess) +void gg_logoff(struct gg_session *sess) { if (!sess) return; - /* XXX dopisa zwalnianie i zamykanie wszystkiego, co mogウo zosta */ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess); + + if (GG_S_NA(sess->status)) + gg_change_status(sess, GG_STATUS_NOT_AVAIL); + +#ifdef GG_CONFIG_HAVE_OPENSSL + if (sess->ssl) + SSL_shutdown(sess->ssl); +#endif + + sess->resolver_cleanup(&sess->resolver, 1); - if (sess->password) - free(sess->password); - - if (sess->initial_descr) - free(sess->initial_descr); + if (sess->fd != -1) { + shutdown(sess->fd, SHUT_RDWR); + close(sess->fd); + sess->fd = -1; + } + + if (sess->send_buf) { + free(sess->send_buf); + sess->send_buf = NULL; + sess->send_left = 0; + } +} - if (sess->client_version) - free(sess->client_version); +/** + * Zwalnia zasoby uナシywane przez poナてczenie z serwerem. Funkcjト naleナシy wywoナBト + * po zamkniト冂iu 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; - if (sess->header_buf) - free(sess->header_buf); + if (!sess) + return; + + /* XXX dopisaト zwalnianie i zamykanie wszystkiego, co mogナP zostaト */ -#ifdef __GG_LIBGADU_HAVE_OPENSSL + free(sess->password); + free(sess->initial_descr); + free(sess->client_version); + free(sess->header_buf); + +#ifdef GG_CONFIG_HAVE_OPENSSL if (sess->ssl) SSL_free(sess->ssl); @@ -1183,23 +1042,7 @@ SSL_CTX_free(sess->ssl_ctx); #endif -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (sess->resolver) { - pthread_cancel(*((pthread_t*) sess->resolver)); - free(sess->resolver); - sess->resolver = NULL; - } -#elif defined _WIN32 - if (sess->resolver) { - HANDLE h = sess->resolver; - TerminateThread(h, 0); - CloseHandle(h); - sess->resolver = NULL; - } -#else - if (sess->pid != -1) - waitpid(sess->pid, NULL, WNOHANG); -#endif + sess->resolver_cleanup(&sess->resolver, 1); if (sess->fd != -1) close(sess->fd); @@ -1207,24 +1050,37 @@ while (sess->images) gg_image_queue_remove(sess, sess->images, 1); + free(sess->send_buf); + + for (dcc = sess->dcc7_list; dcc; dcc = dcc->next) + dcc->sess = NULL; + free(sess); } -/* - * gg_change_status() +#ifndef DOXYGEN + +/** + * \internal Funkcja wysyナBjトca pakiet zmiany statusu uナシytkownika. * - * zmienia status uソytkownika. przydatne do /away i /busy oraz /quit. + * \param sess Struktura sesji + * \param status Nowy status uナシytkownika + * \param descr Opis statusu uナシytkownika (lub \c NULL) + * \param time Czas powrotu w postaci uniksowego znacznika czasu (lub 0) * - * - sess - opis sesji - * - status - nowy status uソytkownika + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u * - * 0, -1. + * \ingroup status */ -int gg_change_status(struct gg_session *sess, int status) +static int gg_change_status_common(struct gg_session *sess, int status, const char *descr, int time) { - struct gg_new_status p; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status); + char *new_descr = NULL; + uint32_t new_time; + int descr_len = 0; + int descr_len_max; + int packet_type; + int append_null = 0; + int res; if (!sess) { errno = EFAULT; @@ -1236,67 +1092,422 @@ return -1; } - p.status = gg_fix32(status); + /* XXX, obcinaト stany ktテウrych stary protokテウナ niezna (czyt. dnd->aw; ffc->av) */ + + /* dodaj flagト obsナVgi poナてczeナ gナPsowych zgodnト z GG 7.x */ + if ((sess->protocol_version >= 0x2a) && (sess->protocol_version < 0x2d /* ? */ ) && (sess->protocol_flags & GG_HAS_AUDIO_MASK) && !GG_S_I(status)) + status |= GG_STATUS_VOICE_MASK; sess->status = status; - return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), NULL); + if (sess->protocol_version >= 0x2d) { + if (descr != NULL && sess->encoding != GG_ENCODING_UTF8) { + new_descr = gg_cp_to_utf8(descr); + + if (!new_descr) + return -1; + } + + if (sess->protocol_version >= 0x2e) + packet_type = GG_NEW_STATUS80; + else /* sess->protocol_version == 0x2d */ + packet_type = GG_NEW_STATUS80BETA; + descr_len_max = GG_STATUS_DESCR_MAXSIZE; + append_null = 1; + + } else { + packet_type = GG_NEW_STATUS; + descr_len_max = GG_STATUS_DESCR_MAXSIZE_PRE_8_0; + + if (time != 0) + append_null = 1; + } + + if (descr) { + descr_len = strlen((new_descr) ? new_descr : descr); + + if (descr_len > descr_len_max) + descr_len = descr_len_max; + + // XXX pamiト冲aト o tym, ナシeby nie ucinaト w ナ孑odku znaku utf-8 + } + + if (time) + new_time = gg_fix32(time); + + if (packet_type == GG_NEW_STATUS80) { + struct gg_new_status80 p; + + p.status = gg_fix32(status); + p.flags = gg_fix32(0x00800001); + p.description_size = gg_fix32(descr_len); + res = gg_send_packet(sess, + packet_type, + &p, + sizeof(p), + (new_descr) ? new_descr : descr, + descr_len, + NULL); + + } else { + struct gg_new_status p; + + p.status = gg_fix32(status); + res = gg_send_packet(sess, + packet_type, + &p, + sizeof(p), + (new_descr) ? new_descr : descr, + descr_len, + (append_null) ? "\0" : NULL, + (append_null) ? 1 : 0, + (time) ? &new_time : NULL, + (time) ? sizeof(new_time) : 0, + NULL); + } + + free(new_descr); + return res; } -/* - * gg_change_status_descr() +#endif /* DOXYGEN */ + +/** + * Zmienia status uナシytkownika. * - * zmienia status uソytkownika na opisowy. + * \param sess Struktura sesji + * \param status Nowy status uナシytkownika + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u * - * - sess - opis sesji - * - status - nowy status uソytkownika - * - descr - opis statusu + * \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_common(sess, status, NULL, 0); +} + +/** + * Zmienia status uナシytkownika na status opisowy. * - * 0, -1. + * \param sess Struktura sesji + * \param status Nowy status uナシytkownika + * \param descr Opis statusu uナシytkownika + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup status */ int gg_change_status_descr(struct gg_session *sess, int status, const char *descr) { - struct gg_new_status p; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr); - - if (!sess || !descr) { - errno = EFAULT; - return -1; - } + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr); - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - p.status = gg_fix32(status); - - sess->status = status; - - return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), NULL); + return gg_change_status_common(sess, status, descr, 0); } -/* - * gg_change_status_descr_time() - * - * zmienia status uソytkownika na opisowy z godzinア powrotu. +/** + * Zmienia status uナシytkownika na status opisowy z podanym czasem powrotu. * - * - sess - opis sesji - * - status - nowy status uソytkownika - * - descr - opis statusu - * - time - czas w formacie uniksowym + * \param sess Struktura sesji + * \param status Nowy status uナシytkownika + * \param descr Opis statusu uナシytkownika + * \param time Czas powrotu w postaci uniksowego znacznika czasu * - * 0, -1. + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup status */ int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time) { - struct gg_new_status p; - uint32_t newtime; + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time); + + return gg_change_status_common(sess, status, descr, time); +} + +/** + * WysyナB 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ナ嫩i + * \param recipient Numer adresata + * \param message Treナ崙 wiadomoナ嫩i + * + * \return Numer sekwencyjny wiadomoナ嫩i lub -1 w przypadku bナて囘u. + * + * \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); + + return gg_send_message_confer_richtext(sess, msgclass, 1, &recipient, message, NULL, 0); +} + +/** + * WysyナB wiadomoナ崙 formatowanト. + * + * Zwraca losowy numer sekwencyjny, ktテウry moナシna zignorowaト albo wykorzystaト + * do potwierdzenia. + * + * \param sess Struktura sesji + * \param msgclass Klasa wiadomoナ嫩i + * \param recipient Numer adresata + * \param message Treナ崙 wiadomoナ嫩i + * \param format Informacje o formatowaniu + * \param formatlen DナVgoナ崙 informacji o formatowaniu + * + * \return Numer sekwencyjny wiadomoナ嫩i lub -1 w przypadku bナて囘u. + * + * \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_confer_richtext(sess, msgclass, 1, &recipient, message, format, formatlen); +} + +/** + * WysyナB 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ナ嫩i + * \param recipients_count Liczba adresatテウw + * \param recipients Wskaナコnik do tablicy z numerami adresatテウw + * \param message Treナ崙 wiadomoナ嫩i + * + * \return Numer sekwencyjny wiadomoナ嫩i lub -1 w przypadku bナて囘u. + * + * \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_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0); +} + +/** + * \internal Dodaje tekst na koniec bufora. + * + * \param dst Wskaナコnik na bufor roboczy + * \param pos Wskaナコnik na aktualne poナPナシenie w buforze roboczym + * \param src Dodawany tekst + * \param len DナVgoナ崙 dodawanego tekstu + */ +static void gg_append(char *dst, int *pos, const void *src, int 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 utf_msg Tekst ナコrテウdナPwy + * \param format Atrybuty tekstu ナコrテウdナPwego + * \param format_len DナVgoナ崙 bloku atrybutテウw tekstu ナコrテウdナPwego + * + * \note Dokleja \c \\0 na koナcu bufora wynikowego. + * + * \return DナVgoナ崙 tekstu wynikowego bez \c \\0 (nawet jeナ嬪i \c dst to \c NULL). + */ +static int gg_convert_to_html(char *dst, const char *utf_msg, const unsigned char *format, int format_len) +{ + const char span_fmt[] = "<span style=\"color:#%02x%02x%02x; font-family:'MS Shell Dlg 2'; font-size:9pt; \">"; + const int span_len = 75; + const char img_fmt[] = "<img src=\"%02x%02x%02x%02x%02x%02x%02x%02x\">"; + const int img_len = 28; + int char_pos = 0; + int format_idx = 3; + unsigned char old_attr = 0; + const unsigned char *color = (const unsigned char*) "\x00\x00\x00"; + int len, i; + + len = 0; + + for (i = 0; utf_msg[i] != 0; i++) { + unsigned char attr; + int attr_pos; + + if (format_idx + 3 <= format_len) { + attr_pos = format[format_idx] | (format[format_idx + 1] << 8); + attr = format[format_idx + 2]; + } else { + attr_pos = -1; + attr = 0; + } + + if (attr_pos == char_pos) { + format_idx += 3; + + if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0) { + if (char_pos != 0) { + if ((old_attr & GG_FONT_UNDERLINE) != 0) + gg_append(dst, &len, "</u>", 4); + + if ((old_attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "</i>", 4); + + if ((old_attr & GG_FONT_BOLD) != 0) + gg_append(dst, &len, "</b>", 4); - gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time); + gg_append(dst, &len, "</span>", 7); + } + + if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) { + color = &format[format_idx]; + format_idx += 3; + } else { + color = (const unsigned char*) "\x00\x00\x00"; + } + + if (dst != NULL) + sprintf(&dst[len], span_fmt, color[0], color[1], color[2]); + len += span_len; + } else if (char_pos == 0) { + if (dst != NULL) + sprintf(&dst[len], span_fmt, 0, 0, 0); + len += span_len; + } + + if ((attr & GG_FONT_BOLD) != 0) + gg_append(dst, &len, "<b>", 3); + + if ((attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "<i>", 3); + + if ((attr & GG_FONT_UNDERLINE) != 0) + gg_append(dst, &len, "<u>", 3); + + if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) { + if (dst != NULL) { + sprintf(&dst[len], img_fmt, + format[format_idx + 9], + format[format_idx + 8], + format[format_idx + 7], + format[format_idx + 6], + format[format_idx + 5], + format[format_idx + 4], + format[format_idx + 3], + format[format_idx + 2]); + } + + len += img_len; + format_idx += 10; + } + + old_attr = attr; + } else if (i == 0) { + if (dst != NULL) + sprintf(&dst[len], span_fmt, 0, 0, 0); + + len += span_len; + } - if (!sess || !descr || !time) { + switch (utf_msg[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, "<br>", 4); + break; + case '\r': + break; + default: + if (dst != NULL) + dst[len] = utf_msg[i]; + len++; + } + + /* Sprawdナコ, czy bajt nie jest kontynuacjト znaku unikodowego. */ + + if ((utf_msg[i] & 0xc0) != 0xc0) + char_pos++; + } + + if ((old_attr & GG_FONT_UNDERLINE) != 0) + gg_append(dst, &len, "</u>", 4); + + if ((old_attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "</i>", 4); + + if ((old_attr & GG_FONT_BOLD) != 0) + gg_append(dst, &len, "</b>", 4); + + /* Dla pustych tekstテウw dodaj pusty <span>. */ + + if (i == 0) { + if (dst != NULL) + sprintf(&dst[len], span_fmt, 0, 0, 0); + + len += span_len; + } + + gg_append(dst, &len, "</span>", 7); + + if (dst != NULL) + dst[len] = 0; + + return len; +} + +/** + * WysyナB 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ナ嫩i + * \param recipients_count Liczba adresatテウw + * \param recipients Wskaナコnik do tablicy z numerami adresatテウw + * \param message Treナ崙 wiadomoナ嫩i + * \param format Informacje o formatowaniu + * \param formatlen DナVgoナ崙 informacji o formatowaniu + * + * \return Numer sekwencyjny wiadomoナ嫩i lub -1 w przypadku bナて囘u. + * + * \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) +{ + struct gg_send_msg s; + struct gg_send_msg80 s80; + struct gg_msg_recipients r; + char *cp_msg = NULL; + char *utf_msg = NULL; + char *html_msg = NULL; + int seq_no; + int i, j, k; + uin_t *recps; + + 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); + + if (!sess) { errno = EFAULT; return -1; } @@ -1306,75 +1517,184 @@ return -1; } - p.status = gg_fix32(status); + if (message == NULL || recipients_count <= 0 || recipients_count > 0xffff || (recipients_count != 1 && recipients == NULL)) { + errno = EINVAL; + return -1; + } + + if (sess->encoding == GG_ENCODING_UTF8) { + if (!(cp_msg = gg_utf8_to_cp((const char *) message))) + return -1; + + utf_msg = (char*) message; + } else { + if (sess->protocol_version >= 0x2d) { + if (!(utf_msg = gg_cp_to_utf8((const char *) message))) + return -1; + } + + cp_msg = (char*) message; + } + + if (sess->protocol_version < 0x2d) { + if (!sess->seq) + sess->seq = 0x01740000 | (rand() & 0xffff); + seq_no = sess->seq; + sess->seq += (rand() % 0x300) + 0x300; - sess->status = status; + s.msgclass = gg_fix32(msgclass); + s.seq = gg_fix32(seq_no); + } else { + int len; + + // Drobne odchylenie od protokoナV. Jeナ嬪i wysyナBmy kilka + // wiadomoナ嫩i w ciトgu jednej sekundy, zwiト冖szamy poprzedniト + // wartoナ崙, ナシeby kaナシda wiadomoナ崙 miaナB unikalny numer. + + seq_no = time(NULL); + + if (seq_no <= sess->seq) + seq_no = sess->seq + 1; + + sess->seq = seq_no; + + if (format == NULL || formatlen < 3) { + format = (unsigned char*) "\x02\x06\x00\x00\x00\x08\x00\x00\x00"; + formatlen = 9; + } + + len = gg_convert_to_html(NULL, utf_msg, format, formatlen); + + html_msg = malloc(len + 1); + + if (html_msg == NULL) { + seq_no = -1; + goto cleanup; + } + + gg_convert_to_html(html_msg, utf_msg, format, formatlen); - newtime = gg_fix32(time); + s80.seq = gg_fix32(seq_no); + s80.msgclass = gg_fix32(msgclass); + s80.offset_plain = gg_fix32(sizeof(s80) + strlen(html_msg) + 1); + s80.offset_attr = gg_fix32(sizeof(s80) + strlen(html_msg) + 1 + strlen(cp_msg) + 1); + } + + if (recipients_count > 1) { + r.flag = 0x01; + r.count = gg_fix32(recipients_count - 1); + + recps = malloc(sizeof(uin_t) * recipients_count); + + if (!recps) { + seq_no = -1; + goto cleanup; + } + + for (i = 0; i < recipients_count; i++) { + for (j = 0, k = 0; j < recipients_count; j++) { + if (recipients[j] != recipients[i]) { + recps[k] = gg_fix32(recipients[j]); + k++; + } + } + + if (sess->protocol_version < 0x2d) { + s.recipient = gg_fix32(recipients[i]); - return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), &newtime, sizeof(newtime), NULL); + if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), cp_msg, strlen(cp_msg) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1) + seq_no = -1; + } else { + s80.recipient = gg_fix32(recipients[i]); + + if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80), html_msg, strlen(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 { + if (sess->protocol_version < 0x2d) { + s.recipient = gg_fix32(recipients[0]); + + if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), cp_msg, strlen(cp_msg) + 1, format, formatlen, NULL) == -1) + seq_no = -1; + } else { + s80.recipient = gg_fix32(recipients[0]); + + if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80), html_msg, strlen(html_msg) + 1, cp_msg, strlen(cp_msg) + 1, format, formatlen, NULL) == -1) + seq_no = -1; + } + } + +cleanup: + if (cp_msg != (char*) message) + free(cp_msg); + + if (utf_msg != (char*) message) + free(utf_msg); + + free(html_msg); + + return seq_no; } -/* - * gg_logoff() +/** + * WysyナB wiadomoナ崙 binarnト przeznaczonト dla klienta. + * + * Wiadomoナ嫩i miト囘zy klientami przesyナB siト np. w celu wywoナBnia zwrotnego + * poナてczenia bezpoナ孑edniego. Funkcja zwraca losowy numer sekwencyjny, + * ktテウry moナシna zignorowaト albo wykorzystaト do potwierdzenia. * - * wylogowuje uソytkownika i zamyka poウアczenie, ale nie zwalnia pami鹹i. + * \param sess Struktura sesji + * \param msgclass Klasa wiadomoナ嫩i + * \param recipient Numer adresata + * \param message Treナ崙 wiadomoナ嫩i + * \param message_len DナVgoナ崙 wiadomoナ嫩i * - * - sess - opis sesji + * \return Numer sekwencyjny wiadomoナ嫩i lub -1 w przypadku bナて囘u. + * + * \ingroup messages */ -void gg_logoff(struct gg_session *sess) +int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len) { - if (!sess) - return; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess); - - if (GG_S_NA(sess->status & ~GG_STATUS_FRIENDS_MASK)) - gg_change_status(sess, GG_STATUS_NOT_AVAIL); - -#ifdef __GG_LIBGADU_HAVE_OPENSSL - if (sess->ssl) - SSL_shutdown(sess->ssl); -#endif + struct gg_send_msg s; -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (sess->resolver) { - pthread_cancel(*((pthread_t*) sess->resolver)); - free(sess->resolver); - sess->resolver = NULL; - } -#elif defined _WIN32 - if (sess->resolver) { - HANDLE h = sess->resolver; - TerminateThread(h, 0); - CloseHandle(h); - sess->resolver = NULL; + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient); + + if (!sess) { + errno = EFAULT; + return -1; } -#else - if (sess->pid != -1) { - waitpid(sess->pid, NULL, WNOHANG); - sess->pid = -1; + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; } -#endif - - if (sess->fd != -1) { - shutdown(sess->fd, SHUT_RDWR); - close(sess->fd); - sess->fd = -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); } -/* - * gg_image_request() +/** + * WysyナB ナシトdanie obrazka o podanych parametrach. * - * wysyウa ソアdanie wysウania obrazka o podanych parametrach. + * Wiadomoナ嫩i obrazkowe nie zawierajト samych obrazkテウw, a tylko ich rozmiary + * i sumy kontrolne. Odbiorca najpierw szuka obrazkテウw w swojej pamiト冂i + * podrト冂znej i dopiero gdy ich nie znajdzie, wysyナB ナシトdanie do nadawcy. + * Wynik zostanie przekazany zdarzeniem \c GG_EVENT_IMAGE_REPLY. * - * - sess - opis sesji - * - recipient - numer adresata - * - size - rozmiar obrazka - * - crc32 - suma kontrolna obrazka + * \param sess Struktura sesji + * \param recipient Numer adresata + * \param size Rozmiar obrazka w bajtach + * \param crc32 Suma kontrola obrazka * - * 0/-1 + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup messages */ int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32) { @@ -1383,13 +1703,13 @@ char dummy = 0; int res; - gg_debug(GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32); + 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; @@ -1407,7 +1727,7 @@ r.flag = 0x04; 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) { @@ -1415,14 +1735,14 @@ char *buf; if (!q) { - gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n"); + 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(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n"); free(q); return -1; } @@ -1449,20 +1769,20 @@ return res; } -/* - * gg_image_reply() - * - * wysyウa ソアdany obrazek. +/** + * WysyナB ナシトdany obrazek. * - * - sess - opis sesji - * - recipient - numer adresata - * - filename - nazwa pliku - * - image - bufor z obrazkiem - * - size - rozmiar obrazka + * \param sess Struktura sesji + * \param recipient Numer adresata + * \param filename Nazwa pliku + * \param image Bufor z obrazkiem + * \param size Rozmiar obrazka * - * 0/-1 + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup messages */ -int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const unsigned char *image, int size) +int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size) { struct gg_msg_image_reply *r; struct gg_send_msg s; @@ -1470,7 +1790,7 @@ char buf[1910]; int res = -1; - gg_debug(GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size); + 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; @@ -1487,7 +1807,7 @@ return -1; } - /* wytnij カcieソki, zostaw tylko nazw pliku */ + /* wytnij ナ嫩ieナシki, zostaw tylko nazwト pliku */ while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\'))) filename = tmp + 1; @@ -1495,7 +1815,7 @@ errno = EINVAL; return -1; } - + s.recipient = gg_fix32(recipient); s.seq = gg_fix32(0); s.msgclass = gg_fix32(GG_CLASS_MSG); @@ -1505,26 +1825,26 @@ r->flag = 0x05; r->size = gg_fix32(size); - r->crc32 = gg_fix32(gg_crc32(0, image, size)); + r->crc32 = gg_fix32(gg_crc32(0, (unsigned char*) image, size)); while (size > 0) { - size_t buflen, chunklen; - + int buflen, chunklen; + /* \0 + struct gg_msg_image_reply */ buflen = sizeof(struct gg_msg_image_reply) + 1; - /* w pierwszym kawaウku jest nazwa pliku */ + /* w pierwszym kawaナLu jest nazwa pliku */ if (r->flag == 0x05) { strcpy(buf + buflen, filename); buflen += strlen(filename) + 1; } - chunklen = ((size_t)size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : (size_t)size; + chunklen = (size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : size; memcpy(buf + buflen, image, chunklen); size -= chunklen; image += chunklen; - + res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), buf, buflen + chunklen, NULL); if (res == -1) @@ -1536,83 +1856,34 @@ return res; } -/* - * gg_send_message_ctcp() +/** + * WysyナB do serwera listト kontaktテウw. * - * wysyウa wiadomoカ do innego uソytkownika. zwraca losowy numer - * sekwencyjny, ktry moソna zignorowa albo wykorzysta do potwierdzenia. + * Funkcja informuje serwer o liナ嫩ie kontaktテウw, ktテウrych statusy bト囘ト + * 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ナBト po poナてczeniu, nawet jeナ嬪i + * jest pusta. * - * - sess - opis sesji - * - msgclass - rodzaj wiadomoカci - * - recipient - numer adresata - * - message - treカ wiadomoカci - * - message_len - dウugoカ + * \param sess Struktura sesji + * \param userlist Wskaナコnik do tablicy numerテウw kontaktテウw + * \param types Wskaナコnik do tablicy rodzajテウw kontaktテウw + * \param count Liczba kontaktテウw * - * numer sekwencyjny wiadomoカci lub -1 w przypadku bウ鹽u. + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup contacts */ -int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len) +int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count) { - struct gg_send_msg s; - - gg_debug(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); -} + struct gg_notify *n; + uin_t *u; + char *t; + int i, res = 0; -/* - * gg_send_message() - * - * wysyウa wiadomoカ do innego uソytkownika. zwraca losowy numer - * sekwencyjny, ktry moソna zignorowa albo wykorzysta do potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomoカci - * - recipient - numer adresata - * - message - treカ wiadomoカci - * - * numer sekwencyjny wiadomoカci lub -1 w przypadku bウ鹽u. - */ -int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message) -{ - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message); - - return gg_send_message_richtext(sess, msgclass, recipient, message, NULL, 0); -} - -/* - * gg_send_message_richtext() - * - * wysyウa kolorowア wiadomoカ do innego uソytkownika. zwraca losowy numer - * sekwencyjny, ktry moソna zignorowa albo wykorzysta do potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomoカci - * - recipient - numer adresata - * - message - treカ wiadomoカci - * - format - informacje o formatowaniu - * - formatlen - dウugoカ informacji o formatowaniu - * - * numer sekwencyjny wiadomoカci lub -1 w przypadku bウ鹽u. - */ -int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen) -{ - struct gg_send_msg s; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen); + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count); if (!sess) { errno = EFAULT; @@ -1624,186 +1895,12 @@ return -1; } - if (!message) { - errno = EFAULT; - return -1; - } - - s.recipient = gg_fix32(recipient); - if (!sess->seq) - sess->seq = 0x01740000 | (rand() & 0xffff); - s.seq = gg_fix32(sess->seq); - s.msgclass = gg_fix32(msgclass); - sess->seq += (rand() % 0x300) + 0x300; - - if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen((const char *)message) + 1, format, formatlen, NULL) == -1) - return -1; - - return gg_fix32(s.seq); -} - -/* - * gg_send_message_confer() - * - * wysyウa wiadomoカ do kilku uソytkownikow (konferencja). zwraca losowy numer - * sekwencyjny, ktry moソna zignorowa albo wykorzysta do potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomoカci - * - recipients_count - iloカ adresatw - * - recipients - numerki adresatw - * - message - treカ wiadomoカci - * - * numer sekwencyjny wiadomoカci lub -1 w przypadku bウ鹽u. - */ -int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message) -{ - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message); - - return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0); -} - -/* - * gg_send_message_confer_richtext() - * - * wysyウa kolorowア wiadomoカ do kilku uソytkownikow (konferencja). zwraca - * losowy numer sekwencyjny, ktry moソna zignorowa albo wykorzysta do - * potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomoカci - * - recipients_count - iloカ adresatw - * - recipients - numerki adresatw - * - message - treカ wiadomoカci - * - format - informacje o formatowaniu - * - formatlen - dウugoカ informacji o formatowaniu - * - * numer sekwencyjny wiadomoカci lub -1 w przypadku bウ鹽u. - */ -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) -{ - struct gg_send_msg s; - struct gg_msg_recipients r; - int i, j, k; - uin_t *recps; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - if (!message || recipients_count <= 0 || recipients_count > 0xffff || !recipients) { - errno = EINVAL; - return -1; - } - - r.flag = 0x01; - r.count = gg_fix32(recipients_count - 1); - - if (!sess->seq) - sess->seq = 0x01740000 | (rand() & 0xffff); - s.seq = gg_fix32(sess->seq); - s.msgclass = gg_fix32(msgclass); - - recps = malloc(sizeof(uin_t) * recipients_count); - if (!recps) - return -1; - - for (i = 0; i < recipients_count; i++) { - - s.recipient = gg_fix32(recipients[i]); - - for (j = 0, k = 0; j < recipients_count; j++) - if (recipients[j] != recipients[i]) { - recps[k] = gg_fix32(recipients[j]); - k++; - } - - if (!i) - sess->seq += (rand() % 0x300) + 0x300; - - if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen((const char *)message) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1) { - free(recps); - return -1; - } - } - - free(recps); - - return gg_fix32(s.seq); -} - -/* - * gg_ping() - * - * wysyウa do serwera pakiet ping. - * - * - sess - opis sesji - * - * 0, -1. - */ -int gg_ping(struct gg_session *sess) -{ - gg_debug(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); -} - -/* - * gg_notify_ex() - * - * wysyウa serwerowi list kontaktw (wraz z odpowiadajアcymi im typami userw), - * dzi麑i czemu wie, czyj stan nas interesuje. - * - * - sess - opis sesji - * - userlist - wskaシnik do tablicy numerw - * - types - wskaシnik do tablicy typw uソytkownikw - * - count - iloカ numerkw - * - * 0, -1. - */ -int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count) -{ - struct gg_notify *n; - uin_t *u; - char *t; - int i, res = 0; - - gg_debug(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 (!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; @@ -1814,12 +1911,12 @@ if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count))) return -1; - - for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) { + + for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) { n[i].uin = gg_fix32(*u); n[i].dunno1 = *t; } - + if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) { free(n); res = -1; @@ -1836,17 +1933,19 @@ return res; } -/* - * gg_notify() +/** + * WysyナB do serwera listト kontaktテウw. * - * wysyウa serwerowi list kontaktw, dzi麑i czemu wie, czyj stan nas - * interesuje. + * Funkcja jest odpowiednikiem \c gg_notify_ex(), gdzie wszystkie kontakty + * sト rodzaju \c GG_USER_NORMAL. * - * - sess - opis sesji - * - userlist - wskaシnik do tablicy numerw - * - count - iloカ numerkw + * \param sess Struktura sesji + * \param userlist Wskaナコnik do tablicy numerテウw kontaktテウw + * \param count Liczba kontaktテウw * - * 0, -1. + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup contacts */ int gg_notify(struct gg_session *sess, uin_t *userlist, int count) { @@ -1854,13 +1953,13 @@ uin_t *u; int i, res = 0; - gg_debug(GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count); - + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count); + if (!sess) { errno = EFAULT; return -1; } - + if (sess->state != GG_STATE_CONNECTED) { errno = ENOTCONN; return -1; @@ -1868,10 +1967,10 @@ 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; @@ -1879,15 +1978,15 @@ part_count = count; packet_type = GG_NOTIFY_LAST; } - + if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count))) return -1; - - for (u = userlist, i = 0; i < part_count; u++, i++) { + + for (u = userlist, i = 0; i < part_count; u++, i++) { n[i].uin = gg_fix32(*u); n[i].dunno1 = GG_USER_NORMAL; } - + if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) { res = -1; free(n); @@ -1903,73 +2002,27 @@ return res; } -/* - * gg_add_notify_ex() +/** + * Dodaje kontakt. * - * dodaje do listy kontaktw dany numer w trakcie poウアczenia. - * dodawanemu uソytkownikowi okreカlamy jego typ (patrz protocol.html) + * 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. * - * - sess - opis sesji - * - uin - numer - * - type - typ + * \param sess Struktura sesji + * \param uin Numer kontaktu + * \param type Rodzaj kontaktu * - * 0, -1. + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup contacts */ int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type) { struct gg_add_remove a; - gg_debug(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; - } - - a.uin = gg_fix32(uin); - a.dunno1 = type; - - return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL); -} + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type); -/* - * gg_add_notify() - * - * dodaje do listy kontaktw dany numer w trakcie poウアczenia. - * - * - sess - opis sesji - * - uin - numer - * - * 0, -1. - */ -int gg_add_notify(struct gg_session *sess, uin_t uin) -{ - return gg_add_notify_ex(sess, uin, GG_USER_NORMAL); -} - -/* - * gg_remove_notify_ex() - * - * usuwa z listy kontaktw w trakcie poウアczenia. - * usuwanemu uソytkownikowi okreカlamy jego typ (patrz protocol.html) - * - * - sess - opis sesji - * - uin - numer - * - type - typ - * - * 0, -1. - */ -int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type) -{ - struct gg_add_remove a; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type); - if (!sess) { errno = EFAULT; return -1; @@ -1982,35 +2035,101 @@ 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup contacts + */ +int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type) +{ + struct gg_add_remove a; + + 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; + } + + a.uin = gg_fix32(uin); + a.dunno1 = type; + return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL); } -/* - * gg_remove_notify() +/** + * Usuwa kontakt. * - * usuwa z listy kontaktw w trakcie poウアczenia. + * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich + * kontaktテウw to \c GG_USER_NORMAL. * - * - sess - opis sesji - * - uin - numer + * \param sess Struktura sesji + * \param uin Numer kontaktu * - * 0, -1. + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup contacts */ int gg_remove_notify(struct gg_session *sess, uin_t uin) { return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL); } -/* - * gg_userlist_request() +/** + * WysyナB do serwera zapytanie dotyczトce listy kontaktテウw. * - * wysyウa ソアdanie/zapytanie listy kontaktw na serwerze. + * Funkcja sナVナシ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ナZwu na poナてczenie. Format + * listy kontaktテウw jest ignorowany przez serwer, ale ze wzglト囘u na + * kompatybilnoナ崙 z innymi klientami, naleナシy przechowywaト dane w tym samym + * formacie co oryginalny klient Gadu-Gadu. * - * - sess - opis sesji - * - type - rodzaj zapytania/ソアdania - * - request - treカ zapytania/ソアdania (moソe by NULL) + * Program nie musi siト przejmowaト fragmentacjト listy kontaktテウw wynikajトcト + * z protokoナV -- wysyナB i odbiera kompletnト listト. * - * 0, -1 + * \param sess Struktura sesji + * \param type Rodzaj zapytania + * \param request Treナ崙 zapytania (moナシe byト rテウwne NULL) + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup importexport */ int gg_userlist_request(struct gg_session *sess, char type, const char *request) { @@ -2020,7 +2139,7 @@ errno = EFAULT; return -1; } - + if (sess->state != GG_STATE_CONNECTED) { errno = ENOTCONN; return -1; @@ -2030,7 +2149,7 @@ sess->userlist_blocks = 1; return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL); } - + len = strlen(request); sess->userlist_blocks = 0; @@ -2053,6 +2172,8 @@ return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL); } +/* @} */ + /* * Local variables: * c-indentation-style: k&r
--- a/libpurple/protocols/gg/lib/libgadu.h Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/gg/lib/libgadu.h Tue Mar 16 12:07:06 2010 +0900 @@ -1,12 +1,13 @@ -/* $Id: libgadu.h 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: libgadu.h.in 878 2009-11-16 23:48:19Z wojtekka $ */ /* - * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Woシny <speedy@ziew.org> - * Arkadiusz Miカkiewicz <arekm@pld-linux.org> - * Tomasz Chiliski <chilek@chilan.com> + * (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl> + * Robert J. Woナコny <speedy@ziew.org> + * Arkadiusz Miナ嫐iewicz <arekm@pld-linux.org> + * Tomasz Chiliナгki <chilek@chilan.com> * Piotr Wysocki <wysek@linux.bydg.org> * Dawid Jarosz <dawjar@poczta.onet.pl> + * Jakub Zawadzki <darkjames@darkjames.ath.cx> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -19,338 +20,568 @@ * * 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 Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ +/** + * \file libgadu.h + * + * \brief Gナづウwny plik nagナづウwkowy biblioteki + */ + #ifndef __GG_LIBGADU_H #define __GG_LIBGADU_H #ifdef __cplusplus -#ifdef _MSC_VER +#ifdef _WIN32 #pragma pack(push, 1) #endif extern "C" { #endif -#include <libgadu-config.h> #include <sys/types.h> #include <stdio.h> #include <stdarg.h> -#ifdef __GG_LIBGADU_HAVE_OPENSSL +/** \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 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 TLS support. */ +#undef GG_CONFIG_HAVE_OPENSSL + +/* Defined if uintX_t types are defined in <stdint.h>. */ +#undef GG_CONFIG_HAVE_STDINT_H + +/* Defined if uintX_t types are defined in <inttypes.h>. */ +#undef GG_CONFIG_HAVE_INTTYPES_H + +/* Defined if uintX_t types are defined in <sys/inttypes.h>. */ +#undef GG_CONFIG_HAVE_SYS_INTTYPES_H + +/* Defined if uintX_t types are defined in <sys/int_types.h>. */ +#undef GG_CONFIG_HAVE_SYS_INT_TYPES_H + +/* Defined if uintX_t types are defined in <sys/types.h>. */ +#undef GG_CONFIG_HAVE_SYS_TYPES_H + +#ifdef GG_CONFIG_HAVE_OPENSSL #include <openssl/ssl.h> #endif -/* - * typedef uin_t - * - * typ reprezentujアcy numer osoby. +#ifdef GG_CONFIG_HAVE_STDINT_H +#include <stdint.h> +#else +# ifdef GG_CONFIG_HAVE_INTTYPES_H +# include <inttypes.h> +# else +# ifdef GG_CONFIG_HAVE_SYS_INTTYPES_H +# include <sys/inttypes.h> +# else +# ifdef GG_CONFIG_HAVE_SYS_INT_TYPES_H +# include <sys/int_types.h> +# else +# ifdef GG_CONFIG_HAVE_SYS_TYPES_H +# include <sys/types.h> +# else + +#ifndef __AC_STDINT_H +#define __AC_STDINT_H + +/* ISO C 9X: 7.18 Integer types <stdint.h> */ + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; + +#ifndef __CYGWIN__ +#define __int8_t_defined +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +#endif + +#endif /* __AC_STDINT_H */ + +# endif +# endif +# endif +# endif +#endif + +/** \endcond */ + +/** + * Numer Gadu-Gadu. */ typedef uint32_t uin_t; -/* - * oglna struktura opisujアca rソne sesje. przydatna w klientach. +/** + * Identyfikator poナてczenia bezpoナ孑edniego Gadu-Gadu 7.x. + */ +typedef struct { + uint8_t id[8]; +} gg_dcc7_id_t; + +/** + * Makro deklarujトce pola wspテウlne dla struktur sesji. */ #define gg_common_head(x) \ - int fd; /* podglアdany deskryptor */ \ - int check; /* sprawdzamy zapis czy odczyt */ \ - int state; /* aktualny stan maszynki */ \ - int error; /* kod bウ鹽u dla GG_STATE_ERROR */ \ - int type; /* rodzaj sesji */ \ - int id; /* identyfikator */ \ - int timeout; /* sugerowany timeout w sekundach */ \ - int (*callback)(x*); /* callback przy zmianach */ \ - void (*destroy)(x*); /* funkcja niszczenia */ + 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ナて囘u 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ナZ 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_session +struct gg_dcc7; + +/** + * Sposテウb rozwiトzywania nazw serwerテウw. + */ +typedef enum { + GG_RESOLVER_DEFAULT = 0, /**< Domyナ嬪ny 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_WIN32, + GG_RESOLVER_CUSTOM, /**< Funkcje rozwiトzywania nazw dostarczone przed aplikacjト */ + GG_RESOLVER_INVALID = -1 /**< NieprawidナPwy 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ナPwe kodowanie */ +} gg_encoding_t; + +/** + * Sesja Gadu-Gadu. * - * struktura opisujアca danア sesj. tworzona przez gg_login(), zwalniana - * przez gg_free_session(). + * 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; /* czy poウアczenie jest asynchroniczne */ - int pid; /* pid procesu resolvera */ - int port; /* port, z ktrym si ウアczymy */ - int seq; /* numer sekwencyjny ostatniej wiadomoカci */ - int last_pong; /* czas otrzymania ostatniego ping/pong */ - int last_event; /* czas otrzymania ostatniego pakietu */ + 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ナ嫩i */ + int last_pong; /**< Czas otrzymania ostatniej ramki utrzymaniowej */ + int last_event; /**< Czas otrzymania ostatniego pakietu */ - struct gg_event *event; /* zdarzenie po ->callback() */ + struct gg_event *event; /**< Zdarzenie po wywoナBniu \c callback */ - uint32_t proxy_addr; /* adres proxy, keszowany */ - uint16_t proxy_port; /* port proxy */ + uint32_t proxy_addr; /**< Adres serwera poナ孑edniczトcego */ + uint16_t proxy_port; /**< Port serwera poナ孑edniczトcego */ - uint32_t hub_addr; /* adres huba po resolvni鹹iu */ - uint32_t server_addr; /* adres serwera, od huba */ + uint32_t hub_addr; /**< Adres huba po rozwiトzaniu nazwy */ + uint32_t server_addr; /**< Adres serwera otrzymany od huba */ - uint32_t client_addr; /* adres klienta */ - uint16_t client_port; /* port, na ktrym klient sウucha */ + uint32_t client_addr; /**< Adres gniazda dla poナてczeナ bezpoナ孑ednich do wersji Gadu-Gadu 6.x */ + uint16_t client_port; /**< Port gniazda dla poナてczeナ bezpoナ孑ednich do wersji Gadu-Gadu 6.x */ - uint32_t external_addr; /* adres zewnetrzny klienta */ - uint16_t external_port; /* port zewnetrzny klienta */ + uint32_t external_addr; /**< Publiczny adres dla poナてczeナ bezpoナ孑ednich do wersji Gadu-Gadu 6.x */ + uint16_t external_port; /**< Publiczny port dla poナてczeナ bezpoナ孑ednich do wersji Gadu-Gadu 6.x */ - uin_t uin; /* numerek klienta */ - char *password; /* i jego hasウo. zwalniane automagicznie */ + uin_t uin; /**< WナBsny numer Gadu-Gadu */ + char *password; /**< HasナP (zwalniane po uナシyciu) */ - int initial_status; /* poczアtkowy stan klienta */ - int status; /* aktualny stan klienta */ + int initial_status; /**< Poczトtkowy status */ + int status; /**< Aktualny status */ - char *recv_buf; /* bufor na otrzymywane pakiety */ - int recv_done; /* ile juソ wczytano do bufora */ - int recv_left; /* i ile jeszcze trzeba wczyta */ + char *recv_buf; /**< Bufor na odbierany pakiety */ + int recv_done; /**< Liczba wczytanych bajtテウw pakietu */ + int recv_left; /**< Liczba pozostaナZch do wczytania bajtテウw pakietu */ - int protocol_version; /* wersja uソywanego protokoウu */ - char *client_version; /* wersja uソywanego klienta */ - int last_sysmsg; /* ostatnia wiadomoカ systemowa */ + int protocol_version; /**< Wersja protokoナV (bez flag) */ + char *client_version; /**< Wersja klienta */ + int last_sysmsg; /**< Numer ostatniej wiadomoナ嫩i systemowej */ - char *initial_descr; /* poczアtkowy opis stanu klienta */ + char *initial_descr; /**< Poczトtkowy opis statusu */ - void *resolver; /* wskaシnik na informacje resolvera */ + void *resolver; /**< Dane prywatne procesu lub wトtku rozwiトzujトcego nazwト serwera */ - char *header_buf; /* bufor na poczアtek nagウwka */ - unsigned int header_done;/* ile juソ mamy */ + char *header_buf; /**< Bufor na poczトtek nagナづウwka pakietu */ + unsigned int header_done; /**< Liczba wczytanych bajtテウw nagナづウwka pakietu */ -#ifdef __GG_LIBGADU_HAVE_OPENSSL - SSL *ssl; /* sesja TLS */ - SSL_CTX *ssl_ctx; /* kontekst sesji? */ +#ifdef GG_CONFIG_HAVE_OPENSSL + SSL *ssl; /**< Struktura TLS */ + SSL_CTX *ssl_ctx; /**< Kontekst sesji TLS */ #else - void *ssl; /* zachowujemy ABI */ - void *ssl_ctx; + void *ssl; /**< Struktura TLS */ + void *ssl_ctx; /**< Kontekst sesji TLS */ #endif - int image_size; /* maksymalny rozmiar obrazkw w KiB */ + int image_size; /**< Maksymalny rozmiar obsナVgiwanych obrazkテウw w KiB */ + + char *userlist_reply; /**< Bufor z odbieranト listト kontaktテウw */ + + int userlist_blocks; /**< Liczba czト卩嫩i listy kontaktテウw */ - char *userlist_reply; /* fragment odpowiedzi listy kontaktw */ + struct gg_image_queue *images; /**< Lista wczytywanych obrazkテウw */ + + int hash_type; /**< Rodzaj funkcji skrテウtu hasナB (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1) */ + + char *send_buf; /**< Bufor z danymi do wysナBnia */ + int send_left; /**< Liczba bajtテウw do wysナBnia */ - int userlist_blocks; /* na ile kawaウkw podzielono list kontaktw */ + struct gg_dcc7 *dcc7_list; /**< Lista poナてczeナ bezpoナ孑ednich skojarzonych z sesjト */ + + int soft_timeout; /**< Flaga mテウwiトca, ナシe po przekroczeniu \c timeout naleナシy wywoナBト \c gg_watch_fd() */ + + int protocol_flags; /**< Flagi protokoナV */ - struct gg_image_queue *images; /* aktualnie wczytywane obrazki */ + 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ナV */ }; -/* - * struct gg_http +/** + * Poナてczenie HTTP. * - * oglna struktura opisujアca stan wszystkich operacji HTTP. tworzona - * przez gg_http_connect(), zwalniana przez gg_http_free(). + * 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; /* czy poウアczenie asynchroniczne */ - int pid; /* pid procesu resolvera */ - int port; /* port, z ktrym si ウアczymy */ + 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ナVgi HTTP */ - char *query; /* bufor zapytania http */ - char *header; /* bufor nagウwka */ - int header_size; /* rozmiar wczytanego nagウwka */ - char *body; /* bufor otrzymanych informacji */ - unsigned int body_size; /* oczekiwana iloカ informacji */ + 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 */ - void *data; /* dane danej operacji http */ - - char *user_data; /* dane uソytkownika, nie sア zwalniane przez gg_http_free() */ + 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 */ +}; - void *resolver; /* wskaシnik na informacje resolvera */ - - unsigned int body_done; /* ile juソ treカci odebrano? */ -}; +/** \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 -#define GG_MAX_PATH 276 +/** \endcond */ + +#define GG_MAX_PATH 276 /**< Maksymalny rozmiar nazwy pliku w strukturze \c gg_file_info */ -/* - * struct gg_file_info +/** + * Odpowiednik struktury WIN32_FIND_DATA z API WIN32. * - * odpowiednik windowsowej struktury WIN32_FIND_DATA niezb鹽nej przy - * wysyウaniu plikw. + * Wykorzystywana przy poナてczeniach bezpoナ孑ednich 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 */ -} GG_PACKED; + 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 */; -/* - * struct gg_dcc +/** + * Poナてczenie bezpoナ孑ednie do wersji Gadu-Gadu 6.x. * - * struktura opisujアca nasウuchujアce gniazdo poウアcze mi鹽zy klientami. - * tworzona przez gg_dcc_socket_create(), zwalniana przez gg_dcc_free(). + * 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; /* opis zdarzenia */ + struct gg_event *event; /**< Zdarzenie po wywoナBniu \c callback */ - int active; /* czy to my si ウアczymy? */ - int port; /* port, na ktrym siedzi */ - uin_t uin; /* uin klienta */ - uin_t peer_uin; /* uin drugiej strony */ - int file_fd; /* deskryptor pliku */ - unsigned int offset; /* offset w pliku */ - unsigned int chunk_size;/* rozmiar kawaウka */ - unsigned int chunk_offset;/* offset w aktualnym kawaウku */ + int active; /**< Flaga poナてczenia aktywnego (nieuナシywana) */ + int port; /**< Port gniazda nasナVchujトcego */ + uin_t uin; /**< WナBsny numer Gadu-Gadu */ + uin_t peer_uin; /**< Numer Gadu-Gadu drugiej strony poナてczenia */ + int file_fd; /**< deskryptor pliku */ + unsigned int offset; /**< PoナPナシenie w pliku */ + unsigned int chunk_size; + /**< Rozmiar kawaナLa pliku */ + unsigned int chunk_offset; + /**< PoナPナシenie w aktualnym kawaナLu pliku */ struct gg_file_info file_info; - /* informacje o pliku */ - int established; /* poウアczenie ustanowione */ - uint8_t *voice_buf; /* bufor na pakiet poウアczenia gウosowego */ - int incoming; /* poウアczenie przychodzアce */ - char *chunk_buf; /* bufor na kawaウek danych */ - uint32_t remote_addr; /* adres drugiej strony */ - uint16_t remote_port; /* port drugiej strony */ + /**< Informacje o pliku */ + int established; /**< Flaga ustanowienia poナてczenia */ + char *voice_buf; /**< Bufor na pakiet poナてczenia gナPsowego */ + 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 */ }; -/* - * enum gg_session_t +#define GG_DCC7_HASH_LEN 20 /**< Maksymalny rozmiar skrテウtu pliku w poナてczeniach bezpoナ孑enich */ +#define GG_DCC7_FILENAME_LEN 255 /**< Maksymalny rozmiar nazwy pliku w poナてczeniach bezpoナ孑ednich */ +#define GG_DCC7_INFO_LEN 64 /**< Maksymalny rozmiar informacji o poナてczeniach bezpoナ孑ednich */ + +/** + * Poナてczenie bezpoナ孑ednie od wersji Gadu-Gadu 7.x. * - * rodzaje sesji. + * \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ナBsny numer Gadu-Gadu */ + uin_t peer_uin; /**< Numer Gadu-Gadu drugiej strony poナてczenia */ + + int file_fd; /**< Deskryptor przesyナBnego pliku */ + unsigned int offset; /**< Aktualne poナPナシenie w przesyナBnym pliku */ + unsigned int size; /**< Rozmiar przesyナBnego pliku */ + unsigned char filename[GG_DCC7_FILENAME_LEN + 1]; + /**< Nazwa przesyナBnego pliku */ + unsigned char hash[GG_DCC7_HASH_LEN]; + /**< Skrテウt SHA1 przesyナBnego pliku */ + + int dcc_type; /**< Rodzaj poナてczenia bezpoナ孑edniego */ + 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ト冪ne poナてczenie w liナ嫩ie */ + + int soft_timeout; /**< Flaga mテウwiトca, ナシe po przekroczeniu \c timeout naleナシy wywoナBト \c gg_dcc7_watch_fd() */ + int seek; /**< Flaga mテウwiトca, ナシe moナシna zmieniaト poナPナシenie w wysyナBnym pliku */ +}; + +/** + * Rodzaj sesji. */ enum gg_session_t { - GG_SESSION_GG = 1, /* poウアczenie z serwerem gg */ - GG_SESSION_HTTP, /* oglna sesja http */ - GG_SESSION_SEARCH, /* szukanie */ - GG_SESSION_REGISTER, /* rejestrowanie */ - GG_SESSION_REMIND, /* przypominanie hasウa */ - GG_SESSION_PASSWD, /* zmiana hasウa */ - GG_SESSION_CHANGE, /* zmiana informacji o sobie */ - GG_SESSION_DCC, /* oglne poウアczenie DCC */ - GG_SESSION_DCC_SOCKET, /* nasウuchujアcy socket */ - GG_SESSION_DCC_SEND, /* wysyウanie pliku */ - GG_SESSION_DCC_GET, /* odbieranie pliku */ - GG_SESSION_DCC_VOICE, /* rozmowa gウosowa */ - GG_SESSION_USERLIST_GET, /* pobieranie userlisty */ - GG_SESSION_USERLIST_PUT, /* wysyウanie userlisty */ - GG_SESSION_UNREGISTER, /* usuwanie konta */ - GG_SESSION_USERLIST_REMOVE, /* usuwanie userlisty */ - GG_SESSION_TOKEN, /* pobieranie tokenu */ + 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ナB */ + GG_SESSION_PASSWD, /**< Zmiana hasナB */ + GG_SESSION_CHANGE, /**< Zmiana informacji w katalogu publicznym (nieaktualne) */ + GG_SESSION_DCC, /**< Poナてczenie bezpoナ孑ednie (do wersji 6.x) */ + GG_SESSION_DCC_SOCKET, /**< Gniazdo nasナVchujトce (do wersji 6.x) */ + GG_SESSION_DCC_SEND, /**< WysyナBnie pliku (do wersji 6.x) */ + GG_SESSION_DCC_GET, /**< Odbieranie pliku (do wersji 6.x) */ + GG_SESSION_DCC_VOICE, /**< Rozmowa gナPsowa (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ナVchujトce (od wersji 7.x) */ + GG_SESSION_DCC7_SEND, /**< WysyナBnie pliku (od wersji 7.x) */ + GG_SESSION_DCC7_GET, /**< Odbieranie pliku (od wersji 7.x) */ + GG_SESSION_DCC7_VOICE, /**< Rozmowa gナPsowa (od wersji 7.x) */ - GG_SESSION_USER0 = 256, /* zdefiniowana dla uソytkownika */ - GG_SESSION_USER1, /* j.w. */ - GG_SESSION_USER2, /* j.w. */ - GG_SESSION_USER3, /* j.w. */ - GG_SESSION_USER4, /* j.w. */ - GG_SESSION_USER5, /* j.w. */ - GG_SESSION_USER6, /* j.w. */ - GG_SESSION_USER7 /* j.w. */ + 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 */ }; -/* - * enum gg_state_t - * - * opisuje stan asynchronicznej maszyny. +/** + * Aktualny stan sesji. */ enum gg_state_t { - /* wsplne */ - GG_STATE_IDLE = 0, /* nie powinno wystアpi. */ - GG_STATE_RESOLVING, /* wywoウaウ gethostbyname() */ - GG_STATE_CONNECTING, /* wywoウaウ connect() */ - GG_STATE_READING_DATA, /* czeka na dane http */ - GG_STATE_ERROR, /* wystアpiウ bウアd. kod w x->error */ + /* 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ナて囘u w polu \c error */ - /* gg_session */ - GG_STATE_CONNECTING_HUB, /* wywoウaウ connect() na huba */ - GG_STATE_CONNECTING_GG, /* wywoウaウ connect() na serwer */ - GG_STATE_READING_KEY, /* czeka na klucz */ - GG_STATE_READING_REPLY, /* czeka na odpowiedシ */ - GG_STATE_CONNECTED, /* poウアczyウ si */ + /* 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, /* wysyウa zapytanie http */ - GG_STATE_READING_HEADER, /* czeka na nagウwek http */ - GG_STATE_PARSING, /* przetwarza dane */ - GG_STATE_DONE, /* skoczyウ */ + /* gg_http */ + GG_STATE_SENDING_QUERY, /**< WysナBno 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_dcc */ + GG_STATE_LISTENING, /* czeka na poナてczenia */ GG_STATE_READING_UIN_1, /* czeka na uin peera */ - GG_STATE_READING_UIN_2, /* czeka na swj uin */ - GG_STATE_SENDING_ACK, /* wysyウa potwierdzenie dcc */ + GG_STATE_READING_UIN_2, /* czeka na swテウj uin */ + GG_STATE_SENDING_ACK, /* wysyナB 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_REQUEST, /* czeka na komendト */ + GG_STATE_SENDING_REQUEST, /* wysyナB komendト */ + GG_STATE_SENDING_FILE_INFO, /* wysyナB 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_SENDING_FILE_ACK, /* wysyナB 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_SENDING_FILE_HEADER, /* wysyナB 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_SENDING_FILE, /* wysyナB 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 */ + GG_STATE_SENDING_VOICE_ACK, /* wysyナB potwierdzenie voip */ + GG_STATE_SENDING_VOICE_REQUEST, /* wysyナB ナシトdanie voip */ + GG_STATE_READING_TYPE, /* czeka na typ poナてczenia */ /* nowe. bez sensu jest to API. */ - GG_STATE_TLS_NEGOTIATION /* negocjuje poウアczenie TLS */ + GG_STATE_TLS_NEGOTIATION, /**< Negocjacja poナてczenia szyfrowanego */ + + GG_STATE_REQUESTING_ID, /**< Oczekiwanie na nadanie identyfikatora poナてczenia bezpoナ孑edniego */ + GG_STATE_WAITING_FOR_ACCEPT, /**< Oczekiwanie na potwierdzenie lub odrzucenie poナてczenia bezpoナ孑edniego */ + GG_STATE_WAITING_FOR_INFO, /**< Oczekiwanie na informacje o poナてczeniu bezpoナ孑ednim */ + + GG_STATE_READING_ID, /**< Odebranie identyfikatora poナてczenia bezpoナ孑edniego */ + GG_STATE_SENDING_ID /**< WysナBno identyfikatora poナてczenia bezpoナ孑edniego */ }; -/* - * enum gg_check_t +/** + * Informacja o tym, czy biblioteka chce zapisywaト i/lub czytaト + * z deskryptora. Maska bitowa. * - * informuje, co proces klienta powinien sprawdzi na deskryptorze danego - * poウアczenia. + * \ingroup events */ enum gg_check_t { - GG_CHECK_NONE = 0, /* nic. nie powinno wystアpi */ - GG_CHECK_WRITE = 1, /* sprawdzamy moソliwoカ zapisu */ - GG_CHECK_READ = 2 /* sprawdzamy moソliwoカ odczytu */ + GG_CHECK_NONE = 0, /**< Nie sprawdzaj niczego */ + GG_CHECK_WRITE = 1, /**< Sprawdナコ moナシliwoナ崙 zapisu */ + GG_CHECK_READ = 2 /**< Sprawdナコ moナシliwoナ崙 odczytu */ }; -/* - * struct gg_login_params +/** + * Parametry poナてczenia z serwerem Gadu-Gadu. Parametry zostaナZ przeniesione + * do struktury, by uniknトト zmian API po rozszerzeniu protokoナV i dodaniu + * kolejnych opcji poナてczenia. Czト卩崙 parametrテウw, ktテウre nie sト juナシ aktualne + * lub nie majト znaczenia, zostaナB usuniト冲a z dokumentacji. * - * parametry gg_login(). przeniesiono do struktury, ソeby uniknア problemw - * z ciアgウymi zmianami API, gdy dodano coカ nowego do protokoウu. + * \ingroup login */ struct gg_login_params { - uin_t uin; /* numerek */ - char *password; /* hasウo */ - int async; /* asynchroniczne sockety? */ - int status; /* poczアtkowy status klienta */ - char *status_descr; /* opis statusu */ - uint32_t server_addr; /* adres serwera gg */ - uint16_t server_port; /* port serwera gg */ - uint32_t client_addr; /* adres dcc klienta */ - uint16_t client_port; /* port dcc klienta */ - int protocol_version; /* wersja protokoウu */ - char *client_version; /* wersja klienta */ - int has_audio; /* czy ma dシwi麑? */ - int last_sysmsg; /* ostatnia wiadomoカ systemowa */ - uint32_t external_addr; /* adres widziany na zewnatrz */ - uint16_t external_port; /* port widziany na zewnatrz */ - int tls; /* czy ウアczymy po TLS? */ - int image_size; /* maksymalny rozmiar obrazka w KiB */ - int era_omnix; /* czy udawa klienta era omnix? */ + uin_t uin; /**< Numer Gadu-Gadu */ + char *password; /**< HasナP */ + int async; /**< Flaga asynchronicznego poナてczenia (domyナ嬪nie nie) */ + int status; /**< Poczトtkowy status uナシytkownika (domyナ嬪nie \c GG_STATUS_AVAIL) */ + char *status_descr; /**< Poczトtkowy opis uナシytkownika (domyナ嬪nie brak) */ + uint32_t server_addr; /**< Adres serwera Gadu-Gadu (domyナ嬪nie pobierany automatycznie) */ + uint16_t server_port; /**< Port serwera Gadu-Gadu (domyナ嬪nie pobierany automatycznie) */ +#ifndef DOXYGEN + uint32_t client_addr; /**< Adres poナてczeナ bezpoナ孑ednich (nieaktualne) */ + uint16_t client_port; /**< Port poナてczeナ bezpoナ孑ednich (nieaktualne) */ +#endif + int protocol_version; /**< Wersja protokoナV wysyナBna do serwera (domyナ嬪nie najnowsza obsナVgiwana) */ + char *client_version; /**< Wersja klienta wysyナBna do serwera (domyナ嬪nie najnowsza znana) */ + int has_audio; /**< Flaga obsナVgi poナてczeナ gナPsowych */ + int last_sysmsg; /**< Numer ostatnio odebranej wiadomoナ嫩i systemowej */ + uint32_t external_addr; /**< Adres publiczny dla poナてczeナ bezpoナ孑ednich (6.x) */ + uint16_t external_port; /**< Port publiczny dla poナてczeナ bezpoナ孑ednich (6.x) */ +#ifndef DOXYGEN + int tls; /**< Flaga poナてczenia szyfrowanego (nieaktualna) */ +#endif + int image_size; /**< Maksymalny rozmiar obsナVgiwanych obrazkテウw w kilobajtach */ +#ifndef DOXYGEN + int era_omnix; /**< Flaga udawania klienta Era Omnix (nieaktualna) */ +#endif + int hash_type; /**< Rodzaj skrテウtu hasナB (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1, domyナ嬪nie SHA1) */ + gg_encoding_t encoding; /**< Rodzaj kodowania uナシywanego w sesji (domyナ嬪nie CP1250) */ + gg_resolver_t resolver; /**< Sposテウb rozwiトzywania nazw (patrz \ref build-resolver) */ + int protocol_features; /**< Opcje protokoナV (flagi GG_FEATURE_*). */ - char dummy[6 * sizeof(int)]; /* miejsce na kolejnych 6 zmiennych, - * ソeby z dodaniem parametru nie - * zmieniaウ si rozmiar struktury */ +#ifndef DOXYGEN + char dummy[2 * sizeof(int)]; /**< \internal Miejsce na kilka kolejnych + parametrテウw, ナシeby wraz z dodawaniem kolejnych + parametrテウw nie zmieniaナ siト rozmiar struktury */ +#endif + }; struct gg_session *gg_login(const struct gg_login_params *p); @@ -367,232 +598,337 @@ int gg_ping(struct gg_session *sess); int gg_userlist_request(struct gg_session *sess, char 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 unsigned char *image, int size); +int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size); uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len); -struct gg_image_queue { - uin_t sender; /* nadawca obrazka */ - uint32_t size; /* rozmiar */ - uint32_t crc32; /* suma kontrolna */ - char *filename; /* nazwa pliku */ - char *image; /* bufor z obrazem */ - uint32_t done; /* ile juソ wczytano */ +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)); - struct gg_image_queue *next; /* nast麪ny na liカcie */ -}; +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)); -/* - * enum gg_event_t +/** + * Rodzaj zdarzenia. * - * rodzaje zdarze. + * \ingroup events */ enum gg_event_t { - GG_EVENT_NONE = 0, /* nic si nie wydarzyウo */ - GG_EVENT_MSG, /* otrzymano wiadomoカ */ - GG_EVENT_NOTIFY, /* ktoカ si pojawiウ */ - GG_EVENT_NOTIFY_DESCR, /* ktoカ si pojawiウ z opisem */ - GG_EVENT_STATUS, /* ktoカ zmieniウ stan */ - GG_EVENT_ACK, /* potwierdzenie wysウania wiadomoカci */ - GG_EVENT_PONG, /* pakiet pong */ - GG_EVENT_CONN_FAILED, /* poウアczenie si nie udaウo */ - GG_EVENT_CONN_SUCCESS, /* poウアczenie si powiodウo */ - GG_EVENT_DISCONNECT, /* serwer zrywa poウアczenie */ + GG_EVENT_NONE = 0, /**< Nie wydarzyナP siト nic wartego uwagi */ + GG_EVENT_MSG, /**< \brief Otrzymano wiadomoナ崙. Przekazuje rテウwnieナシ wiadomoナ嫩i systemowe od numeru 0. */ + GG_EVENT_NOTIFY, /**< \brief Informacja o statusach osテウb z listy kontaktテウw (przed 6.0). Zdarzenie naleナシy obsナVgiwaト, jeナ嬪i planuje siト uナシywaト protokoナV w wersji starszej niナシ domyナ嬪na. */ + GG_EVENT_NOTIFY_DESCR, /**< \brief Informacja o statusie opisowym osoby z listy kontaktテウw (przed 6.0). Zdarzenie naleナシy obsナVgiwaト, jeナ嬪i planuje siト uナシywaト protokoナV w wersji starszej niナシ domyナ嬪na. */ + GG_EVENT_STATUS, /**< \brief Zmiana statusu osoby z listy kontaktテウw (przed 6.0). Zdarzenie naleナシy obsナVgiwaト, jeナ嬪i planuje siト uナシywaト protokoナV w wersji starszej niナシ domyナ嬪na. */ + GG_EVENT_ACK, /**< Potwierdzenie dorト冂zenia wiadomoナ嫩i */ + GG_EVENT_PONG, /**< \brief Utrzymanie poナてczenia. Obecnie serwer nie wysyナB juナシ do klienta ramek utrzymania poナてczenia, polega wyナてcznie na wysyナBniu ramek przez klienta. */ + GG_EVENT_CONN_FAILED, /**< \brief Nie udaナP siト poナてczyト */ + GG_EVENT_CONN_SUCCESS, /**< \brief Poナてczono z serwerem. Pierwszト rzeczト, jakト naleナシy zrobiト jest wysナBnie 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ナ孑ednie (6.x) */ + GG_EVENT_DCC_ERROR, /**< Bナてd poナてczenia bezpoナ孑edniego (6.x) */ + GG_EVENT_DCC_DONE, /**< Zakoナczono poナてczenie bezpoナ孑ednie (6.x) */ + GG_EVENT_DCC_CLIENT_ACCEPT, /**< Moment akceptacji klienta w poナてczeniu bezpoナ孑ednim (6.x) */ + GG_EVENT_DCC_CALLBACK, /**< Zwrotne poナてczenie bezpoナ孑ednie (6.x) */ + GG_EVENT_DCC_NEED_FILE_INFO, /**< Naleナシy wypeナOiト \c file_info dla poナてczenia bezpoナ孑edniego (6.x) */ + GG_EVENT_DCC_NEED_FILE_ACK, /**< Czeka na potwierdzenie pliku w poナてczeniu bezpoナ孑ednim (6.x) */ + GG_EVENT_DCC_NEED_VOICE_ACK, /**< Czeka na potwierdzenie rozmowy w poナてczeniu bezpoナ孑ednim (6.x) */ + GG_EVENT_DCC_VOICE_DATA, /**< Dane bezpoナ孑edniego poナてczenia gナPsowego (6.x) */ - GG_EVENT_DCC_NEW, /* nowe poウアczenie mi鹽zy klientami */ - GG_EVENT_DCC_ERROR, /* bウアd poウアczenia mi鹽zy klientami */ - GG_EVENT_DCC_DONE, /* zakoczono poウアczenie */ - GG_EVENT_DCC_CLIENT_ACCEPT, /* moment akceptacji klienta */ - GG_EVENT_DCC_CALLBACK, /* klient si poウアczyウ na ソアdanie */ - GG_EVENT_DCC_NEED_FILE_INFO, /* naleソy wypeウni file_info */ - GG_EVENT_DCC_NEED_FILE_ACK, /* czeka na potwierdzenie pliku */ - GG_EVENT_DCC_NEED_VOICE_ACK, /* czeka na potwierdzenie rozmowy */ - GG_EVENT_DCC_VOICE_DATA, /* ramka danych rozmowy gウosowej */ + GG_EVENT_PUBDIR50_SEARCH_REPLY, /**< Odpowiedナコ katalogu publicznego */ + GG_EVENT_PUBDIR50_READ, /**< Odczytano wナBsne dane z katalogu publicznego */ + GG_EVENT_PUBDIR50_WRITE, /**< Zmieniono wナBsne 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 */ + GG_EVENT_USERLIST, /**< Wynik importu lub eksportu listy kontaktテウw */ + GG_EVENT_IMAGE_REQUEST, /**< ナサトdanie przesナBnia obrazka z wiadommoナ嫩i */ + GG_EVENT_IMAGE_REPLY, /**< PrzysナBno obrazek z wiadomoナ嫩i */ + GG_EVENT_DCC_ACK, /**< Potwierdzenie transmisji w poナてczeniu bezpoナ孑ednim (6.x) */ - GG_EVENT_PUBDIR50_SEARCH_REPLY, /* odpowiedz wyszukiwania */ - GG_EVENT_PUBDIR50_READ, /* odczytano wウasne dane z katalogu */ - GG_EVENT_PUBDIR50_WRITE, /* wpisano wウasne dane do katalogu */ + GG_EVENT_DCC7_NEW, /**< Nowe poナてczenie bezpoナ孑ednie (7.x) */ + GG_EVENT_DCC7_ACCEPT, /**< Zaakceptowano poナてczenie bezpoナ孑ednie (7.x), nowy deskryptor */ + GG_EVENT_DCC7_REJECT, /**< Odrzucono poナてczenie bezpoナ孑ednie (7.x) */ + GG_EVENT_DCC7_CONNECTED, /**< Zestawiono poナてczenie bezpoナ孑ednie (7.x), nowy deskryptor */ + GG_EVENT_DCC7_ERROR, /**< Bナてd poナてczenia bezpoナ孑edniego (7.x) */ + GG_EVENT_DCC7_DONE, /**< Zakoナczono poナてczenie bezpoナ孑ednie (7.x) */ + GG_EVENT_DCC7_PENDING, /**< Trwa prテウba poナてczenia bezpoナ孑edniego (7.x), nowy deskryptor */ - GG_EVENT_STATUS60, /* ktoカ zmieniウ stan w GG 6.0 */ - GG_EVENT_NOTIFY60, /* ktoカ si pojawiウ w GG 6.0 */ - GG_EVENT_USERLIST, /* odpowiedシ listy kontaktw w GG 6.0 */ - GG_EVENT_IMAGE_REQUEST, /* proカba o wysウanie obrazka GG 6.0 */ - GG_EVENT_IMAGE_REPLY, /* podesウany obrazek GG 6.0 */ - GG_EVENT_DCC_ACK /* potwierdzenie transmisji */ + 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ト冪ny z opisem dotarナB do serwera i moナシna zakoナczyト poナてczenie TCP. */ }; #define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY -/* - * enum gg_failure_t - * - * okreカla powd nieudanego poウアczenia. +/** + * Powテウd nieudanego poナてczenia. */ enum gg_failure_t { - GG_FAILURE_RESOLVING = 1, /* nie znaleziono serwera */ - GG_FAILURE_CONNECTING, /* nie moソna si poウアczy */ - GG_FAILURE_INVALID, /* serwer zwrciウ 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, /* XXX nieuソywane */ - GG_FAILURE_TLS, /* bウアd negocjacji TLS */ - GG_FAILURE_NEED_EMAIL /* serwer rozウアczyウ nas z proカbア o zmian emaila */ + GG_FAILURE_RESOLVING = 1, /**< Nie znaleziono serwera */ + GG_FAILURE_CONNECTING, /**< Bナてd poナてczenia */ + GG_FAILURE_INVALID, /**< Serwer zwrテウciナ nieprawidナPwe dane */ + GG_FAILURE_READING, /**< Zerwano poナてczenie podczas odczytu */ + GG_FAILURE_WRITING, /**< Zerwano poナてczenie podczas zapisu */ + GG_FAILURE_PASSWORD, /**< NieprawidナPwe hasナP */ + GG_FAILURE_404, /**< Nieuナシywane */ + GG_FAILURE_TLS, /**< Bナてd negocjacji szyfrowanego poナてczenia */ + GG_FAILURE_NEED_EMAIL, /**< Serwer rozナてczyナ nas z proナ嫦ト o zmianト adresu e-mail */ + GG_FAILURE_INTRUDER, /**< Zbyt wiele prテウb poナてczenia z nieprawidナPwym hasナFm */ + GG_FAILURE_UNAVAILABLE /**< Serwery sト wyナてczone */ }; -/* - * enum gg_error_t +/** + * Kod bナて囘u danej operacji. * - * okreカla rodzaj bウ鹽u wywoウanego przez danア operacj. nie zawiera - * przesadnie szczegウowych informacji o powodzie bウ鹽u, by nie komplikowa - * obsウugi bウ鹽w. jeカli wymagana jest wi麑sza dokウadnoカ, naleソy sprawdzi - * zawartoカ zmiennej errno. + * Nie zawiera przesadnie szczegテウナPwych informacji o powodach bナて囘テウw, by nie + * komplikowaト ich obsナVgi. Jeナ嬪i wymagana jest wiト冖sza dokナBdnoナ崙, naleナシy + * sprawdziト zawartoナ崙 zmiennej systemowej \c errno. */ enum gg_error_t { - GG_ERROR_RESOLVING = 1, /* bウアd znajdowania hosta */ - GG_ERROR_CONNECTING, /* bウアd ウaczenia si */ - GG_ERROR_READING, /* bウアd odczytu */ - GG_ERROR_WRITING, /* bウアd wysyウania */ + 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ナBnia */ - GG_ERROR_DCC_HANDSHAKE, /* bウアd negocjacji */ - GG_ERROR_DCC_FILE, /* bウアd odczytu/zapisu pliku */ - GG_ERROR_DCC_EOF, /* plik si skoczyウ? */ - GG_ERROR_DCC_NET, /* bウアd wysyウania/odbierania */ - GG_ERROR_DCC_REFUSED /* poウアczenie odrzucone przez usera */ + 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ナBnia/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ナBnia/odbierania */ + GG_ERROR_DCC7_REFUSED /**< Poナてczenie odrzucone */ }; -/* - * struktury dotyczアce wyszukiwania w GG 5.0. NIE NALEッY SIハ DO NICH - * ODWO」YWAニ BEZPOヲREDNIO! do dost麪u do nich sウuソア funkcje gg_pubdir50_*() +/** + * Pole zapytania lub odpowiedzi katalogu publicznego. */ struct gg_pubdir50_entry { - int num; - char *field; - char *value; -}; + 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; - uin_t next; - int type; - uint32_t seq; - struct gg_pubdir50_entry *entries; - int entries_count; -}; + int count; /**< Liczba wynikテウw odpowiedzi */ + uin_t next; /**< Numer poczトtkowy nastト冪nego 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 */; -/* - * typedef gg_pubdir_50_t +/** + * Zapytanie lub odpowiedナコ katalogu publicznego. * - * typ opisujアcy zapytanie lub wynik zapytania katalogu publicznego - * z protokoウu GG 5.0. nie naleソy si odwoウywa bezpoカrednio do jego - * pl -- sウuソア do tego funkcje gg_pubdir50_*() + * Do pテウl nie naleナシy siト odwoナZwaト bezpoナ孑ednio -- wszystkie niezbト囘ne + * informacje sト dostト冪ne za pomocト funkcji \c gg_pubdir50_* */ typedef struct gg_pubdir50_s *gg_pubdir50_t; -/* - * struct gg_event +/** + * Opis zdarzenia \c GG_EVENT_MSG. + */ +struct gg_event_msg { + uin_t sender; /**< Numer nadawcy */ + int msgclass; /**< Klasa wiadomoナ嫩i */ + time_t time; /**< Czas nadania */ + unsigned char *message; /**< Treナ崙 wiadomoナ嫩i */ + + int recipients_count; /**< Liczba odbiorcテウw konferencji */ + uin_t *recipients; /**< Odbiorcy konferencji */ + + int formats_length; /**< DナVgoナ崙 informacji o formatowaniu tekstu */ + void *formats; /**< Informacje o formatowaniu tekstu */ + uint32_t seq; /**< Numer sekwencyjny wiadomoナ嫩i */ + + char *xhtml_message; /**< Treナ崙 wiadomoナ嫩i w formacie XHTML (moナシe byト rテウwne \c NULL, jeナ嬪i wiadomoナ崙 nie zawiera treナ嫩i XHTML) */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_NOTIFY_DESCR. + */ +struct gg_event_notify_descr { + struct gg_notify_reply *notify; /**< Informacje o liナ嫩ie 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ナ孑ednich */ + uint16_t remote_port; /**< Port dla poナてczeナ bezpoナ孑ednich */ + int version; /**< Wersja protokoナV */ + int image_size; /**< Maksymalny rozmiar obsナVgiwanych obrazkテウw w KiB */ + char *descr; /**< Opis statusu */ + time_t time; /**< Czas powrotu */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_NOTIFY_REPLY60. + */ +struct gg_event_notify60 { + uin_t uin; /**< Numer Gadu-Gadu */ + int status; /**< Nowy status */ + uint32_t remote_ip; /**< Adres IP dla poナてczeナ bezpoナ孑ednich */ + uint16_t remote_port; /**< Port dla poナてczeナ bezpoナ孑ednich */ + int version; /**< Wersja protokoナV */ + int image_size; /**< Maksymalny rozmiar obsナVgiwanych obrazkテウw w KiB */ + char *descr; /**< Opis statusu */ + time_t time; /**< Czas powrotu */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_ACK. + */ +struct gg_event_ack { + uin_t recipient; /**< Numer odbiorcy */ + int status; /**< Status dorト冂zenia */ + int seq; /**< Numer sekwencyjny wiadomoナ嫩i */ +}; + +/** + * 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ト冖owe */ + int length; /**< Rozmiar danych dナコwiト冖owych */ +}; + +/** + * 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_DCC7_CONNECTED. + */ +struct gg_event_dcc7_connected { + struct gg_dcc7 *dcc7; /**< Struktura poナてczenia */ + // XXX czy coナ siト przyda? +}; + +/** + * 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 */ +}; + +/** + * Unia wszystkich zdarzeナ zwracanych przez funkcje \c gg_watch_fd(), + * \c gg_dcc_watch_fd() i \c gg_dcc7_watch_fd(). * - * struktura opisujアca rodzaj zdarzenia. wychodzi z gg_watch_fd() lub - * z gg_dcc_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ナ嫩i (\c GG_EVENT_ACK) */ + struct gg_event_image_request image_request; /**< ナサトdanie wysナBnia 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_dcc *dcc_new; /**< Nowe poナてczenie bezpoナ孑ednie (\c GG_EVENT_DCC_NEW) */ + enum gg_error_t dcc_error; /**< Bナてd poナてczenia bezpoナ孑edniego (\c GG_EVENT_DCC_ERROR) */ + struct gg_event_dcc_voice_data dcc_voice_data; /**< Dane poナてczenia gナPsowego (\c GG_EVENT_DCC_VOICE_DATA) */ + struct gg_dcc7 *dcc7_new; /**< Nowe poナてczenie bezpoナ孑ednie (\c GG_EVENT_DCC7_NEW) */ + enum gg_error_t dcc7_error; /**< Bナてd poナてczenia bezpoナ孑edniego (\c GG_EVENT_DCC7_ERROR) */ + struct gg_event_dcc7_connected dcc7_connected; /**< Informacja o zestawieniu poナてczenia bezpoナ孑edniego (\c GG_EVENT_DCC7_CONNECTED) */ + struct gg_event_dcc7_reject dcc7_reject; /**< Odrzucono poナてczenia bezpoナ孑edniego (\c GG_EVENT_DCC7_REJECT) */ + struct gg_event_dcc7_accept dcc7_accept; /**< Zaakceptowano poナてczenie bezpoナ孑ednie (\c GG_EVENT_DCC7_ACCEPT) */ +}; + +/** + * 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 -- gg_event_t */ - union { /* @event */ - struct gg_notify_reply *notify; /* informacje o liカcie kontaktw -- GG_EVENT_NOTIFY */ - - enum gg_failure_t failure; /* bウアd poウアczenia -- GG_EVENT_FAILURE */ - - struct gg_dcc *dcc_new; /* nowe poウアczenie bezpoカrednie -- GG_EVENT_DCC_NEW */ - - int dcc_error; /* bウアd poウアczenia bezpoカredniego -- GG_EVENT_DCC_ERROR */ - - gg_pubdir50_t pubdir50; /* wynik operacji zwiアzanej z katalogiem publicznym -- GG_EVENT_PUBDIR50_* */ - - struct { /* @msg odebrano wiadomoカ -- GG_EVENT_MSG */ - uin_t sender; /* numer nadawcy */ - int msgclass; /* klasa wiadomoカci */ - time_t time; /* czas nadania */ - unsigned char *message; /* treカ wiadomoカci */ - - int recipients_count; /* iloカ odbiorcw konferencji */ - uin_t *recipients; /* odbiorcy konferencji */ - - int formats_length; /* dウugoカ informacji o formatowaniu tekstu */ - void *formats; /* informacje o formatowaniu tekstu */ - } msg; - - struct { /* @notify_descr informacje o liカcie kontaktw z opisami stanu -- GG_EVENT_NOTIFY_DESCR */ - struct gg_notify_reply *notify; /* informacje o liカcie kontaktw */ - char *descr; /* opis stanu */ - } notify_descr; - - struct { /* @status zmiana stanu -- GG_EVENT_STATUS */ - uin_t uin; /* numer */ - uint32_t status; /* nowy stan */ - char *descr; /* opis stanu */ - } status; - - struct { /* @status60 zmiana stanu -- GG_EVENT_STATUS60 */ - uin_t uin; /* numer */ - int status; /* nowy stan */ - uint32_t remote_ip; /* adres ip */ - uint16_t remote_port; /* port */ - int version; /* wersja klienta */ - int image_size; /* maksymalny rozmiar grafiki w KiB */ - char *descr; /* opis stanu */ - time_t time; /* czas powrotu */ - } status60; - - struct { /* @notify60 informacja o liカcie kontaktw -- GG_EVENT_NOTIFY60 */ - uin_t uin; /* numer */ - int status; /* stan */ - uint32_t remote_ip; /* adres ip */ - uint16_t remote_port; /* port */ - int version; /* wersja klienta */ - int image_size; /* maksymalny rozmiar grafiki w KiB */ - char *descr; /* opis stanu */ - time_t time; /* czas powrotu */ - } *notify60; - - struct { /* @ack potwierdzenie wiadomoカci -- GG_EVENT_ACK */ - uin_t recipient; /* numer odbiorcy */ - int status; /* stan dor鹹zenia wiadomoカci */ - int seq; /* numer sekwencyjny wiadomoカci */ - } ack; - - struct { /* @dcc_voice_data otrzymano dane dシwi麑owe -- GG_EVENT_DCC_VOICE_DATA */ - uint8_t *data; /* dane dシwi麑owe */ - int length; /* iloカ danych dシwi麑owych */ - } dcc_voice_data; - - struct { /* @userlist odpowiedシ listy kontaktw serwera */ - char type; /* rodzaj odpowiedzi */ - char *reply; /* treカ odpowiedzi */ - } userlist; - - struct { /* @image_request proカba o obrazek */ - uin_t sender; /* nadawca proカby */ - uint32_t size; /* rozmiar obrazka */ - uint32_t crc32; /* suma kontrolna */ - } image_request; - - struct { /* @image_reply odpowiedシ z obrazkiem */ - uin_t sender; /* nadawca odpowiedzi */ - uint32_t size; /* rozmiar obrazka */ - uint32_t crc32; /* suma kontrolna */ - char *filename; /* nazwa pliku */ - char *image; /* bufor z obrazkiem */ - } image_reply; - } 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); -#define gg_free_event gg_event_free -/* - * funkcje obsウugi listy kontaktw. - */ 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); @@ -600,76 +936,11 @@ 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); -/* - * funkcje obsウugi http. - */ 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); -void gg_http_free_fields(struct gg_http *h); -#define gg_free_http gg_http_free -/* - * struktury opisujアca kryteria wyszukiwania dla gg_search(). nieaktualne, - * zastアpione przez gg_pubdir50_t. pozostawiono je dla zachowania ABI. - */ -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; -}; - -struct gg_search { - int count; - struct gg_search_result *results; -}; - -struct gg_search_result { - uin_t uin; - char *first_name; - char *last_name; - char *nickname; - int born; - int gender; - char *city; - int active; -}; - -#define GG_GENDER_NONE 0 -#define GG_GENDER_FEMALE 1 -#define GG_GENDER_MALE 2 - -/* - * funkcje wyszukiwania. - */ -struct gg_http *gg_search(const struct gg_search_request *r, int async); -int gg_search_watch_fd(struct gg_http *f); -void gg_free_search(struct gg_http *f); -#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); -const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start); -const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start); -const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start); -void gg_search_request_free(struct gg_search_request *r); - -/* - * funkcje obsウugi katalogu publicznego zgodne z GG 5.0. tym razem funkcje - * zachowujア pewien poziom abstrakcji, ソeby uniknア zmian ABI przy zmianach - * w protokole. - * - * NIE NALEッY SIハ ODWO」YWAニ DO PモL gg_pubdir50_t BEZPOヲREDNIO! - */ 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); @@ -681,6 +952,8 @@ 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" @@ -699,110 +972,114 @@ #define GG_PUBDIR50_FAMILYNAME "familyname" #define GG_PUBDIR50_FAMILYCITY "familycity" -int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length); +#else -/* - * struct gg_pubdir +/** + * \ingroup pubdir50 * - * operacje na katalogu publicznym. + * Rodzaj pola zapytania. */ -struct gg_pubdir { - int success; /* czy si udaウo */ - uin_t uin; /* otrzymany numerek. 0 jeカli bウアd */ +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ナFト */ + GG_PUBDIR50_ACTIVE, /**< Osoba dostト冪na (tylko wyszukiwanie) */ + GG_PUBDIR50_START, /**< Numer poczトtkowy wyszukiwania (tylko wyszukiwanie) */ + GG_PUBDIR50_FAMILYNAME, /**< Nazwisko rodowe (tylko wysyナBnie informacji o sobie) */ + GG_PUBDIR50_FAMILYCITY, /**< Miejscowoナ崙 pochodzenia (tylko wysyナBnie informacji o sobie) */ +}; + +/** + * \ingroup pubdir50 + * + * Wartoナ崙 pola GG_PUBDIR50_GENDER przy wyszukiwaniu. Brak pola oznacza dowolnト pナFト. + */ +enum { + GG_PUBDIR50_GENDER_FEMALE, /**< Kobieta */ + GG_PUBDIR50_GENDER_MALE, /**< Mト卩シczyzna */ }; -/* oglne funkcje, nie powinny by uソywane */ +/** + * \ingroup pubdir50 + * + * Wartoナ崙 pola GG_PUBDIR50_GENDER przy wysyナBniu 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ト冪ne */ +}; + +#endif /* DOXYGEN */ + +/** + * 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ナて囘u */ +}; + int gg_pubdir_watch_fd(struct gg_http *f); void gg_pubdir_free(struct gg_http *f); -#define gg_free_pubdir gg_pubdir_free +/** + * Token autoryzacji niektテウrych operacji HTTP. + * + * \ingroup token + */ struct gg_token { - int width; /* szerokoカ obrazka */ - int height; /* wysokoカ obrazka */ - int length; /* iloカ znakw w tokenie */ - char *tokenid; /* id tokenu */ + int width; /**< Szerokoナ崙 obrazka */ + int height; /**< Wysokoナ崙 obrazka */ + int length; /**< Liczba znakテウw w tokenie */ + char *tokenid; /**< Identyfikator tokenu */ }; -/* funkcje dotyczアce tokenw */ struct gg_http *gg_token(int async); int gg_token_watch_fd(struct gg_http *h); void gg_token_free(struct gg_http *h); -/* rejestracja nowego numerka */ -struct gg_http *gg_register(const char *email, const char *password, int async); -struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async); 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 -#define gg_free_register gg_pubdir_free +#endif -struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async); -struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async); 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 -/* przypomnienie hasウa e-mailem */ -struct gg_http *gg_remind_passwd(uin_t uin, int async); -struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async); 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 -#define gg_free_remind_passwd gg_pubdir_free - -/* zmiana hasウa */ -struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async); -struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async); -struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async); -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); -#define gg_change_passwd_free gg_pubdir_free -#define gg_free_change_passwd gg_pubdir_free - -/* - * struct gg_change_info_request - * - * opis ソアdania zmiany informacji w katalogu publicznym. - */ -struct gg_change_info_request { - char *first_name; /* imi */ - char *last_name; /* nazwisko */ - char *nickname; /* pseudonim */ - char *email; /* email */ - int born; /* rok urodzenia */ - int gender; /* pウe */ - char *city; /* miasto */ -}; - -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); -void gg_change_info_request_free(struct gg_change_info_request *r); +#endif -struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async); -#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 - -/* - * funkcje dotyczアce listy kontaktw na serwerze. - */ -struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async); -int gg_userlist_get_watch_fd(struct gg_http *f); -void gg_userlist_get_free(struct gg_http *f); +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 -struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async); -int gg_userlist_put_watch_fd(struct gg_http *f); -void gg_userlist_put_free(struct gg_http *f); - -struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async); -int gg_userlist_remove_watch_fd(struct gg_http *f); -void gg_userlist_remove_free(struct gg_http *f); - - - -/* - * funkcje dotyczアce komunikacji mi鹽zy klientami. - */ -extern int gg_dcc_port; /* port, na ktrym nasウuchuje klient */ -extern unsigned long gg_dcc_ip; /* adres, na ktrym nasウuchuje klient */ +extern int gg_dcc_port; +extern unsigned long gg_dcc_ip; int gg_dcc_request(struct gg_session *sess, uin_t uin); @@ -814,119 +1091,248 @@ 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 -#define GG_DCC_VOICE_FRAME_LENGTH_505 326 +#define GG_DCC_VOICE_FRAME_LENGTH 195 /**< Rozmiar pakietu gナPsowego przed wersjト Gadu-Gadu 5.0.5 */ +#define GG_DCC_VOICE_FRAME_LENGTH_505 326 /**< Rozmiar pakietu gナPsowego od wersji Gadu-Gadu 5.0.5 */ struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port); -#define gg_dcc_socket_free gg_free_dcc +#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); -#define gg_free_dcc gg_dcc_free - -/* - * jeカli chcemy sobie podebugowa, wystarczy ustawi `gg_debug_level'. - * niestety w miar przybywania wpisw `gg_debug(...)' nie chciaウo mi - * si ustawia odpowiednich leveli, wi鹹 wi麑szoカ szウa do _MISC. - */ -extern int gg_debug_level; /* poziom debugowania. mapa bitowa staウych GG_DEBUG_* */ -/* - * moソna poda wskaシnik do funkcji obsウugujアcej wywoウania gg_debug(). - * nieoficjalne, nieudokumentowane, moソe si zmieni. jeカli ktoカ jest - * zainteresowany, niech da zna na ekg-devel. - */ +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); -/* - * moソna poda plik, do ktrego b鹽ア zapisywane teksty z gg_debug(). - */ extern FILE *gg_debug_file; -#define GG_DEBUG_NET 1 -#define GG_DEBUG_TRAFFIC 2 -#define GG_DEBUG_DUMP 4 -#define GG_DEBUG_FUNCTION 8 -#define GG_DEBUG_MISC 16 +/** + * \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ナ嫩i pakietテウw */ +#define GG_DEBUG_FUNCTION 8 /**< Rejestracja wywoナBナ funkcji */ +#define GG_DEBUG_MISC 16 /**< Rejestracja rテウナシnych informacji */ +/** @} */ #ifdef GG_DEBUG_DISABLE #define gg_debug(x, y...) do { } while(0) +#define gg_debug_session(z, x, y...) do { } while(0) #else void gg_debug(int level, const char *format, ...); +void gg_debug_session(struct gg_session *sess, int level, const char *format, ...); #endif const char *gg_libgadu_version(void); -/* - * konfiguracja http proxy. - */ -extern int gg_proxy_enabled; /* wウアcza obsウug proxy */ -extern char *gg_proxy_host; /* okreカla adres serwera proxy */ -extern int gg_proxy_port; /* okreカla port serwera proxy */ -extern char *gg_proxy_username; /* okreカla nazw uソytkownika przy autoryzacji serwera proxy */ -extern char *gg_proxy_password; /* okreカla hasウo uソytkownika przy autoryzacji serwera proxy */ -extern int gg_proxy_http_only; /* wウアcza obsウug proxy wyウアcznie dla usウug HTTP */ +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 -/* - * adres, z ktrego カlemy pakiety (np ウアczymy si z serwerem) - * uソywany przy gg_connect() +#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. */ -extern unsigned long gg_local_ip; -/* - * ------------------------------------------------------------------------- - * poniソej znajdujア si wewn黎rzne sprawy biblioteki. zwykウy klient nie - * powinien ich w ogle rusza, bo i nie ma po co. wszystko moソna zaウatwi - * procedurami wyソszego poziomu, ktrych definicje znajdujア si na poczアtku - * tego pliku. - * ------------------------------------------------------------------------- - */ +enum { + GG_PUBDIR50_WRITE, /**< WysナBnie 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 -#ifdef __GG_LIBGADU_HAVE_PTHREAD -int gg_resolve_pthread(int *fd, void **resolver, const char *hostname); -#elif defined _WIN32 -int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname); +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; + +#ifdef __GNUC__ +char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))) GG_DEPRECATED; +#else +char *gg_saprintf(const char *format, ...) GG_DEPRECATED; #endif -#ifdef _WIN32 -int gg_thread_socket(int thread_id, int socket); -#endif - -int gg_resolve(int *fd, int *pid, const char *hostname); - -#if defined __GNUC__ && !defined _WIN32 -char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); -#else -char *gg_saprintf(const char *format, ...); -#endif - -char *gg_vsaprintf(const char *format, va_list ap); +char *gg_vsaprintf(const char *format, va_list ap) GG_DEPRECATED; #define gg_alloc_sprintf gg_saprintf -char *gg_get_line(char **ptr); +char *gg_get_line(char **ptr) GG_DEPRECATED; -int gg_connect(void *addr, int port, int async); -struct in_addr *gg_gethostbyname(const char *hostname); -char *gg_read_line(int sock, char *buf, int length); -void gg_chomp(char *line); -char *gg_urlencode(const char *str); -int gg_http_hash(const char *format, ...); -int gg_read(struct gg_session *sess, char *buf, int length); -int gg_write(struct gg_session *sess, const char *buf, int length); -void *gg_recv_packet(struct gg_session *sess); -int gg_send_packet(struct gg_session *sess, int type, ...); -unsigned int gg_login_hash(const unsigned char *password, unsigned int seed); -uint32_t gg_fix32(uint32_t x); -uint16_t gg_fix16(uint16_t x); +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) GG_DEPRECATED; +uint16_t gg_fix16(uint16_t x) GG_DEPRECATED; #define fix16 gg_fix16 #define fix32 gg_fix32 -char *gg_proxy_auth(void); -char *gg_base64_encode(const char *buf); -char *gg_base64_decode(const char *buf); -int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq); +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 */ +} GG_DEPRECATED; + +int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED; +int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED; +int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED; +int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED; +int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED; #define GG_APPMSG_HOST "appmsg.gadu-gadu.pl" #define GG_APPMSG_PORT 80 @@ -941,63 +1347,111 @@ #define GG_HTTPS_PORT 443 #define GG_HTTP_USERAGENT "Mozilla/4.7 [en] (Win98; I)" -#define GG_DEFAULT_CLIENT_VERSION "6, 1, 0, 158" -#define GG_DEFAULT_PROTOCOL_VERSION 0x24 +#define GG_DEFAULT_CLIENT_VERSION "8.0.0.7669" +#define GG_DEFAULT_PROTOCOL_VERSION 0x2e #define GG_DEFAULT_TIMEOUT 30 #define GG_HAS_AUDIO_MASK 0x40000000 +#define GG_HAS_AUDIO7_MASK 0x20000000 #define GG_ERA_OMNIX_MASK 0x04000000 -#define GG_LIBGADU_VERSION "1.5.20050718" +#undef GG_LIBGADU_VERSION + +#ifndef DOXYGEN + +#define GG_FEATURE_MSG77 0x01 +#define GG_FEATURE_STATUS77 0x02 +#define GG_FEATURE_DND_FFC 0x10 +#define GG_FEATURE_IMAGE_DESCR 0x20 + +/* Poniナシsze makra zostaナZ zachowane dla zgodnoナ嫩i API */ +#define GG_FEATURE_MSG80 0 +#define GG_FEATURE_STATUS80 0 +#define GG_FEATURE_STATUS80BETA 0 +#define GG_FEATURE_ALL (GG_FEATURE_DND_FFC | GG_FEATURE_IMAGE_DESCR) + +#else + +/** + * \ingroup login + * + * Flagi opcji protokoナV. + */ +enum { + GG_FEATURE_MSG77, /**< Klient ナシyczy sobie otrzymywaト wiadomoナ嫩i zgodnie z protokoナFm 7.7 */ + GG_FEATURE_STATUS77, /**< Klient ナシyczy sobie otrzymywaト zmiany stanu zgodnie z protokoナFm 7.7 */ + GG_FEATURE_DND_FFC, /**< Klient obsナVguje statusy "nie przeszkadzaト" i "poGGadaj ze mnト" */ + GG_FEATURE_IMAGE_DESCR, /**< Klient obsナVguje 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 */ + uint32_t length; /* dナVgoナ崙 reszty pakietu */ } GG_PACKED; #define GG_WELCOME 0x0001 #define GG_NEED_EMAIL 0x0014 struct gg_welcome { - uint32_t key; /* klucz szyfrowania hasウa */ + uint32_t key; /* klucz szyfrowania hasナB */ } GG_PACKED; #define GG_LOGIN 0x000c struct gg_login { - uint32_t uin; /* mj numerek */ - uint32_t hash; /* hash hasウa */ - uint32_t status; /* status na dzie dobry */ + uint32_t uin; /* mテウj numerek */ + uint32_t hash; /* hash hasナB */ + uint32_t status; /* status na dzieナ dobry */ uint32_t version; /* moja wersja klienta */ - uint32_t local_ip; /* mj adres ip */ - uint16_t local_port; /* port, na ktrym sウucham */ + uint32_t local_ip; /* mテウj adres ip */ + uint16_t local_port; /* port, na ktテウrym sナVcham */ } GG_PACKED; #define GG_LOGIN_EXT 0x0013 struct gg_login_ext { - uint32_t uin; /* mj numerek */ - uint32_t hash; /* hash hasウa */ - uint32_t status; /* status na dzie dobry */ + uint32_t uin; /* mテウj numerek */ + uint32_t hash; /* hash hasナB */ + uint32_t status; /* status na dzieナ dobry */ uint32_t version; /* moja wersja klienta */ - uint32_t local_ip; /* mj adres ip */ - uint16_t local_port; /* port, na ktrym sウucham */ - uint32_t external_ip; /* zewn黎rzny adres ip */ - uint16_t external_port; /* zewn黎rzny port */ + uint32_t local_ip; /* mテウj adres ip */ + uint16_t local_port; /* port, na ktテウrym sナVcham */ + uint32_t external_ip; /* zewnト冲rzny adres ip */ + uint16_t external_port; /* zewnト冲rzny port */ } GG_PACKED; #define GG_LOGIN60 0x0015 struct gg_login60 { - uint32_t uin; /* mj numerek */ - uint32_t hash; /* hash hasウa */ - uint32_t status; /* status na dzie dobry */ + uint32_t uin; /* mテウj numerek */ + uint32_t hash; /* hash hasナB */ + uint32_t status; /* status na dzieナ dobry */ uint32_t version; /* moja wersja klienta */ uint8_t dunno1; /* 0x00 */ - uint32_t local_ip; /* mj adres ip */ - uint16_t local_port; /* port, na ktrym sウucham */ - uint32_t external_ip; /* zewn黎rzny adres ip */ - uint16_t external_port; /* zewn黎rzny port */ + uint32_t local_ip; /* mテウj adres ip */ + uint16_t local_port; /* port, na ktテウrym sナVcham */ + uint32_t external_ip; /* zewnト冲rzny adres ip */ + uint16_t external_port; /* zewnト冲rzny 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ナB */ + uint8_t hash[64]; /* hash hasナB dopeナOiony 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ナVcham */ + uint32_t external_ip; /* zewnト冲rzny adres ip (???) */ + uint16_t external_port; /* zewnト冲rzny port (???) */ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ uint8_t dunno2; /* 0xbe */ } GG_PACKED; @@ -1008,70 +1462,135 @@ #define GG_PUBDIR50_REQUEST 0x0014 -#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 - struct gg_pubdir50_request { uint8_t type; /* GG_PUBDIR50_* */ - uint32_t seq; /* czas wysウania zapytania */ + uint32_t seq; /* czas wysナBnia zapytania */ } GG_PACKED; #define GG_PUBDIR50_REPLY 0x000e struct gg_pubdir50_reply { uint8_t type; /* GG_PUBDIR50_* */ - uint32_t seq; /* czas wysウania zapytania */ + uint32_t seq; /* czas wysナBnia zapytania */ } GG_PACKED; #define GG_NEW_STATUS 0x0002 -#define GG_STATUS_NOT_AVAIL 0x0001 /* niedost麪ny */ -#define GG_STATUS_NOT_AVAIL_DESCR 0x0015 /* niedost麪ny z opisem (4.8) */ -#define GG_STATUS_AVAIL 0x0002 /* dost麪ny */ -#define GG_STATUS_AVAIL_DESCR 0x0004 /* dost麪ny z opisem (4.9) */ -#define GG_STATUS_BUSY 0x0003 /* zaj黎y */ -#define GG_STATUS_BUSY_DESCR 0x0005 /* zaj黎y z opisem (4.8) */ -#define GG_STATUS_INVISIBLE 0x0014 /* niewidoczny (4.6) */ -#define GG_STATUS_INVISIBLE_DESCR 0x0016 /* niewidoczny z opisem (4.9) */ -#define GG_STATUS_BLOCKED 0x0006 /* zablokowany */ +#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_IMAGE_MASK 0x0100 +#define GG_STATUS_DESCR_MASK 0x4000 +#define GG_STATUS_FRIENDS_MASK 0x8000 + +#else -#define GG_STATUS_FRIENDS_MASK 0x8000 /* tylko dla znajomych (4.6) */ - -#define GG_STATUS_DESCR_MAXSIZE 70 +/** + * Rodzaje statusテウw uナシytkownika. + * + * \ingroup status + */ +enum { + GG_STATUS_NOT_AVAIL, /**< Niedostト冪ny */ + GG_STATUS_NOT_AVAIL_DESCR, /**< Niedostト冪ny z opisem */ + GG_STATUS_FFC, /**< PoGGadaj ze mnト */ + GG_STATUS_FFC_DESCR, /**< PoGGadaj ze mnト z opisem */ + GG_STATUS_AVAIL, /**< Dostト冪ny */ + GG_STATUS_AVAIL_DESCR, /**< Dostト冪ny z opisem */ + GG_STATUS_BUSY, /**< Zajト冲y */ + GG_STATUS_BUSY_DESCR, /**< Zajト冲y z opisem */ + GG_STATUS_DND, /**< Nie przeszkadzaト */ + GG_STATUS_DND_DESCR, /**< Nie przeszakdzaト z opisem */ + GG_STATUS_INVISIBLE, /**< Niewidoczny (tylko wナBsny status) */ + GG_STATUS_INVISIBLE_DESCR, /**< Niewidoczny z opisem (tylko wナBsny status) */ + GG_STATUS_BLOCKED, /**< Zablokowany (tylko status innych) */ + GG_STATUS_IMAGE_MASK, /**< Flaga bitowa oznaczajトca opis graficzny (tylko jeナ嬪i wybrano \c GG_FEATURE_IMAGE_DESCR) */ + GG_STATUS_DESCR_MASK, /**< Flaga bitowa oznaczajトca status z opisem (tylko jeナ嬪i wybrano \c GG_FEATURE_IMAGE_DESCR) */ + GG_STATUS_FRIENDS_MASK, /**< Flaga bitowa dostト冪noナ嫩i tylko dla znajomych */ +}; -/* - * makra do ウatwego i szybkiego sprawdzania stanu. +#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ナVgoナ嫩 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鹽nienia trybu tylko dla znajomych */ -#define GG_S(x) ((x) & ~GG_STATUS_FRIENDS_MASK) +/* GG_S() stan bez uwzglト囘nienia dodatkowych flag */ +#define GG_S(x) ((x) & GG_STATUS_MASK) -/* GG_S_A() dost麪ny */ -#define GG_S_A(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR) + +/* GG_S_FF() chト冲ny do rozmowy */ +#define GG_S_FF(x) (GG_S(x) == GG_STATUS_FFC || GG_S(x) == GG_STATUS_FFC_DESCR) -/* GG_S_NA() niedost麪ny */ -#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR) +/* GG_S_AV() dostト冪ny */ +#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_B() zaj黎y */ -#define GG_S_B(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ト冪ny */ +#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ト冪ny lub chト冲ny do rozmowy */ +#define GG_S_A(x) (GG_S_FF(x) || GG_S_AV(x)) + +/* GG_S_B() zajト冲y 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_AVAIL_DESCR || GG_S(x) == GG_STATUS_BUSY_DESCR || GG_S(x) == GG_STATUS_INVISIBLE_DESCR) +#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 */ +/* 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; /* na jaki zmieni? */ + uint32_t status; /**< Nowy status */ } GG_PACKED; #define GG_NOTIFY_FIRST 0x000f @@ -1081,12 +1600,29 @@ struct gg_notify { uint32_t uin; /* numerek danej osoby */ - uint8_t dunno1; /* rodzaj wpisu w liカcie */ + uint8_t dunno1; /* rodzaj wpisu w liナ嫩ie */ } GG_PACKED; -#define GG_USER_OFFLINE 0x01 /* b鹽ziemy niewidoczni dla uソytkownika */ -#define GG_USER_NORMAL 0x03 /* zwykウy uソytkownik */ -#define GG_USER_BLOCKED 0x04 /* zablokowany uソytkownik */ +#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ナZ kontakt */ + GG_USER_BLOCKED, /**< Zablokowany */ + GG_USER_OFFLINE, /**< Niewidoczny dla kontaktu */ +}; + +#endif /* DOXYGEN */ #define GG_LIST_EMPTY 0x0012 @@ -1096,7 +1632,7 @@ uint32_t uin; /* numerek */ uint32_t status; /* status danej osoby */ uint32_t remote_ip; /* adres ip delikwenta */ - uint16_t remote_port; /* port, na ktrym sウucha klient */ + uint16_t remote_port; /* port, na ktテウrym sナVcha klient */ uint32_t version; /* wersja klienta */ uint16_t dunno2; /* znowu port? */ } GG_PACKED; @@ -1107,7 +1643,7 @@ 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 ktrym sウucha klient */ + uint16_t remote_port; /* port, na ktテウrym sナVcha klient */ uint8_t version; /* wersja klienta */ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ uint8_t dunno1; /* 0x00 */ @@ -1119,12 +1655,38 @@ 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 ktrym sウucha klient */ + uint16_t remote_port; /* port, na ktテウrym sナVcha 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ナVcha 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ナVcha 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 @@ -1142,15 +1704,41 @@ #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 /* kompatybilnoカ wstecz */ +#define GG_CLASS_EXT GG_CLASS_ACK /**< Dla kompatybilnoナ嫩i wstecz */ + +#else -#define GG_MSG_MAXSIZE 2000 +/** + * Klasy wiadomoナ嫩i. Wartoナ嫩i sト maskami bitowymi, ktテウre w wiト冖szoナ嫩i + * 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ナVgoナ崙 wiadomoナ嫩i. + * + * \ingroup messages + */ +#define GG_MSG_MAXSIZE 1989 struct gg_send_msg { uint32_t recipient; @@ -1163,16 +1751,19 @@ uint16_t length; } GG_PACKED; +/** + * Struktura opisujトca formatowanie tekstu. W zaleナシnoナ嫩i od wartoナ嫩i 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; - uint8_t font; + uint16_t position; /**< Poczトtkowy znak formatowania (liczony od 0) */ + uint8_t font; /**< Atrybuty formatowania */ } GG_PACKED; -struct gg_msg_richtext_image { - uint16_t unknown1; - uint32_t size; - uint32_t crc32; -} GG_PACKED; +#ifndef DOXYGEN #define GG_FONT_BOLD 0x01 #define GG_FONT_ITALIC 0x02 @@ -1180,10 +1771,44 @@ #define GG_FONT_COLOR 0x08 #define GG_FONT_IMAGE 0x80 +#else + +/** + * Atrybuty formatowania wiadomoナ嫩i. + * + * \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; - uint8_t green; - uint8_t blue; + uint8_t red; /**< SkナBdowa czerwona koloru */ + uint8_t green; /**< SkナBdowa zielona koloru */ + uint8_t blue; /**< SkナBdowa niebieska koloru */ +} GG_PACKED; + +/** + * Strukturya opisujトca obrazek wstawiony do wiadomoナ嫩i dla atrubutu + * \c GG_FONT_IMAGE. + * + * \ingroup messages + */ +struct gg_msg_richtext_image { + uint16_t unknown1; /**< Nieznane pole o wartoナ嫩i 0x0109 */ + uint32_t size; /**< Rozmiar obrazka */ + uint32_t crc32; /**< Suma kontrolna CRC32 obrazka */ } GG_PACKED; struct gg_msg_recipients { @@ -1207,12 +1832,32 @@ #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ト冂zenia wiadomoナ嫩i. + * + * \ingroup messages + */ +enum +{ + GG_ACK_DELIVERED, /**< Wiadomoナ崙 dostarczono. */ + GG_ACK_QUEUED, /**< Wiadomoナ崙 zakolejkowano z powodu niedostト冪noナ嫩i odbiorcy. */ + GG_ACK_BLOCKED, /**< Wiadomoナ崙 zablokowana przez serwer (spam, ナ孩iトteczne ograniczenia itd.) */ + GG_ACK_MBOXFULL, /**< Wiadomoナ嫩i nie dostarczono z powodu zapeナOionej kolejki wiadomoナ嫩i odbiorcy. */ + GG_ACK_NOT_DELIVERED /**< Wiadomoナ嫩i nie dostarczono (tylko dla \c GG_CLASS_CTCP). */ +}; + +#endif /* DOXYGEN */ + struct gg_send_msg_ack { uint32_t status; uint32_t recipient; @@ -1236,29 +1881,59 @@ #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; -/* - * pakiety, staウe, struktury dla DCC - */ - struct gg_dcc_tiny_packet { uint8_t type; /* rodzaj pakietu */ } GG_PACKED; @@ -1274,14 +1949,14 @@ } GG_PACKED; /* - * pki co, nie znamy dokウadnie protokoウu. nie wiemy, co czemu odpowiada. - * nazwy sア niepowaソne i tymczasowe. + * pテウki co, nie znamy dokナBdnie protokoナV. 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鹹 mu damy */ +#define GG_DCC_HAVE_FILE 0x0001 /* wiト冂 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_CATCH_FILE 0x0002 /* wysyナBmy plik */ #define GG_DCC_FILEATTR_READONLY 0x0020 @@ -1290,11 +1965,88 @@ #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" */ +} 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ナ孑ednie */ +#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ナ孑ednie juナシ trwa, nie umiem obsナVナシyト wiト冂ej */ +#define GG_DCC7_REJECT_USER 0x00000002 /**< Uナシytkownik odrzuciナ poナてczenie */ +#define GG_DCC7_REJECT_VERSION 0x00000006 /**< Druga strona ma wersjト klienta nieobsナVgujトcト poナてczeナ bezpoナ孑ednich 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ナPsu */ +#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 + +struct gg_dcc7_dunno1 { + // XXX +} GG_PACKED; + +#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 */ + #ifdef __cplusplus -#ifdef _MSC_VER +} +#ifdef _WIN32 #pragma pack(pop) #endif -} #endif #endif /* __GG_LIBGADU_H */
--- a/libpurple/protocols/gg/lib/obsolete.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/gg/lib/obsolete.c Tue Mar 16 12:07:06 2010 +0900 @@ -1,4 +1,4 @@ -/* $Id: obsolete.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: obsolete.c 854 2009-10-12 21:06:28Z wojtekka $ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl> @@ -14,16 +14,23 @@ * * 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 Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ -/* - * Plik zawiera deklaracje funkcji, ktre sア juソ nieaktualne ze wzgl鹽u - * na zmiany w protokole, ale sア wymagane przez aplikacje linkowane ze - * starszymi wersjami bibliotek. +/** + * \file obsolete.c + * + * \brief Nieaktualne funkcje + * + * Plik zawiera definicje funkcji, ktテウre sト juナシ nieaktualne ze wzglト囘u + * na zmiany w protokole. Programy konsolidowane ze starszych wersjami + * bibliotek powinny nadal mieト moナシliwoナ崙 dziaナBnia, mimo ograniczonej + * funkcjonalnoナ嫩i. */ +/** \cond obsolete */ + #include <errno.h> #include "libgadu.h" @@ -205,3 +212,25 @@ { } + +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; +} + +/** \endcond */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/protocol.h Tue Mar 16 12:07:06 2010 +0900 @@ -0,0 +1,165 @@ +/* $Id$ */ + +/* + * (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef LIBGADU_PROTOCOL_H +#define LIBGADU_PROTOCOL_H + +#include "libgadu.h" + +#ifdef _WIN32 +#pragma pack(push, 1) +#endif + +#define GG_LOGIN80BETA 0x0029 + +#define GG_LOGIN80 0x0031 + +#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 GG8_LANG "pl" +#define GG8_VERSION "Gadu-Gadu Client Build 8.0.0.8731" + +struct gg_login80 { + uint32_t uin; /* mテウj numerek */ + uint8_t language[2]; /* jト凛yk: GG8_LANG */ + uint8_t hash_type; /* rodzaj hashowania hasナB */ + uint8_t hash[64]; /* hash hasナB dopeナOiony zerami */ + uint32_t status; /* status na dzieナ dobry */ + uint32_t flags; /* flagi (przeznaczenie nieznane) */ + uint32_t features; /* opcje protokoナV (GG8_FEATURES) */ + uint32_t local_ip; /* mテウj adres ip */ + uint16_t local_port; /* port, na ktテウrym sナVcham */ + uint32_t external_ip; /* zewnト冲rzny adres ip (???) */ + uint16_t external_port; /* zewnト冲rzny 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_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_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 flags; /* flagi (przeznaczenie nieznane) */ + uint32_t remote_ip; /* adres IP bezpoナ孑ednich poナてczeナ */ + uint16_t remote_port; /* port bezpoナ孑ednich poナてczeナ */ + uint8_t image_size; /* maksymalny rozmiar obrazkテウw w KB */ + uint8_t unknown2; /* 0x00 */ + uint32_t unknown3; /* 0x00000000 */ + 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_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; + +struct gg_dcc7_voice_nodata { /* wyciszony mikrofon, ten pakiet jest wysylany co 1s (jesli chcemy podtrzymac polaczenie) */ + 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; + +#ifdef _WIN32 +#pragma pack(pop) +#endif + +#endif /* LIBGADU_PROTOCOL_H */
--- a/libpurple/protocols/gg/lib/pubdir.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/gg/lib/pubdir.c Tue Mar 16 12:07:06 2010 +0900 @@ -1,8 +1,9 @@ -/* $Id: pubdir.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: pubdir.c 502 2008-01-10 23:25:17Z wojtekka $ */ /* - * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> + * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> * Dawid Jarosz <dawjar@poczta.onet.pl> + * Adam Wysocki <gophi@ekg.chmurka.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -15,11 +16,18 @@ * * 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 Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ +/** + * \file pubdir.c + * + * \brief ObsナVga katalogu publicznego + */ + #include "libgadu.h" +#include "libgadu-config.h" #include <ctype.h> #include <errno.h> @@ -29,20 +37,20 @@ #include <string.h> #include <unistd.h> -/* - * gg_register3() +/** + * Rejestruje nowego uナシytkownika. * - * rozpoczyna rejestracj uソytkownika protokoウem GG 6.0. wymaga wczeカniejszego - * pobrania tokenu za pomocア funkcji gg_token(). + * Wymaga wczeナ嬾iejszego pobrania tokenu za pomocト \c gg_token(). * - * - email - adres e-mail klienta - * - password - hasウo klienta - * - tokenid - identyfikator tokenu - * - tokenval - wartoカ tokenu - * - async - poウアczenie asynchroniczne + * \param email Adres e-mail + * \param password HasナP + * \param tokenid Identyfikator tokenu + * \param tokenval Zawartoナ崙 tokenu + * \param async Flaga poナてczenia asynchronicznego * - * zaalokowana struct gg_http, ktrア poシniej naleソy zwolni - * funkcjア gg_register_free(), albo NULL jeカli wystアpiウ bウアd. + * \return Struktura \c gg_http lub \c NULL w przypadku bナて囘u + * + * \ingroup register */ struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async) { @@ -121,19 +129,59 @@ return h; } -/* - * gg_unregister3() +#ifdef DOXYGEN + +/** + * Funkcja wywoナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia. + * + * Operacja bト囘zie zakoナczona, gdy pole \c state bト囘zie rテウwne \c GG_STATE_DONE. + * Jeナ嬪i wystトpi bナてd, \c state bト囘zie rテウwne \c GG_STATE_ERROR, a kod bナて囘u + * znajdzie siト w polu \c error. + * + * \note W rzeczywistoナ嫩i funkcja jest makrem rozwijanym do + * \c gg_pubdir_watch_fd(). * - * usuwa konto uソytkownika z serwera protokoウem GG 6.0 + * \param h Struktura poナてczenia + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup register + */ +int gg_register_watch_fd(struct gg_httpd *h) +{ + return gg_pubdir_watch_fd(h); +} + +/** + * Zwalnia zasoby po operacji. * - * - uin - numerek GG - * - password - hasウo klienta - * - tokenid - identyfikator tokenu - * - tokenval - wartoカ tokenu - * - async - poウアczenie asynchroniczne + * \note W rzeczywistoナ嫩i funkcja jest makrem rozwijanym do \c gg_pubdir_free(). + * + * \param h Struktura poナてczenia * - * zaalokowana struct gg_http, ktrア poシniej naleソy zwolni - * funkcjア gg_unregister_free(), albo NULL jeカli wystアpiウ bウアd. + * \ingroup register + */ +void gg_register_free(struct gg_http *h) +{ + return gg_pubdir_free(h); +} + +#endif /* DOXYGEN */ + +/** + * Usuwa uナシytkownika. + * + * Wymaga wczeナ嬾iejszego pobrania tokenu za pomocト \c gg_token(). + * + * \param uin Numer Gadu-Gadu + * \param password HasナP + * \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ナて囘u + * + * \ingroup unregister */ struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async) { @@ -145,7 +193,7 @@ errno = EFAULT; return NULL; } - + __pwd = gg_saprintf("%ld", random()); __fmpwd = gg_urlencode(password); __tokenid = gg_urlencode(tokenid); @@ -210,22 +258,61 @@ return h; } -/* - * gg_change_passwd4() +#ifdef DOXYGEN + +/** + * Funkcja wywoナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia. + * + * Operacja bト囘zie zakoナczona, gdy pole \c state bト囘zie rテウwne \c GG_STATE_DONE. + * Jeナ嬪i wystトpi bナてd, \c state bト囘zie rテウwne \c GG_STATE_ERROR, a kod bナて囘u + * znajdzie siト w polu \c error. + * + * \note W rzeczywistoナ嫩i funkcja jest makrem rozwijanym do + * \c gg_pubdir_watch_fd(). + * + * \param h Struktura poナてczenia * - * wysyウa ソアdanie zmiany hasウa zgodnie z protokoウem GG 6.0. wymaga - * wczeカniejszego pobrania tokenu za pomocア funkcji gg_token(). + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup unregister + */ +int gg_unregister_watch_fd(struct gg_httpd *h) +{ + return gg_pubdir_watch_fd(h); +} + +/** + * Zwalnia zasoby po operacji. + * + * \note W rzeczywistoナ嫩i funkcja jest makrem rozwijanym do \c gg_pubdir_free(). * - * - uin - numer - * - email - adres e-mail - * - passwd - stare hasウo - * - newpasswd - nowe hasウo - * - tokenid - identyfikator tokenu - * - tokenval - wartoカ tokenu - * - async - poウアczenie asynchroniczne + * \param h Struktura poナてczenia + * + * \ingroup unregister + */ +void gg_unregister_free(struct gg_http *h) +{ + return gg_pubdir_free(h); +} + +#endif /* DOXYGEN */ + +/** + * Zmienia hasナP uナシytkownika. * - * zaalokowana struct gg_http, ktrア poシniej naleソy zwolni - * funkcjア gg_change_passwd_free(), albo NULL jeカli wystアpiウ bウアd. + * Wymaga wczeナ嬾iejszego pobrania tokenu za pomocト \c gg_token(). + * + * \param uin Numer Gadu-Gadu + * \param email Adres e-mail + * \param passwd Obecne hasナP + * \param newpasswd Nowe hasナP + * \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ナて囘u + * + * \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) { @@ -309,19 +396,59 @@ return h; } -/* - * gg_remind_passwd3() +#ifdef DOXYGEN + +/** + * Funkcja wywoナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia. + * + * Operacja bト囘zie zakoナczona, gdy pole \c state bト囘zie rテウwne \c GG_STATE_DONE. + * Jeナ嬪i wystトpi bナてd, \c state bト囘zie rテウwne \c GG_STATE_ERROR, a kod bナて囘u + * znajdzie siト w polu \c error. + * + * \note W rzeczywistoナ嫩i funkcja jest makrem rozwijanym do + * \c gg_pubdir_watch_fd(). * - * wysyウa ソアdanie przypomnienia hasウa e-mailem. + * \param h Struktura poナてczenia + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup passwd + */ +int gg_change_passwd_watch_fd(struct gg_httpd *h) +{ + return gg_pubdir_watch_fd(h); +} + +/** + * Zwalnia zasoby po operacji. * - * - uin - numer - * - email - adres e-mail taki, jak ten zapisany na serwerze - * - async - poウアczenie asynchroniczne - * - tokenid - identyfikator tokenu - * - tokenval - wartoカ tokenu + * \note W rzeczywistoナ嫩i funkcja jest makrem rozwijanym do \c gg_pubdir_free(). + * + * \param h Struktura poナてczenia * - * zaalokowana struct gg_http, ktrア poシniej naleソy zwolni - * funkcjア gg_remind_passwd_free(), albo NULL jeカli wystアpiウ bウアd. + * \ingroup passwd + */ +void gg_change_passwd_free(struct gg_http *h) +{ + return gg_pubdir_free(h); +} + +#endif /* DOXYGEN */ + +/** + * WysyナB hasナP uナシytkownika na e-mail. + * + * Wymaga wczeナ嬾iejszego 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ナて囘u + * + * \ingroup remind */ struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async) { @@ -396,17 +523,55 @@ return h; } -/* - * gg_pubdir_watch_fd() +#ifdef DOXYGEN + +/** + * Funkcja wywoナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia. * - * przy asynchronicznych operacjach na katalogu publicznym naleソy wywoウywa - * t funkcj przy zmianach na obserwowanym deskryptorze. + * Operacja bト囘zie zakoナczona, gdy pole \c state bト囘zie rテウwne \c GG_STATE_DONE. + * Jeナ嬪i wystトpi bナてd, \c state bト囘zie rテウwne \c GG_STATE_ERROR, a kod bナて囘u + * znajdzie siト w polu \c error. + * + * \note W rzeczywistoナ嫩i funkcja jest makrem rozwijanym do + * \c gg_pubdir_watch_fd(). + * + * \param h Struktura poナてczenia + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u * - * - h - struktura opisujアca poウアczenie + * \ingroup remind + */ +int gg_remind_watch_fd(struct gg_httpd *h) +{ + return gg_pubdir_watch_fd(h); +} + +/** + * Zwalnia zasoby po operacji. + * + * \note W rzeczywistoナ嫩i funkcja jest makrem rozwijanym do \c gg_pubdir_free(). + * + * \param h Struktura poナてczenia * - * jeカli wszystko poszウo dobrze to 0, inaczej -1. operacja b鹽zie - * zakoczona, jeカli h->state == GG_STATE_DONE. jeカli wystアpi jakiカ - * bウアd, to b鹽zie tam GG_STATE_ERROR i odpowiedni kod bウ鹽u w h->error. + * \ingroup remind + */ +void gg_remind_free(struct gg_http *h) +{ + return gg_pubdir_free(h); +} + +#endif /* DOXYGEN */ + +/** + * Funkcja wywoナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia. + * + * Operacja bト囘zie zakoナczona, gdy pole \c state bト囘zie rテウwne \c GG_STATE_DONE. + * Jeナ嬪i wystトpi bナてd, \c state bト囘zie rテウwne \c GG_STATE_ERROR, a kod bナて囘u + * znajdzie siト w polu \c error. + * + * \param h Struktura poナてczenia + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u */ int gg_pubdir_watch_fd(struct gg_http *h) { @@ -447,7 +612,11 @@ gg_debug(GG_DEBUG_MISC, "=> pubdir, let's parse \"%s\"\n", h->body); - if ((tmp = strstr(h->body, "success")) || (tmp = strstr(h->body, "results"))) { + 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); + 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); @@ -458,12 +627,10 @@ return 0; } -/* - * gg_pubdir_free() +/** + * Zwalnia zasoby po operacji na katalogu publicznym. * - * zwalnia pami po efektach operacji na katalogu publicznym. - * - * - h - zwalniana struktura + * \param h Struktura poナてczenia */ void gg_pubdir_free(struct gg_http *h) { @@ -474,14 +641,17 @@ gg_http_free(h); } -/* - * gg_token() +/** + * Pobiera token do autoryzacji operacji na katalogu publicznym. + * + * Token jest niezbト囘ny do tworzenia nowego i usuwania uナシytkownika, + * zmiany hasナB itd. * - * pobiera z serwera token do autoryzacji zakウadania konta, usuwania - * konta i zmiany hasウa. + * \param async Flaga poナてczenia asynchronicznego * - * zaalokowana struct gg_http, ktrア poシniej naleソy zwolni - * funkcjア gg_token_free(), albo NULL jeカli wystアpiウ bウアd. + * \return Struktura \c gg_http lub \c NULL w przypadku bナて囘u + * + * \ingroup token */ struct gg_http *gg_token(int async) { @@ -511,17 +681,18 @@ return h; } -/* - * gg_token_watch_fd() - * - * przy asynchronicznych operacjach zwiアzanych z tokenem naleソy wywoウywa - * t funkcj przy zmianach na obserwowanym deskryptorze. +/** + * Funkcja wywoナZwana po zaobserwowaniu zmian na deskryptorze poナてczenia. * - * - h - struktura opisujアca poウアczenie + * Operacja bト囘zie zakoナczona, gdy pole \c state bト囘zie rテウwne \c GG_STATE_DONE. + * Jeナ嬪i wystトpi bナてd, \c state bト囘zie rテウwne \c GG_STATE_ERROR, a kod bナて囘u + * znajdzie siト w polu \c error. * - * jeカli wszystko poszウo dobrze to 0, inaczej -1. operacja b鹽zie - * zakoczona, jeカli h->state == GG_STATE_DONE. jeカli wystアpi jakiカ - * bウアd, to b鹽zie tam GG_STATE_ERROR i odpowiedni kod bウ鹽u w h->error. + * \param h Struktura poナてczenia + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup token */ int gg_token_watch_fd(struct gg_http *h) { @@ -547,8 +718,8 @@ 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 + /* jeナ嬪i h->data jest puste, to ナ嫩iトgaliナ嬶y tokenid i url do niego, + * ale jeナ嬪i coナ tam jest, to znaczy, ナシe mamy drugi etap polegajトcy * na pobieraniu tokenu. */ if (!h->data) { int width, height, length; @@ -573,8 +744,8 @@ return -1; } - /* dostaliカmy tokenid i wszystkie niezb鹽ne informacje, - * wi鹹 pobierzmy obrazek z tokenem */ + /* dostaliナ嬶y tokenid i wszystkie niezbト囘ne informacje, + * wiト冂 pobierzmy obrazek z tokenem */ if (strncmp(url, "http://", 7)) { path = gg_saprintf("%s?tokenid=%s", url, tokenid); @@ -623,6 +794,8 @@ free(path); free(url); + gg_http_free_fields(h); + memcpy(h, h2, sizeof(struct gg_http)); free(h2); @@ -652,12 +825,12 @@ return 0; } -/* - * gg_token_free() +/** + * Zwalnia zasoby po operacji pobierania tokenu. * - * zwalnia pami po efektach pobierania tokenu. + * \param h Struktura poナてczenia * - * - h - zwalniana struktura + * \ingroup token */ void gg_token_free(struct gg_http *h) {
--- a/libpurple/protocols/gg/lib/pubdir50.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/gg/lib/pubdir50.c Tue Mar 16 12:07:06 2010 +0900 @@ -1,4 +1,4 @@ -/* $Id: pubdir50.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: pubdir50.c 854 2009-10-12 21:06:28Z wojtekka $ */ /* * (C) Copyright 2003 Wojtek Kaniewski <wojtekka@irc.pl> @@ -14,11 +14,15 @@ * * 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 Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ -#include "libgadu.h" +/** + * \file pubdir50.c + * + * \brief ObsナVga katalogu publicznego od wersji Gadu-Gadu 5.x + */ #include <errno.h> #include <stdlib.h> @@ -26,12 +30,17 @@ #include <time.h> #include <glib.h> -/* - * gg_pubdir50_new() +#include "libgadu.h" +#include "libgadu-internal.h" + +/** + * Tworzy nowe zapytanie katalogu publicznego. * - * tworzy nowア zmiennア typu gg_pubdir50_t. + * \param type Rodzaj zapytania * - * zaalokowana zmienna lub NULL w przypadku braku pami鹹i. + * \return Zmienna \c gg_pubdir50_t lub \c NULL w przypadku bナて囘u. + * + * \ingroup pubdir50 */ gg_pubdir50_t gg_pubdir50_new(int type) { @@ -51,17 +60,16 @@ return res; } -/* - * gg_pubdir50_add_n() // funkcja wewn黎rzna - * - * funkcja dodaje lub zast麪uje istniejアce pole do zapytania lub odpowiedzi. +/** + * \internal Dodaje lub zastト冪uje pole zapytania lub odpowiedzi katalogu + * publicznego. * - * - req - wskaシnik opisu zapytania, - * - num - numer wyniku (0 dla zapytania), - * - field - nazwa pola, - * - value - wartoカ pola, + * \param req Zapytanie lub odpowiedナコ + * \param num Numer wyniku odpowiedzi (0 dla zapytania) + * \param field Nazwa pola + * \param value Wartoナ崙 pola * - * 0/-1 + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u */ static int gg_pubdir50_add_n(gg_pubdir50_t req, int num, const char *field, const char *value) { @@ -111,31 +119,31 @@ return 0; } -/* - * gg_pubdir50_add() - * - * funkcja dodaje pole do zapytania. +/** + * Dodaje pole zapytania. * - * - req - wskaシnik opisu zapytania, - * - field - nazwa pola, - * - value - wartoカ pola, + * \param req Zapytanie + * \param field Nazwa pola + * \param value Wartoナ崙 pola * - * 0/-1 + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \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); } -/* - * gg_pubdir50_seq_set() - * - * ustawia numer sekwencyjny zapytania. +/** + * Ustawia numer sekwencyjny zapytania. * - * - req - zapytanie, - * - seq - nowy numer sekwencyjny. + * \param req Zapytanie + * \param seq Numer sekwencyjny * - * 0/-1. + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + * + * \ingroup pubdir50 */ int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq) { @@ -152,12 +160,12 @@ return 0; } -/* - * gg_pubdir50_free() +/** + * Zwalnia zasoby po zapytaniu lub odpowiedzi katalogu publicznego. * - * zwalnia pami po zapytaniu lub rezultacie szukania uソytkownika. + * \param s Zapytanie lub odpowiedナコ * - * - s - zwalniana zmienna, + * \ingroup pubdir50 */ void gg_pubdir50_free(gg_pubdir50_t s) { @@ -175,15 +183,15 @@ free(s); } -/* - * gg_pubdir50() - * - * wysyウa zapytanie katalogu publicznego do serwera. +/** + * WysyナB zapytanie katalogu publicznego do serwera. * - * - sess - sesja, - * - req - zapytanie. + * \param sess Struktura sesji + * \param req Zapytanie * - * numer sekwencyjny wyszukiwania lub 0 w przypadku bウ鹽u. + * \return Numer sekwencyjny zapytania lub 0 w przypadku bナて囘u + * + * \ingroup pubdir50 */ uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req) { @@ -192,16 +200,16 @@ char *buf, *p; struct gg_pubdir50_request *r; - gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req); + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req); if (!sess || !req) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n"); errno = EFAULT; return 0; } if (sess->state != GG_STATE_CONNECTED) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() not connected\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() not connected\n"); errno = ENOTCONN; return 0; } @@ -211,30 +219,81 @@ if (req->entries[i].num) continue; - size += strlen(req->entries[i].field) + 1; - size += strlen(req->entries[i].value) + 1; + if (sess->encoding == GG_ENCODING_CP1250) { + size += strlen(req->entries[i].field) + 1; + size += strlen(req->entries[i].value) + 1; + } else { + char *tmp; + + tmp = gg_utf8_to_cp(req->entries[i].field); + + if (tmp == NULL) + return -1; + + size += strlen(tmp) + 1; + + free(tmp); + + tmp = gg_utf8_to_cp(req->entries[i].value); + + if (tmp == NULL) + return -1; + + size += strlen(tmp) + 1; + + free(tmp); + } } if (!(buf = malloc(size))) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", 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; - res = time(NULL); r->type = req->type; - r->seq = (req->seq) ? gg_fix32(req->seq) : gg_fix32(time(NULL)); - req->seq = gg_fix32(r->seq); + r->seq = gg_fix32(req->seq); for (i = 0, p = buf + 5; i < req->entries_count; i++) { if (req->entries[i].num) continue; - strcpy(p, req->entries[i].field); - p += strlen(p) + 1; + 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; + + tmp = gg_utf8_to_cp(req->entries[i].field); + + if (tmp == NULL) { + free(buf); + return -1; + } - strcpy(p, req->entries[i].value); - p += strlen(p) + 1; + strcpy(p, tmp); + p += strlen(tmp) + 1; + free(tmp); + + tmp = gg_utf8_to_cp(req->entries[i].value); + + 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) @@ -246,26 +305,26 @@ } /* - * gg_pubdir50_handle_reply() // funkcja wewn黎rzna - * - * analizuje przychodzアcy pakiet odpowiedzi i zapisuje wynik w struct gg_event. + * \internal Analizuje przychodzトcy pakiet odpowiedzi i zapisuje wynik + * w strukturze \c gg_event. * - * - e - opis zdarzenia - * - packet - zawartoカ pakietu odpowiedzi - * - length - dウugoカ pakietu odpowiedzi + * \param sess Struktura sesji + * \param e Struktura zdarzenia + * \param packet Pakiet odpowiedzi + * \param length DナVgoナ崙 pakietu odpowiedzi * - * 0/-1 + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u */ -int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length) +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; struct gg_pubdir50_reply *r = (struct gg_pubdir50_reply*) packet; gg_pubdir50_t res; int num = 0; - gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply(%p, %p, %d);\n", e, packet, length); + gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply_sess(%p, %p, %p, %d);\n", sess, e, packet, length); - if (!e || !packet) { + if (!sess || !e || !packet) { gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() invalid arguments\n"); errno = EFAULT; return -1; @@ -300,11 +359,11 @@ break; } - /* brak wynikw? */ + /* brak wynikテウw? */ if (length == 5) return 0; - /* pomi poczアtek odpowiedzi */ + /* pomiナ poczトtek odpowiedzi */ p = packet + 5; while (p < end) { @@ -312,7 +371,7 @@ field = p; - /* sprawdシ, czy nie mamy podziaウu na kolejne pole */ + /* sprawdナコ, czy nie mamy podziaナV na kolejne pole */ if (!*field) { num++; field++; @@ -321,22 +380,22 @@ value = NULL; for (p = field; p < end; p++) { - /* jeカli mamy koniec tekstu... */ + /* jeナ嬪i mamy koniec tekstu... */ if (!*p) { - /* ...i jeszcze nie mieliカmy wartoカci pola to - * wiemy, ソe po tym zerze jest wartoカ... */ + /* ...i jeszcze nie mieliナ嬶y wartoナ嫩i 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黎li */ + * wartoナ嫩i i moナシemy wychodziト + * grzecznie z pト冲li */ break; } } - /* sprawdシmy, czy pole nie wychodzi poza pakiet, ソeby nie - * mie segfaultw, jeカli serwer przestanie zakacza pakietw + /* sprawdナコmy, czy pole nie wychodzi poza pakiet, ナシeby nie + * mieト segfaultテウw, jeナ嬪i serwer przestanie zakaナczaト pakietテウw * przez \0 */ if (p == end) { @@ -346,14 +405,30 @@ p++; - /* jeカli dostaliカmy namier na nast麪ne wyniki, to znaczy ソe - * mamy koniec wynikw i nie jest to kolejna osoba. */ + /* jeナ嬪i dostaliナ嬶y namier na nastト冪ne wyniki, to znaczy ナシe + * mamy koniec wynikテウw i nie jest to kolejna osoba. */ if (!strcasecmp(field, "nextstart")) { res->next = atoi(value); num--; } else { - if (gg_pubdir50_add_n(res, num, field, value) == -1) - goto failure; + if (sess->encoding == GG_ENCODING_CP1250) { + if (gg_pubdir50_add_n(res, num, field, value) == -1) + goto failure; + } else { + char *tmp; + + tmp = gg_cp_to_utf8(value); + + if (tmp == NULL) + goto failure; + + if (gg_pubdir50_add_n(res, num, field, tmp) == -1) { + free(tmp); + goto failure; + } + + free(tmp); + } } } @@ -366,16 +441,16 @@ return -1; } -/* - * gg_pubdir50_get() - * - * pobiera informacj z rezultatu wyszukiwania. +/** + * Pobiera pole z odpowiedzi katalogu publicznego. * - * - res - rezultat wyszukiwania, - * - num - numer odpowiedzi, - * - field - nazwa pola (wielkoカ liter nie ma znaczenia). + * \param res Odpowiedナコ + * \param num Numer wyniku odpowiedzi + * \param field Nazwa pola (wielkoナ崙 liter nie ma znaczenia) * - * wartoカ pola lub NULL, jeカli nie znaleziono. + * \return Wartoナ崙 pola lub \c NULL jeナ嬪i nie znaleziono + * + * \ingroup pubdir50 */ const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field) { @@ -400,57 +475,61 @@ return value; } -/* - * gg_pubdir50_count() +/** + * Zwraca liczbト wynikテウw odpowiedzi. * - * zwraca iloカ wynikw danego zapytania. + * \param res Odpowiedナコ * - * - res - odpowiedシ + * \return Liczba wynikテウw lub -1 w przypadku bナて囘u * - * iloカ lub -1 w przypadku bウ鹽u. + * \ingroup pubdir50 */ int gg_pubdir50_count(gg_pubdir50_t res) { return (!res) ? -1 : res->count; } -/* - * gg_pubdir50_type() +/** + * Zwraca rodzaj zapytania lub odpowiedzi. * - * zwraca rodzaj zapytania lub odpowiedzi. + * \param res Zapytanie lub odpowiedナコ * - * - res - zapytanie lub odpowiedシ + * \return Rodzaj lub -1 w przypadku bナて囘u * - * iloカ lub -1 w przypadku bウ鹽u. + * \ingroup pubdir50 */ int gg_pubdir50_type(gg_pubdir50_t res) { return (!res) ? -1 : res->type; } -/* - * gg_pubdir50_next() +/** + * Zwraca numer, od ktテウrego naleナシy rozpoczトc kolejne wyszukiwanie. * - * zwraca numer, od ktrego naleソy rozpoczア kolejne wyszukiwanie, jeカli - * zaleソy nam na kolejnych wynikach. + * DナVナシsze odpowiedzi katalogu publicznego sト wysyナBne przez serwer + * w mniejszych paczkach. Po otrzymaniu odpowiedzi, jeナ嬪i numer kolejnego + * wyszukiwania jest wiト冖szy od zera, dalsze wyniki moナシna otrzymaト przez + * wywoナBnie kolejnego zapytania z okreナ嬪onym numerem poczトtkowym. * - * - res - odpowiedシ + * \param res Odpowiedナコ * - * numer lub -1 w przypadku bウ鹽u. + * \return Numer lub -1 w przypadku bナて囘u + * + * \ingroup pubdir50 */ uin_t gg_pubdir50_next(gg_pubdir50_t res) { return (!res) ? (unsigned) -1 : res->next; } -/* - * gg_pubdir50_seq() +/** + * Zwraca numer sekwencyjny zapytania lub odpowiedzi. * - * zwraca numer sekwencyjny zapytania lub odpowiedzi. + * \param res Zapytanie lub odpowiedナコ * - * - res - zapytanie lub odpowiedシ + * \return Numer sekwencyjny lub -1 w przypadku bナて囘u * - * numer lub -1 w przypadku bウ鹽u. + * \ingroup pubdir50 */ uint32_t gg_pubdir50_seq(gg_pubdir50_t res) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/resolver.c Tue Mar 16 12:07:06 2010 +0900 @@ -0,0 +1,1065 @@ +/* $Id$ */ + +/* + * (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl> + * Robert J. Woナコny <speedy@ziew.org> + * Arkadiusz Miナ嫐iewicz <arekm@pld-linux.org> + * Tomasz Chiliナгki <chilek@chilan.com> + * Adam Wysocki <gophi@ekg.chmurka.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/** + * \file resolver.c + * + * \brief Funkcje rozwiトzywania nazw + */ + +#ifndef _WIN32 +# include <sys/wait.h> +# include <netdb.h> +# include <signal.h> +# include <netinet/in.h> +# include <arpa/inet.h> +#endif + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "libgadu.h" +#include "resolver.h" +#include "compat.h" + +/** Sposテウb rozwiトzywania nazw serwerテウw */ +static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT; + +/** Funkcja rozpoczynajトca rozwiトzywanie nazwy */ +static int (*gg_global_resolver_start)(int *fd, void **private_data, const char *hostname); + +/** Funkcja zwalniajトca zasoby po rozwiトzaniu nazwy */ +static void (*gg_global_resolver_cleanup)(void **private_data, int force); + +#ifdef GG_CONFIG_HAVE_PTHREAD + +#include <pthread.h> + +/** + * \internal Funkcja pomocnicza zwalniajトca zasoby po rozwiトzywaniu nazwy + * w wトtku. + * + * \param data Wskaナコnik na wskaナコnik bufora zaalokowanego w wトtku + */ +static void gg_gethostbyname_cleaner(void *data) +{ + char **buf_ptr = (char**) data; + + if (buf_ptr != NULL) { + free(*buf_ptr); + *buf_ptr = NULL; + } +} + +#endif /* GG_CONFIG_HAVE_PTHREAD */ + +/** + * \internal Odpowiednik \c gethostbyname zapewniajトcy wspテウナCieナシnoナ崙. + * + * Jeナ嬪i dany system dostarcza \c gethostbyname_r, uナシywa siト tej wersji, jeナ嬪i + * nie, to zwykナFj \c gethostbyname. + * + * \param hostname Nazwa serwera + * \param addr Wskaナコnik na rezultat rozwiトzywania nazwy + * \param pthread Flaga blokowania unicestwiania wトtku podczas alokacji pamiト冂i + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, 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 result = -1; + int h_errnop; + int ret = 0; +#ifdef GG_CONFIG_HAVE_PTHREAD + int old_state; +#endif + +#ifdef GG_CONFIG_HAVE_PTHREAD + pthread_cleanup_push(gg_gethostbyname_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) { +#ifndef sun + while ((ret = gethostbyname_r(hostname, &he, buf, buf_len, &he_ptr, &h_errnop)) == ERANGE) { +#else + while (((he_ptr = gethostbyname_r(hostname, &he, buf, buf_len, &h_errnop)) == NULL) && (errno == ERANGE)) { +#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) { + memcpy(addr, he_ptr->h_addr, sizeof(struct in_addr)); + result = 0; + } + +#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(1); +#endif + + return result; +#else + struct hostent *he; + + he = gethostbyname(hostname); + + if (he == NULL) + return -1; + + memcpy(addr, he->h_addr, sizeof(struct in_addr)); + + return 0; +#endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */ +} + +/** + * \internal Odpowiednik \c gethostbyname zapewniajトcy wspテウナCieナシnoナ崙. + * + * Jeナ嬪i dany system dostarcza \c gethostbyname_r, uナシywa siト tej wersji, jeナ嬪i + * nie, to zwykナFj \c gethostbyname. + * + * \param hostname Nazwa serwera + * + * \return Zaalokowana struktura \c in_addr lub NULL w przypadku bナて囘u. + */ +struct in_addr *gg_gethostbyname(const char *hostname) +{ + struct in_addr *addr; + + if (!(addr = malloc(sizeof(struct in_addr)))) + return NULL; + + if (gg_gethostbyname_real(hostname, addr, 0)) { + free(addr); + return NULL; + } + return addr; +} + +/** + * \internal Struktura przekazywana do wトtku rozwiトzujトcego nazwト. + */ +struct gg_resolver_fork_data { + int pid; /*< Identyfikator procesu */ +}; + + + +#ifdef _WIN32 +/** + * Deal with the fact that you can't select() on a win32 file fd. + * This makes it practically impossible to tie into purple's event loop. + * + * -This is thanks to Tor Lillqvist. + * XXX - Move this to where the rest of the the win32 compatiblity stuff goes when we push the changes back to libgadu. + */ +static int +socket_pipe (int *fds) +{ + SOCKET temp, socket1 = -1, socket2 = -1; + struct sockaddr_in saddr; + int len; + u_long arg; + fd_set read_set, write_set; + struct timeval tv; + + temp = socket(AF_INET, SOCK_STREAM, 0); + + if (temp == INVALID_SOCKET) { + goto out0; + } + + arg = 1; + if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) { + goto out0; + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = 0; + saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) { + goto out0; + } + + if (listen(temp, 1) == SOCKET_ERROR) { + goto out0; + } + + len = sizeof(saddr); + if (getsockname(temp, (struct sockaddr *)&saddr, &len)) { + goto out0; + } + + socket1 = socket(AF_INET, SOCK_STREAM, 0); + + if (socket1 == INVALID_SOCKET) { + goto out0; + } + + arg = 1; + if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { + goto out1; + } + + if (connect(socket1, (struct sockaddr *)&saddr, len) != SOCKET_ERROR || + WSAGetLastError() != WSAEWOULDBLOCK) { + goto out1; + } + + FD_ZERO(&read_set); + FD_SET(temp, &read_set); + + tv.tv_sec = 0; + tv.tv_usec = 0; + + if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) { + goto out1; + } + + if (!FD_ISSET(temp, &read_set)) { + goto out1; + } + + socket2 = accept(temp, (struct sockaddr *) &saddr, &len); + if (socket2 == INVALID_SOCKET) { + goto out1; + } + + FD_ZERO(&write_set); + FD_SET(socket1, &write_set); + + tv.tv_sec = 0; + tv.tv_usec = 0; + + if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) { + goto out2; + } + + if (!FD_ISSET(socket1, &write_set)) { + goto out2; + } + + arg = 0; + if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { + goto out2; + } + + arg = 0; + if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) { + goto out2; + } + + fds[0] = socket1; + fds[1] = socket2; + + closesocket (temp); + + return 0; + +out2: + closesocket (socket2); +out1: + closesocket (socket1); +out0: + closesocket (temp); + errno = EIO; /* XXX */ + + return -1; +} +#endif + + + +#ifdef _WIN32 +struct gg_resolve_win32thread_data { + char *hostname; + int fd; +}; + +static DWORD WINAPI gg_resolve_win32thread_thread(LPVOID arg) +{ + struct gg_resolve_win32thread_data *d = arg; + struct in_addr a; + + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() host: %s, fd: %i called\n", d->hostname, d->fd); + + if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { + /* W przypadku bナて囘u gg_gethostbyname_real() zwrテウci -1 + * i nie zmieni &addr. Tam jest juナシ INADDR_NONE, + * wiト冂 nie musimy robiト nic wiト冂ej. */ + gg_gethostbyname_real(d->hostname, &a, 0); + } + + // if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { + // struct in_addr *hn; + + // if (!(hn = gg_gethostbyname(d->hostname))) + // a.s_addr = INADDR_NONE; + // else { + // a.s_addr = hn->s_addr; + // free(hn); + // } + // } + + write(d->fd, &a, sizeof(a)); + close(d->fd); + + free(d->hostname); + d->hostname = NULL; + + free(d); + + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() done\n"); + + return 0; +} + + +static int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname) +{ + struct gg_resolve_win32thread_data *d = NULL; + HANDLE h; + DWORD dwTId; + int pipes[2], new_errno; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_win32thread(%p, %p, \"%s\");\n", fd, resolver, hostname); + + if (!resolver || !fd || !hostname) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() invalid arguments\n"); + errno = EFAULT; + return -1; + } + + if (socket_pipe(pipes) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); + return -1; + } + + if (!(d = malloc(sizeof(*d)))) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); + new_errno = errno; + goto cleanup; + } + + d->hostname = NULL; + + if (!(d->hostname = strdup(hostname))) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); + new_errno = errno; + goto cleanup; + } + + d->fd = pipes[1]; + + h = CreateThread(NULL, 0, gg_resolve_win32thread_thread, + d, 0, &dwTId); + + if (h == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create thread\n"); + new_errno = errno; + goto cleanup; + } + + *resolver = h; + *fd = pipes[0]; + + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() done\n"); + + return 0; + +cleanup: + if (d) { + free(d->hostname); + free(d); + } + + close(pipes[0]); + close(pipes[1]); + + errno = new_errno; + + return -1; + +} + +static void gg_resolve_win32thread_cleanup(void **priv_data, int force) +{ + struct gg_resolve_win32thread_data *data; + + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() force: %i called\n", force); + + if (priv_data == NULL || *priv_data == NULL) + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() priv_data: NULL\n"); + return; + + data = (struct gg_resolve_win32thread_data*) *priv_data; + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() data: %s called\n", data->hostname); + *priv_data = NULL; + + if (force) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() force called\n", force); + //pthread_cancel(data->thread); + //pthread_join(data->thread, NULL); + } + + free(data->hostname); + data->hostname = NULL; + + if (data->fd != -1) { + close(data->fd); + data->fd = -1; + } + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() done\n"); + free(data); +} +#endif + +#ifndef _WIN32 +/** + * \internal Rozwiトzuje nazwト serwera w osobnym procesie. + * + * Poナてczenia asynchroniczne nie mogト blokowaト procesu w trakcie rozwiトzywania + * nazwy serwera. W tym celu tworzony jest potok, nowy proces i dopiero w nim + * przeprowadzane jest rozwiトzywanie nazwy. Deskryptor strony do odczytu + * zapisuje siト w strukturze sieci i czeka na dane w postaci struktury + * \c in_addr. Jeナ嬪i nie znaleziono nazwy, zwracana jest \c INADDR_NONE. + * + * \param fd Wskaナコnik na zmiennト, gdzie zostanie umieszczony deskryptor + * potoku + * \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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +static int gg_resolver_fork_start(int *fd, void **priv_data, const char *hostname) +{ + struct gg_resolver_fork_data *data = NULL; + struct in_addr addr; + 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 (pipe(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) { + close(pipes[0]); + + if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) { + /* W przypadku bナて囘u gg_gethostbyname_real() zwrテウci -1 + * i nie zmieni &addr. Tam jest juナシ INADDR_NONE, + * wiト冂 nie musimy robiト nic wiト冂ej. */ + gg_gethostbyname_real(hostname, &addr, 0); + } + + if (write(pipes[1], &addr, sizeof(addr)) != sizeof(addr)) + exit(1); + + exit(0); + } + + 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ナZwana 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ナBnia + */ +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); + + waitpid(data->pid, NULL, WNOHANG); + + free(data); +} +#endif + +#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 rfd; /*< Deskryptor do odczytu */ + int wfd; /*< Deskryptor do zapisu */ +}; + +/** + * \internal Usuwanie zasobテウw po wトtku rozwiトzywaniu nazwy. + * + * Funkcja wywoナZwana 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ナBnia + */ +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); + } + + free(data->hostname); + data->hostname = NULL; + + if (data->wfd != -1) { + close(data->wfd); + data->wfd = -1; + } + + 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; + struct in_addr addr; + + pthread_detach(pthread_self()); + + if ((addr.s_addr = inet_addr(data->hostname)) == INADDR_NONE) { + /* W przypadku bナて囘u gg_gethostbyname_real() zwrテウci -1 + * i nie zmieni &addr. Tam jest juナシ INADDR_NONE, + * wiト冂 nie musimy robiト nic wiト冂ej. */ + gg_gethostbyname_real(data->hostname, &addr, 1); + } + + if (write(data->wfd, &addr, sizeof(addr)) == sizeof(addr)) + pthread_exit(NULL); + else + pthread_exit((void*) -1); + + return NULL; /* ナシeby kompilator nie marudziナ */ +} + +/** + * \internal Rozwiトzuje nazwト serwera w osobnym wトtku. + * + * Funkcja dziaナB analogicznie do \c gg_resolver_fork_start(), z tト rテウナシnicト, + * ナシe dziaナB na wトtkach, nie procesach. Jest dostト冪na wyナてcznie gdy podczas + * kompilacji wナてczono odpowiedniト opcjト. + * + * \param fd Wskaナコnik na zmiennト, gdzie zostanie umieszczony deskryptor + * potoku + * \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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +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 (pipe(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->rfd = pipes[0]; + 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) { + free(data->hostname); + free(data); + } + + close(pipes[0]); + close(pipes[1]); + + errno = new_errno; + + return -1; +} + +#endif /* GG_CONFIG_HAVE_PTHREAD */ + +/** + * 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type) +{ + if (gs == NULL) { + errno = EINVAL; + return -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 _WIN32 + type = GG_RESOLVER_WIN32; +#else + type = GG_RESOLVER_FORK; +#endif + +#if defined(GG_CONFIG_HAVE_PTHREAD) || defined(GG_CONFIG_PTHREAD_DEFAULT) + type = GG_RESOLVER_PTHREAD; +#endif + } + + switch (type) { +#ifdef _WIN32 + case GG_RESOLVER_WIN32: + gs->resolver_type = type; + gs->resolver_start = gg_resolve_win32thread; + gs->resolver_cleanup = gg_resolve_win32thread_cleanup; + return 0; +#else + 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 + + 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) +{ + if (gs == NULL) { + errno = EINVAL; + return GG_RESOLVER_INVALID; + } + + return gs->resolver_type; +} + +/** + * Ustawia wナBsny 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 + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int)) +{ + if (gs == NULL || 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +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 _WIN32 + type = GG_RESOLVER_WIN32; +#else + type = GG_RESOLVER_FORK; +#endif + +#if defined(GG_CONFIG_HAVE_PTHREAD) || defined(GG_CONFIG_PTHREAD_DEFAULT) + type = GG_RESOLVER_PTHREAD; +#endif + } + + switch (type) { +#ifdef _WIN32 + case GG_RESOLVER_WIN32: + gh->resolver_type = type; + gh->resolver_start = gg_resolve_win32thread; + gh->resolver_cleanup = gg_resolve_win32thread_cleanup; + return 0; +#else + 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 + + 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ナBsny 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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +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ナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +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; + +#ifndef _WIN32 + 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 _WIN32 + case GG_RESOLVER_WIN32: + gg_global_resolver_type = type; + gg_global_resolver_start = gg_resolve_win32thread; + gg_global_resolver_cleanup = gg_resolve_win32thread_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 + + 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ナBsny sposテウb rozwiトzywania nazw globalnie dla biblioteki. + * + * \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ト冪ujトco: + * - \c "int *fd" — wskaナコnik na zmiennト, gdzie zostanie umieszczony deskryptor potoku + * - \c "void **priv_data" — wskaナコnik na zmiennト, gdzie moナシna umieナ嫩iト 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ト冪ujト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ト冂ia sesji. + * + * WナBsny kod rozwiトzywania nazwy powinien stworzyト potok, parト gniazd lub + * inny deskryptor pozwalajトcy na co najmniej jednostronnト komunikacjト i + * przekazaト go w parametrze \c fd. Po zakoナczeniu rozwiトzywania nazwy, + * powinien wysナBト otrzymany adres IP w postaci sieciowej (big-endian) do + * deskryptora. Jeナ嬪i rozwiトzywanie nazwy siト nie powiedzie, naleナシy wysナBト + * \c INADDR_NONE. Nastト冪nie zostanie wywoナBna funkcja zwalniajトca zasoby + * z parametrem \c force rテウwnym \c 0. Gdyby sesja zostaナB zakoナczona przed + * rozwiトzaniem nazwy, np. za pomocト funkcji \c gg_logoff(), funkcja + * zwalniajトca zasoby zostanie wywoナBna z parametrem \c force rテウwnym \c 1. + * + * \return 0 jeナ嬪i siト powiodナP, -1 w przypadku bナて囘u + */ +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; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/resolver.h Tue Mar 16 12:07:06 2010 +0900 @@ -0,0 +1,30 @@ +/* $Id$ */ + +/* + * (C) Copyright 2008 Wojtek Kaniewski <wojtekka@irc.pl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef LIBGADU_RESOLVER_H +#define LIBGADU_RESOLVER_H + +#ifndef _WIN32 +# include <arpa/inet.h> +#endif + +int gg_gethostbyname_real(const char *hostname, struct in_addr *result, int pthread); + +#endif /* LIBGADU_RESOLVER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/sha1.c Tue Mar 16 12:07:06 2010 +0900 @@ -0,0 +1,303 @@ +/* $Id: sha1.c 632 2008-07-30 18:40:06Z darkjames $ */ + +/* + * (C) Copyright 2007 Wojtek Kaniewski <wojtekka@irc.pl> + * + * Public domain SHA-1 implementation by Steve Reid <steve@edmweb.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/** + * \file sha1.c + * + * \brief Funkcje wyznaczania skrトでUu SHA1 + */ + +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#include "libgadu.h" + +/** \cond ignore */ + +#ifdef GG_CONFIG_HAVE_OPENSSL + +#include <openssl/sha.h> + +#else + +/* +SHA-1 in C +By Steve Reid <steve@edmweb.com> +100% Public Domain + +Modified by Wojtek Kaniewski <wojtekka@toxygen.net> for compatibility +with libgadu and OpenSSL API. + +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define LITTLE_ENDIAN * This should be #define'd if true. */ +/* #define SHA1HANDSOFF * Copies data before messing with it. */ + +#include <string.h> + +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 void SHA1_Init(SHA_CTX* context); +static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len); +static void 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 */ +#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); + + +/* 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; +static unsigned char workspace[64]; + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); + /* 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. */ + 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); + /* 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 */ + a = b = c = d = e = 0; +} + + +/* SHA1_Init - Initialize new context */ + +static void 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; +} + + +/* Run your data through this. */ + +static void 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); +} + + +/* Add padding and return the message digest. */ + +static void SHA1_Final(unsigned char digest[20], SHA_CTX* context) +{ +uint32_t i, j; +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, (unsigned char *)"\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1_Update(context, (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 */ + i = j = 0; + 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 +} + +#endif /* GG_CONFIG_HAVE_OPENSSL */ + +/** \endcond */ + +/** \cond internal */ + +/** + * \internal Liczy skrトでU SHA1 z ziarna i hasトケツB. + * + * \param password HasトケツP + * \param seed Ziarno + * \param result Bufor na wynik funkcji skrトでUu (20 bajtトでX) + */ +void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result) +{ + SHA_CTX ctx; + + SHA1_Init(&ctx); + SHA1_Update(&ctx, (const unsigned char*) password, strlen(password)); + seed = gg_fix32(seed); + SHA1_Update(&ctx, (uint8_t*) &seed, 4); + + SHA1_Final(result, &ctx); +} + +/** + * \internal Liczy skrトでU SHA1 z pliku. + * + * \param fd Deskryptor pliku + * \param result Wskaトケナ殤ik na skrトでU + * + * \return 0 lub -1 + */ +int gg_file_hash_sha1(int fd, uint8_t *result) +{ + unsigned char buf[4096]; + SHA_CTX ctx; + off_t pos, len; + int res; + + 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; + + SHA1_Init(&ctx); + + if (len <= 10485760) { + while ((res = read(fd, buf, sizeof(buf))) > 0) + SHA1_Update(&ctx, buf, res); + } else { + int i; + + for (i = 0; i < 9; i++) { + int j; + + if (lseek(fd, (len - 1048576) / 9 * i, SEEK_SET) == (off_t) - 1) + return -1; + + for (j = 0; j < 1048576 / sizeof(buf); j++) { + if ((res = read(fd, buf, sizeof(buf))) != sizeof(buf)) { + res = -1; + break; + } + + SHA1_Update(&ctx, buf, res); + } + + if (res == -1) + break; + } + } + + if (res == -1) + return -1; + + SHA1_Final(result, &ctx); + + if (lseek(fd, pos, SEEK_SET) == (off_t) -1) + return -1; + + return 0; +} + +/** \endcond */
--- a/libpurple/protocols/irc/irc.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/irc/irc.c Tue Mar 16 12:07:06 2010 +0900 @@ -369,7 +369,7 @@ const char *pass = purple_connection_get_password(gc); if (pass && *pass) { - buf = irc_format(irc, "vv", "PASS", pass); + buf = irc_format(irc, "v:", "PASS", pass); if (irc_send(irc, buf) < 0) { g_free(buf); return FALSE;
--- a/libpurple/protocols/jabber/bosh.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/jabber/bosh.c Tue Mar 16 12:07:06 2010 +0900 @@ -521,7 +521,7 @@ } if (version) { - const char *dot = strstr(version, "."); + const char *dot = strchr(version, '.'); int major, minor = 0; purple_debug_info("jabber", "BOSH connection manager version %s\n", version);
--- a/libpurple/protocols/jabber/buddy.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/jabber/buddy.c Tue Mar 16 12:07:06 2010 +0900 @@ -218,7 +218,7 @@ for (l = jb->resources; l; l = l->next) { JabberBuddyResource *jbr = l->data; - if (g_str_equal(resource, jbr->name)) + if (jbr->name && g_str_equal(resource, jbr->name)) return jbr; } @@ -1824,7 +1824,8 @@ if(!jb) return m; - if (js->protocol_version == JABBER_PROTO_0_9 && jb != js->user_jb) { + if (js->protocol_version.major == 0 && js->protocol_version.minor == 9 && + jb != js->user_jb) { if(jb->invisible & JABBER_INVIS_BUDDY) { act = purple_menu_action_new(_("Un-hide From"), PURPLE_CALLBACK(jabber_buddy_make_visible),
--- a/libpurple/protocols/jabber/data.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/jabber/data.c Tue Mar 16 12:07:06 2010 +0900 @@ -39,7 +39,7 @@ JabberStream *js) { JabberData *data = g_new0(JabberData, 1); - gchar *checksum = purple_util_get_image_checksum(rawdata, size); + gchar *checksum = jabber_calculate_data_sha1sum(rawdata, size); gchar cid[256]; g_snprintf(cid, sizeof(cid), "sha1+%s@bob.xmpp.org", checksum);
--- a/libpurple/protocols/jabber/disco.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/jabber/disco.c Tue Mar 16 12:07:06 2010 +0900 @@ -517,8 +517,12 @@ const char *category, *type, *name; category = xmlnode_get_attrib(child, "category"); type = xmlnode_get_attrib(child, "type"); - if(category && type && !strcmp(category, "pubsub") && !strcmp(type,"pep")) + if(category && type && !strcmp(category, "pubsub") && !strcmp(type,"pep")) { + PurpleConnection *gc = js->gc; js->pep = TRUE; + gc->flags |= PURPLE_CONNECTION_SUPPORT_MOODS | + PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES; + } if (!category || strcmp(category, "server")) continue; if (!type || strcmp(type, "im"))
--- a/libpurple/protocols/jabber/jabber.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/jabber/jabber.c Tue Mar 16 12:07:06 2010 +0900 @@ -74,7 +74,10 @@ GList *jabber_features = NULL; GList *jabber_identities = NULL; -static GSList *jabber_cmds = NULL; + +static GHashTable *jabber_cmds = NULL; /* PurplePlugin * => GSList of ids */ + +static gint plugin_ref = 0; static void jabber_unregister_account_cb(JabberStream *js); static void try_srv_connect(JabberStream *js); @@ -869,7 +872,8 @@ js->old_length = 0; js->keepalive_timeout = 0; /* Set the default protocol version to 1.0. Overridden in parser.c. */ - js->protocol_version = JABBER_PROTO_1_0; + js->protocol_version.major = 1; + js->protocol_version.minor = 0; js->sessions = NULL; js->stun_ip = NULL; js->stun_port = 0; @@ -3318,40 +3322,67 @@ } } -void jabber_register_commands(void) +static PurpleCmdRet +jabber_cmd_mood(PurpleConversation *conv, + const char *cmd, char **args, char **error, void *data) { + JabberStream *js = conv->account->gc->proto_data; + + if (js->pep) { + /* if no argument was given, unset mood */ + if (!args | !args[0]) { + jabber_mood_set(js, NULL, NULL); + } else if (!args[1]) { + jabber_mood_set(js, args[0], NULL); + } else { + jabber_mood_set(js, args[0], args[1]); + } + + return PURPLE_CMD_RET_OK; + } else { + /* account does not support PEP, can't set a mood */ + purple_conversation_write(conv, NULL, + _("Account does not support PEP, can't set mood"), + PURPLE_MESSAGE_ERROR, time(NULL)); + return PURPLE_CMD_RET_FAILED; + } +} + +static void jabber_register_commands(PurplePlugin *plugin) +{ + GSList *commands = NULL; PurpleCmdId id; id = purple_cmd_register("config", "", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-jabber", jabber_cmd_chat_config, _("config: Configure a chat room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("configure", "", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-jabber", jabber_cmd_chat_config, _("configure: Configure a chat room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("nick", "s", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-jabber", jabber_cmd_chat_nick, _("nick <new nickname>: Change your nickname."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("part", "s", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", jabber_cmd_chat_part, _("part [message]: Leave the room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("register", "", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-jabber", jabber_cmd_chat_register, _("register: Register with a chat room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); /* XXX: there needs to be a core /topic cmd, methinks */ id = purple_cmd_register("topic", "s", PURPLE_CMD_P_PRPL, @@ -3360,7 +3391,7 @@ jabber_cmd_chat_topic, _("topic [new topic]: View or change the topic."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("ban", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | @@ -3368,7 +3399,7 @@ jabber_cmd_chat_ban, _("ban <user> [reason]: Ban a user from the room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("affiliate", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | @@ -3376,7 +3407,7 @@ jabber_cmd_chat_affiliate, _("affiliate <owner|admin|member|outcast|none> [nick1] [nick2] ...: Get the users with an affiliation or set users' affiliation with the room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("role", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | @@ -3384,7 +3415,7 @@ jabber_cmd_chat_role, _("role <moderator|participant|visitor|none> [nick1] [nick2] ...: Get the users with a role or set users' role with the room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("invite", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | @@ -3392,7 +3423,7 @@ jabber_cmd_chat_invite, _("invite <user> [message]: Invite a user to the room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("join", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | @@ -3400,7 +3431,7 @@ jabber_cmd_chat_join, _("join: <room> [password]: Join a chat on this server."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("kick", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | @@ -3408,14 +3439,14 @@ jabber_cmd_chat_kick, _("kick <user> [reason]: Kick a user from the room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("msg", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-jabber", jabber_cmd_chat_msg, _("msg <user> <message>: Send a private message to another user."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("ping", "w", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM | @@ -3423,24 +3454,39 @@ "prpl-jabber", jabber_cmd_ping, _("ping <jid>: Ping a user/component/server."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("buzz", "w", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", jabber_cmd_buzz, _("buzz: Buzz a user to get their attention"), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("mood", "ws", PURPLE_CMD_P_PRPL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM | + PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, + "prpl-jabber", jabber_cmd_mood, + _("mood: Set current user mood"), NULL); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); + + g_hash_table_insert(jabber_cmds, plugin, commands); } -void jabber_unregister_commands(void) +static void cmds_free_func(gpointer value) { - while (jabber_cmds != NULL) { - purple_cmd_unregister(GPOINTER_TO_UINT(jabber_cmds->data)); - jabber_cmds = g_slist_delete_link(jabber_cmds, jabber_cmds); + GSList *commands = value; + while (commands) { + purple_cmd_unregister(GPOINTER_TO_UINT(commands->data)); + commands = g_slist_delete_link(commands, commands); } } +static void jabber_unregister_commands(PurplePlugin *plugin) +{ + g_hash_table_remove(jabber_cmds, plugin); +} + /* IPC functions */ /** @@ -3489,14 +3535,46 @@ jabber_caps_broadcast_change(); } -void -jabber_init_plugin(PurplePlugin *plugin) +static void +jabber_do_init(void) { GHashTable *ui_info = purple_core_get_ui_info(); const gchar *ui_type; const gchar *type = "pc"; /* default client type, if unknown or unspecified */ const gchar *ui_name = NULL; +#ifdef HAVE_CYRUS_SASL + /* We really really only want to do this once per process */ + static gboolean sasl_initialized = FALSE; +#ifdef _WIN32 + UINT old_error_mode; + gchar *sasldir; +#endif + int ret; +#endif + + /* XXX - If any other plugin wants SASL this won't be good ... */ +#ifdef HAVE_CYRUS_SASL + if (!sasl_initialized) { + sasl_initialized = TRUE; +#ifdef _WIN32 + sasldir = g_build_filename(wpurple_install_dir(), "sasl2", NULL); + sasl_set_path(SASL_PATH_TYPE_PLUGIN, sasldir); + g_free(sasldir); + /* Suppress error popups for failing to load sasl plugins */ + old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS); +#endif + if ((ret = sasl_client_init(NULL)) != SASL_OK) { + purple_debug_error("xmpp", "Error (%d) initializing SASL.\n", ret); + } +#ifdef _WIN32 + /* Restore the original error mode */ + SetErrorMode(old_error_mode); +#endif + } +#endif + + jabber_cmds = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, cmds_free_func); ui_type = ui_info ? g_hash_table_lookup(ui_info, "client_type") : NULL; if (ui_type) { @@ -3560,7 +3638,53 @@ G_CALLBACK(jabber_caps_broadcast_change), NULL); #endif + /* reverse order of unload_plugin */ + jabber_iq_init(); + jabber_presence_init(); + jabber_caps_init(); + /* PEP things should be init via jabber_pep_init, not here */ + jabber_pep_init(); + jabber_data_init(); + jabber_bosh_init(); + + /* TODO: Implement adding and retrieving own features via IPC API */ + + jabber_ibb_init(); + jabber_si_init(); + jabber_auth_init(); +} + +static void +jabber_do_uninit(void) +{ + /* reverse order of jabber_do_init */ + jabber_bosh_uninit(); + jabber_data_uninit(); + jabber_si_uninit(); + jabber_ibb_uninit(); + /* PEP things should be uninit via jabber_pep_uninit, not here */ + jabber_pep_uninit(); + jabber_caps_uninit(); + jabber_presence_uninit(); + jabber_iq_uninit(); + + jabber_auth_uninit(); + jabber_features_destroy(); + jabber_identities_destroy(); + + g_hash_table_destroy(jabber_cmds); + jabber_cmds = NULL; +} + +void jabber_plugin_init(PurplePlugin *plugin) +{ + ++plugin_ref; + + if (plugin_ref == 1) + jabber_do_init(); + + jabber_register_commands(plugin); /* IPC functions */ purple_plugin_ipc_register(plugin, "contact_has_feature", PURPLE_CALLBACK(jabber_ipc_contact_has_feature), @@ -3575,7 +3699,6 @@ NULL, 1, purple_value_new(PURPLE_TYPE_STRING)); - /* Modifying these? Look at libxmpp.c:load_plugin for the signal versions */ purple_plugin_ipc_register(plugin, "register_namespace_watcher", PURPLE_CALLBACK(jabber_iq_signal_register), purple_marshal_VOID__POINTER_POINTER, @@ -3589,14 +3712,95 @@ NULL, 2, purple_value_new(PURPLE_TYPE_STRING), /* node */ purple_value_new(PURPLE_TYPE_STRING)); /* namespace */ + + purple_signal_register(plugin, "jabber-register-namespace-watcher", + purple_marshal_VOID__POINTER_POINTER, + NULL, 2, + purple_value_new(PURPLE_TYPE_STRING), /* node */ + purple_value_new(PURPLE_TYPE_STRING)); /* namespace */ + + purple_signal_register(plugin, "jabber-unregister-namespace-watcher", + purple_marshal_VOID__POINTER_POINTER, + NULL, 2, + purple_value_new(PURPLE_TYPE_STRING), /* node */ + purple_value_new(PURPLE_TYPE_STRING)); /* namespace */ + + purple_signal_connect(plugin, "jabber-register-namespace-watcher", + plugin, PURPLE_CALLBACK(jabber_iq_signal_register), NULL); + purple_signal_connect(plugin, "jabber-unregister-namespace-watcher", + plugin, PURPLE_CALLBACK(jabber_iq_signal_unregister), NULL); + + + purple_signal_register(plugin, "jabber-receiving-xmlnode", + purple_marshal_VOID__POINTER_POINTER, NULL, 2, + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), + purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); + + purple_signal_register(plugin, "jabber-sending-xmlnode", + purple_marshal_VOID__POINTER_POINTER, NULL, 2, + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), + purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); + + /* + * Do not remove this or the plugin will fail. Completely. You have been + * warned! + */ + purple_signal_connect_priority(plugin, "jabber-sending-xmlnode", + plugin, PURPLE_CALLBACK(jabber_send_signal_cb), + NULL, PURPLE_SIGNAL_PRIORITY_HIGHEST); + + purple_signal_register(plugin, "jabber-sending-text", + purple_marshal_VOID__POINTER_POINTER, NULL, 2, + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), + purple_value_new_outgoing(PURPLE_TYPE_STRING)); + + purple_signal_register(plugin, "jabber-receiving-message", + purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER_POINTER, + purple_value_new(PURPLE_TYPE_BOOLEAN), 6, + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), + purple_value_new(PURPLE_TYPE_STRING), /* type */ + purple_value_new(PURPLE_TYPE_STRING), /* id */ + purple_value_new(PURPLE_TYPE_STRING), /* from */ + purple_value_new(PURPLE_TYPE_STRING), /* to */ + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); + + purple_signal_register(plugin, "jabber-receiving-iq", + purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER, + purple_value_new(PURPLE_TYPE_BOOLEAN), 5, + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), + purple_value_new(PURPLE_TYPE_STRING), /* type */ + purple_value_new(PURPLE_TYPE_STRING), /* id */ + purple_value_new(PURPLE_TYPE_STRING), /* from */ + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); + + purple_signal_register(plugin, "jabber-watched-iq", + purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER, + purple_value_new(PURPLE_TYPE_BOOLEAN), 5, + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), + purple_value_new(PURPLE_TYPE_STRING), /* type */ + purple_value_new(PURPLE_TYPE_STRING), /* id */ + purple_value_new(PURPLE_TYPE_STRING), /* from */ + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); /* child */ + + purple_signal_register(plugin, "jabber-receiving-presence", + purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER, + purple_value_new(PURPLE_TYPE_BOOLEAN), 4, + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), + purple_value_new(PURPLE_TYPE_STRING), /* type */ + purple_value_new(PURPLE_TYPE_STRING), /* from */ + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); } -void -jabber_uninit_plugin(PurplePlugin *plugin) +void jabber_plugin_uninit(PurplePlugin *plugin) { + g_return_if_fail(plugin_ref > 0); + + purple_signals_unregister_by_instance(plugin); purple_plugin_ipc_unregister_all(plugin); - jabber_auth_uninit(); - jabber_features_destroy(); - jabber_identities_destroy(); + jabber_unregister_commands(plugin); + + --plugin_ref; + if (plugin_ref == 0) + jabber_do_uninit(); }
--- a/libpurple/protocols/jabber/jabber.h Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/jabber/jabber.h Tue Mar 16 12:07:06 2010 +0900 @@ -105,9 +105,9 @@ xmlParserCtxt *context; xmlnode *current; - enum { - JABBER_PROTO_0_9, - JABBER_PROTO_1_0 + struct { + guint8 major; + guint8 minor; } protocol_version; JabberSaslMech *auth_mech; @@ -376,10 +376,7 @@ PurpleMediaCaps jabber_get_media_caps(PurpleAccount *account, const char *who); gboolean jabber_can_receive_file(PurpleConnection *gc, const gchar *who); -void jabber_register_commands(void); -void jabber_unregister_commands(void); - -void jabber_init_plugin(PurplePlugin *plugin); -void jabber_uninit_plugin(PurplePlugin *plugin); +void jabber_plugin_init(PurplePlugin *plugin); +void jabber_plugin_uninit(PurplePlugin *plugin); #endif /* PURPLE_JABBER_H_ */
--- a/libpurple/protocols/jabber/jutil.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/jabber/jutil.c Tue Mar 16 12:07:06 2010 +0900 @@ -302,7 +302,7 @@ const guchar *c; c = (const guchar *)in; - while (*c) { + for ( ; *c; ++c) { if (*c > 0x7f || (*c < 0x20 && *c != '\t' && *c != '\n' && *c != '\r')) return NULL;
--- a/libpurple/protocols/jabber/libxmpp.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/jabber/libxmpp.c Tue Mar 16 12:07:06 2010 +0900 @@ -132,104 +132,14 @@ static gboolean load_plugin(PurplePlugin *plugin) { - purple_signal_register(plugin, "jabber-receiving-xmlnode", - purple_marshal_VOID__POINTER_POINTER, NULL, 2, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); - - purple_signal_register(plugin, "jabber-sending-xmlnode", - purple_marshal_VOID__POINTER_POINTER, NULL, 2, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); - - /* - * Do not remove this or the plugin will fail. Completely. You have been - * warned! - */ - purple_signal_connect_priority(plugin, "jabber-sending-xmlnode", - plugin, PURPLE_CALLBACK(jabber_send_signal_cb), - NULL, PURPLE_SIGNAL_PRIORITY_HIGHEST); - - purple_signal_register(plugin, "jabber-sending-text", - purple_marshal_VOID__POINTER_POINTER, NULL, 2, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new_outgoing(PURPLE_TYPE_STRING)); - - purple_signal_register(plugin, "jabber-receiving-message", - purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER_POINTER, - purple_value_new(PURPLE_TYPE_BOOLEAN), 6, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new(PURPLE_TYPE_STRING), /* type */ - purple_value_new(PURPLE_TYPE_STRING), /* id */ - purple_value_new(PURPLE_TYPE_STRING), /* from */ - purple_value_new(PURPLE_TYPE_STRING), /* to */ - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); - - purple_signal_register(plugin, "jabber-receiving-iq", - purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER, - purple_value_new(PURPLE_TYPE_BOOLEAN), 5, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new(PURPLE_TYPE_STRING), /* type */ - purple_value_new(PURPLE_TYPE_STRING), /* id */ - purple_value_new(PURPLE_TYPE_STRING), /* from */ - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); - - purple_signal_register(plugin, "jabber-watched-iq", - purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER, - purple_value_new(PURPLE_TYPE_BOOLEAN), 5, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new(PURPLE_TYPE_STRING), /* type */ - purple_value_new(PURPLE_TYPE_STRING), /* id */ - purple_value_new(PURPLE_TYPE_STRING), /* from */ - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); /* child */ - - /* Modifying these? Look at jabber_init_plugin for the ipc versions */ - purple_signal_register(plugin, "jabber-register-namespace-watcher", - purple_marshal_VOID__POINTER_POINTER, - NULL, 2, - purple_value_new(PURPLE_TYPE_STRING), /* node */ - purple_value_new(PURPLE_TYPE_STRING)); /* namespace */ - - purple_signal_register(plugin, "jabber-unregister-namespace-watcher", - purple_marshal_VOID__POINTER_POINTER, - NULL, 2, - purple_value_new(PURPLE_TYPE_STRING), /* node */ - purple_value_new(PURPLE_TYPE_STRING)); /* namespace */ - - purple_signal_connect(plugin, "jabber-register-namespace-watcher", - plugin, PURPLE_CALLBACK(jabber_iq_signal_register), NULL); - purple_signal_connect(plugin, "jabber-unregister-namespace-watcher", - plugin, PURPLE_CALLBACK(jabber_iq_signal_unregister), NULL); - - purple_signal_register(plugin, "jabber-receiving-presence", - purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER, - purple_value_new(PURPLE_TYPE_BOOLEAN), 4, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new(PURPLE_TYPE_STRING), /* type */ - purple_value_new(PURPLE_TYPE_STRING), /* from */ - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); + jabber_plugin_init(plugin); return TRUE; } static gboolean unload_plugin(PurplePlugin *plugin) { - purple_signals_unregister_by_instance(plugin); - - /* reverse order of init_plugin */ - jabber_bosh_uninit(); - jabber_data_uninit(); - jabber_si_uninit(); - jabber_ibb_uninit(); - /* PEP things should be uninit via jabber_pep_uninit, not here */ - jabber_pep_uninit(); - jabber_caps_uninit(); - jabber_iq_uninit(); - - jabber_unregister_commands(); - - /* Stay on target...stay on target... Almost there... */ - jabber_uninit_plugin(plugin); + jabber_plugin_uninit(plugin); return TRUE; } @@ -339,13 +249,6 @@ static void init_plugin(PurplePlugin *plugin) { -#ifdef HAVE_CYRUS_SASL -#ifdef _WIN32 - UINT old_error_mode; - gchar *sasldir; -#endif - int ret; -#endif PurpleAccountUserSplit *split; PurpleAccountOption *option; @@ -354,7 +257,7 @@ purple_account_user_split_set_reverse(split, FALSE); prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); - split = purple_account_user_split_new(_("Resource"), NULL, '/'); + split = purple_account_user_split_new(_("Resource"), "", '/'); purple_account_user_split_set_reverse(split, FALSE); prpl_info.user_splits = g_list_append(prpl_info.user_splits, split); @@ -402,42 +305,9 @@ option); my_protocol = plugin; - jabber_init_plugin(plugin); purple_prefs_remove("/plugins/prpl/jabber"); - /* XXX - If any other plugin wants SASL this won't be good ... */ -#ifdef HAVE_CYRUS_SASL -#ifdef _WIN32 - sasldir = g_build_filename(wpurple_install_dir(), "sasl2", NULL); - sasl_set_path(SASL_PATH_TYPE_PLUGIN, sasldir); - g_free(sasldir); - /* Suppress error popups for failing to load sasl plugins */ - old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS); -#endif - if ((ret = sasl_client_init(NULL)) != SASL_OK) { - purple_debug_error("xmpp", "Error (%d) initializing SASL.\n", ret); - } -#ifdef _WIN32 - /* Restore the original error mode */ - SetErrorMode(old_error_mode); -#endif -#endif - jabber_register_commands(); - - /* reverse order of unload_plugin */ - jabber_iq_init(); - jabber_caps_init(); - /* PEP things should be init via jabber_pep_init, not here */ - jabber_pep_init(); - jabber_data_init(); - jabber_bosh_init(); - - /* TODO: Implement adding and retrieving own features via IPC API */ - - jabber_ibb_init(); - jabber_si_init(); - purple_signal_connect(purple_get_core(), "uri-handler", plugin, PURPLE_CALLBACK(xmpp_uri_handler), NULL); }
--- a/libpurple/protocols/jabber/message.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/jabber/message.c Tue Mar 16 12:07:06 2010 +0900 @@ -74,24 +74,6 @@ jb = jabber_buddy_find(jm->js, jm->from, TRUE); jbr = jabber_buddy_find_resource(jb, jid->resource); - if (jid->resource) { - /* - * We received a message from a specific resource, so we probably want a - * reply to go to this specific resource (i.e. bind/lock the - * conversation to this resource). - * - * This works because purple_conv_im_send gets the name from - * purple_conversation_get_name() - */ - PurpleConversation *conv; - - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, jm->from, account); - if (conv && !g_str_equal(jm->from, purple_conversation_get_name(conv))) { - purple_debug_info("jabber", "Binding conversation to %s\n", jm->from); - purple_conversation_set_name(conv, jm->from); - } - } - if(!jm->xhtml && !jm->body) { if (jbr) { if (jm->chat_state != JM_STATE_NONE) @@ -137,6 +119,28 @@ serv_got_typing_stopped(gc, jm->from); } } else { + if (jid->resource) { + /* + * We received a message from a specific resource, so + * we probably want a reply to go to this specific + * resource (i.e. bind/lock the conversation to this + * resource). + * + * This works because purple_conv_im_send gets the name + * from purple_conversation_get_name() + */ + PurpleConversation *conv; + + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, + jm->from, account); + if (conv && !g_str_equal(jm->from, + purple_conversation_get_name(conv))) { + purple_debug_info("jabber", "Binding conversation to %s\n", + jm->from); + purple_conversation_set_name(conv, jm->from); + } + } + if(jbr) { if (jm->chat_state != JM_STATE_NONE) jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED;
--- a/libpurple/protocols/jabber/parser.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/jabber/parser.c Tue Mar 16 12:07:06 2010 +0900 @@ -44,14 +44,28 @@ if(!element_name) { return; } else if(!xmlStrcmp(element_name, (xmlChar*) "stream")) { - js->protocol_version = JABBER_PROTO_0_9; + js->protocol_version.major = 0; + js->protocol_version.minor = 9; for(i=0; i < nb_attributes * 5; i += 5) { int attrib_len = attributes[i+4] - attributes[i+3]; char *attrib = g_strndup((gchar *)attributes[i+3], attrib_len); - if(!xmlStrcmp(attributes[i], (xmlChar*) "version") - && !strcmp(attrib, "1.0")) { - js->protocol_version = JABBER_PROTO_1_0; + if(!xmlStrcmp(attributes[i], (xmlChar*) "version")) { + const char *dot = strchr(attrib, '.'); + + js->protocol_version.major = atoi(attrib); + js->protocol_version.minor = dot ? atoi(dot + 1) : 0; + + if (js->protocol_version.major > 1) + /* TODO: Send <unsupported-version/> error */ + purple_connection_error_reason(js->gc, + PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, + _("XMPP Version Mismatch")); + + if (js->protocol_version.major == 0 && js->protocol_version.minor != 9) { + purple_debug_warning("jabber", "Treating version %s as 0.9 for backward " + "compatibility\n", attrib); + } g_free(attrib); } else if(!xmlStrcmp(attributes[i], (xmlChar*) "id")) { g_free(js->stream_id); @@ -255,7 +269,8 @@ } } - if (js->protocol_version == JABBER_PROTO_0_9 && !js->gc->disconnect_timeout && + if (js->protocol_version.major == 0 && js->protocol_version.minor == 9 && + !js->gc->disconnect_timeout && (js->state == JABBER_STREAM_INITIALIZING || js->state == JABBER_STREAM_INITIALIZING_ENCRYPTION)) { /*
--- a/libpurple/protocols/jabber/pep.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/jabber/pep.c Tue Mar 16 12:07:06 2010 +0900 @@ -56,7 +56,6 @@ void jabber_pep_init_actions(GList **m) { /* register the PEP-specific actions */ - jabber_mood_init_action(m); jabber_nick_init_action(m); }
--- a/libpurple/protocols/jabber/presence.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/jabber/presence.c Tue Mar 16 12:07:06 2010 +0900 @@ -43,6 +43,37 @@ #include "usermood.h" #include "usertune.h" +static GHashTable *presence_handlers = NULL; + +static const struct { + const char *name; + JabberPresenceType type; +} jabber_presence_types[] = { + { "error", JABBER_PRESENCE_ERROR }, + { "probe", JABBER_PRESENCE_PROBE }, + { "unavailable", JABBER_PRESENCE_UNAVAILABLE }, + { "subscribe", JABBER_PRESENCE_SUBSCRIBE }, + { "subscribed", JABBER_PRESENCE_SUBSCRIBED }, + { "unsubscribe", JABBER_PRESENCE_UNSUBSCRIBE }, + { "unsubscribed", JABBER_PRESENCE_UNSUBSCRIBED } + /* { NULL, JABBER_PRESENCE_AVAILABLE } the default */ +}; + +static JabberPresenceType +str_to_presence_type(const char *type) +{ + int i; + + if (type == NULL) + return JABBER_PRESENCE_AVAILABLE; + + for (i = 0; i < G_N_ELEMENTS(jabber_presence_types); ++i) + if (g_str_equal(type, jabber_presence_types[i].name)) + return jabber_presence_types[i].type; + + purple_debug_warning("jabber", "Unknown presence type '%s'\n", type); + return JABBER_PRESENCE_AVAILABLE; +} static void chats_send_presence_foreach(gpointer key, gpointer val, gpointer user_data) @@ -137,7 +168,9 @@ if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_MOOD) { const char *mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); - jabber_mood_set(js, mood, NULL); + const char *mood_text = + purple_status_get_attr_string(status, PURPLE_MOOD_COMMENT); + jabber_mood_set(js, mood, mood_text); return; } @@ -474,7 +507,6 @@ purple_prpl_got_media_caps( purple_connection_get_account(userdata->js->gc), userdata->from); - if (info == NULL) goto out; @@ -506,573 +538,498 @@ g_free(userdata); } +gboolean +handle_presence_chat(JabberStream *js, JabberPresence *presence, xmlnode *packet) +{ + static int i = 1; + PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE; + JabberChat *chat = presence->chat; + + if (presence->state == JABBER_BUDDY_STATE_ERROR) { + char *title, *msg = jabber_parse_error(js, packet, NULL); + + if (!chat->conv) { + title = g_strdup_printf(_("Error joining chat %s"), presence->from); + purple_serv_got_join_chat_failed(js->gc, chat->components); + } else { + title = g_strdup_printf(_("Error in chat %s"), presence->from); + if (g_hash_table_size(chat->members) == 0) + serv_got_chat_left(js->gc, chat->id); + } + purple_notify_error(js->gc, title, title, msg); + g_free(title); + g_free(msg); + + if (g_hash_table_size(chat->members) == 0) + /* Only destroy the chat if the error happened while joining */ + jabber_chat_destroy(chat); + return FALSE; + } + + if (presence->type == JABBER_PRESENCE_AVAILABLE) { + const char *jid = NULL; + const char *affiliation = NULL; + const char *role = NULL; + gboolean is_our_resource = FALSE; /* Is the presence about us? */ + JabberBuddyResource *jbr; + + /* + * XEP-0045 mandates the presence to include a resource (which is + * treated as the chat nick). Some non-compliant servers allow + * joining without a nick. + */ + if (!presence->jid_from->resource) + return FALSE; + + if (presence->chat_info.item) { + jid = xmlnode_get_attrib(presence->chat_info.item, "jid"); + affiliation = xmlnode_get_attrib(presence->chat_info.item, "affiliation"); + role = xmlnode_get_attrib(presence->chat_info.item, "role"); + } + + if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(110))) + is_our_resource = TRUE; + + if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(201))) { + chat->config_dialog_type = PURPLE_REQUEST_ACTION; + chat->config_dialog_handle = + purple_request_action(js->gc, + _("Create New Room"), + _("Create New Room"), + _("You are creating a new room. Would" + " you like to configure it, or" + " accept the default settings?"), + /* Default Action */ 1, + purple_connection_get_account(js->gc), NULL, chat->conv, + chat, 2, + _("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure), + _("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room)); + } + + if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(210))) { + /* server rewrote room-nick */ + g_free(chat->handle); + chat->handle = g_strdup(presence->jid_from->resource); + } + + if (purple_strequal(affiliation, "owner")) + flags |= PURPLE_CBFLAGS_FOUNDER; + if (role) { + if (g_str_equal(role, "moderator")) + flags |= PURPLE_CBFLAGS_OP; + else if (g_str_equal(role, "participant")) + flags |= PURPLE_CBFLAGS_VOICE; + } + + if(!chat->conv) { + char *room_jid = g_strdup_printf("%s@%s", presence->jid_from->node, presence->jid_from->domain); + chat->id = i++; + chat->conv = serv_got_joined_chat(js->gc, chat->id, room_jid); + purple_conv_chat_set_nick(PURPLE_CONV_CHAT(chat->conv), chat->handle); + + jabber_chat_disco_traffic(chat); + g_free(room_jid); + } + + jbr = jabber_buddy_track_resource(presence->jb, presence->jid_from->resource, presence->priority, presence->state, presence->status); + jbr->commands_fetched = TRUE; + + jabber_chat_track_handle(chat, presence->jid_from->resource, jid, affiliation, role); + + if(!jabber_chat_find_buddy(chat->conv, presence->jid_from->resource)) + purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource, + jid, flags, !presence->delayed); + else + purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource, + flags); + } else if (presence->type == JABBER_PRESENCE_UNAVAILABLE) { + gboolean nick_change = FALSE; + gboolean kick = FALSE; + gboolean is_our_resource = FALSE; /* Is the presence about us? */ + + const char *jid = NULL; + + /* If the chat nick is invalid, we haven't yet joined, or we've + * already left (it was probably us leaving after we closed the + * chat), we don't care. + */ + if (!presence->jid_from->resource || !chat->conv || chat->left) { + if (chat->left && + presence->jid_from->resource && chat->handle && !strcmp(presence->jid_from->resource, chat->handle)) + jabber_chat_destroy(chat); + return FALSE; + } + + is_our_resource = (0 == g_utf8_collate(presence->jid_from->resource, chat->handle)); + + jabber_buddy_remove_resource(presence->jb, presence->jid_from->resource); + + if (presence->chat_info.item) + jid = xmlnode_get_attrib(presence->chat_info.item, "jid"); + + if (chat->muc) { + if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(110))) + is_our_resource = TRUE; + + if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(301))) { + /* XXX: We got banned. YAY! (No GIR, that's bad) */ + } + + + if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(303))) { + const char *nick = NULL; + if (presence->chat_info.item) + nick = xmlnode_get_attrib(presence->chat_info.item, "nick"); + + /* nick change */ + if (nick) { + purple_debug_warning("jabber", "Chat presence indicating a nick change, but no new nickname!\n"); + } else { + nick_change = TRUE; + + if (g_str_equal(presence->jid_from->resource, chat->handle)) { + /* Changing our own nickname */ + g_free(chat->handle); + chat->handle = g_strdup(nick); + } + + purple_conv_chat_rename_user(PURPLE_CONV_CHAT(chat->conv), + presence->jid_from->resource, + nick); + jabber_chat_remove_handle(chat, + presence->jid_from->resource); + } + } + + if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(307))) { + /* Someone was kicked from the room */ + const char *actor = NULL; + char *reason = NULL; + char *tmp; + + kick = TRUE; + + if (presence->chat_info.item) { + xmlnode *node; + + node = xmlnode_get_child(presence->chat_info.item, "actor"); + if (node) + actor = xmlnode_get_attrib(node, "jid"); + node = xmlnode_get_child(presence->chat_info.item, "reason"); + if (node) + reason = xmlnode_get_data(node); + } + + if (reason == NULL) + reason = g_strdup(_("No reason")); + + if (is_our_resource) { + if (actor) + tmp = g_strdup_printf(_("You have been kicked by %s: (%s)"), + actor, reason); + else + tmp = g_strdup_printf(_("You have been kicked: (%s)"), + reason); + } else { + if (actor) + tmp = g_strdup_printf(_("Kicked by %s (%s)"), + actor, reason); + else + tmp = g_strdup_printf(_("Kicked (%s)"), + reason); + } + + g_free(presence->status); + presence->status = tmp; + + g_free(reason); + } + + if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(321))) { + /* XXX: removed due to an affiliation change */ + } + + if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(322))) { + /* XXX: removed because room is now members-only */ + } + + if (g_slist_find(presence->chat_info.codes, GINT_TO_POINTER(332))) { + /* XXX: removed due to system shutdown */ + } + } + + /* + * Possibly another connected resource of our JID (see XEP-0045 + * v1.24 section 7.1.10) being disconnected. Should be + * distinguished by the item_jid. + * Also possibly works around bits of an Openfire bug. See + * #8319. + */ + if (is_our_resource && jid && !purple_strequal(presence->to, jid)) { + /* TODO: When the above is a loop, this needs to still act + * sanely for all cases (this code is a little fragile). */ + if (!kick && !nick_change) + /* Presumably, kicks and nick changes also affect us. */ + is_our_resource = FALSE; + } + + if(!nick_change) { + if (is_our_resource) { + if (kick) + purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource, + presence->status, PURPLE_MESSAGE_SYSTEM, time(NULL)); + + serv_got_chat_left(js->gc, chat->id); + jabber_chat_destroy(chat); + } else { + purple_conv_chat_remove_user(PURPLE_CONV_CHAT(chat->conv), presence->jid_from->resource, + presence->status); + jabber_chat_remove_handle(chat, presence->jid_from->resource); + } + } + } + + return TRUE; +} + +gboolean +handle_presence_contact(JabberStream *js, JabberPresence *presence) +{ + JabberBuddyResource *jbr; + PurpleAccount *account; + PurpleBuddy *b; + char *buddy_name; + PurpleConversation *conv; + + buddy_name = jabber_id_get_bare_jid(presence->jid_from); + + account = purple_connection_get_account(js->gc); + b = purple_find_buddy(account, buddy_name); + + /* + * Unbind/unlock from sending messages to a specific resource on + * presence changes. This is locked to a specific resource when + * receiving a message (in message.c). + */ + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, + buddy_name, account); + if (conv) { + purple_debug_info("jabber", "Changed conversation binding from %s to %s\n", + purple_conversation_get_name(conv), buddy_name); + purple_conversation_set_name(conv, buddy_name); + } + + if (b == NULL) { + if (presence->jb != js->user_jb) { + purple_debug_warning("jabber", "Got presence for unknown buddy %s on account %s (%p)\n", + buddy_name, purple_account_get_username(account), account); + return FALSE; + } else { + /* this is a different resource of our own account. Resume even when this account isn't on our blist */ + } + } + + if(b && presence->vcard_avatar_hash) { + const char *avatar_hash2 = purple_buddy_icons_get_checksum_for_user(b); + if(!avatar_hash2 || strcmp(presence->vcard_avatar_hash, avatar_hash2)) { + JabberIq *iq; + xmlnode *vcard; + + /* XXX this is a crappy way of trying to prevent + * someone from spamming us with presence packets + * and causing us to DoS ourselves...what we really + * need is a queue system that can throttle itself, + * but i'm too tired to write that right now */ + if(!g_slist_find(js->pending_avatar_requests, presence->jb)) { + + js->pending_avatar_requests = g_slist_prepend(js->pending_avatar_requests, presence->jb); + + iq = jabber_iq_new(js, JABBER_IQ_GET); + xmlnode_set_attrib(iq->node, "to", buddy_name); + vcard = xmlnode_new_child(iq->node, "vCard"); + xmlnode_set_namespace(vcard, "vcard-temp"); + + jabber_iq_set_callback(iq, jabber_vcard_parse_avatar, NULL); + jabber_iq_send(iq); + } + } + } + + if (presence->state == JABBER_BUDDY_STATE_ERROR || + presence->type == JABBER_PRESENCE_UNAVAILABLE || + presence->type == JABBER_PRESENCE_UNSUBSCRIBED) { + jabber_buddy_remove_resource(presence->jb, presence->jid_from->resource); + } else { + jbr = jabber_buddy_track_resource(presence->jb, + presence->jid_from->resource, presence->priority, + presence->state, presence->status); + jbr->idle = presence->idle ? time(NULL) - presence->idle : 0; + } + + jbr = jabber_buddy_find_resource(presence->jb, NULL); + if (jbr) { + jabber_google_presence_incoming(js, buddy_name, jbr); + purple_prpl_got_user_status(account, buddy_name, + jabber_buddy_state_get_status_id(jbr->state), + "priority", jbr->priority, + "message", jbr->status, + NULL); + purple_prpl_got_user_idle(account, buddy_name, + jbr->idle, jbr->idle); + if (presence->nickname) + serv_got_alias(js->gc, buddy_name, presence->nickname); + } else { + purple_prpl_got_user_status(account, buddy_name, + jabber_buddy_state_get_status_id(JABBER_BUDDY_STATE_UNAVAILABLE), + presence->status ? "message" : NULL, presence->status, + NULL); + } + g_free(buddy_name); + + return TRUE; +} + void jabber_presence_parse(JabberStream *js, xmlnode *packet) { - const char *from; const char *type; - char *status = NULL; - int priority = 0; - JabberID *jid; - JabberChat *chat; - JabberBuddy *jb; - JabberBuddyResource *jbr = NULL, *found_jbr = NULL; - PurpleConvChatBuddyFlags flags = PURPLE_CBFLAGS_NONE; - gboolean delayed = FALSE; - const gchar *stamp = NULL; /* from <delayed/> element */ - PurpleAccount *account; - PurpleBuddy *b = NULL; - char *buddy_name; - JabberBuddyState state = JABBER_BUDDY_STATE_UNKNOWN; - xmlnode *y; - char *avatar_hash = NULL; - xmlnode *caps = NULL; - int idle = 0; - gchar *nickname = NULL; - gboolean signal_return; + JabberBuddyResource *jbr = NULL; + gboolean signal_return, ret; + JabberPresence presence; + xmlnode *child; - from = xmlnode_get_attrib(packet, "from"); + memset(&presence, 0, sizeof(presence)); + /* defaults */ + presence.state = JABBER_BUDDY_STATE_UNKNOWN; + presence.sent = time(NULL); + /* interesting values */ + presence.from = xmlnode_get_attrib(packet, "from"); + presence.to = xmlnode_get_attrib(packet, "to"); type = xmlnode_get_attrib(packet, "type"); - - jb = jabber_buddy_find(js, from, TRUE); - g_return_if_fail(jb != NULL); + presence.type = str_to_presence_type(type); - signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc), - "jabber-receiving-presence", js->gc, type, from, packet)); - if (signal_return) - return; + presence.jb = jabber_buddy_find(js, presence.from, TRUE); + g_return_if_fail(presence.jb != NULL); - account = purple_connection_get_account(js->gc); - - jid = jabber_id_new(from); - if (jid == NULL) { + presence.jid_from = jabber_id_new(presence.from); + if (presence.jid_from == NULL) { purple_debug_error("jabber", "Ignoring presence with malformed 'from' " - "JID: %s\n", from); + "JID: %s\n", presence.from); return; } - if(jb->error_msg) { - g_free(jb->error_msg); - jb->error_msg = NULL; + signal_return = GPOINTER_TO_INT(purple_signal_emit_return_1(purple_connection_get_prpl(js->gc), + "jabber-receiving-presence", js->gc, type, presence.from, packet)); + if (signal_return) { + goto out; + } + + presence.chat = jabber_chat_find(js, presence.jid_from->node, + presence.jid_from->domain); + if(presence.jb->error_msg) { + g_free(presence.jb->error_msg); + presence.jb->error_msg = NULL; } - if (type == NULL) { - xmlnode *show; - char *show_data = NULL; - - state = JABBER_BUDDY_STATE_ONLINE; - - show = xmlnode_get_child(packet, "show"); - if (show) { - show_data = xmlnode_get_data(show); - if (show_data) { - state = jabber_buddy_show_get_state(show_data); - g_free(show_data); - } else - purple_debug_warning("jabber", "<show/> present on presence, " - "but no contents!\n"); - } - } else if (g_str_equal(type, "error")) { + if (presence.type == JABBER_PRESENCE_AVAILABLE) { + presence.state = JABBER_BUDDY_STATE_ONLINE; + } else if (presence.type == JABBER_PRESENCE_ERROR) { + /* TODO: Is this handled properly? Should it be treated as per-jbr? */ char *msg = jabber_parse_error(js, packet, NULL); - - state = JABBER_BUDDY_STATE_ERROR; - jb->error_msg = msg ? msg : g_strdup(_("Unknown Error in presence")); - } else if (g_str_equal(type, "subscribe")) { + presence.state = JABBER_BUDDY_STATE_ERROR; + presence.jb->error_msg = msg ? msg : g_strdup(_("Unknown Error in presence")); + } else if (presence.type == JABBER_PRESENCE_SUBSCRIBE) { + /* TODO: Move to handle_subscribe() (so nick is extracted by the + * PresenceHandler */ struct _jabber_add_permit *jap = g_new0(struct _jabber_add_permit, 1); gboolean onlist = FALSE; + PurpleAccount *account; PurpleBuddy *buddy; - JabberBuddy *jb = NULL; xmlnode *nick; - buddy = purple_find_buddy(account, from); + account = purple_connection_get_account(js->gc); + buddy = purple_find_buddy(account, presence.from); nick = xmlnode_get_child_with_namespace(packet, "nick", "http://jabber.org/protocol/nick"); if (nick) - nickname = xmlnode_get_data(nick); + presence.nickname = xmlnode_get_data(nick); if (buddy) { - jb = jabber_buddy_find(js, from, TRUE); - if ((jb->subscription & (JABBER_SUB_TO | JABBER_SUB_PENDING))) + if ((presence.jb->subscription & (JABBER_SUB_TO | JABBER_SUB_PENDING))) onlist = TRUE; } jap->gc = js->gc; - jap->who = g_strdup(from); + jap->who = g_strdup(presence.from); jap->js = js; - purple_account_request_authorization(account, from, NULL, nickname, + purple_account_request_authorization(account, presence.from, NULL, presence.nickname, NULL, onlist, authorize_add_cb, deny_add_cb, jap); - g_free(nickname); - jabber_id_free(jid); - return; - } else if (g_str_equal(type, "subscribed")) { - /* we've been allowed to see their presence, but we don't care */ - jabber_id_free(jid); - return; - } else if (g_str_equal(type, "unsubscribe")) { + goto out; + } else if (presence.type == JABBER_PRESENCE_SUBSCRIBED) { + /* This case (someone has approved our subscribe request) is handled + * by the roster push the server sends along with this. + */ + goto out; + } else if (presence.type == JABBER_PRESENCE_UNSUBSCRIBE) { /* XXX I'm not sure this is the right way to handle this, it * might be better to add "unsubscribe" to the presence status * if lower down, but I'm not sure. */ /* they are unsubscribing from our presence, we don't care */ /* Well, maybe just a little, we might want/need to start * acknowledging this (and the others) at some point. */ - jabber_id_free(jid); - return; - } else if (g_str_equal(type, "probe")) { + goto out; + } else if (presence.type == JABBER_PRESENCE_PROBE) { purple_debug_warning("jabber", "Ignoring presence probe\n"); - jabber_id_free(jid); - return; - } else if (g_str_equal(type, "unavailable")) { - state = JABBER_BUDDY_STATE_UNAVAILABLE; - } else if (g_str_equal(type, "unsubscribed")) { - state = JABBER_BUDDY_STATE_UNKNOWN; + goto out; + } else if (presence.type == JABBER_PRESENCE_UNAVAILABLE) { + presence.state = JABBER_BUDDY_STATE_UNAVAILABLE; + } else if (presence.type == JABBER_PRESENCE_UNSUBSCRIBED) { + presence.state = JABBER_BUDDY_STATE_UNKNOWN; } else { purple_debug_warning("jabber", "Ignoring presence with invalid type " "'%s'\n", type); - jabber_id_free(jid); - return; + goto out; } - - for(y = packet->child; y; y = y->next) { - const char *xmlns; - if(y->type != XMLNODE_TYPE_TAG) - continue; - xmlns = xmlnode_get_namespace(y); - - if(!strcmp(y->name, "status")) { - g_free(status); - status = xmlnode_get_data(y); - } else if(!strcmp(y->name, "priority")) { - char *p = xmlnode_get_data(y); - if(p) { - priority = atoi(p); - g_free(p); - } - } else if(xmlns == NULL) { - /* The rest of the cases used to check xmlns individually. */ + for (child = packet->child; child; child = child->next) { + char *key; + JabberPresenceHandler *pih; + if (child->type != XMLNODE_TYPE_TAG) continue; - } else if(!strcmp(y->name, "delay") && !strcmp(xmlns, NS_DELAYED_DELIVERY)) { - /* XXX: compare the time. jabber:x:delay can happen on presence packets that aren't really and truly delayed */ - delayed = TRUE; - stamp = xmlnode_get_attrib(y, "stamp"); - } else if(!strcmp(y->name, "c") && !strcmp(xmlns, "http://jabber.org/protocol/caps")) { - caps = y; /* store for later, when creating buddy resource */ - } else if (g_str_equal(y->name, "nick") && g_str_equal(xmlns, "http://jabber.org/protocol/nick")) { - nickname = xmlnode_get_data(y); - } else if(!strcmp(y->name, "x")) { - if(!strcmp(xmlns, NS_DELAYED_DELIVERY_LEGACY)) { - /* XXX: compare the time. jabber:x:delay can happen on presence packets that aren't really and truly delayed */ - delayed = TRUE; - stamp = xmlnode_get_attrib(y, "stamp"); - } else if(!strcmp(xmlns, "http://jabber.org/protocol/muc#user")) { - } else if(!strcmp(xmlns, "vcard-temp:x:update")) { - xmlnode *photo = xmlnode_get_child(y, "photo"); - if(photo) { - g_free(avatar_hash); - avatar_hash = xmlnode_get_data(photo); - } - } - } else if (!strcmp(y->name, "query") && - !strcmp(xmlnode_get_namespace(y), NS_LAST_ACTIVITY)) { - /* resource has specified idle */ - const gchar *seconds = xmlnode_get_attrib(y, "seconds"); - if (seconds) { - /* we may need to take "delayed" into account here */ - idle = atoi(seconds); - } - } - } - - if (idle && delayed && stamp) { - /* if we have a delayed presence, we need to add the delay to the idle - value */ - time_t offset = time(NULL) - purple_str_to_time(stamp, TRUE, NULL, NULL, - NULL); - purple_debug_info("jabber", "got delay %s yielding %ld s offset\n", - stamp, offset); - idle += offset; + + key = g_strdup_printf("%s %s", child->name, xmlnode_get_namespace(child)); + pih = g_hash_table_lookup(presence_handlers, key); + g_free(key); + if (pih) + pih(js, &presence, child); } - /* DEALING WITH CHATS */ - if(jid->node && (chat = jabber_chat_find(js, jid->node, jid->domain))) { - static int i = 1; - - if(state == JABBER_BUDDY_STATE_ERROR) { - char *title, *msg = jabber_parse_error(js, packet, NULL); - - if (!chat->conv) { - title = g_strdup_printf(_("Error joining chat %s"), from); - purple_serv_got_join_chat_failed(js->gc, chat->components); - } else { - title = g_strdup_printf(_("Error in chat %s"), from); - if (g_hash_table_size(chat->members) == 0) - serv_got_chat_left(js->gc, chat->id); - } - purple_notify_error(js->gc, title, title, msg); - g_free(title); - g_free(msg); - - if (g_hash_table_size(chat->members) == 0) - /* Only destroy the chat if the error happened while joining */ - jabber_chat_destroy(chat); - jabber_id_free(jid); - g_free(status); - g_free(avatar_hash); - g_free(nickname); - return; - } - - if (type == NULL) { - xmlnode *x; - const char *real_jid = NULL; - const char *affiliation = NULL; - const char *role = NULL; - gboolean is_our_resource = FALSE; /* Is the presence about us? */ - - /* - * XEP-0045 mandates the presence to include a resource (which is - * treated as the chat nick). Some non-compliant servers allow - * joining without a nick. - */ - if (!jid->resource) { - jabber_id_free(jid); - g_free(avatar_hash); - g_free(nickname); - g_free(status); - return; - } - - x = xmlnode_get_child_with_namespace(packet, "x", - "http://jabber.org/protocol/muc#user"); - if (x) { - xmlnode *status_node; - xmlnode *item_node; - - for (status_node = xmlnode_get_child(x, "status"); status_node; - status_node = xmlnode_get_next_twin(status_node)) { - const char *code = xmlnode_get_attrib(status_node, "code"); - if (!code) - continue; - - if (g_str_equal(code, "110")) { - is_our_resource = TRUE; - } else if (g_str_equal(code, "201")) { - if ((chat = jabber_chat_find(js, jid->node, jid->domain))) { - chat->config_dialog_type = PURPLE_REQUEST_ACTION; - chat->config_dialog_handle = - purple_request_action(js->gc, - _("Create New Room"), - _("Create New Room"), - _("You are creating a new room. Would" - " you like to configure it, or" - " accept the default settings?"), - /* Default Action */ 1, - account, NULL, chat->conv, - chat, 2, - _("_Configure Room"), G_CALLBACK(jabber_chat_request_room_configure), - _("_Accept Defaults"), G_CALLBACK(jabber_chat_create_instant_room)); - } - } else if (g_str_equal(code, "210")) { - /* server rewrote room-nick */ - if((chat = jabber_chat_find(js, jid->node, jid->domain))) { - g_free(chat->handle); - chat->handle = g_strdup(jid->resource); - } - } - } - - item_node = xmlnode_get_child(x, "item"); - if (item_node) { - real_jid = xmlnode_get_attrib(item_node, "jid"); - affiliation = xmlnode_get_attrib(item_node, "affiliation"); - role = xmlnode_get_attrib(item_node, "role"); - - if (purple_strequal(affiliation, "owner")) - flags |= PURPLE_CBFLAGS_FOUNDER; - if (role) { - if (g_str_equal(role, "moderator")) - flags |= PURPLE_CBFLAGS_OP; - else if (g_str_equal(role, "participant")) - flags |= PURPLE_CBFLAGS_VOICE; - } - } - } - - if(!chat->conv) { - char *room_jid = g_strdup_printf("%s@%s", jid->node, jid->domain); - chat->id = i++; - chat->muc = (x != NULL); - chat->conv = serv_got_joined_chat(js->gc, chat->id, room_jid); - purple_conv_chat_set_nick(PURPLE_CONV_CHAT(chat->conv), chat->handle); - - jabber_chat_disco_traffic(chat); - g_free(room_jid); - } - - jbr = jabber_buddy_track_resource(jb, jid->resource, priority, state, - status); - jbr->commands_fetched = TRUE; - - jabber_chat_track_handle(chat, jid->resource, real_jid, affiliation, role); - - if(!jabber_chat_find_buddy(chat->conv, jid->resource)) - purple_conv_chat_add_user(PURPLE_CONV_CHAT(chat->conv), jid->resource, - real_jid, flags, !delayed); - else - purple_conv_chat_user_set_flags(PURPLE_CONV_CHAT(chat->conv), jid->resource, - flags); - } else if (g_str_equal(type, "unavailable")) { - xmlnode *x; - gboolean nick_change = FALSE; - gboolean kick = FALSE; - gboolean is_our_resource = FALSE; /* Is the presence about us? */ - - /* If the chat nick is invalid, we haven't yet joined, or we've - * already left (it was probably us leaving after we closed the - * chat), we don't care. - */ - if (!jid->resource || !chat->conv || chat->left) { - if (chat->left && - jid->resource && chat->handle && !strcmp(jid->resource, chat->handle)) - jabber_chat_destroy(chat); - jabber_id_free(jid); - g_free(status); - g_free(avatar_hash); - g_free(nickname); - return; - } - - is_our_resource = (0 == g_utf8_collate(jid->resource, chat->handle)); - - jabber_buddy_remove_resource(jb, jid->resource); - - x = xmlnode_get_child_with_namespace(packet, "x", - "http://jabber.org/protocol/muc#user"); - if (chat->muc && x) { - const char *nick; - const char *item_jid = NULL; - const char *to; - xmlnode *stat; - xmlnode *item; - - item = xmlnode_get_child(x, "item"); - if (item) - item_jid = xmlnode_get_attrib(item, "jid"); - - for (stat = xmlnode_get_child(x, "status"); stat; - stat = xmlnode_get_next_twin(stat)) { - const char *code = xmlnode_get_attrib(stat, "code"); - - if (!code) - continue; - - if (g_str_equal(code, "110")) { - is_our_resource = TRUE; - } else if(!strcmp(code, "301")) { - /* XXX: we got banned */ - } else if(!strcmp(code, "303") && item && - (nick = xmlnode_get_attrib(item, "nick"))) { - nick_change = TRUE; - if(!strcmp(jid->resource, chat->handle)) { - g_free(chat->handle); - chat->handle = g_strdup(nick); - } - - /* TODO: This should probably be moved out of the loop */ - purple_conv_chat_rename_user(PURPLE_CONV_CHAT(chat->conv), jid->resource, nick); - jabber_chat_remove_handle(chat, jid->resource); - continue; - } else if(!strcmp(code, "307")) { - /* Someone was kicked from the room */ - xmlnode *reason = NULL, *actor = NULL; - const char *actor_name = NULL; - char *reason_text = NULL; - char *tmp; - - kick = TRUE; - - if (item) { - reason = xmlnode_get_child(item, "reason"); - actor = xmlnode_get_child(item, "actor"); - - if (reason != NULL) - reason_text = xmlnode_get_data(reason); - if (actor != NULL) - actor_name = xmlnode_get_attrib(actor, "jid"); - } - - if (reason_text == NULL) - reason_text = g_strdup(_("No reason")); - - if (is_our_resource) { - if (actor_name != NULL) - tmp = g_strdup_printf(_("You have been kicked by %s: (%s)"), - actor_name, reason_text); - else - tmp = g_strdup_printf(_("You have been kicked: (%s)"), - reason_text); - } else { - if (actor_name != NULL) - tmp = g_strdup_printf(_("Kicked by %s (%s)"), - actor_name, reason_text); - else - tmp = g_strdup_printf(_("Kicked (%s)"), - reason_text); - } - - g_free(reason_text); - g_free(status); - status = tmp; - } else if(!strcmp(code, "321")) { - /* XXX: removed due to an affiliation change */ - } else if(!strcmp(code, "322")) { - /* XXX: removed because room is now members-only */ - } else if(!strcmp(code, "332")) { - /* XXX: removed due to system shutdown */ - } - } - - /* - * Possibly another connected resource of our JID (see XEP-0045 - * v1.24 section 7.1.10) being disconnected. Should be - * distinguished by the item_jid. - * Also possibly works around bits of an Openfire bug. See - * #8319. - */ - to = xmlnode_get_attrib(packet, "to"); - if (is_our_resource && item_jid && !purple_strequal(to, item_jid)) { - /* TODO: When the above is a loop, this needs to still act - * sanely for all cases (this code is a little fragile). */ - if (!kick && !nick_change) - /* Presumably, kicks and nick changes also affect us. */ - is_our_resource = FALSE; - } - } - if(!nick_change) { - if (is_our_resource) { - if (kick) - purple_conv_chat_write(PURPLE_CONV_CHAT(chat->conv), jid->resource, - status, PURPLE_MESSAGE_SYSTEM, time(NULL)); - - serv_got_chat_left(js->gc, chat->id); - jabber_chat_destroy(chat); - } else { - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(chat->conv), jid->resource, - status); - jabber_chat_remove_handle(chat, jid->resource); - } - } - } else { - /* A type that isn't available or unavailable */ - purple_debug_error("jabber", "MUC presence with bad type: %s\n", - type); - - jabber_id_free(jid); - g_free(avatar_hash); - g_free(status); - g_free(nickname); - g_return_if_reached(); - } - /* End of DEALING WITH CHATS...about 5000 lines ago */ - } else { - /* DEALING WITH CONTACT (i.e. not a chat) */ - PurpleConversation *conv; - - buddy_name = g_strdup_printf("%s%s%s", jid->node ? jid->node : "", - jid->node ? "@" : "", jid->domain); - - /* - * Unbind/unlock from sending messages to a specific resource on - * presence changes. This is locked to a specific resource when - * receiving a message (in message.c). - */ - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, - buddy_name, account); - if (conv) { - purple_debug_info("jabber", "Changed conversation binding from %s to %s\n", - purple_conversation_get_name(conv), buddy_name); - purple_conversation_set_name(conv, buddy_name); - } - - if((b = purple_find_buddy(account, buddy_name)) == NULL) { - if (jb != js->user_jb) { - purple_debug_warning("jabber", "Got presence for unknown buddy %s on account %s (%p)\n", - buddy_name, purple_account_get_username(account), account); - jabber_id_free(jid); - g_free(avatar_hash); - g_free(buddy_name); - g_free(nickname); - g_free(status); - return; - } else { - /* this is a different resource of our own account. Resume even when this account isn't on our blist */ - } - } - - if(b && avatar_hash) { - const char *avatar_hash2 = purple_buddy_icons_get_checksum_for_user(b); - if(!avatar_hash2 || strcmp(avatar_hash, avatar_hash2)) { - JabberIq *iq; - xmlnode *vcard; - - /* XXX this is a crappy way of trying to prevent - * someone from spamming us with presence packets - * and causing us to DoS ourselves...what we really - * need is a queue system that can throttle itself, - * but i'm too tired to write that right now */ - if(!g_slist_find(js->pending_avatar_requests, jb)) { - - js->pending_avatar_requests = g_slist_prepend(js->pending_avatar_requests, jb); - - iq = jabber_iq_new(js, JABBER_IQ_GET); - xmlnode_set_attrib(iq->node, "to", buddy_name); - vcard = xmlnode_new_child(iq->node, "vCard"); - xmlnode_set_namespace(vcard, "vcard-temp"); - - jabber_iq_set_callback(iq, jabber_vcard_parse_avatar, NULL); - jabber_iq_send(iq); - } - } - } - - if(state == JABBER_BUDDY_STATE_ERROR || - (type && (g_str_equal(type, "unavailable") || - g_str_equal(type, "unsubscribed")))) { - jabber_buddy_remove_resource(jb, jid->resource); - } else { - jbr = jabber_buddy_track_resource(jb, jid->resource, priority, - state, status); - if (idle) { - jbr->idle = time(NULL) - idle; - } else { - jbr->idle = 0; - } - } - - if((found_jbr = jabber_buddy_find_resource(jb, NULL))) { - jabber_google_presence_incoming(js, buddy_name, found_jbr); - purple_prpl_got_user_status(account, buddy_name, jabber_buddy_state_get_status_id(found_jbr->state), "priority", found_jbr->priority, "message", found_jbr->status, NULL); - purple_prpl_got_user_idle(account, buddy_name, found_jbr->idle, found_jbr->idle); - if (nickname) - serv_got_alias(js->gc, buddy_name, nickname); - } else { - purple_prpl_got_user_status(account, buddy_name, "offline", status ? "message" : NULL, status, NULL); - } - g_free(buddy_name); + if (presence.delayed && presence.idle) { + /* Delayed and idle, so update idle time */ + presence.idle = presence.idle + (time(NULL) - presence.sent); } - if (caps && !type) { + /* TODO: Handle tracking jb(r) here? */ + + if (presence.chat) + ret = handle_presence_chat(js, &presence, packet); + else + ret = handle_presence_contact(js, &presence); + if (!ret) + goto out; + + if (presence.caps && presence.type == JABBER_PRESENCE_AVAILABLE) { /* handle Entity Capabilities (XEP-0115) */ - const char *node = xmlnode_get_attrib(caps, "node"); - const char *ver = xmlnode_get_attrib(caps, "ver"); - const char *hash = xmlnode_get_attrib(caps, "hash"); - const char *ext = xmlnode_get_attrib(caps, "ext"); + const char *node = xmlnode_get_attrib(presence.caps, "node"); + const char *ver = xmlnode_get_attrib(presence.caps, "ver"); + const char *hash = xmlnode_get_attrib(presence.caps, "hash"); + const char *ext = xmlnode_get_attrib(presence.caps, "ext"); /* v1.3 uses: node, ver, and optionally ext. * v1.5 uses: node, ver, and hash. */ if (node && *node && ver && *ver) { gchar **exts = ext && *ext ? g_strsplit(ext, " ", -1) : NULL; - jbr = jabber_buddy_find_resource(jb, jid->resource); + jbr = jabber_buddy_find_resource(presence.jb, presence.jid_from->resource); /* Look it up if we don't already have all this information */ if (!jbr || !jbr->caps.info || @@ -1082,9 +1039,9 @@ !jabber_caps_exts_known(jbr->caps.info, (gchar **)exts)) { JabberPresenceCapabilities *userdata = g_new0(JabberPresenceCapabilities, 1); userdata->js = js; - userdata->jb = jb; - userdata->from = g_strdup(from); - jabber_caps_get_info(js, from, node, ver, hash, exts, + userdata->jb = presence.jb; + userdata->from = g_strdup(presence.from); + jabber_caps_get_info(js, presence.from, node, ver, hash, exts, (jabber_caps_get_info_cb)jabber_presence_set_capabilities, userdata); } else { @@ -1094,10 +1051,12 @@ } } - g_free(nickname); - g_free(status); - jabber_id_free(jid); - g_free(avatar_hash); +out: + g_free(presence.nickname); + g_free(presence.status); + jabber_id_free(presence.jid_from); + g_free(presence.nickname); + g_free(presence.vcard_avatar_hash); } void jabber_presence_subscription_set(JabberStream *js, const char *who, const char *type) @@ -1140,3 +1099,170 @@ *priority = purple_status_get_attr_int(status, "priority"); } } + +/* Incoming presence handlers */ +static void +parse_priority(JabberStream *js, JabberPresence *presence, xmlnode *priority) +{ + char *p = xmlnode_get_data(priority); + + if (presence->priority != 0) + purple_debug_warning("jabber", "presence stanza received with multiple " + "priority children!?\n"); + + if (p) { + presence->priority = atoi(p); + g_free(p); + } else + purple_debug_warning("jabber", "Empty <priority/> in presence!\n"); +} + +static void +parse_show(JabberStream *js, JabberPresence *presence, xmlnode *show) +{ + char *cdata; + + if (presence->type != JABBER_PRESENCE_AVAILABLE) { + purple_debug_warning("jabber", "<show/> present on presence, but " + "type is not default ('available')\n"); + return; + } + + cdata = xmlnode_get_data(show); + if (cdata) { + presence->state = jabber_buddy_show_get_state(cdata); + g_free(cdata); + } else + purple_debug_warning("jabber", "<show/> present on presence, but " + "no contents!\n"); +} + +static void +parse_status(JabberStream *js, JabberPresence *presence, xmlnode *status) +{ + /* TODO: Check/track language attribute? */ + + g_free(presence->status); + presence->status = xmlnode_get_data(status); +} + +static void +parse_delay(JabberStream *js, JabberPresence *presence, xmlnode *delay) +{ + /* XXX: compare the time. Can happen on presence stanzas that aren't + * actually delayed. + */ + const char *stamp = xmlnode_get_attrib(delay, "stamp"); + presence->delayed = TRUE; + presence->sent = purple_str_to_time(stamp, TRUE, NULL, NULL, NULL); +} + +static void +parse_idle(JabberStream *js, JabberPresence *presence, xmlnode *query) +{ + const gchar *seconds = xmlnode_get_attrib(query, "seconds"); + if (seconds) { + presence->idle = atoi(seconds); + if (presence->idle < 0) { + purple_debug_warning("jabber", "Received bogus idle time %s\n", seconds); + presence->idle = 0; + } + } +} + +static void +parse_caps(JabberStream *js, JabberPresence *presence, xmlnode *c) +{ + /* TODO: Move the rest of the caps handling in here, after changing the + * the "do we have details about this (node, ver) and exts" to not + * require the jbr to be present (since that happens later). + */ + presence->caps = c; +} + +static void +parse_nickname(JabberStream *js, JabberPresence *presence, xmlnode *nick) +{ + g_free(presence->nickname); + presence->nickname = xmlnode_get_data(nick); +} + +static void +parse_vcard_avatar(JabberStream *js, JabberPresence *presence, xmlnode *x) +{ + xmlnode *photo = xmlnode_get_child(x, "photo"); + if (photo) { + g_free(presence->vcard_avatar_hash); + presence->vcard_avatar_hash = xmlnode_get_data(photo); + } +} + +static void +parse_muc_user(JabberStream *js, JabberPresence *presence, xmlnode *x) +{ + xmlnode *status; + + if (presence->chat == NULL) { + purple_debug_warning("jabber", "Ignoring MUC gloop on non-MUC presence\n"); + return; + } + + if (presence->chat->conv == NULL) + presence->chat->muc = TRUE; + + for (status = xmlnode_get_child(x, "status"); status; + status = xmlnode_get_next_twin(status)) { + const char *code = xmlnode_get_attrib(status, "code"); + int val; + if (!code) + continue; + + val = atoi(code); + if (val == 0 || val < 0) { + purple_debug_warning("jabber", "Ignoring bogus status code '%s'\n", + code); + continue; + } + + presence->chat_info.codes = g_slist_prepend(presence->chat_info.codes, GINT_TO_POINTER(val)); + } + + presence->chat_info.item = xmlnode_get_child(x, "item"); +} + +void jabber_presence_register_handler(const char *node, const char *xmlns, + JabberPresenceHandler *handler) +{ + /* + * This is valid because nodes nor namespaces cannot have spaces in them + * (see http://www.w3.org/TR/2006/REC-xml-20060816/ and + * http://www.w3.org/TR/REC-xml-names/) + */ + char *key = g_strdup_printf("%s %s", node, xmlns); + g_hash_table_replace(presence_handlers, key, handler); +} + +void jabber_presence_init(void) +{ + presence_handlers = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + + /* Core RFC things */ + jabber_presence_register_handler("priority", "jabber:client", parse_priority); + jabber_presence_register_handler("show", "jabber:client", parse_show); + jabber_presence_register_handler("status", "jabber:client", parse_status); + + /* XEPs */ + jabber_presence_register_handler("c", "http://jabber.org/protocol/caps", parse_caps); + jabber_presence_register_handler("delay", NS_DELAYED_DELIVERY, parse_delay); + jabber_presence_register_handler("nick", "http://jabber.org/protocol/nick", parse_nickname); + jabber_presence_register_handler("query", NS_LAST_ACTIVITY, parse_idle); + jabber_presence_register_handler("x", NS_DELAYED_DELIVERY_LEGACY, parse_delay); + jabber_presence_register_handler("x", "http://jabber.org/protocol/muc#user", parse_muc_user); + jabber_presence_register_handler("x", "vcard-temp:x:update", parse_vcard_avatar); +} + +void jabber_presence_uninit(void) +{ + g_hash_table_destroy(presence_handlers); + presence_handlers = NULL; +}
--- a/libpurple/protocols/jabber/presence.h Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/jabber/presence.h Tue Mar 16 12:07:06 2010 +0900 @@ -24,10 +24,63 @@ #ifndef PURPLE_JABBER_PRESENCE_H_ #define PURPLE_JABBER_PRESENCE_H_ +typedef enum { + JABBER_PRESENCE_ERROR = -2, + JABBER_PRESENCE_PROBE = -1, + JABBER_PRESENCE_AVAILABLE, + JABBER_PRESENCE_UNAVAILABLE, + JABBER_PRESENCE_SUBSCRIBE, + JABBER_PRESENCE_SUBSCRIBED, + JABBER_PRESENCE_UNSUBSCRIBE, + JABBER_PRESENCE_UNSUBSCRIBED +} JabberPresenceType; + +typedef struct _JabberPresenceChatInfo JabberPresenceChatInfo; +typedef struct _JabberPresence JabberPresence; + #include "buddy.h" +#include "chat.h" #include "jabber.h" +#include "jutil.h" #include "xmlnode.h" +struct _JabberPresenceChatInfo { + GSList *codes; + xmlnode *item; +}; + +struct _JabberPresence { + JabberPresenceType type; + JabberID *jid_from; + const char *from; + const char *to; + const char *id; + + JabberBuddy *jb; + JabberChat *chat; + JabberPresenceChatInfo chat_info; + xmlnode *caps; /* TODO: Temporary, see presence.c:parse_caps */ + + JabberBuddyState state; + gchar *status; + int priority; + + char *vcard_avatar_hash; + char *nickname; + + gboolean delayed; + time_t sent; + int idle; +}; + +typedef void (JabberPresenceHandler)(JabberStream *js, JabberPresence *presence, + xmlnode *child); +void jabber_presence_register_handler(const char *node, const char *xmlns, + JabberPresenceHandler *handler); + +void jabber_presence_init(void); +void jabber_presence_uninit(void); + void jabber_set_status(PurpleAccount *account, PurpleStatus *status); /**
--- a/libpurple/protocols/jabber/usermood.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/jabber/usermood.c Tue Mar 16 12:07:06 2010 +0900 @@ -170,68 +170,6 @@ jabber_pep_register_handler("http://jabber.org/protocol/mood", jabber_mood_cb); } -static void do_mood_set_from_fields(PurpleConnection *gc, PurpleRequestFields *fields) { - JabberStream *js; - const int max_mood_idx = sizeof(moods) / sizeof(moods[0]) - 1; - int selected_mood = purple_request_fields_get_choice(fields, "mood"); - - if (!PURPLE_CONNECTION_IS_VALID(gc)) { - purple_debug_error("jabber", "Unable to set mood; account offline.\n"); - return; - } - - js = gc->proto_data; - - if (selected_mood < 0 || selected_mood >= max_mood_idx) { - purple_debug_error("jabber", "Invalid mood index (%d) selected.\n", selected_mood); - return; - } - - jabber_mood_set(js, moods[selected_mood].mood, purple_request_fields_get_string(fields, "text")); -} - -static void do_mood_set_mood(PurplePluginAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; - - PurpleRequestFields *fields; - PurpleRequestFieldGroup *group; - PurpleRequestField *field; - int i; - - 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("mood", - _("Mood"), 0); - - for(i = 0; moods[i].mood; ++i) - purple_request_field_choice_add(field, _(moods[i].description)); - - purple_request_field_set_required(field, TRUE); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_string_new("text", - _("Description"), NULL, - FALSE); - purple_request_field_group_add_field(group, field); - - purple_request_fields(gc, _("Edit User Mood"), - _("Edit User Mood"), - _("Please select your mood from the list."), - fields, - _("Set"), G_CALLBACK(do_mood_set_from_fields), - _("Cancel"), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); - -} - -void jabber_mood_init_action(GList **m) { - PurplePluginAction *act = purple_plugin_action_new(_("Set Mood..."), do_mood_set_mood); - *m = g_list_append(*m, act); -} - void jabber_mood_set(JabberStream *js, const char *mood, const char *text) { xmlnode *publish, *moodnode; @@ -256,14 +194,5 @@ PurpleMood *jabber_get_moods(PurpleAccount *account) { - PurpleConnection *gc = purple_account_get_connection(account); - JabberStream *js = (JabberStream *) gc->proto_data; - - if (js->pep) { - purple_debug_info("jabber", "get_moods: account supports PEP\n"); - return moods; - } else { - purple_debug_info("jabber", "get_moods: account doesn't support PEP\n"); - return NULL; - } + return moods; } \ No newline at end of file
--- a/libpurple/protocols/jabber/usermood.h Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/jabber/usermood.h Tue Mar 16 12:07:06 2010 +0900 @@ -30,8 +30,6 @@ void jabber_mood_init(void); -void jabber_mood_init_action(GList **m); - void jabber_mood_set(JabberStream *js, const char *mood, /* must be one of the valid strings defined in the XEP */ const char *text /* might be NULL */);
--- a/libpurple/protocols/msnp9/Makefile.am Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,90 +0,0 @@ -EXTRA_DIST = \ - Makefile.mingw - -pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) - -MSNP9SOURCES = \ - cmdproc.c \ - cmdproc.h \ - command.c \ - command.h \ - dialog.c \ - dialog.h \ - directconn.c \ - directconn.h \ - error.c \ - error.h \ - group.c \ - group.h \ - history.c \ - history.h \ - httpconn.c \ - httpconn.h \ - msg.c \ - msg.h \ - msn.c \ - msn.h \ - nexus.c \ - nexus.h \ - notification.c \ - notification.h \ - object.c \ - object.h \ - page.c \ - page.h \ - servconn.c \ - servconn.h \ - session.c \ - session.h \ - slp.c \ - slp.h \ - slpcall.c \ - slpcall.h \ - slplink.c \ - slplink.h \ - slpmsg.c \ - slpmsg.h \ - slpsession.c \ - slpsession.h \ - state.c \ - state.h \ - switchboard.c \ - switchboard.h \ - sync.c \ - sync.h \ - table.c \ - table.h \ - transaction.c \ - transaction.h \ - user.c \ - user.h \ - userlist.c \ - userlist.h \ - msn-utils.c \ - msn-utils.h - -AM_CFLAGS = $(st) - -libmsn_la_LDFLAGS = -module -avoid-version - -if STATIC_MSN - -st = -DPURPLE_STATIC_PRPL -noinst_LTLIBRARIES = libmsn.la -libmsn_la_SOURCES = $(MSNP9SOURCES) -libmsn_la_CFLAGS = $(AM_CFLAGS) - -else - -st = -pkg_LTLIBRARIES = libmsn.la -libmsn_la_SOURCES = $(MSNP9SOURCES) -libmsn_la_LIBADD = $(GLIB_LIBS) - -endif - -AM_CPPFLAGS = \ - -I$(top_srcdir)/libpurple \ - -I$(top_builddir)/libpurple \ - $(GLIB_CFLAGS) \ - $(DEBUG_CFLAGS)
--- a/libpurple/protocols/msnp9/Makefile.mingw Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,105 +0,0 @@ -# -# Makefile.mingw -# -# Description: Makefile for win32 (mingw) version of libmsn -# - -PIDGIN_TREE_TOP := ../../.. -include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak - -TARGET = libmsn -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 = cmdproc.c \ - command.c \ - dialog.c \ - directconn.c \ - error.c \ - group.c \ - history.c \ - httpconn.c \ - msg.c \ - msn.c \ - nexus.c \ - notification.c \ - object.c \ - page.c \ - servconn.c \ - session.c \ - slp.c \ - slpcall.c \ - slplink.c \ - slpmsg.c \ - slpsession.c \ - state.c \ - switchboard.c \ - sync.c \ - table.c \ - transaction.c \ - user.c \ - userlist.c \ - msn-utils.c - -OBJECTS = $(C_SRC:%.c=%.o) - -## -## LIBRARIES -## -LIBS = \ - -lglib-2.0 \ - -lintl \ - -lws2_32 \ - -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)
--- a/libpurple/protocols/msnp9/cmdproc.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,344 +0,0 @@ -/** - * @file cmdproc.c MSN command processor functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "cmdproc.h" - -MsnCmdProc * -msn_cmdproc_new(MsnSession *session) -{ - MsnCmdProc *cmdproc; - - cmdproc = g_new0(MsnCmdProc, 1); - - cmdproc->session = session; - cmdproc->txqueue = g_queue_new(); - cmdproc->history = msn_history_new(); - - return cmdproc; -} - -void -msn_cmdproc_destroy(MsnCmdProc *cmdproc) -{ - MsnTransaction *trans; - - while ((trans = g_queue_pop_head(cmdproc->txqueue)) != NULL) - msn_transaction_destroy(trans); - - g_queue_free(cmdproc->txqueue); - - msn_history_destroy(cmdproc->history); - - if (cmdproc->last_cmd != NULL) - msn_command_destroy(cmdproc->last_cmd); - - g_free(cmdproc); -} - -void -msn_cmdproc_process_queue(MsnCmdProc *cmdproc) -{ - MsnTransaction *trans; - - while ((trans = g_queue_pop_head(cmdproc->txqueue)) != NULL) - msn_cmdproc_send_trans(cmdproc, trans); -} - -void -msn_cmdproc_queue_trans(MsnCmdProc *cmdproc, MsnTransaction *trans) -{ - g_return_if_fail(cmdproc != NULL); - g_return_if_fail(trans != NULL); - - g_queue_push_tail(cmdproc->txqueue, trans); -} - -static void -show_debug_cmd(MsnCmdProc *cmdproc, gboolean incoming, const char *command) -{ - MsnServConn *servconn; - const char *names[] = { "NS", "SB" }; - char *show; - char tmp; - size_t len; - - servconn = cmdproc->servconn; - len = strlen(command); - show = g_strdup(command); - - tmp = (incoming) ? 'S' : 'C'; - - if ((show[len - 1] == '\n') && (show[len - 2] == '\r')) - { - show[len - 2] = '\0'; - } - - purple_debug_misc("msn", "%c: %s %03d: %s\n", tmp, - names[servconn->type], servconn->num, show); - - g_free(show); -} - -void -msn_cmdproc_send_trans(MsnCmdProc *cmdproc, MsnTransaction *trans) -{ - MsnServConn *servconn; - char *data; - size_t len; - - g_return_if_fail(cmdproc != NULL); - g_return_if_fail(trans != NULL); - - servconn = cmdproc->servconn; - - if (!servconn->connected) - return; - - msn_history_add(cmdproc->history, trans); - - data = msn_transaction_to_string(trans); - - len = strlen(data); - - show_debug_cmd(cmdproc, FALSE, data); - - if (trans->callbacks == NULL) - trans->callbacks = g_hash_table_lookup(cmdproc->cbs_table->cmds, - trans->command); - - if (trans->payload != NULL) - { - data = g_realloc(data, len + trans->payload_len); - memcpy(data + len, trans->payload, trans->payload_len); - len += trans->payload_len; - - /* - * We're done with trans->payload. Free it so that the memory - * doesn't sit around in cmdproc->history. - */ - g_free(trans->payload); - trans->payload = NULL; - trans->payload_len = 0; - } - - msn_servconn_write(servconn, data, len); - - g_free(data); -} - -void -msn_cmdproc_send_quick(MsnCmdProc *cmdproc, const char *command, - const char *format, ...) -{ - MsnServConn *servconn; - char *data; - char *params = NULL; - va_list arg; - size_t len; - - g_return_if_fail(cmdproc != NULL); - g_return_if_fail(command != NULL); - - servconn = cmdproc->servconn; - - if (!servconn->connected) - return; - - if (format != NULL) - { - va_start(arg, format); - params = g_strdup_vprintf(format, arg); - va_end(arg); - } - - if (params != NULL) - data = g_strdup_printf("%s %s\r\n", command, params); - else - data = g_strdup_printf("%s\r\n", command); - - g_free(params); - - len = strlen(data); - - show_debug_cmd(cmdproc, FALSE, data); - - msn_servconn_write(servconn, data, len); - - g_free(data); -} - -void -msn_cmdproc_send(MsnCmdProc *cmdproc, const char *command, - const char *format, ...) -{ - MsnTransaction *trans; - va_list arg; - - g_return_if_fail(cmdproc != NULL); - g_return_if_fail(command != NULL); - - if (!cmdproc->servconn->connected) - return; - - trans = g_new0(MsnTransaction, 1); - - trans->command = g_strdup(command); - - if (format != NULL) - { - va_start(arg, format); - trans->params = g_strdup_vprintf(format, arg); - va_end(arg); - } - - msn_cmdproc_send_trans(cmdproc, trans); -} - -void -msn_cmdproc_process_payload(MsnCmdProc *cmdproc, char *payload, - int payload_len) -{ - MsnCommand *last; - - g_return_if_fail(cmdproc != NULL); - - last = cmdproc->last_cmd; - last->payload = g_memdup(payload, payload_len); - last->payload_len = payload_len; - - if (last->payload_cb != NULL) - last->payload_cb(cmdproc, last, payload, payload_len); -} - -void -msn_cmdproc_process_msg(MsnCmdProc *cmdproc, MsnMessage *msg) -{ - MsnMsgTypeCb cb; - - if (msn_message_get_content_type(msg) == NULL) - { - purple_debug_misc("msn", "failed to find message content\n"); - return; - } - - cb = g_hash_table_lookup(cmdproc->cbs_table->msgs, - msn_message_get_content_type(msg)); - - if (cb == NULL) - { - purple_debug_warning("msn", "Unhandled content-type '%s'\n", - msn_message_get_content_type(msg)); - - return; - } - - cb(cmdproc, msg); -} - -void -msn_cmdproc_process_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnTransCb cb = NULL; - MsnTransaction *trans = NULL; - - if (cmd->trId) - trans = msn_history_find(cmdproc->history, cmd->trId); - - if (trans != NULL) - if (trans->timer) - purple_timeout_remove(trans->timer); - - if (g_ascii_isdigit(cmd->command[0])) - { - if (trans != NULL) - { - MsnErrorCb error_cb = NULL; - int error; - - error = atoi(cmd->command); - - if (trans->error_cb != NULL) - error_cb = trans->error_cb; - - if (error_cb == NULL && cmdproc->cbs_table->errors != NULL) - error_cb = g_hash_table_lookup(cmdproc->cbs_table->errors, trans->command); - - if (error_cb != NULL) - { - error_cb(cmdproc, trans, error); - } - else - { -#if 1 - msn_error_handle(cmdproc->session, error); -#else - purple_debug_warning("msn", "Unhandled error '%s'\n", - cmd->command); -#endif - } - - return; - } - } - - if (cmdproc->cbs_table->async != NULL) - cb = g_hash_table_lookup(cmdproc->cbs_table->async, cmd->command); - - if (cb == NULL && trans != NULL) - { - cmd->trans = trans; - - if (trans->callbacks != NULL) - cb = g_hash_table_lookup(trans->callbacks, cmd->command); - } - - if (cb == NULL && cmdproc->cbs_table->fallback != NULL) - cb = g_hash_table_lookup(cmdproc->cbs_table->fallback, cmd->command); - - if (cb != NULL) - { - cb(cmdproc, cmd); - } - else - { - purple_debug_warning("msn", "Unhandled command '%s'\n", - cmd->command); - } - - if (trans != NULL && trans->pendent_cmd != NULL) - msn_transaction_unqueue_cmd(trans, cmdproc); -} - -void -msn_cmdproc_process_cmd_text(MsnCmdProc *cmdproc, const char *command) -{ - show_debug_cmd(cmdproc, TRUE, command); - - if (cmdproc->last_cmd != NULL) - msn_command_destroy(cmdproc->last_cmd); - - cmdproc->last_cmd = msn_command_from_string(command); - - msn_cmdproc_process_cmd(cmdproc, cmdproc->last_cmd); -}
--- a/libpurple/protocols/msnp9/cmdproc.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,74 +0,0 @@ -/** - * @file cmdproc.h MSN command processor functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_CMDPROC_H_ -#define _MSN_CMDPROC_H_ - -typedef struct _MsnCmdProc MsnCmdProc; - -#include "session.h" -#include "servconn.h" -#include "error.h" -#include "command.h" -#include "table.h" -#include "history.h" - -struct _MsnCmdProc -{ - MsnSession *session; - MsnServConn *servconn; - - GQueue *txqueue; - - MsnCommand *last_cmd; - - MsnTable *cbs_table; - - MsnHistory *history; - - void *data; /**< Extra data, like the switchboard. */ -}; - -MsnCmdProc *msn_cmdproc_new(MsnSession *session); -void msn_cmdproc_destroy(MsnCmdProc *cmdproc); - -void msn_cmdproc_process_queue(MsnCmdProc *cmdproc); - -void msn_cmdproc_send_trans(MsnCmdProc *cmdproc, MsnTransaction *trans); -void msn_cmdproc_queue_trans(MsnCmdProc *cmdproc, - MsnTransaction *trans); -void msn_cmdproc_send(MsnCmdProc *cmdproc, const char *command, - const char *format, ...); -void msn_cmdproc_send_quick(MsnCmdProc *cmdproc, const char *command, - const char *format, ...); - -void msn_cmdproc_process_msg(MsnCmdProc *cmdproc, - MsnMessage *msg); -void msn_cmdproc_process_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd); -void msn_cmdproc_process_cmd_text(MsnCmdProc *cmdproc, const char *command); -void msn_cmdproc_process_payload(MsnCmdProc *cmdproc, - char *payload, int payload_len); - -void msn_cmdproc_disconnect(MsnCmdProc *cmdproc); - -#endif /* _MSN_CMDPROC_H_ */
--- a/libpurple/protocols/msnp9/command.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,125 +0,0 @@ -/** - * @file command.c MSN command functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "command.h" - -static gboolean -is_num(char *str) -{ - char *c; - for (c = str; *c; c++) { - if (!(g_ascii_isdigit(*c))) - return FALSE; - } - - return TRUE; -} - -MsnCommand * -msn_command_from_string(const char *string) -{ - MsnCommand *cmd; - char *tmp; - char *param_start; - - g_return_val_if_fail(string != NULL, NULL); - - tmp = g_strdup(string); - param_start = strchr(tmp, ' '); - - cmd = g_new0(MsnCommand, 1); - cmd->command = tmp; - - if (param_start) - { - *param_start++ = '\0'; - cmd->params = g_strsplit(param_start, " ", 0); - } - - if (cmd->params != NULL) - { - int c; - - for (c = 0; cmd->params[c]; c++); - cmd->param_count = c; - - if (cmd->param_count) { - char *param = cmd->params[0]; - cmd->trId = is_num(param) ? atoi(param) : 0; - } else { - cmd->trId = 0; - } - } - else - cmd->trId = 0; - - msn_command_ref(cmd); - - return cmd; -} - -void -msn_command_destroy(MsnCommand *cmd) -{ - g_return_if_fail(cmd != NULL); - - if (cmd->ref_count > 0) - { - msn_command_unref(cmd); - return; - } - - if (cmd->payload != NULL) - g_free(cmd->payload); - - g_free(cmd->command); - g_strfreev(cmd->params); - g_free(cmd); -} - -MsnCommand * -msn_command_ref(MsnCommand *cmd) -{ - g_return_val_if_fail(cmd != NULL, NULL); - - cmd->ref_count++; - return cmd; -} - -MsnCommand * -msn_command_unref(MsnCommand *cmd) -{ - g_return_val_if_fail(cmd != NULL, NULL); - g_return_val_if_fail(cmd->ref_count > 0, NULL); - - cmd->ref_count--; - - if (cmd->ref_count == 0) - { - msn_command_destroy(cmd); - return NULL; - } - - return cmd; -}
--- a/libpurple/protocols/msnp9/command.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,61 +0,0 @@ -/** - * @file command.h MSN command functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_COMMAND_H -#define _MSN_COMMAND_H - -typedef struct _MsnCommand MsnCommand; - -#include "cmdproc.h" -#include "transaction.h" - -typedef void (*MsnPayloadCb)(MsnCmdProc *cmdproc, MsnCommand *cmd, - char *payload, size_t len); - -/** - * A received command. - */ -struct _MsnCommand -{ - unsigned int trId; - - char *command; - char **params; - int param_count; - - int ref_count; - - MsnTransaction *trans; - - char *payload; - size_t payload_len; - - MsnPayloadCb payload_cb; -}; - -MsnCommand *msn_command_from_string(const char *string); -void msn_command_destroy(MsnCommand *cmd); -MsnCommand *msn_command_ref(MsnCommand *cmd); -MsnCommand *msn_command_unref(MsnCommand *cmd); - -#endif /* _MSN_COMMAND_H */
--- a/libpurple/protocols/msnp9/dialog.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,138 +0,0 @@ -/** - * @file dialog.c Dialog functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ - -#include "msn.h" -#include "dialog.h" - -typedef struct -{ - PurpleConnection *gc; - char *who; - char *group; - gboolean add; - -} MsnAddRemData; - -/* Remove the buddy referenced by the MsnAddRemData before the serverside list is changed. - * If the buddy will be added, he'll be added back; if he will be removed, he won't be. */ -static void -msn_complete_sync_issue(MsnAddRemData *data) -{ - PurpleBuddy *buddy; - PurpleGroup *group = NULL; - - if (data->group != NULL) - group = purple_find_group(data->group); - - if (group != NULL) - buddy = purple_find_buddy_in_group(purple_connection_get_account(data->gc), data->who, group); - else - buddy = purple_find_buddy(purple_connection_get_account(data->gc), data->who); - - if (buddy != NULL) - purple_blist_remove_buddy(buddy); -} - -static void -msn_add_cb(MsnAddRemData *data) -{ - MsnSession *session; - MsnUserList *userlist; - - msn_complete_sync_issue(data); - - session = data->gc->proto_data; - userlist = session->userlist; - - msn_userlist_add_buddy(userlist, data->who, MSN_LIST_FL, data->group); - - g_free(data->group); - g_free(data->who); - g_free(data); -} - -static void -msn_rem_cb(MsnAddRemData *data) -{ - MsnSession *session; - MsnUserList *userlist; - - msn_complete_sync_issue(data); - - session = data->gc->proto_data; - userlist = session->userlist; - - msn_userlist_rem_buddy(userlist, data->who, MSN_LIST_FL, data->group); - - g_free(data->group); - g_free(data->who); - g_free(data); -} - -void -msn_show_sync_issue(MsnSession *session, const char *passport, - const char *group_name) -{ - PurpleConnection *gc; - PurpleAccount *account; - MsnAddRemData *data; - char *msg, *reason; - - account = session->account; - gc = purple_account_get_connection(account); - - data = g_new0(MsnAddRemData, 1); - data->who = g_strdup(passport); - data->group = g_strdup(group_name); - data->gc = gc; - - msg = g_strdup_printf(_("Buddy list synchronization issue in %s (%s)"), - purple_account_get_username(account), - purple_account_get_protocol_name(account)); - - if (group_name != NULL) - { - reason = g_strdup_printf(_("%s on the local list is " - "inside the group \"%s\" but not on " - "the server list. " - "Do you want this buddy to be added?"), - passport, group_name); - } - else - { - reason = g_strdup_printf(_("%s is on the local list but " - "not on the server list. " - "Do you want this buddy to be added?"), - passport); - } - - purple_request_action(gc, NULL, msg, reason, PURPLE_DEFAULT_ACTION_NONE, - purple_connection_get_account(gc), data->who, NULL, - data, 2, - _("Yes"), G_CALLBACK(msn_add_cb), - _("No"), G_CALLBACK(msn_rem_cb)); - - g_free(reason); - g_free(msg); -}
--- a/libpurple/protocols/msnp9/dialog.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,30 +0,0 @@ -/** - * @file dialog.h Dialog functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_DIALOG_H_ -#define _MSN_DIALOG_H_ - -void msn_show_sync_issue(MsnSession *session, const char *passport, - const char *group_name); - -#endif /* _MSN_DIALOG_H_ */
--- a/libpurple/protocols/msnp9/directconn.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,505 +0,0 @@ -/** - * @file directconn.c MSN direct connection functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "directconn.h" - -#include "slp.h" -#include "slpmsg.h" - -/************************************************************************** - * Directconn Specific - **************************************************************************/ - -void -msn_directconn_send_handshake(MsnDirectConn *directconn) -{ - MsnSlpLink *slplink; - MsnSlpMessage *slpmsg; - - g_return_if_fail(directconn != NULL); - - slplink = directconn->slplink; - - slpmsg = msn_slpmsg_new(slplink); - slpmsg->flags = 0x100; - - if (directconn->nonce != NULL) - { - guint32 t1; - guint16 t2; - guint16 t3; - guint16 t4; - guint64 t5; - - sscanf (directconn->nonce, "%08X-%04hX-%04hX-%04hX-%012" G_GINT64_MODIFIER "X", &t1, &t2, &t3, &t4, &t5); - - t1 = GUINT32_TO_LE(t1); - t2 = GUINT16_TO_LE(t2); - t3 = GUINT16_TO_LE(t3); - t4 = GUINT16_TO_BE(t4); - t5 = GUINT64_TO_BE(t5); - - slpmsg->ack_id = t1; - slpmsg->ack_sub_id = t2 | (t3 << 16); - slpmsg->ack_size = t4 | t5; - } - - g_free(directconn->nonce); - - msn_slplink_send_slpmsg(slplink, slpmsg); - - directconn->acked =TRUE; -} - -/************************************************************************** - * Connection Functions - **************************************************************************/ - -#if 0 -static int -create_listener(int port) -{ - int fd; - int flags; - const int on = 1; - -#if 0 - struct addrinfo hints; - struct addrinfo *c, *res; - char port_str[5]; - - snprintf(port_str, sizeof(port_str), "%d", port); - - memset(&hints, 0, sizeof(hints)); - - hints.ai_flags = AI_PASSIVE; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - if (getaddrinfo(NULL, port_str, &hints, &res) != 0) - { - purple_debug_error("msn", "Could not get address info: %s.\n", - port_str); - return -1; - } - - for (c = res; c != NULL; c = c->ai_next) - { - fd = socket(c->ai_family, c->ai_socktype, c->ai_protocol); - - if (fd < 0) - continue; - - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - if (bind(fd, c->ai_addr, c->ai_addrlen) == 0) - break; - - close(fd); - } - - if (c == NULL) - { - purple_debug_error("msn", "Could not find socket: %s.\n", port_str); - return -1; - } - - freeaddrinfo(res); -#else - struct sockaddr_in sockin; - - fd = socket(AF_INET, SOCK_STREAM, 0); - - if (fd < 0) - return -1; - - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) - { - close(fd); - return -1; - } - - memset(&sockin, 0, sizeof(struct sockaddr_in)); - sockin.sin_family = AF_INET; - sockin.sin_port = htons(port); - - if (bind(fd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) - { - close(fd); - return -1; - } -#endif - - if (listen (fd, 4) != 0) - { - close (fd); - return -1; - } - - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags | O_NONBLOCK); -#ifndef _WIN32 - fcntl(fd, F_SETFD, FD_CLOEXEC); -#endif - - return fd; -} -#endif - -static size_t -msn_directconn_write(MsnDirectConn *directconn, - const char *data, size_t len) -{ - char *buffer, *tmp; - size_t buf_size; - size_t ret; - guint32 sent_len; - - g_return_val_if_fail(directconn != NULL, 0); - - buf_size = len + 4; - buffer = tmp = g_malloc(buf_size); - - sent_len = GUINT32_TO_LE(len); - - memcpy(tmp, &sent_len, 4); - tmp += 4; - memcpy(tmp, data, len); - tmp += len; - - ret = write(directconn->fd, buffer, buf_size); - -#ifdef DEBUG_DC - char *str; - str = g_strdup_printf("%s/msntest/w%.4d.bin", g_get_home_dir(), directconn->c); - - FILE *tf = g_fopen(str, "w"); - fwrite(buffer, 1, buf_size, tf); - fclose(tf); - - g_free(str); -#endif - - g_free(buffer); - -#if 0 - /* Let's write the length of the data. */ - ret = write(directconn->fd, &len, sizeof(len)); - - /* Let's write the data. */ - ret = write(directconn->fd, data, len); - - char *str; - str = g_strdup_printf("/home/revo/msntest/w%.4d.bin", directconn->c); - - FILE *tf = g_fopen(str, "w"); - fwrite(&len, 1, sizeof(len), tf); - fwrite(data, 1, len, tf); - fclose(tf); - - g_free(str); -#endif - - directconn->c++; - - return ret; -} - -#if 0 -void -msn_directconn_parse_nonce(MsnDirectConn *directconn, const char *nonce) -{ - guint32 t1; - guint16 t2; - guint16 t3; - guint16 t4; - guint64 t5; - - g_return_if_fail(directconn != NULL); - g_return_if_fail(nonce != NULL); - - sscanf (nonce, "%08X-%04hX-%04hX-%04hX-%012llX", &t1, &t2, &t3, &t4, &t5); - - t1 = GUINT32_TO_LE(t1); - t2 = GUINT16_TO_LE(t2); - t3 = GUINT16_TO_LE(t3); - t4 = GUINT16_TO_BE(t4); - t5 = GUINT64_TO_BE(t5); - - directconn->slpheader = g_new0(MsnSlpHeader, 1); - - directconn->slpheader->ack_id = t1; - directconn->slpheader->ack_sub_id = t2 | (t3 << 16); - directconn->slpheader->ack_size = t4 | t5; -} -#endif - -void -msn_directconn_send_msg(MsnDirectConn *directconn, MsnMessage *msg) -{ - char *body; - size_t body_len; - - body = msn_message_gen_slp_body(msg, &body_len); - - msn_directconn_write(directconn, body, body_len); -} - -static void -msn_directconn_process_msg(MsnDirectConn *directconn, MsnMessage *msg) -{ - purple_debug_info("msn", "directconn: process_msg\n"); - - msn_slplink_process_msg(directconn->slplink, msg); -} - -static void -read_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - MsnDirectConn* directconn; - char *body; - size_t len, body_len; - - purple_debug_info("msn", "read_cb: %d, %d\n", source, cond); - - directconn = data; - - /* Let's read the length of the data. */ - len = read(directconn->fd, &body_len, sizeof(body_len)); - - if (len <= 0) - { - /* ERROR */ - purple_debug_error("msn", "error reading\n"); - - msn_directconn_destroy(directconn); - - return; - } - - body_len = GUINT32_FROM_LE(body_len); - - purple_debug_info("msn", "body_len=%" G_GSIZE_FORMAT "\n", body_len); - - if (body_len <= 0) - { - /* ERROR */ - purple_debug_error("msn", "error reading\n"); - - msn_directconn_destroy(directconn); - - return; - } - - body = g_try_malloc(body_len); - - if (body != NULL) - { - /* Let's read the data. */ - len = read(directconn->fd, body, body_len); - - purple_debug_info("msn", "len=%" G_GSIZE_FORMAT "\n", len); - } - else - { - purple_debug_error("msn", "Failed to allocate memory for read\n"); - len = 0; - } - - if (len > 0) - { - MsnMessage *msg; - -#ifdef DEBUG_DC - str = g_strdup_printf("/home/revo/msntest/r%.4d.bin", directconn->c); - - FILE *tf = g_fopen(str, "w"); - fwrite(body, 1, len, tf); - fclose(tf); - - g_free(str); -#endif - - directconn->c++; - - msg = msn_message_new_msnslp(); - msn_message_parse_slp_body(msg, body, body_len); - - msn_directconn_process_msg(directconn, msg); - } - else - { - /* ERROR */ - purple_debug_error("msn", "error reading\n"); - - msn_directconn_destroy(directconn); - } - - g_free(body); -} - -static void -connect_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - MsnDirectConn* directconn; - int fd; - - purple_debug_misc("msn", "directconn: connect_cb: %d\n", source); - - directconn = data; - directconn->connect_data = NULL; - - if (TRUE) - { - fd = source; - } - else - { - struct sockaddr_in client_addr; - socklen_t client; - fd = accept (source, (struct sockaddr *)&client_addr, &client); - } - - directconn->fd = fd; - - if (fd > 0) - { - directconn->inpa = purple_input_add(fd, PURPLE_INPUT_READ, read_cb, - directconn); - - if (TRUE) - { - /* Send foo. */ - msn_directconn_write(directconn, "foo", strlen("foo") + 1); - - /* Send Handshake */ - msn_directconn_send_handshake(directconn); - } - else - { - } - } - else - { - /* ERROR */ - purple_debug_error("msn", "could not add input\n"); - - if (directconn->inpa) - purple_input_remove(directconn->inpa); - - close(directconn->fd); - } -} - -static void -directconn_connect_cb(gpointer data, gint source, const gchar *error_message) -{ - if (error_message) - purple_debug_error("msn", "Error making direct connection: %s\n", error_message); - - connect_cb(data, source, PURPLE_INPUT_READ); -} - -gboolean -msn_directconn_connect(MsnDirectConn *directconn, const char *host, int port) -{ - MsnSession *session; - - g_return_val_if_fail(directconn != NULL, FALSE); - g_return_val_if_fail(host != NULL, TRUE); - g_return_val_if_fail(port > 0, FALSE); - - session = directconn->slplink->session; - -#if 0 - if (session->http_method) - { - servconn->http_data->gateway_host = g_strdup(host); - } -#endif - - directconn->connect_data = purple_proxy_connect(NULL, session->account, - host, port, directconn_connect_cb, directconn); - - return (directconn->connect_data != NULL); -} - -#if 0 -void -msn_directconn_listen(MsnDirectConn *directconn) -{ - int port; - int fd; - - port = 7000; - - for (fd = -1; fd < 0;) - fd = create_listener(++port); - - directconn->fd = fd; - - directconn->inpa = purple_input_add(fd, PURPLE_INPUT_READ, connect_cb, - directconn); - - directconn->port = port; - directconn->c = 0; -} -#endif - -MsnDirectConn* -msn_directconn_new(MsnSlpLink *slplink) -{ - MsnDirectConn *directconn; - - directconn = g_new0(MsnDirectConn, 1); - - directconn->slplink = slplink; - - if (slplink->directconn != NULL) - purple_debug_info("msn", "got_transresp: LEAK\n"); - - slplink->directconn = directconn; - - return directconn; -} - -void -msn_directconn_destroy(MsnDirectConn *directconn) -{ - if (directconn->connect_data != NULL) - purple_proxy_connect_cancel(directconn->connect_data); - - if (directconn->inpa != 0) - purple_input_remove(directconn->inpa); - - if (directconn->fd >= 0) - close(directconn->fd); - - if (directconn->nonce != NULL) - g_free(directconn->nonce); - - directconn->slplink->directconn = NULL; - - g_free(directconn); -}
--- a/libpurple/protocols/msnp9/directconn.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,63 +0,0 @@ -/** - * @file directconn.h MSN direct connection functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_DIRECTCONN_H_ -#define _MSN_DIRECTCONN_H_ - -typedef struct _MsnDirectConn MsnDirectConn; - -#include "slplink.h" -#include "slp.h" -#include "msg.h" - -struct _MsnDirectConn -{ - MsnSlpLink *slplink; - MsnSlpCall *initial_call; - - PurpleProxyConnectData *connect_data; - - gboolean acked; - - char *nonce; - - int fd; - - int port; - int inpa; - - int c; -}; - -MsnDirectConn *msn_directconn_new(MsnSlpLink *slplink); -gboolean msn_directconn_connect(MsnDirectConn *directconn, - const char *host, int port); -#if 0 -void msn_directconn_listen(MsnDirectConn *directconn); -#endif -void msn_directconn_send_msg(MsnDirectConn *directconn, MsnMessage *msg); -void msn_directconn_parse_nonce(MsnDirectConn *directconn, const char *nonce); -void msn_directconn_destroy(MsnDirectConn *directconn); -void msn_directconn_send_handshake(MsnDirectConn *directconn); - -#endif /* _MSN_DIRECTCONN_H_ */
--- a/libpurple/protocols/msnp9/error.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,269 +0,0 @@ -/** - * @file error.c Error functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "error.h" - -const char * -msn_error_get_text(unsigned int type, gboolean *debug) -{ - static char msg[MSN_BUF_LEN]; - *debug = FALSE; - - switch (type) { - case 0: - g_snprintf(msg, sizeof(msg), - _("Unable to parse message")); - *debug = TRUE; - break; - case 200: - g_snprintf(msg, sizeof(msg), - _("Syntax Error (probably a client bug)")); - *debug = TRUE; - break; - case 201: - g_snprintf(msg, sizeof(msg), - _("Invalid email address")); - break; - case 205: - g_snprintf(msg, sizeof(msg), _("User does not exist")); - break; - case 206: - g_snprintf(msg, sizeof(msg), - _("Fully qualified domain name missing")); - break; - case 207: - g_snprintf(msg, sizeof(msg), _("Already logged in")); - break; - case 208: - g_snprintf(msg, sizeof(msg), _("Invalid username")); - break; - case 209: - g_snprintf(msg, sizeof(msg), _("Invalid friendly name")); - break; - case 210: - g_snprintf(msg, sizeof(msg), _("List full")); - break; - case 215: - g_snprintf(msg, sizeof(msg), _("Already there")); - *debug = TRUE; - break; - case 216: - g_snprintf(msg, sizeof(msg), _("Not on list")); - break; - case 217: - g_snprintf(msg, sizeof(msg), _("User is offline")); - break; - case 218: - g_snprintf(msg, sizeof(msg), _("Already in the mode")); - *debug = TRUE; - break; - case 219: - g_snprintf(msg, sizeof(msg), _("Already in opposite list")); - *debug = TRUE; - break; - case 223: - g_snprintf(msg, sizeof(msg), _("Too many groups")); - break; - case 224: - g_snprintf(msg, sizeof(msg), _("Invalid group")); - break; - case 225: - g_snprintf(msg, sizeof(msg), _("User not in group")); - break; - case 229: - g_snprintf(msg, sizeof(msg), _("Group name too long")); - break; - case 230: - g_snprintf(msg, sizeof(msg), _("Cannot remove group zero")); - *debug = TRUE; - break; - case 231: - g_snprintf(msg, sizeof(msg), - _("Tried to add a user to a group " - "that doesn't exist")); - break; - case 280: - g_snprintf(msg, sizeof(msg), _("Switchboard failed")); - *debug = TRUE; - break; - case 281: - g_snprintf(msg, sizeof(msg), _("Notify transfer failed")); - *debug = TRUE; - break; - - case 300: - g_snprintf(msg, sizeof(msg), _("Required fields missing")); - *debug = TRUE; - break; - case 301: - g_snprintf(msg, sizeof(msg), _("Too many hits to a FND")); - *debug = TRUE; - break; - case 302: - g_snprintf(msg, sizeof(msg), _("Not logged in")); - break; - - case 500: - g_snprintf(msg, sizeof(msg), _("Service temporarily unavailable")); - break; - case 501: - g_snprintf(msg, sizeof(msg), _("Database server error")); - *debug = TRUE; - break; - case 502: - g_snprintf(msg, sizeof(msg), _("Command disabled")); - *debug = TRUE; - break; - case 510: - g_snprintf(msg, sizeof(msg), _("File operation error")); - *debug = TRUE; - break; - case 520: - g_snprintf(msg, sizeof(msg), _("Memory allocation error")); - *debug = TRUE; - break; - case 540: - g_snprintf(msg, sizeof(msg), _("Wrong CHL value sent to server")); - *debug = TRUE; - break; - - case 600: - g_snprintf(msg, sizeof(msg), _("Server busy")); - break; - case 601: - g_snprintf(msg, sizeof(msg), _("Server unavailable")); - break; - case 602: - g_snprintf(msg, sizeof(msg), _("Peer notification server down")); - *debug = TRUE; - break; - case 603: - g_snprintf(msg, sizeof(msg), _("Database connect error")); - *debug = TRUE; - break; - case 604: - g_snprintf(msg, sizeof(msg), - _("Server is going down (abandon ship)")); - break; - case 605: - g_snprintf(msg, sizeof(msg), _("Server unavailable")); - break; - - case 707: - g_snprintf(msg, sizeof(msg), _("Error creating connection")); - *debug = TRUE; - break; - case 710: - g_snprintf(msg, sizeof(msg), - _("CVR parameters are either unknown or not allowed")); - *debug = TRUE; - break; - case 711: - g_snprintf(msg, sizeof(msg), _("Unable to write")); - break; - case 712: - g_snprintf(msg, sizeof(msg), _("Session overload")); - *debug = TRUE; - break; - case 713: - g_snprintf(msg, sizeof(msg), _("User is too active")); - break; - case 714: - g_snprintf(msg, sizeof(msg), _("Too many sessions")); - break; - case 715: - g_snprintf(msg, sizeof(msg), _("Passport not verified")); - break; - case 717: - g_snprintf(msg, sizeof(msg), _("Bad friend file")); - *debug = TRUE; - break; - case 731: - g_snprintf(msg, sizeof(msg), _("Not expected")); - *debug = TRUE; - break; - - case 800: - g_snprintf(msg, sizeof(msg), - _("Friendly name changes too rapidly")); - break; - - case 910: - case 912: - case 918: - case 919: - case 921: - case 922: - g_snprintf(msg, sizeof(msg), _("Server too busy")); - break; - case 911: - case 917: - g_snprintf(msg, sizeof(msg), _("Authentication failed")); - break; - case 913: - g_snprintf(msg, sizeof(msg), _("Not allowed when offline")); - break; - case 914: - case 915: - case 916: - g_snprintf(msg, sizeof(msg), _("Server unavailable")); - break; - case 920: - g_snprintf(msg, sizeof(msg), _("Not accepting new users")); - break; - case 923: - g_snprintf(msg, sizeof(msg), - _("Kids Passport without parental consent")); - break; - case 924: - g_snprintf(msg, sizeof(msg), - _("Passport account not yet verified")); - break; - case 928: - g_snprintf(msg, sizeof(msg), _("Bad ticket")); - *debug = TRUE; - break; - - default: - g_snprintf(msg, sizeof(msg), _("Unknown Error Code %d"), type); - *debug = TRUE; - break; - } - - return msg; -} - -void -msn_error_handle(MsnSession *session, unsigned int type) -{ - char buf[MSN_BUF_LEN]; - gboolean debug; - - g_snprintf(buf, sizeof(buf), _("MSN Error: %s\n"), - msn_error_get_text(type, &debug)); - if (debug) - purple_debug_warning("msn", "error %d: %s\n", type, buf); - else - purple_notify_error(session->account->gc, NULL, buf, NULL); -}
--- a/libpurple/protocols/msnp9/error.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -/** - * @file error.h Error functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_ERROR_H_ -#define _MSN_ERROR_H_ - -#include "session.h" - -/** - * Returns the string representation of an error type. - * - * @param type The error type. - * @param debug Whether this should be treated as a debug log message or a user-visible error - * - * @return The string representation of the error type. - */ -const char *msn_error_get_text(unsigned int type, gboolean *debug); - -/** - * Handles an error. - * - * @param session The current session. - * @param type The error type. - */ -void msn_error_handle(MsnSession *session, unsigned int type); - -#endif /* _MSN_ERROR_H_ */
--- a/libpurple/protocols/msnp9/group.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,89 +0,0 @@ -/** - * @file group.c Group functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "group.h" - -MsnGroup * -msn_group_new(MsnUserList *userlist, int id, const char *name) -{ - MsnGroup *group; - - g_return_val_if_fail(id >= 0, NULL); - g_return_val_if_fail(name != NULL, NULL); - - group = g_new0(MsnGroup, 1); - - msn_userlist_add_group(userlist, group); - - group->id = id; - group->name = g_strdup(name); - - return group; -} - -void -msn_group_destroy(MsnGroup *group) -{ - g_return_if_fail(group != NULL); - - g_free(group->name); - g_free(group); -} - -void -msn_group_set_id(MsnGroup *group, int id) -{ - g_return_if_fail(group != NULL); - g_return_if_fail(id >= 0); - - group->id = id; -} - -void -msn_group_set_name(MsnGroup *group, const char *name) -{ - g_return_if_fail(group != NULL); - g_return_if_fail(name != NULL); - - if (group->name != NULL) - g_free(group->name); - - group->name = g_strdup(name); -} - -int -msn_group_get_id(const MsnGroup *group) -{ - g_return_val_if_fail(group != NULL, -1); - - return group->id; -} - -const char * -msn_group_get_name(const MsnGroup *group) -{ - g_return_val_if_fail(group != NULL, NULL); - - return group->name; -}
--- a/libpurple/protocols/msnp9/group.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -/** - * @file group.h Group functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_GROUP_H_ -#define _MSN_GROUP_H_ - -typedef struct _MsnGroup MsnGroup; - -#include <stdio.h> - -#include "session.h" -#include "user.h" - -#include "userlist.h" - -/** - * A group. - */ -struct _MsnGroup -{ - MsnSession *session; /**< The MSN session. */ - - int id; /**< The group ID. */ - char *name; /**< The name of the group. */ -}; - -/**************************************************************************/ -/** @name Group API */ -/**************************************************************************/ -/*@{*/ - -/** - * Creates a new group structure. - * - * @param session The MSN session. - * @param id The group ID. - * @param name The name of the group. - * - * @return A new group structure. - */ -MsnGroup *msn_group_new(MsnUserList *userlist, int id, const char *name); - -/** - * Destroys a group structure. - * - * @param group The group to destroy. - */ -void msn_group_destroy(MsnGroup *group); - -/** - * Sets the ID for a group. - * - * @param group The group. - * @param id The ID. - */ -void msn_group_set_id(MsnGroup *group, int id); - -/** - * Sets the name for a group. - * - * @param group The group. - * @param name The name. - */ -void msn_group_set_name(MsnGroup *group, const char *name); - -/** - * Returns the ID for a group. - * - * @param group The group. - * - * @return The ID. - */ -int msn_group_get_id(const MsnGroup *group); - -/** - * Returns the name for a group. - * - * @param group The group. - * - * @return The name. - */ -const char *msn_group_get_name(const MsnGroup *group); -#endif /* _MSN_GROUP_H_ */
--- a/libpurple/protocols/msnp9/history.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,86 +0,0 @@ -/** - * @file history.c MSN history functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "history.h" - -MsnHistory * -msn_history_new(void) -{ - MsnHistory *history = g_new0(MsnHistory, 1); - - history->trId = 1; - - history->queue = g_queue_new(); - - return history; -} - -void -msn_history_destroy(MsnHistory *history) -{ - MsnTransaction *trans; - - while ((trans = g_queue_pop_head(history->queue)) != NULL) - msn_transaction_destroy(trans); - - g_queue_free(history->queue); - g_free(history); -} - -MsnTransaction * -msn_history_find(MsnHistory *history, unsigned int trId) -{ - MsnTransaction *trans; - GList *list; - - for (list = history->queue->head; list != NULL; list = list->next) - { - trans = list->data; - if (trans->trId == trId) - return trans; - } - - return NULL; -} - -void -msn_history_add(MsnHistory *history, MsnTransaction *trans) -{ - GQueue *queue; - - g_return_if_fail(history != NULL); - g_return_if_fail(trans != NULL); - - queue = history->queue; - - trans->trId = history->trId++; - - g_queue_push_tail(queue, trans); - - if (queue->length > MSN_HIST_ELEMS) - { - trans = g_queue_pop_head(queue); - msn_transaction_destroy(trans); - } -}
--- a/libpurple/protocols/msnp9/history.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,47 +0,0 @@ -/** - * @file history.h MSN history functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_HISTORY_H -#define _MSN_HISTORY_H - -#define MSN_HIST_ELEMS 0x30 - -typedef struct _MsnHistory MsnHistory; - -#include "transaction.h" - -/** - * The history. - */ -struct _MsnHistory -{ - GQueue *queue; - unsigned int trId; -}; - -MsnHistory *msn_history_new(void); -void msn_history_destroy(MsnHistory *history); -MsnTransaction *msn_history_find(MsnHistory *history, unsigned int triId); -void msn_history_add(MsnHistory *history, MsnTransaction *trans); - -#endif /* _MSN_HISTORY_H */
--- a/libpurple/protocols/msnp9/httpconn.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,775 +0,0 @@ -/** - * @file httpmethod.c HTTP connection method - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "debug.h" -#include "httpconn.h" - -typedef struct -{ - MsnHttpConn *httpconn; - char *body; - size_t body_len; -} MsnHttpQueueData; - -static void -msn_httpconn_process_queue(MsnHttpConn *httpconn) -{ - httpconn->waiting_response = FALSE; - - if (httpconn->queue != NULL) - { - MsnHttpQueueData *queue_data; - - queue_data = (MsnHttpQueueData *)httpconn->queue->data; - - httpconn->queue = g_list_remove(httpconn->queue, queue_data); - - msn_httpconn_write(queue_data->httpconn, - queue_data->body, - queue_data->body_len); - - g_free(queue_data->body); - g_free(queue_data); - } -} - -static gboolean -msn_httpconn_parse_data(MsnHttpConn *httpconn, const char *buf, - size_t size, char **ret_buf, size_t *ret_size, - gboolean *error) -{ - const char *s, *c; - char *header, *body; - const char *body_start; - char *tmp; - size_t body_len = 0; - gboolean wasted = FALSE; - - g_return_val_if_fail(httpconn != NULL, FALSE); - g_return_val_if_fail(buf != NULL, FALSE); - g_return_val_if_fail(size > 0, FALSE); - g_return_val_if_fail(ret_buf != NULL, FALSE); - g_return_val_if_fail(ret_size != NULL, FALSE); - g_return_val_if_fail(error != NULL, FALSE); - -#if 0 - purple_debug_info("msn", "HTTP: parsing data {%s}\n", buf); -#endif - - /* Healthy defaults. */ - body = NULL; - - *ret_buf = NULL; - *ret_size = 0; - *error = FALSE; - - /* First, some tests to see if we have a full block of stuff. */ - if (((strncmp(buf, "HTTP/1.1 200 OK\r\n", 17) != 0) && - (strncmp(buf, "HTTP/1.1 100 Continue\r\n", 23) != 0)) && - ((strncmp(buf, "HTTP/1.0 200 OK\r\n", 17) != 0) && - (strncmp(buf, "HTTP/1.0 100 Continue\r\n", 23) != 0))) - { - *error = TRUE; - - return FALSE; - } - - if (strncmp(buf, "HTTP/1.1 100 Continue\r\n", 23) == 0) - { - if ((s = strstr(buf, "\r\n\r\n")) == NULL) - return FALSE; - - s += 4; - - if (*s == '\0') - { - *ret_buf = g_strdup(""); - *ret_size = 0; - - msn_httpconn_process_queue(httpconn); - - return TRUE; - } - - buf = s; - size -= (s - buf); - } - - if ((s = strstr(buf, "\r\n\r\n")) == NULL) - /* Need to wait for the full HTTP header to arrive */ - return FALSE; - - s += 4; /* Skip \r\n */ - header = g_strndup(buf, s - buf); - body_start = s; - body_len = size - (body_start - buf); - - if ((s = purple_strcasestr(header, "Content-Length: ")) != NULL) - { - int tmp_len; - - s += strlen("Content-Length: "); - - if ((c = strchr(s, '\r')) == NULL) - { - g_free(header); - - return FALSE; - } - - tmp = g_strndup(s, c - s); - tmp_len = atoi(tmp); - g_free(tmp); - - if (body_len != tmp_len) - { - /* Need to wait for the full packet to arrive */ - - g_free(header); - -#if 0 - purple_debug_warning("msn", - "body length (%d) != content length (%d)\n", - body_len, tmp_len); -#endif - - return FALSE; - } - } - - body = g_malloc0(body_len + 1); - memcpy(body, body_start, body_len); - -#ifdef MSN_DEBUG_HTTP - purple_debug_misc("msn", "Incoming HTTP buffer (header): {%s\r\n}\n", - header); -#endif - - /* Now we should be able to process the data. */ - if ((s = purple_strcasestr(header, "X-MSN-Messenger: ")) != NULL) - { - char *full_session_id, *gw_ip, *session_action; - char *t, *session_id; - char **elems, **cur, **tokens; - - full_session_id = gw_ip = session_action = NULL; - - s += strlen("X-MSN-Messenger: "); - - if ((c = strchr(s, '\r')) == NULL) - { - msn_session_set_error(httpconn->session, - MSN_ERROR_HTTP_MALFORMED, NULL); - purple_debug_error("msn", "Malformed X-MSN-Messenger field.\n{%s}\n", - buf); - - g_free(body); - return FALSE; - } - - tmp = g_strndup(s, c - s); - - elems = g_strsplit(tmp, "; ", 0); - - for (cur = elems; *cur != NULL; cur++) - { - tokens = g_strsplit(*cur, "=", 2); - - if (strcmp(tokens[0], "SessionID") == 0) - full_session_id = tokens[1]; - else if (strcmp(tokens[0], "GW-IP") == 0) - gw_ip = tokens[1]; - else if (strcmp(tokens[0], "Session") == 0) - session_action = tokens[1]; - else - g_free(tokens[1]); - - g_free(tokens[0]); - /* Don't free each of the tokens, only the array. */ - g_free(tokens); - } - - g_strfreev(elems); - - g_free(tmp); - - if ((session_action != NULL) && (strcmp(session_action, "close") == 0)) - wasted = TRUE; - - g_free(session_action); - - t = strchr(full_session_id, '.'); - session_id = g_strndup(full_session_id, t - full_session_id); - - if (!wasted) - { - g_free(httpconn->full_session_id); - httpconn->full_session_id = full_session_id; - - g_free(httpconn->session_id); - httpconn->session_id = session_id; - - g_free(httpconn->host); - httpconn->host = gw_ip; - } - else - { - MsnServConn *servconn; - - /* It's going to die. */ - /* poor thing */ - - servconn = httpconn->servconn; - - /* I'll be honest, I don't fully understand all this, but this - * causes crashes, Stu. */ - /* if (servconn != NULL) - servconn->wasted = TRUE; */ - - g_free(full_session_id); - g_free(session_id); - g_free(gw_ip); - } - } - - g_free(header); - - *ret_buf = body; - *ret_size = body_len; - - msn_httpconn_process_queue(httpconn); - - return TRUE; -} - -static void -read_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - MsnHttpConn *httpconn; - MsnServConn *servconn; - MsnSession *session; - char buf[MSN_BUF_LEN]; - char *cur, *end, *old_rx_buf; - int len, cur_len; - char *result_msg = NULL; - size_t result_len = 0; - gboolean error = FALSE; - - httpconn = data; - servconn = NULL; - session = httpconn->session; - - len = read(httpconn->fd, buf, sizeof(buf) - 1); - - if (len < 0 && errno == EAGAIN) - return; - else if (len <= 0) - { - purple_debug_error("msn", "HTTP: Read error\n"); - msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_READ); - - return; - } - - buf[len] = '\0'; - - httpconn->rx_buf = g_realloc(httpconn->rx_buf, len + httpconn->rx_len + 1); - memcpy(httpconn->rx_buf + httpconn->rx_len, buf, len + 1); - httpconn->rx_len += len; - - if (!msn_httpconn_parse_data(httpconn, httpconn->rx_buf, httpconn->rx_len, - &result_msg, &result_len, &error)) - { - /* Either we must wait for more input, or something went wrong */ - if (error) - msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_READ); - - return; - } - - httpconn->servconn->processing = FALSE; - - servconn = httpconn->servconn; - - if (error) - { - purple_debug_error("msn", "HTTP: Special error\n"); - msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_READ); - - return; - } - - g_free(httpconn->rx_buf); - httpconn->rx_buf = NULL; - httpconn->rx_len = 0; - - if (result_len == 0) - { - /* Nothing to do here */ -#if 0 - purple_debug_info("msn", "HTTP: nothing to do here\n"); -#endif - g_free(result_msg); - return; - } - - g_free(servconn->rx_buf); - servconn->rx_buf = result_msg; - servconn->rx_len = result_len; - - end = old_rx_buf = servconn->rx_buf; - - servconn->processing = TRUE; - - do - { - cur = end; - - if (servconn->payload_len) - { - if (servconn->payload_len > servconn->rx_len) - /* The payload is still not complete. */ - break; - - cur_len = servconn->payload_len; - end += cur_len; - } - else - { - end = strstr(cur, "\r\n"); - - if (end == NULL) - /* The command is still not complete. */ - break; - - *end = '\0'; - end += 2; - cur_len = end - cur; - } - - servconn->rx_len -= cur_len; - - if (servconn->payload_len) - { - msn_cmdproc_process_payload(servconn->cmdproc, cur, cur_len); - servconn->payload_len = 0; - } - else - { - msn_cmdproc_process_cmd_text(servconn->cmdproc, cur); - } - } while (servconn->connected && servconn->rx_len > 0); - - if (servconn->connected) - { - if (servconn->rx_len > 0) - servconn->rx_buf = g_memdup(cur, servconn->rx_len); - else - servconn->rx_buf = NULL; - } - - servconn->processing = FALSE; - - if (servconn->wasted) - msn_servconn_destroy(servconn); - - g_free(old_rx_buf); -} - -static void -httpconn_write_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - MsnHttpConn *httpconn; - int ret, writelen; - - httpconn = data; - writelen = purple_circ_buffer_get_max_read(httpconn->tx_buf); - - if (writelen == 0) - { - purple_input_remove(httpconn->tx_handler); - httpconn->tx_handler = 0; - return; - } - - ret = write(httpconn->fd, httpconn->tx_buf->outptr, writelen); - if (ret <= 0) - { - if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) - /* No worries */ - return; - - /* Error! */ - msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_WRITE); - return; - } - - purple_circ_buffer_mark_read(httpconn->tx_buf, ret); - - /* TODO: I don't think these 2 lines are needed. Remove them? */ - if (ret == writelen) - httpconn_write_cb(data, source, cond); -} - -static gboolean -write_raw(MsnHttpConn *httpconn, const char *data, size_t data_len) -{ - ssize_t res; /* result of the write operation */ - - if (httpconn->tx_handler == 0) - res = write(httpconn->fd, data, data_len); - else - { - res = -1; - errno = EAGAIN; - } - - if ((res <= 0) && ((errno != EAGAIN) && (errno != EWOULDBLOCK))) - { - msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_WRITE); - return FALSE; - } - - if (res < 0 || res < data_len) - { - if (res < 0) - res = 0; - if (httpconn->tx_handler == 0 && httpconn->fd) - httpconn->tx_handler = purple_input_add(httpconn->fd, - PURPLE_INPUT_WRITE, httpconn_write_cb, httpconn); - purple_circ_buffer_append(httpconn->tx_buf, data + res, - data_len - res); - } - - return TRUE; -} - -static char * -msn_httpconn_proxy_auth(MsnHttpConn *httpconn) -{ - PurpleAccount *account; - PurpleProxyInfo *gpi; - const char *username, *password; - char *auth = NULL; - - account = httpconn->session->account; - - gpi = purple_proxy_get_setup(account); - - if (gpi == NULL || !(purple_proxy_info_get_type(gpi) == PURPLE_PROXY_HTTP || - purple_proxy_info_get_type(gpi) == PURPLE_PROXY_USE_ENVVAR)) - return NULL; - - username = purple_proxy_info_get_username(gpi); - password = purple_proxy_info_get_password(gpi); - - if (username != NULL) { - char *tmp; - auth = g_strdup_printf("%s:%s", username, password ? password : ""); - tmp = purple_base64_encode((const guchar *)auth, strlen(auth)); - g_free(auth); - auth = g_strdup_printf("Proxy-Authorization: Basic %s\r\n", tmp); - g_free(tmp); - } - - return auth; -} - -static gboolean -msn_httpconn_poll(gpointer data) -{ - MsnHttpConn *httpconn; - char *header; - char *auth; - - httpconn = data; - - g_return_val_if_fail(httpconn != NULL, FALSE); - - if ((httpconn->host == NULL) || (httpconn->full_session_id == NULL)) - { - /* There's no need to poll if the session is not fully established */ - return TRUE; - } - - if (httpconn->waiting_response) - { - /* There's no need to poll if we're already waiting for a response */ - return TRUE; - } - - auth = msn_httpconn_proxy_auth(httpconn); - - header = g_strdup_printf( - "POST http://%s/gateway/gateway.dll?Action=poll&SessionID=%s HTTP/1.1\r\n" - "Accept: */*\r\n" - "Accept-Language: en-us\r\n" - "User-Agent: MSMSGS\r\n" - "Host: %s\r\n" - "Proxy-Connection: Keep-Alive\r\n" - "%s" /* Proxy auth */ - "Connection: Keep-Alive\r\n" - "Pragma: no-cache\r\n" - "Content-Type: application/x-msn-messenger\r\n" - "Content-Length: 0\r\n\r\n", - httpconn->host, - httpconn->full_session_id, - httpconn->host, - auth ? auth : ""); - - g_free(auth); - - if (write_raw(httpconn, header, strlen(header))) - httpconn->waiting_response = TRUE; - - g_free(header); - - return TRUE; -} - -ssize_t -msn_httpconn_write(MsnHttpConn *httpconn, const char *body, size_t body_len) -{ - char *params; - char *data; - int header_len; - char *auth; - const char *server_types[] = { "NS", "SB" }; - const char *server_type; - char *host; - MsnServConn *servconn; - - /* TODO: remove http data from servconn */ - - g_return_val_if_fail(httpconn != NULL, 0); - g_return_val_if_fail(body != NULL, 0); - g_return_val_if_fail(body_len > 0, 0); - - servconn = httpconn->servconn; - - if (httpconn->waiting_response) - { - MsnHttpQueueData *queue_data = g_new0(MsnHttpQueueData, 1); - - queue_data->httpconn = httpconn; - queue_data->body = g_memdup(body, body_len); - queue_data->body_len = body_len; - - httpconn->queue = g_list_append(httpconn->queue, queue_data); - - return body_len; - } - - server_type = server_types[servconn->type]; - - if (httpconn->virgin) - { - host = "gateway.messenger.hotmail.com"; - - /* The first time servconn->host is the host we should connect to. */ - params = g_strdup_printf("Action=open&Server=%s&IP=%s", - server_type, - servconn->host); - httpconn->virgin = FALSE; - } - else - { - /* The rest of the times servconn->host is the gateway host. */ - host = httpconn->host; - - if (host == NULL || httpconn->full_session_id == NULL) - { - purple_debug_warning("msn", "Attempted HTTP write before session is established\n"); - return -1; - } - - params = g_strdup_printf("SessionID=%s", - httpconn->full_session_id); - } - - auth = msn_httpconn_proxy_auth(httpconn); - - data = g_strdup_printf( - "POST http://%s/gateway/gateway.dll?%s HTTP/1.1\r\n" - "Accept: */*\r\n" - "Accept-Language: en-us\r\n" - "User-Agent: MSMSGS\r\n" - "Host: %s\r\n" - "Proxy-Connection: Keep-Alive\r\n" - "%s" /* Proxy auth */ - "Connection: Keep-Alive\r\n" - "Pragma: no-cache\r\n" - "Content-Type: application/x-msn-messenger\r\n" - "Content-Length: %d\r\n\r\n", - host, - params, - host, - auth ? auth : "", - (int) body_len); - - g_free(params); - - g_free(auth); - - header_len = strlen(data); - data = g_realloc(data, header_len + body_len); - memcpy(data + header_len, body, body_len); - - if (write_raw(httpconn, data, header_len + body_len)) - httpconn->waiting_response = TRUE; - - g_free(data); - - return body_len; -} - -MsnHttpConn * -msn_httpconn_new(MsnServConn *servconn) -{ - MsnHttpConn *httpconn; - - g_return_val_if_fail(servconn != NULL, NULL); - - httpconn = g_new0(MsnHttpConn, 1); - - purple_debug_info("msn", "new httpconn (%p)\n", httpconn); - - /* TODO: Remove this */ - httpconn->session = servconn->session; - - httpconn->servconn = servconn; - - httpconn->tx_buf = purple_circ_buffer_new(MSN_BUF_LEN); - httpconn->tx_handler = 0; - - return httpconn; -} - -void -msn_httpconn_destroy(MsnHttpConn *httpconn) -{ - g_return_if_fail(httpconn != NULL); - - purple_debug_info("msn", "destroy httpconn (%p)\n", httpconn); - - if (httpconn->connected) - msn_httpconn_disconnect(httpconn); - - g_free(httpconn->full_session_id); - - g_free(httpconn->session_id); - - g_free(httpconn->host); - - purple_circ_buffer_destroy(httpconn->tx_buf); - if (httpconn->tx_handler > 0) - purple_input_remove(httpconn->tx_handler); - - g_free(httpconn); -} - -static void -connect_cb(gpointer data, gint source, const gchar *error_message) -{ - MsnHttpConn *httpconn; - - httpconn = data; - httpconn->connect_data = NULL; - httpconn->fd = source; - - if (source >= 0) - { - httpconn->inpa = purple_input_add(httpconn->fd, PURPLE_INPUT_READ, - read_cb, data); - - httpconn->timer = purple_timeout_add_seconds(3, msn_httpconn_poll, httpconn); - - msn_httpconn_process_queue(httpconn); - } - else - { - purple_debug_error("msn", "HTTP: Connection error\n"); - msn_servconn_got_error(httpconn->servconn, MSN_SERVCONN_ERROR_CONNECT); - } -} - -gboolean -msn_httpconn_connect(MsnHttpConn *httpconn, const char *host, int port) -{ - g_return_val_if_fail(httpconn != NULL, FALSE); - g_return_val_if_fail(host != NULL, FALSE); - g_return_val_if_fail(port > 0, FALSE); - - if (httpconn->connected) - msn_httpconn_disconnect(httpconn); - - httpconn->connect_data = purple_proxy_connect(NULL, httpconn->session->account, - host, 80, connect_cb, httpconn); - - if (httpconn->connect_data != NULL) - { - httpconn->waiting_response = TRUE; - httpconn->connected = TRUE; - } - - return httpconn->connected; -} - -void -msn_httpconn_disconnect(MsnHttpConn *httpconn) -{ - g_return_if_fail(httpconn != NULL); - - if (!httpconn->connected) - return; - - if (httpconn->connect_data != NULL) - { - purple_proxy_connect_cancel(httpconn->connect_data); - httpconn->connect_data = NULL; - } - - if (httpconn->timer) - { - purple_timeout_remove(httpconn->timer); - httpconn->timer = 0; - } - - if (httpconn->inpa > 0) - { - purple_input_remove(httpconn->inpa); - httpconn->inpa = 0; - } - - close(httpconn->fd); - httpconn->fd = -1; - - g_free(httpconn->rx_buf); - httpconn->rx_buf = NULL; - httpconn->rx_len = 0; - - httpconn->connected = FALSE; - - /* msn_servconn_disconnect(httpconn->servconn); */ -}
--- a/libpurple/protocols/msnp9/httpconn.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,111 +0,0 @@ -/** - * @file httpconn.h HTTP connection - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_HTTPCONN_H_ -#define _MSN_HTTPCONN_H_ - -typedef struct _MsnHttpConn MsnHttpConn; - -#include "circbuffer.h" -#include "servconn.h" - -/** - * An HTTP Connection. - */ -struct _MsnHttpConn -{ - MsnSession *session; /**< The MSN Session. */ - MsnServConn *servconn; /**< The connection object. */ - - PurpleProxyConnectData *connect_data; - - char *full_session_id; /**< The full session id. */ - char *session_id; /**< The trimmed session id. */ - - int timer; /**< The timer for polling. */ - - gboolean waiting_response; /**< The flag that states if we are waiting - a response from the server. */ - gboolean connected; /**< The flag that states if the connection is on. */ - gboolean virgin; /**< The flag that states if this connection - should specify the host (not gateway) to - connect to. */ - - char *host; /**< The HTTP gateway host. */ - GList *queue; /**< The queue of data chunks to write. */ - - int fd; /**< The connection's file descriptor. */ - guint inpa; /**< The connection's input handler. */ - - char *rx_buf; /**< The receive buffer. */ - int rx_len; /**< The receive buffer length. */ - - PurpleCircBuffer *tx_buf; - guint tx_handler; -}; - -/** - * Creates a new HTTP connection object. - * - * @param servconn The connection object. - * - * @return The new object. - */ -MsnHttpConn *msn_httpconn_new(MsnServConn *servconn); - -/** - * Destroys an HTTP connection object. - * - * @param httpconn The HTTP connection object. - */ -void msn_httpconn_destroy(MsnHttpConn *httpconn); - -/** - * Writes a chunk of data to the HTTP connection. - * - * @param servconn The server connection. - * @param data The data to write. - * @param data_len The size of the data to write. - * - * @return The number of bytes written. - */ -ssize_t msn_httpconn_write(MsnHttpConn *httpconn, const char *data, size_t data_len); - -/** - * Connects the HTTP connection object to a host. - * - * @param httpconn The HTTP connection object. - * @param host The host to connect to. - * @param port The port to connect to. - */ -gboolean msn_httpconn_connect(MsnHttpConn *httpconn, - const char *host, int port); - -/** - * Disconnects the HTTP connection object. - * - * @param httpconn The HTTP connection object. - */ -void msn_httpconn_disconnect(MsnHttpConn *httpconn); - -#endif /* _MSN_HTTPCONN_H_ */
--- a/libpurple/protocols/msnp9/msg.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,785 +0,0 @@ -/** - * @file msg.c Message functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "msg.h" - -MsnMessage * -msn_message_new(MsnMsgType type) -{ - MsnMessage *msg; - - msg = g_new0(MsnMessage, 1); - msg->type = type; - -#ifdef MSN_DEBUG_MSG - purple_debug_info("msn", "message new (%p)(%d)\n", msg, type); -#endif - - msg->attr_table = g_hash_table_new_full(g_str_hash, g_str_equal, - g_free, g_free); - - msn_message_ref(msg); - - return msg; -} - -void -msn_message_destroy(MsnMessage *msg) -{ - g_return_if_fail(msg != NULL); - - if (msg->ref_count > 0) - { - msn_message_unref(msg); - - return; - } - -#ifdef MSN_DEBUG_MSG - purple_debug_info("msn", "message destroy (%p)\n", msg); -#endif - - if (msg->remote_user != NULL) - g_free(msg->remote_user); - - if (msg->body != NULL) - g_free(msg->body); - - if (msg->content_type != NULL) - g_free(msg->content_type); - - if (msg->charset != NULL) - g_free(msg->charset); - - g_hash_table_destroy(msg->attr_table); - g_list_free(msg->attr_list); - - g_free(msg); -} - -MsnMessage * -msn_message_ref(MsnMessage *msg) -{ - g_return_val_if_fail(msg != NULL, NULL); - - msg->ref_count++; - -#ifdef MSN_DEBUG_MSG - purple_debug_info("msn", "message ref (%p)[%d]\n", msg, msg->ref_count); -#endif - - return msg; -} - -MsnMessage * -msn_message_unref(MsnMessage *msg) -{ - g_return_val_if_fail(msg != NULL, NULL); - g_return_val_if_fail(msg->ref_count > 0, NULL); - - msg->ref_count--; - -#ifdef MSN_DEBUG_MSG - purple_debug_info("msn", "message unref (%p)[%d]\n", msg, msg->ref_count); -#endif - - if (msg->ref_count == 0) - { - msn_message_destroy(msg); - - return NULL; - } - - return msg; -} - -MsnMessage * -msn_message_new_plain(const char *message) -{ - MsnMessage *msg; - char *message_cr; - - msg = msn_message_new(MSN_MSG_TEXT); - msn_message_set_attr(msg, "User-Agent", PACKAGE_NAME "/" DISPLAY_VERSION); - msn_message_set_content_type(msg, "text/plain"); - msn_message_set_charset(msg, "UTF-8"); - msn_message_set_flag(msg, 'A'); - msn_message_set_attr(msg, "X-MMS-IM-Format", - "FN=MS%20Sans%20Serif; EF=; CO=0; PF=0"); - - message_cr = purple_str_add_cr(message); - msn_message_set_bin_data(msg, message_cr, strlen(message_cr)); - g_free(message_cr); - - return msg; -} - -MsnMessage * -msn_message_new_msnslp(void) -{ - MsnMessage *msg; - - msg = msn_message_new(MSN_MSG_SLP); - - msn_message_set_attr(msg, "User-Agent", NULL); - - msg->msnslp_message = TRUE; - - msn_message_set_flag(msg, 'D'); - msn_message_set_content_type(msg, "application/x-msnmsgrp2p"); - - return msg; -} - -MsnMessage * -msn_message_new_nudge(void) -{ - MsnMessage *msg; - - msg = msn_message_new(MSN_MSG_NUDGE); - msn_message_set_content_type(msg, "text/x-msnmsgr-datacast"); - msn_message_set_flag(msg, 'N'); - msn_message_set_bin_data(msg, "ID: 1\r\n", 7); - - return msg; -} - -void -msn_message_parse_slp_body(MsnMessage *msg, const char *body, size_t len) -{ - MsnSlpHeader header; - const char *tmp; - int body_len; - - tmp = body; - - if (len < sizeof(header)) { - g_return_if_reached(); - } - - /* Import the header. */ - memcpy(&header, tmp, sizeof(header)); - tmp += sizeof(header); - - msg->msnslp_header.session_id = GUINT32_FROM_LE(header.session_id); - msg->msnslp_header.id = GUINT32_FROM_LE(header.id); - msg->msnslp_header.offset = GUINT64_FROM_LE(header.offset); - msg->msnslp_header.total_size = GUINT64_FROM_LE(header.total_size); - msg->msnslp_header.length = GUINT32_FROM_LE(header.length); - msg->msnslp_header.flags = GUINT32_FROM_LE(header.flags); - msg->msnslp_header.ack_id = GUINT32_FROM_LE(header.ack_id); - msg->msnslp_header.ack_sub_id = GUINT32_FROM_LE(header.ack_sub_id); - msg->msnslp_header.ack_size = GUINT64_FROM_LE(header.ack_size); - - /* Import the body. */ - body_len = len - (tmp - body); - /* msg->body_len = msg->msnslp_header.length; */ - - if (body_len > 0) { - msg->body_len = len - (tmp - body); - msg->body = g_malloc0(msg->body_len + 1); - memcpy(msg->body, tmp, msg->body_len); - tmp += body_len; - } -} - -void -msn_message_parse_payload(MsnMessage *msg, - const char *payload, size_t payload_len) -{ - char *tmp_base, *tmp; - const char *content_type; - char *end; - char **elems, **cur, **tokens; - - g_return_if_fail(payload != NULL); - - tmp_base = tmp = g_malloc0(payload_len + 1); - memcpy(tmp_base, payload, payload_len); - - /* Parse the attributes. */ - end = strstr(tmp, "\r\n\r\n"); - /* TODO? some clients use \r delimiters instead of \r\n, the official client - * doesn't send such messages, but does handle receiving them. We'll just - * avoid crashing for now */ - if (end == NULL) { - g_free(tmp_base); - g_return_if_reached(); - } - *end = '\0'; - - elems = g_strsplit(tmp, "\r\n", 0); - - for (cur = elems; *cur != NULL; cur++) - { - const char *key, *value; - - tokens = g_strsplit(*cur, ": ", 2); - - key = tokens[0]; - value = tokens[1]; - - if (!strcmp(key, "MIME-Version")) - { - g_strfreev(tokens); - continue; - } - - if (!strcmp(key, "Content-Type")) - { - char *charset, *c; - - if ((c = strchr(value, ';')) != NULL) - { - if ((charset = strchr(c, '=')) != NULL) - { - charset++; - msn_message_set_charset(msg, charset); - } - - *c = '\0'; - } - - msn_message_set_content_type(msg, value); - } - else - { - msn_message_set_attr(msg, key, value); - } - - g_strfreev(tokens); - } - - g_strfreev(elems); - - /* Proceed to the end of the "\r\n\r\n" */ - tmp = end + 4; - - /* Now we *should* be at the body. */ - content_type = msn_message_get_content_type(msg); - - if (content_type != NULL && - !strcmp(content_type, "application/x-msnmsgrp2p")) - { - MsnSlpHeader header; - MsnSlpFooter footer; - int body_len; - - if (payload_len - (tmp - tmp_base) < sizeof(header)) { - g_free(tmp_base); - g_return_if_reached(); - } - - msg->msnslp_message = TRUE; - - /* Import the header. */ - memcpy(&header, tmp, sizeof(header)); - tmp += sizeof(header); - - msg->msnslp_header.session_id = GUINT32_FROM_LE(header.session_id); - msg->msnslp_header.id = GUINT32_FROM_LE(header.id); - msg->msnslp_header.offset = GUINT64_FROM_LE(header.offset); - msg->msnslp_header.total_size = GUINT64_FROM_LE(header.total_size); - msg->msnslp_header.length = GUINT32_FROM_LE(header.length); - msg->msnslp_header.flags = GUINT32_FROM_LE(header.flags); - msg->msnslp_header.ack_id = GUINT32_FROM_LE(header.ack_id); - msg->msnslp_header.ack_sub_id = GUINT32_FROM_LE(header.ack_sub_id); - msg->msnslp_header.ack_size = GUINT64_FROM_LE(header.ack_size); - - body_len = payload_len - (tmp - tmp_base) - sizeof(footer); - - /* Import the body. */ - if (body_len > 0) { - msg->body_len = body_len; - msg->body = g_malloc0(msg->body_len + 1); - memcpy(msg->body, tmp, msg->body_len); - tmp += body_len; - } - - /* Import the footer. */ - if (body_len >= 0) { - memcpy(&footer, tmp, sizeof(footer)); - tmp += sizeof(footer); - msg->msnslp_footer.value = GUINT32_FROM_BE(footer.value); - } - } - else - { - if (payload_len - (tmp - tmp_base) > 0) { - msg->body_len = payload_len - (tmp - tmp_base); - msg->body = g_malloc0(msg->body_len + 1); - memcpy(msg->body, tmp, msg->body_len); - } - } - - g_free(tmp_base); -} - -MsnMessage * -msn_message_new_from_cmd(MsnSession *session, MsnCommand *cmd) -{ - MsnMessage *msg; - - g_return_val_if_fail(cmd != NULL, NULL); - - msg = msn_message_new(MSN_MSG_UNKNOWN); - - msg->remote_user = g_strdup(cmd->params[0]); - /* msg->size = atoi(cmd->params[2]); */ - msg->cmd = cmd; - - return msg; -} - -char * -msn_message_gen_slp_body(MsnMessage *msg, size_t *ret_size) -{ - MsnSlpHeader header; - - char *tmp, *base; - const void *body; - size_t len, body_len; - - g_return_val_if_fail(msg != NULL, NULL); - - len = MSN_BUF_LEN; - - base = tmp = g_malloc(len + 1); - - body = msn_message_get_bin_data(msg, &body_len); - - header.session_id = GUINT32_TO_LE(msg->msnslp_header.session_id); - header.id = GUINT32_TO_LE(msg->msnslp_header.id); - header.offset = GUINT64_TO_LE(msg->msnslp_header.offset); - header.total_size = GUINT64_TO_LE(msg->msnslp_header.total_size); - header.length = GUINT32_TO_LE(msg->msnslp_header.length); - header.flags = GUINT32_TO_LE(msg->msnslp_header.flags); - header.ack_id = GUINT32_TO_LE(msg->msnslp_header.ack_id); - header.ack_sub_id = GUINT32_TO_LE(msg->msnslp_header.ack_sub_id); - header.ack_size = GUINT64_TO_LE(msg->msnslp_header.ack_size); - - memcpy(tmp, &header, 48); - tmp += 48; - - if (body != NULL) - { - memcpy(tmp, body, body_len); - tmp += body_len; - } - - if (ret_size != NULL) - *ret_size = tmp - base; - - return base; -} - -char * -msn_message_gen_payload(MsnMessage *msg, size_t *ret_size) -{ - GList *l; - char *n, *base, *end; - int len; - size_t body_len = 0; - const void *body; - - g_return_val_if_fail(msg != NULL, NULL); - - len = MSN_BUF_LEN; - - base = n = end = g_malloc(len + 1); - end += len; - - /* Standard header. */ - if (msg->charset == NULL) - { - g_snprintf(n, len, - "MIME-Version: 1.0\r\n" - "Content-Type: %s\r\n", - msg->content_type); - } - else - { - g_snprintf(n, len, - "MIME-Version: 1.0\r\n" - "Content-Type: %s; charset=%s\r\n", - msg->content_type, msg->charset); - } - - n += strlen(n); - - for (l = msg->attr_list; l != NULL; l = l->next) - { - const char *key; - const char *value; - - key = l->data; - value = msn_message_get_attr(msg, key); - - g_snprintf(n, end - n, "%s: %s\r\n", key, value); - n += strlen(n); - } - - n += g_strlcpy(n, "\r\n", end - n); - - body = msn_message_get_bin_data(msg, &body_len); - - if (msg->msnslp_message) - { - MsnSlpHeader header; - MsnSlpFooter footer; - - header.session_id = GUINT32_TO_LE(msg->msnslp_header.session_id); - header.id = GUINT32_TO_LE(msg->msnslp_header.id); - header.offset = GUINT64_TO_LE(msg->msnslp_header.offset); - header.total_size = GUINT64_TO_LE(msg->msnslp_header.total_size); - header.length = GUINT32_TO_LE(msg->msnslp_header.length); - header.flags = GUINT32_TO_LE(msg->msnslp_header.flags); - header.ack_id = GUINT32_TO_LE(msg->msnslp_header.ack_id); - header.ack_sub_id = GUINT32_TO_LE(msg->msnslp_header.ack_sub_id); - header.ack_size = GUINT64_TO_LE(msg->msnslp_header.ack_size); - - memcpy(n, &header, 48); - n += 48; - - if (body != NULL) - { - memcpy(n, body, body_len); - - n += body_len; - } - - footer.value = GUINT32_TO_BE(msg->msnslp_footer.value); - - memcpy(n, &footer, 4); - n += 4; - } - else - { - if (body != NULL) - { - memcpy(n, body, body_len); - n += body_len; - } - } - - if (ret_size != NULL) - { - *ret_size = n - base; - - if (*ret_size > 1664) - *ret_size = 1664; - } - - return base; -} - -void -msn_message_set_flag(MsnMessage *msg, char flag) -{ - g_return_if_fail(msg != NULL); - g_return_if_fail(flag != 0); - - msg->flag = flag; -} - -char -msn_message_get_flag(const MsnMessage *msg) -{ - g_return_val_if_fail(msg != NULL, 0); - - return msg->flag; -} - -void -msn_message_set_bin_data(MsnMessage *msg, const void *data, size_t len) -{ - g_return_if_fail(msg != NULL); - - /* There is no need to waste memory on data we cannot send anyway */ - if (len > 1664) - len = 1664; - - if (msg->body != NULL) - g_free(msg->body); - - if (data != NULL && len > 0) - { - msg->body = botch_utf((gchar *)data, len, &msg->body_len); /* yaz */ - } - else - { - msg->body = NULL; - msg->body_len = 0; - } -} - -const void * -msn_message_get_bin_data(const MsnMessage *msg, size_t *len) -{ - g_return_val_if_fail(msg != NULL, NULL); - - if (len) - *len = msg->body_len; - - return msg->body; -} - -void -msn_message_set_content_type(MsnMessage *msg, const char *type) -{ - g_return_if_fail(msg != NULL); - - if (msg->content_type != NULL) - g_free(msg->content_type); - - msg->content_type = (type != NULL) ? g_strdup(type) : NULL; -} - -const char * -msn_message_get_content_type(const MsnMessage *msg) -{ - g_return_val_if_fail(msg != NULL, NULL); - - return msg->content_type; -} - -void -msn_message_set_charset(MsnMessage *msg, const char *charset) -{ - g_return_if_fail(msg != NULL); - - if (msg->charset != NULL) - g_free(msg->charset); - - msg->charset = (charset != NULL) ? g_strdup(charset) : NULL; -} - -const char * -msn_message_get_charset(const MsnMessage *msg) -{ - g_return_val_if_fail(msg != NULL, NULL); - - return msg->charset; -} - -void -msn_message_set_attr(MsnMessage *msg, const char *attr, const char *value) -{ - const char *temp; - char *new_attr; - - g_return_if_fail(msg != NULL); - g_return_if_fail(attr != NULL); - - temp = msn_message_get_attr(msg, attr); - - if (value == NULL) - { - if (temp != NULL) - { - GList *l; - - for (l = msg->attr_list; l != NULL; l = l->next) - { - if (!g_ascii_strcasecmp(l->data, attr)) - { - msg->attr_list = g_list_remove(msg->attr_list, l->data); - - break; - } - } - - g_hash_table_remove(msg->attr_table, attr); - } - - return; - } - - new_attr = g_strdup(attr); - - g_hash_table_insert(msg->attr_table, new_attr, g_strdup(value)); - - if (temp == NULL) - msg->attr_list = g_list_append(msg->attr_list, new_attr); -} - -const char * -msn_message_get_attr(const MsnMessage *msg, const char *attr) -{ - g_return_val_if_fail(msg != NULL, NULL); - g_return_val_if_fail(attr != NULL, NULL); - - return g_hash_table_lookup(msg->attr_table, attr); -} - -GHashTable * -msn_message_get_hashtable_from_body(const MsnMessage *msg) -{ - GHashTable *table; - size_t body_len; - const char *body; - char **elems, **cur, **tokens, *body_str; - - g_return_val_if_fail(msg != NULL, NULL); - - table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); - - body = msn_message_get_bin_data(msg, &body_len); - - g_return_val_if_fail(body != NULL, NULL); - - body_str = g_strndup(body, body_len); - elems = g_strsplit(body_str, "\r\n", 0); - g_free(body_str); - - for (cur = elems; *cur != NULL; cur++) - { - if (**cur == '\0') - break; - - tokens = g_strsplit(*cur, ": ", 2); - - if (tokens[0] != NULL && tokens[1] != NULL) - g_hash_table_insert(table, tokens[0], tokens[1]); - - g_free(tokens); - } - - g_strfreev(elems); - - return table; -} - -char * -msn_message_to_string(MsnMessage *msg) -{ - size_t body_len; - const char *body; - - g_return_val_if_fail(msg != NULL, NULL); - g_return_val_if_fail(msg->type == MSN_MSG_TEXT, NULL); - - body = msn_message_get_bin_data(msg, &body_len); - - return g_strndup(body, body_len); -} - -void -msn_message_show_readable(MsnMessage *msg, const char *info, - gboolean text_body) -{ - GString *str; - size_t body_len; - const char *body; - GList *l; - - g_return_if_fail(msg != NULL); - - str = g_string_new(NULL); - - /* Standard header. */ - if (msg->charset == NULL) - { - g_string_append_printf(str, - "MIME-Version: 1.0\r\n" - "Content-Type: %s\r\n", - msg->content_type); - } - else - { - g_string_append_printf(str, - "MIME-Version: 1.0\r\n" - "Content-Type: %s; charset=%s\r\n", - msg->content_type, msg->charset); - } - - for (l = msg->attr_list; l; l = l->next) - { - char *key; - const char *value; - - key = l->data; - value = msn_message_get_attr(msg, key); - - g_string_append_printf(str, "%s: %s\r\n", key, value); - } - - g_string_append(str, "\r\n"); - - body = msn_message_get_bin_data(msg, &body_len); - - if (msg->msnslp_message) - { - g_string_append_printf(str, "Session ID: %u\r\n", msg->msnslp_header.session_id); - g_string_append_printf(str, "ID: %u\r\n", msg->msnslp_header.id); - g_string_append_printf(str, "Offset: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.offset); - g_string_append_printf(str, "Total size: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.total_size); - g_string_append_printf(str, "Length: %u\r\n", msg->msnslp_header.length); - g_string_append_printf(str, "Flags: 0x%x\r\n", msg->msnslp_header.flags); - g_string_append_printf(str, "ACK ID: %u\r\n", msg->msnslp_header.ack_id); - g_string_append_printf(str, "SUB ID: %u\r\n", msg->msnslp_header.ack_sub_id); - g_string_append_printf(str, "ACK Size: %" G_GUINT64_FORMAT "\r\n", msg->msnslp_header.ack_size); - -#ifdef MSN_DEBUG_SLP_VERBOSE - if (body != NULL) - { - if (text_body) - { - g_string_append_len(str, body, body_len); - if (body[body_len - 1] == '\0') - { - str->len--; - g_string_append(str, " 0x00"); - } - g_string_append(str, "\r\n"); - } - else - { - int i; - for (i = 0; i < msg->body_len; i++) - { - g_string_append_printf(str, "%.2hhX ", body[i]); - if ((i % 16) == 15) - g_string_append(str, "\r\n"); - } - g_string_append(str, "\r\n"); - } - } -#endif - - g_string_append_printf(str, "Footer: %u\r\n", msg->msnslp_footer.value); - } - else - { - if (body != NULL) - { - g_string_append_len(str, body, body_len); - g_string_append(str, "\r\n"); - } - } - - purple_debug_info("msn", "Message %s:\n{%s}\n", info, str->str); - - g_string_free(str, TRUE); -}
--- a/libpurple/protocols/msnp9/msg.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,349 +0,0 @@ -/** - * @file msg.h Message functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_MSG_H_ -#define _MSN_MSG_H_ - -typedef struct _MsnMessage MsnMessage; - -#include "session.h" -#include "user.h" - -#include "command.h" -#include "transaction.h" - -typedef void (*MsnMsgCb)(MsnMessage *, void *data); - -/* -typedef enum -{ - MSN_MSG_NORMAL, - MSN_MSG_SLP_SB, - MSN_MSG_SLP_DC - -} MsnMsgType; -*/ - -typedef enum -{ - MSN_MSG_UNKNOWN, - MSN_MSG_TEXT, - MSN_MSG_TYPING, - MSN_MSG_CAPS, - MSN_MSG_SLP, - MSN_MSG_NUDGE - -} MsnMsgType; - -typedef enum -{ - MSN_MSG_ERROR_NONE, /**< No error. */ - MSN_MSG_ERROR_TIMEOUT, /**< The message timedout. */ - MSN_MSG_ERROR_NAK, /**< The message could not be sent. */ - MSN_MSG_ERROR_SB, /**< The error comes from the switchboard. */ - MSN_MSG_ERROR_UNKNOWN /**< An unknown error occurred. */ - -} MsnMsgErrorType; - -typedef struct -{ - guint32 session_id; - guint32 id; - guint64 offset; - guint64 total_size; - guint32 length; - guint32 flags; - guint32 ack_id; - guint32 ack_sub_id; - guint64 ack_size; - -} MsnSlpHeader; - -typedef struct -{ - guint32 value; - -} MsnSlpFooter; - -/** - * A message. - */ -struct _MsnMessage -{ - size_t ref_count; /**< The reference count. */ - - MsnMsgType type; - - gboolean msnslp_message; - - char *remote_user; - char flag; - - char *content_type; - char *charset; - char *body; - gsize body_len; - - MsnSlpHeader msnslp_header; - MsnSlpFooter msnslp_footer; - - GHashTable *attr_table; - GList *attr_list; - - gboolean ack_ref; /**< A flag that states if this message has - been ref'ed for using it in a callback. */ - - MsnCommand *cmd; - MsnTransaction *trans; - - MsnMsgCb ack_cb; /**< The callback to call when we receive an ACK of this - message. */ - MsnMsgCb nak_cb; /**< The callback to call when we receive a NAK of this - message. */ - void *ack_data; /**< The data used by callbacks. */ - - MsnMsgErrorType error; /**< The error of the message. */ -}; - -/** - * Creates a new, empty message. - * - * @return A new message. - */ -MsnMessage *msn_message_new(MsnMsgType type); - -/** - * Creates a new, empty MSNSLP message. - * - * @return A new MSNSLP message. - */ -MsnMessage *msn_message_new_msnslp(void); - -/** - * Creates a new nudge message. - * - * @return A new nudge message. - */ -MsnMessage *msn_message_new_nudge(void); - -/** - * Creates a new plain message. - * - * @return A new plain message. - */ -MsnMessage *msn_message_new_plain(const char *message); - -/** - * Creates a MSNSLP ack message. - * - * @param acked_msg The message to acknowledge. - * - * @return A new MSNSLP ack message. - */ -MsnMessage *msn_message_new_msnslp_ack(MsnMessage *acked_msg); - -/** - * Creates a new message based off a command. - * - * @param session The MSN session. - * @param cmd The command. - * - * @return The new message. - */ -MsnMessage *msn_message_new_from_cmd(MsnSession *session, MsnCommand *cmd); - -/** - * Parses the payload of a message. - * - * @param msg The message. - * @param payload The payload. - * @param payload_len The length of the payload. - */ -void msn_message_parse_payload(MsnMessage *msg, const char *payload, - size_t payload_len); - -/** - * Destroys a message. - * - * @param msg The message to destroy. - */ -void msn_message_destroy(MsnMessage *msg); - -/** - * Increments the reference count on a message. - * - * @param msg The message. - * - * @return @a msg - */ -MsnMessage *msn_message_ref(MsnMessage *msg); - -/** - * Decrements the reference count on a message. - * - * This will destroy the structure if the count hits 0. - * - * @param msg The message. - * - * @return @a msg, or @c NULL if the new count is 0. - */ -MsnMessage *msn_message_unref(MsnMessage *msg); - -/** - * Generates the payload data of a message. - * - * @param msg The message. - * @param ret_size The returned size of the payload. - * - * @return The payload data of the message. - */ -char *msn_message_gen_payload(MsnMessage *msg, size_t *ret_size); - -/** - * Sets the flag for an outgoing message. - * - * @param msg The message. - * @param flag The flag. - */ -void msn_message_set_flag(MsnMessage *msg, char flag); - -/** - * Returns the flag for an outgoing message. - * - * @param msg The message. - * - * @return The flag. - */ -char msn_message_get_flag(const MsnMessage *msg); - -#if 0 -/** - * Sets the body of a message. - * - * @param msg The message. - * @param body The body of the message. - */ -void msn_message_set_body(MsnMessage *msg, const char *body); - -/** - * Returns the body of the message. - * - * @param msg The message. - * - * @return The body of the message. - */ -const char *msn_message_get_body(const MsnMessage *msg); -#endif -/** - * Sets the binary content of the message. - * - * @param msg The message. - * @param data The binary data. - * @param len The length of the data. - */ -void msn_message_set_bin_data(MsnMessage *msg, const void *data, size_t len); - -/** - * Returns the binary content of the message. - * - * @param msg The message. - * @param len The returned length of the data. - * - * @return The binary data. - */ -const void *msn_message_get_bin_data(const MsnMessage *msg, size_t *len); - -/** - * Sets the content type in a message. - * - * @param msg The message. - * @param type The content-type. - */ -void msn_message_set_content_type(MsnMessage *msg, const char *type); - -/** - * Returns the content type in a message. - * - * @param msg The message. - * - * @return The content-type. - */ -const char *msn_message_get_content_type(const MsnMessage *msg); - -/** - * Sets the charset in a message. - * - * @param msg The message. - * @param charset The charset. - */ -void msn_message_set_charset(MsnMessage *msg, const char *charset); - -/** - * Returns the charset in a message. - * - * @param msg The message. - * - * @return The charset. - */ -const char *msn_message_get_charset(const MsnMessage *msg); - -/** - * Sets an attribute in a message. - * - * @param msg The message. - * @param attr The attribute name. - * @param value The attribute value. - */ -void msn_message_set_attr(MsnMessage *msg, const char *attr, - const char *value); - -/** - * Returns an attribute from a message. - * - * @param msg The message. - * @param attr The attribute. - * - * @return The value, or @c NULL if not found. - */ -const char *msn_message_get_attr(const MsnMessage *msg, const char *attr); - -/** - * Parses the body and returns it in the form of a hashtable. - * - * @param msg The message. - * - * @return The resulting hashtable. - */ -GHashTable *msn_message_get_hashtable_from_body(const MsnMessage *msg); - -void msn_message_show_readable(MsnMessage *msg, const char *info, - gboolean text_body); - -void msn_message_parse_slp_body(MsnMessage *msg, const char *body, - size_t len); - -char *msn_message_gen_slp_body(MsnMessage *msg, size_t *ret_size); - -char *msn_message_to_string(MsnMessage *msg); - -#endif /* _MSN_MSG_H_ */
--- a/libpurple/protocols/msnp9/msn-utils.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,441 +0,0 @@ -/** - * @file msn-utils.c Utility functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "msn-utils.h" - -void -msn_parse_format(const char *mime, char **pre_ret, char **post_ret) -{ - char *cur; - GString *pre = g_string_new(NULL); - GString *post = g_string_new(NULL); - unsigned int colors[3]; - - if (pre_ret != NULL) *pre_ret = NULL; - if (post_ret != NULL) *post_ret = NULL; - - cur = strstr(mime, "FN="); - - if (cur && (*(cur = cur + 3) != ';')) - { - pre = g_string_append(pre, "<FONT FACE=\""); - - while (*cur && *cur != ';') - { - pre = g_string_append_c(pre, *cur); - cur++; - } - - pre = g_string_append(pre, "\">"); - post = g_string_prepend(post, "</FONT>"); - } - - cur = strstr(mime, "EF="); - - if (cur && (*(cur = cur + 3) != ';')) - { - while (*cur && *cur != ';') - { - pre = g_string_append_c(pre, '<'); - pre = g_string_append_c(pre, *cur); - pre = g_string_append_c(pre, '>'); - post = g_string_prepend_c(post, '>'); - post = g_string_prepend_c(post, *cur); - post = g_string_prepend_c(post, '/'); - post = g_string_prepend_c(post, '<'); - cur++; - } - } - - cur = strstr(mime, "CO="); - - if (cur && (*(cur = cur + 3) != ';')) - { - int i; - - i = sscanf(cur, "%02x%02x%02x;", &colors[0], &colors[1], &colors[2]); - - if (i > 0) - { - char tag[64]; - - if (i == 1) - { - colors[1] = 0; - colors[2] = 0; - } - else if (i == 2) - { - unsigned int temp = colors[0]; - - colors[0] = colors[1]; - colors[1] = temp; - colors[2] = 0; - } - else if (i == 3) - { - unsigned int temp = colors[2]; - - colors[2] = colors[0]; - colors[0] = temp; - } - - g_snprintf(tag, sizeof(tag), - "<FONT COLOR=\"#%02hhx%02hhx%02hhx\">", - colors[0], colors[1], colors[2]); - - pre = g_string_append(pre, tag); - post = g_string_prepend(post, "</FONT>"); - } - } - - cur = strstr(mime, "RL="); - - if (cur && (*(cur = cur + 3) != ';')) - { - if (*cur == '1') - { - /* RTL text was received */ - pre = g_string_append(pre, "<SPAN style=\"direction:rtl;text-align:right;\">"); - post = g_string_prepend(post, "</SPAN>"); - } - } - - cur = g_strdup(purple_url_decode(pre->str)); - g_string_free(pre, TRUE); - - if (pre_ret != NULL) - *pre_ret = cur; - else - g_free(cur); - - cur = g_strdup(purple_url_decode(post->str)); - g_string_free(post, TRUE); - - if (post_ret != NULL) - *post_ret = cur; - else - g_free(cur); -} - -/* - * We need this because we're only supposed to encode spaces in the font - * names. purple_url_encode() isn't acceptable. - */ -static const char * -encode_spaces(const char *str) -{ - static char buf[BUF_LEN]; - const char *c; - char *d; - - g_return_val_if_fail(str != NULL, NULL); - - for (c = str, d = buf; *c != '\0'; c++) - { - if (*c == ' ') - { - *d++ = '%'; - *d++ = '2'; - *d++ = '0'; - } - else - *d++ = *c; - } - *d = '\0'; - - return buf; -} - -/* - * Taken from the zephyr plugin. - * This parses HTML formatting (put out by one of the gtkimhtml widgets - * and converts it to msn formatting. It doesn't deal with the tag closing, - * but gtkimhtml widgets give valid html. - * It currently deals properly with <b>, <u>, <i>, <font face=...>, - * <font color=...>, <span dir=...>, <span style="direction: ...">. - * It ignores <font back=...> and <font size=...> - */ -void -msn_import_html(const char *html, char **attributes, char **message) -{ - int len, retcount = 0; - const char *c; - char *msg; - char *fontface = NULL; - char fonteffect[4]; - char fontcolor[7]; - char direction = '0'; - - gboolean has_bold = FALSE; - gboolean has_italic = FALSE; - gboolean has_underline = FALSE; - gboolean has_strikethrough = FALSE; - - g_return_if_fail(html != NULL); - g_return_if_fail(attributes != NULL); - g_return_if_fail(message != NULL); - - len = strlen(html); - msg = g_malloc0(len + 1); - - memset(fontcolor, 0, sizeof(fontcolor)); - strcat(fontcolor, "0"); - memset(fonteffect, 0, sizeof(fonteffect)); - - for (c = html; *c != '\0';) - { - if (*c == '<') - { - if (!g_ascii_strncasecmp(c + 1, "br>", 3)) - { - msg[retcount++] = '\r'; - msg[retcount++] = '\n'; - c += 4; - } - else if (!g_ascii_strncasecmp(c + 1, "i>", 2)) - { - if (!has_italic) - { - strcat(fonteffect, "I"); - has_italic = TRUE; - } - c += 3; - } - else if (!g_ascii_strncasecmp(c + 1, "b>", 2)) - { - if (!has_bold) - { - strcat(fonteffect, "B"); - has_bold = TRUE; - } - c += 3; - } - else if (!g_ascii_strncasecmp(c + 1, "u>", 2)) - { - if (!has_underline) - { - strcat(fonteffect, "U"); - has_underline = TRUE; - } - c += 3; - } - else if (!g_ascii_strncasecmp(c + 1, "s>", 2)) - { - if (!has_strikethrough) - { - strcat(fonteffect, "S"); - has_strikethrough = TRUE; - } - c += 3; - } - else if (!g_ascii_strncasecmp(c + 1, "a href=\"", 8)) - { - c += 9; - - if (!g_ascii_strncasecmp(c, "mailto:", 7)) - c += 7; - - while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2)) - msg[retcount++] = *c++; - - if (*c != '\0') - c += 2; - - /* ignore descriptive string */ - while ((*c != '\0') && g_ascii_strncasecmp(c, "</a>", 4)) - c++; - - if (*c != '\0') - c += 4; - } - else if (!g_ascii_strncasecmp(c + 1, "span", 4)) - { - /* Bi-directional text support using CSS properties in span tags */ - c += 5; - - while (*c != '\0' && *c != '>') - { - while (*c == ' ') - c++; - if (!g_ascii_strncasecmp(c, "dir=\"rtl\"", 9)) - { - c += 9; - direction = '1'; - } - else if (!g_ascii_strncasecmp(c, "style=\"", 7)) - { - /* Parse inline CSS attributes */ - char *attributes; - int attr_len = 0; - c += 7; - while (*(c + attr_len) != '\0' && *(c + attr_len) != '"') - attr_len++; - if (*(c + attr_len) == '"') - { - char *attr_dir; - attributes = g_strndup(c, attr_len); - attr_dir = purple_markup_get_css_property(attributes, "direction"); - if (attr_dir && (!g_ascii_strncasecmp(attr_dir, "RTL", 3))) - direction = '1'; - g_free(attr_dir); - g_free(attributes); - } - - } - else - { - c++; - } - } - if (*c == '>') - c++; - } - else if (!g_ascii_strncasecmp(c + 1, "font", 4)) - { - c += 5; - - while ((*c != '\0') && !g_ascii_strncasecmp(c, " ", 1)) - c++; - - if (!g_ascii_strncasecmp(c, "color=\"#", 7)) - { - c += 8; - - fontcolor[0] = *(c + 4); - fontcolor[1] = *(c + 5); - fontcolor[2] = *(c + 2); - fontcolor[3] = *(c + 3); - fontcolor[4] = *c; - fontcolor[5] = *(c + 1); - - c += 8; - } - else if (!g_ascii_strncasecmp(c, "face=\"", 6)) - { - const char *end = NULL; - const char *comma = NULL; - unsigned int namelen = 0; - - c += 6; - end = strchr(c, '\"'); - comma = strchr(c, ','); - - if (comma == NULL || comma > end) - namelen = (unsigned int)(end - c); - else - namelen = (unsigned int)(comma - c); - - fontface = g_strndup(c, namelen); - c = end + 2; - } - else - { - /* Drop all unrecognized/misparsed font tags */ - while ((*c != '\0') && g_ascii_strncasecmp(c, "\">", 2)) - c++; - - if (*c != '\0') - c += 2; - } - } - else - { - while ((*c != '\0') && (*c != '>')) - c++; - if (*c != '\0') - c++; - } - } - else if (*c == '&') - { - if (!g_ascii_strncasecmp(c, "<", 4)) - { - msg[retcount++] = '<'; - c += 4; - } - else if (!g_ascii_strncasecmp(c, ">", 4)) - { - msg[retcount++] = '>'; - c += 4; - } - else if (!g_ascii_strncasecmp(c, " ", 6)) - { - msg[retcount++] = ' '; - c += 6; - } - else if (!g_ascii_strncasecmp(c, """, 6)) - { - msg[retcount++] = '"'; - c += 6; - } - else if (!g_ascii_strncasecmp(c, "&", 5)) - { - msg[retcount++] = '&'; - c += 5; - } - else if (!g_ascii_strncasecmp(c, "'", 6)) - { - msg[retcount++] = '\''; - c += 6; - } - else - msg[retcount++] = *c++; - } - else - msg[retcount++] = *c++; - } - - if (fontface == NULL) - fontface = g_strdup("MS Sans Serif"); - - *attributes = g_strdup_printf("FN=%s; EF=%s; CO=%s; PF=0; RL=%c", - encode_spaces(fontface), - fonteffect, fontcolor, direction); - *message = g_strdup(msg); - - g_free(fontface); - g_free(msg); -} - -void -msn_parse_socket(const char *str, char **ret_host, int *ret_port) -{ - char *host; - char *c; - int port; - - host = g_strdup(str); - - if ((c = strchr(host, ':')) != NULL) - { - *c = '\0'; - port = atoi(c + 1); - } - else - port = 1863; - - *ret_host = host; - *ret_port = port; -}
--- a/libpurple/protocols/msnp9/msn-utils.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,51 +0,0 @@ -/** - * @file msn-utils.h Utility functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_UTILS_H_ -#define _MSN_UTILS_H_ - -/** - * Parses the MSN message formatting into a format compatible with Purple. - * - * @param mime The mime header with the formatting. - * @param pre_ret The returned prefix string. - * @param post_ret The returned postfix string. - * - * @return The new message. - */ -void msn_parse_format(const char *mime, char **pre_ret, char **post_ret); - -/** - * Parses the Purple message formatting (html) into the MSN format. - * - * @param html The html message to format. - * @param attributes The returned attributes string. - * @param message The returned message string. - * - * @return The new message. - */ -void msn_import_html(const char *html, char **attributes, char **message); - -void msn_parse_socket(const char *str, char **ret_host, int *ret_port); - -#endif /* _MSN_UTILS_H_ */
--- a/libpurple/protocols/msnp9/msn.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2368 +0,0 @@ -/** - * @file msn.c The MSN protocol plugin - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#define PHOTO_SUPPORT 1 - -#include <glib.h> - -#include "msn.h" -#include "accountopt.h" -#include "eventloop.h" -#include "msg.h" -#include "page.h" -#include "pluginpref.h" -#include "prefs.h" -#include "session.h" -#include "smiley.h" -#include "state.h" -#include "util.h" -#include "cmds.h" -#include "core.h" -#include "prpl.h" -#include "msn-utils.h" -#include "version.h" - -#include "switchboard.h" -#include "notification.h" -#include "sync.h" -#include "slplink.h" - -#if PHOTO_SUPPORT -#include "imgstore.h" -#endif - -typedef struct -{ - PurpleConnection *gc; - const char *passport; - -} MsnMobileData; - -typedef struct -{ - PurpleConnection *gc; - char *name; - -} MsnGetInfoData; - -typedef struct -{ - MsnGetInfoData *info_data; - char *stripped; - char *url_buffer; - PurpleNotifyUserInfo *user_info; - char *photo_url_text; - -} MsnGetInfoStepTwoData; - -typedef struct -{ - PurpleConnection *gc; - const char *who; - char *msg; - PurpleMessageFlags flags; - time_t when; -} MsnIMData; - -typedef struct -{ - char *smile; - MsnObject *obj; -} MsnEmoticon; - -static const char * -msn_normalize(const PurpleAccount *account, const char *str) -{ - static char buf[BUF_LEN]; - char *tmp; - - g_return_val_if_fail(str != NULL, NULL); - - g_snprintf(buf, sizeof(buf), "%s%s", str, - (strchr(str, '@') ? "" : "@hotmail.com")); - - tmp = g_utf8_strdown(buf, -1); - strncpy(buf, tmp, sizeof(buf)); - g_free(tmp); - - return buf; -} - -static gboolean -msn_send_attention(PurpleConnection *gc, const char *username, guint type) -{ - MsnMessage *msg; - MsnSession *session; - MsnSwitchBoard *swboard; - - msg = msn_message_new_nudge(); - session = gc->proto_data; - swboard = msn_session_get_swboard(session, username, MSN_SB_FLAG_IM); - - if (swboard == NULL) - return FALSE; - - msn_switchboard_send_msg(swboard, msg, TRUE); - msn_message_destroy(msg); - - return TRUE; -} - -static GList * -msn_attention_types(PurpleAccount *account) -{ - static GList *list = NULL; - - if (!list) { - list = g_list_append(list, purple_attention_type_new("Nudge", _("Nudge"), - _("%s has nudged you!"), _("Nudging %s..."))); - } - - return list; -} - -static GHashTable * -msn_get_account_text_table(PurpleAccount *unused) -{ - GHashTable *table; - - table = g_hash_table_new(g_str_hash, g_str_equal); - - g_hash_table_insert(table, "login_label", (gpointer)_("Email Address...")); - - return table; -} - -static PurpleCmdRet -msn_cmd_nudge(PurpleConversation *conv, const gchar *cmd, gchar **args, gchar **error, void *data) -{ - PurpleAccount *account = purple_conversation_get_account(conv); - PurpleConnection *gc = purple_account_get_connection(account); - const gchar *username; - - username = purple_conversation_get_name(conv); - - purple_prpl_send_attention(gc, username, MSN_NUDGE); - - return PURPLE_CMD_RET_OK; -} - -static void -msn_act_id(PurpleConnection *gc, const char *entry) -{ - MsnCmdProc *cmdproc; - MsnSession *session; - PurpleAccount *account; - const char *alias; - gchar *tmp; - gsize dummy; - - session = gc->proto_data; - cmdproc = session->notification->cmdproc; - account = purple_connection_get_account(gc); - - if(entry && strlen(entry)) { - tmp = botch_utf(entry, strlen(entry), &dummy); - alias = purple_url_encode(tmp); - g_free(tmp); - } - else - alias = ""; - - if (strlen(alias) > BUDDY_ALIAS_MAXLEN) - { - purple_notify_error(gc, NULL, - _("Your new MSN friendly name is too long."), NULL); - return; - } - - msn_cmdproc_send(cmdproc, "REA", "%s %s", - purple_account_get_username(account), - alias); -} - -static void -msn_set_prp(PurpleConnection *gc, const char *type, const char *entry) -{ - MsnCmdProc *cmdproc; - MsnSession *session; - - session = gc->proto_data; - cmdproc = session->notification->cmdproc; - - if (entry == NULL || *entry == '\0') - { - msn_cmdproc_send(cmdproc, "PRP", "%s", type); - } - else - { - msn_cmdproc_send(cmdproc, "PRP", "%s %s", type, - purple_url_encode(entry)); - } -} - -static void -msn_set_home_phone_cb(PurpleConnection *gc, const char *entry) -{ - msn_set_prp(gc, "PHH", entry); -} - -static void -msn_set_work_phone_cb(PurpleConnection *gc, const char *entry) -{ - msn_set_prp(gc, "PHW", entry); -} - -static void -msn_set_mobile_phone_cb(PurpleConnection *gc, const char *entry) -{ - msn_set_prp(gc, "PHM", entry); -} - -static void -enable_msn_pages_cb(PurpleConnection *gc) -{ - msn_set_prp(gc, "MOB", "Y"); -} - -static void -disable_msn_pages_cb(PurpleConnection *gc) -{ - msn_set_prp(gc, "MOB", "N"); -} - -static void -send_to_mobile(PurpleConnection *gc, const char *who, const char *entry) -{ - MsnTransaction *trans; - MsnSession *session; - MsnCmdProc *cmdproc; - MsnPage *page; - char *payload; - size_t payload_len; - - session = gc->proto_data; - cmdproc = session->notification->cmdproc; - - page = msn_page_new(); - msn_page_set_body(page, entry); - - payload = msn_page_gen_payload(page, &payload_len); - - trans = msn_transaction_new(cmdproc, "PGD", "%s 1 %d", who, payload_len); - - msn_transaction_set_payload(trans, payload, payload_len); - - msn_page_destroy(page); - - msn_cmdproc_send_trans(cmdproc, trans); -} - -static void -send_to_mobile_cb(MsnMobileData *data, const char *entry) -{ - send_to_mobile(data->gc, data->passport, entry); - g_free(data); -} - -static void -close_mobile_page_cb(MsnMobileData *data, const char *entry) -{ - g_free(data); -} - -/* -- */ - -static void -msn_show_set_friendly_name(PurplePluginAction *action) -{ - PurpleConnection *gc; - - gc = (PurpleConnection *) action->context; - - purple_request_input(gc, NULL, _("Set your friendly name."), - _("This is the name that other MSN buddies will " - "see you as."), - purple_connection_get_display_name(gc), FALSE, FALSE, NULL, - _("OK"), G_CALLBACK(msn_act_id), - _("Cancel"), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); -} - -static void -msn_show_set_home_phone(PurplePluginAction *action) -{ - PurpleConnection *gc; - MsnSession *session; - - gc = (PurpleConnection *) action->context; - session = gc->proto_data; - - purple_request_input(gc, NULL, _("Set your home phone number."), NULL, - msn_user_get_home_phone(session->user), FALSE, FALSE, NULL, - _("OK"), G_CALLBACK(msn_set_home_phone_cb), - _("Cancel"), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); -} - -static void -msn_show_set_work_phone(PurplePluginAction *action) -{ - PurpleConnection *gc; - MsnSession *session; - - gc = (PurpleConnection *) action->context; - session = gc->proto_data; - - purple_request_input(gc, NULL, _("Set your work phone number."), NULL, - msn_user_get_work_phone(session->user), FALSE, FALSE, NULL, - _("OK"), G_CALLBACK(msn_set_work_phone_cb), - _("Cancel"), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); -} - -static void -msn_show_set_mobile_phone(PurplePluginAction *action) -{ - PurpleConnection *gc; - MsnSession *session; - - gc = (PurpleConnection *) action->context; - session = gc->proto_data; - - purple_request_input(gc, NULL, _("Set your mobile phone number."), NULL, - msn_user_get_mobile_phone(session->user), FALSE, FALSE, NULL, - _("OK"), G_CALLBACK(msn_set_mobile_phone_cb), - _("Cancel"), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); -} - -static void -msn_show_set_mobile_pages(PurplePluginAction *action) -{ - PurpleConnection *gc; - - gc = (PurpleConnection *) action->context; - - purple_request_action(gc, NULL, _("Allow MSN Mobile pages?"), - _("Do you want to allow or disallow people on " - "your buddy list to send you MSN Mobile pages " - "to your cell phone or other mobile device?"), - PURPLE_DEFAULT_ACTION_NONE, - purple_connection_get_account(gc), NULL, NULL, - gc, 3, - _("Allow"), G_CALLBACK(enable_msn_pages_cb), - _("Disallow"), G_CALLBACK(disable_msn_pages_cb), - _("Cancel"), NULL); -} - -static void -msn_show_hotmail_inbox(PurplePluginAction *action) -{ - PurpleConnection *gc; - MsnSession *session; - - gc = (PurpleConnection *) action->context; - session = gc->proto_data; - - if (session->passport_info.file == NULL) - { - purple_notify_error(gc, NULL, - _("This Hotmail account may not be active."), NULL); - return; - } - - purple_notify_uri(gc, session->passport_info.file); -} - -static void -show_send_to_mobile_cb(PurpleBlistNode *node, gpointer ignored) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - MsnSession *session; - MsnMobileData *data; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(buddy->account); - - session = gc->proto_data; - - data = g_new0(MsnMobileData, 1); - data->gc = gc; - data->passport = buddy->name; - - purple_request_input(gc, NULL, _("Send a mobile message."), NULL, - NULL, TRUE, FALSE, NULL, - _("Page"), G_CALLBACK(send_to_mobile_cb), - _("Close"), G_CALLBACK(close_mobile_page_cb), - purple_connection_get_account(gc), purple_buddy_get_name(buddy), NULL, - data); -} - -static gboolean -msn_offline_message(const PurpleBuddy *buddy) { - MsnUser *user; - if (buddy == NULL) - return FALSE; - user = buddy->proto_data; - return user && user->mobile; -} - -static void -initiate_chat_cb(PurpleBlistNode *node, gpointer data) -{ - PurpleBuddy *buddy; - PurpleConnection *gc; - - MsnSession *session; - MsnSwitchBoard *swboard; - - g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); - - buddy = (PurpleBuddy *) node; - gc = purple_account_get_connection(buddy->account); - - session = gc->proto_data; - - swboard = msn_switchboard_new(session); - msn_switchboard_request(swboard); - msn_switchboard_request_add_user(swboard, buddy->name); - - /* TODO: This might move somewhere else, after USR might be */ - swboard->chat_id = msn_switchboard_get_chat_id(); - swboard->conv = serv_got_joined_chat(gc, swboard->chat_id, "MSN Chat"); - swboard->flag = MSN_SB_FLAG_IM; - - purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv), - purple_account_get_username(buddy->account), NULL, PURPLE_CBFLAGS_NONE, TRUE); -} - -static void -t_msn_xfer_init(PurpleXfer *xfer) -{ - MsnSlpLink *slplink = xfer->data; - msn_slplink_request_ft(slplink, xfer); -} - -static PurpleXfer* -msn_new_xfer(PurpleConnection *gc, const char *who) -{ - MsnSession *session; - MsnSlpLink *slplink; - PurpleXfer *xfer; - - session = gc->proto_data; - - xfer = purple_xfer_new(gc->account, PURPLE_XFER_SEND, who); - if (xfer) - { - slplink = msn_session_get_slplink(session, who); - - xfer->data = slplink; - - purple_xfer_set_init_fnc(xfer, t_msn_xfer_init); - } - - return xfer; -} - -static void -msn_send_file(PurpleConnection *gc, const char *who, const char *file) -{ - PurpleXfer *xfer = msn_new_xfer(gc, who); - - if (file) - purple_xfer_request_accepted(xfer, file); - else - purple_xfer_request(xfer); -} - -static gboolean -msn_can_receive_file(PurpleConnection *gc, const char *who) -{ - PurpleAccount *account; - char *normal; - gboolean ret; - - account = purple_connection_get_account(gc); - - normal = g_strdup(msn_normalize(account, purple_account_get_username(account))); - - ret = strcmp(normal, msn_normalize(account, who)); - - g_free(normal); - - return ret; -} - -/************************************************************************** - * Protocol Plugin ops - **************************************************************************/ - -static const char * -msn_list_icon(PurpleAccount *a, PurpleBuddy *b) -{ - return "msn"; -} - -static char * -msn_status_text(PurpleBuddy *buddy) -{ - PurplePresence *presence; - PurpleStatus *status; - - presence = purple_buddy_get_presence(buddy); - status = purple_presence_get_active_status(presence); - - if (!purple_presence_is_available(presence) && !purple_presence_is_idle(presence)) - { - return g_strdup(purple_status_get_name(status)); - } - - return NULL; -} - -static void -msn_tooltip_text(PurpleBuddy *buddy, PurpleNotifyUserInfo *user_info, gboolean full) -{ - MsnUser *user; - PurplePresence *presence = purple_buddy_get_presence(buddy); - PurpleStatus *status = purple_presence_get_active_status(presence); - - user = buddy->proto_data; - - - if (purple_presence_is_online(presence)) - { - purple_notify_user_info_add_pair(user_info, _("Status"), - (purple_presence_is_idle(presence) ? _("Idle") : purple_status_get_name(status))); - } - - if (full && user) - { - purple_notify_user_info_add_pair(user_info, _("Has you"), - ((user->list_op & (1 << MSN_LIST_RL)) ? _("Yes") : _("No"))); - } - - /* XXX: This is being shown in non-full tooltips because the - * XXX: blocked icon overlay isn't always accurate for MSN. - * XXX: This can die as soon as purple_privacy_check() knows that - * XXX: this prpl always honors both the allow and deny lists. */ - /* While the above comment may be strictly correct (the privacy API needs - * rewriteing), purple_privacy_check() is going to be more accurate at - * indicating whether a particular buddy is going to be able to message - * you, which is the important information that this is trying to convey. */ - if (full && user) - { - const char *phone; - - purple_notify_user_info_add_pair(user_info, _("Blocked"), - ((user->list_op & (1 << MSN_LIST_BL)) ? _("Yes") : _("No"))); - - phone = msn_user_get_home_phone(user); - if (phone != NULL) - purple_notify_user_info_add_pair(user_info, _("Home Phone Number"), phone); - - phone = msn_user_get_work_phone(user); - if (phone != NULL) - purple_notify_user_info_add_pair(user_info, _("Work Phone Number"), phone); - - phone = msn_user_get_mobile_phone(user); - if (phone != NULL) - purple_notify_user_info_add_pair(user_info, _("Mobile Phone Number"), phone); - } -} - -static GList * -msn_status_types(PurpleAccount *account) -{ - PurpleStatusType *status; - GList *types = NULL; - - status = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, - NULL, NULL, FALSE, TRUE, FALSE); - types = g_list_append(types, status); - - status = purple_status_type_new_full(PURPLE_STATUS_AWAY, - NULL, NULL, FALSE, TRUE, FALSE); - types = g_list_append(types, status); - - status = purple_status_type_new_full(PURPLE_STATUS_AWAY, - "brb", _("Be Right Back"), FALSE, TRUE, FALSE); - types = g_list_append(types, status); - - status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, - "busy", _("Busy"), FALSE, TRUE, FALSE); - types = g_list_append(types, status); - - status = purple_status_type_new_full(PURPLE_STATUS_UNAVAILABLE, - "phone", _("On the Phone"), FALSE, TRUE, FALSE); - types = g_list_append(types, status); - - status = purple_status_type_new_full(PURPLE_STATUS_AWAY, - "lunch", _("Out to Lunch"), FALSE, TRUE, FALSE); - types = g_list_append(types, status); - - status = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE, - NULL, NULL, FALSE, TRUE, FALSE); - types = g_list_append(types, status); - - status = purple_status_type_new_full(PURPLE_STATUS_OFFLINE, - NULL, NULL, FALSE, TRUE, FALSE); - types = g_list_append(types, status); - - status = purple_status_type_new_full(PURPLE_STATUS_MOBILE, - "mobile", NULL, FALSE, FALSE, TRUE); - types = g_list_append(types, status); - - return types; -} - -static GList * -msn_actions(PurplePlugin *plugin, gpointer context) -{ - PurpleConnection *gc = (PurpleConnection *)context; - PurpleAccount *account; - const char *user; - - GList *m = NULL; - PurplePluginAction *act; - - act = purple_plugin_action_new(_("Set Friendly Name..."), - msn_show_set_friendly_name); - m = g_list_append(m, act); - m = g_list_append(m, NULL); - - act = purple_plugin_action_new(_("Set Home Phone Number..."), - msn_show_set_home_phone); - m = g_list_append(m, act); - - act = purple_plugin_action_new(_("Set Work Phone Number..."), - msn_show_set_work_phone); - m = g_list_append(m, act); - - act = purple_plugin_action_new(_("Set Mobile Phone Number..."), - msn_show_set_mobile_phone); - m = g_list_append(m, act); - m = g_list_append(m, NULL); - -#if 0 - act = purple_plugin_action_new(_("Enable/Disable Mobile Devices..."), - msn_show_set_mobile_support); - m = g_list_append(m, act); -#endif - - act = purple_plugin_action_new(_("Allow/Disallow Mobile Pages..."), - msn_show_set_mobile_pages); - m = g_list_append(m, act); - - account = purple_connection_get_account(gc); - user = msn_normalize(account, purple_account_get_username(account)); - - if ((strstr(user, "@hotmail.") != NULL) || - (strstr(user, "@msn.com") != NULL)) - { - m = g_list_append(m, NULL); - act = purple_plugin_action_new(_("Open Hotmail Inbox"), - msn_show_hotmail_inbox); - m = g_list_append(m, act); - } - - return m; -} - -static GList * -msn_buddy_menu(PurpleBuddy *buddy) -{ - MsnUser *user; - - GList *m = NULL; - PurpleMenuAction *act; - - g_return_val_if_fail(buddy != NULL, NULL); - - user = buddy->proto_data; - - if (user != NULL) - { - if (user->mobile) - { - act = purple_menu_action_new(_("Send to Mobile"), - PURPLE_CALLBACK(show_send_to_mobile_cb), - NULL, NULL); - m = g_list_append(m, act); - } - } - - if (g_ascii_strcasecmp(buddy->name, - purple_account_get_username(buddy->account))) - { - act = purple_menu_action_new(_("Initiate _Chat"), - PURPLE_CALLBACK(initiate_chat_cb), - NULL, NULL); - m = g_list_append(m, act); - } - - return m; -} - -static GList * -msn_blist_node_menu(PurpleBlistNode *node) -{ - if(PURPLE_BLIST_NODE_IS_BUDDY(node)) - { - return msn_buddy_menu((PurpleBuddy *) node); - } - else - { - return NULL; - } -} - -static void -msn_login(PurpleAccount *account) -{ - PurpleConnection *gc; - MsnSession *session; - const char *username; - const char *host; - gboolean http_method = FALSE; - int port; - - gc = purple_account_get_connection(account); - - if (!purple_ssl_is_supported()) - { - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NO_SSL_SUPPORT, - - _("SSL support is needed for MSN. Please install a supported " - "SSL library.")); - return; - } - - http_method = purple_account_get_bool(account, "http_method", FALSE); - - if (http_method) - host = purple_account_get_string(account, "http_method_server", MSN_HTTPCONN_SERVER); - else - host = purple_account_get_string(account, "server", MSN_SERVER); - port = purple_account_get_int(account, "port", MSN_PORT); - - session = msn_session_new(account); - - gc->proto_data = session; - gc->flags |= PURPLE_CONNECTION_HTML | PURPLE_CONNECTION_FORMATTING_WBFO | PURPLE_CONNECTION_NO_BGCOLOR | - PURPLE_CONNECTION_NO_FONTSIZE | PURPLE_CONNECTION_NO_URLDESC | PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY | - PURPLE_CONNECTION_ALLOW_ATTENTION; - - msn_session_set_login_step(session, MSN_LOGIN_STEP_START); - - /* Hmm, I don't like this. */ - /* XXX shx: Me neither */ - username = msn_normalize(account, purple_account_get_username(account)); - - if (strcmp(username, purple_account_get_username(account))) - purple_account_set_username(account, username); - - if (!msn_session_connect(session, host, port, http_method)) - purple_connection_error_reason(gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, - _("Unable to connect")); -} - -static void -msn_close(PurpleConnection *gc) -{ - MsnSession *session; - - session = gc->proto_data; - - g_return_if_fail(session != NULL); - - msn_session_destroy(session); - - gc->proto_data = NULL; -} - -static gboolean -msn_send_me_im(gpointer data) -{ - MsnIMData *imdata = data; - serv_got_im(imdata->gc, imdata->who, imdata->msg, imdata->flags, imdata->when); - g_free(imdata->msg); - g_free(imdata); - return FALSE; -} - -static GString* -msn_msg_emoticon_add(GString *current, MsnEmoticon *emoticon) -{ - MsnObject *obj; - char *strobj; - - if (emoticon == NULL) - return current; - - obj = emoticon->obj; - - if (!obj) - return current; - - strobj = msn_object_to_string(obj); - - if (current) - g_string_append_printf(current, "\t%s\t%s", - emoticon->smile, strobj); - else { - current = g_string_new(""); - g_string_printf(current,"%s\t%s", - emoticon->smile, strobj); - } - - g_free(strobj); - - return current; -} - -static void -msn_send_emoticons(MsnSwitchBoard *swboard, GString *body) -{ - MsnMessage *msg; - - g_return_if_fail(body != NULL); - - msg = msn_message_new(MSN_MSG_SLP); - msn_message_set_content_type(msg, "text/x-mms-emoticon"); - msn_message_set_flag(msg, 'N'); - msn_message_set_bin_data(msg, body->str, body->len); - - msn_switchboard_send_msg(swboard, msg, TRUE); - msn_message_destroy(msg); -} - -static void msn_emoticon_destroy(MsnEmoticon *emoticon) -{ - if (emoticon->obj) - msn_object_destroy(emoticon->obj); - g_free(emoticon->smile); - g_free(emoticon); -} - -static GSList* msn_msg_grab_emoticons(const char *msg, const char *username) -{ - GSList *list; - GList *smileys; - PurpleSmiley *smiley; - PurpleStoredImage *img; - char *ptr; - MsnEmoticon *emoticon; - int length; - - list = NULL; - smileys = purple_smileys_get_all(); - length = strlen(msg); - - for (; smileys; smileys = g_list_delete_link(smileys, smileys)) { - smiley = (PurpleSmiley*)smileys->data; - - ptr = g_strstr_len(msg, length, purple_smiley_get_shortcut(smiley)); - - if (!ptr) - continue; - - img = purple_smiley_get_stored_image(smiley); - - emoticon = g_new0(MsnEmoticon, 1); - emoticon->smile = g_strdup(purple_smiley_get_shortcut(smiley)); - emoticon->obj = msn_object_new_from_image(img, - purple_imgstore_get_filename(img), - username, MSN_OBJECT_EMOTICON); - - purple_imgstore_unref(img); - list = g_slist_prepend(list, emoticon); - } - - return list; -} - -static int -msn_send_im(PurpleConnection *gc, const char *who, const char *message, - PurpleMessageFlags flags) -{ - PurpleAccount *account; - PurpleBuddy *buddy = purple_find_buddy(gc->account, who); - MsnMessage *msg; - char *msgformat; - char *msgtext; - const char *username; - - account = purple_connection_get_account(gc); - username = purple_account_get_username(account); - - if (buddy) { - PurplePresence *p = purple_buddy_get_presence(buddy); - if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) { - char *text = purple_markup_strip_html(message); - send_to_mobile(gc, who, text); - g_free(text); - return 1; - } - } - - msn_import_html(message, &msgformat, &msgtext); - - if (strlen(msgtext) + strlen(msgformat) + strlen(DISPLAY_VERSION) > 1564) - { - g_free(msgformat); - g_free(msgtext); - - return -E2BIG; - } - - msg = msn_message_new_plain(msgtext); - msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat); - - g_free(msgformat); - g_free(msgtext); - - if (g_ascii_strcasecmp(who, username)) - { - MsnSession *session; - MsnSwitchBoard *swboard; - MsnEmoticon *smile; - GSList *smileys; - GString *emoticons = NULL; - - session = gc->proto_data; - swboard = msn_session_get_swboard(session, who, MSN_SB_FLAG_IM); - smileys = msn_msg_grab_emoticons(message, username); - - while (smileys) { - smile = (MsnEmoticon*)smileys->data; - emoticons = msn_msg_emoticon_add(emoticons,smile); - msn_emoticon_destroy(smile); - smileys = g_slist_delete_link(smileys, smileys); - } - - if (emoticons) { - msn_send_emoticons(swboard, emoticons); - g_string_free(emoticons, TRUE); - } - - msn_switchboard_send_msg(swboard, msg, TRUE); - } - else - { - char *body_str, *body_enc, *pre, *post; - const char *format; - MsnIMData *imdata = g_new0(MsnIMData, 1); - /* - * In MSN, you can't send messages to yourself, so - * we'll fake like we received it ;) - */ - body_str = msn_message_to_string(msg); - body_enc = g_markup_escape_text(body_str, -1); - g_free(body_str); - - format = msn_message_get_attr(msg, "X-MMS-IM-Format"); - msn_parse_format(format, &pre, &post); - body_str = g_strdup_printf("%s%s%s", pre ? pre : "", - body_enc ? body_enc : "", post ? post : ""); - g_free(body_enc); - g_free(pre); - g_free(post); - - serv_got_typing_stopped(gc, who); - imdata->gc = gc; - imdata->who = who; - imdata->msg = body_str; - imdata->flags = flags; - imdata->when = time(NULL); - purple_timeout_add(0, msn_send_me_im, imdata); - } - - msn_message_destroy(msg); - - return 1; -} - -static unsigned int -msn_send_typing(PurpleConnection *gc, const char *who, PurpleTypingState state) -{ - PurpleAccount *account; - MsnSession *session; - MsnSwitchBoard *swboard; - MsnMessage *msg; - - account = purple_connection_get_account(gc); - session = gc->proto_data; - - /* - * TODO: I feel like this should be "if (state != PURPLE_TYPING)" - * but this is how it was before, and I don't want to break - * anything. --KingAnt - */ - if (state == PURPLE_NOT_TYPING) - return 0; - - if (!g_ascii_strcasecmp(who, purple_account_get_username(account))) - { - /* We'll just fake it, since we're sending to ourself. */ - serv_got_typing(gc, who, MSN_TYPING_RECV_TIMEOUT, PURPLE_TYPING); - - return MSN_TYPING_SEND_TIMEOUT; - } - - swboard = msn_session_find_swboard(session, who); - - if (swboard == NULL || !msn_switchboard_can_send(swboard)) - return 0; - - swboard->flag |= MSN_SB_FLAG_IM; - - msg = msn_message_new(MSN_MSG_TYPING); - msn_message_set_content_type(msg, "text/x-msmsgscontrol"); - msn_message_set_flag(msg, 'U'); - msn_message_set_attr(msg, "TypingUser", - purple_account_get_username(account)); - msn_message_set_bin_data(msg, "\r\n", 2); - - msn_switchboard_send_msg(swboard, msg, FALSE); - - msn_message_destroy(msg); - - return MSN_TYPING_SEND_TIMEOUT; -} - -static void -msn_set_status(PurpleAccount *account, PurpleStatus *status) -{ - PurpleConnection *gc; - MsnSession *session; - - gc = purple_account_get_connection(account); - - if (gc != NULL) - { - session = gc->proto_data; - msn_change_status(session); - } -} - -static void -msn_set_idle(PurpleConnection *gc, int idle) -{ - MsnSession *session; - - session = gc->proto_data; - - msn_change_status(session); -} - -#if 0 -static void -fake_userlist_add_buddy(MsnUserList *userlist, - const char *who, int list_id, - const char *group_name) -{ - MsnUser *user; - static int group_id_c = 1; - int group_id; - - group_id = -1; - - if (group_name != NULL) - { - MsnGroup *group; - group = msn_group_new(userlist, group_id_c, group_name); - group_id = group_id_c++; - } - - user = msn_userlist_find_user(userlist, who); - - if (user == NULL) - { - user = msn_user_new(userlist, who, NULL); - msn_userlist_add_user(userlist, user); - } - else - if (user->list_op & (1 << list_id)) - { - if (list_id == MSN_LIST_FL) - { - if (group_id >= 0) - if (g_list_find(user->group_ids, - GINT_TO_POINTER(group_id))) - return; - } - else - return; - } - - if (group_id >= 0) - { - user->group_ids = g_list_append(user->group_ids, - GINT_TO_POINTER(group_id)); - } - - user->list_op |= (1 << list_id); -} -#endif - -static void -msn_add_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) -{ - MsnSession *session; - MsnUserList *userlist; - const char *who; - - session = gc->proto_data; - userlist = session->userlist; - who = msn_normalize(gc->account, buddy->name); - - if (!session->logged_in) - { -#if 0 - fake_userlist_add_buddy(session->sync_userlist, who, MSN_LIST_FL, - group ? group->name : NULL); -#else - purple_debug_error("msn", "msn_add_buddy called before connected\n"); -#endif - - return; - } - -#if 0 - if (group != NULL && group->name != NULL) - purple_debug_info("msn", "msn_add_buddy: %s, %s\n", who, group->name); - else - purple_debug_info("msn", "msn_add_buddy: %s\n", who); -#endif - -#if 0 - /* Which is the max? */ - if (session->fl_users_count >= 150) - { - purple_debug_info("msn", "Too many buddies\n"); - /* Buddy list full */ - /* TODO: purple should be notified of this */ - return; - } -#endif - - /* XXX - Would group ever be NULL here? I don't think so... - * shx: Yes it should; MSN handles non-grouped buddies, and this is only - * internal. */ - msn_userlist_add_buddy(userlist, who, MSN_LIST_FL, - group ? group->name : NULL); -} - -static void -msn_rem_buddy(PurpleConnection *gc, PurpleBuddy *buddy, PurpleGroup *group) -{ - MsnSession *session; - MsnUserList *userlist; - - session = gc->proto_data; - userlist = session->userlist; - - if (!session->logged_in) - return; - - /* XXX - Does buddy->name need to be msn_normalize'd here? --KingAnt */ - msn_userlist_rem_buddy(userlist, buddy->name, MSN_LIST_FL, group->name); -} - -static void -msn_add_permit(PurpleConnection *gc, const char *who) -{ - MsnSession *session; - MsnUserList *userlist; - MsnUser *user; - - session = gc->proto_data; - userlist = session->userlist; - user = msn_userlist_find_user(userlist, who); - - if (!session->logged_in) - return; - - if (user != NULL && user->list_op & MSN_LIST_BL_OP) - msn_userlist_rem_buddy(userlist, who, MSN_LIST_BL, NULL); - - msn_userlist_add_buddy(userlist, who, MSN_LIST_AL, NULL); -} - -static void -msn_add_deny(PurpleConnection *gc, const char *who) -{ - MsnSession *session; - MsnUserList *userlist; - MsnUser *user; - - session = gc->proto_data; - userlist = session->userlist; - user = msn_userlist_find_user(userlist, who); - - if (!session->logged_in) - return; - - if (user != NULL && user->list_op & MSN_LIST_AL_OP) - msn_userlist_rem_buddy(userlist, who, MSN_LIST_AL, NULL); - - msn_userlist_add_buddy(userlist, who, MSN_LIST_BL, NULL); -} - -static void -msn_rem_permit(PurpleConnection *gc, const char *who) -{ - MsnSession *session; - MsnUserList *userlist; - MsnUser *user; - - session = gc->proto_data; - userlist = session->userlist; - - if (!session->logged_in) - return; - - user = msn_userlist_find_user(userlist, who); - - msn_userlist_rem_buddy(userlist, who, MSN_LIST_AL, NULL); - - if (user != NULL && user->list_op & MSN_LIST_RL_OP) - msn_userlist_add_buddy(userlist, who, MSN_LIST_BL, NULL); -} - -static void -msn_rem_deny(PurpleConnection *gc, const char *who) -{ - MsnSession *session; - MsnUserList *userlist; - MsnUser *user; - - session = gc->proto_data; - userlist = session->userlist; - - if (!session->logged_in) - return; - - user = msn_userlist_find_user(userlist, who); - - msn_userlist_rem_buddy(userlist, who, MSN_LIST_BL, NULL); - - if (user != NULL && user->list_op & MSN_LIST_RL_OP) - msn_userlist_add_buddy(userlist, who, MSN_LIST_AL, NULL); -} - -static void -msn_set_permit_deny(PurpleConnection *gc) -{ - PurpleAccount *account; - MsnSession *session; - MsnCmdProc *cmdproc; - - account = purple_connection_get_account(gc); - session = gc->proto_data; - cmdproc = session->notification->cmdproc; - - if (account->perm_deny == PURPLE_PRIVACY_ALLOW_ALL || - account->perm_deny == PURPLE_PRIVACY_DENY_USERS) - { - msn_cmdproc_send(cmdproc, "BLP", "%s", "AL"); - } - else - { - msn_cmdproc_send(cmdproc, "BLP", "%s", "BL"); - } -} - -static void -msn_chat_invite(PurpleConnection *gc, int id, const char *msg, - const char *who) -{ - MsnSession *session; - MsnSwitchBoard *swboard; - - session = gc->proto_data; - - swboard = msn_session_find_swboard_with_id(session, id); - - if (swboard == NULL) - { - /* if we have no switchboard, everyone else left the chat already */ - swboard = msn_switchboard_new(session); - msn_switchboard_request(swboard); - swboard->chat_id = id; - swboard->conv = purple_find_chat(gc, id); - } - - swboard->flag |= MSN_SB_FLAG_IM; - - msn_switchboard_request_add_user(swboard, who); -} - -static void -msn_chat_leave(PurpleConnection *gc, int id) -{ - MsnSession *session; - MsnSwitchBoard *swboard; - PurpleConversation *conv; - - session = gc->proto_data; - - swboard = msn_session_find_swboard_with_id(session, id); - - /* if swboard is NULL we were the only person left anyway */ - if (swboard == NULL) - return; - - conv = swboard->conv; - - msn_switchboard_release(swboard, MSN_SB_FLAG_IM); - - /* If other switchboards managed to associate themselves with this - * conv, make sure they know it's gone! */ - if (conv != NULL) - { - while ((swboard = msn_session_find_swboard_with_conv(session, conv)) != NULL) - swboard->conv = NULL; - } -} - -static int -msn_chat_send(PurpleConnection *gc, int id, const char *message, PurpleMessageFlags flags) -{ - PurpleAccount *account; - MsnSession *session; - MsnSwitchBoard *swboard; - MsnMessage *msg; - char *msgformat; - char *msgtext; - - account = purple_connection_get_account(gc); - session = gc->proto_data; - swboard = msn_session_find_swboard_with_id(session, id); - - if (swboard == NULL) - return -EINVAL; - - if (!swboard->ready) - return 0; - - swboard->flag |= MSN_SB_FLAG_IM; - - msn_import_html(message, &msgformat, &msgtext); - - if (strlen(msgtext) + strlen(msgformat) + strlen(DISPLAY_VERSION) > 1564) - { - g_free(msgformat); - g_free(msgtext); - - return -E2BIG; - } - - msg = msn_message_new_plain(msgtext); - msn_message_set_attr(msg, "X-MMS-IM-Format", msgformat); - msn_switchboard_send_msg(swboard, msg, FALSE); - msn_message_destroy(msg); - - g_free(msgformat); - g_free(msgtext); - - serv_got_chat_in(gc, id, purple_account_get_username(account), flags, - message, time(NULL)); - - return 0; -} - -static void -msn_keepalive(PurpleConnection *gc) -{ - MsnSession *session; - - session = gc->proto_data; - - if (!session->http_method) - { - MsnCmdProc *cmdproc; - - cmdproc = session->notification->cmdproc; - - msn_cmdproc_send_quick(cmdproc, "PNG", NULL, NULL); - } -} - -static void -msn_group_buddy(PurpleConnection *gc, const char *who, - const char *old_group_name, const char *new_group_name) -{ - MsnSession *session; - MsnUserList *userlist; - - session = gc->proto_data; - userlist = session->userlist; - - msn_userlist_move_buddy(userlist, who, old_group_name, new_group_name); -} - -static void -msn_rename_group(PurpleConnection *gc, const char *old_name, - PurpleGroup *group, GList *moved_buddies) -{ - MsnSession *session; - MsnCmdProc *cmdproc; - int old_gid; - const char *enc_new_group_name; - - session = gc->proto_data; - cmdproc = session->notification->cmdproc; - enc_new_group_name = purple_url_encode(group->name); - - old_gid = msn_userlist_find_group_id(session->userlist, old_name); - - if (old_gid >= 0) - { - msn_cmdproc_send(cmdproc, "REG", "%d %s 0", old_gid, - enc_new_group_name); - } - else - { - msn_cmdproc_send(cmdproc, "ADG", "%s 0", enc_new_group_name); - } -} - -static void -msn_convo_closed(PurpleConnection *gc, const char *who) -{ - MsnSession *session; - MsnSwitchBoard *swboard; - PurpleConversation *conv; - - session = gc->proto_data; - - swboard = msn_session_find_swboard(session, who); - - /* - * Don't perform an assertion here. If swboard is NULL, then the - * switchboard was either closed by the other party, or the person - * is talking to himself. - */ - if (swboard == NULL) - return; - - conv = swboard->conv; - - /* If we release the switchboard here, it may still have messages - pending ACK which would result in incorrect unsent message errors. - Just let it timeout... This is *so* going to screw with people who - use dumb clients that report "User has closed the conversation window" */ - /* msn_switchboard_release(swboard, MSN_SB_FLAG_IM); */ - swboard->conv = NULL; - - /* If other switchboards managed to associate themselves with this - * conv, make sure they know it's gone! */ - if (conv != NULL) - { - while ((swboard = msn_session_find_swboard_with_conv(session, conv)) != NULL) - swboard->conv = NULL; - } -} - -static void -msn_set_buddy_icon(PurpleConnection *gc, PurpleStoredImage *img) -{ - MsnSession *session; - MsnUser *user; - - session = gc->proto_data; - user = session->user; - - msn_user_set_buddy_icon(user, img); - - msn_change_status(session); -} - -static void -msn_remove_group(PurpleConnection *gc, PurpleGroup *group) -{ - MsnSession *session; - MsnCmdProc *cmdproc; - int group_id; - - session = gc->proto_data; - cmdproc = session->notification->cmdproc; - - if ((group_id = msn_userlist_find_group_id(session->userlist, group->name)) >= 0) - { - msn_cmdproc_send(cmdproc, "RMG", "%d", group_id); - } -} - -/** - * Extract info text from info_data and add it to user_info - */ -static gboolean -msn_tooltip_extract_info_text(PurpleNotifyUserInfo *user_info, MsnGetInfoData *info_data) -{ - PurpleBuddy *b; - - b = purple_find_buddy(purple_connection_get_account(info_data->gc), - info_data->name); - - if (b) - { - char *tmp; - - if (b->alias && b->alias[0]) - { - char *aliastext = g_markup_escape_text(b->alias, -1); - purple_notify_user_info_add_pair(user_info, _("Alias"), aliastext); - g_free(aliastext); - } - - if (b->server_alias) - { - char *nicktext = g_markup_escape_text(b->server_alias, -1); - tmp = g_strdup_printf("<font sml=\"msn\">%s</font><br>", nicktext); - purple_notify_user_info_add_pair(user_info, _("Nickname"), tmp); - g_free(tmp); - g_free(nicktext); - } - - /* Add the tooltip information */ - msn_tooltip_text(b, user_info, TRUE); - - return TRUE; - } - - return FALSE; -} - -#if PHOTO_SUPPORT - -static char * -msn_get_photo_url(const char *url_text) -{ - char *p, *q; - - if ((p = strstr(url_text, " contactparams:photopreauthurl=\"")) != NULL) - { - p += strlen(" contactparams:photopreauthurl=\""); - } - - if (p && (strncmp(p, "http://", 8) == 0) && ((q = strchr(p, '"')) != NULL)) - return g_strndup(p, q - p); - - return NULL; -} - -static void msn_got_photo(PurpleUtilFetchUrlData *url_data, gpointer data, - const gchar *url_text, size_t len, const gchar *error_message); - -#endif - -#if 0 -static char *msn_info_date_reformat(const char *field, size_t len) -{ - char *tmp = g_strndup(field, len); - time_t t = purple_str_to_time(tmp, FALSE, NULL, NULL, NULL); - - g_free(tmp); - return g_strdup(purple_date_format_short(localtime(&t))); -} -#endif - -#define MSN_GOT_INFO_GET_FIELD(a, b) \ - found = purple_markup_extract_info_field(stripped, stripped_len, user_info, \ - "\n" a ":", 0, "\n", 0, "Undisclosed", b, 0, NULL, NULL); \ - if (found) \ - sect_info = TRUE; - -#define MSN_GOT_INFO_GET_FIELD_NO_SEARCH(a, b) \ - found = purple_markup_extract_info_field(stripped, stripped_len, user_info, \ - "\n" a ":", 0, "\n", 0, "Undisclosed", b, 0, NULL, msn_info_strip_search_link); \ - if (found) \ - sect_info = TRUE; - -static char * -msn_info_strip_search_link(const char *field, size_t len) -{ - const char *c; - if ((c = strstr(field, " (http://")) == NULL) - return g_strndup(field, len); - return g_strndup(field, c - field); -} - -static void -msn_got_info(PurpleUtilFetchUrlData *url_data, gpointer data, - const gchar *url_text, size_t len, const gchar *error_message) -{ - MsnGetInfoData *info_data = (MsnGetInfoData *)data; - PurpleNotifyUserInfo *user_info; - char *stripped, *p, *q, *tmp; - char *user_url = NULL; - gboolean found; - gboolean has_tooltip_text = FALSE; - gboolean has_info = FALSE; - gboolean sect_info = FALSE; - gboolean has_contact_info = FALSE; - char *url_buffer; - int stripped_len; -#if PHOTO_SUPPORT - char *photo_url_text = NULL; - MsnGetInfoStepTwoData *info2_data = NULL; -#endif - - purple_debug_info("msn", "In msn_got_info\n"); - - /* Make sure the connection is still valid */ - if (g_list_find(purple_connections_get_all(), info_data->gc) == NULL) - { - purple_debug_warning("msn", "invalid connection. ignoring buddy info.\n"); - g_free(info_data->name); - g_free(info_data); - return; - } - - user_info = purple_notify_user_info_new(); - has_tooltip_text = msn_tooltip_extract_info_text(user_info, info_data); - - if (error_message != NULL || url_text == NULL || strcmp(url_text, "") == 0) - { - tmp = g_strdup_printf("<b>%s</b>", _("Error retrieving profile")); - purple_notify_user_info_add_pair(user_info, NULL, tmp); - g_free(tmp); - - purple_notify_userinfo(info_data->gc, info_data->name, user_info, NULL, NULL); - purple_notify_user_info_destroy(user_info); - - g_free(info_data->name); - g_free(info_data); - return; - } - - url_buffer = g_strdup(url_text); - - /* If they have a homepage link, MSN masks it such that we need to - * fetch the url out before purple_markup_strip_html() nukes it */ - /* I don't think this works with the new spaces profiles - Stu 3/2/06 */ - if ((p = strstr(url_text, - "Take a look at my </font><A class=viewDesc title=\"")) != NULL) - { - p += 50; - - if ((q = strchr(p, '"')) != NULL) - user_url = g_strndup(p, q - p); - } - - /* - * purple_markup_strip_html() doesn't strip out character entities like - * and · - */ - while ((p = strstr(url_buffer, " ")) != NULL) - { - *p = ' '; /* Turn 's into ordinary blanks */ - p += 1; - memmove(p, p + 5, strlen(p + 5)); - url_buffer[strlen(url_buffer) - 5] = '\0'; - } - - while ((p = strstr(url_buffer, "·")) != NULL) - { - memmove(p, p + 6, strlen(p + 6)); - url_buffer[strlen(url_buffer) - 6] = '\0'; - } - - /* Nuke the nasty \r's that just get in the way */ - purple_str_strip_char(url_buffer, '\r'); - - /* MSN always puts in ' for apostrophes...replace them */ - while ((p = strstr(url_buffer, "'")) != NULL) - { - *p = '\''; - memmove(p + 1, p + 5, strlen(p + 5)); - url_buffer[strlen(url_buffer) - 4] = '\0'; - } - - /* Nuke the html, it's easier than trying to parse the horrid stuff */ - stripped = purple_markup_strip_html(url_buffer); - stripped_len = strlen(stripped); - - purple_debug_misc("msn", "stripped = %p\n", stripped); - purple_debug_misc("msn", "url_buffer = %p\n", url_buffer); - - /* General section header */ - if (has_tooltip_text) - purple_notify_user_info_add_section_break(user_info); - - purple_notify_user_info_add_section_header(user_info, _("General")); - - /* Extract their Name and put it in */ - MSN_GOT_INFO_GET_FIELD("Name", _("Name")); - - /* General */ - MSN_GOT_INFO_GET_FIELD("Nickname", _("Nickname")); - MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Age", _("Age")); - MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Gender", _("Gender")); - MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Occupation", _("Occupation")); - MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Location", _("Location")); - - /* Extract their Interests and put it in */ - found = purple_markup_extract_info_field(stripped, stripped_len, user_info, - "\nInterests\t", 0, " (/default.aspx?page=searchresults", 0, - "Undisclosed", _("Hobbies and Interests") /* _("Interests") */, - 0, NULL, NULL); - - if (found) - sect_info = TRUE; - - MSN_GOT_INFO_GET_FIELD("More about me", _("A Little About Me")); - - if (sect_info) - { - has_info = TRUE; - sect_info = FALSE; - } - else - { - /* Remove the section header */ - purple_notify_user_info_remove_last_item(user_info); - if (has_tooltip_text) - purple_notify_user_info_remove_last_item(user_info); - } - - /* Social */ - purple_notify_user_info_add_section_break(user_info); - purple_notify_user_info_add_section_header(user_info, _("Social")); - - MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Marital status", _("Marital Status")); - MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Interested in", _("Interests")); - MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Pets", _("Pets")); - MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Hometown", _("Hometown")); - MSN_GOT_INFO_GET_FIELD("Places lived", _("Places Lived")); - MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Fashion", _("Fashion")); - MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Humor", _("Humor")); - MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Music", _("Music")); - MSN_GOT_INFO_GET_FIELD_NO_SEARCH("Favorite quote", _("Favorite Quote")); - - if (sect_info) - { - has_info = TRUE; - sect_info = FALSE; - } - else - { - /* Remove the section header */ - purple_notify_user_info_remove_last_item(user_info); - purple_notify_user_info_remove_last_item(user_info); - } - - /* Contact Info */ - /* Personal */ - purple_notify_user_info_add_section_break(user_info); - purple_notify_user_info_add_section_header(user_info, _("Contact Info")); - purple_notify_user_info_add_section_header(user_info, _("Personal")); - - MSN_GOT_INFO_GET_FIELD("Name", _("Name")); - MSN_GOT_INFO_GET_FIELD("Significant other", _("Significant Other")); - MSN_GOT_INFO_GET_FIELD("Home phone", _("Home Phone")); - MSN_GOT_INFO_GET_FIELD("Home phone 2", _("Home Phone 2")); - MSN_GOT_INFO_GET_FIELD("Home address", _("Home Address")); - MSN_GOT_INFO_GET_FIELD("Personal Mobile", _("Personal Mobile")); - MSN_GOT_INFO_GET_FIELD("Home fax", _("Home Fax")); - MSN_GOT_INFO_GET_FIELD("Personal email", _("Personal Email")); - MSN_GOT_INFO_GET_FIELD("Personal IM", _("Personal IM")); - MSN_GOT_INFO_GET_FIELD("Birthday", _("Birthday")); - MSN_GOT_INFO_GET_FIELD("Anniversary", _("Anniversary")); - MSN_GOT_INFO_GET_FIELD("Notes", _("Notes")); - - if (sect_info) - { - has_info = TRUE; - sect_info = FALSE; - has_contact_info = TRUE; - } - else - { - /* Remove the section header */ - purple_notify_user_info_remove_last_item(user_info); - } - - /* Business */ - purple_notify_user_info_add_section_header(user_info, _("Work")); - MSN_GOT_INFO_GET_FIELD("Name", _("Name")); - MSN_GOT_INFO_GET_FIELD("Job title", _("Job Title")); - MSN_GOT_INFO_GET_FIELD("Company", _("Company")); - MSN_GOT_INFO_GET_FIELD("Department", _("Department")); - MSN_GOT_INFO_GET_FIELD("Profession", _("Profession")); - MSN_GOT_INFO_GET_FIELD("Work phone 1", _("Work Phone")); - MSN_GOT_INFO_GET_FIELD("Work phone 2", _("Work Phone 2")); - MSN_GOT_INFO_GET_FIELD("Work address", _("Work Address")); - MSN_GOT_INFO_GET_FIELD("Work mobile", _("Work Mobile")); - MSN_GOT_INFO_GET_FIELD("Work pager", _("Work Pager")); - MSN_GOT_INFO_GET_FIELD("Work fax", _("Work Fax")); - MSN_GOT_INFO_GET_FIELD("Work email", _("Work Email")); - MSN_GOT_INFO_GET_FIELD("Work IM", _("Work IM")); - MSN_GOT_INFO_GET_FIELD("Start date", _("Start Date")); - MSN_GOT_INFO_GET_FIELD("Notes", _("Notes")); - - if (sect_info) - { - has_info = TRUE; - sect_info = FALSE; - has_contact_info = TRUE; - } - else - { - /* Remove the section header */ - purple_notify_user_info_remove_last_item(user_info); - } - - if (!has_contact_info) - { - /* Remove the Contact Info section header */ - purple_notify_user_info_remove_last_item(user_info); - } - -#if 0 /* these probably don't show up any more */ - /* - * The fields, 'A Little About Me', 'Favorite Things', 'Hobbies - * and Interests', 'Favorite Quote', and 'My Homepage' may or may - * not appear, in any combination. However, they do appear in - * certain order, so we can successively search to pin down the - * distinct values. - */ - - /* Check if they have A Little About Me */ - found = purple_markup_extract_info_field(stripped, stripped_len, s, - " A Little About Me \n\n", 0, "Favorite Things", '\n', NULL, - _("A Little About Me"), 0, NULL, NULL); - - if (!found) - { - found = purple_markup_extract_info_field(stripped, stripped_len, s, - " A Little About Me \n\n", 0, "Hobbies and Interests", '\n', - NULL, _("A Little About Me"), 0, NULL, NULL); - } - - if (!found) - { - found = purple_markup_extract_info_field(stripped, stripped_len, s, - " A Little About Me \n\n", 0, "Favorite Quote", '\n', NULL, - _("A Little About Me"), 0, NULL, NULL); - } - - if (!found) - { - found = purple_markup_extract_info_field(stripped, stripped_len, s, - " A Little About Me \n\n", 0, "My Homepage \n\nTake a look", - '\n', - NULL, _("A Little About Me"), 0, NULL, NULL); - } - - if (!found) - { - purple_markup_extract_info_field(stripped, stripped_len, s, - " A Little About Me \n\n", 0, "last updated", '\n', NULL, - _("A Little About Me"), 0, NULL, NULL); - } - - if (found) - has_info = TRUE; - - /* Check if they have Favorite Things */ - found = purple_markup_extract_info_field(stripped, stripped_len, s, - " Favorite Things \n\n", 0, "Hobbies and Interests", '\n', NULL, - _("Favorite Things"), 0, NULL, NULL); - - if (!found) - { - found = purple_markup_extract_info_field(stripped, stripped_len, s, - " Favorite Things \n\n", 0, "Favorite Quote", '\n', NULL, - _("Favorite Things"), 0, NULL, NULL); - } - - if (!found) - { - found = purple_markup_extract_info_field(stripped, stripped_len, s, - " Favorite Things \n\n", 0, "My Homepage \n\nTake a look", '\n', - NULL, _("Favorite Things"), 0, NULL, NULL); - } - - if (!found) - { - purple_markup_extract_info_field(stripped, stripped_len, s, - " Favorite Things \n\n", 0, "last updated", '\n', NULL, - _("Favorite Things"), 0, NULL, NULL); - } - - if (found) - has_info = TRUE; - - /* Check if they have Hobbies and Interests */ - found = purple_markup_extract_info_field(stripped, stripped_len, s, - " Hobbies and Interests \n\n", 0, "Favorite Quote", '\n', NULL, - _("Hobbies and Interests"), 0, NULL, NULL); - - if (!found) - { - found = purple_markup_extract_info_field(stripped, stripped_len, s, - " Hobbies and Interests \n\n", 0, "My Homepage \n\nTake a look", - '\n', NULL, _("Hobbies and Interests"), 0, NULL, NULL); - } - - if (!found) - { - purple_markup_extract_info_field(stripped, stripped_len, s, - " Hobbies and Interests \n\n", 0, "last updated", '\n', NULL, - _("Hobbies and Interests"), 0, NULL, NULL); - } - - if (found) - has_info = TRUE; - - /* Check if they have Favorite Quote */ - found = purple_markup_extract_info_field(stripped, stripped_len, s, - "Favorite Quote \n\n", 0, "My Homepage \n\nTake a look", '\n', NULL, - _("Favorite Quote"), 0, NULL, NULL); - - if (!found) - { - purple_markup_extract_info_field(stripped, stripped_len, s, - "Favorite Quote \n\n", 0, "last updated", '\n', NULL, - _("Favorite Quote"), 0, NULL, NULL); - } - - if (found) - has_info = TRUE; - - /* Extract the last updated date and put it in */ - found = purple_markup_extract_info_field(stripped, stripped_len, s, - " last updated:", 1, "\n", 0, NULL, _("Last Updated"), 0, - NULL, msn_info_date_reformat); - - if (found) - has_info = TRUE; -#endif - - /* If we were able to fetch a homepage url earlier, stick it in there */ - if (user_url != NULL) - { - tmp = g_strdup_printf("<a href=\"%s\">%s</a>", user_url, user_url); - purple_notify_user_info_add_pair(user_info, _("Homepage"), tmp); - g_free(tmp); - g_free(user_url); - - has_info = TRUE; - } - - if (!has_info) - { - /* MSN doesn't actually distinguish between "unknown member" and - * a known member with an empty profile. Try to explain this fact. - * Note that if we have a nonempty tooltip_text, we know the user - * exists. - */ - /* This doesn't work with the new spaces profiles - Stu 3/2/06 - char *p = strstr(url_buffer, "Unknown Member </TITLE>"); - * This might not work for long either ... */ - /* Nope, it failed some time before 5/2/07 :( - char *p = strstr(url_buffer, "form id=\"SpacesSearch\" name=\"SpacesSearch\""); - * Let's see how long this one holds out for ... */ - char *p = strstr(url_buffer, "<form id=\"profile_form\" name=\"profile_form\" action=\"http://spaces.live.com/profile.aspx?cid=0\""); - PurpleBuddy *b = purple_find_buddy - (purple_connection_get_account(info_data->gc), info_data->name); - purple_notify_user_info_add_pair(user_info, _("Error retrieving profile"), - ((p && b) ? _("The user has not created a public profile.") : - (p ? _("MSN reported not being able to find the user's profile. " - "This either means that the user does not exist, " - "or that the user exists " - "but has not created a public profile.") : - _("Could not find " /* This should never happen */ - "any information in the user's profile. " - "The user most likely does not exist.")))); - } - - /* put a link to the actual profile URL */ - tmp = g_strdup_printf("<a href=\"%s%s\">%s%s</a>", - PROFILE_URL, info_data->name, PROFILE_URL, info_data->name); - purple_notify_user_info_add_pair(user_info, _("Profile URL"), tmp); - g_free(tmp); - -#if PHOTO_SUPPORT - /* Find the URL to the photo; must be before the marshalling [Bug 994207] */ - photo_url_text = msn_get_photo_url(url_text); - - /* Marshall the existing state */ - info2_data = g_malloc0(sizeof(MsnGetInfoStepTwoData)); - info2_data->info_data = info_data; - info2_data->stripped = stripped; - info2_data->url_buffer = url_buffer; - info2_data->user_info = user_info; - info2_data->photo_url_text = photo_url_text; - - /* Try to put the photo in there too, if there's one */ - if (photo_url_text) - { - purple_util_fetch_url(photo_url_text, FALSE, NULL, FALSE, msn_got_photo, - info2_data); - } - else - { - /* Emulate a callback */ - /* TODO: Huh? */ - msn_got_photo(NULL, info2_data, NULL, 0, NULL); - } -} - -static void -msn_got_photo(PurpleUtilFetchUrlData *url_data, gpointer user_data, - const gchar *url_text, size_t len, const gchar *error_message) -{ - MsnGetInfoStepTwoData *info2_data = (MsnGetInfoStepTwoData *)user_data; - int id = -1; - - /* Unmarshall the saved state */ - MsnGetInfoData *info_data = info2_data->info_data; - char *stripped = info2_data->stripped; - char *url_buffer = info2_data->url_buffer; - PurpleNotifyUserInfo *user_info = info2_data->user_info; - char *photo_url_text = info2_data->photo_url_text; - - /* Make sure the connection is still valid if we got here by fetching a photo url */ - if (url_text && (error_message != NULL || - g_list_find(purple_connections_get_all(), info_data->gc) == NULL)) - { - purple_debug_warning("msn", "invalid connection. ignoring buddy photo info.\n"); - g_free(stripped); - g_free(url_buffer); - purple_notify_user_info_destroy(user_info); - g_free(info_data->name); - g_free(info_data); - g_free(photo_url_text); - g_free(info2_data); - - return; - } - - /* Try to put the photo in there too, if there's one and is readable */ - if (user_data && url_text && len != 0) - { - if (strstr(url_text, "400 Bad Request") - || strstr(url_text, "403 Forbidden") - || strstr(url_text, "404 Not Found")) - { - - purple_debug_info("msn", "Error getting %s: %s\n", - photo_url_text, url_text); - } - else - { - char buf[1024]; - purple_debug_info("msn", "%s is %" G_GSIZE_FORMAT - " bytes\n", photo_url_text, len); - id = purple_imgstore_add_with_id(g_memdup(url_text, len), len, NULL); - g_snprintf(buf, sizeof(buf), "<img id=\"%d\"><br>", id); - purple_notify_user_info_prepend_pair(user_info, NULL, buf); - } - } - - /* We continue here from msn_got_info, as if nothing has happened */ -#endif - purple_notify_userinfo(info_data->gc, info_data->name, user_info, NULL, NULL); - - g_free(stripped); - g_free(url_buffer); - purple_notify_user_info_destroy(user_info); - g_free(info_data->name); - g_free(info_data); -#if PHOTO_SUPPORT - g_free(photo_url_text); - g_free(info2_data); - if (id != -1) - purple_imgstore_unref_by_id(id); -#endif -} - -static void -msn_get_info(PurpleConnection *gc, const char *name) -{ - MsnGetInfoData *data; - char *url; - - data = g_new0(MsnGetInfoData, 1); - data->gc = gc; - data->name = g_strdup(name); - - url = g_strdup_printf("%s%s", PROFILE_URL, name); - - purple_util_fetch_url(url, FALSE, - "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)", - TRUE, msn_got_info, data); - - g_free(url); -} - -static gboolean msn_load(PurplePlugin *plugin) -{ - msn_notification_init(); - msn_switchboard_init(); - msn_sync_init(); - - return TRUE; -} - -static gboolean msn_unload(PurplePlugin *plugin) -{ - msn_notification_end(); - msn_switchboard_end(); - msn_sync_end(); - - return TRUE; -} - -static PurpleAccount *find_acct(const char *prpl, const char *acct_id) -{ - PurpleAccount *acct = NULL; - - /* If we have a specific acct, use it */ - if (acct_id) { - acct = purple_accounts_find(acct_id, prpl); - if (acct && !purple_account_is_connected(acct)) - acct = NULL; - } else { /* Otherwise find an active account for the protocol */ - GList *l = purple_accounts_get_all(); - while (l) { - if (!strcmp(prpl, purple_account_get_protocol_id(l->data)) - && purple_account_is_connected(l->data)) { - acct = l->data; - break; - } - l = l->next; - } - } - - return acct; -} - -static gboolean msn_uri_handler(const char *proto, const char *cmd, GHashTable *params) -{ - char *acct_id = g_hash_table_lookup(params, "account"); - PurpleAccount *acct; - - if (g_ascii_strcasecmp(proto, "msnim")) - return FALSE; - - acct = find_acct("prpl-msn", acct_id); - - if (!acct) - return FALSE; - - /* msnim:chat?contact=user@domain.tld */ - if (!g_ascii_strcasecmp(cmd, "Chat")) { - char *sname = g_hash_table_lookup(params, "contact"); - if (sname) { - PurpleConversation *conv = purple_find_conversation_with_account( - PURPLE_CONV_TYPE_IM, sname, acct); - if (conv == NULL) - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, acct, sname); - purple_conversation_present(conv); - } - /*else - **If pidgindialogs_im() was in the core, we could use it here. - * It is all purple_request_* based, but I'm not sure it really belongs in the core - pidgindialogs_im();*/ - - return TRUE; - } - /* msnim:add?contact=user@domain.tld */ - else if (!g_ascii_strcasecmp(cmd, "Add")) { - char *name = g_hash_table_lookup(params, "contact"); - purple_blist_request_add_buddy(acct, name, NULL, NULL); - return TRUE; - } - - return FALSE; -} - - -static PurplePluginProtocolInfo prpl_info = -{ - OPT_PROTO_MAIL_CHECK, - NULL, /* user_splits */ - NULL, /* protocol_options */ - {"png", 0, 0, 96, 96, 0, PURPLE_ICON_SCALE_SEND}, /* icon_spec */ - msn_list_icon, /* list_icon */ - NULL, /* list_emblems */ - msn_status_text, /* status_text */ - msn_tooltip_text, /* tooltip_text */ - msn_status_types, /* away_states */ - msn_blist_node_menu, /* blist_node_menu */ - NULL, /* chat_info */ - NULL, /* chat_info_defaults */ - msn_login, /* login */ - msn_close, /* close */ - msn_send_im, /* send_im */ - NULL, /* set_info */ - msn_send_typing, /* send_typing */ - msn_get_info, /* get_info */ - msn_set_status, /* set_away */ - msn_set_idle, /* set_idle */ - NULL, /* change_passwd */ - msn_add_buddy, /* add_buddy */ - NULL, /* add_buddies */ - msn_rem_buddy, /* remove_buddy */ - NULL, /* remove_buddies */ - msn_add_permit, /* add_permit */ - msn_add_deny, /* add_deny */ - msn_rem_permit, /* rem_permit */ - msn_rem_deny, /* rem_deny */ - msn_set_permit_deny, /* set_permit_deny */ - NULL, /* join_chat */ - NULL, /* reject chat invite */ - NULL, /* get_chat_name */ - msn_chat_invite, /* chat_invite */ - msn_chat_leave, /* chat_leave */ - NULL, /* chat_whisper */ - msn_chat_send, /* chat_send */ - msn_keepalive, /* keepalive */ - NULL, /* register_user */ - NULL, /* get_cb_info */ - NULL, /* get_cb_away */ - NULL, /* alias_buddy */ - msn_group_buddy, /* group_buddy */ - msn_rename_group, /* rename_group */ - NULL, /* buddy_free */ - msn_convo_closed, /* convo_closed */ - msn_normalize, /* normalize */ - msn_set_buddy_icon, /* set_buddy_icon */ - msn_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 */ - msn_can_receive_file, /* can_receive_file */ - msn_send_file, /* send_file */ - msn_new_xfer, /* new_xfer */ - msn_offline_message, /* offline_message */ - NULL, /* whiteboard_prpl_ops */ - NULL, /* send_raw */ - NULL, /* roomlist_room_serialize */ - NULL, /* unregister_user */ - msn_send_attention, /* send_attention */ - msn_attention_types, /* attention_types */ - sizeof(PurplePluginProtocolInfo), /* struct_size */ - msn_get_account_text_table, /* get_account_text_table */ - NULL, /* initiate_media */ - NULL /* can_do_media */ -}; - -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-msn", /**< id */ - "MSN", /**< name */ - DISPLAY_VERSION, /**< version */ - /** summary */ - N_("MSN Protocol Plugin"), - /** description */ - N_("MSN Protocol Plugin"), - "Christian Hammond <chipx86@gnupdate.org>", /**< author */ - PURPLE_WEBSITE, /**< homepage */ - - msn_load, /**< load */ - msn_unload, /**< unload */ - NULL, /**< destroy */ - - NULL, /**< ui_info */ - &prpl_info, /**< extra_info */ - NULL, /**< prefs_info */ - msn_actions, - - /* padding */ - NULL, - NULL, - NULL, - NULL -}; - -static void -init_plugin(PurplePlugin *plugin) -{ - PurpleAccountOption *option; - - option = purple_account_option_string_new(_("Server"), "server", - MSN_SERVER); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_int_new(_("Port"), "port", 1863); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_bool_new(_("Use HTTP Method"), - "http_method", FALSE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_string_new(_("HTTP Method Server"), - "http_method_server", MSN_HTTPCONN_SERVER); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - option = purple_account_option_bool_new(_("Show custom smileys"), - "custom_smileys", TRUE); - prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, - option); - - purple_cmd_register("nudge", "", PURPLE_CMD_P_PRPL, - PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY, - "prpl-msn", msn_cmd_nudge, - _("nudge: nudge a user to get their attention"), NULL); - - purple_prefs_remove("/plugins/prpl/msn"); - - purple_signal_connect(purple_get_core(), "uri-handler", plugin, - PURPLE_CALLBACK(msn_uri_handler), NULL); -} - -PURPLE_INIT_PLUGIN(msnp9, init_plugin, info);
--- a/libpurple/protocols/msnp9/msn.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,134 +0,0 @@ -/** - * @file msn.h The MSN protocol plugin - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_H_ -#define _MSN_H_ - -/* #define MSN_DEBUG_MSG 1 */ -/* #define MSN_DEBUG_SLPMSG 1 */ -/* #define MSN_DEBUG_HTTP 1 */ - -/* #define MSN_DEBUG_SLP 1 */ -/* #define MSN_DEBUG_SLP_VERBOSE 1 */ -/* #define MSN_DEBUG_SLP_FILES 1 */ - -/* #define MSN_DEBUG_NS 1 */ -/* #define MSN_DEBUG_SB 1 */ - -#include "internal.h" - -#include "account.h" -#include "accountopt.h" -#include "blist.h" -#include "connection.h" -#include "conversation.h" -#include "debug.h" -#include "cipher.h" -#include "notify.h" -#include "privacy.h" -#include "proxy.h" -#include "prpl.h" -#include "request.h" -#include "servconn.h" -#include "sslconn.h" -#include "util.h" - -#include "ft.h" - -#define MSN_BUF_LEN 8192 - -#define USEROPT_MSNSERVER 3 -#define MSN_SERVER "messenger.hotmail.com" -#define MSN_HTTPCONN_SERVER "gateway.messenger.hotmail.com" -#define USEROPT_MSNPORT 4 -#define MSN_PORT 1863 - -#define MSN_TYPING_RECV_TIMEOUT 6 -#define MSN_TYPING_SEND_TIMEOUT 4 - -#define HOTMAIL_URL "http://www.hotmail.com/cgi-bin/folders" -#define PASSPORT_URL "http://lc1.law13.hotmail.passport.com/cgi-bin/dologin?login=" -#define PROFILE_URL "http://spaces.live.com/profile.aspx?mem=" - -#define USEROPT_HOTMAIL 0 - -#define BUDDY_ALIAS_MAXLEN 387 - -#define MSN_FT_GUID "{5D3E02AB-6190-11d3-BBBB-00C04F795683}" - -#define MSN_CLIENTINFO \ - "Client-Name: Purple/" VERSION "\r\n" \ - "Chat-Logging: Y\r\n" - -/* Index into attention_types */ -#define MSN_NUDGE 0 - -typedef enum -{ - MSN_LIST_FL_OP = 0x01, - MSN_LIST_AL_OP = 0x02, - MSN_LIST_BL_OP = 0x04, - MSN_LIST_RL_OP = 0x08 - -} MsnListOp; - -typedef enum -{ - MSN_CLIENT_CAP_WIN_MOBILE = 0x00001, - MSN_CLIENT_CAP_UNKNOWN_1 = 0x00002, - MSN_CLIENT_CAP_INK_GIF = 0x00004, - MSN_CLIENT_CAP_INK_ISF = 0x00008, - MSN_CLIENT_CAP_VIDEO_CHAT = 0x00010, - MSN_CLIENT_CAP_BASE = 0x00020, - MSN_CLIENT_CAP_MSNMOBILE = 0x00040, - MSN_CLIENT_CAP_MSNDIRECT = 0x00080, - MSN_CLIENT_CAP_WEBMSGR = 0x00100, - MSN_CLIENT_CAP_DIRECTIM = 0x04000, - MSN_CLIENT_CAP_WINKS = 0x08000, - MSN_CLIENT_CAP_SEARCH = 0x10000 - -} MsnClientCaps; - -typedef enum -{ - MSN_CLIENT_VER_5_0 = 0x00, - MSN_CLIENT_VER_6_0 = 0x10, /* MSNC1 */ - MSN_CLIENT_VER_6_1 = 0x20, /* MSNC2 */ - MSN_CLIENT_VER_6_2 = 0x30, /* MSNC3 */ - MSN_CLIENT_VER_7_0 = 0x40, /* MSNC4 */ - MSN_CLIENT_VER_7_5 = 0x50 /* MSNC5 */ - -} MsnClientVerId; - -#define MSN_CLIENT_ID_VERSION MSN_CLIENT_VER_7_0 -#define MSN_CLIENT_ID_RESERVED_1 0x00 -#define MSN_CLIENT_ID_RESERVED_2 0x00 -#define MSN_CLIENT_ID_CAPABILITIES MSN_CLIENT_CAP_BASE - -#define MSN_CLIENT_ID \ - ((MSN_CLIENT_ID_VERSION << 24) | \ - (MSN_CLIENT_ID_RESERVED_1 << 16) | \ - (MSN_CLIENT_ID_RESERVED_2 << 8) | \ - (MSN_CLIENT_ID_CAPABILITIES)) - -#endif /* _MSN_H_ */
--- a/libpurple/protocols/msnp9/nexus.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,513 +0,0 @@ -/** - * @file nexus.c MSN Nexus functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "nexus.h" -#include "notification.h" - -/************************************************************************** - * Main - **************************************************************************/ - -MsnNexus * -msn_nexus_new(MsnSession *session) -{ - MsnNexus *nexus; - - nexus = g_new0(MsnNexus, 1); - nexus->session = session; - nexus->challenge_data = g_hash_table_new_full(g_str_hash, - g_str_equal, g_free, g_free); - - return nexus; -} - -void -msn_nexus_destroy(MsnNexus *nexus) -{ - if (nexus->gsc) - purple_ssl_close(nexus->gsc); - - g_free(nexus->login_host); - - g_free(nexus->login_path); - - if (nexus->challenge_data != NULL) - g_hash_table_destroy(nexus->challenge_data); - - if (nexus->input_handler > 0) - purple_input_remove(nexus->input_handler); - g_free(nexus->write_buf); - g_free(nexus->read_buf); - - g_free(nexus); -} - -/************************************************************************** - * Util - **************************************************************************/ - -static gssize -msn_ssl_read(MsnNexus *nexus) -{ - gssize len; - char temp_buf[4096]; - - if ((len = purple_ssl_read(nexus->gsc, temp_buf, - sizeof(temp_buf))) > 0) - { - nexus->read_buf = g_realloc(nexus->read_buf, - nexus->read_len + len + 1); - strncpy(nexus->read_buf + nexus->read_len, temp_buf, len); - nexus->read_len += len; - nexus->read_buf[nexus->read_len] = '\0'; - } - - return len; -} - -static void -nexus_write_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - MsnNexus *nexus = data; - int len, total_len; - - total_len = strlen(nexus->write_buf); - - len = purple_ssl_write(nexus->gsc, - nexus->write_buf + nexus->written_len, - total_len - nexus->written_len); - - if (len < 0 && errno == EAGAIN) - return; - else if (len <= 0) { - purple_input_remove(nexus->input_handler); - nexus->input_handler = 0; - /* TODO: notify of the error */ - return; - } - nexus->written_len += len; - - if (nexus->written_len < total_len) - return; - - purple_input_remove(nexus->input_handler); - nexus->input_handler = 0; - - g_free(nexus->write_buf); - nexus->write_buf = NULL; - nexus->written_len = 0; - - nexus->written_cb(nexus, source, 0); -} - -/************************************************************************** - * Login - **************************************************************************/ - -static void -login_connect_cb(gpointer data, PurpleSslConnection *gsc, - PurpleInputCondition cond); - -static void -login_error_cb(PurpleSslConnection *gsc, PurpleSslErrorType error, void *data) -{ - MsnNexus *nexus; - MsnSession *session; - - nexus = data; - g_return_if_fail(nexus != NULL); - - nexus->gsc = NULL; - - session = nexus->session; - g_return_if_fail(session != NULL); - - msn_session_set_error(session, MSN_ERROR_SERVCONN, _("Unable to connect")); - /* the above line will result in nexus being destroyed, so we don't want - * to destroy it here, or we'd crash */ -} - -static void -nexus_login_written_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - MsnNexus *nexus = data; - MsnSession *session; - int len; - - session = nexus->session; - g_return_if_fail(session != NULL); - - if (nexus->input_handler == 0) - /* TODO: Use purple_ssl_input_add()? */ - nexus->input_handler = purple_input_add(nexus->gsc->fd, - PURPLE_INPUT_READ, nexus_login_written_cb, nexus); - - - len = msn_ssl_read(nexus); - - if (len < 0 && errno == EAGAIN) - return; - else if (len <= 0) { - purple_input_remove(nexus->input_handler); - nexus->input_handler = 0; - g_free(nexus->read_buf); - nexus->read_buf = NULL; - nexus->read_len = 0; - /* TODO: error handling */ - return; - } - - if (g_strstr_len(nexus->read_buf, nexus->read_len, - "\r\n\r\n") == NULL) - return; - - purple_input_remove(nexus->input_handler); - nexus->input_handler = 0; - - purple_ssl_close(nexus->gsc); - nexus->gsc = NULL; - - purple_debug_misc("msn", "ssl buffer: {%s}\n", nexus->read_buf); - - if (strstr(nexus->read_buf, "HTTP/1.1 302") != NULL) - { - /* Redirect. */ - char *location, *c; - - location = strstr(nexus->read_buf, "Location: "); - if (location == NULL) - { - g_free(nexus->read_buf); - nexus->read_buf = NULL; - nexus->read_len = 0; - - return; - } - location = strchr(location, ' ') + 1; - - if ((c = strchr(location, '\r')) != NULL) - *c = '\0'; - - /* Skip the http:// */ - if ((c = strchr(location, '/')) != NULL) - location = c + 2; - - if ((c = strchr(location, '/')) != NULL) - { - g_free(nexus->login_path); - nexus->login_path = g_strdup(c); - - *c = '\0'; - } - - g_free(nexus->login_host); - nexus->login_host = g_strdup(location); - - nexus->gsc = purple_ssl_connect(session->account, - nexus->login_host, PURPLE_SSL_DEFAULT_PORT, - login_connect_cb, login_error_cb, nexus); - } - else if (strstr(nexus->read_buf, "HTTP/1.1 401 Unauthorized") != NULL) - { - const char *error; - - if ((error = strstr(nexus->read_buf, "WWW-Authenticate")) != NULL) - { - if ((error = strstr(error, "cbtxt=")) != NULL) - { - const char *c; - char *temp; - - error += strlen("cbtxt="); - - if ((c = strchr(error, '\n')) == NULL) - c = error + strlen(error); - - temp = g_strndup(error, c - error); - error = purple_url_decode(temp); - g_free(temp); - if ((temp = strstr(error, " Do one of the following or try again:")) != NULL) - *temp = '\0'; - } - } - - msn_session_set_error(session, MSN_ERROR_AUTH, error); - } - else if (strstr(nexus->read_buf, "HTTP/1.1 503 Service Unavailable")) - { - msn_session_set_error(session, MSN_ERROR_SERV_UNAVAILABLE, NULL); - } - else if (strstr(nexus->read_buf, "HTTP/1.1 200 OK")) - { - char *base, *c; - char *login_params; - -#if 0 - /* All your base are belong to us. */ - base = buffer; - - /* For great cookie! */ - while ((base = strstr(base, "Set-Cookie: ")) != NULL) - { - base += strlen("Set-Cookie: "); - - c = strchr(base, ';'); - - session->login_cookies = - g_list_append(session->login_cookies, - g_strndup(base, c - base)); - } -#endif - - base = strstr(nexus->read_buf, "Authentication-Info: "); - - g_return_if_fail(base != NULL); - - base = strstr(base, "from-PP='"); - base += strlen("from-PP='"); - c = strchr(base, '\''); - - login_params = g_strndup(base, c - base); - - msn_got_login_params(session, login_params); - - g_free(login_params); - - msn_nexus_destroy(nexus); - session->nexus = NULL; - return; - } - - g_free(nexus->read_buf); - nexus->read_buf = NULL; - nexus->read_len = 0; - -} - -/* this guards against missing hash entries */ -static char * -nexus_challenge_data_lookup(GHashTable *challenge_data, const char *key) -{ - char *entry; - - return (entry = (char *)g_hash_table_lookup(challenge_data, key)) ? - entry : "(null)"; -} - -void -login_connect_cb(gpointer data, PurpleSslConnection *gsc, - PurpleInputCondition cond) -{ - MsnNexus *nexus; - MsnSession *session; - char *username, *password, *encpass; - char *request_str, *head, *tail; - char *buffer = NULL; - guint32 ctint; - - nexus = data; - g_return_if_fail(nexus != NULL); - - session = nexus->session; - g_return_if_fail(session != NULL); - - msn_session_set_login_step(session, MSN_LOGIN_STEP_GET_COOKIE); - - username = - g_strdup(purple_url_encode(purple_account_get_username(session->account))); - - password = g_utf8_strncpy(g_strdup(purple_connection_get_password(session->account->gc)), - purple_connection_get_password(session->account->gc), 16); - encpass = g_strdup(purple_url_encode(password)); - g_free(password); - - ctint = strtoul((char *)g_hash_table_lookup(nexus->challenge_data, "ct"), NULL, 10) + 200; - - head = g_strdup_printf( - "GET %s HTTP/1.1\r\n" - "Authorization: Passport1.4 OrgVerb=GET,OrgURL=%s,sign-in=%s", - nexus->login_path, - (char *)g_hash_table_lookup(nexus->challenge_data, "ru"), - username); - - tail = g_strdup_printf( - "lc=%s,id=%s,tw=%s,fs=%s,ru=%s,ct=%" G_GUINT32_FORMAT ",kpp=%s,kv=%s,ver=%s,tpf=%s\r\n" - "User-Agent: MSMSGS\r\n" - "Host: %s\r\n" - "Connection: Keep-Alive\r\n" - "Cache-Control: no-cache\r\n", - nexus_challenge_data_lookup(nexus->challenge_data, "lc"), - nexus_challenge_data_lookup(nexus->challenge_data, "id"), - nexus_challenge_data_lookup(nexus->challenge_data, "tw"), - nexus_challenge_data_lookup(nexus->challenge_data, "fs"), - nexus_challenge_data_lookup(nexus->challenge_data, "ru"), - ctint, - nexus_challenge_data_lookup(nexus->challenge_data, "kpp"), - nexus_challenge_data_lookup(nexus->challenge_data, "kv"), - nexus_challenge_data_lookup(nexus->challenge_data, "ver"), - nexus_challenge_data_lookup(nexus->challenge_data, "tpf"), - nexus->login_host); - - buffer = g_strdup_printf("%s,pwd=XXXXXXXX,%s\r\n", head, tail); - request_str = g_strdup_printf("%s,pwd=%s,%s\r\n", head, encpass, tail); - - purple_debug_misc("msn", "Sending: {%s}\n", buffer); - - g_free(buffer); - g_free(head); - g_free(tail); - g_free(username); - g_free(encpass); - - nexus->write_buf = request_str; - nexus->written_len = 0; - - nexus->read_len = 0; - - nexus->written_cb = nexus_login_written_cb; - - nexus->input_handler = purple_input_add(gsc->fd, PURPLE_INPUT_WRITE, - nexus_write_cb, nexus); - - nexus_write_cb(nexus, gsc->fd, PURPLE_INPUT_WRITE); - - return; - - -} - -static void -nexus_connect_written_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - MsnNexus *nexus = data; - int len; - char *da_login; - char *base, *c; - - if (nexus->input_handler == 0) - /* TODO: Use purple_ssl_input_add()? */ - nexus->input_handler = purple_input_add(nexus->gsc->fd, - PURPLE_INPUT_READ, nexus_connect_written_cb, nexus); - - /* Get the PassportURLs line. */ - len = msn_ssl_read(nexus); - - if (len < 0 && errno == EAGAIN) - return; - else if (len <= 0) { - purple_input_remove(nexus->input_handler); - nexus->input_handler = 0; - g_free(nexus->read_buf); - nexus->read_buf = NULL; - nexus->read_len = 0; - /* TODO: error handling */ - return; - } - - if (g_strstr_len(nexus->read_buf, nexus->read_len, - "\r\n\r\n") == NULL) - return; - - purple_input_remove(nexus->input_handler); - nexus->input_handler = 0; - - base = strstr(nexus->read_buf, "PassportURLs"); - - if (base == NULL) - { - g_free(nexus->read_buf); - nexus->read_buf = NULL; - nexus->read_len = 0; - return; - } - - if ((da_login = strstr(base, "DALogin=")) != NULL) - { - /* skip over "DALogin=" */ - da_login += 8; - - if ((c = strchr(da_login, ',')) != NULL) - *c = '\0'; - - if ((c = strchr(da_login, '/')) != NULL) - { - nexus->login_path = g_strdup(c); - *c = '\0'; - } - - nexus->login_host = g_strdup(da_login); - } - - g_free(nexus->read_buf); - nexus->read_buf = NULL; - nexus->read_len = 0; - - purple_ssl_close(nexus->gsc); - - /* Now begin the connection to the login server. */ - nexus->gsc = purple_ssl_connect(nexus->session->account, - nexus->login_host, PURPLE_SSL_DEFAULT_PORT, - login_connect_cb, login_error_cb, nexus); -} - - -/************************************************************************** - * Connect - **************************************************************************/ - -static void -nexus_connect_cb(gpointer data, PurpleSslConnection *gsc, - PurpleInputCondition cond) -{ - MsnNexus *nexus; - MsnSession *session; - - nexus = data; - g_return_if_fail(nexus != NULL); - - session = nexus->session; - g_return_if_fail(session != NULL); - - msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH); - - nexus->write_buf = g_strdup("GET /rdr/pprdr.asp\r\n\r\n"); - nexus->written_len = 0; - - nexus->read_len = 0; - - nexus->written_cb = nexus_connect_written_cb; - - nexus->input_handler = purple_input_add(gsc->fd, PURPLE_INPUT_WRITE, - nexus_write_cb, nexus); - - nexus_write_cb(nexus, gsc->fd, PURPLE_INPUT_WRITE); -} - -void -msn_nexus_connect(MsnNexus *nexus) -{ - nexus->gsc = purple_ssl_connect(nexus->session->account, - "nexus.passport.com", PURPLE_SSL_DEFAULT_PORT, - nexus_connect_cb, login_error_cb, nexus); -}
--- a/libpurple/protocols/msnp9/nexus.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -/** - * @file nexus.h MSN Nexus functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_NEXUS_H_ -#define _MSN_NEXUS_H_ - -typedef struct _MsnNexus MsnNexus; - -struct _MsnNexus -{ - MsnSession *session; - - char *login_host; - char *login_path; - GHashTable *challenge_data; - PurpleSslConnection *gsc; - - guint input_handler; - - char *write_buf; - gsize written_len; - PurpleInputFunction written_cb; - - char *read_buf; - gsize read_len; -}; - -void msn_nexus_connect(MsnNexus *nexus); -MsnNexus *msn_nexus_new(MsnSession *session); -void msn_nexus_destroy(MsnNexus *nexus); - -#endif /* _MSN_NEXUS_H_ */
--- a/libpurple/protocols/msnp9/notification.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1510 +0,0 @@ -/** - * @file notification.c Notification server functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "notification.h" -#include "state.h" -#include "error.h" -#include "msn-utils.h" -#include "page.h" - -#include "userlist.h" -#include "sync.h" -#include "slplink.h" - -static MsnTable *cbs_table; - -/************************************************************************** - * Main - **************************************************************************/ - -static void -destroy_cb(MsnServConn *servconn) -{ - MsnNotification *notification; - - notification = servconn->cmdproc->data; - g_return_if_fail(notification != NULL); - - msn_notification_destroy(notification); -} - -MsnNotification * -msn_notification_new(MsnSession *session) -{ - MsnNotification *notification; - MsnServConn *servconn; - - g_return_val_if_fail(session != NULL, NULL); - - notification = g_new0(MsnNotification, 1); - - notification->session = session; - notification->servconn = servconn = msn_servconn_new(session, MSN_SERVCONN_NS); - msn_servconn_set_destroy_cb(servconn, destroy_cb); - - notification->cmdproc = servconn->cmdproc; - notification->cmdproc->data = notification; - notification->cmdproc->cbs_table = cbs_table; - - return notification; -} - -void -msn_notification_destroy(MsnNotification *notification) -{ - notification->cmdproc->data = NULL; - - msn_servconn_set_destroy_cb(notification->servconn, NULL); - - msn_servconn_destroy(notification->servconn); - - g_free(notification); -} - -/************************************************************************** - * Connect - **************************************************************************/ - -static void -connect_cb(MsnServConn *servconn) -{ - MsnCmdProc *cmdproc; - MsnSession *session; - PurpleAccount *account; - char **a, **c, *vers; - int i; - - g_return_if_fail(servconn != NULL); - - cmdproc = servconn->cmdproc; - session = servconn->session; - account = session->account; - - /* Allocate an array for CVR0, NULL, and all the versions */ - a = c = g_new0(char *, session->protocol_ver - 8 + 3); - - for (i = session->protocol_ver; i >= 8; i--) - *c++ = g_strdup_printf("MSNP%d", i); - - *c++ = g_strdup("CVR0"); - - vers = g_strjoinv(" ", a); - - if (session->login_step == MSN_LOGIN_STEP_START) - msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE); - else - msn_session_set_login_step(session, MSN_LOGIN_STEP_HANDSHAKE2); - - msn_cmdproc_send(cmdproc, "VER", "%s", vers); - - g_strfreev(a); - g_free(vers); -} - -gboolean -msn_notification_connect(MsnNotification *notification, const char *host, int port) -{ - MsnServConn *servconn; - - g_return_val_if_fail(notification != NULL, FALSE); - - servconn = notification->servconn; - - msn_servconn_set_connect_cb(servconn, connect_cb); - notification->in_use = msn_servconn_connect(servconn, host, port); - - return notification->in_use; -} - -void -msn_notification_disconnect(MsnNotification *notification) -{ - g_return_if_fail(notification != NULL); - g_return_if_fail(notification->in_use); - - msn_servconn_disconnect(notification->servconn); - - notification->in_use = FALSE; -} - -/************************************************************************** - * Util - **************************************************************************/ - -static void -group_error_helper(MsnSession *session, const char *msg, int group_id, int error) -{ - PurpleAccount *account; - PurpleConnection *gc; - char *reason = NULL; - char *title = NULL; - - account = session->account; - gc = purple_account_get_connection(account); - - if (error == 224) - { - if (group_id == 0) - { - return; - } - else - { - const char *group_name; - group_name = - msn_userlist_find_group_name(session->userlist, - group_id); - reason = g_strdup_printf(_("%s is not a valid group."), - group_name); - } - } - else - { - reason = g_strdup(_("Unknown error.")); - } - - title = g_strdup_printf(_("%s on %s (%s)"), msg, - purple_account_get_username(account), - purple_account_get_protocol_name(account)); - purple_notify_error(gc, NULL, title, reason); - g_free(title); - g_free(reason); -} - -/************************************************************************** - * Login - **************************************************************************/ - -void -msn_got_login_params(MsnSession *session, const char *login_params) -{ - MsnCmdProc *cmdproc; - - cmdproc = session->notification->cmdproc; - - msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_END); - - msn_cmdproc_send(cmdproc, "USR", "TWN S %s", login_params); -} - -static void -cvr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - PurpleAccount *account; - - account = cmdproc->session->account; - - msn_cmdproc_send(cmdproc, "USR", "TWN I %s", - purple_account_get_username(account)); -} - -static void -usr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - PurpleAccount *account; - PurpleConnection *gc; - - session = cmdproc->session; - account = session->account; - gc = purple_account_get_connection(account); - - if (!g_ascii_strcasecmp(cmd->params[1], "OK")) - { - /* OK */ - const char *friendly = purple_url_decode(cmd->params[3]); - - session->passport_info.verified = atoi(cmd->params[4]); - - purple_connection_set_display_name(gc, friendly); - - msn_session_set_login_step(session, MSN_LOGIN_STEP_SYN); - - msn_cmdproc_send(cmdproc, "SYN", "%s", "0"); - } - else if (!g_ascii_strcasecmp(cmd->params[1], "TWN")) - { - /* Passport authentication */ - char **elems, **cur, **tokens; - - session->nexus = msn_nexus_new(session); - - /* Parse the challenge data. */ - - elems = g_strsplit(cmd->params[3], ",", 0); - - for (cur = elems; *cur != NULL; cur++) - { - tokens = g_strsplit(*cur, "=", 2); - g_hash_table_insert(session->nexus->challenge_data, tokens[0], tokens[1]); - /* Don't free each of the tokens, only the array. */ - g_free(tokens); - } - - g_strfreev(elems); - - msn_session_set_login_step(session, MSN_LOGIN_STEP_AUTH_START); - - msn_nexus_connect(session->nexus); - } -} - -static void -usr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error) -{ - MsnErrorType msnerr = 0; - - switch (error) - { - case 500: - case 601: - case 910: - case 921: - msnerr = MSN_ERROR_SERV_UNAVAILABLE; - break; - case 911: - msnerr = MSN_ERROR_AUTH; - break; - default: - return; - break; - } - - msn_session_set_error(cmdproc->session, msnerr, NULL); -} - -static void -ver_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - PurpleAccount *account; - gboolean protocol_supported = FALSE; - char proto_str[8]; - size_t i; - - session = cmdproc->session; - account = session->account; - - g_snprintf(proto_str, sizeof(proto_str), "MSNP%d", session->protocol_ver); - - for (i = 1; i < cmd->param_count; i++) - { - if (!strcmp(cmd->params[i], proto_str)) - { - protocol_supported = TRUE; - break; - } - } - - if (!protocol_supported) - { - msn_session_set_error(session, MSN_ERROR_UNSUPPORTED_PROTOCOL, - NULL); - return; - } - - msn_cmdproc_send(cmdproc, "CVR", - "0x0409 winnt 5.1 i386 MSNMSGR 6.0.0602 MSMSGS %s", - purple_account_get_username(account)); -} - -/************************************************************************** - * Log out - **************************************************************************/ - -static void -out_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - if (!g_ascii_strcasecmp(cmd->params[0], "OTH")) - msn_session_set_error(cmdproc->session, MSN_ERROR_SIGN_OTHER, - NULL); - else if (!g_ascii_strcasecmp(cmd->params[0], "SSD")) - msn_session_set_error(cmdproc->session, MSN_ERROR_SERV_DOWN, NULL); -} - -void -msn_notification_close(MsnNotification *notification) -{ - g_return_if_fail(notification != NULL); - - if (!notification->in_use) - return; - - msn_cmdproc_send_quick(notification->cmdproc, "OUT", NULL, NULL); - - msn_notification_disconnect(notification); -} - -/************************************************************************** - * Messages - **************************************************************************/ - -static void -msg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, - size_t len) -{ - MsnMessage *msg; - - msg = msn_message_new_from_cmd(cmdproc->session, cmd); - - msn_message_parse_payload(msg, payload, len); -#ifdef MSN_DEBUG_NS - msn_message_show_readable(msg, "Notification", TRUE); -#endif - - msn_cmdproc_process_msg(cmdproc, msg); - - msn_message_destroy(msg); -} - -static void -msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - /* NOTE: cmd is not always cmdproc->last_cmd, sometimes cmd is a queued - * command and we are processing it */ - - if (cmd->payload == NULL) - { - cmdproc->last_cmd->payload_cb = msg_cmd_post; - cmdproc->servconn->payload_len = atoi(cmd->params[2]); - } - else - { - g_return_if_fail(cmd->payload_cb != NULL); - - cmd->payload_cb(cmdproc, cmd, cmd->payload, cmd->payload_len); - } -} - -/************************************************************************** - * Challenges - **************************************************************************/ - -static void -chl_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnTransaction *trans; - char buf[33]; - const char *challenge_resp; - PurpleCipher *cipher; - PurpleCipherContext *context; - guchar digest[16]; - int i; - - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); - - purple_cipher_context_append(context, (const guchar *)cmd->params[1], - strlen(cmd->params[1])); - - challenge_resp = "VT6PX?UQTM4WM%YR"; - - purple_cipher_context_append(context, (const guchar *)challenge_resp, - strlen(challenge_resp)); - purple_cipher_context_digest(context, sizeof(digest), digest, NULL); - purple_cipher_context_destroy(context); - - for (i = 0; i < 16; i++) - g_snprintf(buf + (i*2), 3, "%02x", digest[i]); - - trans = msn_transaction_new(cmdproc, "QRY", "%s 32", "PROD0038W!61ZTF9"); - - msn_transaction_set_payload(trans, buf, 32); - - msn_cmdproc_send_trans(cmdproc, trans); -} - -/************************************************************************** - * Buddy Lists - **************************************************************************/ - -static void -add_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - MsnUser *user; - const char *list; - const char *passport; - const char *friendly; - MsnListId list_id; - int group_id; - - list = cmd->params[1]; - passport = cmd->params[3]; - friendly = purple_url_decode(cmd->params[4]); - - session = cmdproc->session; - - user = msn_userlist_find_user(session->userlist, passport); - - if (user == NULL) - { - user = msn_user_new(session->userlist, passport, friendly); - msn_userlist_add_user(session->userlist, user); - } - else - msn_user_set_friendly_name(user, friendly); - - list_id = msn_get_list_id(list); - - if (cmd->param_count >= 6) - group_id = atoi(cmd->params[5]); - else - group_id = -1; - - msn_got_add_user(session, user, list_id, group_id); - msn_user_update(user); -} - -static void -add_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error) -{ - MsnSession *session; - PurpleAccount *account; - PurpleConnection *gc; - const char *list, *passport; - char *reason = NULL; - char *msg = NULL; - char **params; - - session = cmdproc->session; - account = session->account; - gc = purple_account_get_connection(account); - params = g_strsplit(trans->params, " ", 0); - - list = params[0]; - passport = params[1]; - - if (!strcmp(list, "FL")) - msg = g_strdup_printf(_("Unable to add user on %s (%s)"), - purple_account_get_username(account), - purple_account_get_protocol_name(account)); - else if (!strcmp(list, "BL")) - msg = g_strdup_printf(_("Unable to block user on %s (%s)"), - purple_account_get_username(account), - purple_account_get_protocol_name(account)); - else if (!strcmp(list, "AL")) - msg = g_strdup_printf(_("Unable to permit user on %s (%s)"), - purple_account_get_username(account), - purple_account_get_protocol_name(account)); - - if (!strcmp(list, "FL")) - { - if (error == 210) - { - reason = g_strdup_printf(_("%s could not be added because " - "your buddy list is full."), passport); - } - } - - if (reason == NULL) - { - if (error == 208) - { - reason = g_strdup_printf(_("%s is not a valid passport account."), - passport); - } - else if (error == 500) - { - reason = g_strdup(_("Service Temporarily Unavailable.")); - } - else - { - reason = g_strdup(_("Unknown error.")); - } - } - - if (msg != NULL) - { - purple_notify_error(gc, NULL, msg, reason); - g_free(msg); - } - - if (!strcmp(list, "FL")) - { - PurpleBuddy *buddy; - - buddy = purple_find_buddy(account, passport); - - if (buddy != NULL) - purple_blist_remove_buddy(buddy); - } - - g_free(reason); - - g_strfreev(params); -} - -static void -adg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - gint group_id; - const char *group_name; - - session = cmdproc->session; - - group_id = atoi(cmd->params[3]); - - group_name = purple_url_decode(cmd->params[2]); - - msn_group_new(session->userlist, group_id, group_name); - - /* There is a user that must me moved to this group */ - if (cmd->trans->data) - { - /* msn_userlist_move_buddy(); */ - MsnUserList *userlist = cmdproc->session->userlist; - MsnMoveBuddy *data = cmd->trans->data; - - if (data->old_group_name != NULL) - { - msn_userlist_rem_buddy(userlist, data->who, MSN_LIST_FL, data->old_group_name); - g_free(data->old_group_name); - } - - msn_userlist_add_buddy(userlist, data->who, MSN_LIST_FL, group_name); - g_free(data->who); - - } -} - -static void -qng_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - static int count = 0; - const char *passport; - PurpleAccount *account; - - session = cmdproc->session; - account = session->account; - - if (session->passport_info.file == NULL) - return; - - passport = purple_normalize(account, purple_account_get_username(account)); - - if ((strstr(passport, "@hotmail.") == NULL) && - (strstr(passport, "@live.com") == NULL) && - (strstr(passport, "@msn.com") == NULL)) - return; - - if (count++ < 26) - return; - - count = 0; - msn_cmdproc_send(cmdproc, "URL", "%s", "INBOX"); -} - - -static void -fln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSlpLink *slplink; - MsnUser *user; - - user = msn_userlist_find_user(cmdproc->session->userlist, cmd->params[0]); - - user->status = "offline"; - msn_user_update(user); - - slplink = msn_session_find_slplink(cmdproc->session, cmd->params[0]); - - if (slplink != NULL) - msn_slplink_destroy(slplink); - -} - -static void -iln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - PurpleAccount *account; - PurpleConnection *gc; - MsnUser *user; - MsnObject *msnobj; - const char *state, *passport, *friendly; - - session = cmdproc->session; - account = session->account; - gc = purple_account_get_connection(account); - - state = cmd->params[1]; - passport = cmd->params[2]; - friendly = purple_url_decode(cmd->params[3]); - - user = msn_userlist_find_user(session->userlist, passport); - - serv_got_alias(gc, passport, friendly); - - msn_user_set_friendly_name(user, friendly); - - if (session->protocol_ver >= 9 && cmd->param_count == 6) - { - msnobj = msn_object_new_from_string(purple_url_decode(cmd->params[5])); - msn_user_set_object(user, msnobj); - } - - msn_user_set_state(user, state); - msn_user_update(user); -} - -static void -ipg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len) -{ -#if 0 - purple_debug_misc("msn", "Incoming Page: {%s}\n", payload); -#endif -} - -static void -ipg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - cmdproc->servconn->payload_len = atoi(cmd->params[0]); - cmdproc->last_cmd->payload_cb = ipg_cmd_post; -} - -static void -nln_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - PurpleAccount *account; - PurpleConnection *gc; - MsnUser *user; - MsnObject *msnobj; - int clientid; - const char *state, *passport, *friendly, *old_friendly; - - session = cmdproc->session; - account = session->account; - gc = purple_account_get_connection(account); - - state = cmd->params[0]; - passport = cmd->params[1]; - friendly = purple_url_decode(cmd->params[2]); - - user = msn_userlist_find_user(session->userlist, passport); - - old_friendly = msn_user_get_friendly_name(user); - if (!old_friendly || (old_friendly && (!friendly || strcmp(old_friendly, friendly)))) - { - serv_got_alias(gc, passport, friendly); - msn_user_set_friendly_name(user, friendly); - } - - if (session->protocol_ver >= 9) - { - if (cmd->param_count == 5) - { - msnobj = - msn_object_new_from_string(purple_url_decode(cmd->params[4])); - msn_user_set_object(user, msnobj); - } - else - { - msn_user_set_object(user, NULL); - } - } - - clientid = atoi(cmd->params[3]); - user->mobile = (clientid & MSN_CLIENT_CAP_MSNMOBILE); - - msn_user_set_state(user, state); - msn_user_update(user); -} - -#if 0 -static void -chg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - char *state = cmd->params[1]; - int state_id = 0; - - if (!strcmp(state, "NLN")) - state_id = MSN_ONLINE; - else if (!strcmp(state, "BSY")) - state_id = MSN_BUSY; - else if (!strcmp(state, "IDL")) - state_id = MSN_IDLE; - else if (!strcmp(state, "BRB")) - state_id = MSN_BRB; - else if (!strcmp(state, "AWY")) - state_id = MSN_AWAY; - else if (!strcmp(state, "PHN")) - state_id = MSN_PHONE; - else if (!strcmp(state, "LUN")) - state_id = MSN_LUNCH; - else if (!strcmp(state, "HDN")) - state_id = MSN_HIDDEN; - - cmdproc->session->state = state_id; -} -#endif - - -static void -not_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len) -{ -#if 0 - MSN_SET_PARAMS("NOT %d\r\n%s", cmdproc->servconn->payload, payload); - purple_debug_misc("msn", "Notification: {%s}\n", payload); -#endif -} - -static void -not_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - cmdproc->servconn->payload_len = atoi(cmd->params[0]); - cmdproc->last_cmd->payload_cb = not_cmd_post; -} - -static void -rea_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - PurpleAccount *account; - PurpleConnection *gc; - const char *friendly; - char *username; - - session = cmdproc->session; - account = session->account; - username = g_strdup(purple_normalize(account, - purple_account_get_username(account))); - - /* Only set display name if our *own* friendly name changed! */ - if (strcmp(username, purple_normalize(account, cmd->params[2]))) - { - g_free(username); - return; - } - - g_free(username); - - gc = account->gc; - friendly = purple_url_decode(cmd->params[3]); - - purple_connection_set_display_name(gc, friendly); -} - -static void -prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session = cmdproc->session; - const char *type, *value; - - g_return_if_fail(cmd->param_count >= 3); - - type = cmd->params[2]; - - if (cmd->param_count == 4) - { - value = cmd->params[3]; - if (!strcmp(type, "PHH")) - msn_user_set_home_phone(session->user, purple_url_decode(value)); - else if (!strcmp(type, "PHW")) - msn_user_set_work_phone(session->user, purple_url_decode(value)); - else if (!strcmp(type, "PHM")) - msn_user_set_mobile_phone(session->user, purple_url_decode(value)); - } - else - { - if (!strcmp(type, "PHH")) - msn_user_set_home_phone(session->user, NULL); - else if (!strcmp(type, "PHW")) - msn_user_set_work_phone(session->user, NULL); - else if (!strcmp(type, "PHM")) - msn_user_set_mobile_phone(session->user, NULL); - } -} - -static void -bpr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - const char *type, *value, *passport; - MsnUser *user; - - passport = cmd->params[1]; - user = msn_userlist_find_user(cmdproc->session->userlist, passport); - - g_return_if_fail(user != NULL); - - type = cmd->params[2]; - value = cmd->params[3]; - - if (value) - { - if (!strcmp(type, "MOB")) - { - if (!strcmp(value, "Y")) - user->mobile = TRUE; - else if (!strcmp(value, "N")) - user->mobile = FALSE; - } - else if (!strcmp(type, "PHH")) - msn_user_set_home_phone(user, purple_url_decode(value)); - else if (!strcmp(type, "PHW")) - msn_user_set_work_phone(user, purple_url_decode(value)); - else if (!strcmp(type, "PHM")) - msn_user_set_mobile_phone(user, purple_url_decode(value)); - } -} - -static void -reg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - int group_id; - const char *group_name; - - session = cmdproc->session; - group_id = atoi(cmd->params[2]); - group_name = purple_url_decode(cmd->params[3]); - - msn_userlist_rename_group_id(session->userlist, group_id, group_name); -} - -static void -reg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error) -{ - int group_id; - char **params; - - params = g_strsplit(trans->params, " ", 0); - - group_id = atoi(params[0]); - - group_error_helper(cmdproc->session, _("Unable to rename group"), group_id, error); - - g_strfreev(params); -} - -static void -rem_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - MsnUser *user; - const char *list; - const char *passport; - MsnListId list_id; - int group_id; - - session = cmdproc->session; - list = cmd->params[1]; - passport = cmd->params[3]; - user = msn_userlist_find_user(session->userlist, passport); - - g_return_if_fail(user != NULL); - - list_id = msn_get_list_id(list); - - if (cmd->param_count == 5) - group_id = atoi(cmd->params[4]); - else - group_id = -1; - - msn_got_rem_user(session, user, list_id, group_id); - msn_user_update(user); -} - -static void -rmg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - int group_id; - - session = cmdproc->session; - group_id = atoi(cmd->params[2]); - - msn_userlist_remove_group_id(session->userlist, group_id); -} - -static void -rmg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error) -{ - int group_id; - char **params; - - params = g_strsplit(trans->params, " ", 0); - - group_id = atoi(params[0]); - - group_error_helper(cmdproc->session, _("Unable to delete group"), group_id, error); - - g_strfreev(params); -} - -static void -syn_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - MsnSync *sync; - int total_users; - - session = cmdproc->session; - - if (cmd->param_count == 2) - { - /* - * This can happen if we sent a SYN with an up-to-date - * buddy list revision, but we send 0 to get a full list. - * So, error out. - */ - - msn_session_set_error(cmdproc->session, MSN_ERROR_BAD_BLIST, NULL); - return; - } - - total_users = atoi(cmd->params[2]); - - sync = msn_sync_new(session); - sync->total_users = total_users; - sync->old_cbs_table = cmdproc->cbs_table; - - session->sync = sync; - cmdproc->cbs_table = sync->cbs_table; -} - -/************************************************************************** - * Misc commands - **************************************************************************/ - -static void -url_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - PurpleAccount *account; - const char *rru; - const char *url; - PurpleCipher *cipher; - PurpleCipherContext *context; - guchar digest[16]; - FILE *fd; - char *buf; - char buf2[3]; - char sendbuf[64]; - int i; - - session = cmdproc->session; - account = session->account; - - rru = cmd->params[1]; - url = cmd->params[2]; - - buf = g_strdup_printf("%s%lu%s", - session->passport_info.mspauth ? session->passport_info.mspauth : "BOGUS", - time(NULL) - session->passport_info.sl, - purple_connection_get_password(account->gc)); - - cipher = purple_ciphers_find_cipher("md5"); - context = purple_cipher_context_new(cipher, NULL); - - purple_cipher_context_append(context, (const guchar *)buf, strlen(buf)); - purple_cipher_context_digest(context, sizeof(digest), digest, NULL); - purple_cipher_context_destroy(context); - - g_free(buf); - - memset(sendbuf, 0, sizeof(sendbuf)); - - for (i = 0; i < 16; i++) - { - g_snprintf(buf2, sizeof(buf2), "%02x", digest[i]); - strcat(sendbuf, buf2); - } - - if (session->passport_info.file != NULL) - { - g_unlink(session->passport_info.file); - g_free(session->passport_info.file); - } - - if ((fd = purple_mkstemp(&session->passport_info.file, FALSE)) == NULL) - { - purple_debug_error("msn", - "Error opening temp passport file: %s\n", - g_strerror(errno)); - } - else - { -#ifdef _WIN32 - fputs("<!-- saved from url=(0013)about:internet -->\n", fd); -#endif - fputs("<html>\n" - "<head>\n" - "<noscript>\n" - "<meta http-equiv=\"Refresh\" content=\"0; " - "url=http://www.hotmail.com\">\n" - "</noscript>\n" - "</head>\n\n", - fd); - - fprintf(fd, "<body onload=\"document.pform.submit(); \">\n"); - fprintf(fd, "<form name=\"pform\" action=\"%s\" method=\"POST\">\n\n", - url); - fprintf(fd, "<input type=\"hidden\" name=\"mode\" value=\"ttl\">\n"); - fprintf(fd, "<input type=\"hidden\" name=\"login\" value=\"%s\">\n", - purple_account_get_username(account)); - fprintf(fd, "<input type=\"hidden\" name=\"username\" value=\"%s\">\n", - purple_account_get_username(account)); - if (session->passport_info.sid != NULL) - fprintf(fd, "<input type=\"hidden\" name=\"sid\" value=\"%s\">\n", - session->passport_info.sid); - if (session->passport_info.kv != NULL) - fprintf(fd, "<input type=\"hidden\" name=\"kv\" value=\"%s\">\n", - session->passport_info.kv); - fprintf(fd, "<input type=\"hidden\" name=\"id\" value=\"2\">\n"); - fprintf(fd, "<input type=\"hidden\" name=\"sl\" value=\"%ld\">\n", - time(NULL) - session->passport_info.sl); - fprintf(fd, "<input type=\"hidden\" name=\"rru\" value=\"%s\">\n", - rru); - if (session->passport_info.mspauth != NULL) - fprintf(fd, "<input type=\"hidden\" name=\"auth\" value=\"%s\">\n", - session->passport_info.mspauth); - fprintf(fd, "<input type=\"hidden\" name=\"creds\" value=\"%s\">\n", - sendbuf); /* TODO Digest me (huh? -- ChipX86) */ - fprintf(fd, "<input type=\"hidden\" name=\"svc\" value=\"mail\">\n"); - fprintf(fd, "<input type=\"hidden\" name=\"js\" value=\"yes\">\n"); - fprintf(fd, "</form></body>\n"); - fprintf(fd, "</html>\n"); - - if (fclose(fd)) - { - purple_debug_error("msn", - "Error closing temp passport file: %s\n", - g_strerror(errno)); - - g_unlink(session->passport_info.file); - g_free(session->passport_info.file); - session->passport_info.file = NULL; - } -#ifdef _WIN32 - else - { - /* - * Renaming file with .html extension, so that the - * win32 open_url will work. - */ - char *tmp; - - if ((tmp = - g_strdup_printf("%s.html", - session->passport_info.file)) != NULL) - { - if (g_rename(session->passport_info.file, - tmp) == 0) - { - g_free(session->passport_info.file); - session->passport_info.file = tmp; - } - else - g_free(tmp); - } - } -#endif - } -} -/************************************************************************** - * Switchboards - **************************************************************************/ - -static void -rng_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - MsnSwitchBoard *swboard; - const char *session_id; - char *host; - int port; - - session = cmdproc->session; - session_id = cmd->params[0]; - - msn_parse_socket(cmd->params[1], &host, &port); - - if (session->http_method) - port = 80; - - swboard = msn_switchboard_new(session); - - msn_switchboard_set_invited(swboard, TRUE); - msn_switchboard_set_session_id(swboard, cmd->params[0]); - msn_switchboard_set_auth_key(swboard, cmd->params[3]); - swboard->im_user = g_strdup(cmd->params[4]); - /* msn_switchboard_add_user(swboard, cmd->params[4]); */ - - if (!msn_switchboard_connect(swboard, host, port)) - msn_switchboard_destroy(swboard); - - g_free(host); -} - -static void -xfr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - char *host; - int port; - - if (strcmp(cmd->params[1], "SB") && strcmp(cmd->params[1], "NS")) - { - /* Maybe we can have a generic bad command error. */ - purple_debug_error("msn", "Bad XFR command (%s)\n", cmd->params[1]); - return; - } - - msn_parse_socket(cmd->params[2], &host, &port); - - if (!strcmp(cmd->params[1], "SB")) - { - purple_debug_error("msn", "This shouldn't be handled here.\n"); - } - else if (!strcmp(cmd->params[1], "NS")) - { - MsnSession *session; - - session = cmdproc->session; - - msn_session_set_login_step(session, MSN_LOGIN_STEP_TRANSFER); - - msn_notification_connect(session->notification, host, port); - } - - g_free(host); -} - -/************************************************************************** - * Message Types - **************************************************************************/ - -static void -profile_msg(MsnCmdProc *cmdproc, MsnMessage *msg) -{ - MsnSession *session; - const char *value; - - session = cmdproc->session; - - if (strcmp(msg->remote_user, "Hotmail")) - /* This isn't an official message. */ - return; - - if ((value = msn_message_get_attr(msg, "kv")) != NULL) - { - g_free(session->passport_info.kv); - session->passport_info.kv = g_strdup(value); - } - - if ((value = msn_message_get_attr(msg, "sid")) != NULL) - { - g_free(session->passport_info.sid); - session->passport_info.sid = g_strdup(value); - } - - if ((value = msn_message_get_attr(msg, "MSPAuth")) != NULL) - { - g_free(session->passport_info.mspauth); - session->passport_info.mspauth = g_strdup(value); - } - - if ((value = msn_message_get_attr(msg, "ClientIP")) != NULL) - { - g_free(session->passport_info.client_ip); - session->passport_info.client_ip = g_strdup(value); - } - - if ((value = msn_message_get_attr(msg, "ClientPort")) != NULL) - session->passport_info.client_port = ntohs(atoi(value)); - - if ((value = msn_message_get_attr(msg, "LoginTime")) != NULL) - session->passport_info.sl = atol(value); -} - -static void -initial_email_msg(MsnCmdProc *cmdproc, MsnMessage *msg) -{ - MsnSession *session; - PurpleConnection *gc; - GHashTable *table; - const char *unread; - - session = cmdproc->session; - gc = session->account->gc; - - if (strcmp(msg->remote_user, "Hotmail")) - /* This isn't an official message. */ - return; - - if (session->passport_info.file == NULL) - { - MsnTransaction *trans; - trans = msn_transaction_new(cmdproc, "URL", "%s", "INBOX"); - msn_transaction_queue_cmd(trans, msg->cmd); - - msn_cmdproc_send_trans(cmdproc, trans); - - return; - } - - if (!purple_account_get_check_mail(session->account)) - return; - - table = msn_message_get_hashtable_from_body(msg); - - unread = g_hash_table_lookup(table, "Inbox-Unread"); - - if (unread != NULL) - { - int count = atoi(unread); - - if (count > 0) - { - const char *passports[2] = { msn_user_get_passport(session->user) }; - const char *urls[2] = { session->passport_info.file }; - - purple_notify_emails(gc, count, FALSE, NULL, NULL, - passports, urls, NULL, NULL); - } - } - - g_hash_table_destroy(table); -} - -static void -email_msg(MsnCmdProc *cmdproc, MsnMessage *msg) -{ - MsnSession *session; - PurpleConnection *gc; - GHashTable *table; - char *from, *subject, *tmp; - - session = cmdproc->session; - gc = session->account->gc; - - if (strcmp(msg->remote_user, "Hotmail")) - /* This isn't an official message. */ - return; - - if (session->passport_info.file == NULL) - { - MsnTransaction *trans; - trans = msn_transaction_new(cmdproc, "URL", "%s", "INBOX"); - msn_transaction_queue_cmd(trans, msg->cmd); - - msn_cmdproc_send_trans(cmdproc, trans); - - return; - } - - if (!purple_account_get_check_mail(session->account)) - return; - - table = msn_message_get_hashtable_from_body(msg); - - from = subject = NULL; - - tmp = g_hash_table_lookup(table, "From"); - if (tmp != NULL) - from = purple_mime_decode_field(tmp); - - tmp = g_hash_table_lookup(table, "Subject"); - if (tmp != NULL) - subject = purple_mime_decode_field(tmp); - - purple_notify_email(gc, - (subject != NULL ? subject : ""), - (from != NULL ? from : ""), - msn_user_get_passport(session->user), - session->passport_info.file, NULL, NULL); - - g_free(from); - g_free(subject); - - g_hash_table_destroy(table); -} - -static void -system_msg(MsnCmdProc *cmdproc, MsnMessage *msg) -{ - GHashTable *table; - const char *type_s; - - if (strcmp(msg->remote_user, "Hotmail")) - /* This isn't an official message. */ - return; - - table = msn_message_get_hashtable_from_body(msg); - - if ((type_s = g_hash_table_lookup(table, "Type")) != NULL) - { - int type = atoi(type_s); - char buf[MSN_BUF_LEN]; - int minutes; - - switch (type) - { - case 1: - minutes = atoi(g_hash_table_lookup(table, "Arg1")); - g_snprintf(buf, sizeof(buf), dngettext(PACKAGE, - "The MSN server will shut down for maintenance " - "in %d minute. You will automatically be " - "signed out at that time. Please finish any " - "conversations in progress.\n\nAfter the " - "maintenance has been completed, you will be " - "able to successfully sign in.", - "The MSN server will shut down for maintenance " - "in %d minutes. You will automatically be " - "signed out at that time. Please finish any " - "conversations in progress.\n\nAfter the " - "maintenance has been completed, you will be " - "able to successfully sign in.", minutes), - minutes); - default: - break; - } - - if (*buf != '\0') - purple_notify_info(cmdproc->session->account->gc, NULL, buf, NULL); - } - - g_hash_table_destroy(table); -} - -void -msn_notification_add_buddy(MsnNotification *notification, const char *list, - const char *who, const char *friendly_name, - int group_id) -{ - MsnCmdProc *cmdproc; - cmdproc = notification->servconn->cmdproc; - - if (group_id < 0 && !strcmp(list, "FL")) - group_id = 0; - - if (group_id >= 0) - { - msn_cmdproc_send(cmdproc, "ADD", "%s %s %s %d", - list, who, friendly_name, group_id); - } - else - { - msn_cmdproc_send(cmdproc, "ADD", "%s %s %s", list, who, friendly_name); - } -} - -void -msn_notification_rem_buddy(MsnNotification *notification, const char *list, - const char *who, int group_id) -{ - MsnCmdProc *cmdproc; - cmdproc = notification->servconn->cmdproc; - - if (group_id >= 0) - { - msn_cmdproc_send(cmdproc, "REM", "%s %s %d", list, who, group_id); - } - else - { - msn_cmdproc_send(cmdproc, "REM", "%s %s", list, who); - } -} - -/************************************************************************** - * Init - **************************************************************************/ - -void -msn_notification_init(void) -{ - /* TODO: check prp, blp */ - - cbs_table = msn_table_new(); - - /* Synchronous */ - msn_table_add_cmd(cbs_table, "CHG", "CHG", NULL); - msn_table_add_cmd(cbs_table, "CHG", "ILN", iln_cmd); - msn_table_add_cmd(cbs_table, "ADD", "ADD", add_cmd); - msn_table_add_cmd(cbs_table, "ADD", "ILN", iln_cmd); - msn_table_add_cmd(cbs_table, "REM", "REM", rem_cmd); - msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd); - msn_table_add_cmd(cbs_table, "USR", "XFR", xfr_cmd); - msn_table_add_cmd(cbs_table, "SYN", "SYN", syn_cmd); - msn_table_add_cmd(cbs_table, "CVR", "CVR", cvr_cmd); - msn_table_add_cmd(cbs_table, "VER", "VER", ver_cmd); - msn_table_add_cmd(cbs_table, "REA", "REA", rea_cmd); - msn_table_add_cmd(cbs_table, "PRP", "PRP", prp_cmd); - /* msn_table_add_cmd(cbs_table, "BLP", "BLP", blp_cmd); */ - msn_table_add_cmd(cbs_table, "BLP", "BLP", NULL); - msn_table_add_cmd(cbs_table, "REG", "REG", reg_cmd); - msn_table_add_cmd(cbs_table, "ADG", "ADG", adg_cmd); - msn_table_add_cmd(cbs_table, "RMG", "RMG", rmg_cmd); - msn_table_add_cmd(cbs_table, "XFR", "XFR", xfr_cmd); - - /* Asynchronous */ - msn_table_add_cmd(cbs_table, NULL, "IPG", ipg_cmd); - msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd); - msn_table_add_cmd(cbs_table, NULL, "NOT", not_cmd); - msn_table_add_cmd(cbs_table, NULL, "BPR", bpr_cmd); - - msn_table_add_cmd(cbs_table, NULL, "CHL", chl_cmd); - msn_table_add_cmd(cbs_table, NULL, "REM", rem_cmd); - msn_table_add_cmd(cbs_table, NULL, "ADD", add_cmd); - - msn_table_add_cmd(cbs_table, NULL, "QRY", NULL); - msn_table_add_cmd(cbs_table, NULL, "QNG", qng_cmd); - msn_table_add_cmd(cbs_table, NULL, "FLN", fln_cmd); - msn_table_add_cmd(cbs_table, NULL, "NLN", nln_cmd); - msn_table_add_cmd(cbs_table, NULL, "ILN", iln_cmd); - msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd); - msn_table_add_cmd(cbs_table, NULL, "RNG", rng_cmd); - - msn_table_add_cmd(cbs_table, NULL, "URL", url_cmd); - - msn_table_add_cmd(cbs_table, "fallback", "XFR", xfr_cmd); - - msn_table_add_error(cbs_table, "ADD", add_error); - msn_table_add_error(cbs_table, "REG", reg_error); - msn_table_add_error(cbs_table, "RMG", rmg_error); - /* msn_table_add_error(cbs_table, "REA", rea_error); */ - msn_table_add_error(cbs_table, "USR", usr_error); - - msn_table_add_msg_type(cbs_table, - "text/x-msmsgsprofile", - profile_msg); - msn_table_add_msg_type(cbs_table, - "text/x-msmsgsinitialemailnotification", - initial_email_msg); - msn_table_add_msg_type(cbs_table, - "text/x-msmsgsemailnotification", - email_msg); - msn_table_add_msg_type(cbs_table, - "application/x-msmsgssystemmessage", - system_msg); -} - -void -msn_notification_end(void) -{ - msn_table_destroy(cbs_table); -}
--- a/libpurple/protocols/msnp9/notification.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,70 +0,0 @@ -/** - * @file notification.h Notification server functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_NOTIFICATION_H_ -#define _MSN_NOTIFICATION_H_ - -typedef struct _MsnNotification MsnNotification; - -#include "session.h" -#include "servconn.h" -#include "cmdproc.h" - -struct _MsnNotification -{ - MsnSession *session; - MsnCmdProc *cmdproc; - MsnServConn *servconn; - - gboolean in_use; -}; - -#include "state.h" - -void msn_notification_end(void); -void msn_notification_init(void); - -void msn_notification_add_buddy(MsnNotification *notification, - const char *list, const char *who, - const char *friendly_name, int group_id); -void msn_notification_rem_buddy(MsnNotification *notification, - const char *list, const char *who, - int group_id); -MsnNotification *msn_notification_new(MsnSession *session); -void msn_notification_destroy(MsnNotification *notification); -gboolean msn_notification_connect(MsnNotification *notification, - const char *host, int port); -void msn_notification_disconnect(MsnNotification *notification); - -/** - * Closes a notification. - * - * It's first closed, and then disconnected. - * - * @param notification The notification object to close. - */ -void msn_notification_close(MsnNotification *notification); - -void msn_got_login_params(MsnSession *session, const char *login_params); - -#endif /* _MSN_NOTIFICATION_H_ */
--- a/libpurple/protocols/msnp9/object.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,417 +0,0 @@ -/** - * @file object.c MSNObject API - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "object.h" -#include "debug.h" -/* Sha1 stuff */ -#include "cipher.h" -/* Base64 stuff */ -#include "util.h" - -#define GET_STRING_TAG(field, id) \ - if ((tag = strstr(str, id "=\"")) != NULL) \ - { \ - tag += strlen(id "=\""); \ - c = strchr(tag, '"'); \ - if (c != NULL) \ - { \ - if (obj->field != NULL) \ - g_free(obj->field); \ - obj->field = g_strndup(tag, c - tag); \ - } \ - } - -#define GET_INT_TAG(field, id) \ - if ((tag = strstr(str, id "=\"")) != NULL) \ - { \ - char buf[16]; \ - size_t offset; \ - tag += strlen(id "=\""); \ - c = strchr(tag, '"'); \ - if (c != NULL) \ - { \ - memset(buf, 0, sizeof(buf)); \ - offset = c - tag; \ - if (offset >= sizeof(buf)) \ - offset = sizeof(buf) - 1; \ - strncpy(buf, tag, offset); \ - obj->field = atoi(buf); \ - } \ - } - -static GList *local_objs; - -MsnObject * -msn_object_new(void) -{ - MsnObject *obj; - - obj = g_new0(MsnObject, 1); - - msn_object_set_type(obj, MSN_OBJECT_UNKNOWN); - msn_object_set_friendly(obj, "AAA="); - - return obj; -} - -MsnObject * -msn_object_new_from_string(const char *str) -{ - MsnObject *obj; - char *tag, *c; - - g_return_val_if_fail(str != NULL, NULL); - - if (strncmp(str, "<msnobj ", 8)) - return NULL; - - obj = msn_object_new(); - - GET_STRING_TAG(creator, "Creator"); - GET_INT_TAG(size, "Size"); - GET_INT_TAG(type, "Type"); - GET_STRING_TAG(location, "Location"); - GET_STRING_TAG(friendly, "Friendly"); - GET_STRING_TAG(sha1d, "SHA1D"); - GET_STRING_TAG(sha1c, "SHA1C"); - - /* If we are missing any of the required elements then discard the object */ - /* SHA1C is not always sent anymore */ - if (obj->creator == NULL || obj->size == 0 || obj->type == 0 - || obj->location == NULL || obj->friendly == NULL - || obj->sha1d == NULL /*|| obj->sha1c == NULL*/) { - purple_debug_error("msn", "Discarding invalid msnobj: '%s'\n", str); - msn_object_destroy(obj); - obj = NULL; - } - - return obj; -} - -MsnObject* -msn_object_new_from_image(PurpleStoredImage *img, const char *location, - const char *creator, MsnObjectType type) -{ - MsnObject *msnobj; - - PurpleCipherContext *ctx; - char *buf; - gconstpointer data; - size_t size; - char *base64; - unsigned char digest[20]; - - msnobj = NULL; - - if (img == NULL) - return msnobj; - - size = purple_imgstore_get_size(img); - data = purple_imgstore_get_data(img); - - /* New object */ - msnobj = msn_object_new(); - msn_object_set_local(msnobj); - msn_object_set_type(msnobj, type); - msn_object_set_location(msnobj, location); - msn_object_set_creator(msnobj, creator); - - msn_object_set_image(msnobj, img); - - /* Compute the SHA1D field. */ - memset(digest, 0, sizeof(digest)); - - ctx = purple_cipher_context_new_by_name("sha1", NULL); - purple_cipher_context_append(ctx, data, size); - purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL); - - base64 = purple_base64_encode(digest, sizeof(digest)); - msn_object_set_sha1d(msnobj, base64); - g_free(base64); - - msn_object_set_size(msnobj, size); - - /* Compute the SHA1C field. */ - buf = g_strdup_printf( - "Creator%sSize%dType%dLocation%sFriendly%sSHA1D%s", - msn_object_get_creator(msnobj), - msn_object_get_size(msnobj), - msn_object_get_type(msnobj), - msn_object_get_location(msnobj), - msn_object_get_friendly(msnobj), - msn_object_get_sha1d(msnobj)); - - memset(digest, 0, sizeof(digest)); - - purple_cipher_context_reset(ctx, NULL); - purple_cipher_context_append(ctx, (const guchar *)buf, strlen(buf)); - purple_cipher_context_digest(ctx, sizeof(digest), digest, NULL); - purple_cipher_context_destroy(ctx); - g_free(buf); - - base64 = purple_base64_encode(digest, sizeof(digest)); - msn_object_set_sha1c(msnobj, base64); - g_free(base64); - - return msnobj; -} - -void -msn_object_destroy(MsnObject *obj) -{ - g_return_if_fail(obj != NULL); - - g_free(obj->creator); - g_free(obj->location); - g_free(obj->friendly); - g_free(obj->sha1d); - g_free(obj->sha1c); - - purple_imgstore_unref(obj->img); - - if (obj->local) - local_objs = g_list_remove(local_objs, obj); - - g_free(obj); -} - -char * -msn_object_to_string(const MsnObject *obj) -{ - char *str; - const char *sha1c; - - g_return_val_if_fail(obj != NULL, NULL); - - sha1c = msn_object_get_sha1c(obj); - - str = g_strdup_printf("<msnobj Creator=\"%s\" Size=\"%d\" Type=\"%d\" " - "Location=\"%s\" Friendly=\"%s\" SHA1D=\"%s\"" - "%s%s%s/>", - msn_object_get_creator(obj), - msn_object_get_size(obj), - msn_object_get_type(obj), - msn_object_get_location(obj), - msn_object_get_friendly(obj), - msn_object_get_sha1d(obj), - sha1c ? " SHA1C=\"" : "", - sha1c ? sha1c : "", - sha1c ? "\"" : ""); - - return str; -} - -void -msn_object_set_creator(MsnObject *obj, const char *creator) -{ - g_return_if_fail(obj != NULL); - - if (obj->creator != NULL) - g_free(obj->creator); - - obj->creator = (creator == NULL ? NULL : g_strdup(creator)); -} - -void -msn_object_set_size(MsnObject *obj, int size) -{ - g_return_if_fail(obj != NULL); - - obj->size = size; -} - -void -msn_object_set_type(MsnObject *obj, MsnObjectType type) -{ - g_return_if_fail(obj != NULL); - - obj->type = type; -} - -void -msn_object_set_location(MsnObject *obj, const char *location) -{ - g_return_if_fail(obj != NULL); - - if (obj->location != NULL) - g_free(obj->location); - - obj->location = (location == NULL ? NULL : g_strdup(location)); -} - -void -msn_object_set_friendly(MsnObject *obj, const char *friendly) -{ - g_return_if_fail(obj != NULL); - - if (obj->friendly != NULL) - g_free(obj->friendly); - - obj->friendly = (friendly == NULL ? NULL : g_strdup(friendly)); -} - -void -msn_object_set_sha1d(MsnObject *obj, const char *sha1d) -{ - g_return_if_fail(obj != NULL); - - if (obj->sha1d != NULL) - g_free(obj->sha1d); - - obj->sha1d = (sha1d == NULL ? NULL : g_strdup(sha1d)); -} - -void -msn_object_set_sha1c(MsnObject *obj, const char *sha1c) -{ - g_return_if_fail(obj != NULL); - - if (obj->sha1c != NULL) - g_free(obj->sha1c); - - obj->sha1c = (sha1c == NULL ? NULL : g_strdup(sha1c)); -} - -const char * -msn_object_get_creator(const MsnObject *obj) -{ - g_return_val_if_fail(obj != NULL, NULL); - - return obj->creator; -} - -int -msn_object_get_size(const MsnObject *obj) -{ - g_return_val_if_fail(obj != NULL, 0); - - return obj->size; -} - -MsnObjectType -msn_object_get_type(const MsnObject *obj) -{ - g_return_val_if_fail(obj != NULL, MSN_OBJECT_UNKNOWN); - - return obj->type; -} - -const char * -msn_object_get_location(const MsnObject *obj) -{ - g_return_val_if_fail(obj != NULL, NULL); - - return obj->location; -} - -const char * -msn_object_get_friendly(const MsnObject *obj) -{ - g_return_val_if_fail(obj != NULL, NULL); - - return obj->friendly; -} - -const char * -msn_object_get_sha1d(const MsnObject *obj) -{ - g_return_val_if_fail(obj != NULL, NULL); - - return obj->sha1d; -} - -const char * -msn_object_get_sha1c(const MsnObject *obj) -{ - g_return_val_if_fail(obj != NULL, NULL); - - return obj->sha1c; -} - -const char * -msn_object_get_sha1(const MsnObject *obj) -{ - g_return_val_if_fail(obj != NULL, NULL); - - if(obj->sha1c != NULL) { - return obj->sha1c; - } else { - return obj->sha1d; - } -} - -static MsnObject * -msn_object_find_local(const char *sha1) -{ - GList *l; - - g_return_val_if_fail(sha1 != NULL, NULL); - - for (l = local_objs; l != NULL; l = l->next) - { - MsnObject *local_obj = l->data; - - if (!strcmp(msn_object_get_sha1(local_obj), sha1)) - return local_obj; - } - - return NULL; - -} - -void -msn_object_set_local(MsnObject *obj) -{ - g_return_if_fail(obj != NULL); - - obj->local = TRUE; - - local_objs = g_list_append(local_objs, obj); -} - -void -msn_object_set_image(MsnObject *obj, PurpleStoredImage *img) -{ - g_return_if_fail(obj != NULL); - g_return_if_fail(img != NULL); - - /* obj->local = TRUE; */ - - purple_imgstore_unref(obj->img); - obj->img = purple_imgstore_ref(img); -} - -PurpleStoredImage * -msn_object_get_image(const MsnObject *obj) -{ - MsnObject *local_obj; - - g_return_val_if_fail(obj != NULL, NULL); - - local_obj = msn_object_find_local(msn_object_get_sha1(obj)); - - if (local_obj != NULL) - return local_obj->img; - - return NULL; -}
--- a/libpurple/protocols/msnp9/object.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,242 +0,0 @@ -/** - * @file object.h MSNObject API - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_OBJECT_H_ -#define _MSN_OBJECT_H_ - -#include "imgstore.h" - -#include "internal.h" - -typedef enum -{ - MSN_OBJECT_UNKNOWN = -1, /**< Unknown object */ - MSN_OBJECT_RESERVED1 = 1, /**< Reserved */ - MSN_OBJECT_EMOTICON = 2, /**< Custom Emoticon */ - MSN_OBJECT_USERTILE = 3, /**< UserTile (buddy icon) */ - MSN_OBJECT_RESERVED2 = 4, /**< Reserved */ - MSN_OBJECT_BACKGROUND = 5 /**< Background */ - -} MsnObjectType; - -typedef struct -{ - gboolean local; - - char *creator; - int size; - MsnObjectType type; - PurpleStoredImage *img; - char *location; - char *friendly; - char *sha1d; - char *sha1c; - -} MsnObject; - -/** - * Creates a MsnObject structure. - * - * @return A new MsnObject structure. - */ -MsnObject *msn_object_new(void); - -/** - * Creates a MsnObject structure from a string. - * - * @param str The string. - * - * @return The new MsnObject structure. - */ -MsnObject *msn_object_new_from_string(const char *str); - -/** - * Creates a MsnObject structure from a stored image - * - * @param img The image associated to object - * @param location The object location as stored in MsnObject - * @param creator The creator of the object - * @param type The type of the object - * - * @return A new MsnObject structure - */ -MsnObject *msn_object_new_from_image(PurpleStoredImage *img, - const char *location, const char *creator, MsnObjectType type); - -/** - * Destroys an MsnObject structure. - * - * @param obj The object structure. - */ -void msn_object_destroy(MsnObject *obj); - -/** - * Outputs a string representation of an MsnObject. - * - * @param obj The object. - * - * @return The string representation. This must be freed. - */ -char *msn_object_to_string(const MsnObject *obj); - -/** - * Sets the creator field in a MsnObject. - * - * @param creator The creator value. - */ -void msn_object_set_creator(MsnObject *obj, const char *creator); - -/** - * Sets the size field in a MsnObject. - * - * @param size The size value. - */ -void msn_object_set_size(MsnObject *obj, int size); - -/** - * Sets the type field in a MsnObject. - * - * @param type The type value. - */ -void msn_object_set_type(MsnObject *obj, MsnObjectType type); - -/** - * Sets the location field in a MsnObject. - * - * @param location The location value. - */ -void msn_object_set_location(MsnObject *obj, const char *location); - -/** - * Sets the friendly name field in a MsnObject. - * - * @param friendly The friendly name value. - */ -void msn_object_set_friendly(MsnObject *obj, const char *friendly); - -/** - * Sets the SHA1D field in a MsnObject. - * - * @param sha1d The sha1d value. - */ -void msn_object_set_sha1d(MsnObject *obj, const char *sha1d); - -/** - * Sets the SHA1C field in a MsnObject. - * - * @param sha1c The sha1c value. - */ -void msn_object_set_sha1c(MsnObject *obj, const char *sha1c); - -/** - * Associates an image with a MsnObject. - * - * @param obj The object. - * @param img The image to associate. - */ -void msn_object_set_image(MsnObject *obj, PurpleStoredImage *img); - -/** - * Returns a MsnObject's creator value. - * - * @param obj The object. - * - * @return The creator value. - */ -const char *msn_object_get_creator(const MsnObject *obj); - -/** - * Returns a MsnObject's size value. - * - * @param obj The object. - * - * @return The size value. - */ -int msn_object_get_size(const MsnObject *obj); - -/** - * Returns a MsnObject's type. - * - * @param obj The object. - * - * @return The object type. - */ -MsnObjectType msn_object_get_type(const MsnObject *obj); - -/** - * Returns a MsnObject's location value. - * - * @param obj The object. - * - * @return The location value. - */ -const char *msn_object_get_location(const MsnObject *obj); - -/** - * Returns a MsnObject's friendly name value. - * - * @param obj The object. - * - * @return The friendly name value. - */ -const char *msn_object_get_friendly(const MsnObject *obj); - -/** - * Returns a MsnObject's SHA1D value. - * - * @param obj The object. - * - * @return The SHA1D value. - */ -const char *msn_object_get_sha1d(const MsnObject *obj); - -/** - * Returns a MsnObject's SHA1C value. - * - * @param obj The object. - * - * @return The SHA1C value. - */ -const char *msn_object_get_sha1c(const MsnObject *obj); - -/** - * Returns a MsnObject's SHA1C value if it exists, otherwise SHA1D. - * - * @param obj The object. - * - * @return The SHA1C value. - */ -const char *msn_object_get_sha1(const MsnObject *obj); - -/** - * Returns the image associated with the MsnObject. - * - * @param obj The object. - * - * @return The associated image. - */ -PurpleStoredImage *msn_object_get_image(const MsnObject *obj); - -void msn_object_set_local(MsnObject *obj); - -#endif /* _MSN_OBJECT_H_ */
--- a/libpurple/protocols/msnp9/page.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,89 +0,0 @@ -/** - * @file page.c Paging functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "page.h" - -MsnPage * -msn_page_new(void) -{ - MsnPage *page; - - page = g_new0(MsnPage, 1); - - return page; -} - -void -msn_page_destroy(MsnPage *page) -{ - g_return_if_fail(page != NULL); - - if (page->body != NULL) - g_free(page->body); - - if (page->from_location != NULL) - g_free(page->from_location); - - if (page->from_phone != NULL) - g_free(page->from_phone); - - g_free(page); -} - -char * -msn_page_gen_payload(const MsnPage *page, size_t *ret_size) -{ - char *str; - - g_return_val_if_fail(page != NULL, NULL); - - str = - g_strdup_printf("<TEXT xml:space=\"preserve\" enc=\"utf-8\">%s</TEXT>", - msn_page_get_body(page)); - - if (ret_size != NULL) - *ret_size = strlen(str); - - return str; -} - -void -msn_page_set_body(MsnPage *page, const char *body) -{ - g_return_if_fail(page != NULL); - g_return_if_fail(body != NULL); - - if (page->body != NULL) - g_free(page->body); - - page->body = g_strdup(body); -} - -const char * -msn_page_get_body(const MsnPage *page) -{ - g_return_val_if_fail(page != NULL, NULL); - - return page->body; -}
--- a/libpurple/protocols/msnp9/page.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,81 +0,0 @@ -/** - * @file page.h Paging functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_PAGE_H_ -#define _MSN_PAGE_H_ - -typedef struct _MsnPage MsnPage; - -#include "session.h" - -/** - * A page. - */ -struct _MsnPage -{ - char *from_location; - char *from_phone; - - char *body; -}; - -/** - * Creates a new, empty page. - * - * @return A new page. - */ -MsnPage *msn_page_new(void); - -/** - * Destroys a page. - */ -void msn_page_destroy(MsnPage *page); - -/** - * Generates the payload data of a page. - * - * @param page The page. - * @param ret_size The returned size of the payload. - * - * @return The payload data of a page. - */ -char *msn_page_gen_payload(const MsnPage *page, size_t *ret_size); - -/** - * Sets the body of a page. - * - * @param page The page. - * @param body The body of the page. - */ -void msn_page_set_body(MsnPage *page, const char *body); - -/** - * Returns the body of the page. - * - * @param page The page. - * - * @return The body of the page. - */ -const char *msn_page_get_body(const MsnPage *page); - -#endif /* _MSN_PAGE_H_ */
--- a/libpurple/protocols/msnp9/servconn.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,556 +0,0 @@ -/** - * @file servconn.c Server connection functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "servconn.h" -#include "error.h" - -static void read_cb(gpointer data, gint source, PurpleInputCondition cond); - -/************************************************************************** - * Main - **************************************************************************/ - -MsnServConn * -msn_servconn_new(MsnSession *session, MsnServConnType type) -{ - MsnServConn *servconn; - - g_return_val_if_fail(session != NULL, NULL); - - servconn = g_new0(MsnServConn, 1); - - servconn->type = type; - - servconn->session = session; - servconn->cmdproc = msn_cmdproc_new(session); - servconn->cmdproc->servconn = servconn; - - servconn->httpconn = msn_httpconn_new(servconn); - - servconn->num = session->servconns_count++; - - servconn->tx_buf = purple_circ_buffer_new(MSN_BUF_LEN); - servconn->tx_handler = 0; - - return servconn; -} - -void -msn_servconn_destroy(MsnServConn *servconn) -{ - g_return_if_fail(servconn != NULL); - - if (servconn->processing) - { - servconn->wasted = TRUE; - return; - } - - if (servconn->connected) - msn_servconn_disconnect(servconn); - - if (servconn->destroy_cb) - servconn->destroy_cb(servconn); - - if (servconn->httpconn != NULL) - msn_httpconn_destroy(servconn->httpconn); - - g_free(servconn->host); - - purple_circ_buffer_destroy(servconn->tx_buf); - if (servconn->tx_handler > 0) - purple_input_remove(servconn->tx_handler); - - msn_cmdproc_destroy(servconn->cmdproc); - g_free(servconn); -} - -void -msn_servconn_set_connect_cb(MsnServConn *servconn, - void (*connect_cb)(MsnServConn *)) -{ - g_return_if_fail(servconn != NULL); - servconn->connect_cb = connect_cb; -} - -void -msn_servconn_set_disconnect_cb(MsnServConn *servconn, - void (*disconnect_cb)(MsnServConn *)) -{ - g_return_if_fail(servconn != NULL); - - servconn->disconnect_cb = disconnect_cb; -} - -void -msn_servconn_set_destroy_cb(MsnServConn *servconn, - void (*destroy_cb)(MsnServConn *)) -{ - g_return_if_fail(servconn != NULL); - - servconn->destroy_cb = destroy_cb; -} - -/************************************************************************** - * Utility - **************************************************************************/ - -void -msn_servconn_got_error(MsnServConn *servconn, MsnServConnError error) -{ - char *tmp; - const char *reason; - - const char *names[] = { "Notification", "Switchboard" }; - const char *name; - - name = names[servconn->type]; - - switch (error) - { - case MSN_SERVCONN_ERROR_CONNECT: - reason = _("Unable to connect"); break; - case MSN_SERVCONN_ERROR_WRITE: - reason = _("Writing error"); break; - case MSN_SERVCONN_ERROR_READ: - reason = _("Reading error"); break; - default: - reason = _("Unknown error"); break; - } - - purple_debug_error("msn", "Connection error from %s server (%s): %s\n", - name, servconn->host, reason); - tmp = g_strdup_printf(_("Connection error from %s server:\n%s"), - name, reason); - - if (servconn->type == MSN_SERVCONN_NS) - { - msn_session_set_error(servconn->session, MSN_ERROR_SERVCONN, tmp); - } - else if (servconn->type == MSN_SERVCONN_SB) - { - MsnSwitchBoard *swboard; - swboard = servconn->cmdproc->data; - if (swboard != NULL) - swboard->error = MSN_SB_ERROR_CONNECTION; - } - - msn_servconn_disconnect(servconn); - - g_free(tmp); -} - -/************************************************************************** - * Connect - **************************************************************************/ - -static void -connect_cb(gpointer data, gint source, const gchar *error_message) -{ - MsnServConn *servconn; - - servconn = data; - servconn->connect_data = NULL; - servconn->processing = FALSE; - - if (servconn->wasted) - { - if (source >= 0) - close(source); - msn_servconn_destroy(servconn); - return; - } - - servconn->fd = source; - - if (source >= 0) - { - servconn->connected = TRUE; - - /* Someone wants to know we connected. */ - servconn->connect_cb(servconn); - servconn->inpa = purple_input_add(servconn->fd, PURPLE_INPUT_READ, - read_cb, data); - } - else - { - purple_debug_error("msn", "Connection error: %s\n", error_message); - msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_CONNECT); - } -} - -gboolean -msn_servconn_connect(MsnServConn *servconn, const char *host, int port) -{ - MsnSession *session; - - g_return_val_if_fail(servconn != NULL, FALSE); - g_return_val_if_fail(host != NULL, FALSE); - g_return_val_if_fail(port > 0, FALSE); - - session = servconn->session; - - if (servconn->connected) - msn_servconn_disconnect(servconn); - - g_free(servconn->host); - servconn->host = g_strdup(host); - - if (session->http_method) - { - /* HTTP Connection. */ - - if (!servconn->httpconn->connected) - if (!msn_httpconn_connect(servconn->httpconn, host, port)) - return FALSE; - - servconn->connected = TRUE; - servconn->httpconn->virgin = TRUE; - - /* Someone wants to know we connected. */ - servconn->connect_cb(servconn); - - return TRUE; - } - - servconn->connect_data = purple_proxy_connect(NULL, session->account, - host, port, connect_cb, servconn); - - if (servconn->connect_data != NULL) - { - servconn->processing = TRUE; - return TRUE; - } - else - return FALSE; -} - -void -msn_servconn_disconnect(MsnServConn *servconn) -{ - g_return_if_fail(servconn != NULL); - - if (servconn->connect_data != NULL) - { - purple_proxy_connect_cancel(servconn->connect_data); - servconn->connect_data = NULL; - } - - if (!servconn->connected) - { - /* We could not connect. */ - if (servconn->disconnect_cb != NULL) - servconn->disconnect_cb(servconn); - - return; - } - - if (servconn->session->http_method) - { - /* Fake disconnection. */ - if (servconn->disconnect_cb != NULL) - servconn->disconnect_cb(servconn); - - return; - } - - if (servconn->inpa > 0) - { - purple_input_remove(servconn->inpa); - servconn->inpa = 0; - } - - close(servconn->fd); - - servconn->rx_buf = NULL; - servconn->rx_len = 0; - servconn->payload_len = 0; - - servconn->connected = FALSE; - - if (servconn->disconnect_cb != NULL) - servconn->disconnect_cb(servconn); -} - -static void -servconn_write_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - MsnServConn *servconn = data; - int ret, writelen; - - writelen = purple_circ_buffer_get_max_read(servconn->tx_buf); - - if (writelen == 0) { - purple_input_remove(servconn->tx_handler); - servconn->tx_handler = 0; - return; - } - - ret = write(servconn->fd, servconn->tx_buf->outptr, writelen); - - if (ret < 0 && errno == EAGAIN) - return; - else if (ret <= 0) { - msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_WRITE); - return; - } - - purple_circ_buffer_mark_read(servconn->tx_buf, ret); -} - -ssize_t -msn_servconn_write(MsnServConn *servconn, const char *buf, size_t len) -{ - ssize_t ret = 0; - - g_return_val_if_fail(servconn != NULL, 0); - - if (!servconn->session->http_method) - { - if (servconn->tx_handler == 0) { - switch (servconn->type) - { - case MSN_SERVCONN_NS: - case MSN_SERVCONN_SB: - ret = write(servconn->fd, buf, len); - break; -#if 0 - case MSN_SERVCONN_DC: - ret = write(servconn->fd, &buf, sizeof(len)); - ret = write(servconn->fd, buf, len); - break; -#endif - default: - ret = write(servconn->fd, buf, len); - break; - } - } else { - ret = -1; - errno = EAGAIN; - } - - if (ret < 0 && errno == EAGAIN) - ret = 0; - if (ret >= 0 && ret < len) { - if (servconn->tx_handler == 0) - servconn->tx_handler = purple_input_add( - servconn->fd, PURPLE_INPUT_WRITE, - servconn_write_cb, servconn); - purple_circ_buffer_append(servconn->tx_buf, buf + ret, - len - ret); - } - } - else - { - ret = msn_httpconn_write(servconn->httpconn, buf, len); - } - - if (ret == -1) - { - msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_WRITE); - } - - return ret; -} - -static void -read_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - MsnServConn *servconn; - MsnSession *session; - char buf[MSN_BUF_LEN]; - char *cur, *end, *old_rx_buf; - int len, cur_len; - - servconn = data; - session = servconn->session; - - len = read(servconn->fd, buf, sizeof(buf) - 1); - servconn->session->account->gc->last_received = time(NULL); - - if (len < 0 && errno == EAGAIN) - return; - else if (len <= 0) - { - purple_debug_error("msn", "servconn read error, len: %d error: %s\n", len, g_strerror(errno)); - msn_servconn_got_error(servconn, MSN_SERVCONN_ERROR_READ); - - return; - } - - buf[len] = '\0'; - - servconn->rx_buf = g_realloc(servconn->rx_buf, len + servconn->rx_len + 1); - memcpy(servconn->rx_buf + servconn->rx_len, buf, len + 1); - servconn->rx_len += len; - - end = old_rx_buf = servconn->rx_buf; - - servconn->processing = TRUE; - - do - { - cur = end; - - if (servconn->payload_len) - { - if (servconn->payload_len > servconn->rx_len) - /* The payload is still not complete. */ - break; - - cur_len = servconn->payload_len; - end += cur_len; - } - else - { - end = strstr(cur, "\r\n"); - - if (end == NULL) - /* The command is still not complete. */ - break; - - *end = '\0'; - end += 2; - cur_len = end - cur; - } - - servconn->rx_len -= cur_len; - - if (servconn->payload_len) - { - msn_cmdproc_process_payload(servconn->cmdproc, cur, cur_len); - servconn->payload_len = 0; - } - else - { - msn_cmdproc_process_cmd_text(servconn->cmdproc, cur); - } - } while (servconn->connected && !servconn->wasted && servconn->rx_len > 0); - - if (servconn->connected && !servconn->wasted) - { - if (servconn->rx_len > 0) - servconn->rx_buf = g_memdup(cur, servconn->rx_len); - else - servconn->rx_buf = NULL; - } - - servconn->processing = FALSE; - - if (servconn->wasted) - msn_servconn_destroy(servconn); - - g_free(old_rx_buf); -} - -#if 0 -static int -create_listener(int port) -{ - int fd; - int flags; - const int on = 1; - -#if 0 - struct addrinfo hints; - struct addrinfo *c, *res; - char port_str[5]; - - snprintf(port_str, sizeof(port_str), "%d", port); - - memset(&hints, 0, sizeof(hints)); - - hints.ai_flags = AI_PASSIVE; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - - if (getaddrinfo(NULL, port_str, &hints, &res) != 0) - { - purple_debug_error("msn", "Could not get address info: %s.\n", - port_str); - return -1; - } - - for (c = res; c != NULL; c = c->ai_next) - { - fd = socket(c->ai_family, c->ai_socktype, c->ai_protocol); - - if (fd < 0) - continue; - - setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - - if (bind(fd, c->ai_addr, c->ai_addrlen) == 0) - break; - - close(fd); - } - - if (c == NULL) - { - purple_debug_error("msn", "Could not find socket: %s.\n", port_str); - return -1; - } - - freeaddrinfo(res); -#else - struct sockaddr_in sockin; - - fd = socket(AF_INET, SOCK_STREAM, 0); - - if (fd < 0) - return -1; - - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) != 0) - { - close(fd); - return -1; - } - - memset(&sockin, 0, sizeof(struct sockaddr_in)); - sockin.sin_family = AF_INET; - sockin.sin_port = htons(port); - - if (bind(fd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) - { - close(fd); - return -1; - } -#endif - - if (listen (fd, 4) != 0) - { - close (fd); - return -1; - } - - flags = fcntl(fd, F_GETFL); - fcntl(fd, F_SETFL, flags | O_NONBLOCK); -#ifndef _WIN32 - fcntl(fd, F_SETFD, FD_CLOEXEC); -#endif - - return fd; -} -#endif
--- a/libpurple/protocols/msnp9/servconn.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,171 +0,0 @@ -/** - * @file servconn.h Server connection functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_SERVCONN_H_ -#define _MSN_SERVCONN_H_ - -typedef struct _MsnServConn MsnServConn; - -#include "session.h" -#include "cmdproc.h" - -#include "proxy.h" -#include "httpconn.h" - -/** - * Connection error types. - */ -typedef enum -{ - MSN_SERVCONN_ERROR_NONE, - MSN_SERVCONN_ERROR_CONNECT, - MSN_SERVCONN_ERROR_WRITE, - MSN_SERVCONN_ERROR_READ, - -} MsnServConnError; - -/** - * Connection types. - */ -typedef enum -{ - MSN_SERVCONN_NS, - MSN_SERVCONN_SB - -} MsnServConnType; - -/** - * A Connection. - */ -struct _MsnServConn -{ - MsnServConnType type; /**< The type of this connection. */ - MsnSession *session; /**< The MSN session of this connection. */ - MsnCmdProc *cmdproc; /**< The command processor of this connection. */ - - PurpleProxyConnectData *connect_data; - - gboolean connected; /**< A flag that states if it's connected. */ - gboolean processing; /**< A flag that states if something is working - with this connection. */ - gboolean wasted; /**< A flag that states if it should be destroyed. */ - - char *host; /**< The host this connection is connected or should be - connected to. */ - int num; /**< A number id of this connection. */ - - MsnHttpConn *httpconn; /**< The HTTP connection this connection should use. */ - - int fd; /**< The connection's file descriptor. */ - int inpa; /**< The connection's input handler. */ - - char *rx_buf; /**< The receive buffer. */ - int rx_len; /**< The receive buffer lenght. */ - - size_t payload_len; /**< The length of the payload. - It's only set when we've received a command that - has a payload. */ - - PurpleCircBuffer *tx_buf; - guint tx_handler; - - void (*connect_cb)(MsnServConn *); /**< The callback to call when connecting. */ - void (*disconnect_cb)(MsnServConn *); /**< The callback to call when disconnecting. */ - void (*destroy_cb)(MsnServConn *); /**< The callback to call when destroying. */ -}; - -/** - * Creates a new connection object. - * - * @param session The session. - * @param type The type of the connection. - */ -MsnServConn *msn_servconn_new(MsnSession *session, MsnServConnType type); - -/** - * Destroys a connection object. - * - * @param servconn The connection. - */ -void msn_servconn_destroy(MsnServConn *servconn); - -/** - * Connects to a host. - * - * @param servconn The connection. - * @param host The host. - * @param port The port. - */ -gboolean msn_servconn_connect(MsnServConn *servconn, const char *host, int port); - -/** - * Disconnects. - * - * @param servconn The connection. - */ -void msn_servconn_disconnect(MsnServConn *servconn); - -/** - * Sets the connect callback. - * - * @param servconn The servconn. - * @param connect_cb The connect callback. - */ -void msn_servconn_set_connect_cb(MsnServConn *servconn, - void (*connect_cb)(MsnServConn *)); -/** - * Sets the disconnect callback. - * - * @param servconn The servconn. - * @param disconnect_cb The disconnect callback. - */ -void msn_servconn_set_disconnect_cb(MsnServConn *servconn, - void (*disconnect_cb)(MsnServConn *)); -/** - * Sets the destroy callback. - * - * @param servconn The servconn that's being destroyed. - * @param destroy_cb The destroy callback. - */ -void msn_servconn_set_destroy_cb(MsnServConn *servconn, - void (*destroy_cb)(MsnServConn *)); - -/** - * Writes a chunck of data to the servconn. - * - * @param servconn The servconn. - * @param buf The data to write. - * @param size The size of the data. - */ -ssize_t msn_servconn_write(MsnServConn *servconn, const char *buf, - size_t size); - -/** - * Function to call whenever an error related to a switchboard occurs. - * - * @param servconn The servconn. - * @param error The error that happened. - */ -void msn_servconn_got_error(MsnServConn *servconn, MsnServConnError error); - -#endif /* _MSN_SERVCONN_H_ */
--- a/libpurple/protocols/msnp9/session.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,432 +0,0 @@ -/** - * @file session.c MSN session functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "session.h" -#include "notification.h" - -#include "dialog.h" - -MsnSession * -msn_session_new(PurpleAccount *account) -{ - MsnSession *session; - - g_return_val_if_fail(account != NULL, NULL); - - session = g_new0(MsnSession, 1); - - session->account = account; - session->notification = msn_notification_new(session); - session->userlist = msn_userlist_new(session); - - session->user = msn_user_new(session->userlist, - purple_account_get_username(account), NULL); - - session->protocol_ver = 9; - - return session; -} - -void -msn_session_destroy(MsnSession *session) -{ - g_return_if_fail(session != NULL); - - session->destroying = TRUE; - - if (session->connected) - msn_session_disconnect(session); - - if (session->notification != NULL) - msn_notification_destroy(session->notification); - - while (session->switches != NULL) - msn_switchboard_destroy(session->switches->data); - - while (session->slplinks != NULL) - msn_slplink_destroy(session->slplinks->data); - - msn_userlist_destroy(session->userlist); - - g_free(session->passport_info.kv); - g_free(session->passport_info.sid); - g_free(session->passport_info.mspauth); - g_free(session->passport_info.client_ip); - - if (session->passport_info.file != NULL) - { - g_unlink(session->passport_info.file); - g_free(session->passport_info.file); - } - - if (session->sync != NULL) - msn_sync_destroy(session->sync); - - if (session->nexus != NULL) - msn_nexus_destroy(session->nexus); - - if (session->user != NULL) - msn_user_destroy(session->user); - - g_free(session); -} - -gboolean -msn_session_connect(MsnSession *session, const char *host, int port, - gboolean http_method) -{ - g_return_val_if_fail(session != NULL, FALSE); - g_return_val_if_fail(!session->connected, TRUE); - - session->connected = TRUE; - session->http_method = http_method; - - if (session->notification == NULL) - { - purple_debug_error("msn", "This shouldn't happen\n"); - g_return_val_if_reached(FALSE); - } - - if (msn_notification_connect(session->notification, host, port)) - { - return TRUE; - } - - return FALSE; -} - -void -msn_session_disconnect(MsnSession *session) -{ - g_return_if_fail(session != NULL); - g_return_if_fail(session->connected); - - session->connected = FALSE; - - while (session->switches != NULL) - msn_switchboard_close(session->switches->data); - - if (session->notification != NULL) - msn_notification_close(session->notification); -} - -/* TODO: This must go away when conversation is redesigned */ -MsnSwitchBoard * -msn_session_find_swboard(MsnSession *session, const char *username) -{ - GList *l; - - g_return_val_if_fail(session != NULL, NULL); - g_return_val_if_fail(username != NULL, NULL); - - for (l = session->switches; l != NULL; l = l->next) - { - MsnSwitchBoard *swboard; - - swboard = l->data; - - if ((swboard->im_user != NULL) && !strcmp(username, swboard->im_user)) - return swboard; - } - - return NULL; -} - -MsnSwitchBoard * -msn_session_find_swboard_with_conv(MsnSession *session, PurpleConversation *conv) -{ - GList *l; - - g_return_val_if_fail(session != NULL, NULL); - g_return_val_if_fail(conv != NULL, NULL); - - for (l = session->switches; l != NULL; l = l->next) - { - MsnSwitchBoard *swboard; - - swboard = l->data; - - if (swboard->conv == conv) - return swboard; - } - - return NULL; -} - -MsnSwitchBoard * -msn_session_find_swboard_with_id(const MsnSession *session, int chat_id) -{ - GList *l; - - g_return_val_if_fail(session != NULL, NULL); - g_return_val_if_fail(chat_id >= 0, NULL); - - for (l = session->switches; l != NULL; l = l->next) - { - MsnSwitchBoard *swboard; - - swboard = l->data; - - if (swboard->chat_id == chat_id) - return swboard; - } - - return NULL; -} - -MsnSwitchBoard * -msn_session_get_swboard(MsnSession *session, const char *username, - MsnSBFlag flag) -{ - MsnSwitchBoard *swboard; - - g_return_val_if_fail(session != NULL, NULL); - - swboard = msn_session_find_swboard(session, username); - - if (swboard == NULL) - { - swboard = msn_switchboard_new(session); - swboard->im_user = g_strdup(username); - msn_switchboard_request(swboard); - msn_switchboard_request_add_user(swboard, username); - } - - swboard->flag |= flag; - - return swboard; -} - -static void -msn_session_sync_users(MsnSession *session) -{ - PurpleConnection *gc = purple_account_get_connection(session->account); - GList *to_remove = NULL; - GSList *buddies; - - g_return_if_fail(gc != NULL); - - /* The core used to use msn_add_buddy to add all buddies before - * being logged in. This no longer happens, so we manually iterate - * over the whole buddy list to identify sync issues. */ - for (buddies = purple_find_buddies(session->account, NULL); buddies; - buddies = g_slist_delete_link(buddies, buddies)) { - PurpleBuddy *buddy = buddies->data; - const char *buddy_name = purple_buddy_get_name(buddy); - const char *group_name = purple_group_get_name(purple_buddy_get_group(buddy)); - MsnUser *remote_user; - gboolean found = FALSE; - - remote_user = msn_userlist_find_user(session->userlist, buddy_name); - - if (remote_user && remote_user->list_op & MSN_LIST_FL_OP) { - int group_id; - GList *l; - - group_id = msn_userlist_find_group_id(remote_user->userlist, - group_name); - - for (l = remote_user->group_ids; l; l = l->next) { - if (group_id == GPOINTER_TO_INT(l->data)) { - found = TRUE; - break; - } - } - - /* We don't care if they're in a different group, as long as they're on the - * list somewhere. If we check for the group, we cause pain, agony and - * suffering for people who decide to re-arrange their buddy list elsewhere. - */ - if (!found) - { - if ((remote_user == NULL) || !(remote_user->list_op & MSN_LIST_FL_OP)) { - /* The user is not on the server list */ - msn_show_sync_issue(session, buddy_name, group_name); - } else { - /* The user is not in that group on the server list */ - to_remove = g_list_prepend(to_remove, buddy); - } - } - } - } - - if (to_remove != NULL) { - g_list_foreach(to_remove, (GFunc)purple_blist_remove_buddy, NULL); - g_list_free(to_remove); - } -} - -void -msn_session_set_error(MsnSession *session, MsnErrorType error, - const char *info) -{ - PurpleConnection *gc; - PurpleConnectionError reason; - char *msg; - - gc = purple_account_get_connection(session->account); - - switch (error) - { - case MSN_ERROR_SERVCONN: - reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - msg = g_strdup(info); - break; - case MSN_ERROR_UNSUPPORTED_PROTOCOL: - reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - msg = g_strdup(_("Our protocol is not supported by the " - "server")); - break; - case MSN_ERROR_HTTP_MALFORMED: - reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - msg = g_strdup(_("Error parsing HTTP")); - break; - case MSN_ERROR_SIGN_OTHER: - reason = PURPLE_CONNECTION_ERROR_NAME_IN_USE; - msg = g_strdup(_("You have signed on from another location")); - if (!purple_account_get_remember_password(session->account)) - purple_account_set_password(session->account, NULL); - break; - case MSN_ERROR_SERV_UNAVAILABLE: - reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - msg = g_strdup(_("The MSN servers are temporarily " - "unavailable. Please wait and try " - "again.")); - break; - case MSN_ERROR_SERV_DOWN: - reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - msg = g_strdup(_("The MSN servers are going down " - "temporarily")); - break; - case MSN_ERROR_AUTH: - reason = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; - msg = g_strdup_printf(_("Unable to authenticate: %s"), - (info == NULL ) ? - _("Unknown error") : info); - break; - case MSN_ERROR_BAD_BLIST: - reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - msg = g_strdup(_("Your MSN buddy list is temporarily " - "unavailable. Please wait and try " - "again.")); - break; - default: - reason = PURPLE_CONNECTION_ERROR_NETWORK_ERROR; - msg = g_strdup(_("Unknown error")); - break; - } - - msn_session_disconnect(session); - - purple_connection_error_reason(gc, reason, msg); - - g_free(msg); -} - -static const char * -get_login_step_text(MsnSession *session) -{ - const char *steps_text[] = { - _("Connecting"), - _("Handshaking"), - _("Transferring"), - _("Handshaking"), - _("Starting authentication"), - _("Getting cookie"), - _("Authenticating"), - _("Sending cookie"), - _("Retrieving buddy list") - }; - - return steps_text[session->login_step]; -} - -void -msn_session_set_login_step(MsnSession *session, MsnLoginStep step) -{ - PurpleConnection *gc; - - /* Prevent the connection progress going backwards, eg. if we get - * transferred several times during login */ - if (session->login_step > step) - return; - - /* If we're already logged in, we're probably here because of a - * mid-session XFR from the notification server, so we don't want to - * popup the connection progress dialog */ - if (session->logged_in) - return; - - gc = session->account->gc; - - session->login_step = step; - - purple_connection_update_progress(gc, get_login_step_text(session), step, - MSN_LOGIN_STEPS); -} - -void -msn_session_finish_login(MsnSession *session) -{ - PurpleAccount *account; - PurpleConnection *gc; - PurpleStoredImage *img; - const char *passport; - - if (session->logged_in) { - /* We are probably here because of a mid-session notification server XFR - * We must send a CHG now, otherwise the servers default to invisible, - * and prevent things happening, like sending IMs */ - msn_change_status(session); - return; - } - - account = session->account; - gc = purple_account_get_connection(account); - - img = purple_buddy_icons_find_account_icon(session->account); - msn_user_set_buddy_icon(session->user, img); - purple_imgstore_unref(img); - - session->logged_in = TRUE; - - msn_change_status(session); - - purple_connection_set_state(gc, PURPLE_CONNECTED); - - /* Sync users */ - msn_session_sync_users(session); - /* It seems that some accounts that haven't accessed hotmail for a while - * and @msn.com accounts don't automatically get the initial email - * notification so we always request it on login - */ - - passport = purple_normalize(account, purple_account_get_username(account)); - - if ((strstr(passport, "@hotmail.") != NULL) || - (strstr(passport, "@msn.com") != NULL)) - { - msn_cmdproc_send(session->notification->cmdproc, "URL", "%s", "INBOX"); - } -}
--- a/libpurple/protocols/msnp9/session.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,225 +0,0 @@ -/** - * @file session.h MSN session functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_SESSION_H_ -#define _MSN_SESSION_H_ - -typedef struct _MsnSession MsnSession; - -#include "sslconn.h" - -#include "user.h" -#include "slpcall.h" - -#include "notification.h" -#include "switchboard.h" -#include "group.h" - -#include "cmdproc.h" -#include "nexus.h" -#include "httpconn.h" - -#include "userlist.h" -#include "sync.h" - -/** - * Types of errors. - */ -typedef enum -{ - MSN_ERROR_SERVCONN, - MSN_ERROR_UNSUPPORTED_PROTOCOL, - MSN_ERROR_HTTP_MALFORMED, - MSN_ERROR_AUTH, - MSN_ERROR_BAD_BLIST, - MSN_ERROR_SIGN_OTHER, - MSN_ERROR_SERV_DOWN, - MSN_ERROR_SERV_UNAVAILABLE - -} MsnErrorType; - -/** - * Login steps. - */ -typedef enum -{ - MSN_LOGIN_STEP_START, - MSN_LOGIN_STEP_HANDSHAKE, - MSN_LOGIN_STEP_TRANSFER, - MSN_LOGIN_STEP_HANDSHAKE2, - MSN_LOGIN_STEP_AUTH_START, - MSN_LOGIN_STEP_AUTH, - MSN_LOGIN_STEP_GET_COOKIE, - MSN_LOGIN_STEP_AUTH_END, - MSN_LOGIN_STEP_SYN, - MSN_LOGIN_STEP_END - -} MsnLoginStep; - -#define MSN_LOGIN_STEPS MSN_LOGIN_STEP_END - -struct _MsnSession -{ - PurpleAccount *account; - MsnUser *user; - - guint protocol_ver; - - MsnLoginStep login_step; /**< The current step in the login process. */ - - gboolean connected; - gboolean logged_in; /**< A temporal flag to ignore local buddy list adds. */ - gboolean destroying; /**< A flag that states if the session is being destroyed. */ - gboolean http_method; - - MsnNotification *notification; - MsnNexus *nexus; - MsnSync *sync; - - MsnUserList *userlist; - - int servconns_count; /**< The count of server connections. */ - GList *switches; /**< The list of all the switchboards. */ - GList *directconns; /**< The list of all the directconnections. */ - GList *slplinks; /**< The list of all the slplinks. */ - - struct - { - char *kv; - char *sid; - char *mspauth; - unsigned long sl; - char *file; - char *client_ip; - int client_port; - int verified; - } passport_info; -}; - -/** - * Creates an MSN session. - * - * @param account The account. - * - * @return The new MSN session. - */ -MsnSession *msn_session_new(PurpleAccount *account); - -/** - * Destroys an MSN session. - * - * @param session The MSN session to destroy. - */ -void msn_session_destroy(MsnSession *session); - -/** - * Connects to and initiates an MSN session. - * - * @param session The MSN session. - * @param host The dispatch server host. - * @param port The dispatch server port. - * @param http_method Whether to use or not http_method. - * - * @return @c TRUE on success, @c FALSE on failure. - */ -gboolean msn_session_connect(MsnSession *session, - const char *host, int port, - gboolean http_method); - -/** - * Disconnects from an MSN session. - * - * @param session The MSN session. - */ -void msn_session_disconnect(MsnSession *session); - - /** - * Finds a switchboard with the given username. - * - * @param session The MSN session. - * @param username The username to search for. - * - * @return The switchboard, if found. - */ -MsnSwitchBoard *msn_session_find_swboard(MsnSession *session, - const char *username); - - /** - * Finds a switchboard with the given conversation. - * - * @param session The MSN session. - * @param conv The conversation to search for. - * - * @return The switchboard, if found. - */ -MsnSwitchBoard *msn_session_find_swboard_with_conv(MsnSession *session, - PurpleConversation *conv); -/** - * Finds a switchboard with the given chat ID. - * - * @param session The MSN session. - * @param chat_id The chat ID to search for. - * - * @return The switchboard, if found. - */ -MsnSwitchBoard *msn_session_find_swboard_with_id(const MsnSession *session, - int chat_id); - -/** - * Returns a switchboard to communicate with certain username. - * - * @param session The MSN session. - * @param username The username to search for. - * @param flag The flag of the switchboard - * - * @return The switchboard. - */ -MsnSwitchBoard *msn_session_get_swboard(MsnSession *session, - const char *username, MsnSBFlag flag); - -/** - * Sets an error for the MSN session. - * - * @param session The MSN session. - * @param error The error. - * @param info Extra information. - */ -void msn_session_set_error(MsnSession *session, MsnErrorType error, - const char *info); - -/** - * Sets the current step in the login proccess. - * - * @param session The MSN session. - * @param step The current step. - */ -void msn_session_set_login_step(MsnSession *session, MsnLoginStep step); - -/** - * Finish the login proccess. - * - * @param session The MSN session. - */ -void msn_session_finish_login(MsnSession *session); - -#endif /* _MSN_SESSION_H_ */
--- a/libpurple/protocols/msnp9/slp.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1122 +0,0 @@ -/** - * @file msnslp.c MSNSLP support - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "slp.h" -#include "slpcall.h" -#include "slpmsg.h" -#include "slpsession.h" - -#include "object.h" -#include "user.h" -#include "switchboard.h" - -#include "smiley.h" - -/* Seconds to delay between sending buddy icon requests to the server. */ -#define BUDDY_ICON_DELAY 20 - -static void send_ok(MsnSlpCall *slpcall, const char *branch, - const char *type, const char *content); - -static void send_decline(MsnSlpCall *slpcall, const char *branch, - const char *type, const char *content); - -void msn_request_user_display(MsnUser *user); - -/************************************************************************** - * Util - **************************************************************************/ - -static char * -get_token(const char *str, const char *start, const char *end) -{ - const char *c, *c2; - - if ((c = strstr(str, start)) == NULL) - return NULL; - - c += strlen(start); - - if (end != NULL) - { - if ((c2 = strstr(c, end)) == NULL) - return NULL; - - return g_strndup(c, c2 - c); - } - else - { - /* This has to be changed */ - return g_strdup(c); - } - -} - -/************************************************************************** - * Xfer - **************************************************************************/ - -static void -msn_xfer_init(PurpleXfer *xfer) -{ - MsnSlpCall *slpcall; - /* MsnSlpLink *slplink; */ - char *content; - - purple_debug_info("msn", "xfer_init\n"); - - slpcall = xfer->data; - - /* Send Ok */ - content = g_strdup_printf("SessionID: %lu\r\n\r\n", - slpcall->session_id); - - send_ok(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody", - content); - - g_free(content); - msn_slplink_unleash(slpcall->slplink); -} - -void -msn_xfer_cancel(PurpleXfer *xfer) -{ - MsnSlpCall *slpcall; - char *content; - - g_return_if_fail(xfer != NULL); - g_return_if_fail(xfer->data != NULL); - - slpcall = xfer->data; - - if (purple_xfer_get_status(xfer) == PURPLE_XFER_STATUS_CANCEL_LOCAL) - { - if (slpcall->started) - { - msn_slp_call_close(slpcall); - } - else - { - content = g_strdup_printf("SessionID: %lu\r\n\r\n", - slpcall->session_id); - - send_decline(slpcall, slpcall->branch, "application/x-msnmsgr-sessionreqbody", - content); - - g_free(content); - msn_slplink_unleash(slpcall->slplink); - - msn_slp_call_destroy(slpcall); - } - } -} - -void -msn_xfer_progress_cb(MsnSlpCall *slpcall, gsize total_length, gsize len, gsize offset) -{ - PurpleXfer *xfer; - - xfer = slpcall->xfer; - - xfer->bytes_sent = (offset + len); - xfer->bytes_remaining = total_length - (offset + len); - - purple_xfer_update_progress(xfer); -} - -void -msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session) -{ - if ((purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_DONE) && - (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_REMOTE) && - (purple_xfer_get_status(slpcall->xfer) != PURPLE_XFER_STATUS_CANCEL_LOCAL)) - { - purple_xfer_cancel_remote(slpcall->xfer); - } -} - -void -msn_xfer_completed_cb(MsnSlpCall *slpcall, const guchar *body, - gsize size) -{ - PurpleXfer *xfer = slpcall->xfer; - purple_xfer_set_completed(xfer, TRUE); - purple_xfer_end(xfer); -} - -/************************************************************************** - * SLP Control - **************************************************************************/ - -#if 0 -static void -got_transresp(MsnSlpCall *slpcall, const char *nonce, - const char *ips_str, int port) -{ - MsnDirectConn *directconn; - char **ip_addrs, **c; - - directconn = msn_directconn_new(slpcall->slplink); - - directconn->initial_call = slpcall; - - /* msn_directconn_parse_nonce(directconn, nonce); */ - directconn->nonce = g_strdup(nonce); - - ip_addrs = g_strsplit(ips_str, " ", -1); - - for (c = ip_addrs; *c != NULL; c++) - { - purple_debug_info("msn", "ip_addr = %s\n", *c); - if (msn_directconn_connect(directconn, *c, port)) - break; - } - - g_strfreev(ip_addrs); -} -#endif - -static void -send_ok(MsnSlpCall *slpcall, const char *branch, - const char *type, const char *content) -{ - MsnSlpLink *slplink; - MsnSlpMessage *slpmsg; - - slplink = slpcall->slplink; - - /* 200 OK */ - slpmsg = msn_slpmsg_sip_new(slpcall, 1, - "MSNSLP/1.0 200 OK", - branch, type, content); - -#ifdef MSN_DEBUG_SLP - slpmsg->info = "SLP 200 OK"; - slpmsg->text_body = TRUE; -#endif - - msn_slplink_queue_slpmsg(slplink, slpmsg); - - msn_slp_call_session_init(slpcall); -} - -static void -send_decline(MsnSlpCall *slpcall, const char *branch, - const char *type, const char *content) -{ - MsnSlpLink *slplink; - MsnSlpMessage *slpmsg; - - slplink = slpcall->slplink; - - /* 603 Decline */ - slpmsg = msn_slpmsg_sip_new(slpcall, 1, - "MSNSLP/1.0 603 Decline", - branch, type, content); - -#ifdef MSN_DEBUG_SLP - slpmsg->info = "SLP 603 Decline"; - slpmsg->text_body = TRUE; -#endif - - msn_slplink_queue_slpmsg(slplink, slpmsg); -} - -#define MAX_FILE_NAME_LEN 0x226 - -static void -got_sessionreq(MsnSlpCall *slpcall, const char *branch, - const char *euf_guid, const char *context) -{ - if (!strcmp(euf_guid, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6")) - { - /* Emoticon or UserDisplay */ - char *content; - gsize len; - MsnSlpSession *slpsession; - MsnSlpLink *slplink; - MsnSlpMessage *slpmsg; - MsnObject *obj; - char *msnobj_data; - PurpleStoredImage *img; - int type; - - /* Send Ok */ - content = g_strdup_printf("SessionID: %lu\r\n\r\n", - slpcall->session_id); - - send_ok(slpcall, branch, "application/x-msnmsgr-sessionreqbody", - content); - - g_free(content); - - slplink = slpcall->slplink; - - msnobj_data = (char *)purple_base64_decode(context, &len); - obj = msn_object_new_from_string(msnobj_data); - type = msn_object_get_type(obj); - g_free(msnobj_data); - - if ((type != MSN_OBJECT_USERTILE) && (type != MSN_OBJECT_EMOTICON)) - { - purple_debug_error("msn", "Wrong object?\n"); - msn_object_destroy(obj); - g_return_if_reached(); - } - - if (type == MSN_OBJECT_EMOTICON) { - char *path; - path = g_build_filename(purple_smileys_get_storing_dir(), - obj->location, NULL); - img = purple_imgstore_new_from_file(path); - g_free(path); - } else { - img = msn_object_get_image(obj); - if (img) - purple_imgstore_ref(img); - } - msn_object_destroy(obj); - - if (img == NULL) - { - purple_debug_error("msn", "Wrong object.\n"); - g_return_if_reached(); - } - - slpsession = msn_slplink_find_slp_session(slplink, - slpcall->session_id); - - /* DATA PREP */ - slpmsg = msn_slpmsg_new(slplink); - slpmsg->slpcall = slpcall; - slpmsg->slpsession = slpsession; - slpmsg->session_id = slpsession->id; - msn_slpmsg_set_body(slpmsg, NULL, 4); -#ifdef MSN_DEBUG_SLP - slpmsg->info = "SLP DATA PREP"; -#endif - msn_slplink_queue_slpmsg(slplink, slpmsg); - - /* DATA */ - slpmsg = msn_slpmsg_new(slplink); - slpmsg->slpcall = slpcall; - slpmsg->slpsession = slpsession; - slpmsg->flags = 0x20; -#ifdef MSN_DEBUG_SLP - slpmsg->info = "SLP DATA"; -#endif - msn_slpmsg_set_image(slpmsg, img); - msn_slplink_queue_slpmsg(slplink, slpmsg); - purple_imgstore_unref(img); - } - else if (!strcmp(euf_guid, "5D3E02AB-6190-11D3-BBBB-00C04F795683")) - { - /* File Transfer */ - PurpleAccount *account; - PurpleXfer *xfer; - char *bin; - gsize bin_len; - guint32 file_size; - gchar *file_name; - gunichar2 *uni_name; - - account = slpcall->slplink->session->account; - - slpcall->cb = msn_xfer_completed_cb; - slpcall->end_cb = msn_xfer_end_cb; - slpcall->progress_cb = msn_xfer_progress_cb; - slpcall->branch = g_strdup(branch); - - slpcall->pending = TRUE; - - xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, - slpcall->slplink->remote_user); - if (xfer) - { - bin = (char *)purple_base64_decode(context, &bin_len); - file_size = GUINT32_FROM_LE(*(gsize *)(bin + 8)); - - uni_name = (gunichar2 *)(bin + 20); - while(*uni_name != 0 && ((char *)uni_name - (bin + 20)) < MAX_FILE_NAME_LEN) { - *uni_name = GUINT16_FROM_LE(*uni_name); - uni_name++; - } - - file_name = g_utf16_to_utf8((const gunichar2 *)(bin + 20), -1, - NULL, NULL, NULL); - - g_free(bin); - - purple_xfer_set_filename(xfer, file_name ? file_name : ""); - g_free(file_name); - purple_xfer_set_size(xfer, file_size); - purple_xfer_set_init_fnc(xfer, msn_xfer_init); - purple_xfer_set_request_denied_fnc(xfer, msn_xfer_cancel); - purple_xfer_set_cancel_recv_fnc(xfer, msn_xfer_cancel); - - slpcall->xfer = xfer; - purple_xfer_ref(slpcall->xfer); - - xfer->data = slpcall; - - purple_xfer_request(xfer); - } - } -} - -void -send_bye(MsnSlpCall *slpcall, const char *type) -{ - MsnSlpLink *slplink; - MsnSlpMessage *slpmsg; - char *header; - - slplink = slpcall->slplink; - - g_return_if_fail(slplink != NULL); - - header = g_strdup_printf("BYE MSNMSGR:%s MSNSLP/1.0", - slplink->local_user); - - slpmsg = msn_slpmsg_sip_new(slpcall, 0, header, - "A0D624A6-6C0C-4283-A9E0-BC97B4B46D32", - type, - "\r\n"); - g_free(header); - -#ifdef MSN_DEBUG_SLP - slpmsg->info = "SLP BYE"; - slpmsg->text_body = TRUE; -#endif - - msn_slplink_queue_slpmsg(slplink, slpmsg); -} - -static void -got_invite(MsnSlpCall *slpcall, - const char *branch, const char *type, const char *content) -{ - MsnSlpLink *slplink; - - slplink = slpcall->slplink; - - if (!strcmp(type, "application/x-msnmsgr-sessionreqbody")) - { - char *euf_guid, *context; - char *temp; - - euf_guid = get_token(content, "EUF-GUID: {", "}\r\n"); - - temp = get_token(content, "SessionID: ", "\r\n"); - if (temp != NULL) - slpcall->session_id = atoi(temp); - g_free(temp); - - temp = get_token(content, "AppID: ", "\r\n"); - if (temp != NULL) - slpcall->app_id = atoi(temp); - g_free(temp); - - context = get_token(content, "Context: ", "\r\n"); - - if (context != NULL) - got_sessionreq(slpcall, branch, euf_guid, context); - - g_free(context); - g_free(euf_guid); - } - else if (!strcmp(type, "application/x-msnmsgr-transreqbody")) - { - /* A direct connection? */ - - char *listening, *nonce; - char *content; - - if (FALSE) - { -#if 0 - MsnDirectConn *directconn; - /* const char *ip_addr; */ - char *ip_port; - int port; - - /* ip_addr = purple_prefs_get_string("/purple/ft/public_ip"); */ - ip_port = "5190"; - listening = "true"; - nonce = rand_guid(); - - directconn = msn_directconn_new(slplink); - - /* msn_directconn_parse_nonce(directconn, nonce); */ - directconn->nonce = g_strdup(nonce); - - msn_directconn_listen(directconn); - - port = directconn->port; - - content = g_strdup_printf( - "Bridge: TCPv1\r\n" - "Listening: %s\r\n" - "Nonce: {%s}\r\n" - "Ipv4Internal-Addrs: 192.168.0.82\r\n" - "Ipv4Internal-Port: %d\r\n" - "\r\n", - listening, - nonce, - port); -#endif - } - else - { - listening = "false"; - nonce = g_strdup("00000000-0000-0000-0000-000000000000"); - - content = g_strdup_printf( - "Bridge: TCPv1\r\n" - "Listening: %s\r\n" - "Nonce: {%s}\r\n" - "\r\n", - listening, - nonce); - } - - send_ok(slpcall, branch, - "application/x-msnmsgr-transrespbody", content); - - g_free(content); - g_free(nonce); - } - else if (!strcmp(type, "application/x-msnmsgr-transrespbody")) - { -#if 0 - char *ip_addrs; - char *temp; - char *nonce; - int port; - - nonce = get_token(content, "Nonce: {", "}\r\n"); - ip_addrs = get_token(content, "IPv4Internal-Addrs: ", "\r\n"); - - temp = get_token(content, "IPv4Internal-Port: ", "\r\n"); - if (temp != NULL) - port = atoi(temp); - else - port = -1; - g_free(temp); - - if (ip_addrs == NULL) - return; - - if (port > 0) - got_transresp(slpcall, nonce, ip_addrs, port); - - g_free(nonce); - g_free(ip_addrs); -#endif - } -} - -static void -got_ok(MsnSlpCall *slpcall, - const char *type, const char *content) -{ - g_return_if_fail(slpcall != NULL); - g_return_if_fail(type != NULL); - - if (!strcmp(type, "application/x-msnmsgr-sessionreqbody")) - { -#if 0 - if (slpcall->type == MSN_SLPCALL_DC) - { - /* First let's try a DirectConnection. */ - - MsnSlpLink *slplink; - MsnSlpMessage *slpmsg; - char *header; - char *content; - char *branch; - - slplink = slpcall->slplink; - - branch = rand_guid(); - - content = g_strdup_printf( - "Bridges: TRUDPv1 TCPv1\r\n" - "NetID: 0\r\n" - "Conn-Type: Direct-Connect\r\n" - "UPnPNat: false\r\n" - "ICF: false\r\n" - ); - - header = g_strdup_printf("INVITE MSNMSGR:%s MSNSLP/1.0", - slplink->remote_user); - - slpmsg = msn_slp_sipmsg_new(slpcall, 0, header, branch, - "application/x-msnmsgr-transreqbody", - content); - -#ifdef MSN_DEBUG_SLP - slpmsg->info = "SLP INVITE"; - slpmsg->text_body = TRUE; -#endif - msn_slplink_send_slpmsg(slplink, slpmsg); - - g_free(header); - g_free(content); - - g_free(branch); - } - else - { - msn_slp_call_session_init(slpcall); - } -#else - msn_slp_call_session_init(slpcall); -#endif - } - else if (!strcmp(type, "application/x-msnmsgr-transreqbody")) - { - /* Do we get this? */ - purple_debug_info("msn", "OK with transreqbody\n"); - } - else if (!strcmp(type, "application/x-msnmsgr-transrespbody")) - { -#if 0 - char *ip_addrs; - char *temp; - char *nonce; - int port; - - nonce = get_token(content, "Nonce: {", "}\r\n"); - ip_addrs = get_token(content, "IPv4Internal-Addrs: ", "\r\n"); - - temp = get_token(content, "IPv4Internal-Port: ", "\r\n"); - if (temp != NULL) - port = atoi(temp); - else - port = -1; - g_free(temp); - - if (ip_addrs == NULL) - return; - - if (port > 0) - got_transresp(slpcall, nonce, ip_addrs, port); - - g_free(nonce); - g_free(ip_addrs); -#endif - } -} - -MsnSlpCall * -msn_slp_sip_recv(MsnSlpLink *slplink, const char *body) -{ - MsnSlpCall *slpcall; - - if (body == NULL) - { - purple_debug_warning("msn", "received bogus message\n"); - return NULL; - } - - if (!strncmp(body, "INVITE", strlen("INVITE"))) - { - char *branch; - char *content; - char *content_type; - - slpcall = msn_slp_call_new(slplink); - - /* From: <msnmsgr:buddy@hotmail.com> */ -#if 0 - slpcall->remote_user = get_token(body, "From: <msnmsgr:", ">\r\n"); -#endif - - branch = get_token(body, ";branch={", "}"); - - slpcall->id = get_token(body, "Call-ID: {", "}"); - -#if 0 - long content_len = -1; - - temp = get_token(body, "Content-Length: ", "\r\n"); - if (temp != NULL) - content_len = atoi(temp); - g_free(temp); -#endif - content_type = get_token(body, "Content-Type: ", "\r\n"); - - content = get_token(body, "\r\n\r\n", NULL); - - got_invite(slpcall, branch, content_type, content); - - g_free(branch); - g_free(content_type); - g_free(content); - } - else if (!strncmp(body, "MSNSLP/1.0 ", strlen("MSNSLP/1.0 "))) - { - char *content; - char *content_type; - /* Make sure this is "OK" */ - const char *status = body + strlen("MSNSLP/1.0 "); - char *call_id; - - call_id = get_token(body, "Call-ID: {", "}"); - slpcall = msn_slplink_find_slp_call(slplink, call_id); - g_free(call_id); - - g_return_val_if_fail(slpcall != NULL, NULL); - - if (strncmp(status, "200 OK", 6)) - { - /* It's not valid. Kill this off. */ - char temp[32]; - const char *c; - - /* Eww */ - if ((c = strchr(status, '\r')) || (c = strchr(status, '\n')) || - (c = strchr(status, '\0'))) - { - size_t offset = c - status; - if (offset >= sizeof(temp)) - offset = sizeof(temp) - 1; - - strncpy(temp, status, offset); - temp[offset] = '\0'; - } - - purple_debug_error("msn", "Received non-OK result: %s\n", temp); - - slpcall->wasted = TRUE; - - /* msn_slp_call_destroy(slpcall); */ - return slpcall; - } - - content_type = get_token(body, "Content-Type: ", "\r\n"); - - content = get_token(body, "\r\n\r\n", NULL); - - got_ok(slpcall, content_type, content); - - g_free(content_type); - g_free(content); - } - else if (!strncmp(body, "BYE", strlen("BYE"))) - { - char *call_id; - - call_id = get_token(body, "Call-ID: {", "}"); - slpcall = msn_slplink_find_slp_call(slplink, call_id); - g_free(call_id); - - if (slpcall != NULL) - slpcall->wasted = TRUE; - - /* msn_slp_call_destroy(slpcall); */ - } - else - slpcall = NULL; - - return slpcall; -} - -/************************************************************************** - * Msg Callbacks - **************************************************************************/ - -void -msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg) -{ - MsnSession *session; - MsnSlpLink *slplink; - - session = cmdproc->servconn->session; - slplink = msn_session_get_slplink(session, msg->remote_user); - - if (slplink->swboard == NULL) - { - /* We will need this in order to change its flags. */ - slplink->swboard = (MsnSwitchBoard *)cmdproc->data; - /* If swboard is NULL, something has probably gone wrong earlier on - * I didn't want to do this, but MSN 7 is somehow causing us to crash - * here, I couldn't reproduce it to debug more, and people are - * reporting bugs. Hopefully this doesn't cause more crashes. Stu. - */ - if (slplink->swboard != NULL) - slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink); - else - purple_debug_error("msn", "msn_p2p_msg, swboard is NULL, ouch!\n"); - } - - msn_slplink_process_msg(slplink, msg); -} - -static void -got_emoticon(MsnSlpCall *slpcall, - const guchar *data, gsize size) -{ - - PurpleConversation *conv; - PurpleConnection *gc; - const char *who; - - gc = slpcall->slplink->session->account->gc; - who = slpcall->slplink->remote_user; - - if ((conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, gc->account))) { - - /* FIXME: it would be better if we wrote the data as we received it - instead of all at once, calling write multiple times and - close once at the very end - */ - purple_conv_custom_smiley_write(conv, slpcall->data_info, data, size); - purple_conv_custom_smiley_close(conv, slpcall->data_info); - } -#ifdef MSN_DEBUG_UD - purple_debug_info("msn", "Got smiley: %s\n", slpcall->data_info); -#endif -} - -void -msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg) -{ - MsnSession *session; - MsnSlpLink *slplink; - MsnObject *obj; - char **tokens; - char *smile, *body_str; - const char *body, *who, *sha1; - guint tok; - size_t body_len; - - PurpleConversation *conv; - - session = cmdproc->servconn->session; - - if (!purple_account_get_bool(session->account, "custom_smileys", TRUE)) - return; - - body = msn_message_get_bin_data(msg, &body_len); - body_str = g_strndup(body, body_len); - - /* MSN Messenger 7 may send more than one MSNObject in a single message... - * Maybe 10 tokens is a reasonable max value. */ - tokens = g_strsplit(body_str, "\t", 10); - - g_free(body_str); - - for (tok = 0; tok < 9; tok += 2) { - if (tokens[tok] == NULL || tokens[tok + 1] == NULL) { - break; - } - - smile = tokens[tok]; - obj = msn_object_new_from_string(purple_url_decode(tokens[tok + 1])); - - if (obj == NULL) - break; - - who = msn_object_get_creator(obj); - sha1 = msn_object_get_sha1(obj); - - slplink = msn_session_get_slplink(session, who); - - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, who, - session->account); - - /* If the conversation doesn't exist then this is a custom smiley - * used in the first message in a MSN conversation: we need to create - * the conversation now, otherwise the custom smiley won't be shown. - * This happens because every GtkIMHtml has its own smiley tree: if - * the conversation doesn't exist then we cannot associate the new - * smiley with its GtkIMHtml widget. */ - if (!conv) { - conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, session->account, who); - } - - if (purple_conv_custom_smiley_add(conv, smile, "sha1", sha1, TRUE)) { - msn_slplink_request_object(slplink, smile, got_emoticon, NULL, obj); - } - - msn_object_destroy(obj); - obj = NULL; - who = NULL; - sha1 = NULL; - } - g_strfreev(tokens); -} - -static gboolean -buddy_icon_cached(PurpleConnection *gc, MsnObject *obj) -{ - PurpleAccount *account; - PurpleBuddy *buddy; - const char *old; - const char *new; - - g_return_val_if_fail(obj != NULL, FALSE); - - account = purple_connection_get_account(gc); - - buddy = purple_find_buddy(account, msn_object_get_creator(obj)); - if (buddy == NULL) - return FALSE; - - old = purple_buddy_icons_get_checksum_for_user(buddy); - new = msn_object_get_sha1(obj); - - if (new == NULL) - return FALSE; - - /* If the old and new checksums are the same, and the file actually exists, - * then return TRUE */ - if (old != NULL && !strcmp(old, new)) - return TRUE; - - return FALSE; -} - -static void -msn_release_buddy_icon_request(MsnUserList *userlist) -{ - MsnUser *user; - - g_return_if_fail(userlist != NULL); - -#ifdef MSN_DEBUG_UD - purple_debug_info("msn", "Releasing buddy icon request\n"); -#endif - - if (userlist->buddy_icon_window > 0) - { - GQueue *queue; - PurpleAccount *account; - const char *username; - - queue = userlist->buddy_icon_requests; - - if (g_queue_is_empty(userlist->buddy_icon_requests)) - return; - - user = g_queue_pop_head(queue); - - account = userlist->session->account; - username = user->passport; - - userlist->buddy_icon_window--; - msn_request_user_display(user); - -#ifdef MSN_DEBUG_UD - purple_debug_info("msn", "msn_release_buddy_icon_request(): buddy_icon_window-- yields =%d\n", - userlist->buddy_icon_window); -#endif - } -} - -/* - * Called on a timeout from end_user_display(). Frees a buddy icon window slow and dequeues the next - * buddy icon request if there is one. - */ -static gboolean -msn_release_buddy_icon_request_timeout(gpointer data) -{ - MsnUserList *userlist = (MsnUserList *)data; - - /* Free one window slot */ - userlist->buddy_icon_window++; - - /* Clear the tag for our former request timer */ - userlist->buddy_icon_request_timer = 0; - - msn_release_buddy_icon_request(userlist); - - return FALSE; -} - -void -msn_queue_buddy_icon_request(MsnUser *user) -{ - PurpleAccount *account; - MsnObject *obj; - GQueue *queue; - - g_return_if_fail(user != NULL); - - account = user->userlist->session->account; - - obj = msn_user_get_object(user); - - if (obj == NULL) - { - purple_buddy_icons_set_for_user(account, user->passport, NULL, 0, NULL); - return; - } - - if (!buddy_icon_cached(account->gc, obj)) - { - MsnUserList *userlist; - - userlist = user->userlist; - queue = userlist->buddy_icon_requests; - -#ifdef MSN_DEBUG_UD - purple_debug_info("msn", "Queueing buddy icon request for %s (buddy_icon_window = %i)\n", - user->passport, userlist->buddy_icon_window); -#endif - - g_queue_push_tail(queue, user); - - if (userlist->buddy_icon_window > 0) - msn_release_buddy_icon_request(userlist); - } -} - -static void -got_user_display(MsnSlpCall *slpcall, - const guchar *data, gsize size) -{ - MsnUserList *userlist; - const char *info; - PurpleAccount *account; - - g_return_if_fail(slpcall != NULL); - - info = slpcall->data_info; -#ifdef MSN_DEBUG_UD - purple_debug_info("msn", "Got User Display: %s\n", slpcall->slplink->remote_user); -#endif - - userlist = slpcall->slplink->session->userlist; - account = slpcall->slplink->session->account; - - purple_buddy_icons_set_for_user(account, slpcall->slplink->remote_user, - g_memdup(data, size), size, info); - -#if 0 - /* Free one window slot */ - userlist->buddy_icon_window++; - - purple_debug_info("msn", "got_user_display(): buddy_icon_window++ yields =%d\n", - userlist->buddy_icon_window); - - msn_release_buddy_icon_request(userlist); -#endif -} - -static void -end_user_display(MsnSlpCall *slpcall, MsnSession *session) -{ - MsnUserList *userlist; - - g_return_if_fail(session != NULL); - -#ifdef MSN_DEBUG_UD - purple_debug_info("msn", "End User Display\n"); -#endif - - userlist = session->userlist; - - /* If the session is being destroyed we better stop doing anything. */ - if (session->destroying) - return; - - /* Delay before freeing a buddy icon window slot and requesting the next icon, if appropriate. - * If we don't delay, we'll rapidly hit the MSN equivalent of AIM's rate limiting; the server will - * send us an error 800 like so: - * - * C: NS 000: XFR 21 SB - * S: NS 000: 800 21 - */ - if (userlist->buddy_icon_request_timer) { - /* Free the window slot used by this previous request */ - userlist->buddy_icon_window++; - - /* Clear our pending timeout */ - purple_timeout_remove(userlist->buddy_icon_request_timer); - } - - /* Wait BUDDY_ICON_DELAY_S seconds before freeing our window slot and requesting the next icon. */ - userlist->buddy_icon_request_timer = purple_timeout_add_seconds(BUDDY_ICON_DELAY, - msn_release_buddy_icon_request_timeout, userlist); -} - -void -msn_request_user_display(MsnUser *user) -{ - PurpleAccount *account; - MsnSession *session; - MsnSlpLink *slplink; - MsnObject *obj; - const char *info; - - session = user->userlist->session; - account = session->account; - - slplink = msn_session_get_slplink(session, user->passport); - - obj = msn_user_get_object(user); - - info = msn_object_get_sha1(obj); - - if (g_ascii_strcasecmp(user->passport, - purple_account_get_username(account))) - { - msn_slplink_request_object(slplink, info, got_user_display, - end_user_display, obj); - } - else - { - MsnObject *my_obj = NULL; - gconstpointer data = NULL; - size_t len = 0; - -#ifdef MSN_DEBUG_UD - purple_debug_info("msn", "Requesting our own user display\n"); -#endif - - my_obj = msn_user_get_object(session->user); - - if (my_obj != NULL) - { - PurpleStoredImage *img = msn_object_get_image(my_obj); - data = purple_imgstore_get_data(img); - len = purple_imgstore_get_size(img); - } - - purple_buddy_icons_set_for_user(account, user->passport, g_memdup(data, len), len, info); - - /* Free one window slot */ - session->userlist->buddy_icon_window++; - -#ifdef MSN_DEBUG_UD - purple_debug_info("msn", "msn_request_user_display(): buddy_icon_window++ yields =%d\n", - session->userlist->buddy_icon_window); -#endif - - msn_release_buddy_icon_request(session->userlist); - } -}
--- a/libpurple/protocols/msnp9/slp.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -/** - * @file slp.h MSNSLP support - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_SLP_H_ -#define _MSN_SLP_H_ - -#include "slpcall.h" -#include "session.h" -#include "internal.h" -#include "ft.h" - -void msn_xfer_progress_cb(MsnSlpCall *slpcall, gsize total_length, gsize - len, gsize offset); - -MsnSlpCall * msn_slp_sip_recv(MsnSlpLink *slplink, - const char *body); - -void send_bye(MsnSlpCall *slpcall, const char *type); - -void msn_xfer_completed_cb(MsnSlpCall *slpcall, - const guchar *body, gsize size); - -void msn_xfer_cancel(PurpleXfer *xfer); -void msn_xfer_end_cb(MsnSlpCall *slpcall, MsnSession *session); - -void msn_queue_buddy_icon_request(MsnUser *user); - -#endif /* _MSN_SLP_H_ */
--- a/libpurple/protocols/msnp9/slpcall.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,272 +0,0 @@ -/** - * @file slpcall.c SLP Call Functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "slpcall.h" -#include "slpsession.h" - -#include "slp.h" - -/* #define MSN_DEBUG_SLPCALL */ - -/************************************************************************** - * Util - **************************************************************************/ - -static char * -rand_guid(void) -{ - return g_strdup_printf("%4X%4X-%4X-%4X-%4X-%4X%4X%4X", - rand() % 0xAAFF + 0x1111, - rand() % 0xAAFF + 0x1111, - rand() % 0xAAFF + 0x1111, - rand() % 0xAAFF + 0x1111, - rand() % 0xAAFF + 0x1111, - rand() % 0xAAFF + 0x1111, - rand() % 0xAAFF + 0x1111, - rand() % 0xAAFF + 0x1111); -} - -/************************************************************************** - * Main - **************************************************************************/ - -MsnSlpCall * -msn_slp_call_new(MsnSlpLink *slplink) -{ - MsnSlpCall *slpcall; - - g_return_val_if_fail(slplink != NULL, NULL); - - slpcall = g_new0(MsnSlpCall, 1); - -#ifdef MSN_DEBUG_SLPCALL - purple_debug_info("msn", "slpcall_new: slpcall(%p)\n", slpcall); -#endif - - slpcall->slplink = slplink; - - msn_slplink_add_slpcall(slplink, slpcall); - - slpcall->timer = purple_timeout_add_seconds(MSN_SLPCALL_TIMEOUT, msn_slp_call_timeout, slpcall); - - return slpcall; -} - -void -msn_slp_call_destroy(MsnSlpCall *slpcall) -{ - GList *e; - MsnSession *session; - -#ifdef MSN_DEBUG_SLPCALL - purple_debug_info("msn", "slpcall_destroy: slpcall(%p)\n", slpcall); -#endif - - g_return_if_fail(slpcall != NULL); - - if (slpcall->timer) - purple_timeout_remove(slpcall->timer); - - if (slpcall->id != NULL) - g_free(slpcall->id); - - if (slpcall->branch != NULL) - g_free(slpcall->branch); - - if (slpcall->data_info != NULL) - g_free(slpcall->data_info); - - for (e = slpcall->slplink->slp_msgs; e != NULL; ) - { - MsnSlpMessage *slpmsg = e->data; - e = e->next; - -#ifdef MSN_DEBUG_SLPCALL_VERBOSE - purple_debug_info("msn", "slpcall_destroy: trying slpmsg(%p)\n", - slpmsg); -#endif - - if (slpmsg->slpcall == slpcall) - { - msn_slpmsg_destroy(slpmsg); - } - } - - session = slpcall->slplink->session; - - msn_slplink_remove_slpcall(slpcall->slplink, slpcall); - - if (slpcall->end_cb != NULL) - slpcall->end_cb(slpcall, session); - - if (slpcall->xfer != NULL) { - slpcall->xfer->data = NULL; - purple_xfer_unref(slpcall->xfer); - } - - g_free(slpcall); -} - -void -msn_slp_call_init(MsnSlpCall *slpcall, MsnSlpCallType type) -{ - slpcall->session_id = rand() % 0xFFFFFF00 + 4; - slpcall->id = rand_guid(); - slpcall->type = type; -} - -void -msn_slp_call_session_init(MsnSlpCall *slpcall) -{ - MsnSlpSession *slpsession; - - slpsession = msn_slp_session_new(slpcall); - - if (slpcall->session_init_cb) - slpcall->session_init_cb(slpsession); - - slpcall->started = TRUE; -} - -void -msn_slp_call_invite(MsnSlpCall *slpcall, const char *euf_guid, - int app_id, const char *context) -{ - MsnSlpLink *slplink; - MsnSlpMessage *slpmsg; - char *header; - char *content; - - g_return_if_fail(slpcall != NULL); - g_return_if_fail(context != NULL); - - slplink = slpcall->slplink; - - slpcall->branch = rand_guid(); - - content = g_strdup_printf( - "EUF-GUID: {%s}\r\n" - "SessionID: %lu\r\n" - "AppID: %d\r\n" - "Context: %s\r\n\r\n", - euf_guid, - slpcall->session_id, - app_id, - context); - - header = g_strdup_printf("INVITE MSNMSGR:%s MSNSLP/1.0", - slplink->remote_user); - - slpmsg = msn_slpmsg_sip_new(slpcall, 0, header, slpcall->branch, - "application/x-msnmsgr-sessionreqbody", content); - -#ifdef MSN_DEBUG_SLP - slpmsg->info = "SLP INVITE"; - slpmsg->text_body = TRUE; -#endif - - msn_slplink_send_slpmsg(slplink, slpmsg); - - g_free(header); - g_free(content); -} - -void -msn_slp_call_close(MsnSlpCall *slpcall) -{ - g_return_if_fail(slpcall != NULL); - g_return_if_fail(slpcall->slplink != NULL); - - send_bye(slpcall, "application/x-msnmsgr-sessionclosebody"); - msn_slplink_unleash(slpcall->slplink); - msn_slp_call_destroy(slpcall); -} - -gboolean -msn_slp_call_timeout(gpointer data) -{ - MsnSlpCall *slpcall; - - slpcall = data; - -#ifdef MSN_DEBUG_SLPCALL - purple_debug_info("msn", "slpcall_timeout: slpcall(%p)\n", slpcall); -#endif - - if (!slpcall->pending && !slpcall->progress) - { - msn_slp_call_destroy(slpcall); - return FALSE; - } - - slpcall->progress = FALSE; - - return TRUE; -} - -MsnSlpCall * -msn_slp_process_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) -{ - MsnSlpCall *slpcall; - const guchar *body; - gsize body_len; - - slpcall = NULL; - body = slpmsg->buffer; - body_len = slpmsg->size; - - if (slpmsg->flags == 0x0) - { - char *body_str; - - body_str = g_strndup((const char *)body, body_len); - slpcall = msn_slp_sip_recv(slplink, body_str); - g_free(body_str); - } - else if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) - { - slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->session_id); - - if (slpcall != NULL) - { - if (slpcall->timer) - purple_timeout_remove(slpcall->timer); - - slpcall->cb(slpcall, body, body_len); - - slpcall->wasted = TRUE; - } - } -#if 0 - else if (slpmsg->flags == 0x100) - { - slpcall = slplink->directconn->initial_call; - - if (slpcall != NULL) - msn_slp_call_session_init(slpcall); - } -#endif - - return slpcall; -}
--- a/libpurple/protocols/msnp9/slpcall.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ -/** - * @file slpcall.h SLP Call functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_SLPCALL_H_ -#define _MSN_SLPCALL_H_ - -#include "internal.h" -#include "ft.h" - -typedef struct _MsnSlpCall MsnSlpCall; - -#include "slplink.h" -#include "slpsession.h" - -/* The official client seems to timeout slp calls after 5 minutes */ -#define MSN_SLPCALL_TIMEOUT 300 - -typedef enum -{ - MSN_SLPCALL_ANY, - MSN_SLPCALL_DC, - -} MsnSlpCallType; - -struct _MsnSlpCall -{ - /* MsnSession *session; */ - MsnSlpLink *slplink; - - MsnSlpCallType type; - - /* Call-ID */ - char *id; - char *branch; - - long session_id; - long app_id; - - gboolean pending; /**< A flag that states if we should wait for this - slpcall to start and do not time out. */ - gboolean progress; /**< A flag that states if there has been progress since - the last time out. */ - gboolean wasted; /**< A flag that states if this slpcall is going to be - destroyed. */ - gboolean started; /**< A flag that states if this slpcall's session has - been initiated. */ - - void (*progress_cb)(MsnSlpCall *slpcall, - gsize total_length, gsize len, gsize offset); - void (*session_init_cb)(MsnSlpSession *slpsession); - - /* Can be checksum, or smile */ - char *data_info; - - PurpleXfer *xfer; - - MsnSlpCb cb; - void (*end_cb)(MsnSlpCall *slpcall, MsnSession *session); - - int timer; -}; - -MsnSlpCall *msn_slp_call_new(MsnSlpLink *slplink); -void msn_slp_call_init(MsnSlpCall *slpcall, MsnSlpCallType type); -void msn_slp_call_session_init(MsnSlpCall *slpcall); -void msn_slp_call_destroy(MsnSlpCall *slpcall); -void msn_slp_call_invite(MsnSlpCall *slpcall, const char *euf_guid, - int app_id, const char *context); -void msn_slp_call_close(MsnSlpCall *slpcall); -gboolean msn_slp_call_timeout(gpointer data); - -#endif /* _MSN_SLPCALL_H_ */
--- a/libpurple/protocols/msnp9/slplink.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,822 +0,0 @@ -/** - * @file slplink.c MSNSLP Link support - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "slplink.h" - -#include "switchboard.h" -#include "slp.h" - -void msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg); - -#ifdef MSN_DEBUG_SLP_FILES -static int m_sc = 0; -static int m_rc = 0; - -static void -debug_msg_to_file(MsnMessage *msg, gboolean send) -{ - char *tmp; - char *dir; - char *pload; - FILE *tf; - int c; - gsize pload_size; - - dir = send ? "send" : "recv"; - c = send ? m_sc++ : m_rc++; - tmp = g_strdup_printf("%s/msntest/%s/%03d", g_get_home_dir(), dir, c); - tf = g_fopen(tmp, "wb"); - if (tf == NULL) - { - purple_debug_error("msn", "could not open debug file\n"); - return; - } - pload = msn_message_gen_payload(msg, &pload_size); - fwrite(pload, 1, pload_size, tf); - fclose(tf); - g_free(tmp); -} -#endif - -/************************************************************************** - * Main - **************************************************************************/ - -MsnSlpLink * -msn_slplink_new(MsnSession *session, const char *username) -{ - MsnSlpLink *slplink; - - g_return_val_if_fail(session != NULL, NULL); - - slplink = g_new0(MsnSlpLink, 1); - -#ifdef MSN_DEBUG_SLPLINK - purple_debug_info("msn", "slplink_new: slplink(%p)\n", slplink); -#endif - - slplink->session = session; - slplink->slp_seq_id = rand() % 0xFFFFFF00 + 4; - - slplink->local_user = g_strdup(msn_user_get_passport(session->user)); - slplink->remote_user = g_strdup(username); - - slplink->slp_msg_queue = g_queue_new(); - - session->slplinks = - g_list_append(session->slplinks, slplink); - - return slplink; -} - -void -msn_slplink_destroy(MsnSlpLink *slplink) -{ - MsnSession *session; - -#ifdef MSN_DEBUG_SLPLINK - purple_debug_info("msn", "slplink_destroy: slplink(%p)\n", slplink); -#endif - - g_return_if_fail(slplink != NULL); - - if (slplink->swboard != NULL) - slplink->swboard->slplinks = g_list_remove(slplink->swboard->slplinks, slplink); - - session = slplink->session; - - if (slplink->local_user != NULL) - g_free(slplink->local_user); - - if (slplink->remote_user != NULL) - g_free(slplink->remote_user); - - if (slplink->directconn != NULL) - msn_directconn_destroy(slplink->directconn); - - while (slplink->slp_calls != NULL) - msn_slp_call_destroy(slplink->slp_calls->data); - - g_queue_free(slplink->slp_msg_queue); - - session->slplinks = - g_list_remove(session->slplinks, slplink); - - g_free(slplink); -} - -MsnSlpLink * -msn_session_find_slplink(MsnSession *session, const char *who) -{ - GList *l; - - for (l = session->slplinks; l != NULL; l = l->next) - { - MsnSlpLink *slplink; - - slplink = l->data; - - if (!strcmp(slplink->remote_user, who)) - return slplink; - } - - return NULL; -} - -MsnSlpLink * -msn_session_get_slplink(MsnSession *session, const char *username) -{ - MsnSlpLink *slplink; - - g_return_val_if_fail(session != NULL, NULL); - g_return_val_if_fail(username != NULL, NULL); - - slplink = msn_session_find_slplink(session, username); - - if (slplink == NULL) - slplink = msn_slplink_new(session, username); - - return slplink; -} - -MsnSlpSession * -msn_slplink_find_slp_session(MsnSlpLink *slplink, long session_id) -{ - GList *l; - MsnSlpSession *slpsession; - - for (l = slplink->slp_sessions; l != NULL; l = l->next) - { - slpsession = l->data; - - if (slpsession->id == session_id) - return slpsession; - } - - return NULL; -} - -void -msn_slplink_add_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall) -{ - if (slplink->swboard != NULL) - slplink->swboard->flag |= MSN_SB_FLAG_FT; - - slplink->slp_calls = g_list_append(slplink->slp_calls, slpcall); -} - -void -msn_slplink_remove_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall) -{ - slplink->slp_calls = g_list_remove(slplink->slp_calls, slpcall); - - /* The slplink has no slpcalls in it. If no one is using it, we might - * destroy the switchboard, but we should be careful not to use the slplink - * again. */ - if (slplink->slp_calls == NULL) - { - if (slplink->swboard != NULL) - { - if (msn_switchboard_release(slplink->swboard, MSN_SB_FLAG_FT)) - /* I'm not sure this is the best thing to do, but it's better - * than nothing. */ - slpcall->slplink = NULL; - } - } -} - -MsnSlpCall * -msn_slplink_find_slp_call(MsnSlpLink *slplink, const char *id) -{ - GList *l; - MsnSlpCall *slpcall; - - if (!id) - return NULL; - - for (l = slplink->slp_calls; l != NULL; l = l->next) - { - slpcall = l->data; - - if (slpcall->id && !strcmp(slpcall->id, id)) - return slpcall; - } - - return NULL; -} - -MsnSlpCall * -msn_slplink_find_slp_call_with_session_id(MsnSlpLink *slplink, long id) -{ - GList *l; - MsnSlpCall *slpcall; - - for (l = slplink->slp_calls; l != NULL; l = l->next) - { - slpcall = l->data; - - if (slpcall->session_id == id) - return slpcall; - } - - return NULL; -} - -void -msn_slplink_send_msg(MsnSlpLink *slplink, MsnMessage *msg) -{ - if (slplink->directconn != NULL) - { - msn_directconn_send_msg(slplink->directconn, msg); - } - else - { - if (slplink->swboard == NULL) - { - slplink->swboard = msn_session_get_swboard(slplink->session, - slplink->remote_user, MSN_SB_FLAG_FT); - - if (slplink->swboard == NULL) - return; - - /* If swboard is destroyed we will be too */ - slplink->swboard->slplinks = g_list_prepend(slplink->swboard->slplinks, slplink); - } - - msn_switchboard_send_msg(slplink->swboard, msg, TRUE); - } -} - -/* We have received the message ack */ -static void -msg_ack(MsnMessage *msg, void *data) -{ - MsnSlpMessage *slpmsg; - long long real_size; - - slpmsg = data; - - real_size = (slpmsg->flags == 0x2) ? 0 : slpmsg->size; - - slpmsg->offset += msg->msnslp_header.length; - - if (slpmsg->offset < real_size) - { - msn_slplink_send_msgpart(slpmsg->slplink, slpmsg); - } - else - { - /* The whole message has been sent */ - if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) - { - if (slpmsg->slpcall != NULL) - { - if (slpmsg->slpcall->cb) - slpmsg->slpcall->cb(slpmsg->slpcall, - NULL, 0); - } - } - } - - slpmsg->msgs = g_list_remove(slpmsg->msgs, msg); -} - -/* We have received the message nak. */ -static void -msg_nak(MsnMessage *msg, void *data) -{ - MsnSlpMessage *slpmsg; - - slpmsg = data; - - msn_slplink_send_msgpart(slpmsg->slplink, slpmsg); - - slpmsg->msgs = g_list_remove(slpmsg->msgs, msg); -} - -void -msn_slplink_send_msgpart(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) -{ - MsnMessage *msg; - long long real_size; - size_t len = 0; - - /* Maybe we will want to create a new msg for this slpmsg instead of - * reusing the same one all the time. */ - msg = slpmsg->msg; - - real_size = (slpmsg->flags == 0x2) ? 0 : slpmsg->size; - - if (slpmsg->offset < real_size) - { - if (slpmsg->fp) - { - char data[1202]; - len = fread(data, 1, sizeof(data), slpmsg->fp); - msn_message_set_bin_data(msg, data, len); - } - else - { - len = slpmsg->size - slpmsg->offset; - - if (len > 1202) - len = 1202; - - msn_message_set_bin_data(msg, slpmsg->buffer + slpmsg->offset, len); - } - - msg->msnslp_header.offset = slpmsg->offset; - msg->msnslp_header.length = len; - } - -#ifdef MSN_DEBUG_SLP - msn_message_show_readable(msg, slpmsg->info, slpmsg->text_body); -#endif - -#ifdef MSN_DEBUG_SLP_FILES - debug_msg_to_file(msg, TRUE); -#endif - - slpmsg->msgs = - g_list_append(slpmsg->msgs, msg); - msn_slplink_send_msg(slplink, msg); - - if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) && - (slpmsg->slpcall != NULL)) - { - slpmsg->slpcall->progress = TRUE; - - if (slpmsg->slpcall->progress_cb != NULL) - { - slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size, - len, slpmsg->offset); - } - } - - /* slpmsg->offset += len; */ -} - -void -msn_slplink_release_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) -{ - MsnMessage *msg; - - slpmsg->msg = msg = msn_message_new_msnslp(); - - if (slpmsg->flags == 0x0) - { - msg->msnslp_header.session_id = slpmsg->session_id; - msg->msnslp_header.ack_id = rand() % 0xFFFFFF00; - } - else if (slpmsg->flags == 0x2) - { - msg->msnslp_header.session_id = slpmsg->session_id; - msg->msnslp_header.ack_id = slpmsg->ack_id; - msg->msnslp_header.ack_size = slpmsg->ack_size; - msg->msnslp_header.ack_sub_id = slpmsg->ack_sub_id; - } - else if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) - { - MsnSlpSession *slpsession; - slpsession = slpmsg->slpsession; - - g_return_if_fail(slpsession != NULL); - msg->msnslp_header.session_id = slpsession->id; - msg->msnslp_footer.value = slpsession->app_id; - msg->msnslp_header.ack_id = rand() % 0xFFFFFF00; - } - else if (slpmsg->flags == 0x100) - { - msg->msnslp_header.ack_id = slpmsg->ack_id; - msg->msnslp_header.ack_sub_id = slpmsg->ack_sub_id; - msg->msnslp_header.ack_size = slpmsg->ack_size; - } - - msg->msnslp_header.id = slpmsg->id; - msg->msnslp_header.flags = slpmsg->flags; - - msg->msnslp_header.total_size = slpmsg->size; - - msn_message_set_attr(msg, "P2P-Dest", slplink->remote_user); - - msg->ack_cb = msg_ack; - msg->nak_cb = msg_nak; - msg->ack_data = slpmsg; - - msn_slplink_send_msgpart(slplink, slpmsg); - - msn_message_destroy(msg); -} - -void -msn_slplink_queue_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) -{ - slpmsg->id = slplink->slp_seq_id++; - - g_queue_push_head(slplink->slp_msg_queue, slpmsg); -} - -void -msn_slplink_send_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg) -{ - slpmsg->id = slplink->slp_seq_id++; - - msn_slplink_release_slpmsg(slplink, slpmsg); -} - -void -msn_slplink_unleash(MsnSlpLink *slplink) -{ - MsnSlpMessage *slpmsg; - - /* Send the queued msgs in the order they came. */ - - while ((slpmsg = g_queue_pop_tail(slplink->slp_msg_queue)) != NULL) - { - msn_slplink_release_slpmsg(slplink, slpmsg); - } -} - -void -msn_slplink_send_ack(MsnSlpLink *slplink, MsnMessage *msg) -{ - MsnSlpMessage *slpmsg; - - slpmsg = msn_slpmsg_new(slplink); - - slpmsg->session_id = msg->msnslp_header.session_id; - slpmsg->size = msg->msnslp_header.total_size; - slpmsg->flags = 0x02; - slpmsg->ack_id = msg->msnslp_header.id; - slpmsg->ack_sub_id = msg->msnslp_header.ack_id; - slpmsg->ack_size = msg->msnslp_header.total_size; - -#ifdef MSN_DEBUG_SLP - slpmsg->info = "SLP ACK"; -#endif - - msn_slplink_send_slpmsg(slplink, slpmsg); - msn_slpmsg_destroy(slpmsg); -} - -static void -send_file_cb(MsnSlpSession *slpsession) -{ - MsnSlpCall *slpcall; - MsnSlpMessage *slpmsg; - struct stat st; - PurpleXfer *xfer; - - slpcall = slpsession->slpcall; - slpmsg = msn_slpmsg_new(slpcall->slplink); - slpmsg->slpcall = slpcall; - slpmsg->flags = 0x1000030; - slpmsg->slpsession = slpsession; -#ifdef MSN_DEBUG_SLP - slpmsg->info = "SLP FILE"; -#endif - xfer = (PurpleXfer *)slpcall->xfer; - purple_xfer_start(slpcall->xfer, -1, NULL, 0); - slpmsg->fp = xfer->dest_fp; - if (g_stat(purple_xfer_get_local_filename(xfer), &st) == 0) - slpmsg->size = st.st_size; - xfer->dest_fp = NULL; /* Disable double fclose() */ - - msn_slplink_send_slpmsg(slpcall->slplink, slpmsg); -} - -void -msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg) -{ - MsnSlpMessage *slpmsg; - const char *data; - guint64 offset; - gsize len; - -#ifdef MSN_DEBUG_SLP - msn_slpmsg_show(msg); -#endif - -#ifdef MSN_DEBUG_SLP_FILES - debug_msg_to_file(msg, FALSE); -#endif - - if (msg->msnslp_header.total_size < msg->msnslp_header.length) - { - purple_debug_error("msn", "This can't be good\n"); - g_return_if_reached(); - } - - slpmsg = NULL; - data = msn_message_get_bin_data(msg, &len); - - /* - OVERHEAD! - if (msg->msnslp_header.length < msg->msnslp_header.total_size) - */ - - offset = msg->msnslp_header.offset; - - if (offset == 0) - { - slpmsg = msn_slpmsg_new(slplink); - slpmsg->id = msg->msnslp_header.id; - slpmsg->session_id = msg->msnslp_header.session_id; - slpmsg->size = msg->msnslp_header.total_size; - slpmsg->flags = msg->msnslp_header.flags; - - if (slpmsg->session_id) - { - if (slpmsg->slpcall == NULL) - slpmsg->slpcall = msn_slplink_find_slp_call_with_session_id(slplink, slpmsg->session_id); - - if (slpmsg->slpcall != NULL) - { - if (slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) - { - PurpleXfer *xfer; - - xfer = slpmsg->slpcall->xfer; - - if (xfer != NULL) - { - purple_xfer_ref(xfer); - purple_xfer_start(xfer, -1, NULL, 0); - - if (xfer->data == NULL) { - purple_xfer_unref(xfer); - return; - } else { - purple_xfer_unref(xfer); - slpmsg->fp = xfer->dest_fp; - xfer->dest_fp = NULL; /* Disable double fclose() */ - } - } - } - } - } - if (!slpmsg->fp && slpmsg->size) - { - slpmsg->buffer = g_try_malloc(slpmsg->size); - if (slpmsg->buffer == NULL) - { - purple_debug_error("msn", "Failed to allocate buffer for slpmsg\n"); - msn_slpmsg_destroy(slpmsg); - return; - } - } - } - else - { - slpmsg = msn_slplink_message_find(slplink, msg->msnslp_header.session_id, msg->msnslp_header.id); - } - - if (slpmsg == NULL) - { - /* Probably the transfer was canceled */ - purple_debug_error("msn", "Couldn't find slpmsg\n"); - return; - } - - if (slpmsg->fp) - { - /* fseek(slpmsg->fp, offset, SEEK_SET); */ - len = fwrite(data, 1, len, slpmsg->fp); - } - else if (slpmsg->size && slpmsg->buffer) - { - if (G_MAXSIZE - len < offset || (offset + len) > slpmsg->size) - { - purple_debug_error("msn", "Oversized slpmsg\n"); - g_return_if_reached(); - } - else - memcpy(slpmsg->buffer + offset, data, len); - } - - if ((slpmsg->flags == 0x20 || slpmsg->flags == 0x1000030) && - (slpmsg->slpcall != NULL)) - { - slpmsg->slpcall->progress = TRUE; - - if (slpmsg->slpcall->progress_cb != NULL) - { - slpmsg->slpcall->progress_cb(slpmsg->slpcall, slpmsg->size, - len, offset); - } - } - -#if 0 - if (slpmsg->buffer == NULL) - return; -#endif - - if (msg->msnslp_header.offset + msg->msnslp_header.length - >= msg->msnslp_header.total_size) - { - /* All the pieces of the slpmsg have been received */ - MsnSlpCall *slpcall; - - slpcall = msn_slp_process_msg(slplink, slpmsg); - - if (slpmsg->flags == 0x100) - { - MsnDirectConn *directconn; - - directconn = slplink->directconn; - - if (!directconn->acked) - msn_directconn_send_handshake(directconn); - } - else if (slpmsg->flags == 0x0 || slpmsg->flags == 0x20 || - slpmsg->flags == 0x1000030) - { - /* Release all the messages and send the ACK */ - - msn_slplink_send_ack(slplink, msg); - msn_slplink_unleash(slplink); - } - - msn_slpmsg_destroy(slpmsg); - - if (slpcall != NULL && slpcall->wasted) - msn_slp_call_destroy(slpcall); - } -} - -MsnSlpMessage * -msn_slplink_message_find(MsnSlpLink *slplink, long session_id, long id) -{ - GList *e; - - for (e = slplink->slp_msgs; e != NULL; e = e->next) - { - MsnSlpMessage *slpmsg = e->data; - - if ((slpmsg->session_id == session_id) && (slpmsg->id == id)) - return slpmsg; - } - - return NULL; -} - -typedef struct -{ - guint32 length; - guint32 unk1; - guint32 file_size; - guint32 unk2; - guint32 unk3; -} MsnContextHeader; - -#define MAX_FILE_NAME_LEN 0x226 - -static gchar * -gen_context(const char *file_name, const char *file_path) -{ - struct stat st; - gsize size = 0; - MsnContextHeader header; - gchar *u8 = NULL; - guchar *base; - guchar *n; - gchar *ret; - gunichar2 *uni = NULL; - glong currentChar = 0; - glong uni_len = 0; - gsize len; - - if (g_stat(file_path, &st) == 0) - size = st.st_size; - - if(!file_name) { - u8 = purple_utf8_try_convert(g_basename(file_path)); - file_name = u8; - } - - uni = g_utf8_to_utf16(file_name, -1, NULL, &uni_len, NULL); - - if(u8) { - g_free(u8); - file_name = NULL; - u8 = NULL; - } - - len = sizeof(MsnContextHeader) + MAX_FILE_NAME_LEN + 4; - - header.length = GUINT32_TO_LE(len); - header.unk1 = GUINT32_TO_LE(2); - header.file_size = GUINT32_TO_LE(size); - header.unk2 = GUINT32_TO_LE(0); - header.unk3 = GUINT32_TO_LE(0); - - base = g_malloc(len + 1); - n = base; - - memcpy(n, &header, sizeof(MsnContextHeader)); - n += sizeof(MsnContextHeader); - - memset(n, 0x00, MAX_FILE_NAME_LEN); - for(currentChar = 0; currentChar < uni_len; currentChar++) { - *((gunichar2 *)n + currentChar) = GUINT16_TO_LE(uni[currentChar]); - } - n += MAX_FILE_NAME_LEN; - - memset(n, 0xFF, 4); - n += 4; - - g_free(uni); - ret = purple_base64_encode(base, len); - g_free(base); - return ret; -} - -void -msn_slplink_request_ft(MsnSlpLink *slplink, PurpleXfer *xfer) -{ - MsnSlpCall *slpcall; - char *context; - const char *fn; - const char *fp; - - fn = purple_xfer_get_filename(xfer); - fp = purple_xfer_get_local_filename(xfer); - - g_return_if_fail(slplink != NULL); - g_return_if_fail(fp != NULL); - - slpcall = msn_slp_call_new(slplink); - msn_slp_call_init(slpcall, MSN_SLPCALL_DC); - - slpcall->session_init_cb = send_file_cb; - slpcall->end_cb = msn_xfer_end_cb; - slpcall->progress_cb = msn_xfer_progress_cb; - slpcall->cb = msn_xfer_completed_cb; - slpcall->xfer = xfer; - purple_xfer_ref(slpcall->xfer); - - slpcall->pending = TRUE; - - purple_xfer_set_cancel_send_fnc(xfer, msn_xfer_cancel); - - xfer->data = slpcall; - - context = gen_context(fn, fp); - - msn_slp_call_invite(slpcall, "5D3E02AB-6190-11D3-BBBB-00C04F795683", 2, - context); - - g_free(context); -} - -void -msn_slplink_request_object(MsnSlpLink *slplink, - const char *info, - MsnSlpCb cb, - MsnSlpEndCb end_cb, - const MsnObject *obj) -{ - MsnSlpCall *slpcall; - char *msnobj_data; - char *msnobj_base64; - - g_return_if_fail(slplink != NULL); - g_return_if_fail(obj != NULL); - - msnobj_data = msn_object_to_string(obj); - msnobj_base64 = purple_base64_encode((const guchar *)msnobj_data, strlen(msnobj_data)); - g_free(msnobj_data); - - slpcall = msn_slp_call_new(slplink); - msn_slp_call_init(slpcall, MSN_SLPCALL_ANY); - - slpcall->data_info = g_strdup(info); - slpcall->cb = cb; - slpcall->end_cb = end_cb; - - msn_slp_call_invite(slpcall, "A4268EEC-FEC5-49E5-95C3-F126696BDBF6", 1, - msnobj_base64); - - g_free(msnobj_base64); -}
--- a/libpurple/protocols/msnp9/slplink.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,97 +0,0 @@ -/** - * @file slplink.h MSNSLP Link support - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_SLPLINK_H_ -#define _MSN_SLPLINK_H_ - -typedef struct _MsnSlpLink MsnSlpLink; - -#include "directconn.h" -#include "slpcall.h" -#include "slpmsg.h" - -#include "switchboard.h" - -#include "ft.h" - -#include "session.h" - -typedef void (*MsnSlpCb)(MsnSlpCall *slpcall, - const guchar *data, gsize size); -typedef void (*MsnSlpEndCb)(MsnSlpCall *slpcall, MsnSession *session); - -struct _MsnSlpLink -{ - MsnSession *session; - MsnSwitchBoard *swboard; - - char *local_user; - char *remote_user; - - int slp_seq_id; - - MsnDirectConn *directconn; - - GList *slp_calls; - GList *slp_sessions; - GList *slp_msgs; - - GQueue *slp_msg_queue; -}; - -MsnSlpLink *msn_slplink_new(MsnSession *session, const char *username); -void msn_slplink_destroy(MsnSlpLink *slplink); -MsnSlpLink *msn_session_find_slplink(MsnSession *session, - const char *who); -MsnSlpLink *msn_session_get_slplink(MsnSession *session, const char *username); -MsnSlpSession *msn_slplink_find_slp_session(MsnSlpLink *slplink, - long session_id); -void msn_slplink_add_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall); -void msn_slplink_remove_slpcall(MsnSlpLink *slplink, MsnSlpCall *slpcall); -MsnSlpCall *msn_slplink_find_slp_call(MsnSlpLink *slplink, - const char *id); -MsnSlpCall *msn_slplink_find_slp_call_with_session_id(MsnSlpLink *slplink, long id); -void msn_slplink_send_msg(MsnSlpLink *slplink, MsnMessage *msg); -void msn_slplink_release_slpmsg(MsnSlpLink *slplink, - MsnSlpMessage *slpmsg); -void msn_slplink_queue_slpmsg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg); -void msn_slplink_send_slpmsg(MsnSlpLink *slplink, - MsnSlpMessage *slpmsg); -void msn_slplink_unleash(MsnSlpLink *slplink); -void msn_slplink_send_ack(MsnSlpLink *slplink, MsnMessage *msg); -void msn_slplink_process_msg(MsnSlpLink *slplink, MsnMessage *msg); -MsnSlpMessage *msn_slplink_message_find(MsnSlpLink *slplink, long session_id, long id); -void msn_slplink_append_slp_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg); -void msn_slplink_remove_slp_msg(MsnSlpLink *slplink, - MsnSlpMessage *slpmsg); -void msn_slplink_request_ft(MsnSlpLink *slplink, PurpleXfer *xfer); - -void msn_slplink_request_object(MsnSlpLink *slplink, - const char *info, - MsnSlpCb cb, - MsnSlpEndCb end_cb, - const MsnObject *obj); - -MsnSlpCall *msn_slp_process_msg(MsnSlpLink *slplink, MsnSlpMessage *slpmsg); - -#endif /* _MSN_SLPLINK_H_ */
--- a/libpurple/protocols/msnp9/slpmsg.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,239 +0,0 @@ -/** - * @file slpmsg.h SLP Message functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "slpmsg.h" -#include "slplink.h" - -/************************************************************************** - * SLP Message - **************************************************************************/ - -MsnSlpMessage * -msn_slpmsg_new(MsnSlpLink *slplink) -{ - MsnSlpMessage *slpmsg; - - slpmsg = g_new0(MsnSlpMessage, 1); - -#ifdef MSN_DEBUG_SLPMSG - purple_debug_info("msn", "slpmsg new (%p)\n", slpmsg); -#endif - - slpmsg->slplink = slplink; - - slplink->slp_msgs = - g_list_append(slplink->slp_msgs, slpmsg); - - return slpmsg; -} - -void -msn_slpmsg_destroy(MsnSlpMessage *slpmsg) -{ - MsnSlpLink *slplink; - GList *cur; - - g_return_if_fail(slpmsg != NULL); - -#ifdef MSN_DEBUG_SLPMSG - purple_debug_info("msn", "slpmsg destroy (%p)\n", slpmsg); -#endif - - slplink = slpmsg->slplink; - - if (slpmsg->fp != NULL) - fclose(slpmsg->fp); - - purple_imgstore_unref(slpmsg->img); - - /* We don't want to free the data of the PurpleStoredImage, - * but to avoid code duplication, it's sharing buffer. */ - if (slpmsg->img == NULL) - g_free(slpmsg->buffer); - -#ifdef MSN_DEBUG_SLP - /* - if (slpmsg->info != NULL) - g_free(slpmsg->info); - */ -#endif - - for (cur = slpmsg->msgs; cur != NULL; cur = cur->next) - { - /* Something is pointing to this slpmsg, so we should remove that - * pointer to prevent a crash. */ - /* Ex: a user goes offline and after that we receive an ACK */ - - MsnMessage *msg = cur->data; - -#ifdef MSN_DEBUG_SLPMSG - purple_debug_info("msn", "Unlink slpmsg callbacks.\n"); -#endif - - msg->ack_cb = NULL; - msg->nak_cb = NULL; - msg->ack_data = NULL; - } - - slplink->slp_msgs = g_list_remove(slplink->slp_msgs, slpmsg); - - g_free(slpmsg); -} - -void -msn_slpmsg_set_body(MsnSlpMessage *slpmsg, const char *body, - long long size) -{ - /* We can only have one data source at a time. */ - g_return_if_fail(slpmsg->buffer == NULL); - g_return_if_fail(slpmsg->img == NULL); - g_return_if_fail(slpmsg->fp == NULL); - - if (body != NULL) - slpmsg->buffer = g_memdup(body, size); - else - slpmsg->buffer = g_new0(guchar, size); - - slpmsg->size = size; -} - -void -msn_slpmsg_set_image(MsnSlpMessage *slpmsg, PurpleStoredImage *img) -{ - /* We can only have one data source at a time. */ - g_return_if_fail(slpmsg->buffer == NULL); - g_return_if_fail(slpmsg->img == NULL); - g_return_if_fail(slpmsg->fp == NULL); - - slpmsg->img = purple_imgstore_ref(img); - slpmsg->buffer = (guchar *)purple_imgstore_get_data(img); - slpmsg->size = purple_imgstore_get_size(img); -} - -void -msn_slpmsg_open_file(MsnSlpMessage *slpmsg, const char *file_name) -{ - struct stat st; - - /* We can only have one data source at a time. */ - g_return_if_fail(slpmsg->buffer == NULL); - g_return_if_fail(slpmsg->img == NULL); - g_return_if_fail(slpmsg->fp == NULL); - - slpmsg->fp = g_fopen(file_name, "rb"); - - if (g_stat(file_name, &st) == 0) - slpmsg->size = st.st_size; -} - -#ifdef MSN_DEBUG_SLP -void -msn_slpmsg_show(MsnMessage *msg) -{ - const char *info; - gboolean text; - guint32 flags; - - text = FALSE; - - flags = GUINT32_TO_LE(msg->msnslp_header.flags); - - switch (flags) - { - case 0x0: - info = "SLP CONTROL"; - text = TRUE; - break; - case 0x2: - info = "SLP ACK"; break; - case 0x20: - case 0x1000030: - info = "SLP DATA"; break; - default: - info = "SLP UNKNOWN"; break; - } - - msn_message_show_readable(msg, info, text); -} -#endif - -MsnSlpMessage * -msn_slpmsg_sip_new(MsnSlpCall *slpcall, int cseq, - const char *header, const char *branch, - const char *content_type, const char *content) -{ - MsnSlpLink *slplink; - MsnSlpMessage *slpmsg; - char *body; - gsize body_len; - gsize content_len; - - g_return_val_if_fail(slpcall != NULL, NULL); - g_return_val_if_fail(header != NULL, NULL); - - slplink = slpcall->slplink; - - /* Let's remember that "content" should end with a 0x00 */ - - content_len = (content != NULL) ? strlen(content) + 1 : 0; - - body = g_strdup_printf( - "%s\r\n" - "To: <msnmsgr:%s>\r\n" - "From: <msnmsgr:%s>\r\n" - "Via: MSNSLP/1.0/TLP ;branch={%s}\r\n" - "CSeq: %d\r\n" - "Call-ID: {%s}\r\n" - "Max-Forwards: 0\r\n" - "Content-Type: %s\r\n" - "Content-Length: %" G_GSIZE_FORMAT "\r\n" - "\r\n", - header, - slplink->remote_user, - slplink->local_user, - branch, - cseq, - slpcall->id, - content_type, - content_len); - - body_len = strlen(body); - - if (content_len > 0) - { - body_len += content_len; - body = g_realloc(body, body_len); - g_strlcat(body, content, body_len); - } - - slpmsg = msn_slpmsg_new(slplink); - msn_slpmsg_set_body(slpmsg, body, body_len); - - slpmsg->sip = TRUE; - slpmsg->slpcall = slpcall; - - g_free(body); - - return slpmsg; -}
--- a/libpurple/protocols/msnp9/slpmsg.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,109 +0,0 @@ -/** - * @file slpmsg.h SLP Message functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_SLPMSG_H_ -#define _MSN_SLPMSG_H_ - -typedef struct _MsnSlpMessage MsnSlpMessage; - -#include "imgstore.h" - -#include "slpsession.h" -#include "slpcall.h" -#include "slplink.h" -#include "session.h" -#include "msg.h" - -#include "slp.h" - -/** - * A SLP Message This contains everything that we will need to send a SLP - * Message even if has to be sent in several parts. - */ -struct _MsnSlpMessage -{ - MsnSlpSession *slpsession; - MsnSlpCall *slpcall; /**< The slpcall to which this slp message belongs (if applicable). */ - MsnSlpLink *slplink; /**< The slplink through which this slp message is being sent. */ - MsnSession *session; - - long session_id; - long id; - long ack_id; - long ack_sub_id; - long long ack_size; - long app_id; - - gboolean sip; /**< A flag that states if this is a SIP slp message. */ - int ref_count; /**< The reference count. */ - long flags; - - FILE *fp; - PurpleStoredImage *img; - guchar *buffer; - long long offset; - long long size; - - GList *msgs; /**< The real messages. */ - -#if 1 - MsnMessage *msg; /**< The temporary real message that will be sent. */ -#endif - -#ifdef MSN_DEBUG_SLP - char *info; - gboolean text_body; -#endif -}; - -/** - * Creates a new slp message - * - * @param slplink The slplink through which this slp message will be sent. - * @return The created slp message. - */ -MsnSlpMessage *msn_slpmsg_new(MsnSlpLink *slplink); - -/** - * Destroys a slp message - * - * @param slpmsg The slp message to destory. - */ -void msn_slpmsg_destroy(MsnSlpMessage *slpmsg); - -void msn_slpmsg_set_body(MsnSlpMessage *slpmsg, const char *body, - long long size); -void msn_slpmsg_set_image(MsnSlpMessage *slpmsg, PurpleStoredImage *img); -void msn_slpmsg_open_file(MsnSlpMessage *slpmsg, - const char *file_name); -MsnSlpMessage * msn_slpmsg_sip_new(MsnSlpCall *slpcall, int cseq, - const char *header, - const char *branch, - const char *content_type, - const char *content); - -#ifdef MSN_DEBUG_SLP -void msn_slpmsg_show(MsnMessage *msg); -#endif - -#endif /* _MSN_SLPMSG_H_ */
--- a/libpurple/protocols/msnp9/slpsession.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -/** - * @file slpsession.h SLP Session functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "slpsession.h" - -/************************************************************************** - * SLP Session - **************************************************************************/ - -MsnSlpSession * -msn_slp_session_new(MsnSlpCall *slpcall) -{ - MsnSlpSession *slpsession; - - g_return_val_if_fail(slpcall != NULL, NULL); - - slpsession = g_new0(MsnSlpSession, 1); - - slpsession->slpcall = slpcall; - slpsession->id = slpcall->session_id; - slpsession->call_id = slpcall->id; - slpsession->app_id = slpcall->app_id; - - slpcall->slplink->slp_sessions = - g_list_append(slpcall->slplink->slp_sessions, slpsession); - - return slpsession; -} - -void -msn_slp_session_destroy(MsnSlpSession *slpsession) -{ - g_return_if_fail(slpsession != NULL); - - if (slpsession->call_id != NULL) - g_free(slpsession->call_id); - - slpsession->slpcall->slplink->slp_sessions = - g_list_remove(slpsession->slpcall->slplink->slp_sessions, slpsession); - - g_free(slpsession); -} - -#if 0 -static void -msn_slp_session_send_slpmsg(MsnSlpSession *slpsession, MsnSlpMessage *slpmsg) -{ - slpmsg->slpsession = slpsession; - -#if 0 - slpmsg->session_id = slpsession->id; - slpmsg->app_id = slpsession->app_id; -#endif - - msn_slplink_send_slpmsg(slpsession->slpcall->slplink, slpmsg); -} -#endif
--- a/libpurple/protocols/msnp9/slpsession.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,48 +0,0 @@ -/** - * @file slpsession.h SLP Session functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_SLPSESSION_H_ -#define _MSN_SLPSESSION_H_ - -typedef struct _MsnSlpSession MsnSlpSession; - -#include "slpcall.h" -#include "slpsession.h" -#include "slpmsg.h" - -struct _MsnSlpSession -{ - /* MsnSlpLink *slplink; */ - MsnSlpCall *slpcall; - - long id; - - long app_id; - char *call_id; -}; - -MsnSlpSession *msn_slp_session_new(MsnSlpCall *slpcall); -void msn_slp_session_destroy(MsnSlpSession *slpsession); -void msn_slpsession_send_slpmsg(MsnSlpSession *slpsession, - MsnSlpMessage *slpmsg); -#endif /* _MSN_SLPSESSION_H_ */
--- a/libpurple/protocols/msnp9/state.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,132 +0,0 @@ -/** - * @file state.c State functions and definitions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "state.h" - -static const char *away_text[] = -{ - N_("Available"), - N_("Available"), - N_("Busy"), - N_("Idle"), - N_("Be Right Back"), - N_("Away From Computer"), - N_("On The Phone"), - N_("Out To Lunch"), - N_("Available"), - N_("Available") -}; - -void -msn_change_status(MsnSession *session) -{ - PurpleAccount *account; - MsnCmdProc *cmdproc; - MsnUser *user; - MsnObject *msnobj; - const char *state_text; - - g_return_if_fail(session != NULL); - g_return_if_fail(session->notification != NULL); - - account = session->account; - cmdproc = session->notification->cmdproc; - user = session->user; - state_text = msn_state_get_text(msn_state_from_account(account)); - - /* If we're not logged in yet, don't send the status to the server, - * it will be sent when login completes - */ - if (!session->logged_in) - return; - - msnobj = msn_user_get_object(user); - - if (msnobj == NULL) - { - msn_cmdproc_send(cmdproc, "CHG", "%s %d", state_text, - MSN_CLIENT_ID); - } - else - { - char *msnobj_str; - - msnobj_str = msn_object_to_string(msnobj); - - msn_cmdproc_send(cmdproc, "CHG", "%s %d %s", state_text, - MSN_CLIENT_ID, purple_url_encode(msnobj_str)); - - g_free(msnobj_str); - } -} - -const char * -msn_away_get_text(MsnAwayType type) -{ - g_return_val_if_fail(type <= MSN_HIDDEN, NULL); - - return _(away_text[type]); -} - -const char * -msn_state_get_text(MsnAwayType state) -{ - static char *status_text[] = - { "NLN", "NLN", "BSY", "IDL", "BRB", "AWY", "PHN", "LUN", "HDN", "HDN" }; - - return status_text[state]; -} - -MsnAwayType -msn_state_from_account(PurpleAccount *account) -{ - MsnAwayType msnstatus; - PurplePresence *presence; - PurpleStatus *status; - const char *status_id; - - presence = purple_account_get_presence(account); - status = purple_presence_get_active_status(presence); - status_id = purple_status_get_id(status); - - if (!strcmp(status_id, "away")) - msnstatus = MSN_AWAY; - else if (!strcmp(status_id, "brb")) - msnstatus = MSN_BRB; - else if (!strcmp(status_id, "busy")) - msnstatus = MSN_BUSY; - else if (!strcmp(status_id, "phone")) - msnstatus = MSN_PHONE; - else if (!strcmp(status_id, "lunch")) - msnstatus = MSN_LUNCH; - else if (!strcmp(status_id, "invisible")) - msnstatus = MSN_HIDDEN; - else - msnstatus = MSN_ONLINE; - - if ((msnstatus == MSN_ONLINE) && purple_presence_is_idle(presence)) - msnstatus = MSN_IDLE; - - return msnstatus; -}
--- a/libpurple/protocols/msnp9/state.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,64 +0,0 @@ -/** - * @file state.h State functions and definitions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_STATE_H_ -#define _MSN_STATE_H_ - -/** - * Away types. - */ -typedef enum -{ - MSN_ONLINE = 1, - MSN_BUSY = 2, - MSN_IDLE = 3, - MSN_BRB = 4, - MSN_AWAY = 5, - MSN_PHONE = 6, - MSN_LUNCH = 7, - MSN_OFFLINE = 8, - MSN_HIDDEN = 9 - -} MsnAwayType; - -/** - * Changes the status of the user. - * - * @param session The MSN session. - */ -void msn_change_status(MsnSession *session); - -/** - * Returns the string representation of an away type. - * - * @param type The away type. - * - * @return The string representation of the away type. - */ -const char *msn_away_get_text(MsnAwayType type); - -const char *msn_state_get_text(MsnAwayType state); - -MsnAwayType msn_state_from_account(PurpleAccount *account); - -#endif /* _MSN_STATE_H_ */
--- a/libpurple/protocols/msnp9/switchboard.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1305 +0,0 @@ -/** - * @file switchboard.c MSN switchboard functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "prefs.h" -#include "switchboard.h" -#include "notification.h" -#include "msn-utils.h" - -#include "error.h" - -static MsnTable *cbs_table; - -static void msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg, - MsnMsgErrorType error); - -/************************************************************************** - * Main - **************************************************************************/ - -MsnSwitchBoard * -msn_switchboard_new(MsnSession *session) -{ - MsnSwitchBoard *swboard; - MsnServConn *servconn; - - g_return_val_if_fail(session != NULL, NULL); - - swboard = g_new0(MsnSwitchBoard, 1); - - swboard->session = session; - swboard->servconn = servconn = msn_servconn_new(session, MSN_SERVCONN_SB); - swboard->cmdproc = servconn->cmdproc; - - swboard->msg_queue = g_queue_new(); - swboard->empty = TRUE; - - swboard->cmdproc->data = swboard; - swboard->cmdproc->cbs_table = cbs_table; - - session->switches = g_list_append(session->switches, swboard); - - return swboard; -} - -void -msn_switchboard_destroy(MsnSwitchBoard *swboard) -{ - MsnSession *session; - MsnMessage *msg; - GList *l; - -#ifdef MSN_DEBUG_SB - purple_debug_info("msn", "switchboard_destroy: swboard(%p)\n", swboard); -#endif - - g_return_if_fail(swboard != NULL); - - if (swboard->destroying) - return; - - swboard->destroying = TRUE; - - /* If it linked us is because its looking for trouble */ - while (swboard->slplinks != NULL) - msn_slplink_destroy(swboard->slplinks->data); - - /* Destroy the message queue */ - while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL) - { - if (swboard->error != MSN_SB_ERROR_NONE) - { - /* The messages could not be sent due to a switchboard error */ - msg_error_helper(swboard->cmdproc, msg, - MSN_MSG_ERROR_SB); - } - msn_message_unref(msg); - } - - g_queue_free(swboard->msg_queue); - - /* msg_error_helper will both remove the msg from ack_list and - unref it, so we don't need to do either here */ - while ((l = swboard->ack_list) != NULL) - msg_error_helper(swboard->cmdproc, l->data, MSN_MSG_ERROR_SB); - - g_free(swboard->im_user); - g_free(swboard->auth_key); - g_free(swboard->session_id); - - for (l = swboard->users; l != NULL; l = l->next) - g_free(l->data); - - if (swboard->users != NULL) - g_list_free(swboard->users); - - session = swboard->session; - session->switches = g_list_remove(session->switches, swboard); - -#if 0 - /* This should never happen or we are in trouble. */ - if (swboard->servconn != NULL) - msn_servconn_destroy(swboard->servconn); -#endif - - swboard->cmdproc->data = NULL; - - msn_servconn_set_disconnect_cb(swboard->servconn, NULL); - - msn_servconn_destroy(swboard->servconn); - - g_free(swboard); -} - -void -msn_switchboard_set_auth_key(MsnSwitchBoard *swboard, const char *key) -{ - g_return_if_fail(swboard != NULL); - g_return_if_fail(key != NULL); - - swboard->auth_key = g_strdup(key); -} - -const char * -msn_switchboard_get_auth_key(MsnSwitchBoard *swboard) -{ - g_return_val_if_fail(swboard != NULL, NULL); - - return swboard->auth_key; -} - -void -msn_switchboard_set_session_id(MsnSwitchBoard *swboard, const char *id) -{ - g_return_if_fail(swboard != NULL); - g_return_if_fail(id != NULL); - - if (swboard->session_id != NULL) - g_free(swboard->session_id); - - swboard->session_id = g_strdup(id); -} - -const char * -msn_switchboard_get_session_id(MsnSwitchBoard *swboard) -{ - g_return_val_if_fail(swboard != NULL, NULL); - - return swboard->session_id; -} - -int -msn_switchboard_get_chat_id(void) -{ - static int chat_id = 1; - - return chat_id++; -} - -void -msn_switchboard_set_invited(MsnSwitchBoard *swboard, gboolean invited) -{ - g_return_if_fail(swboard != NULL); - - swboard->invited = invited; -} - -gboolean -msn_switchboard_is_invited(MsnSwitchBoard *swboard) -{ - g_return_val_if_fail(swboard != NULL, FALSE); - - return swboard->invited; -} - -/************************************************************************** - * Utility - **************************************************************************/ - -static void -send_clientcaps(MsnSwitchBoard *swboard) -{ - MsnMessage *msg; - - msg = msn_message_new(MSN_MSG_CAPS); - msn_message_set_content_type(msg, "text/x-clientcaps"); - msn_message_set_flag(msg, 'U'); - msn_message_set_bin_data(msg, MSN_CLIENTINFO, strlen(MSN_CLIENTINFO)); - - msn_switchboard_send_msg(swboard, msg, TRUE); - - msn_message_destroy(msg); -} - -static void -msn_switchboard_add_user(MsnSwitchBoard *swboard, const char *user) -{ - MsnCmdProc *cmdproc; - PurpleAccount *account; - - g_return_if_fail(swboard != NULL); - - cmdproc = swboard->cmdproc; - account = cmdproc->session->account; - - swboard->users = g_list_prepend(swboard->users, g_strdup(user)); - swboard->current_users++; - swboard->empty = FALSE; - -#ifdef MSN_DEBUG_CHAT - purple_debug_info("msn", "user=[%s], total=%d\n", user, - swboard->current_users); -#endif - - if (!(swboard->flag & MSN_SB_FLAG_IM) && (swboard->conv != NULL)) - { - /* This is a helper switchboard. */ - purple_debug_error("msn", "switchboard_add_user: conv != NULL\n"); - return; - } - - if ((swboard->conv != NULL) && - (purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT)) - { - purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv), user, NULL, - PURPLE_CBFLAGS_NONE, TRUE); - } - else if (swboard->current_users > 1 || swboard->total_users > 1) - { - if (swboard->conv == NULL || - purple_conversation_get_type(swboard->conv) != PURPLE_CONV_TYPE_CHAT) - { - GList *l; - -#ifdef MSN_DEBUG_CHAT - purple_debug_info("msn", "[chat] Switching to chat.\n"); -#endif - -#if 0 - /* this is bad - it causes msn_switchboard_close to be called on the - * switchboard we're in the middle of using :( */ - if (swboard->conv != NULL) - purple_conversation_destroy(swboard->conv); -#endif - - swboard->chat_id = msn_switchboard_get_chat_id(); - swboard->flag |= MSN_SB_FLAG_IM; - swboard->conv = serv_got_joined_chat(account->gc, - swboard->chat_id, - "MSN Chat"); - - for (l = swboard->users; l != NULL; l = l->next) - { - const char *tmp_user; - - tmp_user = l->data; - -#ifdef MSN_DEBUG_CHAT - purple_debug_info("msn", "[chat] Adding [%s].\n", tmp_user); -#endif - - purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv), - tmp_user, NULL, PURPLE_CBFLAGS_NONE, TRUE); - } - -#ifdef MSN_DEBUG_CHAT - purple_debug_info("msn", "[chat] We add ourselves.\n"); -#endif - - purple_conv_chat_add_user(PURPLE_CONV_CHAT(swboard->conv), - purple_account_get_username(account), - NULL, PURPLE_CBFLAGS_NONE, TRUE); - - g_free(swboard->im_user); - swboard->im_user = NULL; - } - } - else if (swboard->conv == NULL) - { - swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, - user, account); - } - else - { - purple_debug_warning("msn", "switchboard_add_user: This should not happen!\n"); - } -} - -static PurpleConversation * -msn_switchboard_get_conv(MsnSwitchBoard *swboard) -{ - PurpleAccount *account; - - g_return_val_if_fail(swboard != NULL, NULL); - - if (swboard->conv != NULL) - return swboard->conv; - - purple_debug_error("msn", "Switchboard with unassigned conversation\n"); - - account = swboard->session->account; - - return (swboard->conv = purple_conversation_new(PURPLE_CONV_TYPE_IM, - account, swboard->im_user)); -} - -static void -msn_switchboard_report_user(MsnSwitchBoard *swboard, PurpleMessageFlags flags, const char *msg) -{ - PurpleConversation *conv; - - g_return_if_fail(swboard != NULL); - g_return_if_fail(msg != NULL); - - if ((conv = msn_switchboard_get_conv(swboard)) != NULL) - { - purple_conversation_write(conv, NULL, msg, flags, time(NULL)); - } -} - -static void -swboard_error_helper(MsnSwitchBoard *swboard, int reason, const char *passport) -{ - g_return_if_fail(swboard != NULL); - - purple_debug_warning("msg", "Error: Unable to call the user %s for reason %i\n", - passport ? passport : "(null)", reason); - - /* TODO: if current_users > 0, this is probably a chat and an invite failed, - * we should report that in the chat or something */ - if (swboard->current_users == 0) - { - swboard->error = reason; - msn_switchboard_close(swboard); - } -} - -static void -cal_error_helper(MsnTransaction *trans, int reason) -{ - MsnSwitchBoard *swboard; - const char *passport; - char **params; - - params = g_strsplit(trans->params, " ", 0); - - passport = params[0]; - - swboard = trans->data; - - purple_debug_warning("msn", "cal_error_helper: command %s failed for reason %i\n",trans->command,reason); - - swboard_error_helper(swboard, reason, passport); - - g_strfreev(params); -} - -static void -msg_error_helper(MsnCmdProc *cmdproc, MsnMessage *msg, MsnMsgErrorType error) -{ - MsnSwitchBoard *swboard; - - g_return_if_fail(cmdproc != NULL); - g_return_if_fail(msg != NULL); - - if ((error != MSN_MSG_ERROR_SB) && (msg->nak_cb != NULL)) - msg->nak_cb(msg, msg->ack_data); - - swboard = cmdproc->data; - - /* This is not good, and should be fixed somewhere else. */ - g_return_if_fail(swboard != NULL); - - if (msg->type == MSN_MSG_TEXT) - { - const char *format, *str_reason; - char *body_str, *body_enc, *pre, *post; - -#if 0 - if (swboard->conv == NULL) - { - if (msg->ack_ref) - msn_message_unref(msg); - - return; - } -#endif - - if (error == MSN_MSG_ERROR_TIMEOUT) - { - str_reason = _("Message may have not been sent " - "because a timeout occurred:"); - } - else if (error == MSN_MSG_ERROR_SB) - { - switch (swboard->error) - { - case MSN_SB_ERROR_OFFLINE: - str_reason = _("Message could not be sent, " - "not allowed while invisible:"); - break; - case MSN_SB_ERROR_USER_OFFLINE: - str_reason = _("Message could not be sent " - "because the user is offline:"); - break; - case MSN_SB_ERROR_CONNECTION: - str_reason = _("Message could not be sent " - "because a connection error occurred:"); - break; - case MSN_SB_ERROR_TOO_FAST: - str_reason = _("Message could not be sent " - "because we are sending too quickly:"); - break; - case MSN_SB_ERROR_AUTHFAILED: - str_reason = _("Message could not be sent " - "because we were unable to establish a " - "session with the server. This is " - "likely a server problem, try again in " - "a few minutes:"); - break; - default: - str_reason = _("Message could not be sent " - "because an error with " - "the switchboard occurred:"); - break; - } - } - else - { - str_reason = _("Message may have not been sent " - "because an unknown error occurred:"); - } - - body_str = msn_message_to_string(msg); - body_enc = g_markup_escape_text(body_str, -1); - g_free(body_str); - - format = msn_message_get_attr(msg, "X-MMS-IM-Format"); - msn_parse_format(format, &pre, &post); - body_str = g_strdup_printf("%s%s%s", pre ? pre : "", - body_enc ? body_enc : "", post ? post : ""); - g_free(body_enc); - g_free(pre); - g_free(post); - - msn_switchboard_report_user(swboard, PURPLE_MESSAGE_ERROR, - str_reason); - msn_switchboard_report_user(swboard, PURPLE_MESSAGE_RAW, - body_str); - - g_free(body_str); - } - - /* If a timeout occures we will want the msg around just in case we - * receive the ACK after the timeout. */ - if (msg->ack_ref && error != MSN_MSG_ERROR_TIMEOUT) - { - swboard->ack_list = g_list_remove(swboard->ack_list, msg); - msn_message_unref(msg); - } -} - -/************************************************************************** - * Message Stuff - **************************************************************************/ - -/** Called when a message times out. */ -static void -msg_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans) -{ - MsnMessage *msg; - - msg = trans->data; - - msg_error_helper(cmdproc, msg, MSN_MSG_ERROR_TIMEOUT); -} - -/** Called when we receive an error of a message. */ -static void -msg_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error) -{ - msg_error_helper(cmdproc, trans->data, MSN_MSG_ERROR_UNKNOWN); -} - -#if 0 -/** Called when we receive an ack of a special message. */ -static void -msg_ack(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnMessage *msg; - - msg = cmd->trans->data; - - if (msg->ack_cb != NULL) - msg->ack_cb(msg->ack_data); - - msn_message_unref(msg); -} - -/** Called when we receive a nak of a special message. */ -static void -msg_nak(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnMessage *msg; - - msg = cmd->trans->data; - - msn_message_unref(msg); -} -#endif - -static void -release_msg(MsnSwitchBoard *swboard, MsnMessage *msg) -{ - MsnCmdProc *cmdproc; - MsnTransaction *trans; - char *payload; - gsize payload_len; - - g_return_if_fail(swboard != NULL); - g_return_if_fail(msg != NULL); - - cmdproc = swboard->cmdproc; - - payload = msn_message_gen_payload(msg, &payload_len); - -#ifdef MSN_DEBUG_SB - msn_message_show_readable(msg, "SB SEND", FALSE); -#endif - - trans = msn_transaction_new(cmdproc, "MSG", "%c %d", - msn_message_get_flag(msg), payload_len); - - /* Data for callbacks */ - msn_transaction_set_data(trans, msg); - - if (msg->type == MSN_MSG_TEXT) - { - msg->ack_ref = TRUE; - msn_message_ref(msg); - swboard->ack_list = g_list_append(swboard->ack_list, msg); - msn_transaction_set_timeout_cb(trans, msg_timeout); - } - else if (msg->type == MSN_MSG_SLP) - { - msg->ack_ref = TRUE; - msn_message_ref(msg); - swboard->ack_list = g_list_append(swboard->ack_list, msg); - msn_transaction_set_timeout_cb(trans, msg_timeout); -#if 0 - if (msg->ack_cb != NULL) - { - msn_transaction_add_cb(trans, "ACK", msg_ack); - msn_transaction_add_cb(trans, "NAK", msg_nak); - } -#endif - } - - trans->payload = payload; - trans->payload_len = payload_len; - - msg->trans = trans; - - msn_cmdproc_send_trans(cmdproc, trans); -} - -static void -queue_msg(MsnSwitchBoard *swboard, MsnMessage *msg) -{ - g_return_if_fail(swboard != NULL); - g_return_if_fail(msg != NULL); - - purple_debug_info("msn", "Appending message to queue.\n"); - - g_queue_push_tail(swboard->msg_queue, msg); - - msn_message_ref(msg); -} - -static void -process_queue(MsnSwitchBoard *swboard) -{ - MsnMessage *msg; - - g_return_if_fail(swboard != NULL); - - purple_debug_info("msn", "Processing queue\n"); - - while ((msg = g_queue_pop_head(swboard->msg_queue)) != NULL) - { - purple_debug_info("msn", "Sending message\n"); - release_msg(swboard, msg); - msn_message_unref(msg); - } -} - -gboolean -msn_switchboard_can_send(MsnSwitchBoard *swboard) -{ - g_return_val_if_fail(swboard != NULL, FALSE); - - if (swboard->empty || !g_queue_is_empty(swboard->msg_queue)) - return FALSE; - - return TRUE; -} - -void -msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg, - gboolean queue) -{ - g_return_if_fail(swboard != NULL); - g_return_if_fail(msg != NULL); - - if (msn_switchboard_can_send(swboard)) - release_msg(swboard, msg); - else if (queue) - queue_msg(swboard, msg); -} - -/************************************************************************** - * Switchboard Commands - **************************************************************************/ - -static void -ans_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSwitchBoard *swboard; - - swboard = cmdproc->data; - swboard->ready = TRUE; -} - -static void -bye_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSwitchBoard *swboard; - const char *user; - - swboard = cmdproc->data; - user = cmd->params[0]; - - /* cmdproc->data is set to NULL when the switchboard is destroyed; - * we may get a bye shortly thereafter. */ - g_return_if_fail(swboard != NULL); - - if (!(swboard->flag & MSN_SB_FLAG_IM) && (swboard->conv != NULL)) - purple_debug_error("msn_switchboard", "bye_cmd: helper bug\n"); - - if (swboard->conv == NULL) - { - /* This is a helper switchboard */ - msn_switchboard_destroy(swboard); - } - else if ((swboard->current_users > 1) || - (purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT)) - { - /* This is a switchboard used for a chat */ - purple_conv_chat_remove_user(PURPLE_CONV_CHAT(swboard->conv), user, NULL); - swboard->current_users--; - if (swboard->current_users == 0) - msn_switchboard_destroy(swboard); - } - else - { - /* This is a switchboard used for a im session */ - msn_switchboard_destroy(swboard); - } -} - -static void -iro_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - PurpleAccount *account; - PurpleConnection *gc; - MsnSwitchBoard *swboard; - - account = cmdproc->session->account; - gc = account->gc; - swboard = cmdproc->data; - - swboard->total_users = atoi(cmd->params[2]); - - msn_switchboard_add_user(swboard, cmd->params[3]); -} - -static void -joi_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session; - PurpleAccount *account; - PurpleConnection *gc; - MsnSwitchBoard *swboard; - const char *passport; - - passport = cmd->params[0]; - - session = cmdproc->session; - account = session->account; - gc = account->gc; - swboard = cmdproc->data; - - msn_switchboard_add_user(swboard, passport); - - process_queue(swboard); - - if (!session->http_method) - send_clientcaps(swboard); - - if (swboard->closed) - msn_switchboard_close(swboard); -} - -static void -msg_cmd_post(MsnCmdProc *cmdproc, MsnCommand *cmd, char *payload, size_t len) -{ - MsnMessage *msg; - - msg = msn_message_new_from_cmd(cmdproc->session, cmd); - - msn_message_parse_payload(msg, payload, len); -#ifdef MSN_DEBUG_SB - msn_message_show_readable(msg, "SB RECV", FALSE); -#endif - - if (msg->remote_user != NULL) - g_free (msg->remote_user); - - msg->remote_user = g_strdup(cmd->params[0]); - msn_cmdproc_process_msg(cmdproc, msg); - - msn_message_destroy(msg); -} - -static void -msg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - cmdproc->servconn->payload_len = atoi(cmd->params[2]); - cmdproc->last_cmd->payload_cb = msg_cmd_post; -} - -static void -nak_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnMessage *msg; - - msg = cmd->trans->data; - g_return_if_fail(msg != NULL); - - msg_error_helper(cmdproc, msg, MSN_MSG_ERROR_NAK); -} - -static void -ack_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSwitchBoard *swboard; - MsnMessage *msg; - - msg = cmd->trans->data; - - if (msg->ack_cb != NULL) - msg->ack_cb(msg, msg->ack_data); - - swboard = cmdproc->data; - if (swboard) - swboard->ack_list = g_list_remove(swboard->ack_list, msg); - msn_message_unref(msg); -} - -static void -out_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - PurpleConnection *gc; - MsnSwitchBoard *swboard; - - gc = cmdproc->session->account->gc; - swboard = cmdproc->data; - - if (swboard->current_users > 1) - serv_got_chat_left(gc, swboard->chat_id); - - msn_switchboard_disconnect(swboard); -} - -static void -usr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSwitchBoard *swboard; - - swboard = cmdproc->data; - -#if 0 - GList *l; - - for (l = swboard->users; l != NULL; l = l->next) - { - const char *user; - user = l->data; - - msn_cmdproc_send(cmdproc, "CAL", "%s", user); - } -#endif - - swboard->ready = TRUE; - msn_cmdproc_process_queue(cmdproc); -} - -/************************************************************************** - * Message Handlers - **************************************************************************/ -static void -plain_msg(MsnCmdProc *cmdproc, MsnMessage *msg) -{ - PurpleConnection *gc; - MsnSwitchBoard *swboard; - const char *body; - char *body_str; - char *body_enc; - char *body_final; - size_t body_len, new_len; - const char *passport; - const char *value; - - gc = cmdproc->session->account->gc; - swboard = cmdproc->data; - - body = msn_message_get_bin_data(msg, &body_len); - body_str = sanitize_utf(body, body_len, &new_len); - body_enc = g_markup_escape_text(body_str, -1); - g_free(body_str); - - passport = msg->remote_user; - - if (!strcmp(passport, "messenger@microsoft.com") && - strstr(body, "immediate security update")) - { - return; - } - -#if 0 - if ((value = msn_message_get_attr(msg, "User-Agent")) != NULL) - { - purple_debug_misc("msn", "User-Agent = '%s'\n", value); - } -#endif - - if ((value = msn_message_get_attr(msg, "X-MMS-IM-Format")) != NULL) - { - char *pre, *post; - - msn_parse_format(value, &pre, &post); - - body_final = g_strdup_printf("%s%s%s", pre ? pre : "", - body_enc ? body_enc : "", post ? post : ""); - - g_free(pre); - g_free(post); - g_free(body_enc); - } - else - { - body_final = body_enc; - } - - swboard->flag |= MSN_SB_FLAG_IM; - - if (swboard->current_users > 1 || - ((swboard->conv != NULL) && - purple_conversation_get_type(swboard->conv) == PURPLE_CONV_TYPE_CHAT)) - { - /* If current_users is always ok as it should then there is no need to - * check if this is a chat. */ - if (swboard->current_users <= 1) - purple_debug_misc("msn", "plain_msg: current_users(%d)\n", - swboard->current_users); - - serv_got_chat_in(gc, swboard->chat_id, passport, 0, body_final, - time(NULL)); - if (swboard->conv == NULL) - { - swboard->conv = purple_find_chat(gc, swboard->chat_id); - swboard->flag |= MSN_SB_FLAG_IM; - } - } - else - { - serv_got_im(gc, passport, body_final, 0, time(NULL)); - if (swboard->conv == NULL) - { - swboard->conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, - passport, purple_connection_get_account(gc)); - swboard->flag |= MSN_SB_FLAG_IM; - } - } - - g_free(body_final); -} - -static void -control_msg(MsnCmdProc *cmdproc, MsnMessage *msg) -{ - PurpleConnection *gc; - MsnSwitchBoard *swboard; - char *passport; - - gc = cmdproc->session->account->gc; - swboard = cmdproc->data; - passport = msg->remote_user; - - if (swboard->current_users == 1 && - msn_message_get_attr(msg, "TypingUser") != NULL) - { - serv_got_typing(gc, passport, MSN_TYPING_RECV_TIMEOUT, - PURPLE_TYPING); - } -} - -static void -clientcaps_msg(MsnCmdProc *cmdproc, MsnMessage *msg) -{ -#if 0 - MsnSession *session; - MsnSwitchBoard *swboard; - MsnUser *user; - GHashTable *clientcaps; - const char *value; - - char *passport = msg->sender; - - session = cmdproc->session; - swboard = cmdproc->servconn->swboard; - - clientcaps = msn_message_get_hashtable_from_body(msg); -#endif -} - -static void -nudge_msg(MsnCmdProc *cmdproc, MsnMessage *msg) -{ - MsnSwitchBoard *swboard; - PurpleAccount *account; - const char *user; - - swboard = cmdproc->data; - account = cmdproc->session->account; - user = msg->remote_user; - - serv_got_attention(account->gc, user, MSN_NUDGE); -} - -/************************************************************************** - * Connect stuff - **************************************************************************/ -static void -ans_usr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error); - -static void -connect_cb(MsnServConn *servconn) -{ - MsnSwitchBoard *swboard; - MsnTransaction *trans; - MsnCmdProc *cmdproc; - PurpleAccount *account; - - cmdproc = servconn->cmdproc; - g_return_if_fail(cmdproc != NULL); - - account = cmdproc->session->account; - swboard = cmdproc->data; - g_return_if_fail(swboard != NULL); - - if (msn_switchboard_is_invited(swboard)) - { - swboard->empty = FALSE; - - trans = msn_transaction_new(cmdproc, "ANS", "%s %s %s", - purple_account_get_username(account), - swboard->auth_key, swboard->session_id); - } - else - { - trans = msn_transaction_new(cmdproc, "USR", "%s %s", - purple_account_get_username(account), - swboard->auth_key); - } - - msn_transaction_set_error_cb(trans, ans_usr_error); - msn_transaction_set_data(trans, swboard); - msn_cmdproc_send_trans(cmdproc, trans); -} - -static void -ans_usr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error) -{ - MsnSwitchBoard *swboard; - char **params; - char *passport; - int reason = MSN_SB_ERROR_UNKNOWN; - - if (error == 911) - { - reason = MSN_SB_ERROR_AUTHFAILED; - } - - purple_debug_warning("msn", "ans_usr_error: command %s gave error %i\n", trans->command, error); - - params = g_strsplit(trans->params, " ", 0); - passport = params[0]; - swboard = trans->data; - - swboard_error_helper(swboard, reason, passport); - - g_strfreev(params); -} - -static void -disconnect_cb(MsnServConn *servconn) -{ - MsnSwitchBoard *swboard; - - swboard = servconn->cmdproc->data; - g_return_if_fail(swboard != NULL); - - msn_servconn_set_disconnect_cb(swboard->servconn, NULL); - - msn_switchboard_destroy(swboard); -} - -gboolean -msn_switchboard_connect(MsnSwitchBoard *swboard, const char *host, int port) -{ - g_return_val_if_fail(swboard != NULL, FALSE); - - msn_servconn_set_connect_cb(swboard->servconn, connect_cb); - msn_servconn_set_disconnect_cb(swboard->servconn, disconnect_cb); - - return msn_servconn_connect(swboard->servconn, host, port); -} - -void -msn_switchboard_disconnect(MsnSwitchBoard *swboard) -{ - g_return_if_fail(swboard != NULL); - - msn_servconn_disconnect(swboard->servconn); -} - -/************************************************************************** - * Call stuff - **************************************************************************/ -static void -got_cal(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ -#if 0 - MsnSwitchBoard *swboard; - const char *user; - - swboard = cmdproc->data; - - user = cmd->params[0]; - - msn_switchboard_add_user(swboard, user); -#endif -} - -static void -cal_timeout(MsnCmdProc *cmdproc, MsnTransaction *trans) -{ - purple_debug_warning("msn", "cal_timeout: command %s timed out\n", trans->command); - - cal_error_helper(trans, MSN_SB_ERROR_UNKNOWN); -} - -static void -cal_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error) -{ - int reason = MSN_SB_ERROR_UNKNOWN; - - if (error == 215) - { - purple_debug_info("msn", "Invited user already in switchboard\n"); - return; - } - else if (error == 217) - { - reason = MSN_SB_ERROR_USER_OFFLINE; - } - - purple_debug_warning("msn", "cal_error: command %s gave error %i\n", trans->command, error); - - cal_error_helper(trans, reason); -} - -void -msn_switchboard_request_add_user(MsnSwitchBoard *swboard, const char *user) -{ - MsnTransaction *trans; - MsnCmdProc *cmdproc; - - g_return_if_fail(swboard != NULL); - - cmdproc = swboard->cmdproc; - - trans = msn_transaction_new(cmdproc, "CAL", "%s", user); - /* this doesn't do anything, but users seem to think that - * 'Unhandled command' is some kind of error, so we don't report it */ - msn_transaction_add_cb(trans, "CAL", got_cal); - - msn_transaction_set_data(trans, swboard); - msn_transaction_set_timeout_cb(trans, cal_timeout); - - if (swboard->ready) - msn_cmdproc_send_trans(cmdproc, trans); - else - msn_cmdproc_queue_trans(cmdproc, trans); -} - -/************************************************************************** - * Create & Transfer stuff - **************************************************************************/ - -static void -got_swboard(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSwitchBoard *swboard; - char *host; - int port; - swboard = cmd->trans->data; - - if (g_list_find(cmdproc->session->switches, swboard) == NULL) - /* The conversation window was closed. */ - return; - - msn_switchboard_set_auth_key(swboard, cmd->params[4]); - - msn_parse_socket(cmd->params[2], &host, &port); - - if (!msn_switchboard_connect(swboard, host, port)) - msn_switchboard_destroy(swboard); - - g_free(host); -} - -static void -xfr_error(MsnCmdProc *cmdproc, MsnTransaction *trans, int error) -{ - MsnSwitchBoard *swboard; - int reason = MSN_SB_ERROR_UNKNOWN; - - if (error == 913) - reason = MSN_SB_ERROR_OFFLINE; - else if (error == 800) - reason = MSN_SB_ERROR_TOO_FAST; - - swboard = trans->data; - - purple_debug_info("msn", "xfr_error %i for %s: trans %p, command %s, reason %i\n", - error, (swboard->im_user ? swboard->im_user : "(null)"), trans, - (trans->command ? trans->command : "(null)"), reason); - - swboard_error_helper(swboard, reason, swboard->im_user); -} - -void -msn_switchboard_request(MsnSwitchBoard *swboard) -{ - MsnCmdProc *cmdproc; - MsnTransaction *trans; - - g_return_if_fail(swboard != NULL); - - cmdproc = swboard->session->notification->cmdproc; - - trans = msn_transaction_new(cmdproc, "XFR", "%s", "SB"); - msn_transaction_add_cb(trans, "XFR", got_swboard); - - msn_transaction_set_data(trans, swboard); - msn_transaction_set_error_cb(trans, xfr_error); - - msn_cmdproc_send_trans(cmdproc, trans); -} - -void -msn_switchboard_close(MsnSwitchBoard *swboard) -{ - g_return_if_fail(swboard != NULL); - - if (swboard->error != MSN_SB_ERROR_NONE) - { - msn_switchboard_destroy(swboard); - } - else if (g_queue_is_empty(swboard->msg_queue) || - !swboard->session->connected) - { - MsnCmdProc *cmdproc; - cmdproc = swboard->cmdproc; - msn_cmdproc_send_quick(cmdproc, "OUT", NULL, NULL); - - msn_switchboard_destroy(swboard); - } - else - { - swboard->closed = TRUE; - } -} - -gboolean -msn_switchboard_release(MsnSwitchBoard *swboard, MsnSBFlag flag) -{ - g_return_val_if_fail(swboard != NULL, FALSE); - - swboard->flag &= ~flag; - - if (flag == MSN_SB_FLAG_IM) - /* Forget any conversation that used to be associated with this - * swboard. */ - swboard->conv = NULL; - - if (swboard->flag == 0) - { - msn_switchboard_close(swboard); - return TRUE; - } - - return FALSE; -} - -/************************************************************************** - * Init stuff - **************************************************************************/ - -void -msn_switchboard_init(void) -{ - cbs_table = msn_table_new(); - - msn_table_add_cmd(cbs_table, "ANS", "ANS", ans_cmd); - msn_table_add_cmd(cbs_table, "ANS", "IRO", iro_cmd); - - msn_table_add_cmd(cbs_table, "MSG", "ACK", ack_cmd); - msn_table_add_cmd(cbs_table, "MSG", "NAK", nak_cmd); - - msn_table_add_cmd(cbs_table, "USR", "USR", usr_cmd); - - msn_table_add_cmd(cbs_table, NULL, "MSG", msg_cmd); - msn_table_add_cmd(cbs_table, NULL, "JOI", joi_cmd); - msn_table_add_cmd(cbs_table, NULL, "BYE", bye_cmd); - msn_table_add_cmd(cbs_table, NULL, "OUT", out_cmd); - -#if 0 - /* They might skip the history */ - msn_table_add_cmd(cbs_table, NULL, "ACK", NULL); -#endif - - msn_table_add_error(cbs_table, "MSG", msg_error); - msn_table_add_error(cbs_table, "CAL", cal_error); - - /* Register the message type callbacks. */ - msn_table_add_msg_type(cbs_table, "text/plain", - plain_msg); - msn_table_add_msg_type(cbs_table, "text/x-msmsgscontrol", - control_msg); - msn_table_add_msg_type(cbs_table, "text/x-clientcaps", - clientcaps_msg); - msn_table_add_msg_type(cbs_table, "text/x-clientinfo", - clientcaps_msg); - msn_table_add_msg_type(cbs_table, "application/x-msnmsgrp2p", - msn_p2p_msg); - msn_table_add_msg_type(cbs_table, "text/x-mms-emoticon", - msn_emoticon_msg); - msn_table_add_msg_type(cbs_table, "text/x-mms-animemoticon", - msn_emoticon_msg); - msn_table_add_msg_type(cbs_table, "text/x-msnmsgr-datacast", - nudge_msg); -#if 0 - msn_table_add_msg_type(cbs_table, "text/x-msmmsginvite", - msn_invite_msg); -#endif -} - -void -msn_switchboard_end(void) -{ - msn_table_destroy(cbs_table); -}
--- a/libpurple/protocols/msnp9/switchboard.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,284 +0,0 @@ -/** - * @file switchboard.h MSN switchboard functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_SWITCHBOARD_H_ -#define _MSN_SWITCHBOARD_H_ - -typedef struct _MsnSwitchBoard MsnSwitchBoard; - -#include "conversation.h" - -#include "msg.h" -#include "user.h" - -#include "servconn.h" - -#include "slplink.h" - -/** - * A switchboard error. - */ -typedef enum -{ - MSN_SB_ERROR_NONE, /**< No error. */ - MSN_SB_ERROR_CAL, /**< The user could not join (answer the call). */ - MSN_SB_ERROR_OFFLINE, /**< The account is offline. */ - MSN_SB_ERROR_USER_OFFLINE, /**< The user to call is offline. */ - MSN_SB_ERROR_CONNECTION, /**< There was a connection error. */ - MSN_SB_ERROR_TOO_FAST, /**< We are sending too fast */ - MSN_SB_ERROR_AUTHFAILED, /**< Authentication failed joining the switchboard session */ - MSN_SB_ERROR_UNKNOWN /**< An unknown error occurred. */ - -} MsnSBErrorType; - -/** - * A switchboard flag. - */ -typedef enum -{ - MSN_SB_FLAG_IM = 0x01, /**< This switchboard is being used for a conversation. */ - MSN_SB_FLAG_FT = 0x02, /**< This switchboard is being used for file transfer. */ - -} MsnSBFlag; - -/** - * A switchboard. - * - * A place where a bunch of users send messages to the rest of the users. - */ -struct _MsnSwitchBoard -{ - MsnSession *session; - MsnServConn *servconn; - MsnCmdProc *cmdproc; - char *im_user; - - MsnSBFlag flag; - char *auth_key; - char *session_id; - - PurpleConversation *conv; /**< The conversation that displays the - messages of this switchboard, or @c NULL if - this is a helper switchboard. */ - - gboolean empty; /**< A flag that states if the swithcboard has no - users in it. */ - gboolean invited; /**< A flag that states if we were invited to the - switchboard. */ - gboolean ready; /**< A flag that states if this switchboard is - ready to be used. */ - gboolean closed; /**< A flag that states if the switchboard has - been closed by the user. */ - gboolean destroying; /**< A flag that states if the switchboard is - alredy on the process of destruction. */ - - int current_users; - int total_users; - GList *users; - - int chat_id; - - GQueue *msg_queue; /**< Queue of messages to send. */ - GList *ack_list; /**< List of messages waiting for an ack. */ - - MsnSBErrorType error; /**< The error that occurred in this switchboard - (if applicable). */ - GList *slplinks; /**< The list of slplinks that are using this switchboard. */ -}; - -/** - * Initialize the variables for switchboard creation. - */ -void msn_switchboard_init(void); - -/** - * Destroy the variables for switchboard creation. - */ -void msn_switchboard_end(void); - -/** - * Creates a new switchboard. - * - * @param session The MSN session. - * - * @return The new switchboard. - */ -MsnSwitchBoard *msn_switchboard_new(MsnSession *session); - -/** - * Destroys a switchboard. - * - * @param swboard The switchboard to destroy. - */ -void msn_switchboard_destroy(MsnSwitchBoard *swboard); - -/** - * Sets the auth key the switchboard must use when connecting. - * - * @param swboard The switchboard. - * @param key The auth key. - */ -void msn_switchboard_set_auth_key(MsnSwitchBoard *swboard, const char *key); - -/** - * Returns the auth key the switchboard must use when connecting. - * - * @param swboard The switchboard. - * - * @return The auth key. - */ -const char *msn_switchboard_get_auth_key(MsnSwitchBoard *swboard); - -/** - * Sets the session ID the switchboard must use when connecting. - * - * @param swboard The switchboard. - * @param id The session ID. - */ -void msn_switchboard_set_session_id(MsnSwitchBoard *swboard, const char *id); - -/** - * Returns the session ID the switchboard must use when connecting. - * - * @param swboard The switchboard. - * - * @return The session ID. - */ -const char *msn_switchboard_get_session_id(MsnSwitchBoard *swboard); - -/** - * Returns the next chat ID for use by a switchboard. - * - * @return The chat ID. - */ -int msn_switchboard_get_chat_id(void); - -/** - * Sets whether or not we were invited to this switchboard. - * - * @param swboard The switchboard. - * @param invite @c TRUE if invited, @c FALSE otherwise. - */ -void msn_switchboard_set_invited(MsnSwitchBoard *swboard, gboolean invited); - -/** - * Returns whether or not we were invited to this switchboard. - * - * @param swboard The switchboard. - * - * @return @c TRUE if invited, @c FALSE otherwise. - */ -gboolean msn_switchboard_is_invited(MsnSwitchBoard *swboard); - -/** - * Connects to a switchboard. - * - * @param swboard The switchboard. - * @param host The switchboard server host. - * @param port The switcbharod server port. - * - * @return @c TRUE if able to connect, or @c FALSE otherwise. - */ -gboolean msn_switchboard_connect(MsnSwitchBoard *swboard, - const char *host, int port); - -/** - * Disconnects from a switchboard. - * - * @param swboard The switchboard to disconnect from. - */ -void msn_switchboard_disconnect(MsnSwitchBoard *swboard); - -/** - * Closes the switchboard. - * - * Called when a conversation is closed. - * - * @param swboard The switchboard to close. - */ -void msn_switchboard_close(MsnSwitchBoard *swboard); - -/** - * Release a switchboard from a certain function. - * - * @param swboard The switchboard to release. - * @param flag The flag that states the function. - * - * @return @c TRUE if the switchboard was closed, @c FALSE otherwise. - */ -gboolean msn_switchboard_release(MsnSwitchBoard *swboard, MsnSBFlag flag); - -/** - * Returns whether or not we currently can send a message through this - * switchboard. - * - * @param swboard The switchboard. - * - * @return @c TRUE if a message can be sent, @c FALSE otherwise. - */ -gboolean msn_switchboard_can_send(MsnSwitchBoard *swboard); - -/** - * Sends a message through this switchboard. - * - * @param swboard The switchboard. - * @param msg The message. - * @param queue A flag that states if we want this message to be queued (in - * the case it cannot currently be sent). - * - * @return @c TRUE if a message can be sent, @c FALSE otherwise. - */ -void msn_switchboard_send_msg(MsnSwitchBoard *swboard, MsnMessage *msg, - gboolean queue); - -gboolean msn_switchboard_chat_leave(MsnSwitchBoard *swboard); -gboolean msn_switchboard_chat_invite(MsnSwitchBoard *swboard, const char *who); - -void msn_switchboard_request(MsnSwitchBoard *swboard); -void msn_switchboard_request_add_user(MsnSwitchBoard *swboard, const char *user); - -/** - * Processes peer to peer messages. - * - * @param cmdproc The command processor. - * @param msg The message. - */ -void msn_p2p_msg(MsnCmdProc *cmdproc, MsnMessage *msg); - -/** - * Processes emoticon messages. - * - * @param cmdproc The command processor. - * @param msg The message. - */ -void msn_emoticon_msg(MsnCmdProc *cmdproc, MsnMessage *msg); - -/** - * Processes INVITE messages. - * - * @param cmdproc The command processor. - * @param msg The message. - */ -void msn_invite_msg(MsnCmdProc *cmdproc, MsnMessage *msg); - -#endif /* _MSN_SWITCHBOARD_H_ */
--- a/libpurple/protocols/msnp9/sync.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,260 +0,0 @@ -/** - * @file sync.c MSN list synchronization functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "sync.h" -#include "state.h" - -static MsnTable *cbs_table; - -static void -blp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - PurpleConnection *gc = cmdproc->session->account->gc; - const char *list_name; - - list_name = cmd->params[0]; - - if (!g_ascii_strcasecmp(list_name, "AL")) - { - /* - * If the current setting is AL, messages from users who - * are not in BL will be delivered. - * - * In other words, deny some. - */ - gc->account->perm_deny = PURPLE_PRIVACY_DENY_USERS; - } - else - { - /* If the current setting is BL, only messages from people - * who are in the AL will be delivered. - * - * In other words, permit some. - */ - gc->account->perm_deny = PURPLE_PRIVACY_ALLOW_USERS; - } -} - -static void -prp_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session = cmdproc->session; - const char *type, *value; - - type = cmd->params[0]; - value = cmd->params[1]; - - if (cmd->param_count == 2) - { - if (!strcmp(type, "PHH")) - msn_user_set_home_phone(session->user, purple_url_decode(value)); - else if (!strcmp(type, "PHW")) - msn_user_set_work_phone(session->user, purple_url_decode(value)); - else if (!strcmp(type, "PHM")) - msn_user_set_mobile_phone(session->user, purple_url_decode(value)); - } - else - { - if (!strcmp(type, "PHH")) - msn_user_set_home_phone(session->user, NULL); - else if (!strcmp(type, "PHW")) - msn_user_set_work_phone(session->user, NULL); - else if (!strcmp(type, "PHM")) - msn_user_set_mobile_phone(session->user, NULL); - } -} - -static void -lsg_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session = cmdproc->session; - const char *name; - int group_id; - - group_id = atoi(cmd->params[0]); - name = purple_url_decode(cmd->params[1]); - - msn_group_new(session->userlist, group_id, name); - - /* HACK */ - if (group_id == 0) - { - /* Group of ungroupped buddies */ - if (session->sync->total_users == 0) - { - cmdproc->cbs_table = session->sync->old_cbs_table; - - msn_session_finish_login(session); - - msn_sync_destroy(session->sync); - session->sync = NULL; - } - return; - } - - if ((purple_find_group(name)) == NULL) - { - PurpleGroup *g = purple_group_new(name); - purple_blist_add_group(g, NULL); - } -} - -static void -lst_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSession *session = cmdproc->session; - char *passport = NULL; - const char *friend = NULL; - int list_op; - MsnUser *user; - - passport = cmd->params[0]; - friend = purple_url_decode(cmd->params[1]); - list_op = atoi(cmd->params[2]); - - user = msn_user_new(session->userlist, passport, friend); - - msn_userlist_add_user(session->userlist, user); - - session->sync->last_user = user; - - /* TODO: This can be improved */ - - if (list_op & MSN_LIST_FL_OP) - { - char **c; - char **tokens; - const char *group_nums; - GSList *group_ids; - - group_nums = cmd->params[3]; - - group_ids = NULL; - - tokens = g_strsplit(group_nums, ",", -1); - - for (c = tokens; *c != NULL; c++) - { - int id; - - id = atoi(*c); - group_ids = g_slist_append(group_ids, GINT_TO_POINTER(id)); - } - - g_strfreev(tokens); - - msn_got_lst_user(session, user, list_op, group_ids); - - g_slist_free(group_ids); - } - else - { - msn_got_lst_user(session, user, list_op, NULL); - } - - session->sync->num_users++; - - if (session->sync->num_users == session->sync->total_users) - { - cmdproc->cbs_table = session->sync->old_cbs_table; - - msn_session_finish_login(session); - - msn_sync_destroy(session->sync); - session->sync = NULL; - } -} - -static void -bpr_cmd(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ - MsnSync *sync = cmdproc->session->sync; - const char *type, *value; - MsnUser *user; - - user = sync->last_user; - - g_return_if_fail(user != NULL); - - type = cmd->params[0]; - value = cmd->params[1]; - - if (value) - { - if (!strcmp(type, "MOB")) - { - if (!strcmp(value, "Y")) - user->mobile = TRUE; - else if (!strcmp(value, "N")) - user->mobile = FALSE; - } - else if (!strcmp(type, "PHH")) - msn_user_set_home_phone(user, purple_url_decode(value)); - else if (!strcmp(type, "PHW")) - msn_user_set_work_phone(user, purple_url_decode(value)); - else if (!strcmp(type, "PHM")) - msn_user_set_mobile_phone(user, purple_url_decode(value)); - } -} - -void -msn_sync_init(void) -{ - /* TODO: check prp, blp, bpr */ - - cbs_table = msn_table_new(); - - /* Syncing */ - msn_table_add_cmd(cbs_table, NULL, "GTC", NULL); - msn_table_add_cmd(cbs_table, NULL, "BLP", blp_cmd); - msn_table_add_cmd(cbs_table, NULL, "PRP", prp_cmd); - msn_table_add_cmd(cbs_table, NULL, "LSG", lsg_cmd); - msn_table_add_cmd(cbs_table, NULL, "LST", lst_cmd); - msn_table_add_cmd(cbs_table, NULL, "BPR", bpr_cmd); -} - -void -msn_sync_end(void) -{ - msn_table_destroy(cbs_table); -} - -MsnSync * -msn_sync_new(MsnSession *session) -{ - MsnSync *sync; - - sync = g_new0(MsnSync, 1); - - sync->session = session; - sync->cbs_table = cbs_table; - - return sync; -} - -void -msn_sync_destroy(MsnSync *sync) -{ - g_free(sync); -}
--- a/libpurple/protocols/msnp9/sync.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -/** - * @file sync.h MSN list synchronization functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_SYNC_H_ -#define _MSN_SYNC_H_ - -typedef struct _MsnSync MsnSync; - -#include "session.h" -#include "table.h" -#include "user.h" - -struct _MsnSync -{ - MsnSession *session; - MsnTable *cbs_table; - MsnTable *old_cbs_table; - - int num_users; - int total_users; - int num_groups; - int total_groups; - MsnUser *last_user; -}; - -void msn_sync_init(void); -void msn_sync_end(void); - -MsnSync * msn_sync_new(MsnSession *session); -void msn_sync_destroy(MsnSync *sync); - -#endif /* _MSN_SYNC_H_ */
--- a/libpurple/protocols/msnp9/table.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,132 +0,0 @@ -/** - * @file table.c MSN helper structure - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "table.h" - -static void -null_cmd_cb(MsnCmdProc *cmdproc, MsnCommand *cmd) -{ -} - -static void -null_error_cb(MsnCmdProc *cmdproc, MsnTransaction *trans, int error) -{ -} - -MsnTable * -msn_table_new() -{ - MsnTable *table; - - table = g_new0(MsnTable, 1); - - table->cmds = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, (GDestroyNotify)g_hash_table_destroy); - table->msgs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); - table->errors = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); - - table->async = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); - table->fallback = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); - - return table; -} - -void -msn_table_destroy(MsnTable *table) -{ - g_return_if_fail(table != NULL); - - g_hash_table_destroy(table->cmds); - g_hash_table_destroy(table->msgs); - g_hash_table_destroy(table->errors); - - g_hash_table_destroy(table->async); - g_hash_table_destroy(table->fallback); - - g_free(table); -} - -void -msn_table_add_cmd(MsnTable *table, - char *command, char *answer, MsnTransCb cb) -{ - GHashTable *cbs; - - g_return_if_fail(table != NULL); - g_return_if_fail(answer != NULL); - - cbs = NULL; - - if (command == NULL) - { - cbs = table->async; - } - else if (strcmp(command, "fallback") == 0) - { - cbs = table->fallback; - } - else - { - cbs = g_hash_table_lookup(table->cmds, command); - - if (cbs == NULL) - { - cbs = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL); - g_hash_table_insert(table->cmds, command, cbs); - } - } - - if (cb == NULL) - cb = null_cmd_cb; - - g_hash_table_insert(cbs, answer, cb); -} - -void -msn_table_add_error(MsnTable *table, - char *answer, MsnErrorCb cb) -{ - g_return_if_fail(table != NULL); - g_return_if_fail(answer != NULL); - - if (cb == NULL) - cb = null_error_cb; - - g_hash_table_insert(table->errors, answer, cb); -} - -void -msn_table_add_msg_type(MsnTable *table, - char *type, MsnMsgTypeCb cb) -{ - g_return_if_fail(table != NULL); - g_return_if_fail(type != NULL); - g_return_if_fail(cb != NULL); - -#if 0 - if (cb == NULL) - cb = null_msg_cb; -#endif - - g_hash_table_insert(table->msgs, type, cb); -}
--- a/libpurple/protocols/msnp9/table.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,53 +0,0 @@ -/** - * @file table.h MSN helper structure - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_TABLE_H_ -#define _MSN_TABLE_H_ - -typedef struct _MsnTable MsnTable; - -#include "cmdproc.h" -#include "transaction.h" -#include "msg.h" - -typedef void (*MsnMsgTypeCb)(MsnCmdProc *cmdproc, MsnMessage *msg); - -struct _MsnTable -{ - GHashTable *cmds; - GHashTable *msgs; - GHashTable *errors; - - GHashTable *async; - GHashTable *fallback; -}; - -MsnTable *msn_table_new(void); -void msn_table_destroy(MsnTable *table); - -void msn_table_add_cmd(MsnTable *table, char *command, char *answer, - MsnTransCb cb); -void msn_table_add_error(MsnTable *table, char *answer, MsnErrorCb cb); -void msn_table_add_msg_type(MsnTable *table, char *type, MsnMsgTypeCb cb); - -#endif /* _MSN_TABLE_H_ */
--- a/libpurple/protocols/msnp9/transaction.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,221 +0,0 @@ -/** - * @file transaction.c MSN transaction functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "transaction.h" - -MsnTransaction * -msn_transaction_new(MsnCmdProc *cmdproc, const char *command, - const char *format, ...) -{ - MsnTransaction *trans; - va_list arg; - - g_return_val_if_fail(command != NULL, NULL); - - trans = g_new0(MsnTransaction, 1); - - trans->cmdproc = cmdproc; - trans->command = g_strdup(command); - - if (format != NULL) - { - va_start(arg, format); - trans->params = g_strdup_vprintf(format, arg); - va_end(arg); - } - - /* trans->queue = g_queue_new(); */ - - return trans; -} - -void -msn_transaction_destroy(MsnTransaction *trans) -{ - g_return_if_fail(trans != NULL); - - g_free(trans->command); - g_free(trans->params); - g_free(trans->payload); - -#if 0 - if (trans->pendent_cmd != NULL) - msn_message_unref(trans->pendent_msg); -#endif - -#if 0 - MsnTransaction *elem; - if (trans->queue != NULL) - { - while ((elem = g_queue_pop_head(trans->queue)) != NULL) - msn_transaction_destroy(elem); - - g_queue_free(trans->queue); - } -#endif - - if (trans->callbacks != NULL && trans->has_custom_callbacks) - g_hash_table_destroy(trans->callbacks); - - if (trans->timer) - purple_timeout_remove(trans->timer); - - g_free(trans); -} - -char * -msn_transaction_to_string(MsnTransaction *trans) -{ - char *str; - - g_return_val_if_fail(trans != NULL, FALSE); - - if (trans->params != NULL) - str = g_strdup_printf("%s %u %s\r\n", trans->command, trans->trId, trans->params); - else - str = g_strdup_printf("%s %u\r\n", trans->command, trans->trId); - - return str; -} - -void -msn_transaction_queue_cmd(MsnTransaction *trans, MsnCommand *cmd) -{ - purple_debug_info("msn", "queueing command.\n"); - trans->pendent_cmd = cmd; - msn_command_ref(cmd); -} - -void -msn_transaction_unqueue_cmd(MsnTransaction *trans, MsnCmdProc *cmdproc) -{ - MsnCommand *cmd; - - if (!cmdproc->servconn->connected) - return; - - purple_debug_info("msn", "unqueueing command.\n"); - cmd = trans->pendent_cmd; - - g_return_if_fail(cmd != NULL); - - msn_cmdproc_process_cmd(cmdproc, cmd); - msn_command_unref(cmd); - - trans->pendent_cmd = NULL; -} - -#if 0 -void -msn_transaction_queue(MsnTransaction *trans, MsnTransaction *elem) -{ - if (trans->queue == NULL) - trans->queue = g_queue_new(); - - g_queue_push_tail(trans->queue, elem); -} - -void -msn_transaction_unqueue(MsnTransaction *trans, MsnCmdProc *cmdproc) -{ - MsnTransaction *elem; - - while ((elem = g_queue_pop_head(trans->queue)) != NULL) - msn_cmdproc_send_trans(cmdproc, elem); -} -#endif - -void -msn_transaction_set_payload(MsnTransaction *trans, - const char *payload, int payload_len) -{ - g_return_if_fail(trans != NULL); - g_return_if_fail(payload != NULL); - - trans->payload = g_strdup(payload); - trans->payload_len = payload_len ? payload_len : strlen(trans->payload); -} - -void -msn_transaction_set_data(MsnTransaction *trans, void *data) -{ - g_return_if_fail(trans != NULL); - - trans->data = data; -} - -void -msn_transaction_add_cb(MsnTransaction *trans, char *answer, - MsnTransCb cb) -{ - g_return_if_fail(trans != NULL); - g_return_if_fail(answer != NULL); - - if (trans->callbacks == NULL) - { - trans->has_custom_callbacks = TRUE; - trans->callbacks = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, - NULL); - } - else if (trans->has_custom_callbacks != TRUE) - g_return_if_reached (); - - g_hash_table_insert(trans->callbacks, answer, cb); -} - -static gboolean -transaction_timeout(gpointer data) -{ - MsnTransaction *trans; - - trans = data; - g_return_val_if_fail(trans != NULL, FALSE); - -#if 0 - purple_debug_info("msn", "timed out: %s %d %s\n", trans->command, trans->trId, trans->params); -#endif - - if (trans->timeout_cb != NULL) - trans->timeout_cb(trans->cmdproc, trans); - - return FALSE; -} - -void -msn_transaction_set_timeout_cb(MsnTransaction *trans, MsnTimeoutCb cb) -{ - if (trans->timer) - { - purple_debug_error("msn", "This shouldn't be happening\n"); - purple_timeout_remove(trans->timer); - } - trans->timeout_cb = cb; - trans->timer = purple_timeout_add_seconds(60, transaction_timeout, trans); -} - -void -msn_transaction_set_error_cb(MsnTransaction *trans, MsnErrorCb cb) -{ - trans->error_cb = cb; -}
--- a/libpurple/protocols/msnp9/transaction.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,80 +0,0 @@ -/** - * @file transaction.h MSN transaction functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_TRANSACTION_H -#define _MSN_TRANSACTION_H - -typedef struct _MsnTransaction MsnTransaction; - -#include "command.h" -#include "cmdproc.h" - -typedef void (*MsnTransCb)(MsnCmdProc *cmdproc, MsnCommand *cmd); -typedef void (*MsnTimeoutCb)(MsnCmdProc *cmdproc, MsnTransaction *trans); -typedef void (*MsnErrorCb)(MsnCmdProc *cmdproc, MsnTransaction *trans, - int error); - -/** - * A transaction. A sending command that will initiate the transaction. - */ -struct _MsnTransaction -{ - MsnCmdProc *cmdproc; - unsigned int trId; - - char *command; - char *params; - - int timer; - - void *data; /**< The data to be used on the different callbacks. */ - GHashTable *callbacks; - gboolean has_custom_callbacks; - MsnErrorCb error_cb; - MsnTimeoutCb timeout_cb; - - char *payload; - size_t payload_len; - - GQueue *queue; - MsnCommand *pendent_cmd; /**< The command that is waiting for the result of - this transaction. */ -}; - -MsnTransaction *msn_transaction_new(MsnCmdProc *cmdproc, - const char *command, - const char *format, ...); -void msn_transaction_destroy(MsnTransaction *trans); - -char *msn_transaction_to_string(MsnTransaction *trans); -void msn_transaction_queue_cmd(MsnTransaction *trans, MsnCommand *cmd); -void msn_transaction_unqueue_cmd(MsnTransaction *trans, MsnCmdProc *cmdproc); -void msn_transaction_set_payload(MsnTransaction *trans, - const char *payload, int payload_len); -void msn_transaction_set_data(MsnTransaction *trans, void *data); -void msn_transaction_add_cb(MsnTransaction *trans, char *answer, - MsnTransCb cb); -void msn_transaction_set_error_cb(MsnTransaction *trans, MsnErrorCb cb); -void msn_transaction_set_timeout_cb(MsnTransaction *trans, MsnTimeoutCb cb); - -#endif /* _MSN_TRANSACTION_H */
--- a/libpurple/protocols/msnp9/user.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,332 +0,0 @@ -/** - * @file user.c User functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "user.h" -#include "slp.h" - -MsnUser * -msn_user_new(MsnUserList *userlist, const char *passport, - const char *friendly_name) -{ - MsnUser *user; - - user = g_new0(MsnUser, 1); - - user->userlist = userlist; - - msn_user_set_passport(user, passport); - if (friendly_name != NULL) - msn_user_set_friendly_name(user, friendly_name); - - return user; -} - -void -msn_user_destroy(MsnUser *user) -{ - g_return_if_fail(user != NULL); - - if (user->clientcaps != NULL) - g_hash_table_destroy(user->clientcaps); - - if (user->group_ids != NULL) - g_list_free(user->group_ids); - - if (user->msnobj != NULL) - msn_object_destroy(user->msnobj); - - g_free(user->passport); - g_free(user->friendly_name); - g_free(user->phone.home); - g_free(user->phone.work); - g_free(user->phone.mobile); - - g_free(user); -} - -void -msn_user_update(MsnUser *user) -{ - PurpleAccount *account; - - account = user->userlist->session->account; - - if (user->status != NULL) { - if (!strcmp(user->status, "offline") && user->mobile) { - purple_prpl_got_user_status(account, user->passport, "offline", NULL); - purple_prpl_got_user_status(account, user->passport, "mobile", NULL); - } else { - purple_prpl_got_user_status(account, user->passport, user->status, NULL); - purple_prpl_got_user_status_deactive(account, user->passport, "mobile"); - } - } - - if (user->idle) - purple_prpl_got_user_idle(account, user->passport, TRUE, -1); - else - purple_prpl_got_user_idle(account, user->passport, FALSE, 0); -} - -void -msn_user_set_state(MsnUser *user, const char *state) -{ - const char *status; - - if (!g_ascii_strcasecmp(state, "BSY")) - status = "busy"; - else if (!g_ascii_strcasecmp(state, "BRB")) - status = "brb"; - else if (!g_ascii_strcasecmp(state, "AWY")) - status = "away"; - else if (!g_ascii_strcasecmp(state, "PHN")) - status = "phone"; - else if (!g_ascii_strcasecmp(state, "LUN")) - status = "lunch"; - else - status = "available"; - - if (!g_ascii_strcasecmp(state, "IDL")) - user->idle = TRUE; - else - user->idle = FALSE; - - user->status = status; -} - -void -msn_user_set_passport(MsnUser *user, const char *passport) -{ - g_return_if_fail(user != NULL); - - g_free(user->passport); - user->passport = g_strdup(passport); -} - -void -msn_user_set_friendly_name(MsnUser *user, const char *name) -{ - MsnCmdProc *cmdproc; - MsnSession *session; - const char *encoded; - - g_return_if_fail(user != NULL); - - encoded = purple_url_encode(name); - session = user->userlist->session; - - if (user->friendly_name && strcmp(user->friendly_name, name) - && (strlen(encoded) < 387) && session->passport_info.verified && - (user->list_op & MSN_LIST_FL_OP)) { - /* copy the new name to the server list, but only when new */ - /* should we check this more thoroughly? */ - cmdproc = session->notification->cmdproc; - msn_cmdproc_send(cmdproc, "REA", "%s %s", - user->passport, - encoded); - } - - g_free(user->friendly_name); - user->friendly_name = g_strdup(name); -} - -void -msn_user_set_buddy_icon(MsnUser *user, PurpleStoredImage *img) -{ - MsnObject *msnobj = NULL; - - g_return_if_fail(user != NULL); - - msnobj = msn_object_new_from_image(img, "TFR2C2.tmp", - user->passport, MSN_OBJECT_USERTILE); - - if(!msnobj) - purple_debug_error("msn", "Unable to open buddy icon from %s!\n", user->passport); - - msn_user_set_object(user, msnobj); -} - -void -msn_user_add_group_id(MsnUser *user, int id) -{ - MsnUserList *userlist; - PurpleAccount *account; - PurpleBuddy *b; - PurpleGroup *g; - const char *passport; - const char *group_name; - - g_return_if_fail(user != NULL); - g_return_if_fail(id >= 0); - - user->group_ids = g_list_append(user->group_ids, GINT_TO_POINTER(id)); - - userlist = user->userlist; - account = userlist->session->account; - passport = msn_user_get_passport(user); - - group_name = msn_userlist_find_group_name(userlist, id); - - g = purple_find_group(group_name); - - if ((id == 0) && (g == NULL)) - { - g = purple_group_new(group_name); - purple_blist_add_group(g, NULL); - } - - b = purple_find_buddy_in_group(account, passport, g); - - if (b == NULL) - { - b = purple_buddy_new(account, passport, NULL); - - purple_blist_add_buddy(b, NULL, g, NULL); - } - - b->proto_data = user; -} - -void -msn_user_remove_group_id(MsnUser *user, int id) -{ - g_return_if_fail(user != NULL); - g_return_if_fail(id >= 0); - - user->group_ids = g_list_remove(user->group_ids, GINT_TO_POINTER(id)); -} - -void -msn_user_set_home_phone(MsnUser *user, const char *number) -{ - g_return_if_fail(user != NULL); - - if (user->phone.home != NULL) - g_free(user->phone.home); - - user->phone.home = (number == NULL ? NULL : g_strdup(number)); -} - -void -msn_user_set_work_phone(MsnUser *user, const char *number) -{ - g_return_if_fail(user != NULL); - - if (user->phone.work != NULL) - g_free(user->phone.work); - - user->phone.work = (number == NULL ? NULL : g_strdup(number)); -} - -void -msn_user_set_mobile_phone(MsnUser *user, const char *number) -{ - g_return_if_fail(user != NULL); - - if (user->phone.mobile != NULL) - g_free(user->phone.mobile); - - user->phone.mobile = (number == NULL ? NULL : g_strdup(number)); -} - -void -msn_user_set_object(MsnUser *user, MsnObject *obj) -{ - g_return_if_fail(user != NULL); - - if (user->msnobj != NULL) - msn_object_destroy(user->msnobj); - - user->msnobj = obj; - - if (user->list_op & MSN_LIST_FL_OP) - msn_queue_buddy_icon_request(user); -} - -void -msn_user_set_client_caps(MsnUser *user, GHashTable *info) -{ - g_return_if_fail(user != NULL); - g_return_if_fail(info != NULL); - - if (user->clientcaps != NULL) - g_hash_table_destroy(user->clientcaps); - - user->clientcaps = info; -} - -const char * -msn_user_get_passport(const MsnUser *user) -{ - g_return_val_if_fail(user != NULL, NULL); - - return user->passport; -} - -const char * -msn_user_get_friendly_name(const MsnUser *user) -{ - g_return_val_if_fail(user != NULL, NULL); - - return user->friendly_name; -} - -const char * -msn_user_get_home_phone(const MsnUser *user) -{ - g_return_val_if_fail(user != NULL, NULL); - - return user->phone.home; -} - -const char * -msn_user_get_work_phone(const MsnUser *user) -{ - g_return_val_if_fail(user != NULL, NULL); - - return user->phone.work; -} - -const char * -msn_user_get_mobile_phone(const MsnUser *user) -{ - g_return_val_if_fail(user != NULL, NULL); - - return user->phone.mobile; -} - -MsnObject * -msn_user_get_object(const MsnUser *user) -{ - g_return_val_if_fail(user != NULL, NULL); - - return user->msnobj; -} - -GHashTable * -msn_user_get_client_caps(const MsnUser *user) -{ - g_return_val_if_fail(user != NULL, NULL); - - return user->clientcaps; -}
--- a/libpurple/protocols/msnp9/user.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,266 +0,0 @@ -/** - * @file user.h User functions - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_USER_H_ -#define _MSN_USER_H_ - -typedef struct _MsnUser MsnUser; - -#include "session.h" -#include "object.h" - -#include "userlist.h" - -/** - * A user. - */ -struct _MsnUser -{ -#if 0 - MsnSession *session; /**< The MSN session. */ -#endif - MsnUserList *userlist; - - char *passport; /**< The passport account. */ - char *friendly_name; /**< The friendly name. */ - - const char *status; /**< The state of the user. */ - gboolean idle; /**< The idle state of the user. */ - - struct - { - char *home; /**< Home phone number. */ - char *work; /**< Work phone number. */ - char *mobile; /**< Mobile phone number. */ - - } phone; - - gboolean authorized; /**< Authorized to add this user. */ - gboolean mobile; /**< Signed up with MSN Mobile. */ - - GList *group_ids; /**< The group IDs. */ - - MsnObject *msnobj; /**< The user's MSN Object. */ - - GHashTable *clientcaps; /**< The client's capabilities. */ - - int list_op; -}; - -/**************************************************************************/ -/** @name User API */ -/**************************************************************************/ -/*@{*/ - -/** - * Creates a new user structure. - * - * @param session The MSN session. - * @param passport The initial passport. - * @param stored_name The initial stored name. - * - * @return A new user structure. - */ -MsnUser *msn_user_new(MsnUserList *userlist, const char *passport, - const char *friendly_name); - -/** - * Destroys a user structure. - * - * @param user The user to destroy. - */ -void msn_user_destroy(MsnUser *user); - - -/** - * Updates the user. - * - * Communicates with the core to update the ui, etc. - * - * @param user The user to update. - */ -void msn_user_update(MsnUser *user); - -/** - * Sets the new state of user. - * - * @param user The user. - * @param state The state string. - */ -void msn_user_set_state(MsnUser *user, const char *state); - -/** - * Sets the passport account for a user. - * - * @param user The user. - * @param passport The passport account. - */ -void msn_user_set_passport(MsnUser *user, const char *passport); - -/** - * Sets the friendly name for a user. - * - * @param user The user. - * @param name The friendly name. - */ -void msn_user_set_friendly_name(MsnUser *user, const char *name); - -/** - * Sets the buddy icon for a local user. - * - * @param user The user. - * @param img The buddy icon image - */ -void msn_user_set_buddy_icon(MsnUser *user, PurpleStoredImage *img); - -/** - * Sets the group ID list for a user. - * - * @param user The user. - * @param ids The group ID list. - */ -void msn_user_set_group_ids(MsnUser *user, GList *ids); - -/** - * Adds the group ID for a user. - * - * @param user The user. - * @param id The group ID. - */ -void msn_user_add_group_id(MsnUser *user, int id); - -/** - * Removes the group ID from a user. - * - * @param user The user. - * @param id The group ID. - */ -void msn_user_remove_group_id(MsnUser *user, int id); - -/** - * Sets the home phone number for a user. - * - * @param user The user. - * @param number The home phone number. - */ -void msn_user_set_home_phone(MsnUser *user, const char *number); - -/** - * Sets the work phone number for a user. - * - * @param user The user. - * @param number The work phone number. - */ -void msn_user_set_work_phone(MsnUser *user, const char *number); - -/** - * Sets the mobile phone number for a user. - * - * @param user The user. - * @param number The mobile phone number. - */ -void msn_user_set_mobile_phone(MsnUser *user, const char *number); - -/** - * Sets the MSNObject for a user. - * - * @param user The user. - * @param obj The MSNObject. - */ -void msn_user_set_object(MsnUser *user, MsnObject *obj); - -/** - * Sets the client information for a user. - * - * @param user The user. - * @param info The client information. - */ -void msn_user_set_client_caps(MsnUser *user, GHashTable *info); - - -/** - * Returns the passport account for a user. - * - * @param user The user. - * - * @return The passport account. - */ -const char *msn_user_get_passport(const MsnUser *user); - -/** - * Returns the friendly name for a user. - * - * @param user The user. - * - * @return The friendly name. - */ -const char *msn_user_get_friendly_name(const MsnUser *user); - -/** - * Returns the home phone number for a user. - * - * @param user The user. - * - * @return The user's home phone number. - */ -const char *msn_user_get_home_phone(const MsnUser *user); - -/** - * Returns the work phone number for a user. - * - * @param user The user. - * - * @return The user's work phone number. - */ -const char *msn_user_get_work_phone(const MsnUser *user); - -/** - * Returns the mobile phone number for a user. - * - * @param user The user. - * - * @return The user's mobile phone number. - */ -const char *msn_user_get_mobile_phone(const MsnUser *user); - -/** - * Returns the MSNObject for a user. - * - * @param user The user. - * - * @return The MSNObject. - */ -MsnObject *msn_user_get_object(const MsnUser *user); - -/** - * Returns the client information for a user. - * - * @param user The user. - * - * @return The client information. - */ -GHashTable *msn_user_get_client_caps(const MsnUser *user); - -/*@}*/ - -#endif /* _MSN_USER_H_ */
--- a/libpurple/protocols/msnp9/userlist.c Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,709 +0,0 @@ -/** - * @file userlist.c MSN user list support - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include "msn.h" -#include "userlist.h" - -const char *lists[] = { "FL", "AL", "BL", "RL" }; - -typedef struct -{ - PurpleConnection *gc; - char *who; - char *friendly; - -} MsnPermitAdd; - -/************************************************************************** - * Callbacks - **************************************************************************/ -static void -msn_accept_add_cb(gpointer data) -{ - MsnPermitAdd *pa = data; - MsnSession *session; - MsnUserList *userlist; - - if (PURPLE_CONNECTION_IS_VALID(pa->gc)) { - session = pa->gc->proto_data; - userlist = session->userlist; - - msn_userlist_add_buddy(userlist, pa->who, MSN_LIST_AL, NULL); - } - - g_free(pa->who); - g_free(pa->friendly); - g_free(pa); -} - -static void -msn_cancel_add_cb(gpointer data) -{ - MsnPermitAdd *pa = data; - MsnSession *session; - MsnUserList *userlist; - - if (PURPLE_CONNECTION_IS_VALID(pa->gc)) { - session = pa->gc->proto_data; - userlist = session->userlist; - - msn_userlist_add_buddy(userlist, pa->who, MSN_LIST_BL, NULL); - } - - g_free(pa->who); - g_free(pa->friendly); - g_free(pa); -} - -static void -got_new_entry(PurpleConnection *gc, const char *passport, const char *friendly) -{ - MsnPermitAdd *pa; - - pa = g_new0(MsnPermitAdd, 1); - pa->who = g_strdup(passport); - pa->friendly = g_strdup(friendly); - pa->gc = gc; - - purple_account_request_authorization(purple_connection_get_account(gc), passport, NULL, friendly, NULL, - purple_find_buddy(purple_connection_get_account(gc), passport) != NULL, - msn_accept_add_cb, msn_cancel_add_cb, pa); -} - -/************************************************************************** - * Utility functions - **************************************************************************/ - -static gboolean -user_is_in_group(MsnUser *user, int group_id) -{ - if (user == NULL) - return FALSE; - - if (group_id < 0) - return FALSE; - - if (g_list_find(user->group_ids, GINT_TO_POINTER(group_id))) - return TRUE; - - return FALSE; -} - -static gboolean -user_is_there(MsnUser *user, int list_id, int group_id) -{ - int list_op; - - if (user == NULL) - return FALSE; - - list_op = 1 << list_id; - - if (!(user->list_op & list_op)) - return FALSE; - - if (list_id == MSN_LIST_FL) - { - if (group_id >= 0) - return user_is_in_group(user, group_id); - } - - return TRUE; -} - -static const char* -get_friendly_name(MsnUser *user) -{ - const char *friendly_name; - - g_return_val_if_fail(user != NULL, NULL); - - friendly_name = msn_user_get_friendly_name(user); - - if (friendly_name != NULL) - friendly_name = purple_url_encode(friendly_name); - else - friendly_name = msn_user_get_passport(user); - - /* this might be a bit of a hack, but it should prevent notification server - * disconnections for people who have buddies with insane friendly names - * who added you to their buddy list from being disconnected. Stu. */ - /* Shx: What? Isn't the store_name obtained from the server, and hence it's - * below the BUDDY_ALIAS_MAXLEN ? */ - /* Stu: yeah, that's why it's a bit of a hack, as you pointed out, we're - * probably decoding the incoming store_name wrong, or something. bleh. */ - - if (strlen(friendly_name) > BUDDY_ALIAS_MAXLEN) - friendly_name = msn_user_get_passport(user); - - return friendly_name; -} - -static void -msn_request_add_group(MsnUserList *userlist, const char *who, - const char *old_group_name, const char *new_group_name) -{ - MsnCmdProc *cmdproc; - MsnTransaction *trans; - MsnMoveBuddy *data; - - cmdproc = userlist->session->notification->cmdproc; - data = g_new0(MsnMoveBuddy, 1); - - data->who = g_strdup(who); - - if (old_group_name) - data->old_group_name = g_strdup(old_group_name); - - trans = msn_transaction_new(cmdproc, "ADG", "%s %d", - purple_url_encode(new_group_name), - 0); - - msn_transaction_set_data(trans, data); - - msn_cmdproc_send_trans(cmdproc, trans); -} - -/************************************************************************** - * Server functions - **************************************************************************/ - -MsnListId -msn_get_list_id(const char *list) -{ - if (list[0] == 'F') - return MSN_LIST_FL; - else if (list[0] == 'A') - return MSN_LIST_AL; - else if (list[0] == 'B') - return MSN_LIST_BL; - else if (list[0] == 'R') - return MSN_LIST_RL; - - return -1; -} - -void -msn_got_add_user(MsnSession *session, MsnUser *user, - MsnListId list_id, int group_id) -{ - PurpleAccount *account; - const char *passport; - const char *friendly; - - account = session->account; - - passport = msn_user_get_passport(user); - friendly = msn_user_get_friendly_name(user); - - if (list_id == MSN_LIST_FL) - { - PurpleConnection *gc; - - gc = purple_account_get_connection(account); - - serv_got_alias(gc, passport, friendly); - - if (group_id >= 0) - { - msn_user_add_group_id(user, group_id); - } - else - { - /* session->sync->fl_users_count++; */ - } - } - else if (list_id == MSN_LIST_AL) - { - purple_privacy_permit_add(account, passport, TRUE); - } - else if (list_id == MSN_LIST_BL) - { - purple_privacy_deny_add(account, passport, TRUE); - } - else if (list_id == MSN_LIST_RL) - { - PurpleConnection *gc; - PurpleConversation *convo; - - gc = purple_account_get_connection(account); - - purple_debug_info("msn", - "%s has added you to his or her buddy list.\n", - passport); - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, passport, account); - if (convo) { - PurpleBuddy *buddy; - char *msg; - - buddy = purple_find_buddy(account, passport); - msg = g_strdup_printf( - _("%s has added you to his or her buddy list."), - buddy ? purple_buddy_get_contact_alias(buddy) : passport); - purple_conv_im_write(PURPLE_CONV_IM(convo), passport, msg, - PURPLE_MESSAGE_SYSTEM, time(NULL)); - g_free(msg); - } - - if (!(user->list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP))) - { - /* - * TODO: The friendly name was NULL for me when I - * looked at this. Maybe we should use the store - * name instead? --KingAnt - */ - got_new_entry(gc, passport, friendly); - } - } - - user->list_op |= (1 << list_id); - /* purple_user_add_list_id (user, list_id); */ -} - -void -msn_got_rem_user(MsnSession *session, MsnUser *user, - MsnListId list_id, int group_id) -{ - PurpleAccount *account; - const char *passport; - - account = session->account; - - passport = msn_user_get_passport(user); - - if (list_id == MSN_LIST_FL) - { - /* TODO: When is the user totally removed? */ - if (group_id >= 0) - { - msn_user_remove_group_id(user, group_id); - return; - } - else - { - /* session->sync->fl_users_count--; */ - } - } - else if (list_id == MSN_LIST_AL) - { - purple_privacy_permit_remove(account, passport, TRUE); - } - else if (list_id == MSN_LIST_BL) - { - purple_privacy_deny_remove(account, passport, TRUE); - } - else if (list_id == MSN_LIST_RL) - { - PurpleConversation *convo; - - purple_debug_info("msn", - "%s has removed you from his or her buddy list.\n", - passport); - - convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, passport, account); - if (convo) { - PurpleBuddy *buddy; - char *msg; - - buddy = purple_find_buddy(account, passport); - msg = g_strdup_printf( - _("%s has removed you from his or her buddy list."), - buddy ? purple_buddy_get_contact_alias(buddy) : passport); - purple_conv_im_write(PURPLE_CONV_IM(convo), passport, msg, - PURPLE_MESSAGE_SYSTEM, time(NULL)); - g_free(msg); - } - } - - user->list_op &= ~(1 << list_id); - /* purple_user_remove_list_id (user, list_id); */ - - if (user->list_op == 0) - { - purple_debug_info("msn", "Buddy '%s' shall be deleted?.\n", - passport); - - } -} - -void -msn_got_lst_user(MsnSession *session, MsnUser *user, - int list_op, GSList *group_ids) -{ - PurpleConnection *gc; - PurpleAccount *account; - const char *passport; - const char *store; - - account = session->account; - gc = purple_account_get_connection(account); - - passport = msn_user_get_passport(user); - store = msn_user_get_friendly_name(user); - - if (list_op & MSN_LIST_AL_OP) - { - /* These are users who are allowed to see our status. */ - purple_privacy_deny_remove(account, passport, TRUE); - purple_privacy_permit_add(account, passport, TRUE); - } - - if (list_op & MSN_LIST_BL_OP) - { - /* These are users who are not allowed to see our status. */ - purple_privacy_permit_remove(account, passport, TRUE); - purple_privacy_deny_add(account, passport, TRUE); - } - - if (list_op & MSN_LIST_FL_OP) - { - GSList *c; - for (c = group_ids; c != NULL; c = g_slist_next(c)) - { - int group_id; - group_id = GPOINTER_TO_INT(c->data); - msn_user_add_group_id(user, group_id); - } - - /* FIXME: It might be a real alias */ - /* Umm, what? This might fix bug #1385130 */ - serv_got_alias(gc, passport, store); - } - - if (list_op & MSN_LIST_RL_OP) - { - /* These are users who have us on their buddy list. */ - /* - * TODO: What is store name set to when this happens? - * For one of my accounts "something@hotmail.com" - * the store name was "something." Maybe we - * should use the friendly name, instead? --KingAnt - */ - - if (!(list_op & (MSN_LIST_AL_OP | MSN_LIST_BL_OP))) - { - got_new_entry(gc, passport, store); - } - } - - user->list_op = list_op; -} - -/************************************************************************** - * UserList functions - **************************************************************************/ - -MsnUserList* -msn_userlist_new(MsnSession *session) -{ - MsnUserList *userlist; - - userlist = g_new0(MsnUserList, 1); - - userlist->session = session; - userlist->buddy_icon_requests = g_queue_new(); - - /* buddy_icon_window is the number of allowed simultaneous buddy icon requests. - * XXX With smarter rate limiting code, we could allow more at once... 5 was the limit set when - * we weren't retrieiving any more than 5 per MSN session. */ - userlist->buddy_icon_window = 1; - - return userlist; -} - -void -msn_userlist_destroy(MsnUserList *userlist) -{ - GList *l; - - for (l = userlist->users; l != NULL; l = l->next) - { - msn_user_destroy(l->data); - } - - g_list_free(userlist->users); - - for (l = userlist->groups; l != NULL; l = l->next) - { - msn_group_destroy(l->data); - } - - g_list_free(userlist->groups); - - g_queue_free(userlist->buddy_icon_requests); - - if (userlist->buddy_icon_request_timer) - purple_timeout_remove(userlist->buddy_icon_request_timer); - - g_free(userlist); -} - -void -msn_userlist_add_user(MsnUserList *userlist, MsnUser *user) -{ - userlist->users = g_list_prepend(userlist->users, user); -} - -void -msn_userlist_remove_user(MsnUserList *userlist, MsnUser *user) -{ - userlist->users = g_list_remove(userlist->users, user); -} - -MsnUser * -msn_userlist_find_user(MsnUserList *userlist, const char *passport) -{ - GList *l; - - g_return_val_if_fail(passport != NULL, NULL); - - for (l = userlist->users; l != NULL; l = l->next) - { - MsnUser *user = (MsnUser *)l->data; - - g_return_val_if_fail(user->passport != NULL, NULL); - - if (!strcmp(passport, user->passport)) - return user; - } - - return NULL; -} - -void -msn_userlist_add_group(MsnUserList *userlist, MsnGroup *group) -{ - userlist->groups = g_list_append(userlist->groups, group); -} - -void -msn_userlist_remove_group(MsnUserList *userlist, MsnGroup *group) -{ - userlist->groups = g_list_remove(userlist->groups, group); -} - -MsnGroup * -msn_userlist_find_group_with_id(MsnUserList *userlist, int id) -{ - GList *l; - - g_return_val_if_fail(userlist != NULL, NULL); - g_return_val_if_fail(id >= 0, NULL); - - for (l = userlist->groups; l != NULL; l = l->next) - { - MsnGroup *group = l->data; - - if (group->id == id) - return group; - } - - return NULL; -} - -MsnGroup * -msn_userlist_find_group_with_name(MsnUserList *userlist, const char *name) -{ - GList *l; - - g_return_val_if_fail(userlist != NULL, NULL); - g_return_val_if_fail(name != NULL, NULL); - - for (l = userlist->groups; l != NULL; l = l->next) - { - MsnGroup *group = l->data; - - if ((group->name != NULL) && !g_ascii_strcasecmp(name, group->name)) - return group; - } - - return NULL; -} - -int -msn_userlist_find_group_id(MsnUserList *userlist, const char *group_name) -{ - MsnGroup *group; - - group = msn_userlist_find_group_with_name(userlist, group_name); - - if (group != NULL) - return msn_group_get_id(group); - else - return -1; -} - -const char * -msn_userlist_find_group_name(MsnUserList *userlist, int group_id) -{ - MsnGroup *group; - - group = msn_userlist_find_group_with_id(userlist, group_id); - - if (group != NULL) - return msn_group_get_name(group); - else - return NULL; -} - -void -msn_userlist_rename_group_id(MsnUserList *userlist, int group_id, - const char *new_name) -{ - MsnGroup *group; - - group = msn_userlist_find_group_with_id(userlist, group_id); - - if (group != NULL) - msn_group_set_name(group, new_name); -} - -void -msn_userlist_remove_group_id(MsnUserList *userlist, int group_id) -{ - MsnGroup *group; - - group = msn_userlist_find_group_with_id(userlist, group_id); - - if (group != NULL) - { - msn_userlist_remove_group(userlist, group); - msn_group_destroy(group); - } -} - -void -msn_userlist_rem_buddy(MsnUserList *userlist, - const char *who, int list_id, const char *group_name) -{ - MsnUser *user; - int group_id; - const char *list; - - user = msn_userlist_find_user(userlist, who); - group_id = -1; - - if (group_name != NULL) - { - group_id = msn_userlist_find_group_id(userlist, group_name); - - if (group_id < 0) - { - /* Whoa, there is no such group. */ - purple_debug_error("msn", "Group doesn't exist: %s\n", group_name); - return; - } - } - - /* First we're going to check if not there. */ - if (!(user_is_there(user, list_id, group_id))) - { - list = lists[list_id]; - purple_debug_error("msn", "User '%s' is not there: %s\n", - who, list); - return; - } - - /* Then request the rem to the server. */ - list = lists[list_id]; - - msn_notification_rem_buddy(userlist->session->notification, list, who, group_id); -} - -void -msn_userlist_add_buddy(MsnUserList *userlist, - const char *who, int list_id, - const char *group_name) -{ - MsnUser *user; - int group_id; - const char *list; - const char *friendly_name; - - group_id = -1; - - if (!purple_email_is_valid(who)) - { - /* only notify the user about problems adding to the friends list - * maybe we should do something else for other lists, but it probably - * won't cause too many problems if we just ignore it */ - if (list_id == MSN_LIST_FL) - { - char *str = g_strdup_printf(_("Unable to add \"%s\"."), who); - purple_notify_error(NULL, NULL, str, - _("The username specified is invalid.")); - g_free(str); - } - - return; - } - - if (group_name != NULL) - { - group_id = msn_userlist_find_group_id(userlist, group_name); - - if (group_id < 0) - { - /* Whoa, we must add that group first. */ - msn_request_add_group(userlist, who, NULL, group_name); - return; - } - } - - user = msn_userlist_find_user(userlist, who); - - /* First we're going to check if it's already there. */ - if (user_is_there(user, list_id, group_id)) - { - list = lists[list_id]; - purple_debug_error("msn", "User '%s' is already there: %s\n", who, list); - return; - } - - friendly_name = (user != NULL) ? get_friendly_name(user) : who; - - /* Then request the add to the server. */ - list = lists[list_id]; - - msn_notification_add_buddy(userlist->session->notification, list, who, - friendly_name, group_id); -} - -void -msn_userlist_move_buddy(MsnUserList *userlist, const char *who, - const char *old_group_name, const char *new_group_name) -{ - int new_group_id; - - new_group_id = msn_userlist_find_group_id(userlist, new_group_name); - - if (new_group_id < 0) - { - msn_request_add_group(userlist, who, old_group_name, new_group_name); - return; - } - - msn_userlist_add_buddy(userlist, who, MSN_LIST_FL, new_group_name); - msn_userlist_rem_buddy(userlist, who, MSN_LIST_FL, old_group_name); -}
--- a/libpurple/protocols/msnp9/userlist.h Thu Mar 04 15:19:39 2010 +0900 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,103 +0,0 @@ -/** - * @file userlist.h MSN user list support - * - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#ifndef _MSN_USERLIST_H_ -#define _MSN_USERLIST_H_ - -typedef struct _MsnUserList MsnUserList; - -#include "cmdproc.h" -#include "user.h" -#include "group.h" - -typedef enum -{ - MSN_LIST_FL, - MSN_LIST_AL, - MSN_LIST_BL, - MSN_LIST_RL - -} MsnListId; - -typedef struct -{ - char *who; - char *old_group_name; - -} MsnMoveBuddy; - -struct _MsnUserList -{ - MsnSession *session; - - /* MsnUsers *users; */ - /* MsnGroups *groups; */ - - GList *users; - GList *groups; - - GQueue *buddy_icon_requests; - int buddy_icon_window; - guint buddy_icon_request_timer; - - int fl_users_count; - -}; - -MsnListId msn_get_list_id(const char *list); - -void msn_got_add_user(MsnSession *session, MsnUser *user, - MsnListId list_id, int group_id); -void msn_got_rem_user(MsnSession *session, MsnUser *user, - MsnListId list_id, int group_id); -void msn_got_lst_user(MsnSession *session, MsnUser *user, - int list_op, GSList *group_ids); - -MsnUserList *msn_userlist_new(MsnSession *session); -void msn_userlist_destroy(MsnUserList *userlist); -void msn_userlist_add_user(MsnUserList *userlist, MsnUser *user); -void msn_userlist_remove_user(MsnUserList *userlist, MsnUser *user); -MsnUser *msn_userlist_find_user(MsnUserList *userlist, - const char *passport); -void msn_userlist_add_group(MsnUserList *userlist, MsnGroup *group); -void msn_userlist_remove_group(MsnUserList *userlist, MsnGroup *group); -MsnGroup *msn_userlist_find_group_with_id(MsnUserList *userlist, int id); -MsnGroup *msn_userlist_find_group_with_name(MsnUserList *userlist, - const char *name); -int msn_userlist_find_group_id(MsnUserList *userlist, - const char *group_name); -const char *msn_userlist_find_group_name(MsnUserList *userlist, - int group_id); -void msn_userlist_rename_group_id(MsnUserList *userlist, int group_id, - const char *new_name); -void msn_userlist_remove_group_id(MsnUserList *userlist, int group_id); - -void msn_userlist_rem_buddy(MsnUserList *userlist, const char *who, - int list_id, const char *group_name); -void msn_userlist_add_buddy(MsnUserList *userlist, const char *who, - int list_id, const char *group_name); -void msn_userlist_move_buddy(MsnUserList *userlist, const char *who, - const char *old_group_name, - const char *new_group_name); - -#endif /* _MSN_USERLIST_H_ */
--- a/libpurple/protocols/oscar/libaim.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/oscar/libaim.c Tue Mar 16 12:07:06 2010 +0900 @@ -31,7 +31,6 @@ OPT_PROTO_MAIL_CHECK | OPT_PROTO_IM_IMAGE, NULL, /* user_splits */ NULL, /* protocol_options */ - /* The mimimum icon size below is not needed in AIM 6.0 */ {"gif,jpeg,bmp,ico", 0, 0, 100, 100, 7168, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ oscar_list_icon_aim, /* list_icon */ oscar_list_emblem, /* list_emblems */
--- a/libpurple/protocols/oscar/libicq.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/oscar/libicq.c Tue Mar 16 12:07:06 2010 +0900 @@ -41,8 +41,7 @@ OPT_PROTO_MAIL_CHECK | OPT_PROTO_IM_IMAGE, NULL, /* user_splits */ NULL, /* protocol_options */ - {"gif,jpeg,bmp,ico", 48, 48, 52, 64, 7168, - PURPLE_ICON_SCALE_SEND | PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ + {"gif,jpeg,bmp,ico", 0, 0, 100, 100, 7168, PURPLE_ICON_SCALE_DISPLAY}, /* icon_spec */ oscar_list_icon_icq, /* list_icon */ oscar_list_emblem, /* list_emblems */ oscar_status_text, /* status_text */
--- a/libpurple/protocols/oscar/oscar.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/oscar/oscar.c Tue Mar 16 12:07:06 2010 +0900 @@ -1601,6 +1601,7 @@ if (oscar_util_valid_name_icq((purple_account_get_username(account)))) { od->icq = TRUE; + gc->flags |= PURPLE_CONNECTION_SUPPORT_MOODS; } else { gc->flags |= PURPLE_CONNECTION_HTML; gc->flags |= PURPLE_CONNECTION_AUTO_RESP; @@ -3830,7 +3831,7 @@ if (utf8 == NULL) /* The conversion failed! */ utf8 = g_strdup(_("[Unable to display a message from this user because it contained invalid characters.]")); - serv_got_chat_in(gc, ccon->id, info->bn, 0, utf8, time((time_t)NULL)); + serv_got_chat_in(gc, ccon->id, info->bn, 0, utf8, time(NULL)); g_free(utf8); return 1;
--- a/libpurple/protocols/yahoo/libymsg.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/yahoo/libymsg.c Tue Mar 16 12:07:06 2010 +0900 @@ -1860,6 +1860,7 @@ return; } else if (len > 0 && ret_data && *ret_data) { + PurpleAccount *account = purple_connection_get_account(gc); gchar **split_data = g_strsplit(ret_data, "\r\n", -1); int totalelements = 0; int response_no = -1; @@ -1867,11 +1868,13 @@ totalelements = g_strv_length(split_data); - if(totalelements == 1) + if(totalelements == 1) { /* Received an error code */ response_no = strtol(split_data[0], NULL, 10); - else if(totalelements >= 2) { + } else if(totalelements == 2 || totalelements == 3 ) { /* received valid data */ response_no = strtol(split_data[0], NULL, 10); token = g_strdup(split_data[1] + strlen("ymsgr=")); + } else { /* It looks like a transparent proxy has returned a document we don't want */ + response_no = -1; } g_strfreev(split_data); @@ -1890,8 +1893,8 @@ case 1212: /* Password incorrect */ /* Set password to NULL. Avoids account locking. Brings dialog to enter password if clicked on Re-enable account */ - if (!purple_account_get_remember_password(purple_connection_get_account(gc))) - purple_account_set_password(purple_connection_get_account(gc), NULL); + if (!purple_account_get_remember_password(account)) + purple_account_set_password(account, NULL); error_reason = g_strdup(_("Incorrect password")); error = PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED; break; @@ -1933,7 +1936,6 @@ else { /* OK to login, correct information provided */ PurpleUtilFetchUrlData *url_data = NULL; - PurpleAccount *account = purple_connection_get_account(gc); char *url = NULL; gboolean yahoojp = yahoo_is_japan(account); gboolean proxy_ssl = purple_account_get_bool(account, "proxy_ssl", FALSE); @@ -2737,8 +2739,9 @@ PurpleAccount *account; YahooFriend *f; - /* if status is not 1 ie YAHOO_STATUS_BRB, the packet bounced back, so contains our own ip */ - if(!(pkt->status == YAHOO_STATUS_BRB)) + /* if status is not YAHOO_STATUS_BRB or YAHOO_STATUS_P2P, the packet bounced back, + * so it contains our own ip */ + if(pkt->status != YAHOO_STATUS_BRB && pkt->status != YAHOO_STATUS_P2P) return ; while (l) {
--- a/libpurple/protocols/yahoo/libymsg.h Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/protocols/yahoo/libymsg.h Tue Mar 16 12:07:06 2010 +0900 @@ -119,6 +119,7 @@ YAHOO_STATUS_ONVACATION, YAHOO_STATUS_OUTTOLUNCH, YAHOO_STATUS_STEPPEDOUT, + YAHOO_STATUS_P2P = 11, YAHOO_STATUS_INVISIBLE = 12, YAHOO_STATUS_CUSTOM = 99, YAHOO_STATUS_IDLE = 999,
--- a/libpurple/prpl.h Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/prpl.h Tue Mar 16 12:07:06 2010 +0900 @@ -572,8 +572,7 @@ /** * Returns an array of "PurpleMood"s, with the last one having - * "mood" set to @c NULL, or NULL if the account does not support setting - * a mood. + * "mood" set to @c NULL. */ PurpleMood *(*get_moods)(PurpleAccount *account); };
--- a/libpurple/signals.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/signals.c Tue Mar 16 12:07:06 2010 +0900 @@ -1009,6 +1009,21 @@ } void +purple_marshal_POINTER__POINTER( + PurpleCallback cb, va_list args, void *data, + void **return_val) +{ + gpointer ret_val; + void *arg1 = va_arg(args, void *); + + ret_val = ((gpointer(*)(void *, void *))cb)(arg1, data); + + if (return_val != NULL) + *return_val = ret_val; +} + + +void purple_marshal_POINTER__POINTER_INT( PurpleCallback cb, va_list args, void *data, void **return_val)
--- a/libpurple/signals.h Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/signals.h Tue Mar 16 12:07:06 2010 +0900 @@ -355,6 +355,8 @@ void purple_marshal_BOOLEAN__INT_POINTER( PurpleCallback cb, va_list args, void *data, void **return_val); +void purple_marshal_POINTER__POINTER( + PurpleCallback cb, va_list args, void *data, void **return_val); void purple_marshal_POINTER__POINTER_INT( PurpleCallback cb, va_list args, void *data, void **return_val); void purple_marshal_POINTER__POINTER_INT64(
--- a/libpurple/upnp.c Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/upnp.c Tue Mar 16 12:07:06 2010 +0900 @@ -542,7 +542,7 @@ len = recv(dd->fd, buf, sizeof(buf) - 1, 0); - if(len > 0) { + if(len >= 0) { buf[len] = '\0'; break; } else if(errno != EINTR) {
--- a/libpurple/win32/global.mak Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/win32/global.mak Tue Mar 16 12:07:06 2010 +0900 @@ -10,8 +10,8 @@ # Locations of our various dependencies WIN32_DEV_TOP ?= $(PIDGIN_TREE_TOP)/../win32-dev -ASPELL_TOP ?= $(WIN32_DEV_TOP)/aspell-dev-0-50-3-3 -GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.11-daa1 +GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.16 +ENCHANT_TOP ?= $(WIN32_DEV_TOP)/enchant_1.5.0-2_win32 GTK_TOP ?= $(WIN32_DEV_TOP)/gtk_2_0-2.14 GTK_BIN ?= $(GTK_TOP)/bin BONJOUR_TOP ?= $(WIN32_DEV_TOP)/Bonjour_SDK @@ -105,7 +105,7 @@ GMSGFMT ?= $(WIN32_DEV_TOP)/gettext-0.17/bin/msgfmt MAKENSIS ?= makensis.exe MAKENSISOPT ?= / -PERL ?= /cygdrive/c/perl/bin/perl +PERL ?= perl WINDRES ?= windres STRIP ?= strip
--- a/libpurple/win32/rules.mak Thu Mar 04 15:19:39 2010 +0900 +++ b/libpurple/win32/rules.mak Tue Mar 16 12:07:06 2010 +0900 @@ -4,7 +4,7 @@ $(CC) $(CFLAGS) $(DEFINES) $(INCLUDE_PATHS) -o $@ -c $< %.c: %.xs - $(PERL) $(EXTUTILS)/xsubpp -typemap $(EXTUTILS)/typemap -typemap $(PURPLE_PERL_TOP)/common/typemap $< > $@ + $(PERL) -MExtUtils::ParseXS -e 'ExtUtils::ParseXS::process_file(filename => "$<", output => "$@", typemap => "$(PURPLE_PERL_TOP)/common/typemap");' %.o: %.rc $(WINDRES) -I$(PURPLE_TOP) -i $< -o $@
--- a/pidgin/Makefile.mingw Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/Makefile.mingw Tue Mar 16 12:07:06 2010 +0900 @@ -9,8 +9,6 @@ DEFINES := $(subst -DWIN32_LEAN_AND_MEAN,,$(DEFINES)) -NEEDED_DLLS = $(GTKSPELL_TOP)/gtkspell/libgtkspell.dll - ## ## VARIABLE DEFINITIONS ## @@ -43,14 +41,12 @@ -I$(GTK_TOP)/include/atk-1.0 \ -I$(GTK_TOP)/include/cairo \ -I$(GTK_TOP)/lib/gtk-2.0/include \ - -I$(GTKSPELL_TOP) \ - -I$(ASPELL_TOP)/include + -I$(GTKSPELL_TOP)/include/gtkspell-2.0 LIB_PATHS += -L$(GTK_TOP)/lib \ -L$(PURPLE_TOP) \ -L$(PIDGIN_TOP) \ - -L$(PIDGIN_IDLETRACK_TOP) \ - -L$(ASPELL_TOP)/lib + -L$(PIDGIN_IDLETRACK_TOP) ## ## SOURCES, OBJECTS @@ -151,7 +147,6 @@ install_shallow: $(PIDGIN_INSTALL_DIR) $(EXE_TARGET).exe $(PIDGIN_TARGET).dll cp $(EXE_TARGET).exe $(PIDGIN_TARGET).dll $(PIDGIN_INSTALL_DIR) - cp $(NEEDED_DLLS) $(PIDGIN_INSTALL_DIR) install: install_shallow all $(MAKE) -C $(PIDGIN_PLUGINS_TOP) -f $(MINGW_MAKEFILE) install
--- a/pidgin/gtkblist.c Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/gtkblist.c Tue Mar 16 12:07:06 2010 +0900 @@ -3389,6 +3389,253 @@ !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled")); } +static char *get_mood_icon_path(const char *mood) +{ + char *path; + + if (!strcmp(mood, "busy")) { + path = g_build_filename(DATADIR, "pixmaps", "pidgin", + "status", "16", "busy.png", NULL); + } else if (!strcmp(mood, "hiptop")) { + path = g_build_filename(DATADIR, "pixmaps", "pidgin", + "emblems", "16", "hiptop.png", NULL); + } else { + char *filename = g_strdup_printf("%s.png", mood); + path = g_build_filename(DATADIR, "pixmaps", "pidgin", + "emotes", "small", filename, NULL); + g_free(filename); + } + return path; +} + +static void +update_status_with_mood(PurpleAccount *account, const gchar *mood, + const gchar *text) +{ + if (mood != NULL && !purple_strequal(mood, "")) { + if (text) { + purple_account_set_status(account, "mood", TRUE, + PURPLE_MOOD_NAME, mood, + PURPLE_MOOD_COMMENT, text, + NULL); + } else { + purple_account_set_status(account, "mood", TRUE, + PURPLE_MOOD_NAME, mood, + NULL); + } + } else { + purple_account_set_status(account, "mood", FALSE, NULL); + } +} + +static void +edit_mood_cb(PurpleConnection *gc, PurpleRequestFields *fields) +{ + PurpleRequestField *mood_field, *text_field; + GList *l; + + mood_field = purple_request_fields_get_field(fields, "mood"); + text_field = purple_request_fields_get_field(fields, "text"); + l = purple_request_field_list_get_selected(mood_field); + + if (l) { + const char *mood = purple_request_field_list_get_data(mood_field, l->data); + const char *text = purple_request_field_string_get_value(text_field); + + if (gc) { + PurpleAccount *account = purple_connection_get_account(gc); + + update_status_with_mood(account, mood, text); + } else { + GList *accounts = purple_accounts_get_all_active(); + + for (; accounts ; accounts = g_list_delete_link(accounts, accounts)) { + PurpleAccount *account = (PurpleAccount *) accounts->data; + PurpleConnection *gc = purple_account_get_connection(account); + + if (gc->flags && PURPLE_CONNECTION_SUPPORT_MOODS) { + update_status_with_mood(account, mood, text); + } + } + } + } +} + +static void +global_moods_for_each(gpointer key, gpointer value, gpointer user_data) +{ + GList **out_moods = (GList **) user_data; + PurpleMood *mood = (PurpleMood *) value; + + *out_moods = g_list_append(*out_moods, mood); +} + +static PurpleMood * +get_global_moods(void) +{ + GHashTable *global_moods = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + GHashTable *mood_counts = + g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + GList *accounts = purple_accounts_get_all_active(); + PurpleMood *result = NULL; + GList *out_moods = NULL; + int i = 0; + int num_accounts = 0; + + for (; accounts ; accounts = g_list_delete_link(accounts, accounts)) { + PurpleAccount *account = (PurpleAccount *) accounts->data; + PurpleConnection *gc = purple_account_get_connection(account); + + if (gc->flags & PURPLE_CONNECTION_SUPPORT_MOODS) { + PurplePluginProtocolInfo *prpl_info = + PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); + PurpleMood *mood = NULL; + + for (mood = prpl_info->get_moods(account) ; + mood->mood != NULL ; mood++) { + int mood_count = + GPOINTER_TO_INT(g_hash_table_lookup(mood_counts, mood->mood)); + + if (!g_hash_table_lookup(global_moods, mood->mood)) { + g_hash_table_insert(global_moods, g_strdup(mood->mood), mood); + } + g_hash_table_insert(mood_counts, g_strdup(mood->mood), + GINT_TO_POINTER(mood_count + 1)); + } + + num_accounts++; + } + } + + g_hash_table_foreach(global_moods, global_moods_for_each, &out_moods); + result = g_new0(PurpleMood, g_hash_table_size(global_moods) + 1); + + while (out_moods) { + PurpleMood *mood = (PurpleMood *) out_moods->data; + int in_num_accounts = + GPOINTER_TO_INT(g_hash_table_lookup(mood_counts, mood->mood)); + + if (in_num_accounts == num_accounts) { + /* mood is present in all accounts supporting moods */ + result[i].mood = mood->mood; + result[i].description = mood->description; + i++; + } + out_moods = g_list_delete_link(out_moods, out_moods); + } + + g_hash_table_destroy(global_moods); + g_hash_table_destroy(mood_counts); + + return result; +} + +/* get current set mood for all mood-supporting accounts, or NULL if not set + or not set to the same on all */ +static const gchar * +get_global_mood_status(void) +{ + GList *accounts = purple_accounts_get_all_active(); + const gchar *found_mood = NULL; + + for (; accounts ; accounts = g_list_delete_link(accounts, accounts)) { + PurpleAccount *account = (PurpleAccount *) accounts->data; + + if (purple_account_get_connection(account)->flags & + PURPLE_CONNECTION_SUPPORT_MOODS) { + PurplePresence *presence = purple_account_get_presence(account); + PurpleStatus *status = purple_presence_get_status(presence, "mood"); + const gchar *curr_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); + + if (found_mood != NULL && !purple_strequal(curr_mood, found_mood)) { + /* found a different mood */ + found_mood = NULL; + break; + } else { + found_mood = curr_mood; + } + } + } + + return found_mood; +} + +static void +set_mood_cb(GtkWidget *widget, PurpleAccount *account) +{ + const char *current_mood; + PurpleRequestFields *fields; + PurpleRequestFieldGroup *g; + PurpleRequestField *f; + PurpleConnection *gc = NULL; + PurplePluginProtocolInfo *prpl_info = NULL; + PurpleMood *mood; + PurpleMood *global_moods = get_global_moods(); + + if (account) { + PurplePresence *presence = purple_account_get_presence(account); + PurpleStatus *status = purple_presence_get_status(presence, "mood"); + gc = purple_account_get_connection(account); + g_return_if_fail(gc->prpl != NULL); + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); + current_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); + } else { + current_mood = get_global_mood_status(); + } + + fields = purple_request_fields_new(); + g = purple_request_field_group_new(NULL); + f = purple_request_field_list_new("mood", _("Please select your mood from the list")); + + purple_request_field_list_add(f, _("None"), ""); + if (current_mood == NULL) + purple_request_field_list_add_selected(f, _("None")); + + /* TODO: rlaager wants this sorted. */ + for (mood = account ? prpl_info->get_moods(account) : global_moods; + mood->mood != NULL ; mood++) { + char *path; + + if (mood->mood == NULL || mood->description == NULL) + continue; + + path = get_mood_icon_path(mood->mood); + purple_request_field_list_add_icon(f, _(mood->description), + path, (gpointer)mood->mood); + g_free(path); + + if (current_mood && !strcmp(current_mood, mood->mood)) + purple_request_field_list_add_selected(f, _(mood->description)); + } + purple_request_field_group_add_field(g, f); + + purple_request_fields_add_group(fields, g); + + /* if the connection allows setting a mood message */ + if (gc && (gc->flags & PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES)) { + g = purple_request_field_group_new(NULL); + f = purple_request_field_string_new("text", + _("Message (optional)"), NULL, FALSE); + purple_request_field_group_add_field(g, f); + purple_request_fields_add_group(fields, g); + } + + purple_request_fields(gc, _("Edit User Mood"), _("Edit User Mood"), + NULL, fields, + _("OK"), G_CALLBACK(edit_mood_cb), + _("Cancel"), NULL, + gc ? purple_connection_get_account(gc) : NULL, + NULL, NULL, gc); + + g_free(global_moods); +} + +static void +set_mood_show(void) +{ + set_mood_cb(NULL, NULL); +} /*************************************************** * Crap * @@ -3428,6 +3675,7 @@ { N_("/Tools/Plu_gins"), "<CTL>U", pidgin_plugin_dialog_show, 2, "<StockItem>", PIDGIN_STOCK_TOOLBAR_PLUGINS }, { N_("/Tools/Pr_eferences"), "<CTL>P", pidgin_prefs_show, 0, "<StockItem>", GTK_STOCK_PREFERENCES }, { N_("/Tools/Pr_ivacy"), NULL, pidgin_privacy_dialog_show, 0, "<Item>", NULL }, + { N_("/Tools/Set _Mood"), "<CTL>M", set_mood_show, 0, "<Item>", NULL }, { "/Tools/sep2", NULL, NULL, 0, "<Separator>", NULL }, { N_("/Tools/_File Transfers"), "<CTL>T", pidgin_xfer_dialog_show, 0, "<StockItem>", PIDGIN_STOCK_TOOLBAR_TRANSFER }, { N_("/Tools/R_oom List"), NULL, pidgin_roomlist_dialog_show, 0, "<Item>", NULL }, @@ -3760,25 +4008,6 @@ return pb; } -static char *get_mood_icon_path(const char *mood) -{ - char *path; - - if (!strcmp(mood, "busy")) { - path = g_build_filename(DATADIR, "pixmaps", "pidgin", - "status", "16", "busy.png", NULL); - } else if (!strcmp(mood, "hiptop")) { - path = g_build_filename(DATADIR, "pixmaps", "pidgin", - "emblems", "16", "hiptop.png", NULL); - } else { - char *filename = g_strdup_printf("%s.png", mood); - path = g_build_filename(DATADIR, "pixmaps", "pidgin", - "emotes", "small", filename, NULL); - g_free(filename); - } - return path; -} - GdkPixbuf * pidgin_blist_get_emblem(PurpleBlistNode *node) { @@ -7821,82 +8050,7 @@ purple_account_set_enabled(account, PIDGIN_UI, FALSE); } -static void -edit_mood_cb(PurpleConnection *gc, PurpleRequestFields *fields) -{ - PurpleRequestField *f; - GList *l; - - f = purple_request_fields_get_field(fields, "mood"); - l = purple_request_field_list_get_selected(f); - - if (l) { - const char *mood = purple_request_field_list_get_data(f, l->data); - PurpleAccount *account = purple_connection_get_account(gc); - - if (mood != NULL && !purple_strequal(mood, "")) { - purple_account_set_status(account, "mood", TRUE, - PURPLE_MOOD_NAME, mood, - NULL); - } else { - purple_account_set_status(account, "mood", FALSE, NULL); - } - } -} - -static void -set_mood_cb(GtkWidget *widget, PurpleAccount *account) -{ - PurplePresence *presence = purple_account_get_presence(account); - PurpleStatus *status = purple_presence_get_status(presence, "mood"); - const char *current_mood; - PurpleRequestFields *fields; - PurpleRequestFieldGroup *g; - PurpleRequestField *f; - PurpleConnection *gc = purple_account_get_connection(account); - PurplePluginProtocolInfo *prpl_info; - PurpleMood *mood; - - g_return_if_fail(gc->prpl != NULL); - prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); - - current_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); - - fields = purple_request_fields_new(); - g = purple_request_field_group_new(NULL); - f = purple_request_field_list_new("mood", _("Please select your mood from the list")); - - purple_request_field_list_add(f, _("None"), ""); - if (current_mood == NULL) - purple_request_field_list_add_selected(f, _("None")); - - /* TODO: rlaager wants this sorted. */ - for (mood = prpl_info->get_moods(account); - mood->mood != NULL ; mood++) { - char *path; - - if (mood->mood == NULL || mood->description == NULL) - continue; - - path = get_mood_icon_path(mood->mood); - purple_request_field_list_add_icon(f, _(mood->description), - path, (gpointer)mood->mood); - g_free(path); - - if (current_mood && !strcmp(current_mood, mood->mood)) - purple_request_field_list_add_selected(f, _(mood->description)); - } - purple_request_field_group_add_field(g, f); - - purple_request_fields_add_group(fields, g); - - purple_request_fields(gc, _("Edit User Mood"), _("Edit User Mood"), - NULL, fields, - _("OK"), G_CALLBACK(edit_mood_cb), - _("Cancel"), NULL, - purple_connection_get_account(gc), - NULL, NULL, gc); -} + void pidgin_blist_update_accounts_menu(void) @@ -8023,7 +8177,7 @@ (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods) || PURPLE_PLUGIN_HAS_ACTIONS(plugin))) { if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods) && - prpl_info->get_moods(account) != NULL) { + gc->flags & PURPLE_CONNECTION_SUPPORT_MOODS) { GList *types; for (types = purple_account_get_status_types(account);
--- a/pidgin/gtkdialogs.c Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/gtkdialogs.c Tue Mar 16 12:07:06 2010 +0900 @@ -76,7 +76,6 @@ {"John 'rekkanoryo' Bailey", N_("bug master"), NULL}, {"Ethan 'Paco-Paco' Blanton", NULL, NULL}, {"Hylke Bons", N_("artist"), "hylkebons@gmail.com"}, - {"Thomas Butter", NULL, NULL}, /* feel free to not translate this */ {N_("Ka-Hing Cheung"), NULL, NULL}, {"Sadrul Habib Chowdhury", NULL, NULL}, @@ -87,8 +86,6 @@ {"Richard 'rlaager' Laager", NULL, "rlaager@pidgin.im"}, {"Sulabh 'sulabh_m' Mahajan", NULL, NULL}, {"Richard 'wabz' Nelson", NULL, NULL}, - {"Christopher 'siege' O'Brien", NULL, "taliesein@users.sf.net"}, - {"Bartosz Oler", NULL, NULL}, {"Etan 'deryni' Reisner", NULL, NULL}, {"Tim 'marv' Ringenbach", NULL, NULL}, {"Michael 'Maiku' Ruprecht", N_("voice and video"), NULL}, @@ -114,12 +111,15 @@ /* Order: Alphabetical by Last Name */ static const struct developer retired_developers[] = { {"Herman Bloggs", N_("win32 port"), "herman@bluedigits.com"}, + {"Thomas Butter", NULL, NULL}, {"Jim Duchek", N_("maintainer"), "jim@linuxpimps.com"}, {"Rob Flynn", N_("maintainer"), NULL}, {"Adam Fritzler", N_("libfaim maintainer"), NULL}, {"Christian 'ChipX86' Hammond", N_("webmaster"), NULL}, /* If "lazy bum" translates literally into a serious insult, use something else or omit it. */ {"Syd Logan", N_("hacker and designated driver [lazy bum]"), NULL}, + {"Christopher 'siege' O'Brien", NULL, "taliesein@users.sf.net"}, + {"Bartosz Oler", NULL, NULL}, {"Megan 'Cae' Schneider", N_("support/QA"), NULL}, {"Jim Seymour", N_("XMPP"), NULL}, {"Mark Spencer", N_("original author"), "markster@marko.net"},
--- a/pidgin/gtkimhtml.c Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/gtkimhtml.c Tue Mar 16 12:07:06 2010 +0900 @@ -195,7 +195,7 @@ purple_debug_info("imhtml clipboard", "from clipboard: %s\n", clipboard); - fd = g_fopen("e:\\purplecb.txt", "wb"); + fd = g_fopen("c:\\purplecb.txt", "wb"); fprintf(fd, "%s", clipboard); fclose(fd); #endif @@ -1201,6 +1201,14 @@ memcpy(text, selection_data->data, selection_data->length); } +#ifdef _WIN32 + if (gtk_selection_data_get_data_type(selection_data) == gdk_atom_intern("HTML Format", FALSE)) { + char *tmp = clipboard_win32_to_html(text); + g_free(text); + text = tmp; + } +#endif + if (selection_data->length >= 2 && (*(guint16 *)text == 0xfeff || *(guint16 *)text == 0xfffe)) { /* This is UTF-16 */ @@ -1261,13 +1269,16 @@ #ifdef _WIN32 /* If we're on windows, let's see if we can get data from the HTML Format clipboard before we try to paste from the GTK buffer */ - if (!clipboard_paste_html_win32(imhtml)) -#endif - { + if (!clipboard_paste_html_win32(imhtml)) { + GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD); + gtk_clipboard_request_text(clipboard, paste_plaintext_received_cb, imhtml); + + } +#else GtkClipboard *clipboard = gtk_widget_get_clipboard(GTK_WIDGET(imhtml), GDK_SELECTION_CLIPBOARD); gtk_clipboard_request_contents(clipboard, gdk_atom_intern("text/html", FALSE), paste_received_cb, imhtml); - } +#endif g_signal_stop_emission_by_name(imhtml, "paste-clipboard"); } @@ -1693,8 +1704,10 @@ g_signal_connect_after(G_OBJECT(imhtml), "realize", G_CALLBACK(imhtml_realized_remove_primary), NULL); g_signal_connect(G_OBJECT(imhtml), "unrealize", G_CALLBACK(imhtml_destroy_add_primary), NULL); +#ifndef _WIN32 g_signal_connect_after(G_OBJECT(GTK_IMHTML(imhtml)->text_buffer), "mark-set", G_CALLBACK(mark_set_so_update_selection_cb), imhtml); +#endif gtk_widget_add_events(GTK_WIDGET(imhtml), GDK_LEAVE_NOTIFY_MASK | GDK_ENTER_NOTIFY_MASK);
--- a/pidgin/plugins/perl/common/Makefile.mingw Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/plugins/perl/common/Makefile.mingw Tue Mar 16 12:07:06 2010 +0900 @@ -12,7 +12,6 @@ DEFINES := $(subst -DWIN32_LEAN_AND_MEAN,,$(DEFINES)) TARGET = Pidgin -EXTUTILS ?= C:/perl/lib/ExtUtils ## ## INCLUDE PATHS
--- a/pidgin/plugins/xmppconsole.c Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/plugins/xmppconsole.c Tue Mar 16 12:07:06 2010 +0900 @@ -845,7 +845,7 @@ /** summary */ N_("Send and receive raw XMPP stanzas."), /** description */ - N_("This plugin is useful for debbuging XMPP servers or clients."), + N_("This plugin is useful for debugging XMPP servers or clients."), "Sean Egan <seanegan@gmail.com>", /**< author */ PURPLE_WEBSITE, /**< homepage */
--- a/pidgin/win32/gtkdocklet-win32.c Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/gtkdocklet-win32.c Tue Mar 16 12:07:06 2010 +0900 @@ -21,7 +21,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02111-1301, USA. */ - +#define _WIN32_IE 0x0500 #include <windows.h> #include <gdk/gdkwin32.h> #include <gdk/gdk.h> @@ -51,7 +51,7 @@ /* This is used to trigger click events on so they appear to GTK+ as if they are triggered by input */ static GtkWidget *dummy_button = NULL; static GtkWidget *dummy_window = NULL; -static NOTIFYICONDATA _nicon_data; +static NOTIFYICONDATAW _nicon_data; static gboolean dummy_button_cb(GtkWidget *widget, GdkEventButton *event, gpointer user_data) { pidgin_docklet_clicked(event->button); @@ -64,7 +64,7 @@ switch(msg) { case WM_CREATE: purple_debug_info("docklet", "WM_CREATE\n"); - taskbarRestartMsg = RegisterWindowMessage("TaskbarCreated"); + taskbarRestartMsg = RegisterWindowMessageW(L"TaskbarCreated"); break; case WM_TIMER: @@ -114,7 +114,7 @@ if (msg == taskbarRestartMsg) { /* explorer crashed and left us hanging... This will put the systray icon back in it's place, when it restarts */ - Shell_NotifyIcon(NIM_ADD, &_nicon_data); + Shell_NotifyIconW(NIM_ADD, &_nicon_data); } break; }/* end switch */ @@ -124,10 +124,10 @@ /* Create hidden window to process systray messages */ static HWND systray_create_hiddenwin() { - WNDCLASSEX wcex; - LPCTSTR wname; + WNDCLASSEXW wcex; + wchar_t *wname; - wname = TEXT("WinpidginSystrayWinCls"); + wname = L"WinpidginSystrayWinCls"; wcex.cbSize = sizeof(wcex); wcex.style = 0; @@ -142,22 +142,25 @@ wcex.lpszClassName = wname; wcex.hIconSm = NULL; - RegisterClassEx(&wcex); + RegisterClassExW(&wcex); /* Create the window */ - return (CreateWindow(wname, "", 0, 0, 0, 0, 0, GetDesktopWindow(), NULL, winpidgin_exe_hinstance(), 0)); + return (CreateWindowW(wname, L"", 0, 0, 0, 0, 0, GetDesktopWindow(), NULL, winpidgin_exe_hinstance(), 0)); } static void systray_init_icon(HWND hWnd) { + wchar_t *w; ZeroMemory(&_nicon_data, sizeof(_nicon_data)); - _nicon_data.cbSize = sizeof(NOTIFYICONDATA); + _nicon_data.cbSize = sizeof(NOTIFYICONDATAW); _nicon_data.hWnd = hWnd; _nicon_data.uID = 0; _nicon_data.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; _nicon_data.uCallbackMessage = WM_TRAYMESSAGE; _nicon_data.hIcon = NULL; - lstrcpy(_nicon_data.szTip, PIDGIN_NAME); - Shell_NotifyIcon(NIM_ADD, &_nicon_data); + w = g_utf8_to_utf16(PIDGIN_NAME, -1, NULL, NULL, NULL); + wcsncpy(_nicon_data.szTip, w, sizeof(_nicon_data.szTip) / sizeof(wchar_t)); + g_free(w); + Shell_NotifyIconW(NIM_ADD, &_nicon_data); pidgin_docklet_embedded(); } @@ -486,11 +489,11 @@ g_return_if_fail(hicon != NULL); _nicon_data.hIcon = hicon; - Shell_NotifyIcon(NIM_MODIFY, &_nicon_data); + Shell_NotifyIconW(NIM_MODIFY, &_nicon_data); } static void systray_remove_nid(void) { - Shell_NotifyIcon(NIM_DELETE, &_nicon_data); + Shell_NotifyIconW(NIM_DELETE, &_nicon_data); } static void winpidgin_tray_update_icon(PurpleStatusPrimitive status, @@ -547,19 +550,19 @@ static void winpidgin_tray_blank_icon() { _nicon_data.hIcon = NULL; - Shell_NotifyIcon(NIM_MODIFY, &_nicon_data); + Shell_NotifyIconW(NIM_MODIFY, &_nicon_data); } static void winpidgin_tray_set_tooltip(gchar *tooltip) { - if (tooltip) { - char *locenc = NULL; - locenc = g_locale_from_utf8(tooltip, -1, NULL, NULL, NULL); - lstrcpyn(_nicon_data.szTip, locenc, sizeof(_nicon_data.szTip) / sizeof(TCHAR)); - g_free(locenc); - } else { - lstrcpy(_nicon_data.szTip, PIDGIN_NAME); - } - Shell_NotifyIcon(NIM_MODIFY, &_nicon_data); + const char *value = tooltip; + wchar_t *w; + if (value == NULL) { + value = PIDGIN_NAME; + } + w = g_utf8_to_utf16(value, -1, NULL, NULL, NULL); + wcsncpy(_nicon_data.szTip, w, sizeof(_nicon_data.szTip) / sizeof(wchar_t)); + g_free(w); + Shell_NotifyIconW(NIM_MODIFY, &_nicon_data); } static void winpidgin_tray_minimize(PidginBuddyList *gtkblist) {
--- a/pidgin/win32/nsis/available.lst Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/available.lst Tue Mar 16 12:07:06 2010 +0900 @@ -4,72 +4,46 @@ sq,AL,sq_AL,Albanian (Albania),sq_AL.zip bg,BG,bg_BG,Bulgarian (Bulgaria),bg_BG.zip ca,ES,ca_ES,Catalan (Spain),ca_ES.zip +cop,EG,cop_EG,Coptic (Egypt),cop_EG.zip hr,HR,hr_HR,Croatian (Croatia),hr_HR.zip cs,CZ,cs_CZ,Czech (Czech Republic),cs_CZ.zip da,DK,da_DK,Danish (Denmark),da_DK.zip nl,NL,nl_NL,Dutch (Netherlands),nl_NL.zip nl,NL,nl_med,Dutch Medical (Netherlands),nl_med.zip -nl,BE,nl_NL,Dutch (Belgium),nl_NL.zip -nl,BE,nl_NL,Dutch Medical (Belgium),nl_NL.zip en,AU,en_AU,English (Australia),en_AU.zip en,CA,en_CA,English (Canada),en_CA.zip en,NZ,en_NZ,English (New Zealand),en_NZ.zip en,ZA,en_ZA,English (South Africa),en_ZA.zip en,GB,en_GB,English (United Kingdom),en_GB.zip en,US,en_US,English (United States),en_US.zip -cop,EG,cop_EG,Coptic (Egypt),cop_EG.zip eo,ANY,eo_l3,Esperanto,eo.zip et,EE,et_EE,Estonian (Estonia),et_EE.zip fo,FO,fo_FO,Faroese (Faroe Islands),fo_FO.zip -fr,BE,fr_FR,Franテァais Rテゥforme 1990 & Classique (Belgium),fr_FR_1-3-2.zip -fr,CA,fr_FR,Franテァais Rテゥforme 1990 & Classique (Canada),fr_FR_1-3-2.zip -fr,FR,fr_FR,Franテァais Rテゥforme 1990 & Classique (France),fr_FR_1-3-2.zip -fr,LU,fr_FR,Franテァais Rテゥforme 1990 & Classique (Luxembourg),fr_FR_1-3-2.zip -fr,MC,fr_FR,Franテァais Rテゥforme 1990 & Classique (Monaco),fr_FR_1-3-2.zip -fr,CH,fr_FR,Franテァais Rテゥforme 1990 & Classique (Switzerland),fr_FR_1-3-2.zip -fr,BE,fr_FR-classique,Franテァais Classique (Belgium),fr_FR-classique_1-3-2.zip -fr,CA,fr_FR-classique,Franテァais Classique (Canada),fr_FR-classique_1-3-2.zip -fr,FR,fr_FR-classique,Franテァais Classique (France),fr_FR-classique_1-3-2.zip -fr,LU,fr_FR-classique,Franテァais Classique (Luxembourg),fr_FR-classique_1-3-2.zip -fr,MC,fr_FR-classique,Franテァais Classique (Monaco),fr_FR-classique_1-3-2.zip -fr,CH,fr_FR-classique,Franテァais Classique (Switzerland),fr_FR-classique_1-3-2.zip -fr,BE,fr_FR-1990,Franテァais Rテゥforme 1990 (Belgium),fr_FR-1990_1-3-2.zip -fr,CA,fr_FR-1990,Franテァais Rテゥforme 1990 (Canada),fr_FR-1990_1-3-2.zip -fr,FR,fr_FR-1990,Franテァais Rテゥforme 1990 (France),fr_FR-1990_1-3-2.zip -fr,LU,fr_FR-1990,Franテァais Rテゥforme 1990 (Luxembourg),fr_FR-1990_1-3-2.zip -fr,MC,fr_FR-1990,Franテァais Rテゥforme 1990 (Monaco),fr_FR-1990_1-3-2.zip -fr,CH,fr_FR-1990,Franテァais Rテゥforme 1990 (Switzerland),fr_FR-1990_1-3-2.zip +fr,FR,fr_FR-classique,Fran軋is Classique,fr_FR-classique_1-3-2.zip +fr,FR,fr_FR-1990,Fran軋is R馭orme 1990,fr_FR-1990_1-3-2.zip +fr,FR,fr_FR,Fran軋is R馭orme 1990 & Classique,fr_FR_1-3-2.zip fy,NL,fy_NL,Frisian (Netherlands),fy_NL.zip gl,ES,gl_ES,Galician (Spain),gl_ES.zip gsc,FR,gsc_FR,Gascon (France),gsc_FR.zip -de,AT,de_DE,German (Austria Base),de_DE.zip de,AT,de_AT,German (Austria Extension),de_AT.zip de,AT,de_AT_frami,German (Austria) neu 08/2006 (frami),de_AT_frami.zip de,DE,de_DE,German (Germany),de_DE.zip de,DE,de_DE_frami,German (Germany) neu 08/2006 (frami),de_DE_frami.zip -de,LI,de_CH,German (Liechtenstein),de_CH.zip -de,LI,de_CH_frami,German (Liechtenstein) neu 08/2006 (frami),de_CH_frami.zip -de,LU,de_DE,German (Luxembourg),de_DE.zip -de,LU,de_DE_frami,German (Luxembourg) neu 08/2006 (frami),de_DE_frami.zip de,CH,de_CH,German (Switzerland),de_CH.zip de,CT,de_CH_frami,German (Switzerland) neu 08/2006 (frami),de_CH_frami.zip el,GR,el_GR,Greek (Greece),el_GR.zip gu,IN,gu_IN,Gujarati (India),gu_IN.zip -gd,GB,gd_GB,Scots Gaelic (Scotland),gd_GB.zip he,IL,he_IL,Hebrew (Israel),he_IL.zip hil,PH,hil_PH,Hiligaynon (Philippines),hil_PH.zip hu,HU,hu_HU,Hungarian (Hungary),hu_HU.zip hu,HU,hu_HU_comb,Hungarian (Hungary) collected compounds,hu_HU_comb.zip id,ID,id_ID,Indonesian (Indonesia),id_ID.zip -ia,ANY,ia_ANY,Interlingua (ANY locale),ia_ANY.zip ga,IE,ga_IE,Irish (Ireland),ga_IE.zip it,IT,it_IT,Italian (Italy),it_IT.zip -it,CH,it_IT,Italian (Switzerland),it_IT.zip sw,KE,sw_KE,Kiswahili (Africa),sw_KE.zip +ku,TR,ku_TR,Kurdish (Turkey),ku_TR.zip +it,IT,la,Latin,la.zip lv,LV,lv_LV,Latvian (Latvia),lv_LV.zip -ku,TR,ku_TR,Kurdish (Turkey),ku_TR.zip -ku,TR,ku_TR,Kurdish (Syria),ku_TR.zip -it,IT,la,Latin (for x-register),la.zip lt,LT,lt_LT,Lithuanian (Lithuania),lt_LT.zip mk,MK,mk_MK,Macedonian (Macedonia),mk_MK.zip ms,MY,ms_MY,Malay (Malaysia),ms_MY.zip @@ -89,6 +63,7 @@ ru,RU,ru_RU,Russian (Russia),ru_RU.zip ru,RU,ru_RU_ye,Russian_ye (Russia),ru_RU_ye.zip ru,RU,ru_RU_yo,Russian_yo (Russia),ru_RU_yo.zip +gd,GB,gd_GB,Scots Gaelic (Scotland),gd_GB.zip tn,ZA,tn_ZA,Setswana (Africa),tn_ZA.zip sk,SK,sk_SK,Slovak (Slovakia),sk_SK.zip sl,SI,sl_SI,Slovenian (Slovenia),sl_SI.zip @@ -104,7 +79,6 @@ es,EC,es_EC,Spanish (Ecuador),es_EC.zip es,SV,es_SV,Spanish (El Salvador),es_SV.zip es,GT,es_GT,Spanish (Guatemala),es_GT.zip -es,HN,es_HN,Spanish (Honduras),es_HN.zip es,MX,es_MX,Spanish (Mexico),es_MX.zip es,NI,es_NI,Spanish (Nicaragua),es_NI.zip es,PA,es_PA,Spanish (Panama),es_PA.zip @@ -118,8 +92,7 @@ sv,SE,sv_SE,Swedish (Sweden),sv_SE.zip ts,ZA,ts_ZA,Tsonga (South Africa),ts_ZA.zip uk,UA,uk_UA,Ukrainian (Ukraine),uk_UA.zip -ur,IN,ur_PK,Urdu (India),ur_PK.zip -ur,PK,ur_PK,Urdu (Pakistan),ur_PK.zip +ur,PK,ur_PK,Urdu,ur_PK.zip ve,ZA,ve_ZA,Venda (South Africa),ve_ZA.zip vi,VN,vi_VN,Vietnamese (Viet-Nam),vi_VN.zip cy,GB,cy_GB,Welsh (Wales),cy_GB.zip
--- a/pidgin/win32/nsis/generate_gtk_zip.sh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/generate_gtk_zip.sh Tue Mar 16 12:07:06 2010 +0900 @@ -14,31 +14,23 @@ CONTENTS_FILE=$INSTALL_DIR/CONTENTS #This needs to be changed every time there is any sort of change. -BUNDLE_VERSION=2.14.7.0 +BUNDLE_VERSION=2.16.6.0 -ATK="http://ftp.gnome.org/pub/gnome/binaries/win32/atk/1.24/atk_1.24.0-1_win32.zip ATK 1.24.0-1" +ATK="http://ftp.acc.umu.se/pub/gnome/binaries/win32/atk/1.26/atk_1.26.0-1_win32.zip ATK 1.26.0-1" CAIRO="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/cairo_1.8.10-1_win32.zip Cairo 1.8.10-1" EXPAT="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/expat_2.0.1-1_win32.zip Expat 2.0.1-1" FONTCONFIG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/fontconfig_2.8.0-2_win32.zip Fontconfig 2.8.0-2" FREETYPE="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/freetype_2.3.11-2_win32.zip Freetype 2.3.11-2" GETTEXT="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/gettext-runtime-0.17-1.zip Gettext 0.17-1" GLIB="http://ftp.gnome.org/pub/gnome/binaries/win32/glib/2.20/glib_2.20.5-1_win32.zip Glib 2.20.5-1" -GTK="http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.14/gtk+_2.14.7-1_win32.zip GTK+ 2.14.7-1" -LIBJPEG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/jpeg_7-1_win32.zip libjpeg 7-1" -#Used by GTK+ -LIBPNG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libpng_1.2.39-1_win32.zip libpng 1.2.39-1" -#Used by Cairo -LIBPNG2="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libpng_1.4.0-1_win32.zip libpng 1.4.0-1" -LIBTIFF="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libtiff_3.9.1-1_win32.zip libtiff 3.9.1-1" -#PANGO="http://ftp.gnome.org/pub/gnome/binaries/win32/pango/1.22/pango_1.22.4-1_win32.zip Pango 1.22.4-1" +GTK="http://ftp.acc.umu.se/pub/gnome/binaries/win32/gtk+/2.16/gtk+_2.16.6-2_win32.zip GTK+ 2.16.6-2" +LIBPNG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libpng_1.4.0-1_win32.zip libpng 1.4.0-1" PANGO="http://ftp.gnome.org/pub/gnome/binaries/win32/pango/1.26/pango_1.26.2-1_win32.zip Pango 1.26.2-1" ZLIB="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/zlib-1.2.3.zip zlib 1.2.3" -ALL="ATK CAIRO EXPAT FONTCONFIG FREETYPE GETTEXT GLIB GTK LIBJPEG LIBPNG LIBPNG2 LIBTIFF PANGO ZLIB" +ALL="ATK CAIRO EXPAT FONTCONFIG FREETYPE GETTEXT GLIB GTK LIBPNG PANGO ZLIB" -if [ ! -e $STAGE_DIR ]; then - mkdir $STAGE_DIR -fi +mkdir -p $STAGE_DIR cd $STAGE_DIR rm -rf $INSTALL_DIR
--- a/pidgin/win32/nsis/langmacros.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/langmacros.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -48,6 +48,7 @@ !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_DESKTOP_SHORTCUT_DESC ${CUR_LANG} !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_STARTMENU_SHORTCUT_DESC ${CUR_LANG} !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT DEBUG_SYMBOLS_SECTION_TITLE ${CUR_LANG} + !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT TRANSLATIONS_SECTION_TITLE ${CUR_LANG} ; Installer Finish Page !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_FINISH_VISIT_WEB_SITE ${CUR_LANG} @@ -65,37 +66,11 @@ ; Spellcheck Section Prompts !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SECTION_TITLE ${CUR_LANG} !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ERROR ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_DICT_ERROR ${CUR_LANG} !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SECTION_DESCRIPTION ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT ASPELL_INSTALL_FAILED ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_BRETON ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_CATALAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_CZECH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_WELSH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_DANISH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_GERMAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ENGLISH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_GREEK ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ESPERANTO ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SPANISH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_FAROESE ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_FRENCH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ITALIAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_DUTCH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_NORWEGIAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_POLISH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_PORTUGUESE ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ROMANIAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_RUSSIAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SLOVAK ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SWEDISH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_UKRAINIAN ${CUR_LANG} !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_DEBUGSYMBOLS_ERROR ${CUR_LANG} !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_GTK_DOWNLOAD_ERROR ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT TRANSLATIONS_SECTION_TITLE ${CUR_LANG} - !undef CUR_LANG !macroend
--- a/pidgin/win32/nsis/pidgin-installer.nsi Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Tue Mar 16 12:07:06 2010 +0900 @@ -54,7 +54,6 @@ ;Defines !define PIDGIN_NSIS_INCLUDE_PATH "." -!define PIDGIN_INSTALLER_DEPS "..\..\..\..\win32-dev\pidgin-inst-deps-20100223" ; Remove these and the stuff that uses them at some point !define OLD_GAIM_REG_KEY "SOFTWARE\gaim" @@ -72,8 +71,8 @@ !define PERL_REG_KEY "SOFTWARE\Perl" !define PERL_DLL "perl510.dll" -!define ASPELL_REG_KEY "SOFTWARE\Aspell" !define DOWNLOADER_URL "http://pidgin.im/win32/download_redir.php" +!define SPELL_DOWNLOAD_URL "http://ftp.services.openoffice.org/pub/OpenOffice.org/contrib/dictionaries" !define MEMENTO_REGISTRY_ROOT HKLM !define MEMENTO_REGISTRY_KEY "${PIDGIN_UNINSTALL_KEY}" @@ -303,7 +302,7 @@ IfErrors uninstall_problem ; Ready to uninstall.. ClearErrors - ExecWait '"$TEMP\$R6" /S /KEEPGTK=1 _?=$R1' + ExecWait '"$TEMP\$R6" /S /UPGRADE=1 _?=$R1' IfErrors exec_error Delete "$TEMP\$R6" Goto done @@ -418,7 +417,6 @@ Delete "$INSTDIR\plugins\libjabber.dll" File /r /x locale ..\..\..\${PIDGIN_INSTALL_DIR}\*.* - File "${PIDGIN_INSTALLER_DEPS}\exchndl.dll" ; Check if Perl is installed, if so add it to the AppPaths ReadRegStr $R2 HKLM ${PERL_REG_KEY} "" @@ -508,7 +506,7 @@ !macro LANG_SECTION lang ${MementoUnselectedSection} "${lang}" SecLang_${lang} SetOutPath "$INSTDIR\locale\${lang}\LC_MESSAGES" - File /oname=pidgin.mo "..\..\..\${PIDGIN_INSTALL_DIR}\locale\${lang}\LC_MESSAGES\pidgin.mo" + File "..\..\..\${PIDGIN_INSTALL_DIR}\locale\${lang}\LC_MESSAGES\*.mo" SetOutPath "$INSTDIR" ${MementoSectionEnd} !macroend @@ -521,95 +519,15 @@ ;-------------------------------- ;Spell Checking -SectionGroup /e $(PIDGIN_SPELLCHECK_SECTION_TITLE) SecSpellCheck - Section /o $(PIDGIN_SPELLCHECK_BRETON) SecSpellCheckBreton - Push ${SecSpellCheckBreton} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_CATALAN) SecSpellCheckCatalan - Push ${SecSpellCheckCatalan} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_CZECH) SecSpellCheckCzech - Push ${SecSpellCheckCzech} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_WELSH) SecSpellCheckWelsh - Push ${SecSpellCheckWelsh} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_DANISH) SecSpellCheckDanish - Push ${SecSpellCheckDanish} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_GERMAN) SecSpellCheckGerman - Push ${SecSpellCheckGerman} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_GREEK) SecSpellCheckGreek - Push ${SecSpellCheckGreek} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_ENGLISH) SecSpellCheckEnglish - Push ${SecSpellCheckEnglish} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_ESPERANTO) SecSpellCheckEsperanto - Push ${SecSpellCheckEsperanto} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_SPANISH) SecSpellCheckSpanish - Push ${SecSpellCheckSpanish} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_FAROESE) SecSpellCheckFaroese - Push ${SecSpellCheckFaroese} - Call InstallAspellAndDict +!macro SPELLCHECK_SECTION lang lang_name lang_file + Section /o "${lang_name}" SecSpell_${lang} + Push ${lang_file} + Push ${lang} + Call InstallDict SectionEnd - Section /o $(PIDGIN_SPELLCHECK_FRENCH) SecSpellCheckFrench - Push ${SecSpellCheckFrench} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_ITALIAN) SecSpellCheckItalian - Push ${SecSpellCheckItalian} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_DUTCH) SecSpellCheckDutch - Push ${SecSpellCheckDutch} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_NORWEGIAN) SecSpellCheckNorwegian - Push ${SecSpellCheckNorwegian} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_POLISH) SecSpellCheckPolish - Push ${SecSpellCheckPolish} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_PORTUGUESE) SecSpellCheckPortuguese - Push ${SecSpellCheckPortuguese} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_ROMANIAN) SecSpellCheckRomanian - Push ${SecSpellCheckRomanian} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_RUSSIAN) SecSpellCheckRussian - Push ${SecSpellCheckRussian} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_SLOVAK) SecSpellCheckSlovak - Push ${SecSpellCheckSlovak} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_SWEDISH) SecSpellCheckSwedish - Push ${SecSpellCheckSwedish} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_UKRAINIAN) SecSpellCheckUkrainian - Push ${SecSpellCheckUkrainian} - Call InstallAspellAndDict - SectionEnd +!macroend +SectionGroup $(PIDGIN_SPELLCHECK_SECTION_TITLE) SecSpellCheck + !include "pidgin-spellcheck.nsh" SectionGroupEnd Section /o $(DEBUG_SYMBOLS_SECTION_TITLE) SecDebugSymbols @@ -773,9 +691,16 @@ Delete "$INSTDIR\sounds\purple\send.wav" RMDir "$INSTDIR\sounds\purple" RMDir "$INSTDIR\sounds" + Delete "$INSTDIR\spellcheck\libenchant.dll" + Delete "$INSTDIR\spellcheck\libgtkspell-0.dll" + Delete "$INSTDIR\spellcheck\lib\enchant\libenchant_aspell.dll" + Delete "$INSTDIR\spellcheck\lib\enchant\libenchant_ispell.dll" + Delete "$INSTDIR\spellcheck\lib\enchant\libenchant_myspell.dll" + RMDir "$INSTDIR\spellcheck\lib\enchant" + RMDir "$INSTDIR\spellcheck\lib" + RMDir "$INSTDIR\spellcheck" Delete "$INSTDIR\freebl3.dll" Delete "$INSTDIR\idletrack.dll" - Delete "$INSTDIR\libgtkspell.dll" Delete "$INSTDIR\libjabber.dll" Delete "$INSTDIR\libnspr4.dll" Delete "$INSTDIR\libmeanwhile-1.dll" @@ -807,10 +732,13 @@ ; Remove the local GTK+ copy (if we're not just upgrading) ${GetParameters} $R0 ClearErrors - ${GetOptions} "$R0" "/KEEPGTK=" $R1 + ${GetOptions} "$R0" "/UPGRADE=" $R1 IfErrors +2 - StrCmp $R1 "1" +2 + StrCmp $R1 "1" upgrade_done RMDir /r "$INSTDIR\Gtk" + ; Remove the downloaded spellcheck dictionaries (if we're not just upgrading) + RMDir /r "$INSTDIR\spellcheck" + upgrade_done: ;Try to remove Pidgin install dir (only if empty) RMDir "$INSTDIR" @@ -849,50 +777,7 @@ !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheck} \ $(PIDGIN_SPELLCHECK_SECTION_DESCRIPTION) - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckBreton} \ - "$(PIDGIN_SPELLCHECK_BRETON) (862kb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckCatalan} \ - "$(PIDGIN_SPELLCHECK_CATALAN) (3.9Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckCzech} \ - "$(PIDGIN_SPELLCHECK_CZECH) (17Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckWelsh} \ - "$(PIDGIN_SPELLCHECK_WELSH) (4.2Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckDanish} \ - "$(PIDGIN_SPELLCHECK_DANISH) (6.9Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckGerman} \ - "$(PIDGIN_SPELLCHECK_GERMAN) (5.4Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckGreek} \ - "$(PIDGIN_SPELLCHECK_GREEK) (7.1Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckEnglish} \ - "$(PIDGIN_SPELLCHECK_ENGLISH) (2.3Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckEsperanto} \ - "$(PIDGIN_SPELLCHECK_ESPERANTO) (5.7Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckSpanish} \ - "$(PIDGIN_SPELLCHECK_SPANISH) (7.0Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckFaroese} \ - "$(PIDGIN_SPELLCHECK_FAROESE) (913kb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckFrench} \ - "$(PIDGIN_SPELLCHECK_FRENCH) (9.3Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckItalian} \ - "$(PIDGIN_SPELLCHECK_ITALIAN) (770kb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckDutch} \ - "$(PIDGIN_SPELLCHECK_DUTCH) (3.7Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckNorwegian} \ - "$(PIDGIN_SPELLCHECK_NORWEGIAN) (3.2Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckPolish} \ - "$(PIDGIN_SPELLCHECK_POLISH) (9.3Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckPortuguese} \ - "$(PIDGIN_SPELLCHECK_PORTUGUESE) (5.5Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckRomanian} \ - "$(PIDGIN_SPELLCHECK_ROMANIAN) (906kb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckRussian} \ - "$(PIDGIN_SPELLCHECK_RUSSIAN) (11Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckSlovak} \ - "$(PIDGIN_SPELLCHECK_SLOVAK) (8.0Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckSwedish} \ - "$(PIDGIN_SPELLCHECK_SWEDISH) (2.2Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckUkrainian} \ - "$(PIDGIN_SPELLCHECK_UKRAINIAN) (12Mb)" + !insertmacro MUI_FUNCTION_DESCRIPTION_END ;-------------------------------- @@ -1239,12 +1124,6 @@ ${MementoSectionRestore} - !insertmacro SetSectionFlag ${SecSpellCheck} ${SF_RO} - !insertmacro UnselectSection ${SecSpellCheck} - - ;Mark the dictionaries that are already installed as readonly - Call SelectAndDisableInstalledDictionaries - ;Preselect the URI handlers as appropriate Call SelectURIHandlerSelections @@ -1343,6 +1222,10 @@ StrCpy $INSTDIR "$R2\Pidgin" instdir_done: + + ;Mark the dictionaries that are already installed as readonly + Call SelectAndDisableInstalledDictionaries + ;LogSet on Pop $R3 Pop $R2 @@ -1399,234 +1282,72 @@ ; Convert the a Section index to the language code ; Push the section index onto the stack and pop off the language code after the call ; This will set the error code, if no match is found -Function GetLangCodeForSection - ClearErrors - Push $R0 - Exch - Pop $R0 ;This is the section index - - IntCmp $R0 ${SecSpellCheckBreton} 0 +3 +3 - StrCpy $R0 "br" - Goto done - IntCmp $R0 ${SecSpellCheckCatalan} 0 +3 +3 - StrCpy $R0 "ca" - Goto done - IntCmp $R0 ${SecSpellCheckCzech} 0 +3 +3 - StrCpy $R0 "cs" - Goto done - IntCmp $R0 ${SecSpellCheckWelsh} 0 +3 +3 - StrCpy $R0 "cy" - Goto done - IntCmp $R0 ${SecSpellCheckDanish} 0 +3 +3 - StrCpy $R0 "da" - Goto done - IntCmp $R0 ${SecSpellCheckGerman} 0 +3 +3 - StrCpy $R0 "de" - Goto done - IntCmp $R0 ${SecSpellCheckGreek} 0 +3 +3 - StrCpy $R0 "el" - Goto done - IntCmp $R0 ${SecSpellCheckEnglish} 0 +3 +3 - StrCpy $R0 "en" - Goto done - IntCmp $R0 ${SecSpellCheckEsperanto} 0 +3 +3 - StrCpy $R0 "eo" - Goto done - IntCmp $R0 ${SecSpellCheckSpanish} 0 +3 +3 - StrCpy $R0 "es" - Goto done - IntCmp $R0 ${SecSpellCheckFaroese} 0 +3 +3 - StrCpy $R0 "fo" - Goto done - IntCmp $R0 ${SecSpellCheckFrench} 0 +3 +3 - StrCpy $R0 "fr" - Goto done - IntCmp $R0 ${SecSpellCheckItalian} 0 +3 +3 - StrCpy $R0 "it" - Goto done - IntCmp $R0 ${SecSpellCheckDutch} 0 +3 +3 - StrCpy $R0 "nl" - Goto done - IntCmp $R0 ${SecSpellCheckNorwegian} 0 +3 +3 - StrCpy $R0 "no" - Goto done - IntCmp $R0 ${SecSpellCheckPolish} 0 +3 +3 - StrCpy $R0 "pl" - Goto done - IntCmp $R0 ${SecSpellCheckPortuguese} 0 +3 +3 - StrCpy $R0 "pt" - Goto done - IntCmp $R0 ${SecSpellCheckRomanian} 0 +3 +3 - StrCpy $R0 "ro" - Goto done - IntCmp $R0 ${SecSpellCheckRussian} 0 +3 +3 - StrCpy $R0 "ru" - Goto done - IntCmp $R0 ${SecSpellCheckSlovak} 0 +3 +3 - StrCpy $R0 "sk" - Goto done - IntCmp $R0 ${SecSpellCheckSwedish} 0 +3 +3 - StrCpy $R0 "sv" - Goto done - IntCmp $R0 ${SecSpellCheckUkrainian} 0 +3 +3 - StrCpy $R0 "uk" - Goto done - - SetErrors - - done: - Exch $R0 -FunctionEnd ;GetLangCodeForSection ; Select and Disable any Sections that have currently installed dictionaries +!macro CHECK_SPELLCHECK_SECTION lang + ;Advance to the next (correct) section index + IntOp $R0 $R0 + 1 + IfFileExists "$INSTDIR\spellcheck\share\enchant\myspell\${lang}.dic" 0 done_${lang} + SectionGetFlags $R0 $R1 + IntOp $R1 $R1 | ${SF_RO} ; Mark Readonly + IntOp $R1 $R1 | ${SF_SELECTED} ; Select + SectionSetFlags $R0 $R1 + done_${lang}: +!macroend Function SelectAndDisableInstalledDictionaries Push $R0 Push $R1 - Push $R2 - - ; Start with the first language dictionary - IntOp $R0 ${SecSpellCheck} + 1 - - start: - ; If it is the end of the section group, stop - SectionGetFlags $R0 $R1 - IntOp $R2 $R1 & ${SF_SECGRPEND} - IntCmp $R2 ${SF_SECGRPEND} done - - Push $R0 - Call GetLangCodeForSection - Pop $R2 - IfErrors end_loop - ReadRegStr $R2 HKLM "${ASPELL_REG_KEY}-$R2" "" ; Check that the dictionary is installed - StrCmp $R2 "" end_loop ; If it isn't installed, skip to the next item - IntOp $R1 $R1 | ${SF_RO} ; Mark Readonly - IntOp $R1 $R1 | ${SF_SELECTED} ; Select - SectionSetFlags $R0 $R1 - - end_loop: - IntOp $R0 $R0 + 1 ;Advance to the next section - Goto start - done: - Pop $R2 - Pop $R1 - Pop $R0 -FunctionEnd - -Function InstallAspellAndDict - Push $R0 - Exch - Call GetLangCodeForSection - Pop $R0 ;This is the language code - Push $R1 - - InitPluginsDir - - IfErrors done ; We weren't able to convert the section to lang code + !insertmacro SetSectionFlag ${SecSpellCheck} ${SF_RO} + !insertmacro UnselectSection ${SecSpellCheck} - retry: - Call InstallAspell - Pop $R1 - StrCmp $R1 "" +3 - StrCmp $R1 "cancel" done - MessageBox MB_RETRYCANCEL "$(PIDGIN_SPELLCHECK_ERROR) : $R1" /SD IDCANCEL IDRETRY retry IDCANCEL done - - retry_dict: - Push $R0 - Call InstallAspellDictionary - Pop $R1 - StrCmp $R1 "" +3 - StrCmp $R1 "cancel" done - MessageBox MB_RETRYCANCEL "$(PIDGIN_SPELLCHECK_DICT_ERROR) : $R1" /SD IDCANCEL IDRETRY retry_dict - - done: + IntOp $R0 ${SecSpellCheck} + 0 + !include "pidgin-spellcheck-preselect.nsh" Pop $R1 Pop $R0 FunctionEnd -Function InstallAspell - Push $R0 - Push $R1 - Push $R2 - - check: - ClearErrors - ReadRegDWORD $R0 HKLM ${ASPELL_REG_KEY} "AspellVersion" - IntCmp $R0 15 installed - - ; If this is the check after installation, don't infinite loop on failure - StrCmp $R1 "$PLUGINSDIR\aspell_installer.exe" 0 +3 - StrCpy $R0 $(ASPELL_INSTALL_FAILED) - Goto done - - ; We need to download and install aspell - StrCpy $R1 "$PLUGINSDIR\aspell_installer.exe" - StrCpy $R2 "${DOWNLOADER_URL}?version=${PIDGIN_VERSION}&dl_pkg=aspell_core" - DetailPrint "Downloading Aspell... ($R2)" - NSISdl::download /TIMEOUT=10000 $R2 $R1 - Pop $R0 - StrCmp $R0 "success" +2 - Goto done - ExecWait '"$R1"' - Delete $R1 - Goto check ; Check that it is now installed correctly - - installed: ;Aspell is currently installed, no error message - DetailPrint "Aspell is installed" - StrCpy $R0 '' - - done: - Pop $R2 - Pop $R1 - Exch $R0 -FunctionEnd - -Function InstallAspellDictionary +Function InstallDict Push $R0 Exch Pop $R0 ;This is the language code Push $R1 + Exch 2 + Pop $R1 ;This is the language file Push $R2 Push $R3 - Push $R4 - check: ClearErrors - ReadRegStr $R2 HKLM "${ASPELL_REG_KEY}-$R0" "" - StrCmp $R2 "" 0 installed + IfFileExists "$INSTDIR\spellcheck\share\enchant\myspell\$R0.dic" installed + + InitPluginsDir - ; If this is the check after installation, don't infinite loop on failure - StrCmp $R1 "$PLUGINSDIR\aspell_dict-$R0.exe" 0 +3 - StrCpy $R0 $(ASPELL_INSTALL_FAILED) + ; We need to download and install dictionary + StrCpy $R2 "$PLUGINSDIR\$R1" + StrCpy $R3 "${SPELL_DOWNLOAD_URL}/$R1" + DetailPrint "Downloading the $R0 Dictionary... ($R3)" + retry: + NSISdl::download /TIMEOUT=10000 "$R3" "$R2" + Pop $R3 + StrCmp $R3 "cancel" done + StrCmp $R3 "success" +3 + MessageBox MB_RETRYCANCEL "$(PIDGIN_SPELLCHECK_ERROR) : $R3" /SD IDCANCEL IDRETRY retry IDCANCEL done + Goto done + SetOutPath "$INSTDIR\spellcheck\share\enchant\myspell" + nsisunz::UnzipToLog "$R2" "$OUTDIR" + SetOutPath "$INSTDIR" + Pop $R3 + StrCmp $R3 "success" installed + DetailPrint "$R3" ;print error message to log Goto done - ; We need to download and install aspell - StrCpy $R1 "$PLUGINSDIR\aspell_dict-$R0.exe" - StrCpy $R3 "${DOWNLOADER_URL}?version=${PIDGIN_VERSION}&dl_pkg=lang_$R0" - DetailPrint "Downloading the Aspell $R0 Dictionary... ($R3)" - NSISdl::download /TIMEOUT=10000 $R3 $R1 - Pop $R3 - StrCmp $R3 "success" +3 - StrCpy $R0 $R3 - Goto done - ; Use a specific temporary $OUTDIR for each dictionary because the installer doesn't clean up after itself - StrCpy $R4 "$OUTDIR" - SetOutPath "$PLUGINSDIR\aspell_dict-$R0" - ExecWait '"$R1"' - SetOutPath "$R4" - RMDir /r "$PLUGINSDIR\aspell_dict-$R0" - Delete $R1 - Goto check ; Check that it is now installed correctly - installed: ;The dictionary is currently installed, no error message - DetailPrint "Aspell $R0 Dictionary is installed" - StrCpy $R0 '' + DetailPrint "$R0 Dictionary is installed" done: - Pop $R4 Pop $R3 Pop $R2 - Pop $R1 - Exch $R0 + Pop $R0 + Exch $R1 FunctionEnd
--- a/pidgin/win32/nsis/translations/afrikaans.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/afrikaans.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -49,29 +49,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Speltoets-ondersteuning" !define PIDGIN_SPELLCHECK_ERROR "Fout met installering van speltoetser" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Fout met installering van speltoetswoordeboek" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Ondersteuning vir speltoeter. (Internetverbinding benodigd vir installasie)" -!define ASPELL_INSTALL_FAILED "Installasie het misluk" -!define PIDGIN_SPELLCHECK_BRETON "Bretons" -!define PIDGIN_SPELLCHECK_CATALAN "Katalaans" -!define PIDGIN_SPELLCHECK_CZECH "Tsjeggies" -!define PIDGIN_SPELLCHECK_WELSH "Wallies" -!define PIDGIN_SPELLCHECK_DANISH "Deens" -!define PIDGIN_SPELLCHECK_GERMAN "Duits" -!define PIDGIN_SPELLCHECK_GREEK "Grieks" -!define PIDGIN_SPELLCHECK_ENGLISH "Engels" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Spaans" -!define PIDGIN_SPELLCHECK_FAROESE "Faroes" -!define PIDGIN_SPELLCHECK_FRENCH "Frans" -!define PIDGIN_SPELLCHECK_ITALIAN "Italiaans" -!define PIDGIN_SPELLCHECK_DUTCH "Nederlands" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Noorweegs" -!define PIDGIN_SPELLCHECK_POLISH "Pools" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugees" -!define PIDGIN_SPELLCHECK_ROMANIAN "Roemeens" -!define PIDGIN_SPELLCHECK_RUSSIAN "Russies" -!define PIDGIN_SPELLCHECK_SLOVAK "Slowaaks" -!define PIDGIN_SPELLCHECK_SWEDISH "Sweeds" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Oekraens"
--- a/pidgin/win32/nsis/translations/arabic.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/arabic.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -47,29 +47,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "マレ ヌ睫マ゙゙ ヌ眷聶ヌニ" !define PIDGIN_SPELLCHECK_ERROR "ホリテ テヒ萇チ ハヒネハ ヌ睫マ゙゙ ヌ眷聶ヌニ" -!define PIDGIN_SPELLCHECK_DICT_ERROR "ホリテ テヒ萇チ ハヒネハ ゙ヌ肆モ ヌ睫マ゙゙ ヌ眷聶ヌニ" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "マレ ヌ睫マ゙゙ ヌ眷聶ヌニ. (聒礦ネ ヌハユヌ ネヌ眷萍ム萍 矣ハヒネハ)" -!define ASPELL_INSTALL_FAILED "ンヤ ヌ睫ヒネハ" -!define PIDGIN_SPELLCHECK_BRETON "Breton" -!define PIDGIN_SPELLCHECK_CATALAN "Catalan" -!define PIDGIN_SPELLCHECK_CZECH "Czech" -!define PIDGIN_SPELLCHECK_WELSH "Welsh" -!define PIDGIN_SPELLCHECK_DANISH "Danish" -!define PIDGIN_SPELLCHECK_GERMAN "German" -!define PIDGIN_SPELLCHECK_GREEK "Greek" -!define PIDGIN_SPELLCHECK_ENGLISH "English" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Spanish" -!define PIDGIN_SPELLCHECK_FAROESE "Faroese" -!define PIDGIN_SPELLCHECK_FRENCH "French" -!define PIDGIN_SPELLCHECK_ITALIAN "Italian" -!define PIDGIN_SPELLCHECK_DUTCH "Dutch" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norwegian" -!define PIDGIN_SPELLCHECK_POLISH "Polish" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portuguese" -!define PIDGIN_SPELLCHECK_ROMANIAN "Romanian" -!define PIDGIN_SPELLCHECK_RUSSIAN "Russian" -!define PIDGIN_SPELLCHECK_SLOVAK "Slovak" -!define PIDGIN_SPELLCHECK_SWEDISH "Swedish" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrainian"
--- a/pidgin/win32/nsis/translations/basque.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/basque.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -47,29 +47,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Zuzentzaile Ortografikoa" !define PIDGIN_SPELLCHECK_ERROR "Errorea Zuzentzaile Ortografikoa instalatzean" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Errorea Zuzentzaile Ortografikoarentzako hiztegia instalatzean" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Zuzentzaile Ortografikoa. (Internet konexioa behar du instalatzeko)" -!define ASPELL_INSTALL_FAILED "Ezin izan da instalatu" -!define PIDGIN_SPELLCHECK_BRETON "Britaniera" -!define PIDGIN_SPELLCHECK_CATALAN "Katalana" -!define PIDGIN_SPELLCHECK_CZECH "Txekiera" -!define PIDGIN_SPELLCHECK_WELSH "Gaelikoa" -!define PIDGIN_SPELLCHECK_DANISH "Daniera" -!define PIDGIN_SPELLCHECK_GERMAN "Alemana" -!define PIDGIN_SPELLCHECK_GREEK "Grekoa" -!define PIDGIN_SPELLCHECK_ENGLISH "Ingelesa" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperantoa" -!define PIDGIN_SPELLCHECK_SPANISH "Gaztelania" -!define PIDGIN_SPELLCHECK_FAROESE "Faroera" -!define PIDGIN_SPELLCHECK_FRENCH "Frantsesa" -!define PIDGIN_SPELLCHECK_ITALIAN "Italiera" -!define PIDGIN_SPELLCHECK_DUTCH "Nederlandera" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norvegiera" -!define PIDGIN_SPELLCHECK_POLISH "Poloniera" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugesa" -!define PIDGIN_SPELLCHECK_ROMANIAN "Errumaniera" -!define PIDGIN_SPELLCHECK_RUSSIAN "Errusiera" -!define PIDGIN_SPELLCHECK_SLOVAK "Eslovakiera" -!define PIDGIN_SPELLCHECK_SWEDISH "Suediera" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukraniera"
--- a/pidgin/win32/nsis/translations/catalan.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/catalan.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -46,29 +46,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Suport a la Verificaci de l'Ortografia " !define PIDGIN_SPELLCHECK_ERROR "Error instal.lant verificaci de l'ortografia" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Error Instal.lant Diccionari per a Verificaci de l'Ortografia" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Suport per a Verificaci de l'Ortografia. (駸 necesaria connexi a internet per dur a terme la instal.laci)" -!define ASPELL_INSTALL_FAILED "La instal.laci ha fallat" -!define PIDGIN_SPELLCHECK_BRETON "Bret" -!define PIDGIN_SPELLCHECK_CATALAN "Catal" -!define PIDGIN_SPELLCHECK_CZECH "Txec" -!define PIDGIN_SPELLCHECK_WELSH "Galキl鑚" -!define PIDGIN_SPELLCHECK_DANISH "Dan鑚" -!define PIDGIN_SPELLCHECK_GERMAN "Alemany" -!define PIDGIN_SPELLCHECK_GREEK "Grec" -!define PIDGIN_SPELLCHECK_ENGLISH "Angl鑚" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Espanyol" -!define PIDGIN_SPELLCHECK_FAROESE "Fero鑚" -!define PIDGIN_SPELLCHECK_FRENCH "Franc鑚" -!define PIDGIN_SPELLCHECK_ITALIAN "Itali" -!define PIDGIN_SPELLCHECK_DUTCH "Holand鑚" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Noruec" -!define PIDGIN_SPELLCHECK_POLISH "Polon鑚" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugu鑚" -!define PIDGIN_SPELLCHECK_ROMANIAN "Roman鑚" -!define PIDGIN_SPELLCHECK_RUSSIAN "Rus" -!define PIDGIN_SPELLCHECK_SLOVAK "Eslovac" -!define PIDGIN_SPELLCHECK_SWEDISH "Suec" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ucran鑚"
--- a/pidgin/win32/nsis/translations/dutch.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/dutch.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -39,28 +39,4 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Spellingscontrole" !define PIDGIN_SPELLCHECK_ERROR "Fout bij installatie van spellingscontrole" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Fout bij installatie van woordenboek voor spellingscontrole" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Ondersteuning voor spellingscontrole. (Internetverbinding nodig voor installatie)" -!define ASPELL_INSTALL_FAILED "Installatie mislukt" -!define PIDGIN_SPELLCHECK_BRETON "Bretons" -!define PIDGIN_SPELLCHECK_CATALAN "Catalaans" -!define PIDGIN_SPELLCHECK_CZECH "Tsjechisch" -!define PIDGIN_SPELLCHECK_WELSH "Welsh" -!define PIDGIN_SPELLCHECK_DANISH "Deens" -!define PIDGIN_SPELLCHECK_GERMAN "Duits" -!define PIDGIN_SPELLCHECK_GREEK "Grieks" -!define PIDGIN_SPELLCHECK_ENGLISH "Engels" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Spaans" -!define PIDGIN_SPELLCHECK_FAROESE "Faroese" -!define PIDGIN_SPELLCHECK_FRENCH "Frans" -!define PIDGIN_SPELLCHECK_ITALIAN "Italiaans" -!define PIDGIN_SPELLCHECK_DUTCH "Nederlands" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Noors" -!define PIDGIN_SPELLCHECK_POLISH "Pools" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugees" -!define PIDGIN_SPELLCHECK_ROMANIAN "Roemeens" -!define PIDGIN_SPELLCHECK_RUSSIAN "Russisch" -!define PIDGIN_SPELLCHECK_SLOVAK "Slowaaks" -!define PIDGIN_SPELLCHECK_SWEDISH "Zweeds" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Oekrans"
--- a/pidgin/win32/nsis/translations/english.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/english.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -54,31 +54,7 @@ ; Spellcheck Section Prompts !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SECTION_TITLE "Spellchecking Support" !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ERROR "Error Installing Spellchecking" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_DICT_ERROR "Error Installing Spellchecking Dictionary" !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Support for Spellchecking. (Internet connection required for installation)" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING ASPELL_INSTALL_FAILED "Installation Failed" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_BRETON "Breton" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_CATALAN "Catalan" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_CZECH "Czech" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_WELSH "Welsh" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_DANISH "Danish" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_GERMAN "German" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_GREEK "Greek" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ENGLISH "English" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SPANISH "Spanish" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_FAROESE "Faroese" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_FRENCH "French" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ITALIAN "Italian" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_DUTCH "Dutch" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_NORWEGIAN "Norwegian" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_POLISH "Polish" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_PORTUGUESE "Portuguese" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ROMANIAN "Romanian" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_RUSSIAN "Russian" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SLOVAK "Slovak" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SWEDISH "Swedish" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_UKRAINIAN "Ukrainian" !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_DEBUGSYMBOLS_ERROR "Error Installing Debug Symbols"
--- a/pidgin/win32/nsis/translations/finnish.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/finnish.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -48,29 +48,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Oikolukutuki" !define PIDGIN_SPELLCHECK_ERROR "Virhe asennettaessa oikolukua" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Virhe asennettaessa oikoluvun sanakirjaa" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Tuki oikoluvulle. (Asennukseen tarvitaan Internet-yhteys)" -!define ASPELL_INSTALL_FAILED "Asennus ep與nnistui" -!define PIDGIN_SPELLCHECK_BRETON "bretoni" -!define PIDGIN_SPELLCHECK_CATALAN "katalaani" -!define PIDGIN_SPELLCHECK_CZECH "tshekki" -!define PIDGIN_SPELLCHECK_WELSH "kymri" -!define PIDGIN_SPELLCHECK_DANISH "tanska" -!define PIDGIN_SPELLCHECK_GERMAN "saksa" -!define PIDGIN_SPELLCHECK_GREEK "kreikka" -!define PIDGIN_SPELLCHECK_ENGLISH "englanti" -!define PIDGIN_SPELLCHECK_ESPERANTO "esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "espanja" -!define PIDGIN_SPELLCHECK_FAROESE "f蒿ri" -!define PIDGIN_SPELLCHECK_FRENCH "ranska" -!define PIDGIN_SPELLCHECK_ITALIAN "italia" -!define PIDGIN_SPELLCHECK_DUTCH "hollanti" -!define PIDGIN_SPELLCHECK_NORWEGIAN "norja" -!define PIDGIN_SPELLCHECK_POLISH "puola" -!define PIDGIN_SPELLCHECK_PORTUGUESE "portugali" -!define PIDGIN_SPELLCHECK_ROMANIAN "romania" -!define PIDGIN_SPELLCHECK_RUSSIAN "ven臻" -!define PIDGIN_SPELLCHECK_SLOVAK "slovakia" -!define PIDGIN_SPELLCHECK_SWEDISH "ruotsi" -!define PIDGIN_SPELLCHECK_UKRAINIAN "ukraina"
--- a/pidgin/win32/nsis/translations/french.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/french.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -52,28 +52,4 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Correction orthographique" !define PIDGIN_SPELLCHECK_ERROR "Erreur l'installation du correcteur orthographique" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Erreur l'installation du dictionnaire pour le correcteur orthographique" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Correction orthogaphique. (Une connexion internet est n馗essaire pour son installation)" -!define ASPELL_INSTALL_FAILED "ノchec de l'installation" -!define PIDGIN_SPELLCHECK_BRETON "Breton" -!define PIDGIN_SPELLCHECK_CATALAN "Catalan" -!define PIDGIN_SPELLCHECK_CZECH "Tch鑷ue" -!define PIDGIN_SPELLCHECK_WELSH "Gallois" -!define PIDGIN_SPELLCHECK_DANISH "Danois" -!define PIDGIN_SPELLCHECK_GERMAN "Allemand" -!define PIDGIN_SPELLCHECK_GREEK "Grec" -!define PIDGIN_SPELLCHECK_ENGLISH "Anglais" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esp駻anto" -!define PIDGIN_SPELLCHECK_SPANISH "Espagnol" -!define PIDGIN_SPELLCHECK_FAROESE "F駻ingien" -!define PIDGIN_SPELLCHECK_FRENCH "Fran軋is" -!define PIDGIN_SPELLCHECK_ITALIAN "Italien" -!define PIDGIN_SPELLCHECK_DUTCH "Hollandais" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norv馮ien" -!define PIDGIN_SPELLCHECK_POLISH "Polonais" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugais" -!define PIDGIN_SPELLCHECK_ROMANIAN "Roumain" -!define PIDGIN_SPELLCHECK_RUSSIAN "Russe" -!define PIDGIN_SPELLCHECK_SLOVAK "Slovaque" -!define PIDGIN_SPELLCHECK_SWEDISH "Su馘ois" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrainien"
--- a/pidgin/win32/nsis/translations/german.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/german.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -50,28 +50,4 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Untersttzung fr Rechtschreibkontrolle" !define PIDGIN_SPELLCHECK_ERROR "Fehler bei der Installation der Rechtschreibkontrolle" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Fehler bei der Installation des Wrterbuches fr die Rechtschreibkontrolle" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Untersttzung fr Rechtschreibkontrolle. (Fr die Installation ist eine Internet-Verbindung ntig)" -!define ASPELL_INSTALL_FAILED "Installation gescheitert" -!define PIDGIN_SPELLCHECK_BRETON "Bretonisch" -!define PIDGIN_SPELLCHECK_CATALAN "Katalanisch" -!define PIDGIN_SPELLCHECK_CZECH "Tschechisch" -!define PIDGIN_SPELLCHECK_WELSH "Walisisch" -!define PIDGIN_SPELLCHECK_DANISH "D舅isch" -!define PIDGIN_SPELLCHECK_GERMAN "Deutsch" -!define PIDGIN_SPELLCHECK_GREEK "Griechisch" -!define PIDGIN_SPELLCHECK_ENGLISH "Englisch" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Spanisch" -!define PIDGIN_SPELLCHECK_FAROESE "Farersprache" -!define PIDGIN_SPELLCHECK_FRENCH "Franzsisch" -!define PIDGIN_SPELLCHECK_ITALIAN "Italienisch" -!define PIDGIN_SPELLCHECK_DUTCH "Holl舅disch" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norwegisch" -!define PIDGIN_SPELLCHECK_POLISH "Polnisch" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugiesisch" -!define PIDGIN_SPELLCHECK_ROMANIAN "Rum舅isch" -!define PIDGIN_SPELLCHECK_RUSSIAN "Russisch" -!define PIDGIN_SPELLCHECK_SLOVAK "Slowakisch" -!define PIDGIN_SPELLCHECK_SWEDISH "Schwedisch" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrainisch"
--- a/pidgin/win32/nsis/translations/hebrew.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/hebrew.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -50,29 +50,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "鴉 矣肓褝 琺褝" !define PIDGIN_SPELLCHECK_ERROR "粳琅 砌 矼鴒褝 琺褝" -!define PIDGIN_SPELLCHECK_DICT_ERROR "粳琅 砌 鴈襃 矼鴒褝 琺褝" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "鴉 砒 矼鴒褝 琺褝 (肄 鉉砒 琺顏 蕣)" -!define ASPELL_INSTALL_FAILED "蒿 " -!define PIDGIN_SPELLCHECK_BRETON "碾韃" -!define PIDGIN_SPELLCHECK_CATALAN "韲褞鴾" -!define PIDGIN_SPELLCHECK_CZECH "'鴾" -!define PIDGIN_SPELLCHECK_WELSH "裹鴾" -!define PIDGIN_SPELLCHECK_DANISH "胙鴾" -!define PIDGIN_SPELLCHECK_GERMAN "糲鴾" -!define PIDGIN_SPELLCHECK_GREEK "鱧褞鴾" -!define PIDGIN_SPELLCHECK_ENGLISH "瑩粮鴾" -!define PIDGIN_SPELLCHECK_ESPERANTO "瑰韃" -!define PIDGIN_SPELLCHECK_SPANISH "肓" -!define PIDGIN_SPELLCHECK_FAROESE "鴾" -!define PIDGIN_SPELLCHECK_FRENCH "鴾" -!define PIDGIN_SPELLCHECK_ITALIAN "琺顆鴾" -!define PIDGIN_SPELLCHECK_DUTCH "蒟肓" -!define PIDGIN_SPELLCHECK_NORWEGIAN "褸裹粳" -!define PIDGIN_SPELLCHECK_POLISH "褌鴾" -!define PIDGIN_SPELLCHECK_PORTUGUESE "褸韃粫鴾" -!define PIDGIN_SPELLCHECK_ROMANIAN "褓鴾" -!define PIDGIN_SPELLCHECK_RUSSIAN "褥鴾" -!define PIDGIN_SPELLCHECK_SLOVAK "裹鴾" -!define PIDGIN_SPELLCHECK_SWEDISH "裹肓" -!define PIDGIN_SPELLCHECK_UKRAINIAN "瑯琺鴾"
--- a/pidgin/win32/nsis/translations/hungarian.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/hungarian.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -49,29 +49,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Helyesr疽-ellenrz駸 t疥ogat疽a" !define PIDGIN_SPELLCHECK_ERROR "Hiba a helyesr疽-ellenrz駸 telept駸e kzben" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Hiba a helyesr疽-ellenrz駸i szt疵 telept駸e kzben" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Helyesr疽-ellenrz駸 t疥ogat疽a. (Internetkapcsolat szks馮es a telept駸hez)" -!define ASPELL_INSTALL_FAILED "A telept駸 sikertelen" -!define PIDGIN_SPELLCHECK_BRETON "Breton" -!define PIDGIN_SPELLCHECK_CATALAN "Katal疣" -!define PIDGIN_SPELLCHECK_CZECH "Cseh" -!define PIDGIN_SPELLCHECK_WELSH "Walesi" -!define PIDGIN_SPELLCHECK_DANISH "D疣" -!define PIDGIN_SPELLCHECK_GERMAN "N駑et" -!define PIDGIN_SPELLCHECK_GREEK "Grg" -!define PIDGIN_SPELLCHECK_ENGLISH "Angol" -!define PIDGIN_SPELLCHECK_ESPERANTO "Eszperant" -!define PIDGIN_SPELLCHECK_SPANISH "Spanyol" -!define PIDGIN_SPELLCHECK_FAROESE "Farai" -!define PIDGIN_SPELLCHECK_FRENCH "Francia" -!define PIDGIN_SPELLCHECK_ITALIAN "Olasz" -!define PIDGIN_SPELLCHECK_DUTCH "Holland" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norv馮" -!define PIDGIN_SPELLCHECK_POLISH "Lengyel" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portug疝" -!define PIDGIN_SPELLCHECK_ROMANIAN "Rom疣" -!define PIDGIN_SPELLCHECK_RUSSIAN "Orosz" -!define PIDGIN_SPELLCHECK_SLOVAK "Szlov疚" -!define PIDGIN_SPELLCHECK_SWEDISH "Sv馘" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukr疣"
--- a/pidgin/win32/nsis/translations/italian.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/italian.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -49,29 +49,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Supporto per il correttore ortografico" !define PIDGIN_SPELLCHECK_ERROR "Errore nell'installazione del correttore ortografico" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Errore nell'installazione del dizionario per il correttore ortografico" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Supporto per il correttore ortografico. (ネ richiesta una connessione a internet per l'installazione)" -!define ASPELL_INSTALL_FAILED "Installazione fallita" -!define PIDGIN_SPELLCHECK_BRETON "Bretone" -!define PIDGIN_SPELLCHECK_CATALAN "Catalano" -!define PIDGIN_SPELLCHECK_CZECH "Ceco" -!define PIDGIN_SPELLCHECK_WELSH "Gallese" -!define PIDGIN_SPELLCHECK_DANISH "Danese" -!define PIDGIN_SPELLCHECK_GERMAN "Tedesco" -!define PIDGIN_SPELLCHECK_GREEK "Greco" -!define PIDGIN_SPELLCHECK_ENGLISH "Inglese" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Spagnolo" -!define PIDGIN_SPELLCHECK_FAROESE "Faroese" -!define PIDGIN_SPELLCHECK_FRENCH "Francese" -!define PIDGIN_SPELLCHECK_ITALIAN "Italiano" -!define PIDGIN_SPELLCHECK_DUTCH "Olandese" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norvegese" -!define PIDGIN_SPELLCHECK_POLISH "Polacco" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portoghese" -!define PIDGIN_SPELLCHECK_ROMANIAN "Rumeno" -!define PIDGIN_SPELLCHECK_RUSSIAN "Russo" -!define PIDGIN_SPELLCHECK_SLOVAK "Slovacco" -!define PIDGIN_SPELLCHECK_SWEDISH "Svedese" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ucraino"
--- a/pidgin/win32/nsis/translations/japanese.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/japanese.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -45,29 +45,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "スペルチェックのサポート" !define PIDGIN_SPELLCHECK_ERROR "スペルチェックのインストールに失敗しました" -!define PIDGIN_SPELLCHECK_DICT_ERROR "スペルチェック辞書のインストールに失敗しました。" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "スペルチェックのサポート (インターネット接続がインストールに必要です)" -!define ASPELL_INSTALL_FAILED "インストールに失敗しました" -!define PIDGIN_SPELLCHECK_BRETON "ブルターニュ語" -!define PIDGIN_SPELLCHECK_CATALAN "カタルーニャ語" -!define PIDGIN_SPELLCHECK_CZECH "チェコ語" -!define PIDGIN_SPELLCHECK_WELSH "ウェールズ語" -!define PIDGIN_SPELLCHECK_DANISH "デンマーク語" -!define PIDGIN_SPELLCHECK_GERMAN "ドイツ語" -!define PIDGIN_SPELLCHECK_GREEK "ギリシャ語" -!define PIDGIN_SPELLCHECK_ENGLISH "英語" -!define PIDGIN_SPELLCHECK_ESPERANTO "エスペラント語" -!define PIDGIN_SPELLCHECK_SPANISH "スペイン語" -!define PIDGIN_SPELLCHECK_FAROESE "フェロー語" -!define PIDGIN_SPELLCHECK_FRENCH "フランス語" -!define PIDGIN_SPELLCHECK_ITALIAN "イタリア語" -!define PIDGIN_SPELLCHECK_DUTCH "オランダ語" -!define PIDGIN_SPELLCHECK_NORWEGIAN "ノルウェー後" -!define PIDGIN_SPELLCHECK_POLISH "ポーランド語" -!define PIDGIN_SPELLCHECK_PORTUGUESE "ポルトガル語" -!define PIDGIN_SPELLCHECK_ROMANIAN "ルーマニア語" -!define PIDGIN_SPELLCHECK_RUSSIAN "ロシア語" -!define PIDGIN_SPELLCHECK_SLOVAK "スロヴァキア語" -!define PIDGIN_SPELLCHECK_SWEDISH "スウェーデン後" -!define PIDGIN_SPELLCHECK_UKRAINIAN "ウクライナ語"
--- a/pidgin/win32/nsis/translations/kurdish.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/kurdish.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -44,29 +44,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Desteka kontrola rastnivs" !define PIDGIN_SPELLCHECK_ERROR "Di sazkirina kontrola rastnivs de 軻wt derket." -!define PIDGIN_SPELLCHECK_DICT_ERROR "Di sazkirina ferhenga rastnivs de 軻wt derket." !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Desteka kontrola rastnivs. (Ji bo sazkirin nternet p黔st e)" -!define ASPELL_INSTALL_FAILED "Sazkirin Serneket" -!define PIDGIN_SPELLCHECK_BRETON "Breton" -!define PIDGIN_SPELLCHECK_CATALAN "Catalan" -!define PIDGIN_SPELLCHECK_CZECH "ヌek" -!define PIDGIN_SPELLCHECK_WELSH "Welsh" -!define PIDGIN_SPELLCHECK_DANISH "Danik" -!define PIDGIN_SPELLCHECK_GERMAN "Alman" -!define PIDGIN_SPELLCHECK_GREEK "Yewnan" -!define PIDGIN_SPELLCHECK_ENGLISH "ホngilz" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Span" -!define PIDGIN_SPELLCHECK_FAROESE "Faroese" -!define PIDGIN_SPELLCHECK_FRENCH "Frans" -!define PIDGIN_SPELLCHECK_ITALIAN "ホtal" -!define PIDGIN_SPELLCHECK_DUTCH "Dutch" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norwec" -!define PIDGIN_SPELLCHECK_POLISH "Pol" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portekiz" -!define PIDGIN_SPELLCHECK_ROMANIAN "Roman" -!define PIDGIN_SPELLCHECK_RUSSIAN "Rus" -!define PIDGIN_SPELLCHECK_SLOVAK "Slovak" -!define PIDGIN_SPELLCHECK_SWEDISH "Sw鹽" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrayn"
--- a/pidgin/win32/nsis/translations/lithuanian.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/lithuanian.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -47,29 +47,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Raybos tikrinimo palaikymas" !define PIDGIN_SPELLCHECK_ERROR "Raybos tikrinimo palaikymo diegimo klaida" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Raybos tikrinimo odyno diegimo klaida" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Raybos tikrinimo palaikymas. (Diegimui btina interneto jungtis)" -!define ASPELL_INSTALL_FAILED "Diegimas nepavyko" -!define PIDGIN_SPELLCHECK_BRETON "Breton kalba" -!define PIDGIN_SPELLCHECK_CATALAN "Katalon kalba" -!define PIDGIN_SPELLCHECK_CZECH "ネek kalba" -!define PIDGIN_SPELLCHECK_WELSH "Val kalba" -!define PIDGIN_SPELLCHECK_DANISH "Dan kalba" -!define PIDGIN_SPELLCHECK_GERMAN "Vokie鑛 kalba" -!define PIDGIN_SPELLCHECK_GREEK "Graik kalba" -!define PIDGIN_SPELLCHECK_ENGLISH "Angl kalba" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto kalba" -!define PIDGIN_SPELLCHECK_SPANISH "Ispan kalba" -!define PIDGIN_SPELLCHECK_FAROESE "Farer kalba" -!define PIDGIN_SPELLCHECK_FRENCH "Prancz kalba" -!define PIDGIN_SPELLCHECK_ITALIAN "Ital kalba" -!define PIDGIN_SPELLCHECK_DUTCH "Oland kalba" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norveg kalba" -!define PIDGIN_SPELLCHECK_POLISH "Lenk kalba" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugal kalba" -!define PIDGIN_SPELLCHECK_ROMANIAN "Rumun kalba" -!define PIDGIN_SPELLCHECK_RUSSIAN "Rus kalba" -!define PIDGIN_SPELLCHECK_SLOVAK "Slovak kalba" -!define PIDGIN_SPELLCHECK_SWEDISH "ミved kalba" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrainie鑛 kalba"
--- a/pidgin/win32/nsis/translations/norwegian.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/norwegian.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -45,28 +45,4 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Sttte for stavekontroll" !define PIDGIN_SPELLCHECK_ERROR "Det oppstod en feil ved installering av stavekontroll" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Det oppstod en feil ved installering av ordboken for stavekontroll" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Sttte for stavekontroll. (Internettoppkobling p虧revd for installasjon)" -!define ASPELL_INSTALL_FAILED "Installasjonen mislyktes." -!define PIDGIN_SPELLCHECK_BRETON "Bretagnsk" -!define PIDGIN_SPELLCHECK_CATALAN "Katalansk" -!define PIDGIN_SPELLCHECK_CZECH "Tsjekkisk" -!define PIDGIN_SPELLCHECK_WELSH "Walisisk" -!define PIDGIN_SPELLCHECK_DANISH "Dansk" -!define PIDGIN_SPELLCHECK_GERMAN "Tysk" -!define PIDGIN_SPELLCHECK_GREEK "Gresk" -!define PIDGIN_SPELLCHECK_ENGLISH "Engelsk" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Spansk" -!define PIDGIN_SPELLCHECK_FAROESE "F誡ysk" -!define PIDGIN_SPELLCHECK_FRENCH "Fransk" -!define PIDGIN_SPELLCHECK_ITALIAN "Italiensk" -!define PIDGIN_SPELLCHECK_DUTCH "Nederlandsk" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norsk" -!define PIDGIN_SPELLCHECK_POLISH "Polsk" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugisisk" -!define PIDGIN_SPELLCHECK_ROMANIAN "Rumensk" -!define PIDGIN_SPELLCHECK_RUSSIAN "Russisk" -!define PIDGIN_SPELLCHECK_SLOVAK "Slovakisk" -!define PIDGIN_SPELLCHECK_SWEDISH "Svensk" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrainsk"
--- a/pidgin/win32/nsis/translations/persian.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/persian.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -51,29 +51,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "ヤハネヌ蓆 ロ瞿晨ヌネ ヌ聶ヌ" !define PIDGIN_SPELLCHECK_ERROR "ホリヌ 裝隻 蒄ネ ロ瞿晨ヌネ ヌ聶ヌ" -!define PIDGIN_SPELLCHECK_DICT_ERROR "ホリヌ 裝隻 蒄ネ 瞻ハ昜ヌ肄 ロ瞿晨ヌネ ヌ聶ヌ" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "ヤハネヌ蓆 ロ瞿晨ヌネ ヌ聶ヌ. (ネムヌ 蒄ネ ヌハユヌ ヌ萍ム萍 睇メ ヌモハ)" -!define ASPELL_INSTALL_FAILED "蒄ネ ヤ侖ハ ホ贄マ" -!define PIDGIN_SPELLCHECK_BRETON "ネムハヌ蓖ヌ" -!define PIDGIN_SPELLCHECK_CATALAN "佗ハヌ睇" -!define PIDGIN_SPELLCHECK_CZECH "腰" -!define PIDGIN_SPELLCHECK_WELSH "跫瞑" -!define PIDGIN_SPELLCHECK_DANISH "マヌ蒹ヌム們" -!define PIDGIN_SPELLCHECK_GERMAN "ツ矼ヌ蓆" -!define PIDGIN_SPELLCHECK_GREEK "趾ヌ蓆" -!define PIDGIN_SPELLCHECK_ENGLISH "ヌ苣硼モ" -!define PIDGIN_SPELLCHECK_ESPERANTO "ヌモムヌ萍" -!define PIDGIN_SPELLCHECK_SPANISH "ヌモヌ蓖ヌ" -!define PIDGIN_SPELLCHECK_FAROESE "ンヌム跫" -!define PIDGIN_SPELLCHECK_FRENCH "ンムヌ萼跪" -!define PIDGIN_SPELLCHECK_ITALIAN "ヌハヌ硼ヌ" -!define PIDGIN_SPELLCHECK_DUTCH "裔蔆" -!define PIDGIN_SPELLCHECK_NORWEGIAN "葭謗" -!define PIDGIN_SPELLCHECK_POLISH "砒モハヌ蓆" -!define PIDGIN_SPELLCHECK_PORTUGUESE "ムハロヌ碆" -!define PIDGIN_SPELLCHECK_ROMANIAN "ム跂ヌ蓖ヌ" -!define PIDGIN_SPELLCHECK_RUSSIAN "ム贊" -!define PIDGIN_SPELLCHECK_SLOVAK "ヌモ礦ヌ們" -!define PIDGIN_SPELLCHECK_SWEDISH "モ貳マ" -!define PIDGIN_SPELLCHECK_UKRAINIAN "ヌ譏ムヌ蓆"
--- a/pidgin/win32/nsis/translations/portuguese-br.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/portuguese-br.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -43,28 +43,4 @@ !define URI_HANDLERS_SECTION_TITLE "Handlers para endere輟s" !define PIDGIN_SPELLCHECK_SECTION_TITLE "Suporte a verifica鈬o ortogr畴ica" !define PIDGIN_SPELLCHECK_ERROR "Erro ao instalar a verifica鈬o ortogr畴ica" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Erro ao instalar o dicion疵io da verifica鈬o ortogr畴ica" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Suporte a verifica鈬o ortogr畴ica (A instala鈬o necessita de conex縊 a internet)" -!define ASPELL_INSTALL_FAILED "Falha na instala鈬o" -!define PIDGIN_SPELLCHECK_BRETON "Bret縊" -!define PIDGIN_SPELLCHECK_CATALAN "Catal縊" -!define PIDGIN_SPELLCHECK_CZECH "Tcheco" -!define PIDGIN_SPELLCHECK_WELSH "Gal黌" -!define PIDGIN_SPELLCHECK_DANISH "Dinamarqu黌" -!define PIDGIN_SPELLCHECK_GERMAN "Alem縊" -!define PIDGIN_SPELLCHECK_GREEK "Grego" -!define PIDGIN_SPELLCHECK_ENGLISH "Ingl黌" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Espanhol" -!define PIDGIN_SPELLCHECK_FAROESE "Fero黌" -!define PIDGIN_SPELLCHECK_FRENCH "Franc黌" -!define PIDGIN_SPELLCHECK_ITALIAN "Italiano" -!define PIDGIN_SPELLCHECK_DUTCH "Holand黌" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Noruegu黌" -!define PIDGIN_SPELLCHECK_POLISH "Polon黌" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugu黌" -!define PIDGIN_SPELLCHECK_ROMANIAN "Romeno" -!define PIDGIN_SPELLCHECK_RUSSIAN "Russo" -!define PIDGIN_SPELLCHECK_SLOVAK "Eslovaco" -!define PIDGIN_SPELLCHECK_SWEDISH "Sueco" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ucraniano"
--- a/pidgin/win32/nsis/translations/simp-chinese.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/simp-chinese.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -43,28 +43,4 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "ニエミエシイ鰒ァウヨ" !define PIDGIN_SPELLCHECK_ERROR "ーイラーニエミエシイ魑エ" -!define PIDGIN_SPELLCHECK_DICT_ERROR "ーイラーニエミエシイ鰊ヨオ莎エ" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "ニエミエシイ鰒ァウヨ。」(ーイラーミ靨ェチャスモオス Internet)" -!define ASPELL_INSTALL_FAILED "ーイラーハァーワ" -!define PIDGIN_SPELLCHECK_BRETON "イシタカ狷瞠" -!define PIDGIN_SPELLCHECK_CATALAN "シモフゥヅト瞋ヌモ" -!define PIDGIN_SPELLCHECK_CZECH "スンソヒモ" -!define PIDGIN_SPELLCHECK_WELSH "ヘカハソモ" -!define PIDGIN_SPELLCHECK_DANISH "オ、ツモ" -!define PIDGIN_SPELLCHECK_GERMAN "オツモ" -!define PIDGIN_SPELLCHECK_GREEK "マ」ターモ" -!define PIDGIN_SPELLCHECK_ENGLISH "モ「モ" -!define PIDGIN_SPELLCHECK_ESPERANTO "ハタス醺" -!define PIDGIN_SPELLCHECK_SPANISH "ホー獏タモ" -!define PIDGIN_SPELLCHECK_FAROESE "キィヅモ" -!define PIDGIN_SPELLCHECK_FRENCH "キィモ" -!define PIDGIN_SPELLCHECK_ITALIAN "メ箒タモ" -!define PIDGIN_SPELLCHECK_DUTCH "コノタシモ" -!define PIDGIN_SPELLCHECK_NORWEGIAN "ナイヘモ" -!define PIDGIN_SPELLCHECK_POLISH "イィタシモ" -!define PIDGIN_SPELLCHECK_PORTUGUESE "ニマフムムタモ" -!define PIDGIN_SPELLCHECK_ROMANIAN "ヅツト瞋ヌモ" -!define PIDGIN_SPELLCHECK_RUSSIAN "カモ" -!define PIDGIN_SPELLCHECK_SLOVAK "ヒケツ蟾・ソヒモ" -!define PIDGIN_SPELLCHECK_SWEDISH "ネオ萼" -!define PIDGIN_SPELLCHECK_UKRAINIAN "ホレソヒタシモ"
--- a/pidgin/win32/nsis/translations/slovak.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/slovak.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -43,29 +43,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Podpora kontroly pravopisu" !define PIDGIN_SPELLCHECK_ERROR "Chyba pri in嗾al當ii kontroly pravopisu" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Chyba pri in嗾al當ii slovnka kontroly pravopisu" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Podpora kontroly pravopisu (Nutn pripojenie k Internetu)" -!define ASPELL_INSTALL_FAILED "In嗾al當ia zlyhala" -!define PIDGIN_SPELLCHECK_BRETON "Bretnsky" -!define PIDGIN_SPELLCHECK_CATALAN "Katal疣sky" -!define PIDGIN_SPELLCHECK_CZECH "ネesk" -!define PIDGIN_SPELLCHECK_WELSH "Welshsk" -!define PIDGIN_SPELLCHECK_DANISH "D疣sky" -!define PIDGIN_SPELLCHECK_GERMAN "Nemeck" -!define PIDGIN_SPELLCHECK_GREEK "Gr馗ky" -!define PIDGIN_SPELLCHECK_ENGLISH "Anglick" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperantsk" -!define PIDGIN_SPELLCHECK_SPANISH "角anielsk" -!define PIDGIN_SPELLCHECK_FAROESE "Faroesk" -!define PIDGIN_SPELLCHECK_FRENCH "Franczsky" -!define PIDGIN_SPELLCHECK_ITALIAN "Taliansk" -!define PIDGIN_SPELLCHECK_DUTCH "Holandsk" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Nrsky" -!define PIDGIN_SPELLCHECK_POLISH "Poセsk" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugalsk" -!define PIDGIN_SPELLCHECK_ROMANIAN "Rumunsk" -!define PIDGIN_SPELLCHECK_RUSSIAN "Rusk" -!define PIDGIN_SPELLCHECK_SLOVAK "Slovensk" -!define PIDGIN_SPELLCHECK_SWEDISH "革馘sky" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrajinsk"
--- a/pidgin/win32/nsis/translations/slovenian.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/slovenian.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -49,29 +49,4 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Podpora preverjanja 鑽kovanja" !define PIDGIN_SPELLCHECK_ERROR "Napaka pri name夊anju preverjanja 鑽kovanja" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Napaka pri name夊anju slovarja za preverjanje 鑽kovanja" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Podpora preverjanja 鑽kovanja. (Za namestitev je potrebna spletna povezava)" -!define ASPELL_INSTALL_FAILED "Namestitev ni uspela." -!define PIDGIN_SPELLCHECK_BRETON "bretonski" -!define PIDGIN_SPELLCHECK_CATALAN "katalonski" -!define PIDGIN_SPELLCHECK_CZECH "鐺嗅i" -!define PIDGIN_SPELLCHECK_WELSH "vel嗅i" -!define PIDGIN_SPELLCHECK_DANISH "danski" -!define PIDGIN_SPELLCHECK_GERMAN "nem嗅i" -!define PIDGIN_SPELLCHECK_GREEK "gr嗅i" -!define PIDGIN_SPELLCHECK_ENGLISH "angle嗅i" -!define PIDGIN_SPELLCHECK_ESPERANTO "esperantski" -!define PIDGIN_SPELLCHECK_SPANISH "嗔anski" -!define PIDGIN_SPELLCHECK_FAROESE "farojski" -!define PIDGIN_SPELLCHECK_FRENCH "francoski" -!define PIDGIN_SPELLCHECK_ITALIAN "italijanski" -!define PIDGIN_SPELLCHECK_DUTCH "nizozemski" -!define PIDGIN_SPELLCHECK_NORWEGIAN "norve嗅i" -!define PIDGIN_SPELLCHECK_POLISH "poljski" -!define PIDGIN_SPELLCHECK_PORTUGUESE "portugalski" -!define PIDGIN_SPELLCHECK_ROMANIAN "romunski" -!define PIDGIN_SPELLCHECK_RUSSIAN "ruski" -!define PIDGIN_SPELLCHECK_SLOVAK "slova嗅i" -!define PIDGIN_SPELLCHECK_SLOVENIAN "slovenski" -!define PIDGIN_SPELLCHECK_SWEDISH "嘛edski" -!define PIDGIN_SPELLCHECK_UKRAINIAN "ukrajinski"
--- a/pidgin/win32/nsis/translations/swedish.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/swedish.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -49,28 +49,4 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Std fr r舩tstavning" !define PIDGIN_SPELLCHECK_ERROR "Fel vid installation fr r舩tstavning" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Fel vid installation av r舩tstavningsordlista" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Std fr R舩tstavning. (Internetanslutning kr舸s fr installation)" -!define ASPELL_INSTALL_FAILED "Installationen misslyckades" -!define PIDGIN_SPELLCHECK_BRETON "Bretonska" -!define PIDGIN_SPELLCHECK_CATALAN "Katalanska" -!define PIDGIN_SPELLCHECK_CZECH "Tjeckiska" -!define PIDGIN_SPELLCHECK_WELSH "Kymriska" -!define PIDGIN_SPELLCHECK_DANISH "Danska" -!define PIDGIN_SPELLCHECK_GERMAN "Tyska" -!define PIDGIN_SPELLCHECK_GREEK "Grekiska" -!define PIDGIN_SPELLCHECK_ENGLISH "Engelska" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Spanska" -!define PIDGIN_SPELLCHECK_FAROESE "F舐iska" -!define PIDGIN_SPELLCHECK_FRENCH "Franska" -!define PIDGIN_SPELLCHECK_ITALIAN "Italienska" -!define PIDGIN_SPELLCHECK_DUTCH "Nederl舅dska" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norska" -!define PIDGIN_SPELLCHECK_POLISH "Polska" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugisiska" -!define PIDGIN_SPELLCHECK_ROMANIAN "Rum舅ska" -!define PIDGIN_SPELLCHECK_RUSSIAN "Ryska" -!define PIDGIN_SPELLCHECK_SLOVAK "Slovakiska" -!define PIDGIN_SPELLCHECK_SWEDISH "Svenska" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrainska"
--- a/pidgin/win32/nsis/translations/trad-chinese.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/trad-chinese.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -51,29 +51,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "ォヲrタヒャd・\ッ" !define PIDGIN_SPELLCHECK_ERROR "ヲwクヒォヲrタヒャdウ~、、オo・ヘソサ~" -!define PIDGIN_SPELLCHECK_DICT_ERROR "ヲwクヒォヲrタヒャd・ホェコオィ蟲~、、オo・ヘソサ~" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "ォヲrタヒャd、莇ゥ。]ヲwクヒカキヲウコサレコクウsスu。^。C" -!define ASPELL_INSTALL_FAILED "ヲwクヒ・「アム" -!define PIDGIN_SPELLCHECK_BRETON "・ャィスヲh・ァ、" -!define PIDGIN_SPELLCHECK_CATALAN "・[ョカゥ、" -!define PIDGIN_SPELLCHECK_CZECH "アカァJ、" -!define PIDGIN_SPELLCHECK_WELSH "ォツコクエオ、" -!define PIDGIN_SPELLCHECK_DANISH "、ヲウチ、" -!define PIDGIN_SPELLCHECK_GERMAN "シw、" -!define PIDGIN_SPELLCHECK_GREEK "ァニテセ、" -!define PIDGIN_SPELLCHECK_ENGLISH "ュ^、" -!define PIDGIN_SPELLCHECK_ESPERANTO "・@ャノサy" -!define PIDGIN_SPELLCHECK_SPANISH "ヲ隸Z、、" -!define PIDGIN_SPELLCHECK_FAROESE "ェkテケクsョq、" -!define PIDGIN_SPELLCHECK_FRENCH "ェk、" -!define PIDGIN_SPELLCHECK_ITALIAN "キN、jァQ、" -!define PIDGIN_SPELLCHECK_DUTCH "イト、" -!define PIDGIN_SPELLCHECK_NORWEGIAN "ョソォツ、" -!define PIDGIN_SPELLCHECK_POLISH "ェiト、" -!define PIDGIN_SPELLCHECK_PORTUGUESE "オ螟" -!define PIDGIN_SPELLCHECK_ROMANIAN "テケーィ・ァィネ、" -!define PIDGIN_SPELLCHECK_RUSSIAN "ォX、" -!define PIDGIN_SPELLCHECK_SLOVAK "エオャ・・ァJ、" -!define PIDGIN_SPELLCHECK_SWEDISH "キ遞螟" -!define PIDGIN_SPELLCHECK_UKRAINIAN "ッQァJト、"
--- a/pidgin/win32/nsis/translations/valencian.nsh Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/nsis/translations/valencian.nsh Tue Mar 16 12:07:06 2010 +0900 @@ -46,29 +46,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Soport de Correccio Ortografica" !define PIDGIN_SPELLCHECK_ERROR "Erro Instalant Correccio Ortografica" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Erro Instalant Diccionari de Correccio Ortografica" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Soport per a Correccio Ortografica. (es requerix conexio a Internet per a fer l'instalacio)" -!define ASPELL_INSTALL_FAILED "L'Instalacio fall" -!define PIDGIN_SPELLCHECK_BRETON "Breto" -!define PIDGIN_SPELLCHECK_CATALAN "Catal" -!define PIDGIN_SPELLCHECK_CZECH "Chec" -!define PIDGIN_SPELLCHECK_WELSH "Gal駸" -!define PIDGIN_SPELLCHECK_DANISH "Danes" -!define PIDGIN_SPELLCHECK_GERMAN "Alem" -!define PIDGIN_SPELLCHECK_GREEK "Grec" -!define PIDGIN_SPELLCHECK_ENGLISH "Angles" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Espanyol" -!define PIDGIN_SPELLCHECK_FAROESE "Feroes" -!define PIDGIN_SPELLCHECK_FRENCH "Frances" -!define PIDGIN_SPELLCHECK_ITALIAN "Itali" -!define PIDGIN_SPELLCHECK_DUTCH "Holandes" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Noruec" -!define PIDGIN_SPELLCHECK_POLISH "Polac" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugues" -!define PIDGIN_SPELLCHECK_ROMANIAN "Romanes" -!define PIDGIN_SPELLCHECK_RUSSIAN "Rus" -!define PIDGIN_SPELLCHECK_SLOVAK "Eslovac" -!define PIDGIN_SPELLCHECK_SWEDISH "Suec" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ucrani"
--- a/pidgin/win32/winpidgin.c Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/winpidgin.c Tue Mar 16 12:07:06 2010 +0900 @@ -188,6 +188,18 @@ return TRUE; } +static BOOL dll_prep(const TCHAR *pidgin_dir) { + TCHAR path[MAX_PATH + 1]; + path[0] = _T('\0'); + + if (*pidgin_dir) { + _sntprintf(path, sizeof(path) / sizeof(TCHAR), _T("%s\\Gtk\\bin"), pidgin_dir); + path[sizeof(path) / sizeof(TCHAR)] = _T('\0'); + } + + return common_dll_prep(path); +} + static void portable_mode_dll_prep(const TCHAR *pidgin_dir) { /* need to be able to fit MAX_PATH + "PIDGIN_ASPELL_DIR=\\Aspell\\bin" in path2 */ TCHAR path[MAX_PATH + 1]; @@ -233,18 +245,6 @@ } } -static BOOL dll_prep(const TCHAR *pidgin_dir) { - TCHAR path[MAX_PATH + 1]; - path[0] = _T('\0'); - - if (*pidgin_dir) { - _sntprintf(path, sizeof(path) / sizeof(TCHAR), _T("%s\\Gtk\\bin"), pidgin_dir); - path[sizeof(path) / sizeof(TCHAR)] = _T('\0'); - } - - return common_dll_prep(path); -} - static TCHAR* winpidgin_lcid_to_posix(LCID lcid) { TCHAR *posix = NULL; int lang_id = PRIMARYLANGID(lcid);
--- a/pidgin/win32/wspell.c Thu Mar 04 15:19:39 2010 +0900 +++ b/pidgin/win32/wspell.c Tue Mar 16 12:07:06 2010 +0900 @@ -3,7 +3,7 @@ * * File: wspell.c * Date: March, 2003 - * Description: Windows Purple gtkspell interface. + * Description: Windows Pidgin gtkspell interface. * * Copyright (C) 2002-2003, Herman Bloggs <hermanator12002@yahoo.com> * @@ -32,8 +32,22 @@ #include "win32dep.h" #include "wspell.h" +/* Intermediate function so that we can eat Enchant error popups when it doesn't find a DLL + * This is fixed upstream, but not released */ +GtkSpell* (*wpidginspell_new_attach_proxy) (GtkTextView *, + const gchar *, + GError **) = NULL; + /* GTKSPELL DUMMY FUNCS */ -static GtkSpell* wgtkspell_new_attach(GtkTextView *view, const gchar *lang, GError **error) {return NULL;} +static GtkSpell* wgtkspell_new_attach(GtkTextView *view, const gchar *lang, GError **error) { + GtkSpell *ret = NULL; + if (wpidginspell_new_attach_proxy) { + UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS); + ret = wpidginspell_new_attach_proxy(view, lang, error); + SetErrorMode(old_error_mode); + } + return ret; +} static GtkSpell* wgtkspell_get_from_text_view(GtkTextView *view) {return NULL;} static void wgtkspell_detach(GtkSpell *spell) {} static gboolean wgtkspell_set_language(GtkSpell *spell, const gchar *lang, GError **error) {return FALSE;} @@ -54,28 +68,44 @@ void (*wpidginspell_recheck_all) (GtkSpell*) = wgtkspell_recheck_all; +#define GTKSPELL_DLL "libgtkspell-0.dll" + static void load_gtkspell() { - wpidginspell_new_attach = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_new_attach" ); - wpidginspell_get_from_text_view = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_get_from_text_view"); - wpidginspell_detach = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_detach"); - wpidginspell_set_language = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_set_language"); - wpidginspell_recheck_all = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_recheck_all"); + UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS); + gchar *tmp, *tmp2; + const char *path = g_getenv("PATH"); + tmp = g_build_filename(wpurple_install_dir(), "spellcheck", NULL); + tmp2 = g_strdup_printf("%s%s%s", (path ? path : ""), + (path ? G_SEARCHPATH_SEPARATOR_S : ""), + tmp); + g_free(tmp); + g_setenv("PATH", tmp2, TRUE); + + /* Suppress error popups */ + wpidginspell_new_attach_proxy = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_new_attach" ); + if (wpidginspell_new_attach_proxy) { + wpidginspell_get_from_text_view = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_get_from_text_view"); + wpidginspell_detach = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_detach"); + wpidginspell_set_language = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_set_language"); + wpidginspell_recheck_all = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_recheck_all"); + } else { + purple_debug_warning("wspell", "Couldn't load gtkspell (%s) \n", GTKSPELL_DLL); + /*wpidginspell_new_attach = wgtkspell_new_attach;*/ + } + SetErrorMode(old_error_mode); } -static char* lookup_aspell_path() { +static void lookup_aspell_path() { const char *tmp; + gchar *aspell_path; if ((tmp = g_getenv("PIDGIN_ASPELL_DIR"))) - return g_strdup(tmp); - - return wpurple_read_reg_string(HKEY_LOCAL_MACHINE, "Software\\Aspell", "Path"); -} - -void winpidgin_spell_init() { - char *aspell_path = lookup_aspell_path(); + aspell_path = g_strdup(tmp); + else + aspell_path = wpurple_read_reg_string(HKEY_LOCAL_MACHINE, "Software\\Aspell", "Path"); if (aspell_path != NULL) { - char *tmp = g_strconcat(aspell_path, "\\aspell-15.dll", NULL); + char *tmp = g_build_filename(aspell_path, "aspell-15.dll", NULL); if (g_file_test(tmp, G_FILE_TEST_EXISTS)) { const char *path = g_getenv("PATH"); purple_debug_info("wspell", "Found Aspell in %s\n", aspell_path); @@ -88,14 +118,18 @@ g_setenv("PATH", tmp, TRUE); - load_gtkspell(); - } else { + } else purple_debug_warning("wspell", "Couldn't find aspell-15.dll\n"); - } g_free(tmp); g_free(aspell_path); - } else { - purple_debug_warning("wspell", "Couldn't find path for Aspell\n"); - } + } else + purple_debug_warning("wspell", "Aspell installation not found (this isn't necessarily a problem)\n"); } + +void winpidgin_spell_init() { + /* We keep doing the aspell path thing so that previously installed dictionaries still work */ + lookup_aspell_path(); + + load_gtkspell(); +}
--- a/po/POTFILES.in Thu Mar 04 15:19:39 2010 +0900 +++ b/po/POTFILES.in Tue Mar 16 12:07:06 2010 +0900 @@ -119,16 +119,6 @@ libpurple/protocols/msn/state.c libpurple/protocols/msn/switchboard.c libpurple/protocols/msn/userlist.c -libpurple/protocols/msnp9/dialog.c -libpurple/protocols/msnp9/error.c -libpurple/protocols/msnp9/msn.c -libpurple/protocols/msnp9/nexus.c -libpurple/protocols/msnp9/notification.c -libpurple/protocols/msnp9/servconn.c -libpurple/protocols/msnp9/session.c -libpurple/protocols/msnp9/state.c -libpurple/protocols/msnp9/switchboard.c -libpurple/protocols/msnp9/userlist.c libpurple/protocols/mxit/actions.c libpurple/protocols/mxit/filexfer.c libpurple/protocols/mxit/http.c