Mercurial > pidgin
changeset 30526:8648453adb22
propagate from branch 'im.pidgin.pidgin' (head 96b27a20c7250b87761a8bd4f5540bf1fa32b6f4)
to branch 'im.pidgin.cpw.malu.xmpp.google_relay' (head 303f4072d75faa7770a5339dbfea05da7cee30a4)
author | Marcus Lundblad <ml@update.uu.se> |
---|---|
date | Tue, 09 Mar 2010 21:41:41 +0000 |
parents | 3d08b59ebf59 (current diff) d93676b59db1 (diff) |
children | f01d73b4452e |
files | libpurple/protocols/jabber/jabber.c libpurple/protocols/jabber/jabber.h pidgin/gtkdocklet-x11.c pidgin/pixmaps/emblems/16/mobile.png pidgin/pixmaps/emblems/16/music.png pidgin/pixmaps/emblems/scalable/mobile.svg pidgin/pixmaps/emblems/scalable/music.svg pidgin/pixmaps/emotes/default/24/eat.png pidgin/pixmaps/emotes/default/24/in-love.png pidgin/pixmaps/emotes/default/24/love.png pidgin/pixmaps/emotes/default/24/nailbiting.png pidgin/pixmaps/emotes/default/24/shock.png pidgin/pixmaps/emotes/default/24/smile-big.png pidgin/pixmaps/emotes/default/24/smile.png pidgin/pixmaps/emotes/default/24/sweat.png pidgin/pixmaps/emotes/default/24/tremble.png pidgin/pixmaps/emotes/default/24/yawn.png pidgin/pixmaps/emotes/small/16/love.png pidgin/pixmaps/emotes/small/16/shock.png pidgin/pixmaps/emotes/small/16/smile-big.png pidgin/pixmaps/emotes/small/16/smile.png pidgin/pixmaps/emotes/small/16/yawn.png |
diffstat | 228 files changed, 11629 insertions(+), 6356 deletions(-) [+] |
line wrap: on
line diff
--- a/.mtn-ignore Thu Feb 18 22:42:19 2010 +0000 +++ b/.mtn-ignore Tue Mar 09 21:41:41 2010 +0000 @@ -38,7 +38,10 @@ pidgin.spec$ pidgin-.*.tar.gz pidgin-.*.tar.bz2 +pidgin-*.*.*-dbgsym$ +pidgin-*.*.*-dbgsym.zip$ pidgin-*.*.*-win32bin$ +pidgin-*.*.*-win32-bin.zip$ pidgin/pidgin$ pidgin/pixmaps/emotes/default/24/theme pidgin/pixmaps/emotes/none/theme @@ -48,6 +51,9 @@ pidgin/plugins/perl/common/Makefile.old pidgin/win32/pidgin_dll_rc.rc$ pidgin/win32/pidgin_exe_rc.rc$ +pidgin/win32/nsis/gtk-runtime-*.*.*.*.zip +pidgin/win32/nsis/gtk_runtime_stage$ +pidgin/win32/nsis/pidgin-translations.nsh$ install-sh libpurple/dbus-bindings.c libpurple/dbus-signals.c
--- a/COPYRIGHT Thu Feb 18 22:42:19 2010 +0000 +++ b/COPYRIGHT Tue Mar 09 21:41:41 2010 +0000 @@ -228,6 +228,7 @@ Instant Messaging Freedom, Inc. Vitaliy Ischenko Intel Corporation +Andrew Ivanov Scott Jackson Hans Petter Jansson David Jedelsky @@ -237,7 +238,7 @@ Yuriy Kaminskiy Anders Kaseorg Praveen Karadakal -Jaromír Karmazín +Tomáš Kebert John Kelm Jochen Kemnade Yann Kerherve @@ -302,6 +303,7 @@ Peter McCurdy Kurt McKee Torrey McMahon +Greg McNew Robert McQueen Mihály Mészáros Robert Mibus @@ -541,6 +543,7 @@ Jared Yanovich Timmy Yee Li Yuan +Yuriy Yevgrafov Nickolai Zeldovich Tom Zickel Marco Ziech
--- a/ChangeLog Thu Feb 18 22:42:19 2010 +0000 +++ b/ChangeLog Tue Mar 09 21:41:41 2010 +0000 @@ -15,8 +15,27 @@ * Use GtkStatusIcon for the docklet, providing better integration in notification area. * Added UI for sending attentions (buzz, nudge) on supporting protocols. - * Make the search dialog unobtrusive in the conversation window (by making - it look and behave like the search dialog in Firefox) + * Make the search dialog unobtrusive in the conversation window (by + making it look and behave like the search dialog in Firefox) + * The Recent Log Activity sort method for the Buddy List now + distinguishes between no activity and a small amount of activity + in the distant past. (Greg McNew) + + Bonjour: + * Added support for IPv6. (Thanks to T_X for testing) + + Gadu-Gadu: + * Updated our bundled libgadu to 1.9.0-rc2 (many thanks to Krzysztof + Klinikowski for the work and testing put in here!) + + ICQ: + * X-Status (Custom ICQ status icon) support (Andrew Ivanov, Tomáš Kebert, + Yuriy Yevgrafov, and trac users bob007, salieff, and nops) + + XMPP: + * Direct messages to a specific resource only upon receipt of a message + with content (as opposed to a typing notification, etc). (Thanks to + rjoly for testing) version 2.6.6 (02/18/2010): libpurple:
--- a/ChangeLog.API Thu Feb 18 22:42:19 2010 +0000 +++ b/ChangeLog.API Tue Mar 09 21:41:41 2010 +0000 @@ -146,6 +146,7 @@ * xmlnode_from_file * xmlnode_get_parent * xmlnode_set_attrib_full + * PURPLE_STATUS_MOOD as a new PurpleStatusPrimitive Changed: * xmlnode_remove_attrib now removes all attributes with the
--- a/ChangeLog.win32 Thu Feb 18 22:42:19 2010 +0000 +++ b/ChangeLog.win32 Tue Mar 09 21:41:41 2010 +0000 @@ -4,6 +4,10 @@ * Win9x no longer supported. * Crash Report files (pidgin.RPT) are now generated in the ~/.purple directory instead of the installation directory. + * NSS SSL Library upgraded to 3.12.5 (thanks to Berke Viktor) + * GtkSpell upgraded to 2.0.16, changing the spellchecking backend to + enchant. This means that myspell, hunspell (OpenOffice) and existing + aspell dictionaries can be used. version 2.6.6 (02/18/2010): * Installer translations for: Norwegian nynorsk
--- a/Makefile.mingw Thu Feb 18 22:42:19 2010 +0000 +++ b/Makefile.mingw Tue Mar 09 21:41:41 2010 +0000 @@ -31,10 +31,7 @@ exit; \ }' VERSION) -GTK_INSTALL_VERSION = $(shell \ - source ../gtk_installer/version.sh; \ - echo $$gtk_version \ -) +GTK_INSTALL_VERSION = 2.14.7.0 STRIPPED_RELEASE_DIR = $(PIDGIN_TREE_TOP)/pidgin-$(PIDGIN_VERSION)-win32bin DEBUG_SYMBOLS_DIR = $(PIDGIN_TREE_TOP)/pidgin-$(PIDGIN_VERSION)-dbgsym @@ -44,19 +41,22 @@ # should be included in this list so they don't get stripped EXTERNAL_DLLS = \ comerr32.dll \ + exchndl.dll \ freebl3.dll \ gssapi32.dll \ k5sprt32.dll \ krb5_32.dll \ - libgtkspell.dll \ + libenchant.dll \ + libgtkspell-0.dll \ libmeanwhile-1.dll \ + libnspr4.dll \ + libplc4.dll \ + libplds4.dll \ libsasl.dll \ libxml2.dll \ - nspr4.dll \ nss3.dll \ nssckbi.dll \ - plc4.dll \ - plds4.dll \ + nssutil3.dll \ saslANONYMOUS.dll \ saslCRAMMD5.dll \ saslDIGESTMD5.dll \ @@ -67,12 +67,13 @@ libsilcclient-1-1-2.dll \ smime3.dll \ softokn3.dll \ + sqlite3.dll \ ssl3.dll #build an expression for `find` to use to ignore the above files EXTERNAL_DLLS_FIND_EXP = $(patsubst %,-o -name %,$(EXTERNAL_DLLS)) -.PHONY: all docs install installer installer_offline installer_zip debug_symbols_zip installers clean uninstall create_release_install_dir generate_translations_installer_include $(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) all: $(PIDGIN_CONFIG_H) $(PIDGIN_REVISION_H) $(MAKE) -C $(PURPLE_TOP) -f $(MINGW_MAKEFILE) @@ -89,26 +90,48 @@ endif $(MAKE) -C share/ca-certs -f $(MINGW_MAKEFILE) install $(MAKE) -C share/sounds -f $(MINGW_MAKEFILE) install + mkdir -p $(PIDGIN_INSTALL_DIR)/spellcheck + cp $(GTKSPELL_TOP)/bin/libgtkspell-0.dll $(PIDGIN_INSTALL_DIR)/spellcheck + cp $(ENCHANT_TOP)/bin/libenchant.dll $(PIDGIN_INSTALL_DIR)/spellcheck + cp -R $(ENCHANT_TOP)/lib $(PIDGIN_INSTALL_DIR)/spellcheck + cp $(WIN32_DEV_TOP)/pidgin-inst-deps-20100223/exchndl.dll $(PIDGIN_INSTALL_DIR) -generate_translations_installer_include: create_release_install_dir - rm -f pidgin/win32/nsis/pidgin-translations.nsh +pidgin/win32/nsis/gtk-runtime-$(GTK_BUNDLE_VERSION).zip: + pidgin/win32/nsis/generate_gtk_zip.sh `pwd` + +generate_installer_includes: create_release_install_dir pidgin/win32/nsis/gtk-runtime-$(GTK_BUNDLE_VERSION).zip debug_symbols_zip + rm -f pidgin/win32/nsis/pidgin-translations.nsh pidgin/win32/nsis/pidgin-spellcheck.nsh pidgin/win32/nsis/pidgin-spellcheck-preselect.nsh find $(STRIPPED_RELEASE_DIR)/locale -maxdepth 1 -mindepth 1 \ -exec basename {} ';' \ | sed -e s/^/\!insertmacro\ LANG_SECTION\ \"/ -e s/$$/\"/ \ > pidgin/win32/nsis/pidgin-translations.nsh + #Convert the available.lst lines to "!insertmacro SPELLCHECK_SECTION lang lang_name lang_file" + sed -e "/^#/d" -e "s/^[^,]\{1,\},[^,]\{1,\},/\"/" \ + -e "s/,/\"\ \"/" -e "s/,/\"\ \"/" -e "s/[\ \t]*$$/\"/" \ + -e "s/^/\!insertmacro\ SPELLCHECK_SECTION\ /" \ + pidgin/win32/nsis/available.lst \ + > pidgin/win32/nsis/pidgin-spellcheck.nsh + #Convert the lines to "!insertmacro CHECK_SPELLCHECK_SECTION lang" + iconv -f latin1 -t utf-8 pidgin/win32/nsis/pidgin-spellcheck.nsh | \ + sed -e "s/SPELLCHECK_SECTION/CHECK_SPELLCHECK_SECTION/" \ + -e "s/ \"[^\"]*\"\ \"[^\"]*\"[\t\ ]*$$//" | \ + iconv -f utf-8 -t latin1 \ + > pidgin/win32/nsis/pidgin-spellcheck-preselect.nsh create_release_install_dir: install rm -rf $(STRIPPED_RELEASE_DIR) - cp -R $(PIDGIN_INSTALL_DIR) $(STRIPPED_RELEASE_DIR) + mkdir $(STRIPPED_RELEASE_DIR) + tar -cf - $(PIDGIN_INSTALL_DIR) --exclude=Gtk --exclude=spellcheck/share \ + | tar --strip 2 -xC $(STRIPPED_RELEASE_DIR) -f - find $(STRIPPED_RELEASE_DIR) \( -name '*.dll' -o -name '*.exe' \) \ -not \( -false $(EXTERNAL_DLLS_FIND_EXP) \) \ -exec $(STRIP) --strip-unneeded {} ';' -installer: create_release_install_dir generate_translations_installer_include +installer: generate_installer_includes $(MAKENSIS) $(MAKENSISOPT)V3 $(MAKENSISOPT)DPIDGIN_VERSION="$(PIDGIN_VERSION)" $(MAKENSISOPT)DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" $(MAKENSISOPT)DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" $(MAKENSISOPT)DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION).exe ./ -installer_offline: create_release_install_dir generate_translations_installer_include debug_symbols_zip +installer_offline: generate_installer_includes $(MAKENSIS) $(MAKENSISOPT)V3 $(MAKENSISOPT)DPIDGIN_VERSION="$(PIDGIN_VERSION)" $(MAKENSISOPT)DPIDGIN_PRODUCT_VERSION="$(PIDGIN_PRODUCT_VERSION)" $(MAKENSISOPT)DOFFLINE_INSTALLER $(MAKENSISOPT)DPIDGIN_INSTALL_DIR="$(STRIPPED_RELEASE_DIR)" $(MAKENSISOPT)DGTK_INSTALL_VERSION="$(GTK_INSTALL_VERSION)" pidgin/win32/nsis/pidgin-installer.nsi mv pidgin/win32/nsis/pidgin-$(PIDGIN_VERSION)-offline.exe ./
--- a/configure.ac Thu Feb 18 22:42:19 2010 +0000 +++ b/configure.ac Tue Mar 09 21:41:41 2010 +0000 @@ -2394,6 +2394,30 @@ AC_DEFINE(HAVE_TM_GMTOFF, 1, [Define if you have a tm_gmtoff member in struct tm]) fi +AC_CACHE_CHECK([whether va_lists can be copied by value], ac_cv_va_val_copy,[ + AC_TRY_RUN([#include <stdarg.h> +#include <stdlib.h> + void f (int i, ...) { + va_list args1, args2; + va_start (args1, i); + args2 = args1; + if (va_arg (args2, int) != 42 || va_arg (args1, int) != 42) + exit (1); + va_end (args1); va_end (args2); + } + int main() { + f (0, 42); + return 0; + }], + [ac_cv_va_val_copy=yes], + [ac_cv_va_val_copy=no], + [ac_cv_va_val_copy=yes]) +]) + +if test "x$ac_cv_va_val_copy" = "xno"; then + AC_DEFINE(VA_COPY_AS_ARRAY, 1, ['va_lists' cannot be copied as values]) +fi + dnl ####################################################################### dnl # Check for check dnl ####################################################################### @@ -2583,6 +2607,7 @@ echo Build with Cyrus SASL support. : $enable_cyrus_sasl echo Use kerberos 4 with zephyr.... : $kerberos echo Use external libzephyr........ : $zephyr +echo Use external libgadu.......... : $gadu_libs echo Install pixmaps............... : $enable_pixmaps echo Install translations.......... : $enable_i18n echo Has you....................... : yes
--- a/finch/gntplugin.c Thu Feb 18 22:42:19 2010 +0000 +++ b/finch/gntplugin.c Tue Mar 09 21:41:41 2010 +0000 @@ -498,7 +498,7 @@ break; } stringlist = g_list_prepend(stringlist, value); - purple_request_field_list_add(field, label, value); + purple_request_field_list_add_icon(field, label, NULL, value); if (strcmp(value, current_value) == 0) purple_request_field_list_add_selected(field, label); list = list->next->next;
--- a/finch/gntprefs.c Thu Feb 18 22:42:19 2010 +0000 +++ b/finch/gntprefs.c Tue Mar 09 21:41:41 2010 +0000 @@ -171,7 +171,7 @@ default: break; } - purple_request_field_list_add(field, data, iter->data); + purple_request_field_list_add_icon(field, data, NULL, iter->data); if (select) purple_request_field_list_add_selected(field, data); }
--- a/finch/plugins/gnthistory.c Thu Feb 18 22:42:19 2010 +0000 +++ b/finch/plugins/gnthistory.c Tue Mar 09 21:41:41 2010 +0000 @@ -158,7 +158,7 @@ while (list) { const char *label = _(list->data); list = g_list_delete_link(list, list); - purple_request_field_list_add(field, label, list->data); + purple_request_field_list_add_icon(field, label, NULL, list->data); if (system && strcmp(system, list->data) == 0) purple_request_field_list_add_selected(field, label); list = g_list_delete_link(list, list);
--- a/libpurple/Makefile.mingw Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/Makefile.mingw Tue Mar 09 21:41:41 2010 +0000 @@ -8,7 +8,7 @@ include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak TARGET = libpurple -NEEDED_DLLS = $(LIBXML2_TOP)/bin/libxml2.dll +NEEDED_DLLS = $(LIBXML2_TOP)/bin/libxml2-2.dll ## ## INCLUDE PATHS @@ -20,7 +20,7 @@ -I$(GTK_TOP)/include \ -I$(GTK_TOP)/include/glib-2.0 \ -I$(GTK_TOP)/lib/glib-2.0/include \ - -I$(LIBXML2_TOP)/include + -I$(LIBXML2_TOP)/include/libxml2 LIB_PATHS += -L$(GTK_TOP)/lib \ -L$(LIBXML2_TOP)/lib
--- a/libpurple/connection.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/connection.h Tue Mar 09 21:41:41 2010 +0000 @@ -44,8 +44,9 @@ PURPLE_CONNECTION_NO_FONTSIZE = 0x0020, /**< Connection does not send/receive font sizes */ PURPLE_CONNECTION_NO_URLDESC = 0x0040, /**< Connection does not support descriptions with links */ PURPLE_CONNECTION_NO_IMAGES = 0x0080, /**< Connection does not support sending of images */ - PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY = 0x0100 /**< Connection supports sending and receiving custom smileys */ - + PURPLE_CONNECTION_ALLOW_CUSTOM_SMILEY = 0x0100, /**< Connection supports sending and receiving custom smileys */ + PURPLE_CONNECTION_SUPPORT_MOODS = 0x0200, /**< Connection supports setting moods */ + PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES = 0x0400 /**< Connection supports setting a message on moods */ } PurpleConnectionFlags; typedef enum
--- a/libpurple/example/nullclient.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/example/nullclient.c Tue Mar 09 21:41:41 2010 +0000 @@ -27,7 +27,11 @@ #include <signal.h> #include <string.h> +#ifndef _WIN32 #include <unistd.h> +#else +#include "win32/win32dep.h" +#endif #include "defines.h" @@ -80,7 +84,11 @@ if (condition & PURPLE_INPUT_WRITE) cond |= PURPLE_GLIB_WRITE_COND; +#if defined _WIN32 && !defined WINPIDGIN_USE_GLIB_IO_CHANNEL + channel = wpurple_g_io_channel_win32_new_socket(fd); +#else channel = g_io_channel_unix_new(fd); +#endif closure->result = g_io_add_watch_full(channel, G_PRIORITY_DEFAULT, cond, purple_glib_io_invoke, closure, purple_glib_io_destroy); @@ -253,12 +261,14 @@ PurpleSavedStatus *status; char *res; +#ifndef _WIN32 /* libpurple's built-in DNS resolution forks processes to perform * blocking lookups without blocking the main process. It does not * handle SIGCHLD itself, so if the UI does not you quickly get an army * of zombie subprocesses marching around. */ signal(SIGCHLD, SIG_IGN); +#endif init_libpurple();
--- a/libpurple/ft.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/ft.c Tue Mar 09 21:41:41 2010 +0000 @@ -579,7 +579,7 @@ type = purple_xfer_get_type(xfer); account = purple_xfer_get_account(xfer); - purple_debug_misc("xfer", "request accepted for %p\n", xfer); + purple_debug_misc("xfer", "request accepted for %p\n", xfer); if (!filename && type == PURPLE_XFER_RECEIVE) { xfer->status = PURPLE_XFER_STATUS_ACCEPTED; @@ -1085,9 +1085,10 @@ return; } } else if (xfer->type == PURPLE_XFER_SEND) { - size_t result; + size_t result = 0; size_t s = MIN(purple_xfer_get_bytes_remaining(xfer), xfer->current_buffer_size); PurpleXferPrivData *priv = g_hash_table_lookup(xfers_data, xfer); + gboolean read = TRUE; /* this is so the prpl can keep the connection open if it needs to for some odd reason. */ @@ -1099,54 +1100,59 @@ return; } - if (ui_ops && ui_ops->ui_read) { - gssize tmp = ui_ops->ui_read(xfer, &buffer, s); - if (tmp == 0) { - /* - * The UI claimed it was ready, but didn't have any data for - * us... It will call purple_xfer_ui_ready when ready, which - * sets back up this watcher. - */ - if (xfer->watcher != 0) { - purple_input_remove(xfer->watcher); - xfer->watcher = 0; + if (priv->buffer) { + if (priv->buffer->len < s) { + s -= priv->buffer->len; + read = TRUE; + } else { + read = FALSE; + } + } + + if (read) { + if (ui_ops && ui_ops->ui_read) { + gssize tmp = ui_ops->ui_read(xfer, &buffer, s); + if (tmp == 0) { + /* + * The UI claimed it was ready, but didn't have any data for + * us... It will call purple_xfer_ui_ready when ready, which + * sets back up this watcher. + */ + if (xfer->watcher != 0) { + purple_input_remove(xfer->watcher); + xfer->watcher = 0; + } + + /* Need to indicate the prpl is still ready... */ + priv->ready |= PURPLE_XFER_READY_PRPL; + + g_return_if_reached(); + } else if (tmp < 0) { + purple_debug_error("filetransfer", "Unable to read whole buffer.\n"); + purple_xfer_cancel_local(xfer); + return; } - /* Need to indicate the prpl is still ready... */ - priv->ready |= PURPLE_XFER_READY_PRPL; - - /* - * if we requested 0 bytes it's only normal that end up here - * we shouldn't return as we still have something to - * write in priv->buffer - */ - if (s != 0) - g_return_if_reached(); - } else if (tmp < 0) { - purple_debug_error("filetransfer", "Unable to read whole buffer.\n"); - purple_xfer_cancel_local(xfer); - return; - } - - result = tmp; - } else { - buffer = g_malloc0(s); - result = fread(buffer, 1, s, xfer->dest_fp); - if (result != s) { - purple_debug_error("filetransfer", "Unable to read whole buffer.\n"); - purple_xfer_cancel_local(xfer); - g_free(buffer); - return; + result = tmp; + } else { + buffer = g_malloc(s); + result = fread(buffer, 1, s, xfer->dest_fp); + if (result != s) { + purple_debug_error("filetransfer", "Unable to read whole buffer.\n"); + purple_xfer_cancel_local(xfer); + g_free(buffer); + return; + } } } - + if (priv->buffer) { priv->buffer = g_byte_array_append(priv->buffer, buffer, result); g_free(buffer); buffer = priv->buffer->data; result = priv->buffer->len; } - + r = purple_xfer_write(xfer, buffer, result); if (r == -1) { @@ -1161,12 +1167,12 @@ */ purple_xfer_increase_buffer_size(xfer); } else { - if (ui_ops && ui_ops->data_not_sent) - ui_ops->data_not_sent(xfer, buffer + r, result -r); + if (ui_ops && ui_ops->data_not_sent) + ui_ops->data_not_sent(xfer, buffer + r, result - r); } if (priv->buffer) { - /* + /* * Remove what we wrote * If we wrote the whole buffer the byte array will be empty * Otherwise we'll kee what wasn't sent for next time.
--- a/libpurple/log.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/log.c Tue Mar 09 21:41:41 2010 +0000 @@ -302,7 +302,7 @@ } } - score = (gint)score_double; + score = (gint) ceil(score_double); g_hash_table_replace(logsize_users_decayed, lu, GINT_TO_POINTER(score)); } return score;
--- a/libpurple/plugins/log_reader.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/plugins/log_reader.c Tue Mar 09 21:41:41 2010 +0000 @@ -2644,7 +2644,7 @@ g_free(contents); } g_free(path); -#endif /* !GTK_CHECK_VERSION(2,6,0) */ +#endif /* !GLIB_CHECK_VERSION(2,6,0) */ } /* path */ if (!found) {
--- a/libpurple/plugins/perl/common/Request.xs Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/plugins/perl/common/Request.xs Tue Mar 09 21:41:41 2010 +0000 @@ -380,6 +380,13 @@ void * data void +purple_request_field_list_add_icon(field, item, icon_path, data) + Purple::Request::Field field + const char *item + const char *icon_path + void * data + +void purple_request_field_list_add_selected(field, item) Purple::Request::Field field const char *item
--- a/libpurple/plugins/perl/common/module.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/plugins/perl/common/module.h Tue Mar 09 21:41:41 2010 +0000 @@ -9,6 +9,7 @@ #include <glib.h> #ifdef _WIN32 #undef pipe +#undef STRINGIFY #endif #include <EXTERN.h> #include <perl.h>
--- a/libpurple/plugins/perl/perl-common.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/plugins/perl/perl-common.c Tue Mar 09 21:41:41 2010 +0000 @@ -472,74 +472,74 @@ } SV * -purple_perl_sv_from_vargs(const PurpleValue *value, va_list args, void ***copy_arg) +purple_perl_sv_from_vargs(const PurpleValue *value, va_list *args, void ***copy_arg) { if (purple_value_is_outgoing(value)) { switch (purple_value_get_type(value)) { case PURPLE_TYPE_SUBTYPE: - if ((*copy_arg = va_arg(args, void **)) == NULL) + if ((*copy_arg = va_arg(*args, void **)) == NULL) return &PL_sv_undef; return purple_perl_sv_from_subtype(value, *(void **)*copy_arg); case PURPLE_TYPE_BOOLEAN: - if ((*copy_arg = (void *)va_arg(args, gboolean *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, gboolean *)) == NULL) return &PL_sv_undef; return newSViv(*(gboolean *)*copy_arg); case PURPLE_TYPE_INT: - if ((*copy_arg = (void *)va_arg(args, int *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, int *)) == NULL) return &PL_sv_undef; return newSViv(*(int *)*copy_arg); case PURPLE_TYPE_UINT: - if ((*copy_arg = (void *)va_arg(args, unsigned int *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, unsigned int *)) == NULL) return &PL_sv_undef; return newSVuv(*(unsigned int *)*copy_arg); case PURPLE_TYPE_LONG: - if ((*copy_arg = (void *)va_arg(args, long *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, long *)) == NULL) return &PL_sv_undef; return newSViv(*(long *)*copy_arg); case PURPLE_TYPE_ULONG: - if ((*copy_arg = (void *)va_arg(args, + if ((*copy_arg = (void *)va_arg(*args, unsigned long *)) == NULL) return &PL_sv_undef; return newSVuv(*(unsigned long *)*copy_arg); case PURPLE_TYPE_INT64: - if ((*copy_arg = (void *)va_arg(args, gint64 *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, gint64 *)) == NULL) return &PL_sv_undef; return newSViv(*(gint64 *)*copy_arg); case PURPLE_TYPE_UINT64: - if ((*copy_arg = (void *)va_arg(args, guint64 *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, guint64 *)) == NULL) return &PL_sv_undef; return newSVuv(*(guint64 *)*copy_arg); case PURPLE_TYPE_STRING: - if ((*copy_arg = (void *)va_arg(args, char **)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, char **)) == NULL) return &PL_sv_undef; return newSVGChar(*(char **)*copy_arg); case PURPLE_TYPE_POINTER: - if ((*copy_arg = va_arg(args, void **)) == NULL) + if ((*copy_arg = va_arg(*args, void **)) == NULL) return &PL_sv_undef; return newSViv((IV)*(void **)*copy_arg); case PURPLE_TYPE_BOXED: /* Uh.. I dunno. Try this? */ - if ((*copy_arg = va_arg(args, void **)) == NULL) + if ((*copy_arg = va_arg(*args, void **)) == NULL) return &PL_sv_undef; return sv_2mortal(purple_perl_bless_object( @@ -553,40 +553,40 @@ } else { switch (purple_value_get_type(value)) { case PURPLE_TYPE_SUBTYPE: - if ((*copy_arg = va_arg(args, void *)) == NULL) + if ((*copy_arg = va_arg(*args, void *)) == NULL) return &PL_sv_undef; return purple_perl_sv_from_subtype(value, *copy_arg); case PURPLE_TYPE_BOOLEAN: - *copy_arg = GINT_TO_POINTER( va_arg(args, gboolean) ); + *copy_arg = GINT_TO_POINTER( va_arg(*args, gboolean) ); return newSViv((gboolean)GPOINTER_TO_INT(*copy_arg)); case PURPLE_TYPE_INT: - *copy_arg = GINT_TO_POINTER( va_arg(args, int) ); + *copy_arg = GINT_TO_POINTER( va_arg(*args, int) ); return newSViv(GPOINTER_TO_INT(*copy_arg)); case PURPLE_TYPE_UINT: - *copy_arg = GUINT_TO_POINTER(va_arg(args, unsigned int)); + *copy_arg = GUINT_TO_POINTER(va_arg(*args, unsigned int)); return newSVuv(GPOINTER_TO_UINT(*copy_arg)); case PURPLE_TYPE_LONG: - *copy_arg = (void *)va_arg(args, long); + *copy_arg = (void *)va_arg(*args, long); return newSViv((long)*copy_arg); case PURPLE_TYPE_ULONG: - *copy_arg = (void *)va_arg(args, unsigned long); + *copy_arg = (void *)va_arg(*args, unsigned long); return newSVuv((unsigned long)*copy_arg); case PURPLE_TYPE_INT64: #if 0 /* XXX This yells and complains. */ - *copy_arg = va_arg(args, gint64); + *copy_arg = va_arg(*args, gint64); return newSViv(*copy_arg); #endif @@ -595,27 +595,27 @@ case PURPLE_TYPE_UINT64: /* XXX This also yells and complains. */ #if 0 - *copy_arg = (void *)va_arg(args, guint64); + *copy_arg = (void *)va_arg(*args, guint64); return newSVuv(*copy_arg); #endif break; case PURPLE_TYPE_STRING: - if ((*copy_arg = (void *)va_arg(args, char *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, char *)) == NULL) return &PL_sv_undef; return newSVGChar((char *)*copy_arg); case PURPLE_TYPE_POINTER: - if ((*copy_arg = (void *)va_arg(args, void *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL) return &PL_sv_undef; return newSViv((IV)*copy_arg); case PURPLE_TYPE_BOXED: /* Uh.. I dunno. Try this? */ - if ((*copy_arg = (void *)va_arg(args, void *)) == NULL) + if ((*copy_arg = (void *)va_arg(*args, void *)) == NULL) return &PL_sv_undef; return sv_2mortal(purple_perl_bless_object(*copy_arg,
--- a/libpurple/plugins/perl/perl-common.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/plugins/perl/perl-common.h Tue Mar 09 21:41:41 2010 +0000 @@ -3,6 +3,7 @@ #include <glib.h> #ifdef _WIN32 +#undef STRINGIFY #undef pipe #endif #include <EXTERN.h> @@ -65,7 +66,7 @@ #endif void *purple_perl_data_from_sv(PurpleValue *value, SV *sv); -SV *purple_perl_sv_from_vargs(const PurpleValue *value, va_list args, +SV *purple_perl_sv_from_vargs(const PurpleValue *value, va_list *args, void ***copy_arg); SV *purple_perl_sv_from_fun(PurplePlugin *plugin, SV *callback); #endif /* _PURPLE_PERL_COMMON_H_ */
--- a/libpurple/plugins/perl/perl-handlers.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/plugins/perl/perl-handlers.c Tue Mar 09 21:41:41 2010 +0000 @@ -298,7 +298,11 @@ for (i = 0; i < value_count; i++) { sv_args[i] = purple_perl_sv_from_vargs(values[i], - args, +#ifdef VA_COPY_AS_ARRAY + (va_list*)args, +#else + (va_list*)&args, +#endif ©_args[i]); XPUSHs(sv_args[i]);
--- a/libpurple/plugins/ssl/Makefile.mingw Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/plugins/ssl/Makefile.mingw Tue Mar 09 21:41:41 2010 +0000 @@ -15,14 +15,16 @@ NEEDED_DLLS = \ $(NSS_TOP)/lib/freebl3.dll \ + $(NSS_TOP)/lib/libnspr4.dll \ + $(NSS_TOP)/lib/libplc4.dll \ + $(NSS_TOP)/lib/libplds4.dll \ $(NSS_TOP)/lib/nss3.dll \ $(NSS_TOP)/lib/nssckbi.dll \ - $(NSS_TOP)/lib/softokn3.dll \ + $(NSS_TOP)/lib/nssutil3.dll \ $(NSS_TOP)/lib/smime3.dll \ - $(NSS_TOP)/lib/ssl3.dll \ - $(NSPR_TOP)/lib/nspr4.dll \ - $(NSPR_TOP)/lib/plc4.dll \ - $(NSPR_TOP)/lib/plds4.dll + $(NSS_TOP)/lib/softokn3.dll \ + $(NSS_TOP)/lib/sqlite3.dll \ + $(NSS_TOP)/lib/ssl3.dll ## ## INCLUDE PATHS @@ -34,13 +36,11 @@ -I$(PURPLE_TOP) \ -I$(PURPLE_TOP)/win32 \ -I$(PIDGIN_TREE_TOP) \ - -I$(NSS_TOP)/include \ - -I$(NSPR_TOP)/include + -I$(NSS_TOP)/include LIB_PATHS += -L$(GTK_TOP)/lib \ -L$(PURPLE_TOP) \ - -L$(NSS_TOP)/lib \ - -L$(NSPR_TOP)/lib + -L$(NSS_TOP)/lib ## ## SOURCES, OBJECTS
--- a/libpurple/protocols/bonjour/Makefile.mingw Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/bonjour/Makefile.mingw Tue Mar 09 21:41:41 2010 +0000 @@ -30,7 +30,7 @@ -I$(GTK_TOP)/include/glib-2.0 \ -I$(GTK_TOP)/lib/glib-2.0/include \ -I$(BONJOUR_TOP)/Include \ - -I$(LIBXML2_TOP)/include \ + -I$(LIBXML2_TOP)/include/libxml2 \ -I$(PURPLE_TOP) \ -I$(PURPLE_TOP)/win32 \ -I$(PIDGIN_TREE_TOP)
--- a/libpurple/protocols/bonjour/bonjour.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/bonjour/bonjour.c Tue Mar 09 21:41:41 2010 +0000 @@ -101,6 +101,8 @@ /* Start waiting for jabber connections (iChat style) */ bd->jabber_data = g_new0(BonjourJabber, 1); + bd->jabber_data->socket = -1; + bd->jabber_data->socket6 = -1; bd->jabber_data->port = purple_account_get_int(account, "port", BONJOUR_DEFAULT_PORT); bd->jabber_data->account = account; @@ -525,7 +527,8 @@ sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL, /* get_account_text_table */ NULL, /* initiate_media */ - NULL /* can_do_media */ + NULL, /* get_media_caps */ + NULL /* get_moods */ }; static PurplePluginInfo info =
--- a/libpurple/protocols/bonjour/bonjour_ft.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/bonjour/bonjour_ft.c Tue Mar 09 21:41:41 2010 +0000 @@ -747,8 +747,7 @@ XepIq *iq; xmlnode *query, *streamhost; gchar *port; - const char *next_ip, *local_ip; - const char token [] = ";"; + GSList *local_ips; BonjourData *bd; purple_debug_info("bonjour", "Bonjour-bytestreams-listen. sock=%d.\n", sock); @@ -773,17 +772,16 @@ xfer->local_port = purple_network_get_port_from_fd(sock); - local_ip = purple_network_get_my_ip_ext2(sock); - /* cheat a little here - the intent of the "const" attribute is to make it clear that the string doesn't need to be freed */ - next_ip = strtok((char *)local_ip, token); + local_ips = bonjour_jabber_get_local_ips(sock); port = g_strdup_printf("%hu", xfer->local_port); - while(next_ip != NULL) { + while(local_ips) { streamhost = xmlnode_new_child(query, "streamhost"); xmlnode_set_attrib(streamhost, "jid", xf->sid); - xmlnode_set_attrib(streamhost, "host", next_ip); + xmlnode_set_attrib(streamhost, "host", local_ips->data); xmlnode_set_attrib(streamhost, "port", port); - next_ip = strtok(NULL, token); + g_free(local_ips->data); + local_ips = g_slist_delete_link(local_ips, local_ips); } g_free(port); @@ -796,15 +794,17 @@ XepXfer *xf; if(xfer == NULL) return; + purple_debug_info("bonjour", "Bonjour-bytestreams-init.\n"); xf = xfer->data; + purple_network_listen_map_external(FALSE); xf->listen_data = purple_network_listen_range(0, 0, SOCK_STREAM, bonjour_bytestreams_listen, xfer); purple_network_listen_map_external(TRUE); - if (xf->listen_data == NULL) { + if (xf->listen_data == NULL) purple_xfer_cancel_local(xfer); - } + return; }
--- a/libpurple/protocols/bonjour/jabber.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/bonjour/jabber.c Tue Mar 09 21:41:41 2010 +0000 @@ -44,6 +44,11 @@ #endif #include <fcntl.h> +#ifdef HAVE_GETIFADDRS +#include <ifaddrs.h> +#endif + + #include "network.h" #include "eventloop.h" #include "connection.h" @@ -623,15 +628,22 @@ } +#ifndef INET6_ADDRSTRLEN +#define INET6_ADDRSTRLEN 46 +#endif + static void _server_socket_handler(gpointer data, int server_socket, PurpleInputCondition condition) { BonjourJabber *jdata = data; - struct sockaddr_in their_addr; /* connector's address information */ - socklen_t sin_size = sizeof(struct sockaddr); + struct sockaddr_storage their_addr; /* connector's address information */ + socklen_t sin_size = sizeof(struct sockaddr_storage); int client_socket; int flags; - char *address_text = NULL; +#ifdef HAVE_INET_NTOP + char addrstr[INET6_ADDRSTRLEN]; +#endif + const char *address_text; struct _match_buddies_by_address_t *mbba; BonjourJabberConversation *bconv; GSList *buddies; @@ -640,7 +652,9 @@ if (condition != PURPLE_INPUT_READ) return; - if ((client_socket = accept(server_socket, (struct sockaddr *)&their_addr, &sin_size)) == -1) + memset(&their_addr, 0, sin_size); + + if ((client_socket = accept(server_socket, (struct sockaddr*)&their_addr, &sin_size)) == -1) return; flags = fcntl(client_socket, F_GETFL); @@ -650,7 +664,16 @@ #endif /* Look for the buddy that has opened the conversation and fill information */ - address_text = inet_ntoa(their_addr.sin_addr); +#ifdef HAVE_INET_NTOP + if (their_addr.ss_family == AF_INET6) + address_text = inet_ntop(their_addr.ss_family, &((struct sockaddr_in6 *)&their_addr)->sin6_addr, + addrstr, sizeof(addrstr)); + else + address_text = inet_ntop(their_addr.ss_family, &((struct sockaddr_in *)&their_addr)->sin_addr, + addrstr, sizeof(addrstr)); +#else + address_text = inet_ntoa(((struct sockaddr_in *)&their_addr)->sin_addr); +#endif purple_debug_info("bonjour", "Received incoming connection from %s.\n", address_text); mbba = g_new0(struct _match_buddies_by_address_t, 1); mbba->address = address_text; @@ -680,52 +703,42 @@ } -gint -bonjour_jabber_start(BonjourJabber *jdata) +static int +start_serversocket_listening(int port, int socket, struct sockaddr *addr, size_t addr_size, gboolean ip6, gboolean allow_port_fallback) { - struct sockaddr_in my_addr; + int ret_port = port; - /* Open a listening socket for incoming conversations */ - jdata->socket = socket(PF_INET, SOCK_STREAM, 0); - if (jdata->socket < 0) { - gchar *buf = g_strdup_printf(_("Unable to create socket: %s"), - g_strerror(errno)); - purple_connection_error_reason(jdata->account->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf); - g_free(buf); - return -1; - } - - memset(&my_addr, 0, sizeof(struct sockaddr_in)); - my_addr.sin_family = AF_INET; + purple_debug_info("bonjour", "Attempting to bind IPv%d socket to port %d.\n", ip6 ? 6 : 4, port); /* Try to use the specified port - if it isn't available, use a random port */ - my_addr.sin_port = htons(jdata->port); - if (bind(jdata->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0) - { + if (bind(socket, addr, addr_size) != 0) { + purple_debug_info("bonjour", "Unable to bind to specified " - "port %i: %s\n", jdata->port, g_strerror(errno)); - my_addr.sin_port = 0; - if (bind(jdata->socket, (struct sockaddr*)&my_addr, sizeof(struct sockaddr)) != 0) - { - gchar *buf = g_strdup_printf(_("Unable to bind socket " - "to port: %s"), g_strerror(errno)); - purple_connection_error_reason(jdata->account->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf); - g_free(buf); + "port %i: %s\n", port, g_strerror(errno)); + + if (!allow_port_fallback) { + purple_debug_warning("bonjour", "Not attempting random port assignment.\n"); return -1; } - jdata->port = purple_network_get_port_from_fd(jdata->socket); +#ifdef PF_INET6 + if (ip6) + ((struct sockaddr_in6 *) addr)->sin6_port = 0; + else +#endif + ((struct sockaddr_in *) addr)->sin_port = 0; + + if (bind(socket, addr, addr_size) != 0) { + purple_debug_error("bonjour", "Unable to bind IPv%d socket to port: %s\n", ip6 ? 6 : 4, g_strerror(errno)); + return -1; + } + ret_port = purple_network_get_port_from_fd(socket); } + purple_debug_info("bonjour", "Bound IPv%d socket to port %d.\n", ip6 ? 6 : 4, ret_port); + /* Attempt to listen on the bound socket */ - if (listen(jdata->socket, 10) != 0) - { - gchar *buf = g_strdup_printf(_("Unable to listen on socket: %s"), - g_strerror(errno)); - purple_connection_error_reason(jdata->account->gc, - PURPLE_CONNECTION_ERROR_NETWORK_ERROR, buf); - g_free(buf); + if (listen(socket, 10) != 0) { + purple_debug_error("bonjour", "Unable to listen on IPv%d socket: %s\n", ip6 ? 6 : 4, g_strerror(errno)); return -1; } @@ -739,8 +752,66 @@ } #endif - /* Open a watcher in the socket we have just opened */ - jdata->watcher_id = purple_input_add(jdata->socket, PURPLE_INPUT_READ, _server_socket_handler, jdata); + return ret_port; +} + +gint +bonjour_jabber_start(BonjourJabber *jdata) +{ + int ipv6_port = -1, ipv4_port = -1; + + /* Open a listening socket for incoming conversations */ +#ifdef PF_INET6 + jdata->socket6 = socket(PF_INET6, SOCK_STREAM, 0); +#endif + jdata->socket = socket(PF_INET, SOCK_STREAM, 0); + if (jdata->socket == -1 && jdata->socket6 == -1) { + purple_debug_error("bonjour", "Unable to create socket: %s", + g_strerror(errno)); + return -1; + } + +#ifdef PF_INET6 + if (jdata->socket6 != -1) { + struct sockaddr_in6 addr6; + memset(&addr6, 0, sizeof(addr6)); + addr6.sin6_family = AF_INET6; + addr6.sin6_port = htons(jdata->port); + addr6.sin6_addr = in6addr_any; + ipv6_port = start_serversocket_listening(jdata->port, jdata->socket6, (struct sockaddr *) &addr6, sizeof(addr6), TRUE, TRUE); + /* Open a watcher in the socket we have just opened */ + if (ipv6_port > 0) { + jdata->watcher_id6 = purple_input_add(jdata->socket6, PURPLE_INPUT_READ, _server_socket_handler, jdata); + jdata->port = ipv6_port; + } else { + purple_debug_error("bonjour", "Failed to start listening on IPv6 socket.\n"); + close(jdata->socket6); + jdata->socket6 = -1; + } + } +#endif + if (jdata->socket != -1) { + struct sockaddr_in addr4; + memset(&addr4, 0, sizeof(addr4)); + addr4.sin_family = AF_INET; + addr4.sin_port = htons(jdata->port); + ipv4_port = start_serversocket_listening(jdata->port, jdata->socket, (struct sockaddr *) &addr4, sizeof(addr4), FALSE, ipv6_port != -1); + /* Open a watcher in the socket we have just opened */ + if (ipv4_port > 0) { + jdata->watcher_id = purple_input_add(jdata->socket, PURPLE_INPUT_READ, _server_socket_handler, jdata); + jdata->port = ipv4_port; + } else { + purple_debug_error("bonjour", "Failed to start listening on IPv4 socket.\n"); + close(jdata->socket); + jdata->socket = -1; + } + } + + if (!(ipv6_port > 0 || ipv4_port > 0)) { + purple_debug_error("bonjour", "Unable to listen on socket: %s", + g_strerror(errno)); + return -1; + } return jdata->port; } @@ -1101,6 +1172,10 @@ close(jdata->socket); if (jdata->watcher_id > 0) purple_input_remove(jdata->watcher_id); + if (jdata->socket6 >= 0) + close(jdata->socket6); + if (jdata->watcher_id6 > 0) + purple_input_remove(jdata->watcher_id6); /* Close all the conversation sockets and remove all the watchers after sending end streams */ if (jdata->account->gc != NULL) { @@ -1234,58 +1309,97 @@ return (ret >= 0) ? 0 : -1; } -/* This returns a ';' delimited string containing all non-localhost IPs */ -const char * -purple_network_get_my_ip_ext2(int fd) +/* This returns a list containing all non-localhost IPs */ +GSList * +bonjour_jabber_get_local_ips(int fd) { - char buffer[1024]; - static char ip_ext[17 * 10]; + GSList *ips = NULL; + const char *address_text; + int ret; + +#ifdef HAVE_GETIFADDRS /* This is required for IPv6 */ + { + struct ifaddrs *ifap, *ifa; + struct sockaddr *addr; + char addrstr[INET6_ADDRSTRLEN]; + + ret = getifaddrs(&ifap); + if (ret != 0) { + const char *error = g_strerror(errno); + purple_debug_error("bonjour", "getifaddrs() error: %s\n", error ? error : "(null)"); + return NULL; + } + + for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { + if (!(ifa->ifa_flags & IFF_RUNNING) || (ifa->ifa_flags & IFF_LOOPBACK) || ifa->ifa_addr == NULL) + continue; + + addr = ifa->ifa_addr; + address_text = NULL; + switch (addr->sa_family) { + case AF_INET: + address_text = inet_ntop(addr->sa_family, &((struct sockaddr_in *)addr)->sin_addr, + addrstr, sizeof(addrstr)); + break; +#ifdef PF_INET6 + case AF_INET6: + address_text = inet_ntop(addr->sa_family, &((struct sockaddr_in6 *)addr)->sin6_addr, + addrstr, sizeof(addrstr)); + break; +#endif + } + + if (address_text != NULL) { + if (addr->sa_family == AF_INET) + ips = g_slist_append(ips, g_strdup(address_text)); + else + ips = g_slist_prepend(ips, g_strdup(address_text)); + } + } + + freeifaddrs(ifap); + + } +#else + { char *tmp; - char *tip; struct ifconf ifc; struct ifreq *ifr; + char buffer[1024]; struct sockaddr_in *sinptr; - guint32 lhost = htonl(127 * 256 * 256 * 256 + 1); - long unsigned int add; int source = fd; - int len, count = 0; if (fd < 0) source = socket(PF_INET, SOCK_STREAM, 0); ifc.ifc_len = sizeof(buffer); ifc.ifc_req = (struct ifreq *)buffer; - ioctl(source, SIOCGIFCONF, &ifc); + ret = ioctl(source, SIOCGIFCONF, &ifc); if (fd < 0) close(source); - memset(ip_ext, 0, sizeof(ip_ext)); - memcpy(ip_ext, "0.0.0.0", 7); + if (ret < 0) { + const char *error = g_strerror(errno); + purple_debug_error("bonjour", "ioctl(SIOCGIFCONF) error: %s\n", error ? error : "(null)"); + return NULL; + } + tmp = buffer; - tip = ip_ext; - while (tmp < buffer + ifc.ifc_len && count < 10) - { + while (tmp < buffer + ifc.ifc_len) { ifr = (struct ifreq *)tmp; tmp += HX_SIZE_OF_IFREQ(*ifr); - if (ifr->ifr_addr.sa_family == AF_INET) - { + if (ifr->ifr_addr.sa_family == AF_INET) { sinptr = (struct sockaddr_in *)&ifr->ifr_addr; - if (sinptr->sin_addr.s_addr != lhost) - { - add = ntohl(sinptr->sin_addr.s_addr); - len = g_snprintf(tip, 17, "%lu.%lu.%lu.%lu;", - ((add >> 24) & 255), - ((add >> 16) & 255), - ((add >> 8) & 255), - add & 255); - tip = &tip[len]; - count++; - continue; + if ((ntohl(sinptr->sin_addr.s_addr) >> 24) != 127) { + address_text = inet_ntoa(sinptr->sin_addr); + ips = g_slist_prepend(ips, g_strdup(address_text)); } - } + } } + } +#endif - return ip_ext; + return ips; }
--- a/libpurple/protocols/bonjour/jabber.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/bonjour/jabber.h Tue Mar 09 21:41:41 2010 +0000 @@ -37,7 +37,9 @@ { gint port; gint socket; + gint socket6; gint watcher_id; + gint watcher_id6; PurpleAccount *account; GSList *pending_conversations; } BonjourJabber; @@ -105,6 +107,6 @@ XepIq *xep_iq_new(void *data, XepIqType type, const char *to, const char *from, const char *id); int xep_iq_send_and_free(XepIq *iq); -const char *purple_network_get_my_ip_ext2(int fd); +GSList * bonjour_jabber_get_local_ips(int fd); #endif /* _BONJOUR_JABBER_H_ */
--- a/libpurple/protocols/bonjour/mdns_avahi.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/bonjour/mdns_avahi.c Tue Mar 09 21:41:41 2010 +0000 @@ -189,8 +189,12 @@ bb->ips = g_slist_remove(bb->ips, rd->ip); g_free((gchar *) rd->ip); } - bb->ips = g_slist_prepend(bb->ips, g_strdup(ip)); - rd->ip = bb->ips->data; + rd->ip = g_strdup(ip); + /* IPv6 goes at the front of the list and IPv4 at the end so that we "prefer" IPv6, if present */ + if (protocol == AVAHI_PROTO_INET6) + bb->ips = g_slist_prepend(bb->ips, (gchar *) rd->ip); + else + bb->ips = g_slist_append(bb->ips, (gchar *) rd->ip); } bb->port_p2pj = port; @@ -249,7 +253,7 @@ /* Make sure it isn't us */ if (purple_utf8_strcasecmp(name, account->username) != 0) { if (!avahi_service_resolver_new(avahi_service_browser_get_client(b), - interface, protocol, name, type, domain, AVAHI_PROTO_INET, + interface, protocol, name, type, domain, protocol, 0, _resolver_callback, account)) { purple_debug_warning("bonjour", "_browser_callback -- Error initiating resolver: %s\n", avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))); @@ -448,14 +452,14 @@ case PUBLISH_START: publish_result = avahi_entry_group_add_service_strlst( idata->group, AVAHI_IF_UNSPEC, - AVAHI_PROTO_INET, 0, + AVAHI_PROTO_UNSPEC, 0, purple_account_get_username(data->account), LINK_LOCAL_RECORD_NAME, NULL, NULL, data->port_p2pj, lst); break; case PUBLISH_UPDATE: publish_result = avahi_entry_group_update_service_txt_strlst( idata->group, AVAHI_IF_UNSPEC, - AVAHI_PROTO_INET, 0, + AVAHI_PROTO_UNSPEC, 0, purple_account_get_username(data->account), LINK_LOCAL_RECORD_NAME, NULL, lst); break; @@ -487,7 +491,7 @@ g_return_val_if_fail(idata != NULL, FALSE); - idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_INET, LINK_LOCAL_RECORD_NAME, NULL, 0, _browser_callback, data->account); + idata->sb = avahi_service_browser_new(idata->client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, LINK_LOCAL_RECORD_NAME, NULL, 0, _browser_callback, data->account); if (!idata->sb) { purple_debug_error("bonjour", @@ -533,7 +537,7 @@ purple_account_get_username(data->account)); ret = avahi_entry_group_add_record(idata->buddy_icon_group, AVAHI_IF_UNSPEC, - AVAHI_PROTO_INET, flags, svc_name, + AVAHI_PROTO_UNSPEC, flags, svc_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 120, avatar_data, avatar_len); g_free(svc_name); @@ -622,7 +626,7 @@ name = g_strdup_printf("%s." LINK_LOCAL_RECORD_NAME "local", buddy->name); idata->buddy_icon_rec_browser = avahi_record_browser_new(session_idata->client, AVAHI_IF_UNSPEC, - AVAHI_PROTO_INET, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0, + AVAHI_PROTO_UNSPEC, name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_NULL, 0, _buddy_icon_record_cb, buddy); g_free(name);
--- a/libpurple/protocols/bonjour/mdns_win32.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/bonjour/mdns_win32.c Tue Mar 09 21:41:41 2010 +0000 @@ -251,7 +251,7 @@ static void DNSSD_API _mdns_service_resolve_callback(DNSServiceRef sdRef, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, - const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const char *txtRecord, void *context) + const char *fullname, const char *hosttarget, uint16_t port, uint16_t txtLen, const unsigned char *txtRecord, void *context) { ResolveCallbackArgs *args = (ResolveCallbackArgs*) context; Win32BuddyImplData *idata = args->bb->mdns_impl_data;
--- a/libpurple/protocols/gg/Makefile.am Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/gg/Makefile.am Tue Mar 09 21:41:41 2010 +0000 @@ -4,14 +4,19 @@ lib/compat.h \ lib/COPYING \ lib/dcc.c \ + lib/dcc7.c \ lib/events.c \ lib/http.c \ lib/libgadu.c \ lib/libgadu-config.h \ lib/libgadu.h \ lib/obsolete.c \ + lib/protocol.h \ + lib/pubdir.c \ lib/pubdir50.c \ - lib/pubdir.c + lib/resolver.c \ + lib/resolver.h \ + lib/sha1.c pkgdir = $(libdir)/purple-$(PURPLE_MAJOR_VERSION) @@ -20,16 +25,21 @@ lib/common.c \ lib/compat.h \ lib/dcc.c \ + lib/dcc7.c \ lib/events.c \ lib/http.c \ lib/libgadu.c \ lib/libgadu-config.h \ lib/libgadu.h \ lib/obsolete.c \ + lib/protocol.h \ + lib/pubdir.c \ lib/pubdir50.c \ - lib/pubdir.c + lib/resolver.c \ + lib/resolver.h \ + lib/sha1.c -INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib +INTGG_CFLAGS = -I$(top_srcdir)/libpurple/protocols/gg/lib -DGG_IGNORE_DEPRECATED endif GGSOURCES = \
--- a/libpurple/protocols/gg/Makefile.mingw Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/gg/Makefile.mingw Tue Mar 09 21:41:41 2010 +0000 @@ -8,7 +8,7 @@ include $(PIDGIN_TREE_TOP)/libpurple/win32/global.mak TARGET = libgg -CFLAGS += -include win32dep.h +CFLAGS += -include win32dep.h -DGG_IGNORE_DEPRECATED TYPE = PLUGIN # Static or Plugin... @@ -41,12 +41,16 @@ ## C_SRC = \ lib/common.c \ + lib/dcc.c \ + lib/dcc7.c \ lib/events.c \ lib/http.c \ lib/libgadu.c \ lib/obsolete.c \ lib/pubdir.c \ lib/pubdir50.c \ + lib/resolver.c \ + lib/sha1.c \ buddylist.c \ confer.c \ gg.c \
--- a/libpurple/protocols/gg/buddylist.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/gg/buddylist.c Tue Mar 09 21:41:41 2010 +0000 @@ -82,7 +82,7 @@ gchar **users_tbl; int i; char *utf8buddylist = charset_convert(buddylist, "CP1250", "UTF-8"); - + /* Don't limit the number of records in a buddylist. */ users_tbl = g_strsplit(utf8buddylist, "\r\n", -1);
--- a/libpurple/protocols/gg/gg.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/gg/gg.c Tue Mar 09 21:41:41 2010 +0000 @@ -1000,8 +1000,8 @@ int status, const char *descr) { gchar *from; + gchar *msg; const char *st; - gchar *msg; gchar *avatarurl; PurpleUtilFetchUrlData *url_data; @@ -1018,29 +1018,37 @@ switch (status) { case GG_STATUS_NOT_AVAIL: case GG_STATUS_NOT_AVAIL_DESCR: - st = "offline"; + st = purple_primitive_get_id_from_type(PURPLE_STATUS_OFFLINE); + break; + case GG_STATUS_FFC: + case GG_STATUS_FFC_DESCR: + st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE); break; case GG_STATUS_AVAIL: case GG_STATUS_AVAIL_DESCR: - st = "available"; + st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE); break; case GG_STATUS_BUSY: case GG_STATUS_BUSY_DESCR: - st = "away"; + st = purple_primitive_get_id_from_type(PURPLE_STATUS_AWAY); break; + case GG_STATUS_DND: + case GG_STATUS_DND_DESCR: + st = purple_primitive_get_id_from_type(PURPLE_STATUS_UNAVAILABLE); case GG_STATUS_BLOCKED: /* user is blocking us.... */ st = "blocked"; break; default: - st = "available"; + st = purple_primitive_get_id_from_type(PURPLE_STATUS_AVAILABLE); purple_debug_info("gg", "GG_EVENT_NOTIFY: Unknown status: %d\n", status); break; } purple_debug_info("gg", "st = %s\n", st); - msg = charset_convert(descr, "CP1250", "UTF-8"); + //msg = charset_convert(descr, "CP1250", "UTF-8"); + msg = g_strdup_printf("%s", descr); purple_prpl_got_user_status(purple_connection_get_account(gc), from, st, "message", msg, NULL); g_free(from); @@ -1078,6 +1086,12 @@ case GG_STATUS_AVAIL_DESCR: st = _("Available"); break; + case GG_STATUS_FFC: + case GG_STATUS_FFC_DESCR: + return _("Chatty"); + case GG_STATUS_DND: + case GG_STATUS_DND_DESCR: + return _("Do Not Disturb"); case GG_STATUS_BUSY: case GG_STATUS_BUSY_DESCR: st = _("Away"); @@ -1347,8 +1361,11 @@ from = g_strdup_printf("%lu", (unsigned long int)ev->event.msg.sender); + /* tmp = charset_convert((const char *)ev->event.msg.message, "CP1250", "UTF-8"); + */ + tmp = g_strdup_printf("%s", ev->event.msg.message); purple_str_strip_char(tmp, '\r'); msg = g_markup_escape_text(tmp, -1); g_free(tmp); @@ -1562,7 +1579,7 @@ purple_debug_info("gg", "notify_pre: (%d) status: %d\n", ev->event.notify->uin, - ev->event.notify->status); + GG_S(ev->event.notify->status)); n = (ev->type == GG_EVENT_NOTIFY) ? ev->event.notify : ev->event.notify_descr.notify; @@ -1573,17 +1590,17 @@ purple_debug_info("gg", "notify: (%d) status: %d; descr: %s\n", - n->uin, n->status, descr ? descr : "(null)"); + n->uin, GG_S(n->status), descr ? descr : "(null)"); ggp_generic_status_handler(gc, - n->uin, n->status, descr); + n->uin, GG_S(n->status), descr); } } break; case GG_EVENT_NOTIFY60: purple_debug_info("gg", "notify60_pre: (%d) status=%d; version=%d; descr=%s\n", - ev->event.notify60->uin, ev->event.notify60->status, + ev->event.notify60->uin, GG_S(ev->event.notify60->status), ev->event.notify60->version, ev->event.notify60->descr ? ev->event.notify60->descr : "(null)"); @@ -1602,7 +1619,7 @@ break; case GG_EVENT_STATUS: purple_debug_info("gg", "status: (%d) status=%d; descr=%s\n", - ev->event.status.uin, ev->event.status.status, + ev->event.status.uin, GG_S(ev->event.status.status), ev->event.status.descr ? ev->event.status.descr : "(null)"); ggp_generic_status_handler(gc, ev->event.status.uin, @@ -1611,12 +1628,12 @@ case GG_EVENT_STATUS60: purple_debug_info("gg", "status60: (%d) status=%d; version=%d; descr=%s\n", - ev->event.status60.uin, ev->event.status60.status, + ev->event.status60.uin, GG_S(ev->event.status60.status), ev->event.status60.version, ev->event.status60.descr ? ev->event.status60.descr : "(null)"); ggp_generic_status_handler(gc, ev->event.status60.uin, - ev->event.status60.status, ev->event.status60.descr); + GG_S(ev->event.status60.status), ev->event.status60.descr); break; case GG_EVENT_USERLIST: if (ev->event.userlist.type == GG_USERLIST_GET_REPLY) { @@ -1829,6 +1846,16 @@ NULL); types = g_list_append(types, type); + /* + * New statuses for GG 8.0 like PoGGadaj ze mna (not yet because + * libpurple can't support Chatty status) and Nie przeszkadzac + */ + type = purple_status_type_new_with_attrs( + PURPLE_STATUS_UNAVAILABLE, NULL, NULL, TRUE, TRUE, FALSE, + "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), + NULL); + types = g_list_append(types, type); + /* * This status is necessary to display guys who are blocking *us*. */ @@ -1930,6 +1957,9 @@ presence = purple_account_get_presence(account); status = purple_presence_get_active_status(presence); + glp->encoding = GG_ENCODING_UTF8; + glp->protocol_features = (GG_FEATURE_STATUS80|GG_FEATURE_DND_FFC); + glp->async = 1; glp->status = ggp_to_gg_status(status, &glp->status_descr); glp->tls = 0; @@ -2100,8 +2130,11 @@ plain = purple_unescape_html(msg); } + /* tmp = charset_convert(plain, "UTF-8", "CP1250"); - + */ + tmp = g_strdup_printf("%s", plain); + if (tmp && (format_length - sizeof(struct gg_msg_richtext))) { if(gg_send_message_richtext(info->session, GG_CLASS_CHAT, ggp_str_to_uin(who), (unsigned char *)tmp, format, format_length) < 0) { ret = -1; @@ -2160,6 +2193,9 @@ } else if (strcmp(status_id, "away") == 0) { new_status = GG_STATUS_BUSY; new_status_descr = GG_STATUS_BUSY_DESCR; + } else if (strcmp(status_id, "unavailable") == 0) { + new_status = GG_STATUS_DND; + new_status_descr = GG_STATUS_DND_DESCR; } else if (strcmp(status_id, "invisible") == 0) { new_status = GG_STATUS_INVISIBLE; new_status_descr = GG_STATUS_INVISIBLE_DESCR; @@ -2177,9 +2213,12 @@ new_msg = purple_status_get_attr_string(status, "message"); if(new_msg) { + /* char *tmp = purple_markup_strip_html(new_msg); *msg = charset_convert(tmp, "UTF-8", "CP1250"); g_free(tmp); + */ + *msg = purple_markup_strip_html(new_msg); return new_status_descr; } else { @@ -2279,7 +2318,8 @@ GGPInfo *info = gc->proto_data; GGPChat *chat = NULL; GList *l; - char *msg, *plain; + /* char *msg, *plain; */ + gchar *msg; uin_t *uins; int count = 0; @@ -2310,9 +2350,12 @@ uins[count++] = uin; } + /* plain = purple_unescape_html(message); msg = charset_convert(plain, "UTF-8", "CP1250"); g_free(plain); + */ + msg = purple_unescape_html(message); gg_send_message_confer(info->session, GG_CLASS_CHAT, count, uins, (unsigned char *)msg); g_free(msg); @@ -2465,7 +2508,8 @@ sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL, /* get_account_text_table */ NULL, /* initiate_media */ - NULL /* can_do_media */ + NULL, /* can_do_media */ + NULL /* get_moods */ }; static PurplePluginInfo info = {
--- a/libpurple/protocols/gg/lib/common.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/gg/lib/common.c Tue Mar 09 21:41:41 2010 +0000 @@ -1,8 +1,8 @@ -/* $Id: common.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: common.c 878 2009-11-16 23:48:19Z wojtekka $ */ /* * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Wony <speedy@ziew.org> + * Robert J. Woźny <speedy@ziew.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -15,109 +15,180 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ +/* + * Funkcje konwersji między UTF-8 i CP1250 są oparte o kod biblioteki iconv. + * Informacje o prawach autorskich oryginalnego kodu zamieszczono poniżej: + * + * Copyright (C) 1999-2001, 2004 Free Software Foundation, Inc. + * This file is part of the GNU LIBICONV Library. + * + * The GNU LIBICONV Library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * The GNU LIBICONV Library is distributed in the hope that it will be + * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with the GNU LIBICONV Library; see the file COPYING.LIB. + * If not, write to the Free Software Foundation, Inc., 51 Franklin Street, + * Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** + * \file common.c + * + * \brief Funkcje wykorzystywane przez różne moduły biblioteki + */ + #include "libgadu.h" +#include "libgadu-internal.h" #ifndef _WIN32 -#include <sys/types.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#ifdef sun -# include <sys/filio.h> -#endif +# include <sys/types.h> +# include <sys/ioctl.h> +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# ifdef sun +# include <sys/filio.h> +# endif #endif #include <errno.h> #include <fcntl.h> + #ifndef _WIN32 -#include <netdb.h> +# include <netdb.h> #endif + #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +/** + * Plik, do którego będą przekazywane informacje odpluskwiania. + * + * Funkcja \c gg_debug() i pochodne mogą być przechwytywane przez aplikację + * korzystającą z biblioteki, by wyświetlić je na żądanie użytkownika lub + * zapisać do późniejszej analizy. Jeśli nie określono pliku, wybrane + * informacje będą wysyłane do standardowego wyjścia błędu (\c stderr). + * + * \ingroup debug + */ FILE *gg_debug_file = NULL; #ifndef GG_DEBUG_DISABLE -/* - * gg_debug() // funkcja wewntrzna +/** + * \internal Przekazuje informacje odpluskwiania do odpowiedniej funkcji. + * + * Jeśli aplikacja ustawiła odpowiednią funkcję obsługi w + * \c gg_debug_handler_session lub \c gg_debug_handler, jest ona wywoływana. + * W przeciwnym wypadku wynik jest wysyłany do standardowego wyjścia błędu. * - * wywietla komunikat o danym poziomie, o ile uytkownik sobie tego yczy. + * \param sess Struktura sesji (może być \c NULL) + * \param level Poziom informacji + * \param format Format wiadomości (zgodny z \c printf) + * \param ap Lista argumentów (zgodna z \c printf) + */ +void gg_debug_common(struct gg_session *sess, int level, const char *format, va_list ap) +{ + if (gg_debug_handler_session) + (*gg_debug_handler_session)(sess, level, format, ap); + else if (gg_debug_handler) + (*gg_debug_handler)(level, format, ap); + else if (gg_debug_level & level) + vfprintf(gg_debug_file ? gg_debug_file : stderr, format, ap); +} + + +/** + * \internal Przekazuje informację odpluskawiania. * - * - level - poziom wiadomoci - * - format... - tre wiadomoci (kompatybilna z printf()) + * \param level Poziom wiadomości + * \param format Format wiadomości (zgodny z \c printf) + * + * \ingroup debug */ void gg_debug(int level, const char *format, ...) { va_list ap; int old_errno = errno; - - if (gg_debug_handler) { - va_start(ap, format); - (*gg_debug_handler)(level, format, ap); - va_end(ap); + va_start(ap, format); + gg_debug_common(NULL, level, format, ap); + va_end(ap); + errno = old_errno; +} - goto cleanup; - } - - if ((gg_debug_level & level)) { - va_start(ap, format); - vfprintf((gg_debug_file) ? gg_debug_file : stderr, format, ap); - va_end(ap); - } - -cleanup: +/** + * \internal Przekazuje informację odpluskwiania związaną z sesją. + * + * \param sess Struktura sesji + * \param level Poziom wiadomości + * \param format Format wiadomości (zgodny z \c printf) + * + * \ingroup debug + */ +void gg_debug_session(struct gg_session *sess, int level, const char *format, ...) +{ + va_list ap; + int old_errno = errno; + va_start(ap, format); + gg_debug_common(sess, level, format, ap); + va_end(ap); errno = old_errno; } #endif -/* - * gg_vsaprintf() // funkcja pomocnicza +/** + * \internal Odpowiednik funkcji \c vsprintf alokujący miejsce na wynik. + * + * Funkcja korzysta z funkcji \c vsnprintf, sprawdzając czy dostępna funkcja + * systemowa jest zgodna ze standardem C99 czy wcześniejszymi. * - * robi dokadnie to samo, co vsprintf(), tyle e alokuje sobie wczeniej - * miejsce na dane. powinno dziaa na tych maszynach, ktre maj funkcj - * vsnprintf() zgodn z C99, jak i na wczeniejszych. + * \param format Format wiadomości (zgodny z \c printf) + * \param ap Lista argumentów (zgodna z \c printf) * - * - format - opis wywietlanego tekstu jak dla printf() - * - ap - lista argumentw dla printf() + * \return Zaalokowany bufor lub NULL, jeśli zabrakło pamięci. * - * zaalokowany bufor, ktry naley pniej zwolni, lub NULL - * jeli nie udao si wykona zadania. + * \ingroup helper */ char *gg_vsaprintf(const char *format, va_list ap) { int size = 0; const char *start; char *buf = NULL; - -#ifdef __GG_LIBGADU_HAVE_VA_COPY + +#ifdef GG_CONFIG_HAVE_VA_COPY va_list aq; va_copy(aq, ap); #else -# ifdef __GG_LIBGADU_HAVE___VA_COPY +# ifdef GG_CONFIG_HAVE___VA_COPY va_list aq; __va_copy(aq, ap); # endif #endif - start = format; + start = format; -#ifndef __GG_LIBGADU_HAVE_C99_VSNPRINTF +#ifndef GG_CONFIG_HAVE_C99_VSNPRINTF { int res; char *tmp; - + size = 128; do { size *= 2; @@ -132,9 +203,9 @@ #else { char tmp[2]; - - /* libce Solarisa przy buforze NULL zawsze zwracaj -1, wic - * musimy poda co istniejcego jako cel printf()owania. */ + + /* libce Solarisa przy buforze NULL zawsze zwracają -1, więc + * musimy podać coś istniejącego jako cel printf()owania. */ size = vsnprintf(tmp, sizeof(tmp), format, ap); if (!(buf = malloc(size + 1))) return NULL; @@ -142,33 +213,33 @@ #endif format = start; - -#ifdef __GG_LIBGADU_HAVE_VA_COPY + +#ifdef GG_CONFIG_HAVE_VA_COPY vsnprintf(buf, size + 1, format, aq); va_end(aq); #else -# ifdef __GG_LIBGADU_HAVE___VA_COPY +# ifdef GG_CONFIG_HAVE___VA_COPY vsnprintf(buf, size + 1, format, aq); va_end(aq); # else vsnprintf(buf, size + 1, format, ap); # endif #endif - + return buf; } -/* - * gg_saprintf() // funkcja pomocnicza +/** + * \internal Odpowiednik funkcji \c sprintf alokujący miejsce na wynik. + * + * Funkcja korzysta z funkcji \c vsnprintf, sprawdzając czy dostępna funkcja + * systemowa jest zgodna ze standardem C99 czy wcześniejszymi. * - * robi dokadnie to samo, co sprintf(), tyle e alokuje sobie wczeniej - * miejsce na dane. powinno dziaa na tych maszynach, ktre maj funkcj - * vsnprintf() zgodn z C99, jak i na wczeniejszych. + * \param format Format wiadomości (zgodny z \c printf) * - * - format... - tre taka sama jak w funkcji printf() + * \return Zaalokowany bufor lub NULL, jeśli zabrakło pamięci. * - * zaalokowany bufor, ktry naley pniej zwolni, lub NULL - * jeli nie udao si wykona zadania. + * \ingroup helper */ char *gg_saprintf(const char *format, ...) { @@ -182,18 +253,17 @@ return res; } -/* - * gg_get_line() // funkcja pomocnicza - * - * podaje kolejn lini z bufora tekstowego. niszczy go bezpowrotnie, dzielc - * na kolejne stringi. zdarza si, nie ma potrzeby pisania funkcji dublujcej - * bufor eby tylko mie nieruszone dane wejciowe, skoro i tak nie bd nam - * poniej potrzebne. obcina `\r\n'. - * - * - ptr - wskanik do zmiennej, ktra przechowuje aktualn pozycj - * w przemiatanym buforze - * - * wskanik do kolejnej linii tekstu lub NULL, jeli to ju koniec bufora. +/** + * \internal Pobiera linię tekstu z bufora. + * + * Funkcja niszczy bufor źródłowy bezpowrotnie, dzieląc go na kolejne ciągi + * znaków i obcina znaki końca linii. + * + * \param ptr Wskaźnik do zmiennej, która przechowuje aktualne położenie + * w analizowanym buforze + * + * \return Wskaźnik do kolejnej linii tekstu lub NULL, jeśli to już koniec + * bufora. */ char *gg_get_line(char **ptr) { @@ -207,99 +277,30 @@ if (!(foo = strchr(*ptr, '\n'))) *ptr += strlen(*ptr); else { + size_t len; *ptr = foo + 1; *foo = 0; - if (strlen(res) > 1 && res[strlen(res) - 1] == '\r') - res[strlen(res) - 1] = 0; + + len = strlen(res); + + if (len > 1 && res[len - 1] == '\r') + res[len - 1] = 0; } return res; } -/* - * gg_connect() // funkcja pomocnicza +/** + * \internal Czyta linię tekstu z gniazda. * - * czy si z serwerem. pierwszy argument jest typu (void *), eby nie - * musie niczego inkludowa w libgadu.h i nie psu jaki gupich zalenoci - * na dziwnych systemach. - * - * - addr - adres serwera (struct in_addr *) - * - port - port serwera - * - async - asynchroniczne poczenie + * Funkcja czyta tekst znak po znaku, więc nie jest efektywna, ale dzięki + * brakowi buforowania, nie koliduje z innymi funkcjami odczytu. * - * deskryptor gniazda lub -1 w przypadku bdu (kod bdu w zmiennej errno). - */ -int gg_connect(void *addr, int port, int async) -{ - int sock, one = 1, errno2; - struct sockaddr_in sin; - struct in_addr *a = addr; - struct sockaddr_in myaddr; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n", inet_ntoa(*a), port, async); - - if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_connect() socket() failed (errno=%d, %s)\n", errno, strerror(errno)); - return -1; - } - - memset(&myaddr, 0, sizeof(myaddr)); - myaddr.sin_family = AF_INET; - - myaddr.sin_addr.s_addr = gg_local_ip; - - if (bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_connect() bind() failed (errno=%d, %s)\n", errno, strerror(errno)); - return -1; - } - -#ifdef ASSIGN_SOCKETS_TO_THREADS - gg_win32_thread_socket(0, sock); -#endif - - if (async) { -#ifdef FIONBIO - if (ioctl(sock, FIONBIO, &one) == -1) { -#else - if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { -#endif - gg_debug(GG_DEBUG_MISC, "// gg_connect() ioctl() failed (errno=%d, %s)\n", errno, strerror(errno)); - errno2 = errno; - close(sock); - errno = errno2; - return -1; - } - } - - sin.sin_port = htons(port); - sin.sin_family = AF_INET; - sin.sin_addr.s_addr = a->s_addr; - - if (connect(sock, (struct sockaddr*) &sin, sizeof(sin)) == -1) { - if (errno && (!async || errno != EINPROGRESS)) { - gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() failed (errno=%d, %s)\n", errno, strerror(errno)); - errno2 = errno; - close(sock); - errno = errno2; - return -1; - } - gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() in progress\n"); - } - - return sock; -} - -/* - * gg_read_line() // funkcja pomocnicza + * \param sock Deskryptor gniazda + * \param buf Wskaźnik do bufora + * \param length Długość bufora * - * czyta jedn lini tekstu z gniazda. - * - * - sock - deskryptor gniazda - * - buf - wskanik do bufora - * - length - dugo bufora - * - * jeli trafi na bd odczytu lub podano nieprawidowe parametry, zwraca NULL. - * inaczej zwraca buf. + * \return Zwraca \c buf jeśli się powiodło, lub \c NULL w przypadku błędu. */ char *gg_read_line(int sock, char *buf, int length) { @@ -331,38 +332,116 @@ return buf; } -/* - * gg_chomp() // funkcja pomocnicza +/** + * \internal Nawiązuje połączenie TCP. + * + * \param addr Wskaźnik na strukturę \c in_addr z adresem serwera + * \param port Port serwera + * \param async Flaga asynchronicznego połączenia + * + * \return Deskryptor gniazda lub -1 w przypadku błędu * - * ucina "\r\n" lub "\n" z koca linii. + * \ingroup helper + */ +int gg_connect(void *addr, int port, int async) +{ + int sock, one = 1, errno2; + struct sockaddr_in sin; + struct in_addr *a = addr; + struct sockaddr_in myaddr; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_connect(%s, %d, %d);\n", inet_ntoa(*a), port, async); + + if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_connect() socket() failed (errno=%d, %s)\n", errno, strerror(errno)); + return -1; + } + + memset(&myaddr, 0, sizeof(myaddr)); + myaddr.sin_family = AF_INET; + + myaddr.sin_addr.s_addr = gg_local_ip; + + if (bind(sock, (struct sockaddr *) &myaddr, sizeof(myaddr)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_connect() bind() failed (errno=%d, %s)\n", errno, strerror(errno)); + errno2 = errno; + close(sock); + errno = errno2; + return -1; + } + +#ifdef ASSIGN_SOCKETS_TO_THREADS + gg_win32_thread_socket(0, sock); +#endif + + if (async) { +#ifdef FIONBIO + if (ioctl(sock, FIONBIO, &one) == -1) { +#else + if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { +#endif + gg_debug(GG_DEBUG_MISC, "// gg_connect() ioctl() failed (errno=%d, %s)\n", errno, strerror(errno)); + errno2 = errno; + close(sock); + errno = errno2; + return -1; + } + } + + sin.sin_port = htons(port); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = a->s_addr; + + if (connect(sock, (struct sockaddr*) &sin, sizeof(sin)) == -1) { + if (errno && (!async || errno != EINPROGRESS)) { + gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() failed (errno=%d, %s)\n", errno, strerror(errno)); + errno2 = errno; + close(sock); + errno = errno2; + return -1; + } + gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() in progress\n"); + } + + return sock; +} + +/** + * \internal Usuwa znaki końca linii. * - * - line - linia do przycicia + * Funkcja działa bezpośrednio na buforze. + * + * \param line Bufor z tekstem + * + * \ingroup helper */ void gg_chomp(char *line) { int len; - + if (!line) return; len = strlen(line); - + if (len > 0 && line[len - 1] == '\n') line[--len] = 0; if (len > 0 && line[len - 1] == '\r') line[--len] = 0; } -/* - * gg_urlencode() // funkcja wewntrzna +/** + * \internal Koduje ciąg znaków do postacji adresu HTTP. * - * zamienia podany tekst na cig znakw do formularza http. przydaje si - * przy rnych usugach katalogu publicznego. + * Zamienia znaki niedrukowalne, spoza ASCII i mające specjalne znaczenie + * dla protokołu HTTP na encje postaci \c %XX, gdzie \c XX jest szesnastkową + * wartością znaku. + * + * \param str Ciąg znaków do zakodowania * - * - str - cig znakw do zakodowania + * \return Zaalokowany bufor lub \c NULL w przypadku błędu. * - * zaalokowany bufor, ktry naley pniej zwolni albo NULL - * w przypadku bdu. + * \ingroup helper */ char *gg_urlencode(const char *str) { @@ -400,16 +479,19 @@ return buf; } -/* - * gg_http_hash() // funkcja wewntrzna +/** + * \internal Wyznacza skrót dla usług HTTP. * - * funkcja liczca hash dla adresu e-mail, hasa i paru innych. + * Funkcja jest wykorzystywana do wyznaczania skrótu adresu e-mail, hasła + * i innych wartości przekazywanych jako parametry usług HTTP. * - * - format... - format kolejnych parametrw ('s' jeli dany parametr jest - * cigiem znakw lub 'u' jeli numerem GG) + * W parametrze \c format należy umieścić znaki określające postać kolejnych + * parametrów: \c 's' jeśli parametr jest ciągiem znaków, \c 'u' jeśli jest + * liczbą. * - * hash wykorzystywany przy rejestracji i wszelkich manipulacjach wasnego - * wpisu w katalogu publicznym. + * \param format Format kolejnych parametrów (niezgodny z \c printf) + * + * \return Wartość skrótu */ int gg_http_hash(const char *format, ...) { @@ -428,7 +510,7 @@ } else { if (!(arg = va_arg(ap, char*))) arg = ""; - } + } i = 0; while ((c = (unsigned char) arg[i++]) != 0) { @@ -442,95 +524,6 @@ return (b < 0 ? -b : b); } -/* - * gg_gethostbyname() // funkcja pomocnicza - * - * odpowiednik gethostbyname() troszczcy si o wspbieno, gdy mamy do - * dyspozycji funkcj gethostbyname_r(). - * - * - hostname - nazwa serwera - * - * zwraca wskanik na struktur in_addr, ktr naley zwolni. - */ -struct in_addr *gg_gethostbyname(const char *hostname) -{ - struct in_addr *addr = NULL; - -#ifdef HAVE_GETHOSTBYNAME_R - char *tmpbuf = NULL, *buf = NULL; - struct hostent *hp = NULL, *hp2 = NULL; - int h_errnop, ret; - size_t buflen = 1024; - int new_errno; - - new_errno = ENOMEM; - - if (!(addr = malloc(sizeof(struct in_addr)))) - goto cleanup; - - if (!(hp = calloc(1, sizeof(*hp)))) - goto cleanup; - - if (!(buf = malloc(buflen))) - goto cleanup; - - tmpbuf = buf; - - while ((ret = gethostbyname_r(hostname, hp, buf, buflen, &hp2, &h_errnop)) == ERANGE) { - buflen *= 2; - - if (!(tmpbuf = realloc(buf, buflen))) - break; - - buf = tmpbuf; - } - - if (ret) - new_errno = h_errnop; - - if (ret || !hp2 || !tmpbuf) - goto cleanup; - - memcpy(addr, hp->h_addr, sizeof(struct in_addr)); - - free(buf); - free(hp); - - return addr; - -cleanup: - errno = new_errno; - - if (addr) - free(addr); - if (hp) - free(hp); - if (buf) - free(buf); - - return NULL; -#else - struct hostent *hp; - - if (!(addr = malloc(sizeof(struct in_addr)))) { - goto cleanup; - } - - if (!(hp = gethostbyname(hostname))) - goto cleanup; - - memcpy(addr, hp->h_addr, sizeof(struct in_addr)); - - return addr; - -cleanup: - if (addr) - free(addr); - - return NULL; -#endif -} - #ifdef ASSIGN_SOCKETS_TO_THREADS typedef struct gg_win32_thread { @@ -541,23 +534,22 @@ struct gg_win32_thread *gg_win32_threads = 0; -/* - * gg_win32_thread_socket() // funkcja pomocnicza, tylko dla win32 +/** + * \internal Zwraca deskryptor gniazda, które było ostatnio tworzone dla wątku. * - * zwraca deskryptor gniazda, ktre byo ostatnio tworzone dla wtku - * o podanym identyfikatorze. + * Jeśli na win32 przy połączeniach synchronicznych zapamiętamy w jakim + * wątku uruchomiliśmy funkcję, która się z czymkolwiek łączy, to z osobnego + * wątku możemy anulować połączenie poprzez \c gg_win32_thread_socket(watek,-1) * - * jeli na win32 przy poczeniach synchronicznych zapamitamy w jakim - * wtku uruchomilimy funkcj, ktra si z czymkolwiek czy, to z osobnego - * wtku moemy anulowa poczenie poprzez gg_win32_thread_socket(watek, -1); - * - * - thread_id - id wtku. jeli jest rwne 0, brany jest aktualny wtek, - * jeli rwne -1, usuwa wpis o podanym sockecie. - * - socket - deskryptor gniazda. jeli rwne 0, zwraca deskryptor gniazda - * dla podanego wtku, jeli rwne -1, usuwa wpis, jeli co - * innego, ustawia dla podanego wtku dany numer deskryptora. + * \param thread_id Identyfikator wątku (jeśli jest równe 0, brany jest + * aktualny wątek, jeśli równe -1, usuwa wpis dotyczący + * danego gniazda sockecie) + * \param socket Deskryptor gniazda (jeśli równe 0, zwraca deskryptor gniazda + * dla podanego wątku, jeśli równe -1, usuwa wpis, jeśli coś + * innego, ustawia dla podanego wątku dany numer deskryptora) * - * jeli socket jest rwne 0, zwraca deskryptor gniazda dla podanego wtku. + * \return Jeśli socket jest równe 0, zwraca deskryptor gniazda dla podanego + * wątku. */ int gg_win32_thread_socket(int thread_id, int socket) { @@ -567,7 +559,7 @@ if (!thread_id) thread_id = GetCurrentThreadId(); - + while (wsk) { if ((thread_id == -1 && wsk->socket == socket) || wsk->id == thread_id) { if (close) { @@ -593,7 +585,7 @@ closesocket(socket); if (close || !socket) return 0; - + /* Dodaje nowy element */ wsk = malloc(sizeof(gg_win32_thread)); wsk->id = thread_id; @@ -606,28 +598,33 @@ #endif /* ASSIGN_SOCKETS_TO_THREADS */ +/** + * \internal Zestaw znaków kodowania base64. + */ static char gg_base64_charset[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; -/* - * gg_base64_encode() +/** + * \internal Koduje ciąg znaków do base64. + * + * Wynik funkcji należy zwolnić za pomocą \c free. * - * zapisuje cig znakw w base64. + * \param buf Bufor z danami do zakodowania * - * - buf - cig znakw. + * \return Zaalokowany bufor z zakodowanymi danymi * - * zaalokowany bufor. + * \ingroup helper */ char *gg_base64_encode(const char *buf) { char *out, *res; unsigned int i = 0, j = 0, k = 0, len = strlen(buf); - + res = out = malloc((len / 3 + 1) * 4 + 2); if (!res) return NULL; - + while (j <= len) { switch (i % 4) { case 0: @@ -660,20 +657,22 @@ if (i % 4) for (j = 0; j < 4 - (i % 4); j++, out++) *out = '='; - + *out = 0; - + return res; } -/* - * gg_base64_decode() +/** + * \internal Dekoduje ciąg znaków zapisany w base64. + * + * Wynik funkcji należy zwolnić za pomocą \c free. * - * dekoduje cig znakw z base64. + * \param buf Bufor źródłowy z danymi do zdekodowania * - * - buf - cig znakw. + * \return Zaalokowany bufor ze zdekodowanymi danymi * - * zaalokowany bufor. + * \ingroup helper */ char *gg_base64_decode(const char *buf) { @@ -683,7 +682,7 @@ if (!buf) return NULL; - + save = res = calloc(1, (strlen(buf) / 4 + 1) * 3 + 2); if (!save) @@ -720,23 +719,24 @@ index %= 4; } *res = 0; - + return save; } -/* - * gg_proxy_auth() // funkcja wewntrzna +/** + * \internal Tworzy nagłówek autoryzacji serwera pośredniczącego. * - * tworzy nagwek autoryzacji dla proxy. - * - * zaalokowany tekst lub NULL, jeli proxy nie jest wczone lub nie wymaga - * autoryzacji. + * Dane pobiera ze zmiennych globalnych \c gg_proxy_username i + * \c gg_proxy_password. + * + * \return Zaalokowany bufor z tekstem lub NULL, jeśli serwer pośredniczący + * nie jest używany lub nie wymaga autoryzacji. */ char *gg_proxy_auth() { char *tmp, *enc, *out; unsigned int tmp_size; - + if (!gg_proxy_enabled || !gg_proxy_username || !gg_proxy_password) return NULL; @@ -749,14 +749,14 @@ free(tmp); return NULL; } - + free(tmp); if (!(out = malloc(strlen(enc) + 40))) { free(enc); return NULL; } - + snprintf(out, strlen(enc) + 40, "Proxy-Authorization: Basic %s\r\n", enc); free(enc); @@ -764,13 +764,21 @@ return out; } +/** + * \internal Tablica pomocnicza do wyznaczania sumy kontrolnej. + */ static uint32_t gg_crc32_table[256]; + +/** + * \internal Flaga wypełnienia tablicy pomocniczej do wyznaczania sumy + * kontrolnej. + */ static int gg_crc32_initialized = 0; -/* - * gg_crc32_make_table() // funkcja wewntrzna +/** + * \internal Tworzy tablicę pomocniczą do wyznaczania sumy kontrolnej. */ -static void gg_crc32_make_table() +static void gg_crc32_make_table(void) { uint32_t h = 1; unsigned int i, j; @@ -787,16 +795,15 @@ gg_crc32_initialized = 1; } -/* - * gg_crc32() - * - * wyznacza sum kontroln CRC32 danego bloku danych. +/** + * Wyznacza sumę kontrolną CRC32. * - * - crc - suma kontrola poprzedniego bloku danych lub 0 jeli pierwszy - * - buf - bufor danych - * - size - ilo danych + * \param crc Suma kontrola poprzedniego bloku danych lub 0 jeśli liczona + * jest suma kontrolna pierwszego bloku + * \param buf Bufor danych + * \param len Długość bufora danych * - * suma kontrolna CRC32. + * \return Suma kontrolna. */ uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len) { @@ -814,6 +821,186 @@ return crc ^ 0xffffffffL; } +/** + * \internal Tablica konwersji między CP1250 a UTF-8. + */ +static const uint16_t table_cp1250[] = { + 0x20ac, '?', 0x201a, '?', 0x201e, 0x2026, 0x2020, 0x2021, + '?', 0x2030, 0x0160, 0x2039, 0x015a, 0x0164, 0x017d, 0x0179, + '?', 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, + '?', 0x2122, 0x0161, 0x203a, 0x015b, 0x0165, 0x017e, 0x017a, + 0x00a0, 0x02c7, 0x02d8, 0x0141, 0x00a4, 0x0104, 0x00a6, 0x00a7, + 0x00a8, 0x00a9, 0x015e, 0x00ab, 0x00ac, 0x00ad, 0x00ae, 0x017b, + 0x00b0, 0x00b1, 0x02db, 0x0142, 0x00b4, 0x00b5, 0x00b6, 0x00b7, + 0x00b8, 0x0105, 0x015f, 0x00bb, 0x013d, 0x02dd, 0x013e, 0x017c, + 0x0154, 0x00c1, 0x00c2, 0x0102, 0x00c4, 0x0139, 0x0106, 0x00c7, + 0x010c, 0x00c9, 0x0118, 0x00cb, 0x011a, 0x00cd, 0x00ce, 0x010e, + 0x0110, 0x0143, 0x0147, 0x00d3, 0x00d4, 0x0150, 0x00d6, 0x00d7, + 0x0158, 0x016e, 0x00da, 0x0170, 0x00dc, 0x00dd, 0x0162, 0x00df, + 0x0155, 0x00e1, 0x00e2, 0x0103, 0x00e4, 0x013a, 0x0107, 0x00e7, + 0x010d, 0x00e9, 0x0119, 0x00eb, 0x011b, 0x00ed, 0x00ee, 0x010f, + 0x0111, 0x0144, 0x0148, 0x00f3, 0x00f4, 0x0151, 0x00f6, 0x00f7, + 0x0159, 0x016f, 0x00fa, 0x0171, 0x00fc, 0x00fd, 0x0163, 0x02d9, +}; + +/** + * \internal Zamienia tekst kodowany CP1250 na UTF-8. + * + * \param b Tekst źródłowy w CP1250. + * + * \return Zaalokowany bufor z tekstem w UTF-8. + */ +char *gg_cp_to_utf8(const char *b) +{ + unsigned char *buf = (unsigned char *) b; + char *newbuf; + int newlen = 0; + int i, j; + + for (i = 0; buf[i]; i++) { + uint16_t znak = (buf[i] < 0x80) ? buf[i] : table_cp1250[buf[i]-0x80]; + + if (znak < 0x80) newlen += 1; + else if (znak < 0x800) newlen += 2; + else newlen += 3; + } + + if (!(newbuf = malloc(newlen+1))) { + gg_debug(GG_DEBUG_MISC, "// gg_cp_to_utf8() not enough memory\n"); + return NULL; + } + + for (i = 0, j = 0; buf[i]; i++) { + uint16_t znak = (buf[i] < 0x80) ? buf[i] : table_cp1250[buf[i]-0x80]; + int count; + + if (znak < 0x80) count = 1; + else if (znak < 0x800) count = 2; + else count = 3; + + switch (count) { + case 3: newbuf[j+2] = 0x80 | (znak & 0x3f); znak = znak >> 6; znak |= 0x800; + case 2: newbuf[j+1] = 0x80 | (znak & 0x3f); znak = znak >> 6; znak |= 0xc0; + case 1: newbuf[j] = znak; + } + j += count; + } + newbuf[j] = '\0'; + + return newbuf; +} + +/** + * \internal Dekoduje jeden znak UTF-8. + * + * \note Funkcja nie jest kompletną implementacją UTF-8, a wersją uproszczoną + * do potrzeb kodowania CP1250. + * + * \param s Tekst źródłowy. + * \param n Długość tekstu źródłowego. + * \param ch Wskaźnik na wynik dekodowania. + * + * \return Długość zdekodowanej sekwencji w bajtach lub wartość mniejsza + * od zera w przypadku błędu. + */ +static int gg_utf8_helper(unsigned char *s, int n, uint16_t *ch) +{ + unsigned char c = s[0]; + + if (c < 0x80) { + *ch = c; + return 1; + } + + if (c < 0xc2) + return -1; + + if (c < 0xe0) { + if (n < 2) + return -2; + if (!((s[1] ^ 0x80) < 0x40)) + return -1; + *ch = ((uint16_t) (c & 0x1f) << 6) | (uint16_t) (s[1] ^ 0x80); + return 2; + } + + if (c < 0xf0) { + if (n < 3) + return -2; + if (!((s[1] ^ 0x80) < 0x40 && (s[2] ^ 0x80) < 0x40 && (c >= 0xe1 || s[1] >= 0xa0))) + return -1; + *ch = ((uint16_t) (c & 0x0f) << 12) | ((uint16_t) (s[1] ^ 0x80) << 6) | (uint16_t) (s[2] ^ 0x80); + return 3; + } + + return -1; +} + +/** + * \internal Zamienia tekst kodowany UTF-8 na CP1250. + * + * \param b Tekst źródłowy w UTF-8. + * + * \return Zaalokowany bufor z tekstem w CP1250. + */ +char *gg_utf8_to_cp(const char *b) +{ + unsigned char *buf = (unsigned char *) b; + char *newbuf; + int newlen = 0; + int len; + int i, j; + + len = strlen(b); + + for (i = 0; i < len; newlen++) { + uint16_t discard; + int ret; + + ret = gg_utf8_helper(&buf[i], len - i, &discard); + + if (ret > 0) + i += ret; + else + i++; + } + + if (!(newbuf = malloc(newlen+1))) { + gg_debug(GG_DEBUG_MISC, "// gg_utf8_to_cp() not enough memory\n"); + return NULL; + } + + for (i = 0, j = 0; buf[i]; j++) { + uint16_t znak; + int ret, k; + + ret = gg_utf8_helper(&buf[i], len - i, &znak); + + if (ret > 0) { + i += ret; + } else { + znak = '?'; + i++; + } + + if (znak < 0x80) { + newbuf[j] = znak; + continue; + } + + newbuf[j] = '?'; + + for (k = 0; k < (sizeof(table_cp1250)/sizeof(table_cp1250[0])); k++) { + if (table_cp1250[k] == znak) { + newbuf[j] = (0x80 | k); + break; + } + } + } + newbuf[j] = '\0'; + + return newbuf; +} /* * Local variables:
--- a/libpurple/protocols/gg/lib/compat.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/gg/lib/compat.h Tue Mar 09 21:41:41 2010 +0000 @@ -1,8 +1,8 @@ -/* $Id: compat.h 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: compat.h 506 2008-01-14 22:15:05Z wojtekka $ */ /* * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Wony <speedy@ziew.org> + * Robert J. Woźny <speedy@ziew.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -15,10 +15,16 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ +/** + * \file compat.h + * + * \brief Makra zapewniające kompatybilność API na różnych systemach + */ + #ifndef __COMPAT_H #define __COMPAT_H
--- a/libpurple/protocols/gg/lib/dcc.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/gg/lib/dcc.c Tue Mar 09 21:41:41 2010 +0000 @@ -1,8 +1,9 @@ -/* $Id: dcc.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: dcc.c 711 2009-04-16 00:52:47Z darkjames $ */ /* - * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> - * Tomasz Chiliski <chilek@chilan.com> + * (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka@irc.pl> + * Tomasz Chiliński <chilek@chilan.com> + * Adam Wysocki <gophi@ekg.chmurka.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -15,22 +16,28 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ +/** + * \file dcc.c + * + * \brief Obsługa połączeń bezpośrednich do wersji Gadu-Gadu 6.x + */ #include "libgadu.h" #include <sys/types.h> #include <sys/stat.h> + #ifndef _WIN32 -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#ifdef sun -# include <sys/filio.h> -#endif +# include <sys/ioctl.h> +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# ifdef sun +# include <sys/filio.h> +# endif #endif #include <ctype.h> @@ -43,67 +50,71 @@ #include <unistd.h> #include "compat.h" + #ifndef GG_DEBUG_DISABLE -/* - * gg_dcc_debug_data() // funkcja wewntrzna + +/** + * \internal Przekazuje zawartość pakietu do odpluskwiania. * - * wywietla zrzut pakietu w hexie. - * - * - prefix - prefiks zrzutu pakietu - * - fd - deskryptor gniazda - * - buf - bufor z danymi - * - size - rozmiar danych + * \param prefix Prefiks informacji + * \param fd Deskryptor gniazda + * \param buf Bufor z danumi + * \param size Rozmiar bufora z danymi */ static void gg_dcc_debug_data(const char *prefix, int fd, const void *buf, unsigned int size) { unsigned int i; - + gg_debug(GG_DEBUG_MISC, "++ gg_dcc %s (fd=%d,len=%d)", prefix, fd, size); - + for (i = 0; i < size; i++) gg_debug(GG_DEBUG_MISC, " %.2x", ((unsigned char*) buf)[i]); - + gg_debug(GG_DEBUG_MISC, "\n"); } #else #define gg_dcc_debug_data(a,b,c,d) do { } while (0) #endif -/* +/** + * Wysyła żądanie zwrotnego połączenia bezpośredniego. + * + * Funkcję wykorzystuje się, jeśli nie ma możliwości połączenia się z odbiorcą + * pliku lub rozmowy głosowej. Po otrzymaniu żądania druga strona spróbuje + * nawiązać zwrotne połączenie bezpośrednie z nadawcą. * gg_dcc_request() * - * wysya informacj o tym, e dany klient powinien si z nami poczy. - * wykorzystywane, kiedy druga strona, ktrej chcemy co wysa jest za - * maskarad. + * \param sess Struktura sesji + * \param uin Numer odbiorcy * - * - sess - struktura opisujca sesj GG - * - uin - numerek odbiorcy + * \return Patrz \c gg_send_message_ctcp() * - * patrz gg_send_message_ctcp(). + * \ingroup dcc6 */ int gg_dcc_request(struct gg_session *sess, uin_t uin) { - return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (const unsigned char *)"\002", 1); + return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (unsigned char*) "\002", 1); } -/* - * gg_dcc_fill_filetime() // funkcja wewntrzna +/** + * \internal Zamienia znacznik czasu w postaci uniksowej na format API WIN32. * - * zamienia czas w postaci unixowej na windowsowy. + * \note Funkcja działa jedynie gdy kompilator obsługuje typ danych + * \c long \c long. * - * - unix - czas w postaci unixowej - * - filetime - czas w postaci windowsowej + * \param ut Czas w postaci uniksowej + * \param ft Czas w postaci API WIN32 */ static void gg_dcc_fill_filetime(uint32_t ut, uint32_t *ft) { -#ifdef __GG_LIBGADU_HAVE_LONG_LONG +#ifdef GG_CONFIG_HAVE_LONG_LONG unsigned long long tmp; tmp = ut; tmp += 11644473600LL; tmp *= 10000000LL; -#ifndef __GG_LIBGADU_BIGENDIAN +#ifndef GG_CONFIG_BIGENDIAN ft[0] = (uint32_t) tmp; ft[1] = (uint32_t) (tmp >> 32); #else @@ -114,31 +125,33 @@ #endif } -/* - * gg_dcc_fill_file_info() +/** + * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku. * - * wypenia pola struct gg_dcc niezbdne do wysania pliku. + * \note Większą funkcjonalność zapewnia funkcja \c gg_dcc_fill_file_info2(). * - * - d - struktura opisujca poczenie DCC - * - filename - nazwa pliku + * \param d Struktura połączenia + * \param filename Nazwa pliku * - * 0, -1. + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup dcc6 */ int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename) { return gg_dcc_fill_file_info2(d, filename, filename); } -/* - * gg_dcc_fill_file_info2() - * - * wypenia pola struct gg_dcc niezbdne do wysania pliku. +/** + * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku. * - * - d - struktura opisujca poczenie DCC - * - filename - nazwa pliku - * - local_filename - nazwa na lokalnym systemie plikw + * \param d Struktura połączenia + * \param filename Nazwa pliku zapisywana w strukturze + * \param local_filename Nazwa pliku w lokalnym systemie plików * - * 0, -1. + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup dcc6 */ int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename) { @@ -225,25 +238,23 @@ *q = 175; } } - + gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() short name \"%s\", dos name \"%s\"\n", name, d->file_info.short_filename); - strncpy((char *)d->file_info.filename, name, sizeof(d->file_info.filename) - 1); + strncpy((char*) d->file_info.filename, name, sizeof(d->file_info.filename) - 1); return 0; } -/* - * gg_dcc_transfer() // funkcja wewntrzna - * - * inicjuje proces wymiany pliku z danym klientem. +/** + * \internal Rozpoczyna połączenie bezpośrednie z danym klientem. * - * - ip - adres ip odbiorcy - * - port - port odbiorcy - * - my_uin - wasny numer - * - peer_uin - numer obiorcy - * - type - rodzaj wymiany (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_GET) + * \param ip Adres IP odbiorcy + * \param port Port odbiorcy + * \param my_uin Własny numer + * \param peer_uin Numer odbiorcy + * \param type Rodzaj połączenia (\c GG_SESSION_DCC_SEND lub \c GG_SESSION_DCC_GET) * - * zaalokowana struct gg_dcc lub NULL jeli wystpi bd. + * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu */ static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin, int type) { @@ -251,9 +262,9 @@ struct in_addr addr; addr.s_addr = ip; - + gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_transfer(%s, %d, %ld, %ld, %s);\n", inet_ntoa(addr), port, my_uin, peer_uin, (type == GG_SESSION_DCC_SEND) ? "SEND" : "GET"); - + if (!ip || ip == INADDR_NONE || !port || !my_uin || !peer_uin) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() invalid arguments\n"); errno = EINVAL; @@ -284,18 +295,17 @@ return d; } -/* - * gg_dcc_get_file() - * - * inicjuje proces odbierania pliku od danego klienta, gdy ten wysa do - * nas danie poczenia. +/** + * Rozpoczyna odbieranie pliku przez zwrotne połączenie bezpośrednie. * - * - ip - adres ip odbiorcy - * - port - port odbiorcy - * - my_uin - wasny numer - * - peer_uin - numer obiorcy + * \param ip Adres IP nadawcy + * \param port Port nadawcy + * \param my_uin Własny numer + * \param peer_uin Numer nadawcy * - * zaalokowana struct gg_dcc lub NULL jeli wystpi bd. + * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu + * + * \ingroup dcc6 */ struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) { @@ -304,17 +314,17 @@ return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_GET); } -/* - * gg_dcc_send_file() - * - * inicjuje proces wysyania pliku do danego klienta. +/** + * Rozpoczyna wysyłanie pliku. * - * - ip - adres ip odbiorcy - * - port - port odbiorcy - * - my_uin - wasny numer - * - peer_uin - numer obiorcy + * \param ip Adres IP odbiorcy + * \param port Port odbiorcy + * \param my_uin Własny numer + * \param peer_uin Numer odbiorcy * - * zaalokowana struct gg_dcc lub NULL jeli wystpi bd. + * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu + * + * \ingroup dcc6 */ struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) { @@ -323,17 +333,17 @@ return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_SEND); } -/* - * gg_dcc_voice_chat() - * - * prbuje nawiza poczenie gosowe. +/** + * Rozpoczyna połączenie głosowe. * - * - ip - adres ip odbiorcy - * - port - port odbiorcy - * - my_uin - wasny numer - * - peer_uin - numer obiorcy + * \param ip Adres IP odbiorcy + * \param port Port odbiorcy + * \param my_uin Własny numer + * \param peer_uin Numer odbiorcy * - * zaalokowana struct gg_dcc lub NULL jeli wystpi bd. + * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu + * + * \ingroup dcc6 */ struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin) { @@ -342,30 +352,34 @@ return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_VOICE); } -/* - * gg_dcc_set_type() +/** + * Ustawia typ przychodzącego połączenia bezpośredniego. + * + * Funkcję należy wywołać po otrzymaniu zdarzenia \c GG_EVENT_DCC_CALLBACK. * - * po zdarzeniu GG_EVENT_DCC_CALLBACK naley ustawi typ poczenia za - * pomoc tej funkcji. + * \param d Struktura połączenia + * \param type Rodzaj połączenia (\c GG_SESSION_DCC_SEND lub + * \c GG_SESSION_DCC_VOICE) * - * - d - struktura opisujca poczenie - * - type - typ poczenia (GG_SESSION_DCC_SEND lub GG_SESSION_DCC_VOICE) + * \ingroup dcc6 */ void gg_dcc_set_type(struct gg_dcc *d, int type) { d->type = type; d->state = (type == GG_SESSION_DCC_SEND) ? GG_STATE_SENDING_FILE_INFO : GG_STATE_SENDING_VOICE_REQUEST; } - -/* - * gg_dcc_callback() // funkcja wewntrzna + +/** + * \internal Funkcja zwrotna połączenia bezpośredniego. * - * wywoywana z struct gg_dcc->callback, odpala gg_dcc_watch_fd i umieszcza - * rezultat w struct gg_dcc->event. + * Pole \c callback struktury \c gg_dcc zawiera wskaźnik do tej funkcji. + * Wywołuje ona \c gg_watch_fd() i zachowuje wynik w polu \c event. * - * - d - structura opisujca poczenie + * \note Funkcjonalność funkcjo zwrotnej nie jest już wspierana. * - * 0, -1. + * \param d Struktura połączenia + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu */ static int gg_dcc_callback(struct gg_dcc *d) { @@ -376,25 +390,26 @@ return (e != NULL) ? 0 : -1; } -/* - * gg_dcc_socket_create() +/** + * Tworzy gniazdo nasłuchujące dla połączeń bezpośrednich. * - * tworzy gniazdo dla bezporedniej komunikacji midzy klientami. + * Funkcja przywiązuje gniazdo do pierwszego wolnego portu TCP. * - * - uin - wasny numer - * - port - preferowany port, jeli rwny 0 lub -1, prbuje domylnego + * \param uin Własny numer + * \param port Preferowany port (jeśli równy 0 lub -1, próbuje się domyślnego) * - * zaalokowana struct gg_dcc, ktr poniej naley zwolni funkcj - * gg_dcc_free(), albo NULL jeli wystpi bd. + * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu + * + * \ingroup dcc6 */ struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port) { struct gg_dcc *c; struct sockaddr_in sin; int sock, bound = 0, errno2; - + gg_debug(GG_DEBUG_FUNCTION, "** gg_create_dcc_socket(%d, %d);\n", uin, port); - + if (!uin) { gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() invalid arguments\n"); errno = EINVAL; @@ -408,12 +423,12 @@ if (!port) port = GG_DEFAULT_DCC_PORT; - + while (!bound) { sin.sin_family = AF_INET; sin.sin_addr.s_addr = INADDR_ANY; sin.sin_port = htons(port); - + gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() trying port %d\n", port); if (!bind(sock, (struct sockaddr*) &sin, sizeof(sin))) bound = 1; @@ -433,7 +448,7 @@ errno = errno2; return NULL; } - + gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() bound to port %d\n", port); if (!(c = malloc(sizeof(*c)))) { @@ -452,20 +467,20 @@ c->check = GG_CHECK_READ; c->callback = gg_dcc_callback; c->destroy = gg_dcc_free; - + return c; } -/* - * gg_dcc_voice_send() - * - * wysya ramk danych dla rozmowy gosowej. +/** + * Wysyła ramkę danych połączenia głosowego. * - * - d - struktura opisujca poczenie dcc - * - buf - bufor z danymi - * - length - rozmiar ramki + * \param d Struktura połączenia + * \param buf Bufor z danymi + * \param length Długość bufora z danymi * - * 0, -1. + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup dcc6 */ int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length) { @@ -500,7 +515,14 @@ return 0; } -#define gg_read(fd, buf, size) \ +/** + * \internal Odbiera dane z połączenia bezpośredniego z obsługą błędów. + * + * \param fd Deskryptor gniazda + * \param buf Bufor na dane + * \param size Rozmiar bufora na dane + */ +#define gg_dcc_read(fd, buf, size) \ { \ int tmp = read(fd, buf, size); \ \ @@ -517,9 +539,16 @@ return e; \ } \ gg_dcc_debug_data("read", fd, buf, size); \ -} +} -#define gg_write(fd, buf, size) \ +/** + * \internal Wysyła dane do połączenia bezpośredniego z obsługą błędów. + * + * \param fd Deskryptor gniazda + * \param buf Bufor z danymi + * \param size Rozmiar bufora z danymi + */ +#define gg_dcc_write(fd, buf, size) \ { \ int tmp; \ gg_dcc_debug_data("write", fd, buf, size); \ @@ -536,14 +565,18 @@ } \ } -/* - * gg_dcc_watch_fd() +/** + * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia. * - * funkcja, ktr naley wywoa, gdy co si zmieni na gg_dcc->fd. + * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia + * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania. + * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free. * - * - h - struktura zwrcona przez gg_create_dcc_socket() + * \param h Struktura połączenia * - * zaalokowana struct gg_event lub NULL, jeli zabrako pamici na ni. + * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd + * + * \ingroup dcc6 */ struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h) { @@ -551,7 +584,7 @@ int foo; gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_watch_fd(%p);\n", h); - + if (!h || (h->type != GG_SESSION_DCC && h->type != GG_SESSION_DCC_SOCKET && h->type != GG_SESSION_DCC_SEND && h->type != GG_SESSION_DCC_GET && h->type != GG_SESSION_DCC_VOICE)) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid argument\n"); errno = EINVAL; @@ -568,10 +601,9 @@ if (h->type == GG_SESSION_DCC_SOCKET) { struct sockaddr_in sin; struct gg_dcc *c; - int fd; - socklen_t sin_len = sizeof(sin); - int one = 1; - + int fd, one = 1; + unsigned int sin_len = sizeof(sin); + if ((fd = accept(h->fd, (struct sockaddr*) &sin, &sin_len)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't accept() new connection (errno=%d, %s)\n", errno, strerror(errno)); return e; @@ -607,7 +639,7 @@ c->file_fd = -1; c->remote_addr = sin.sin_addr.s_addr; c->remote_port = ntohs(sin.sin_port); - + e->type = GG_EVENT_DCC_NEW; e->event.dcc_new = c; @@ -617,8 +649,7 @@ struct gg_dcc_small_packet small; struct gg_dcc_big_packet big; int size, tmp, res; - socklen_t res_size = sizeof(res); - unsigned int utmp; + unsigned int utmp, res_size = sizeof(res); char buf[1024], ack[] = "UDAG"; struct gg_dcc_file_info_packet { @@ -634,8 +665,8 @@ uin_t uin; gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_READING_UIN_%d\n", (h->state == GG_STATE_READING_UIN_1) ? 1 : 2); - - gg_read(h->fd, &uin, sizeof(uin)); + + gg_dcc_read(h->fd, &uin, sizeof(uin)); if (h->state == GG_STATE_READING_UIN_1) { h->state = GG_STATE_READING_UIN_2; @@ -656,7 +687,7 @@ case GG_STATE_SENDING_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n"); - gg_write(h->fd, ack, 4); + gg_dcc_write(h->fd, ack, 4); h->state = GG_STATE_READING_TYPE; h->check = GG_CHECK_READ; @@ -666,8 +697,8 @@ case GG_STATE_READING_TYPE: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_TYPE\n"); - - gg_read(h->fd, &small, sizeof(small)); + + gg_dcc_read(h->fd, &small, sizeof(small)); small.type = gg_fix32(small.type); @@ -680,7 +711,7 @@ h->timeout = GG_DEFAULT_TIMEOUT; e->type = GG_EVENT_DCC_CALLBACK; - + break; case 0x0002: /* XXX */ @@ -703,8 +734,8 @@ case GG_STATE_READING_REQUEST: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_REQUEST\n"); - - gg_read(h->fd, &small, sizeof(small)); + + gg_dcc_read(h->fd, &small, sizeof(small)); small.type = gg_fix32(small.type); @@ -715,7 +746,7 @@ h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; break; - + case 0x0003: /* XXX */ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() voice chat request\n"); h->state = GG_STATE_SENDING_VOICE_ACK; @@ -725,22 +756,22 @@ e->type = GG_EVENT_DCC_NEED_VOICE_ACK; break; - + default: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc request (%.4x) from %ld\n", small.type, h->peer_uin); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; } - + return e; case GG_STATE_READING_FILE_INFO: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_INFO\n"); - - gg_read(h->fd, &file_info_packet, sizeof(file_info_packet)); + + gg_dcc_read(h->fd, &file_info_packet, sizeof(file_info_packet)); memcpy(&h->file_info, &file_info_packet.file_info, sizeof(h->file_info)); - + h->file_info.mode = gg_fix32(h->file_info.mode); h->file_info.size = gg_fix32(h->file_info.size); @@ -749,17 +780,17 @@ h->timeout = GG_DCC_TIMEOUT_FILE_ACK; e->type = GG_EVENT_DCC_NEED_FILE_ACK; - + return e; case GG_STATE_SENDING_FILE_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_ACK\n"); - + big.type = gg_fix32(0x0006); /* XXX */ big.dunno1 = gg_fix32(h->offset); big.dunno2 = 0; - gg_write(h->fd, &big, sizeof(big)); + gg_dcc_write(h->fd, &big, sizeof(big)); h->state = GG_STATE_READING_FILE_HEADER; h->chunk_size = sizeof(big); @@ -773,25 +804,25 @@ h->timeout = GG_DEFAULT_TIMEOUT; return e; - + case GG_STATE_SENDING_VOICE_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_ACK\n"); - + tiny.type = 0x01; /* XXX */ - gg_write(h->fd, &tiny, sizeof(tiny)); + gg_dcc_write(h->fd, &tiny, sizeof(tiny)); h->state = GG_STATE_READING_VOICE_HEADER; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; h->offset = 0; - + return e; - + case GG_STATE_READING_FILE_HEADER: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_HEADER\n"); - + tmp = read(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset); if (tmp == -1) { @@ -802,7 +833,7 @@ } gg_dcc_debug_data("read", h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset); - + h->chunk_offset += tmp; if (h->chunk_offset < h->chunk_size) @@ -823,7 +854,7 @@ return e; } - if (h->chunk_size == 0) { + if (h->chunk_size == 0) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() empty chunk, EOF\n"); e->type = GG_EVENT_DCC_DONE; return e; @@ -833,13 +864,13 @@ h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; h->established = 1; - + return e; case GG_STATE_READING_VOICE_HEADER: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_HEADER\n"); - - gg_read(h->fd, &tiny, sizeof(tiny)); + + gg_dcc_read(h->fd, &tiny, sizeof(tiny)); switch (tiny.type) { case 0x03: /* XXX */ @@ -850,19 +881,19 @@ break; case 0x04: /* XXX */ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() peer breaking connection\n"); - /* XXX zwraca odpowiedni event */ + /* XXX zwracać odpowiedni event */ default: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown request (%.2x)\n", tiny.type); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; } - + return e; case GG_STATE_READING_VOICE_SIZE: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_SIZE\n"); - - gg_read(h->fd, &small, sizeof(small)); + + gg_dcc_read(h->fd, &small, sizeof(small)); small.type = gg_fix32(small.type); @@ -870,7 +901,7 @@ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid voice frame size (%d)\n", small.type); e->type = GG_EVENT_DCC_ERROR; e->event.dcc_error = GG_ERROR_DCC_NET; - + return e; } @@ -879,18 +910,19 @@ if (!(h->voice_buf = malloc(h->chunk_size))) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory for voice frame\n"); + free(e); return NULL; } h->state = GG_STATE_READING_VOICE_DATA; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; - + return e; case GG_STATE_READING_VOICE_DATA: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_DATA\n"); - + tmp = read(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset); if (tmp < 1) { if (tmp == -1) { @@ -909,7 +941,7 @@ if (h->chunk_offset >= h->chunk_size) { e->type = GG_EVENT_DCC_VOICE_DATA; - e->event.dcc_voice_data.data = h->voice_buf; + e->event.dcc_voice_data.data = (unsigned char*) h->voice_buf; e->event.dcc_voice_data.length = h->chunk_size; h->state = GG_STATE_READING_VOICE_HEADER; h->voice_buf = NULL; @@ -917,7 +949,7 @@ h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; - + return e; case GG_STATE_CONNECTING: @@ -925,7 +957,7 @@ uin_t uins[2]; gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_CONNECTING\n"); - + res = 0; if ((foo = getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size)) || res) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connection failed (fd=%d,errno=%d(%s),foo=%d,res=%d(%s))\n", h->fd, errno, strerror(errno), foo, res, strerror(res)); @@ -935,23 +967,23 @@ } gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connected, sending uins\n"); - + uins[0] = gg_fix32(h->uin); uins[1] = gg_fix32(h->peer_uin); - gg_write(h->fd, uins, sizeof(uins)); - + gg_dcc_write(h->fd, uins, sizeof(uins)); + h->state = GG_STATE_READING_ACK; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; - + return e; } case GG_STATE_READING_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_ACK\n"); - - gg_read(h->fd, buf, 4); + + gg_dcc_read(h->fd, buf, 4); if (strncmp(buf, ack, 4)) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n"); @@ -964,29 +996,29 @@ h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; h->state = GG_STATE_SENDING_REQUEST; - + return e; case GG_STATE_SENDING_VOICE_REQUEST: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_REQUEST\n"); small.type = gg_fix32(0x0003); - - gg_write(h->fd, &small, sizeof(small)); + + gg_dcc_write(h->fd, &small, sizeof(small)); h->state = GG_STATE_READING_VOICE_ACK; h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; - + return e; - + case GG_STATE_SENDING_REQUEST: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_REQUEST\n"); small.type = (h->type == GG_SESSION_DCC_GET) ? gg_fix32(0x0003) : gg_fix32(0x0002); /* XXX */ - - gg_write(h->fd, &small, sizeof(small)); - + + gg_dcc_write(h->fd, &small, sizeof(small)); + switch (h->type) { case GG_SESSION_DCC_GET: h->state = GG_STATE_READING_REQUEST; @@ -1002,7 +1034,7 @@ if (h->file_fd == -1) e->type = GG_EVENT_DCC_NEED_FILE_INFO; break; - + case GG_SESSION_DCC_VOICE: h->state = GG_STATE_SENDING_VOICE_REQUEST; h->check = GG_CHECK_WRITE; @@ -1011,7 +1043,7 @@ } return e; - + case GG_STATE_SENDING_FILE_INFO: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n"); @@ -1021,8 +1053,8 @@ } small.type = gg_fix32(0x0001); /* XXX */ - - gg_write(h->fd, &small, sizeof(small)); + + gg_dcc_write(h->fd, &small, sizeof(small)); file_info_packet.big.type = gg_fix32(0x0003); /* XXX */ file_info_packet.big.dunno1 = 0; @@ -1030,26 +1062,26 @@ memcpy(&file_info_packet.file_info, &h->file_info, sizeof(h->file_info)); - /* zostaj teraz u nas, wic odwracamy z powrotem */ + /* zostają teraz u nas, więc odwracamy z powrotem */ h->file_info.size = gg_fix32(h->file_info.size); h->file_info.mode = gg_fix32(h->file_info.mode); - - gg_write(h->fd, &file_info_packet, sizeof(file_info_packet)); + + gg_dcc_write(h->fd, &file_info_packet, sizeof(file_info_packet)); h->state = GG_STATE_READING_FILE_ACK; h->check = GG_CHECK_READ; h->timeout = GG_DCC_TIMEOUT_FILE_ACK; return e; - + case GG_STATE_READING_FILE_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_ACK\n"); - - gg_read(h->fd, &big, sizeof(big)); + + gg_dcc_read(h->fd, &big, sizeof(big)); - /* XXX sprawdza wynik */ + /* XXX sprawdzać wynik */ h->offset = gg_fix32(big.dunno1); - + h->state = GG_STATE_SENDING_FILE_HEADER; h->check = GG_CHECK_WRITE; h->timeout = GG_DEFAULT_TIMEOUT; @@ -1060,8 +1092,8 @@ case GG_STATE_READING_VOICE_ACK: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_ACK\n"); - - gg_read(h->fd, &tiny, sizeof(tiny)); + + gg_dcc_read(h->fd, &tiny, sizeof(tiny)); if (tiny.type != 0x01) { gg_debug(GG_DEBUG_MISC, "// invalid reply (%.2x), connection refused\n", tiny.type); @@ -1075,14 +1107,14 @@ h->timeout = GG_DEFAULT_TIMEOUT; e->type = GG_EVENT_DCC_ACK; - + return e; case GG_STATE_SENDING_FILE_HEADER: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_HEADER\n"); - + h->chunk_offset = 0; - + if ((h->chunk_size = h->file_info.size - h->offset) > 4096) { h->chunk_size = 4096; big.type = gg_fix32(0x0003); /* XXX */ @@ -1091,8 +1123,8 @@ big.dunno1 = gg_fix32(h->chunk_size); big.dunno2 = 0; - - gg_write(h->fd, &big, sizeof(big)); + + gg_dcc_write(h->fd, &big, sizeof(big)); h->state = GG_STATE_SENDING_FILE; h->check = GG_CHECK_WRITE; @@ -1100,13 +1132,13 @@ h->established = 1; return e; - + case GG_STATE_SENDING_FILE: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE\n"); - + if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf)) utmp = sizeof(buf); - + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset=%d, size=%d\n", h->offset, h->file_info.size); /* koniec pliku? */ @@ -1117,11 +1149,17 @@ return e; } + if (h->offset >= h->file_info.size) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n"); + e->type = GG_EVENT_DCC_DONE; + return e; + } + lseek(h->file_fd, h->offset, SEEK_SET); size = read(h->file_fd, buf, utmp); - /* bd */ + /* błąd */ if (size == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno)); @@ -1139,8 +1177,8 @@ return e; } - - /* jeli wczytalimy wicej, utnijmy. */ + + /* jeśli wczytaliśmy więcej, utnijmy. */ if (h->offset + size > h->file_info.size) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() too much (read=%d, ofs=%d, size=%d)\n", size, h->offset, h->file_info.size); size = h->file_info.size - h->offset; @@ -1161,15 +1199,22 @@ return e; } - h->offset += size; - + if (tmp == 0) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (connection reset)\n"); + e->type = GG_EVENT_DCC_ERROR; + e->event.dcc_error = GG_ERROR_DCC_NET; + return e; + } + + h->offset += tmp; + if (h->offset >= h->file_info.size) { e->type = GG_EVENT_DCC_DONE; return e; } - - h->chunk_offset += size; - + + h->chunk_offset += tmp; + if (h->chunk_offset >= h->chunk_size) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n"); h->state = GG_STATE_SENDING_FILE_HEADER; @@ -1178,22 +1223,28 @@ h->state = GG_STATE_SENDING_FILE; h->timeout = GG_DCC_TIMEOUT_SEND; } - + h->check = GG_CHECK_WRITE; return e; case GG_STATE_GETTING_FILE: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_GETTING_FILE\n"); - + if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf)) utmp = sizeof(buf); - + + if (h->offset >= h->file_info.size) { + gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n"); + e->type = GG_EVENT_DCC_DONE; + return e; + } + size = read(h->fd, buf, utmp); gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() ofs=%d, size=%d, read()=%d\n", h->offset, h->file_info.size, size); - - /* bd */ + + /* błąd */ if (size == -1) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno)); @@ -1211,9 +1262,9 @@ return e; } - + tmp = write(h->file_fd, buf, size); - + if (tmp == -1 || tmp < size) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d:fd=%d:res=%d:%s)\n", tmp, h->file_fd, size, strerror(errno)); e->type = GG_EVENT_DCC_ERROR; @@ -1222,14 +1273,14 @@ } h->offset += size; - + if (h->offset >= h->file_info.size) { e->type = GG_EVENT_DCC_DONE; return e; } h->chunk_offset += size; - + if (h->chunk_offset >= h->chunk_size) { gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n"); h->state = GG_STATE_READING_FILE_HEADER; @@ -1245,11 +1296,11 @@ h->state = GG_STATE_GETTING_FILE; h->timeout = GG_DCC_TIMEOUT_GET; } - + h->check = GG_CHECK_READ; return e; - + default: gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_???\n"); e->type = GG_EVENT_DCC_ERROR; @@ -1258,35 +1309,28 @@ return e; } } - + return e; } -#undef gg_read -#undef gg_write - -/* - * gg_dcc_free() +/** + * Zwalnia zasoby używane przez połączenie bezpośrednie. * - * zwalnia pami po strukturze poczenia dcc. + * \param d Struktura połączenia * - * - d - zwalniana struktura + * \ingroup dcc6 */ void gg_dcc_free(struct gg_dcc *d) { gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_free(%p);\n", d); - + if (!d) return; if (d->fd != -1) close(d->fd); - if (d->chunk_buf) { - free(d->chunk_buf); - d->chunk_buf = NULL; - } - + free(d->chunk_buf); free(d); }
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/dcc7.c Tue Mar 09 21:41:41 2010 +0000 @@ -0,0 +1,1217 @@ +/* $Id: dcc7.c 711 2009-04-16 00:52:47Z darkjames $ */ + +/* + * (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka@irc.pl> + * Tomasz Chiliński <chilek@chilan.com> + * Adam Wysocki <gophi@ekg.chmurka.net> + * + * Thanks to Jakub Zawadzki <darkjames@darkjames.ath.cx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, + * USA. + */ + +/** + * \file dcc7.c + * + * \brief Obsługa połączeń bezpośrednich od wersji Gadu-Gadu 7.x + */ + +#include "libgadu.h" + +#include <sys/types.h> +#include <sys/stat.h> + +#ifndef _WIN32 +# include <sys/ioctl.h> +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# ifdef sun +# include <sys/filio.h> +# endif +#endif + +#include <time.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <string.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include "compat.h" + +#define gg_debug_dcc(dcc, fmt...) \ + gg_debug_session((dcc) ? (dcc)->sess : NULL, fmt) + +/** + * \internal Dodaje połączenie bezpośrednie do sesji. + * + * \param sess Struktura sesji + * \param dcc Struktura połączenia + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +static int gg_dcc7_session_add(struct gg_session *sess, struct gg_dcc7 *dcc) +{ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_add(%p, %p)\n", sess, dcc); + + if (!sess || !dcc || dcc->next) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n"); + errno = EINVAL; + return -1; + } + + dcc->next = sess->dcc7_list; + sess->dcc7_list = dcc; + + return 0; +} + +/** + * \internal Usuwa połączenie bezpośrednie z sesji. + * + * \param sess Struktura sesji + * \param dcc Struktura połączenia + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +static int gg_dcc7_session_remove(struct gg_session *sess, struct gg_dcc7 *dcc) +{ + struct gg_dcc7 *tmp; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_remove(%p, %p)\n", sess, dcc); + + if (!sess || !dcc) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n"); + errno = EINVAL; + return -1; + } + + if (sess->dcc7_list == dcc) { + sess->dcc7_list = dcc->next; + dcc->next = NULL; + return 0; + } + + for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) { + if (tmp->next == dcc) { + tmp = dcc->next; + dcc->next = NULL; + return 0; + } + } + + errno = ENOENT; + return -1; +} + +/** + * \internal Zwraca strukturę połączenia o danym identyfikatorze. + * + * \param sess Struktura sesji + * \param id Identyfikator połączenia + * \param uin Numer nadawcy lub odbiorcy + * + * \return Struktura połączenia lub \c NULL jeśli nie znaleziono + */ +static struct gg_dcc7 *gg_dcc7_session_find(struct gg_session *sess, gg_dcc7_id_t id, uin_t uin) +{ + struct gg_dcc7 *tmp; + int empty; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_find(%p, ..., %d)\n", sess, (int) uin); + + empty = !memcmp(&id, "\0\0\0\0\0\0\0\0", 8); + + for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) { + if (empty) { + if (tmp->peer_uin == uin && !tmp->state == GG_STATE_WAITING_FOR_ACCEPT) + return tmp; + } else { + if (!memcmp(&tmp->cid, &id, sizeof(id))) + return tmp; + } + } + + return NULL; +} + +/** + * \internal Nawiązuje połączenie bezpośrednie + * + * \param sess Struktura sesji + * \param dcc Struktura połączenia + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +static int gg_dcc7_connect(struct gg_session *sess, struct gg_dcc7 *dcc) +{ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p, %p)\n", sess, dcc); + + if (!sess || !dcc) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_connect() invalid parameters\n"); + errno = EINVAL; + return -1; + } + + if ((dcc->fd = gg_connect(&dcc->remote_addr, dcc->remote_port, 1)) == -1) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_connect() connection failed\n"); + return -1; + } + + dcc->state = GG_STATE_CONNECTING; + dcc->check = GG_CHECK_WRITE; + dcc->timeout = GG_DCC7_TIMEOUT_CONNECT; + dcc->soft_timeout = 1; + + return 0; +} + +/** + * \internal Tworzy gniazdo nasłuchujące dla połączenia bezpośredniego + * + * \param dcc Struktura połączenia + * \param port Preferowany port (jeśli równy 0 lub -1, próbuje się domyślnego) + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +static int gg_dcc7_listen(struct gg_dcc7 *dcc, uint16_t port) +{ + struct sockaddr_in sin; + int fd; + + gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen(%p, %d)\n", dcc, port); + + if (!dcc) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() invalid parameters\n"); + errno = EINVAL; + return -1; + } + + if ((fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() can't create socket (%s)\n", strerror(errno)); + return -1; + } + + // XXX losować porty? + + if (!port) + port = GG_DEFAULT_DCC_PORT; + + while (1) { + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = INADDR_ANY; + sin.sin_port = htons(port); + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() trying port %d\n", port); + + if (!bind(fd, (struct sockaddr*) &sin, sizeof(sin))) + break; + + if (port++ == 65535) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() no free port found\n"); + close(fd); + errno = ENOENT; + return -1; + } + } + + if (listen(fd, 1)) { + int errsv = errno; + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to listen (%s)\n", strerror(errno)); + close(fd); + errno = errsv; + return -1; + } + + dcc->fd = fd; + dcc->local_port = port; + + dcc->state = GG_STATE_LISTENING; + dcc->check = GG_CHECK_READ; + dcc->timeout = GG_DCC7_TIMEOUT_FILE_ACK; + + return 0; +} + +/** + * \internal Tworzy gniazdo nasłuchujące i wysyła jego parametry + * + * \param dcc Struktura połączenia + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +static int gg_dcc7_listen_and_send_info(struct gg_dcc7 *dcc) +{ + struct gg_dcc7_info pkt; + + gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc); + + // XXX dać możliwość konfiguracji? + + dcc->local_addr = dcc->sess->client_addr; + + if (gg_dcc7_listen(dcc, 0) == -1) + return -1; + + memset(&pkt, 0, sizeof(pkt)); + pkt.uin = gg_fix32(dcc->peer_uin); + pkt.type = GG_DCC7_TYPE_P2P; + pkt.id = dcc->cid; + snprintf((char*) pkt.info, sizeof(pkt.info), "%s %d", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), dcc->local_port); + + return gg_send_packet(dcc->sess, GG_DCC7_INFO, &pkt, sizeof(pkt), NULL); +} + +/** + * \internal Odwraca połączenie po nieudanym connect() + * + * \param dcc Struktura połączenia + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +static int gg_dcc7_reverse_connect(struct gg_dcc7 *dcc) +{ + gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reverse_connect(%p)\n", dcc); + + if (dcc->reverse) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() already reverse connection\n"); + return -1; + } + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() timeout, trying reverse connection\n"); + close(dcc->fd); + dcc->fd = -1; + dcc->reverse = 1; + + return gg_dcc7_listen_and_send_info(dcc); +} + +/** + * \internal Wysyła do serwera żądanie nadania identyfikatora sesji + * + * \param sess Struktura sesji + * \param type Rodzaj połączenia (\c GG_DCC7_TYPE_FILE lub \c GG_DCC7_TYPE_VOICE) + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +static int gg_dcc7_request_id(struct gg_session *sess, uint32_t type) +{ + struct gg_dcc7_id_request pkt; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_request_id(%p, %d)\n", sess, type); + + if (!sess) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid parameters\n"); + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() not connected\n"); + errno = ENOTCONN; + return -1; + } + + if (type != GG_DCC7_TYPE_VOICE && type != GG_DCC7_TYPE_FILE) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_request_id() invalid transfer type (%d)\n", type); + errno = EINVAL; + return -1; + } + + memset(&pkt, 0, sizeof(pkt)); + pkt.type = gg_fix32(type); + + return gg_send_packet(sess, GG_DCC7_ID_REQUEST, &pkt, sizeof(pkt), NULL); +} + +/** + * \internal Rozpoczyna wysyłanie pliku. + * + * Funkcja jest wykorzystywana przez \c gg_dcc7_send_file() oraz + * \c gg_dcc_send_file_fd(). + * + * \param sess Struktura sesji + * \param rcpt Numer odbiorcy + * \param fd Deskryptor pliku + * \param size Rozmiar pliku + * \param filename1250 Nazwa pliku w kodowaniu CP-1250 + * \param hash Skrót SHA-1 pliku + * \param seek Flaga mówiąca, czy można używać lseek() + * + * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu + * + * \ingroup dcc7 + */ +static struct gg_dcc7 *gg_dcc7_send_file_common(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash, int seek) +{ + struct gg_dcc7 *dcc = NULL; + + if (!sess || !rcpt || !filename1250 || !hash || fd == -1) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() invalid parameters\n"); + errno = EINVAL; + goto fail; + } + + if (!(dcc = malloc(sizeof(struct gg_dcc7)))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file_common() not enough memory\n"); + goto fail; + } + + if (gg_dcc7_request_id(sess, GG_DCC7_TYPE_FILE) == -1) + goto fail; + + memset(dcc, 0, sizeof(struct gg_dcc7)); + dcc->type = GG_SESSION_DCC7_SEND; + dcc->dcc_type = GG_DCC7_TYPE_FILE; + dcc->state = GG_STATE_REQUESTING_ID; + dcc->timeout = GG_DEFAULT_TIMEOUT; + dcc->sess = sess; + dcc->fd = -1; + dcc->uin = sess->uin; + dcc->peer_uin = rcpt; + dcc->file_fd = fd; + dcc->size = size; + dcc->seek = seek; + + strncpy((char*) dcc->filename, filename1250, GG_DCC7_FILENAME_LEN - 1); + dcc->filename[GG_DCC7_FILENAME_LEN] = 0; + + memcpy(dcc->hash, hash, GG_DCC7_HASH_LEN); + + if (gg_dcc7_session_add(sess, dcc) == -1) + goto fail; + + return dcc; + +fail: + free(dcc); + return NULL; +} + +/** + * Rozpoczyna wysyłanie pliku o danej nazwie. + * + * \param sess Struktura sesji + * \param rcpt Numer odbiorcy + * \param filename Nazwa pliku w lokalnym systemie plików + * \param filename1250 Nazwa pliku w kodowaniu CP-1250 + * \param hash Skrót SHA-1 pliku (lub \c NULL jeśli ma być wyznaczony) + * + * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu + * + * \ingroup dcc7 + */ +struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash) +{ + struct gg_dcc7 *dcc = NULL; + const char *tmp; + char hash_buf[GG_DCC7_HASH_LEN]; + struct stat st; + int fd = -1; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file(%p, %d, \"%s\", %p)\n", sess, rcpt, filename, hash); + + if (!sess || !rcpt || !filename) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() invalid parameters\n"); + errno = EINVAL; + goto fail; + } + + if (!filename1250) + filename1250 = filename; + + if (stat(filename, &st) == -1) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() stat() failed (%s)\n", strerror(errno)); + goto fail; + } + + if ((st.st_mode & S_IFDIR)) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() that's a directory\n"); + errno = EINVAL; + goto fail; + } + + if ((fd = open(filename, O_RDONLY)) == -1) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_send_file() open() failed (%s)\n", strerror(errno)); + goto fail; + } + + if (!hash) { + if (gg_file_hash_sha1(fd, (uint8_t*) hash_buf) == -1) + goto fail; + + hash = hash_buf; + } + + if ((tmp = strrchr(filename1250, '/'))) + filename1250 = tmp + 1; + + if (!(dcc = gg_dcc7_send_file_common(sess, rcpt, fd, st.st_size, filename1250, hash, 1))) + goto fail; + + return dcc; + +fail: + if (fd != -1) { + int errsv = errno; + close(fd); + errno = errsv; + } + + free(dcc); + return NULL; +} + +/** + * \internal Rozpoczyna wysyłanie pliku o danym deskryptorze. + * + * \note Wysyłanie pliku nie będzie działać poprawnie, jeśli deskryptor + * źródłowy jest w trybie nieblokującym i w pewnym momencie zabraknie danych. + * + * \param sess Struktura sesji + * \param rcpt Numer odbiorcy + * \param fd Deskryptor pliku + * \param size Rozmiar pliku + * \param filename1250 Nazwa pliku w kodowaniu CP-1250 + * \param hash Skrót SHA-1 pliku + * + * \return Struktura \c gg_dcc7 lub \c NULL w przypadku błędu + * + * \ingroup dcc7 + */ +struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash) +{ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_send_file_fd(%p, %d, %d, %u, \"%s\", %p)\n", sess, rcpt, fd, size, filename1250, hash); + + return gg_dcc7_send_file_common(sess, rcpt, fd, size, filename1250, hash, 0); +} + + +/** + * Potwierdza chęć odebrania pliku. + * + * \param dcc Struktura połączenia + * \param offset Początkowy offset przy wznawianiu przesyłania pliku + * + * \note Biblioteka nie zmienia położenia w odbieranych plikach. Jeśli offset + * początkowy jest różny od zera, należy ustawić go funkcją \c lseek() lub + * podobną. + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup dcc7 + */ +int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset) +{ + struct gg_dcc7_accept pkt; + + gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_accept(%p, %d)\n", dcc, offset); + + if (!dcc || !dcc->sess) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_accept() invalid parameters\n"); + errno = EFAULT; + return -1; + } + + memset(&pkt, 0, sizeof(pkt)); + pkt.uin = gg_fix32(dcc->peer_uin); + pkt.id = dcc->cid; + pkt.offset = gg_fix32(offset); + + if (gg_send_packet(dcc->sess, GG_DCC7_ACCEPT, &pkt, sizeof(pkt), NULL) == -1) + return -1; + + dcc->offset = offset; + + return gg_dcc7_listen_and_send_info(dcc); +} + +/** + * Odrzuca próbę przesłania pliku. + * + * \param dcc Struktura połączenia + * \param reason Powód odrzucenia + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup dcc7 + */ +int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason) +{ + struct gg_dcc7_reject pkt; + + gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_reject(%p, %d)\n", dcc, reason); + + if (!dcc || !dcc->sess) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_reject() invalid parameters\n"); + errno = EFAULT; + return -1; + } + + memset(&pkt, 0, sizeof(pkt)); + pkt.uin = gg_fix32(dcc->peer_uin); + pkt.id = dcc->cid; + pkt.reason = gg_fix32(reason); + + return gg_send_packet(dcc->sess, GG_DCC7_REJECT, &pkt, sizeof(pkt), NULL); +} + +/** + * \internal Obsługuje pakiet identyfikatora połączenia bezpośredniego. + * + * \param sess Struktura sesji + * \param e Struktura zdarzenia + * \param payload Treść pakietu + * \param len Długość pakietu + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len) +{ + struct gg_dcc7_id_reply *p = payload; + struct gg_dcc7 *tmp; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_id(%p, %p, %p, %d)\n", sess, e, payload, len); + + for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) { + gg_debug_session(sess, GG_DEBUG_MISC, "// checking dcc %p, state %d, type %d\n", tmp, tmp->state, tmp->dcc_type); + + if (tmp->state != GG_STATE_REQUESTING_ID || tmp->dcc_type != gg_fix32(p->type)) + continue; + + tmp->cid = p->id; + + switch (tmp->dcc_type) { + case GG_DCC7_TYPE_FILE: + { + struct gg_dcc7_new s; + + memset(&s, 0, sizeof(s)); + s.id = tmp->cid; + s.type = gg_fix32(GG_DCC7_TYPE_FILE); + s.uin_from = gg_fix32(tmp->uin); + s.uin_to = gg_fix32(tmp->peer_uin); + s.size = gg_fix32(tmp->size); + + strncpy((char*) s.filename, (char*) tmp->filename, GG_DCC7_FILENAME_LEN); + + tmp->state = GG_STATE_WAITING_FOR_ACCEPT; + tmp->timeout = GG_DCC7_TIMEOUT_FILE_ACK; + + return gg_send_packet(sess, GG_DCC7_NEW, &s, sizeof(s), NULL); + } + } + } + + return 0; +} + +/** + * \internal Obsługuje pakiet akceptacji połączenia bezpośredniego. + * + * \param sess Struktura sesji + * \param e Struktura zdarzenia + * \param payload Treść pakietu + * \param len Długość pakietu + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len) +{ + struct gg_dcc7_accept *p = payload; + struct gg_dcc7 *dcc; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_accept(%p, %p, %p, %d)\n", sess, e, payload, len); + + if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() unknown dcc session\n"); + // XXX wysłać reject? + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; + return 0; + } + + if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_accept() invalid state\n"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; + return 0; + } + + // XXX czy dla odwrotnego połączenia powinniśmy wywołać już zdarzenie GG_DCC7_ACCEPT? + + dcc->offset = gg_fix32(p->offset); + dcc->state = GG_STATE_WAITING_FOR_INFO; + + return 0; +} + +/** + * \internal Obsługuje pakiet informacji o połączeniu bezpośrednim. + * + * \param sess Struktura sesji + * \param e Struktura zdarzenia + * \param payload Treść pakietu + * \param len Długość pakietu + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len) +{ + struct gg_dcc7_info *p = payload; + struct gg_dcc7 *dcc; + char *tmp; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_info(%p, %p, %p, %d)\n", sess, e, payload, len); + + if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unknown dcc session\n"); + return 0; + } + + if (p->type != GG_DCC7_TYPE_P2P) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unhandled transfer type (%d)\n", p->type); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; + return 0; + } + + if ((dcc->remote_addr = inet_addr(p->info)) == INADDR_NONE) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP address\n"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; + return 0; + } + + if (!(tmp = strchr(p->info, ' ')) || !(dcc->remote_port = atoi(tmp + 1))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP port\n"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; + return 0; + } + + // jeśli nadal czekamy na połączenie przychodzące, a druga strona nie + // daje rady i oferuje namiary na siebie, bierzemy co dają. + + if (dcc->state != GG_STATE_WAITING_FOR_INFO && (dcc->state != GG_STATE_LISTENING || dcc->reverse)) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid state\n"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; + return 0; + } + + if (dcc->state == GG_STATE_LISTENING) { + close(dcc->fd); + dcc->fd = -1; + dcc->reverse = 1; + } + + if (dcc->type == GG_SESSION_DCC7_SEND) { + e->type = GG_EVENT_DCC7_ACCEPT; + e->event.dcc7_accept.dcc7 = dcc; + e->event.dcc7_accept.type = gg_fix32(p->type); + e->event.dcc7_accept.remote_ip = dcc->remote_addr; + e->event.dcc7_accept.remote_port = dcc->remote_port; + } else { + e->type = GG_EVENT_DCC7_PENDING; + } + + if (gg_dcc7_connect(sess, dcc) == -1) { + if (gg_dcc7_reverse_connect(dcc) == -1) { + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc7_error = GG_ERROR_DCC7_NET; + return 0; + } + } + + return 0; +} + +/** + * \internal Obsługuje pakiet odrzucenia połączenia bezpośredniego. + * + * \param sess Struktura sesji + * \param e Struktura zdarzenia + * \param payload Treść pakietu + * \param len Długość pakietu + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len) +{ + struct gg_dcc7_reject *p = payload; + struct gg_dcc7 *dcc; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_reject(%p, %p, %p, %d)\n", sess, e, payload, len); + + if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() unknown dcc session\n"); + return 0; + } + + if (dcc->state != GG_STATE_WAITING_FOR_ACCEPT) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_reject() invalid state\n"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE; + return 0; + } + + e->type = GG_EVENT_DCC7_REJECT; + e->event.dcc7_reject.dcc7 = dcc; + e->event.dcc7_reject.reason = gg_fix32(p->reason); + + // XXX ustawić state na rejected? + + return 0; +} + +/** + * \internal Obsługuje pakiet nowego połączenia bezpośredniego. + * + * \param sess Struktura sesji + * \param e Struktura zdarzenia + * \param payload Treść pakietu + * \param len Długość pakietu + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len) +{ + struct gg_dcc7_new *p = payload; + struct gg_dcc7 *dcc; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_new(%p, %p, %p, %d)\n", sess, e, payload, len); + + switch (gg_fix32(p->type)) { + case GG_DCC7_TYPE_FILE: + if (!(dcc = malloc(sizeof(struct gg_dcc7)))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() not enough memory\n"); + return -1; + } + + memset(dcc, 0, sizeof(struct gg_dcc7)); + dcc->type = GG_SESSION_DCC7_GET; + dcc->dcc_type = GG_DCC7_TYPE_FILE; + dcc->fd = -1; + dcc->file_fd = -1; + dcc->uin = sess->uin; + dcc->peer_uin = gg_fix32(p->uin_from); + dcc->cid = p->id; + dcc->sess = sess; + + if (gg_dcc7_session_add(sess, dcc) == -1) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n"); + gg_dcc7_free(dcc); + return -1; + } + + dcc->size = gg_fix32(p->size); + strncpy((char*) dcc->filename, (char*) p->filename, GG_DCC7_FILENAME_LEN - 1); + dcc->filename[GG_DCC7_FILENAME_LEN] = 0; + memcpy(dcc->hash, p->hash, GG_DCC7_HASH_LEN); + + e->type = GG_EVENT_DCC7_NEW; + e->event.dcc7_new = dcc; + + break; + + case GG_DCC7_TYPE_VOICE: + if (!(dcc = malloc(sizeof(struct gg_dcc7)))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_packet() not enough memory\n"); + return -1; + } + + memset(dcc, 0, sizeof(struct gg_dcc7)); + + dcc->type = GG_SESSION_DCC7_VOICE; + dcc->dcc_type = GG_DCC7_TYPE_VOICE; + dcc->fd = -1; + dcc->file_fd = -1; + dcc->uin = sess->uin; + dcc->peer_uin = gg_fix32(p->uin_from); + dcc->cid = p->id; + dcc->sess = sess; + + if (gg_dcc7_session_add(sess, dcc) == -1) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unable to add to session\n"); + gg_dcc7_free(dcc); + return -1; + } + + e->type = GG_EVENT_DCC7_NEW; + e->event.dcc7_new = dcc; + + break; + + default: + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_new() unknown dcc type (%d) from %ld\n", gg_fix32(p->type), gg_fix32(p->uin_from)); + + break; + } + + return 0; +} + +/** + * \internal Ustawia odpowiednie stany wewnętrzne w zależności od rodzaju + * połączenia. + * + * \param dcc Struktura połączenia + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu. + */ +static int gg_dcc7_postauth_fixup(struct gg_dcc7 *dcc) +{ + gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_postauth_fixup(%p)\n", dcc); + + if (!dcc) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_postauth_fixup() invalid parameters\n"); + errno = EINVAL; + return -1; + } + + switch (dcc->type) { + case GG_SESSION_DCC7_GET: + dcc->state = GG_STATE_GETTING_FILE; + dcc->check = GG_CHECK_READ; + return 0; + + case GG_SESSION_DCC7_SEND: + dcc->state = GG_STATE_SENDING_FILE; + dcc->check = GG_CHECK_WRITE; + return 0; + + case GG_SESSION_DCC7_VOICE: + dcc->state = GG_STATE_READING_VOICE_DATA; + dcc->check = GG_CHECK_READ; + return 0; + } + + errno = EINVAL; + + return -1; +} + +/** + * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia. + * + * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia + * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania. + * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free(). + * + * \param dcc Struktura połączenia + * + * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd + * + * \ingroup dcc7 + */ +struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *dcc) +{ + struct gg_event *e; + + gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_watch_fd(%p)\n", dcc); + + if (!dcc || (dcc->type != GG_SESSION_DCC7_SEND && dcc->type != GG_SESSION_DCC7_GET && dcc->type != GG_SESSION_DCC7_VOICE)) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid parameters\n"); + errno = EINVAL; + return NULL; + } + + if (!(e = malloc(sizeof(struct gg_event)))) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory\n"); + return NULL; + } + + memset(e, 0, sizeof(struct gg_event)); + e->type = GG_EVENT_NONE; + + switch (dcc->state) { + case GG_STATE_LISTENING: + { + struct sockaddr_in sin; + int fd, one = 1; + socklen_t sin_len = sizeof(sin); + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_LISTENING\n"); + + if ((fd = accept(dcc->fd, (struct sockaddr*) &sin, &sin_len)) == -1) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() accept() failed (%s)\n", strerror(errno)); + return e; + } + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port)); + +#ifdef FIONBIO + if (ioctl(fd, FIONBIO, &one) == -1) { +#else + if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { +#endif + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() can't set nonblocking (%s)\n", strerror(errno)); + close(fd); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; + return e; + } + + close(dcc->fd); + dcc->fd = fd; + + dcc->state = GG_STATE_READING_ID; + dcc->check = GG_CHECK_READ; + dcc->timeout = GG_DEFAULT_TIMEOUT; + dcc->incoming = 1; + + dcc->remote_port = ntohs(sin.sin_port); + dcc->remote_addr = sin.sin_addr.s_addr; + + e->type = GG_EVENT_DCC7_CONNECTED; + e->event.dcc7_connected.dcc7 = dcc; + + return e; + } + + case GG_STATE_CONNECTING: + { + int res = 0, error = 0; + unsigned int error_size = sizeof(error); + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n"); + + dcc->soft_timeout = 0; + + if (dcc->timeout == 0) + error = ETIMEDOUT; + + if (error || (res = getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &error, &error_size)) == -1 || error != 0) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error)); + + if (gg_dcc7_reverse_connect(dcc) != -1) { + e->type = GG_EVENT_DCC7_PENDING; + } else { + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_NET; + } + + return e; + } + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connected, sending id\n"); + + dcc->state = GG_STATE_SENDING_ID; + dcc->check = GG_CHECK_WRITE; + dcc->timeout = GG_DEFAULT_TIMEOUT; + dcc->incoming = 0; + + return e; + } + + case GG_STATE_READING_ID: + { + gg_dcc7_id_t id; + int res; + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n"); + + if ((res = read(dcc->fd, &id, sizeof(id))) != sizeof(id)) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno)); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; + return e; + } + + if (memcmp(&id, &dcc->cid, sizeof(id))) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; + return e; + } + + if (dcc->incoming) { + dcc->state = GG_STATE_SENDING_ID; + dcc->check = GG_CHECK_WRITE; + dcc->timeout = GG_DEFAULT_TIMEOUT; + } else { + gg_dcc7_postauth_fixup(dcc); + dcc->timeout = GG_DEFAULT_TIMEOUT; + } + + return e; + } + + case GG_STATE_SENDING_ID: + { + int res; + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n"); + + if ((res = write(dcc->fd, &dcc->cid, sizeof(dcc->cid))) != sizeof(dcc->cid)) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)", res, strerror(errno)); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; + return e; + } + + if (dcc->incoming) { + gg_dcc7_postauth_fixup(dcc); + dcc->timeout = GG_DEFAULT_TIMEOUT; + } else { + dcc->state = GG_STATE_READING_ID; + dcc->check = GG_CHECK_READ; + dcc->timeout = GG_DEFAULT_TIMEOUT; + } + + return e; + } + + case GG_STATE_SENDING_FILE: + { + char buf[1024]; + int chunk, res; + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_SENDING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size); + + if (dcc->offset >= dcc->size) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() offset >= size, finished\n"); + e->type = GG_EVENT_DCC7_DONE; + return e; + } + + if (dcc->seek && lseek(dcc->file_fd, dcc->offset, SEEK_SET) == (off_t) -1) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() lseek() failed (%s)\n", strerror(errno)); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_FILE; + return e; + } + + if ((chunk = dcc->size - dcc->offset) > sizeof(buf)) + chunk = sizeof(buf); + + if ((res = read(dcc->file_fd, buf, chunk)) < 1) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (res=%d, %s)\n", res, strerror(errno)); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_FILE : GG_ERROR_DCC7_EOF; + return e; + } + + if ((res = write(dcc->fd, buf, res)) == -1) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%s)\n", strerror(errno)); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_NET; + return e; + } + + dcc->offset += res; + + if (dcc->offset >= dcc->size) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n"); + e->type = GG_EVENT_DCC7_DONE; + return e; + } + + dcc->state = GG_STATE_SENDING_FILE; + dcc->check = GG_CHECK_WRITE; + dcc->timeout = GG_DCC7_TIMEOUT_SEND; + + return e; + } + + case GG_STATE_GETTING_FILE: + { + char buf[1024]; + int res, wres; + + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_GETTING_FILE (offset=%d, size=%d)\n", dcc->offset, dcc->size); + + if (dcc->offset >= dcc->size) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n"); + e->type = GG_EVENT_DCC7_DONE; + return e; + } + + if ((res = read(dcc->fd, buf, sizeof(buf))) < 1) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (fd=%d, res=%d, %s)\n", dcc->fd, res, strerror(errno)); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = (res == -1) ? GG_ERROR_DCC7_NET : GG_ERROR_DCC7_EOF; + return e; + } + + // XXX zapisywać do skutku? + + if ((wres = write(dcc->file_fd, buf, res)) < res) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (fd=%d, res=%d, %s)\n", dcc->file_fd, wres, strerror(errno)); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_FILE; + return e; + } + + dcc->offset += res; + + if (dcc->offset >= dcc->size) { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n"); + e->type = GG_EVENT_DCC7_DONE; + return e; + } + + dcc->state = GG_STATE_GETTING_FILE; + dcc->check = GG_CHECK_READ; + dcc->timeout = GG_DCC7_TIMEOUT_GET; + + return e; + } + + default: + { + gg_debug_dcc(dcc, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n"); + e->type = GG_EVENT_DCC7_ERROR; + e->event.dcc_error = GG_ERROR_DCC7_HANDSHAKE; + + return e; + } + } + + return e; +} + +/** + * Zwalnia zasoby używane przez połączenie bezpośrednie. + * + * \param dcc Struktura połączenia + * + * \ingroup dcc7 + */ +void gg_dcc7_free(struct gg_dcc7 *dcc) +{ + gg_debug_dcc(dcc, GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p)\n", dcc); + + if (!dcc) + return; + + if (dcc->fd != -1) + close(dcc->fd); + + if (dcc->file_fd != -1) + close(dcc->file_fd); + + if (dcc->sess) + gg_dcc7_session_remove(dcc->sess, dcc); + + free(dcc); +} +
--- a/libpurple/protocols/gg/lib/events.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/gg/lib/events.c Tue Mar 09 21:41:41 2010 +0000 @@ -1,9 +1,10 @@ -/* $Id: events.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: events.c 855 2009-10-12 21:42:51Z wojtekka $ */ /* - * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Wony <speedy@ziew.org> - * Arkadiusz Mikiewicz <arekm@pld-linux.org> + * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> + * Robert J. Woźny <speedy@ziew.org> + * Arkadiusz Miśkiewicz <arekm@pld-linux.org> + * Adam Wysocki <gophi@ekg.chmurka.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -16,71 +17,78 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ +/** + * \file events.c + * + * \brief Obsługa zdarzeń + */ + #include "libgadu.h" +#include "libgadu-internal.h" #include <sys/types.h> + #ifndef _WIN32 -#include <sys/wait.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> +# include <sys/ioctl.h> +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> #endif -#include "libgadu-config.h" +#include "compat.h" +#include "protocol.h" #include <errno.h> -#ifdef __GG_LIBGADU_HAVE_PTHREAD -# include <pthread.h> -#endif #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <unistd.h> -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#include <ctype.h> +#ifdef GG_CONFIG_HAVE_OPENSSL # include <openssl/err.h> # include <openssl/x509.h> #endif -#include "compat.h" - -/* - * gg_event_free() +/** + * Zwalnia pamięć zajmowaną przez informację o zdarzeniu. * - * zwalnia pami zajmowan przez informacj o zdarzeniu. + * Funkcję należy wywoływać za każdym razem gdy funkcja biblioteki zwróci + * strukturę \c gg_event. * - * - e - wskanik do informacji o zdarzeniu + * \param e Struktura zdarzenia + * + * \ingroup events */ void gg_event_free(struct gg_event *e) { gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e); - + if (!e) return; - + switch (e->type) { case GG_EVENT_MSG: free(e->event.msg.message); free(e->event.msg.formats); free(e->event.msg.recipients); break; - + case GG_EVENT_NOTIFY: free(e->event.notify); break; - + case GG_EVENT_NOTIFY60: { int i; for (i = 0; e->event.notify60[i].uin; i++) free(e->event.notify60[i].descr); - + free(e->event.notify60); break; @@ -89,7 +97,7 @@ case GG_EVENT_STATUS60: free(e->event.status60.descr); break; - + case GG_EVENT_STATUS: free(e->event.status.descr); break; @@ -112,26 +120,30 @@ case GG_EVENT_USERLIST: free(e->event.userlist.reply); break; - + case GG_EVENT_IMAGE_REPLY: free(e->event.image_reply.filename); free(e->event.image_reply.image); break; + + case GG_EVENT_XML_EVENT: + free(e->event.xml_event.data); + break; } free(e); } -/* - * gg_image_queue_remove() - * - * usuwa z kolejki dany wpis. +/** \cond internal */ + +/** + * \internal Usuwa obrazek z kolejki do wysłania. * - * - s - sesja - * - q - kolejka - * - freeq - czy zwolni kolejk + * \param s Struktura sesji + * \param q Struktura obrazka + * \param freeq Flaga zwolnienia elementu kolejki * - * 0/-1 + * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd */ int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) { @@ -162,13 +174,14 @@ return 0; } -/* - * gg_image_queue_parse() // funkcja wewntrzna +/** + * \internal Analizuje przychodzący pakiet z obrazkiem. * - * parsuje przychodzcy pakiet z obrazkiem. - * - * - e - opis zdarzenia - * - + * \param e Struktura zdarzenia + * \param p Bufor z danymi + * \param len Długość bufora + * \param sess Struktura sesji + * \param sender Numer nadawcy */ static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len, struct gg_session *sess, uin_t sender) { @@ -180,8 +193,8 @@ return; } - /* znajd dany obrazek w kolejce danej sesji */ - + /* znajdź dany obrazek w kolejce danej sesji */ + for (qq = sess->images, q = NULL; qq; qq = qq->next) { if (sender == qq->sender && i->size == qq->size && i->crc32 == qq->crc32) { q = qq; @@ -190,34 +203,23 @@ } if (!q) { - gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, i->size, i->crc32); return; } if (p[0] == 0x05) { - unsigned int i, ok = 0; - q->done = 0; len -= sizeof(struct gg_msg_image_reply); p += sizeof(struct gg_msg_image_reply); - /* sprawd, czy mamy tekst zakoczony \0 */ - - for (i = 0; i < len; i++) { - if (!p[i]) { - ok = 1; - break; - } - } - - if (!ok) { - gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender); + if (memchr(p, 0, len) == NULL) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender); return; } if (!(q->filename = strdup(p))) { - gg_debug(GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n"); return; } @@ -230,11 +232,11 @@ if (q->done + len > q->size) len = q->size - q->done; - + memcpy(q->image + q->done, p, len); q->done += len; - /* jeli skoczono odbiera obrazek, wygeneruj zdarzenie */ + /* jeśli skończono odbierać obrazek, wygeneruj zdarzenie */ if (q->done >= q->size) { e->type = GG_EVENT_IMAGE_REPLY; @@ -250,78 +252,55 @@ } } -/* - * gg_handle_recv_msg() // funkcja wewntrzna - * - * obsuguje pakiet z przychodzc wiadomoci, rozbijajc go na dodatkowe - * struktury (konferencje, kolorki) w razie potrzeby. +/** + * \internal Analizuje informacje rozszerzone wiadomości. + * + * \param sess Struktura sesji. + * \param e Struktura zdarzenia. + * \param sender Numer nadawcy. + * \param p Wskaźnik na dane rozszerzone. + * \param packet_end Wskaźnik na koniec pakietu. * - * - h - nagwek pakietu - * - e - opis zdarzenia - * - * 0, -1. + * \return 0 jeśli się powiodło, -1 jeśli wiadomość obsłużono i wynik ma + * zostać przekazany aplikacji, -2 jeśli wystąpił błąd ogólny, -3 jeśli + * wiadomość jest niepoprawna. */ -static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess) +static int gg_handle_recv_msg_options(struct gg_session *sess, struct gg_event *e, uin_t sender, char *p, char *packet_end) { - struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header)); - char *p, *packet_end = (char*) r + h->length; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e); - - if (!r->seq && !r->msgclass) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n"); - e->type = GG_EVENT_NONE; - return 0; - } - - for (p = (char*) r + sizeof(*r); *p; p++) { - if (*p == 0x02 && p == packet_end - 1) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n"); - break; - } - if (p >= packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n"); - goto malformed; - } - } - - p++; - - /* przeanalizuj dodatkowe opcje */ while (p < packet_end) { switch (*p) { case 0x01: /* konferencja */ { struct gg_msg_recipients *m = (void*) p; uint32_t i, count; - + p += sizeof(*m); - + if (p > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1)\n"); goto malformed; } count = gg_fix32(m->count); if (p + count * sizeof(uin_t) > packet_end || p + count * sizeof(uin_t) < p || count > 0xffff) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (1.5)\n"); goto malformed; } - + if (!(e->event.msg.recipients = (void*) malloc(count * sizeof(uin_t)))) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for recipients data\n"); goto fail; } - + for (i = 0; i < count; i++, p += sizeof(uint32_t)) { uint32_t u; memcpy(&u, p, sizeof(uint32_t)); e->event.msg.recipients[i] = gg_fix32(u); } - + e->event.msg.recipients_count = count; - + break; } @@ -329,9 +308,9 @@ { uint16_t len; char *buf; - + if (p + 3 > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (2)\n"); goto malformed; } @@ -339,18 +318,18 @@ len = gg_fix16(len); if (!(buf = malloc(len))) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() not enough memory for richtext data\n"); goto fail; } p += 3; if (p + len > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); free(buf); goto malformed; } - + memcpy(buf, p, len); e->event.msg.formats = buf; @@ -366,17 +345,17 @@ struct gg_msg_image_request *i = (void*) p; if (p + sizeof(*i) > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (3)\n"); goto malformed; } - e->event.image_request.sender = gg_fix32(r->sender); + e->event.image_request.sender = sender; e->event.image_request.size = gg_fix32(i->size); e->event.image_request.crc32 = gg_fix32(i->crc32); e->type = GG_EVENT_IMAGE_REQUEST; - return 0; + goto handled; } case 0x05: /* image_reply */ @@ -386,75 +365,347 @@ if (p + sizeof(struct gg_msg_image_reply) == packet_end) { - /* pusta odpowied - klient po drugiej stronie nie ma danego obrazka */ + /* pusta odpowiedź - klient po drugiej stronie nie ma żądanego obrazka */ e->type = GG_EVENT_IMAGE_REPLY; - e->event.image_reply.sender = gg_fix32(r->sender); + e->event.image_reply.sender = sender; e->event.image_reply.size = 0; e->event.image_reply.crc32 = gg_fix32(rep->crc32); e->event.image_reply.filename = NULL; e->event.image_reply.image = NULL; - return 0; + goto handled; } else if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() packet out of bounds (4)\n"); goto malformed; } rep->size = gg_fix32(rep->size); rep->crc32 = gg_fix32(rep->crc32); - gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, gg_fix32(r->sender)); - - return 0; + gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, sender); + + goto handled; } default: { - gg_debug(GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() unknown payload 0x%.2x\n", *p); p = packet_end; } } } + return 0; + +handled: + return -1; + +fail: + return -2; + +malformed: + return -3; +} + +/** + * \internal Analizuje przychodzący pakiet z wiadomością. + * + * Rozbija pakiet na poszczególne składniki -- tekst, informacje + * o konferencjach, formatowani itd. + * + * \param h Wskaźnik do odebranego pakietu + * \param e Struktura zdarzenia + * \param sess Struktura sesji + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +static int gg_handle_recv_msg(struct gg_header *h, struct gg_event *e, struct gg_session *sess) +{ + struct gg_recv_msg *r = (struct gg_recv_msg*) ((char*) h + sizeof(struct gg_header)); + char *p, *packet_end = (char*) r + h->length; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg(%p, %p);\n", h, e); + + if (!r->seq && !r->msgclass) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() oops, silently ignoring the bait\n"); + e->type = GG_EVENT_NONE; + return 0; + } + + /* znajdź \0 */ + for (p = (char*) r + sizeof(*r); ; p++) { + if (p >= packet_end) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() malformed packet, message out of bounds (0)\n"); + goto malformed; + } + + if (*p == 0x02 && p == packet_end - 1) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg() received ctcp packet\n"); + break; + } + + if (!*p) + break; + } + + p++; + + switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), p, packet_end)) { + case -1: // handled + return 0; + + case -2: // failed + goto fail; + + case -3: // malformed + goto malformed; + } + e->type = GG_EVENT_MSG; e->event.msg.msgclass = gg_fix32(r->msgclass); e->event.msg.sender = gg_fix32(r->sender); e->event.msg.time = gg_fix32(r->time); - e->event.msg.message = (unsigned char *)strdup((char*) r + sizeof(*r)); + e->event.msg.seq = gg_fix32(r->seq); + e->event.msg.message = (unsigned char*) strdup((char*) r + sizeof(*r)); return 0; malformed: e->type = GG_EVENT_NONE; - + free(e->event.msg.message); free(e->event.msg.recipients); free(e->event.msg.formats); return 0; fail: + free(e->event.msg.message); free(e->event.msg.recipients); free(e->event.msg.formats); return -1; } -/* - * gg_watch_fd_connected() // funkcja wewntrzna +/** + * \internal Zamienia tekst w formacie HTML na czysty tekst. + * + * \param dst Bufor wynikowy (może być \c NULL) + * \param html Tekst źródłowy + * + * \note Dokleja \c \\0 na końcu bufora wynikowego. * - * patrzy na gniazdo, odbiera pakiet i wypenia struktur zdarzenia. + * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL). + */ +static int gg_convert_from_html(char *dst, const char *html) +{ + const char *src, *entity, *tag; + int len, in_tag, in_entity; + + len = 0; + in_tag = 0; + tag = NULL; + in_entity = 0; + entity = NULL; + + for (src = html; *src != 0; src++) { + if (*src == '<') { + tag = src; + in_tag = 1; + continue; + } + + if (in_tag && (*src == '>')) { + if (strncmp(tag, "<br", 3) == 0) { + if (dst != NULL) + dst[len] = '\n'; + len++; + } + in_tag = 0; + continue; + } + + if (in_tag) + continue; + + if (*src == '&') { + in_entity = 1; + entity = src; + continue; + } + + if (in_entity && *src == ';') { + in_entity = 0; + if (dst != NULL) { + if (strncmp(entity, "<", 4) == 0) + dst[len] = '<'; + else if (strncmp(entity, ">", 4) == 0) + dst[len] = '>'; + else if (strncmp(entity, """, 6) == 0) + dst[len] = '"'; + else if (strncmp(entity, "'", 6) == 0) + dst[len] = '\''; + else if (strncmp(entity, "&", 5) == 0) + dst[len] = '&'; + else + dst[len] = '?'; + } + len++; + continue; + } + + if (in_entity && !(isalnum(*src) || *src == '#')) + in_entity = 0; + + if (in_entity) + continue; + + if (dst != NULL) + dst[len] = *src; + + len++; + } + + if (dst != NULL) + dst[len] = 0; + + return len; +} + +/** + * \internal Analizuje przychodzący pakiet z wiadomością protokołu Gadu-Gadu 8.0. + * + * Rozbija pakiet na poszczególne składniki -- tekst, informacje + * o konferencjach, formatowani itd. + * + * \param h Wskaźnik do odebranego pakietu + * \param e Struktura zdarzenia + * \param sess Struktura sesji * - * - sess - struktura opisujca sesj - * - e - opis zdarzenia + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +static int gg_handle_recv_msg80(struct gg_header *h, struct gg_event *e, struct gg_session *sess) +{ + char *packet = (char*) h + sizeof(struct gg_header); + struct gg_recv_msg80 *r = (struct gg_recv_msg80*) packet; + uint32_t offset_plain; + uint32_t offset_attr; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg80(%p, %p);\n", h, e); + + if (!r->seq && !r->msgclass) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() oops, silently ignoring the bait\n"); + goto malformed; + } + + offset_plain = gg_fix32(r->offset_plain); + offset_attr = gg_fix32(r->offset_attr); + + if (offset_plain < sizeof(struct gg_recv_msg80) || offset_plain >= h->length) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (0)\n"); + goto malformed; + } + + if (offset_attr < sizeof(struct gg_recv_msg80) || offset_attr > h->length) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, attr out of bounds (1)\n"); + offset_attr = 0; /* nie parsuj attr. */ + /* goto ignore; */ + } + + /* Normalna sytuacja, więc nie podpada pod powyższy warunek. */ + if (offset_attr == h->length) + offset_attr = 0; + + if (memchr(packet + offset_plain, 0, h->length - offset_plain) == NULL) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (2)\n"); + goto malformed; + } + + if (offset_plain > sizeof(struct gg_recv_msg80) && memchr(packet + sizeof(struct gg_recv_msg80), 0, offset_plain - sizeof(struct gg_recv_msg80)) == NULL) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (3)\n"); + goto malformed; + } + + e->type = GG_EVENT_MSG; + e->event.msg.msgclass = gg_fix32(r->msgclass); + e->event.msg.sender = gg_fix32(r->sender); + e->event.msg.time = gg_fix32(r->time); + e->event.msg.seq = gg_fix32(r->seq); + + if (sess->encoding == GG_ENCODING_CP1250) { + e->event.msg.message = (unsigned char*) strdup(packet + offset_plain); + } else { + if (offset_plain > sizeof(struct gg_recv_msg80)) { + int len; + + len = gg_convert_from_html(NULL, packet + sizeof(struct gg_recv_msg80)); + + e->event.msg.message = malloc(len + 1); + + if (e->event.msg.message == NULL) + goto fail; + + gg_convert_from_html((char*) e->event.msg.message, packet + sizeof(struct gg_recv_msg80)); + } else { + e->event.msg.message = (unsigned char*) gg_cp_to_utf8(packet + offset_plain); + } + } + + if (offset_plain > sizeof(struct gg_recv_msg80)) { + if (sess->encoding == GG_ENCODING_UTF8) + e->event.msg.xhtml_message = strdup(packet + sizeof(struct gg_recv_msg80)); + else + e->event.msg.xhtml_message = gg_utf8_to_cp(packet + sizeof(struct gg_recv_msg80)); + } else { + e->event.msg.xhtml_message = NULL; + } + + if (offset_attr != 0) { + switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), packet + offset_attr, packet + h->length)) { + case -1: // handled + return 0; + + case -2: // failed + goto fail; + + case -3: // malformed + goto malformed; + } + } + + return 0; + +fail: + free(e->event.msg.message); + free(e->event.msg.xhtml_message); + free(e->event.msg.recipients); + free(e->event.msg.formats); + return -1; + +malformed: + e->type = GG_EVENT_NONE; + free(e->event.msg.message); + free(e->event.msg.xhtml_message); + free(e->event.msg.recipients); + free(e->event.msg.formats); + return 0; +} + +/** + * \internal Odbiera pakiet od serwera. * - * 0, -1. + * Analizuje pakiet i wypełnia strukturę zdarzenia. + * + * \param sess Struktura sesji + * \param e Struktura zdarzenia + * + * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd */ static int gg_watch_fd_connected(struct gg_session *sess, struct gg_event *e) { struct gg_header *h = NULL; char *p; - gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e); + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e); if (!sess) { errno = EFAULT; @@ -462,76 +713,86 @@ } if (!(h = gg_recv_packet(sess))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() gg_recv_packet failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail; } p = (char*) h + sizeof(struct gg_header); - + switch (h->type) { case GG_RECV_MSG: { if (h->length >= sizeof(struct gg_recv_msg)) if (gg_handle_recv_msg(h, e, sess)) goto fail; - + break; } + case GG_RECV_MSG80: + { + if (h->length >= sizeof(struct gg_recv_msg80)) + if (gg_handle_recv_msg80(h, e, sess)) + goto fail; + + break; + } + + case GG_NOTIFY_REPLY: { struct gg_notify_reply *n = (void*) p; unsigned int count, i; char *tmp; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); if (h->length < sizeof(*n)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() incomplete packet\n"); errno = EINVAL; goto fail; } if (gg_fix32(n->status) == GG_STATUS_BUSY_DESCR || gg_fix32(n->status) == GG_STATUS_NOT_AVAIL_DESCR || gg_fix32(n->status) == GG_STATUS_AVAIL_DESCR) { e->type = GG_EVENT_NOTIFY_DESCR; - + if (!(e->event.notify_descr.notify = (void*) malloc(sizeof(*n) * 2))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } e->event.notify_descr.notify[1].uin = 0; memcpy(e->event.notify_descr.notify, p, sizeof(*n)); e->event.notify_descr.notify[0].uin = gg_fix32(e->event.notify_descr.notify[0].uin); e->event.notify_descr.notify[0].status = gg_fix32(e->event.notify_descr.notify[0].status); - e->event.notify_descr.notify[0].remote_ip = e->event.notify_descr.notify[0].remote_ip; e->event.notify_descr.notify[0].remote_port = gg_fix16(e->event.notify_descr.notify[0].remote_port); + e->event.notify_descr.notify[0].version = gg_fix32(e->event.notify_descr.notify[0].version); count = h->length - sizeof(*n); if (!(tmp = malloc(count + 1))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } memcpy(tmp, p + sizeof(*n), count); tmp[count] = 0; e->event.notify_descr.descr = tmp; - + } else { e->type = GG_EVENT_NOTIFY; - + if (!(e->event.notify = (void*) malloc(h->length + 2 * sizeof(*n)))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } - + memcpy(e->event.notify, p, h->length); count = h->length / sizeof(*n); e->event.notify[count].uin = 0; - + for (i = 0; i < count; i++) { e->event.notify[i].uin = gg_fix32(e->event.notify[i].uin); e->event.notify[i].status = gg_fix32(e->event.notify[i].status); - e->event.notify[i].remote_ip = e->event.notify[i].remote_ip; e->event.notify[i].remote_port = gg_fix16(e->event.notify[i].remote_port); + e->event.notify[i].version = gg_fix32(e->event.notify[i].version); } } @@ -542,7 +803,7 @@ { struct gg_status *s = (void*) p; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); if (h->length >= sizeof(*s)) { e->type = GG_EVENT_STATUS; @@ -564,23 +825,176 @@ break; } - case GG_NOTIFY_REPLY60: + case GG_NOTIFY_REPLY77: + case GG_NOTIFY_REPLY80BETA: { - struct gg_notify_reply60 *n = (void*) p; + struct gg_notify_reply77 *n = (void*) p; unsigned int length = h->length, i = 0; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); e->type = GG_EVENT_NOTIFY60; e->event.notify60 = malloc(sizeof(*e->event.notify60)); if (!e->event.notify60) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } e->event.notify60[0].uin = 0; - + + while (length >= sizeof(struct gg_notify_reply77)) { + uin_t uin = gg_fix32(n->uin); + char *tmp; + + e->event.notify60[i].uin = uin & 0x00ffffff; + e->event.notify60[i].status = n->status; + e->event.notify60[i].remote_ip = n->remote_ip; + e->event.notify60[i].remote_port = gg_fix16(n->remote_port); + e->event.notify60[i].version = n->version; + e->event.notify60[i].image_size = n->image_size; + e->event.notify60[i].descr = NULL; + e->event.notify60[i].time = 0; + + if (uin & 0x40000000) + e->event.notify60[i].version |= GG_HAS_AUDIO_MASK; + if (uin & 0x20000000) + e->event.notify60[i].version |= GG_HAS_AUDIO7_MASK; + if (uin & 0x08000000) + e->event.notify60[i].version |= GG_ERA_OMNIX_MASK; + + if (GG_S_D(n->status)) { + unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply77)); + + if (sizeof(struct gg_notify_reply77) + descr_len <= length) { + char *descr; + + if (!(descr = malloc(descr_len + 1))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + + memcpy(descr, (char*) n + sizeof(struct gg_notify_reply77) + 1, descr_len); + descr[descr_len] = 0; + + if (h->type == GG_NOTIFY_REPLY80BETA && sess->encoding != GG_ENCODING_UTF8) { + char *cp_descr = gg_utf8_to_cp(descr); + + if (!cp_descr) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + free(descr); + goto fail; + } + + free(descr); + descr = cp_descr; + } + + e->event.notify60[i].descr = descr; + + /* XXX czas */ + + length -= sizeof(struct gg_notify_reply77) + descr_len + 1; + n = (void*) ((char*) n + sizeof(struct gg_notify_reply77) + descr_len + 1); + } else { + length = 0; + } + + } else { + length -= sizeof(struct gg_notify_reply77); + n = (void*) ((char*) n + sizeof(struct gg_notify_reply77)); + } + + if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + free(e->event.notify60); + goto fail; + } + + e->event.notify60 = (void*) tmp; + e->event.notify60[++i].uin = 0; + } + + break; + } + + case GG_STATUS77: + case GG_STATUS80BETA: + { + struct gg_status77 *s = (void*) p; + uint32_t uin; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + + if (h->length < sizeof(*s)) + break; + + uin = gg_fix32(s->uin); + + e->type = GG_EVENT_STATUS60; + e->event.status60.uin = uin & 0x00ffffff; + e->event.status60.status = s->status; + e->event.status60.remote_ip = s->remote_ip; + e->event.status60.remote_port = gg_fix16(s->remote_port); + e->event.status60.version = s->version; + e->event.status60.image_size = s->image_size; + e->event.status60.descr = NULL; + e->event.status60.time = 0; + + if (uin & 0x40000000) + e->event.status60.version |= GG_HAS_AUDIO_MASK; + if (uin & 0x20000000) + e->event.status60.version |= GG_HAS_AUDIO7_MASK; + if (uin & 0x08000000) + e->event.status60.version |= GG_ERA_OMNIX_MASK; + + if (h->length > sizeof(*s)) { + int len = h->length - sizeof(*s); + char *buf = malloc(len + 1); + + /* XXX, jesli malloc() sie nie uda to robic tak samo jak przy GG_NOTIFY_REPLY* ? + * - goto fail; (?) + */ + if (buf) { + memcpy(buf, (char*) p + sizeof(*s), len); + buf[len] = 0; + + if (h->type == GG_STATUS80BETA && sess->encoding != GG_ENCODING_UTF8) { + char *cp_buf = gg_utf8_to_cp(buf); + free(buf); + buf = cp_buf; + } + } + + e->event.status60.descr = buf; + + if (len > 4 && p[h->length - 5] == 0) { + uint32_t t; + memcpy(&t, p + h->length - 4, sizeof(uint32_t)); + e->event.status60.time = gg_fix32(t); + } + } + + break; + } + + case GG_NOTIFY_REPLY60: + { + struct gg_notify_reply60 *n = (void*) p; + unsigned int length = h->length, i = 0; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + + e->type = GG_EVENT_NOTIFY60; + e->event.notify60 = malloc(sizeof(*e->event.notify60)); + + if (!e->event.notify60) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + + e->event.notify60[0].uin = 0; + while (length >= sizeof(struct gg_notify_reply60)) { uin_t uin = gg_fix32(n->uin); char *tmp; @@ -602,9 +1016,9 @@ if (GG_S_D(n->status)) { unsigned char descr_len = *((char*) n + sizeof(struct gg_notify_reply60)); - if (descr_len < length) { + if (sizeof(struct gg_notify_reply60) + descr_len <= length) { if (!(e->event.notify60[i].descr = malloc(descr_len + 1))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); goto fail; } @@ -612,17 +1026,20 @@ e->event.notify60[i].descr[descr_len] = 0; /* XXX czas */ + + length -= sizeof(struct gg_notify_reply60) + descr_len + 1; + n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1); + } else { + length = 0; } - - length -= sizeof(struct gg_notify_reply60) + descr_len + 1; - n = (void*) ((char*) n + sizeof(struct gg_notify_reply60) + descr_len + 1); + } else { length -= sizeof(struct gg_notify_reply60); n = (void*) ((char*) n + sizeof(struct gg_notify_reply60)); } if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); free(e->event.notify60); goto fail; } @@ -633,13 +1050,13 @@ break; } - + case GG_STATUS60: { struct gg_status60 *s = (void*) p; uint32_t uin; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); if (h->length < sizeof(*s)) break; @@ -682,11 +1099,132 @@ break; } + case GG_STATUS80: + { + struct gg_notify_reply80 *s = (void*) p; + uint32_t descr_len; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a status change\n"); + + if (h->length < sizeof(*s)) + break; + + e->type = GG_EVENT_STATUS60; + e->event.status60.uin = gg_fix32(s->uin); + e->event.status60.status = gg_fix32(s->status); + e->event.status60.remote_ip = s->remote_ip; + e->event.status60.remote_port = gg_fix16(s->remote_port); + e->event.status60.image_size = s->image_size; + e->event.status60.descr = NULL; + e->event.status60.version = 0x00; /* not-supported */ + e->event.status60.time = 0; /* not-supported */ + + descr_len = gg_fix32(s->descr_len); + + if (descr_len > 0 && h->length-sizeof(*s) >= descr_len) { + char *buf = malloc(descr_len + 1); + + if (buf) { + memcpy(buf, (char*) p + sizeof(*s), descr_len); + buf[descr_len] = 0; + + if (sess->encoding != GG_ENCODING_UTF8) { + char *cp_buf = gg_utf8_to_cp(buf); + free(buf); + buf = cp_buf; + } + } + + e->event.status60.descr = buf; + } + break; + } + + case GG_NOTIFY_REPLY80: + { + struct gg_notify_reply80 *n = (void*) p; + int length = h->length, i = 0; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n"); + + e->type = GG_EVENT_NOTIFY60; + e->event.notify60 = malloc(sizeof(*e->event.notify60)); + if (!e->event.notify60) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + + e->event.notify60[0].uin = 0; + + while (length >= sizeof(struct gg_notify_reply80)) { + uint32_t descr_len; + char *tmp; + + e->event.notify60[i].uin = gg_fix32(n->uin); + e->event.notify60[i].status = gg_fix32(n->status); + e->event.notify60[i].remote_ip = n->remote_ip; + e->event.notify60[i].remote_port= gg_fix16(n->remote_port); + e->event.notify60[i].image_size = n->image_size; + e->event.notify60[i].descr = NULL; + e->event.notify60[i].version = 0x00; /* not-supported */ + e->event.notify60[i].time = 0; /* not-supported */ + + + descr_len = gg_fix32(n->descr_len); + + length -= sizeof(struct gg_notify_reply80); + n = (void*) ((char*) n + sizeof(struct gg_notify_reply80)); + + if (descr_len) { + if (length >= descr_len) { + /* XXX, GG_S_D(n->status) */ + char *descr; + + if (!(descr = malloc(descr_len + 1))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + goto fail; + } + + memcpy(descr, n, descr_len); + descr[descr_len] = 0; + + if (sess->encoding != GG_ENCODING_UTF8) { + char *cp_descr = gg_utf8_to_cp(descr); + + if (!cp_descr) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + free(descr); + goto fail; + } + + free(descr); + descr = cp_descr; + } + e->event.notify60[i].descr = descr; + + length -= descr_len; + n = (void*) ((char*) n + descr_len); + } else + length = 0; + } + + if (!(tmp = realloc(e->event.notify60, (i + 2) * sizeof(*e->event.notify60)))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for notify data\n"); + free(e->event.notify60); + goto fail; + } + + e->event.notify60 = (void*) tmp; + e->event.notify60[++i].uin = 0; + } + break; + } + case GG_SEND_MSG_ACK: { struct gg_send_msg_ack *s = (void*) p; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n"); if (h->length < sizeof(*s)) break; @@ -699,9 +1237,9 @@ break; } - case GG_PONG: + case GG_PONG: { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n"); e->type = GG_EVENT_PONG; sess->last_pong = time(NULL); @@ -711,27 +1249,47 @@ case GG_DISCONNECTING: { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection warning\n"); e->type = GG_EVENT_DISCONNECT; break; } + case GG_DISCONNECT_ACK: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received disconnection acknowledge\n"); + e->type = GG_EVENT_DISCONNECT_ACK; + break; + } + + case GG_XML_EVENT: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received XML event\n"); + e->type = GG_EVENT_XML_EVENT; + if (!(e->event.xml_event.data = (char *) malloc(h->length + 1))) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for XML event data\n"); + goto fail; + } + memcpy(e->event.xml_event.data, p, h->length); + e->event.xml_event.data[h->length] = 0; + break; + } + case GG_PUBDIR50_REPLY: { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n"); - if (gg_pubdir50_handle_reply(e, p, h->length) == -1) + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received pubdir/search reply\n"); + if (gg_pubdir50_handle_reply_sess(sess, e, p, h->length) == -1) goto fail; break; } case GG_USERLIST_REPLY: { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n"); if (h->length < 1) break; - /* jeli odpowied na eksport, wywoaj zdarzenie tylko + /* jeśli odpowiedź na eksport, wywołaj zdarzenie tylko * gdy otrzymano wszystkie odpowiedzi */ if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) { if (--sess->userlist_blocks) @@ -743,11 +1301,11 @@ if (h->length > 1) { char *tmp; unsigned int len = (sess->userlist_reply) ? strlen(sess->userlist_reply) : 0; - - gg_debug(GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len); - + + gg_debug_session(sess, GG_DEBUG_MISC, "userlist_reply=%p, len=%d\n", sess->userlist_reply, len); + if (!(tmp = realloc(sess->userlist_reply, len + h->length))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for userlist reply\n"); free(sess->userlist_reply); sess->userlist_reply = NULL; goto fail; @@ -769,10 +1327,75 @@ break; } + case GG_DCC7_ID_REPLY: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 id packet\n"); + + if (h->length < sizeof(struct gg_dcc7_id_reply)) + break; + + if (gg_dcc7_handle_id(sess, e, p, h->length) == -1) + goto fail; + + break; + } + + case GG_DCC7_ACCEPT: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 accept\n"); + + if (h->length < sizeof(struct gg_dcc7_accept)) + break; + + if (gg_dcc7_handle_accept(sess, e, p, h->length) == -1) + goto fail; + + break; + } + + case GG_DCC7_NEW: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 request\n"); + + if (h->length < sizeof(struct gg_dcc7_new)) + break; + + if (gg_dcc7_handle_new(sess, e, p, h->length) == -1) + goto fail; + + break; + } + + case GG_DCC7_REJECT: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 reject\n"); + + if (h->length < sizeof(struct gg_dcc7_reject)) + break; + + if (gg_dcc7_handle_reject(sess, e, p, h->length) == -1) + goto fail; + + break; + } + + case GG_DCC7_INFO: + { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 info\n"); + + if (h->length < sizeof(struct gg_dcc7_info)) + break; + + if (gg_dcc7_handle_info(sess, e, p, h->length) == -1) + goto fail; + + break; + } + default: - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type); } - + free(h); return 0; @@ -781,19 +1404,20 @@ return -1; } -/* - * gg_watch_fd() - * - * funkcja, ktr naley wywoa, gdy co si stanie z obserwowanym - * deskryptorem. zwraca klientowi informacj o tym, co si dzieje. +/** \endcond */ + +/** + * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze sesji. * - * - sess - opis sesji + * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia + * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania. + * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free(). * - * wskanik do struktury gg_event, ktr trzeba zwolni pniej - * za pomoc gg_event_free(). jesli rodzaj zdarzenia jest rwny - * GG_EVENT_NONE, naley je zignorowa. jeli zwrcio NULL, - * stao si co niedobrego -- albo zabrako pamici albo zerwao - * poczenie. + * \param sess Struktura sesji + * + * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd + * + * \ingroup events */ struct gg_event *gg_watch_fd(struct gg_session *sess) { @@ -802,68 +1426,79 @@ int port = 0; int errno2 = 0; - gg_debug(GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess); - + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd(%p);\n", sess); + if (!sess) { errno = EFAULT; return NULL; } if (!(e = (void*) calloc(1, sizeof(*e)))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() not enough memory for event data\n"); return NULL; } e->type = GG_EVENT_NONE; + if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED)) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending %d bytes of queued data\n", sess->send_left); + + res = write(sess->fd, sess->send_buf, sess->send_left); + + if (res == -1 && errno != EAGAIN) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno)); + + if (sess->state == GG_STATE_READING_REPLY) + goto fail_connecting; + else + goto done; + } + + if (res == sess->send_left) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent all queued data\n"); + free(sess->send_buf); + sess->send_buf = NULL; + sess->send_left = 0; + } else if (res > 0) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sent %d bytes of queued data, %d bytes left\n", res, sess->send_left - res); + + memmove(sess->send_buf, sess->send_buf + res, sess->send_left - res); + sess->send_left -= res; + } + } + switch (sess->state) { case GG_STATE_RESOLVING: { struct in_addr addr; int failed = 0; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n"); if (read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n"); failed = 1; errno2 = errno; } - + close(sess->fd); sess->fd = -1; -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (sess->resolver) { - pthread_cancel(*((pthread_t*) sess->resolver)); - free(sess->resolver); - sess->resolver = NULL; - } -#elif defined _WIN32 - if (sess->resolver) { - HANDLE h = sess->resolver; - TerminateThread(h, 0); - CloseHandle(h); - sess->resolver = NULL; - } -#else - waitpid(sess->pid, NULL, 0); - sess->pid = -1; -#endif + sess->resolver_cleanup(&sess->resolver, 0); if (failed) { errno = errno2; goto fail_resolving; } - /* jeli jestemy w resolverze i mamy ustawiony port - * proxy, znaczy, e resolvowalimy proxy. zatem + /* jeśli jesteśmy w resolverze i mamy ustawiony port + * proxy, znaczy, że resolvowaliśmy proxy. zatem * wpiszmy jego adres. */ if (sess->proxy_port) sess->proxy_addr = addr.s_addr; /* zapiszmy sobie adres huba i adres serwera (do - * bezporedniego poczenia, jeli hub ley) + * bezpośredniego połączenia, jeśli hub leży) * z resolvera. */ if (sess->proxy_addr && sess->proxy_port) port = sess->proxy_port; @@ -872,21 +1507,27 @@ port = GG_APPMSG_PORT; } - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port); - - /* czymy si albo z hubem, albo z proxy, zalenie - * od tego, co resolvowalimy. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), port); + + /* łączymy się albo z hubem, albo z proxy, zależnie + * od tego, co resolvowaliśmy. */ if ((sess->fd = gg_connect(&addr, port, sess->async)) == -1) { - /* jeli w trybie asynchronicznym gg_connect() - * zwrci bd, nie ma sensu prbowa dalej. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); + /* jeśli w trybie asynchronicznym gg_connect() + * zwróci błąd, nie ma sensu próbować dalej. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); goto fail_connecting; } - /* jeli podano serwer i czmy si przez proxy, - * jest to bezporednie poczenie, inaczej jest + /* jeśli podano serwer i łączmy się przez proxy, + * jest to bezpośrednie połączenie, inaczej jest * do huba. */ - sess->state = (sess->proxy_addr && sess->proxy_port && sess->server_addr) ? GG_STATE_CONNECTING_GG : GG_STATE_CONNECTING_HUB; + + if (sess->proxy_addr && sess->proxy_port && sess->server_addr) { + sess->state = GG_STATE_CONNECTING_GG; + sess->soft_timeout = 1; + } else + sess->state = GG_STATE_CONNECTING_HUB; + sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; @@ -898,42 +1539,25 @@ char buf[1024], *client, *auth; int res = 0; socklen_t res_size = sizeof(res); - const char *host, *appmsg; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n"); - - /* jeli asynchroniczne, sprawdzamy, czy nie wystpi - * przypadkiem jaki bd. */ + const char *host; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_HUB\n"); + + /* jeśli asynchroniczne, sprawdzamy, czy nie wystąpił + * przypadkiem jakiś błąd. */ if (sess->async && (getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { - /* no tak, nie udao si poczy z proxy. nawet - * nie prbujemy dalej. */ - if (sess->proxy_addr && sess->proxy_port) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); - goto fail_connecting; - } - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s), trying direct connection\n", res, strerror(res)); - close(sess->fd); - - if ((sess->fd = gg_connect(&sess->hub_addr, GG_DEFAULT_PORT, sess->async)) == -1) { - /* przy asynchronicznych, gg_connect() - * zwraca -1 przy bdach socket(), - * ioctl(), braku routingu itd. dlatego - * nawet nie prbujemy dalej. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() direct connection failed (errno=%d, %s), critical\n", errno, strerror(errno)); - goto fail_connecting; - } - - sess->state = GG_STATE_CONNECTING_GG; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - break; + if (sess->proxy_addr && sess->proxy_port) + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); + else + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to hub failed (errno=%d, %s)\n", res, strerror(res)); + + goto fail_connecting; } - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n"); + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected to hub, sending query\n"); if (!(client = gg_urlencode((sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n"); goto fail_connecting; } @@ -942,41 +1566,43 @@ else host = ""; -#ifdef __GG_LIBGADU_HAVE_OPENSSL - if (sess->ssl) - appmsg = "appmsg3.asp"; - else + auth = gg_proxy_auth(); + +#ifdef GG_CONFIG_HAVE_OPENSSL + if (sess->ssl) { + snprintf(buf, sizeof(buf) - 1, + "GET %s/appsvc/appmsg3.asp?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n" + "Host: " GG_APPMSG_HOST "\r\n" + "User-Agent: " GG_HTTP_USERAGENT "\r\n" + "Pragma: no-cache\r\n" + "%s" + "\r\n", host, sess->uin, client, sess->last_sysmsg, (auth) ? auth : ""); + } else #endif - appmsg = "appmsg2.asp"; - - auth = gg_proxy_auth(); - - snprintf(buf, sizeof(buf) - 1, - "GET %s/appsvc/%s?fmnumber=%u&version=%s&lastmsg=%d HTTP/1.0\r\n" - "Host: " GG_APPMSG_HOST "\r\n" - "User-Agent: " GG_HTTP_USERAGENT "\r\n" - "Pragma: no-cache\r\n" - "%s" - "\r\n", host, appmsg, sess->uin, client, sess->last_sysmsg, (auth) ? auth : ""); - - if (auth) - free(auth); - + { + snprintf(buf, sizeof(buf) - 1, + "GET %s/appsvc/appmsg_ver8.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s HTTP/1.0\r\n" + "Host: " GG_APPMSG_HOST "\r\n" + "%s" + "\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : ""); + } + + free(auth); free(client); - /* zwolnij pami po wersji klienta. */ + /* zwolnij pamięć po wersji klienta. */ if (sess->client_version) { free(sess->client_version); sess->client_version = NULL; } - gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf); - - /* zapytanie jest krtkie, wic zawsze zmieci si - * do bufora gniazda. jeli write() zwrci mniej, - * stao si co zego. */ + gg_debug_session(sess, GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", buf); + + /* zapytanie jest krótkie, więc zawsze zmieści się + * do bufora gniazda. jeśli write() zwróci mniej, + * stało się coś złego. */ if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() sending query failed\n"); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_WRITING; @@ -999,72 +1625,36 @@ int port = GG_DEFAULT_PORT; struct in_addr addr; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n"); - - /* czytamy lini z gniazda i obcinamy \r\n. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_DATA\n"); + + /* czytamy linię z gniazda i obcinamy \r\n. */ gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); - gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf); - - /* sprawdzamy, czy wszystko w porzdku. */ + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http header (%s)\n", buf); + + /* sprawdzamy, czy wszystko w porządku. */ if (strncmp(buf, "HTTP/1.", 7) || strncmp(buf + 9, "200", 3)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() that's not what we've expected, trying direct connection\n"); - - close(sess->fd); - - /* jeli otrzymalimy jakie dziwne informacje, - * prbujemy si czy z pominiciem huba. */ - if (sess->proxy_addr && sess->proxy_port) { - if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { - /* trudno. nie wyszo. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail_connecting; - } - - sess->state = GG_STATE_CONNECTING_GG; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - break; - } - - sess->port = GG_DEFAULT_PORT; - - /* czymy si na port 8074 huba. */ - if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); - - sess->port = GG_HTTPS_PORT; - - /* czymy si na port 443. */ - if ((sess->fd = gg_connect(&sess->hub_addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail_connecting; - } - } - - sess->state = GG_STATE_CONNECTING_GG; - sess->check = GG_CHECK_WRITE; - sess->timeout = GG_DEFAULT_TIMEOUT; - break; + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n"); + goto fail_connecting; } - - /* ignorujemy reszt nagwka. */ + + /* ignorujemy resztę nagłówka. */ while (strcmp(buf, "\r\n") && strcmp(buf, "")) gg_read_line(sess->fd, buf, sizeof(buf) - 1); - /* czytamy pierwsz lini danych. */ + /* czytamy pierwszą linię danych. */ gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); - - /* jeli pierwsza liczba w linii nie jest rwna zeru, - * oznacza to, e mamy wiadomo systemow. */ + + /* jeśli pierwsza liczba w linii nie jest równa zeru, + * oznacza to, że mamy wiadomość systemową. */ if (atoi(buf)) { char tmp[1024], *foo, *sysmsg_buf = NULL; int len = 0; - + while (gg_read_line(sess->fd, tmp, sizeof(tmp) - 1)) { if (!(foo = realloc(sysmsg_buf, len + strlen(tmp) + 2))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n"); break; } @@ -1074,23 +1664,32 @@ strcpy(sysmsg_buf, tmp); else strcat(sysmsg_buf, tmp); - + len += strlen(tmp); } - + e->type = GG_EVENT_MSG; e->event.msg.msgclass = atoi(buf); e->event.msg.sender = 0; - e->event.msg.message = (unsigned char *)sysmsg_buf; + e->event.msg.message = (unsigned char*) sysmsg_buf; } - + close(sess->fd); - - gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf); + + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf); /* analizujemy otrzymane dane. */ tmp = buf; - + +#ifdef GG_CONFIG_HAVE_OPENSSL + if (!sess->ssl) +#endif + { + while (*tmp && *tmp != ' ') + tmp++; + while (*tmp && *tmp == ' ') + tmp++; + } while (*tmp && *tmp != ' ') tmp++; while (*tmp && *tmp == ' ') @@ -1105,36 +1704,43 @@ port = atoi(tmp + 1); } + if (!strcmp(host, "notoperating")) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno)); + sess->fd = -1; + goto fail_unavailable; + } + addr.s_addr = inet_addr(host); sess->server_addr = addr.s_addr; if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) { - /* jeli mamy proxy, czymy si z nim. */ + /* jeśli mamy proxy, łączymy się z nim. */ if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) { - /* nie wyszo? trudno. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); + /* nie wyszło? trudno. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } - + sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; + sess->soft_timeout = 1; break; } sess->port = port; - /* czymy si z waciwym serwerem. */ + /* łączymy się z właściwym serwerem. */ if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno)); sess->port = GG_HTTPS_PORT; - /* nie wyszo? prbujemy portu 443. */ + /* nie wyszło? próbujemy portu 443. */ if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) { - /* ostatnia deska ratunku zawioda? + /* ostatnia deska ratunku zawiodła? * w takim razie zwijamy manatki. */ - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } } @@ -1142,7 +1748,8 @@ sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; sess->timeout = GG_DEFAULT_TIMEOUT; - + sess->soft_timeout = 1; + break; } @@ -1151,14 +1758,16 @@ int res = 0; socklen_t res_size = sizeof(res); - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n"); - - /* jeli wystpi bd podczas czenia si... */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTING_GG\n"); + + sess->soft_timeout = 0; + + /* jeśli wystąpił błąd podczas łączenia się... */ if (sess->async && (sess->timeout == 0 || getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) { - /* jeli nie udao si poczenie z proxy, - * nie mamy czego prbowa wicej. */ + /* jeśli nie udało się połączenie z proxy, + * nie mamy czego próbować więcej. */ if (sess->proxy_addr && sess->proxy_port) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res)); goto fail_connecting; } @@ -1170,36 +1779,46 @@ errno = ETIMEDOUT; #endif -#ifdef __GG_LIBGADU_HAVE_OPENSSL - /* jeli logujemy si po TLS, nie prbujemy - * si czy ju z niczym innym w przypadku - * bdu. nie do, e nie ma sensu, to i - * trzeba by si bawi w tworzenie na nowo +#ifdef GG_CONFIG_HAVE_OPENSSL + /* jeśli logujemy się po TLS, nie próbujemy + * się łączyć już z niczym innym w przypadku + * błędu. nie dość, że nie ma sensu, to i + * trzeba by się bawić w tworzenie na nowo * SSL i SSL_CTX. */ if (sess->ssl) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res)); goto fail_connecting; } #endif - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", res, strerror(res)); + + if (sess->port == GG_HTTPS_PORT) + goto fail_connecting; sess->port = GG_HTTPS_PORT; - /* prbujemy na port 443. */ + /* próbujemy na port 443. */ if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail_connecting; } + + sess->state = GG_STATE_CONNECTING_GG; + sess->check = GG_CHECK_WRITE; + sess->timeout = GG_DEFAULT_TIMEOUT; + sess->soft_timeout = 1; + + break; } - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() connected\n"); - + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n"); + if (gg_proxy_http_only) sess->proxy_port = 0; - /* jeli mamy proxy, wylijmy zapytanie. */ + /* jeśli mamy proxy, wyślijmy zapytanie. */ if (sess->proxy_addr && sess->proxy_port) { char buf[100], *auth = gg_proxy_auth(); struct in_addr addr; @@ -1211,20 +1830,22 @@ snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port); - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n// %s", buf); - - /* wysyamy zapytanie. jest ono na tyle krtkie, - * e musi si zmieci w buforze gniazda. jeli - * write() zawiedzie, stao si co zego. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy request:\n// %s", buf); + + /* wysyłamy zapytanie. jest ono na tyle krótkie, + * że musi się zmieścić w buforze gniazda. jeśli + * write() zawiedzie, stało się coś złego. */ if (write(sess->fd, buf, strlen(buf)) < (signed)strlen(buf)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + free(auth); goto fail_connecting; } if (auth) { - gg_debug(GG_DEBUG_MISC, "// %s", auth); + gg_debug_session(sess, GG_DEBUG_MISC, "// %s", auth); if (write(sess->fd, auth, strlen(auth)) < (signed)strlen(auth)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + free(auth); goto fail_connecting; } @@ -1232,12 +1853,12 @@ } if (write(sess->fd, "\r\n", 2) < 2) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n"); goto fail_connecting; } } -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL if (sess->ssl) { SSL_set_fd(sess->ssl, sess->fd); @@ -1256,19 +1877,19 @@ break; } -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL case GG_STATE_TLS_NEGOTIATION: { int res; X509 *peer; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n"); if ((res = SSL_connect(sess->ssl)) <= 0) { int err = SSL_get_error(sess->ssl, res); if (res == 0) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() disconnected during TLS negotiation\n"); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_TLS; @@ -1277,9 +1898,9 @@ sess->fd = -1; break; } - + if (err == SSL_ERROR_WANT_READ) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to read\n"); sess->state = GG_STATE_TLS_NEGOTIATION; sess->check = GG_CHECK_READ; @@ -1287,7 +1908,7 @@ break; } else if (err == SSL_ERROR_WANT_WRITE) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() wants to write\n"); sess->state = GG_STATE_TLS_NEGOTIATION; sess->check = GG_CHECK_WRITE; @@ -1299,8 +1920,8 @@ ERR_error_string_n(ERR_get_error(), buf, sizeof(buf)); - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf); - + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf); + e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_TLS; sess->state = GG_STATE_IDLE; @@ -1310,20 +1931,20 @@ } } - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n// cipher: %s\n", SSL_get_cipher_name(sess->ssl)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded:\n// cipher: %s\n", SSL_get_cipher_name(sess->ssl)); peer = SSL_get_peer_certificate(sess->ssl); if (!peer) - gg_debug(GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n"); else { char buf[1024]; X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf)); - gg_debug(GG_DEBUG_MISC, "// cert subject: %s\n", buf); + gg_debug_session(sess, GG_DEBUG_MISC, "// cert subject: %s\n", buf); X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf)); - gg_debug(GG_DEBUG_MISC, "// cert issuer: %s\n", buf); + gg_debug_session(sess, GG_DEBUG_MISC, "// cert issuer: %s\n", buf); } sess->state = GG_STATE_READING_KEY; @@ -1336,45 +1957,48 @@ case GG_STATE_READING_KEY: { - struct gg_header *h; + struct gg_header *h; struct gg_welcome *w; - struct gg_login60 l; - unsigned int hash; - char *password = sess->password; + unsigned char *password = (unsigned char*) sess->password; int ret; - - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n"); - - memset(&l, 0, sizeof(l)); - l.dunno2 = 0xbe; - - /* XXX bardzo, bardzo, bardzo gupi pomys na pozbycie - * si tekstu wrzucanego przez proxy. */ + uint8_t login_hash[64]; + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_KEY\n"); + + memset(login_hash, 0, sizeof(login_hash)); + + /* XXX bardzo, bardzo, bardzo głupi pomysł na pozbycie + * się tekstu wrzucanego przez proxy. */ if (sess->proxy_addr && sess->proxy_port) { char buf[100]; strcpy(buf, ""); gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n// %s\n", buf); - + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() proxy response:\n// %s\n", buf); + while (strcmp(buf, "")) { gg_read_line(sess->fd, buf, sizeof(buf) - 1); gg_chomp(buf); if (strcmp(buf, "")) - gg_debug(GG_DEBUG_MISC, "// %s\n", buf); + gg_debug_session(sess, GG_DEBUG_MISC, "// %s\n", buf); } /* XXX niech czeka jeszcze raz w tej samej - * fazie. gupio, ale dziaa. */ + * fazie. głupio, ale działa. */ sess->proxy_port = 0; - + break; } /* czytaj pierwszy pakiet. */ if (!(h = gg_recv_packet(sess))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); + if (errno == EAGAIN) { + sess->check = GG_CHECK_READ; + break; + } + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_READING; @@ -1387,7 +2011,7 @@ } if (h->type != GG_WELCOME) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n"); free(h); close(sess->fd); sess->fd = -1; @@ -1397,61 +2021,127 @@ sess->state = GG_STATE_IDLE; break; } - + w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header)); w->key = gg_fix32(w->key); - hash = gg_login_hash((unsigned char *)password, w->key); - - gg_debug(GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> hash %.8x\n", w->key, hash); - + switch (sess->hash_type) { + case GG_LOGIN_HASH_GG32: + { + unsigned int hash; + + hash = gg_fix32(gg_login_hash(password, w->key)); + gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> GG32 hash %.8x\n", w->key, hash); + memcpy(login_hash, &hash, sizeof(hash)); + + break; + } + + case GG_LOGIN_HASH_SHA1: + { + char tmp[41]; + int i; + + gg_login_hash_sha1((char*) password, w->key, login_hash); + for (i = 0; i < 40; i += 2) + snprintf(tmp + i, sizeof(tmp) - i, "%02x", login_hash[i / 2]); + + gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_watch_fd() challenge %.4x --> SHA1 hash: %s\n", w->key, tmp); + + break; + } + } + free(h); - free(sess->password); sess->password = NULL; - { - struct in_addr dcc_ip; - dcc_ip.s_addr = gg_dcc_ip; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() gg_dcc_ip = %s\n", inet_ntoa(dcc_ip)); - } - if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) { struct sockaddr_in sin; socklen_t sin_len = sizeof(sin); - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n"); if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr)); - l.local_ip = sin.sin_addr.s_addr; + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detected address to %s\n", inet_ntoa(sin.sin_addr)); + sess->client_addr = sin.sin_addr.s_addr; } else { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n"); - l.local_ip = 0; + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n"); + sess->client_addr = 0; } - } else - l.local_ip = gg_dcc_ip; - - l.uin = gg_fix32(sess->uin); - l.hash = gg_fix32(hash); - l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); - l.version = gg_fix32(sess->protocol_version); - l.local_port = gg_fix16(gg_dcc_port); - l.image_size = sess->image_size; - - if (sess->external_addr && sess->external_port > 1023) { - l.external_ip = sess->external_addr; - l.external_port = gg_fix16(sess->external_port); + } else + sess->client_addr = gg_dcc_ip; + + if (sess->protocol_version >= 0x2e) { + struct gg_login80 l; + + uint32_t tmp_version_len = gg_fix32(strlen(GG8_VERSION)); + uint32_t tmp_descr_len = gg_fix32((sess->initial_descr) ? strlen(sess->initial_descr) : 0); + + memset(&l, 0, sizeof(l)); + l.uin = gg_fix32(sess->uin); + memcpy(l.language, GG8_LANG, sizeof(l.language)); + l.hash_type = sess->hash_type; + memcpy(l.hash, login_hash, sizeof(login_hash)); + l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); + l.flags = gg_fix32(0x00800001); + l.features = gg_fix32(sess->protocol_features); + l.image_size = sess->image_size; + l.dunno2 = 0x64; + + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80 packet\n"); + ret = gg_send_packet(sess, GG_LOGIN80, + &l, sizeof(l), + &tmp_version_len, sizeof(uint32_t), GG8_VERSION, strlen(GG8_VERSION), + &tmp_descr_len, sizeof(uint32_t), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, + NULL); + + } else if (sess->protocol_version == 0x2d) { + struct gg_login70 l; + + memset(&l, 0, sizeof(l)); + l.uin = gg_fix32(sess->uin); + l.local_ip = (sess->external_addr) ? sess->external_addr : sess->client_addr; + l.local_port = gg_fix16((sess->external_port > 1023) ? sess->external_port : gg_dcc_port); + l.hash_type = sess->hash_type; + memcpy(l.hash, login_hash, sizeof(login_hash)); + l.image_size = sess->image_size; + l.dunno2 = 0x64; + l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); + l.version = gg_fix32(sess->protocol_version | sess->protocol_flags); + + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN80BETA packet\n"); + ret = gg_send_packet(sess, GG_LOGIN80BETA, + &l, sizeof(l), + sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, + (sess->initial_descr) ? "\0" : NULL, (sess->initial_descr) ? 1 : 0, + NULL); + } else { + struct gg_login70 l; + + memset(&l, 0, sizeof(l)); + l.local_ip = (sess->external_addr) ? sess->external_addr : sess->client_addr; + l.uin = gg_fix32(sess->uin); + l.local_port = gg_fix16((sess->external_port > 1023) ? sess->external_port : gg_dcc_port); + l.hash_type = sess->hash_type; + memcpy(l.hash, login_hash, sizeof(login_hash)); + l.image_size = sess->image_size; + l.dunno2 = 0xbe; + l.status = gg_fix32(sess->initial_status ? sess->initial_status : GG_STATUS_AVAIL); + l.version = gg_fix32(sess->protocol_version | sess->protocol_flags); + + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN70 packet\n"); + ret = gg_send_packet(sess, GG_LOGIN70, + &l, sizeof(l), + sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, + NULL); } - gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending GG_LOGIN60 packet\n"); - ret = gg_send_packet(sess, GG_LOGIN60, &l, sizeof(l), sess->initial_descr, (sess->initial_descr) ? strlen(sess->initial_descr) : 0, NULL); - free(sess->initial_descr); sess->initial_descr = NULL; if (ret == -1) { - gg_debug(GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno)); errno2 = errno; close(sess->fd); errno = errno2; @@ -1461,8 +2151,9 @@ sess->state = GG_STATE_IDLE; break; } - + sess->state = GG_STATE_READING_REPLY; + sess->check = GG_CHECK_READ; break; } @@ -1471,10 +2162,15 @@ { struct gg_header *h; - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n"); if (!(h = gg_recv_packet(sess))) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); + if (errno == EAGAIN) { + sess->check = GG_CHECK_READ; + break; + } + + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() didn't receive packet (errno=%d, %s)\n", errno, strerror(errno)); e->type = GG_EVENT_CONN_FAILED; e->event.failure = GG_FAILURE_READING; sess->state = GG_STATE_IDLE; @@ -1484,11 +2180,12 @@ sess->fd = -1; break; } - - if (h->type == GG_LOGIN_OK) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n"); + + if (h->type == GG_LOGIN_OK || h->type == GG_NEED_EMAIL || h->type == GG_LOGIN80_OK) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login succeded\n"); e->type = GG_EVENT_CONN_SUCCESS; sess->state = GG_STATE_CONNECTED; + sess->check = GG_CHECK_READ; sess->timeout = -1; sess->status = (sess->initial_status) ? sess->initial_status : GG_STATUS_AVAIL; free(h); @@ -1496,15 +2193,15 @@ } if (h->type == GG_LOGIN_FAILED) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() login failed\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() login failed\n"); e->event.failure = GG_FAILURE_PASSWORD; errno = EACCES; - } else if (h->type == GG_NEED_EMAIL) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() email change needed\n"); - e->event.failure = GG_FAILURE_NEED_EMAIL; + } else if (h->type == GG_DISCONNECTING) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() too many incorrect password attempts\n"); + e->event.failure = GG_FAILURE_INTRUDER; errno = EACCES; } else { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n"); e->event.failure = GG_FAILURE_INVALID; errno = EINVAL; } @@ -1522,13 +2219,13 @@ case GG_STATE_CONNECTED: { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n"); sess->last_event = time(NULL); - + if ((res = gg_watch_fd_connected(sess, e)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() watch_fd_connected failed (errno=%d, %s)\n", errno, strerror(errno)); if (errno == EAGAIN) { e->type = GG_EVENT_NONE; @@ -1536,6 +2233,9 @@ } else res = -1; } + + sess->check = GG_CHECK_READ; + break; } } @@ -1544,10 +2244,13 @@ if (res == -1) { free(e); e = NULL; + } else { + if (sess->send_buf && (sess->state == GG_STATE_READING_REPLY || sess->state == GG_STATE_CONNECTED)) + sess->check |= GG_CHECK_WRITE; } return e; - + fail_connecting: if (sess->fd != -1) { errno2 = errno; @@ -1565,6 +2268,12 @@ e->event.failure = GG_FAILURE_RESOLVING; sess->state = GG_STATE_IDLE; goto done; + +fail_unavailable: + e->type = GG_EVENT_CONN_FAILED; + e->event.failure = GG_FAILURE_UNAVAILABLE; + sess->state = GG_STATE_IDLE; + goto done; } /*
--- a/libpurple/protocols/gg/lib/http.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/gg/lib/http.c Tue Mar 09 21:41:41 2010 +0000 @@ -1,4 +1,4 @@ -/* $Id: http.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: http.c 833 2009-10-01 20:48:01Z wojtekka $ */ /* * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> @@ -14,52 +14,68 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ +/** + * \file http.c + * + * \brief Obsługa połączeń HTTP + */ + #include "libgadu.h" #include <sys/types.h> + #ifndef _WIN32 -#include <sys/wait.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> #endif -#include "libgadu-config.h" +#include "compat.h" +#include "resolver.h" #include <ctype.h> #include <errno.h> + #ifndef _WIN32 -#include <netdb.h> +# include <netdb.h> #endif -#ifdef __GG_LIBGADU_HAVE_PTHREAD -# include <pthread.h> -#endif + +#include <signal.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include "compat.h" - -/* - * gg_http_connect() // funkcja pomocnicza +/** + * Rozpoczyna połączenie HTTP. * - * rozpoczyna poczenie po http. + * Funkcja przeprowadza połączenie HTTP przy połączeniu synchronicznym, + * zwracając wynik w polach struktury \c gg_http, lub błąd, gdy sesja się + * nie powiedzie. + * + * Przy połączeniu asynchronicznym, funkcja rozpoczyna połączenie, a dalsze + * etapy będą przeprowadzane po wykryciu zmian (\c watch) na obserwowanym + * deskryptorze (\c fd) i wywołaniu funkcji \c gg_http_watch_fd(). * - * - hostname - adres serwera - * - port - port serwera - * - async - asynchroniczne poczenie - * - method - metoda http (GET, POST, cokolwiek) - * - path - cieka do zasobu (musi by poprzedzona ,,/'') - * - header - nagwek zapytania plus ewentualne dane dla POST + * Po zakończeniu, należy zwolnić strukturę za pomocą funkcji + * \c gg_http_free(). Połączenie asynchroniczne można zatrzymać w każdej + * chwili za pomocą \c gg_http_stop(). * - * zaalokowana struct gg_http, ktr poniej naley - * zwolni funkcj gg_http_free(), albo NULL jeli wystpi bd. + * \param hostname Adres serwera + * \param port Port serwera + * \param async Flaga asynchronicznego połączenia + * \param method Metoda HTTP + * \param path Ścieżka do zasobu (musi być poprzedzona znakiem '/') + * \param header Nagłówek zapytania plus ewentualne dane dla POST + * + * \return Zaalokowana struktura \c gg_http lub NULL, jeśli wystąpił błąd. + * + * \ingroup http */ struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header) { @@ -70,7 +86,7 @@ errno = EFAULT; return NULL; } - + if (!(h = malloc(sizeof(*h)))) return NULL; memset(h, 0, sizeof(*h)); @@ -80,6 +96,8 @@ h->fd = -1; h->type = GG_SESSION_HTTP; + gg_http_set_resolver(h, GG_RESOLVER_DEFAULT); + if (gg_proxy_enabled) { char *auth = gg_proxy_auth(); @@ -88,9 +106,8 @@ "", header); hostname = gg_proxy_host; h->port = port = gg_proxy_port; + free(auth); - if (auth) - free(auth); } else { h->query = gg_saprintf("%s %s HTTP/1.0\r\n%s", method, path, header); @@ -102,17 +119,11 @@ errno = ENOMEM; return NULL; } - + gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h->query); if (async) { -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (gg_resolve_pthread(&h->fd, &h->resolver, hostname)) { -#elif defined _WIN32 - if (gg_resolve_win32thread(&h->fd, &h->resolver, hostname)) { -#else - if (gg_resolve(&h->fd, &h->pid, hostname)) { -#endif + if (h->resolver_start(&h->fd, &h->resolver, hostname) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver failed\n"); gg_http_free(h); errno = ENOENT; @@ -125,19 +136,16 @@ h->check = GG_CHECK_READ; h->timeout = GG_DEFAULT_TIMEOUT; } else { - struct in_addr *hn, a; + struct in_addr addr; - if (!(hn = gg_gethostbyname(hostname))) { + if (gg_gethostbyname_real(hostname, &addr, 0) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_http_connect() host not found\n"); gg_http_free(h); errno = ENOENT; return NULL; - } else { - a.s_addr = hn->s_addr; - free(hn); } - if (!(h->fd = gg_connect(&a, port, 0)) == -1) { + if ((h->fd = gg_connect(&addr, port, 0)) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_http_connect() connection failed (errno=%d, %s)\n", errno, strerror(errno)); gg_http_free(h); return NULL; @@ -159,10 +167,12 @@ h->callback = gg_http_watch_fd; h->destroy = gg_http_free; - + return h; } +#ifndef DOXYGEN + #define gg_http_error(x) \ close(h->fd); \ h->fd = -1; \ @@ -170,17 +180,22 @@ h->error = x; \ return 0; -/* - * gg_http_watch_fd() - * - * przy asynchronicznej obsudze HTTP funkcj t naley wywoa, jeli - * zmienio si co na obserwowanym deskryptorze. +#endif /* DOXYGEN */ + +/** + * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia. * - * - h - struktura opisujca poczenie + * Operacja będzie zakończona, gdy pole \c state będzie równe + * \c GG_STATE_PARSING. W tym miejscu działanie przejmuje zwykle funkcja + * korzystająca z \c gg_http_watch_fd(). W przypadku błędu połączenia, + * pole \c state będzie równe \c GG_STATE_ERROR, a kod błędu znajdzie się + * w polu \c error. * - * jeli wszystko poszo dobrze to 0, inaczej -1. poczenie bdzie - * zakoczone, jeli h->state == GG_STATE_PARSING. jeli wystpi jaki - * bd, to bdzie tam GG_STATE_ERROR i odpowiedni kod bdu w h->error. + * \param h Struktura połączenia + * + * \return \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup http */ int gg_http_watch_fd(struct gg_http *h) { @@ -205,22 +220,7 @@ close(h->fd); h->fd = -1; -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (h->resolver) { - pthread_cancel(*((pthread_t *) h->resolver)); - free(h->resolver); - h->resolver = NULL; - } -#elif defined _WIN32 - if (h->resolver) { - HANDLE hnd = h->resolver; - TerminateThread(hnd, 0); - CloseHandle(hnd); - h->resolver = NULL; - } -#else - waitpid(h->pid, NULL, 0); -#endif + h->resolver_cleanup(&h->resolver, 0); gg_debug(GG_DEBUG_MISC, "=> http, connecting to %s:%d\n", inet_ntoa(a), h->port); @@ -264,7 +264,7 @@ gg_http_error(GG_ERROR_WRITING); } - if (res < 0 || (size_t)res < strlen(h->query)) { + if (res < strlen(h->query)) { gg_debug(GG_DEBUG_MISC, "=> http, partial header sent (led=%d, sent=%d)\n", strlen(h->query), res); memmove(h->query, h->query + res, strlen(h->query) - res + 1); @@ -346,7 +346,7 @@ h->body_size = 0; line = h->header; *tmp = 0; - + gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header); while (line) { @@ -449,7 +449,7 @@ return 0; } - + if (h->fd != -1) close(h->fd); @@ -460,14 +460,14 @@ return -1; } -#undef gg_http_error - -/* - * gg_http_stop() +/** + * Kończy asynchroniczne połączenie HTTP. * - * jeli poczenie jest w trakcie, przerywa je. nie zwalnia h->data. - * - * - h - struktura opisujca poczenie + * Po zatrzymaniu należy zwolnić zasoby funkcją \c gg_http_free(). + * + * \param h Struktura połączenia + * + * \ingroup http */ void gg_http_stop(struct gg_http *h) { @@ -477,15 +477,20 @@ if (h->state == GG_STATE_ERROR || h->state == GG_STATE_DONE) return; - if (h->fd != -1) + if (h->fd != -1) { close(h->fd); - h->fd = -1; + h->fd = -1; + } + + h->resolver_cleanup(&h->resolver, 1); } -/* - * gg_http_free_fields() // funkcja wewntrzna +/** + * \internal Zwalnia pola struktury \c gg_http. * - * zwalnia pola struct gg_http, ale nie zwalnia samej struktury. + * Funkcja zwalnia same pola, nie zwalnia struktury. + * + * \param h Struktura połączenia */ void gg_http_free_fields(struct gg_http *h) { @@ -501,19 +506,21 @@ free(h->query); h->query = NULL; } - + if (h->header) { free(h->header); h->header = NULL; } } -/* - * gg_http_free() +/** + * Zwalnia zasoby po połączeniu HTTP. * - * prbuje zamkn poczenie i zwalnia pami po nim. + * Jeśli połączenie nie zostało jeszcze zakończone, jest przerywane. * - * - h - struktura, ktr naley zlikwidowa + * \param h Struktura połączenia + * + * \ingroup http */ void gg_http_free(struct gg_http *h) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/libgadu-internal.h Tue Mar 09 21:41:41 2010 +0000 @@ -0,0 +1,30 @@ +/* $Id$ */ + +/* + * (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef LIBGADU_INTERNAL_H +#define LIBGADU_INTERNAL_H + +#include "libgadu.h" + +char *gg_cp_to_utf8(const char *b); +char *gg_utf8_to_cp(const char *b); +int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length); + +#endif /* LIBGADU_INTERNAL_H */
--- a/libpurple/protocols/gg/lib/libgadu.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/gg/lib/libgadu.c Tue Mar 09 21:41:41 2010 +0000 @@ -1,10 +1,11 @@ -/* $Id: libgadu.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: libgadu.c 878 2009-11-16 23:48:19Z wojtekka $ */ /* - * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Wony <speedy@ziew.org> - * Arkadiusz Mikiewicz <arekm@pld-linux.org> - * Tomasz Chiliski <chilek@chilan.com> + * (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl> + * Robert J. Woźny <speedy@ziew.org> + * Arkadiusz Miśkiewicz <arekm@pld-linux.org> + * Tomasz Chiliński <chilek@chilan.com> + * Adam Wysocki <gophi@ekg.chmurka.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -17,227 +18,200 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ +/** + * \file libgadu.c + * + * \brief Główny moduł biblioteki + */ + #include "libgadu.h" +#include "libgadu-config.h" +#include "libgadu-internal.h" #include <sys/types.h> -#ifndef _WIN32 -#include <sys/wait.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#ifdef sun -# include <sys/filio.h> -#endif + +#ifdef _WIN32 +# include <io.h> +# include <fcntl.h> +# include <errno.h> +# define SHUT_RDWR SD_BOTH #else -#include <io.h> -#include <fcntl.h> -#include <errno.h> -#define SHUT_RDWR SD_BOTH +# include <sys/socket.h> +# include <netinet/in.h> +# include <arpa/inet.h> +# ifdef sun +# include <sys/filio.h> +# endif #endif -#include "libgadu-config.h" +#include "compat.h" +#include "protocol.h" +#include "resolver.h" -#include <errno.h> #ifndef _WIN32 -#include <netdb.h> +# include <errno.h> /* on Win32 this is included above */ +# include <netdb.h> #endif -#ifdef __GG_LIBGADU_HAVE_PTHREAD -# include <pthread.h> -#endif + #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <signal.h> +#include <time.h> #include <unistd.h> -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL # include <openssl/err.h> # include <openssl/rand.h> #endif -#include "compat.h" +#define GG_LIBGADU_VERSION "1.9.0-rc2" + +/** + * Poziom rejestracji informacji odpluskwiających. Zmienna jest maską bitową + * składającą się ze stałych \c GG_DEBUG_... + * + * \ingroup debug + */ +int gg_debug_level = 0; -int gg_debug_level = 0; +/** + * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno + * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, są równe + * \c NULL, informacje są wysyłane do standardowego wyjścia błędu (\c stderr). + * + * \param level Poziom rejestracji + * \param format Format wiadomości (zgodny z \c printf) + * \param ap Lista argumentów (zgodna z \c printf) + * + * \note Funkcja jest przesłaniana przez \c gg_debug_handler_session. + * + * \ingroup debug + */ void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL; +/** + * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno + * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, są równe + * \c NULL, informacje są wysyłane do standardowego wyjścia błędu. + * + * \param sess Sesja której dotyczy informacja lub \c NULL + * \param level Poziom rejestracji + * \param format Format wiadomości (zgodny z \c printf) + * \param ap Lista argumentów (zgodna z \c printf) + * + * \note Funkcja przesłania przez \c gg_debug_handler_session. + * + * \ingroup debug + */ +void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap) = NULL; + +/** + * Port gniazda nasłuchującego dla połączeń bezpośrednich. + * + * \ingroup ip + */ int gg_dcc_port = 0; + +/** + * Adres IP gniazda nasłuchującego dla połączeń bezpośrednich. + * + * \ingroup ip + */ unsigned long gg_dcc_ip = 0; +/** + * Adres lokalnego interfejsu IP, z którego wywoływane są wszystkie połączenia. + * + * \ingroup ip + */ unsigned long gg_local_ip = 0; -/* - * zmienne opisujce parametry proxy http. + +/** + * Flaga włączenia połączeń przez serwer pośredniczący. + * + * \ingroup proxy + */ +int gg_proxy_enabled = 0; + +/** + * Adres serwera pośredniczącego. + * + * \ingroup proxy */ char *gg_proxy_host = NULL; + +/** + * Port serwera pośredniczącego. + * + * \ingroup proxy + */ int gg_proxy_port = 0; -int gg_proxy_enabled = 0; + +/** + * Flaga używania serwera pośredniczącego jedynie dla usług HTTP. + * + * \ingroup proxy + */ int gg_proxy_http_only = 0; + +/** + * Nazwa użytkownika do autoryzacji serwera pośredniczącego. + * + * \ingroup proxy + */ char *gg_proxy_username = NULL; + +/** + * Hasło użytkownika do autoryzacji serwera pośredniczącego. + * + * \ingroup proxy + */ char *gg_proxy_password = NULL; -#ifndef lint +#ifndef DOXYGEN + +#ifndef lint static char rcsid[] #ifdef __GNUC__ __attribute__ ((unused)) #endif -= "$Id: libgadu.c 16856 2006-08-19 01:13:25Z evands $"; -#endif - -#ifdef _WIN32 -/** - * Deal with the fact that you can't select() on a win32 file fd. - * This makes it practically impossible to tie into purple's event loop. - * - * -This is thanks to Tor Lillqvist. - * XXX - Move this to where the rest of the the win32 compatiblity stuff goes when we push the changes back to libgadu. - */ -static int -socket_pipe (int *fds) -{ - SOCKET temp, socket1 = -1, socket2 = -1; - struct sockaddr_in saddr; - int len; - u_long arg; - fd_set read_set, write_set; - struct timeval tv; - - temp = socket(AF_INET, SOCK_STREAM, 0); - - if (temp == INVALID_SOCKET) { - goto out0; - } - - arg = 1; - if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) { - goto out0; - } - - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; - saddr.sin_port = 0; - saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); - - if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) { - goto out0; - } - - if (listen(temp, 1) == SOCKET_ERROR) { - goto out0; - } - - len = sizeof(saddr); - if (getsockname(temp, (struct sockaddr *)&saddr, &len)) { - goto out0; - } - - socket1 = socket(AF_INET, SOCK_STREAM, 0); - - if (socket1 == INVALID_SOCKET) { - goto out0; - } - - arg = 1; - if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { - goto out1; - } - - if (connect(socket1, (struct sockaddr *)&saddr, len) != SOCKET_ERROR || - WSAGetLastError() != WSAEWOULDBLOCK) { - goto out1; - } - - FD_ZERO(&read_set); - FD_SET(temp, &read_set); - - tv.tv_sec = 0; - tv.tv_usec = 0; - - if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) { - goto out1; - } - - if (!FD_ISSET(temp, &read_set)) { - goto out1; - } - - socket2 = accept(temp, (struct sockaddr *) &saddr, &len); - if (socket2 == INVALID_SOCKET) { - goto out1; - } - - FD_ZERO(&write_set); - FD_SET(socket1, &write_set); - - tv.tv_sec = 0; - tv.tv_usec = 0; - - if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) { - goto out2; - } - - if (!FD_ISSET(socket1, &write_set)) { - goto out2; - } - - arg = 0; - if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { - goto out2; - } - - arg = 0; - if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) { - goto out2; - } - - fds[0] = socket1; - fds[1] = socket2; - - closesocket (temp); - - return 0; - -out2: - closesocket (socket2); -out1: - closesocket (socket1); -out0: - closesocket (temp); - errno = EIO; /* XXX */ - - return -1; -} += "$Id: libgadu.c 878 2009-11-16 23:48:19Z wojtekka $"; #endif -/* - * gg_libgadu_version() +#endif /* DOXYGEN */ + +/** + * Zwraca wersję biblioteki. * - * zwraca wersj libgadu. + * \return Wskaźnik na statyczny bufor z wersją biblioteki. * - * - brak - * - * wersja libgadu. + * \ingroup version */ const char *gg_libgadu_version() { return GG_LIBGADU_VERSION; } -/* - * gg_fix32() +/** + * \internal Zamienia kolejność bajtów w 32-bitowym słowie. + * + * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach + * big-endianowych odwraca kolejność bajtów w słowie. * - * zamienia kolejno bajtw w liczbie 32-bitowej tak, by odpowiadaa - * kolejnoci bajtw w protokole GG. ze wzgldu na LE-owo serwera, - * zamienia tylko na maszynach BE-wych. + * \param x Liczba do zamiany * - * - x - liczba do zamiany + * \return Liczba z odpowiednią kolejnością bajtów * - * liczba z odpowiedni kolejnoci bajtw. + * \ingroup helper */ uint32_t gg_fix32(uint32_t x) { -#ifndef __GG_LIBGADU_BIGENDIAN +#ifndef GG_CONFIG_BIGENDIAN return x; #else return (uint32_t) @@ -248,20 +222,21 @@ #endif } -/* - * gg_fix16() +/** + * \internal Zamienia kolejność bajtów w 16-bitowym słowie. + * + * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach + * big-endianowych zamienia kolejność bajtów w słowie. * - * zamienia kolejno bajtw w liczbie 16-bitowej tak, by odpowiadaa - * kolejnoci bajtw w protokole GG. ze wzgldu na LE-owo serwera, - * zamienia tylko na maszynach BE-wych. + * \param x Liczba do zamiany * - * - x - liczba do zamiany + * \return Liczba z odpowiednią kolejnością bajtów * - * liczba z odpowiedni kolejnoci bajtw. + * \ingroup helper */ uint16_t gg_fix16(uint16_t x) { -#ifndef __GG_LIBGADU_BIGENDIAN +#ifndef GG_CONFIG_BIGENDIAN return x; #else return (uint16_t) @@ -270,15 +245,13 @@ #endif } -/* - * gg_login_hash() // funkcja wewntrzna - * - * liczy hash z hasa i danego seeda. - * - * - password - haso do hashowania - * - seed - warto podana przez serwer +/** + * \internal Liczy skrót z hasła i ziarna. * - * hash. + * \param password Hasło + * \param seed Ziarno podane przez serwer + * + * \return Wartość skrótu */ unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) { @@ -304,313 +277,22 @@ return y; } -#ifndef _WIN32 - -/* - * gg_resolve() // funkcja wewntrzna - * - * tworzy potok, forkuje si i w drugim procesie zaczyna resolvowa - * podanego hosta. zapisuje w sesji deskryptor potoku. jeli co tam - * bdzie gotowego, znaczy, e mona wczyta struct in_addr. jeli - * nie znajdzie, zwraca INADDR_NONE. - * - * - fd - wskanik gdzie wrzuci deskryptor - * - pid - gdzie wrzuci pid procesu potomnego - * - hostname - nazwa hosta do zresolvowania +/** + * \internal Odbiera od serwera dane binarne. * - * 0, -1. - */ -int gg_resolve(int *fd, int *pid, const char *hostname) -{ - int pipes[2], res; - struct in_addr a; - int errno2; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve(%p, %p, \"%s\");\n", fd, pid, hostname); - - if (!fd || !pid) { - errno = EFAULT; - return -1; - } - - if (pipe(pipes) == -1) - return -1; - - if ((res = fork()) == -1) { - errno2 = errno; - close(pipes[0]); - close(pipes[1]); - errno = errno2; - return -1; - } - - if (!res) { - if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) { - struct in_addr *hn; - - if (!(hn = gg_gethostbyname(hostname))) - a.s_addr = INADDR_NONE; - else { - a.s_addr = hn->s_addr; - free(hn); - } - } - - write(pipes[1], &a, sizeof(a)); - - _exit(0); - } - - close(pipes[1]); - - *fd = pipes[0]; - *pid = res; - - return 0; -} -#endif - -#ifdef __GG_LIBGADU_HAVE_PTHREAD - -struct gg_resolve_pthread_data { - char *hostname; - int fd; -}; - -static void *gg_resolve_pthread_thread(void *arg) -{ - struct gg_resolve_pthread_data *d = arg; - struct in_addr a; - - pthread_detach(pthread_self()); - - if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { - struct in_addr *hn; - - if (!(hn = gg_gethostbyname(d->hostname))) - a.s_addr = INADDR_NONE; - else { - a.s_addr = hn->s_addr; - free(hn); - } - } - - write(d->fd, &a, sizeof(a)); - close(d->fd); - - free(d->hostname); - d->hostname = NULL; - - free(d); - - pthread_exit(NULL); - - return NULL; /* eby kompilator nie marudzi */ -} - -/* - * gg_resolve_pthread() // funkcja wewntrzna - * - * tworzy potok, nowy wtek i w nim zaczyna resolvowa podanego hosta. - * zapisuje w sesji deskryptor potoku. jeli co tam bdzie gotowego, - * znaczy, e mona wczyta struct in_addr. jeli nie znajdzie, zwraca - * INADDR_NONE. - * - * - fd - wskanik do zmiennej przechowujcej desktyptor resolvera - * - resolver - wskanik do wskanika resolvera - * - hostname - nazwa hosta do zresolvowania + * Funkcja odbiera dane od serwera zajmując się TLS w razie konieczności. * - * 0, -1. - */ -int gg_resolve_pthread(int *fd, void **resolver, const char *hostname) -{ - struct gg_resolve_pthread_data *d = NULL; - pthread_t *tmp; - int pipes[2], new_errno; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_pthread(%p, %p, \"%s\");\n", fd, resolver, hostname); - - if (!resolver || !fd || !hostname) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() invalid arguments\n"); - errno = EFAULT; - return -1; - } - - if (!(tmp = malloc(sizeof(pthread_t)))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory for pthread id\n"); - return -1; - } - - if (pipe(pipes) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); - free(tmp); - return -1; - } - - if (!(d = malloc(sizeof(*d)))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n"); - new_errno = errno; - goto cleanup; - } - - d->hostname = NULL; - - if (!(d->hostname = strdup(hostname))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() out of memory\n"); - new_errno = errno; - goto cleanup; - } - - d->fd = pipes[1]; - - if (pthread_create(tmp, NULL, gg_resolve_pthread_thread, d)) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_phread() unable to create thread\n"); - new_errno = errno; - goto cleanup; - } - - gg_debug(GG_DEBUG_MISC, "// gg_resolve_pthread() %p\n", tmp); - - *resolver = tmp; - - *fd = pipes[0]; - - return 0; - -cleanup: - if (d) { - free(d->hostname); - free(d); - } - - close(pipes[0]); - close(pipes[1]); - - free(tmp); - - errno = new_errno; - - return -1; -} - -#elif defined _WIN32 - -struct gg_resolve_win32thread_data { - char *hostname; - int fd; -}; - -static DWORD WINAPI gg_resolve_win32thread_thread(LPVOID arg) -{ - struct gg_resolve_win32thread_data *d = arg; - struct in_addr a; - - if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { - struct in_addr *hn; - - if (!(hn = gg_gethostbyname(d->hostname))) - a.s_addr = INADDR_NONE; - else { - a.s_addr = hn->s_addr; - free(hn); - } - } - - write(d->fd, &a, sizeof(a)); - close(d->fd); - - free(d->hostname); - d->hostname = NULL; - - free(d); - - return 0; -} - - -int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname) -{ - struct gg_resolve_win32thread_data *d = NULL; - HANDLE h; - DWORD dwTId; - int pipes[2], new_errno; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_win32thread(%p, %p, \"%s\");\n", fd, resolver, hostname); - - if (!resolver || !fd || !hostname) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() invalid arguments\n"); - errno = EFAULT; - return -1; - } - - if (socket_pipe(pipes) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); - return -1; - } - - if (!(d = malloc(sizeof(*d)))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); - new_errno = GetLastError(); - goto cleanup; - } - - d->hostname = NULL; - - if (!(d->hostname = strdup(hostname))) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); - new_errno = GetLastError(); - goto cleanup; - } - - d->fd = pipes[1]; - - h = CreateThread(NULL, 0, gg_resolve_win32thread_thread, - d, 0, &dwTId); - - if (h == NULL) { - gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create thread\n"); - new_errno = GetLastError(); - goto cleanup; - } - - *resolver = h; - *fd = pipes[0]; - - return 0; - -cleanup: - if (d) { - free(d->hostname); - free(d); - } - - close(pipes[0]); - close(pipes[1]); - - errno = new_errno; - - return -1; - -} -#endif - -/* - * gg_read() // funkcja pomocnicza + * \param sess Struktura sesji + * \param buf Bufor na danymi + * \param length Długość bufora * - * czyta z gniazda okrelon ilo bajtw. bierze pod uwag, czy mamy - * poczenie zwyke czy TLS. - * - * - sess - sesja, - * - buf - bufor, - * - length - ilo bajtw, - * - * takie same wartoci jak read(). + * \return To samo co funkcja systemowa \c read */ int gg_read(struct gg_session *sess, char *buf, int length) { int res; -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL if (sess->ssl) { int err; @@ -631,23 +313,22 @@ return res; } -/* - * gg_write() // funkcja pomocnicza +/** + * \internal Wysyła do serwera dane binarne. * - * zapisuje do gniazda okrelon ilo bajtw. bierze pod uwag, czy mamy - * poczenie zwyke czy TLS. + * Funkcja wysyła dane do serwera zajmując się TLS w razie konieczności. * - * - sess - sesja, - * - buf - bufor, - * - length - ilo bajtw, + * \param sess Struktura sesji + * \param buf Bufor z danymi + * \param length Długość bufora * - * takie same wartoci jak write(). + * \return To samo co funkcja systemowa \c write */ int gg_write(struct gg_session *sess, const char *buf, int length) { int res = 0; -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL if (sess->ssl) { int err; @@ -664,19 +345,48 @@ } else #endif { - int written = 0; - - while (written < length) { - res = write(sess->fd, buf + written, length - written); + if (!sess->async) { + int written = 0; + + while (written < length) { + res = write(sess->fd, buf + written, length - written); + + if (res == -1) { + if (errno != EINTR) + break; + + continue; + } + + written += res; + res = written; + } + } else { + if (!sess->send_buf) + res = write(sess->fd, buf, length); + else + res = 0; if (res == -1) { - if (errno == EAGAIN) - continue; - else - break; - } else { - written += res; - res = written; + if (errno != EAGAIN) + return res; + + res = 0; + } + + if (res < length) { + char *tmp; + + if (!(tmp = realloc(sess->send_buf, sess->send_left + length - res))) { + errno = ENOMEM; + return -1; + } + + sess->send_buf = tmp; + + memcpy(sess->send_buf + sess->send_left, buf + res, length - res); + + sess->send_left += length - res; } } } @@ -684,17 +394,19 @@ return res; } -/* - * gg_recv_packet() // funkcja wewntrzna +/** + * \internal Odbiera pakiet od serwera. * - * odbiera jeden pakiet i zwraca wskanik do niego. pami po nim - * naley zwolni za pomoc free(). + * Funkcja odczytuje nagłówek pakietu, a następnie jego zawartość i zwraca + * w zaalokowanym buforze. * - * - sess - opis sesji + * Przy połączeniach asynchronicznych, funkcja może nie być w stanie + * skompletować całego pakietu -- w takim przypadku zwróci -1, a kodem błędu + * będzie \c EAGAIN. * - * w przypadku bdu NULL, kod bdu w errno. naley zwrci uwag, e gdy - * poczenie jest nieblokujce, a kod bdu wynosi EAGAIN, nie udao si - * odczyta caego pakietu i nie naley tego traktowa jako bd. + * \param sess Struktura sesji + * + * \return Wskaźnik do zaalokowanego bufora */ void *gg_recv_packet(struct gg_session *sess) { @@ -702,8 +414,8 @@ char *buf = NULL; int ret = 0, offset, size = 0; - gg_debug(GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess); - + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess); + if (!sess) { errno = EFAULT; return NULL; @@ -712,7 +424,7 @@ if (sess->recv_left < 1) { if (sess->header_buf) { memcpy(&h, sess->header_buf, sess->header_done); - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv: resuming last read (%d bytes left)\n", sizeof(h) - sess->header_done); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv: resuming last read (%d bytes left)\n", sizeof(h) - sess->header_done); free(sess->header_buf); sess->header_buf = NULL; } else @@ -721,34 +433,36 @@ while (sess->header_done < sizeof(h)) { ret = gg_read(sess, (char*) &h + sess->header_done, sizeof(h) - sess->header_done); - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv(%d,%p,%d) = %d\n", sess->fd, &h + sess->header_done, sizeof(h) - sess->header_done, ret); if (!ret) { errno = ECONNRESET; - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n"); return NULL; } if (ret == -1) { if (errno == EINTR) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() interrupted system call, resuming\n"); continue; } if (errno == EAGAIN) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() incomplete header received\n"); if (!(sess->header_buf = malloc(sess->header_done))) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() not enough memory\n"); return NULL; } memcpy(sess->header_buf, &h, sess->header_done); + errno = EAGAIN; + return NULL; } - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno)); return NULL; } @@ -761,22 +475,22 @@ h.length = gg_fix32(h.length); } else memcpy(&h, sess->recv_buf, sizeof(h)); - - /* jakie sensowne limity na rozmiar pakietu */ + + /* jakieś sensowne limity na rozmiar pakietu */ if (h.length > 65535) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() invalid packet length (%d)\n", h.length); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() invalid packet length (%d)\n", h.length); errno = ERANGE; return NULL; } if (sess->recv_left > 0) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() resuming last gg_recv_packet()\n"); size = sess->recv_left; offset = sess->recv_done; buf = sess->recv_buf; } else { if (!(buf = malloc(sizeof(h) + h.length + 1))) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n"); return NULL; } @@ -788,24 +502,23 @@ while (size > 0) { ret = gg_read(sess, buf + sizeof(h) + offset, size); - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv(%d,%p,%d) = %d\n", sess->fd, buf + sizeof(h) + offset, size, ret); if (!ret) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n"); - free(buf); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n"); errno = ECONNRESET; return NULL; } if (ret > -1 && ret <= size) { offset += ret; size -= ret; - } else if (ret == -1) { + } else if (ret == -1) { int errno2 = errno; - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno)); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno)); errno = errno2; if (errno == EAGAIN) { - gg_debug(GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() %d bytes received, %d left\n", offset, size); sess->recv_buf = buf; sess->recv_left = size; sess->recv_done = offset; @@ -823,49 +536,45 @@ if ((gg_debug_level & GG_DEBUG_DUMP)) { unsigned int i; - gg_debug(GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type); - for (i = 0; i < sizeof(h) + h.length; i++) - gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]); - gg_debug(GG_DEBUG_DUMP, "\n"); + gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_recv_packet(%.2x)", h.type); + for (i = 0; i < sizeof(h) + h.length; i++) + gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", (unsigned char) buf[i]); + gg_debug_session(sess, GG_DEBUG_DUMP, "\n"); } return buf; } -/* - * gg_send_packet() // funkcja wewntrzna +/** + * \internal Wysyła pakiet do serwera. * - * konstruuje pakiet i wysya go do serwera. + * Funkcja konstruuje pakiet do wysłania z dowolnej liczby fragmentów. Jeśli + * rozmiar pakietu jest za duży, by móc go wysłać za jednym razem, pozostała + * część zostanie zakolejkowana i wysłana, gdy będzie to możliwe. * - * - sock - deskryptor gniazda - * - type - typ pakietu - * - payload_1 - pierwsza cz pakietu - * - payload_length_1 - dugo pierwszej czci - * - payload_2 - druga cz pakietu - * - payload_length_2 - dugo drugiej czci - * - ... - kolejne czci pakietu i ich dugoci - * - NULL - kocowym parametr (konieczny!) + * \param sess Struktura sesji + * \param type Rodzaj pakietu + * \param ... Lista kolejnych części pakietu (wskaźnik na bufor i długość + * typu \c int) zakończona \c NULL * - * jeli si powiodo, zwraca 0, w przypadku bdu -1. jeli errno == ENOMEM, - * zabrako pamici. inaczej by bd przy wysyaniu pakietu. dla errno == 0 - * nie wysano caego pakietu. + * \return 0 jeśli się powiodło, -1 w przypadku błędu */ int gg_send_packet(struct gg_session *sess, int type, ...) { struct gg_header *h; char *tmp; - int tmp_length; + unsigned int tmp_length; void *payload; unsigned int payload_length; va_list ap; int res; - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...)\n", sess, type); + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...);\n", sess, type); tmp_length = sizeof(struct gg_header); if (!(tmp = malloc(tmp_length))) { - gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n"); return -1; } @@ -879,14 +588,14 @@ payload_length = va_arg(ap, unsigned int); if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) { - gg_debug(GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() not enough memory for payload\n"); free(tmp); va_end(ap); return -1; } tmp = tmp2; - + memcpy(tmp + tmp_length, payload, payload_length); tmp_length += payload_length; @@ -902,52 +611,81 @@ if ((gg_debug_level & GG_DEBUG_DUMP)) { int i; - gg_debug(GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type)); + gg_debug_session(sess, GG_DEBUG_DUMP, "// gg_send_packet(0x%.2x)", gg_fix32(h->type)); for (i = 0; i < tmp_length; ++i) - gg_debug(GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]); - gg_debug(GG_DEBUG_DUMP, "\n"); + gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", (unsigned char) tmp[i]); + gg_debug_session(sess, GG_DEBUG_DUMP, "\n"); } - - if ((res = gg_write(sess, tmp, tmp_length)) < tmp_length) { - gg_debug(GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno)); - free(tmp); + + res = gg_write(sess, tmp, tmp_length); + + free(tmp); + + if (res == -1) { + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() write() failed. res = %d, errno = %d (%s)\n", res, errno, strerror(errno)); return -1; } - - free(tmp); + + if (sess->async) + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() partial write(), %d sent, %d left, %d total left\n", res, tmp_length - res, sess->send_left); + + if (sess->send_buf) + sess->check |= GG_CHECK_WRITE; + return 0; } -/* - * gg_session_callback() // funkcja wewntrzna +/** + * \internal Funkcja zwrotna sesji. + * + * Pole \c callback struktury \c gg_session zawiera wskaźnik do tej funkcji. + * Wywołuje ona \c gg_watch_fd i zachowuje wynik w polu \c event. + * + * \note Korzystanie z tej funkcjonalności nie jest już zalecane. * - * wywoywany z gg_session->callback, wykonuje gg_watch_fd() i pakuje - * do gg_session->event jego wynik. + * \param sess Struktura sesji + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu */ -static int gg_session_callback(struct gg_session *s) +static int gg_session_callback(struct gg_session *sess) { - if (!s) { + if (!sess) { errno = EFAULT; return -1; } - return ((s->event = gg_watch_fd(s)) != NULL) ? 0 : -1; + return ((sess->event = gg_watch_fd(sess)) != NULL) ? 0 : -1; } -/* - * gg_login() +/** + * Łączy się z serwerem Gadu-Gadu. * - * rozpoczyna procedur czenia si z serwerem. reszt obsuguje si przez - * gg_watch_fd(). + * Przy połączeniu synchronicznym funkcja zakończy działanie po nawiązaniu + * połączenia lub gdy wystąpi błąd. Po udanym połączeniu należy wywoływać + * funkcję \c gg_watch_fd(), która odbiera informacje od serwera i zwraca + * informacje o zdarzeniach. * - * UWAGA! program musi obsuy SIGCHLD, jeli czy si asynchronicznie, - * eby poprawnie zamkn proces resolvera. + * Przy połączeniu asynchronicznym funkcja rozpocznie procedurę połączenia + * i zwróci zaalokowaną strukturę. Pole \c fd struktury \c gg_session zawiera + * deskryptor, który należy obserwować funkcją \c select, \c poll lub za + * pomocą mechanizmów użytej pętli zdarzeń (Glib, Qt itp.). Pole \c check + * jest maską bitową mówiącą, czy biblioteka chce być informowana o możliwości + * odczytu danych (\c GG_CHECK_READ) czy zapisu danych (\c GG_CHECK_WRITE). + * Po zaobserwowaniu zmian na deskryptorze należy wywołać funkcję + * \c gg_watch_fd(). Podczas korzystania z połączeń asynchronicznych, w trakcie + * połączenia może zostać stworzony dodatkowy proces rozwiązujący nazwę + * serwera -- z tego powodu program musi poprawnie obsłużyć sygnał SIGCHLD. * - * - p - struktura opisujca pocztkowy stan. wymagane pola: uin, - * password + * \note Po nawiązaniu połączenia z serwerem należy wysłać listę kontaktów + * za pomocą funkcji \c gg_notify() lub \c gg_notify_ex(). * - * w przypadku bdu NULL, jeli idzie dobrze (async) albo poszo - * dobrze (sync), zwrci wskanik do zaalokowanej struct gg_session. + * \param p Struktura opisująca parametry połączenia. Wymagane pola: uin, + * password, async. + * + * \return Wskaźnik do zaalokowanej struktury sesji \c gg_session lub NULL + * w przypadku błędu. + * + * \ingroup login */ struct gg_session *gg_login(const struct gg_login_params *p) { @@ -981,8 +719,9 @@ goto fail; } - if (p->status_descr && !(sess->initial_descr = strdup(p->status_descr))) { - gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n"); + if (p->hash_type < 0 || p->hash_type > GG_LOGIN_HASH_SHA1) { + gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unknown hash type (%d)\n", p->hash_type); + errno = EFAULT; goto fail; } @@ -999,18 +738,59 @@ sess->server_addr = p->server_addr; sess->external_port = p->external_port; sess->external_addr = p->external_addr; + + sess->protocol_features = (p->protocol_features & ~(GG_FEATURE_STATUS77 | GG_FEATURE_MSG77)); + + if (!(p->protocol_features & GG_FEATURE_STATUS77)) + sess->protocol_features |= GG_FEATURE_STATUS80; + + if (!(p->protocol_features & GG_FEATURE_MSG77)) + sess->protocol_features |= GG_FEATURE_MSG80; + sess->protocol_version = (p->protocol_version) ? p->protocol_version : GG_DEFAULT_PROTOCOL_VERSION; + if (p->era_omnix) - sess->protocol_version |= GG_ERA_OMNIX_MASK; + sess->protocol_flags |= GG_ERA_OMNIX_MASK; if (p->has_audio) - sess->protocol_version |= GG_HAS_AUDIO_MASK; + sess->protocol_flags |= GG_HAS_AUDIO_MASK; sess->client_version = (p->client_version) ? strdup(p->client_version) : NULL; sess->last_sysmsg = p->last_sysmsg; sess->image_size = p->image_size; sess->pid = -1; + sess->encoding = p->encoding; + + if (gg_session_set_resolver(sess, p->resolver) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unsupported resolver type (%d)\n", p->resolver); + errno = EFAULT; + goto fail; + } + + if (p->status_descr) { + int max_length; + + if (sess->protocol_version >= 0x2d) + max_length = GG_STATUS_DESCR_MAXSIZE; + else + max_length = GG_STATUS_DESCR_MAXSIZE_PRE_8_0; + + if (sess->protocol_version >= 0x2d && p->encoding != GG_ENCODING_UTF8) + sess->initial_descr = gg_cp_to_utf8(p->status_descr); + else + sess->initial_descr = strdup(p->status_descr); + + if (!sess->initial_descr) { + gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for status\n"); + goto fail; + } + + // XXX pamiętać, żeby nie ciąć w środku znaku utf-8 + + if (strlen(sess->initial_descr) > max_length) + sess->initial_descr[max_length] = 0; + } if (p->tls == 1) { -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL char buf[1024]; OpenSSL_add_ssl_algorithms(); @@ -1023,7 +803,7 @@ } rstruct; time(&rstruct.time); - rstruct.ptr = (void *) &rstruct; + rstruct.ptr = (void *) &rstruct; RAND_seed((void *) rdata, sizeof(rdata)); RAND_seed((void *) &rstruct, sizeof(rstruct)); @@ -1050,7 +830,7 @@ gg_debug(GG_DEBUG_MISC, "// gg_login() client requested TLS but no support compiled in\n"); #endif } - + if (gg_proxy_enabled) { hostname = gg_proxy_host; sess->proxy_port = port = gg_proxy_port; @@ -1059,37 +839,50 @@ port = GG_APPMSG_PORT; } - if (!p->async) { - struct in_addr a; + if (p->hash_type) + sess->hash_type = p->hash_type; + else + sess->hash_type = GG_LOGIN_HASH_SHA1; - if (!p->server_addr || !p->server_port) { - if ((a.s_addr = inet_addr(hostname)) == INADDR_NONE) { - struct in_addr *hn; - - if (!(hn = gg_gethostbyname(hostname))) { + if (!p->async) { + struct in_addr addr; + + if (!sess->server_addr) { + if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) { + if (gg_gethostbyname_real(hostname, &addr, 0) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_login() host \"%s\" not found\n", hostname); goto fail; - } else { - a.s_addr = hn->s_addr; - free(hn); } } } else { - a.s_addr = p->server_addr; - port = p->server_port; + addr.s_addr = sess->server_addr; + port = sess->port; } - sess->hub_addr = a.s_addr; + sess->hub_addr = addr.s_addr; if (gg_proxy_enabled) - sess->proxy_addr = a.s_addr; + sess->proxy_addr = addr.s_addr; + + if ((sess->fd = gg_connect(&addr, port, 0)) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + + /* nie wyszło? próbujemy portu 443. */ + if (sess->server_addr) { + sess->port = GG_HTTPS_PORT; - if ((sess->fd = gg_connect(&a, port, 0)) == -1) { - gg_debug(GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno)); - goto fail; + if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, 0)) == -1) { + /* ostatnia deska ratunku zawiodła? + * w takim razie zwijamy manatki. */ + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_login() connection failed (errno=%d, %s)\n", errno, strerror(errno)); + goto fail; + } + } else { + goto fail; + } } - if (p->server_addr && p->server_port) + if (sess->server_addr) sess->state = GG_STATE_CONNECTING_GG; else sess->state = GG_STATE_CONNECTING_HUB; @@ -1114,15 +907,9 @@ return sess; } - + if (!sess->server_addr || gg_proxy_enabled) { -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (gg_resolve_pthread(&sess->fd, &sess->resolver, hostname)) { -#elif defined _WIN32 - if (gg_resolve_win32thread(&sess->fd, &sess->resolver, hostname)) { -#else - if (gg_resolve(&sess->fd, &sess->pid, hostname)) { -#endif + if (sess->resolver_start(&sess->fd, &sess->resolver, hostname) == -1) { gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno)); goto fail; } @@ -1133,49 +920,121 @@ } sess->state = GG_STATE_CONNECTING_GG; sess->check = GG_CHECK_WRITE; + sess->soft_timeout = 1; } return sess; fail: if (sess) { - if (sess->password) - free(sess->password); - if (sess->initial_descr) - free(sess->initial_descr); + free(sess->password); + free(sess->initial_descr); free(sess); } - + return NULL; } -/* - * gg_free_session() +/** + * Wysyła do serwera pakiet utrzymania połączenia. + * + * Klient powinien regularnie co minutę wysyłać pakiet utrzymania połączenia, + * inaczej serwer uzna, że klient stracił łączność z siecią i zerwie + * połączenie. + * + * \param sess Struktura sesji + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu * - * prbuje zamkn poczenia i zwalnia pami zajmowan przez sesj. + * \ingroup login + */ +int gg_ping(struct gg_session *sess) +{ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess); + + if (!sess) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + return gg_send_packet(sess, GG_PING, NULL); +} + +/** + * Kończy połączenie z serwerem. * - * - sess - opis sesji + * Funkcja nie zwalnia zasobów, więc po jej wywołaniu należy użyć + * \c gg_free_session(). Jeśli chce się ustawić opis niedostępności, należy + * wcześniej wywołać funkcję \c gg_change_status_descr() lub + * \c gg_change_status_descr_time(). + * + * \note Jeśli w buforze nadawczym połączenia z serwerem znajdują się jeszcze + * dane (np. z powodu strat pakietów na łączu), prawdopodobnie zostaną one + * utracone przy zrywaniu połączenia. + * + * \param sess Struktura sesji + * + * \ingroup login */ -void gg_free_session(struct gg_session *sess) +void gg_logoff(struct gg_session *sess) { if (!sess) return; - /* XXX dopisa zwalnianie i zamykanie wszystkiego, co mogo zosta */ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess); + + if (GG_S_NA(sess->status)) + gg_change_status(sess, GG_STATUS_NOT_AVAIL); + +#ifdef GG_CONFIG_HAVE_OPENSSL + if (sess->ssl) + SSL_shutdown(sess->ssl); +#endif + + sess->resolver_cleanup(&sess->resolver, 1); - if (sess->password) - free(sess->password); - - if (sess->initial_descr) - free(sess->initial_descr); + if (sess->fd != -1) { + shutdown(sess->fd, SHUT_RDWR); + close(sess->fd); + sess->fd = -1; + } + + if (sess->send_buf) { + free(sess->send_buf); + sess->send_buf = NULL; + sess->send_left = 0; + } +} - if (sess->client_version) - free(sess->client_version); +/** + * Zwalnia zasoby używane przez połączenie z serwerem. Funkcję należy wywołać + * po zamknięciu połączenia z serwerem, by nie doprowadzić do wycieku zasobów + * systemowych. + * + * \param sess Struktura sesji + * + * \ingroup login + */ +void gg_free_session(struct gg_session *sess) +{ + struct gg_dcc7 *dcc; - if (sess->header_buf) - free(sess->header_buf); + if (!sess) + return; + + /* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */ -#ifdef __GG_LIBGADU_HAVE_OPENSSL + free(sess->password); + free(sess->initial_descr); + free(sess->client_version); + free(sess->header_buf); + +#ifdef GG_CONFIG_HAVE_OPENSSL if (sess->ssl) SSL_free(sess->ssl); @@ -1183,23 +1042,7 @@ SSL_CTX_free(sess->ssl_ctx); #endif -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (sess->resolver) { - pthread_cancel(*((pthread_t*) sess->resolver)); - free(sess->resolver); - sess->resolver = NULL; - } -#elif defined _WIN32 - if (sess->resolver) { - HANDLE h = sess->resolver; - TerminateThread(h, 0); - CloseHandle(h); - sess->resolver = NULL; - } -#else - if (sess->pid != -1) - waitpid(sess->pid, NULL, WNOHANG); -#endif + sess->resolver_cleanup(&sess->resolver, 1); if (sess->fd != -1) close(sess->fd); @@ -1207,24 +1050,37 @@ while (sess->images) gg_image_queue_remove(sess, sess->images, 1); + free(sess->send_buf); + + for (dcc = sess->dcc7_list; dcc; dcc = dcc->next) + dcc->sess = NULL; + free(sess); } -/* - * gg_change_status() +#ifndef DOXYGEN + +/** + * \internal Funkcja wysyłająca pakiet zmiany statusu użytkownika. * - * zmienia status uytkownika. przydatne do /away i /busy oraz /quit. + * \param sess Struktura sesji + * \param status Nowy status użytkownika + * \param descr Opis statusu użytkownika (lub \c NULL) + * \param time Czas powrotu w postaci uniksowego znacznika czasu (lub 0) * - * - sess - opis sesji - * - status - nowy status uytkownika + * \return 0 jeśli się powiodło, -1 w przypadku błędu * - * 0, -1. + * \ingroup status */ -int gg_change_status(struct gg_session *sess, int status) +static int gg_change_status_common(struct gg_session *sess, int status, const char *descr, int time) { - struct gg_new_status p; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status); + char *new_descr = NULL; + uint32_t new_time; + int descr_len = 0; + int descr_len_max; + int packet_type; + int append_null = 0; + int res; if (!sess) { errno = EFAULT; @@ -1236,67 +1092,422 @@ return -1; } - p.status = gg_fix32(status); + /* XXX, obcinać stany których stary protokół niezna (czyt. dnd->aw; ffc->av) */ + + /* dodaj flagę obsługi połączeń głosowych zgodną z GG 7.x */ + if ((sess->protocol_version >= 0x2a) && (sess->protocol_version < 0x2d /* ? */ ) && (sess->protocol_flags & GG_HAS_AUDIO_MASK) && !GG_S_I(status)) + status |= GG_STATUS_VOICE_MASK; sess->status = status; - return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), NULL); + if (sess->protocol_version >= 0x2d) { + if (descr != NULL && sess->encoding != GG_ENCODING_UTF8) { + new_descr = gg_cp_to_utf8(descr); + + if (!new_descr) + return -1; + } + + if (sess->protocol_version >= 0x2e) + packet_type = GG_NEW_STATUS80; + else /* sess->protocol_version == 0x2d */ + packet_type = GG_NEW_STATUS80BETA; + descr_len_max = GG_STATUS_DESCR_MAXSIZE; + append_null = 1; + + } else { + packet_type = GG_NEW_STATUS; + descr_len_max = GG_STATUS_DESCR_MAXSIZE_PRE_8_0; + + if (time != 0) + append_null = 1; + } + + if (descr) { + descr_len = strlen((new_descr) ? new_descr : descr); + + if (descr_len > descr_len_max) + descr_len = descr_len_max; + + // XXX pamiętać o tym, żeby nie ucinać w środku znaku utf-8 + } + + if (time) + new_time = gg_fix32(time); + + if (packet_type == GG_NEW_STATUS80) { + struct gg_new_status80 p; + + p.status = gg_fix32(status); + p.flags = gg_fix32(0x00800001); + p.description_size = gg_fix32(descr_len); + res = gg_send_packet(sess, + packet_type, + &p, + sizeof(p), + (new_descr) ? new_descr : descr, + descr_len, + NULL); + + } else { + struct gg_new_status p; + + p.status = gg_fix32(status); + res = gg_send_packet(sess, + packet_type, + &p, + sizeof(p), + (new_descr) ? new_descr : descr, + descr_len, + (append_null) ? "\0" : NULL, + (append_null) ? 1 : 0, + (time) ? &new_time : NULL, + (time) ? sizeof(new_time) : 0, + NULL); + } + + free(new_descr); + return res; } -/* - * gg_change_status_descr() +#endif /* DOXYGEN */ + +/** + * Zmienia status użytkownika. * - * zmienia status uytkownika na opisowy. + * \param sess Struktura sesji + * \param status Nowy status użytkownika + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu * - * - sess - opis sesji - * - status - nowy status uytkownika - * - descr - opis statusu + * \ingroup status + */ +int gg_change_status(struct gg_session *sess, int status) +{ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status); + + return gg_change_status_common(sess, status, NULL, 0); +} + +/** + * Zmienia status użytkownika na status opisowy. * - * 0, -1. + * \param sess Struktura sesji + * \param status Nowy status użytkownika + * \param descr Opis statusu użytkownika + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup status */ int gg_change_status_descr(struct gg_session *sess, int status, const char *descr) { - struct gg_new_status p; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr); - - if (!sess || !descr) { - errno = EFAULT; - return -1; - } + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr); - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - p.status = gg_fix32(status); - - sess->status = status; - - return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), NULL); + return gg_change_status_common(sess, status, descr, 0); } -/* - * gg_change_status_descr_time() - * - * zmienia status uytkownika na opisowy z godzin powrotu. +/** + * Zmienia status użytkownika na status opisowy z podanym czasem powrotu. * - * - sess - opis sesji - * - status - nowy status uytkownika - * - descr - opis statusu - * - time - czas w formacie uniksowym + * \param sess Struktura sesji + * \param status Nowy status użytkownika + * \param descr Opis statusu użytkownika + * \param time Czas powrotu w postaci uniksowego znacznika czasu * - * 0, -1. + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup status */ int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time) { - struct gg_new_status p; - uint32_t newtime; + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time); + + return gg_change_status_common(sess, status, descr, time); +} + +/** + * Wysyła wiadomość do użytkownika. + * + * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać + * do potwierdzenia. + * + * \param sess Struktura sesji + * \param msgclass Klasa wiadomości + * \param recipient Numer adresata + * \param message Treść wiadomości + * + * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu. + * + * \ingroup messages + */ +int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message) +{ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message); + + return gg_send_message_confer_richtext(sess, msgclass, 1, &recipient, message, NULL, 0); +} + +/** + * Wysyła wiadomość formatowaną. + * + * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać + * do potwierdzenia. + * + * \param sess Struktura sesji + * \param msgclass Klasa wiadomości + * \param recipient Numer adresata + * \param message Treść wiadomości + * \param format Informacje o formatowaniu + * \param formatlen Długość informacji o formatowaniu + * + * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu. + * + * \ingroup messages + */ +int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen) +{ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen); + + return gg_send_message_confer_richtext(sess, msgclass, 1, &recipient, message, format, formatlen); +} + +/** + * Wysyła wiadomość w ramach konferencji. + * + * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać + * do potwierdzenia. + * + * \param sess Struktura sesji + * \param msgclass Klasa wiadomości + * \param recipients_count Liczba adresatów + * \param recipients Wskaźnik do tablicy z numerami adresatów + * \param message Treść wiadomości + * + * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu. + * + * \ingroup messages + */ +int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message) +{ + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message); + + return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0); +} + +/** + * \internal Dodaje tekst na koniec bufora. + * + * \param dst Wskaźnik na bufor roboczy + * \param pos Wskaźnik na aktualne położenie w buforze roboczym + * \param src Dodawany tekst + * \param len Długość dodawanego tekstu + */ +static void gg_append(char *dst, int *pos, const void *src, int len) +{ + if (dst != NULL) + memcpy(&dst[*pos], src, len); + + *pos += len; +} + +/** + * \internal Zamienia tekst z formatowaniem Gadu-Gadu na HTML. + * + * \param dst Bufor wynikowy (może być \c NULL) + * \param utf_msg Tekst źródłowy + * \param format Atrybuty tekstu źródłowego + * \param format_len Długość bloku atrybutów tekstu źródłowego + * + * \note Dokleja \c \\0 na końcu bufora wynikowego. + * + * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL). + */ +static int gg_convert_to_html(char *dst, const char *utf_msg, const unsigned char *format, int format_len) +{ + const char span_fmt[] = "<span style=\"color:#%02x%02x%02x; font-family:'MS Shell Dlg 2'; font-size:9pt; \">"; + const int span_len = 75; + const char img_fmt[] = "<img src=\"%02x%02x%02x%02x%02x%02x%02x%02x\">"; + const int img_len = 28; + int char_pos = 0; + int format_idx = 3; + unsigned char old_attr = 0; + const unsigned char *color = (const unsigned char*) "\x00\x00\x00"; + int len, i; + + len = 0; + + for (i = 0; utf_msg[i] != 0; i++) { + unsigned char attr; + int attr_pos; + + if (format_idx + 3 <= format_len) { + attr_pos = format[format_idx] | (format[format_idx + 1] << 8); + attr = format[format_idx + 2]; + } else { + attr_pos = -1; + attr = 0; + } + + if (attr_pos == char_pos) { + format_idx += 3; + + if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0) { + if (char_pos != 0) { + if ((old_attr & GG_FONT_UNDERLINE) != 0) + gg_append(dst, &len, "</u>", 4); + + if ((old_attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "</i>", 4); + + if ((old_attr & GG_FONT_BOLD) != 0) + gg_append(dst, &len, "</b>", 4); - gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time); + gg_append(dst, &len, "</span>", 7); + } + + if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) { + color = &format[format_idx]; + format_idx += 3; + } else { + color = (const unsigned char*) "\x00\x00\x00"; + } + + if (dst != NULL) + sprintf(&dst[len], span_fmt, color[0], color[1], color[2]); + len += span_len; + } else if (char_pos == 0) { + if (dst != NULL) + sprintf(&dst[len], span_fmt, 0, 0, 0); + len += span_len; + } + + if ((attr & GG_FONT_BOLD) != 0) + gg_append(dst, &len, "<b>", 3); + + if ((attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "<i>", 3); + + if ((attr & GG_FONT_UNDERLINE) != 0) + gg_append(dst, &len, "<u>", 3); + + if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) { + if (dst != NULL) { + sprintf(&dst[len], img_fmt, + format[format_idx + 9], + format[format_idx + 8], + format[format_idx + 7], + format[format_idx + 6], + format[format_idx + 5], + format[format_idx + 4], + format[format_idx + 3], + format[format_idx + 2]); + } + + len += img_len; + format_idx += 10; + } + + old_attr = attr; + } else if (i == 0) { + if (dst != NULL) + sprintf(&dst[len], span_fmt, 0, 0, 0); + + len += span_len; + } - if (!sess || !descr || !time) { + switch (utf_msg[i]) { + case '&': + gg_append(dst, &len, "&", 5); + break; + case '<': + gg_append(dst, &len, "<", 4); + break; + case '>': + gg_append(dst, &len, ">", 4); + break; + case '\'': + gg_append(dst, &len, "'", 6); + break; + case '\"': + gg_append(dst, &len, """, 6); + break; + case '\n': + gg_append(dst, &len, "<br>", 4); + break; + case '\r': + break; + default: + if (dst != NULL) + dst[len] = utf_msg[i]; + len++; + } + + /* Sprawdź, czy bajt nie jest kontynuacją znaku unikodowego. */ + + if ((utf_msg[i] & 0xc0) != 0xc0) + char_pos++; + } + + if ((old_attr & GG_FONT_UNDERLINE) != 0) + gg_append(dst, &len, "</u>", 4); + + if ((old_attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "</i>", 4); + + if ((old_attr & GG_FONT_BOLD) != 0) + gg_append(dst, &len, "</b>", 4); + + /* Dla pustych tekstów dodaj pusty <span>. */ + + if (i == 0) { + if (dst != NULL) + sprintf(&dst[len], span_fmt, 0, 0, 0); + + len += span_len; + } + + gg_append(dst, &len, "</span>", 7); + + if (dst != NULL) + dst[len] = 0; + + return len; +} + +/** + * Wysyła wiadomość formatowaną w ramach konferencji. + * + * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać + * do potwierdzenia. + * + * \param sess Struktura sesji + * \param msgclass Klasa wiadomości + * \param recipients_count Liczba adresatów + * \param recipients Wskaźnik do tablicy z numerami adresatów + * \param message Treść wiadomości + * \param format Informacje o formatowaniu + * \param formatlen Długość informacji o formatowaniu + * + * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu. + * + * \ingroup messages + */ +int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen) +{ + struct gg_send_msg s; + struct gg_send_msg80 s80; + struct gg_msg_recipients r; + char *cp_msg = NULL; + char *utf_msg = NULL; + char *html_msg = NULL; + int seq_no; + int i, j, k; + uin_t *recps; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen); + + if (!sess) { errno = EFAULT; return -1; } @@ -1306,75 +1517,184 @@ return -1; } - p.status = gg_fix32(status); + if (message == NULL || recipients_count <= 0 || recipients_count > 0xffff || (recipients_count != 1 && recipients == NULL)) { + errno = EINVAL; + return -1; + } + + if (sess->encoding == GG_ENCODING_UTF8) { + if (!(cp_msg = gg_utf8_to_cp((const char *) message))) + return -1; + + utf_msg = (char*) message; + } else { + if (sess->protocol_version >= 0x2d) { + if (!(utf_msg = gg_cp_to_utf8((const char *) message))) + return -1; + } + + cp_msg = (char*) message; + } + + if (sess->protocol_version < 0x2d) { + if (!sess->seq) + sess->seq = 0x01740000 | (rand() & 0xffff); + seq_no = sess->seq; + sess->seq += (rand() % 0x300) + 0x300; - sess->status = status; + s.msgclass = gg_fix32(msgclass); + s.seq = gg_fix32(seq_no); + } else { + int len; + + // Drobne odchylenie od protokołu. Jeśli wysyłamy kilka + // wiadomości w ciągu jednej sekundy, zwiększamy poprzednią + // wartość, żeby każda wiadomość miała unikalny numer. + + seq_no = time(NULL); + + if (seq_no <= sess->seq) + seq_no = sess->seq + 1; + + sess->seq = seq_no; + + if (format == NULL || formatlen < 3) { + format = (unsigned char*) "\x02\x06\x00\x00\x00\x08\x00\x00\x00"; + formatlen = 9; + } + + len = gg_convert_to_html(NULL, utf_msg, format, formatlen); + + html_msg = malloc(len + 1); + + if (html_msg == NULL) { + seq_no = -1; + goto cleanup; + } + + gg_convert_to_html(html_msg, utf_msg, format, formatlen); - newtime = gg_fix32(time); + s80.seq = gg_fix32(seq_no); + s80.msgclass = gg_fix32(msgclass); + s80.offset_plain = gg_fix32(sizeof(s80) + strlen(html_msg) + 1); + s80.offset_attr = gg_fix32(sizeof(s80) + strlen(html_msg) + 1 + strlen(cp_msg) + 1); + } + + if (recipients_count > 1) { + r.flag = 0x01; + r.count = gg_fix32(recipients_count - 1); + + recps = malloc(sizeof(uin_t) * recipients_count); + + if (!recps) { + seq_no = -1; + goto cleanup; + } + + for (i = 0; i < recipients_count; i++) { + for (j = 0, k = 0; j < recipients_count; j++) { + if (recipients[j] != recipients[i]) { + recps[k] = gg_fix32(recipients[j]); + k++; + } + } + + if (sess->protocol_version < 0x2d) { + s.recipient = gg_fix32(recipients[i]); - return gg_send_packet(sess, GG_NEW_STATUS, &p, sizeof(p), descr, (strlen(descr) > GG_STATUS_DESCR_MAXSIZE) ? GG_STATUS_DESCR_MAXSIZE : strlen(descr), &newtime, sizeof(newtime), NULL); + if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), cp_msg, strlen(cp_msg) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1) + seq_no = -1; + } else { + s80.recipient = gg_fix32(recipients[i]); + + if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80), html_msg, strlen(html_msg) + 1, cp_msg, strlen(cp_msg) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1) + seq_no = -1; + } + } + + free(recps); + } else { + if (sess->protocol_version < 0x2d) { + s.recipient = gg_fix32(recipients[0]); + + if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), cp_msg, strlen(cp_msg) + 1, format, formatlen, NULL) == -1) + seq_no = -1; + } else { + s80.recipient = gg_fix32(recipients[0]); + + if (gg_send_packet(sess, GG_SEND_MSG80, &s80, sizeof(s80), html_msg, strlen(html_msg) + 1, cp_msg, strlen(cp_msg) + 1, format, formatlen, NULL) == -1) + seq_no = -1; + } + } + +cleanup: + if (cp_msg != (char*) message) + free(cp_msg); + + if (utf_msg != (char*) message) + free(utf_msg); + + free(html_msg); + + return seq_no; } -/* - * gg_logoff() +/** + * Wysyła wiadomość binarną przeznaczoną dla klienta. + * + * Wiadomości między klientami przesyła się np. w celu wywołania zwrotnego + * połączenia bezpośredniego. Funkcja zwraca losowy numer sekwencyjny, + * który można zignorować albo wykorzystać do potwierdzenia. * - * wylogowuje uytkownika i zamyka poczenie, ale nie zwalnia pamici. + * \param sess Struktura sesji + * \param msgclass Klasa wiadomości + * \param recipient Numer adresata + * \param message Treść wiadomości + * \param message_len Długość wiadomości * - * - sess - opis sesji + * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu. + * + * \ingroup messages */ -void gg_logoff(struct gg_session *sess) +int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len) { - if (!sess) - return; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess); - - if (GG_S_NA(sess->status & ~GG_STATUS_FRIENDS_MASK)) - gg_change_status(sess, GG_STATUS_NOT_AVAIL); - -#ifdef __GG_LIBGADU_HAVE_OPENSSL - if (sess->ssl) - SSL_shutdown(sess->ssl); -#endif + struct gg_send_msg s; -#ifdef __GG_LIBGADU_HAVE_PTHREAD - if (sess->resolver) { - pthread_cancel(*((pthread_t*) sess->resolver)); - free(sess->resolver); - sess->resolver = NULL; - } -#elif defined _WIN32 - if (sess->resolver) { - HANDLE h = sess->resolver; - TerminateThread(h, 0); - CloseHandle(h); - sess->resolver = NULL; + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient); + + if (!sess) { + errno = EFAULT; + return -1; } -#else - if (sess->pid != -1) { - waitpid(sess->pid, NULL, WNOHANG); - sess->pid = -1; + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; } -#endif - - if (sess->fd != -1) { - shutdown(sess->fd, SHUT_RDWR); - close(sess->fd); - sess->fd = -1; - } + + s.recipient = gg_fix32(recipient); + s.seq = gg_fix32(0); + s.msgclass = gg_fix32(msgclass); + + return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL); } -/* - * gg_image_request() +/** + * Wysyła żądanie obrazka o podanych parametrach. * - * wysya danie wysania obrazka o podanych parametrach. + * Wiadomości obrazkowe nie zawierają samych obrazków, a tylko ich rozmiary + * i sumy kontrolne. Odbiorca najpierw szuka obrazków w swojej pamięci + * podręcznej i dopiero gdy ich nie znajdzie, wysyła żądanie do nadawcy. + * Wynik zostanie przekazany zdarzeniem \c GG_EVENT_IMAGE_REPLY. * - * - sess - opis sesji - * - recipient - numer adresata - * - size - rozmiar obrazka - * - crc32 - suma kontrolna obrazka + * \param sess Struktura sesji + * \param recipient Numer adresata + * \param size Rozmiar obrazka w bajtach + * \param crc32 Suma kontrola obrazka * - * 0/-1 + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup messages */ int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32) { @@ -1383,13 +1703,13 @@ char dummy = 0; int res; - gg_debug(GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32); + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32); if (!sess) { errno = EFAULT; return -1; } - + if (sess->state != GG_STATE_CONNECTED) { errno = ENOTCONN; return -1; @@ -1407,7 +1727,7 @@ r.flag = 0x04; r.size = gg_fix32(size); r.crc32 = gg_fix32(crc32); - + res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), &dummy, 1, &r, sizeof(r), NULL); if (!res) { @@ -1415,14 +1735,14 @@ char *buf; if (!q) { - gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n"); return -1; } buf = malloc(size); if (size && !buf) { - gg_debug(GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n"); free(q); return -1; } @@ -1449,20 +1769,20 @@ return res; } -/* - * gg_image_reply() - * - * wysya dany obrazek. +/** + * Wysyła żądany obrazek. * - * - sess - opis sesji - * - recipient - numer adresata - * - filename - nazwa pliku - * - image - bufor z obrazkiem - * - size - rozmiar obrazka + * \param sess Struktura sesji + * \param recipient Numer adresata + * \param filename Nazwa pliku + * \param image Bufor z obrazkiem + * \param size Rozmiar obrazka * - * 0/-1 + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup messages */ -int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const unsigned char *image, int size) +int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size) { struct gg_msg_image_reply *r; struct gg_send_msg s; @@ -1470,7 +1790,7 @@ char buf[1910]; int res = -1; - gg_debug(GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size); + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size); if (!sess || !filename || !image) { errno = EFAULT; @@ -1487,7 +1807,7 @@ return -1; } - /* wytnij cieki, zostaw tylko nazw pliku */ + /* wytnij ścieżki, zostaw tylko nazwę pliku */ while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\'))) filename = tmp + 1; @@ -1495,7 +1815,7 @@ errno = EINVAL; return -1; } - + s.recipient = gg_fix32(recipient); s.seq = gg_fix32(0); s.msgclass = gg_fix32(GG_CLASS_MSG); @@ -1505,26 +1825,26 @@ r->flag = 0x05; r->size = gg_fix32(size); - r->crc32 = gg_fix32(gg_crc32(0, image, size)); + r->crc32 = gg_fix32(gg_crc32(0, (unsigned char*) image, size)); while (size > 0) { - size_t buflen, chunklen; - + int buflen, chunklen; + /* \0 + struct gg_msg_image_reply */ buflen = sizeof(struct gg_msg_image_reply) + 1; - /* w pierwszym kawaku jest nazwa pliku */ + /* w pierwszym kawałku jest nazwa pliku */ if (r->flag == 0x05) { strcpy(buf + buflen, filename); buflen += strlen(filename) + 1; } - chunklen = ((size_t)size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : (size_t)size; + chunklen = (size >= sizeof(buf) - buflen) ? (sizeof(buf) - buflen) : size; memcpy(buf + buflen, image, chunklen); size -= chunklen; image += chunklen; - + res = gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), buf, buflen + chunklen, NULL); if (res == -1) @@ -1536,83 +1856,34 @@ return res; } -/* - * gg_send_message_ctcp() +/** + * Wysyła do serwera listę kontaktów. * - * wysya wiadomo do innego uytkownika. zwraca losowy numer - * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia. + * Funkcja informuje serwer o liście kontaktów, których statusy będą + * obserwowane lub kontaktów, które bedą blokowane. Dla każdego z \c count + * kontaktów tablica \c userlist zawiera numer, a tablica \c types rodzaj + * kontaktu (\c GG_USER_NORMAL, \c GG_USER_OFFLINE, \c GG_USER_BLOCKED). + * + * Listę kontaktów należy \b zawsze wysyłać po połączeniu, nawet jeśli + * jest pusta. * - * - sess - opis sesji - * - msgclass - rodzaj wiadomoci - * - recipient - numer adresata - * - message - tre wiadomoci - * - message_len - dugo + * \param sess Struktura sesji + * \param userlist Wskaźnik do tablicy numerów kontaktów + * \param types Wskaźnik do tablicy rodzajów kontaktów + * \param count Liczba kontaktów * - * numer sekwencyjny wiadomoci lub -1 w przypadku bdu. + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup contacts */ -int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len) +int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count) { - struct gg_send_msg s; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - s.recipient = gg_fix32(recipient); - s.seq = gg_fix32(0); - s.msgclass = gg_fix32(msgclass); - - return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL); -} + struct gg_notify *n; + uin_t *u; + char *t; + int i, res = 0; -/* - * gg_send_message() - * - * wysya wiadomo do innego uytkownika. zwraca losowy numer - * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomoci - * - recipient - numer adresata - * - message - tre wiadomoci - * - * numer sekwencyjny wiadomoci lub -1 w przypadku bdu. - */ -int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message) -{ - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message(%p, %d, %u, %p)\n", sess, msgclass, recipient, message); - - return gg_send_message_richtext(sess, msgclass, recipient, message, NULL, 0); -} - -/* - * gg_send_message_richtext() - * - * wysya kolorow wiadomo do innego uytkownika. zwraca losowy numer - * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomoci - * - recipient - numer adresata - * - message - tre wiadomoci - * - format - informacje o formatowaniu - * - formatlen - dugo informacji o formatowaniu - * - * numer sekwencyjny wiadomoci lub -1 w przypadku bdu. - */ -int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen) -{ - struct gg_send_msg s; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_richtext(%p, %d, %u, %p, %p, %d);\n", sess, msgclass, recipient, message, format, formatlen); + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count); if (!sess) { errno = EFAULT; @@ -1624,186 +1895,12 @@ return -1; } - if (!message) { - errno = EFAULT; - return -1; - } - - s.recipient = gg_fix32(recipient); - if (!sess->seq) - sess->seq = 0x01740000 | (rand() & 0xffff); - s.seq = gg_fix32(sess->seq); - s.msgclass = gg_fix32(msgclass); - sess->seq += (rand() % 0x300) + 0x300; - - if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen((const char *)message) + 1, format, formatlen, NULL) == -1) - return -1; - - return gg_fix32(s.seq); -} - -/* - * gg_send_message_confer() - * - * wysya wiadomo do kilku uytkownikow (konferencja). zwraca losowy numer - * sekwencyjny, ktry mona zignorowa albo wykorzysta do potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomoci - * - recipients_count - ilo adresatw - * - recipients - numerki adresatw - * - message - tre wiadomoci - * - * numer sekwencyjny wiadomoci lub -1 w przypadku bdu. - */ -int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message) -{ - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer(%p, %d, %d, %p, %p);\n", sess, msgclass, recipients_count, recipients, message); - - return gg_send_message_confer_richtext(sess, msgclass, recipients_count, recipients, message, NULL, 0); -} - -/* - * gg_send_message_confer_richtext() - * - * wysya kolorow wiadomo do kilku uytkownikow (konferencja). zwraca - * losowy numer sekwencyjny, ktry mona zignorowa albo wykorzysta do - * potwierdzenia. - * - * - sess - opis sesji - * - msgclass - rodzaj wiadomoci - * - recipients_count - ilo adresatw - * - recipients - numerki adresatw - * - message - tre wiadomoci - * - format - informacje o formatowaniu - * - formatlen - dugo informacji o formatowaniu - * - * numer sekwencyjny wiadomoci lub -1 w przypadku bdu. - */ -int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen) -{ - struct gg_send_msg s; - struct gg_msg_recipients r; - int i, j, k; - uin_t *recps; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - if (!message || recipients_count <= 0 || recipients_count > 0xffff || !recipients) { - errno = EINVAL; - return -1; - } - - r.flag = 0x01; - r.count = gg_fix32(recipients_count - 1); - - if (!sess->seq) - sess->seq = 0x01740000 | (rand() & 0xffff); - s.seq = gg_fix32(sess->seq); - s.msgclass = gg_fix32(msgclass); - - recps = malloc(sizeof(uin_t) * recipients_count); - if (!recps) - return -1; - - for (i = 0; i < recipients_count; i++) { - - s.recipient = gg_fix32(recipients[i]); - - for (j = 0, k = 0; j < recipients_count; j++) - if (recipients[j] != recipients[i]) { - recps[k] = gg_fix32(recipients[j]); - k++; - } - - if (!i) - sess->seq += (rand() % 0x300) + 0x300; - - if (gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, strlen((const char *)message) + 1, &r, sizeof(r), recps, (recipients_count - 1) * sizeof(uin_t), format, formatlen, NULL) == -1) { - free(recps); - return -1; - } - } - - free(recps); - - return gg_fix32(s.seq); -} - -/* - * gg_ping() - * - * wysya do serwera pakiet ping. - * - * - sess - opis sesji - * - * 0, -1. - */ -int gg_ping(struct gg_session *sess) -{ - gg_debug(GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - return gg_send_packet(sess, GG_PING, NULL); -} - -/* - * gg_notify_ex() - * - * wysya serwerowi list kontaktw (wraz z odpowiadajcymi im typami userw), - * dziki czemu wie, czyj stan nas interesuje. - * - * - sess - opis sesji - * - userlist - wskanik do tablicy numerw - * - types - wskanik do tablicy typw uytkownikw - * - count - ilo numerkw - * - * 0, -1. - */ -int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count) -{ - struct gg_notify *n; - uin_t *u; - char *t; - int i, res = 0; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - if (!userlist || !count) return gg_send_packet(sess, GG_LIST_EMPTY, NULL); - + while (count > 0) { int part_count, packet_type; - + if (count > 400) { part_count = 400; packet_type = GG_NOTIFY_FIRST; @@ -1814,12 +1911,12 @@ if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count))) return -1; - - for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) { + + for (u = userlist, t = types, i = 0; i < part_count; u++, t++, i++) { n[i].uin = gg_fix32(*u); n[i].dunno1 = *t; } - + if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) { free(n); res = -1; @@ -1836,17 +1933,19 @@ return res; } -/* - * gg_notify() +/** + * Wysyła do serwera listę kontaktów. * - * wysya serwerowi list kontaktw, dziki czemu wie, czyj stan nas - * interesuje. + * Funkcja jest odpowiednikiem \c gg_notify_ex(), gdzie wszystkie kontakty + * są rodzaju \c GG_USER_NORMAL. * - * - sess - opis sesji - * - userlist - wskanik do tablicy numerw - * - count - ilo numerkw + * \param sess Struktura sesji + * \param userlist Wskaźnik do tablicy numerów kontaktów + * \param count Liczba kontaktów * - * 0, -1. + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup contacts */ int gg_notify(struct gg_session *sess, uin_t *userlist, int count) { @@ -1854,13 +1953,13 @@ uin_t *u; int i, res = 0; - gg_debug(GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count); - + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_notify(%p, %p, %d);\n", sess, userlist, count); + if (!sess) { errno = EFAULT; return -1; } - + if (sess->state != GG_STATE_CONNECTED) { errno = ENOTCONN; return -1; @@ -1868,10 +1967,10 @@ if (!userlist || !count) return gg_send_packet(sess, GG_LIST_EMPTY, NULL); - + while (count > 0) { int part_count, packet_type; - + if (count > 400) { part_count = 400; packet_type = GG_NOTIFY_FIRST; @@ -1879,15 +1978,15 @@ part_count = count; packet_type = GG_NOTIFY_LAST; } - + if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count))) return -1; - - for (u = userlist, i = 0; i < part_count; u++, i++) { + + for (u = userlist, i = 0; i < part_count; u++, i++) { n[i].uin = gg_fix32(*u); n[i].dunno1 = GG_USER_NORMAL; } - + if (gg_send_packet(sess, packet_type, n, sizeof(*n) * part_count, NULL) == -1) { res = -1; free(n); @@ -1903,73 +2002,27 @@ return res; } -/* - * gg_add_notify_ex() +/** + * Dodaje kontakt. * - * dodaje do listy kontaktw dany numer w trakcie poczenia. - * dodawanemu uytkownikowi okrelamy jego typ (patrz protocol.html) + * Dodaje do listy kontaktów dany numer w trakcie połączenia. Aby zmienić + * rodzaj kontaktu (np. z normalnego na zablokowany), należy najpierw usunąć + * poprzedni rodzaj, ponieważ serwer operuje na maskach bitowych. * - * - sess - opis sesji - * - uin - numer - * - type - typ + * \param sess Struktura sesji + * \param uin Numer kontaktu + * \param type Rodzaj kontaktu * - * 0, -1. + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup contacts */ int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type) { struct gg_add_remove a; - gg_debug(GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type); - - if (!sess) { - errno = EFAULT; - return -1; - } - - if (sess->state != GG_STATE_CONNECTED) { - errno = ENOTCONN; - return -1; - } - - a.uin = gg_fix32(uin); - a.dunno1 = type; - - return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL); -} + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type); -/* - * gg_add_notify() - * - * dodaje do listy kontaktw dany numer w trakcie poczenia. - * - * - sess - opis sesji - * - uin - numer - * - * 0, -1. - */ -int gg_add_notify(struct gg_session *sess, uin_t uin) -{ - return gg_add_notify_ex(sess, uin, GG_USER_NORMAL); -} - -/* - * gg_remove_notify_ex() - * - * usuwa z listy kontaktw w trakcie poczenia. - * usuwanemu uytkownikowi okrelamy jego typ (patrz protocol.html) - * - * - sess - opis sesji - * - uin - numer - * - type - typ - * - * 0, -1. - */ -int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type) -{ - struct gg_add_remove a; - - gg_debug(GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type); - if (!sess) { errno = EFAULT; return -1; @@ -1982,35 +2035,101 @@ a.uin = gg_fix32(uin); a.dunno1 = type; - + + return gg_send_packet(sess, GG_ADD_NOTIFY, &a, sizeof(a), NULL); +} + +/** + * Dodaje kontakt. + * + * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich + * kontaktów to \c GG_USER_NORMAL. + * + * \param sess Struktura sesji + * \param uin Numer kontaktu + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup contacts + */ +int gg_add_notify(struct gg_session *sess, uin_t uin) +{ + return gg_add_notify_ex(sess, uin, GG_USER_NORMAL); +} + +/** + * Usuwa kontakt. + * + * Usuwa z listy kontaktów dany numer w trakcie połączenia. + * + * \param sess Struktura sesji + * \param uin Numer kontaktu + * \param type Rodzaj kontaktu + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup contacts + */ +int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type) +{ + struct gg_add_remove a; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_remove_notify_ex(%p, %u, %d);\n", sess, uin, type); + + if (!sess) { + errno = EFAULT; + return -1; + } + + if (sess->state != GG_STATE_CONNECTED) { + errno = ENOTCONN; + return -1; + } + + a.uin = gg_fix32(uin); + a.dunno1 = type; + return gg_send_packet(sess, GG_REMOVE_NOTIFY, &a, sizeof(a), NULL); } -/* - * gg_remove_notify() +/** + * Usuwa kontakt. * - * usuwa z listy kontaktw w trakcie poczenia. + * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich + * kontaktów to \c GG_USER_NORMAL. * - * - sess - opis sesji - * - uin - numer + * \param sess Struktura sesji + * \param uin Numer kontaktu * - * 0, -1. + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup contacts */ int gg_remove_notify(struct gg_session *sess, uin_t uin) { return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL); } -/* - * gg_userlist_request() +/** + * Wysyła do serwera zapytanie dotyczące listy kontaktów. * - * wysya danie/zapytanie listy kontaktw na serwerze. + * Funkcja służy do importu lub eksportu listy kontaktów do serwera. + * W odróżnieniu od funkcji \c gg_notify(), ta lista kontaktów jest przez + * serwer jedynie przechowywana i nie ma wpływu na połączenie. Format + * listy kontaktów jest ignorowany przez serwer, ale ze względu na + * kompatybilność z innymi klientami, należy przechowywać dane w tym samym + * formacie co oryginalny klient Gadu-Gadu. * - * - sess - opis sesji - * - type - rodzaj zapytania/dania - * - request - tre zapytania/dania (moe by NULL) + * Program nie musi się przejmować fragmentacją listy kontaktów wynikającą + * z protokołu -- wysyła i odbiera kompletną listę. * - * 0, -1 + * \param sess Struktura sesji + * \param type Rodzaj zapytania + * \param request Treść zapytania (może być równe NULL) + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup importexport */ int gg_userlist_request(struct gg_session *sess, char type, const char *request) { @@ -2020,7 +2139,7 @@ errno = EFAULT; return -1; } - + if (sess->state != GG_STATE_CONNECTED) { errno = ENOTCONN; return -1; @@ -2030,7 +2149,7 @@ sess->userlist_blocks = 1; return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL); } - + len = strlen(request); sess->userlist_blocks = 0; @@ -2053,6 +2172,8 @@ return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL); } +/* @} */ + /* * Local variables: * c-indentation-style: k&r
--- a/libpurple/protocols/gg/lib/libgadu.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/gg/lib/libgadu.h Tue Mar 09 21:41:41 2010 +0000 @@ -1,12 +1,13 @@ -/* $Id: libgadu.h 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: libgadu.h.in 878 2009-11-16 23:48:19Z wojtekka $ */ /* - * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl> - * Robert J. Wony <speedy@ziew.org> - * Arkadiusz Mikiewicz <arekm@pld-linux.org> - * Tomasz Chiliski <chilek@chilan.com> + * (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl> + * Robert J. Woźny <speedy@ziew.org> + * Arkadiusz Miśkiewicz <arekm@pld-linux.org> + * Tomasz Chiliński <chilek@chilan.com> * Piotr Wysocki <wysek@linux.bydg.org> * Dawid Jarosz <dawjar@poczta.onet.pl> + * Jakub Zawadzki <darkjames@darkjames.ath.cx> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -19,338 +20,568 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ +/** + * \file libgadu.h + * + * \brief Główny plik nagłówkowy biblioteki + */ + #ifndef __GG_LIBGADU_H #define __GG_LIBGADU_H #ifdef __cplusplus -#ifdef _MSC_VER +#ifdef _WIN32 #pragma pack(push, 1) #endif extern "C" { #endif -#include <libgadu-config.h> #include <sys/types.h> #include <stdio.h> #include <stdarg.h> -#ifdef __GG_LIBGADU_HAVE_OPENSSL +/** \cond ignore */ + +/* Defined if libgadu was compiled for bigendian machine. */ +#undef GG_CONFIG_BIGENDIAN + +/* Defined if this machine has gethostbyname_r(). */ +#undef GG_CONFIG_HAVE_GETHOSTBYNAME_R + +/* Defined if libgadu was compiled and linked with pthread support. */ +#undef GG_CONFIG_HAVE_PTHREAD + +/* Defined if pthread resolver is the default one. */ +#undef GG_CONFIG_PTHREAD_DEFAULT + +/* Defined if this machine has C99-compiliant vsnprintf(). */ +#undef GG_CONFIG_HAVE_C99_VSNPRINTF + +/* Defined if this machine has va_copy(). */ +#undef GG_CONFIG_HAVE_VA_COPY + +/* Defined if this machine has __va_copy(). */ +#undef GG_CONFIG_HAVE___VA_COPY + +/* Defined if this machine supports long long. */ +#undef GG_CONFIG_HAVE_LONG_LONG + +/* Defined if libgadu was compiled and linked with TLS support. */ +#undef GG_CONFIG_HAVE_OPENSSL + +/* Defined if uintX_t types are defined in <stdint.h>. */ +#undef GG_CONFIG_HAVE_STDINT_H + +/* Defined if uintX_t types are defined in <inttypes.h>. */ +#undef GG_CONFIG_HAVE_INTTYPES_H + +/* Defined if uintX_t types are defined in <sys/inttypes.h>. */ +#undef GG_CONFIG_HAVE_SYS_INTTYPES_H + +/* Defined if uintX_t types are defined in <sys/int_types.h>. */ +#undef GG_CONFIG_HAVE_SYS_INT_TYPES_H + +/* Defined if uintX_t types are defined in <sys/types.h>. */ +#undef GG_CONFIG_HAVE_SYS_TYPES_H + +#ifdef GG_CONFIG_HAVE_OPENSSL #include <openssl/ssl.h> #endif -/* - * typedef uin_t - * - * typ reprezentujcy numer osoby. +#ifdef GG_CONFIG_HAVE_STDINT_H +#include <stdint.h> +#else +# ifdef GG_CONFIG_HAVE_INTTYPES_H +# include <inttypes.h> +# else +# ifdef GG_CONFIG_HAVE_SYS_INTTYPES_H +# include <sys/inttypes.h> +# else +# ifdef GG_CONFIG_HAVE_SYS_INT_TYPES_H +# include <sys/int_types.h> +# else +# ifdef GG_CONFIG_HAVE_SYS_TYPES_H +# include <sys/types.h> +# else + +#ifndef __AC_STDINT_H +#define __AC_STDINT_H + +/* ISO C 9X: 7.18 Integer types <stdint.h> */ + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; + +#ifndef __CYGWIN__ +#define __int8_t_defined +typedef signed char int8_t; +typedef signed short int16_t; +typedef signed int int32_t; +#endif + +#endif /* __AC_STDINT_H */ + +# endif +# endif +# endif +# endif +#endif + +/** \endcond */ + +/** + * Numer Gadu-Gadu. */ typedef uint32_t uin_t; -/* - * oglna struktura opisujca rne sesje. przydatna w klientach. +/** + * Identyfikator połączenia bezpośredniego Gadu-Gadu 7.x. + */ +typedef struct { + uint8_t id[8]; +} gg_dcc7_id_t; + +/** + * Makro deklarujące pola wspólne dla struktur sesji. */ #define gg_common_head(x) \ - int fd; /* podgldany deskryptor */ \ - int check; /* sprawdzamy zapis czy odczyt */ \ - int state; /* aktualny stan maszynki */ \ - int error; /* kod bdu dla GG_STATE_ERROR */ \ - int type; /* rodzaj sesji */ \ - int id; /* identyfikator */ \ - int timeout; /* sugerowany timeout w sekundach */ \ - int (*callback)(x*); /* callback przy zmianach */ \ - void (*destroy)(x*); /* funkcja niszczenia */ + int fd; /**< Obserwowany deskryptor */ \ + int check; /**< Informacja o żądaniu odczytu/zapisu (patrz \ref gg_check_t) */ \ + int state; /**< Aktualny stan połączenia (patrz \ref gg_state_t) */ \ + int error; /**< Kod błędu dla \c GG_STATE_ERROR (patrz \ref gg_error_t) */ \ + int type; /**< Rodzaj sesji (patrz \ref gg_session_t) */ \ + int id; /**< Identyfikator sesji */ \ + int timeout; /**< Czas pozostały do zakończenia stanu */ \ + int (*callback)(x*); /**< Funkcja zwrotna */ \ + void (*destroy)(x*); /**< Funkcja zwalniania zasobów */ +/** + * Struktura wspólna dla wszystkich sesji i połączeń. Pozwala na proste + * rzutowanie niezależne od rodzaju połączenia. + */ struct gg_common { gg_common_head(struct gg_common) }; struct gg_image_queue; -/* - * struct gg_session +struct gg_dcc7; + +/** + * Sposób rozwiązywania nazw serwerów. + */ +typedef enum { + GG_RESOLVER_DEFAULT = 0, /**< Domyślny sposób rozwiązywania nazw (jeden z poniższych) */ + GG_RESOLVER_FORK, /**< Rozwiązywanie nazw bazujące na procesach */ + GG_RESOLVER_PTHREAD, /**< Rozwiązywanie nazw bazujące na wątkach */ + GG_RESOLVER_WIN32, + GG_RESOLVER_CUSTOM, /**< Funkcje rozwiązywania nazw dostarczone przed aplikację */ + GG_RESOLVER_INVALID = -1 /**< Nieprawidłowy sposób rozwiązywania nazw (wynik \c gg_session_get_resolver) */ +} gg_resolver_t; + +/** + * Rodzaj kodowania znaków. + */ +typedef enum { + GG_ENCODING_CP1250 = 0, /**< Kodowanie CP1250 */ + GG_ENCODING_UTF8, /**< Kodowanie UTF-8 */ + GG_ENCODING_INVALID = -1 /**< Nieprawidłowe kodowanie */ +} gg_encoding_t; + +/** + * Sesja Gadu-Gadu. * - * struktura opisujca dan sesj. tworzona przez gg_login(), zwalniana - * przez gg_free_session(). + * Tworzona przez funkcję \c gg_login(), zwalniana przez \c gg_free_session(). + * + * \ingroup login */ struct gg_session { gg_common_head(struct gg_session) - int async; /* czy poczenie jest asynchroniczne */ - int pid; /* pid procesu resolvera */ - int port; /* port, z ktrym si czymy */ - int seq; /* numer sekwencyjny ostatniej wiadomoci */ - int last_pong; /* czas otrzymania ostatniego ping/pong */ - int last_event; /* czas otrzymania ostatniego pakietu */ + int async; /**< Flaga połączenia asynchronicznego */ + int pid; /**< Numer procesu rozwiązującego nazwę serwera */ + int port; /**< Port serwera */ + int seq; /**< Numer sekwencyjny ostatniej wiadomości */ + int last_pong; /**< Czas otrzymania ostatniej ramki utrzymaniowej */ + int last_event; /**< Czas otrzymania ostatniego pakietu */ - struct gg_event *event; /* zdarzenie po ->callback() */ + struct gg_event *event; /**< Zdarzenie po wywołaniu \c callback */ - uint32_t proxy_addr; /* adres proxy, keszowany */ - uint16_t proxy_port; /* port proxy */ + uint32_t proxy_addr; /**< Adres serwera pośredniczącego */ + uint16_t proxy_port; /**< Port serwera pośredniczącego */ - uint32_t hub_addr; /* adres huba po resolvniciu */ - uint32_t server_addr; /* adres serwera, od huba */ + uint32_t hub_addr; /**< Adres huba po rozwiązaniu nazwy */ + uint32_t server_addr; /**< Adres serwera otrzymany od huba */ - uint32_t client_addr; /* adres klienta */ - uint16_t client_port; /* port, na ktrym klient sucha */ + uint32_t client_addr; /**< Adres gniazda dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */ + uint16_t client_port; /**< Port gniazda dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */ - uint32_t external_addr; /* adres zewnetrzny klienta */ - uint16_t external_port; /* port zewnetrzny klienta */ + uint32_t external_addr; /**< Publiczny adres dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */ + uint16_t external_port; /**< Publiczny port dla połączeń bezpośrednich do wersji Gadu-Gadu 6.x */ - uin_t uin; /* numerek klienta */ - char *password; /* i jego haso. zwalniane automagicznie */ + uin_t uin; /**< Własny numer Gadu-Gadu */ + char *password; /**< Hasło (zwalniane po użyciu) */ - int initial_status; /* pocztkowy stan klienta */ - int status; /* aktualny stan klienta */ + int initial_status; /**< Początkowy status */ + int status; /**< Aktualny status */ - char *recv_buf; /* bufor na otrzymywane pakiety */ - int recv_done; /* ile ju wczytano do bufora */ - int recv_left; /* i ile jeszcze trzeba wczyta */ + char *recv_buf; /**< Bufor na odbierany pakiety */ + int recv_done; /**< Liczba wczytanych bajtów pakietu */ + int recv_left; /**< Liczba pozostałych do wczytania bajtów pakietu */ - int protocol_version; /* wersja uywanego protokou */ - char *client_version; /* wersja uywanego klienta */ - int last_sysmsg; /* ostatnia wiadomo systemowa */ + int protocol_version; /**< Wersja protokołu (bez flag) */ + char *client_version; /**< Wersja klienta */ + int last_sysmsg; /**< Numer ostatniej wiadomości systemowej */ - char *initial_descr; /* pocztkowy opis stanu klienta */ + char *initial_descr; /**< Początkowy opis statusu */ - void *resolver; /* wskanik na informacje resolvera */ + void *resolver; /**< Dane prywatne procesu lub wątku rozwiązującego nazwę serwera */ - char *header_buf; /* bufor na pocztek nagwka */ - unsigned int header_done;/* ile ju mamy */ + char *header_buf; /**< Bufor na początek nagłówka pakietu */ + unsigned int header_done; /**< Liczba wczytanych bajtów nagłówka pakietu */ -#ifdef __GG_LIBGADU_HAVE_OPENSSL - SSL *ssl; /* sesja TLS */ - SSL_CTX *ssl_ctx; /* kontekst sesji? */ +#ifdef GG_CONFIG_HAVE_OPENSSL + SSL *ssl; /**< Struktura TLS */ + SSL_CTX *ssl_ctx; /**< Kontekst sesji TLS */ #else - void *ssl; /* zachowujemy ABI */ - void *ssl_ctx; + void *ssl; /**< Struktura TLS */ + void *ssl_ctx; /**< Kontekst sesji TLS */ #endif - int image_size; /* maksymalny rozmiar obrazkw w KiB */ + int image_size; /**< Maksymalny rozmiar obsługiwanych obrazków w KiB */ + + char *userlist_reply; /**< Bufor z odbieraną listą kontaktów */ + + int userlist_blocks; /**< Liczba części listy kontaktów */ - char *userlist_reply; /* fragment odpowiedzi listy kontaktw */ + struct gg_image_queue *images; /**< Lista wczytywanych obrazków */ + + int hash_type; /**< Rodzaj funkcji skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1) */ + + char *send_buf; /**< Bufor z danymi do wysłania */ + int send_left; /**< Liczba bajtów do wysłania */ - int userlist_blocks; /* na ile kawakw podzielono list kontaktw */ + struct gg_dcc7 *dcc7_list; /**< Lista połączeń bezpośrednich skojarzonych z sesją */ + + int soft_timeout; /**< Flaga mówiąca, że po przekroczeniu \c timeout należy wywołać \c gg_watch_fd() */ + + int protocol_flags; /**< Flagi protokołu */ - struct gg_image_queue *images; /* aktualnie wczytywane obrazki */ + gg_encoding_t encoding; /**< Rodzaj kodowania znaków */ + + gg_resolver_t resolver_type; /**< Sposób rozwiązywania nazw serwerów */ + int (*resolver_start)(int *fd, void **private_data, const char *hostname); /**< Funkcja rozpoczynająca rozwiązywanie nazwy */ + void (*resolver_cleanup)(void **private_data, int force); /**< Funkcja zwalniająca zasoby po rozwiązaniu nazwy */ + + int protocol_features; /**< Opcje protokołu */ }; -/* - * struct gg_http +/** + * Połączenie HTTP. * - * oglna struktura opisujca stan wszystkich operacji HTTP. tworzona - * przez gg_http_connect(), zwalniana przez gg_http_free(). + * Tworzone przez \c gg_http_connect(), zwalniane przez \c gg_http_free(). + * + * \ingroup http */ struct gg_http { gg_common_head(struct gg_http) - int async; /* czy poczenie asynchroniczne */ - int pid; /* pid procesu resolvera */ - int port; /* port, z ktrym si czymy */ + int async; /**< Flaga połączenia asynchronicznego */ + int pid; /**< Identyfikator procesu rozwiązującego nazwę serwera */ + int port; /**< Port */ + + char *query; /**< Zapytanie HTTP */ + char *header; /**< Odebrany nagłówek */ + int header_size; /**< Rozmiar wczytanego nagłówka */ + char *body; /**< Odebrana strona */ + unsigned int body_size; /**< Rozmiar strony */ + + void *data; /**< Dane prywatne usługi HTTP */ - char *query; /* bufor zapytania http */ - char *header; /* bufor nagwka */ - int header_size; /* rozmiar wczytanego nagwka */ - char *body; /* bufor otrzymanych informacji */ - unsigned int body_size; /* oczekiwana ilo informacji */ + char *user_data; /**< Dane prywatne użytkownika (nie są zwalniane) */ + + void *resolver; /**< Dane prywatne procesu lub wątku rozwiązującego nazwę */ + + unsigned int body_done; /**< Liczba odebranych bajtów strony */ - void *data; /* dane danej operacji http */ - - char *user_data; /* dane uytkownika, nie s zwalniane przez gg_http_free() */ + gg_resolver_t resolver_type; /**< Sposób rozwiązywania nazw serwerów */ + int (*resolver_start)(int *fd, void **private_data, const char *hostname); /**< Funkcja rozpoczynająca rozwiązywanie nazwy */ + void (*resolver_cleanup)(void **private_data, int force); /**< Funkcja zwalniająca zasoby po rozwiązaniu nazwy */ +}; - void *resolver; /* wskanik na informacje resolvera */ - - unsigned int body_done; /* ile ju treci odebrano? */ -}; +/** \cond ignore */ #ifdef __GNUC__ #define GG_PACKED __attribute__ ((packed)) +#ifndef GG_IGNORE_DEPRECATED +#define GG_DEPRECATED __attribute__ ((deprecated)) +#else +#define GG_DEPRECATED +#endif #else #define GG_PACKED +#define GG_DEPRECATED #endif -#define GG_MAX_PATH 276 +/** \endcond */ + +#define GG_MAX_PATH 276 /**< Maksymalny rozmiar nazwy pliku w strukturze \c gg_file_info */ -/* - * struct gg_file_info +/** + * Odpowiednik struktury WIN32_FIND_DATA z API WIN32. * - * odpowiednik windowsowej struktury WIN32_FIND_DATA niezbdnej przy - * wysyaniu plikw. + * Wykorzystywana przy połączeniach bezpośrednich do wersji Gadu-Gadu 6.x. */ struct gg_file_info { - uint32_t mode; /* dwFileAttributes */ - uint32_t ctime[2]; /* ftCreationTime */ - uint32_t atime[2]; /* ftLastAccessTime */ - uint32_t mtime[2]; /* ftLastWriteTime */ - uint32_t size_hi; /* nFileSizeHigh */ - uint32_t size; /* nFileSizeLow */ - uint32_t reserved0; /* dwReserved0 */ - uint32_t reserved1; /* dwReserved1 */ - unsigned char filename[GG_MAX_PATH - 14]; /* cFileName */ - unsigned char short_filename[14]; /* cAlternateFileName */ -} GG_PACKED; + uint32_t mode; /**< dwFileAttributes */ + uint32_t ctime[2]; /**< ftCreationTime */ + uint32_t atime[2]; /**< ftLastAccessTime */ + uint32_t mtime[2]; /**< ftLastWriteTime */ + uint32_t size_hi; /**< nFileSizeHigh */ + uint32_t size; /**< nFileSizeLow */ + uint32_t reserved0; /**< dwReserved0 */ + uint32_t reserved1; /**< dwReserved1 */ + unsigned char filename[GG_MAX_PATH - 14]; /**< cFileName */ + unsigned char short_filename[14]; /**< cAlternateFileName */ +} /** \cond ignore */ GG_PACKED /** \endcond */; -/* - * struct gg_dcc +/** + * Połączenie bezpośrednie do wersji Gadu-Gadu 6.x. * - * struktura opisujca nasuchujce gniazdo pocze midzy klientami. - * tworzona przez gg_dcc_socket_create(), zwalniana przez gg_dcc_free(). + * Tworzone przez \c gg_dcc_socket_create(), \c gg_dcc_get_file(), + * \c gg_dcc_send_file() lub \c gg_dcc_voice_chat(), zwalniane przez + * \c gg_dcc_free(). + * + * \ingroup dcc6 */ struct gg_dcc { gg_common_head(struct gg_dcc) - struct gg_event *event; /* opis zdarzenia */ + struct gg_event *event; /**< Zdarzenie po wywołaniu \c callback */ - int active; /* czy to my si czymy? */ - int port; /* port, na ktrym siedzi */ - uin_t uin; /* uin klienta */ - uin_t peer_uin; /* uin drugiej strony */ - int file_fd; /* deskryptor pliku */ - unsigned int offset; /* offset w pliku */ - unsigned int chunk_size;/* rozmiar kawaka */ - unsigned int chunk_offset;/* offset w aktualnym kawaku */ + int active; /**< Flaga połączenia aktywnego (nieużywana) */ + int port; /**< Port gniazda nasłuchującego */ + uin_t uin; /**< Własny numer Gadu-Gadu */ + uin_t peer_uin; /**< Numer Gadu-Gadu drugiej strony połączenia */ + int file_fd; /**< deskryptor pliku */ + unsigned int offset; /**< Położenie w pliku */ + unsigned int chunk_size; + /**< Rozmiar kawałka pliku */ + unsigned int chunk_offset; + /**< Położenie w aktualnym kawałku pliku */ struct gg_file_info file_info; - /* informacje o pliku */ - int established; /* poczenie ustanowione */ - uint8_t *voice_buf; /* bufor na pakiet poczenia gosowego */ - int incoming; /* poczenie przychodzce */ - char *chunk_buf; /* bufor na kawaek danych */ - uint32_t remote_addr; /* adres drugiej strony */ - uint16_t remote_port; /* port drugiej strony */ + /**< Informacje o pliku */ + int established; /**< Flaga ustanowienia połączenia */ + char *voice_buf; /**< Bufor na pakiet połączenia głosowego */ + int incoming; /**< Flaga połączenia przychodzącego */ + char *chunk_buf; /**< Bufor na fragment danych */ + uint32_t remote_addr; /**< Adres drugiej strony */ + uint16_t remote_port; /**< Port drugiej strony */ }; -/* - * enum gg_session_t +#define GG_DCC7_HASH_LEN 20 /**< Maksymalny rozmiar skrótu pliku w połączeniach bezpośrenich */ +#define GG_DCC7_FILENAME_LEN 255 /**< Maksymalny rozmiar nazwy pliku w połączeniach bezpośrednich */ +#define GG_DCC7_INFO_LEN 64 /**< Maksymalny rozmiar informacji o połączeniach bezpośrednich */ + +/** + * Połączenie bezpośrednie od wersji Gadu-Gadu 7.x. * - * rodzaje sesji. + * \ingroup dcc7 + */ +struct gg_dcc7 { + gg_common_head(struct gg_dcc7) + + gg_dcc7_id_t cid; /**< Identyfikator połączenia */ + + struct gg_event *event; /**< Struktura zdarzenia */ + + uin_t uin; /**< Własny numer Gadu-Gadu */ + uin_t peer_uin; /**< Numer Gadu-Gadu drugiej strony połączenia */ + + int file_fd; /**< Deskryptor przesyłanego pliku */ + unsigned int offset; /**< Aktualne położenie w przesyłanym pliku */ + unsigned int size; /**< Rozmiar przesyłanego pliku */ + unsigned char filename[GG_DCC7_FILENAME_LEN + 1]; + /**< Nazwa przesyłanego pliku */ + unsigned char hash[GG_DCC7_HASH_LEN]; + /**< Skrót SHA1 przesyłanego pliku */ + + int dcc_type; /**< Rodzaj połączenia bezpośredniego */ + int established; /**< Flaga ustanowienia połączenia */ + int incoming; /**< Flaga połączenia przychodzącego */ + int reverse; /**< Flaga połączenia zwrotnego */ + + uint32_t local_addr; /**< Adres lokalny */ + uint16_t local_port; /**< Port lokalny */ + + uint32_t remote_addr; /**< Adres drugiej strony */ + uint16_t remote_port; /**< Port drugiej strony */ + + struct gg_session *sess; + /**< Sesja do której przypisano połączenie */ + struct gg_dcc7 *next; /**< Następne połączenie w liście */ + + int soft_timeout; /**< Flaga mówiąca, że po przekroczeniu \c timeout należy wywołać \c gg_dcc7_watch_fd() */ + int seek; /**< Flaga mówiąca, że można zmieniać położenie w wysyłanym pliku */ +}; + +/** + * Rodzaj sesji. */ enum gg_session_t { - GG_SESSION_GG = 1, /* poczenie z serwerem gg */ - GG_SESSION_HTTP, /* oglna sesja http */ - GG_SESSION_SEARCH, /* szukanie */ - GG_SESSION_REGISTER, /* rejestrowanie */ - GG_SESSION_REMIND, /* przypominanie hasa */ - GG_SESSION_PASSWD, /* zmiana hasa */ - GG_SESSION_CHANGE, /* zmiana informacji o sobie */ - GG_SESSION_DCC, /* oglne poczenie DCC */ - GG_SESSION_DCC_SOCKET, /* nasuchujcy socket */ - GG_SESSION_DCC_SEND, /* wysyanie pliku */ - GG_SESSION_DCC_GET, /* odbieranie pliku */ - GG_SESSION_DCC_VOICE, /* rozmowa gosowa */ - GG_SESSION_USERLIST_GET, /* pobieranie userlisty */ - GG_SESSION_USERLIST_PUT, /* wysyanie userlisty */ - GG_SESSION_UNREGISTER, /* usuwanie konta */ - GG_SESSION_USERLIST_REMOVE, /* usuwanie userlisty */ - GG_SESSION_TOKEN, /* pobieranie tokenu */ + GG_SESSION_GG = 1, /**< Połączenie z serwerem Gadu-Gadu */ + GG_SESSION_HTTP, /**< Połączenie HTTP */ + GG_SESSION_SEARCH, /**< Wyszukiwanie w katalogu publicznym (nieaktualne) */ + GG_SESSION_REGISTER, /**< Rejestracja nowego konta */ + GG_SESSION_REMIND, /**< Przypominanie hasła */ + GG_SESSION_PASSWD, /**< Zmiana hasła */ + GG_SESSION_CHANGE, /**< Zmiana informacji w katalogu publicznym (nieaktualne) */ + GG_SESSION_DCC, /**< Połączenie bezpośrednie (do wersji 6.x) */ + GG_SESSION_DCC_SOCKET, /**< Gniazdo nasłuchujące (do wersji 6.x) */ + GG_SESSION_DCC_SEND, /**< Wysyłanie pliku (do wersji 6.x) */ + GG_SESSION_DCC_GET, /**< Odbieranie pliku (do wersji 6.x) */ + GG_SESSION_DCC_VOICE, /**< Rozmowa głosowa (do wersji 6.x) */ + GG_SESSION_USERLIST_GET, /**< Import listy kontaktów z serwera (nieaktualne) */ + GG_SESSION_USERLIST_PUT, /**< Eksport listy kontaktów do serwera (nieaktualne) */ + GG_SESSION_UNREGISTER, /**< Usuwanie konta */ + GG_SESSION_USERLIST_REMOVE, /**< Usuwanie listy kontaktów z serwera (nieaktualne) */ + GG_SESSION_TOKEN, /**< Pobieranie tokenu */ + GG_SESSION_DCC7_SOCKET, /**< Gniazdo nasłuchujące (od wersji 7.x) */ + GG_SESSION_DCC7_SEND, /**< Wysyłanie pliku (od wersji 7.x) */ + GG_SESSION_DCC7_GET, /**< Odbieranie pliku (od wersji 7.x) */ + GG_SESSION_DCC7_VOICE, /**< Rozmowa głosowa (od wersji 7.x) */ - GG_SESSION_USER0 = 256, /* zdefiniowana dla uytkownika */ - GG_SESSION_USER1, /* j.w. */ - GG_SESSION_USER2, /* j.w. */ - GG_SESSION_USER3, /* j.w. */ - GG_SESSION_USER4, /* j.w. */ - GG_SESSION_USER5, /* j.w. */ - GG_SESSION_USER6, /* j.w. */ - GG_SESSION_USER7 /* j.w. */ + GG_SESSION_USER0 = 256, /**< Rodzaj zadeklarowany dla użytkownika */ + GG_SESSION_USER1, /**< Rodzaj zadeklarowany dla użytkownika */ + GG_SESSION_USER2, /**< Rodzaj zadeklarowany dla użytkownika */ + GG_SESSION_USER3, /**< Rodzaj zadeklarowany dla użytkownika */ + GG_SESSION_USER4, /**< Rodzaj zadeklarowany dla użytkownika */ + GG_SESSION_USER5, /**< Rodzaj zadeklarowany dla użytkownika */ + GG_SESSION_USER6, /**< Rodzaj zadeklarowany dla użytkownika */ + GG_SESSION_USER7 /**< Rodzaj zadeklarowany dla użytkownika */ }; -/* - * enum gg_state_t - * - * opisuje stan asynchronicznej maszyny. +/** + * Aktualny stan sesji. */ enum gg_state_t { - /* wsplne */ - GG_STATE_IDLE = 0, /* nie powinno wystpi. */ - GG_STATE_RESOLVING, /* wywoa gethostbyname() */ - GG_STATE_CONNECTING, /* wywoa connect() */ - GG_STATE_READING_DATA, /* czeka na dane http */ - GG_STATE_ERROR, /* wystpi bd. kod w x->error */ + /* wspólne */ + GG_STATE_IDLE = 0, /**< Nie dzieje się nic */ + GG_STATE_RESOLVING, /**< Oczekiwanie na rozwiązanie nazwy serwera */ + GG_STATE_CONNECTING, /**< Oczekiwanie na połączenie */ + GG_STATE_READING_DATA, /**< Oczekiwanie na dane */ + GG_STATE_ERROR, /**< Kod błędu w polu \c error */ - /* gg_session */ - GG_STATE_CONNECTING_HUB, /* wywoa connect() na huba */ - GG_STATE_CONNECTING_GG, /* wywoa connect() na serwer */ - GG_STATE_READING_KEY, /* czeka na klucz */ - GG_STATE_READING_REPLY, /* czeka na odpowied */ - GG_STATE_CONNECTED, /* poczy si */ + /* gg_session */ + GG_STATE_CONNECTING_HUB, /**< Oczekiwanie na połączenie z hubem */ + GG_STATE_CONNECTING_GG, /**< Oczekiwanie na połączenie z serwerem */ + GG_STATE_READING_KEY, /**< Oczekiwanie na klucz */ + GG_STATE_READING_REPLY, /**< Oczekiwanie na odpowiedź serwera */ + GG_STATE_CONNECTED, /**< Połączono z serwerem */ - /* gg_http */ - GG_STATE_SENDING_QUERY, /* wysya zapytanie http */ - GG_STATE_READING_HEADER, /* czeka na nagwek http */ - GG_STATE_PARSING, /* przetwarza dane */ - GG_STATE_DONE, /* skoczy */ + /* gg_http */ + GG_STATE_SENDING_QUERY, /**< Wysłano zapytanie HTTP */ + GG_STATE_READING_HEADER, /**< Oczekiwanie na nagłówek HTTP */ + GG_STATE_PARSING, /**< Przetwarzanie danych */ + GG_STATE_DONE, /**< Połączenie zakończone */ - /* gg_dcc */ - GG_STATE_LISTENING, /* czeka na poczenia */ + /* gg_dcc */ + GG_STATE_LISTENING, /* czeka na połączenia */ GG_STATE_READING_UIN_1, /* czeka na uin peera */ - GG_STATE_READING_UIN_2, /* czeka na swj uin */ - GG_STATE_SENDING_ACK, /* wysya potwierdzenie dcc */ + GG_STATE_READING_UIN_2, /* czeka na swój uin */ + GG_STATE_SENDING_ACK, /* wysyła potwierdzenie dcc */ GG_STATE_READING_ACK, /* czeka na potwierdzenie dcc */ - GG_STATE_READING_REQUEST, /* czeka na komend */ - GG_STATE_SENDING_REQUEST, /* wysya komend */ - GG_STATE_SENDING_FILE_INFO, /* wysya informacje o pliku */ + GG_STATE_READING_REQUEST, /* czeka na komendę */ + GG_STATE_SENDING_REQUEST, /* wysyła komendę */ + GG_STATE_SENDING_FILE_INFO, /* wysyła informacje o pliku */ GG_STATE_READING_PRE_FILE_INFO, /* czeka na pakiet przed file_info */ GG_STATE_READING_FILE_INFO, /* czeka na informacje o pliku */ - GG_STATE_SENDING_FILE_ACK, /* wysya potwierdzenie pliku */ + GG_STATE_SENDING_FILE_ACK, /* wysyła potwierdzenie pliku */ GG_STATE_READING_FILE_ACK, /* czeka na potwierdzenie pliku */ - GG_STATE_SENDING_FILE_HEADER, /* wysya nagwek pliku */ - GG_STATE_READING_FILE_HEADER, /* czeka na nagwek */ + GG_STATE_SENDING_FILE_HEADER, /* wysyła nagłówek pliku */ + GG_STATE_READING_FILE_HEADER, /* czeka na nagłówek */ GG_STATE_GETTING_FILE, /* odbiera plik */ - GG_STATE_SENDING_FILE, /* wysya plik */ + GG_STATE_SENDING_FILE, /* wysyła plik */ GG_STATE_READING_VOICE_ACK, /* czeka na potwierdzenie voip */ GG_STATE_READING_VOICE_HEADER, /* czeka na rodzaj bloku voip */ GG_STATE_READING_VOICE_SIZE, /* czeka na rozmiar bloku voip */ GG_STATE_READING_VOICE_DATA, /* czeka na dane voip */ - GG_STATE_SENDING_VOICE_ACK, /* wysya potwierdzenie voip */ - GG_STATE_SENDING_VOICE_REQUEST, /* wysya danie voip */ - GG_STATE_READING_TYPE, /* czeka na typ poczenia */ + GG_STATE_SENDING_VOICE_ACK, /* wysyła potwierdzenie voip */ + GG_STATE_SENDING_VOICE_REQUEST, /* wysyła żądanie voip */ + GG_STATE_READING_TYPE, /* czeka na typ połączenia */ /* nowe. bez sensu jest to API. */ - GG_STATE_TLS_NEGOTIATION /* negocjuje poczenie TLS */ + GG_STATE_TLS_NEGOTIATION, /**< Negocjacja połączenia szyfrowanego */ + + GG_STATE_REQUESTING_ID, /**< Oczekiwanie na nadanie identyfikatora połączenia bezpośredniego */ + GG_STATE_WAITING_FOR_ACCEPT, /**< Oczekiwanie na potwierdzenie lub odrzucenie połączenia bezpośredniego */ + GG_STATE_WAITING_FOR_INFO, /**< Oczekiwanie na informacje o połączeniu bezpośrednim */ + + GG_STATE_READING_ID, /**< Odebranie identyfikatora połączenia bezpośredniego */ + GG_STATE_SENDING_ID /**< Wysłano identyfikatora połączenia bezpośredniego */ }; -/* - * enum gg_check_t +/** + * Informacja o tym, czy biblioteka chce zapisywać i/lub czytać + * z deskryptora. Maska bitowa. * - * informuje, co proces klienta powinien sprawdzi na deskryptorze danego - * poczenia. + * \ingroup events */ enum gg_check_t { - GG_CHECK_NONE = 0, /* nic. nie powinno wystpi */ - GG_CHECK_WRITE = 1, /* sprawdzamy moliwo zapisu */ - GG_CHECK_READ = 2 /* sprawdzamy moliwo odczytu */ + GG_CHECK_NONE = 0, /**< Nie sprawdzaj niczego */ + GG_CHECK_WRITE = 1, /**< Sprawdź możliwość zapisu */ + GG_CHECK_READ = 2 /**< Sprawdź możliwość odczytu */ }; -/* - * struct gg_login_params +/** + * Parametry połączenia z serwerem Gadu-Gadu. Parametry zostały przeniesione + * do struktury, by uniknąć zmian API po rozszerzeniu protokołu i dodaniu + * kolejnych opcji połączenia. Część parametrów, które nie są już aktualne + * lub nie mają znaczenia, została usunięta z dokumentacji. * - * parametry gg_login(). przeniesiono do struktury, eby unikn problemw - * z cigymi zmianami API, gdy dodano co nowego do protokou. + * \ingroup login */ struct gg_login_params { - uin_t uin; /* numerek */ - char *password; /* haso */ - int async; /* asynchroniczne sockety? */ - int status; /* pocztkowy status klienta */ - char *status_descr; /* opis statusu */ - uint32_t server_addr; /* adres serwera gg */ - uint16_t server_port; /* port serwera gg */ - uint32_t client_addr; /* adres dcc klienta */ - uint16_t client_port; /* port dcc klienta */ - int protocol_version; /* wersja protokou */ - char *client_version; /* wersja klienta */ - int has_audio; /* czy ma dwik? */ - int last_sysmsg; /* ostatnia wiadomo systemowa */ - uint32_t external_addr; /* adres widziany na zewnatrz */ - uint16_t external_port; /* port widziany na zewnatrz */ - int tls; /* czy czymy po TLS? */ - int image_size; /* maksymalny rozmiar obrazka w KiB */ - int era_omnix; /* czy udawa klienta era omnix? */ + uin_t uin; /**< Numer Gadu-Gadu */ + char *password; /**< Hasło */ + int async; /**< Flaga asynchronicznego połączenia (domyślnie nie) */ + int status; /**< Początkowy status użytkownika (domyślnie \c GG_STATUS_AVAIL) */ + char *status_descr; /**< Początkowy opis użytkownika (domyślnie brak) */ + uint32_t server_addr; /**< Adres serwera Gadu-Gadu (domyślnie pobierany automatycznie) */ + uint16_t server_port; /**< Port serwera Gadu-Gadu (domyślnie pobierany automatycznie) */ +#ifndef DOXYGEN + uint32_t client_addr; /**< Adres połączeń bezpośrednich (nieaktualne) */ + uint16_t client_port; /**< Port połączeń bezpośrednich (nieaktualne) */ +#endif + int protocol_version; /**< Wersja protokołu wysyłana do serwera (domyślnie najnowsza obsługiwana) */ + char *client_version; /**< Wersja klienta wysyłana do serwera (domyślnie najnowsza znana) */ + int has_audio; /**< Flaga obsługi połączeń głosowych */ + int last_sysmsg; /**< Numer ostatnio odebranej wiadomości systemowej */ + uint32_t external_addr; /**< Adres publiczny dla połączeń bezpośrednich (6.x) */ + uint16_t external_port; /**< Port publiczny dla połączeń bezpośrednich (6.x) */ +#ifndef DOXYGEN + int tls; /**< Flaga połączenia szyfrowanego (nieaktualna) */ +#endif + int image_size; /**< Maksymalny rozmiar obsługiwanych obrazków w kilobajtach */ +#ifndef DOXYGEN + int era_omnix; /**< Flaga udawania klienta Era Omnix (nieaktualna) */ +#endif + int hash_type; /**< Rodzaj skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1, domyślnie SHA1) */ + gg_encoding_t encoding; /**< Rodzaj kodowania używanego w sesji (domyślnie CP1250) */ + gg_resolver_t resolver; /**< Sposób rozwiązywania nazw (patrz \ref build-resolver) */ + int protocol_features; /**< Opcje protokołu (flagi GG_FEATURE_*). */ - char dummy[6 * sizeof(int)]; /* miejsce na kolejnych 6 zmiennych, - * eby z dodaniem parametru nie - * zmienia si rozmiar struktury */ +#ifndef DOXYGEN + char dummy[2 * sizeof(int)]; /**< \internal Miejsce na kilka kolejnych + parametrów, żeby wraz z dodawaniem kolejnych + parametrów nie zmieniał się rozmiar struktury */ +#endif + }; struct gg_session *gg_login(const struct gg_login_params *p); @@ -367,232 +598,337 @@ int gg_ping(struct gg_session *sess); int gg_userlist_request(struct gg_session *sess, char type, const char *request); int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32); -int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const unsigned char *image, int size); +int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size); uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len); -struct gg_image_queue { - uin_t sender; /* nadawca obrazka */ - uint32_t size; /* rozmiar */ - uint32_t crc32; /* suma kontrolna */ - char *filename; /* nazwa pliku */ - char *image; /* bufor z obrazem */ - uint32_t done; /* ile ju wczytano */ +int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type); +gg_resolver_t gg_session_get_resolver(struct gg_session *gs); +int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int)); + +int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type); +gg_resolver_t gg_http_get_resolver(struct gg_http *gh); +int gg_http_set_custom_resolver(struct gg_http *gh, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int)); - struct gg_image_queue *next; /* nastpny na licie */ -}; +int gg_global_set_resolver(gg_resolver_t type); +gg_resolver_t gg_global_get_resolver(void); +int gg_global_set_custom_resolver(int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int)); -/* - * enum gg_event_t +/** + * Rodzaj zdarzenia. * - * rodzaje zdarze. + * \ingroup events */ enum gg_event_t { - GG_EVENT_NONE = 0, /* nic si nie wydarzyo */ - GG_EVENT_MSG, /* otrzymano wiadomo */ - GG_EVENT_NOTIFY, /* kto si pojawi */ - GG_EVENT_NOTIFY_DESCR, /* kto si pojawi z opisem */ - GG_EVENT_STATUS, /* kto zmieni stan */ - GG_EVENT_ACK, /* potwierdzenie wysania wiadomoci */ - GG_EVENT_PONG, /* pakiet pong */ - GG_EVENT_CONN_FAILED, /* poczenie si nie udao */ - GG_EVENT_CONN_SUCCESS, /* poczenie si powiodo */ - GG_EVENT_DISCONNECT, /* serwer zrywa poczenie */ + GG_EVENT_NONE = 0, /**< Nie wydarzyło się nic wartego uwagi */ + GG_EVENT_MSG, /**< \brief Otrzymano wiadomość. Przekazuje również wiadomości systemowe od numeru 0. */ + GG_EVENT_NOTIFY, /**< \brief Informacja o statusach osób z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */ + GG_EVENT_NOTIFY_DESCR, /**< \brief Informacja o statusie opisowym osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */ + GG_EVENT_STATUS, /**< \brief Zmiana statusu osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */ + GG_EVENT_ACK, /**< Potwierdzenie doręczenia wiadomości */ + GG_EVENT_PONG, /**< \brief Utrzymanie połączenia. Obecnie serwer nie wysyła już do klienta ramek utrzymania połączenia, polega wyłącznie na wysyłaniu ramek przez klienta. */ + GG_EVENT_CONN_FAILED, /**< \brief Nie udało się połączyć */ + GG_EVENT_CONN_SUCCESS, /**< \brief Połączono z serwerem. Pierwszą rzeczą, jaką należy zrobić jest wysłanie listy kontaktów. */ + GG_EVENT_DISCONNECT, /**< \brief Serwer zrywa połączenie. Zdarza się, gdy równolegle do serwera podłączy się druga sesja i trzeba zerwać połączenie z pierwszą. */ + + GG_EVENT_DCC_NEW, /**< Nowe połączenie bezpośrednie (6.x) */ + GG_EVENT_DCC_ERROR, /**< Błąd połączenia bezpośredniego (6.x) */ + GG_EVENT_DCC_DONE, /**< Zakończono połączenie bezpośrednie (6.x) */ + GG_EVENT_DCC_CLIENT_ACCEPT, /**< Moment akceptacji klienta w połączeniu bezpośrednim (6.x) */ + GG_EVENT_DCC_CALLBACK, /**< Zwrotne połączenie bezpośrednie (6.x) */ + GG_EVENT_DCC_NEED_FILE_INFO, /**< Należy wypełnić \c file_info dla połączenia bezpośredniego (6.x) */ + GG_EVENT_DCC_NEED_FILE_ACK, /**< Czeka na potwierdzenie pliku w połączeniu bezpośrednim (6.x) */ + GG_EVENT_DCC_NEED_VOICE_ACK, /**< Czeka na potwierdzenie rozmowy w połączeniu bezpośrednim (6.x) */ + GG_EVENT_DCC_VOICE_DATA, /**< Dane bezpośredniego połączenia głosowego (6.x) */ - GG_EVENT_DCC_NEW, /* nowe poczenie midzy klientami */ - GG_EVENT_DCC_ERROR, /* bd poczenia midzy klientami */ - GG_EVENT_DCC_DONE, /* zakoczono poczenie */ - GG_EVENT_DCC_CLIENT_ACCEPT, /* moment akceptacji klienta */ - GG_EVENT_DCC_CALLBACK, /* klient si poczy na danie */ - GG_EVENT_DCC_NEED_FILE_INFO, /* naley wypeni file_info */ - GG_EVENT_DCC_NEED_FILE_ACK, /* czeka na potwierdzenie pliku */ - GG_EVENT_DCC_NEED_VOICE_ACK, /* czeka na potwierdzenie rozmowy */ - GG_EVENT_DCC_VOICE_DATA, /* ramka danych rozmowy gosowej */ + GG_EVENT_PUBDIR50_SEARCH_REPLY, /**< Odpowiedź katalogu publicznego */ + GG_EVENT_PUBDIR50_READ, /**< Odczytano własne dane z katalogu publicznego */ + GG_EVENT_PUBDIR50_WRITE, /**< Zmieniono własne dane w katalogu publicznym */ + + GG_EVENT_STATUS60, /**< Zmiana statusu osoby z listy kontaktów */ + GG_EVENT_NOTIFY60, /**< Informacja o statusach osób z listy kontaktów */ + GG_EVENT_USERLIST, /**< Wynik importu lub eksportu listy kontaktów */ + GG_EVENT_IMAGE_REQUEST, /**< Żądanie przesłania obrazka z wiadommości */ + GG_EVENT_IMAGE_REPLY, /**< Przysłano obrazek z wiadomości */ + GG_EVENT_DCC_ACK, /**< Potwierdzenie transmisji w połączeniu bezpośrednim (6.x) */ - GG_EVENT_PUBDIR50_SEARCH_REPLY, /* odpowiedz wyszukiwania */ - GG_EVENT_PUBDIR50_READ, /* odczytano wasne dane z katalogu */ - GG_EVENT_PUBDIR50_WRITE, /* wpisano wasne dane do katalogu */ + GG_EVENT_DCC7_NEW, /**< Nowe połączenie bezpośrednie (7.x) */ + GG_EVENT_DCC7_ACCEPT, /**< Zaakceptowano połączenie bezpośrednie (7.x), nowy deskryptor */ + GG_EVENT_DCC7_REJECT, /**< Odrzucono połączenie bezpośrednie (7.x) */ + GG_EVENT_DCC7_CONNECTED, /**< Zestawiono połączenie bezpośrednie (7.x), nowy deskryptor */ + GG_EVENT_DCC7_ERROR, /**< Błąd połączenia bezpośredniego (7.x) */ + GG_EVENT_DCC7_DONE, /**< Zakończono połączenie bezpośrednie (7.x) */ + GG_EVENT_DCC7_PENDING, /**< Trwa próba połączenia bezpośredniego (7.x), nowy deskryptor */ - GG_EVENT_STATUS60, /* kto zmieni stan w GG 6.0 */ - GG_EVENT_NOTIFY60, /* kto si pojawi w GG 6.0 */ - GG_EVENT_USERLIST, /* odpowied listy kontaktw w GG 6.0 */ - GG_EVENT_IMAGE_REQUEST, /* proba o wysanie obrazka GG 6.0 */ - GG_EVENT_IMAGE_REPLY, /* podesany obrazek GG 6.0 */ - GG_EVENT_DCC_ACK /* potwierdzenie transmisji */ + GG_EVENT_XML_EVENT, /**< Otrzymano komunikat systemowy (7.7) */ + GG_EVENT_DISCONNECT_ACK, /**< \brief Potwierdzenie zakończenia sesji. Informuje o tym, że zmiana stanu na niedostępny z opisem dotarła do serwera i można zakończyć połączenie TCP. */ }; #define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY -/* - * enum gg_failure_t - * - * okrela powd nieudanego poczenia. +/** + * Powód nieudanego połączenia. */ enum gg_failure_t { - GG_FAILURE_RESOLVING = 1, /* nie znaleziono serwera */ - GG_FAILURE_CONNECTING, /* nie mona si poczy */ - GG_FAILURE_INVALID, /* serwer zwrci nieprawidowe dane */ - GG_FAILURE_READING, /* zerwano poczenie podczas odczytu */ - GG_FAILURE_WRITING, /* zerwano poczenie podczas zapisu */ - GG_FAILURE_PASSWORD, /* nieprawidowe haso */ - GG_FAILURE_404, /* XXX nieuywane */ - GG_FAILURE_TLS, /* bd negocjacji TLS */ - GG_FAILURE_NEED_EMAIL /* serwer rozczy nas z prob o zmian emaila */ + GG_FAILURE_RESOLVING = 1, /**< Nie znaleziono serwera */ + GG_FAILURE_CONNECTING, /**< Błąd połączenia */ + GG_FAILURE_INVALID, /**< Serwer zwrócił nieprawidłowe dane */ + GG_FAILURE_READING, /**< Zerwano połączenie podczas odczytu */ + GG_FAILURE_WRITING, /**< Zerwano połączenie podczas zapisu */ + GG_FAILURE_PASSWORD, /**< Nieprawidłowe hasło */ + GG_FAILURE_404, /**< Nieużywane */ + GG_FAILURE_TLS, /**< Błąd negocjacji szyfrowanego połączenia */ + GG_FAILURE_NEED_EMAIL, /**< Serwer rozłączył nas z prośbą o zmianę adresu e-mail */ + GG_FAILURE_INTRUDER, /**< Zbyt wiele prób połączenia z nieprawidłowym hasłem */ + GG_FAILURE_UNAVAILABLE /**< Serwery są wyłączone */ }; -/* - * enum gg_error_t +/** + * Kod błędu danej operacji. * - * okrela rodzaj bdu wywoanego przez dan operacj. nie zawiera - * przesadnie szczegowych informacji o powodzie bdu, by nie komplikowa - * obsugi bdw. jeli wymagana jest wiksza dokadno, naley sprawdzi - * zawarto zmiennej errno. + * Nie zawiera przesadnie szczegółowych informacji o powodach błędów, by nie + * komplikować ich obsługi. Jeśli wymagana jest większa dokładność, należy + * sprawdzić zawartość zmiennej systemowej \c errno. */ enum gg_error_t { - GG_ERROR_RESOLVING = 1, /* bd znajdowania hosta */ - GG_ERROR_CONNECTING, /* bd aczenia si */ - GG_ERROR_READING, /* bd odczytu */ - GG_ERROR_WRITING, /* bd wysyania */ + GG_ERROR_RESOLVING = 1, /**< Nie znaleziono hosta */ + GG_ERROR_CONNECTING, /**< Błąd połączenia */ + GG_ERROR_READING, /**< Błąd odczytu/odbierania */ + GG_ERROR_WRITING, /**< Błąd zapisu/wysyłania */ - GG_ERROR_DCC_HANDSHAKE, /* bd negocjacji */ - GG_ERROR_DCC_FILE, /* bd odczytu/zapisu pliku */ - GG_ERROR_DCC_EOF, /* plik si skoczy? */ - GG_ERROR_DCC_NET, /* bd wysyania/odbierania */ - GG_ERROR_DCC_REFUSED /* poczenie odrzucone przez usera */ + GG_ERROR_DCC_HANDSHAKE, /**< Błąd negocjacji */ + GG_ERROR_DCC_FILE, /**< Błąd odczytu/zapisu pliku */ + GG_ERROR_DCC_EOF, /**< Przedwczesny koniec pliku */ + GG_ERROR_DCC_NET, /**< Błąd wysyłania/odbierania */ + GG_ERROR_DCC_REFUSED, /**< Połączenie odrzucone */ + + GG_ERROR_DCC7_HANDSHAKE, /**< Błąd negocjacji */ + GG_ERROR_DCC7_FILE, /**< Błąd odczytu/zapisu pliku */ + GG_ERROR_DCC7_EOF, /**< Przedwczesny koniec pliku */ + GG_ERROR_DCC7_NET, /**< Błąd wysyłania/odbierania */ + GG_ERROR_DCC7_REFUSED /**< Połączenie odrzucone */ }; -/* - * struktury dotyczce wyszukiwania w GG 5.0. NIE NALEY SI DO NICH - * ODWOYWA BEZPOREDNIO! do dostpu do nich su funkcje gg_pubdir50_*() +/** + * Pole zapytania lub odpowiedzi katalogu publicznego. */ struct gg_pubdir50_entry { - int num; - char *field; - char *value; -}; + int num; /**< Numer wyniku */ + char *field; /**< Nazwa pola */ + char *value; /**< Wartość pola */ +} /* GG_DEPRECATED */; +/** + * Zapytanie lub odpowiedź katalogu publicznego. + * + * Patrz \c gg_pubdir50_t. + */ struct gg_pubdir50_s { - int count; - uin_t next; - int type; - uint32_t seq; - struct gg_pubdir50_entry *entries; - int entries_count; -}; + int count; /**< Liczba wyników odpowiedzi */ + uin_t next; /**< Numer początkowy następnego zapytania */ + int type; /**< Rodzaj zapytania */ + uint32_t seq; /**< Numer sekwencyjny */ + struct gg_pubdir50_entry *entries; /**< Pola zapytania lub odpowiedzi */ + int entries_count; /**< Liczba pól */ +} /* GG_DEPRECATED */; -/* - * typedef gg_pubdir_50_t +/** + * Zapytanie lub odpowiedź katalogu publicznego. * - * typ opisujcy zapytanie lub wynik zapytania katalogu publicznego - * z protokou GG 5.0. nie naley si odwoywa bezporednio do jego - * pl -- su do tego funkcje gg_pubdir50_*() + * Do pól nie należy się odwoływać bezpośrednio -- wszystkie niezbędne + * informacje są dostępne za pomocą funkcji \c gg_pubdir50_* */ typedef struct gg_pubdir50_s *gg_pubdir50_t; -/* - * struct gg_event +/** + * Opis zdarzenia \c GG_EVENT_MSG. + */ +struct gg_event_msg { + uin_t sender; /**< Numer nadawcy */ + int msgclass; /**< Klasa wiadomości */ + time_t time; /**< Czas nadania */ + unsigned char *message; /**< Treść wiadomości */ + + int recipients_count; /**< Liczba odbiorców konferencji */ + uin_t *recipients; /**< Odbiorcy konferencji */ + + int formats_length; /**< Długość informacji o formatowaniu tekstu */ + void *formats; /**< Informacje o formatowaniu tekstu */ + uint32_t seq; /**< Numer sekwencyjny wiadomości */ + + char *xhtml_message; /**< Treść wiadomości w formacie XHTML (może być równe \c NULL, jeśli wiadomość nie zawiera treści XHTML) */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_NOTIFY_DESCR. + */ +struct gg_event_notify_descr { + struct gg_notify_reply *notify; /**< Informacje o liście kontaktów */ + char *descr; /**< Opis status */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_STATUS. + */ +struct gg_event_status { + uin_t uin; /**< Numer Gadu-Gadu */ + uint32_t status; /**< Nowy status */ + char *descr; /**< Opis */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_STATUS60. + */ +struct gg_event_status60 { + uin_t uin; /**< Numer Gadu-Gadu */ + int status; /**< Nowy status */ + uint32_t remote_ip; /**< Adres IP dla połączeń bezpośrednich */ + uint16_t remote_port; /**< Port dla połączeń bezpośrednich */ + int version; /**< Wersja protokołu */ + int image_size; /**< Maksymalny rozmiar obsługiwanych obrazków w KiB */ + char *descr; /**< Opis statusu */ + time_t time; /**< Czas powrotu */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_NOTIFY_REPLY60. + */ +struct gg_event_notify60 { + uin_t uin; /**< Numer Gadu-Gadu */ + int status; /**< Nowy status */ + uint32_t remote_ip; /**< Adres IP dla połączeń bezpośrednich */ + uint16_t remote_port; /**< Port dla połączeń bezpośrednich */ + int version; /**< Wersja protokołu */ + int image_size; /**< Maksymalny rozmiar obsługiwanych obrazków w KiB */ + char *descr; /**< Opis statusu */ + time_t time; /**< Czas powrotu */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_ACK. + */ +struct gg_event_ack { + uin_t recipient; /**< Numer odbiorcy */ + int status; /**< Status doręczenia */ + int seq; /**< Numer sekwencyjny wiadomości */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_USERLIST. + */ +struct gg_event_userlist { + char type; /**< Rodzaj odpowiedzi */ + char *reply; /**< Treść odpowiedzi */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_DCC_VOICE_DATA. + */ +struct gg_event_dcc_voice_data { + uint8_t *data; /**< Dane dźwiękowe */ + int length; /**< Rozmiar danych dźwiękowych */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_IMAGE_REQUEST. + */ +struct gg_event_image_request { + uin_t sender; /**< Nadawca żądania */ + uint32_t size; /**< Rozmiar obrazka */ + uint32_t crc32; /**< Suma kontrolna CRC32 */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_IMAGE_REPLY. + */ +struct gg_event_image_reply { + uin_t sender; /**< Nadawca obrazka */ + uint32_t size; /**< Rozmiar obrazka */ + uint32_t crc32; /**< Suma kontrolna CRC32 */ + char *filename; /**< Nazwa pliku */ + char *image; /**< Bufor z obrazkiem */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_XML_EVENT. + */ +struct gg_event_xml_event { + char *data; /**< Bufor z komunikatem */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_DCC7_CONNECTED. + */ +struct gg_event_dcc7_connected { + struct gg_dcc7 *dcc7; /**< Struktura połączenia */ + // XXX czy coś się przyda? +}; + +/** + * Opis zdarzenia \c GG_EVENT_DCC7_REJECT. + */ +struct gg_event_dcc7_reject { + struct gg_dcc7 *dcc7; /**< Struktura połączenia */ + int reason; /**< powód odrzucenia */ +}; + +/** + * Opis zdarzenia \c GG_EVENT_DCC7_ACCEPT. + */ +struct gg_event_dcc7_accept { + struct gg_dcc7 *dcc7; /**< Struktura połączenia */ + int type; /**< Sposób połączenia (P2P, przez serwer) */ + uint32_t remote_ip; /**< Adres zdalnego klienta */ + uint16_t remote_port; /**< Port zdalnego klienta */ +}; + +/** + * Unia wszystkich zdarzeń zwracanych przez funkcje \c gg_watch_fd(), + * \c gg_dcc_watch_fd() i \c gg_dcc7_watch_fd(). * - * struktura opisujca rodzaj zdarzenia. wychodzi z gg_watch_fd() lub - * z gg_dcc_watch_fd() + * \ingroup events + */ +union gg_event_union { + enum gg_failure_t failure; /**< Błąd połączenia (\c GG_EVENT_CONN_FAILED) */ + struct gg_notify_reply *notify; /**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY) */ + struct gg_event_notify_descr notify_descr; /**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY_DESCR) */ + struct gg_event_status status; /**< Zmiana statusu kontaktów (\c GG_EVENT_STATUS) */ + struct gg_event_status60 status60; /**< Zmiana statusu kontaktów (\c GG_EVENT_STATUS60) */ + struct gg_event_notify60 *notify60; /**< Zmiana statusu kontaktów (\c GG_EVENT_NOTIFY60) */ + struct gg_event_msg msg; /**< Otrzymano wiadomość (\c GG_EVENT_MSG) */ + struct gg_event_ack ack; /**< Potwierdzenie wiadomości (\c GG_EVENT_ACK) */ + struct gg_event_image_request image_request; /**< Żądanie wysłania obrazka (\c GG_EVENT_IMAGE_REQUEST) */ + struct gg_event_image_reply image_reply; /**< Odpowiedź z obrazkiem (\c GG_EVENT_IMAGE_REPLY) */ + struct gg_event_userlist userlist; /**< Odpowiedź listy kontaktów (\c GG_EVENT_USERLIST) */ + gg_pubdir50_t pubdir50; /**< Odpowiedź katalogu publicznego (\c GG_EVENT_PUBDIR50_*) */ + struct gg_event_xml_event xml_event; /**< Zdarzenie systemowe (\c GG_EVENT_XML_EVENT) */ + struct gg_dcc *dcc_new; /**< Nowe połączenie bezpośrednie (\c GG_EVENT_DCC_NEW) */ + enum gg_error_t dcc_error; /**< Błąd połączenia bezpośredniego (\c GG_EVENT_DCC_ERROR) */ + struct gg_event_dcc_voice_data dcc_voice_data; /**< Dane połączenia głosowego (\c GG_EVENT_DCC_VOICE_DATA) */ + struct gg_dcc7 *dcc7_new; /**< Nowe połączenie bezpośrednie (\c GG_EVENT_DCC7_NEW) */ + enum gg_error_t dcc7_error; /**< Błąd połączenia bezpośredniego (\c GG_EVENT_DCC7_ERROR) */ + struct gg_event_dcc7_connected dcc7_connected; /**< Informacja o zestawieniu połączenia bezpośredniego (\c GG_EVENT_DCC7_CONNECTED) */ + struct gg_event_dcc7_reject dcc7_reject; /**< Odrzucono połączenia bezpośredniego (\c GG_EVENT_DCC7_REJECT) */ + struct gg_event_dcc7_accept dcc7_accept; /**< Zaakceptowano połączenie bezpośrednie (\c GG_EVENT_DCC7_ACCEPT) */ +}; + +/** + * Opis zdarzenia. + * + * Zwracany przez funkcje \c gg_watch_fd(), \c gg_dcc_watch_fd() + * i \c gg_dcc7_watch_fd(). Po przeanalizowaniu należy zwolnić + * za pomocą \c gg_event_free(). + * + * \ingroup events */ struct gg_event { - int type; /* rodzaj zdarzenia -- gg_event_t */ - union { /* @event */ - struct gg_notify_reply *notify; /* informacje o licie kontaktw -- GG_EVENT_NOTIFY */ - - enum gg_failure_t failure; /* bd poczenia -- GG_EVENT_FAILURE */ - - struct gg_dcc *dcc_new; /* nowe poczenie bezporednie -- GG_EVENT_DCC_NEW */ - - int dcc_error; /* bd poczenia bezporedniego -- GG_EVENT_DCC_ERROR */ - - gg_pubdir50_t pubdir50; /* wynik operacji zwizanej z katalogiem publicznym -- GG_EVENT_PUBDIR50_* */ - - struct { /* @msg odebrano wiadomo -- GG_EVENT_MSG */ - uin_t sender; /* numer nadawcy */ - int msgclass; /* klasa wiadomoci */ - time_t time; /* czas nadania */ - unsigned char *message; /* tre wiadomoci */ - - int recipients_count; /* ilo odbiorcw konferencji */ - uin_t *recipients; /* odbiorcy konferencji */ - - int formats_length; /* dugo informacji o formatowaniu tekstu */ - void *formats; /* informacje o formatowaniu tekstu */ - } msg; - - struct { /* @notify_descr informacje o licie kontaktw z opisami stanu -- GG_EVENT_NOTIFY_DESCR */ - struct gg_notify_reply *notify; /* informacje o licie kontaktw */ - char *descr; /* opis stanu */ - } notify_descr; - - struct { /* @status zmiana stanu -- GG_EVENT_STATUS */ - uin_t uin; /* numer */ - uint32_t status; /* nowy stan */ - char *descr; /* opis stanu */ - } status; - - struct { /* @status60 zmiana stanu -- GG_EVENT_STATUS60 */ - uin_t uin; /* numer */ - int status; /* nowy stan */ - uint32_t remote_ip; /* adres ip */ - uint16_t remote_port; /* port */ - int version; /* wersja klienta */ - int image_size; /* maksymalny rozmiar grafiki w KiB */ - char *descr; /* opis stanu */ - time_t time; /* czas powrotu */ - } status60; - - struct { /* @notify60 informacja o licie kontaktw -- GG_EVENT_NOTIFY60 */ - uin_t uin; /* numer */ - int status; /* stan */ - uint32_t remote_ip; /* adres ip */ - uint16_t remote_port; /* port */ - int version; /* wersja klienta */ - int image_size; /* maksymalny rozmiar grafiki w KiB */ - char *descr; /* opis stanu */ - time_t time; /* czas powrotu */ - } *notify60; - - struct { /* @ack potwierdzenie wiadomoci -- GG_EVENT_ACK */ - uin_t recipient; /* numer odbiorcy */ - int status; /* stan dorczenia wiadomoci */ - int seq; /* numer sekwencyjny wiadomoci */ - } ack; - - struct { /* @dcc_voice_data otrzymano dane dwikowe -- GG_EVENT_DCC_VOICE_DATA */ - uint8_t *data; /* dane dwikowe */ - int length; /* ilo danych dwikowych */ - } dcc_voice_data; - - struct { /* @userlist odpowied listy kontaktw serwera */ - char type; /* rodzaj odpowiedzi */ - char *reply; /* tre odpowiedzi */ - } userlist; - - struct { /* @image_request proba o obrazek */ - uin_t sender; /* nadawca proby */ - uint32_t size; /* rozmiar obrazka */ - uint32_t crc32; /* suma kontrolna */ - } image_request; - - struct { /* @image_reply odpowied z obrazkiem */ - uin_t sender; /* nadawca odpowiedzi */ - uint32_t size; /* rozmiar obrazka */ - uint32_t crc32; /* suma kontrolna */ - char *filename; /* nazwa pliku */ - char *image; /* bufor z obrazkiem */ - } image_reply; - } event; + int type; /**< Rodzaj zdarzenia */ + union gg_event_union event; /**< Informacja o zdarzeniu */ }; struct gg_event *gg_watch_fd(struct gg_session *sess); void gg_event_free(struct gg_event *e); -#define gg_free_event gg_event_free -/* - * funkcje obsugi listy kontaktw. - */ int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count); int gg_notify(struct gg_session *sess, uin_t *userlist, int count); int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type); @@ -600,76 +936,11 @@ int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type); int gg_remove_notify(struct gg_session *sess, uin_t uin); -/* - * funkcje obsugi http. - */ struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header); int gg_http_watch_fd(struct gg_http *h); void gg_http_stop(struct gg_http *h); void gg_http_free(struct gg_http *h); -void gg_http_free_fields(struct gg_http *h); -#define gg_free_http gg_http_free -/* - * struktury opisujca kryteria wyszukiwania dla gg_search(). nieaktualne, - * zastpione przez gg_pubdir50_t. pozostawiono je dla zachowania ABI. - */ -struct gg_search_request { - int active; - unsigned int start; - char *nickname; - char *first_name; - char *last_name; - char *city; - int gender; - int min_birth; - int max_birth; - char *email; - char *phone; - uin_t uin; -}; - -struct gg_search { - int count; - struct gg_search_result *results; -}; - -struct gg_search_result { - uin_t uin; - char *first_name; - char *last_name; - char *nickname; - int born; - int gender; - char *city; - int active; -}; - -#define GG_GENDER_NONE 0 -#define GG_GENDER_FEMALE 1 -#define GG_GENDER_MALE 2 - -/* - * funkcje wyszukiwania. - */ -struct gg_http *gg_search(const struct gg_search_request *r, int async); -int gg_search_watch_fd(struct gg_http *f); -void gg_free_search(struct gg_http *f); -#define gg_search_free gg_free_search - -const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start); -const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start); -const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start); -const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start); -void gg_search_request_free(struct gg_search_request *r); - -/* - * funkcje obsugi katalogu publicznego zgodne z GG 5.0. tym razem funkcje - * zachowuj pewien poziom abstrakcji, eby unikn zmian ABI przy zmianach - * w protokole. - * - * NIE NALEY SI ODWOYWA DO PL gg_pubdir50_t BEZPOREDNIO! - */ uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req); gg_pubdir50_t gg_pubdir50_new(int type); int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value); @@ -681,6 +952,8 @@ uint32_t gg_pubdir50_seq(gg_pubdir50_t res); void gg_pubdir50_free(gg_pubdir50_t res); +#ifndef DOXYGEN + #define GG_PUBDIR50_UIN "FmNumber" #define GG_PUBDIR50_STATUS "FmStatus" #define GG_PUBDIR50_FIRSTNAME "firstname" @@ -699,110 +972,114 @@ #define GG_PUBDIR50_FAMILYNAME "familyname" #define GG_PUBDIR50_FAMILYCITY "familycity" -int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length); +#else -/* - * struct gg_pubdir +/** + * \ingroup pubdir50 * - * operacje na katalogu publicznym. + * Rodzaj pola zapytania. */ -struct gg_pubdir { - int success; /* czy si udao */ - uin_t uin; /* otrzymany numerek. 0 jeli bd */ +enum { + GG_PUBDIR50_UIN, /**< Numer Gadu-Gadu */ + GG_PUBDIR50_STATUS, /**< Status (tylko wynik wyszukiwania) */ + GG_PUBDIR50_FIRSTNAME, /**< Imię */ + GG_PUBDIR50_LASTNAME, /**< Nazwisko */ + GG_PUBDIR50_NICKNAME, /**< Pseudonim */ + GG_PUBDIR50_BIRTHYEAR, /**< Rok urodzenia lub przedział lat oddzielony spacją */ + GG_PUBDIR50_CITY, /**< Miejscowość */ + GG_PUBDIR50_GENDER, /**< Płeć */ + GG_PUBDIR50_ACTIVE, /**< Osoba dostępna (tylko wyszukiwanie) */ + GG_PUBDIR50_START, /**< Numer początkowy wyszukiwania (tylko wyszukiwanie) */ + GG_PUBDIR50_FAMILYNAME, /**< Nazwisko rodowe (tylko wysyłanie informacji o sobie) */ + GG_PUBDIR50_FAMILYCITY, /**< Miejscowość pochodzenia (tylko wysyłanie informacji o sobie) */ +}; + +/** + * \ingroup pubdir50 + * + * Wartość pola GG_PUBDIR50_GENDER przy wyszukiwaniu. Brak pola oznacza dowolną płeć. + */ +enum { + GG_PUBDIR50_GENDER_FEMALE, /**< Kobieta */ + GG_PUBDIR50_GENDER_MALE, /**< Mężczyzna */ }; -/* oglne funkcje, nie powinny by uywane */ +/** + * \ingroup pubdir50 + * + * Wartość pola GG_PUBDIR50_GENDER przy wysyłaniu informacji o sobie. + */ +enum { + GG_PUBDIR50_GENDER_SET_FEMALE, /**< Kobieta */ + GG_PUBDIR50_GENDER_SET_MALE, /**< Mężczyzna */ +}; + +/** + * \ingroup pubdir50 + * + * Wartość pola GG_PUBDIR50_ACTIVE. + */ +enum { + GG_PUBDIR50_ACTIVE_TRUE, /**< Wyszukaj tylko osoby dostępne */ +}; + +#endif /* DOXYGEN */ + +/** + * Wynik operacji na katalogu publicznym. + * + * \ingroup http + */ +struct gg_pubdir { + int success; /**< Flaga powodzenia operacji */ + uin_t uin; /**< Otrzymany numer lub 0 w przypadku błędu */ +}; + int gg_pubdir_watch_fd(struct gg_http *f); void gg_pubdir_free(struct gg_http *f); -#define gg_free_pubdir gg_pubdir_free +/** + * Token autoryzacji niektórych operacji HTTP. + * + * \ingroup token + */ struct gg_token { - int width; /* szeroko obrazka */ - int height; /* wysoko obrazka */ - int length; /* ilo znakw w tokenie */ - char *tokenid; /* id tokenu */ + int width; /**< Szerokość obrazka */ + int height; /**< Wysokość obrazka */ + int length; /**< Liczba znaków w tokenie */ + char *tokenid; /**< Identyfikator tokenu */ }; -/* funkcje dotyczce tokenw */ struct gg_http *gg_token(int async); int gg_token_watch_fd(struct gg_http *h); void gg_token_free(struct gg_http *h); -/* rejestracja nowego numerka */ -struct gg_http *gg_register(const char *email, const char *password, int async); -struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async); struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async); +#ifndef DOXYGEN #define gg_register_watch_fd gg_pubdir_watch_fd #define gg_register_free gg_pubdir_free -#define gg_free_register gg_pubdir_free +#endif -struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async); -struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async); struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async); +#ifndef DOXYGEN #define gg_unregister_watch_fd gg_pubdir_watch_fd #define gg_unregister_free gg_pubdir_free +#endif -/* przypomnienie hasa e-mailem */ -struct gg_http *gg_remind_passwd(uin_t uin, int async); -struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async); struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async); +#ifndef DOXYGEN #define gg_remind_passwd_watch_fd gg_pubdir_watch_fd #define gg_remind_passwd_free gg_pubdir_free -#define gg_free_remind_passwd gg_pubdir_free - -/* zmiana hasa */ -struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async); -struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async); -struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async); -struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async); -#define gg_change_passwd_free gg_pubdir_free -#define gg_free_change_passwd gg_pubdir_free - -/* - * struct gg_change_info_request - * - * opis dania zmiany informacji w katalogu publicznym. - */ -struct gg_change_info_request { - char *first_name; /* imi */ - char *last_name; /* nazwisko */ - char *nickname; /* pseudonim */ - char *email; /* email */ - int born; /* rok urodzenia */ - int gender; /* pe */ - char *city; /* miasto */ -}; - -struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city); -void gg_change_info_request_free(struct gg_change_info_request *r); +#endif -struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async); -#define gg_change_pubdir_watch_fd gg_pubdir_watch_fd -#define gg_change_pubdir_free gg_pubdir_free -#define gg_free_change_pubdir gg_pubdir_free - -/* - * funkcje dotyczce listy kontaktw na serwerze. - */ -struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async); -int gg_userlist_get_watch_fd(struct gg_http *f); -void gg_userlist_get_free(struct gg_http *f); +struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async); +#ifndef DOXYGEN +#define gg_change_passwd_watch_fd gg_pubdir_watch_fd +#define gg_change_passwd_free gg_pubdir_free +#endif -struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async); -int gg_userlist_put_watch_fd(struct gg_http *f); -void gg_userlist_put_free(struct gg_http *f); - -struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async); -int gg_userlist_remove_watch_fd(struct gg_http *f); -void gg_userlist_remove_free(struct gg_http *f); - - - -/* - * funkcje dotyczce komunikacji midzy klientami. - */ -extern int gg_dcc_port; /* port, na ktrym nasuchuje klient */ -extern unsigned long gg_dcc_ip; /* adres, na ktrym nasuchuje klient */ +extern int gg_dcc_port; +extern unsigned long gg_dcc_ip; int gg_dcc_request(struct gg_session *sess, uin_t uin); @@ -814,119 +1091,248 @@ int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename); int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length); -#define GG_DCC_VOICE_FRAME_LENGTH 195 -#define GG_DCC_VOICE_FRAME_LENGTH_505 326 +#define GG_DCC_VOICE_FRAME_LENGTH 195 /**< Rozmiar pakietu głosowego przed wersją Gadu-Gadu 5.0.5 */ +#define GG_DCC_VOICE_FRAME_LENGTH_505 326 /**< Rozmiar pakietu głosowego od wersji Gadu-Gadu 5.0.5 */ struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port); -#define gg_dcc_socket_free gg_free_dcc +#ifndef DOXYGEN +#define gg_dcc_socket_free gg_dcc_free #define gg_dcc_socket_watch_fd gg_dcc_watch_fd +#endif struct gg_event *gg_dcc_watch_fd(struct gg_dcc *d); void gg_dcc_free(struct gg_dcc *c); -#define gg_free_dcc gg_dcc_free - -/* - * jeli chcemy sobie podebugowa, wystarczy ustawi `gg_debug_level'. - * niestety w miar przybywania wpisw `gg_debug(...)' nie chciao mi - * si ustawia odpowiednich leveli, wic wikszo sza do _MISC. - */ -extern int gg_debug_level; /* poziom debugowania. mapa bitowa staych GG_DEBUG_* */ -/* - * mona poda wskanik do funkcji obsugujcej wywoania gg_debug(). - * nieoficjalne, nieudokumentowane, moe si zmieni. jeli kto jest - * zainteresowany, niech da zna na ekg-devel. - */ +struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *d); +struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash); +struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash); +int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset); +int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason); +void gg_dcc7_free(struct gg_dcc7 *d); + +extern int gg_debug_level; + extern void (*gg_debug_handler)(int level, const char *format, va_list ap); +extern void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap); -/* - * mona poda plik, do ktrego bd zapisywane teksty z gg_debug(). - */ extern FILE *gg_debug_file; -#define GG_DEBUG_NET 1 -#define GG_DEBUG_TRAFFIC 2 -#define GG_DEBUG_DUMP 4 -#define GG_DEBUG_FUNCTION 8 -#define GG_DEBUG_MISC 16 +/** + * \ingroup debug + * @{ + */ +#define GG_DEBUG_NET 1 /**< Rejestracja zdarzeń związanych z siecią */ +#define GG_DEBUG_TRAFFIC 2 /**< Rejestracja ruchu sieciowego */ +#define GG_DEBUG_DUMP 4 /**< Rejestracja zawartości pakietów */ +#define GG_DEBUG_FUNCTION 8 /**< Rejestracja wywołań funkcji */ +#define GG_DEBUG_MISC 16 /**< Rejestracja różnych informacji */ +/** @} */ #ifdef GG_DEBUG_DISABLE #define gg_debug(x, y...) do { } while(0) +#define gg_debug_session(z, x, y...) do { } while(0) #else void gg_debug(int level, const char *format, ...); +void gg_debug_session(struct gg_session *sess, int level, const char *format, ...); #endif const char *gg_libgadu_version(void); -/* - * konfiguracja http proxy. - */ -extern int gg_proxy_enabled; /* wcza obsug proxy */ -extern char *gg_proxy_host; /* okrela adres serwera proxy */ -extern int gg_proxy_port; /* okrela port serwera proxy */ -extern char *gg_proxy_username; /* okrela nazw uytkownika przy autoryzacji serwera proxy */ -extern char *gg_proxy_password; /* okrela haso uytkownika przy autoryzacji serwera proxy */ -extern int gg_proxy_http_only; /* wcza obsug proxy wycznie dla usug HTTP */ +extern int gg_proxy_enabled; +extern char *gg_proxy_host; +extern int gg_proxy_port; +extern char *gg_proxy_username; +extern char *gg_proxy_password; +extern int gg_proxy_http_only; + +extern unsigned long gg_local_ip; + +#define GG_LOGIN_HASH_GG32 0x01 /**< Algorytm Gadu-Gadu */ +#define GG_LOGIN_HASH_SHA1 0x02 /**< Algorytm SHA1 */ +#ifndef DOXYGEN -/* - * adres, z ktrego lemy pakiety (np czymy si z serwerem) - * uywany przy gg_connect() +#define GG_PUBDIR50_WRITE 0x01 +#define GG_PUBDIR50_READ 0x02 +#define GG_PUBDIR50_SEARCH 0x03 +#define GG_PUBDIR50_SEARCH_REQUEST GG_PUBDIR50_SEARCH +#define GG_PUBDIR50_SEARCH_REPLY 0x05 + +#else + +/** + * \ingroup pubdir50 + * + * Rodzaj zapytania lub odpowiedzi katalogu publicznego. */ -extern unsigned long gg_local_ip; -/* - * ------------------------------------------------------------------------- - * poniej znajduj si wewntrzne sprawy biblioteki. zwyky klient nie - * powinien ich w ogle rusza, bo i nie ma po co. wszystko mona zaatwi - * procedurami wyszego poziomu, ktrych definicje znajduj si na pocztku - * tego pliku. - * ------------------------------------------------------------------------- - */ +enum { + GG_PUBDIR50_WRITE, /**< Wysłanie do serwera informacji o sobie */ + GG_PUBDIR50_READ, /**< Pobranie z serwera informacji o sobie */ + GG_PUBDIR50_SEARCH, /**< Wyszukiwanie w katalogu publicznym */ + GG_PUBDIR50_SEARCH_REPLY, /**< Wynik wyszukiwania w katalogu publicznym */ +}; + +#endif /* DOXYGEN */ + +/** \cond obsolete */ + +#define gg_free_event gg_event_free +#define gg_free_http gg_http_free +#define gg_free_pubdir gg_pubdir_free +#define gg_free_register gg_pubdir_free +#define gg_free_remind_passwd gg_pubdir_free +#define gg_free_dcc gg_dcc_free +#define gg_free_change_passwd gg_pubdir_free + +struct gg_search_request { + int active; + unsigned int start; + char *nickname; + char *first_name; + char *last_name; + char *city; + int gender; + int min_birth; + int max_birth; + char *email; + char *phone; + uin_t uin; +} /* GG_DEPRECATED */; + +struct gg_search { + int count; + struct gg_search_result *results; +} GG_DEPRECATED; + +struct gg_search_result { + uin_t uin; + char *first_name; + char *last_name; + char *nickname; + int born; + int gender; + char *city; + int active; +} GG_DEPRECATED; + +#define GG_GENDER_NONE 0 +#define GG_GENDER_FEMALE 1 +#define GG_GENDER_MALE 2 + +struct gg_http *gg_search(const struct gg_search_request *r, int async) GG_DEPRECATED; +int gg_search_watch_fd(struct gg_http *f) GG_DEPRECATED; +void gg_free_search(struct gg_http *f) GG_DEPRECATED; +#define gg_search_free gg_free_search -#ifdef __GG_LIBGADU_HAVE_PTHREAD -int gg_resolve_pthread(int *fd, void **resolver, const char *hostname); -#elif defined _WIN32 -int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname); +const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start) GG_DEPRECATED; +const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start) GG_DEPRECATED; +const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start) GG_DEPRECATED; +const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start) GG_DEPRECATED; +void gg_search_request_free(struct gg_search_request *r) GG_DEPRECATED; + +struct gg_http *gg_register(const char *email, const char *password, int async) GG_DEPRECATED; +struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async) GG_DEPRECATED; + +struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async) GG_DEPRECATED; +struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async) GG_DEPRECATED; + +struct gg_http *gg_remind_passwd(uin_t uin, int async) GG_DEPRECATED; +struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async) GG_DEPRECATED; + +struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async) GG_DEPRECATED; +struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async) GG_DEPRECATED; +struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async) GG_DEPRECATED; + +struct gg_change_info_request { + char *first_name; + char *last_name; + char *nickname; + char *email; + int born; + int gender; + char *city; +} /* GG_DEPRECATED */; + +struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city) GG_DEPRECATED; +void gg_change_info_request_free(struct gg_change_info_request *r) GG_DEPRECATED; + +struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async) GG_DEPRECATED; +#define gg_change_pubdir_watch_fd gg_pubdir_watch_fd +#define gg_change_pubdir_free gg_pubdir_free +#define gg_free_change_pubdir gg_pubdir_free + +struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async) GG_DEPRECATED; +int gg_userlist_get_watch_fd(struct gg_http *f) GG_DEPRECATED; +void gg_userlist_get_free(struct gg_http *f) GG_DEPRECATED; + +struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async) GG_DEPRECATED; +int gg_userlist_put_watch_fd(struct gg_http *f) GG_DEPRECATED; +void gg_userlist_put_free(struct gg_http *f) GG_DEPRECATED; + +struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async) GG_DEPRECATED; +int gg_userlist_remove_watch_fd(struct gg_http *f) GG_DEPRECATED; +void gg_userlist_remove_free(struct gg_http *f) GG_DEPRECATED; + +int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length) GG_DEPRECATED; + +/** \endcond */ + +int gg_file_hash_sha1(int fd, uint8_t *result) GG_DEPRECATED; + +#ifdef __GNUC__ +char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))) GG_DEPRECATED; +#else +char *gg_saprintf(const char *format, ...) GG_DEPRECATED; #endif -#ifdef _WIN32 -int gg_thread_socket(int thread_id, int socket); -#endif - -int gg_resolve(int *fd, int *pid, const char *hostname); - -#if defined __GNUC__ && !defined _WIN32 -char *gg_saprintf(const char *format, ...) __attribute__ ((format (printf, 1, 2))); -#else -char *gg_saprintf(const char *format, ...); -#endif - -char *gg_vsaprintf(const char *format, va_list ap); +char *gg_vsaprintf(const char *format, va_list ap) GG_DEPRECATED; #define gg_alloc_sprintf gg_saprintf -char *gg_get_line(char **ptr); +char *gg_get_line(char **ptr) GG_DEPRECATED; -int gg_connect(void *addr, int port, int async); -struct in_addr *gg_gethostbyname(const char *hostname); -char *gg_read_line(int sock, char *buf, int length); -void gg_chomp(char *line); -char *gg_urlencode(const char *str); -int gg_http_hash(const char *format, ...); -int gg_read(struct gg_session *sess, char *buf, int length); -int gg_write(struct gg_session *sess, const char *buf, int length); -void *gg_recv_packet(struct gg_session *sess); -int gg_send_packet(struct gg_session *sess, int type, ...); -unsigned int gg_login_hash(const unsigned char *password, unsigned int seed); -uint32_t gg_fix32(uint32_t x); -uint16_t gg_fix16(uint16_t x); +int gg_connect(void *addr, int port, int async) GG_DEPRECATED; +struct in_addr *gg_gethostbyname(const char *hostname) GG_DEPRECATED; +char *gg_read_line(int sock, char *buf, int length) GG_DEPRECATED; +void gg_chomp(char *line) GG_DEPRECATED; +char *gg_urlencode(const char *str) GG_DEPRECATED; +int gg_http_hash(const char *format, ...) GG_DEPRECATED; +void gg_http_free_fields(struct gg_http *h) GG_DEPRECATED; +int gg_read(struct gg_session *sess, char *buf, int length) GG_DEPRECATED; +int gg_write(struct gg_session *sess, const char *buf, int length) GG_DEPRECATED; +void *gg_recv_packet(struct gg_session *sess) GG_DEPRECATED; +int gg_send_packet(struct gg_session *sess, int type, ...) GG_DEPRECATED; +unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) GG_DEPRECATED; +void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result) GG_DEPRECATED; +uint32_t gg_fix32(uint32_t x) GG_DEPRECATED; +uint16_t gg_fix16(uint16_t x) GG_DEPRECATED; #define fix16 gg_fix16 #define fix32 gg_fix32 -char *gg_proxy_auth(void); -char *gg_base64_encode(const char *buf); -char *gg_base64_decode(const char *buf); -int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq); +char *gg_proxy_auth(void) GG_DEPRECATED; +char *gg_base64_encode(const char *buf) GG_DEPRECATED; +char *gg_base64_decode(const char *buf) GG_DEPRECATED; +int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) GG_DEPRECATED; + +/** + * Kolejka odbieranych obrazków. + */ +struct gg_image_queue { + uin_t sender; /**< Nadawca obrazka */ + uint32_t size; /**< Rozmiar obrazka */ + uint32_t crc32; /**< Suma kontrolna CRC32 */ + char *filename; /**< Nazwa pliku */ + char *image; /**< Bufor z odebranymi danymi */ + uint32_t done; /**< Rozmiar odebranych danych */ + + struct gg_image_queue *next; /**< Kolejny element listy */ +} GG_DEPRECATED; + +int gg_dcc7_handle_id(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED; +int gg_dcc7_handle_new(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED; +int gg_dcc7_handle_info(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED; +int gg_dcc7_handle_accept(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED; +int gg_dcc7_handle_reject(struct gg_session *sess, struct gg_event *e, void *payload, int len) GG_DEPRECATED; #define GG_APPMSG_HOST "appmsg.gadu-gadu.pl" #define GG_APPMSG_PORT 80 @@ -941,63 +1347,111 @@ #define GG_HTTPS_PORT 443 #define GG_HTTP_USERAGENT "Mozilla/4.7 [en] (Win98; I)" -#define GG_DEFAULT_CLIENT_VERSION "6, 1, 0, 158" -#define GG_DEFAULT_PROTOCOL_VERSION 0x24 +#define GG_DEFAULT_CLIENT_VERSION "8.0.0.7669" +#define GG_DEFAULT_PROTOCOL_VERSION 0x2e #define GG_DEFAULT_TIMEOUT 30 #define GG_HAS_AUDIO_MASK 0x40000000 +#define GG_HAS_AUDIO7_MASK 0x20000000 #define GG_ERA_OMNIX_MASK 0x04000000 -#define GG_LIBGADU_VERSION "1.5.20050718" +#undef GG_LIBGADU_VERSION + +#ifndef DOXYGEN + +#define GG_FEATURE_MSG77 0x01 +#define GG_FEATURE_STATUS77 0x02 +#define GG_FEATURE_DND_FFC 0x10 +#define GG_FEATURE_IMAGE_DESCR 0x20 + +/* Poniższe makra zostały zachowane dla zgodności API */ +#define GG_FEATURE_MSG80 0 +#define GG_FEATURE_STATUS80 0 +#define GG_FEATURE_STATUS80BETA 0 +#define GG_FEATURE_ALL (GG_FEATURE_DND_FFC | GG_FEATURE_IMAGE_DESCR) + +#else + +/** + * \ingroup login + * + * Flagi opcji protokołu. + */ +enum { + GG_FEATURE_MSG77, /**< Klient życzy sobie otrzymywać wiadomości zgodnie z protokołem 7.7 */ + GG_FEATURE_STATUS77, /**< Klient życzy sobie otrzymywać zmiany stanu zgodnie z protokołem 7.7 */ + GG_FEATURE_DND_FFC, /**< Klient obsługuje statusy "nie przeszkadzać" i "poGGadaj ze mną" */ + GG_FEATURE_IMAGE_DESCR, /**< Klient obsługuje opisy graficzne oraz flagę \c GG_STATUS80_DESCR_MASK */ +}; + + +#endif #define GG_DEFAULT_DCC_PORT 1550 struct gg_header { uint32_t type; /* typ pakietu */ - uint32_t length; /* dugo reszty pakietu */ + uint32_t length; /* długość reszty pakietu */ } GG_PACKED; #define GG_WELCOME 0x0001 #define GG_NEED_EMAIL 0x0014 struct gg_welcome { - uint32_t key; /* klucz szyfrowania hasa */ + uint32_t key; /* klucz szyfrowania hasła */ } GG_PACKED; #define GG_LOGIN 0x000c struct gg_login { - uint32_t uin; /* mj numerek */ - uint32_t hash; /* hash hasa */ - uint32_t status; /* status na dzie dobry */ + uint32_t uin; /* mój numerek */ + uint32_t hash; /* hash hasła */ + uint32_t status; /* status na dzień dobry */ uint32_t version; /* moja wersja klienta */ - uint32_t local_ip; /* mj adres ip */ - uint16_t local_port; /* port, na ktrym sucham */ + uint32_t local_ip; /* mój adres ip */ + uint16_t local_port; /* port, na którym słucham */ } GG_PACKED; #define GG_LOGIN_EXT 0x0013 struct gg_login_ext { - uint32_t uin; /* mj numerek */ - uint32_t hash; /* hash hasa */ - uint32_t status; /* status na dzie dobry */ + uint32_t uin; /* mój numerek */ + uint32_t hash; /* hash hasła */ + uint32_t status; /* status na dzień dobry */ uint32_t version; /* moja wersja klienta */ - uint32_t local_ip; /* mj adres ip */ - uint16_t local_port; /* port, na ktrym sucham */ - uint32_t external_ip; /* zewntrzny adres ip */ - uint16_t external_port; /* zewntrzny port */ + uint32_t local_ip; /* mój adres ip */ + uint16_t local_port; /* port, na którym słucham */ + uint32_t external_ip; /* zewnętrzny adres ip */ + uint16_t external_port; /* zewnętrzny port */ } GG_PACKED; #define GG_LOGIN60 0x0015 struct gg_login60 { - uint32_t uin; /* mj numerek */ - uint32_t hash; /* hash hasa */ - uint32_t status; /* status na dzie dobry */ + uint32_t uin; /* mój numerek */ + uint32_t hash; /* hash hasła */ + uint32_t status; /* status na dzień dobry */ uint32_t version; /* moja wersja klienta */ uint8_t dunno1; /* 0x00 */ - uint32_t local_ip; /* mj adres ip */ - uint16_t local_port; /* port, na ktrym sucham */ - uint32_t external_ip; /* zewntrzny adres ip */ - uint16_t external_port; /* zewntrzny port */ + uint32_t local_ip; /* mój adres ip */ + uint16_t local_port; /* port, na którym słucham */ + uint32_t external_ip; /* zewnętrzny adres ip */ + uint16_t external_port; /* zewnętrzny port */ + uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ + uint8_t dunno2; /* 0xbe */ +} GG_PACKED; + +#define GG_LOGIN70 0x0019 + +struct gg_login70 { + uint32_t uin; /* mój numerek */ + uint8_t hash_type; /* rodzaj hashowania hasła */ + uint8_t hash[64]; /* hash hasła dopełniony zerami */ + uint32_t status; /* status na dzień dobry */ + uint32_t version; /* moja wersja klienta */ + uint8_t dunno1; /* 0x00 */ + uint32_t local_ip; /* mój adres ip */ + uint16_t local_port; /* port, na którym słucham */ + uint32_t external_ip; /* zewnętrzny adres ip (???) */ + uint16_t external_port; /* zewnętrzny port (???) */ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ uint8_t dunno2; /* 0xbe */ } GG_PACKED; @@ -1008,70 +1462,135 @@ #define GG_PUBDIR50_REQUEST 0x0014 -#define GG_PUBDIR50_WRITE 0x01 -#define GG_PUBDIR50_READ 0x02 -#define GG_PUBDIR50_SEARCH 0x03 -#define GG_PUBDIR50_SEARCH_REQUEST GG_PUBDIR50_SEARCH -#define GG_PUBDIR50_SEARCH_REPLY 0x05 - struct gg_pubdir50_request { uint8_t type; /* GG_PUBDIR50_* */ - uint32_t seq; /* czas wysania zapytania */ + uint32_t seq; /* czas wysłania zapytania */ } GG_PACKED; #define GG_PUBDIR50_REPLY 0x000e struct gg_pubdir50_reply { uint8_t type; /* GG_PUBDIR50_* */ - uint32_t seq; /* czas wysania zapytania */ + uint32_t seq; /* czas wysłania zapytania */ } GG_PACKED; #define GG_NEW_STATUS 0x0002 -#define GG_STATUS_NOT_AVAIL 0x0001 /* niedostpny */ -#define GG_STATUS_NOT_AVAIL_DESCR 0x0015 /* niedostpny z opisem (4.8) */ -#define GG_STATUS_AVAIL 0x0002 /* dostpny */ -#define GG_STATUS_AVAIL_DESCR 0x0004 /* dostpny z opisem (4.9) */ -#define GG_STATUS_BUSY 0x0003 /* zajty */ -#define GG_STATUS_BUSY_DESCR 0x0005 /* zajty z opisem (4.8) */ -#define GG_STATUS_INVISIBLE 0x0014 /* niewidoczny (4.6) */ -#define GG_STATUS_INVISIBLE_DESCR 0x0016 /* niewidoczny z opisem (4.9) */ -#define GG_STATUS_BLOCKED 0x0006 /* zablokowany */ +#ifndef DOXYGEN + +#define GG_STATUS_NOT_AVAIL 0x0001 +#define GG_STATUS_NOT_AVAIL_DESCR 0x0015 +#define GG_STATUS_FFC 0x0017 +#define GG_STATUS_FFC_DESCR 0x0018 +#define GG_STATUS_AVAIL 0x0002 +#define GG_STATUS_AVAIL_DESCR 0x0004 +#define GG_STATUS_BUSY 0x0003 +#define GG_STATUS_BUSY_DESCR 0x0005 +#define GG_STATUS_DND 0x0021 +#define GG_STATUS_DND_DESCR 0x0022 +#define GG_STATUS_INVISIBLE 0x0014 +#define GG_STATUS_INVISIBLE_DESCR 0x0016 +#define GG_STATUS_BLOCKED 0x0006 + +#define GG_STATUS_IMAGE_MASK 0x0100 +#define GG_STATUS_DESCR_MASK 0x4000 +#define GG_STATUS_FRIENDS_MASK 0x8000 + +#else -#define GG_STATUS_FRIENDS_MASK 0x8000 /* tylko dla znajomych (4.6) */ - -#define GG_STATUS_DESCR_MAXSIZE 70 +/** + * Rodzaje statusów użytkownika. + * + * \ingroup status + */ +enum { + GG_STATUS_NOT_AVAIL, /**< Niedostępny */ + GG_STATUS_NOT_AVAIL_DESCR, /**< Niedostępny z opisem */ + GG_STATUS_FFC, /**< PoGGadaj ze mną */ + GG_STATUS_FFC_DESCR, /**< PoGGadaj ze mną z opisem */ + GG_STATUS_AVAIL, /**< Dostępny */ + GG_STATUS_AVAIL_DESCR, /**< Dostępny z opisem */ + GG_STATUS_BUSY, /**< Zajęty */ + GG_STATUS_BUSY_DESCR, /**< Zajęty z opisem */ + GG_STATUS_DND, /**< Nie przeszkadzać */ + GG_STATUS_DND_DESCR, /**< Nie przeszakdzać z opisem */ + GG_STATUS_INVISIBLE, /**< Niewidoczny (tylko własny status) */ + GG_STATUS_INVISIBLE_DESCR, /**< Niewidoczny z opisem (tylko własny status) */ + GG_STATUS_BLOCKED, /**< Zablokowany (tylko status innych) */ + GG_STATUS_IMAGE_MASK, /**< Flaga bitowa oznaczająca opis graficzny (tylko jeśli wybrano \c GG_FEATURE_IMAGE_DESCR) */ + GG_STATUS_DESCR_MASK, /**< Flaga bitowa oznaczająca status z opisem (tylko jeśli wybrano \c GG_FEATURE_IMAGE_DESCR) */ + GG_STATUS_FRIENDS_MASK, /**< Flaga bitowa dostępności tylko dla znajomych */ +}; -/* - * makra do atwego i szybkiego sprawdzania stanu. +#endif /* DOXYGEN */ + +/** + * \ingroup status + * + * Flaga bitowa dostepnosci informujaca ze mozemy voipowac */ +#define GG_STATUS_VOICE_MASK 0x20000 /**< czy ma wlaczone audio (7.7) */ + +/** + * \ingroup status + * + * Maksymalna długośc opisu. + */ +#define GG_STATUS_DESCR_MAXSIZE 255 +#define GG_STATUS_DESCR_MAXSIZE_PRE_8_0 70 + +#define GG_STATUS_MASK 0xff + /* GG_S_F() tryb tylko dla znajomych */ #define GG_S_F(x) (((x) & GG_STATUS_FRIENDS_MASK) != 0) -/* GG_S() stan bez uwzgldnienia trybu tylko dla znajomych */ -#define GG_S(x) ((x) & ~GG_STATUS_FRIENDS_MASK) +/* GG_S() stan bez uwzględnienia dodatkowych flag */ +#define GG_S(x) ((x) & GG_STATUS_MASK) -/* GG_S_A() dostpny */ -#define GG_S_A(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR) + +/* GG_S_FF() chętny do rozmowy */ +#define GG_S_FF(x) (GG_S(x) == GG_STATUS_FFC || GG_S(x) == GG_STATUS_FFC_DESCR) -/* GG_S_NA() niedostpny */ -#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR) +/* GG_S_AV() dostępny */ +#define GG_S_AV(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR) + +/* GG_S_AW() zaraz wracam */ +#define GG_S_AW(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR) -/* GG_S_B() zajty */ -#define GG_S_B(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR) +/* GG_S_DD() nie przeszkadzać */ +#define GG_S_DD(x) (GG_S(x) == GG_STATUS_DND || GG_S(x) == GG_STATUS_DND_DESCR) + +/* GG_S_NA() niedostępny */ +#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR) /* GG_S_I() niewidoczny */ #define GG_S_I(x) (GG_S(x) == GG_STATUS_INVISIBLE || GG_S(x) == GG_STATUS_INVISIBLE_DESCR) + +/* GG_S_A() dostępny lub chętny do rozmowy */ +#define GG_S_A(x) (GG_S_FF(x) || GG_S_AV(x)) + +/* GG_S_B() zajęty lub nie przeszkadzać */ +#define GG_S_B(x) (GG_S_AW(x) || GG_S_DD(x)) + + /* GG_S_D() stan opisowy */ -#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || GG_S(x) == GG_STATUS_AVAIL_DESCR || GG_S(x) == GG_STATUS_BUSY_DESCR || GG_S(x) == GG_STATUS_INVISIBLE_DESCR) +#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || \ + GG_S(x) == GG_STATUS_FFC_DESCR || \ + GG_S(x) == GG_STATUS_AVAIL_DESCR || \ + GG_S(x) == GG_STATUS_BUSY_DESCR || \ + GG_S(x) == GG_STATUS_DND_DESCR || \ + GG_S(x) == GG_STATUS_INVISIBLE_DESCR) -/* GG_S_BL() blokowany lub blokujcy */ +/* GG_S_BL() blokowany lub blokujący */ #define GG_S_BL(x) (GG_S(x) == GG_STATUS_BLOCKED) +/** + * Zmiana statusu (pakiet \c GG_NEW_STATUS i \c GG_NEW_STATUS80BETA) + */ struct gg_new_status { - uint32_t status; /* na jaki zmieni? */ + uint32_t status; /**< Nowy status */ } GG_PACKED; #define GG_NOTIFY_FIRST 0x000f @@ -1081,12 +1600,29 @@ struct gg_notify { uint32_t uin; /* numerek danej osoby */ - uint8_t dunno1; /* rodzaj wpisu w licie */ + uint8_t dunno1; /* rodzaj wpisu w liście */ } GG_PACKED; -#define GG_USER_OFFLINE 0x01 /* bdziemy niewidoczni dla uytkownika */ -#define GG_USER_NORMAL 0x03 /* zwyky uytkownik */ -#define GG_USER_BLOCKED 0x04 /* zablokowany uytkownik */ +#ifndef DOXYGEN + +#define GG_USER_OFFLINE 0x01 +#define GG_USER_NORMAL 0x03 +#define GG_USER_BLOCKED 0x04 + +#else + +/** + * \ingroup contacts + * + * Rodzaj kontaktu. + */ +enum { + GG_USER_NORMAL, /**< Zwykły kontakt */ + GG_USER_BLOCKED, /**< Zablokowany */ + GG_USER_OFFLINE, /**< Niewidoczny dla kontaktu */ +}; + +#endif /* DOXYGEN */ #define GG_LIST_EMPTY 0x0012 @@ -1096,7 +1632,7 @@ uint32_t uin; /* numerek */ uint32_t status; /* status danej osoby */ uint32_t remote_ip; /* adres ip delikwenta */ - uint16_t remote_port; /* port, na ktrym sucha klient */ + uint16_t remote_port; /* port, na którym słucha klient */ uint32_t version; /* wersja klienta */ uint16_t dunno2; /* znowu port? */ } GG_PACKED; @@ -1107,7 +1643,7 @@ uint32_t uin; /* numerek plus flagi w MSB */ uint8_t status; /* status danej osoby */ uint32_t remote_ip; /* adres ip delikwenta */ - uint16_t remote_port; /* port, na ktrym sucha klient */ + uint16_t remote_port; /* port, na którym słucha klient */ uint8_t version; /* wersja klienta */ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ uint8_t dunno1; /* 0x00 */ @@ -1119,12 +1655,38 @@ uint32_t uin; /* numerek plus flagi w MSB */ uint8_t status; /* status danej osoby */ uint32_t remote_ip; /* adres ip delikwenta */ - uint16_t remote_port; /* port, na ktrym sucha klient */ + uint16_t remote_port; /* port, na którym słucha klient */ uint8_t version; /* wersja klienta */ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ uint8_t dunno1; /* 0x00 */ } GG_PACKED; +#define GG_NOTIFY_REPLY77 0x0018 + +struct gg_notify_reply77 { + uint32_t uin; /* numerek plus flagi w MSB */ + uint8_t status; /* status danej osoby */ + uint32_t remote_ip; /* adres ip delikwenta */ + uint16_t remote_port; /* port, na którym słucha klient */ + uint8_t version; /* wersja klienta */ + uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ + uint8_t dunno1; /* 0x00 */ + uint32_t dunno2; /* ? */ +} GG_PACKED; + +#define GG_STATUS77 0x0017 + +struct gg_status77 { + uint32_t uin; /* numerek plus flagi w MSB */ + uint8_t status; /* status danej osoby */ + uint32_t remote_ip; /* adres ip delikwenta */ + uint16_t remote_port; /* port, na którym słucha klient */ + uint8_t version; /* wersja klienta */ + uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ + uint8_t dunno1; /* 0x00 */ + uint32_t dunno2; /* ? */ +} GG_PACKED; + #define GG_ADD_NOTIFY 0x000d #define GG_REMOVE_NOTIFY 0x000e @@ -1142,15 +1704,41 @@ #define GG_SEND_MSG 0x000b +#ifndef DOXYGEN + #define GG_CLASS_QUEUED 0x0001 #define GG_CLASS_OFFLINE GG_CLASS_QUEUED #define GG_CLASS_MSG 0x0004 #define GG_CLASS_CHAT 0x0008 #define GG_CLASS_CTCP 0x0010 #define GG_CLASS_ACK 0x0020 -#define GG_CLASS_EXT GG_CLASS_ACK /* kompatybilno wstecz */ +#define GG_CLASS_EXT GG_CLASS_ACK /**< Dla kompatybilności wstecz */ + +#else -#define GG_MSG_MAXSIZE 2000 +/** + * Klasy wiadomości. Wartości są maskami bitowymi, które w większości + * przypadków można łączyć (połączenie \c GG_CLASS_MSG i \c GG_CLASS_CHAT + * nie ma sensu). + * + * \ingroup messages + */ +enum { + GG_CLASS_MSG, /**< Wiadomość ma pojawić się w osobnym oknie */ + GG_CLASS_CHAT, /**< Wiadomość ma pojawić się w oknie rozmowy */ + GG_CLASS_CTCP, /**< Wiadomość przeznaczona dla klienta Gadu-Gadu */ + GG_CLASS_ACK, /**< Klient nie życzy sobie potwierdzenia */ + GG_CLASS_QUEUED, /**< Wiadomość zakolejkowana na serwerze (tylko przy odbieraniu) */ +}; + +#endif /* DOXYGEN */ + +/** + * Maksymalna długość wiadomości. + * + * \ingroup messages + */ +#define GG_MSG_MAXSIZE 1989 struct gg_send_msg { uint32_t recipient; @@ -1163,16 +1751,19 @@ uint16_t length; } GG_PACKED; +/** + * Struktura opisująca formatowanie tekstu. W zależności od wartości pola + * \c font, zaraz za tą strukturą może wystąpić \c gg_msg_richtext_color + * lub \c gg_msg_richtext_image. + * + * \ingroup messages + */ struct gg_msg_richtext_format { - uint16_t position; - uint8_t font; + uint16_t position; /**< Początkowy znak formatowania (liczony od 0) */ + uint8_t font; /**< Atrybuty formatowania */ } GG_PACKED; -struct gg_msg_richtext_image { - uint16_t unknown1; - uint32_t size; - uint32_t crc32; -} GG_PACKED; +#ifndef DOXYGEN #define GG_FONT_BOLD 0x01 #define GG_FONT_ITALIC 0x02 @@ -1180,10 +1771,44 @@ #define GG_FONT_COLOR 0x08 #define GG_FONT_IMAGE 0x80 +#else + +/** + * Atrybuty formatowania wiadomości. + * + * \ingroup messages + */ +enum { + GG_FONT_BOLD, + GG_FONT_ITALIC, + GG_FONT_UNDERLINE, + GG_FONT_COLOR, + GG_FONT_IMAGE +}; + +#endif /* DOXYGEN */ + +/** + * Struktura opisującą kolor tekstu dla atrybutu \c GG_FONT_COLOR. + * + * \ingroup messages + */ struct gg_msg_richtext_color { - uint8_t red; - uint8_t green; - uint8_t blue; + uint8_t red; /**< Składowa czerwona koloru */ + uint8_t green; /**< Składowa zielona koloru */ + uint8_t blue; /**< Składowa niebieska koloru */ +} GG_PACKED; + +/** + * Strukturya opisująca obrazek wstawiony do wiadomości dla atrubutu + * \c GG_FONT_IMAGE. + * + * \ingroup messages + */ +struct gg_msg_richtext_image { + uint16_t unknown1; /**< Nieznane pole o wartości 0x0109 */ + uint32_t size; /**< Rozmiar obrazka */ + uint32_t crc32; /**< Suma kontrolna CRC32 obrazka */ } GG_PACKED; struct gg_msg_recipients { @@ -1207,12 +1832,32 @@ #define GG_SEND_MSG_ACK 0x0005 +#ifndef DOXYGEN + #define GG_ACK_BLOCKED 0x0001 #define GG_ACK_DELIVERED 0x0002 #define GG_ACK_QUEUED 0x0003 #define GG_ACK_MBOXFULL 0x0004 #define GG_ACK_NOT_DELIVERED 0x0006 +#else + +/** + * Status doręczenia wiadomości. + * + * \ingroup messages + */ +enum +{ + GG_ACK_DELIVERED, /**< Wiadomość dostarczono. */ + GG_ACK_QUEUED, /**< Wiadomość zakolejkowano z powodu niedostępności odbiorcy. */ + GG_ACK_BLOCKED, /**< Wiadomość zablokowana przez serwer (spam, świąteczne ograniczenia itd.) */ + GG_ACK_MBOXFULL, /**< Wiadomości nie dostarczono z powodu zapełnionej kolejki wiadomości odbiorcy. */ + GG_ACK_NOT_DELIVERED /**< Wiadomości nie dostarczono (tylko dla \c GG_CLASS_CTCP). */ +}; + +#endif /* DOXYGEN */ + struct gg_send_msg_ack { uint32_t status; uint32_t recipient; @@ -1236,29 +1881,59 @@ #define GG_USERLIST_REQUEST 0x0016 +#define GG_XML_EVENT 0x0027 + +#ifndef DOXYGEN + #define GG_USERLIST_PUT 0x00 #define GG_USERLIST_PUT_MORE 0x01 #define GG_USERLIST_GET 0x02 +#else + +/** + * \ingroup importexport + * + * Rodzaj zapytania. + */ +enum { + GG_USERLIST_PUT, /**< Eksport listy kontaktów. */ + GG_USERLIST_GET, /**< Import listy kontaktów. */ +}; + +#endif /* DOXYGEN */ + struct gg_userlist_request { uint8_t type; } GG_PACKED; #define GG_USERLIST_REPLY 0x0010 +#ifndef DOXYGEN + #define GG_USERLIST_PUT_REPLY 0x00 #define GG_USERLIST_PUT_MORE_REPLY 0x02 #define GG_USERLIST_GET_REPLY 0x06 #define GG_USERLIST_GET_MORE_REPLY 0x04 +#else + +/** + * \ingroup importexport + * + * Rodzaj odpowiedzi. + */ +enum { + GG_USERLIST_PUT_REPLY, /**< Wyeksportowano listy kontaktów. */ + GG_USERLIST_GET_REPLY, /**< Zaimportowano listę kontaktów. */ +}; + +#endif /* DOXYGEN */ + struct gg_userlist_reply { uint8_t type; } GG_PACKED; -/* - * pakiety, stae, struktury dla DCC - */ - struct gg_dcc_tiny_packet { uint8_t type; /* rodzaj pakietu */ } GG_PACKED; @@ -1274,14 +1949,14 @@ } GG_PACKED; /* - * pki co, nie znamy dokadnie protokou. nie wiemy, co czemu odpowiada. - * nazwy s niepowane i tymczasowe. + * póki co, nie znamy dokładnie protokołu. nie wiemy, co czemu odpowiada. + * nazwy są niepoważne i tymczasowe. */ #define GG_DCC_WANT_FILE 0x0003 /* peer chce plik */ -#define GG_DCC_HAVE_FILE 0x0001 /* wic mu damy */ +#define GG_DCC_HAVE_FILE 0x0001 /* więc mu damy */ #define GG_DCC_HAVE_FILEINFO 0x0003 /* niech ma informacje o pliku */ #define GG_DCC_GIMME_FILE 0x0006 /* peer jest pewny */ -#define GG_DCC_CATCH_FILE 0x0002 /* wysyamy plik */ +#define GG_DCC_CATCH_FILE 0x0002 /* wysyłamy plik */ #define GG_DCC_FILEATTR_READONLY 0x0020 @@ -1290,11 +1965,88 @@ #define GG_DCC_TIMEOUT_FILE_ACK 300 /* 5 minut */ #define GG_DCC_TIMEOUT_VOICE_ACK 300 /* 5 minut */ +#define GG_DCC7_INFO 0x1f + +struct gg_dcc7_info { + uint32_t uin; /* numer nadawcy */ + uint32_t type; /* sposób połączenia */ + gg_dcc7_id_t id; /* identyfikator połączenia */ + char info[GG_DCC7_INFO_LEN]; /* informacje o połączeniu "ip port" */ +} GG_PACKED; + +#define GG_DCC7_NEW 0x20 + +struct gg_dcc7_new { + gg_dcc7_id_t id; /* identyfikator połączenia */ + uint32_t uin_from; /* numer nadawcy */ + uint32_t uin_to; /* numer odbiorcy */ + uint32_t type; /* rodzaj transmisji */ + unsigned char filename[GG_DCC7_FILENAME_LEN]; /* nazwa pliku */ + uint32_t size; /* rozmiar pliku */ + uint32_t size_hi; /* rozmiar pliku (starsze bajty) */ + unsigned char hash[GG_DCC7_HASH_LEN]; /* hash SHA1 */ +} GG_PACKED; + +#define GG_DCC7_ACCEPT 0x21 + +struct gg_dcc7_accept { + uint32_t uin; /* numer przyjmującego połączenie */ + gg_dcc7_id_t id; /* identyfikator połączenia */ + uint32_t offset; /* offset przy wznawianiu transmisji */ + uint32_t dunno1; /* 0x00000000 */ +} GG_PACKED; + +// XXX API +#define GG_DCC7_TYPE_P2P 0x00000001 /**< Połączenie bezpośrednie */ +#define GG_DCC7_TYPE_SERVER 0x00000002 /**< Połączenie przez serwer */ + +#define GG_DCC7_REJECT 0x22 + +struct gg_dcc7_reject { + uint32_t uin; /**< Numer odrzucającego połączenie */ + gg_dcc7_id_t id; /**< Identyfikator połączenia */ + uint32_t reason; /**< Powód rozłączenia */ +} GG_PACKED; + +// XXX API +#define GG_DCC7_REJECT_BUSY 0x00000001 /**< Połączenie bezpośrednie już trwa, nie umiem obsłużyć więcej */ +#define GG_DCC7_REJECT_USER 0x00000002 /**< Użytkownik odrzucił połączenie */ +#define GG_DCC7_REJECT_VERSION 0x00000006 /**< Druga strona ma wersję klienta nieobsługującą połączeń bezpośrednich tego typu */ + +#define GG_DCC7_ID_REQUEST 0x23 + +struct gg_dcc7_id_request { + uint32_t type; /**< Rodzaj tranmisji */ +} GG_PACKED; + +// XXX API +#define GG_DCC7_TYPE_VOICE 0x00000001 /**< Transmisja głosu */ +#define GG_DCC7_TYPE_FILE 0x00000004 /**< transmisja pliku */ + +#define GG_DCC7_ID_REPLY 0x23 + +struct gg_dcc7_id_reply { + uint32_t type; /** Rodzaj transmisji */ + gg_dcc7_id_t id; /** Przyznany identyfikator */ +} GG_PACKED; + +#define GG_DCC7_DUNNO1 0x24 + +struct gg_dcc7_dunno1 { + // XXX +} GG_PACKED; + +#define GG_DCC7_TIMEOUT_CONNECT 10 /* 10 sekund */ +#define GG_DCC7_TIMEOUT_SEND 1800 /* 30 minut */ +#define GG_DCC7_TIMEOUT_GET 1800 /* 30 minut */ +#define GG_DCC7_TIMEOUT_FILE_ACK 300 /* 5 minut */ +#define GG_DCC7_TIMEOUT_VOICE_ACK 300 /* 5 minut */ + #ifdef __cplusplus -#ifdef _MSC_VER +} +#ifdef _WIN32 #pragma pack(pop) #endif -} #endif #endif /* __GG_LIBGADU_H */
--- a/libpurple/protocols/gg/lib/obsolete.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/gg/lib/obsolete.c Tue Mar 09 21:41:41 2010 +0000 @@ -1,4 +1,4 @@ -/* $Id: obsolete.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: obsolete.c 854 2009-10-12 21:06:28Z wojtekka $ */ /* * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl> @@ -14,16 +14,23 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ -/* - * Plik zawiera deklaracje funkcji, ktre s ju nieaktualne ze wzgldu - * na zmiany w protokole, ale s wymagane przez aplikacje linkowane ze - * starszymi wersjami bibliotek. +/** + * \file obsolete.c + * + * \brief Nieaktualne funkcje + * + * Plik zawiera definicje funkcji, które są już nieaktualne ze względu + * na zmiany w protokole. Programy konsolidowane ze starszych wersjami + * bibliotek powinny nadal mieć możliwość działania, mimo ograniczonej + * funkcjonalności. */ +/** \cond obsolete */ + #include <errno.h> #include "libgadu.h" @@ -205,3 +212,25 @@ { } + +int gg_resolve(int *fd, int *pid, const char *hostname) +{ + return -1; +} + +void gg_resolve_pthread_cleanup(void *arg, int kill) +{ + +} + +int gg_resolve_pthread(int *fd, void **resolver, const char *hostname) +{ + return -1; +} + +int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length) +{ + return -1; +} + +/** \endcond */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/protocol.h Tue Mar 09 21:41:41 2010 +0000 @@ -0,0 +1,165 @@ +/* $Id$ */ + +/* + * (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef LIBGADU_PROTOCOL_H +#define LIBGADU_PROTOCOL_H + +#include "libgadu.h" + +#ifdef _WIN32 +#pragma pack(push, 1) +#endif + +#define GG_LOGIN80BETA 0x0029 + +#define GG_LOGIN80 0x0031 + +#undef GG_FEATURE_STATUS80BETA +#undef GG_FEATURE_MSG80 +#undef GG_FEATURE_STATUS80 +#define GG_FEATURE_STATUS80BETA 0x01 +#define GG_FEATURE_MSG80 0x02 +#define GG_FEATURE_STATUS80 0x05 + +#define GG8_LANG "pl" +#define GG8_VERSION "Gadu-Gadu Client Build 8.0.0.8731" + +struct gg_login80 { + uint32_t uin; /* mój numerek */ + uint8_t language[2]; /* język: GG8_LANG */ + uint8_t hash_type; /* rodzaj hashowania hasła */ + uint8_t hash[64]; /* hash hasła dopełniony zerami */ + uint32_t status; /* status na dzień dobry */ + uint32_t flags; /* flagi (przeznaczenie nieznane) */ + uint32_t features; /* opcje protokołu (GG8_FEATURES) */ + uint32_t local_ip; /* mój adres ip */ + uint16_t local_port; /* port, na którym słucham */ + uint32_t external_ip; /* zewnętrzny adres ip (???) */ + uint16_t external_port; /* zewnętrzny port (???) */ + uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */ + uint8_t dunno2; /* 0x64 */ +} GG_PACKED; + +#define GG_LOGIN_HASH_TYPE_INVALID 0x0016 + +#define GG_LOGIN80_OK 0x0035 + +#define GG_NEW_STATUS80BETA 0x0028 + +#define GG_NEW_STATUS80 0x0038 + +/** + * Zmiana stanu (pakiet \c GG_NEW_STATUS80) + */ +struct gg_new_status80 { + uint32_t status; /**< Nowy status */ + uint32_t flags; /**< flagi (nieznane przeznaczenie) */ + uint32_t description_size; /**< rozmiar opisu */ +} GG_PACKED; + +#define GG_STATUS80BETA 0x002a +#define GG_NOTIFY_REPLY80BETA 0x002b + +#define GG_STATUS80 0x0036 +#define GG_NOTIFY_REPLY80 0x0037 + +struct gg_notify_reply80 { + uint32_t uin; /* numerek plus flagi w najstarszym bajcie */ + uint32_t status; /* status danej osoby */ + uint32_t flags; /* flagi (przeznaczenie nieznane) */ + uint32_t remote_ip; /* adres IP bezpośrednich połączeń */ + uint16_t remote_port; /* port bezpośrednich połączeń */ + uint8_t image_size; /* maksymalny rozmiar obrazków w KB */ + uint8_t unknown2; /* 0x00 */ + uint32_t unknown3; /* 0x00000000 */ + uint32_t descr_len; /* rozmiar opisu */ +} GG_PACKED; + +#define GG_SEND_MSG80 0x002d + +struct gg_send_msg80 { + uint32_t recipient; + uint32_t seq; + uint32_t msgclass; + uint32_t offset_plain; + uint32_t offset_attr; +} GG_PACKED; + +#define GG_RECV_MSG80 0x002e + +struct gg_recv_msg80 { + uint32_t sender; + uint32_t seq; + uint32_t time; + uint32_t msgclass; + uint32_t offset_plain; + uint32_t offset_attr; +} GG_PACKED; + +#define GG_DISCONNECT_ACK 0x000d + +#define GG_DCC7_VOICE_RETRIES 0x11 /* 17 powtorzen */ + +#define GG_DCC7_RESERVED1 0xdeadc0de +#define GG_DCC7_RESERVED2 0xdeadbeaf + +struct gg_dcc7_voice_auth { + uint8_t type; /* 0x00 -> wysylanie ID + 0x01 -> potwierdzenie ID + */ + gg_dcc7_id_t id; /* identyfikator połączenia */ + uint32_t reserved1; /* GG_DCC7_RESERVED1 */ + uint32_t reserved2; /* GG_DCC7_RESERVED2 */ +} GG_PACKED; + +struct gg_dcc7_voice_nodata { /* wyciszony mikrofon, ten pakiet jest wysylany co 1s (jesli chcemy podtrzymac polaczenie) */ + uint8_t type; /* 0x02 */ + gg_dcc7_id_t id; /* identyfikator połączenia */ + uint32_t reserved1; /* GG_DCC7_RESERVED1 */ + uint32_t reserved2; /* GG_DCC7_RESERVED2 */ +} GG_PACKED; + +struct gg_dcc7_voice_data { + uint8_t type; /* 0x03 */ + uint32_t did; /* XXX: co ile zwieksza sie u nas id pakietu [uzywac 0x28] */ + uint32_t len; /* rozmiar strukturki - 1 (sizeof(type)) */ + uint32_t packet_id; /* numerek pakietu */ + uint32_t datalen; /* rozmiar danych */ + /* char data[]; */ /* ramki: albo gsm, albo speex, albo melp, albo inne. */ +} GG_PACKED; + +struct gg_dcc7_voice_init { + uint8_t type; /* 0x04 */ + uint32_t id; /* nr kroku [0x1 - 0x5] */ + uint32_t protocol; /* XXX: wersja protokolu (0x29, 0x2a, 0x2b) */ + uint32_t len; /* rozmiar sizeof(protocol)+sizeof(len)+sizeof(data) = 0x08 + sizeof(data) */ + /* char data[]; */ /* reszta danych */ +} GG_PACKED; + +struct gg_dcc7_voice_init_confirm { + uint8_t type; /* 0x05 */ + uint32_t id; /* id tego co potwierdzamy [0x1 - 0x5] */ +} GG_PACKED; + +#ifdef _WIN32 +#pragma pack(pop) +#endif + +#endif /* LIBGADU_PROTOCOL_H */
--- a/libpurple/protocols/gg/lib/pubdir.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/gg/lib/pubdir.c Tue Mar 09 21:41:41 2010 +0000 @@ -1,8 +1,9 @@ -/* $Id: pubdir.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: pubdir.c 502 2008-01-10 23:25:17Z wojtekka $ */ /* - * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl> + * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl> * Dawid Jarosz <dawjar@poczta.onet.pl> + * Adam Wysocki <gophi@ekg.chmurka.net> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -15,11 +16,18 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ +/** + * \file pubdir.c + * + * \brief Obsługa katalogu publicznego + */ + #include "libgadu.h" +#include "libgadu-config.h" #include <ctype.h> #include <errno.h> @@ -29,20 +37,20 @@ #include <string.h> #include <unistd.h> -/* - * gg_register3() +/** + * Rejestruje nowego użytkownika. * - * rozpoczyna rejestracj uytkownika protokoem GG 6.0. wymaga wczeniejszego - * pobrania tokenu za pomoc funkcji gg_token(). + * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token(). * - * - email - adres e-mail klienta - * - password - haso klienta - * - tokenid - identyfikator tokenu - * - tokenval - warto tokenu - * - async - poczenie asynchroniczne + * \param email Adres e-mail + * \param password Hasło + * \param tokenid Identyfikator tokenu + * \param tokenval Zawartość tokenu + * \param async Flaga połączenia asynchronicznego * - * zaalokowana struct gg_http, ktr poniej naley zwolni - * funkcj gg_register_free(), albo NULL jeli wystpi bd. + * \return Struktura \c gg_http lub \c NULL w przypadku błędu + * + * \ingroup register */ struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async) { @@ -121,19 +129,59 @@ return h; } -/* - * gg_unregister3() +#ifdef DOXYGEN + +/** + * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia. + * + * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE. + * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu + * znajdzie się w polu \c error. + * + * \note W rzeczywistości funkcja jest makrem rozwijanym do + * \c gg_pubdir_watch_fd(). * - * usuwa konto uytkownika z serwera protokoem GG 6.0 + * \param h Struktura połączenia + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup register + */ +int gg_register_watch_fd(struct gg_httpd *h) +{ + return gg_pubdir_watch_fd(h); +} + +/** + * Zwalnia zasoby po operacji. * - * - uin - numerek GG - * - password - haso klienta - * - tokenid - identyfikator tokenu - * - tokenval - warto tokenu - * - async - poczenie asynchroniczne + * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free(). + * + * \param h Struktura połączenia * - * zaalokowana struct gg_http, ktr poniej naley zwolni - * funkcj gg_unregister_free(), albo NULL jeli wystpi bd. + * \ingroup register + */ +void gg_register_free(struct gg_http *h) +{ + return gg_pubdir_free(h); +} + +#endif /* DOXYGEN */ + +/** + * Usuwa użytkownika. + * + * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token(). + * + * \param uin Numer Gadu-Gadu + * \param password Hasło + * \param tokenid Identyfikator tokenu + * \param tokenval Zawartość tokenu + * \param async Flaga połączenia asynchronicznego + * + * \return Struktura \c gg_http lub \c NULL w przypadku błędu + * + * \ingroup unregister */ struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async) { @@ -145,7 +193,7 @@ errno = EFAULT; return NULL; } - + __pwd = gg_saprintf("%ld", random()); __fmpwd = gg_urlencode(password); __tokenid = gg_urlencode(tokenid); @@ -210,22 +258,61 @@ return h; } -/* - * gg_change_passwd4() +#ifdef DOXYGEN + +/** + * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia. + * + * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE. + * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu + * znajdzie się w polu \c error. + * + * \note W rzeczywistości funkcja jest makrem rozwijanym do + * \c gg_pubdir_watch_fd(). + * + * \param h Struktura połączenia * - * wysya danie zmiany hasa zgodnie z protokoem GG 6.0. wymaga - * wczeniejszego pobrania tokenu za pomoc funkcji gg_token(). + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup unregister + */ +int gg_unregister_watch_fd(struct gg_httpd *h) +{ + return gg_pubdir_watch_fd(h); +} + +/** + * Zwalnia zasoby po operacji. + * + * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free(). * - * - uin - numer - * - email - adres e-mail - * - passwd - stare haso - * - newpasswd - nowe haso - * - tokenid - identyfikator tokenu - * - tokenval - warto tokenu - * - async - poczenie asynchroniczne + * \param h Struktura połączenia + * + * \ingroup unregister + */ +void gg_unregister_free(struct gg_http *h) +{ + return gg_pubdir_free(h); +} + +#endif /* DOXYGEN */ + +/** + * Zmienia hasło użytkownika. * - * zaalokowana struct gg_http, ktr poniej naley zwolni - * funkcj gg_change_passwd_free(), albo NULL jeli wystpi bd. + * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token(). + * + * \param uin Numer Gadu-Gadu + * \param email Adres e-mail + * \param passwd Obecne hasło + * \param newpasswd Nowe hasło + * \param tokenid Identyfikator tokenu + * \param tokenval Zawartość tokenu + * \param async Flaga połączenia asynchronicznego + * + * \return Struktura \c gg_http lub \c NULL w przypadku błędu + * + * \ingroup passwd */ struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async) { @@ -309,19 +396,59 @@ return h; } -/* - * gg_remind_passwd3() +#ifdef DOXYGEN + +/** + * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia. + * + * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE. + * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu + * znajdzie się w polu \c error. + * + * \note W rzeczywistości funkcja jest makrem rozwijanym do + * \c gg_pubdir_watch_fd(). * - * wysya danie przypomnienia hasa e-mailem. + * \param h Struktura połączenia + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup passwd + */ +int gg_change_passwd_watch_fd(struct gg_httpd *h) +{ + return gg_pubdir_watch_fd(h); +} + +/** + * Zwalnia zasoby po operacji. * - * - uin - numer - * - email - adres e-mail taki, jak ten zapisany na serwerze - * - async - poczenie asynchroniczne - * - tokenid - identyfikator tokenu - * - tokenval - warto tokenu + * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free(). + * + * \param h Struktura połączenia * - * zaalokowana struct gg_http, ktr poniej naley zwolni - * funkcj gg_remind_passwd_free(), albo NULL jeli wystpi bd. + * \ingroup passwd + */ +void gg_change_passwd_free(struct gg_http *h) +{ + return gg_pubdir_free(h); +} + +#endif /* DOXYGEN */ + +/** + * Wysyła hasło użytkownika na e-mail. + * + * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token(). + * + * \param uin Numer Gadu-Gadu + * \param email Adres e-mail (podany przy rejestracji) + * \param tokenid Identyfikator tokenu + * \param tokenval Zawartość tokenu + * \param async Flaga połączenia asynchronicznego + * + * \return Struktura \c gg_http lub \c NULL w przypadku błędu + * + * \ingroup remind */ struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async) { @@ -396,17 +523,55 @@ return h; } -/* - * gg_pubdir_watch_fd() +#ifdef DOXYGEN + +/** + * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia. * - * przy asynchronicznych operacjach na katalogu publicznym naley wywoywa - * t funkcj przy zmianach na obserwowanym deskryptorze. + * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE. + * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu + * znajdzie się w polu \c error. + * + * \note W rzeczywistości funkcja jest makrem rozwijanym do + * \c gg_pubdir_watch_fd(). + * + * \param h Struktura połączenia + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu * - * - h - struktura opisujca poczenie + * \ingroup remind + */ +int gg_remind_watch_fd(struct gg_httpd *h) +{ + return gg_pubdir_watch_fd(h); +} + +/** + * Zwalnia zasoby po operacji. + * + * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free(). + * + * \param h Struktura połączenia * - * jeli wszystko poszo dobrze to 0, inaczej -1. operacja bdzie - * zakoczona, jeli h->state == GG_STATE_DONE. jeli wystpi jaki - * bd, to bdzie tam GG_STATE_ERROR i odpowiedni kod bdu w h->error. + * \ingroup remind + */ +void gg_remind_free(struct gg_http *h) +{ + return gg_pubdir_free(h); +} + +#endif /* DOXYGEN */ + +/** + * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia. + * + * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE. + * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu + * znajdzie się w polu \c error. + * + * \param h Struktura połączenia + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu */ int gg_pubdir_watch_fd(struct gg_http *h) { @@ -447,7 +612,11 @@ gg_debug(GG_DEBUG_MISC, "=> pubdir, let's parse \"%s\"\n", h->body); - if ((tmp = strstr(h->body, "success")) || (tmp = strstr(h->body, "results"))) { + if ((tmp = strstr(h->body, "Tokens okregisterreply_packet.reg.dwUserId="))) { + p->success = 1; + p->uin = strtol(tmp + sizeof("Tokens okregisterreply_packet.reg.dwUserId=") - 1, NULL, 0); + gg_debug(GG_DEBUG_MISC, "=> pubdir, success (okregisterreply, uin=%d)\n", p->uin); + } else if ((tmp = strstr(h->body, "success")) || (tmp = strstr(h->body, "results"))) { p->success = 1; if (tmp[7] == ':') p->uin = strtol(tmp + 8, NULL, 0); @@ -458,12 +627,10 @@ return 0; } -/* - * gg_pubdir_free() +/** + * Zwalnia zasoby po operacji na katalogu publicznym. * - * zwalnia pami po efektach operacji na katalogu publicznym. - * - * - h - zwalniana struktura + * \param h Struktura połączenia */ void gg_pubdir_free(struct gg_http *h) { @@ -474,14 +641,17 @@ gg_http_free(h); } -/* - * gg_token() +/** + * Pobiera token do autoryzacji operacji na katalogu publicznym. + * + * Token jest niezbędny do tworzenia nowego i usuwania użytkownika, + * zmiany hasła itd. * - * pobiera z serwera token do autoryzacji zakadania konta, usuwania - * konta i zmiany hasa. + * \param async Flaga połączenia asynchronicznego * - * zaalokowana struct gg_http, ktr poniej naley zwolni - * funkcj gg_token_free(), albo NULL jeli wystpi bd. + * \return Struktura \c gg_http lub \c NULL w przypadku błędu + * + * \ingroup token */ struct gg_http *gg_token(int async) { @@ -511,17 +681,18 @@ return h; } -/* - * gg_token_watch_fd() - * - * przy asynchronicznych operacjach zwizanych z tokenem naley wywoywa - * t funkcj przy zmianach na obserwowanym deskryptorze. +/** + * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia. * - * - h - struktura opisujca poczenie + * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE. + * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu + * znajdzie się w polu \c error. * - * jeli wszystko poszo dobrze to 0, inaczej -1. operacja bdzie - * zakoczona, jeli h->state == GG_STATE_DONE. jeli wystpi jaki - * bd, to bdzie tam GG_STATE_ERROR i odpowiedni kod bdu w h->error. + * \param h Struktura połączenia + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup token */ int gg_token_watch_fd(struct gg_http *h) { @@ -547,8 +718,8 @@ if (h->state != GG_STATE_PARSING) return 0; - /* jeli h->data jest puste, to cigalimy tokenid i url do niego, - * ale jeli co tam jest, to znaczy, e mamy drugi etap polegajcy + /* jeśli h->data jest puste, to ściągaliśmy tokenid i url do niego, + * ale jeśli coś tam jest, to znaczy, że mamy drugi etap polegający * na pobieraniu tokenu. */ if (!h->data) { int width, height, length; @@ -573,8 +744,8 @@ return -1; } - /* dostalimy tokenid i wszystkie niezbdne informacje, - * wic pobierzmy obrazek z tokenem */ + /* dostaliśmy tokenid i wszystkie niezbędne informacje, + * więc pobierzmy obrazek z tokenem */ if (strncmp(url, "http://", 7)) { path = gg_saprintf("%s?tokenid=%s", url, tokenid); @@ -623,6 +794,8 @@ free(path); free(url); + gg_http_free_fields(h); + memcpy(h, h2, sizeof(struct gg_http)); free(h2); @@ -652,12 +825,12 @@ return 0; } -/* - * gg_token_free() +/** + * Zwalnia zasoby po operacji pobierania tokenu. * - * zwalnia pami po efektach pobierania tokenu. + * \param h Struktura połączenia * - * - h - zwalniana struktura + * \ingroup token */ void gg_token_free(struct gg_http *h) {
--- a/libpurple/protocols/gg/lib/pubdir50.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/gg/lib/pubdir50.c Tue Mar 09 21:41:41 2010 +0000 @@ -1,4 +1,4 @@ -/* $Id: pubdir50.c 16856 2006-08-19 01:13:25Z evands $ */ +/* $Id: pubdir50.c 854 2009-10-12 21:06:28Z wojtekka $ */ /* * (C) Copyright 2003 Wojtek Kaniewski <wojtekka@irc.pl> @@ -14,23 +14,32 @@ * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301, + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, * USA. */ -#include "libgadu.h" +/** + * \file pubdir50.c + * + * \brief Obsługa katalogu publicznego od wersji Gadu-Gadu 5.x + */ #include <errno.h> #include <stdlib.h> #include <string.h> #include <time.h> -/* - * gg_pubdir50_new() +#include "libgadu.h" +#include "libgadu-internal.h" + +/** + * Tworzy nowe zapytanie katalogu publicznego. * - * tworzy now zmienn typu gg_pubdir50_t. + * \param type Rodzaj zapytania * - * zaalokowana zmienna lub NULL w przypadku braku pamici. + * \return Zmienna \c gg_pubdir50_t lub \c NULL w przypadku błędu. + * + * \ingroup pubdir50 */ gg_pubdir50_t gg_pubdir50_new(int type) { @@ -50,17 +59,16 @@ return res; } -/* - * gg_pubdir50_add_n() // funkcja wewntrzna - * - * funkcja dodaje lub zastpuje istniejce pole do zapytania lub odpowiedzi. +/** + * \internal Dodaje lub zastępuje pole zapytania lub odpowiedzi katalogu + * publicznego. * - * - req - wskanik opisu zapytania, - * - num - numer wyniku (0 dla zapytania), - * - field - nazwa pola, - * - value - warto pola, + * \param req Zapytanie lub odpowiedź + * \param num Numer wyniku odpowiedzi (0 dla zapytania) + * \param field Nazwa pola + * \param value Wartość pola * - * 0/-1 + * \return 0 jeśli się powiodło, -1 w przypadku błędu */ static int gg_pubdir50_add_n(gg_pubdir50_t req, int num, const char *field, const char *value) { @@ -110,31 +118,31 @@ return 0; } -/* - * gg_pubdir50_add() - * - * funkcja dodaje pole do zapytania. +/** + * Dodaje pole zapytania. * - * - req - wskanik opisu zapytania, - * - field - nazwa pola, - * - value - warto pola, + * \param req Zapytanie + * \param field Nazwa pola + * \param value Wartość pola * - * 0/-1 + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup pubdir50 */ int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value) { return gg_pubdir50_add_n(req, 0, field, value); } -/* - * gg_pubdir50_seq_set() - * - * ustawia numer sekwencyjny zapytania. +/** + * Ustawia numer sekwencyjny zapytania. * - * - req - zapytanie, - * - seq - nowy numer sekwencyjny. + * \param req Zapytanie + * \param seq Numer sekwencyjny * - * 0/-1. + * \return 0 jeśli się powiodło, -1 w przypadku błędu + * + * \ingroup pubdir50 */ int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq) { @@ -151,12 +159,12 @@ return 0; } -/* - * gg_pubdir50_free() +/** + * Zwalnia zasoby po zapytaniu lub odpowiedzi katalogu publicznego. * - * zwalnia pami po zapytaniu lub rezultacie szukania uytkownika. + * \param s Zapytanie lub odpowiedź * - * - s - zwalniana zmienna, + * \ingroup pubdir50 */ void gg_pubdir50_free(gg_pubdir50_t s) { @@ -174,15 +182,15 @@ free(s); } -/* - * gg_pubdir50() - * - * wysya zapytanie katalogu publicznego do serwera. +/** + * Wysyła zapytanie katalogu publicznego do serwera. * - * - sess - sesja, - * - req - zapytanie. + * \param sess Struktura sesji + * \param req Zapytanie * - * numer sekwencyjny wyszukiwania lub 0 w przypadku bdu. + * \return Numer sekwencyjny zapytania lub 0 w przypadku błędu + * + * \ingroup pubdir50 */ uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req) { @@ -191,16 +199,16 @@ char *buf, *p; struct gg_pubdir50_request *r; - gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req); + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req); if (!sess || !req) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n"); errno = EFAULT; return 0; } if (sess->state != GG_STATE_CONNECTED) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() not connected\n"); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() not connected\n"); errno = ENOTCONN; return 0; } @@ -210,30 +218,81 @@ if (req->entries[i].num) continue; - size += strlen(req->entries[i].field) + 1; - size += strlen(req->entries[i].value) + 1; + if (sess->encoding == GG_ENCODING_CP1250) { + size += strlen(req->entries[i].field) + 1; + size += strlen(req->entries[i].value) + 1; + } else { + char *tmp; + + tmp = gg_utf8_to_cp(req->entries[i].field); + + if (tmp == NULL) + return -1; + + size += strlen(tmp) + 1; + + free(tmp); + + tmp = gg_utf8_to_cp(req->entries[i].value); + + if (tmp == NULL) + return -1; + + size += strlen(tmp) + 1; + + free(tmp); + } } if (!(buf = malloc(size))) { - gg_debug(GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size); + gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size); return 0; } + if (!req->seq) + req->seq = time(NULL); + + res = req->seq; + r = (struct gg_pubdir50_request*) buf; - res = time(NULL); r->type = req->type; - r->seq = (req->seq) ? gg_fix32(req->seq) : gg_fix32(time(NULL)); - req->seq = gg_fix32(r->seq); + r->seq = gg_fix32(req->seq); for (i = 0, p = buf + 5; i < req->entries_count; i++) { if (req->entries[i].num) continue; - strcpy(p, req->entries[i].field); - p += strlen(p) + 1; + if (sess->encoding == GG_ENCODING_CP1250) { + strcpy(p, req->entries[i].field); + p += strlen(p) + 1; + + strcpy(p, req->entries[i].value); + p += strlen(p) + 1; + } else { + char *tmp; + + tmp = gg_utf8_to_cp(req->entries[i].field); + + if (tmp == NULL) { + free(buf); + return -1; + } - strcpy(p, req->entries[i].value); - p += strlen(p) + 1; + strcpy(p, tmp); + p += strlen(tmp) + 1; + free(tmp); + + tmp = gg_utf8_to_cp(req->entries[i].value); + + if (tmp == NULL) { + free(buf); + return -1; + } + + strcpy(p, tmp); + p += strlen(tmp) + 1; + free(tmp); + } } if (gg_send_packet(sess, GG_PUBDIR50_REQUEST, buf, size, NULL, 0) == -1) @@ -245,26 +304,26 @@ } /* - * gg_pubdir50_handle_reply() // funkcja wewntrzna - * - * analizuje przychodzcy pakiet odpowiedzi i zapisuje wynik w struct gg_event. + * \internal Analizuje przychodzący pakiet odpowiedzi i zapisuje wynik + * w strukturze \c gg_event. * - * - e - opis zdarzenia - * - packet - zawarto pakietu odpowiedzi - * - length - dugo pakietu odpowiedzi + * \param sess Struktura sesji + * \param e Struktura zdarzenia + * \param packet Pakiet odpowiedzi + * \param length Długość pakietu odpowiedzi * - * 0/-1 + * \return 0 jeśli się powiodło, -1 w przypadku błędu */ -int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length) +int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length) { const char *end = packet + length, *p; struct gg_pubdir50_reply *r = (struct gg_pubdir50_reply*) packet; gg_pubdir50_t res; int num = 0; - gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply(%p, %p, %d);\n", e, packet, length); + gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_handle_reply_sess(%p, %p, %p, %d);\n", sess, e, packet, length); - if (!e || !packet) { + if (!sess || !e || !packet) { gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() invalid arguments\n"); errno = EFAULT; return -1; @@ -299,11 +358,11 @@ break; } - /* brak wynikw? */ + /* brak wyników? */ if (length == 5) return 0; - /* pomi pocztek odpowiedzi */ + /* pomiń początek odpowiedzi */ p = packet + 5; while (p < end) { @@ -311,7 +370,7 @@ field = p; - /* sprawd, czy nie mamy podziau na kolejne pole */ + /* sprawdź, czy nie mamy podziału na kolejne pole */ if (!*field) { num++; field++; @@ -320,22 +379,22 @@ value = NULL; for (p = field; p < end; p++) { - /* jeli mamy koniec tekstu... */ + /* jeśli mamy koniec tekstu... */ if (!*p) { - /* ...i jeszcze nie mielimy wartoci pola to - * wiemy, e po tym zerze jest warto... */ + /* ...i jeszcze nie mieliśmy wartości pola to + * wiemy, że po tym zerze jest wartość... */ if (!value) value = p + 1; else /* ...w przeciwym wypadku koniec - * wartoci i moemy wychodzi - * grzecznie z ptli */ + * wartości i możemy wychodzić + * grzecznie z pętli */ break; } } - /* sprawdmy, czy pole nie wychodzi poza pakiet, eby nie - * mie segfaultw, jeli serwer przestanie zakacza pakietw + /* sprawdźmy, czy pole nie wychodzi poza pakiet, żeby nie + * mieć segfaultów, jeśli serwer przestanie zakańczać pakietów * przez \0 */ if (p == end) { @@ -345,14 +404,30 @@ p++; - /* jeli dostalimy namier na nastpne wyniki, to znaczy e - * mamy koniec wynikw i nie jest to kolejna osoba. */ + /* jeśli dostaliśmy namier na następne wyniki, to znaczy że + * mamy koniec wyników i nie jest to kolejna osoba. */ if (!strcasecmp(field, "nextstart")) { res->next = atoi(value); num--; } else { - if (gg_pubdir50_add_n(res, num, field, value) == -1) - goto failure; + if (sess->encoding == GG_ENCODING_CP1250) { + if (gg_pubdir50_add_n(res, num, field, value) == -1) + goto failure; + } else { + char *tmp; + + tmp = gg_cp_to_utf8(value); + + if (tmp == NULL) + goto failure; + + if (gg_pubdir50_add_n(res, num, field, tmp) == -1) { + free(tmp); + goto failure; + } + + free(tmp); + } } } @@ -365,16 +440,16 @@ return -1; } -/* - * gg_pubdir50_get() - * - * pobiera informacj z rezultatu wyszukiwania. +/** + * Pobiera pole z odpowiedzi katalogu publicznego. * - * - res - rezultat wyszukiwania, - * - num - numer odpowiedzi, - * - field - nazwa pola (wielko liter nie ma znaczenia). + * \param res Odpowiedź + * \param num Numer wyniku odpowiedzi + * \param field Nazwa pola (wielkość liter nie ma znaczenia) * - * warto pola lub NULL, jeli nie znaleziono. + * \return Wartość pola lub \c NULL jeśli nie znaleziono + * + * \ingroup pubdir50 */ const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field) { @@ -399,57 +474,61 @@ return value; } -/* - * gg_pubdir50_count() +/** + * Zwraca liczbę wyników odpowiedzi. * - * zwraca ilo wynikw danego zapytania. + * \param res Odpowiedź * - * - res - odpowied + * \return Liczba wyników lub -1 w przypadku błędu * - * ilo lub -1 w przypadku bdu. + * \ingroup pubdir50 */ int gg_pubdir50_count(gg_pubdir50_t res) { return (!res) ? -1 : res->count; } -/* - * gg_pubdir50_type() +/** + * Zwraca rodzaj zapytania lub odpowiedzi. * - * zwraca rodzaj zapytania lub odpowiedzi. + * \param res Zapytanie lub odpowiedź * - * - res - zapytanie lub odpowied + * \return Rodzaj lub -1 w przypadku błędu * - * ilo lub -1 w przypadku bdu. + * \ingroup pubdir50 */ int gg_pubdir50_type(gg_pubdir50_t res) { return (!res) ? -1 : res->type; } -/* - * gg_pubdir50_next() +/** + * Zwraca numer, od którego należy rozpocząc kolejne wyszukiwanie. * - * zwraca numer, od ktrego naley rozpocz kolejne wyszukiwanie, jeli - * zaley nam na kolejnych wynikach. + * Dłuższe odpowiedzi katalogu publicznego są wysyłane przez serwer + * w mniejszych paczkach. Po otrzymaniu odpowiedzi, jeśli numer kolejnego + * wyszukiwania jest większy od zera, dalsze wyniki można otrzymać przez + * wywołanie kolejnego zapytania z określonym numerem początkowym. * - * - res - odpowied + * \param res Odpowiedź * - * numer lub -1 w przypadku bdu. + * \return Numer lub -1 w przypadku błędu + * + * \ingroup pubdir50 */ uin_t gg_pubdir50_next(gg_pubdir50_t res) { return (!res) ? (unsigned) -1 : res->next; } -/* - * gg_pubdir50_seq() +/** + * Zwraca numer sekwencyjny zapytania lub odpowiedzi. * - * zwraca numer sekwencyjny zapytania lub odpowiedzi. + * \param res Zapytanie lub odpowiedź * - * - res - zapytanie lub odpowied + * \return Numer sekwencyjny lub -1 w przypadku błędu * - * numer lub -1 w przypadku bdu. + * \ingroup pubdir50 */ uint32_t gg_pubdir50_seq(gg_pubdir50_t res) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/resolver.c Tue Mar 09 21:41:41 2010 +0000 @@ -0,0 +1,1065 @@ +/* $Id$ */ + +/* + * (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl> + * Robert J. Woźny <speedy@ziew.org> + * Arkadiusz Miśkiewicz <arekm@pld-linux.org> + * Tomasz Chiliński <chilek@chilan.com> + * Adam Wysocki <gophi@ekg.chmurka.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/** + * \file resolver.c + * + * \brief Funkcje rozwiązywania nazw + */ + +#ifndef _WIN32 +# include <sys/wait.h> +# include <netdb.h> +# include <signal.h> +# include <netinet/in.h> +# include <arpa/inet.h> +#endif + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "libgadu.h" +#include "resolver.h" +#include "compat.h" + +/** Sposób rozwiązywania nazw serwerów */ +static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT; + +/** Funkcja rozpoczynająca rozwiązywanie nazwy */ +static int (*gg_global_resolver_start)(int *fd, void **private_data, const char *hostname); + +/** Funkcja zwalniająca zasoby po rozwiązaniu nazwy */ +static void (*gg_global_resolver_cleanup)(void **private_data, int force); + +#ifdef GG_CONFIG_HAVE_PTHREAD + +#include <pthread.h> + +/** + * \internal Funkcja pomocnicza zwalniająca zasoby po rozwiązywaniu nazwy + * w wątku. + * + * \param data Wskaźnik na wskaźnik bufora zaalokowanego w wątku + */ +static void gg_gethostbyname_cleaner(void *data) +{ + char **buf_ptr = (char**) data; + + if (buf_ptr != NULL) { + free(*buf_ptr); + *buf_ptr = NULL; + } +} + +#endif /* GG_CONFIG_HAVE_PTHREAD */ + +/** + * \internal Odpowiednik \c gethostbyname zapewniający współbieżność. + * + * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli + * nie, to zwykłej \c gethostbyname. + * + * \param hostname Nazwa serwera + * \param addr Wskaźnik na rezultat rozwiązywania nazwy + * \param pthread Flaga blokowania unicestwiania wątku podczas alokacji pamięci + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +int gg_gethostbyname_real(const char *hostname, struct in_addr *addr, int pthread) +{ +#ifdef GG_CONFIG_HAVE_GETHOSTBYNAME_R + char *buf = NULL; + char *new_buf = NULL; + struct hostent he; + struct hostent *he_ptr = NULL; + size_t buf_len = 1024; + int result = -1; + int h_errnop; + int ret = 0; +#ifdef GG_CONFIG_HAVE_PTHREAD + int old_state; +#endif + +#ifdef GG_CONFIG_HAVE_PTHREAD + pthread_cleanup_push(gg_gethostbyname_cleaner, &buf); + + if (pthread) + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); +#endif + + buf = malloc(buf_len); + +#ifdef GG_CONFIG_HAVE_PTHREAD + if (pthread) + pthread_setcancelstate(old_state, NULL); +#endif + + if (buf != NULL) { +#ifndef sun + while ((ret = gethostbyname_r(hostname, &he, buf, buf_len, &he_ptr, &h_errnop)) == ERANGE) { +#else + while (((he_ptr = gethostbyname_r(hostname, &he, buf, buf_len, &h_errnop)) == NULL) && (errno == ERANGE)) { +#endif + buf_len *= 2; + +#ifdef GG_CONFIG_HAVE_PTHREAD + if (pthread) + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); +#endif + + new_buf = realloc(buf, buf_len); + + if (new_buf != NULL) + buf = new_buf; + +#ifdef GG_CONFIG_HAVE_PTHREAD + if (pthread) + pthread_setcancelstate(old_state, NULL); +#endif + + if (new_buf == NULL) { + ret = ENOMEM; + break; + } + } + + if (ret == 0 && he_ptr != NULL) { + memcpy(addr, he_ptr->h_addr, sizeof(struct in_addr)); + result = 0; + } + +#ifdef GG_CONFIG_HAVE_PTHREAD + if (pthread) + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); +#endif + + free(buf); + buf = NULL; + +#ifdef GG_CONFIG_HAVE_PTHREAD + if (pthread) + pthread_setcancelstate(old_state, NULL); +#endif + } + +#ifdef GG_CONFIG_HAVE_PTHREAD + pthread_cleanup_pop(1); +#endif + + return result; +#else + struct hostent *he; + + he = gethostbyname(hostname); + + if (he == NULL) + return -1; + + memcpy(addr, he->h_addr, sizeof(struct in_addr)); + + return 0; +#endif /* GG_CONFIG_HAVE_GETHOSTBYNAME_R */ +} + +/** + * \internal Odpowiednik \c gethostbyname zapewniający współbieżność. + * + * Jeśli dany system dostarcza \c gethostbyname_r, używa się tej wersji, jeśli + * nie, to zwykłej \c gethostbyname. + * + * \param hostname Nazwa serwera + * + * \return Zaalokowana struktura \c in_addr lub NULL w przypadku błędu. + */ +struct in_addr *gg_gethostbyname(const char *hostname) +{ + struct in_addr *addr; + + if (!(addr = malloc(sizeof(struct in_addr)))) + return NULL; + + if (gg_gethostbyname_real(hostname, addr, 0)) { + free(addr); + return NULL; + } + return addr; +} + +/** + * \internal Struktura przekazywana do wątku rozwiązującego nazwę. + */ +struct gg_resolver_fork_data { + int pid; /*< Identyfikator procesu */ +}; + + + +#ifdef _WIN32 +/** + * Deal with the fact that you can't select() on a win32 file fd. + * This makes it practically impossible to tie into purple's event loop. + * + * -This is thanks to Tor Lillqvist. + * XXX - Move this to where the rest of the the win32 compatiblity stuff goes when we push the changes back to libgadu. + */ +static int +socket_pipe (int *fds) +{ + SOCKET temp, socket1 = -1, socket2 = -1; + struct sockaddr_in saddr; + int len; + u_long arg; + fd_set read_set, write_set; + struct timeval tv; + + temp = socket(AF_INET, SOCK_STREAM, 0); + + if (temp == INVALID_SOCKET) { + goto out0; + } + + arg = 1; + if (ioctlsocket(temp, FIONBIO, &arg) == SOCKET_ERROR) { + goto out0; + } + + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = AF_INET; + saddr.sin_port = 0; + saddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + + if (bind(temp, (struct sockaddr *)&saddr, sizeof (saddr))) { + goto out0; + } + + if (listen(temp, 1) == SOCKET_ERROR) { + goto out0; + } + + len = sizeof(saddr); + if (getsockname(temp, (struct sockaddr *)&saddr, &len)) { + goto out0; + } + + socket1 = socket(AF_INET, SOCK_STREAM, 0); + + if (socket1 == INVALID_SOCKET) { + goto out0; + } + + arg = 1; + if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { + goto out1; + } + + if (connect(socket1, (struct sockaddr *)&saddr, len) != SOCKET_ERROR || + WSAGetLastError() != WSAEWOULDBLOCK) { + goto out1; + } + + FD_ZERO(&read_set); + FD_SET(temp, &read_set); + + tv.tv_sec = 0; + tv.tv_usec = 0; + + if (select(0, &read_set, NULL, NULL, NULL) == SOCKET_ERROR) { + goto out1; + } + + if (!FD_ISSET(temp, &read_set)) { + goto out1; + } + + socket2 = accept(temp, (struct sockaddr *) &saddr, &len); + if (socket2 == INVALID_SOCKET) { + goto out1; + } + + FD_ZERO(&write_set); + FD_SET(socket1, &write_set); + + tv.tv_sec = 0; + tv.tv_usec = 0; + + if (select(0, NULL, &write_set, NULL, NULL) == SOCKET_ERROR) { + goto out2; + } + + if (!FD_ISSET(socket1, &write_set)) { + goto out2; + } + + arg = 0; + if (ioctlsocket(socket1, FIONBIO, &arg) == SOCKET_ERROR) { + goto out2; + } + + arg = 0; + if (ioctlsocket(socket2, FIONBIO, &arg) == SOCKET_ERROR) { + goto out2; + } + + fds[0] = socket1; + fds[1] = socket2; + + closesocket (temp); + + return 0; + +out2: + closesocket (socket2); +out1: + closesocket (socket1); +out0: + closesocket (temp); + errno = EIO; /* XXX */ + + return -1; +} +#endif + + + +#ifdef _WIN32 +struct gg_resolve_win32thread_data { + char *hostname; + int fd; +}; + +static DWORD WINAPI gg_resolve_win32thread_thread(LPVOID arg) +{ + struct gg_resolve_win32thread_data *d = arg; + struct in_addr a; + + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() host: %s, fd: %i called\n", d->hostname, d->fd); + + if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { + /* W przypadku błędu gg_gethostbyname_real() zwróci -1 + * i nie zmieni &addr. Tam jest już INADDR_NONE, + * więc nie musimy robić nic więcej. */ + gg_gethostbyname_real(d->hostname, &a, 0); + } + + // if ((a.s_addr = inet_addr(d->hostname)) == INADDR_NONE) { + // struct in_addr *hn; + + // if (!(hn = gg_gethostbyname(d->hostname))) + // a.s_addr = INADDR_NONE; + // else { + // a.s_addr = hn->s_addr; + // free(hn); + // } + // } + + write(d->fd, &a, sizeof(a)); + close(d->fd); + + free(d->hostname); + d->hostname = NULL; + + free(d); + + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_thread() done\n"); + + return 0; +} + + +static int gg_resolve_win32thread(int *fd, void **resolver, const char *hostname) +{ + struct gg_resolve_win32thread_data *d = NULL; + HANDLE h; + DWORD dwTId; + int pipes[2], new_errno; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_resolve_win32thread(%p, %p, \"%s\");\n", fd, resolver, hostname); + + if (!resolver || !fd || !hostname) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() invalid arguments\n"); + errno = EFAULT; + return -1; + } + + if (socket_pipe(pipes) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); + return -1; + } + + if (!(d = malloc(sizeof(*d)))) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); + new_errno = errno; + goto cleanup; + } + + d->hostname = NULL; + + if (!(d->hostname = strdup(hostname))) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() out of memory\n"); + new_errno = errno; + goto cleanup; + } + + d->fd = pipes[1]; + + h = CreateThread(NULL, 0, gg_resolve_win32thread_thread, + d, 0, &dwTId); + + if (h == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() unable to create thread\n"); + new_errno = errno; + goto cleanup; + } + + *resolver = h; + *fd = pipes[0]; + + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread() done\n"); + + return 0; + +cleanup: + if (d) { + free(d->hostname); + free(d); + } + + close(pipes[0]); + close(pipes[1]); + + errno = new_errno; + + return -1; + +} + +static void gg_resolve_win32thread_cleanup(void **priv_data, int force) +{ + struct gg_resolve_win32thread_data *data; + + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() force: %i called\n", force); + + if (priv_data == NULL || *priv_data == NULL) + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() priv_data: NULL\n"); + return; + + data = (struct gg_resolve_win32thread_data*) *priv_data; + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() data: %s called\n", data->hostname); + *priv_data = NULL; + + if (force) { + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() force called\n", force); + //pthread_cancel(data->thread); + //pthread_join(data->thread, NULL); + } + + free(data->hostname); + data->hostname = NULL; + + if (data->fd != -1) { + close(data->fd); + data->fd = -1; + } + gg_debug(GG_DEBUG_MISC, "// gg_resolve_win32thread_cleanup() done\n"); + free(data); +} +#endif + +#ifndef _WIN32 +/** + * \internal Rozwiązuje nazwę serwera w osobnym procesie. + * + * Połączenia asynchroniczne nie mogą blokować procesu w trakcie rozwiązywania + * nazwy serwera. W tym celu tworzony jest potok, nowy proces i dopiero w nim + * przeprowadzane jest rozwiązywanie nazwy. Deskryptor strony do odczytu + * zapisuje się w strukturze sieci i czeka na dane w postaci struktury + * \c in_addr. Jeśli nie znaleziono nazwy, zwracana jest \c INADDR_NONE. + * + * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor + * potoku + * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik + * do numeru procesu potomnego rozwiązującego nazwę + * \param hostname Nazwa serwera do rozwiązania + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +static int gg_resolver_fork_start(int *fd, void **priv_data, const char *hostname) +{ + struct gg_resolver_fork_data *data = NULL; + struct in_addr addr; + int pipes[2], new_errno; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_fork_start(%p, %p, \"%s\");\n", fd, priv_data, hostname); + + if (fd == NULL || priv_data == NULL || hostname == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() invalid arguments\n"); + errno = EFAULT; + return -1; + } + + data = malloc(sizeof(struct gg_resolver_fork_data)); + + if (data == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() out of memory for resolver data\n"); + return -1; + } + + if (pipe(pipes) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); + free(data); + return -1; + } + + data->pid = fork(); + + if (data->pid == -1) { + new_errno = errno; + goto cleanup; + } + + if (data->pid == 0) { + close(pipes[0]); + + if ((addr.s_addr = inet_addr(hostname)) == INADDR_NONE) { + /* W przypadku błędu gg_gethostbyname_real() zwróci -1 + * i nie zmieni &addr. Tam jest już INADDR_NONE, + * więc nie musimy robić nic więcej. */ + gg_gethostbyname_real(hostname, &addr, 0); + } + + if (write(pipes[1], &addr, sizeof(addr)) != sizeof(addr)) + exit(1); + + exit(0); + } + + close(pipes[1]); + + gg_debug(GG_DEBUG_MISC, "// gg_resolver_fork_start() %p\n", data); + + *fd = pipes[0]; + *priv_data = data; + + return 0; + +cleanup: + free(data); + close(pipes[0]); + close(pipes[1]); + + errno = new_errno; + + return -1; +} + +/** + * \internal Usuwanie zasobów po procesie rozwiązywaniu nazwy. + * + * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu + * zasobów sesji podczas rozwiązywania nazwy. + * + * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych + * danych + * \param force Flaga usuwania zasobów przed zakończeniem działania + */ +void gg_resolver_fork_cleanup(void **priv_data, int force) +{ + struct gg_resolver_fork_data *data; + + if (priv_data == NULL || *priv_data == NULL) + return; + + data = (struct gg_resolver_fork_data*) *priv_data; + *priv_data = NULL; + + if (force) + kill(data->pid, SIGKILL); + + waitpid(data->pid, NULL, WNOHANG); + + free(data); +} +#endif + +#ifdef GG_CONFIG_HAVE_PTHREAD + +/** + * \internal Struktura przekazywana do wątku rozwiązującego nazwę. + */ +struct gg_resolver_pthread_data { + pthread_t thread; /*< Identyfikator wątku */ + char *hostname; /*< Nazwa serwera */ + int rfd; /*< Deskryptor do odczytu */ + int wfd; /*< Deskryptor do zapisu */ +}; + +/** + * \internal Usuwanie zasobów po wątku rozwiązywaniu nazwy. + * + * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu + * zasobów sesji podczas rozwiązywania nazwy. + * + * \param priv_data Wskaźnik na zmienną przechowującą wskaźnik do prywatnych + * danych + * \param force Flaga usuwania zasobów przed zakończeniem działania + */ +static void gg_resolver_pthread_cleanup(void **priv_data, int force) +{ + struct gg_resolver_pthread_data *data; + + if (priv_data == NULL || *priv_data == NULL) + return; + + data = (struct gg_resolver_pthread_data *) *priv_data; + *priv_data = NULL; + + if (force) { + pthread_cancel(data->thread); + pthread_join(data->thread, NULL); + } + + free(data->hostname); + data->hostname = NULL; + + if (data->wfd != -1) { + close(data->wfd); + data->wfd = -1; + } + + free(data); +} + +/** + * \internal Wątek rozwiązujący nazwę. + * + * \param arg Wskaźnik na strukturę \c gg_resolver_pthread_data + */ +static void *gg_resolver_pthread_thread(void *arg) +{ + struct gg_resolver_pthread_data *data = arg; + struct in_addr addr; + + pthread_detach(pthread_self()); + + if ((addr.s_addr = inet_addr(data->hostname)) == INADDR_NONE) { + /* W przypadku błędu gg_gethostbyname_real() zwróci -1 + * i nie zmieni &addr. Tam jest już INADDR_NONE, + * więc nie musimy robić nic więcej. */ + gg_gethostbyname_real(data->hostname, &addr, 1); + } + + if (write(data->wfd, &addr, sizeof(addr)) == sizeof(addr)) + pthread_exit(NULL); + else + pthread_exit((void*) -1); + + return NULL; /* żeby kompilator nie marudził */ +} + +/** + * \internal Rozwiązuje nazwę serwera w osobnym wątku. + * + * Funkcja działa analogicznie do \c gg_resolver_fork_start(), z tą różnicą, + * że działa na wątkach, nie procesach. Jest dostępna wyłącznie gdy podczas + * kompilacji włączono odpowiednią opcję. + * + * \param fd Wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor + * potoku + * \param priv_data Wskaźnik na zmienną, gdzie zostanie umieszczony wskaźnik + * do prywatnych danych wątku rozwiązującego nazwę + * \param hostname Nazwa serwera do rozwiązania + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +static int gg_resolver_pthread_start(int *fd, void **priv_data, const char *hostname) +{ + struct gg_resolver_pthread_data *data = NULL; + int pipes[2], new_errno; + + gg_debug(GG_DEBUG_FUNCTION, "** gg_resolver_pthread_start(%p, %p, \"%s\");\n", fd, priv_data, hostname); + + if (fd == NULL || priv_data == NULL || hostname == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() invalid arguments\n"); + errno = EFAULT; + return -1; + } + + data = malloc(sizeof(struct gg_resolver_pthread_data)); + + if (data == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory for resolver data\n"); + return -1; + } + + if (pipe(pipes) == -1) { + gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable to create pipes (errno=%d, %s)\n", errno, strerror(errno)); + free(data); + return -1; + } + + data->hostname = strdup(hostname); + + if (data->hostname == NULL) { + gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() out of memory\n"); + new_errno = errno; + goto cleanup; + } + + data->rfd = pipes[0]; + data->wfd = pipes[1]; + + if (pthread_create(&data->thread, NULL, gg_resolver_pthread_thread, data)) { + gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() unable to create thread\n"); + new_errno = errno; + goto cleanup; + } + + gg_debug(GG_DEBUG_MISC, "// gg_resolver_pthread_start() %p\n", data); + + *fd = pipes[0]; + *priv_data = data; + + return 0; + +cleanup: + if (data) { + free(data->hostname); + free(data); + } + + close(pipes[0]); + close(pipes[1]); + + errno = new_errno; + + return -1; +} + +#endif /* GG_CONFIG_HAVE_PTHREAD */ + +/** + * Ustawia sposób rozwiązywania nazw w sesji. + * + * \param gs Struktura sesji + * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver) + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type) +{ + if (gs == NULL) { + errno = EINVAL; + return -1; + } + + if (type == GG_RESOLVER_DEFAULT) { + if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) { + gs->resolver_type = gg_global_resolver_type; + gs->resolver_start = gg_global_resolver_start; + gs->resolver_cleanup = gg_global_resolver_cleanup; + return 0; + } + +#ifdef _WIN32 + type = GG_RESOLVER_WIN32; +#else + type = GG_RESOLVER_FORK; +#endif + +#if defined(GG_CONFIG_HAVE_PTHREAD) || defined(GG_CONFIG_PTHREAD_DEFAULT) + type = GG_RESOLVER_PTHREAD; +#endif + } + + switch (type) { +#ifdef _WIN32 + case GG_RESOLVER_WIN32: + gs->resolver_type = type; + gs->resolver_start = gg_resolve_win32thread; + gs->resolver_cleanup = gg_resolve_win32thread_cleanup; + return 0; +#else + case GG_RESOLVER_FORK: + gs->resolver_type = type; + gs->resolver_start = gg_resolver_fork_start; + gs->resolver_cleanup = gg_resolver_fork_cleanup; + return 0; +#endif + +#ifdef GG_CONFIG_HAVE_PTHREAD + case GG_RESOLVER_PTHREAD: + gs->resolver_type = type; + gs->resolver_start = gg_resolver_pthread_start; + gs->resolver_cleanup = gg_resolver_pthread_cleanup; + return 0; +#endif + + default: + errno = EINVAL; + return -1; + } +} + +/** + * Zwraca sposób rozwiązywania nazw w sesji. + * + * \param gs Struktura sesji + * + * \return Sposób rozwiązywania nazw + */ +gg_resolver_t gg_session_get_resolver(struct gg_session *gs) +{ + if (gs == NULL) { + errno = EINVAL; + return GG_RESOLVER_INVALID; + } + + return gs->resolver_type; +} + +/** + * Ustawia własny sposób rozwiązywania nazw w sesji. + * + * \param gs Struktura sesji + * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy + * \param resolver_cleanup Funkcja zwalniająca zasoby + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int)) +{ + if (gs == NULL || resolver_start == NULL || resolver_cleanup == NULL) { + errno = EINVAL; + return -1; + } + + gs->resolver_type = GG_RESOLVER_CUSTOM; + gs->resolver_start = resolver_start; + gs->resolver_cleanup = resolver_cleanup; + + return 0; +} + +/** + * Ustawia sposób rozwiązywania nazw połączenia HTTP. + * + * \param gh Struktura połączenia + * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver) + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +int gg_http_set_resolver(struct gg_http *gh, gg_resolver_t type) +{ + if (gh == NULL) { + errno = EINVAL; + return -1; + } + + if (type == GG_RESOLVER_DEFAULT) { + if (gg_global_resolver_type != GG_RESOLVER_DEFAULT) { + gh->resolver_type = gg_global_resolver_type; + gh->resolver_start = gg_global_resolver_start; + gh->resolver_cleanup = gg_global_resolver_cleanup; + return 0; + } + +#ifdef _WIN32 + type = GG_RESOLVER_WIN32; +#else + type = GG_RESOLVER_FORK; +#endif + +#if defined(GG_CONFIG_HAVE_PTHREAD) || defined(GG_CONFIG_PTHREAD_DEFAULT) + type = GG_RESOLVER_PTHREAD; +#endif + } + + switch (type) { +#ifdef _WIN32 + case GG_RESOLVER_WIN32: + gh->resolver_type = type; + gh->resolver_start = gg_resolve_win32thread; + gh->resolver_cleanup = gg_resolve_win32thread_cleanup; + return 0; +#else + case GG_RESOLVER_FORK: + gh->resolver_type = type; + gh->resolver_start = gg_resolver_fork_start; + gh->resolver_cleanup = gg_resolver_fork_cleanup; + return 0; +#endif + +#ifdef GG_CONFIG_HAVE_PTHREAD + case GG_RESOLVER_PTHREAD: + gh->resolver_type = type; + gh->resolver_start = gg_resolver_pthread_start; + gh->resolver_cleanup = gg_resolver_pthread_cleanup; + return 0; +#endif + + default: + errno = EINVAL; + return -1; + } +} + +/** + * Zwraca sposób rozwiązywania nazw połączenia HTTP. + * + * \param gh Struktura połączenia + * + * \return Sposób rozwiązywania nazw + */ +gg_resolver_t gg_http_get_resolver(struct gg_http *gh) +{ + if (gh == NULL) { + errno = EINVAL; + return GG_RESOLVER_INVALID; + } + + return gh->resolver_type; +} + +/** + * Ustawia własny sposób rozwiązywania nazw połączenia HTTP. + * + * \param gh Struktura sesji + * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy + * \param resolver_cleanup Funkcja zwalniająca zasoby + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +int gg_http_set_custom_resolver(struct gg_http *gh, int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int)) +{ + if (gh == NULL || resolver_start == NULL || resolver_cleanup == NULL) { + errno = EINVAL; + return -1; + } + + gh->resolver_type = GG_RESOLVER_CUSTOM; + gh->resolver_start = resolver_start; + gh->resolver_cleanup = resolver_cleanup; + + return 0; +} + +/** + * Ustawia sposób rozwiązywania nazw globalnie dla biblioteki. + * + * \param type Sposób rozwiązywania nazw (patrz \ref build-resolver) + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +int gg_global_set_resolver(gg_resolver_t type) +{ + switch (type) { + case GG_RESOLVER_DEFAULT: + gg_global_resolver_type = type; + gg_global_resolver_start = NULL; + gg_global_resolver_cleanup = NULL; + return 0; + +#ifndef _WIN32 + case GG_RESOLVER_FORK: + gg_global_resolver_type = type; + gg_global_resolver_start = gg_resolver_fork_start; + gg_global_resolver_cleanup = gg_resolver_fork_cleanup; + return 0; +#endif + +#ifdef _WIN32 + case GG_RESOLVER_WIN32: + gg_global_resolver_type = type; + gg_global_resolver_start = gg_resolve_win32thread; + gg_global_resolver_cleanup = gg_resolve_win32thread_cleanup; + return 0; +#endif + +#ifdef GG_CONFIG_HAVE_PTHREAD + case GG_RESOLVER_PTHREAD: + gg_global_resolver_type = type; + gg_global_resolver_start = gg_resolver_pthread_start; + gg_global_resolver_cleanup = gg_resolver_pthread_cleanup; + return 0; +#endif + + default: + errno = EINVAL; + return -1; + } +} + +/** + * Zwraca sposób rozwiązywania nazw globalnie dla biblioteki. + * + * \return Sposób rozwiązywania nazw + */ +gg_resolver_t gg_global_get_resolver(void) +{ + return gg_global_resolver_type; +} + +/** + * Ustawia własny sposób rozwiązywania nazw globalnie dla biblioteki. + * + * \param resolver_start Funkcja rozpoczynająca rozwiązywanie nazwy + * \param resolver_cleanup Funkcja zwalniająca zasoby + * + * Parametry funkcji rozpoczynającej rozwiązywanie nazwy wyglądają następująco: + * - \c "int *fd" — wskaźnik na zmienną, gdzie zostanie umieszczony deskryptor potoku + * - \c "void **priv_data" — wskaźnik na zmienną, gdzie można umieścić wskaźnik do prywatnych danych na potrzeby rozwiązywania nazwy + * - \c "const char *name" — nazwa serwera do rozwiązania + * + * Parametry funkcji zwalniającej zasoby wyglądają następująco: + * - \c "void **priv_data" — wskaźnik na zmienną przechowującą wskaźnik do prywatnych danych, należy go ustawić na \c NULL po zakończeniu + * - \c "int force" — flaga mówiąca o tym, że zasoby są zwalniane przed zakończeniem rozwiązywania nazwy, np. z powodu zamknięcia sesji. + * + * Własny kod rozwiązywania nazwy powinien stworzyć potok, parę gniazd lub + * inny deskryptor pozwalający na co najmniej jednostronną komunikację i + * przekazać go w parametrze \c fd. Po zakończeniu rozwiązywania nazwy, + * powinien wysłać otrzymany adres IP w postaci sieciowej (big-endian) do + * deskryptora. Jeśli rozwiązywanie nazwy się nie powiedzie, należy wysłać + * \c INADDR_NONE. Następnie zostanie wywołana funkcja zwalniająca zasoby + * z parametrem \c force równym \c 0. Gdyby sesja została zakończona przed + * rozwiązaniem nazwy, np. za pomocą funkcji \c gg_logoff(), funkcja + * zwalniająca zasoby zostanie wywołana z parametrem \c force równym \c 1. + * + * \return 0 jeśli się powiodło, -1 w przypadku błędu + */ +int gg_global_set_custom_resolver(int (*resolver_start)(int*, void**, const char*), void (*resolver_cleanup)(void**, int)) +{ + if (resolver_start == NULL || resolver_cleanup == NULL) { + errno = EINVAL; + return -1; + } + + gg_global_resolver_type = GG_RESOLVER_CUSTOM; + gg_global_resolver_start = resolver_start; + gg_global_resolver_cleanup = resolver_cleanup; + + return 0; +} +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/resolver.h Tue Mar 09 21:41:41 2010 +0000 @@ -0,0 +1,30 @@ +/* $Id$ */ + +/* + * (C) Copyright 2008 Wojtek Kaniewski <wojtekka@irc.pl> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef LIBGADU_RESOLVER_H +#define LIBGADU_RESOLVER_H + +#ifndef _WIN32 +# include <arpa/inet.h> +#endif + +int gg_gethostbyname_real(const char *hostname, struct in_addr *result, int pthread); + +#endif /* LIBGADU_RESOLVER_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/sha1.c Tue Mar 09 21:41:41 2010 +0000 @@ -0,0 +1,303 @@ +/* $Id: sha1.c 632 2008-07-30 18:40:06Z darkjames $ */ + +/* + * (C) Copyright 2007 Wojtek Kaniewski <wojtekka@irc.pl> + * + * Public domain SHA-1 implementation by Steve Reid <steve@edmweb.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/** + * \file sha1.c + * + * \brief Funkcje wyznaczania skrĂłtu SHA1 + */ + +#include <string.h> +#include <sys/types.h> +#include <unistd.h> + +#include "libgadu.h" + +/** \cond ignore */ + +#ifdef GG_CONFIG_HAVE_OPENSSL + +#include <openssl/sha.h> + +#else + +/* +SHA-1 in C +By Steve Reid <steve@edmweb.com> +100% Public Domain + +Modified by Wojtek Kaniewski <wojtekka@toxygen.net> for compatibility +with libgadu and OpenSSL API. + +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define LITTLE_ENDIAN * This should be #define'd if true. */ +/* #define SHA1HANDSOFF * Copies data before messing with it. */ + +#include <string.h> + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + unsigned char buffer[64]; +} SHA_CTX; + +static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64]); +static void SHA1_Init(SHA_CTX* context); +static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len); +static void SHA1_Final(unsigned char digest[20], SHA_CTX* context); + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#ifndef GG_CONFIG_BIGENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#else +#define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64]) +{ +uint32_t a, b, c, d, e; +typedef union { + unsigned char c[64]; + uint32_t l[16]; +} CHAR64LONG16; +CHAR64LONG16* block; +static unsigned char workspace[64]; + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1_Init - Initialize new context */ + +static void SHA1_Init(SHA_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len) +{ +unsigned int i, j; + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1_Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1_Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + + +/* Add padding and return the message digest. */ + +static void SHA1_Final(unsigned char digest[20], SHA_CTX* context) +{ +uint32_t i, j; +unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1_Update(context, (unsigned char *)"\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1_Update(context, (unsigned char *)"\0", 1); + } + SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); + } + /* Wipe variables */ + i = j = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(&finalcount, 0, 8); +#ifdef SHA1HANDSOFF /* make SHA1_Transform overwrite it's own static vars */ + SHA1_Transform(context->state, context->buffer); +#endif +} + +#endif /* GG_CONFIG_HAVE_OPENSSL */ + +/** \endcond */ + +/** \cond internal */ + +/** + * \internal Liczy skrĂłt SHA1 z ziarna i hasĹa. + * + * \param password HasĹo + * \param seed Ziarno + * \param result Bufor na wynik funkcji skrĂłtu (20 bajtĂłw) + */ +void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result) +{ + SHA_CTX ctx; + + SHA1_Init(&ctx); + SHA1_Update(&ctx, (const unsigned char*) password, strlen(password)); + seed = gg_fix32(seed); + SHA1_Update(&ctx, (uint8_t*) &seed, 4); + + SHA1_Final(result, &ctx); +} + +/** + * \internal Liczy skrĂłt SHA1 z pliku. + * + * \param fd Deskryptor pliku + * \param result WskaĹşnik na skrĂłt + * + * \return 0 lub -1 + */ +int gg_file_hash_sha1(int fd, uint8_t *result) +{ + unsigned char buf[4096]; + SHA_CTX ctx; + off_t pos, len; + int res; + + if ((pos = lseek(fd, 0, SEEK_CUR)) == (off_t) -1) + return -1; + + if ((len = lseek(fd, 0, SEEK_END)) == (off_t) -1) + return -1; + + if (lseek(fd, 0, SEEK_SET) == (off_t) -1) + return -1; + + SHA1_Init(&ctx); + + if (len <= 10485760) { + while ((res = read(fd, buf, sizeof(buf))) > 0) + SHA1_Update(&ctx, buf, res); + } else { + int i; + + for (i = 0; i < 9; i++) { + int j; + + if (lseek(fd, (len - 1048576) / 9 * i, SEEK_SET) == (off_t) - 1) + return -1; + + for (j = 0; j < 1048576 / sizeof(buf); j++) { + if ((res = read(fd, buf, sizeof(buf))) != sizeof(buf)) { + res = -1; + break; + } + + SHA1_Update(&ctx, buf, res); + } + + if (res == -1) + break; + } + } + + if (res == -1) + return -1; + + SHA1_Final(result, &ctx); + + if (lseek(fd, pos, SEEK_SET) == (off_t) -1) + return -1; + + return 0; +} + +/** \endcond */
--- a/libpurple/protocols/irc/irc.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/irc/irc.c Tue Mar 09 21:41:41 2010 +0000 @@ -369,7 +369,7 @@ const char *pass = purple_connection_get_password(gc); if (pass && *pass) { - buf = irc_format(irc, "vv", "PASS", pass); + buf = irc_format(irc, "v:", "PASS", pass); if (irc_send(irc, buf) < 0) { g_free(buf); return FALSE; @@ -944,7 +944,8 @@ sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL, /* get_account_text_table */ NULL, /* initiate_media */ - NULL /* can_do_media */ + NULL, /* get_media_caps */ + NULL /* get_moods */ }; static gboolean load_plugin (PurplePlugin *plugin) {
--- a/libpurple/protocols/jabber/Makefile.mingw Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/jabber/Makefile.mingw Tue Mar 09 21:41:41 2010 +0000 @@ -29,7 +29,7 @@ -I$(GTK_TOP)/include \ -I$(GTK_TOP)/include/glib-2.0 \ -I$(GTK_TOP)/lib/glib-2.0/include \ - -I$(LIBXML2_TOP)/include \ + -I$(LIBXML2_TOP)/include/libxml2 \ -I$(PURPLE_TOP) \ -I$(PURPLE_TOP)/win32 \ -I$(PIDGIN_TREE_TOP)
--- a/libpurple/protocols/jabber/buddy.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/jabber/buddy.c Tue Mar 09 21:41:41 2010 +0000 @@ -218,7 +218,7 @@ for (l = jb->resources; l; l = l->next) { JabberBuddyResource *jbr = l->data; - if (g_str_equal(resource, jbr->name)) + if (jbr->name && g_str_equal(resource, jbr->name)) return jbr; }
--- a/libpurple/protocols/jabber/data.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/jabber/data.c Tue Mar 09 21:41:41 2010 +0000 @@ -39,7 +39,7 @@ JabberStream *js) { JabberData *data = g_new0(JabberData, 1); - gchar *checksum = purple_util_get_image_checksum(rawdata, size); + gchar *checksum = jabber_calculate_data_sha1sum(rawdata, size); gchar cid[256]; g_snprintf(cid, sizeof(cid), "sha1+%s@bob.xmpp.org", checksum);
--- a/libpurple/protocols/jabber/disco.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/jabber/disco.c Tue Mar 09 21:41:41 2010 +0000 @@ -517,8 +517,12 @@ const char *category, *type, *name; category = xmlnode_get_attrib(child, "category"); type = xmlnode_get_attrib(child, "type"); - if(category && type && !strcmp(category, "pubsub") && !strcmp(type,"pep")) + if(category && type && !strcmp(category, "pubsub") && !strcmp(type,"pep")) { + PurpleConnection *gc = js->gc; js->pep = TRUE; + gc->flags |= PURPLE_CONNECTION_SUPPORT_MOODS | + PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES; + } if (!category || strcmp(category, "server")) continue; if (!type || strcmp(type, "im"))
--- a/libpurple/protocols/jabber/jabber.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/jabber/jabber.c Tue Mar 09 21:41:41 2010 +0000 @@ -62,6 +62,7 @@ #include "roster.h" #include "ping.h" #include "si.h" +#include "usermood.h" #include "xdata.h" #include "pep.h" #include "adhoccommands.h" @@ -69,9 +70,14 @@ #include "jingle/jingle.h" #include "jingle/rtp.h" +#define PING_TIMEOUT 60 + GList *jabber_features = NULL; GList *jabber_identities = NULL; -static GSList *jabber_cmds = NULL; + +static GHashTable *jabber_cmds = NULL; /* PurplePlugin * => GSList of ids */ + +static gint plugin_ref = 0; static void jabber_unregister_account_cb(JabberStream *js); static void try_srv_connect(JabberStream *js); @@ -520,9 +526,12 @@ void jabber_keepalive(PurpleConnection *gc) { - JabberStream *js = gc->proto_data; - - if (js->keepalive_timeout == 0) { + JabberStream *js = purple_connection_get_protocol_data(gc); + time_t now = time(NULL); + + if (js->keepalive_timeout == 0 && (now - js->last_ping) >= PING_TIMEOUT) { + js->last_ping = now; + jabber_keepalive_ping(js); js->keepalive_timeout = purple_timeout_add_seconds(120, (GSourceFunc)(jabber_keepalive_timeout), gc); @@ -2080,18 +2089,31 @@ if (full) { PurpleStatus *status; - status = purple_presence_get_active_status(presence); - mood = purple_status_get_attr_string(status, "mood"); - if(mood != NULL) { + status = purple_presence_get_status(presence, "mood"); + mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); + if(mood && *mood) { const char *moodtext; - moodtext = purple_status_get_attr_string(status, "moodtext"); - if(moodtext != NULL) { - char *moodplustext = g_strdup_printf("%s (%s)", mood, moodtext); + /* find the mood */ + PurpleMood *moods = jabber_get_moods(account); + const char *description = NULL; + + for (; moods->mood ; moods++) { + if (purple_strequal(moods->mood, mood)) { + description = moods->description; + break; + } + } + + moodtext = purple_status_get_attr_string(status, PURPLE_MOOD_COMMENT); + if(moodtext && *moodtext) { + char *moodplustext = + g_strdup_printf("%s (%s)", description ? _(description) : mood, moodtext); purple_notify_user_info_add_pair(user_info, _("Mood"), moodplustext); g_free(moodplustext); } else - purple_notify_user_info_add_pair(user_info, _("Mood"), mood); + purple_notify_user_info_add_pair(user_info, _("Mood"), + description ? _(description) : mood); } if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_TUNE)) { PurpleStatus *tune = purple_presence_get_status(presence, "tune"); @@ -2152,7 +2174,15 @@ "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING), "buzz", _("Allow Buzz"), buzz_enabled, NULL); - types = g_list_append(types, type); + types = g_list_prepend(types, type); + + + type = purple_status_type_new_with_attrs(PURPLE_STATUS_MOOD, + "mood", NULL, TRUE, TRUE, TRUE, + PURPLE_MOOD_NAME, _("Mood Name"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_MOOD_COMMENT, _("Mood Comment"), purple_value_new(PURPLE_TYPE_STRING), + NULL); + types = g_list_prepend(types, type); priority_value = purple_value_new(PURPLE_TYPE_INT); purple_value_set_int(priority_value, 1); @@ -2168,7 +2198,7 @@ "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING), "buzz", _("Allow Buzz"), buzz_enabled, NULL); - types = g_list_append(types, type); + types = g_list_prepend(types, type); priority_value = purple_value_new(PURPLE_TYPE_INT); purple_value_set_int(priority_value, 0); @@ -2184,7 +2214,7 @@ "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING), "buzz", _("Allow Buzz"), buzz_enabled, NULL); - types = g_list_append(types, type); + types = g_list_prepend(types, type); priority_value = purple_value_new(PURPLE_TYPE_INT); purple_value_set_int(priority_value, 0); @@ -2200,7 +2230,7 @@ "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING), "buzz", _("Allow Buzz"), buzz_enabled, NULL); - types = g_list_append(types, type); + types = g_list_prepend(types, type); priority_value = purple_value_new(PURPLE_TYPE_INT); purple_value_set_int(priority_value, 0); @@ -2213,11 +2243,11 @@ "moodtext", _("Mood Text"), purple_value_new(PURPLE_TYPE_STRING), "nick", _("Nickname"), purple_value_new(PURPLE_TYPE_STRING), NULL); - types = g_list_append(types, type); + types = g_list_prepend(types, type); /* if(js->protocol_version == JABBER_PROTO_0_9) - m = g_list_append(m, _("Invisible")); + "Invisible" */ type = purple_status_type_new_with_attrs(PURPLE_STATUS_OFFLINE, @@ -2225,7 +2255,7 @@ NULL, TRUE, TRUE, FALSE, "message", _("Message"), purple_value_new(PURPLE_TYPE_STRING), NULL); - types = g_list_append(types, type); + types = g_list_prepend(types, type); type = purple_status_type_new_with_attrs(PURPLE_STATUS_TUNE, "tune", NULL, FALSE, TRUE, TRUE, @@ -2239,9 +2269,9 @@ PURPLE_TUNE_YEAR, _("Tune Year"), purple_value_new(PURPLE_TYPE_INT), PURPLE_TUNE_URL, _("Tune URL"), purple_value_new(PURPLE_TYPE_STRING), NULL); - types = g_list_append(types, type); - - return types; + types = g_list_prepend(types, type); + + return g_list_reverse(types); } static void @@ -2948,7 +2978,16 @@ gchar *error = NULL; if (!_jabber_send_buzz(js, username, &error)) { + PurpleAccount *account = purple_connection_get_account(gc); + PurpleConversation *conv = + purple_find_conversation_with_account(PURPLE_CONV_TYPE_ANY, username, account); purple_debug_error("jabber", "jabber_send_attention: jabber_cmd_buzz failed with error: %s\n", error ? error : "(NULL)"); + + if (conv) { + purple_conversation_write(conv, username, error, PURPLE_MESSAGE_ERROR, + time(NULL)); + } + g_free(error); return FALSE; } @@ -3301,40 +3340,67 @@ } } -void jabber_register_commands(void) +static PurpleCmdRet +jabber_cmd_mood(PurpleConversation *conv, + const char *cmd, char **args, char **error, void *data) { + JabberStream *js = conv->account->gc->proto_data; + + if (js->pep) { + /* if no argument was given, unset mood */ + if (!args | !args[0]) { + jabber_mood_set(js, NULL, NULL); + } else if (!args[1]) { + jabber_mood_set(js, args[0], NULL); + } else { + jabber_mood_set(js, args[0], args[1]); + } + + return PURPLE_CMD_RET_OK; + } else { + /* account does not support PEP, can't set a mood */ + purple_conversation_write(conv, NULL, + _("Account does not support PEP, can't set mood"), + PURPLE_MESSAGE_ERROR, time(NULL)); + return PURPLE_CMD_RET_FAILED; + } +} + +static void jabber_register_commands(PurplePlugin *plugin) +{ + GSList *commands = NULL; PurpleCmdId id; id = purple_cmd_register("config", "", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-jabber", jabber_cmd_chat_config, _("config: Configure a chat room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("configure", "", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-jabber", jabber_cmd_chat_config, _("configure: Configure a chat room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("nick", "s", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-jabber", jabber_cmd_chat_nick, _("nick <new nickname>: Change your nickname."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("part", "s", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", jabber_cmd_chat_part, _("part [message]: Leave the room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("register", "", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-jabber", jabber_cmd_chat_register, _("register: Register with a chat room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); /* XXX: there needs to be a core /topic cmd, methinks */ id = purple_cmd_register("topic", "s", PURPLE_CMD_P_PRPL, @@ -3343,7 +3409,7 @@ jabber_cmd_chat_topic, _("topic [new topic]: View or change the topic."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("ban", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | @@ -3351,7 +3417,7 @@ jabber_cmd_chat_ban, _("ban <user> [reason]: Ban a user from the room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("affiliate", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | @@ -3359,7 +3425,7 @@ jabber_cmd_chat_affiliate, _("affiliate <owner|admin|member|outcast|none> [nick1] [nick2] ...: Get the users with an affiliation or set users' affiliation with the room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("role", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | @@ -3367,7 +3433,7 @@ jabber_cmd_chat_role, _("role <moderator|participant|visitor|none> [nick1] [nick2] ...: Get the users with a role or set users' role with the room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("invite", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | @@ -3375,7 +3441,7 @@ jabber_cmd_chat_invite, _("invite <user> [message]: Invite a user to the room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("join", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | @@ -3383,7 +3449,7 @@ jabber_cmd_chat_join, _("join: <room> [password]: Join a chat on this server."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("kick", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY | @@ -3391,14 +3457,14 @@ jabber_cmd_chat_kick, _("kick <user> [reason]: Kick a user from the room."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("msg", "ws", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_PRPL_ONLY, "prpl-jabber", jabber_cmd_chat_msg, _("msg <user> <message>: Send a private message to another user."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("ping", "w", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM | @@ -3406,24 +3472,39 @@ "prpl-jabber", jabber_cmd_ping, _("ping <jid>: Ping a user/component/server."), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); id = purple_cmd_register("buzz", "w", PURPLE_CMD_P_PRPL, PURPLE_CMD_FLAG_IM | PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, "prpl-jabber", jabber_cmd_buzz, _("buzz: Buzz a user to get their attention"), NULL); - jabber_cmds = g_slist_prepend(jabber_cmds, GUINT_TO_POINTER(id)); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); + + id = purple_cmd_register("mood", "ws", PURPLE_CMD_P_PRPL, + PURPLE_CMD_FLAG_CHAT | PURPLE_CMD_FLAG_IM | + PURPLE_CMD_FLAG_PRPL_ONLY | PURPLE_CMD_FLAG_ALLOW_WRONG_ARGS, + "prpl-jabber", jabber_cmd_mood, + _("mood: Set current user mood"), NULL); + commands = g_slist_prepend(commands, GUINT_TO_POINTER(id)); + + g_hash_table_insert(jabber_cmds, plugin, commands); } -void jabber_unregister_commands(void) +static void cmds_free_func(gpointer value) { - while (jabber_cmds != NULL) { - purple_cmd_unregister(GPOINTER_TO_UINT(jabber_cmds->data)); - jabber_cmds = g_slist_delete_link(jabber_cmds, jabber_cmds); + GSList *commands = value; + while (commands) { + purple_cmd_unregister(GPOINTER_TO_UINT(commands->data)); + commands = g_slist_delete_link(commands, commands); } } +static void jabber_unregister_commands(PurplePlugin *plugin) +{ + g_hash_table_remove(jabber_cmds, plugin); +} + /* IPC functions */ /** @@ -3472,8 +3553,8 @@ jabber_caps_broadcast_change(); } -void -jabber_init_plugin(PurplePlugin *plugin) +static void +jabber_do_init(void) { GHashTable *ui_info = purple_core_get_ui_info(); const gchar *ui_type; @@ -3481,6 +3562,8 @@ unspecified */ const gchar *ui_name = NULL; + jabber_cmds = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, cmds_free_func); + ui_type = ui_info ? g_hash_table_lookup(ui_info, "client_type") : NULL; if (ui_type) { if (strcmp(ui_type, "pc") == 0 || @@ -3543,7 +3626,52 @@ G_CALLBACK(jabber_caps_broadcast_change), NULL); #endif + /* reverse order of unload_plugin */ + jabber_iq_init(); + jabber_caps_init(); + /* PEP things should be init via jabber_pep_init, not here */ + jabber_pep_init(); + jabber_data_init(); + jabber_bosh_init(); + + /* TODO: Implement adding and retrieving own features via IPC API */ + + jabber_ibb_init(); + jabber_si_init(); + jabber_auth_init(); +} + +static void +jabber_do_uninit(void) +{ + /* reverse order of jabber_do_init */ + jabber_bosh_uninit(); + jabber_data_uninit(); + jabber_si_uninit(); + jabber_ibb_uninit(); + /* PEP things should be uninit via jabber_pep_uninit, not here */ + jabber_pep_uninit(); + jabber_caps_uninit(); + jabber_presence_uninit(); + jabber_iq_uninit(); + + jabber_auth_uninit(); + jabber_features_destroy(); + jabber_identities_destroy(); + + g_hash_table_destroy(jabber_cmds); + jabber_cmds = NULL; +} + +void jabber_plugin_init(PurplePlugin *plugin) +{ + ++plugin_ref; + + if (plugin_ref == 1) + jabber_do_init(); + + jabber_register_commands(plugin); /* IPC functions */ purple_plugin_ipc_register(plugin, "contact_has_feature", PURPLE_CALLBACK(jabber_ipc_contact_has_feature), @@ -3558,7 +3686,6 @@ NULL, 1, purple_value_new(PURPLE_TYPE_STRING)); - /* Modifying these? Look at libxmpp.c:load_plugin for the signal versions */ purple_plugin_ipc_register(plugin, "register_namespace_watcher", PURPLE_CALLBACK(jabber_iq_signal_register), purple_marshal_VOID__POINTER_POINTER, @@ -3572,14 +3699,95 @@ NULL, 2, purple_value_new(PURPLE_TYPE_STRING), /* node */ purple_value_new(PURPLE_TYPE_STRING)); /* namespace */ + + purple_signal_register(plugin, "jabber-register-namespace-watcher", + purple_marshal_VOID__POINTER_POINTER, + NULL, 2, + purple_value_new(PURPLE_TYPE_STRING), /* node */ + purple_value_new(PURPLE_TYPE_STRING)); /* namespace */ + + purple_signal_register(plugin, "jabber-unregister-namespace-watcher", + purple_marshal_VOID__POINTER_POINTER, + NULL, 2, + purple_value_new(PURPLE_TYPE_STRING), /* node */ + purple_value_new(PURPLE_TYPE_STRING)); /* namespace */ + + purple_signal_connect(plugin, "jabber-register-namespace-watcher", + plugin, PURPLE_CALLBACK(jabber_iq_signal_register), NULL); + purple_signal_connect(plugin, "jabber-unregister-namespace-watcher", + plugin, PURPLE_CALLBACK(jabber_iq_signal_unregister), NULL); + + + purple_signal_register(plugin, "jabber-receiving-xmlnode", + purple_marshal_VOID__POINTER_POINTER, NULL, 2, + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), + purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); + + purple_signal_register(plugin, "jabber-sending-xmlnode", + purple_marshal_VOID__POINTER_POINTER, NULL, 2, + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), + purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); + + /* + * Do not remove this or the plugin will fail. Completely. You have been + * warned! + */ + purple_signal_connect_priority(plugin, "jabber-sending-xmlnode", + plugin, PURPLE_CALLBACK(jabber_send_signal_cb), + NULL, PURPLE_SIGNAL_PRIORITY_HIGHEST); + + purple_signal_register(plugin, "jabber-sending-text", + purple_marshal_VOID__POINTER_POINTER, NULL, 2, + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), + purple_value_new_outgoing(PURPLE_TYPE_STRING)); + + purple_signal_register(plugin, "jabber-receiving-message", + purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER_POINTER, + purple_value_new(PURPLE_TYPE_BOOLEAN), 6, + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), + purple_value_new(PURPLE_TYPE_STRING), /* type */ + purple_value_new(PURPLE_TYPE_STRING), /* id */ + purple_value_new(PURPLE_TYPE_STRING), /* from */ + purple_value_new(PURPLE_TYPE_STRING), /* to */ + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); + + purple_signal_register(plugin, "jabber-receiving-iq", + purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER, + purple_value_new(PURPLE_TYPE_BOOLEAN), 5, + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), + purple_value_new(PURPLE_TYPE_STRING), /* type */ + purple_value_new(PURPLE_TYPE_STRING), /* id */ + purple_value_new(PURPLE_TYPE_STRING), /* from */ + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); + + purple_signal_register(plugin, "jabber-watched-iq", + purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER, + purple_value_new(PURPLE_TYPE_BOOLEAN), 5, + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), + purple_value_new(PURPLE_TYPE_STRING), /* type */ + purple_value_new(PURPLE_TYPE_STRING), /* id */ + purple_value_new(PURPLE_TYPE_STRING), /* from */ + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); /* child */ + + purple_signal_register(plugin, "jabber-receiving-presence", + purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER, + purple_value_new(PURPLE_TYPE_BOOLEAN), 4, + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), + purple_value_new(PURPLE_TYPE_STRING), /* type */ + purple_value_new(PURPLE_TYPE_STRING), /* from */ + purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); } -void -jabber_uninit_plugin(PurplePlugin *plugin) +void jabber_plugin_uninit(PurplePlugin *plugin) { + g_return_if_fail(plugin_ref > 0); + + purple_signals_unregister_by_instance(plugin); purple_plugin_ipc_unregister_all(plugin); - jabber_auth_uninit(); - jabber_features_destroy(); - jabber_identities_destroy(); + jabber_unregister_commands(plugin); + + --plugin_ref; + if (plugin_ref == 0) + jabber_do_uninit(); }
--- a/libpurple/protocols/jabber/jabber.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/jabber/jabber.h Tue Mar 09 21:41:41 2010 +0000 @@ -166,6 +166,11 @@ time_t idle; time_t old_idle; + /** When we last pinged the server, so we don't ping more + * often than once every minute. + */ + time_t last_ping; + JabberID *user; JabberBuddy *user_jb; @@ -376,10 +381,7 @@ PurpleMediaCaps jabber_get_media_caps(PurpleAccount *account, const char *who); gboolean jabber_can_receive_file(PurpleConnection *gc, const gchar *who); -void jabber_register_commands(void); -void jabber_unregister_commands(void); - -void jabber_init_plugin(PurplePlugin *plugin); -void jabber_uninit_plugin(PurplePlugin *plugin); +void jabber_plugin_init(PurplePlugin *plugin); +void jabber_plugin_uninit(PurplePlugin *plugin); #endif /* PURPLE_JABBER_H_ */
--- a/libpurple/protocols/jabber/jutil.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/jabber/jutil.c Tue Mar 09 21:41:41 2010 +0000 @@ -302,7 +302,7 @@ const guchar *c; c = (const guchar *)in; - while (*c) { + for ( ; *c; ++c) { if (*c > 0x7f || (*c < 0x20 && *c != '\t' && *c != '\n' && *c != '\r')) return NULL;
--- a/libpurple/protocols/jabber/libxmpp.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/jabber/libxmpp.c Tue Mar 09 21:41:41 2010 +0000 @@ -43,6 +43,7 @@ #include "presence.h" #include "google.h" #include "pep.h" +#include "usermood.h" #include "usertune.h" #include "caps.h" #include "data.h" @@ -126,108 +127,19 @@ NULL, /* get_account_text_table */ jabber_initiate_media, /* initiate_media */ jabber_get_media_caps, /* get_media_caps */ + jabber_get_moods /* get_moods */ }; static gboolean load_plugin(PurplePlugin *plugin) { - purple_signal_register(plugin, "jabber-receiving-xmlnode", - purple_marshal_VOID__POINTER_POINTER, NULL, 2, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); - - purple_signal_register(plugin, "jabber-sending-xmlnode", - purple_marshal_VOID__POINTER_POINTER, NULL, 2, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); - - /* - * Do not remove this or the plugin will fail. Completely. You have been - * warned! - */ - purple_signal_connect_priority(plugin, "jabber-sending-xmlnode", - plugin, PURPLE_CALLBACK(jabber_send_signal_cb), - NULL, PURPLE_SIGNAL_PRIORITY_HIGHEST); - - purple_signal_register(plugin, "jabber-sending-text", - purple_marshal_VOID__POINTER_POINTER, NULL, 2, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new_outgoing(PURPLE_TYPE_STRING)); - - purple_signal_register(plugin, "jabber-receiving-message", - purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER_POINTER, - purple_value_new(PURPLE_TYPE_BOOLEAN), 6, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new(PURPLE_TYPE_STRING), /* type */ - purple_value_new(PURPLE_TYPE_STRING), /* id */ - purple_value_new(PURPLE_TYPE_STRING), /* from */ - purple_value_new(PURPLE_TYPE_STRING), /* to */ - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); - - purple_signal_register(plugin, "jabber-receiving-iq", - purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER, - purple_value_new(PURPLE_TYPE_BOOLEAN), 5, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new(PURPLE_TYPE_STRING), /* type */ - purple_value_new(PURPLE_TYPE_STRING), /* id */ - purple_value_new(PURPLE_TYPE_STRING), /* from */ - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); - - purple_signal_register(plugin, "jabber-watched-iq", - purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER, - purple_value_new(PURPLE_TYPE_BOOLEAN), 5, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new(PURPLE_TYPE_STRING), /* type */ - purple_value_new(PURPLE_TYPE_STRING), /* id */ - purple_value_new(PURPLE_TYPE_STRING), /* from */ - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); /* child */ - - /* Modifying these? Look at jabber_init_plugin for the ipc versions */ - purple_signal_register(plugin, "jabber-register-namespace-watcher", - purple_marshal_VOID__POINTER_POINTER, - NULL, 2, - purple_value_new(PURPLE_TYPE_STRING), /* node */ - purple_value_new(PURPLE_TYPE_STRING)); /* namespace */ - - purple_signal_register(plugin, "jabber-unregister-namespace-watcher", - purple_marshal_VOID__POINTER_POINTER, - NULL, 2, - purple_value_new(PURPLE_TYPE_STRING), /* node */ - purple_value_new(PURPLE_TYPE_STRING)); /* namespace */ - - purple_signal_connect(plugin, "jabber-register-namespace-watcher", - plugin, PURPLE_CALLBACK(jabber_iq_signal_register), NULL); - purple_signal_connect(plugin, "jabber-unregister-namespace-watcher", - plugin, PURPLE_CALLBACK(jabber_iq_signal_unregister), NULL); - - purple_signal_register(plugin, "jabber-receiving-presence", - purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER, - purple_value_new(PURPLE_TYPE_BOOLEAN), 4, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new(PURPLE_TYPE_STRING), /* type */ - purple_value_new(PURPLE_TYPE_STRING), /* from */ - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); + jabber_plugin_init(plugin); return TRUE; } static gboolean unload_plugin(PurplePlugin *plugin) { - purple_signals_unregister_by_instance(plugin); - - /* reverse order of init_plugin */ - jabber_bosh_uninit(); - jabber_data_uninit(); - jabber_si_uninit(); - jabber_ibb_uninit(); - /* PEP things should be uninit via jabber_pep_uninit, not here */ - jabber_pep_uninit(); - jabber_caps_uninit(); - jabber_iq_uninit(); - - jabber_unregister_commands(); - - /* Stay on target...stay on target... Almost there... */ - jabber_uninit_plugin(plugin); + jabber_plugin_uninit(plugin); return TRUE; } @@ -400,7 +312,6 @@ option); my_protocol = plugin; - jabber_init_plugin(plugin); purple_prefs_remove("/plugins/prpl/jabber"); @@ -421,20 +332,6 @@ SetErrorMode(old_error_mode); #endif #endif - jabber_register_commands(); - - /* reverse order of unload_plugin */ - jabber_iq_init(); - jabber_caps_init(); - /* PEP things should be init via jabber_pep_init, not here */ - jabber_pep_init(); - jabber_data_init(); - jabber_bosh_init(); - - /* TODO: Implement adding and retrieving own features via IPC API */ - - jabber_ibb_init(); - jabber_si_init(); purple_signal_connect(purple_get_core(), "uri-handler", plugin, PURPLE_CALLBACK(xmpp_uri_handler), NULL);
--- a/libpurple/protocols/jabber/message.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/jabber/message.c Tue Mar 09 21:41:41 2010 +0000 @@ -74,24 +74,6 @@ jb = jabber_buddy_find(jm->js, jm->from, TRUE); jbr = jabber_buddy_find_resource(jb, jid->resource); - if (jid->resource) { - /* - * We received a message from a specific resource, so we probably want a - * reply to go to this specific resource (i.e. bind/lock the - * conversation to this resource). - * - * This works because purple_conv_im_send gets the name from - * purple_conversation_get_name() - */ - PurpleConversation *conv; - - conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, jm->from, account); - if (conv && !g_str_equal(jm->from, purple_conversation_get_name(conv))) { - purple_debug_info("jabber", "Binding conversation to %s\n", jm->from); - purple_conversation_set_name(conv, jm->from); - } - } - if(!jm->xhtml && !jm->body) { if (jbr) { if (jm->chat_state != JM_STATE_NONE) @@ -137,6 +119,28 @@ serv_got_typing_stopped(gc, jm->from); } } else { + if (jid->resource) { + /* + * We received a message from a specific resource, so + * we probably want a reply to go to this specific + * resource (i.e. bind/lock the conversation to this + * resource). + * + * This works because purple_conv_im_send gets the name + * from purple_conversation_get_name() + */ + PurpleConversation *conv; + + conv = purple_find_conversation_with_account(PURPLE_CONV_TYPE_IM, + jm->from, account); + if (conv && !g_str_equal(jm->from, + purple_conversation_get_name(conv))) { + purple_debug_info("jabber", "Binding conversation to %s\n", + jm->from); + purple_conversation_set_name(conv, jm->from); + } + } + if(jbr) { if (jm->chat_state != JM_STATE_NONE) jbr->chat_states = JABBER_CHAT_STATES_SUPPORTED;
--- a/libpurple/protocols/jabber/pep.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/jabber/pep.c Tue Mar 09 21:41:41 2010 +0000 @@ -56,7 +56,6 @@ void jabber_pep_init_actions(GList **m) { /* register the PEP-specific actions */ - jabber_mood_init_action(m); jabber_nick_init_action(m); } @@ -89,10 +88,11 @@ JabberIq *iq = jabber_iq_new(js, JABBER_IQ_GET); xmlnode *pubsub, *items; - xmlnode_set_attrib(iq->node,"to",to); + if (to) + xmlnode_set_attrib(iq->node, "to", to); + pubsub = xmlnode_new_child(iq->node,"pubsub"); - - xmlnode_set_namespace(pubsub,"http://jabber.org/protocol/pubsub"); + xmlnode_set_namespace(pubsub, "http://jabber.org/protocol/pubsub"); items = xmlnode_new_child(pubsub, "items"); xmlnode_set_attrib(items,"node",node);
--- a/libpurple/protocols/jabber/presence.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/jabber/presence.c Tue Mar 09 21:41:41 2010 +0000 @@ -40,6 +40,7 @@ #include "jutil.h" #include "adhoccommands.h" +#include "usermood.h" #include "usertune.h" @@ -131,6 +132,17 @@ gc = purple_account_get_connection(account); js = purple_connection_get_protocol_data(gc); + + /* it's a mood update */ + if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_MOOD) { + const char *mood = + purple_status_get_attr_string(status, PURPLE_MOOD_NAME); + const char *mood_text = + purple_status_get_attr_string(status, PURPLE_MOOD_COMMENT); + jabber_mood_set(js, mood, mood_text); + return; + } + jabber_presence_send(js, FALSE); }
--- a/libpurple/protocols/jabber/useravatar.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/jabber/useravatar.c Tue Mar 09 21:41:41 2010 +0000 @@ -239,16 +239,12 @@ void jabber_avatar_fetch_mine(JabberStream *js) { - char *jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain); - if (js->initial_avatar_hash) { - jabber_pep_request_item(js, jid, NS_AVATAR_0_12_METADATA, NULL, + jabber_pep_request_item(js, NULL, NS_AVATAR_0_12_METADATA, NULL, do_got_own_avatar_0_12_cb); - jabber_pep_request_item(js, jid, NS_AVATAR_1_1_METADATA, NULL, + jabber_pep_request_item(js, NULL, NS_AVATAR_1_1_METADATA, NULL, do_got_own_avatar_cb); } - - g_free(jid); } typedef struct _JabberBuddyAvatarUpdateURLInfo {
--- a/libpurple/protocols/jabber/usermood.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/jabber/usermood.c Tue Mar 09 21:41:41 2010 +0000 @@ -30,100 +30,102 @@ #include "request.h" #include "debug.h" -static const char * const moodstrings[] = { - "afraid", - "amazed", - "amorous", - "angry", - "annoyed", - "anxious", - "aroused", - "ashamed", - "bored", - "brave", - "calm", - "cautious", - "cold", - "confident", - "confused", - "contemplative", - "contented", - "cranky", - "crazy", - "creative", - "curious", - "dejected", - "depressed", - "disappointed", - "disgusted", - "dismayed", - "distracted", - "embarrassed", - "envious", - "excited", - "flirtatious", - "frustrated", - "grumpy", - "guilty", - "happy", - "hopeful", - "hot", - "humbled", - "humiliated", - "hungry", - "hurt", - "impressed", - "in_awe", - "in_love", - "indignant", - "interested", - "intoxicated", - "invincible", - "jealous", - "lonely", - "lucky", - "mean", - "moody", - "nervous", - "neutral", - "offended", - "outraged", - "playful", - "proud", - "relaxed", - "relieved", - "remorseful", - "restless", - "sad", - "sarcastic", - "serious", - "shocked", - "shy", - "sick", - "sleepy", - "spontaneous", - "stressed", - "strong", - "surprised", - "thankful", - "thirsty", - "tired", - "weak", - "worried" +static PurpleMood moods[] = { + {"afraid", N_("Afraid"), NULL}, + {"amazed", N_("Amazed"), NULL}, + {"amorous", N_("Amorous"), NULL}, + {"angry", N_("Angry"), NULL}, + {"annoyed", N_("Annoyed"), NULL}, + {"anxious", N_("Anxious"), NULL}, + {"aroused", N_("Aroused"), NULL}, + {"ashamed", N_("Ashamed"), NULL}, + {"bored", N_("Bored"), NULL}, + {"brave", N_("Brave"), NULL}, + {"calm", N_("Calm"), NULL}, + {"cautious", N_("Cautious"), NULL}, + {"cold", N_("Cold"), NULL}, + {"confident", N_("Confident"), NULL}, + {"confused", N_("Confused"), NULL}, + {"contemplative", N_("Contemplative"), NULL}, + {"contented", N_("Contented"), NULL}, + {"cranky", N_("Cranky"), NULL}, + {"crazy", N_("Crazy"), NULL}, + {"creative", N_("Creative"), NULL}, + {"curious", N_("Curious"), NULL}, + {"dejected", N_("Dejected"), NULL}, + {"depressed", N_("Depressed"), NULL}, + {"disappointed", N_("Disappointed"), NULL}, + {"disgusted", N_("Disgusted"), NULL}, + {"dismayed", N_("Dismayed"), NULL}, + {"distracted", N_("Distracted"), NULL}, + {"embarrassed", N_("Embarrassed"), NULL}, + {"envious", N_("Envious"), NULL}, + {"excited", N_("Excited"), NULL}, + {"flirtatious", N_("Flirtatious"), NULL}, + {"frustrated", N_("Frustrated"), NULL}, + {"grateful", N_("Grateful"), NULL}, + {"grieving", N_("Grieving"), NULL}, + {"grumpy", N_("Grumpy"), NULL}, + {"guilty", N_("Guilty"), NULL}, + {"happy", N_("Happy"), NULL}, + {"hopeful", N_("Hopeful"), NULL}, + {"hot", N_("Hot"), NULL}, + {"humbled", N_("Humbled"), NULL}, + {"humiliated", N_("Humiliated"), NULL}, + {"hungry", N_("Hungry"), NULL}, + {"hurt", N_("Hurt"), NULL}, + {"impressed", N_("Impressed"), NULL}, + {"in_awe", N_("In awe"), NULL}, + {"in_love", N_("In love"), NULL}, + {"indignant", N_("Indignant"), NULL}, + {"interested", N_("Interested"), NULL}, + {"intoxicated", N_("Intoxicated"), NULL}, + {"invincible", N_("Invincible"), NULL}, + {"jealous", N_("Jealous"), NULL}, + {"lonely", N_("Lonely"), NULL}, + {"lost", N_("Lost"), NULL}, + {"lucky", N_("Lucky"), NULL}, + {"mean", N_("Mean"), NULL}, + {"moody", N_("Moody"), NULL}, + {"nervous", N_("Nervous"), NULL}, + {"neutral", N_("Neutral"), NULL}, + {"offended", N_("Offended"), NULL}, + {"outraged", N_("Outraged"), NULL}, + {"playful", N_("Playful"), NULL}, + {"proud", N_("Proud"), NULL}, + {"relaxed", N_("Relaxed"), NULL}, + {"relieved", N_("Relieved"), NULL}, + {"remorseful", N_("Remorseful"), NULL}, + {"restless", N_("Restless"), NULL}, + {"sad", N_("Sad"), NULL}, + {"sarcastic", N_("Sarcastic"), NULL}, + {"satisfied", N_("Satisfied"), NULL}, + {"serious", N_("Serious"), NULL}, + {"shocked", N_("Shocked"), NULL}, + {"shy", N_("Shy"), NULL}, + {"sick", N_("Sick"), NULL}, + {"sleepy", N_("Sleepy"), NULL}, + {"spontaneous", N_("Spontaneous"), NULL}, + {"stressed", N_("Stressed"), NULL}, + {"strong", N_("Strong"), NULL}, + {"surprised", N_("Surprised"), NULL}, + {"thankful", N_("Thankful"), NULL}, + {"thirsty", N_("Thirsty"), NULL}, + {"tired", N_("Tired"), NULL}, + {"undefined", N_("Undefined"), NULL}, + {"weak", N_("Weak"), NULL}, + {"worried", N_("Worried"), NULL}, + /* Mark the last record. */ + {NULL, NULL, NULL} }; -static void -jabber_mood_cb(JabberStream *js, const char *from, xmlnode *items) -{ - xmlnode *item; - JabberBuddy *buddy = jabber_buddy_find(js, from, FALSE); +static void jabber_mood_cb(JabberStream *js, const char *from, xmlnode *items) { + /* it doesn't make sense to have more than one item here, so let's just pick the first one */ + xmlnode *item = xmlnode_get_child(items, "item"); const char *newmood = NULL; char *moodtext = NULL; - xmlnode *child, *mood; - - /* it doesn't make sense to have more than one item here, so let's just pick the first one */ - item = xmlnode_get_child(items, "item"); - + JabberBuddy *buddy = jabber_buddy_find(js, from, FALSE); + xmlnode *moodinfo, *mood; /* ignore the mood of people not on our buddy list */ if (!buddy || !item) return; @@ -131,39 +133,34 @@ mood = xmlnode_get_child_with_namespace(item, "mood", "http://jabber.org/protocol/mood"); if (!mood) return; - for (child = mood->child; child; child = child->next) { - if (child->type != XMLNODE_TYPE_TAG) - continue; - - if (g_str_equal("text", child->name) && moodtext == NULL) - moodtext = xmlnode_get_data(child); - else { - int i; - for (i = 0; i < G_N_ELEMENTS(moodstrings); ++i) { - /* verify that the mood is known (valid) */ - if (g_str_equal(child->name, moodstrings[i])) { - newmood = moodstrings[i]; - break; + for (moodinfo = mood->child; moodinfo; moodinfo = moodinfo->next) { + if (moodinfo->type == XMLNODE_TYPE_TAG) { + if (!strcmp(moodinfo->name, "text")) { + if (!moodtext) /* only pick the first one */ + moodtext = xmlnode_get_data(moodinfo); + } else { + int i; + for (i = 0; moods[i].mood; ++i) { + /* verify that the mood is known (valid) */ + if (!strcmp(moodinfo->name, moods[i].mood)) { + newmood = moods[i].mood; + break; + } } } + if (newmood != NULL && moodtext != NULL) + break; } if (newmood != NULL && moodtext != NULL) break; } if (newmood != NULL) { - PurpleAccount *account; - const char *status_id; - JabberBuddyResource *resource = jabber_buddy_find_resource(buddy, NULL); - if (!resource) { /* huh? */ - g_free(moodtext); - return; - } - status_id = jabber_buddy_state_get_status_id(resource->state); - - account = purple_connection_get_account(js->gc); - purple_prpl_got_user_status(account, from, status_id, "mood", - _(newmood), "moodtext", - moodtext ? moodtext : "", NULL); + purple_prpl_got_user_status(js->gc->account, from, "mood", + PURPLE_MOOD_NAME, newmood, + PURPLE_MOOD_COMMENT, moodtext, + NULL); + } else { + purple_prpl_got_user_status_deactive(js->gc->account, from, "mood"); } g_free(moodtext); } @@ -173,77 +170,17 @@ jabber_pep_register_handler("http://jabber.org/protocol/mood", jabber_mood_cb); } -static void do_mood_set_from_fields(PurpleConnection *gc, PurpleRequestFields *fields) { - JabberStream *js; - int selected_mood = purple_request_fields_get_choice(fields, "mood"); - - if (!PURPLE_CONNECTION_IS_VALID(gc)) { - purple_debug_error("jabber", "Unable to set mood; account offline.\n"); - return; - } - - js = gc->proto_data; - - if (selected_mood < 0 || selected_mood >= G_N_ELEMENTS(moodstrings)) { - purple_debug_error("jabber", "Invalid mood index (%d) selected.\n", selected_mood); - return; - } - - jabber_mood_set(js, moodstrings[selected_mood], purple_request_fields_get_string(fields, "text")); -} - -static void do_mood_set_mood(PurplePluginAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; - - PurpleRequestFields *fields; - PurpleRequestFieldGroup *group; - PurpleRequestField *field; - int i; - - fields = purple_request_fields_new(); - group = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, group); - - field = purple_request_field_choice_new("mood", - _("Mood"), 0); - - for(i = 0; i < G_N_ELEMENTS(moodstrings); ++i) - purple_request_field_choice_add(field, _(moodstrings[i])); - - purple_request_field_set_required(field, TRUE); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_string_new("text", - _("Description"), NULL, - FALSE); - purple_request_field_group_add_field(group, field); - - purple_request_fields(gc, _("Edit User Mood"), - _("Edit User Mood"), - _("Please select your mood from the list."), - fields, - _("Set"), G_CALLBACK(do_mood_set_from_fields), - _("Cancel"), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); - -} - -void jabber_mood_init_action(GList **m) { - PurplePluginAction *act = purple_plugin_action_new(_("Set Mood..."), do_mood_set_mood); - *m = g_list_append(*m, act); -} - void jabber_mood_set(JabberStream *js, const char *mood, const char *text) { xmlnode *publish, *moodnode; - g_return_if_fail(mood != NULL); - publish = xmlnode_new("publish"); xmlnode_set_attrib(publish,"node","http://jabber.org/protocol/mood"); moodnode = xmlnode_new_child(xmlnode_new_child(publish, "item"), "mood"); xmlnode_set_namespace(moodnode, "http://jabber.org/protocol/mood"); - xmlnode_new_child(moodnode, mood); + if (mood) { + /* if mood is NULL, set an empty mood node, meaning: unset mood */ + xmlnode_new_child(moodnode, mood); + } if (text && text[0] != '\0') { xmlnode *textnode = xmlnode_new_child(moodnode, "text"); @@ -254,3 +191,8 @@ /* publish is freed by jabber_pep_publish -> jabber_iq_send -> jabber_iq_free (yay for well-defined memory management rules) */ } + +PurpleMood *jabber_get_moods(PurpleAccount *account) +{ + return moods; +} \ No newline at end of file
--- a/libpurple/protocols/jabber/usermood.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/jabber/usermood.h Tue Mar 09 21:41:41 2010 +0000 @@ -30,10 +30,10 @@ void jabber_mood_init(void); -void jabber_mood_init_action(GList **m); - void jabber_mood_set(JabberStream *js, const char *mood, /* must be one of the valid strings defined in the XEP */ const char *text /* might be NULL */); +PurpleMood *jabber_get_moods(PurpleAccount *account); + #endif /* PURPLE_JABBER_USERMOOD_H_ */
--- a/libpurple/protocols/jabber/usernick.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/jabber/usernick.c Tue Mar 09 21:41:41 2010 +0000 @@ -86,14 +86,12 @@ } static void do_nick_set_nick(PurplePluginAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; - JabberStream *js = gc->proto_data; - char *jid = g_strdup_printf("%s@%s", js->user->node, js->user->domain); + PurpleConnection *gc = action->context; + JabberStream *js = purple_connection_get_protocol_data(gc); /* since the nickname might have been changed by another resource of this account, we always have to request the old one from the server to present as the default for the new one */ - jabber_pep_request_item(js, jid, "http://jabber.org/protocol/nick", NULL, do_nick_got_own_nick_cb); - g_free(jid); + jabber_pep_request_item(js, NULL, "http://jabber.org/protocol/nick", NULL, do_nick_got_own_nick_cb); } void jabber_nick_init(void) {
--- a/libpurple/protocols/jabber/xdata.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/jabber/xdata.c Tue Mar 09 21:41:41 2010 +0000 @@ -305,7 +305,7 @@ data->values = g_slist_prepend(data->values, value); - purple_request_field_list_add(field, lbl, value); + purple_request_field_list_add_icon(field, lbl, NULL, value); if(g_list_find_custom(selected, value, (GCompareFunc)strcmp)) purple_request_field_list_add_selected(field, lbl); }
--- a/libpurple/protocols/msn/msn.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/msn/msn.c Tue Mar 09 21:41:41 2010 +0000 @@ -2732,7 +2732,8 @@ sizeof(PurplePluginProtocolInfo), /* struct_size */ msn_get_account_text_table, /* get_account_text_table */ NULL, /* initiate_media */ - NULL /* can_do_media */ + NULL, /* get_media_caps */ + NULL /* get_moods */ }; static PurplePluginInfo info =
--- a/libpurple/protocols/msn/slp.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/msn/slp.c Tue Mar 09 21:41:41 2010 +0000 @@ -308,8 +308,6 @@ return NULL; } -#define MAX_FILE_NAME_LEN 0x226 - static void got_sessionreq(MsnSlpCall *slpcall, const char *branch, const char *euf_guid, const char *context) @@ -382,7 +380,7 @@ /* File Transfer */ PurpleAccount *account; PurpleXfer *xfer; - char *bin; + MsnFileContext *header; gsize bin_len; guint32 file_size; char *file_name; @@ -396,16 +394,18 @@ xfer = purple_xfer_new(account, PURPLE_XFER_RECEIVE, slpcall->slplink->remote_user); - if (xfer) - { - bin = (char *)purple_base64_decode(context, &bin_len); - file_size = GUINT32_FROM_LE(*(gsize *)(bin + 8)); - file_name = g_convert(bin + 20, MAX_FILE_NAME_LEN, "UTF-8", "UTF-16LE", + header = (MsnFileContext *)purple_base64_decode(context, &bin_len); + if (bin_len >= sizeof(MsnFileContext) - 1 && + (header->version == 2 || + (header->version == 3 && header->length == sizeof(MsnFileContext) + 63))) { + file_size = GUINT64_FROM_LE(header->file_size); + + file_name = g_convert((const gchar *)&header->file_name, + MAX_FILE_NAME_LEN * 2, + "UTF-8", "UTF-16LE", NULL, NULL, NULL); - g_free(bin); - purple_xfer_set_filename(xfer, file_name ? file_name : ""); g_free(file_name); purple_xfer_set_size(xfer, file_size); @@ -424,6 +424,7 @@ purple_xfer_request(xfer); } + g_free(header); accepted = TRUE;
--- a/libpurple/protocols/msn/slp.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/msn/slp.h Tue Mar 09 21:41:41 2010 +0000 @@ -30,6 +30,25 @@ #include "session.h" #include "slpcall.h" +#define MAX_FILE_NAME_LEN 260 /* MAX_PATH in Windows */ + +/** + * The context data for a file transfer request + */ +#pragma pack(push,1) /* Couldn't they have made it the right size? */ +typedef struct +{ + guint32 length; /*< Length of header */ + guint32 version; /*< MSN version */ + guint64 file_size; /*< Size of file */ + guint32 type; /*< Transfer type */ + gunichar2 file_name[MAX_FILE_NAME_LEN]; /*< Self-explanatory */ + gchar unknown1[30]; /*< Used somehow for background sharing */ + guint32 unknown2; /*< Possibly for background sharing as well */ + gchar preview[1]; /*< File preview data, 96x96 PNG */ +} MsnFileContext; +#pragma pack(pop) + MsnSlpCall * msn_slp_sip_recv(MsnSlpLink *slplink, const char *body);
--- a/libpurple/protocols/msn/slplink.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/msn/slplink.c Tue Mar 09 21:41:41 2010 +0000 @@ -307,6 +307,8 @@ slpmsg->offset += msg->msnslp_header.length; + slpmsg->msgs = g_list_remove(slpmsg->msgs, msg); + if (slpmsg->offset < real_size) { if (slpmsg->slpcall->xfer && purple_xfer_get_status(slpmsg->slpcall->xfer) == PURPLE_XFER_STATUS_STARTED) @@ -331,8 +333,6 @@ } } } - - slpmsg->msgs = g_list_remove(slpmsg->msgs, msg); } /* We have received the message nak. */ @@ -658,74 +658,51 @@ } } -typedef struct -{ - guint32 length; - guint32 unk1; - guint32 file_size; - guint32 unk2; - guint32 unk3; -} MsnContextHeader; - -#define MAX_FILE_NAME_LEN 0x226 - static gchar * gen_context(PurpleXfer *xfer, const char *file_name, const char *file_path) { gsize size = 0; - MsnContextHeader header; + MsnFileContext header; gchar *u8 = NULL; - guchar *base; - guchar *n; gchar *ret; gunichar2 *uni = NULL; glong currentChar = 0; - glong uni_len = 0; - gsize len; + glong len = 0; size = purple_xfer_get_size(xfer); - if(!file_name) { + if (!file_name) { gchar *basename = g_path_get_basename(file_path); u8 = purple_utf8_try_convert(basename); g_free(basename); file_name = u8; } - uni = g_utf8_to_utf16(file_name, -1, NULL, &uni_len, NULL); + uni = g_utf8_to_utf16(file_name, -1, NULL, &len, NULL); - if(u8) { + if (u8) { g_free(u8); file_name = NULL; u8 = NULL; } - len = sizeof(MsnContextHeader) + MAX_FILE_NAME_LEN + 4; - - header.length = GUINT32_TO_LE(len); - header.unk1 = GUINT32_TO_LE(2); - header.file_size = GUINT32_TO_LE(size); - header.unk2 = GUINT32_TO_LE(0); - header.unk3 = GUINT32_TO_LE(0); - - base = g_malloc(len + 1); - n = base; + header.length = GUINT32_TO_LE(sizeof(MsnFileContext) - 1); + header.version = GUINT32_TO_LE(2); /* V.3 contains additional unnecessary data */ + header.file_size = GUINT64_TO_LE(size); + header.type = GUINT32_TO_LE(1); /* No file preview */ - memcpy(n, &header, sizeof(MsnContextHeader)); - n += sizeof(MsnContextHeader); + len = MIN(len, MAX_FILE_NAME_LEN); + for (currentChar = 0; currentChar < len; currentChar++) { + header.file_name[currentChar] = GUINT16_TO_LE(uni[currentChar]); + } + memset(&header.file_name[currentChar], 0x00, (MAX_FILE_NAME_LEN - currentChar) * 2); - memset(n, 0x00, MAX_FILE_NAME_LEN); - for(currentChar = 0; currentChar < uni_len; currentChar++) { - *((gunichar2 *)n + currentChar) = GUINT16_TO_LE(uni[currentChar]); - } - n += MAX_FILE_NAME_LEN; - - memset(n, 0xFF, 4); - n += 4; + memset(&header.unknown1, 0, sizeof(header.unknown1)); + header.unknown2 = GUINT32_TO_LE(0xffffffff); + header.preview[0] = '\0'; g_free(uni); - ret = purple_base64_encode(base, len); - g_free(base); + ret = purple_base64_encode((const guchar *)&header, sizeof(MsnFileContext)); return ret; }
--- a/libpurple/protocols/mxit/mxit.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/mxit/mxit.c Tue Mar 09 21:41:41 2010 +0000 @@ -633,8 +633,9 @@ NULL, /* attention_types */ sizeof( PurplePluginProtocolInfo ), /* struct_size */ mxit_get_text_table, /* get_account_text_table */ - NULL, - NULL + NULL, /* initiate_media */ + NULL, /* get_media_caps */ + NULL /* get_moods */ };
--- a/libpurple/protocols/mxit/mxit.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/mxit/mxit.h Tue Mar 09 21:41:41 2010 +0000 @@ -37,7 +37,9 @@ #endif #elif defined( _WIN32 ) /* windows architecture */ +#ifndef HOST_NAME_MAX #define HOST_NAME_MAX 512 +#endif #include "libc_interface.h" #elif defined( __linux__ ) /* linux architecture */
--- a/libpurple/protocols/myspace/myspace.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/myspace/myspace.c Tue Mar 09 21:41:41 2010 +0000 @@ -3093,7 +3093,8 @@ sizeof(PurplePluginProtocolInfo), /* struct_size */ msim_get_account_text_table, /* get_account_text_table */ NULL, /* initiate_media */ - NULL /* can_do_media */ + NULL, /* get_media_caps */ + NULL /* get_moods */ }; /**
--- a/libpurple/protocols/novell/novell.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/novell/novell.c Tue Mar 09 21:41:41 2010 +0000 @@ -3529,7 +3529,8 @@ sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL, /* get_account_text_table */ NULL, /* initiate_media */ - NULL /* can_do_media */ + NULL, /* get_media_caps */ + NULL /* get_moods */ }; static PurplePluginInfo info = {
--- a/libpurple/protocols/null/nullprpl.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/null/nullprpl.c Tue Mar 09 21:41:41 2010 +0000 @@ -1117,9 +1117,10 @@ NULL, /* send_attention */ NULL, /* get_attention_types */ sizeof(PurplePluginProtocolInfo), /* struct_size */ - NULL, - NULL, /* initiate_media */ - NULL /* can_do_media */ + NULL, /* get_account_text_table */ + NULL, /* initiate_media */ + NULL, /* get_media_caps */ + NULL /* get_moods */ }; static void nullprpl_init(PurplePlugin *plugin)
--- a/libpurple/protocols/oscar/clientlogin.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/oscar/clientlogin.c Tue Mar 09 21:41:41 2010 +0000 @@ -43,7 +43,7 @@ #include "core.h" #define URL_CLIENT_LOGIN "https://api.screenname.aol.com/auth/clientLogin" -#define URL_START_OSCAR_SESSION "http://api.oscar.aol.com/aim/startOSCARSession" +#define URL_START_OSCAR_SESSION "https://api.oscar.aol.com/aim/startOSCARSession" /* * Using clientLogin requires a developer ID. This key is for libpurple. @@ -177,10 +177,23 @@ code = atoi(tmp); if (code != 200) { + xmlnode *status_detail_node; + guint status_detail = 0; + + status_detail_node = xmlnode_get_child(response_node, + "statusDetailCode"); + if (status_detail_node) { + gchar *data = xmlnode_get_data(status_detail_node); + if (data) { + status_detail = atoi(data); + g_free(data); + } + } + purple_debug_error("oscar", "startOSCARSession response statusCode " "was %s: %s\n", tmp, response); - if (code == 401 || code == 607) + if ((code == 401 && status_detail != 1014) || code == 607) purple_connection_error_reason(gc, PURPLE_CONNECTION_ERROR_OTHER_ERROR, _("You have been connecting and disconnecting too " @@ -293,7 +306,11 @@ static void send_start_oscar_session(OscarData *od, const char *token, const char *session_key, time_t hosttime) { char *query_string, *signature, *url; - gboolean use_tls = purple_account_get_bool(purple_connection_get_account(od->gc), "use_ssl", OSCAR_DEFAULT_USE_SSL); + PurpleAccount *account; + gboolean use_tls; + + account = purple_connection_get_account(od->gc); + use_tls = purple_account_get_bool(account, "use_ssl", OSCAR_DEFAULT_USE_SSL); /* * Construct the GET parameters. 0x00000611 is the distid given to @@ -317,7 +334,8 @@ g_free(signature); /* Make the request */ - od->url_data = purple_util_fetch_url(url, TRUE, NULL, FALSE, + od->url_data = purple_util_fetch_url_request_len_with_account(account, + url, TRUE, NULL, FALSE, NULL, FALSE, -1, start_oscar_session_cb, od); g_free(url); } @@ -573,8 +591,9 @@ g_string_free(body, TRUE); /* Send the POST request */ - od->url_data = purple_util_fetch_url_request(URL_CLIENT_LOGIN, - TRUE, NULL, FALSE, request->str, FALSE, + od->url_data = purple_util_fetch_url_request_len_with_account( + purple_connection_get_account(gc), URL_CLIENT_LOGIN, + TRUE, NULL, FALSE, request->str, FALSE, -1, client_login_cb, od); g_string_free(request, TRUE); }
--- a/libpurple/protocols/oscar/family_buddy.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/oscar/family_buddy.c Tue Mar 09 21:41:41 2010 +0000 @@ -221,6 +221,18 @@ if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) ret = userfunc(od, conn, frame, &userinfo); + if (snac->subtype == SNAC_SUBTYPE_BUDDY_ONCOMING && + userinfo.capabilities & OSCAR_CAPABILITY_XTRAZ) { + PurpleAccount *account = purple_connection_get_account(od->gc); + PurpleBuddy *buddy = purple_find_buddy(account, userinfo.bn); + + if (buddy) { + PurplePresence *presence = purple_buddy_get_presence(buddy); + + if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOOD)) + icq_im_xstatus_request(od, userinfo.bn); + } + } aim_info_free(&userinfo); return ret;
--- a/libpurple/protocols/oscar/family_icbm.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/oscar/family_icbm.c Tue Mar 09 21:41:41 2010 +0000 @@ -2624,6 +2624,15 @@ char *bn; guchar *cookie; guint8 bnlen; + char *xml = NULL; + int hdrlen; + int curpos; + int num1,num2; + char *desc, *title, *temp; + PurpleAccount *account; + PurpleBuddy *buddy; + PurplePresence *presence; + PurpleStatus *status; cookie = byte_stream_getraw(bs, 8); channel = byte_stream_get16(bs); @@ -2633,14 +2642,54 @@ if (channel == 0x0002) { - if (reason == 0x0003) /* channel-specific */ - /* parse status note text */ - parse_status_note_text(od, cookie, bn, bs); - - byte_stream_get16(bs); /* Unknown */ - byte_stream_get16(bs); /* Unknown */ - if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) - ret = userfunc(od, conn, frame, channel, bn, reason, cookie); + hdrlen = byte_stream_getle16(bs); + if ( ((hdrlen == 27 ) && (bs->len > (27 + 51)))) { + byte_stream_advance(bs, 51); + num1 = byte_stream_getle16(bs); + num2 = byte_stream_getle16(bs); + purple_debug_misc("oscar", "X-Status: Num1 %i, num2 %i\n",num1, num2); + + if(((num1 == 0x4f00)&&(num2 == 0x3b00))) { + byte_stream_advance(bs, 86); + curpos = byte_stream_curpos(bs); + xml = byte_stream_getstr(bs, bs->len - curpos); + purple_debug_misc("oscar", "X-Status: Received XML reply\n"); + if(xml) { + /* purple_debug_misc("oscar", "X-Status: XML reply: %s\n", (const char*) xml); */ + if ((desc=strstr(xml,"<desc>")) != NULL) { + temp=strstr(xml,"</desc>"); + temp[0]=0; + desc=desc+12; + } + if ((title=strstr(xml,"<title>")) != NULL) { + temp=strstr(xml,"</title>"); + temp[0]=0; + title=title+13; + } else { + title=""; + } + strcpy(xml,title); + if (desc) { + strcat(xml, " - "); + strcat(xml, desc); + } + purple_debug_misc("oscar", "X-Status reply: %s\n", (const char*)xml); + account = purple_connection_get_account(od->gc); + buddy = purple_find_buddy(account, bn); + presence = purple_buddy_get_presence(buddy); + status = purple_presence_get_active_status(presence); + purple_prpl_got_user_status(account, bn, + purple_status_get_id(status), "message", xml, NULL); + } else { + purple_debug_misc("oscar", "X-Status: Can't get XML reply string\n"); + } + } else { + purple_debug_misc("oscar", "X-Status: 0x0004, 0x000b not an xstatus reply\n" ); + /* if ((userfunc = aim_callhandler(od, snac->family, snac->subtype))) + ret = userfunc(od, conn, frame, channel, sn, reason); */ + } + + } } else if (channel == 0x0004) { /* ICQ message */ switch (reason) { @@ -2699,6 +2748,7 @@ g_free(cookie); g_free(bn); + g_free(xml); return ret; } @@ -2807,6 +2857,181 @@ } /* + * Subtype 0x0006 - Send eXtra Status request + */ +int icq_im_xstatus_request(OscarData *od, const char *sn) +{ + FlapConnection *conn; + aim_snacid_t snacid; + guchar cookie[8]; + GSList *outer_tlvlist = NULL, *inner_tlvlist = NULL; + ByteStream bs, header, plugindata; + PurpleAccount *account; + const char *fmt; + char *statxml; + int xmllen; + + static const guint8 pluginid[] = + { + 0x09, 0x46, 0x13, 0x49, 0x4C, 0x7F, 0x11, 0xD1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00 + }; + + static const guint8 c_plugindata[] = + { + 0x1B, 0x00, 0x0A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x4F, 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0, 0x9C, + 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00, 0x00, 0x53, 0x63, 0x72, 0x69, 0x70, + 0x74, 0x20, 0x50, 0x6C, 0x75, 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74, + 0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, + 0x72, 0x72, 0x69, 0x76, 0x65, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x14, 0x01, 0x00, 0x00, 0x10, 0x01, 0x00, 0x00 + }; + + if (!od || !(conn = flap_connection_findbygroup(od, 0x0004))) + return -EINVAL; + + if (!sn) + return -EINVAL; + + fmt = "<N><QUERY><Q><PluginID>srvMng</PluginID></Q></QUERY><NOTIFY><srv><id>cAwaySrv</id><req><id>AwayStat</id><trans>2</trans><senderId>%s</senderId></req></srv></NOTIFY></N>\r\n"; + + account = purple_connection_get_account(od->gc); + xmllen = strlen(fmt) - 2 + strlen(account->username); + + statxml = (char*) g_malloc(xmllen); + snprintf(statxml, xmllen, fmt, account->username); + + aim_icbm_makecookie(cookie); + + byte_stream_new(&bs, 10 + 8 + 2 + 1 + strlen(sn) + 2 + + 2 + 2 + 8 + 16 + 2 + 2 + 2 + 2 + 2 + + 2 + 2 + sizeof(c_plugindata) + xmllen + + 2 + 2); + + snacid = aim_cachesnac(od, 0x0004, 0x0006, 0x0000, NULL, 0); + aim_im_puticbm(&bs, cookie, 0x0002, sn); + + byte_stream_new(&header, (7*2) + 16 + 8 + 2 + sizeof(c_plugindata) + xmllen); /* TLV 0x0005 Stream + Size */ + byte_stream_new(&plugindata, (sizeof(c_plugindata) + xmllen)); + + byte_stream_put16(&header, 0x0000); /* Message Type: Request */ + byte_stream_putraw(&header, cookie, sizeof(cookie)); /* Message ID */ + byte_stream_putraw(&header, pluginid, sizeof(pluginid)); /* Plugin ID */ + + aim_tlvlist_add_16(&inner_tlvlist, 0x000a, 0x0001); + aim_tlvlist_add_noval(&inner_tlvlist, 0x000f); + + /* Add Plugin Specific Data */ + byte_stream_putraw(&plugindata, c_plugindata, sizeof(c_plugindata)); /* Content of TLV 0x2711 */ + byte_stream_putstr(&plugindata, statxml); + + aim_tlvlist_add_raw(&inner_tlvlist, 0x2711, (sizeof(c_plugindata) + xmllen), plugindata.data); + + aim_tlvlist_write(&header, &inner_tlvlist); + + + aim_tlvlist_add_raw(&outer_tlvlist, 0x0005, byte_stream_curpos(&header), header.data); + aim_tlvlist_add_noval(&outer_tlvlist, 0x0003); /* Empty TLV 0x0003 */ + + aim_tlvlist_write(&bs, &outer_tlvlist); + + purple_debug_misc("oscar", "X-Status Request\n"); + flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x0006, 0x0000, snacid, &bs, TRUE); + + aim_tlvlist_free(inner_tlvlist); + aim_tlvlist_free(outer_tlvlist); + byte_stream_destroy(&header); + byte_stream_destroy(&plugindata); + byte_stream_destroy(&bs); + g_free(statxml); + + return 0; +} + +int icq_relay_xstatus(OscarData *od, const char *sn, const guchar *cookie) +{ + FlapConnection *conn; + ByteStream bs; + aim_snacid_t snacid; + PurpleAccount *account; + PurpleStatus *status; + const char *fmt; + const char *formatted_msg; + char *msg; + char *statxml; + const char *title; + int len; + + static const guint8 plugindata[] = { + 0x1B, 0x00, + 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0xF9, 0xD1, 0x0E, 0x00, 0xF9, 0xD1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x4F, + 0x00, 0x3B, 0x60, 0xB3, 0xEF, 0xD8, 0x2A, 0x6C, 0x45, 0xA4, 0xE0, + 0x9C, 0x5A, 0x5E, 0x67, 0xE8, 0x65, 0x08, 0x00, 0x2A, 0x00, 0x00, + 0x00, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x20, 0x50, 0x6C, 0x75, + 0x67, 0x2D, 0x69, 0x6E, 0x3A, 0x20, 0x52, 0x65, 0x6D, 0x6F, 0x74, + 0x65, 0x20, 0x4E, 0x6F, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, + 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x72, 0x72, 0x69, 0x76, 0x65, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xF3, 0x01, 0x00, 0x00, 0xEF, 0x01, 0x00, 0x00 + }; + + fmt = "<NR><RES><ret event='OnRemoteNotification'><srv><id>cAwaySrv</id><val srv_id='cAwaySrv'><Root><CASXtraSetAwayMessage></CASXtraSetAwayMessage>&l t;uin>%s</uin><index>1</index><title>%s</title><desc>%s</desc></Root></val></srv><srv><id>cRandomizerSrv</id><val srv_id='cRandomizerSrv'>undefined</val></srv></ret></RES></NR>\r\n"; + + + if (!od || !(conn = flap_connection_findbygroup(od, 0x0002))) + return -EINVAL; + + if (!sn) + return -EINVAL; + + account = purple_connection_get_account(od->gc); + if(!account) return -EINVAL; + +/* if (!strcmp(account->username, sn)) + icq_im_xstatus_request(od, sn); */ + + status = purple_presence_get_active_status(account->presence); + if (!status) return -EINVAL; + title = purple_status_get_name(status); + if (!title) return -EINVAL; + formatted_msg = purple_status_get_attr_string(status, "message"); + if (!formatted_msg) return -EINVAL; + msg = purple_markup_strip_html(formatted_msg); + if (!msg) return -EINVAL; + len = strlen(fmt)-6+strlen(account->username)+strlen(title)+strlen(msg); + statxml = (char*) g_malloc(len); + + snprintf(statxml, len, fmt, + account->username, title, msg); + + purple_debug_misc("oscar", "X-Status AutoReply: %s, %s\n", formatted_msg, msg); + + byte_stream_new(&bs, 10 + 8 + 2 + 1 + strlen(sn) + 2 + sizeof(plugindata) + strlen(statxml)); /* 16 extra */ + + snacid = aim_cachesnac(od, 0x0004, 0x000b, 0x0000, NULL, 0); + aim_im_puticbm(&bs, cookie, 0x0002, sn); + byte_stream_put16(&bs, 0x0003); + byte_stream_putraw(&bs, plugindata, sizeof(plugindata)); + byte_stream_putraw(&bs, (const guint8*)statxml, strlen(statxml)); + + flap_connection_send_snac_with_priority(od, conn, 0x0004, 0x000b, 0x0000, snacid, &bs, TRUE); + + g_free(statxml); + g_free(msg); + byte_stream_destroy(&bs); + + return 0; +} + +/* * Subtype 0x0014 - Receive a mini typing notification (mtn) packet. * * This is supported by winaim5 and newer, MacAIM bleh and newer, iChat bleh and newer,
--- a/libpurple/protocols/oscar/family_locate.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/oscar/family_locate.c Tue Mar 09 21:41:41 2010 +0000 @@ -45,7 +45,7 @@ * But, eh. */ static const struct { - guint32 flag; + guint64 flag; guint8 data[16]; } aim_caps[] = { @@ -166,6 +166,16 @@ {0x09, 0x46, 0x13, 0x4a, 0x4c, 0x7f, 0x11, 0xd1, 0x22, 0x82, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + /* New format of caps (xtraz icons) */ + {OSCAR_CAPABILITY_NEWCAPS, + {0x09, 0x46, 0x00, 0x00, 0x4c, 0x7f, 0x11, 0xd1, + 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, + + /* Support xtraz statuses */ + {OSCAR_CAPABILITY_XTRAZ, + {0x1a, 0x09, 0x3c, 0x6c, 0xd7, 0xFD, 0x4e, 0xc5, + 0x9d, 0x51, 0xa6, 0x47, 0x4e, 0x34, 0xf5, 0xa0}}, + {OSCAR_CAPABILITY_SENDBUDDYLIST, {0x09, 0x46, 0x13, 0x4b, 0x4c, 0x7f, 0x11, 0xd1, 0x82, 0x22, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00}}, @@ -240,6 +250,202 @@ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, }; +/* Keep this array synchronized with icq_purple_moods. */ +static const struct { + const char *mood; + guint8 data[16]; +} icq_custom_icons[] = { + + {"thinking", + {0x3f, 0xb0, 0xbd, 0x36, 0xaf, 0x3b, 0x4a, 0x60, + 0x9e, 0xef, 0xcf, 0x19, 0x0f, 0x6a, 0x5a, 0x7f}}, + + {"busy", + {0x48, 0x8e, 0x14, 0x89, 0x8a, 0xca, 0x4a, 0x08, + 0x82, 0xaa, 0x77, 0xce, 0x7a, 0x16, 0x52, 0x08}}, + + {"shopping", + {0x63, 0x62, 0x73, 0x37, 0xa0, 0x3f, 0x49, 0xff, + 0x80, 0xe5, 0xf7, 0x09, 0xcd, 0xe0, 0xa4, 0xee}}, + + /* This was in the original patch, but isn't what the official client + * (ICQ 6) sets when you choose its typewriter icon. */ + {"typing", + {0x63, 0x4f, 0x6b, 0xd8 ,0xad, 0xd2, 0x4a, 0xa1, + 0xaa, 0xb9, 0x11, 0x5b, 0xc2, 0x6d, 0x05, 0xa1}}, + + {"question", + {0x63, 0x14, 0x36, 0xff, 0x3f, 0x8a, 0x40, 0xd0, + 0xa5, 0xcb, 0x7b, 0x66, 0xe0, 0x51, 0xb3, 0x64}}, + + {"angry", + {0x01, 0xd8, 0xd7, 0xee, 0xac, 0x3b, 0x49, 0x2a, + 0xa5, 0x8d, 0xd3, 0xd8, 0x77, 0xe6, 0x6b, 0x92}}, + + {"plate", + {0xf8, 0xe8, 0xd7, 0xb2, 0x82, 0xc4, 0x41, 0x42, + 0x90, 0xf8, 0x10, 0xc6, 0xce, 0x0a, 0x89, 0xa6}}, + + {"cinema", + {0x10, 0x7a, 0x9a, 0x18, 0x12, 0x32, 0x4d, 0xa4, + 0xb6, 0xcd, 0x08, 0x79, 0xdb, 0x78, 0x0f, 0x09}}, + + {"sick", + {0x1f, 0x7a, 0x40, 0x71, 0xbf, 0x3b, 0x4e, 0x60, + 0xbc, 0x32, 0x4c, 0x57, 0x87, 0xb0, 0x4c, 0xf1}}, + + {"typing", + {0x2c, 0xe0, 0xe4, 0xe5, 0x7c, 0x64, 0x43, 0x70, + 0x9c, 0x3a, 0x7a, 0x1c, 0xe8, 0x78, 0xa7, 0xdc}}, + + {"suit", + {0xb7, 0x08, 0x67, 0xf5, 0x38, 0x25, 0x43, 0x27, + 0xa1, 0xff, 0xcf, 0x4c, 0xc1, 0x93, 0x97, 0x97}}, + + {"bathing", + {0x5a, 0x58, 0x1e, 0xa1, 0xe5, 0x80, 0x43, 0x0c, + 0xa0, 0x6f, 0x61, 0x22, 0x98, 0xb7, 0xe4, 0xc7}}, + + {"tv", + {0x80, 0x53, 0x7d, 0xe2, 0xa4, 0x67, 0x4a, 0x76, + 0xb3, 0x54, 0x6d, 0xfd, 0x07, 0x5f, 0x5e, 0xc6}}, + + {"excited", + {0x6f, 0x49, 0x30, 0x98, 0x4f, 0x7c, 0x4a, 0xff, + 0xa2, 0x76, 0x34, 0xa0, 0x3b, 0xce, 0xae, 0xa7}}, + + {"sleeping", + {0x78, 0x5e, 0x8c, 0x48, 0x40, 0xd3, 0x4c, 0x65, + 0x88, 0x6f, 0x04, 0xcf, 0x3f, 0x3f, 0x43, 0xdf}}, + + {"hiptop", + {0x10, 0x11, 0x17, 0xc9, 0xa3, 0xb0, 0x40, 0xf9, + 0x81, 0xac, 0x49, 0xe1, 0x59, 0xfb, 0xd5, 0xd4}}, + + {"in_love", + {0xdd, 0xcf, 0x0e, 0xa9, 0x71, 0x95, 0x40, 0x48, + 0xa9, 0xc6, 0x41, 0x32, 0x06, 0xd6, 0xf2, 0x80}}, + + {"sleepy", + {0x83, 0xc9, 0xb7, 0x8e, 0x77, 0xe7, 0x43, 0x78, + 0xb2, 0xc5, 0xfb, 0x6c, 0xfc, 0xc3, 0x5b, 0xec}}, + + {"meeting", + {0xf1, 0x8a, 0xb5, 0x2e, 0xdc, 0x57, 0x49, 0x1d, + 0x99, 0xdc, 0x64, 0x44, 0x50, 0x24, 0x57, 0xaf}}, + + {"phone", + {0x12, 0x92, 0xe5, 0x50, 0x1b, 0x64, 0x4f, 0x66, + 0xb2, 0x06, 0xb2, 0x9a, 0xf3, 0x78, 0xe4, 0x8d}}, + + {"surfing", + {0xa6, 0xed, 0x55, 0x7e, 0x6b, 0xf7, 0x44, 0xd4, + 0xa5, 0xd4, 0xd2, 0xe7, 0xd9, 0x5c, 0xe8, 0x1f}}, + + {"mobile", + {0x16, 0x0c, 0x60, 0xbb, 0xdd, 0x44, 0x43, 0xf3, + 0x91, 0x40, 0x05, 0x0f, 0x00, 0xe6, 0xc0, 0x09}}, + + {"search", + {0xd4, 0xe2, 0xb0, 0xba, 0x33, 0x4e, 0x4f, 0xa5, + 0x98, 0xd0, 0x11, 0x7d, 0xbf, 0x4d, 0x3c, 0xc8}}, + + {"party", + {0xe6, 0x01, 0xe4, 0x1c, 0x33, 0x73, 0x4b, 0xd1, + 0xbc, 0x06, 0x81, 0x1d, 0x6c, 0x32, 0x3d, 0x81}}, + + {"coffee", + {0x1b, 0x78, 0xae, 0x31, 0xfa, 0x0b, 0x4d, 0x38, + 0x93, 0xd1, 0x99, 0x7e, 0xee, 0xaf, 0xb2, 0x18}}, + + {"console", + {0xd4, 0xa6, 0x11, 0xd0, 0x8f, 0x01, 0x4e, 0xc0, + 0x92, 0x23, 0xc5, 0xb6, 0xbe, 0xc6, 0xcc, 0xf0}}, + + {"internet", + {0x12, 0xd0, 0x7e, 0x3e, 0xf8, 0x85, 0x48, 0x9e, + 0x8e, 0x97, 0xa7, 0x2a, 0x65, 0x51, 0xe5, 0x8d}}, + + {"cigarette", + {0x64, 0x43, 0xc6, 0xaf, 0x22, 0x60, 0x45, 0x17, + 0xb5, 0x8c, 0xd7, 0xdf, 0x8e, 0x29, 0x03, 0x52}}, + + {"writing", + {0x00, 0x72, 0xd9, 0x08, 0x4a, 0xd1, 0x43, 0xdd, + 0x91, 0x99, 0x6f, 0x02, 0x69, 0x66, 0x02, 0x6f}}, + + {"beer", + {0x8c, 0x50, 0xdb, 0xae, 0x81, 0xed, 0x47, 0x86, + 0xac, 0xca, 0x16, 0xcc, 0x32, 0x13, 0xc7, 0xb7}}, + + {"music", + {0x61, 0xbe, 0xe0, 0xdd, 0x8b, 0xdd, 0x47, 0x5d, + 0x8d, 0xee, 0x5f, 0x4b, 0xaa, 0xcf, 0x19, 0xa7}}, + + {"studying", + {0x60, 0x9d, 0x52, 0xf8, 0xa2, 0x9a, 0x49, 0xa6, + 0xb2, 0xa0, 0x25, 0x24, 0xc5, 0xe9, 0xd2, 0x60}}, + + {"working", + {0xba, 0x74, 0xdb, 0x3e, 0x9e, 0x24, 0x43, 0x4b, + 0x87, 0xb6, 0x2f, 0x6b, 0x8d, 0xfe, 0xe5, 0x0f}}, + + {"restroom", + {0x16, 0xf5, 0xb7, 0x6f, 0xa9, 0xd2, 0x40, 0x35, + 0x8c, 0xc5, 0xc0, 0x84, 0x70, 0x3c, 0x98, 0xfa}}, + + {NULL, + {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}} +}; + +/* Keep this array synchronized with icq_custom_icons. */ +static PurpleMood icq_purple_moods[] = { + {"thinking", N_("Thinking"), NULL}, + {"busy", N_("Busy"), NULL}, + {"shopping", N_("Shopping"), NULL}, + /* This was in the original patch, but isn't what the official client + * (ICQ 6) sets when you choose its typewriter icon. */ + {"typing", NULL, NULL}, + {"question", N_("Questioning"), NULL}, + {"angry", N_("Angry"), NULL}, + {"plate", N_("Eating"), NULL}, + {"cinema", N_("Watching a movie"), NULL}, + {"sick", N_("Sick"), NULL}, + {"typing", N_("Typing"), NULL}, + {"suit", N_("At the office"), NULL}, + {"bathing", N_("Taking a bath"), NULL}, + {"tv", N_("Watching TV"), NULL}, + {"excited", N_("Having fun"), NULL}, + {"sleeping", N_("Sleeping"), NULL}, + {"hiptop", N_("Using a PDA"), NULL}, + {"in_love", N_("In love"), NULL}, + /* Sleepy / Tired */ + {"sleepy", N_("Sleepy"), NULL}, + {"meeting", N_("Meeting friends"), NULL}, + {"phone", N_("On the phone"), NULL}, + {"surfing", N_("Surfing"), NULL}, + /* "I am mobile." / "John is mobile." */ + {"mobile", N_("Mobile"), NULL}, + {"search", N_("Searching the web"), NULL}, + {"party", N_("At a party"), NULL}, + {"coffee", N_("Having Coffee"), NULL}, + /* Playing video games */ + {"console", N_("Gaming"), NULL}, + {"internet", N_("Browsing the web"), NULL}, + {"cigarette", N_("Smoking"), NULL}, + {"writing", N_("Writing"), NULL}, + /* Drinking [Alcohol] */ + {"beer", N_("Drinking"), NULL}, + {"music", N_("Listening to music"), NULL}, + {"studying", N_("Studying"), NULL}, + {"working", N_("Working"), NULL}, + {"restroom", N_("In the restroom"), NULL}, + /* Mark the last record. */ + {NULL, NULL, NULL}, +}; + + /* * Add the userinfo to our linked list. If we already have userinfo * for this buddy, then just overwrite parts of the old data. @@ -274,6 +480,7 @@ cur->sessionlen = userinfo->sessionlen; if (userinfo->capabilities != 0) cur->capabilities = userinfo->capabilities; + cur->present |= userinfo->present; if (userinfo->iconcsumlen > 0) { @@ -398,13 +605,38 @@ cap[8], cap[9], cap[10], cap[11], cap[12], cap[13], cap[14], cap[15]); - g_free(cap); } return flags; } +static const char * +aim_receive_custom_icon(OscarData *od, ByteStream *bs, int len) +{ + int offset; + const char *result = NULL; + + for (offset = 0; byte_stream_empty(bs) && (offset < len); offset += 0x10) { + /* check wheather this capability is a custom user icon */ + guint8 *cap; + int i; + + cap = byte_stream_getraw(bs, 0x10); + + for (i = 0; icq_custom_icons[i].mood; i++) { + if (memcmp(&icq_custom_icons[i].data, cap, 0x10) == 0) { + purple_debug_misc("oscar", "Custom status icon: %s\n", icq_purple_moods[i].description); + result = icq_custom_icons[i].mood; + break; /* should only match once... */ + } + } + g_free(cap); + } + + return result; +} + guint32 aim_locate_getcaps_short(OscarData *od, ByteStream *bs, int len) { @@ -495,6 +727,38 @@ g_free(info->away_encoding); } +static const struct { + char *icqmood; + const char *mood; +} icqmoods[] = { + {"icqmood0", "shopping"}, + {"icqmood1", "bathing"}, + {"icqmood2", "sleepy"}, + {"icqmood3", "party"}, + {"icqmood4", "beer"}, + {"icqmood5", "thinking"}, + {"icqmood6", "plate"}, + {"icqmood7", "tv"}, + {"icqmood8", "meeting"}, + {"icqmood9", "coffee"}, + {"icqmood10", "music"}, + {"icqmood11", "suit"}, + {"icqmood12", "cinema"}, + {"icqmood13", "smile-big"}, + {"icqmood14", "phone"}, + {"icqmood15", "console"}, + {"icqmood16", "studying"}, + {"icqmood17", "sick"}, + {"icqmood18", "sleeping"}, + {"icqmood19", "surfing"}, + {"icqmood20", "internet"}, + {"icqmood21", "working"}, + {"icqmood22", "typing"}, + {"icqmood23", "angry"}, + {NULL, 0} + +}; + /* * AIM is fairly regular about providing user info. This is a generic * routine to extract it in its standard form. @@ -535,11 +799,12 @@ for (curtlv = 0; curtlv < tlvcnt; curtlv++) { guint16 type, length; int endpos; + int curpos; type = byte_stream_get16(bs); length = byte_stream_get16(bs); - - endpos = byte_stream_curpos(bs) + MIN(length, byte_stream_empty(bs)); + curpos = byte_stream_curpos(bs); + endpos = curpos + MIN(length, byte_stream_empty(bs)); if (type == 0x0001) { /* @@ -651,11 +916,23 @@ outinfo->present |= AIM_USERINFO_PRESENT_ICQDATA; } else if (type == 0x000d) { + PurpleAccount *account = purple_connection_get_account(od->gc); + const char *mood; + /* * OSCAR Capability information */ outinfo->capabilities |= aim_locate_getcaps(od, bs, length); outinfo->present |= AIM_USERINFO_PRESENT_CAPABILITIES; + byte_stream_setpos(bs, curpos); + + mood = aim_receive_custom_icon(od, bs, length); + if (mood) + purple_prpl_got_user_status(account, outinfo->bn, "mood", + PURPLE_MOOD_NAME, mood, + NULL); + else + purple_prpl_got_user_status_deactive(account, outinfo->bn, "mood"); } else if (type == 0x000e) { /* @@ -791,6 +1068,37 @@ outinfo->itmsurl_encoding = NULL; } } break; + + case 0x000e: { /* ICQ mood */ + PurpleAccount *account = purple_connection_get_account(od->gc); + char *icqmood; + gint32 i; + const char *mood = NULL; + + icqmood = byte_stream_getstr(bs, length2); + + /* icqmood = "" means X-Status + * with no mood icon. */ + if (*icqmood) { + for (i = 0; icqmoods[i].icqmood; i++) { + if (!strcmp(icqmood, icqmoods[i].icqmood)) { + mood = icqmoods[i].mood; + break; /* should only match once... */ + } + } + + if (!mood) + purple_debug_warning("oscar", "Unknown icqmood: %s\n", icqmood); + } + g_free(icqmood); + + if (mood) + purple_prpl_got_user_status(account, outinfo->bn, "mood", + PURPLE_MOOD_NAME, mood, + NULL); + else + purple_prpl_got_user_status_deactive(account, outinfo->bn, "mood"); + } break; } /* Save ourselves. */ @@ -857,6 +1165,10 @@ return 0; } +/* Apparently, this is never called. + * If you activate it, figure out a way to know what mood to pass to + * aim_tlvlist_add_caps() below. --rlaager */ +#if 0 /* * Inverse of aim_info_extract() */ @@ -892,8 +1204,9 @@ } #endif - if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES) - aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities); + if (info->present & AIM_USERINFO_PRESENT_CAPABILITIES) { + aim_tlvlist_add_caps(&tlvlist, 0x000d, info->capabilities, NULL); + } if (info->present & AIM_USERINFO_PRESENT_SESSIONLEN) aim_tlvlist_add_32(&tlvlist, (guint16)((info->flags & AIM_FLAG_AOL) ? 0x0010 : 0x000f), info->sessionlen); @@ -904,6 +1217,7 @@ return 0; } +#endif /* * Subtype 0x0001 @@ -1089,6 +1403,10 @@ aim_locate_setcaps(OscarData *od, guint32 caps) { FlapConnection *conn; + PurpleAccount *account = purple_connection_get_account(od->gc); + PurplePresence *presence = purple_account_get_presence(account); + PurpleStatus *status = purple_presence_get_status(presence, "mood"); + const char *mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); ByteStream bs; aim_snacid_t snacid; GSList *tlvlist = NULL; @@ -1096,7 +1414,7 @@ if (!od || !(conn = flap_connection_findbygroup(od, SNAC_FAMILY_LOCATE))) return -EINVAL; - aim_tlvlist_add_caps(&tlvlist, 0x0005, caps); + aim_tlvlist_add_caps(&tlvlist, 0x0005, caps, mood); byte_stream_new(&bs, aim_tlvlist_size(tlvlist)); @@ -1179,9 +1497,21 @@ /* Caps will be 5 */ if ((tlv = aim_tlv_gettlv(tlvlist, 0x0005, 1))) { ByteStream cbs; + PurpleAccount *account = purple_connection_get_account(od->gc); + const char *mood; + byte_stream_init(&cbs, tlv->value, tlv->length); userinfo->capabilities = aim_locate_getcaps(od, &cbs, tlv->length); + byte_stream_rewind(&cbs); userinfo->present = AIM_USERINFO_PRESENT_CAPABILITIES; + + mood = aim_receive_custom_icon(od, &cbs, tlv->length); + if (mood) + purple_prpl_got_user_status(account, userinfo->bn, "mood", + PURPLE_MOOD_NAME, mood, + NULL); + else + purple_prpl_got_user_status_deactive(account, userinfo->bn, "mood"); } aim_tlvlist_free(tlvlist); @@ -1399,3 +1729,57 @@ return 0; } + +#if 0 //rlaager +const char* aim_get_custom_icon_mood(gint32 no) +{ + if (no >= G_N_ELEMENTS(aim_custom_icons) || no < 1) + return NULL; + return aim_custom_icons[no].mood.mood; +} +#endif + +const char* +icq_get_custom_icon_description(const char *mood) +{ + int i; + + if (!(mood && *mood)) + return NULL; + + for (i = 0; icq_custom_icons[i].mood; i++) { + /* We check that description is not NULL to exclude + * duplicates, like the typing duplicate. */ + if (icq_purple_moods[i].description && + !strcmp(mood, icq_custom_icons[i].mood)) { + return icq_purple_moods[i].description; + } + } + + return NULL; +} + +guint8* +icq_get_custom_icon_data(const char *mood) +{ + int i; + + if (!(mood && *mood)) + return NULL; + + for (i = 0; icq_custom_icons[i].mood; i++) { + /* We check that description is not NULL to exclude + * duplicates, like the typing duplicate. */ + if (icq_purple_moods[i].description && + !strcmp(mood, icq_custom_icons[i].mood)) { + return (guint8 *)icq_custom_icons[i].data; + } + } + return NULL; +} + +PurpleMood* +icq_get_purple_moods(PurpleAccount *account) +{ + return icq_purple_moods; +}
--- a/libpurple/protocols/oscar/flap_connection.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/oscar/flap_connection.c Tue Mar 09 21:41:41 2010 +0000 @@ -364,6 +364,12 @@ conn->connect_data = NULL; } + if (conn->gsc != NULL && conn->gsc->connect_data != NULL) + { + purple_ssl_close(conn->gsc); + conn->gsc = NULL; + } + if (conn->new_conn_data != NULL) { if (conn->type == SNAC_FAMILY_CHAT)
--- a/libpurple/protocols/oscar/libaim.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/oscar/libaim.c Tue Mar 09 21:41:41 2010 +0000 @@ -97,7 +97,8 @@ sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL, /* get_account_text_table */ NULL, /* initiate_media */ - NULL /* can_do_media */ + NULL, /* get_media_caps */ + NULL /* get_moods */ }; static PurplePluginInfo info =
--- a/libpurple/protocols/oscar/libicq.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/oscar/libicq.c Tue Mar 09 21:41:41 2010 +0000 @@ -108,7 +108,8 @@ sizeof(PurplePluginProtocolInfo), /* struct_size */ icq_get_account_text_table, /* get_account_text_table */ NULL, /* initiate_media */ - NULL /* can_do_media */ + NULL, /* can_do_media */ + oscar_get_purple_moods, /* get_moods */ }; static PurplePluginInfo info =
--- a/libpurple/protocols/oscar/oscar.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/oscar/oscar.c Tue Mar 09 21:41:41 2010 +0000 @@ -61,6 +61,11 @@ #define OSCAR_STATUS_ID_FREE4CHAT "free4chat" #define OSCAR_STATUS_ID_CUSTOM "custom" #define OSCAR_STATUS_ID_MOBILE "mobile" +#define OSCAR_STATUS_ID_EVIL "evil" +#define OSCAR_STATUS_ID_DEPRESSION "depression" +#define OSCAR_STATUS_ID_ATHOME "athome" +#define OSCAR_STATUS_ID_ATWORK "atwork" +#define OSCAR_STATUS_ID_LUNCH "lunch" #define AIMHASHDATA "http://pidgin.im/aim_data.php3" @@ -68,7 +73,7 @@ static OscarCapability purple_caps = (OSCAR_CAPABILITY_CHAT | OSCAR_CAPABILITY_BUDDYICON | OSCAR_CAPABILITY_DIRECTIM | OSCAR_CAPABILITY_SENDFILE | OSCAR_CAPABILITY_UNICODE | OSCAR_CAPABILITY_INTEROPERATE | - OSCAR_CAPABILITY_SHORTCAPS | OSCAR_CAPABILITY_TYPING); + OSCAR_CAPABILITY_SHORTCAPS | OSCAR_CAPABILITY_TYPING | OSCAR_CAPABILITY_ICQSERVERRELAY | OSCAR_CAPABILITY_NEWCAPS | OSCAR_CAPABILITY_XTRAZ); static guint8 features_aim[] = {0x01, 0x01, 0x01, 0x02}; static guint8 features_icq[] = {0x01, 0x06}; @@ -672,7 +677,7 @@ { GString *str; const gchar *tmp; - guint bit = 1; + guint64 bit = 1; str = g_string_new(""); @@ -703,6 +708,10 @@ case OSCAR_CAPABILITY_GAMES2: tmp = _("Games"); break; + case OSCAR_CAPABILITY_XTRAZ: + case OSCAR_CAPABILITY_NEWCAPS: + tmp = _("ICQ Xtraz"); + break; case OSCAR_CAPABILITY_ADDINS: tmp = _("Add-Ins"); break; @@ -784,6 +793,16 @@ return g_strdup(_("Web Aware")); else if (state & AIM_ICQ_STATE_INVISIBLE) return g_strdup(_("Invisible")); + else if (state & AIM_ICQ_STATE_EVIL) + return g_strdup(_("Evil")); + else if (state & AIM_ICQ_STATE_DEPRESSION) + return g_strdup(_("Depression")); + else if (state & AIM_ICQ_STATE_ATHOME) + return g_strdup(_("At home")); + else if (state & AIM_ICQ_STATE_ATWORK) + return g_strdup(_("At work")); + else if (state & AIM_ICQ_STATE_LUNCH) + return g_strdup(_("At lunch")); else return g_strdup(_("Online")); } @@ -961,6 +980,16 @@ } } + if (presence) { + const char *mood; + const char *description; + status = purple_presence_get_status(presence, "mood"); + mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); + description = icq_get_custom_icon_description(mood); + if (description && *description) + purple_notify_user_info_add_pair(user_info, _("Mood"), _(description)); + } + purple_notify_user_info_add_pair(user_info, _("Status"), message); g_free(message); } @@ -1544,6 +1573,7 @@ if (oscar_util_valid_name_icq((purple_account_get_username(account)))) { od->icq = TRUE; + gc->flags |= PURPLE_CONNECTION_SUPPORT_MOODS; } else { gc->flags |= PURPLE_CONNECTION_HTML; gc->flags |= PURPLE_CONNECTION_AUTO_RESP; @@ -2240,6 +2270,16 @@ status_id = OSCAR_STATUS_ID_AWAY; else if (type & AIM_ICQ_STATE_INVISIBLE) status_id = OSCAR_STATUS_ID_INVISIBLE; + else if (type & AIM_ICQ_STATE_EVIL) + status_id = OSCAR_STATUS_ID_EVIL; + else if (type & AIM_ICQ_STATE_DEPRESSION) + status_id = OSCAR_STATUS_ID_DEPRESSION; + else if (type & AIM_ICQ_STATE_ATHOME) + status_id = OSCAR_STATUS_ID_ATHOME; + else if (type & AIM_ICQ_STATE_ATWORK) + status_id = OSCAR_STATUS_ID_ATWORK; + else if (type & AIM_ICQ_STATE_LUNCH) + status_id = OSCAR_STATUS_ID_LUNCH; else status_id = OSCAR_STATUS_ID_AVAILABLE; } else { @@ -2552,7 +2592,9 @@ { PurpleConnection *gc; PurpleAccount *account; + PurpleMessageFlags flags = 0; char *message = NULL; + char *rtfmsg = NULL; g_return_val_if_fail(od != NULL, 0); g_return_val_if_fail(od->gc != NULL, 0); @@ -2582,6 +2624,20 @@ } } + if (args->info.rtfmsg.rtfmsg != NULL) + { + if (args->encoding != NULL) + { + char *encoding = NULL; + encoding = oscar_encoding_extract(args->encoding); + rtfmsg = oscar_encoding_to_utf8(account, encoding, args->info.rtfmsg.rtfmsg, + strlen(args->info.rtfmsg.rtfmsg)); + g_free(encoding); + } else { + if (g_utf8_validate(args->info.rtfmsg.rtfmsg, strlen(args->info.rtfmsg.rtfmsg), NULL)) + rtfmsg = g_strdup(args->info.rtfmsg.rtfmsg); + } + } if (args->type & OSCAR_CAPABILITY_CHAT) { char *encoding, *utf8name, *tmp; @@ -2667,10 +2723,28 @@ else if (args->type & OSCAR_CAPABILITY_ICQSERVERRELAY) { - purple_debug_error("oscar", "Got an ICQ Server Relay message of " + purple_debug_info("oscar", "Got an ICQ Server Relay message of " "type %d\n", args->info.rtfmsg.msgtype); - } - + purple_debug_info("oscar", "Sending X-Status Reply\n"); + + if(args->info.rtfmsg.msgtype == 26) + icq_relay_xstatus(od, userinfo->bn, args->cookie); + + if(args->info.rtfmsg.msgtype == 1) + { + if(rtfmsg) + { + serv_got_im(gc, userinfo->bn, rtfmsg, flags, + time(NULL)); + } + else + { + serv_got_im(gc, userinfo->bn, + args->info.rtfmsg.rtfmsg, flags, + time(NULL)); + } + } + } else { purple_debug_error("oscar", "Unknown request class %hu\n", @@ -3250,6 +3324,29 @@ } break; + case 0x0006: { /* Reply from an ICQ status message request */ + char *statusmsg, **splitmsg; + PurpleNotifyUserInfo *user_info; + + /* Split at (carriage return/newline)'s, then rejoin later with BRs between. */ + statusmsg = oscar_icqstatus(state); + splitmsg = g_strsplit(msg, "\r\n", 0); + + user_info = purple_notify_user_info_new(); + + purple_notify_user_info_add_pair(user_info, _("UIN"), who); + purple_notify_user_info_add_pair(user_info, _("Status"), statusmsg); + purple_notify_user_info_add_section_break(user_info); + purple_notify_user_info_add_pair(user_info, NULL, g_strjoinv("<BR>", splitmsg)); + + g_free(statusmsg); + g_strfreev(splitmsg); + + purple_notify_userinfo(gc, who, user_info, NULL, NULL); + purple_notify_user_info_destroy(user_info); + + } break; + default: { purple_debug_warning("oscar", "Received an unknown client auto-response from %s. " @@ -3701,7 +3798,7 @@ if (utf8 == NULL) /* The conversion failed! */ utf8 = g_strdup(_("[Unable to display a message from this user because it contained invalid characters.]")); - serv_got_chat_in(gc, ccon->id, info->bn, 0, utf8, time((time_t)NULL)); + serv_got_chat_in(gc, ccon->id, info->bn, 0, utf8, time(NULL)); g_free(utf8); return 1; @@ -4848,6 +4945,16 @@ data |= AIM_ICQ_STATE_CHAT; else if (!strcmp(status_id, OSCAR_STATUS_ID_INVISIBLE)) data |= AIM_ICQ_STATE_INVISIBLE; + else if (!strcmp(status_id, OSCAR_STATUS_ID_EVIL)) + data |= AIM_ICQ_STATE_EVIL; + else if (!strcmp(status_id, OSCAR_STATUS_ID_DEPRESSION)) + data |= AIM_ICQ_STATE_DEPRESSION; + else if (!strcmp(status_id, OSCAR_STATUS_ID_ATWORK)) + data |= AIM_ICQ_STATE_ATWORK; + else if (!strcmp(status_id, OSCAR_STATUS_ID_ATHOME)) + data |= AIM_ICQ_STATE_ATHOME; + else if (!strcmp(status_id, OSCAR_STATUS_ID_LUNCH)) + data |= AIM_ICQ_STATE_LUNCH; else if (!strcmp(status_id, OSCAR_STATUS_ID_CUSTOM)) data |= AIM_ICQ_STATE_OUT | AIM_ICQ_STATE_AWAY; @@ -5009,6 +5116,12 @@ pc = purple_account_get_connection(account); od = purple_connection_get_protocol_data(pc); + /* There's no need to do the stuff below for mood updates. */ + if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_MOOD) { + aim_locate_setcaps(od, purple_caps); + return; + } + /* Set the AIM-style away message for both AIM and ICQ accounts */ oscar_set_info_and_status(account, FALSE, NULL, TRUE, status); @@ -5961,6 +6074,11 @@ return 0; } +PurpleMood* oscar_get_purple_moods(PurpleAccount *account) +{ + return icq_get_purple_moods(account); +} + const char *oscar_list_icon_icq(PurpleAccount *a, PurpleBuddy *b) { const char *name = b ? purple_buddy_get_name(b) : NULL; @@ -6031,12 +6149,17 @@ return "admin"; if (userinfo->flags & AIM_FLAG_ACTIVEBUDDY) return "bot"; - if (userinfo->capabilities & OSCAR_CAPABILITY_HIPTOP) - return "hiptop"; if (userinfo->capabilities & OSCAR_CAPABILITY_SECUREIM) return "secure"; if (userinfo->icqinfo.status & AIM_ICQ_STATE_BIRTHDAY) return "birthday"; + + /* Make the mood icon override anything below this. */ + if (purple_presence_is_status_primitive_active(presence, PURPLE_STATUS_MOOD)) + return NULL; + + if (userinfo->capabilities & OSCAR_CAPABILITY_HIPTOP) + return "hiptop"; } return NULL; } @@ -6259,9 +6382,53 @@ purple_value_new(PURPLE_TYPE_STRING), NULL); status_types = g_list_prepend(status_types, type); - type = purple_status_type_new_full(PURPLE_STATUS_AVAILABLE, + type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, OSCAR_STATUS_ID_FREE4CHAT, - _("Free For Chat"), TRUE, is_icq, FALSE); + _("Free For Chat"), TRUE, is_icq, FALSE, + "message", _("Message"), + purple_value_new(PURPLE_TYPE_STRING), NULL); + + status_types = g_list_prepend(status_types, type); + + type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, + OSCAR_STATUS_ID_EVIL, + _("Evil"), TRUE, is_icq, FALSE, + "message", _("Message"), + purple_value_new(PURPLE_TYPE_STRING), NULL); + status_types = g_list_prepend(status_types, type); + + + type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, + OSCAR_STATUS_ID_DEPRESSION, + _("Depression"), TRUE, is_icq, FALSE, + "message", _("Message"), + purple_value_new(PURPLE_TYPE_STRING), NULL); + status_types = g_list_prepend(status_types, type); + + + type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, + OSCAR_STATUS_ID_ATHOME, + _("At home"), TRUE, is_icq, FALSE, + "message", _("Message"), + purple_value_new(PURPLE_TYPE_STRING), NULL); + status_types = g_list_prepend(status_types, type); + + + type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, + OSCAR_STATUS_ID_ATWORK, + _("At work"), TRUE, is_icq, FALSE, + "message", _("Message"), + purple_value_new(PURPLE_TYPE_STRING), NULL); + + status_types = g_list_prepend(status_types, type); + + + type = purple_status_type_new_with_attrs(PURPLE_STATUS_AVAILABLE, + OSCAR_STATUS_ID_LUNCH, + _("Lunch"), TRUE, is_icq, FALSE, + "message", _("Message"), + purple_value_new(PURPLE_TYPE_STRING), NULL); + status_types = g_list_prepend(status_types, type); type = purple_status_type_new_with_attrs(PURPLE_STATUS_AWAY, @@ -6271,9 +6438,12 @@ purple_value_new(PURPLE_TYPE_STRING), NULL); status_types = g_list_prepend(status_types, type); - type = purple_status_type_new_full(PURPLE_STATUS_INVISIBLE, + type = purple_status_type_new_with_attrs(PURPLE_STATUS_INVISIBLE, OSCAR_STATUS_ID_INVISIBLE, - NULL, TRUE, TRUE, FALSE); + NULL, TRUE, TRUE, FALSE, + "message", _("Message"), + purple_value_new(PURPLE_TYPE_STRING), NULL); + status_types = g_list_prepend(status_types, type); type = purple_status_type_new_full(PURPLE_STATUS_MOBILE, OSCAR_STATUS_ID_MOBILE, NULL, FALSE, FALSE, TRUE); @@ -6306,9 +6476,14 @@ NULL, TRUE, TRUE, FALSE); status_types = g_list_prepend(status_types, type); - status_types = g_list_reverse(status_types); - - return status_types; + type = purple_status_type_new_with_attrs(PURPLE_STATUS_MOOD, + "mood", NULL, TRUE, is_icq, TRUE, + PURPLE_MOOD_NAME, _("Mood Name"), purple_value_new(PURPLE_TYPE_STRING), + PURPLE_MOOD_COMMENT, _("Mood Comment"), purple_value_new(PURPLE_TYPE_STRING), + NULL); + status_types = g_list_prepend(status_types, type); + + return g_list_reverse(status_types); } static void oscar_ssi_editcomment(struct name_data *data, const char *text) { @@ -6480,6 +6655,23 @@ } } +static void oscar_get_icqxstatusmsg (PurpleBlistNode *node, gpointer ignore) +{ + PurpleBuddy *buddy; + PurpleConnection *gc; + PurpleAccount *account; + + + g_return_if_fail(PURPLE_BLIST_NODE_IS_BUDDY(node)); + + buddy = (PurpleBuddy *)node; + gc = purple_account_get_connection(buddy->account); + account = purple_connection_get_account(gc); + purple_debug_info("oscar", "Manual X-Status Get From %s to %s:\n", purple_buddy_get_name(buddy), account->username); + + icq_im_xstatus_request(gc->proto_data, purple_buddy_get_name(buddy)); +} + static void oscar_get_aim_info_cb(PurpleBlistNode *node, gpointer ignore) { @@ -6529,15 +6721,13 @@ menu = g_list_prepend(menu, act); } -#if 0 if (od->icq) { - act = purple_menu_action_new(_("Get Status Msg"), - PURPLE_CALLBACK(oscar_get_icqstatusmsg), + act = purple_menu_action_new(_("Get X-Status Msg"), + PURPLE_CALLBACK(oscar_get_icqxstatusmsg), NULL, NULL); menu = g_list_prepend(menu, act); } -#endif if (userinfo && oscar_util_name_compare(purple_account_get_username(account), bname) &&
--- a/libpurple/protocols/oscar/oscar.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/oscar/oscar.h Tue Mar 09 21:41:41 2010 +0000 @@ -375,8 +375,12 @@ OSCAR_CAPABILITY_CAMERA = 0x04000000, OSCAR_CAPABILITY_ICHAT_SCREENSHARE = 0x08000000, OSCAR_CAPABILITY_TYPING = 0x10000000, - OSCAR_CAPABILITY_GENERICUNKNOWN = 0x20000000, - OSCAR_CAPABILITY_LAST = 0x40000000 + OSCAR_CAPABILITY_NEWCAPS = 0x20000000, + OSCAR_CAPABILITY_XTRAZ = 0x40000000, + OSCAR_CAPABILITY_GENERICUNKNOWN = 0x80000000, +#warning Fix OSCAR_CAPABILITY_LAST situation + // TODO: We're out of bits. Rework things that depend on this or remove some capability. (Or, ensure this is a 64-bit type.) + OSCAR_CAPABILITY_LAST = 0x100000000 } OscarCapability; /* @@ -572,6 +576,12 @@ #define AIM_ICQ_STATE_BUSY 0x00000010 #define AIM_ICQ_STATE_CHAT 0x00000020 #define AIM_ICQ_STATE_INVISIBLE 0x00000100 +#define AIM_ICQ_STATE_EVIL 0x00003000 +#define AIM_ICQ_STATE_DEPRESSION 0x00004000 +#define AIM_ICQ_STATE_ATHOME 0x00005000 +#define AIM_ICQ_STATE_ATWORK 0x00006000 +#define AIM_ICQ_STATE_LUNCH 0x00002001 +#define AIM_ICQ_STATE_EVIL 0x00003000 #define AIM_ICQ_STATE_WEBAWARE 0x00010000 #define AIM_ICQ_STATE_HIDEIP 0x00020000 #define AIM_ICQ_STATE_BIRTHDAY 0x00080000 @@ -1012,7 +1022,8 @@ /* 0x0008 */ int aim_im_warn(OscarData *od, FlapConnection *conn, const char *destbn, guint32 flags); /* 0x000b */ int aim_im_denytransfer(OscarData *od, const char *bn, const guchar *cookie, guint16 code); /* 0x0010 */ int aim_im_reqofflinemsgs(OscarData *od); -/* 0x0014 */ int aim_im_sendmtn(OscarData *od, guint16 channel, const char *bn, guint16 event); +/* 0x0014 */ int aim_im_sendmtn(OscarData *od, guint16 type1, const char *bn, guint16 type2); +/* 0x000b */ int icq_relay_xstatus (OscarData *od, const char *sn, const guchar* cookie); void aim_icbm_makecookie(guchar* cookie); gchar *oscar_encoding_extract(const char *encoding); gchar *oscar_encoding_to_utf8(PurpleAccount *account, const char *encoding, const char *text, int textlen); @@ -1144,8 +1155,10 @@ void aim_info_free(aim_userinfo_t *); int aim_info_extract(OscarData *od, ByteStream *bs, aim_userinfo_t *); int aim_putuserinfo(ByteStream *bs, aim_userinfo_t *info); - - +PurpleMood* icq_get_purple_moods(PurpleAccount *account); +const char* icq_get_custom_icon_description(const char *mood); +guint8* icq_get_custom_icon_data(const char *mood); +int icq_im_xstatus_request(OscarData *od, const char *sn); /* 0x0003 - family_buddy.c */ /* 0x0002 */ void aim_buddylist_reqrights(OscarData *, FlapConnection *); @@ -1474,7 +1487,7 @@ int aim_tlvlist_add_16(GSList **list, const guint16 type, const guint16 value); int aim_tlvlist_add_32(GSList **list, const guint16 type, const guint32 value); int aim_tlvlist_add_str(GSList **list, const guint16 type, const char *value); -int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps); +int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps, const char *mood); int aim_tlvlist_add_userinfo(GSList **list, guint16 type, aim_userinfo_t *userinfo); int aim_tlvlist_add_chatroom(GSList **list, guint16 type, guint16 exchange, const char *roomname, guint16 instance); int aim_tlvlist_add_frozentlvlist(GSList **list, guint16 type, GSList **tl);
--- a/libpurple/protocols/oscar/oscarcommon.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/oscar/oscarcommon.h Tue Mar 09 21:41:41 2010 +0000 @@ -30,6 +30,7 @@ #include "prpl.h" #include "version.h" #include "notify.h" +#include "status.h" #define OSCAR_DEFAULT_LOGIN_SERVER "login.messaging.aol.com" #define OSCAR_DEFAULT_LOGIN_PORT 5190 @@ -51,6 +52,7 @@ #ifdef _WIN32 const char *oscar_get_locale_charset(void); #endif +PurpleMood* oscar_get_purple_moods(PurpleAccount *account); const char *oscar_list_icon_icq(PurpleAccount *a, PurpleBuddy *b); const char *oscar_list_icon_aim(PurpleAccount *a, PurpleBuddy *b); const char* oscar_list_emblem(PurpleBuddy *b);
--- a/libpurple/protocols/oscar/tlv.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/oscar/tlv.c Tue Mar 09 21:41:41 2010 +0000 @@ -407,10 +407,11 @@ * @param caps Bitfield of capability flags to send * @return The size of the value added. */ -int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps) +int aim_tlvlist_add_caps(GSList **list, const guint16 type, const guint32 caps, const char *mood) { guint8 buf[256]; /* TODO: Don't use a fixed length buffer */ ByteStream bs; + guint8 *data; if (caps == 0) return 0; /* nothing there anyway */ @@ -418,6 +419,11 @@ byte_stream_init(&bs, buf, sizeof(buf)); byte_stream_putcaps(&bs, caps); + + /* adding of custom icon GUID */ + data = icq_get_custom_icon_data(mood); + if (data != NULL) + byte_stream_putraw(&bs, data, 16); return aim_tlvlist_add_raw(list, type, byte_stream_curpos(&bs), buf); }
--- a/libpurple/protocols/qq/qq.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/qq/qq.c Tue Mar 09 21:41:41 2010 +0000 @@ -1037,7 +1037,8 @@ sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL, /* get_account_text_table */ NULL, /* initiate_media */ - NULL /* can_do_media */ + NULL, /* get_media_caps */ + NULL /* get_moods */ }; static PurplePluginInfo info = {
--- a/libpurple/protocols/sametime/sametime.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/sametime/sametime.c Tue Mar 09 21:41:41 2010 +0000 @@ -3527,10 +3527,10 @@ purple_request_field_list_set_multi_select(f, FALSE); for(; confs; confs = confs->next) { struct mwConference *c = confs->data; - purple_request_field_list_add(f, mwConference_getTitle(c), c); + purple_request_field_list_add_icon(f, mwConference_getTitle(c), NULL, c); } - purple_request_field_list_add(f, _("Create New Conference..."), - GINT_TO_POINTER(0x01)); + purple_request_field_list_add_icon(f, _("Create New Conference..."), + NULL, GINT_TO_POINTER(0x01)); purple_request_field_group_add_field(g, f); f = purple_request_field_string_new(CHAT_KEY_INVITE, "Message", NULL, FALSE); @@ -5458,7 +5458,7 @@ res->id = g_strdup(match->id); res->name = g_strdup(match->name); - purple_request_field_list_add(f, res->name, res); + purple_request_field_list_add_icon(f, res->name, NULL, res); } purple_request_field_group_add_field(g, f);
--- a/libpurple/protocols/silc/buddy.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/silc/buddy.c Tue Mar 09 21:41:41 2010 +0000 @@ -1183,7 +1183,7 @@ client_entry->username, *client_entry->hostname ? client_entry->hostname : "", fingerprint ? tmp2 : ""); - purple_request_field_list_add(f, tmp, client_entry); + purple_request_field_list_add_icon(f, tmp, NULL, client_entry); silc_free(fingerprint); }
--- a/libpurple/protocols/silc/chat.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/silc/chat.c Tue Mar 09 21:41:41 2010 +0000 @@ -475,7 +475,7 @@ g_snprintf(tmp2, sizeof(tmp2), "%s\n %s\n %s", ident->realname ? ident->realname : ident->username ? ident->username : "", fingerprint, babbleprint); - purple_request_field_list_add(f, tmp2, public_key); + purple_request_field_list_add_icon(f, tmp2, NULL, public_key); silc_free(fingerprint); silc_free(babbleprint);
--- a/libpurple/protocols/silc/silc.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/silc/silc.c Tue Mar 09 21:41:41 2010 +0000 @@ -2116,7 +2116,8 @@ sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL, /* get_account_text_table */ NULL, /* initiate_media */ - NULL /* can_do_media */ + NULL, /* get_media_caps */ + NULL /* get_moods */ }; static PurplePluginInfo info =
--- a/libpurple/protocols/silc10/buddy.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/silc10/buddy.c Tue Mar 09 21:41:41 2010 +0000 @@ -1176,7 +1176,7 @@ clients[i]->username, clients[i]->hostname ? clients[i]->hostname : "", fingerprint ? tmp2 : ""); - purple_request_field_list_add(f, tmp, clients[i]); + purple_request_field_list_add_icon(f, tmp, NULL, clients[i]); silc_free(fingerprint); }
--- a/libpurple/protocols/silc10/chat.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/silc10/chat.c Tue Mar 09 21:41:41 2010 +0000 @@ -449,7 +449,7 @@ g_snprintf(tmp2, sizeof(tmp2), "%s\n %s\n %s", ident->realname ? ident->realname : ident->username ? ident->username : "", fingerprint, babbleprint); - purple_request_field_list_add(f, tmp2, pubkey); + purple_request_field_list_add_icon(f, tmp2, NULL, pubkey); silc_free(fingerprint); silc_free(babbleprint);
--- a/libpurple/protocols/simple/simple.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/simple/simple.c Tue Mar 09 21:41:41 2010 +0000 @@ -2086,7 +2086,8 @@ sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL, /* get_account_text_table */ NULL, /* initiate_media */ - NULL /* can_do_media */ + NULL, /* get_media_caps */ + NULL /* get_moods */ };
--- a/libpurple/protocols/yahoo/libyahoo.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/yahoo/libyahoo.c Tue Mar 09 21:41:41 2010 +0000 @@ -264,7 +264,8 @@ sizeof(PurplePluginProtocolInfo), /* struct_size */ yahoo_get_account_text_table, /* get_account_text_table */ NULL, /* initiate_media */ - NULL /* can_do_media */ + NULL, /* get_media_caps */ + NULL /* get_moods */ }; static PurplePluginInfo info =
--- a/libpurple/protocols/yahoo/libyahoojp.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/yahoo/libyahoojp.c Tue Mar 09 21:41:41 2010 +0000 @@ -160,7 +160,8 @@ sizeof(PurplePluginProtocolInfo), /* struct_size */ yahoojp_get_account_text_table, /* get_account_text_table */ NULL, /* initiate_media */ - NULL /* can_do_media */ + NULL, /* get_media_caps */ + NULL /* get_moods */ }; static PurplePluginInfo info =
--- a/libpurple/protocols/zephyr/zephyr.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/protocols/zephyr/zephyr.c Tue Mar 09 21:41:41 2010 +0000 @@ -2908,7 +2908,8 @@ sizeof(PurplePluginProtocolInfo), /* struct_size */ NULL, /* get_account_text_table */ NULL, /* initate_media */ - NULL /* can_do_media */ + NULL, /* get_media_caps */ + NULL /* get_moods */ }; static PurplePluginInfo info = {
--- a/libpurple/prpl.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/prpl.h Tue Mar 09 21:41:41 2010 +0000 @@ -569,6 +569,12 @@ */ PurpleMediaCaps (*get_media_caps)(PurpleAccount *account, const char *who); + + /** + * Returns an array of "PurpleMood"s, with the last one having + * "mood" set to @c NULL. + */ + PurpleMood *(*get_moods)(PurpleAccount *account); }; #define PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl, member) \
--- a/libpurple/request.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/request.c Tue Mar 09 21:41:41 2010 +0000 @@ -850,13 +850,40 @@ purple_request_field_list_add(PurpleRequestField *field, const char *item, void *data) { + purple_request_field_list_add_icon(field, item, NULL, data); +} + +void +purple_request_field_list_add_icon(PurpleRequestField *field, const char *item, const char* icon_path, + void *data) +{ g_return_if_fail(field != NULL); g_return_if_fail(item != NULL); g_return_if_fail(data != NULL); g_return_if_fail(field->type == PURPLE_REQUEST_FIELD_LIST); + if (icon_path) + { + if (field->u.list.icons == NULL) + { + GList *l; + for (l = field->u.list.items ; l != NULL ; l = l->next) + { + /* Order doesn't matter, because we're just + * filing in blank items. So, we use + * g_list_prepend() because it's faster. */ + field->u.list.icons = g_list_prepend(field->u.list.icons, NULL); + } + } + field->u.list.icons = g_list_append(field->u.list.icons, g_strdup(icon_path)); + } + else if (field->u.list.icons) + { + /* Keep this even with the items list. */ + field->u.list.icons = g_list_append(field->u.list.icons, NULL); + } + field->u.list.items = g_list_append(field->u.list.items, g_strdup(item)); - g_hash_table_insert(field->u.list.item_data, g_strdup(item), data); } @@ -962,6 +989,15 @@ return field->u.list.items; } +GList * +purple_request_field_list_get_icons(const PurpleRequestField *field) +{ + g_return_val_if_fail(field != NULL, NULL); + g_return_val_if_fail(field->type == PURPLE_REQUEST_FIELD_LIST, NULL); + + return field->u.list.icons; +} + PurpleRequestField * purple_request_field_label_new(const char *id, const char *text) {
--- a/libpurple/request.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/request.h Tue Mar 09 21:41:41 2010 +0000 @@ -150,6 +150,7 @@ struct { GList *items; + GList *icons; GHashTable *item_data; GList *selected; GHashTable *selected_table; @@ -953,11 +954,24 @@ * @param field The list field. * @param item The list item. * @param data The associated data. + * + * @deprecated Use purple_request_field_list_add_icon() instead. */ void purple_request_field_list_add(PurpleRequestField *field, const char *item, void *data); /** + * Adds an item to a list field. + * + * @param field The list field. + * @param item The list item. + * @param icon_path The path to icon file, or @c NULL for no icon. + * @param data The associated data. + */ +void purple_request_field_list_add_icon(PurpleRequestField *field, + const char *item, const char* icon_path, void* data); + +/** * Adds a selected item to the list field. * * @param field The field. @@ -1015,6 +1029,18 @@ */ GList *purple_request_field_list_get_items(const PurpleRequestField *field); +/** + * Returns a list of icons in a list field. + * + * The icons will correspond with the items, in order. + * + * @param field The field. + * + * @constreturn The list of icons or @c NULL (i.e. the empty GList) if no + * items have icons. + */ +GList *purple_request_field_list_get_icons(const PurpleRequestField *field); + /*@}*/ /**************************************************************************/
--- a/libpurple/signals.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/signals.c Tue Mar 09 21:41:41 2010 +0000 @@ -1009,6 +1009,21 @@ } void +purple_marshal_POINTER__POINTER( + PurpleCallback cb, va_list args, void *data, + void **return_val) +{ + gpointer ret_val; + void *arg1 = va_arg(args, void *); + + ret_val = ((gpointer(*)(void *, void *))cb)(arg1, data); + + if (return_val != NULL) + *return_val = ret_val; +} + + +void purple_marshal_POINTER__POINTER_INT( PurpleCallback cb, va_list args, void *data, void **return_val)
--- a/libpurple/signals.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/signals.h Tue Mar 09 21:41:41 2010 +0000 @@ -355,6 +355,8 @@ void purple_marshal_BOOLEAN__INT_POINTER( PurpleCallback cb, va_list args, void *data, void **return_val); +void purple_marshal_POINTER__POINTER( + PurpleCallback cb, va_list args, void *data, void **return_val); void purple_marshal_POINTER__POINTER_INT( PurpleCallback cb, va_list args, void *data, void **return_val); void purple_marshal_POINTER__POINTER_INT64(
--- a/libpurple/status.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/status.c Tue Mar 09 21:41:41 2010 +0000 @@ -137,6 +137,7 @@ -200, /* extended away */ -400, /* mobile */ 0, /* tune */ + 0, /* mood */ -10, /* idle, special case. */ -5, /* idle time, special case. */ 10 /* Offline messageable */ @@ -157,15 +158,16 @@ } const status_primitive_map[] = { - { PURPLE_STATUS_UNSET, "unset", N_("Unset") }, - { PURPLE_STATUS_OFFLINE, "offline", N_("Offline") }, - { PURPLE_STATUS_AVAILABLE, "available", N_("Available") }, - { PURPLE_STATUS_UNAVAILABLE, "unavailable", N_("Do not disturb") }, - { PURPLE_STATUS_INVISIBLE, "invisible", N_("Invisible") }, - { PURPLE_STATUS_AWAY, "away", N_("Away") }, - { PURPLE_STATUS_EXTENDED_AWAY, "extended_away", N_("Extended away") }, - { PURPLE_STATUS_MOBILE, "mobile", N_("Mobile") }, - { PURPLE_STATUS_TUNE, "tune", N_("Listening to music") } + { PURPLE_STATUS_UNSET, "unset", N_("Unset") }, + { PURPLE_STATUS_OFFLINE, "offline", N_("Offline") }, + { PURPLE_STATUS_AVAILABLE, "available", N_("Available") }, + { PURPLE_STATUS_UNAVAILABLE, "unavailable", N_("Do not disturb") }, + { PURPLE_STATUS_INVISIBLE, "invisible", N_("Invisible") }, + { PURPLE_STATUS_AWAY, "away", N_("Away") }, + { PURPLE_STATUS_EXTENDED_AWAY, "extended_away", N_("Extended away") }, + { PURPLE_STATUS_MOBILE, "mobile", N_("Mobile") }, + { PURPLE_STATUS_TUNE, "tune", N_("Listening to music"), }, + { PURPLE_STATUS_MOOD, "mood", N_("Feeling") }, }; const char *
--- a/libpurple/status.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/status.h Tue Mar 09 21:41:41 2010 +0000 @@ -87,6 +87,12 @@ typedef struct _PurplePresence PurplePresence; typedef struct _PurpleStatus PurpleStatus; +typedef struct _PurpleMood { + const char *mood; + const char *description; + gpointer *padding; +} PurpleMood; + /** * A context for a presence. * @@ -106,8 +112,7 @@ */ /* * If you add a value to this enum, make sure you update - * the status_primitive_map array in status.c and the special-cases for idle - * and offline-messagable just below it. + * the status_primitive_map and primitive_scores arrays in status.c. */ typedef enum { @@ -120,6 +125,7 @@ PURPLE_STATUS_EXTENDED_AWAY, PURPLE_STATUS_MOBILE, PURPLE_STATUS_TUNE, + PURPLE_STATUS_MOOD, PURPLE_STATUS_NUM_PRIMITIVES } PurpleStatusPrimitive; @@ -139,6 +145,9 @@ #define PURPLE_TUNE_URL "tune_url" #define PURPLE_TUNE_FULL "tune_full" +#define PURPLE_MOOD_NAME "mood" +#define PURPLE_MOOD_COMMENT "moodtext" + #ifdef __cplusplus extern "C" { #endif
--- a/libpurple/upnp.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/upnp.c Tue Mar 09 21:41:41 2010 +0000 @@ -542,7 +542,7 @@ len = recv(dd->fd, buf, sizeof(buf) - 1, 0); - if(len > 0) { + if(len >= 0) { buf[len] = '\0'; break; } else if(errno != EINTR) {
--- a/libpurple/win32/global.mak Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/win32/global.mak Tue Mar 09 21:41:41 2010 +0000 @@ -10,15 +10,14 @@ # Locations of our various dependencies WIN32_DEV_TOP ?= $(PIDGIN_TREE_TOP)/../win32-dev -ASPELL_TOP ?= $(WIN32_DEV_TOP)/aspell-dev-0-50-3-3 -GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.11-daa1 +GTKSPELL_TOP ?= $(WIN32_DEV_TOP)/gtkspell-2.0.16 +ENCHANT_TOP ?= $(WIN32_DEV_TOP)/enchant_1.5.0-2_win32 GTK_TOP ?= $(WIN32_DEV_TOP)/gtk_2_0-2.14 GTK_BIN ?= $(GTK_TOP)/bin BONJOUR_TOP ?= $(WIN32_DEV_TOP)/Bonjour_SDK -LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.6.30 +LIBXML2_TOP ?= $(WIN32_DEV_TOP)/libxml2-2.7.4 MEANWHILE_TOP ?= $(WIN32_DEV_TOP)/meanwhile-1.0.2_daa2 -NSPR_TOP ?= $(WIN32_DEV_TOP)/nspr-4.6.4 -NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.11.4 +NSS_TOP ?= $(WIN32_DEV_TOP)/nss-3.12.5-nspr-4.8.2 PERL_LIB_TOP ?= $(WIN32_DEV_TOP)/perl-5.10.0 SILC_TOOLKIT ?= $(WIN32_DEV_TOP)/silc-toolkit-1.1.8 TCL_LIB_TOP ?= $(WIN32_DEV_TOP)/tcl-8.4.5 @@ -103,7 +102,7 @@ ifeq "$(origin CC)" "default" CC := gcc.exe endif -GMSGFMT ?= $(GTK_BIN)/msgfmt +GMSGFMT ?= $(WIN32_DEV_TOP)/gettext-0.17/bin/msgfmt MAKENSIS ?= makensis.exe MAKENSISOPT ?= / PERL ?= /cygdrive/c/perl/bin/perl
--- a/libpurple/win32/libc_interface.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/win32/libc_interface.c Tue Mar 09 21:41:41 2010 +0000 @@ -33,6 +33,7 @@ #include "libc_internal.h" #include <glib/gstdio.h> +/** This is redefined here because we can't include internal.h */ #ifdef ENABLE_NLS # include <locale.h> # include <libintl.h> @@ -61,7 +62,7 @@ /* helpers */ static int wpurple_is_socket( int fd ) { int optval; - unsigned int optlen = sizeof(int); + int optlen = sizeof(int); if( (getsockopt(fd, SOL_SOCKET, SO_TYPE, (void*)&optval, &optlen)) == SOCKET_ERROR ) { int error = WSAGetLastError(); @@ -972,7 +973,7 @@ memset(zonename, 0, sizeof(zonename)); namesize = sizeof(zonename); - if ((r = RegQueryValueEx(key, "Std", NULL, NULL, zonename, &namesize)) != ERROR_SUCCESS) + if ((r = RegQueryValueEx(key, "Std", NULL, NULL, (LPBYTE)zonename, &namesize)) != ERROR_SUCCESS) { purple_debug_warning("wpurple", "could not query value for 'std' to identify Windows timezone: %i\n", (int) r); RegCloseKey(key); @@ -987,7 +988,7 @@ } memset(zonename, 0, sizeof(zonename)); namesize = sizeof(zonename); - if ((r = RegQueryValueEx(key, "Dlt", NULL, NULL, zonename, &namesize)) != ERROR_SUCCESS) + if ((r = RegQueryValueEx(key, "Dlt", NULL, NULL, (LPBYTE)zonename, &namesize)) != ERROR_SUCCESS) { purple_debug_warning("wpurple", "could not query value for 'dlt' to identify Windows timezone: %i\n", (int) r); RegCloseKey(key); @@ -1047,29 +1048,12 @@ return ""; } +int wpurple_g_access (const gchar *filename, int mode); /** - * g_access: - * @filename: a pathname in the GLib file name encoding (UTF-8 on Windows) - * @mode: as in access() - * - * A wrapper for the POSIX access() function. This function is used to - * test a pathname for one or several of read, write or execute - * permissions, or just existence. On Windows, the underlying access() - * function in the C library only checks the READONLY attribute, and - * does not look at the ACL at all. Software that needs to handle file - * permissions on Windows more exactly should use the Win32 API. - * - * See the C library manual for more details about access(). - * - * Returns: zero if the pathname refers to an existing file system - * object that has all the tested permissions, or -1 otherwise or on - * error. - * - * Since: 2.8 + * @deprecated - remove for 3.0.0 */ int -wpurple_g_access (const gchar *filename, - int mode) +wpurple_g_access (const gchar *filename, int mode) { return g_access(filename, mode); }
--- a/libpurple/win32/libc_interface.h Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/win32/libc_interface.h Tue Mar 09 21:41:41 2010 +0000 @@ -129,19 +129,14 @@ wpurple_gettimeofday( timeval, timezone ) /* stdio.h */ +#undef snprintf #define snprintf _snprintf +#undef vsnprintf #define vsnprintf _vsnprintf #define rename( oldname, newname ) \ wpurple_rename( oldname, newname ) -#ifdef g_rename -# undef g_rename -#endif -/* This is necessary because we want rename on win32 to be able to overwrite an existing file, it is done in internal.h if GLib < 2.6*/ -#define g_rename(oldname, newname) \ -wpurple_rename(oldname, newname) - /* sys/stat.h */ #define fchmod(a,b)
--- a/libpurple/win32/win32dep.c Thu Feb 18 22:42:19 2010 +0000 +++ b/libpurple/win32/win32dep.c Tue Mar 09 21:41:41 2010 +0000 @@ -22,7 +22,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA * */ -#define _WIN32_IE 0x500 +#define _WIN32_IE 0x501 #include "internal.h" #include <winuser.h>
--- a/pidgin/Makefile.am Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/Makefile.am Tue Mar 09 21:41:41 2010 +0000 @@ -21,6 +21,7 @@ win32/winpidgin.c \ win32/wspell.c \ win32/wspell.h \ + win32/nsis/generate_gtk_zip.sh \ win32/nsis/pixmaps/pidgin-header.bmp \ win32/nsis/pixmaps/pidgin-intro.bmp \ win32/nsis/pixmaps/pidgin-install.ico \ @@ -89,7 +90,6 @@ gtkdnd-hints.c \ gtkdocklet.c \ gtkdocklet-gtk.c \ - gtkdocklet-x11.c \ gtkeventloop.c \ gtkft.c \ gtkicon-theme.c \
--- a/pidgin/Makefile.mingw Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/Makefile.mingw Tue Mar 09 21:41:41 2010 +0000 @@ -9,8 +9,6 @@ DEFINES := $(subst -DWIN32_LEAN_AND_MEAN,,$(DEFINES)) -NEEDED_DLLS = $(GTKSPELL_TOP)/gtkspell/libgtkspell.dll - ## ## VARIABLE DEFINITIONS ## @@ -43,14 +41,12 @@ -I$(GTK_TOP)/include/atk-1.0 \ -I$(GTK_TOP)/include/cairo \ -I$(GTK_TOP)/lib/gtk-2.0/include \ - -I$(GTKSPELL_TOP) \ - -I$(ASPELL_TOP)/include + -I$(GTKSPELL_TOP)/include/gtkspell-2.0 LIB_PATHS += -L$(GTK_TOP)/lib \ -L$(PURPLE_TOP) \ -L$(PIDGIN_TOP) \ - -L$(PIDGIN_IDLETRACK_TOP) \ - -L$(ASPELL_TOP)/lib + -L$(PIDGIN_IDLETRACK_TOP) ## ## SOURCES, OBJECTS @@ -151,7 +147,6 @@ install_shallow: $(PIDGIN_INSTALL_DIR) $(EXE_TARGET).exe $(PIDGIN_TARGET).dll cp $(EXE_TARGET).exe $(PIDGIN_TARGET).dll $(PIDGIN_INSTALL_DIR) - cp $(NEEDED_DLLS) $(PIDGIN_INSTALL_DIR) install: install_shallow all $(MAKE) -C $(PIDGIN_PLUGINS_TOP) -f $(MINGW_MAKEFILE) install
--- a/pidgin/gtkblist.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/gtkblist.c Tue Mar 09 21:41:41 2010 +0000 @@ -2660,39 +2660,30 @@ /* Altered from do_colorshift in gnome-panel */ static void -do_alphashift (GdkPixbuf *dest, GdkPixbuf *src, int shift) +do_alphashift(GdkPixbuf *pixbuf, int shift) { gint i, j; - gint width, height, has_alpha, srcrowstride, destrowstride; - guchar *target_pixels; - guchar *original_pixels; - guchar *pixsrc; - guchar *pixdest; + gint width, height, padding; + guchar *pixels; int val; - guchar a; - - has_alpha = gdk_pixbuf_get_has_alpha (src); - if (!has_alpha) + + if (!gdk_pixbuf_get_has_alpha(pixbuf)) return; - width = gdk_pixbuf_get_width (src); - height = gdk_pixbuf_get_height (src); - srcrowstride = gdk_pixbuf_get_rowstride (src); - destrowstride = gdk_pixbuf_get_rowstride (dest); - target_pixels = gdk_pixbuf_get_pixels (dest); - original_pixels = gdk_pixbuf_get_pixels (src); + width = gdk_pixbuf_get_width(pixbuf); + height = gdk_pixbuf_get_height(pixbuf); + padding = gdk_pixbuf_get_rowstride(pixbuf) - width * 4; + pixels = gdk_pixbuf_get_pixels(pixbuf); for (i = 0; i < height; i++) { - pixdest = target_pixels + i*destrowstride; - pixsrc = original_pixels + i*srcrowstride; for (j = 0; j < width; j++) { - *(pixdest++) = *(pixsrc++); - *(pixdest++) = *(pixsrc++); - *(pixdest++) = *(pixsrc++); - a = *(pixsrc++); - val = a - shift; - *(pixdest++) = CLAMP(val, 0, 255); - } + pixels++; + pixels++; + pixels++; + val = *pixels - shift; + *(pixels++) = CLAMP(val, 0, 255); + } + pixels += padding; } } @@ -3667,7 +3658,8 @@ /* Offline? */ - /* FIXME: Why is this status special-cased by the core? -- rlaager */ + /* FIXME: Why is this status special-cased by the core? --rlaager + * FIXME: Alternatively, why not have the core do all of them? --rlaager */ if (!PURPLE_BUDDY_IS_ONLINE(b)) { purple_notify_user_info_add_pair(user_info, _("Status"), _("Offline")); } @@ -3762,6 +3754,24 @@ return pb; } +static char *get_mood_icon_path(const char *mood) +{ + char *path; + + if (!strcmp(mood, "busy")) { + path = g_build_filename(DATADIR, "pixmaps", "pidgin", + "status", "16", "busy.png", NULL); + } else if (!strcmp(mood, "hiptop")) { + path = g_build_filename(DATADIR, "pixmaps", "pidgin", + "emblems", "16", "hiptop.png", NULL); + } else { + char *filename = g_strdup_printf("%s.png", mood); + path = g_build_filename(DATADIR, "pixmaps", "pidgin", + "emotes", "small", filename, NULL); + g_free(filename); + } + return path; +} GdkPixbuf * pidgin_blist_get_emblem(PurpleBlistNode *node) @@ -3773,7 +3783,7 @@ PurplePluginProtocolInfo *prpl_info; const char *name = NULL; char *filename, *path; - PurplePresence *p; + PurplePresence *p = NULL; PurpleStatus *tune; if(PURPLE_BLIST_NODE_IS_CONTACT(node)) { @@ -3786,14 +3796,17 @@ gtkbuddynode = node->ui_data; p = purple_buddy_get_presence(buddy); if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) { - path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", - "16", "mobile.png", NULL); + /* This emblem comes from the small emoticon set now, + * to reduce duplication. */ + path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emotes", + "small", "mobile.png", NULL); return _pidgin_blist_get_cached_emblem(path); } if (((struct _pidgin_blist_node*)(node->parent->ui_data))->contact_expanded) { - if (!purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons")) - return pidgin_create_prpl_icon(((PurpleBuddy*)node)->account, PIDGIN_PRPL_ICON_SMALL); + if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/blist/show_protocol_icons")) + return NULL; + return pidgin_create_prpl_icon(((PurpleBuddy*)node)->account, PIDGIN_PRPL_ICON_SMALL); } } else { return NULL; @@ -3806,9 +3819,14 @@ return _pidgin_blist_get_cached_emblem(path); } - p = purple_buddy_get_presence(buddy); + /* If we came through the contact code flow above, we didn't need + * to get the presence until now. */ + if (p == NULL) + p = purple_buddy_get_presence(buddy); + if (purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOBILE)) { - path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "mobile.png", NULL); + /* This emblem comes from the small emoticon set now, to reduce duplication. */ + path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emotes", "small", "mobile.png", NULL); return _pidgin_blist_get_cached_emblem(path); } @@ -3827,7 +3845,8 @@ return _pidgin_blist_get_cached_emblem(path); } /* Regular old "tune" is the only one in all protocols. */ - path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", "music.png", NULL); + /* This emblem comes from the small emoticon set now, to reduce duplication. */ + path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emotes", "small", "music.png", NULL); return _pidgin_blist_get_cached_emblem(path); } @@ -3839,13 +3858,24 @@ if (prpl_info && prpl_info->list_emblem) name = prpl_info->list_emblem(buddy); - if (name == NULL) - return NULL; - - filename = g_strdup_printf("%s.png", name); - - path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", filename, NULL); - g_free(filename); + if (name == NULL) { + PurpleStatus *status; + + if (!purple_presence_is_status_primitive_active(p, PURPLE_STATUS_MOOD)) + return NULL; + + status = purple_presence_get_status(p, "mood"); + name = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); + + if (!(name && *name)) + return NULL; + + path = get_mood_icon_path(name); + } else { + filename = g_strdup_printf("%s.png", name); + path = g_build_filename(DATADIR, "pixmaps", "pidgin", "emblems", "16", filename, NULL); + g_free(filename); + } /* _pidgin_blist_get_cached_emblem() assumes ownership of path */ return _pidgin_blist_get_cached_emblem(path); @@ -6328,7 +6358,7 @@ g_object_ref(G_OBJECT(gtkblist->empty_avatar)); avatar = gtkblist->empty_avatar; } else if ((!PURPLE_BUDDY_IS_ONLINE(buddy) || purple_presence_is_idle(presence))) { - do_alphashift(avatar, avatar, 77); + do_alphashift(avatar, 77); } emblem = pidgin_blist_get_emblem((PurpleBlistNode*) buddy); @@ -7785,6 +7815,101 @@ purple_account_set_enabled(account, PIDGIN_UI, FALSE); } +static void +edit_mood_cb(PurpleConnection *gc, PurpleRequestFields *fields) +{ + PurpleRequestField *mood_field, *text_field; + GList *l; + + mood_field = purple_request_fields_get_field(fields, "mood"); + text_field = purple_request_fields_get_field(fields, "text"); + l = purple_request_field_list_get_selected(mood_field); + + if (l) { + const char *mood = purple_request_field_list_get_data(mood_field, l->data); + const char *text = purple_request_field_string_get_value(text_field); + PurpleAccount *account = purple_connection_get_account(gc); + + if (mood != NULL && !purple_strequal(mood, "")) { + if (text) { + purple_account_set_status(account, "mood", TRUE, + PURPLE_MOOD_NAME, mood, + PURPLE_MOOD_COMMENT, text, + NULL); + } else { + purple_account_set_status(account, "mood", TRUE, + PURPLE_MOOD_NAME, mood, + NULL); + } + } else { + purple_account_set_status(account, "mood", FALSE, NULL); + } + } +} + +static void +set_mood_cb(GtkWidget *widget, PurpleAccount *account) +{ + PurplePresence *presence = purple_account_get_presence(account); + PurpleStatus *status = purple_presence_get_status(presence, "mood"); + const char *current_mood; + PurpleRequestFields *fields; + PurpleRequestFieldGroup *g; + PurpleRequestField *f; + PurpleConnection *gc = purple_account_get_connection(account); + PurplePluginProtocolInfo *prpl_info; + PurpleMood *mood; + + g_return_if_fail(gc->prpl != NULL); + prpl_info = PURPLE_PLUGIN_PROTOCOL_INFO(gc->prpl); + + current_mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); + + fields = purple_request_fields_new(); + g = purple_request_field_group_new(NULL); + f = purple_request_field_list_new("mood", _("Please select your mood from the list")); + + purple_request_field_list_add(f, _("None"), ""); + if (current_mood == NULL) + purple_request_field_list_add_selected(f, _("None")); + + /* TODO: rlaager wants this sorted. */ + for (mood = prpl_info->get_moods(account); + mood->mood != NULL ; mood++) { + char *path; + + if (mood->mood == NULL || mood->description == NULL) + continue; + + path = get_mood_icon_path(mood->mood); + purple_request_field_list_add_icon(f, _(mood->description), + path, (gpointer)mood->mood); + g_free(path); + + if (current_mood && !strcmp(current_mood, mood->mood)) + purple_request_field_list_add_selected(f, _(mood->description)); + } + purple_request_field_group_add_field(g, f); + + purple_request_fields_add_group(fields, g); + + /* if the connection allows setting a mood message */ + if (gc->flags & PURPLE_CONNECTION_SUPPORT_MOOD_MESSAGES) { + g = purple_request_field_group_new(NULL); + f = purple_request_field_string_new("text", + _("Message (optional)"), NULL, FALSE); + purple_request_field_group_add_field(g, f); + purple_request_fields_add_group(fields, g); + } + + purple_request_fields(gc, _("Edit User Mood"), _("Edit User Mood"), + NULL, fields, + _("OK"), G_CALLBACK(edit_mood_cb), + _("Cancel"), NULL, + purple_connection_get_account(gc), + NULL, NULL, gc); +} + void pidgin_blist_update_accounts_menu(void) { @@ -7864,6 +7989,7 @@ PurpleAccount *account = NULL; GdkPixbuf *pixbuf = NULL; PurplePlugin *plugin = NULL; + PurplePluginProtocolInfo *prpl_info; account = accounts->data; @@ -7903,8 +8029,34 @@ gc = purple_account_get_connection(account); plugin = gc && PURPLE_CONNECTION_IS_CONNECTED(gc) ? gc->prpl : NULL; - if (plugin && PURPLE_PLUGIN_HAS_ACTIONS(plugin)) { - build_plugin_actions(submenu, plugin, gc); + prpl_info = plugin ? PURPLE_PLUGIN_PROTOCOL_INFO(plugin) : NULL; + + if (prpl_info && + (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods) || + PURPLE_PLUGIN_HAS_ACTIONS(plugin))) { + if (PURPLE_PROTOCOL_PLUGIN_HAS_FUNC(prpl_info, get_moods) && + gc->flags & PURPLE_CONNECTION_SUPPORT_MOODS) { + GList *types; + + for (types = purple_account_get_status_types(account); + types != NULL ; types = types->next) { + PurpleStatusType *type = types->data; + + if (strcmp(purple_status_type_get_id(type), "mood") != 0) + continue; + + menuitem = gtk_menu_item_new_with_mnemonic(_("Set _Mood...")); + g_signal_connect(G_OBJECT(menuitem), "activate", + G_CALLBACK(set_mood_cb), account); + gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem); + + /* Be safe. It shouldn't match more than once anyway */ + break; + } + } + if (PURPLE_PLUGIN_HAS_ACTIONS(plugin)) { + build_plugin_actions(submenu, plugin, gc); + } } else { menuitem = gtk_menu_item_new_with_label(_("No actions available")); gtk_menu_shell_append(GTK_MENU_SHELL(submenu), menuitem);
--- a/pidgin/gtkdebug.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/gtkdebug.c Tue Mar 09 21:41:41 2010 +0000 @@ -250,11 +250,11 @@ static void pause_cb(GtkWidget *w, DebugWindow *win) { - win->paused = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)); + win->paused = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(w)); #ifdef HAVE_REGEX_H if(!win->paused) { - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter))) + if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) regex_filter_all(win); else regex_show_all(win); @@ -445,7 +445,7 @@ /* we check if the filter is on in case it was only of the options that * got changed, and not the expression. */ - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter))) + if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) regex_filter_all(win); } @@ -459,9 +459,9 @@ if(!win || !win->window) return; - current = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter)); + current = gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter)); if(active != current) - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win->filter), active); + gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter), active); } static void @@ -483,7 +483,7 @@ win->invert = active; - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter))) + if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) regex_filter_all(win); } @@ -496,7 +496,7 @@ win->highlight = active; - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter))) + if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) regex_filter_all(win); } @@ -522,7 +522,7 @@ gtk_tree_model_get(model, iter, 0, &text, 1, &level, -1); if (level >= purple_prefs_get_int(PIDGIN_PREFS_ROOT "/debug/filterlevel")) { - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter))) { + if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) { regex_match(win, text); } else { gtk_imhtml_append_text(GTK_IMHTML(win->text), text, 0); @@ -546,8 +546,8 @@ static void regex_changed_cb(GtkWidget *w, DebugWindow *win) { - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter))) { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win->filter), + if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) { + gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter), FALSE); } @@ -561,9 +561,9 @@ regex_key_release_cb(GtkWidget *w, GdkEventKey *e, DebugWindow *win) { if(e->keyval == GDK_Return && GTK_WIDGET_IS_SENSITIVE(win->filter) && - !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter))) + !gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) { - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win->filter), TRUE); + gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter), TRUE); } } @@ -588,10 +588,10 @@ } static void -regex_filter_toggled_cb(GtkToggleButton *button, DebugWindow *win) { +regex_filter_toggled_cb(GtkToggleToolButton *button, DebugWindow *win) { gboolean active; - active = gtk_toggle_button_get_active(button); + active = gtk_toggle_tool_button_get_active(button); purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/debug/filter", active); @@ -611,7 +611,7 @@ if (GPOINTER_TO_INT(value) != gtk_combo_box_get_active(GTK_COMBO_BOX(win->filterlevel))) gtk_combo_box_set_active(GTK_COMBO_BOX(win->filterlevel), GPOINTER_TO_INT(value)); - if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(win->filter))) + if(gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON(win->filter))) regex_filter_all(win); else regex_show_all(win); @@ -677,9 +677,12 @@ GtkWidget *vbox; GtkWidget *toolbar; GtkWidget *frame; - GtkWidget *image; gint width, height; void *handle; + GtkToolItem *item; +#if !GTK_CHECK_VERSION(2,12,0) + GtkTooltips *tooltips; +#endif win = g_new0(DebugWindow, 1); @@ -720,7 +723,12 @@ if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/toolbar")) { /* Setup our top button bar thingie. */ toolbar = gtk_toolbar_new(); +#if !GTK_CHECK_VERSION(2,12,0) + tooltips = gtk_tooltips_new(); +#endif +#if !GTK_CHECK_VERSION(2,14,0) gtk_toolbar_set_tooltips(GTK_TOOLBAR(toolbar), TRUE); +#endif gtk_toolbar_set_show_arrow(GTK_TOOLBAR(toolbar), TRUE); g_signal_connect(G_OBJECT(toolbar), "button-press-event", G_CALLBACK(toolbar_context), win); @@ -735,61 +743,93 @@ #ifndef HAVE_REGEX_H /* Find button */ - gtk_toolbar_insert_stock(GTK_TOOLBAR(toolbar), GTK_STOCK_FIND, - _("Find"), NULL, G_CALLBACK(find_cb), - win, -1); + item = gtk_tool_button_new_from_stock(GTK_STOCK_FIND); + gtk_tool_item_set_is_important(item, TRUE); +#if GTK_CHECK_VERSION(2,12,0) + gtk_tool_item_set_tooltip_text(item, _("Find")); +#else + gtk_tool_item_set_tooltip(item, tooltips, _("Find"), NULL); +#endif + g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(find_cb), win); + gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item)); #endif /* HAVE_REGEX_H */ /* Save */ - gtk_toolbar_insert_stock(GTK_TOOLBAR(toolbar), GTK_STOCK_SAVE, - _("Save"), NULL, G_CALLBACK(save_cb), - win, -1); + item = gtk_tool_button_new_from_stock(GTK_STOCK_SAVE); + gtk_tool_item_set_is_important(item, TRUE); +#if GTK_CHECK_VERSION(2,12,0) + gtk_tool_item_set_tooltip_text(item, _("Save")); +#else + gtk_tool_item_set_tooltip(item, tooltips, _("Save"), NULL); +#endif + g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(save_cb), win); + gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item)); /* Clear button */ - gtk_toolbar_insert_stock(GTK_TOOLBAR(toolbar), GTK_STOCK_CLEAR, - _("Clear"), NULL, G_CALLBACK(clear_cb), - win, -1); + item = gtk_tool_button_new_from_stock(GTK_STOCK_CLEAR); + gtk_tool_item_set_is_important(item, TRUE); +#if GTK_CHECK_VERSION(2,12,0) + gtk_tool_item_set_tooltip_text(item, _("Clear")); +#else + gtk_tool_item_set_tooltip(item, tooltips, _("Clear"), NULL); +#endif + g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(clear_cb), win); + gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item)); - gtk_toolbar_insert_space(GTK_TOOLBAR(toolbar), -1); + item = gtk_separator_tool_item_new(); + gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item)); /* Pause */ - image = gtk_image_new_from_stock(PIDGIN_STOCK_PAUSE, GTK_ICON_SIZE_MENU); - gtk_toolbar_append_element(GTK_TOOLBAR(toolbar), - GTK_TOOLBAR_CHILD_TOGGLEBUTTON, - NULL, _("Pause"), _("Pause"), - NULL, image, - G_CALLBACK(pause_cb), win); + item = gtk_toggle_tool_button_new_from_stock(PIDGIN_STOCK_PAUSE); + gtk_tool_item_set_is_important(item, TRUE); +#if GTK_CHECK_VERSION(2,12,0) + gtk_tool_item_set_tooltip_text(item, _("Pause")); +#else + gtk_tool_item_set_tooltip(item, tooltips, _("Pause"), NULL); +#endif + g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(pause_cb), win); + gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item)); #ifdef HAVE_REGEX_H /* regex stuff */ - gtk_toolbar_insert_space(GTK_TOOLBAR(toolbar), -1); + item = gtk_separator_tool_item_new(); + gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item)); /* regex toggle button */ - image = gtk_image_new_from_stock(GTK_STOCK_FIND, GTK_ICON_SIZE_MENU); - win->filter = - gtk_toolbar_append_element(GTK_TOOLBAR(toolbar), - GTK_TOOLBAR_CHILD_TOGGLEBUTTON, - NULL, _("Filter"), _("Filter"), - NULL, image, - G_CALLBACK(regex_filter_toggled_cb), - win); + item = gtk_toggle_tool_button_new_from_stock(GTK_STOCK_FIND); + gtk_tool_item_set_is_important(item, TRUE); + win->filter = GTK_WIDGET(item); + gtk_tool_button_set_label(GTK_TOOL_BUTTON(win->filter), _("Filter")); +#if GTK_CHECK_VERSION(2,12,0) + gtk_tool_item_set_tooltip_text(GTK_TOOL_ITEM(win->filter), _("Filter")); +#else + gtk_tooltips_set_tip(tooltips, win->filter, _("Filter"), NULL); +#endif + g_signal_connect(G_OBJECT(win->filter), "clicked", G_CALLBACK(regex_filter_toggled_cb), win); + gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(win->filter)); + /* we purposely disable the toggle button here in case * /purple/gtk/debug/expression has an empty string. If it does not have * an empty string, the change signal will get called and make the * toggle button sensitive. */ gtk_widget_set_sensitive(win->filter, FALSE); - gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(win->filter), + gtk_toggle_tool_button_set_active(GTK_TOGGLE_TOOL_BUTTON(win->filter), purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/filter")); purple_prefs_connect_callback(handle, PIDGIN_PREFS_ROOT "/debug/filter", regex_pref_filter_cb, win); /* regex entry */ win->expression = gtk_entry_new(); - gtk_toolbar_append_element(GTK_TOOLBAR(toolbar), - GTK_TOOLBAR_CHILD_WIDGET, win->expression, - NULL, _("Right click for more options."), - NULL, NULL, NULL, NULL); + item = gtk_tool_item_new(); +#if GTK_CHECK_VERSION(2,12,0) + gtk_widget_set_tooltip_text(win->expression, _("Right click for more options.")); +#else + gtk_tooltips_set_tip(tooltips, win->expression, _("Right click for more options."), NULL); +#endif + gtk_container_add(GTK_CONTAINER(item), GTK_WIDGET(win->expression)); + gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item)); + /* this needs to be before the text is set from the pref if we want it * to colorize a stored expression. */ @@ -815,18 +855,23 @@ #endif /* HAVE_REGEX_H */ - gtk_toolbar_insert_space(GTK_TOOLBAR(toolbar), -1); + item = gtk_separator_tool_item_new(); + gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item)); - gtk_toolbar_append_element(GTK_TOOLBAR(toolbar), - GTK_TOOLBAR_CHILD_WIDGET, gtk_label_new(_("Level ")), - NULL, _("Select the debug filter level."), - NULL, NULL, NULL, NULL); + item = gtk_tool_item_new(); + gtk_container_add(GTK_CONTAINER(item), gtk_label_new(_("Level "))); + gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item)); win->filterlevel = gtk_combo_box_new_text(); - gtk_toolbar_append_element(GTK_TOOLBAR(toolbar), - GTK_TOOLBAR_CHILD_WIDGET, win->filterlevel, - NULL, _("Select the debug filter level."), - NULL, NULL, NULL, NULL); + item = gtk_tool_item_new(); +#if GTK_CHECK_VERSION(2,12,0) + gtk_widget_set_tooltip_text(win->filterlevel, _("Select the debug filter level.")); +#else + gtk_tooltips_set_tip(tooltips, win->filterlevel, _("Select the debug filter level."), NULL); +#endif + gtk_container_add(GTK_CONTAINER(item), win->filterlevel); + gtk_container_add(GTK_CONTAINER(toolbar), GTK_WIDGET(item)); + gtk_combo_box_append_text(GTK_COMBO_BOX(win->filterlevel), _("All")); gtk_combo_box_append_text(GTK_COMBO_BOX(win->filterlevel), _("Misc")); gtk_combo_box_append_text(GTK_COMBO_BOX(win->filterlevel), _("Info"));
--- a/pidgin/gtkdocklet-gtk.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/gtkdocklet-gtk.c Tue Mar 09 21:41:41 2010 +0000 @@ -26,8 +26,6 @@ #include "pidginstock.h" #include "gtkdocklet.h" -#if GTK_CHECK_VERSION(2,10,0) - /* globals */ GtkStatusIcon *docklet = NULL; @@ -155,6 +153,3 @@ gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), DATADIR G_DIR_SEPARATOR_S "pixmaps" G_DIR_SEPARATOR_S "pidgin" G_DIR_SEPARATOR_S "tray"); } - -#endif /* GTK_CHECK_VERSION(2,10,0) */ -
--- a/pidgin/gtkdocklet-x11.c Thu Feb 18 22:42:19 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,359 +0,0 @@ -/* - * System tray icon (aka docklet) plugin for Purple - * - * Copyright (C) 2002-3 Robert McQueen <robot101@debian.org> - * Copyright (C) 2003 Herman Bloggs <hermanator12002@yahoo.com> - * Inspired by a similar plugin by: - * John (J5) Palmieri <johnp@martianrock.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02111-1301, USA. - */ - -#include "internal.h" -#include "pidgin.h" -#include "debug.h" -#include "prefs.h" -#include "pidginstock.h" - -#include "gtkdialogs.h" - -#include "eggtrayicon.h" -#include "gtkdocklet.h" -#include <gdk/gdkkeysyms.h> - -#if !GTK_CHECK_VERSION(2,10,0) - -#define SHORT_EMBED_TIMEOUT 5000 -#define LONG_EMBED_TIMEOUT 15000 - -/* globals */ -static EggTrayIcon *docklet = NULL; -static GtkWidget *image = NULL; -static GtkTooltips *tooltips = NULL; -static GdkPixbuf *blank_icon = NULL; -static int embed_timeout = 0; -static int docklet_height = 0; - -/* protos */ -static void docklet_x11_create(gboolean); - -static gboolean -docklet_x11_recreate_cb(gpointer data) -{ - docklet_x11_create(TRUE); - - return FALSE; /* for when we're called by the glib idle handler */ -} - -static void -docklet_x11_embedded_cb(GtkWidget *widget, void *data) -{ - purple_debug(PURPLE_DEBUG_INFO, "docklet", "X11 embedded\n"); - - g_source_remove(embed_timeout); - embed_timeout = 0; - pidgin_docklet_embedded(); - purple_prefs_set_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded", FALSE); -} - -static void -docklet_x11_destroyed_cb(GtkWidget *widget, void *data) -{ - purple_debug(PURPLE_DEBUG_INFO, "docklet", "X11 destroyed\n"); - - pidgin_docklet_remove(); - - g_object_unref(G_OBJECT(docklet)); - docklet = NULL; - - g_idle_add(docklet_x11_recreate_cb, NULL); -} - -static gboolean -docklet_x11_clicked_cb(GtkWidget *button, GdkEventButton *event, void *data) -{ - if (event->type != GDK_BUTTON_PRESS) - return FALSE; - - pidgin_docklet_clicked(event->button); - return TRUE; -} - -static gboolean -docklet_x11_pressed_cb(GtkWidget *button, GdkEventKey *event) -{ - guint state, keyval; - - state = event->state & gtk_accelerator_get_default_mod_mask(); - keyval = event->keyval; - if (state == 0 && - (keyval == GDK_Return || - keyval == GDK_KP_Enter || - keyval == GDK_ISO_Enter || - keyval == GDK_space || - keyval == GDK_KP_Space)) - { - pidgin_docklet_clicked(1); - return TRUE; - } - - return FALSE; -} - -static void -docklet_x11_popup_cb(GtkWidget *button) -{ - pidgin_docklet_clicked(3); -} - -static void -docklet_x11_update_icon(PurpleStatusPrimitive status, gboolean connecting, gboolean pending) -{ - const gchar *icon_name = NULL; - - g_return_if_fail(image != NULL); - - switch (status) { - case PURPLE_STATUS_OFFLINE: - icon_name = PIDGIN_STOCK_TRAY_OFFLINE; - break; - case PURPLE_STATUS_AWAY: - icon_name = PIDGIN_STOCK_TRAY_AWAY; - break; - case PURPLE_STATUS_UNAVAILABLE: - icon_name = PIDGIN_STOCK_TRAY_BUSY; - break; - case PURPLE_STATUS_EXTENDED_AWAY: - icon_name = PIDGIN_STOCK_TRAY_XA; - break; - case PURPLE_STATUS_INVISIBLE: - icon_name = PIDGIN_STOCK_TRAY_INVISIBLE; - break; - default: - icon_name = PIDGIN_STOCK_TRAY_AVAILABLE; - break; - } - - if (pending) - icon_name = PIDGIN_STOCK_TRAY_PENDING; - if (connecting) - icon_name = PIDGIN_STOCK_TRAY_CONNECT; - - if(icon_name) { - int icon_size; - if (docklet_height < 22) - icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL); - else if (docklet_height < 32) - icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_SMALL); - else if (docklet_height < 48) - icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_MEDIUM); - else - icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_LARGE); - - gtk_image_set_from_stock(GTK_IMAGE(image), icon_name, icon_size); - } -} - -static void -docklet_x11_resize_icon(GtkWidget *widget) -{ - if (docklet_height == MIN(widget->allocation.height, widget->allocation.width)) - return; - docklet_height = MIN(widget->allocation.height, widget->allocation.width); - pidgin_docklet_update_icon(); -} - -static void -docklet_x11_blank_icon(void) -{ - if (!blank_icon) { - GtkIconSize size = GTK_ICON_SIZE_LARGE_TOOLBAR; - gint width, height; - g_object_get(G_OBJECT(image), "icon-size", &size, NULL); - gtk_icon_size_lookup(size, &width, &height); - blank_icon = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height); - gdk_pixbuf_fill(blank_icon, 0); - } - - gtk_image_set_from_pixbuf(GTK_IMAGE(image), blank_icon); -} - -static void -docklet_x11_set_tooltip(gchar *tooltip) -{ - if (!tooltips) - tooltips = gtk_tooltips_new(); - - /* image->parent is a GtkEventBox */ - if (tooltip) { - gtk_tooltips_enable(tooltips); - gtk_tooltips_set_tip(tooltips, image->parent, tooltip, NULL); - } else { - gtk_tooltips_set_tip(tooltips, image->parent, "", NULL); - gtk_tooltips_disable(tooltips); - } -} - -static void -docklet_x11_position_menu(GtkMenu *menu, int *x, int *y, gboolean *push_in, - gpointer user_data) -{ - GtkWidget *widget = GTK_WIDGET(docklet); - GtkRequisition req; - gint menu_xpos, menu_ypos; - - gtk_widget_size_request(GTK_WIDGET(menu), &req); - gdk_window_get_origin(widget->window, &menu_xpos, &menu_ypos); - - menu_xpos += widget->allocation.x; - menu_ypos += widget->allocation.y; - - if (menu_ypos > gdk_screen_get_height(gtk_widget_get_screen(widget)) / 2) - menu_ypos -= req.height; - else - menu_ypos += widget->allocation.height; - - *x = menu_xpos; - *y = menu_ypos; - - *push_in = TRUE; -} - -static void -docklet_x11_destroy(void) -{ - g_return_if_fail(docklet != NULL); - - if (embed_timeout) - g_source_remove(embed_timeout); - - pidgin_docklet_remove(); - - g_signal_handlers_disconnect_by_func(G_OBJECT(docklet), G_CALLBACK(docklet_x11_destroyed_cb), NULL); - gtk_widget_destroy(GTK_WIDGET(docklet)); - - g_object_unref(G_OBJECT(docklet)); - docklet = NULL; - - if (blank_icon) - g_object_unref(G_OBJECT(blank_icon)); - blank_icon = NULL; - - image = NULL; - - purple_debug(PURPLE_DEBUG_INFO, "docklet", "X11 destroyed\n"); -} - -static gboolean -docklet_x11_embed_timeout_cb(gpointer data) -{ - /* The docklet was not embedded within the timeout. - * Remove it as a visibility manager, but leave the plugin - * loaded so that it can embed automatically if/when a notification - * area becomes available. - */ - purple_debug_info("docklet", "X11 failed to embed within timeout\n"); - pidgin_docklet_remove(); - - return FALSE; -} - -static void -docklet_x11_create(gboolean recreate) -{ - GtkWidget *box; - - if (docklet) { - /* if this is being called when a tray icon exists, it's because - something messed up. try destroying it before we proceed, - although docklet_refcount may be all hosed. hopefully won't happen. */ - purple_debug(PURPLE_DEBUG_WARNING, "docklet", "trying to create icon but it already exists?\n"); - docklet_x11_destroy(); - } - - docklet = egg_tray_icon_new(PIDGIN_NAME); - box = gtk_event_box_new(); - image = gtk_image_new(); - GTK_WIDGET_SET_FLAGS (image, GTK_CAN_FOCUS); - - g_signal_connect(G_OBJECT(docklet), "embedded", G_CALLBACK(docklet_x11_embedded_cb), NULL); - g_signal_connect(G_OBJECT(docklet), "destroy", G_CALLBACK(docklet_x11_destroyed_cb), NULL); - g_signal_connect(G_OBJECT(docklet), "size-allocate", G_CALLBACK(docklet_x11_resize_icon), NULL); - g_signal_connect(G_OBJECT(box), "button-press-event", G_CALLBACK(docklet_x11_clicked_cb), NULL); - g_signal_connect(G_OBJECT(box), "key-press-event", G_CALLBACK(docklet_x11_pressed_cb), NULL); - g_signal_connect(G_OBJECT(box), "popup-menu", G_CALLBACK(docklet_x11_popup_cb), NULL); - gtk_container_add(GTK_CONTAINER(box), image); - gtk_container_add(GTK_CONTAINER(docklet), box); - - if (!gtk_check_version(2,4,0)) - g_object_set(G_OBJECT(box), "visible-window", FALSE, NULL); - - gtk_widget_show_all(GTK_WIDGET(docklet)); - - /* ref the docklet before we bandy it about the place */ - g_object_ref(G_OBJECT(docklet)); - - /* This is a hack to avoid a race condition between the docklet getting - * embedded in the notification area and the gtkblist restoring its - * previous visibility state. If the docklet does not get embedded within - * the timeout, it will be removed as a visibility manager until it does - * get embedded. Ideally, we would only call docklet_embedded() when the - * icon was actually embedded. This only happens when the docklet is first - * created, not when being recreated. - * - * The x11 docklet tracks whether it successfully embedded in a pref and - * allows for a longer timeout period if it successfully embedded the last - * time it was run. This should hopefully solve problems with the buddy - * list not properly starting hidden when Pidgin is started on login. - */ - if(!recreate) { - pidgin_docklet_embedded(); - if(purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded")) { - embed_timeout = g_timeout_add(LONG_EMBED_TIMEOUT, docklet_x11_embed_timeout_cb, NULL); - } else { - embed_timeout = g_timeout_add(SHORT_EMBED_TIMEOUT, docklet_x11_embed_timeout_cb, NULL); - } - } - - purple_debug(PURPLE_DEBUG_INFO, "docklet", "X11 created\n"); -} - -static void -docklet_x11_create_ui_op(void) -{ - docklet_x11_create(FALSE); -} - -static struct docklet_ui_ops ui_ops = -{ - docklet_x11_create_ui_op, - docklet_x11_destroy, - docklet_x11_update_icon, - docklet_x11_blank_icon, - docklet_x11_set_tooltip, - docklet_x11_position_menu -}; - -void -docklet_ui_init() -{ - pidgin_docklet_set_ui_ops(&ui_ops); - purple_prefs_add_none(PIDGIN_PREFS_ROOT "/docklet/x11"); - purple_prefs_add_bool(PIDGIN_PREFS_ROOT "/docklet/x11/embedded", FALSE); -} - -#endif /* !GTK_CHECK_VERSION(2,10,0) */ -
--- a/pidgin/gtkft.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/gtkft.c Tue Mar 09 21:41:41 2010 +0000 @@ -575,7 +575,7 @@ /* Build the tree model */ /* Transfer type, Progress Bar, Filename, Size, Remaining */ - model = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_DOUBLE, + model = gtk_list_store_new(NUM_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_POINTER); dialog->model = model; @@ -609,7 +609,7 @@ /* Progress bar column */ renderer = gtk_cell_renderer_progress_new(); column = gtk_tree_view_column_new_with_attributes(_("Progress"), renderer, - "percentage", COLUMN_PROGRESS, NULL); + "value", COLUMN_PROGRESS, NULL); gtk_tree_view_column_set_resizable(GTK_TREE_VIEW_COLUMN(column), TRUE); gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column); @@ -906,7 +906,7 @@ lfilename = utf8; gtk_list_store_set(dialog->model, &data->iter, COLUMN_STATUS, pixbuf, - COLUMN_PROGRESS, 0.0, + COLUMN_PROGRESS, 0, COLUMN_FILENAME, (type == PURPLE_XFER_RECEIVE) ? purple_xfer_get_filename(xfer) : lfilename, @@ -1039,7 +1039,7 @@ remaining_str = purple_str_size_to_units(purple_xfer_get_bytes_remaining(xfer)); gtk_list_store_set(xfer_dialog->model, &data->iter, - COLUMN_PROGRESS, purple_xfer_get_progress(xfer), + COLUMN_PROGRESS, (gint)(purple_xfer_get_progress(xfer) * 100), COLUMN_SIZE, size_str, COLUMN_REMAINING, remaining_str, -1);
--- a/pidgin/gtkprefs.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/gtkprefs.c Tue Mar 09 21:41:41 2010 +0000 @@ -902,9 +902,7 @@ cell_rend = gtk_cell_renderer_text_new(); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell_rend, TRUE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), cell_rend, "markup", 1, NULL); -#if GTK_CHECK_VERSION(2,6,0) g_object_set(cell_rend, "ellipsize", PANGO_ELLIPSIZE_END, NULL); -#endif gtk_drag_dest_set(combo_box, GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_HIGHLIGHT | GTK_DEST_DEFAULT_DROP, te, sizeof(te) / sizeof(GtkTargetEntry) , GDK_ACTION_COPY | GDK_ACTION_MOVE);
--- a/pidgin/gtkprivacy.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/gtkprivacy.c Tue Mar 09 21:41:41 2010 +0000 @@ -220,7 +220,7 @@ for (i = 0; i < menu_entry_count; i++) { if (menu_entries[i].num == account->perm_deny) { - gtk_option_menu_set_history(GTK_OPTION_MENU(dialog->type_menu), i); + gtk_combo_box_set_active(GTK_COMBO_BOX(dialog->type_menu), i); break; } } @@ -234,9 +234,9 @@ * Even better: the privacy API needs to not suck. */ static void -type_changed_cb(GtkOptionMenu *optmenu, PidginPrivacyDialog *dialog) +type_changed_cb(GtkComboBox *combo, PidginPrivacyDialog *dialog) { - int new_type = menu_entries[gtk_option_menu_get_history(optmenu)].num; + int new_type = menu_entries[gtk_combo_box_get_active(combo)].num; dialog->account->perm_deny = new_type; serv_set_permit_deny(purple_account_get_connection(dialog->account)); @@ -343,8 +343,7 @@ GtkWidget *button; GtkWidget *dropdown; GtkWidget *label; - GtkWidget *menu; - int selected = 0; + int selected = -1; int i; dialog = g_new0(PidginPrivacyDialog, 1); @@ -372,22 +371,19 @@ dialog->account = pidgin_account_option_menu_get_selected(dropdown); /* Add the drop-down list with the allow/block types. */ - dialog->type_menu = gtk_option_menu_new(); + dialog->type_menu = gtk_combo_box_new_text(); gtk_box_pack_start(GTK_BOX(vbox), dialog->type_menu, FALSE, FALSE, 0); gtk_widget_show(dialog->type_menu); - /* Build the menu for that. */ - menu = gtk_menu_new(); - for (i = 0; i < menu_entry_count; i++) { - pidgin_new_item(menu, _(menu_entries[i].text)); + gtk_combo_box_append_text(GTK_COMBO_BOX(dialog->type_menu), + _(menu_entries[i].text)); if (menu_entries[i].num == dialog->account->perm_deny) selected = i; } - gtk_option_menu_set_menu(GTK_OPTION_MENU(dialog->type_menu), menu); - gtk_option_menu_set_history(GTK_OPTION_MENU(dialog->type_menu), selected); + gtk_combo_box_set_active(GTK_COMBO_BOX(dialog->type_menu), selected); g_signal_connect(G_OBJECT(dialog->type_menu), "changed", G_CALLBACK(type_changed_cb), dialog); @@ -421,7 +417,7 @@ button = pidgin_dialog_add_button(GTK_DIALOG(dialog->win), GTK_STOCK_CLOSE, G_CALLBACK(close_cb), dialog); dialog->close_button = button; - type_changed_cb(GTK_OPTION_MENU(dialog->type_menu), dialog); + type_changed_cb(GTK_COMBO_BOX(dialog->type_menu), dialog); #if 0 if (dialog->account->perm_deny == PURPLE_PRIVACY_ALLOW_USERS) { gtk_widget_show(dialog->allow_widget);
--- a/pidgin/gtkrequest.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/gtkrequest.c Tue Mar 09 21:41:41 2010 +0000 @@ -230,10 +230,10 @@ } static void -field_choice_menu_cb(GtkOptionMenu *menu, PurpleRequestField *field) +field_choice_menu_cb(GtkComboBox *menu, PurpleRequestField *field) { purple_request_field_choice_set_value(field, - gtk_option_menu_get_history(menu)); + gtk_combo_box_get_active(menu)); } static void @@ -928,26 +928,15 @@ if (num_labels > 5) { - GtkWidget *menu; - GtkWidget *item; - - widget = gtk_option_menu_new(); - - menu = gtk_menu_new(); + widget = gtk_combo_box_new_text(); for (l = labels; l != NULL; l = l->next) { const char *text = l->data; - - item = gtk_menu_item_new_with_label(text); - gtk_widget_show(item); - - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_combo_box_append_text(GTK_COMBO_BOX(widget), text); } - gtk_widget_show(menu); - gtk_option_menu_set_menu(GTK_OPTION_MENU(widget), menu); - gtk_option_menu_set_history(GTK_OPTION_MENU(widget), + gtk_combo_box_set_active(GTK_COMBO_BOX(widget), purple_request_field_choice_get_default_value(field)); g_signal_connect(G_OBJECT(widget), "changed", @@ -1064,6 +1053,9 @@ GtkTreeViewColumn *column; GtkTreeIter iter; GList *l; + GList *icons = NULL; + + icons = purple_request_field_list_get_icons(field); /* Create the scrolled window */ sw = gtk_scrolled_window_new(NULL, NULL); @@ -1075,7 +1067,10 @@ gtk_widget_show(sw); /* Create the list store */ - store = gtk_list_store_new(2, G_TYPE_POINTER, G_TYPE_STRING); + if (icons) + store = gtk_list_store_new(3, G_TYPE_POINTER, G_TYPE_STRING, GDK_TYPE_PIXBUF); + else + store = gtk_list_store_new(2, G_TYPE_POINTER, G_TYPE_STRING); /* Create the tree view */ treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)); @@ -1094,13 +1089,38 @@ gtk_tree_view_column_pack_start(column, renderer, TRUE); gtk_tree_view_column_add_attribute(column, renderer, "text", 1); + if (icons) + { + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(column, renderer, TRUE); + gtk_tree_view_column_add_attribute(column, renderer, "pixbuf", 2); + + gtk_widget_set_size_request(treeview, 200, 400); + } + for (l = purple_request_field_list_get_items(field); l != NULL; l = l->next) { const char *text = (const char *)l->data; gtk_list_store_append(store, &iter); - gtk_list_store_set(store, &iter, + if (icons) + { + const char *icon_path = (const char *)icons->data; + GdkPixbuf* pixbuf = NULL; + + if (icon_path) + pixbuf = gdk_pixbuf_new_from_file(icon_path, NULL); + + gtk_list_store_set(store, &iter, + 0, purple_request_field_list_get_data(field, text), + 1, text, + 2, pixbuf, + -1); + icons = icons->next; + } + else + gtk_list_store_set(store, &iter, 0, purple_request_field_list_get_data(field, text), 1, text, -1);
--- a/pidgin/gtksavedstatuses.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/gtksavedstatuses.c Tue Mar 09 21:41:41 2010 +0000 @@ -85,17 +85,16 @@ }; /** - * These are used in the GtkComboBox to select the specific - * PurpleStatusType when setting a substatus for a particular saved - * status. + * These are used in the GtkComboBox to select the specific PurpleStatusType + * when setting a (sub)status for a particular saved status. */ enum { - SUBSTATUS_COLUMN_ICON, + STATUS_COLUMN_ICON, /** A hidden column containing the ID of this PurpleStatusType. */ - SUBSTATUS_COLUMN_STATUS_ID, - SUBSTATUS_COLUMN_STATUS_NAME, - SUBSTATUS_NUM_COLUMNS + STATUS_COLUMN_STATUS_ID, + STATUS_COLUMN_STATUS_NAME, + STATUS_NUM_COLUMNS }; typedef struct @@ -118,7 +117,7 @@ gchar *original_title; GtkEntry *title; - GtkOptionMenu *type; + GtkComboBox *type; GtkIMHtml *message; } StatusEditor; @@ -742,7 +741,7 @@ return; } - type = gtk_option_menu_get_history(dialog->type) + (PURPLE_STATUS_UNSET + 1); + type = gtk_combo_box_get_active(dialog->type) + (PURPLE_STATUS_UNSET + 1); message = gtk_imhtml_get_markup(dialog->message); unformatted = purple_markup_strip_html(message); @@ -837,53 +836,56 @@ } static GtkWidget * -create_stock_item(const gchar *str, const gchar *icon) -{ - GtkWidget *menuitem = gtk_menu_item_new(); - GtkWidget *label = gtk_label_new_with_mnemonic(str); - GtkWidget *hbox = gtk_hbox_new(FALSE, 4); - GtkIconSize icon_size = gtk_icon_size_from_name(PIDGIN_ICON_SIZE_TANGO_EXTRA_SMALL); - GtkWidget *image = gtk_image_new_from_stock(icon, icon_size);; - - gtk_widget_show(label); - gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT); - gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5); - gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0); - gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0); - - gtk_container_add(GTK_CONTAINER(menuitem), hbox); - - return menuitem; -} - -static GtkWidget * create_status_type_menu(PurpleStatusPrimitive type) { int i; GtkWidget *dropdown; - GtkWidget *menu; - GtkWidget *item; + GtkListStore *store; + GtkTreeIter iter; + GtkCellRenderer *renderer; - dropdown = gtk_option_menu_new(); - menu = gtk_menu_new(); + store = gtk_list_store_new(STATUS_NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); for (i = PURPLE_STATUS_UNSET + 1; i < PURPLE_STATUS_NUM_PRIMITIVES; i++) { - if (i == PURPLE_STATUS_MOBILE || i == PURPLE_STATUS_TUNE) + /* Someone should fix this for 3.0.0. The independent boolean + * should probably be set on the status type, not the status. + * I guess that would prevent third party plugins from creating + * independent statuses? + */ + if (i == PURPLE_STATUS_MOBILE || + i == PURPLE_STATUS_MOOD || + i == PURPLE_STATUS_TUNE) /* * Special-case these. They're intended to be independent * status types, so don't show them in the list. */ continue; - item = create_stock_item(purple_primitive_get_name_from_type(i), - get_stock_icon_from_primitive(i)); - gtk_menu_shell_append(GTK_MENU_SHELL(menu), item); + gtk_list_store_append(store, &iter); + gtk_list_store_set(store, &iter, + STATUS_COLUMN_ICON, get_stock_icon_from_primitive(i), + STATUS_COLUMN_STATUS_ID, purple_primitive_get_id_from_type(i), + STATUS_COLUMN_STATUS_NAME, purple_primitive_get_name_from_type(i), + -1); } - gtk_menu_set_active(GTK_MENU(menu), type - (PURPLE_STATUS_UNSET + 1)); - gtk_option_menu_set_menu(GTK_OPTION_MENU(dropdown), menu); - gtk_widget_show_all(menu); + dropdown = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store)); + + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dropdown), renderer, FALSE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dropdown), renderer, + "stock-id", STATUS_COLUMN_ICON, + NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(dropdown), renderer, TRUE); + gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(dropdown), renderer, + "text", STATUS_COLUMN_STATUS_NAME, + NULL); + + gtk_combo_box_set_active(GTK_COMBO_BOX(dropdown), + type - (PURPLE_STATUS_UNSET + 1)); return dropdown; } @@ -1153,7 +1155,7 @@ dropdown = create_status_type_menu(purple_savedstatus_get_type(saved_status)); else dropdown = create_status_type_menu(PURPLE_STATUS_AWAY); - dialog->type = GTK_OPTION_MENU(dropdown); + dialog->type = GTK_COMBO_BOX(dropdown); pidgin_add_widget_to_vbox(GTK_BOX(vbox), _("_Status:"), sg, dropdown, TRUE, NULL); /* Status message */ @@ -1268,7 +1270,7 @@ if (!gtk_combo_box_get_active_iter(box, &iter)) return; gtk_tree_model_get(GTK_TREE_MODEL(select->model), &iter, - SUBSTATUS_COLUMN_STATUS_ID, &id, + STATUS_COLUMN_STATUS_ID, &id, -1); type = purple_account_get_status_type(select->account, id); g_free(id); @@ -1359,7 +1361,7 @@ } gtk_tree_model_get(GTK_TREE_MODEL(dialog->model), &iter, - SUBSTATUS_COLUMN_STATUS_ID, &id, + STATUS_COLUMN_STATUS_ID, &id, -1); type = purple_account_get_status_type(dialog->account, id); if (purple_status_type_get_attr(type, "message") != NULL) @@ -1449,7 +1451,7 @@ gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0); gtk_size_group_add_widget(sg, label); - dialog->model = gtk_list_store_new(SUBSTATUS_NUM_COLUMNS, + dialog->model = gtk_list_store_new(STATUS_NUM_COLUMNS, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING); @@ -1462,12 +1464,12 @@ NULL); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), rend, FALSE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), rend, - "stock-id", SUBSTATUS_COLUMN_ICON, NULL); + "stock-id", STATUS_COLUMN_ICON, NULL); rend = GTK_CELL_RENDERER(gtk_cell_renderer_text_new()); gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo), rend, TRUE); gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo), rend, - "text", SUBSTATUS_COLUMN_STATUS_NAME, NULL); + "text", STATUS_COLUMN_STATUS_NAME, NULL); g_signal_connect(G_OBJECT(combo), "changed", G_CALLBACK(substatus_selection_changed_cb), dialog); @@ -1543,9 +1545,9 @@ gtk_list_store_append(dialog->model, &iter); gtk_list_store_set(dialog->model, &iter, - SUBSTATUS_COLUMN_ICON, pidgin_stock_id_from_status_primitive(prim), - SUBSTATUS_COLUMN_STATUS_ID, id, - SUBSTATUS_COLUMN_STATUS_NAME, name, + STATUS_COLUMN_ICON, pidgin_stock_id_from_status_primitive(prim), + STATUS_COLUMN_STATUS_ID, id, + STATUS_COLUMN_STATUS_NAME, name, -1); if ((status_id != NULL) && !strcmp(status_id, id)) {
--- a/pidgin/gtkutils.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/gtkutils.c Tue Mar 09 21:41:41 2010 +0000 @@ -406,13 +406,13 @@ gtk_container_add(GTK_CONTAINER(button), bbox); if (icon) { - gtk_box_pack_start_defaults(GTK_BOX(bbox), ibox); + gtk_box_pack_start(GTK_BOX(bbox), ibox, TRUE, TRUE, 0); image = gtk_image_new_from_stock(icon, GTK_ICON_SIZE_BUTTON); gtk_box_pack_end(GTK_BOX(ibox), image, FALSE, TRUE, 0); } if (text) { - gtk_box_pack_start_defaults(GTK_BOX(bbox), lbox); + gtk_box_pack_start(GTK_BOX(bbox), lbox, TRUE, TRUE, 0); label = gtk_label_new(NULL); gtk_label_set_text_with_mnemonic(GTK_LABEL(label), text); gtk_label_set_mnemonic_widget(GTK_LABEL(label), button);
--- a/pidgin/pidginstock.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/pidginstock.c Tue Mar 09 21:41:41 2010 +0000 @@ -270,37 +270,30 @@ /* Altered from do_colorshift in gnome-panel */ static void -do_alphashift(GdkPixbuf *dest, GdkPixbuf *src) +do_alphashift(GdkPixbuf *pixbuf) { gint i, j; - gint width, height, has_alpha, srcrowstride, destrowstride; - guchar *target_pixels; - guchar *original_pixels; - guchar *pixsrc; - guchar *pixdest; + gint width, height, padding; + guchar *pixels; guchar a; - has_alpha = gdk_pixbuf_get_has_alpha (src); - if (!has_alpha) + if (!gdk_pixbuf_get_has_alpha(pixbuf)) return; - width = gdk_pixbuf_get_width (src); - height = gdk_pixbuf_get_height (src); - srcrowstride = gdk_pixbuf_get_rowstride (src); - destrowstride = gdk_pixbuf_get_rowstride (dest); - target_pixels = gdk_pixbuf_get_pixels (dest); - original_pixels = gdk_pixbuf_get_pixels (src); + width = gdk_pixbuf_get_width(pixbuf); + height = gdk_pixbuf_get_height(pixbuf); + padding = gdk_pixbuf_get_rowstride(pixbuf) - width * 4; + pixels = gdk_pixbuf_get_pixels(pixbuf); for (i = 0; i < height; i++) { - pixdest = target_pixels + i*destrowstride; - pixsrc = original_pixels + i*srcrowstride; for (j = 0; j < width; j++) { - *(pixdest++) = *(pixsrc++); - *(pixdest++) = *(pixsrc++); - *(pixdest++) = *(pixsrc++); - a = *(pixsrc++); - *(pixdest++) = a / 2; + pixels++; + pixels++; + pixels++; + a = *(pixels); + *(pixels++) = a / 2; } + pixels += padding; } } @@ -348,7 +341,7 @@ g_return_if_fail(filename != NULL); pixbuf = gdk_pixbuf_new_from_file(filename, NULL); if (translucent) - do_alphashift(pixbuf, pixbuf); + do_alphashift(pixbuf); source = gtk_icon_source_new(); gtk_icon_source_set_pixbuf(source, pixbuf); @@ -378,7 +371,7 @@ g_return_if_fail(filename != NULL); pixbuf = gdk_pixbuf_new_from_file(filename, NULL); if (translucent) - do_alphashift(pixbuf, pixbuf); + do_alphashift(pixbuf); source = gtk_icon_source_new(); gtk_icon_source_set_pixbuf(source, pixbuf);
--- a/pidgin/pixmaps/Makefile.am Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/pixmaps/Makefile.am Tue Mar 09 21:41:41 2010 +0000 @@ -101,8 +101,6 @@ emblems/16/half-operator.png \ emblems/16/hiptop.png \ emblems/16/male.png \ - emblems/16/mobile.png \ - emblems/16/music.png \ emblems/16/not-authorized.png \ emblems/16/operator.png \ emblems/16/qq-member.png \ @@ -120,8 +118,6 @@ emblems/scalable/free-for-chat.svg \ emblems/scalable/game.svg \ emblems/scalable/male.svg \ - emblems/scalable/mobile.svg \ - emblems/scalable/music.svg \ emblems/scalable/not-authorized.svg \ emblems/scalable/qq-member.svg \ emblems/scalable/secure.svg \ @@ -207,6 +203,7 @@ emotes/default/24/scalable/yin-yang.svg EMOTES_SMALL_16_SCALABLE = \ + emotes/small/16/scalable/mobile.svg \ emotes/small/16/scalable/pidgin-emotes.svg PROTOCOLS_16_SCALABLE = \
--- a/pidgin/pixmaps/emblems/scalable/mobile.svg Thu Feb 18 22:42:19 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,264 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="16" - height="16" - id="svg2" - sodipodi:version="0.32" - inkscape:version="0.46" - version="1.0" - sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/emblems" - sodipodi:docname="mobile.svg" - inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/emblems/mobile.png" - inkscape:export-xdpi="90" - inkscape:export-ydpi="90" - inkscape:output_extension="org.inkscape.output.svg.inkscape"> - <defs - id="defs4"> - <linearGradient - inkscape:collect="always" - id="linearGradient3335"> - <stop - style="stop-color:#204a87;stop-opacity:1;" - offset="0" - id="stop3337" /> - <stop - style="stop-color:#204a87;stop-opacity:0;" - offset="1" - id="stop3339" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient3300"> - <stop - style="stop-color:#204a87;stop-opacity:1;" - offset="0" - id="stop3302" /> - <stop - style="stop-color:#204a87;stop-opacity:0;" - offset="1" - id="stop3304" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3300" - id="linearGradient3315" - gradientUnits="userSpaceOnUse" - x1="-0.0061673229" - y1="11.454797" - x2="5.6395993" - y2="11.454797" - gradientTransform="matrix(0.580679,0,0,0.709346,1.207372,-1.624473)" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3335" - id="linearGradient3341" - x1="-27.615904" - y1="21.696934" - x2="-19.2363" - y2="18.633484" - gradientUnits="userSpaceOnUse" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3300" - id="linearGradient3353" - x1="-28.160521" - y1="22.551313" - x2="-18.170761" - y2="17.799398" - gradientUnits="userSpaceOnUse" /> - </defs> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="36.388908" - inkscape:cx="12.47515" - inkscape:cy="8.110346" - inkscape:document-units="px" - inkscape:current-layer="layer1" - showgrid="true" - fill="#888a85" - inkscape:window-width="1268" - inkscape:window-height="972" - inkscape:window-x="6" - inkscape:window-y="21" - inkscape:grid-points="true" /> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1"> - <rect - style="opacity:1;fill:#d3d7cf;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000036;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - id="rect1331" - width="7.9919939" - height="12.993911" - x="7.5040998" - y="1.504545" /> - <path - sodipodi:type="inkscape:offset" - inkscape:radius="-1.0228416" - inkscape:original="M 7.5 1.5 L 7.5 14.5 L 15.5 14.5 L 15.5 1.5 L 7.5 1.5 z " - xlink:href="#rect1331" - style="opacity:0.5;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.99199408px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path2206" - inkscape:href="#rect1331" - d="M 8.53125,2.53125 L 8.53125,13.46875 L 14.46875,13.46875 L 14.46875,2.53125 L 8.53125,2.53125 z " - transform="matrix(1.010173,0,0,1.005973,-0.115518,-4.844059e-2)" /> - <rect - style="fill:#3465a4;fill-opacity:1;fill-rule:evenodd;stroke:#204a87;stroke-width:0.99999928px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect2208" - width="3.9807076" - height="3.0005636" - x="9.5027981" - y="3.499944" /> - <path - style="opacity:0.38150288;color:#000000;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" - d="M 9,3 L 9,6 C 9.6849325,5.8078592 10.39969,5.560911 11.098766,5.2718451 C 12.171483,4.8282801 13.137384,4.3213781 14,3.7572819 L 14,3 L 9,3 z " - id="path2220" /> - <rect - style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect2233" - width="1" - height="1" - x="9" - y="8" /> - <path - sodipodi:type="spiral" - style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:url(#linearGradient3353);stroke-width:7.08695936px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path3252" - sodipodi:cx="-11" - sodipodi:cy="17" - sodipodi:expansion="1" - sodipodi:revolution="1.1139722" - sodipodi:radius="17.723295" - sodipodi:argument="-9.424778" - sodipodi:t0="0.70807511" - d="M -14.027341,29.178804 C -22.234168,28.813068 -27.756867,20.82956 -27.038836,13.013154 C -26.789036,10.293863 -25.851755,7.6562253 -24.369854,5.3654734" - transform="matrix(0.120201,5.654854e-2,-4.742318e-2,0.143331,9.572983,5.110321)" /> - <path - sodipodi:type="spiral" - style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:url(#linearGradient3341);stroke-width:3.73703289px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="path3254" - sodipodi:cx="-11" - sodipodi:cy="17" - sodipodi:expansion="1" - sodipodi:revolution="1.1157676" - sodipodi:radius="17.751867" - sodipodi:argument="-9.424778" - sodipodi:t0="0.69832003" - d="M -13.258788,29.188958 C -21.390692,29.337435 -27.331334,21.779752 -27.099873,14.016463 C -27.00639,10.881001 -25.990716,7.8068052 -24.259099,5.1963947" - transform="matrix(0.219568,0.111334,-8.662678e-2,0.282195,10.94256,3.73758)" /> - <path - style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:url(#linearGradient3315);stroke-width:1.00000131px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="M 4.5539611,12.498317 C 2.0020373,11.459655 0.92343648,7.5201545 1.7880505,4.3367901 C 2.2231942,2.7346624 3.0921081,1.3783732 4.1882245,0.50356567" - id="path3256" - sodipodi:nodetypes="csc" /> - <rect - style="opacity:0.5;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect3355" - width="1" - height="1" - x="7" - y="1" /> - <rect - style="opacity:0.5;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect1340" - width="1" - height="1" - x="15" - y="1" /> - <rect - style="opacity:0.5;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect1342" - width="1" - height="1" - x="15" - y="14" /> - <rect - style="opacity:0.5;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect1344" - width="1" - height="1" - x="7" - y="14" /> - <rect - style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect2220" - width="1" - height="1" - x="11" - y="8" /> - <rect - style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect2222" - width="1" - height="1" - x="13" - y="8" /> - <rect - style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect2224" - width="1" - height="1" - x="13" - y="10" /> - <rect - style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect2226" - width="1" - height="1" - x="11" - y="10" /> - <rect - style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect2228" - width="1" - height="1" - x="9" - y="10" /> - <rect - style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect2230" - width="1" - height="1" - x="9" - y="12" /> - <rect - style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect2232" - width="1" - height="1" - x="11" - y="12" /> - <rect - style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - id="rect2234" - width="1" - height="1" - x="13" - y="12" /> - </g> -</svg>
--- a/pidgin/pixmaps/emblems/scalable/music.svg Thu Feb 18 22:42:19 2010 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,189 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="16" - height="16" - id="svg2" - sodipodi:version="0.32" - inkscape:version="0.46" - sodipodi:modified="true" - version="1.0" - sodipodi:docname="music.svg" - inkscape:output_extension="org.inkscape.output.svg.inkscape"> - <defs - id="defs4"> - <linearGradient - inkscape:collect="always" - id="linearGradient3185"> - <stop - style="stop-color:#4e9a06;stop-opacity:1;" - offset="0" - id="stop3187" /> - <stop - style="stop-color:#4e9a06;stop-opacity:0" - offset="1" - id="stop3189" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient3177"> - <stop - style="stop-color:#4e9a06;stop-opacity:1;" - offset="0" - id="stop3179" /> - <stop - style="stop-color:#4e9a06;stop-opacity:0;" - offset="1" - id="stop3181" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient3153"> - <stop - style="stop-color:#eeeeec;stop-opacity:1;" - offset="0" - id="stop3155" /> - <stop - style="stop-color:#eeeeec;stop-opacity:0;" - offset="1" - id="stop3157" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3153" - id="linearGradient3159" - x1="2.5409546" - y1="10.048674" - x2="10.378205" - y2="15.928688" - gradientUnits="userSpaceOnUse" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3177" - id="radialGradient3183" - cx="5.2116022" - cy="8.4051199" - fx="5.2116022" - fy="8.4051199" - r="2.9404981" - gradientTransform="matrix(2.6050387,0,0,2.2888674,-8.415579,-10.767812)" - gradientUnits="userSpaceOnUse" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3185" - id="radialGradient3191" - cx="5.1978397" - cy="8.4135866" - fx="5.1978397" - fy="8.4135866" - r="3.1428281" - gradientTransform="matrix(2.8202152,0,0,2.4999643,-9.461187,-12.455954)" - gradientUnits="userSpaceOnUse" /> - </defs> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1.0" - gridtolerance="10000" - guidetolerance="10" - objecttolerance="10" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="22.4" - inkscape:cx="19.784002" - inkscape:cy="11.848" - inkscape:document-units="px" - inkscape:current-layer="layer1" - width="16px" - height="16px" - showgrid="true" - inkscape:window-width="1440" - inkscape:window-height="847" - inkscape:window-x="0" - inkscape:window-y="22" /> - <metadata - id="metadata7"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - inkscape:label="Layer 1" - inkscape:groupmode="layer" - id="layer1"> - <path - sodipodi:type="arc" - style="opacity:1;fill:#555753;fill-opacity:1;stroke:#222728;stroke-width:1.14297926;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - id="path2160" - sodipodi:cx="6.4712896" - sodipodi:cy="14.484771" - sodipodi:rx="3.5986683" - sodipodi:ry="2.1781414" - d="M 10.069958 14.484771 A 3.5986683 2.1781414 0 1 1 2.8726213,14.484771 A 3.5986683 2.1781414 0 1 1 10.069958 14.484771 z" - transform="matrix(0.8336417,0,0,0.918214,4.1052631,-0.8001194)" /> - <rect - style="opacity:1;fill:#222728;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - id="rect3143" - width="1" - height="12" - x="12" - y="1" - rx="0.18940361" - ry="0.20662212" /> - <path - style="fill:#222728;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" - d="M 12.006464,3.0203051 L 11.981638,1 C 13.945163,1 17.837365,1.1548232 14.996311,7.9684328 C 15.596089,2.2547574 13.743811,3.0203051 12.006464,3.0203051 z " - id="rect3146" - sodipodi:nodetypes="cccc" /> - <path - sodipodi:type="arc" - style="opacity:1;fill:#555753;fill-opacity:1;stroke:url(#linearGradient3159);stroke-width:1.97969818;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - id="path3151" - sodipodi:cx="6.4712896" - sodipodi:cy="14.484771" - sodipodi:rx="3.5986683" - sodipodi:ry="2.1781414" - d="M 10.069958 14.484771 A 3.5986683 2.1781414 0 1 1 2.8726213,14.484771 A 3.5986683 2.1781414 0 1 1 10.069958 14.484771 z" - transform="matrix(0.5557611,0,0,0.4591071,5.903509,5.8499391)" /> - <path - sodipodi:type="arc" - style="opacity:1;fill:none;fill-opacity:1;stroke:url(#radialGradient3191);stroke-width:0.98568761;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - id="path3173" - sodipodi:cx="8.0970039" - sodipodi:cy="11.122857" - sodipodi:rx="4.3089318" - sodipodi:ry="3.5513175" - d="M 3.9729753,12.152015 A 4.3089318,3.5513175 0 0 1 9.0871283,7.6665672" - transform="matrix(1.0443424,0,0,0.9855497,-0.4560443,3.7870959e-2)" - sodipodi:start="2.8475788" - sodipodi:end="4.9442449" - sodipodi:open="true" /> - <path - sodipodi:type="arc" - style="opacity:1;fill:none;fill-opacity:1;stroke:url(#radialGradient3183);stroke-width:0.58102763;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" - id="path3175" - sodipodi:cx="8.0970039" - sodipodi:cy="11.122857" - sodipodi:rx="4.3089318" - sodipodi:ry="3.5513175" - d="M 3.9729753,12.152015 A 4.3089318,3.5513175 0 0 1 9.0871283,7.6665672" - transform="matrix(1.6981233,0,0,1.7443645,-5.9310642,-8.697544)" - sodipodi:start="2.8475788" - sodipodi:end="4.9442449" - sodipodi:open="true" /> - </g> -</svg>
--- a/pidgin/pixmaps/emotes/default/24/Makefile.am Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/pixmaps/emotes/default/24/Makefile.am Tue Mar 09 21:41:41 2010 +0000 @@ -2,6 +2,7 @@ act-up.png \ airplane.png \ alien.png \ + amorous.png \ angel.png \ angry.png \ arrogant.png \ @@ -56,8 +57,9 @@ dont-know.png \ drink.png \ drool.png \ - eat.png \ + hungry.png \ embarrassed.png \ + excited.png \ excruciating.png \ eyeroll.png \ female-fighter.png \ @@ -81,7 +83,7 @@ hug-left.png \ hug-right.png \ hypnotized.png \ - in-love.png \ + in_love.png \ island.png \ jump.png \ kissed.png \ @@ -94,7 +96,6 @@ liquor.png \ loser.png \ love-over.png \ - love.png \ lying.png \ mad-tongue.png \ mail.png \ @@ -114,7 +115,7 @@ msn.png \ musical-note.png \ music.png \ - nailbiting.png \ + nervous.png \ neutral.png \ on-the-phone.png \ party.png \ @@ -144,7 +145,7 @@ secret.png \ shame.png \ sheep.png \ - shock.png \ + shocked.png \ shout.png \ shut-mouth.png \ sick.png \ @@ -153,9 +154,9 @@ sinister.png \ skeleton.png \ skywalker.png \ + sleeping.png \ sleepy.png \ - smile-big.png \ - smile.png \ + happy.png \ smirk.png \ snail.png \ snicker.png \ @@ -167,7 +168,7 @@ stop.png \ struggle.png \ sun.png \ - sweat.png \ + hot.png \ talktohand.png \ teeth.png \ terror.png \ @@ -175,7 +176,7 @@ thunder.png \ time-out.png \ tongue.png \ - tremble.png \ + afraid.png \ turtle.png \ tv.png \ umbrella.png \ @@ -188,7 +189,6 @@ wilt.png \ wink.png \ worship.png \ - yawn.png \ yin-yang.png
--- a/pidgin/pixmaps/emotes/default/24/default.theme.in Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/pixmaps/emotes/default/24/default.theme.in Tue Mar 09 21:41:41 2010 +0000 @@ -6,16 +6,16 @@ # Default smileys [default] -smile.png :) :-) -smile-big.png :-D :-d :D :d +happy.png :) :-) +excited.png :-D :-d :D :d sad.png :-( :( wink.png ;-) ;) -tongue.png :P :-P :-p :p -shock.png =-O =-o +tongue.png :P :p :-P :-p +shocked.png =-O =-o kiss.png :-* glasses-cool.png 8-) embarrassed.png :-[ -crying.png :'( +crying.png :'( :'-( thinking.png :-/ :-\\ angel.png O:-) o:-) shut-mouth.png :-X @@ -23,20 +23,70 @@ foot-in-mouth.png :-! shout.png >:o >:O ! skywalker.png C:-) c:-) C:) c:) -! monkey.png :-(|) :(|) 8-|) -! cyclops.png O-) o-) +! monkey.png :-(|) :(|) 8-|) +! cyclops.png O-) o-) + + +[XMPP] +# Following XEP-0038 + GTalk + our default set, in default set order +# The GTalk strings come from ticket #3307. +happy.png :) :-) =) +excited.png :-D :-d :D :d =D =d +sad.png :-( :( +wink.png ;-) ;) ;^) +tongue.png :P :p :-P :-p +shocked.png =-O =-o :-O :-o +kiss.png :kiss: :-* +glasses-cool.png 8-) B-) +embarrassed.png :-[ +crying.png :'-( :'( +thinking.png :-/ :-\\ +angel.png O:-) o:-) +shut-mouth.png :-X +moneymouth.png :-$ +foot-in-mouth.png :-! +shout.png >:o >:O + +# Following XEP-0038 + GTalk +angry.png >:-( >:( X-( x-( +good.png :yes: +bad.png :no: +stop.png :wait: +rose.png @->-- :rose: +phone.png :telephone: +mail.png :email: +lamp.png :jabber: +cake.png :cake: +in_love.png :heart: :love: <3 +love-over.png :brokenheart: +musical-note.png :music: +beer.png :beer: +coffee.png :coffee: +coins.png :money: +moon.png :moon: +sun.png :sun: +star.png :star: + +# Others +neutral.png :| :-| +victory.png \\m/ + +# Hidden icons from the default set. +! skywalker.png C:-) c:-) C:) c:) +! monkey.png :-(|) :(|) 8-|) +! cyclops.png O-) o-) # Following AIM 6.1 [AIM] -smile.png :-) :) +happy.png :-) :) wink.png ;-) ;) sad.png :-( :( -tongue.png :-P :P :-p :p -shock.png =-O +tongue.png :P :p :-P :-p +shocked.png =-O kiss.png :-* shout.png >:o -smile-big.png :-D :D +excited.png :-D :D moneymouth.png :-$ foot-in-mouth.png :-! embarrassed.png :-[ @@ -46,26 +96,27 @@ shut-mouth.png :-X glasses-cool.png 8-) ! skywalker.png C:-) c:-) C:) c:) -! monkey.png :-(|) :(|) +! monkey.png :-(|) :(|) 8-|) +! cyclops.png O-) o-) # Following Windows Live Messenger 8.1 [MSN] -smile.png :) :-) -smile-big.png :D :d :-D :-d +happy.png :) :-) +excited.png :D :d :-D :-d wink.png ;) ;-) -shock.png :-O :-o :O :o -tongue.png :P :p :-P :-p +shocked.png :-O :-o :O :o +tongue.png :-P :P :-p :p glasses-cool.png (H) (h) angry.png :@ :-@ embarrassed.png :$ :-$ -confused.png :S :s :-S :-s +confused.png :S :s :-S :-s sad.png :( :-( crying.png :'( neutral.png :| :-| devil.png (6) angel.png (A) (a) -love.png (L) (l) +in_love.png (L) (l) love-over.png (U) (u) msn.png (M) (m) cat.png (@) @@ -125,10 +176,11 @@ thunder.png (li) party.png <:o) eyeroll.png 8-) -yawn.png |-) +sleepy.png |-) bunny.png ('.') ! skywalker.png C:-) c:-) C:) c:) -! monkey.png :-(|) :(|) +! monkey.png :-(|) :(|) 8-|) +! cyclops.png O-) o-) # Hidden MSN emotes cigarette.png (ci) (CI) @@ -139,7 +191,7 @@ # Following QQ 2006 [QQ] -shock.png /:O /jy /surprised +shocked.png /:O /jy /surprised curl-lip.png /:~ /pz /curl_lip desire.png /:* /se /desire dazed.png /:| /dazed @@ -147,13 +199,13 @@ crying.png /:< /ll /cry bashful.png /:$ /hx /bashful shut-mouth.png /:X /bz /shut_mouth -sleepy.png /:Z /shui /sleep +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 -smile-big.png /:D /cy /toothy_smile -smile.png /:) /wx /small_smile +excited.png /:D /cy /toothy_smile +happy.png /:) /wx /small_smile sad.png /:( /ng /sad glasses-cool.png /:+ /kuk /cool doctor.png /:# /feid /SARS @@ -164,9 +216,9 @@ disdain.png /;d /by /disdain arrogant.png /;o /am /arrogant starving.png /:g /jie /starving -yawn.png /|-) /kun /sleepy +sleepy.png /|-) /kun /sleepy terror.png /:! /jk /terror -sweat.png /:L /sweat +hot.png /:L /sweat smirk.png /:> /hanx /smirk soldier.png /:; /db /soldier struggle.png /;f /fendou /struggle @@ -180,8 +232,8 @@ hammer.png /xx /qiao /hammer bye.png /bye /zj /bye go-away.png /go /shan /go -tremble.png /shake /fad /shake -in-love.png /love /aiq /love +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 @@ -200,12 +252,12 @@ musical-note.png /music /yy /music poop.png /shit /bb /shit coffee.png /coffee /kf /coffee -eat.png /eat /fan /eat +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 -love.png /heart /xin /heart +in_love.png /heart /xin /heart love-over.png /break /xs /broken_heart meeting.png /meeting /hy /meeting present.png /gift /lw /gift @@ -233,20 +285,21 @@ girl.png /<00> /nv /woman boy.png /<11> /nan /man ! skywalker.png C:-) c:-) C:) c:) -! monkey.png :-(|) :(|) +! monkey.png :-(|) :(|) 8-|) +! cyclops.png O-) o-) # Following ICQ 6.0 [ICQ] -smile.png :-) :) +happy.png :-) :) neutral.png :-$ sad.png :-( :( -shock.png =-O +shocked.png =-O wink.png ;-) ;) tongue.png :-P :P :-p :p music.png [:-} laugh.png *JOKINGLY* -sleepy.png *TIRED* +sleeping.png *TIRED* crying.png :'( :'-( sick.png :-! kissed.png *KISSED* @@ -265,26 +318,27 @@ good.png *THUMBS\ UP* shout.png >:o >:O :-@ beer.png *DRINK* -smile-big.png :-D :D +excited.png :-D :D glasses-cool.png 8-) -in-love.png *IN\ LOVE* +amorous.png *IN\ LOVE* ! skywalker.png C:-) c:-) C:) c:) -! monkey.png :-(|) :(|) +! monkey.png :-(|) :(|) 8-|) +! cyclops.png O-) o-) # Following Yahoo! Messenger 8.1 [Yahoo] -smile.png :) :-) +happy.png :) :-) question.png :-/ :-\\ -shock.png :-O :O :-o :o +shocked.png :-O :O :-o :o devil.png >:) angel.png O:-) o:-) 0:-) sick.png :-& -yawn.png (:| +sleepy.png (:| hypnotized.png @-) on-the-phone.png :)] sad.png :( :-( -in-love.png :x :-x :X :-X +amorous.png :x :-x :X :-X angry.png X-( x-( X( x( crying.png :(( glasses-nerdy.png :-B :-b @@ -301,11 +355,11 @@ thinking.png :-? waiting.png :-w :-W at-wits-end.png ~x( ~X( -smile-big.png :D :-D :d :-d +excited.png :D :-D :d :-d tongue.png :-P :P :-p :p glasses-cool.png B-) b-) neutral.png :| :-| -sleepy.png I-) i-) |-) +sleeping.png I-) i-) |-) clown.png :o) :O) doh.png #-o #-O weep.png :-< @@ -321,107 +375,16 @@ time-out.png :-t :-T hug-left.png >:D< >:d< love-over.png =(( -sweat.png #:-S #:-s +hot.png #:-S #:-s rotfl.png =)) :-j :-J loser.png L-) l-) party.png <:-P <:-p -nailbiting.png :-SS :-Ss :-sS :-ss +nervous.png :-SS :-Ss :-sS :-ss cowboy.png <):) desire.png 8-> ! skywalker.png C:-) c:-) C:) c:) -! monkey.png :-(|) :(|) - -# Hidden Yahoo emotes -alien.png =:) >-) -beat-up.png b-( B-( -chicken.png ~:> -coffee.png ~o) ~O) -cow.png 3:-O 3:-o -dance.png \\:D/ \\:d/ -rose.png @};- -dont-know.png :-L :-l -skeleton.png 8-X 8-x -lamp.png *-:) -monkey.png :(|) -coins.png $-) -peace.png :)>- -pig.png :@) -pray.png [-o< [-O< -pumpkin.png (~~) -shame.png [-X [-x -flag.png **== -clover.png %%- -musical-note.png :-" -giggle.png ;)) -worship.png ^:)^ -star.png (*) -waving.png >:/ -talktohand.png :-@ - -# Only available after activating the Yahoo! Fighter IMVironment -male-fighter1.png o-> O-> -male-fighter2.png o=> O=> -female-fighter.png o-+ O-+ -yin-yang.png (%) - -# Following Yahoo! Messenger 8.1 -[Yahoo JAPAN] -smile.png :) :-) -question.png :-/ :-\\ -shock.png :-O :O :-o :o -devil.png >:) -angel.png O:-) o:-) 0:-) -sick.png :-& -yawn.png (:| -hypnotized.png @-) -on-the-phone.png :)] -sad.png :( :-( -in-love.png :x :-x :X :-X -angry.png X-( x-( X( x( -crying.png :(( -glasses-nerdy.png :-B :-b -quiet.png :-$ -drool.png =P~ =p~ -lying.png :^O :^o -call-me.png :-c -wink.png ;) ;-) -embarrassed.png :"> -mean.png :-> :> -laugh.png :)) :-)) -bye.png =; -arrogant.png [-( -thinking.png :-? -waiting.png :-w :-W -at-wits-end.png ~x( ~X( -smile-big.png :D :-D :d :-d -tongue.png :-P :P :-p :p -glasses-cool.png B-) b-) -neutral.png :| :-| -sleepy.png I-) i-) |-) -clown.png :o) :O) -doh.png #-o #-O -weep.png :-< -go-away.png :-h -lashes.png ;;) -kiss.png :-* :* -confused.png :-S :-s -sarcastic.png /:) -eyeroll.png 8-| -silly.png 8-} -clap.png =D> =d> -mad-tongue.png >:P >:p -time-out.png :-t :-T -hug-left.png >:D< >:d< -love-over.png =(( -sweat.png #:-S #:-s -rotfl.png =)) :-j :-J -loser.png L-) l-) -party.png <:-P <:-p -nailbiting.png :-SS :-Ss :-sS :-ss -cowboy.png <):) -desire.png 8-> -! skywalker.png C:-) c:-) C:) c:) -! monkey.png :-(|) :(|) +! monkey.png :-(|) :(|) 8-|) +! cyclops.png O-) o-) # Hidden Yahoo emotes alien.png =:) >-) @@ -459,14 +422,14 @@ # Following MySpaceIM Beta 1.0.697.0 [MySpaceIM] -smile-big.png :D :-D +excited.png :D :-D devil.png }:) confused.png :Z glasses-nerdy.png B) bulgy-eyes.png %) freaked-out.png :E smile.png :) :-) -in-love.png :X +amorous.png :X laugh.png :)) mohawk.png -: mad-tongue.png X( @@ -474,7 +437,7 @@ glasses-nerdy.png Q) doh.png :G pirate.png P) -shock.png :O +shocked.png :O sidefrown.png :{ sinister.png :B smirk.png :, @@ -484,23 +447,7 @@ wink.png ;-) ;) sad.png :[ kiss.png :x +! skywalker.png C:-) c:-) C:) c:) +! monkey.png :-(|) :(|) 8-|) +! cyclops.png O-) o-) -[XMPP] -# XMPP emoticons -smile.png :) :-) =) -smile-big.png :D :-D =D -wink.png ;) ;-) ;^) -shock.png :-o -tongue.png :P :-P :-p :p -glasses-cool.png B-) -angry.png X-( -sad.png :( :-( =( -crying.png :'( -neutral.png :-| -thinking.png :-/ -love.png <3 -monkey.png :(|) -victory.png \\m/ -! skywalker.png C:-) c:-) C:) c:) -! monkey.png :-(|) :(|) 8-|) -! cyclops.png O-) o-)
--- a/pidgin/pixmaps/emotes/small/16/Makefile.am Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/pixmaps/emotes/small/16/Makefile.am Tue Mar 09 21:41:41 2010 +0000 @@ -1,4 +1,24 @@ +# These are mood images that are NOT also used in the smiley theme. +MOODS = \ + afraid.png \ + bathing.png \ + cinema.png \ + disappointed.png \ + embarrassed.png \ + internet.png \ + music.png \ + restroom.png \ + search.png \ + shopping.png \ + studying.png \ + suit.png \ + surfing.png \ + typing.png \ + working.png \ + writing.png + SMILEYS = \ + amorous.png \ angel.png \ angry.png \ beer.png \ @@ -12,12 +32,15 @@ crying.png \ devil.png \ dont-know.png \ + excited.png \ grin.png \ + happy.png \ hug-left.png \ hug-right.png \ + in_love.png \ kiss.png \ - love.png \ meeting.png \ + mobile.png \ musical-note.png \ nerdy.png \ neutral.png \ @@ -27,18 +50,16 @@ question.png \ sad.png \ shame.png \ - shock.png \ + shocked.png \ sick.png \ silent.png \ + sleeping.png \ sleepy.png \ - smile-big.png \ - smile.png \ thinking.png \ tongue.png \ tv.png \ uhm-yeah.png \ - wink.png \ - yawn.png + wink.png pidginsmileypix_in_files = small.theme.in @@ -46,6 +67,7 @@ if INSTALL_PIXMAPS pidginsmileypixdir = $(datadir)/pixmaps/pidgin/emotes/small pidginsmileypix_DATA = \ + $(MOODS) \ $(SMILEYS) \ theme @@ -56,4 +78,4 @@ $< > $@ endif -EXTRA_DIST = $(SMILEYS) $(pidginsmileypix_in_files) theme +EXTRA_DIST = $(MOODS) $(SMILEYS) $(pidginsmileypix_in_files) theme
--- a/pidgin/pixmaps/emotes/small/16/TODO Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/pixmaps/emotes/small/16/TODO Tue Mar 09 21:41:41 2010 +0000 @@ -1,7 +1,18 @@ The following icons where just scaled down from the 24x24 and may need work: + afraid.png + amorous.png + disappointed.png + embarrassed.png + happy.png + hot.png + hungry.png + mean.png meeting.png + nervous.png question.png + sarcastic.png search.png + shocked.png sleepy.png smile-big.png tv.png @@ -13,7 +24,7 @@ cigarette.png coffee.png console.png - love.png + in_love.png musical-note.png party.png phone.png
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pixmaps/emotes/small/16/scalable/mobile.svg Tue Mar 09 21:41:41 2010 +0000 @@ -0,0 +1,264 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.46" + version="1.0" + sodipodi:docbase="/home/hbons/Desktop/Gaim Refresh/emblems" + sodipodi:docname="mobile.svg" + inkscape:export-filename="/home/hbons/Desktop/Gaim Refresh/emblems/mobile.png" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs4"> + <linearGradient + inkscape:collect="always" + id="linearGradient3335"> + <stop + style="stop-color:#204a87;stop-opacity:1;" + offset="0" + id="stop3337" /> + <stop + style="stop-color:#204a87;stop-opacity:0;" + offset="1" + id="stop3339" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient3300"> + <stop + style="stop-color:#204a87;stop-opacity:1;" + offset="0" + id="stop3302" /> + <stop + style="stop-color:#204a87;stop-opacity:0;" + offset="1" + id="stop3304" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3300" + id="linearGradient3315" + gradientUnits="userSpaceOnUse" + x1="-0.0061673229" + y1="11.454797" + x2="5.6395993" + y2="11.454797" + gradientTransform="matrix(0.580679,0,0,0.709346,1.207372,-1.624473)" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3335" + id="linearGradient3341" + x1="-27.615904" + y1="21.696934" + x2="-19.2363" + y2="18.633484" + gradientUnits="userSpaceOnUse" /> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3300" + id="linearGradient3353" + x1="-28.160521" + y1="22.551313" + x2="-18.170761" + y2="17.799398" + gradientUnits="userSpaceOnUse" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="36.388908" + inkscape:cx="12.47515" + inkscape:cy="8.110346" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="true" + fill="#888a85" + inkscape:window-width="1268" + inkscape:window-height="972" + inkscape:window-x="6" + inkscape:window-y="21" + inkscape:grid-points="true" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <rect + style="opacity:1;fill:#d3d7cf;fill-opacity:1;fill-rule:evenodd;stroke:#888a85;stroke-width:1.00000036;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect1331" + width="7.9919939" + height="12.993911" + x="7.5040998" + y="1.504545" /> + <path + sodipodi:type="inkscape:offset" + inkscape:radius="-1.0228416" + inkscape:original="M 7.5 1.5 L 7.5 14.5 L 15.5 14.5 L 15.5 1.5 L 7.5 1.5 z " + xlink:href="#rect1331" + style="opacity:0.5;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:0.99199408px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="path2206" + inkscape:href="#rect1331" + d="M 8.53125,2.53125 L 8.53125,13.46875 L 14.46875,13.46875 L 14.46875,2.53125 L 8.53125,2.53125 z " + transform="matrix(1.010173,0,0,1.005973,-0.115518,-4.844059e-2)" /> + <rect + style="fill:#3465a4;fill-opacity:1;fill-rule:evenodd;stroke:#204a87;stroke-width:0.99999928px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2208" + width="3.9807076" + height="3.0005636" + x="9.5027981" + y="3.499944" /> + <path + style="opacity:0.38150288;color:#000000;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" + d="M 9,3 L 9,6 C 9.6849325,5.8078592 10.39969,5.560911 11.098766,5.2718451 C 12.171483,4.8282801 13.137384,4.3213781 14,3.7572819 L 14,3 L 9,3 z " + id="path2220" /> + <rect + style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2233" + width="1" + height="1" + x="9" + y="8" /> + <path + sodipodi:type="spiral" + style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:url(#linearGradient3353);stroke-width:7.08695936px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="path3252" + sodipodi:cx="-11" + sodipodi:cy="17" + sodipodi:expansion="1" + sodipodi:revolution="1.1139722" + sodipodi:radius="17.723295" + sodipodi:argument="-9.424778" + sodipodi:t0="0.70807511" + d="M -14.027341,29.178804 C -22.234168,28.813068 -27.756867,20.82956 -27.038836,13.013154 C -26.789036,10.293863 -25.851755,7.6562253 -24.369854,5.3654734" + transform="matrix(0.120201,5.654854e-2,-4.742318e-2,0.143331,9.572983,5.110321)" /> + <path + sodipodi:type="spiral" + style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:url(#linearGradient3341);stroke-width:3.73703289px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="path3254" + sodipodi:cx="-11" + sodipodi:cy="17" + sodipodi:expansion="1" + sodipodi:revolution="1.1157676" + sodipodi:radius="17.751867" + sodipodi:argument="-9.424778" + sodipodi:t0="0.69832003" + d="M -13.258788,29.188958 C -21.390692,29.337435 -27.331334,21.779752 -27.099873,14.016463 C -27.00639,10.881001 -25.990716,7.8068052 -24.259099,5.1963947" + transform="matrix(0.219568,0.111334,-8.662678e-2,0.282195,10.94256,3.73758)" /> + <path + style="fill:none;fill-opacity:0.75;fill-rule:evenodd;stroke:url(#linearGradient3315);stroke-width:1.00000131px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + d="M 4.5539611,12.498317 C 2.0020373,11.459655 0.92343648,7.5201545 1.7880505,4.3367901 C 2.2231942,2.7346624 3.0921081,1.3783732 4.1882245,0.50356567" + id="path3256" + sodipodi:nodetypes="csc" /> + <rect + style="opacity:0.5;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect3355" + width="1" + height="1" + x="7" + y="1" /> + <rect + style="opacity:0.5;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect1340" + width="1" + height="1" + x="15" + y="1" /> + <rect + style="opacity:0.5;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect1342" + width="1" + height="1" + x="15" + y="14" /> + <rect + style="opacity:0.5;fill:#eeeeec;fill-opacity:0.75;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect1344" + width="1" + height="1" + x="7" + y="14" /> + <rect + style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2220" + width="1" + height="1" + x="11" + y="8" /> + <rect + style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2222" + width="1" + height="1" + x="13" + y="8" /> + <rect + style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2224" + width="1" + height="1" + x="13" + y="10" /> + <rect + style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2226" + width="1" + height="1" + x="11" + y="10" /> + <rect + style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2228" + width="1" + height="1" + x="9" + y="10" /> + <rect + style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2230" + width="1" + height="1" + x="9" + y="12" /> + <rect + style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2232" + width="1" + height="1" + x="11" + y="12" /> + <rect + style="opacity:0.84393065;fill:#888a85;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" + id="rect2234" + width="1" + height="1" + x="13" + y="12" /> + </g> +</svg>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/pixmaps/emotes/small/16/scalable/music.svg Tue Mar 09 21:41:41 2010 +0000 @@ -0,0 +1,189 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="16" + height="16" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.46" + sodipodi:modified="true" + version="1.0" + sodipodi:docname="music.svg" + inkscape:output_extension="org.inkscape.output.svg.inkscape"> + <defs + id="defs4"> + <linearGradient + inkscape:collect="always" + id="linearGradient3185"> + <stop + style="stop-color:#4e9a06;stop-opacity:1;" + offset="0" + id="stop3187" /> + <stop + style="stop-color:#4e9a06;stop-opacity:0" + offset="1" + id="stop3189" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient3177"> + <stop + style="stop-color:#4e9a06;stop-opacity:1;" + offset="0" + id="stop3179" /> + <stop + style="stop-color:#4e9a06;stop-opacity:0;" + offset="1" + id="stop3181" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + id="linearGradient3153"> + <stop + style="stop-color:#eeeeec;stop-opacity:1;" + offset="0" + id="stop3155" /> + <stop + style="stop-color:#eeeeec;stop-opacity:0;" + offset="1" + id="stop3157" /> + </linearGradient> + <linearGradient + inkscape:collect="always" + xlink:href="#linearGradient3153" + id="linearGradient3159" + x1="2.5409546" + y1="10.048674" + x2="10.378205" + y2="15.928688" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3177" + id="radialGradient3183" + cx="5.2116022" + cy="8.4051199" + fx="5.2116022" + fy="8.4051199" + r="2.9404981" + gradientTransform="matrix(2.6050387,0,0,2.2888674,-8.415579,-10.767812)" + gradientUnits="userSpaceOnUse" /> + <radialGradient + inkscape:collect="always" + xlink:href="#linearGradient3185" + id="radialGradient3191" + cx="5.1978397" + cy="8.4135866" + fx="5.1978397" + fy="8.4135866" + r="3.1428281" + gradientTransform="matrix(2.8202152,0,0,2.4999643,-9.461187,-12.455954)" + gradientUnits="userSpaceOnUse" /> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + gridtolerance="10000" + guidetolerance="10" + objecttolerance="10" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="22.4" + inkscape:cx="19.784002" + inkscape:cy="11.848" + inkscape:document-units="px" + inkscape:current-layer="layer1" + width="16px" + height="16px" + showgrid="true" + inkscape:window-width="1440" + inkscape:window-height="847" + inkscape:window-x="0" + inkscape:window-y="22" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1"> + <path + sodipodi:type="arc" + style="opacity:1;fill:#555753;fill-opacity:1;stroke:#222728;stroke-width:1.14297926;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path2160" + sodipodi:cx="6.4712896" + sodipodi:cy="14.484771" + sodipodi:rx="3.5986683" + sodipodi:ry="2.1781414" + d="M 10.069958 14.484771 A 3.5986683 2.1781414 0 1 1 2.8726213,14.484771 A 3.5986683 2.1781414 0 1 1 10.069958 14.484771 z" + transform="matrix(0.8336417,0,0,0.918214,4.1052631,-0.8001194)" /> + <rect + style="opacity:1;fill:#222728;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="rect3143" + width="1" + height="12" + x="12" + y="1" + rx="0.18940361" + ry="0.20662212" /> + <path + style="fill:#222728;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-opacity:1" + d="M 12.006464,3.0203051 L 11.981638,1 C 13.945163,1 17.837365,1.1548232 14.996311,7.9684328 C 15.596089,2.2547574 13.743811,3.0203051 12.006464,3.0203051 z " + id="rect3146" + sodipodi:nodetypes="cccc" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:#555753;fill-opacity:1;stroke:url(#linearGradient3159);stroke-width:1.97969818;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3151" + sodipodi:cx="6.4712896" + sodipodi:cy="14.484771" + sodipodi:rx="3.5986683" + sodipodi:ry="2.1781414" + d="M 10.069958 14.484771 A 3.5986683 2.1781414 0 1 1 2.8726213,14.484771 A 3.5986683 2.1781414 0 1 1 10.069958 14.484771 z" + transform="matrix(0.5557611,0,0,0.4591071,5.903509,5.8499391)" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:none;fill-opacity:1;stroke:url(#radialGradient3191);stroke-width:0.98568761;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3173" + sodipodi:cx="8.0970039" + sodipodi:cy="11.122857" + sodipodi:rx="4.3089318" + sodipodi:ry="3.5513175" + d="M 3.9729753,12.152015 A 4.3089318,3.5513175 0 0 1 9.0871283,7.6665672" + transform="matrix(1.0443424,0,0,0.9855497,-0.4560443,3.7870959e-2)" + sodipodi:start="2.8475788" + sodipodi:end="4.9442449" + sodipodi:open="true" /> + <path + sodipodi:type="arc" + style="opacity:1;fill:none;fill-opacity:1;stroke:url(#radialGradient3183);stroke-width:0.58102763;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" + id="path3175" + sodipodi:cx="8.0970039" + sodipodi:cy="11.122857" + sodipodi:rx="4.3089318" + sodipodi:ry="3.5513175" + d="M 3.9729753,12.152015 A 4.3089318,3.5513175 0 0 1 9.0871283,7.6665672" + transform="matrix(1.6981233,0,0,1.7443645,-5.9310642,-8.697544)" + sodipodi:start="2.8475788" + sodipodi:end="4.9442449" + sodipodi:open="true" /> + </g> +</svg>
--- a/pidgin/pixmaps/emotes/small/16/small.theme.in Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/pixmaps/emotes/small/16/small.theme.in Tue Mar 09 21:41:41 2010 +0000 @@ -6,27 +6,58 @@ # Default smileys [default] -smile.png :) :-) -smile-big.png :-D :-d :D :d +happy.png :) :-) +excited.png :-D :-d :D :d sad.png :-( :( wink.png ;-) ;) -tongue.png :P :-P :-p :p -shock.png =-O =-o +tongue.png :P :p :-P :-p +shocked.png =-O =-o kiss.png :-* -crying.png :'( +embarrassed.png :-[ +crying.png :'( :'-( thinking.png :-/ :-\\ angel.png O:-) o:-) +[XMPP] +# Following XEP-0038 + GTalk + our default set, in default set order +# The GTalk strings come from ticket #3307. +happy.png :) :-) =) +excited.png :-D :-d :D :d =D =d +sad.png :-( :( +wink.png ;-) ;) ;^) +tongue.png :P :p :-P :-p +shocked.png =-O =-o :-O :-o +kiss.png :kiss: :-* +embarrassed.png :-[ +crying.png :'-( :'( +thinking.png :-/ :-\\ +angel.png O:-) o:-) + +# Following XEP-0038 + GTalk +angry.png >:-( >:( X-( x-( +phone.png :telephone: +in_love.png :heart: :love: <3 +musical-note.png :music: +beer.png :beer: +coffee.png :coffee: + +# Others +neutral.png :| :-| + +# Hidden icons from the default set. + + # Following AIM 6.1 [AIM] -smile.png :-) :) +happy.png :-) :) wink.png ;-) ;) sad.png :-( :( -tongue.png :-P :P :-p :p -shock.png =-O +tongue.png :P :p :-P :-p +shocked.png =-O kiss.png :-* -smile-big.png :-D :D +excited.png :-D :D +embarrassed.png :-[ angel.png O:-) thinking.png :-\\ :-/ crying.png :'( @@ -34,19 +65,20 @@ # Following Windows Live Messenger 8.1 [MSN] -smile.png :) :-) -smile-big.png :D :d :-D :-d +happy.png :) :-) +excited.png :D :d :-D :-d wink.png ;) ;-) -shock.png :-O :-o :O :o -tongue.png :P :p :-P :-p +shocked.png :-O :-o :O :o +tongue.png :-P :P :-p :p angry.png :@ :-@ -confused.png :S :s :-S :-s +embarrassed.png :$ :-$ +confused.png :S :s :-S :-s sad.png :( :-( crying.png :'( neutral.png :| :-| devil.png (6) angel.png (A) (a) -love.png (L) (l) +in_love.png (L) (l) musical-note.png (8) kiss.png (K) (k) camera.png (P) (p) @@ -55,12 +87,14 @@ hug-left.png ({) hug-right.png (}) beer.png (B) (b) +sarcastic.png ^o) sick.png +o( plate.png (pl) +mobile.png (mp) dont-know.png :^) thinking.png *-) party.png <:o) -yawn.png |-) +sleepy.png |-) # Hidden MSN emotes cigarette.png (ci) (CI) @@ -69,22 +103,27 @@ # Following QQ 2006 [QQ] -shock.png /:O /jy /surprised +shocked.png /:O /jy /surprised party.png /8-) /dy /revel crying.png /:< /ll /cry -sleepy.png /:Z /shui /sleep -smile-big.png /:D /cy /toothy_smile -smile.png /:) /wx /small_smile +sleeping.png /:Z /shui /sleep +embarrassed.png /:-| /gg /embarassed +excited.png /:D /cy /toothy_smile +happy.png /:) /wx /small_smile sad.png /:( /ng /sad sick.png /:T /tu /vomit -yawn.png /|-) /kun /sleepy +sleepy.png /|-) /kun /sleepy +hot.png /:L /sweat question.png /? /yiw /question +afraid.png /shake /fad /shake +amorous.png /love /aiq /love search.png /find /zhao /search hug-left.png /hug /yb /hug musical-note.png /music /yy /music coffee.png /coffee /kf /coffee +hungry.png /eat /fan /eat kiss.png /kiss /wen /kiss -love.png /heart /xin /heart +in_love.png /heart /xin /heart meeting.png /meeting /hy /meeting phone.png /phone /dh /phone tv.png /TV /ds /TV @@ -93,45 +132,54 @@ # Following ICQ 6.0 [ICQ] -smile.png :-) :) +happy.png :-) :) neutral.png :-$ sad.png :-( :( -shock.png =-O +shocked.png =-O wink.png ;-) ;) tongue.png :-P :P :-p :p -sleepy.png *TIRED* +music.png [:-} +sleeping.png *TIRED* crying.png :'( :'-( sick.png :-! kiss.png :-{} :-* +embarrassed.png :-[ devil.png ]:-> angel.png O:-) thinking.png :-\\ :-/ beer.png *DRINK* -smile-big.png :-D :D +excited.png :-D :D +amorous.png *IN\ LOVE* # Following Yahoo! Messenger 8.1 [Yahoo] -smile.png :) :-) +happy.png :) :-) question.png :-/ :-\\ -shock.png :-O :O :-o :o +shocked.png :-O :O :-o :o devil.png >:) angel.png O:-) o:-) 0:-) sick.png :-& -yawn.png (:| +sleepy.png (:| sad.png :( :-( +amorous.png :x :-x :X :-X angry.png X-( x-( X( x( crying.png :(( wink.png ;) ;-) +embarrassed.png :"> +mean.png :-> :> thinking.png :-? -smile-big.png :D :-D :d :-d +excited.png :D :-D :d :-d tongue.png :-P :P :-p :p neutral.png :| :-| -sleepy.png I-) i-) |-) +sleeping.png I-) i-) |-) kiss.png :-* :* confused.png :-S :-s +sarcastic.png /:) hug-left.png >:D< >:d< +hot.png #:-S #:-s party.png <:-P <:-p +nervous.png :-SS :-Ss :-sS :-ss # Hidden Yahoo emotes coffee.png ~o) ~O) @@ -171,30 +219,14 @@ # Following MySpaceIM Beta 1.0.697.0 [MySpaceIM] -smile-big.png :D :-D +excited.png :D :-D devil.png }:) confused.png :Z -smile.png :) :-) -shock.png :O +amorous.png :X +shocked.png :O neutral.png :| tongue.png :P :p wink.png ;-) ;) sad.png :[ kiss.png :x -[XMPP] -# XMPP emoticons -smile.png :) :-) =) -smile-big.png :D :-D =D -wink.png ;) ;-) ;^) -shock.png :-o -tongue.png :P :-P :-p :p -glasses-cool.png B-) -angry.png X-( -sad.png :( :-( =( -crying.png :'( -neutral.png :-| -thinking.png :-/ -love.png <3 -monkey.png :(|) -victory.png \\m/
--- a/pidgin/plugins/gestures/gestures.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/plugins/gestures/gestures.c Tue Mar 09 21:41:41 2010 +0000 @@ -145,6 +145,15 @@ } #if 0 +#if GTK_CHECK_VERSION(2,4,0) +static void +mouse_button_menu_cb(GtkComboBox *opt, gpointer data) +{ + int button = gtk_combo_box_get_active(opt); + + gstroke_set_mouse_button(button + 2); +} +#else static void mouse_button_menu_cb(GtkMenuItem *item, gpointer data) { @@ -153,6 +162,7 @@ gstroke_set_mouse_button(button + 2); } #endif +#endif static void toggle_draw_cb(GtkToggleButton *toggle, gpointer data) @@ -220,8 +230,10 @@ GtkWidget *toggle; #if 0 GtkWidget *opt; +#if GTK_CHECK_VERSION(2,4,0) GtkWidget *menu, *item; #endif +#endif /* Outside container */ ret = gtk_vbox_new(FALSE, 18); @@ -231,6 +243,19 @@ vbox = pidgin_make_frame(ret, _("Mouse Gestures Configuration")); #if 0 +#if GTK_CHECK_VERSION(2,4,0) + /* Mouse button drop-down menu */ + opt = gtk_combo_box_new_text(); + + gtk_combo_box_append_text(_("Middle mouse button")); + gtk_combo_box_append_text(_("Right mouse button")); + g_signal_connect(G_OBJECT(opt), "changed", + G_CALLBACK(mouse_button_menu_cb), NULL); + + gtk_box_pack_start(GTK_BOX(vbox), opt, FALSE, FALSE, 0); + gtk_combo_box_set_active(GTK_COMBO_BOX(opt), + gstroke_get_mouse_button() - 2); +#else /* Mouse button drop-down menu */ menu = gtk_menu_new(); opt = gtk_option_menu_new(); @@ -250,6 +275,7 @@ gtk_option_menu_set_history(GTK_OPTION_MENU(opt), gstroke_get_mouse_button() - 2); #endif +#endif /* "Visual gesture display" checkbox */ toggle = gtk_check_button_new_with_mnemonic(_("_Visual gesture display"));
--- a/pidgin/plugins/gevolution/assoc-buddy.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/plugins/gevolution/assoc-buddy.c Tue Mar 09 21:41:41 2010 +0000 @@ -20,7 +20,6 @@ */ #include "internal.h" #include "gtkblist.h" -#include "gtkexpander.h" #include "pidgin.h" #include "gtkutils.h" #include "gtkimhtml.h"
--- a/pidgin/plugins/gtkbuddynote.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/plugins/gtkbuddynote.c Tue Mar 09 21:41:41 2010 +0000 @@ -31,11 +31,13 @@ const gchar *note = purple_blist_node_get_string(node, "notes"); if ((note != NULL) && (*note != '\0')) { - char *tmp; + char *tmp, *esc; purple_markup_html_to_xhtml(note, NULL, &tmp); + esc = g_markup_escape_text(tmp, -1); + g_free(tmp); g_string_append_printf(text, _("\n<b>Buddy Note</b>: %s"), - tmp); - g_free(tmp); + esc); + g_free(esc); } } }
--- a/pidgin/plugins/win32/transparency/win2ktrans.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/plugins/win32/transparency/win2ktrans.c Tue Mar 09 21:41:41 2010 +0000 @@ -72,8 +72,6 @@ static const char *OPT_WINTRANS_BL_ONTOP = "/plugins/gtk/win32/wintrans/bl_always_on_top"; static GSList *window_list = NULL; -static BOOL (*MySetLayeredWindowAttributes)(HWND hwnd, COLORREF crKey, BYTE bAlpha, DWORD dwFlags) = NULL; - /* * CODE */ @@ -81,31 +79,31 @@ /* Set window transparency level */ static void set_wintrans(GtkWidget *window, int alpha, gboolean enabled, gboolean always_on_top) { - if (MySetLayeredWindowAttributes) { - HWND hWnd = GDK_WINDOW_HWND(window->window); - LONG style = GetWindowLong(hWnd, GWL_EXSTYLE); - if (enabled) { - style |= WS_EX_LAYERED; - } else { - style &= ~WS_EX_LAYERED; - } - SetWindowLong(hWnd, GWL_EXSTYLE, style); + + HWND hWnd = GDK_WINDOW_HWND(window->window); + LONG style = GetWindowLong(hWnd, GWL_EXSTYLE); + if (enabled) { + style |= WS_EX_LAYERED; + } else { + style &= ~WS_EX_LAYERED; + } + SetWindowLong(hWnd, GWL_EXSTYLE, style); - if (enabled) { - SetWindowPos(hWnd, - always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, - 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); - MySetLayeredWindowAttributes(hWnd, 0, alpha, LWA_ALPHA); - } else { - /* Ask the window and its children to repaint */ - SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + if (enabled) { + SetWindowPos(hWnd, + always_on_top ? HWND_TOPMOST : HWND_NOTOPMOST, + 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + SetLayeredWindowAttributes(hWnd, 0, alpha, LWA_ALPHA); + } else { + /* Ask the window and its children to repaint */ + SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); - RedrawWindow(hWnd, NULL, NULL, - RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN); - } + RedrawWindow(hWnd, NULL, NULL, + RDW_ERASE | RDW_INVALIDATE | RDW_FRAME | RDW_ALLCHILDREN); } + } /* When a conv window is focused, if we're only transparent when unfocused, @@ -491,14 +489,6 @@ * EXPORTED FUNCTIONS */ static gboolean plugin_load(PurplePlugin *plugin) { - MySetLayeredWindowAttributes = (void*) wpurple_find_and_loadproc( - "user32.dll", "SetLayeredWindowAttributes"); - - if (!MySetLayeredWindowAttributes) { - purple_debug_error(WINTRANS_PLUGIN_ID, - "SetLayeredWindowAttributes API not found (Required W2K+)\n"); - return FALSE; - } purple_signal_connect(purple_conversations_get_handle(), "conversation-created", plugin,
--- a/pidgin/plugins/xmppconsole.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/plugins/xmppconsole.c Tue Mar 09 21:41:41 2010 +0000 @@ -621,7 +621,7 @@ } static void -signed_on_cb(PurpleConnection *gc) +signing_on_cb(PurpleConnection *gc) { if (!console) return; @@ -630,7 +630,9 @@ console->accounts = g_list_append(console->accounts, gc); console->count++; - if (console->count > 1) + if (console->count == 1) + console->gc = gc; + else gtk_widget_show_all(console->hbox); } @@ -680,8 +682,8 @@ PURPLE_CALLBACK(xmlnode_received_cb), NULL); purple_signal_connect(jabber, "jabber-sending-text", xmpp_console_handle, PURPLE_CALLBACK(xmlnode_sent_cb), NULL); - purple_signal_connect(purple_connections_get_handle(), "signed-on", - plugin, PURPLE_CALLBACK(signed_on_cb), NULL); + purple_signal_connect(purple_connections_get_handle(), "signing-on", + plugin, PURPLE_CALLBACK(signing_on_cb), NULL); purple_signal_connect(purple_connections_get_handle(), "signed-off", plugin, PURPLE_CALLBACK(signed_off_cb), NULL);
--- a/pidgin/win32/gtkdocklet-win32.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/gtkdocklet-win32.c Tue Mar 09 21:41:41 2010 +0000 @@ -21,7 +21,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02111-1301, USA. */ - +#define _WIN32_IE 0x0500 #include <windows.h> #include <gdk/gdkwin32.h> #include <gdk/gdk.h> @@ -51,7 +51,7 @@ /* This is used to trigger click events on so they appear to GTK+ as if they are triggered by input */ static GtkWidget *dummy_button = NULL; static GtkWidget *dummy_window = NULL; -static NOTIFYICONDATA _nicon_data; +static NOTIFYICONDATAW _nicon_data; static gboolean dummy_button_cb(GtkWidget *widget, GdkEventButton *event, gpointer user_data) { pidgin_docklet_clicked(event->button); @@ -64,7 +64,7 @@ switch(msg) { case WM_CREATE: purple_debug_info("docklet", "WM_CREATE\n"); - taskbarRestartMsg = RegisterWindowMessage("TaskbarCreated"); + taskbarRestartMsg = RegisterWindowMessageW(L"TaskbarCreated"); break; case WM_TIMER: @@ -114,7 +114,7 @@ if (msg == taskbarRestartMsg) { /* explorer crashed and left us hanging... This will put the systray icon back in it's place, when it restarts */ - Shell_NotifyIcon(NIM_ADD, &_nicon_data); + Shell_NotifyIconW(NIM_ADD, &_nicon_data); } break; }/* end switch */ @@ -124,10 +124,10 @@ /* Create hidden window to process systray messages */ static HWND systray_create_hiddenwin() { - WNDCLASSEX wcex; - LPCTSTR wname; + WNDCLASSEXW wcex; + wchar_t *wname; - wname = TEXT("WinpidginSystrayWinCls"); + wname = L"WinpidginSystrayWinCls"; wcex.cbSize = sizeof(wcex); wcex.style = 0; @@ -142,22 +142,25 @@ wcex.lpszClassName = wname; wcex.hIconSm = NULL; - RegisterClassEx(&wcex); + RegisterClassExW(&wcex); /* Create the window */ - return (CreateWindow(wname, "", 0, 0, 0, 0, 0, GetDesktopWindow(), NULL, winpidgin_exe_hinstance(), 0)); + return (CreateWindowW(wname, L"", 0, 0, 0, 0, 0, GetDesktopWindow(), NULL, winpidgin_exe_hinstance(), 0)); } static void systray_init_icon(HWND hWnd) { + wchar_t *w; ZeroMemory(&_nicon_data, sizeof(_nicon_data)); - _nicon_data.cbSize = sizeof(NOTIFYICONDATA); + _nicon_data.cbSize = sizeof(NOTIFYICONDATAW); _nicon_data.hWnd = hWnd; _nicon_data.uID = 0; _nicon_data.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; _nicon_data.uCallbackMessage = WM_TRAYMESSAGE; _nicon_data.hIcon = NULL; - lstrcpy(_nicon_data.szTip, PIDGIN_NAME); - Shell_NotifyIcon(NIM_ADD, &_nicon_data); + w = g_utf8_to_utf16(PIDGIN_NAME, -1, NULL, NULL, NULL); + wcsncpy(_nicon_data.szTip, w, sizeof(_nicon_data.szTip) / sizeof(wchar_t)); + g_free(w); + Shell_NotifyIconW(NIM_ADD, &_nicon_data); pidgin_docklet_embedded(); } @@ -486,11 +489,11 @@ g_return_if_fail(hicon != NULL); _nicon_data.hIcon = hicon; - Shell_NotifyIcon(NIM_MODIFY, &_nicon_data); + Shell_NotifyIconW(NIM_MODIFY, &_nicon_data); } static void systray_remove_nid(void) { - Shell_NotifyIcon(NIM_DELETE, &_nicon_data); + Shell_NotifyIconW(NIM_DELETE, &_nicon_data); } static void winpidgin_tray_update_icon(PurpleStatusPrimitive status, @@ -547,19 +550,19 @@ static void winpidgin_tray_blank_icon() { _nicon_data.hIcon = NULL; - Shell_NotifyIcon(NIM_MODIFY, &_nicon_data); + Shell_NotifyIconW(NIM_MODIFY, &_nicon_data); } static void winpidgin_tray_set_tooltip(gchar *tooltip) { - if (tooltip) { - char *locenc = NULL; - locenc = g_locale_from_utf8(tooltip, -1, NULL, NULL, NULL); - lstrcpyn(_nicon_data.szTip, locenc, sizeof(_nicon_data.szTip) / sizeof(TCHAR)); - g_free(locenc); - } else { - lstrcpy(_nicon_data.szTip, PIDGIN_NAME); - } - Shell_NotifyIcon(NIM_MODIFY, &_nicon_data); + const char *value = tooltip; + wchar_t *w; + if (value == NULL) { + value = PIDGIN_NAME; + } + w = g_utf8_to_utf16(value, -1, NULL, NULL, NULL); + wcsncpy(_nicon_data.szTip, w, sizeof(_nicon_data.szTip) / sizeof(wchar_t)); + g_free(w); + Shell_NotifyIconW(NIM_MODIFY, &_nicon_data); } static void winpidgin_tray_minimize(PidginBuddyList *gtkblist) {
--- a/pidgin/win32/gtkwin32dep.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/gtkwin32dep.c Tue Mar 09 21:41:41 2010 +0000 @@ -47,8 +47,6 @@ #include "zlib.h" #include "untar.h" -#include <libintl.h> - #include "gtkwin32dep.h" #include "win32dep.h" #include "gtkconv.h" @@ -64,8 +62,6 @@ HWND messagewin_hwnd; static int gtkwin32_handle; -typedef BOOL (CALLBACK* LPFNFLASHWINDOWEX)(PFLASHWINFO); -static LPFNFLASHWINDOWEX MyFlashWindowEx = NULL; static gboolean pwm_handles_connections = TRUE; @@ -308,6 +304,7 @@ void winpidgin_window_flash(GtkWindow *window, gboolean flash) { GdkWindow * gdkwin; + FLASHWINFO info; g_return_if_fail(window != NULL); @@ -319,25 +316,19 @@ if(GDK_WINDOW_DESTROYED(gdkwin)) return; - if(MyFlashWindowEx) { - FLASHWINFO info; + memset(&info, 0, sizeof(FLASHWINFO)); + info.cbSize = sizeof(FLASHWINFO); + info.hwnd = GDK_WINDOW_HWND(gdkwin); + if (flash) { + DWORD flashCount; + info.uCount = 3; + if (SystemParametersInfo(SPI_GETFOREGROUNDFLASHCOUNT, 0, &flashCount, 0)) + info.uCount = flashCount; + info.dwFlags = FLASHW_ALL | FLASHW_TIMER; + } else + info.dwFlags = FLASHW_STOP; + info.dwTimeout = 0; - memset(&info, 0, sizeof(FLASHWINFO)); - info.cbSize = sizeof(FLASHWINFO); - info.hwnd = GDK_WINDOW_HWND(gdkwin); - if (flash) { - DWORD flashCount; - info.uCount = 3; - if (SystemParametersInfo(SPI_GETFOREGROUNDFLASHCOUNT, 0, &flashCount, 0)) - info.uCount = flashCount; - info.dwFlags = FLASHW_ALL | FLASHW_TIMER; - } else - info.dwFlags = FLASHW_STOP; - info.dwTimeout = 0; - - MyFlashWindowEx(&info); - } else - FlashWindow(GDK_WINDOW_HWND(gdkwin), flash); } void @@ -384,11 +375,27 @@ } void winpidgin_init(HINSTANCE hint) { + FARPROC proc; purple_debug_info("winpidgin", "winpidgin_init start\n"); exe_hInstance = hint; + proc = wpurple_find_and_loadproc("exchndl.dll", "SetLogFile"); + if (proc) { + gchar *debug_dir, *locale_debug_dir; + + debug_dir = g_build_filename(purple_user_dir(), "pidgin.RPT", NULL); + locale_debug_dir = g_locale_from_utf8(debug_dir, -1, NULL, NULL, NULL); + + purple_debug_info("winpidgin", "Setting exchndl.dll LogFile to %s\n", debug_dir); + + (proc)(locale_debug_dir); + + g_free(debug_dir); + g_free(locale_debug_dir); + } + /* IdleTracker Initialization */ if(!winpidgin_set_idlehooks()) purple_debug_error("winpidgin", "Failed to initialize idle tracker\n"); @@ -399,8 +406,6 @@ messagewin_hwnd = winpidgin_message_window_init(); - MyFlashWindowEx = (LPFNFLASHWINDOWEX) wpurple_find_and_loadproc("user32.dll", "FlashWindowEx"); - purple_debug_info("winpidgin", "winpidgin_init end\n"); } @@ -436,36 +441,16 @@ return TRUE; } -typedef HMONITOR WINAPI _MonitorFromWindow(HWND, DWORD); -typedef BOOL WINAPI _GetMonitorInfo(HMONITOR, LPMONITORINFO); - static gboolean get_WorkingAreaRectForWindow(HWND hwnd, RECT *workingAreaRc) { - static _MonitorFromWindow *the_MonitorFromWindow; - static _GetMonitorInfo *the_GetMonitorInfo; - static gboolean initialized = FALSE; HMONITOR monitor; MONITORINFO info; - if(!initialized) { - the_MonitorFromWindow = (_MonitorFromWindow*) - wpurple_find_and_loadproc("user32", "MonitorFromWindow"); - the_GetMonitorInfo = (_GetMonitorInfo*) - wpurple_find_and_loadproc("user32", "GetMonitorInfoA"); - initialized = TRUE; - } - - if(!the_MonitorFromWindow) - return FALSE; - - if(!the_GetMonitorInfo) - return FALSE; - - monitor = the_MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); + monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTOPRIMARY); info.cbSize = sizeof(info); - if(!the_GetMonitorInfo(monitor, &info)) + if(!GetMonitorInfo(monitor, &info)) return FALSE; CopyRect(workingAreaRc, &(info.rcWork));
--- a/pidgin/win32/nsis/available.lst Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/available.lst Tue Mar 09 21:41:41 2010 +0000 @@ -4,72 +4,46 @@ sq,AL,sq_AL,Albanian (Albania),sq_AL.zip bg,BG,bg_BG,Bulgarian (Bulgaria),bg_BG.zip ca,ES,ca_ES,Catalan (Spain),ca_ES.zip +cop,EG,cop_EG,Coptic (Egypt),cop_EG.zip hr,HR,hr_HR,Croatian (Croatia),hr_HR.zip cs,CZ,cs_CZ,Czech (Czech Republic),cs_CZ.zip da,DK,da_DK,Danish (Denmark),da_DK.zip nl,NL,nl_NL,Dutch (Netherlands),nl_NL.zip nl,NL,nl_med,Dutch Medical (Netherlands),nl_med.zip -nl,BE,nl_NL,Dutch (Belgium),nl_NL.zip -nl,BE,nl_NL,Dutch Medical (Belgium),nl_NL.zip en,AU,en_AU,English (Australia),en_AU.zip en,CA,en_CA,English (Canada),en_CA.zip en,NZ,en_NZ,English (New Zealand),en_NZ.zip en,ZA,en_ZA,English (South Africa),en_ZA.zip en,GB,en_GB,English (United Kingdom),en_GB.zip en,US,en_US,English (United States),en_US.zip -cop,EG,cop_EG,Coptic (Egypt),cop_EG.zip eo,ANY,eo_l3,Esperanto,eo.zip et,EE,et_EE,Estonian (Estonia),et_EE.zip fo,FO,fo_FO,Faroese (Faroe Islands),fo_FO.zip -fr,BE,fr_FR,Français Réforme 1990 & Classique (Belgium),fr_FR_1-3-2.zip -fr,CA,fr_FR,Français Réforme 1990 & Classique (Canada),fr_FR_1-3-2.zip -fr,FR,fr_FR,Français Réforme 1990 & Classique (France),fr_FR_1-3-2.zip -fr,LU,fr_FR,Français Réforme 1990 & Classique (Luxembourg),fr_FR_1-3-2.zip -fr,MC,fr_FR,Français Réforme 1990 & Classique (Monaco),fr_FR_1-3-2.zip -fr,CH,fr_FR,Français Réforme 1990 & Classique (Switzerland),fr_FR_1-3-2.zip -fr,BE,fr_FR-classique,Français Classique (Belgium),fr_FR-classique_1-3-2.zip -fr,CA,fr_FR-classique,Français Classique (Canada),fr_FR-classique_1-3-2.zip -fr,FR,fr_FR-classique,Français Classique (France),fr_FR-classique_1-3-2.zip -fr,LU,fr_FR-classique,Français Classique (Luxembourg),fr_FR-classique_1-3-2.zip -fr,MC,fr_FR-classique,Français Classique (Monaco),fr_FR-classique_1-3-2.zip -fr,CH,fr_FR-classique,Français Classique (Switzerland),fr_FR-classique_1-3-2.zip -fr,BE,fr_FR-1990,Français Réforme 1990 (Belgium),fr_FR-1990_1-3-2.zip -fr,CA,fr_FR-1990,Français Réforme 1990 (Canada),fr_FR-1990_1-3-2.zip -fr,FR,fr_FR-1990,Français Réforme 1990 (France),fr_FR-1990_1-3-2.zip -fr,LU,fr_FR-1990,Français Réforme 1990 (Luxembourg),fr_FR-1990_1-3-2.zip -fr,MC,fr_FR-1990,Français Réforme 1990 (Monaco),fr_FR-1990_1-3-2.zip -fr,CH,fr_FR-1990,Français Réforme 1990 (Switzerland),fr_FR-1990_1-3-2.zip +fr,FR,fr_FR-classique,Franais Classique,fr_FR-classique_1-3-2.zip +fr,FR,fr_FR-1990,Franais Rforme 1990,fr_FR-1990_1-3-2.zip +fr,FR,fr_FR,Franais Rforme 1990 & Classique,fr_FR_1-3-2.zip fy,NL,fy_NL,Frisian (Netherlands),fy_NL.zip gl,ES,gl_ES,Galician (Spain),gl_ES.zip gsc,FR,gsc_FR,Gascon (France),gsc_FR.zip -de,AT,de_DE,German (Austria Base),de_DE.zip de,AT,de_AT,German (Austria Extension),de_AT.zip de,AT,de_AT_frami,German (Austria) neu 08/2006 (frami),de_AT_frami.zip de,DE,de_DE,German (Germany),de_DE.zip de,DE,de_DE_frami,German (Germany) neu 08/2006 (frami),de_DE_frami.zip -de,LI,de_CH,German (Liechtenstein),de_CH.zip -de,LI,de_CH_frami,German (Liechtenstein) neu 08/2006 (frami),de_CH_frami.zip -de,LU,de_DE,German (Luxembourg),de_DE.zip -de,LU,de_DE_frami,German (Luxembourg) neu 08/2006 (frami),de_DE_frami.zip de,CH,de_CH,German (Switzerland),de_CH.zip de,CT,de_CH_frami,German (Switzerland) neu 08/2006 (frami),de_CH_frami.zip el,GR,el_GR,Greek (Greece),el_GR.zip gu,IN,gu_IN,Gujarati (India),gu_IN.zip -gd,GB,gd_GB,Scots Gaelic (Scotland),gd_GB.zip he,IL,he_IL,Hebrew (Israel),he_IL.zip hil,PH,hil_PH,Hiligaynon (Philippines),hil_PH.zip hu,HU,hu_HU,Hungarian (Hungary),hu_HU.zip hu,HU,hu_HU_comb,Hungarian (Hungary) collected compounds,hu_HU_comb.zip id,ID,id_ID,Indonesian (Indonesia),id_ID.zip -ia,ANY,ia_ANY,Interlingua (ANY locale),ia_ANY.zip ga,IE,ga_IE,Irish (Ireland),ga_IE.zip it,IT,it_IT,Italian (Italy),it_IT.zip -it,CH,it_IT,Italian (Switzerland),it_IT.zip sw,KE,sw_KE,Kiswahili (Africa),sw_KE.zip +ku,TR,ku_TR,Kurdish (Turkey),ku_TR.zip +it,IT,la,Latin,la.zip lv,LV,lv_LV,Latvian (Latvia),lv_LV.zip -ku,TR,ku_TR,Kurdish (Turkey),ku_TR.zip -ku,TR,ku_TR,Kurdish (Syria),ku_TR.zip -it,IT,la,Latin (for x-register),la.zip lt,LT,lt_LT,Lithuanian (Lithuania),lt_LT.zip mk,MK,mk_MK,Macedonian (Macedonia),mk_MK.zip ms,MY,ms_MY,Malay (Malaysia),ms_MY.zip @@ -89,6 +63,7 @@ ru,RU,ru_RU,Russian (Russia),ru_RU.zip ru,RU,ru_RU_ye,Russian_ye (Russia),ru_RU_ye.zip ru,RU,ru_RU_yo,Russian_yo (Russia),ru_RU_yo.zip +gd,GB,gd_GB,Scots Gaelic (Scotland),gd_GB.zip tn,ZA,tn_ZA,Setswana (Africa),tn_ZA.zip sk,SK,sk_SK,Slovak (Slovakia),sk_SK.zip sl,SI,sl_SI,Slovenian (Slovenia),sl_SI.zip @@ -104,7 +79,6 @@ es,EC,es_EC,Spanish (Ecuador),es_EC.zip es,SV,es_SV,Spanish (El Salvador),es_SV.zip es,GT,es_GT,Spanish (Guatemala),es_GT.zip -es,HN,es_HN,Spanish (Honduras),es_HN.zip es,MX,es_MX,Spanish (Mexico),es_MX.zip es,NI,es_NI,Spanish (Nicaragua),es_NI.zip es,PA,es_PA,Spanish (Panama),es_PA.zip @@ -118,8 +92,7 @@ sv,SE,sv_SE,Swedish (Sweden),sv_SE.zip ts,ZA,ts_ZA,Tsonga (South Africa),ts_ZA.zip uk,UA,uk_UA,Ukrainian (Ukraine),uk_UA.zip -ur,IN,ur_PK,Urdu (India),ur_PK.zip -ur,PK,ur_PK,Urdu (Pakistan),ur_PK.zip +ur,PK,ur_PK,Urdu,ur_PK.zip ve,ZA,ve_ZA,Venda (South Africa),ve_ZA.zip vi,VN,vi_VN,Vietnamese (Viet-Nam),vi_VN.zip cy,GB,cy_GB,Welsh (Wales),cy_GB.zip
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/win32/nsis/generate_gtk_zip.sh Tue Mar 09 21:41:41 2010 +0000 @@ -0,0 +1,80 @@ +#!/bin/bash +# Script to generate zip file for GTK+ runtime to be included in Pidgin installer + +PIDGIN_BASE=$1 + +if [ ! -e $PIDGIN_BASE/ChangeLog.win32 ]; 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 +#Subdirectory of $STAGE_DIR +INSTALL_DIR=Gtk +CONTENTS_FILE=$INSTALL_DIR/CONTENTS + +#This needs to be changed every time there is any sort of change. +BUNDLE_VERSION=2.14.7.0 + +ATK="http://ftp.gnome.org/pub/gnome/binaries/win32/atk/1.24/atk_1.24.0-1_win32.zip ATK 1.24.0-1" +CAIRO="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/cairo_1.8.10-1_win32.zip Cairo 1.8.10-1" +EXPAT="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/expat_2.0.1-1_win32.zip Expat 2.0.1-1" +FONTCONFIG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/fontconfig_2.8.0-2_win32.zip Fontconfig 2.8.0-2" +FREETYPE="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/freetype_2.3.11-2_win32.zip Freetype 2.3.11-2" +GETTEXT="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/gettext-runtime-0.17-1.zip Gettext 0.17-1" +GLIB="http://ftp.gnome.org/pub/gnome/binaries/win32/glib/2.20/glib_2.20.5-1_win32.zip Glib 2.20.5-1" +GTK="http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.14/gtk+_2.14.7-1_win32.zip GTK+ 2.14.7-1" +LIBJPEG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/jpeg-6b-4.zip libjpeg 6b-4" +#Used by GTK+ +LIBPNG="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libpng_1.2.39-1_win32.zip libpng 1.2.39-1" +#Used by Cairo +LIBPNG2="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libpng_1.4.0-1_win32.zip libpng 1.4.0-1" +LIBTIFF="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/libtiff_3.9.1-1_win32.zip libtiff 3.9.1-1" +PANGO="http://ftp.gnome.org/pub/gnome/binaries/win32/pango/1.26/pango_1.26.2-1_win32.zip Pango 1.26.2-1" +ZLIB="http://ftp.gnome.org/pub/gnome/binaries/win32/dependencies/zlib-1.2.3.zip zlib 1.2.3" + +ALL="ATK CAIRO EXPAT FONTCONFIG FREETYPE GETTEXT GLIB GTK LIBJPEG LIBPNG LIBPNG2 LIBTIFF PANGO ZLIB" + +mkdir -p $STAGE_DIR +cd $STAGE_DIR + +rm -rf $INSTALL_DIR +mkdir $INSTALL_DIR + +#new CONTENTS file +echo Bundle Version $BUNDLE_VERSION > $CONTENTS_FILE + +function download_and_extract { + URL=${1%%\ *} + NAME=${1#*\ } + FILE=`basename $URL` + if [ ! -e $FILE ]; then + echo Downloading $NAME + wget $URL + fi + unzip -q $FILE -d $INSTALL_DIR + echo "$NAME" >> $CONTENTS_FILE +} + +for VAL in $ALL +do + VAR=${!VAL} + download_and_extract "$VAR" +done + +#Default GTK+ Theme to MS-Windows +echo gtk-theme-name = \"MS-Windows\" > $INSTALL_DIR/etc/gtk-2.0/gtkrc + +#Blow away translations that we don't have in Pidgin +for LOCALE_DIR in $INSTALL_DIR/share/locale/* +do + LOCALE=`basename $LOCALE_DIR` + if [ ! -e $PIDGIN_BASE/po/$LOCALE.po ]; then + echo Remove $LOCALE translation as it is missing from Pidgin + rm -r $LOCALE_DIR + fi +done + +#Generate zip file to be included in installer +zip -9 -r ../gtk-runtime-$BUNDLE_VERSION.zip Gtk +
--- a/pidgin/win32/nsis/langmacros.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/langmacros.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -48,6 +48,7 @@ !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_DESKTOP_SHORTCUT_DESC ${CUR_LANG} !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_STARTMENU_SHORTCUT_DESC ${CUR_LANG} !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT DEBUG_SYMBOLS_SECTION_TITLE ${CUR_LANG} + !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT TRANSLATIONS_SECTION_TITLE ${CUR_LANG} ; Installer Finish Page !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_FINISH_VISIT_WEB_SITE ${CUR_LANG} @@ -65,34 +66,10 @@ ; Spellcheck Section Prompts !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SECTION_TITLE ${CUR_LANG} !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ERROR ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_DICT_ERROR ${CUR_LANG} !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SECTION_DESCRIPTION ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT ASPELL_INSTALL_FAILED ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_BRETON ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_CATALAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_CZECH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_WELSH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_DANISH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_GERMAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ENGLISH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_GREEK ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ESPERANTO ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SPANISH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_FAROESE ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_FRENCH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ITALIAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_DUTCH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_NORWEGIAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_POLISH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_PORTUGUESE ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ROMANIAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_RUSSIAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SLOVAK ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SWEDISH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_UKRAINIAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_DEBUGSYMBOLS_ERROR ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_GTK_DOWNLOAD_ERROR ${CUR_LANG} + !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_DEBUGSYMBOLS_ERROR ${CUR_LANG} + !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_GTK_DOWNLOAD_ERROR ${CUR_LANG} !undef CUR_LANG !macroend
--- a/pidgin/win32/nsis/pidgin-installer.nsi Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Tue Mar 09 21:41:41 2010 +0000 @@ -28,6 +28,7 @@ ShowInstDetails show ShowUninstDetails show SetDateSave on +RequestExecutionLevel highest ; $name and $INSTDIR are set in .onInit function.. @@ -53,7 +54,6 @@ ;Defines !define PIDGIN_NSIS_INCLUDE_PATH "." -!define PIDGIN_INSTALLER_DEPS "..\..\..\..\win32-dev\pidgin-inst-deps-0.2" ; Remove these and the stuff that uses them at some point !define OLD_GAIM_REG_KEY "SOFTWARE\gaim" @@ -71,8 +71,8 @@ !define PERL_REG_KEY "SOFTWARE\Perl" !define PERL_DLL "perl510.dll" -!define ASPELL_REG_KEY "SOFTWARE\Aspell" !define DOWNLOADER_URL "http://pidgin.im/win32/download_redir.php" +!define SPELL_DOWNLOAD_URL "http://ftp.services.openoffice.org/pub/OpenOffice.org/contrib/dictionaries" !define MEMENTO_REGISTRY_ROOT HKLM !define MEMENTO_REGISTRY_KEY "${PIDGIN_UNINSTALL_KEY}" @@ -302,7 +302,7 @@ IfErrors uninstall_problem ; Ready to uninstall.. ClearErrors - ExecWait '"$TEMP\$R6" /S /KEEPGTK=1 _?=$R1' + ExecWait '"$TEMP\$R6" /S /UPGRADE=1 _?=$R1' IfErrors exec_error Delete "$TEMP\$R6" Goto done @@ -340,7 +340,7 @@ !ifdef OFFLINE_INSTALLER SetOutPath $PLUGINSDIR - File /oname=gtk.zip "..\..\..\..\gtk_installer\gtk-runtime-${GTK_INSTALL_VERSION}.zip" + File /oname=gtk.zip ".\gtk-runtime-${GTK_INSTALL_VERSION}.zip" !else @@ -352,12 +352,14 @@ Pop $R0 StrCmp $R0 "cancel" done StrCmp $R0 "success" +2 - MessageBox MB_RETRYCANCEL "$(PIDGIN_GTK_DOWNLOAD_ERROR) : $R1" /SD IDCANCEL IDRETRY retry IDCANCEL done + MessageBox MB_RETRYCANCEL "$(PIDGIN_GTK_DOWNLOAD_ERROR) : $R2" /SD IDCANCEL IDRETRY retry IDCANCEL done !endif + ;Delete the old Gtk directory + RMDir /r "$INSTDIR\Gtk" + SetOutPath "$INSTDIR" - nsisunz::UnzipToLog $R1 "$INSTDIR" Pop $R0 StrCmp $R0 "success" +2 @@ -415,7 +417,6 @@ Delete "$INSTDIR\plugins\libjabber.dll" File /r /x locale ..\..\..\${PIDGIN_INSTALL_DIR}\*.* - File "${PIDGIN_INSTALLER_DEPS}\exchndl.dll" ; Check if Perl is installed, if so add it to the AppPaths ReadRegStr $R2 HKLM ${PERL_REG_KEY} "" @@ -505,7 +506,7 @@ !macro LANG_SECTION lang ${MementoUnselectedSection} "${lang}" SecLang_${lang} SetOutPath "$INSTDIR\locale\${lang}\LC_MESSAGES" - File /oname=pidgin.mo "..\..\..\${PIDGIN_INSTALL_DIR}\locale\${lang}\LC_MESSAGES\pidgin.mo" + File "..\..\..\${PIDGIN_INSTALL_DIR}\locale\${lang}\LC_MESSAGES\*.mo" SetOutPath "$INSTDIR" ${MementoSectionEnd} !macroend @@ -518,109 +519,29 @@ ;-------------------------------- ;Spell Checking -SectionGroup /e $(PIDGIN_SPELLCHECK_SECTION_TITLE) SecSpellCheck - Section /o $(PIDGIN_SPELLCHECK_BRETON) SecSpellCheckBreton - Push ${SecSpellCheckBreton} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_CATALAN) SecSpellCheckCatalan - Push ${SecSpellCheckCatalan} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_CZECH) SecSpellCheckCzech - Push ${SecSpellCheckCzech} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_WELSH) SecSpellCheckWelsh - Push ${SecSpellCheckWelsh} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_DANISH) SecSpellCheckDanish - Push ${SecSpellCheckDanish} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_GERMAN) SecSpellCheckGerman - Push ${SecSpellCheckGerman} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_GREEK) SecSpellCheckGreek - Push ${SecSpellCheckGreek} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_ENGLISH) SecSpellCheckEnglish - Push ${SecSpellCheckEnglish} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_ESPERANTO) SecSpellCheckEsperanto - Push ${SecSpellCheckEsperanto} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_SPANISH) SecSpellCheckSpanish - Push ${SecSpellCheckSpanish} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_FAROESE) SecSpellCheckFaroese - Push ${SecSpellCheckFaroese} - Call InstallAspellAndDict +!macro SPELLCHECK_SECTION lang lang_name lang_file + Section /o "${lang_name}" SecSpell_${lang} + Push ${lang_file} + Push ${lang} + Call InstallDict SectionEnd - Section /o $(PIDGIN_SPELLCHECK_FRENCH) SecSpellCheckFrench - Push ${SecSpellCheckFrench} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_ITALIAN) SecSpellCheckItalian - Push ${SecSpellCheckItalian} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_DUTCH) SecSpellCheckDutch - Push ${SecSpellCheckDutch} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_NORWEGIAN) SecSpellCheckNorwegian - Push ${SecSpellCheckNorwegian} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_POLISH) SecSpellCheckPolish - Push ${SecSpellCheckPolish} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_PORTUGUESE) SecSpellCheckPortuguese - Push ${SecSpellCheckPortuguese} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_ROMANIAN) SecSpellCheckRomanian - Push ${SecSpellCheckRomanian} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_RUSSIAN) SecSpellCheckRussian - Push ${SecSpellCheckRussian} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_SLOVAK) SecSpellCheckSlovak - Push ${SecSpellCheckSlovak} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_SWEDISH) SecSpellCheckSwedish - Push ${SecSpellCheckSwedish} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_UKRAINIAN) SecSpellCheckUkrainian - Push ${SecSpellCheckUkrainian} - Call InstallAspellAndDict - SectionEnd +!macroend +SectionGroup $(PIDGIN_SPELLCHECK_SECTION_TITLE) SecSpellCheck + !include "pidgin-spellcheck.nsh" SectionGroupEnd Section /o $(DEBUG_SYMBOLS_SECTION_TITLE) SecDebugSymbols + InitPluginsDir - - ; We need to download and extract the debug symbols - StrCpy $R1 "$PLUGINSDIR\pidgin-${PIDGIN_VERSION}-dbgsym.zip" + StrCpy $R1 "$PLUGINSDIR\dbgsym.zip" !ifdef OFFLINE_INSTALLER SetOutPath $PLUGINSDIR - File /oname=pidgin-${PIDGIN_VERSION}-dbgsym.zip "..\..\..\..\gtk_installer\gtk-runtime-${GTK_INSTALL_VERSION}.zip" + File /oname=dbgsym.zip "..\..\..\pidgin-${PIDGIN_VERSION}-dbgsym.zip" !else + ; We need to download the debug symbols retry: StrCpy $R2 "${DOWNLOADER_URL}?version=${PIDGIN_VERSION}&dl_pkg=dbgsym" DetailPrint "Downloading Debug Symbols... ($R2)" @@ -632,6 +553,7 @@ !endif + SetOutPath "$INSTDIR" nsisunz::UnzipToLog $R1 "$INSTDIR" Pop $R0 StrCmp $R0 "success" +2 @@ -769,27 +691,36 @@ Delete "$INSTDIR\sounds\purple\send.wav" RMDir "$INSTDIR\sounds\purple" RMDir "$INSTDIR\sounds" + Delete "$INSTDIR\spellcheck\libenchant.dll" + Delete "$INSTDIR\spellcheck\libgtkspell-0.dll" + Delete "$INSTDIR\spellcheck\lib\enchant\libenchant_aspell.dll" + Delete "$INSTDIR\spellcheck\lib\enchant\libenchant_ispell.dll" + Delete "$INSTDIR\spellcheck\lib\enchant\libenchant_myspell.dll" + RMDir "$INSTDIR\spellcheck\lib\enchant" + RMDir "$INSTDIR\spellcheck\lib" + RMDir "$INSTDIR\spellcheck" Delete "$INSTDIR\freebl3.dll" Delete "$INSTDIR\idletrack.dll" - Delete "$INSTDIR\libgtkspell.dll" Delete "$INSTDIR\libjabber.dll" + Delete "$INSTDIR\libnspr4.dll" Delete "$INSTDIR\libmeanwhile-1.dll" Delete "$INSTDIR\liboscar.dll" + Delete "$INSTDIR\libplc4.dll" + Delete "$INSTDIR\libplds4.dll" Delete "$INSTDIR\libpurple.dll" Delete "$INSTDIR\libsasl.dll" Delete "$INSTDIR\libsilc-1-1-2.dll" Delete "$INSTDIR\libsilcclient-1-1-2.dll" - Delete "$INSTDIR\libxml2.dll" + Delete "$INSTDIR\libxml2-2.dll" Delete "$INSTDIR\libymsg.dll" - Delete "$INSTDIR\nspr4.dll" Delete "$INSTDIR\nss3.dll" + Delete "$INSTDIR\nssutil3.dll" Delete "$INSTDIR\nssckbi.dll" Delete "$INSTDIR\pidgin.dll" Delete "$INSTDIR\pidgin.exe" - Delete "$INSTDIR\plc4.dll" - Delete "$INSTDIR\plds4.dll" Delete "$INSTDIR\smime3.dll" Delete "$INSTDIR\softokn3.dll" + Delete "$INSTDIR\sqlite3.dll" Delete "$INSTDIR\ssl3.dll" Delete "$INSTDIR\${PIDGIN_UNINST_EXE}" Delete "$INSTDIR\exchndl.dll" @@ -801,10 +732,13 @@ ; Remove the local GTK+ copy (if we're not just upgrading) ${GetParameters} $R0 ClearErrors - ${GetOptions} "$R3" "/KEEPGTK=" $R1 + ${GetOptions} "$R0" "/UPGRADE=" $R1 IfErrors +2 - StrCmp $R1 "1" +2 + StrCmp $R1 "1" upgrade_done RMDir /r "$INSTDIR\Gtk" + ; Remove the downloaded spellcheck dictionaries (if we're not just upgrading) + RMDir /r "$INSTDIR\spellcheck" + upgrade_done: ;Try to remove Pidgin install dir (only if empty) RMDir "$INSTDIR" @@ -843,50 +777,7 @@ !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheck} \ $(PIDGIN_SPELLCHECK_SECTION_DESCRIPTION) - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckBreton} \ - "$(PIDGIN_SPELLCHECK_BRETON) (862kb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckCatalan} \ - "$(PIDGIN_SPELLCHECK_CATALAN) (3.9Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckCzech} \ - "$(PIDGIN_SPELLCHECK_CZECH) (17Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckWelsh} \ - "$(PIDGIN_SPELLCHECK_WELSH) (4.2Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckDanish} \ - "$(PIDGIN_SPELLCHECK_DANISH) (6.9Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckGerman} \ - "$(PIDGIN_SPELLCHECK_GERMAN) (5.4Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckGreek} \ - "$(PIDGIN_SPELLCHECK_GREEK) (7.1Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckEnglish} \ - "$(PIDGIN_SPELLCHECK_ENGLISH) (2.3Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckEsperanto} \ - "$(PIDGIN_SPELLCHECK_ESPERANTO) (5.7Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckSpanish} \ - "$(PIDGIN_SPELLCHECK_SPANISH) (7.0Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckFaroese} \ - "$(PIDGIN_SPELLCHECK_FAROESE) (913kb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckFrench} \ - "$(PIDGIN_SPELLCHECK_FRENCH) (9.3Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckItalian} \ - "$(PIDGIN_SPELLCHECK_ITALIAN) (770kb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckDutch} \ - "$(PIDGIN_SPELLCHECK_DUTCH) (3.7Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckNorwegian} \ - "$(PIDGIN_SPELLCHECK_NORWEGIAN) (3.2Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckPolish} \ - "$(PIDGIN_SPELLCHECK_POLISH) (9.3Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckPortuguese} \ - "$(PIDGIN_SPELLCHECK_PORTUGUESE) (5.5Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckRomanian} \ - "$(PIDGIN_SPELLCHECK_ROMANIAN) (906kb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckRussian} \ - "$(PIDGIN_SPELLCHECK_RUSSIAN) (11Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckSlovak} \ - "$(PIDGIN_SPELLCHECK_SLOVAK) (8.0Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckSwedish} \ - "$(PIDGIN_SPELLCHECK_SWEDISH) (2.2Mb)" - !insertmacro MUI_DESCRIPTION_TEXT ${SecSpellCheckUkrainian} \ - "$(PIDGIN_SPELLCHECK_UKRAINIAN) (12Mb)" + !insertmacro MUI_FUNCTION_DESCRIPTION_END ;-------------------------------- @@ -1233,12 +1124,6 @@ ${MementoSectionRestore} - !insertmacro SetSectionFlag ${SecSpellCheck} ${SF_RO} - !insertmacro UnselectSection ${SecSpellCheck} - - ;Mark the dictionaries that are already installed as readonly - Call SelectAndDisableInstalledDictionaries - ;Preselect the URI handlers as appropriate Call SelectURIHandlerSelections @@ -1337,6 +1222,10 @@ StrCpy $INSTDIR "$R2\Pidgin" instdir_done: + + ;Mark the dictionaries that are already installed as readonly + Call SelectAndDisableInstalledDictionaries + ;LogSet on Pop $R3 Pop $R2 @@ -1368,6 +1257,10 @@ Push $R0 Push $R1 +!ifdef OFFLINE_INSTALLER + !insertmacro SelectSection ${SecDebugSymbols} +!endif + Call DoWeNeedGtk Pop $R0 IntCmp $R0 1 done gtk_not_mandatory @@ -1389,234 +1282,72 @@ ; Convert the a Section index to the language code ; Push the section index onto the stack and pop off the language code after the call ; This will set the error code, if no match is found -Function GetLangCodeForSection - ClearErrors - Push $R0 - Exch - Pop $R0 ;This is the section index - - IntCmp $R0 ${SecSpellCheckBreton} 0 +3 +3 - StrCpy $R0 "br" - Goto done - IntCmp $R0 ${SecSpellCheckCatalan} 0 +3 +3 - StrCpy $R0 "ca" - Goto done - IntCmp $R0 ${SecSpellCheckCzech} 0 +3 +3 - StrCpy $R0 "cs" - Goto done - IntCmp $R0 ${SecSpellCheckWelsh} 0 +3 +3 - StrCpy $R0 "cy" - Goto done - IntCmp $R0 ${SecSpellCheckDanish} 0 +3 +3 - StrCpy $R0 "da" - Goto done - IntCmp $R0 ${SecSpellCheckGerman} 0 +3 +3 - StrCpy $R0 "de" - Goto done - IntCmp $R0 ${SecSpellCheckGreek} 0 +3 +3 - StrCpy $R0 "el" - Goto done - IntCmp $R0 ${SecSpellCheckEnglish} 0 +3 +3 - StrCpy $R0 "en" - Goto done - IntCmp $R0 ${SecSpellCheckEsperanto} 0 +3 +3 - StrCpy $R0 "eo" - Goto done - IntCmp $R0 ${SecSpellCheckSpanish} 0 +3 +3 - StrCpy $R0 "es" - Goto done - IntCmp $R0 ${SecSpellCheckFaroese} 0 +3 +3 - StrCpy $R0 "fo" - Goto done - IntCmp $R0 ${SecSpellCheckFrench} 0 +3 +3 - StrCpy $R0 "fr" - Goto done - IntCmp $R0 ${SecSpellCheckItalian} 0 +3 +3 - StrCpy $R0 "it" - Goto done - IntCmp $R0 ${SecSpellCheckDutch} 0 +3 +3 - StrCpy $R0 "nl" - Goto done - IntCmp $R0 ${SecSpellCheckNorwegian} 0 +3 +3 - StrCpy $R0 "no" - Goto done - IntCmp $R0 ${SecSpellCheckPolish} 0 +3 +3 - StrCpy $R0 "pl" - Goto done - IntCmp $R0 ${SecSpellCheckPortuguese} 0 +3 +3 - StrCpy $R0 "pt" - Goto done - IntCmp $R0 ${SecSpellCheckRomanian} 0 +3 +3 - StrCpy $R0 "ro" - Goto done - IntCmp $R0 ${SecSpellCheckRussian} 0 +3 +3 - StrCpy $R0 "ru" - Goto done - IntCmp $R0 ${SecSpellCheckSlovak} 0 +3 +3 - StrCpy $R0 "sk" - Goto done - IntCmp $R0 ${SecSpellCheckSwedish} 0 +3 +3 - StrCpy $R0 "sv" - Goto done - IntCmp $R0 ${SecSpellCheckUkrainian} 0 +3 +3 - StrCpy $R0 "uk" - Goto done - - SetErrors - - done: - Exch $R0 -FunctionEnd ;GetLangCodeForSection ; Select and Disable any Sections that have currently installed dictionaries +!macro CHECK_SPELLCHECK_SECTION lang + ;Advance to the next (correct) section index + IntOp $R0 $R0 + 1 + IfFileExists "$INSTDIR\spellcheck\share\enchant\myspell\${lang}.dic" 0 done_${lang} + SectionGetFlags $R0 $R1 + IntOp $R1 $R1 | ${SF_RO} ; Mark Readonly + IntOp $R1 $R1 | ${SF_SELECTED} ; Select + SectionSetFlags $R0 $R1 + done_${lang}: +!macroend Function SelectAndDisableInstalledDictionaries Push $R0 Push $R1 - Push $R2 - - ; Start with the first language dictionary - IntOp $R0 ${SecSpellCheck} + 1 - - start: - ; If it is the end of the section group, stop - SectionGetFlags $R0 $R1 - IntOp $R2 $R1 & ${SF_SECGRPEND} - IntCmp $R2 ${SF_SECGRPEND} done - - Push $R0 - Call GetLangCodeForSection - Pop $R2 - IfErrors end_loop - ReadRegStr $R2 HKLM "${ASPELL_REG_KEY}-$R2" "" ; Check that the dictionary is installed - StrCmp $R2 "" end_loop ; If it isn't installed, skip to the next item - IntOp $R1 $R1 | ${SF_RO} ; Mark Readonly - IntOp $R1 $R1 | ${SF_SELECTED} ; Select - SectionSetFlags $R0 $R1 - - end_loop: - IntOp $R0 $R0 + 1 ;Advance to the next section - Goto start - done: - Pop $R2 - Pop $R1 - Pop $R0 -FunctionEnd - -Function InstallAspellAndDict - Push $R0 - Exch - Call GetLangCodeForSection - Pop $R0 ;This is the language code - Push $R1 - - InitPluginsDir - - IfErrors done ; We weren't able to convert the section to lang code + !insertmacro SetSectionFlag ${SecSpellCheck} ${SF_RO} + !insertmacro UnselectSection ${SecSpellCheck} - retry: - Call InstallAspell - Pop $R1 - StrCmp $R1 "" +3 - StrCmp $R1 "cancel" done - MessageBox MB_RETRYCANCEL "$(PIDGIN_SPELLCHECK_ERROR) : $R1" /SD IDCANCEL IDRETRY retry IDCANCEL done - - retry_dict: - Push $R0 - Call InstallAspellDictionary - Pop $R1 - StrCmp $R1 "" +3 - StrCmp $R1 "cancel" done - MessageBox MB_RETRYCANCEL "$(PIDGIN_SPELLCHECK_DICT_ERROR) : $R1" /SD IDCANCEL IDRETRY retry_dict - - done: + IntOp $R0 ${SecSpellCheck} + 0 + !include "pidgin-spellcheck-preselect.nsh" Pop $R1 Pop $R0 FunctionEnd -Function InstallAspell - Push $R0 - Push $R1 - Push $R2 - - check: - ClearErrors - ReadRegDWORD $R0 HKLM ${ASPELL_REG_KEY} "AspellVersion" - IntCmp $R0 15 installed - - ; If this is the check after installation, don't infinite loop on failure - StrCmp $R1 "$PLUGINSDIR\aspell_installer.exe" 0 +3 - StrCpy $R0 $(ASPELL_INSTALL_FAILED) - Goto done - - ; We need to download and install aspell - StrCpy $R1 "$PLUGINSDIR\aspell_installer.exe" - StrCpy $R2 "${DOWNLOADER_URL}?version=${PIDGIN_VERSION}&dl_pkg=aspell_core" - DetailPrint "Downloading Aspell... ($R2)" - NSISdl::download /TIMEOUT=10000 $R2 $R1 - Pop $R0 - StrCmp $R0 "success" +2 - Goto done - ExecWait '"$R1"' - Delete $R1 - Goto check ; Check that it is now installed correctly - - installed: ;Aspell is currently installed, no error message - DetailPrint "Aspell is installed" - StrCpy $R0 '' - - done: - Pop $R2 - Pop $R1 - Exch $R0 -FunctionEnd - -Function InstallAspellDictionary +Function InstallDict Push $R0 Exch Pop $R0 ;This is the language code Push $R1 + Exch 2 + Pop $R1 ;This is the language file Push $R2 Push $R3 - Push $R4 - check: ClearErrors - ReadRegStr $R2 HKLM "${ASPELL_REG_KEY}-$R0" "" - StrCmp $R2 "" 0 installed + IfFileExists "$INSTDIR\spellcheck\share\enchant\myspell\$R0.dic" installed + + InitPluginsDir - ; If this is the check after installation, don't infinite loop on failure - StrCmp $R1 "$PLUGINSDIR\aspell_dict-$R0.exe" 0 +3 - StrCpy $R0 $(ASPELL_INSTALL_FAILED) + ; We need to download and install dictionary + StrCpy $R2 "$PLUGINSDIR\$R1" + StrCpy $R3 "${SPELL_DOWNLOAD_URL}/$R1" + DetailPrint "Downloading the $R0 Dictionary... ($R3)" + retry: + NSISdl::download /TIMEOUT=10000 "$R3" "$R2" + Pop $R3 + StrCmp $R3 "cancel" done + StrCmp $R3 "success" +3 + MessageBox MB_RETRYCANCEL "$(PIDGIN_SPELLCHECK_ERROR) : $R3" /SD IDCANCEL IDRETRY retry IDCANCEL done + Goto done + SetOutPath "$INSTDIR\spellcheck\share\enchant\myspell" + nsisunz::UnzipToLog "$R2" "$OUTDIR" + SetOutPath "$INSTDIR" + Pop $R3 + StrCmp $R3 "success" installed + DetailPrint "$R3" ;print error message to log Goto done - ; We need to download and install aspell - StrCpy $R1 "$PLUGINSDIR\aspell_dict-$R0.exe" - StrCpy $R3 "${DOWNLOADER_URL}?version=${PIDGIN_VERSION}&dl_pkg=lang_$R0" - DetailPrint "Downloading the Aspell $R0 Dictionary... ($R3)" - NSISdl::download /TIMEOUT=10000 $R3 $R1 - Pop $R3 - StrCmp $R3 "success" +3 - StrCpy $R0 $R3 - Goto done - ; Use a specific temporary $OUTDIR for each dictionary because the installer doesn't clean up after itself - StrCpy $R4 "$OUTDIR" - SetOutPath "$PLUGINSDIR\aspell_dict-$R0" - ExecWait '"$R1"' - SetOutPath "$R4" - RMDir /r "$PLUGINSDIR\aspell_dict-$R0" - Delete $R1 - Goto check ; Check that it is now installed correctly - installed: ;The dictionary is currently installed, no error message - DetailPrint "Aspell $R0 Dictionary is installed" - StrCpy $R0 '' + DetailPrint "$R0 Dictionary is installed" done: - Pop $R4 Pop $R3 Pop $R2 - Pop $R1 - Exch $R0 + Pop $R0 + Exch $R1 FunctionEnd
--- a/pidgin/win32/nsis/translations/afrikaans.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/afrikaans.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -49,29 +49,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Speltoets-ondersteuning" !define PIDGIN_SPELLCHECK_ERROR "Fout met installering van speltoetser" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Fout met installering van speltoetswoordeboek" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Ondersteuning vir speltoeter. (Internetverbinding benodigd vir installasie)" -!define ASPELL_INSTALL_FAILED "Installasie het misluk" -!define PIDGIN_SPELLCHECK_BRETON "Bretons" -!define PIDGIN_SPELLCHECK_CATALAN "Katalaans" -!define PIDGIN_SPELLCHECK_CZECH "Tsjeggies" -!define PIDGIN_SPELLCHECK_WELSH "Wallies" -!define PIDGIN_SPELLCHECK_DANISH "Deens" -!define PIDGIN_SPELLCHECK_GERMAN "Duits" -!define PIDGIN_SPELLCHECK_GREEK "Grieks" -!define PIDGIN_SPELLCHECK_ENGLISH "Engels" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Spaans" -!define PIDGIN_SPELLCHECK_FAROESE "Faroes" -!define PIDGIN_SPELLCHECK_FRENCH "Frans" -!define PIDGIN_SPELLCHECK_ITALIAN "Italiaans" -!define PIDGIN_SPELLCHECK_DUTCH "Nederlands" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Noorweegs" -!define PIDGIN_SPELLCHECK_POLISH "Pools" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugees" -!define PIDGIN_SPELLCHECK_ROMANIAN "Roemeens" -!define PIDGIN_SPELLCHECK_RUSSIAN "Russies" -!define PIDGIN_SPELLCHECK_SLOVAK "Slowaaks" -!define PIDGIN_SPELLCHECK_SWEDISH "Sweeds" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Oekraens"
--- a/pidgin/win32/nsis/translations/arabic.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/arabic.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -47,29 +47,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE " " !define PIDGIN_SPELLCHECK_ERROR " " -!define PIDGIN_SPELLCHECK_DICT_ERROR " " !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION " . ( )" -!define ASPELL_INSTALL_FAILED " " -!define PIDGIN_SPELLCHECK_BRETON "Breton" -!define PIDGIN_SPELLCHECK_CATALAN "Catalan" -!define PIDGIN_SPELLCHECK_CZECH "Czech" -!define PIDGIN_SPELLCHECK_WELSH "Welsh" -!define PIDGIN_SPELLCHECK_DANISH "Danish" -!define PIDGIN_SPELLCHECK_GERMAN "German" -!define PIDGIN_SPELLCHECK_GREEK "Greek" -!define PIDGIN_SPELLCHECK_ENGLISH "English" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Spanish" -!define PIDGIN_SPELLCHECK_FAROESE "Faroese" -!define PIDGIN_SPELLCHECK_FRENCH "French" -!define PIDGIN_SPELLCHECK_ITALIAN "Italian" -!define PIDGIN_SPELLCHECK_DUTCH "Dutch" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norwegian" -!define PIDGIN_SPELLCHECK_POLISH "Polish" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portuguese" -!define PIDGIN_SPELLCHECK_ROMANIAN "Romanian" -!define PIDGIN_SPELLCHECK_RUSSIAN "Russian" -!define PIDGIN_SPELLCHECK_SLOVAK "Slovak" -!define PIDGIN_SPELLCHECK_SWEDISH "Swedish" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrainian"
--- a/pidgin/win32/nsis/translations/basque.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/basque.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -47,29 +47,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Zuzentzaile Ortografikoa" !define PIDGIN_SPELLCHECK_ERROR "Errorea Zuzentzaile Ortografikoa instalatzean" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Errorea Zuzentzaile Ortografikoarentzako hiztegia instalatzean" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Zuzentzaile Ortografikoa. (Internet konexioa behar du instalatzeko)" -!define ASPELL_INSTALL_FAILED "Ezin izan da instalatu" -!define PIDGIN_SPELLCHECK_BRETON "Britaniera" -!define PIDGIN_SPELLCHECK_CATALAN "Katalana" -!define PIDGIN_SPELLCHECK_CZECH "Txekiera" -!define PIDGIN_SPELLCHECK_WELSH "Gaelikoa" -!define PIDGIN_SPELLCHECK_DANISH "Daniera" -!define PIDGIN_SPELLCHECK_GERMAN "Alemana" -!define PIDGIN_SPELLCHECK_GREEK "Grekoa" -!define PIDGIN_SPELLCHECK_ENGLISH "Ingelesa" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperantoa" -!define PIDGIN_SPELLCHECK_SPANISH "Gaztelania" -!define PIDGIN_SPELLCHECK_FAROESE "Faroera" -!define PIDGIN_SPELLCHECK_FRENCH "Frantsesa" -!define PIDGIN_SPELLCHECK_ITALIAN "Italiera" -!define PIDGIN_SPELLCHECK_DUTCH "Nederlandera" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norvegiera" -!define PIDGIN_SPELLCHECK_POLISH "Poloniera" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugesa" -!define PIDGIN_SPELLCHECK_ROMANIAN "Errumaniera" -!define PIDGIN_SPELLCHECK_RUSSIAN "Errusiera" -!define PIDGIN_SPELLCHECK_SLOVAK "Eslovakiera" -!define PIDGIN_SPELLCHECK_SWEDISH "Suediera" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukraniera"
--- a/pidgin/win32/nsis/translations/catalan.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/catalan.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -46,29 +46,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Suport a la Verificaci de l'Ortografia " !define PIDGIN_SPELLCHECK_ERROR "Error instal.lant verificaci de l'ortografia" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Error Instal.lant Diccionari per a Verificaci de l'Ortografia" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Suport per a Verificaci de l'Ortografia. (s necesaria connexi a internet per dur a terme la instal.laci)" -!define ASPELL_INSTALL_FAILED "La instal.laci ha fallat" -!define PIDGIN_SPELLCHECK_BRETON "Bret" -!define PIDGIN_SPELLCHECK_CATALAN "Catal" -!define PIDGIN_SPELLCHECK_CZECH "Txec" -!define PIDGIN_SPELLCHECK_WELSH "Galls" -!define PIDGIN_SPELLCHECK_DANISH "Dans" -!define PIDGIN_SPELLCHECK_GERMAN "Alemany" -!define PIDGIN_SPELLCHECK_GREEK "Grec" -!define PIDGIN_SPELLCHECK_ENGLISH "Angls" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Espanyol" -!define PIDGIN_SPELLCHECK_FAROESE "Feros" -!define PIDGIN_SPELLCHECK_FRENCH "Francs" -!define PIDGIN_SPELLCHECK_ITALIAN "Itali" -!define PIDGIN_SPELLCHECK_DUTCH "Holands" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Noruec" -!define PIDGIN_SPELLCHECK_POLISH "Polons" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugus" -!define PIDGIN_SPELLCHECK_ROMANIAN "Romans" -!define PIDGIN_SPELLCHECK_RUSSIAN "Rus" -!define PIDGIN_SPELLCHECK_SLOVAK "Eslovac" -!define PIDGIN_SPELLCHECK_SWEDISH "Suec" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ucrans"
--- a/pidgin/win32/nsis/translations/dutch.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/dutch.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -39,28 +39,4 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Spellingscontrole" !define PIDGIN_SPELLCHECK_ERROR "Fout bij installatie van spellingscontrole" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Fout bij installatie van woordenboek voor spellingscontrole" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Ondersteuning voor spellingscontrole. (Internetverbinding nodig voor installatie)" -!define ASPELL_INSTALL_FAILED "Installatie mislukt" -!define PIDGIN_SPELLCHECK_BRETON "Bretons" -!define PIDGIN_SPELLCHECK_CATALAN "Catalaans" -!define PIDGIN_SPELLCHECK_CZECH "Tsjechisch" -!define PIDGIN_SPELLCHECK_WELSH "Welsh" -!define PIDGIN_SPELLCHECK_DANISH "Deens" -!define PIDGIN_SPELLCHECK_GERMAN "Duits" -!define PIDGIN_SPELLCHECK_GREEK "Grieks" -!define PIDGIN_SPELLCHECK_ENGLISH "Engels" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Spaans" -!define PIDGIN_SPELLCHECK_FAROESE "Faroese" -!define PIDGIN_SPELLCHECK_FRENCH "Frans" -!define PIDGIN_SPELLCHECK_ITALIAN "Italiaans" -!define PIDGIN_SPELLCHECK_DUTCH "Nederlands" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Noors" -!define PIDGIN_SPELLCHECK_POLISH "Pools" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugees" -!define PIDGIN_SPELLCHECK_ROMANIAN "Roemeens" -!define PIDGIN_SPELLCHECK_RUSSIAN "Russisch" -!define PIDGIN_SPELLCHECK_SLOVAK "Slowaaks" -!define PIDGIN_SPELLCHECK_SWEDISH "Zweeds" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Oekrans"
--- a/pidgin/win32/nsis/translations/english.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/english.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -25,13 +25,14 @@ !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SHORTCUTS_SECTION_TITLE "Shortcuts" !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_DESKTOP_SHORTCUT_SECTION_TITLE "Desktop" !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_STARTMENU_SHORTCUT_SECTION_TITLE "Start Menu" +!insertmacro PIDGIN_MACRO_DEFAULT_STRING TRANSLATIONS_SECTION_TITLE "Localizations" !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SECTION_DESCRIPTION "Core Pidgin files and dlls" !insertmacro PIDGIN_MACRO_DEFAULT_STRING GTK_SECTION_DESCRIPTION "A multi-platform GUI toolkit, used by Pidgin" !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SHORTCUTS_SECTION_DESCRIPTION "Shortcuts for starting Pidgin" !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_DESKTOP_SHORTCUT_DESC "Create a shortcut to Pidgin on the Desktop" !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_STARTMENU_SHORTCUT_DESC "Create a Start Menu entry for Pidgin" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING DEBUG_SYMBOLS_SECTION_TITLE "Debug Symbols (for reporting crashes)" +!insertmacro PIDGIN_MACRO_DEFAULT_STRING DEBUG_SYMBOLS_SECTION_TITLE "Debug Symbols (for reporting crashes)" ; GTK+ Directory Page @@ -53,32 +54,9 @@ ; Spellcheck Section Prompts !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SECTION_TITLE "Spellchecking Support" !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ERROR "Error Installing Spellchecking" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_DICT_ERROR "Error Installing Spellchecking Dictionary" !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Support for Spellchecking. (Internet connection required for installation)" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING ASPELL_INSTALL_FAILED "Installation Failed" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_BRETON "Breton" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_CATALAN "Catalan" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_CZECH "Czech" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_WELSH "Welsh" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_DANISH "Danish" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_GERMAN "German" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_GREEK "Greek" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ENGLISH "English" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SPANISH "Spanish" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_FAROESE "Faroese" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_FRENCH "French" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ITALIAN "Italian" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_DUTCH "Dutch" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_NORWEGIAN "Norwegian" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_POLISH "Polish" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_PORTUGUESE "Portuguese" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ROMANIAN "Romanian" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_RUSSIAN "Russian" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SLOVAK "Slovak" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SWEDISH "Swedish" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_UKRAINIAN "Ukrainian" + +!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_DEBUGSYMBOLS_ERROR "Error Installing Debug Symbols" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_DEBUGSYMBOLS_ERROR "Error Installing Debug Symbols" +!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_GTK_DOWNLOAD_ERROR "Error Downloading the GTK+ Runtime" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_GTK_DOWNLOAD_ERROR "Error Downloading the GTK+ Runtime"
--- a/pidgin/win32/nsis/translations/finnish.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/finnish.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -48,29 +48,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Oikolukutuki" !define PIDGIN_SPELLCHECK_ERROR "Virhe asennettaessa oikolukua" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Virhe asennettaessa oikoluvun sanakirjaa" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Tuki oikoluvulle. (Asennukseen tarvitaan Internet-yhteys)" -!define ASPELL_INSTALL_FAILED "Asennus eponnistui" -!define PIDGIN_SPELLCHECK_BRETON "bretoni" -!define PIDGIN_SPELLCHECK_CATALAN "katalaani" -!define PIDGIN_SPELLCHECK_CZECH "tshekki" -!define PIDGIN_SPELLCHECK_WELSH "kymri" -!define PIDGIN_SPELLCHECK_DANISH "tanska" -!define PIDGIN_SPELLCHECK_GERMAN "saksa" -!define PIDGIN_SPELLCHECK_GREEK "kreikka" -!define PIDGIN_SPELLCHECK_ENGLISH "englanti" -!define PIDGIN_SPELLCHECK_ESPERANTO "esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "espanja" -!define PIDGIN_SPELLCHECK_FAROESE "fri" -!define PIDGIN_SPELLCHECK_FRENCH "ranska" -!define PIDGIN_SPELLCHECK_ITALIAN "italia" -!define PIDGIN_SPELLCHECK_DUTCH "hollanti" -!define PIDGIN_SPELLCHECK_NORWEGIAN "norja" -!define PIDGIN_SPELLCHECK_POLISH "puola" -!define PIDGIN_SPELLCHECK_PORTUGUESE "portugali" -!define PIDGIN_SPELLCHECK_ROMANIAN "romania" -!define PIDGIN_SPELLCHECK_RUSSIAN "venj" -!define PIDGIN_SPELLCHECK_SLOVAK "slovakia" -!define PIDGIN_SPELLCHECK_SWEDISH "ruotsi" -!define PIDGIN_SPELLCHECK_UKRAINIAN "ukraina"
--- a/pidgin/win32/nsis/translations/french.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/french.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -52,28 +52,4 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Correction orthographique" !define PIDGIN_SPELLCHECK_ERROR "Erreur l'installation du correcteur orthographique" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Erreur l'installation du dictionnaire pour le correcteur orthographique" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Correction orthogaphique. (Une connexion internet est ncessaire pour son installation)" -!define ASPELL_INSTALL_FAILED "chec de l'installation" -!define PIDGIN_SPELLCHECK_BRETON "Breton" -!define PIDGIN_SPELLCHECK_CATALAN "Catalan" -!define PIDGIN_SPELLCHECK_CZECH "Tchque" -!define PIDGIN_SPELLCHECK_WELSH "Gallois" -!define PIDGIN_SPELLCHECK_DANISH "Danois" -!define PIDGIN_SPELLCHECK_GERMAN "Allemand" -!define PIDGIN_SPELLCHECK_GREEK "Grec" -!define PIDGIN_SPELLCHECK_ENGLISH "Anglais" -!define PIDGIN_SPELLCHECK_ESPERANTO "Espranto" -!define PIDGIN_SPELLCHECK_SPANISH "Espagnol" -!define PIDGIN_SPELLCHECK_FAROESE "Fringien" -!define PIDGIN_SPELLCHECK_FRENCH "Franais" -!define PIDGIN_SPELLCHECK_ITALIAN "Italien" -!define PIDGIN_SPELLCHECK_DUTCH "Hollandais" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norvgien" -!define PIDGIN_SPELLCHECK_POLISH "Polonais" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugais" -!define PIDGIN_SPELLCHECK_ROMANIAN "Roumain" -!define PIDGIN_SPELLCHECK_RUSSIAN "Russe" -!define PIDGIN_SPELLCHECK_SLOVAK "Slovaque" -!define PIDGIN_SPELLCHECK_SWEDISH "Sudois" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrainien"
--- a/pidgin/win32/nsis/translations/german.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/german.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -50,28 +50,4 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Untersttzung fr Rechtschreibkontrolle" !define PIDGIN_SPELLCHECK_ERROR "Fehler bei der Installation der Rechtschreibkontrolle" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Fehler bei der Installation des Wrterbuches fr die Rechtschreibkontrolle" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Untersttzung fr Rechtschreibkontrolle. (Fr die Installation ist eine Internet-Verbindung ntig)" -!define ASPELL_INSTALL_FAILED "Installation gescheitert" -!define PIDGIN_SPELLCHECK_BRETON "Bretonisch" -!define PIDGIN_SPELLCHECK_CATALAN "Katalanisch" -!define PIDGIN_SPELLCHECK_CZECH "Tschechisch" -!define PIDGIN_SPELLCHECK_WELSH "Walisisch" -!define PIDGIN_SPELLCHECK_DANISH "Dnisch" -!define PIDGIN_SPELLCHECK_GERMAN "Deutsch" -!define PIDGIN_SPELLCHECK_GREEK "Griechisch" -!define PIDGIN_SPELLCHECK_ENGLISH "Englisch" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Spanisch" -!define PIDGIN_SPELLCHECK_FAROESE "Farersprache" -!define PIDGIN_SPELLCHECK_FRENCH "Franzsisch" -!define PIDGIN_SPELLCHECK_ITALIAN "Italienisch" -!define PIDGIN_SPELLCHECK_DUTCH "Hollndisch" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norwegisch" -!define PIDGIN_SPELLCHECK_POLISH "Polnisch" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugiesisch" -!define PIDGIN_SPELLCHECK_ROMANIAN "Rumnisch" -!define PIDGIN_SPELLCHECK_RUSSIAN "Russisch" -!define PIDGIN_SPELLCHECK_SLOVAK "Slowakisch" -!define PIDGIN_SPELLCHECK_SWEDISH "Schwedisch" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrainisch"
--- a/pidgin/win32/nsis/translations/hebrew.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/hebrew.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -50,29 +50,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE " " !define PIDGIN_SPELLCHECK_ERROR " " -!define PIDGIN_SPELLCHECK_DICT_ERROR " " !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION " ( )" -!define ASPELL_INSTALL_FAILED " " -!define PIDGIN_SPELLCHECK_BRETON "" -!define PIDGIN_SPELLCHECK_CATALAN "" -!define PIDGIN_SPELLCHECK_CZECH "'" -!define PIDGIN_SPELLCHECK_WELSH "" -!define PIDGIN_SPELLCHECK_DANISH "" -!define PIDGIN_SPELLCHECK_GERMAN "" -!define PIDGIN_SPELLCHECK_GREEK "" -!define PIDGIN_SPELLCHECK_ENGLISH "" -!define PIDGIN_SPELLCHECK_ESPERANTO "" -!define PIDGIN_SPELLCHECK_SPANISH "" -!define PIDGIN_SPELLCHECK_FAROESE "" -!define PIDGIN_SPELLCHECK_FRENCH "" -!define PIDGIN_SPELLCHECK_ITALIAN "" -!define PIDGIN_SPELLCHECK_DUTCH "" -!define PIDGIN_SPELLCHECK_NORWEGIAN "" -!define PIDGIN_SPELLCHECK_POLISH "" -!define PIDGIN_SPELLCHECK_PORTUGUESE "" -!define PIDGIN_SPELLCHECK_ROMANIAN "" -!define PIDGIN_SPELLCHECK_RUSSIAN "" -!define PIDGIN_SPELLCHECK_SLOVAK "" -!define PIDGIN_SPELLCHECK_SWEDISH "" -!define PIDGIN_SPELLCHECK_UKRAINIAN ""
--- a/pidgin/win32/nsis/translations/hungarian.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/hungarian.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -49,29 +49,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Helyesrs-ellenrzs tmogatsa" !define PIDGIN_SPELLCHECK_ERROR "Hiba a helyesrs-ellenrzs teleptse kzben" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Hiba a helyesrs-ellenrzsi sztr teleptse kzben" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Helyesrs-ellenrzs tmogatsa. (Internetkapcsolat szksges a teleptshez)" -!define ASPELL_INSTALL_FAILED "A telepts sikertelen" -!define PIDGIN_SPELLCHECK_BRETON "Breton" -!define PIDGIN_SPELLCHECK_CATALAN "Kataln" -!define PIDGIN_SPELLCHECK_CZECH "Cseh" -!define PIDGIN_SPELLCHECK_WELSH "Walesi" -!define PIDGIN_SPELLCHECK_DANISH "Dn" -!define PIDGIN_SPELLCHECK_GERMAN "Nmet" -!define PIDGIN_SPELLCHECK_GREEK "Grg" -!define PIDGIN_SPELLCHECK_ENGLISH "Angol" -!define PIDGIN_SPELLCHECK_ESPERANTO "Eszperant" -!define PIDGIN_SPELLCHECK_SPANISH "Spanyol" -!define PIDGIN_SPELLCHECK_FAROESE "Farai" -!define PIDGIN_SPELLCHECK_FRENCH "Francia" -!define PIDGIN_SPELLCHECK_ITALIAN "Olasz" -!define PIDGIN_SPELLCHECK_DUTCH "Holland" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norvg" -!define PIDGIN_SPELLCHECK_POLISH "Lengyel" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugl" -!define PIDGIN_SPELLCHECK_ROMANIAN "Romn" -!define PIDGIN_SPELLCHECK_RUSSIAN "Orosz" -!define PIDGIN_SPELLCHECK_SLOVAK "Szlovk" -!define PIDGIN_SPELLCHECK_SWEDISH "Svd" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrn"
--- a/pidgin/win32/nsis/translations/italian.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/italian.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -49,29 +49,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Supporto per il correttore ortografico" !define PIDGIN_SPELLCHECK_ERROR "Errore nell'installazione del correttore ortografico" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Errore nell'installazione del dizionario per il correttore ortografico" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Supporto per il correttore ortografico. ( richiesta una connessione a internet per l'installazione)" -!define ASPELL_INSTALL_FAILED "Installazione fallita" -!define PIDGIN_SPELLCHECK_BRETON "Bretone" -!define PIDGIN_SPELLCHECK_CATALAN "Catalano" -!define PIDGIN_SPELLCHECK_CZECH "Ceco" -!define PIDGIN_SPELLCHECK_WELSH "Gallese" -!define PIDGIN_SPELLCHECK_DANISH "Danese" -!define PIDGIN_SPELLCHECK_GERMAN "Tedesco" -!define PIDGIN_SPELLCHECK_GREEK "Greco" -!define PIDGIN_SPELLCHECK_ENGLISH "Inglese" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Spagnolo" -!define PIDGIN_SPELLCHECK_FAROESE "Faroese" -!define PIDGIN_SPELLCHECK_FRENCH "Francese" -!define PIDGIN_SPELLCHECK_ITALIAN "Italiano" -!define PIDGIN_SPELLCHECK_DUTCH "Olandese" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norvegese" -!define PIDGIN_SPELLCHECK_POLISH "Polacco" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portoghese" -!define PIDGIN_SPELLCHECK_ROMANIAN "Rumeno" -!define PIDGIN_SPELLCHECK_RUSSIAN "Russo" -!define PIDGIN_SPELLCHECK_SLOVAK "Slovacco" -!define PIDGIN_SPELLCHECK_SWEDISH "Svedese" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ucraino"
--- a/pidgin/win32/nsis/translations/japanese.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/japanese.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -45,29 +45,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Xy`FbÑT|[g" !define PIDGIN_SPELLCHECK_ERROR "Xy`FbÑCXg[Ɏs܂" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Xy`FbÑCXg[Ɏs܂B" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Xy`FbÑT|[g (C^[lbgڑCXg[ɕKvł)" -!define ASPELL_INSTALL_FAILED "CXg[Ɏs܂" -!define PIDGIN_SPELLCHECK_BRETON "u^[j" -!define PIDGIN_SPELLCHECK_CATALAN "J^[j" -!define PIDGIN_SPELLCHECK_CZECH "`FR" -!define PIDGIN_SPELLCHECK_WELSH "EF[Y" -!define PIDGIN_SPELLCHECK_DANISH "f}[N" -!define PIDGIN_SPELLCHECK_GERMAN "hCc" -!define PIDGIN_SPELLCHECK_GREEK "MV" -!define PIDGIN_SPELLCHECK_ENGLISH "p" -!define PIDGIN_SPELLCHECK_ESPERANTO "GXyg" -!define PIDGIN_SPELLCHECK_SPANISH "XyC" -!define PIDGIN_SPELLCHECK_FAROESE "tF[" -!define PIDGIN_SPELLCHECK_FRENCH "tX" -!define PIDGIN_SPELLCHECK_ITALIAN "C^A" -!define PIDGIN_SPELLCHECK_DUTCH "I_" -!define PIDGIN_SPELLCHECK_NORWEGIAN "mEF[" -!define PIDGIN_SPELLCHECK_POLISH "|[h" -!define PIDGIN_SPELLCHECK_PORTUGUESE "|gK" -!define PIDGIN_SPELLCHECK_ROMANIAN "[}jA" -!define PIDGIN_SPELLCHECK_RUSSIAN "VA" -!define PIDGIN_SPELLCHECK_SLOVAK "X@LA" -!define PIDGIN_SPELLCHECK_SWEDISH "XEF[f" -!define PIDGIN_SPELLCHECK_UKRAINIAN "ENCi"
--- a/pidgin/win32/nsis/translations/kurdish.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/kurdish.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -44,29 +44,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Desteka kontrola rastnivs" !define PIDGIN_SPELLCHECK_ERROR "Di sazkirina kontrola rastnivs de ewt derket." -!define PIDGIN_SPELLCHECK_DICT_ERROR "Di sazkirina ferhenga rastnivs de ewt derket." !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Desteka kontrola rastnivs. (Ji bo sazkirin nternet pwst e)" -!define ASPELL_INSTALL_FAILED "Sazkirin Serneket" -!define PIDGIN_SPELLCHECK_BRETON "Breton" -!define PIDGIN_SPELLCHECK_CATALAN "Catalan" -!define PIDGIN_SPELLCHECK_CZECH "ek" -!define PIDGIN_SPELLCHECK_WELSH "Welsh" -!define PIDGIN_SPELLCHECK_DANISH "Danik" -!define PIDGIN_SPELLCHECK_GERMAN "Alman" -!define PIDGIN_SPELLCHECK_GREEK "Yewnan" -!define PIDGIN_SPELLCHECK_ENGLISH "ngilz" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Span" -!define PIDGIN_SPELLCHECK_FAROESE "Faroese" -!define PIDGIN_SPELLCHECK_FRENCH "Frans" -!define PIDGIN_SPELLCHECK_ITALIAN "tal" -!define PIDGIN_SPELLCHECK_DUTCH "Dutch" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norwec" -!define PIDGIN_SPELLCHECK_POLISH "Pol" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portekiz" -!define PIDGIN_SPELLCHECK_ROMANIAN "Roman" -!define PIDGIN_SPELLCHECK_RUSSIAN "Rus" -!define PIDGIN_SPELLCHECK_SLOVAK "Slovak" -!define PIDGIN_SPELLCHECK_SWEDISH "Swd" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrayn"
--- a/pidgin/win32/nsis/translations/lithuanian.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/lithuanian.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -47,29 +47,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Raybos tikrinimo palaikymas" !define PIDGIN_SPELLCHECK_ERROR "Raybos tikrinimo palaikymo diegimo klaida" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Raybos tikrinimo odyno diegimo klaida" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Raybos tikrinimo palaikymas. (Diegimui btina interneto jungtis)" -!define ASPELL_INSTALL_FAILED "Diegimas nepavyko" -!define PIDGIN_SPELLCHECK_BRETON "Breton kalba" -!define PIDGIN_SPELLCHECK_CATALAN "Katalon kalba" -!define PIDGIN_SPELLCHECK_CZECH "ek kalba" -!define PIDGIN_SPELLCHECK_WELSH "Val kalba" -!define PIDGIN_SPELLCHECK_DANISH "Dan kalba" -!define PIDGIN_SPELLCHECK_GERMAN "Vokiei kalba" -!define PIDGIN_SPELLCHECK_GREEK "Graik kalba" -!define PIDGIN_SPELLCHECK_ENGLISH "Angl kalba" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto kalba" -!define PIDGIN_SPELLCHECK_SPANISH "Ispan kalba" -!define PIDGIN_SPELLCHECK_FAROESE "Farer kalba" -!define PIDGIN_SPELLCHECK_FRENCH "Prancz kalba" -!define PIDGIN_SPELLCHECK_ITALIAN "Ital kalba" -!define PIDGIN_SPELLCHECK_DUTCH "Oland kalba" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norveg kalba" -!define PIDGIN_SPELLCHECK_POLISH "Lenk kalba" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugal kalba" -!define PIDGIN_SPELLCHECK_ROMANIAN "Rumun kalba" -!define PIDGIN_SPELLCHECK_RUSSIAN "Rus kalba" -!define PIDGIN_SPELLCHECK_SLOVAK "Slovak kalba" -!define PIDGIN_SPELLCHECK_SWEDISH "ved kalba" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrainiei kalba"
--- a/pidgin/win32/nsis/translations/norwegian.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/norwegian.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -45,28 +45,4 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Sttte for stavekontroll" !define PIDGIN_SPELLCHECK_ERROR "Det oppstod en feil ved installering av stavekontroll" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Det oppstod en feil ved installering av ordboken for stavekontroll" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Sttte for stavekontroll. (Internettoppkobling pkrevd for installasjon)" -!define ASPELL_INSTALL_FAILED "Installasjonen mislyktes." -!define PIDGIN_SPELLCHECK_BRETON "Bretagnsk" -!define PIDGIN_SPELLCHECK_CATALAN "Katalansk" -!define PIDGIN_SPELLCHECK_CZECH "Tsjekkisk" -!define PIDGIN_SPELLCHECK_WELSH "Walisisk" -!define PIDGIN_SPELLCHECK_DANISH "Dansk" -!define PIDGIN_SPELLCHECK_GERMAN "Tysk" -!define PIDGIN_SPELLCHECK_GREEK "Gresk" -!define PIDGIN_SPELLCHECK_ENGLISH "Engelsk" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Spansk" -!define PIDGIN_SPELLCHECK_FAROESE "Frysk" -!define PIDGIN_SPELLCHECK_FRENCH "Fransk" -!define PIDGIN_SPELLCHECK_ITALIAN "Italiensk" -!define PIDGIN_SPELLCHECK_DUTCH "Nederlandsk" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norsk" -!define PIDGIN_SPELLCHECK_POLISH "Polsk" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugisisk" -!define PIDGIN_SPELLCHECK_ROMANIAN "Rumensk" -!define PIDGIN_SPELLCHECK_RUSSIAN "Russisk" -!define PIDGIN_SPELLCHECK_SLOVAK "Slovakisk" -!define PIDGIN_SPELLCHECK_SWEDISH "Svensk" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrainsk"
--- a/pidgin/win32/nsis/translations/norwegian_nynorsk.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/norwegian_nynorsk.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -9,7 +9,6 @@ ; Startup Checks !define INSTALLER_IS_RUNNING "Installasjonsprogrammet kjrer allereie." !define PIDGIN_IS_RUNNING "Pidgin kjrer no. Lukk programmet og prv igjen." -!define GTK_INSTALLER_NEEDED "GTK+-kjremiljet manglar eller treng bli oppdatert.$\rInstaller v${GTK_MIN_VERSION} eller nyare av GTK+-kjremiljet" ; License Page !define PIDGIN_LICENSE_BUTTON "Neste >" @@ -28,20 +27,12 @@ !define PIDGIN_DESKTOP_SHORTCUT_DESC "Lag ein snarveg til Pidgin p skrivebordet" !define PIDGIN_STARTMENU_SHORTCUT_DESC "Lag ein snarveg til Pidgin p startmenyen" -; GTK+ Directory Page -!define GTK_UPGRADE_PROMPT "Fann ei gammal utgve av GTK+-kjremiljet. Vil du oppdatera ho?$\rMerk: $(^Name) vil kanskje ikkje fungera om du ikkje oppdaterer." -!define GTK_WINDOWS_INCOMPATIBLE "Windows 95/98/Me er ikkje kompatibelt med GTK+ 2.8.0 eller nyare. GTK+ ${GTK_INSTALL_VERSION} kjem ikkje til bli installert.$\rInstallasjonen vil bli abvroten om ikkje GTK+ ${GTK_MIN_VERSION} eller nyare allereie er installert." - ; Installer Finish Page !define PIDGIN_FINISH_VISIT_WEB_SITE "Besk Pidgin si nettside" ; Pidgin Section Prompts and Texts !define PIDGIN_PROMPT_CONTINUE_WITHOUT_UNINSTALL "Klarte ikkje avinstallera Pidgin-utgva som er i bruk. Den nye utgva kjem til bli installert utan ta vekk den gjeldande." -; GTK+ Section Prompts -!define GTK_INSTALL_ERROR "Klarte ikkje installera GTK+-kjremiljet." -!define GTK_BAD_INSTALL_PATH "Klarer ikkje laga eller f tilgang til bana du skreiv." - ; URL Handler section !define URI_HANDLERS_SECTION_TITLE "URI-referanse"
--- a/pidgin/win32/nsis/translations/persian.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/persian.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -51,29 +51,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE " ؝ " !define PIDGIN_SPELLCHECK_ERROR " ؝ " -!define PIDGIN_SPELLCHECK_DICT_ERROR " ʝ ؝ " !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION " ؝ . ( )" -!define ASPELL_INSTALL_FAILED " Ԙ " -!define PIDGIN_SPELLCHECK_BRETON "" -!define PIDGIN_SPELLCHECK_CATALAN "" -!define PIDGIN_SPELLCHECK_CZECH "" -!define PIDGIN_SPELLCHECK_WELSH "" -!define PIDGIN_SPELLCHECK_DANISH "ј" -!define PIDGIN_SPELLCHECK_GERMAN "" -!define PIDGIN_SPELLCHECK_GREEK "" -!define PIDGIN_SPELLCHECK_ENGLISH "" -!define PIDGIN_SPELLCHECK_ESPERANTO "Ӂ" -!define PIDGIN_SPELLCHECK_SPANISH "Ӂ" -!define PIDGIN_SPELLCHECK_FAROESE "" -!define PIDGIN_SPELLCHECK_FRENCH "" -!define PIDGIN_SPELLCHECK_ITALIAN "" -!define PIDGIN_SPELLCHECK_DUTCH "" -!define PIDGIN_SPELLCHECK_NORWEGIAN "" -!define PIDGIN_SPELLCHECK_POLISH "" -!define PIDGIN_SPELLCHECK_PORTUGUESE "" -!define PIDGIN_SPELLCHECK_ROMANIAN "" -!define PIDGIN_SPELLCHECK_RUSSIAN "" -!define PIDGIN_SPELLCHECK_SLOVAK "ǘ" -!define PIDGIN_SPELLCHECK_SWEDISH "" -!define PIDGIN_SPELLCHECK_UKRAINIAN ""
--- a/pidgin/win32/nsis/translations/polish.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/polish.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -25,17 +25,9 @@ !define PIDGIN_DESKTOP_SHORTCUT_DESC "Utworzenie skrtu do programu Pidgin na pulpicie" !define PIDGIN_STARTMENU_SHORTCUT_DESC "Utworzenie wpisu w menu Start dla programu Pidgin" -; GTK+ Directory Page -!define GTK_UPGRADE_PROMPT "Odnaleziono star wersj biblioteki GTK+. Zaktualizowa j?$\rUwaga: program $(^Name) moe bez tego nie dziaa." -!define GTK_WINDOWS_INCOMPATIBLE "Systemy Windows 95/98/Me s niezgodne z bibliotek GTK+ 2.8.0 lub nowsz. Biblioteka GTK+ ${GTK_INSTALL_VERSION} nie zostanie zainstalowana.$\rJeli brak zainstalowanej biblioteki GTK+ ${GTK_MIN_VERSION} lub nowszej, instalacja zostanie przerwana." - ; Installer Finish Page !define PIDGIN_FINISH_VISIT_WEB_SITE "Odwied stron WWW programu Pidgin" -; GTK+ Section Prompts -!define GTK_INSTALL_ERROR "Bd podczas instalowania biblioteki GTK+." -!define GTK_BAD_INSTALL_PATH "Nie mona uzyska dostpu do podanej cieki lub jej utworzy." - ; Uninstall Section Prompts !define un.PIDGIN_UNINSTALL_ERROR_1 "Instalator nie moe odnale wpisw w rejestrze dla programu Pidgin.$\rMoliwe, e inny uytkownik zainstalowa ten program." -!define un.PIDGIN_UNINSTALL_ERROR_2 "Brak uprawnie do odinstalowania tego programu." \ No newline at end of file +!define un.PIDGIN_UNINSTALL_ERROR_2 "Brak uprawnie do odinstalowania tego programu."
--- a/pidgin/win32/nsis/translations/portuguese-br.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/portuguese-br.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -43,28 +43,4 @@ !define URI_HANDLERS_SECTION_TITLE "Handlers para endereos" !define PIDGIN_SPELLCHECK_SECTION_TITLE "Suporte a verificao ortogrfica" !define PIDGIN_SPELLCHECK_ERROR "Erro ao instalar a verificao ortogrfica" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Erro ao instalar o dicionrio da verificao ortogrfica" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Suporte a verificao ortogrfica (A instalao necessita de conexo a internet)" -!define ASPELL_INSTALL_FAILED "Falha na instalao" -!define PIDGIN_SPELLCHECK_BRETON "Breto" -!define PIDGIN_SPELLCHECK_CATALAN "Catalo" -!define PIDGIN_SPELLCHECK_CZECH "Tcheco" -!define PIDGIN_SPELLCHECK_WELSH "Gals" -!define PIDGIN_SPELLCHECK_DANISH "Dinamarqus" -!define PIDGIN_SPELLCHECK_GERMAN "Alemo" -!define PIDGIN_SPELLCHECK_GREEK "Grego" -!define PIDGIN_SPELLCHECK_ENGLISH "Ingls" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Espanhol" -!define PIDGIN_SPELLCHECK_FAROESE "Feros" -!define PIDGIN_SPELLCHECK_FRENCH "Francs" -!define PIDGIN_SPELLCHECK_ITALIAN "Italiano" -!define PIDGIN_SPELLCHECK_DUTCH "Holands" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Noruegus" -!define PIDGIN_SPELLCHECK_POLISH "Polons" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugus" -!define PIDGIN_SPELLCHECK_ROMANIAN "Romeno" -!define PIDGIN_SPELLCHECK_RUSSIAN "Russo" -!define PIDGIN_SPELLCHECK_SLOVAK "Eslovaco" -!define PIDGIN_SPELLCHECK_SWEDISH "Sueco" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ucraniano"
--- a/pidgin/win32/nsis/translations/simp-chinese.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/simp-chinese.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -43,28 +43,4 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "ƴд֧" !define PIDGIN_SPELLCHECK_ERROR "װƴд" -!define PIDGIN_SPELLCHECK_DICT_ERROR "װƴдֵ" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "ƴд֧֡(װҪӵ Internet)" -!define ASPELL_INSTALL_FAILED "װʧ" -!define PIDGIN_SPELLCHECK_BRETON "" -!define PIDGIN_SPELLCHECK_CATALAN "̩" -!define PIDGIN_SPELLCHECK_CZECH "ݿ" -!define PIDGIN_SPELLCHECK_WELSH "ʿ" -!define PIDGIN_SPELLCHECK_DANISH "" -!define PIDGIN_SPELLCHECK_GERMAN "" -!define PIDGIN_SPELLCHECK_GREEK "ϣ" -!define PIDGIN_SPELLCHECK_ENGLISH "Ӣ" -!define PIDGIN_SPELLCHECK_ESPERANTO "" -!define PIDGIN_SPELLCHECK_SPANISH "" -!define PIDGIN_SPELLCHECK_FAROESE "" -!define PIDGIN_SPELLCHECK_FRENCH "" -!define PIDGIN_SPELLCHECK_ITALIAN "" -!define PIDGIN_SPELLCHECK_DUTCH "" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Ų" -!define PIDGIN_SPELLCHECK_POLISH "" -!define PIDGIN_SPELLCHECK_PORTUGUESE "" -!define PIDGIN_SPELLCHECK_ROMANIAN "" -!define PIDGIN_SPELLCHECK_RUSSIAN "" -!define PIDGIN_SPELLCHECK_SLOVAK "˹工" -!define PIDGIN_SPELLCHECK_SWEDISH "" -!define PIDGIN_SPELLCHECK_UKRAINIAN "ڿ"
--- a/pidgin/win32/nsis/translations/slovak.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/slovak.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -43,29 +43,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Podpora kontroly pravopisu" !define PIDGIN_SPELLCHECK_ERROR "Chyba pri intalcii kontroly pravopisu" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Chyba pri intalcii slovnka kontroly pravopisu" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Podpora kontroly pravopisu (Nutn pripojenie k Internetu)" -!define ASPELL_INSTALL_FAILED "Intalcia zlyhala" -!define PIDGIN_SPELLCHECK_BRETON "Bretnsky" -!define PIDGIN_SPELLCHECK_CATALAN "Katalnsky" -!define PIDGIN_SPELLCHECK_CZECH "esk" -!define PIDGIN_SPELLCHECK_WELSH "Welshsk" -!define PIDGIN_SPELLCHECK_DANISH "Dnsky" -!define PIDGIN_SPELLCHECK_GERMAN "Nemeck" -!define PIDGIN_SPELLCHECK_GREEK "Grcky" -!define PIDGIN_SPELLCHECK_ENGLISH "Anglick" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperantsk" -!define PIDGIN_SPELLCHECK_SPANISH "panielsk" -!define PIDGIN_SPELLCHECK_FAROESE "Faroesk" -!define PIDGIN_SPELLCHECK_FRENCH "Franczsky" -!define PIDGIN_SPELLCHECK_ITALIAN "Taliansk" -!define PIDGIN_SPELLCHECK_DUTCH "Holandsk" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Nrsky" -!define PIDGIN_SPELLCHECK_POLISH "Posk" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugalsk" -!define PIDGIN_SPELLCHECK_ROMANIAN "Rumunsk" -!define PIDGIN_SPELLCHECK_RUSSIAN "Rusk" -!define PIDGIN_SPELLCHECK_SLOVAK "Slovensk" -!define PIDGIN_SPELLCHECK_SWEDISH "vdsky" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrajinsk"
--- a/pidgin/win32/nsis/translations/slovenian.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/slovenian.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -49,29 +49,4 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Podpora preverjanja rkovanja" !define PIDGIN_SPELLCHECK_ERROR "Napaka pri nameanju preverjanja rkovanja" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Napaka pri nameanju slovarja za preverjanje rkovanja" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Podpora preverjanja rkovanja. (Za namestitev je potrebna spletna povezava)" -!define ASPELL_INSTALL_FAILED "Namestitev ni uspela." -!define PIDGIN_SPELLCHECK_BRETON "bretonski" -!define PIDGIN_SPELLCHECK_CATALAN "katalonski" -!define PIDGIN_SPELLCHECK_CZECH "eki" -!define PIDGIN_SPELLCHECK_WELSH "velki" -!define PIDGIN_SPELLCHECK_DANISH "danski" -!define PIDGIN_SPELLCHECK_GERMAN "nemki" -!define PIDGIN_SPELLCHECK_GREEK "grki" -!define PIDGIN_SPELLCHECK_ENGLISH "angleki" -!define PIDGIN_SPELLCHECK_ESPERANTO "esperantski" -!define PIDGIN_SPELLCHECK_SPANISH "panski" -!define PIDGIN_SPELLCHECK_FAROESE "farojski" -!define PIDGIN_SPELLCHECK_FRENCH "francoski" -!define PIDGIN_SPELLCHECK_ITALIAN "italijanski" -!define PIDGIN_SPELLCHECK_DUTCH "nizozemski" -!define PIDGIN_SPELLCHECK_NORWEGIAN "norveki" -!define PIDGIN_SPELLCHECK_POLISH "poljski" -!define PIDGIN_SPELLCHECK_PORTUGUESE "portugalski" -!define PIDGIN_SPELLCHECK_ROMANIAN "romunski" -!define PIDGIN_SPELLCHECK_RUSSIAN "ruski" -!define PIDGIN_SPELLCHECK_SLOVAK "slovaki" -!define PIDGIN_SPELLCHECK_SLOVENIAN "slovenski" -!define PIDGIN_SPELLCHECK_SWEDISH "vedski" -!define PIDGIN_SPELLCHECK_UKRAINIAN "ukrajinski"
--- a/pidgin/win32/nsis/translations/swedish.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/swedish.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -49,28 +49,4 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Std fr rttstavning" !define PIDGIN_SPELLCHECK_ERROR "Fel vid installation fr rttstavning" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Fel vid installation av rttstavningsordlista" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Std fr Rttstavning. (Internetanslutning krvs fr installation)" -!define ASPELL_INSTALL_FAILED "Installationen misslyckades" -!define PIDGIN_SPELLCHECK_BRETON "Bretonska" -!define PIDGIN_SPELLCHECK_CATALAN "Katalanska" -!define PIDGIN_SPELLCHECK_CZECH "Tjeckiska" -!define PIDGIN_SPELLCHECK_WELSH "Kymriska" -!define PIDGIN_SPELLCHECK_DANISH "Danska" -!define PIDGIN_SPELLCHECK_GERMAN "Tyska" -!define PIDGIN_SPELLCHECK_GREEK "Grekiska" -!define PIDGIN_SPELLCHECK_ENGLISH "Engelska" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Spanska" -!define PIDGIN_SPELLCHECK_FAROESE "Friska" -!define PIDGIN_SPELLCHECK_FRENCH "Franska" -!define PIDGIN_SPELLCHECK_ITALIAN "Italienska" -!define PIDGIN_SPELLCHECK_DUTCH "Nederlndska" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Norska" -!define PIDGIN_SPELLCHECK_POLISH "Polska" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugisiska" -!define PIDGIN_SPELLCHECK_ROMANIAN "Rumnska" -!define PIDGIN_SPELLCHECK_RUSSIAN "Ryska" -!define PIDGIN_SPELLCHECK_SLOVAK "Slovakiska" -!define PIDGIN_SPELLCHECK_SWEDISH "Svenska" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ukrainska"
--- a/pidgin/win32/nsis/translations/trad-chinese.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/trad-chinese.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -51,29 +51,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "rˬd\" !define PIDGIN_SPELLCHECK_ERROR "w˫rˬd~oͿ~" -!define PIDGIN_SPELLCHECK_DICT_ERROR "w˫rˬdΪ~oͿ~" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "rˬd䴩]w˶ںsu^C" -!define ASPELL_INSTALL_FAILED "w˥" -!define PIDGIN_SPELLCHECK_BRETON "h" -!define PIDGIN_SPELLCHECK_CATALAN "[" -!define PIDGIN_SPELLCHECK_CZECH "J" -!define PIDGIN_SPELLCHECK_WELSH "º" -!define PIDGIN_SPELLCHECK_DANISH "" -!define PIDGIN_SPELLCHECK_GERMAN "w" -!define PIDGIN_SPELLCHECK_GREEK "þ" -!define PIDGIN_SPELLCHECK_ENGLISH "^" -!define PIDGIN_SPELLCHECK_ESPERANTO "@ɻy" -!define PIDGIN_SPELLCHECK_SPANISH "Z" -!define PIDGIN_SPELLCHECK_FAROESE "kùsq" -!define PIDGIN_SPELLCHECK_FRENCH "k" -!define PIDGIN_SPELLCHECK_ITALIAN "NjQ" -!define PIDGIN_SPELLCHECK_DUTCH "" -!define PIDGIN_SPELLCHECK_NORWEGIAN "¤" -!define PIDGIN_SPELLCHECK_POLISH "i" -!define PIDGIN_SPELLCHECK_PORTUGUESE "" -!define PIDGIN_SPELLCHECK_ROMANIAN "ùȤ" -!define PIDGIN_SPELLCHECK_RUSSIAN "X" -!define PIDGIN_SPELLCHECK_SLOVAK "J" -!define PIDGIN_SPELLCHECK_SWEDISH "" -!define PIDGIN_SPELLCHECK_UKRAINIAN "QJ"
--- a/pidgin/win32/nsis/translations/valencian.nsh Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/nsis/translations/valencian.nsh Tue Mar 09 21:41:41 2010 +0000 @@ -46,29 +46,5 @@ ; Spellcheck Section Prompts !define PIDGIN_SPELLCHECK_SECTION_TITLE "Soport de Correccio Ortografica" !define PIDGIN_SPELLCHECK_ERROR "Erro Instalant Correccio Ortografica" -!define PIDGIN_SPELLCHECK_DICT_ERROR "Erro Instalant Diccionari de Correccio Ortografica" !define PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Soport per a Correccio Ortografica. (es requerix conexio a Internet per a fer l'instalacio)" -!define ASPELL_INSTALL_FAILED "L'Instalacio fall" -!define PIDGIN_SPELLCHECK_BRETON "Breto" -!define PIDGIN_SPELLCHECK_CATALAN "Catal" -!define PIDGIN_SPELLCHECK_CZECH "Chec" -!define PIDGIN_SPELLCHECK_WELSH "Gals" -!define PIDGIN_SPELLCHECK_DANISH "Danes" -!define PIDGIN_SPELLCHECK_GERMAN "Alem" -!define PIDGIN_SPELLCHECK_GREEK "Grec" -!define PIDGIN_SPELLCHECK_ENGLISH "Angles" -!define PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!define PIDGIN_SPELLCHECK_SPANISH "Espanyol" -!define PIDGIN_SPELLCHECK_FAROESE "Feroes" -!define PIDGIN_SPELLCHECK_FRENCH "Frances" -!define PIDGIN_SPELLCHECK_ITALIAN "Itali" -!define PIDGIN_SPELLCHECK_DUTCH "Holandes" -!define PIDGIN_SPELLCHECK_NORWEGIAN "Noruec" -!define PIDGIN_SPELLCHECK_POLISH "Polac" -!define PIDGIN_SPELLCHECK_PORTUGUESE "Portugues" -!define PIDGIN_SPELLCHECK_ROMANIAN "Romanes" -!define PIDGIN_SPELLCHECK_RUSSIAN "Rus" -!define PIDGIN_SPELLCHECK_SLOVAK "Eslovac" -!define PIDGIN_SPELLCHECK_SWEDISH "Suec" -!define PIDGIN_SPELLCHECK_UKRAINIAN "Ucrani"
--- a/pidgin/win32/winpidgin.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/winpidgin.c Tue Mar 09 21:41:41 2010 +0000 @@ -97,7 +97,7 @@ return ret; } -static void common_dll_prep(const TCHAR *path) { +static BOOL common_dll_prep(const TCHAR *path) { HMODULE hmod; HKEY hkey; struct _stat stat_buf; @@ -110,7 +110,7 @@ if (_tstat(test_path, &stat_buf) != 0) { printf("Unable to determine GTK+ path. \n" "Assuming GTK+ is in the PATH.\n"); - return; + return FALSE; } @@ -184,6 +184,20 @@ printf("SafeDllSearchMode is set to 0\n"); }/*end else*/ } + + return TRUE; +} + +static BOOL dll_prep(const TCHAR *pidgin_dir) { + TCHAR path[MAX_PATH + 1]; + path[0] = _T('\0'); + + if (*pidgin_dir) { + _sntprintf(path, sizeof(path) / sizeof(TCHAR), _T("%s\\Gtk\\bin"), pidgin_dir); + path[sizeof(path) / sizeof(TCHAR)] = _T('\0'); + } + + return common_dll_prep(path); } static void portable_mode_dll_prep(const TCHAR *pidgin_dir) { @@ -224,22 +238,11 @@ _tprintf(_T("%s\n"), path2); _tputenv(path2); - /* set the GTK+ path to be \\path\to\GTK\bin */ - _tcscat(path, _T("\\GTK\\bin")); - - common_dll_prep(path); -} - -static void dll_prep(const TCHAR *pidgin_dir) { - TCHAR gtk_path[MAX_PATH + 1]; - gtk_path[0] = _T('\0'); - - if (*pidgin_dir) { - _sntprintf(gtk_path, sizeof(gtk_path) / sizeof(TCHAR), _T("%s\\Gtk\\bin"), pidgin_dir); - gtk_path[sizeof(gtk_path) / sizeof(TCHAR)] = _T('\0'); + if (!dll_prep(pidgin_dir)) { + /* set the GTK+ path to be \\path\to\GTK\bin */ + _tcscat(path, _T("\\GTK\\bin")); + common_dll_prep(path); } - - common_dll_prep(gtk_path); } static TCHAR* winpidgin_lcid_to_posix(LCID lcid) {
--- a/pidgin/win32/wspell.c Thu Feb 18 22:42:19 2010 +0000 +++ b/pidgin/win32/wspell.c Tue Mar 09 21:41:41 2010 +0000 @@ -3,7 +3,7 @@ * * File: wspell.c * Date: March, 2003 - * Description: Windows Purple gtkspell interface. + * Description: Windows Pidgin gtkspell interface. * * Copyright (C) 2002-2003, Herman Bloggs <hermanator12002@yahoo.com> * @@ -32,8 +32,22 @@ #include "win32dep.h" #include "wspell.h" +/* Intermediate function so that we can eat Enchant error popups when it doesn't find a DLL + * This is fixed upstream, but not released */ +GtkSpell* (*wpidginspell_new_attach_proxy) (GtkTextView *, + const gchar *, + GError **) = NULL; + /* GTKSPELL DUMMY FUNCS */ -static GtkSpell* wgtkspell_new_attach(GtkTextView *view, const gchar *lang, GError **error) {return NULL;} +static GtkSpell* wgtkspell_new_attach(GtkTextView *view, const gchar *lang, GError **error) { + GtkSpell *ret = NULL; + if (wpidginspell_new_attach_proxy) { + UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS); + ret = wpidginspell_new_attach_proxy(view, lang, error); + SetErrorMode(old_error_mode); + } + return ret; +} static GtkSpell* wgtkspell_get_from_text_view(GtkTextView *view) {return NULL;} static void wgtkspell_detach(GtkSpell *spell) {} static gboolean wgtkspell_set_language(GtkSpell *spell, const gchar *lang, GError **error) {return FALSE;} @@ -54,28 +68,44 @@ void (*wpidginspell_recheck_all) (GtkSpell*) = wgtkspell_recheck_all; +#define GTKSPELL_DLL "libgtkspell-0.dll" + static void load_gtkspell() { - wpidginspell_new_attach = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_new_attach" ); - wpidginspell_get_from_text_view = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_get_from_text_view"); - wpidginspell_detach = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_detach"); - wpidginspell_set_language = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_set_language"); - wpidginspell_recheck_all = (void*) wpurple_find_and_loadproc("libgtkspell.dll", "gtkspell_recheck_all"); + UINT old_error_mode = SetErrorMode(SEM_FAILCRITICALERRORS); + gchar *tmp, *tmp2; + const char *path = g_getenv("PATH"); + tmp = g_build_filename(wpurple_install_dir(), "spellcheck", NULL); + tmp2 = g_strdup_printf("%s%s%s", (path ? path : ""), + (path ? G_SEARCHPATH_SEPARATOR_S : ""), + tmp); + g_free(tmp); + g_setenv("PATH", tmp2, TRUE); + + /* Suppress error popups */ + wpidginspell_new_attach_proxy = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_new_attach" ); + if (wpidginspell_new_attach_proxy) { + wpidginspell_get_from_text_view = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_get_from_text_view"); + wpidginspell_detach = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_detach"); + wpidginspell_set_language = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_set_language"); + wpidginspell_recheck_all = (void*) wpurple_find_and_loadproc(GTKSPELL_DLL, "gtkspell_recheck_all"); + } else { + purple_debug_warning("wspell", "Couldn't load gtkspell (%s) \n", GTKSPELL_DLL); + /*wpidginspell_new_attach = wgtkspell_new_attach;*/ + } + SetErrorMode(old_error_mode); } -static char* lookup_aspell_path() { +static void lookup_aspell_path() { const char *tmp; + gchar *aspell_path; if ((tmp = g_getenv("PIDGIN_ASPELL_DIR"))) - return g_strdup(tmp); - - return wpurple_read_reg_string(HKEY_LOCAL_MACHINE, "Software\\Aspell", "Path"); -} - -void winpidgin_spell_init() { - char *aspell_path = lookup_aspell_path(); + aspell_path = g_strdup(tmp); + else + aspell_path = wpurple_read_reg_string(HKEY_LOCAL_MACHINE, "Software\\Aspell", "Path"); if (aspell_path != NULL) { - char *tmp = g_strconcat(aspell_path, "\\aspell-15.dll", NULL); + char *tmp = g_build_filename(aspell_path, "aspell-15.dll", NULL); if (g_file_test(tmp, G_FILE_TEST_EXISTS)) { const char *path = g_getenv("PATH"); purple_debug_info("wspell", "Found Aspell in %s\n", aspell_path); @@ -88,14 +118,18 @@ g_setenv("PATH", tmp, TRUE); - load_gtkspell(); - } else { + } else purple_debug_warning("wspell", "Couldn't find aspell-15.dll\n"); - } g_free(tmp); g_free(aspell_path); - } else { - purple_debug_warning("wspell", "Couldn't find path for Aspell\n"); - } + } else + purple_debug_warning("wspell", "Aspell installation not found (this isn't necessarily a problem)\n"); } + +void winpidgin_spell_init() { + /* We keep doing the aspell path thing so that previously installed dictionaries still work */ + lookup_aspell_path(); + + load_gtkspell(); +}
--- a/po/POTFILES.in Thu Feb 18 22:42:19 2010 +0000 +++ b/po/POTFILES.in Tue Mar 09 21:41:41 2010 +0000 @@ -145,6 +145,7 @@ libpurple/protocols/novell/novell.c libpurple/protocols/oscar/clientlogin.c libpurple/protocols/oscar/family_chatnav.c +libpurple/protocols/oscar/family_locate.c libpurple/protocols/oscar/flap_connection.c libpurple/protocols/oscar/libaim.c libpurple/protocols/oscar/libicq.c @@ -213,14 +214,12 @@ pidgin/gtkaccount.c pidgin/gtkblist-theme.c pidgin/gtkblist.c -pidgin/gtkcellview.c pidgin/gtkcertmgr.c pidgin/gtkconn.c pidgin/gtkconv.c pidgin/gtkdebug.c pidgin/gtkdialogs.c pidgin/gtkdocklet.c -pidgin/gtkexpander.c pidgin/gtkft.c pidgin/gtkimhtml.c pidgin/gtkimhtmltoolbar.c @@ -241,7 +240,6 @@ pidgin/gtkutils.c pidgin/gtkwhiteboard.c pidgin/pidgin.h -pidgin/pidgincombobox.c pidgin/pidginstock.c pidgin/pidgintooltip.c pidgin/pixmaps/emotes/default/24/default.theme.in