# HG changeset patch # User Marcus Lundblad # Date 1268170915 0 # Node ID 10bf97a36be01587b39a4b3afaec6814b634f40d # Parent fd354d0bfbc06a1951f8a98894737720cdc51ce1# Parent d93676b59db13b185d52006b49f776d3eca036eb propagate from branch 'im.pidgin.pidgin' (head 96b27a20c7250b87761a8bd4f5540bf1fa32b6f4) to branch 'im.pidgin.cpw.malu.ft_thumbnails' (head 230fd6c21fdf1ed459a653bcd9808880a36204c3) diff -r fd354d0bfbc0 -r 10bf97a36be0 .mtn-ignore --- a/.mtn-ignore Sun Feb 28 22:56:50 2010 +0000 +++ b/.mtn-ignore Tue Mar 09 21:41:55 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 diff -r fd354d0bfbc0 -r 10bf97a36be0 COPYRIGHT --- a/COPYRIGHT Sun Feb 28 22:56:50 2010 +0000 +++ b/COPYRIGHT Tue Mar 09 21:41:55 2010 +0000 @@ -303,6 +303,7 @@ Peter McCurdy Kurt McKee Torrey McMahon +Greg McNew Robert McQueen Mihály Mészáros Robert Mibus diff -r fd354d0bfbc0 -r 10bf97a36be0 ChangeLog --- a/ChangeLog Sun Feb 28 22:56:50 2010 +0000 +++ b/ChangeLog Tue Mar 09 21:41:55 2010 +0000 @@ -15,12 +15,28 @@ * 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: * Fix 'make check' on OS X. (David Fang) diff -r fd354d0bfbc0 -r 10bf97a36be0 ChangeLog.win32 --- a/ChangeLog.win32 Sun Feb 28 22:56:50 2010 +0000 +++ b/ChangeLog.win32 Tue Mar 09 21:41:55 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 diff -r fd354d0bfbc0 -r 10bf97a36be0 Makefile.mingw --- a/Makefile.mingw Sun Feb 28 22:56:50 2010 +0000 +++ b/Makefile.mingw Tue Mar 09 21:41:55 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 ./ diff -r fd354d0bfbc0 -r 10bf97a36be0 configure.ac --- a/configure.ac Sun Feb 28 22:56:50 2010 +0000 +++ b/configure.ac Tue Mar 09 21:41:55 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 +#include + 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 diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/connection.h --- a/libpurple/connection.h Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/connection.h Tue Mar 09 21:41:55 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 diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/ft.c --- a/libpurple/ft.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/ft.c Tue Mar 09 21:41:55 2010 +0000 @@ -622,7 +622,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; @@ -1128,9 +1128,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. */ @@ -1142,54 +1143,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) { @@ -1204,12 +1210,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. diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/log.c --- a/libpurple/log.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/log.c Tue Mar 09 21:41:55 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; diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/plugins/perl/common/module.h --- a/libpurple/plugins/perl/common/module.h Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/plugins/perl/common/module.h Tue Mar 09 21:41:55 2010 +0000 @@ -9,6 +9,7 @@ #include #ifdef _WIN32 #undef pipe +#undef STRINGIFY #endif #include #include diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/plugins/perl/perl-common.c --- a/libpurple/plugins/perl/perl-common.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/plugins/perl/perl-common.c Tue Mar 09 21:41:55 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, diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/plugins/perl/perl-common.h --- a/libpurple/plugins/perl/perl-common.h Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/plugins/perl/perl-common.h Tue Mar 09 21:41:55 2010 +0000 @@ -3,6 +3,7 @@ #include #ifdef _WIN32 +#undef STRINGIFY #undef pipe #endif #include @@ -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_ */ diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/plugins/perl/perl-handlers.c --- a/libpurple/plugins/perl/perl-handlers.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/plugins/perl/perl-handlers.c Tue Mar 09 21:41:55 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]); diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/plugins/ssl/Makefile.mingw --- a/libpurple/plugins/ssl/Makefile.mingw Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/plugins/ssl/Makefile.mingw Tue Mar 09 21:41:55 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 diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/bonjour/mdns_win32.c --- a/libpurple/protocols/bonjour/mdns_win32.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/bonjour/mdns_win32.c Tue Mar 09 21:41:55 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; diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/Makefile.am --- a/libpurple/protocols/gg/Makefile.am Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/gg/Makefile.am Tue Mar 09 21:41:55 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 = \ diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/Makefile.mingw --- a/libpurple/protocols/gg/Makefile.mingw Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/gg/Makefile.mingw Tue Mar 09 21:41:55 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 \ diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/buddylist.c --- a/libpurple/protocols/gg/buddylist.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/gg/buddylist.c Tue Mar 09 21:41:55 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); diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/gg.c --- a/libpurple/protocols/gg/gg.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/gg/gg.c Tue Mar 09 21:41:55 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); diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/lib/common.c --- a/libpurple/protocols/gg/lib/common.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/gg/lib/common.c Tue Mar 09 21:41:55 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 - * Robert J. Wony + * Robert J. Woźny * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -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 -#include -#include -#include -#include -#ifdef sun -# include -#endif +# include +# include +# include +# include +# include +# ifdef sun +# include +# endif #endif #include #include + #ifndef _WIN32 -#include +# include #endif + #include #include #include #include #include +/** + * 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: diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/lib/compat.h --- a/libpurple/protocols/gg/lib/compat.h Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/gg/lib/compat.h Tue Mar 09 21:41:55 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 - * Robert J. Wony + * Robert J. Woźny * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -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 diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/lib/dcc.c --- a/libpurple/protocols/gg/lib/dcc.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/gg/lib/dcc.c Tue Mar 09 21:41:55 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 - * Tomasz Chiliski + * (C) Copyright 2001-2008 Wojtek Kaniewski + * Tomasz Chiliński + * Adam Wysocki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -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 #include + #ifndef _WIN32 -#include -#include -#include -#include -#ifdef sun -# include -#endif +# include +# include +# include +# include +# ifdef sun +# include +# endif #endif #include @@ -43,67 +50,71 @@ #include #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); } diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/lib/dcc7.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/dcc7.c Tue Mar 09 21:41:55 2010 +0000 @@ -0,0 +1,1217 @@ +/* $Id: dcc7.c 711 2009-04-16 00:52:47Z darkjames $ */ + +/* + * (C) Copyright 2001-2008 Wojtek Kaniewski + * Tomasz Chiliński + * Adam Wysocki + * + * Thanks to Jakub Zawadzki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, + * USA. + */ + +/** + * \file dcc7.c + * + * \brief Obsługa połączeń bezpośrednich od wersji Gadu-Gadu 7.x + */ + +#include "libgadu.h" + +#include +#include + +#ifndef _WIN32 +# include +# include +# include +# include +# ifdef sun +# include +# endif +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#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); +} + diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/lib/events.c --- a/libpurple/protocols/gg/lib/events.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/gg/lib/events.c Tue Mar 09 21:41:55 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 - * Robert J. Wony - * Arkadiusz Mikiewicz + * (C) Copyright 2001-2006 Wojtek Kaniewski + * Robert J. Woźny + * Arkadiusz Miśkiewicz + * Adam Wysocki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -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 + #ifndef _WIN32 -#include -#include -#include -#include -#include +# include +# include +# include +# include #endif -#include "libgadu-config.h" +#include "compat.h" +#include "protocol.h" #include -#ifdef __GG_LIBGADU_HAVE_PTHREAD -# include -#endif #include #include #include #include #include -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#include +#ifdef GG_CONFIG_HAVE_OPENSSL # include # include #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, "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; } /* diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/lib/http.c --- a/libpurple/protocols/gg/lib/http.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/gg/lib/http.c Tue Mar 09 21:41:55 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 @@ -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 + #ifndef _WIN32 -#include -#include -#include -#include +# include +# include +# include #endif -#include "libgadu-config.h" +#include "compat.h" +#include "resolver.h" #include #include + #ifndef _WIN32 -#include +# include #endif -#ifdef __GG_LIBGADU_HAVE_PTHREAD -# include -#endif + +#include #include #include #include #include #include -#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) { diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/lib/libgadu-internal.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/libgadu-internal.h Tue Mar 09 21:41:55 2010 +0000 @@ -0,0 +1,30 @@ +/* $Id$ */ + +/* + * (C) Copyright 2009 Jakub Zawadzki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef LIBGADU_INTERNAL_H +#define LIBGADU_INTERNAL_H + +#include "libgadu.h" + +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 */ diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/lib/libgadu.c --- a/libpurple/protocols/gg/lib/libgadu.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/gg/lib/libgadu.c Tue Mar 09 21:41:55 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 - * Robert J. Wony - * Arkadiusz Mikiewicz - * Tomasz Chiliski + * (C) Copyright 2001-2009 Wojtek Kaniewski + * Robert J. Woźny + * Arkadiusz Miśkiewicz + * Tomasz Chiliński + * Adam Wysocki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -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 -#ifndef _WIN32 -#include -#include -#include -#include -#ifdef sun -# include -#endif + +#ifdef _WIN32 +# include +# include +# include +# define SHUT_RDWR SD_BOTH #else -#include -#include -#include -#define SHUT_RDWR SD_BOTH +# include +# include +# include +# ifdef sun +# include +# endif #endif -#include "libgadu-config.h" +#include "compat.h" +#include "protocol.h" +#include "resolver.h" -#include #ifndef _WIN32 -#include +# include /* on Win32 this is included above */ +# include #endif -#ifdef __GG_LIBGADU_HAVE_PTHREAD -# include -#endif + #include #include #include #include +#include +#include #include -#ifdef __GG_LIBGADU_HAVE_OPENSSL +#ifdef GG_CONFIG_HAVE_OPENSSL # include # include #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[] = ""; + const int span_len = 75; + const char img_fmt[] = ""; + 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, "", 4); + + if ((old_attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "", 4); + + if ((old_attr & GG_FONT_BOLD) != 0) + gg_append(dst, &len, "", 4); - gg_debug(GG_DEBUG_FUNCTION, "** gg_change_status_descr_time(%p, %d, \"%s\", %d);\n", sess, status, descr, time); + gg_append(dst, &len, "", 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, "", 3); + + if ((attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "", 3); + + if ((attr & GG_FONT_UNDERLINE) != 0) + gg_append(dst, &len, "", 3); + + if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) { + if (dst != NULL) { + sprintf(&dst[len], img_fmt, + format[format_idx + 9], + format[format_idx + 8], + format[format_idx + 7], + format[format_idx + 6], + format[format_idx + 5], + format[format_idx + 4], + format[format_idx + 3], + format[format_idx + 2]); + } + + len += img_len; + format_idx += 10; + } + + old_attr = attr; + } 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, "
", 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, "
", 4); + + if ((old_attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "
", 4); + + if ((old_attr & GG_FONT_BOLD) != 0) + gg_append(dst, &len, "
", 4); + + /* Dla pustych tekstów dodaj pusty . */ + + if (i == 0) { + if (dst != NULL) + sprintf(&dst[len], span_fmt, 0, 0, 0); + + len += span_len; + } + + gg_append(dst, &len, "", 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 diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/lib/libgadu.h --- a/libpurple/protocols/gg/lib/libgadu.h Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/gg/lib/libgadu.h Tue Mar 09 21:41:55 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 - * Robert J. Wony - * Arkadiusz Mikiewicz - * Tomasz Chiliski + * (C) Copyright 2001-2009 Wojtek Kaniewski + * Robert J. Woźny + * Arkadiusz Miśkiewicz + * Tomasz Chiliński * Piotr Wysocki * Dawid Jarosz + * Jakub Zawadzki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -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 #include #include #include -#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 . */ +#undef GG_CONFIG_HAVE_STDINT_H + +/* Defined if uintX_t types are defined in . */ +#undef GG_CONFIG_HAVE_INTTYPES_H + +/* Defined if uintX_t types are defined in . */ +#undef GG_CONFIG_HAVE_SYS_INTTYPES_H + +/* Defined if uintX_t types are defined in . */ +#undef GG_CONFIG_HAVE_SYS_INT_TYPES_H + +/* Defined if uintX_t types are defined in . */ +#undef GG_CONFIG_HAVE_SYS_TYPES_H + +#ifdef GG_CONFIG_HAVE_OPENSSL #include #endif -/* - * typedef uin_t - * - * typ reprezentujcy numer osoby. +#ifdef GG_CONFIG_HAVE_STDINT_H +#include +#else +# ifdef GG_CONFIG_HAVE_INTTYPES_H +# include +# else +# ifdef GG_CONFIG_HAVE_SYS_INTTYPES_H +# include +# else +# ifdef GG_CONFIG_HAVE_SYS_INT_TYPES_H +# include +# else +# ifdef GG_CONFIG_HAVE_SYS_TYPES_H +# include +# else + +#ifndef __AC_STDINT_H +#define __AC_STDINT_H + +/* ISO C 9X: 7.18 Integer types */ + +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 */ diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/lib/obsolete.c --- a/libpurple/protocols/gg/lib/obsolete.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/gg/lib/obsolete.c Tue Mar 09 21:41:55 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 @@ -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 #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 */ diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/lib/protocol.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/protocol.h Tue Mar 09 21:41:55 2010 +0000 @@ -0,0 +1,165 @@ +/* $Id$ */ + +/* + * (C) Copyright 2009 Jakub Zawadzki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef LIBGADU_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 */ diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/lib/pubdir.c --- a/libpurple/protocols/gg/lib/pubdir.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/gg/lib/pubdir.c Tue Mar 09 21:41:55 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 + * (C) Copyright 2001-2006 Wojtek Kaniewski * Dawid Jarosz + * Adam Wysocki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License Version @@ -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 #include @@ -29,20 +37,20 @@ #include #include -/* - * 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) { diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/lib/pubdir50.c --- a/libpurple/protocols/gg/lib/pubdir50.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/gg/lib/pubdir50.c Tue Mar 09 21:41:55 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 @@ -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 #include #include #include -/* - * 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) { diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/lib/resolver.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/resolver.c Tue Mar 09 21:41:55 2010 +0000 @@ -0,0 +1,1065 @@ +/* $Id$ */ + +/* + * (C) Copyright 2001-2009 Wojtek Kaniewski + * Robert J. Woźny + * Arkadiusz Miśkiewicz + * Tomasz Chiliński + * Adam Wysocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/** + * \file resolver.c + * + * \brief Funkcje rozwiązywania nazw + */ + +#ifndef _WIN32 +# include +# include +# include +# include +# include +#endif + +#include +#include +#include +#include + +#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 + +/** + * \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; +} + diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/lib/resolver.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/resolver.h Tue Mar 09 21:41:55 2010 +0000 @@ -0,0 +1,30 @@ +/* $Id$ */ + +/* + * (C) Copyright 2008 Wojtek Kaniewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef LIBGADU_RESOLVER_H +#define LIBGADU_RESOLVER_H + +#ifndef _WIN32 +# include +#endif + +int gg_gethostbyname_real(const char *hostname, struct in_addr *result, int pthread); + +#endif /* LIBGADU_RESOLVER_H */ diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/gg/lib/sha1.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libpurple/protocols/gg/lib/sha1.c Tue Mar 09 21:41:55 2010 +0000 @@ -0,0 +1,303 @@ +/* $Id: sha1.c 632 2008-07-30 18:40:06Z darkjames $ */ + +/* + * (C) Copyright 2007 Wojtek Kaniewski + * + * Public domain SHA-1 implementation by Steve Reid + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/** + * \file sha1.c + * + * \brief Funkcje wyznaczania skrĂłtu SHA1 + */ + +#include +#include +#include + +#include "libgadu.h" + +/** \cond ignore */ + +#ifdef GG_CONFIG_HAVE_OPENSSL + +#include + +#else + +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +Modified by Wojtek Kaniewski for compatibility +with libgadu and OpenSSL API. + +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define LITTLE_ENDIAN * This should be #define'd if true. */ +/* #define SHA1HANDSOFF * Copies data before messing with it. */ + +#include + +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 */ diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/irc/irc.c --- a/libpurple/protocols/irc/irc.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/irc/irc.c Tue Mar 09 21:41:55 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; diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/jabber/buddy.c --- a/libpurple/protocols/jabber/buddy.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/jabber/buddy.c Tue Mar 09 21:41:55 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; } diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/jabber/data.c --- a/libpurple/protocols/jabber/data.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/jabber/data.c Tue Mar 09 21:41:55 2010 +0000 @@ -39,7 +39,7 @@ gboolean ephemeral, 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); diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/jabber/disco.c --- a/libpurple/protocols/jabber/disco.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/jabber/disco.c Tue Mar 09 21:41:55 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")) diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/jabber/jabber.c --- a/libpurple/protocols/jabber/jabber.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/jabber/jabber.c Tue Mar 09 21:41:55 2010 +0000 @@ -74,7 +74,10 @@ GList *jabber_features = NULL; GList *jabber_identities = NULL; -static GSList *jabber_cmds = NULL; + +static GHashTable *jabber_cmds = NULL; /* PurplePlugin * => GSList of ids */ + +static gint plugin_ref = 0; static void jabber_unregister_account_cb(JabberStream *js); static void try_srv_connect(JabberStream *js); @@ -2957,7 +2960,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; } @@ -3306,40 +3318,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, @@ -3348,7 +3387,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 | @@ -3356,7 +3395,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 | @@ -3364,7 +3403,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 | @@ -3372,7 +3411,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 | @@ -3380,7 +3419,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 | @@ -3388,7 +3427,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 | @@ -3396,14 +3435,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 | @@ -3411,24 +3450,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 */ /** @@ -3477,8 +3531,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; @@ -3486,6 +3540,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 || @@ -3548,7 +3604,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), @@ -3563,7 +3664,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, @@ -3577,14 +3677,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(); } diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/jabber/jabber.h --- a/libpurple/protocols/jabber/jabber.h Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/jabber/jabber.h Tue Mar 09 21:41:55 2010 +0000 @@ -376,10 +376,7 @@ PurpleMediaCaps jabber_get_media_caps(PurpleAccount *account, const char *who); gboolean jabber_can_receive_file(PurpleConnection *gc, const gchar *who); -void jabber_register_commands(void); -void jabber_unregister_commands(void); - -void jabber_init_plugin(PurplePlugin *plugin); -void jabber_uninit_plugin(PurplePlugin *plugin); +void jabber_plugin_init(PurplePlugin *plugin); +void jabber_plugin_uninit(PurplePlugin *plugin); #endif /* PURPLE_JABBER_H_ */ diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/jabber/jutil.c --- a/libpurple/protocols/jabber/jutil.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/jabber/jutil.c Tue Mar 09 21:41:55 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; diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/jabber/libxmpp.c --- a/libpurple/protocols/jabber/libxmpp.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/jabber/libxmpp.c Tue Mar 09 21:41:55 2010 +0000 @@ -132,104 +132,14 @@ static gboolean load_plugin(PurplePlugin *plugin) { - purple_signal_register(plugin, "jabber-receiving-xmlnode", - purple_marshal_VOID__POINTER_POINTER, NULL, 2, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); - - purple_signal_register(plugin, "jabber-sending-xmlnode", - purple_marshal_VOID__POINTER_POINTER, NULL, 2, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new_outgoing(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); - - /* - * Do not remove this or the plugin will fail. Completely. You have been - * warned! - */ - purple_signal_connect_priority(plugin, "jabber-sending-xmlnode", - plugin, PURPLE_CALLBACK(jabber_send_signal_cb), - NULL, PURPLE_SIGNAL_PRIORITY_HIGHEST); - - purple_signal_register(plugin, "jabber-sending-text", - purple_marshal_VOID__POINTER_POINTER, NULL, 2, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new_outgoing(PURPLE_TYPE_STRING)); - - purple_signal_register(plugin, "jabber-receiving-message", - purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER_POINTER, - purple_value_new(PURPLE_TYPE_BOOLEAN), 6, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new(PURPLE_TYPE_STRING), /* type */ - purple_value_new(PURPLE_TYPE_STRING), /* id */ - purple_value_new(PURPLE_TYPE_STRING), /* from */ - purple_value_new(PURPLE_TYPE_STRING), /* to */ - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); - - purple_signal_register(plugin, "jabber-receiving-iq", - purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER, - purple_value_new(PURPLE_TYPE_BOOLEAN), 5, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new(PURPLE_TYPE_STRING), /* type */ - purple_value_new(PURPLE_TYPE_STRING), /* id */ - purple_value_new(PURPLE_TYPE_STRING), /* from */ - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); - - purple_signal_register(plugin, "jabber-watched-iq", - purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER_POINTER, - purple_value_new(PURPLE_TYPE_BOOLEAN), 5, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new(PURPLE_TYPE_STRING), /* type */ - purple_value_new(PURPLE_TYPE_STRING), /* id */ - purple_value_new(PURPLE_TYPE_STRING), /* from */ - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); /* child */ - - /* Modifying these? Look at jabber_init_plugin for the ipc versions */ - purple_signal_register(plugin, "jabber-register-namespace-watcher", - purple_marshal_VOID__POINTER_POINTER, - NULL, 2, - purple_value_new(PURPLE_TYPE_STRING), /* node */ - purple_value_new(PURPLE_TYPE_STRING)); /* namespace */ - - purple_signal_register(plugin, "jabber-unregister-namespace-watcher", - purple_marshal_VOID__POINTER_POINTER, - NULL, 2, - purple_value_new(PURPLE_TYPE_STRING), /* node */ - purple_value_new(PURPLE_TYPE_STRING)); /* namespace */ - - purple_signal_connect(plugin, "jabber-register-namespace-watcher", - plugin, PURPLE_CALLBACK(jabber_iq_signal_register), NULL); - purple_signal_connect(plugin, "jabber-unregister-namespace-watcher", - plugin, PURPLE_CALLBACK(jabber_iq_signal_unregister), NULL); - - purple_signal_register(plugin, "jabber-receiving-presence", - purple_marshal_BOOLEAN__POINTER_POINTER_POINTER_POINTER, - purple_value_new(PURPLE_TYPE_BOOLEAN), 4, - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_CONNECTION), - purple_value_new(PURPLE_TYPE_STRING), /* type */ - purple_value_new(PURPLE_TYPE_STRING), /* from */ - purple_value_new(PURPLE_TYPE_SUBTYPE, PURPLE_SUBTYPE_XMLNODE)); + jabber_plugin_init(plugin); return TRUE; } static gboolean unload_plugin(PurplePlugin *plugin) { - purple_signals_unregister_by_instance(plugin); - - /* reverse order of init_plugin */ - jabber_bosh_uninit(); - jabber_data_uninit(); - jabber_si_uninit(); - jabber_ibb_uninit(); - /* PEP things should be uninit via jabber_pep_uninit, not here */ - jabber_pep_uninit(); - jabber_caps_uninit(); - jabber_iq_uninit(); - - jabber_unregister_commands(); - - /* Stay on target...stay on target... Almost there... */ - jabber_uninit_plugin(plugin); + jabber_plugin_uninit(plugin); return TRUE; } @@ -402,7 +312,6 @@ option); my_protocol = plugin; - jabber_init_plugin(plugin); purple_prefs_remove("/plugins/prpl/jabber"); @@ -423,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); diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/jabber/message.c --- a/libpurple/protocols/jabber/message.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/jabber/message.c Tue Mar 09 21:41:55 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; diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/jabber/pep.c --- a/libpurple/protocols/jabber/pep.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/jabber/pep.c Tue Mar 09 21:41:55 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); diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/jabber/presence.c --- a/libpurple/protocols/jabber/presence.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/jabber/presence.c Tue Mar 09 21:41:55 2010 +0000 @@ -137,7 +137,9 @@ if (purple_status_type_get_primitive(purple_status_get_type(status)) == PURPLE_STATUS_MOOD) { const char *mood = purple_status_get_attr_string(status, PURPLE_MOOD_NAME); - jabber_mood_set(js, mood, NULL); + const char *mood_text = + purple_status_get_attr_string(status, PURPLE_MOOD_COMMENT); + jabber_mood_set(js, mood, mood_text); return; } diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/jabber/useravatar.c --- a/libpurple/protocols/jabber/useravatar.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/jabber/useravatar.c Tue Mar 09 21:41:55 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 { diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/jabber/usermood.c --- a/libpurple/protocols/jabber/usermood.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/jabber/usermood.c Tue Mar 09 21:41:55 2010 +0000 @@ -119,10 +119,6 @@ {NULL, NULL, NULL} }; -static PurpleMood empty_moods[] = { - {NULL, NULL, NULL} -}; - 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"); @@ -174,68 +170,6 @@ jabber_pep_register_handler("http://jabber.org/protocol/mood", jabber_mood_cb); } -static void do_mood_set_from_fields(PurpleConnection *gc, PurpleRequestFields *fields) { - JabberStream *js; - const int max_mood_idx = sizeof(moods) / sizeof(moods[0]) - 1; - int selected_mood = purple_request_fields_get_choice(fields, "mood"); - - if (!PURPLE_CONNECTION_IS_VALID(gc)) { - purple_debug_error("jabber", "Unable to set mood; account offline.\n"); - return; - } - - js = gc->proto_data; - - if (selected_mood < 0 || selected_mood >= max_mood_idx) { - purple_debug_error("jabber", "Invalid mood index (%d) selected.\n", selected_mood); - return; - } - - jabber_mood_set(js, moods[selected_mood].mood, purple_request_fields_get_string(fields, "text")); -} - -static void do_mood_set_mood(PurplePluginAction *action) { - PurpleConnection *gc = (PurpleConnection *) action->context; - - PurpleRequestFields *fields; - PurpleRequestFieldGroup *group; - PurpleRequestField *field; - int i; - - fields = purple_request_fields_new(); - group = purple_request_field_group_new(NULL); - purple_request_fields_add_group(fields, group); - - field = purple_request_field_choice_new("mood", - _("Mood"), 0); - - for(i = 0; moods[i].mood; ++i) - purple_request_field_choice_add(field, _(moods[i].description)); - - purple_request_field_set_required(field, TRUE); - purple_request_field_group_add_field(group, field); - - field = purple_request_field_string_new("text", - _("Description"), NULL, - FALSE); - purple_request_field_group_add_field(group, field); - - purple_request_fields(gc, _("Edit User Mood"), - _("Edit User Mood"), - _("Please select your mood from the list."), - fields, - _("Set"), G_CALLBACK(do_mood_set_from_fields), - _("Cancel"), NULL, - purple_connection_get_account(gc), NULL, NULL, - gc); - -} - -void jabber_mood_init_action(GList **m) { - PurplePluginAction *act = purple_plugin_action_new(_("Set Mood..."), do_mood_set_mood); - *m = g_list_append(*m, act); -} - void jabber_mood_set(JabberStream *js, const char *mood, const char *text) { xmlnode *publish, *moodnode; @@ -260,14 +194,5 @@ PurpleMood *jabber_get_moods(PurpleAccount *account) { - PurpleConnection *gc = purple_account_get_connection(account); - JabberStream *js = (JabberStream *) gc->proto_data; - - if (js->pep) { - purple_debug_info("jabber", "get_moods: account supports PEP\n"); - return moods; - } else { - purple_debug_info("jabber", "get_moods: account doesn't support PEP\n"); - return empty_moods; - } + return moods; } \ No newline at end of file diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/jabber/usermood.h --- a/libpurple/protocols/jabber/usermood.h Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/jabber/usermood.h Tue Mar 09 21:41:55 2010 +0000 @@ -30,8 +30,6 @@ void jabber_mood_init(void); -void jabber_mood_init_action(GList **m); - void jabber_mood_set(JabberStream *js, const char *mood, /* must be one of the valid strings defined in the XEP */ const char *text /* might be NULL */); diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/jabber/usernick.c --- a/libpurple/protocols/jabber/usernick.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/jabber/usernick.c Tue Mar 09 21:41:55 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) { diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/msn/slplink.c --- a/libpurple/protocols/msn/slplink.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/msn/slplink.c Tue Mar 09 21:41:55 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. */ diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/mxit/mxit.h --- a/libpurple/protocols/mxit/mxit.h Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/mxit/mxit.h Tue Mar 09 21:41:55 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 */ diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/null/nullprpl.c --- a/libpurple/protocols/null/nullprpl.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/null/nullprpl.c Tue Mar 09 21:41:55 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) diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/protocols/oscar/oscar.c --- a/libpurple/protocols/oscar/oscar.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/protocols/oscar/oscar.c Tue Mar 09 21:41:55 2010 +0000 @@ -1573,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; @@ -3797,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; diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/signals.c --- a/libpurple/signals.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/signals.c Tue Mar 09 21:41:55 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) diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/signals.h --- a/libpurple/signals.h Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/signals.h Tue Mar 09 21:41:55 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( diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/upnp.c --- a/libpurple/upnp.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/upnp.c Tue Mar 09 21:41:55 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) { diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/win32/global.mak --- a/libpurple/win32/global.mak Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/win32/global.mak Tue Mar 09 21:41:55 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.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 diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/win32/libc_interface.c --- a/libpurple/win32/libc_interface.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/win32/libc_interface.c Tue Mar 09 21:41:55 2010 +0000 @@ -62,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(); @@ -973,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); @@ -988,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); diff -r fd354d0bfbc0 -r 10bf97a36be0 libpurple/win32/win32dep.c --- a/libpurple/win32/win32dep.c Sun Feb 28 22:56:50 2010 +0000 +++ b/libpurple/win32/win32dep.c Tue Mar 09 21:41:55 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 diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/Makefile.am --- a/pidgin/Makefile.am Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/Makefile.am Tue Mar 09 21:41:55 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 \ diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/Makefile.mingw --- a/pidgin/Makefile.mingw Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/Makefile.mingw Tue Mar 09 21:41:55 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 diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/gtkblist.c --- a/pidgin/gtkblist.c Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/gtkblist.c Tue Mar 09 21:41:55 2010 +0000 @@ -7818,20 +7818,29 @@ static void edit_mood_cb(PurpleConnection *gc, PurpleRequestFields *fields) { - PurpleRequestField *f; + PurpleRequestField *mood_field, *text_field; GList *l; - f = purple_request_fields_get_field(fields, "mood"); - l = purple_request_field_list_get_selected(f); + 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(f, l->data); + 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, "")) { - purple_account_set_status(account, "mood", TRUE, - PURPLE_MOOD_NAME, mood, - NULL); + 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); } @@ -7847,7 +7856,6 @@ PurpleRequestFields *fields; PurpleRequestFieldGroup *g; PurpleRequestField *f; - char* na_fn; PurpleConnection *gc = purple_account_get_connection(account); PurplePluginProtocolInfo *prpl_info; PurpleMood *mood; @@ -7861,19 +7869,10 @@ g = purple_request_field_group_new(NULL); f = purple_request_field_list_new("mood", _("Please select your mood from the list")); - na_fn = g_build_filename("pixmaps", "pidgin", "emblems", "16", "not-authorized.png", NULL); - - purple_request_field_list_add_icon(f, _("None"), na_fn, NULL); + purple_request_field_list_add(f, _("None"), ""); if (current_mood == NULL) purple_request_field_list_add_selected(f, _("None")); - g_free(na_fn); - - /* first item is an empty one for unsetting the mood */ - purple_request_field_list_add(f, "", ""); - if (purple_strequal(current_mood, "")) - purple_request_field_list_add_selected(f, ""); - /* TODO: rlaager wants this sorted. */ for (mood = prpl_info->get_moods(account); mood->mood != NULL ; mood++) { @@ -7894,6 +7893,15 @@ 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), @@ -8026,8 +8034,10 @@ 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)) { + 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; diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/plugins/xmppconsole.c --- a/pidgin/plugins/xmppconsole.c Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/plugins/xmppconsole.c Tue Mar 09 21:41:55 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); diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/gtkdocklet-win32.c --- a/pidgin/win32/gtkdocklet-win32.c Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/gtkdocklet-win32.c Tue Mar 09 21:41:55 2010 +0000 @@ -21,7 +21,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02111-1301, USA. */ - +#define _WIN32_IE 0x0500 #include #include #include @@ -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) { diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/available.lst --- a/pidgin/win32/nsis/available.lst Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/available.lst Tue Mar 09 21:41:55 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 diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/generate_gtk_zip.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pidgin/win32/nsis/generate_gtk_zip.sh Tue Mar 09 21:41:55 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 + diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/langmacros.nsh --- a/pidgin/win32/nsis/langmacros.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/langmacros.nsh Tue Mar 09 21:41:55 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,37 +66,11 @@ ; Spellcheck Section Prompts !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SECTION_TITLE ${CUR_LANG} !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ERROR ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_DICT_ERROR ${CUR_LANG} !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SECTION_DESCRIPTION ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT ASPELL_INSTALL_FAILED ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_BRETON ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_CATALAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_CZECH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_WELSH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_DANISH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_GERMAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ENGLISH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_GREEK ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ESPERANTO ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SPANISH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_FAROESE ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_FRENCH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ITALIAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_DUTCH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_NORWEGIAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_POLISH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_PORTUGUESE ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_ROMANIAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_RUSSIAN ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SLOVAK ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_SWEDISH ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_SPELLCHECK_UKRAINIAN ${CUR_LANG} !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_DEBUGSYMBOLS_ERROR ${CUR_LANG} !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT PIDGIN_GTK_DOWNLOAD_ERROR ${CUR_LANG} - !insertmacro PIDGIN_MACRO_LANGSTRING_INSERT TRANSLATIONS_SECTION_TITLE ${CUR_LANG} - !undef CUR_LANG !macroend diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/pidgin-installer.nsi --- a/pidgin/win32/nsis/pidgin-installer.nsi Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/pidgin-installer.nsi Tue Mar 09 21:41:55 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-20100223" ; 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 @@ -356,6 +356,9 @@ !endif + ;Delete the old Gtk directory + RMDir /r "$INSTDIR\Gtk" + SetOutPath "$INSTDIR" nsisunz::UnzipToLog $R1 "$INSTDIR" Pop $R0 @@ -414,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} "" @@ -504,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 @@ -517,95 +519,15 @@ ;-------------------------------- ;Spell Checking -SectionGroup /e $(PIDGIN_SPELLCHECK_SECTION_TITLE) SecSpellCheck - Section /o $(PIDGIN_SPELLCHECK_BRETON) SecSpellCheckBreton - Push ${SecSpellCheckBreton} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_CATALAN) SecSpellCheckCatalan - Push ${SecSpellCheckCatalan} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_CZECH) SecSpellCheckCzech - Push ${SecSpellCheckCzech} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_WELSH) SecSpellCheckWelsh - Push ${SecSpellCheckWelsh} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_DANISH) SecSpellCheckDanish - Push ${SecSpellCheckDanish} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_GERMAN) SecSpellCheckGerman - Push ${SecSpellCheckGerman} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_GREEK) SecSpellCheckGreek - Push ${SecSpellCheckGreek} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_ENGLISH) SecSpellCheckEnglish - Push ${SecSpellCheckEnglish} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_ESPERANTO) SecSpellCheckEsperanto - Push ${SecSpellCheckEsperanto} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_SPANISH) SecSpellCheckSpanish - Push ${SecSpellCheckSpanish} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_FAROESE) SecSpellCheckFaroese - Push ${SecSpellCheckFaroese} - Call InstallAspellAndDict +!macro SPELLCHECK_SECTION lang lang_name lang_file + Section /o "${lang_name}" SecSpell_${lang} + Push ${lang_file} + Push ${lang} + Call InstallDict SectionEnd - Section /o $(PIDGIN_SPELLCHECK_FRENCH) SecSpellCheckFrench - Push ${SecSpellCheckFrench} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_ITALIAN) SecSpellCheckItalian - Push ${SecSpellCheckItalian} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_DUTCH) SecSpellCheckDutch - Push ${SecSpellCheckDutch} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_NORWEGIAN) SecSpellCheckNorwegian - Push ${SecSpellCheckNorwegian} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_POLISH) SecSpellCheckPolish - Push ${SecSpellCheckPolish} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_PORTUGUESE) SecSpellCheckPortuguese - Push ${SecSpellCheckPortuguese} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_ROMANIAN) SecSpellCheckRomanian - Push ${SecSpellCheckRomanian} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_RUSSIAN) SecSpellCheckRussian - Push ${SecSpellCheckRussian} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_SLOVAK) SecSpellCheckSlovak - Push ${SecSpellCheckSlovak} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_SWEDISH) SecSpellCheckSwedish - Push ${SecSpellCheckSwedish} - Call InstallAspellAndDict - SectionEnd - Section /o $(PIDGIN_SPELLCHECK_UKRAINIAN) SecSpellCheckUkrainian - Push ${SecSpellCheckUkrainian} - Call InstallAspellAndDict - SectionEnd +!macroend +SectionGroup $(PIDGIN_SPELLCHECK_SECTION_TITLE) SecSpellCheck + !include "pidgin-spellcheck.nsh" SectionGroupEnd Section /o $(DEBUG_SYMBOLS_SECTION_TITLE) SecDebugSymbols @@ -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-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} "$R0" "/KEEPGTK=" $R1 + ${GetOptions} "$R0" "/UPGRADE=" $R1 IfErrors +2 - StrCmp $R1 "1" +2 + StrCmp $R1 "1" upgrade_done RMDir /r "$INSTDIR\Gtk" + ; Remove the downloaded spellcheck dictionaries (if we're not just upgrading) + RMDir /r "$INSTDIR\spellcheck" + upgrade_done: ;Try to remove Pidgin install dir (only if empty) RMDir "$INSTDIR" @@ -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 @@ -1393,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 diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/afrikaans.nsh --- a/pidgin/win32/nsis/translations/afrikaans.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/afrikaans.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/arabic.nsh --- a/pidgin/win32/nsis/translations/arabic.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/arabic.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/basque.nsh --- a/pidgin/win32/nsis/translations/basque.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/basque.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/catalan.nsh --- a/pidgin/win32/nsis/translations/catalan.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/catalan.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/dutch.nsh --- a/pidgin/win32/nsis/translations/dutch.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/dutch.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/english.nsh --- a/pidgin/win32/nsis/translations/english.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/english.nsh Tue Mar 09 21:41:55 2010 +0000 @@ -54,31 +54,7 @@ ; Spellcheck Section Prompts !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SECTION_TITLE "Spellchecking Support" !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ERROR "Error Installing Spellchecking" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_DICT_ERROR "Error Installing Spellchecking Dictionary" !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SECTION_DESCRIPTION "Support for Spellchecking. (Internet connection required for installation)" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING ASPELL_INSTALL_FAILED "Installation Failed" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_BRETON "Breton" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_CATALAN "Catalan" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_CZECH "Czech" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_WELSH "Welsh" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_DANISH "Danish" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_GERMAN "German" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_GREEK "Greek" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ENGLISH "English" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ESPERANTO "Esperanto" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SPANISH "Spanish" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_FAROESE "Faroese" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_FRENCH "French" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ITALIAN "Italian" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_DUTCH "Dutch" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_NORWEGIAN "Norwegian" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_POLISH "Polish" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_PORTUGUESE "Portuguese" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_ROMANIAN "Romanian" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_RUSSIAN "Russian" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SLOVAK "Slovak" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_SWEDISH "Swedish" -!insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_SPELLCHECK_UKRAINIAN "Ukrainian" !insertmacro PIDGIN_MACRO_DEFAULT_STRING PIDGIN_DEBUGSYMBOLS_ERROR "Error Installing Debug Symbols" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/finnish.nsh --- a/pidgin/win32/nsis/translations/finnish.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/finnish.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/french.nsh --- a/pidgin/win32/nsis/translations/french.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/french.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/german.nsh --- a/pidgin/win32/nsis/translations/german.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/german.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/hebrew.nsh --- a/pidgin/win32/nsis/translations/hebrew.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/hebrew.nsh Tue Mar 09 21:41:55 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 "" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/hungarian.nsh --- a/pidgin/win32/nsis/translations/hungarian.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/hungarian.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/italian.nsh --- a/pidgin/win32/nsis/translations/italian.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/italian.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/japanese.nsh --- a/pidgin/win32/nsis/translations/japanese.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/japanese.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/kurdish.nsh --- a/pidgin/win32/nsis/translations/kurdish.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/kurdish.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/lithuanian.nsh --- a/pidgin/win32/nsis/translations/lithuanian.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/lithuanian.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/norwegian.nsh --- a/pidgin/win32/nsis/translations/norwegian.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/norwegian.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/persian.nsh --- a/pidgin/win32/nsis/translations/persian.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/persian.nsh Tue Mar 09 21:41:55 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 "" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/portuguese-br.nsh --- a/pidgin/win32/nsis/translations/portuguese-br.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/portuguese-br.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/simp-chinese.nsh --- a/pidgin/win32/nsis/translations/simp-chinese.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/simp-chinese.nsh Tue Mar 09 21:41:55 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 "ڿ" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/slovak.nsh --- a/pidgin/win32/nsis/translations/slovak.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/slovak.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/slovenian.nsh --- a/pidgin/win32/nsis/translations/slovenian.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/slovenian.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/swedish.nsh --- a/pidgin/win32/nsis/translations/swedish.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/swedish.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/trad-chinese.nsh --- a/pidgin/win32/nsis/translations/trad-chinese.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/trad-chinese.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/nsis/translations/valencian.nsh --- a/pidgin/win32/nsis/translations/valencian.nsh Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/nsis/translations/valencian.nsh Tue Mar 09 21:41:55 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" diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/winpidgin.c --- a/pidgin/win32/winpidgin.c Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/winpidgin.c Tue Mar 09 21:41:55 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) { diff -r fd354d0bfbc0 -r 10bf97a36be0 pidgin/win32/wspell.c --- a/pidgin/win32/wspell.c Sun Feb 28 22:56:50 2010 +0000 +++ b/pidgin/win32/wspell.c Tue Mar 09 21:41:55 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 * @@ -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(); +}