Fri, 25 Jan 2013 02:22:38 -0500
Merge with default.
--- a/.hgignore Thu Aug 23 01:27:48 2012 -0400 +++ b/.hgignore Fri Jan 25 02:22:38 2013 -0500 @@ -7,8 +7,10 @@ .*/perl/common/[^/]+\.c$ .*/perl/common/blib.* .*/perl/common/pm_to_blib$ +.*/perl/common/MYMETA\.(json|yml) .*~$ .*\.a$ +.*\.asc$ .*\.bak$ .*\.bs$ .*\.def$
--- a/COPYRIGHT Thu Aug 23 01:27:48 2012 -0400 +++ b/COPYRIGHT Fri Jan 25 02:22:38 2013 -0500 @@ -17,6 +17,7 @@ Copyright (C) 1998-2012 by the following: +Mark Saleem Abdulrasool Jakub Adam Dave Ahlswede @@ -74,6 +75,7 @@ Éric Boumaour Chris Boyle Stanislav Brabec +Bartosz Brachaczek Quentin Brandon Derrick J Brashear Mauro Sérgio Ferreira Brasil @@ -176,6 +178,7 @@ Gavan Fantom (gavan) Leonardo Fernandes David Fiander +Michael Fiedler Ryan Flegel Rob Flynn <gaim@robflynn.com> Rob Foehl (rwf) @@ -256,6 +259,7 @@ Vitaliy Ischenko Intel Corporation Andrew Ivanov +Momchil Ivanov Scott Jackson Hans Petter Jansson David Jedelsky @@ -417,6 +421,7 @@ Ari Pollak Stephen Pope Cristi Posoiu +Alexei Potashnik Nathan Poznick Jory A. Pratt David Preece @@ -458,6 +463,7 @@ Thanumalayan S. Jonathan Sailor Elliott Sales de Andrade +Catalin Salgau Tomasz Sałaciński <tsalacinski@gmail.com> Pradyumna Sampath Arvind Samptur @@ -510,6 +516,7 @@ Lex Spoon Chris Stafford Kevin Stange +Ferdinand Stehle Joshua Stein Jakub Steiner Richard Stellingwerff
--- a/ChangeLog Thu Aug 23 01:27:48 2012 -0400 +++ b/ChangeLog Fri Jan 25 02:22:38 2013 -0500 @@ -7,6 +7,9 @@ either 2 or 3 will attempt to build with specifically 2.x or 3.x support. The default is 'auto', which will first look for 3.x development headers and then 2.x development headers. + * Add email notification in the docklet area. (Alexei) (#3571) + * Add a pref to select the type messages in conversation that triggers + the docklet notification. (Momchil) (#12598) Finch: * Support the conversation-extended signal for extending the @@ -57,21 +60,82 @@ * Fix a crash at startup with large contact list. Avatar support for buddies will be disabled till 3.0.0. (#15226, #14305) + General: + * The configure script will now exit with status 1 when specifying + invalid protocol plugins using the --with-static-prpls and + --with-dynamic-prpls arguments. (Michael Fiedler) (#15316) + + libpurple: + * Don't link directly to libgcrypt when building with GnuTLS support. + (Bartosz Brachaczek) (#15329) + * Fix UPnP mappings on routers that return empty <URLBase/> elements + in their response. (Ferdinand Stehle) (#15373) + * Tcl plugin uses saner, race-free plugin loading. + * Fix the Tcl signals-test plugin for savedstatus-changed. + (Andrew Shadura) (#15443) + + Pidgin: + * Make Pidgin more friendly to non-X11 GTK+, such as MacPorts' +no_x11 + variant. + Gadu-Gadu: * Fix a crash at startup with large contact list. Avatar support for buddies will be disabled till 3.0.0. (#15226, #14305) + IRC: + * Support for SASL authentication. (Thijs Alkemade, Andy Spencer) + (#13270) + * Print topic setter information at channel join. (#13317) + MSN: + * Fix SSL certificate issue when signing into MSN for some users. * Fix a crash when removing a user before its icon is loaded. (Mark Barfield) (#15217) + MXit: + * Display farewell messages in a different colour to distinguish + them from normal messages. + * Add support for typing notification. + * Add support for the Relationship Status profile attribute. + * Remove all reference to Hidden Number. + * Ignore new invites to join a GroupChat if you're already joined, or + still have a pending invite. + * The buddy's name was not centered vertically in the buddy-list if they + did not have a status-message or mood set. + Yahoo!: * Fix a double-free in profile/picture loading code. (Mihai Serban) (#15053) + * Fix retrieving server-side buddy aliases. (Catalin Salgu) (#15381) Plugins: * The Voice/Video Settings plugin supports using the sndio GStreamer - backends. (Brad Smith) (#14414) + backends. (Brad Smith) (#14414) + * Fix a crash in the Contact Availability Detection plugin. (Mark) + (#15327) + * Make the Message Notification plugin more friendly to non-X11 GTK+, + such as MacPorts' +no_x11 variant. + + Windows-Specific Changes: + * Compile with secure flags (#15290) + * Installer downloads GTK+ Runtime and Debug Symbols more securely. + (#15277) + * Updates to a number of dependencies, some of which have security + related fixes. (#14571, #15285, #15286) + * ATK 1.32.0-2 + * Cyrus SASL 2.1.25 + * expat 2.1.0-1 + * freetype 2.4.10-1 + * gettext 0.18.1.1-2 + * Glib 2.28.8-1 + * libpng 1.4.12-1 + * libxml2 2.9.0-1 + * NSS 3.13.6 and NSPR 4.9.2 + * Pango 1.29.4-1 + * SILC 1.1.10 + * zlib 1.2.5-2 + * Patch libmeanwhile (sametime library) to fix crash. (Jonathan Rice) + (#12637) version 2.10.6 (07/06/2012): Pidgin:
--- a/ChangeLog.API Thu Aug 23 01:27:48 2012 -0400 +++ b/ChangeLog.API Fri Jan 25 02:22:38 2013 -0500 @@ -3,6 +3,7 @@ version 3.0.0 (??/??/????): libpurple: Added: + * displaying-emails-clear signal (notification signal) * pidgin_create_webview * purple_account_is_disconnecting * purple_account_get_ui_data @@ -23,6 +24,8 @@ * purple_conversation_message_get_alias * purple_conversation_message_get_conv * purple_contact_get_contact_size + * purple_notify_emails_pending + * purple_notify_emails_present * purple_notify_searchresult_column_get_title * purple_notify_searchresult_column_is_visible * purple_notify_searchresult_column_set_visible @@ -72,12 +75,14 @@ * Various WebKit-related functions in gtkwebview.h * xmlnode_get_default_namespace * xmlnode_strip_prefixes + * PidginDockletFlag Changed: * purple_account_add_buddy now takes an invite message as the last parameter * purple_account_add_buddies now takes an invite message as the last parameter + * purple_buddy_icon_unref no longer has a return value * purple_certificate_check_signature_chain now returns a list of failing PurpleCertificate*s as the second parameter * purple_connection_error now takes a PurpleConnectionError @@ -85,6 +90,8 @@ * purple_conversation_get_gc renamed to purple_conversation_get_connection * purple_dnsquery_a now takes a PurpleAccount as the first parameter + * purple_imgstore_add renamed to purple_imgstore_new + * purple_imgstore_add_with_id renamed to purple_imgstore_new_with_id * purple_network_listen now takes the protocol family as the second parameter * purple_network_listen now takes a boolean indicating external port @@ -114,6 +121,9 @@ * purple_xfer_set_bytes_sent now takes a goffset as the bytes_sent parameter * purple_xfer_set_size now takes a goffset as the size parameter + * PurpleCertificateVerificationStatus enumeration is now merged with + internal flags, thus removing PURPLE_CERTIFICATE_INVALID and + replacing it with more precise errors. * PurpleConnectionUiOps.report_disconnect now passes a PurpleConnectionError as the second parameter * PurpleXfer.bytes_remaining is now a goffset @@ -157,6 +167,9 @@ * PidginConversation.sg * purple_account_add_buddies_with_invite * purple_account_add_buddy_with_invite + * purple_blist_load + * purple_blist_new + * purple_set_blist * purple_blist_update_buddy_icon * purple_buddy_get_local_alias * purple_buddy_icons_has_custom_icon @@ -188,6 +201,7 @@ * purple_plugins_unregister_load_notify_cb * purple_plugins_unregister_probe_notify_cb * purple_plugins_unregister_unload_notify_cb + * purple_pounces_load * purple_presence_add_status * purple_presence_add_list * purple_proxy_connect_socks5 @@ -212,6 +226,7 @@ * purple_util_fetch_url_request, instead. * purple_util_fetch_url_request_len_with_account. Use purple_util_fetch_url_request, instead. + * PurpleCertificateVerificationStatus.PURPLE_CERTIFICATE_INVALID * PurpleConnectionUiOps.report_disconnect_reason * PurplePluginProtocolInfo.add_buddy_with_invite * PurplePluginProtocolInfo.add_buddies_with_invite
--- a/Makefile.am Thu Aug 23 01:27:48 2012 -0400 +++ b/Makefile.am Fri Jan 25 02:22:38 2013 -0500 @@ -6,7 +6,7 @@ HACKING \ Makefile.mingw \ PLUGIN_HOWTO \ - README.MTN \ + README.hg \ README.mingw \ config.h.mingw \ doxy2devhelp.xsl \ @@ -54,13 +54,13 @@ # ... and have no changes in the working copy. (this isn't really necessary with hg because hg id appends a "+") test "x`hg st -mard`" = x -packages: +sign-packages: dist gpg -ab pidgin-$(PACKAGE_VERSION).tar.gz gpg -ab pidgin-$(PACKAGE_VERSION).tar.bz2 gpg --verify pidgin-$(PACKAGE_VERSION).tar.gz.asc pidgin-$(PACKAGE_VERSION).tar.gz gpg --verify pidgin-$(PACKAGE_VERSION).tar.bz2.asc pidgin-$(PACKAGE_VERSION).tar.bz2 -release: commit-check version-check distcheck packages +release: commit-check version-check distcheck sign-packages if INSTALL_I18N PO_DIR=po
--- a/Makefile.mingw Thu Aug 23 01:27:48 2012 -0400 +++ b/Makefile.mingw Fri Jan 25 02:22:38 2013 -0500 @@ -31,11 +31,21 @@ exit; \ }' VERSION) -GTK_INSTALL_VERSION = 2.16.6.0 +GTK_INSTALL_VERSION = 2.16.6.1 + +authenticode_sign = $(MONO_SIGNCODE) \ + -spc "$(SIGNCODE_SPC)" -v "$(SIGNCODE_PVK)" \ + -a sha1 -$$ commercial \ + -n "$(2)" -i "https://pidgin.im" \ + -t "http://timestamp.verisign.com/scripts/timstamp.dll" -tr 10 \ + $(1) + +gpg_sign = $(GPG_SIGN) -ab $(1) && $(GPG_SIGN) --verify $(1).asc STRIPPED_RELEASE_DIR = $(PIDGIN_TREE_TOP)/pidgin-$(PIDGIN_VERSION)-win32bin DEBUG_SYMBOLS_DIR = $(PIDGIN_TREE_TOP)/pidgin-$(PIDGIN_VERSION)-dbgsym +PIDGIN_INST_DEP_DIR="$(WIN32_DEV_TOP)/pidgin-inst-deps-20120910" # Any *.dll or *.exe files included in win32-install-dir that we don't compile # should be included in this list so they don't get stripped @@ -55,6 +65,7 @@ libplc4.dll \ libplds4.dll \ libsasl.dll \ + libssp-0.dll \ libxml2-2.dll \ nss3.dll \ nssckbi.dll \ @@ -66,7 +77,7 @@ saslLOGIN.dll \ saslPLAIN.dll \ libsilc-1-1-2.dll \ - libsilcclient-1-1-2.dll \ + libsilcclient-1-1-3.dll \ smime3.dll \ softokn3.dll \ sqlite3.dll \ @@ -77,7 +88,7 @@ include $(PIDGIN_COMMON_RULES) -.PHONY: all docs install installer installer_offline installer_zip debug_symbols_zip installers clean uninstall create_release_install_dir generate_installer_includes $(PIDGIN_REVISION_H) $(PIDGIN_REVISION_RAW_TXT) +.PHONY: all docs install installer installer_offline installer_zip debug_symbols_zip installers clean uninstall create_release_install_dir generate_installer_includes $(PIDGIN_REVISION_H) $(PIDGIN_REVISION_RAW_TXT) gtk_runtime_zip all: $(PIDGIN_CONFIG_H) $(PIDGIN_REVISION_H) $(MAKE) -C $(PURPLE_TOP) -f $(MINGW_MAKEFILE) @@ -98,12 +109,13 @@ 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/enchant/*.dll $(PIDGIN_INSTALL_DIR)/spellcheck/lib/enchant - cp $(WIN32_DEV_TOP)/pidgin-inst-deps-20100315/exchndl.dll $(PIDGIN_INSTALL_DIR) + cp $(PIDGIN_INST_DEP_DIR)/exchndl.dll $(PIDGIN_INSTALL_DIR) + cp $(GCC_SSP_TOP)/bin/libssp-0.dll $(PIDGIN_INSTALL_DIR) -pidgin/win32/nsis/gtk-runtime-$(GTK_BUNDLE_VERSION).zip: - pidgin/win32/nsis/generate_gtk_zip.sh `pwd` +gtk_runtime_zip: + pidgin/win32/nsis/generate_gtk_zip.sh "`pwd`" "$(GPG_SIGN)" -generate_installer_includes: create_release_install_dir pidgin/win32/nsis/gtk-runtime-$(GTK_BUNDLE_VERSION).zip debug_symbols_zip $(PIDGIN_TREE_TOP)/pidgin/win32/nsis/nsis_translations.desktop +generate_installer_includes: create_release_install_dir gtk_runtime_zip debug_symbols_zip $(PIDGIN_TREE_TOP)/pidgin/win32/nsis/nsis_translations.desktop 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 {} ';' \ @@ -137,18 +149,32 @@ find $(STRIPPED_RELEASE_DIR) \( -name '*.dll' -o -name '*.exe' \) \ -not \( -false $(EXTERNAL_DLLS_FIND_EXP) \) \ -exec $(STRIP) --strip-unneeded {} ';' + $(call authenticode_sign, $(STRIPPED_RELEASE_DIR)/pidgin.exe, "Pidgin $(PIDGIN_VERSION)") installer: generate_installer_includes - $(MAKENSIS) -V3 -DPIDGIN_VERSION="$(PIDGIN_VERSION)" -DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" -DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" -DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi + $(eval $@_DEBUG_SYMBOLS_SHA1SUM := $(shell sha1sum $(DEBUG_SYMBOLS_DIR).zip | sed -e "s/\ .*$$//")) + $(eval $@_GTK_SHA1SUM := $(shell sha1sum pidgin/win32/nsis/gtk-runtime-$(GTK_INSTALL_VERSION).zip | sed -e "s/\ .*$$//")) + $(MAKENSIS) -V3 -DPIDGIN_VERSION="$(PIDGIN_VERSION)" -DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" \ + -DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" -DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" \ + -DDEBUG_SYMBOLS_SHA1SUM="$($@_DEBUG_SYMBOLS_SHA1SUM)" -DGTK_SHA1SUM="$($@_GTK_SHA1SUM)"\ + pidgin/win32/nsis/pidgin-installer.nsi + $(call authenticode_sign, pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION).exe, "Pidgin Installer") mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION).exe ./ + $(call gpg_sign, pidgin-$(PIDGIN_VERSION).exe) installer_offline: generate_installer_includes - $(MAKENSIS) -V3 -DPIDGIN_VERSION="$(PIDGIN_VERSION)" -DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" -DOFFLINE_INSTALLER -DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" -DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi + $(MAKENSIS) -V3 -DPIDGIN_VERSION="$(PIDGIN_VERSION)" -DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" \ + -DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" -DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" \ + -DOFFLINE_INSTALLER \ + pidgin/win32/nsis/pidgin-installer.nsi + $(call authenticode_sign, pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION)-offline.exe, "Pidgin Installer") mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION)-offline.exe ./ + $(call gpg_sign, pidgin-$(PIDGIN_VERSION)-offline.exe) installer_zip: create_release_install_dir rm -f pidgin-$(PIDGIN_VERSION)-win32-bin.zip zip -9 -r pidgin-$(PIDGIN_VERSION)-win32-bin.zip $(STRIPPED_RELEASE_DIR) + $(call gpg_sign, pidgin-$(PIDGIN_VERSION)-win32-bin.zip) debug_symbols_zip: install rm -rf $(DEBUG_SYMBOLS_DIR) $(DEBUG_SYMBOLS_DIR).zip @@ -156,7 +182,9 @@ tar -cf - `find $(PIDGIN_INSTALL_DIR) \( -name '*.dll' -o -name '*.exe' \) \ -not \( -false $(EXTERNAL_DLLS_FIND_EXP) \) -print` \ | tar --strip 2 --xform s/$$/.dbgsym/ -xC $(DEBUG_SYMBOLS_DIR) -f - + cp $(MEANWHILE_TOP)/bin/libmeanwhile-1.dll.unstripped $(DEBUG_SYMBOLS_DIR)/libmeanwhile-1.dll.dbgsym zip -9 -r $(DEBUG_SYMBOLS_DIR).zip $(DEBUG_SYMBOLS_DIR) + $(call gpg_sign, $(DEBUG_SYMBOLS_DIR).zip) installers: installer installer_offline debug_symbols_zip installer_zip
--- a/autogen.sh Thu Aug 23 01:27:48 2012 -0400 +++ b/autogen.sh Fri Jan 25 02:22:38 2013 -0500 @@ -160,5 +160,8 @@ ############################################################################### # Run configure ############################################################################### -echo "running ./configure ${CONFIGURE_FLAGS} $@" -./configure ${CONFIGURE_FLAGS} $@ +if test -z "$NOCONFIGURE"; then + echo "running ./configure ${CONFIGURE_FLAGS} $@" + ./configure ${CONFIGURE_FLAGS} $@ +fi +
--- a/configure.ac Thu Aug 23 01:27:48 2012 -0400 +++ b/configure.ac Fri Jan 25 02:22:38 2013 -0500 @@ -1,5 +1,5 @@ dnl Process this file with autoconf to produce a configure script. -AC_PREREQ([2.50]) +AC_PREREQ([2.63]) # UPDATING VERSION NUMBERS FOR RELEASES # @@ -74,9 +74,8 @@ AC_CANONICAL_HOST AC_CONFIG_HEADERS([config.h]) -AM_INIT_AUTOMAKE([1.9 -Wno-portability dist-bzip2]) -dnl TODO: Always use AM_SILENT_RULES when we depend on automake >= 1.11 -m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) +AM_INIT_AUTOMAKE([1.11 -Wno-portability dist-bzip2]) +AM_SILENT_RULES([yes]) PURPLE_MAJOR_VERSION=purple_major_version PURPLE_MINOR_VERSION=purple_minor_version @@ -111,8 +110,8 @@ AC_PROG_CC AM_PROG_CC_C_O AC_PROG_CXX -AC_DISABLE_STATIC -AC_PROG_LIBTOOL +LT_PREREQ([2.2.6]) +LT_INIT([disable-static]) LIBTOOL="$LIBTOOL --silent" AC_PROG_INSTALL PKG_PROG_PKG_CONFIG @@ -159,7 +158,7 @@ [AC_DEFINE([HAVE_GETADDRINFO], [1], [Define to 1 if you have the getaddrinfo function.])], [AC_CHECK_LIB(socket, getaddrinfo, - [AC_DEFINE([HAVE_GETADDRINFO]) LIBS="-lsocket -lsnl $LIBS"], , , -lnsl)]) + [AC_DEFINE([HAVE_GETADDRINFO]) LIBS="-lsocket -lnsl $LIBS"], , -lnsl)]) AC_CHECK_FUNCS(inet_ntop) AC_CHECK_FUNCS(getifaddrs) dnl Check for socklen_t (in Unix98) @@ -209,7 +208,7 @@ AC_MSG_ERROR([unable to find the ceil() function]) ]) -AC_MSG_CHECKING(for fileno()) +AC_MSG_CHECKING([for fileno()]) AC_RUN_IFELSE([AC_LANG_SOURCE([[ #include <stdio.h> @@ -233,7 +232,7 @@ AC_MSG_RESULT(no) ]) -AC_MSG_CHECKING(for the %z format string in strftime()) +AC_MSG_CHECKING([for the %z format string in strftime()]) AC_RUN_IFELSE([AC_LANG_SOURCE([[ #ifdef HAVE_SYS_TIME_H #include <sys/time.h> @@ -298,10 +297,10 @@ dnl ####################################################################### dnl # Disable creation and installation of translation files dnl ####################################################################### -AC_ARG_ENABLE(nls, AC_HELP_STRING([--disable-nls], [disable installation of translation files]), enable_i18n="$enableval", enable_i18n=yes) +AC_ARG_ENABLE(nls, AS_HELP_STRING([--disable-nls], [disable installation of translation files]), enable_i18n="$enableval", enable_i18n=yes) if test x$enable_i18n = xyes; then - AC_PROG_INTLTOOL + IT_PROG_INTLTOOL GETTEXT_PACKAGE=pidgin AC_SUBST(GETTEXT_PACKAGE) @@ -333,16 +332,16 @@ AM_CONDITIONAL(INSTALL_I18N, test "x$enable_i18n" = "xyes") dnl ####################################################################### -dnl # Check for GLib 2.16 (required) +dnl # Check for GLib 2.20 (required) dnl ####################################################################### # TODO: gmodule-2.0 is only needed if enable_plugins is 'yes'. It # might be nice to change this check so that it's not required # if enable_plugins is 'no'. -PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.16.0 gobject-2.0 gmodule-2.0 gthread-2.0], , [ +PKG_CHECK_MODULES(GLIB, [glib-2.0 >= 2.20.0 gobject-2.0 gmodule-2.0 gthread-2.0], , [ AC_MSG_RESULT(no) AC_MSG_ERROR([ -You must have GLib 2.16.0 or newer development headers installed to build. +You must have GLib 2.20.0 or newer development headers installed to build. If you have these installed already you may need to install pkg-config so I can find them. @@ -354,7 +353,7 @@ AC_SUBST(GLIB_GENMARSHAL) AC_ARG_WITH([extraversion], - AC_HELP_STRING([--with-extraversion=STRING], + AS_HELP_STRING([--with-extraversion=STRING], [extra version number to be displayed in Help->About and --help (for packagers)]), EXTRA_VERSION=$withval) @@ -364,19 +363,19 @@ AC_DEFINE_UNQUOTED(DISPLAY_VERSION, "$VERSION", [display version info]) fi -AC_ARG_ENABLE(missing-dependencies, [AC_HELP_STRING([--disable-missing-dependencies], +AC_ARG_ENABLE(missing-dependencies, [AS_HELP_STRING([--disable-missing-dependencies], [skip missing dependencies instead of aborting configure])], force_deps="$enableval", force_deps="yes") AC_ARG_WITH(x, [], with_x="$withval", with_x="yes") -AC_ARG_ENABLE(gtkui, [AC_HELP_STRING([--disable-gtkui], +AC_ARG_ENABLE(gtkui, [AS_HELP_STRING([--disable-gtkui], [compile without GTK+ user interface])], enable_gtkui="$enableval", enable_gtkui="yes") -AC_ARG_WITH(gtk, [AC_HELP_STRING([--with-gtk=<version>], +AC_ARG_WITH(gtk, [AS_HELP_STRING([--with-gtk=<version>], [compile with GTK+ 2 or 3 user interface (default: auto)])], with_gtk="$withval", with_gtk="auto") -AC_ARG_ENABLE(consoleui, [AC_HELP_STRING([--disable-consoleui], +AC_ARG_ENABLE(consoleui, [AS_HELP_STRING([--disable-consoleui], [compile without console user interface])], [enable_consoleui=$enableval force_finch=$enableval], [enable_consoleui=yes force_finch=no]) @@ -384,35 +383,35 @@ dnl # Check for GTK+ 2.10 and other things used by the GTK UI dnl ####################################################################### AC_ARG_ENABLE(screensaver, - [AC_HELP_STRING([--disable-screensaver], + [AS_HELP_STRING([--disable-screensaver], [compile without X screensaver extension (used to detect idleness)])], enable_screensaver="$enableval", enable_screensaver="yes") AC_ARG_ENABLE(sm, - [AC_HELP_STRING([--disable-sm], + [AS_HELP_STRING([--disable-sm], [compile without X session management support])], enable_sm="$enableval", enable_sm="yes") AC_ARG_ENABLE(startup-notification, - [AC_HELP_STRING([--disable-startup-notification], + [AS_HELP_STRING([--disable-startup-notification], [compile without startup notification support])], enable_startup_notification="$enableval", enable_startup_notification="yes") AC_ARG_ENABLE(gtkspell, - [AC_HELP_STRING([--disable-gtkspell], + [AS_HELP_STRING([--disable-gtkspell], [compile without GtkSpell automatic spell checking])], enable_gtkspell="$enableval", enable_gtkspell="yes") AC_ARG_ENABLE(gevolution, - [AC_HELP_STRING([--enable-gevolution], + [AS_HELP_STRING([--enable-gevolution], [compile with the Evolution plugin])], enable_gevolution="$enableval", enable_gevolution="no") AC_ARG_ENABLE(cap, - [AC_HELP_STRING([--enable-cap], + [AS_HELP_STRING([--enable-cap], [compile with Contact Availability Prediction plugin])], enable_cap="$enableval", enable_cap="no") AC_ARG_ENABLE(gestures, - [AC_HELP_STRING([--disable-gestures], + [AS_HELP_STRING([--disable-gestures], [compile without the gestures plugin])], enable_gestures="$enableval", enable_gestures="yes") AC_ARG_ENABLE(gcr, - [AC_HELP_STRING([--enable-gcr], + [AS_HELP_STRING([--enable-gcr], [compile with GCR certificate widgets])], enable_gcr="$enableval", enable_gcr="no") @@ -464,6 +463,9 @@ AC_SUBST(GTK_CFLAGS) AC_SUBST(GTK_LIBS) + GTK_PC_MODULE="gtk+-${with_gtk}.0" + AC_SUBST(GTK_PC_MODULE) + dnl We only really need Pango >= 1.4 for decent RTL support PKG_CHECK_MODULES(PANGO, [pango >= 1.4.0], AC_DEFINE(HAVE_PANGO14, 1, [Define if we have Pango 1.4 or newer.]),:) @@ -716,7 +718,7 @@ dnl ####################################################################### GNT_LIBS="" GNT_CFLAGS="" -AC_ARG_WITH(ncurses-headers, [AC_HELP_STRING([--with-ncurses-headers=DIR], +AC_ARG_WITH(ncurses-headers, [AS_HELP_STRING([--with-ncurses-headers=DIR], [compile finch against the ncurses includes in DIR])], [ac_ncurses_includes="$withval"], [ac_ncurses_includes=""]) if test "x$enable_consoleui" = "xyes"; then @@ -817,14 +819,25 @@ AC_SUBST(LIBXML_LIBS) dnl ####################################################################### +dnl # Check for JSON-GLib (required) +dnl ####################################################################### + +PKG_CHECK_MODULES([JSON], [json-glib-1.0 >= 0.14.0], , [ + AC_MSG_RESULT(no) + AC_MSG_ERROR([ +You must have JSON-GLib >= 0.14.0 development headers installed to build. +])]) + +AC_SUBST(JSON_CFLAGS) +AC_SUBST(JSON_LIBS) + +dnl ####################################################################### dnl # Check for zlib (required) dnl ####################################################################### PKG_CHECK_MODULES(ZLIB, [zlib >= 1.2.0], , [ - AC_MSG_RESULT(no) - AC_MSG_ERROR([ -You must have zlib >= 1.2.0 development headers installed to build. -])]) + AC_SEARCH_LIBS([deflate], [z], [], AC_MSG_ERROR([You must have zlib >= 1.2.0 development headers installed to build.]), []) +]) AC_SUBST(ZLIB_CFLAGS) AC_SUBST(ZLIB_LIBS) @@ -839,42 +852,102 @@ dnl ####################################################################### dnl # Check for GStreamer dnl ####################################################################### -dnl -dnl TODO: Depend on gstreamer >= 0.10.10, and remove the conditional use of -dnl gst_registry_fork_set_enabled. AC_ARG_ENABLE(gstreamer, - [AC_HELP_STRING([--disable-gstreamer], [compile without GStreamer audio support])], + [AS_HELP_STRING([--disable-gstreamer], [compile without GStreamer audio support])], enable_gst="$enableval", enable_gst="yes") +AC_ARG_WITH(gstreamer, [AS_HELP_STRING([--with-gstreamer=<version>], + [compile with GStreamer 0.10 or 1.0 interface (default: auto)])], + with_gstreamer="$withval", with_gstreamer="auto") if test "x$enable_gst" != "xno"; then - PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10], [ - AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer for playing sounds]) - AC_SUBST(GSTREAMER_CFLAGS) - AC_SUBST(GSTREAMER_LIBS) - AC_CHECK_LIB(gstreamer-0.10, gst_registry_fork_set_enabled, - [ AC_DEFINE(GST_CAN_DISABLE_FORKING, [], - [Define if gst_registry_fork_set_enabled exists])], - [], [$GSTREAMER_LIBS]) - ], [ - AC_MSG_RESULT(no) - enable_gst="no" - if test "x$force_deps" = "xyes" ; then - AC_MSG_ERROR([ + if test "x$with_gstreamer" == "xauto"; then + PKG_CHECK_MODULES(GSTREAMER, [gstreamer-1.0], [ + AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer for playing sounds]) + with_gstreamer="1.0" + AC_SUBST(GSTREAMER_CFLAGS) + AC_SUBST(GSTREAMER_LIBS) + dnl Check whether forking stuff is required for this version. + ], [ + PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10], [ + AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer for playing sounds]) + with_gstreamer="0.10" + AC_SUBST(GSTREAMER_CFLAGS) + AC_SUBST(GSTREAMER_LIBS) + ], [ + AC_MSG_RESULT(no) + enable_gst="no" + if test "x$force_deps" = "xyes" ; then + AC_MSG_ERROR([ GStreamer development headers not found. Use --disable-gstreamer if you do not need GStreamer (sound) support. ]) - fi]) + fi + ]) + ]) + elif test "x$with_gstreamer" == "x1.0"; then + PKG_CHECK_MODULES(GSTREAMER, [gstreamer-1.0], [ + AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer 1.0 for playing sounds]) + AC_SUBST(GSTREAMER_CFLAGS) + AC_SUBST(GSTREAMER_LIBS) + ], [ + AC_MSG_RESULT(no) + enable_gst="no" + if test "x$force_deps" = "xyes" ; then + AC_MSG_ERROR([ +GStreamer development headers not found. +Use --disable-gstreamer if you do not need GStreamer (sound) support. +]) + fi + ]) + elif test "x$with_gstreamer" == "x0.10"; then + PKG_CHECK_MODULES(GSTREAMER, [gstreamer-0.10], [ + AC_DEFINE(USE_GSTREAMER, 1, [Use GStreamer 0.10 for playing sounds]) + AC_SUBST(GSTREAMER_CFLAGS) + AC_SUBST(GSTREAMER_LIBS) + ], [ + AC_MSG_RESULT(no) + enable_gst="no" + if test "x$force_deps" = "xyes" ; then + AC_MSG_ERROR([ +GStreamer development headers not found. +Use --disable-gstreamer if you do not need GStreamer (sound) support. +]) + fi + ]) + else + AC_MSG_ERROR([--with-gstreamer must specify one of 0.10, 1.0 or auto.]) + fi +fi + +dnl ####################################################################### +dnl # Check for GStreamer Video +dnl ####################################################################### +if test "x$enable_gst" != "xno" -a "x$with_gstreamer" == "x1.0"; then + AC_ARG_ENABLE(gstreamer-video, + [AS_HELP_STRING([--disable-gstreamer-video], [compile without GStreamer 1.0 Video Overlay support])], + enable_gstvideo="$enableval", enable_gstvideo="yes") + if test "x$enable_gstvideo" != "xno"; then + PKG_CHECK_MODULES(GSTVIDEO, [gstreamer-video-1.0], [ + AC_DEFINE(USE_GSTVIDEO, 1, [Use GStreamer Video Overlay support]) + AC_SUBST(GSTVIDEO_CFLAGS) + AC_SUBST(GSTVIDEO_LIBS) + ], [ + enable_gstvideo="no" + ]) + fi +else + enable_gstvideo="no" fi dnl ####################################################################### dnl # Check for GStreamer Interfaces dnl ####################################################################### -if test "x$enable_gst" != "xno"; then +if test "x$enable_gst" != "xno" -a "x$with_gstreamer" == "x0.10"; then AC_ARG_ENABLE(gstreamer-interfaces, - [AC_HELP_STRING([--disable-gstreamer-interfaces], [compile without GStreamer interface support])], + [AS_HELP_STRING([--disable-gstreamer-interfaces], [compile without GStreamer 0.10 interface support])], enable_gstinterfaces="$enableval", enable_gstinterfaces="yes") if test "x$enable_gstinterfaces" != "xno"; then PKG_CHECK_MODULES(GSTINTERFACES, [gstreamer-interfaces-0.10], [ - AC_DEFINE(USE_GSTINTERFACES, 1, [Use GStreamer interfaces for X overlay support]) + AC_DEFINE(USE_GSTINTERFACES, 1, [Use GStreamer 0.10 interfaces for X overlay support]) AC_SUBST(GSTINTERFACES_CFLAGS) AC_SUBST(GSTINTERFACES_LIBS) ], [ @@ -889,32 +962,43 @@ dnl # Check for Farstream dnl ####################################################################### AC_ARG_ENABLE(farstream, - [AC_HELP_STRING([--disable-farstream], [compile without farstream support])], + [AS_HELP_STRING([--disable-farstream], [compile without farstream support])], enable_farstream="$enableval", enable_farstream="yes") if test "x$enable_farstream" != "xno"; then - PKG_CHECK_MODULES(FARSTREAM, [farstream-0.1], [ - AC_SUBST(FARSTREAM_CFLAGS) - AC_SUBST(FARSTREAM_LIBS) - ], [ - # Try farsight. - PKG_CHECK_MODULES(FARSTREAM, [farsight2-0.10 >= 0.0.9], [ - AC_DEFINE(HAVE_FARSIGHT, 1, [Use Farsight instead of Farstream]) + if test "x$with_gstreamer" == "x1.0"; then + PKG_CHECK_MODULES(FARSTREAM, [farstream-0.2], [ + AC_SUBST(FARSTREAM_CFLAGS) + AC_SUBST(FARSTREAM_LIBS) + ], [ + enable_farstream="no" + ]) + else + PKG_CHECK_MODULES(FARSTREAM, [farstream-0.1], [ AC_SUBST(FARSTREAM_CFLAGS) AC_SUBST(FARSTREAM_LIBS) - ], [ - enable_farstream="no" - ]) - ]) - fi + ], [ + # Try farsight. + PKG_CHECK_MODULES(FARSTREAM, [farsight2-0.10 >= 0.0.9], [ + AC_DEFINE(HAVE_FARSIGHT, 1, [Use Farsight instead of Farstream]) + AC_SUBST(FARSTREAM_CFLAGS) + AC_SUBST(FARSTREAM_LIBS) + ], [ + enable_farstream="no" + ]) + ]) + fi +fi dnl ####################################################################### dnl # Check for Voice and Video support dnl ####################################################################### AC_ARG_ENABLE(vv, - [AC_HELP_STRING([--disable-vv], [compile without voice and video support])], + [AS_HELP_STRING([--disable-vv], [compile without voice and video support])], enable_vv="$enableval", enable_vv="yes") if test "x$enable_vv" != "xno"; then - if test "x$enable_gstreamer" != "xno" -a "x$enable_gstinterfaces" != "xno" -a "x$enable_farstream" != "xno"; then + if test "x$enable_gst" != "xno" -a "x$with_gstreamer" == "x1.0" -a "x$enable_gstvideo" != "xno" -a "x$enable_farstream" != "xno"; then + AC_DEFINE(USE_VV, 1, [Use voice and video]) + elif test "x$enable_gst" != "xno" -a "x$with_gstreamer" == "x0.10" -a "x$enable_gstinterfaces" != "xno" -a "x$enable_farstream" != "xno"; then AC_DEFINE(USE_VV, 1, [Use voice and video]) else enable_vv="no" @@ -927,14 +1011,14 @@ fi fi fi -AM_CONDITIONAL(USE_VV, test "x$enable_gstreamer" != "xno" -a "x$enable_gstinterfaces" != "xno" -a "x$enable_farstream" != "xno") +AM_CONDITIONAL(USE_VV, test "x$enable_vv" != "xno") dnl ####################################################################### dnl # Check for Internationalized Domain Name support dnl ####################################################################### AC_ARG_ENABLE(idn, - [AC_HELP_STRING([--disable-idn], [compile without IDN support])], + [AS_HELP_STRING([--disable-idn], [compile without IDN support])], [enable_idn="$enableval" force_idn=$enableval], [enable_idn="yes" force_idn=no]) if test "x$enable_idn" != "xno"; then PKG_CHECK_MODULES(IDN, libidn >= 0.0.0, [ @@ -957,7 +1041,7 @@ dnl # Check for Meanwhile headers (for Sametime) dnl ####################################################################### AC_ARG_ENABLE(meanwhile, - [AC_HELP_STRING([--disable-meanwhile], + [AS_HELP_STRING([--disable-meanwhile], [compile without meanwhile (required for Sametime support)])], enable_meanwhile="$enableval", enable_meanwhile="yes") if test "x$enable_meanwhile" = "xyes"; then @@ -979,13 +1063,13 @@ dnl # Check for Native Avahi headers (for Bonjour) dnl ####################################################################### AC_ARG_ENABLE(avahi, - [AC_HELP_STRING([--disable-avahi], + [AS_HELP_STRING([--disable-avahi], [compile without avahi (required for Bonjour support)])], enable_avahi="$enableval", enable_avahi="yes") if test "x$enable_avahi" = "xyes"; then - AC_ARG_WITH(avahi-client-includes, [AC_HELP_STRING([--with-avahi-client-includes=DIR], [compile the Bonjour plugin against the Avahi Client includes in DIR])], [ac_avahi_client_includes="$withval"], [ac_avahi_client_includes="no"]) - AC_ARG_WITH(avahi-client-libs, [AC_HELP_STRING([--with-avahi-client-libs=DIR], [compile the Bonjour plugin against the Avahi Client libs in DIR])], [ac_avahi_client_libs="$withval"], [ac_avahi_client_libs="no"]) + AC_ARG_WITH(avahi-client-includes, [AS_HELP_STRING([--with-avahi-client-includes=DIR], [compile the Bonjour plugin against the Avahi Client includes in DIR])], [ac_avahi_client_includes="$withval"], [ac_avahi_client_includes="no"]) + AC_ARG_WITH(avahi-client-libs, [AS_HELP_STRING([--with-avahi-client-libs=DIR], [compile the Bonjour plugin against the Avahi Client libs in DIR])], [ac_avahi_client_libs="$withval"], [ac_avahi_client_libs="no"]) AVAHI_CFLAGS="" AVAHI_LIBS="" @@ -1032,8 +1116,8 @@ dnl ####################################################################### dnl # Check for SILC client includes and libraries dnl ####################################################################### -AC_ARG_WITH(silc-includes, [AC_HELP_STRING([--with-silc-includes=DIR], [compile the SILC plugin against includes in DIR])], [ac_silc_includes="$withval"], [ac_silc_includes="no"]) -AC_ARG_WITH(silc-libs, [AC_HELP_STRING([--with-silc-libs=DIR], [compile the SILC plugin against the SILC libs in DIR])], [ac_silc_libs="$withval"], [ac_silc_libs="no"]) +AC_ARG_WITH(silc-includes, [AS_HELP_STRING([--with-silc-includes=DIR], [compile the SILC plugin against includes in DIR])], [ac_silc_includes="$withval"], [ac_silc_includes="no"]) +AC_ARG_WITH(silc-libs, [AS_HELP_STRING([--with-silc-libs=DIR], [compile the SILC plugin against the SILC libs in DIR])], [ac_silc_libs="$withval"], [ac_silc_libs="no"]) SILC_CFLAGS="" SILC_LIBS="" have_silc="no" @@ -1079,8 +1163,8 @@ dnl ####################################################################### dnl # Check for Gadu-Gadu client includes and libraries dnl ####################################################################### -AC_ARG_WITH(gadu-includes, [AC_HELP_STRING([--with-gadu-includes=DIR], [compile the Gadu-Gadu plugin against includes in DIR])], [ac_gadu_includes="$withval"], [ac_gadu_includes="no"]) -AC_ARG_WITH(gadu-libs, [AC_HELP_STRING([--with-gadu-libs=DIR], [compile the Gadu-Gadu plugin against the libs in DIR])], [ac_gadu_libs="$withval"], [ac_gadu_libs="no"]) +AC_ARG_WITH(gadu-includes, [AS_HELP_STRING([--with-gadu-includes=DIR], [compile the Gadu-Gadu plugin against includes in DIR])], [ac_gadu_includes="$withval"], [ac_gadu_includes="no"]) +AC_ARG_WITH(gadu-libs, [AS_HELP_STRING([--with-gadu-libs=DIR], [compile the Gadu-Gadu plugin against the libs in DIR])], [ac_gadu_libs="$withval"], [ac_gadu_libs="no"]) GADU_CFLAGS="" GADU_LIBS="" GADU_LIBGADU_VERSION=1.11.2 @@ -1183,7 +1267,7 @@ AC_ARG_ENABLE(distrib,,,enable_distrib=no) AM_CONDITIONAL(DISTRIB, test "x$enable_distrib" = "xyes") DYNAMIC_PRPLS=all -AC_ARG_WITH(static-prpls, [AC_HELP_STRING([--with-static-prpls], [Link to certain protocols statically])], [STATIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`], [STATIC_PRPLS=""]) +AC_ARG_WITH(static-prpls, [AS_HELP_STRING([--with-static-prpls], [Link to certain protocols statically])], [STATIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`], [STATIC_PRPLS=""]) if test "x$STATIC_PRPLS" != "x" -a "x$DYNAMIC_PRPLS" = "xall"; then DYNAMIC_PRPLS="" fi @@ -1245,7 +1329,7 @@ simple) static_simple=yes ;; yahoo) static_yahoo=yes ;; zephyr) static_zephyr=yes ;; - *) echo "Invalid static protocol $i!!" ; exit ;; + *) echo "Invalid static protocol $i!!" ; exit 1 ;; esac done AM_CONDITIONAL(STATIC_BONJOUR, test "x$static_bonjour" = "xyes") @@ -1266,7 +1350,7 @@ AC_DEFINE_UNQUOTED(STATIC_PROTO_INIT, $extern_init static void static_proto_init(void) { $load_proto }, [Loads static protocol plugin module initialization functions.]) -AC_ARG_WITH(dynamic_prpls, [AC_HELP_STRING([--with-dynamic-prpls], [specify which protocols to build dynamically])], [DYNAMIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`]) +AC_ARG_WITH(dynamic_prpls, [AS_HELP_STRING([--with-dynamic-prpls], [specify which protocols to build dynamically])], [DYNAMIC_PRPLS=`echo $withval | $sedpath 's/,/ /g'`]) if test "x$DYNAMIC_PRPLS" = "xall" ; then DYNAMIC_PRPLS="bonjour gg irc jabber msn mxit myspace novell oscar sametime silc simple yahoo zephyr" fi @@ -1299,19 +1383,19 @@ simple) dynamic_simple=yes ;; yahoo) dynamic_yahoo=yes ;; zephyr) dynamic_zephyr=yes ;; - *) echo "Invalid dynamic protocol $i!!" ; exit ;; + *) echo "Invalid dynamic protocol $i!!" ; exit 1 ;; esac done -AC_ARG_ENABLE(plugins, [AC_HELP_STRING([--disable-plugins], [compile without plugin support])], , enable_plugins=yes) -AC_ARG_WITH(krb4, [AC_HELP_STRING([--with-krb4=PREFIX], [compile Zephyr plugin with Kerberos 4 support])], kerberos="$withval", kerberos="no") -AC_ARG_WITH(zephyr, [AC_HELP_STRING([--with-zephyr=PREFIX], [compile Zephyr plugin against external libzephyr])], zephyr="$withval", zephyr="no") +AC_ARG_ENABLE(plugins, [AS_HELP_STRING([--disable-plugins], [compile without plugin support])], , enable_plugins=yes) +AC_ARG_WITH(krb4, [AS_HELP_STRING([--with-krb4=PREFIX], [compile Zephyr plugin with Kerberos 4 support])], kerberos="$withval", kerberos="no") +AC_ARG_WITH(zephyr, [AS_HELP_STRING([--with-zephyr=PREFIX], [compile Zephyr plugin against external libzephyr])], zephyr="$withval", zephyr="no") AM_CONDITIONAL(EXTERNAL_LIBZEPHYR, test "x$zephyr" != "xno") AC_CHECK_HEADERS(sys/utsname.h) AC_CHECK_FUNC(uname) -AC_ARG_ENABLE(fortify, [AC_HELP_STRING([--disable-fortify], [compile without FORTIFY_SOURCE support])], , enable_fortify=yes) +AC_ARG_ENABLE(fortify, [AS_HELP_STRING([--disable-fortify], [compile without FORTIFY_SOURCE support])], , enable_fortify=yes) DEBUG_CFLAGS="$DEBUG_CFLAGS -DPURPLE_DISABLE_DEPRECATED -DPIDGIN_DISABLE_DEPRECATED -DFINCH_DISABLE_DEPRECATED -DGNT_DISABLE_DEPRECATED" if test "x$GCC" = "xyes"; then @@ -1404,8 +1488,8 @@ dnl # Check for D-Bus libraries dnl ####################################################################### -AC_ARG_ENABLE(dbus, [AC_HELP_STRING([--disable-dbus], [disable D-Bus support])], , enable_dbus=yes) -AC_ARG_ENABLE(nm, [AC_HELP_STRING([--disable-nm], [disable NetworkManager support (requires D-Bus)])], enable_nm=$enableval, enable_nm=yes) +AC_ARG_ENABLE(dbus, [AS_HELP_STRING([--disable-dbus], [disable D-Bus support])], , enable_dbus=yes) +AC_ARG_ENABLE(nm, [AS_HELP_STRING([--disable-nm], [disable NetworkManager support (requires D-Bus)])], enable_nm=$enableval, enable_nm=yes) if test "x$enable_dbus" = "xyes" ; then AC_CHECK_PROG(enable_dbus, dbus-binding-tool, yes, no) @@ -1608,7 +1692,7 @@ dnl in C (brrrr ...). AC_ARG_WITH([python], - AC_HELP_STRING([--with-python=PATH], + AS_HELP_STRING([--with-python=PATH], [which python interpreter to use for dbus code generation]), PYTHON=$withval) @@ -1642,7 +1726,7 @@ dnl # although a newer dbus is installed. But I have tried to order the dnl # directory searching to keep this situation at a minimum. dnl ########################################################################### -AC_ARG_WITH(dbus-services, [AC_HELP_STRING([--with-dbus-services=<dir>], [where the D-Bus services directory is located.])]) +AC_ARG_WITH(dbus-services, [AS_HELP_STRING([--with-dbus-services=<dir>], [where the D-Bus services directory is located.])]) DBUS_SERVICES_DIR="" @@ -1723,7 +1807,7 @@ dnl ####################################################################### dnl # Check for Mono support dnl ####################################################################### -AC_ARG_ENABLE(mono, [AC_HELP_STRING([--enable-mono], [compile with Mono runtime support (experimental)])], , enable_mono=no) +AC_ARG_ENABLE(mono, [AS_HELP_STRING([--enable-mono], [compile with Mono runtime support (experimental)])], , enable_mono=no) if test x"$enable_mono" = x"yes" ; then PKG_CHECK_MODULES(MONO, mono, [ AC_SUBST(MONO_CFLAGS) @@ -1764,7 +1848,7 @@ dnl ####################################################################### dnl # Check for Perl support dnl ####################################################################### -AC_ARG_ENABLE(perl, [AC_HELP_STRING([--disable-perl], [compile without perl scripting])], , enable_perl=yes) +AC_ARG_ENABLE(perl, [AS_HELP_STRING([--disable-perl], [compile without perl scripting])], , enable_perl=yes) if test "$enable_plugins" = no ; then enable_perl=no @@ -1887,7 +1971,7 @@ dnl # Thanks go to Evolution for the checks. dnl ####################################################################### -AC_ARG_WITH(system-ssl-certs, [AC_HELP_STRING([--with-system-ssl-certs=<dir>], [directory containing system-wide SSL CA certificates])], [ssl_certificates_dir=$withval]) +AC_ARG_WITH(system-ssl-certs, [AS_HELP_STRING([--with-system-ssl-certs=<dir>], [directory containing system-wide SSL CA certificates])], [ssl_certificates_dir=$withval]) SSL_CERTIFICATES_DIR="" if ! test -z "$ssl_certificates_dir" ; then @@ -1957,7 +2041,7 @@ fi AC_ARG_WITH(gnutls-libs, - [AC_HELP_STRING([--with-gnutls-libs=PREFIX], [location of GnuTLS libraries.])], + [AS_HELP_STRING([--with-gnutls-libs=PREFIX], [location of GnuTLS libraries.])], [ with_gnutls_libs="$withval" ]) if test "x$with_gnutls_libs" != "xno" -a \ @@ -1972,8 +2056,8 @@ AC_CACHE_CHECK([for GnuTLS libraries], ac_cv_gnutls_libs, [ - LIBS="$LIBS $with_gnutls_libs -lgnutls -lgcrypt" - AC_TRY_LINK_FUNC(gnutls_init, ac_cv_gnutls_libs="yes", ac_cv_gnutls_libs="no") + LIBS="$LIBS $with_gnutls_libs -lgnutls" + AC_LINK_IFELSE([AC_LANG_CALL([], [gnutls_init])], ac_cv_gnutls_libs="yes", ac_cv_gnutls_libs="no") LIBS="$LIBS_save" ]) @@ -1981,7 +2065,7 @@ AC_DEFINE(HAVE_GNUTLS, 1, [Define if you have GnuTLS]) AC_DEFINE(HAVE_SSL) msg_gnutls="GnuTLS" - GNUTLS_LIBS="$with_gnutls_libs -lgnutls -lgcrypt" + GNUTLS_LIBS="$with_gnutls_libs -lgnutls" enable_gnutls="yes" else @@ -2044,19 +2128,19 @@ looked_for_nss="yes" AC_ARG_WITH(nspr-includes, - [AC_HELP_STRING([--with-nspr-includes=PREFIX], [specify location of Mozilla nspr4 includes.])], + [AS_HELP_STRING([--with-nspr-includes=PREFIX], [specify location of Mozilla nspr4 includes.])], [with_nspr_includes="$withval"]) AC_ARG_WITH(nspr-libs, - [AC_HELP_STRING([--with-nspr-libs=PREFIX], [specify location of Mozilla nspr4 libs.])], + [AS_HELP_STRING([--with-nspr-libs=PREFIX], [specify location of Mozilla nspr4 libs.])], [with_nspr_libs="$withval"]) AC_ARG_WITH(nss-includes, - [AC_HELP_STRING([--with-nss-includes=PREFIX], [specify location of Mozilla nss3 includes.])], + [AS_HELP_STRING([--with-nss-includes=PREFIX], [specify location of Mozilla nss3 includes.])], [with_nss_includes="$withval"]) AC_ARG_WITH(nss-libs, - [AC_HELP_STRING([--with-nss-libs=PREFIX], [specify location of Mozilla nss3 libs.])], + [AS_HELP_STRING([--with-nss-libs=PREFIX], [specify location of Mozilla nss3 libs.])], [with_nss_libs="$withval"]) @@ -2176,7 +2260,7 @@ LDFLAGS="$LDFLAGS" fi - AC_TRY_LINK_FUNC(PR_Init, + AC_LINK_IFELSE([AC_LANG_CALL([], [PR_Init])], [ac_cv_moz_nspr_libs="yes"], [ac_cv_moz_nspr_libs="no"]) @@ -2264,7 +2348,7 @@ LDFLAGS="$LDFLAGS -L$with_nspr_libs -L$with_nss_libs" LIBS="$nsslibs $nsprlibs" - AC_TRY_LINK_FUNC(NSS_Init, + AC_LINK_IFELSE([AC_LANG_CALL([], [NSS_Init])], [ac_cv_moz_nss_libs="yes"], [ac_cv_moz_nss_libs="no"]) @@ -2272,7 +2356,7 @@ nsslibs="-lssl3 -lsmime3 -lnss3 -lsoftokn3" LDFLAGS="$LDFLAGS -L$with_nspr_libs -L$with_nss_libs" LIBS="$LIBS $nsslibs" - AC_TRY_LINK_FUNC(NSS_Init, + AC_LINK_IFELSE([AC_LANG_CALL([], [NSS_Init])], [ac_cv_moz_nss_libs="yes"], [ac_cv_moz_nss_libs="no"]) fi @@ -2344,9 +2428,9 @@ dnl ####################################################################### dnl # Check for Tcl dnl ####################################################################### -AC_ARG_ENABLE(tcl, [AC_HELP_STRING([--disable-tcl], +AC_ARG_ENABLE(tcl, [AS_HELP_STRING([--disable-tcl], [compile without Tcl scripting])], enable_tcl="$enableval", enable_tcl="yes") -AC_ARG_WITH(tclconfig, [AC_HELP_STRING([--with-tclconfig=DIR], +AC_ARG_WITH(tclconfig, [AS_HELP_STRING([--with-tclconfig=DIR], [directory containing tclConfig.sh])]) if test "$enable_plugins" = no; then @@ -2423,9 +2507,9 @@ dnl ####################################################################### dnl # Check for Tk dnl ####################################################################### -AC_ARG_ENABLE(tk, [AC_HELP_STRING([--disable-tk], +AC_ARG_ENABLE(tk, [AS_HELP_STRING([--disable-tk], [compile without Tcl support for Tk])], enable_tk="$enableval", enable_tk="yes") -AC_ARG_WITH(tkconfig, [AC_HELP_STRING([--with-tkconfig=DIR], +AC_ARG_WITH(tkconfig, [AS_HELP_STRING([--with-tkconfig=DIR], [directory containing tkConfig.sh])]) if test "$enable_tcl" = yes -a "$enable_tk" = yes; then @@ -2507,7 +2591,7 @@ dnl AC_CHECK_SIZEOF(short) AC_CHECK_FUNCS(snprintf connect) AC_SUBST(SASL_LIBS) -AC_ARG_ENABLE(cyrus-sasl, AC_HELP_STRING([--enable-cyrus-sasl], [enable Cyrus SASL support for jabberd]), enable_cyrus_sasl=$enableval, enable_cyrus_sasl=no) +AC_ARG_ENABLE(cyrus-sasl, AS_HELP_STRING([--enable-cyrus-sasl], [enable Cyrus SASL support for jabberd]), enable_cyrus_sasl=$enableval, enable_cyrus_sasl=no) if test "x$enable_cyrus_sasl" = "xyes" ; then AC_CHECK_LIB(sasl2, sasl_client_init, [ AM_CONDITIONAL(USE_CYRUS_SASL, true) @@ -2658,14 +2742,14 @@ dnl ####################################################################### dnl # Disable pixmap installation dnl ####################################################################### -AC_ARG_ENABLE(pixmaps-install, AC_HELP_STRING([--disable-pixmaps-install], [disable installation of pixmap files - Pidgin still needs them!]), enable_pixmaps="$enableval", enable_pixmaps=yes) +AC_ARG_ENABLE(pixmaps-install, AS_HELP_STRING([--disable-pixmaps-install], [disable installation of pixmap files - Pidgin still needs them!]), enable_pixmaps="$enableval", enable_pixmaps=yes) AM_CONDITIONAL(INSTALL_PIXMAPS, test "x$enable_pixmaps" = "xyes") dnl ####################################################################### dnl # Tweak status tray icon installation directory dnl ####################################################################### -AC_ARG_ENABLE(trayicon-compat, AC_HELP_STRING([--enable-trayicon-compat], [install tray icons in location compatible with older releases of hicolor-icon-theme]), enable_traycompat="$enableval", enable_traycompat=no) +AC_ARG_ENABLE(trayicon-compat, AS_HELP_STRING([--enable-trayicon-compat], [install tray icons in location compatible with older releases of hicolor-icon-theme]), enable_traycompat="$enableval", enable_traycompat=no) AM_CONDITIONAL(ENABLE_TRAYCOMPAT, test "x$enable_traycompat" = "xyes") @@ -2673,15 +2757,15 @@ dnl # Check for Doxygen and dot (part of GraphViz) dnl ####################################################################### AC_ARG_ENABLE(doxygen, - [AC_HELP_STRING([--disable-doxygen], + [AS_HELP_STRING([--disable-doxygen], [disable documentation with doxygen])], enable_doxygen="$enableval", enable_doxygen="yes") AC_ARG_ENABLE(dot, - [AC_HELP_STRING([--disable-dot], + [AS_HELP_STRING([--disable-dot], [disable graphs in doxygen via 'dot'])], enable_dot="$enableval", enable_dot="yes") AC_ARG_ENABLE(devhelp, - [AC_HELP_STRING([--disable-devhelp], + [AS_HELP_STRING([--disable-devhelp], [disable building index for devhelp documentation browser])], enable_devhelp="$enableval", enable_devhelp="yes") @@ -2726,7 +2810,7 @@ AM_CONDITIONAL(HAVE_DOXYGEN, test "x$enable_doxygen" = "xyes") AM_CONDITIONAL(HAVE_XSLTPROC, test "x$enable_devhelp" = "xyes") -AC_ARG_ENABLE(debug, [AC_HELP_STRING([--enable-debug], +AC_ARG_ENABLE(debug, [AS_HELP_STRING([--enable-debug], [compile with debugging support])], , enable_debug=no) if test "x$enable_debug" = "xyes" ; then @@ -2822,6 +2906,7 @@ echo Protocols to link statically.. : $STATIC_PRPLS echo echo Build with GStreamer support.. : $enable_gst +echo Build for GStreamer version... : $with_gstreamer echo Build with D-Bus support...... : $enable_dbus echo Build with voice and video.... : $enable_vv if test "x$enable_dbus" = "xyes" ; then
--- a/finch/finch.c Thu Aug 23 01:27:48 2012 -0400 +++ b/finch/finch.c Fri Jan 25 02:22:38 2013 -0500 @@ -219,11 +219,7 @@ gnt_input_add, g_source_remove, NULL, /* input_get_error */ -#if GLIB_CHECK_VERSION(2,14,0) g_timeout_add_seconds, -#else - NULL, -#endif /* padding */ NULL, @@ -381,19 +377,12 @@ abort(); } - /* TODO: Move blist loading into purple_blist_init() */ - purple_set_blist(purple_blist_new()); - purple_blist_load(); - /* TODO: should this be moved into finch_prefs_init() ? */ finch_prefs_update_old(); /* load plugins we had when we quit */ purple_plugins_load_saved("/finch/plugins/loaded"); - /* TODO: Move pounces loading into purple_pounces_init() */ - purple_pounces_load(); - if (opt_nologin) { /* Set all accounts to "offline" */
--- a/finch/gntsound.c Thu Aug 23 01:27:48 2012 -0400 +++ b/finch/gntsound.c Fri Jan 25 02:22:38 2013 -0500 @@ -553,7 +553,11 @@ return; } +#if GST_CHECK_VERSION(1,0,0) play = gst_element_factory_make("playbin", "play"); +#else + play = gst_element_factory_make("playbin2", "play"); +#endif if (play == NULL) { return;
--- a/finch/libgnt/gnt.h Thu Aug 23 01:27:48 2012 -0400 +++ b/finch/libgnt/gnt.h Fri Jan 25 02:22:38 2013 -0500 @@ -40,19 +40,6 @@ #include "gntkeys.h" /** - * Get things to compile in Glib < 2.8 - */ -#if !GLIB_CHECK_VERSION(2,8,0) - #define G_PARAM_STATIC_NAME G_PARAM_PRIVATE - #define G_PARAM_STATIC_NICK G_PARAM_PRIVATE - #define G_PARAM_STATIC_BLURB G_PARAM_PRIVATE -#endif - -#if !GLIB_CHECK_VERSION(2,14,0) - #define g_timeout_add_seconds(time, callback, data) g_timeout_add(time * 1000, callback, data) -#endif - -/** * Initialize GNT. */ void gnt_init(void);
--- a/finch/libgnt/gntcolors.c Thu Aug 23 01:27:48 2012 -0400 +++ b/finch/libgnt/gntcolors.c Fri Jan 25 02:22:38 2013 -0500 @@ -142,7 +142,6 @@ restore_colors(); } -#if GLIB_CHECK_VERSION(2,6,0) int gnt_colors_get_color(char *key) { @@ -293,8 +292,6 @@ g_strfreev(keys); } -#endif /* GKeyFile */ - int gnt_color_pair(int pair) { return (hascolors ? COLOR_PAIR(pair) :
--- a/finch/libgnt/gntcolors.h Thu Aug 23 01:27:48 2012 -0400 +++ b/finch/libgnt/gntcolors.h Fri Jan 25 02:22:38 2013 -0500 @@ -71,7 +71,6 @@ */ void gnt_uninit_colors(void); -#if GLIB_CHECK_VERSION(2,6,0) /** * Parse color information from a file. * @@ -96,7 +95,6 @@ * @since 2.4.0 */ int gnt_colors_get_color(char *key); -#endif /** * Return the appropriate character attribute for a specified color.
--- a/finch/libgnt/gntfilesel.c Thu Aug 23 01:27:48 2012 -0400 +++ b/finch/libgnt/gntfilesel.c Fri Jan 25 02:22:38 2013 -0500 @@ -66,106 +66,6 @@ } } -#if !GLIB_CHECK_VERSION(2,8,0) -/* ripped from glib/gfileutils.c */ -static gchar * -g_build_path_va (const gchar *separator, - gchar **str_array) -{ - GString *result; - gint separator_len = strlen (separator); - gboolean is_first = TRUE; - gboolean have_leading = FALSE; - const gchar *single_element = NULL; - const gchar *next_element; - const gchar *last_trailing = NULL; - gint i = 0; - - result = g_string_new (NULL); - - next_element = str_array[i++]; - - while (TRUE) { - const gchar *element; - const gchar *start; - const gchar *end; - - if (next_element) { - element = next_element; - next_element = str_array[i++]; - } else - break; - - /* Ignore empty elements */ - if (!*element) - continue; - - start = element; - - if (separator_len) { - while (start && - strncmp (start, separator, separator_len) == 0) - start += separator_len; - } - - end = start + strlen (start); - - if (separator_len) { - while (end >= start + separator_len && - strncmp (end - separator_len, separator, separator_len) == 0) - end -= separator_len; - - last_trailing = end; - while (last_trailing >= element + separator_len && - strncmp (last_trailing - separator_len, separator, separator_len) == 0) - last_trailing -= separator_len; - - if (!have_leading) { - /* If the leading and trailing separator strings are in the - * same element and overlap, the result is exactly that element - */ - if (last_trailing <= start) - single_element = element; - - g_string_append_len (result, element, start - element); - have_leading = TRUE; - } else - single_element = NULL; - } - - if (end == start) - continue; - - if (!is_first) - g_string_append (result, separator); - - g_string_append_len (result, start, end - start); - is_first = FALSE; - } - - if (single_element) { - g_string_free (result, TRUE); - return g_strdup (single_element); - } else { - if (last_trailing) - g_string_append (result, last_trailing); - - return g_string_free (result, FALSE); - } -} - -static gchar * -g_build_pathv (const gchar *separator, - gchar **args) -{ - if (!args) - return NULL; - - return g_build_path_va (separator, args); -} - -#endif - static char * process_path(const char *path) {
--- a/finch/libgnt/gntmain.c Thu Aug 23 01:27:48 2012 -0400 +++ b/finch/libgnt/gntmain.c Fri Jan 25 02:22:38 2013 -0500 @@ -675,7 +675,6 @@ return gnt_clipboard_get_string(clipboard); } -#if GLIB_CHECK_VERSION(2,4,0) typedef struct { void (*callback)(int status, gpointer data); @@ -697,13 +696,11 @@ refresh(); refresh_screen(); } -#endif gboolean gnt_giveup_console(const char *wd, char **argv, char **envp, gint *stin, gint *stout, gint *sterr, void (*callback)(int status, gpointer data), gpointer data) { -#if GLIB_CHECK_VERSION(2,4,0) GPid pid = 0; ChildProcess *cp = NULL; @@ -721,18 +718,11 @@ g_child_watch_add(pid, reap_child, cp); return TRUE; -#else - return FALSE; -#endif } gboolean gnt_is_refugee() { -#if GLIB_CHECK_VERSION(2,4,0) return (wm && wm->mode == GNT_KP_MODE_WAIT_ON_CHILD); -#else - return FALSE; -#endif } const char *C_(const char *x)
--- a/finch/libgnt/gntprogressbar.c Thu Aug 23 01:27:48 2012 -0400 +++ b/finch/libgnt/gntprogressbar.c Fri Jan 25 02:22:38 2013 -0500 @@ -36,16 +36,9 @@ struct _GntProgressBar { GntWidget parent; -#if !GLIB_CHECK_VERSION(2,4,0) - GntProgressBarPrivate priv; -#endif }; -#if GLIB_CHECK_VERSION(2,4,0) #define GNT_PROGRESS_BAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GNT_TYPE_PROGRESS_BAR, GntProgressBarPrivate)) -#else -#define GNT_PROGRESS_BAR_GET_PRIVATE(o) &(GNT_PROGRESS_BAR(o)->priv) -#endif static GntWidgetClass *parent_class = NULL; @@ -128,9 +121,7 @@ parent_class = GNT_WIDGET_CLASS (klass); -#if GLIB_CHECK_VERSION(2,4,0) g_type_class_add_private (g_class, sizeof (GntProgressBarPrivate)); -#endif parent_class->draw = gnt_progress_bar_draw; parent_class->size_request = gnt_progress_bar_size_request;
--- a/finch/libgnt/gntstyle.c Thu Aug 23 01:27:48 2012 -0400 +++ b/finch/libgnt/gntstyle.c Fri Jan 25 02:22:38 2013 -0500 @@ -35,9 +35,7 @@ #define MAX_WORKSPACES 99 -#if GLIB_CHECK_VERSION(2,6,0) static GKeyFile *gkfile; -#endif static char * str_styles[GNT_STYLES]; static int int_styles[GNT_STYLES]; @@ -50,7 +48,6 @@ char *gnt_style_get_from_name(const char *group, const char *key) { -#if GLIB_CHECK_VERSION(2,6,0) const char *prg = g_get_prgname(); if ((group == NULL || *group == '\0') && prg && g_key_file_has_group(gkfile, prg)) @@ -58,15 +55,11 @@ if (!group) group = "general"; return g_key_file_get_value(gkfile, group, key, NULL); -#else - return NULL; -#endif } int gnt_style_get_color(char *group, char *key) { -#if GLIB_CHECK_VERSION(2,6,0) int fg = 0, bg = 0; gsize n; char **vals; @@ -79,14 +72,10 @@ } g_strfreev(vals); return ret; -#else - return 0; -#endif } char **gnt_style_get_string_list(const char *group, const char *key, gsize *length) { -#if GLIB_CHECK_VERSION(2,6,0) const char *prg = g_get_prgname(); if ((group == NULL || *group == '\0') && prg && g_key_file_has_group(gkfile, prg)) @@ -94,9 +83,6 @@ if (!group) group = "general"; return g_key_file_get_string_list(gkfile, group, key, length, NULL); -#else - return NULL; -#endif } gboolean gnt_style_get_bool(GntStyle style, gboolean def) @@ -134,7 +120,6 @@ return def; } -#if GLIB_CHECK_VERSION(2,6,0) static void refine(char *text) { @@ -175,11 +160,9 @@ { return (char *)gnt_key_translate(key); } -#endif void gnt_style_read_workspaces(GntWM *wm) { -#if GLIB_CHECK_VERSION(2,6,0) int i; gchar *name; gsize c; @@ -212,11 +195,10 @@ g_strfreev(titles); } } -#endif } + void gnt_style_read_actions(GType type, GntBindableClass *klass) { -#if GLIB_CHECK_VERSION(2,6,0) char *name; GError *error = NULL; @@ -264,12 +246,10 @@ g_strfreev(keys); } g_free(name); -#endif } gboolean gnt_style_read_menu_accels(const char *name, GHashTable *table) { -#if GLIB_CHECK_VERSION(2,6,0) char *kname; GError *error = NULL; gboolean ret = FALSE; @@ -322,13 +302,10 @@ g_free(kname); return ret; -#endif - return FALSE; } void gnt_styles_get_keyremaps(GType type, GHashTable *hash) { -#if GLIB_CHECK_VERSION(2,6,0) char *name; GError *error = NULL; @@ -373,10 +350,8 @@ } g_free(name); -#endif } -#if GLIB_CHECK_VERSION(2,6,0) static void read_general_style(GKeyFile *kfile) { @@ -419,11 +394,9 @@ } g_strfreev(keys); } -#endif void gnt_style_read_configure_file(const char *filename) { -#if GLIB_CHECK_VERSION(2,6,0) GError *error = NULL; gkfile = g_key_file_new(); @@ -436,7 +409,6 @@ } gnt_colors_parse(gkfile); read_general_style(gkfile); -#endif } void gnt_init_styles() @@ -458,9 +430,7 @@ str_styles[i] = NULL; } -#if GLIB_CHECK_VERSION(2,6,0) g_key_file_free(gkfile); gkfile = NULL; -#endif }
--- a/finch/libgnt/gntwm.c Thu Aug 23 01:27:48 2012 -0400 +++ b/finch/libgnt/gntwm.c Fri Jan 25 02:22:38 2013 -0500 @@ -37,14 +37,7 @@ #endif #include <glib.h> -#if GLIB_CHECK_VERSION(2,6,0) -# include <glib/gstdio.h> -#else -# include <sys/types.h> -# include <sys/stat.h> -# include <fcntl.h> -# define g_fopen open -#endif +#include <glib/gstdio.h> #include <ctype.h> #include <gmodule.h> #include <stdlib.h> @@ -337,7 +330,6 @@ static void read_window_positions(GntWM *wm) { -#if GLIB_CHECK_VERSION(2,6,0) GKeyFile *gfile = g_key_file_new(); char *filename = g_build_filename(g_get_home_dir(), ".gntpositions", NULL); GError *error = NULL; @@ -379,7 +371,6 @@ g_free(filename); g_key_file_free(gfile); -#endif } static gboolean check_idle(gpointer n) @@ -1753,31 +1744,6 @@ return FALSE; } -#if !GLIB_CHECK_VERSION(2,4,0) -struct -{ - gpointer data; - gpointer value; -} table_find_data; - -static void -table_find_helper(gpointer key, gpointer value, gpointer data) -{ - GHRFunc func = data; - if (func(key, value, table_find_data.data)) - table_find_data.value = value; -} - -static gpointer -g_hash_table_find(GHashTable * table, GHRFunc func, gpointer data) -{ - table_find_data.data = data; - table_find_data.value = NULL; - g_hash_table_foreach(table, table_find_helper, func); - return table_find_data.value; -} -#endif - static GntWS * new_widget_find_workspace(GntWM *wm, GntWidget *widget) {
--- a/finch/plugins/gnttinyurl.c Thu Aug 23 01:27:48 2012 -0400 +++ b/finch/plugins/gnttinyurl.c Fri Jan 25 02:22:38 2013 -0500 @@ -21,6 +21,7 @@ #include "internal.h" #include <glib.h> +#include "obsolete.h" #define PLUGIN_STATIC_NAME TinyURL #define PREFS_BASE "/plugins/gnt/tinyurl"
--- a/libpurple/Makefile.am Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/Makefile.am Fri Jan 25 02:22:38 2013 -0500 @@ -50,6 +50,7 @@ desktopitem.c \ eventloop.c \ ft.c \ + http.c \ idle.c \ imgstore.c \ keyring.c \ @@ -66,6 +67,7 @@ network.c \ ntlm.c \ notify.c \ + obsolete.c \ plugin.c \ pluginpref.c \ pounce.c \ @@ -118,6 +120,7 @@ desktopitem.h \ eventloop.h \ ft.h \ + http.h \ idle.h \ imgstore.h \ keyring.h \ @@ -285,6 +288,7 @@ noinst_HEADERS= \ internal.h \ + obsolete.h \ media/backend-fs2.h \ valgrind.h @@ -309,8 +313,10 @@ $(INTLLIBS) \ $(FARSTREAM_LIBS) \ $(GSTREAMER_LIBS) \ + $(GSTVIDEO_LIBS) \ $(GSTINTERFACES_LIBS) \ $(IDN_LIBS) \ + $(JSON_LIBS) \ ciphers/libpurple-ciphers.la \ -lm @@ -325,9 +331,11 @@ $(LIBXML_CFLAGS) \ $(FARSTREAM_CFLAGS) \ $(GSTREAMER_CFLAGS) \ + $(GSTVIDEO_CFLAGS) \ $(GSTINTERFACES_CFLAGS) \ $(IDN_CFLAGS) \ - $(NETWORKMANAGER_CFLAGS) + $(NETWORKMANAGER_CFLAGS) \ + $(JSON_CFLAGS) # INSTALL_SSL_CERTIFICATES is true when SSL_CERTIFICATES_DIR is empty. # We want to use SSL_CERTIFICATES_DIR when it's not empty.
--- a/libpurple/Makefile.mingw Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/Makefile.mingw Fri Jan 25 02:22:38 2013 -0500 @@ -10,6 +10,19 @@ TARGET = libpurple NEEDED_DLLS = $(LIBXML2_TOP)/bin/libxml2-2.dll +ifeq ($(CYRUS_SASL), 1) +NEEDED_DLLS += $(CYRUS_SASL_TOP)/bin/libsasl.dll + +CYRUS_SASL_PLUGINS = \ + $(CYRUS_SASL_TOP)/bin/sasl2/saslANONYMOUS.dll \ + $(CYRUS_SASL_TOP)/bin/sasl2/saslCRAMMD5.dll \ + $(CYRUS_SASL_TOP)/bin/sasl2/saslDIGESTMD5.dll \ + $(CYRUS_SASL_TOP)/bin/sasl2/saslGSSAPI.dll \ + $(CYRUS_SASL_TOP)/bin/sasl2/saslLOGIN.dll \ + $(CYRUS_SASL_TOP)/bin/sasl2/saslPLAIN.dll + +endif + ## ## INCLUDE PATHS ## @@ -39,10 +52,7 @@ ciphers/gchecksum.c \ ciphers/hmac.c \ ciphers/md4.c \ - ciphers/md5.c \ ciphers/rc4.c \ - ciphers/sha1.c \ - ciphers/sha256.c \ circbuffer.c \ cmds.c \ connection.c \ @@ -126,6 +136,10 @@ install_shallow: $(PURPLE_INSTALL_DIR) $(TARGET).dll cp $(TARGET).dll $(PURPLE_INSTALL_DIR) cp $(NEEDED_DLLS) $(PURPLE_INSTALL_DIR) +ifeq ($(CYRUS_SASL), 1) + mkdir -p $(PURPLE_INSTALL_DIR)/sasl2 + cp $(CYRUS_SASL_PLUGINS) $(PURPLE_INSTALL_DIR)/sasl2 +endif install: install_shallow all $(MAKE) -C $(PURPLE_PROTOS_TOP) -f $(MINGW_MAKEFILE) install
--- a/libpurple/accountopt.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/accountopt.c Fri Jan 25 02:22:38 2013 -0500 @@ -27,6 +27,7 @@ #include "accountopt.h" #include "util.h" +#include "glibcompat.h" /** * An option for an account.
--- a/libpurple/blist.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/blist.c Fri Jan 25 02:22:38 2013 -0500 @@ -597,9 +597,8 @@ } } -/* TODO: Make static and rename to load_blist */ -void -purple_blist_load() +static void +load_blist(void) { xmlnode *purple, *blist, *privacy; @@ -721,7 +720,8 @@ * Public API functions * *****************************************************************************/ -PurpleBuddyList *purple_blist_new() +void +purple_blist_boot(void) { PurpleBlistUiOps *ui_ops; GList *account; @@ -749,13 +749,9 @@ if (ui_ops != NULL && ui_ops->new_list != NULL) ui_ops->new_list(gbl); - return gbl; -} - -void -purple_set_blist(PurpleBuddyList *list) -{ - purplebuddylist = list; + purplebuddylist = gbl; + + load_blist(); } PurpleBuddyList *
--- a/libpurple/blist.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/blist.h Fri Jan 25 02:22:38 2013 -0500 @@ -252,22 +252,6 @@ /*@{*/ /** - * Creates a new buddy list - * - * @return The new buddy list. - * @deprecated In 3.0.0, this will be handled by purple_blist_init() - */ -PurpleBuddyList *purple_blist_new(void); - -/** - * Sets the main buddy list. - * - * @param blist The buddy list you want to use. - * @deprecated In 3.0.0, this will be handled by purple_blist_init() - */ -void purple_set_blist(PurpleBuddyList *blist); - -/** * Returns the main buddy list. * * @return The main buddy list. @@ -1008,11 +992,6 @@ /****************************************************************************************/ /** - * Loads the buddy list from ~/.purple/blist.xml. - */ -void purple_blist_load(void); - -/** * Schedule a save of the blist.xml file. This is used by the privacy * API whenever the privacy settings are changed. If you make a change * to blist.xml using one of the functions in the buddy list API, then @@ -1204,6 +1183,13 @@ void purple_blist_init(void); /** + * Loads the buddy list. + * + * You shouldn't call this. purple_core_init() will do it for you. + */ +void purple_blist_boot(void); + +/** * Uninitializes the buddy list subsystem. */ void purple_blist_uninit(void);
--- a/libpurple/buddyicon.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/buddyicon.c Fri Jan 25 02:22:38 2013 -0500 @@ -42,7 +42,7 @@ the icon data. */ char *username; /**< The username the icon belongs to. */ char *checksum; /**< The protocol checksum. */ - int ref_count; /**< The buddy icon reference count. */ + unsigned int ref_count; /**< The buddy icon reference count. */ }; /** @@ -75,7 +75,7 @@ static GHashTable *icon_data_cache = NULL; /** - * This hash table contains references counts for how many times each + * This hash table contains reference counts for how many times each * icon in the ~/.purple/icons/ directory is being used. It's pretty * crazy. It maintains the reference count across sessions, too, so * if you exit Pidgin then this hash table is reconstructed the next @@ -156,7 +156,7 @@ if (!purple_buddy_icons_is_caching()) return; - dirname = purple_buddy_icons_get_cache_dir(); + dirname = purple_buddy_icons_get_cache_dir(); path = g_build_filename(dirname, purple_imgstore_get_filename(img), NULL); if (!g_file_test(dirname, G_FILE_TEST_IS_DIR)) @@ -189,7 +189,7 @@ if (GPOINTER_TO_INT(g_hash_table_lookup(icon_file_cache, filename))) return; - dirname = purple_buddy_icons_get_cache_dir(); + dirname = purple_buddy_icons_get_cache_dir(); path = g_build_filename(dirname, filename, NULL); if (g_file_test(path, G_FILE_TEST_EXISTS)) @@ -243,36 +243,26 @@ } static PurpleStoredImage * -purple_buddy_icon_data_new(guchar *icon_data, size_t icon_len, const char *filename) +purple_buddy_icon_data_new(guchar *icon_data, size_t icon_len) { char *file; PurpleStoredImage *img; g_return_val_if_fail(icon_data != NULL, NULL); - g_return_val_if_fail(icon_len > 0, NULL); + g_return_val_if_fail(icon_len > 0, NULL); - if (filename == NULL) - { - file = purple_util_get_image_filename(icon_data, icon_len); - if (file == NULL) - { - g_free(icon_data); - return NULL; - } - } - else - file = g_strdup(filename); + file = purple_util_get_image_filename(icon_data, icon_len); - if ((img = g_hash_table_lookup(icon_data_cache, file))) - { + img = g_hash_table_lookup(icon_data_cache, file); + if (img) { g_free(file); g_free(icon_data); return purple_imgstore_ref(img); } - img = purple_imgstore_add(icon_data, icon_len, file); + img = purple_imgstore_new(icon_data, icon_len, file); - /* This will take ownership of file and g_free it either now or later. */ + /* This will take ownership of file and free it as needed */ g_hash_table_insert(icon_data_cache, file, img); purple_buddy_icon_data_cache(img); @@ -352,13 +342,13 @@ return icon; } -PurpleBuddyIcon * +void purple_buddy_icon_unref(PurpleBuddyIcon *icon) { if (icon == NULL) - return NULL; + return; - g_return_val_if_fail(icon->ref_count > 0, NULL); + g_return_if_fail(icon->ref_count > 0); icon->ref_count--; @@ -375,11 +365,7 @@ PURPLE_DBUS_UNREGISTER_POINTER(icon); g_slice_free(PurpleBuddyIcon, icon); - - return NULL; } - - return icon; } void @@ -469,7 +455,7 @@ if (data != NULL) { if (len > 0) - icon->img = purple_buddy_icon_data_new(data, len, NULL); + icon->img = purple_buddy_icon_data_new(data, len); else g_free(data); } @@ -638,6 +624,7 @@ if ((icon_cache == NULL) || ((icon = g_hash_table_lookup(icon_cache, username)) == NULL)) { + /* The icon is not currently cached in memory--try reading from disk */ PurpleBuddy *b = purple_find_buddy(account, username); const char *protocol_icon_file; const char *dirname; @@ -729,9 +716,8 @@ PurpleStoredImage *img = NULL; char *old_icon; - if (icon_data != NULL && icon_len > 0) - { - img = purple_buddy_icon_data_new(icon_data, icon_len, NULL); + if (icon_data != NULL && icon_len > 0) { + img = purple_buddy_icon_data_new(icon_data, icon_len); } old_icon = g_strdup(purple_account_get_string(account, "buddy_icon", NULL)); @@ -864,7 +850,7 @@ old_img = g_hash_table_lookup(pointer_icon_cache, node); if (icon_data != NULL && icon_len > 0) { - img = purple_buddy_icon_data_new(icon_data, icon_len, NULL); + img = purple_buddy_icon_data_new(icon_data, icon_len); } old_icon = g_strdup(purple_blist_node_get_string(node, @@ -1093,7 +1079,7 @@ pointer_icon_cache = g_hash_table_new(g_direct_hash, g_direct_equal); if (!cache_dir) - cache_dir = g_build_filename(purple_user_dir(), "icons", NULL); + cache_dir = g_build_filename(purple_user_dir(), "icons", NULL); purple_signal_connect(purple_imgstore_get_handle(), "image-deleting", purple_buddy_icons_get_handle(),
--- a/libpurple/buddyicon.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/buddyicon.h Fri Jan 25 02:22:38 2013 -0500 @@ -49,8 +49,8 @@ /** * Creates a new buddy icon structure and populates it. * - * If the buddy icon already exists, you'll get a reference to that structure, - * which will have been updated with the data supplied. + * If an icon for this account+username already exists, you'll get a reference + * to that structure, which will have been updated with the data supplied. * * @param account The account the user is on. * @param username The username the icon belongs to. @@ -79,10 +79,8 @@ * If the reference count reaches 0, the icon will be destroyed. * * @param icon The buddy icon. - * - * @return @a icon, or @c NULL if the reference count reached 0. */ -PurpleBuddyIcon *purple_buddy_icon_unref(PurpleBuddyIcon *icon); +void purple_buddy_icon_unref(PurpleBuddyIcon *icon); /** * Updates every instance of this icon. @@ -331,7 +329,7 @@ /** * Sets whether or not buddy icon caching is enabled. * - * @param caching TRUE of buddy icon caching should be enabled, or + * @param caching TRUE if buddy icon caching should be enabled, or * FALSE otherwise. */ void purple_buddy_icons_set_caching(gboolean caching);
--- a/libpurple/certificate.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/certificate.c Fri Jan 25 02:22:38 2013 -0500 @@ -41,53 +41,8 @@ /** List of registered Pools */ static GList *cert_pools = NULL; -/* - * TODO: Merge this with PurpleCertificateVerificationStatus for 3.0.0 */ -typedef enum { - PURPLE_CERTIFICATE_UNKNOWN_ERROR = -1, - - /* Not an error */ - PURPLE_CERTIFICATE_NO_PROBLEMS = 0, - - /* Non-fatal */ - PURPLE_CERTIFICATE_NON_FATALS_MASK = 0x0000FFFF, - - /* The certificate is self-signed. */ - PURPLE_CERTIFICATE_SELF_SIGNED = 0x01, - - /* The CA is not in libpurple's pool of certificates. */ - PURPLE_CERTIFICATE_CA_UNKNOWN = 0x02, - - /* The current time is before the certificate's specified - * activation time. - */ - PURPLE_CERTIFICATE_NOT_ACTIVATED = 0x04, - - /* The current time is after the certificate's specified expiration time */ - PURPLE_CERTIFICATE_EXPIRED = 0x08, - - /* The certificate's subject name doesn't match the expected */ - PURPLE_CERTIFICATE_NAME_MISMATCH = 0x10, - - /* No CA pool was found. This shouldn't happen... */ - PURPLE_CERTIFICATE_NO_CA_POOL = 0x20, - - /* Fatal */ - PURPLE_CERTIFICATE_FATALS_MASK = 0xFFFF0000, - - /* The signature chain could not be validated. Due to limitations in the - * the current API, this also indicates one of the CA certificates in the - * chain is expired (or not yet activated). FIXME 3.0.0 */ - PURPLE_CERTIFICATE_INVALID_CHAIN = 0x10000, - - /* The signature has been revoked. */ - PURPLE_CERTIFICATE_REVOKED = 0x20000, - - PURPLE_CERTIFICATE_LAST = 0x40000, -} PurpleCertificateInvalidityFlags; - static const gchar * -invalidity_reason_to_string(PurpleCertificateInvalidityFlags flag) +invalidity_reason_to_string(PurpleCertificateVerificationStatus flag) { switch (flag) { case PURPLE_CERTIFICATE_SELF_SIGNED: @@ -121,6 +76,9 @@ case PURPLE_CERTIFICATE_REVOKED: return _("The certificate has been revoked."); break; + case PURPLE_CERTIFICATE_REJECTED: + return _("The certificate was rejected by the user."); + break; case PURPLE_CERTIFICATE_UNKNOWN_ERROR: default: return _("An unknown certificate error occurred."); @@ -700,7 +658,7 @@ "VRQ on cert from %s rejected\n", vrq->subject_name); - purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_INVALID); + purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_REJECTED); } static void @@ -1319,7 +1277,7 @@ purple_debug_warning("certificate/x509/tls_cached", "User REJECTED cert\n"); - purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_INVALID); + purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_REJECTED); } /** Validates a certificate by asking the user @@ -1351,11 +1309,11 @@ static void x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq, - PurpleCertificateInvalidityFlags flags); + PurpleCertificateVerificationStatus flags); static void x509_tls_cached_complete(PurpleCertificateVerificationRequest *vrq, - PurpleCertificateInvalidityFlags flags) + PurpleCertificateVerificationStatus flags) { PurpleCertificatePool *tls_peers; PurpleCertificate *peer_crt = vrq->cert_chain->data; @@ -1377,13 +1335,16 @@ secondary = g_strconcat(tmp, " ", error, NULL); g_free(tmp); + purple_debug_error("certificate/x509/tls_cached", + "Unable to validate certificate: %s\n", secondary); + purple_notify_error(NULL, /* TODO: Probably wrong. */ _("SSL Certificate Error"), _("Unable to validate certificate"), secondary); g_free(secondary); - purple_certificate_verify_complete(vrq, PURPLE_CERTIFICATE_INVALID); + purple_certificate_verify_complete(vrq, flags); return; } else if (flags & PURPLE_CERTIFICATE_NON_FATALS_MASK) { /* Non-fatal error. Prompt the user. */ @@ -1448,7 +1409,7 @@ static void x509_tls_cached_cert_in_cache(PurpleCertificateVerificationRequest *vrq, - PurpleCertificateInvalidityFlags flags) + PurpleCertificateVerificationStatus flags) { /* TODO: Looking this up by name over and over is expensive. Fix, please! */ @@ -1502,7 +1463,7 @@ */ static void x509_tls_cached_check_subject_name(PurpleCertificateVerificationRequest *vrq, - PurpleCertificateInvalidityFlags flags) + PurpleCertificateVerificationStatus flags) { PurpleCertificate *peer_crt; GList *chain = vrq->cert_chain; @@ -1531,7 +1492,7 @@ */ static void x509_tls_cached_unknown_peer(PurpleCertificateVerificationRequest *vrq, - PurpleCertificateInvalidityFlags flags) + PurpleCertificateVerificationStatus flags) { PurpleCertificatePool *ca; PurpleCertificate *peer_crt; @@ -1611,7 +1572,7 @@ * CA, or is a trusted CA (based on fingerprint). */ /* If, for whatever reason, there is no Certificate Authority pool - loaded, we'll verify the subject name and then warn about thsi. */ + loaded, we'll verify the subject name and then warn about this. */ if ( !ca ) { purple_debug_error("certificate/x509/tls_cached", "No X.509 Certificate Authority pool " @@ -1637,8 +1598,6 @@ "Also checking for a CA with DN=%s\n", ca2_id); ca_crts = g_slist_concat(x509_ca_get_certs(ca_id), x509_ca_get_certs(ca2_id)); - g_free(ca_id); - g_free(ca2_id); if ( NULL == ca_crts ) { flags |= PURPLE_CERTIFICATE_CA_UNKNOWN; @@ -1647,6 +1606,8 @@ "found. I'll prompt the user, I guess.\n"); x509_tls_cached_check_subject_name(vrq, flags); + g_free(ca_id); + g_free(ca2_id); return; } @@ -1681,12 +1642,19 @@ g_byte_array_free(ca_fpr, TRUE); } - if (valid == FALSE) + if (valid == FALSE) { + purple_debug_error("certificate/x509/tls_cached", + "Unable to verify final certificate %s signed by %s. " + "Not a trusted root or signed by a trusted root.\n", + ca2_id, ca_id); flags |= PURPLE_CERTIFICATE_INVALID_CHAIN; + } g_slist_foreach(ca_crts, (GFunc)purple_certificate_destroy, NULL); g_slist_free(ca_crts); g_byte_array_free(last_fpr, TRUE); + g_free(ca_id); + g_free(ca2_id); x509_tls_cached_check_subject_name(vrq, flags); } @@ -1697,7 +1665,7 @@ const gchar *tls_peers_name = "tls_peers"; /* Name of local cache */ PurpleCertificatePool *tls_peers; time_t now, activation, expiration; - PurpleCertificateInvalidityFlags flags = PURPLE_CERTIFICATE_NO_PROBLEMS; + PurpleCertificateVerificationStatus flags = PURPLE_CERTIFICATE_VALID; gboolean ret; g_return_if_fail(vrq);
--- a/libpurple/certificate.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/certificate.h Fri Jan 25 02:22:38 2013 -0500 @@ -36,8 +36,49 @@ typedef enum { - PURPLE_CERTIFICATE_INVALID = 0, - PURPLE_CERTIFICATE_VALID = 1 + PURPLE_CERTIFICATE_UNKNOWN_ERROR = -1, + + /* Not an error */ + PURPLE_CERTIFICATE_VALID = 0, + + /* Non-fatal */ + PURPLE_CERTIFICATE_NON_FATALS_MASK = 0x0000FFFF, + + /* The certificate is self-signed. */ + PURPLE_CERTIFICATE_SELF_SIGNED = 0x01, + + /* The CA is not in libpurple's pool of certificates. */ + PURPLE_CERTIFICATE_CA_UNKNOWN = 0x02, + + /* The current time is before the certificate's specified + * activation time. + */ + PURPLE_CERTIFICATE_NOT_ACTIVATED = 0x04, + + /* The current time is after the certificate's specified expiration time */ + PURPLE_CERTIFICATE_EXPIRED = 0x08, + + /* The certificate's subject name doesn't match the expected */ + PURPLE_CERTIFICATE_NAME_MISMATCH = 0x10, + + /* No CA pool was found. This shouldn't happen... */ + PURPLE_CERTIFICATE_NO_CA_POOL = 0x20, + + /* Fatal */ + PURPLE_CERTIFICATE_FATALS_MASK = 0xFFFF0000, + + /* The signature chain could not be validated. Due to limitations in the + * the current API, this also indicates one of the CA certificates in the + * chain is expired (or not yet activated). FIXME 3.0.0 */ + PURPLE_CERTIFICATE_INVALID_CHAIN = 0x10000, + + /* The signature has been revoked. */ + PURPLE_CERTIFICATE_REVOKED = 0x20000, + + /* The certificate was rejected by the user. */ + PURPLE_CERTIFICATE_REJECTED = 0x40000, + + PURPLE_CERTIFICATE_LAST = 0x80000, } PurpleCertificateVerificationStatus; typedef struct _PurpleCertificate PurpleCertificate;
--- a/libpurple/ciphers/Makefile.am Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/ciphers/Makefile.am Fri Jan 25 02:22:38 2013 -0500 @@ -5,10 +5,7 @@ gchecksum.c \ hmac.c \ md4.c \ - md5.c \ - rc4.c \ - sha1.c \ - sha256.c + rc4.c INCLUDES = -I$(top_srcdir)/libpurple
--- a/libpurple/ciphers/gchecksum.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/ciphers/gchecksum.c Fri Jan 25 02:22:38 2013 -0500 @@ -1,7 +1,5 @@ #include <cipher.h> -#if GLIB_CHECK_VERSION(2,16,0) - static void purple_g_checksum_init(PurpleCipherContext *context, GChecksumType type) { @@ -19,13 +17,7 @@ checksum = purple_cipher_context_get_data(context); g_return_if_fail(checksum != NULL); -#if GLIB_CHECK_VERSION(2,18,0) g_checksum_reset(checksum); -#else - g_checksum_free(checksum); - checksum = g_checksum_new(type); - purple_cipher_context_set_data(context, checksum); -#endif } static void @@ -139,6 +131,3 @@ PURPLE_G_CHECKSUM_IMPLEMENTATION(md5, MD5, G_CHECKSUM_MD5, 64); PURPLE_G_CHECKSUM_IMPLEMENTATION(sha1, SHA1, G_CHECKSUM_SHA1, 64); PURPLE_G_CHECKSUM_IMPLEMENTATION(sha256, SHA256, G_CHECKSUM_SHA256, 64); - -#endif /* GLIB_CHECK_VERSION(2,16,0) */ -
--- a/libpurple/ciphers/md5.c Thu Aug 23 01:27:48 2012 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,326 +0,0 @@ -/* - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * Original md5 - * Copyright (C) 2001-2003 Christophe Devine <c.devine@cr0.net> - * - * 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 <cipher.h> - -#if !GLIB_CHECK_VERSION(2,16,0) - -#define MD5_HMAC_BLOCK_SIZE 64 - -struct MD5Context { - guint32 total[2]; - guint32 state[4]; - guchar buffer[64]; -}; - -#define MD5_GET_GUINT32(n,b,i) { \ - (n) = ((guint32)(b) [(i) ] ) \ - | ((guint32)(b) [(i) + 1] << 8) \ - | ((guint32)(b) [(i) + 2] << 16) \ - | ((guint32)(b) [(i) + 3] << 24); \ -} -#define MD5_PUT_GUINT32(n,b,i) { \ - (b)[(i) ] = (guchar)((n) ); \ - (b)[(i) + 1] = (guchar)((n) >> 8); \ - (b)[(i) + 2] = (guchar)((n) >> 16); \ - (b)[(i) + 3] = (guchar)((n) >> 24); \ -} - -static size_t -md5_get_block_size(PurpleCipherContext *context) -{ - /* This does not change (in this case) */ - return MD5_HMAC_BLOCK_SIZE; -} - -static void -md5_init(PurpleCipherContext *context, gpointer extra) { - struct MD5Context *md5_context; - - md5_context = g_new0(struct MD5Context, 1); - - purple_cipher_context_set_data(context, md5_context); - - purple_cipher_context_reset(context, extra); -} - -static void -md5_reset(PurpleCipherContext *context, gpointer extra) { - struct MD5Context *md5_context; - - md5_context = purple_cipher_context_get_data(context); - - md5_context->total[0] = 0; - md5_context->total[1] = 0; - - md5_context->state[0] = 0x67452301; - md5_context->state[1] = 0xEFCDAB89; - md5_context->state[2] = 0x98BADCFE; - md5_context->state[3] = 0x10325476; - - memset(md5_context->buffer, 0, sizeof(md5_context->buffer)); -} - -static void -md5_uninit(PurpleCipherContext *context) { - struct MD5Context *md5_context; - - purple_cipher_context_reset(context, NULL); - - md5_context = purple_cipher_context_get_data(context); - memset(md5_context, 0, sizeof(*md5_context)); - - g_free(md5_context); - md5_context = NULL; -} - -static void -md5_process(struct MD5Context *md5_context, const guchar data[64]) { - guint32 X[16], A, B, C, D; - - A = md5_context->state[0]; - B = md5_context->state[1]; - C = md5_context->state[2]; - D = md5_context->state[3]; - - MD5_GET_GUINT32(X[ 0], data, 0); - MD5_GET_GUINT32(X[ 1], data, 4); - MD5_GET_GUINT32(X[ 2], data, 8); - MD5_GET_GUINT32(X[ 3], data, 12); - MD5_GET_GUINT32(X[ 4], data, 16); - MD5_GET_GUINT32(X[ 5], data, 20); - MD5_GET_GUINT32(X[ 6], data, 24); - MD5_GET_GUINT32(X[ 7], data, 28); - MD5_GET_GUINT32(X[ 8], data, 32); - MD5_GET_GUINT32(X[ 9], data, 36); - MD5_GET_GUINT32(X[10], data, 40); - MD5_GET_GUINT32(X[11], data, 44); - MD5_GET_GUINT32(X[12], data, 48); - MD5_GET_GUINT32(X[13], data, 52); - MD5_GET_GUINT32(X[14], data, 56); - MD5_GET_GUINT32(X[15], data, 60); - -#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) -#define P(a,b,c,d,k,s,t) { \ - a += F(b,c,d) + X[k] + t; \ - a = S(a,s) + b; \ -} - - /* first pass */ -#define F(x,y,z) (z ^ (x & (y ^ z))) - P(A, B, C, D, 0, 7, 0xD76AA478); - P(D, A, B, C, 1, 12, 0xE8C7B756); - P(C, D, A, B, 2, 17, 0x242070DB); - P(B, C, D, A, 3, 22, 0xC1BDCEEE); - P(A, B, C, D, 4, 7, 0xF57C0FAF); - P(D, A, B, C, 5, 12, 0x4787C62A); - P(C, D, A, B, 6, 17, 0xA8304613); - P(B, C, D, A, 7, 22, 0xFD469501); - P(A, B, C, D, 8, 7, 0x698098D8); - P(D, A, B, C, 9, 12, 0x8B44F7AF); - P(C, D, A, B, 10, 17, 0xFFFF5BB1); - P(B, C, D, A, 11, 22, 0x895CD7BE); - P(A, B, C, D, 12, 7, 0x6B901122); - P(D, A, B, C, 13, 12, 0xFD987193); - P(C, D, A, B, 14, 17, 0xA679438E); - P(B, C, D, A, 15, 22, 0x49B40821); -#undef F - - /* second pass */ -#define F(x,y,z) (y ^ (z & (x ^ y))) - P(A, B, C, D, 1, 5, 0xF61E2562); - P(D, A, B, C, 6, 9, 0xC040B340); - P(C, D, A, B, 11, 14, 0x265E5A51); - P(B, C, D, A, 0, 20, 0xE9B6C7AA); - P(A, B, C, D, 5, 5, 0xD62F105D); - P(D, A, B, C, 10, 9, 0x02441453); - P(C, D, A, B, 15, 14, 0xD8A1E681); - P(B, C, D, A, 4, 20, 0xE7D3FBC8); - P(A, B, C, D, 9, 5, 0x21E1CDE6); - P(D, A, B, C, 14, 9, 0xC33707D6); - P(C, D, A, B, 3, 14, 0xF4D50D87); - P(B, C, D, A, 8, 20, 0x455A14ED); - P(A, B, C, D, 13, 5, 0xA9E3E905); - P(D, A, B, C, 2, 9, 0xFCEFA3F8); - P(C, D, A, B, 7, 14, 0x676F02D9); - P(B, C, D, A, 12, 20, 0x8D2A4C8A); -#undef F - - /* third pass */ -#define F(x,y,z) (x ^ y ^ z) - P(A, B, C, D, 5, 4, 0xFFFA3942); - P(D, A, B, C, 8, 11, 0x8771F681); - P(C, D, A, B, 11, 16, 0x6D9D6122); - P(B, C, D, A, 14, 23, 0xFDE5380C); - P(A, B, C, D, 1, 4, 0xA4BEEA44); - P(D, A, B, C, 4, 11, 0x4BDECFA9); - P(C, D, A, B, 7, 16, 0xF6BB4B60); - P(B, C, D, A, 10, 23, 0xBEBFBC70); - P(A, B, C, D, 13, 4, 0x289B7EC6); - P(D, A, B, C, 0, 11, 0xEAA127FA); - P(C, D, A, B, 3, 16, 0xD4EF3085); - P(B, C, D, A, 6, 23, 0x04881D05); - P(A, B, C, D, 9, 4, 0xD9D4D039); - P(D, A, B, C, 12, 11, 0xE6DB99E5); - P(C, D, A, B, 15, 16, 0x1FA27CF8); - P(B, C, D, A, 2, 23, 0xC4AC5665); -#undef F - - /* forth pass */ -#define F(x,y,z) (y ^ (x | ~z)) - P(A, B, C, D, 0, 6, 0xF4292244); - P(D, A, B, C, 7, 10, 0x432AFF97); - P(C, D, A, B, 14, 15, 0xAB9423A7); - P(B, C, D, A, 5, 21, 0xFC93A039); - P(A, B, C, D, 12, 6, 0x655B59C3); - P(D, A, B, C, 3, 10, 0x8F0CCC92); - P(C, D, A, B, 10, 15, 0xFFEFF47D); - P(B, C, D, A, 1, 21, 0x85845DD1); - P(A, B, C, D, 8, 6, 0x6FA87E4F); - P(D, A, B, C, 15, 10, 0xFE2CE6E0); - P(C, D, A, B, 6, 15, 0xA3014314); - P(B, C, D, A, 13, 21, 0x4E0811A1); - P(A, B, C, D, 4, 6, 0xF7537E82); - P(D, A, B, C, 11, 10, 0xBD3AF235); - P(C, D, A, B, 2, 15, 0x2AD7D2BB); - P(B, C, D, A, 9, 21, 0xEB86D391); -#undef F -#undef P -#undef S - - md5_context->state[0] += A; - md5_context->state[1] += B; - md5_context->state[2] += C; - md5_context->state[3] += D; - } - -static void -md5_append(PurpleCipherContext *context, const guchar *data, size_t len) { - struct MD5Context *md5_context = NULL; - guint32 left = 0, fill = 0; - - g_return_if_fail(context != NULL); - - md5_context = purple_cipher_context_get_data(context); - g_return_if_fail(md5_context != NULL); - - left = md5_context->total[0] & 0x3F; - fill = 64 - left; - - md5_context->total[0] += len; - md5_context->total[0] &= 0xFFFFFFFF; - - if(md5_context->total[0] < len) - md5_context->total[1]++; - - if(left && len >= fill) { - memcpy((md5_context->buffer + left), data, fill); - md5_process(md5_context, md5_context->buffer); - len -= fill; - data += fill; - left = 0; - } - - while(len >= 64) { - md5_process(md5_context, data); - len -= 64; - data += 64; - } - - if(len) { - memcpy((md5_context->buffer + left), data, len); - } -} - - static gboolean -md5_digest(PurpleCipherContext *context, size_t in_len, guchar digest[16], - size_t *out_len) -{ - struct MD5Context *md5_context = NULL; - guint32 last, pad; - guint32 high, low; - guchar message[8]; - guchar padding[64] = { - 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 - }; - - g_return_val_if_fail(in_len >= 16, FALSE); - - md5_context = purple_cipher_context_get_data(context); - - high = (md5_context->total[0] >> 29) - | (md5_context->total[1] << 3); - low = (md5_context->total[0] << 3); - - MD5_PUT_GUINT32(low, message, 0); - MD5_PUT_GUINT32(high, message, 4); - - last = md5_context->total[0] & 0x3F; - pad = (last < 56) ? (56 - last) : (120 - last); - - md5_append(context, padding, pad); - md5_append(context, message, 8); - - MD5_PUT_GUINT32(md5_context->state[0], digest, 0); - MD5_PUT_GUINT32(md5_context->state[1], digest, 4); - MD5_PUT_GUINT32(md5_context->state[2], digest, 8); - MD5_PUT_GUINT32(md5_context->state[3], digest, 12); - - if(out_len) - *out_len = 16; - - return TRUE; -} - -static PurpleCipherOps MD5Ops = { - NULL, /* Set Option */ - NULL, /* Get Option */ - md5_init, /* init */ - md5_reset, /* reset */ - md5_uninit, /* uninit */ - NULL, /* set iv */ - md5_append, /* append */ - md5_digest, /* digest */ - NULL, /* encrypt */ - NULL, /* decrypt */ - NULL, /* set salt */ - NULL, /* get salt size */ - NULL, /* set key */ - NULL, /* get key size */ - NULL, /* set batch mode */ - NULL, /* get batch mode */ - md5_get_block_size, /* get block size */ - NULL /* set key with len */ -}; - -PurpleCipherOps * -purple_md5_cipher_get_ops(void) { - return &MD5Ops; -} - -#endif /* !GLIB_CHECK_VERSION(2,16,0) */ -
--- a/libpurple/ciphers/sha1.c Thu Aug 23 01:27:48 2012 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,281 +0,0 @@ -/* - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include <cipher.h> -#include <util.h> - -#if !GLIB_CHECK_VERSION(2,16,0) - -#define SHA1_HMAC_BLOCK_SIZE 64 -#define SHA1_ROTL(X,n) ((((X) << (n)) | ((X) >> (32-(n)))) & 0xFFFFFFFF) - -struct SHA1Context { - guint32 H[5]; - guint32 W[80]; - - gint lenW; - - guint32 sizeHi; - guint32 sizeLo; -}; - -static size_t -sha1_get_block_size(PurpleCipherContext *context) -{ - /* This does not change (in this case) */ - return SHA1_HMAC_BLOCK_SIZE; -} - -static void -sha1_hash_block(struct SHA1Context *sha1_ctx) { - gint i; - guint32 A, B, C, D, E, T; - - for(i = 16; i < 80; i++) { - sha1_ctx->W[i] = SHA1_ROTL(sha1_ctx->W[i - 3] ^ - sha1_ctx->W[i - 8] ^ - sha1_ctx->W[i - 14] ^ - sha1_ctx->W[i - 16], 1); - } - - A = sha1_ctx->H[0]; - B = sha1_ctx->H[1]; - C = sha1_ctx->H[2]; - D = sha1_ctx->H[3]; - E = sha1_ctx->H[4]; - - for(i = 0; i < 20; i++) { - T = (SHA1_ROTL(A, 5) + (((C ^ D) & B) ^ D) + E + sha1_ctx->W[i] + 0x5A827999) & 0xFFFFFFFF; - E = D; - D = C; - C = SHA1_ROTL(B, 30); - B = A; - A = T; - } - - for(i = 20; i < 40; i++) { - T = (SHA1_ROTL(A, 5) + (B ^ C ^ D) + E + sha1_ctx->W[i] + 0x6ED9EBA1) & 0xFFFFFFFF; - E = D; - D = C; - C = SHA1_ROTL(B, 30); - B = A; - A = T; - } - - for(i = 40; i < 60; i++) { - T = (SHA1_ROTL(A, 5) + ((B & C) | (D & (B | C))) + E + sha1_ctx->W[i] + 0x8F1BBCDC) & 0xFFFFFFFF; - E = D; - D = C; - C = SHA1_ROTL(B, 30); - B = A; - A = T; - } - - for(i = 60; i < 80; i++) { - T = (SHA1_ROTL(A, 5) + (B ^ C ^ D) + E + sha1_ctx->W[i] + 0xCA62C1D6) & 0xFFFFFFFF; - E = D; - D = C; - C = SHA1_ROTL(B, 30); - B = A; - A = T; - } - - sha1_ctx->H[0] += A; - sha1_ctx->H[1] += B; - sha1_ctx->H[2] += C; - sha1_ctx->H[3] += D; - sha1_ctx->H[4] += E; -} - -static void -sha1_set_opt(PurpleCipherContext *context, const gchar *name, void *value) { - struct SHA1Context *ctx; - - ctx = purple_cipher_context_get_data(context); - - if(purple_strequal(name, "sizeHi")) { - ctx->sizeHi = GPOINTER_TO_INT(value); - } else if(purple_strequal(name, "sizeLo")) { - ctx->sizeLo = GPOINTER_TO_INT(value); - } else if(purple_strequal(name, "lenW")) { - ctx->lenW = GPOINTER_TO_INT(value); - } -} - -static void * -sha1_get_opt(PurpleCipherContext *context, const gchar *name) { - struct SHA1Context *ctx; - - ctx = purple_cipher_context_get_data(context); - - if(purple_strequal(name, "sizeHi")) { - return GINT_TO_POINTER(ctx->sizeHi); - } else if(purple_strequal(name, "sizeLo")) { - return GINT_TO_POINTER(ctx->sizeLo); - } else if(purple_strequal(name, "lenW")) { - return GINT_TO_POINTER(ctx->lenW); - } - - return NULL; -} - -static void -sha1_init(PurpleCipherContext *context, void *extra) { - struct SHA1Context *sha1_ctx; - - sha1_ctx = g_new0(struct SHA1Context, 1); - - purple_cipher_context_set_data(context, sha1_ctx); - - purple_cipher_context_reset(context, extra); -} - -static void -sha1_reset(PurpleCipherContext *context, void *extra) { - struct SHA1Context *sha1_ctx; - gint i; - - sha1_ctx = purple_cipher_context_get_data(context); - - g_return_if_fail(sha1_ctx); - - sha1_ctx->lenW = 0; - sha1_ctx->sizeHi = 0; - sha1_ctx->sizeLo = 0; - - sha1_ctx->H[0] = 0x67452301; - sha1_ctx->H[1] = 0xEFCDAB89; - sha1_ctx->H[2] = 0x98BADCFE; - sha1_ctx->H[3] = 0x10325476; - sha1_ctx->H[4] = 0xC3D2E1F0; - - for(i = 0; i < 80; i++) - sha1_ctx->W[i] = 0; -} - -static void -sha1_uninit(PurpleCipherContext *context) { - struct SHA1Context *sha1_ctx; - - purple_cipher_context_reset(context, NULL); - - sha1_ctx = purple_cipher_context_get_data(context); - - memset(sha1_ctx, 0, sizeof(struct SHA1Context)); - - g_free(sha1_ctx); - sha1_ctx = NULL; -} - -static void -sha1_append(PurpleCipherContext *context, const guchar *data, size_t len) { - struct SHA1Context *sha1_ctx; - gint i; - - sha1_ctx = purple_cipher_context_get_data(context); - - g_return_if_fail(sha1_ctx); - - for(i = 0; i < len; i++) { - sha1_ctx->W[sha1_ctx->lenW / 4] <<= 8; - sha1_ctx->W[sha1_ctx->lenW / 4] |= data[i]; - - if((++sha1_ctx->lenW) % 64 == 0) { - sha1_hash_block(sha1_ctx); - sha1_ctx->lenW = 0; - } - - sha1_ctx->sizeLo += 8; - sha1_ctx->sizeHi += (sha1_ctx->sizeLo < 8); - } -} - -static gboolean -sha1_digest(PurpleCipherContext *context, size_t in_len, guchar digest[20], - size_t *out_len) -{ - struct SHA1Context *sha1_ctx; - guchar pad0x80 = 0x80, pad0x00 = 0x00; - guchar padlen[8]; - gint i; - - g_return_val_if_fail(in_len >= 20, FALSE); - - sha1_ctx = purple_cipher_context_get_data(context); - - g_return_val_if_fail(sha1_ctx, FALSE); - - padlen[0] = (guchar)((sha1_ctx->sizeHi >> 24) & 255); - padlen[1] = (guchar)((sha1_ctx->sizeHi >> 16) & 255); - padlen[2] = (guchar)((sha1_ctx->sizeHi >> 8) & 255); - padlen[3] = (guchar)((sha1_ctx->sizeHi >> 0) & 255); - padlen[4] = (guchar)((sha1_ctx->sizeLo >> 24) & 255); - padlen[5] = (guchar)((sha1_ctx->sizeLo >> 16) & 255); - padlen[6] = (guchar)((sha1_ctx->sizeLo >> 8) & 255); - padlen[7] = (guchar)((sha1_ctx->sizeLo >> 0) & 255); - - /* pad with a 1, then zeroes, then length */ - purple_cipher_context_append(context, &pad0x80, 1); - while(sha1_ctx->lenW != 56) - purple_cipher_context_append(context, &pad0x00, 1); - purple_cipher_context_append(context, padlen, 8); - - for(i = 0; i < 20; i++) { - digest[i] = (guchar)(sha1_ctx->H[i / 4] >> 24); - sha1_ctx->H[i / 4] <<= 8; - } - - purple_cipher_context_reset(context, NULL); - - if(out_len) - *out_len = 20; - - return TRUE; -} - -static PurpleCipherOps SHA1Ops = { - sha1_set_opt, /* Set Option */ - sha1_get_opt, /* Get Option */ - sha1_init, /* init */ - sha1_reset, /* reset */ - sha1_uninit, /* uninit */ - NULL, /* set iv */ - sha1_append, /* append */ - sha1_digest, /* digest */ - NULL, /* encrypt */ - NULL, /* decrypt */ - NULL, /* set salt */ - NULL, /* get salt size */ - NULL, /* set key */ - NULL, /* get key size */ - NULL, /* set batch mode */ - NULL, /* get batch mode */ - sha1_get_block_size, /* get block size */ - NULL /* set key with len */ -}; - -PurpleCipherOps * -purple_sha1_cipher_get_ops(void) { - return &SHA1Ops; -} - -#endif /* !GLIB_CHECK_VERSION(2,16,0) */ -
--- a/libpurple/ciphers/sha256.c Thu Aug 23 01:27:48 2012 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,283 +0,0 @@ -/* - * purple - * - * Purple is the legal property of its developers, whose names are too numerous - * to list here. Please refer to the COPYRIGHT file distributed with this - * source distribution. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA - */ -#include <cipher.h> - -#if !GLIB_CHECK_VERSION(2,16,0) - -#define SHA256_HMAC_BLOCK_SIZE 64 -#define SHA256_ROTR(X,n) ((((X) >> (n)) | ((X) << (32-(n)))) & 0xFFFFFFFF) - -static const guint32 sha256_K[64] = -{ - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 -}; - -struct SHA256Context { - guint32 H[8]; - guint32 W[64]; - - gint lenW; - - guint32 sizeHi; - guint32 sizeLo; -}; - -static size_t -sha256_get_block_size(PurpleCipherContext *context) -{ - /* This does not change (in this case) */ - return SHA256_HMAC_BLOCK_SIZE; -} - -static void -sha256_hash_block(struct SHA256Context *sha256_ctx) { - gint i; - guint32 A, B, C, D, E, F, G, H, T1, T2; - - for(i = 16; i < 64; i++) { - sha256_ctx->W[i] = - (SHA256_ROTR(sha256_ctx->W[i-2], 17) ^ SHA256_ROTR(sha256_ctx->W[i-2], 19) ^ (sha256_ctx->W[i-2] >> 10)) - + sha256_ctx->W[i-7] - + (SHA256_ROTR(sha256_ctx->W[i-15], 7) ^ SHA256_ROTR(sha256_ctx->W[i-15], 18) ^ (sha256_ctx->W[i-15] >> 3)) - + sha256_ctx->W[i-16]; - } - - A = sha256_ctx->H[0]; - B = sha256_ctx->H[1]; - C = sha256_ctx->H[2]; - D = sha256_ctx->H[3]; - E = sha256_ctx->H[4]; - F = sha256_ctx->H[5]; - G = sha256_ctx->H[6]; - H = sha256_ctx->H[7]; - - for(i = 0; i < 64; i++) { - T1 = H - + (SHA256_ROTR(E, 6) ^ SHA256_ROTR(E, 11) ^ SHA256_ROTR(E, 25)) - + ((E & F) ^ ((~E) & G)) - + sha256_K[i] + sha256_ctx->W[i]; - T2 = (SHA256_ROTR(A, 2) ^ SHA256_ROTR(A, 13) ^ SHA256_ROTR(A, 22)) - + ((A & B) ^ (A & C) ^ (B & C)); - H = G; - G = F; - F = E; - E = D + T1; - D = C; - C = B; - B = A; - A = T1 + T2; - } - - sha256_ctx->H[0] += A; - sha256_ctx->H[1] += B; - sha256_ctx->H[2] += C; - sha256_ctx->H[3] += D; - sha256_ctx->H[4] += E; - sha256_ctx->H[5] += F; - sha256_ctx->H[6] += G; - sha256_ctx->H[7] += H; -} - -static void -sha256_set_opt(PurpleCipherContext *context, const gchar *name, void *value) { - struct SHA256Context *ctx; - - ctx = purple_cipher_context_get_data(context); - - if(!strcmp(name, "sizeHi")) { - ctx->sizeHi = GPOINTER_TO_INT(value); - } else if(!strcmp(name, "sizeLo")) { - ctx->sizeLo = GPOINTER_TO_INT(value); - } else if(!strcmp(name, "lenW")) { - ctx->lenW = GPOINTER_TO_INT(value); - } -} - -static void * -sha256_get_opt(PurpleCipherContext *context, const gchar *name) { - struct SHA256Context *ctx; - - ctx = purple_cipher_context_get_data(context); - - if(!strcmp(name, "sizeHi")) { - return GINT_TO_POINTER(ctx->sizeHi); - } else if(!strcmp(name, "sizeLo")) { - return GINT_TO_POINTER(ctx->sizeLo); - } else if(!strcmp(name, "lenW")) { - return GINT_TO_POINTER(ctx->lenW); - } - - return NULL; -} - -static void -sha256_init(PurpleCipherContext *context, void *extra) { - struct SHA256Context *sha256_ctx; - - sha256_ctx = g_new0(struct SHA256Context, 1); - - purple_cipher_context_set_data(context, sha256_ctx); - - purple_cipher_context_reset(context, extra); -} - -static void -sha256_reset(PurpleCipherContext *context, void *extra) { - struct SHA256Context *sha256_ctx; - gint i; - - sha256_ctx = purple_cipher_context_get_data(context); - - g_return_if_fail(sha256_ctx); - - sha256_ctx->lenW = 0; - sha256_ctx->sizeHi = 0; - sha256_ctx->sizeLo = 0; - - sha256_ctx->H[0] = 0x6a09e667; - sha256_ctx->H[1] = 0xbb67ae85; - sha256_ctx->H[2] = 0x3c6ef372; - sha256_ctx->H[3] = 0xa54ff53a; - sha256_ctx->H[4] = 0x510e527f; - sha256_ctx->H[5] = 0x9b05688c; - sha256_ctx->H[6] = 0x1f83d9ab; - sha256_ctx->H[7] = 0x5be0cd19; - - for(i = 0; i < 64; i++) - sha256_ctx->W[i] = 0; -} - -static void -sha256_uninit(PurpleCipherContext *context) { - struct SHA256Context *sha256_ctx; - - purple_cipher_context_reset(context, NULL); - - sha256_ctx = purple_cipher_context_get_data(context); - - memset(sha256_ctx, 0, sizeof(struct SHA256Context)); - - g_free(sha256_ctx); - sha256_ctx = NULL; -} - -static void -sha256_append(PurpleCipherContext *context, const guchar *data, size_t len) { - struct SHA256Context *sha256_ctx; - gint i; - - sha256_ctx = purple_cipher_context_get_data(context); - - g_return_if_fail(sha256_ctx); - - for(i = 0; i < len; i++) { - sha256_ctx->W[sha256_ctx->lenW / 4] <<= 8; - sha256_ctx->W[sha256_ctx->lenW / 4] |= data[i]; - - if((++sha256_ctx->lenW) % 64 == 0) { - sha256_hash_block(sha256_ctx); - sha256_ctx->lenW = 0; - } - - sha256_ctx->sizeLo += 8; - sha256_ctx->sizeHi += (sha256_ctx->sizeLo < 8); - } -} - -static gboolean -sha256_digest(PurpleCipherContext *context, size_t in_len, guchar digest[32], - size_t *out_len) -{ - struct SHA256Context *sha256_ctx; - guchar pad0x80 = 0x80, pad0x00 = 0x00; - guchar padlen[8]; - gint i; - - g_return_val_if_fail(in_len >= 32, FALSE); - - sha256_ctx = purple_cipher_context_get_data(context); - - g_return_val_if_fail(sha256_ctx, FALSE); - - padlen[0] = (guchar)((sha256_ctx->sizeHi >> 24) & 255); - padlen[1] = (guchar)((sha256_ctx->sizeHi >> 16) & 255); - padlen[2] = (guchar)((sha256_ctx->sizeHi >> 8) & 255); - padlen[3] = (guchar)((sha256_ctx->sizeHi >> 0) & 255); - padlen[4] = (guchar)((sha256_ctx->sizeLo >> 24) & 255); - padlen[5] = (guchar)((sha256_ctx->sizeLo >> 16) & 255); - padlen[6] = (guchar)((sha256_ctx->sizeLo >> 8) & 255); - padlen[7] = (guchar)((sha256_ctx->sizeLo >> 0) & 255); - - /* pad with a 1, then zeroes, then length */ - purple_cipher_context_append(context, &pad0x80, 1); - while(sha256_ctx->lenW != 56) - purple_cipher_context_append(context, &pad0x00, 1); - purple_cipher_context_append(context, padlen, 8); - - for(i = 0; i < 32; i++) { - digest[i] = (guchar)(sha256_ctx->H[i / 4] >> 24); - sha256_ctx->H[i / 4] <<= 8; - } - - purple_cipher_context_reset(context, NULL); - - if(out_len) - *out_len = 32; - - return TRUE; -} - -static PurpleCipherOps SHA256Ops = { - sha256_set_opt, /* Set Option */ - sha256_get_opt, /* Get Option */ - sha256_init, /* init */ - sha256_reset, /* reset */ - sha256_uninit, /* uninit */ - NULL, /* set iv */ - sha256_append, /* append */ - sha256_digest, /* digest */ - NULL, /* encrypt */ - NULL, /* decrypt */ - NULL, /* set salt */ - NULL, /* get salt size */ - NULL, /* set key */ - NULL, /* get key size */ - NULL, /* set batch mode */ - NULL, /* get batch mode */ - sha256_get_block_size, /* get block size */ - NULL /* set key with len */ -}; - -PurpleCipherOps * -purple_sha256_cipher_get_ops(void) { - return &SHA256Ops; -} - -#endif /* !GLIB_CHECK_VERSION(2,16,0) */ -
--- a/libpurple/connection.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/connection.c Fri Jan 25 02:22:38 2013 -0500 @@ -31,6 +31,7 @@ #include "connection.h" #include "dbus-maybe.h" #include "debug.h" +#include "http.h" #include "log.h" #include "notify.h" #include "prefs.h" @@ -251,6 +252,7 @@ update_keepalive(gc, FALSE); + purple_http_conn_cancel_all(gc); purple_proxy_connect_cancel_with_handle(gc); prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl);
--- a/libpurple/core.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/core.c Fri Jan 25 02:22:38 2013 -0500 @@ -33,6 +33,7 @@ #include "debug.h" #include "dnsquery.h" #include "ft.h" +#include "http.h" #include "idle.h" #include "imgstore.h" #include "keyring.h" @@ -176,6 +177,7 @@ purple_stun_init(); purple_xfers_init(); purple_idle_init(); + purple_http_init(); purple_smileys_init(); /* * Call this early on to try to auto-detect our IP address and @@ -189,6 +191,9 @@ /* The UI may have registered some theme types, so refresh them */ purple_theme_manager_refresh(); + /* Load the buddy list after UI init */ + purple_blist_boot(); + return TRUE; } @@ -224,6 +229,7 @@ /* Save .xml files, remove signals, etc. */ purple_smileys_uninit(); + purple_http_uninit(); purple_idle_uninit(); purple_pounces_uninit(); purple_blist_uninit(); @@ -260,9 +266,11 @@ #endif purple_cmds_uninit(); - /* Everything after util_uninit cannot try to write things to the confdir */ + purple_log_uninit(); + /* Everything after util_uninit cannot try to write things to the + * confdir nor use purple_escape_js + */ purple_util_uninit(); - purple_log_uninit(); purple_signals_uninit();
--- a/libpurple/dnsquery.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/dnsquery.c Fri Jan 25 02:22:38 2013 -0500 @@ -80,8 +80,7 @@ }; static GSList *free_dns_children = NULL; -/* TODO: Make me a GQueue when we require >= glib 2.4 */ -static GSList *queued_requests = NULL; +static GQueue *queued_requests = NULL; static int number_of_dns_children = 0; @@ -581,12 +580,11 @@ PurpleDnsQueryData *query_data; PurpleDnsQueryResolverProcess *resolver; - if (queued_requests == NULL) + if (g_queue_is_empty(queued_requests)) /* No more DNS queries, yay! */ return; - query_data = queued_requests->data; - queued_requests = g_slist_delete_link(queued_requests, queued_requests); + query_data = g_queue_pop_head(queued_requests); /* * If we have any children, attempt to have them perform the DNS @@ -610,7 +608,7 @@ if (number_of_dns_children >= MAX_DNS_CHILDREN) { /* Apparently all our children are busy */ - queued_requests = g_slist_prepend(queued_requests, query_data); + g_queue_push_head(queued_requests, query_data); return; } @@ -696,7 +694,7 @@ static void resolve_host(PurpleDnsQueryData *query_data) { - queued_requests = g_slist_append(queued_requests, query_data); + g_queue_push_tail(queued_requests, query_data); handle_next_queued_request(); } @@ -953,7 +951,7 @@ ops->destroy(query_data); #if defined(PURPLE_DNSQUERY_USE_FORK) - queued_requests = g_slist_remove(queued_requests, query_data); + g_queue_remove(queued_requests, query_data); if (query_data->resolver != NULL) /* @@ -1030,6 +1028,9 @@ void purple_dnsquery_init(void) { +#if defined(PURPLE_DNSQUERY_USE_FORK) + queued_requests = g_queue_new(); +#endif } void @@ -1041,5 +1042,9 @@ purple_dnsquery_resolver_destroy(free_dns_children->data); free_dns_children = g_slist_remove(free_dns_children, free_dns_children->data); } + + g_queue_free(queued_requests); + queued_requests = NULL; #endif /* end PURPLE_DNSQUERY_USE_FORK */ } +
--- a/libpurple/example/nullclient.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/example/nullclient.c Fri Jan 25 02:22:38 2013 -0500 @@ -103,11 +103,7 @@ glib_input_add, g_source_remove, NULL, -#if GLIB_CHECK_VERSION(2,14,0) g_timeout_add_seconds, -#else - NULL, -#endif /* padding */ NULL, @@ -218,19 +214,12 @@ abort(); } - /* Create and load the buddylist. */ - purple_set_blist(purple_blist_new()); - purple_blist_load(); - /* Load the preferences. */ purple_prefs_load(); /* Load the desired plugins. The client should save the list of loaded plugins in * the preferences using purple_plugins_save_loaded(PLUGIN_SAVE_PREF) */ purple_plugins_load_saved(PLUGIN_SAVE_PREF); - - /* Load the pounces. */ - purple_pounces_load(); } static void
--- a/libpurple/ft.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/ft.c Fri Jan 25 02:22:38 2013 -0500 @@ -303,7 +303,7 @@ if (print_thumbnail && thumbnail_data) { gchar *message_with_img; gpointer data = g_memdup(thumbnail_data, size); - int id = purple_imgstore_add_with_id(data, size, NULL); + int id = purple_imgstore_new_with_id(data, size, NULL); message_with_img = g_strdup_printf("<img src='" PURPLE_STORED_IMAGE_PROTOCOL "%d'> %s", @@ -319,7 +319,7 @@ } void -purple_xfer_conversation_write(PurpleXfer *xfer, gchar *message, +purple_xfer_conversation_write(PurpleXfer *xfer, const gchar *message, gboolean is_error) { purple_xfer_conversation_write_internal(xfer, message, is_error, FALSE);
--- a/libpurple/ft.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/ft.h Fri Jan 25 02:22:38 2013 -0500 @@ -705,7 +705,7 @@ * @param message The message to display. * @param is_error Is this an error message?. */ -void purple_xfer_conversation_write(PurpleXfer *xfer, char *message, gboolean is_error); +void purple_xfer_conversation_write(PurpleXfer *xfer, const gchar *message, gboolean is_error); /** * Allows the UI to signal it's ready to send/receive data (depending on
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/glibcompat.h Fri Jan 25 02:22:38 2013 -0500 @@ -0,0 +1,53 @@ +/* pidgin + * + * Pidgin 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 _PIDGINGLIBCOMPAT_H_ +#define _PIDGINGLIBCOMPAT_H_ + +/* This file is internal to Pidgin. Do not use! + * Also, any public API should not depend on this file. + */ + +#if !GLIB_CHECK_VERSION(2, 28, 0) + +static inline gint64 g_get_monotonic_time(void) +{ + GTimeVal time_s; + + g_get_current_time(&time_s); + + return ((gint64)time_s.tv_sec << 32) | time_s.tv_usec; +} + +static inline void g_list_free_full(GList *list, GDestroyNotify free_func) +{ + g_list_foreach(list, (GFunc)free_func, NULL); + g_list_free(list); +} + +static inline void g_slist_free_full(GSList *list, GDestroyNotify free_func) +{ + g_slist_foreach(list, (GFunc)free_func, NULL); + g_slist_free(list); +} + +#endif /* 2.28.0 */ + +#endif /* _PIDGINGLIBCOMPAT_H_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/http.c Fri Jan 25 02:22:38 2013 -0500 @@ -0,0 +1,2357 @@ +/** + * @file http.c HTTP API + * @ingroup core + */ + +/* 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 "http.h" + +#include "internal.h" + +#include "debug.h" +#include "ntlm.h" + +#define PURPLE_HTTP_URL_CREDENTIALS_CHARS "a-z0-9.,~_/*!&%?=+\\^-" +#define PURPLE_HTTP_MAX_RECV_BUFFER_LEN 10240 +#define PURPLE_HTTP_MAX_READ_BUFFER_LEN 10240 + +#define PURPLE_HTTP_REQUEST_DEFAULT_MAX_REDIRECTS 20 +#define PURPLE_HTTP_REQUEST_DEFAULT_TIMEOUT 30 + +typedef struct _PurpleHttpURL PurpleHttpURL; + +typedef struct _PurpleHttpSocket PurpleHttpSocket; + +typedef struct _PurpleHttpHeaders PurpleHttpHeaders; + +struct _PurpleHttpSocket +{ + gboolean is_ssl; + PurpleSslConnection *ssl_connection; + PurpleProxyConnectData *raw_connection; + int fd; + guint inpa; +}; + +struct _PurpleHttpRequest +{ + int ref_count; + + gchar *url; + gchar *method; + PurpleHttpHeaders *headers; + PurpleHttpCookieJar *cookie_jar; + + gchar *contents; + int contents_length; + PurpleHttpContentReader contents_reader; + gpointer contents_reader_data; + PurpleHttpContentWriter response_writer; + gpointer response_writer_data; + + int timeout; + int max_redirects; + gboolean http11; + int max_length; +}; + +struct _PurpleHttpConnection +{ + PurpleConnection *gc; + PurpleHttpCallback callback; + gpointer user_data; + gboolean is_reading; + + PurpleHttpURL *url; + PurpleHttpRequest *request; + PurpleHttpResponse *response; + + PurpleHttpSocket socket; + GString *request_header; + int request_header_written, request_contents_written; + gboolean main_header_got, headers_got; + GString *response_buffer; + + GString *contents_reader_buffer; + gboolean contents_reader_requested; + + int redirects_count; + int data_length_got; + + int length_expected, length_got; + + gboolean is_chunked, in_chunk, chunks_done; + int chunk_length, chunk_got; + + GList *link_global, *link_gc; + + guint timeout_handle; + + PurpleHttpProgressWatcher watcher; + gpointer watcher_user_data; + guint watcher_interval_threshold; + gint64 watcher_last_call; + guint watcher_delayed_handle; +}; + +struct _PurpleHttpResponse +{ + int code; + gchar *error; + + GString *contents; + PurpleHttpHeaders *headers; +}; + +struct _PurpleHttpURL +{ + gchar *protocol; + gchar *user; + gchar *password; + gchar *host; + int port; + gchar *path; + gchar *fragment; +}; + +struct _PurpleHttpHeaders +{ + GList *list; + GHashTable *by_name; +}; + +typedef struct +{ + time_t expires; + gchar *value; +} PurpleHttpCookie; + +struct _PurpleHttpCookieJar +{ + int ref_count; + + GHashTable *tab; +}; + +static time_t purple_http_rfc1123_to_time(const gchar *str); + +static PurpleHttpConnection * purple_http_connection_new( + PurpleHttpRequest *request, PurpleConnection *gc); +static void purple_http_connection_terminate(PurpleHttpConnection *hc); +static void purple_http_conn_notify_progress_watcher(PurpleHttpConnection *hc); + +static PurpleHttpResponse * purple_http_response_new(void); +static void purple_http_response_free(PurpleHttpResponse *response); + +static void purple_http_cookie_jar_parse(PurpleHttpCookieJar *cookie_jar, + GList *values); +static gchar * purple_http_cookie_jar_gen(PurpleHttpCookieJar *cookie_jar); +gchar * purple_http_cookie_jar_dump(PurpleHttpCookieJar *cjar); + +static PurpleHttpURL * purple_http_url_parse(const char *url); +static void purple_http_url_free(PurpleHttpURL *parsed_url); +static void purple_http_url_relative(PurpleHttpURL *base_url, + PurpleHttpURL *relative_url); +static gchar * purple_http_url_print(PurpleHttpURL *parsed_url); + +static GRegex *purple_http_re_url, *purple_http_re_url_host, + *purple_http_re_rfc1123; + +/** + * Values: pointers to running PurpleHttpConnection. + */ +static GList *purple_http_hc_list; + +/** + * Keys: pointers to PurpleConnection. + * Values: GList of pointers to running PurpleHttpConnection. + */ +static GHashTable *purple_http_hc_by_gc; + +/** + * Keys: pointers to PurpleHttpConnection. + * Values: pointers to links in purple_http_hc_list. + */ +static GHashTable *purple_http_hc_by_ptr; + +/*** Helper functions *********************************************************/ + +static time_t purple_http_rfc1123_to_time(const gchar *str) +{ + static const gchar *months[13] = {"jan", "feb", "mar", "apr", "may", "jun", + "jul", "aug", "sep", "oct", "nov", "dec", NULL}; + GMatchInfo *match_info; + gchar *d_date, *d_month, *d_year, *d_time; + int month; + gchar *iso_date; + time_t t; + + g_return_val_if_fail(str != NULL, 0); + + g_regex_match(purple_http_re_rfc1123, str, 0, &match_info); + if (!g_match_info_matches(match_info)) { + g_match_info_free(match_info); + return 0; + } + g_match_info_free(match_info); + + d_date = g_match_info_fetch(match_info, 1); + d_month = g_match_info_fetch(match_info, 2); + d_year = g_match_info_fetch(match_info, 3); + d_time = g_match_info_fetch(match_info, 4); + + month = 0; + while (months[month] != NULL) + { + if (0 == g_ascii_strcasecmp(d_month, months[month])) + break; + month++; + } + month++; + + iso_date = g_strdup_printf("%s-%02d-%sT%s+00:00", + d_year, month, d_date, d_time); + + g_free(d_date); + g_free(d_month); + g_free(d_year); + g_free(d_time); + + if (month > 12) { + purple_debug_warning("http", "Invalid month: %s\n", d_month); + g_free(iso_date); + return 0; + } + + t = purple_str_to_time(iso_date, TRUE, NULL, NULL, NULL); + + g_free(iso_date); + + return t; +} + +/*** Headers collection *******************************************************/ + +static PurpleHttpHeaders * purple_http_headers_new(void); +static void purple_http_headers_free(PurpleHttpHeaders *hdrs); +static void purple_http_headers_add(PurpleHttpHeaders *hdrs, const gchar *key, + const gchar *value); +static const GList * purple_http_headers_get_all(PurpleHttpHeaders *hdrs); +static GList * purple_http_headers_get_all_by_name( + PurpleHttpHeaders *hdrs, const gchar *key); +static const gchar * purple_http_headers_get(PurpleHttpHeaders *hdrs, + const gchar *key); +static gboolean purple_http_headers_get_int(PurpleHttpHeaders *hdrs, + const gchar *key, int *dst); +static gboolean purple_http_headers_match(PurpleHttpHeaders *hdrs, + const gchar *key, const gchar *value); +static gchar * purple_http_headers_dump(PurpleHttpHeaders *hdrs); + +static PurpleHttpHeaders * purple_http_headers_new(void) +{ + PurpleHttpHeaders *hdrs = g_new0(PurpleHttpHeaders, 1); + + hdrs->by_name = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)g_list_free); + + return hdrs; +} + +static void purple_http_headers_free_kvp(PurpleKeyValuePair *kvp) +{ + g_free(kvp->key); + g_free(kvp->value); + g_free(kvp); +} + +static void purple_http_headers_free(PurpleHttpHeaders *hdrs) +{ + if (hdrs == NULL) + return; + + g_hash_table_destroy(hdrs->by_name); + g_list_free_full(hdrs->list, + (GDestroyNotify)purple_http_headers_free_kvp); + g_free(hdrs); +} + +static void purple_http_headers_add(PurpleHttpHeaders *hdrs, const gchar *key, + const gchar *value) +{ + PurpleKeyValuePair *kvp; + GList *named_values, *new_values; + gchar *key_low; + + g_return_if_fail(hdrs != NULL); + g_return_if_fail(key != NULL); + g_return_if_fail(value != NULL); + + kvp = g_new0(PurpleKeyValuePair, 1); + kvp->key = g_strdup(key); + kvp->value = g_strdup(value); + hdrs->list = g_list_append(hdrs->list, kvp); + + key_low = g_ascii_strdown(key, -1); + named_values = g_hash_table_lookup(hdrs->by_name, key_low); + new_values = g_list_append(named_values, kvp->value); + if (named_values) + g_free(key_low); + else + g_hash_table_insert(hdrs->by_name, key_low, new_values); +} + +static void purple_http_headers_remove(PurpleHttpHeaders *hdrs, + const gchar *key) +{ + GList *it, *curr; + + g_return_if_fail(hdrs != NULL); + g_return_if_fail(key != NULL); + + if (!g_hash_table_remove(hdrs->by_name, key)) + return; + + /* Could be optimized to O(1). */ + it = g_list_first(hdrs->list); + while (it) + { + PurpleKeyValuePair *kvp = it->data; + curr = it; + it = g_list_next(it); + if (g_ascii_strcasecmp(kvp->key, key) != 0) + continue; + + hdrs->list = g_list_delete_link(hdrs->list, curr); + purple_http_headers_free_kvp(kvp); + } +} + +static const GList * purple_http_headers_get_all(PurpleHttpHeaders *hdrs) +{ + return hdrs->list; +} + +/* return const */ +static GList * purple_http_headers_get_all_by_name( + PurpleHttpHeaders *hdrs, const gchar *key) +{ + GList *values; + gchar *key_low; + + g_return_val_if_fail(hdrs != NULL, NULL); + g_return_val_if_fail(key != NULL, NULL); + + key_low = g_ascii_strdown(key, -1); + values = g_hash_table_lookup(hdrs->by_name, key_low); + g_free(key_low); + + return values; +} + +static const gchar * purple_http_headers_get(PurpleHttpHeaders *hdrs, + const gchar *key) +{ + const GList *values = purple_http_headers_get_all_by_name(hdrs, key); + + if (!values) + return NULL; + + return values->data; +} + +static gboolean purple_http_headers_get_int(PurpleHttpHeaders *hdrs, + const gchar *key, int *dst) +{ + int val; + const gchar *str; + + str = purple_http_headers_get(hdrs, key); + if (!str) + return FALSE; + + if (1 != sscanf(str, "%d", &val)) + return FALSE; + + *dst = val; + return TRUE; +} + +static gboolean purple_http_headers_match(PurpleHttpHeaders *hdrs, + const gchar *key, const gchar *value) +{ + const gchar *str; + + str = purple_http_headers_get(hdrs, key); + if (str == NULL || value == NULL) + return str == value; + + return (g_ascii_strcasecmp(str, value) == 0); +} + +static gchar * purple_http_headers_dump(PurpleHttpHeaders *hdrs) +{ + const GList *hdr; + + GString *s = g_string_new(""); + + hdr = purple_http_headers_get_all(hdrs); + while (hdr) { + PurpleKeyValuePair *kvp = hdr->data; + hdr = g_list_next(hdr); + + g_string_append_printf(s, "%s: %s%s", kvp->key, + (gchar*)kvp->value, hdr ? "\n" : ""); + } + + return g_string_free(s, FALSE); +} + +/*** HTTP protocol backend ****************************************************/ + +static void _purple_http_disconnect(PurpleHttpConnection *hc); + +static void _purple_http_gen_headers(PurpleHttpConnection *hc); +static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc, gint fd); +static void _purple_http_recv(gpointer _hc, gint fd, + PurpleInputCondition cond); +static void _purple_http_recv_ssl(gpointer _hc, + PurpleSslConnection *ssl_connection, PurpleInputCondition cond); +static void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond); + +static void _purple_http_connected_raw(gpointer _hc, gint source, + const gchar *error_message); +static void _purple_http_connected_ssl(gpointer _hc, + PurpleSslConnection *ssl_connection, PurpleInputCondition cond); +static void _purple_http_connected_ssl_error( + PurpleSslConnection *ssl_connection, PurpleSslErrorType error, + gpointer _hc); + +/* closes current connection (if exists), estabilishes one and proceeds with + * request */ +static gboolean _purple_http_reconnect(PurpleHttpConnection *hc); + +static void _purple_http_error(PurpleHttpConnection *hc, const char *format, + ...) G_GNUC_PRINTF(2, 3); + +static void _purple_http_error(PurpleHttpConnection *hc, const char *format, + ...) +{ + va_list args; + + va_start(args, format); + hc->response->error = g_strdup_vprintf(format, args); + va_end(args); + + purple_http_conn_cancel(hc); +} + +static void _purple_http_gen_headers(PurpleHttpConnection *hc) +{ + GString *h; + PurpleHttpURL *url; + const GList *hdr; + PurpleHttpRequest *req; + PurpleHttpHeaders *hdrs; + gchar *request_url, *tmp_url = NULL; + + PurpleProxyInfo *proxy; + gboolean proxy_http = FALSE; + const gchar *proxy_username, *proxy_password; + + g_return_if_fail(hc != NULL); + + if (hc->request_header != NULL) + return; + + req = hc->request; + url = hc->url; + hdrs = req->headers; + proxy = purple_proxy_get_setup(hc->gc ? + purple_connection_get_account(hc->gc) : NULL); + + proxy_http = (purple_proxy_info_get_type(proxy) == PURPLE_PROXY_HTTP || + purple_proxy_info_get_type(proxy) == PURPLE_PROXY_USE_ENVVAR); + /* this is HTTP proxy, but used with tunelling with CONNECT */ + if (proxy_http && url->port != 80) + proxy_http = FALSE; + + hc->request_header = h = g_string_new(""); + hc->request_header_written = 0; + hc->request_contents_written = 0; + + if (proxy_http) + request_url = tmp_url = purple_http_url_print(url); + else + request_url = url->path; + + g_string_append_printf(h, "%s %s HTTP/%s\r\n", + req->method ? req->method : "GET", + request_url, + req->http11 ? "1.1" : "1.0"); + + if (tmp_url) + g_free(tmp_url); + + if (!purple_http_headers_get(hdrs, "host")) + g_string_append_printf(h, "Host: %s\r\n", url->host); + if (!purple_http_headers_get(hdrs, "connection")) + g_string_append(h, "Connection: close\r\n"); + if (!purple_http_headers_get(hdrs, "accept")) + g_string_append(h, "Accept: */*\r\n"); + + if (req->contents_length > 0 && !purple_http_headers_get(hdrs, + "content-length")) + g_string_append_printf(h, "Content-Length: %u\r\n", + req->contents_length); + + if (proxy_http) + g_string_append(h, "Proxy-Connection: close\r\n"); + + proxy_username = purple_proxy_info_get_username(proxy); + if (proxy_http && proxy_username != NULL && proxy_username[0] != '\0') { + gchar *proxy_auth, *ntlm_type1, *tmp; + int len; + + proxy_password = purple_proxy_info_get_password(proxy); + if (proxy_password == NULL) + proxy_password = ""; + + tmp = g_strdup_printf("%s:%s", proxy_username, proxy_password); + len = strlen(tmp); + proxy_auth = purple_base64_encode((const guchar *)tmp, len); + memset(tmp, 0, len); + g_free(tmp); + + ntlm_type1 = purple_ntlm_gen_type1(purple_get_host_name(), ""); + + g_string_append_printf(h, "Proxy-Authorization: Basic %s\r\n", + proxy_auth); + g_string_append_printf(h, "Proxy-Authorization: NTLM %s\r\n", + ntlm_type1); + g_string_append(h, "Proxy-Connection: Close\r\n"); + + memset(proxy_auth, 0, strlen(proxy_auth)); + g_free(proxy_auth); + g_free(ntlm_type1); + } + + hdr = purple_http_headers_get_all(hdrs); + while (hdr) { + PurpleKeyValuePair *kvp = hdr->data; + hdr = g_list_next(hdr); + + g_string_append_printf(h, "%s: %s\r\n", + kvp->key, (gchar*)kvp->value); + } + + if (!purple_http_cookie_jar_is_empty(req->cookie_jar)) { + gchar * cookies = purple_http_cookie_jar_gen(req->cookie_jar); + g_string_append_printf(h, "Cookie: %s\r\n", cookies); + g_free(cookies); + } + + g_string_append_printf(h, "\r\n"); + + if (purple_debug_is_unsafe() && purple_debug_is_verbose()) { + purple_debug_misc("http", "Generated request headers:\n%s", + h->str); + } +} + +static gboolean _purple_http_recv_headers(PurpleHttpConnection *hc, + const gchar *buf, int len) +{ + gchar *eol, *delim; + + if (hc->headers_got) { + purple_debug_error("http", "Headers already got\n"); + _purple_http_error(hc, _("Error parsing HTTP")); + return FALSE; + } + + g_string_append_len(hc->response_buffer, buf, len); + if (hc->response_buffer->len > PURPLE_HTTP_MAX_RECV_BUFFER_LEN) { + purple_debug_error("http", + "Buffer too big when parsing headers\n"); + _purple_http_error(hc, _("Error parsing HTTP")); + return FALSE; + } + + while ((eol = strstr(hc->response_buffer->str, "\r\n")) + != NULL) { + gchar *hdrline = hc->response_buffer->str; + int hdrline_len = eol - hdrline; + + hdrline[hdrline_len] = '\0'; + + if (hdrline[0] == '\0') { + if (!hc->main_header_got) { + hc->response->code = 0; + purple_debug_warning("http", + "Main header not present\n"); + _purple_http_error(hc, _("Error parsing HTTP")); + return FALSE; + } + hc->headers_got = TRUE; + if (purple_debug_is_verbose()) + purple_debug_misc("http", "Got headers end\n"); + } else if (!hc->main_header_got) { + hc->main_header_got = TRUE; + delim = strchr(hdrline, ' '); + if (delim == NULL || 1 != sscanf(delim + 1, "%d", + &hc->response->code)) { + purple_debug_warning("http", + "Invalid response code\n"); + _purple_http_error(hc, _("Error parsing HTTP")); + return FALSE; + } + if (purple_debug_is_verbose()) + purple_debug_misc("http", + "Got main header with code %d\n", + hc->response->code); + } else { + if (purple_debug_is_verbose() && + purple_debug_is_unsafe()) + purple_debug_misc("http", "Got header: %s\n", + hdrline); + delim = strchr(hdrline, ':'); + if (delim == NULL || delim == hdrline) { + purple_debug_warning("http", + "Bad header delimiter\n"); + _purple_http_error(hc, _("Error parsing HTTP")); + return FALSE; + } + *delim++ = '\0'; + while (*delim == ' ') + delim++; + + purple_http_headers_add(hc->response->headers, hdrline, delim); + } + + g_string_erase(hc->response_buffer, 0, hdrline_len + 2); + if (hc->headers_got) + break; + } + return TRUE; +} + +static gboolean _purple_http_recv_body_data(PurpleHttpConnection *hc, + const gchar *buf, int len) +{ + int current_offset = hc->data_length_got; + + if (hc->request->max_length >= 0) { + if (hc->data_length_got + len > hc->request->max_length) { + len = hc->request->max_length - hc->data_length_got; + hc->length_expected = hc->length_got; + } + } + hc->data_length_got += len; + + if (len == 0) + return TRUE; + + if (hc->request->response_writer != NULL) { + gboolean succ; + succ = hc->request->response_writer(hc, hc->response, buf, + current_offset, len, hc->request->response_writer_data); + if (!succ) { + purple_debug_error("http", + "Cannot write using callback\n"); + _purple_http_error(hc, + _("Error handling retrieved data")); + return FALSE; + } + } else { + if (hc->response->contents == NULL) + hc->response->contents = g_string_new(""); + g_string_append_len(hc->response->contents, buf, len); + } + + purple_http_conn_notify_progress_watcher(hc); + return TRUE; +} + +static gboolean _purple_http_recv_body_chunked(PurpleHttpConnection *hc, + const gchar *buf, int len) +{ + gchar *eol, *line; + int line_len; + + if (hc->chunks_done) + return FALSE; + if (!hc->response_buffer) + hc->response_buffer = g_string_new(""); + + g_string_append_len(hc->response_buffer, buf, len); + if (hc->response_buffer->len > PURPLE_HTTP_MAX_RECV_BUFFER_LEN) { + purple_debug_error("http", + "Buffer too big when searching for chunk\n"); + _purple_http_error(hc, _("Error parsing HTTP")); + return FALSE; + } + + while (hc->response_buffer->len > 0) { + if (hc->in_chunk) { + int got_now = hc->response_buffer->len; + if (hc->chunk_got + got_now > hc->chunk_length) + got_now = hc->chunk_length - hc->chunk_got; + hc->chunk_got += got_now; + + if (!_purple_http_recv_body_data(hc, + hc->response_buffer->str, got_now)) + return FALSE; + + g_string_erase(hc->response_buffer, 0, got_now); + hc->in_chunk = (hc->chunk_got < hc->chunk_length); + + if (purple_debug_is_verbose()) + purple_debug_misc("http", "Chunk (%d/%d)\n", + hc->chunk_got, hc->chunk_length); + + continue; + } + + line = hc->response_buffer->str; + eol = strstr(line, "\r\n"); + if (eol == line) { + g_string_erase(hc->response_buffer, 0, 2); + line = hc->response_buffer->str; + eol = strstr(line, "\r\n"); + } + if (eol == NULL) { + /* waiting for more data (unlikely, but possible) */ + if (hc->response_buffer->len > 20) { + purple_debug_warning("http", "Chunk length not " + "found (buffer too large)\n"); + _purple_http_error(hc, _("Error parsing HTTP")); + return FALSE; + } + return TRUE; + } + line_len = eol - line; + + if (1 != sscanf(line, "%x", &hc->chunk_length)) { + if (purple_debug_is_unsafe()) + purple_debug_warning("http", + "Chunk length not found in [%s]\n", + line); + else + purple_debug_warning("http", + "Chunk length not found\n"); + _purple_http_error(hc, _("Error parsing HTTP")); + return FALSE; + } + hc->chunk_got = 0; + hc->in_chunk = TRUE; + + if (purple_debug_is_verbose()) + purple_debug_misc("http", "Found chunk of length %d\n", hc->chunk_length); + + g_string_erase(hc->response_buffer, 0, line_len + 2); + + if (hc->chunk_length == 0) { + hc->chunks_done = TRUE; + hc->in_chunk = FALSE; + return TRUE; + } + } + + return TRUE; +} + +static gboolean _purple_http_recv_body(PurpleHttpConnection *hc, + const gchar *buf, int len) +{ + if (hc->is_chunked) + { + hc->length_got += len; + return _purple_http_recv_body_chunked(hc, buf, len); + } + + if (hc->length_expected >= 0 && + len + hc->length_got > hc->length_expected) + len = hc->length_expected - hc->length_got; + hc->length_got += len; + + return _purple_http_recv_body_data(hc, buf, len); +} + +static gboolean _purple_http_recv_loopbody(PurpleHttpConnection *hc, gint fd) +{ + PurpleHttpSocket *hs = &hc->socket; + int len; + gchar buf[4096]; + gboolean got_anything; + + if (hs->is_ssl) + len = purple_ssl_read(hs->ssl_connection, buf, sizeof(buf)); + else + len = read(fd, buf, sizeof(buf)); + got_anything = (len > 0); + + if (len < 0 && errno == EAGAIN) + return FALSE; + + if (len < 0) { + _purple_http_error(hc, _("Error reading from %s: %s"), + hc->url->host, g_strerror(errno)); + return FALSE; + } + + /* EOF */ + if (len == 0) { + if (hc->length_expected >= 0 && + hc->length_got < hc->length_expected) { + purple_debug_warning("http", "No more data while reading" + " contents\n"); + _purple_http_error(hc, _("Error parsing HTTP")); + return FALSE; + } + if (hc->headers_got) + hc->length_expected = hc->length_got; + else { + purple_debug_warning("http", "No more data while " + "parsing headers\n"); + _purple_http_error(hc, _("Error parsing HTTP")); + return FALSE; + } + } + + if (!hc->headers_got && len > 0) { + if (!_purple_http_recv_headers(hc, buf, len)) + return FALSE; + len = 0; + if (hc->headers_got) { + if (!purple_http_headers_get_int(hc->response->headers, + "Content-Length", &hc->length_expected)) + hc->length_expected = -1; + hc->is_chunked = (purple_http_headers_match( + hc->response->headers, + "Transfer-Encoding", "chunked")); + } + if (hc->headers_got && hc->response_buffer && + hc->response_buffer->len > 0) { + int buffer_len = hc->response_buffer->len; + gchar *buffer = g_string_free(hc->response_buffer, FALSE); + hc->response_buffer = NULL; + _purple_http_recv_body(hc, buffer, buffer_len); + } + if (!hc->headers_got) + return got_anything; + } + + if (len > 0) { + if (!_purple_http_recv_body(hc, buf, len)) + return FALSE; + } + + if (hc->is_chunked && hc->chunks_done) + hc->length_expected = hc->length_got; + + if (hc->length_expected >= 0 && hc->length_got >= hc->length_expected) { + const gchar *redirect; + + if (!hc->headers_got) { + hc->response->code = 0; + purple_debug_warning("http", "No headers got\n"); + _purple_http_error(hc, _("Error parsing HTTP")); + return FALSE; + } + + if (purple_debug_is_unsafe() && purple_debug_is_verbose()) { + gchar *hdrs = purple_http_headers_dump( + hc->response->headers); + purple_debug_misc("http", "Got response headers: %s\n", + hdrs); + g_free(hdrs); + } + + purple_http_cookie_jar_parse(hc->request->cookie_jar, + purple_http_headers_get_all_by_name( + hc->response->headers, "Set-Cookie")); + + if (purple_debug_is_unsafe() && purple_debug_is_verbose() && + !purple_http_cookie_jar_is_empty( + hc->request->cookie_jar)) { + gchar *cookies = purple_http_cookie_jar_dump( + hc->request->cookie_jar); + purple_debug_misc("http", "Cookies: %s\n", cookies); + g_free(cookies); + } + + if (hc->response->code == 407) { + _purple_http_error(hc, _("Invalid proxy credentials")); + return FALSE; + } + + redirect = purple_http_headers_get(hc->response->headers, + "location"); + if (redirect && (hc->request->max_redirects == -1 || + hc->request->max_redirects > hc->redirects_count)) { + PurpleHttpURL *url = purple_http_url_parse(redirect); + + hc->redirects_count++; + + if (!url) { + if (purple_debug_is_unsafe()) + purple_debug_warning("http", + "Invalid redirect to %s\n", + redirect); + else + purple_debug_warning("http", + "Invalid redirect\n"); + _purple_http_error(hc, _("Error parsing HTTP")); + } + + purple_http_url_relative(hc->url, url); + purple_http_url_free(url); + + _purple_http_reconnect(hc); + return FALSE; + } + + _purple_http_disconnect(hc); + purple_http_connection_terminate(hc); + return FALSE; + } + + return got_anything; +} + +static void _purple_http_recv(gpointer _hc, gint fd, PurpleInputCondition cond) +{ + PurpleHttpConnection *hc = _hc; + + while (_purple_http_recv_loopbody(hc, fd)); +} + +static void _purple_http_recv_ssl(gpointer _hc, + PurpleSslConnection *ssl_connection, PurpleInputCondition cond) +{ + _purple_http_recv(_hc, -1, cond); +} + +static void _purple_http_send_got_data(PurpleHttpConnection *hc, + gboolean success, gboolean eof, size_t stored) +{ + int estimated_length; + + g_return_if_fail(hc != NULL); + + if (!success) { + _purple_http_error(hc, _("Error requesting data to write")); + return; + } + + hc->contents_reader_requested = FALSE; + g_string_set_size(hc->contents_reader_buffer, stored); + if (!eof) + return; + + estimated_length = hc->request_contents_written + stored; + + if (hc->request->contents_length != -1 && + hc->request->contents_length != estimated_length) { + purple_debug_warning("http", + "Invalid amount of data has been written\n"); + } + hc->request->contents_length = estimated_length; +} + +static void _purple_http_send(gpointer _hc, gint fd, PurpleInputCondition cond) +{ + PurpleHttpConnection *hc = _hc; + PurpleHttpSocket *hs = &hc->socket; + int written, write_len; + const gchar *write_from; + gboolean writing_headers; + + /* Waiting for data. This could be written more efficiently, by removing + * (and later, adding) hs->inpa. */ + if (hc->contents_reader_requested) + return; + + _purple_http_gen_headers(hc); + + writing_headers = + (hc->request_header_written < hc->request_header->len); + if (writing_headers) { + write_from = hc->request_header->str + + hc->request_header_written; + write_len = hc->request_header->len - + hc->request_header_written; + } else if (hc->request->contents_reader) { + if (hc->contents_reader_requested) + return; /* waiting for data */ + if (!hc->contents_reader_buffer) + hc->contents_reader_buffer = g_string_new(""); + if (hc->contents_reader_buffer->len == 0) { + hc->contents_reader_requested = TRUE; + g_string_set_size(hc->contents_reader_buffer, + PURPLE_HTTP_MAX_READ_BUFFER_LEN); + hc->request->contents_reader(hc, + hc->contents_reader_buffer->str, + hc->request_contents_written, + PURPLE_HTTP_MAX_READ_BUFFER_LEN, + hc->request->contents_reader_data, + _purple_http_send_got_data); + return; + } + write_from = hc->contents_reader_buffer->str; + write_len = hc->contents_reader_buffer->len; + } else { + write_from = hc->request->contents + + hc->request_contents_written; + write_len = hc->request->contents_length - + hc->request_contents_written; + } + + if (write_len == 0) { + purple_debug_warning("http", "Nothing to write\n"); + written = 0; + } else if (hs->is_ssl) + written = purple_ssl_write(hs->ssl_connection, + write_from, write_len); + else + written = write(hs->fd, write_from, write_len); + + if (written < 0 && errno == EAGAIN) + return; + + if (written < 0) { + _purple_http_error(hc, _("Error writing to %s: %s"), + hc->url->host, g_strerror(errno)); + return; + } + + if (writing_headers) { + hc->request_header_written += written; + purple_http_conn_notify_progress_watcher(hc); + if (hc->request_header_written < hc->request_header->len) + return; + if (hc->request->contents_length > 0) + return; + } else { + hc->request_contents_written += written; + purple_http_conn_notify_progress_watcher(hc); + if (hc->contents_reader_buffer) + g_string_erase(hc->contents_reader_buffer, 0, written); + if (hc->request_contents_written < hc->request->contents_length) + return; + } + + /* request is completely written, let's read the response */ + hc->is_reading = TRUE; + purple_input_remove(hs->inpa); + hs->inpa = 0; + if (hs->is_ssl) + purple_ssl_input_add(hs->ssl_connection, + _purple_http_recv_ssl, hc); + else + hs->inpa = purple_input_add(hs->fd, PURPLE_INPUT_READ, + _purple_http_recv, hc); +} + +static void _purple_http_connected_raw(gpointer _hc, gint fd, + const gchar *error_message) +{ + PurpleHttpConnection *hc = _hc; + PurpleHttpSocket *hs = &hc->socket; + + hs->raw_connection = NULL; + + if (fd == -1) { + _purple_http_error(hc, _("Unable to connect to %s: %s"), + hc->url->host, error_message); + return; + } + + hs->fd = fd; + hs->inpa = purple_input_add(fd, PURPLE_INPUT_WRITE, + _purple_http_send, hc); +} + +static void _purple_http_connected_ssl(gpointer _hc, + PurpleSslConnection *ssl_connection, PurpleInputCondition cond) +{ + PurpleHttpConnection *hc = _hc; + PurpleHttpSocket *hs = &hc->socket; + + hs->fd = hs->ssl_connection->fd; + hs->inpa = purple_input_add(hs->fd, PURPLE_INPUT_WRITE, + _purple_http_send, hc); +} + +static void _purple_http_connected_ssl_error( + PurpleSslConnection *ssl_connection, PurpleSslErrorType error, + gpointer _hc) +{ + PurpleHttpConnection *hc = _hc; + PurpleHttpSocket *hs = &hc->socket; + + hs->ssl_connection = NULL; + _purple_http_error(hc, _("Unable to connect to %s: %s"), + hc->url->host, purple_ssl_strerror(error)); +} + +static void _purple_http_disconnect(PurpleHttpConnection *hc) +{ + PurpleHttpSocket *hs; + + g_return_if_fail(hc != NULL); + + hs = &hc->socket; + + if (hc->request_header) + g_string_free(hc->request_header, TRUE); + hc->request_header = NULL; + if (hc->response_buffer) + g_string_free(hc->response_buffer, TRUE); + hc->response_buffer = NULL; + + if (hs->inpa != 0) + purple_input_remove(hs->inpa); + + if (hs->is_ssl) { + if (hs->ssl_connection != NULL) + purple_ssl_close(hs->ssl_connection); + } else { + if (hs->raw_connection != NULL) + purple_proxy_connect_cancel(hs->raw_connection); + if (hs->fd > 0) + close(hs->fd); + } + + memset(hs, 0, sizeof(PurpleHttpSocket)); +} + +static gboolean _purple_http_reconnect(PurpleHttpConnection *hc) +{ + PurpleHttpURL *url; + gboolean is_ssl = FALSE; + PurpleAccount *account = NULL; + + g_return_val_if_fail(hc != NULL, FALSE); + g_return_val_if_fail(hc->url != NULL, FALSE); + + _purple_http_disconnect(hc); + + if (purple_debug_is_verbose()) { + if (purple_debug_is_unsafe()) { + gchar *url = purple_http_url_print(hc->url); + purple_debug_misc("http", "Connecting to %s...\n", url); + g_free(url); + } else + purple_debug_misc("http", "Connecting to %s...\n", + hc->url->host); + } + + if (hc->gc) + account = purple_connection_get_account(hc->gc); + + url = hc->url; + if (url->protocol[0] == '\0' || + g_ascii_strcasecmp(url->protocol, "http") == 0) { + /* do nothing */ + } else if (g_ascii_strcasecmp(url->protocol, "https") == 0) { + is_ssl = TRUE; + } else { + _purple_http_error(hc, _("Unsupported protocol: %s"), + url->protocol); + return FALSE; + } + + hc->socket.is_ssl = is_ssl; + if (is_ssl) { + if (!purple_ssl_is_supported()) { + _purple_http_error(hc, _("Unable to connect to %s: %s"), + url->host, _("Server requires TLS/SSL, " + "but no TLS/SSL support was found.")); + return FALSE; + } + hc->socket.ssl_connection = purple_ssl_connect(account, + url->host, url->port, + _purple_http_connected_ssl, + _purple_http_connected_ssl_error, hc); +/* TODO + purple_ssl_set_compatibility_level(hc->socket.ssl_connection, + PURPLE_SSL_COMPATIBILITY_SECURE); +*/ + } else { + hc->socket.raw_connection = purple_proxy_connect(hc->gc, account, + url->host, url->port, + _purple_http_connected_raw, hc); + } + + if (hc->socket.ssl_connection == NULL && + hc->socket.raw_connection == NULL) { + _purple_http_error(hc, _("Unable to connect to %s"), url->host); + return FALSE; + } + + purple_http_headers_free(hc->response->headers); + hc->response->headers = purple_http_headers_new(); + hc->response_buffer = g_string_new(""); + hc->main_header_got = FALSE; + hc->headers_got = FALSE; + if (hc->response->contents != NULL) + g_string_free(hc->response->contents, TRUE); + hc->response->contents = NULL; + hc->length_got = 0; + hc->data_length_got = 0; + hc->length_expected = -1; + hc->is_chunked = FALSE; + hc->in_chunk = FALSE; + hc->chunks_done = FALSE; + + purple_http_conn_notify_progress_watcher(hc); + + return TRUE; +} + +/*** Performing HTTP requests *************************************************/ + +static gboolean purple_http_request_timeout(gpointer _hc) +{ + PurpleHttpConnection *hc = _hc; + + purple_debug_warning("http", "Timeout reached for request %p\n", hc); + + purple_http_conn_cancel(hc); + + return FALSE; +} + +PurpleHttpConnection * purple_http_get(PurpleConnection *gc, const gchar *url, + PurpleHttpCallback callback, gpointer user_data) +{ + PurpleHttpRequest *request; + PurpleHttpConnection *hc; + + g_return_val_if_fail(url != NULL, NULL); + + request = purple_http_request_new(url); + hc = purple_http_request(gc, request, callback, user_data); + purple_http_request_unref(request); + + return hc; +} + +PurpleHttpConnection * purple_http_request(PurpleConnection *gc, + PurpleHttpRequest *request, PurpleHttpCallback callback, + gpointer user_data) +{ + PurpleHttpConnection *hc; + + g_return_val_if_fail(request != NULL, NULL); + + hc = purple_http_connection_new(request, gc); + hc->callback = callback; + hc->user_data = user_data; + + if (purple_debug_is_unsafe()) + purple_debug_misc("http", "Performing new request %p for %s.\n", + hc, request->url); + else + purple_debug_misc("http", "Performing new request %p.\n", hc); + + hc->url = purple_http_url_parse(request->url); + if (!hc->url || hc->url->host[0] == '\0') { + purple_debug_error("http", "Invalid URL requested.\n"); + purple_http_connection_terminate(hc); + return NULL; + } + + _purple_http_reconnect(hc); + + hc->timeout_handle = purple_timeout_add_seconds(request->timeout, + purple_http_request_timeout, hc); + + return hc; +} + +/*** HTTP connection API ******************************************************/ + +static void purple_http_connection_free(PurpleHttpConnection *hc); +static gboolean purple_http_conn_notify_progress_watcher_timeout(gpointer _hc); + +static PurpleHttpConnection * purple_http_connection_new( + PurpleHttpRequest *request, PurpleConnection *gc) +{ + PurpleHttpConnection *hc = g_new0(PurpleHttpConnection, 1); + + hc->request = request; + purple_http_request_ref(request); + hc->response = purple_http_response_new(); + + hc->link_global = purple_http_hc_list = + g_list_prepend(purple_http_hc_list, hc); + g_hash_table_insert(purple_http_hc_by_ptr, hc, hc->link_global); + if (gc) { + GList *gc_list = g_hash_table_lookup(purple_http_hc_by_gc, gc); + g_hash_table_steal(purple_http_hc_by_gc, gc); + hc->link_gc = gc_list = g_list_prepend(gc_list, hc); + g_hash_table_insert(purple_http_hc_by_gc, gc, gc_list); + hc->gc = gc; + } + + return hc; +} + +static void purple_http_connection_free(PurpleHttpConnection *hc) +{ + if (hc->timeout_handle) + purple_timeout_remove(hc->timeout_handle); + if (hc->watcher_delayed_handle) + purple_timeout_remove(hc->watcher_delayed_handle); + + purple_http_url_free(hc->url); + purple_http_request_unref(hc->request); + purple_http_response_free(hc->response); + + if (hc->contents_reader_buffer) + g_string_free(hc->contents_reader_buffer, TRUE); + + if (hc->request_header) + g_string_free(hc->request_header, TRUE); + + purple_http_hc_list = g_list_delete_link(purple_http_hc_list, + hc->link_global); + g_hash_table_remove(purple_http_hc_by_ptr, hc); + if (hc->gc) { + GList *gc_list, *gc_list_new; + gc_list = g_hash_table_lookup(purple_http_hc_by_gc, hc->gc); + g_assert(gc_list != NULL); + + gc_list_new = g_list_delete_link(gc_list, hc->link_gc); + if (gc_list != gc_list_new) { + g_hash_table_steal(purple_http_hc_by_gc, hc->gc); + if (gc_list_new) + g_hash_table_insert(purple_http_hc_by_gc, + hc->gc, gc_list_new); + } + } + + g_free(hc); +} + +/* call callback and do the cleanup */ +static void purple_http_connection_terminate(PurpleHttpConnection *hc) +{ + g_return_if_fail(hc != NULL); + + purple_debug_misc("http", "Request %p performed %s.\n", hc, + purple_http_response_is_successfull(hc->response) ? + "successfully" : "without success"); + + if (hc->callback) + hc->callback(hc, hc->response, hc->user_data); + + purple_http_connection_free(hc); +} + +void purple_http_conn_cancel(PurpleHttpConnection *http_conn) +{ + if (http_conn == NULL) + return; + + http_conn->response->code = 0; + _purple_http_disconnect(http_conn); + purple_http_connection_terminate(http_conn); +} + +void purple_http_conn_cancel_all(PurpleConnection *gc) +{ + GList *gc_list = g_hash_table_lookup(purple_http_hc_by_gc, gc); + + while (gc_list) { + PurpleHttpConnection *hc = gc_list->data; + gc_list = g_list_next(gc_list); + purple_http_conn_cancel(hc); + } + + if (NULL != g_hash_table_lookup(purple_http_hc_by_gc, gc)) + purple_debug_error("http", "Couldn't cancel all connections " + "related to gc=%p\n", gc); +} + +gboolean purple_http_conn_is_running(PurpleHttpConnection *http_conn) +{ + if (http_conn == NULL) + return FALSE; + return (NULL != g_hash_table_lookup(purple_http_hc_by_ptr, http_conn)); +} + +PurpleHttpRequest * purple_http_conn_get_request(PurpleHttpConnection *http_conn) +{ + g_return_val_if_fail(http_conn != NULL, NULL); + + return http_conn->request; +} + +PurpleHttpCookieJar * purple_http_conn_get_cookie_jar( + PurpleHttpConnection *http_conn) +{ + return purple_http_request_get_cookie_jar(purple_http_conn_get_request( + http_conn)); +} + +PurpleConnection * purple_http_conn_get_purple_connection( + PurpleHttpConnection *http_conn) +{ + g_return_val_if_fail(http_conn != NULL, NULL); + + return http_conn->gc; +} + +void purple_http_conn_set_progress_watcher(PurpleHttpConnection *http_conn, + PurpleHttpProgressWatcher watcher, gpointer user_data, + guint interval_threshold) +{ + g_return_if_fail(http_conn != NULL); + + http_conn->watcher = watcher; + http_conn->watcher_user_data = user_data; + http_conn->watcher_interval_threshold = interval_threshold; +} + +static void purple_http_conn_notify_progress_watcher( + PurpleHttpConnection *hc) +{ + gint64 now; + gboolean reading_state; + int processed, total; + + g_return_if_fail(hc != NULL); + + if (hc->watcher == NULL) + return; + + reading_state = hc->is_reading; + if (reading_state) { + total = hc->length_expected; + processed = hc->length_got; + } else { + total = hc->request->contents_length; + processed = hc->request_contents_written; + if (total == 0) + total = -1; + } + if (total != -1 && total < processed) { + purple_debug_warning("http", "Processed too much\n"); + total = processed; + } + + now = g_get_monotonic_time(); + if (hc->watcher_last_call + hc->watcher_interval_threshold + > now && processed != total) { + if (hc->watcher_delayed_handle) + return; + hc->watcher_delayed_handle = purple_timeout_add_seconds( + 1 + hc->watcher_interval_threshold / 1000000, + purple_http_conn_notify_progress_watcher_timeout, hc); + return; + } + + if (hc->watcher_delayed_handle) + purple_timeout_remove(hc->watcher_delayed_handle); + hc->watcher_delayed_handle = 0; + + hc->watcher_last_call = now; + hc->watcher(hc, reading_state, processed, total, hc->watcher_user_data); +} + +static gboolean purple_http_conn_notify_progress_watcher_timeout(gpointer _hc) +{ + PurpleHttpConnection *hc = _hc; + + purple_http_conn_notify_progress_watcher(hc); + + return FALSE; +} + +/*** Cookie jar API ***********************************************************/ + +static PurpleHttpCookie * purple_http_cookie_new(const gchar *value); +void purple_http_cookie_free(PurpleHttpCookie *cookie); + +static void purple_http_cookie_jar_set_ext(PurpleHttpCookieJar *cookie_jar, + const gchar *name, const gchar *value, time_t expires); + +static PurpleHttpCookie * purple_http_cookie_new(const gchar *value) +{ + PurpleHttpCookie *cookie = g_new0(PurpleHttpCookie, 1); + + cookie->value = g_strdup(value); + cookie->expires = -1; + + return cookie; +} + +void purple_http_cookie_free(PurpleHttpCookie *cookie) +{ + g_free(cookie->value); + g_free(cookie); +} + +void purple_http_cookie_jar_free(PurpleHttpCookieJar *cookie_jar); + +PurpleHttpCookieJar * purple_http_cookie_jar_new(void) +{ + PurpleHttpCookieJar *cjar = g_new0(PurpleHttpCookieJar, 1); + + cjar->ref_count = 1; + cjar->tab = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + (GDestroyNotify)purple_http_cookie_free); + + return cjar; +} + +void purple_http_cookie_jar_free(PurpleHttpCookieJar *cookie_jar) +{ + g_hash_table_destroy(cookie_jar->tab); + g_free(cookie_jar); +} + +void purple_http_cookie_jar_ref(PurpleHttpCookieJar *cookie_jar) +{ + g_return_if_fail(cookie_jar != NULL); + + cookie_jar->ref_count++; +} + +PurpleHttpCookieJar * purple_http_cookie_jar_unref( + PurpleHttpCookieJar *cookie_jar) +{ + if (cookie_jar == NULL) + return NULL; + + g_return_val_if_fail(cookie_jar->ref_count > 0, NULL); + + cookie_jar->ref_count--; + if (cookie_jar->ref_count > 0) + return cookie_jar; + + purple_http_cookie_jar_free(cookie_jar); + return NULL; +} + +static void purple_http_cookie_jar_parse(PurpleHttpCookieJar *cookie_jar, + GList *values) +{ + values = g_list_first(values); + while (values) { + const gchar *cookie = values->data; + const gchar *eqsign, *semicolon; + gchar *name, *value; + time_t expires = -1; + values = g_list_next(values); + + eqsign = strchr(cookie, '='); + semicolon = strchr(cookie, ';'); + + if (eqsign == NULL || eqsign == cookie || + (semicolon != NULL && semicolon < eqsign)) { + if (purple_debug_is_unsafe()) + purple_debug_warning("http", + "Invalid cookie: [%s]\n", cookie); + else + purple_debug_warning("http", "Invalid cookie."); + } + + name = g_strndup(cookie, eqsign - cookie); + eqsign++; + if (semicolon != NULL) + value = g_strndup(eqsign, semicolon - eqsign); + else + value = g_strdup(eqsign); + + if (semicolon != NULL) { + GMatchInfo *match_info; + GRegex *re_expires = g_regex_new( + "expires=([a-z0-9, :]+)", + G_REGEX_OPTIMIZE | G_REGEX_CASELESS, + G_REGEX_MATCH_NOTEMPTY, NULL); + + g_regex_match(re_expires, semicolon, 0, &match_info); + if (g_match_info_matches(match_info)) { + gchar *expire_date = + g_match_info_fetch(match_info, 1); + expires = purple_http_rfc1123_to_time( + expire_date); + g_free(expire_date); + } + g_match_info_free(match_info); + + g_regex_unref(re_expires); + } + + purple_http_cookie_jar_set_ext(cookie_jar, name, value, expires); + + g_free(name); + g_free(value); + } +} + +static gchar * purple_http_cookie_jar_gen(PurpleHttpCookieJar *cookie_jar) +{ + GHashTableIter it; + gchar *key; + PurpleHttpCookie *cookie; + GString *str; + time_t now = time(NULL); + + g_return_val_if_fail(cookie_jar != NULL, NULL); + + str = g_string_new(""); + + g_hash_table_iter_init(&it, cookie_jar->tab); + while (g_hash_table_iter_next(&it, (gpointer*)&key, + (gpointer*)&cookie)) { + if (cookie->expires != -1 && cookie->expires <= now) + continue; + g_string_append_printf(str, "%s=%s; ", key, cookie->value); + } + + if (str->len > 0) + g_string_truncate(str, str->len - 2); + return g_string_free(str, FALSE); +} + +void purple_http_cookie_jar_set(PurpleHttpCookieJar *cookie_jar, + const gchar *name, const gchar *value) +{ + purple_http_cookie_jar_set_ext(cookie_jar, name, value, -1); +} + +static void purple_http_cookie_jar_set_ext(PurpleHttpCookieJar *cookie_jar, + const gchar *name, const gchar *value, time_t expires) +{ + g_return_if_fail(cookie_jar != NULL); + g_return_if_fail(name != NULL); + + if (expires != -1 && time(NULL) >= expires) + value = NULL; + + if (value != NULL) { + PurpleHttpCookie *cookie = purple_http_cookie_new(value); + cookie->expires = expires; + g_hash_table_insert(cookie_jar->tab, g_strdup(name), cookie); + } else + g_hash_table_remove(cookie_jar->tab, name); +} + +const gchar * purple_http_cookie_jar_get(PurpleHttpCookieJar *cookie_jar, + const gchar *name) +{ + PurpleHttpCookie *cookie; + + g_return_val_if_fail(cookie_jar != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + cookie = g_hash_table_lookup(cookie_jar->tab, name); + if (!cookie) + return NULL; + + return cookie->value; +} + +gchar * purple_http_cookie_jar_dump(PurpleHttpCookieJar *cjar) +{ + GHashTableIter it; + gchar *key; + PurpleHttpCookie *cookie; + GString *str = g_string_new(""); + + g_hash_table_iter_init(&it, cjar->tab); + while (g_hash_table_iter_next(&it, (gpointer*)&key, (gpointer*)&cookie)) + g_string_append_printf(str, "%s: %s (expires: %lld)\n", key, + cookie->value, (long long int)cookie->expires); + + if (str->len > 0) + g_string_truncate(str, str->len - 1); + return g_string_free(str, FALSE); +} + +gboolean purple_http_cookie_jar_is_empty(PurpleHttpCookieJar *cookie_jar) +{ + g_return_val_if_fail(cookie_jar != NULL, TRUE); + + return g_hash_table_size(cookie_jar->tab) == 0; +} + +/*** Request API **************************************************************/ + +static void purple_http_request_free(PurpleHttpRequest *request); + +PurpleHttpRequest * purple_http_request_new(const gchar *url) +{ + PurpleHttpRequest *request; + + g_return_val_if_fail(url != NULL, NULL); + + request = g_new0(PurpleHttpRequest, 1); + + request->ref_count = 1; + request->url = g_strdup(url); + request->headers = purple_http_headers_new(); + request->cookie_jar = purple_http_cookie_jar_new(); + + request->timeout = PURPLE_HTTP_REQUEST_DEFAULT_TIMEOUT; + request->max_redirects = PURPLE_HTTP_REQUEST_DEFAULT_MAX_REDIRECTS; + request->http11 = TRUE; + request->max_length = -1; + + return request; +} + +static void purple_http_request_free(PurpleHttpRequest *request) +{ + purple_http_headers_free(request->headers); + purple_http_cookie_jar_unref(request->cookie_jar); + g_free(request->url); + g_free(request); +} + +void purple_http_request_ref(PurpleHttpRequest *request) +{ + g_return_if_fail(request != NULL); + + request->ref_count++; +} + +PurpleHttpRequest * purple_http_request_unref(PurpleHttpRequest *request) +{ + if (request == NULL) + return NULL; + + g_return_val_if_fail(request->ref_count > 0, NULL); + + request->ref_count--; + if (request->ref_count > 0) + return request; + + purple_http_request_free(request); + return NULL; +} + +void purple_http_request_set_url(PurpleHttpRequest *request, const gchar *url) +{ + g_return_if_fail(request != NULL); + g_return_if_fail(url != NULL); + + g_free(request->url); + request->url = g_strdup(url); +} + +const gchar * purple_http_request_get_url(PurpleHttpRequest *request) +{ + g_return_val_if_fail(request != NULL, NULL); + + return request->url; +} + +void purple_http_request_set_method(PurpleHttpRequest *request, const gchar *method) +{ + g_return_if_fail(request != NULL); + + g_free(request->method); + request->method = g_strdup(method); +} + +const gchar * purple_http_request_get_method(PurpleHttpRequest *request) +{ + g_return_val_if_fail(request != NULL, NULL); + + return request->method; +} + +void purple_http_request_set_contents(PurpleHttpRequest *request, + const gchar *contents, int length) +{ + g_return_if_fail(request != NULL); + g_return_if_fail(length >= -1); + + request->contents_reader = NULL; + request->contents_reader_data = NULL; + + g_free(request->contents); + if (contents == NULL || length == 0) { + request->contents = NULL; + request->contents_length = 0; + return; + } + + if (length == -1) + length = strlen(contents); + request->contents = g_memdup(contents, length); + request->contents_length = length; +} + +void purple_http_request_set_contents_reader(PurpleHttpRequest *request, + PurpleHttpContentReader reader, int contents_length, gpointer user_data) +{ + g_return_if_fail(request != NULL); + g_return_if_fail(reader != NULL); + g_return_if_fail(contents_length >= -1); + + g_free(request->contents); + request->contents = NULL; + request->contents_length = contents_length; + request->contents_reader = reader; + request->contents_reader_data = user_data; +} + +void purple_http_request_set_response_writer(PurpleHttpRequest *request, + PurpleHttpContentWriter writer, gpointer user_data) +{ + g_return_if_fail(request != NULL); + + if (writer == NULL) + user_data = NULL; + request->response_writer = writer; + request->response_writer_data = user_data; +} + +void purple_http_request_set_timeout(PurpleHttpRequest *request, int timeout) +{ + g_return_if_fail(request != NULL); + + if (timeout < -1) + timeout = -1; + + request->timeout = timeout; +} + +int purple_http_request_get_timeout(PurpleHttpRequest *request) +{ + g_return_val_if_fail(request != NULL, -1); + + return request->timeout; +} + +void purple_http_request_set_max_redirects(PurpleHttpRequest *request, + int max_redirects) +{ + g_return_if_fail(request != NULL); + + if (max_redirects < -1) + max_redirects = -1; + + request->max_redirects = max_redirects; +} + +int purple_http_request_get_max_redirects(PurpleHttpRequest *request) +{ + g_return_val_if_fail(request != NULL, -1); + + return request->max_redirects; +} + +void purple_http_request_set_cookie_jar(PurpleHttpRequest *request, + PurpleHttpCookieJar *cookie_jar) +{ + g_return_if_fail(request != NULL); + g_return_if_fail(cookie_jar != NULL); + + purple_http_cookie_jar_ref(cookie_jar); + purple_http_cookie_jar_unref(request->cookie_jar); + request->cookie_jar = cookie_jar; +} + +PurpleHttpCookieJar * purple_http_request_get_cookie_jar( + PurpleHttpRequest *request) +{ + g_return_val_if_fail(request != NULL, NULL); + + return request->cookie_jar; +} + +void purple_http_request_set_http11(PurpleHttpRequest *request, gboolean http11) +{ + g_return_if_fail(request != NULL); + + request->http11 = http11; +} + +gboolean purple_http_request_is_http11(PurpleHttpRequest *request) +{ + g_return_val_if_fail(request != NULL, FALSE); + + return request->http11; +} + +void purple_http_request_set_max_len(PurpleHttpRequest *request, int max_len) +{ + g_return_if_fail(request != NULL); + + if (max_len < -1) + max_len = -1; + + request->max_length = max_len; +} + +int purple_http_request_get_max_len(PurpleHttpRequest *request) +{ + g_return_val_if_fail(request != NULL, -1); + + return request->max_length; +} + +void purple_http_request_header_set(PurpleHttpRequest *request, + const gchar *key, const gchar *value) +{ + g_return_if_fail(request != NULL); + g_return_if_fail(key != NULL); + + purple_http_headers_remove(request->headers, key); + if (value) + purple_http_headers_add(request->headers, key, value); +} + +void purple_http_request_header_set_printf(PurpleHttpRequest *request, + const gchar *key, const gchar *format, ...) +{ + va_list args; + gchar *value; + + g_return_if_fail(request != NULL); + g_return_if_fail(key != NULL); + g_return_if_fail(format != NULL); + + va_start(args, format); + value = g_strdup_vprintf(format, args); + va_end(args); + + purple_http_request_header_set(request, key, value); + g_free(value); +} + +void purple_http_request_header_add(PurpleHttpRequest *request, + const gchar *key, const gchar *value) +{ + g_return_if_fail(request != NULL); + g_return_if_fail(key != NULL); + + purple_http_headers_add(request->headers, key, value); +} + +/*** HTTP response API ********************************************************/ + +static PurpleHttpResponse * purple_http_response_new(void) +{ + PurpleHttpResponse *response = g_new0(PurpleHttpResponse, 1); + + return response; +} + +static void purple_http_response_free(PurpleHttpResponse *response) +{ + if (response->contents != NULL) + g_string_free(response->contents, TRUE); + g_free(response->error); + purple_http_headers_free(response->headers); + g_free(response); +} + +gboolean purple_http_response_is_successfull(PurpleHttpResponse *response) +{ + int code; + + g_return_val_if_fail(response != NULL, FALSE); + + code = response->code; + + if (code <= 0) + return FALSE; + + if (code / 100 == 2) + return TRUE; + + return FALSE; +} + +int purple_http_response_get_code(PurpleHttpResponse *response) +{ + g_return_val_if_fail(response != NULL, 0); + + return response->code; +} + +const gchar * purple_http_response_get_error(PurpleHttpResponse *response) +{ + g_return_val_if_fail(response != NULL, NULL); + + return response->error; +} + +gsize purple_http_response_get_data_len(PurpleHttpResponse *response) +{ + g_return_val_if_fail(response != NULL, 0); + + if (response->contents == NULL) + return 0; + + return response->contents->len; +} + +const gchar * purple_http_response_get_data(PurpleHttpResponse *response, size_t *len) +{ + const gchar *ret = ""; + + g_return_val_if_fail(response != NULL, NULL); + + if (response->contents != NULL) { + ret = response->contents->str; + if(len) + *len = response->contents->len; + } else { + if(len) + *len = 0; + } + + return ret; +} + +const GList * purple_http_response_get_all_headers(PurpleHttpResponse *response) +{ + g_return_val_if_fail(response != NULL, NULL); + + return purple_http_headers_get_all(response->headers); +} + +const GList * purple_http_response_get_headers_by_name( + PurpleHttpResponse *response, const gchar *name) +{ + g_return_val_if_fail(response != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + return purple_http_headers_get_all_by_name(response->headers, name); +} + +const gchar * purple_http_response_get_header(PurpleHttpResponse *response, + const gchar *name) +{ + g_return_val_if_fail(response != NULL, NULL); + g_return_val_if_fail(name != NULL, NULL); + + return purple_http_headers_get(response->headers, name); +} + +/*** URL functions ************************************************************/ + +static PurpleHttpURL * purple_http_url_parse(const char *raw_url) +{ + PurpleHttpURL *url; + GMatchInfo *match_info; + + gchar *host_full, *tmp; + + g_return_val_if_fail(raw_url != NULL, NULL); + + url = g_new0(PurpleHttpURL, 1); + + if (!g_regex_match(purple_http_re_url, raw_url, 0, &match_info)) { + if (purple_debug_is_verbose() && purple_debug_is_unsafe()) { + purple_debug_warning("http", + "Invalid URL provided: %s\n", + raw_url); + } + return NULL; + } + + url->protocol = g_match_info_fetch(match_info, 1); + host_full = g_match_info_fetch(match_info, 2); + url->path = g_match_info_fetch(match_info, 3); + url->fragment = g_match_info_fetch(match_info, 4); + g_match_info_free(match_info); + + if (url->protocol[0] == '\0') { + g_free(url->protocol); + url->protocol = NULL; + } else if (url->protocol != NULL) { + tmp = url->protocol; + url->protocol = g_ascii_strdown(url->protocol, -1); + g_free(tmp); + } + if (host_full[0] == '\0') { + g_free(host_full); + host_full = NULL; + } + if (url->path[0] == '\0') { + g_free(url->path); + url->path = NULL; + } + if ((url->protocol == NULL) != (host_full == NULL)) + purple_debug_warning("http", "Protocol or host not present " + "(unlikely case)\n"); + + if (host_full) { + gchar *port_str; + + if (!g_regex_match(purple_http_re_url_host, host_full, 0, + &match_info)) { + if (purple_debug_is_verbose() && + purple_debug_is_unsafe()) { + purple_debug_warning("http", + "Invalid host provided for URL: %s\n", + raw_url); + } + + g_free(host_full); + purple_http_url_free(url); + return NULL; + } + + url->user = g_match_info_fetch(match_info, 1); + url->password = g_match_info_fetch(match_info, 2); + url->host = g_match_info_fetch(match_info, 3); + port_str = g_match_info_fetch(match_info, 4); + + if (port_str && port_str[0]) + url->port = atoi(port_str); + + if (url->user[0] == '\0') { + g_free(url->user); + url->user = NULL; + } + if (url->password[0] == '\0') { + g_free(url->password); + url->password = NULL; + } + if (url->host[0] == '\0') { + g_free(url->host); + url->host = NULL; + } else if (url->host != NULL) { + tmp = url->host; + url->host = g_ascii_strdown(url->host, -1); + g_free(tmp); + } + + g_free(port_str); + g_match_info_free(match_info); + + g_free(host_full); + host_full = NULL; + } + + if (url->host != NULL) { + if (url->protocol == NULL) + url->protocol = g_strdup("http"); + if (url->port == 0 && 0 == strcmp(url->protocol, "http")) + url->port = 80; + if (url->port == 0 && 0 == strcmp(url->protocol, "https")) + url->port = 443; + if (url->path == NULL) + url->path = g_strdup("/"); + if (url->path[0] != '/') + purple_debug_warning("http", + "URL path doesn't start with slash\n"); + } + + return url; +} + +static void purple_http_url_free(PurpleHttpURL *parsed_url) +{ + if (parsed_url == NULL) + return; + + g_free(parsed_url->protocol); + g_free(parsed_url->user); + g_free(parsed_url->password); + g_free(parsed_url->host); + g_free(parsed_url->path); + g_free(parsed_url->fragment); + g_free(parsed_url); +} + +static void purple_http_url_relative(PurpleHttpURL *base_url, + PurpleHttpURL *relative_url) +{ + g_return_if_fail(base_url != NULL); + g_return_if_fail(relative_url != NULL); + + if (relative_url->host) { + g_free(base_url->protocol); + base_url->protocol = g_strdup(relative_url->protocol); + g_free(base_url->user); + base_url->user = g_strdup(relative_url->user); + g_free(base_url->password); + base_url->password = g_strdup(relative_url->password); + g_free(base_url->host); + base_url->host = g_strdup(relative_url->host); + base_url->port = relative_url->port; + + g_free(base_url->path); + base_url->path = NULL; + } + + if (relative_url->path) { + if (relative_url->path[0] == '/' || + base_url->path == NULL) { + g_free(base_url->path); + base_url->path = g_strdup(relative_url->path); + } else { + gchar *last_slash = strrchr(base_url->path, '/'); + gchar *tmp; + if (last_slash == NULL) + base_url->path[0] = '\0'; + else + last_slash[1] = '\0'; + tmp = base_url->path; + base_url->path = g_strconcat(base_url->path, + relative_url->path, NULL); + g_free(tmp); + } + } + + g_free(base_url->fragment); + base_url->fragment = g_strdup(relative_url->fragment); +} + +static gchar * purple_http_url_print(PurpleHttpURL *parsed_url) +{ + GString *url = g_string_new(""); + gboolean before_host_printed = FALSE, host_printed = FALSE; + gboolean port_is_default = FALSE; + + if (parsed_url->protocol) { + g_string_append_printf(url, "%s://", parsed_url->protocol); + before_host_printed = TRUE; + if (parsed_url->port == 80 && 0 == strcmp(parsed_url->protocol, + "http")) + port_is_default = TRUE; + if (parsed_url->port == 443 && 0 == strcmp(parsed_url->protocol, + "https")) + port_is_default = TRUE; + } + if (parsed_url->user || parsed_url->password) { + if (parsed_url->user) + g_string_append(url, parsed_url->user); + g_string_append_printf(url, ":%s", parsed_url->password); + g_string_append(url, "@"); + before_host_printed = TRUE; + } + if (parsed_url->host || parsed_url->port) { + if (!parsed_url->host) + g_string_append_printf(url, "{???}:%d", + parsed_url->port); + else { + g_string_append(url, parsed_url->host); + if (!port_is_default) + g_string_append_printf(url, ":%d", + parsed_url->port); + } + host_printed = TRUE; + } + if (parsed_url->path) { + if (!host_printed && before_host_printed) + g_string_append(url, "{???}"); + g_string_append(url, parsed_url->path); + } + if (parsed_url->fragment) + g_string_append_printf(url, "#%s", parsed_url->fragment); + + return g_string_free(url, FALSE); +} + +/*** HTTP Subsystem ***********************************************************/ + +void purple_http_init(void) +{ + purple_http_re_url = g_regex_new("^" + + "(?:" /* host part beginning */ + "([a-z]+)\\:/*" /* protocol */ + "([^/]+)" /* username, password, host, port */ + ")?" /* host part ending */ + + "([^#]*)" /* path */ + + "(?:#" "(.*)" ")?" /* fragment */ + + "$", G_REGEX_OPTIMIZE | G_REGEX_CASELESS, + G_REGEX_MATCH_NOTEMPTY, NULL); + + purple_http_re_url_host = g_regex_new("^" + + "(?:" /* user credentials part beginning */ + "([" PURPLE_HTTP_URL_CREDENTIALS_CHARS "]+)" /* username */ + "(?::([" PURPLE_HTTP_URL_CREDENTIALS_CHARS "]+))" /* password */ + "@)?" /* user credentials part ending */ + + "([a-z0-9.-]+)" /* host */ + "(?::([0-9]+))?" /* port*/ + + "$", G_REGEX_OPTIMIZE | G_REGEX_CASELESS, + G_REGEX_MATCH_NOTEMPTY, NULL); + + purple_http_re_rfc1123 = g_regex_new( + "^[a-z]+, " /* weekday */ + "([0-9]+) " /* date */ + "([a-z]+) " /* month */ + "([0-9]+) " /* year */ + "([0-9]+:[0-9]+:[0-9]+) " /* time */ + "(?:GMT|UTC)$", + G_REGEX_OPTIMIZE | G_REGEX_CASELESS, + G_REGEX_MATCH_NOTEMPTY, NULL); + + purple_http_hc_list = NULL; + purple_http_hc_by_ptr = g_hash_table_new(g_direct_hash, g_direct_equal); + purple_http_hc_by_gc = g_hash_table_new_full(g_direct_hash, + g_direct_equal, NULL, (GDestroyNotify)g_list_free); +} + +static void purple_http_foreach_conn_cancel(gpointer _hc, gpointer user_data) +{ + PurpleHttpConnection *hc = _hc; + purple_http_conn_cancel(hc); +} + +void purple_http_uninit(void) +{ + g_regex_unref(purple_http_re_url); + purple_http_re_url = NULL; + g_regex_unref(purple_http_re_url_host); + purple_http_re_url_host = NULL; + g_regex_unref(purple_http_re_rfc1123); + purple_http_re_rfc1123 = NULL; + + g_list_foreach(purple_http_hc_list, purple_http_foreach_conn_cancel, + NULL); + + if (purple_http_hc_list != NULL || + 0 != g_hash_table_size(purple_http_hc_by_ptr) || + 0 != g_hash_table_size(purple_http_hc_by_gc)) + purple_debug_warning("http", + "Couldn't cleanup all connections.\n"); + + g_list_free(purple_http_hc_list); + purple_http_hc_list = NULL; + g_hash_table_destroy(purple_http_hc_by_gc); + purple_http_hc_by_gc = NULL; + g_hash_table_destroy(purple_http_hc_by_ptr); + purple_http_hc_by_ptr = NULL; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/http.h Fri Jan 25 02:22:38 2013 -0500 @@ -0,0 +1,590 @@ +/** + * @file http.h HTTP API + * @ingroup core + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef _PURPLE_HTTP_H_ +#define _PURPLE_HTTP_H_ + +#include <glib.h> + +#include "connection.h" + +/** + * A structure containing all data required to generate a single HTTP request. + */ +typedef struct _PurpleHttpRequest PurpleHttpRequest; + +/** + * A representation of actually running HTTP request. Can be used to cancel the + * request. + */ +typedef struct _PurpleHttpConnection PurpleHttpConnection; + +/** + * All information got with response for HTTP request. + */ +typedef struct _PurpleHttpResponse PurpleHttpResponse; + +/** + * An collection of cookies, got from HTTP response or provided for HTTP + * request. + */ +typedef struct _PurpleHttpCookieJar PurpleHttpCookieJar; + +/** + * An callback called after performing (successfully or not) HTTP request. + */ +typedef void (*PurpleHttpCallback)(PurpleHttpConnection *http_conn, + PurpleHttpResponse *response, gpointer user_data); + +/** + * An callback called after storing data requested by PurpleHttpContentReader. + */ +typedef void (*PurpleHttpContentReaderCb)(PurpleHttpConnection *http_conn, + gboolean success, gboolean eof, size_t stored); + +/** + * An callback for getting large request contents (ie. from file stored on + * disk). + * + * @param http_conn Connection, which requests data. + * @param buffer Buffer to store data to (with offset ignored). + * @param offset Position, from where to read data. + * @param length Length of data to read. + * @param user_data The user data passed with callback function. + * @param cb The function to call after storing data to buffer. + */ +typedef void (*PurpleHttpContentReader)(PurpleHttpConnection *http_conn, + gchar *buffer, size_t offset, size_t length, gpointer user_data, + PurpleHttpContentReaderCb cb); + +/** + * An callback for writting large response contents. + * + * @param http_conn Connection, which requests data. + * @param response Response at point got so far (may change later). + * @param buffer Buffer to read data from (with offset ignored). + * @param offset Position of data got (its value is offset + length of + * previous call), can be safely ignored. + * @param length Length of data read. + * @param user_data The user data passed with callback function. + * @return TRUE, if succeeded, FALSE otherwise. + */ +typedef gboolean (*PurpleHttpContentWriter)(PurpleHttpConnection *http_conn, + PurpleHttpResponse *response, const gchar *buffer, size_t offset, + size_t length, gpointer user_data); + +/** + * An callback for watching HTTP connection progress. + * + * @param http_conn The HTTP Connection. + * @param reading_state FALSE, is we are sending the request, TRUE, when reading + * the response. + * @param processed The amount of data already processed. + * @param total Total amount of data (in current state). + * @param user_data The user data passed with callback function. + */ +typedef void (*PurpleHttpProgressWatcher)(PurpleHttpConnection *http_conn, + gboolean reading_state, int processed, int total, gpointer user_data); + +G_BEGIN_DECLS + +/**************************************************************************/ +/** @name Performing HTTP requests */ +/**************************************************************************/ +/*@{*/ + +/** + * Fetches the data from a URL with GET request, and passes it to a callback + * function. + * + * @param gc The connection for which the request is needed, or NULL. + * @param url The URL. + * @param callback The callback function. + * @param data The user data to pass to the callback function. + * @return The HTTP connection struct. + */ +PurpleHttpConnection * purple_http_get(PurpleConnection *gc, const gchar *url, + PurpleHttpCallback callback, gpointer user_data); + +/** + * Fetches a HTTP request and passes the response to a callback function. + * Provided request struct can be shared by multiple http requests but can not + * be modified when any of these is running. + * + * @param gc The connection for which the request is needed, or NULL. + * @param request The request. + * @param callback The callback function. + * @param user_data The user data to pass to the callback function. + * @return The HTTP connection struct. + */ +PurpleHttpConnection * purple_http_request(PurpleConnection *gc, + PurpleHttpRequest *request, PurpleHttpCallback callback, + gpointer user_data); + +/**************************************************************************/ +/** @name HTTP connection API */ +/**************************************************************************/ +/*@{*/ + +/** + * Cancel a pending HTTP request. + * + * @param http_conn The data returned when you initiated the HTTP request. + */ +void purple_http_conn_cancel(PurpleHttpConnection *http_conn); + +/** + * Cancels all HTTP connections associated with the specified handle. + * + * @param gc The handle. + */ +void purple_http_conn_cancel_all(PurpleConnection *gc); + +/** + * Checks, if provided HTTP request is running. + * + * @param http_conn The HTTP connection (may be invalid pointer). + * @return TRUE, if provided connection is currently running. + */ +gboolean purple_http_conn_is_running(PurpleHttpConnection *http_conn); + +/** + * Gets PurpleHttpRequest used for specified HTTP connection. + * + * @param http_conn The HTTP connection. + * @return The PurpleHttpRequest object. + */ +PurpleHttpRequest * purple_http_conn_get_request( + PurpleHttpConnection *http_conn); + +/** + * Gets cookie jar used within connection. + * + * @param http_conn The HTTP connection. + * @return The cookie jar. + */ +PurpleHttpCookieJar * purple_http_conn_get_cookie_jar( + PurpleHttpConnection *http_conn); + +/** + * Gets PurpleConnection tied with specified HTTP connection. + * + * @param http_conn The HTTP connection. + * @return The PurpleConnection object. + */ +PurpleConnection * purple_http_conn_get_purple_connection( + PurpleHttpConnection *http_conn); + +/** + * Sets the watcher, called after writing or reading data to/from HTTP stream. + * May be used for updating transfer progress gauge. + * + * @param http_conn The HTTP connection. + * @param watcher The watcher. + * @param user_data The user data to pass to the callback function. + * @param interval_threshold Minimum interval (in microseconds) of calls to + * watcher. + */ +void purple_http_conn_set_progress_watcher(PurpleHttpConnection *http_conn, + PurpleHttpProgressWatcher watcher, gpointer user_data, + guint interval_threshold); + +/*@}*/ + + +/**************************************************************************/ +/** @name Cookie jar API */ +/**************************************************************************/ +/*@{*/ + +/** + * Creates new cookie jar, + * + * @return empty cookie jar. + */ +PurpleHttpCookieJar * purple_http_cookie_jar_new(void); + +/** + * Increment the reference count. + * + * @param cookie_jar The cookie jar. + */ +void purple_http_cookie_jar_ref(PurpleHttpCookieJar *cookie_jar); + +/** + * Decrement the reference count. + * + * If the reference count reaches zero, the cookie jar will be freed. + * + * @param cookie_jar The cookie jar. + * @return @a cookie_jar or @c NULL if the reference count reached zero. + */ +PurpleHttpCookieJar * purple_http_cookie_jar_unref( + PurpleHttpCookieJar *cookie_jar); + +/** + * Sets the cookie. + * + * @param cookie_jar The cookie jar. + * @param name Cookie name. + * @param value Cookie contents. + */ +void purple_http_cookie_jar_set(PurpleHttpCookieJar *cookie_jar, + const gchar *name, const gchar *value); + +/** + * Gets the cookie. + * + * @param cookie_jar The cookie jar. + * @param name Cookie name. + * @return Cookie contents, or NULL, if cookie doesn't exists. + */ +const gchar * purple_http_cookie_jar_get(PurpleHttpCookieJar *cookie_jar, + const gchar *name); + +/** + * Checks, if the cookie jar contains any cookies. + * + * @param cookie_jar The cookie jar. + * @return TRUE, if cookie jar contains any cookie, FALSE otherwise. + */ +gboolean purple_http_cookie_jar_is_empty(PurpleHttpCookieJar *cookie_jar); + +/*@}*/ + + +/**************************************************************************/ +/** @name HTTP Request API */ +/**************************************************************************/ +/*@{*/ + +/** + * Creates the new instance of HTTP request configuration. + * + * @param url The URL to request for. + * @return The new instance of HTTP request struct. + */ +PurpleHttpRequest * purple_http_request_new(const gchar *url); + +/** + * Increment the reference count. + * + * @param request The request. + */ +void purple_http_request_ref(PurpleHttpRequest *request); + +/** + * Decrement the reference count. + * + * If the reference count reaches zero, the http request struct will be freed. + * + * @param request The request. + * @return @a request or @c NULL if the reference count reached zero. + */ +PurpleHttpRequest * purple_http_request_unref(PurpleHttpRequest *request); + +/** + * Sets URL for HTTP request. + * + * @param request The request. + * @param url The url. + */ +void purple_http_request_set_url(PurpleHttpRequest *request, const gchar *url); + +/** + * Gets URL set for the HTTP request. + * + * @param request The request. + * @return URL set for this request. + */ +const gchar * purple_http_request_get_url(PurpleHttpRequest *request); + +/** + * Sets custom HTTP method used for the request. + * + * @param request The request. + * @param method The method, or NULL for default. + */ +void purple_http_request_set_method(PurpleHttpRequest *request, + const gchar *method); + +/** + * Gets HTTP method set for the request. + * + * @param request The request. + * @return The method. + */ +const gchar * purple_http_request_get_method(PurpleHttpRequest *request); + +/** + * Sets contents of HTTP request (for example, POST data). + * + * @param request The request. + * @param contents The contents. + * @param length The length of contents (-1 if it's a NULL-terminated string) + */ +void purple_http_request_set_contents(PurpleHttpRequest *request, + const gchar *contents, int length); + +/** + * Sets contents reader for HTTP request, used mainly for possible large + * uploads. + * + * @param request The request. + * @param reader The reader callback. + * @param contents_size The size of all contents. + * @param user_data The user data to pass to the callback function. + */ +void purple_http_request_set_contents_reader(PurpleHttpRequest *request, + PurpleHttpContentReader reader, int contents_length, gpointer user_data); + +/** + * Set contents writer for HTTP response. + * + * @param request The request. + * @param reader The writer callback, or NULL to remove existing. + * @param user_data The user data to pass to the callback function. + */ +void purple_http_request_set_response_writer(PurpleHttpRequest *request, + PurpleHttpContentWriter writer, gpointer user_data); + +/** + * Set maximum amount of time, that request is allowed to run. + * + * @param request The request. + * @param timeout Time (in seconds) after that timeout will be cancelled, + * -1 for infinite time. + */ +void purple_http_request_set_timeout(PurpleHttpRequest *request, int timeout); + +/** + * Get maximum amount of time, that request is allowed to run. + * + * @param request The request. + * @return Timeout currently set (-1 for infinite). + */ +int purple_http_request_get_timeout(PurpleHttpRequest *request); + +/** + * Sets maximum amount of redirects. + * + * @param request The request. + * @param max_redirects Maximum amount of redirects, or -1 for unlimited. + */ +void purple_http_request_set_max_redirects(PurpleHttpRequest *request, + int max_redirects); + +/** + * Gets maximum amount of redirects. + * + * @param request The request. + * @return Current maximum amount of redirects (-1 for unlimited). + */ +int purple_http_request_get_max_redirects(PurpleHttpRequest *request); + +/** + * Sets cookie jar used for the request. + * + * @param request The request. + * @param cookie_jar The cookie jar. + */ +void purple_http_request_set_cookie_jar(PurpleHttpRequest *request, + PurpleHttpCookieJar *cookie_jar); + +/** + * Gets cookie jar used for the request. + * + * @param request The request. + * @return The cookie jar. + */ +PurpleHttpCookieJar * purple_http_request_get_cookie_jar( + PurpleHttpRequest *request); + +/** + * Sets HTTP version to use. + * + * @param request The request. + * @param http11 TRUE for HTTP/1.1, FALSE for HTTP/1.0. + */ +void purple_http_request_set_http11(PurpleHttpRequest *request, + gboolean http11); + +/** + * Gets used HTTP version. + * + * @param request The request. + * @return TRUE, if we use HTTP/1.1, FALSE for HTTP/1.0. + */ +gboolean purple_http_request_is_http11(PurpleHttpRequest *request); + +/** + * Sets maximum length of response content to read. + * + * Headers length doesn't count here. + * + * @param request The request. + * @param max_len Maximum length of response to read (-1 for unlimited). + */ +void purple_http_request_set_max_len(PurpleHttpRequest *request, int max_len); + +/** + * Gets maximum length of response content to read. + * + * @param request The request. + * @return Maximum length of response to read, or -1 if unlimited. + */ +int purple_http_request_get_max_len(PurpleHttpRequest *request); + +/** + * Sets (replaces, if exists) specified HTTP request header with provided value. + * + * @param key A header to be set. + * @param value A value to set, or NULL to remove specified header from request. + * + * @see purple_http_request_header_add + */ +void purple_http_request_header_set(PurpleHttpRequest *request, + const gchar *key, const gchar *value); + +void purple_http_request_header_set_printf(PurpleHttpRequest *request, + const gchar *key, const gchar *format, ...) G_GNUC_PRINTF(3, 4); + +/** + * Adds (without replacing, if exists) an HTTP request header. + * + * @param key A header to be set. + * @param value A value to set. + * + * @see purple_http_request_header_set + */ +void purple_http_request_header_add(PurpleHttpRequest *request, + const gchar *key, const gchar *value); + +/*@}*/ + +/**************************************************************************/ +/** @name HTTP response API */ +/**************************************************************************/ +/*@{*/ + +/** + * Checks, if HTTP request was performed successfully. + * + * @param response The response. + * @return TRUE, if request was performed successfully. + */ +gboolean purple_http_response_is_successfull(PurpleHttpResponse *response); + +/** + * Gets HTTP response code. + * + * @param response The response. + * @return HTTP response code. + */ +int purple_http_response_get_code(PurpleHttpResponse *response); + +/** + * Gets error description. + * + * @param response The response. + * @return Localized error description or NULL, if there was no error. + */ +const gchar * purple_http_response_get_error(PurpleHttpResponse *response); + +/** + * Gets HTTP response data length. + * + * @param response The response. + * @return Data length; + */ +gsize purple_http_response_get_data_len(PurpleHttpResponse *response); + +/** + * Gets HTTP response data. + * + * Response data is not written, if writer callback was set for request. + * + * @param response The response. + * @param len Return address for the size of the data. Can be NULL. + * @return The data. + */ +const gchar * purple_http_response_get_data(PurpleHttpResponse *response, size_t *len); + +/** + * Gets all headers got with response. + * + * @param response The response. + * @return GList of PurpleKeyValuePair, which keys are header field + * names (gchar*) and values are its contents (gchar*). + */ +const GList * purple_http_response_get_all_headers(PurpleHttpResponse *response); + +/** + * Gets all headers with specified name got with response. + * + * @param response The response. + * @param name The name of header field. + * @return GList of header field records contents (gchar*). + */ +const GList * purple_http_response_get_headers_by_name( + PurpleHttpResponse *response, const gchar *name); + +/** + * Gets one header contents with specified name got with response. + * + * To get all headers with the same name, use + * purple_http_response_get_headers_by_name instead. + * + * @param response The response. + * @param name The name of header field. + * @return Header field contents or NULL, if there is no such one. + */ +const gchar * purple_http_response_get_header(PurpleHttpResponse *response, + const gchar *name); + +/*@}*/ + + +/**************************************************************************/ +/** @name HTTP Subsystem */ +/**************************************************************************/ +/*@{*/ + +/** + * Initializes the http subsystem. + */ +void purple_http_init(void); + +/** + * Uninitializes the http subsystem. + */ +void purple_http_uninit(void); + +/*@}*/ + +G_END_DECLS + +#endif /* _PURPLE_HTTP_H_ */
--- a/libpurple/imgstore.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/imgstore.c Fri Jan 25 02:22:38 2013 -0500 @@ -36,20 +36,20 @@ static unsigned int nextid = 0; /* - * NOTE: purple_imgstore_add() creates these without zeroing the memory, so + * NOTE: purple_imgstore_new() creates these without zeroing the memory, so * NOTE: make sure to update that function when adding members. */ struct _PurpleStoredImage { int id; guint8 refcount; - size_t size; /**< The image data's size. */ - char *filename; /**< The filename (for the UI) */ - gpointer data; /**< The image data. */ + size_t size; /**< The image data's size. */ + char *filename; /**< The filename (for the UI) */ + gpointer data; /**< The image data. */ }; PurpleStoredImage * -purple_imgstore_add(gpointer data, size_t size, const char *filename) +purple_imgstore_new(gpointer data, size_t size, const char *filename) { PurpleStoredImage *img; @@ -82,30 +82,33 @@ g_error_free(err); return NULL; } - return purple_imgstore_add(data, len, path); + return purple_imgstore_new(data, len, path); } int -purple_imgstore_add_with_id(gpointer data, size_t size, const char *filename) +purple_imgstore_new_with_id(gpointer data, size_t size, const char *filename) { - PurpleStoredImage *img = purple_imgstore_add(data, size, filename); - if (img) { - /* - * Use the next unused id number. We do it in a loop on the - * off chance that nextid wraps back around to 0 and the hash - * table still contains entries from the first time around. - */ - do { - img->id = ++nextid; - } while (img->id == 0 || g_hash_table_lookup(imgstore, &(img->id)) != NULL); - - g_hash_table_insert(imgstore, &(img->id), img); + PurpleStoredImage *img = purple_imgstore_new(data, size, filename); + if (!img) { + return 0; } - return (img ? img->id : 0); + /* + * Use the next unused id number. We do it in a loop on the + * off chance that nextid wraps back around to 0 and the hash + * table still contains entries from the first time around. + */ + do { + img->id = ++nextid; + } while (img->id == 0 || g_hash_table_lookup(imgstore, &(img->id)) != NULL); + + g_hash_table_insert(imgstore, &(img->id), img); + + return img->id; } -PurpleStoredImage *purple_imgstore_find_by_id(int id) { +PurpleStoredImage *purple_imgstore_find_by_id(int id) +{ PurpleStoredImage *img = g_hash_table_lookup(imgstore, &id); if (img != NULL) @@ -114,7 +117,8 @@ return img; } -gconstpointer purple_imgstore_get_data(PurpleStoredImage *img) { +gconstpointer purple_imgstore_get_data(PurpleStoredImage *img) +{ g_return_val_if_fail(img != NULL, NULL); return img->data;
--- a/libpurple/imgstore.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/imgstore.h Fri Jan 25 02:22:38 2013 -0500 @@ -1,5 +1,6 @@ /** - * @file imgstore.h IM Image Store API + * @file imgstore.h Utility functions for reference-counted in-memory + * image data. * @ingroup core * @see @ref imgstore-signals */ @@ -39,66 +40,89 @@ G_BEGIN_DECLS /** - * Add an image to the store. + * Create a new PurpleStoredImage. * - * The caller owns a reference to the image in the store, and must dereference - * the image with purple_imgstore_unref() for it to be freed. + * The image is not added to the image store and no ID is assigned. If you + * need to reference the image by an ID, use purple_imgstore_new_with_id() + * instead. * - * No ID is allocated when using this function. If you need to reference the - * image by an ID, use purple_imgstore_add_with_id() instead. + * The caller owns a reference to this image and must dereference it with + * purple_imgstore_unref() for it to be freed. * - * @param data Pointer to the image data, which the imgstore will take - * ownership of and free as appropriate. If you want a - * copy of the data, make it before calling this function. - * @param size Image data's size. - * @param filename Filename associated with image. This is for your + * @param data Pointer to the image data, which the imgstore will take + * ownership of and free as appropriate. If you want a + * copy of the data, make it before calling this function. + * @param size Image data's size. + * @param filename Filename associated with image. This is for your * convenience. It could be the full path to the * image or, more commonly, the filename of the image * without any directory information. It can also be * NULL, if you don't need to keep track of a filename. + * If you intend to use this filename to write the file to + * disk, make sure the filename is appropriately escaped. + * You may wish to use purple_escape_filename(). * - * @return The stored image. + * @return The image, or NULL if the image could not be created (because of + * empty data or size). */ PurpleStoredImage * -purple_imgstore_add(gpointer data, size_t size, const char *filename); +purple_imgstore_new(gpointer data, size_t size, const char *filename); /** - * Create an image and add it to the store. + * Create a PurpleStoredImage using purple_imgstore_new() by reading the + * given filename from disk. + * + * The image is not added to the image store and no ID is assigned. If you + * need to reference the image by an ID, use purple_imgstore_new_with_id() + * instead. * - * @param path The path to the image. + * Make sure the filename is appropriately escaped. You may wish to use + * purple_escape_filename(). The PurpleStoredImage's filename will be set + * to the given path. * - * @return The stored image. + * The caller owns a reference to this image and must dereference it with + * purple_imgstore_unref() for it to be freed. + * + * @param path The path to the image. + * + * @return The image, or NULL if the image could not be created (because of + * empty data or size). */ PurpleStoredImage * purple_imgstore_new_from_file(const char *path); /** - * Add an image to the store, allocating an ID. - * - * The caller owns a reference to the image in the store, and must dereference - * the image with purple_imgstore_unref_by_id() or purple_imgstore_unref() - * for it to be freed. + * Create a PurpleStoredImage using purple_imgstore_new() and add the + * image to the image store. A unique ID will be assigned to the image. * - * @param data Pointer to the image data, which the imgstore will take - * ownership of and free as appropriate. If you want a - * copy of the data, make it before calling this function. - * @param size Image data's size. - * @param filename Filename associated with image. This is for your + * The caller owns a reference to the image and must dereference it with + * purple_imgstore_unref() or purple_imgstore_unref_by_id() for it to be + * freed. + * + * @param data Pointer to the image data, which the imgstore will take + * ownership of and free as appropriate. If you want a + * copy of the data, make it before calling this function. + * @param size Image data's size. + * @param filename Filename associated with image. This is for your * convenience. It could be the full path to the * image or, more commonly, the filename of the image * without any directory information. It can also be * NULL, if you don't need to keep track of a filename. - + * If you intend to use this filename to write the file to + * disk, make sure the filename is appropriately escaped. + * You may wish to use purple_escape_filename() + * * @return ID for the image. This is a unique number that can be used - * within libpurple to reference the image. + * within libpurple to reference the image. 0 is returned if the + * image could not be created (because of empty data or size). */ -int purple_imgstore_add_with_id(gpointer data, size_t size, const char *filename); +int purple_imgstore_new_with_id(gpointer data, size_t size, const char *filename); /** * Retrieve an image from the store. The caller does not own a * reference to the image. * - * @param id The ID for the image. + * @param id The ID for the image. * * @return A pointer to the requested image, or NULL if it was not found. */ @@ -107,7 +131,7 @@ /** * Retrieves a pointer to the image's data. * - * @param img The Image + * @param img The Image. * * @return A pointer to the data, which must not * be freed or modified. @@ -117,7 +141,7 @@ /** * Retrieves the length of the image's data. * - * @param img The Image + * @param img The Image. * * @return The size of the data that the pointer returned by * purple_imgstore_get_data points to. @@ -125,9 +149,12 @@ size_t purple_imgstore_get_size(PurpleStoredImage *img); /** - * Retrieves a pointer to the image's filename. + * Retrieves a pointer to the image's filename. If you intend to use this + * filename to write the file to disk, make sure the filename was + * appropriately escaped when you created the PurpleStoredImage. You may + * wish to use purple_escape_filename(). * - * @param img The image + * @param img The image. * * @return A pointer to the filename, which must not * be freed or modified. @@ -138,7 +165,7 @@ * Looks at the magic numbers of the image data (the first few bytes) * and returns an extension corresponding to the image's file type. * - * @param img The image. + * @param img The image. * * @return The image's extension (for example "png") or "icon" * if unknown. @@ -174,7 +201,7 @@ * purple_imgstore_ref(), so if you have a PurpleStoredImage, it'll * be more efficient to call purple_imgstore_ref() directly. * - * @param id The ID for the image. + * @param id The ID for the image. */ void purple_imgstore_ref_by_id(int id); @@ -185,7 +212,7 @@ * purple_imgstore_unref(), so if you have a PurpleStoredImage, it'll * be more efficient to call purple_imgstore_unref() directly. * - * @param id The ID for the image. + * @param id The ID for the image. */ void purple_imgstore_unref_by_id(int id);
--- a/libpurple/log.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/log.c Fri Jan 25 02:22:38 2013 -0500 @@ -835,7 +835,10 @@ fclose(image_file); /* Attempt to not leave half-written files around. */ - unlink(path); + if (g_unlink(path)) { + purple_debug_error("log", "Error deleting partial " + "file %s: %s\n", path, g_strerror(errno)); + } } else { @@ -1763,7 +1766,7 @@ index_tmp = g_strdup_printf("%s.XXXXXX", pathstr); if ((index_fd = g_mkstemp(index_tmp)) == -1) { purple_debug_error("log", "Failed to open index temp file: %s\n", - g_strerror(errno)); + g_strerror(errno)); g_free(pathstr); g_free(index_tmp); index = NULL; @@ -1771,7 +1774,7 @@ if ((index = fdopen(index_fd, "wb")) == NULL) { purple_debug_error("log", "Failed to fdopen() index temp file: %s\n", - g_strerror(errno)); + g_strerror(errno)); close(index_fd); if (index_tmp != NULL) { @@ -1827,7 +1830,6 @@ log->logger_data = data; list = g_list_prepend(list, log); - /* XXX: There is apparently Is there a proper way to print a time_t? */ if (index != NULL) fprintf(index, "%d\t%d\t%lu\n", data->offset, data->length, (unsigned long)log->time); } @@ -1887,9 +1889,8 @@ log->logger_data = data; list = g_list_prepend(list, log); - /* XXX: Is there a proper way to print a time_t? */ if (index != NULL) - fprintf(index, "%d\t%d\t%d\n", data->offset, data->length, (int)log->time); + fprintf(index, "%d\t%d\t%lu\n", data->offset, data->length, (unsigned long)log->time); } } @@ -2022,7 +2023,7 @@ /* Search the buddy list to find the account and to determine if this is a buddy. */ for (gnode = purple_blist_get_root(); !found && gnode != NULL; - gnode = purple_blist_node_get_sibling_next(gnode)) + gnode = purple_blist_node_get_sibling_next(gnode)) { if (!PURPLE_BLIST_NODE_IS_GROUP(gnode)) continue; @@ -2036,7 +2037,7 @@ for (bnode = purple_blist_node_get_first_child(cnode); !found && bnode != NULL; - bnode = purple_blist_node_get_sibling_next(bnode)) + bnode = purple_blist_node_get_sibling_next(bnode)) { PurpleBuddy *buddy = (PurpleBuddy *)bnode;
--- a/libpurple/media/backend-fs2.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/media/backend-fs2.c Fri Jan 25 02:22:38 2013 -0500 @@ -179,9 +179,17 @@ purple_media_backend_fs2_init(PurpleMediaBackendFs2 *self) {} +#if GST_CHECK_VERSION(1,0,0) +static GstPadProbeReturn +event_probe_cb(GstPad *srcpad, GstPadProbeInfo *info, gpointer unused) +#else static gboolean event_probe_cb(GstPad *srcpad, GstEvent *event, gboolean release_pad) +#endif { +#if GST_CHECK_VERSION(1,0,0) + GstEvent *event = GST_PAD_PROBE_INFO_EVENT(info); +#endif if (GST_EVENT_TYPE(event) == GST_EVENT_CUSTOM_DOWNSTREAM && gst_event_has_name(event, "purple-unlink-tee")) { @@ -189,22 +197,40 @@ gst_pad_unlink(srcpad, gst_pad_get_peer(srcpad)); +#if GST_CHECK_VERSION(1,0,0) + gst_pad_remove_probe(srcpad, + g_value_get_ulong(gst_structure_get_value(s, "handler-id"))); +#else gst_pad_remove_event_probe(srcpad, g_value_get_uint(gst_structure_get_value(s, "handler-id"))); +#endif if (g_value_get_boolean(gst_structure_get_value(s, "release-pad"))) gst_element_release_request_pad(GST_ELEMENT_PARENT(srcpad), srcpad); +#if GST_CHECK_VERSION(1,0,0) + return GST_PAD_PROBE_DROP; +#else return FALSE; +#endif } +#if GST_CHECK_VERSION(1,0,0) + return GST_PAD_PROBE_OK; +#else return TRUE; +#endif } static void unlink_teepad_dynamic(GstPad *srcpad, gboolean release_pad) { +#if GST_CHECK_VERSION(1,0,0) + gulong id = gst_pad_add_probe(srcpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, + event_probe_cb, NULL, NULL); +#else guint id = gst_pad_add_event_probe(srcpad, G_CALLBACK(event_probe_cb), NULL); +#endif if (GST_IS_GHOST_PAD(srcpad)) srcpad = gst_ghost_pad_get_target(GST_GHOST_PAD(srcpad)); @@ -213,7 +239,11 @@ gst_event_new_custom(GST_EVENT_CUSTOM_DOWNSTREAM, gst_structure_new("purple-unlink-tee", "release-pad", G_TYPE_BOOLEAN, release_pad, +#if GST_CHECK_VERSION(1,0,0) + "handler-id", G_TYPE_ULONG, id, +#else "handler-id", G_TYPE_UINT, id, +#endif NULL))); } @@ -793,9 +823,12 @@ gdouble value_db; gdouble percent; - list = gst_structure_get_value( - gst_message_get_structure(msg), value_name); + list = gst_structure_get_value(gst_message_get_structure(msg), value_name); +#if GST_CHECK_VERSION(1,0,0) + value = g_value_array_get_nth(g_value_get_boxed(list), 0); +#else value = gst_value_list_get_value(list, 0); +#endif value_db = g_value_get_double(value); percent = pow(10, value_db / 20); return (percent > 1.0) ? 1.0 : percent; @@ -809,11 +842,12 @@ PURPLE_MEDIA_BACKEND_FS2_GET_PRIVATE(self); GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg)); static guint level_id = 0; + const GstStructure *structure = gst_message_get_structure(msg); if (level_id == 0) level_id = g_signal_lookup("level", PURPLE_TYPE_MEDIA); - if (gst_structure_has_name(msg->structure, "level")) { + if (gst_structure_has_name(structure, "level")) { GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg)); gchar *name; gchar *participant = NULL; @@ -868,12 +902,12 @@ return; #ifdef HAVE_FARSIGHT - if (gst_structure_has_name(msg->structure, "farsight-error")) { + if (gst_structure_has_name(structure, "farsight-error")) { #else - if (gst_structure_has_name(msg->structure, "farstream-error")) { + if (gst_structure_has_name(structure, "farstream-error")) { #endif FsError error_no; - gst_structure_get_enum(msg->structure, "error-no", + gst_structure_get_enum(structure, "error-no", FS_TYPE_ERROR, (gint*)&error_no); switch (error_no) { case FS_ERROR_NO_CODECS: @@ -910,7 +944,7 @@ #endif error_no, gst_structure_get_string( - msg->structure, "error-msg")); + structure, "error-msg")); break; } @@ -924,7 +958,7 @@ #endif purple_media_end(priv->media, NULL, NULL); } - } else if (gst_structure_has_name(msg->structure, + } else if (gst_structure_has_name(structure, #ifdef HAVE_FARSIGHT "farsight-new-local-candidate")) { #else @@ -939,9 +973,9 @@ PurpleMediaBackendFs2Stream *media_stream; gchar *name; - value = gst_structure_get_value(msg->structure, "stream"); + value = gst_structure_get_value(structure, "stream"); stream = g_value_get_object(value); - value = gst_structure_get_value(msg->structure, "candidate"); + value = gst_structure_get_value(structure, "candidate"); local_candidate = g_value_get_boxed(value); session = get_session_from_fs_stream(self, stream); @@ -963,7 +997,7 @@ g_signal_emit_by_name(self, "new-candidate", session->id, name, candidate); g_object_unref(candidate); - } else if (gst_structure_has_name(msg->structure, + } else if (gst_structure_has_name(structure, #ifdef HAVE_FARSIGHT "farsight-local-candidates-prepared")) { #else @@ -975,7 +1009,7 @@ PurpleMediaBackendFs2Session *session; gchar *name; - value = gst_structure_get_value(msg->structure, "stream"); + value = gst_structure_get_value(structure, "stream"); stream = g_value_get_object(value); session = get_session_from_fs_stream(self, stream); @@ -985,7 +1019,7 @@ g_signal_emit_by_name(self, "candidates-prepared", session->id, name); - } else if (gst_structure_has_name(msg->structure, + } else if (gst_structure_has_name(structure, #ifdef HAVE_FARSIGHT "farsight-new-active-candidate-pair")) { #else @@ -1000,13 +1034,11 @@ PurpleMediaCandidate *lcandidate, *rcandidate; gchar *name; - value = gst_structure_get_value(msg->structure, "stream"); + value = gst_structure_get_value(structure, "stream"); stream = g_value_get_object(value); - value = gst_structure_get_value(msg->structure, - "local-candidate"); + value = gst_structure_get_value(structure, "local-candidate"); local_candidate = g_value_get_boxed(value); - value = gst_structure_get_value(msg->structure, - "remote-candidate"); + value = gst_structure_get_value(structure, "remote-candidate"); remote_candidate = g_value_get_boxed(value); g_object_get(stream, "participant", &participant, NULL); @@ -1023,7 +1055,7 @@ g_object_unref(lcandidate); g_object_unref(rcandidate); - } else if (gst_structure_has_name(msg->structure, + } else if (gst_structure_has_name(structure, #ifdef HAVE_FARSIGHT "farsight-recv-codecs-changed")) { #else @@ -1033,7 +1065,7 @@ GList *codecs; FsCodec *codec; - value = gst_structure_get_value(msg->structure, "codecs"); + value = gst_structure_get_value(structure, "codecs"); codecs = g_value_get_boxed(value); codec = codecs->data; @@ -1044,7 +1076,7 @@ "farstream-recv-codecs-changed: %s\n", #endif codec->encoding_name); - } else if (gst_structure_has_name(msg->structure, + } else if (gst_structure_has_name(structure, #ifdef HAVE_FARSIGHT "farsight-component-state-changed")) { #else @@ -1055,9 +1087,9 @@ guint component; const gchar *state; - value = gst_structure_get_value(msg->structure, "state"); + value = gst_structure_get_value(structure, "state"); fsstate = g_value_get_enum(value); - value = gst_structure_get_value(msg->structure, "component"); + value = gst_structure_get_value(structure, "component"); component = g_value_get_uint(value); switch (fsstate) { @@ -1092,7 +1124,7 @@ #endif "component: %u state: %s\n", component, state); - } else if (gst_structure_has_name(msg->structure, + } else if (gst_structure_has_name(structure, #ifdef HAVE_FARSIGHT "farsight-send-codec-changed")) { #else @@ -1102,7 +1134,7 @@ FsCodec *codec; gchar *codec_str; - value = gst_structure_get_value(msg->structure, "codec"); + value = gst_structure_get_value(structure, "codec"); codec = g_value_get_boxed(value); codec_str = fs_codec_to_string(codec); @@ -1115,7 +1147,7 @@ codec_str); g_free(codec_str); - } else if (gst_structure_has_name(msg->structure, + } else if (gst_structure_has_name(structure, #ifdef HAVE_FARSIGHT "farsight-codecs-changed")) { #else @@ -1125,7 +1157,7 @@ FsSession *fssession; GList *sessions; - value = gst_structure_get_value(msg->structure, "session"); + value = gst_structure_get_value(structure, "session"); fssession = g_value_get_object(value); sessions = g_hash_table_get_values(priv->sessions); @@ -1569,7 +1601,11 @@ srcpad = gst_element_get_static_pad(session->srcvalve, "src"); g_object_set(volume, "volume", input_volume, NULL); } else { +#if GST_CHECK_VERSION(1,0,0) + srcpad = gst_element_get_request_pad(session->tee, "src_%u"); +#else srcpad = gst_element_get_request_pad(session->tee, "src%d"); +#endif } purple_debug_info("backend-fs2", "connecting pad: %s\n", @@ -1787,14 +1823,10 @@ * audioresample ! audioconvert ! realsink */ stream->queue = gst_element_factory_make("queue", NULL); - stream->volume = gst_element_factory_make( - "volume", NULL); - g_object_set(stream->volume, "volume", - output_volume, NULL); - stream->level = gst_element_factory_make( - "level", NULL); - stream->src = gst_element_factory_make( - "liveadder", NULL); + stream->volume = gst_element_factory_make("volume", NULL); + g_object_set(stream->volume, "volume", output_volume, NULL); + stream->level = gst_element_factory_make("level", NULL); + stream->src = gst_element_factory_make("liveadder", NULL); sink = purple_media_manager_get_element( purple_media_get_manager(priv->media), PURPLE_MEDIA_RECV_AUDIO, priv->media, @@ -1813,10 +1845,12 @@ gst_element_link(stream->queue, stream->volume); sink = stream->queue; } else if (codec->media_type == FS_MEDIA_TYPE_VIDEO) { - stream->src = gst_element_factory_make( - "fsfunnel", NULL); - sink = gst_element_factory_make( - "fakesink", NULL); +#if GST_CHECK_VERSION(1,0,0) + stream->src = gst_element_factory_make("funnel", NULL); +#else + stream->src = gst_element_factory_make("fsfunnel", NULL); +#endif + sink = gst_element_factory_make("fakesink", NULL); g_object_set(G_OBJECT(sink), "async", FALSE, NULL); gst_bin_add(GST_BIN(priv->confbin), sink); gst_element_set_state(sink, GST_STATE_PLAYING); @@ -1830,7 +1864,11 @@ gst_element_link_many(stream->src, stream->tee, sink, NULL); } +#if GST_CHECK_VERSION(1,0,0) + sinkpad = gst_element_get_request_pad(stream->src, "sink_%u"); +#else sinkpad = gst_element_get_request_pad(stream->src, "sink%d"); +#endif gst_pad_link(srcpad, sinkpad); gst_object_unref(sinkpad);
--- a/libpurple/mediamanager.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/mediamanager.c Fri Jan 25 02:22:38 2013 -0500 @@ -44,7 +44,11 @@ #else #include <farstream/fs-element-added-notifier.h> #endif +#if GST_CHECK_VERSION(1,0,0) +#include <gst/video/videooverlay.h> +#else #include <gst/interfaces/xoverlay.h> +#endif /** @copydoc _PurpleMediaManagerPrivate */ typedef struct _PurpleMediaManagerPrivate PurpleMediaManagerPrivate; @@ -270,8 +274,11 @@ gst_bus_add_signal_watch(GST_BUS(bus)); g_signal_connect(G_OBJECT(bus), "message", G_CALLBACK(pipeline_bus_call), manager); - gst_bus_set_sync_handler(bus, - gst_bus_sync_signal_handler, NULL); +#if GST_CHECK_VERSION(1,0,0) + gst_bus_set_sync_handler(bus, gst_bus_sync_signal_handler, NULL, NULL); +#else + gst_bus_set_sync_handler(bus, gst_bus_sync_signal_handler, NULL); +#endif gst_object_unref(bus); filename = g_build_filename(purple_user_dir(), @@ -401,20 +408,31 @@ { GstElement *parent = GST_ELEMENT_PARENT(pad); GstIterator *iter; +#if GST_CHECK_VERSION(1,0,0) + GValue tmp = G_VALUE_INIT; +#endif GstPad *remaining_pad; GstIteratorResult result; - gst_element_release_request_pad(GST_ELEMENT_PARENT(pad), pad); + gst_element_release_request_pad(parent, pad); iter = gst_element_iterate_src_pads(parent); +#if GST_CHECK_VERSION(1,0,0) + result = gst_iterator_next(iter, &tmp); +#else result = gst_iterator_next(iter, (gpointer)&remaining_pad); +#endif if (result == GST_ITERATOR_DONE) { gst_element_set_locked_state(parent, TRUE); gst_element_set_state(parent, GST_STATE_NULL); gst_bin_remove(GST_BIN(GST_ELEMENT_PARENT(parent)), parent); } else if (result == GST_ITERATOR_OK) { +#if GST_CHECK_VERSION(1,0,0) + remaining_pad = g_value_get_object(&tmp); + g_value_reset(&tmp); +#endif gst_object_unref(remaining_pad); } @@ -452,7 +470,11 @@ { #ifdef USE_VV if (manager->priv->video_caps == NULL) +#if GST_CHECK_VERSION(1,0,0) + manager->priv->video_caps = gst_caps_from_string("video/x-raw," +#else manager->priv->video_caps = gst_caps_from_string("video/x-raw-yuv," +#endif "width=[250,352], height=[200,288], framerate=[1/1,20/1]"); return manager->priv->video_caps; #else @@ -535,7 +557,11 @@ g_free(id); tee = gst_bin_get_by_name(GST_BIN(ret), "tee"); +#if GST_CHECK_VERSION(1,0,0) + pad = gst_element_get_request_pad(tee, "src_%u"); +#else pad = gst_element_get_request_pad(tee, "src%d"); +#endif gst_object_unref(tee); ghost = gst_ghost_pad_new(NULL, pad); gst_object_unref(pad); @@ -726,9 +752,12 @@ { GstElement *sink; - if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT || - !gst_structure_has_name(msg->structure, - "prepare-xwindow-id")) + if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT +#if GST_CHECK_VERSION(1,0,0) + || !gst_is_video_overlay_prepare_window_handle_message(msg)) +#else + || !gst_structure_has_name(msg->structure, "prepare-xwindow-id")) +#endif return; sink = GST_ELEMENT(GST_MESSAGE_SRC(msg)); @@ -742,7 +771,10 @@ | G_SIGNAL_MATCH_DATA, 0, 0, NULL, window_id_cb, ow); -#if GST_CHECK_VERSION(0,10,31) +#if GST_CHECK_VERSION(1,0,0) + gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(msg)), + ow->window_id); +#elif GST_CHECK_VERSION(0,10,31) gst_x_overlay_set_window_handle(GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), ow->window_id); #else @@ -773,17 +805,19 @@ (participant == ow->participant)) && !strcmp(session_id, ow->session_id)) { GstBus *bus; - GstElement *queue, *colorspace; + GstElement *queue, *convert; GstElement *tee = purple_media_get_tee(media, session_id, participant); if (tee == NULL) continue; - queue = gst_element_factory_make( - "queue", NULL); - colorspace = gst_element_factory_make( - "ffmpegcolorspace", NULL); + queue = gst_element_factory_make("queue", NULL); +#if GST_CHECK_VERSION(1,0,0) + convert = gst_element_factory_make("videoconvert", NULL); +#else + convert = gst_element_factory_make("ffmpegcolorspace", NULL); +#endif ow->sink = purple_media_manager_get_element( manager, PURPLE_MEDIA_RECV_VIDEO, ow->media, ow->session_id, @@ -804,7 +838,7 @@ } gst_bin_add_many(GST_BIN(GST_ELEMENT_PARENT(tee)), - queue, colorspace, ow->sink, NULL); + queue, convert, ow->sink, NULL); bus = gst_pipeline_get_bus(GST_PIPELINE( manager->priv->pipeline)); @@ -813,10 +847,10 @@ gst_object_unref(bus); gst_element_set_state(ow->sink, GST_STATE_PLAYING); - gst_element_set_state(colorspace, GST_STATE_PLAYING); + gst_element_set_state(convert, GST_STATE_PLAYING); gst_element_set_state(queue, GST_STATE_PLAYING); - gst_element_link(colorspace, ow->sink); - gst_element_link(queue, colorspace); + gst_element_link(convert, ow->sink); + gst_element_link(queue, convert); gst_element_link(tee, queue); } }
--- a/libpurple/notify.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/notify.c Fri Jan 25 02:22:38 2013 -0500 @@ -818,6 +818,9 @@ purple_value_new(PURPLE_TYPE_POINTER), purple_value_new(PURPLE_TYPE_UINT)); + purple_signal_register(handle, "displaying-emails-clear", + purple_marshal_VOID, NULL, 0); + purple_signal_register(handle, "displaying-userinfo", purple_marshal_VOID__POINTER_POINTER_POINTER, NULL, 3, purple_value_new(PURPLE_TYPE_SUBTYPE,
--- a/libpurple/ntlm.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/ntlm.c Fri Jan 25 02:22:38 2013 -0500 @@ -220,16 +220,39 @@ des_ecb_encrypt(plaintext, results + 16, key); } +/* + * TODO: We think we should be using cryptographically secure random numbers + * here. We think the rand() function is probably bad. We think + * /dev/urandom is a step up, but using a random function from an SSL + * library would probably be best. In Windows we could possibly also + * use CryptGenRandom. + */ static void -gensesskey(char *buffer, const char *oldkey) +gensesskey(char *buffer) { - int i = 0; - if(oldkey == NULL) { - for(i=0; i<16; i++) { - buffer[i] = (char)(rand() & 0xff); + int fd; + int i; + ssize_t red = 0; + + fd = open("/dev/urandom", O_RDONLY); + if (fd >= 0) { + red = read(fd, buffer, 16); + if (red < 0) { + purple_debug_warning("ntlm", "Error reading from /dev/urandom: %s." + " Falling back to inferior method.\n", g_strerror(errno)); + red = 0; + } else if (red < 16) { + purple_debug_warning("ntlm", "Tried reading 16 bytes from " + "/dev/urandom but only got %zd. Falling back to " + "inferior method\n", red); } } else { - memcpy(buffer, oldkey, 16); + purple_debug_warning("ntlm", "Error opening /dev/urandom: %s." + " Falling back to inferior method.\n", g_strerror(errno)); + } + + for (i = red; i < 16; i++) { + buffer[i] = (char)(rand() & 0xff); } } @@ -366,7 +389,7 @@ /* LCS Stuff */ if (flags) { tmsg->flags = GUINT32_TO_LE(0x409082d4); - gensesskey(sesskey, NULL); + gensesskey(sesskey); memcpy(tmp, sesskey, 0x10); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/obsolete.c Fri Jan 25 02:22:38 2013 -0500 @@ -0,0 +1,776 @@ +/* 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 "obsolete.h" + +#include "internal.h" +#include "debug.h" +#include "http.h" +#include "ntlm.h" + +struct _PurpleUtilFetchUrlData +{ + PurpleUtilFetchUrlCallback callback; + void *user_data; + + struct + { + char *user; + char *passwd; + char *address; + int port; + char *page; + + } website; + + char *url; + int num_times_redirected; + gboolean full; + char *user_agent; + gboolean http11; + char *request; + gsize request_written; + gboolean include_headers; + + gboolean is_ssl; + PurpleSslConnection *ssl_connection; + PurpleProxyConnectData *connect_data; + int fd; + guint inpa; + + gboolean got_headers; + gboolean has_explicit_data_len; + char *webdata; + gsize len; + unsigned long data_len; + gssize max_len; + gboolean chunked; + PurpleAccount *account; + + PurpleHttpConnection *wrapped_request; + gboolean cancelled; +}; + +typedef struct +{ + PurpleUtilFetchUrlData *url_data; + PurpleUtilFetchUrlCallback cb; + gpointer user_data; +} PurpleUtilLegacyWrapData; + +static void purple_util_fetch_url_cb(PurpleHttpConnection *http_conn, + PurpleHttpResponse *response, gpointer _wrap_data) +{ + PurpleUtilLegacyWrapData *wrap_data = _wrap_data; + const char *data = NULL; + size_t len; + + if (wrap_data->cb && !wrap_data->url_data->cancelled) { + data = purple_http_response_get_data(response, &len); + + wrap_data->cb(wrap_data->url_data, wrap_data->user_data, data, len, + purple_http_response_get_error(response)); + } + + g_free(wrap_data->url_data); + g_free(wrap_data); +} + +PurpleUtilFetchUrlData * purple_util_fetch_url(const gchar *url, gboolean full, + const gchar *user_agent, gboolean http11, gssize max_len, + PurpleUtilFetchUrlCallback cb, gpointer data) +{ + PurpleHttpRequest *request; + PurpleUtilFetchUrlData *url_data; + PurpleUtilLegacyWrapData *wrap_data; + + if (FALSE) + return purple_util_fetch_url_request(NULL, url, full, + user_agent, http11, NULL, FALSE, max_len, cb, data); + + wrap_data = g_new0(PurpleUtilLegacyWrapData, 1); + url_data = g_new0(PurpleUtilFetchUrlData, 1); + request = purple_http_request_new(url); + + wrap_data->url_data = url_data; + wrap_data->cb = cb; + wrap_data->user_data = data; + + if (user_agent) + purple_http_request_header_set(request, + "User-Agent", user_agent); + purple_http_request_set_http11(request, http11); + purple_http_request_set_max_len(request, max_len); + + url_data->wrapped_request = purple_http_request(NULL, request, + purple_util_fetch_url_cb, wrap_data); + + purple_http_request_unref(request); + + return url_data; +} + +/** + * The arguments to this function are similar to printf. + */ +static void +purple_util_fetch_url_error(PurpleUtilFetchUrlData *gfud, const char *format, ...) +{ + gchar *error_message; + va_list args; + + va_start(args, format); + error_message = g_strdup_vprintf(format, args); + va_end(args); + + gfud->callback(gfud, gfud->user_data, NULL, 0, error_message); + g_free(error_message); + purple_util_fetch_url_cancel(gfud); +} + +static void url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message); +static void ssl_url_fetch_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond); +static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data); + +static gboolean +parse_redirect(const char *data, size_t data_len, + PurpleUtilFetchUrlData *gfud) +{ + gchar *s; + gchar *new_url, *temp_url, *end; + gboolean full; + int len; + + if ((s = g_strstr_len(data, data_len, "\nLocation: ")) == NULL) + /* We're not being redirected */ + return FALSE; + + s += strlen("Location: "); + end = strchr(s, '\r'); + + /* Just in case :) */ + if (end == NULL) + end = strchr(s, '\n'); + + if (end == NULL) + return FALSE; + + len = end - s; + + new_url = g_malloc(len + 1); + strncpy(new_url, s, len); + new_url[len] = '\0'; + + full = gfud->full; + + if (*new_url == '/' || g_strstr_len(new_url, len, "://") == NULL) + { + temp_url = new_url; + + new_url = g_strdup_printf("%s:%d%s", gfud->website.address, + gfud->website.port, temp_url); + + g_free(temp_url); + + full = FALSE; + } + + purple_debug_info("util", "Redirecting to %s\n", new_url); + + gfud->num_times_redirected++; + if (gfud->num_times_redirected >= 5) + { + purple_util_fetch_url_error(gfud, + _("Could not open %s: Redirected too many times"), + gfud->url); + return TRUE; + } + + /* + * Try again, with this new location. This code is somewhat + * ugly, but we need to reuse the gfud because whoever called + * us is holding a reference to it. + */ + g_free(gfud->url); + gfud->url = new_url; + gfud->full = full; + g_free(gfud->request); + gfud->request = NULL; + + if (gfud->is_ssl) { + gfud->is_ssl = FALSE; + purple_ssl_close(gfud->ssl_connection); + gfud->ssl_connection = NULL; + } else { + purple_input_remove(gfud->inpa); + gfud->inpa = 0; + close(gfud->fd); + gfud->fd = -1; + } + gfud->request_written = 0; + gfud->len = 0; + gfud->data_len = 0; + + g_free(gfud->website.user); + g_free(gfud->website.passwd); + g_free(gfud->website.address); + g_free(gfud->website.page); + purple_url_parse(new_url, &gfud->website.address, &gfud->website.port, + &gfud->website.page, &gfud->website.user, &gfud->website.passwd); + + if (purple_strcasestr(new_url, "https://") != NULL) { + gfud->is_ssl = TRUE; + gfud->ssl_connection = purple_ssl_connect(gfud->account, + gfud->website.address, gfud->website.port, + ssl_url_fetch_connect_cb, ssl_url_fetch_error_cb, gfud); + } else { + gfud->connect_data = purple_proxy_connect(NULL, gfud->account, + gfud->website.address, gfud->website.port, + url_fetch_connect_cb, gfud); + } + + if (gfud->ssl_connection == NULL && gfud->connect_data == NULL) + { + purple_util_fetch_url_error(gfud, _("Unable to connect to %s"), + gfud->website.address); + } + + return TRUE; +} + +static const char * +find_header_content(const char *data, size_t data_len, const char *header, size_t header_len) +{ + const char *p = NULL; + + if (header_len <= 0) + header_len = strlen(header); + + /* Note: data is _not_ nul-terminated. */ + if (data_len > header_len) { + if (header[0] == '\n') + p = (g_ascii_strncasecmp(data, header + 1, header_len - 1) == 0) ? data : NULL; + if (!p) + p = purple_strcasestr(data, header); + if (p) + p += header_len; + } + + /* If we can find the header at all, try to sscanf it. + * Response headers should end with at least \r\n, so sscanf is safe, + * if we make sure that there is indeed a \n in our header. + */ + if (p && g_strstr_len(p, data_len - (p - data), "\n")) { + return p; + } + + return NULL; +} + +static size_t +parse_content_len(const char *data, size_t data_len) +{ + size_t content_len = 0; + const char *p = NULL; + + p = find_header_content(data, data_len, "\nContent-Length: ", sizeof("\nContent-Length: ") - 1); + if (p) { + sscanf(p, "%" G_GSIZE_FORMAT, &content_len); + purple_debug_misc("util", "parsed %" G_GSIZE_FORMAT "\n", content_len); + } + + return content_len; +} + +static gboolean +content_is_chunked(const char *data, size_t data_len) +{ + const char *p = find_header_content(data, data_len, "\nTransfer-Encoding: ", sizeof("\nTransfer-Encoding: ") - 1); + if (p && g_ascii_strncasecmp(p, "chunked", 7) == 0) + return TRUE; + + return FALSE; +} + +/* Process in-place */ +static void +process_chunked_data(char *data, gsize *len) +{ + gsize sz; + gsize newlen = 0; + char *p = data; + char *s = data; + + while (*s) { + /* Read the size of this chunk */ + if (sscanf(s, "%" G_GSIZE_MODIFIER "x", &sz) != 1) + { + purple_debug_error("util", "Error processing chunked data: " + "Expected data length, found: %s\n", s); + break; + } + if (sz == 0) { + /* We've reached the last chunk */ + /* + * TODO: The spec allows "footers" to follow the last chunk. + * If there is more data after this line then we should + * treat it like a header. + */ + break; + } + + /* Advance to the start of the data */ + s = strstr(s, "\r\n"); + if (s == NULL) + break; + s += 2; + + if (s + sz > data + *len) { + purple_debug_error("util", "Error processing chunked data: " + "Chunk size %" G_GSIZE_FORMAT " bytes was longer " + "than the data remaining in the buffer (%" + G_GSIZE_FORMAT " bytes)\n", sz, data + *len - s); + } + + /* Move all data overtop of the chunk length that we read in earlier */ + g_memmove(p, s, sz); + p += sz; + s += sz; + newlen += sz; + if (*s != '\r' && *(s + 1) != '\n') { + purple_debug_error("util", "Error processing chunked data: " + "Expected \\r\\n, found: %s\n", s); + break; + } + s += 2; + } + + /* NULL terminate the data */ + *p = 0; + + *len = newlen; +} + +static void +url_fetch_recv_cb(gpointer url_data, gint source, PurpleInputCondition cond) +{ + PurpleUtilFetchUrlData *gfud = url_data; + int len; + char buf[4096]; + char *data_cursor; + gboolean got_eof = FALSE; + + /* + * Read data in a loop until we can't read any more! This is a + * little confusing because we read using a different function + * depending on whether the socket is ssl or cleartext. + */ + while ((gfud->is_ssl && ((len = purple_ssl_read(gfud->ssl_connection, buf, sizeof(buf))) > 0)) || + (!gfud->is_ssl && (len = read(source, buf, sizeof(buf))) > 0)) + { + if(gfud->max_len != -1 && (gfud->len + len) > gfud->max_len) { + purple_util_fetch_url_error(gfud, _("Error reading from %s: response too long (%d bytes limit)"), + gfud->website.address, gfud->max_len); + return; + } + + /* If we've filled up our buffer, make it bigger */ + if((gfud->len + len) >= gfud->data_len) { + while((gfud->len + len) >= gfud->data_len) + gfud->data_len += sizeof(buf); + + gfud->webdata = g_realloc(gfud->webdata, gfud->data_len); + } + + data_cursor = gfud->webdata + gfud->len; + + gfud->len += len; + + memcpy(data_cursor, buf, len); + + gfud->webdata[gfud->len] = '\0'; + + if(!gfud->got_headers) { + char *end_of_headers; + + /* See if we've reached the end of the headers yet */ + end_of_headers = strstr(gfud->webdata, "\r\n\r\n"); + if (end_of_headers) { + char *new_data; + guint header_len = (end_of_headers + 4 - gfud->webdata); + size_t content_len; + + purple_debug_misc("util", "Response headers: '%.*s'\n", + header_len, gfud->webdata); + + /* See if we can find a redirect. */ + if(parse_redirect(gfud->webdata, header_len, gfud)) + return; + + gfud->got_headers = TRUE; + + /* No redirect. See if we can find a content length. */ + content_len = parse_content_len(gfud->webdata, header_len); + gfud->chunked = content_is_chunked(gfud->webdata, header_len); + + if (content_len == 0) { + /* We'll stick with an initial 8192 */ + content_len = 8192; + } else { + gfud->has_explicit_data_len = TRUE; + } + + + /* If we're returning the headers too, we don't need to clean them out */ + if (gfud->include_headers) { + gfud->data_len = content_len + header_len; + gfud->webdata = g_realloc(gfud->webdata, gfud->data_len); + } else { + size_t body_len = gfud->len - header_len; + + content_len = MAX(content_len, body_len); + + new_data = g_try_malloc(content_len); + if (new_data == NULL) { + purple_debug_error("util", + "Failed to allocate %" G_GSIZE_FORMAT " bytes: %s\n", + content_len, g_strerror(errno)); + purple_util_fetch_url_error(gfud, + _("Unable to allocate enough memory to hold " + "the contents from %s. The web server may " + "be trying something malicious."), + gfud->website.address); + + return; + } + + /* We may have read part of the body when reading the headers, don't lose it */ + if (body_len > 0) { + memcpy(new_data, end_of_headers + 4, body_len); + } + + /* Out with the old... */ + g_free(gfud->webdata); + + /* In with the new. */ + gfud->len = body_len; + gfud->data_len = content_len; + gfud->webdata = new_data; + } + } + } + + if(gfud->has_explicit_data_len && gfud->len >= gfud->data_len) { + got_eof = TRUE; + break; + } + } + + if(len < 0) { + if(errno == EAGAIN) { + return; + } else { + purple_util_fetch_url_error(gfud, _("Error reading from %s: %s"), + gfud->website.address, g_strerror(errno)); + return; + } + } + + if((len == 0) || got_eof) { + gfud->webdata = g_realloc(gfud->webdata, gfud->len + 1); + gfud->webdata[gfud->len] = '\0'; + + if (!gfud->include_headers && gfud->chunked) { + /* Process only if we don't want the headers. */ + process_chunked_data(gfud->webdata, &gfud->len); + } + + gfud->callback(gfud, gfud->user_data, gfud->webdata, gfud->len, NULL); + purple_util_fetch_url_cancel(gfud); + } +} + +static void ssl_url_fetch_recv_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond) +{ + url_fetch_recv_cb(data, -1, cond); +} + +/** + * This function is called when the socket is available to be written + * to. + * + * @param source The file descriptor that can be written to. This can + * be an http connection or it can be the SSL connection of an + * https request. So be careful what you use it for! If it's + * an https request then use purple_ssl_write() instead of + * writing to it directly. + */ +static void +url_fetch_send_cb(gpointer data, gint source, PurpleInputCondition cond) +{ + PurpleUtilFetchUrlData *gfud; + int len, total_len; + + gfud = data; + + if (gfud->request == NULL) { + + PurpleProxyInfo *gpi = purple_proxy_get_setup(gfud->account); + GString *request_str = g_string_new(NULL); + + g_string_append_printf(request_str, "GET %s%s HTTP/%s\r\n" + "Connection: close\r\n", + (gfud->full ? "" : "/"), + (gfud->full ? (gfud->url ? gfud->url : "") : (gfud->website.page ? gfud->website.page : "")), + (gfud->http11 ? "1.1" : "1.0")); + + if (gfud->user_agent) + g_string_append_printf(request_str, "User-Agent: %s\r\n", gfud->user_agent); + + /* Host header is not forbidden in HTTP/1.0 requests, and HTTP/1.1 + * clients must know how to handle the "chunked" transfer encoding. + * Purple doesn't know how to handle "chunked", so should always send + * the Host header regardless, to get around some observed problems + */ + g_string_append_printf(request_str, "Accept: */*\r\n" + "Host: %s\r\n", + (gfud->website.address ? gfud->website.address : "")); + + if (purple_proxy_info_get_username(gpi) != NULL + && (purple_proxy_info_get_type(gpi) == PURPLE_PROXY_USE_ENVVAR + || purple_proxy_info_get_type(gpi) == PURPLE_PROXY_HTTP)) { + /* This chunk of code was copied from proxy.c http_start_connect_tunneling() + * This is really a temporary hack - we need a more complete proxy handling solution, + * so I didn't think it was worthwhile to refactor for reuse + */ + char *t1, *t2, *ntlm_type1; + char hostname[256]; + int ret; + + ret = gethostname(hostname, sizeof(hostname)); + hostname[sizeof(hostname) - 1] = '\0'; + if (ret < 0 || hostname[0] == '\0') { + purple_debug_warning("util", "proxy - gethostname() failed -- is your hostname set?"); + strcpy(hostname, "localhost"); + } + + t1 = g_strdup_printf("%s:%s", + purple_proxy_info_get_username(gpi), + purple_proxy_info_get_password(gpi) ? + purple_proxy_info_get_password(gpi) : ""); + t2 = purple_base64_encode((const guchar *)t1, strlen(t1)); + g_free(t1); + + ntlm_type1 = purple_ntlm_gen_type1(hostname, ""); + + g_string_append_printf(request_str, + "Proxy-Authorization: Basic %s\r\n" + "Proxy-Authorization: NTLM %s\r\n" + "Proxy-Connection: Keep-Alive\r\n", + t2, ntlm_type1); + g_free(ntlm_type1); + g_free(t2); + } + + g_string_append(request_str, "\r\n"); + + gfud->request = g_string_free(request_str, FALSE); + } + + if(purple_debug_is_unsafe()) + purple_debug_misc("util", "Request: '%s'\n", gfud->request); + else + purple_debug_misc("util", "request constructed\n"); + + total_len = strlen(gfud->request); + + if (gfud->is_ssl) + len = purple_ssl_write(gfud->ssl_connection, gfud->request + gfud->request_written, + total_len - gfud->request_written); + else + len = write(gfud->fd, gfud->request + gfud->request_written, + total_len - gfud->request_written); + + if (len < 0 && errno == EAGAIN) + return; + else if (len < 0) { + purple_util_fetch_url_error(gfud, _("Error writing to %s: %s"), + gfud->website.address, g_strerror(errno)); + return; + } + gfud->request_written += len; + + if (gfud->request_written < total_len) + return; + + /* We're done writing our request, now start reading the response */ + if (gfud->is_ssl) { + purple_input_remove(gfud->inpa); + gfud->inpa = 0; + purple_ssl_input_add(gfud->ssl_connection, ssl_url_fetch_recv_cb, gfud); + } else { + purple_input_remove(gfud->inpa); + gfud->inpa = purple_input_add(gfud->fd, PURPLE_INPUT_READ, url_fetch_recv_cb, + gfud); + } +} + +static void +url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message) +{ + PurpleUtilFetchUrlData *gfud; + + gfud = url_data; + gfud->connect_data = NULL; + + if (source == -1) + { + purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"), + (gfud->website.address ? gfud->website.address : ""), error_message); + return; + } + + gfud->fd = source; + + gfud->inpa = purple_input_add(source, PURPLE_INPUT_WRITE, + url_fetch_send_cb, gfud); + url_fetch_send_cb(gfud, source, PURPLE_INPUT_WRITE); +} + +static void ssl_url_fetch_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond) +{ + PurpleUtilFetchUrlData *gfud; + + gfud = data; + + gfud->inpa = purple_input_add(ssl_connection->fd, PURPLE_INPUT_WRITE, + url_fetch_send_cb, gfud); + url_fetch_send_cb(gfud, ssl_connection->fd, PURPLE_INPUT_WRITE); +} + +static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data) +{ + PurpleUtilFetchUrlData *gfud; + + gfud = data; + gfud->ssl_connection = NULL; + + purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"), + (gfud->website.address ? gfud->website.address : ""), + purple_ssl_strerror(error)); +} + +PurpleUtilFetchUrlData * +purple_util_fetch_url_request(PurpleAccount *account, + const char *url, gboolean full, const char *user_agent, gboolean http11, + const char *request, gboolean include_headers, gssize max_len, + PurpleUtilFetchUrlCallback callback, void *user_data) +{ + PurpleUtilFetchUrlData *gfud; + + g_return_val_if_fail(url != NULL, NULL); + g_return_val_if_fail(callback != NULL, NULL); + + if(purple_debug_is_unsafe()) + purple_debug_info("util", + "requested to fetch (%s), full=%d, user_agent=(%s), http11=%d\n", + url, full, user_agent?user_agent:"(null)", http11); + else + purple_debug_info("util", "requesting to fetch a URL\n"); + + gfud = g_new0(PurpleUtilFetchUrlData, 1); + + gfud->callback = callback; + gfud->user_data = user_data; + gfud->url = g_strdup(url); + gfud->user_agent = g_strdup(user_agent); + gfud->http11 = http11; + gfud->full = full; + gfud->request = g_strdup(request); + gfud->include_headers = include_headers; + gfud->fd = -1; + gfud->max_len = max_len; + gfud->account = account; + + purple_url_parse(url, &gfud->website.address, &gfud->website.port, + &gfud->website.page, &gfud->website.user, &gfud->website.passwd); + + if (purple_strcasestr(url, "https://") != NULL) { + if (!purple_ssl_is_supported()) { + purple_util_fetch_url_error(gfud, + _("Unable to connect to %s: %s"), + gfud->website.address, + _("Server requires TLS/SSL, but no TLS/SSL support was found.")); + return NULL; + } + + gfud->is_ssl = TRUE; + gfud->ssl_connection = purple_ssl_connect(account, + gfud->website.address, gfud->website.port, + ssl_url_fetch_connect_cb, ssl_url_fetch_error_cb, gfud); + } else { + gfud->connect_data = purple_proxy_connect(NULL, account, + gfud->website.address, gfud->website.port, + url_fetch_connect_cb, gfud); + } + + if (gfud->ssl_connection == NULL && gfud->connect_data == NULL) + { + purple_util_fetch_url_error(gfud, _("Unable to connect to %s"), + gfud->website.address); + return NULL; + } + + return gfud; +} + +void +purple_util_fetch_url_cancel(PurpleUtilFetchUrlData *gfud) +{ + if (gfud->wrapped_request != NULL) { + gfud->cancelled = TRUE; + purple_http_conn_cancel(gfud->wrapped_request); + return; + } + + if (gfud->ssl_connection != NULL) + purple_ssl_close(gfud->ssl_connection); + + if (gfud->connect_data != NULL) + purple_proxy_connect_cancel(gfud->connect_data); + + if (gfud->inpa > 0) + purple_input_remove(gfud->inpa); + + if (gfud->fd >= 0) + close(gfud->fd); + + g_free(gfud->website.user); + g_free(gfud->website.passwd); + g_free(gfud->website.address); + g_free(gfud->website.page); + g_free(gfud->url); + g_free(gfud->user_agent); + g_free(gfud->request); + g_free(gfud->webdata); + + g_free(gfud); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/obsolete.h Fri Jan 25 02:22:38 2013 -0500 @@ -0,0 +1,110 @@ +/** + * @file obsolete.h Obsolete code, to be removed + * @ingroup core + */ + +/* purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ +#ifndef _PURPLE_OBSOLETE_H_ +#define _PURPLE_OBSOLETE_H_ + +#include <glib.h> +#include "account.h" + +/**************************************************************************/ +/** @name URI/URL Functions */ +/**************************************************************************/ +/*@{*/ + +/** + * An opaque structure representing a URL request. Can be used to cancel + * the request. + */ +typedef struct _PurpleUtilFetchUrlData PurpleUtilFetchUrlData; + +/** + * This is the signature used for functions that act as the callback + * to purple_util_fetch_url() or purple_util_fetch_url_request(). + * + * @param url_data The same value that was returned when you called + * purple_fetch_url() or purple_fetch_url_request(). + * @param user_data The user data that your code passed into either + * purple_util_fetch_url() or purple_util_fetch_url_request(). + * @param url_text This will be NULL on error. Otherwise this + * will contain the contents of the URL. + * @param len 0 on error, otherwise this is the length of buf. + * @param error_message If something went wrong then this will contain + * a descriptive error message, and buf will be + * NULL and len will be 0. + */ +typedef void (*PurpleUtilFetchUrlCallback)(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message); + +/** + * Fetches the data from a URL, and passes it to a callback function. + * + * @param url The URL. + * @param full TRUE if this is the full URL, or FALSE if it's a + * partial URL. + * @param user_agent The user agent field to use, or NULL. + * @param http11 TRUE if HTTP/1.1 should be used to download the file. + * @param max_len The maximum number of bytes to retrieve (-1 for unlimited) + * @param cb The callback function. + * @param data The user data to pass to the callback function. + */ +PurpleUtilFetchUrlData * purple_util_fetch_url(const gchar *url, gboolean full, + const gchar *user_agent, gboolean http11, gssize max_len, + PurpleUtilFetchUrlCallback cb, gpointer data); + +/** + * Fetches the data from a URL, and passes it to a callback function. + * + * @param account The account for which the request is needed, or NULL. + * @param url The URL. + * @param full TRUE if this is the full URL, or FALSE if it's a + * partial URL. + * @param user_agent The user agent field to use, or NULL. + * @param http11 TRUE if HTTP/1.1 should be used to download the file. + * @param request A HTTP request to send to the server instead of the + * standard GET + * @param include_headers + * If TRUE, include the HTTP headers in the response. + * @param max_len The maximum number of bytes to retrieve (-1 for unlimited) + * @param callback The callback function. + * @param data The user data to pass to the callback function. + */ +PurpleUtilFetchUrlData *purple_util_fetch_url_request( + PurpleAccount *account, const gchar *url, + gboolean full, const gchar *user_agent, gboolean http11, + const gchar *request, gboolean include_headers, gssize max_len, + PurpleUtilFetchUrlCallback callback, gpointer data); + +/** + * Cancel a pending URL request started with either + * purple_util_fetch_url_request() or purple_util_fetch_url(). + * + * @param url_data The data returned when you initiated the URL fetch. + */ +void purple_util_fetch_url_cancel(PurpleUtilFetchUrlData *url_data); + + +/*@}*/ + +#endif /* _PURPLE_OBSOLETE_H_ */
--- a/libpurple/plugins/autoaccept.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/plugins/autoaccept.c Fri Jan 25 02:22:38 2013 -0500 @@ -239,7 +239,7 @@ * --Mark Doliner, 2011-01-03 */ if (!purple_prefs_exists(PREF_STRANGER)) { - if (purple_prefs_get_bool(PREF_STRANGER_OLD)) + if (purple_prefs_exists(PREF_STRANGER_OLD) && purple_prefs_get_bool(PREF_STRANGER_OLD)) purple_prefs_add_int(PREF_STRANGER, FT_REJECT); else purple_prefs_set_int(PREF_STRANGER, FT_ASK);
--- a/libpurple/plugins/log_reader.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/plugins/log_reader.c Fri Jan 25 02:22:38 2013 -0500 @@ -921,7 +921,6 @@ xmlnode *to; enum name_guesses name_guessed = NAME_GUESS_UNKNOWN; const char *their_name; - time_t time_unix; struct tm *tm; char *timestamp; char *tmp; @@ -1101,7 +1100,7 @@ text = g_string_append(text, ";\">"); } - time_unix = msn_logger_parse_timestamp(message, &tm); + msn_logger_parse_timestamp(message, &tm); timestamp = g_strdup_printf("<font size=\"2\">(%02u:%02u:%02u)</font> ", tm->tm_hour, tm->tm_min, tm->tm_sec); @@ -2581,7 +2580,7 @@ /* Read talk.ini file to find the log directory. */ GError *error = NULL; -#if 0 && GLIB_CHECK_VERSION(2,6,0) /* FIXME: Not tested yet. */ +#if 0 /* FIXME: Not tested yet. */ GKeyFile *key_file; purple_debug_info("Trillian talk.ini read", "Reading %s\n", path); @@ -2608,7 +2607,7 @@ g_key_file_free(key_file); } -#else /* !GLIB_CHECK_VERSION(2,6,0) */ +#else gchar *contents = NULL; purple_debug_info("Trillian talk.ini read", @@ -2644,7 +2643,7 @@ g_free(contents); } g_free(path); -#endif /* !GLIB_CHECK_VERSION(2,6,0) */ +#endif } /* path */ if (!found) {
--- a/libpurple/plugins/perl/common/BuddyIcon.xs Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/plugins/perl/common/BuddyIcon.xs Fri Jan 25 02:22:38 2013 -0500 @@ -7,7 +7,7 @@ purple_buddy_icon_ref(icon) Purple::Buddy::Icon icon -Purple::Buddy::Icon +void purple_buddy_icon_unref(icon) Purple::Buddy::Icon icon
--- a/libpurple/plugins/perl/common/BuddyList.xs Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/plugins/perl/common/BuddyList.xs Fri Jan 25 02:22:38 2013 -0500 @@ -35,10 +35,6 @@ Purple::BuddyList purple_get_blist() -void -purple_set_blist(blist) - Purple::BuddyList blist - MODULE = Purple::BuddyList PACKAGE = Purple::Find PREFIX = purple_find_ PROTOTYPES: ENABLE @@ -176,9 +172,6 @@ Purple::BuddyList::Group group Purple::BuddyList::Node node -Purple::BuddyList -purple_blist_new() - void purple_blist_show() @@ -237,9 +230,6 @@ Purple::BuddyList::Group group void -purple_blist_load() - -void purple_blist_schedule_save() void
--- a/libpurple/plugins/perl/common/Certificate.xs Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/plugins/perl/common/Certificate.xs Fri Jan 25 02:22:38 2013 -0500 @@ -43,8 +43,20 @@ static const constiv *civ, const_iv[] = { #define const_iv(name) {#name, (IV)PURPLE_CERTIFICATE_##name} - const_iv(INVALID), + const_iv(UNKNOWN_ERROR), const_iv(VALID), + const_iv(NON_FATALS_MASK), + const_iv(SELF_SIGNED), + const_iv(CA_UNKNOWN), + const_iv(NOT_ACTIVATED), + const_iv(EXPIRED), + const_iv(NAME_MISMATCH), + const_iv(NO_CA_POOL), + const_iv(FATALS_MASK), + const_iv(INVALID_CHAIN), + const_iv(REVOKED), + const_iv(REJECTED), + const_iv(LAST), }; for (civ = const_iv + sizeof(const_iv) / sizeof(const_iv[0]); civ-- > const_iv; )
--- a/libpurple/plugins/perl/common/ImgStore.xs Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/plugins/perl/common/ImgStore.xs Fri Jan 25 02:22:38 2013 -0500 @@ -4,13 +4,13 @@ PROTOTYPES: ENABLE Purple::StoredImage -purple_imgstore_add(data, size, filename) +purple_imgstore_new(data, size, filename) void *data size_t size const char *filename int -purple_imgstore_add_with_id(data, size, filename) +purple_imgstore_new_with_id(data, size, filename) void *data size_t size const char *filename
--- a/libpurple/plugins/perl/common/Makefile.mingw Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/plugins/perl/common/Makefile.mingw Fri Jan 25 02:22:38 2013 -0500 @@ -112,7 +112,7 @@ $(PERL) -MAutoSplit -e 'autosplit("lib/Purple.pm")' $(TARGET).dll: $(PURPLE_DLL).a $(PURPLE_PERL_DLL).a $(FALLBACKS) $(OBJECTS) - $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) -o $(TARGET).dll + $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) $(DLL_LD_FLAGS) -o $(TARGET).dll ## ## CLEAN
--- a/libpurple/plugins/perl/common/Pounce.xs Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/plugins/perl/common/Pounce.xs Fri Jan 25 02:22:38 2013 -0500 @@ -121,9 +121,6 @@ Purple::Handle purple_pounces_get_handle() -gboolean -purple_pounces_load() - void purple_pounces_unregister_handler(ui) const char *ui
--- a/libpurple/plugins/perl/common/Util.xs Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/plugins/perl/common/Util.xs Fri Jan 25 02:22:38 2013 -0500 @@ -1,31 +1,5 @@ #include "module.h" -static void -purple_perl_util_url_cb(PurpleUtilFetchUrlData *url_data, void *user_data, - const gchar *url_text, size_t size, - const gchar *error_message) -{ - SV *sv = (SV *)user_data; - dSP; - ENTER; - SAVETMPS; - PUSHMARK(SP); - - XPUSHs(sv_2mortal(newSVpvn(url_text, size))); - PUTBACK; - - call_sv(sv, G_EVAL | G_SCALAR); - SPAGAIN; - - /* XXX Make sure this destroys it correctly and that we don't want - * something like sv_2mortal(sv) or something else here instead. */ - SvREFCNT_dec(sv); - - PUTBACK; - FREETMPS; - LEAVE; -} - static void markup_find_tag_foreach(GQuark key_id, char *data, HV *hv) { const char *key = NULL; key = g_quark_to_string(key_id); @@ -460,31 +434,6 @@ MODULE = Purple::Util PACKAGE = Purple::Util PREFIX = purple_util_ PROTOTYPES: ENABLE - #XXX: expand... -void -purple_util_fetch_url(plugin, url, full, user_agent, http11, max_len, cb) - Purple::Plugin plugin - const char *url - gboolean full - const char *user_agent - gboolean http11 - gssize max_len - SV * cb -PREINIT: - PurpleUtilFetchUrlData *data; -PPCODE: - /* XXX: i don't like this... only plugins can use it... */ - SV *sv = purple_perl_sv_from_fun(plugin, cb); - - if (sv != NULL) { - data = purple_util_fetch_url(url, full, user_agent, http11, max_len, - purple_perl_util_url_cb, sv); - XPUSHs(sv_2mortal(purple_perl_bless_object(data, "Purple::Util::FetchUrlData"))); - } else { - purple_debug_warning("perl", "Callback not a valid type, only strings and coderefs allowed in purple_util_fetch_url.\n"); - XSRETURN_UNDEF; - } - void purple_util_set_user_dir(dir) const char *dir
--- a/libpurple/plugins/perl/common/module.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/plugins/perl/common/module.h Fri Jan 25 02:22:38 2013 -0500 @@ -271,7 +271,6 @@ /* util.h */ typedef PurpleInfoFieldFormatCallback Purple__Util__InfoFieldFormatCallback; -typedef PurpleUtilFetchUrlData Purple__Util__FetchUrlData; typedef PurpleMenuAction * Purple__Menu__Action; /* value.h */
--- a/libpurple/plugins/tcl/signal-test.tcl Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/plugins/tcl/signal-test.tcl Fri Jan 25 02:22:38 2013 -0500 @@ -108,7 +108,7 @@ purple::debug -info "tcl signal" "plugin-unload [list $args]" } -purple::signal connect [purple::savedstatuses handle] savedstatus-changed args { +purple::signal connect [purple::savedstatus handle] savedstatus-changed args { purple::debug -info "tcl signal" "savedstatus-changed [list $args]" purple::debug -info "tcl signal" "purple::savedstatus current = [purple::savedstatus current]" }
--- a/libpurple/plugins/tcl/tcl.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/plugins/tcl/tcl.c Fri Jan 25 02:22:38 2013 -0500 @@ -174,37 +174,17 @@ Tcl_Interp *interp; Tcl_Parse parse; Tcl_Obj *result, **listitems; - struct stat st; - FILE *fp; - char *buf, *cur; + char *buf; const char *next; - int len, found = 0, err = 0, nelems; + int found = 0, err = 0, nelems; + gsize len; gboolean status = FALSE; - if ((fp = g_fopen(plugin->path, "r")) == NULL) - return FALSE; - if (fstat(fileno(fp), &st)) { - fclose(fp); + + if (!g_file_get_contents(plugin->path, &buf, &len, NULL)) { + purple_debug(PURPLE_DEBUG_INFO, "tcl", "Error opening plugin %s\n", + plugin->path); return FALSE; } - len = st.st_size; - - buf = g_malloc(len + 1); - - cur = buf; - while (fgets(cur, GPOINTER_TO_INT(buf) - (buf - cur), fp)) { - cur += strlen(cur); - if (feof(fp)) - break; - } - - if (ferror(fp)) { - purple_debug(PURPLE_DEBUG_ERROR, "tcl", "error reading %s (%s)\n", plugin->path, g_strerror(errno)); - g_free(buf); - fclose(fp); - return FALSE; - } - - fclose(fp); if ((interp = tcl_create_interp()) == NULL) { return FALSE;
--- a/libpurple/pounce.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/pounce.c Fri Jan 25 02:22:38 2013 -0500 @@ -572,7 +572,7 @@ NULL }; -gboolean +static gboolean purple_pounces_load(void) { gchar *filename = g_build_filename(purple_user_dir(), "pounces.xml", NULL); @@ -1190,6 +1190,8 @@ purple_signal_connect(conv_handle, "received-im-msg", handle, PURPLE_CALLBACK(received_message_cb), NULL); + + purple_pounces_load(); } void
--- a/libpurple/pounce.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/pounce.h Fri Jan 25 02:22:38 2013 -0500 @@ -289,14 +289,6 @@ PurplePounce *purple_find_pounce(const PurpleAccount *pouncer, const char *pouncee, PurplePounceEvent events); - -/** - * Loads the pounces. - * - * @return @c TRUE if the pounces could be loaded. - */ -gboolean purple_pounces_load(void); - /** * Registers a pounce handler for a UI. *
--- a/libpurple/protocols/bonjour/bonjour_ft.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/bonjour/bonjour_ft.c Fri Jan 25 02:22:38 2013 -0500 @@ -33,7 +33,7 @@ static void bonjour_bytestreams_init(PurpleXfer *xfer); static void -bonjour_bytestreams_connect(PurpleXfer *xfer, PurpleBuddy *pb); +bonjour_bytestreams_connect(PurpleXfer *xfer); static void bonjour_xfer_init(PurpleXfer *xfer); static void @@ -280,6 +280,25 @@ xep_iq_send_and_free(iq); } +/** + * Frees the whole tree of an xml node + * + * First determines the root of the xml tree and then frees the whole tree + * from there. + * + * @param node The node to free the tree from + */ +static void +xmlnode_free_tree(xmlnode *node) +{ + g_return_if_fail(node != NULL); + + while(xmlnode_get_parent(node)) + node = xmlnode_get_parent(node); + + xmlnode_free(node); +} + static void bonjour_free_xfer(PurpleXfer *xfer) { @@ -310,6 +329,9 @@ g_free(xf->proxy_host); g_free(xf->buddy_ip); g_free(xf->sid); + + xmlnode_free_tree(xf->streamhost); + g_free(xf); purple_xfer_set_protocol_data(xfer, NULL); } @@ -441,7 +463,7 @@ if (si && (profile = xmlnode_get_attrib(si, "profile")) && !strcmp(profile, "http://jabber.org/protocol/si/profile/file-transfer")) { const char *filename = NULL, *filesize_str = NULL; - int filesize = 0; + goffset filesize = 0; xmlnode *file; const char *sid = xmlnode_get_attrib(si, "id"); @@ -449,7 +471,7 @@ if ((file = xmlnode_get_child(si, "file"))) { filename = xmlnode_get_attrib(file, "name"); if((filesize_str = xmlnode_get_attrib(file, "size"))) - filesize = atoi(filesize_str); + filesize = g_ascii_strtoll(filesize_str, NULL, 10); } /* TODO: Make sure that it is advertising a bytestreams transfer */ @@ -547,19 +569,97 @@ return !strcmp(host, buddy_ip); } +static inline gint +xep_addr_differ(const char *buddy_ip, const char *host) +{ + return !xep_cmp_addr(host, buddy_ip); +} + +/** + * Create and insert an identical twin + * + * Creates a copy of the specified node and inserts it right after + * this original node. + * + * @param node The node to clone + * @return A pointer to the new, cloned twin if successful + * or NULL otherwise. + */ +static xmlnode * +xmlnode_insert_twin_copy(xmlnode *node) { + xmlnode *copy; + + g_return_val_if_fail(node != NULL, NULL); + + copy = xmlnode_copy(node); + g_return_val_if_fail(copy != NULL, NULL); + + copy->next = node->next; + node->next = copy; + + return copy; +} + +/** + * Tries to append an interface scope to an IPv6 link local address. + * + * If the given address is a link local IPv6 address (with no + * interface scope) then we try to determine all fitting interfaces + * from our Bonjour IP address list. + * + * For any such found matches we insert a copy of our current xml + * streamhost entry right after this streamhost entry and append + * the determined interface to the host address of this copy. + * + * @param cur_streamhost The XML streamhost node we examine + * @param host The host address to examine in text form + * @param pb Buddy to get the list of link local IPv6 addresses + * and their interface from + * @return Returns TRUE if the specified 'host' address is a + * link local IPv6 address with no interface scope. + * Otherwise returns FALSE. + */ static gboolean -__xep_bytestreams_parse(PurpleBuddy *pb, PurpleXfer *xfer, xmlnode *query, +add_ipv6_link_local_ifaces(xmlnode *cur_streamhost, const char *host, + const PurpleBuddy *pb) { + xmlnode *new_streamhost = NULL; + struct in6_addr in6_addr; + BonjourBuddy *bb; + GSList *ip_elem; + + if (inet_pton(AF_INET6, host, &in6_addr) != 1 || + !IN6_IS_ADDR_LINKLOCAL(&in6_addr) || + strchr(host, '%')) + return FALSE; + + bb = purple_buddy_get_protocol_data(pb); + + for (ip_elem = bb->ips; + (ip_elem = g_slist_find_custom(ip_elem, host, (GCompareFunc)&xep_addr_differ)); + ip_elem = ip_elem->next) { + purple_debug_info("bonjour", "Inserting an xmlnode twin copy for %s with new host address %s\n", + host, (char*)ip_elem->data); + new_streamhost = xmlnode_insert_twin_copy(cur_streamhost); + xmlnode_set_attrib(new_streamhost, "host", ip_elem->data); + } + + if (!new_streamhost) + purple_debug_info("bonjour", "No interface for this IPv6 link local address found: %s\n", + host); + + return TRUE; +} + +static gboolean +__xep_bytestreams_parse(PurpleBuddy *pb, PurpleXfer *xfer, xmlnode *streamhost, const char *iq_id) { + char *tmp_iq_id; const char *jid, *host, *port; int portnum; - xmlnode *streamhost; XepXfer *xf = purple_xfer_get_protocol_data(xfer); - for(streamhost = xmlnode_get_child(query, "streamhost"); - streamhost; - streamhost = xmlnode_get_next_twin(streamhost)) { - + for(; streamhost; streamhost = xmlnode_get_next_twin(streamhost)) { if(!(jid = xmlnode_get_attrib(streamhost, "jid")) || !(host = xmlnode_get_attrib(streamhost, "host")) || !(port = xmlnode_get_attrib(streamhost, "port")) || @@ -568,29 +668,36 @@ continue; } - if(!xep_cmp_addr(host, xf->buddy_ip)) + /* skip IPv6 link local addresses with no interface scope + * (but try to add a new one with an interface scope then) */ + if(add_ipv6_link_local_ifaces(streamhost, host, pb)) continue; + tmp_iq_id = g_strdup(iq_id); g_free(xf->iq_id); - xf->iq_id = g_strdup(iq_id); + g_free(xf->jid); + g_free(xf->proxy_host); + + xf->iq_id = tmp_iq_id; xf->jid = g_strdup(jid); - xf->proxy_host = g_strdup(xf->buddy_ip); + xf->proxy_host = g_strdup(host); xf->proxy_port = portnum; + xf->streamhost = streamhost; + xf->pb = pb; purple_debug_info("bonjour", "bytestream offer parse" "jid=%s host=%s port=%d.\n", jid, host, portnum); - bonjour_bytestreams_connect(xfer, pb); + bonjour_bytestreams_connect(xfer); return TRUE; } return FALSE; } - void xep_bytestreams_parse(PurpleConnection *pc, xmlnode *packet, PurpleBuddy *pb) { const char *type, *from, *iq_id, *sid; - xmlnode *query; + xmlnode *query, *streamhost; BonjourData *bd; PurpleXfer *xfer; @@ -610,6 +717,10 @@ if(!type) return; + query = xmlnode_copy(query); + if (!query) + return; + if(strcmp(type, "set")) { purple_debug_info("bonjour", "bytestream offer Message type - Unknown-%s.\n", type); return; @@ -621,7 +732,9 @@ sid = xmlnode_get_attrib(query, "sid"); xfer = bonjour_si_xfer_find(bd, sid, from); - if(xfer && __xep_bytestreams_parse(pb, xfer, query, iq_id)) + streamhost = xmlnode_get_child(query, "streamhost"); + + if(xfer && streamhost && __xep_bytestreams_parse(pb, xfer, streamhost, iq_id)) return; /* success */ purple_debug_error("bonjour", "Didn't find an acceptable streamhost.\n"); @@ -864,15 +977,22 @@ XepIq *iq; xmlnode *q_node, *tmp_node; BonjourData *bd; + gboolean ret = FALSE; xf->proxy_connection = NULL; if(source < 0) { - purple_debug_error("bonjour", "Error connecting via SOCKS5 - %s\n", - error_message ? error_message : "(null)"); - xep_ft_si_reject(xf->data, xf->iq_id, purple_xfer_get_remote_user(xfer), "404", "cancel"); - /* Cancel the connection */ - purple_xfer_cancel_local(xfer); + purple_debug_error("bonjour", "Error connecting via SOCKS5 to %s - %s\n", + xf->proxy_host, error_message ? error_message : "(null)"); + + tmp_node = xmlnode_get_next_twin(xf->streamhost); + ret = __xep_bytestreams_parse(xf->pb, xfer, tmp_node, xf->iq_id); + + if (!ret) { + xep_ft_si_reject(xf->data, xf->iq_id, purple_xfer_get_remote_user(xfer), "404", "cancel"); + /* Cancel the connection */ + purple_xfer_cancel_local(xfer); + } return; } @@ -894,8 +1014,9 @@ } static void -bonjour_bytestreams_connect(PurpleXfer *xfer, PurpleBuddy *pb) +bonjour_bytestreams_connect(PurpleXfer *xfer) { + PurpleBuddy *pb; PurpleAccount *account = NULL; XepXfer *xf; char dstaddr[41]; @@ -913,6 +1034,7 @@ if(!xf) return; + pb = xf->pb; name = purple_buddy_get_name(pb); account = purple_buddy_get_account(pb);
--- a/libpurple/protocols/bonjour/bonjour_ft.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/bonjour/bonjour_ft.h Fri Jan 25 02:22:38 2013 -0500 @@ -50,6 +50,8 @@ char *jid; char *proxy_host; int proxy_port; + xmlnode *streamhost; + PurpleBuddy *pb; }; /**
--- a/libpurple/protocols/gg/avatar.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/gg/avatar.c Fri Jan 25 02:22:38 2013 -0500 @@ -30,6 +30,8 @@ #include "avatar.h" #include <debug.h> +#include <glibcompat.h> +#include <obsolete.h> #include "gg.h" #include "utils.h"
--- a/libpurple/protocols/gg/gg.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/gg/gg.c Fri Jan 25 02:22:38 2013 -0500 @@ -696,8 +696,11 @@ } break; case GG_EVENT_CONN_FAILED: - purple_input_remove(info->inpa); - info->inpa = 0; + if (info->inpa > 0) + { + purple_input_remove(info->inpa); + info->inpa = 0; + } purple_debug_info("gg", "Connection failure: %d\n", ev->event.failure); switch (ev->event.failure) {
--- a/libpurple/protocols/gg/image.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/gg/image.c Fri Jan 25 02:22:38 2013 -0500 @@ -30,6 +30,7 @@ #include "image.h" #include <debug.h> +#include <glibcompat.h> #include "gg.h" #include "utils.h" @@ -176,7 +177,7 @@ gchar *imgtag_replace; GList *pending_messages_it; - stored_id = purple_imgstore_add_with_id( + stored_id = purple_imgstore_new_with_id( g_memdup(image_reply->image, image_reply->size), image_reply->size, image_reply->filename);
--- a/libpurple/protocols/gg/oauth/oauth-purple.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/gg/oauth/oauth-purple.c Fri Jan 25 02:22:38 2013 -0500 @@ -34,6 +34,7 @@ #include "../xml.h" #include <debug.h> +#include <obsolete.h> #define GGP_OAUTH_RESPONSE_MAX 10240
--- a/libpurple/protocols/gg/pubdir-prpl.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/gg/pubdir-prpl.c Fri Jan 25 02:22:38 2013 -0500 @@ -31,6 +31,7 @@ #include <debug.h> #include <request.h> +#include <obsolete.h> #include "oauth/oauth-purple.h" #include "xml.h"
--- a/libpurple/protocols/gg/roster.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/gg/roster.c Fri Jan 25 02:22:38 2013 -0500 @@ -35,6 +35,7 @@ #include "purplew.h" #include <debug.h> +#include <glibcompat.h> #define GGP_ROSTER_SYNC_SETT "gg-synchronized" #define GGP_ROSTER_DEBUG 0
--- a/libpurple/protocols/gg/servconn.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/gg/servconn.c Fri Jan 25 02:22:38 2013 -0500 @@ -32,6 +32,7 @@ #include "utils.h" #include <debug.h> +#include <glibcompat.h> #define GGP_SERVCONN_HISTORY_PREF "/plugins/prpl/gg/server_history" #define GGP_SERVCONN_HISTORY_MAXLEN 15
--- a/libpurple/protocols/gg/status.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/gg/status.c Fri Jan 25 02:22:38 2013 -0500 @@ -132,6 +132,7 @@ if (status_message) { gchar *stripped = purple_markup_strip_html(status_message); + g_strstrip(stripped); *message = ggp_status_validate_description(stripped); g_free(stripped); } @@ -405,11 +406,16 @@ PurpleBuddy *buddy = purple_find_buddy(account, ggp_uin_to_str(uin)); const gchar *purple_status = ggp_status_to_purplestatus(status); gchar *status_message = NULL; + gboolean is_own; + + is_own = 0 == g_strcmp0(ggp_uin_to_str(uin), purple_account_get_username(account)); if (!buddy) { - purple_debug_warning("gg", "ggp_status_got_others_buddy: " - "buddy %u not found\n", uin); + if (!is_own) + purple_debug_warning("gg", + "ggp_status_got_others_buddy: " + "buddy %u not found\n", uin); return; } ggp_buddy_get_data(buddy)->blocked = (status == GG_STATUS_BLOCKED);
--- a/libpurple/protocols/irc/Makefile.mingw Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/irc/Makefile.mingw Fri Jan 25 02:22:38 2013 -0500 @@ -54,6 +54,13 @@ -lintl \ -lpurple + +ifeq ($(CYRUS_SASL), 1) +INCLUDE_PATHS += -I$(CYRUS_SASL_TOP)/include +LIB_PATHS += -L$(CYRUS_SASL_TOP)/bin +LIBS += -llibsasl +endif + include $(PIDGIN_COMMON_RULES) ##
--- a/libpurple/protocols/irc/irc.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/irc/irc.c Fri Jan 25 02:22:38 2013 -0500 @@ -155,6 +155,7 @@ char *tosend= g_strdup(buf); purple_signal_emit(_irc_plugin, "irc-sending-text", purple_account_get_connection(irc->account), &tosend); + if (tosend == NULL) return 0; @@ -393,9 +394,17 @@ const char *username, *realname; struct irc_conn *irc = purple_connection_get_protocol_data(gc); const char *pass = purple_connection_get_password(gc); +#ifdef HAVE_CYRUS_SASL + const gboolean use_sasl = purple_account_get_bool(irc->account, "sasl", FALSE); +#endif if (pass && *pass) { - buf = irc_format(irc, "v:", "PASS", pass); +#ifdef HAVE_CYRUS_SASL + if (use_sasl) + buf = irc_format(irc, "vv:", "CAP", "REQ", "sasl"); + else /* intended to fall through */ +#endif + buf = irc_format(irc, "v:", "PASS", pass); if (irc_send(irc, buf) < 0) { g_free(buf); return FALSE; @@ -529,6 +538,17 @@ g_free(irc->mode_chars); g_free(irc->reqnick); +#ifdef HAVE_CYRUS_SASL + if (irc->sasl_conn) { + sasl_dispose(&irc->sasl_conn); + irc->sasl_conn = NULL; + } + g_free(irc->sasl_cb); + if(irc->sasl_mechs) + g_string_free(irc->sasl_mechs, TRUE); +#endif + + g_free(irc); } @@ -1047,6 +1067,16 @@ option = purple_account_option_bool_new(_("Use SSL"), "ssl", FALSE); prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); +#ifdef HAVE_CYRUS_SASL + option = purple_account_option_bool_new(_("Authenticate with SASL"), "sasl", FALSE); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); + + option = purple_account_option_bool_new( + _("Allow plaintext SASL auth over unencrypted connection"), + "auth_plain_in_clear", FALSE); + prpl_info.protocol_options = g_list_append(prpl_info.protocol_options, option); +#endif + _irc_plugin = plugin; purple_prefs_remove("/plugins/prpl/irc/quitmsg");
--- a/libpurple/protocols/irc/irc.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/irc/irc.h Fri Jan 25 02:22:38 2013 -0500 @@ -25,6 +25,10 @@ #include <glib.h> +#ifdef HAVE_CYRUS_SASL +#include <sasl/sasl.h> +#endif + #include "circbuffer.h" #include "ft.h" #include "roomlist.h" @@ -93,6 +97,13 @@ char *mode_chars; char *reqnick; gboolean nickused; +#ifdef HAVE_CYRUS_SASL + sasl_conn_t *sasl_conn; + const char *current_mech; + GString *sasl_mechs; + gboolean mech_works; + sasl_callback_t *sasl_cb; +#endif }; struct irc_buddy { @@ -163,11 +174,19 @@ void irc_msg_regonly(struct irc_conn *irc, const char *name, const char *from, char **args); void irc_msg_time(struct irc_conn *irc, const char *name, const char *from, char **args); void irc_msg_topic(struct irc_conn *irc, const char *name, const char *from, char **args); +void irc_msg_topicinfo(struct irc_conn *irc, const char *name, const char *from, char **args); void irc_msg_unavailable(struct irc_conn *irc, const char *name, const char *from, char **args); void irc_msg_unknown(struct irc_conn *irc, const char *name, const char *from, char **args); void irc_msg_wallops(struct irc_conn *irc, const char *name, const char *from, char **args); void irc_msg_whois(struct irc_conn *irc, const char *name, const char *from, char **args); void irc_msg_who(struct irc_conn *irc, const char *name, const char *from, char **args); +#ifdef HAVE_CYRUS_SASL +void irc_msg_cap(struct irc_conn *irc, const char *name, const char *from, char **args); +void irc_msg_auth(struct irc_conn *irc, char *arg); +void irc_msg_authok(struct irc_conn *irc, const char *name, const char *from, char **args); +void irc_msg_authtryagain(struct irc_conn *irc, const char *name, const char *from, char **args); +void irc_msg_authfail(struct irc_conn *irc, const char *name, const char *from, char **args); +#endif void irc_msg_ignore(struct irc_conn *irc, const char *name, const char *from, char **args);
--- a/libpurple/protocols/irc/msgs.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/irc/msgs.c Fri Jan 25 02:22:38 2013 -0500 @@ -32,6 +32,10 @@ #include <stdio.h> #include <stdlib.h> +#ifdef HAVE_CYRUS_SASL +#include <sasl/sasl.h> +#endif + static char *irc_mask_nick(const char *mask); static char *irc_mask_userhost(const char *mask); static void irc_chat_remove_buddy(PurpleConversation *convo, char *data[2]); @@ -42,6 +46,10 @@ const char *from, const char *to, const char *rawmsg, gboolean notice); +#ifdef HAVE_CYRUS_SASL +static void irc_sasl_finish(struct irc_conn *irc); +#endif + static char *irc_mask_nick(const char *mask) { char *end, *buf; @@ -634,6 +642,38 @@ g_free(topic); } +void irc_msg_topicinfo(struct irc_conn *irc, const char *name, const char *from, char **args) +{ + PurpleConversation *convo; + struct tm *tm; + time_t t; + char *msg, *timestamp, *datestamp; + + if (!args || !args[1] || !args[2] || !args[3]) + return; + + convo = purple_find_conversation_with_account(PURPLE_CONV_TYPE_CHAT, args[1], irc->account); + if (!convo) { + purple_debug(PURPLE_DEBUG_ERROR, "irc", "Got topic info for %s, which doesn't exist\n", args[1]); + return; + } + + t = (time_t)atol(args[3]); + if (t == 0) { + purple_debug(PURPLE_DEBUG_ERROR, "irc", "Got apparently nonsensical topic timestamp %s\n", args[3]); + return; + } + tm = localtime(&t); + + timestamp = g_strdup(purple_time_format(tm)); + datestamp = g_strdup(purple_date_format_short(tm)); + msg = g_strdup_printf("Topic for %s set by %s at %s on %s", args[1], args[2], timestamp, datestamp); + purple_conv_chat_write(PURPLE_CONV_CHAT(convo), "", msg, PURPLE_MESSAGE_SYSTEM | PURPLE_MESSAGE_NO_LINKIFY, time(NULL)); + g_free(timestamp); + g_free(datestamp); + g_free(msg); +} + void irc_msg_unknown(struct irc_conn *irc, const char *name, const char *from, char **args) { PurpleConnection *gc = purple_account_get_connection(irc->account); @@ -1422,6 +1462,379 @@ g_free(msg); } +#ifdef HAVE_CYRUS_SASL +static int +irc_sasl_cb_secret(sasl_conn_t *conn, void *ctx, int id, sasl_secret_t **secret) +{ + struct irc_conn *irc = ctx; + sasl_secret_t *sasl_secret; + const char *pw; + size_t len; + + pw = purple_account_get_password(irc->account); + + if (!conn || !secret || id != SASL_CB_PASS) + return SASL_BADPARAM; + + len = strlen(pw); + /* Not an off-by-one because sasl_secret_t defines char data[1] */ + /* TODO: This can probably be moved to glib's allocator */ + sasl_secret = malloc(sizeof(sasl_secret_t) + len); + if (!sasl_secret) + return SASL_NOMEM; + + sasl_secret->len = len; + strcpy((char*)sasl_secret->data, pw); + + *secret = sasl_secret; + return SASL_OK; +} + +static int +irc_sasl_cb_log(void *context, int level, const char *message) +{ + if(level <= SASL_LOG_TRACE) + purple_debug_info("sasl", "%s\n", message); + + return SASL_OK; +} + +static int +irc_sasl_cb_simple(void *ctx, int id, const char **res, unsigned *len) +{ + struct irc_conn *irc = ctx; + PurpleConnection *gc = purple_account_get_connection(irc->account); + + switch(id) { + case SASL_CB_AUTHNAME: + *res = purple_connection_get_display_name(gc); + break; + case SASL_CB_USER: + *res = ""; + break; + default: + return SASL_BADPARAM; + } + if (len) *len = strlen((char *)*res); + return SASL_OK; +} + +static void +irc_auth_start_cyrus(struct irc_conn *irc) +{ + int ret = 0; + char *buf; + sasl_security_properties_t secprops; + PurpleAccount *account = irc->account; + PurpleConnection *gc = purple_account_get_connection(account); + + gboolean plaintext; + gboolean again = FALSE; + + /* Set up security properties and options */ + secprops.min_ssf = 0; + secprops.security_flags = SASL_SEC_NOANONYMOUS; + + if (!irc->gsc) { + secprops.max_ssf = -1; + secprops.maxbufsize = 4096; + plaintext = purple_account_get_bool(account, "auth_plain_in_clear", FALSE); + if (!plaintext) + secprops.security_flags |= SASL_SEC_NOPLAINTEXT; + } else { + secprops.max_ssf = 0; + secprops.maxbufsize = 0; + plaintext = TRUE; + } + + secprops.property_names = 0; + secprops.property_values = 0; + + do { + gchar *tmp = NULL; + again = FALSE; + + ret = sasl_client_new("irc", irc->server, NULL, NULL, irc->sasl_cb, 0, &irc->sasl_conn); + + if (ret != SASL_OK) { + purple_debug_error("irc", "sasl_client_new failed: %d\n", ret); + tmp = g_strdup_printf(_("Failed to initialize SASL authentication: %s"), + sasl_errdetail(irc->sasl_conn)); + purple_connection_error_reason (gc, + PURPLE_CONNECTION_ERROR_OTHER_ERROR, tmp); + g_free(tmp); + return; + } + + sasl_setprop(irc->sasl_conn, SASL_AUTH_EXTERNAL, irc->account->username); + sasl_setprop(irc->sasl_conn, SASL_SEC_PROPS, &secprops); + + ret = sasl_client_start(irc->sasl_conn, irc->sasl_mechs->str, NULL, NULL, NULL, &irc->current_mech); + + switch (ret) { + case SASL_OK: + case SASL_CONTINUE: + irc->mech_works = FALSE; + break; + case SASL_NOMECH: + purple_connection_error_reason (gc, + PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, + _("SASL authentication failed: No worthy authentication mechanisms found.")); + + irc_sasl_finish(irc); + return; + case SASL_BADPARAM: + case SASL_NOMEM: + tmp = g_strdup_printf(_("SASL authentication failed: %s"), sasl_errdetail(irc->sasl_conn)); + purple_connection_error_reason (gc, + PURPLE_CONNECTION_ERROR_OTHER_ERROR, tmp); + g_free(tmp); + + irc_sasl_finish(irc); + return; + default: + purple_debug_error("irc", "sasl_client_start failed: %s\n", sasl_errdetail(irc->sasl_conn)); + + if (irc->current_mech && *irc->current_mech) { + char *pos; + if ((pos = strstr(irc->sasl_mechs->str, irc->current_mech))) { + size_t index = pos - irc->sasl_mechs->str; + g_string_erase(irc->sasl_mechs, index, strlen(irc->current_mech)); + + /* Remove space which separated this mech from the next */ + if ((irc->sasl_mechs->str)[index] == ' ') { + g_string_erase(irc->sasl_mechs, index, 1); + } + } + + again = TRUE; + } + irc_sasl_finish(irc); + } + } while (again); + + purple_debug_info("irc", "Using SASL: %s\n", irc->current_mech); + + buf = irc_format(irc, "vv", "AUTHENTICATE", irc->current_mech); + irc_send(irc, buf); + g_free(buf); +} + +/* SASL authentication */ +void +irc_msg_cap(struct irc_conn *irc, const char *name, const char *from, char **args) +{ + int ret = 0; + int id = 0; + PurpleConnection *gc = purple_account_get_connection(irc->account); + const char *mech_list = NULL; + + if (!args[1] || !args[2] || strncmp(args[2], "sasl ", 6)) + return; + if (strncmp(args[1], "ACK", 4)) { + const char *tmp = _("SASL authentication failed: Server does not support SASL authentication."); + purple_connection_error_reason (gc, + PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, tmp); + + irc_sasl_finish(irc); + return; + } + + if ((ret = sasl_client_init(NULL)) != SASL_OK) { + const char *tmp = _("SASL authentication failed: Initializing SASL failed."); + purple_connection_error_reason (gc, + PURPLE_CONNECTION_ERROR_OTHER_ERROR, tmp); + return; + } + + irc->sasl_cb = g_new0(sasl_callback_t, 5); + + irc->sasl_cb[id].id = SASL_CB_AUTHNAME; + irc->sasl_cb[id].proc = irc_sasl_cb_simple; + irc->sasl_cb[id].context = (void *)irc; + id++; + + irc->sasl_cb[id].id = SASL_CB_USER; + irc->sasl_cb[id].proc = irc_sasl_cb_simple; + irc->sasl_cb[id].context = (void *)irc; + id++; + + irc->sasl_cb[id].id = SASL_CB_PASS; + irc->sasl_cb[id].proc = irc_sasl_cb_secret; + irc->sasl_cb[id].context = (void *)irc; + id++; + + irc->sasl_cb[id].id = SASL_CB_LOG; + irc->sasl_cb[id].proc = irc_sasl_cb_log; + irc->sasl_cb[id].context = (void *)irc; + id++; + + irc->sasl_cb[id].id = SASL_CB_LIST_END; + + /* We need to do this to be able to list the mechanisms. */ + ret = sasl_client_new("irc", irc->server, NULL, NULL, irc->sasl_cb, 0, &irc->sasl_conn); + + sasl_listmech(irc->sasl_conn, NULL, "", " ", "", &mech_list, NULL, NULL); + purple_debug_info("irc", "SASL: we have available: %s\n", mech_list); + + if (ret != SASL_OK) { + gchar *tmp; + + purple_debug_error("irc", "sasl_client_new failed: %d\n", ret); + tmp = g_strdup_printf(_("Failed to initialize SASL authentication: %s"), + sasl_errdetail(irc->sasl_conn)); + purple_connection_error_reason (gc, + PURPLE_CONNECTION_ERROR_OTHER_ERROR, tmp); + g_free(tmp); + + return; + } + + irc->sasl_mechs = g_string_new(mech_list); + + irc_auth_start_cyrus(irc); +} + +void +irc_msg_auth(struct irc_conn *irc, char *arg) +{ + PurpleConnection *gc = purple_account_get_connection(irc->account); + char *buf, *authinfo; + char *serverin = NULL; + unsigned serverinlen = 0; + const gchar *c_out; + unsigned int clen; + int ret; + + irc->mech_works = TRUE; + + if (!arg) + return; + + if (arg[0] != '+') { + serverin = arg; + serverinlen = strlen(serverin); + } + + ret = sasl_client_step(irc->sasl_conn, serverin, serverinlen, + NULL, &c_out, &clen); + + if (ret != SASL_OK && ret != SASL_CONTINUE) { + + gchar *tmp = g_strdup_printf(_("SASL authentication failed: %s"), + sasl_errdetail(irc->sasl_conn)); + purple_connection_error_reason (gc, + PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, tmp); + g_free(tmp); + + irc_sasl_finish(irc); + + return; + } + + authinfo = purple_base64_encode((const guchar*)c_out, clen); + + buf = irc_format(irc, "vv", "AUTHENTICATE", authinfo); + irc_send(irc, buf); + g_free(buf); + g_free(authinfo); +} + +void +irc_msg_authok(struct irc_conn *irc, const char *name, const char *from, char **args) +{ + char *buf; + + sasl_dispose(&irc->sasl_conn); + irc->sasl_conn = NULL; + purple_debug_info("irc", "Succesfully authenticated using SASL.\n"); + + /* Finish auth session */ + buf = irc_format(irc, "vv", "CAP", "END"); + irc_send(irc, buf); + g_free(buf); +} + +void +irc_msg_authtryagain(struct irc_conn *irc, const char *name, const char *from, char **args) +{ + PurpleConnection *gc = purple_account_get_connection(irc->account); + + /* We already received at least one AUTHENTICATE reply from the + * server. This suggests it supports this mechanism, but the + * password was incorrect. It would be better to abort and inform + * the user than to try again with a different mechanism, so they + * aren't told the server supports no worthy mechanisms. + */ + if (irc->mech_works) { + purple_connection_error_reason (gc, + PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect Password")); + + irc_sasl_finish(irc); + + return; + } + + if (irc->current_mech) { + char *pos; + if ((pos = strstr(irc->sasl_mechs->str, irc->current_mech))) { + size_t index = pos - irc->sasl_mechs->str; + g_string_erase(irc->sasl_mechs, index, strlen(irc->current_mech)); + + /* Remove space which separated this mech from the next */ + if ((irc->sasl_mechs->str)[index] == ' ') { + g_string_erase(irc->sasl_mechs, index, 1); + } + } + } + if (*irc->sasl_mechs->str) { + sasl_dispose(&irc->sasl_conn); + + purple_debug_info("irc", "Now trying with %s\n", irc->sasl_mechs->str); + irc_auth_start_cyrus(irc); + } else { + purple_connection_error_reason (gc, + PURPLE_CONNECTION_ERROR_AUTHENTICATION_IMPOSSIBLE, + _("SASL authentication failed: No worthy mechanisms found")); + + irc_sasl_finish(irc); + } +} + +void +irc_msg_authfail(struct irc_conn *irc, const char *name, const char *from, char **args) +{ + PurpleConnection *gc = purple_account_get_connection(irc->account); + + /* Only show an error if we did not abort ourselves. */ + if (irc->sasl_conn) { + purple_debug_info("irc", "SASL authentication failed: %s", sasl_errdetail(irc->sasl_conn)); + + purple_connection_error_reason (gc, + PURPLE_CONNECTION_ERROR_AUTHENTICATION_FAILED, _("Incorrect Password")); + } + + irc_sasl_finish(irc); +} + +static void +irc_sasl_finish(struct irc_conn *irc) +{ + char *buf; + + sasl_dispose(&irc->sasl_conn); + irc->sasl_conn = NULL; + + g_free(irc->sasl_cb); + irc->sasl_cb = NULL; + + /* Auth failed, abort */ + buf = irc_format(irc, "vv", "CAP", "END"); + irc_send(irc, buf); + g_free(buf); +} +#endif + void irc_msg_ignore(struct irc_conn *irc, const char *name, const char *from, char **args) { return;
--- a/libpurple/protocols/irc/parse.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/irc/parse.c Fri Jan 25 02:22:38 2013 -0500 @@ -73,7 +73,7 @@ { "324", "ncv:", irc_msg_chanmode }, /* Channel modes */ { "331", "nc:", irc_msg_topic }, /* No channel topic */ { "332", "nc:", irc_msg_topic }, /* Channel topic */ - { "333", "*", irc_msg_ignore }, /* Topic setter stuff */ + { "333", "ncvv", irc_msg_topicinfo }, /* Topic setter stuff */ { "352", "ncvvvnv:", irc_msg_who }, /* Channel WHO */ { "353", "nvc:", irc_msg_names }, /* Names list */ { "366", "nc:", irc_msg_names }, /* End of names */ @@ -102,6 +102,14 @@ { "501", "n:", irc_msg_badmode }, /* Unknown mode flag */ { "506", "nc:", irc_msg_nosend }, /* Must identify to send */ { "515", "nc:", irc_msg_regonly }, /* Registration required */ +#ifdef HAVE_CYRUS_SASL + { "903", "*", irc_msg_authok}, /* SASL auth successful */ + { "904", "*", irc_msg_authtryagain }, /* SASL auth failed, can recover */ + { "905", "*", irc_msg_authfail }, /* SASL auth failed */ + { "906", "*", irc_msg_authfail }, /* SASL auth failed */ + { "907", "*", irc_msg_authfail }, /* SASL auth failed */ + { "cap", "vv:", irc_msg_cap }, /* SASL capable */ +#endif { "invite", "n:", irc_msg_invite }, /* Invited */ { "join", ":", irc_msg_join }, /* Joined a channel */ { "kick", "cn:", irc_msg_kick }, /* KICK */ @@ -678,6 +686,11 @@ PURPLE_CONNECTION_ERROR_NETWORK_ERROR, _("Disconnected.")); return; +#ifdef HAVE_CYRUS_SASL + } else if (!strncmp(input, "AUTHENTICATE ", 13)) { + irc_msg_auth(irc, input + 13); + return; +#endif } if (input[0] != ':' || (cur = strchr(input, ' ')) == NULL) {
--- a/libpurple/protocols/jabber/Makefile.am Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/Makefile.am Fri Jan 25 02:22:38 2013 -0500 @@ -31,6 +31,8 @@ google/gmail.h \ google/google.c \ google/google.h \ + google/google_p2p.c \ + google/google_p2p.h \ google/google_presence.c \ google/google_presence.h \ google/google_roster.c \ @@ -113,8 +115,7 @@ libjabber_la_SOURCES = $(JABBERSOURCES) libjabber_la_LIBADD = $(GLIB_LIBS) $(SASL_LIBS) $(LIBXML_LIBS) $(IDN_LIBS)\ $(FARSIGHT_LIBS) \ - $(GSTREAMER_LIBS) \ - $(GSTINTERFACES_LIBS) + $(GSTREAMER_LIBS) libfacebook_la_SOURCES = libfacebook.c libfacebook_la_LIBADD = libjabber.la @@ -135,5 +136,4 @@ $(IDN_CFLAGS) \ $(LIBXML_CFLAGS) \ $(FARSIGHT_CFLAGS) \ - $(GSTREAMER_CFLAGS) \ - $(GSTINTERFACES_CFLAGS) + $(GSTREAMER_CFLAGS)
--- a/libpurple/protocols/jabber/Makefile.mingw Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/Makefile.mingw Fri Jan 25 02:22:38 2013 -0500 @@ -113,21 +113,9 @@ -lpurple ifeq ($(CYRUS_SASL), 1) -CYRUS_SASL_TOP := $(WIN32_DEV_TOP)/cyrus-sasl-2.1.22-daa1 INCLUDE_PATHS += -I$(CYRUS_SASL_TOP)/include LIB_PATHS += -L$(CYRUS_SASL_TOP)/bin LIBS += -llibsasl -CYRUS_SASL_DLLS = \ - $(CYRUS_SASL_TOP)/bin/libsasl.dll - -CYRUS_SASL_PLUGINS = \ - $(CYRUS_SASL_TOP)/bin/sasl2/saslANONYMOUS.dll \ - $(CYRUS_SASL_TOP)/bin/sasl2/saslCRAMMD5.dll \ - $(CYRUS_SASL_TOP)/bin/sasl2/saslDIGESTMD5.dll \ - $(CYRUS_SASL_TOP)/bin/sasl2/saslGSSAPI.dll \ - $(CYRUS_SASL_TOP)/bin/sasl2/saslLOGIN.dll \ - $(CYRUS_SASL_TOP)/bin/sasl2/saslPLAIN.dll - endif include $(PIDGIN_COMMON_RULES) @@ -144,11 +132,6 @@ cp $(GTALK_TARGET).dll $(DLL_INSTALL_DIR) cp $(XMPP_TARGET).dll $(DLL_INSTALL_DIR) cp $(TARGET).dll $(PURPLE_INSTALL_DIR) -ifeq ($(CYRUS_SASL), 1) - mkdir -p $(PURPLE_INSTALL_DIR)/sasl2 - cp $(CYRUS_SASL_DLLS) $(PURPLE_INSTALL_DIR) - cp $(CYRUS_SASL_PLUGINS) $(PURPLE_INSTALL_DIR)/sasl2 -endif $(OBJECTS): $(PURPLE_CONFIG_H)
--- a/libpurple/protocols/jabber/buddy.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/buddy.c Fri Jan 25 02:22:38 2013 -0500 @@ -1203,7 +1203,7 @@ char *img_text; char *hash; - jbi->vcard_imgids = g_slist_prepend(jbi->vcard_imgids, GINT_TO_POINTER(purple_imgstore_add_with_id(g_memdup(data, size), size, "logo.png"))); + jbi->vcard_imgids = g_slist_prepend(jbi->vcard_imgids, GINT_TO_POINTER(purple_imgstore_new_with_id(g_memdup(data, size), size, "logo.png"))); img_text = g_strdup_printf("<img src='" PURPLE_STORED_IMAGE_PROTOCOL "%d'>", GPOINTER_TO_INT(jbi->vcard_imgids->data));
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/google/google_p2p.c Fri Jan 25 02:22:38 2013 -0500 @@ -0,0 +1,442 @@ +/** + * @file google_p2p.c + * + * 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 "internal.h" + +#include "google_p2p.h" +#include "jingle/jingle.h" +#include "debug.h" + +#include <string.h> + +struct _JingleGoogleP2PPrivate +{ + GList *local_candidates; + GList *remote_candidates; +}; + +#define JINGLE_GOOGLE_P2P_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), JINGLE_TYPE_GOOGLE_P2P, JingleGoogleP2PPrivate)) + +static void jingle_google_p2p_class_init (JingleGoogleP2PClass *klass); +static void jingle_google_p2p_init (JingleGoogleP2P *google_p2p); +static void jingle_google_p2p_finalize (GObject *object); +static void jingle_google_p2p_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +static void jingle_google_p2p_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); +static JingleTransport *jingle_google_p2p_parse_internal(xmlnode *google_p2p); +static xmlnode *jingle_google_p2p_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action); +static void jingle_google_p2p_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate); +static GList *jingle_google_p2p_get_remote_candidates(JingleTransport *transport); + +static JingleTransportClass *parent_class = NULL; + +enum { + PROP_0, + PROP_LOCAL_CANDIDATES, + PROP_REMOTE_CANDIDATES, +}; + +static JingleGoogleP2PCandidate * +jingle_google_p2p_candidate_copy(JingleGoogleP2PCandidate *candidate) +{ + JingleGoogleP2PCandidate *new_candidate = g_new0(JingleGoogleP2PCandidate, 1); + new_candidate->id = g_strdup(candidate->id); + new_candidate->address = g_strdup(candidate->address); + new_candidate->port = candidate->port; + new_candidate->preference = candidate->preference; + new_candidate->type = g_strdup(candidate->type); + new_candidate->protocol = g_strdup(candidate->protocol); + new_candidate->username = g_strdup(candidate->username); + new_candidate->password = g_strdup(candidate->password); + new_candidate->generation = candidate->generation; + + new_candidate->rem_known = candidate->rem_known; + + return new_candidate; +} + +static void +jingle_google_p2p_candidate_free(JingleGoogleP2PCandidate *candidate) +{ + g_free(candidate->id); + g_free(candidate->address); + g_free(candidate->type); + g_free(candidate->protocol); + g_free(candidate->username); + g_free(candidate->password); +} + +GType +jingle_google_p2p_candidate_get_type(void) +{ + static GType type = 0; + + if (type == 0) { + type = g_boxed_type_register_static("JingleGoogleP2PCandidate", + (GBoxedCopyFunc)jingle_google_p2p_candidate_copy, + (GBoxedFreeFunc)jingle_google_p2p_candidate_free); + } + return type; +} + +JingleGoogleP2PCandidate * +jingle_google_p2p_candidate_new(const gchar *id, guint generation, + const gchar *address, guint port, guint preference, + const gchar *type, const gchar *protocol, + const gchar *username, const gchar *password) +{ + JingleGoogleP2PCandidate *candidate = g_new0(JingleGoogleP2PCandidate, 1); + candidate->id = g_strdup(id); + candidate->address = g_strdup(address); + candidate->port = port; + candidate->preference = preference; + candidate->type = g_strdup(type); + candidate->protocol = g_strdup(protocol); + candidate->username = g_strdup(username); + candidate->password = g_strdup(password); + candidate->generation = generation; + + candidate->rem_known = FALSE; + return candidate; +} + +GType +jingle_google_p2p_get_type(void) +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = { + sizeof(JingleGoogleP2PClass), + NULL, + NULL, + (GClassInitFunc) jingle_google_p2p_class_init, + NULL, + NULL, + sizeof(JingleGoogleP2P), + 0, + (GInstanceInitFunc) jingle_google_p2p_init, + NULL + }; + type = g_type_register_static(JINGLE_TYPE_TRANSPORT, "JingleGoogleP2P", &info, 0); + } + return type; +} + +static void +jingle_google_p2p_class_init(JingleGoogleP2PClass *klass) +{ + GObjectClass *gobject_class = (GObjectClass *)klass; + parent_class = g_type_class_peek_parent(klass); + + gobject_class->finalize = jingle_google_p2p_finalize; + gobject_class->set_property = jingle_google_p2p_set_property; + gobject_class->get_property = jingle_google_p2p_get_property; + klass->parent_class.to_xml = jingle_google_p2p_to_xml_internal; + klass->parent_class.parse = jingle_google_p2p_parse_internal; + klass->parent_class.transport_type = NS_GOOGLE_TRANSPORT_P2P; + klass->parent_class.add_local_candidate = jingle_google_p2p_add_local_candidate; + klass->parent_class.get_remote_candidates = jingle_google_p2p_get_remote_candidates; + + g_object_class_install_property(gobject_class, PROP_LOCAL_CANDIDATES, + g_param_spec_pointer("local-candidates", + "Local candidates", + "The local candidates for this transport.", + G_PARAM_READABLE)); + + g_object_class_install_property(gobject_class, PROP_REMOTE_CANDIDATES, + g_param_spec_pointer("remote-candidates", + "Remote candidates", + "The remote candidates for this transport.", + G_PARAM_READABLE)); + + g_type_class_add_private(klass, sizeof(JingleGoogleP2PPrivate)); +} + +static void +jingle_google_p2p_init(JingleGoogleP2P *google_p2p) +{ + google_p2p->priv = JINGLE_GOOGLE_P2P_GET_PRIVATE(google_p2p); + google_p2p->priv->local_candidates = NULL; + google_p2p->priv->remote_candidates = NULL; +} + +static void +jingle_google_p2p_finalize(GObject *google_p2p) +{ +/* JingleGoogleP2PPrivate *priv = JINGLE_GOOGLE_P2P_GET_PRIVATE(google_p2p); */ + purple_debug_info("jingle","jingle_google_p2p_finalize\n"); + + G_OBJECT_CLASS(parent_class)->finalize(google_p2p); +} + +static void +jingle_google_p2p_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) +{ + JingleGoogleP2P *google_p2p; + + g_return_if_fail(object != NULL); + g_return_if_fail(JINGLE_IS_GOOGLE_P2P(object)); + + google_p2p = JINGLE_GOOGLE_P2P(object); + + switch (prop_id) { + case PROP_LOCAL_CANDIDATES: + google_p2p->priv->local_candidates = g_value_get_pointer(value); + break; + case PROP_REMOTE_CANDIDATES: + google_p2p->priv->remote_candidates = g_value_get_pointer(value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +jingle_google_p2p_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +{ + JingleGoogleP2P *google_p2p; + + g_return_if_fail(object != NULL); + g_return_if_fail(JINGLE_IS_GOOGLE_P2P(object)); + + google_p2p = JINGLE_GOOGLE_P2P(object); + + switch (prop_id) { + case PROP_LOCAL_CANDIDATES: + g_value_set_pointer(value, google_p2p->priv->local_candidates); + break; + case PROP_REMOTE_CANDIDATES: + g_value_set_pointer(value, google_p2p->priv->remote_candidates); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +jingle_google_p2p_add_local_candidate(JingleTransport *transport, const gchar *id, + guint generation, PurpleMediaCandidate *candidate) +{ + JingleGoogleP2P *google_p2p = JINGLE_GOOGLE_P2P(transport); + JingleGoogleP2PCandidate *google_p2p_candidate; + gchar *ip; + gchar *username; + gchar *password; + PurpleMediaCandidateType type; + PurpleMediaNetworkProtocol protocol; + GList *iter; + + ip = purple_media_candidate_get_ip(candidate); + username = purple_media_candidate_get_username(candidate); + password = purple_media_candidate_get_password(candidate); + type = purple_media_candidate_get_candidate_type(candidate); + protocol = purple_media_candidate_get_protocol(candidate); + + google_p2p_candidate = jingle_google_p2p_candidate_new(id, generation, + ip, purple_media_candidate_get_port(candidate), + purple_media_candidate_get_priority(candidate), + type == PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? "host" : + type == PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? "srflx" : + type == PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX ? "prflx" : + type == PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? "relay" : + "", + protocol == PURPLE_MEDIA_NETWORK_PROTOCOL_UDP ? "udp" : "tcp", + username, password); + + g_free(password); + g_free(username); + g_free(ip); + + for (iter = google_p2p->priv->local_candidates; iter; iter = g_list_next(iter)) { + JingleGoogleP2PCandidate *c = iter->data; + if (!strcmp(c->id, id)) { + generation = c->generation + 1; + + g_boxed_free(JINGLE_TYPE_GOOGLE_P2P_CANDIDATE, c); + google_p2p->priv->local_candidates = g_list_delete_link( + google_p2p->priv->local_candidates, iter); + + google_p2p_candidate->generation = generation; + + google_p2p->priv->local_candidates = g_list_append( + google_p2p->priv->local_candidates, candidate); + return; + } + } + + google_p2p->priv->local_candidates = g_list_append( + google_p2p->priv->local_candidates, google_p2p_candidate); +} + +static GList * +jingle_google_p2p_get_remote_candidates(JingleTransport *transport) +{ + JingleGoogleP2P *google_p2p = JINGLE_GOOGLE_P2P(transport); + GList *candidates = google_p2p->priv->remote_candidates; + GList *ret = NULL; + + for (; candidates; candidates = g_list_next(candidates)) { + JingleGoogleP2PCandidate *candidate = candidates->data; + PurpleMediaCandidate *new_candidate = purple_media_candidate_new("", 0, + !strcmp(candidate->type, "host") ? + PURPLE_MEDIA_CANDIDATE_TYPE_HOST : + !strcmp(candidate->type, "srflx") ? + PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX : + !strcmp(candidate->type, "prflx") ? + PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX : + !strcmp(candidate->type, "relay") ? + PURPLE_MEDIA_CANDIDATE_TYPE_RELAY : 0, + !strcmp(candidate->protocol, "udp") ? + PURPLE_MEDIA_NETWORK_PROTOCOL_UDP : + PURPLE_MEDIA_NETWORK_PROTOCOL_TCP, + candidate->address, candidate->port); + g_object_set(new_candidate, + "username", candidate->username, + "password", candidate->password, + "priority", candidate->preference, + NULL); + ret = g_list_append(ret, new_candidate); + } + + return ret; +} + +static JingleGoogleP2PCandidate * +jingle_google_p2p_get_remote_candidate_by_id(JingleGoogleP2P *google_p2p, + const gchar *id) +{ + GList *iter = google_p2p->priv->remote_candidates; + for (; iter; iter = g_list_next(iter)) { + JingleGoogleP2PCandidate *candidate = iter->data; + if (!strcmp(candidate->id, id)) { + return candidate; + } + } + return NULL; +} + +static void +jingle_google_p2p_add_remote_candidate(JingleGoogleP2P *google_p2p, JingleGoogleP2PCandidate *candidate) +{ + JingleGoogleP2PPrivate *priv = JINGLE_GOOGLE_P2P_GET_PRIVATE(google_p2p); + JingleGoogleP2PCandidate *google_p2p_candidate = + jingle_google_p2p_get_remote_candidate_by_id(google_p2p, + candidate->id); + if (google_p2p_candidate != NULL) { + priv->remote_candidates = g_list_remove(priv->remote_candidates, + google_p2p_candidate); + g_boxed_free(JINGLE_TYPE_GOOGLE_P2P_CANDIDATE, google_p2p_candidate); + } + priv->remote_candidates = g_list_append(priv->remote_candidates, candidate); +} + +static JingleTransport * +jingle_google_p2p_parse_internal(xmlnode *google_p2p) +{ + JingleTransport *transport = parent_class->parse(google_p2p); + xmlnode *candidate = xmlnode_get_child(google_p2p, "candidate"); + JingleGoogleP2PCandidate *google_p2p_candidate = NULL; + + for (; candidate; candidate = xmlnode_get_next_twin(candidate)) { + const gchar *generation = xmlnode_get_attrib(candidate, "generation"); + const gchar *id = xmlnode_get_attrib(candidate, "name"); + const gchar *address = xmlnode_get_attrib(candidate, "address"); + const gchar *port = xmlnode_get_attrib(candidate, "port"); + const gchar *preference = xmlnode_get_attrib(candidate, "preference"); + const gchar *type = xmlnode_get_attrib(candidate, "type"); + const gchar *protocol = xmlnode_get_attrib(candidate, "protocol"); + const gchar *username = xmlnode_get_attrib(candidate, "username"); + const gchar *password = xmlnode_get_attrib(candidate, "password"); + + if (!generation || !id || !address || !port || !preference || + !type || !protocol || !username || !password) + continue; + + google_p2p_candidate = jingle_google_p2p_candidate_new(id, + atoi(generation), + address, + atoi(port), + atoi(preference), + type, + protocol, + username, password); + google_p2p_candidate->rem_known = TRUE; + jingle_google_p2p_add_remote_candidate(JINGLE_GOOGLE_P2P(transport), google_p2p_candidate); + } + + return transport; +} + +static xmlnode * +jingle_google_p2p_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action) +{ + xmlnode *node = parent_class->to_xml(transport, content, action); + + if (action == JINGLE_SESSION_INITIATE || + action == JINGLE_SESSION_ACCEPT || + action == JINGLE_TRANSPORT_INFO || + action == JINGLE_CONTENT_ADD || + action == JINGLE_TRANSPORT_REPLACE) { + JingleGoogleP2PPrivate *priv = JINGLE_GOOGLE_P2P_GET_PRIVATE(transport); + GList *iter = priv->local_candidates; + + for (; iter; iter = g_list_next(iter)) { + JingleGoogleP2PCandidate *candidate = iter->data; + xmlnode *xmltransport; + gchar *generation, *network, *port, *preference; + + if (candidate->rem_known == TRUE) + continue; + + candidate->rem_known = TRUE; + + xmltransport = xmlnode_new_child(node, "candidate"); + generation = g_strdup_printf("%d", candidate->generation); + network = g_strdup_printf("%d", candidate->network); + port = g_strdup_printf("%d", candidate->port); + preference = g_strdup_printf("%d", candidate->preference); + + xmlnode_set_attrib(xmltransport, "generation", generation); + xmlnode_set_attrib(xmltransport, "name", candidate->id); + xmlnode_set_attrib(xmltransport, "address", candidate->address); + xmlnode_set_attrib(xmltransport, "network", network); + xmlnode_set_attrib(xmltransport, "port", port); + xmlnode_set_attrib(xmltransport, "preference", preference); + xmlnode_set_attrib(xmltransport, "protocol", candidate->protocol); + xmlnode_set_attrib(xmltransport, "type", candidate->type); + xmlnode_set_attrib(xmltransport, "username", candidate->username); + xmlnode_set_attrib(xmltransport, "password", candidate->password); + + g_free(generation); + g_free(network); + g_free(port); + g_free(preference); + } + } + + return node; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/jabber/google/google_p2p.h Fri Jan 25 02:22:38 2013 -0500 @@ -0,0 +1,102 @@ +/** + * @file google_p2p.h + * + * purple + * + * Purple is the legal property of its developers, whose names are too numerous + * to list here. Please refer to the COPYRIGHT file distributed with this + * source distribution. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA + */ + +#ifndef PURPLE_JABBER_JINGLE_GOOGLE_P2P_H +#define PURPLE_JABBER_JINGLE_GOOGLE_P2P_H + +#include <glib.h> +#include <glib-object.h> + +#include "jingle/transport.h" + +G_BEGIN_DECLS + +#define JINGLE_TYPE_GOOGLE_P2P (jingle_google_p2p_get_type()) +#define JINGLE_TYPE_GOOGLE_P2P_CANDIDATE (jingle_google_p2p_candidate_get_type()) +#define JINGLE_GOOGLE_P2P(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), JINGLE_TYPE_GOOGLE_P2P, JingleGoogleP2P)) +#define JINGLE_GOOGLE_P2P_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), JINGLE_TYPE_GOOGLE_P2P, JingleGoogleP2PClass)) +#define JINGLE_IS_GOOGLE_P2P(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), JINGLE_TYPE_GOOGLE_P2P)) +#define JINGLE_IS_GOOGLE_P2P_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), JINGLE_TYPE_GOOGLE_P2P)) +#define JINGLE_GOOGLE_P2P_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), JINGLE_TYPE_GOOGLE_P2P, JingleGoogleP2PClass)) + +/** @copydoc _JingleGoogleP2P */ +typedef struct _JingleGoogleP2P JingleGoogleP2P; +/** @copydoc _JingleGoogleP2PClass */ +typedef struct _JingleGoogleP2PClass JingleGoogleP2PClass; +/** @copydoc _JingleGoogleP2PPrivate */ +typedef struct _JingleGoogleP2PPrivate JingleGoogleP2PPrivate; +/** @copydoc _JingleGoogleP2PCandidate */ +typedef struct _JingleGoogleP2PCandidate JingleGoogleP2PCandidate; + +/** The Google P2P class */ +struct _JingleGoogleP2PClass +{ + JingleTransportClass parent_class; /**< The parent class. */ + + xmlnode *(*to_xml) (JingleTransport *transport, xmlnode *content, JingleActionType action); + JingleTransport *(*parse) (xmlnode *transport); +}; + +/** The Google P2P class's private data */ +struct _JingleGoogleP2P +{ + JingleTransport parent; /**< The parent of this object. */ + JingleGoogleP2PPrivate *priv; /**< The private data of this object. */ +}; + +struct _JingleGoogleP2PCandidate +{ + gchar *id; + gchar *address; + guint port; + guint preference; + gchar *type; + gchar *protocol; + guint network; + gchar *username; + gchar *password; + guint generation; + + gboolean rem_known; /* TRUE if the remote side knows + * about this candidate */ +}; + +GType jingle_google_p2p_candidate_get_type(void); + +/** + * Gets the Google P2P class's GType + * + * @return The Google P2P class's GType. + */ +GType jingle_google_p2p_get_type(void); + +JingleGoogleP2PCandidate *jingle_google_p2p_candidate_new(const gchar *id, + guint generation, const gchar *address, guint port, guint preference, + const gchar *type, const gchar *protocol, + const gchar *username, const gchar *password); + +G_END_DECLS + +#endif /* PURPLE_JABBER_JINGLE_GOOGLE_P2P_H */ +
--- a/libpurple/protocols/jabber/google/relay.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/google/relay.c Fri Jan 25 02:22:38 2013 -0500 @@ -20,6 +20,7 @@ #include "internal.h" #include "debug.h" +#include "obsolete.h" #include "relay.h"
--- a/libpurple/protocols/jabber/jabber.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/jabber.c Fri Jan 25 02:22:38 2013 -0500 @@ -44,6 +44,7 @@ #include "util.h" #include "version.h" #include "xmlnode.h" +#include "obsolete.h" #include "auth.h" #include "buddy.h"
--- a/libpurple/protocols/jabber/jingle/content.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/jingle/content.h Fri Jan 25 02:22:38 2013 -0500 @@ -68,10 +68,6 @@ JingleContentPrivate *priv; /**< The private data of this object. */ }; -#ifdef __cplusplus -extern "C" { -#endif - /** * Gets the content class's GType * @@ -111,10 +107,6 @@ xmlnode *jingle_content_to_xml(JingleContent *content, xmlnode *jingle, JingleActionType action); void jingle_content_handle_action(JingleContent *content, xmlnode *xmlcontent, JingleActionType action); -#ifdef __cplusplus -} -#endif - G_END_DECLS #endif /* PURPLE_JABBER_JINGLE_CONTENT_H */
--- a/libpurple/protocols/jabber/jingle/iceudp.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/jingle/iceudp.c Fri Jan 25 02:22:38 2013 -0500 @@ -45,6 +45,8 @@ static void jingle_iceudp_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static JingleTransport *jingle_iceudp_parse_internal(xmlnode *iceudp); static xmlnode *jingle_iceudp_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action); +static void jingle_iceudp_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate); +static GList *jingle_iceudp_get_remote_candidates(JingleTransport *transport); static JingleTransportClass *parent_class = NULL; @@ -58,10 +60,10 @@ jingle_iceudp_candidate_copy(JingleIceUdpCandidate *candidate) { JingleIceUdpCandidate *new_candidate = g_new0(JingleIceUdpCandidate, 1); + new_candidate->id = g_strdup(candidate->id); new_candidate->component = candidate->component; new_candidate->foundation = g_strdup(candidate->foundation); new_candidate->generation = candidate->generation; - new_candidate->id = g_strdup(candidate->id); new_candidate->ip = g_strdup(candidate->ip); new_candidate->network = candidate->network; new_candidate->port = candidate->port; @@ -105,17 +107,18 @@ } JingleIceUdpCandidate * -jingle_iceudp_candidate_new(guint component, const gchar *foundation, - guint generation, const gchar *id, const gchar *ip, +jingle_iceudp_candidate_new(const gchar *id, + guint component, const gchar *foundation, + guint generation, const gchar *ip, guint network, guint port, guint priority, const gchar *protocol, const gchar *type, const gchar *username, const gchar *password) { JingleIceUdpCandidate *candidate = g_new0(JingleIceUdpCandidate, 1); + candidate->id = g_strdup(id); candidate->component = component; candidate->foundation = g_strdup(foundation); candidate->generation = generation; - candidate->id = g_strdup(id); candidate->ip = g_strdup(ip); candidate->network = network; candidate->port = port; @@ -165,6 +168,8 @@ klass->parent_class.to_xml = jingle_iceudp_to_xml_internal; klass->parent_class.parse = jingle_iceudp_parse_internal; klass->parent_class.transport_type = JINGLE_TRANSPORT_ICEUDP; + klass->parent_class.add_local_candidate = jingle_iceudp_add_local_candidate; + klass->parent_class.get_remote_candidates = jingle_iceudp_get_remote_candidates; g_object_class_install_property(gobject_class, PROP_LOCAL_CANDIDATES, g_param_spec_pointer("local-candidates", @@ -246,36 +251,93 @@ } } -void -jingle_iceudp_add_local_candidate(JingleIceUdp *iceudp, JingleIceUdpCandidate *candidate) +static void +jingle_iceudp_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate) { - GList *iter = iceudp->priv->local_candidates; + JingleIceUdp *iceudp = JINGLE_ICEUDP(transport); + PurpleMediaCandidateType type; + gchar *ip; + gchar *username; + gchar *password; + JingleIceUdpCandidate *iceudp_candidate; + GList *iter; + + ip = purple_media_candidate_get_ip(candidate); + username = purple_media_candidate_get_username(candidate); + password = purple_media_candidate_get_password(candidate); + type = purple_media_candidate_get_candidate_type(candidate); - for (; iter; iter = g_list_next(iter)) { + iceudp_candidate = jingle_iceudp_candidate_new(id, + purple_media_candidate_get_component_id(candidate), + purple_media_candidate_get_foundation(candidate), + generation, ip, 0, + purple_media_candidate_get_port(candidate), + purple_media_candidate_get_priority(candidate), "udp", + type == PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? "host" : + type == PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? "srflx" : + type == PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX ? "prflx" : + type == PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? "relay" : + "", username, password); + iceudp_candidate->reladdr = purple_media_candidate_get_base_ip(candidate); + iceudp_candidate->relport = purple_media_candidate_get_base_port(candidate); + + g_free(password); + g_free(username); + g_free(ip); + + for (iter = iceudp->priv->local_candidates; iter; iter = g_list_next(iter)) { JingleIceUdpCandidate *c = iter->data; - if (!strcmp(c->id, candidate->id)) { - guint generation = c->generation + 1; + if (!strcmp(c->id, id)) { + generation = c->generation + 1; g_boxed_free(JINGLE_TYPE_ICEUDP_CANDIDATE, c); iceudp->priv->local_candidates = g_list_delete_link( iceudp->priv->local_candidates, iter); - candidate->generation = generation; + iceudp_candidate->generation = generation; iceudp->priv->local_candidates = g_list_append( - iceudp->priv->local_candidates, candidate); + iceudp->priv->local_candidates, iceudp_candidate); return; } } iceudp->priv->local_candidates = g_list_append( - iceudp->priv->local_candidates, candidate); + iceudp->priv->local_candidates, iceudp_candidate); } -GList * -jingle_iceudp_get_remote_candidates(JingleIceUdp *iceudp) +static GList * +jingle_iceudp_get_remote_candidates(JingleTransport *transport) { - return g_list_copy(iceudp->priv->remote_candidates); + JingleIceUdp *iceudp = JINGLE_ICEUDP(transport); + GList *candidates = iceudp->priv->remote_candidates; + GList *ret = NULL; + + for (; candidates; candidates = g_list_next(candidates)) { + JingleIceUdpCandidate *candidate = candidates->data; + PurpleMediaCandidate *new_candidate = purple_media_candidate_new( + candidate->foundation, candidate->component, + !strcmp(candidate->type, "host") ? + PURPLE_MEDIA_CANDIDATE_TYPE_HOST : + !strcmp(candidate->type, "srflx") ? + PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX : + !strcmp(candidate->type, "prflx") ? + PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX : + !strcmp(candidate->type, "relay") ? + PURPLE_MEDIA_CANDIDATE_TYPE_RELAY : 0, + PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, + candidate->ip, candidate->port); + g_object_set(new_candidate, + "base-ip", candidate->reladdr, + "base-port", candidate->relport, + "username", candidate->username, + "password", candidate->password, + "priority", candidate->priority, + NULL); + ret = g_list_append(ret, new_candidate); + } + + return ret; } static JingleIceUdpCandidate * @@ -335,10 +397,10 @@ continue; iceudp_candidate = jingle_iceudp_candidate_new( + id, atoi(component), foundation, atoi(generation), - id, ip, atoi(network), atoi(port),
--- a/libpurple/protocols/jabber/jingle/iceudp.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/jingle/iceudp.h Fri Jan 25 02:22:38 2013 -0500 @@ -67,10 +67,10 @@ struct _JingleIceUdpCandidate { + gchar *id; guint component; gchar *foundation; guint generation; - gchar *id; gchar *ip; guint network; guint port; @@ -87,10 +87,6 @@ * about this candidate */ }; -#ifdef __cplusplus -extern "C" { -#endif - GType jingle_iceudp_candidate_get_type(void); /** @@ -100,17 +96,11 @@ */ GType jingle_iceudp_get_type(void); -JingleIceUdpCandidate *jingle_iceudp_candidate_new(guint component, - const gchar *foundation, guint generation, const gchar *id, +JingleIceUdpCandidate *jingle_iceudp_candidate_new(const gchar *id, + guint component, const gchar *foundation, guint generation, const gchar *ip, guint network, guint port, guint priority, const gchar *protocol, const gchar *type, const gchar *username, const gchar *password); -void jingle_iceudp_add_local_candidate(JingleIceUdp *iceudp, JingleIceUdpCandidate *candidate); -GList *jingle_iceudp_get_remote_candidates(JingleIceUdp *iceudp); - -#ifdef __cplusplus -} -#endif G_END_DECLS
--- a/libpurple/protocols/jabber/jingle/jingle.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/jingle/jingle.c Fri Jan 25 02:22:38 2013 -0500 @@ -29,6 +29,7 @@ #include "content.h" #include "debug.h" #include "jingle.h" +#include "google/google_p2p.h" #include "session.h" #include "iceudp.h" #include "rawudp.h" @@ -58,6 +59,8 @@ #ifdef USE_VV else if (!strcmp(type, JINGLE_APP_RTP)) return JINGLE_TYPE_RTP; + else if (!strcmp(type, NS_GOOGLE_TRANSPORT_P2P)) + return JINGLE_TYPE_GOOGLE_P2P; #endif #if 0 else if (!strcmp(type, JINGLE_APP_FT)) @@ -429,18 +432,14 @@ jingle_actions[action_type].handler(session, jingle); } -static void -jingle_terminate_sessions_gh(gpointer key, gpointer value, gpointer user_data) -{ - g_object_unref(value); -} - void jingle_terminate_sessions(JabberStream *js) { - if (js->sessions) - g_hash_table_foreach(js->sessions, - jingle_terminate_sessions_gh, NULL); + if (js->sessions) { + GList *list = g_hash_table_get_values(js->sessions); + for (; list; list = g_list_delete_link(list, list)) + g_object_unref(list->data); + } } #ifdef USE_VV
--- a/libpurple/protocols/jabber/jingle/jingle.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/jingle/jingle.h Fri Jan 25 02:22:38 2013 -0500 @@ -30,10 +30,6 @@ G_BEGIN_DECLS -#ifdef __cplusplus -extern "C" { -#endif - #define JINGLE "urn:xmpp:jingle:1" #define JINGLE_ERROR "urn:xmpp:jingle:errors:0" #define JINGLE_APP_FT "urn:xmpp:jingle:apps:file-transfer:1" @@ -86,10 +82,6 @@ const gchar *relay_username, const gchar *relay_password, guint *num_params); #endif -#ifdef __cplusplus -} -#endif - G_END_DECLS #endif /* PURPLE_JABBER_JINGLE_H */
--- a/libpurple/protocols/jabber/jingle/rawudp.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/jingle/rawudp.c Fri Jan 25 02:22:38 2013 -0500 @@ -45,6 +45,8 @@ static void jingle_rawudp_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static JingleTransport *jingle_rawudp_parse_internal(xmlnode *rawudp); static xmlnode *jingle_rawudp_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action); +static void jingle_rawudp_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate); +static GList *jingle_rawudp_get_remote_candidates(JingleTransport *transport); static JingleTransportClass *parent_class = NULL; @@ -137,6 +139,8 @@ klass->parent_class.to_xml = jingle_rawudp_to_xml_internal; klass->parent_class.parse = jingle_rawudp_parse_internal; klass->parent_class.transport_type = JINGLE_TRANSPORT_RAWUDP; + klass->parent_class.add_local_candidate = jingle_rawudp_add_local_candidate; + klass->parent_class.get_remote_candidates = jingle_rawudp_get_remote_candidates; g_object_class_install_property(gobject_class, PROP_LOCAL_CANDIDATES, g_param_spec_pointer("local-candidates", @@ -218,36 +222,58 @@ } } -void -jingle_rawudp_add_local_candidate(JingleRawUdp *rawudp, JingleRawUdpCandidate *candidate) +static void +jingle_rawudp_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate) { - GList *iter = rawudp->priv->local_candidates; + JingleRawUdp *rawudp = JINGLE_RAWUDP(transport); + gchar *ip; + JingleRawUdpCandidate *rawudp_candidate; + GList *iter; - for (; iter; iter = g_list_next(iter)) { + ip = purple_media_candidate_get_ip(candidate); + rawudp_candidate = jingle_rawudp_candidate_new(id, generation, + purple_media_candidate_get_component_id(candidate), + ip, purple_media_candidate_get_port(candidate)); + g_free(ip); + + for (iter = rawudp->priv->local_candidates; iter; iter = g_list_next(iter)) { JingleRawUdpCandidate *c = iter->data; - if (!strcmp(c->id, candidate->id)) { - guint generation = c->generation + 1; + if (!strcmp(c->id, id)) { + generation = c->generation + 1; g_boxed_free(JINGLE_TYPE_RAWUDP_CANDIDATE, c); rawudp->priv->local_candidates = g_list_delete_link( rawudp->priv->local_candidates, iter); - candidate->generation = generation; + rawudp_candidate->generation = generation; rawudp->priv->local_candidates = g_list_append( - rawudp->priv->local_candidates, candidate); + rawudp->priv->local_candidates, rawudp_candidate); return; } } rawudp->priv->local_candidates = g_list_append( - rawudp->priv->local_candidates, candidate); + rawudp->priv->local_candidates, rawudp_candidate); } -GList * -jingle_rawudp_get_remote_candidates(JingleRawUdp *rawudp) +static GList * +jingle_rawudp_get_remote_candidates(JingleTransport *transport) { - return g_list_copy(rawudp->priv->remote_candidates); + JingleRawUdp *rawudp = JINGLE_RAWUDP(transport); + GList *candidates = rawudp->priv->remote_candidates; + GList *ret = NULL; + + for (; candidates; candidates = g_list_next(candidates)) { + JingleRawUdpCandidate *candidate = candidates->data; + ret = g_list_append(ret, purple_media_candidate_new("", + candidate->component, + PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX, + PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, + candidate->ip, candidate->port)); + } + + return ret; } static JingleRawUdpCandidate *
--- a/libpurple/protocols/jabber/jingle/rawudp.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/jingle/rawudp.h Fri Jan 25 02:22:38 2013 -0500 @@ -77,10 +77,6 @@ * about this candidate */ }; -#ifdef __cplusplus -extern "C" { -#endif - GType jingle_rawudp_candidate_get_type(void); /** @@ -92,12 +88,6 @@ JingleRawUdpCandidate *jingle_rawudp_candidate_new(const gchar *id, guint generation, guint component, const gchar *ip, guint port); -void jingle_rawudp_add_local_candidate(JingleRawUdp *rawudp, JingleRawUdpCandidate *candidate); -GList *jingle_rawudp_get_remote_candidates(JingleRawUdp *rawudp); - -#ifdef __cplusplus -} -#endif G_END_DECLS
--- a/libpurple/protocols/jabber/jingle/rtp.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/jingle/rtp.c Fri Jan 25 02:22:38 2013 -0500 @@ -28,6 +28,7 @@ #include "jabber.h" #include "jingle.h" +#include "google/google_p2p.h" #include "media.h" #include "mediamanager.h" #include "iceudp.h" @@ -226,134 +227,23 @@ return media; } -static JingleRawUdpCandidate * -jingle_rtp_candidate_to_rawudp(JingleSession *session, guint generation, - PurpleMediaCandidate *candidate) -{ - gchar *id = jabber_get_next_id(jingle_session_get_js(session)); - gchar *ip = purple_media_candidate_get_ip(candidate); - JingleRawUdpCandidate *rawudp_candidate = - jingle_rawudp_candidate_new(id, generation, - purple_media_candidate_get_component_id(candidate), - ip, purple_media_candidate_get_port(candidate)); - g_free(ip); - g_free(id); - return rawudp_candidate; -} - -static JingleIceUdpCandidate * -jingle_rtp_candidate_to_iceudp(JingleSession *session, guint generation, - PurpleMediaCandidate *candidate) -{ - gchar *id = jabber_get_next_id(jingle_session_get_js(session)); - gchar *ip = purple_media_candidate_get_ip(candidate); - gchar *username = purple_media_candidate_get_username(candidate); - gchar *password = purple_media_candidate_get_password(candidate); - PurpleMediaCandidateType type = - purple_media_candidate_get_candidate_type(candidate); - - JingleIceUdpCandidate *iceudp_candidate = jingle_iceudp_candidate_new( - purple_media_candidate_get_component_id(candidate), - purple_media_candidate_get_foundation(candidate), - generation, id, ip, 0, - purple_media_candidate_get_port(candidate), - purple_media_candidate_get_priority(candidate), "udp", - type == PURPLE_MEDIA_CANDIDATE_TYPE_HOST ? "host" : - type == PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX ? "srflx" : - type == PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX ? "prflx" : - type == PURPLE_MEDIA_CANDIDATE_TYPE_RELAY ? "relay" : - "", username, password); - iceudp_candidate->reladdr = - purple_media_candidate_get_base_ip(candidate); - iceudp_candidate->relport = - purple_media_candidate_get_base_port(candidate); - g_free(password); - g_free(username); - g_free(ip); - g_free(id); - return iceudp_candidate; -} - static JingleTransport * -jingle_rtp_candidates_to_transport(JingleSession *session, GType type, guint generation, GList *candidates) +jingle_rtp_candidates_to_transport(JingleSession *session, const gchar *type, guint generation, GList *candidates) { - if (type == JINGLE_TYPE_RAWUDP) { - JingleTransport *transport = jingle_transport_create(JINGLE_TRANSPORT_RAWUDP); - JingleRawUdpCandidate *rawudp_candidate; - for (; candidates; candidates = g_list_next(candidates)) { - PurpleMediaCandidate *candidate = candidates->data; - rawudp_candidate = jingle_rtp_candidate_to_rawudp( - session, generation, candidate); - jingle_rawudp_add_local_candidate( - JINGLE_RAWUDP(transport), - rawudp_candidate); - } - return transport; - } else if (type == JINGLE_TYPE_ICEUDP) { - JingleTransport *transport = jingle_transport_create(JINGLE_TRANSPORT_ICEUDP); - JingleIceUdpCandidate *iceudp_candidate; - for (; candidates; candidates = g_list_next(candidates)) { - PurpleMediaCandidate *candidate = candidates->data; - iceudp_candidate = jingle_rtp_candidate_to_iceudp( - session, generation, candidate); - jingle_iceudp_add_local_candidate( - JINGLE_ICEUDP(transport), - iceudp_candidate); - } - return transport; - } else { + JingleTransport *transport; + + transport = jingle_transport_create(type); + if (!transport) return NULL; - } -} - -static GList * -jingle_rtp_transport_to_candidates(JingleTransport *transport) -{ - const gchar *type = jingle_transport_get_transport_type(transport); - GList *ret = NULL; - if (!strcmp(type, JINGLE_TRANSPORT_RAWUDP)) { - GList *candidates = jingle_rawudp_get_remote_candidates(JINGLE_RAWUDP(transport)); - for (; candidates; candidates = g_list_delete_link(candidates, candidates)) { - JingleRawUdpCandidate *candidate = candidates->data; - ret = g_list_append(ret, purple_media_candidate_new( - "", candidate->component, - PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX, - PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, - candidate->ip, candidate->port)); - } - - return ret; - } else if (!strcmp(type, JINGLE_TRANSPORT_ICEUDP)) { - GList *candidates = jingle_iceudp_get_remote_candidates(JINGLE_ICEUDP(transport)); + for (; candidates; candidates = g_list_next(candidates)) { + PurpleMediaCandidate *candidate = candidates->data; + gchar *id = jabber_get_next_id(jingle_session_get_js(session)); + jingle_transport_add_local_candidate(transport, id, generation, candidate); + g_free(id); + } - for (; candidates; candidates = g_list_delete_link(candidates, candidates)) { - JingleIceUdpCandidate *candidate = candidates->data; - PurpleMediaCandidate *new_candidate = purple_media_candidate_new( - candidate->foundation, candidate->component, - !strcmp(candidate->type, "host") ? - PURPLE_MEDIA_CANDIDATE_TYPE_HOST : - !strcmp(candidate->type, "srflx") ? - PURPLE_MEDIA_CANDIDATE_TYPE_SRFLX : - !strcmp(candidate->type, "prflx") ? - PURPLE_MEDIA_CANDIDATE_TYPE_PRFLX : - !strcmp(candidate->type, "relay") ? - PURPLE_MEDIA_CANDIDATE_TYPE_RELAY : 0, - PURPLE_MEDIA_NETWORK_PROTOCOL_UDP, - candidate->ip, candidate->port); - g_object_set(new_candidate, - "base-ip", candidate->reladdr, - "base-port", candidate->relport, - "username", candidate->username, - "password", candidate->password, - "priority", candidate->priority, NULL); - ret = g_list_append(ret, new_candidate); - } - - return ret; - } else { - return NULL; - } + return transport; } static void jingle_rtp_ready(JingleSession *session); @@ -378,10 +268,9 @@ oldtransport = jingle_content_get_transport(content); candidates = purple_media_get_local_candidates(media, sid, name); - transport = JINGLE_TRANSPORT(jingle_rtp_candidates_to_transport( - session, JINGLE_IS_RAWUDP(oldtransport) ? - JINGLE_TYPE_RAWUDP : JINGLE_TYPE_ICEUDP, - 0, candidates)); + transport = jingle_rtp_candidates_to_transport( + session, jingle_transport_get_transport_type(oldtransport), + 0, candidates); g_list_free(candidates); g_object_unref(oldtransport); @@ -404,9 +293,9 @@ static void jingle_rtp_new_candidate_cb(PurpleMedia *media, gchar *sid, gchar *name, PurpleMediaCandidate *candidate, JingleSession *session) { - JingleContent *content = jingle_session_find_content( - session, sid, NULL); + JingleContent *content = jingle_session_find_content(session, sid, NULL); JingleTransport *transport; + gchar *id; purple_debug_info("jingle-rtp", "jingle_rtp_new_candidate_cb\n"); @@ -419,19 +308,13 @@ transport = jingle_content_get_transport(content); - if (JINGLE_IS_ICEUDP(transport)) - jingle_iceudp_add_local_candidate(JINGLE_ICEUDP(transport), - jingle_rtp_candidate_to_iceudp( - session, 1, candidate)); - else if (JINGLE_IS_RAWUDP(transport)) - jingle_rawudp_add_local_candidate(JINGLE_RAWUDP(transport), - jingle_rtp_candidate_to_rawudp( - session, 1, candidate)); + id = jabber_get_next_id(jingle_session_get_js(session)); + jingle_transport_add_local_candidate(transport, id, 1, candidate); + g_free(id); g_object_unref(transport); - jabber_iq_send(jingle_session_to_packet(session, - JINGLE_TRANSPORT_INFO)); + jabber_iq_send(jingle_session_to_packet(session, JINGLE_TRANSPORT_INFO)); } static void @@ -604,6 +487,8 @@ transmitter = "rawudp"; else if (JINGLE_IS_ICEUDP(transport)) transmitter = "nice"; + else if (JINGLE_IS_GOOGLE_P2P(transport)) + transmitter = "nice"; else transmitter = "notransmitter"; g_object_unref(transport); @@ -827,7 +712,7 @@ transport = jingle_transport_parse( xmlnode_get_child(xmlcontent, "transport")); description = xmlnode_get_child(xmlcontent, "description"); - candidates = jingle_rtp_transport_to_candidates(transport); + candidates = jingle_transport_get_remote_candidates(transport); codecs = jingle_rtp_parse_codecs(description); name = jingle_content_get_name(content); remote_jid = jingle_session_get_remote_jid(session); @@ -863,7 +748,7 @@ JingleSession *session = jingle_content_get_session(content); JingleTransport *transport = jingle_transport_parse( xmlnode_get_child(xmlcontent, "transport")); - GList *candidates = jingle_rtp_transport_to_candidates(transport); + GList *candidates = jingle_transport_get_remote_candidates(transport); gchar *name = jingle_content_get_name(content); gchar *remote_jid = jingle_session_get_remote_jid(session); @@ -973,6 +858,8 @@ transport_type = JINGLE_TRANSPORT_ICEUDP; } else if (jabber_resource_has_capability(jbr, JINGLE_TRANSPORT_RAWUDP)) { transport_type = JINGLE_TRANSPORT_RAWUDP; + } else if (jabber_resource_has_capability(jbr, NS_GOOGLE_TRANSPORT_P2P)) { + transport_type = NS_GOOGLE_TRANSPORT_P2P; } else { purple_debug_error("jingle-rtp", "Resource doesn't support " "the same transport types\n");
--- a/libpurple/protocols/jabber/jingle/rtp.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/jingle/rtp.h Fri Jan 25 02:22:38 2013 -0500 @@ -65,10 +65,6 @@ JingleRtpPrivate *priv; /**< The private data of this object. */ }; -#ifdef __cplusplus -extern "C" { -#endif - /** * Gets the rtp class's GType * @@ -84,10 +80,6 @@ PurpleMediaSessionType type); void jingle_rtp_terminate_session(JabberStream *js, const gchar *who); -#ifdef __cplusplus -} -#endif - G_END_DECLS #endif /* USE_VV */
--- a/libpurple/protocols/jabber/jingle/session.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/jingle/session.h Fri Jan 25 02:22:38 2013 -0500 @@ -62,10 +62,6 @@ struct _JingleContent; -#ifdef __cplusplus -extern "C" { -#endif - /** * Gets the session class's GType * @@ -109,10 +105,6 @@ JabberIq *jingle_session_terminate_packet(JingleSession *session, const gchar *reason); JabberIq *jingle_session_redirect_packet(JingleSession *session, const gchar *sid); -#ifdef __cplusplus -} -#endif - G_END_DECLS #endif /* PURPLE_JABBER_JINGLE_SESSION_H */
--- a/libpurple/protocols/jabber/jingle/transport.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/jingle/transport.c Fri Jan 25 02:22:38 2013 -0500 @@ -44,6 +44,8 @@ static void jingle_transport_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); JingleTransport *jingle_transport_parse_internal(xmlnode *transport); xmlnode *jingle_transport_to_xml_internal(JingleTransport *transport, xmlnode *content, JingleActionType action); +static void jingle_transport_add_local_candidate_internal(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate); +static GList *jingle_transport_get_remote_candidates_internal(JingleTransport *transport); static GObjectClass *parent_class = NULL; @@ -85,6 +87,8 @@ gobject_class->get_property = jingle_transport_get_property; klass->to_xml = jingle_transport_to_xml_internal; klass->parse = jingle_transport_parse_internal; + klass->add_local_candidate = jingle_transport_add_local_candidate_internal; + klass->get_remote_candidates = jingle_transport_get_remote_candidates_internal; g_type_class_add_private(klass, sizeof(JingleTransportPrivate)); } @@ -143,6 +147,32 @@ return JINGLE_TRANSPORT_GET_CLASS(transport)->transport_type; } +void +jingle_transport_add_local_candidate(JingleTransport *transport, const gchar *id, + guint generation, PurpleMediaCandidate *candidate) +{ + JINGLE_TRANSPORT_GET_CLASS(transport)->add_local_candidate(transport, id, + generation, candidate); +} + +void +jingle_transport_add_local_candidate_internal(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate) +{ + /* Nothing to do */ +} + +GList * +jingle_transport_get_remote_candidates(JingleTransport *transport) +{ + return JINGLE_TRANSPORT_GET_CLASS(transport)->get_remote_candidates(transport); +} + +static GList * +jingle_transport_get_remote_candidates_internal(JingleTransport *transport) +{ + return NULL; +} + JingleTransport * jingle_transport_parse_internal(xmlnode *transport) {
--- a/libpurple/protocols/jabber/jingle/transport.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/jingle/transport.h Fri Jan 25 02:22:38 2013 -0500 @@ -55,6 +55,8 @@ const gchar *transport_type; xmlnode *(*to_xml) (JingleTransport *transport, xmlnode *content, JingleActionType action); JingleTransport *(*parse) (xmlnode *transport); + void (*add_local_candidate) (JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate); + GList *(*get_remote_candidates) (JingleTransport *transport); }; /** The transport class's private data */ @@ -64,10 +66,6 @@ JingleTransportPrivate *priv; /**< The private data of this object. */ }; -#ifdef __cplusplus -extern "C" { -#endif - /** * Gets the transport class's GType * @@ -77,15 +75,13 @@ JingleTransport *jingle_transport_create(const gchar *type); const gchar *jingle_transport_get_transport_type(JingleTransport *transport); -void jingle_transport_add_candidate(); + +void jingle_transport_add_local_candidate(JingleTransport *transport, const gchar *id, guint generation, PurpleMediaCandidate *candidate); +GList *jingle_transport_get_remote_candidates(JingleTransport *transport); JingleTransport *jingle_transport_parse(xmlnode *transport); xmlnode *jingle_transport_to_xml(JingleTransport *transport, xmlnode *content, JingleActionType action); -#ifdef __cplusplus -} -#endif - G_END_DECLS #endif /* PURPLE_JABBER_JINGLE_TRANSPORT_H */
--- a/libpurple/protocols/jabber/namespaces.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/namespaces.h Fri Jan 25 02:22:38 2013 -0500 @@ -109,4 +109,9 @@ #define NS_GOOGLE_SESSION_PHONE "http://www.google.com/session/phone" #define NS_GOOGLE_SESSION_VIDEO "http://www.google.com/session/video" +#define NS_GOOGLE_TRANSPORT_P2P "http://www.google.com/transport/p2p" + +/* Apple extension(s) */ +#define NS_APPLE_IDLE "http://www.apple.com/xmpp/idle" + #endif /* PURPLE_JABBER_NAMESPACES_H_ */
--- a/libpurple/protocols/jabber/presence.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/presence.c Fri Jan 25 02:22:38 2013 -0500 @@ -1017,7 +1017,7 @@ pih(js, &presence, child); } - if (presence.delayed && presence.idle) { + if (presence.delayed && presence.idle && presence.adjust_idle_for_delay) { /* Delayed and idle, so update idle time */ presence.idle = presence.idle + (time(NULL) - presence.sent); } @@ -1172,11 +1172,33 @@ } static void +parse_apple_idle(JabberStream *js, JabberPresence *presence, xmlnode *x) +{ + xmlnode *since = xmlnode_get_child(x, "idle-since"); + if (since) { + char *stamp = xmlnode_get_data_unescaped(since); + if (stamp) { + time_t tstamp = purple_str_to_time(stamp, TRUE, NULL, NULL, NULL); + if (tstamp != 0) { + presence->idle = time(NULL) - tstamp; + presence->adjust_idle_for_delay = FALSE; + if(presence->idle < 0) { + purple_debug_warning("jabber", "Received bogus idle timestamp %s\n", stamp); + presence->idle = 0; + } + } + } + g_free(stamp); + } +} + +static void parse_idle(JabberStream *js, JabberPresence *presence, xmlnode *query) { const gchar *seconds = xmlnode_get_attrib(query, "seconds"); if (seconds) { presence->idle = atoi(seconds); + presence->adjust_idle_for_delay = TRUE; if (presence->idle < 0) { purple_debug_warning("jabber", "Received bogus idle time %s\n", seconds); presence->idle = 0; @@ -1276,6 +1298,9 @@ 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); + + /* Apple idle */ + jabber_presence_register_handler("x", NS_APPLE_IDLE, parse_apple_idle); } void jabber_presence_uninit(void)
--- a/libpurple/protocols/jabber/presence.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/presence.h Fri Jan 25 02:22:38 2013 -0500 @@ -71,6 +71,7 @@ gboolean delayed; time_t sent; int idle; + gboolean adjust_idle_for_delay; }; typedef void (JabberPresenceHandler)(JabberStream *js, JabberPresence *presence,
--- a/libpurple/protocols/jabber/useravatar.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/jabber/useravatar.c Fri Jan 25 02:22:38 2013 -0500 @@ -22,6 +22,7 @@ */ #include "internal.h" +#include "obsolete.h" #include "useravatar.h" #include "pep.h"
--- a/libpurple/protocols/msn/msn.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/msn/msn.c Fri Jan 25 02:22:38 2013 -0500 @@ -27,6 +27,7 @@ #include "debug.h" #include "request.h" +#include "obsolete.h" #include "accountopt.h" #include "contact.h" @@ -2782,7 +2783,7 @@ { 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); + id = purple_imgstore_new_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_html(user_info, NULL, buf); }
--- a/libpurple/protocols/msn/notification.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/msn/notification.c Fri Jan 25 02:22:38 2013 -0500 @@ -1592,7 +1592,7 @@ /* Disconnect others, if MPOP is disabled */ if (is_me && !session->enable_mpop - && strncasecmp(id + 1, session->guid, 36) != 0) { + && g_ascii_strncasecmp(id + 1, session->guid, 36) != 0) { purple_debug_info("msn", "Disconnecting Endpoint %s\n", id); tmp = g_strdup_printf("%s;%s", user->passport, id);
--- a/libpurple/protocols/msn/session.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/msn/session.c Fri Jan 25 02:22:38 2013 -0500 @@ -24,6 +24,7 @@ #include "internal.h" #include "debug.h" +#include "obsolete.h" #include "error.h" #include "msnutils.h"
--- a/libpurple/protocols/msn/slp.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/msn/slp.c Fri Jan 25 02:22:38 2013 -0500 @@ -24,6 +24,7 @@ #include "internal.h" #include "debug.h" +#include "obsolete.h" #include "slp.h" #include "slpcall.h"
--- a/libpurple/protocols/msn/switchboard.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/msn/switchboard.c Fri Jan 25 02:22:38 2013 -0500 @@ -827,7 +827,7 @@ return; } - imgid = purple_imgstore_add_with_id(image_data, image_len, NULL); + imgid = purple_imgstore_new_with_id(image_data, image_len, NULL); image_msg = g_strdup_printf("<IMG SRC='" PURPLE_STORED_IMAGE_PROTOCOL "%d'>", imgid);
--- a/libpurple/protocols/msn/user.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/msn/user.c Fri Jan 25 02:22:38 2013 -0500 @@ -761,17 +761,7 @@ str = purple_normalize_nocase(NULL, msn_user_get_passport(user)); pass = g_strdup(str); -#if GLIB_CHECK_VERSION(2,16,0) result = g_strcmp0(pass, purple_normalize_nocase(NULL, passport)); -#else - str = purple_normalize_nocase(NULL, passport); - if (!pass) - result = -(pass != str); - else if (!str) - result = pass != str; - else - result = strcmp(pass, str); -#endif /* GLIB < 2.16.0 */ g_free(pass);
--- a/libpurple/protocols/mxit/actions.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/mxit/actions.c Fri Jan 25 02:22:38 2013 -0500 @@ -43,11 +43,12 @@ */ static void mxit_profile_cb( PurpleConnection* gc, PurpleRequestFields* fields ) { - struct MXitSession* session = purple_connection_get_protocol_data( gc ) ; + struct MXitSession* session = purple_connection_get_protocol_data( gc ); PurpleRequestField* field = NULL; const char* name = NULL; const char* bday = NULL; const char* err = NULL; + GList* entry = NULL; purple_debug_info( MXIT_PLUGIN_ID, "mxit_profile_cb\n" ); @@ -166,6 +167,14 @@ g_string_append( attributes, attrib ); acount++; + /* relationship status */ + field = purple_request_fields_get_field( fields, "relationship" ); + entry = g_list_first( purple_request_field_list_get_selected( field ) ); + profile->relationship = atoi( purple_request_field_list_get_data( field, entry->data ) ); + g_snprintf( attrib, sizeof( attrib ), "\01%s\01%i\01%i", CP_PROFILE_RELATIONSHIP, CP_PROFILE_TYPE_SHORT, profile->relationship ); + g_string_append( attributes, attrib ); + acount++; + /* update flags */ field = purple_request_fields_get_field( fields, "searchable" ); if ( purple_request_field_bool_get_value( field ) ) /* is searchable -> clear not-searchable flag */ @@ -253,6 +262,22 @@ field = purple_request_field_string_new( "whereami", _( "Where I Live" ), profile->whereami, FALSE); purple_request_field_group_add_field( public_group, field ); + /* relationship status */ + field = purple_request_field_list_new( "relationship", _( "Relationship Status" ) ); + purple_request_field_list_set_multi_select( field, FALSE ); + purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_UNKNOWN ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_UNKNOWN ) ); + purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_DONTSAY ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_DONTSAY ) ); + purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_SINGLE ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_SINGLE ) ); + purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_INVOLVED ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_INVOLVED ) ); + purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_ENGAGED ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_ENGAGED ) ); + purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_MARRIED ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_MARRIED ) ); + purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_COMPLICATED ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_COMPLICATED ) ); + purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_WIDOWED ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_WIDOWED ) ); + purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_SEPARATED ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_SEPARATED ) ); + purple_request_field_list_add_icon( field, mxit_relationship_to_name( MXIT_RELATIONSHIP_DIVORCED ), NULL, g_strdup_printf( "%i", MXIT_RELATIONSHIP_DIVORCED ) ); + purple_request_field_list_add_selected( field, mxit_relationship_to_name( profile->relationship ) ); + purple_request_field_group_add_field( public_group, field ); + purple_request_fields_add_group( fields, public_group ); }
--- a/libpurple/protocols/mxit/cipher.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/mxit/cipher.c Fri Jan 25 02:22:38 2013 -0500 @@ -163,6 +163,10 @@ /* base64 decode the message */ raw_message = purple_base64_decode( message, &raw_len ); + /* AES-encrypted data is always blocks of 16 bytes */ + if ( ( raw_len == 0 ) || ( raw_len % 16 != 0 ) ) + return NULL; + /* build the AES key */ ExpandKey( (unsigned char*) transport_layer_key( session ), (unsigned char*) exkey );
--- a/libpurple/protocols/mxit/formcmds.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/mxit/formcmds.c Fri Jan 25 02:22:38 2013 -0500 @@ -28,6 +28,7 @@ #include <glib.h> #include "purple.h" +#include "obsolete.h" #include "protocol.h" #include "mxit.h" @@ -106,7 +107,7 @@ } /* we now have the inline image, store a copy in the imagestore */ - id = purple_imgstore_add_with_id(g_memdup(url_text, len), len, NULL); + id = purple_imgstore_new_with_id(g_memdup(url_text, len), len, NULL); /* map the inline image id to purple image id */ intptr = g_malloc(sizeof(int)); @@ -334,7 +335,7 @@ if (img) { rawimg = purple_base64_decode(img, &rawimglen); //purple_util_write_data_to_file_absolute("/tmp/mxitinline.png", (char*) rawimg, rawimglen); - imgid = purple_imgstore_add_with_id(rawimg, rawimglen, NULL); + imgid = purple_imgstore_new_with_id(rawimg, rawimglen, NULL); g_string_append_printf(msg, "<img src=\"" PURPLE_STORED_IMAGE_PROTOCOL "%i\">", imgid); @@ -372,7 +373,7 @@ reply = g_hash_table_lookup(hash, "replymsg"); if (reply) { g_string_append_printf(msg, "\n"); - mxit_add_html_link(mx, reply, FALSE, _( "click here" )); + mxit_add_html_link(mx, purple_url_decode(reply), FALSE, _( "click here" )); } }
--- a/libpurple/protocols/mxit/login.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/mxit/login.c Fri Jan 25 02:22:38 2013 -0500 @@ -25,6 +25,7 @@ #include "internal.h" #include "purple.h" +#include "obsolete.h" #include "protocol.h" #include "mxit.h"
--- a/libpurple/protocols/mxit/markup.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/mxit/markup.c Fri Jan 25 02:22:38 2013 -0500 @@ -25,6 +25,7 @@ #include "internal.h" #include "purple.h" +#include "obsolete.h" #include "protocol.h" #include "mxit.h" @@ -59,7 +60,9 @@ }; -#define MXIT_VIBE_MSG_COLOR "#9933FF" +#define MXIT_VIBE_MSG_COLOR "#9933FF" +#define MXIT_FAREWELL_MSG_COLOR "#949494" + /* vibes */ static const char* vibes[] = { @@ -593,7 +596,7 @@ } /* we now have the emoticon, store it in the imagestore */ - id = purple_imgstore_add_with_id( em_data, em_size, NULL ); + id = purple_imgstore_new_with_id( em_data, em_size, NULL ); /* map the mxit emoticon id to purple image id */ intptr = g_malloc( sizeof( int ) ); @@ -1003,6 +1006,12 @@ break; } } + + if ( msgflags & CP_MSG_FAREWELL ) { + /* this is a farewell message */ + g_string_prepend( mx->msg, "<font color=\""MXIT_FAREWELL_MSG_COLOR"\"><i>" ); + g_string_append( mx->msg, "</i></font>" ); + } }
--- a/libpurple/protocols/mxit/mxit.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/mxit/mxit.c Fri Jan 25 02:22:38 2013 -0500 @@ -427,7 +427,7 @@ char* statusmsg2; /* Handle mood changes */ - if ( purple_status_type_get_primitive(purple_status_get_type( status ) ) == PURPLE_STATUS_MOOD ) { + if ( purple_status_type_get_primitive( purple_status_get_type( status ) ) == PURPLE_STATUS_MOOD ) { const char* moodid = purple_status_get_attr_string( status, PURPLE_MOOD_NAME ); int mood;
--- a/libpurple/protocols/mxit/profile.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/mxit/profile.c Fri Jan 25 02:22:38 2013 -0500 @@ -35,6 +35,40 @@ /*------------------------------------------------------------------------ + * Return the MXit Relationship status as a string. + * + * @param id The Relationship status value (see profile.h) + * @return The relationship status as a text string. + */ +const char* mxit_relationship_to_name( short id ) +{ + switch ( id ) { + case MXIT_RELATIONSHIP_UNKNOWN : + return _( "Unknown" ); + case MXIT_RELATIONSHIP_DONTSAY : + return _( "Don't want to say" ); + case MXIT_RELATIONSHIP_SINGLE : + return _( "Single" ); + case MXIT_RELATIONSHIP_INVOLVED : + return _( "In a relationship" ); + case MXIT_RELATIONSHIP_ENGAGED : + return _( "Engaged" ); + case MXIT_RELATIONSHIP_MARRIED : + return _( "Married" ); + case MXIT_RELATIONSHIP_COMPLICATED : + return _( "It's complicated" ); + case MXIT_RELATIONSHIP_WIDOWED : + return _( "Widowed" ); + case MXIT_RELATIONSHIP_SEPARATED : + return _( "Separated" ); + case MXIT_RELATIONSHIP_DIVORCED : + return _( "Divorced" ); + default : + return ""; + } +} + +/*------------------------------------------------------------------------ * Returns true if it is a valid date. * * @param bday Date-of-Birth string (YYYY-MM-DD) @@ -130,7 +164,7 @@ age = now.tm_year - bdate.tm_year; if ( now.tm_mon < bdate.tm_mon ) /* is before month of birth */ age--; - else if ( (now.tm_mon == bdate.tm_mon ) && ( now.tm_mday < bdate.tm_mday ) ) /* before birthday in current month */ + else if ( ( now.tm_mon == bdate.tm_mon ) && ( now.tm_mday < bdate.tm_mday ) ) /* before birthday in current month */ age--; return age; @@ -195,6 +229,8 @@ if ( *profile->whereami ) purple_notify_user_info_add_pair_plaintext( info, _( "Where I Live" ), profile->whereami ); + purple_notify_user_info_add_pair_plaintext( info, _( "Relationship Status" ), mxit_relationship_to_name( profile->relationship ) ); + purple_notify_user_info_add_section_break( info ); if ( contact ) { @@ -236,7 +272,7 @@ img_text = g_strdup_printf( "<img src='" PURPLE_STORED_IMAGE_PROTOCOL "%d'>", contact->imgid ); purple_notify_user_info_add_pair_html( info, _( "Photo" ), img_text ); - g_free(img_text); + g_free( img_text ); } if ( contact->statusMsg ) {
--- a/libpurple/protocols/mxit/profile.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/mxit/profile.h Fri Jan 25 02:22:38 2013 -0500 @@ -29,6 +29,18 @@ #include <glib.h> +/* MXit relationship status types */ +#define MXIT_RELATIONSHIP_UNKNOWN 0 +#define MXIT_RELATIONSHIP_DONTSAY 1 +#define MXIT_RELATIONSHIP_SINGLE 2 +#define MXIT_RELATIONSHIP_INVOLVED 3 +#define MXIT_RELATIONSHIP_ENGAGED 4 +#define MXIT_RELATIONSHIP_MARRIED 5 +#define MXIT_RELATIONSHIP_COMPLICATED 6 +#define MXIT_RELATIONSHIP_WIDOWED 7 +#define MXIT_RELATIONSHIP_SEPARATED 8 +#define MXIT_RELATIONSHIP_DIVORCED 9 + struct MXitProfile { /* required */ char loginname[64]; /* name user uses to log into MXit with (aka 'mxitid') */ @@ -47,6 +59,7 @@ char regcountry[3]; /* user's registered country code */ char whereami[51]; /* where am I / where I live */ char aboutme[513]; /* about me */ + int relationship; /* relationship status */ int flags; /* user's profile flags */ gint64 lastonline; /* user's last-online timestamp */ @@ -55,6 +68,7 @@ struct MXitSession; void mxit_show_profile( struct MXitSession* session, const char* username, struct MXitProfile* profile ); void mxit_show_search_results( struct MXitSession* session, int searchType, int maxResults, GList* entries ); +const char* mxit_relationship_to_name( short id ); gboolean validateDate( const char* bday );
--- a/libpurple/protocols/mxit/protocol.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/mxit/protocol.c Fri Jan 25 02:22:38 2013 -0500 @@ -25,6 +25,7 @@ #include "internal.h" #include "purple.h" +#include "obsolete.h" #include "protocol.h" #include "mxit.h" @@ -458,7 +459,7 @@ packet->headerlen = 0; /* create generic packet header */ - hlen = snprintf( header, sizeof( header ), "id=%s%c", purple_account_get_username( session->acc), CP_REC_TERM ); /* client msisdn */ + hlen = snprintf( header, sizeof( header ), "id=%s%c", purple_account_get_username( session->acc ), CP_REC_TERM ); /* client msisdn */ if ( session->http ) { /* http connection only */ @@ -1135,7 +1136,7 @@ * @param id The identifier of the event (received in message) * @param event Identified the type of event */ -void mxit_send_msgevent( struct MXitSession* session, const char* to, const char* id, int event) +void mxit_send_msgevent( struct MXitSession* session, const char* to, const char* id, int event ) { char data[CP_MAX_PACKET]; int datalen; @@ -1450,7 +1451,7 @@ const char* statusmsg; const char* profilelist[] = { CP_PROFILE_BIRTHDATE, CP_PROFILE_GENDER, CP_PROFILE_FULLNAME, CP_PROFILE_TITLE, CP_PROFILE_FIRSTNAME, CP_PROFILE_LASTNAME, CP_PROFILE_EMAIL, - CP_PROFILE_MOBILENR, CP_PROFILE_WHEREAMI, CP_PROFILE_ABOUTME, CP_PROFILE_FLAGS }; + CP_PROFILE_MOBILENR, CP_PROFILE_WHEREAMI, CP_PROFILE_ABOUTME, CP_PROFILE_RELATIONSHIP, CP_PROFILE_FLAGS }; purple_account_set_int( session->acc, MXIT_CONFIG_STATE, MXIT_STATE_LOGIN ); @@ -1877,6 +1878,10 @@ /* about me */ g_strlcpy( profile->aboutme, fvalue, sizeof( profile->aboutme ) ); } + else if ( strcmp( CP_PROFILE_RELATIONSHIP, fname ) == 0) { + /* relatinship status */ + profile->relationship = strtol( fvalue, NULL, 10 ); + } else { /* invalid profile attribute */ purple_debug_error( MXIT_PLUGIN_ID, "Invalid profile attribute received '%s' \n", fname ); @@ -2030,6 +2035,47 @@ g_list_foreach( entries, (GFunc)g_free, NULL ); } +/*------------------------------------------------------------------------ + * Process a received message event packet. + * + * @param session The MXit session object + * @param records The packet's data records + * @param rcount The number of data records + */ +static void mxit_parse_cmd_msgevent( struct MXitSession* session, struct record** records, int rcount ) +{ + int event; + + /* + * contactAddress \1 dateTime \1 id \1 event + */ + + /* strip off dummy domain */ + mxit_strip_domain( records[0]->fields[0]->data ); + + event = atoi( records[0]->fields[3]->data ); + + switch ( event ) { + case CP_MSGEVENT_TYPING : /* user is typing */ + case CP_MSGEVENT_ANGRY : /* user is typing angrily */ + serv_got_typing( session->con, records[0]->fields[0]->data, 0, PURPLE_TYPING ); + break; + + case CP_MSGEVENT_STOPPED : /* user has stopped typing */ + serv_got_typing_stopped( session->con, records[0]->fields[0]->data ); + break; + + case CP_MSGEVENT_ERASING : /* user is erasing text */ + case CP_MSGEVENT_DELIVERED : /* message was delivered */ + case CP_MSGEVENT_DISPLAYED : /* message was viewed */ + /* these are currently not supported by libPurple */ + break; + + default: + purple_debug_error( MXIT_PLUGIN_ID, "Unknown message event received (%i)\n", event ); + } +} + /*------------------------------------------------------------------------ * Return the length of a multimedia chunk @@ -2136,7 +2182,7 @@ contact = get_mxit_invite_contact( session, chunk.mxitid ); if ( contact ) { /* this is an invite (add image to the internal image store) */ - contact->imgid = purple_imgstore_add_with_id( g_memdup( chunk.data, chunk.length ), chunk.length, NULL ); + contact->imgid = purple_imgstore_new_with_id( g_memdup( chunk.data, chunk.length ), chunk.length, NULL ); /* show the profile */ mxit_show_profile( session, chunk.mxitid, contact->profile ); } @@ -2309,6 +2355,11 @@ mxit_parse_cmd_suggestcontacts( session, &packet->records[2], packet->rcount - 2 ); break; + case CP_CMD_GOT_MSGEVENT : + /* received message event */ + mxit_parse_cmd_msgevent( session, &packet->records[2], packet->rcount - 2 ); + break; + case CP_CMD_MOOD : /* mood update */ case CP_CMD_UPDATE :
--- a/libpurple/protocols/mxit/protocol.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/mxit/protocol.h Fri Jan 25 02:22:38 2013 -0500 @@ -77,9 +77,12 @@ #define MXIT_CF_VOICE 0x1000000 #define MXIT_CF_VIDEO 0x2000000 #define MXIT_CF_TOUCHSCREEN 0x4000000 +#define MXIT_CF_SVC_CONNECTION 0x8000000 +#define MXIT_CF_MXML 0x10000000 +#define MXIT_CF_TYPING_NOTIFY 0x20000000 /* Client features supported by this implementation */ -#define MXIT_CP_FEATURES ( MXIT_CF_FILE_TRANSFER | MXIT_CF_FILE_ACCESS | MXIT_CF_AUDIO | MXIT_CF_MARKUP | MXIT_CF_EXT_MARKUP | MXIT_CF_NO_GATEWAYS | MXIT_CF_IMAGES | MXIT_CF_COMMANDS | MXIT_CF_VIBES | MXIT_CF_MIDP2 ) +#define MXIT_CP_FEATURES ( MXIT_CF_FILE_TRANSFER | MXIT_CF_FILE_ACCESS | MXIT_CF_AUDIO | MXIT_CF_MARKUP | MXIT_CF_EXT_MARKUP | MXIT_CF_NO_GATEWAYS | MXIT_CF_IMAGES | MXIT_CF_COMMANDS | MXIT_CF_VIBES | MXIT_CF_MIDP2 | MXIT_CF_TYPING_NOTIFY ) #define MXIT_PING_INTERVAL ( 5 * 60 ) /* ping the server after X seconds of being idle (5 minutes) */ @@ -132,6 +135,7 @@ #define CP_CMD_SPLASHCLICK 0x001F /* (31) splash-screen clickthrough */ #define CP_CMD_STATUS 0x0020 /* (32) set shown presence & status */ #define CP_CMD_MSGEVENT 0x0023 /* (35) Raise message event */ +#define CP_CMD_GOT_MSGEVENT 0x0024 /* (36) Get message event */ #define CP_CMD_MOOD 0x0029 /* (41) set mood */ #define CP_CMD_KICK 0x002B /* (43) login kick */ #define CP_CMD_GRPCHAT_CREATE 0x002C /* (44) create new groupchat */ @@ -161,6 +165,7 @@ #define CP_MSG_RPLY_TL_ENCRYPT 0x0080 /* reply should be transport encrypted */ #define CP_MSG_MARKUP 0x0200 /* message may contain markup */ #define CP_MSG_EMOTICON 0x0400 /* message may contain custom emoticons */ +#define CP_MSG_FAREWELL 0x0800 /* this is a farewell message */ /* redirect types */ #define CP_REDIRECT_PERMANENT 1 /* permanent redirect */ @@ -178,6 +183,10 @@ /* message event types */ #define CP_MSGEVENT_DELIVERED 0x02 /* message was delivered */ #define CP_MSGEVENT_DISPLAYED 0x04 /* message was viewed */ +#define CP_MSGEVENT_TYPING 0x10 /* user is typing */ +#define CP_MSGEVENT_STOPPED 0x20 /* user has stopped typing */ +#define CP_MSGEVENT_ANGRY 0x40 /* user is typing angrily */ +#define CP_MSGEVENT_ERASING 0x80 /* user is erasing text */ /* extended profile attribute fields */ #define CP_PROFILE_BIRTHDATE "birthdate" /* Birthdate (String - ISO 8601 format) */ @@ -198,9 +207,11 @@ #define CP_PROFILE_LASTSEEN "lastseen" /* Last-Online timestamp */ #define CP_PROFILE_WHEREAMI "whereami" /* Where am I / Where I live */ #define CP_PROFILE_ABOUTME "aboutme" /* About me */ +#define CP_PROFILE_RELATIONSHIP "relationship" /* Relationship Status */ /* extended profile field types */ #define CP_PROFILE_TYPE_BOOL 0x02 /* boolean (0 or 1) */ +#define CP_PROFILE_TYPE_SHORT 0x04 /* short (16-bit) */ #define CP_PROFILE_TYPE_INT 0x05 /* integer (32-bit) */ #define CP_PROFILE_TYPE_LONG 0x06 /* long (64-bit) */ #define CP_PROFILE_TYPE_UTF8 0x0A /* UTF8 string */
--- a/libpurple/protocols/mxit/roster.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/mxit/roster.h Fri Jan 25 02:22:38 2013 -0500 @@ -82,6 +82,7 @@ /* MXit presence flags */ #define MXIT_PFLAG_VOICE 0x1 #define MXIT_PFLAG_VIDEO 0x2 +#define MXIT_PFLAG_TYPING 0x4 /* Subscription types */
--- a/libpurple/protocols/mxit/splashscreen.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/mxit/splashscreen.c Fri Jan 25 02:22:38 2013 -0500 @@ -184,7 +184,7 @@ char buf[128]; /* Add splash-image to imagestore */ - imgid = purple_imgstore_add_with_id(g_memdup(imgdata, imglen), imglen, NULL); + imgid = purple_imgstore_new_with_id(g_memdup(imgdata, imglen), imglen, NULL); /* Generate and display message */ g_snprintf(buf, sizeof(buf),
--- a/libpurple/protocols/myspace/message.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/myspace/message.c Fri Jan 25 02:22:38 2013 -0500 @@ -129,7 +129,7 @@ * * @param first_key The first argument (a key), or NULL to take all arguments * from argp. - * @param argp A va_list of variadic arguments, already started with va_start(). Will be va_end()'d. + * @param argp A va_list of variadic arguments, already started with va_start(). * @return New MsimMessage *, must be freed with msim_msg_free(). * * For internal use - users probably want msim_msg_new() or msim_send(). @@ -211,7 +211,6 @@ break; } } while(key); - va_end(argp); return msg; } @@ -227,14 +226,16 @@ MsimMessage * msim_msg_new(gchar *first_key, ...) { + MsimMessage *ret = NULL; va_list argp; if (first_key) { - va_start(argp, first_key); - return msim_msg_new_v(first_key, argp); - } else { - return NULL; + va_start(argp, first_key); + ret = msim_msg_new_v(first_key, argp); + va_end(argp); } + + return ret; } /** @@ -960,6 +961,7 @@ va_start(argp, session); msg = msim_msg_new_v(NULL, argp); + va_end(argp); /* Actually send the message. */ success = msim_msg_send(session, msg);
--- a/libpurple/protocols/myspace/user.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/myspace/user.h Fri Jan 25 02:22:38 2013 -0500 @@ -20,6 +20,8 @@ #ifndef _MYSPACE_USER_H #define _MYSPACE_USER_H +#include "obsolete.h" + /* Hold ephemeral information about buddies, for proto_data of PurpleBuddy. */ /* GHashTable? */ typedef struct _MsimUser
--- a/libpurple/protocols/oscar/family_feedbag.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/oscar/family_feedbag.c Fri Jan 25 02:22:38 2013 -0500 @@ -605,9 +605,10 @@ if (od->ssi.pending) { for (cur=od->ssi.pending; cur->next; cur=cur->next); cur->next = new; - } else + } else { od->ssi.pending = new; - aim_ssi_item_debug_append(debugstr, "Deleting item ", cur1); + } + aim_ssi_item_debug_append(debugstr, "Deleting item ", cur1); } } } @@ -626,9 +627,10 @@ if (od->ssi.pending) { for (cur=od->ssi.pending; cur->next; cur=cur->next); cur->next = new; - } else + } else { od->ssi.pending = new; - aim_ssi_item_debug_append(debugstr, "Adding item ", cur1); + } + aim_ssi_item_debug_append(debugstr, "Adding item ", cur1); } } } @@ -648,20 +650,23 @@ if (od->ssi.pending) { for (cur=od->ssi.pending; cur->next; cur=cur->next); cur->next = new; - } else + } else { od->ssi.pending = new; - aim_ssi_item_debug_append(debugstr, "Modifying item ", cur1); + } + aim_ssi_item_debug_append(debugstr, "Modifying item ", cur1); } } } if (debugstr->len > 0) { purple_debug_info("oscar", "%s", debugstr->str); if (purple_debug_is_verbose()) { + PurpleAccount *account = purple_connection_get_account(od->gc); g_string_truncate(debugstr, 0); - for (cur1 = od->ssi.local.data; cur1; cur1 = cur1->next) + for (cur1 = od->ssi.local.data; cur1; cur1 = cur1->next) { aim_ssi_item_debug_append(debugstr, "\t", cur1); + } purple_debug_misc("oscar", "Dumping item list of account %s:\n%s", - purple_account_get_username(purple_connection_get_account(od->gc)), debugstr->str); + purple_account_get_username(account), debugstr->str); } } g_string_free(debugstr, TRUE);
--- a/libpurple/protocols/oscar/odc.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/oscar/odc.c Fri Jan 25 02:22:38 2013 -0500 @@ -356,7 +356,7 @@ if ((embedded_data != NULL) && (embedded_data->size == size)) { - imgid = purple_imgstore_add_with_id(g_memdup(embedded_data->data, size), size, src); + imgid = purple_imgstore_new_with_id(g_memdup(embedded_data->data, size), size, src); /* Record the image number */ images = g_slist_append(images, GINT_TO_POINTER(imgid));
--- a/libpurple/protocols/oscar/oscar.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/oscar/oscar.c Fri Jan 25 02:22:38 2013 -0500 @@ -633,15 +633,6 @@ return subtype1 - subtype2; } -#if !GLIB_CHECK_VERSION(2,14,0) -static void hash_table_get_list_of_keys(gpointer key, gpointer value, gpointer user_data) -{ - GList **handlers = (GList **)user_data; - - *handlers = g_list_prepend(*handlers, key); -} -#endif /* GLIB < 2.14.0 */ - void oscar_login(PurpleAccount *account) { @@ -711,12 +702,7 @@ oscar_data_addhandler(od, SNAC_FAMILY_USERLOOKUP, 0x0003, purple_parse_searchreply, 0); g_string_append(msg, "Registered handlers: "); -#if GLIB_CHECK_VERSION(2,14,0) handlers = g_hash_table_get_keys(od->handlerlist); -#else - handlers = NULL; - g_hash_table_foreach(od->handlerlist, hash_table_get_list_of_keys, &handlers); -#endif /* GLIB < 2.14.0 */ sorted_handlers = g_list_sort(g_list_copy(handlers), compare_handlers); for (cur = sorted_handlers; cur; cur = cur->next) { guint x = GPOINTER_TO_UINT(cur->data); @@ -2317,6 +2303,7 @@ va_list ap; guint16 chan, reason; char *who; + int ret = 1; va_start(ap, fr); chan = (guint16)va_arg(ap, unsigned int); @@ -2325,7 +2312,7 @@ if (chan == 0x0002) { /* File transfer declined */ guchar *cookie = va_arg(ap, guchar *); - return purple_parse_clientauto_ch2(od, who, reason, cookie); + ret = purple_parse_clientauto_ch2(od, who, reason, cookie); } else if (chan == 0x0004) { /* ICQ message */ guint32 state = 0; char *msg = NULL; @@ -2333,12 +2320,12 @@ state = va_arg(ap, guint32); msg = va_arg(ap, char *); } - return purple_parse_clientauto_ch4(od, who, reason, state, msg); + ret = purple_parse_clientauto_ch4(od, who, reason, state, msg); } va_end(ap); - return 1; + return ret; } static int purple_parse_genericerr(OscarData *od, FlapConnection *conn, FlapFrame *fr, ...) {
--- a/libpurple/protocols/oscar/oscar.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/oscar/oscar.h Fri Jan 25 02:22:38 2013 -0500 @@ -35,6 +35,7 @@ #include "eventloop.h" #include "proxy.h" #include "sslconn.h" +#include "obsolete.h" #include <stdio.h> #include <string.h>
--- a/libpurple/protocols/sametime/sametime.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/sametime/sametime.c Fri Jan 25 02:22:38 2013 -0500 @@ -2780,7 +2780,7 @@ cid = make_cid(cid); /* add image to the purple image store */ - img = purple_imgstore_add_with_id(d_dat, d_len, cid); + img = purple_imgstore_new_with_id(d_dat, d_len, cid); /* map the cid to the image store identifier */ g_hash_table_insert(img_by_cid, cid, GINT_TO_POINTER(img));
--- a/libpurple/protocols/silc/Makefile.mingw Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/silc/Makefile.mingw Fri Jan 25 02:22:38 2013 -0500 @@ -11,7 +11,7 @@ TARGET = libsilc NEEDED_DLLS = $(SILC_TOOLKIT)/bin/libsilc-1-1-2.dll \ - $(SILC_TOOLKIT)/bin/libsilcclient-1-1-2.dll + $(SILC_TOOLKIT)/bin/libsilcclient-1-1-3.dll TYPE = PLUGIN # Static or Plugin...
--- a/libpurple/protocols/silc/ops.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/silc/ops.c Fri Jan 25 02:22:38 2013 -0500 @@ -207,7 +207,7 @@ if (channel && !convo) goto out; - imgid = purple_imgstore_add_with_id(g_memdup(data, data_len), data_len, ""); + imgid = purple_imgstore_new_with_id(g_memdup(data, data_len), data_len, ""); if (imgid) { cflags |= PURPLE_MESSAGE_IMAGES | PURPLE_MESSAGE_RECV; g_snprintf(tmp, sizeof(tmp),
--- a/libpurple/protocols/yahoo/libymsg.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/yahoo/libymsg.c Fri Jan 25 02:22:38 2013 -0500 @@ -22,6 +22,7 @@ */ #include "internal.h" +#include "obsolete.h" #include "account.h" #include "accountopt.h"
--- a/libpurple/protocols/yahoo/libymsg.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/yahoo/libymsg.h Fri Jan 25 02:22:38 2013 -0500 @@ -96,6 +96,7 @@ #define YAHOOJP_CLIENT_VERSION "9.0.0.1727" #define YAHOO_CLIENT_USERAGENT "Mozilla/5.0" +#define YAHOO_CLIENT_USERAGENT_ALIAS "Mozilla/4.0 (compatible; MSIE 5.5)" /* Index into attention types list. */ #define YAHOO_BUZZ 0
--- a/libpurple/protocols/yahoo/yahoo_aliases.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/yahoo/yahoo_aliases.c Fri Jan 25 02:22:38 2013 -0500 @@ -23,6 +23,7 @@ #include "internal.h" +#include "obsolete.h" #include "account.h" #include "accountopt.h"
--- a/libpurple/protocols/yahoo/yahoo_picture.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/yahoo/yahoo_picture.c Fri Jan 25 02:22:38 2013 -0500 @@ -22,6 +22,7 @@ */ #include "internal.h" +#include "obsolete.h" #include "account.h" #include "accountopt.h"
--- a/libpurple/protocols/yahoo/yahoo_profile.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/protocols/yahoo/yahoo_profile.c Fri Jan 25 02:22:38 2013 -0500 @@ -24,6 +24,7 @@ #define PHOTO_SUPPORT 1 #include "internal.h" +#include "obsolete.h" #include "debug.h" #include "notify.h" #include "util.h" @@ -1045,7 +1046,7 @@ } else { purple_debug_info("yahoo", "%s is %" G_GSIZE_FORMAT " bytes\n", photo_url_text, len); - id = purple_imgstore_add_with_id(g_memdup(url_text, len), len, NULL); + id = purple_imgstore_new_with_id(g_memdup(url_text, len), len, NULL); tmp = g_strdup_printf("<img id=\"" PURPLE_STORED_IMAGE_PROTOCOL "%d\"><br>", id);
--- a/libpurple/prpl.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/prpl.h Fri Jan 25 02:22:38 2013 -0500 @@ -475,7 +475,7 @@ * On the other hand, both of these are invalid for protocols with * number-based usernames, so function should return NULL in such case. * - * @param account The account, that username is related with. Can + * @param account The account the username is related to. Can * be NULL. * @param who The username to convert. * @return Normalized username, or NULL, if it's invalid.
--- a/libpurple/smiley.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/smiley.c Fri Jan 25 02:22:38 2013 -0500 @@ -608,7 +608,7 @@ return NULL; } - stored_img = purple_imgstore_add(smiley_data, smiley_data_len, filename); + stored_img = purple_imgstore_new(smiley_data, smiley_data_len, filename); g_free(filename);
--- a/libpurple/stringref.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/stringref.c Fri Jan 25 02:22:38 2013 -0500 @@ -73,7 +73,9 @@ len = strlen(value); newref = g_malloc(sizeof(PurpleStringref) + len); - g_strlcpy(newref->value, value, len); + /* g_strlcpy() takes the size of the buffer, including the NUL. + strlen() returns the length of the string, without the NUL. */ + g_strlcpy(newref->value, value, len + 1); newref->ref = 1; return newref;
--- a/libpurple/tests/check_libpurple.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/tests/check_libpurple.c Fri Jan 25 02:22:38 2013 -0500 @@ -25,11 +25,7 @@ purple_check_input_add, g_source_remove, NULL, /* input_get_error */ -#if GLIB_CHECK_VERSION(2,14,0) g_timeout_add_seconds, -#else - NULL, -#endif NULL, NULL, NULL
--- a/libpurple/tests/test_util.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/tests/test_util.c Fri Jan 25 02:22:38 2013 -0500 @@ -167,14 +167,179 @@ { gchar *xhtml = NULL; gchar *plaintext = NULL; + purple_markup_html_to_xhtml("<a>", &xhtml, &plaintext); assert_string_equal_free("<a href=\"\"></a>", xhtml); assert_string_equal_free("", plaintext); + purple_markup_html_to_xhtml("<A href='URL'>ABOUT</a>", &xhtml, &plaintext); + assert_string_equal_free("<a href=\"URL\">ABOUT</a>", xhtml); + assert_string_equal_free("ABOUT <URL>", plaintext); + + purple_markup_html_to_xhtml("<a href='URL'>URL</a>", &xhtml, &plaintext); + assert_string_equal_free("URL", plaintext); + assert_string_equal_free("<a href=\"URL\">URL</a>", xhtml); + + purple_markup_html_to_xhtml("<a href='mailto:mail'>mail</a>", &xhtml, &plaintext); + assert_string_equal_free("mail", plaintext); + assert_string_equal_free("<a href=\"mailto:mail\">mail</a>", xhtml); + + purple_markup_html_to_xhtml("<A href='\"U'R&L'>ABOUT</a>", &xhtml, &plaintext); + assert_string_equal_free("<a href=\""U'R&L\">ABOUT</a>", xhtml); + assert_string_equal_free("ABOUT <\"U'R&L>", plaintext); + + purple_markup_html_to_xhtml("<img src='SRC' alt='ALT'/>", &xhtml, &plaintext); + assert_string_equal_free("<img src='SRC' alt='ALT' />", xhtml); + assert_string_equal_free("ALT", plaintext); + + purple_markup_html_to_xhtml("<img src=\"'S'R&C\" alt=\"'A'L&T\"/>", &xhtml, &plaintext); + assert_string_equal_free("<img src=''S'R&C' alt=''A'L&T' />", xhtml); + assert_string_equal_free("'A'L&T", plaintext); + + purple_markup_html_to_xhtml("<unknown>", &xhtml, &plaintext); + assert_string_equal_free("<unknown>", xhtml); + assert_string_equal_free("<unknown>", plaintext); + + purple_markup_html_to_xhtml("é&", &xhtml, &plaintext); + assert_string_equal_free("é&", xhtml); + assert_string_equal_free("é&", plaintext); + + purple_markup_html_to_xhtml("<h1>A<h2>B</h2>C</h1>", &xhtml, &plaintext); + assert_string_equal_free("<h1>A<h2>B</h2>C</h1>", xhtml); + assert_string_equal_free("ABC", plaintext); + + purple_markup_html_to_xhtml("<h1><h2><h3><h4>", &xhtml, &plaintext); + assert_string_equal_free("<h1><h2><h3><h4></h4></h3></h2></h1>", xhtml); + assert_string_equal_free("", plaintext); + + purple_markup_html_to_xhtml("<italic/>", &xhtml, &plaintext); + assert_string_equal_free("<em/>", xhtml); + assert_string_equal_free("", plaintext); + + purple_markup_html_to_xhtml("</", &xhtml, &plaintext); + assert_string_equal_free("</", xhtml); + assert_string_equal_free("</", plaintext); + + purple_markup_html_to_xhtml("</div>", &xhtml, &plaintext); + assert_string_equal_free("", xhtml); + assert_string_equal_free("", plaintext); + + purple_markup_html_to_xhtml("<hr/>", &xhtml, &plaintext); + assert_string_equal_free("<br/>", xhtml); + assert_string_equal_free("\n", plaintext); + + purple_markup_html_to_xhtml("<hr>", &xhtml, &plaintext); + assert_string_equal_free("<br/>", xhtml); + assert_string_equal_free("\n", plaintext); + + purple_markup_html_to_xhtml("<br />", &xhtml, &plaintext); + assert_string_equal_free("<br/>", xhtml); + assert_string_equal_free("\n", plaintext); + + purple_markup_html_to_xhtml("<br>INSIDE</br>", &xhtml, &plaintext); + assert_string_equal_free("<br/>INSIDE", xhtml); + assert_string_equal_free("\nINSIDE", plaintext); + + purple_markup_html_to_xhtml("<div></div>", &xhtml, &plaintext); + assert_string_equal_free("<div></div>", xhtml); + assert_string_equal_free("", plaintext); + + purple_markup_html_to_xhtml("<div/>", &xhtml, &plaintext); + assert_string_equal_free("<div/>", xhtml); + assert_string_equal_free("", plaintext); + + purple_markup_html_to_xhtml("<div attr='\"&<>'/>", &xhtml, &plaintext); + assert_string_equal_free("<div attr='"&<>'/>", xhtml); + assert_string_equal_free("", plaintext); + + purple_markup_html_to_xhtml("<div attr=\"'\"/>", &xhtml, &plaintext); + assert_string_equal_free("<div attr=\"'\"/>", xhtml); + assert_string_equal_free("", plaintext); + + purple_markup_html_to_xhtml("<div/> < <div/>", &xhtml, &plaintext); + assert_string_equal_free("<div/> < <div/>", xhtml); + assert_string_equal_free(" < ", plaintext); + + purple_markup_html_to_xhtml("<div>x</div>", &xhtml, &plaintext); + assert_string_equal_free("<div>x</div>", xhtml); + assert_string_equal_free("x", plaintext); + + purple_markup_html_to_xhtml("<b>x</b>", &xhtml, &plaintext); + assert_string_equal_free("<span style='font-weight: bold;'>x</span>", xhtml); + assert_string_equal_free("x", plaintext); + + purple_markup_html_to_xhtml("<bold>x</bold>", &xhtml, &plaintext); + assert_string_equal_free("<span style='font-weight: bold;'>x</span>", xhtml); + assert_string_equal_free("x", plaintext); + + purple_markup_html_to_xhtml("<strong>x</strong>", &xhtml, &plaintext); + assert_string_equal_free("<span style='font-weight: bold;'>x</span>", xhtml); + assert_string_equal_free("x", plaintext); + + purple_markup_html_to_xhtml("<u>x</u>", &xhtml, &plaintext); + assert_string_equal_free("<span style='text-decoration: underline;'>x</span>", xhtml); + assert_string_equal_free("x", plaintext); + + purple_markup_html_to_xhtml("<underline>x</underline>", &xhtml, &plaintext); + assert_string_equal_free("<span style='text-decoration: underline;'>x</span>", xhtml); + assert_string_equal_free("x", plaintext); + + purple_markup_html_to_xhtml("<s>x</s>", &xhtml, &plaintext); + assert_string_equal_free("<span style='text-decoration: line-through;'>x</span>", xhtml); + assert_string_equal_free("x", plaintext); + + purple_markup_html_to_xhtml("<strike>x</strike>", &xhtml, &plaintext); + assert_string_equal_free("<span style='text-decoration: line-through;'>x</span>", xhtml); + assert_string_equal_free("x", plaintext); + + purple_markup_html_to_xhtml("<sub>x</sub>", &xhtml, &plaintext); + assert_string_equal_free("<span style='vertical-align:sub;'>x</span>", xhtml); + assert_string_equal_free("x", plaintext); + + purple_markup_html_to_xhtml("<sup>x</sup>", &xhtml, &plaintext); + assert_string_equal_free("<span style='vertical-align:super;'>x</span>", xhtml); + assert_string_equal_free("x", plaintext); purple_markup_html_to_xhtml("<FONT>x</FONT>", &xhtml, &plaintext); assert_string_equal_free("x", xhtml); assert_string_equal_free("x", plaintext); + + purple_markup_html_to_xhtml("<font face=\"'Times>New & Roman'\">x</font>", &xhtml, &plaintext); + assert_string_equal_free("x", plaintext); + assert_string_equal_free("<span style='font-family: \"Times>New & Roman\";'>x</span>", xhtml); + + purple_markup_html_to_xhtml("<font back=\"'color>blue&red'\">x</font>", &xhtml, &plaintext); + assert_string_equal_free("x", plaintext); + assert_string_equal_free("<span style='background: \"color>blue&red\";'>x</span>", xhtml); + + purple_markup_html_to_xhtml("<font color=\"'color>blue&red'\">x</font>", &xhtml, &plaintext); + assert_string_equal_free("x", plaintext); + assert_string_equal_free("<span style='color: \"color>blue&red\";'>x</span>", xhtml); + + purple_markup_html_to_xhtml("<font size=1>x</font>", &xhtml, &plaintext); + assert_string_equal_free("x", plaintext); + assert_string_equal_free("<span style='font-size: xx-small;'>x</span>", xhtml); + + purple_markup_html_to_xhtml("<font size=432>x</font>", &xhtml, &plaintext); + assert_string_equal_free("x", plaintext); + assert_string_equal_free("<span style='font-size: medium;'>x</span>", xhtml); + + /* The following tests document a behaviour that looks suspicious */ + + /* bug report http://developer.pidgin.im/ticket/13485 */ + purple_markup_html_to_xhtml("<!--COMMENT-->", &xhtml, &plaintext); + assert_string_equal_free("<!--COMMENT-->", xhtml); + assert_string_equal_free("COMMENT-->", plaintext); + + /* no bug report */ + purple_markup_html_to_xhtml("<br />", &xhtml, &plaintext); + assert_string_equal_free("<br />", xhtml); + assert_string_equal_free("<br />", plaintext); + + /* same code section as <br /> */ + purple_markup_html_to_xhtml("<hr />", &xhtml, &plaintext); + assert_string_equal_free("<hr />", xhtml); + assert_string_equal_free("<hr />", plaintext); } END_TEST
--- a/libpurple/upnp.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/upnp.c Fri Jan 25 02:22:38 2013 -0500 @@ -34,6 +34,7 @@ #include "signals.h" #include "util.h" #include "xmlnode.h" +#include "obsolete.h" /*************************************************************** ** General Defines * @@ -256,9 +257,12 @@ } /* get the baseURL of the device */ + baseURL = NULL; if((baseURLNode = xmlnode_get_child(xmlRootNode, "URLBase")) != NULL) { baseURL = xmlnode_get_data(baseURLNode); - } else { + } + /* fixes upnp-descriptions with empty urlbase-element */ + if(baseURL == NULL){ baseURL = g_strdup(httpURL); }
--- a/libpurple/util.c Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/util.c Fri Jan 25 02:22:38 2013 -0500 @@ -28,50 +28,11 @@ #include "core.h" #include "debug.h" #include "notify.h" -#include "ntlm.h" #include "prpl.h" #include "prefs.h" #include "util.h" -struct _PurpleUtilFetchUrlData -{ - PurpleUtilFetchUrlCallback callback; - void *user_data; - - struct - { - char *user; - char *passwd; - char *address; - int port; - char *page; - - } website; - - char *url; - int num_times_redirected; - gboolean full; - char *user_agent; - gboolean http11; - char *request; - gsize request_written; - gboolean include_headers; - - gboolean is_ssl; - PurpleSslConnection *ssl_connection; - PurpleProxyConnectData *connect_data; - int fd; - guint inpa; - - gboolean got_headers; - gboolean has_explicit_data_len; - char *webdata; - gsize len; - unsigned long data_len; - gssize max_len; - gboolean chunked; - PurpleAccount *account; -}; +#include <json-glib/json-glib.h> struct _PurpleMenuAction { @@ -84,6 +45,8 @@ static char *custom_user_dir = NULL; static char *user_dir = NULL; +static JsonNode *escape_js_node = NULL; +static JsonGenerator *escape_js_gen = NULL; PurpleMenuAction * purple_menu_action_new(const char *label, PurpleCallback callback, gpointer data, @@ -165,8 +128,8 @@ void purple_util_init(void) { - /* This does nothing right now. It exists for symmetry with - * purple_util_uninit() and forwards compatibility. */ + escape_js_node = json_node_new(JSON_NODE_VALUE); + escape_js_gen = json_generator_new(); } void @@ -179,6 +142,12 @@ g_free(user_dir); user_dir = NULL; + + json_node_free(escape_js_node); + escape_js_node = NULL; + + g_object_unref(escape_js_gen); + escape_js_gen = NULL; } /************************************************************************** @@ -1891,7 +1860,7 @@ if (cdata && url && (!g_string_equal(cdata, url) && (g_ascii_strncasecmp(url->str, "mailto:", 7) != 0 || g_utf8_collate(url->str + 7, cdata->str) != 0))) - g_string_append_printf(plain, " <%s>", g_strstrip(url->str)); + g_string_append_printf(plain, " <%s>", g_strstrip(purple_unescape_html(url->str))); if (cdata) { g_string_free(cdata, TRUE); cdata = NULL; @@ -2022,33 +1991,39 @@ if (!g_ascii_strncasecmp(c, "<img", 4) && (*(c+4) == '>' || *(c+4) == ' ')) { const char *p = c + 4; GString *src = NULL, *alt = NULL; +#define ESCAPE(from, to) \ + CHECK_QUOTE(from); \ + while (VALID_CHAR(from)) { \ + int len; \ + if ((*from == '&') && (purple_markup_unescape_entity(from, &len) == NULL)) \ + to = g_string_append(to, "&"); \ + else if (*from == '\'') \ + to = g_string_append(to, "'"); \ + else \ + to = g_string_append_c(to, *from); \ + from++; \ + } + while (*p && *p != '>') { if (!g_ascii_strncasecmp(p, "src=", 4)) { const char *q = p + 4; if (src) g_string_free(src, TRUE); src = g_string_new(""); - CHECK_QUOTE(q); - while (VALID_CHAR(q)) { - src = g_string_append_c(src, *q); - q++; - } + ESCAPE(q, src); p = q; } else if (!g_ascii_strncasecmp(p, "alt=", 4)) { const char *q = p + 4; if (alt) g_string_free(alt, TRUE); alt = g_string_new(""); - CHECK_QUOTE(q); - while (VALID_CHAR(q)) { - alt = g_string_append_c(alt, *q); - q++; - } + ESCAPE(q, alt); p = q; } else { p++; } } +#undef ESCAPE if ((c = strchr(p, '>')) != NULL) c++; else @@ -2058,7 +2033,7 @@ g_string_append_printf(xhtml, "<img src='%s' alt='%s' />", g_strstrip(src->str), alt ? alt->str : ""); if(alt) { if(plain) - plain = g_string_append(plain, alt->str); + plain = g_string_append(plain, purple_unescape_html(alt->str)); if(!src && xhtml) xhtml = g_string_append(xhtml, alt->str); g_string_free(alt, TRUE); @@ -2083,6 +2058,8 @@ int len; if ((*q == '&') && (purple_markup_unescape_entity(q, &len) == NULL)) url = g_string_append(url, "&"); + else if (*q == '"') + url = g_string_append(url, """); else url = g_string_append_c(url, *q); q++; @@ -2104,6 +2081,18 @@ g_string_append_printf(xhtml, "<a href=\"%s\">", url ? g_strstrip(url->str) : ""); continue; } +#define ESCAPE(from, to) \ + CHECK_QUOTE(from); \ + while (VALID_CHAR(from)) { \ + int len; \ + if ((*from == '&') && (purple_markup_unescape_entity(from, &len) == NULL)) \ + to = g_string_append(to, "&"); \ + else if (*from == '\'') \ + to = g_string_append_c(to, '\"'); \ + else \ + to = g_string_append_c(to, *from); \ + from++; \ + } if(!g_ascii_strncasecmp(c, "<font", 5) && (*(c+5) == '>' || *(c+5) == ' ')) { const char *p = c + 5; GString *style = g_string_new(""); @@ -2112,33 +2101,21 @@ if (!g_ascii_strncasecmp(p, "back=", 5)) { const char *q = p + 5; GString *color = g_string_new(""); - CHECK_QUOTE(q); - while (VALID_CHAR(q)) { - color = g_string_append_c(color, *q); - q++; - } + ESCAPE(q, color); g_string_append_printf(style, "background: %s; ", color->str); g_string_free(color, TRUE); p = q; } else if (!g_ascii_strncasecmp(p, "color=", 6)) { const char *q = p + 6; GString *color = g_string_new(""); - CHECK_QUOTE(q); - while (VALID_CHAR(q)) { - color = g_string_append_c(color, *q); - q++; - } + ESCAPE(q, color); g_string_append_printf(style, "color: %s; ", color->str); g_string_free(color, TRUE); p = q; } else if (!g_ascii_strncasecmp(p, "face=", 5)) { const char *q = p + 5; GString *face = g_string_new(""); - CHECK_QUOTE(q); - while (VALID_CHAR(q)) { - face = g_string_append_c(face, *q); - q++; - } + ESCAPE(q, face); g_string_append_printf(style, "font-family: %s; ", g_strstrip(face->str)); g_string_free(face, TRUE); p = q; @@ -2193,6 +2170,7 @@ g_string_free(style, TRUE); continue; } +#undef ESCAPE if (!g_ascii_strncasecmp(c, "<body ", 6)) { const char *p = c + 6; gboolean did_something = FALSE; @@ -3022,6 +3000,15 @@ return FALSE; } +#ifndef _WIN32 + /* Set file permissions */ + if (fchmod(fileno(file), S_IRUSR | S_IWUSR) == -1) + { + purple_debug_error("util", "Error setting permissions of %s: %s\n", + filename_temp, g_strerror(errno)); + } +#endif + /* Write to file */ real_size = (size == -1) ? strlen(data) : (size_t) size; byteswritten = fwrite(data, 1, real_size, file); @@ -3101,15 +3088,6 @@ return FALSE; } -#ifndef _WIN32 - /* Set file permissions */ - if (chmod(filename_temp, S_IRUSR | S_IWUSR) == -1) - { - purple_debug_error("util", "Error setting permissions of file %s: %s\n", - filename_temp, g_strerror(errno)); - } -#endif - /* Rename to the REAL name */ if (g_rename(filename_temp, filename_full) == -1) { @@ -3421,12 +3399,7 @@ gboolean purple_strequal(const gchar *left, const gchar *right) { -#if GLIB_CHECK_VERSION(2,16,0) return (g_strcmp0(left, right) == 0); -#else - return ((left == NULL && right == NULL) || - (left != NULL && right != NULL && strcmp(left, right) == 0)); -#endif } const char * @@ -3733,9 +3706,9 @@ } if (size_index == 0) { - return g_strdup_printf("%" G_GSIZE_FORMAT " %s", size, size_str[size_index]); + return g_strdup_printf("%" G_GOFFSET_FORMAT " %s", size, _(size_str[size_index])); } else { - return g_strdup_printf("%.2f %s", size_mag, size_str[size_index]); + return g_strdup_printf("%.2f %s", size_mag, _(size_str[size_index])); } } } @@ -3997,646 +3970,6 @@ #undef PASSWD_CTRL } -/** - * The arguments to this function are similar to printf. - */ -static void -purple_util_fetch_url_error(PurpleUtilFetchUrlData *gfud, const char *format, ...) -{ - gchar *error_message; - va_list args; - - va_start(args, format); - error_message = g_strdup_vprintf(format, args); - va_end(args); - - gfud->callback(gfud, gfud->user_data, NULL, 0, error_message); - g_free(error_message); - purple_util_fetch_url_cancel(gfud); -} - -static void url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message); -static void ssl_url_fetch_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond); -static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data); - -static gboolean -parse_redirect(const char *data, size_t data_len, - PurpleUtilFetchUrlData *gfud) -{ - gchar *s; - gchar *new_url, *temp_url, *end; - gboolean full; - int len; - - if ((s = g_strstr_len(data, data_len, "\nLocation: ")) == NULL) - /* We're not being redirected */ - return FALSE; - - s += strlen("Location: "); - end = strchr(s, '\r'); - - /* Just in case :) */ - if (end == NULL) - end = strchr(s, '\n'); - - if (end == NULL) - return FALSE; - - len = end - s; - - new_url = g_malloc(len + 1); - strncpy(new_url, s, len); - new_url[len] = '\0'; - - full = gfud->full; - - if (*new_url == '/' || g_strstr_len(new_url, len, "://") == NULL) - { - temp_url = new_url; - - new_url = g_strdup_printf("%s:%d%s", gfud->website.address, - gfud->website.port, temp_url); - - g_free(temp_url); - - full = FALSE; - } - - purple_debug_info("util", "Redirecting to %s\n", new_url); - - gfud->num_times_redirected++; - if (gfud->num_times_redirected >= 5) - { - purple_util_fetch_url_error(gfud, - _("Could not open %s: Redirected too many times"), - gfud->url); - return TRUE; - } - - /* - * Try again, with this new location. This code is somewhat - * ugly, but we need to reuse the gfud because whoever called - * us is holding a reference to it. - */ - g_free(gfud->url); - gfud->url = new_url; - gfud->full = full; - g_free(gfud->request); - gfud->request = NULL; - - if (gfud->is_ssl) { - gfud->is_ssl = FALSE; - purple_ssl_close(gfud->ssl_connection); - gfud->ssl_connection = NULL; - } else { - purple_input_remove(gfud->inpa); - gfud->inpa = 0; - close(gfud->fd); - gfud->fd = -1; - } - gfud->request_written = 0; - gfud->len = 0; - gfud->data_len = 0; - - g_free(gfud->website.user); - g_free(gfud->website.passwd); - g_free(gfud->website.address); - g_free(gfud->website.page); - purple_url_parse(new_url, &gfud->website.address, &gfud->website.port, - &gfud->website.page, &gfud->website.user, &gfud->website.passwd); - - if (purple_strcasestr(new_url, "https://") != NULL) { - gfud->is_ssl = TRUE; - gfud->ssl_connection = purple_ssl_connect(gfud->account, - gfud->website.address, gfud->website.port, - ssl_url_fetch_connect_cb, ssl_url_fetch_error_cb, gfud); - } else { - gfud->connect_data = purple_proxy_connect(NULL, gfud->account, - gfud->website.address, gfud->website.port, - url_fetch_connect_cb, gfud); - } - - if (gfud->ssl_connection == NULL && gfud->connect_data == NULL) - { - purple_util_fetch_url_error(gfud, _("Unable to connect to %s"), - gfud->website.address); - } - - return TRUE; -} - -static const char * -find_header_content(const char *data, size_t data_len, const char *header, size_t header_len) -{ - const char *p = NULL; - - if (header_len <= 0) - header_len = strlen(header); - - /* Note: data is _not_ nul-terminated. */ - if (data_len > header_len) { - if (header[0] == '\n') - p = (g_ascii_strncasecmp(data, header + 1, header_len - 1) == 0) ? data : NULL; - if (!p) - p = purple_strcasestr(data, header); - if (p) - p += header_len; - } - - /* If we can find the header at all, try to sscanf it. - * Response headers should end with at least \r\n, so sscanf is safe, - * if we make sure that there is indeed a \n in our header. - */ - if (p && g_strstr_len(p, data_len - (p - data), "\n")) { - return p; - } - - return NULL; -} - -static size_t -parse_content_len(const char *data, size_t data_len) -{ - size_t content_len = 0; - const char *p = NULL; - - p = find_header_content(data, data_len, "\nContent-Length: ", sizeof("\nContent-Length: ") - 1); - if (p) { - sscanf(p, "%" G_GSIZE_FORMAT, &content_len); - purple_debug_misc("util", "parsed %" G_GSIZE_FORMAT "\n", content_len); - } - - return content_len; -} - -static gboolean -content_is_chunked(const char *data, size_t data_len) -{ - const char *p = find_header_content(data, data_len, "\nTransfer-Encoding: ", sizeof("\nTransfer-Encoding: ") - 1); - if (p && g_ascii_strncasecmp(p, "chunked", 7) == 0) - return TRUE; - - return FALSE; -} - -/* Process in-place */ -static void -process_chunked_data(char *data, gsize *len) -{ - gsize sz; - gsize newlen = 0; - char *p = data; - char *s = data; - - while (*s) { - /* Read the size of this chunk */ - if (sscanf(s, "%" G_GSIZE_MODIFIER "x", &sz) != 1) - { - purple_debug_error("util", "Error processing chunked data: " - "Expected data length, found: %s\n", s); - break; - } - if (sz == 0) { - /* We've reached the last chunk */ - /* - * TODO: The spec allows "footers" to follow the last chunk. - * If there is more data after this line then we should - * treat it like a header. - */ - break; - } - - /* Advance to the start of the data */ - s = strstr(s, "\r\n"); - if (s == NULL) - break; - s += 2; - - if (s + sz > data + *len) { - purple_debug_error("util", "Error processing chunked data: " - "Chunk size %" G_GSIZE_FORMAT " bytes was longer " - "than the data remaining in the buffer (%" - G_GSIZE_FORMAT " bytes)\n", sz, data + *len - s); - } - - /* Move all data overtop of the chunk length that we read in earlier */ - g_memmove(p, s, sz); - p += sz; - s += sz; - newlen += sz; - if (*s != '\r' && *(s + 1) != '\n') { - purple_debug_error("util", "Error processing chunked data: " - "Expected \\r\\n, found: %s\n", s); - break; - } - s += 2; - } - - /* NULL terminate the data */ - *p = 0; - - *len = newlen; -} - -static void -url_fetch_recv_cb(gpointer url_data, gint source, PurpleInputCondition cond) -{ - PurpleUtilFetchUrlData *gfud = url_data; - int len; - char buf[4096]; - char *data_cursor; - gboolean got_eof = FALSE; - - /* - * Read data in a loop until we can't read any more! This is a - * little confusing because we read using a different function - * depending on whether the socket is ssl or cleartext. - */ - while ((gfud->is_ssl && ((len = purple_ssl_read(gfud->ssl_connection, buf, sizeof(buf))) > 0)) || - (!gfud->is_ssl && (len = read(source, buf, sizeof(buf))) > 0)) - { - if(gfud->max_len != -1 && (gfud->len + len) > gfud->max_len) { - purple_util_fetch_url_error(gfud, _("Error reading from %s: response too long (%d bytes limit)"), - gfud->website.address, gfud->max_len); - return; - } - - /* If we've filled up our buffer, make it bigger */ - if((gfud->len + len) >= gfud->data_len) { - while((gfud->len + len) >= gfud->data_len) - gfud->data_len += sizeof(buf); - - gfud->webdata = g_realloc(gfud->webdata, gfud->data_len); - } - - data_cursor = gfud->webdata + gfud->len; - - gfud->len += len; - - memcpy(data_cursor, buf, len); - - gfud->webdata[gfud->len] = '\0'; - - if(!gfud->got_headers) { - char *end_of_headers; - - /* See if we've reached the end of the headers yet */ - end_of_headers = strstr(gfud->webdata, "\r\n\r\n"); - if (end_of_headers) { - char *new_data; - guint header_len = (end_of_headers + 4 - gfud->webdata); - size_t content_len; - - purple_debug_misc("util", "Response headers: '%.*s'\n", - header_len, gfud->webdata); - - /* See if we can find a redirect. */ - if(parse_redirect(gfud->webdata, header_len, gfud)) - return; - - gfud->got_headers = TRUE; - - /* No redirect. See if we can find a content length. */ - content_len = parse_content_len(gfud->webdata, header_len); - gfud->chunked = content_is_chunked(gfud->webdata, header_len); - - if (content_len == 0) { - /* We'll stick with an initial 8192 */ - content_len = 8192; - } else { - gfud->has_explicit_data_len = TRUE; - } - - - /* If we're returning the headers too, we don't need to clean them out */ - if (gfud->include_headers) { - gfud->data_len = content_len + header_len; - gfud->webdata = g_realloc(gfud->webdata, gfud->data_len); - } else { - size_t body_len = gfud->len - header_len; - - content_len = MAX(content_len, body_len); - - new_data = g_try_malloc(content_len); - if (new_data == NULL) { - purple_debug_error("util", - "Failed to allocate %" G_GSIZE_FORMAT " bytes: %s\n", - content_len, g_strerror(errno)); - purple_util_fetch_url_error(gfud, - _("Unable to allocate enough memory to hold " - "the contents from %s. The web server may " - "be trying something malicious."), - gfud->website.address); - - return; - } - - /* We may have read part of the body when reading the headers, don't lose it */ - if (body_len > 0) { - memcpy(new_data, end_of_headers + 4, body_len); - } - - /* Out with the old... */ - g_free(gfud->webdata); - - /* In with the new. */ - gfud->len = body_len; - gfud->data_len = content_len; - gfud->webdata = new_data; - } - } - } - - if(gfud->has_explicit_data_len && gfud->len >= gfud->data_len) { - got_eof = TRUE; - break; - } - } - - if(len < 0) { - if(errno == EAGAIN) { - return; - } else { - purple_util_fetch_url_error(gfud, _("Error reading from %s: %s"), - gfud->website.address, g_strerror(errno)); - return; - } - } - - if((len == 0) || got_eof) { - gfud->webdata = g_realloc(gfud->webdata, gfud->len + 1); - gfud->webdata[gfud->len] = '\0'; - - if (!gfud->include_headers && gfud->chunked) { - /* Process only if we don't want the headers. */ - process_chunked_data(gfud->webdata, &gfud->len); - } - - gfud->callback(gfud, gfud->user_data, gfud->webdata, gfud->len, NULL); - purple_util_fetch_url_cancel(gfud); - } -} - -static void ssl_url_fetch_recv_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond) -{ - url_fetch_recv_cb(data, -1, cond); -} - -/** - * This function is called when the socket is available to be written - * to. - * - * @param source The file descriptor that can be written to. This can - * be an http connection or it can be the SSL connection of an - * https request. So be careful what you use it for! If it's - * an https request then use purple_ssl_write() instead of - * writing to it directly. - */ -static void -url_fetch_send_cb(gpointer data, gint source, PurpleInputCondition cond) -{ - PurpleUtilFetchUrlData *gfud; - int len, total_len; - - gfud = data; - - if (gfud->request == NULL) { - - PurpleProxyInfo *gpi = purple_proxy_get_setup(gfud->account); - GString *request_str = g_string_new(NULL); - - g_string_append_printf(request_str, "GET %s%s HTTP/%s\r\n" - "Connection: close\r\n", - (gfud->full ? "" : "/"), - (gfud->full ? (gfud->url ? gfud->url : "") : (gfud->website.page ? gfud->website.page : "")), - (gfud->http11 ? "1.1" : "1.0")); - - if (gfud->user_agent) - g_string_append_printf(request_str, "User-Agent: %s\r\n", gfud->user_agent); - - /* Host header is not forbidden in HTTP/1.0 requests, and HTTP/1.1 - * clients must know how to handle the "chunked" transfer encoding. - * Purple doesn't know how to handle "chunked", so should always send - * the Host header regardless, to get around some observed problems - */ - g_string_append_printf(request_str, "Accept: */*\r\n" - "Host: %s\r\n", - (gfud->website.address ? gfud->website.address : "")); - - if (purple_proxy_info_get_username(gpi) != NULL - && (purple_proxy_info_get_type(gpi) == PURPLE_PROXY_USE_ENVVAR - || purple_proxy_info_get_type(gpi) == PURPLE_PROXY_HTTP)) { - /* This chunk of code was copied from proxy.c http_start_connect_tunneling() - * This is really a temporary hack - we need a more complete proxy handling solution, - * so I didn't think it was worthwhile to refactor for reuse - */ - char *t1, *t2, *ntlm_type1; - char hostname[256]; - int ret; - - ret = gethostname(hostname, sizeof(hostname)); - hostname[sizeof(hostname) - 1] = '\0'; - if (ret < 0 || hostname[0] == '\0') { - purple_debug_warning("util", "proxy - gethostname() failed -- is your hostname set?"); - strcpy(hostname, "localhost"); - } - - t1 = g_strdup_printf("%s:%s", - purple_proxy_info_get_username(gpi), - purple_proxy_info_get_password(gpi) ? - purple_proxy_info_get_password(gpi) : ""); - t2 = purple_base64_encode((const guchar *)t1, strlen(t1)); - g_free(t1); - - ntlm_type1 = purple_ntlm_gen_type1(hostname, ""); - - g_string_append_printf(request_str, - "Proxy-Authorization: Basic %s\r\n" - "Proxy-Authorization: NTLM %s\r\n" - "Proxy-Connection: Keep-Alive\r\n", - t2, ntlm_type1); - g_free(ntlm_type1); - g_free(t2); - } - - g_string_append(request_str, "\r\n"); - - gfud->request = g_string_free(request_str, FALSE); - } - - if(purple_debug_is_unsafe()) - purple_debug_misc("util", "Request: '%s'\n", gfud->request); - else - purple_debug_misc("util", "request constructed\n"); - - total_len = strlen(gfud->request); - - if (gfud->is_ssl) - len = purple_ssl_write(gfud->ssl_connection, gfud->request + gfud->request_written, - total_len - gfud->request_written); - else - len = write(gfud->fd, gfud->request + gfud->request_written, - total_len - gfud->request_written); - - if (len < 0 && errno == EAGAIN) - return; - else if (len < 0) { - purple_util_fetch_url_error(gfud, _("Error writing to %s: %s"), - gfud->website.address, g_strerror(errno)); - return; - } - gfud->request_written += len; - - if (gfud->request_written < total_len) - return; - - /* We're done writing our request, now start reading the response */ - if (gfud->is_ssl) { - purple_input_remove(gfud->inpa); - gfud->inpa = 0; - purple_ssl_input_add(gfud->ssl_connection, ssl_url_fetch_recv_cb, gfud); - } else { - purple_input_remove(gfud->inpa); - gfud->inpa = purple_input_add(gfud->fd, PURPLE_INPUT_READ, url_fetch_recv_cb, - gfud); - } -} - -static void -url_fetch_connect_cb(gpointer url_data, gint source, const gchar *error_message) -{ - PurpleUtilFetchUrlData *gfud; - - gfud = url_data; - gfud->connect_data = NULL; - - if (source == -1) - { - purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"), - (gfud->website.address ? gfud->website.address : ""), error_message); - return; - } - - gfud->fd = source; - - gfud->inpa = purple_input_add(source, PURPLE_INPUT_WRITE, - url_fetch_send_cb, gfud); - url_fetch_send_cb(gfud, source, PURPLE_INPUT_WRITE); -} - -static void ssl_url_fetch_connect_cb(gpointer data, PurpleSslConnection *ssl_connection, PurpleInputCondition cond) -{ - PurpleUtilFetchUrlData *gfud; - - gfud = data; - - gfud->inpa = purple_input_add(ssl_connection->fd, PURPLE_INPUT_WRITE, - url_fetch_send_cb, gfud); - url_fetch_send_cb(gfud, ssl_connection->fd, PURPLE_INPUT_WRITE); -} - -static void ssl_url_fetch_error_cb(PurpleSslConnection *ssl_connection, PurpleSslErrorType error, gpointer data) -{ - PurpleUtilFetchUrlData *gfud; - - gfud = data; - gfud->ssl_connection = NULL; - - purple_util_fetch_url_error(gfud, _("Unable to connect to %s: %s"), - (gfud->website.address ? gfud->website.address : ""), - purple_ssl_strerror(error)); -} - -PurpleUtilFetchUrlData * -purple_util_fetch_url_request(PurpleAccount *account, - const char *url, gboolean full, const char *user_agent, gboolean http11, - const char *request, gboolean include_headers, gssize max_len, - PurpleUtilFetchUrlCallback callback, void *user_data) -{ - PurpleUtilFetchUrlData *gfud; - - g_return_val_if_fail(url != NULL, NULL); - g_return_val_if_fail(callback != NULL, NULL); - - if(purple_debug_is_unsafe()) - purple_debug_info("util", - "requested to fetch (%s), full=%d, user_agent=(%s), http11=%d\n", - url, full, user_agent?user_agent:"(null)", http11); - else - purple_debug_info("util", "requesting to fetch a URL\n"); - - gfud = g_new0(PurpleUtilFetchUrlData, 1); - - gfud->callback = callback; - gfud->user_data = user_data; - gfud->url = g_strdup(url); - gfud->user_agent = g_strdup(user_agent); - gfud->http11 = http11; - gfud->full = full; - gfud->request = g_strdup(request); - gfud->include_headers = include_headers; - gfud->fd = -1; - gfud->max_len = max_len; - gfud->account = account; - - purple_url_parse(url, &gfud->website.address, &gfud->website.port, - &gfud->website.page, &gfud->website.user, &gfud->website.passwd); - - if (purple_strcasestr(url, "https://") != NULL) { - if (!purple_ssl_is_supported()) { - purple_util_fetch_url_error(gfud, - _("Unable to connect to %s: %s"), - gfud->website.address, - _("Server requires TLS/SSL, but no TLS/SSL support was found.")); - return NULL; - } - - gfud->is_ssl = TRUE; - gfud->ssl_connection = purple_ssl_connect(account, - gfud->website.address, gfud->website.port, - ssl_url_fetch_connect_cb, ssl_url_fetch_error_cb, gfud); - } else { - gfud->connect_data = purple_proxy_connect(NULL, account, - gfud->website.address, gfud->website.port, - url_fetch_connect_cb, gfud); - } - - if (gfud->ssl_connection == NULL && gfud->connect_data == NULL) - { - purple_util_fetch_url_error(gfud, _("Unable to connect to %s"), - gfud->website.address); - return NULL; - } - - return gfud; -} - -void -purple_util_fetch_url_cancel(PurpleUtilFetchUrlData *gfud) -{ - if (gfud->ssl_connection != NULL) - purple_ssl_close(gfud->ssl_connection); - - if (gfud->connect_data != NULL) - purple_proxy_connect_cancel(gfud->connect_data); - - if (gfud->inpa > 0) - purple_input_remove(gfud->inpa); - - if (gfud->fd >= 0) - close(gfud->fd); - - g_free(gfud->website.user); - g_free(gfud->website.passwd); - g_free(gfud->website.address); - g_free(gfud->website.page); - g_free(gfud->url); - g_free(gfud->user_agent); - g_free(gfud->request); - g_free(gfud->webdata); - - g_free(gfud); -} const char * purple_url_decode(const char *str) @@ -5364,6 +4697,18 @@ return buf; } +gchar * purple_escape_js(const gchar *str) +{ + gchar *escaped; + + json_node_set_string(escape_js_node, str); + json_generator_set_root(escape_js_gen, escape_js_node); + escaped = json_generator_to_data(escape_js_gen, NULL); + json_node_set_boolean(escape_js_node, FALSE); + + return escaped; +} + void purple_restore_default_signal_handlers(void) { #ifndef _WIN32
--- a/libpurple/util.h Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/util.h Fri Jan 25 02:22:38 2013 -0500 @@ -32,12 +32,6 @@ #include <stdio.h> /** - * An opaque structure representing a URL request. Can be used to cancel - * the request. - */ -typedef struct _PurpleUtilFetchUrlData PurpleUtilFetchUrlData; - -/** * A generic structure that contains information about an "action." One * place this is is used is by PRPLs to tell the core the list of available * right-click actions for a buddy list row. @@ -1165,70 +1159,6 @@ char **ret_path, char **ret_user, char **ret_passwd); /** - * This is the signature used for functions that act as the callback - * to purple_util_fetch_url() or purple_util_fetch_url_request(). - * - * @param url_data The same value that was returned when you called - * purple_fetch_url() or purple_fetch_url_request(). - * @param user_data The user data that your code passed into either - * purple_util_fetch_url() or purple_util_fetch_url_request(). - * @param url_text This will be NULL on error. Otherwise this - * will contain the contents of the URL. - * @param len 0 on error, otherwise this is the length of buf. - * @param error_message If something went wrong then this will contain - * a descriptive error message, and buf will be - * NULL and len will be 0. - */ -typedef void (*PurpleUtilFetchUrlCallback)(PurpleUtilFetchUrlData *url_data, gpointer user_data, const gchar *url_text, gsize len, const gchar *error_message); - -/** - * Fetches the data from a URL, and passes it to a callback function. - * - * @param url The URL. - * @param full TRUE if this is the full URL, or FALSE if it's a - * partial URL. - * @param user_agent The user agent field to use, or NULL. - * @param http11 TRUE if HTTP/1.1 should be used to download the file. - * @param max_len The maximum number of bytes to retrieve (-1 for unlimited) - * @param cb The callback function. - * @param data The user data to pass to the callback function. - */ -#define purple_util_fetch_url(url, full, user_agent, http11, max_len, cb, data) \ - purple_util_fetch_url_request(NULL, url, full, user_agent, http11, NULL, \ - FALSE, max_len, cb, data); - -/** - * Fetches the data from a URL, and passes it to a callback function. - * - * @param account The account for which the request is needed, or NULL. - * @param url The URL. - * @param full TRUE if this is the full URL, or FALSE if it's a - * partial URL. - * @param user_agent The user agent field to use, or NULL. - * @param http11 TRUE if HTTP/1.1 should be used to download the file. - * @param request A HTTP request to send to the server instead of the - * standard GET - * @param include_headers - * If TRUE, include the HTTP headers in the response. - * @param max_len The maximum number of bytes to retrieve (-1 for unlimited) - * @param callback The callback function. - * @param data The user data to pass to the callback function. - */ -PurpleUtilFetchUrlData *purple_util_fetch_url_request( - PurpleAccount *account, const gchar *url, - gboolean full, const gchar *user_agent, gboolean http11, - const gchar *request, gboolean include_headers, gssize max_len, - PurpleUtilFetchUrlCallback callback, gpointer data); - -/** - * Cancel a pending URL request started with either - * purple_util_fetch_url_request() or purple_util_fetch_url(). - * - * @param url_data The data returned when you initiated the URL fetch. - */ -void purple_util_fetch_url_cancel(PurpleUtilFetchUrlData *url_data); - -/** * Decodes a URL into a plain string. * * This will change hex codes and such to their ascii equivalents. @@ -1460,6 +1390,15 @@ const char *purple_escape_filename(const char *str); /** + * Escapes javascript-unfriendly substrings from a string. + * + * @param str The string to escape. + * + * @return The javascript-safe string (must be g_free'd after use). + */ +gchar * purple_escape_js(const gchar *str); + +/** * Restore default signal handlers for signals which might reasonably have * handlers. This should be called by a fork()'d child process, since child processes * inherit the handlers of the parent.
--- a/libpurple/win32/global.mak Thu Aug 23 01:27:48 2012 -0400 +++ b/libpurple/win32/global.mak Fri Jan 25 02:22:38 2013 -0500 @@ -15,13 +15,15 @@ GTK_TOP ?= $(WIN32_DEV_TOP)/gtk_2_0-2.14 GTK_BIN ?= $(GTK_TOP)/bin BONJOUR_TOP ?= $(WIN32_DEV_TOP)/Bonjour_SDK -LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.7.4 -MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2_daa2 -NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.12.5-nspr-4.8.2 +LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.9.0 +MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2_daa3 +NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.13.6-nspr-4.9.2 PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl-5.10.0 -SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.8 +SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.10 TCL_LIB_TOP ?= $(WIN32_DEV_TOP)/tcl-8.4.5 GSTREAMER_TOP ?= $(WIN32_DEV_TOP)/gstreamer-0.10.13 +GCC_SSP_TOP ?= $(WIN32_DEV_TOP)/gcc-core-4.4.0-mingw32-dll +CYRUS_SASL_TOP ?= $(WIN32_DEV_TOP)/cyrus-sasl-2.1.25 # Where we installing this stuff to? PIDGIN_INSTALL_DIR := $(PIDGIN_TREE_TOP)/win32-install-dir @@ -57,6 +59,10 @@ GCCWARNINGS ?= -Waggregate-return -Wcast-align -Wdeclaration-after-statement -Werror-implicit-function-declaration -Wextra -Wno-sign-compare -Wno-unused-parameter -Winit-self -Wmissing-declarations -Wmissing-prototypes -Wnested-externs -Wpointer-arith -Wundef +CC_HARDENING_OPTIONS ?= -Wstack-protector -fwrapv -fno-strict-overflow -Wno-missing-field-initializers -Wformat-security -fstack-protector-all --param ssp-buffer-size=1 +LD_HARDENING_OPTIONS ?= -Wl,--dynamicbase -Wl,--nxcompat + + # parse the version number from the configure.ac file if it is newer #m4_define([purple_major_version], [2]) #m4_define([purple_minor_version], [0]) @@ -84,17 +90,14 @@ DEFINES += -DHAVE_CONFIG_H -DWIN32_LEAN_AND_MEAN -# Use -g flag when building debug version of Pidgin (including plugins). -# Use -fnative-struct instead of -mms-bitfields when using mingw 1.1 -# (gcc 2.95) -CFLAGS += -O2 -Wall $(GCCWARNINGS) -pipe -mno-cygwin -mms-bitfields -g +CFLAGS += -O2 -Wall $(GCCWARNINGS) $(CC_HARDENING_OPTIONS) -pipe -mms-bitfields -g # If not specified, dlls are built with the default base address of 0x10000000. # When loaded into a process address space a dll will be rebased if its base # address colides with the base address of an existing dll. To avoid rebasing # we do the following. Rebasing can slow down the load time of dlls and it # also renders debug info useless. -DLL_LD_FLAGS += -Wl,--enable-auto-image-base +DLL_LD_FLAGS += -Wl,--enable-auto-image-base -Wl,--enable-auto-import $(LD_HARDENING_OPTIONS) -lssp # Build programs ifeq "$(origin CC)" "default" @@ -106,6 +109,8 @@ WINDRES ?= windres STRIP ?= strip INTLTOOL_MERGE ?= $(WIN32_DEV_TOP)/intltool_0.40.4-1_win32/bin/intltool-merge +MONO_SIGNCODE ?= signcode +GPG_SIGN ?= gpg PIDGIN_COMMON_RULES := $(PURPLE_TOP)/win32/rules.mak PIDGIN_COMMON_TARGETS := $(PURPLE_TOP)/win32/targets.mak
--- a/pidgin.spec.in Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin.spec.in Fri Jan 25 02:22:38 2013 -0500 @@ -368,7 +368,7 @@ %doc ChangeLog %doc NEWS %doc README -%doc README.MTN +%doc README.hg %doc doc/the_penguin.txt %doc %{_mandir}/man1/pidgin.* %doc %{_mandir}/man3*/*
--- a/pidgin/Makefile.am Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/Makefile.am Fri Jan 25 02:22:38 2013 -0500 @@ -168,6 +168,7 @@ $(LIBXML_LIBS) \ $(WEBKIT_LIBS) \ $(GTK_LIBS) \ + $(X11_LIBS) \ $(top_builddir)/libpurple/libpurple.la if USE_INTERNAL_LIBGADU @@ -188,6 +189,7 @@ $(GSTREAMER_CFLAGS) \ $(DEBUG_CFLAGS) \ $(GTK_CFLAGS) \ + $(X11_CFLAGS) \ $(DBUS_CFLAGS) \ $(GTKSPELL_CFLAGS) \ $(LIBXML_CFLAGS) \
--- a/pidgin/Makefile.mingw Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/Makefile.mingw Fri Jan 25 02:22:38 2013 -0500 @@ -18,7 +18,7 @@ WINAPP := -mwindows -LDFLAGS := $(WINAPP) +LDFLAGS := $(WINAPP) $(LD_HARDENING_OPTIONS) -Wl,--enable-auto-import -lssp ## ## INCLUDE PATHS @@ -163,7 +163,7 @@ $(CC) -shared $(PIDGIN_OBJECTS) $(LIB_PATHS) $(PIDGIN_LIBS) $(DLL_LD_FLAGS) -Wl,--output-def,$(PIDGIN_TARGET).def,--out-implib,$(PIDGIN_TARGET).dll.a -o $(PIDGIN_TARGET).dll $(EXE_TARGET).exe: $(PIDGIN_CONFIG_H) $(PIDGIN_DLL).a $(EXE_OBJECTS) $(PIDGIN_TARGET).dll - $(CC) $(LDFLAGS) $(EXE_OBJECTS) -o $(EXE_TARGET).exe + $(CC) $(EXE_OBJECTS) $(LDFLAGS) -o $(EXE_TARGET).exe ## ## CLEAN RULES
--- a/pidgin/gtk3compat.h Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtk3compat.h Fri Jan 25 02:22:38 2013 -0500 @@ -37,6 +37,16 @@ #define gdk_x11_window_get_xid GDK_WINDOW_XWINDOW #define gtk_widget_get_preferred_size(x,y,z) gtk_widget_size_request(x,z) +#ifdef GDK_WINDOWING_X11 +#define GDK_IS_X11_WINDOW(window) TRUE +#endif +#ifdef GDK_WINDOWING_WIN32 +#define GDK_IS_WIN32_WINDOW(window) TRUE +#endif +#ifdef GDK_WINDOWING_QUARTZ +#define GDK_IS_QUARTZ_WINDOW(window) TRUE +#endif + #if !GTK_CHECK_VERSION(2,24,0) #define gdk_x11_set_sm_client_id gdk_set_sm_client_id
--- a/pidgin/gtkaccount.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkaccount.c Fri Jan 25 02:22:38 2013 -0500 @@ -185,7 +185,7 @@ if (data != NULL) { if (len > 0) - dialog->icon_img = purple_imgstore_add(data, len, new_icon_path); + dialog->icon_img = purple_imgstore_new(data, len, new_icon_path); else g_free(data); }
--- a/pidgin/gtkblist.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkblist.c Fri Jan 25 02:22:38 2013 -0500 @@ -1147,6 +1147,10 @@ chat_select_account_cb(GObject *w, PurpleAccount *account, PidginChatData *data) { + g_return_if_fail(w != NULL); + g_return_if_fail(data != NULL); + g_return_if_fail(account != NULL); + if (strcmp(purple_account_get_protocol_id(data->rq_data.account), purple_account_get_protocol_id(account)) == 0) { @@ -3884,8 +3888,7 @@ { pce = cur->data; - if (!pce->secret && (!pce->required && - g_hash_table_lookup(purple_chat_get_components(chat), pce->identifier) == NULL)) + if (!pce->secret) { tmp = purple_text_strip_mnemonic(pce->label); name = g_markup_escape_text(tmp, -1); @@ -5142,6 +5145,10 @@ gtkblist->headline_destroy(gtkblist->headline_data); reset_headline(gtkblist); } + } else { + if (gtkblist->headline_destroy) + gtkblist->headline_destroy(gtkblist->headline_data); + reset_headline(gtkblist); } return FALSE; @@ -5152,7 +5159,7 @@ { GdkCursor *hand_cursor = gdk_cursor_new(GDK_HAND2); gdk_window_set_cursor(gtk_widget_get_window(widget), hand_cursor); - gdk_cursor_unref(hand_cursor); + g_object_unref(hand_cursor); } static gboolean
--- a/pidgin/gtkconv-theme.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkconv-theme.c Fri Jan 25 02:22:38 2013 -0500 @@ -66,6 +66,8 @@ char *incoming_next_context_html; char *outgoing_next_context_html; char *basestyle_css; + + GArray *nick_colors; } PidginConvThemePrivate; /****************************************************************************** @@ -498,6 +500,9 @@ } g_free(priv->variant); + if (priv->nick_colors) + g_array_unref(priv->nick_colors); + parent_class->finalize(obj); } @@ -749,3 +754,44 @@ } } +GArray * +pidgin_conversation_theme_get_nick_colors(PidginConvTheme *theme) +{ + PidginConvThemePrivate *priv; + const char *dir; + + g_return_val_if_fail(theme != NULL, NULL); + + priv = PIDGIN_CONV_THEME_GET_PRIVATE(theme); + + dir = purple_theme_get_dir(PURPLE_THEME(theme)); + if (NULL == priv->nick_colors) + { + char *file = g_build_filename(dir, "Contents", "Resources", "Incoming", "SenderColors.txt", NULL); + char *contents; + priv->nick_colors = g_array_new(FALSE, FALSE, sizeof(GdkColor)); + if (g_file_get_contents(file, &contents, NULL, NULL)) { + int i; + gchar ** color_strings = g_strsplit_set(contents, "\r\n:", -1); + + for(i=0; color_strings[i]; i++) + { + GdkColor color; + if(gdk_color_parse(color_strings[i], &color)) + { + g_array_append_val(priv->nick_colors, color); + } + } + + g_strfreev(color_strings); + g_free(contents); + } + g_free(file); + } + + if(priv->nick_colors->len) + return g_array_ref(priv->nick_colors); + else + return NULL; +} +
--- a/pidgin/gtkconv-theme.h Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkconv-theme.h Fri Jan 25 02:22:38 2013 -0500 @@ -191,6 +191,15 @@ */ char *pidgin_conversation_theme_get_css_path(PidginConvTheme *theme); +/** + * Get (and reference) the array of nick colors + * + * @param theme The conversation theme + * + * @return Pointer to GArray of nick colors, or NULL if no colors in theme + */ +GArray *pidgin_conversation_theme_get_nick_colors(PidginConvTheme *theme); + G_END_DECLS #endif /* PIDGIN_CONV_THEME_H */
--- a/pidgin/gtkconv.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkconv.c Fri Jan 25 02:22:38 2013 -0500 @@ -33,13 +33,6 @@ # include <X11/Xlib.h> #endif -#ifdef USE_GTKSPELL -# include <gtkspell/gtkspell.h> -# ifdef _WIN32 -# include "wspell.h" -# endif -#endif - #include <gdk/gdkkeysyms.h> #include "account.h" @@ -157,9 +150,8 @@ #define MIN_BRIGHTNESS_CONTRAST 75 #define MIN_COLOR_CONTRAST 200 -#define NUM_NICK_COLORS 220 -static GdkColor *nick_colors = NULL; -static guint nbr_nick_colors; +#define NICK_COLOR_GENERATE_COUNT 220 +static GArray *generated_nick_colors = NULL; /* These probably won't conflict with any WebKit values. */ #define PIDGIN_DRAG_BLIST_NODE (1337) @@ -215,7 +207,7 @@ static void update_typing_icon(PidginConversation *gtkconv); static void update_typing_message(PidginConversation *gtkconv, const char *message); gboolean pidgin_conv_has_focus(PurpleConversation *conv); -static GdkColor* generate_nick_colors(guint *numcolors, GdkColor background); +static GArray* generate_nick_colors(guint numcolors, GdkColor background); static gboolean color_is_visible(GdkColor foreground, GdkColor background, int color_contrast, int brightness_contrast); static GtkTextTag *get_buddy_tag(PurpleConversation *conv, const char *who, PurpleMessageFlags flag, gboolean create); static void pidgin_conv_update_fields(PurpleConversation *conv, PidginConvFields fields); @@ -234,7 +226,7 @@ GtkStyle *style = gtk_widget_get_style(gtkconv->webview); float scale; - col = nick_colors[g_str_hash(name) % nbr_nick_colors]; + col = g_array_index(gtkconv->nick_colors, GdkColor, g_str_hash(name) % gtkconv->nick_colors->len); scale = ((1-(LUMINANCE(style->base[GTK_STATE_NORMAL]) / LUMINANCE(style->white))) * (LUMINANCE(style->white)/MAX(MAX(col.red, col.blue), col.green))); @@ -1652,6 +1644,8 @@ node = webkit_dom_node_list_item(nodes, len - 1); } + g_object_unref(nodes); + return node; } @@ -3321,15 +3315,10 @@ return FALSE; buddy = purple_find_buddy(account, purple_conversation_get_name(conv)); - - /* gotta remain bug-compatible :( libpurple < 2.0.2 didn't handle - * removing "isolated" buddy nodes well */ - if (purple_version_check(2, 0, 2) == NULL) { - if ((buddy == NULL) && (gtkconv->webview != NULL)) { - buddy = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_buddy"); - } - - if ((buddy == NULL) && (gtkconv->webview != NULL)) { + if (!buddy && gtkconv->webview) { + buddy = g_object_get_data(G_OBJECT(gtkconv->webview), "transient_buddy"); + + if (!buddy) { buddy = purple_buddy_new(account, purple_conversation_get_name(conv), NULL); purple_blist_node_set_flags((PurpleBlistNode *)buddy, PURPLE_BLIST_NODE_FLAG_NO_SAVE); @@ -5795,6 +5784,7 @@ gtkconv->theme = PIDGIN_CONV_THEME(g_object_ref(theme)); gtkconv->last_flags = 0; + if (conv_type == PURPLE_CONV_TYPE_IM) { gtkconv->u.im = g_malloc0(sizeof(PidginImPane)); } else if (conv_type == PURPLE_CONV_TYPE_CHAT) { @@ -5892,9 +5882,13 @@ else pidgin_conv_placement_place(gtkconv); - if (nick_colors == NULL) { - nbr_nick_colors = NUM_NICK_COLORS; - nick_colors = generate_nick_colors(&nbr_nick_colors, gtk_widget_get_style(gtkconv->webview)->base[GTK_STATE_NORMAL]); + if (generated_nick_colors == NULL) { + generated_nick_colors = generate_nick_colors(NICK_COLOR_GENERATE_COUNT, gtk_widget_get_style(gtkconv->webview)->base[GTK_STATE_NORMAL]); + } + + if(NULL == (gtkconv->nick_colors = pidgin_conversation_theme_get_nick_colors(gtkconv->theme))) + { + gtkconv->nick_colors = g_array_ref(generated_nick_colors); } if (purple_conversation_get_features(conv) & PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY) @@ -6010,6 +6004,8 @@ g_source_remove(gtkconv->attach.timer); } + g_array_unref(gtkconv->nick_colors); + g_object_disconnect(G_OBJECT(gtkconv->theme), "any_signal::notify", conv_variant_changed_cb, gtkconv, NULL); g_object_unref(gtkconv->theme); @@ -6320,6 +6316,10 @@ } else if (g_str_has_prefix(cur, "%sender%")) { replace = alias; + } else if (g_str_has_prefix(cur, "%senderColor%")) { + const GdkColor *color = get_nick_color(PIDGIN_CONVERSATION(conv), name); + replace = freeval = g_strdup_printf("#%02x%02x%02x", (color->red >> 8), (color->green >> 8), (color->blue >> 8)); + } else if (g_str_has_prefix(cur, "%service%")) { replace = purple_account_get_protocol_name(purple_conversation_get_account(conv)); @@ -7409,15 +7409,28 @@ const char *topic = gtkconv->u.chat->topic_text ? gtk_entry_get_text(GTK_ENTRY(gtkconv->u.chat->topic_text)) : NULL; - char *esc = NULL, *tmp; - esc = topic ? g_markup_escape_text(topic, -1) : NULL; - tmp = g_markup_escape_text(purple_conversation_get_title(conv), -1); - markup = g_strdup_printf("%s%s<span color='%s' size='smaller'>%s</span>", - tmp, esc && *esc ? "\n" : "", + const char *title = purple_conversation_get_title(conv); + const char *name = purple_conversation_get_name(conv); + + char *topic_esc, *unaliased, *unaliased_esc, *title_esc; + + topic_esc = topic ? g_markup_escape_text(topic, -1) : NULL; + unaliased = g_utf8_collate(title, name) ? g_strdup_printf("(%s)", name) : NULL; + unaliased_esc = unaliased ? g_markup_escape_text(unaliased, -1) : NULL; + title_esc = g_markup_escape_text(title, -1); + + markup = g_strdup_printf("%s%s<span size='smaller'>%s</span>%s<span color='%s' size='smaller'>%s</span>", + title_esc, + unaliased_esc ? " " : "", + unaliased_esc ? unaliased_esc : "", + topic_esc && *topic_esc ? "\n" : "", pidgin_get_dim_grey_string(gtkconv->infopane), - esc ? esc : ""); - g_free(tmp); - g_free(esc); + topic_esc ? topic_esc : ""); + + g_free(title_esc); + g_free(topic_esc); + g_free(unaliased); + g_free(unaliased_esc); } gtk_list_store_set(gtkconv->infopane_model, &(gtkconv->infopane_iter), CONV_TEXT_COLUMN, markup, -1); @@ -7887,7 +7900,6 @@ spellcheck_pref_cb(const char *name, PurplePrefType type, gconstpointer value, gpointer data) { -#ifdef USE_GTKSPELL GList *cl; PurpleConversation *conv; PidginConversation *gtkconv; @@ -7901,9 +7913,9 @@ gtkconv = PIDGIN_CONVERSATION(conv); - pidgin_webview_set_spellcheck(GTK_WEBVIEW(gtkconv->entry), value); - } -#endif + pidgin_webview_set_spellcheck(GTK_WEBVIEW(gtkconv->entry), + (gboolean)GPOINTER_TO_INT(value)); + } } static void @@ -8775,6 +8787,7 @@ default_conv_theme = purple_theme_manager_load_theme(theme_dir, "conversation"); g_free(theme_dir); +#if !GTK_CHECK_VERSION(3,0,0) { /* Set default tab colors */ GString *str = g_string_new(NULL); @@ -8810,6 +8823,7 @@ g_string_free(str, TRUE); gtk_rc_reset_styles(settings); } +#endif } void @@ -10099,12 +10113,12 @@ void pidgin_conv_window_destroy(PidginWindow *win) { - PidginConversation *gtkconv; - GList *iter; - if (win->gtkconvs) { - for (iter = win->gtkconvs; iter != NULL; iter = iter->next) { - gtkconv = iter->data; + GList *iter = win->gtkconvs; + while (iter) + { + PidginConversation *gtkconv = iter->data; + iter = iter->next; close_conv_cb(NULL, gtkconv); } return; @@ -10170,6 +10184,52 @@ return FALSE; } +#if GTK_CHECK_VERSION(3,0,0) +static void +set_default_tab_colors(GtkWidget *widget) +{ + GString *str; + GtkCssProvider *provider; + GError *error = NULL; + int iter; + + struct { + const char *labelname; + const char *color; + } styles[] = { + {"tab-label-typing", "#4e9a06"}, + {"tab-label-typed", "#c4a000"}, + {"tab-label-attention", "#006aff"}, + {"tab-label-unreadchat", "#cc0000"}, + {"tab-label-event", "#888a85"}, + {NULL, NULL} + }; + + str = g_string_new(NULL); + + for (iter = 0; styles[iter].labelname; iter++) { + g_string_append_printf(str, + "#%s {\n" + " color: %s;\n" + "}\n", + styles[iter].labelname, + styles[iter].color); + } + + provider = gtk_css_provider_new(); + + gtk_css_provider_load_from_data(provider, str->str, str->len, &error); + + gtk_style_context_add_provider(gtk_widget_get_style_context(widget), + GTK_STYLE_PROVIDER(provider), + GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); + + if (error) + g_error_free(error); + g_string_free(str, TRUE); +} +#endif + void pidgin_conv_window_add_gtkconv(PidginWindow *win, PidginConversation *gtkconv) { @@ -10208,6 +10268,9 @@ /* Tab label. */ gtkconv->tab_label = gtk_label_new(tmp_lab = purple_conversation_get_title(conv)); +#if GTK_CHECK_VERSION(3,0,0) + set_default_tab_colors(gtkconv->tab_label); +#endif gtk_widget_set_name(gtkconv->tab_label, "tab-label"); gtkconv->menu_tabby = gtk_hbox_new(FALSE, PIDGIN_HIG_BOX_SPACE); @@ -10944,12 +11007,11 @@ } -static GdkColor* -generate_nick_colors(guint *color_count, GdkColor background) -{ - guint numcolors = *color_count; +static GArray* +generate_nick_colors(guint numcolors, GdkColor background) +{ guint i = 0, j = 0; - GdkColor *colors = g_new(GdkColor, numcolors); + GArray *colors = g_array_new(FALSE, FALSE, sizeof(GdkColor)); GdkColor nick_highlight; GdkColor send_color; time_t breakout_time; @@ -10973,7 +11035,7 @@ color_is_visible(color, nick_highlight, MIN_COLOR_CONTRAST / 2, 0) && color_is_visible(color, send_color, MIN_COLOR_CONTRAST / 4, 0)) { - colors[i] = color; + g_array_append_val(colors, color); i++; } j++; @@ -10992,17 +11054,13 @@ color_is_visible(color, nick_highlight, MIN_COLOR_CONTRAST / 2, 0) && color_is_visible(color, send_color, MIN_COLOR_CONTRAST / 4, 0)) { - colors[i] = color; + g_array_append_val(colors, color); i++; } } if (i < numcolors) { - GdkColor *c = colors; purple_debug_warning("gtkconv", "Unable to generate enough random colors before timeout. %u colors found.\n", i); - colors = g_memdup(c, i * sizeof(GdkColor)); - g_free(c); - *color_count = i; } return colors;
--- a/pidgin/gtkconv.h Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkconv.h Fri Jan 25 02:22:38 2013 -0500 @@ -101,6 +101,7 @@ GtkWidget *menu_tabby; PidginConvTheme *theme; + GArray *nick_colors; PurpleMessageFlags last_flags; GtkWidget *webview; GtkWidget *entry;
--- a/pidgin/gtkdebug.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkdebug.c Fri Jan 25 02:22:38 2013 -0500 @@ -76,7 +76,22 @@ /* Regex */ \ "div.hide{display:none;}" \ "span.regex{background-color:#ffafaf;font-weight:bold;}" \ - "</style></head><body class=l%d></body></html>" + "</style><script>" \ + "function append(level, time, cat, msg) {" \ + "var div = document.createElement('div');" \ + "div.className = 'l' + level;" \ + "div.appendChild(document.createTextNode('(' + time + ') '));" \ + "if (cat) {" \ + "var cat_n = document.createElement('b');" \ + "cat_n.appendChild(document.createTextNode(cat + ':'));" \ + "div.appendChild(cat_n);" \ + "div.appendChild(document.createTextNode(' '));" \ + "}" \ + "div.appendChild(document.createTextNode(msg));" \ + "document.body.appendChild(div);" \ + "alert('appended');" \ + "}" \ + "</script></head><body class=l%d></body></html>" static DebugWindow *debug_win = NULL; static guint debug_enabled_timer = 0; @@ -94,7 +109,8 @@ text = gtk_entry_get_text(GTK_ENTRY(debug_win->expression)); purple_prefs_set_string(PIDGIN_PREFS_ROOT "/debug/regex", text); } - g_regex_unref(debug_win->regex); + if (debug_win->regex != NULL) + g_regex_unref(debug_win->regex); /* If the "Save Log" dialog is open then close it */ purple_request_close_with_handle(debug_win); @@ -207,8 +223,16 @@ if (!WEBKIT_DOM_IS_HTML_ELEMENT(div)) return; +#if (WEBKIT_MAJOR_VERSION == 1 && \ + WEBKIT_MINOR_VERSION == 9 && \ + WEBKIT_MICRO_VERSION == 90) + /* Workaround WebKit API bug. */ + classes = webkit_dom_element_get_class_list(WEBKIT_DOM_ELEMENT(div)); +#else classes = webkit_dom_html_element_get_class_list(WEBKIT_DOM_HTML_ELEMENT(div)); +#endif webkit_dom_dom_token_list_toggle(classes, "hide", NULL); + g_object_unref(classes); } static void @@ -235,6 +259,8 @@ webkit_dom_node_replace_child(parent, WEBKIT_DOM_NODE(text), span, &err); } + + g_object_unref(nodes); } static void @@ -395,11 +421,14 @@ /* Re-show debug lines that didn't match regex */ list = webkit_dom_document_get_elements_by_class_name(dom, "hide"); i = webkit_dom_node_list_get_length(list); + while (i--) { WebKitDOMNode *div = webkit_dom_node_list_item(list, i); regex_toggle_div(div); } + g_object_unref(list); + if (filter) { list = webkit_dom_document_get_elements_by_tag_name(dom, "div"); @@ -407,6 +436,8 @@ WebKitDOMNode *div = webkit_dom_node_list_item(list, i); regex_match(win, dom, div); } + + g_object_unref(list); } } @@ -624,25 +655,39 @@ } static void -regex_html_appended_cb(GtkWebView *webview, WebKitDOMRange *range, DebugWindow *win) +debug_window_appended(DebugWindow *win) { - if (!win || !win->window) + WebKitDOMDocument *dom; + WebKitDOMHTMLElement *body; + WebKitDOMNode *div; + + if (!gtk_toggle_tool_button_get_active( + GTK_TOGGLE_TOOL_BUTTON(win->filter))) return; - if (gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) { - WebKitDOMDocument *dom; - WebKitDOMHTMLElement *body; - WebKitDOMNode *div; + dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(win->text)); + body = webkit_dom_document_get_body(dom); + div = webkit_dom_node_get_last_child(WEBKIT_DOM_NODE(body)); + + if (webkit_dom_element_webkit_matches_selector( + WEBKIT_DOM_ELEMENT(div), "body>div:not(#pause)", NULL)) + regex_match(win, dom, div); +} - dom = webkit_web_view_get_dom_document(WEBKIT_WEB_VIEW(win->text)); - body = webkit_dom_document_get_body(dom); - div = webkit_dom_node_get_last_child(WEBKIT_DOM_NODE(body)); +static gboolean debug_window_alert_cb(WebKitWebView *webview, + WebKitWebFrame *frame, gchar *message, gpointer _win) +{ + DebugWindow *win = _win; - if (webkit_dom_element_webkit_matches_selector(WEBKIT_DOM_ELEMENT(div), - "body>div:not(#pause)", - NULL)) - regex_match(win, dom, div); + if (!win || !win->window) + return FALSE; + + if (g_strcmp0(message, "appended") == 0) { + debug_window_appended(win); + return TRUE; } + + return FALSE; } static DebugWindow * @@ -816,8 +861,8 @@ gtk_box_pack_start(GTK_BOX(vbox), frame, TRUE, TRUE, 0); gtk_widget_show(frame); - g_signal_connect(G_OBJECT(win->text), "html-appended", - G_CALLBACK(regex_html_appended_cb), win); + g_signal_connect(G_OBJECT(win->text), "script-alert", + G_CALLBACK(debug_window_alert_cb), win); clear_cb(NULL, win); @@ -943,6 +988,7 @@ REGISTER_G_LOG_HANDLER("GModule"); REGISTER_G_LOG_HANDLER("GLib-GObject"); REGISTER_G_LOG_HANDLER("GThread"); + REGISTER_G_LOG_HANDLER("Json"); #ifdef USE_GSTREAMER REGISTER_G_LOG_HANDLER("GStreamer"); #endif @@ -984,41 +1030,29 @@ static void pidgin_debug_print(PurpleDebugLevel level, const char *category, - const char *arg_s) + const char *arg_s) { - gchar *ts_s; - gchar *esc_s, *cat_s, *tmp, *s; + gchar *esc_s; const char *mdate; time_t mtime; + gchar *js; - if (debug_win == NULL || - !purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled")) - { + if (debug_win == NULL) return; - } + if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled")) + return; mtime = time(NULL); mdate = purple_utf8_strftime("%H:%M:%S", localtime(&mtime)); - ts_s = g_strdup_printf("(%s) ", mdate); - if (category == NULL) - cat_s = g_strdup(""); - else - cat_s = g_strdup_printf("<b>%s:</b> ", category); - tmp = purple_utf8_try_convert(arg_s); - esc_s = g_markup_escape_text(tmp, -1); + esc_s = purple_escape_js(arg_s); - s = g_strdup_printf("<div class=\"l%d\">%s%s%s</div>", - level, ts_s, cat_s, esc_s); - - g_free(ts_s); - g_free(cat_s); + js = g_strdup_printf("append(%d, '%s', '%s', %s);", + level, mdate, category ? category : "", esc_s); g_free(esc_s); - g_free(tmp); - gtk_webview_append_html(GTK_WEBVIEW(debug_win->text), s); - - g_free(s); + gtk_webview_safe_execute_script(GTK_WEBVIEW(debug_win->text), js); + g_free(js); } static gboolean
--- a/pidgin/gtkdialogs.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkdialogs.c Fri Jan 25 02:22:38 2013 -0500 @@ -207,6 +207,7 @@ {NULL, NULL, "Erdal Ronahi", "erdal.ronahi@gmail.com"}, {NULL, NULL, "Rizoyê Xerzî", "rizoxerzi@hotmail.com"}, {N_("Lao"), "lo", "Anousak Souphavah", "anousak@gmail.com"}, + {N_("Lithuanian"), "lt", "Algimantas Margevičius", "margevicius.algimantas@gmail.com"}, {N_("Maithili"), "mai", "Sangeeta Kumari", "sangeeta_0975@yahoo.com"}, {NULL, NULL, "Rajesh Ranjan", "rajeshkajha@yahoo.com"}, {N_("Meadow Mari"), "mhr", "David Preece", "davidpreece1@gmail.com"}, @@ -219,7 +220,7 @@ {N_("Malay"), "ms_MY", "Muhammad Najmi bin Ahmad Zabidi", "najmi.zabidi@gmail.com"}, {N_("Burmese"), "my_MM", "Thura Hlaing", "trhura@gmail.com"}, {N_("Bokmål Norwegian"), "nb", "Hans Fredrik Nordhaug", "hans@nordhaug.priv.no"}, - {N_("Nepali"), "ne", "Shyam Krishna Bal", "shyamkrishna_bal@yahoo.com"}, + {N_("Nepali"), "ne", "Shyam Krishna Bal", NULL}, {N_("Dutch, Flemish"), "nl", "Gideon van Melle", "translations@gvmelle.com"}, {N_("Norwegian Nynorsk"), "nn", "Yngve Spjeld Landro", "l10n@landro.net"}, {N_("Occitan"), "oc", "Yannig Marchegay", "yannig@marchegay.org"}, @@ -246,7 +247,6 @@ {NULL, NULL, "Viveka Nathan K", "vivekanathan@users.sourceforge.net"}, {N_("Telugu"), "te", "Krishnababu Krottapalli", "krottapalli@ymail.com"}, {N_("Thai"), "th", "Isriya Paireepairit", "markpeak@gmail.com"}, - {N_("Turkish"), "tr", "Serdar Soytetir", "tulliana@gmail.com"}, {N_("Ukranian"), "uk", "Oleksandr Kovalenko", "alx.kovalenko@gmail.com"}, {N_("Urdu"), "ur", "RKVS Raman", "rkvsraman@gmail.com"}, {N_("Vietnamese"), "vi", "Nguyễn Vũ Hưng", "vuhung16plus@gmail.com"}, @@ -324,7 +324,8 @@ {N_("Swedish"), "sv", "Tore Lundqvist", NULL}, {NULL, NULL, "Christian Rose", NULL}, {N_("Telugu"), "te", "Mr. Subbaramaih", "info.gist@cdac.in"}, - {N_("Turkish"), "tr", "Ahmet Alp Balkan", NULL}, + {N_("Turkish"), "tr", "Serdar Soytetir", "tulliana@gmail.com"}, + {NULL, "tr", "Ahmet Alp Balkan", NULL}, {N_("Vietnamese"), "vi", N_("T.M.Thanh and the Gnome-Vi Team"), "gnomevi-list@lists.sf.net"}, {N_("Simplified Chinese"), "zh_CN", "Hashao", NULL}, {NULL, NULL, "Rocky S. Lee", NULL},
--- a/pidgin/gtkdocklet.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkdocklet.c Fri Jan 25 02:22:38 2013 -0500 @@ -44,6 +44,7 @@ #include "pidginstock.h" #include "gtkdocklet.h" #include "gtkdialogs.h" +#include "gtknotify.h" #include "gtk3compat.h" @@ -58,8 +59,7 @@ static GtkStatusIcon *docklet = NULL; static guint embed_timeout = 0; static PurpleStatusPrimitive status = PURPLE_STATUS_OFFLINE; -static gboolean pending = FALSE; -static gboolean connecting = FALSE; +static PidginDockletFlag flags = 0; static gboolean enable_join_chat = FALSE; static gboolean visible = FALSE; static gboolean visibility_manager = FALSE; @@ -71,8 +71,14 @@ /************************************************************************** * docklet status and utility functions **************************************************************************/ +static inline gboolean +docklet_is_blinking() +{ + return flags && !(flags & PIDGIN_DOCKLET_CONNECTING); +} + static void -docklet_gtk_status_update_icon(PurpleStatusPrimitive status, gboolean connecting, gboolean pending) +docklet_gtk_status_update_icon(PurpleStatusPrimitive status, PidginDockletFlag newflag) { const gchar *icon_name = NULL; @@ -97,9 +103,11 @@ break; } - if (pending) + if (newflag & PIDGIN_DOCKLET_EMAIL_PENDING) + icon_name = PIDGIN_STOCK_TRAY_EMAIL; + if (newflag & PIDGIN_DOCKLET_CONV_PENDING) icon_name = PIDGIN_STOCK_TRAY_PENDING; - if (connecting) + if (newflag & PIDGIN_DOCKLET_CONNECTING) icon_name = PIDGIN_STOCK_TRAY_CONNECT; if (icon_name) { @@ -108,7 +116,11 @@ #if !GTK_CHECK_VERSION(3,0,0) if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/blink")) { - gtk_status_icon_set_blinking(docklet, (pending && !connecting)); + gboolean pending = FALSE; + pending |= (newflag & PIDGIN_DOCKLET_EMAIL_PENDING); + pending |= (newflag & PIDGIN_DOCKLET_CONV_PENDING); + gtk_status_icon_set_blinking(docklet, pending && + !(newflag & PIDGIN_DOCKLET_CONNECTING)); } else if (gtk_status_icon_get_blinking(docklet)) { gtk_status_icon_set_blinking(docklet, FALSE); } @@ -129,7 +141,7 @@ return l_im; l_chat = pidgin_conversations_find_unseen_list(PURPLE_CONV_TYPE_CHAT, - PIDGIN_UNSEEN_NICK, + purple_prefs_get_int(PIDGIN_PREFS_ROOT "/conversations/notification_chat"), FALSE, max); if (l_im != NULL && l_chat != NULL) @@ -147,7 +159,7 @@ int count; PurpleSavedStatus *saved_status; PurpleStatusPrimitive newstatus = PURPLE_STATUS_OFFLINE; - gboolean newpending = FALSE, newconnecting = FALSE; + PidginDockletFlag newflags = 0; /* get the current savedstatus */ saved_status = purple_savedstatus_get_current(); @@ -174,7 +186,7 @@ if (convs != NULL) { /* set tooltip if messages are pending */ GString *tooltip_text = g_string_new(""); - newpending = TRUE; + newflags |= PIDGIN_DOCKLET_CONV_PENDING; for (l = convs, count = 0 ; l != NULL ; l = l->next, count++) { PurpleConversation *conv = (PurpleConversation *)l->data; @@ -223,18 +235,20 @@ continue; if (purple_account_is_connecting(account)) - newconnecting = TRUE; + newflags |= PIDGIN_DOCKLET_CONNECTING; + + if (pidgin_notify_emails_pending()) + newflags |= PIDGIN_DOCKLET_EMAIL_PENDING; } newstatus = purple_savedstatus_get_type(saved_status); /* update the icon if we changed status */ - if (status != newstatus || pending!=newpending || connecting!=newconnecting) { + if (status != newstatus || flags != newflags) { status = newstatus; - pending = newpending; - connecting = newconnecting; + flags = newflags; - docklet_gtk_status_update_icon(status, connecting, pending); + docklet_gtk_status_update_icon(status, flags); } return FALSE; /* for when we're called by the glib idle handler */ @@ -709,7 +723,7 @@ menuitem = gtk_menu_item_new_with_mnemonic(_("_Unread Messages")); - if (pending) { + if (flags & PIDGIN_DOCKLET_CONV_PENDING) { GtkWidget *submenu = gtk_menu_new(); GList *l = get_pending_list(0); if (l == NULL) { @@ -785,7 +799,9 @@ { switch (button_type) { case 1: - if (pending) { + if (flags & PIDGIN_DOCKLET_EMAIL_PENDING) { + pidgin_notify_emails_present(NULL); + } else if (flags & PIDGIN_DOCKLET_CONV_PENDING) { GList *l = get_pending_list(1); if (l != NULL) { pidgin_conv_present_conversation((PurpleConversation *)l->data); @@ -811,7 +827,7 @@ } visible = TRUE; docklet_update_status(); - docklet_gtk_status_update_icon(status, connecting, pending); + docklet_gtk_status_update_icon(status, flags); } static void @@ -827,14 +843,6 @@ } } -static gboolean -docklet_gtk_recreate_cb(gpointer data) -{ - docklet_gtk_status_create(TRUE); - - return FALSE; -} - #ifndef _WIN32 static gboolean docklet_gtk_embed_timeout_cb(gpointer data) @@ -896,19 +904,6 @@ #endif static void -docklet_gtk_destroyed_cb(GtkWidget *widget, gpointer data) -{ - purple_debug_info("docklet", "destroyed\n"); - - pidgin_docklet_remove(); - - g_object_unref(G_OBJECT(docklet)); - docklet = NULL; - - g_idle_add(docklet_gtk_recreate_cb, NULL); -} - -static void docklet_gtk_status_activated_cb(GtkStatusIcon *status_icon, gpointer user_data) { pidgin_docklet_clicked(1); @@ -939,7 +934,6 @@ } gtk_status_icon_set_visible(docklet, FALSE); - g_signal_handlers_disconnect_by_func(G_OBJECT(docklet), G_CALLBACK(docklet_gtk_destroyed_cb), NULL); g_object_unref(G_OBJECT(docklet)); docklet = NULL; @@ -965,7 +959,6 @@ #if GTK_CHECK_VERSION(2,12,0) g_signal_connect(G_OBJECT(docklet), "notify::embedded", G_CALLBACK(docklet_gtk_embedded_cb), NULL); #endif - g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_gtk_destroyed_cb), NULL); gtk_status_icon_set_visible(docklet, TRUE); @@ -1025,6 +1018,7 @@ void *accounts_handle = purple_accounts_get_handle(); void *status_handle = purple_savedstatuses_get_handle(); void *docklet_handle = pidgin_docklet_get_handle(); + void *notify_handle = purple_notify_get_handle(); gchar *tmp; purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet"); @@ -1032,6 +1026,7 @@ purple_prefs_add_string(PIDGIN_PREFS_ROOT "/docklet/show", "always"); purple_prefs_connect_callback(docklet_handle, PIDGIN_PREFS_ROOT "/docklet/show", docklet_show_pref_changed_cb, NULL); + purple_prefs_add_int(PIDGIN_PREFS_ROOT "/conversations/notification_chat", PIDGIN_UNSEEN_TEXT); purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet/gtk"); if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded")) { @@ -1064,6 +1059,12 @@ docklet_handle, PURPLE_CALLBACK(docklet_conv_updated_cb), NULL); purple_signal_connect(status_handle, "savedstatus-changed", docklet_handle, PURPLE_CALLBACK(docklet_update_status_cb), NULL); + purple_signal_connect(notify_handle, "displaying-email-notification", + docklet_handle, PURPLE_CALLBACK(docklet_update_status_cb), NULL); + purple_signal_connect(notify_handle, "displaying-emails-notification", + docklet_handle, PURPLE_CALLBACK(docklet_update_status_cb), NULL); + purple_signal_connect(notify_handle, "displaying-emails-clear", + docklet_handle, PURPLE_CALLBACK(docklet_update_status_cb), NULL); #if 0 purple_signal_connect(purple_get_core(), "quitting", docklet_handle, PURPLE_CALLBACK(purple_quit_cb), NULL);
--- a/pidgin/gtkdocklet.h Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkdocklet.h Fri Jan 25 02:22:38 2013 -0500 @@ -27,6 +27,13 @@ G_BEGIN_DECLS +typedef enum +{ + PIDGIN_DOCKLET_CONNECTING = 0x1, + PIDGIN_DOCKLET_CONV_PENDING = 0x2, + PIDGIN_DOCKLET_EMAIL_PENDING = 0x4 +} PidginDockletFlag; + /** * Returns the GtkStatusIcon used for the docklet. */
--- a/pidgin/gtkeventloop.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkeventloop.c Fri Jan 25 02:22:38 2013 -0500 @@ -124,11 +124,7 @@ pidgin_input_add, g_source_remove, NULL, /* input_get_error */ -#if GLIB_CHECK_VERSION(2,14,0) g_timeout_add_seconds, -#else - NULL, -#endif NULL, NULL, NULL
--- a/pidgin/gtkimhtml.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkimhtml.c Fri Jan 25 02:22:38 2013 -0500 @@ -2523,7 +2523,7 @@ [19:58] <Robot101> marv: images go into the imgstore, a refcounted... well.. hash. :) [19:59] <KingAnt> marv: I think the image tag used by the core is something like <img id="#"/> [19:59] Ro0tSiEgE robert42 RobFlynn Robot101 ross22 roz -[20:00] <KingAnt> marv: Where the ID is the what is returned when you add the image to the imgstore using purple_imgstore_add +[20:00] <KingAnt> marv: Where the ID is the what is returned when you add the image to the imgstore using purple_imgstore_new [20:00] <marv> Robot101: so how does the image get passed to serv_got_im() and serv_send_im()? just as the <img id="#" and then the prpl looks it up from the store? [20:00] <KingAnt> marv: Right [20:00] <marv> alright @@ -3963,8 +3963,7 @@ gtk_container_add(GTK_CONTAINER(box), GTK_WIDGET(image->image)); - if(!gtk_check_version(2, 4, 0)) - g_object_set(G_OBJECT(box), "visible-window", FALSE, NULL); + g_object_set(G_OBJECT(box), "visible-window", FALSE, NULL); gtk_widget_show(GTK_WIDGET(image->image)); gtk_widget_show(box);
--- a/pidgin/gtkimhtmltoolbar.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkimhtmltoolbar.c Fri Jan 25 02:22:38 2013 -0500 @@ -529,7 +529,7 @@ name = strrchr(filename, G_DIR_SEPARATOR) + 1; - id = purple_imgstore_add_with_id(filedata, size, name); + id = purple_imgstore_new_with_id(filedata, size, name); if (id == 0) { buf = g_strdup_printf(_("Failed to store image: %s\n"), filename);
--- a/pidgin/gtkmain.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkmain.c Fri Jan 25 02:22:38 2013 -0500 @@ -88,10 +88,6 @@ SIGINT, SIGTERM, SIGQUIT, - SIGCHLD, -#if defined(USE_GSTREAMER) && !defined(GST_CAN_DISABLE_FORKING) - SIGALRM, -#endif -1 }; @@ -136,29 +132,6 @@ static void sighandler(int sig); -/* - * This child process reaping stuff is currently only used for processes that - * were forked to play sounds. It's not needed for forked DNS child, which - * have their own waitpid() call. It might be wise to move this code into - * gtksound.c. - */ -static void -clean_pid(void) -{ - int status; - pid_t pid; - - do { - pid = waitpid(-1, &status, WNOHANG); - } while (pid != 0 && pid != (pid_t)-1); - - if ((pid == (pid_t) - 1) && (errno != ECHILD)) { - char errmsg[BUFSIZ]; - snprintf(errmsg, sizeof(errmsg), "Warning: waitpid() returned %d", pid); - perror(errmsg); - } -} - static void sighandler(int sig) { ssize_t written; @@ -209,33 +182,8 @@ return FALSE; } - switch (sig) { -#if defined(USE_GSTREAMER) && !defined(GST_CAN_DISABLE_FORKING) -/* By default, gstreamer forks when you initialize it, and waitpids for the - * child. But if libpurple reaps the child rather than leaving it to - * gstreamer, gstreamer's initialization fails. So, we wait a second before - * reaping child processes, to give gst a chance to reap it if it wants to. - * - * This is not needed in later gstreamers, which let us disable the forking. - * And, it breaks the world on some Real Unices. - */ - case SIGCHLD: - /* Restore signal catching */ - signal(SIGCHLD, sighandler); - alarm(1); - break; - case SIGALRM: -#else - case SIGCHLD: -#endif - clean_pid(); - /* Restore signal catching */ - signal(SIGCHLD, sighandler); - break; - default: - purple_debug_warning("sighandler", "Caught signal %d\n", sig); - purple_core_quit(); - } + purple_debug_warning("sighandler", "Caught signal %d\n", sig); + purple_core_quit(); return TRUE; } @@ -465,8 +413,8 @@ /* FUCKING GET ME A TOWEL! */ #ifdef _WIN32 /* suppress gcc "no previous prototype" warning */ -int pidgin_main(HINSTANCE hint, int argc, char *argv[]); -int pidgin_main(HINSTANCE hint, int argc, char *argv[]) +int __cdecl pidgin_main(HINSTANCE hint, int argc, char *argv[]); +int __cdecl pidgin_main(HINSTANCE hint, int argc, char *argv[]) #else int main(int argc, char *argv[]) #endif @@ -806,16 +754,9 @@ return 0; } - /* TODO: Move blist loading into purple_blist_init() */ - purple_set_blist(purple_blist_new()); - purple_blist_load(); - /* load plugins we had when we quit */ purple_plugins_load_saved(PIDGIN_PREFS_ROOT "/plugins/loaded"); - /* TODO: Move pounces loading into purple_pounces_init() */ - purple_pounces_load(); - ui_main(); #ifdef USE_SM
--- a/pidgin/gtkmedia.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkmedia.c Fri Jan 25 02:22:38 2013 -0500 @@ -38,14 +38,16 @@ #ifdef USE_VV #include "media-gst.h" -#ifdef _WIN32 +#ifdef GDK_WINDOWING_WIN32 #include <gdk/gdkwin32.h> -#elif defined(GDK_WINDOWING_QUARTZ) +#endif +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#endif +#ifdef GDK_WINDOWING_QUARTZ #include <gdk/gdkquartz.h> #endif -#include <gst/interfaces/xoverlay.h> - #include "gtk3compat.h" #define PIDGIN_TYPE_MEDIA (pidgin_media_get_type()) @@ -551,15 +553,27 @@ } if (window) { - gulong window_id; -#ifdef _WIN32 - window_id = GDK_WINDOW_HWND(window); -#elif defined(HAVE_X11) - window_id = gdk_x11_window_get_xid(window); -#elif defined(GDK_WINDOWING_QUARTZ) - window_id = (gulong) gdk_quartz_window_get_nsview(window); -#else -# error "Unsupported windowing system" + gulong window_id = 0; +#ifdef GDK_WINDOWING_WIN32 + if (GDK_IS_WIN32_WINDOW(window)) + window_id = GDK_WINDOW_HWND(window); + else +#endif +#ifdef GDK_WINDOWING_X11 + if (GDK_IS_X11_WINDOW(window)) + window_id = gdk_x11_window_get_xid(window); + else +#endif +#ifdef GDK_WINDOWING_QUARTZ + if (GDK_IS_QUARTZ_WINDOW(window)) + window_id = (gulong)gdk_quartz_window_get_nsview(window); + else +#endif + g_warning("Unsupported GDK backend"); +#if !(defined(GDK_WINDOWING_WIN32) \ + || defined(GDK_WINDOWING_X11) \ + || defined(GDK_WINDOWING_QUARTZ)) +# error "Unsupported GDK windowing system" #endif purple_media_set_output_window(priv->media, data->session_id,
--- a/pidgin/gtknotify.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtknotify.c Fri Jan 25 02:22:38 2013 -0500 @@ -375,11 +375,37 @@ static void reset_mail_dialog(GtkDialog *unused) { + g_return_if_fail(mail_dialog != NULL); + if (mail_dialog->in_use) return; gtk_widget_destroy(mail_dialog->dialog); g_free(mail_dialog); mail_dialog = NULL; + purple_signal_emit(purple_notify_get_handle(), "displaying-emails-clear"); +} + +gboolean +pidgin_notify_emails_pending() +{ +#if GTK_CHECK_VERSION(2,18,0) + return mail_dialog != NULL + && !gtk_widget_get_visible(mail_dialog->dialog); +#else + return mail_dialog != NULL + && !GTK_WIDGET_VISIBLE(mail_dialog->dialog); +#endif +} + +void pidgin_notify_emails_present(void *data) +{ + if (pidgin_notify_emails_pending()) { + gtk_widget_show_all(mail_dialog->dialog); + mail_dialog->in_use = TRUE; + pidgin_blist_set_headline(NULL, NULL, NULL, NULL, NULL); + mail_dialog->in_use = FALSE; + } + purple_signal_emit(purple_notify_get_handle(), "displaying-emails-clear"); } static void @@ -796,7 +822,7 @@ remove the notifications when replacing an old notification. */ pidgin_blist_set_headline(label_text, - pixbuf, G_CALLBACK(gtk_widget_show_all), mail_dialog->dialog, + pixbuf, G_CALLBACK(pidgin_notify_emails_present), mail_dialog->dialog, (GDestroyNotify)reset_mail_dialog); mail_dialog->in_use = FALSE; g_free(label_text);
--- a/pidgin/gtknotify.h Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtknotify.h Fri Jan 25 02:22:38 2013 -0500 @@ -63,4 +63,18 @@ G_END_DECLS +/** + * Returns TRUE if there are unseen emails, FALSE otherwise. + * + * @return TRUE if there are unseen emails, FALSE otherwise. + */ +gboolean pidgin_notify_emails_pending(void); + +/** + * Presents mail dialog to the user. + * + * @return void. + */ +void pidgin_notify_emails_present(void *data); + #endif /* _PIDGINNOTIFY_H_ */
--- a/pidgin/gtkpounce.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkpounce.c Fri Jan 25 02:22:38 2013 -0500 @@ -1510,31 +1510,13 @@ if (command != NULL) { -#ifndef _WIN32 - char *localecmd = g_locale_from_utf8(command, -1, NULL, - NULL, NULL); - - if (localecmd != NULL) - { - int pid = fork(); - - if (pid == 0) { - char *args[4]; - - args[0] = "sh"; - args[1] = "-c"; - args[2] = (char *)localecmd; - args[3] = NULL; - - execvp(args[0], args); - - _exit(0); - } - g_free(localecmd); + GError *error = NULL; + if (!g_spawn_command_line_async(command, &error)) { + purple_debug_error("gtkpounce", + "pounce command could not be launched: %s\n", + error->message); + g_error_free(error); } -#else /* !_WIN32 */ - winpidgin_shell_execute(command, "open", NULL); -#endif /* !_WIN32 */ } }
--- a/pidgin/gtkprefs.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkprefs.c Fri Jan 25 02:22:38 2013 -0500 @@ -26,6 +26,7 @@ */ #include "internal.h" #include "pidgin.h" +#include "obsolete.h" #include "debug.h" #include "nat-pmp.h" @@ -58,6 +59,24 @@ #include "gtkwebview.h" #include "gtkwebviewtoolbar.h" #include "pidginstock.h" +#ifdef USE_VV +#include "media-gst.h" +#if GST_CHECK_VERSION(1,0,0) +#include <gst/video/videooverlay.h> +#else +#include <gst/interfaces/xoverlay.h> +#include <gst/interfaces/propertyprobe.h> +#endif +#ifdef GDK_WINDOWING_WIN32 +#include <gdk/gdkwin32.h> +#endif +#ifdef GDK_WINDOWING_X11 +#include <gdk/gdkx.h> +#endif +#ifdef GDK_WINDOWING_QUARTZ +#include <gdk/gdkquartz.h> +#endif +#endif #include "gtk3compat.h" @@ -105,6 +124,60 @@ static GtkListStore *prefs_status_icon_themes; static GtkListStore *prefs_smiley_themes; +#ifdef USE_VV + +static const gchar *AUDIO_SRC_PLUGINS[] = { + "alsasrc", "ALSA", + /* "esdmon", "ESD", ? */ + "osssrc", "OSS", + "pulsesrc", "PulseAudio", + "sndiosrc", "sndio", + /* "audiotestsrc wave=silence", "Silence", */ + "audiotestsrc", "Test Sound", + NULL +}; + +static const gchar *AUDIO_SINK_PLUGINS[] = { + "alsasink", "ALSA", + "artsdsink", "aRts", + "esdsink", "ESD", + "osssink", "OSS", + "pulsesink", "PulseAudio", + "sndiosink", "sndio", + NULL +}; + +static const gchar *VIDEO_SRC_PLUGINS[] = { + "videotestsrc", "Test Input", + "dshowvideosrc","DirectDraw", + "ksvideosrc", "KS Video", + "qcamsrc", "Quickcam", + "v4lsrc", "Video4Linux", + "v4l2src", "Video4Linux2", + "v4lmjpegsrc", "Video4Linux MJPEG", + NULL +}; + +static const gchar *VIDEO_SINK_PLUGINS[] = { + /* "aasink", "AALib", Didn't work for me */ + "directdrawsink","DirectDraw", + "glimagesink", "OpenGL", + "ximagesink", "X Window System", + "xvimagesink", "X Window System (Xv)", + NULL +}; + +typedef struct { + GtkWidget *level; + GtkWidget *threshold; + GtkWidget *volume; +} BusCbCtx; + +static GstElement *voice_pipeline; +static GstElement *video_pipeline; + +#endif + /* * PROTOTYPES */ @@ -661,6 +734,19 @@ prefs_smiley_themes = gtk_list_store_new(3, GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_STRING); } +/** + * Attempt to load the given directory as a theme. If we are unable to + * open the path as a theme then we recurse into path and attempt to + * load each subdirectory that we encounter. + * + * @param path A directory containing a theme. The theme could be at the + * top level of this directory or in any subdirectory thereof. + * @param type The type of theme to load. The loader for this theme type + * will be used and this loader will determine what constitutes a + * "theme." + * + * @return A new reference to a PurpleTheme. + */ static PurpleTheme * prefs_theme_find_theme(const gchar *path, const gchar *type) { @@ -804,7 +890,11 @@ "purple", info->type, NULL); /* move the entire directory to new location */ - g_rename(purple_theme_get_dir(theme), theme_dest); + if (g_rename(purple_theme_get_dir(theme), theme_dest)) { + purple_debug_error("gtkprefs", "Error renaming %s to %s: " + "%s\n", purple_theme_get_dir(theme), theme_dest, + g_strerror(errno)); + } g_free(theme_dest); g_remove(destdir); @@ -848,14 +938,21 @@ if(!g_file_test(theme_dest, G_FILE_TEST_IS_DIR)) purple_build_dir(theme_dest, S_IRUSR | S_IWUSR | S_IXUSR); - g_rename(purple_theme_get_dir(theme), theme_dest); + if (g_rename(purple_theme_get_dir(theme), theme_dest)) { + purple_debug_error("gtkprefs", "Error renaming %s to %s: " + "%s\n", purple_theme_get_dir(theme), theme_dest, + g_strerror(errno)); + } g_free(theme_dest); g_object_unref(theme); prefs_themes_refresh(); } else { - g_remove(temp_path); + if (g_remove(temp_path)) { + purple_debug_error("gtkprefs", "Error removing %s: %s\n", + temp_path, g_strerror(errno)); + } purple_notify_error(NULL, NULL, _("Theme failed to load."), NULL); } } else { @@ -1648,6 +1745,13 @@ vbox = pidgin_make_frame(ret, _("Conversations")); + pidgin_prefs_dropdown(vbox, _("Chat notification:"), + PURPLE_PREF_INT, PIDGIN_PREFS_ROOT "/conversations/notification_chat", + _("On unseen events"), PIDGIN_UNSEEN_EVENT, + _("On unseen text"), PIDGIN_UNSEEN_TEXT, + _("On unseen text and the nick was said"), PIDGIN_UNSEEN_NICK, + NULL); + pidgin_prefs_checkbox(_("Show _formatting on incoming messages"), PIDGIN_PREFS_ROOT "/conversations/show_incoming_formatting", vbox); pidgin_prefs_checkbox(_("Close IMs immediately when the tab is closed"), @@ -1664,10 +1768,8 @@ pidgin_prefs_checkbox(_("_Notify buddies that you are typing to them"), "/purple/conversations/im/send_typing", vbox); -#ifdef USE_GTKSPELL pidgin_prefs_checkbox(_("Highlight _misspelled words"), PIDGIN_PREFS_ROOT "/conversations/spellcheck", vbox); -#endif pidgin_prefs_checkbox(_("Use smooth-scrolling"), PIDGIN_PREFS_ROOT "/conversations/use_smooth_scrolling", vbox); @@ -3015,6 +3117,616 @@ return ret; } +#ifdef USE_VV +static GList * +get_vv_element_devices(const gchar *element_name) +{ + GList *ret = NULL; + GstElement *element; + GObjectClass *klass; +#if !GST_CHECK_VERSION(1,0,0) + GstPropertyProbe *probe; + const GParamSpec *pspec; +#endif + + ret = g_list_prepend(ret, (gpointer)_("Default")); + ret = g_list_prepend(ret, ""); + + if (!strcmp(element_name, "<custom>") || (*element_name == '\0')) { + return g_list_reverse(ret); + } + + element = gst_element_factory_make(element_name, "test"); + if (!element) { + purple_debug_info("vvconfig", "'%s' - unable to find element\n", + element_name); + return g_list_reverse(ret); + } + + klass = G_OBJECT_GET_CLASS (element); + if (!klass) { + purple_debug_info("vvconfig", "'%s' - unable to find GObject Class\n", + element_name); + return g_list_reverse(ret); + } + +#if GST_CHECK_VERSION(1,0,0) + purple_debug_info("vvconfig", "'%s' - no device\n", element_name); +#else + if (!g_object_class_find_property(klass, "device") || + !GST_IS_PROPERTY_PROBE(element) || + !(probe = GST_PROPERTY_PROBE(element)) || + !(pspec = gst_property_probe_get_property(probe, "device"))) { + purple_debug_info("vvconfig", "'%s' - no device\n", element_name); + } else { + gint n; + GValueArray *array; + + /* Set autoprobe[-fps] to FALSE to avoid delays when probing. */ + if (g_object_class_find_property(klass, "autoprobe")) { + g_object_set(G_OBJECT(element), "autoprobe", FALSE, NULL); + if (g_object_class_find_property(klass, "autoprobe-fps")) { + g_object_set(G_OBJECT(element), "autoprobe-fps", FALSE, NULL); + } + } + + array = gst_property_probe_probe_and_get_values(probe, pspec); + if (array == NULL) { + purple_debug_info("vvconfig", "'%s' has no devices\n", element_name); + return g_list_reverse(ret); + } + + for (n = 0; n < array->n_values; ++n) { + GValue *device; + const gchar *name; + const gchar *device_name; + + device = g_value_array_get_nth(array, n); + g_object_set_property(G_OBJECT(element), "device", device); + if (gst_element_set_state(element, GST_STATE_READY) != GST_STATE_CHANGE_SUCCESS) { + purple_debug_warning("vvconfig", "Error changing state of %s\n", + element_name); + continue; + } + + g_object_get(G_OBJECT(element), "device-name", &name, NULL); + device_name = g_value_get_string(device); + if (name == NULL) + name = _("Unknown"); + purple_debug_info("vvconfig", "Found device %s : %s for %s\n", + device_name, name, element_name); + ret = g_list_prepend(ret, (gpointer)name); + ret = g_list_prepend(ret, (gpointer)device_name); + gst_element_set_state(element, GST_STATE_NULL); + } + } +#endif + gst_object_unref(element); + + return g_list_reverse(ret); +} + +static GList * +get_vv_element_plugins(const gchar **plugins) +{ + GList *ret = NULL; + + ret = g_list_prepend(ret, (gpointer)_("Default")); + ret = g_list_prepend(ret, ""); + for (; plugins[0] && plugins[1]; plugins += 2) { +#if GST_CHECK_VERSION(1,0,0) + if (gst_registry_check_feature_version(gst_registry_get(), + plugins[0], 0, 0, 0)) { +#else + if (gst_default_registry_check_feature_version(plugins[0], 0, 0, 0)) { +#endif + ret = g_list_prepend(ret, (gpointer)plugins[1]); + ret = g_list_prepend(ret, (gpointer)plugins[0]); + } + } + + return g_list_reverse(ret); +} + +static void +vv_plugin_changed_cb(const gchar *name, PurplePrefType type, + gconstpointer value, gpointer data) +{ + GtkWidget *vbox = data; + GtkSizeGroup *sg; + GtkWidget *widget; + gchar *pref; + GList *devices; + + sg = g_object_get_data(G_OBJECT(vbox), "size-group"); + widget = g_object_get_data(G_OBJECT(vbox), "device-hbox"); + gtk_widget_destroy(widget); + + pref = g_strdup(name); + strcpy(pref + strlen(pref) - strlen("plugin"), "device"); + devices = get_vv_element_devices(value); + if (g_list_find_custom(devices, purple_prefs_get_string(pref), + (GCompareFunc)strcmp) == NULL) + purple_prefs_set_string(pref, g_list_next(devices)->data); + widget = pidgin_prefs_dropdown_from_list(vbox, _("_Device"), + PURPLE_PREF_STRING, pref, devices); + g_list_free(devices); + gtk_size_group_add_widget(sg, widget); + gtk_misc_set_alignment(GTK_MISC(widget), 0, 0.5); + + g_object_set_data(G_OBJECT(vbox), "device-hbox", + gtk_widget_get_parent(widget)); + g_signal_connect_swapped(widget, "destroy", G_CALLBACK(g_free), pref); +} + +static void +make_vv_frame(GtkWidget *parent, GtkSizeGroup *sg, + const gchar *name, const gchar **plugin_strs, + const gchar *plugin_pref, const gchar *device_pref) +{ + GtkWidget *vbox, *widget; + GList *plugins, *devices; + + vbox = pidgin_make_frame(parent, name); + + /* Setup plugin preference */ + plugins = get_vv_element_plugins(plugin_strs); + widget = pidgin_prefs_dropdown_from_list(vbox, _("_Plugin"), + PURPLE_PREF_STRING, plugin_pref, + plugins); + g_list_free(plugins); + gtk_size_group_add_widget(sg, widget); + gtk_misc_set_alignment(GTK_MISC(widget), 0, 0.5); + + /* Setup device preference */ + devices = get_vv_element_devices(purple_prefs_get_string(plugin_pref)); + if (g_list_find_custom(devices, purple_prefs_get_string(device_pref), + (GCompareFunc)strcmp) == NULL) + purple_prefs_set_string(device_pref, g_list_next(devices)->data); + widget = pidgin_prefs_dropdown_from_list(vbox, _("_Device"), + PURPLE_PREF_STRING, device_pref, + devices); + g_list_free(devices); + gtk_size_group_add_widget(sg, widget); + gtk_misc_set_alignment(GTK_MISC(widget), 0, 0.5); + + widget = gtk_widget_get_parent(widget); + g_object_set_data(G_OBJECT(vbox), "size-group", sg); + g_object_set_data(G_OBJECT(vbox), "device-hbox", widget); + purple_prefs_connect_callback(vbox, plugin_pref, vv_plugin_changed_cb, + vbox); + g_signal_connect_swapped(vbox, "destroy", + G_CALLBACK(purple_prefs_disconnect_by_handle), vbox); +} + +static GstElement * +create_test_element(const char *type, const char *dir, PurpleMediaElementInfo *info) +{ + char *tmp; + const gchar *plugin; + const gchar *device; + GstElement *ret; + + tmp = g_strdup_printf(PIDGIN_PREFS_ROOT "/vvconfig/%s/%s/plugin", type, dir); + plugin = purple_prefs_get_string(tmp); + g_free(tmp); + + tmp = g_strdup_printf(PIDGIN_PREFS_ROOT "/vvconfig/%s/%s/device", type, dir); + device = purple_prefs_get_string(tmp); + g_free(tmp); + + if (plugin[0] == '\0') + return purple_media_element_info_call_create(info, NULL, NULL, NULL); + + ret = gst_element_factory_make(plugin, NULL); + if (device[0] != '\0') + g_object_set(G_OBJECT(ret), "device", device, NULL); + if (!strcmp(plugin, "videotestsrc")) + g_object_set(G_OBJECT(ret), "is-live", 1, NULL); + + return ret; +} + +static void +vv_test_switch_page_cb(GtkNotebook *notebook, GtkWidget *page, guint num, gpointer data) +{ + GtkWidget *test = data; + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(test), FALSE); +} + +static GstElement * +create_voice_pipeline(void) +{ + PurpleMediaManager *manager; + PurpleMediaElementInfo *audio_src, *audio_sink; + GstElement *pipeline; + GstElement *src, *sink; + GstElement *volume; + GstElement *level; + GstElement *valve; + + manager = purple_media_manager_get(); + audio_src = purple_media_manager_get_active_element(manager, + PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SRC); + audio_sink = purple_media_manager_get_active_element(manager, + PURPLE_MEDIA_ELEMENT_AUDIO | PURPLE_MEDIA_ELEMENT_SINK); + + pipeline = gst_pipeline_new("voicetest"); + src = create_test_element("audio", "src", audio_src); + sink = create_test_element("audio", "sink", audio_sink); + volume = gst_element_factory_make("volume", "volume"); + level = gst_element_factory_make("level", "level"); + valve = gst_element_factory_make("valve", "valve"); + + gst_bin_add_many(GST_BIN(pipeline), src, volume, level, valve, sink, NULL); + gst_element_link_many(src, volume, level, valve, sink, NULL); + + gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING); + + return pipeline; +} + +static void +on_volume_change_cb(GtkWidget *w, GstBin *pipeline) +{ + GstElement *volume; + + g_return_if_fail(pipeline != NULL); + + volume = gst_bin_get_by_name(pipeline, "volume"); + g_object_set(volume, "volume", + gtk_scale_button_get_value(GTK_SCALE_BUTTON(w)) * 10.0, NULL); +} + +static gdouble +gst_msg_db_to_percent(GstMessage *msg, gchar *value_name) +{ + const GValue *list; + const GValue *value; + gdouble value_db; + gdouble percent; + + list = gst_structure_get_value(gst_message_get_structure(msg), value_name); +#if GST_CHECK_VERSION(1,0,0) + value = g_value_array_get_nth(g_value_get_boxed(list), 0); +#else + value = gst_value_list_get_value(list, 0); +#endif + value_db = g_value_get_double(value); + percent = pow(10, value_db / 20); + return (percent > 1.0) ? 1.0 : percent; +} + +static gboolean +gst_bus_cb(GstBus *bus, GstMessage *msg, BusCbCtx *ctx) +{ + if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ELEMENT && + gst_structure_has_name(gst_message_get_structure(msg), "level")) { + + GstElement *src = GST_ELEMENT(GST_MESSAGE_SRC(msg)); + gchar *name = gst_element_get_name(src); + + if (!strcmp(name, "level")) { + gdouble percent; + gdouble threshold; + GstElement *valve; + + percent = gst_msg_db_to_percent(msg, "rms"); + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ctx->level), percent); + + percent = gst_msg_db_to_percent(msg, "decay"); + threshold = gtk_range_get_value(GTK_RANGE(ctx->threshold)) / 100.0; + valve = gst_bin_get_by_name(GST_BIN(GST_ELEMENT_PARENT(src)), "valve"); + g_object_set(valve, "drop", (percent < threshold), NULL); + g_object_set(ctx->level, "text", + (percent < threshold) ? _("DROP") : " ", NULL); + } + + g_free(name); + } + + return TRUE; +} + +static void +voice_test_destroy_cb(GtkWidget *w, gpointer data) +{ + if (!voice_pipeline) + return; + + gst_element_set_state(voice_pipeline, GST_STATE_NULL); + gst_object_unref(voice_pipeline); + voice_pipeline = NULL; +} + +static void +toggle_voice_test_cb(GtkToggleButton *test, gpointer data) +{ + BusCbCtx *ctx = data; + GstBus *bus; + + if (gtk_toggle_button_get_active(test)) { + gtk_widget_set_sensitive(ctx->level, TRUE); + voice_pipeline = create_voice_pipeline(); + bus = gst_pipeline_get_bus(GST_PIPELINE(voice_pipeline)); + gst_bus_add_signal_watch(bus); + g_signal_connect(bus, "message", G_CALLBACK(gst_bus_cb), ctx); + gst_object_unref(bus); + + g_signal_connect(ctx->volume, "value-changed", + G_CALLBACK(on_volume_change_cb), voice_pipeline); + g_signal_connect(test, "destroy", + G_CALLBACK(voice_test_destroy_cb), NULL); + g_signal_connect(prefsnotebook, "switch-page", + G_CALLBACK(vv_test_switch_page_cb), test); + } else { + gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(ctx->level), 0.0); + gtk_widget_set_sensitive(ctx->level, FALSE); + g_object_disconnect(ctx->volume, "any-signal::value-changed", + G_CALLBACK(on_volume_change_cb), voice_pipeline, + NULL); + g_object_disconnect(test, "any-signal::destroy", + G_CALLBACK(voice_test_destroy_cb), NULL, + NULL); + g_object_disconnect(prefsnotebook, "any-signal::switch-page", + G_CALLBACK(vv_test_switch_page_cb), test, + NULL); + voice_test_destroy_cb(NULL, NULL); + } +} + +static void +volume_changed_cb(GtkScaleButton *button, gdouble value, gpointer data) +{ + purple_prefs_set_int("/purple/media/audio/volume/input", value * 100); +} + +static void +threshold_value_changed_cb(GtkScale *scale, GtkWidget *label) +{ + int value; + char *tmp; + + value = (int)gtk_range_get_value(GTK_RANGE(scale)); + tmp = g_strdup_printf(_("Silence threshold: %d%%"), value); + gtk_label_set_label(GTK_LABEL(label), tmp); + g_free(tmp); + + purple_prefs_set_int("/purple/media/audio/silence_threshold", value); +} + +static void +make_voice_test(GtkWidget *vbox) +{ + GtkWidget *test; + GtkWidget *hbox; + GtkWidget *label; + GtkWidget *level; + GtkWidget *volume; + GtkWidget *threshold; + BusCbCtx *ctx; + char *tmp; + + label = gtk_label_new(NULL); + gtk_box_pack_start(GTK_BOX(vbox), label, TRUE, TRUE, 0); + + hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + label = gtk_label_new(_("Volume:")); + gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); + volume = gtk_volume_button_new(); + gtk_box_pack_start(GTK_BOX(hbox), volume, TRUE, TRUE, 0); + gtk_scale_button_set_value(GTK_SCALE_BUTTON(volume), + purple_prefs_get_int("/purple/media/audio/volume/input") / 100.0); + g_signal_connect(volume, "value-changed", + G_CALLBACK(volume_changed_cb), NULL); + + tmp = g_strdup_printf(_("Silence threshold: %d%%"), + purple_prefs_get_int("/purple/media/audio/silence_threshold")); + label = gtk_label_new(tmp); + gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0); + gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); + g_free(tmp); + threshold = gtk_hscale_new_with_range(0, 100, 1); + gtk_box_pack_start(GTK_BOX(vbox), threshold, FALSE, FALSE, 0); + gtk_range_set_value(GTK_RANGE(threshold), + purple_prefs_get_int("/purple/media/audio/silence_threshold")); + gtk_scale_set_draw_value(GTK_SCALE(threshold), FALSE); + g_signal_connect(threshold, "value-changed", + G_CALLBACK(threshold_value_changed_cb), label); + + test = gtk_toggle_button_new_with_label(_("Test Audio")); + gtk_box_pack_start(GTK_BOX(vbox), test, FALSE, FALSE, 0); + + level = gtk_progress_bar_new(); + gtk_box_pack_start(GTK_BOX(vbox), level, FALSE, FALSE, 0); + gtk_widget_set_sensitive(level, FALSE); + + ctx = g_new(BusCbCtx, 1); + ctx->volume = volume; + ctx->level = level; + ctx->threshold = threshold; + g_signal_connect(test, "toggled", + G_CALLBACK(toggle_voice_test_cb), ctx); +} + +static GstElement * +create_video_pipeline(void) +{ + PurpleMediaManager *manager; + PurpleMediaElementInfo *video_src, *video_sink; + GstElement *pipeline; + GstElement *src, *sink; + + manager = purple_media_manager_get(); + video_src = purple_media_manager_get_active_element(manager, + PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SRC); + video_sink = purple_media_manager_get_active_element(manager, + PURPLE_MEDIA_ELEMENT_VIDEO | PURPLE_MEDIA_ELEMENT_SINK); + + pipeline = gst_pipeline_new("videotest"); + src = create_test_element("video", "src", video_src); + sink = create_test_element("video", "sink", video_sink); + + gst_bin_add_many(GST_BIN(pipeline), src, sink, NULL); + gst_element_link_many(src, sink, NULL); + + gst_element_set_state(GST_ELEMENT(pipeline), GST_STATE_PLAYING); + + return pipeline; +} + +static void +video_test_destroy_cb(GtkWidget *w, gpointer data) +{ + if (!video_pipeline) + return; + + gst_element_set_state(video_pipeline, GST_STATE_NULL); + gst_object_unref(video_pipeline); + video_pipeline = NULL; +} + +static void +window_id_cb(GstBus *bus, GstMessage *msg, gulong window_id) +{ + if (GST_MESSAGE_TYPE(msg) != GST_MESSAGE_ELEMENT +#if GST_CHECK_VERSION(1,0,0) + || !gst_is_video_overlay_prepare_window_handle_message(msg)) +#else + || !gst_structure_has_name(msg->structure, "prepare-xwindow-id")) +#endif + return; + + g_signal_handlers_disconnect_matched(bus, + G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, + 0, 0, NULL, window_id_cb, + (gpointer)window_id); + +#if GST_CHECK_VERSION(1,0,0) + gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(GST_MESSAGE_SRC(msg)), + window_id); +#elif GST_CHECK_VERSION(0,10,31) + gst_x_overlay_set_window_handle(GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), + window_id); +#else + gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(GST_MESSAGE_SRC(msg)), + window_id); +#endif +} + +static void +toggle_video_test_cb(GtkToggleButton *test, gpointer data) +{ + GtkWidget *video = data; + GstBus *bus; + + if (gtk_toggle_button_get_active(test)) { + GdkWindow *window = gtk_widget_get_window(video); + gulong window_id = 0; +#ifdef GDK_WINDOWING_WIN32 + if (GDK_IS_WIN32_WINDOW(window)) + window_id = GDK_WINDOW_HWND(window); + else +#endif +#ifdef GDK_WINDOWING_X11 + if (GDK_IS_X11_WINDOW(window)) + window_id = gdk_x11_window_get_xid(window); + else +#endif +#ifdef GDK_WINDOWING_QUARTZ + if (GDK_IS_QUARTZ_WINDOW(window)) + window_id = (gulong)gdk_quartz_window_get_nsview(window); + else +#endif + g_warning("Unsupported GDK backend"); +#if !(defined(GDK_WINDOWING_WIN32) \ + || defined(GDK_WINDOWING_X11) \ + || defined(GDK_WINDOWING_QUARTZ)) +# error "Unsupported GDK windowing system" +#endif + + video_pipeline = create_video_pipeline(); + bus = gst_pipeline_get_bus(GST_PIPELINE(video_pipeline)); +#if GST_CHECK_VERSION(1,0,0) + gst_bus_set_sync_handler(bus, gst_bus_sync_signal_handler, NULL, NULL); +#else + gst_bus_set_sync_handler(bus, gst_bus_sync_signal_handler, NULL); +#endif + g_signal_connect(bus, "sync-message::element", + G_CALLBACK(window_id_cb), (gpointer)window_id); + gst_object_unref(bus); + + g_signal_connect(test, "destroy", + G_CALLBACK(video_test_destroy_cb), NULL); + g_signal_connect(prefsnotebook, "switch-page", + G_CALLBACK(vv_test_switch_page_cb), test); + } else { + g_object_disconnect(test, "any-signal::destroy", + G_CALLBACK(video_test_destroy_cb), NULL, + NULL); + g_object_disconnect(prefsnotebook, "any-signal::switch-page", + G_CALLBACK(vv_test_switch_page_cb), test, + NULL); + video_test_destroy_cb(NULL, NULL); + } +} + +static void +make_video_test(GtkWidget *vbox) +{ + GtkWidget *test; + GtkWidget *video; + GdkColor color = {0, 0, 0, 0}; + + video = gtk_drawing_area_new(); + gtk_box_pack_start(GTK_BOX(vbox), video, TRUE, TRUE, 0); + gtk_widget_modify_bg(video, GTK_STATE_NORMAL, &color); + gtk_widget_set_size_request(GTK_WIDGET(video), 240, 180); + + test = gtk_toggle_button_new_with_label(_("Test Video")); + gtk_box_pack_start(GTK_BOX(vbox), test, FALSE, FALSE, 0); + + g_signal_connect(test, "toggled", + G_CALLBACK(toggle_video_test_cb), video); +} + +static GtkWidget * +vv_page(void) +{ + GtkWidget *ret; + GtkWidget *vbox; + GtkSizeGroup *sg; + + ret = gtk_hbox_new(FALSE, PIDGIN_HIG_CAT_SPACE); + gtk_container_set_border_width(GTK_CONTAINER(ret), PIDGIN_HIG_BORDER); + + sg = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); + + vbox = pidgin_make_frame(ret, _("Audio")); + make_vv_frame(vbox, sg, _("Input"), AUDIO_SRC_PLUGINS, + PIDGIN_PREFS_ROOT "/vvconfig/audio/src/plugin", + PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device"); + make_vv_frame(vbox, sg, _("Output"), AUDIO_SINK_PLUGINS, + PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/plugin", + PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device"); + make_voice_test(vbox); + + vbox = pidgin_make_frame(ret, _("Video")); + make_vv_frame(vbox, sg, _("Input"), VIDEO_SRC_PLUGINS, + PIDGIN_PREFS_ROOT "/vvconfig/video/src/plugin", + PIDGIN_PREFS_ROOT "/vvconfig/video/src/device"); + make_vv_frame(vbox, sg, _("Output"), VIDEO_SINK_PLUGINS, + PIDGIN_PREFS_ROOT "/vvconfig/video/sink/plugin", + PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device"); + make_video_test(vbox); + + gtk_widget_show_all(ret); + + return ret; +} +#endif + static int prefs_notebook_add_page(const char *text, GtkWidget *page, int ind) { @@ -3042,6 +3754,9 @@ prefs_notebook_add_page(_("Sounds"), sound_page(), notebook_page++); prefs_notebook_add_page(_("Status / Idle"), away_page(), notebook_page++); prefs_notebook_add_page(_("Themes"), theme_page(), notebook_page++); +#ifdef USE_VV + prefs_notebook_add_page(_("Voice/Video"), vv_page(), notebook_page++); +#endif } void @@ -3173,6 +3888,26 @@ purple_prefs_connect_callback(&prefs, PIDGIN_PREFS_ROOT "/smileys/theme", smiley_theme_pref_cb, NULL); +#ifdef USE_VV + /* Voice/Video */ + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig"); + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/audio"); + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/audio/src"); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/plugin", ""); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device", ""); + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink"); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/plugin", ""); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device", ""); + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video"); + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video/src"); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/video/src/plugin", ""); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/video/src/device", ""); + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video"); + purple_prefs_add_none(PIDGIN_PREFS_ROOT "/vvconfig/video/sink"); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/plugin", ""); + purple_prefs_add_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device", ""); +#endif + pidgin_prefs_update_old(); } @@ -3261,4 +3996,42 @@ PIDGIN_PREFS_ROOT "/conversations/im/x"); purple_prefs_rename(PIDGIN_PREFS_ROOT "/conversations/y", PIDGIN_PREFS_ROOT "/conversations/im/y"); + + /* Fixup vvconfig plugin prefs */ + if (purple_prefs_exists("/plugins/core/vvconfig/audio/src/plugin")) { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/plugin", + purple_prefs_get_string("/plugins/core/vvconfig/audio/src/plugin")); + } + if (purple_prefs_exists("/plugins/core/vvconfig/audio/src/device")) { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/src/device", + purple_prefs_get_string("/plugins/core/vvconfig/audio/src/device")); + } + if (purple_prefs_exists("/plugins/core/vvconfig/audio/sink/plugin")) { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/plugin", + purple_prefs_get_string("/plugins/core/vvconfig/audio/sink/plugin")); + } + if (purple_prefs_exists("/plugins/core/vvconfig/audio/sink/device")) { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/audio/sink/device", + purple_prefs_get_string("/plugins/core/vvconfig/audio/sink/device")); + } + if (purple_prefs_exists("/plugins/core/vvconfig/video/src/plugin")) { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/src/plugin", + purple_prefs_get_string("/plugins/core/vvconfig/video/src/plugin")); + } + if (purple_prefs_exists("/plugins/core/vvconfig/video/src/device")) { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/src/device", + purple_prefs_get_string("/plugins/core/vvconfig/video/src/device")); + } + if (purple_prefs_exists("/plugins/gtk/vvconfig/video/sink/plugin")) { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/plugin", + purple_prefs_get_string("/plugins/gtk/vvconfig/video/sink/plugin")); + } + if (purple_prefs_exists("/plugins/gtk/vvconfig/video/sink/device")) { + purple_prefs_set_string(PIDGIN_PREFS_ROOT "/vvconfig/video/sink/device", + purple_prefs_get_string("/plugins/gtk/vvconfig/video/sink/device")); + } + + purple_prefs_remove("/plugins/core/vvconfig"); + purple_prefs_remove("/plugins/gtk/vvconfig"); } +
--- a/pidgin/gtksmiley.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtksmiley.c Fri Jan 25 02:22:38 2013 -0500 @@ -27,6 +27,7 @@ #include "internal.h" #include "pidgin.h" +#include "obsolete.h" #include "debug.h" #include "notify.h" @@ -417,21 +418,40 @@ g_signal_connect(window, "response", G_CALLBACK(do_add_select_cb), s); /* The vbox */ +#if GTK_CHECK_VERSION(3,0,0) + vbox = gtk_grid_new(); + gtk_grid_set_row_spacing(GTK_GRID(vbox), PIDGIN_HIG_BORDER); +#else vbox = gtk_vbox_new(FALSE, PIDGIN_HIG_BORDER); +#endif gtk_container_add(GTK_CONTAINER(gtk_dialog_get_content_area(GTK_DIALOG(window))), vbox); gtk_widget_show(vbox); /* The hbox */ +#if GTK_CHECK_VERSION(3,0,0) + hbox = gtk_grid_new(); + gtk_grid_set_column_spacing(GTK_GRID(hbox), PIDGIN_HIG_BORDER); + gtk_grid_attach(GTK_GRID(vbox), hbox, 0, 0, 1, 1); +#else hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER); gtk_container_add(GTK_CONTAINER(GTK_VBOX(vbox)), hbox); +#endif label = gtk_label_new_with_mnemonic(_("_Image:")); +#if GTK_CHECK_VERSION(3,0,0) + gtk_grid_attach(GTK_GRID(hbox), label, 0, 0, 1, 1); +#else gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); +#endif gtk_widget_show(label); filech = gtk_button_new(); +#if GTK_CHECK_VERSION(3,0,0) + gtk_grid_attach_next_to(GTK_GRID(hbox), filech, NULL, GTK_POS_RIGHT, 1, 1); +#else gtk_box_pack_end(GTK_BOX(hbox), filech, FALSE, FALSE, 0); +#endif pidgin_set_accessible_label(filech, label); s->smiley_image = gtk_image_new(); @@ -453,12 +473,23 @@ gtk_widget_show_all(hbox); /* info */ +#if GTK_CHECK_VERSION(3,0,0) + hbox = gtk_grid_new(); + gtk_grid_set_column_spacing(GTK_GRID(hbox), PIDGIN_HIG_BORDER); + + gtk_grid_attach_next_to(GTK_GRID(vbox), hbox, NULL, GTK_POS_BOTTOM, 1, 1); +#else hbox = gtk_hbox_new(FALSE, PIDGIN_HIG_BORDER); gtk_container_add(GTK_CONTAINER(GTK_VBOX(vbox)),hbox); +#endif /* Shortcut text */ label = gtk_label_new_with_mnemonic(_("S_hortcut text:")); +#if GTK_CHECK_VERSION(3,0,0) + gtk_grid_attach(GTK_GRID(hbox), label, 0, 0, 1, 1); +#else gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); +#endif gtk_widget_show(label); s->smile = gtk_entry_new(); @@ -476,7 +507,11 @@ g_signal_connect(G_OBJECT(s->smile), "insert-text", G_CALLBACK(smiley_name_insert_cb), s); g_signal_connect(G_OBJECT(s->smile), "delete-text", G_CALLBACK(smiley_name_delete_cb), s); +#if GTK_CHECK_VERSION(3,0,0) + gtk_grid_attach_next_to(GTK_GRID(hbox), s->smile, NULL, GTK_POS_RIGHT, 1, 1); +#else gtk_box_pack_end(GTK_BOX(hbox), s->smile, FALSE, FALSE, 0); +#endif gtk_widget_show(s->smile); gtk_widget_show(hbox);
--- a/pidgin/gtksound.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtksound.c Fri Jan 25 02:22:38 2013 -0500 @@ -319,9 +319,6 @@ #ifdef USE_GSTREAMER purple_debug_info("sound", "Initializing sound output drivers.\n"); -#ifdef GST_CAN_DISABLE_FORKING - gst_registry_fork_set_enabled (FALSE); -#endif if ((gst_init_failed = !gst_init_check(NULL, NULL, &error))) { purple_notify_error(NULL, _("GStreamer Failure"), _("GStreamer failed to initialize."), @@ -530,7 +527,11 @@ return; } +#if GST_CHECK_VERSION(1,0,0) play = gst_element_factory_make("playbin", "play"); +#else + play = gst_element_factory_make("playbin2", "play"); +#endif if (play == NULL) { return;
--- a/pidgin/gtkstatusbox.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkstatusbox.c Fri Jan 25 02:22:38 2013 -0500 @@ -43,6 +43,7 @@ #include <gdk/gdkkeysyms.h> #include "internal.h" +#include "obsolete.h" #include "account.h" #include "buddyicon.h" @@ -60,13 +61,6 @@ #include "gtkstatusbox.h" #include "gtkutils.h" -#ifdef USE_GTKSPELL -# include <gtkspell/gtkspell.h> -# ifdef _WIN32 -# include "wspell.h" -# endif -#endif - #include "gtk3compat.h" /* Timeout for typing notifications in seconds */
--- a/pidgin/gtkutils.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkutils.c Fri Jan 25 02:22:38 2013 -0500 @@ -28,9 +28,7 @@ #include "internal.h" #include "pidgin.h" -#ifndef _WIN32 -# include <X11/Xlib.h> -#else +#ifdef _WIN32 # ifdef small # undef small # endif @@ -120,6 +118,7 @@ NULL); webkit_web_view_set_settings(WEBKIT_WEB_VIEW(webview), settings); + g_object_unref(settings); } #endif } @@ -1411,7 +1410,7 @@ } shortname = strrchr(data->filename, G_DIR_SEPARATOR); shortname = shortname ? shortname + 1 : data->filename; - id = purple_imgstore_add_with_id(filedata, size, shortname); + id = purple_imgstore_new_with_id(filedata, size, shortname); gtk_webview_insert_image(GTK_WEBVIEW(gtkconv->entry), id); purple_imgstore_unref_by_id(id); @@ -2162,7 +2161,7 @@ static void icon_preview_change_cb(GtkFileChooser *widget, struct _icon_chooser *dialog) { - GdkPixbuf *pixbuf, *scale; + GdkPixbuf *pixbuf; int height, width; char *basename, *markup, *size; struct stat st; @@ -2171,7 +2170,7 @@ filename = gtk_file_chooser_get_preview_filename( GTK_FILE_CHOOSER(dialog->icon_filesel)); - if (!filename || g_stat(filename, &st) || !(pixbuf = pidgin_pixbuf_new_from_file(filename))) + if (!filename || g_stat(filename, &st) || !(pixbuf = pidgin_pixbuf_new_from_file_at_size(filename, 128, 128))) { gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), NULL); gtk_label_set_markup(GTK_LABEL(dialog->icon_text), ""); @@ -2179,8 +2178,7 @@ return; } - width = gdk_pixbuf_get_width(pixbuf); - height = gdk_pixbuf_get_height(pixbuf); + gdk_pixbuf_get_file_info(filename, &width, &height); basename = g_path_get_basename(filename); size = purple_str_size_to_units(st.st_size); markup = g_strdup_printf(_("<b>File:</b> %s\n" @@ -2188,13 +2186,10 @@ "<b>Image size:</b> %dx%d"), basename, size, width, height); - scale = gdk_pixbuf_scale_simple(pixbuf, width * 50 / height, - 50, GDK_INTERP_BILINEAR); - gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), scale); + gtk_image_set_from_pixbuf(GTK_IMAGE(dialog->icon_preview), pixbuf); gtk_label_set_markup(GTK_LABEL(dialog->icon_text), markup); g_object_unref(G_OBJECT(pixbuf)); - g_object_unref(G_OBJECT(scale)); g_free(filename); g_free(basename); g_free(size);
--- a/pidgin/gtkwebview.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkwebview.c Fri Jan 25 02:22:38 2013 -0500 @@ -38,6 +38,7 @@ #define MAX_FONT_SIZE 7 #define MAX_SCROLL_TIME 0.4 /* seconds */ #define SCROLL_DELAY 33 /* milliseconds */ +#define GTK_WEBVIEW_MAX_PROCESS_TIME 100000 /* microseconds */ #define GTK_WEBVIEW_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE((obj), GTK_TYPE_WEBVIEW, GtkWebViewPriv)) @@ -705,8 +706,8 @@ } } -static gboolean -process_load_queue(GtkWebView *webview) +static void +process_load_queue_element(GtkWebView *webview) { GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); int type; @@ -717,15 +718,6 @@ WebKitDOMRange *range; gboolean require_scroll = FALSE; - if (priv->is_loading) { - priv->loader = 0; - return FALSE; - } - if (!priv->load_queue || g_queue_is_empty(priv->load_queue)) { - priv->loader = 0; - return FALSE; - } - type = GPOINTER_TO_INT(g_queue_pop_head(priv->load_queue)); str = g_queue_pop_head(priv->load_queue); @@ -783,7 +775,35 @@ } g_free(str); +} +static gboolean +process_load_queue(GtkWebView *webview) +{ + GtkWebViewPriv *priv = GTK_WEBVIEW_GET_PRIVATE(webview); + gint64 start_time; + + if (priv->is_loading) { + priv->loader = 0; + return FALSE; + } + if (!priv->load_queue || g_queue_is_empty(priv->load_queue)) { + priv->loader = 0; + return FALSE; + } + + start_time = g_get_monotonic_time(); + while (!g_queue_is_empty(priv->load_queue)) { + process_load_queue_element(webview); + if (g_get_monotonic_time() - start_time > + GTK_WEBVIEW_MAX_PROCESS_TIME) + break; + } + + if (g_queue_is_empty(priv->load_queue)) { + priv->loader = 0; + return FALSE; + } return TRUE; } @@ -1143,6 +1163,7 @@ node, uri); g_free(uri); + g_object_unref(hit); return TRUE; }
--- a/pidgin/gtkwebviewtoolbar.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/gtkwebviewtoolbar.c Fri Jan 25 02:22:38 2013 -0500 @@ -579,7 +579,7 @@ name = strrchr(filename, G_DIR_SEPARATOR) + 1; - id = purple_imgstore_add_with_id(filedata, size, name); + id = purple_imgstore_new_with_id(filedata, size, name); if (id == 0) { buf = g_strdup_printf(_("Failed to store image: %s\n"), filename); @@ -594,7 +594,9 @@ g_free(filename); gtk_webview_insert_image(GTK_WEBVIEW(toolbar->webview), id); - purple_imgstore_unref_by_id(id); + /* TODO: do it after passing an image to prpl, not before + * purple_imgstore_unref_by_id(id); + */ } static void
--- a/pidgin/minidialog.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/minidialog.c Fri Jan 25 02:22:38 2013 -0500 @@ -455,6 +455,7 @@ g_object_class_install_property (object_class, PROP_ENABLE_DESCRIPTION_MARKUP, param_spec); } +#if !GTK_CHECK_VERSION(3,0,0) /* 16 is the width of the icon, due to PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL */ #define BLIST_WIDTH_OTHER_THAN_LABEL \ ((PIDGIN_HIG_BOX_SPACE * 3) + 16) @@ -476,13 +477,16 @@ gtk_widget_set_size_request(GTK_WIDGET(priv->title), label_width, -1); gtk_widget_set_size_request(GTK_WIDGET(priv->desc), label_width, -1); } +#endif static void pidgin_mini_dialog_init(PidginMiniDialog *self) { GtkBox *self_box = GTK_BOX(self); +#if !GTK_CHECK_VERSION(3,0,0) guint blist_width = purple_prefs_get_int(BLIST_WIDTH_PREF); guint label_width = blist_width - BLIST_WIDTH_OTHER_THAN_LABEL; +#endif PidginMiniDialogPrivate *priv = g_new0(PidginMiniDialogPrivate, 1); self->priv = priv; @@ -495,7 +499,9 @@ gtk_misc_set_alignment(GTK_MISC(priv->icon), 0, 0); priv->title = GTK_LABEL(gtk_label_new(NULL)); +#if !GTK_CHECK_VERSION(3,0,0) gtk_widget_set_size_request(GTK_WIDGET(priv->title), label_width, -1); +#endif gtk_label_set_line_wrap(priv->title, TRUE); gtk_label_set_selectable(priv->title, TRUE); gtk_misc_set_alignment(GTK_MISC(priv->title), 0, 0); @@ -504,7 +510,9 @@ gtk_box_pack_start(priv->title_box, GTK_WIDGET(priv->title), TRUE, TRUE, 0); priv->desc = GTK_LABEL(gtk_label_new(NULL)); +#if !GTK_CHECK_VERSION(3,0,0) gtk_widget_set_size_request(GTK_WIDGET(priv->desc), label_width, -1); +#endif gtk_label_set_line_wrap(priv->desc, TRUE); gtk_misc_set_alignment(GTK_MISC(priv->desc), 0, 0); gtk_label_set_selectable(priv->desc, TRUE); @@ -513,8 +521,10 @@ */ g_object_set(G_OBJECT(priv->desc), "no-show-all", TRUE, NULL); +#if !GTK_CHECK_VERSION(3,0,0) purple_prefs_connect_callback(self, BLIST_WIDTH_PREF, blist_width_changed_cb, self); +#endif self->contents = GTK_BOX(gtk_vbox_new(FALSE, 0));
--- a/pidgin/pidgin-3.pc.in Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/pidgin-3.pc.in Fri Jan 25 02:22:38 2013 -0500 @@ -11,5 +11,5 @@ Name: Pidgin Description: Pidgin is a GTK2-based instant messenger application. Version: @VERSION@ -Requires: gtk+-2.0 purple-3 +Requires: @GTK_PC_MODULE@ purple-3 Cflags: -I${includedir}
--- a/pidgin/pidgin.h Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/pidgin.h Fri Jan 25 02:22:38 2013 -0500 @@ -29,10 +29,6 @@ #include <gtk/gtk.h> -#ifdef GDK_WINDOWING_X11 -# include <gdk/gdkx.h> -#endif - #ifdef _WIN32 # include "gtkwin32dep.h" #endif
--- a/pidgin/pidginstock.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/pidginstock.c Fri Jan 25 02:22:38 2013 -0500 @@ -67,7 +67,7 @@ { PIDGIN_STOCK_IGNORE, NULL, GTK_STOCK_DIALOG_ERROR }, { PIDGIN_STOCK_INVITE, NULL, GTK_STOCK_JUMP_TO }, { PIDGIN_STOCK_MODIFY, NULL, GTK_STOCK_PREFERENCES }, - { PIDGIN_STOCK_ADD, NULL, GTK_STOCK_ADD }, + { PIDGIN_STOCK_ADD, NULL, GTK_STOCK_ADD }, { PIDGIN_STOCK_PAUSE, NULL, GTK_STOCK_MEDIA_PAUSE }, { PIDGIN_STOCK_POUNCE, NULL, GTK_STOCK_REDO }, { PIDGIN_STOCK_OPEN_MAIL, NULL, GTK_STOCK_JUMP_TO }, @@ -80,17 +80,17 @@ static const GtkStockItem stock_items[] = { - { PIDGIN_STOCK_ALIAS, N_("_Alias"), 0, 0, NULL }, - { PIDGIN_STOCK_CHAT, N_("_Join"), 0, 0, NULL }, - { PIDGIN_STOCK_CLOSE_TABS, N_("Close _tabs"), 0, 0, NULL }, - { PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, N_("I_M"), 0, 0, NULL }, - { PIDGIN_STOCK_TOOLBAR_USER_INFO, N_("_Get Info"), 0, 0, NULL }, - { PIDGIN_STOCK_INVITE, N_("_Invite"), 0, 0, NULL }, - { PIDGIN_STOCK_MODIFY, N_("_Modify..."), 0, 0, NULL }, - { PIDGIN_STOCK_ADD, N_("_Add..."), 0, 0, NULL }, - { PIDGIN_STOCK_OPEN_MAIL, N_("_Open Mail"), 0, 0, NULL }, - { PIDGIN_STOCK_PAUSE, N_("_Pause"), 0, 0, NULL }, - { PIDGIN_STOCK_EDIT, N_("_Edit"), 0, 0, NULL } + { PIDGIN_STOCK_ALIAS, N_("_Alias"), 0, 0, PACKAGE }, + { PIDGIN_STOCK_CHAT, N_("_Join"), 0, 0, PACKAGE }, + { PIDGIN_STOCK_CLOSE_TABS, N_("Close _tabs"), 0, 0, PACKAGE }, + { PIDGIN_STOCK_TOOLBAR_MESSAGE_NEW, N_("I_M"), 0, 0, PACKAGE }, + { PIDGIN_STOCK_TOOLBAR_USER_INFO, N_("_Get Info"), 0, 0, PACKAGE }, + { PIDGIN_STOCK_INVITE, N_("_Invite"), 0, 0, PACKAGE }, + { PIDGIN_STOCK_MODIFY, N_("_Modify..."), 0, 0, PACKAGE }, + { PIDGIN_STOCK_ADD, N_("_Add..."), 0, 0, PACKAGE }, + { PIDGIN_STOCK_OPEN_MAIL, N_("_Open Mail"), 0, 0, PACKAGE }, + { PIDGIN_STOCK_PAUSE, N_("_Pause"), 0, 0, PACKAGE }, + { PIDGIN_STOCK_EDIT, N_("_Edit"), 0, 0, PACKAGE } }; typedef struct {
--- a/pidgin/pixmaps/Makefile.am Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/pixmaps/Makefile.am Fri Jan 25 02:22:38 2013 -0500 @@ -297,6 +297,7 @@ protocols/48/bonjour.png \ protocols/48/facebook.png \ protocols/48/gadu-gadu.png \ + protocols/48/google-talk.png \ protocols/48/novell.png \ protocols/48/icq.png \ protocols/48/irc.png \
--- a/pidgin/pixmaps/emotes/default/24/default.theme.in Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/pixmaps/emotes/default/24/default.theme.in Fri Jan 25 02:22:38 2013 -0500 @@ -189,106 +189,6 @@ fingers-crossed.png (yn) (YN) -# Following QQ 2006 -[QQ] -shocked.png /:O /jy /surprised -curl-lip.png /:~ /pz /curl_lip -desire.png /:* /se /desire -dazed.png /:| /dazed -party.png /8-) /dy /revel -crying.png /:< /ll /cry -bashful.png /:$ /hx /bashful -shut-mouth.png /:X /bz /shut_mouth -sleeping.png /:Z /shui /sleep -weep.png /:'( /dk /weep -embarrassed.png /:-| /gg /embarassed -pissed-off.png /:@ /fn /pissed_off -act-up.png /:P /tp /act_up -excited.png /:D /cy /toothy_smile -happy.png /:) /wx /small_smile -sad.png /:( /ng /sad -glasses-cool.png /:+ /kuk /cool -doctor.png /:# /feid /SARS -silly.png /:Q /zk /crazy -sick.png /:T /tu /vomit -snicker.png /;p /tx /titter -cute.png /;-D /ka /cute -disdain.png /;d /by /disdain -arrogant.png /;o /am /arrogant -starving.png /:g /jie /starving -sleepy.png /|-) /kun /sleepy -terror.png /:! /jk /terror -hot.png /:L /sweat -smirk.png /:> /hanx /smirk -soldier.png /:; /db /soldier -struggle.png /;f /fendou /struggle -curse.png /:-S /zhm /curse -question.png /? /yiw /question -quiet.png /;x /xu /shh -hypnotized.png /;@ /yun /dizzy -excruciating.png /:8 /zhem /excrutiating -freaked-out.png /;! /shuai /freaked_out -skeleton.png /!!! /kl /skeleton -hammer.png /xx /qiao /hammer -bye.png /bye /zj /bye -go-away.png /go /shan /go -afraid.png /shake /fad /shake -amorous.png /love /aiq /love -jump.png /jump /tiao /jump -search.png /find /zhao /search -lashes.png /& /mm /beautiful_eyebrows -pig.png /pig /zt /pig -cat.png /cat /mm /cat -dog.png /dog /xg /dog -hug-left.png /hug /yb /hug -coins.png /$ /qianc /money -lamp.png /! /dp /lightbulb -bowl.png /cup /bei /cup -cake.png /cake /dg /cake -thunder.png /li /shd /lightning -bomb.png /bome /zhd /bomb -knife.png /kn /dao /knife -soccerball.png /footb /zq /soccer -musical-note.png /music /yy /music -poop.png /shit /bb /shit -coffee.png /coffee /kf /coffee -hungry.png /eat /fan /eat -pill.png /pill /yw /pill -rose.png /rose /mg /rose -wilt.png /fade /dx /wilt -kiss.png /kiss /wen /kiss -in_love.png /heart /xin /heart -love-over.png /break /xs /broken_heart -meeting.png /meeting /hy /meeting -present.png /gift /lw /gift -phone.png /phone /dh /phone -clock.png /time /sj /time -mail.png /email /yj /email -tv.png /TV /ds /TV -sun.png /sun /ty /sun -moon.png /moon /yl /moon -good.png /strong /qiang /thumbs_up -bad.png /weak /ruo /thumbs_down -handshake.png /share /ws /handshake -victory.png /v /shl /victory -beauty.png /<J> /mn /beauty -qq.png /<QQ> /qz /qq -blowkiss.png /<L> /fw /blow_kiss -angry.png /<O> /oh /angry -liquor.png /<B> /bj /baijiu -can.png /<U> /qsh /soda -watermelon.png /<W> /xigua /watermelon -rain.png /<!!> /xy /rain -cloudy.png /<~> /duoy /cloudy -snowman.png /<Z> /xr /snowman -star.png /<*> /xixing /star -girl.png /<00> /nv /woman -boy.png /<11> /nan /man -! skywalker.png C:-) c:-) C:) c:) -! monkey.png :-(|) :(|) 8-|) -! cyclops.png O-) o-) - - # Following ICQ 6.0 [ICQ] happy.png :-) :)
--- a/pidgin/pixmaps/emotes/small/16/Makefile.am Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/pixmaps/emotes/small/16/Makefile.am Fri Jan 25 02:22:38 2013 -0500 @@ -43,6 +43,7 @@ excruciating.png \ eyeroll.png \ girl.png \ + glasses-cool.png \ happy.png \ hug-left.png \ hug-right.png \ @@ -65,6 +66,7 @@ question.png \ rose.png \ sad.png \ + sarcastic.png \ shame.png \ shocked.png \ shut-mouth.png \
--- a/pidgin/pixmaps/emotes/small/16/small.theme.in Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/pixmaps/emotes/small/16/small.theme.in Fri Jan 25 02:22:38 2013 -0500 @@ -118,44 +118,6 @@ console.png (xx) (XX) -# Following QQ 2006 -[QQ] -shocked.png /:O /jy /surprised -party.png /8-) /dy /revel -crying.png /:< /ll /cry -shut-mouth.png /:X /bz /shut_mouth -sleeping.png /:Z /shui /sleep -embarrassed.png /:-| /gg /embarassed -pissed-off.png /:@ /fn /pissed_off -excited.png /:D /cy /toothy_smile -happy.png /:) /wx /small_smile -sad.png /:( /ng /sad -glasses-cool.png /:+ /kuk /cool -sick.png /:T /tu /vomit -sleepy.png /|-) /kun /sleepy -hot.png /:L /sweat -question.png /? /yiw /question -excruciating.png /:8 /zhem /excrutiating -afraid.png /shake /fad /shake -amorous.png /love /aiq /love -search.png /find /zhao /search -hug-left.png /hug /yb /hug -lamp.png /! /dp /lightbulb -thunder.png /li /shd /lightning -musical-note.png /music /yy /music -coffee.png /coffee /kf /coffee -hungry.png /eat /fan /eat -rose.png /rose /mg /rose -kiss.png /kiss /wen /kiss -in_love.png /heart /xin /heart -meeting.png /meeting /hy /meeting -phone.png /phone /dh /phone -tv.png /TV /ds /TV -angry.png /<O> /oh /angry -girl.png /<00> /nv /woman -boy.png /<11> /nan /man - - # Following ICQ 6.0 [ICQ] happy.png :-) :)
--- a/pidgin/plugins/Makefile.am Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/plugins/Makefile.am Fri Jan 25 02:22:38 2013 -0500 @@ -45,7 +45,6 @@ sendbutton_la_LDFLAGS = -module -avoid-version spellchk_la_LDFLAGS = -module -avoid-version themeedit_la_LDFLAGS = -module -avoid-version -vvconfig_la_LDFLAGS = -module -avoid-version webkit_la_LDFLAGS = -module -avoid-version xmppconsole_la_LDFLAGS = -module -avoid-version @@ -67,10 +66,6 @@ webkit.la \ xmppconsole.la -if USE_VV -plugin_LTLIBRARIES += vvconfig.la -endif - noinst_LTLIBRARIES = \ contact_priority.la \ gtk_signals_test.la
--- a/pidgin/plugins/cap/cap.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/plugins/cap/cap.c Fri Jan 25 02:22:38 2013 -0500 @@ -43,7 +43,7 @@ int threshold = purple_prefs_get_int("/plugins/gtk/cap/threshold"); int min_minute = (current_minute - threshold) % 1440; int max_minute = (current_minute + threshold) % 1440; - char *sql; + char *sql, sta_id = NULL; sqlite3_stmt *stmt = NULL; const char *tail = NULL; int rc; @@ -94,7 +94,9 @@ sqlite3_free(sql); - if(strcmp(purple_status_get_id(get_status_for(buddy)), "offline") == 0) { + sta_id = purple_status_get_id(get_status_for(buddy)); + + if(sta_id && !strcmp(sta_id, "offline")) { /* This is kind of stupid, change it. */ if(prediction == 1.0f) prediction = 0.0f;
--- a/pidgin/plugins/disco/xmppdisco.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/plugins/disco/xmppdisco.c Fri Jan 25 02:22:38 2013 -0500 @@ -265,7 +265,7 @@ g_return_val_if_fail(str != NULL, ""); for ( ; disco_type_mappings[i].from; ++i) { - if (!strcasecmp(str, disco_type_mappings[i].from)) + if (!g_ascii_strcasecmp(str, disco_type_mappings[i].from)) return disco_type_mappings[i].to; }
--- a/pidgin/plugins/notify.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/plugins/notify.c Fri Jan 25 02:22:38 2013 -0500 @@ -96,16 +96,10 @@ #include "gtkplugin.h" #include "gtkutils.h" -#ifndef _WIN32 -#include <X11/Xatom.h> -#include <X11/Xlib.h> -#include <X11/Xutil.h> -#endif - #define NOTIFY_PLUGIN_ID "gtk-x11-notify" static PurplePlugin *my_plugin = NULL; -#ifndef _WIN32 +#ifdef HAVE_X11 static GdkAtom _Cardinal = GDK_NONE; static GdkAtom _PurpleUnseenCount = GDK_NONE; #endif @@ -525,7 +519,7 @@ static void handle_count_xprop(PidginWindow *purplewin) { -#ifndef _WIN32 +#ifdef HAVE_X11 guint count; GtkWidget *window; GdkWindow *gdkwin; @@ -757,7 +751,7 @@ g_signal_connect(G_OBJECT(toggle), "toggled", G_CALLBACK(method_toggle_cb), "method_count"); -#ifndef _WIN32 +#ifdef HAVE_X11 /* Count xprop method button */ toggle = gtk_check_button_new_with_mnemonic(_("Insert count of new message into _X property")); gtk_box_pack_start(GTK_BOX(vbox), toggle, FALSE, FALSE, 0);
--- a/pidgin/plugins/perl/common/Makefile.mingw Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/plugins/perl/common/Makefile.mingw Fri Jan 25 02:22:38 2013 -0500 @@ -102,7 +102,7 @@ $(C_FILES): $(PIDGIN_CONFIG_H) $(TARGET).dll: $(PIDGIN_DLL).a $(PURPLE_PERL_DLL).a $(OBJECTS) - $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(LIBS) -o $(TARGET).dll + $(CC) -shared $(OBJECTS) $(LIB_PATHS) $(DLL_LD_FLAGS) $(LIBS) -o $(TARGET).dll ## ## CLEAN
--- a/pidgin/plugins/relnot.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/plugins/relnot.c Fri Jan 25 02:22:38 2013 -0500 @@ -36,6 +36,7 @@ #include "debug.h" #include "gtkblist.h" #include "gtkutils.h" +#include "http.h" #include "notify.h" #include "pidginstock.h" #include "prefs.h" @@ -60,42 +61,20 @@ purple_notify_uri(NULL, PURPLE_WEBSITE); } -static void -version_fetch_cb(PurpleUtilFetchUrlData *url_data, gpointer user_data, - const gchar *response, size_t len, const gchar *error_message) +static void version_fetch_cb(PurpleHttpConnection *hc, + PurpleHttpResponse *response, gpointer user_data) { gchar *cur_ver; - const char *tmp, *changelog; - char response_code[4]; + const char *changelog; GtkWidget *release_dialog; GString *message; int i = 0; - if(error_message || !response || !len) + if(!purple_http_response_is_successfull(response)) return; - memset(response_code, '\0', sizeof(response_code)); - /* Parse the status code - the response should be in the form of "HTTP/?.? 200 ..." */ - if ((tmp = strstr(response, " ")) != NULL) { - tmp++; - /* Read the 3 digit status code */ - if (len - (tmp - response) > 3) { - memcpy(response_code, tmp, 3); - } - } - - if (strcmp(response_code, "200") != 0) { - purple_debug_error("relnot", "Didn't recieve a HTTP status code of 200.\n"); - return; - } - - /* Go to the start of the data */ - if((changelog = strstr(response, "\r\n\r\n")) == NULL) { - purple_debug_error("relnot", "Unable to find start of HTTP response data.\n"); - return; - } - changelog += 4; + changelog = purple_http_response_get_data(response, NULL); while(changelog[i] && changelog[i] != '\n') i++; @@ -131,10 +110,10 @@ { int last_check = purple_prefs_get_int("/plugins/gtk/relnot/last_check"); if(!last_check || time(NULL) - last_check > MIN_CHECK_INTERVAL) { - gchar *url, *request; + gchar *url; const char *host = "pidgin.im"; - url = g_strdup_printf("http://%s/version.php?version=%s&build=%s", + url = g_strdup_printf("https://%s/version.php?version=%s&build=%s", host, purple_core_get_version(), #ifdef _WIN32 @@ -144,18 +123,8 @@ #endif ); - request = g_strdup_printf( - "GET %s HTTP/1.0\r\n" - "Connection: close\r\n" - "Accept: */*\r\n" - "Host: %s\r\n\r\n", - url, - host); + purple_http_get(NULL, url, version_fetch_cb, NULL); - purple_util_fetch_url_request(NULL, url, TRUE, NULL, FALSE, - request, TRUE, -1, version_fetch_cb, NULL); - - g_free(request); g_free(url); purple_prefs_set_int("/plugins/gtk/relnot/last_check", time(NULL));
--- a/pidgin/smileyparser.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/smileyparser.c Fri Jan 25 02:22:38 2013 -0500 @@ -151,7 +151,7 @@ } /* now for each theme smiley, observe that this does look nasty */ - if (!current_smiley_theme || !(current_smiley_theme->list)) { + if (!current_smiley_theme) { purple_debug_warning("smiley", "theme does not exist\n"); return temp; }
--- a/pidgin/themes/Contents/Resources/Content.html Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/themes/Contents/Resources/Content.html Fri Jan 25 02:22:38 2013 -0500 @@ -1,6 +1,6 @@ <div class="x-container %messageClasses% %messageDirection%"> <abbr class="x-time" title="%time{yyyy-MM-ddTHH:mm:ssZZ}%">(%time%)</abbr> - <div class="x-sender">%sender%:</div> - <div class="x-message">%message%</div> + <span class="x-sender">%sender%:</span> + <span class="x-message">%message%</span> </div> <div id="insert"></div>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/themes/Contents/Resources/Incoming/Content.html Fri Jan 25 02:22:38 2013 -0500 @@ -0,0 +1,8 @@ +<div class="x-container %messageClasses% %messageDirection%"> + <span class="x-header" style="color: %senderColor%;"> + <abbr class="x-time" title="%time{yyyy-MM-ddTHH:mm:ssZZ}%">(%time%)</abbr> + <span class="x-sender">%sender%:</span> + </span> + <span class="x-message">%message%</span> +</div> +<div id="insert"></div>
--- a/pidgin/themes/Contents/Resources/main.css Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/themes/Contents/Resources/main.css Fri Jan 25 02:22:38 2013 -0500 @@ -10,8 +10,11 @@ .x-container, .x-status_container { + clear: left; line-height: 1.3em; margin-bottom: 4px; + height: 100%; + overflow: hidden; } .x-container .x-time, @@ -23,22 +26,19 @@ .x-container .x-sender { - display: inline; font-weight: bold; } -.x-container .x-message, -.x-status_container .x-message +.x-message p { - display: inline; + margin: 0; } /* Colour-ify things */ -.x-container.incoming .x-time, -.x-container.incoming .x-sender +#Chat:not(.groupchat) .x-container.incoming .x-header { - color: #cc0000; + color: #cc0000 ! important; } .x-container.outgoing .x-time,
--- a/pidgin/themes/Makefile.am Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/themes/Makefile.am Fri Jan 25 02:22:38 2013 -0500 @@ -7,6 +7,8 @@ themeresources_DATA = Contents/Resources/Content.html \ Contents/Resources/Status.html \ Contents/Resources/main.css +themeresourcesincomingdir = $(themeresourcesdir)/Incoming +themeresourcesincoming_DATA = Contents/Resources/Incoming/Content.html themevariantsdir = $(themeresourcesdir)/Variants themevariants_DATA = Contents/Resources/Variants/Default.css \ Contents/Resources/Variants/No-Timestamps.css @@ -15,5 +17,6 @@ $(themetemplate_DATA) \ $(themecontents_DATA) \ $(themeresources_DATA) \ + $(themeresourcesincoming_DATA) \ $(themevariants_DATA)
--- a/pidgin/win32/gtkdocklet-win32.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/win32/gtkdocklet-win32.c Fri Jan 25 02:22:38 2013 -0500 @@ -46,7 +46,7 @@ */ static HWND systray_hwnd = NULL; /* additional two cached_icons entries for pending and connecting icons */ -static HICON cached_icons[PURPLE_STATUS_NUM_PRIMITIVES + 2]; +static HICON cached_icons[PURPLE_STATUS_NUM_PRIMITIVES + 3]; static GtkWidget *image = NULL; /* 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; @@ -497,14 +497,16 @@ } static void winpidgin_tray_update_icon(PurpleStatusPrimitive status, - gboolean connecting, gboolean pending) { + PidginDockletFlag flags) { int icon_index; g_return_if_fail(image != NULL); - if(connecting) + if(flags & PIDGIN_DOCKLET_CONNECTING) icon_index = PURPLE_STATUS_NUM_PRIMITIVES; - else if(pending) + else if(flags & PIDGIN_DOCKLET_EMAIL_PENDING) + icon_index = PURPLE_STATUS_NUM_PRIMITIVES+2; + else if(flags & PIDGIN_DOCKLET_CONV_PENDING) icon_index = PURPLE_STATUS_NUM_PRIMITIVES+1; else icon_index = status; @@ -535,9 +537,11 @@ break; } - if (pending) + if (flags & PIDGIN_DOCKLET_EMAIL_PENDING) + icon_name = PIDGDIN_STOCK_TRAY_EMAIL; + else if (flags & PIDGIN_DOCKLET_CONV_PENDING) icon_name = PIDGIN_STOCK_TRAY_PENDING; - if (connecting) + else if (flags & PIDGIN_DOCKLET_CONNECTING) icon_name = PIDGIN_STOCK_TRAY_CONNECT; g_return_if_fail(icon_name != NULL);
--- a/pidgin/win32/gtkwin32dep.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/win32/gtkwin32dep.c Fri Jan 25 02:22:38 2013 -0500 @@ -171,10 +171,19 @@ } void winpidgin_notify_uri(const char *uri) { - /* We'll allow whatever URI schemes are supported by the - * default http browser. + /* Allow a few commonly used and "safe" schemes to go to the specific + * class handlers and send everything else to the default http browser. + * This isn't optimal, but should cover the most common cases. I didn't + * see any better secure solutions when I did some research. */ - winpidgin_shell_execute(uri, "open", "http"); + gchar *scheme = g_uri_parse_scheme(uri); + if (scheme && (g_ascii_strcasecmp(scheme, "https") == 0 + || g_ascii_strcasecmp(scheme, "ftp") == 0 + || g_ascii_strcasecmp(scheme, "mailto") == 0)) + winpidgin_shell_execute(uri, "open", scheme); + else + winpidgin_shell_execute(uri, "open", "http"); + g_free(scheme); } #define PIDGIN_WM_FOCUS_REQUEST (WM_APP + 13) @@ -377,7 +386,8 @@ } void winpidgin_init(HINSTANCE hint) { - FARPROC proc; + typedef void (__cdecl* LPFNSETLOGFILE)(const LPCSTR); + LPFNSETLOGFILE MySetLogFile; gchar *exchndl_dll_path; purple_debug_info("winpidgin", "winpidgin_init start\n"); @@ -385,10 +395,10 @@ exe_hInstance = hint; exchndl_dll_path = g_build_filename(wpurple_install_dir(), "exchndl.dll", NULL); - proc = wpurple_find_and_loadproc(exchndl_dll_path, "SetLogFile"); + MySetLogFile = (LPFNSETLOGFILE) wpurple_find_and_loadproc(exchndl_dll_path, "SetLogFile"); g_free(exchndl_dll_path); exchndl_dll_path = NULL; - if (proc) { + if (MySetLogFile) { gchar *debug_dir, *locale_debug_dir; debug_dir = g_build_filename(purple_user_dir(), "pidgin.RPT", NULL); @@ -396,7 +406,7 @@ purple_debug_info("winpidgin", "Setting exchndl.dll LogFile to %s\n", debug_dir); - (proc)(locale_debug_dir); + MySetLogFile(locale_debug_dir); g_free(debug_dir); g_free(locale_debug_dir);
--- a/pidgin/win32/nsis/generate_gtk_zip.sh Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/win32/nsis/generate_gtk_zip.sh Fri Jan 25 02:22:38 2013 -0500 @@ -2,31 +2,57 @@ # Script to generate zip file for GTK+ runtime to be included in Pidgin installer PIDGIN_BASE=$1 +GPG_SIGN=$2 if [ ! -e $PIDGIN_BASE/ChangeLog ]; then echo $(basename $0) must must have the pidgin base dir specified as a parameter. exit 1 fi -STAGE_DIR=$PIDGIN_BASE/pidgin/win32/nsis/gtk_runtime_stage +STAGE_DIR=`readlink -f $PIDGIN_BASE/pidgin/win32/nsis/gtk_runtime_stage` #Subdirectory of $STAGE_DIR INSTALL_DIR=Gtk CONTENTS_FILE=$INSTALL_DIR/CONTENTS +PIDGIN_VERSION=$( < $PIDGIN_BASE/VERSION ) #This needs to be changed every time there is any sort of change. -BUNDLE_VERSION=2.16.6.0 +BUNDLE_VERSION=2.16.6.1 +BUNDLE_SHA1SUM=5e16b7efb11943e8c80bc390f6c38df904fd36ed +ZIP_FILE="$PIDGIN_BASE/pidgin/win32/nsis/gtk-runtime-$BUNDLE_VERSION.zip" -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.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" +#Download the existing file (so that we distribute the exact same file for all releases with the same bundle version) +FILE="$ZIP_FILE" +if [ ! -e "$FILE" ]; then + wget "https://pidgin.im/win32/download_redir.php?version=$PIDGIN_VERSION>k_version=$BUNDLE_VERSION&dl_pkg=gtk" -O "$FILE" +fi +CHECK_SHA1SUM=`sha1sum $FILE` +CHECK_SHA1SUM=${CHECK_SHA1SUM%%\ *} +if [ "$CHECK_SHA1SUM" != "$BUNDLE_SHA1SUM" ]; then + echo "sha1sum ($CHECK_SHA1SUM) for $FILE doesn't match expected value of $BUNDLE_SHA1SUM" + # Allow "devel" versions to build their own bundles if the download doesn't succeed + if [[ "$PIDGIN_VERSION" == *"devel" ]]; then + echo "Continuing GTK+ Bundle creation for development version of Pidgin" + else + exit 1 + fi +else + exit 0 +fi + + +ATK="http://ftp.gnome.org/pub/gnome/binaries/win32/atk/1.32/atk_1.32.0-2_win32.zip ATK 1.32.0-2 sha1sum:3c31c9d6b19af840e2bd8ccbfef4072a6548dc4e" +#Cairo 1.10.2 has a bug that can be seen when selecting text +#CAIRO="http://ftp.gnome.org/pub/GNOME/binaries/win32/dependencies/cairo_1.10.2-2_win32.zip Cairo 1.10.2-2 sha1sum:d44cd66a9f4d7d29a8f2c28d1c1c5f9b0525ba44" +CAIRO="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/cairo_1.8.10-1_win32.zip Cairo 1.8.10-1 sha1sum:a08476cccd807943958610977a138c4d6097c7b8" +EXPAT="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/expat_2.1.0-1_win32.zip Expat 2.1.0-1 gpg:0x71D4DDE53F188CBE" +FONTCONFIG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/fontconfig_2.8.0-2_win32.zip Fontconfig 2.8.0-2 sha1sum:37a3117ea6cc50c8a88fba9b6018f35a04fa71ce" +FREETYPE="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/freetype_2.4.10-1_win32.zip Freetype 2.4.10-1 gpg:0x71D4DDE53F188CBE" +GETTEXT="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/gettext-runtime_0.18.1.1-2_win32.zip Gettext 0.18.1.1-2 sha1sum:a7cc1ce2b99b408d1bbea9a3b4520fcaf26783b3" +GLIB="http://ftp.gnome.org/pub/gnome/binaries/win32/glib/2.28/glib_2.28.8-1_win32.zip Glib 2.28.8-1 sha1sum:5d158f4c77ca0b5508e1042955be573dd940b574" +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 sha1sum:012853e6de814ebda0cc4459f9eed8ae680e6d17" +LIBPNG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libpng_1.4.12-1_win32.zip libpng 1.4.12-1 gpg:0x71D4DDE53F188CBE" +PANGO="http://ftp.gnome.org/pub/gnome/binaries/win32/pango/1.29/pango_1.29.4-1_win32.zip Pango 1.29.4-1 sha1sum:3959319bd04fbce513458857f334ada279b8cdd4" +ZLIB="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/zlib_1.2.5-2_win32.zip zlib 1.2.5-2 sha1sum:568907188761df2d9309196e447d91bbc5555d2b" ALL="ATK CAIRO EXPAT FONTCONFIG FREETYPE GETTEXT GLIB GTK LIBPNG PANGO ZLIB" @@ -41,11 +67,40 @@ function download_and_extract { URL=${1%%\ *} - NAME=${1#*\ } + VALIDATION=${1##*\ } + NAME=${1%\ *} + NAME=${NAME#*\ } FILE=$(basename $URL) if [ ! -e $FILE ]; then echo Downloading $NAME - wget $URL || return 1 + wget $URL || exit 1 + fi + VALIDATION_TYPE=${VALIDATION%%:*} + VALIDATION_VALUE=${VALIDATION##*:} + if [ $VALIDATION_TYPE == 'sha1sum' ]; then + CHECK_SHA1SUM=`sha1sum $FILE` + CHECK_SHA1SUM=${CHECK_SHA1SUM%%\ *} + if [ "$CHECK_SHA1SUM" != "$VALIDATION_VALUE" ]; then + echo "sha1sum ($CHECK_SHA1SUM) for $FILE doesn't match expected value of $VALIDATION_VALUE" + exit 1 + fi + elif [ $VALIDATION_TYPE == 'gpg' ]; then + if [ ! -e "$FILE.asc" ]; then + echo Downloading GPG key for $NAME + wget "$URL.asc" || exit 1 + fi + #Use our own keyring to avoid adding stuff to the main keyring + #This doesn't use $GPG_SIGN because we don't this validation to be bypassed when people are skipping signing output + GPG_BASE="gpg -q --keyring $STAGE_DIR/$VALIDATION_VALUE-keyring.gpg" + if [[ ! -e $STAGE_DIR/$VALIDATION_VALUE-keyring.gpg \ + || `$GPG_BASE --list-keys "$VALIDATION_VALUE" > /dev/null && echo -n "0"` -ne 0 ]]; then + touch $STAGE_DIR/$VALIDATION_VALUE-keyring.gpg + $GPG_BASE --no-default-keyring --keyserver pgp.mit.edu --recv-key "$VALIDATION_VALUE" || exit 1 + fi + $GPG_BASE --verify "$FILE.asc" || (echo "$FILE failed signature verification"; exit 1) || exit 1 + else + echo "Unrecognized validation type of $VALIDATION_TYPE" + exit 1 fi EXTENSION=${FILE##*.} #This is an OpenSuSE build service RPM @@ -77,7 +132,9 @@ done #Generate zip file to be included in installer -zip -9 -r ../gtk-runtime-$BUNDLE_VERSION.zip Gtk +rm -f $ZIP_FILE +zip -9 -r $ZIP_FILE Gtk +($GPG_SIGN -ab $ZIP_FILE && $GPG_SIGN --verify $ZIP_FILE.asc) || exit 1 exit 0
--- a/pidgin/win32/nsis/pidgin-installer.nsi Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Fri Jan 25 02:22:38 2013 -0500 @@ -34,7 +34,6 @@ !include "MUI.nsh" !include "Sections.nsh" -!include "WinVer.nsh" !include "LogicLib.nsh" !include "Memento.nsh" @@ -71,7 +70,7 @@ !define PERL_REG_KEY "SOFTWARE\Perl" !define PERL_DLL "perl510.dll" -!define DOWNLOADER_URL "http://pidgin.im/win32/download_redir.php?version=${PIDGIN_VERSION}" +!define DOWNLOADER_URL "https://pidgin.im/win32/download_redir.php?version=${PIDGIN_VERSION}" !define MEMENTO_REGISTRY_ROOT HKLM !define MEMENTO_REGISTRY_KEY "${PIDGIN_UNINSTALL_KEY}" @@ -93,6 +92,7 @@ ;Reserve files used in .onInit ;for faster start-up ReserveFile "${NSISDIR}\Plugins\System.dll" +ReserveFile "${NSISDIR}\Plugins\UserInfo.dll" !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS !insertmacro MUI_RESERVEFILE_LANGDLL @@ -118,8 +118,8 @@ ;Finish Page config !define MUI_FINISHPAGE_NOAUTOCLOSE - !define MUI_FINISHPAGE_RUN "$INSTDIR\pidgin.exe" - !define MUI_FINISHPAGE_RUN_NOTCHECKED + ;!define MUI_FINISHPAGE_RUN "$INSTDIR\pidgin.exe" + ;!define MUI_FINISHPAGE_RUN_NOTCHECKED !define MUI_FINISHPAGE_LINK $(PIDGINFINISHVISITWEBSITE) !define MUI_FINISHPAGE_LINK_LOCATION "http://pidgin.im" @@ -148,13 +148,6 @@ !include "${PIDGIN_NSIS_INCLUDE_PATH}\langmacros.nsh" ;-------------------------------- -;Reserve Files - ; Only need this if using bzip2 compression - - !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS - !insertmacro MUI_RESERVEFILE_LANGDLL - ReserveFile "${NSISDIR}\Plugins\UserInfo.dll" - ;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Start Install Sections ;; @@ -268,9 +261,18 @@ NSISdl::download /TIMEOUT=10000 $R2 $R1 Pop $R0 ;StrCmp $R0 "cancel" done - StrCmp $R0 "success" +2 + StrCmp $R0 "success" 0 prompt_retry + + Push "${GTK_SHA1SUM}" + Push "$R1" ; Filename + Call CheckSHA1Sum + Pop $R0 + + StrCmp "$R0" "0" extract + prompt_retry: MessageBox MB_RETRYCANCEL "$(PIDGINGTKDOWNLOADERROR)" /SD IDCANCEL IDRETRY retry IDCANCEL done + extract: !endif ;Delete the old Gtk directory @@ -282,7 +284,9 @@ StrCmp $R0 "success" +2 DetailPrint "$R0" ;print error message to log +!ifndef OFFLINE_INSTALLER done: +!endif SectionEnd ; end of GTK+ section ;-------------------------------- @@ -335,7 +339,7 @@ Delete "$INSTDIR\plugins\liboscar.dll" Delete "$INSTDIR\plugins\libjabber.dll" - File /r /x locale ..\..\..\${PIDGIN_INSTALL_DIR}\*.* + File /r /x locale /x Gtk ..\..\..\${PIDGIN_INSTALL_DIR}\*.* ; Check if Perl is installed, if so add it to the AppPaths ReadRegStr $R2 HKLM ${PERL_REG_KEY} "" @@ -351,21 +355,6 @@ perl_done: - ; If this is under NT4, delete the SILC support stuff - ; there is a bug that will prevent any account from connecting - ; See https://lists.silcnet.org/pipermail/silc-devel/2005-January/001588.html - ; Also, remove the GSSAPI SASL plugin and associated files as they aren't - ; compatible with NT4. - ${If} ${IsNT} - ${AndIf} ${IsWinNT4} - ;SILC - Delete "$INSTDIR\plugins\libsilc.dll" - Delete "$INSTDIR\libsilcclient-1-1-2.dll" - Delete "$INSTDIR\libsilc-1-1-2.dll" - ;GSSAPI - Delete "$INSTDIR\sasl2\saslGSSAPI.dll" - ${EndIf} - SetOutPath "$INSTDIR" ; If we don't have install rights we're done @@ -467,9 +456,18 @@ NSISdl::download /TIMEOUT=10000 $R2 $R1 Pop $R0 StrCmp $R0 "cancel" done - StrCmp $R0 "success" +2 + StrCmp $R0 "success" 0 prompt_retry + + Push "${DEBUG_SYMBOLS_SHA1SUM}" + Push "$R1" ; Filename + Call CheckSHA1Sum + Pop $R0 + + StrCmp "$R0" "0" extract + prompt_retry: MessageBox MB_RETRYCANCEL "$(PIDGINDEBUGSYMBOLSERROR)" /SD IDCANCEL IDRETRY retry IDCANCEL done + extract: !endif SetOutPath "$INSTDIR" @@ -478,7 +476,9 @@ StrCmp $R0 "success" +2 DetailPrint "$R0" ;print error message to log +!ifndef OFFLINE_INSTALLER done: +!endif SectionEnd ;-------------------------------- @@ -637,7 +637,8 @@ Delete "$INSTDIR\libpurple.dll" Delete "$INSTDIR\libsasl.dll" Delete "$INSTDIR\libsilc-1-1-2.dll" - Delete "$INSTDIR\libsilcclient-1-1-2.dll" + Delete "$INSTDIR\libsilcclient-1-1-3.dll" + Delete "$INSTDIR\libssp-0.dll" Delete "$INSTDIR\libxml2-2.dll" Delete "$INSTDIR\libymsg.dll" Delete "$INSTDIR\nss3.dll" @@ -1300,3 +1301,40 @@ Pop $R0 Exch $R1 FunctionEnd + +!ifndef OFFLINE_INSTALLER +; Input Stack: Filename, SHA1sum +; Output Return Code: 0=Match; 1=FileSum error; 2=Mismatch +Function CheckSHA1Sum + Push $R0 + Exch + Pop $R0 ;Filename + Push $R2 + Exch 2 + Pop $R2 ;SHA1sum + Push $R1 + + SHA1Plugin::FileSum "$R0" + Pop $R1 + Pop $R0 + + StrCmp "$R1" "0" +4 + DetailPrint "SHA1Sum calculation error: $R0" + IntOp $R1 0 + 1 + Goto done + + ; Compare the SHA1Sums + StrCmp $R2 $R0 +4 + DetailPrint "SHA1Sum mismatch... Expected $R2; got $R0" + IntOp $R1 0 + 2 + Goto done + + IntOp $R1 0 + 0 + + done: + Pop $R2 + Pop $R0 + Exch $R1 ;$R1 has the return code +FunctionEnd +!endif +
--- a/pidgin/win32/winpidgin.c Thu Aug 23 01:27:48 2012 -0400 +++ b/pidgin/win32/winpidgin.c Fri Jan 25 02:22:38 2013 -0500 @@ -38,9 +38,10 @@ #include <sys/stat.h> #include "config.h" -typedef int (CALLBACK* LPFNPIDGINMAIN)(HINSTANCE, int, char**); -typedef void (CALLBACK* LPFNSETDLLDIRECTORY)(LPCWSTR); -typedef BOOL (CALLBACK* LPFNATTACHCONSOLE)(DWORD); +typedef int (__cdecl* LPFNPIDGINMAIN)(HINSTANCE, int, char**); +typedef void (WINAPI* LPFNSETDLLDIRECTORY)(LPCWSTR); +typedef BOOL (WINAPI* LPFNATTACHCONSOLE)(DWORD); +typedef BOOL (WINAPI* LPFNSETPROCESSDEPPOLICY)(DWORD); static BOOL portable_mode = FALSE; @@ -556,7 +557,7 @@ len = WideCharToMultiByte(CP_UTF8, 0, tmp1, wlen, NULL, 0, NULL, NULL); if (len) { - utf8msg = malloc(len * sizeof(char)); + utf8msg = malloc(len); len = WideCharToMultiByte(CP_UTF8, 0, tmp1, wlen, utf8msg, len, NULL, NULL); } @@ -642,16 +643,24 @@ } } + /* Permanently enable DEP if the OS supports it */ + if ((hmod = GetModuleHandleW(L"kernel32.dll"))) { + LPFNSETPROCESSDEPPOLICY MySetProcessDEPPolicy = + (LPFNSETPROCESSDEPPOLICY) + GetProcAddress(hmod, "SetProcessDEPPolicy"); + if (MySetProcessDEPPolicy) + MySetProcessDEPPolicy(1); //PROCESS_DEP_ENABLE + } + if (debug || help || version) { /* If stdout hasn't been redirected to a file, alloc a console * (_istty() doesn't work for stuff using the GUI subsystem) */ if (_fileno(stdout) == -1 || _fileno(stdout) == -2) { LPFNATTACHCONSOLE MyAttachConsole = NULL; - if ((hmod = GetModuleHandleW(L"kernel32.dll"))) { + if (hmod) MyAttachConsole = (LPFNATTACHCONSOLE) GetProcAddress(hmod, "AttachConsole"); - } if ((MyAttachConsole && MyAttachConsole(ATTACH_PARENT_PROCESS)) || AllocConsole()) { freopen("CONOUT$", "w", stdout); @@ -683,31 +692,34 @@ wcscat(pidgin_dir, L"\\exchndl.dll"); if ((hmod = LoadLibraryW(pidgin_dir))) { - FARPROC proc; + typedef void (__cdecl* LPFNSETLOGFILE)(const LPCSTR); + LPFNSETLOGFILE MySetLogFile; /* exchndl.dll is built without UNICODE */ char debug_dir[MAX_PATH]; printf("Loaded exchndl.dll\n"); /* Temporarily override exchndl.dll's logfile * to something sane (Pidgin will override it * again when it initializes) */ - proc = GetProcAddress(hmod, "SetLogFile"); - if (proc) { - if (GetTempPathA(sizeof(debug_dir) * sizeof(char), debug_dir) != 0) { + MySetLogFile = (LPFNSETLOGFILE) GetProcAddress(hmod, "SetLogFile"); + if (MySetLogFile) { + if (GetTempPathA(sizeof(debug_dir), debug_dir) != 0) { strcat(debug_dir, "pidgin.RPT"); printf(" Setting exchndl.dll LogFile to %s\n", debug_dir); - (proc)(debug_dir); + MySetLogFile(debug_dir); } } - proc = GetProcAddress(hmod, "SetDebugInfoDir"); - if (proc) { + /* The function signature for SetDebugInfoDir is the same as SetLogFile, + * so we can reuse the variable */ + MySetLogFile = (LPFNSETLOGFILE) GetProcAddress(hmod, "SetDebugInfoDir"); + if (MySetLogFile) { char *pidgin_dir_ansi = NULL; /* Restore pidgin_dir to point to where the executable is */ pidgin_dir_start[0] = L'\0'; i = WideCharToMultiByte(CP_ACP, 0, pidgin_dir, -1, NULL, 0, NULL, NULL); if (i != 0) { - pidgin_dir_ansi = malloc(i * sizeof(char)); + pidgin_dir_ansi = malloc(i); i = WideCharToMultiByte(CP_ACP, 0, pidgin_dir, -1, pidgin_dir_ansi, i, NULL, NULL); if (i == 0) { @@ -716,13 +728,13 @@ } } if (pidgin_dir_ansi != NULL) { - _snprintf(debug_dir, sizeof(debug_dir) / sizeof(char), + _snprintf(debug_dir, sizeof(debug_dir), "%s\\pidgin-%s-dbgsym", pidgin_dir_ansi, VERSION); - debug_dir[sizeof(debug_dir) / sizeof(char) - 1] = '\0'; + debug_dir[sizeof(debug_dir) - 1] = '\0'; printf(" Setting exchndl.dll DebugInfoDir to %s\n", debug_dir); - (proc)(debug_dir); + MySetLogFile(debug_dir); free(pidgin_dir_ansi); } } @@ -800,7 +812,7 @@ int len = WideCharToMultiByte(CP_UTF8, 0, szArglist[i], -1, NULL, 0, NULL, NULL); if (len != 0) { - char *arg = malloc(len * sizeof(char)); + char *arg = malloc(len); len = WideCharToMultiByte(CP_UTF8, 0, szArglist[i], -1, arg, len, NULL, NULL); if (len != 0) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/share/ca-certs/Baltimore_CyberTrust_Root.pem Fri Jan 25 02:22:38 2013 -0500 @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ +RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD +VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX +DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y +ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy +VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr +mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr +IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK +mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu +XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy +dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye +jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1 +BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3 +DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92 +9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx +jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0 +Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz +ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS +R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp +-----END CERTIFICATE-----
--- a/share/ca-certs/Makefile.am Thu Aug 23 01:27:48 2012 -0400 +++ b/share/ca-certs/Makefile.am Fri Jan 25 02:22:38 2013 -0500 @@ -1,6 +1,7 @@ CERTIFICATES = \ AddTrust_External_Root.pem \ America_Online_Root_Certification_Authority_1.pem \ + Baltimore_CyberTrust_Root.pem \ CAcert_Root.pem \ CAcert_Class3.pem \ Deutsche_Telekom_Root_CA_2.pem \ @@ -14,7 +15,6 @@ Thawte_Premium_Server_CA.pem \ Thawte_Primary_Root_CA.pem \ ValiCert_Class_2_VA.pem \ - Verisign_RSA_Secure_Server_CA.pem \ Verisign_Class3_Primary_CA.pem \ VeriSign_Class_3_Public_Primary_Certification_Authority_-_G2.pem \ VeriSign_Class_3_Public_Primary_Certification_Authority_-_G5.pem \ @@ -23,9 +23,7 @@ EXTRA_CERTS = \ AOL_Member_CA.pem \ DigiCertHighAssuranceCA-3.pem \ - Microsoft_Internet_Authority.pem \ Microsoft_Internet_Authority_2010.pem \ - Microsoft_Secure_Server_Authority.pem \ Microsoft_Secure_Server_Authority_2010.pem \ VeriSign_Class3_Extended_Validation_CA.pem \ VeriSign_International_Server_Class_3_CA.pem
--- a/share/ca-certs/Microsoft_Internet_Authority.pem Thu Aug 23 01:27:48 2012 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,29 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFCjCCBHOgAwIBAgIEBycWdTANBgkqhkiG9w0BAQUFADB1MQswCQYDVQQGEwJV -UzEYMBYGA1UEChMPR1RFIENvcnBvcmF0aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJU -cnVzdCBTb2x1dGlvbnMsIEluYy4xIzAhBgNVBAMTGkdURSBDeWJlclRydXN0IEds -b2JhbCBSb290MB4XDTA4MDIxOTE4MjcwMloXDTExMDIxOTE4MjQ1M1owJzElMCMG -A1UEAxMcTWljcm9zb2Z0IEludGVybmV0IEF1dGhvcml0eTCCAiIwDQYJKoZIhvcN -AQEBBQADggIPADCCAgoCggIBAKiloatvDehDG/rQriel2AC9qmSJdvjKb2fmJf30 -K7SaC3zQu8kGQxENUEFsHsH0jmBejJ9vvn9tHZ8hGL+kORvWUVBskdMzP65rC0V0 -VeVgUYzPZ7MvrLfhh9+v3yJ7qRuf1aNgmzJg5t1AA91X+aRrFT4lRzl9BFWhQ1VS -XaD7l6qoiyhD8FbrdLRAe61swsRmzWeXoy6NJpOBsGXaCSG1Jooylro+zkWxt97c -NkNf/wYqoYcIXo02YpFbwreveejW9a0Lh/1z9+e9aiMtC5QnPT57GTqNINt5R0rp -Iz4g3GJhmjXVoVF/tev5DMJuhRgPoz0W0aA3UnSmTWh2RFvgqawLqSRrKUhVjySi -/m5s62uG5xxIftO7/6ljzS061CFoV/RBl/I3WghYp04sr4cSXWa/rL449YhBT8BJ -jltefWCYAOcT1nA4oFXwXbl1qCUIkZ0bqwju2FGW5vl2qh6vmzcQjc3XxD0m2UqC -yJNFa9SUgVXtUCqeOI+KqgLW01tpqZteG10byWKmppTVAvdPwHoGE0bl6wBwXldE -f7Pn4lqu6Yp4bedP3Qv1sfp2H7D98cxSwYRt3UcXN2kjTPv+pby2fEHm9GWf3iE/ -7OtzUI7LYhP7+rGyuCYTtZKH1jDWHrTZBpnAPmrAQQHCI8/4TjF9ZQ1mqRi6x9I9 -9XGfAgMBAAGjggFvMIIBazASBgNVHRMBAf8ECDAGAQH/AgEBMFMGA1UdIARMMEow -SAYJKwYBBAGxPgEAMDswOQYIKwYBBQUHAgEWLWh0dHA6Ly9jeWJlcnRydXN0Lm9t -bmlyb290LmNvbS9yZXBvc2l0b3J5LmNmbTAOBgNVHQ8BAf8EBAMCAYYwgYkGA1Ud -IwSBgTB/oXmkdzB1MQswCQYDVQQGEwJVUzEYMBYGA1UEChMPR1RFIENvcnBvcmF0 -aW9uMScwJQYDVQQLEx5HVEUgQ3liZXJUcnVzdCBTb2x1dGlvbnMsIEluYy4xIzAh -BgNVBAMTGkdURSBDeWJlclRydXN0IEdsb2JhbCBSb290ggIBpTBFBgNVHR8EPjA8 -MDqgOKA2hjRodHRwOi8vd3d3LnB1YmxpYy10cnVzdC5jb20vY2dpLWJpbi9DUkwv -MjAxOC9jZHAuY3JsMB0GA1UdDgQWBBTG27vA2CAZkvFg/IjxWH+8G06PGjANBgkq -hkiG9w0BAQUFAAOBgQBnSDXCyiqGmHTAEJOtZYVm/IbzGtzCY423NF6/yuccYZkm -spJnDoh8nq3nx3P2KBEyPAqoQ1MEFC+ByQjV4AAQ9dMQALUGNRfHhFUhBeeIybYd -bzvKOxSlIYSwO2T56+oXyDlkbQWKmHec5qzCE0lwGHslsRU/CHwhuFu2nH+CxQ== ------END CERTIFICATE-----
--- a/share/ca-certs/Microsoft_Secure_Server_Authority.pem Thu Aug 23 01:27:48 2012 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIGEzCCA/ugAwIBAgIKYRZtLwAEAAAAIDANBgkqhkiG9w0BAQUFADAnMSUwIwYD -VQQDExxNaWNyb3NvZnQgSW50ZXJuZXQgQXV0aG9yaXR5MB4XDTA4MDQwOTIxMzc1 -NFoXDTExMDIxOTE4MjQ1M1owgYsxEzARBgoJkiaJk/IsZAEZFgNjb20xGTAXBgoJ -kiaJk/IsZAEZFgltaWNyb3NvZnQxFDASBgoJkiaJk/IsZAEZFgRjb3JwMRcwFQYK -CZImiZPyLGQBGRYHcmVkbW9uZDEqMCgGA1UEAxMhTWljcm9zb2Z0IFNlY3VyZSBT -ZXJ2ZXIgQXV0aG9yaXR5MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA -kYTz6fKXvrdfIr5o3Ue4CRIzhTE+8JE4hrLTQki3emjYn/CfHRPb7hmMiOZmWBdE -DUEymyXOyZ7Sy2tC6WaBC4onVYotPoSsaOZJv6EJeHPk64RiWTfX+XqufRndYOEC -DUmotYQNPV/8InioIBf9+gOSsAMdmyGZF6C1PkJqvPZTRxNv6hxuMMb6uOQIPoFX -/ceQvAOZcJx2qGsAVKsJHylYkC0GgVyFVhOI0vcZZBcP5T+NtOmyjVBWdxS413HL -D+8w+3wG0bOP8EyOeRnuf0KLXGBangte0ZFIRd28GXpo5UrcA/r5000e2RTHmhC4 -8YPMIoi+q9XZoF5R0Z069QIDAQABo4IB2jCCAdYwEgYDVR0TAQH/BAgwBgEB/wIB -ADAdBgNVHQ4EFgQUFFXEOeA9LtFVLkiWsNh+FCIGk7wwCwYDVR0PBAQDAgGGMBIG -CSsGAQQBgjcVAQQFAgMFAAUwIwYJKwYBBAGCNxUCBBYEFM7FoL4P/nlmdZEP8PeS -WzWYqBWzMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMB8GA1UdIwQYMBaAFMbb -u8DYIBmS8WD8iPFYf7wbTo8aMIGjBgNVHR8EgZswgZgwgZWggZKggY+GNmh0dHA6 -Ly9tc2NybC5taWNyb3NvZnQuY29tL3BraS9tc2NvcnAvY3JsL21zd3d3KDQpLmNy -bIY0aHR0cDovL2NybC5taWNyb3NvZnQuY29tL3BraS9tc2NvcnAvY3JsL21zd3d3 -KDQpLmNybIYfaHR0cDovL2NvcnBwa2kvY3JsL21zd3d3KDQpLmNybDB5BggrBgEF -BQcBAQRtMGswPAYIKwYBBQUHMAKGMGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9w -a2kvbXNjb3JwL21zd3d3KDQpLmNydDArBggrBgEFBQcwAoYfaHR0cDovL2NvcnBw -a2kvYWlhL21zd3d3KDQpLmNydDANBgkqhkiG9w0BAQUFAAOCAgEAempuzk/VLM4N -H9TAbFtCjKc95iGmyx2bHbEk9m2cbGxXjBre+N4cJoIYYmhLrZ6L712ov1NjM73b -m8fb2Fy8Yw8Cmwc8VtarPZT2yzGr8MhNUDVuZswaKfjCY3H7RYv/XKc7AOMd25WP -/M0WTT4Bna6hl9dUaDGwv5SZFFIJ17FLo4FR2H7IkOOI/WcUPAHeDXUewp4qRPE/ -560xZrLSeNH2lKnOAwwXxwnXSo5WOF5AQXh1nRdbBV9Nu7yI6jH1QV6fKf6oFU2Y -IOjpnJ0FihVB6XoZ0wNOUMzPEEQcTfIoVoc+t0iK02wcmTLgBgbYU703dHvvPTcn -IfdI2mscx8l9MjUOdklIIve0FhCxRPqHpEeKjM95gllbXmWgQxAXiog+A62fEo5d -M7nfeEyiweSlhj1cv+2dyhzyS5saKYkk3ocCnOMCyD0M+4gJx4n4b/zT3rcujyN+ -7m20PbBTjcdTT1+AxOs75rON2hhKUqqrk2MDCpnEJsNK4TuRyDUtm9r+AxaZ4XRK -MT8InY1Xl9hzrIK6MVERYH46kxg6odwpzJ8Urn4dREBiMy6Gzq8mtyXvpYEcmeGL -zz1aT7qNNbQ0qqbPb6RpOMHlUWOIhVWJC71T5WK1pynAc3P9zOm8BkUYvIyJvCbR -bufCGVng4FAtVZ1advxSVRoa4GyuFZ8= ------END CERTIFICATE-----
--- a/share/ca-certs/Verisign_RSA_Secure_Server_CA.pem Thu Aug 23 01:27:48 2012 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,15 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICNDCCAaECEAKtZn5ORf5eV288mBle3cAwDQYJKoZIhvcNAQECBQAwXzEL -MAkGA1UEBhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMu -MS4wLAYDVQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9y -aXR5MB4XDTk0MTEwOTAwMDAwMFoXDTEwMDEwNzIzNTk1OVowXzELMAkGA1UE -BhMCVVMxIDAeBgNVBAoTF1JTQSBEYXRhIFNlY3VyaXR5LCBJbmMuMS4wLAYD -VQQLEyVTZWN1cmUgU2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGb -MA0GCSqGSIb3DQEBAQUAA4GJADCBhQJ+AJLOesGugz5aqomDV6wlAXYMra6O -LDfO6zV4ZFQD5YRAUcm/jwjiioII0haGN1XpsSECrXZogZoFokvJSyVmIlZs -iAeP94FZbYQHZXATcXY+m3dM41CJVphIuR2nKRoTLkoRWZweFdVJVCxzOmmC -sZc5nG1wZ0jl3S3WyB57AgMBAAEwDQYJKoZIhvcNAQECBQADfgBl3X7hsuyw -4jrg7HFGmhkRuNPHoLQDQCYCPgmc4RKz0Vr2N6W3YQO2WxZpO8ZECAyIUwxr -l0nHPjXcbLm7qt9cuzovk2C2qUtN8iD3zV9/ZHuO3ABc1/p3yjkWWW8O6tO1 -g39NTUJWdrTJXwT4OPjr0l91X817/OWOgHz8UA== ------END CERTIFICATE-----
--- a/valgrind-suppressions Thu Aug 23 01:27:48 2012 -0400 +++ b/valgrind-suppressions Fri Jan 25 02:22:38 2013 -0500 @@ -151,4 +151,16 @@ fun:purple_plugins_load_saved fun:main } - +{ + libfontconfig leaks + Memcheck:Leak + fun:*alloc + obj:/usr/lib/libfontconfig.so.* + ... +} +{ + webkitgtk uninitialized values + Memcheck:Cond + obj:/usr/lib/libwebkitgtk-* + ... +}