summaryrefslogtreecommitdiff
path: root/protocols/Gadu-Gadu
diff options
context:
space:
mode:
authorVadim Dashevskiy <watcherhd@gmail.com>2012-05-15 10:38:20 +0000
committerVadim Dashevskiy <watcherhd@gmail.com>2012-05-15 10:38:20 +0000
commit48540940b6c28bb4378abfeb500ec45a625b37b6 (patch)
tree2ef294c0763e802f91d868bdef4229b6868527de /protocols/Gadu-Gadu
parent5c350913f011e119127baeb32a6aedeb4f0d33bc (diff)
initial commit
git-svn-id: http://svn.miranda-ng.org/main/trunk@2 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'protocols/Gadu-Gadu')
-rw-r--r--protocols/Gadu-Gadu/Gadu-Gadu.dep939
-rw-r--r--protocols/Gadu-Gadu/Gadu-Gadu.dsp378
-rw-r--r--protocols/Gadu-Gadu/Gadu-Gadu.dsw29
-rw-r--r--protocols/Gadu-Gadu/Gadu-Gadu.mak728
-rw-r--r--protocols/Gadu-Gadu/Gadu-Gadu.vcproj1623
-rw-r--r--protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj934
-rw-r--r--protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj.filters212
-rw-r--r--protocols/Gadu-Gadu/Gadu-Gadu_8.vcproj1126
-rw-r--r--protocols/Gadu-Gadu/Gadu-Gadu_9.vcproj1971
-rw-r--r--protocols/Gadu-Gadu/avatar.c461
-rw-r--r--protocols/Gadu-Gadu/core.c1841
-rw-r--r--protocols/Gadu-Gadu/dialogs.c1043
-rw-r--r--protocols/Gadu-Gadu/docs/build-howto.txt26
-rw-r--r--protocols/Gadu-Gadu/docs/gg-license.txt340
-rw-r--r--protocols/Gadu-Gadu/docs/gg-readme.txt338
-rw-r--r--protocols/Gadu-Gadu/docs/gg-translation-sample.txt400
-rw-r--r--protocols/Gadu-Gadu/dynstuff.c613
-rw-r--r--protocols/Gadu-Gadu/dynstuff.h70
-rw-r--r--protocols/Gadu-Gadu/filetransfer.c982
-rw-r--r--protocols/Gadu-Gadu/gg.c774
-rw-r--r--protocols/Gadu-Gadu/gg.h506
-rw-r--r--protocols/Gadu-Gadu/groupchat.c681
-rw-r--r--protocols/Gadu-Gadu/icolib.c109
-rw-r--r--protocols/Gadu-Gadu/icons/block.icobin0 -> 2550 bytes
-rw-r--r--protocols/Gadu-Gadu/icons/clear_ignored_conference.icobin0 -> 2038 bytes
-rw-r--r--protocols/Gadu-Gadu/icons/conference.icobin0 -> 9062 bytes
-rw-r--r--protocols/Gadu-Gadu/icons/delete.icobin0 -> 2550 bytes
-rw-r--r--protocols/Gadu-Gadu/icons/export_list_to_server.icobin0 -> 2038 bytes
-rw-r--r--protocols/Gadu-Gadu/icons/export_list_to_txt_file.icobin0 -> 2038 bytes
-rw-r--r--protocols/Gadu-Gadu/icons/gg.icobin0 -> 6830 bytes
-rw-r--r--protocols/Gadu-Gadu/icons/image.icobin0 -> 2038 bytes
-rw-r--r--protocols/Gadu-Gadu/icons/import_list_from_server.icobin0 -> 2038 bytes
-rw-r--r--protocols/Gadu-Gadu/icons/import_list_from_txt_file.icobin0 -> 2038 bytes
-rw-r--r--protocols/Gadu-Gadu/icons/list.icobin0 -> 2550 bytes
-rw-r--r--protocols/Gadu-Gadu/icons/next.icobin0 -> 2038 bytes
-rw-r--r--protocols/Gadu-Gadu/icons/previous.icobin0 -> 2038 bytes
-rw-r--r--protocols/Gadu-Gadu/icons/remove_list_from_server.icobin0 -> 2038 bytes
-rw-r--r--protocols/Gadu-Gadu/icons/save.icobin0 -> 2038 bytes
-rw-r--r--protocols/Gadu-Gadu/icons/sessions.icobin0 -> 6830 bytes
-rw-r--r--protocols/Gadu-Gadu/icons/settings.icobin0 -> 6830 bytes
-rw-r--r--protocols/Gadu-Gadu/image.c1186
-rw-r--r--protocols/Gadu-Gadu/import.c670
-rw-r--r--protocols/Gadu-Gadu/keepalive.c92
-rw-r--r--protocols/Gadu-Gadu/libgadu/COPYING504
-rw-r--r--protocols/Gadu-Gadu/libgadu/common.c975
-rw-r--r--protocols/Gadu-Gadu/libgadu/compat.h36
-rw-r--r--protocols/Gadu-Gadu/libgadu/dcc.c1363
-rw-r--r--protocols/Gadu-Gadu/libgadu/dcc7.c1655
-rw-r--r--protocols/Gadu-Gadu/libgadu/events.c2864
-rw-r--r--protocols/Gadu-Gadu/libgadu/http.c544
-rw-r--r--protocols/Gadu-Gadu/libgadu/internal.h48
-rw-r--r--protocols/Gadu-Gadu/libgadu/libgadu.c2353
-rw-r--r--protocols/Gadu-Gadu/libgadu/libgadu.h2311
-rw-r--r--protocols/Gadu-Gadu/libgadu/obsolete.c238
-rw-r--r--protocols/Gadu-Gadu/libgadu/protocol.h277
-rw-r--r--protocols/Gadu-Gadu/libgadu/pthread.c78
-rw-r--r--protocols/Gadu-Gadu/libgadu/pthread.h56
-rw-r--r--protocols/Gadu-Gadu/libgadu/pubdir.c862
-rw-r--r--protocols/Gadu-Gadu/libgadu/pubdir50.c557
-rw-r--r--protocols/Gadu-Gadu/libgadu/resolver.c766
-rw-r--r--protocols/Gadu-Gadu/libgadu/resolver.h33
-rw-r--r--protocols/Gadu-Gadu/libgadu/sha1.c308
-rw-r--r--protocols/Gadu-Gadu/libgadu/win32.c65
-rw-r--r--protocols/Gadu-Gadu/libgadu/win32.h75
-rw-r--r--protocols/Gadu-Gadu/links.c173
-rw-r--r--protocols/Gadu-Gadu/m_assocmgr.h301
-rw-r--r--protocols/Gadu-Gadu/m_folders.h284
-rw-r--r--protocols/Gadu-Gadu/m_metacontacts.h166
-rw-r--r--protocols/Gadu-Gadu/oauth.c586
-rw-r--r--protocols/Gadu-Gadu/ownerinfo.c88
-rw-r--r--protocols/Gadu-Gadu/popups.c172
-rw-r--r--protocols/Gadu-Gadu/resource.h147
-rw-r--r--protocols/Gadu-Gadu/resource.rc355
-rw-r--r--protocols/Gadu-Gadu/services.c1020
-rw-r--r--protocols/Gadu-Gadu/sessions.c445
-rw-r--r--protocols/Gadu-Gadu/token.c178
-rw-r--r--protocols/Gadu-Gadu/userutils.c307
-rw-r--r--protocols/Gadu-Gadu/version.h25
-rw-r--r--protocols/Gadu-Gadu/version.rc56
79 files changed, 38343 insertions, 0 deletions
diff --git a/protocols/Gadu-Gadu/Gadu-Gadu.dep b/protocols/Gadu-Gadu/Gadu-Gadu.dep
new file mode 100644
index 0000000000..56ff26a212
--- /dev/null
+++ b/protocols/Gadu-Gadu/Gadu-Gadu.dep
@@ -0,0 +1,939 @@
+# Microsoft Developer Studio Generated Dependency File, included by Gadu-Gadu.mak
+
+.\libgadu\common.c : \
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\newpluginapi.h"\
+ ".\libgadu\internal.h"\
+ ".\libgadu\libgadu.h"\
+ ".\libgadu\win32.h"\
+
+
+.\libgadu\dcc.c : \
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\newpluginapi.h"\
+ ".\libgadu\compat.h"\
+ ".\libgadu\libgadu.h"\
+ ".\libgadu\win32.h"\
+
+
+.\libgadu\dcc7.c : \
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\newpluginapi.h"\
+ ".\libgadu\compat.h"\
+ ".\libgadu\internal.h"\
+ ".\libgadu\libgadu.h"\
+ ".\libgadu\protocol.h"\
+ ".\libgadu\resolver.h"\
+ ".\libgadu\win32.h"\
+
+
+.\libgadu\events.c : \
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\newpluginapi.h"\
+ ".\libgadu\compat.h"\
+ ".\libgadu\internal.h"\
+ ".\libgadu\libgadu.h"\
+ ".\libgadu\protocol.h"\
+ ".\libgadu\win32.h"\
+
+
+.\libgadu\http.c : \
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\newpluginapi.h"\
+ ".\libgadu\compat.h"\
+ ".\libgadu\libgadu.h"\
+ ".\libgadu\resolver.h"\
+ ".\libgadu\win32.h"\
+
+
+.\libgadu\libgadu.c : \
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\newpluginapi.h"\
+ ".\libgadu\compat.h"\
+ ".\libgadu\internal.h"\
+ ".\libgadu\libgadu.h"\
+ ".\libgadu\protocol.h"\
+ ".\libgadu\resolver.h"\
+ ".\libgadu\win32.h"\
+
+
+.\libgadu\pthread.c : \
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\newpluginapi.h"\
+ ".\libgadu\pthread.h"\
+
+
+.\libgadu\pubdir.c : \
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\newpluginapi.h"\
+ ".\libgadu\libgadu.h"\
+ ".\libgadu\win32.h"\
+
+
+.\libgadu\pubdir50.c : \
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\newpluginapi.h"\
+ ".\libgadu\internal.h"\
+ ".\libgadu\libgadu.h"\
+ ".\libgadu\win32.h"\
+
+
+.\libgadu\resolver.c : \
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\newpluginapi.h"\
+ ".\libgadu\compat.h"\
+ ".\libgadu\libgadu.h"\
+ ".\libgadu\pthread.h"\
+ ".\libgadu\resolver.h"\
+ ".\libgadu\win32.h"\
+
+
+.\libgadu\sha1.c : \
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\newpluginapi.h"\
+ ".\libgadu\libgadu.h"\
+ ".\libgadu\win32.h"\
+
+
+.\libgadu\win32.c : \
+ ".\libgadu\win32.h"\
+
+
+.\avatar.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\libgadu\protocol.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\core.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\dialogs.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\dynstuff.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\filetransfer.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\gg.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_version.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+ ".\version.h"\
+
+
+.\groupchat.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_folders.h"\
+ ".\m_metacontacts.h"\
+ ".\resource.h"\
+
+
+.\icolib.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\image.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\import.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\keepalive.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\links.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_assocmgr.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\oauth.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\libgadu\protocol.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\ownerinfo.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\popups.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\services.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\sessions.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\token.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\userutils.c : \
+ "..\..\include\m_avatars.h"\
+ "..\..\include\m_button.h"\
+ "..\..\include\m_chat.h"\
+ "..\..\include\m_clc.h"\
+ "..\..\include\m_clist.h"\
+ "..\..\include\m_clistint.h"\
+ "..\..\include\m_clui.h"\
+ "..\..\include\m_database.h"\
+ "..\..\include\m_file.h"\
+ "..\..\include\m_freeimage.h"\
+ "..\..\include\m_genmenu.h"\
+ "..\..\include\m_icolib.h"\
+ "..\..\include\m_ignore.h"\
+ "..\..\include\m_imgsrvc.h"\
+ "..\..\include\m_langpack.h"\
+ "..\..\include\m_message.h"\
+ "..\..\include\m_netlib.h"\
+ "..\..\include\m_options.h"\
+ "..\..\include\m_plugins.h"\
+ "..\..\include\m_popup.h"\
+ "..\..\include\m_protocols.h"\
+ "..\..\include\m_protoint.h"\
+ "..\..\include\m_protomod.h"\
+ "..\..\include\m_protosvc.h"\
+ "..\..\include\m_skin.h"\
+ "..\..\include\m_ssl.h"\
+ "..\..\include\m_stdhdr.h"\
+ "..\..\include\m_system.h"\
+ "..\..\include\m_userinfo.h"\
+ "..\..\include\m_utils.h"\
+ "..\..\include\m_xml.h"\
+ "..\..\include\newpluginapi.h"\
+ "..\..\include\statusmodes.h"\
+ "..\..\include\win2k.h"\
+ ".\dynstuff.h"\
+ ".\gg.h"\
+ ".\libgadu\libgadu.h"\
+ ".\m_folders.h"\
+ ".\resource.h"\
+
+
+.\resource.rc : \
+ "..\..\include\m_version.h"\
+ ".\icons\block.ico"\
+ ".\icons\clear_ignored_conference.ico"\
+ ".\icons\conference.ico"\
+ ".\icons\delete.ico"\
+ ".\icons\export_list_to_server.ico"\
+ ".\icons\export_list_to_txt_file.ico"\
+ ".\icons\gg.ico"\
+ ".\icons\image.ico"\
+ ".\icons\import_list_from_server.ico"\
+ ".\icons\import_list_from_txt_file.ico"\
+ ".\icons\list.ico"\
+ ".\icons\next.ico"\
+ ".\icons\previous.ico"\
+ ".\icons\remove_list_from_server.ico"\
+ ".\icons\save.ico"\
+ ".\icons\sessions.ico"\
+ ".\icons\settings.ico"\
+ ".\resource.h"\
+ ".\version.h"\
+ ".\version.rc"\
+
diff --git a/protocols/Gadu-Gadu/Gadu-Gadu.dsp b/protocols/Gadu-Gadu/Gadu-Gadu.dsp
new file mode 100644
index 0000000000..0fa4846284
--- /dev/null
+++ b/protocols/Gadu-Gadu/Gadu-Gadu.dsp
@@ -0,0 +1,378 @@
+# Microsoft Developer Studio Project File - Name="GG" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) Dynamic-Link Library" 0x0102
+
+CFG=GG - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE
+!MESSAGE NMAKE /f "Gadu-Gadu.mak".
+!MESSAGE
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "Gadu-Gadu.mak" CFG="GG - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "GG - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "GG - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+CPP=cl.exe
+MTL=midl.exe
+RSC=rc.exe
+
+!IF "$(CFG)" == "GG - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "Release"
+# PROP BASE Intermediate_Dir "Release"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "Release"
+# PROP Intermediate_Dir "Release"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /Zi /O1 /I "../../include" /I "libgadu" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /YX /FD /c
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "NDEBUG"
+# ADD RSC /l 0x409 /i "../../include" /d "NDEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /machine:I386
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib version.lib /nologo /base:"0x32500000" /dll /map /debug /machine:I386 /out:"../../bin/release/plugins/GG.dll" /ALIGN:4096 /ignore:4108
+# SUBTRACT LINK32 /pdb:none
+
+!ELSEIF "$(CFG)" == "GG - Win32 Debug"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "Debug"
+# PROP BASE Intermediate_Dir "Debug"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "Debug"
+# PROP Intermediate_Dir "Debug"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+# ADD BASE CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /YX /FD /GZ /c
+# ADD CPP /nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "libgadu" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /YX /FD /GZ /c
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
+# ADD BASE RSC /l 0x409 /d "_DEBUG"
+# ADD RSC /l 0x409 /i "../../include" /d "_DEBUG"
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /dll /debug /machine:I386 /pdbtype:sept
+# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib version.lib /nologo /base:"0x32500000" /dll /map /debug /machine:I386 /out:"../../bin/debug/plugins/GG.dll" /pdbtype:sept
+# SUBTRACT LINK32 /pdb:none
+
+!ENDIF
+
+# Begin Target
+
+# Name "GG - Win32 Release"
+# Name "GG - Win32 Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+# Begin Group "libgadu"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\libgadu\common.c
+# SUBTRACT CPP /YX
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\compat.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\dcc.c
+# SUBTRACT CPP /YX
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\dcc7.c
+# SUBTRACT CPP /YX
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\events.c
+# SUBTRACT CPP /YX
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\http.c
+# SUBTRACT CPP /YX
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\internal.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\libgadu.c
+# SUBTRACT CPP /YX
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\libgadu.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\protocol.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\pthread.c
+# SUBTRACT CPP /YX
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\pthread.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\pubdir.c
+# SUBTRACT CPP /YX
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\pubdir50.c
+# SUBTRACT CPP /YX
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\resolver.c
+# SUBTRACT CPP /YX
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\resolver.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\sha1.c
+# SUBTRACT CPP /YX
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\win32.c
+# SUBTRACT CPP /YX
+# End Source File
+# Begin Source File
+
+SOURCE=.\libgadu\win32.h
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=.\avatar.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\core.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\dialogs.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\dynstuff.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\filetransfer.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\gg.c
+# ADD CPP /Yc"gg.h"
+# End Source File
+# Begin Source File
+
+SOURCE=.\groupchat.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\icolib.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\image.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\import.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\keepalive.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\links.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\oauth.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\ownerinfo.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\popups.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\services.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\sessions.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\token.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\userutils.c
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE=.\dynstuff.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\gg.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\version.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE=.\icons\block.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icons\clear_ignored_conference.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icons\conference.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icons\delete.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icons\export_list_to_server.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icons\export_list_to_txt_file.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icons\gg.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icons\image.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icons\import_list_from_server.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icons\import_list_from_txt_file.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icons\list.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icons\next.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icons\previous.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icons\remove_list_from_server.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\resource.rc
+# End Source File
+# Begin Source File
+
+SOURCE=.\icons\save.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icons\sessions.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\icons\settings.ico
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE=".\docs\build-howto.txt"
+# End Source File
+# Begin Source File
+
+SOURCE=".\docs\gg-license.txt"
+# End Source File
+# Begin Source File
+
+SOURCE=".\docs\gg-readme.txt"
+# End Source File
+# Begin Source File
+
+SOURCE=".\docs\gg-translation-sample.txt"
+# End Source File
+# End Target
+# End Project
diff --git a/protocols/Gadu-Gadu/Gadu-Gadu.dsw b/protocols/Gadu-Gadu/Gadu-Gadu.dsw
new file mode 100644
index 0000000000..95a01d8270
--- /dev/null
+++ b/protocols/Gadu-Gadu/Gadu-Gadu.dsw
@@ -0,0 +1,29 @@
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Gadu-Gadu"=".\Gadu-Gadu.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/protocols/Gadu-Gadu/Gadu-Gadu.mak b/protocols/Gadu-Gadu/Gadu-Gadu.mak
new file mode 100644
index 0000000000..00d0d64716
--- /dev/null
+++ b/protocols/Gadu-Gadu/Gadu-Gadu.mak
@@ -0,0 +1,728 @@
+# Microsoft Developer Studio Generated NMAKE File, Based on Gadu-Gadu.dsp
+!IF "$(CFG)" == ""
+CFG=GG - Win32 Release
+!MESSAGE No configuration specified. Defaulting to GG - Win32 Release.
+!ENDIF
+
+!IF "$(CFG)" != "GG - Win32 Release" && "$(CFG)" != "GG - Win32 Debug"
+!MESSAGE Invalid configuration "$(CFG)" specified.
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE
+!MESSAGE NMAKE /f "Gadu-Gadu.mak" CFG="GG - Win32 Release"
+!MESSAGE
+!MESSAGE Possible choices for configuration are:
+!MESSAGE
+!MESSAGE "GG - Win32 Release" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE "GG - Win32 Debug" (based on "Win32 (x86) Dynamic-Link Library")
+!MESSAGE
+!ERROR An invalid configuration is specified.
+!ENDIF
+
+!IF "$(OS)" == "Windows_NT"
+NULL=
+!ELSE
+NULL=nul
+!ENDIF
+
+!IF "$(CFG)" == "GG - Win32 Release"
+
+OUTDIR=.\Release
+INTDIR=.\Release
+# Begin Custom Macros
+OutDir=.\Release
+# End Custom Macros
+
+ALL : "..\..\bin\release\plugins\GG.dll" "$(OUTDIR)\Gadu-Gadu.pch"
+
+
+CLEAN :
+ -@erase "$(INTDIR)\avatar.obj"
+ -@erase "$(INTDIR)\common.obj"
+ -@erase "$(INTDIR)\core.obj"
+ -@erase "$(INTDIR)\dcc.obj"
+ -@erase "$(INTDIR)\dcc7.obj"
+ -@erase "$(INTDIR)\dialogs.obj"
+ -@erase "$(INTDIR)\dynstuff.obj"
+ -@erase "$(INTDIR)\events.obj"
+ -@erase "$(INTDIR)\filetransfer.obj"
+ -@erase "$(INTDIR)\Gadu-Gadu.pch"
+ -@erase "$(INTDIR)\gg.obj"
+ -@erase "$(INTDIR)\groupchat.obj"
+ -@erase "$(INTDIR)\http.obj"
+ -@erase "$(INTDIR)\icolib.obj"
+ -@erase "$(INTDIR)\image.obj"
+ -@erase "$(INTDIR)\import.obj"
+ -@erase "$(INTDIR)\keepalive.obj"
+ -@erase "$(INTDIR)\libgadu.obj"
+ -@erase "$(INTDIR)\links.obj"
+ -@erase "$(INTDIR)\oauth.obj"
+ -@erase "$(INTDIR)\ownerinfo.obj"
+ -@erase "$(INTDIR)\popups.obj"
+ -@erase "$(INTDIR)\pthread.obj"
+ -@erase "$(INTDIR)\pubdir.obj"
+ -@erase "$(INTDIR)\pubdir50.obj"
+ -@erase "$(INTDIR)\resolver.obj"
+ -@erase "$(INTDIR)\resource.res"
+ -@erase "$(INTDIR)\services.obj"
+ -@erase "$(INTDIR)\sessions.obj"
+ -@erase "$(INTDIR)\sha1.obj"
+ -@erase "$(INTDIR)\token.obj"
+ -@erase "$(INTDIR)\userutils.obj"
+ -@erase "$(INTDIR)\vc60.idb"
+ -@erase "$(INTDIR)\vc60.pdb"
+ -@erase "$(INTDIR)\win32.obj"
+ -@erase "$(OUTDIR)\GG.exp"
+ -@erase "$(OUTDIR)\GG.lib"
+ -@erase "$(OUTDIR)\GG.map"
+ -@erase "$(OUTDIR)\GG.pdb"
+ -@erase "..\..\bin\release\plugins\GG.dll"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MD /W3 /GX /Zi /O1 /I "../../include" /I "libgadu" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fp"$(INTDIR)\Gadu-Gadu.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "NDEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\resource.res" /i "../../include" /d "NDEBUG"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\Gadu-Gadu.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib version.lib /nologo /base:"0x32500000" /dll /incremental:no /pdb:"$(OUTDIR)\GG.pdb" /map:"$(INTDIR)\GG.map" /debug /machine:I386 /out:"../../bin/release/plugins/GG.dll" /implib:"$(OUTDIR)\GG.lib" /ALIGN:4096 /ignore:4108
+LINK32_OBJS= \
+ "$(INTDIR)\common.obj" \
+ "$(INTDIR)\dcc.obj" \
+ "$(INTDIR)\dcc7.obj" \
+ "$(INTDIR)\events.obj" \
+ "$(INTDIR)\http.obj" \
+ "$(INTDIR)\libgadu.obj" \
+ "$(INTDIR)\pthread.obj" \
+ "$(INTDIR)\pubdir.obj" \
+ "$(INTDIR)\pubdir50.obj" \
+ "$(INTDIR)\resolver.obj" \
+ "$(INTDIR)\sha1.obj" \
+ "$(INTDIR)\win32.obj" \
+ "$(INTDIR)\avatar.obj" \
+ "$(INTDIR)\core.obj" \
+ "$(INTDIR)\dialogs.obj" \
+ "$(INTDIR)\dynstuff.obj" \
+ "$(INTDIR)\filetransfer.obj" \
+ "$(INTDIR)\gg.obj" \
+ "$(INTDIR)\groupchat.obj" \
+ "$(INTDIR)\icolib.obj" \
+ "$(INTDIR)\image.obj" \
+ "$(INTDIR)\import.obj" \
+ "$(INTDIR)\keepalive.obj" \
+ "$(INTDIR)\links.obj" \
+ "$(INTDIR)\oauth.obj" \
+ "$(INTDIR)\ownerinfo.obj" \
+ "$(INTDIR)\popups.obj" \
+ "$(INTDIR)\services.obj" \
+ "$(INTDIR)\sessions.obj" \
+ "$(INTDIR)\token.obj" \
+ "$(INTDIR)\userutils.obj" \
+ "$(INTDIR)\resource.res"
+
+"..\..\bin\release\plugins\GG.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ELSEIF "$(CFG)" == "GG - Win32 Debug"
+
+OUTDIR=.\Debug
+INTDIR=.\Debug
+# Begin Custom Macros
+OutDir=.\Debug
+# End Custom Macros
+
+ALL : "..\..\bin\debug\plugins\GG.dll" "$(OUTDIR)\Gadu-Gadu.pch"
+
+
+CLEAN :
+ -@erase "$(INTDIR)\avatar.obj"
+ -@erase "$(INTDIR)\common.obj"
+ -@erase "$(INTDIR)\core.obj"
+ -@erase "$(INTDIR)\dcc.obj"
+ -@erase "$(INTDIR)\dcc7.obj"
+ -@erase "$(INTDIR)\dialogs.obj"
+ -@erase "$(INTDIR)\dynstuff.obj"
+ -@erase "$(INTDIR)\events.obj"
+ -@erase "$(INTDIR)\filetransfer.obj"
+ -@erase "$(INTDIR)\Gadu-Gadu.pch"
+ -@erase "$(INTDIR)\gg.obj"
+ -@erase "$(INTDIR)\groupchat.obj"
+ -@erase "$(INTDIR)\http.obj"
+ -@erase "$(INTDIR)\icolib.obj"
+ -@erase "$(INTDIR)\image.obj"
+ -@erase "$(INTDIR)\import.obj"
+ -@erase "$(INTDIR)\keepalive.obj"
+ -@erase "$(INTDIR)\libgadu.obj"
+ -@erase "$(INTDIR)\links.obj"
+ -@erase "$(INTDIR)\oauth.obj"
+ -@erase "$(INTDIR)\ownerinfo.obj"
+ -@erase "$(INTDIR)\popups.obj"
+ -@erase "$(INTDIR)\pthread.obj"
+ -@erase "$(INTDIR)\pubdir.obj"
+ -@erase "$(INTDIR)\pubdir50.obj"
+ -@erase "$(INTDIR)\resolver.obj"
+ -@erase "$(INTDIR)\resource.res"
+ -@erase "$(INTDIR)\services.obj"
+ -@erase "$(INTDIR)\sessions.obj"
+ -@erase "$(INTDIR)\sha1.obj"
+ -@erase "$(INTDIR)\token.obj"
+ -@erase "$(INTDIR)\userutils.obj"
+ -@erase "$(INTDIR)\vc60.idb"
+ -@erase "$(INTDIR)\vc60.pdb"
+ -@erase "$(INTDIR)\win32.obj"
+ -@erase "$(OUTDIR)\GG.exp"
+ -@erase "$(OUTDIR)\GG.lib"
+ -@erase "$(OUTDIR)\GG.map"
+ -@erase "$(OUTDIR)\GG.pdb"
+ -@erase "..\..\bin\debug\plugins\GG.dll"
+ -@erase "..\..\bin\debug\plugins\GG.ilk"
+
+"$(OUTDIR)" :
+ if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
+
+CPP=cl.exe
+CPP_PROJ=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "libgadu" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fp"$(INTDIR)\Gadu-Gadu.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+
+.c{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.obj::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.c{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cpp{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+.cxx{$(INTDIR)}.sbr::
+ $(CPP) @<<
+ $(CPP_PROJ) $<
+<<
+
+MTL=midl.exe
+MTL_PROJ=/nologo /D "_DEBUG" /mktyplib203 /win32
+RSC=rc.exe
+RSC_PROJ=/l 0x409 /fo"$(INTDIR)\resource.res" /i "../../include" /d "_DEBUG"
+BSC32=bscmake.exe
+BSC32_FLAGS=/nologo /o"$(OUTDIR)\Gadu-Gadu.bsc"
+BSC32_SBRS= \
+
+LINK32=link.exe
+LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib version.lib /nologo /base:"0x32500000" /dll /incremental:yes /pdb:"$(OUTDIR)\GG.pdb" /map:"$(INTDIR)\GG.map" /debug /machine:I386 /out:"../../bin/debug/plugins/GG.dll" /implib:"$(OUTDIR)\GG.lib" /pdbtype:sept
+LINK32_OBJS= \
+ "$(INTDIR)\common.obj" \
+ "$(INTDIR)\dcc.obj" \
+ "$(INTDIR)\dcc7.obj" \
+ "$(INTDIR)\events.obj" \
+ "$(INTDIR)\http.obj" \
+ "$(INTDIR)\libgadu.obj" \
+ "$(INTDIR)\pthread.obj" \
+ "$(INTDIR)\pubdir.obj" \
+ "$(INTDIR)\pubdir50.obj" \
+ "$(INTDIR)\resolver.obj" \
+ "$(INTDIR)\sha1.obj" \
+ "$(INTDIR)\win32.obj" \
+ "$(INTDIR)\avatar.obj" \
+ "$(INTDIR)\core.obj" \
+ "$(INTDIR)\dialogs.obj" \
+ "$(INTDIR)\dynstuff.obj" \
+ "$(INTDIR)\filetransfer.obj" \
+ "$(INTDIR)\gg.obj" \
+ "$(INTDIR)\groupchat.obj" \
+ "$(INTDIR)\icolib.obj" \
+ "$(INTDIR)\image.obj" \
+ "$(INTDIR)\import.obj" \
+ "$(INTDIR)\keepalive.obj" \
+ "$(INTDIR)\links.obj" \
+ "$(INTDIR)\oauth.obj" \
+ "$(INTDIR)\ownerinfo.obj" \
+ "$(INTDIR)\popups.obj" \
+ "$(INTDIR)\services.obj" \
+ "$(INTDIR)\sessions.obj" \
+ "$(INTDIR)\token.obj" \
+ "$(INTDIR)\userutils.obj" \
+ "$(INTDIR)\resource.res"
+
+"..\..\bin\debug\plugins\GG.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
+ $(LINK32) @<<
+ $(LINK32_FLAGS) $(LINK32_OBJS)
+<<
+
+!ENDIF
+
+
+!IF "$(NO_EXTERNAL_DEPS)" != "1"
+!IF EXISTS("Gadu-Gadu.dep")
+!INCLUDE "Gadu-Gadu.dep"
+!ELSE
+!MESSAGE Warning: cannot find "Gadu-Gadu.dep"
+!ENDIF
+!ENDIF
+
+
+!IF "$(CFG)" == "GG - Win32 Release" || "$(CFG)" == "GG - Win32 Debug"
+SOURCE=.\libgadu\common.c
+
+!IF "$(CFG)" == "GG - Win32 Release"
+
+CPP_SWITCHES=/nologo /MD /W3 /GX /Zi /O1 /I "../../include" /I "libgadu" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+"$(INTDIR)\common.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ELSEIF "$(CFG)" == "GG - Win32 Debug"
+
+CPP_SWITCHES=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "libgadu" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+
+"$(INTDIR)\common.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ENDIF
+
+SOURCE=.\libgadu\dcc.c
+
+!IF "$(CFG)" == "GG - Win32 Release"
+
+CPP_SWITCHES=/nologo /MD /W3 /GX /Zi /O1 /I "../../include" /I "libgadu" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+"$(INTDIR)\dcc.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ELSEIF "$(CFG)" == "GG - Win32 Debug"
+
+CPP_SWITCHES=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "libgadu" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+
+"$(INTDIR)\dcc.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ENDIF
+
+SOURCE=.\libgadu\dcc7.c
+
+!IF "$(CFG)" == "GG - Win32 Release"
+
+CPP_SWITCHES=/nologo /MD /W3 /GX /Zi /O1 /I "../../include" /I "libgadu" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+"$(INTDIR)\dcc7.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ELSEIF "$(CFG)" == "GG - Win32 Debug"
+
+CPP_SWITCHES=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "libgadu" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+
+"$(INTDIR)\dcc7.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ENDIF
+
+SOURCE=.\libgadu\events.c
+
+!IF "$(CFG)" == "GG - Win32 Release"
+
+CPP_SWITCHES=/nologo /MD /W3 /GX /Zi /O1 /I "../../include" /I "libgadu" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+"$(INTDIR)\events.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ELSEIF "$(CFG)" == "GG - Win32 Debug"
+
+CPP_SWITCHES=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "libgadu" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+
+"$(INTDIR)\events.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ENDIF
+
+SOURCE=.\libgadu\http.c
+
+!IF "$(CFG)" == "GG - Win32 Release"
+
+CPP_SWITCHES=/nologo /MD /W3 /GX /Zi /O1 /I "../../include" /I "libgadu" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+"$(INTDIR)\http.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ELSEIF "$(CFG)" == "GG - Win32 Debug"
+
+CPP_SWITCHES=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "libgadu" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+
+"$(INTDIR)\http.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ENDIF
+
+SOURCE=.\libgadu\libgadu.c
+
+!IF "$(CFG)" == "GG - Win32 Release"
+
+CPP_SWITCHES=/nologo /MD /W3 /GX /Zi /O1 /I "../../include" /I "libgadu" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+"$(INTDIR)\libgadu.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ELSEIF "$(CFG)" == "GG - Win32 Debug"
+
+CPP_SWITCHES=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "libgadu" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+
+"$(INTDIR)\libgadu.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ENDIF
+
+SOURCE=.\libgadu\pthread.c
+
+!IF "$(CFG)" == "GG - Win32 Release"
+
+CPP_SWITCHES=/nologo /MD /W3 /GX /Zi /O1 /I "../../include" /I "libgadu" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+"$(INTDIR)\pthread.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ELSEIF "$(CFG)" == "GG - Win32 Debug"
+
+CPP_SWITCHES=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "libgadu" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+
+"$(INTDIR)\pthread.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ENDIF
+
+SOURCE=.\libgadu\pubdir.c
+
+!IF "$(CFG)" == "GG - Win32 Release"
+
+CPP_SWITCHES=/nologo /MD /W3 /GX /Zi /O1 /I "../../include" /I "libgadu" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+"$(INTDIR)\pubdir.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ELSEIF "$(CFG)" == "GG - Win32 Debug"
+
+CPP_SWITCHES=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "libgadu" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+
+"$(INTDIR)\pubdir.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ENDIF
+
+SOURCE=.\libgadu\pubdir50.c
+
+!IF "$(CFG)" == "GG - Win32 Release"
+
+CPP_SWITCHES=/nologo /MD /W3 /GX /Zi /O1 /I "../../include" /I "libgadu" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+"$(INTDIR)\pubdir50.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ELSEIF "$(CFG)" == "GG - Win32 Debug"
+
+CPP_SWITCHES=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "libgadu" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+
+"$(INTDIR)\pubdir50.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ENDIF
+
+SOURCE=.\libgadu\resolver.c
+
+!IF "$(CFG)" == "GG - Win32 Release"
+
+CPP_SWITCHES=/nologo /MD /W3 /GX /Zi /O1 /I "../../include" /I "libgadu" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+"$(INTDIR)\resolver.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ELSEIF "$(CFG)" == "GG - Win32 Debug"
+
+CPP_SWITCHES=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "libgadu" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+
+"$(INTDIR)\resolver.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ENDIF
+
+SOURCE=.\libgadu\sha1.c
+
+!IF "$(CFG)" == "GG - Win32 Release"
+
+CPP_SWITCHES=/nologo /MD /W3 /GX /Zi /O1 /I "../../include" /I "libgadu" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+"$(INTDIR)\sha1.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ELSEIF "$(CFG)" == "GG - Win32 Debug"
+
+CPP_SWITCHES=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "libgadu" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+
+"$(INTDIR)\sha1.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ENDIF
+
+SOURCE=.\libgadu\win32.c
+
+!IF "$(CFG)" == "GG - Win32 Release"
+
+CPP_SWITCHES=/nologo /MD /W3 /GX /Zi /O1 /I "../../include" /I "libgadu" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+"$(INTDIR)\win32.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ELSEIF "$(CFG)" == "GG - Win32 Debug"
+
+CPP_SWITCHES=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "libgadu" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+
+"$(INTDIR)\win32.obj" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ENDIF
+
+SOURCE=.\avatar.c
+
+"$(INTDIR)\avatar.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\core.c
+
+"$(INTDIR)\core.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\dialogs.c
+
+"$(INTDIR)\dialogs.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\dynstuff.c
+
+"$(INTDIR)\dynstuff.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\filetransfer.c
+
+"$(INTDIR)\filetransfer.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\gg.c
+
+!IF "$(CFG)" == "GG - Win32 Release"
+
+CPP_SWITCHES=/nologo /MD /W3 /GX /Zi /O1 /I "../../include" /I "libgadu" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fp"$(INTDIR)\Gadu-Gadu.pch" /Yc"gg.h" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c
+
+"$(INTDIR)\gg.obj" "$(INTDIR)\Gadu-Gadu.pch" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ELSEIF "$(CFG)" == "GG - Win32 Debug"
+
+CPP_SWITCHES=/nologo /MDd /W3 /Gm /GX /ZI /Od /I "../../include" /I "libgadu" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "GG_EXPORTS" /FAcs /Fa"$(INTDIR)\\" /Fp"$(INTDIR)\Gadu-Gadu.pch" /Yc"gg.h" /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c
+
+"$(INTDIR)\gg.obj" "$(INTDIR)\Gadu-Gadu.pch" : $(SOURCE) "$(INTDIR)"
+ $(CPP) @<<
+ $(CPP_SWITCHES) $(SOURCE)
+<<
+
+
+!ENDIF
+
+SOURCE=.\groupchat.c
+
+"$(INTDIR)\groupchat.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\icolib.c
+
+"$(INTDIR)\icolib.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\image.c
+
+"$(INTDIR)\image.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\import.c
+
+"$(INTDIR)\import.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\keepalive.c
+
+"$(INTDIR)\keepalive.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\links.c
+
+"$(INTDIR)\links.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\oauth.c
+
+"$(INTDIR)\oauth.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\ownerinfo.c
+
+"$(INTDIR)\ownerinfo.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\popups.c
+
+"$(INTDIR)\popups.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\services.c
+
+"$(INTDIR)\services.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\sessions.c
+
+"$(INTDIR)\sessions.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\token.c
+
+"$(INTDIR)\token.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\userutils.c
+
+"$(INTDIR)\userutils.obj" : $(SOURCE) "$(INTDIR)"
+
+
+SOURCE=.\resource.rc
+
+"$(INTDIR)\resource.res" : $(SOURCE) "$(INTDIR)"
+ $(RSC) $(RSC_PROJ) $(SOURCE)
+
+
+
+!ENDIF
+
diff --git a/protocols/Gadu-Gadu/Gadu-Gadu.vcproj b/protocols/Gadu-Gadu/Gadu-Gadu.vcproj
new file mode 100644
index 0000000000..d1b763fb26
--- /dev/null
+++ b/protocols/Gadu-Gadu/Gadu-Gadu.vcproj
@@ -0,0 +1,1623 @@
+<?xml version="1.0" encoding="windows-1251"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="7.10"
+ Name="GG"
+ ProjectGUID="{9F77E06C-47DD-4F07-AD5A-AB1DF6E19060}"
+ SccProjectName=""
+ SccLocalPath="">
+ <Platforms>
+ <Platform
+ Name="Win32"/>
+ </Platforms>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,libgadu"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;GG_EXPORTS"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderFile=".\Debug/Gadu-Gadu.pch"
+ AssemblerOutput="2"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="4"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wsock32.lib version.lib"
+ OutputFile="../../bin/debug/plugins/GG.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile=".\Debug/GG.pdb"
+ GenerateMapFile="TRUE"
+ MapFileName=".\Debug/GG.map"
+ BaseAddress="0x32500000"
+ ImportLibrary=".\Debug/GG.lib"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="TRUE"
+ SuppressStartupBanner="TRUE"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/Gadu-Gadu.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ GlobalOptimizations="TRUE"
+ InlineFunctionExpansion="1"
+ FavorSizeOrSpeed="2"
+ AdditionalIncludeDirectories="../../include,libgadu"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;GG_EXPORTS"
+ StringPooling="TRUE"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="TRUE"
+ UsePrecompiledHeader="2"
+ AssemblerOutput="2"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="3"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ IgnoreImportLibrary="TRUE"
+ AdditionalOptions="/ALIGN:4096 /ignore:4108"
+ AdditionalDependencies="vc7to6.lib wsock32.lib version.lib"
+ OutputFile="$(OutDir)/GG.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ AdditionalLibraryDirectories="../../lib"
+ IgnoreAllDefaultLibraries="TRUE"
+ GenerateDebugInformation="TRUE"
+ GenerateMapFile="TRUE"
+ BaseAddress="0x32500000"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="TRUE"
+ SuppressStartupBanner="TRUE"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/Gadu-Gadu.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Debug Unicode|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,libgadu"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;GG_EXPORTS"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderFile=".\Debug/Gadu-Gadu.pch"
+ AssemblerOutput="2"
+ AssemblerListingLocation=".\Debug/"
+ ObjectFile=".\Debug/"
+ ProgramDataBaseFileName=".\Debug/"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="4"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wsock32.lib version.lib"
+ OutputFile="../../bin/debug/plugins/GG.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile=".\Debug/GG.pdb"
+ GenerateMapFile="TRUE"
+ MapFileName=".\Debug/GG.map"
+ BaseAddress="0x32500000"
+ ImportLibrary=".\Debug/GG.lib"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="TRUE"
+ SuppressStartupBanner="TRUE"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/Gadu-Gadu.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ <Configuration
+ Name="Release Unicode|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="FALSE"
+ CharacterSet="2"
+ WholeProgramOptimization="FALSE">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="3"
+ GlobalOptimizations="TRUE"
+ InlineFunctionExpansion="1"
+ FavorSizeOrSpeed="2"
+ OmitFramePointers="TRUE"
+ AdditionalIncludeDirectories="../../include,libgadu"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;GG_EXPORTS"
+ StringPooling="TRUE"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="TRUE"
+ UsePrecompiledHeader="2"
+ AssemblerOutput="2"
+ WarningLevel="3"
+ SuppressStartupBanner="TRUE"
+ DebugInformationFormat="3"
+ CompileAs="0"/>
+ <Tool
+ Name="VCCustomBuildTool"/>
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /ignore:4108"
+ AdditionalDependencies="vc7to6.lib wsock32.lib version.lib"
+ LinkIncremental="1"
+ SuppressStartupBanner="TRUE"
+ AdditionalLibraryDirectories="../../lib"
+ IgnoreAllDefaultLibraries="TRUE"
+ GenerateDebugInformation="TRUE"
+ ProgramDatabaseFile=".\Release/GG.pdb"
+ GenerateMapFile="TRUE"
+ MapFileName=".\Release/GG.map"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ OptimizeForWindows98="1"
+ BaseAddress="0x32500000"
+ ImportLibrary=".\Release/GG.lib"
+ TargetMachine="1"/>
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="TRUE"
+ SuppressStartupBanner="TRUE"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/Gadu-Gadu.tlb"
+ HeaderFileName=""/>
+ <Tool
+ Name="VCPostBuildEventTool"/>
+ <Tool
+ Name="VCPreBuildEventTool"/>
+ <Tool
+ Name="VCPreLinkEventTool"/>
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"/>
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"/>
+ <Tool
+ Name="VCXMLDataGeneratorTool"/>
+ <Tool
+ Name="VCWebDeploymentTool"/>
+ <Tool
+ Name="VCManagedWrapperGeneratorTool"/>
+ <Tool
+ Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat">
+ <File
+ RelativePath="avatar.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="core.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="dialogs.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="dynstuff.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="filetransfer.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="gg.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="1"
+ PrecompiledHeaderThrough="gg.h"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="1"
+ PrecompiledHeaderThrough="gg.h"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="1"
+ PrecompiledHeaderThrough="gg.h"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="1"
+ PrecompiledHeaderThrough="gg.h"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="groupchat.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="icolib.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="image.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="import.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="keepalive.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="links.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="oauth.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="ownerinfo.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="popups.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="services.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="sessions.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="token.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="userutils.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"/>
+ </FileConfiguration>
+ </File>
+ <Filter
+ Name="libgadu"
+ Filter="">
+ <File
+ RelativePath="libgadu\common.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\compat.h">
+ </File>
+ <File
+ RelativePath="libgadu\dcc.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\dcc7.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\events.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\http.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\internal.h">
+ </File>
+ <File
+ RelativePath="libgadu\libgadu.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\libgadu.h">
+ </File>
+ <File
+ RelativePath="libgadu\protocol.h">
+ </File>
+ <File
+ RelativePath="libgadu\pthread.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\pthread.h">
+ </File>
+ <File
+ RelativePath="libgadu\pubdir.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\pubdir50.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\resolver.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\resolver.h">
+ </File>
+ <File
+ RelativePath="libgadu\sha1.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\win32.c">
+ <FileConfiguration
+ Name="Debug|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ BasicRuntimeChecks="3"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32">
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="1"
+ AdditionalIncludeDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_MBCS;_USRDLL;GG_EXPORTS;$(NoInherit)"
+ UsePrecompiledHeader="0"/>
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\win32.h">
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl">
+ <File
+ RelativePath="dynstuff.h">
+ </File>
+ <File
+ RelativePath="gg.h">
+ </File>
+ <File
+ RelativePath="resource.h">
+ </File>
+ <File
+ RelativePath="version.h">
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe">
+ <File
+ RelativePath="icons\block.ico">
+ </File>
+ <File
+ RelativePath="icons\clear_ignored_conference.ico">
+ </File>
+ <File
+ RelativePath="icons\conference.ico">
+ </File>
+ <File
+ RelativePath="icons\delete.ico">
+ </File>
+ <File
+ RelativePath="icons\export_list_to_server.ico">
+ </File>
+ <File
+ RelativePath="icons\export_list_to_txt_file.ico">
+ </File>
+ <File
+ RelativePath="icons\gg.ico">
+ </File>
+ <File
+ RelativePath="icons\image.ico">
+ </File>
+ <File
+ RelativePath="icons\import_list_from_server.ico">
+ </File>
+ <File
+ RelativePath="icons\import_list_from_txt_file.ico">
+ </File>
+ <File
+ RelativePath="icons\list.ico">
+ </File>
+ <File
+ RelativePath="icons\next.ico">
+ </File>
+ <File
+ RelativePath="icons\previous.ico">
+ </File>
+ <File
+ RelativePath="icons\remove_list_from_server.ico">
+ </File>
+ <File
+ RelativePath="resource.rc">
+ </File>
+ <File
+ RelativePath="icons\save.ico">
+ </File>
+ <File
+ RelativePath="icons\sessions.ico">
+ </File>
+ <File
+ RelativePath="icons\settings.ico">
+ </File>
+ </Filter>
+ <File
+ RelativePath="docs\build-howto.txt">
+ </File>
+ <File
+ RelativePath="docs\gg-license.txt">
+ </File>
+ <File
+ RelativePath="docs\gg-readme.txt">
+ </File>
+ <File
+ RelativePath="docs\gg-translation-sample.txt">
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj b/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj
new file mode 100644
index 0000000000..8d8128ae5b
--- /dev/null
+++ b/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj
@@ -0,0 +1,934 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup Label="ProjectConfigurations">
+ <ProjectConfiguration Include="Debug Unicode|Win32">
+ <Configuration>Debug Unicode</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug Unicode|x64">
+ <Configuration>Debug Unicode</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|Win32">
+ <Configuration>Debug</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release Unicode|Win32">
+ <Configuration>Release Unicode</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release Unicode|x64">
+ <Configuration>Release Unicode</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectName>GG</ProjectName>
+ <ProjectGuid>{6DA1396F-2A44-4D5C-8442-012F71006217}</ProjectGuid>
+ <RootNamespace>GG</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>DynamicLibrary</ConfigurationType>
+ <UseOfMfc>false</UseOfMfc>
+ <CharacterSet>MultiByte</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+ </ImportGroup>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup>
+ <_ProjectFileVersion>10.0.30128.1</_ProjectFileVersion>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)/Obj/$(ProjectName)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionDir)$(Configuration)64/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionDir)$(Configuration)64/Obj/$(ProjectName)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)/Obj/$(ProjectName)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)$(Configuration)64/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)$(Configuration)64/Obj/$(ProjectName)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">$(SolutionDir)$(Configuration)/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">$(SolutionDir)$(Configuration)/Obj/$(ProjectName)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">false</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">$(SolutionDir)$(Configuration)64/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">$(SolutionDir)$(Configuration)64/Obj/$(ProjectName)\</IntDir>
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">false</LinkIncremental>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">$(SolutionDir)$(Configuration)/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">$(SolutionDir)$(Configuration)/Obj/$(ProjectName)\</IntDir>
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">$(SolutionDir)$(Configuration)64/Plugins\</OutDir>
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">$(SolutionDir)$(Configuration)64/Obj/$(ProjectName)\</IntDir>
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" />
+ <CodeAnalysisRuleSet Condition="'$(Configuration)|$(Platform)'=='Release|x64'">AllRules.ruleset</CodeAnalysisRuleSet>
+ <CodeAnalysisRules Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+ <CodeAnalysisRuleAssemblies Condition="'$(Configuration)|$(Platform)'=='Release|x64'" />
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\Debug/Gadu-Gadu.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../../include;libgadu;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;GG_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>gg.h</PrecompiledHeaderFile>
+ <AssemblerListingLocation>
+ </AssemblerListingLocation>
+ <BrowseInformationFile>
+ </BrowseInformationFile>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>Default</CompileAs>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ <AdditionalIncludeDirectories>./../../include/msapi/</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>wsock32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>
+ </MapFileName>
+ <BaseAddress>0x32500000</BaseAddress>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <DataExecutionPrevention>
+ </DataExecutionPrevention>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <Midl>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>X64</TargetEnvironment>
+ <TypeLibraryName>.\Debug/Gadu-Gadu.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../../include;libgadu;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;GG_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <PrecompiledHeaderFile>gg.h</PrecompiledHeaderFile>
+ <AssemblerListingLocation>
+ </AssemblerListingLocation>
+ <BrowseInformationFile>
+ </BrowseInformationFile>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>Default</CompileAs>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ <AdditionalIncludeDirectories>./../../include/msapi/</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>wsock32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>
+ </MapFileName>
+ <BaseAddress>0x32500000</BaseAddress>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <DataExecutionPrevention>
+ </DataExecutionPrevention>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX64</TargetMachine>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\Release/Gadu-Gadu.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Full</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <AdditionalIncludeDirectories>../../include;libgadu;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;GG_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>gg.h</PrecompiledHeaderFile>
+ <AssemblerListingLocation>
+ </AssemblerListingLocation>
+ <BrowseInformationFile>
+ </BrowseInformationFile>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>Default</CompileAs>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ <AdditionalIncludeDirectories>./../../include/msapi/</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalOptions>/ALIGN:4096 /ignore:4108 %(AdditionalOptions)</AdditionalOptions>
+ <AdditionalDependencies>wsock32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>
+ </MapFileName>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
+ <BaseAddress>0x32500000</BaseAddress>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <DataExecutionPrevention>
+ </DataExecutionPrevention>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>X64</TargetEnvironment>
+ <TypeLibraryName>.\Release/Gadu-Gadu.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Full</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <AdditionalIncludeDirectories>../../include;libgadu;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;GG_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <PrecompiledHeaderFile>gg.h</PrecompiledHeaderFile>
+ <AssemblerListingLocation>
+ </AssemblerListingLocation>
+ <BrowseInformationFile>
+ </BrowseInformationFile>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>Default</CompileAs>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ <AdditionalIncludeDirectories>./../../include/msapi/</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalOptions>/ALIGN:4096 /ignore:4108 %(AdditionalOptions)</AdditionalOptions>
+ <AdditionalDependencies>wsock32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>
+ </MapFileName>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
+ <BaseAddress>0x32500000</BaseAddress>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <DataExecutionPrevention>
+ </DataExecutionPrevention>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX64</TargetMachine>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\Debug/Gadu-Gadu.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../../include;libgadu;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;GG_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>false</StringPooling>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>gg.h</PrecompiledHeaderFile>
+ <AssemblerListingLocation>
+ </AssemblerListingLocation>
+ <BrowseInformationFile>
+ </BrowseInformationFile>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>Default</CompileAs>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ <AdditionalIncludeDirectories>./../../include/msapi/</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>wsock32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(TargetDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>
+ </MapFileName>
+ <BaseAddress>0x32500000</BaseAddress>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <DataExecutionPrevention>
+ </DataExecutionPrevention>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">
+ <Midl>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>X64</TargetEnvironment>
+ <TypeLibraryName>.\Debug/Gadu-Gadu.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Disabled</Optimization>
+ <AdditionalIncludeDirectories>../../include;libgadu;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;GG_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>false</StringPooling>
+ <MinimalRebuild>true</MinimalRebuild>
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <FloatingPointModel>Precise</FloatingPointModel>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <PrecompiledHeaderFile>gg.h</PrecompiledHeaderFile>
+ <AssemblerListingLocation>
+ </AssemblerListingLocation>
+ <BrowseInformationFile>
+ </BrowseInformationFile>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>Default</CompileAs>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ <AdditionalIncludeDirectories>./../../include/msapi/</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalDependencies>wsock32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(TargetDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>
+ </MapFileName>
+ <BaseAddress>0x32500000</BaseAddress>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <DataExecutionPrevention>
+ </DataExecutionPrevention>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX64</TargetMachine>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>Win32</TargetEnvironment>
+ <TypeLibraryName>.\Release/Gadu-Gadu.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Full</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <AdditionalIncludeDirectories>../../include;libgadu;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;GG_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <PrecompiledHeaderFile>gg.h</PrecompiledHeaderFile>
+ <AssemblerListingLocation>
+ </AssemblerListingLocation>
+ <BrowseInformationFile>
+ </BrowseInformationFile>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>Default</CompileAs>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ <AdditionalIncludeDirectories>./../../include/msapi/</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalOptions>/ALIGN:4096 /ignore:4108 %(AdditionalOptions)</AdditionalOptions>
+ <AdditionalDependencies>wsock32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(TargetDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>
+ </MapFileName>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
+ <BaseAddress>0x32500000</BaseAddress>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <DataExecutionPrevention>
+ </DataExecutionPrevention>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX86</TargetMachine>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">
+ <Midl>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <MkTypLibCompatible>true</MkTypLibCompatible>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <TargetEnvironment>X64</TargetEnvironment>
+ <TypeLibraryName>.\Release/Gadu-Gadu.tlb</TypeLibraryName>
+ <HeaderFileName>
+ </HeaderFileName>
+ </Midl>
+ <ClCompile>
+ <Optimization>Full</Optimization>
+ <InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ <FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <AdditionalIncludeDirectories>../../include;libgadu;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+ <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;GG_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <StringPooling>true</StringPooling>
+ <ExceptionHandling>
+ </ExceptionHandling>
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
+ <BufferSecurityCheck>false</BufferSecurityCheck>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <FloatingPointModel>Fast</FloatingPointModel>
+ <RuntimeTypeInfo>false</RuntimeTypeInfo>
+ <PrecompiledHeader>
+ </PrecompiledHeader>
+ <PrecompiledHeaderFile>gg.h</PrecompiledHeaderFile>
+ <AssemblerListingLocation>
+ </AssemblerListingLocation>
+ <BrowseInformationFile>
+ </BrowseInformationFile>
+ <WarningLevel>Level3</WarningLevel>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
+ <CompileAs>Default</CompileAs>
+ <DisableSpecificWarnings>4996;%(DisableSpecificWarnings)</DisableSpecificWarnings>
+ </ClCompile>
+ <ResourceCompile>
+ <PreprocessorDefinitions>NDEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <Culture>0x0409</Culture>
+ <AdditionalIncludeDirectories>./../../include/msapi/</AdditionalIncludeDirectories>
+ </ResourceCompile>
+ <Link>
+ <AdditionalOptions>/ALIGN:4096 /ignore:4108 %(AdditionalOptions)</AdditionalOptions>
+ <AdditionalDependencies>wsock32.lib;version.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ProgramDatabaseFile>$(TargetDir)$(TargetName).pdb</ProgramDatabaseFile>
+ <GenerateMapFile>false</GenerateMapFile>
+ <MapFileName>
+ </MapFileName>
+ <OptimizeReferences>true</OptimizeReferences>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <LinkTimeCodeGeneration>UseLinkTimeCodeGeneration</LinkTimeCodeGeneration>
+ <BaseAddress>0x32500000</BaseAddress>
+ <RandomizedBaseAddress>false</RandomizedBaseAddress>
+ <DataExecutionPrevention>
+ </DataExecutionPrevention>
+ <ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
+ <TargetMachine>MachineX64</TargetMachine>
+ </Link>
+ <Bscmake>
+ <SuppressStartupBanner>true</SuppressStartupBanner>
+ </Bscmake>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="avatar.c" />
+ <ClCompile Include="core.c" />
+ <ClCompile Include="dialogs.c" />
+ <ClCompile Include="dynstuff.c" />
+ <ClCompile Include="filetransfer.c" />
+ <ClCompile Include="gg.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="groupchat.c" />
+ <ClCompile Include="icolib.c" />
+ <ClCompile Include="image.c" />
+ <ClCompile Include="import.c" />
+ <ClCompile Include="keepalive.c" />
+ <ClCompile Include="links.c" />
+ <ClCompile Include="oauth.c" />
+ <ClCompile Include="ownerinfo.c" />
+ <ClCompile Include="popups.c" />
+ <ClCompile Include="services.c" />
+ <ClCompile Include="sessions.c" />
+ <ClCompile Include="token.c" />
+ <ClCompile Include="userutils.c" />
+ <ClCompile Include="libgadu\common.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="libgadu\dcc.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="libgadu\dcc7.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="libgadu\events.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="libgadu\http.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="libgadu\libgadu.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="libgadu\pthread.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="libgadu\pubdir.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="libgadu\pubdir50.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="libgadu\resolver.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="libgadu\sha1.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ <ClCompile Include="libgadu\win32.c">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release Unicode|x64'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ </PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ </PrecompiledHeader>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="libgadu\compat.h" />
+ <ClInclude Include="libgadu\internal.h" />
+ <ClInclude Include="libgadu\libgadu.h" />
+ <ClInclude Include="libgadu\protocol.h" />
+ <ClInclude Include="libgadu\pthread.h" />
+ <ClInclude Include="libgadu\resolver.h" />
+ <ClInclude Include="libgadu\win32.h" />
+ <ClInclude Include="dynstuff.h" />
+ <ClInclude Include="gg.h" />
+ <ClInclude Include="resource.h" />
+ <ClInclude Include="version.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="icons\block.ico" />
+ <None Include="icons\clear_ignored_conference.ico" />
+ <None Include="icons\conference.ico" />
+ <None Include="icons\delete.ico" />
+ <None Include="icons\export_list_to_server.ico" />
+ <None Include="icons\export_list_to_txt_file.ico" />
+ <None Include="icons\gg.ico" />
+ <None Include="icons\image.ico" />
+ <None Include="icons\import_list_from_server.ico" />
+ <None Include="icons\import_list_from_txt_file.ico" />
+ <None Include="icons\list.ico" />
+ <None Include="icons\next.ico" />
+ <None Include="icons\previous.ico" />
+ <None Include="icons\remove_list_from_server.ico" />
+ <None Include="icons\save.ico" />
+ <None Include="icons\sessions.ico" />
+ <None Include="icons\settings.ico" />
+ <None Include="docs\build-howto.txt" />
+ <None Include="docs\gg-license.txt" />
+ <None Include="docs\gg-readme.txt" />
+ <None Include="docs\gg-translation-sample.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj.filters b/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj.filters
new file mode 100644
index 0000000000..762ede137b
--- /dev/null
+++ b/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj.filters
@@ -0,0 +1,212 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <Filter Include="Source Files">
+ <UniqueIdentifier>{3653ac07-1494-483a-8db4-4d335a086e79}</UniqueIdentifier>
+ <Extensions>cpp;c;cxx;rc;def;r;odl;idl;hpj;bat</Extensions>
+ </Filter>
+ <Filter Include="Source Files\libgadu">
+ <UniqueIdentifier>{f35667b1-a47c-4c87-b4aa-edb42e11629e}</UniqueIdentifier>
+ </Filter>
+ <Filter Include="Header Files">
+ <UniqueIdentifier>{9d7b4bf9-8289-4ead-a2ad-294b4c5f1e13}</UniqueIdentifier>
+ <Extensions>h;hpp;hxx;hm;inl</Extensions>
+ </Filter>
+ <Filter Include="Resource Files">
+ <UniqueIdentifier>{8a0835f2-408c-40e6-bd62-75d4e24cae16}</UniqueIdentifier>
+ <Extensions>ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe</Extensions>
+ </Filter>
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="avatar.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="core.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="dialogs.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="dynstuff.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="filetransfer.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="gg.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="groupchat.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="icolib.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="image.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="import.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="keepalive.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="links.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="oauth.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="ownerinfo.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="popups.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="services.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="sessions.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="token.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="userutils.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ <ClCompile Include="libgadu\common.c">
+ <Filter>Source Files\libgadu</Filter>
+ </ClCompile>
+ <ClCompile Include="libgadu\dcc.c">
+ <Filter>Source Files\libgadu</Filter>
+ </ClCompile>
+ <ClCompile Include="libgadu\dcc7.c">
+ <Filter>Source Files\libgadu</Filter>
+ </ClCompile>
+ <ClCompile Include="libgadu\events.c">
+ <Filter>Source Files\libgadu</Filter>
+ </ClCompile>
+ <ClCompile Include="libgadu\http.c">
+ <Filter>Source Files\libgadu</Filter>
+ </ClCompile>
+ <ClCompile Include="libgadu\libgadu.c">
+ <Filter>Source Files\libgadu</Filter>
+ </ClCompile>
+ <ClCompile Include="libgadu\pthread.c">
+ <Filter>Source Files\libgadu</Filter>
+ </ClCompile>
+ <ClCompile Include="libgadu\pubdir.c">
+ <Filter>Source Files\libgadu</Filter>
+ </ClCompile>
+ <ClCompile Include="libgadu\pubdir50.c">
+ <Filter>Source Files\libgadu</Filter>
+ </ClCompile>
+ <ClCompile Include="libgadu\resolver.c">
+ <Filter>Source Files\libgadu</Filter>
+ </ClCompile>
+ <ClCompile Include="libgadu\sha1.c">
+ <Filter>Source Files\libgadu</Filter>
+ </ClCompile>
+ <ClCompile Include="libgadu\win32.c">
+ <Filter>Source Files\libgadu</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="libgadu\compat.h">
+ <Filter>Source Files\libgadu</Filter>
+ </ClInclude>
+ <ClInclude Include="libgadu\internal.h">
+ <Filter>Source Files\libgadu</Filter>
+ </ClInclude>
+ <ClInclude Include="libgadu\libgadu.h">
+ <Filter>Source Files\libgadu</Filter>
+ </ClInclude>
+ <ClInclude Include="libgadu\protocol.h">
+ <Filter>Source Files\libgadu</Filter>
+ </ClInclude>
+ <ClInclude Include="libgadu\pthread.h">
+ <Filter>Source Files\libgadu</Filter>
+ </ClInclude>
+ <ClInclude Include="libgadu\resolver.h">
+ <Filter>Source Files\libgadu</Filter>
+ </ClInclude>
+ <ClInclude Include="libgadu\win32.h">
+ <Filter>Source Files\libgadu</Filter>
+ </ClInclude>
+ <ClInclude Include="dynstuff.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="gg.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ <ClInclude Include="version.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="icons\block.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="icons\clear_ignored_conference.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="icons\conference.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="icons\delete.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="icons\export_list_to_server.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="icons\export_list_to_txt_file.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="icons\gg.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="icons\image.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="icons\import_list_from_server.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="icons\import_list_from_txt_file.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="icons\list.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="icons\next.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="icons\previous.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="icons\remove_list_from_server.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="icons\save.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="icons\sessions.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="icons\settings.ico">
+ <Filter>Resource Files</Filter>
+ </None>
+ <None Include="docs\build-howto.txt" />
+ <None Include="docs\gg-license.txt" />
+ <None Include="docs\gg-readme.txt" />
+ <None Include="docs\gg-translation-sample.txt" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="resource.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/protocols/Gadu-Gadu/Gadu-Gadu_8.vcproj b/protocols/Gadu-Gadu/Gadu-Gadu_8.vcproj
new file mode 100644
index 0000000000..0ec4c7b068
--- /dev/null
+++ b/protocols/Gadu-Gadu/Gadu-Gadu_8.vcproj
@@ -0,0 +1,1126 @@
+<?xml version="1.0" encoding="windows-1250"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8,00"
+ Name="GG"
+ ProjectGUID="{6DA1396F-2A44-4D5C-8442-012F71006217}"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/Gadu-Gadu.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,libgadu"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;GG_EXPORTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="gg.h"
+ AssemblerListingLocation=""
+ BrowseInformationFile=""
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ CompileAs="0"
+ DisableSpecificWarnings="4996"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wsock32.lib version.lib"
+ OutputFile="$(OutDir)/GG.dll"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ GenerateMapFile="false"
+ MapFileName=""
+ BaseAddress="0x32500000"
+ ImportLibrary=".\Debug/GG.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/Gadu-Gadu.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="3"
+ InlineFunctionExpansion="2"
+ FavorSizeOrSpeed="2"
+ OmitFramePointers="true"
+ WholeProgramOptimization="true"
+ AdditionalIncludeDirectories="../../include,libgadu"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;GG_EXPORTS"
+ StringPooling="true"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="gg.h"
+ AssemblerListingLocation=""
+ BrowseInformationFile=""
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="3"
+ CompileAs="0"
+ DisableSpecificWarnings="4996"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /ignore:4108"
+ AdditionalDependencies="wsock32.lib version.lib"
+ OutputFile="$(OutDir)/GG.dll"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ GenerateMapFile="false"
+ MapFileName=""
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ LinkTimeCodeGeneration="1"
+ BaseAddress="0x32500000"
+ ImportLibrary=".\Release/GG.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug Unicode|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/Gadu-Gadu.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,libgadu"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;GG_EXPORTS"
+ StringPooling="false"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ EnableFunctionLevelLinking="true"
+ FloatingPointModel="0"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="gg.h"
+ AssemblerListingLocation=""
+ BrowseInformationFile=""
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="4"
+ CompileAs="0"
+ DisableSpecificWarnings="4996"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wsock32.lib version.lib"
+ OutputFile="$(OutDir)/GG.dll"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(TargetDir)$(TargetName).pdb"
+ GenerateMapFile="false"
+ MapFileName=""
+ BaseAddress="0x32500000"
+ ImportLibrary=".\Debug/GG.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release Unicode|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/Gadu-Gadu.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="3"
+ InlineFunctionExpansion="2"
+ FavorSizeOrSpeed="2"
+ OmitFramePointers="true"
+ WholeProgramOptimization="true"
+ AdditionalIncludeDirectories="../../include,libgadu"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;GG_EXPORTS"
+ StringPooling="true"
+ RuntimeLibrary="2"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="gg.h"
+ AssemblerListingLocation=""
+ BrowseInformationFile=""
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="3"
+ CompileAs="0"
+ DisableSpecificWarnings="4996"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /ignore:4108"
+ AdditionalDependencies="wsock32.lib version.lib"
+ OutputFile="$(OutDir)/GG.dll"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(TargetDir)$(TargetName).pdb"
+ GenerateMapFile="false"
+ MapFileName=""
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ LinkTimeCodeGeneration="1"
+ BaseAddress="0x32500000"
+ ImportLibrary=".\Release/GG.lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ <File
+ RelativePath="avatar.c"
+ >
+ </File>
+ <File
+ RelativePath="core.c"
+ >
+ </File>
+ <File
+ RelativePath="dialogs.c"
+ >
+ </File>
+ <File
+ RelativePath="dynstuff.c"
+ >
+ </File>
+ <File
+ RelativePath="filetransfer.c"
+ >
+ </File>
+ <File
+ RelativePath="gg.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="groupchat.c"
+ >
+ </File>
+ <File
+ RelativePath=".\icolib.c"
+ >
+ </File>
+ <File
+ RelativePath="image.c"
+ >
+ </File>
+ <File
+ RelativePath="import.c"
+ >
+ </File>
+ <File
+ RelativePath="keepalive.c"
+ >
+ </File>
+ <File
+ RelativePath="links.c"
+ >
+ </File>
+ <File
+ RelativePath="oauth.c"
+ >
+ </File>
+ <File
+ RelativePath="ownerinfo.c"
+ >
+ </File>
+ <File
+ RelativePath="popups.c"
+ >
+ </File>
+ <File
+ RelativePath="services.c"
+ >
+ </File>
+ <File
+ RelativePath="sessions.c"
+ >
+ </File>
+ <File
+ RelativePath="token.c"
+ >
+ </File>
+ <File
+ RelativePath="userutils.c"
+ >
+ </File>
+ <Filter
+ Name="libgadu"
+ >
+ <File
+ RelativePath="libgadu\common.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\compat.h"
+ >
+ </File>
+ <File
+ RelativePath="libgadu\dcc.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\dcc7.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\events.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\http.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\internal.h"
+ >
+ </File>
+ <File
+ RelativePath="libgadu\libgadu.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\libgadu.h"
+ >
+ </File>
+ <File
+ RelativePath="libgadu\protocol.h"
+ >
+ </File>
+ <File
+ RelativePath="libgadu\pthread.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\pthread.h"
+ >
+ </File>
+ <File
+ RelativePath="libgadu\pubdir.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\pubdir50.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\resolver.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\resolver.h"
+ >
+ </File>
+ <File
+ RelativePath="libgadu\sha1.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\win32.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\win32.h"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ <File
+ RelativePath="dynstuff.h"
+ >
+ </File>
+ <File
+ RelativePath="gg.h"
+ >
+ </File>
+ <File
+ RelativePath="resource.h"
+ >
+ </File>
+ <File
+ RelativePath="version.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+ >
+ <File
+ RelativePath="icons\block.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\clear_ignored_conference.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\conference.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\delete.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\export_list_to_server.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\export_list_to_txt_file.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\gg.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\image.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\import_list_from_server.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\import_list_from_txt_file.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\list.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\next.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\previous.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\remove_list_from_server.ico"
+ >
+ </File>
+ <File
+ RelativePath="resource.rc"
+ >
+ </File>
+ <File
+ RelativePath="icons\save.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\sessions.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\settings.ico"
+ >
+ </File>
+ </Filter>
+ <File
+ RelativePath="docs\build-howto.txt"
+ >
+ </File>
+ <File
+ RelativePath="docs\gg-license.txt"
+ >
+ </File>
+ <File
+ RelativePath="docs\gg-readme.txt"
+ >
+ </File>
+ <File
+ RelativePath="docs\gg-translation-sample.txt"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/protocols/Gadu-Gadu/Gadu-Gadu_9.vcproj b/protocols/Gadu-Gadu/Gadu-Gadu_9.vcproj
new file mode 100644
index 0000000000..f3d4b1a902
--- /dev/null
+++ b/protocols/Gadu-Gadu/Gadu-Gadu_9.vcproj
@@ -0,0 +1,1971 @@
+<?xml version="1.0" encoding="windows-1250"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9.00"
+ Name="GG"
+ ProjectGUID="{6DA1396F-2A44-4D5C-8442-012F71006217}"
+ RootNamespace="GG"
+ TargetFrameworkVersion="131072"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ <Platform
+ Name="x64"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/Gadu-Gadu.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,libgadu"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;GG_EXPORTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="gg.h"
+ AssemblerListingLocation=""
+ BrowseInformationFile=""
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="3"
+ CompileAs="0"
+ DisableSpecificWarnings="4996"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wsock32.lib version.lib"
+ OutputFile="$(OutDir)/GG.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ GenerateMapFile="false"
+ MapFileName=""
+ BaseAddress="0x32500000"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary="$(IntDir)/$(TargetName).lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug|x64"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)64/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)64/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="3"
+ TypeLibraryName=".\Debug/Gadu-Gadu.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,libgadu"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;GG_EXPORTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="0"
+ PrecompiledHeaderThrough="gg.h"
+ AssemblerListingLocation=""
+ BrowseInformationFile=""
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="3"
+ CompileAs="0"
+ DisableSpecificWarnings="4996"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wsock32.lib version.lib"
+ OutputFile="$(OutDir)/GG.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ GenerateMapFile="false"
+ MapFileName=""
+ BaseAddress="0x32500000"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary="$(IntDir)/$(TargetName).lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/Gadu-Gadu.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="3"
+ InlineFunctionExpansion="2"
+ EnableIntrinsicFunctions="true"
+ FavorSizeOrSpeed="2"
+ WholeProgramOptimization="true"
+ AdditionalIncludeDirectories="../../include,libgadu"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;GG_EXPORTS"
+ StringPooling="true"
+ ExceptionHandling="0"
+ RuntimeLibrary="2"
+ BufferSecurityCheck="false"
+ EnableFunctionLevelLinking="true"
+ FloatingPointModel="2"
+ RuntimeTypeInfo="false"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="gg.h"
+ AssemblerListingLocation=""
+ BrowseInformationFile=""
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="3"
+ CompileAs="0"
+ DisableSpecificWarnings="4996"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /ignore:4108"
+ AdditionalDependencies="wsock32.lib version.lib"
+ OutputFile="$(OutDir)/GG.dll"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ GenerateMapFile="false"
+ MapFileName=""
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ LinkTimeCodeGeneration="1"
+ BaseAddress="0x32500000"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary="$(IntDir)/$(TargetName).lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|x64"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)64/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)64/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="3"
+ TypeLibraryName=".\Release/Gadu-Gadu.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="3"
+ InlineFunctionExpansion="2"
+ EnableIntrinsicFunctions="true"
+ FavorSizeOrSpeed="2"
+ WholeProgramOptimization="true"
+ AdditionalIncludeDirectories="../../include,libgadu"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;GG_EXPORTS"
+ StringPooling="true"
+ ExceptionHandling="0"
+ RuntimeLibrary="2"
+ BufferSecurityCheck="false"
+ EnableFunctionLevelLinking="true"
+ FloatingPointModel="2"
+ RuntimeTypeInfo="false"
+ UsePrecompiledHeader="0"
+ PrecompiledHeaderThrough="gg.h"
+ AssemblerListingLocation=""
+ BrowseInformationFile=""
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="3"
+ CompileAs="0"
+ DisableSpecificWarnings="4996"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /ignore:4108"
+ AdditionalDependencies="wsock32.lib version.lib"
+ OutputFile="$(OutDir)/GG.dll"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ GenerateMapFile="false"
+ MapFileName=""
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ LinkTimeCodeGeneration="1"
+ BaseAddress="0x32500000"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary="$(IntDir)/$(TargetName).lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug Unicode|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Debug/Gadu-Gadu.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,libgadu"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;GG_EXPORTS"
+ StringPooling="false"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ EnableFunctionLevelLinking="true"
+ FloatingPointModel="0"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="gg.h"
+ AssemblerListingLocation=""
+ BrowseInformationFile=""
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="3"
+ CompileAs="0"
+ DisableSpecificWarnings="4996"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wsock32.lib version.lib"
+ OutputFile="$(OutDir)/GG.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(TargetDir)$(TargetName).pdb"
+ GenerateMapFile="false"
+ MapFileName=""
+ BaseAddress="0x32500000"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary="$(IntDir)/$(TargetName).lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Debug Unicode|x64"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)64/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)64/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="_DEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="3"
+ TypeLibraryName=".\Debug/Gadu-Gadu.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="../../include,libgadu"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;GG_EXPORTS"
+ StringPooling="false"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="3"
+ EnableFunctionLevelLinking="true"
+ FloatingPointModel="0"
+ UsePrecompiledHeader="0"
+ PrecompiledHeaderThrough="gg.h"
+ AssemblerListingLocation=""
+ BrowseInformationFile=""
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="3"
+ CompileAs="0"
+ DisableSpecificWarnings="4996"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="_DEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="wsock32.lib version.lib"
+ OutputFile="$(OutDir)/GG.dll"
+ LinkIncremental="1"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(TargetDir)$(TargetName).pdb"
+ GenerateMapFile="false"
+ MapFileName=""
+ BaseAddress="0x32500000"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary="$(IntDir)/$(TargetName).lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release Unicode|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="1"
+ TypeLibraryName=".\Release/Gadu-Gadu.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="3"
+ InlineFunctionExpansion="2"
+ EnableIntrinsicFunctions="true"
+ FavorSizeOrSpeed="2"
+ WholeProgramOptimization="true"
+ AdditionalIncludeDirectories="../../include,libgadu"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;GG_EXPORTS"
+ StringPooling="true"
+ ExceptionHandling="0"
+ RuntimeLibrary="2"
+ BufferSecurityCheck="false"
+ EnableFunctionLevelLinking="true"
+ FloatingPointModel="2"
+ RuntimeTypeInfo="false"
+ UsePrecompiledHeader="2"
+ PrecompiledHeaderThrough="gg.h"
+ AssemblerListingLocation=""
+ BrowseInformationFile=""
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="3"
+ CompileAs="0"
+ DisableSpecificWarnings="4996"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /ignore:4108"
+ AdditionalDependencies="wsock32.lib version.lib"
+ OutputFile="$(OutDir)/GG.dll"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(TargetDir)$(TargetName).pdb"
+ GenerateMapFile="false"
+ MapFileName=""
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ LinkTimeCodeGeneration="1"
+ BaseAddress="0x32500000"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary="$(IntDir)/$(TargetName).lib"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release Unicode|x64"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)64/Plugins"
+ IntermediateDirectory="$(SolutionDir)$(ConfigurationName)64/Obj/$(ProjectName)"
+ ConfigurationType="2"
+ UseOfMFC="0"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ CharacterSet="2"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ PreprocessorDefinitions="NDEBUG"
+ MkTypLibCompatible="true"
+ SuppressStartupBanner="true"
+ TargetEnvironment="3"
+ TypeLibraryName=".\Release/Gadu-Gadu.tlb"
+ HeaderFileName=""
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="3"
+ InlineFunctionExpansion="2"
+ EnableIntrinsicFunctions="true"
+ FavorSizeOrSpeed="2"
+ WholeProgramOptimization="true"
+ AdditionalIncludeDirectories="../../include,libgadu"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;GG_EXPORTS"
+ StringPooling="true"
+ ExceptionHandling="0"
+ RuntimeLibrary="2"
+ BufferSecurityCheck="false"
+ EnableFunctionLevelLinking="true"
+ FloatingPointModel="2"
+ RuntimeTypeInfo="false"
+ UsePrecompiledHeader="0"
+ PrecompiledHeaderThrough="gg.h"
+ AssemblerListingLocation=""
+ BrowseInformationFile=""
+ WarningLevel="3"
+ SuppressStartupBanner="true"
+ DebugInformationFormat="3"
+ CompileAs="0"
+ DisableSpecificWarnings="4996"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ PreprocessorDefinitions="NDEBUG"
+ Culture="1033"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalOptions="/ALIGN:4096 /ignore:4108"
+ AdditionalDependencies="wsock32.lib version.lib"
+ OutputFile="$(OutDir)/GG.dll"
+ SuppressStartupBanner="true"
+ GenerateDebugInformation="true"
+ ProgramDatabaseFile="$(TargetDir)$(TargetName).pdb"
+ GenerateMapFile="false"
+ MapFileName=""
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ LinkTimeCodeGeneration="1"
+ BaseAddress="0x32500000"
+ RandomizedBaseAddress="1"
+ DataExecutionPrevention="0"
+ ImportLibrary="$(IntDir)/$(TargetName).lib"
+ TargetMachine="17"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ SuppressStartupBanner="true"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
+ >
+ <File
+ RelativePath="avatar.c"
+ >
+ </File>
+ <File
+ RelativePath="core.c"
+ >
+ </File>
+ <File
+ RelativePath="dialogs.c"
+ >
+ </File>
+ <File
+ RelativePath="dynstuff.c"
+ >
+ </File>
+ <File
+ RelativePath="filetransfer.c"
+ >
+ </File>
+ <File
+ RelativePath="gg.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="groupchat.c"
+ >
+ </File>
+ <File
+ RelativePath="icolib.c"
+ >
+ </File>
+ <File
+ RelativePath="image.c"
+ >
+ </File>
+ <File
+ RelativePath="import.c"
+ >
+ </File>
+ <File
+ RelativePath="keepalive.c"
+ >
+ </File>
+ <File
+ RelativePath="links.c"
+ >
+ </File>
+ <File
+ RelativePath="oauth.c"
+ >
+ </File>
+ <File
+ RelativePath="ownerinfo.c"
+ >
+ </File>
+ <File
+ RelativePath="popups.c"
+ >
+ </File>
+ <File
+ RelativePath="services.c"
+ >
+ </File>
+ <File
+ RelativePath="sessions.c"
+ >
+ </File>
+ <File
+ RelativePath="token.c"
+ >
+ </File>
+ <File
+ RelativePath="userutils.c"
+ >
+ </File>
+ <Filter
+ Name="libgadu"
+ >
+ <File
+ RelativePath="libgadu\common.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\compat.h"
+ >
+ </File>
+ <File
+ RelativePath="libgadu\dcc.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\dcc7.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\events.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\http.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\internal.h"
+ >
+ </File>
+ <File
+ RelativePath="libgadu\libgadu.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\libgadu.h"
+ >
+ </File>
+ <File
+ RelativePath="libgadu\protocol.h"
+ >
+ </File>
+ <File
+ RelativePath="libgadu\pthread.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\pthread.h"
+ >
+ </File>
+ <File
+ RelativePath="libgadu\pubdir.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\pubdir50.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\resolver.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\resolver.h"
+ >
+ </File>
+ <File
+ RelativePath="libgadu\sha1.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\win32.c"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Debug Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release Unicode|x64"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="libgadu\win32.h"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl"
+ >
+ <File
+ RelativePath="dynstuff.h"
+ >
+ </File>
+ <File
+ RelativePath="gg.h"
+ >
+ </File>
+ <File
+ RelativePath="resource.h"
+ >
+ </File>
+ <File
+ RelativePath="version.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
+ >
+ <File
+ RelativePath="icons\block.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\clear_ignored_conference.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\conference.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\delete.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\export_list_to_server.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\export_list_to_txt_file.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\gg.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\image.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\import_list_from_server.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\import_list_from_txt_file.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\list.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\next.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\previous.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\remove_list_from_server.ico"
+ >
+ </File>
+ <File
+ RelativePath="resource.rc"
+ >
+ </File>
+ <File
+ RelativePath="icons\save.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\sessions.ico"
+ >
+ </File>
+ <File
+ RelativePath="icons\settings.ico"
+ >
+ </File>
+ </Filter>
+ <File
+ RelativePath="docs\build-howto.txt"
+ >
+ </File>
+ <File
+ RelativePath="docs\gg-license.txt"
+ >
+ </File>
+ <File
+ RelativePath="docs\gg-readme.txt"
+ >
+ </File>
+ <File
+ RelativePath="docs\gg-translation-sample.txt"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/protocols/Gadu-Gadu/avatar.c b/protocols/Gadu-Gadu/avatar.c
new file mode 100644
index 0000000000..a0d18e1eb3
--- /dev/null
+++ b/protocols/Gadu-Gadu/avatar.c
@@ -0,0 +1,461 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2009-2012 Bartosz Białek
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+#include <io.h>
+#include <fcntl.h>
+#include "protocol.h"
+
+//////////////////////////////////////////////////////////
+// Avatars support
+void gg_getavatarfilename(GGPROTO *gg, HANDLE hContact, char *pszDest, int cbLen)
+{
+ int tPathLen;
+ char *path = (char *)alloca(cbLen);
+ char *avatartype = NULL;
+
+ if (gg->hAvatarsFolder == NULL || FoldersGetCustomPath(gg->hAvatarsFolder, path, cbLen, "")) {
+ char *tmpPath = Utils_ReplaceVars("%miranda_avatarcache%");
+ tPathLen = mir_snprintf(pszDest, cbLen, "%s\\%s", tmpPath, GG_PROTO);
+ mir_free(tmpPath);
+ }
+ else {
+ strcpy(pszDest, path);
+ tPathLen = (int)strlen(pszDest);
+ }
+
+ if (_access(pszDest, 0))
+ CallService(MS_UTILS_CREATEDIRTREE, 0, (LPARAM)pszDest);
+
+ switch (DBGetContactSettingByte(hContact, GG_PROTO, GG_KEY_AVATARTYPE, GG_KEYDEF_AVATARTYPE)) {
+ case PA_FORMAT_JPEG: avatartype = "jpg"; break;
+ case PA_FORMAT_GIF: avatartype = "gif"; break;
+ case PA_FORMAT_PNG: avatartype = "png"; break;
+ }
+
+ if (hContact != NULL) {
+ DBVARIANT dbv;
+ if (!DBGetContactSettingString(hContact, GG_PROTO, GG_KEY_AVATARHASH, &dbv)) {
+ mir_snprintf(pszDest + tPathLen, cbLen - tPathLen, "\\%s.%s", dbv.pszVal, avatartype);
+ DBFreeVariant(&dbv);
+ }
+ }
+ else
+ mir_snprintf(pszDest + tPathLen, cbLen - tPathLen, "\\%s avatar.%s", GG_PROTO, avatartype);
+}
+
+void gg_getavatarfileinfo(GGPROTO *gg, uin_t uin, char **avatarurl, int *type)
+{
+ NETLIBHTTPREQUEST req = {0};
+ NETLIBHTTPREQUEST *resp;
+ char szUrl[128];
+ *avatarurl = NULL;
+ *type = PA_FORMAT_UNKNOWN;
+
+ req.cbSize = sizeof(req);
+ req.requestType = REQUEST_GET;
+ req.szUrl = szUrl;
+ mir_snprintf(szUrl, 128, "http://api.gadu-gadu.pl/avatars/%d/0.xml", uin);
+ req.flags = NLHRF_NODUMP | NLHRF_HTTP11 | NLHRF_REDIRECT;
+ resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)gg->netlib, (LPARAM)&req);
+ if (resp) {
+ if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) {
+ HXML hXml;
+ TCHAR *xmlAction;
+ TCHAR *tag;
+
+ xmlAction = gg_a2t(resp->pData);
+ tag = gg_a2t("result");
+ hXml = xi.parseString(xmlAction, 0, tag);
+
+ if (hXml != NULL) {
+ HXML node;
+ char *blank;
+
+ mir_free(tag); tag = gg_a2t("users/user/avatars/avatar");
+ node = xi.getChildByPath(hXml, tag, 0);
+ mir_free(tag); tag = gg_a2t("blank");
+ blank = node != NULL ? gg_t2a(xi.getAttrValue(node, tag)) : NULL;
+
+ if (blank != NULL && strcmp(blank, "1")) {
+ mir_free(tag); tag = gg_a2t("users/user/avatars/avatar/bigAvatar");
+ node = xi.getChildByPath(hXml, tag, 0);
+ *avatarurl = node != NULL ? gg_t2a(xi.getText(node)) : NULL;
+
+ mir_free(tag); tag = gg_a2t("users/user/avatars/avatar/originBigAvatar");
+ node = xi.getChildByPath(hXml, tag, 0);
+ if (node != NULL) {
+ char *orgavurl = gg_t2a(xi.getText(node));
+ char *avtype = strrchr(orgavurl, '.');
+ avtype++;
+ if (!_stricmp(avtype, "jpg"))
+ *type = PA_FORMAT_JPEG;
+ else if (!_stricmp(avtype, "gif"))
+ *type = PA_FORMAT_GIF;
+ else if (!_stricmp(avtype, "png"))
+ *type = PA_FORMAT_PNG;
+ mir_free(orgavurl);
+ }
+ }
+ else *avatarurl = mir_strdup("");
+ mir_free(blank);
+ xi.destroyNode(hXml);
+ }
+ mir_free(tag);
+ mir_free(xmlAction);
+ }
+ else gg_netlog(gg, "gg_getavatarfileinfo(): Invalid response code from HTTP request");
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp);
+ }
+ else gg_netlog(gg, "gg_getavatarfileinfo(): No response from HTTP request");
+}
+
+char *gg_avatarhash(char *param)
+{
+ mir_sha1_byte_t digest[MIR_SHA1_HASH_SIZE];
+ char *result;
+ int i;
+
+ if (param == NULL || (result = (char *)mir_alloc(MIR_SHA1_HASH_SIZE * 2 + 1)) == NULL)
+ return NULL;
+
+ mir_sha1_hash(param, (int)strlen(param), digest);
+ for (i = 0; i < MIR_SHA1_HASH_SIZE; i++)
+ sprintf(result + (i<<1), "%02x", digest[i]);
+
+ return result;
+}
+
+typedef struct
+{
+ HANDLE hContact;
+ char *AvatarURL;
+} GGGETAVATARDATA;
+
+void gg_getavatar(GGPROTO *gg, HANDLE hContact, char *szAvatarURL)
+{
+ if (gg->pth_avatar.dwThreadId) {
+ GGGETAVATARDATA *data = mir_alloc(sizeof(GGGETAVATARDATA));
+ data->hContact = hContact;
+ data->AvatarURL = mir_strdup(szAvatarURL);
+ EnterCriticalSection(&gg->avatar_mutex);
+ list_add(&gg->avatar_transfers, data, 0);
+ LeaveCriticalSection(&gg->avatar_mutex);
+ }
+}
+
+typedef struct
+{
+ HANDLE hContact;
+ int iWaitFor;
+} GGREQUESTAVATARDATA;
+
+void gg_requestavatar(GGPROTO *gg, HANDLE hContact, int iWaitFor)
+{
+ if (DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)
+ && gg->pth_avatar.dwThreadId) {
+ GGREQUESTAVATARDATA *data = mir_alloc(sizeof(GGREQUESTAVATARDATA));
+ data->hContact = hContact;
+ data->iWaitFor = iWaitFor;
+ EnterCriticalSection(&gg->avatar_mutex);
+ list_add(&gg->avatar_requests, data, 0);
+ LeaveCriticalSection(&gg->avatar_mutex);
+ }
+}
+
+void __cdecl gg_avatarrequestthread(GGPROTO *gg, void *empty)
+{
+ list_t l;
+
+ gg_netlog(gg, "gg_avatarrequestthread(): Avatar Request Thread Starting");
+ while (gg->pth_avatar.dwThreadId)
+ {
+ EnterCriticalSection(&gg->avatar_mutex);
+ if (gg->avatar_requests) {
+ GGREQUESTAVATARDATA *data = (GGREQUESTAVATARDATA *)gg->avatar_requests->data;
+ char *AvatarURL;
+ int AvatarType, iWaitFor = data->iWaitFor;
+ HANDLE hContact = data->hContact;
+
+ list_remove(&gg->avatar_requests, data, 0);
+ mir_free(data);
+ LeaveCriticalSection(&gg->avatar_mutex);
+
+ gg_getavatarfileinfo(gg, DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0), &AvatarURL, &AvatarType);
+ if (AvatarURL != NULL && strlen(AvatarURL) > 0)
+ DBWriteContactSettingString(hContact, GG_PROTO, GG_KEY_AVATARURL, AvatarURL);
+ else
+ DBDeleteContactSetting(hContact, GG_PROTO, GG_KEY_AVATARURL);
+ DBWriteContactSettingByte(hContact, GG_PROTO, GG_KEY_AVATARTYPE, (BYTE)AvatarType);
+ DBWriteContactSettingByte(hContact, GG_PROTO, GG_KEY_AVATARREQUESTED, 1);
+
+ if (iWaitFor) {
+ PROTO_AVATAR_INFORMATION pai = {0};
+ pai.cbSize = sizeof(pai);
+ pai.hContact = hContact;
+ if (gg_getavatarinfo(gg, (WPARAM)GAIF_FORCE, (LPARAM)&pai) != GAIR_WAITFOR)
+ ProtoBroadcastAck(GG_PROTO, hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, (HANDLE)&pai, 0);
+ }
+ else ProtoBroadcastAck(GG_PROTO, hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, 0, 0);
+ }
+ else LeaveCriticalSection(&gg->avatar_mutex);
+
+ EnterCriticalSection(&gg->avatar_mutex);
+ if (gg->avatar_transfers) {
+ GGGETAVATARDATA *data = (GGGETAVATARDATA *)gg->avatar_transfers->data;
+ NETLIBHTTPREQUEST req = {0};
+ NETLIBHTTPREQUEST *resp;
+ PROTO_AVATAR_INFORMATION pai = {0};
+ int result = 0;
+
+ pai.cbSize = sizeof(pai);
+ pai.hContact = data->hContact;
+ pai.format = DBGetContactSettingByte(pai.hContact, GG_PROTO, GG_KEY_AVATARTYPE, GG_KEYDEF_AVATARTYPE);
+
+ req.cbSize = sizeof(req);
+ req.requestType = REQUEST_GET;
+ req.szUrl = data->AvatarURL;
+ req.flags = NLHRF_NODUMP | NLHRF_HTTP11 | NLHRF_REDIRECT;
+ resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)gg->netlib, (LPARAM)&req);
+ if (resp) {
+ if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) {
+ int file_fd;
+
+ gg_getavatarfilename(gg, pai.hContact, pai.filename, sizeof(pai.filename));
+ file_fd = _open(pai.filename, _O_WRONLY | _O_TRUNC | _O_BINARY | _O_CREAT, _S_IREAD | _S_IWRITE);
+ if (file_fd != -1) {
+ _write(file_fd, resp->pData, resp->dataLength);
+ _close(file_fd);
+ result = 1;
+ }
+ }
+ else gg_netlog(gg, "gg_avatarrequestthread(): Invalid response code from HTTP request");
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp);
+ }
+ else gg_netlog(gg, "gg_avatarrequestthread(): No response from HTTP request");
+
+ ProtoBroadcastAck(GG_PROTO, pai.hContact, ACKTYPE_AVATAR,
+ result ? ACKRESULT_SUCCESS : ACKRESULT_FAILED, (HANDLE)&pai, 0);
+
+ if (!pai.hContact)
+ CallService(MS_AV_REPORTMYAVATARCHANGED, (WPARAM)GG_PROTO, 0);
+
+ list_remove(&gg->avatar_transfers, data, 0);
+ mir_free(data->AvatarURL);
+ mir_free(data);
+ }
+ LeaveCriticalSection(&gg->avatar_mutex);
+ SleepEx(100, FALSE);
+ }
+
+ for (l = gg->avatar_requests; l; l = l->next) {
+ GGREQUESTAVATARDATA *data = (GGREQUESTAVATARDATA *)l->data;
+ mir_free(data);
+ }
+ for (l = gg->avatar_transfers; l; l = l->next) {
+ GGGETAVATARDATA *data = (GGGETAVATARDATA *)l->data;
+ mir_free(data->AvatarURL);
+ mir_free(data);
+ }
+ list_destroy(gg->avatar_requests, 0);
+ list_destroy(gg->avatar_transfers, 0);
+ gg_netlog(gg, "gg_avatarrequestthread(): Avatar Request Thread Ending");
+}
+
+void gg_initavatarrequestthread(GGPROTO *gg)
+{
+ DWORD exitCode = 0;
+
+ GetExitCodeThread(gg->pth_avatar.hThread, &exitCode);
+ if (exitCode != STILL_ACTIVE) {
+ gg->avatar_requests = gg->avatar_transfers = NULL;
+ gg->pth_avatar.hThread = gg_forkthreadex(gg, gg_avatarrequestthread, NULL, &gg->pth_avatar.dwThreadId);
+ }
+}
+
+void gg_uninitavatarrequestthread(GGPROTO *gg)
+{
+ gg->pth_avatar.dwThreadId = 0;
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_uninitavatarrequestthread(): Waiting until Avatar Request Thread finished, if needed.");
+#endif
+ gg_threadwait(gg, &gg->pth_avatar);
+}
+
+void __cdecl gg_getuseravatarthread(GGPROTO *gg, void *empty)
+{
+ PROTO_AVATAR_INFORMATION pai = {0};
+ char *AvatarURL;
+ int AvatarType;
+
+ gg_getavatarfileinfo(gg, DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0), &AvatarURL, &AvatarType);
+ if (AvatarURL != NULL && strlen(AvatarURL) > 0)
+ DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_AVATARURL, AvatarURL);
+ else
+ DBDeleteContactSetting(NULL, GG_PROTO, GG_KEY_AVATARURL);
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_AVATARTYPE, (BYTE)AvatarType);
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_AVATARREQUESTED, 1);
+
+ pai.cbSize = sizeof(pai);
+ gg_getavatarinfo(gg, (WPARAM)GAIF_FORCE, (LPARAM)&pai);
+}
+
+void gg_getuseravatar(GGPROTO *gg)
+{
+ if (DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)
+ && DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0))
+ gg_forkthread(gg, gg_getuseravatarthread, NULL);
+}
+
+void __cdecl gg_setavatarthread(GGPROTO *gg, void *param)
+{
+ NETLIBHTTPHEADER httpHeaders[4];
+ NETLIBHTTPREQUEST req = {0};
+ NETLIBHTTPREQUEST *resp;
+ char *szFilename = (char *)param;
+ const char *contentend = "\r\n--AaB03x--\r\n";
+ char szUrl[128], uin[32], *authHeader, *data, *avatardata, content[256],
+ *fileext, image_ext[4], image_type[11];
+ int file_fd, avatardatalen, datalen, contentlen, contentendlen, res = 0, repeat = 0;
+
+ gg_netlog(gg, "gg_setavatar(): Trying to set user avatar using %s...", szFilename);
+ UIN2ID(DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0), uin);
+
+ file_fd = _open(szFilename, _O_RDONLY | _O_BINARY, _S_IREAD);
+ if (file_fd == -1) {
+ gg_netlog(gg, "gg_setavatar(): Failed to open avatar file (%s).", strerror(errno));
+ mir_free(szFilename);
+ gg_getuseravatar(gg);
+ return;
+ }
+ avatardatalen = _filelength(file_fd);
+ avatardata = (char *)mir_alloc(avatardatalen);
+
+ _read(file_fd, avatardata, avatardatalen);
+ _close(file_fd);
+
+ fileext = strrchr(szFilename, '.');
+ fileext++;
+ if (!_stricmp(fileext, "jpg")) {
+ strcpy(image_ext, "jpg");
+ strcpy(image_type, "image/jpeg");
+ }
+ else if (!_stricmp(fileext, "gif")) {
+ strcpy(image_ext, "gif");
+ strcpy(image_type, "image/gif");
+ }
+ else /*if (!_stricmp(fileext, "png"))*/ {
+ strcpy(image_ext, "png");
+ strcpy(image_type, "image/png");
+ }
+
+ mir_snprintf(content, 256, "--AaB03x\r\nContent-Disposition: form-data; name=\"_method\"\r\n\r\nPUT\r\n--AaB03x\r\nContent-Disposition: form-data; name=\"avatar\"; filename=\"%s.%s\"\r\nContent-Type: %s\r\n\r\n",
+ uin, image_ext, image_type);
+ contentlen = (int)strlen(content);
+ contentendlen = (int)strlen(contentend);
+
+ datalen = contentlen + avatardatalen + contentendlen;
+ data = (char *)mir_alloc(datalen);
+ memcpy(data, content, contentlen);
+ memcpy(data + contentlen, avatardata, avatardatalen);
+ memcpy(data + contentlen + avatardatalen, contentend, contentendlen);
+
+ mir_snprintf(szUrl, 128, "http://api.gadu-gadu.pl/avatars/%s/0.xml", uin);
+ gg_oauth_checktoken(gg, 0);
+ authHeader = gg_oauth_header(gg, "PUT", szUrl);
+
+ req.cbSize = sizeof(req);
+ req.requestType = REQUEST_POST;
+ req.szUrl = szUrl;
+ req.flags = NLHRF_NODUMP | NLHRF_HTTP11;
+ req.headersCount = 4;
+ req.headers = httpHeaders;
+ httpHeaders[0].szName = "User-Agent";
+ httpHeaders[0].szValue = GG8_VERSION;
+ httpHeaders[1].szName = "Authorization";
+ httpHeaders[1].szValue = authHeader;
+ httpHeaders[2].szName = "Accept";
+ httpHeaders[2].szValue = "*/*";
+ httpHeaders[3].szName = "Content-Type";
+ httpHeaders[3].szValue = "multipart/form-data; boundary=AaB03x";
+ req.pData = data;
+ req.dataLength = datalen;
+
+ resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)gg->netlib, (LPARAM)&req);
+ if (resp) {
+ if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) {
+#ifdef DEBUGMODE
+ gg_netlog(gg, "%s", resp->pData);
+#endif
+ res = 1;
+ }
+ else gg_netlog(gg, "gg_setavatar(): Invalid response code from HTTP request");
+ if (resp->resultCode == 403 || resp->resultCode == 401)
+ repeat = 1;
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp);
+ }
+ else gg_netlog(gg, "gg_setavatar(): No response from HTTP request");
+
+ if (repeat) { // Access Token expired - we need to obtain new
+ mir_free(authHeader);
+ gg_oauth_checktoken(gg, 1);
+ authHeader = gg_oauth_header(gg, "PUT", szUrl);
+
+ ZeroMemory(&req, sizeof(req));
+ req.cbSize = sizeof(req);
+ req.requestType = REQUEST_POST;
+ req.szUrl = szUrl;
+ req.flags = NLHRF_NODUMP | NLHRF_HTTP11;
+ req.headersCount = 4;
+ req.headers = httpHeaders;
+ req.pData = data;
+ req.dataLength = datalen;
+
+ resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)gg->netlib, (LPARAM)&req);
+ if (resp) {
+ if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) {
+#ifdef DEBUGMODE
+ gg_netlog(gg, "%s", resp->pData);
+#endif
+ res = 1;
+ }
+ else gg_netlog(gg, "gg_setavatar(): Invalid response code from HTTP request");
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp);
+ }
+ else gg_netlog(gg, "gg_setavatar(): No response from HTTP request");
+ }
+
+ mir_free(authHeader);
+ mir_free(avatardata);
+ mir_free(data);
+
+ if (res)
+ gg_netlog(gg, "gg_setavatar(): User avatar set successfully.");
+ else
+ gg_netlog(gg, "gg_setavatar(): Failed to set user avatar.");
+
+ mir_free(szFilename);
+ gg_getuseravatar(gg);
+}
+
+void gg_setavatar(GGPROTO *gg, const char *szFilename)
+{
+ gg_forkthread(gg, gg_setavatarthread, (void*)mir_strdup(szFilename));
+}
diff --git a/protocols/Gadu-Gadu/core.c b/protocols/Gadu-Gadu/core.c
new file mode 100644
index 0000000000..ea739c6dce
--- /dev/null
+++ b/protocols/Gadu-Gadu/core.c
@@ -0,0 +1,1841 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2009 Adam Strzelecki <ono+miranda@java.pl>
+// Copyright (c) 2009-2012 Bartosz Białek
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+#include <errno.h>
+#include <io.h>
+
+////////////////////////////////////////////////////////////
+// Swap bits in DWORD
+uint32_t swap32(uint32_t x)
+{
+ return (uint32_t)
+ (((x & (uint32_t) 0x000000ffU) << 24) |
+ ((x & (uint32_t) 0x0000ff00U) << 8) |
+ ((x & (uint32_t) 0x00ff0000U) >> 8) |
+ ((x & (uint32_t) 0xff000000U) >> 24));
+}
+
+////////////////////////////////////////////////////////////
+// Is online function
+GGINLINE int gg_isonline(GGPROTO *gg)
+{
+ int isonline;
+ EnterCriticalSection(&gg->sess_mutex);
+ isonline = (gg->sess != NULL);
+ LeaveCriticalSection(&gg->sess_mutex);
+
+ return isonline;
+}
+
+////////////////////////////////////////////////////////////
+// Send disconnect request and wait for server thread to die
+void gg_disconnect(GGPROTO *gg)
+{
+ // If main loop then send disconnect request
+ if (gg_isonline(gg))
+ {
+ // Fetch proper status msg
+ char *szMsg = NULL;
+
+ // Loadup status
+ if (DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_LEAVESTATUSMSG, GG_KEYDEF_LEAVESTATUSMSG))
+ {
+ DBVARIANT dbv;
+ switch (DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_LEAVESTATUS, GG_KEYDEF_LEAVESTATUS))
+ {
+ case ID_STATUS_ONLINE:
+ EnterCriticalSection(&gg->modemsg_mutex);
+ szMsg = mir_strdup(gg->modemsg.online);
+ LeaveCriticalSection(&gg->modemsg_mutex);
+ if (!szMsg &&
+ !DBGetContactSettingString(NULL, "SRAway", gg_status2db(ID_STATUS_ONLINE, "Default"), &dbv))
+ {
+ if (dbv.pszVal && *(dbv.pszVal))
+ szMsg = mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ break;
+ case ID_STATUS_AWAY:
+ EnterCriticalSection(&gg->modemsg_mutex);
+ szMsg = mir_strdup(gg->modemsg.away);
+ LeaveCriticalSection(&gg->modemsg_mutex);
+ if (!szMsg &&
+ !DBGetContactSettingString(NULL, "SRAway", gg_status2db(ID_STATUS_AWAY, "Default"), &dbv))
+ {
+ if (dbv.pszVal && *(dbv.pszVal))
+ szMsg = mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ break;
+ case ID_STATUS_DND:
+ EnterCriticalSection(&gg->modemsg_mutex);
+ szMsg = mir_strdup(gg->modemsg.dnd);
+ LeaveCriticalSection(&gg->modemsg_mutex);
+ if (!szMsg &&
+ !DBGetContactSettingString(NULL, "SRAway", gg_status2db(ID_STATUS_DND, "Default"), &dbv))
+ {
+ if (dbv.pszVal && *(dbv.pszVal))
+ szMsg = mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ break;
+ case ID_STATUS_FREECHAT:
+ EnterCriticalSection(&gg->modemsg_mutex);
+ szMsg = mir_strdup(gg->modemsg.freechat);
+ LeaveCriticalSection(&gg->modemsg_mutex);
+ if (!szMsg &&
+ !DBGetContactSettingString(NULL, "SRAway", gg_status2db(ID_STATUS_FREECHAT, "Default"), &dbv))
+ {
+ if (dbv.pszVal && *(dbv.pszVal))
+ szMsg = mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ break;
+ case ID_STATUS_INVISIBLE:
+ EnterCriticalSection(&gg->modemsg_mutex);
+ szMsg = mir_strdup(gg->modemsg.invisible);
+ LeaveCriticalSection(&gg->modemsg_mutex);
+ if (!szMsg &&
+ !DBGetContactSettingString(NULL, "SRAway", gg_status2db(ID_STATUS_INVISIBLE, "Default"), &dbv))
+ {
+ if (dbv.pszVal && *(dbv.pszVal))
+ szMsg = mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ break;
+ default:
+ // Set last status
+ EnterCriticalSection(&gg->modemsg_mutex);
+ szMsg = mir_strdup(gg_getstatusmsg(gg, gg->proto.m_iStatus));
+ LeaveCriticalSection(&gg->modemsg_mutex);
+ }
+ }
+
+ EnterCriticalSection(&gg->sess_mutex);
+ // Check if it has message
+ if (szMsg)
+ {
+ gg_change_status_descr(gg->sess, GG_STATUS_NOT_AVAIL_DESCR, szMsg);
+ mir_free(szMsg);
+ // Wait for disconnection acknowledge
+ }
+ else
+ {
+ gg_change_status(gg->sess, GG_STATUS_NOT_AVAIL);
+ // Send logoff immediately
+ gg_logoff(gg->sess);
+ }
+ LeaveCriticalSection(&gg->sess_mutex);
+ }
+ // Else cancel connection attempt
+ else if (gg->sock)
+ closesocket(gg->sock);
+}
+
+////////////////////////////////////////////////////////////
+// DNS lookup function
+uint32_t gg_dnslookup(GGPROTO *gg, char *host)
+{
+ uint32_t ip;
+ struct hostent *he;
+
+ ip = inet_addr(host);
+ if(ip != INADDR_NONE)
+ {
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_dnslookup(): Parameter \"%s\" is already IP number.", host);
+#endif
+ return ip;
+ }
+ he = gethostbyname(host);
+ if(he)
+ {
+ ip = *(uint32_t *) he->h_addr_list[0];
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_dnslookup(): Parameter \"%s\" was resolved to %d.%d.%d.%d.", host,
+ LOBYTE(LOWORD(ip)), HIBYTE(LOWORD(ip)), LOBYTE(HIWORD(ip)), HIBYTE(HIWORD(ip)));
+#endif
+ return ip;
+ }
+ gg_netlog(gg, "gg_dnslookup(): Cannot resolve hostname \"%s\".", host);
+ return 0;
+}
+
+////////////////////////////////////////////////////////////
+// Host list decoder
+typedef struct
+{
+ char hostname[128];
+ int port;
+} GGHOST;
+#define ISHOSTALPHA(a) (((a) >= '0' && (a) <= '9') || ((a) >= 'a' && (a) <= 'z') || (a) == '.' || (a) == '-')
+int gg_decodehosts(char *var, GGHOST *hosts, int max)
+{
+ int hp = 0;
+ char *hostname = NULL;
+ char *portname = NULL;
+
+ while(var && *var && hp < max)
+ {
+ if(ISHOSTALPHA(*var))
+ {
+ hostname = var;
+
+ while(var && *var && ISHOSTALPHA(*var)) var ++;
+
+ if(var && *var == ':' && var++ && *var && isdigit(*var))
+ {
+ *(var - 1) = 0;
+ portname = var;
+ while(var && *var && isdigit(*var)) var++;
+ if(*var) { *var = 0; var ++; }
+ }
+ else
+ if(*var) { *var = 0; var ++; }
+
+ // Insert new item
+ hosts[hp].hostname[127] = 0;
+ strncpy(hosts[hp].hostname, hostname, 127);
+ hosts[hp].port = portname ? atoi(portname) : 443;
+ hp ++;
+
+ // Zero the names
+ hostname = NULL;
+ portname = NULL;
+ }
+ else
+ var ++;
+ }
+ return hp;
+}
+
+////////////////////////////////////////////////////////////
+// Main connection session thread
+void __cdecl gg_mainthread(GGPROTO *gg, void *empty)
+{
+ // Miranda variables
+ NETLIBUSERSETTINGS nlus = {0};
+ DBVARIANT dbv;
+ // Gadu-Gadu variables
+ struct gg_login_params p = {0};
+ struct gg_event *e;
+ struct gg_session *sess;
+ // Host cycling variables
+ int hostnum = 0, hostcount = 0;
+ GGHOST hosts[64];
+ // Gadu-gadu login errors
+ static const struct tagReason { int type; char *str; } reason[] = {
+ { GG_FAILURE_RESOLVING, "Miranda was unable to resolve the name of the Gadu-Gadu server to its numeric address." },
+ { GG_FAILURE_CONNECTING, "Miranda was unable to make a connection with a server. It is likely that the server is down, in which case you should wait for a while and try again later." },
+ { GG_FAILURE_INVALID, "Received invalid server response." },
+ { GG_FAILURE_READING, "The connection with the server was abortively closed during the connection attempt. You may have lost your local network connection." },
+ { GG_FAILURE_WRITING, "The connection with the server was abortively closed during the connection attempt. You may have lost your local network connection." },
+ { GG_FAILURE_PASSWORD, "Your Gadu-Gadu number and password combination was rejected by the Gadu-Gadu server. Please check login details at M->Options->Network->Gadu-Gadu and try again." },
+ { GG_FAILURE_404, "Connecting to Gadu-Gadu hub failed." },
+ { GG_FAILURE_TLS, "Cannot establish secure connection." },
+ { GG_FAILURE_NEED_EMAIL, "Server disconnected asking you for changing your e-mail." },
+ { GG_FAILURE_INTRUDER, "Too many login attempts with invalid password." },
+ { GG_FAILURE_UNAVAILABLE, "Gadu-Gadu servers are now down. Try again later." },
+ { 0, "Unknown" }
+ };
+ time_t logonTime = 0;
+ time_t timeDeviation = DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_TIMEDEVIATION, GG_KEYDEF_TIMEDEVIATION);
+ int gg_failno = 0;
+
+ gg_netlog(gg, "gg_mainthread(%x): Server Thread Starting", gg);
+#ifdef DEBUGMODE
+ gg_debug_level = GG_DEBUG_NET | GG_DEBUG_TRAFFIC | GG_DEBUG_FUNCTION | GG_DEBUG_MISC;
+#else
+ gg_debug_level = 0;
+#endif
+
+ // Broadcast that service is connecting
+ gg_broadcastnewstatus(gg, ID_STATUS_CONNECTING);
+
+ // Client version and misc settings
+ p.client_version = GG_DEFAULT_CLIENT_VERSION;
+ p.protocol_version = GG_DEFAULT_PROTOCOL_VERSION;
+ p.protocol_features = GG_FEATURE_DND_FFC | GG_FEATURE_UNKNOWN_100 | GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK | GG_FEATURE_TYPING_NOTIFICATION | GG_FEATURE_MULTILOGON;
+ p.encoding = GG_ENCODING_CP1250;
+ p.status_flags = GG_STATUS_FLAG_UNKNOWN;
+ if (DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWLINKS, GG_KEYDEF_SHOWLINKS))
+ p.status_flags |= GG_STATUS_FLAG_SPAM;
+
+ // Use audio
+ /* p.has_audio = 1; */
+
+ // Use async connections
+ /* p.async = 1; */
+
+ // Send Era Omnix info if set
+ p.era_omnix = DBGetContactSettingByte(NULL, GG_PROTO, "EraOmnix", 0);
+
+ // Setup proxy
+ nlus.cbSize = sizeof(nlus);
+ if(CallService(MS_NETLIB_GETUSERSETTINGS, (WPARAM)gg->netlib, (LPARAM)&nlus))
+ {
+ if(nlus.useProxy)
+ gg_netlog(gg, "gg_mainthread(%x): Using proxy %s:%d.", gg, nlus.szProxyServer, nlus.wProxyPort);
+ gg_proxy_enabled = nlus.useProxy;
+ gg_proxy_host = nlus.szProxyServer;
+ gg_proxy_port = nlus.wProxyPort;
+ if(nlus.useProxyAuth)
+ {
+ gg_proxy_username = nlus.szProxyAuthUser;
+ gg_proxy_password = nlus.szProxyAuthPassword;
+ }
+ else
+ gg_proxy_username = gg_proxy_password = NULL;
+ }
+ else
+ {
+ gg_netlog(gg, "gg_mainthread(%x): Failed loading proxy settings.", gg);
+ gg_proxy_enabled = 0;
+ }
+
+ // Check out manual host setting
+ if(DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_MANUALHOST, GG_KEYDEF_MANUALHOST))
+ {
+ if(!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_SERVERHOSTS, &dbv))
+ {
+ hostcount = gg_decodehosts(dbv.pszVal, hosts, 64);
+ DBFreeVariant(&dbv);
+ }
+ }
+
+ // Readup password
+ if(!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv))
+ {
+ CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal);
+ p.password = mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else
+ {
+ gg_netlog(gg, "gg_mainthread(%x): No password specified. Exiting.", gg);
+ gg_broadcastnewstatus(gg, ID_STATUS_OFFLINE);
+ return;
+ }
+
+ // Readup number
+ if(!(p.uin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0)))
+ {
+ gg_netlog(gg, "gg_mainthread(%x): No Gadu-Gadu number specified. Exiting.", gg);
+ gg_broadcastnewstatus(gg, ID_STATUS_OFFLINE);
+ mir_free(p.password);
+ return;
+ }
+
+ // Readup SSL/TLS setting
+ if(p.tls = DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_SSLCONN, GG_KEYDEF_SSLCONN))
+ gg_netlog(gg, "gg_mainthread(%x): Using TLS/SSL for connections.", gg);
+
+ // Gadu-Gadu accepts image sizes upto 255
+ p.image_size = 255;
+
+ ////////////////////////////// DCC STARTUP /////////////////////////////
+ // Uin is ok so startup dcc if not started already
+ if(!gg->dcc)
+ {
+ gg->event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ gg_dccstart(gg);
+
+ // Wait for DCC
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_mainthread(%x): Waiting DCC service to start...", gg);
+#endif
+ while (WaitForSingleObjectEx(gg->event, INFINITE, TRUE) != WAIT_OBJECT_0);
+ CloseHandle(gg->event); gg->event = NULL;
+ }
+ // Check if dcc is running and setup forwarding port
+ if(gg->dcc && DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_FORWARDING, GG_KEYDEF_FORWARDING))
+ {
+ if(!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_FORWARDHOST, &dbv))
+ {
+ if(!(p.external_addr = gg_dnslookup(gg, dbv.pszVal)))
+ {
+ char error[128];
+ mir_snprintf(error, sizeof(error), Translate("External direct connections hostname %s is invalid. Disabling external host forwarding."), dbv.pszVal);
+ gg_showpopup(gg, GG_PROTONAME, error, GG_POPUP_WARNING | GG_POPUP_ALLOW_MSGBOX);
+ }
+ else
+ gg_netlog(gg, "gg_mainthread(%x): Loading forwarding host %s and port %d.", dbv.pszVal, p.external_port, gg);
+ if(p.external_addr) p.external_port = DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_FORWARDPORT, GG_KEYDEF_FORWARDPORT);
+ DBFreeVariant(&dbv);
+ }
+ }
+ // Setup client port
+ if(gg->dcc) p.client_port = gg->dcc->port;
+
+retry:
+ // Loadup startup status & description
+ EnterCriticalSection(&gg->modemsg_mutex);
+ p.status_descr = mir_strdup(gg_getstatusmsg(gg, gg->proto.m_iDesiredStatus));
+ p.status = status_m2gg(gg, gg->proto.m_iDesiredStatus, p.status_descr != NULL);
+
+ gg_netlog(gg, "gg_mainthread(%x): Connecting with number %d, status %d and description \"%s\".", gg, p.uin, gg->proto.m_iDesiredStatus,
+ p.status_descr ? p.status_descr : "<none>");
+ LeaveCriticalSection(&gg->modemsg_mutex);
+
+ // Check manual hosts
+ if(hostnum < hostcount)
+ {
+ if(!(p.server_addr = gg_dnslookup(gg, hosts[hostnum].hostname)))
+ {
+ char error[128];
+ mir_snprintf(error, sizeof(error), Translate("Server hostname %s is invalid. Using default hostname provided by the network."), hosts[hostnum].hostname);
+ gg_showpopup(gg, GG_PROTONAME, error, GG_POPUP_WARNING | GG_POPUP_ALLOW_MSGBOX);
+ }
+ else
+ {
+ p.server_port = hosts[hostnum].port;
+ gg_netlog(gg, "gg_mainthread(%x): Connecting to manually specified host %s (%d.%d.%d.%d) and port %d.", gg,
+ hosts[hostnum].hostname, LOBYTE(LOWORD(p.server_addr)), HIBYTE(LOWORD(p.server_addr)),
+ LOBYTE(HIWORD(p.server_addr)), HIBYTE(HIWORD(p.server_addr)), p.server_port);
+ }
+ }
+ else
+ p.server_port = p.server_addr = 0;
+
+ // Send login request
+ if (!(sess = gg_login(&p, &gg->sock, &gg_failno)))
+ {
+ gg_broadcastnewstatus(gg, ID_STATUS_OFFLINE);
+ // Check if connection attempt wasn't cancelled by the user
+ if (gg->proto.m_iDesiredStatus != ID_STATUS_OFFLINE)
+ {
+ char error[128], *perror = NULL;
+ // Lookup for error desciption
+ if (errno == EACCES)
+ {
+ int i;
+ for (i = 0; reason[i].type; i++) if (reason[i].type == gg_failno)
+ {
+ perror = Translate(reason[i].str);
+ break;
+ }
+ }
+ if (!perror)
+ {
+ mir_snprintf(error, sizeof(error), Translate("Connection cannot be established because of error:\n\t%s"), strerror(errno));
+ perror = error;
+ }
+ gg_netlog(gg, "gg_mainthread(%x): %s", gg, perror);
+ if (DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWCERRORS, GG_KEYDEF_SHOWCERRORS))
+ gg_showpopup(gg, GG_PROTONAME, perror, GG_POPUP_ERROR | GG_POPUP_ALLOW_MSGBOX | GG_POPUP_ONCE);
+
+ // Check if we should reconnect
+ if ((gg_failno >= GG_FAILURE_RESOLVING && gg_failno != GG_FAILURE_PASSWORD && gg_failno != GG_FAILURE_INTRUDER && gg_failno != GG_FAILURE_UNAVAILABLE)
+ && errno == EACCES
+ && (DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ARECONNECT, GG_KEYDEF_ARECONNECT) || (hostnum < hostcount - 1)))
+ {
+ DWORD dwInterval = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_RECONNINTERVAL, GG_KEYDEF_RECONNINTERVAL), dwResult;
+ BOOL bRetry = TRUE;
+
+ gg->hConnStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ dwResult = WaitForSingleObjectEx(gg->hConnStopEvent, dwInterval, TRUE);
+ if ((dwResult == WAIT_OBJECT_0 && gg->proto.m_iDesiredStatus == ID_STATUS_OFFLINE)
+ || (dwResult == WAIT_IO_COMPLETION && Miranda_Terminated()))
+ bRetry = FALSE;
+ CloseHandle(gg->hConnStopEvent);
+ gg->hConnStopEvent = NULL;
+
+ // Reconnect to the next server on the list
+ if (bRetry)
+ {
+ if (hostnum < hostcount - 1) hostnum++;
+ mir_free(p.status_descr);
+ gg_broadcastnewstatus(gg, ID_STATUS_CONNECTING);
+ goto retry;
+ }
+ }
+ // We cannot do more about this
+ EnterCriticalSection(&gg->modemsg_mutex);
+ gg->proto.m_iDesiredStatus = ID_STATUS_OFFLINE;
+ LeaveCriticalSection(&gg->modemsg_mutex);
+ }
+ else
+ gg_netlog(gg, "gg_mainthread(%x)): Connection attempt cancelled by the user.", gg);
+ }
+ else
+ {
+ // Successfully connected
+ logonTime = time(NULL);
+ DBWriteContactSettingDword(NULL, GG_PROTO, GG_KEY_LOGONTIME, logonTime);
+ EnterCriticalSection(&gg->sess_mutex);
+ gg->sess = sess;
+ LeaveCriticalSection(&gg->sess_mutex);
+ // Subscribe users status notifications
+ gg_notifyall(gg);
+ // Set startup status
+ if (gg->proto.m_iDesiredStatus != status_gg2m(gg, p.status))
+ gg_refreshstatus(gg, gg->proto.m_iDesiredStatus);
+ else
+ {
+ gg_broadcastnewstatus(gg, gg->proto.m_iDesiredStatus);
+ // Change status of the contact with our own UIN (if got yourself added to the contact list)
+ gg_changecontactstatus(gg, p.uin, p.status, p.status_descr, 0, 0, 0, 0);
+ }
+ if (gg->check_first_conn) // First connection to the account
+ {
+ // Start search for user data
+ gg_getinfo((PROTO_INTERFACE *)gg, NULL, 0);
+ // Fetch user avatar
+ gg_getuseravatar(gg);
+ gg->check_first_conn = 0;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////
+ // Main loop
+ while(gg_isonline(gg))
+ {
+ // Connection broken/closed
+ if(!(e = gg_watch_fd(gg->sess)))
+ {
+ gg_netlog(gg, "gg_mainthread(%x): Connection closed.", gg);
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_free_session(gg->sess);
+ gg->sess = NULL;
+ LeaveCriticalSection(&gg->sess_mutex);
+ break;
+ }
+ else
+ gg_netlog(gg, "gg_mainthread(%x): Event: %s", gg, ggdebug_eventtype(e));
+
+ switch(e->type)
+ {
+ // Client connected
+ case GG_EVENT_CONN_SUCCESS:
+ // Nada
+ break;
+
+ // Client disconnected or connection failure
+ case GG_EVENT_CONN_FAILED:
+ case GG_EVENT_DISCONNECT:
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_free_session(gg->sess);
+ gg->sess = NULL;
+ LeaveCriticalSection(&gg->sess_mutex);
+ break;
+
+ // Client allowed to disconnect
+ case GG_EVENT_DISCONNECT_ACK:
+ // Send logoff
+ gg_logoff(gg->sess);
+ break;
+
+ // Received ackowledge
+ case GG_EVENT_ACK:
+ if(e->event.ack.seq && e->event.ack.recipient)
+ {
+ ProtoBroadcastAck(GG_PROTO, gg_getcontact(gg, (DWORD)e->event.ack.recipient, 0, 0, NULL),
+ ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE) e->event.ack.seq, 0);
+ }
+ break;
+
+ // Statuslist notify (deprecated)
+ case GG_EVENT_NOTIFY:
+ case GG_EVENT_NOTIFY_DESCR:
+ {
+ struct gg_notify_reply *n;
+
+ n = (e->type == GG_EVENT_NOTIFY) ? e->event.notify : e->event.notify_descr.notify;
+
+ for (; n->uin; n++)
+ {
+ char *descr = (e->type == GG_EVENT_NOTIFY_DESCR) ? e->event.notify_descr.descr : NULL;
+ gg_changecontactstatus(gg, n->uin, n->status, descr, 0, n->remote_ip, n->remote_port, n->version);
+ }
+ break;
+ }
+ // Statuslist notify (version >= 6.0)
+ case GG_EVENT_NOTIFY60:
+ {
+ uin_t uin = (uin_t)DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0);
+ int i;
+ for(i = 0; e->event.notify60[i].uin; i++) {
+ if (e->event.notify60[i].uin == uin) continue;
+ gg_changecontactstatus(gg, e->event.notify60[i].uin, e->event.notify60[i].status, e->event.notify60[i].descr,
+ e->event.notify60[i].time, e->event.notify60[i].remote_ip, e->event.notify60[i].remote_port,
+ e->event.notify60[i].version);
+ gg_requestavatar(gg, gg_getcontact(gg, e->event.notify60[i].uin, 0, 0, NULL), 0);
+ }
+ break;
+ }
+
+ // Pubdir search reply && read own data reply
+ case GG_EVENT_PUBDIR50_SEARCH_REPLY:
+ case GG_EVENT_PUBDIR50_READ:
+ case GG_EVENT_PUBDIR50_WRITE:
+ {
+ gg_pubdir50_t res = e->event.pubdir50;
+ int i, count;
+
+ if (e->type == GG_EVENT_PUBDIR50_SEARCH_REPLY)
+ {
+ gg_netlog(gg, "gg_mainthread(%x): Got user info.", gg);
+ // Store next search UIN
+ if (res->seq == GG_SEQ_SEARCH)
+ gg->next_uin = gg_pubdir50_next(res);
+ }
+ else if (e->type == GG_EVENT_PUBDIR50_READ)
+ {
+ gg_netlog(gg, "gg_mainthread(%x): Got owner info.", gg);
+ }
+ else if (e->type == GG_EVENT_PUBDIR50_WRITE)
+ {
+ gg_netlog(gg, "gg_mainthread(%x): Public directory save succesful.", gg);
+ // Update user details
+ gg_getinfo((PROTO_INTERFACE *)gg, NULL, 0);
+ }
+
+ if ((count = gg_pubdir50_count(res)) > 0)
+ {
+ for (i = 0; i < count; i++)
+ {
+ // Loadup fields
+ const char *__fmnumber = gg_pubdir50_get(res, i, GG_PUBDIR50_UIN);
+ const char *__nick = gg_pubdir50_get(res, i, GG_PUBDIR50_NICKNAME);
+ const char *__firstname = gg_pubdir50_get(res, i, GG_PUBDIR50_FIRSTNAME);
+ const char *__lastname = gg_pubdir50_get(res, i, GG_PUBDIR50_LASTNAME);
+ const char *__familyname = gg_pubdir50_get(res, i, GG_PUBDIR50_FAMILYNAME);
+ const char *__birthyear = gg_pubdir50_get(res, i, GG_PUBDIR50_BIRTHYEAR);
+ const char *__city = gg_pubdir50_get(res, i, GG_PUBDIR50_CITY);
+ const char *__origincity = gg_pubdir50_get(res, i, GG_PUBDIR50_FAMILYCITY);
+ const char *__gender = gg_pubdir50_get(res, i, GG_PUBDIR50_GENDER);
+ const char *__status = gg_pubdir50_get(res, i, GG_PUBDIR50_STATUS);
+ uin_t uin = __fmnumber ? atoi(__fmnumber) : 0;
+
+ HANDLE hContact = (res->seq == GG_SEQ_CHINFO) ? NULL : gg_getcontact(gg, uin, 0, 0, NULL);
+ gg_netlog(gg, "gg_mainthread(%x): Search result for uin %d, seq %d.", gg, uin, res->seq);
+ if (res->seq == GG_SEQ_SEARCH)
+ {
+ char strFmt1[64];
+ char strFmt2[64];
+ GGSEARCHRESULT sr = {0};
+
+ mir_snprintf(strFmt2, sizeof(strFmt2), "%s", (char *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, status_gg2m(gg, atoi(__status)), 0));
+ if (__city)
+ {
+ mir_snprintf(strFmt1, sizeof(strFmt1), ", %s %s", Translate("City:"), __city);
+ strncat(strFmt2, strFmt1, sizeof(strFmt2) - strlen(strFmt2));
+ }
+ if (__birthyear)
+ {
+ time_t t = time(NULL);
+ struct tm *lt = localtime(&t);
+ int br = atoi(__birthyear);
+
+ if (br < (lt->tm_year + 1900) && br > 1900)
+ {
+ mir_snprintf(strFmt1, sizeof(strFmt1), ", %s %d", Translate("Age:"), (lt->tm_year + 1900) - br);
+ strncat(strFmt2, strFmt1, sizeof(strFmt2) - strlen(strFmt2));
+ }
+ }
+
+ sr.hdr.cbSize = sizeof(sr);
+ sr.hdr.nick = (char *)__nick;
+ sr.hdr.firstName = (char *)__firstname;
+ sr.hdr.lastName = (char *)__lastname;
+ sr.hdr.email = strFmt2;
+ sr.hdr.id = _ultoa(uin, strFmt1, 10);
+ sr.uin = uin;
+
+ ProtoBroadcastAck(GG_PROTO, NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE) 1, (LPARAM)&sr);
+ }
+
+ if (((res->seq == GG_SEQ_INFO || res->seq == GG_SEQ_GETNICK) && hContact != NULL)
+ || res->seq == GG_SEQ_CHINFO)
+ {
+ // Change nickname if it's not present
+ if (__nick && (res->seq == GG_SEQ_GETNICK || res->seq == GG_SEQ_CHINFO))
+ DBWriteContactSettingString(hContact, GG_PROTO, GG_KEY_NICK, __nick);
+
+ if (__nick)
+ DBWriteContactSettingString(hContact, GG_PROTO, "NickName", __nick);
+ else if (res->seq == GG_SEQ_CHINFO)
+ DBDeleteContactSetting(NULL, GG_PROTO, "NickName");
+
+ // Change other info
+ if (__city)
+ DBWriteContactSettingString(hContact, GG_PROTO, "City", __city);
+ else if (res->seq == GG_SEQ_CHINFO)
+ DBDeleteContactSetting(NULL, GG_PROTO, "City");
+
+ if (__firstname)
+ DBWriteContactSettingString(hContact, GG_PROTO, "FirstName", __firstname);
+ else if (res->seq == GG_SEQ_CHINFO)
+ DBDeleteContactSetting(NULL, GG_PROTO, "FirstName");
+
+ if (__lastname)
+ DBWriteContactSettingString(hContact, GG_PROTO, "LastName", __lastname);
+ else if (res->seq == GG_SEQ_CHINFO)
+ DBDeleteContactSetting(NULL, GG_PROTO, "LastName");
+
+ if (__familyname)
+ DBWriteContactSettingString(hContact, GG_PROTO, "FamilyName", __familyname);
+ else if (res->seq == GG_SEQ_CHINFO)
+ DBDeleteContactSetting(NULL, GG_PROTO, "FamilyName");
+
+ if (__origincity)
+ DBWriteContactSettingString(hContact, GG_PROTO, "CityOrigin", __origincity);
+ else if (res->seq == GG_SEQ_CHINFO)
+ DBDeleteContactSetting(NULL, GG_PROTO, "CityOrigin");
+
+ if (__birthyear)
+ {
+ time_t t = time(NULL);
+ struct tm *lt = localtime(&t);
+ int br = atoi(__birthyear);
+ if (br > 0)
+ {
+ DBWriteContactSettingWord(hContact, GG_PROTO, "Age", (WORD)(lt->tm_year + 1900 - br));
+ DBWriteContactSettingWord(hContact, GG_PROTO, "BirthYear", (WORD)br);
+ }
+ }
+ else if (res->seq == GG_SEQ_CHINFO)
+ {
+ DBDeleteContactSetting(NULL, GG_PROTO, "Age");
+ DBDeleteContactSetting(NULL, GG_PROTO, "BirthYear");
+ }
+
+ // Gadu-Gadu Male <-> Female
+ if (__gender)
+ {
+ if (res->seq == GG_SEQ_CHINFO)
+ DBWriteContactSettingByte(hContact, GG_PROTO, "Gender",
+ (BYTE)(!strcmp(__gender, GG_PUBDIR50_GENDER_SET_MALE) ? 'M' :
+ (!strcmp(__gender, GG_PUBDIR50_GENDER_SET_FEMALE) ? 'F' : '?')));
+ else
+ DBWriteContactSettingByte(hContact, GG_PROTO, "Gender",
+ (BYTE)(!strcmp(__gender, GG_PUBDIR50_GENDER_MALE) ? 'M' :
+ (!strcmp(__gender, GG_PUBDIR50_GENDER_FEMALE) ? 'F' : '?')));
+ }
+ else if (res->seq == GG_SEQ_CHINFO)
+ {
+ DBDeleteContactSetting(NULL, GG_PROTO, "Gender");
+ }
+
+ gg_netlog(gg, "gg_mainthread(%x): Setting user info for uin %d.", gg, uin);
+ ProtoBroadcastAck(GG_PROTO, hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, (HANDLE) 1, 0);
+ }
+ }
+ }
+ if (res->seq == GG_SEQ_SEARCH)
+ ProtoBroadcastAck(GG_PROTO, NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE) 1, 0);
+ break;
+ }
+
+ // Status (deprecated)
+ case GG_EVENT_STATUS:
+ gg_changecontactstatus(gg, e->event.status.uin, e->event.status.status, e->event.status.descr, 0, 0, 0, 0);
+ break;
+
+ // Status (version >= 6.0)
+ case GG_EVENT_STATUS60:
+ {
+ HANDLE hContact = gg_getcontact(gg, e->event.status60.uin, 0, 0, NULL);
+ int oldstatus = DBGetContactSettingWord(hContact, GG_PROTO, GG_KEY_STATUS, (WORD)ID_STATUS_OFFLINE);
+ uin_t uin = (uin_t)DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0);
+
+ if (e->event.status60.uin == uin)
+ {
+ // Status was changed by the user simultaneously logged on using different Miranda account or IM client
+ int iStatus = status_gg2m(gg, e->event.status60.status);
+ CallProtoService(GG_PROTO, PS_SETAWAYMSG, iStatus, (LPARAM)e->event.status60.descr);
+ CallProtoService(GG_PROTO, PS_SETSTATUS, iStatus, 0);
+ }
+
+ gg_changecontactstatus(gg, e->event.status60.uin, e->event.status60.status, e->event.status60.descr,
+ e->event.status60.time, e->event.status60.remote_ip, e->event.status60.remote_port, e->event.status60.version);
+
+ if (oldstatus == ID_STATUS_OFFLINE && DBGetContactSettingWord(hContact, GG_PROTO, GG_KEY_STATUS, (WORD)ID_STATUS_OFFLINE) != ID_STATUS_OFFLINE)
+ gg_requestavatar(gg, hContact, 0);
+ }
+ break;
+
+ // Received userlist / or put info
+ case GG_EVENT_USERLIST:
+ switch (e->event.userlist.type)
+ {
+ case GG_USERLIST_GET_REPLY:
+ if(e->event.userlist.reply)
+ {
+ gg_parsecontacts(gg, e->event.userlist.reply);
+ MessageBox(
+ NULL,
+ Translate("List import successful."),
+ GG_PROTONAME,
+ MB_OK | MB_ICONINFORMATION
+ );
+ }
+ break;
+
+ case GG_USERLIST_PUT_REPLY:
+ if(gg->list_remove)
+ MessageBox(
+ NULL,
+ Translate("List remove successful."),
+ GG_PROTONAME,
+ MB_OK | MB_ICONINFORMATION
+ );
+ else
+ MessageBox(
+ NULL,
+ Translate("List export successful."),
+ GG_PROTONAME,
+ MB_OK | MB_ICONINFORMATION
+ );
+
+ break;
+ }
+ break;
+
+ // Received message
+ case GG_EVENT_MSG:
+ // This is CTCP request
+ if((e->event.msg.msgclass & GG_CLASS_CTCP))
+ {
+ gg_dccconnect(gg, e->event.msg.sender);
+ }
+ // Check if not conference and block
+ else if(!e->event.msg.recipients_count || gg->gc_enabled)
+ {
+ // Check if groupchat
+ if(e->event.msg.recipients_count && gg->gc_enabled && !DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_IGNORECONF, GG_KEYDEF_IGNORECONF))
+ {
+ char *chat = gg_gc_getchat(gg, e->event.msg.sender, e->event.msg.recipients, e->event.msg.recipients_count);
+ if(chat)
+ {
+ char id[32];
+ GCDEST gcdest = {GG_PROTO, chat, GC_EVENT_MESSAGE};
+ GCEVENT gcevent = {sizeof(GCEVENT), &gcdest};
+ time_t t = time(NULL);
+
+ UIN2ID(e->event.msg.sender, id);
+
+ gcevent.pszUID = id;
+ gcevent.pszText = e->event.msg.message;
+ gcevent.pszNick = (char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) gg_getcontact(gg, e->event.msg.sender, 1, 0, NULL), 0);
+ gcevent.time = (!(e->event.msg.msgclass & GG_CLASS_OFFLINE) || e->event.msg.time > (t - timeDeviation)) ? t : e->event.msg.time;
+ gcevent.dwFlags = GCEF_ADDTOLOG;
+ gg_netlog(gg, "gg_mainthread(%x): Conference message to room %s & id %s.", gg, chat, id);
+ CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent);
+ }
+ }
+ // Check if not empty message ( who needs it? )
+ else if(!e->event.msg.recipients_count && e->event.msg.message && *e->event.msg.message && strcmp(e->event.msg.message, "\xA0\0"))
+ {
+ CCSDATA ccs = {0};
+ PROTORECVEVENT pre = {0};
+ time_t t = time(NULL);
+ ccs.szProtoService = PSR_MESSAGE;
+ ccs.hContact = gg_getcontact(gg, e->event.msg.sender, 1, 0, NULL);
+ ccs.lParam = (LPARAM)&pre;
+ pre.timestamp = (!(e->event.msg.msgclass & GG_CLASS_OFFLINE) || e->event.msg.time > (t - timeDeviation)) ? t : e->event.msg.time;
+ pre.szMessage = e->event.msg.message;
+ CallService(MS_PROTO_CHAINRECV, 0, (LPARAM) &ccs);
+ }
+
+ // RichEdit format included (image)
+ if(e->event.msg.formats_length &&
+ DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_IMGRECEIVE, GG_KEYDEF_IMGRECEIVE) &&
+ !(DBGetContactSettingDword(gg_getcontact(gg, e->event.msg.sender, 1, 0, NULL), "Ignore", "Mask1", 0) & IGNOREEVENT_MESSAGE))
+ {
+ char *formats = e->event.msg.formats;
+ int len = 0, formats_len = e->event.msg.formats_length, add_ptr;
+
+ while (len < formats_len)
+ {
+ add_ptr = sizeof(struct gg_msg_richtext_format);
+ if (((struct gg_msg_richtext_format*)formats)->font & GG_FONT_IMAGE)
+ {
+ struct gg_msg_richtext_image *image = (struct gg_msg_richtext_image *)(formats + add_ptr);
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_image_request(gg->sess, e->event.msg.sender, image->size, image->crc32);
+ LeaveCriticalSection(&gg->sess_mutex);
+
+ gg_netlog(gg, "gg_mainthread: image request sent!");
+ add_ptr += sizeof(struct gg_msg_richtext_image);
+ }
+ if (((struct gg_msg_richtext_format*)formats)->font & GG_FONT_COLOR)
+ add_ptr += sizeof(struct gg_msg_richtext_color);
+ len += add_ptr;
+ formats += add_ptr;
+ }
+ }
+ }
+ break;
+
+ // Message sent from concurrent user session
+ case GG_EVENT_MULTILOGON_MSG:
+ if (e->event.multilogon_msg.recipients_count && gg->gc_enabled && !DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_IGNORECONF, GG_KEYDEF_IGNORECONF))
+ {
+ char *chat = gg_gc_getchat(gg, e->event.multilogon_msg.sender, e->event.multilogon_msg.recipients, e->event.multilogon_msg.recipients_count);
+ if (chat)
+ {
+ char id[32];
+ DBVARIANT dbv;
+ GCDEST gcdest = {GG_PROTO, chat, GC_EVENT_MESSAGE};
+ GCEVENT gcevent = {sizeof(GCEVENT), &gcdest};
+
+ UIN2ID(DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0), id);
+
+ gcevent.pszUID = id;
+ gcevent.pszText = e->event.multilogon_msg.message;
+ if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_NICK, &dbv))
+ gcevent.pszNick = dbv.pszVal;
+ else
+ gcevent.pszNick = Translate("Me");
+ gcevent.time = e->event.multilogon_msg.time;
+ gcevent.bIsMe = 1;
+ gcevent.dwFlags = GCEF_ADDTOLOG;
+ gg_netlog(gg, "gg_mainthread(%x): Sent conference message to room %s.", gg, chat);
+ CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent);
+ if (gcevent.pszNick == dbv.pszVal) DBFreeVariant(&dbv);
+ }
+ }
+ else if (!e->event.multilogon_msg.recipients_count && e->event.multilogon_msg.message && *e->event.multilogon_msg.message
+ && strcmp(e->event.multilogon_msg.message, "\xA0\0"))
+ {
+ DBEVENTINFO dbei = {0};
+ dbei.cbSize = sizeof(dbei);
+ dbei.szModule = GG_PROTO;
+ dbei.timestamp = (DWORD)e->event.multilogon_msg.time;
+ dbei.flags = DBEF_SENT;
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.cbBlob = (DWORD)strlen(e->event.multilogon_msg.message) + 1;
+ dbei.pBlob = (PBYTE)e->event.multilogon_msg.message;
+ CallService(MS_DB_EVENT_ADD, (WPARAM)gg_getcontact(gg, e->event.multilogon_msg.sender, 1, 0, NULL), (LPARAM)&dbei);
+ }
+ break;
+
+ // Information on active concurrent sessions
+ case GG_EVENT_MULTILOGON_INFO:
+ {
+ list_t l;
+ int* iIndexes = NULL, i;
+ gg_netlog(gg, "gg_mainthread(): Concurrent sessions count: %d.", e->event.multilogon_info.count);
+ if (e->event.multilogon_info.count > 0)
+ iIndexes = mir_calloc(e->event.multilogon_info.count * sizeof(int));
+ EnterCriticalSection(&gg->sessions_mutex);
+ for (l = gg->sessions; l; l = l->next)
+ {
+ struct gg_multilogon_session* sess = (struct gg_multilogon_session*)l->data;
+ for (i = 0; i < e->event.multilogon_info.count; i++)
+ {
+ if (!memcmp(&sess->id, &e->event.multilogon_info.sessions[i].id, sizeof(gg_multilogon_id_t)) && iIndexes)
+ {
+ iIndexes[i]++;
+ break;
+ }
+ }
+ mir_free(sess->name);
+ mir_free(sess);
+ }
+ list_destroy(gg->sessions, 0);
+ gg->sessions = NULL;
+ for (i = 0; i < e->event.multilogon_info.count; i++)
+ {
+ struct gg_multilogon_session* sess = mir_alloc(sizeof(struct gg_multilogon_session));
+ memcpy(sess, &e->event.multilogon_info.sessions[i], sizeof(struct gg_multilogon_session));
+ sess->name = mir_strdup(*e->event.multilogon_info.sessions[i].name != '\0'
+ ? e->event.multilogon_info.sessions[i].name
+ : Translate("Unknown client"));
+ list_add(&gg->sessions, sess, 0);
+ }
+ LeaveCriticalSection(&gg->sessions_mutex);
+ gg_sessions_updatedlg(gg);
+ if (ServiceExists(MS_POPUP_ADDPOPUPCLASS))
+ {
+ const char* szText = time(NULL) - logonTime > 3
+ ? Translate("You have logged in at another location")
+ : Translate("You are logged in at another location");
+ for (i = 0; i < e->event.multilogon_info.count; i++)
+ {
+ char szMsg[MAX_SECONDLINE];
+ if (iIndexes && iIndexes[i]) continue;
+ mir_snprintf(szMsg, SIZEOF(szMsg), "%s (%s)", szText,
+ *e->event.multilogon_info.sessions[i].name != '\0'
+ ? e->event.multilogon_info.sessions[i].name
+ : Translate("Unknown client"));
+ gg_showpopup(gg, GG_PROTONAME, szMsg, GG_POPUP_MULTILOGON);
+ }
+ }
+ mir_free(iIndexes);
+ }
+ break;
+
+ // Image reply sent
+ case GG_EVENT_IMAGE_REPLY:
+ // Get rid of empty image
+ if(e->event.image_reply.size && e->event.image_reply.image)
+ {
+ HANDLE hContact = gg_getcontact(gg, e->event.image_reply.sender, 1, 0, NULL);
+ void *img = (void *)gg_img_loadpicture(gg, e, 0);
+
+ if(!img)
+ break;
+
+ if(DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_IMGMETHOD, GG_KEYDEF_IMGMETHOD) == 1 || gg_img_opened(gg, e->event.image_reply.sender))
+ {
+ gg_img_display(gg, hContact, img);
+ }
+ else if(DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_IMGMETHOD, GG_KEYDEF_IMGMETHOD) == 2)
+ {
+ gg_img_displayasmsg(gg, hContact, img);
+ }
+ else
+ {
+ CLISTEVENT cle = {0};
+ char service[128];
+ mir_snprintf(service, sizeof(service), GGS_RECVIMAGE, GG_PROTO);
+
+ cle.cbSize = sizeof(cle);
+ cle.hContact = hContact;
+ cle.hIcon = LoadIconEx("image", FALSE);
+ cle.flags = CLEF_URGENT;
+ cle.hDbEvent = (HANDLE)"img";
+ cle.lParam = (LPARAM)img;
+ cle.pszService = service;
+ cle.pszTooltip = Translate("Incoming image");
+ CallService(MS_CLIST_ADDEVENT, 0, (LPARAM)&cle);
+ ReleaseIconEx("image", FALSE);
+ }
+ }
+ break;
+
+ // Image send request
+ case GG_EVENT_IMAGE_REQUEST:
+ gg_img_sendonrequest(gg, e);
+ break;
+
+ // Incoming direct connection
+ case GG_EVENT_DCC7_NEW:
+ {
+ struct gg_dcc7 *dcc7 = e->event.dcc7_new;
+ gg_netlog(gg, "gg_mainthread(%x): Incoming direct connection.", gg);
+ dcc7->contact = gg_getcontact(gg, dcc7->peer_uin, 0, 0, NULL);
+
+ // Check if user is on the list and if it is my uin
+ if(!dcc7->contact || DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, -1) != dcc7->uin) {
+ gg_dcc7_free(dcc7);
+ e->event.dcc7_new = NULL;
+ break;
+ }
+
+ // Add to waiting transfers
+ EnterCriticalSection(&gg->ft_mutex);
+ list_add(&gg->transfers, dcc7, 0);
+ LeaveCriticalSection(&gg->ft_mutex);
+
+ //////////////////////////////////////////////////
+ // Add file recv request
+ {
+ CCSDATA ccs;
+ PROTORECVEVENT pre;
+ char *szBlob;
+ char *szFilename = dcc7->filename;
+ char *szMsg = dcc7->filename;
+ gg_netlog(gg, "gg_mainthread(%x): Client: %d, File ack filename \"%s\" size %d.", gg, dcc7->peer_uin,
+ dcc7->filename, dcc7->size);
+ // Make new ggtransfer struct
+ szBlob = (char *)malloc(sizeof(DWORD) + strlen(szFilename) + strlen(szMsg) + 2);
+ // Store current dcc
+ *(PDWORD)szBlob = (DWORD)dcc7;
+ // Store filename
+ strcpy(szBlob + sizeof(DWORD), szFilename);
+ // Store description
+ strcpy(szBlob + sizeof(DWORD) + strlen(szFilename) + 1, szMsg);
+ ccs.szProtoService = PSR_FILE;
+ ccs.hContact = dcc7->contact;
+ ccs.wParam = 0;
+ ccs.lParam = (LPARAM)&pre;
+ pre.flags = 0;
+ pre.timestamp = time(NULL);
+ pre.szMessage = szBlob;
+ pre.lParam = 0;
+ CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs);
+ free(szBlob);
+ }
+ e->event.dcc7_new = NULL;
+ }
+ break;
+
+ // Direct connection rejected
+ case GG_EVENT_DCC7_REJECT:
+ {
+ struct gg_dcc7 *dcc7 = e->event.dcc7_reject.dcc7;
+ if (dcc7->type == GG_SESSION_DCC7_SEND)
+ {
+ gg_netlog(gg, "gg_mainthread(%x): File transfer denied by client %d (reason = %d).", gg, dcc7->peer_uin, e->event.dcc7_reject.reason);
+ ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DENIED, dcc7, 0);
+
+ // Remove from watches and free
+ EnterCriticalSection(&gg->ft_mutex);
+ list_remove(&gg->watches, dcc7, 0);
+ LeaveCriticalSection(&gg->ft_mutex);
+ gg_dcc7_free(dcc7);
+ }
+ else
+ {
+ gg_netlog(gg, "gg_mainthread(%x): File transfer aborted by client %d.", gg, dcc7->peer_uin);
+
+ // Remove transfer from waiting list
+ EnterCriticalSection(&gg->ft_mutex);
+ list_remove(&gg->transfers, dcc7, 0);
+ LeaveCriticalSection(&gg->ft_mutex);
+ }
+ }
+ break;
+
+ // Direct connection error
+ case GG_EVENT_DCC7_ERROR:
+ {
+ struct gg_dcc7 *dcc7 = e->event.dcc7_error_ex.dcc7;
+ switch (e->event.dcc7_error)
+ {
+ case GG_ERROR_DCC7_HANDSHAKE:
+ gg_netlog(gg, "gg_mainthread(%x): Client: %d, Handshake error.", gg, dcc7 ? dcc7->peer_uin : 0);
+ break;
+ case GG_ERROR_DCC7_NET:
+ gg_netlog(gg, "gg_mainthread(%x): Client: %d, Network error.", gg, dcc7 ? dcc7->peer_uin : 0);
+ break;
+ case GG_ERROR_DCC7_FILE:
+ gg_netlog(gg, "gg_mainthread(%x): Client: %d, File read/write error.", gg, dcc7 ? dcc7->peer_uin : 0);
+ break;
+ case GG_ERROR_DCC7_EOF:
+ gg_netlog(gg, "gg_mainthread(%x): Client: %d, End of file/connection error.", gg, dcc7 ? dcc7->peer_uin : 0);
+ break;
+ case GG_ERROR_DCC7_REFUSED:
+ gg_netlog(gg, "gg_mainthread(%x): Client: %d, Connection refused error.", gg, dcc7 ? dcc7->peer_uin : 0);
+ break;
+ case GG_ERROR_DCC7_RELAY:
+ gg_netlog(gg, "gg_mainthread(%x): Client: %d, Relay connection error.", gg, dcc7 ? dcc7->peer_uin : 0);
+ break;
+ default:
+ gg_netlog(gg, "gg_mainthread(%x): Client: %d, Unknown error.", gg, dcc7 ? dcc7->peer_uin : 0);
+ }
+ if (!dcc7) break;
+
+ // Remove from watches
+ list_remove(&gg->watches, dcc7, 0);
+
+ // Close file & fail
+ if (dcc7->file_fd != -1)
+ {
+ _close(dcc7->file_fd);
+ dcc7->file_fd = -1;
+ }
+
+ if (dcc7->contact)
+ ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0);
+
+ // Free dcc
+ gg_dcc7_free(dcc7);
+ }
+ break;
+
+ case GG_EVENT_XML_ACTION:
+ if (DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) {
+ HXML hXml;
+ TCHAR *xmlAction;
+ TCHAR *tag;
+
+ xmlAction = gg_a2t(e->event.xml_action.data);
+ tag = gg_a2t("events");
+ hXml = xi.parseString(xmlAction, 0, tag);
+
+ if (hXml != NULL) {
+ HXML node;
+ char *type, *sender;
+
+ mir_free(tag);
+ tag = gg_a2t("event/type");
+ node = xi.getChildByPath(hXml, tag, 0);
+ type = node != NULL ? gg_t2a(xi.getText(node)) : NULL;
+
+ mir_free(tag);
+ tag = gg_a2t("event/sender");
+ node = xi.getChildByPath(hXml, tag, 0);
+ sender = node != NULL ? gg_t2a(xi.getText(node)) : NULL;
+ gg_netlog(gg, "gg_mainthread(%x): XML Action type: %s.", gg, type != NULL ? type : "unknown");
+ // Avatar change notify
+ if (type != NULL && !strcmp(type, "28")) {
+ gg_netlog(gg, "gg_mainthread(%x): Client %s changed his avatar.", gg, sender);
+ gg_requestavatar(gg, gg_getcontact(gg, atoi(sender), 0, 0, NULL), 0);
+ }
+ mir_free(type);
+ mir_free(sender);
+ xi.destroyNode(hXml);
+ }
+ mir_free(tag);
+ mir_free(xmlAction);
+ }
+ break;
+
+ case GG_EVENT_TYPING_NOTIFICATION:
+ {
+ HANDLE hContact = gg_getcontact(gg, e->event.typing_notification.uin, 0, 0, NULL);
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_mainthread(%x): Typing notification from %d (%d).", gg,
+ e->event.typing_notification.uin, e->event.typing_notification.length);
+#endif
+ CallService(MS_PROTO_CONTACTISTYPING, (WPARAM)hContact,
+ e->event.typing_notification.length > 0 ? 7 : PROTOTYPE_CONTACTTYPING_OFF);
+ }
+ break;
+ }
+ // Free event struct
+ gg_free_event(e);
+ }
+
+ gg_broadcastnewstatus(gg, ID_STATUS_OFFLINE);
+ gg_setalloffline(gg);
+ DBWriteContactSettingDword(NULL, GG_PROTO, GG_KEY_LOGONTIME, 0);
+
+ // If it was unwanted disconnection reconnect
+ if(gg->proto.m_iDesiredStatus != ID_STATUS_OFFLINE
+ && DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ARECONNECT, GG_KEYDEF_ARECONNECT))
+ {
+ gg_netlog(gg, "gg_mainthread(%x): Unintentional disconnection detected. Going to reconnect...", gg);
+ hostnum = 0;
+ gg_broadcastnewstatus(gg, ID_STATUS_CONNECTING);
+ mir_free(p.status_descr);
+ goto retry;
+ }
+
+ mir_free(p.password);
+ mir_free(p.status_descr);
+
+ // Destroy concurrent sessions list
+ {
+ list_t l;
+ EnterCriticalSection(&gg->sessions_mutex);
+ for (l = gg->sessions; l; l = l->next)
+ {
+ struct gg_multilogon_session* sess = (struct gg_multilogon_session*)l->data;
+ mir_free(sess->name);
+ mir_free(sess);
+ }
+ list_destroy(gg->sessions, 0);
+ gg->sessions = NULL;
+ LeaveCriticalSection(&gg->sessions_mutex);
+ }
+
+ // Stop dcc server
+ gg->pth_dcc.dwThreadId = 0;
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_mainthread(%x): Waiting until DCC Server Thread finished, if needed.", gg);
+#endif
+ gg_threadwait(gg, &gg->pth_dcc);
+
+ gg_netlog(gg, "gg_mainthread(%x): Server Thread Ending", gg);
+ return;
+}
+
+////////////////////////////////////////////////////////////
+// Change status function
+void gg_broadcastnewstatus(GGPROTO *gg, int newStatus)
+{
+ int oldStatus;
+
+ EnterCriticalSection(&gg->modemsg_mutex);
+ oldStatus = gg->proto.m_iStatus;
+ if(oldStatus == newStatus)
+ {
+ LeaveCriticalSection(&gg->modemsg_mutex);
+ return;
+ }
+ gg->proto.m_iStatus = newStatus;
+ LeaveCriticalSection(&gg->modemsg_mutex);
+
+ ProtoBroadcastAck(GG_PROTO, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldStatus, newStatus);
+
+ gg_netlog(gg, "gg_broadcastnewstatus(): Broadcast new status: %d.", newStatus);
+}
+
+////////////////////////////////////////////////////////////
+// When contact is deleted
+int gg_contactdeleted(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE) wParam;
+ uin_t uin; int type;
+ DBVARIANT dbv;
+
+ uin = (uin_t)DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0);
+ type = DBGetContactSettingByte(hContact, GG_PROTO, "ChatRoom", 0);
+
+ // Terminate conference if contact is deleted
+ if(type && !DBGetContactSetting(hContact, GG_PROTO, "ChatRoomID", &dbv) && gg->gc_enabled)
+ {
+ GCDEST gcdest = {GG_PROTO, dbv.pszVal, GC_EVENT_CONTROL};
+ GCEVENT gcevent = {sizeof(GCEVENT), &gcdest};
+ GGGC *chat = gg_gc_lookup(gg, dbv.pszVal);
+
+ gg_netlog(gg, "gg_gc_event(): Terminating chat %x, id %s from contact list...", chat, dbv.pszVal);
+ if(chat)
+ {
+ // Destroy chat entry
+ free(chat->recipients);
+ list_remove(&gg->chats, chat, 1);
+ // Terminate chat window / shouldn't cascade entry is deleted
+ CallServiceSync(MS_GC_EVENT, SESSION_OFFLINE, (LPARAM)&gcevent);
+ CallServiceSync(MS_GC_EVENT, SESSION_TERMINATE, (LPARAM)&gcevent);
+ }
+
+ DBFreeVariant(&dbv);
+ return 0;
+ }
+
+ if(uin && gg_isonline(gg))
+ {
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_remove_notify_ex(gg->sess, uin, GG_USER_NORMAL);
+ LeaveCriticalSection(&gg->sess_mutex);
+ }
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////
+// When db settings changed
+int gg_dbsettingchanged(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *) lParam;
+ HANDLE hContact = (HANDLE) wParam;
+ char *szProto = NULL;
+
+ // Check if the contact is NULL or we are not online
+ if(!gg_isonline(gg))
+ return 0;
+
+ // If contact has been blocked
+ if(!strcmp(cws->szModule, GG_PROTO) && !strcmp(cws->szSetting, GG_KEY_BLOCK))
+ {
+ gg_notifyuser(gg, hContact, 1);
+ return 0;
+ }
+
+ // Contact is being renamed
+ if(gg->gc_enabled && !strcmp(cws->szModule, GG_PROTO) && !strcmp(cws->szSetting, GG_KEY_NICK)
+ && cws->value.pszVal)
+ {
+ // Groupchat window contact is being renamed
+ DBVARIANT dbv;
+ int type = DBGetContactSettingByte(hContact, GG_PROTO, "ChatRoom", 0);
+ if(type && !DBGetContactSetting(hContact, GG_PROTO, "ChatRoomID", &dbv))
+ {
+ // Most important... check redundancy (fucking cascading)
+ static int cascade = 0;
+ if(!cascade && dbv.pszVal)
+ {
+ GCDEST gcdest = {GG_PROTO, dbv.pszVal, GC_EVENT_CHANGESESSIONAME};
+ GCEVENT gcevent = {sizeof(GCEVENT), &gcdest};
+ gcevent.pszText = cws->value.pszVal;
+ gg_netlog(gg, "gg_dbsettingchanged(): Conference %s was renamed to %s.", dbv.pszVal, cws->value.pszVal);
+ // Mark cascading
+ /* FIXME */ cascade = 1;
+ CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent);
+ /* FIXME */ cascade = 0;
+ }
+ DBFreeVariant(&dbv);
+ }
+ else
+ // Change contact name on all chats
+ gg_gc_changenick(gg, hContact, cws->value.pszVal);
+ }
+
+ // Contact list changes
+ if(!strcmp(cws->szModule, "CList"))
+ {
+ // If name changed... change nick
+ if(!strcmp(cws->szSetting, "MyHandle") && cws->value.type == DBVT_ASCIIZ && cws->value.pszVal)
+ DBWriteContactSettingString(hContact, GG_PROTO, GG_KEY_NICK, cws->value.pszVal);
+
+ // If not on list changed
+ if(!strcmp(cws->szSetting, "NotOnList"))
+ {
+ if(DBGetContactSettingByte(hContact, "CList", "Hidden", 0))
+ return 0;
+ // Notify user normally this time if added to the list permanently
+ if(cws->value.type == DBVT_DELETED || (cws->value.type == DBVT_BYTE && cws->value.bVal == 0))
+ gg_notifyuser(gg, hContact, 1);
+ }
+ }
+ return 0;
+}
+
+////////////////////////////////////////////////////////////
+// All users set offline
+void gg_setalloffline(GGPROTO *gg)
+{
+ HANDLE hContact;
+ char *szProto;
+
+ gg_netlog(gg, "gg_setalloffline(): Setting buddies offline");
+ DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_STATUS, ID_STATUS_OFFLINE);
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if(szProto != NULL && !strcmp(szProto, GG_PROTO))
+ {
+ DBWriteContactSettingWord(hContact, GG_PROTO, GG_KEY_STATUS, ID_STATUS_OFFLINE);
+ // Clear IP and port settings
+ DBDeleteContactSetting(hContact, GG_PROTO, GG_KEY_CLIENTIP);
+ DBDeleteContactSetting(hContact, GG_PROTO, GG_KEY_CLIENTPORT);
+ // Delete status descr
+ DBDeleteContactSetting(hContact, "CList", GG_KEY_STATUSDESCR);
+ }
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_setalloffline(): End");
+#endif
+}
+
+////////////////////////////////////////////////////////////
+// All users set offline
+void gg_notifyuser(GGPROTO *gg, HANDLE hContact, int refresh)
+{
+ uin_t uin;
+ if (!hContact) return;
+ if (gg_isonline(gg) && (uin = (uin_t)DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0)))
+ {
+ // Check if user should be invisible
+ // Or be blocked ?
+ if ((DBGetContactSettingWord(hContact, GG_PROTO, GG_KEY_APPARENT, (WORD) ID_STATUS_ONLINE) == ID_STATUS_OFFLINE) ||
+ DBGetContactSettingByte(hContact, "CList", "NotOnList", 0))
+ {
+ EnterCriticalSection(&gg->sess_mutex);
+ if (refresh)
+ {
+ gg_remove_notify_ex(gg->sess, uin, GG_USER_NORMAL);
+ gg_remove_notify_ex(gg->sess, uin, GG_USER_BLOCKED);
+ }
+
+ gg_add_notify_ex(gg->sess, uin, GG_USER_OFFLINE);
+ LeaveCriticalSection(&gg->sess_mutex);
+ }
+ else if (DBGetContactSettingByte(hContact, GG_PROTO, GG_KEY_BLOCK, 0))
+ {
+ EnterCriticalSection(&gg->sess_mutex);
+ if (refresh)
+ {
+ gg_remove_notify_ex(gg->sess, uin, GG_USER_OFFLINE);
+ }
+
+ gg_add_notify_ex(gg->sess, uin, GG_USER_BLOCKED);
+ LeaveCriticalSection(&gg->sess_mutex);
+ }
+ else
+ {
+ EnterCriticalSection(&gg->sess_mutex);
+ if (refresh)
+ {
+ gg_remove_notify_ex(gg->sess, uin, GG_USER_BLOCKED);
+ }
+
+ gg_add_notify_ex(gg->sess, uin, GG_USER_NORMAL);
+ LeaveCriticalSection(&gg->sess_mutex);
+ }
+ }
+}
+void gg_notifyall(GGPROTO *gg)
+{
+ HANDLE hContact;
+ char *szProto;
+ int count = 0, cc = 0;
+ uin_t *uins;
+ char *types;
+
+ gg_netlog(gg, "gg_notifyall(): Subscribing notification to all users");
+ // Readup count
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (szProto != NULL && !strcmp(szProto, GG_PROTO)) count ++;
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+
+ // Readup list
+ /* FIXME: If we have nothing on the list but we omit gg_notify_ex we have problem with receiving any contacts */
+ if (count == 0)
+ {
+ if(gg_isonline(gg))
+ {
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_notify_ex(gg->sess, NULL, NULL, 0);
+ LeaveCriticalSection(&gg->sess_mutex);
+ }
+ return;
+ }
+ uins = calloc(sizeof(uin_t), count);
+ types = calloc(sizeof(char), count);
+
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact && cc < count)
+ {
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (szProto != NULL && !strcmp(szProto, GG_PROTO) && (uins[cc] = DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0)))
+ {
+ if ((DBGetContactSettingWord(hContact, GG_PROTO, GG_KEY_APPARENT, (WORD) ID_STATUS_ONLINE) == ID_STATUS_OFFLINE) ||
+ DBGetContactSettingByte(hContact, "CList", "NotOnList", 0))
+ types[cc] = GG_USER_OFFLINE;
+ else if (DBGetContactSettingByte(hContact, GG_PROTO, GG_KEY_BLOCK, 0))
+ types[cc] = GG_USER_BLOCKED;
+ else
+ types[cc] = GG_USER_NORMAL;
+ cc ++;
+ }
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+ if (cc < count) count = cc;
+
+ // Send notification
+ if (gg_isonline(gg))
+ {
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_notify_ex(gg->sess, uins, types, count);
+ LeaveCriticalSection(&gg->sess_mutex);
+ }
+
+ // Free variables
+ free(uins); free(types);
+}
+
+////////////////////////////////////////////////////////////
+// Get contact by uin
+HANDLE gg_getcontact(GGPROTO *gg, uin_t uin, int create, int inlist, char *szNick)
+{
+ HANDLE hContact;
+ char *szProto;
+
+ // Look for contact in DB
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if(szProto != NULL && !strcmp(szProto, GG_PROTO))
+ {
+ if((uin_t)DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0) == uin
+ && DBGetContactSettingByte(hContact, GG_PROTO, "ChatRoom", 0) == 0)
+ {
+ if(inlist)
+ {
+ DBDeleteContactSetting(hContact, "CList", "NotOnList");
+ DBDeleteContactSetting(hContact, "CList", "Hidden");
+ }
+ return hContact;
+ }
+ }
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+ if(!create) return NULL;
+
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_ADD, 0, 0);
+
+ if(!hContact)
+ {
+ gg_netlog(gg, "gg_getcontact(): Failed to create Gadu-Gadu contact %s", szNick);
+ return NULL;
+ }
+
+ if(CallService(MS_PROTO_ADDTOCONTACT, (WPARAM) hContact, (LPARAM) GG_PROTO) != 0)
+ {
+ // For some reason we failed to register the protocol for this contact
+ CallService(MS_DB_CONTACT_DELETE, (WPARAM) hContact, 0);
+ gg_netlog(gg, "Failed to register GG contact %d", uin);
+ return NULL;
+ }
+
+ gg_netlog(gg, "gg_getcontact(): Added buddy: %d", uin);
+ if(!inlist)
+ {
+ DBWriteContactSettingByte(hContact, "CList", "NotOnList", 1);
+ //DBWriteContactSettingByte(hContact, "CList", "Hidden", 1);
+ }
+
+ DBWriteContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, (DWORD) uin);
+ DBWriteContactSettingWord(hContact, GG_PROTO, GG_KEY_STATUS, ID_STATUS_OFFLINE);
+
+ // If nick specified use it
+ if(szNick)
+ DBWriteContactSettingString(hContact, GG_PROTO, GG_KEY_NICK, szNick);
+ else if(gg_isonline(gg))
+ {
+ gg_pubdir50_t req;
+
+ // Search for that nick
+ if(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH))
+ {
+ // Add uin and search it
+ gg_pubdir50_add(req, GG_PUBDIR50_UIN, ditoa(uin));
+ gg_pubdir50_seq_set(req, GG_SEQ_GETNICK);
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_pubdir50(gg->sess, req);
+ LeaveCriticalSection(&gg->sess_mutex);
+ gg_pubdir50_free(req);
+ DBWriteContactSettingString(hContact, GG_PROTO, GG_KEY_NICK, ditoa(uin));
+ gg_netlog(gg, "gg_getcontact(): Search for nick on uin: %d", uin);
+ }
+ }
+
+ // Add to notify list and pull avatar for the new contact
+ if(gg_isonline(gg))
+ {
+ PROTO_AVATAR_INFORMATION pai = {0};
+
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_add_notify_ex(gg->sess, uin, (char)(inlist ? GG_USER_NORMAL : GG_USER_OFFLINE));
+ LeaveCriticalSection(&gg->sess_mutex);
+
+ pai.cbSize = sizeof(pai);
+ pai.hContact = hContact;
+ gg_getavatarinfo(gg, (WPARAM)GAIF_FORCE, (LPARAM)&pai);
+
+ // Change status of the contact with our own UIN (if got yourself added to the contact list)
+ if (DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0) == uin)
+ {
+ char *szMsg;
+ EnterCriticalSection(&gg->modemsg_mutex);
+ szMsg = mir_strdup(gg_getstatusmsg(gg, gg->proto.m_iStatus));
+ LeaveCriticalSection(&gg->modemsg_mutex);
+ gg_changecontactstatus(gg, uin, status_m2gg(gg, gg->proto.m_iStatus, szMsg != NULL), szMsg, 0, 0, 0, 0);
+ mir_free(szMsg);
+ }
+ }
+
+ // TODO server side list & add buddy
+ return hContact;
+}
+
+////////////////////////////////////////////////////////////
+// Status conversion
+int status_m2gg(GGPROTO *gg, int status, int descr)
+{
+ // check frends only
+ int mask = DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_FRIENDSONLY, GG_KEYDEF_FRIENDSONLY) ? GG_STATUS_FRIENDS_MASK : 0;
+
+ if(descr)
+ {
+ switch(status)
+ {
+ case ID_STATUS_OFFLINE:
+ return GG_STATUS_NOT_AVAIL_DESCR | mask;
+
+ case ID_STATUS_ONLINE:
+ return GG_STATUS_AVAIL_DESCR | mask;
+
+ case ID_STATUS_AWAY:
+ return GG_STATUS_BUSY_DESCR | mask;
+
+ case ID_STATUS_DND:
+ return GG_STATUS_DND_DESCR | mask;
+
+ case ID_STATUS_FREECHAT:
+ return GG_STATUS_FFC_DESCR | mask;
+
+ case ID_STATUS_INVISIBLE:
+ return GG_STATUS_INVISIBLE_DESCR | mask;
+
+ default:
+ return GG_STATUS_BUSY_DESCR | mask;
+ }
+ }
+ else
+ {
+ switch(status)
+ {
+ case ID_STATUS_OFFLINE:
+ return GG_STATUS_NOT_AVAIL | mask;
+
+ case ID_STATUS_ONLINE:
+ return GG_STATUS_AVAIL | mask;
+
+ case ID_STATUS_AWAY:
+ return GG_STATUS_BUSY | mask;
+
+ case ID_STATUS_DND:
+ return GG_STATUS_DND | mask;
+
+ case ID_STATUS_FREECHAT:
+ return GG_STATUS_FFC | mask;
+
+ case ID_STATUS_INVISIBLE:
+ return GG_STATUS_INVISIBLE | mask;
+
+ default:
+ return GG_STATUS_BUSY | mask;
+ }
+ }
+}
+int status_gg2m(GGPROTO *gg, int status)
+{
+ // ignore additional flags
+ status = GG_S(status);
+
+ // when user has status description but is offline (show it invisible)
+ if(status == GG_STATUS_NOT_AVAIL_DESCR && DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWINVISIBLE, GG_KEYDEF_SHOWINVISIBLE))
+ return ID_STATUS_INVISIBLE;
+
+ // rest of cases
+ switch(status)
+ {
+ case GG_STATUS_NOT_AVAIL:
+ case GG_STATUS_NOT_AVAIL_DESCR:
+ return ID_STATUS_OFFLINE;
+
+ case GG_STATUS_AVAIL:
+ case GG_STATUS_AVAIL_DESCR:
+ return ID_STATUS_ONLINE;
+
+ case GG_STATUS_BUSY:
+ case GG_STATUS_BUSY_DESCR:
+ return ID_STATUS_AWAY;
+
+ case GG_STATUS_DND:
+ case GG_STATUS_DND_DESCR:
+ return ID_STATUS_DND;
+
+ case GG_STATUS_FFC:
+ case GG_STATUS_FFC_DESCR:
+ return ID_STATUS_FREECHAT;
+
+ case GG_STATUS_INVISIBLE:
+ case GG_STATUS_INVISIBLE_DESCR:
+ return ID_STATUS_INVISIBLE;
+
+ case GG_STATUS_BLOCKED:
+ return ID_STATUS_NA;
+
+ default:
+ return ID_STATUS_OFFLINE;
+ }
+}
+
+////////////////////////////////////////////////////////////
+// Called when contact status is changed
+void gg_changecontactstatus(GGPROTO *gg, uin_t uin, int status, const char *idescr, int time, uint32_t remote_ip, uint16_t remote_port, uint32_t version)
+{
+ HANDLE hContact = gg_getcontact(gg, uin, 0, 0, NULL);
+
+ // Check if contact is on list
+ if(!hContact) return;
+
+ // Write contact status
+ DBWriteContactSettingWord(hContact, GG_PROTO, GG_KEY_STATUS, (WORD)status_gg2m(gg, status));
+
+ // Check if there's description and if it's not empty
+ if(idescr && *idescr)
+ {
+ gg_netlog(gg, "gg_changecontactstatus(): Saving for %d status descr \"%s\".", uin, idescr);
+ DBWriteContactSettingString(hContact, "CList", GG_KEY_STATUSDESCR, idescr);
+ }
+ else
+ // Remove status if there's nothing
+ DBDeleteContactSetting(hContact, "CList", GG_KEY_STATUSDESCR);
+
+ // Store contact ip and port
+ if(remote_ip) DBWriteContactSettingDword(hContact, GG_PROTO, GG_KEY_CLIENTIP, (DWORD) swap32(remote_ip));
+ if(remote_port) DBWriteContactSettingWord(hContact, GG_PROTO, GG_KEY_CLIENTPORT, (WORD) remote_port);
+ if(version)
+ {
+ char sversion[48];
+ DBWriteContactSettingDword(hContact, GG_PROTO, GG_KEY_CLIENTVERSION, (DWORD) version);
+ mir_snprintf(sversion, sizeof(sversion), "%sGadu-Gadu %s", (version & 0x00ffffff) > 0x2b ? "Nowe " : "", gg_version2string(version));
+ DBWriteContactSettingString(hContact, GG_PROTO, "MirVer", sversion);
+ }
+}
+
+////////////////////////////////////////////////////////////
+// Returns GG client version string from packet version
+const char *gg_version2string(int v)
+{
+ const char *pstr = "???";
+ v &= 0x00ffffff;
+ switch(v)
+ {
+ case 0x2e:
+ pstr = "8.0 build 8283"; break;
+ case 0x2d:
+ pstr = "8.0 build 4881"; break;
+ case 0x2b:
+ pstr = "< 8.0"; break;
+ case 0x2a:
+ pstr = "7.7 build 3315"; break;
+ case 0x29:
+ pstr = "7.6 build 1688"; break;
+ case 0x28:
+ pstr = "7.5 build 2201"; break;
+ case 0x27:
+ pstr = "7.0 build 22"; break;
+ case 0x26:
+ pstr = "7.0 build 20"; break;
+ case 0x25:
+ pstr = "7.0 build 1"; break;
+ case 0x24:
+ pstr = "6.1 (155) / 7.6 (1359)"; break;
+ case 0x22:
+ pstr = "6.0 build 140"; break;
+ case 0x21:
+ pstr = "6.0 build 133"; break;
+ case 0x20:
+ pstr = "6.0b"; break;
+ case 0x1e:
+ pstr = "5.7b build 121"; break;
+ case 0x1c:
+ pstr = "5.7b"; break;
+ case 0x1b:
+ pstr = "5.0.5"; break;
+ case 0x19:
+ pstr = "5.0.3"; break;
+ case 0x18:
+ pstr = "5.0.0-1"; break;
+ case 0x17:
+ pstr = "4.9.2"; break;
+ case 0x16:
+ pstr = "4.9.1"; break;
+ case 0x15:
+ pstr = "4.8.9"; break;
+ case 0x14:
+ pstr = "4.8.1-3"; break;
+ case 0x11:
+ pstr = "4.6.1-10"; break;
+ case 0x10:
+ pstr = "4.5.15-22"; break;
+ case 0x0f:
+ pstr = "4.5.12"; break;
+ case 0x0b:
+ pstr = "4.0.25-30"; break;
+ default:
+ if (v < 0x0b)
+ pstr = "< 4.0.25";
+ else if (v > 0x2e)
+ pstr = ">= 8.0";
+ break;
+ }
+ return pstr;
+}
diff --git a/protocols/Gadu-Gadu/dialogs.c b/protocols/Gadu-Gadu/dialogs.c
new file mode 100644
index 0000000000..63a87570e6
--- /dev/null
+++ b/protocols/Gadu-Gadu/dialogs.c
@@ -0,0 +1,1043 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2006 Adam Strzelecki <ono+miranda@java.pl>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+
+static INT_PTR CALLBACK gg_genoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static INT_PTR CALLBACK gg_confoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+static INT_PTR CALLBACK gg_advoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+extern INT_PTR CALLBACK gg_userutildlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+////////////////////////////////////////////////////////////////////////////////
+// SetValue
+#define SVS_NORMAL 0
+#define SVS_GENDER 1
+#define SVS_ZEROISUNSPEC 2
+#define SVS_IP 3
+#define SVS_COUNTRY 4
+#define SVS_MONTH 5
+#define SVS_SIGNED 6
+#define SVS_TIMEZONE 7
+#define SVS_GGVERSION 9
+
+static void SetValue(HWND hwndDlg, int idCtrl, HANDLE hContact, char *szModule, char *szSetting, int special, int disableIfUndef)
+{
+ DBVARIANT dbv = {0};
+ char str[80], *pstr = NULL;
+ int unspecified = 0;
+
+ dbv.type = DBVT_DELETED;
+ if (szModule == NULL) unspecified = 1;
+ else unspecified = DBGetContactSettingW(hContact, szModule, szSetting, &dbv);
+ if (!unspecified) {
+ switch (dbv.type) {
+ case DBVT_BYTE:
+ if (special == SVS_GENDER) {
+ if (dbv.cVal == 'M') pstr = Translate("Male");
+ else if (dbv.cVal == 'F') pstr = Translate("Female");
+ else unspecified = 1;
+ }
+ else if (special == SVS_MONTH) {
+ if (dbv.bVal > 0 && dbv.bVal <= 12) {
+ pstr = str;
+ GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SABBREVMONTHNAME1 - 1 + dbv.bVal, str, SIZEOF(str));
+ }
+ else unspecified = 1;
+ }
+ else if (special == SVS_TIMEZONE) {
+ if (dbv.cVal == -100) unspecified = 1;
+ else {
+ pstr = str;
+ mir_snprintf(str, SIZEOF(str), dbv.cVal ? "GMT%+d:%02d" : "GMT", -dbv.cVal / 2, (dbv.cVal & 1) * 30);
+ }
+ }
+ else {
+ unspecified = (special == SVS_ZEROISUNSPEC && dbv.bVal == 0);
+ pstr = _itoa(special == SVS_SIGNED ? dbv.cVal : dbv.bVal, str, 10);
+ }
+ break;
+ case DBVT_WORD:
+ if (special == SVS_COUNTRY) {
+ pstr = (char*)CallService(MS_UTILS_GETCOUNTRYBYNUMBER, dbv.wVal, 0);
+ unspecified = pstr == NULL;
+ }
+ else {
+ unspecified = (special == SVS_ZEROISUNSPEC && dbv.wVal == 0);
+ pstr = _itoa(special == SVS_SIGNED ? dbv.sVal : dbv.wVal, str, 10);
+ }
+ break;
+ case DBVT_DWORD:
+ unspecified = (special == SVS_ZEROISUNSPEC && dbv.dVal == 0);
+ if (special == SVS_IP) {
+ struct in_addr ia;
+ ia.S_un.S_addr = htonl(dbv.dVal);
+ pstr = inet_ntoa(ia);
+ if (dbv.dVal == 0) unspecified = 1;
+ }
+ else if (special == SVS_GGVERSION)
+ pstr = (char *)gg_version2string(dbv.dVal);
+ else
+ pstr = _itoa(special == SVS_SIGNED ? dbv.lVal : dbv.dVal, str, 10);
+ break;
+ case DBVT_ASCIIZ:
+ unspecified = (special == SVS_ZEROISUNSPEC && dbv.pszVal[0] == '\0');
+ pstr = dbv.pszVal;
+ break;
+ default: pstr = str; lstrcpy(str, "???"); break;
+ }
+ }
+
+ if (disableIfUndef)
+ {
+ EnableWindow(GetDlgItem(hwndDlg, idCtrl), !unspecified);
+ if (unspecified)
+ SetDlgItemText(hwndDlg, idCtrl, Translate("<not specified>"));
+ else
+ SetDlgItemText(hwndDlg, idCtrl, pstr);
+ }
+ else
+ {
+ EnableWindow(GetDlgItem(hwndDlg, idCtrl), TRUE);
+ if (!unspecified)
+ SetDlgItemText(hwndDlg, idCtrl, pstr);
+ }
+ DBFreeVariant(&dbv);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Options Page : Init
+int gg_options_init(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = { 0 };
+
+ odp.cbSize = sizeof(odp);
+ odp.position = 1003000;
+ odp.hInstance = hInstance;
+ odp.pszGroup = LPGEN("Network");
+ odp.pszTitle = GG_PROTONAME;
+ odp.dwInitParam = (LPARAM)gg;
+
+ odp.pszTab = LPGEN("General");
+ odp.pszTemplate = MAKEINTRESOURCE(IDD_OPT_GG_GENERAL);
+ odp.pfnDlgProc = gg_genoptsdlgproc;
+ odp.flags = ODPF_BOLDGROUPS | ODPF_DONTTRANSLATE;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) & odp);
+
+ odp.pszTab = LPGEN("Conference");
+ odp.pszTemplate = MAKEINTRESOURCE(IDD_OPT_GG_CONFERENCE);
+ odp.pfnDlgProc = gg_confoptsdlgproc;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) & odp);
+
+ odp.pszTab = LPGEN("Advanced");
+ odp.pszTemplate = MAKEINTRESOURCE(IDD_OPT_GG_ADVANCED);
+ odp.pfnDlgProc = gg_advoptsdlgproc;
+ odp.flags |= ODPF_EXPERTONLY;
+ CallService(MS_OPT_ADDPAGE, wParam, (LPARAM) & odp);
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Check if new user data has been filled in for specified account
+void gg_checknewuser(GGPROTO* gg, uin_t uin, const char* passwd)
+{
+ char oldpasswd[128];
+ DBVARIANT dbv;
+ uin_t olduin = (uin_t)DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0);
+
+ oldpasswd[0] = '\0';
+ if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv))
+ {
+ if (dbv.pszVal) strcpy(oldpasswd, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+
+ if (uin > 0 && strlen(passwd) > 0 && (uin != olduin || strcmp(oldpasswd, passwd)))
+ gg->check_first_conn = 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Options Page : Proc
+static void gg_optsdlgcheck(HWND hwndDlg)
+{
+ char text[128];
+ GetDlgItemText(hwndDlg, IDC_UIN, text, sizeof(text));
+ if(strlen(text))
+ {
+ GetDlgItemText(hwndDlg, IDC_EMAIL, text, sizeof(text));
+ if(strlen(text))
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CHEMAIL), SW_SHOW);
+ else
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CHEMAIL), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CHPASS), SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LOSTPASS), SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_REMOVEACCOUNT), SW_SHOW);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CREATEACCOUNT), SW_HIDE);
+ }
+ else
+ {
+ ShowWindow(GetDlgItem(hwndDlg, IDC_REMOVEACCOUNT), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LOSTPASS), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CHPASS), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CHEMAIL), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CREATEACCOUNT), SW_SHOW);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Proc: General options dialog
+static INT_PTR CALLBACK gg_genoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ DBVARIANT dbv;
+ DWORD num;
+ GGPROTO *gg = (GGPROTO *)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+
+ TranslateDialogDefault(hwndDlg);
+ if (num = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0))
+ {
+ SetDlgItemText(hwndDlg, IDC_UIN, ditoa(num));
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CREATEACCOUNT), SW_HIDE);
+ }
+ else
+ {
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CHPASS), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_REMOVEACCOUNT), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LOSTPASS), SW_HIDE);
+ }
+ if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) {
+ CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal);
+ SetDlgItemText(hwndDlg, IDC_PASSWORD, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, &dbv)) {
+ SetDlgItemText(hwndDlg, IDC_EMAIL, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else
+ {
+ ShowWindow(GetDlgItem(hwndDlg, IDC_LOSTPASS), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDC_CHPASS), SW_HIDE);
+ }
+
+ CheckDlgButton(hwndDlg, IDC_FRIENDSONLY, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_FRIENDSONLY, GG_KEYDEF_FRIENDSONLY));
+ CheckDlgButton(hwndDlg, IDC_SHOWINVISIBLE, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWINVISIBLE, GG_KEYDEF_SHOWINVISIBLE));
+ CheckDlgButton(hwndDlg, IDC_LEAVESTATUSMSG, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_LEAVESTATUSMSG, GG_KEYDEF_LEAVESTATUSMSG));
+ if(gg->gc_enabled)
+ CheckDlgButton(hwndDlg, IDC_IGNORECONF, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_IGNORECONF, GG_KEYDEF_IGNORECONF));
+ else
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_IGNORECONF), FALSE);
+ CheckDlgButton(hwndDlg, IDC_IGNORECONF, TRUE);
+ }
+ CheckDlgButton(hwndDlg, IDC_IMGRECEIVE, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_IMGRECEIVE, GG_KEYDEF_IMGRECEIVE));
+ CheckDlgButton(hwndDlg, IDC_SHOWLINKS, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWLINKS, GG_KEYDEF_SHOWLINKS));
+ CheckDlgButton(hwndDlg, IDC_ENABLEAVATARS, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LEAVESTATUS), IsDlgButtonChecked(hwndDlg, IDC_LEAVESTATUSMSG));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_IMGMETHOD), IsDlgButtonChecked(hwndDlg, IDC_IMGRECEIVE));
+ SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)Translate("<Last Status>")); // 0
+ SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)Translate("Online")); // ID_STATUS_ONLINE
+ SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)Translate("Away")); // ID_STATUS_AWAY
+ SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)Translate("DND")); // ID_STATUS_DND
+ SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)Translate("Free for chat")); // ID_STATUS_FREECHAT
+ SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)Translate("Invisible")); // ID_STATUS_INVISIBLE
+ switch(DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_LEAVESTATUS, GG_KEYDEF_LEAVESTATUS))
+ {
+ case ID_STATUS_ONLINE:
+ SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 1, 0);
+ break;
+ case ID_STATUS_AWAY:
+ SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 2, 0);
+ break;
+ case ID_STATUS_DND:
+ SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 3, 0);
+ break;
+ case ID_STATUS_FREECHAT:
+ SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 4, 0);
+ break;
+ case ID_STATUS_INVISIBLE:
+ SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 5, 0);
+ break;
+ default:
+ SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_SETCURSEL, 0, 0);
+ }
+
+ SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_ADDSTRING, 0, (LPARAM)Translate("System tray icon"));
+ SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_ADDSTRING, 0, (LPARAM)Translate("Popup window"));
+ SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_ADDSTRING, 0, (LPARAM)Translate("Message with [img] BBCode"));
+ SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_SETCURSEL,
+ DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_IMGMETHOD, GG_KEYDEF_IMGMETHOD), 0);
+ break;
+ }
+ case WM_COMMAND:
+ {
+ if ((LOWORD(wParam) == IDC_UIN || LOWORD(wParam) == IDC_PASSWORD || LOWORD(wParam) == IDC_EMAIL)
+ && (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus()))
+ return 0;
+ switch (LOWORD(wParam)) {
+ case IDC_EMAIL:
+ case IDC_UIN:
+ {
+ gg_optsdlgcheck(hwndDlg);
+ break;
+ }
+ case IDC_LEAVESTATUSMSG:
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LEAVESTATUS), IsDlgButtonChecked(hwndDlg, IDC_LEAVESTATUSMSG));
+ break;
+ }
+ case IDC_IMGRECEIVE:
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_IMGMETHOD), IsDlgButtonChecked(hwndDlg, IDC_IMGRECEIVE));
+ break;
+ }
+ case IDC_LOSTPASS:
+ {
+ char email[128];
+ uin_t uin;
+ GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ GetDlgItemText(hwndDlg, IDC_UIN, email, sizeof(email));
+ uin = atoi(email);
+ GetDlgItemText(hwndDlg, IDC_EMAIL, email, sizeof(email));
+ if(!strlen(email))
+ MessageBox(
+ NULL,
+ Translate("You need to specify your registration e-mail first."),
+ GG_PROTONAME,
+ MB_OK | MB_ICONEXCLAMATION);
+ else if(MessageBox(
+ NULL,
+ Translate("Your password will be sent to your registration e-mail.\nDo you want to continue ?"),
+ GG_PROTONAME,
+ MB_OKCANCEL | MB_ICONQUESTION) == IDOK)
+ gg_remindpassword(gg, uin, email);
+ return FALSE;
+ }
+ case IDC_CREATEACCOUNT:
+ case IDC_REMOVEACCOUNT:
+ if(gg_isonline((GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA)))
+ {
+ if(MessageBox(
+ NULL,
+ Translate("You should disconnect before making any permanent changes with your account.\nDo you want to disconnect now ?"),
+ ((GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA))->proto.m_szModuleName,
+ MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL)
+ break;
+ else
+ gg_disconnect((GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA));
+ }
+ case IDC_CHPASS:
+ case IDC_CHEMAIL:
+ {
+ // Readup data
+ GGUSERUTILDLGDATA dat;
+ int ret;
+ char pass[128], email[128];
+ GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ GetDlgItemText(hwndDlg, IDC_UIN, pass, sizeof(pass));
+ dat.uin = atoi(pass);
+ GetDlgItemText(hwndDlg, IDC_PASSWORD, pass, sizeof(pass));
+ GetDlgItemText(hwndDlg, IDC_EMAIL, email, sizeof(email));
+ dat.pass = pass;
+ dat.email = email;
+ dat.gg = gg;
+ if(LOWORD(wParam) == IDC_CREATEACCOUNT)
+ {
+ dat.mode = GG_USERUTIL_CREATE;
+ ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CREATEACCOUNT), hwndDlg, gg_userutildlgproc, (LPARAM)&dat);
+ }
+ else if(LOWORD(wParam) == IDC_CHPASS)
+ {
+ dat.mode = GG_USERUTIL_PASS;
+ ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CHPASS), hwndDlg, gg_userutildlgproc, (LPARAM)&dat);
+ }
+ else if(LOWORD(wParam) == IDC_CHEMAIL)
+ {
+ dat.mode = GG_USERUTIL_EMAIL;
+ ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CHEMAIL), hwndDlg, gg_userutildlgproc, (LPARAM)&dat);
+ }
+ else
+ {
+ dat.mode = GG_USERUTIL_REMOVE;
+ ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_REMOVEACCOUNT), hwndDlg, gg_userutildlgproc, (LPARAM)&dat);
+ }
+
+ if(ret == IDOK)
+ {
+ DBVARIANT dbv;
+ DWORD num;
+ GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ // Show reload required window
+ ShowWindow(GetDlgItem(hwndDlg, IDC_RELOADREQD), SW_SHOW);
+
+ // Update uin
+ if (num = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0))
+ SetDlgItemText(hwndDlg, IDC_UIN, ditoa(num));
+ else
+ SetDlgItemText(hwndDlg, IDC_UIN, "");
+
+ // Update password
+ if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) {
+ CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal);
+ SetDlgItemText(hwndDlg, IDC_PASSWORD, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else
+ SetDlgItemText(hwndDlg, IDC_PASSWORD, "");
+
+ // Update e-mail
+ if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, &dbv)) {
+ SetDlgItemText(hwndDlg, IDC_EMAIL, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else
+ SetDlgItemText(hwndDlg, IDC_EMAIL, "");
+
+ // Update links
+ gg_optsdlgcheck(hwndDlg);
+
+ // Remove details
+ if(LOWORD(wParam) != IDC_CHPASS && LOWORD(wParam) != IDC_CHEMAIL)
+ {
+ DBDeleteContactSetting(NULL, GG_PROTO, GG_KEY_NICK);
+ DBDeleteContactSetting(NULL, GG_PROTO, "NickName");
+ DBDeleteContactSetting(NULL, GG_PROTO, "City");
+ DBDeleteContactSetting(NULL, GG_PROTO, "FirstName");
+ DBDeleteContactSetting(NULL, GG_PROTO, "LastName");
+ DBDeleteContactSetting(NULL, GG_PROTO, "FamilyName");
+ DBDeleteContactSetting(NULL, GG_PROTO, "CityOrigin");
+ DBDeleteContactSetting(NULL, GG_PROTO, "Age");
+ DBDeleteContactSetting(NULL, GG_PROTO, "BirthYear");
+ DBDeleteContactSetting(NULL, GG_PROTO, "Gender");
+ }
+ }
+ }
+ break;
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR) lParam)->code) {
+ case PSN_APPLY:
+ {
+ GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ int status_flags = GG_STATUS_FLAG_UNKNOWN;
+ char str[128];
+ uin_t uin;
+
+ // Write Gadu-Gadu number & password
+ GetDlgItemText(hwndDlg, IDC_UIN, str, sizeof(str));
+ uin = atoi(str);
+ GetDlgItemText(hwndDlg, IDC_PASSWORD, str, sizeof(str));
+ CallService(MS_DB_CRYPT_ENCODESTRING, sizeof(str), (LPARAM) str);
+ gg_checknewuser(gg, uin, str);
+ DBWriteContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, uin);
+ DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, str);
+
+ // Write Gadu-Gadu email
+ GetDlgItemText(hwndDlg, IDC_EMAIL, str, sizeof(str));
+ DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, str);
+
+ // Write checkboxes
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_FRIENDSONLY, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_FRIENDSONLY));
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWINVISIBLE, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWINVISIBLE));
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_LEAVESTATUSMSG, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_LEAVESTATUSMSG));
+ if (gg->gc_enabled)
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_IGNORECONF, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_IGNORECONF));
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_IMGRECEIVE, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_IMGRECEIVE));
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWLINKS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWLINKS));
+ if (IsDlgButtonChecked(hwndDlg, IDC_SHOWLINKS))
+ status_flags |= GG_STATUS_FLAG_SPAM;
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_change_status_flags(gg->sess, status_flags);
+ LeaveCriticalSection(&gg->sess_mutex);
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ENABLEAVATARS));
+
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_IMGMETHOD,
+ (BYTE)SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_GETCURSEL, 0, 0));
+
+ // Write leave status
+ switch(SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_GETCURSEL, 0, 0))
+ {
+ case 1:
+ DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_LEAVESTATUS, ID_STATUS_ONLINE);
+ break;
+ case 2:
+ DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_LEAVESTATUS, ID_STATUS_AWAY);
+ break;
+ case 3:
+ DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_LEAVESTATUS, ID_STATUS_DND);
+ break;
+ case 4:
+ DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_LEAVESTATUS, ID_STATUS_FREECHAT);
+ break;
+ case 5:
+ DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_LEAVESTATUS, ID_STATUS_INVISIBLE);
+ break;
+ default:
+ DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_LEAVESTATUS, GG_KEYDEF_LEAVESTATUS);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Proc: Conference options dialog
+static INT_PTR CALLBACK gg_confoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ DWORD num;
+ GGPROTO *gg = (GGPROTO *)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+
+ TranslateDialogDefault(hwndDlg);
+ SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_ADDSTRING, 0, (LPARAM)Translate("Allow"));
+ SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_ADDSTRING, 0, (LPARAM)Translate("Ask"));
+ SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_ADDSTRING, 0, (LPARAM)Translate("Ignore"));
+ SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_SETCURSEL,
+ DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_TOTAL, GG_KEYDEF_GC_POLICY_TOTAL), 0);
+
+ if (num = DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_COUNT_TOTAL, GG_KEYDEF_GC_COUNT_TOTAL))
+ SetDlgItemText(hwndDlg, IDC_GC_COUNT_TOTAL, ditoa(num));
+
+ SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_ADDSTRING, 0, (LPARAM)Translate("Allow"));
+ SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_ADDSTRING, 0, (LPARAM)Translate("Ask"));
+ SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_ADDSTRING, 0, (LPARAM)Translate("Ignore"));
+ SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_SETCURSEL,
+ DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_UNKNOWN, GG_KEYDEF_GC_POLICY_UNKNOWN), 0);
+
+ if (num = DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_COUNT_UNKNOWN, GG_KEYDEF_GC_COUNT_UNKNOWN))
+ SetDlgItemText(hwndDlg, IDC_GC_COUNT_UNKNOWN, ditoa(num));
+
+ SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_ADDSTRING, 0, (LPARAM)Translate("Allow"));
+ SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_ADDSTRING, 0, (LPARAM)Translate("Ask"));
+ SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_ADDSTRING, 0, (LPARAM)Translate("Ignore"));
+ SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_SETCURSEL,
+ DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_DEFAULT, GG_KEYDEF_GC_POLICY_DEFAULT), 0);
+ break;
+ }
+ case WM_COMMAND:
+ {
+ if ((LOWORD(wParam) == IDC_GC_COUNT_TOTAL || LOWORD(wParam) == IDC_GC_COUNT_UNKNOWN)
+ && (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus()))
+ return 0;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR) lParam)->code) {
+ case PSN_APPLY:
+ {
+ char str[128];
+ GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ // Write groupchat policy
+ DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_TOTAL,
+ (WORD)SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_GETCURSEL, 0, 0));
+ DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_UNKNOWN,
+ (WORD)SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_GETCURSEL, 0, 0));
+ DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_DEFAULT,
+ (WORD)SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_GETCURSEL, 0, 0));
+
+ GetDlgItemText(hwndDlg, IDC_GC_COUNT_TOTAL, str, sizeof(str));
+ DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_COUNT_TOTAL, (WORD)atoi(str));
+ GetDlgItemText(hwndDlg, IDC_GC_COUNT_UNKNOWN, str, sizeof(str));
+ DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_COUNT_UNKNOWN, (WORD)atoi(str));
+
+ break;
+ }
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Proc: Advanced options dialog
+static INT_PTR CALLBACK gg_advoptsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ DBVARIANT dbv;
+ DWORD num;
+ GGPROTO *gg = (GGPROTO *)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+
+ TranslateDialogDefault(hwndDlg);
+ if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_SERVERHOSTS, &dbv)) {
+ SetDlgItemText(hwndDlg, IDC_HOST, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else
+ SetDlgItemText(hwndDlg, IDC_HOST, GG_KEYDEF_SERVERHOSTS);
+
+ CheckDlgButton(hwndDlg, IDC_KEEPALIVE, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_KEEPALIVE, GG_KEYDEF_KEEPALIVE));
+ CheckDlgButton(hwndDlg, IDC_SHOWCERRORS, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWCERRORS, GG_KEYDEF_SHOWCERRORS));
+ CheckDlgButton(hwndDlg, IDC_ARECONNECT, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ARECONNECT, GG_KEYDEF_ARECONNECT));
+ CheckDlgButton(hwndDlg, IDC_MSGACK, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_MSGACK, GG_KEYDEF_MSGACK));
+ CheckDlgButton(hwndDlg, IDC_MANUALHOST, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_MANUALHOST, GG_KEYDEF_MANUALHOST));
+ CheckDlgButton(hwndDlg, IDC_SSLCONN, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_SSLCONN, GG_KEYDEF_SSLCONN));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HOST), IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PORT), IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST));
+
+ CheckDlgButton(hwndDlg, IDC_DIRECTCONNS, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_DIRECTCONNS, GG_KEYDEF_DIRECTCONNS));
+ if (num = DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_DIRECTPORT, GG_KEYDEF_DIRECTPORT))
+ SetDlgItemText(hwndDlg, IDC_DIRECTPORT, ditoa(num));
+ CheckDlgButton(hwndDlg, IDC_FORWARDING, DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_FORWARDING, GG_KEYDEF_FORWARDING));
+ if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_FORWARDHOST, &dbv)) {
+ SetDlgItemText(hwndDlg, IDC_FORWARDHOST, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ if (num = DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_FORWARDPORT, GG_KEYDEF_FORWARDPORT))
+ SetDlgItemText(hwndDlg, IDC_FORWARDPORT, ditoa(num));
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DIRECTPORT), IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDING), IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDPORT), IsDlgButtonChecked(hwndDlg, IDC_FORWARDING) && IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDHOST), IsDlgButtonChecked(hwndDlg, IDC_FORWARDING) && IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS));
+ break;
+ }
+ case WM_COMMAND:
+ {
+ if ((LOWORD(wParam) == IDC_DIRECTPORT || LOWORD(wParam) == IDC_FORWARDHOST || LOWORD(wParam) == IDC_FORWARDPORT)
+ && (HIWORD(wParam) != EN_CHANGE || (HWND) lParam != GetFocus()))
+ return 0;
+ switch (LOWORD(wParam)) {
+ case IDC_MANUALHOST:
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_HOST), IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_PORT), IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST));
+ ShowWindow(GetDlgItem(hwndDlg, IDC_RELOADREQD), SW_SHOW);
+ break;
+ }
+ case IDC_DIRECTCONNS:
+ case IDC_FORWARDING:
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_DIRECTPORT), IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDING), IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDPORT), IsDlgButtonChecked(hwndDlg, IDC_FORWARDING) && IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_FORWARDHOST), IsDlgButtonChecked(hwndDlg, IDC_FORWARDING) && IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS));
+ ShowWindow(GetDlgItem(hwndDlg, IDC_RELOADREQD), SW_SHOW);
+ break;
+ }
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ case WM_NOTIFY:
+ {
+ switch (((LPNMHDR) lParam)->code) {
+ case PSN_APPLY:
+ {
+ char str[512];
+ GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_KEEPALIVE, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_KEEPALIVE));
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_SHOWCERRORS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWCERRORS));
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_ARECONNECT, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ARECONNECT));
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_MSGACK, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_MSGACK));
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_MANUALHOST, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST));
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_SSLCONN, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SSLCONN));
+
+ // Transfer settings
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_DIRECTCONNS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS));
+ DBWriteContactSettingByte(NULL, GG_PROTO, GG_KEY_FORWARDING, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_FORWARDING));
+
+ // Write custom servers
+ GetDlgItemText(hwndDlg, IDC_HOST, str, sizeof(str));
+ DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_SERVERHOSTS, str);
+
+ // Write direct port
+ GetDlgItemText(hwndDlg, IDC_DIRECTPORT, str, sizeof(str));
+ DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_DIRECTPORT, (WORD)atoi(str));
+ // Write forwarding host
+ GetDlgItemText(hwndDlg, IDC_FORWARDHOST, str, sizeof(str));
+ DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_FORWARDHOST, str);
+ GetDlgItemText(hwndDlg, IDC_FORWARDPORT, str, sizeof(str));
+ DBWriteContactSettingWord(NULL, GG_PROTO, GG_KEY_FORWARDPORT, (WORD)atoi(str));
+ break;
+ }
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Info Page : Data
+struct GGDETAILSDLGDATA
+{
+ GGPROTO *gg;
+ HANDLE hContact;
+ int disableUpdate;
+ int updating;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Info Page : Proc
+static INT_PTR CALLBACK gg_detailsdlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ struct GGDETAILSDLGDATA *dat = (struct GGDETAILSDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ TranslateDialogDefault(hwndDlg);
+ dat = (struct GGDETAILSDLGDATA *)mir_alloc(sizeof(struct GGDETAILSDLGDATA));
+ dat->hContact=(HANDLE)lParam;
+ dat->disableUpdate = FALSE;
+ dat->updating = FALSE;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+ // Add genders
+ if(!dat->hContact)
+ {
+ SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)_T("")); // 0
+ SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)Translate("Female")); // 1
+ SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)Translate("Male")); // 2
+ }
+ break;
+ }
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->idFrom)
+ {
+ case 0:
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case PSN_PARAMCHANGED:
+ {
+ dat->gg = (GGPROTO *)((LPPSHNOTIFY)lParam)->lParam;
+ break;
+ }
+ case PSN_INFOCHANGED:
+ {
+ char *szProto;
+ HANDLE hContact = (HANDLE)((LPPSHNOTIFY)lParam)->lParam;
+ GGPROTO *gg = dat->gg;
+
+ // Show updated message
+ if(dat && dat->updating)
+ {
+ MessageBox(
+ NULL,
+ Translate("Your details has been uploaded to the public directory."),
+ GG_PROTONAME,
+ MB_OK | MB_ICONINFORMATION
+ );
+ dat->updating = FALSE;
+ break;
+ }
+
+ if (hContact == NULL)
+ szProto = GG_PROTO;
+ else
+ szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ if (szProto == NULL)
+ break;
+
+ // Disable when updating
+ if(dat) dat->disableUpdate = TRUE;
+
+ SetValue(hwndDlg, IDC_UIN, hContact, szProto, GG_KEY_UIN, 0, hContact != NULL);
+ SetValue(hwndDlg, IDC_REALIP, hContact, szProto, GG_KEY_CLIENTIP, SVS_IP, hContact != NULL);
+ SetValue(hwndDlg, IDC_PORT, hContact, szProto, GG_KEY_CLIENTPORT, SVS_ZEROISUNSPEC, hContact != NULL);
+ SetValue(hwndDlg, IDC_VERSION, hContact, szProto, GG_KEY_CLIENTVERSION, SVS_GGVERSION, hContact != NULL);
+
+ SetValue(hwndDlg, IDC_FIRSTNAME, hContact, szProto, "FirstName", SVS_NORMAL, hContact != NULL);
+ SetValue(hwndDlg, IDC_LASTNAME, hContact, szProto, "LastName", SVS_NORMAL, hContact != NULL);
+ SetValue(hwndDlg, IDC_NICKNAME, hContact, szProto, "NickName", SVS_NORMAL, hContact != NULL);
+ SetValue(hwndDlg, IDC_BIRTHYEAR, hContact, szProto, "BirthYear", SVS_ZEROISUNSPEC, hContact != NULL);
+ SetValue(hwndDlg, IDC_CITY, hContact, szProto, "City", SVS_NORMAL, hContact != NULL);
+ SetValue(hwndDlg, IDC_FAMILYNAME, hContact, szProto, "FamilyName", SVS_NORMAL, hContact != NULL);
+ SetValue(hwndDlg, IDC_CITYORIGIN, hContact, szProto, "CityOrigin", SVS_NORMAL, hContact != NULL);
+
+ if (hContact)
+ {
+ SetValue(hwndDlg, IDC_GENDER, hContact, szProto, "Gender", SVS_GENDER, hContact != NULL);
+ SetValue(hwndDlg, IDC_STATUSDESCR, hContact, "CList", GG_KEY_STATUSDESCR, SVS_NORMAL, hContact != NULL);
+ }
+ else switch((char)DBGetContactSettingByte(hContact, GG_PROTO, "Gender", (BYTE)'?'))
+ {
+ case 'F':
+ SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_SETCURSEL, 1, 0);
+ break;
+ case 'M':
+ SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_SETCURSEL, 2, 0);
+ break;
+ default:
+ SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_SETCURSEL, 0, 0);
+ }
+
+ // Disable when updating
+ if(dat) dat->disableUpdate = FALSE;
+ break;
+ }
+ }
+ break;
+ }
+ break;
+ case WM_COMMAND:
+ if (dat && !dat->hContact && LOWORD(wParam) == IDC_SAVE && HIWORD(wParam) == BN_CLICKED)
+ {
+ // Save user data
+ char text[256];
+ gg_pubdir50_t req;
+ GGPROTO *gg = dat->gg;
+
+ if (!gg_isonline(gg))
+ {
+ MessageBox(NULL,
+ Translate("You have to be logged in before you can change your details."),
+ GG_PROTONAME, MB_OK | MB_ICONSTOP
+ );
+ break;
+ }
+
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SAVE), FALSE);
+
+ req = gg_pubdir50_new(GG_PUBDIR50_WRITE);
+
+ GetDlgItemText(hwndDlg, IDC_FIRSTNAME, text, sizeof(text));
+ if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, text);
+
+ GetDlgItemText(hwndDlg, IDC_LASTNAME, text, sizeof(text));
+ if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, text);
+
+ GetDlgItemText(hwndDlg, IDC_NICKNAME, text, sizeof(text));
+ if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, text);
+
+ GetDlgItemText(hwndDlg, IDC_CITY, text, sizeof(text));
+ if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_CITY, text);
+
+ // Gadu-Gadu Female <-> Male
+ switch(SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_GETCURSEL, 0, 0))
+ {
+ case 1:
+ gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_SET_FEMALE);
+ break;
+ case 2:
+ gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_SET_MALE);
+ break;
+ default:
+ gg_pubdir50_add(req, GG_PUBDIR50_GENDER, "");
+ }
+
+ GetDlgItemText(hwndDlg, IDC_BIRTHYEAR, text, sizeof(text));
+ if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_BIRTHYEAR, text);
+
+ GetDlgItemText(hwndDlg, IDC_FAMILYNAME, text, sizeof(text));
+ if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_FAMILYNAME, text);
+
+ GetDlgItemText(hwndDlg, IDC_CITYORIGIN, text, sizeof(text));
+ if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_FAMILYCITY, text);
+
+ // Run update
+ gg_pubdir50_seq_set(req, GG_SEQ_CHINFO);
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_pubdir50(gg->sess, req);
+ LeaveCriticalSection(&gg->sess_mutex);
+ dat->updating = TRUE;
+
+ gg_pubdir50_free(req);
+ }
+
+ if(dat && !dat->hContact && !dat->disableUpdate && (HIWORD(wParam) == EN_CHANGE && (
+ LOWORD(wParam) == IDC_NICKNAME || LOWORD(wParam) == IDC_FIRSTNAME || LOWORD(wParam) == IDC_LASTNAME || LOWORD(wParam) == IDC_FAMILYNAME ||
+ LOWORD(wParam) == IDC_CITY || LOWORD(wParam) == IDC_CITYORIGIN || LOWORD(wParam) == IDC_BIRTHYEAR) ||
+ HIWORD(wParam) == CBN_SELCHANGE && LOWORD(wParam) == IDC_GENDER))
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SAVE), TRUE);
+
+ switch(LOWORD(wParam))
+ {
+ case IDCANCEL:
+ SendMessage(GetParent(hwndDlg),msg,wParam,lParam);
+ break;
+ }
+ break;
+ case WM_DESTROY:
+ if(dat) mir_free(dat);
+ break;
+ }
+ return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Info Page : Init
+int gg_details_init(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, lParam, 0);
+ if ((szProto == NULL || strcmp(szProto, GG_PROTO)) && lParam || lParam && DBGetContactSettingByte((HANDLE)lParam, GG_PROTO, "ChatRoom", 0))
+ return 0;
+
+ // Here goes init
+ {
+ OPTIONSDIALOGPAGE odp = {0};
+
+ odp.cbSize = sizeof(odp);
+ odp.flags = ODPF_DONTTRANSLATE;
+ odp.hInstance = hInstance;
+ odp.pfnDlgProc = gg_detailsdlgproc;
+ odp.position = -1900000000;
+ odp.pszTemplate = ((HANDLE)lParam != NULL) ? MAKEINTRESOURCE(IDD_INFO_GG) : MAKEINTRESOURCE(IDD_CHINFO_GG);
+ odp.ptszTitle = GG_PROTONAME;
+ odp.dwInitParam = (LPARAM)gg;
+ CallService(MS_USERINFO_ADDPAGE, wParam, (LPARAM)&odp);
+ }
+
+ // Start search for user data
+ if((HANDLE)lParam == NULL)
+ gg_getinfo((PROTO_INTERFACE *)gg, NULL, 0);
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////
+// Proc: Account manager options dialog
+INT_PTR CALLBACK gg_acc_mgr_guidlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+////////////////////////////////////////////////////////////////////////////////////////////
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ {
+ DBVARIANT dbv;
+ DWORD num;
+ GGPROTO *gg = (GGPROTO *)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+
+ TranslateDialogDefault(hwndDlg);
+ if (num = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0))
+ SetDlgItemText(hwndDlg, IDC_UIN, ditoa(num));
+ if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) {
+ CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal);
+ SetDlgItemText(hwndDlg, IDC_PASSWORD, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, &dbv)) {
+ SetDlgItemText(hwndDlg, IDC_EMAIL, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ break;
+ }
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam)) {
+ case IDC_CREATEACCOUNT:
+ {
+ // Readup data
+ GGUSERUTILDLGDATA dat;
+ int ret;
+ char pass[128], email[128];
+ GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ GetDlgItemText(hwndDlg, IDC_UIN, pass, sizeof(pass));
+ dat.uin = atoi(pass);
+ GetDlgItemText(hwndDlg, IDC_PASSWORD, pass, sizeof(pass));
+ GetDlgItemText(hwndDlg, IDC_EMAIL, email, sizeof(email));
+ dat.pass = pass;
+ dat.email = email;
+ dat.gg = gg;
+ dat.mode = GG_USERUTIL_CREATE;
+ ret = DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_CREATEACCOUNT), hwndDlg, gg_userutildlgproc, (LPARAM)&dat);
+
+ if(ret == IDOK)
+ {
+ DBVARIANT dbv;
+ DWORD num;
+ GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ // Show reload required window
+ ShowWindow(GetDlgItem(hwndDlg, IDC_RELOADREQD), SW_SHOW);
+
+ // Update uin
+ if (num = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0))
+ SetDlgItemText(hwndDlg, IDC_UIN, ditoa(num));
+ else
+ SetDlgItemText(hwndDlg, IDC_UIN, "");
+
+ // Update password
+ if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) {
+ CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal);
+ SetDlgItemText(hwndDlg, IDC_PASSWORD, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else
+ SetDlgItemText(hwndDlg, IDC_PASSWORD, "");
+
+ // Update e-mail
+ if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, &dbv)) {
+ SetDlgItemText(hwndDlg, IDC_EMAIL, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else
+ SetDlgItemText(hwndDlg, IDC_EMAIL, "");
+ }
+ }
+
+ }
+ break;
+ }
+ case WM_NOTIFY:
+ {
+ switch(((LPNMHDR)lParam)->idFrom)
+ {
+ case 0:
+ switch (((LPNMHDR) lParam)->code) {
+ case PSN_APPLY:
+ {
+ GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ char str[128];
+ uin_t uin;
+
+ // Write Gadu-Gadu number & password
+ GetDlgItemText(hwndDlg, IDC_UIN, str, sizeof(str));
+ uin = atoi(str);
+ GetDlgItemText(hwndDlg, IDC_PASSWORD, str, sizeof(str));
+ CallService(MS_DB_CRYPT_ENCODESTRING, sizeof(str), (LPARAM) str);
+ gg_checknewuser(gg, uin, str);
+ DBWriteContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, uin);
+ DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, str);
+
+ // Write Gadu-Gadu email
+ GetDlgItemText(hwndDlg, IDC_EMAIL, str, sizeof(str));
+ DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, str);
+ }
+ }
+ }
+ break;
+ }
+ }
+ return FALSE;
+}
diff --git a/protocols/Gadu-Gadu/docs/build-howto.txt b/protocols/Gadu-Gadu/docs/build-howto.txt
new file mode 100644
index 0000000000..ffb8f1c3fe
--- /dev/null
+++ b/protocols/Gadu-Gadu/docs/build-howto.txt
@@ -0,0 +1,26 @@
+///////////////////////////////////////////////////////////////////
+// Gadu-Gadu Protocol Plugin for Miranda IM
+// Author: Adam Strzelecki <ono+miranda@java.pl>
+///////////////////////////////////////////////////////////////////
+
+BUILD HOWTO
+-----------
+
+This plugin was tested with free MingW gcc-3.2 compiler.
+There is no problem to use any other compiler like Visual C++,
+but you have to make a Visual Studio project by your self.
+
+Before you build it with MingW.. make sure that compiler and
+other utilities like "make" are in you PATH.
+
+Build libgadu on libgadu/ subdirectory:
+make
+
+To build release plugin run:
+make
+
+To compile debug version run:
+make DEBUG=1
+
+or use build.bat to make both versions.
+
diff --git a/protocols/Gadu-Gadu/docs/gg-license.txt b/protocols/Gadu-Gadu/docs/gg-license.txt
new file mode 100644
index 0000000000..7f1161073d
--- /dev/null
+++ b/protocols/Gadu-Gadu/docs/gg-license.txt
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/protocols/Gadu-Gadu/docs/gg-readme.txt b/protocols/Gadu-Gadu/docs/gg-readme.txt
new file mode 100644
index 0000000000..1d2255f6ec
--- /dev/null
+++ b/protocols/Gadu-Gadu/docs/gg-readme.txt
@@ -0,0 +1,338 @@
+///////////////////////////////////////////////////////////////////
+// Gadu-Gadu Protocol Plugin for Miranda IM
+// Based on unix ekg client functionlity and libgadu
+//
+// Author: Adam Strzelecki <ono+miranda@java.pl>
+//
+// Thanks: Santithorn Bunchua "KeH", Rober Rainwater,
+// libgadu Authors <http://dev.null.pl/ekg/>,
+// Liquid (Image send/recv), The Leech (Proxy fixes)
+// MirandaIM Polish Forum
+// Angeli-Ka (Gadu-Gadu plugin icons)
+///////////////////////////////////////////////////////////////////
+
+1. About
+--------
+The Gadu-Gadu plugin provides functionality of original Gadu-Gadu
+client extended with extra features available only for Miranda IM
+users. Gadu-Gadu is most popular (competitive to ICQ) instant
+messaging system in Poland. Now supports encrypted secure SSL/TLS
+connections.
+
+2. Features
+-----------
+ - Send/Receive messages
+ - Add/Delete users
+ - Public catalog search
+ - Public catalog info changing
+ - Import/export/remove from server and text file
+ - Status messages support (offline messages too)
+ - Visibility/ignore lists
+ - Password changing
+ - Password remind
+ - Extended public catalog search
+ - HTTP proxy support (uses only Netlib settings)
+ - Create/Remove Gadu-Gadu account
+ - File transfer
+ - Image receving / sending (by Liquid)
+ - Multiple plugin instances/accounts:
+ <name>.dll or <name>debug.dll becomes proto name and id
+ Warning: <name>.dll or <name>debug.dll is treated as
+ the same proto and name is case insensitive.
+ - 1st user mail & phone used for import/export
+ - SSL/TLS secure connections
+ - Groupchat/conferencing (Miranda IM version 0.4 or higher)
+
+Note: For secure connections this plugin requires OpenSSL DLL
+ libraries to be present in system folder or Miranda folder.
+ These libraries can be downloaded from:
+ - http://www.miranda-im.pl/download/misc/
+ - http://www.openssl.org/.
+ If DLL files are not present plugin works only with plain
+ unsecure connections.
+
+4. TODO
+-------
+ - Animated GIFs support (image sending)
+ - Full Netlib integration
+ - Integration with Miranda Image services
+ - Voice chat
+
+5. History
+----------
+0.0.4.6: 2007-07-04
+ - Fix: Message doubling (i.e. with MetaContacts), thanks to Scott Ellis
+ - Fix: Default icon setting problem
+ - Rewritten image & token send & reception using Miranda Image services
+ (This requires latest Miranda version in order to run)
+ - Fix: Window size for big images on sending & reception
+0.0.4.5: 2007-06-20
+ - Angeli-Ka icons are now default Gadu-Gadu icons
+ - Protocol status icons are now built in the Plugin, so taken by default
+ - IcoLib support, now you can redefine all GG icons including "blocked" icon
+ - Gadu-Gadu libgadu update to support fully DCC7 transfers and GG7.7 features
+ (has to be tested, if it really works)
+ - Some safety internal fixes
+0.0.4.2: 2007-02-22
+ - Binary release: Recompiled to support new NETLIB settings structure.
+ !!Warning!! Plugin requires now Miranda 0.6.1+ for proxy support.
+0.0.4.1: 2006-12-20
+ - Fix: 512 characters for settings such as custom server list
+ - Fix: Detection of Gadu-Gadu versions upto 7.6
+ - Plugin now writes MirVer, so all tooltip plugins can show contact client type
+ and version string.
+0.0.4.0: 2006-10-23
+ - Fix: Messages always have NOW timestamps unless they are OFFLINE.
+ Online messages won't be shifted in the order even user has clockskew.
+0.0.3.9: 2006-07-29
+ - Configuration was put into tabs
+ - Fix: Some of the configuration items were not triggering [Apply] properly.
+0.0.3.8: 2006-06-26
+ - Fix: It seems that GG servers now properly handle initial status for GG.
+ This should fix issue of being detected by Inwigilator when connecting with
+ invisible status. (Spotted and fixed thanks to piopawlu)
+ - Fix: New libgadu version 2006.06.21 with fixed some memory leaks
+ - Fix: GG now handles PS_SETAWAYMSG without previously called PS_SETSTATUS.
+ This should fix issue with Watrack that couldn't set the status message
+ for GG protocol. (Spotted and fixed thanks to mruweq)
+ - Fix: We were reading from freed variable after new account registration
+ - Fix: Possible crash when closing debug version
+0.0.3.7: 2005-12-19
+ - Image sending and receiving now uses ImgDecoder if present for PNG images
+ - Fix: String fix for error dialogs' titles
+0.0.3.6: 2005-11-15
+ - Fix: String overflows (injections) that may be caused for example by too long
+ translations or too long incoming filenames
+0.0.3.5: 2005-10-28
+ - Fix: Descriptions beginning with new line were not shown
+ - Fix: Groupchat fixed for database locks
+ - Fix: Upgrade new chat API
+ - Fix: Image reception on system tray was broken
+ - Fix: Image windows are now closed on Miranda exit
+ - Fix: Plugin was writing junk entries to other protocols contacts
+ - Image reception can now be triggered
+ - Images are now scalled to their real size if possible
+ - Small code formatting fixes
+ - SSL connections are disabled now by default
+0.0.3.4: 2005-07-23
+ - Fix: Connection server config was broken
+ - Fix: Password retrieval was broken
+ - Fix: Maximum image size 512KB was not accepted original GG client.
+ Miranda now can receive images upto 255KB from original GG clients.
+ - Fix: Yet another image receive fix, libgadu fixes introduced ghost image
+ messages
+ - Small code formatting fixes
+0.0.3.3: 2005-07-19
+ - Fix: Serious multiple vulnerabilities of libgadu upto ekg version 1.6rc3
+0.0.3.2: 2005-07-14
+ - Fix: libgadu event.c commit version 1.83 by szalik introduced bug that was
+ crashing Miranda on image receive, since it was designed specially for Kadu
+0.0.3.1: 2005-07-12
+ - Hidden db setting "EraOmnix" for being visible as EraOmnix client
+ - Fix: Temporary contacts cannot see our status when having "Friends only"
+ (Features thanks to JacekFH)
+ - Fix: GG thread hanging, not going offline when cannot reconnect
+ after disconnection
+0.0.3.0: 2005-06-27
+ - Status descriptions compatible with MWClist
+ - GG.dll now compiles on Visual C++ 6.0 and Visual C++ 2005
+ - libgadu upgraded to version 20050615
+ - Binaries are now compiled with VC++ 6.0
+ - Fix: Windows 95 & NT problems with GetLongPathName function missing
+0.0.2.9: 2005-04-29
+ - Fix: Status descriptions were not set properly when Miranda wasn't asking
+ for new status. This change may cause incompatibility with status routines
+ different than built-in. Note for misc plugins (mBot, etc.):
+ For avoiding sending gg_setstatus(status, description) twice on
+ miranda_setstatus and miranda_setawaymsg, GG sets status only on
+ miranda_setawaymsg, also this order must be fulfilled to make GG working
+ right with Miranda. (this is how internal module sraway works actually)
+ - When reconnect flag is set and manual server host cycling is on the end of
+ the list, reconnect will go back to the begin
+ (no longer taking address from web at the end)
+ - Fix: Stupid GetModuleFileName returning all lowercase characters from
+ DLL filename > WinNT, now we call GetLongPathName to retrieve valid case
+0.0.2.8: 2005-04-14
+ - Server connection (multi)thread controller (Experimental!)
+ Miranda won't hang anymore when GG is trying to connect & status change is
+ requested (However sometimes on exit it required killing threads (dirty))
+ - Multiple server hostnames (cycling while connecting)
+ - Conference policy setting
+0.0.2.7: 2004-12-26
+ - Gadu-Gadu conferencing support trough chat.dll groupchat plugin
+ (requires > Miranda 0.4)
+0.0.2.6: 2004-12-13
+ - Fix: Notification list should be sent in one piece gg_notify_ex(*uinlist),
+ not one by one gg_add_notify_ex(uin)
+ - Fix: GG.dll uses now internal Miranda safe thread registering
+0.0.2.5: 2004-12-01
+ - Fix: Plugin crashed when reporting token download connection related errors
+ - Fix: Max image size limit changed to 512KB
+ - Option to turn on/off showing contacts that are not on my list but having me
+ on their contact list.
+0.0.2.4: 2004-08-03
+ - Fix: Code of image routines was simplyfied, however needs more care (unstable)
+ - Server host and external forwarding host could be specified as a hostname or
+ IP number (before only IP number was valid). This could be useful for dynamic
+ IPs and DNS mapping services such as dyndns.org.
+0.0.2.3: 2004-07-15
+ - Fix: Proxy authentication fixed (by The Leech)
+ - Images receiving / sending (by Liquid)
+0.0.2.2: 2003-12-20
+ - Fix: Autoreconnect failure, no "Connecting..." message
+ - Fix: Latest M-IM build sends twice SET_OFFLINE when disconnect
+ which caused GG plugin to hang
+0.0.2.1: 2003-11-28
+ - Source: GCC >= 3.2 compiler support
+ - Fix: File transfer crashes, 100% CPU utilization
+ - Fix: Proxy working again (HTTP w/auth)
+ Note: GG works only with HTTP proxies (also with auth) due to limitation of libgadu.
+ Please turn off "Manually specify connection host" before running trough proxy.
+ - Fix: Filesize 135KB - deleted unused icons (thanks to Jacek_FH)
+ - Filetransfer resume support
+ - Ignore incoming conference messages option
+0.0.2.0: 2003-10-27
+ - Gadu-Gadu 6.0 support extended
+ - Token support: Working pass changing, user registration, deletion
+ - Warning: Registration e-mail changing not working (no libgadu implementation)
+ - Server userlist manipulation working again
+0.0.1.9: 2003-10-16
+ - Gadu-Gadu 6.0 (libgadu-CVS) support
+ - Warning: Register, unregister, password change/remind, userlist server manipulation
+ not available for the moment. Need to implement new TOKEN routines introduced in GG6
+ - Warning: SSL still having problems with connection (to be fixed)
+ - Client version info in contact details
+0.0.1.7: 2003-09-xx (Unreleased, tests and fixes)
+0.0.1.7: 2003-07-06
+ - SSL/TLS secure connection to server support (trough OpenSSL)
+ - Option to turn on/off message acknowledgement
+ - Fix: Reconnects when connection is broken on startup only if
+ auto-reconnect option is turned on
+ - Fix: Owner's nickname properly displayed in chat/msg dialogs
+ Note: Use View/Change my details... to load-up nickname.
+ - Fix: Plugin doesn't link to any ssl library but loads it dynamically
+ when OpenSSL dll files exist in system or Miranda folder.
+0.0.1.6: 2003-06-04
+ - Fix: Auto-reconnect do not get 100% cpu anymore when no network (1 sec per trial)
+ - Fix: Blocked icon works again (upx shouldn't compress icons)
+ - Fix: Contact description is now cleared when contact makes it empty
+ - Debug: logged connection errors are more specific now
+0.0.1.5: 2003-04-09
+ - Fix: Multiple instances menu items worked just for first instance
+ - Fix: Auto-reconnect fix, again working
+ - Time deviation support for timestamps (default 5 min)
+ "TimeDeviation" db setting if your clock differs more than 5min from actual time
+ - Better error descriptions on logon failure
+0.0.1.4: 2003-04-04
+ - Fix: User IP numbers in user info shown better (port < 10 means behind firewall)
+ - Fix: %ip% compatible now with tooltip plugin
+ - Fix: Import/Export now uses first user (editable) e-mail and phone number
+ - Fix: Remove all old db uneditable e-mail & phone settings from previous plugins
+ - Debug: Advanced libgadu debug log information displayed in NetLib dump
+ - Filetransfer support with firewall forwarding support (except multiple files)
+ - Multiple instances of plugin supported (see feature list)
+0.0.1.3: 2003-03-31
+ - Fix: Send chat type message instead of normal message
+ - Fix: Next 20 results displayed after searching again
+ - Fix: Public catalog change always writes Male as gender
+ - Extended public catalog search
+ - HTTP proxy support (uses only Netlib settings)
+ - Create/Remove Gadu-Gadu account
+ - Change Gadu-Gadu registration e-mail (requires valid e-mail in configuration)
+0.0.1.2: 2003-03-19
+ - Select status for description after disconnected
+ - Fix: Finally working away msgs based on KeH's Jabber plugin code
+ - Fix: Info page fix for empty "Gender" field
+ - Fix: Import code for groups and subgroups
+ - Removed: "Change status safely" no longer needed
+ - "StartInvisible" db only option to start fully invisible
+0.0.1.1: 2003-03-16
+ - Fix: Male/female fix in public catalog
+0.0.1.0: 2003-03-16
+ - Fix: Public info changing now works
+ - Fix: Status changing bugs
+0.0.0.9: 2003-03-16
+ - Fix: Import bugs
+ - Fix: Status changing bugs (still fighting with)
+0.0.0.8: 2003-03-15
+ - Fix: Multiple main connection thread runned
+ - Fix: Status hangup on Connecting...
+ - Password & e-mail changing
+ - Password remind to e-mail
+ - Owner info changing [NOT WORKING DUE PROTOCOL CHANGES]
+ - Autoreconnect when disconnected unintentionally
+ - Turn on/off connection errors
+ - Leave last status after disconnected
+ - Fix: Double status change on login
+0.0.0.7: 2003-03-14
+ - Fix: Blocked icon fixed
+ - Fix: Safe status changing
+ - Fix: Contacts status set offline when disconnected
+0.0.0.6: 2003-03-13
+ - Fix: Import/export fixes
+ - Name unknown contact with nickname from public catalog
+ if available
+ - Remove from server
+ - Visible/Ignore (blocked) list
+0.0.0.5: 2003-03-13
+ - Contacts info tab
+0.0.0.4: 2003-03-12
+ - Fix: Import from files
+ - Fix: Search and away msg retreival
+0.0.0.3: 2003-03-12
+ - Import/export from server/text file
+ - Fix: Improved stability
+0.0.0.2: 2003-03-11
+ - Improved stability
+ - Status descriptions
+ - Show invisible users
+ - Friends only
+0.0.0.1: (UNRELEASED)
+ - Initial Release
+ - Basic messaging
+
+6. Latest development version:
+------------------------------
+Latest development version is always available at:
+ http://www.miranda-im.org/development/
+
+ - GG.dll - release version
+ - GGdebug.dll - debug version, debug infos in NetLib log
+ - gg-readme.txt - this file with modified changelog
+ - gg-translation-sample.txt - latest partial langpack
+
+7. Bugs
+-------
+ - Before sending reports check if bugs are still present in latest
+ development version
+ - [Polish] Send bug reports to Polish Miranda IM Forum at:
+ http://www.miranda-im.pl/viewforum.php?f=3
+ - [English] Send bug reports to Miranda IM bugtracker at:
+ http://sourceforge.net/tracker/?atid=102179&group_id=2179
+
+8. Discussion & Feature Requests
+--------------------------------
+ - Request features, discuss plugin at Polish Miranda IM Forum:
+ http://www.miranda-im.pl/viewforum.php?f=3
+
+9. Source code
+--------------
+Source code of this plugin is available at Miranda IM SVN repository.
+Consult for details:
+ http://sourceforge.net/svn/?group_id=94142
+
+Copyright (C) 2003-2007 Adam Strzelecki
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
diff --git a/protocols/Gadu-Gadu/docs/gg-translation-sample.txt b/protocols/Gadu-Gadu/docs/gg-translation-sample.txt
new file mode 100644
index 0000000000..faaed2ef54
--- /dev/null
+++ b/protocols/Gadu-Gadu/docs/gg-translation-sample.txt
@@ -0,0 +1,400 @@
+; ** Gadu-Gadu translation strings
+
+; Info page
+[Public Info]
+Katalog publiczny
+[Family name:]
+Nazwisko rodz.:
+[Origin city:]
+Miasto rodz.:
+[Birth year:]
+Rok urodzenia:
+
+; Options
+;[Gadu-Gadu] @ deprecated 0.0.1.4
+;[&Gadu-Gadu] @ deprecated 0.0.1.4
+;[Gadu-Gadu connection] @ deprecated 0.0.1.4
+; Expert short
+[Ex.]
+Eks.
+;[connection] @ deprecated 0.0.5.0
+[%s connection]
+Połączenie %s
+[Friends only]
+Tylko dla przyjaciół
+[Show offline users with status message as invisible in contact list]
+Pokazuj użytkowników z opisem stanu jako niewidocznych
+[Ignore incoming conference messages]
+Ignoruj nadchodzące wiadomości konferencyjne
+;[Automatically pop up the window when a image is received] @ deprecated 0.0.3.4
+[Receive image and after image is received use:]
+Odbieraj nadchodzące obrazki za pomocą metody:
+[System tray icon]
+Ikona zadań systemowych
+[Popup window]
+Wyskakujące okno
+[Message with [img] BBCode]
+Wiadomość z tagiem [img]
+;[Show contacts not on my list having me on their list] @ deprecated 0.0.5.3
+[Show links from unknown contacts]
+Pokazuj linki od nieznajomych
+[Enable avatars]
+Włącz obsługę awatarów
+[Gadu-Gadu Number:]
+Numer Gadu-Gadu:
+[Gadu-Gadu Number]
+Numer Gadu-Gadu
+[Gadu-Gadu User Details]
+Dane użytkownika Gadu-Gadu
+;[Lost Gadu-Gadu Password?] @ deprecated 0.0.1.3
+[Retrieve password]
+Odzyskaj hasło
+[Create new account]
+Utwórz nowe konto
+[Remove account]
+Usuń konto
+[Change password]
+Zmień hasło
+[Change e-mail]
+Zmień e-mail
+[Keep connection alive]
+Utrzymuj połączenie
+[Change status safely]
+Bezpiecznie zmieniaj stan
+[Show connection errors]
+Pokazuj błędy połączenia
+[Automatically reconnect after unintentional disconnection]
+Połącz ponownie automatycznie po niezamierzonym rozłączeniu
+;[Leave last status message after disconnected] << deprecated in 0.0.1.2
+[After disconnection leave away message of status:]
+Po rozłączeniu się zostaw opis następującego stanu:
+[Manually specify connection host]
+Podaj adres hosta serwera ręcznie
+[Manually specify connection servers' hosts]
+Podaj adresy hostów serwerów ręcznie
+;[Use secure SSL connection] @ deprecated 0.0.2.6
+[Host:]
+Host:
+[Use direct connections]
+Używaj połączeń bezpośrednich
+[Use forwarding]
+Używaj forwardowania
+[* new line is separator\n** hostname:port format]
+* nowa linia - separator\n** format nazwahosta:port
+
+; Menu
+[Import List From &Server]
+Importuj listę z &serwera
+[Import List From &Text File...]
+Importuj listę z pliku &tekstowego...
+[&Remove List From Server]
+&Usuń listę z serwera
+[Export List To &Server]
+Eksportuj listę do &serwera
+[Export List To &Text File...]
+Eksportuj listę do pliku &tekstowego...
+[Text files]
+Pliki tekstowe
+
+; Icons
+[Protocol icon]
+Ikona protokołu
+[Import list from server]
+Import listy z serwera
+[Import list from text file]
+Import listy z pliku tekstowego
+[Remove list from server]
+Usunięcie listy z serwera
+[Export list to server]
+Eksport listy do serwera
+[Export list to text file]
+Eksport listy do pliku tekstowego
+[Account settings]
+Ustawienia konta
+[Contact list]
+Lista kontaktów
+;[Blocked to this contact] @ deprecated 0.0.5.2
+[Block user]
+Zablokuj użytkownika
+[Previous image]
+Poprzedni obrazek
+[Next image]
+Następny obrazek
+[Send image]
+Wyślij obrazek
+[Save image]
+Zapisz obrazek
+[Delete image]
+Usuń obrazek
+
+; Errors
+;[Gadu-Gadu Error] << deprecated in 0.0.1.4
+[Connection cannot be established because of error:\n\t%s]
+Połączenie nie może być nawiązane z powodu błędu:\n\t%s
+;Connection specific errors
+[Miranda was unable to resolve the name of the Gadu-Gadu server to its numeric address.]
+Miranda nie mogła ustalić IP serwera Gadu-Gadu na podstawie nazwy hosta.
+[Received invalid server response.]
+Otrzymano niepoprawną odpowiedź od serwera.
+[Cannot establish secure connection.]
+Nie można nawiązać bezpiecznego połączenia.
+[Server disconnected asking you for changing your e-mail.]
+Serwer rozłączył się prosząc o zmianę Twojego adresu e-mail.
+[Too many login attempts with invalid password.]
+Za dużo prób połączeń z podaniem błędnego hasła.
+[Gadu-Gadu servers are now down. Try again later.]
+Serwery Gadu-Gadu są teraz wyłączone. Spróbuj później.
+[Your Gadu-Gadu number and password combination was rejected by the Gadu-Gadu server. Please check login details at M->Options->Network->Gadu-Gadu and try again.]
+Twój numer i hasło zostały odrzucone przez serwer Gadu-Gadu. Sprawdź dane logowania M->Opcje->Sieć->Gadu-Gadu i spróbuj ponownie.
+[Connecting to Gadu-Gadu hub failed.]
+Połączenie z hubem Gadu-Gadu nie udało się.
+; Token stuff
+[Token retrieval failed because of error:\n\t%s]
+Nie można było pobrać tokena z powodu błędu:\n\t%s
+[Could not load token image.]
+Nie można załadować obrazka tokena.
+[Enter token to continue]
+Wprowadź token aby kontynuować
+;[Token]
+;Import export errors
+[You have to be connected before you can import/export contacts from/to server.]
+Połącz się przed importowaniem/eksportowaniem kontaktów z/na serwer.
+[List cannot be exported because of error:\n\t%s]
+Lista nie może być wyeksportowana z powodu błędu:\n\t%s
+[List cannot be exported to file "%s" because of error:\n\t%s]
+Lista nie może być wyeksportowana do pliku "%s" z powodu błędu:\n\t%s
+[List cannot be imported because of error:\n\t%s]
+Lista nie może być zaimportowana z powodu błędu:\n\t%s
+[List cannot be imported from file "%s" because of error:\n\t%s]
+Lista nie może być zaimportowana z pliku "%s" z powodu błędu:\n\t%s
+[List remove successful.]
+Lista została usunięta pomyślnie.
+[List export successful.]
+Lista została wyeksportowana pomyślnie.
+[List import successful.]
+Lista została zaimportowana pomyślnie.
+[contacts]
+kontakty
+[Your password will be sent to your registration e-mail.\nDo you want to continue ?]
+Twoje hasło zostanie wysłane na twój e-mail rejestracyjny.\nChcesz kontynuować ?
+[You need to specify your registration e-mail first.]
+Musisz najpierw wpisać w konfiguracji swój e-mail rejestracyjny.
+[Password could not be reminded because of error:\n\t%s]
+Hasło nie może zostać wysłane z powodu błędu:\n\t%s
+[Password was sent to your e-mail.]
+Hasło zostało wysłane na Twój e-mail.
+;[Your info has been uploaded to public catalog.] @ deprecated since 0.9.30
+[Your details has been uploaded to the public directory.]
+Twoje dane zostały wysłane do katalogu publicznego.
+[You have to be logged in before you can change your details.]
+Musisz być zalogowany, żeby zmienić swoje dane.
+;Password changing
+[Your password cannot be changed because of error:\n\t%s]
+Twoje hasło nie może być zmienione z powodu błędu:\n\t%s
+[Your password has been changed.]
+Twoje hasło zostało zmienione.
+;E-mail changing
+[Your e-mail cannot be changed because of error:\n\t%s]
+Twój e-mail nie może być zmieniony z powodu błędu:\n\t%s
+[Your e-mail has been changed.]
+Twój e-mail został zmieniony.
+;Account registration
+[Cannot register new account because of error:\n\t%s]
+Nie można zarejestrować konta z powodu błędu:\n\t%s
+[Registration rejected]
+Rejestracja została odrzucona
+;[You have registered new account.\nPlease fill up your personal details in "M->Change my details..."] @ deprecated since 0.9.30
+[You have registered new account.\nPlease fill up your personal details in "M->View/Change My Details..."]
+Zarejestrowałeś nowe konto.\nProszę wypełnij informacje o sobie w "M->Pokaż/Zmień Moje Dane..."
+;Account removal
+[Your account cannot be removed because of error:\n\t%s]
+Twoje konto nie może być usunięte z powodu błędu:\n\t%s
+[Bad number or password]
+Zły numer lub hasło
+[Bad old e-mail or password]
+Zły stary e-mail lub hasło
+[Bad e-mail or password]
+Zły e-mail lub hasło
+[Invalid data entered]
+Wprowadzono złe dane
+[Your account has been removed.]
+Twoje konto zostało usunięte.
+;User utils messages
+;[Passwords do not match.] << deprecated in 0.0.1.3
+[Password too short.]
+Hasło za krótkie.
+;[You should disconnect before making any permanent changes with your account.\nDo you want disconnect now ?] << deprecated in 0.0.1.4
+[You should disconnect before making any permanent changes with your account.\nDo you want to disconnect now ?]
+Powinieneś się rozłączyć przed wszelkimi trwałymi zmianami Twojego konta.\nCzy chcesz się rozłączyć ?
+[Server hostname %s is invalid. Using default hostname provided by the network.]
+Adres serwera %s jest nieprawidłowy. Użyty zostanie adres podany przez sieć.
+[External direct connections hostname %s is invalid. Disabling external host forwarding.]
+Adres zewnętrzny dla połączeń bezpośrednich %s jest nieprawidłowy. Forwardowanie zostanie wyłączone.
+
+; Password dialog
+;[Change Gadu-Gadu Password] << deprecated in 0.0.1.3
+[Change Gadu-Gadu password]
+Zmień hasło Gadu-Gadu
+;[Changes current Gadu-Gadu user password.] @ deprecated 0.9.26
+[Change Gadu-Gadu password\nChanges current Gadu-Gadu user password]
+Zmień hasło Gadu-Gadu\nTwoje aktualne hasło zostanie zmienione
+[New password:]
+Nowe hasło:
+[Password:]
+Hasło:
+[Confirm password:]
+Potwierdź hasło:
+
+; E-mail dialog
+[Change Gadu-Gadu e-mail]
+Zmień e-mail Gadu-Gadu
+;[Changes current Gadu-Gadu user e-mail.] @ deprecated 0.9.26
+[Change Gadu-Gadu e-mail\nChanges current Gadu-Gadu user e-mail]
+Zmień e-mail Gadu-Gadu\nTwój aktualny e-mail zostanie zmieniony
+[New e-mail:]
+Nowy e-mail:
+
+; Remove account dialog
+[Remove]
+Usuń
+[Yes, I want to remove my account]
+Tak, chcę usunąć moje konto
+[Remove Gadu-Gadu account]
+Usuń konto Gadu-Gadu
+;[This will remove your Gadu-Gadu account.] @ deprecated 0.9.26
+[Remove Gadu-Gadu account\nThis will remove your Gadu-Gadu account]
+Usuń konto Gadu-Gadu\nTwoje konto zostanie usunięte
+
+; Create account dialog
+[Create Gadu-Gadu account]
+Utwórz konto Gadu-Gadu
+;[This will create new Gadu-Gadu account.] @ deprecated 0.9.26
+[Create Gadu-Gadu account\nThis will create new Gadu-Gadu account]
+Utwórz konto Gadu-Gadu\nZostanie utworzone nowe konto Gadu-Gadu
+
+; Search dialog
+[Age from:]
+Wiek od:
+[to:]
+do:
+[Search online users only]
+Szukaj tylko wśród dostępnych
+
+; Image recv/send
+[&Image]
+&Obrazek
+[Image saved.]
+Zapisano obrazek.
+;[Image received] @ deprecated since 0.9.46
+[Incoming image]
+Nadchodzący obrazek
+[Save image to disk]
+Zapisz obrazek na dysk
+[Image cannot be written to disk.]
+Nie można zapisać obrazka na dysku.
+[Delete image from the list]
+Usuń obrazek z listy
+[Image from %s]
+Obrazek od %s
+[Image for %s]
+Obrazek dla %s
+[Image files (*.bmp,*.gif,*.jpeg,*.jpg,*.png)]
+Pliki obrazów (*.bmp,*.gif,*.jpeg,*.jpg,*.png)
+[Select picture to send]
+Wybierz obrazek do wysłania
+[Could not load image. Image might have unsupported file format.]
+Nie można załadować obrazka. Wybrany plik może mieć nieznany format.
+[Image exceeds maximum allowed size of 255 KB.]
+Rozmiar pliku obrazka przekracza maksymalny dozwolony rozmiar 255 KB.
+
+; Blocking
+[&Unblock]
+Odblokuj
+[&Block]
+Zablokuj
+
+; Conferences
+[Open new conference\nSelect conference participants]
+Otwórz nową konferencję\nWybierz uczestników konferencji
+[Open new conference]
+Otwórz nową konferencję
+;[Select conference participants.] @ deprecated 0.9.26
+[Open &conference...]
+Otwórz &konferencję...
+[&Clear ignored conferences]
+&Wyczyść ignorowane konferencje
+[Clear ignored conferences]
+Wyczyść ignorowane konferencje
+[Me]
+Ja
+[%s initiated the conference.]
+%s zainicjował(a) konferencję.
+[This is my own conference.]
+To jest moja własna konferencja.
+[Conference]
+Konferencja
+[Participants]
+Uczestnicy
+[You have to be connected to open new conference.]
+Musisz być połączony, żeby otworzyć nową konferencję
+[All ignored conferences are now unignored and the conference policy will act again.]
+Wszystkie ignorowane konferencje zostały zresetowane.
+[There are no ignored conferences.]
+Nie ma ignorowanych konferencji.
+[%s has initiated conference with %d participants (%d unknowns).\nDo you want do participate ?]
+%s zainicjował(a) konferencję z %d uczestnikami - %d nieznany(ch).\nChcesz dołączyć do tej konferencji ?
+
+; Conference policy
+[Conference policy]
+Reguły konferencji
+[Ask]
+Pytaj
+[Ignore]
+Ignoruj
+[Allow]
+Zezwól
+[if total participant count greater than:]
+jeśli całkowita liczba uczestników przekracza:
+[if unknown participant count greater than:]
+jeśli liczba nieznanych przekracza:
+[in other case]
+w innym wypadku
+
+; Delete avatar
+[To remove your Gadu-Gadu avatar, you must use the MojaGeneracja.pl website.]
+Aby usunąć swój awatar Gadu-Gadu musisz użyć serwisu MojaGeneracja.pl.
+
+; Concurrent sessions
+[Concurrent sessions]
+Równoległe sesje
+[Concurrent Sessions]
+Równoległe Sesje
+[Concurrent &sessions]
+Równoległe &sesje
+[Concurrent %s Login Sessions\nView information on active concurrent sessions]
+Równoległe Sesje Logowania %s\nWyświetl informacje o aktywnych równoległych sesjach
+[Sign out all sessions]
+Wyloguj wszystkie sesje
+[Client Name]
+Nazwa Klienta
+[IP Address]
+Adres IP
+[Login Time]
+Czas Zalogowania
+[Action]
+Akcja
+[sign out]
+wyloguj
+[There are no active concurrent sessions for this account.]
+Nie ma aktywnych równoległych sesji logowania do tego konta.
+[You have to be logged in to view concurrent sessions.]
+Musisz być zalogowany, aby zobaczyć równoległe sesje.
+[Copy Text]
+Kopiuj Tekst
+;[Whois]
+[You have logged in at another location]
+Właśnie zalogowałeś się w innym miejscu
+[You are logged in at another location]
+Jesteś zalogowany w innym miejscu
+[Unknown client]
+Nieznany klient
diff --git a/protocols/Gadu-Gadu/dynstuff.c b/protocols/Gadu-Gadu/dynstuff.c
new file mode 100644
index 0000000000..d90c4e7908
--- /dev/null
+++ b/protocols/Gadu-Gadu/dynstuff.c
@@ -0,0 +1,613 @@
+/* $Id: dynstuff.c 11259 2010-02-17 04:47:22Z borkra $ */
+
+/*
+ * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Dawid Jarosz <dawjar@poczta.onet.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "gg.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * list_add_sorted()
+ *
+ * dodaje do listy dany element. przy okazji może też skopiować zawarto¶ć.
+ * je¶li poda się jako ostatni parametr funkcję porównuj±c± zawarto¶ć
+ * elementów, może posortować od razu.
+ *
+ * - list - wskaĽnik do listy,
+ * - data - wskaĽnik do elementu,
+ * - alloc_size - rozmiar elementu, je¶li chcemy go skopiować.
+ *
+ * zwraca wskaĽnik zaalokowanego elementu lub NULL w przpadku błędu.
+ */
+void *list_add_sorted(list_t *list, void *data, int alloc_size, int (*comparision)(void *, void *))
+{
+ list_t new, tmp;
+
+ if (!list) {
+ errno = EFAULT;
+ return NULL;
+ }
+
+ new = malloc(sizeof(struct list));
+
+ new->data = data;
+ new->next = NULL;
+
+ if (alloc_size) {
+ new->data = malloc(alloc_size);
+ memcpy(new->data, data, alloc_size);
+ }
+
+ if (!(tmp = *list)) {
+ *list = new;
+ } else {
+ if (!comparision) {
+ while (tmp->next)
+ tmp = tmp->next;
+ tmp->next = new;
+ } else {
+ list_t prev = NULL;
+
+ while (comparision(new->data, tmp->data) > 0) {
+ prev = tmp;
+ tmp = tmp->next;
+ if (!tmp)
+ break;
+ }
+
+ if (!prev) {
+ tmp = *list;
+ *list = new;
+ new->next = tmp;
+ } else {
+ prev->next = new;
+ new->next = tmp;
+ }
+ }
+ }
+
+ return new->data;
+}
+
+/*
+ * list_add()
+ *
+ * wrapper do list_add_sorted(), który zachowuje poprzedni± składnię.
+ */
+void *list_add(list_t *list, void *data, int alloc_size)
+{
+ return list_add_sorted(list, data, alloc_size, NULL);
+}
+
+/*
+ * list_remove()
+ *
+ * usuwa z listy wpis z podanym elementem.
+ *
+ * - list - wskaĽnik do listy,
+ * - data - element,
+ * - free_data - zwolnić pamięć po elemencie.
+ */
+int list_remove(list_t *list, void *data, int free_data)
+{
+ list_t tmp, last = NULL;
+
+ if (!list || !*list) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ tmp = *list;
+ if (tmp->data == data) {
+ *list = tmp->next;
+ } else {
+ for (; tmp && tmp->data != data; tmp = tmp->next)
+ last = tmp;
+ if (!tmp) {
+ errno = ENOENT;
+ return -1;
+ }
+ last->next = tmp->next;
+ }
+
+ if (free_data)
+ free(tmp->data);
+ free(tmp);
+
+ return 0;
+}
+
+/*
+ * list_count()
+ *
+ * zwraca ilo¶ć elementów w danej li¶cie.
+ *
+ * - list - lista.
+ */
+int list_count(list_t list)
+{
+ int count = 0;
+
+ for (; list; list = list->next)
+ count++;
+
+ return count;
+}
+
+/*
+ * list_destroy()
+ *
+ * niszczy wszystkie elementy listy.
+ *
+ * - list - lista,
+ * - free_data - czy zwalniać bufor danych?
+ */
+int list_destroy(list_t list, int free_data)
+{
+ list_t tmp;
+
+ while (list) {
+ if (free_data)
+ free(list->data);
+
+ tmp = list->next;
+
+ free(list);
+
+ list = tmp;
+ }
+
+ return 0;
+}
+
+/*
+ * string_realloc()
+ *
+ * upewnia się, że w stringu będzie wystarczaj±co dużo miejsca.
+ *
+ * - s - ci±g znaków,
+ * - count - wymagana ilo¶ć znaków (bez końcowego '\0').
+ */
+static void string_realloc(string_t s, int count)
+{
+ char *tmp;
+
+ if (s->str && count + 1 <= s->size)
+ return;
+
+ tmp = realloc(s->str, count + 81);
+ if (!s->str)
+ *tmp = 0;
+ tmp[count + 80] = 0;
+ s->size = count + 81;
+ s->str = tmp;
+}
+
+/*
+ * string_append_c()
+ *
+ * dodaje do danego ci±gu jeden znak, alokuj±c przy tym odpowiedni± ilo¶ć
+ * pamięci.
+ *
+ * - s - ci±g znaków.
+ * - c - znaczek do dopisania.
+ */
+int string_append_c(string_t s, char c)
+{
+ if (!s) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ string_realloc(s, s->len + 1);
+
+ s->str[s->len + 1] = 0;
+ s->str[s->len++] = c;
+
+ return 0;
+}
+
+/*
+ * string_append_n()
+ *
+ * dodaje tekst do bufora alokuj±c odpowiedni± ilo¶ć pamięci.
+ *
+ * - s - ci±g znaków,
+ * - str - tekst do dopisania,
+ * - count - ile znaków tego tekstu dopisać? (-1 znaczy, że cały).
+ */
+int string_append_n(string_t s, const char *str, int count)
+{
+ if (!s || !str) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (count == -1)
+ count = (int)strlen(str);
+
+ string_realloc(s, s->len + count);
+
+ s->str[s->len + count] = 0;
+ strncpy(s->str + s->len, str, count);
+
+ s->len += count;
+
+ return 0;
+}
+
+int string_append(string_t s, const char *str)
+{
+ return string_append_n(s, str, -1);
+}
+
+/*
+ * string_insert_n()
+ *
+ * wstawia tekst w podane miejsce bufora.
+ *
+ * - s - ci±g znaków,
+ * - index - miejsce, gdzie mamy wpisać (liczone od 0),
+ * - str - tekst do dopisania,
+ * - count - ilo¶ć znaków do dopisania (-1 znaczy, że wszystkie).
+ */
+void string_insert_n(string_t s, int index, const char *str, int count)
+{
+ if (!s || !str)
+ return;
+
+ if (count == -1)
+ count = (int)strlen(str);
+
+ if (index > s->len)
+ index = s->len;
+
+ string_realloc(s, s->len + count);
+
+ memmove(s->str + index + count, s->str + index, s->len + 1 - index);
+ memmove(s->str + index, str, count);
+
+ s->len += count;
+}
+
+void string_insert(string_t s, int index, const char *str)
+{
+ string_insert_n(s, index, str, -1);
+}
+
+/*
+ * string_init()
+ *
+ * inicjuje strukturę string. alokuje pamięć i przypisuje pierwsz± warto¶ć.
+ *
+ * - value - je¶li NULL, ci±g jest pusty, inaczej kopiuje tam.
+ *
+ * zwraca zaalokowan± strukturę `string'.
+ */
+string_t string_init(const char *value)
+{
+ string_t tmp = malloc(sizeof(struct string));
+
+ if (!value)
+ value = "";
+
+ tmp->str = _strdup(value);
+ tmp->len = (int)strlen(value);
+ tmp->size = (int)strlen(value) + 1;
+
+ return tmp;
+}
+
+/*
+ * string_clear()
+ *
+ * czy¶ci zawarto¶ć struktury `string'.
+ *
+ * - s - ci±g znaków.
+ */
+void string_clear(string_t s)
+{
+ if (!s)
+ return;
+
+ if (s->size > 160) {
+ s->str = realloc(s->str, 80);
+ s->size = 80;
+ }
+
+ s->str[0] = 0;
+ s->len = 0;
+}
+
+/*
+ * string_free()
+ *
+ * zwalnia pamięć po strukturze string i może też zwolnić pamięć po samym
+ * ci±gu znaków.
+ *
+ * - s - struktura, któr± wycinamy,
+ * - free_string - zwolnić pamięć po ci±gu znaków?
+ *
+ * je¶li free_string=0 zwraca wskaĽnik do ci±gu, inaczej NULL.
+ */
+char *string_free(string_t s, int free_string)
+{
+ char *tmp = NULL;
+
+ if (!s)
+ return NULL;
+
+ if (free_string)
+ free(s->str);
+ else
+ tmp = s->str;
+
+ free(s);
+
+ return tmp;
+}
+
+/*
+ * _itoa()
+ *
+ * prosta funkcja, która zwraca tekstow± reprezentację liczby. w obrębie
+ * danego wywołania jakiej¶ funkcji lub wyrażenia może być wywołania 10
+ * razy, ponieważ tyle mamy statycznych buforów. lepsze to niż ci±głe
+ * tworzenie tymczasowych buforów na stosie i sprintf()owanie.
+ *
+ * - i - liczba do zamiany.
+ *
+ * zwraca adres do bufora, którego _NIE_NALEŻY_ zwalniać.
+ */
+const char *ditoa(long int i)
+{
+ static char bufs[10][16];
+ static int index = 0;
+ char *tmp = bufs[index++];
+
+ if (index > 9)
+ index = 0;
+
+ mir_snprintf(tmp, 16, "%ld", i);
+
+ return tmp;
+}
+
+/*
+ * array_make()
+ *
+ * tworzy tablicę tekstów z jednego, rozdzielonego podanymi znakami.
+ *
+ * - string - tekst wej¶ciowy,
+ * - sep - lista elementów oddzielaj±cych,
+ * - max - maksymalna ilo¶ć elementów tablicy. je¶li równe 0, nie ma
+ * ograniczeń rozmiaru tablicy.
+ * - trim - czy większ± ilo¶ć elementów oddzielaj±cych traktować jako
+ * jeden (na przykład spacje, tabulacja itp.)
+ * - quotes - czy pola mog± być zapisywane w cudzysłowiach lub
+ * apostrofach z escapowanymi znakami.
+ *
+ * zaalokowan± tablicę z zaalokowanymi ci±gami znaków, któr± należy
+ * zwolnić funkcj± array_free()
+ */
+char **array_make(const char *string, const char *sep, int max, int trim, int quotes)
+{
+ const char *p, *q;
+ char **result = NULL;
+ int items = 0, last = 0;
+
+ if (!string || !sep)
+ goto failure;
+
+ for (p = string; ; ) {
+ int len = 0;
+ char *token = NULL;
+
+ if (max && items >= max - 1)
+ last = 1;
+
+ if (trim) {
+ while (*p && strchr(sep, *p))
+ p++;
+ if (!*p)
+ break;
+ }
+
+ if (!last && quotes && (*p == '\'' || *p == '\"')) {
+ char sep = *p;
+
+ for (q = p + 1, len = 0; *q; q++, len++) {
+ if (*q == '\\') {
+ q++;
+ if (!*q)
+ break;
+ } else if (*q == sep)
+ break;
+ }
+
+ if ((token = calloc(1, len + 1))) {
+ char *r = token;
+
+ for (q = p + 1; *q; q++, r++) {
+ if (*q == '\\') {
+ q++;
+
+ if (!*q)
+ break;
+
+ switch (*q) {
+ case 'n':
+ *r = '\n';
+ break;
+ case 'r':
+ *r = '\r';
+ break;
+ case 't':
+ *r = '\t';
+ break;
+ default:
+ *r = *q;
+ }
+ } else if (*q == sep) {
+ break;
+ } else
+ *r = *q;
+ }
+
+ *r = 0;
+ }
+
+ p = (*q) ? q + 1 : q;
+
+ } else {
+ for (q = p, len = 0; *q && (last || !strchr(sep, *q)); q++, len++);
+ token = calloc(1, len + 1);
+ strncpy(token, p, len);
+ token[len] = 0;
+ p = q;
+ }
+
+ result = realloc(result, (items + 2) * sizeof(char*));
+ result[items] = token;
+ result[++items] = NULL;
+
+ if (!*p)
+ break;
+
+ p++;
+ }
+
+failure:
+ if (!items)
+ result = calloc(1, sizeof(char*));
+
+ return result;
+}
+
+/*
+ * array_count()
+ *
+ * zwraca ilo¶ć elementów tablicy.
+ */
+int array_count(char **array)
+{
+ int result = 0;
+
+ if (!array)
+ return 0;
+
+ while (*array) {
+ result++;
+ array++;
+ }
+
+ return result;
+}
+
+/*
+ * array_add()
+ *
+ * dodaje element do tablicy.
+ */
+void array_add(char ***array, char *string)
+{
+ int count = array_count(*array);
+
+ *array = realloc(*array, (count + 2) * sizeof(char*));
+ (*array)[count + 1] = NULL;
+ (*array)[count] = string;
+}
+
+/*
+ * array_join()
+ *
+ * ł±czy elementy tablicy w jeden string oddzielaj±c elementy odpowiednim
+ * separatorem.
+ *
+ * - array - wskaĽnik do tablicy,
+ * - sep - seperator.
+ *
+ * zwrócony ci±g znaków należy zwolnić.
+ */
+char *array_join(char **array, const char *sep)
+{
+ string_t s = string_init(NULL);
+ int i;
+
+ if (!array)
+ return _strdup("");
+
+ for (i = 0; array[i]; i++) {
+ if (i)
+ string_append(s, sep);
+
+ string_append(s, array[i]);
+ }
+
+ return string_free(s, 0);
+}
+
+/*
+ * array_contains()
+ *
+ * stwierdza, czy tablica zawiera podany element.
+ *
+ * - array - tablica,
+ * - string - szukany ci±g znaków,
+ * - casesensitive - czy mamy zwracać uwagę na wielko¶ć znaków?
+ *
+ * 0/1
+ */
+int array_contains(char **array, const char *string, int casesensitive)
+{
+ int i;
+
+ if (!array || !string)
+ return 0;
+
+ for (i = 0; array[i]; i++) {
+ if (casesensitive && !strcmp(array[i], string))
+ return 1;
+ if (!casesensitive && !strcasecmp(array[i], string))
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * array_free()
+ *
+ * zwalnia pamieć zajmowan± przez tablicę.
+ */
+void array_free(char **array)
+{
+ char **tmp;
+
+ if (!array)
+ return;
+
+ for (tmp = array; *tmp; tmp++)
+ free(*tmp);
+
+ free(array);
+}
diff --git a/protocols/Gadu-Gadu/dynstuff.h b/protocols/Gadu-Gadu/dynstuff.h
new file mode 100644
index 0000000000..8e36c67ede
--- /dev/null
+++ b/protocols/Gadu-Gadu/dynstuff.h
@@ -0,0 +1,70 @@
+/* $Id: dynstuff.h 4366 2006-12-20 22:40:37Z ono $ */
+
+/*
+ * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Dawid Jarosz <dawjar@poczta.onet.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License Version 2 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __DYNSTUFF_H
+#define __DYNSTUFF_H
+
+/* listy */
+
+struct list {
+ void *data;
+ struct list *next;
+};
+
+typedef struct list * list_t;
+
+void *list_add(list_t *list, void *data, int alloc_size);
+void *list_add_sorted(list_t *list, void *data, int alloc_size, int (*comparision)(void *, void *));
+int list_remove(list_t *list, void *data, int free_data);
+int list_count(list_t list);
+int list_destroy(list_t list, int free_data);
+
+/* stringi */
+
+struct string {
+ char *str;
+ int len, size;
+};
+
+typedef struct string * string_t;
+
+string_t string_init(const char *str);
+int string_append(string_t s, const char *str);
+int string_append_n(string_t s, const char *str, int count);
+int string_append_c(string_t s, char ch);
+void string_insert(string_t s, int index, const char *str);
+void string_insert_n(string_t s, int index, const char *str, int count);
+void string_clear(string_t s);
+char *string_free(string_t s, int free_string);
+
+/* tablice stringów */
+
+char **array_make(const char *string, const char *sep, int max, int trim, int quotes);
+char *array_join(char **array, const char *sep);
+void array_add(char ***array, char *string);
+int array_count(char **array);
+int array_contains(char **array, const char *string, int casesensitive);
+void array_free(char **array);
+
+/* rozszerzenia libców */
+
+const char *ditoa(long int i);
+
+#endif /* __DYNSTUFF_H */
diff --git a/protocols/Gadu-Gadu/filetransfer.c b/protocols/Gadu-Gadu/filetransfer.c
new file mode 100644
index 0000000000..f738dfa774
--- /dev/null
+++ b/protocols/Gadu-Gadu/filetransfer.c
@@ -0,0 +1,982 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2006 Adam Strzelecki <ono+miranda@java.pl>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+#include <errno.h>
+#include <io.h>
+#include <fcntl.h>
+
+void __cdecl gg_dccmainthread(GGPROTO *gg, void *empty);
+
+void gg_dccstart(GGPROTO *gg)
+{
+ DWORD exitCode = 0;
+
+ if(gg->dcc) return;
+
+ // Startup dcc thread
+ GetExitCodeThread(gg->pth_dcc.hThread, &exitCode);
+ // Check if dcc thread isn't running already
+ if(exitCode == STILL_ACTIVE)
+ {
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_dccstart(): DCC thread still active. Exiting...");
+#endif
+ // Signalize mainthread it's started
+ if(gg->event) SetEvent(gg->event);
+ return;
+ }
+
+ // Check if we wan't direct connections
+ if(!DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_DIRECTCONNS, GG_KEYDEF_DIRECTCONNS))
+ {
+ gg_netlog(gg, "gg_dccstart(): No direct connections setup.");
+ if(gg->event) SetEvent(gg->event);
+ return;
+ }
+
+ // Start thread
+ gg->pth_dcc.hThread = gg_forkthreadex(gg, gg_dccmainthread, NULL, &gg->pth_dcc.dwThreadId);
+}
+
+void gg_dccconnect(GGPROTO *gg, uin_t uin)
+{
+ struct gg_dcc *dcc;
+ HANDLE hContact = gg_getcontact(gg, uin, 0, 0, NULL);
+ DWORD ip, myuin; WORD port;
+
+ gg_netlog(gg, "gg_dccconnect(): Connecting to uin %d.", uin);
+
+ // If unknown user or not on list ignore
+ if(!hContact) return;
+
+ // Read user IP and port
+ ip = swap32(DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_CLIENTIP, 0));
+ port = DBGetContactSettingWord(hContact, GG_PROTO, GG_KEY_CLIENTPORT, 0);
+ myuin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0);
+
+ // If not port nor ip nor my uin (?) specified
+ if(!ip || !port || !uin) return;
+
+ if(!(dcc = gg_dcc_get_file(ip, port, myuin, uin)))
+ return;
+
+ // Add client dcc to watches
+ EnterCriticalSection(&gg->ft_mutex);
+ list_add(&gg->watches, dcc, 0);
+ LeaveCriticalSection(&gg->ft_mutex);
+}
+
+//////////////////////////////////////////////////////////
+// THREAD: File transfer fail
+struct ftfaildata
+{
+ HANDLE hContact;
+ HANDLE hProcess;
+};
+void __cdecl gg_ftfailthread(GGPROTO *gg, void *param)
+{
+ struct ftfaildata *ft = (struct ftfaildata *)param;
+ SleepEx(100, FALSE);
+ gg_netlog(gg, "gg_ftfailthread(): Sending failed file transfer.");
+ ProtoBroadcastAck(GG_PROTO, ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft->hProcess, 0);
+ free(ft);
+}
+HANDLE ftfail(GGPROTO *gg, HANDLE hContact)
+{
+ struct ftfaildata *ft = malloc(sizeof(struct ftfaildata));
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_ftfail(): Failing file transfer...");
+#endif
+ srand(time(NULL));
+ ft->hProcess = (HANDLE)rand();
+ ft->hContact = hContact;
+ gg_forkthread(gg, gg_ftfailthread, ft);
+ return ft->hProcess;
+}
+
+////////////////////////////////////////////////////////////
+// Main DCC connection session thread
+
+// Info refresh min time (msec) / half-sec
+#define GGSTATREFRESHEVERY 500
+
+void __cdecl gg_dccmainthread(GGPROTO *gg, void *empty)
+{
+ uin_t uin;
+ struct gg_event *e;
+ struct timeval tv;
+ fd_set rd, wd;
+ int ret;
+ SOCKET maxfd;
+ DWORD tick;
+ list_t l;
+ char filename[MAX_PATH];
+
+ // Zero up lists
+ gg->watches = gg->transfers = gg->requests = l = NULL;
+
+ gg_netlog(gg, "gg_dccmainthread(): DCC Server Thread Starting");
+
+ // Readup number
+ if (!(uin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0)))
+ {
+ gg_netlog(gg, "gg_dccmainthread(): No Gadu-Gadu number specified. Exiting.");
+ if(gg->event) SetEvent(gg->event);
+ return;
+ }
+
+ // Create listen socket on config direct port
+ if(!(gg->dcc = gg_dcc_socket_create(uin, (uint16_t)DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_DIRECTPORT, GG_KEYDEF_DIRECTPORT))))
+ {
+ gg_netlog(gg, "gg_dccmainthread(): Cannot create DCC listen socket. Exiting.");
+ // Signalize mainthread we haven't start
+ if(gg->event) SetEvent(gg->event);
+ return;
+ }
+
+ gg_dcc_port = gg->dcc->port;
+ gg_dcc_ip = inet_addr("255.255.255.255");
+ gg_netlog(gg, "gg_dccmainthread(): Listening on port %d.", gg_dcc_port);
+
+ // Signalize mainthread we started
+ if(gg->event) SetEvent(gg->event);
+
+ // Add main dcc handler to watches
+ list_add(&gg->watches, gg->dcc, 0);
+
+ // Do while we are in the main server thread
+ while(gg->pth_dcc.dwThreadId && gg->dcc)
+ {
+ // Timeouts
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ // Prepare descriptiors for select
+ FD_ZERO(&rd);
+ FD_ZERO(&wd);
+
+ for (maxfd = 0, l = gg->watches; l; l = l->next)
+ {
+ struct gg_common *w = l->data;
+
+ if (!w || w->state == GG_STATE_ERROR || w->state == GG_STATE_IDLE || w->state == GG_STATE_DONE)
+ continue;
+
+ // Check if it's proper descriptor
+ if (w->fd == -1) continue;
+
+ if (w->fd > maxfd)
+ maxfd = w->fd;
+ if ((w->check & GG_CHECK_READ))
+ FD_SET(w->fd, &rd);
+ if ((w->check & GG_CHECK_WRITE))
+ FD_SET(w->fd, &wd);
+ }
+
+ // Wait for data on selects
+ ret = select(maxfd + 1, &rd, &wd, NULL, &tv);
+
+ // Check for select error
+ if (ret == -1)
+ {
+ if (errno == EBADF)
+ gg_netlog(gg, "gg_dccmainthread(): Bad descriptor on select().");
+ else if (errno != EINTR)
+ gg_netlog(gg, "gg_dccmainthread(): Unknown error on select().");
+ continue;
+ }
+
+ // Process watches (carefull with l)
+ l = gg->watches;
+ EnterCriticalSection(&gg->ft_mutex);
+ while (l)
+ {
+ struct gg_common *c = l->data;
+ struct gg_dcc *dcc = l->data;
+ struct gg_dcc7 *dcc7 = l->data;
+ l = l->next;
+
+ switch (c->type)
+ {
+ default:
+ if (!dcc || (!FD_ISSET(dcc->fd, &rd) && !FD_ISSET(dcc->fd, &wd)))
+ continue;
+
+ /////////////////////////////////////////////////////////////////
+ // Process DCC events
+
+ // Connection broken/closed
+ if (!(e = gg_dcc_socket_watch_fd(dcc)))
+ {
+ gg_netlog(gg, "gg_dccmainthread(): Socket closed.");
+ // Remove socket and _close
+ list_remove(&gg->watches, dcc, 0);
+ gg_dcc_socket_free(dcc);
+
+ // Check if it's main socket
+ if(dcc == gg->dcc) gg->dcc = NULL;
+ continue;
+ }
+ else gg_netlog(gg, "gg_dccmainthread(): Event: %s", ggdebug_eventtype(e));
+
+ switch(e->type)
+ {
+ // Client connected
+ case GG_EVENT_DCC_NEW:
+ list_add(&gg->watches, e->event.dcc_new, 0);
+ e->event.dcc_new = NULL;
+ break;
+
+ //
+ case GG_EVENT_NONE:
+ // If transfer in progress do status
+ if(dcc->file_fd != -1 && dcc->offset > 0 && (((tick = GetTickCount()) - dcc->tick) > GGSTATREFRESHEVERY))
+ {
+ PROTOFILETRANSFERSTATUS pfts;
+ dcc->tick = tick;
+ strncpy(filename, dcc->folder, sizeof(filename));
+ strncat(filename, dcc->file_info.filename, sizeof(filename) - strlen(filename));
+ memset(&pfts, 0, sizeof(PROTOFILETRANSFERSTATUS));
+ pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS);
+ pfts.hContact = (HANDLE)dcc->contact;
+ pfts.flags = (dcc->type == GG_SESSION_DCC_SEND);
+ pfts.pszFiles = NULL;
+ pfts.totalFiles = 1;
+ pfts.currentFileNumber = 0;
+ pfts.totalBytes = dcc->file_info.size;
+ pfts.totalProgress = dcc->offset;
+ pfts.szWorkingDir = dcc->folder;
+ pfts.szCurrentFile = filename;
+ pfts.currentFileSize = dcc->file_info.size;
+ pfts.currentFileProgress = dcc->offset;
+ pfts.currentFileTime = 0;
+ ProtoBroadcastAck(GG_PROTO, dcc->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc, (LPARAM)&pfts);
+ }
+ break;
+
+ // Connection was successfuly ended
+ case GG_EVENT_DCC_DONE:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, Transfer done ! Closing connection.", dcc->peer_uin);
+ // Remove from watches
+ list_remove(&gg->watches, dcc, 0);
+ // Close file & success
+ if(dcc->file_fd != -1)
+ {
+ PROTOFILETRANSFERSTATUS pfts;
+ strncpy(filename, dcc->folder, sizeof(filename));
+ strncat(filename, dcc->file_info.filename, sizeof(filename) - strlen(filename));
+ memset(&pfts, 0, sizeof(PROTOFILETRANSFERSTATUS));
+ pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS);
+ pfts.hContact = (HANDLE)dcc->contact;
+ pfts.flags = (dcc->type == GG_SESSION_DCC_SEND);
+ pfts.pszFiles = NULL;
+ pfts.totalFiles = 1;
+ pfts.currentFileNumber = 0;
+ pfts.totalBytes = dcc->file_info.size;
+ pfts.totalProgress = dcc->file_info.size;
+ pfts.szWorkingDir = dcc->folder;
+ pfts.szCurrentFile = filename;
+ pfts.currentFileSize = dcc->file_info.size;
+ pfts.currentFileProgress = dcc->file_info.size;
+ pfts.currentFileTime = 0;
+ ProtoBroadcastAck(GG_PROTO, dcc->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc, (LPARAM)&pfts);
+ _close(dcc->file_fd); dcc->file_fd = -1;
+ ProtoBroadcastAck(GG_PROTO, dcc->contact, ACKTYPE_FILE, ACKRESULT_SUCCESS, dcc, 0);
+ }
+ // Free dcc
+ gg_free_dcc(dcc); if(dcc == gg->dcc) gg->dcc = NULL;
+ break;
+
+ // Client error
+ case GG_EVENT_DCC_ERROR:
+ switch (e->event.dcc_error)
+ {
+ case GG_ERROR_DCC_HANDSHAKE:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, Handshake error.", dcc->peer_uin);
+ break;
+ case GG_ERROR_DCC_NET:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, Network error.", dcc->peer_uin);
+ break;
+ case GG_ERROR_DCC_FILE:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, File read/write error.", dcc->peer_uin);
+ break;
+ case GG_ERROR_DCC_EOF:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, End of file/connection error.", dcc->peer_uin);
+ break;
+ case GG_ERROR_DCC_REFUSED:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, Connection refused error.", dcc->peer_uin);
+ break;
+ default:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, Unknown error.", dcc->peer_uin);
+ }
+ // Don't do anything if it's main socket
+ if(dcc == gg->dcc) break;
+
+ // Remove from watches
+ list_remove(&gg->watches, dcc, 0);
+
+ // Close file & fail
+ if(dcc->contact)
+ {
+ _close(dcc->file_fd); dcc->file_fd = -1;
+ ProtoBroadcastAck(GG_PROTO, dcc->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc, 0);
+ }
+ // Free dcc
+ gg_free_dcc(dcc); if(dcc == gg->dcc) gg->dcc = NULL;
+ break;
+
+ // Need file acknowledgement
+ case GG_EVENT_DCC_NEED_FILE_ACK:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, File ack filename \"%s\" size %d.", dcc->peer_uin,
+ dcc->file_info.filename, dcc->file_info.size);
+ // Do not watch for transfer until user accept it
+ list_remove(&gg->watches, dcc, 0);
+ // Add to waiting transfers
+ list_add(&gg->transfers, dcc, 0);
+
+ //////////////////////////////////////////////////
+ // Add file recv request
+ {
+ CCSDATA ccs;
+ PROTORECVEVENT pre;
+ char *szBlob;
+ char *szFilename = dcc->file_info.filename;
+ char *szMsg = dcc->file_info.filename;
+
+ // Make new ggtransfer struct
+ dcc->contact = gg_getcontact(gg, dcc->peer_uin, 0, 0, NULL);
+ szBlob = (char *)malloc(sizeof(DWORD) + strlen(szFilename) + strlen(szMsg) + 2);
+ // Store current dcc
+ *(PDWORD)szBlob = (DWORD)dcc;
+ // Store filename
+ strcpy(szBlob + sizeof(DWORD), szFilename);
+ // Store description
+ strcpy(szBlob + sizeof(DWORD) + strlen(szFilename) + 1, szMsg);
+ ccs.szProtoService = PSR_FILE;
+ ccs.hContact = dcc->contact;
+ ccs.wParam = 0;
+ ccs.lParam = (LPARAM)&pre;
+ pre.flags = 0;
+ pre.timestamp = time(NULL);
+ pre.szMessage = szBlob;
+ pre.lParam = 0;
+ CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs);
+ free(szBlob);
+ }
+ break;
+
+ // Need client accept
+ case GG_EVENT_DCC_CLIENT_ACCEPT:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, Client accept.", dcc->peer_uin);
+ // Check if user is on the list and if it is my uin
+ if(gg_getcontact(gg, dcc->peer_uin, 0, 0, NULL) &&
+ DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, -1) == dcc->uin)
+ break;
+
+ // Kill unauthorized dcc
+ list_remove(&gg->watches, dcc, 0);
+ gg_free_dcc(dcc); if(dcc == gg->dcc) gg->dcc = NULL;
+ break;
+
+ // Client connected as we wished to (callback)
+ case GG_EVENT_DCC_CALLBACK:
+ {
+ int found = 0;
+ gg_netlog(gg, "gg_dccmainthread(): Callback from client %d.", dcc->peer_uin);
+ // Seek for stored callback request
+ for (l = gg->requests; l; l = l->next)
+ {
+ struct gg_dcc *req = l->data;
+
+ if (req && req->peer_uin == dcc->peer_uin)
+ {
+ gg_dcc_set_type(dcc, GG_SESSION_DCC_SEND);
+ found = 1;
+
+ // Copy data req ===> dcc
+ dcc->folder = req->folder;
+ dcc->contact = req->contact;
+ dcc->file_fd = req->file_fd;
+ memcpy(&dcc->file_info, &req->file_info, sizeof(struct gg_file_info));
+ // Copy data back to dcc ===> req
+ memcpy(req, dcc, sizeof(struct gg_dcc));
+
+ // Remove request
+ list_remove(&gg->requests, req, 0);
+ // Remove dcc from watches
+ list_remove(&gg->watches, dcc, 0);
+ // Add request to watches
+ list_add(&gg->watches, req, 0);
+ // Free old dat
+ gg_free_dcc(dcc);
+ gg_netlog(gg, "gg_dccmainthread(): Found stored request to client %d, filename \"%s\" size %d, folder \"%s\".",
+ req->peer_uin, req->file_info.filename, req->file_info.size, req->folder);
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ gg_netlog(gg, "gg_dccmainthread(): Unknown request to client %d.", dcc->peer_uin);
+ // Kill unauthorized dcc
+ list_remove(&gg->watches, dcc, 0);
+ gg_free_dcc(dcc); if(dcc == gg->dcc) gg->dcc = NULL;
+ }
+ break;
+ }
+ }
+
+ // Free event
+ gg_free_event(e);
+ break;
+
+ case GG_SESSION_DCC7_SOCKET:
+ case GG_SESSION_DCC7_GET:
+ case GG_SESSION_DCC7_SEND:
+ case GG_SESSION_DCC7_VOICE:
+ if (!dcc7 || (!FD_ISSET(dcc7->fd, &rd) && !FD_ISSET(dcc7->fd, &wd)))
+ continue;
+
+ /////////////////////////////////////////////////////////////////
+ // Process DCC7 events
+
+ // Connection broken/closed
+ if (!(e = gg_dcc7_watch_fd(dcc7)))
+ {
+ gg_netlog(gg, "gg_dccmainthread(): Socket closed.");
+ // Remove socket and _close
+ list_remove(&gg->watches, dcc7, 0);
+ gg_dcc7_free(dcc7);
+ continue;
+ }
+ else gg_netlog(gg, "gg_dccmainthread(): Event: %s", ggdebug_eventtype(e));
+
+ switch(e->type)
+ {
+ //
+ case GG_EVENT_NONE:
+ // If transfer in progress do status
+ if(dcc7->file_fd != -1 && dcc7->offset > 0 && (((tick = GetTickCount()) - dcc7->tick) > GGSTATREFRESHEVERY))
+ {
+ PROTOFILETRANSFERSTATUS pfts;
+ dcc7->tick = tick;
+ strncpy(filename, dcc7->folder, sizeof(filename));
+ strncat(filename, dcc7->filename, sizeof(filename) - strlen(filename));
+ memset(&pfts, 0, sizeof(PROTOFILETRANSFERSTATUS));
+ pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS);
+ pfts.hContact = (HANDLE)dcc7->contact;
+ pfts.flags = (dcc7->type == GG_SESSION_DCC7_SEND);
+ pfts.pszFiles = NULL;
+ pfts.totalFiles = 1;
+ pfts.currentFileNumber = 0;
+ pfts.totalBytes = dcc7->size;
+ pfts.totalProgress = dcc7->offset;
+ pfts.szWorkingDir = dcc7->folder;
+ pfts.szCurrentFile = filename;
+ pfts.currentFileSize = dcc7->size;
+ pfts.currentFileProgress = dcc7->offset;
+ pfts.currentFileTime = 0;
+ ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc7, (LPARAM)&pfts);
+ }
+ break;
+
+ // Connection was successfuly ended
+ case GG_EVENT_DCC7_DONE:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, Transfer done ! Closing connection.", dcc->peer_uin);
+ // Remove from watches
+ list_remove(&gg->watches, dcc7, 0);
+ // Close file & success
+ if(dcc7->file_fd != -1)
+ {
+ PROTOFILETRANSFERSTATUS pfts;
+ strncpy(filename, dcc7->folder, sizeof(filename));
+ strncat(filename, dcc7->filename, sizeof(filename) - strlen(filename));
+ memset(&pfts, 0, sizeof(PROTOFILETRANSFERSTATUS));
+ pfts.cbSize = sizeof(PROTOFILETRANSFERSTATUS);
+ pfts.hContact = (HANDLE)dcc7->contact;
+ pfts.flags = (dcc7->type == GG_SESSION_DCC7_SEND);
+ pfts.pszFiles = NULL;
+ pfts.totalFiles = 1;
+ pfts.currentFileNumber = 0;
+ pfts.totalBytes = dcc7->size;
+ pfts.totalProgress = dcc7->size;
+ pfts.szWorkingDir = dcc7->folder;
+ pfts.szCurrentFile = filename;
+ pfts.currentFileSize = dcc7->size;
+ pfts.currentFileProgress = dcc7->size;
+ pfts.currentFileTime = 0;
+ ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc7, (LPARAM)&pfts);
+ _close(dcc7->file_fd); dcc7->file_fd = -1;
+ ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_SUCCESS, dcc7, 0);
+ }
+ // Free dcc
+ gg_dcc7_free(dcc7);
+ break;
+
+ // Client error
+ case GG_EVENT_DCC7_ERROR:
+ switch (e->event.dcc7_error)
+ {
+ case GG_ERROR_DCC7_HANDSHAKE:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, Handshake error.", dcc7->peer_uin);
+ break;
+ case GG_ERROR_DCC7_NET:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, Network error.", dcc7->peer_uin);
+ break;
+ case GG_ERROR_DCC7_FILE:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, File read/write error.", dcc7->peer_uin);
+ break;
+ case GG_ERROR_DCC7_EOF:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, End of file/connection error.", dcc7->peer_uin);
+ break;
+ case GG_ERROR_DCC7_REFUSED:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, Connection refused error.", dcc7->peer_uin);
+ break;
+ case GG_ERROR_DCC7_RELAY:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, Relay connection error.", dcc7->peer_uin);
+ break;
+ default:
+ gg_netlog(gg, "gg_dccmainthread(): Client: %d, Unknown error.", dcc7->peer_uin);
+ }
+ // Remove from watches
+ list_remove(&gg->watches, dcc7, 0);
+
+ // Close file & fail
+ if (dcc7->file_fd != -1)
+ {
+ _close(dcc7->file_fd);
+ dcc7->file_fd = -1;
+ }
+
+ if (dcc7->contact)
+ ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0);
+
+ // Free dcc
+ gg_dcc7_free(dcc7);
+ break;
+ }
+
+ // Free event
+ gg_free_event(e);
+ break;
+ }
+ }
+ LeaveCriticalSection(&gg->ft_mutex);
+ }
+
+ // Close all dcc client sockets
+ for (l = gg->watches; l; l = l->next)
+ {
+ struct gg_common *c = l->data;
+ if (!c) continue;
+ if (c->type == GG_SESSION_DCC7_SOCKET || c->type == GG_SESSION_DCC7_SEND || c->type == GG_SESSION_DCC7_GET)
+ {
+ struct gg_dcc7 *dcc7 = l->data;
+ gg_dcc7_free(dcc7);
+ }
+ else
+ {
+ struct gg_dcc *dcc = l->data;
+ gg_dcc_socket_free(dcc);
+
+ // Check if it's main socket
+ if(dcc == gg->dcc) gg->dcc = NULL;
+ }
+ }
+ // Close all waiting for aknowledgle transfers
+ for (l = gg->transfers; l; l = l->next)
+ {
+ struct gg_common *c = l->data;
+ if (!c) continue;
+ if (c->type == GG_SESSION_DCC7_SOCKET || c->type == GG_SESSION_DCC7_SEND || c->type == GG_SESSION_DCC7_GET)
+ {
+ struct gg_dcc7 *dcc7 = l->data;
+ gg_dcc7_free(dcc7);
+ }
+ else
+ {
+ struct gg_dcc *dcc = l->data;
+ gg_dcc_socket_free(dcc);
+ }
+ }
+ // Close all waiting dcc requests
+ for (l = gg->requests; l; l = l->next)
+ {
+ struct gg_dcc *dcc = l->data;
+ if(dcc) gg_free_dcc(dcc);
+ }
+ list_destroy(gg->watches, 0);
+ list_destroy(gg->transfers, 0);
+ list_destroy(gg->requests, 0);
+
+ gg_dcc_port = 0;
+ gg_dcc_ip = 0;
+ gg_netlog(gg, "gg_dccmainthread(): DCC Server Thread Ending");
+}
+
+////////////////////////////////////////////////////////////
+// Called when received an file
+int gg_recvfile(PROTO_INTERFACE *proto, HANDLE hContact, PROTOFILEEVENT* pre)
+{
+ CCSDATA ccs = { hContact, PSR_FILE, 0, ( LPARAM )pre };
+ return CallService( MS_PROTO_RECVFILE, 0, ( LPARAM )&ccs );
+}
+
+////////////////////////////////////////////////////////////
+// Called when user sends a file
+HANDLE gg_sendfile(PROTO_INTERFACE *proto, HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles)
+{
+ GGPROTO *gg = (GGPROTO *) proto;
+ char *bslash, *filename;
+ struct gg_dcc *dcc;
+ DWORD ip, ver;
+ WORD port;
+ uin_t myuin, uin;
+
+ // Check if main dcc thread is on
+ if(!gg_isonline(gg)) return ftfail(gg, hContact);
+
+ filename = gg_t2a(ppszFiles[0]);
+
+ // Read user IP and port
+ ip = swap32(DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_CLIENTIP, 0));
+ port = DBGetContactSettingWord(hContact, GG_PROTO, GG_KEY_CLIENTPORT, 0);
+ myuin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0);
+ uin = DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0);
+ ver = DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_CLIENTVERSION, 0);
+
+ // Use DCC7 if a contact is using at least version 7.6 or unknown version
+ if ((ver & 0x00ffffff) >= 0x29 || !ver) {
+ struct gg_dcc7 *dcc7;
+
+ EnterCriticalSection(&gg->sess_mutex);
+ if (!(dcc7 = gg_dcc7_send_file(gg->sess, uin, filename, NULL, NULL))) {
+ LeaveCriticalSection(&gg->sess_mutex);
+ gg_netlog(gg, "gg_sendfile(): Failed to send file \"%s\".", filename);
+ mir_free(filename);
+ return ftfail(gg, hContact);
+ }
+ LeaveCriticalSection(&gg->sess_mutex);
+
+ gg_netlog(gg, "gg_sendfile(): Sending file \"%s\" to %d.", filename, uin);
+
+ // Add dcc to watches
+ list_add(&gg->watches, dcc7, 0);
+
+ // Store handle
+ dcc7->contact = hContact;
+ dcc7->folder = _strdup(filename);
+ dcc7->tick = 0;
+ // Make folder name
+ bslash = strrchr(dcc7->folder, '\\');
+ if(bslash)
+ *(bslash + 1) = 0;
+ else
+ *(dcc7->folder) = 0;
+ mir_free(filename);
+ return dcc7;
+ }
+
+ // Return if bad connection info
+ if(!port || !uin || !myuin)
+ {
+ gg_netlog(gg, "gg_sendfile(): Bad contact uin or my uin. Exit.");
+ mir_free(filename);
+ return ftfail(gg, hContact);
+ }
+
+ // Try to connect if not ask user to connect me
+ if((ip && port >= 10 && !(dcc = gg_dcc_send_file(ip, port, myuin, uin))) || (port < 10 && port > 0))
+ {
+ // Make fake dcc structure
+ dcc = malloc(sizeof(struct gg_dcc));
+ memset(dcc, 0, sizeof(struct gg_dcc));
+ // Fill up structures
+ dcc->uin = myuin;
+ dcc->peer_uin = uin;
+ dcc->fd = -1;
+ dcc->type = GG_SESSION_DCC_SEND;
+ gg_netlog(gg, "gg_sendfile(): Requesting user to connect us and scheduling gg_dcc struct for a later use.");
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_dcc_request(gg->sess, uin);
+ LeaveCriticalSection(&gg->sess_mutex);
+ list_add(&gg->requests, dcc, 0);
+ }
+
+ // Write filename
+ if(gg_dcc_fill_file_info(dcc, filename) == -1)
+ {
+ gg_netlog(gg, "gg_sendfile(): Cannot open and file fileinfo \"%s\".", filename);
+ gg_free_dcc(dcc);
+ mir_free(filename);
+ return ftfail(gg, hContact);
+ }
+
+ gg_netlog(gg, "gg_sendfile(): Sending file \"%s\" to %d in %s mode.", filename, uin, (dcc->fd != -1) ? "active" : "passive");
+
+ // Add dcc to watches if not passive
+ if(dcc->fd != -1) list_add(&gg->watches, dcc, 0);
+
+ // Store handle
+ dcc->contact = hContact;
+ dcc->folder = _strdup(filename);
+ dcc->tick = 0;
+ // Make folder name
+ bslash = strrchr(dcc->folder, '\\');
+ if(bslash)
+ *(bslash + 1) = 0;
+ else
+ *(dcc->folder) = 0;
+
+ mir_free(filename);
+ return dcc;
+}
+
+HANDLE gg_dccfileallow(GGPROTO *gg, HANDLE hTransfer, const PROTOCHAR* szPath)
+{
+ struct gg_dcc *dcc = (struct gg_dcc *) hTransfer;
+ char fileName[MAX_PATH], *path = gg_t2a(szPath);
+ strncpy(fileName, path, sizeof(fileName));
+ strncat(fileName, dcc->file_info.filename, sizeof(fileName) - strlen(fileName));
+ dcc->folder = _strdup((char *) path);
+ dcc->tick = 0;
+ mir_free(path);
+
+ // Remove transfer from waiting list
+ EnterCriticalSection(&gg->ft_mutex);
+ list_remove(&gg->transfers, dcc, 0);
+ LeaveCriticalSection(&gg->ft_mutex);
+
+ // Open file for appending and check if ok
+ if((dcc->file_fd = _open(fileName, _O_WRONLY | _O_APPEND | _O_BINARY | _O_CREAT, _S_IREAD | _S_IWRITE)) == -1)
+ {
+ gg_netlog(gg, "gg_dccfileallow(): Failed to create file \"%s\".", fileName);
+ ProtoBroadcastAck(GG_PROTO, dcc->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc, 0);
+ // Free transfer
+ gg_free_dcc(dcc);
+ return 0;
+ }
+
+ // Put an offset to the file
+ dcc->offset = _lseek(dcc->file_fd, 0, SEEK_END);
+
+ // Add to watches and start transfer
+ EnterCriticalSection(&gg->ft_mutex);
+ list_add(&gg->watches, dcc, 0);
+ LeaveCriticalSection(&gg->ft_mutex);
+
+ gg_netlog(gg, "gg_dccfileallow(): Receiving file \"%s\" from %d.", dcc->file_info.filename, dcc->peer_uin);
+
+ return hTransfer;
+}
+
+HANDLE gg_dcc7fileallow(GGPROTO *gg, HANDLE hTransfer, const PROTOCHAR* szPath)
+{
+ struct gg_dcc7 *dcc7 = (struct gg_dcc7 *) hTransfer;
+ char fileName[MAX_PATH], *path = gg_t2a(szPath);
+ int iFtRemoveRes;
+ strncpy(fileName, path, sizeof(fileName));
+ strncat(fileName, dcc7->filename, sizeof(fileName) - strlen(fileName));
+ dcc7->folder = _strdup((char *) path);
+ dcc7->tick = 0;
+ mir_free(path);
+
+ // Remove transfer from waiting list
+ EnterCriticalSection(&gg->ft_mutex);
+ iFtRemoveRes = list_remove(&gg->transfers, dcc7, 0);
+ LeaveCriticalSection(&gg->ft_mutex);
+
+ if (iFtRemoveRes == -1)
+ {
+ gg_netlog(gg, "gg_dcc7fileallow(): File transfer denied.");
+ ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DENIED, dcc7, 0);
+ // Free transfer
+ gg_dcc7_free(dcc7);
+ return 0;
+ }
+
+ // Open file for appending and check if ok
+ if((dcc7->file_fd = _open(fileName, _O_WRONLY | _O_APPEND | _O_BINARY | _O_CREAT, _S_IREAD | _S_IWRITE)) == -1)
+ {
+ gg_netlog(gg, "gg_dcc7fileallow(): Failed to create file \"%s\".", fileName);
+ gg_dcc7_reject(dcc7, GG_DCC7_REJECT_USER);
+ ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0);
+ // Free transfer
+ gg_dcc7_free(dcc7);
+ return 0;
+ }
+
+ // Put an offset to the file
+ dcc7->offset = _lseek(dcc7->file_fd, 0, SEEK_END);
+ gg_dcc7_accept(dcc7, dcc7->offset);
+
+ // Add to watches and start transfer
+ EnterCriticalSection(&gg->ft_mutex);
+ list_add(&gg->watches, dcc7, 0);
+ LeaveCriticalSection(&gg->ft_mutex);
+
+ gg_netlog(gg, "gg_dcc7fileallow(): Receiving file \"%s\" from %d.", dcc7->filename, dcc7->peer_uin);
+
+ return hTransfer;
+}
+
+////////////////////////////////////////////////////////////
+// File receiving allowed
+HANDLE gg_fileallow(PROTO_INTERFACE *proto, HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath)
+{
+ struct gg_common *c = (struct gg_common *) hTransfer;
+
+ // Check if its proper dcc
+ if (!c) return NULL;
+
+ if (c->type == GG_SESSION_DCC7_GET)
+ return gg_dcc7fileallow((GGPROTO *)proto, hTransfer, szPath);
+
+ return gg_dccfileallow((GGPROTO *)proto, hTransfer, szPath);
+}
+
+int gg_dccfiledeny(GGPROTO *gg, HANDLE hTransfer)
+{
+ struct gg_dcc *dcc = (struct gg_dcc *) hTransfer;
+
+ // Remove transfer from any list
+ EnterCriticalSection(&gg->ft_mutex);
+ if(gg->watches) list_remove(&gg->watches, dcc, 0);
+ if(gg->requests) list_remove(&gg->requests, dcc, 0);
+ if(gg->transfers) list_remove(&gg->transfers, dcc, 0);
+ LeaveCriticalSection(&gg->ft_mutex);
+
+ gg_netlog(gg, "gg_dccfiledeny(): Rejected file \"%s\" from/to %d.", dcc->file_info.filename, dcc->peer_uin);
+
+ // Free transfer
+ gg_free_dcc(dcc);
+
+ return 0;
+}
+
+int gg_dcc7filedeny(GGPROTO *gg, HANDLE hTransfer)
+{
+ struct gg_dcc7 *dcc7 = (struct gg_dcc7 *) hTransfer;
+
+ gg_dcc7_reject(dcc7, GG_DCC7_REJECT_USER);
+
+ // Remove transfer from any list
+ EnterCriticalSection(&gg->ft_mutex);
+ if(gg->watches) list_remove(&gg->watches, dcc7, 0);
+ if(gg->transfers) list_remove(&gg->transfers, dcc7, 0);
+ LeaveCriticalSection(&gg->ft_mutex);
+
+ gg_netlog(gg, "gg_dcc7filedeny(): Rejected file \"%s\" from/to %d.", dcc7->filename, dcc7->peer_uin);
+
+ // Free transfer
+ gg_dcc7_free(dcc7);
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////
+// File receiving denied
+int gg_filedeny(PROTO_INTERFACE *proto, HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szReason)
+{
+ struct gg_common *c = (struct gg_common *) hTransfer;
+
+ // Check if its proper dcc
+ if (!c) return 0;
+
+ if (c->type == GG_SESSION_DCC7_GET)
+ return gg_dcc7filedeny((GGPROTO *)proto, hTransfer);
+
+ return gg_dccfiledeny((GGPROTO *)proto, hTransfer);
+}
+
+int gg_dccfilecancel(GGPROTO *gg, HANDLE hTransfer)
+{
+ struct gg_dcc *dcc = (struct gg_dcc *) hTransfer;
+
+ // Remove transfer from any list
+ EnterCriticalSection(&gg->ft_mutex);
+ if(gg->watches) list_remove(&gg->watches, dcc, 0);
+ if(gg->requests) list_remove(&gg->requests, dcc, 0);
+ if(gg->transfers) list_remove(&gg->transfers, dcc, 0);
+ LeaveCriticalSection(&gg->ft_mutex);
+
+ // Send failed info
+ ProtoBroadcastAck(GG_PROTO, dcc->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc, 0);
+ // Close file
+ if(dcc->file_fd != -1)
+ {
+ _close(dcc->file_fd);
+ dcc->file_fd = -1;
+ }
+
+ gg_netlog(gg, "gg_dccfilecancel(): Canceled file \"%s\" from/to %d.", dcc->file_info.filename, dcc->peer_uin);
+
+ // Free transfer
+ gg_free_dcc(dcc);
+
+ return 0;
+}
+
+int gg_dcc7filecancel(GGPROTO *gg, HANDLE hTransfer)
+{
+ struct gg_dcc7 *dcc7 = (struct gg_dcc7 *) hTransfer;
+
+ if (dcc7->type == GG_SESSION_DCC7_SEND && dcc7->state == GG_STATE_WAITING_FOR_ACCEPT)
+ gg_dcc7_abort(dcc7);
+
+ // Remove transfer from any list
+ EnterCriticalSection(&gg->ft_mutex);
+ if(gg->watches) list_remove(&gg->watches, dcc7, 0);
+ if(gg->transfers) list_remove(&gg->transfers, dcc7, 0);
+ LeaveCriticalSection(&gg->ft_mutex);
+
+ // Send failed info
+ ProtoBroadcastAck(GG_PROTO, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0);
+ // Close file
+ if(dcc7->file_fd != -1)
+ {
+ _close(dcc7->file_fd);
+ dcc7->file_fd = -1;
+ }
+
+ gg_netlog(gg, "gg_dcc7filecancel(): Canceled file \"%s\" from/to %d.", dcc7->filename, dcc7->peer_uin);
+
+ // Free transfer
+ gg_dcc7_free(dcc7);
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////
+// File transfer canceled
+int gg_filecancel(PROTO_INTERFACE *proto, HANDLE hContact, HANDLE hTransfer)
+{
+ GGPROTO *gg = (GGPROTO *) proto;
+ struct gg_common *c = (struct gg_common *) hTransfer;
+
+ // Check if its proper dcc
+ if(!c) return 0;
+
+ if (c->type == GG_SESSION_DCC7_SEND || c->type == GG_SESSION_DCC7_GET)
+ return gg_dcc7filecancel((GGPROTO *) proto, hTransfer);
+
+ return gg_dccfilecancel((GGPROTO *) proto, hTransfer);
+}
diff --git a/protocols/Gadu-Gadu/gg.c b/protocols/Gadu-Gadu/gg.c
new file mode 100644
index 0000000000..a9ad33f19b
--- /dev/null
+++ b/protocols/Gadu-Gadu/gg.c
@@ -0,0 +1,774 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2009 Adam Strzelecki <ono+miranda@java.pl>
+// Copyright (c) 2009-2012 Bartosz Białek
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+#include "version.h"
+#include <errno.h>
+
+// Plugin info
+PLUGININFOEX pluginInfo = {
+ sizeof(PLUGININFOEX),
+ "Gadu-Gadu Protocol",
+ __VERSION_DWORD,
+ "Provides support for Gadu-Gadu protocol",
+ "Bartosz Białek, Adam Strzelecki",
+ "dezred"/*antispam*/"@"/*antispam*/"gmail"/*antispam*/"."/*antispam*/"com",
+ "© 2009-2012 Bartosz Białek, 2003-2009 Adam Strzelecki",
+ "http://www.miranda-im.pl/",
+ 0,
+ 0,
+ // {F3FF65F3-250E-416A-BEE9-58C93F85AB33}
+ { 0xf3ff65f3, 0x250e, 0x416a, { 0xbe, 0xe9, 0x58, 0xc9, 0x3f, 0x85, 0xab, 0x33 } }
+};
+static const MUUID interfaces[] = {MIID_PROTOCOL, MIID_LAST};
+
+// Other variables
+HINSTANCE hInstance;
+PLUGINLINK *pluginLink;
+struct MM_INTERFACE mmi;
+struct SHA1_INTERFACE sha1i;
+struct MD5_INTERFACE md5i;
+struct LIST_INTERFACE li;
+XML_API xi;
+SSL_API si;
+CLIST_INTERFACE *pcli;
+int hLangpack;
+list_t g_Instances;
+
+// Event hooks
+static HANDLE hHookModulesLoaded = NULL;
+static HANDLE hHookPreShutdown = NULL;
+
+static unsigned long crc_table[256];
+
+//////////////////////////////////////////////////////////
+// Extra winsock function for error description
+char *ws_strerror(int code)
+{
+ static char err_desc[160];
+
+ // Not a windows error display WinSock
+ if(code == 0)
+ {
+ char buff[128];
+ int len;
+ len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, WSAGetLastError(), 0, buff,
+ sizeof(buff), NULL);
+ if(len == 0)
+ mir_snprintf(err_desc, sizeof(err_desc), "WinSock %u: Unknown error.", WSAGetLastError());
+ else
+ mir_snprintf(err_desc, sizeof(err_desc), "WinSock %d: %s", WSAGetLastError(), buff);
+ return err_desc;
+ }
+
+ // Return normal error
+ return strerror(code);
+}
+
+//////////////////////////////////////////////////////////
+// Build the crc table
+void crc_gentable(void)
+{
+ unsigned long crc, poly;
+ int i, j;
+
+ poly = 0xEDB88320L;
+ for (i = 0; i < 256; i++)
+ {
+ crc = i;
+ for (j = 8; j > 0; j--)
+ {
+ if (crc & 1)
+ crc = (crc >> 1) ^ poly;
+ else
+ crc >>= 1;
+ }
+ crc_table[i] = crc;
+ }
+}
+
+//////////////////////////////////////////////////////////
+// Calculate the crc value
+unsigned long crc_get(char *mem)
+{
+ register unsigned long crc = 0xFFFFFFFF;
+ while(mem && *mem)
+ crc = ((crc>>8) & 0x00FFFFFF) ^ crc_table[(crc ^ *(mem++)) & 0xFF];
+
+ return (crc ^ 0xFFFFFFFF);
+}
+
+//////////////////////////////////////////////////////////
+// http_error_string()
+//
+// returns http error text
+const char *http_error_string(int h)
+{
+ switch (h)
+ {
+ case 0:
+ return Translate((errno == ENOMEM) ? "HTTP failed memory" : "HTTP failed connecting");
+ case GG_ERROR_RESOLVING:
+ return Translate("HTTP failed resolving");
+ case GG_ERROR_CONNECTING:
+ return Translate("HTTP failed connecting");
+ case GG_ERROR_READING:
+ return Translate("HTTP failed reading");
+ case GG_ERROR_WRITING:
+ return Translate("HTTP failed writing");
+ }
+
+ return Translate("Unknown HTTP error");
+}
+
+//////////////////////////////////////////////////////////
+// Gets plugin info
+__declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ if (mirandaVersion < MIRANDA_VERSION_CORE)
+ {
+ MessageBox(
+ NULL,
+ "The Gadu-Gadu protocol plugin cannot be loaded. It requires Miranda IM " MIRANDA_VERSION_CORE_STRING " or later.",
+ "Gadu-Gadu Protocol Plugin",
+ MB_OK | MB_ICONWARNING | MB_SETFOREGROUND | MB_TOPMOST
+ );
+ return NULL;
+ }
+ return &pluginInfo;
+}
+__declspec(dllexport) const MUUID* MirandaPluginInterfaces(void)
+{
+ return interfaces;
+}
+
+//////////////////////////////////////////////////////////
+// Cleanups from last plugin
+void gg_cleanuplastplugin(GGPROTO *gg, DWORD version)
+{
+ HANDLE hContact;
+ char *szProto;
+
+ // Remove bad e-mail and phones from
+ if(version < PLUGIN_MAKE_VERSION(0, 0, 1, 4))
+ {
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_cleanuplastplugin(%d): Cleaning junk Phone settings from < 0.0.1.4 ...", version);
+#endif
+ // Look for contact in DB
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if(szProto != NULL && !strcmp(szProto, GG_PROTO))
+ {
+ // Do contact cleanup
+ DBDeleteContactSetting(hContact, GG_PROTO, GG_KEY_EMAIL);
+ DBDeleteContactSetting(hContact, GG_PROTO, "Phone");
+ }
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+ }
+
+ // Remove GG entries for non GG contacts
+ if(version < PLUGIN_MAKE_VERSION(0, 0, 3, 5))
+ {
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_cleanuplastplugin(%d): Cleaning junk Nick settings from < 0.0.3.5 ...", version);
+#endif
+ // Look for contact in DB
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if(szProto != NULL && strcmp(szProto, GG_PROTO))
+ {
+ // Do nick entry cleanup
+ DBDeleteContactSetting(hContact, GG_PROTO, GG_KEY_NICK);
+ }
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+ }
+
+ // Remove old unneeded entry
+ if(version < PLUGIN_MAKE_VERSION(0, 0, 5, 3))
+ DBDeleteContactSetting(NULL, GG_PROTO, "ShowNotOnMyList");
+
+ // Store this plugin version
+ DBWriteContactSettingDword(NULL, GG_PROTO, GG_PLUGINVERSION, pluginInfo.version);
+}
+
+//////////////////////////////////////////////////////////
+// Custom folders initialization
+void gg_initcustomfolders(GGPROTO *gg)
+{
+ char szPath[MAX_PATH];
+ char *tmpPath = Utils_ReplaceVars("%miranda_avatarcache%");
+ mir_snprintf(szPath, MAX_PATH, "%s\\%s", tmpPath, GG_PROTO);
+ mir_free(tmpPath);
+ gg->hAvatarsFolder = FoldersRegisterCustomPath(GG_PROTO, "Avatars", szPath);
+
+ tmpPath = Utils_ReplaceVars("%miranda_userdata%");
+ mir_snprintf(szPath, MAX_PATH, "%s\\%s\\ImageCache", tmpPath, GG_PROTO);
+ mir_free(tmpPath);
+ gg->hImagesFolder = FoldersRegisterCustomPath(GG_PROTO, "Images", szPath);
+}
+
+//////////////////////////////////////////////////////////
+// When Miranda loaded its modules
+static int gg_modulesloaded(WPARAM wParam, LPARAM lParam)
+{
+ // Get SSL API
+ mir_getSI(&si);
+
+ // File Association Manager support
+ gg_links_init();
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// When Miranda starting shutdown sequence
+static int gg_preshutdown(WPARAM wParam, LPARAM lParam)
+{
+ gg_links_destroy();
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// Gets protocol instance associated with a contact
+static GGPROTO* gg_getprotoinstance(HANDLE hContact)
+{
+ char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ list_t l = g_Instances;
+
+ if (szProto == NULL)
+ return NULL;
+
+ for (; l; l = l->next)
+ {
+ GGPROTO* gg = l->data;
+ if (strcmp(szProto, GG_PROTO) == 0)
+ return gg;
+ }
+
+ return NULL;
+}
+
+//////////////////////////////////////////////////////////
+// Handles PrebuildContactMenu event
+static int gg_prebuildcontactmenu(WPARAM wParam, LPARAM lParam)
+{
+ const HANDLE hContact = (HANDLE)wParam;
+ CLISTMENUITEM mi = {0};
+ GGPROTO* gg = gg_getprotoinstance(hContact);
+
+ if (gg == NULL)
+ return 0;
+
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_NAME | CMIM_FLAGS | CMIF_ICONFROMICOLIB;
+ if (DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0) == DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0) ||
+ DBGetContactSettingByte(hContact, GG_PROTO, "ChatRoom", 0) ||
+ DBGetContactSettingByte(hContact, "CList", "NotOnList", 0))
+ mi.flags |= CMIF_HIDDEN;
+ mi.pszName = DBGetContactSettingByte(hContact, GG_PROTO, GG_KEY_BLOCK, 0) ? LPGEN("&Unblock") : LPGEN("&Block");
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)gg->hBlockMenuItem, (LPARAM)&mi);
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// Contact block service function
+INT_PTR gg_blockuser(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ const HANDLE hContact = (HANDLE)wParam;
+ DBWriteContactSettingByte(hContact, GG_PROTO, GG_KEY_BLOCK, !DBGetContactSettingByte(hContact, GG_PROTO, GG_KEY_BLOCK, 0));
+ gg_notifyuser(gg, hContact, 1);
+ return 0;
+}
+
+
+//////////////////////////////////////////////////////////
+// Contact blocking initialization
+#define GGS_BLOCKUSER "%s/BlockUser"
+static void gg_block_init(GGPROTO *gg)
+{
+ CLISTMENUITEM mi = {0};
+ char service[64];
+
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_ICONFROMICOLIB;
+
+ mir_snprintf(service, sizeof(service), GGS_BLOCKUSER, GG_PROTO);
+ CreateProtoServiceFunction(service, gg_blockuser, gg);
+ mi.position = -500050000;
+ mi.icolibItem = GetIconHandle(IDI_BLOCK);
+ mi.pszName = LPGEN("&Block");
+ mi.pszService = service;
+ mi.pszContactOwner = GG_PROTO;
+ gg->hBlockMenuItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi);
+
+ gg->hPrebuildMenuHook = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, gg_prebuildcontactmenu);
+}
+
+//////////////////////////////////////////////////////////
+// Contact blocking uninitialization
+static void gg_block_uninit(GGPROTO *gg)
+{
+ UnhookEvent(gg->hPrebuildMenuHook);
+ CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)gg->hBlockMenuItem, 0);
+}
+
+//////////////////////////////////////////////////////////
+// Menus initialization
+void gg_menus_init(GGPROTO *gg)
+{
+ HGENMENU hGCRoot, hCLRoot, hRoot = MO_GetProtoRootMenu(gg->proto.m_szModuleName);
+ CLISTMENUITEM mi = {0};
+
+ mi.cbSize = sizeof(mi);
+ if (hRoot == NULL)
+ {
+ mi.ptszName = GG_PROTONAME;
+ mi.position = 500090000;
+ mi.hParentMenu = HGENMENU_ROOT;
+ mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTPOPUP | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED;
+ mi.icolibItem = GetIconHandle(IDI_GG);
+ hGCRoot = hCLRoot = hRoot = gg->hMenuRoot = (HANDLE)CallService(MS_CLIST_ADDPROTOMENUITEM, 0, (LPARAM) &mi);
+ }
+ else
+ {
+ mi.hParentMenu = hRoot;
+ mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTHANDLE | CMIF_TCHAR;
+
+ mi.ptszName = LPGENT("Conference");
+ mi.position = 200001;
+ mi.icolibItem = GetIconHandle(IDI_CONFERENCE);
+ hGCRoot = (HANDLE)CallService(MS_CLIST_ADDPROTOMENUITEM, 0, (LPARAM) &mi);
+
+ mi.ptszName = LPGENT("Contact list");
+ mi.position = 200002;
+ mi.icolibItem = GetIconHandle(IDI_LIST);
+ hCLRoot = (HANDLE)CallService(MS_CLIST_ADDPROTOMENUITEM, 0, (LPARAM) &mi);
+
+ if (gg->hMenuRoot)
+ CallService(MS_CLIST_REMOVEMAINMENUITEM, (WPARAM)gg->hMenuRoot, 0);
+ gg->hMenuRoot = NULL;
+ }
+
+ gg_gc_menus_init(gg, hGCRoot);
+ gg_import_init(gg, hCLRoot);
+ gg_sessions_menus_init(gg, hRoot);
+}
+
+//////////////////////////////////////////////////////////
+// Custom protocol event
+int gg_event(PROTO_INTERFACE *proto, PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam)
+{
+ GGPROTO *gg = (GGPROTO *)proto;
+ switch( eventType )
+ {
+ case EV_PROTO_ONLOAD:
+ {
+ gg->hookOptsInit = HookProtoEvent(ME_OPT_INITIALISE, gg_options_init, gg);
+ gg->hookUserInfoInit = HookProtoEvent(ME_USERINFO_INITIALISE, gg_details_init, gg);
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_event(EV_PROTO_ONLOAD): loading modules...");
+#endif
+ // Init misc stuff
+ gg_icolib_init();
+ gg_initpopups(gg);
+ gg_gc_init(gg);
+ gg_keepalive_init(gg);
+ gg_img_init(gg);
+ gg_block_init(gg);
+
+ // Try to fetch user avatar
+ gg_getuseravatar(gg);
+ break;
+ }
+ case EV_PROTO_ONEXIT:
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_event(EV_PROTO_ONEXIT)/gg_preshutdown(): signalling shutdown...");
+#endif
+ // Stop avatar request thread
+ gg_uninitavatarrequestthread(gg);
+ // Stop main connection session thread
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_event(EV_PROTO_ONEXIT): Waiting until Server Thread finished, if needed.");
+#endif
+ gg_threadwait(gg, &gg->pth_sess);
+ gg_img_shutdown(gg);
+ gg_sessions_closedlg(gg);
+ break;
+
+ case EV_PROTO_ONOPTIONS:
+ return gg_options_init(gg, wParam, lParam);
+
+ case EV_PROTO_ONMENU:
+ gg_menus_init(gg);
+ break;
+
+ case EV_PROTO_ONRENAME:
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_event(EV_PROTO_ONRENAME): renaming account...");
+#endif
+ mir_free(gg->name);
+ gg->name = gg_t2a(gg->proto.m_tszUserName);
+ if (gg->hMenuRoot)
+ {
+ CLISTMENUITEM mi = {0};
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIM_NAME | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED;
+ mi.ptszName = GG_PROTONAME;
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)gg->hMenuRoot, (LPARAM)&mi);
+ }
+ break;
+
+ case EV_PROTO_ONCONTACTDELETED:
+ return gg_contactdeleted(gg, wParam, lParam);
+
+ case EV_PROTO_DBSETTINGSCHANGED:
+ return gg_dbsettingchanged(gg, wParam, lParam);
+ }
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////
+// Module instance initialization
+static GGPROTO *gg_proto_init(const char* pszProtoName, const TCHAR* tszUserName)
+{
+ DWORD dwVersion;
+ GGPROTO *gg = (GGPROTO *)mir_alloc(sizeof(GGPROTO));
+ char szVer[MAX_PATH];
+ NETLIBUSER nlu = { 0 };
+
+ ZeroMemory(gg, sizeof(GGPROTO));
+ gg->proto.vtbl = (PROTO_INTERFACE_VTBL*)mir_alloc(sizeof(PROTO_INTERFACE_VTBL));
+ // Are we running under unicode Miranda core ?
+ CallService(MS_SYSTEM_GETVERSIONTEXT, MAX_PATH, (LPARAM)szVer);
+ _strlwr(szVer); // make sure it is lowercase
+ gg->unicode_core = (strstr(szVer, "unicode") != NULL);
+
+ // Init mutexes
+ InitializeCriticalSection(&gg->sess_mutex);
+ InitializeCriticalSection(&gg->ft_mutex);
+ InitializeCriticalSection(&gg->img_mutex);
+ InitializeCriticalSection(&gg->modemsg_mutex);
+ InitializeCriticalSection(&gg->avatar_mutex);
+ InitializeCriticalSection(&gg->sessions_mutex);
+
+ // Init instance names
+ gg->proto.m_szModuleName = mir_strdup(pszProtoName);
+ gg->proto.m_szProtoName = GGDEF_PROTONAME;
+ gg->proto.m_iVersion = 2;
+
+/* Anyway we won't get Unicode in GG yet */
+#ifdef _UNICODE
+ gg->name = gg->proto.m_tszUserName = mir_tstrdup(tszUserName);
+#else
+ gg->proto.m_tszUserName = gg->unicode_core ? (TCHAR *)mir_wstrdup((wchar_t *)tszUserName) : (TCHAR *)mir_strdup((char *)tszUserName);
+ gg->name = gg_t2a(tszUserName);
+#endif
+
+ // Register netlib user
+ nlu.cbSize = sizeof(nlu);
+ nlu.flags = NUF_OUTGOING | NUF_INCOMING | NUF_HTTPCONNS;
+ nlu.szSettingsModule = gg->proto.m_szModuleName;
+ if (gg->unicode_core) {
+ WCHAR name[128];
+ _snwprintf(name, SIZEOF(name), TranslateW(L"%s connection"), gg->proto.m_tszUserName);
+ nlu.ptszDescriptiveName = (TCHAR *)name;
+ nlu.flags |= NUF_UNICODE;
+ } else {
+ char name[128];
+ mir_snprintf(name, SIZEOF(name), Translate("%s connection"), gg->proto.m_tszUserName);
+ nlu.ptszDescriptiveName = name;
+ }
+ gg->netlib = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu);
+
+ // Register services
+ gg_registerservices(gg);
+
+ // Offline contacts and clear logon time
+ gg_setalloffline(gg);
+ DBWriteContactSettingDword(NULL, GG_PROTO, GG_KEY_LOGONTIME, 0);
+
+ if ((dwVersion = DBGetContactSettingDword(NULL, GG_PROTO, GG_PLUGINVERSION, 0)) < pluginInfo.version)
+ gg_cleanuplastplugin(gg, dwVersion);
+
+ // Add to the instance list
+ list_add(&g_Instances, gg, 0);
+
+ gg_links_instance_init(gg);
+ gg_initcustomfolders(gg);
+ gg_initavatarrequestthread(gg);
+
+ return gg;
+}
+
+//////////////////////////////////////////////////////////
+// Module instance uninitialization
+static int gg_proto_uninit(PROTO_INTERFACE *proto)
+{
+ GGPROTO *gg = (GGPROTO *)proto;
+
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_proto_uninit(): destroying protocol interface");
+#endif
+
+ // Destroy modules
+ gg_block_uninit(gg);
+ gg_img_destroy(gg);
+ gg_keepalive_destroy(gg);
+ gg_gc_destroy(gg);
+
+ // Remove from the instance list
+ list_remove(&g_Instances, gg, 0);
+
+ if (gg->hMenuRoot)
+ CallService(MS_CLIST_REMOVEMAINMENUITEM, (WPARAM)gg->hMenuRoot, 0);
+
+ // Close handles
+ LocalEventUnhook(gg->hookOptsInit);
+ LocalEventUnhook(gg->hookUserInfoInit);
+ Netlib_CloseHandle(gg->netlib);
+
+ // Destroy mutexes
+ DeleteCriticalSection(&gg->sess_mutex);
+ DeleteCriticalSection(&gg->ft_mutex);
+ DeleteCriticalSection(&gg->img_mutex);
+ DeleteCriticalSection(&gg->modemsg_mutex);
+ DeleteCriticalSection(&gg->avatar_mutex);
+ DeleteCriticalSection(&gg->sessions_mutex);
+
+ // Free status messages
+ if (gg->modemsg.online) mir_free(gg->modemsg.online);
+ if (gg->modemsg.away) mir_free(gg->modemsg.away);
+ if (gg->modemsg.dnd) mir_free(gg->modemsg.dnd);
+ if (gg->modemsg.freechat) mir_free(gg->modemsg.freechat);
+ if (gg->modemsg.invisible) mir_free(gg->modemsg.invisible);
+ if (gg->modemsg.offline) mir_free(gg->modemsg.offline);
+
+ mir_free(gg->proto.m_szModuleName);
+ mir_free(gg->proto.m_tszUserName);
+ mir_free(gg->name);
+ mir_free(gg->proto.vtbl);
+ mir_free(gg);
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// When plugin is loaded
+int __declspec(dllexport) Load(PLUGINLINK * link)
+{
+ WSADATA wsaData;
+ PROTOCOLDESCRIPTOR pd;
+
+ pluginLink = link;
+ mir_getMMI(&mmi);
+ mir_getSHA1I(&sha1i);
+ mir_getMD5I(&md5i);
+ mir_getLI(&li);
+ mir_getXI(&xi);
+ mir_getLP(&pluginInfo);
+
+ // Init winsock
+ if (WSAStartup(MAKEWORD( 1, 1 ), &wsaData))
+ return 1;
+
+ pcli = (CLIST_INTERFACE*)CallService(MS_CLIST_RETRIEVE_INTERFACE, 0, (LPARAM)hInstance);
+
+ // Hook system events
+ hHookModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, gg_modulesloaded);
+ hHookPreShutdown = HookEvent(ME_SYSTEM_PRESHUTDOWN, gg_preshutdown);
+
+ // Prepare protocol name
+ ZeroMemory(&pd, sizeof(pd));
+ pd.cbSize = sizeof(pd);
+ pd.szName = GGDEF_PROTO;
+ pd.fnInit = (pfnInitProto)gg_proto_init;
+ pd.fnUninit = (pfnUninitProto)gg_proto_uninit;
+ pd.type = PROTOTYPE_PROTOCOL;
+
+ // Register module
+ CallService(MS_PROTO_REGISTERMODULE, 0, (LPARAM) &pd);
+ gg_links_instancemenu_init();
+
+ // Instance list
+ g_Instances = NULL;
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// When plugin is unloaded
+int __declspec(dllexport) Unload()
+{
+ LocalEventUnhook(hHookModulesLoaded);
+ LocalEventUnhook(hHookPreShutdown);
+
+ // Cleanup WinSock
+ WSACleanup();
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// Adds a new protocol specific service function
+void CreateProtoService(const char* szService, GGPROTOFUNC serviceProc, GGPROTO *gg)
+{
+ char str[MAXMODULELABELLENGTH];
+ mir_snprintf(str, sizeof(str), "%s%s", gg->proto.m_szModuleName, szService);
+ CreateServiceFunctionObj(str, (MIRANDASERVICEOBJ)serviceProc, gg);
+}
+
+//////////////////////////////////////////////////////////
+// Forks a thread
+void gg_forkthread(GGPROTO *gg, GGThreadFunc pFunc, void *param)
+{
+ CloseHandle((HANDLE)mir_forkthreadowner((pThreadFuncOwner)&pFunc, gg, param, NULL));
+}
+
+//////////////////////////////////////////////////////////
+// Forks a thread and returns a pseudo handle for it
+HANDLE gg_forkthreadex(GGPROTO *gg, GGThreadFunc pFunc, void *param, UINT *threadId)
+{
+ return (HANDLE)mir_forkthreadowner((pThreadFuncOwner)&pFunc, gg, param, threadId);
+}
+
+//////////////////////////////////////////////////////////
+// Wait for thread to stop
+void gg_threadwait(GGPROTO *gg, GGTHREAD *thread)
+{
+ if (!thread->hThread) return;
+ while (WaitForSingleObjectEx(thread->hThread, INFINITE, TRUE) != WAIT_OBJECT_0);
+ CloseHandle(thread->hThread);
+ ZeroMemory(thread, sizeof(GGTHREAD));
+}
+
+//////////////////////////////////////////////////////////
+// DEBUGING FUNCTIONS
+struct
+{
+ int type;
+ char *text;
+}
+static const ggdebug_eventype2string[] =
+{
+ {GG_EVENT_NONE, "GG_EVENT_NONE"},
+ {GG_EVENT_MSG, "GG_EVENT_MSG"},
+ {GG_EVENT_NOTIFY, "GG_EVENT_NOTIFY"},
+ {GG_EVENT_NOTIFY_DESCR, "GG_EVENT_NOTIFY_DESCR"},
+ {GG_EVENT_STATUS, "GG_EVENT_STATUS"},
+ {GG_EVENT_ACK, "GG_EVENT_ACK"},
+ {GG_EVENT_PONG, "GG_EVENT_PONG"},
+ {GG_EVENT_CONN_FAILED, "GG_EVENT_CONN_FAILED"},
+ {GG_EVENT_CONN_SUCCESS, "GG_EVENT_CONN_SUCCESS"},
+ {GG_EVENT_DISCONNECT, "GG_EVENT_DISCONNECT"},
+ {GG_EVENT_DCC_NEW, "GG_EVENT_DCC_NEW"},
+ {GG_EVENT_DCC_ERROR, "GG_EVENT_DCC_ERROR"},
+ {GG_EVENT_DCC_DONE, "GG_EVENT_DCC_DONE"},
+ {GG_EVENT_DCC_CLIENT_ACCEPT, "GG_EVENT_DCC_CLIENT_ACCEPT"},
+ {GG_EVENT_DCC_CALLBACK, "GG_EVENT_DCC_CALLBACK"},
+ {GG_EVENT_DCC_NEED_FILE_INFO, "GG_EVENT_DCC_NEED_FILE_INFO"},
+ {GG_EVENT_DCC_NEED_FILE_ACK, "GG_EVENT_DCC_NEED_FILE_ACK"},
+ {GG_EVENT_DCC_NEED_VOICE_ACK, "GG_EVENT_DCC_NEED_VOICE_ACK"},
+ {GG_EVENT_DCC_VOICE_DATA, "GG_EVENT_DCC_VOICE_DATA"},
+ {GG_EVENT_PUBDIR50_SEARCH_REPLY,"GG_EVENT_PUBDIR50_SEARCH_REPLY"},
+ {GG_EVENT_PUBDIR50_READ, "GG_EVENT_PUBDIR50_READ"},
+ {GG_EVENT_PUBDIR50_WRITE, "GG_EVENT_PUBDIR50_WRITE"},
+ {GG_EVENT_STATUS60, "GG_EVENT_STATUS60"},
+ {GG_EVENT_NOTIFY60, "GG_EVENT_NOTIFY60"},
+ {GG_EVENT_USERLIST, "GG_EVENT_USERLIST"},
+ {GG_EVENT_IMAGE_REQUEST, "GG_EVENT_IMAGE_REQUEST"},
+ {GG_EVENT_IMAGE_REPLY, "GG_EVENT_IMAGE_REPLY"},
+ {GG_EVENT_DCC_ACK, "GG_EVENT_DCC_ACK"},
+ {GG_EVENT_DCC7_NEW, "GG_EVENT_DCC7_NEW"},
+ {GG_EVENT_DCC7_ACCEPT, "GG_EVENT_DCC7_ACCEPT"},
+ {GG_EVENT_DCC7_REJECT, "GG_EVENT_DCC7_REJECT"},
+ {GG_EVENT_DCC7_CONNECTED, "GG_EVENT_DCC7_CONNECTED"},
+ {GG_EVENT_DCC7_ERROR, "GG_EVENT_DCC7_ERROR"},
+ {GG_EVENT_DCC7_DONE, "GG_EVENT_DCC7_DONE"},
+ {GG_EVENT_DCC7_PENDING, "GG_EVENT_DCC7_PENDING"},
+ {GG_EVENT_XML_EVENT, "GG_EVENT_XML_EVENT"},
+ {GG_EVENT_DISCONNECT_ACK, "GG_EVENT_DISCONNECT_ACK"},
+ {GG_EVENT_XML_ACTION, "GG_EVENT_XML_ACTION"},
+ {GG_EVENT_TYPING_NOTIFICATION, "GG_EVENT_TYPING_NOTIFICATION"},
+ {GG_EVENT_USER_DATA, "GG_EVENT_USER_DATA"},
+ {GG_EVENT_MULTILOGON_MSG, "GG_EVENT_MULTILOGON_MSG"},
+ {GG_EVENT_MULTILOGON_INFO, "GG_EVENT_MULTILOGON_INFO"},
+ {-1, "<unknown event>"}
+};
+
+const char *ggdebug_eventtype(struct gg_event *e)
+{
+ int i;
+ for(i = 0; ggdebug_eventype2string[i].type != -1; i++)
+ if(ggdebug_eventype2string[i].type == e->type)
+ return ggdebug_eventype2string[i].text;
+ return ggdebug_eventype2string[i].text;
+}
+
+#ifdef DEBUGMODE
+void gg_debughandler(int level, const char *format, va_list ap)
+{
+ char szText[1024], *szFormat = _strdup(format);
+ // Kill end line
+ char *nl = strrchr(szFormat, '\n');
+ if(nl) *nl = 0;
+
+ strncpy(szText, "[libgadu] \0", sizeof(szText));
+
+ mir_vsnprintf(szText + strlen(szText), sizeof(szText) - strlen(szText), szFormat, ap);
+ CallService(MS_NETLIB_LOG, (WPARAM) NULL, (LPARAM) szText);
+ free(szFormat);
+}
+#endif
+
+//////////////////////////////////////////////////////////
+// Log funcion
+int gg_netlog(const GGPROTO *gg, const char *fmt, ...)
+{
+ va_list va;
+ char szText[1024];
+
+ va_start(va, fmt);
+ mir_vsnprintf(szText, sizeof(szText), fmt, va);
+ va_end(va);
+ return CallService(MS_NETLIB_LOG, (WPARAM) gg->netlib, (LPARAM) szText);
+}
+
+//////////////////////////////////////////////////////////
+// main DLL function
+BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, LPVOID reserved)
+{
+ crc_gentable();
+ hInstance = hInst;
+#ifdef DEBUGMODE
+ gg_debug_handler = gg_debughandler;
+#endif
+ return TRUE;
+}
diff --git a/protocols/Gadu-Gadu/gg.h b/protocols/Gadu-Gadu/gg.h
new file mode 100644
index 0000000000..8ebfbd81a8
--- /dev/null
+++ b/protocols/Gadu-Gadu/gg.h
@@ -0,0 +1,506 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2009 Adam Strzelecki <ono+miranda@java.pl>
+// Copyright (c) 2009-2012 Bartosz Białek
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef GG_H
+#define GG_H
+
+#define MIRANDA_VER 0x0A00
+
+#if defined(__DEBUG__) || defined(_DEBUG) || defined(DEBUG)
+#define DEBUGMODE // Debug Mode
+#endif
+
+#if _WIN32_WINNT < 0x0501
+#define _WIN32_WINNT 0x0501
+#endif
+
+#include <m_stdhdr.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Windows headers
+// Visual C++ .NET tries to include winsock.h
+// which is very ver bad
+#if (_MSC_VER >= 1300)
+#include <winsock2.h>
+#else
+#include <windows.h>
+#endif
+#include <commctrl.h>
+#include <commdlg.h>
+#include <process.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/stat.h>
+
+// Miranda IM headers
+#include <newpluginapi.h>
+#include <m_system.h>
+#include <m_database.h>
+#include <m_netlib.h>
+#include <m_protocols.h>
+#include <m_protomod.h>
+#include <m_protosvc.h>
+#include <m_protoint.h>
+#include <m_langpack.h>
+#include <m_plugins.h>
+#include <m_skin.h>
+#include <m_utils.h>
+#include <m_ignore.h>
+#include <m_clist.h>
+#include <m_clistint.h>
+#include <m_options.h>
+#include <m_userinfo.h>
+#include <m_clui.h>
+#include <m_button.h>
+#include <m_clc.h>
+#include <m_message.h>
+#include <m_icolib.h>
+#include <m_imgsrvc.h>
+#include <m_genmenu.h>
+#include <m_file.h>
+#include <m_avatars.h>
+#include <m_xml.h>
+#include <m_chat.h>
+#include <m_popup.h>
+#include <win2k.h>
+
+// Custom profile folders plugin header
+#include "m_folders.h"
+
+// Visual C++ extras
+#ifdef _MSC_VER
+#define vsnprintf _vsnprintf
+#define snprintf _snprintf
+#define GGINLINE
+#else
+#define GGINLINE inline
+#endif
+
+// Plugin headers
+#include "resource.h"
+
+// libgadu headers
+#include "libgadu/libgadu.h"
+#include "dynstuff.h"
+
+// Search
+// Extended search result structure, used for all searches
+typedef struct
+{
+ PROTOSEARCHRESULT hdr;
+ uin_t uin;
+} GGSEARCHRESULT;
+
+typedef struct
+{
+ HANDLE hThread;
+ DWORD dwThreadId;
+} GGTHREAD;
+
+typedef struct
+{
+ PROTO_INTERFACE proto;
+ LPTSTR name;
+ CRITICAL_SECTION ft_mutex, sess_mutex, img_mutex, modemsg_mutex, avatar_mutex, sessions_mutex;
+ list_t watches, transfers, requests, chats, imagedlgs, avatar_requests, avatar_transfers, sessions;
+ int gc_enabled, gc_id, list_remove, unicode_core, check_first_conn;
+ uin_t next_uin;
+ unsigned long last_crc;
+ GGTHREAD pth_dcc;
+ GGTHREAD pth_sess;
+ GGTHREAD pth_avatar;
+ struct gg_session *sess;
+ struct gg_dcc *dcc;
+ HANDLE event;
+ HANDLE hConnStopEvent;
+ SOCKET sock;
+ UINT_PTR timer;
+ struct
+ {
+ char *online;
+ char *away;
+ char *dnd;
+ char *freechat;
+ char *invisible;
+ char *offline;
+ } modemsg;
+ HANDLE netlib,
+ hookOptsInit,
+ hookUserInfoInit,
+ hookGCUserEvent,
+ hookGCMenuBuild;
+ HGENMENU hMenuRoot;
+ HGENMENU hMainMenu[7];
+ HANDLE hPrebuildMenuHook;
+ HANDLE hBlockMenuItem;
+ HANDLE hImageMenuItem;
+ HANDLE hInstanceMenuItem;
+ HANDLE hAvatarsFolder;
+ HANDLE hImagesFolder;
+ HANDLE hwndSessionsDlg;
+} GGPROTO;
+
+typedef struct
+{
+ int mode;
+ uin_t uin;
+ char *pass;
+ char *email;
+ GGPROTO *gg;
+} GGUSERUTILDLGDATA;
+
+typedef struct
+{
+ uin_t *recipients;
+ int recipients_count;
+ char id[32];
+ BOOL ignore;
+} GGGC;
+
+typedef struct
+{
+ char id[256];
+ char val[256];
+} GGTOKEN;
+
+// GG Thread Function
+typedef void (__cdecl GGThreadFunc)(void*, void*);
+
+#if 0 /* #ifdef DEBUGMODE */
+#define EnterCriticalSection(lpCS) {gg_netlog(gg,"EnterCriticalSection @ %s:%d", __FILE__, __LINE__); EnterCriticalSection(lpCS);}
+#define LeaveCriticalSection(lpCS) {gg_netlog(gg,"LeaveCriticalSection @ %s:%d", __FILE__, __LINE__); LeaveCriticalSection(lpCS);}
+#endif
+
+
+// Wrappers of the old interface
+#define GG_PROTO (gg->proto.m_szModuleName)
+#define GG_PROTONAME (gg->name)
+#define GGDEF_PROTO "GG" // Default Proto
+#define GGDEF_PROTONAME "Gadu-Gadu" // Default ProtoName
+
+
+// Process handles / seqs
+#define GG_SEQ_INFO 100
+#define GG_SEQ_SEARCH 200
+#define GG_SEQ_GETNICK 300
+#define GG_SEQ_CHINFO 400
+
+// Services
+#define GGS_IMPORT_SERVER "%s/ImportFromServer"
+#define GGS_REMOVE_SERVER "%s/RemoveFromServer"
+#define GGS_IMPORT_TEXT "%s/ImportFromText"
+#define GGS_EXPORT_SERVER "%s/ExportFromServer"
+#define GGS_EXPORT_TEXT "%s/ExportFromText"
+
+#define GGS_SENDIMAGE "%s/SendImage"
+#define GGS_RECVIMAGE "%s/RecvImage"
+
+// Keys
+#define GG_PLUGINVERSION "Version" // Plugin version.. user for cleanup from previous versions
+
+#define GG_KEY_UIN "UIN" // Uin - unique number
+#define GG_KEY_PASSWORD "Password" // Password
+#define GG_KEY_EMAIL "e-mail" // E-mail
+#define GG_KEY_STATUS "Status" // Status
+#define GG_KEY_NICK "Nick" // Nick
+#define GG_KEY_STATUSDESCR "StatusMsg" // Users status description, to be compatible with MWClist
+ // should be stored in "CList" group
+#define GG_KEY_TOKEN "Token" // OAuth Access Token
+#define GG_KEY_TOKENSECRET "TokenSecret" // OAuth Access Token Secret
+
+#define GG_KEY_KEEPALIVE "KeepAlive" // Keep-alive support
+#define GG_KEYDEF_KEEPALIVE 1
+
+#define GG_KEY_SHOWCERRORS "ShowCErrors" // Show connection errors
+#define GG_KEYDEF_SHOWCERRORS 1
+
+#define GG_KEY_ARECONNECT "AReconnect" // Automatically reconnect
+#define GG_KEYDEF_ARECONNECT 0
+
+#define GG_KEY_LEAVESTATUSMSG "LeaveStatusMsg"// Leave status msg when disconnected
+#define GG_KEYDEF_LEAVESTATUSMSG 0
+#define GG_KEY_LEAVESTATUS "LeaveStatus"
+#define GG_KEYDEF_LEAVESTATUS 0
+
+#define GG_KEY_FRIENDSONLY "FriendsOnly" // Friend only visibility
+#define GG_KEYDEF_FRIENDSONLY 0
+
+#define GG_KEY_SHOWLINKS "ShowLinks" // Show links from unknown contacts
+#define GG_KEYDEF_SHOWLINKS 0
+
+#define GG_KEY_ENABLEAVATARS "EnableAvatars" // Enable avatars support
+#define GG_KEYDEF_ENABLEAVATARS 1
+
+#define GG_KEY_AVATARHASH "AvatarHash" // Contact's avatar hash
+
+#define GG_KEY_AVATARURL "AvatarURL" // Contact's avatar URL
+
+#define GG_KEY_AVATARTYPE "AvatarType" // Contact's avatar format
+#define GG_KEYDEF_AVATARTYPE PA_FORMAT_UNKNOWN
+
+#define GG_KEY_AVATARREQUESTED "AvatarRequested" // When contact's avatar is requested
+#define GG_KEYDEF_AVATARREQUESTED 0
+
+#define GG_KEY_SHOWINVISIBLE "ShowInvisible" // Show invisible users when described
+#define GG_KEYDEF_SHOWINVISIBLE 0
+
+#define GG_KEY_IGNORECONF "IgnoreConf" // Ignore incoming conference messages
+#define GG_KEYDEF_IGNORECONF 0
+
+#define GG_KEY_IMGRECEIVE "ReceiveImg" // Popup image window automatically
+#define GG_KEYDEF_IMGRECEIVE 1
+
+#define GG_KEY_IMGMETHOD "PopupImg" // Popup image window automatically
+#define GG_KEYDEF_IMGMETHOD 1
+
+#define GG_KEY_MSGACK "MessageAck" // Acknowledge when sending msg
+#define GG_KEYDEF_MSGACK 1
+
+#define GG_KEY_MANUALHOST "ManualHost" // Specify by hand server host/port
+#define GG_KEYDEF_MANUALHOST 0
+#define GG_KEY_SSLCONN "SSLConnection" // Use SSL/TLS for connections
+#define GG_KEYDEF_SSLCONN 0
+#define GG_KEY_SERVERHOSTS "ServerHosts" // NL separated list of hosts for server connection
+#define GG_KEYDEF_SERVERHOSTS "91.197.13.54\r\n91.197.13.66\r\n91.197.13.69\r\n91.197.13.72\r\n91.197.13.75\r\n91.197.13.81"
+
+#define GG_KEY_CLIENTIP "IP" // Contact IP (by notify)
+#define GG_KEY_CLIENTPORT "ClientPort" // Contact port
+#define GG_KEY_CLIENTVERSION "ClientVersion" // Contact app version
+
+#define GG_KEY_DIRECTCONNS "DirectConns" // Use direct connections
+#define GG_KEYDEF_DIRECTCONNS 1
+#define GG_KEY_DIRECTPORT "DirectPort" // Direct connections port
+#define GG_KEYDEF_DIRECTPORT 1550
+
+#define GG_KEY_FORWARDING "Forwarding" // Use forwarding
+#define GG_KEYDEF_FORWARDING 0
+#define GG_KEY_FORWARDHOST "ForwardHost" // Forwarding host (firewall)
+#define GG_KEY_FORWARDPORT "ForwardPort" // Forwarding port (firewall port)
+#define GG_KEYDEF_FORWARDPORT 1550 // Forwarding port (firewall port)
+
+#define GG_KEY_GC_POLICY_UNKNOWN "GCPolicyUnknown"
+#define GG_KEYDEF_GC_POLICY_UNKNOWN 1
+
+#define GG_KEY_GC_COUNT_UNKNOWN "GCCountUnknown"
+#define GG_KEYDEF_GC_COUNT_UNKNOWN 5
+
+#define GG_KEY_GC_POLICY_TOTAL "GCPolicyTotal"
+#define GG_KEYDEF_GC_POLICY_TOTAL 1
+
+#define GG_KEY_GC_COUNT_TOTAL "GCCountTotal"
+#define GG_KEYDEF_GC_COUNT_TOTAL 10
+
+#define GG_KEY_GC_POLICY_DEFAULT "GCPolicyDefault"
+#define GG_KEYDEF_GC_POLICY_DEFAULT 0
+
+#define GG_KEY_BLOCK "Block" // Contact is blocked
+#define GG_KEY_APPARENT "ApparentMode" // Visible list
+
+#define GG_KEY_TIMEDEVIATION "TimeDeviation" // Max time deviation for connections (seconds)
+#define GG_KEYDEF_TIMEDEVIATION 300
+
+#define GG_KEY_LOGONTIME "LogonTS"
+
+#define GG_KEY_RECONNINTERVAL "ReconnectInterval"
+#define GG_KEYDEF_RECONNINTERVAL 3000
+
+// chpassdlgproc() multipurpose dialog proc modes
+#define GG_USERUTIL_PASS 0
+#define GG_USERUTIL_CREATE 1
+#define GG_USERUTIL_REMOVE 2
+#define GG_USERUTIL_EMAIL 3
+
+// popup flags
+#define GG_POPUP_ALLOW_MSGBOX 1
+#define GG_POPUP_ONCE 2
+#define GG_POPUP_ERROR 4
+#define GG_POPUP_WARNING 8
+#define GG_POPUP_MULTILOGON 16
+
+#define LocalEventUnhook(hook) if(hook) UnhookEvent(hook)
+
+// Some MSVC compatibility with gcc
+#ifdef _MSC_VER
+#ifndef strcasecmp
+#define strcasecmp _strcmpi
+#endif
+#ifndef strncasecmp
+#define strncasecmp _strnicmp
+#endif
+#endif
+
+// Global variables
+/////////////////////////////////////////////////
+
+extern HINSTANCE hInstance;
+extern PLUGINLINK *pluginLink;
+extern CLIST_INTERFACE *pcli;
+extern struct LIST_INTERFACE li;
+extern list_t g_Instances;
+
+// Screen saver
+#ifndef SPI_GETSCREENSAVERRUNNING
+#define SPI_GETSCREENSAVERRUNNING 114
+#endif
+
+/////////////////////////////////////////////////
+// Methods
+
+/* Helper functions */
+const char *http_error_string(int h);
+unsigned long crc_get(char *mem);
+int status_m2gg(GGPROTO *gg, int status, int descr);
+int status_gg2m(GGPROTO *gg, int status);
+char *gg_status2db(int status, const char *suffix);
+char *ws_strerror(int code);
+uint32_t swap32(uint32_t x);
+const char *gg_version2string(int v);
+void gg_checknewuser(GGPROTO* gg, uin_t uin, const char* passwd);
+
+/* Thread functions */
+void gg_forkthread(GGPROTO *gg, GGThreadFunc pFunc, void *param);
+HANDLE gg_forkthreadex(GGPROTO *gg, GGThreadFunc pFunc, void *param, UINT *threadId);
+void gg_threadwait(GGPROTO *gg, GGTHREAD *thread);
+
+/* Global GG functions */
+void gg_notifyuser(GGPROTO *gg, HANDLE hContact, int refresh);
+void gg_setalloffline(GGPROTO *gg);
+void gg_disconnect(GGPROTO *gg);
+HANDLE gg_getcontact(GGPROTO *gg, uin_t uin, int create, int inlist, char *nick);
+void gg_registerservices(GGPROTO *gg);
+void __cdecl gg_mainthread(GGPROTO *gg, void *empty);
+int gg_isonline(GGPROTO *gg);
+int gg_refreshstatus(GGPROTO *gg, int status);
+
+void gg_broadcastnewstatus(GGPROTO *gg, int newStatus);
+int gg_contactdeleted(GGPROTO *gg, WPARAM wParam, LPARAM lParam);
+int gg_dbsettingchanged(GGPROTO *gg, WPARAM wParam, LPARAM lParam);
+void gg_notifyall(GGPROTO *gg);
+void gg_changecontactstatus(GGPROTO *gg, uin_t uin, int status, const char *idescr, int time, uint32_t remote_ip, uint16_t remote_port, uint32_t version);
+char *gg_getstatusmsg(GGPROTO *gg, int status);
+void gg_dccstart(GGPROTO *gg);
+void gg_dccconnect(GGPROTO *gg, uin_t uin);
+int gg_gettoken(GGPROTO *gg, GGTOKEN *token);
+void gg_parsecontacts(GGPROTO *gg, char *contacts);
+int gg_getinfo(PROTO_INTERFACE *proto, HANDLE hContact, int infoType);
+void gg_remindpassword(GGPROTO *gg, uin_t uin, const char *email);
+void *gg_img_loadpicture(GGPROTO *gg, struct gg_event* e, char *szFileName);
+int gg_img_releasepicture(void *img);
+int gg_img_display(GGPROTO *gg, HANDLE hContact, void *img);
+int gg_img_displayasmsg(GGPROTO *gg, HANDLE hContact, void *img);
+int gg_event(PROTO_INTERFACE *proto, PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam);
+
+/* Avatar functions */
+void gg_getavatarfilename(GGPROTO *gg, HANDLE hContact, char *pszDest, int cbLen);
+char *gg_avatarhash(char *param);
+void gg_getavatar(GGPROTO *gg, HANDLE hContact, char *szAvatarURL);
+void gg_requestavatar(GGPROTO *gg, HANDLE hContact, int iWaitFor);
+void gg_initavatarrequestthread(GGPROTO *gg);
+void gg_uninitavatarrequestthread(GGPROTO *gg);
+void gg_getuseravatar(GGPROTO *gg);
+void gg_setavatar(GGPROTO *gg, const char *szFilename);
+INT_PTR gg_getavatarinfo(GGPROTO *gg, WPARAM wParam, LPARAM lParam);
+
+/* File transfer functions */
+HANDLE gg_fileallow(PROTO_INTERFACE *proto, HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath);
+int gg_filecancel(PROTO_INTERFACE *proto, HANDLE hContact, HANDLE hTransfer);
+int gg_filedeny(PROTO_INTERFACE *proto, HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szReason);
+int gg_recvfile(PROTO_INTERFACE *proto, HANDLE hContact, PROTOFILEEVENT* pre);
+HANDLE gg_sendfile(PROTO_INTERFACE *proto, HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles);
+
+/* Import module */
+void gg_import_init(GGPROTO *gg, HGENMENU hRoot);
+
+/* Keep-alive module */
+void gg_keepalive_init(GGPROTO *gg);
+void gg_keepalive_destroy(GGPROTO *gg);
+
+/* Image reception functions */
+int gg_img_init(GGPROTO *gg);
+int gg_img_destroy(GGPROTO *gg);
+int gg_img_shutdown(GGPROTO *gg);
+INT_PTR gg_img_recvimage(GGPROTO *gg, WPARAM wParam, LPARAM lParam);
+INT_PTR gg_img_sendimage(GGPROTO *gg, WPARAM wParam, LPARAM lParam);
+int gg_img_sendonrequest(GGPROTO *gg, struct gg_event* e);
+BOOL gg_img_opened(GGPROTO *gg, uin_t uin);
+
+/* IcoLib functions */
+void gg_icolib_init();
+HICON LoadIconEx(const char* name, BOOL big);
+HANDLE GetIconHandle(int iconId);
+void ReleaseIconEx(const char* name, BOOL big);
+void WindowSetIcon(HWND hWnd, const char* name);
+void WindowFreeIcon(HWND hWnd);
+
+/* URI parser functions */
+void gg_links_instancemenu_init();
+void gg_links_init();
+void gg_links_destroy();
+void gg_links_instance_init(GGPROTO* gg);
+
+/* OAuth functions */
+char *gg_oauth_header(GGPROTO *gg, const char *httpmethod, const char *url);
+int gg_oauth_checktoken(GGPROTO *gg, int force);
+
+/* UI page initializers */
+int gg_options_init(GGPROTO *gg, WPARAM wParam, LPARAM lParam);
+int gg_details_init(GGPROTO *gg, WPARAM wParam, LPARAM lParam);
+
+/* Groupchat functions */
+int gg_gc_init(GGPROTO *gg);
+void gg_gc_menus_init(GGPROTO *gg, HGENMENU hRoot);
+int gg_gc_destroy(GGPROTO *gg);
+char * gg_gc_getchat(GGPROTO *gg, uin_t sender, uin_t *recipients, int recipients_count);
+GGGC *gg_gc_lookup(GGPROTO *gg, char *id);
+int gg_gc_changenick(GGPROTO *gg, HANDLE hContact, char *pszNick);
+#define UIN2ID(uin,id) _itoa(uin,id,10)
+
+/* Popups functions */
+void gg_initpopups(GGPROTO* gg);
+void gg_showpopup(GGPROTO* gg, const char* nickname, const char* msg, int flags);
+
+/* Sessions functions */
+INT_PTR gg_sessions_view(GGPROTO* gg, WPARAM wParam, LPARAM lParam);
+void gg_sessions_updatedlg(GGPROTO* gg);
+BOOL gg_sessions_closedlg(GGPROTO* gg);
+void gg_sessions_menus_init(GGPROTO* gg, HGENMENU hRoot);
+
+/* Event helpers */
+#define HookProtoEvent(name, func, proto) HookEventObj(name, (MIRANDAHOOKOBJ)func, proto)
+#define CreateProtoServiceFunction(name, func, proto) CreateServiceFunctionObj(name, (MIRANDASERVICEOBJ)func, proto)
+typedef INT_PTR (*GGPROTOFUNC)(GGPROTO*,WPARAM,LPARAM);
+void CreateProtoService(const char* szService, GGPROTOFUNC serviceProc, GGPROTO *gg);
+
+/* ANSI <-> Unicode conversion helpers */
+#define gg_a2t(s) gg->unicode_core ? (TCHAR *)mir_a2u(s) : (TCHAR *)mir_strdup(s)
+#define gg_t2a(s) gg->unicode_core ? mir_u2a((wchar_t *)s) : mir_strdup(s)
+
+// Debug functions
+int gg_netlog(const GGPROTO *gg, const char *fmt, ...);
+const char *ggdebug_eventtype(struct gg_event *e);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/protocols/Gadu-Gadu/groupchat.c b/protocols/Gadu-Gadu/groupchat.c
new file mode 100644
index 0000000000..26a797f163
--- /dev/null
+++ b/protocols/Gadu-Gadu/groupchat.c
@@ -0,0 +1,681 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2006 Adam Strzelecki <ono+miranda@java.pl>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+#include "m_metacontacts.h"
+
+#define GG_GC_GETCHAT "%s/GCGetChat"
+#define GGS_OPEN_CONF "%s/OpenConf"
+#define GGS_CLEAR_IGNORED "%s/ClearIgnored"
+
+int gg_gc_event(GGPROTO *gg, WPARAM wParam, LPARAM lParam);
+INT_PTR gg_gc_openconf(GGPROTO *gg, WPARAM wParam, LPARAM lParam);
+INT_PTR gg_gc_clearignored(GGPROTO *gg, WPARAM wParam, LPARAM lParam);
+
+////////////////////////////////////////////////////////////////////////////////
+// Inits Gadu-Gadu groupchat module using chat.dll
+int gg_gc_init(GGPROTO *gg)
+{
+ if(ServiceExists(MS_GC_REGISTER))
+ {
+ char service[64];
+ GCREGISTER gcr = {0};
+
+ // Register Gadu-Gadu proto
+ gcr.cbSize = sizeof(GCREGISTER);
+ gcr.dwFlags = GC_TCHAR;
+ gcr.iMaxText = 0;
+ gcr.nColors = 0;
+ gcr.pColors = 0;
+ gcr.ptszModuleDispName = gg->proto.m_tszUserName;
+ gcr.pszModule = GG_PROTO;
+ CallServiceSync(MS_GC_REGISTER, 0, (LPARAM)&gcr);
+ gg->hookGCUserEvent = HookProtoEvent(ME_GC_EVENT, gg_gc_event, gg);
+ gg->gc_enabled = TRUE;
+ // create & hook event
+ mir_snprintf(service, 64, GG_GC_GETCHAT, GG_PROTO);
+ gg_netlog(gg, "gg_gc_init(): Registered with groupchat plugin.");
+ }
+ else
+ gg_netlog(gg, "gg_gc_init(): Cannot register with groupchat plugin !!!");
+
+ return 1;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Groupchat menus initialization
+void gg_gc_menus_init(GGPROTO *gg, HGENMENU hRoot)
+{
+ if(gg->gc_enabled)
+ {
+ CLISTMENUITEM mi = {0};
+ char service[64];
+
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTHANDLE;
+ mi.hParentMenu = hRoot;
+
+ // Conferencing
+ mir_snprintf(service, sizeof(service), GGS_OPEN_CONF, GG_PROTO);
+ CreateProtoServiceFunction(service, gg_gc_openconf, gg);
+ mi.position = 2000050001;
+ mi.icolibItem = GetIconHandle(IDI_CONFERENCE);
+ mi.pszName = LPGEN("Open &conference...");
+ mi.pszService = service;
+ gg->hMainMenu[0] = (HANDLE)CallService(MS_CLIST_ADDPROTOMENUITEM, 0, (LPARAM) &mi);
+
+ // Clear ignored conferences
+ mir_snprintf(service, sizeof(service), GGS_CLEAR_IGNORED, GG_PROTO);
+ CreateProtoServiceFunction(service, gg_gc_clearignored, gg);
+ mi.position = 2000050002;
+ mi.icolibItem = GetIconHandle(IDI_CLEAR_CONFERENCE);
+ mi.pszName = LPGEN("&Clear ignored conferences");
+ mi.pszService = service;
+ gg->hMainMenu[1] = (HANDLE)CallService(MS_CLIST_ADDPROTOMENUITEM, 0, (LPARAM) &mi);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Releases Gadu-Gadu groupchat module using chat.dll
+int gg_gc_destroy(GGPROTO *gg)
+{
+ list_t l;
+ for(l = gg->chats; l; l = l->next)
+ {
+ GGGC *chat = (GGGC *)l->data;
+ if(chat->recipients) free(chat->recipients);
+ }
+ list_destroy(gg->chats, 1); gg->chats = NULL;
+ LocalEventUnhook(gg->hookGCUserEvent);
+ LocalEventUnhook(gg->hookGCMenuBuild);
+
+ return 1;
+}
+
+GGGC *gg_gc_lookup(GGPROTO *gg, char *id)
+{
+ GGGC *chat;
+ list_t l;
+
+ for(l = gg->chats; l; l = l->next)
+ {
+ chat = (GGGC *)l->data;
+ if(chat && !strcmp(chat->id, id))
+ return chat;
+ }
+
+ return NULL;
+}
+
+int gg_gc_event(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ GCHOOK *gch = (GCHOOK *)lParam;
+ GGGC *chat = NULL;
+ uin_t uin;
+
+ // Check if we got our protocol, and fields are set
+ if(!gch
+ || !gch->pDest
+ || !gch->pDest->pszID
+ || !gch->pDest->pszModule
+ || lstrcmpi(gch->pDest->pszModule, GG_PROTO)
+ || !(uin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0))
+ || !(chat = gg_gc_lookup(gg, gch->pDest->pszID)))
+ return 0;
+
+ // Window terminated
+ if(gch->pDest->iType == SESSION_TERMINATE)
+ {
+ HANDLE hContact = NULL;
+ gg_netlog(gg, "gg_gc_event(): Terminating chat %x, id %s from chat window...", chat, gch->pDest->pszID);
+ // Destroy chat entry
+ free(chat->recipients);
+ list_remove(&gg->chats, chat, 1);
+ // Remove contact from contact list (duh!) should be done by chat.dll !!
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ DBVARIANT dbv;
+ if(!DBGetContactSettingString(hContact, GG_PROTO, "ChatRoomID", &dbv))
+ {
+ if(dbv.pszVal && !strcmp(gch->pDest->pszID, dbv.pszVal))
+ CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0);
+ DBFreeVariant(&dbv);
+ }
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+ return 1;
+ }
+
+ // Message typed / send only if online
+ if(gg_isonline(gg) && (gch->pDest->iType == GC_USER_MESSAGE) && gch->pszText)
+ {
+ char id[32];
+ DBVARIANT dbv;
+ GCDEST gcdest = {GG_PROTO, gch->pDest->pszID, GC_EVENT_MESSAGE};
+ GCEVENT gcevent = {sizeof(GCEVENT), &gcdest};
+ int lc;
+
+ UIN2ID(uin, id);
+
+ gcevent.pszUID = id;
+ gcevent.pszText = gch->pszText;
+ if(!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_NICK, &dbv))
+ gcevent.pszNick = dbv.pszVal;
+ else
+ gcevent.pszNick = Translate("Me");
+
+ // Get rid of CRLF at back
+ lc = (int)strlen(gch->pszText) - 1;
+ while(lc >= 0 && (gch->pszText[lc] == '\n' || gch->pszText[lc] == '\r')) gch->pszText[lc --] = 0;
+ gcevent.time = time(NULL);
+ gcevent.bIsMe = 1;
+ gcevent.dwFlags = GCEF_ADDTOLOG;
+ gg_netlog(gg, "gg_gc_event(): Sending conference message to room %s, \"%s\".", gch->pDest->pszID, gch->pszText);
+ CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent);
+ if(gcevent.pszNick == dbv.pszVal) DBFreeVariant(&dbv);
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_send_message_confer(gg->sess, GG_CLASS_CHAT, chat->recipients_count, chat->recipients, gch->pszText);
+ LeaveCriticalSection(&gg->sess_mutex);
+ return 1;
+ }
+
+ // Privmessage selected
+ if(gch->pDest->iType == GC_USER_PRIVMESS)
+ {
+ HANDLE hContact = NULL;
+ if((uin = atoi(gch->pszUID)) && (hContact = gg_getcontact(gg, uin, 1, 0, NULL)))
+ CallService(MS_MSG_SENDMESSAGE, (WPARAM)hContact, (LPARAM)0);
+ }
+ gg_netlog(gg, "gg_gc_event(): Unhandled event %d, chat %x, uin %d, text \"%s\".", gch->pDest->iType, chat, uin, gch->pszText);
+
+ return 0;
+}
+
+typedef struct _gg_gc_echat
+{
+ uin_t sender;
+ uin_t *recipients;
+ int recipients_count;
+ char * chat_id;
+} gg_gc_echat;
+
+////////////////////////////////////////////////////////////////////////////////
+// This is main groupchat initialization routine
+char *gg_gc_getchat(GGPROTO *gg, uin_t sender, uin_t *recipients, int recipients_count)
+{
+ list_t l; int i;
+ GGGC *chat;
+ char *senderName, status[256], *name, id[32];
+ uin_t uin; DBVARIANT dbv;
+ GCSESSION gcwindow;
+ GCDEST gcdest = {GG_PROTO, 0, GC_EVENT_ADDGROUP};
+ GCEVENT gcevent = {sizeof(GCEVENT), &gcdest};
+
+ gg_netlog(gg, "gg_gc_getchat(): Count %d.", recipients_count);
+ if(!recipients) return NULL;
+
+ // Look for existing chat
+ for(l = gg->chats; l; l = l->next)
+ {
+ GGGC *chat = (GGGC *)l->data;
+ if(!chat) continue;
+
+ if(chat->recipients_count == recipients_count + (sender ? 1 : 0))
+ {
+ int i, j, found = 0, sok = (sender == 0);
+ if(!sok) for(i = 0; i < chat->recipients_count; i++)
+ if(sender == chat->recipients[i])
+ {
+ sok = 1;
+ break;
+ }
+ if(sok)
+ for(i = 0; i < chat->recipients_count; i++)
+ for(j = 0; j < recipients_count; j++)
+ if(recipients[j] == chat->recipients[i]) found++;
+ // Found all recipients
+ if(found == recipients_count)
+ {
+ if(chat->ignore)
+ gg_netlog(gg, "gg_gc_getchat(): Ignoring existing id %s, size %d.", chat->id, chat->recipients_count);
+ else
+ gg_netlog(gg, "gg_gc_getchat(): Returning existing id %s, size %d.", chat->id, chat->recipients_count);
+ return !(chat->ignore) ? chat->id : NULL;
+ }
+ }
+ }
+
+ // Make new uin list to chat mapping
+ chat = (GGGC *)malloc(sizeof(GGGC));
+ UIN2ID(gg->gc_id ++, chat->id); chat->ignore = FALSE;
+
+ // Check groupchat policy (new) / only for incoming
+ if(sender)
+ {
+ int unknown = (gg_getcontact(gg, sender, 0, 0, NULL) == NULL),
+ unknownSender = unknown;
+ for(i = 0; i < recipients_count; i++)
+ if(!gg_getcontact(gg, recipients[i], 0, 0, NULL))
+ unknown ++;
+ if((DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_DEFAULT, GG_KEYDEF_GC_POLICY_DEFAULT) == 2) ||
+ (DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_TOTAL, GG_KEYDEF_GC_POLICY_TOTAL) == 2 &&
+ recipients_count >= DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_COUNT_TOTAL, GG_KEYDEF_GC_COUNT_TOTAL)) ||
+ (DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_UNKNOWN, GG_KEYDEF_GC_POLICY_UNKNOWN) == 2 &&
+ unknown >= DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_COUNT_UNKNOWN, GG_KEYDEF_GC_COUNT_UNKNOWN)))
+ chat->ignore = TRUE;
+ if(!chat->ignore && ((DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_DEFAULT, GG_KEYDEF_GC_POLICY_DEFAULT) == 1) ||
+ (DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_TOTAL, GG_KEYDEF_GC_POLICY_TOTAL) == 1 &&
+ recipients_count >= DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_COUNT_TOTAL, GG_KEYDEF_GC_COUNT_TOTAL)) ||
+ (DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_POLICY_UNKNOWN, GG_KEYDEF_GC_POLICY_UNKNOWN) == 1 &&
+ unknown >= DBGetContactSettingWord(NULL, GG_PROTO, GG_KEY_GC_COUNT_UNKNOWN, GG_KEYDEF_GC_COUNT_UNKNOWN))))
+ {
+ char *senderName = unknownSender ?
+ Translate("Unknown") : ((char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) gg_getcontact(gg, sender, 0, 0, NULL), 0));
+ char error[256];
+ mir_snprintf(error, sizeof(error), Translate("%s has initiated conference with %d participants (%d unknowns).\nDo you want do participate ?"),
+ senderName, recipients_count + 1, unknown);
+ chat->ignore = (MessageBox(
+ NULL,
+ error,
+ GG_PROTONAME,
+ MB_OKCANCEL | MB_ICONEXCLAMATION
+ ) != IDOK);
+ }
+ if(chat->ignore)
+ {
+ // Copy recipient list
+ chat->recipients_count = recipients_count + (sender ? 1 : 0);
+ chat->recipients = (uin_t *)calloc(chat->recipients_count, sizeof(uin_t));
+ for(i = 0; i < recipients_count; i++)
+ chat->recipients[i] = recipients[i];
+ if(sender) chat->recipients[i] = sender;
+ gg_netlog(gg, "gg_gc_getchat(): Ignoring new chat %s, count %d.", chat->id, chat->recipients_count);
+ list_add(&gg->chats, chat, 0);
+ return NULL;
+ }
+ }
+
+ // Create new chat window
+ senderName = sender ? (char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) gg_getcontact(gg, sender, 1, 0, NULL), 0) : NULL;
+ mir_snprintf(status, 255, sender ? Translate("%s initiated the conference.") : Translate("This is my own conference."), senderName);
+ gcwindow.cbSize = sizeof(GCSESSION);
+ gcwindow.iType = GCW_CHATROOM;
+ gcwindow.pszModule = GG_PROTO;
+ gcwindow.pszName = sender ? senderName : Translate("Conference");
+ gcwindow.pszID = chat->id;
+ gcwindow.pszStatusbarText = status;
+ gcwindow.dwFlags = 0;
+ gcwindow.dwItemData = (DWORD)chat;
+
+ // Here we put nice new hash sign
+ name = calloc(strlen(gcwindow.pszName) + 2, sizeof(char));
+ *name = '#'; strcpy(name + 1, gcwindow.pszName);
+ gcwindow.pszName = name;
+ // Create new room
+ if(CallServiceSync(MS_GC_NEWSESSION, 0, (LPARAM) &gcwindow))
+ {
+ gg_netlog(gg, "gg_gc_getchat(): Cannot create new chat window %s.", chat->id);
+ free(name);
+ free(chat);
+ return NULL;
+ }
+ free(name);
+
+ gcdest.pszID = chat->id;
+ gcevent.pszUID = id;
+ gcevent.dwFlags = GCEF_ADDTOLOG;
+ gcevent.time = 0;
+
+ // Add normal group
+ gcevent.pszStatus = Translate("Participants");
+ CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent);
+ gcdest.iType = GC_EVENT_JOIN;
+
+ // Add myself
+ if(uin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0))
+ {
+ UIN2ID(uin, id);
+ if(!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_NICK, &dbv))
+ gcevent.pszNick = dbv.pszVal;
+ else
+ gcevent.pszNick = Translate("Me");
+ gcevent.bIsMe = 1;
+ CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent);
+ gg_netlog(gg, "gg_gc_getchat(): Myself %s: %s (%s) to the list...", gcevent.pszUID, gcevent.pszNick, gcevent.pszStatus);
+ if(gcevent.pszNick == dbv.pszVal) DBFreeVariant(&dbv);
+ }
+ else gg_netlog(gg, "gg_gc_getchat(): Myself adding failed with uin %d !!!", uin);
+
+ // Copy recipient list
+ chat->recipients_count = recipients_count + (sender ? 1 : 0);
+ chat->recipients = (uin_t *)calloc(chat->recipients_count, sizeof(uin_t));
+ for(i = 0; i < recipients_count; i++)
+ chat->recipients[i] = recipients[i];
+ if(sender) chat->recipients[i] = sender;
+
+ // Add contacts
+ for(i = 0; i < chat->recipients_count; i++)
+ {
+ HANDLE hContact = gg_getcontact(gg, chat->recipients[i], 1, 0, NULL);
+ UIN2ID(chat->recipients[i], id);
+ if(hContact && (name = (char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM) hContact, 0)))
+ gcevent.pszNick = name;
+ else
+ gcevent.pszNick = Translate("'Unknown'");
+ gcevent.bIsMe = 0;
+ gg_netlog(gg, "gg_gc_getchat(): Added %s: %s (%s) to the list...", gcevent.pszUID, gcevent.pszNick, gcevent.pszStatus);
+ CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent);
+ }
+ gcdest.iType = GC_EVENT_CONTROL;
+ CallServiceSync(MS_GC_EVENT, SESSION_INITDONE, (LPARAM)&gcevent);
+ CallServiceSync(MS_GC_EVENT, SESSION_ONLINE, (LPARAM)&gcevent);
+
+ gg_netlog(gg, "gg_gc_getchat(): Returning new chat window %s, count %d.", chat->id, chat->recipients_count);
+ list_add(&gg->chats, chat, 0);
+ return chat->id;
+}
+
+static HANDLE gg_getsubcontact(GGPROTO* gg, HANDLE hContact)
+{
+ char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ char* szMetaProto = (char*)CallService(MS_MC_GETPROTOCOLNAME, 0, 0);
+
+ if (szProto && szMetaProto && (INT_PTR)szMetaProto != CALLSERVICE_NOTFOUND && !lstrcmp(szProto, szMetaProto))
+ {
+ int nSubContacts = (int)CallService(MS_MC_GETNUMCONTACTS, (WPARAM)hContact, 0), i;
+ HANDLE hMetaContact;
+ for (i = 0; i < nSubContacts; i++)
+ {
+ hMetaContact = (HANDLE)CallService(MS_MC_GETSUBCONTACT, (WPARAM)hContact, i);
+ szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hMetaContact, 0);
+ if (szProto && !lstrcmp(szProto, GG_PROTO))
+ return hMetaContact;
+ }
+ }
+ return NULL;
+}
+
+static void gg_gc_resetclistopts(HWND hwndList)
+{
+ int i;
+ SendMessage(hwndList, CLM_SETLEFTMARGIN, 2, 0);
+ SendMessage(hwndList, CLM_SETBKBITMAP, 0, (LPARAM)(HBITMAP)NULL);
+ SendMessage(hwndList, CLM_SETBKCOLOR, GetSysColor(COLOR_WINDOW), 0);
+ SendMessage(hwndList, CLM_SETGREYOUTFLAGS, 0, 0);
+ SendMessage(hwndList, CLM_SETINDENT, 10, 0);
+ SendMessage(hwndList, CLM_SETHIDEEMPTYGROUPS, (WPARAM)TRUE, 0);
+ for (i = 0; i <= FONTID_MAX; i++)
+ SendMessage(hwndList, CLM_SETTEXTCOLOR, i, GetSysColor(COLOR_WINDOWTEXT));
+}
+
+static int gg_gc_countcheckmarks(HWND hwndList)
+{
+ int count = 0;
+ HANDLE hItem, hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ hItem = (HANDLE)SendMessage(hwndList, CLM_FINDCONTACT, (WPARAM)hContact, 0);
+ if (hItem && SendMessage(hwndList, CLM_GETCHECKMARK, (WPARAM)hItem, 0))
+ count++;
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+ return count;
+}
+
+#define HM_SUBCONTACTSCHANGED (WM_USER + 100)
+
+static INT_PTR CALLBACK gg_gc_openconfdlg(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch(message)
+ {
+ case WM_INITDIALOG:
+ {
+ CLCINFOITEM cii = {0};
+ HANDLE hMetaContactsEvent;
+
+ SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)lParam);
+ TranslateDialogDefault(hwndDlg);
+ WindowSetIcon(hwndDlg, "conference");
+ gg_gc_resetclistopts(GetDlgItem(hwndDlg, IDC_CLIST));
+
+ // Hook MetaContacts event (if available)
+ hMetaContactsEvent = HookEventMessage(ME_MC_SUBCONTACTSCHANGED, hwndDlg, HM_SUBCONTACTSCHANGED);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)hMetaContactsEvent);
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ {
+ switch (LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_CLIST);
+ GGPROTO* gg = (GGPROTO*)GetWindowLongPtr(hwndDlg, DWLP_USER);
+ int count = 0, i = 0;
+ // Check if connected
+ if (!gg_isonline(gg))
+ {
+ MessageBox(NULL,
+ Translate("You have to be connected to open new conference."),
+ GG_PROTONAME, MB_OK | MB_ICONSTOP
+ );
+ }
+ else if (hwndList && (count = gg_gc_countcheckmarks(hwndList)) >= 2)
+ {
+ // Create new participiants table
+ char* chat;
+ uin_t* participants = calloc(count, sizeof(uin_t));
+ HANDLE hItem, hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ gg_netlog(gg, "gg_gc_getchat(): Opening new conference for %d contacts.", count);
+ while (hContact && i < count)
+ {
+ hItem = (HANDLE)SendMessage(hwndList, CLM_FINDCONTACT, (WPARAM)hContact, 0);
+ if (hItem && SendMessage(hwndList, CLM_GETCHECKMARK, (WPARAM)hItem, 0))
+ {
+ HANDLE hMetaContact = gg_getsubcontact(gg, hContact); // MetaContacts support
+ participants[i++] = DBGetContactSettingDword(hMetaContact ? hMetaContact : hContact, GG_PROTO, GG_KEY_UIN, 0);
+ }
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+ if (count > i) i = count;
+ chat = gg_gc_getchat(gg, 0, participants, count);
+ if (chat)
+ {
+ GCDEST gcdest = {GG_PROTO, chat, GC_EVENT_CONTROL};
+ GCEVENT gcevent = {sizeof(GCEVENT), &gcdest};
+ CallServiceSync(MS_GC_EVENT, WINDOW_VISIBLE, (LPARAM)&gcevent);
+ }
+ free(participants);
+ }
+ }
+
+ case IDCANCEL:
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ break;
+ }
+
+ case WM_NOTIFY:
+ {
+ switch(((NMHDR*)lParam)->idFrom)
+ {
+ case IDC_CLIST:
+ {
+ switch(((NMHDR*)lParam)->code)
+ {
+ case CLN_OPTIONSCHANGED:
+ gg_gc_resetclistopts(GetDlgItem(hwndDlg, IDC_CLIST));
+ break;
+
+ case CLN_NEWCONTACT:
+ case CLN_CONTACTMOVED:
+ case CLN_LISTREBUILT:
+ {
+ HANDLE hContact;
+ HANDLE hItem;
+ char* szProto;
+ uin_t uin;
+ GGPROTO* gg = (GGPROTO*)GetWindowLongPtr(hwndDlg, DWLP_USER);
+
+ if (!gg) break;
+
+ // Delete non-gg contacts
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ hItem = (HANDLE)SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_FINDCONTACT, (WPARAM)hContact, 0);
+ if (hItem)
+ {
+ HANDLE hMetaContact = gg_getsubcontact(gg, hContact); // MetaContacts support
+ if (hMetaContact)
+ {
+ szProto = GG_PROTO;
+ uin = (uin_t)DBGetContactSettingDword(hMetaContact, GG_PROTO, GG_KEY_UIN, 0);
+ }
+ else
+ {
+ szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0);
+ uin = (uin_t)DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0);
+ }
+
+ if (szProto == NULL || lstrcmp(szProto, GG_PROTO) || !uin || uin == DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0))
+ SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_DELETEITEM, (WPARAM)hItem, 0);
+ }
+ hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0);
+ }
+ }
+ break;
+
+ case CLN_CHECKCHANGED:
+ EnableWindow(GetDlgItem(hwndDlg, IDOK), gg_gc_countcheckmarks(GetDlgItem(hwndDlg, IDC_CLIST)) >= 2);
+ break;
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ case HM_SUBCONTACTSCHANGED:
+ {
+ HWND hwndList = GetDlgItem(hwndDlg, IDC_CLIST);
+ SendMessage(hwndList, CLM_AUTOREBUILD, 0, 0);
+ EnableWindow(GetDlgItem(hwndDlg, IDOK), gg_gc_countcheckmarks(hwndList) >= 2);
+ break;
+ }
+
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ {
+ HANDLE hMetaContactsEvent = (HANDLE)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if (hMetaContactsEvent) UnhookEvent(hMetaContactsEvent);
+ WindowFreeIcon(hwndDlg);
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+INT_PTR gg_gc_clearignored(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ list_t l = gg->chats; BOOL cleared = FALSE;
+ while(l)
+ {
+ GGGC *chat = (GGGC *)l->data;
+ l = l->next;
+ if(chat->ignore)
+ {
+ if(chat->recipients) free(chat->recipients);
+ list_remove(&gg->chats, chat, 1);
+ cleared = TRUE;
+ }
+ }
+ MessageBox(
+ NULL,
+ cleared ?
+ Translate("All ignored conferences are now unignored and the conference policy will act again.") :
+ Translate("There are no ignored conferences."),
+ GG_PROTONAME,
+ MB_OK | MB_ICONINFORMATION
+ );
+
+ return 0;
+}
+
+INT_PTR gg_gc_openconf(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ // Check if connected
+ if (!gg_isonline(gg))
+ {
+ MessageBox(NULL,
+ Translate("You have to be connected to open new conference."),
+ GG_PROTONAME, MB_OK | MB_ICONSTOP
+ );
+ return 0;
+ }
+
+ CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_CONFERENCE), NULL, gg_gc_openconfdlg, (LPARAM)gg);
+ return 1;
+}
+
+int gg_gc_changenick(GGPROTO *gg, HANDLE hContact, char *pszNick)
+{
+ list_t l;
+ uin_t uin = DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0);
+ if(!uin || !pszNick) return 0;
+
+ gg_netlog(gg, "gg_gc_changenick(): Nickname for uin %d changed to %s.", uin, pszNick);
+ // Lookup for chats having this nick
+ for(l = gg->chats; l; l = l->next)
+ {
+ int i;
+ GGGC *chat = (GGGC *)l->data;
+ if(chat->recipients && chat->recipients_count)
+ for(i = 0; i < chat->recipients_count; i++)
+ // Rename this window if it's exising in the chat
+ if(chat->recipients[i] == uin)
+ {
+ char id[32];
+ GCEVENT gce = {sizeof(GCEVENT)};
+ GCDEST gcd;
+
+ UIN2ID(uin, id);
+ gcd.iType = GC_EVENT_NICK;
+ gcd.pszModule = GG_PROTO;
+ gce.pDest = &gcd;
+ gcd.pszID = chat->id;
+ gce.pszUID = id;
+ gce.pszText = pszNick;
+ gg_netlog(gg, "gg_gc_changenick(): Found room %s with uin %d, sending nick change %s.", chat->id, uin, id);
+ CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce);
+
+ break;
+ }
+ }
+
+ return 1;
+}
diff --git a/protocols/Gadu-Gadu/icolib.c b/protocols/Gadu-Gadu/icolib.c
new file mode 100644
index 0000000000..55ceb60cf0
--- /dev/null
+++ b/protocols/Gadu-Gadu/icolib.c
@@ -0,0 +1,109 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2007 Adam Strzelecki <ono+miranda@java.pl>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+
+struct tagiconList
+{
+ const char* szDescr;
+ const char* szName;
+ int defIconID;
+}
+static const iconList[] =
+{
+ { LPGEN("Protocol icon"), "main", IDI_GG },
+ { LPGEN("Import list from server"), "importserver", IDI_IMPORT_SERVER },
+ { LPGEN("Import list from text file"), "importtext", IDI_IMPORT_TEXT },
+ { LPGEN("Remove list from server"), "removeserver", IDI_REMOVE_SERVER },
+ { LPGEN("Export list to server"), "exportserver", IDI_EXPORT_SERVER },
+ { LPGEN("Export list to text file"), "exporttext", IDI_EXPORT_TEXT },
+ { LPGEN("Account settings"), "settings", IDI_SETTINGS },
+ { LPGEN("Contact list"), "list", IDI_LIST },
+ { LPGEN("Block user"), "block", IDI_BLOCK },
+ { LPGEN("Previous image"), "previous", IDI_PREV },
+ { LPGEN("Next image"), "next", IDI_NEXT },
+ { LPGEN("Send image"), "image", IDI_IMAGE },
+ { LPGEN("Save image"), "save", IDI_SAVE },
+ { LPGEN("Delete image"), "delete", IDI_DELETE },
+ { LPGEN("Open new conference"), "conference", IDI_CONFERENCE },
+ { LPGEN("Clear ignored conferences"), "clearignored", IDI_CLEAR_CONFERENCE },
+ { LPGEN("Concurrent sessions"), "sessions", IDI_SESSIONS }
+};
+
+HANDLE hIconLibItem[SIZEOF(iconList)];
+
+void gg_icolib_init()
+{
+ SKINICONDESC sid = {0};
+ char szFile[MAX_PATH];
+ char szSectionName[100];
+ int i;
+
+ mir_snprintf(szSectionName, sizeof( szSectionName ), "%s/%s", LPGEN("Protocols"), LPGEN(GGDEF_PROTO));
+ GetModuleFileNameA(hInstance, szFile, MAX_PATH);
+
+ sid.cbSize = sizeof(SKINICONDESC);
+ sid.pszDefaultFile = szFile;
+ sid.pszSection = szSectionName;
+
+ for(i = 0; i < SIZEOF(iconList); i++) {
+ char szSettingName[100];
+ mir_snprintf(szSettingName, sizeof(szSettingName), "%s_%s", GGDEF_PROTO, iconList[i].szName);
+ sid.pszName = szSettingName;
+ sid.pszDescription = (char*)iconList[i].szDescr;
+ sid.iDefaultIndex = -iconList[i].defIconID;
+ hIconLibItem[i] = (HANDLE) CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid);
+ }
+}
+
+HICON LoadIconEx(const char* name, BOOL big)
+{
+ char szSettingName[100];
+ mir_snprintf(szSettingName, sizeof(szSettingName), "%s_%s", GGDEF_PROTO, name);
+ return (HICON)CallService(MS_SKIN2_GETICON, big, (LPARAM)szSettingName);
+}
+
+HANDLE GetIconHandle(int iconId)
+{
+ int i;
+ for(i = 0; i < SIZEOF(iconList); i++)
+ if (iconList[i].defIconID == iconId)
+ return hIconLibItem[i];
+ return NULL;
+}
+
+void ReleaseIconEx(const char* name, BOOL big)
+{
+ char szSettingName[100];
+ mir_snprintf(szSettingName, sizeof(szSettingName), "%s_%s", GGDEF_PROTO, name);
+ CallService(big ? MS_SKIN2_RELEASEICONBIG : MS_SKIN2_RELEASEICON, 0, (LPARAM)szSettingName);
+}
+
+void WindowSetIcon(HWND hWnd, const char* name)
+{
+ SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)LoadIconEx(name, TRUE));
+ SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)LoadIconEx(name, FALSE));
+}
+
+void WindowFreeIcon(HWND hWnd)
+{
+ CallService(MS_SKIN2_RELEASEICONBIG, SendMessage(hWnd, WM_SETICON, ICON_BIG, 0), 0);
+ CallService(MS_SKIN2_RELEASEICON, SendMessage(hWnd, WM_SETICON, ICON_SMALL, 0), 0);
+}
diff --git a/protocols/Gadu-Gadu/icons/block.ico b/protocols/Gadu-Gadu/icons/block.ico
new file mode 100644
index 0000000000..6f59309853
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/block.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/icons/clear_ignored_conference.ico b/protocols/Gadu-Gadu/icons/clear_ignored_conference.ico
new file mode 100644
index 0000000000..f883585899
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/clear_ignored_conference.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/icons/conference.ico b/protocols/Gadu-Gadu/icons/conference.ico
new file mode 100644
index 0000000000..3e4cfcc606
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/conference.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/icons/delete.ico b/protocols/Gadu-Gadu/icons/delete.ico
new file mode 100644
index 0000000000..9108e38cfd
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/delete.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/icons/export_list_to_server.ico b/protocols/Gadu-Gadu/icons/export_list_to_server.ico
new file mode 100644
index 0000000000..8c0f660788
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/export_list_to_server.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/icons/export_list_to_txt_file.ico b/protocols/Gadu-Gadu/icons/export_list_to_txt_file.ico
new file mode 100644
index 0000000000..fc239f9a02
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/export_list_to_txt_file.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/icons/gg.ico b/protocols/Gadu-Gadu/icons/gg.ico
new file mode 100644
index 0000000000..a49986a12d
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/gg.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/icons/image.ico b/protocols/Gadu-Gadu/icons/image.ico
new file mode 100644
index 0000000000..785cd8ba9e
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/image.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/icons/import_list_from_server.ico b/protocols/Gadu-Gadu/icons/import_list_from_server.ico
new file mode 100644
index 0000000000..21ecf6159d
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/import_list_from_server.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/icons/import_list_from_txt_file.ico b/protocols/Gadu-Gadu/icons/import_list_from_txt_file.ico
new file mode 100644
index 0000000000..2acc738fc7
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/import_list_from_txt_file.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/icons/list.ico b/protocols/Gadu-Gadu/icons/list.ico
new file mode 100644
index 0000000000..86139d5e23
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/list.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/icons/next.ico b/protocols/Gadu-Gadu/icons/next.ico
new file mode 100644
index 0000000000..8ddc389b85
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/next.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/icons/previous.ico b/protocols/Gadu-Gadu/icons/previous.ico
new file mode 100644
index 0000000000..8e6dc1ef08
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/previous.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/icons/remove_list_from_server.ico b/protocols/Gadu-Gadu/icons/remove_list_from_server.ico
new file mode 100644
index 0000000000..44fe7a833a
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/remove_list_from_server.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/icons/save.ico b/protocols/Gadu-Gadu/icons/save.ico
new file mode 100644
index 0000000000..a8251f70e4
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/save.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/icons/sessions.ico b/protocols/Gadu-Gadu/icons/sessions.ico
new file mode 100644
index 0000000000..9f9383e22f
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/sessions.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/icons/settings.ico b/protocols/Gadu-Gadu/icons/settings.ico
new file mode 100644
index 0000000000..6706ec2f6c
--- /dev/null
+++ b/protocols/Gadu-Gadu/icons/settings.ico
Binary files differ
diff --git a/protocols/Gadu-Gadu/image.c b/protocols/Gadu-Gadu/image.c
new file mode 100644
index 0000000000..fa32fedee9
--- /dev/null
+++ b/protocols/Gadu-Gadu/image.c
@@ -0,0 +1,1186 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2009 Adam Strzelecki <ono+miranda@java.pl>
+// Copyright (c) 2009-2012 Bartosz Białek
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+#include <io.h>
+
+#define WM_ADDIMAGE WM_USER + 1
+#define WM_SENDIMG WM_USER + 2
+#define WM_CHOOSEIMG WM_USER + 3
+#define TIMERID_FLASHWND WM_USER + 4
+
+////////////////////////////////////////////////////////////////////////////
+// Image Window : Data
+
+// tablica zawiera uin kolesia i uchwyt do okna, okno zawiera GGIMAGEDLGDATA
+// ktore przechowuje handle kontaktu, i wskaznik na pierwszy obrazek
+// obrazki sa poukladane jako lista jednokierunkowa.
+// przy tworzeniu okna podaje sie handle do kontaktu
+// dodajac obrazek tworzy sie element listy i wysyla do okna
+// wyswietlajac obrazek idzie po liscie jednokierunkowej
+
+typedef struct _GGIMAGEENTRY
+{
+ HBITMAP hBitmap;
+ char *lpszFileName;
+ char *lpData;
+ unsigned long nSize;
+ struct _GGIMAGEENTRY *lpNext;
+ uint32_t crc32;
+} GGIMAGEENTRY;
+
+typedef struct
+{
+ HANDLE hContact;
+ HANDLE hEvent;
+ HWND hWnd;
+ uin_t uin;
+ int nImg, nImgTotal;
+ GGIMAGEENTRY *lpImages;
+ SIZE minSize;
+ BOOL bReceiving;
+ GGPROTO *gg;
+} GGIMAGEDLGDATA;
+
+// Prototypes
+int gg_img_remove(GGIMAGEDLGDATA *dat);
+INT_PTR gg_img_sendimg(GGPROTO *gg, WPARAM wParam, LPARAM lParam);
+
+////////////////////////////////////////////////////////////////////////////
+// Image Module : Adding item to contact menu, creating sync objects
+int gg_img_init(GGPROTO *gg)
+{
+ CLISTMENUITEM mi = {0};
+ char service[64];
+
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_ICONFROMICOLIB;
+
+ // Send image contact menu item
+ mir_snprintf(service, sizeof(service), GGS_SENDIMAGE, GG_PROTO);
+ CreateProtoServiceFunction(service, gg_img_sendimg, gg);
+ mi.position = -2000010000;
+ mi.icolibItem = GetIconHandle(IDI_IMAGE);
+ mi.pszName = LPGEN("&Image");
+ mi.pszService = service;
+ mi.pszContactOwner = GG_PROTO;
+ gg->hImageMenuItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM) &mi);
+
+ // Receive image
+ mir_snprintf(service, sizeof(service), GGS_RECVIMAGE, GG_PROTO);
+ CreateProtoServiceFunction(service, gg_img_recvimage, gg);
+
+ return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Image Module : closing dialogs, sync objects
+int gg_img_shutdown(GGPROTO *gg)
+{
+ list_t l;
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_img_shutdown(): Closing all dialogs...");
+#endif
+ // Rather destroy window instead of just removing structures
+ for (l = gg->imagedlgs; l;)
+ {
+ GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)l->data;
+ l = l->next;
+
+ if (dat && dat->hWnd)
+ {
+ if (IsWindow(dat->hWnd))
+ {
+ // Post message async, since it maybe be different thread
+ if (!PostMessage(dat->hWnd, WM_CLOSE, 0, 0))
+ gg_netlog(gg, "gg_img_shutdown(): Image dlg %x cannot be released !!", dat->hWnd);
+ }
+ else
+ gg_netlog(gg, "gg_img_shutdown(): Image dlg %x not exists, but structure does !!", dat->hWnd);
+ }
+ }
+
+ return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Image Module : destroying list
+int gg_img_destroy(GGPROTO *gg)
+{
+ // Release all dialogs
+ while (gg->imagedlgs && gg_img_remove((GGIMAGEDLGDATA *)gg->imagedlgs->data));
+
+ // Destroy list
+ list_destroy(gg->imagedlgs, 1);
+ CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)gg->hImageMenuItem, (LPARAM) 0);
+
+ return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Painting image
+int gg_img_paint(HWND hwnd, GGIMAGEENTRY *dat)
+{
+ PAINTSTRUCT paintStruct;
+ HDC hdc = BeginPaint(hwnd, &paintStruct);
+ RECT rc;
+
+ GetWindowRect(GetDlgItem(hwnd, IDC_IMG_IMAGE), &rc);
+ ScreenToClient(hwnd, (POINT *)&rc.left);
+ ScreenToClient(hwnd, (POINT *)&rc.right);
+ FillRect(hdc, &rc, (HBRUSH)GetSysColorBrush(COLOR_WINDOW));
+
+ if (dat->hBitmap)
+ {
+ HDC hdcBmp = NULL;
+ int nWidth, nHeight;
+ BITMAP bmp;
+
+ GetObject(dat->hBitmap, sizeof(bmp), &bmp);
+ nWidth = bmp.bmWidth; nHeight = bmp.bmHeight;
+
+ hdcBmp = CreateCompatibleDC(hdc);
+ SelectObject(hdcBmp, dat->hBitmap);
+ if (hdcBmp)
+ {
+ SetStretchBltMode(hdc, HALFTONE);
+ // Draw bitmap
+ if (nWidth > (rc.right-rc.left) || nHeight > (rc.bottom-rc.top))
+ {
+ if ((double)nWidth / (double)nHeight > (double) (rc.right-rc.left) / (double)(rc.bottom-rc.top))
+ {
+ StretchBlt(hdc,
+ rc.left,
+ ((rc.top + rc.bottom) - (rc.right - rc.left) * nHeight / nWidth) / 2,
+ (rc.right - rc.left),
+ (rc.right - rc.left) * nHeight / nWidth,
+ hdcBmp, 0, 0, nWidth, nHeight, SRCCOPY);
+ }
+ else
+ {
+ StretchBlt(hdc,
+ ((rc.left + rc.right) - (rc.bottom - rc.top) * nWidth / nHeight) / 2,
+ rc.top,
+ (rc.bottom - rc.top) * nWidth / nHeight,
+ (rc.bottom - rc.top),
+ hdcBmp, 0, 0, nWidth, nHeight, SRCCOPY);
+ }
+ }
+ else
+ {
+ BitBlt(hdc,
+ (rc.left + rc.right - nWidth) / 2,
+ (rc.top + rc.bottom - nHeight) / 2,
+ nWidth, nHeight,
+ hdcBmp, 0, 0, SRCCOPY);
+ }
+ DeleteDC(hdcBmp);
+ }
+ }
+ EndPaint(hwnd, &paintStruct);
+
+ return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Returns supported image filters
+char *gg_img_getfilter(char *szFilter, int nSize)
+{
+ char *szFilterName, *szFilterMask;
+ char *pFilter = szFilter;
+
+ // Match relative to ImgDecoder presence
+ szFilterName = Translate("Image files (*.bmp,*.gif,*.jpeg,*.jpg,*.png)");
+ szFilterMask = "*.bmp;*.gif;*.jpeg;*.jpg;*.png";
+
+ // Make up filter
+ strncpy(pFilter, szFilterName, nSize);
+ pFilter += strlen(pFilter) + 1;
+ if (pFilter >= szFilter + nSize) return NULL;
+ strncpy(pFilter, szFilterMask, nSize - (pFilter - szFilter));
+ pFilter += strlen(pFilter) + 1;
+ if (pFilter >= szFilter + nSize) return NULL;
+ *pFilter = 0;
+
+ return szFilter;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Save specified image entry
+int gg_img_saveimage(HWND hwnd, GGIMAGEENTRY *dat)
+{
+ OPENFILENAME ofn = {0};
+ char szFileName[MAX_PATH];
+ char szFilter[128];
+
+ if (!dat) return FALSE;
+
+ gg_img_getfilter(szFilter, sizeof(szFilter));
+ strncpy(szFileName, dat->lpszFileName, sizeof(szFileName));
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hwnd;
+ ofn.hInstance = hInstance;
+ ofn.lpstrFile = szFileName;
+ ofn.lpstrFilter = szFilter;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.Flags = OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR | OFN_OVERWRITEPROMPT;
+ if (GetSaveFileName(&ofn))
+ {
+ FILE *fp = fopen(szFileName, "w+b");
+ if (fp)
+ {
+ fwrite(dat->lpData, dat->nSize, 1, fp);
+ fclose(fp);
+ gg_netlog(((GGIMAGEDLGDATA *)GetWindowLongPtr(hwnd, GWLP_USERDATA))->gg, "gg_img_saveimage(): Image saved to %s.", szFileName);
+ }
+ else
+ {
+ gg_netlog(((GGIMAGEDLGDATA *)GetWindowLongPtr(hwnd, GWLP_USERDATA))->gg, "gg_img_saveimage(): Cannot save image to %s.", szFileName);
+ MessageBox(hwnd, Translate("Image cannot be written to disk."), ((GGIMAGEDLGDATA *)GetWindowLongPtr(hwnd, GWLP_USERDATA))->gg->name, MB_OK | MB_ICONERROR);
+ }
+ }
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Fit window size to image size
+BOOL gg_img_fit(HWND hwndDlg)
+{
+ GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ RECT dlgRect, imgRect, wrkRect;
+ int nWidth, nHeight;
+ int rWidth = 0, rHeight = 0;
+ int oWidth = 0, oHeight = 0;
+ BITMAP bmp;
+ GGIMAGEENTRY *img = NULL;
+ HDC hdc;
+
+ // Check if image is loaded
+ if (!dat || !dat->lpImages || !dat->lpImages->hBitmap)
+ return FALSE;
+
+ img = dat->lpImages;
+
+ // Go to last image
+ while (img->lpNext && dat->lpImages->hBitmap)
+ img = img->lpNext;
+
+ // Get rects of display
+ GetWindowRect(hwndDlg, &dlgRect);
+ GetClientRect(GetDlgItem(hwndDlg, IDC_IMG_IMAGE), &imgRect);
+
+ hdc = GetDC(hwndDlg);
+
+ GetObject(img->hBitmap, sizeof(bmp), &bmp);
+ nWidth = bmp.bmWidth; nHeight = bmp.bmHeight;
+ SystemParametersInfo(SPI_GETWORKAREA, 0, &wrkRect, 0);
+
+ ReleaseDC(hwndDlg, hdc);
+
+ if ((imgRect.right - imgRect.left) < nWidth)
+ rWidth = nWidth - imgRect.right + imgRect.left;
+ if ((imgRect.bottom - imgRect.top) < nWidth)
+ rHeight = nHeight - imgRect.bottom + imgRect.top;
+
+ // Check if anything needs resize
+ if (!rWidth && !rHeight)
+ return FALSE;
+
+ oWidth = dlgRect.right - dlgRect.left + rWidth;
+ oHeight = dlgRect.bottom - dlgRect.top + rHeight;
+
+ if (oHeight > wrkRect.bottom - wrkRect.top)
+ {
+ oWidth = (int)((double)(wrkRect.bottom - wrkRect.top + imgRect.bottom - imgRect.top - dlgRect.bottom + dlgRect.top) * nWidth / nHeight)
+ - imgRect.right + imgRect.left + dlgRect.right - dlgRect.left;
+ if (oWidth < dlgRect.right - dlgRect.left)
+ oWidth = dlgRect.right - dlgRect.left;
+ oHeight = wrkRect.bottom - wrkRect.top;
+ }
+ if (oWidth > wrkRect.right - wrkRect.left)
+ {
+ oHeight = (int)((double)(wrkRect.right - wrkRect.left + imgRect.right - imgRect.left - dlgRect.right + dlgRect.left) * nHeight / nWidth)
+ - imgRect.bottom + imgRect.top + dlgRect.bottom - dlgRect.top;
+ if (oHeight < dlgRect.bottom - dlgRect.top)
+ oHeight = dlgRect.bottom - dlgRect.top;
+ oWidth = wrkRect.right - wrkRect.left;
+ }
+ SetWindowPos(hwndDlg, NULL,
+ (wrkRect.left + wrkRect.right - oWidth) / 2,
+ (wrkRect.top + wrkRect.bottom - oHeight) / 2,
+ oWidth, oHeight,
+ SWP_SHOWWINDOW | SWP_NOZORDER /* | SWP_NOACTIVATE */);
+
+ return TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Dialog resizer procedure
+static int sttImageDlgResizer(HWND hwndDlg, LPARAM lParam, UTILRESIZECONTROL* urc)
+{
+ switch (urc->wId)
+ {
+ case IDC_IMG_PREV:
+ case IDC_IMG_NEXT:
+ case IDC_IMG_DELETE:
+ case IDC_IMG_SAVE:
+ return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP;
+ case IDC_IMG_IMAGE:
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORY_HEIGHT | RD_ANCHORX_WIDTH;
+ case IDC_IMG_SEND:
+ case IDC_IMG_CANCEL:
+ return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Send / Recv main dialog procedure
+static INT_PTR CALLBACK gg_img_dlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ {
+ char *szName, szTitle[128];
+ RECT rect;
+
+ TranslateDialogDefault(hwndDlg);
+ // This should be already initialized
+ // InitCommonControls();
+
+ // Get dialog data
+ dat = (GGIMAGEDLGDATA *)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat);
+
+ // Save dialog handle
+ dat->hWnd = hwndDlg;
+
+ // Send event if someone's waiting
+ if (dat->hEvent) SetEvent(dat->hEvent);
+ else gg_netlog(dat->gg, "gg_img_dlgproc(): Creation event not found, but someone might be waiting.");
+
+ // Making buttons flat
+ SendDlgItemMessage(hwndDlg, IDC_IMG_PREV, BUTTONSETASFLATBTN, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDC_IMG_NEXT, BUTTONSETASFLATBTN, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDC_IMG_DELETE, BUTTONSETASFLATBTN, 0, 0);
+ SendDlgItemMessage(hwndDlg, IDC_IMG_SAVE, BUTTONSETASFLATBTN, 0, 0);
+
+ // Setting images for buttons
+ SendDlgItemMessage(hwndDlg, IDC_IMG_PREV, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIconEx("previous", FALSE));
+ SendDlgItemMessage(hwndDlg, IDC_IMG_NEXT, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIconEx("next", FALSE));
+ SendDlgItemMessage(hwndDlg, IDC_IMG_DELETE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIconEx("delete", FALSE));
+ SendDlgItemMessage(hwndDlg, IDC_IMG_SAVE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)LoadIconEx("save", FALSE));
+
+ // Setting tooltips for buttons
+ SendDlgItemMessage(hwndDlg, IDC_IMG_PREV, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Previous image"), BATF_TCHAR);
+ SendDlgItemMessage(hwndDlg, IDC_IMG_NEXT, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Next image"), BATF_TCHAR);
+ SendDlgItemMessage(hwndDlg, IDC_IMG_DELETE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Delete image from the list"), BATF_TCHAR);
+ SendDlgItemMessage(hwndDlg, IDC_IMG_SAVE, BUTTONADDTOOLTIP, (WPARAM)TranslateT("Save image to disk"), BATF_TCHAR);
+
+ // Set main window image
+ WindowSetIcon(hwndDlg, "image");
+
+ szName = (char *) CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)dat->hContact, 0);
+ if (dat->bReceiving)
+ mir_snprintf(szTitle, sizeof(szTitle), Translate("Image from %s"), szName);
+ else
+ mir_snprintf(szTitle, sizeof(szTitle), Translate("Image for %s"), szName);
+ SetWindowText(hwndDlg, szTitle);
+
+ // Store client extents
+ GetClientRect(hwndDlg, &rect);
+ dat->minSize.cx = rect.right - rect.left;
+ dat->minSize.cy = rect.bottom - rect.top;
+ }
+ return TRUE;
+
+ case WM_SIZE:
+ {
+ UTILRESIZEDIALOG urd = {0};
+ urd.cbSize = sizeof(urd);
+ urd.hInstance = hInstance;
+ urd.hwndDlg = hwndDlg;
+ urd.lpTemplate = dat->bReceiving ? MAKEINTRESOURCEA(IDD_IMAGE_RECV) : MAKEINTRESOURCEA(IDD_IMAGE_SEND);
+ urd.pfnResizer = sttImageDlgResizer;
+ CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd);
+ if (wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED)
+ InvalidateRect(hwndDlg, NULL, FALSE);
+ return 0;
+ }
+
+ case WM_SIZING:
+ {
+ RECT *pRect = (RECT *)lParam;
+ if (pRect->right - pRect->left < dat->minSize.cx)
+ {
+ if (wParam == WMSZ_BOTTOMLEFT || wParam == WMSZ_LEFT || wParam == WMSZ_TOPLEFT)
+ pRect->left = pRect->right - dat->minSize.cx;
+ else
+ pRect->right = pRect->left + dat->minSize.cx;
+ }
+ if (pRect->bottom - pRect->top < dat->minSize.cy)
+ {
+ if (wParam == WMSZ_TOPLEFT || wParam == WMSZ_TOP || wParam == WMSZ_TOPRIGHT)
+ pRect->top = pRect->bottom - dat->minSize.cy;
+ else
+ pRect->bottom = pRect->top + dat->minSize.cy;
+ }
+ }
+ return TRUE;
+
+ case WM_CLOSE:
+ EndDialog(hwndDlg, 0);
+ break;
+
+ // Flash the window
+ case WM_TIMER:
+ if (wParam == TIMERID_FLASHWND)
+ FlashWindow(hwndDlg, TRUE);
+ break;
+
+ // Kill the timer
+ case WM_ACTIVATE:
+ if (LOWORD(wParam) != WA_ACTIVE)
+ break;
+ case WM_MOUSEACTIVATE:
+ if (KillTimer(hwndDlg, TIMERID_FLASHWND))
+ FlashWindow(hwndDlg, FALSE);
+ break;
+
+ case WM_PAINT:
+ if (dat->lpImages)
+ {
+ GGIMAGEENTRY *img = dat->lpImages;
+ int i;
+
+ for (i = 1; img && (i < dat->nImg); i++)
+ img = img->lpNext;
+
+ if (!img)
+ {
+ gg_netlog(dat->gg, "gg_img_dlgproc(): Image was not found on the list. Cannot paint the window.");
+ return FALSE;
+ }
+
+ if (dat->bReceiving)
+ {
+ char szTitle[128];
+ mir_snprintf(szTitle, sizeof(szTitle),
+ "%s (%d / %d)", img->lpszFileName, dat->nImg, dat->nImgTotal);
+ SetDlgItemText(hwndDlg, IDC_IMG_NAME, szTitle);
+ }
+ else
+ SetDlgItemText(hwndDlg, IDC_IMG_NAME, img->lpszFileName);
+ gg_img_paint(hwndDlg, img);
+ }
+ break;
+
+ case WM_DESTROY:
+ if (dat)
+ {
+ // Deleting all image entries
+ GGIMAGEENTRY *temp, *img = dat->lpImages;
+ GGPROTO *gg = dat->gg;
+ while (temp = img)
+ {
+ img = img->lpNext;
+ gg_img_releasepicture(temp);
+ }
+ ReleaseIconEx("previous", FALSE);
+ ReleaseIconEx("next", FALSE);
+ ReleaseIconEx("delete", FALSE);
+ ReleaseIconEx("save", FALSE);
+ WindowFreeIcon(hwndDlg);
+ EnterCriticalSection(&gg->img_mutex);
+ list_remove(&gg->imagedlgs, dat, 1);
+ LeaveCriticalSection(&gg->img_mutex);
+ }
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_IMG_CANCEL:
+ EndDialog(hwndDlg, 0);
+ return TRUE;
+
+ case IDC_IMG_PREV:
+ if (dat->nImg > 1)
+ {
+ dat->nImg--;
+ InvalidateRect(hwndDlg, NULL, FALSE);
+ }
+ return TRUE;
+
+ case IDC_IMG_NEXT:
+ if (dat->nImg < dat->nImgTotal)
+ {
+ dat->nImg++;
+ InvalidateRect(hwndDlg, NULL, FALSE);
+ }
+ return TRUE;
+
+ case IDC_IMG_DELETE:
+ {
+ GGIMAGEENTRY *del, *img = dat->lpImages;
+ if (dat->nImg == 1)
+ {
+ del = dat->lpImages;
+ dat->lpImages = img->lpNext;
+ }
+ else
+ {
+ int i;
+ for (i = 1; img && (i < dat->nImg - 1); i++)
+ img = img->lpNext;
+ if (!img)
+ {
+ gg_netlog(dat->gg, "gg_img_dlgproc(): Image was not found on the list. Cannot delete it from the list.");
+ return FALSE;
+ }
+ del = img->lpNext;
+ img->lpNext = del->lpNext;
+ dat->nImg --;
+ }
+
+ if ((-- dat->nImgTotal) == 0)
+ EndDialog(hwndDlg, 0);
+ else
+ InvalidateRect(hwndDlg, NULL, FALSE);
+
+ gg_img_releasepicture(del);
+ }
+ return TRUE;
+
+ case IDC_IMG_SAVE:
+ {
+ GGIMAGEENTRY *img = dat->lpImages;
+ int i;
+
+ for (i = 1; img && (i < dat->nImg); i++)
+ img = img->lpNext;
+ if (!img)
+ {
+ gg_netlog(dat->gg, "gg_img_dlgproc(): Image was not found on the list. Cannot launch saving.");
+ return FALSE;
+ }
+ gg_img_saveimage(hwndDlg, img);
+ }
+ return TRUE;
+
+ case IDC_IMG_SEND:
+ {
+ unsigned char format[20];
+ char *msg = "\xA0\0";
+ GGPROTO *gg = dat->gg;
+
+ if (dat->lpImages && gg_isonline(gg))
+ {
+ uin_t uin = (uin_t)DBGetContactSettingDword(dat->hContact, gg->proto.m_szModuleName, GG_KEY_UIN, 0);
+ struct gg_msg_richtext_format *r = NULL;
+ struct gg_msg_richtext_image *p = NULL;
+ LPVOID pvData = NULL;
+ int len;
+
+ ((struct gg_msg_richtext*)format)->flag = 2;
+
+ r = (struct gg_msg_richtext_format *)(format + sizeof(struct gg_msg_richtext));
+ r->position = 0;
+ r->font = GG_FONT_IMAGE;
+
+ p = (struct gg_msg_richtext_image *)(format + sizeof(struct gg_msg_richtext) + sizeof(struct gg_msg_richtext_format));
+ p->unknown1 = 0x109;
+ p->size = dat->lpImages->nSize;
+
+ dat->lpImages->crc32 = p->crc32 = gg_fix32(gg_crc32(0, dat->lpImages->lpData, dat->lpImages->nSize));
+
+ len = sizeof(struct gg_msg_richtext_format) + sizeof(struct gg_msg_richtext_image);
+ ((struct gg_msg_richtext*)format)->length = len;
+
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_send_message_richtext(gg->sess, GG_CLASS_CHAT, (uin_t)uin, (unsigned char*)msg, format, len + sizeof(struct gg_msg_richtext));
+ LeaveCriticalSection(&gg->sess_mutex);
+
+ // Protect dat from releasing
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)0);
+
+ EndDialog(hwndDlg, 0);
+ }
+ return TRUE;
+ }
+ break;
+ }
+ break;
+
+ case WM_ADDIMAGE: // lParam == GGIMAGEENTRY *dat
+ {
+ GGIMAGEENTRY *lpImage = (GGIMAGEENTRY *)lParam;
+ GGIMAGEENTRY *lpImages = dat->lpImages;
+
+ if (!dat->lpImages) // first image entry
+ dat->lpImages = lpImage;
+ else // adding at the end of the list
+ {
+ while (lpImages->lpNext)
+ lpImages = lpImages->lpNext;
+ lpImages->lpNext = lpImage;
+ }
+ dat->nImg = ++ dat->nImgTotal;
+ }
+ // Fit window to image
+ if (!gg_img_fit(hwndDlg))
+ InvalidateRect(hwndDlg, NULL, FALSE);
+ return TRUE;
+
+ case WM_CHOOSEIMG:
+ {
+ char szFilter[128];
+ char szFileName[MAX_PATH];
+ OPENFILENAME ofn = {0};
+
+ gg_img_getfilter(szFilter, sizeof(szFilter));
+ *szFileName = 0;
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ ofn.hwndOwner = hwndDlg;
+ ofn.hInstance = hInstance;
+ ofn.lpstrFilter = szFilter;
+ ofn.lpstrFile = szFileName;
+ ofn.nMaxFile = MAX_PATH;
+ ofn.lpstrTitle = Translate("Select picture to send");
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_NOCHANGEDIR | OFN_HIDEREADONLY;
+ if (GetOpenFileName(&ofn))
+ {
+ if (dat->lpImages)
+ gg_img_releasepicture(dat->lpImages);
+ if (!(dat->lpImages = (GGIMAGEENTRY *)gg_img_loadpicture(dat->gg, 0, szFileName)))
+ {
+ EndDialog(hwndDlg, 0);
+ return FALSE;
+ }
+ if (!gg_img_fit(hwndDlg))
+ InvalidateRect(hwndDlg, NULL, FALSE);
+ }
+ else
+ {
+ EndDialog(hwndDlg, 0);
+ return FALSE;
+ }
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Image dialog call thread
+void __cdecl gg_img_dlgcallthread(GGPROTO *gg, void *param)
+{
+ HWND hMIWnd = 0; //(HWND) CallService(MS_CLUI_GETHWND, 0, 0);
+
+ GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)param;
+ DialogBoxParam(hInstance, dat->bReceiving ? MAKEINTRESOURCE(IDD_IMAGE_RECV) : MAKEINTRESOURCE(IDD_IMAGE_SEND),
+ hMIWnd, gg_img_dlgproc, (LPARAM) dat);
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Open dialog receive for specified contact
+GGIMAGEDLGDATA *gg_img_recvdlg(GGPROTO *gg, HANDLE hContact)
+{
+ // Create dialog data
+ GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)calloc(1, sizeof(GGIMAGEDLGDATA));
+ dat->hContact = hContact;
+ dat->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ dat->bReceiving = TRUE;
+ dat->gg = gg;
+ ResetEvent(dat->hEvent);
+ gg_forkthread(gg, gg_img_dlgcallthread, dat);
+ return dat;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Checks if an image is already saved to the specified path
+// Returns 1 if yes, 0 if no or -1 if different image on this path is found
+int gg_img_isexists(char *szPath, GGIMAGEENTRY *dat)
+{
+ struct _stat st;
+
+ if (_stat(szPath, &st) != 0)
+ return 0;
+
+ if (st.st_size == dat->nSize)
+ {
+ char *lpData;
+ FILE *fp = fopen(szPath, "rb");
+ if (!fp) return 0;
+ lpData = mir_alloc(dat->nSize);
+ if (fread(lpData, 1, dat->nSize, fp) == dat->nSize)
+ {
+ if (dat->crc32 == gg_fix32(gg_crc32(0, lpData, dat->nSize)) ||
+ memcmp(lpData, dat->lpData, dat->nSize) == 0)
+ {
+ mir_free(lpData);
+ fclose(fp);
+ return 1;
+ }
+ }
+ mir_free(lpData);
+ fclose(fp);
+ }
+
+ return -1;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Determine if image's file name has the proper extension
+char *gg_img_hasextension(const char *filename)
+{
+ if (filename != NULL && *filename != '\0')
+ {
+ char *imgtype = strrchr(filename, '.');
+ if (imgtype != NULL)
+ {
+ size_t len = strlen(imgtype);
+ imgtype++;
+ if (len == 4 && (_stricmp(imgtype, "bmp") == 0 ||
+ _stricmp(imgtype, "gif") == 0 ||
+ _stricmp(imgtype, "jpg") == 0 ||
+ _stricmp(imgtype, "png") == 0))
+ return --imgtype;
+ if (len == 5 && _stricmp(imgtype, "jpeg") == 0)
+ return --imgtype;
+ }
+ }
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Display received image using message with [img] BBCode
+int gg_img_displayasmsg(GGPROTO *gg, HANDLE hContact, void *img)
+{
+ GGIMAGEENTRY *dat = (GGIMAGEENTRY *)img;
+ char szPath[MAX_PATH], *path = (char*)alloca(MAX_PATH), *pImgext, imgext[6];
+ size_t tPathLen;
+ int i, res;
+
+ if (gg->hImagesFolder == NULL || FoldersGetCustomPath(gg->hImagesFolder, path, MAX_PATH, ""))
+ {
+ char *tmpPath = Utils_ReplaceVars("%miranda_userdata%");
+ tPathLen = mir_snprintf(szPath, MAX_PATH, "%s\\%s\\ImageCache", tmpPath, GG_PROTO);
+ mir_free(tmpPath);
+ }
+ else
+ {
+ strcpy(szPath, path);
+ tPathLen = strlen(szPath);
+ }
+
+ if (_access(szPath, 0))
+ CallService(MS_UTILS_CREATEDIRTREE, 0, (LPARAM)szPath);
+
+ mir_snprintf(szPath + tPathLen, MAX_PATH - tPathLen, "\\%s", dat->lpszFileName);
+ if ((pImgext = gg_img_hasextension(szPath)) == NULL)
+ pImgext = szPath + strlen(szPath);
+ mir_snprintf(imgext, SIZEOF(imgext), "%s", pImgext);
+ for (i = 1; ; ++i)
+ {
+ if ((res = gg_img_isexists(szPath, dat)) != -1) break;
+ mir_snprintf(szPath, MAX_PATH, "%.*s (%u)%s", pImgext - szPath, szPath, i, imgext);
+ }
+
+ if (res == 0)
+ {
+ // Image file not found, thus create it
+ FILE *fp = fopen(szPath, "w+b");
+ if (fp)
+ {
+ res = fwrite(dat->lpData, dat->nSize, 1, fp) > 0;
+ fclose(fp);
+ }
+ }
+
+ if (res != 0)
+ {
+ char image_msg[MAX_PATH + 11];
+ CCSDATA ccs = {0};
+ PROTORECVEVENT pre = {0};
+
+ ccs.szProtoService = PSR_MESSAGE;
+ ccs.hContact = hContact;
+ ccs.lParam = (LPARAM)&pre;
+ mir_snprintf(image_msg, SIZEOF(image_msg), "[img]%s[/img]", szPath);
+ pre.timestamp = time(NULL);
+ pre.szMessage = image_msg;
+ CallService(MS_PROTO_CHAINRECV, 0, (LPARAM) &ccs);
+ gg_netlog(gg, "gg_img_displayasmsg: Image saved to %s.", szPath);
+ }
+ else
+ {
+ gg_netlog(gg, "gg_img_displayasmsg: Cannot save image to %s.", szPath);
+ }
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Return if uin has it's window already opened
+BOOL gg_img_opened(GGPROTO *gg, uin_t uin)
+{
+ list_t l = gg->imagedlgs;
+ while (l)
+ {
+ GGIMAGEDLGDATA *dat = (GGIMAGEDLGDATA *)l->data;
+ if (dat->uin == uin)
+ return TRUE;
+ l = l->next;
+ }
+ return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Image Module : Looking for window entry, create if not found
+gg_img_display(GGPROTO *gg, HANDLE hContact, void *img)
+{
+ list_t l = gg->imagedlgs;
+ GGIMAGEDLGDATA *dat;
+
+ if (!img) return FALSE;
+
+ // Look for already open dialog
+ EnterCriticalSection(&gg->img_mutex);
+ while (l)
+ {
+ dat = (GGIMAGEDLGDATA *)l->data;
+ if (dat->bReceiving && dat->hContact == hContact)
+ break;
+ l = l->next;
+ }
+
+ if (!l) dat = NULL;
+
+ if (!dat)
+ {
+ dat = gg_img_recvdlg(gg, hContact);
+ dat->uin = DBGetContactSettingDword(hContact, gg->proto.m_szModuleName, GG_KEY_UIN, 0);
+
+ while (WaitForSingleObjectEx(dat->hEvent, INFINITE, TRUE) != WAIT_OBJECT_0);
+ CloseHandle(dat->hEvent);
+ dat->hEvent = NULL;
+
+ list_add(&gg->imagedlgs, dat, 0);
+ }
+ LeaveCriticalSection(&gg->img_mutex);
+
+ SendMessage(dat->hWnd, WM_ADDIMAGE, 0, (LPARAM)img);
+ if (/*DBGetContactSettingByte(NULL, "Chat", "FlashWindowHighlight", 0) != 0 && */
+ GetActiveWindow() != dat->hWnd && GetForegroundWindow() != dat->hWnd)
+ SetTimer(dat->hWnd, TIMERID_FLASHWND, 900, NULL);
+
+ /* DEPRECATED: No more grabbing the focus... just flashing
+ SetForegroundWindow(dat->hWnd);
+ SetFocus(dat->hWnd);
+ */
+
+ return TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Image Window : Frees image entry structure
+gg_img_releasepicture(void *img)
+{
+ if (!img)
+ return FALSE;
+ if (((GGIMAGEENTRY *)img)->lpszFileName)
+ free(((GGIMAGEENTRY *)img)->lpszFileName);
+ if (((GGIMAGEENTRY *)img)->hBitmap)
+ DeleteObject(((GGIMAGEENTRY *)img)->hBitmap);
+ if (((GGIMAGEENTRY *)img)->lpData)
+ free(((GGIMAGEENTRY *)img)->lpData);
+ free(img);
+
+ return TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Helper function to determine image file format and the right extension
+const char *gg_img_guessfileextension(const char *lpData)
+{
+ if (lpData != NULL)
+ {
+ if (memcmp(lpData, "BM", 2) == 0)
+ return ".bmp";
+ if (memcmp(lpData, "GIF8", 4) == 0)
+ return ".gif";
+ if (memcmp(lpData, "\xFF\xD8", 2) == 0)
+ return ".jpg";
+ if (memcmp(lpData, "\x89PNG", 4) == 0)
+ return ".png";
+ }
+ return "";
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Image Window : Loading picture and sending for display
+void *gg_img_loadpicture(GGPROTO *gg, struct gg_event* e, char *szFileName)
+{
+ GGIMAGEENTRY *dat;
+
+ if (!szFileName &&
+ (!e || !e->event.image_reply.size || !e->event.image_reply.image || !e->event.image_reply.filename))
+ return NULL;
+
+ dat = (GGIMAGEENTRY *)calloc(1, sizeof(GGIMAGEENTRY));
+ if (dat == NULL)
+ return NULL;
+
+ // Copy the file name
+ if (szFileName)
+ {
+ FILE *fp = fopen(szFileName, "rb");
+ if (!fp)
+ {
+ free(dat);
+ gg_netlog(gg, "gg_img_loadpicture(): fopen(\"%s\", \"rb\") failed.", szFileName);
+ return NULL;
+ }
+ fseek(fp, 0, SEEK_END);
+ dat->nSize = ftell(fp);
+ if (dat->nSize <= 0)
+ {
+ fclose(fp);
+ free(dat);
+ gg_netlog(gg, "gg_img_loadpicture(): Zero file size \"%s\" failed.", szFileName);
+ return NULL;
+ }
+ // Maximum acceptable image size
+ if (dat->nSize > 255 * 1024)
+ {
+ fclose(fp);
+ free(dat);
+ gg_netlog(gg, "gg_img_loadpicture(): Image size of \"%s\" exceeds 255 KB.", szFileName);
+ MessageBox(NULL, Translate("Image exceeds maximum allowed size of 255 KB."), GG_PROTONAME, MB_OK | MB_ICONEXCLAMATION);
+ return NULL;
+ }
+ fseek(fp, 0, SEEK_SET);
+ dat->lpData = malloc(dat->nSize);
+ if (fread(dat->lpData, 1, dat->nSize, fp) < dat->nSize)
+ {
+ free(dat->lpData);
+ fclose(fp);
+ free(dat);
+ gg_netlog(gg, "gg_img_loadpicture(): Reading file \"%s\" failed.", szFileName);
+ return NULL;
+ }
+ fclose(fp);
+ dat->lpszFileName = _strdup(szFileName);
+ }
+ // Copy picture from packet
+ else if (e && e->event.image_reply.filename)
+ {
+ dat->nSize = e->event.image_reply.size;
+ dat->lpData = malloc(dat->nSize);
+ memcpy(dat->lpData, e->event.image_reply.image, dat->nSize);
+
+ if (!gg_img_hasextension(e->event.image_reply.filename))
+ {
+ // Add missing file extension
+ const char *szImgType = gg_img_guessfileextension(dat->lpData);
+ if (*szImgType)
+ {
+ dat->lpszFileName = malloc(strlen(e->event.image_reply.filename) + strlen(szImgType) + 1);
+ if (dat->lpszFileName != NULL)
+ {
+ strcpy(dat->lpszFileName, e->event.image_reply.filename);
+ strcat(dat->lpszFileName, szImgType);
+ }
+ }
+ }
+
+ if (dat->lpszFileName == NULL)
+ dat->lpszFileName = _strdup(e->event.image_reply.filename);
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // Loading picture using Miranda Image services
+
+ // Load image from memory
+ if (!szFileName)
+ {
+ IMGSRVC_MEMIO memio;
+ memio.iLen = dat->nSize;
+ memio.pBuf = (void *)dat->lpData;
+ memio.fif = FIF_UNKNOWN; /* detect */
+ memio.flags = 0;
+ dat->hBitmap = (HBITMAP) CallService(MS_IMG_LOADFROMMEM, (WPARAM) &memio, 0);
+ }
+ // Load image from file
+ else
+ dat->hBitmap = (HBITMAP) CallService(MS_IMG_LOAD, (WPARAM) szFileName, 0);
+
+ // If everything is fine return the handle
+ if (dat->hBitmap) return dat;
+
+ gg_netlog(gg, "gg_img_loadpicture(): MS_IMG_LOAD(MEM) failed.");
+ if (dat)
+ {
+ if (dat->lpData)
+ free(dat->lpData);
+ if (dat->lpszFileName)
+ free(dat->lpszFileName);
+ free(dat);
+ }
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Image Recv : AddEvent proc
+INT_PTR gg_img_recvimage(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ CLISTEVENT *cle = (CLISTEVENT *)lParam;
+ GGIMAGEENTRY *img = (GGIMAGEENTRY *)cle->lParam;
+
+ gg_netlog(gg, "gg_img_recvimage(%x, %x): Popup new image.", wParam, lParam);
+ if (!img) return FALSE;
+
+ gg_img_display(gg, cle->hContact, img);
+
+ return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Windows queue management
+////////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////////
+// Removes dat structure
+int gg_img_remove(GGIMAGEDLGDATA *dat)
+{
+ GGIMAGEENTRY *temp = NULL, *img = NULL;
+ GGPROTO *gg;
+
+ if (!dat) return FALSE;
+ gg = dat->gg;
+
+ EnterCriticalSection(&gg->img_mutex);
+
+ // Remove the structure
+ img = dat->lpImages;
+
+ // Destroy picture handle
+ while (temp = img)
+ {
+ img = img->lpNext;
+ gg_img_releasepicture(temp);
+ }
+
+ // Remove from list
+ list_remove(&gg->imagedlgs, dat, 1);
+ LeaveCriticalSection(&gg->img_mutex);
+
+ return TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+//
+GGIMAGEDLGDATA *gg_img_find(GGPROTO *gg, uin_t uin, uint32_t crc32)
+{
+ int res = 0;
+ list_t l = gg->imagedlgs;
+ GGIMAGEDLGDATA *dat;
+
+ EnterCriticalSection(&gg->img_mutex);
+ while (l)
+ {
+ uin_t c_uin;
+
+ dat = (GGIMAGEDLGDATA *)l->data;
+ if (!dat) break;
+
+ c_uin = DBGetContactSettingDword(dat->hContact, dat->gg->proto.m_szModuleName, GG_KEY_UIN, 0);
+
+ if (!dat->bReceiving && dat->lpImages && dat->lpImages->crc32 == crc32 && c_uin == uin)
+ {
+ LeaveCriticalSection(&gg->img_mutex);
+ return dat;
+ }
+
+ l = l->next;
+ }
+ LeaveCriticalSection(&gg->img_mutex);
+
+ gg_netlog(gg, "gg_img_find(): Image not found on the list. It might be released before calling this function.");
+ return NULL;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// Image Module : Send on Request
+BOOL gg_img_sendonrequest(GGPROTO *gg, struct gg_event* e)
+{
+ GGIMAGEDLGDATA *dat = gg_img_find(gg, e->event.image_request.sender, e->event.image_request.crc32);
+
+ if (!gg || !dat || !gg_isonline(gg)) return FALSE;
+
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_image_reply(gg->sess, e->event.image_request.sender, dat->lpImages->lpszFileName, dat->lpImages->lpData, dat->lpImages->nSize);
+ LeaveCriticalSection(&gg->sess_mutex);
+
+ gg_img_remove(dat);
+
+ return TRUE;
+}
+
+////////////////////////////////////////////////////////////////////////////
+// Send Image : Run (Thread and main)
+INT_PTR gg_img_sendimg(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+ GGIMAGEDLGDATA *dat = NULL;
+
+ EnterCriticalSection(&gg->img_mutex);
+ if (!dat)
+ {
+ dat = (GGIMAGEDLGDATA *)calloc(1, sizeof(GGIMAGEDLGDATA));
+ dat->hContact = hContact;
+ dat->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ dat->gg = gg;
+ ResetEvent(dat->hEvent);
+
+ // Create new dialog
+ gg_forkthread(gg, gg_img_dlgcallthread, dat);
+
+ while (WaitForSingleObjectEx(dat->hEvent, INFINITE, TRUE) != WAIT_OBJECT_0);
+ CloseHandle(dat->hEvent);
+ dat->hEvent = NULL;
+
+ list_add(&gg->imagedlgs, dat, 0);
+ }
+
+ // Request choose dialog
+ SendMessage(dat->hWnd, WM_CHOOSEIMG, 0, 0);
+ LeaveCriticalSection(&gg->img_mutex);
+
+ return 0;
+}
diff --git a/protocols/Gadu-Gadu/import.c b/protocols/Gadu-Gadu/import.c
new file mode 100644
index 0000000000..a933135baf
--- /dev/null
+++ b/protocols/Gadu-Gadu/import.c
@@ -0,0 +1,670 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2006 Adam Strzelecki <ono+miranda@java.pl>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Checks if a group already exists in Miranda with
+// the specified name.
+// Returns 1 if a group with the name exists, returns 0 otherwise.
+int GroupNameExists(const char *name)
+{
+ char idstr[33];
+ DBVARIANT dbv;
+ int i;
+
+ for (i = 0; ; i++) {
+ _itoa(i, idstr, 10);
+ if (DBGetContactSettingString(NULL, "CListGroups", idstr, &dbv)) break;
+ if (!strcmp(dbv.pszVal + 1, name)) {
+ DBFreeVariant(&dbv);
+ return 1;
+ }
+ DBFreeVariant(&dbv);
+ }
+ return 0;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Creates a group with a specified name in the
+// Miranda contact list.
+// Returns proper group name
+char *CreateGroup(char *groupName)
+{
+ int groupId;
+ char groupIdStr[11];
+ char groupName2[127];
+ char *p;
+ DBVARIANT dbv;
+
+ // Cleanup group name from weird characters
+
+ // Skip first break
+ while(*groupName && *groupName == '\\') groupName++;
+
+ p = strrchr(groupName, '\\');
+ // Cleanup end
+ while(p && !(*(p + 1)))
+ {
+ *p = 0;
+ p = strrchr(groupName, '\\');
+ }
+ // Create upper groups
+ if(p)
+ {
+ *p = 0;
+ CreateGroup(groupName);
+ *p = '\\';
+ }
+
+ // Is this a duplicate?
+ if (!GroupNameExists(groupName))
+ {
+ lstrcpyn(groupName2 + 1, groupName, (int)strlen(groupName) + 1);
+
+ // Find an unused id
+ for (groupId = 0; ; groupId++) {
+ _itoa(groupId, groupIdStr,10);
+ if (DBGetContactSettingString(NULL, "CListGroups", groupIdStr, &dbv))
+ break;
+ DBFreeVariant(&dbv);
+ }
+
+ groupName2[0] = 1|GROUPF_EXPANDED; // 1 is required so we never get '\0'
+ DBWriteContactSettingString(NULL, "CListGroups", groupIdStr, groupName2);
+ }
+ return groupName;
+}
+
+char *gg_makecontacts(GGPROTO *gg, int cr)
+{
+ string_t s = string_init(NULL);
+ char *contacts;
+
+ // Readup contacts
+ HANDLE hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
+ while (hContact)
+ {
+ char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0);
+ if (szProto != NULL && !strcmp(szProto, GG_PROTO) && !DBGetContactSettingByte(hContact, GG_PROTO, "ChatRoom", 0))
+ {
+ DBVARIANT dbv;
+
+ // Readup FirstName
+ if (!DBGetContactSettingString(hContact, GG_PROTO, "FirstName", &dbv))
+ {
+ string_append(s, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ string_append_c(s, ';');
+ // Readup LastName
+ if (!DBGetContactSettingString(hContact, GG_PROTO, "LastName", &dbv))
+ {
+ string_append(s, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ string_append_c(s, ';');
+
+ // Readup Nick
+ if (!DBGetContactSettingString(hContact, "CList", "MyHandle", &dbv) || !DBGetContactSettingString(hContact, GG_PROTO, GG_KEY_NICK, &dbv))
+ {
+ DBVARIANT dbv2;
+ if (!DBGetContactSettingString(hContact, GG_PROTO, "NickName", &dbv2))
+ {
+ string_append(s, dbv2.pszVal);
+ DBFreeVariant(&dbv2);
+ }
+ else
+ string_append(s, dbv.pszVal);
+ string_append_c(s, ';');
+ string_append(s, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else
+ string_append_c(s, ';');
+ string_append_c(s, ';');
+
+ // Readup Phone (fixed: uses stored editable phones)
+ if (!DBGetContactSettingString(hContact, "UserInfo", "MyPhone0", &dbv))
+ {
+ // Remove SMS postfix
+ char *sms = strstr(dbv.pszVal, " SMS");
+ if(sms) *sms = 0;
+
+ string_append(s, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ string_append_c(s, ';');
+ // Readup Group
+ if (!DBGetContactSettingString(hContact, "CList", "Group", &dbv))
+ {
+ string_append(s, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ string_append_c(s, ';');
+ // Readup Uin
+ string_append(s, ditoa(DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0)));
+ string_append_c(s, ';');
+ // Readup Mail (fixed: uses stored editable mails)
+ if (!DBGetContactSettingString(hContact, "UserInfo", "Mye-mail0", &dbv))
+ {
+ string_append(s, dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ if(cr)
+ string_append(s, ";0;;0;\r\n");
+ else
+ string_append(s, ";0;;0;\n");
+ }
+ hContact = (HANDLE) CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM) hContact, 0);
+ }
+
+ contacts = string_free(s, 0);
+
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_makecontacts(): \n%s", contacts);
+#endif
+
+ return contacts;
+}
+
+char *strndup(char *str, int c)
+{
+ char *ret = (char*)malloc(c + 1);
+ ret[c] = 0;
+ strncpy(ret, str, c);
+ return ret;
+}
+
+void gg_parsecontacts(GGPROTO *gg, char *contacts)
+{
+ char *p = strchr(contacts, ':'), *n;
+ char *strFirstName, *strLastName, *strNickname, *strNick, *strPhone, *strGroup, *strUin, *strMail;
+ uin_t uin;
+
+ // Skip to proper data
+ if(p && p < strchr(contacts, ';')) p++;
+ else p = contacts;
+
+ while(p)
+ {
+ // Processing line
+ strFirstName = strLastName = strNickname = strNick = strPhone = strGroup = strUin = strMail = NULL;
+ uin = 0;
+
+ // FirstName
+ if(p)
+ {
+ n = strchr(p, ';');
+ if(n && n != p) strFirstName = strndup(p, (n - p));
+ p = (n + 1);
+ }
+ // LastName
+ if(n && p)
+ {
+ n = strchr(p, ';');
+ if(n && n != p) strLastName = strndup(p, (n - p));
+ p = (n + 1);
+ }
+ // Nickname
+ if(n && p)
+ {
+ n = strchr(p, ';');
+ if(n && n != p) strNickname = strndup(p, (n - p));
+ p = (n + 1);
+ }
+ // Nick
+ if(n && p)
+ {
+ n = strchr(p, ';');
+ if(n && n != p) strNick = strndup(p, (n - p));
+ p = (n + 1);
+ }
+ // Phone
+ if(n && p)
+ {
+ n = strchr(p, ';');
+ if(n && n != p)
+ {
+ strPhone = malloc((n - p) + 5);
+ strncpy(strPhone, p, (n - p));
+ strcpy((strPhone + (n - p)), " SMS"); // Add SMS postfix
+ }
+ p = (n + 1);
+ }
+ // Group
+ if(n && p)
+ {
+ n = strchr(p, ';');
+ if(n && n != p) strGroup = strndup(p, (n - p));
+ p = (n + 1);
+ }
+ // Uin
+ if(n && p)
+ {
+ n = strchr(p, ';');
+ if(n && n != p)
+ {
+ strUin = strndup(p, (n - p));
+ uin = atoi(strUin);
+ }
+ p = (n + 1);
+ }
+ // Mail
+ if(n && p)
+ {
+ n = strchr(p, ';');
+ if(n && n != p) strMail = strndup(p, (n - p));
+ n = strchr(p, '\n');
+ p = (n + 1);
+ }
+ if(!n) p = NULL;
+
+ // Loadup contact
+ if(uin && strNick)
+ {
+ HANDLE hContact = gg_getcontact(gg, uin, 1, 1, strNick);
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_parsecontacts(): Found contact %d with nickname \"%s\".", uin, strNick);
+#endif
+ // Write group
+ if(hContact && strGroup)
+ DBWriteContactSettingString(hContact, "CList", "Group", CreateGroup(strGroup));
+
+ // Write misc data
+ if(hContact && strFirstName) DBWriteContactSettingString(hContact, GG_PROTO, "FirstName", strFirstName);
+ if(hContact && strLastName) DBWriteContactSettingString(hContact, GG_PROTO, "LastName", strLastName);
+ if(hContact && strPhone) DBWriteContactSettingString(hContact, "UserInfo", "MyPhone0", strPhone); // Store now in User Info
+ if(hContact && strMail) DBWriteContactSettingString(hContact, "UserInfo", "Mye-mail0", strMail); // Store now in User Info
+ }
+
+ // Release stuff
+ if(strFirstName) free(strFirstName);
+ if(strLastName) free(strLastName);
+ if(strNickname) free(strNickname);
+ if(strNick) free(strNick);
+ if(strPhone) free(strPhone);
+ if(strGroup) free(strGroup);
+ if(strUin) free(strUin);
+ if(strMail) free(strMail);
+ }
+}
+
+//////////////////////////////////////////////////////////
+// import from server
+static INT_PTR gg_import_server(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ char *password;
+ uin_t uin;
+ DBVARIANT dbv;
+
+ // Check if connected
+ if (!gg_isonline(gg))
+ {
+ MessageBox(NULL,
+ Translate("You have to be connected before you can import/export contacts from/to server."),
+ GG_PROTONAME, MB_OK | MB_ICONSTOP
+ );
+ return 0;
+ }
+
+ // Readup password
+ if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv))
+ {
+ CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal);
+ password = _strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else return 0;
+
+ if (!(uin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0)))
+ return 0;
+
+ // Making contacts list
+ EnterCriticalSection(&gg->sess_mutex);
+ if (gg_userlist_request(gg->sess, GG_USERLIST_GET, NULL) == -1)
+ {
+ char error[128];
+ LeaveCriticalSection(&gg->sess_mutex);
+ mir_snprintf(error, sizeof(error), Translate("List cannot be imported because of error:\n\t%s"), strerror(errno));
+ MessageBox(
+ NULL,
+ error,
+ GG_PROTONAME,
+ MB_OK | MB_ICONSTOP
+ );
+ gg_netlog(gg, "gg_import_server(): Cannot import list because of \"%s\".", strerror(errno));
+ }
+ LeaveCriticalSection(&gg->sess_mutex);
+ free(password);
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// remove from server
+static INT_PTR gg_remove_server(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ char *password;
+ uin_t uin;
+ DBVARIANT dbv;
+
+ // Check if connected
+ if (!gg_isonline(gg))
+ {
+ MessageBox(NULL,
+ Translate("You have to be connected before you can import/export contacts from/to server."),
+ GG_PROTONAME, MB_OK | MB_ICONSTOP
+ );
+ return 0;
+ }
+
+ // Readup password
+ if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv))
+ {
+ CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal);
+ password = _strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else return 0;
+
+ if (!(uin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0)))
+ return 0;
+
+ // Making contacts list
+ EnterCriticalSection(&gg->sess_mutex);
+ if (gg_userlist_request(gg->sess, GG_USERLIST_PUT, NULL) == -1)
+ {
+ char error[128];
+ LeaveCriticalSection(&gg->sess_mutex);
+ mir_snprintf(error, sizeof(error), Translate("List cannot be removeed because of error:\n\t%s"), strerror(errno));
+ MessageBox(
+ NULL,
+ error,
+ GG_PROTONAME,
+ MB_OK | MB_ICONSTOP
+ );
+ gg_netlog(gg, "gg_remove_server(): Cannot remove list because of \"%s\".", strerror(errno));
+ }
+ LeaveCriticalSection(&gg->sess_mutex);
+
+ // Set list removal
+ gg->list_remove = TRUE;
+ free(password);
+
+ return 0;
+}
+
+static INT_PTR gg_import_text(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ char str[MAX_PATH] = "\0";
+ OPENFILENAME ofn = {0};
+ char filter[512], *pfilter;
+ struct _stat st;
+ FILE *f;
+
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ strncpy(filter, Translate("Text files"), sizeof(filter));
+ strncat(filter, " (*.txt)", sizeof(filter) - strlen(filter));
+ pfilter = filter + strlen(filter) + 1;
+ if(pfilter >= filter + sizeof(filter)) return 0;
+ strncpy(pfilter, "*.TXT", sizeof(filter) - (pfilter - filter));
+ pfilter = pfilter + strlen(pfilter) + 1;
+ if(pfilter >= filter + sizeof(filter)) return 0;
+ strncpy(pfilter, Translate("All Files"), sizeof(filter) - (pfilter - filter));
+ strncat(pfilter, " (*)", sizeof(filter) - (pfilter - filter) - strlen(pfilter));
+ pfilter = pfilter + strlen(pfilter) + 1;
+ if(pfilter >= filter + sizeof(filter)) return 0;
+ strncpy(pfilter, "*", sizeof(filter) - (pfilter - filter));
+ pfilter = pfilter + strlen(pfilter) + 1;
+ if(pfilter >= filter + sizeof(filter)) return 0;
+ *pfilter = '\0';
+ ofn.lpstrFilter = filter;
+ ofn.lpstrFile = str;
+ ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
+ ofn.nMaxFile = sizeof(str);
+ ofn.nMaxFileTitle = MAX_PATH;
+ ofn.lpstrDefExt = "txt";
+
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_import_text()");
+#endif
+ if(!GetOpenFileName(&ofn)) return 0;
+
+ f = fopen(str, "r");
+ _stat(str, &st);
+
+ if(f && st.st_size)
+ {
+ char *contacts = malloc(st.st_size * sizeof(char));
+ fread(contacts, sizeof(char), st.st_size, f);
+ fclose(f);
+ gg_parsecontacts(gg, contacts);
+ free(contacts);
+
+ MessageBox(
+ NULL,
+ Translate("List import successful."),
+ GG_PROTONAME,
+ MB_OK | MB_ICONINFORMATION
+ );
+ }
+ else
+ {
+ char error[128];
+ mir_snprintf(error, sizeof(error), Translate("List cannot be imported from file \"%s\" because of error:\n\t%s"), str, strerror(errno));
+ MessageBox(
+ NULL,
+ error,
+ GG_PROTONAME,
+ MB_OK | MB_ICONSTOP
+ );
+ gg_netlog(gg, "gg_import_text(): Cannot import list from file \"%s\" because of \"%s\".", str, strerror(errno));
+ }
+
+ return 0;
+}
+
+static INT_PTR gg_export_text(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ char str[MAX_PATH];
+ OPENFILENAME ofn = {0};
+ char filter[512], *pfilter;
+ FILE *f;
+
+ strncpy(str, Translate("contacts"), sizeof(str));
+ strncat(str, ".txt", sizeof(str) - strlen(str));
+
+ ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
+ strncpy(filter, Translate("Text files"), sizeof(filter));
+ strncat(filter, " (*.txt)", sizeof(filter) - strlen(filter));
+ pfilter = filter + strlen(filter) + 1;
+ if(pfilter >= filter + sizeof(filter)) return 0;
+ strncpy(pfilter, "*.TXT", sizeof(filter) - (pfilter - filter));
+ pfilter = pfilter + strlen(pfilter) + 1;
+ if(pfilter >= filter + sizeof(filter)) return 0;
+ strncpy(pfilter, Translate("All Files"), sizeof(filter) - (pfilter - filter));
+ strncat(pfilter, " (*)", sizeof(filter) - (pfilter - filter) - strlen(pfilter));
+ pfilter = pfilter + strlen(pfilter) + 1;
+ if(pfilter >= filter + sizeof(filter)) return 0;
+ strncpy(pfilter, "*", sizeof(filter) - (pfilter - filter));
+ pfilter = pfilter + strlen(pfilter) + 1;
+ if(pfilter >= filter + sizeof(filter)) return 0;
+ *pfilter = '\0';
+ ofn.lpstrFilter = filter;
+ ofn.lpstrFile = str;
+ ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;
+ ofn.nMaxFile = sizeof(str);
+ ofn.nMaxFileTitle = MAX_PATH;
+ ofn.lpstrDefExt = "txt";
+
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_export_text(%s).", str);
+#endif
+ if(!GetSaveFileName(&ofn)) return 0;
+
+ if(f = fopen(str, "w"))
+ {
+ char *contacts = gg_makecontacts(gg, 0);
+ fwrite(contacts, sizeof(char), strlen(contacts), f);
+ fclose(f);
+ free(contacts);
+
+ MessageBox(
+ NULL,
+ Translate("List export successful."),
+ GG_PROTONAME,
+ MB_OK | MB_ICONINFORMATION
+ );
+ }
+ else
+ {
+ char error[128];
+ mir_snprintf(error, sizeof(error), Translate("List cannot be exported to file \"%s\" because of error:\n\t%s"), str, strerror(errno));
+ MessageBox(
+ NULL,
+ error,
+ GG_PROTONAME,
+ MB_OK | MB_ICONSTOP
+ );
+ gg_netlog(gg, "gg_import_text(): Cannot export list to file \"%s\" because of \"%s\".", str, strerror(errno));
+ }
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// export to server
+static INT_PTR gg_export_server(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ char *password, *contacts;
+ uin_t uin;
+ DBVARIANT dbv;
+
+ // Check if connected
+ if (!gg_isonline(gg))
+ {
+ MessageBox(NULL,
+ Translate("You have to be connected before you can import/export contacts from/to server."),
+ GG_PROTONAME, MB_OK | MB_ICONSTOP
+ );
+ return 0;
+ }
+
+ // Readup password
+ if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv))
+ {
+ CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal);
+ password = _strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else return 0;
+
+ if (!(uin = DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0)))
+ return 0;
+
+ // Making contacts list
+ contacts = gg_makecontacts(gg, 1);
+
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_userlist_request(%s).", contacts);
+#endif
+
+ EnterCriticalSection(&gg->sess_mutex);
+ if (gg_userlist_request(gg->sess, GG_USERLIST_PUT, contacts) == -1)
+ {
+ char error[128];
+ LeaveCriticalSection(&gg->sess_mutex);
+ mir_snprintf(error, sizeof(error), Translate("List cannot be exported because of error:\n\t%s"), strerror(errno));
+ MessageBox(
+ NULL,
+ error,
+ GG_PROTONAME,
+ MB_OK | MB_ICONSTOP
+ );
+ gg_netlog(gg, "gg_export_server(): Cannot export list because of \"%s\".", strerror(errno));
+ }
+ LeaveCriticalSection(&gg->sess_mutex);
+
+ // Set list removal
+ gg->list_remove = FALSE;
+ free(contacts);
+ free(password);
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// Import menus and stuff
+void gg_import_init(GGPROTO *gg, HGENMENU hRoot)
+{
+ CLISTMENUITEM mi = {0};
+ char service[64];
+
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTHANDLE;
+ mi.hParentMenu = hRoot;
+
+ // Import from server item
+ mir_snprintf(service, sizeof(service), GGS_IMPORT_SERVER, GG_PROTO);
+ CreateProtoServiceFunction(service, gg_import_server, gg);
+ mi.position = 2000500001;
+ mi.icolibItem = GetIconHandle(IDI_IMPORT_SERVER);
+ mi.pszName = LPGEN("Import List From &Server");
+ mi.pszService = service;
+ gg->hMainMenu[2] = (HANDLE)CallService(MS_CLIST_ADDPROTOMENUITEM, 0, (LPARAM) &mi);
+
+ // Import from textfile
+ mir_snprintf(service, sizeof(service), GGS_IMPORT_TEXT, GG_PROTO);
+ CreateProtoServiceFunction(service, gg_import_text, gg);
+ mi.position = 2000500002;
+ mi.icolibItem = GetIconHandle(IDI_IMPORT_TEXT);
+ mi.pszName = LPGEN("Import List From &Text File...");
+ mi.pszService = service;
+ gg->hMainMenu[3] = (HANDLE)CallService(MS_CLIST_ADDPROTOMENUITEM, 0, (LPARAM) &mi);
+
+ // Remove from server
+ mir_snprintf(service, sizeof(service), GGS_REMOVE_SERVER, GG_PROTO);
+ CreateProtoServiceFunction(service, gg_remove_server, gg);
+ mi.position = 2000500003;
+ mi.icolibItem = GetIconHandle(IDI_REMOVE_SERVER);
+ mi.pszName = LPGEN("&Remove List From Server");
+ mi.pszService = service;
+ gg->hMainMenu[4] = (HANDLE)CallService(MS_CLIST_ADDPROTOMENUITEM, 0, (LPARAM) &mi);
+
+ // Export to server
+ mir_snprintf(service, sizeof(service), GGS_EXPORT_SERVER, GG_PROTO);
+ CreateProtoServiceFunction(service, gg_export_server, gg);
+ mi.position = 2005000001;
+ mi.icolibItem = GetIconHandle(IDI_EXPORT_SERVER);
+ mi.pszName = LPGEN("Export List To &Server");
+ mi.pszService = service;
+ gg->hMainMenu[5] = (HANDLE)CallService(MS_CLIST_ADDPROTOMENUITEM, 0, (LPARAM) &mi);
+
+ // Export to textfile
+ mir_snprintf(service, sizeof(service), GGS_EXPORT_TEXT, GG_PROTO);
+ CreateProtoServiceFunction(service, gg_export_text, gg);
+ mi.position = 2005000002;
+ mi.icolibItem = GetIconHandle(IDI_EXPORT_TEXT);
+ mi.pszName = LPGEN("Export List To &Text File...");
+ mi.pszService = service;
+ gg->hMainMenu[6] = (HANDLE)CallService(MS_CLIST_ADDPROTOMENUITEM, 0, (LPARAM) &mi);
+}
diff --git a/protocols/Gadu-Gadu/keepalive.c b/protocols/Gadu-Gadu/keepalive.c
new file mode 100644
index 0000000000..a0c7ddb032
--- /dev/null
+++ b/protocols/Gadu-Gadu/keepalive.c
@@ -0,0 +1,92 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2006 Adam Strzelecki <ono+miranda@java.pl>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+
+/* NOTE: Eventhough SetTimer seems to support UINT_PTR for idEvent, it seems that TimerProc
+ * does not get full pointer but just 2 byte lower bytes.
+ */
+#define MAX_TIMERS 8
+GGPROTO *g_timers[MAX_TIMERS] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+
+static VOID CALLBACK gg_keepalive(HWND hwnd, UINT message, UINT_PTR idEvent, DWORD dwTime)
+{
+ int i;
+
+ //Search for GGPROTO* context
+ for(i = 0; i < MAX_TIMERS; i++)
+ if(g_timers[i]->timer == idEvent)
+ break;
+
+ if(i < MAX_TIMERS)
+ {
+ GGPROTO *gg = g_timers[i];
+ if (gg_isonline(gg))
+ {
+ #ifdef DEBUGMODE
+ gg_netlog(gg, "Sending keep-alive");
+ #endif
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_ping(gg->sess);
+ LeaveCriticalSection(&gg->sess_mutex);
+ }
+ }
+}
+
+void gg_keepalive_init(GGPROTO *gg)
+{
+ if (DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_KEEPALIVE, GG_KEYDEF_KEEPALIVE))
+ {
+ int i;
+ for(i = 0; i < MAX_TIMERS && g_timers[i] != NULL; i++);
+ if(i < MAX_TIMERS)
+ {
+ #ifdef DEBUGMODE
+ gg_netlog(gg, "gg_keepalive_init(): Initializing Timer %d", i);
+ #endif
+ gg->timer = SetTimer(NULL, 0, 1000 * 30, gg_keepalive);
+ g_timers[i] = gg;
+ }
+ }
+}
+
+void gg_keepalive_destroy(GGPROTO *gg)
+{
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_destroykeepalive(): Killing Timer");
+#endif
+ if (gg->timer)
+ {
+ int i;
+ KillTimer(NULL, gg->timer);
+ for(i = 0; i < MAX_TIMERS; i++)
+ if(g_timers[i] == gg) {
+ g_timers[i] = NULL;
+ break;
+ }
+ gg->timer = 0;
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_destroykeepalive(): Killed Timer %d", i);
+#endif
+ }
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_destroykeepalive(): End");
+#endif
+}
diff --git a/protocols/Gadu-Gadu/libgadu/COPYING b/protocols/Gadu-Gadu/libgadu/COPYING
new file mode 100644
index 0000000000..6e816402b7
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/COPYING
@@ -0,0 +1,504 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+
diff --git a/protocols/Gadu-Gadu/libgadu/common.c b/protocols/Gadu-Gadu/libgadu/common.c
new file mode 100644
index 0000000000..b9b41c0547
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/common.c
@@ -0,0 +1,975 @@
+/* coding: UTF-8 */
+/* $Id: common.c 13762 2011-08-09 12:35:16Z dezred $ */
+
+/*
+ * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Robert J. WoĹşny <speedy@ziew.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 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.
+ */
+
+/*
+ * 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 <sys/types.h>
+#ifdef _WIN32
+#include "win32.h"
+#else
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef sun
+# include <sys/filio.h>
+#endif
+#endif /* _WIN32 */
+
+#include <errno.h>
+#include <fcntl.h>
+#ifndef _WIN32
+#include <netdb.h>
+#endif /* _WIN32 */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif /* _WIN32 */
+
+#include "libgadu.h"
+#include "internal.h"
+
+/**
+ * Plik, do którego będą przekazywane informacje odpluskwiania.
+ *
+ * Funkcja \c gg_debug() i pochodne mogą być przechwytywane przez aplikację
+ * korzystającą z biblioteki, by wyświetlić je na żądanie użytkownika lub
+ * zapisać do późniejszej analizy. Jeśli nie określono pliku, wybrane
+ * informacje będą wysyłane do standardowego wyjścia błędu (\c stderr).
+ *
+ * \ingroup debug
+ */
+FILE *gg_debug_file = NULL;
+
+#ifndef GG_DEBUG_DISABLE
+
+/**
+ * \internal Przekazuje informacje odpluskwiania do odpowiedniej funkcji.
+ *
+ * Jeśli aplikacja ustawiła odpowiednią funkcję obsługi w
+ * \c gg_debug_handler_session lub \c gg_debug_handler, jest ona wywoływana.
+ * W przeciwnym wypadku wynik jest wysyłany do standardowego wyjścia błędu.
+ *
+ * \param sess Struktura sesji (może być \c NULL)
+ * \param level Poziom informacji
+ * \param format Format wiadomości (zgodny z \c printf)
+ * \param ap Lista argumentĂłw (zgodna z \c printf)
+ */
+static 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.
+ *
+ * \param level Poziom wiadomości
+ * \param format Format wiadomości (zgodny z \c printf)
+ *
+ * \ingroup debug
+ */
+void gg_debug(int level, const char *format, ...)
+{
+ va_list ap;
+ int old_errno = errno;
+ va_start(ap, format);
+ gg_debug_common(NULL, level, format, ap);
+ va_end(ap);
+ errno = old_errno;
+}
+
+/**
+ * \internal Przekazuje informacjÄ™ odpluskwiania zwiÄ…zanÄ… z sesjÄ….
+ *
+ * \param 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;
+}
+
+/**
+ * \internal Przekazuje informację odpluskwiania związane z zawartością pamięci.
+ *
+ * \param sess Struktura sesji
+ * \param buf Adres w pamięci
+ * \param buf_length Ilość danych do wyświetlenia
+ * \param format Format wiadomości (zgodny z \c printf)
+ *
+ * \ingroup debug
+ */
+void gg_debug_dump_session(struct gg_session *sess, const void *buf, unsigned int buf_length, const char *format, ...)
+{
+ va_list ap;
+
+ if ((gg_debug_level & GG_DEBUG_DUMP)) {
+ unsigned int i;
+
+ va_start(ap, format);
+ gg_debug_common(sess, GG_DEBUG_DUMP, format, ap);
+ for (i = 0; i < buf_length; ++i)
+ gg_debug_session(sess, GG_DEBUG_DUMP, " %.2x", ((unsigned char*) buf)[i]);
+ gg_debug_session(sess, GG_DEBUG_DUMP, "\n");
+ va_end(ap);
+ }
+}
+
+#endif
+
+/**
+ * \internal Odpowiednik funkcji \c vsprintf alokujÄ…cy miejsce na wynik.
+ *
+ * Funkcja korzysta z funkcji \c vsnprintf, sprawdzając czy dostępna funkcja
+ * systemowa jest zgodna ze standardem C99 czy wcześniejszymi.
+ *
+ * \param format Format wiadomości (zgodny z \c printf)
+ * \param ap Lista argumentĂłw (zgodna z \c printf)
+ *
+ * \return Zaalokowany bufor lub NULL, jeśli zabrakło pamięci.
+ *
+ * \ingroup helper
+ */
+char *gg_vsaprintf(const char *format, va_list ap)
+{
+ int size = 0;
+ char *buf = NULL;
+
+#ifdef GG_CONFIG_HAVE_VA_COPY
+ va_list aq;
+
+ va_copy(aq, ap);
+#else
+# ifdef GG_CONFIG_HAVE___VA_COPY
+ va_list aq;
+
+ __va_copy(aq, ap);
+# endif
+#endif
+
+#ifndef GG_CONFIG_HAVE_C99_VSNPRINTF
+ {
+ int res;
+ char *tmp;
+
+ size = 128;
+ do {
+ size *= 2;
+ if (!(tmp = realloc(buf, size))) {
+ free(buf);
+ return NULL;
+ }
+ buf = tmp;
+ res = vsnprintf(buf, size, format, ap);
+ } while (res == size - 1 || res == -1);
+ }
+#else
+ {
+ char tmp[2];
+
+ /* libce Solarisa przy buforze NULL zawsze zwracają -1, więc
+ * musimy podać coś istniejącego jako cel printf()owania. */
+ size = vsnprintf(tmp, sizeof(tmp), format, ap);
+ if (!(buf = malloc(size + 1)))
+ return NULL;
+ }
+#endif
+
+#ifdef GG_CONFIG_HAVE_VA_COPY
+ vsnprintf(buf, size + 1, format, aq);
+ va_end(aq);
+#else
+# 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;
+}
+
+/**
+ * \internal Odpowiednik funkcji \c sprintf alokujÄ…cy miejsce na wynik.
+ *
+ * Funkcja korzysta z funkcji \c vsnprintf, sprawdzając czy dostępna funkcja
+ * systemowa jest zgodna ze standardem C99 czy wcześniejszymi.
+ *
+ * \param format Format wiadomości (zgodny z \c printf)
+ *
+ * \return Zaalokowany bufor lub NULL, jeśli zabrakło pamięci.
+ *
+ * \ingroup helper
+ */
+char *gg_saprintf(const char *format, ...)
+{
+ va_list ap;
+ char *res;
+
+ va_start(ap, format);
+ res = gg_vsaprintf(format, ap);
+ va_end(ap);
+
+ return res;
+}
+
+/**
+ * \internal Pobiera liniÄ™ tekstu z bufora.
+ *
+ * Funkcja niszczy bufor źródłowy bezpowrotnie, dzieląc go na kolejne ciągi
+ * znaków i obcina znaki końca linii.
+ *
+ * \param ptr Wskaźnik do zmiennej, która przechowuje aktualne położenie
+ * w analizowanym buforze
+ *
+ * \return Wskaźnik do kolejnej linii tekstu lub NULL, jeśli to już koniec
+ * bufora.
+ */
+char *gg_get_line(char **ptr)
+{
+ char *foo, *res;
+
+ if (!ptr || !*ptr || !strcmp(*ptr, ""))
+ return NULL;
+
+ res = *ptr;
+
+ if (!(foo = strchr(*ptr, '\n')))
+ *ptr += strlen(*ptr);
+ else {
+ size_t len;
+ *ptr = foo + 1;
+ *foo = 0;
+
+ len = strlen(res);
+
+ if (len > 1 && res[len - 1] == '\r')
+ res[len - 1] = 0;
+ }
+
+ return res;
+}
+
+/**
+ * \internal Czyta liniÄ™ tekstu z gniazda.
+ *
+ * Funkcja czyta tekst znak po znaku, więc nie jest efektywna, ale dzięki
+ * brakowi buforowania, nie koliduje z innymi funkcjami odczytu.
+ *
+ * \param sock Deskryptor gniazda
+ * \param buf WskaĹşnik do bufora
+ * \param length Długość bufora
+ *
+ * \return Zwraca \c buf jeśli się powiodło, lub \c NULL w przypadku błędu.
+ */
+char *gg_read_line(SOCKET sock, char *buf, int length)
+{
+ int ret;
+
+ if (!buf || length < 0)
+ return NULL;
+
+ for (; length > 1; buf++, length--) {
+ do {
+ if ((ret = gg_sock_read(sock, buf, 1)) == -1 && errno != EINTR && errno != EAGAIN) {
+ gg_debug(GG_DEBUG_MISC, "// gg_read_line() error on read (errno=%d, %s)\n", errno, strerror(errno));
+ *buf = 0;
+ return NULL;
+ } else if (ret == 0) {
+ gg_debug(GG_DEBUG_MISC, "// gg_read_line() eof reached\n");
+ *buf = 0;
+ return NULL;
+ }
+ } while (ret == -1 && (errno == EINTR || errno == EAGAIN));
+
+ if (*buf == '\n') {
+ buf++;
+ break;
+ }
+ }
+
+ *buf = 0;
+ return buf;
+}
+
+/**
+ * \internal Nawiązuje połączenie TCP.
+ *
+ * \param addr WskaĹşnik na strukturÄ™ \c in_addr z adresem serwera
+ * \param port Port serwera
+ * \param async Flaga asynchronicznego połączenia
+ *
+ * \return Deskryptor gniazda lub -1 w przypadku błędu
+ *
+ * \ingroup helper
+ */
+#ifdef GG_CONFIG_MIRANDA
+SOCKET gg_connect_internal(void *addr, int port, int async, SOCKET *gg_sock)
+#else
+SOCKET gg_connect(void *addr, int port, int async)
+#endif
+{
+ SOCKET sock;
+ int 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;
+ gg_sock_close(sock);
+ errno = errno2;
+ return -1;
+ }
+
+#ifdef GG_CONFIG_MIRANDA
+ if (gg_sock) *gg_sock = 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;
+ gg_sock_close(sock);
+ errno = errno2;
+ return -1;
+ }
+ }
+
+ sin.sin_port = htons((uint16_t)port);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = a->s_addr;
+
+ errno = 0;
+ 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;
+ gg_sock_close(sock);
+ errno = errno2;
+ return -1;
+ }
+ gg_debug(GG_DEBUG_MISC, "// gg_connect() connect() in progress\n");
+ }
+
+ return sock;
+}
+
+#ifdef GG_CONFIG_MIRANDA
+SOCKET gg_connect(void *addr, int port, int async)
+{
+ return gg_connect_internal(addr, port, async, 0);
+}
+#endif
+
+/**
+ * \internal Usuwa znaki końca linii.
+ *
+ * Funkcja działa bezpośrednio na buforze.
+ *
+ * \param line Bufor z tekstem
+ *
+ * \ingroup helper
+ */
+void gg_chomp(char *line)
+{
+ size_t len;
+
+ if (!line)
+ return;
+
+ len = strlen(line);
+
+ if (len > 0 && line[len - 1] == '\n')
+ line[--len] = 0;
+ if (len > 0 && line[len - 1] == '\r')
+ line[--len] = 0;
+}
+
+/**
+ * \internal Koduje ciÄ…g znakĂłw do postacji adresu HTTP.
+ *
+ * Zamienia znaki niedrukowalne, spoza ASCII i majÄ…ce specjalne znaczenie
+ * dla protokołu HTTP na encje postaci \c %XX, gdzie \c XX jest szesnastkową
+ * wartością znaku.
+ *
+ * \param str CiÄ…g znakĂłw do zakodowania
+ *
+ * \return Zaalokowany bufor lub \c NULL w przypadku błędu.
+ *
+ * \ingroup helper
+ */
+char *gg_urlencode(const char *str)
+{
+ char *q, *buf, hex[] = "0123456789abcdef";
+ const char *p;
+ unsigned int size = 0;
+
+ if (!str)
+ str = "";
+
+ for (p = str; *p; p++, size++) {
+ if (!((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || *p == ' ') || (*p == '@') || (*p == '.') || (*p == '-'))
+ size += 2;
+ }
+
+ if (!(buf = malloc(size + 1)))
+ return NULL;
+
+ for (p = str, q = buf; *p; p++, q++) {
+ if ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p >= '0' && *p <= '9') || (*p == '@') || (*p == '.') || (*p == '-'))
+ *q = *p;
+ else {
+ if (*p == ' ')
+ *q = '+';
+ else {
+ *q++ = '%';
+ *q++ = hex[*p >> 4 & 15];
+ *q = hex[*p & 15];
+ }
+ }
+ }
+
+ *q = 0;
+
+ return buf;
+}
+
+/**
+ * \internal Wyznacza skrót dla usług HTTP.
+ *
+ * Funkcja jest wykorzystywana do wyznaczania skrótu adresu e-mail, hasła
+ * i innych wartości przekazywanych jako parametry usług HTTP.
+ *
+ * W parametrze \c format należy umieścić znaki określające postać kolejnych
+ * parametrów: \c 's' jeśli parametr jest ciągiem znaków, \c 'u' jeśli jest
+ * liczbÄ….
+ *
+ * \param format Format kolejnych parametrĂłw (niezgodny z \c printf)
+ *
+ * \return Wartość skrótu
+ */
+int gg_http_hash(const char *format, ...)
+{
+ unsigned int a, c, i, j;
+ va_list ap;
+ int b = -1;
+
+ va_start(ap, format);
+
+ for (j = 0; j < strlen(format); j++) {
+ char *arg, buf[16];
+
+ if (format[j] == 'u') {
+ snprintf(buf, sizeof(buf), "%d", va_arg(ap, uin_t));
+ arg = buf;
+ } else {
+ if (!(arg = va_arg(ap, char*)))
+ arg = "";
+ }
+
+ i = 0;
+ while ((c = (unsigned char) arg[i++]) != 0) {
+ a = (c ^ b) + (c << 8);
+ b = (a >> 24) | (a << 8);
+ }
+ }
+
+ va_end(ap);
+
+ return (b < 0 ? -b : b);
+}
+
+/**
+ * \internal Zestaw znakĂłw kodowania base64.
+ */
+static char gg_base64_charset[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/**
+ * \internal Koduje ciÄ…g znakĂłw do base64.
+ *
+ * Wynik funkcji należy zwolnić za pomocą \c free.
+ *
+ * \param buf Bufor z danami do zakodowania
+ *
+ * \return Zaalokowany bufor z zakodowanymi danymi
+ *
+ * \ingroup helper
+ */
+char *gg_base64_encode(const char *buf)
+{
+ char *out, *res;
+ unsigned int i = 0, j = 0, k = 0, len = (unsigned int)strlen(buf);
+
+ res = out = malloc((len / 3 + 1) * 4 + 2);
+
+ if (!res)
+ return NULL;
+
+ while (j <= len) {
+ switch (i % 4) {
+ case 0:
+ k = (buf[j] & 252) >> 2;
+ break;
+ case 1:
+ if (j < len)
+ k = ((buf[j] & 3) << 4) | ((buf[j + 1] & 240) >> 4);
+ else
+ k = (buf[j] & 3) << 4;
+
+ j++;
+ break;
+ case 2:
+ if (j < len)
+ k = ((buf[j] & 15) << 2) | ((buf[j + 1] & 192) >> 6);
+ else
+ k = (buf[j] & 15) << 2;
+
+ j++;
+ break;
+ case 3:
+ k = buf[j++] & 63;
+ break;
+ }
+ *out++ = gg_base64_charset[k];
+ i++;
+ }
+
+ if (i % 4)
+ for (j = 0; j < 4 - (i % 4); j++, out++)
+ *out = '=';
+
+ *out = 0;
+
+ return res;
+}
+
+/**
+ * \internal Dekoduje ciÄ…g znakĂłw zapisany w base64.
+ *
+ * Wynik funkcji należy zwolnić za pomocą \c free.
+ *
+ * \param buf Bufor źródłowy z danymi do zdekodowania
+ *
+ * \return Zaalokowany bufor ze zdekodowanymi danymi
+ *
+ * \ingroup helper
+ */
+char *gg_base64_decode(const char *buf)
+{
+ char *res, *save, *foo, val;
+ const char *end;
+ unsigned int index = 0;
+
+ if (!buf)
+ return NULL;
+
+ save = res = calloc(1, (strlen(buf) / 4 + 1) * 3 + 2);
+
+ if (!save)
+ return NULL;
+
+ end = buf + strlen(buf);
+
+ while (*buf && buf < end) {
+ if (*buf == '\r' || *buf == '\n') {
+ buf++;
+ continue;
+ }
+ if (!(foo = strchr(gg_base64_charset, *buf)))
+ foo = gg_base64_charset;
+ val = (int)(foo - gg_base64_charset);
+ buf++;
+ switch (index) {
+ case 0:
+ *res |= val << 2;
+ break;
+ case 1:
+ *res++ |= val >> 4;
+ *res |= val << 4;
+ break;
+ case 2:
+ *res++ |= val >> 2;
+ *res |= val << 6;
+ break;
+ case 3:
+ *res++ |= val;
+ break;
+ }
+ index++;
+ index %= 4;
+ }
+ *res = 0;
+
+ return save;
+}
+
+/**
+ * \internal Tworzy nagłówek autoryzacji serwera pośredniczącego.
+ *
+ * Dane pobiera ze zmiennych globalnych \c gg_proxy_username i
+ * \c gg_proxy_password.
+ *
+ * \return Zaalokowany bufor z tekstem lub NULL, jeśli serwer pośredniczący
+ * nie jest uĹĽywany lub nie wymaga autoryzacji.
+ */
+char *gg_proxy_auth()
+{
+ char *tmp, *enc, *out;
+ size_t tmp_size;
+
+ if (!gg_proxy_enabled || !gg_proxy_username || !gg_proxy_password)
+ return NULL;
+
+ if (!(tmp = malloc((tmp_size = strlen(gg_proxy_username) + strlen(gg_proxy_password) + 2))))
+ return NULL;
+
+ snprintf(tmp, tmp_size, "%s:%s", gg_proxy_username, gg_proxy_password);
+
+ if (!(enc = gg_base64_encode(tmp))) {
+ 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);
+
+ 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;
+
+/**
+ * \internal Tworzy tablicÄ™ pomocniczÄ… do wyznaczania sumy kontrolnej.
+ */
+static void gg_crc32_make_table(void)
+{
+ uint32_t h = 1;
+ unsigned int i, j;
+
+ memset(gg_crc32_table, 0, sizeof(gg_crc32_table));
+
+ for (i = 128; i; i >>= 1) {
+ h = (h >> 1) ^ ((h & 1) ? 0xedb88320L : 0);
+
+ for (j = 0; j < 256; j += 2 * i)
+ gg_crc32_table[i + j] = gg_crc32_table[j] ^ h;
+ }
+
+ gg_crc32_initialized = 1;
+}
+
+/**
+ * Wyznacza sumÄ™ kontrolnÄ… CRC32.
+ *
+ * \param crc Suma kontrola poprzedniego bloku danych lub 0 jeśli liczona
+ * jest suma kontrolna pierwszego bloku
+ * \param buf Bufor danych
+ * \param len Długość bufora danych
+ *
+ * \return Suma kontrolna.
+ */
+uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len)
+{
+ if (!gg_crc32_initialized)
+ gg_crc32_make_table();
+
+ if (!buf || len < 0)
+ return crc;
+
+ crc ^= 0xffffffffL;
+
+ while (len--)
+ crc = (crc >> 8) ^ gg_crc32_table[(crc ^ *buf++) & 0xff];
+
+ 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] = (char)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 = (int)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] = (char)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:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/protocols/Gadu-Gadu/libgadu/compat.h b/protocols/Gadu-Gadu/libgadu/compat.h
new file mode 100644
index 0000000000..1aa17d1e4a
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/compat.h
@@ -0,0 +1,36 @@
+/* coding: UTF-8 */
+/* $Id: compat.h 11075 2009-12-20 15:01:43Z dezred $ */
+
+/*
+ * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Robert J. WoĹşny <speedy@ziew.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 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 compat.h
+ *
+ * \brief Makra zapewniające kompatybilność API na różnych systemach
+ */
+
+#ifndef __COMPAT_H
+#define __COMPAT_H
+
+#ifdef sun
+# define INADDR_NONE ((in_addr_t) 0xffffffff)
+#endif
+
+#endif
diff --git a/protocols/Gadu-Gadu/libgadu/dcc.c b/protocols/Gadu-Gadu/libgadu/dcc.c
new file mode 100644
index 0000000000..2fbe1b430c
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/dcc.c
@@ -0,0 +1,1363 @@
+/* coding: UTF-8 */
+/* $Id: dcc.c 12145 2010-07-07 23:49:04Z dezred $ */
+
+/*
+ * (C) Copyright 2001-2008 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Tomasz Chiliński <chilek@chilan.com>
+ * Adam Wysocki <gophi@ekg.chmurka.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/**
+ * \file dcc.c
+ *
+ * \brief Obsługa połączeń bezpośrednich do wersji Gadu-Gadu 6.x
+ */
+
+#ifndef _WIN64
+#define _USE_32BIT_TIME_T
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef _WIN32
+#include "win32.h"
+#else
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef sun
+# include <sys/filio.h>
+#endif
+#endif /* _WIN32 */
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#include "compat.h"
+#include "libgadu.h"
+
+#ifdef _WIN32
+#undef small
+#endif
+
+#ifndef GG_DEBUG_DISABLE
+
+/**
+ * \internal Przekazuje zawartość pakietu do odpluskwiania.
+ *
+ * \param prefix Prefiks informacji
+ * \param fd Deskryptor gniazda
+ * \param buf Bufor z danumi
+ * \param size Rozmiar bufora z danymi
+ */
+static void gg_dcc_debug_data(const char *prefix, SOCKET 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()
+ *
+ * \param sess Struktura sesji
+ * \param uin Numer odbiorcy
+ *
+ * \return Patrz \c gg_send_message_ctcp()
+ *
+ * \ingroup dcc6
+ */
+int gg_dcc_request(struct gg_session *sess, uin_t uin)
+{
+ return gg_send_message_ctcp(sess, GG_CLASS_CTCP, uin, (unsigned char*) "\002", 1);
+}
+
+/**
+ * \internal Zamienia znacznik czasu w postaci uniksowej na format API WIN32.
+ *
+ * \note Funkcja działa jedynie gdy kompilator obsługuje typ danych
+ * \c long \c long.
+ *
+ * \param ut Czas w postaci uniksowej
+ * \param ft Czas w postaci API WIN32
+ */
+static void gg_dcc_fill_filetime(time_t ut, uint32_t *ft)
+{
+#ifdef GG_CONFIG_HAVE_LONG_LONG
+ unsigned long long tmp;
+
+ tmp = ut;
+ tmp += 11644473600LL;
+ tmp *= 10000000LL;
+
+#ifndef GG_CONFIG_BIGENDIAN
+ ft[0] = (uint32_t) tmp;
+ ft[1] = (uint32_t) (tmp >> 32);
+#else
+ ft[0] = gg_fix32((uint32_t) (tmp >> 32));
+ ft[1] = gg_fix32((uint32_t) tmp);
+#endif
+
+#endif
+}
+
+/**
+ * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku.
+ *
+ * \note Większą funkcjonalność zapewnia funkcja \c gg_dcc_fill_file_info2().
+ *
+ * \param d Struktura połączenia
+ * \param filename Nazwa pliku
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc6
+ */
+int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename)
+{
+ return gg_dcc_fill_file_info2(d, filename, filename);
+}
+
+/**
+ * Wypełnia pola struktury \c gg_dcc niezbędne do wysłania pliku.
+ *
+ * \param d Struktura połączenia
+ * \param filename Nazwa pliku zapisywana w strukturze
+ * \param local_filename Nazwa pliku w lokalnym systemie plikĂłw
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc6
+ */
+int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename)
+{
+ struct stat st;
+ const char *name, *ext, *p;
+ unsigned char *q;
+ int i, j;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_fill_file_info2(%p, \"%s\", \"%s\");\n", d, filename, local_filename);
+
+ if (!d || d->type != GG_SESSION_DCC_SEND) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() invalid arguments\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (stat(local_filename, &st) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() stat() failed (%s)\n", strerror(errno));
+ return -1;
+ }
+
+ if ((st.st_mode & S_IFDIR)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() that's a directory\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if ((d->file_fd = open(local_filename, O_RDONLY | O_BINARY)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() open() failed (%s)\n", strerror(errno));
+ return -1;
+ }
+
+ memset(&d->file_info, 0, sizeof(d->file_info));
+
+ if (!(st.st_mode & S_IWUSR))
+ d->file_info.mode |= gg_fix32(GG_DCC_FILEATTR_READONLY);
+
+ gg_dcc_fill_filetime(st.st_atime, d->file_info.atime);
+ gg_dcc_fill_filetime(st.st_mtime, d->file_info.mtime);
+ gg_dcc_fill_filetime(st.st_ctime, d->file_info.ctime);
+
+ d->file_info.size = gg_fix32(st.st_size);
+ d->file_info.mode = gg_fix32(0x20); /* FILE_ATTRIBUTE_ARCHIVE */
+
+#ifdef _WIN32
+ if (!(name = strrchr(filename, '\\')))
+#else
+ if (!(name = strrchr(filename, '/')))
+#endif
+ name = filename;
+ else
+ name++;
+
+ if (!(ext = strrchr(name, '.')))
+ ext = name + strlen(name);
+
+ for (i = 0, p = name; i < 8 && p < ext; i++, p++)
+ d->file_info.short_filename[i] = toupper(name[i]);
+
+ if (i == 8 && p < ext) {
+ d->file_info.short_filename[6] = '~';
+ d->file_info.short_filename[7] = '1';
+ }
+
+ if (strlen(ext) > 0) {
+ for (j = 0; *ext && j < 4; j++, p++)
+ d->file_info.short_filename[i + j] = toupper(ext[j]);
+ }
+
+ for (q = d->file_info.short_filename; *q; q++) {
+ if (*q == 185) {
+ *q = 165;
+ } else if (*q == 230) {
+ *q = 198;
+ } else if (*q == 234) {
+ *q = 202;
+ } else if (*q == 179) {
+ *q = 163;
+ } else if (*q == 241) {
+ *q = 209;
+ } else if (*q == 243) {
+ *q = 211;
+ } else if (*q == 156) {
+ *q = 140;
+ } else if (*q == 159) {
+ *q = 143;
+ } else if (*q == 191) {
+ *q = 175;
+ }
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_fill_file_info2() short name \"%s\", dos name \"%s\"\n", name, d->file_info.short_filename);
+ strncpy((char*) d->file_info.filename, name, sizeof(d->file_info.filename) - 1);
+
+ return 0;
+}
+
+/**
+ * \internal Rozpoczyna połączenie bezpośrednie z danym klientem.
+ *
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin WĹ‚asny numer
+ * \param peer_uin Numer odbiorcy
+ * \param type Rodzaj połączenia (\c GG_SESSION_DCC_SEND lub \c GG_SESSION_DCC_GET)
+ *
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
+ */
+static struct gg_dcc *gg_dcc_transfer(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin, int type)
+{
+ struct gg_dcc *d = NULL;
+ struct in_addr addr;
+
+ addr.s_addr = ip;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_transfer(%s, %d, %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;
+ return NULL;
+ }
+
+ if (!(d = (void*) calloc(1, sizeof(*d)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() not enough memory\n");
+ return NULL;
+ }
+
+ d->check = GG_CHECK_WRITE;
+ d->state = GG_STATE_CONNECTING;
+ d->type = type;
+ d->timeout = GG_DEFAULT_TIMEOUT;
+ d->file_fd = -1;
+ d->active = 1;
+ d->fd = -1;
+ d->uin = my_uin;
+ d->peer_uin = peer_uin;
+
+ if ((d->fd = gg_connect(&addr, port, 1)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_transfer() connection failed\n");
+ free(d);
+ return NULL;
+ }
+
+ return d;
+}
+
+/**
+ * Rozpoczyna odbieranie pliku przez zwrotne połączenie bezpośrednie.
+ *
+ * \param ip Adres IP nadawcy
+ * \param port Port nadawcy
+ * \param my_uin WĹ‚asny numer
+ * \param peer_uin Numer nadawcy
+ *
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc6
+ */
+struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_get_file() handing over to gg_dcc_transfer()\n");
+
+ return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_GET);
+}
+
+/**
+ * Rozpoczyna wysyłanie pliku.
+ *
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin WĹ‚asny numer
+ * \param peer_uin Numer odbiorcy
+ *
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc6
+ */
+struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_send_file() handing over to gg_dcc_transfer()\n");
+
+ return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_SEND);
+}
+
+/**
+ * Rozpoczyna połączenie głosowe.
+ *
+ * \param ip Adres IP odbiorcy
+ * \param port Port odbiorcy
+ * \param my_uin WĹ‚asny numer
+ * \param peer_uin Numer odbiorcy
+ *
+ * \return Struktura \c gg_dcc lub \c NULL w przypadku błędu
+ *
+ * \ingroup dcc6
+ */
+struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_chat() handing over to gg_dcc_transfer()\n");
+
+ return gg_dcc_transfer(ip, port, my_uin, peer_uin, GG_SESSION_DCC_VOICE);
+}
+
+/**
+ * Ustawia typ przychodzącego połączenia bezpośredniego.
+ *
+ * Funkcję należy wywołać po otrzymaniu zdarzenia \c GG_EVENT_DCC_CALLBACK.
+ *
+ * \param d Struktura połączenia
+ * \param type Rodzaj połączenia (\c GG_SESSION_DCC_SEND lub
+ * \c GG_SESSION_DCC_VOICE)
+ *
+ * \ingroup dcc6
+ */
+void gg_dcc_set_type(struct gg_dcc *d, int type)
+{
+ d->type = type;
+ d->state = (type == GG_SESSION_DCC_SEND) ? GG_STATE_SENDING_FILE_INFO : GG_STATE_SENDING_VOICE_REQUEST;
+}
+
+/**
+ * \internal Funkcja zwrotna połączenia bezpośredniego.
+ *
+ * Pole \c callback struktury \c gg_dcc zawiera wskaĹşnik do tej funkcji.
+ * Wywołuje ona \c gg_watch_fd() i zachowuje wynik w polu \c event.
+ *
+ * \note Funkcjonalność funkcjo zwrotnej nie jest już wspierana.
+ *
+ * \param d Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc_callback(struct gg_dcc *d)
+{
+ struct gg_event *e = gg_dcc_watch_fd(d);
+
+ d->event = e;
+
+ return (e != NULL) ? 0 : -1;
+}
+
+/**
+ * Tworzy gniazdo nasłuchujące dla połączeń bezpośrednich.
+ *
+ * Funkcja przywiÄ…zuje gniazdo do pierwszego wolnego portu TCP.
+ *
+ * \param uin WĹ‚asny numer
+ * \param port Preferowany port (jeśli równy 0 lub -1, próbuje się domyślnego)
+ *
+ * \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;
+ SOCKET sock;
+ int bound = 0, errno2;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_create_dcc_socket(%d, %d);\n", uin, port);
+
+ if (!uin) {
+ gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() invalid arguments\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() can't create socket (%s)\n", strerror(errno));
+ return NULL;
+ }
+
+ if (port == 0 || port == -1)
+ 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;
+ else {
+ if (++port == 65535) {
+ gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() no free port found\n");
+ gg_sock_close(sock);
+ return NULL;
+ }
+ }
+ }
+
+ if (listen(sock, 10)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() unable to listen (%s)\n", strerror(errno));
+ errno2 = errno;
+ gg_sock_close(sock);
+ errno = errno2;
+ return NULL;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() bound to port %d\n", port);
+
+ if (!(c = malloc(sizeof(*c)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_create_dcc_socket() not enough memory for struct\n");
+ gg_sock_close(sock);
+ return NULL;
+ }
+ memset(c, 0, sizeof(*c));
+
+ c->port = c->id = port;
+ c->fd = sock;
+ c->type = GG_SESSION_DCC_SOCKET;
+ c->uin = uin;
+ c->timeout = -1;
+ c->state = GG_STATE_LISTENING;
+ c->check = GG_CHECK_READ;
+ c->callback = gg_dcc_callback;
+ c->destroy = gg_dcc_free;
+
+ return c;
+}
+
+/**
+ * Wysyła ramkę danych połączenia głosowego.
+ *
+ * \param d Struktura połączenia
+ * \param buf Bufor z danymi
+ * \param length Długość bufora z danymi
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc6
+ */
+int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length)
+{
+ struct packet_s {
+ uint8_t type;
+ uint32_t length;
+ } GG_PACKED;
+ struct packet_s packet;
+
+ gg_debug(GG_DEBUG_FUNCTION, "++ gg_dcc_voice_send(%p, %p, %d);\n", d, buf, length);
+ if (!d || !buf || length < 0 || d->type != GG_SESSION_DCC_VOICE) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() invalid argument\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ packet.type = 0x03; /* XXX */
+ packet.length = gg_fix32(length);
+
+ if (gg_sock_write(d->fd, &packet, sizeof(packet)) < (signed)sizeof(packet)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() write() failed\n");
+ return -1;
+ }
+ gg_dcc_debug_data("write", d->fd, &packet, sizeof(packet));
+
+ if (gg_sock_write(d->fd, buf, length) < length) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_voice_send() write() failed\n");
+ return -1;
+ }
+ gg_dcc_debug_data("write", d->fd, buf, length);
+
+ return 0;
+}
+
+/**
+ * \internal Odbiera dane z połączenia bezpośredniego z obsługą błędów.
+ *
+ * \param fd Deskryptor gniazda
+ * \param buf Bufor na dane
+ * \param size Rozmiar bufora na dane
+ */
+#define gg_dcc_read(fd, buf, size) \
+{ \
+ int tmp = gg_sock_read(fd, buf, size); \
+ \
+ if (tmp < (int) size) { \
+ if (tmp == -1) { \
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno)); \
+ } else if (tmp == 0) { \
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed, connection broken\n"); \
+ } else { \
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (%d bytes, %d needed)\n", tmp, size); \
+ } \
+ e->type = GG_EVENT_DCC_ERROR; \
+ e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \
+ return e; \
+ } \
+ gg_dcc_debug_data("read", fd, buf, size); \
+}
+
+/**
+ * \internal Wysyła dane do połączenia bezpośredniego z obsługą błędów.
+ *
+ * \param fd Deskryptor gniazda
+ * \param buf Bufor z danymi
+ * \param size Rozmiar bufora z danymi
+ */
+#define gg_dcc_write(fd, buf, size) \
+{ \
+ int tmp; \
+ gg_dcc_debug_data("write", fd, buf, size); \
+ tmp = gg_sock_write(fd, buf, size); \
+ if (tmp < (int) size) { \
+ if (tmp == -1) { \
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (errno=%d, %s)\n", errno, strerror(errno)); \
+ } else { \
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d needed, %d done)\n", size, tmp); \
+ } \
+ e->type = GG_EVENT_DCC_ERROR; \
+ e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE; \
+ return e; \
+ } \
+}
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
+ * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free.
+ *
+ * \param h Struktura połączenia
+ *
+ * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
+ *
+ * \ingroup dcc6
+ */
+struct gg_event *gg_dcc_watch_fd(struct gg_dcc *h)
+{
+ struct gg_event *e;
+ int foo;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_watch_fd(%p);\n", h);
+
+ if (!h || (h->type != GG_SESSION_DCC && h->type != GG_SESSION_DCC_SOCKET && h->type != GG_SESSION_DCC_SEND && h->type != GG_SESSION_DCC_GET && h->type != GG_SESSION_DCC_VOICE)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() invalid argument\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!(e = (void*) calloc(1, sizeof(*e)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory\n");
+ return NULL;
+ }
+
+ e->type = GG_EVENT_NONE;
+
+ if (h->type == GG_SESSION_DCC_SOCKET) {
+ struct sockaddr_in sin;
+ struct gg_dcc *c;
+ SOCKET fd;
+ int 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;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() new direct connection from %s:%d\n", inet_ntoa(sin.sin_addr), htons(sin.sin_port));
+
+#ifdef FIONBIO
+ if (ioctl(fd, FIONBIO, &one) == -1) {
+#else
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+#endif
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() can't set nonblocking (errno=%d, %s)\n", errno, strerror(errno));
+ gg_sock_close(fd);
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+ return e;
+ }
+
+ if (!(c = (void*) calloc(1, sizeof(*c)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() not enough memory for client data\n");
+
+ free(e);
+ gg_sock_close(fd);
+ return NULL;
+ }
+
+ c->fd = fd;
+ c->check = GG_CHECK_READ;
+ c->state = GG_STATE_READING_UIN_1;
+ c->type = GG_SESSION_DCC;
+ c->timeout = GG_DEFAULT_TIMEOUT;
+ c->file_fd = -1;
+ c->remote_addr = sin.sin_addr.s_addr;
+ c->remote_port = ntohs(sin.sin_port);
+
+ e->type = GG_EVENT_DCC_NEW;
+ e->event.dcc_new = c;
+
+ return e;
+ } else {
+ struct gg_dcc_tiny_packet tiny;
+ struct gg_dcc_small_packet small;
+ struct gg_dcc_big_packet big;
+ int size, tmp, res;
+ unsigned int utmp, res_size = sizeof(res);
+ char buf[1024], ack[] = "UDAG";
+
+ struct gg_dcc_file_info_packet {
+ struct gg_dcc_big_packet big;
+ struct gg_file_info file_info;
+ } GG_PACKED;
+ struct gg_dcc_file_info_packet file_info_packet;
+
+ switch (h->state) {
+ case GG_STATE_READING_UIN_1:
+ case GG_STATE_READING_UIN_2:
+ {
+ uin_t uin;
+
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_READING_UIN_%d\n", (h->state == GG_STATE_READING_UIN_1) ? 1 : 2);
+
+ gg_dcc_read(h->fd, &uin, sizeof(uin));
+
+ if (h->state == GG_STATE_READING_UIN_1) {
+ h->state = GG_STATE_READING_UIN_2;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ h->peer_uin = gg_fix32(uin);
+ } else {
+ h->state = GG_STATE_SENDING_ACK;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ h->uin = gg_fix32(uin);
+ e->type = GG_EVENT_DCC_CLIENT_ACCEPT;
+ }
+
+ return e;
+ }
+
+ case GG_STATE_SENDING_ACK:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_SENDING_ACK\n");
+
+ gg_dcc_write(h->fd, ack, 4);
+
+ h->state = GG_STATE_READING_TYPE;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ return e;
+
+ case GG_STATE_READING_TYPE:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_TYPE\n");
+
+ gg_dcc_read(h->fd, &small, sizeof(small));
+
+ small.type = gg_fix32(small.type);
+
+ switch (small.type) {
+ case 0x0003: /* XXX */
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() callback\n");
+ h->type = GG_SESSION_DCC_SEND;
+ h->state = GG_STATE_SENDING_FILE_INFO;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ e->type = GG_EVENT_DCC_CALLBACK;
+
+ break;
+
+ case 0x0002: /* XXX */
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() dialin\n");
+ h->type = GG_SESSION_DCC_GET;
+ h->state = GG_STATE_READING_REQUEST;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ h->incoming = 1;
+
+ break;
+
+ default:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc type (%.4x) from %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_REQUEST:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_REQUEST\n");
+
+ gg_dcc_read(h->fd, &small, sizeof(small));
+
+ small.type = gg_fix32(small.type);
+
+ switch (small.type) {
+ case 0x0001: /* XXX */
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() file transfer request\n");
+ h->state = GG_STATE_READING_FILE_INFO;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ break;
+
+ case 0x0003: /* XXX */
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() voice chat request\n");
+ h->state = GG_STATE_SENDING_VOICE_ACK;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DCC_TIMEOUT_VOICE_ACK;
+ h->type = GG_SESSION_DCC_VOICE;
+ e->type = GG_EVENT_DCC_NEED_VOICE_ACK;
+
+ break;
+
+ default:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown dcc request (%.4x) from %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_dcc_read(h->fd, &file_info_packet, sizeof(file_info_packet));
+
+ memcpy(&h->file_info, &file_info_packet.file_info, sizeof(h->file_info));
+
+ h->file_info.mode = gg_fix32(h->file_info.mode);
+ h->file_info.size = gg_fix32(h->file_info.size);
+
+ h->state = GG_STATE_SENDING_FILE_ACK;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DCC_TIMEOUT_FILE_ACK;
+
+ e->type = GG_EVENT_DCC_NEED_FILE_ACK;
+
+ return e;
+
+ case GG_STATE_SENDING_FILE_ACK:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_ACK\n");
+
+ big.type = gg_fix32(0x0006); /* XXX */
+ big.dunno1 = gg_fix32(h->offset);
+ big.dunno2 = 0;
+
+ gg_dcc_write(h->fd, &big, sizeof(big));
+
+ h->state = GG_STATE_READING_FILE_HEADER;
+ h->chunk_size = sizeof(big);
+ h->chunk_offset = 0;
+ if (!(h->chunk_buf = malloc(sizeof(big)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n");
+ free(e);
+ return NULL;
+ }
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ return e;
+
+ case GG_STATE_SENDING_VOICE_ACK:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_ACK\n");
+
+ tiny.type = 0x01; /* XXX */
+
+ 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 = gg_sock_read(h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
+
+ if (tmp == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno));
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_NET;
+ return e;
+ }
+
+ gg_dcc_debug_data("read", h->fd, h->chunk_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
+
+ h->chunk_offset += tmp;
+
+ if (h->chunk_offset < h->chunk_size)
+ return e;
+
+ memcpy(&big, h->chunk_buf, sizeof(big));
+ free(h->chunk_buf);
+ h->chunk_buf = NULL;
+
+ big.type = gg_fix32(big.type);
+ h->chunk_size = gg_fix32(big.dunno1);
+ h->chunk_offset = 0;
+
+ if (big.type == 0x0005) { /* XXX */
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() transfer refused\n");
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_REFUSED;
+ return e;
+ }
+
+ if (h->chunk_size == 0) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() empty chunk, EOF\n");
+ e->type = GG_EVENT_DCC_DONE;
+ return e;
+ }
+
+ h->state = GG_STATE_GETTING_FILE;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ h->established = 1;
+
+ return e;
+
+ case GG_STATE_READING_VOICE_HEADER:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_HEADER\n");
+
+ gg_dcc_read(h->fd, &tiny, sizeof(tiny));
+
+ switch (tiny.type) {
+ case 0x03: /* XXX */
+ h->state = GG_STATE_READING_VOICE_SIZE;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ h->established = 1;
+ break;
+ case 0x04: /* XXX */
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() peer breaking connection\n");
+ /* XXX zwracać odpowiedni event */
+ default:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() unknown request (%.2x)\n", tiny.type);
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+ }
+
+ return e;
+
+ case GG_STATE_READING_VOICE_SIZE:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_SIZE\n");
+
+ gg_dcc_read(h->fd, &small, sizeof(small));
+
+ small.type = gg_fix32(small.type);
+
+ if (small.type < 16 || small.type > sizeof(buf)) {
+ 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;
+ }
+
+ h->chunk_size = small.type;
+ h->chunk_offset = 0;
+
+ if (!(h->voice_buf = malloc(h->chunk_size))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory for voice frame\n");
+ free(e);
+ return NULL;
+ }
+
+ h->state = GG_STATE_READING_VOICE_DATA;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ return e;
+
+ case GG_STATE_READING_VOICE_DATA:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_DATA\n");
+
+ tmp = gg_sock_read(h->fd, h->voice_buf + h->chunk_offset, h->chunk_size - h->chunk_offset);
+ if (tmp < 1) {
+ if (tmp == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed (errno=%d, %s)\n", errno, strerror(errno));
+ } else {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed, connection broken\n");
+ }
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_NET;
+ return e;
+ }
+
+ gg_dcc_debug_data("read", h->fd, h->voice_buf + h->chunk_offset, tmp);
+
+ h->chunk_offset += tmp;
+
+ if (h->chunk_offset >= h->chunk_size) {
+ e->type = GG_EVENT_DCC_VOICE_DATA;
+ e->event.dcc_voice_data.data = (unsigned char*) h->voice_buf;
+ e->event.dcc_voice_data.length = h->chunk_size;
+ h->state = GG_STATE_READING_VOICE_HEADER;
+ h->voice_buf = NULL;
+ }
+
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ return e;
+
+ case GG_STATE_CONNECTING:
+ {
+ uin_t uins[2];
+
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_CONNECTING\n");
+
+ res = 0;
+ if ((foo = gg_getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size)) || res) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connection failed (fd=%d,errno=%d(%s),foo=%d,res=%d(%s))\n", h->fd, errno, strerror(errno), foo, res, strerror(res));
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+ return e;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() connected, sending uins\n");
+
+ uins[0] = gg_fix32(h->uin);
+ uins[1] = gg_fix32(h->peer_uin);
+
+ gg_dcc_write(h->fd, uins, sizeof(uins));
+
+ h->state = GG_STATE_READING_ACK;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ return e;
+ }
+
+ case GG_STATE_READING_ACK:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_ACK\n");
+
+ gg_dcc_read(h->fd, buf, 4);
+
+ if (strncmp(buf, ack, 4)) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() did't get ack\n");
+
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+ return e;
+ }
+
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ h->state = GG_STATE_SENDING_REQUEST;
+
+ return e;
+
+ case GG_STATE_SENDING_VOICE_REQUEST:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_VOICE_REQUEST\n");
+
+ small.type = gg_fix32(0x0003);
+
+ 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_dcc_write(h->fd, &small, sizeof(small));
+
+ switch (h->type) {
+ case GG_SESSION_DCC_GET:
+ h->state = GG_STATE_READING_REQUEST;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ break;
+
+ case GG_SESSION_DCC_SEND:
+ h->state = GG_STATE_SENDING_FILE_INFO;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ if (h->file_fd == -1)
+ e->type = GG_EVENT_DCC_NEED_FILE_INFO;
+ break;
+
+ case GG_SESSION_DCC_VOICE:
+ h->state = GG_STATE_SENDING_VOICE_REQUEST;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ break;
+ }
+
+ return e;
+
+ case GG_STATE_SENDING_FILE_INFO:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_INFO\n");
+
+ if (h->file_fd == -1) {
+ e->type = GG_EVENT_DCC_NEED_FILE_INFO;
+ return e;
+ }
+
+ small.type = gg_fix32(0x0001); /* XXX */
+
+ gg_dcc_write(h->fd, &small, sizeof(small));
+
+ file_info_packet.big.type = gg_fix32(0x0003); /* XXX */
+ file_info_packet.big.dunno1 = 0;
+ file_info_packet.big.dunno2 = 0;
+
+ memcpy(&file_info_packet.file_info, &h->file_info, sizeof(h->file_info));
+
+ /* zostają teraz u nas, więc odwracamy z powrotem */
+ h->file_info.size = gg_fix32(h->file_info.size);
+ h->file_info.mode = gg_fix32(h->file_info.mode);
+
+ gg_dcc_write(h->fd, &file_info_packet, sizeof(file_info_packet));
+
+ h->state = GG_STATE_READING_FILE_ACK;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DCC_TIMEOUT_FILE_ACK;
+
+ return e;
+
+ case GG_STATE_READING_FILE_ACK:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_FILE_ACK\n");
+
+ gg_dcc_read(h->fd, &big, sizeof(big));
+
+ /* 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;
+
+ e->type = GG_EVENT_DCC_ACK;
+
+ return e;
+
+ case GG_STATE_READING_VOICE_ACK:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_READING_VOICE_ACK\n");
+
+ gg_dcc_read(h->fd, &tiny, sizeof(tiny));
+
+ if (tiny.type != 0x01) {
+ gg_debug(GG_DEBUG_MISC, "// invalid reply (%.2x), connection refused\n", tiny.type);
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_REFUSED;
+ return e;
+ }
+
+ h->state = GG_STATE_READING_VOICE_HEADER;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ e->type = GG_EVENT_DCC_ACK;
+
+ return e;
+
+ case GG_STATE_SENDING_FILE_HEADER:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE_HEADER\n");
+
+ h->chunk_offset = 0;
+
+ if ((h->chunk_size = h->file_info.size - h->offset) > 4096) {
+ h->chunk_size = 4096;
+ big.type = gg_fix32(0x0003); /* XXX */
+ } else
+ big.type = gg_fix32(0x0002); /* XXX */
+
+ big.dunno1 = gg_fix32(h->chunk_size);
+ big.dunno2 = 0;
+
+ gg_dcc_write(h->fd, &big, sizeof(big));
+
+ h->state = GG_STATE_SENDING_FILE;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ h->established = 1;
+
+ return e;
+
+ case GG_STATE_SENDING_FILE:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_SENDING_FILE\n");
+
+ if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf))
+ utmp = sizeof(buf);
+
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset=%d, size=%d\n", h->offset, h->file_info.size);
+
+ /* koniec pliku? */
+ if (h->file_info.size == 0) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof on empty file\n");
+ e->type = GG_EVENT_DCC_DONE;
+
+ return e;
+ }
+
+ if (h->offset >= h->file_info.size) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n");
+ e->type = GG_EVENT_DCC_DONE;
+ return e;
+ }
+
+ lseek(h->file_fd, h->offset, SEEK_SET);
+
+ size = read(h->file_fd, buf, utmp);
+
+ /* błąd */
+ if (size == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno));
+
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_FILE;
+
+ return e;
+ }
+
+ /* koniec pliku? */
+ if (size == 0) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n");
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_EOF;
+
+ return e;
+ }
+
+ /* jeśli wczytaliśmy więcej, utnijmy. */
+ if (h->offset + size > h->file_info.size) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() too much (read=%d, ofs=%d, size=%d)\n", size, h->offset, h->file_info.size);
+ size = h->file_info.size - h->offset;
+
+ if (size < 1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() reached EOF after cutting\n");
+ e->type = GG_EVENT_DCC_DONE;
+ return e;
+ }
+ }
+
+ tmp = gg_sock_write(h->fd, buf, size);
+
+ if (tmp == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%s)\n", strerror(errno));
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_NET;
+ return e;
+ }
+
+ if (tmp == 0) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() 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 += tmp;
+
+ if (h->chunk_offset >= h->chunk_size) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n");
+ h->state = GG_STATE_SENDING_FILE_HEADER;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ } else {
+ h->state = GG_STATE_SENDING_FILE;
+ h->timeout = GG_DCC_TIMEOUT_SEND;
+ }
+
+ h->check = GG_CHECK_WRITE;
+
+ return e;
+
+ case GG_STATE_GETTING_FILE:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_GETTING_FILE\n");
+
+ if ((utmp = h->chunk_size - h->chunk_offset) > sizeof(buf))
+ utmp = sizeof(buf);
+
+ if (h->offset >= h->file_info.size) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() offset >= size, finished\n");
+ e->type = GG_EVENT_DCC_DONE;
+ return e;
+ }
+
+ size = gg_sock_read(h->fd, buf, utmp);
+
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() ofs=%d, size=%d, read()=%d\n", h->offset, h->file_info.size, size);
+
+ /* błąd */
+ if (size == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() failed. (errno=%d, %s)\n", errno, strerror(errno));
+
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_NET;
+
+ return e;
+ }
+
+ /* koniec? */
+ if (size == 0) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() read() reached eof\n");
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_EOF;
+
+ return e;
+ }
+
+ tmp = write(h->file_fd, buf, size);
+
+ if (tmp == -1 || tmp < size) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() write() failed (%d:fd=%d:res=%d:%s)\n", tmp, h->file_fd, size, strerror(errno));
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_NET;
+ return e;
+ }
+
+ h->offset += size;
+
+ if (h->offset >= h->file_info.size) {
+ e->type = GG_EVENT_DCC_DONE;
+ return e;
+ }
+
+ h->chunk_offset += size;
+
+ if (h->chunk_offset >= h->chunk_size) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() chunk finished\n");
+ h->state = GG_STATE_READING_FILE_HEADER;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ h->chunk_offset = 0;
+ h->chunk_size = sizeof(big);
+ if (!(h->chunk_buf = malloc(sizeof(big)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() out of memory\n");
+ free(e);
+ return NULL;
+ }
+ } else {
+ h->state = GG_STATE_GETTING_FILE;
+ h->timeout = GG_DCC_TIMEOUT_GET;
+ }
+
+ h->check = GG_CHECK_READ;
+
+ return e;
+
+ default:
+ gg_debug(GG_DEBUG_MISC, "// gg_dcc_watch_fd() GG_STATE_???\n");
+ e->type = GG_EVENT_DCC_ERROR;
+ e->event.dcc_error = GG_ERROR_DCC_HANDSHAKE;
+
+ return e;
+ }
+ }
+
+ return e;
+}
+
+/**
+ * Zwalnia zasoby używane przez połączenie bezpośrednie.
+ *
+ * \param d Struktura połączenia
+ *
+ * \ingroup dcc6
+ */
+void gg_dcc_free(struct gg_dcc *d)
+{
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_dcc_free(%p);\n", d);
+
+ if (!d)
+ return;
+
+ if (d->fd != -1)
+ gg_sock_close(d->fd);
+
+ free(d->chunk_buf);
+ free(d);
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/protocols/Gadu-Gadu/libgadu/dcc7.c b/protocols/Gadu-Gadu/libgadu/dcc7.c
new file mode 100644
index 0000000000..f3813e6e83
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/dcc7.c
@@ -0,0 +1,1655 @@
+/* coding: UTF-8 */
+/* $Id: dcc7.c,v 1.2 2007-07-20 23:00:49 wojtekka Exp $ */
+
+/*
+ * (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Tomasz Chiliński <chilek@chilan.com>
+ * Adam Wysocki <gophi@ekg.chmurka.net>
+ * Bartłomiej Zimoń <uzi18@o2.pl>
+ *
+ * Thanks to Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110,
+ * USA.
+ */
+
+/**
+ * \file dcc7.c
+ *
+ * \brief Obsługa połączeń bezpośrednich od wersji Gadu-Gadu 7.x
+ */
+
+#ifndef _WIN64
+#define _USE_32BIT_TIME_T
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef _WIN32
+#include "win32.h"
+#else
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef sun
+# include <sys/filio.h>
+#endif
+#endif /* _WIN32 */
+#include <time.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#include "compat.h"
+#include "libgadu.h"
+#include "protocol.h"
+#include "resolver.h"
+#include "internal.h"
+
+/**
+ * \internal Dodaje połączenie bezpośrednie do sesji.
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_session_add(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_add(%p, %p)\n", sess, dcc);
+
+ if (!sess || !dcc || dcc->next) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_add() invalid parameters\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ dcc->next = sess->dcc7_list;
+ sess->dcc7_list = dcc;
+
+ return 0;
+}
+
+/**
+ * \internal Usuwa połączenie bezpośrednie z sesji.
+ *
+ * \param sess Struktura sesji
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_session_remove(struct gg_session *sess, struct gg_dcc7 *dcc)
+{
+ struct gg_dcc7 *tmp;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_remove(%p, %p)\n", sess, dcc);
+
+ if (sess == NULL || dcc == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_session_remove() invalid parameters\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (sess->dcc7_list == dcc) {
+ sess->dcc7_list = dcc->next;
+ dcc->next = NULL;
+ return 0;
+ }
+
+ for (tmp = sess->dcc7_list; tmp != NULL; tmp = tmp->next) {
+ if (tmp->next == dcc) {
+ tmp->next = dcc->next;
+ dcc->next = NULL;
+ return 0;
+ }
+ }
+
+ errno = ENOENT;
+ return -1;
+}
+
+/**
+ * \internal Zwraca strukturę połączenia o danym identyfikatorze.
+ *
+ * \param sess Struktura sesji
+ * \param id Identyfikator połączenia
+ * \param uin Numer nadawcy lub odbiorcy
+ *
+ * \return Struktura połączenia lub \c NULL jeśli nie znaleziono
+ */
+static struct gg_dcc7 *gg_dcc7_session_find(struct gg_session *sess, gg_dcc7_id_t id, uin_t uin)
+{
+ struct gg_dcc7 *tmp;
+ int empty;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_session_find(%p, ..., %d)\n", sess, (int) uin);
+
+ empty = !memcmp(&id, "\0\0\0\0\0\0\0\0", 8);
+
+ for (tmp = sess->dcc7_list; tmp; tmp = tmp->next) {
+ if (empty) {
+ if (tmp->peer_uin == uin && !tmp->state == GG_STATE_WAITING_FOR_ACCEPT)
+ return tmp;
+ } else {
+ if (!memcmp(&tmp->cid, &id, sizeof(id)))
+ return tmp;
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * \internal Rozpoczyna proces pobierania adresu
+ *
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_get_relay_addr(struct gg_dcc7 *dcc)
+{
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_get_relay_addr(%p)\n", dcc);
+
+ if (dcc == NULL || dcc->sess == NULL) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() invalid parameters\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (dcc->sess->resolver_start(&dcc->fd, &dcc->resolver, GG_RELAY_HOST) == -1) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_get_relay_addr() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
+ return -1;
+ }
+
+ dcc->state = GG_STATE_RESOLVING_RELAY;
+ dcc->check = GG_CHECK_READ;
+ dcc->timeout = GG_DEFAULT_TIMEOUT;
+
+ return 0;
+}
+
+/**
+ * \internal Nawiązuje połączenie bezpośrednie
+ *
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_dcc7_connect(struct gg_dcc7 *dcc)
+{
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_connect(%p)\n", dcc);
+
+ if (dcc == NULL) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, 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((dcc) ? (dcc)->sess : NULL, 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;
+ SOCKET fd;
+
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_listen(%p, %d)\n", dcc, port);
+
+ if (!dcc) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, 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_session((dcc) ? (dcc)->sess : NULL, 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_session((dcc) ? (dcc)->sess : NULL, 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_listen() no free port found\n");
+ gg_sock_close(fd);
+ errno = ENOENT;
+ return -1;
+ }
+ }
+
+ if (listen(fd, 1)) {
+ int errsv = errno;
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_listen() unable to listen (%s)\n", strerror(errno));
+ gg_sock_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;
+ uint16_t external_port;
+ uint16_t local_port;
+ uint32_t count;
+
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_listen_and_send_info(%p)\n", dcc);
+
+ if (!dcc->sess->client_port)
+ local_port = dcc->sess->external_port;
+ else
+ local_port = dcc->sess->client_port;
+
+ if (gg_dcc7_listen(dcc, local_port) == -1)
+ return -1;
+
+ if (!dcc->sess->external_port || dcc->local_port != local_port)
+ external_port = dcc->local_port;
+ else
+ external_port = dcc->sess->external_port;
+
+ if (!dcc->sess->external_addr || dcc->local_port != local_port)
+ dcc->local_addr = dcc->sess->client_addr;
+ else
+ dcc->local_addr = dcc->sess->external_addr;
+
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// dcc7_listen_and_send_info() sending IP address %s and port %d\n", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), external_port);
+
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.uin = gg_fix32(dcc->peer_uin);
+ pkt.type = GG_DCC7_TYPE_P2P;
+ pkt.id = dcc->cid;
+ snprintf((char*) pkt.info, sizeof(pkt.info), "%s %d", inet_ntoa(*((struct in_addr*) &dcc->local_addr)), external_port);
+ // TODO: implement hash count
+ // we MUST fill hash to recive from server request for server connection
+ //snprintf((char*) pkt.hash, sizeof(pkt.hash), "0");
+ count = dcc->local_addr + external_port * rand();
+ snprintf((char*) pkt.hash, sizeof(pkt.hash), "%d", count);
+
+ 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_reverse_connect(%p)\n", dcc);
+
+ if (dcc->reverse) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() already reverse connection\n");
+ return -1;
+ }
+
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_reverse_connect() timeout, trying reverse connection\n");
+ gg_sock_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 = (unsigned int)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 | O_BINARY)) == -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;
+ }
+
+#ifdef _WIN32
+ if ((tmp = strrchr(filename1250, '\\')))
+#else
+ if ((tmp = strrchr(filename1250, '/')))
+#endif
+ 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_accept(%p, %d)\n", dcc, offset);
+
+ if (!dcc || !dcc->sess) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_reject(%p, %d)\n", dcc, reason);
+
+ if (!dcc || !dcc->sess) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, 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);
+}
+
+/**
+ * Przerwanie żądania przesłania pliku.
+ *
+ * \param dcc Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup dcc7
+ */
+int gg_dcc7_abort(struct gg_dcc7 *dcc)
+{
+ struct gg_dcc7_abort pkt;
+
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_abort(%p)\n", dcc);
+
+ if (!dcc || !dcc->sess) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_abort() invalid parameters\n");
+ errno = EFAULT;
+ return -1;
+ }
+
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.id = dcc->cid;
+ pkt.uin_from = gg_fix32(dcc->uin);
+ pkt.uin_to = gg_fix32(dcc->peer_uin);
+
+ return gg_send_packet(dcc->sess, GG_DCC7_ABORT, &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 != (int)gg_fix32(p->type))
+ continue;
+
+ tmp->cid = p->id;
+
+ switch (tmp->dcc_type) {
+ case GG_DCC7_TYPE_FILE:
+ {
+ struct gg_dcc7_new s;
+
+ memset(&s, 0, sizeof(s));
+ s.id = tmp->cid;
+ s.type = gg_fix32(GG_DCC7_TYPE_FILE);
+ s.uin_from = gg_fix32(tmp->uin);
+ s.uin_to = gg_fix32(tmp->peer_uin);
+ s.size = gg_fix32(tmp->size);
+
+ 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;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ 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;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ 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);
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "// gg_dcc7_handle_info() received address: %s, hash: %s\n", p->info, p->hash);
+
+ if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(p->uin)))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unknown dcc session\n");
+ return 0;
+ }
+
+ if (dcc->state == GG_STATE_CONNECTED) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() state is already connected\n");
+ return 0;
+ }
+
+ switch (p->type)
+ {
+ case GG_DCC7_TYPE_P2P:
+ if ((dcc->remote_addr = inet_addr(p->info)) == INADDR_NONE) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid IP address\n");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ 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;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return 0;
+ }
+
+ if (dcc->state == GG_STATE_WAITING_FOR_INFO) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() wainting for info so send one\n");
+ gg_dcc7_listen_and_send_info(dcc);
+ return 0;
+ }
+
+ break;
+
+ case GG_DCC7_TYPE_SERVER:
+ if (!(tmp = strstr(p->info, "GG"))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unknown info packet\n");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return 0;
+ }
+
+#if defined(GG_CONFIG_HAVE_UINT64_T) && defined(GG_CONFIG_HAVE_STRTOULL)
+ {
+ uint64_t cid;
+
+ cid = strtoull(tmp + 2, NULL, 0);
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() info.str=%s, info.id=%llu, sess.id=%llu\n", tmp + 2, cid, *((unsigned long long*) &dcc->cid));
+
+ cid = gg_fix64(cid);
+
+ if (memcmp(&dcc->cid, &cid, sizeof(cid)) != 0) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() invalid session id\n");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return 0;
+ }
+ }
+#endif
+
+ if (gg_dcc7_get_relay_addr(dcc) == -1) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unable to retrieve relay address\n");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return 0;
+ }
+
+ // XXX wysyłać dopiero jeśli uda się połączyć z serwerem?
+
+ gg_send_packet(dcc->sess, GG_DCC7_INFO, payload, len, NULL);
+
+ break;
+
+ default:
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_info() unhandled transfer type (%d)\n", p->type);
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ 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;
+// e->event.dcc7_error_ex.dcc7 = dcc;
+// return 0;
+// }
+
+ if (dcc->state == GG_STATE_LISTENING) {
+ gg_sock_close(dcc->fd);
+ dcc->fd = -1;
+ dcc->reverse = 1;
+ }
+
+ if (dcc->type == GG_SESSION_DCC7_SEND) {
+ e->type = GG_EVENT_DCC7_ACCEPT;
+ e->event.dcc7_accept.dcc7 = dcc;
+ e->event.dcc7_accept.type = gg_fix32(p->type);
+ e->event.dcc7_accept.remote_ip = dcc->remote_addr;
+ e->event.dcc7_accept.remote_port = dcc->remote_port;
+ } else {
+ e->type = GG_EVENT_DCC7_PENDING;
+ e->event.dcc7_pending.dcc7 = dcc;
+ }
+
+ if (dcc->state == GG_STATE_RESOLVING_RELAY)
+ return 0;
+
+ if (gg_dcc7_connect(dcc) == -1) {
+ if (gg_dcc7_reverse_connect(dcc) == -1) {
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_NET;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ 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;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ 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 przerwania żądania 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_abort(struct gg_session *sess, struct gg_event *e, void *payload, int len)
+{
+ struct gg_dcc7_aborted *p = payload;
+ struct gg_dcc7 *dcc;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_dcc7_handle_abort(%p, %p, %p, %d)\n", sess, e, payload, len);
+
+ if (!(dcc = gg_dcc7_session_find(sess, p->id, gg_fix32(0)))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_abort() unknown dcc session\n");
+ return 0;
+ }
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_abort() state %d\n", dcc->state);
+
+ if (dcc->state != GG_STATE_IDLE) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_dcc7_handle_abort() invalid state\n");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return 0;
+ }
+
+ e->type = GG_EVENT_DCC7_REJECT;
+ e->event.dcc7_reject.dcc7 = dcc;
+ e->event.dcc7_reject.reason = gg_fix32(GG_DCC7_REJECT_USER);
+
+ // 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_postauth_fixup(%p)\n", dcc);
+
+ if (!dcc) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, 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_session((dcc) ? (dcc)->sess : NULL, 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid parameters\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (!(e = malloc(sizeof(struct gg_event)))) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, 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;
+ SOCKET fd;
+ int one = 1;
+ unsigned int sin_len = sizeof(sin);
+
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_LISTENING\n");
+
+ if ((fd = accept(dcc->fd, (struct sockaddr*) &sin, &sin_len)) == -1) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() accept() failed (%s)\n", strerror(errno));
+ return e;
+ }
+
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() can't set nonblocking (%s)\n", strerror(errno));
+ gg_sock_close(fd);
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+
+ gg_sock_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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING\n");
+
+ dcc->soft_timeout = 0;
+
+ if (dcc->timeout == 0)
+ error = ETIMEDOUT;
+
+ if (error || (res = gg_getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &error, &error_size)) == -1 || error != 0) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (%s)\n", (res == -1) ? strerror(errno) : strerror(error));
+
+ if (dcc->relay) {
+ for (dcc->relay_index++; dcc->relay_index < dcc->relay_count; dcc->relay_index++) {
+ dcc->remote_addr = dcc->relay_list[dcc->relay_index].addr;
+ dcc->remote_port = dcc->relay_list[dcc->relay_index].port;
+
+ if (gg_dcc7_connect(dcc) == 0)
+ break;
+ }
+
+ if (dcc->relay_index >= dcc->relay_count) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+ } else {
+ if (gg_dcc7_reverse_connect(dcc) != -1) {
+ e->type = GG_EVENT_DCC7_PENDING;
+ e->event.dcc7_pending.dcc7 = dcc;
+ } else {
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_NET;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ }
+
+ return e;
+ }
+ }
+
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connected, sending id\n");
+
+ dcc->state = GG_STATE_SENDING_ID;
+ dcc->check = GG_CHECK_WRITE;
+ dcc->timeout = GG_DEFAULT_TIMEOUT;
+ dcc->incoming = 0;
+
+ return e;
+ }
+
+ case GG_STATE_READING_ID:
+ {
+ int res;
+
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_ID\n");
+
+ if (!dcc->relay) {
+ struct gg_dcc7_welcome_p2p welcome, welcome_ok;
+ welcome_ok.id = dcc->cid;
+
+ if ((res = gg_sock_read(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+
+ if (memcmp(&welcome, &welcome_ok, sizeof(welcome))) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+ } else {
+ struct gg_dcc7_welcome_server welcome, welcome_ok;
+ welcome_ok.magic = GG_DCC7_WELCOME_SERVER;
+ welcome_ok.id = dcc->cid;
+
+ if ((res = gg_sock_read(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+
+ if (memcmp(&welcome, &welcome_ok, sizeof(welcome)) != 0) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() invalid id\n");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_SENDING_ID\n");
+
+ if (!dcc->relay) {
+ struct gg_dcc7_welcome_p2p welcome;
+
+ welcome.id = dcc->cid;
+
+ if ((res = gg_sock_write(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)", res, strerror(errno));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+ } else {
+ struct gg_dcc7_welcome_server welcome;
+
+ welcome.magic = gg_fix32(GG_DCC7_WELCOME_SERVER);
+ welcome.id = dcc->cid;
+
+ if ((res = gg_sock_write(dcc->fd, &welcome, sizeof(welcome))) != sizeof(welcome)) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%d, %s)\n", res, strerror(errno));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ 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_session((dcc) ? (dcc)->sess : NULL, 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() offset >= size, finished\n");
+ e->type = GG_EVENT_DCC7_DONE;
+ e->event.dcc7_done.dcc7 = dcc;
+ return e;
+ }
+
+ if (dcc->seek && lseek(dcc->file_fd, dcc->offset, SEEK_SET) == (off_t) -1) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() lseek() failed (%s)\n", strerror(errno));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_FILE;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+
+ if ((chunk = dcc->size - dcc->offset) > sizeof(buf))
+ chunk = sizeof(buf);
+
+ if ((res = read(dcc->file_fd, buf, chunk)) < 1) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (res=%d, %s)\n", res, strerror(errno));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = (res == -1) ? GG_ERROR_DCC7_FILE : GG_ERROR_DCC7_EOF;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+
+ if ((res = gg_sock_write(dcc->fd, buf, res)) == -1) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() write() failed (%s)\n", strerror(errno));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_NET;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+
+ dcc->offset += res;
+
+ if (dcc->offset >= dcc->size) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+ e->type = GG_EVENT_DCC7_DONE;
+ e->event.dcc7_done.dcc7 = dcc;
+ return e;
+ }
+
+ dcc->state = GG_STATE_SENDING_FILE;
+ dcc->check = GG_CHECK_WRITE;
+ dcc->timeout = GG_DCC7_TIMEOUT_SEND;
+
+ return e;
+ }
+
+ case GG_STATE_GETTING_FILE:
+ {
+ char buf[1024];
+ int res, wres;
+
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+ e->type = GG_EVENT_DCC7_DONE;
+ e->event.dcc7_done.dcc7 = dcc;
+ return e;
+ }
+
+ if ((res = gg_sock_read(dcc->fd, buf, sizeof(buf))) < 1) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, 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.dcc7_error = (res == -1) ? GG_ERROR_DCC7_NET : GG_ERROR_DCC7_EOF;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+
+ // XXX zapisywać do skutku?
+
+ if ((wres = write(dcc->file_fd, buf, res)) < res) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, 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.dcc7_error = GG_ERROR_DCC7_FILE;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+
+ dcc->offset += res;
+
+ if (dcc->offset >= dcc->size) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() finished\n");
+ e->type = GG_EVENT_DCC7_DONE;
+ e->event.dcc7_done.dcc7 = dcc;
+ return e;
+ }
+
+ dcc->state = GG_STATE_GETTING_FILE;
+ dcc->check = GG_CHECK_READ;
+ dcc->timeout = GG_DCC7_TIMEOUT_GET;
+
+ return e;
+ }
+
+ case GG_STATE_RESOLVING_RELAY:
+ {
+ struct in_addr addr;
+
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_RESOLVING_RELAY\n");
+
+ if (gg_sock_read(dcc->fd, &addr, sizeof(addr)) < sizeof(addr) || addr.s_addr == INADDR_NONE) {
+ int errno_save = errno;
+
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolving failed\n");
+ gg_sock_close(dcc->fd);
+ dcc->fd = -1;
+ dcc->sess->resolver_cleanup(&dcc->resolver, 0);
+ errno = errno_save;
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() resolved, connecting to %s:%d\n", inet_ntoa(addr), GG_RELAY_PORT);
+
+ if ((dcc->fd = gg_connect(&addr, GG_RELAY_PORT, 1)) == -1) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s), critical\n", errno, strerror(errno));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+
+ dcc->state = GG_STATE_CONNECTING_RELAY;
+ dcc->check = GG_CHECK_WRITE;
+ dcc->timeout = GG_DEFAULT_TIMEOUT;
+
+ return e;
+ }
+
+ case GG_STATE_CONNECTING_RELAY:
+ {
+ int res;
+ unsigned int res_size = sizeof(res);
+ struct gg_dcc7_relay_req pkt;
+
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_CONNECTING_RELAY\n");
+
+ if (gg_getsockopt(dcc->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) != 0 || res != 0) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+
+ memset(&pkt, 0, sizeof(pkt));
+ pkt.magic = gg_fix32(GG_DCC7_RELAY_REQUEST);
+ pkt.len = gg_fix32(sizeof(pkt));
+ pkt.id = dcc->cid;
+ pkt.type = gg_fix16(GG_DCC7_RELAY_TYPE_SERVER);
+ pkt.dunno1 = gg_fix16(GG_DCC7_RELAY_DUNNO1);
+
+ gg_debug_dump_session((dcc) ? (dcc)->sess : NULL, &pkt, sizeof(pkt), "// gg_dcc7_watch_fd() send pkt(0x%.2x)\n", gg_fix32(pkt.magic));
+
+ if ((res = gg_sock_write(dcc->fd, &pkt, sizeof(pkt))) != sizeof(pkt)) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() sending failed\n");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+
+ dcc->state = GG_STATE_READING_RELAY;
+ dcc->check = GG_CHECK_READ;
+ dcc->timeout = GG_DEFAULT_TIMEOUT;
+
+ return e;
+ }
+
+ case GG_STATE_READING_RELAY:
+ {
+ char buf[256];
+ struct gg_dcc7_relay_reply *pkt;
+ struct gg_dcc7_relay_reply_server srv;
+ int res;
+ int i;
+
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_READING_RELAY\n");
+
+ if ((res = gg_sock_read(dcc->fd, buf, sizeof(buf))) < sizeof(*pkt)) {
+ if (res == 0)
+ errno = ECONNRESET;
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() read() failed (%d, %s)\n", res, strerror(errno));
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+
+ pkt = (struct gg_dcc7_relay_reply*) buf;
+
+ if (gg_fix32(pkt->magic) != GG_DCC7_RELAY_REPLY || gg_fix32(pkt->rcount) < 1 || gg_fix32(pkt->rcount > 256) || gg_fix32(pkt->len) < sizeof(*pkt) + gg_fix32(pkt->rcount) * sizeof(srv)) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_wathc_fd() invalid reply\n");
+ errno = EINVAL;
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+
+ gg_debug_dump_session((dcc) ? (dcc)->sess : NULL, buf, res, "// gg_dcc7_get_relay() read pkt(0x%.2x)\n", gg_fix32(pkt->magic));
+
+ free(dcc->relay_list);
+
+ dcc->relay_index = 0;
+ dcc->relay_count = gg_fix32(pkt->rcount);
+ dcc->relay_list = malloc(dcc->relay_count * sizeof(gg_dcc7_relay_t));
+
+ if (dcc->relay_list == NULL) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() not enough memory");
+ dcc->relay_count = 0;
+ free(e);
+ return NULL;
+ }
+
+ for (i = 0; i < dcc->relay_count; i++) {
+ struct in_addr addr;
+
+ memcpy(&srv, buf + sizeof(*pkt) + i * sizeof(srv), sizeof(srv));
+ dcc->relay_list[i].addr = srv.addr;
+ dcc->relay_list[i].port = gg_fix16(srv.port);
+ dcc->relay_list[i].family = srv.family;
+
+ addr.s_addr = srv.addr;
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// %s %d %d\n", inet_ntoa(addr), gg_fix16(srv.port), srv.family);
+ }
+
+ dcc->relay = 1;
+
+ for (; dcc->relay_index < dcc->relay_count; dcc->relay_index++) {
+ dcc->remote_addr = dcc->relay_list[dcc->relay_index].addr;
+ dcc->remote_port = dcc->relay_list[dcc->relay_index].port;
+
+ if (gg_dcc7_connect(dcc) == 0)
+ break;
+ }
+
+ if (dcc->relay_index >= dcc->relay_count) {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() no relay available");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_RELAY;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+ return e;
+ }
+
+ return e;
+ }
+
+ default:
+ {
+ gg_debug_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_MISC, "// gg_dcc7_watch_fd() GG_STATE_???\n");
+ e->type = GG_EVENT_DCC7_ERROR;
+ e->event.dcc7_error = GG_ERROR_DCC7_HANDSHAKE;
+ e->event.dcc7_error_ex.dcc7 = dcc;
+
+ 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_session((dcc) ? (dcc)->sess : NULL, GG_DEBUG_FUNCTION, "** gg_dcc7_free(%p)\n", dcc);
+
+ if (!dcc)
+ return;
+
+ if (dcc->fd != -1)
+ gg_sock_close(dcc->fd);
+
+ if (dcc->file_fd != -1)
+ close(dcc->file_fd);
+
+ if (dcc->sess)
+ gg_dcc7_session_remove(dcc->sess, dcc);
+
+ free(dcc->relay_list);
+
+ free(dcc);
+}
+
diff --git a/protocols/Gadu-Gadu/libgadu/events.c b/protocols/Gadu-Gadu/libgadu/events.c
new file mode 100644
index 0000000000..a730e9d620
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/events.c
@@ -0,0 +1,2864 @@
+/* coding: UTF-8 */
+/* $Id: events.c 13583 2011-04-12 12:51:18Z dezred $ */
+
+/*
+ * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Robert J. WoĹşny <speedy@ziew.org>
+ * Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ * Adam Wysocki <gophi@ekg.chmurka.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/**
+ * \file events.c
+ *
+ * \brief Obsługa zdarzeń
+ */
+
+#ifndef _WIN64
+#define _USE_32BIT_TIME_T
+#endif
+
+#include <sys/types.h>
+#ifdef _WIN32
+#include "win32.h"
+#else
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif /* _WIN32 */
+
+#include "compat.h"
+#include "libgadu.h"
+#include "protocol.h"
+#include "internal.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <time.h>
+#ifndef _WIN32
+#include <unistd.h>
+#include <ctype.h>
+#endif /* _WIN32 */
+#ifndef GG_CONFIG_MIRANDA
+#ifdef GG_CONFIG_HAVE_OPENSSL
+# include <openssl/err.h>
+# include <openssl/x509.h>
+#endif
+#endif
+
+/**
+ * Zwalnia pamięć zajmowaną przez informację o zdarzeniu.
+ *
+ * Funkcję należy wywoływać za każdym razem gdy funkcja biblioteki zwróci
+ * strukturÄ™ \c gg_event.
+ *
+ * \param e Struktura zdarzenia
+ *
+ * \ingroup events
+ */
+void gg_event_free(struct gg_event *e)
+{
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_event_free(%p);\n", e);
+
+ if (!e)
+ return;
+
+ switch (e->type) {
+ case GG_EVENT_MSG:
+ case GG_EVENT_MULTILOGON_MSG:
+ free(e->event.msg.message);
+ free(e->event.msg.formats);
+ free(e->event.msg.recipients);
+ free(e->event.msg.xhtml_message);
+ break;
+
+ case GG_EVENT_NOTIFY:
+ free(e->event.notify);
+ break;
+
+ case GG_EVENT_NOTIFY60:
+ {
+ int i;
+
+ for (i = 0; e->event.notify60[i].uin; i++)
+ free(e->event.notify60[i].descr);
+
+ free(e->event.notify60);
+
+ break;
+ }
+
+ case GG_EVENT_STATUS60:
+ free(e->event.status60.descr);
+ break;
+
+ case GG_EVENT_STATUS:
+ free(e->event.status.descr);
+ break;
+
+ case GG_EVENT_NOTIFY_DESCR:
+ free(e->event.notify_descr.notify);
+ free(e->event.notify_descr.descr);
+ break;
+
+ case GG_EVENT_DCC_VOICE_DATA:
+ free(e->event.dcc_voice_data.data);
+ break;
+
+ case GG_EVENT_PUBDIR50_SEARCH_REPLY:
+ case GG_EVENT_PUBDIR50_READ:
+ case GG_EVENT_PUBDIR50_WRITE:
+ gg_pubdir50_free(e->event.pubdir50);
+ break;
+
+ case GG_EVENT_USERLIST:
+ free(e->event.userlist.reply);
+ break;
+
+ case GG_EVENT_IMAGE_REPLY:
+ free(e->event.image_reply.filename);
+ free(e->event.image_reply.image);
+ break;
+
+ case GG_EVENT_XML_EVENT:
+ free(e->event.xml_event.data);
+ break;
+
+ case GG_EVENT_XML_ACTION:
+ free(e->event.xml_action.data);
+ break;
+
+ case GG_EVENT_USER_DATA:
+ {
+ unsigned i, j;
+
+ for (i = 0; i < e->event.user_data.user_count; i++) {
+ for (j = 0; j < e->event.user_data.users[i].attr_count; j++) {
+ free(e->event.user_data.users[i].attrs[j].key);
+ free(e->event.user_data.users[i].attrs[j].value);
+ }
+
+ free(e->event.user_data.users[i].attrs);
+ }
+
+ free(e->event.user_data.users);
+
+ break;
+ }
+
+ case GG_EVENT_MULTILOGON_INFO:
+ {
+ int i;
+
+ for (i = 0; i < e->event.multilogon_info.count; i++)
+ free(e->event.multilogon_info.sessions[i].name);
+
+ free(e->event.multilogon_info.sessions);
+
+ break;
+ }
+ }
+
+ free(e);
+}
+
+/** \cond internal */
+
+/**
+ * \internal Usuwa obrazek z kolejki do wysłania.
+ *
+ * \param s Struktura sesji
+ * \param q Struktura obrazka
+ * \param freeq Flaga zwolnienia elementu kolejki
+ *
+ * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
+ */
+int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq)
+{
+ if (!s || !q) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (s->images == q)
+ s->images = q->next;
+ else {
+ struct gg_image_queue *qq;
+
+ for (qq = s->images; qq; qq = qq->next) {
+ if (qq->next == q) {
+ qq->next = q->next;
+ break;
+ }
+ }
+ }
+
+ if (freeq) {
+ free(q->image);
+ free(q->filename);
+ free(q);
+ }
+
+ return 0;
+}
+
+/**
+ * \internal Analizuje przychodzÄ…cy pakiet z obrazkiem.
+ *
+ * \param e Struktura zdarzenia
+ * \param p Bufor z danymi
+ * \param len Długość bufora
+ * \param sess Struktura sesji
+ * \param sender Numer nadawcy
+ * \param size Rozmiar pliku (z nagłówka)
+ * \param crc32 Suma kontrolna (z nagłówka)
+ */
+static void gg_image_queue_parse(struct gg_event *e, char *p, unsigned int len, struct gg_session *sess, uin_t sender, uint32_t size, uint32_t crc32)
+{
+ struct gg_image_queue *q, *qq;
+
+ if (!p || !sess || !e) {
+ errno = EFAULT;
+ return;
+ }
+
+ /* znajdĹş dany obrazek w kolejce danej sesji */
+
+ for (qq = sess->images, q = NULL; qq; qq = qq->next) {
+ if (sender == qq->sender && size == qq->size && crc32 == qq->crc32) {
+ q = qq;
+ break;
+ }
+ }
+
+ if (!q) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() unknown image from %d, size=%d, crc32=%.8x\n", sender, size, crc32);
+ return;
+ }
+
+ if (p[0] == 0x05) {
+ q->done = 0;
+
+ len -= sizeof(struct gg_msg_image_reply);
+ p += sizeof(struct gg_msg_image_reply);
+
+ if (memchr(p, 0, len) == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() malformed packet from %d, unlimited filename\n", sender);
+ return;
+ }
+
+ if (!(q->filename = strdup(p))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_queue_parse() not enough memory for filename\n");
+ return;
+ }
+
+ len -= (unsigned int)strlen(p) + 1;
+ p += strlen(p) + 1;
+ } else {
+ len -= sizeof(struct gg_msg_image_reply);
+ p += sizeof(struct gg_msg_image_reply);
+ }
+
+ if (q->done + len > q->size)
+ len = q->size - q->done;
+
+ memcpy(q->image + q->done, p, len);
+ q->done += len;
+
+ /* jeśli skończono odbierać obrazek, wygeneruj zdarzenie */
+
+ if (q->done >= q->size) {
+ e->type = GG_EVENT_IMAGE_REPLY;
+ e->event.image_reply.sender = sender;
+ e->event.image_reply.size = q->size;
+ e->event.image_reply.crc32 = q->crc32;
+ e->event.image_reply.filename = q->filename;
+ e->event.image_reply.image = q->image;
+
+ gg_image_queue_remove(sess, q, 0);
+
+ free(q);
+ }
+}
+
+/**
+ * \internal Analizuje informacje rozszerzone wiadomości.
+ *
+ * \param sess Struktura sesji.
+ * \param e Struktura zdarzenia.
+ * \param sender Numer nadawcy.
+ * \param p WskaĹşnik na dane rozszerzone.
+ * \param packet_end WskaĹşnik na koniec pakietu.
+ *
+ * \return 0 jeśli się powiodło, -1 jeśli wiadomość obsłużono i wynik ma
+ * zostać przekazany aplikacji, -2 jeśli wystąpił błąd ogólny, -3 jeśli
+ * wiadomość jest niepoprawna.
+ */
+static int gg_handle_recv_msg_options(struct gg_session *sess, struct gg_event *e, uin_t sender, char *p, char *packet_end)
+{
+ while (p < packet_end) {
+ switch (*p) {
+ case 0x01: /* konferencja */
+ {
+ struct gg_msg_recipients *m = (void*) p;
+ uint32_t i, count;
+
+ if (p + sizeof(*m) > packet_end) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (1)\n");
+ goto malformed;
+ }
+
+ memcpy(&count, &m->count, sizeof(count));
+ count = gg_fix32(count);
+ p += sizeof(*m);
+
+ if (p + count * sizeof(uin_t) > packet_end || p + count * sizeof(uin_t) < p || count > 0xffff) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (1.5)\n");
+ goto malformed;
+ }
+
+ if (e->event.msg.recipients != NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() e->event.msg.recipients already exist\n");
+ goto malformed;
+ }
+
+ e->event.msg.recipients = malloc(count * sizeof(uin_t));
+
+ if (e->event.msg.recipients == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() not enough memory for recipients data\n");
+ goto fail;
+ }
+
+ memcpy(e->event.msg.recipients, p, count * sizeof(uin_t));
+ p += count * sizeof(uin_t);
+
+ for (i = 0; i < count; i++)
+ e->event.msg.recipients[i] = gg_fix32(e->event.msg.recipients[i]);
+
+ e->event.msg.recipients_count = count;
+
+ break;
+ }
+
+ case 0x02: /* richtext */
+ {
+ uint16_t len;
+ char *buf;
+
+ if (p + 3 > packet_end) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (2)\n");
+ goto malformed;
+ }
+
+ memcpy(&len, p + 1, sizeof(uint16_t));
+ len = gg_fix16(len);
+
+ if (e->event.msg.formats != NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() e->event.msg.formats already exist\n");
+ goto malformed;
+ }
+
+ buf = malloc(len);
+
+ if (buf == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() not enough memory for richtext data\n");
+ goto fail;
+ }
+
+ p += 3;
+
+ if (p + len > packet_end) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (3)\n");
+ free(buf);
+ goto malformed;
+ }
+
+ memcpy(buf, p, len);
+
+ e->event.msg.formats = buf;
+ e->event.msg.formats_length = len;
+
+ p += len;
+
+ break;
+ }
+
+ case 0x04: /* image_request */
+ {
+ struct gg_msg_image_request *i = (void*) p;
+
+ if (p + sizeof(*i) > packet_end) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (3.5)\n");
+ goto malformed;
+ }
+
+ if (e->event.msg.formats != NULL || e->event.msg.recipients != NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() mixed options (1)\n");
+ goto malformed;
+ }
+
+ memcpy(&e->event.image_request.size, &i->size, sizeof(i->size));
+ memcpy(&e->event.image_request.crc32, &i->crc32, sizeof(i->crc32));
+
+ e->event.image_request.sender = sender;
+ e->event.image_request.size = gg_fix32(e->event.image_request.size);
+ e->event.image_request.crc32 = gg_fix32(e->event.image_request.crc32);
+
+ e->type = GG_EVENT_IMAGE_REQUEST;
+
+ goto handled;
+ }
+
+ case 0x05: /* image_reply */
+ case 0x06:
+ {
+ struct gg_msg_image_reply *rep = (void*) p;
+ uint32_t size;
+ uint32_t crc32;
+
+ if (e->event.msg.formats != NULL || e->event.msg.recipients != NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() mixed options (2)\n");
+ goto malformed;
+ }
+
+ if (p + sizeof(struct gg_msg_image_reply) + 1 > packet_end) {
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() packet out of bounds (4)\n");
+ goto malformed;
+ }
+
+ memcpy(&size, &rep->size, sizeof(size));
+ memcpy(&crc32, &rep->crc32, sizeof(crc32));
+ size = gg_fix32(size);
+ crc32 = gg_fix32(crc32);
+
+ if (p + sizeof(struct gg_msg_image_reply) == packet_end) {
+ /* pusta odpowiedĹş - klient po drugiej stronie nie ma ĹĽÄ…danego obrazka */
+
+ e->type = GG_EVENT_IMAGE_REPLY;
+ e->event.image_reply.sender = sender;
+ e->event.image_reply.size = 0;
+ e->event.image_reply.crc32 = crc32;
+ e->event.image_reply.filename = NULL;
+ e->event.image_reply.image = NULL;
+ goto handled;
+
+ }
+
+ gg_image_queue_parse(e, p, (unsigned int)(packet_end - p), sess, sender, size, crc32);
+
+ goto handled;
+ }
+
+ default:
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg_options() 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;
+ int ctcp = 0;
+
+ 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");
+ ctcp = 1;
+ 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.seq = gg_fix32(r->seq);
+ if (ctcp)
+ e->event.msg.message = (unsigned char*) strdup("\x02");
+ else
+ 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;
+}
+
+/**
+ * \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.
+ *
+ * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
+ */
+static int gg_convert_from_html(char *dst, const char *html)
+{
+ const char *src, *entity, *tag;
+ int len, in_tag, in_entity;
+
+ len = 0;
+ in_tag = 0;
+ tag = NULL;
+ in_entity = 0;
+ entity = NULL;
+
+ for (src = html; *src != 0; src++) {
+ if (*src == '<') {
+ tag = src;
+ in_tag = 1;
+ continue;
+ }
+
+ if (in_tag && (*src == '>')) {
+ if (strncmp(tag, "<br", 3) == 0) {
+ if (dst != NULL)
+ dst[len] = '\n';
+ len++;
+ }
+ in_tag = 0;
+ continue;
+ }
+
+ if (in_tag)
+ continue;
+
+ if (*src == '&') {
+ in_entity = 1;
+ entity = src;
+ continue;
+ }
+
+ if (in_entity && *src == ';') {
+ in_entity = 0;
+ if (dst != NULL) {
+ if (strncmp(entity, "&lt;", 4) == 0)
+ dst[len] = '<';
+ else if (strncmp(entity, "&gt;", 4) == 0)
+ dst[len] = '>';
+ else if (strncmp(entity, "&quot;", 6) == 0)
+ dst[len] = '"';
+ else if (strncmp(entity, "&apos;", 6) == 0)
+ dst[len] = '\'';
+ else if (strncmp(entity, "&amp;", 5) == 0)
+ dst[len] = '&';
+ else
+ dst[len] = '?';
+ }
+ len++;
+ continue;
+ }
+
+ if (in_entity && !(isalnum(*src) || *src == '#'))
+ in_entity = 0;
+
+ if (in_entity)
+ continue;
+
+ if (dst != NULL)
+ dst[len] = *src;
+
+ len++;
+ }
+
+ if (dst != NULL)
+ dst[len] = 0;
+
+ return len;
+}
+
+/**
+ * \internal Analizuje przychodzący pakiet z wiadomością protokołu Gadu-Gadu 8.0.
+ *
+ * Rozbija pakiet na poszczególne składniki -- tekst, informacje
+ * o konferencjach, formatowani itd.
+ *
+ * \param h WskaĹşnik do odebranego pakietu
+ * \param e Struktura zdarzenia
+ * \param sess Struktura sesji
+ * \param event Typ zdarzenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_handle_recv_msg80(struct gg_header *h, struct gg_event *e, struct gg_session *sess, int event)
+{
+ char *packet = (char*) h + sizeof(struct gg_header);
+ struct gg_recv_msg80 *r = (struct gg_recv_msg80*) packet;
+ uint32_t offset_plain;
+ uint32_t offset_attr;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg80(%p, %p);\n", h, e);
+
+ if (!r->seq && !r->msgclass) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() oops, silently ignoring the bait\n");
+ goto malformed;
+ }
+
+ offset_plain = gg_fix32(r->offset_plain);
+ offset_attr = gg_fix32(r->offset_attr);
+
+ if (offset_plain < sizeof(struct gg_recv_msg80) || offset_plain >= h->length) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (0)\n");
+ goto malformed;
+ }
+
+ if (offset_attr < sizeof(struct gg_recv_msg80) || offset_attr > h->length) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, attr out of bounds (1)\n");
+ offset_attr = 0; /* nie parsuj attr. */
+ /* goto ignore; */
+ }
+
+ /* Normalna sytuacja, więc nie podpada pod powyższy warunek. */
+ if (offset_attr == h->length)
+ offset_attr = 0;
+
+ if (memchr(packet + offset_plain, 0, h->length - offset_plain) == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (2)\n");
+ goto malformed;
+ }
+
+ if (offset_plain > sizeof(struct gg_recv_msg80) && memchr(packet + sizeof(struct gg_recv_msg80), 0, offset_plain - sizeof(struct gg_recv_msg80)) == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_recv_msg80() malformed packet, message out of bounds (3)\n");
+ goto malformed;
+ }
+
+ e->type = event;
+ e->event.msg.msgclass = gg_fix32(r->msgclass);
+ e->event.msg.sender = gg_fix32(r->sender);
+ e->event.msg.time = gg_fix32(r->time);
+ e->event.msg.seq = gg_fix32(r->seq);
+
+ if (offset_attr != 0) {
+ switch (gg_handle_recv_msg_options(sess, e, gg_fix32(r->sender), packet + offset_attr, packet + h->length)) {
+ case -1: // handled
+ return 0;
+
+ case -2: // failed
+ goto fail;
+
+ case -3: // malformed
+ goto malformed;
+ }
+ }
+
+ if (sess->encoding == GG_ENCODING_CP1250) {
+ e->event.msg.message = (unsigned char*) strdup(packet + offset_plain);
+ } 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;
+ }
+
+ 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 Wysyła potwierdzenie odebrania wiadomości.
+ *
+ * \param sess Struktura sesji
+ *
+ * \return 0 jeśli się powiodło, -1 jeśli wystąpił błąd
+ */
+static int gg_handle_recv_msg_ack(struct gg_header *h, struct gg_session *sess)
+{
+ char *packet = (char*) h + sizeof(struct gg_header);
+ struct gg_recv_msg80 *r = (struct gg_recv_msg80*) packet;
+ struct gg_recv_msg_ack pkt;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_recv_msg_ack(%p);\n", sess);
+
+ if ((sess->protocol_features & GG_FEATURE_MSG_ACK) == 0)
+ return 0;
+
+ pkt.seq = gg_fix32(r->seq);
+
+ return gg_send_packet(sess, GG_RECV_MSG_ACK, &pkt, sizeof(pkt), NULL);
+}
+
+/**
+ * \internal Analizuje przychodzÄ…cy pakiet z danymi kontaktĂłw.
+ *
+ * \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
+ */
+static int gg_handle_user_data(struct gg_session *sess, struct gg_event *e, void *packet, size_t len)
+{
+ struct gg_user_data d;
+ char *p = (char*) packet;
+ char *packet_end = (char*) packet + len;
+ struct gg_event_user_data_user *users;
+ unsigned i, j;
+ int res = 0;
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "** gg_handle_user_data(%p, %p, %p, %d);\n", sess, e, packet, len);
+
+ e->event.user_data.user_count = 0;
+ e->event.user_data.users = NULL;
+
+ if (p + sizeof(d) > packet_end)
+ goto malformed;
+
+ memcpy(&d, p, sizeof(d));
+ p += sizeof(d);
+
+ d.type = gg_fix32(d.type);
+ d.user_count = gg_fix32(d.user_count);
+
+ if (d.user_count > 0xffff) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (1)\n");
+ goto malformed;
+ }
+
+ if (d.user_count > 0) {
+ users = calloc(d.user_count, sizeof(struct gg_event_user_data_user));
+
+ if (users == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() out of memory (%d*%d)\n", d.user_count, sizeof(struct gg_event_user_data_user));
+ goto fail;
+ }
+ } else {
+ users = NULL;
+ }
+
+ e->type = GG_EVENT_USER_DATA;
+ e->event.user_data.type = d.type;
+ e->event.user_data.user_count = d.user_count;
+ e->event.user_data.users = users;
+
+ gg_debug_session(sess, GG_DEBUG_DUMP, "type=%d, count=%d\n", d.type, d.user_count);
+
+ for (i = 0; i < d.user_count; i++) {
+ struct gg_user_data_user u;
+ struct gg_event_user_data_attr *attrs;
+
+ if (p + sizeof(u) > packet_end) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (2)\n");
+ goto malformed;
+ }
+
+ memcpy(&u, p, sizeof(u));
+ p += sizeof(u);
+
+ u.uin = gg_fix32(u.uin);
+ u.attr_count = gg_fix32(u.attr_count);
+
+ if (u.attr_count > 0xffff) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (2)\n");
+ goto malformed;
+ }
+
+ if (u.attr_count > 0) {
+ attrs = calloc(u.attr_count, sizeof(struct gg_event_user_data_attr));
+
+ if (attrs == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() out of memory (%d*%d)\n", u.attr_count, sizeof(struct gg_event_user_data_attr));
+ goto fail;
+ }
+ } else {
+ attrs = NULL;
+ }
+
+ users[i].uin = u.uin;
+ users[i].attr_count = u.attr_count;
+ users[i].attrs = attrs;
+
+ gg_debug_session(sess, GG_DEBUG_DUMP, " uin=%d, count=%d\n", u.uin, u.attr_count);
+
+ for (j = 0; j < u.attr_count; j++) {
+ uint32_t key_size;
+ uint32_t attr_type;
+ uint32_t value_size;
+ char *key;
+ char *value;
+
+ if (p + sizeof(key_size) > packet_end) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (3)\n");
+ goto malformed;
+ }
+
+ memcpy(&key_size, p, sizeof(key_size));
+ p += sizeof(key_size);
+
+ key_size = gg_fix32(key_size);
+
+ if (key_size > 0xffff || p + key_size > packet_end) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (3)\n");
+ goto malformed;
+ }
+
+ key = malloc(key_size + 1);
+
+ if (key == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() out of memory (%d)\n", key_size + 1);
+ goto fail;
+ }
+
+ memcpy(key, p, key_size);
+ p += key_size;
+
+ key[key_size] = 0;
+
+ attrs[j].key = key;
+
+ if (p + sizeof(attr_type) + sizeof(value_size) > packet_end) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (4)\n");
+ goto malformed;
+ }
+
+ memcpy(&attr_type, p, sizeof(attr_type));
+ p += sizeof(attr_type);
+ memcpy(&value_size, p, sizeof(value_size));
+ p += sizeof(value_size);
+
+ attrs[j].type = gg_fix32(attr_type);
+ value_size = gg_fix32(value_size);
+
+ if (value_size > 0xffff || p + value_size > packet_end) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() malformed packet (5)\n");
+ goto malformed;
+ }
+
+ value = malloc(value_size + 1);
+
+ if (value == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_user_data() out of memory (%d)\n", value_size + 1);
+ goto fail;
+ }
+
+ memcpy(value, p, value_size);
+ p += value_size;
+
+ value[value_size] = 0;
+
+ attrs[j].value = value;
+
+ gg_debug_session(sess, GG_DEBUG_DUMP, " key=\"%s\", type=%d, value=\"%s\"\n", key, attr_type, value);
+ }
+ }
+
+ return 0;
+
+fail:
+ res = -1;
+
+malformed:
+ e->type = GG_EVENT_NONE;
+
+ for (i = 0; i < e->event.user_data.user_count; i++) {
+ for (j = 0; j < e->event.user_data.users[i].attr_count; j++) {
+ free(e->event.user_data.users[i].attrs[j].key);
+ free(e->event.user_data.users[i].attrs[j].value);
+ }
+
+ free(e->event.user_data.users[i].attrs);
+ }
+
+ free(e->event.user_data.users);
+
+ return res;
+}
+
+/**
+ * \internal Analizuje przychodzÄ…cy pakiet z listÄ… sesji multilogowania.
+ *
+ * \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
+ */
+static int gg_handle_multilogon_info(struct gg_session *sess, struct gg_event *e, void *packet, size_t len)
+{
+ char *packet_end = (char*) packet + len;
+ struct gg_multilogon_info *info = (struct gg_multilogon_info*) packet;
+ char *p = (char*) packet + sizeof(*info);
+ struct gg_multilogon_session *sessions = NULL;
+ size_t count;
+ size_t i;
+ int res = 0;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_handle_multilogon_info(%p, %p, %p, %d);\n", sess, e, packet, len);
+
+ count = gg_fix32(info->count);
+
+ if (count > 0xffff) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (1)\n");
+ goto malformed;
+ }
+
+ sessions = calloc(count, sizeof(struct gg_multilogon_session));
+
+ if (sessions == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_multilogon_info() out of memory (%d*%d)\n", count, sizeof(struct gg_multilogon_session));
+ return -1;
+ }
+
+ e->type = GG_EVENT_MULTILOGON_INFO;
+ e->event.multilogon_info.count = (int)count;
+ e->event.multilogon_info.sessions = sessions;
+
+ for (i = 0; i < count; i++) {
+ struct gg_multilogon_info_item item;
+ size_t name_size;
+
+ if (p + sizeof(item) > packet_end) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (2)\n");
+ goto malformed;
+ }
+
+ memcpy(&item, p, sizeof(item));
+
+ sessions[i].id = item.conn_id;
+ sessions[i].remote_addr = item.addr;
+ sessions[i].status_flags = gg_fix32(item.flags);
+ sessions[i].protocol_features = gg_fix32(item.features);
+ sessions[i].logon_time = gg_fix32(item.logon_time);
+
+ p += sizeof(item);
+
+ name_size = gg_fix32(item.name_size);
+
+ if (name_size > 0xffff || p + name_size > packet_end) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_multilogon_info() malformed packet (3)\n");
+ goto malformed;
+ }
+
+ sessions[i].name = malloc(name_size + 1);
+
+ if (sessions[i].name == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_handle_multilogon_info() out of memory (%d)\n", name_size);
+ goto fail;
+ }
+
+ memcpy(sessions[i].name, p, name_size);
+ sessions[i].name[name_size] = 0;
+
+ p += name_size;
+ }
+
+ return 0;
+
+fail:
+ res = -1;
+
+malformed:
+ e->type = GG_EVENT_NONE;
+
+ for (i = 0; i < (size_t)e->event.multilogon_info.count; i++)
+ free(e->event.multilogon_info.sessions[i].name);
+
+ free(e->event.multilogon_info.sessions);
+
+ return res;
+}
+
+/**
+ * \internal Odbiera pakiet od serwera.
+ *
+ * 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_session(sess, GG_DEBUG_FUNCTION, "** gg_watch_fd_connected(%p, %p);\n", sess, e);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (!(h = gg_recv_packet(sess))) {
+ 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) != -1)
+ gg_handle_recv_msg_ack(h, sess);
+ else
+ goto fail;
+ }
+
+ break;
+ }
+
+ case GG_RECV_MSG80:
+ {
+ if (h->length >= sizeof(struct gg_recv_msg80)) {
+ if (gg_handle_recv_msg80(h, e, sess, GG_EVENT_MSG) != -1)
+ gg_handle_recv_msg_ack(h, sess);
+ else
+ goto fail;
+ }
+
+ break;
+ }
+
+ case GG_RECV_OWN_MSG:
+ {
+ if (h->length >= sizeof(struct gg_recv_msg80)) {
+ if (gg_handle_recv_msg80(h, e, sess, GG_EVENT_MULTILOGON_MSG) == -1)
+ goto fail;
+ }
+
+ break;
+ }
+
+ case GG_NOTIFY_REPLY:
+ {
+ struct gg_notify_reply *n = (void*) p;
+ unsigned int count, i;
+ char *tmp;
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a notify reply\n");
+
+ if (h->length < sizeof(*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_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_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_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_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_port = gg_fix16(e->event.notify[i].remote_port);
+ e->event.notify[i].version = gg_fix32(e->event.notify[i].version);
+ }
+ }
+
+ break;
+ }
+
+ case GG_STATUS:
+ {
+ struct gg_status *s = (void*) p;
+
+ 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;
+ memcpy(&e->event.status, p, sizeof(*s));
+ e->event.status.uin = gg_fix32(e->event.status.uin);
+ e->event.status.status = gg_fix32(e->event.status.status);
+ if (h->length > sizeof(*s)) {
+ int len = h->length - sizeof(*s);
+ char *buf = malloc(len + 1);
+ if (buf) {
+ memcpy(buf, p + sizeof(*s), len);
+ buf[len] = 0;
+ }
+ e->event.status.descr = buf;
+ } else
+ e->event.status.descr = NULL;
+ }
+
+ break;
+ }
+
+ case GG_NOTIFY_REPLY77:
+ case GG_NOTIFY_REPLY80BETA:
+ {
+ struct gg_notify_reply77 *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_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;
+
+ 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 & 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_reply60));
+
+ if (sizeof(struct gg_notify_reply60) + descr_len <= length) {
+ if (!(e->event.notify60[i].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(e->event.notify60[i].descr, (char*) n + sizeof(struct gg_notify_reply60) + 1, descr_len);
+ 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;
+ }
+
+ } 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_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_STATUS60:
+ {
+ struct gg_status60 *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 & 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);
+
+ if (buf) {
+ memcpy(buf, (char*) p + sizeof(*s), len);
+ buf[len] = 0;
+ }
+
+ 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_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;
+ 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_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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a message ack\n");
+
+ if (h->length < sizeof(*s))
+ break;
+
+ e->type = GG_EVENT_ACK;
+ e->event.ack.status = gg_fix32(s->status);
+ e->event.ack.recipient = gg_fix32(s->recipient);
+ e->event.ack.seq = gg_fix32(s->seq);
+
+ break;
+ }
+
+ case GG_PONG:
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received a pong\n");
+
+ e->type = GG_EVENT_PONG;
+ sess->last_pong = (int)time(NULL);
+
+ break;
+ }
+
+ case GG_DISCONNECTING:
+ {
+ 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_XML_ACTION:
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received XML action\n");
+ e->type = GG_EVENT_XML_ACTION;
+ if (!(e->event.xml_action.data = (char *) malloc(h->length + 1))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() not enough memory for XML action data\n");
+ goto fail;
+ }
+ memcpy(e->event.xml_action.data, p, h->length);
+ e->event.xml_action.data[h->length] = 0;
+ break;
+ }
+
+ case GG_PUBDIR50_REPLY:
+ {
+ 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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received userlist reply\n");
+
+ if (h->length < 1)
+ break;
+
+ /* jeśli odpowiedź na eksport, wywołaj zdarzenie tylko
+ * gdy otrzymano wszystkie odpowiedzi */
+ if (p[0] == GG_USERLIST_PUT_REPLY || p[0] == GG_USERLIST_PUT_MORE_REPLY) {
+ if (--sess->userlist_blocks)
+ break;
+
+ p[0] = GG_USERLIST_PUT_REPLY;
+ }
+
+ if (h->length > 1) {
+ char *tmp;
+ unsigned int len = (sess->userlist_reply) ? (unsigned int)strlen(sess->userlist_reply) : 0;
+
+ 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_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;
+ }
+
+ sess->userlist_reply = tmp;
+ sess->userlist_reply[len + h->length - 1] = 0;
+ memcpy(sess->userlist_reply + len, p + 1, h->length - 1);
+ }
+
+ if (p[0] == GG_USERLIST_GET_MORE_REPLY)
+ break;
+
+ e->type = GG_EVENT_USERLIST;
+ e->event.userlist.type = p[0];
+ e->event.userlist.reply = sess->userlist_reply;
+ sess->userlist_reply = NULL;
+
+ 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_ABORT:
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received dcc7 abort\n");
+
+ if (h->length < sizeof(struct gg_dcc7_aborted))
+ break;
+
+ if (gg_dcc7_handle_abort(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;
+ }
+
+ case GG_USER_DATA:
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received user data\n");
+
+ if (h->length < sizeof(struct gg_user_data))
+ break;
+
+ if (gg_handle_user_data(sess, e, p, h->length) == -1)
+ goto fail;
+
+ break;
+ }
+
+ case GG_TYPING_NOTIFICATION:
+ {
+ struct gg_typing_notification *n = (void*) p;
+ uin_t uin;
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received typing notification\n");
+
+ if (h->length < sizeof(*n))
+ break;
+
+ memcpy(&uin, &n->uin, sizeof(uin_t));
+
+ e->type = GG_EVENT_TYPING_NOTIFICATION;
+ e->event.typing_notification.uin = gg_fix32(uin);
+ e->event.typing_notification.length = gg_fix16(n->length);
+
+ break;
+ }
+
+ case GG_MULTILOGON_INFO:
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received multilogon info\n");
+
+ if (h->length < sizeof(struct gg_multilogon_info))
+ break;
+
+ if (gg_handle_multilogon_info(sess, e, p, h->length) == -1)
+ goto fail;
+
+ break;
+ }
+
+ default:
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd_connected() received unknown packet 0x%.2x\n", h->type);
+ }
+
+ free(h);
+ return 0;
+
+fail:
+ free(h);
+ return -1;
+}
+
+/** \endcond */
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze sesji.
+ *
+ * Funkcja zwraca strukturę zdarzenia \c gg_event. Jeśli rodzaj zdarzenia
+ * to \c GG_EVENT_NONE, nie wydarzyło się jeszcze nic wartego odnotowania.
+ * Strukturę zdarzenia należy zwolnić funkcja \c gg_event_free().
+ *
+ * \param sess Struktura sesji
+ *
+ * \return Struktura zdarzenia lub \c NULL jeśli wystąpił błąd
+ *
+ * \ingroup events
+ */
+struct gg_event *gg_watch_fd(struct gg_session *sess)
+{
+ struct gg_event *e;
+ int res = 0;
+ int port = 0;
+ int errno2 = 0;
+
+ 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_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 = gg_sock_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;
+ }
+
+ res = 0;
+ }
+
+ switch (sess->state) {
+ case GG_STATE_RESOLVING:
+ {
+ struct in_addr addr;
+ int failed = 0;
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING\n");
+
+ if (gg_sock_read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
+ failed = 1;
+ errno2 = errno;
+ }
+
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+
+ sess->resolver_cleanup(&sess->resolver, 0);
+
+ if (failed) {
+ errno = errno2;
+ goto fail_resolving;
+ }
+
+ /* jeśli jesteśmy w resolverze i mamy ustawiony port
+ * proxy, znaczy, że resolvowaliśmy proxy. zatem
+ * wpiszmy jego adres. */
+ if (sess->proxy_port)
+ sess->proxy_addr = addr.s_addr;
+
+ /* zapiszmy sobie adres huba i adres serwera (do
+ * bezpośredniego połączenia, jeśli hub leży)
+ * z resolvera. */
+ if (sess->proxy_addr && sess->proxy_port)
+ port = sess->proxy_port;
+ else {
+ sess->server_addr = sess->hub_addr = addr.s_addr;
+ port = GG_APPMSG_PORT;
+ }
+
+ 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) {
+ /* 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;
+ }
+
+ /* jeśli podano serwer i łączmy się przez proxy,
+ * jest to bezpośrednie połączenie, inaczej jest
+ * do huba. */
+
+ 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;
+
+ break;
+ }
+
+ case GG_STATE_CONNECTING_HUB:
+ {
+ char buf[1024], *client, *auth;
+ int res = 0;
+ unsigned int res_size = sizeof(res);
+ 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 && (gg_getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
+ 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_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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for client version\n");
+ goto fail_connecting;
+ }
+
+ if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port)
+ host = "http://" GG_APPMSG_HOST;
+ else
+ host = "";
+
+ auth = gg_proxy_auth();
+
+#ifdef GG_CONFIG_MIRANDA
+ if (sess->tls) {
+ snprintf(buf, sizeof(buf) - 1,
+ "GET %s/appsvc/appmsg_ver10.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s&age=2&gender=1 HTTP/1.0\r\n"
+ "Connection: close\r\n"
+ "Host: " GG_APPMSG_HOST "\r\n"
+ "%s"
+ "\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
+ } else
+#elif GG_CONFIG_HAVE_OPENSSL
+ if (sess->ssl != NULL) {
+ snprintf(buf, sizeof(buf) - 1,
+ "GET %s/appsvc/appmsg_ver10.asp?fmnumber=%u&fmt=2&lastmsg=%d&version=%s&age=2&gender=1 HTTP/1.0\r\n"
+ "Connection: close\r\n"
+ "Host: " GG_APPMSG_HOST "\r\n"
+ "%s"
+ "\r\n", host, sess->uin, sess->last_sysmsg, client, (auth) ? auth : "");
+ } else
+#endif
+ {
+ 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. */
+ if (sess->client_version) {
+ free(sess->client_version);
+ sess->client_version = NULL;
+ }
+
+ 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 (gg_sock_write(sess->fd, buf, (int)strlen(buf)) < (signed)strlen(buf)) {
+ 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;
+ sess->state = GG_STATE_IDLE;
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+ break;
+ }
+
+ sess->state = GG_STATE_READING_DATA;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+
+ case GG_STATE_READING_DATA:
+ {
+ char buf[1024], *tmp, *host;
+ int port = GG_DEFAULT_PORT;
+ struct in_addr addr;
+
+ 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_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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid http reply, connection failed\n");
+ goto fail_connecting;
+ }
+
+ /* ignorujemy resztę nagłówka. */
+ while (strcmp(buf, "\r\n") && strcmp(buf, ""))
+ gg_read_line(sess->fd, buf, sizeof(buf) - 1);
+
+ /* czytamy pierwszÄ… liniÄ™ danych. */
+ gg_read_line(sess->fd, buf, sizeof(buf) - 1);
+ gg_chomp(buf);
+
+ /* 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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() out of memory for system message, ignoring\n");
+ break;
+ }
+
+ sysmsg_buf = foo;
+
+ if (!len)
+ strcpy(sysmsg_buf, tmp);
+ else
+ strcat(sysmsg_buf, tmp);
+
+ len += (int)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;
+ }
+
+ gg_sock_close(sess->fd);
+
+ gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() received http data (%s)\n", buf);
+
+ /* analizujemy otrzymane dane. */
+ tmp = buf;
+
+ while (*tmp && *tmp != ' ')
+ tmp++;
+ while (*tmp && *tmp == ' ')
+ tmp++;
+ while (*tmp && *tmp != ' ')
+ tmp++;
+ while (*tmp && *tmp == ' ')
+ tmp++;
+ host = tmp;
+ while (*tmp && *tmp != ' ')
+ tmp++;
+ *tmp = 0;
+
+ if ((tmp = strchr(host, ':'))) {
+ *tmp = 0;
+ port = atoi(tmp + 1);
+ }
+
+ if (!strcmp(host, "notoperating")) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() service unavailable\n", errno, strerror(errno));
+ sess->fd = -1;
+ goto fail_unavailable;
+ }
+
+ addr.s_addr = inet_addr(host);
+ sess->server_addr = addr.s_addr;
+
+ if (!gg_proxy_http_only && sess->proxy_addr && sess->proxy_port) {
+ /* jeśli mamy proxy, łączymy się z nim. */
+ if ((sess->fd = gg_connect(&sess->proxy_addr, sess->proxy_port, sess->async)) == -1) {
+ /* 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;
+
+ /* Jeśli podano nazwę, nie adres serwera... */
+ if (sess->server_addr == INADDR_NONE) {
+ if (sess->resolver_start(&sess->fd, &sess->resolver, host) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() resolving failed (errno=%d, %s)\n", errno, strerror(errno));
+ goto fail_resolving;
+ }
+
+ sess->state = GG_STATE_RESOLVING_GG;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+ break;
+ }
+
+ /* łączymy się z właściwym serwerem. */
+ if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
+
+ sess->port = GG_HTTPS_PORT;
+
+ /* nie wyszło? próbujemy portu 443. */
+ if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
+ /* ostatnia deska ratunku zawiodła?
+ * w takim razie zwijamy manatki. */
+ 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;
+ }
+
+ case GG_STATE_RESOLVING_GG:
+ {
+ struct in_addr addr;
+ int failed = 0;
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_RESOLVING_GG\n");
+
+ if (gg_sock_read(sess->fd, &addr, sizeof(addr)) < (signed)sizeof(addr) || addr.s_addr == INADDR_NONE) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() resolving failed\n");
+ failed = 1;
+ errno2 = errno;
+ }
+
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+
+ sess->resolver_cleanup(&sess->resolver, 0);
+
+ if (failed) {
+ errno = errno2;
+ goto fail_resolving;
+ }
+
+ sess->server_addr = addr.s_addr;
+
+ /* łączymy się z właściwym serwerem. */
+ if ((sess->fd = gg_connect(&addr, sess->port, sess->async)) == -1) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s), trying https\n", errno, strerror(errno));
+
+ sess->port = GG_HTTPS_PORT;
+
+ /* nie wyszło? próbujemy portu 443. */
+ if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, sess->async)) == -1) {
+ /* ostatnia deska ratunku zawiodła?
+ * w takim razie zwijamy manatki. */
+ 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;
+ }
+
+ case GG_STATE_CONNECTING_GG:
+ {
+ int res = 0;
+ unsigned int res_size = sizeof(res);
+
+ 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 || gg_getsockopt(sess->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
+ /* 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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection to proxy failed (errno=%d, %s)\n", res, strerror(res));
+ goto fail_connecting;
+ }
+
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+
+#ifdef ETIMEDOUT
+ if (sess->timeout == 0)
+ errno = ETIMEDOUT;
+#endif
+
+#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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connection failed (errno=%d, %s)\n", res, strerror(res));
+ goto fail_connecting;
+ }
+#endif
+
+ 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;
+
+ /* prĂłbujemy na port 443. */
+ if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) {
+ 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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() connected\n");
+
+ if (gg_proxy_http_only)
+ sess->proxy_port = 0;
+
+ /* jeśli mamy proxy, wyślijmy zapytanie. */
+ if (sess->proxy_addr && sess->proxy_port) {
+ char buf[100], *auth = gg_proxy_auth();
+ struct in_addr addr;
+
+ if (sess->server_addr)
+ addr.s_addr = sess->server_addr;
+ else
+ addr.s_addr = sess->hub_addr;
+
+ snprintf(buf, sizeof(buf), "CONNECT %s:%d HTTP/1.0\r\n", inet_ntoa(addr), sess->port);
+
+ 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 (gg_sock_write(sess->fd, buf, (int)strlen(buf)) < (signed)strlen(buf)) {
+ 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_session(sess, GG_DEBUG_MISC, "// %s", auth);
+ if (gg_sock_write(sess->fd, auth, (int)strlen(auth)) < (signed)strlen(auth)) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+ free(auth);
+ goto fail_connecting;
+ }
+
+ free(auth);
+ }
+
+ if (gg_sock_write(sess->fd, "\r\n", 2) < 2) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() can't send proxy request\n");
+ goto fail_connecting;
+ }
+ }
+
+#ifdef GG_CONFIG_MIRANDA
+ if (sess->tls) {
+ sess->state = GG_STATE_TLS_NEGOTIATION;
+ sess->check = GG_CHECK_WRITE;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+#elif GG_CONFIG_HAVE_OPENSSL
+ if (sess->ssl != NULL) {
+ SSL_set_fd(sess->ssl, (int)sess->fd);
+
+ sess->state = GG_STATE_TLS_NEGOTIATION;
+ sess->check = GG_CHECK_WRITE;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+#endif
+
+ sess->state = GG_STATE_READING_KEY;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+
+#ifdef GG_CONFIG_MIRANDA
+ case GG_STATE_TLS_NEGOTIATION:
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_TLS_NEGOTIATION\n");
+
+ sess->ssl = si.connect(sess->fd, 0, 0);
+
+ if (sess->ssl == NULL) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation failed\n");
+
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_TLS;
+ sess->state = GG_STATE_IDLE;
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+ break;
+ }
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() TLS negotiation succeded\n");
+
+ sess->state = GG_STATE_READING_KEY;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+#elif GG_CONFIG_HAVE_OPENSSL
+ case GG_STATE_TLS_NEGOTIATION:
+ {
+ int res;
+ X509 *peer;
+
+ 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_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;
+ sess->state = GG_STATE_IDLE;
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+ break;
+ }
+
+ if (err == SSL_ERROR_WANT_READ) {
+ 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;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ } else if (err == SSL_ERROR_WANT_WRITE) {
+ 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;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ } else {
+ char buf[256];
+
+ ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() SSL_connect() bailed out: %s\n", buf);
+
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_TLS;
+ sess->state = GG_STATE_IDLE;
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+ break;
+ }
+ }
+
+ 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_session(sess, GG_DEBUG_MISC, "// WARNING! unable to get peer certificate!\n");
+ else {
+ char buf[256];
+
+ X509_NAME_oneline(X509_get_subject_name(peer), buf, sizeof(buf));
+ gg_debug_session(sess, GG_DEBUG_MISC, "// cert subject: %s\n", buf);
+
+ X509_NAME_oneline(X509_get_issuer_name(peer), buf, sizeof(buf));
+ gg_debug_session(sess, GG_DEBUG_MISC, "// cert issuer: %s\n", buf);
+ }
+
+ sess->state = GG_STATE_READING_KEY;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+
+ break;
+ }
+#endif
+
+ case GG_STATE_READING_KEY:
+ {
+ struct gg_header *h;
+ struct gg_welcome *w;
+ unsigned char *password = (unsigned char*) sess->password;
+ int ret;
+ 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_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_session(sess, GG_DEBUG_MISC, "// %s\n", buf);
+ }
+
+ /* XXX niech czeka jeszcze raz w tej samej
+ * fazie. głupio, ale działa. */
+ sess->proxy_port = 0;
+
+ break;
+ }
+
+ /* czytaj pierwszy pakiet. */
+ if (!(h = gg_recv_packet(sess))) {
+ 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;
+ errno2 = errno;
+ gg_sock_close(sess->fd);
+ errno = errno2;
+ sess->fd = -1;
+ break;
+ }
+
+ if (h->type != GG_WELCOME) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet received\n");
+ free(h);
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+ errno = EINVAL;
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_INVALID;
+ sess->state = GG_STATE_IDLE;
+ break;
+ }
+
+ w = (struct gg_welcome*) ((char*) h + sizeof(struct gg_header));
+ w->key = gg_fix32(w->key);
+
+ 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;
+
+ if (gg_dcc_ip == (unsigned long) inet_addr("255.255.255.255")) {
+ struct sockaddr_in sin;
+ unsigned int sin_len = sizeof(sin);
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() detecting address\n");
+
+ if (!getsockname(sess->fd, (struct sockaddr*) &sin, &sin_len)) {
+ 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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() unable to detect address\n");
+ sess->client_addr = 0;
+ }
+ } else
+ sess->client_addr = gg_dcc_ip;
+
+ if (sess->protocol_version >= 0x2e) {
+ struct gg_login80 l;
+ const char *version = (sess->client_version) ? sess->client_version : GG_DEFAULT_CLIENT_VERSION;
+ uint32_t tmp_version_len = gg_fix32((uint32_t)strlen(GG8_VERSION) + (uint32_t)strlen(version));
+ uint32_t tmp_descr_len = gg_fix32((sess->initial_descr) ? (uint32_t)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(sess->status_flags);
+ 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), version, strlen(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((uint16_t)((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((uint16_t)((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);
+ }
+
+ free(sess->initial_descr);
+ sess->initial_descr = NULL;
+
+ if (ret == -1) {
+ gg_debug_session(sess, GG_DEBUG_TRAFFIC, "// gg_watch_fd() sending packet failed. (errno=%d, %s)\n", errno, strerror(errno));
+ errno2 = errno;
+ gg_sock_close(sess->fd);
+ errno = errno2;
+ sess->fd = -1;
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_WRITING;
+ sess->state = GG_STATE_IDLE;
+ break;
+ }
+
+ sess->state = GG_STATE_READING_REPLY;
+ sess->check = GG_CHECK_READ;
+
+ break;
+ }
+
+ case GG_STATE_READING_REPLY:
+ {
+ struct gg_header *h;
+
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_READING_REPLY\n");
+
+ if (!(h = gg_recv_packet(sess))) {
+ 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;
+ errno2 = errno;
+ gg_sock_close(sess->fd);
+ errno = errno2;
+ sess->fd = -1;
+ break;
+ }
+
+ 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);
+ break;
+ }
+
+ if (h->type == GG_LOGIN_FAILED || h->type == GG_LOGIN80_FAILED) {
+ 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_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_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() invalid packet\n");
+ e->event.failure = GG_FAILURE_INVALID;
+ errno = EINVAL;
+ }
+
+ e->type = GG_EVENT_CONN_FAILED;
+ sess->state = GG_STATE_IDLE;
+ errno2 = errno;
+ gg_sock_close(sess->fd);
+ errno = errno2;
+ sess->fd = -1;
+ free(h);
+
+ break;
+ }
+
+ case GG_STATE_CONNECTED:
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_watch_fd() GG_STATE_CONNECTED\n");
+
+ sess->last_event = (int)time(NULL);
+
+ if ((res = gg_watch_fd_connected(sess, e)) == -1) {
+
+ 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;
+ res = 0;
+ } else
+ res = -1;
+ }
+
+ sess->check = GG_CHECK_READ;
+
+ break;
+ }
+ }
+
+done:
+ 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;
+ gg_sock_close(sess->fd);
+ errno = errno2;
+ sess->fd = -1;
+ }
+ e->type = GG_EVENT_CONN_FAILED;
+ e->event.failure = GG_FAILURE_CONNECTING;
+ sess->state = GG_STATE_IDLE;
+ goto done;
+
+fail_resolving:
+ e->type = GG_EVENT_CONN_FAILED;
+ 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;
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/protocols/Gadu-Gadu/libgadu/http.c b/protocols/Gadu-Gadu/libgadu/http.c
new file mode 100644
index 0000000000..c070b3ee53
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/http.c
@@ -0,0 +1,544 @@
+/* coding: UTF-8 */
+/* $Id: http.c 11370 2010-03-13 16:17:54Z dezred $ */
+
+/*
+ * (C) Copyright 2001-2002 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/**
+ * \file http.c
+ *
+ * \brief Obsługa połączeń HTTP
+ */
+
+#include <sys/types.h>
+#ifdef _WIN32
+#include "win32.h"
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif /* _WIN32 */
+
+#include "compat.h"
+#include "libgadu.h"
+#include "resolver.h"
+
+#include <ctype.h>
+#include <errno.h>
+#ifndef _WIN32
+#include <netdb.h>
+#endif /* _WIN32 */
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif /* _WIN32 */
+
+/**
+ * Rozpoczyna połączenie HTTP.
+ *
+ * Funkcja przeprowadza połączenie HTTP przy połączeniu synchronicznym,
+ * zwracając wynik w polach struktury \c gg_http, lub błąd, gdy sesja się
+ * nie powiedzie.
+ *
+ * Przy połączeniu asynchronicznym, funkcja rozpoczyna połączenie, a dalsze
+ * etapy będą przeprowadzane po wykryciu zmian (\c watch) na obserwowanym
+ * deskryptorze (\c fd) i wywołaniu funkcji \c gg_http_watch_fd().
+ *
+ * Po zakończeniu, należy zwolnić strukturę za pomocą funkcji
+ * \c gg_http_free(). Połączenie asynchroniczne można zatrzymać w każdej
+ * chwili za pomocÄ… \c gg_http_stop().
+ *
+ * \param hostname Adres serwera
+ * \param port Port serwera
+ * \param async Flaga asynchronicznego połączenia
+ * \param method Metoda HTTP
+ * \param path Ścieżka do zasobu (musi być poprzedzona znakiem '/')
+ * \param header Nagłówek zapytania plus ewentualne dane dla POST
+ *
+ * \return Zaalokowana struktura \c gg_http lub NULL, jeśli wystąpił błąd.
+ *
+ * \ingroup http
+ */
+struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header)
+{
+ struct gg_http *h;
+
+ if (!hostname || !port || !method || !path || !header) {
+ gg_debug(GG_DEBUG_MISC, "// gg_http_connect() invalid arguments\n");
+ errno = EFAULT;
+ return NULL;
+ }
+
+ if (!(h = malloc(sizeof(*h))))
+ return NULL;
+ memset(h, 0, sizeof(*h));
+
+ h->async = async;
+ h->port = port;
+ h->fd = -1;
+ h->type = GG_SESSION_HTTP;
+
+ gg_http_set_resolver(h, GG_RESOLVER_DEFAULT);
+
+ if (gg_proxy_enabled) {
+ char *auth = gg_proxy_auth();
+
+ h->query = gg_saprintf("%s http://%s:%d%s HTTP/1.0\r\n%s%s",
+ method, hostname, port, path, (auth) ? auth :
+ "", header);
+ hostname = gg_proxy_host;
+ h->port = port = gg_proxy_port;
+ free(auth);
+
+ } else {
+ h->query = gg_saprintf("%s %s HTTP/1.0\r\n%s",
+ method, path, header);
+ }
+
+ if (!h->query) {
+ gg_debug(GG_DEBUG_MISC, "// gg_http_connect() not enough memory for query\n");
+ free(h);
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-QUERY-----\n%s\n=> -----END-HTTP-QUERY-----\n", h->query);
+
+ if (async) {
+ if (h->resolver_start(&h->fd, &h->resolver, hostname) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver failed\n");
+ gg_http_free(h);
+ errno = ENOENT;
+ return NULL;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "// gg_http_connect() resolver = %p\n", h->resolver);
+
+ h->state = GG_STATE_RESOLVING;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ } else {
+ struct in_addr addr;
+
+ 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;
+ }
+
+ 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;
+ }
+
+ h->state = GG_STATE_CONNECTING;
+
+ while (h->state != GG_STATE_ERROR && h->state != GG_STATE_PARSING) {
+ if (gg_http_watch_fd(h) == -1)
+ break;
+ }
+
+ if (h->state != GG_STATE_PARSING) {
+ gg_debug(GG_DEBUG_MISC, "// gg_http_connect() some strange error\n");
+ gg_http_free(h);
+ return NULL;
+ }
+ }
+
+ h->callback = gg_http_watch_fd;
+ h->destroy = gg_http_free;
+
+ return h;
+}
+
+#ifndef DOXYGEN
+
+#define gg_http_error(x) \
+ gg_sock_close(h->fd); \
+ h->fd = -1; \
+ h->state = GG_STATE_ERROR; \
+ h->error = x; \
+ return 0;
+
+#endif /* DOXYGEN */
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe
+ * \c GG_STATE_PARSING. W tym miejscu działanie przejmuje zwykle funkcja
+ * korzystająca z \c gg_http_watch_fd(). W przypadku błędu połączenia,
+ * pole \c state będzie równe \c GG_STATE_ERROR, a kod błędu znajdzie się
+ * w polu \c error.
+ *
+ * \param h Struktura połączenia
+ *
+ * \return \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup http
+ */
+int gg_http_watch_fd(struct gg_http *h)
+{
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_http_watch_fd(%p);\n", h);
+
+ if (!h) {
+ gg_debug(GG_DEBUG_MISC, "// gg_http_watch_fd() invalid arguments\n");
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (h->state == GG_STATE_RESOLVING) {
+ struct in_addr a;
+
+ gg_debug(GG_DEBUG_MISC, "=> http, resolving done\n");
+
+ if (gg_sock_read(h->fd, &a, sizeof(a)) < (signed)sizeof(a) || a.s_addr == INADDR_NONE) {
+ gg_debug(GG_DEBUG_MISC, "=> http, resolver thread failed\n");
+ gg_http_error(GG_ERROR_RESOLVING);
+ }
+
+ gg_sock_close(h->fd);
+ h->fd = -1;
+
+ h->resolver_cleanup(&h->resolver, 0);
+
+ gg_debug(GG_DEBUG_MISC, "=> http, connecting to %s:%d\n", inet_ntoa(a), h->port);
+
+ if ((h->fd = gg_connect(&a, h->port, h->async)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "=> http, connection failed (errno=%d, %s)\n", errno, strerror(errno));
+ gg_http_error(GG_ERROR_CONNECTING);
+ }
+
+ h->state = GG_STATE_CONNECTING;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+
+ return 0;
+ }
+
+ if (h->state == GG_STATE_CONNECTING) {
+ int res = 0;
+ unsigned int res_size = sizeof(res);
+
+ if (h->async && (gg_getsockopt(h->fd, SOL_SOCKET, SO_ERROR, &res, &res_size) || res)) {
+ gg_debug(GG_DEBUG_MISC, "=> http, async connection failed (errno=%d, %s)\n", (res) ? res : errno , strerror((res) ? res : errno));
+ gg_sock_close(h->fd);
+ h->fd = -1;
+ h->state = GG_STATE_ERROR;
+ h->error = GG_ERROR_CONNECTING;
+ if (res)
+ errno = res;
+ return 0;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "=> http, connected, sending request\n");
+
+ h->state = GG_STATE_SENDING_QUERY;
+ }
+
+ if (h->state == GG_STATE_SENDING_QUERY) {
+ int res;
+
+ if ((res = gg_sock_write(h->fd, h->query, (int)strlen(h->query))) < 1) {
+ gg_debug(GG_DEBUG_MISC, "=> http, write() failed (len=%d, res=%d, errno=%d)\n", strlen(h->query), res, errno);
+ gg_http_error(GG_ERROR_WRITING);
+ }
+
+ if (res < (int)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);
+ h->state = GG_STATE_SENDING_QUERY;
+ h->check = GG_CHECK_WRITE;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ } else {
+ gg_debug(GG_DEBUG_MISC, "=> http, request sent (len=%d)\n", strlen(h->query));
+ free(h->query);
+ h->query = NULL;
+
+ h->state = GG_STATE_READING_HEADER;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ }
+
+ return 0;
+ }
+
+ if (h->state == GG_STATE_READING_HEADER) {
+ char buf[1024], *tmp;
+ int res;
+
+ if ((res = gg_sock_read(h->fd, buf, sizeof(buf))) == -1) {
+ gg_debug(GG_DEBUG_MISC, "=> http, reading header failed (errno=%d)\n", errno);
+ if (h->header) {
+ free(h->header);
+ h->header = NULL;
+ }
+ gg_http_error(GG_ERROR_READING);
+ }
+
+ if (!res) {
+ gg_debug(GG_DEBUG_MISC, "=> http, connection reset by peer\n");
+ if (h->header) {
+ free(h->header);
+ h->header = NULL;
+ }
+ gg_http_error(GG_ERROR_READING);
+ }
+
+ gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of header\n", res);
+
+ if (!(tmp = realloc(h->header, h->header_size + res + 1))) {
+ gg_debug(GG_DEBUG_MISC, "=> http, not enough memory for header\n");
+ free(h->header);
+ h->header = NULL;
+ gg_http_error(GG_ERROR_READING);
+ }
+
+ h->header = tmp;
+
+ memcpy(h->header + h->header_size, buf, res);
+ h->header_size += res;
+
+ gg_debug(GG_DEBUG_MISC, "=> http, header_buf=%p, header_size=%d\n", h->header, h->header_size);
+
+ h->header[h->header_size] = 0;
+
+ if ((tmp = strstr(h->header, "\r\n\r\n")) || (tmp = strstr(h->header, "\n\n"))) {
+ int sep_len = (*tmp == '\r') ? 4 : 2;
+ unsigned int left;
+ char *line;
+
+ left = h->header_size - ((long)(tmp) - (long)(h->header) + sep_len);
+
+ gg_debug(GG_DEBUG_MISC, "=> http, got all header (%d bytes, %d left)\n", h->header_size - left, left);
+
+ /* HTTP/1.1 200 OK */
+ if (strlen(h->header) < 16 || strncmp(h->header + 9, "200", 3)) {
+ gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header);
+
+ gg_debug(GG_DEBUG_MISC, "=> http, didn't get 200 OK -- no results\n");
+ free(h->header);
+ h->header = NULL;
+ gg_http_error(GG_ERROR_CONNECTING);
+ }
+
+ h->body_size = 0;
+ line = h->header;
+ *tmp = 0;
+
+ gg_debug(GG_DEBUG_MISC, "=> -----BEGIN-HTTP-HEADER-----\n%s\n=> -----END-HTTP-HEADER-----\n", h->header);
+
+ while (line) {
+ if (!strncasecmp(line, "Content-length: ", 16)) {
+ h->body_size = atoi(line + 16);
+ }
+ line = strchr(line, '\n');
+ if (line)
+ line++;
+ }
+
+ if (h->body_size <= 0) {
+ gg_debug(GG_DEBUG_MISC, "=> http, content-length not found\n");
+ h->body_size = left;
+ }
+
+ if (left > h->body_size) {
+ gg_debug(GG_DEBUG_MISC, "=> http, oversized reply (%d bytes needed, %d bytes left)\n", h->body_size, left);
+ h->body_size = left;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "=> http, body_size=%d\n", h->body_size);
+
+ if (!(h->body = malloc(h->body_size + 1))) {
+ gg_debug(GG_DEBUG_MISC, "=> http, not enough memory (%d bytes for body_buf)\n", h->body_size + 1);
+ free(h->header);
+ h->header = NULL;
+ gg_http_error(GG_ERROR_READING);
+ }
+
+ if (left) {
+ memcpy(h->body, tmp + sep_len, left);
+ h->body_done = left;
+ }
+
+ h->body[left] = 0;
+
+ h->state = GG_STATE_READING_DATA;
+ h->check = GG_CHECK_READ;
+ h->timeout = GG_DEFAULT_TIMEOUT;
+ }
+
+ return 0;
+ }
+
+ if (h->state == GG_STATE_READING_DATA) {
+ char buf[1024];
+ int res;
+
+ if ((res = gg_sock_read(h->fd, buf, sizeof(buf))) == -1) {
+ gg_debug(GG_DEBUG_MISC, "=> http, reading body failed (errno=%d)\n", errno);
+ if (h->body) {
+ free(h->body);
+ h->body = NULL;
+ }
+ gg_http_error(GG_ERROR_READING);
+ }
+
+ if (!res) {
+ if (h->body_done >= h->body_size) {
+ gg_debug(GG_DEBUG_MISC, "=> http, we're done, closing socket\n");
+ h->state = GG_STATE_PARSING;
+ gg_sock_close(h->fd);
+ h->fd = -1;
+ } else {
+ gg_debug(GG_DEBUG_MISC, "=> http, connection closed while reading (have %d, need %d)\n", h->body_done, h->body_size);
+ if (h->body) {
+ free(h->body);
+ h->body = NULL;
+ }
+ gg_http_error(GG_ERROR_READING);
+ }
+
+ return 0;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "=> http, read %d bytes of body\n", res);
+
+ if (h->body_done + res > h->body_size) {
+ char *tmp;
+
+ gg_debug(GG_DEBUG_MISC, "=> http, too much data (%d bytes, %d needed), enlarging buffer\n", h->body_done + res, h->body_size);
+
+ if (!(tmp = realloc(h->body, h->body_done + res + 1))) {
+ gg_debug(GG_DEBUG_MISC, "=> http, not enough memory for data (%d needed)\n", h->body_done + res + 1);
+ free(h->body);
+ h->body = NULL;
+ gg_http_error(GG_ERROR_READING);
+ }
+
+ h->body = tmp;
+ h->body_size = h->body_done + res;
+ }
+
+ h->body[h->body_done + res] = 0;
+ memcpy(h->body + h->body_done, buf, res);
+ h->body_done += res;
+
+ gg_debug(GG_DEBUG_MISC, "=> body_done=%d, body_size=%d\n", h->body_done, h->body_size);
+
+ return 0;
+ }
+
+ if (h->fd != -1)
+ gg_sock_close(h->fd);
+
+ h->fd = -1;
+ h->state = GG_STATE_ERROR;
+ h->error = 0;
+
+ return -1;
+}
+
+/**
+ * Kończy asynchroniczne połączenie HTTP.
+ *
+ * Po zatrzymaniu należy zwolnić zasoby funkcją \c gg_http_free().
+ *
+ * \param h Struktura połączenia
+ *
+ * \ingroup http
+ */
+void gg_http_stop(struct gg_http *h)
+{
+ if (!h)
+ return;
+
+ if (h->state == GG_STATE_ERROR || h->state == GG_STATE_DONE)
+ return;
+
+ if (h->fd != -1) {
+ gg_sock_close(h->fd);
+ h->fd = -1;
+ }
+
+ h->resolver_cleanup(&h->resolver, 1);
+}
+
+/**
+ * \internal Zwalnia pola struktury \c gg_http.
+ *
+ * Funkcja zwalnia same pola, nie zwalnia struktury.
+ *
+ * \param h Struktura połączenia
+ */
+void gg_http_free_fields(struct gg_http *h)
+{
+ if (!h)
+ return;
+
+ if (h->body) {
+ free(h->body);
+ h->body = NULL;
+ }
+
+ if (h->query) {
+ free(h->query);
+ h->query = NULL;
+ }
+
+ if (h->header) {
+ free(h->header);
+ h->header = NULL;
+ }
+}
+
+/**
+ * Zwalnia zasoby po połączeniu HTTP.
+ *
+ * Jeśli połączenie nie zostało jeszcze zakończone, jest przerywane.
+ *
+ * \param h Struktura połączenia
+ *
+ * \ingroup http
+ */
+void gg_http_free(struct gg_http *h)
+{
+ if (!h)
+ return;
+
+ gg_http_stop(h);
+ gg_http_free_fields(h);
+ free(h);
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/protocols/Gadu-Gadu/libgadu/internal.h b/protocols/Gadu-Gadu/libgadu/internal.h
new file mode 100644
index 0000000000..0ffd39fdd5
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/internal.h
@@ -0,0 +1,48 @@
+/* coding: UTF-8 */
+/* $Id$ */
+
+/*
+ * (C) Copyright 2009 Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef LIBGADU_INTERNAL_H
+#define LIBGADU_INTERNAL_H
+
+#include "libgadu.h"
+
+struct gg_dcc7_relay {
+ uint32_t addr;
+ uint16_t port;
+ uint8_t family;
+};
+
+typedef struct gg_dcc7_relay gg_dcc7_relay_t;
+
+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);
+void gg_debug_dump_session(struct gg_session *sess, const void *buf, unsigned int buf_length, const char *format, ...);
+
+int gg_resolve(int *fd, int *pid, const char *hostname);
+int gg_resolve_pthread(int *fd, void **resolver, const char *hostname);
+void gg_resolve_pthread_cleanup(void *resolver, int kill);
+
+#ifdef GG_CONFIG_HAVE_UINT64_T
+uint64_t gg_fix64(uint64_t x);
+#endif
+
+#endif /* LIBGADU_INTERNAL_H */
diff --git a/protocols/Gadu-Gadu/libgadu/libgadu.c b/protocols/Gadu-Gadu/libgadu/libgadu.c
new file mode 100644
index 0000000000..f06f8a5b84
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/libgadu.c
@@ -0,0 +1,2353 @@
+/* coding: UTF-8 */
+/* $Id: libgadu.c 13762 2011-08-09 12:35:16Z dezred $ */
+
+/*
+ * (C) Copyright 2001-2010 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Robert J. WoĹşny <speedy@ziew.org>
+ * Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ * Tomasz Chiliński <chilek@chilan.com>
+ * Adam Wysocki <gophi@ekg.chmurka.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/**
+ * \file libgadu.c
+ *
+ * \brief Główny moduł biblioteki
+ */
+
+#ifndef _WIN64
+#define _USE_32BIT_TIME_T
+#endif
+
+#include <sys/types.h>
+#ifdef _WIN32
+#include "win32.h"
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef sun
+# include <sys/filio.h>
+#endif
+#endif /* _WIN32 */
+
+#include "compat.h"
+#include "libgadu.h"
+#include "protocol.h"
+#include "resolver.h"
+#include "internal.h"
+
+#include <errno.h>
+#ifndef _WIN32
+#include <netdb.h>
+#endif /* _WIN32 */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif /* _WIN32 */
+#if !defined(GG_CONFIG_MIRANDA) && defined(GG_CONFIG_HAVE_OPENSSL)
+# include <openssl/err.h>
+# include <openssl/rand.h>
+#endif
+
+/**
+ * Poziom rejestracji informacji odpluskwiajÄ…cych. Zmienna jest maskÄ… bitowÄ…
+ * składającą się ze stałych \c GG_DEBUG_...
+ *
+ * \ingroup debug
+ */
+int gg_debug_level = 0;
+
+/**
+ * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno
+ * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, sÄ… rĂłwne
+ * \c NULL, informacje są wysyłane do standardowego wyjścia błędu (\c stderr).
+ *
+ * \param level Poziom rejestracji
+ * \param format Format wiadomości (zgodny z \c printf)
+ * \param ap Lista argumentĂłw (zgodna z \c printf)
+ *
+ * \note Funkcja jest przesłaniana przez \c gg_debug_handler_session.
+ *
+ * \ingroup debug
+ */
+void (*gg_debug_handler)(int level, const char *format, va_list ap) = NULL;
+
+/**
+ * Funkcja, do której są przekazywane informacje odpluskwiające. Jeśli zarówno
+ * ten \c gg_debug_handler, jak i \c gg_debug_handler_session, sÄ… rĂłwne
+ * \c NULL, informacje są wysyłane do standardowego wyjścia błędu.
+ *
+ * \param sess Sesja ktĂłrej dotyczy informacja lub \c NULL
+ * \param level Poziom rejestracji
+ * \param format Format wiadomości (zgodny z \c printf)
+ * \param ap Lista argumentĂłw (zgodna z \c printf)
+ *
+ * \note Funkcja przesłania przez \c gg_debug_handler_session.
+ *
+ * \ingroup debug
+ */
+void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap) = NULL;
+
+/**
+ * Port gniazda nasłuchującego dla połączeń bezpośrednich.
+ *
+ * \ingroup ip
+ */
+int gg_dcc_port = 0;
+
+/**
+ * Adres IP gniazda nasłuchującego dla połączeń bezpośrednich.
+ *
+ * \ingroup ip
+ */
+unsigned long gg_dcc_ip = 0;
+
+/**
+ * Adres lokalnego interfejsu IP, z którego wywoływane są wszystkie połączenia.
+ *
+ * \ingroup ip
+ */
+unsigned long gg_local_ip = 0;
+
+/**
+ * Flaga włączenia połączeń przez serwer pośredniczący.
+ *
+ * \ingroup proxy
+ */
+int gg_proxy_enabled = 0;
+
+/**
+ * Adres serwera pośredniczącego.
+ *
+ * \ingroup proxy
+ */
+char *gg_proxy_host = NULL;
+
+/**
+ * Port serwera pośredniczącego.
+ *
+ * \ingroup proxy
+ */
+int gg_proxy_port = 0;
+
+/**
+ * Flaga używania serwera pośredniczącego jedynie dla usług HTTP.
+ *
+ * \ingroup proxy
+ */
+int gg_proxy_http_only = 0;
+
+/**
+ * Nazwa użytkownika do autoryzacji serwera pośredniczącego.
+ *
+ * \ingroup proxy
+ */
+char *gg_proxy_username = NULL;
+
+/**
+ * Hasło użytkownika do autoryzacji serwera pośredniczącego.
+ *
+ * \ingroup proxy
+ */
+char *gg_proxy_password = NULL;
+
+#ifndef DOXYGEN
+
+#ifndef lint
+static char rcsid[]
+#ifdef __GNUC__
+__attribute__ ((unused))
+#endif
+= "$Id: libgadu.c 13762 2011-08-09 12:35:16Z dezred $";
+#endif
+
+#endif /* DOXYGEN */
+
+/**
+ * Zwraca wersjÄ™ biblioteki.
+ *
+ * \return WskaĹşnik na statyczny bufor z wersjÄ… biblioteki.
+ *
+ * \ingroup version
+ */
+const char *gg_libgadu_version()
+{
+ return GG_LIBGADU_VERSION;
+}
+
+#ifdef GG_CONFIG_HAVE_UINT64_T
+/**
+ * \internal Zamienia kolejność bajtów w 64-bitowym słowie.
+ *
+ * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach
+ * big-endianowych odwraca kolejność bajtów w słowie.
+ *
+ * \param x Liczba do zamiany
+ *
+ * \return Liczba z odpowiednią kolejnością bajtów
+ *
+ * \ingroup helper
+ */
+uint64_t gg_fix64(uint64_t x)
+{
+#ifndef GG_CONFIG_BIGENDIAN
+ return x;
+#else
+ return (uint64_t)
+ (((x & (uint64_t) 0x00000000000000ffULL) << 56) |
+ ((x & (uint64_t) 0x000000000000ff00ULL) << 40) |
+ ((x & (uint64_t) 0x0000000000ff0000ULL) << 24) |
+ ((x & (uint64_t) 0x00000000ff000000ULL) << 8) |
+ ((x & (uint64_t) 0x000000ff00000000ULL) >> 8) |
+ ((x & (uint64_t) 0x0000ff0000000000ULL) >> 24) |
+ ((x & (uint64_t) 0x00ff000000000000ULL) >> 40) |
+ ((x & (uint64_t) 0xff00000000000000ULL) >> 56));
+#endif
+}
+#endif /* GG_CONFIG_HAVE_UINT64_T */
+
+/**
+ * \internal Zamienia kolejność bajtów w 32-bitowym słowie.
+ *
+ * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach
+ * big-endianowych odwraca kolejność bajtów w słowie.
+ *
+ * \param x Liczba do zamiany
+ *
+ * \return Liczba z odpowiednią kolejnością bajtów
+ *
+ * \ingroup helper
+ */
+uint32_t gg_fix32(uint32_t x)
+{
+#ifndef GG_CONFIG_BIGENDIAN
+ return x;
+#else
+ return (uint32_t)
+ (((x & (uint32_t) 0x000000ffU) << 24) |
+ ((x & (uint32_t) 0x0000ff00U) << 8) |
+ ((x & (uint32_t) 0x00ff0000U) >> 8) |
+ ((x & (uint32_t) 0xff000000U) >> 24));
+#endif
+}
+
+/**
+ * \internal Zamienia kolejność bajtów w 16-bitowym słowie.
+ *
+ * Ze względu na little-endianowość protokołu Gadu-Gadu, na maszynach
+ * big-endianowych zamienia kolejność bajtów w słowie.
+ *
+ * \param x Liczba do zamiany
+ *
+ * \return Liczba z odpowiednią kolejnością bajtów
+ *
+ * \ingroup helper
+ */
+uint16_t gg_fix16(uint16_t x)
+{
+#ifndef GG_CONFIG_BIGENDIAN
+ return x;
+#else
+ return (uint16_t)
+ (((x & (uint16_t) 0x00ffU) << 8) |
+ ((x & (uint16_t) 0xff00U) >> 8));
+#endif
+}
+
+/**
+ * \internal Liczy skrót z hasła i ziarna.
+ *
+ * \param password Hasło
+ * \param seed Ziarno podane przez serwer
+ *
+ * \return Wartość skrótu
+ */
+unsigned int gg_login_hash(const unsigned char *password, unsigned int seed)
+{
+ unsigned int x, y, z;
+
+ y = seed;
+
+ for (x = 0; *password; password++) {
+ x = (x & 0xffffff00) | *password;
+ y ^= x;
+ y += x;
+ x <<= 8;
+ y ^= x;
+ x <<= 8;
+ y -= x;
+ x <<= 8;
+ y ^= x;
+
+ z = y & 0x1F;
+ y = (y << z) | (y >> (32 - z));
+ }
+
+ return y;
+}
+
+/**
+ * \internal Odbiera od serwera dane binarne.
+ *
+ * Funkcja odbiera dane od serwera zajmując się SSL/TLS w razie konieczności.
+ * Obsługuje EINTR, więc użytkownik nie musi się przejmować przerwanymi
+ * wywołaniami systemowymi.
+ *
+ * \param sess Struktura sesji
+ * \param buf Bufor na danymi
+ * \param length Długość bufora
+ *
+ * \return To samo co funkcja systemowa \c read
+ */
+int gg_read(struct gg_session *sess, char *buf, int length)
+{
+#ifdef GG_CONFIG_MIRANDA
+ if (sess->ssl != NULL)
+ return si.read(sess->ssl, buf, length, 0);
+#elif GG_CONFIG_HAVE_OPENSSL
+ if (sess->ssl != NULL) {
+ for (;;) {
+ int res, err;
+
+ res = SSL_read(sess->ssl, buf, length);
+
+ if (res < 0) {
+ err = SSL_get_error(sess->ssl, res);
+
+ if (err == SSL_ERROR_SYSCALL && errno == EINTR)
+ continue;
+
+ if (err == SSL_ERROR_WANT_READ)
+ errno = EAGAIN;
+ else if (err != SSL_ERROR_SYSCALL)
+ errno = EINVAL;
+
+ return -1;
+ }
+
+ return res;
+ }
+ }
+#endif
+
+ return gg_sock_read(sess->fd, buf, length);
+}
+
+/**
+ * \internal Wysyła do serwera dane binarne.
+ *
+ * Funkcja wysyła dane do serwera zajmując się SSL/TLS w razie konieczności.
+ * Obsługuje EINTR, więc użytkownik nie musi się przejmować przerwanymi
+ * wywołaniami systemowymi.
+ *
+ * \note Funkcja nie zajmuje się buforowaniem wysyłanych danych (patrz
+ * gg_write()).
+ *
+ * \param sess Struktura sesji
+ * \param buf Bufor z danymi
+ * \param length Długość bufora
+ *
+ * \return To samo co funkcja systemowa \c write
+ */
+static int gg_write_common(struct gg_session *sess, const char *buf, int length)
+{
+#ifdef GG_CONFIG_MIRANDA
+ if (sess->ssl != NULL)
+ return si.write(sess->ssl, buf, length);
+#elif GG_CONFIG_HAVE_OPENSSL
+ if (sess->ssl != NULL) {
+ for (;;) {
+ int res, err;
+
+ res = SSL_write(sess->ssl, (void *)buf, length);
+
+ if (res < 0) {
+ err = SSL_get_error(sess->ssl, res);
+
+ if (err == SSL_ERROR_SYSCALL && errno == EINTR)
+ continue;
+
+ if (err == SSL_ERROR_WANT_WRITE)
+ errno = EAGAIN;
+ else if (err != SSL_ERROR_SYSCALL)
+ errno = EINVAL;
+
+ return -1;
+ }
+
+ return res;
+ }
+ }
+#endif
+
+ return gg_sock_write(sess->fd, buf, length);
+}
+
+
+
+/**
+ * \internal Wysyła do serwera dane binarne.
+ *
+ * Funkcja wysyła dane do serwera zajmując się TLS w razie konieczności.
+ *
+ * \param sess Struktura sesji
+ * \param buf Bufor z danymi
+ * \param length Długość bufora
+ *
+ * \return To samo co funkcja systemowa \c write
+ */
+int gg_write(struct gg_session *sess, const char *buf, int length)
+{
+ int res = 0;
+
+ if (!sess->async) {
+ int written = 0;
+
+ while (written < length) {
+ res = gg_write_common(sess, buf + written, length - written);
+
+ if (res == -1)
+ return -1;
+
+ written += res;
+ res = written;
+ }
+ } else {
+ res = 0;
+
+ if (sess->send_buf == NULL) {
+ res = gg_write_common(sess, buf, length);
+
+ if (res == -1)
+ return -1;
+ }
+
+ if (res < length) {
+ char *tmp;
+
+ if (!(tmp = realloc(sess->send_buf, sess->send_left + length - res))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ sess->send_buf = tmp;
+
+ memcpy(sess->send_buf + sess->send_left, buf + res, length - res);
+
+ sess->send_left += length - res;
+ }
+ }
+
+ return res;
+}
+
+/**
+ * \internal Odbiera pakiet od serwera.
+ *
+ * Funkcja odczytuje nagłówek pakietu, a następnie jego zawartość i zwraca
+ * w zaalokowanym buforze.
+ *
+ * Przy połączeniach asynchronicznych, funkcja może nie być w stanie
+ * skompletować całego pakietu -- w takim przypadku zwróci -1, a kodem błędu
+ * będzie \c EAGAIN.
+ *
+ * \param sess Struktura sesji
+ *
+ * \return WskaĹşnik do zaalokowanego bufora
+ */
+void *gg_recv_packet(struct gg_session *sess)
+{
+ struct gg_header h;
+ char *buf = NULL;
+ int ret = 0;
+ unsigned int offset, size = 0;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_recv_packet(%p);\n", sess);
+
+ if (!sess) {
+ errno = EFAULT;
+ return NULL;
+ }
+
+ if (sess->recv_left < 1) {
+ if (sess->header_buf) {
+ memcpy(&h, sess->header_buf, 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
+ sess->header_done = 0;
+
+ while (sess->header_done < sizeof(h)) {
+ ret = gg_read(sess, (char*) &h + sess->header_done, sizeof(h) - sess->header_done);
+
+ 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_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: connection broken\n");
+ return NULL;
+ }
+
+ if (ret == -1) {
+ if (errno == EAGAIN) {
+ 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_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_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() header recv() failed: errno=%d, %s\n", errno, strerror(errno));
+
+ return NULL;
+ }
+
+ sess->header_done += ret;
+
+ }
+
+ h.type = gg_fix32(h.type);
+ h.length = gg_fix32(h.length);
+ } else
+ memcpy(&h, sess->recv_buf, sizeof(h));
+
+ /* jakieĹ› sensowne limity na rozmiar pakietu */
+ if (h.length > 65535) {
+ 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_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_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() not enough memory for packet data\n");
+ return NULL;
+ }
+
+ memcpy(buf, &h, sizeof(h));
+
+ offset = 0;
+ size = h.length;
+ }
+
+ while (size > 0) {
+ ret = gg_read(sess, buf + sizeof(h) + offset, size);
+ 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_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed: connection broken\n");
+ errno = ECONNRESET;
+ return NULL;
+ }
+ if (ret > -1 && ret <= (int)size) {
+ offset += ret;
+ size -= ret;
+ } else if (ret == -1) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_recv_packet() body recv() failed (errno=%d, %s)\n", errno, strerror(errno));
+
+ if (errno == EAGAIN) {
+ 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;
+ return NULL;
+ }
+
+ free(buf);
+ return NULL;
+ }
+ }
+
+ sess->recv_left = 0;
+
+ gg_debug_dump_session(sess, buf, sizeof(h) + h.length, "// gg_recv_packet(0x%.2x)", h.type);
+
+ return buf;
+}
+
+/**
+ * \internal Wysyła pakiet do serwera.
+ *
+ * Funkcja konstruuje pakiet do wysłania z dowolnej liczby fragmentów. Jeśli
+ * rozmiar pakietu jest za duży, by móc go wysłać za jednym razem, pozostała
+ * część zostanie zakolejkowana i wysłana, gdy będzie to możliwe.
+ *
+ * \param sess Struktura sesji
+ * \param type Rodzaj pakietu
+ * \param ... Lista kolejnych części pakietu (wskaźnik na bufor i długość
+ * typu \c int) zakończona \c NULL
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_send_packet(struct gg_session *sess, int type, ...)
+{
+ struct gg_header *h;
+ char *tmp;
+ unsigned int tmp_length;
+ void *payload;
+ unsigned int payload_length;
+ va_list ap;
+ int res;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_packet(%p, 0x%.2x, ...);\n", sess, type);
+
+ tmp_length = sizeof(struct gg_header);
+
+ if (!(tmp = malloc(tmp_length))) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_send_packet() not enough memory for packet header\n");
+ return -1;
+ }
+
+ va_start(ap, type);
+
+ payload = va_arg(ap, void *);
+
+ while (payload) {
+ char *tmp2;
+
+ payload_length = va_arg(ap, unsigned int);
+
+ if (!(tmp2 = realloc(tmp, tmp_length + payload_length))) {
+ gg_debug_session(sess, GG_DEBUG_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;
+
+ payload = va_arg(ap, void *);
+ }
+
+ va_end(ap);
+
+ h = (struct gg_header*) tmp;
+ h->type = gg_fix32(type);
+ h->length = gg_fix32(tmp_length - sizeof(struct gg_header));
+
+ gg_debug_dump_session(sess, tmp, tmp_length, "// gg_send_packet(0x%.2x)", gg_fix32(h->type));
+
+ 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;
+ }
+
+ 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;
+}
+
+/**
+ * \internal Funkcja zwrotna sesji.
+ *
+ * Pole \c callback struktury \c gg_session zawiera wskaĹşnik do tej funkcji.
+ * Wywołuje ona \c gg_watch_fd i zachowuje wynik w polu \c event.
+ *
+ * \note Korzystanie z tej funkcjonalności nie jest już zalecane.
+ *
+ * \param sess Struktura sesji
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_session_callback(struct gg_session *sess)
+{
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ return ((sess->event = gg_watch_fd(sess)) != NULL) ? 0 : -1;
+}
+
+/**
+ * ĹÄ…czy siÄ™ z serwerem Gadu-Gadu.
+ *
+ * Przy połączeniu synchronicznym funkcja zakończy działanie po nawiązaniu
+ * połączenia lub gdy wystąpi błąd. Po udanym połączeniu należy wywoływać
+ * funkcjÄ™ \c gg_watch_fd(), ktĂłra odbiera informacje od serwera i zwraca
+ * informacje o zdarzeniach.
+ *
+ * Przy połączeniu asynchronicznym funkcja rozpocznie procedurę połączenia
+ * i zwrĂłci zaalokowanÄ… strukturÄ™. Pole \c fd struktury \c gg_session zawiera
+ * deskryptor, który należy obserwować funkcją \c select, \c poll lub za
+ * pomocą mechanizmów użytej pętli zdarzeń (Glib, Qt itp.). Pole \c check
+ * jest maską bitową mówiącą, czy biblioteka chce być informowana o możliwości
+ * odczytu danych (\c GG_CHECK_READ) czy zapisu danych (\c GG_CHECK_WRITE).
+ * Po zaobserwowaniu zmian na deskryptorze należy wywołać funkcję
+ * \c gg_watch_fd(). Podczas korzystania z połączeń asynchronicznych, w trakcie
+ * połączenia może zostać stworzony dodatkowy proces rozwiązujący nazwę
+ * serwera -- z tego powodu program musi poprawnie obsłużyć sygnał SIGCHLD.
+ *
+ * \note Po nawiązaniu połączenia z serwerem należy wysłać listę kontaktów
+ * za pomocÄ… funkcji \c gg_notify() lub \c gg_notify_ex().
+ *
+ * \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
+ */
+#ifdef GG_CONFIG_MIRANDA
+struct gg_session *gg_login(const struct gg_login_params *p, SOCKET *gg_sock, int *gg_failno)
+#else
+struct gg_session *gg_login(const struct gg_login_params *p)
+#endif
+{
+ struct gg_session *sess = NULL;
+ char *hostname;
+ int port;
+
+ if (!p) {
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p);\n", p);
+ errno = EFAULT;
+ return NULL;
+ }
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_login(%p: [uin=%u, async=%d, ...]);\n", p, p->uin, p->async);
+
+ if (!(sess = malloc(sizeof(struct gg_session)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for session data\n");
+ goto fail;
+ }
+
+ memset(sess, 0, sizeof(struct gg_session));
+
+ if (!p->password || !p->uin) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. uin and password needed\n");
+ errno = EFAULT;
+ goto fail;
+ }
+
+ if (!(sess->password = strdup(p->password))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() not enough memory for password\n");
+ goto fail;
+ }
+
+ if (p->hash_type < 0 || p->hash_type > GG_LOGIN_HASH_SHA1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() invalid arguments. unknown hash type (%d)\n", p->hash_type);
+ errno = EFAULT;
+ goto fail;
+ }
+
+ sess->uin = p->uin;
+ sess->state = GG_STATE_RESOLVING;
+ sess->check = GG_CHECK_READ;
+ sess->timeout = GG_DEFAULT_TIMEOUT;
+ sess->async = p->async;
+ sess->type = GG_SESSION_GG;
+ sess->initial_status = p->status;
+ sess->callback = gg_session_callback;
+ sess->destroy = gg_free_session;
+ sess->port = (p->server_port) ? p->server_port : ((gg_proxy_enabled) ? GG_HTTPS_PORT : GG_DEFAULT_PORT);
+ sess->server_addr = p->server_addr;
+ sess->external_port = p->external_port;
+ sess->external_addr = p->external_addr;
+ sess->client_port = p->client_port;
+
+ if (p->protocol_features == 0) {
+ sess->protocol_features = GG_FEATURE_MSG80 | GG_FEATURE_STATUS80 | GG_FEATURE_DND_FFC | GG_FEATURE_IMAGE_DESCR | GG_FEATURE_UNKNOWN_100 | GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK | GG_FEATURE_TYPING_NOTIFICATION;
+ } else {
+ sess->protocol_features = (p->protocol_features & ~(GG_FEATURE_STATUS77 | GG_FEATURE_MSG77));
+
+ if (!(p->protocol_features & GG_FEATURE_STATUS77))
+ sess->protocol_features |= GG_FEATURE_STATUS80;
+
+ if (!(p->protocol_features & GG_FEATURE_MSG77))
+ sess->protocol_features |= GG_FEATURE_MSG80;
+ }
+
+ if (!(sess->status_flags = p->status_flags))
+ sess->status_flags = GG_STATUS_FLAG_UNKNOWN | GG_STATUS_FLAG_SPAM;
+
+ sess->protocol_version = (p->protocol_version) ? p->protocol_version : GG_DEFAULT_PROTOCOL_VERSION;
+
+ if (p->era_omnix)
+ sess->protocol_flags |= GG_ERA_OMNIX_MASK;
+ if (p->has_audio)
+ 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 ((signed)strlen(sess->initial_descr) > max_length)
+ sess->initial_descr[max_length] = 0;
+ }
+
+#ifdef GG_CONFIG_MIRANDA
+ sess->tls = p->tls;
+#endif
+ if (p->tls == 1) {
+#ifdef GG_CONFIG_HAVE_OPENSSL
+ char buf[1024];
+
+ OpenSSL_add_ssl_algorithms();
+
+ if (!RAND_status()) {
+ char rdata[1024];
+ struct {
+ time_t time;
+ void *ptr;
+ } rstruct;
+
+ time(&rstruct.time);
+ rstruct.ptr = (void *) &rstruct;
+
+ RAND_seed((void *) rdata, sizeof(rdata));
+ RAND_seed((void *) &rstruct, sizeof(rstruct));
+ }
+
+ sess->ssl_ctx = SSL_CTX_new(SSLv3_client_method());
+
+ if (!sess->ssl_ctx) {
+ ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
+ gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_CTX_new() failed: %s\n", buf);
+ goto fail;
+ }
+
+ SSL_CTX_set_verify(sess->ssl_ctx, SSL_VERIFY_NONE, NULL);
+
+ sess->ssl = SSL_new(sess->ssl_ctx);
+
+ if (!sess->ssl) {
+ ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
+ gg_debug(GG_DEBUG_MISC, "// gg_login() SSL_new() failed: %s\n", buf);
+ goto fail;
+ }
+#elif !defined(GG_CONFIG_MIRANDA)
+ 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;
+ } else {
+ hostname = GG_APPMSG_HOST;
+ port = GG_APPMSG_PORT;
+ }
+
+ if (p->hash_type)
+ sess->hash_type = p->hash_type;
+ else
+ sess->hash_type = GG_LOGIN_HASH_SHA1;
+
+ 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);
+#ifdef GG_CONFIG_MIRANDA
+ errno = EACCES;
+ *gg_failno = GG_FAILURE_RESOLVING;
+#endif
+ goto fail;
+ }
+ }
+ } else {
+ addr.s_addr = sess->server_addr;
+ port = sess->port;
+ }
+
+ sess->hub_addr = addr.s_addr;
+
+ if (gg_proxy_enabled)
+ sess->proxy_addr = addr.s_addr;
+
+#ifdef GG_CONFIG_MIRANDA
+ if ((sess->fd = gg_connect_internal(&addr, port, 0, gg_sock)) == -1) {
+#else
+ if ((sess->fd = gg_connect(&addr, port, 0)) == -1) {
+#endif
+ 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;
+
+#ifdef GG_CONFIG_MIRANDA
+ if ((sess->fd = gg_connect_internal(&addr, GG_HTTPS_PORT, 0, gg_sock)) == -1) {
+#else
+ if ((sess->fd = gg_connect(&addr, GG_HTTPS_PORT, 0)) == -1) {
+#endif
+ /* 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 (sess->server_addr)
+ sess->state = GG_STATE_CONNECTING_GG;
+ else
+ sess->state = GG_STATE_CONNECTING_HUB;
+
+ while (sess->state != GG_STATE_CONNECTED) {
+ struct gg_event *e;
+
+ if (!(e = gg_watch_fd(sess))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() critical error in gg_watch_fd()\n");
+ goto fail;
+ }
+
+ if (e->type == GG_EVENT_CONN_FAILED) {
+ errno = EACCES;
+#ifdef GG_CONFIG_MIRANDA
+ *gg_failno = e->event.failure;
+#endif
+ gg_debug(GG_DEBUG_MISC, "// gg_login() could not login\n");
+ gg_event_free(e);
+ goto fail;
+ }
+
+ gg_event_free(e);
+ }
+
+ return sess;
+ }
+
+ if (!sess->server_addr || gg_proxy_enabled) {
+ 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;
+ }
+ } else {
+ if ((sess->fd = gg_connect(&sess->server_addr, sess->port, sess->async)) == -1) {
+ gg_debug(GG_DEBUG_MISC, "// gg_login() direct connection failed (errno=%d, %s)\n", errno, strerror(errno));
+ goto fail;
+ }
+ sess->state = GG_STATE_CONNECTING_GG;
+ sess->check = GG_CHECK_WRITE;
+ sess->soft_timeout = 1;
+ }
+
+ return sess;
+
+fail:
+ gg_free_session(sess);
+
+ return NULL;
+}
+
+/**
+ * Wysyła do serwera pakiet utrzymania połączenia.
+ *
+ * Klient powinien regularnie co minutę wysyłać pakiet utrzymania połączenia,
+ * inaczej serwer uzna, że klient stracił łączność z siecią i zerwie
+ * połączenie.
+ *
+ * \param sess Struktura sesji
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup login
+ */
+int gg_ping(struct gg_session *sess)
+{
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_ping(%p);\n", sess);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ return gg_send_packet(sess, GG_PING, NULL);
+}
+
+/**
+ * Kończy połączenie z serwerem.
+ *
+ * Funkcja nie zwalnia zasobów, więc po jej wywołaniu należy użyć
+ * \c gg_free_session(). Jeśli chce się ustawić opis niedostępności, należy
+ * wcześniej wywołać funkcję \c gg_change_status_descr() lub
+ * \c gg_change_status_descr_time().
+ *
+ * \note Jeśli w buforze nadawczym połączenia z serwerem znajdują się jeszcze
+ * dane (np. z powodu strat pakietĂłw na Ĺ‚Ä…czu), prawdopodobnie zostanÄ… one
+ * utracone przy zrywaniu połączenia. Aby mieć pewność, że opis statusu
+ * zostanie zachowany, należy ustawić stan \c GG_STATUS_NOT_AVAIL_DESCR
+ * za pomocą funkcji \c gg_change_status_descr() i poczekać na zdarzenie
+ * \c GG_EVENT_DISCONNECT_ACK.
+ *
+ * \param sess Struktura sesji
+ *
+ * \ingroup login
+ */
+void gg_logoff(struct gg_session *sess)
+{
+ if (!sess)
+ return;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_logoff(%p);\n", sess);
+
+#ifdef GG_CONFIG_MIRANDA
+ if (sess->ssl != NULL)
+ si.shutdown(sess->ssl);
+#elif GG_CONFIG_HAVE_OPENSSL
+ if (sess->ssl != NULL)
+ SSL_shutdown(sess->ssl);
+#endif
+
+ sess->resolver_cleanup(&sess->resolver, 1);
+
+ if (sess->fd != -1) {
+ shutdown(sess->fd, SHUT_RDWR);
+ gg_sock_close(sess->fd);
+ sess->fd = -1;
+ }
+
+ if (sess->send_buf) {
+ free(sess->send_buf);
+ sess->send_buf = NULL;
+ sess->send_left = 0;
+ }
+}
+
+/**
+ * Zwalnia zasoby używane przez połączenie z serwerem. Funkcję należy wywołać
+ * po zamknięciu połączenia z serwerem, by nie doprowadzić do wycieku zasobów
+ * systemowych.
+ *
+ * \param sess Struktura sesji
+ *
+ * \ingroup login
+ */
+void gg_free_session(struct gg_session *sess)
+{
+ struct gg_dcc7 *dcc;
+
+ if (!sess)
+ return;
+
+ /* XXX dopisać zwalnianie i zamykanie wszystkiego, co mogło zostać */
+
+ free(sess->password);
+ free(sess->initial_descr);
+ free(sess->client_version);
+ free(sess->header_buf);
+
+#ifdef GG_CONFIG_MIRANDA
+ if (sess->ssl != NULL)
+ si.sfree(sess->ssl);
+#elif GG_CONFIG_HAVE_OPENSSL
+ if (sess->ssl != NULL)
+ SSL_free(sess->ssl);
+
+ if (sess->ssl_ctx)
+ SSL_CTX_free(sess->ssl_ctx);
+#endif
+
+ sess->resolver_cleanup(&sess->resolver, 1);
+
+ if (sess->fd != -1)
+ gg_sock_close(sess->fd);
+
+ 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);
+}
+
+#ifndef DOXYGEN
+
+/**
+ * \internal Funkcja wysyłająca pakiet zmiany statusu użytkownika.
+ *
+ * \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)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup status
+ */
+static int gg_change_status_common(struct gg_session *sess, int status, const char *descr, int time)
+{
+ 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;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ /* 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;
+
+ 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 = (int)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(sess->status_flags);
+ 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;
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Zmienia status uĹĽytkownika.
+ *
+ * \param sess Struktura sesji
+ * \param status Nowy status uĹĽytkownika
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup status
+ */
+int gg_change_status(struct gg_session *sess, int status)
+{
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status(%p, %d);\n", sess, status);
+
+ return gg_change_status_common(sess, status, NULL, 0);
+}
+
+/**
+ * Zmienia status uĹĽytkownika na status opisowy.
+ *
+ * \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)
+{
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_descr(%p, %d, \"%s\");\n", sess, status, descr);
+
+ return gg_change_status_common(sess, status, descr, 0);
+}
+
+/**
+ * Zmienia status uĹĽytkownika na status opisowy z podanym czasem powrotu.
+ *
+ * \param sess Struktura sesji
+ * \param status Nowy status uĹĽytkownika
+ * \param descr Opis statusu uĹĽytkownika
+ * \param time Czas powrotu w postaci uniksowego znacznika czasu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup status
+ */
+int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time)
+{
+ 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);
+}
+
+/**
+ * Funkcja zmieniajÄ…ca flagi statusu.
+ *
+ * \param sess Struktura sesji
+ * \param flags Nowe flagi statusu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \note Aby zmiany weszły w życie, należy ponownie ustawić status za pomocą
+ * funkcji z rodziny \c gg_change_status().
+ *
+ * \ingroup status
+ */
+int gg_change_status_flags(struct gg_session *sess, int flags)
+{
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_change_status_flags(%p, 0x%08x);\n", sess, flags);
+
+ if (sess == NULL) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ sess->status_flags = flags;
+
+ return 0;
+}
+
+/**
+ * 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 src Tekst źródłowy w UTF-8
+ * \param format Atrybuty tekstu źródłowego
+ * \param format_len Długość bloku atrybutów tekstu źródłowego
+ *
+ * \note Wynikowy tekst nie jest idealnym kodem HTML, poniewaĹĽ ma jak
+ * dokładniej odzwierciedlać to, co wygenerowałby oryginalny klient.
+ *
+ * \note Dokleja \c \\0 na końcu bufora wynikowego.
+ *
+ * \return Długość tekstu wynikowego bez \c \\0 (nawet jeśli \c dst to \c NULL).
+ */
+static int gg_convert_to_html(char *dst, const char *src, const unsigned char *format, int format_len)
+{
+ const char span_fmt[] = "<span style=\"color:#%02x%02x%02x; font-family:'MS Shell Dlg 2'; font-size:9pt; \">";
+ const int span_len = 75;
+ const char img_fmt[] = "<img name=\"%02x%02x%02x%02x%02x%02x%02x%02x\">";
+ const int img_len = 29;
+ int char_pos = 0;
+ int format_idx = 0;
+ unsigned char old_attr = 0;
+ const unsigned char *color = (const unsigned char*) "\x00\x00\x00";
+ int len, i;
+ const unsigned char *format_ = (const unsigned char*) format;
+
+ len = 0;
+
+ /* Nie mamy atrybutów dla pierwsze znaku, a tekst nie jest pusty, więc
+ * tak czy inaczej trzeba otworzyć <span>. */
+
+ if (src[0] != 0 && (format_idx + 3 > format_len || (format_[format_idx] | (format_[format_idx + 1] << 8)) != 0)) {
+ if (dst != NULL)
+ sprintf(&dst[len], span_fmt, 0, 0, 0);
+
+ len += span_len;
+ }
+
+ /* Pętla przechodzi też przez kończące \0, żeby móc dokleić obrazek
+ * na końcu tekstu. */
+
+ for (i = 0; ; i++) {
+ /* Analizuj atrybuty tak długo jak dotyczą aktualnego znaku. */
+ for (;;) {
+ unsigned char attr;
+ int attr_pos;
+
+ if (format_idx + 3 > format_len)
+ break;
+
+ attr_pos = format_[format_idx] | (format_[format_idx + 1] << 8);
+
+ if (attr_pos != char_pos)
+ break;
+
+ attr = format_[format_idx + 2];
+
+ /* Nie doklejaj atrybutów na końcu, co najwyżej obrazki. */
+
+ if (src[i] == 0)
+ attr &= ~(GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR);
+
+ format_idx += 3;
+
+ if ((attr & (GG_FONT_BOLD | GG_FONT_ITALIC | GG_FONT_UNDERLINE | GG_FONT_COLOR)) != 0 || (attr == 0 && old_attr != 0)) {
+ if (char_pos != 0) {
+ if ((old_attr & GG_FONT_UNDERLINE) != 0)
+ gg_append(dst, &len, "</u>", 4);
+
+ if ((old_attr & GG_FONT_ITALIC) != 0)
+ gg_append(dst, &len, "</i>", 4);
+
+ if ((old_attr & GG_FONT_BOLD) != 0)
+ gg_append(dst, &len, "</b>", 4);
+
+ if (src[i] != 0)
+ gg_append(dst, &len, "</span>", 7);
+ }
+
+ if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) {
+ color = &format_[format_idx];
+ format_idx += 3;
+ } else {
+ color = (unsigned char*) "\x00\x00\x00";
+ }
+
+ if (src[i] != 0) {
+ if (dst != NULL)
+ sprintf(&dst[len], span_fmt, color[0], color[1], color[2]);
+ len += span_len;
+ }
+ } else if (char_pos == 0 && src[0] != 0) {
+ if (dst != NULL)
+ sprintf(&dst[len], span_fmt, 0, 0, 0);
+ len += span_len;
+ }
+
+ if ((attr & GG_FONT_BOLD) != 0)
+ gg_append(dst, &len, "<b>", 3);
+
+ if ((attr & GG_FONT_ITALIC) != 0)
+ gg_append(dst, &len, "<i>", 3);
+
+ if ((attr & GG_FONT_UNDERLINE) != 0)
+ gg_append(dst, &len, "<u>", 3);
+
+ if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) {
+ if (dst != NULL) {
+ sprintf(&dst[len], img_fmt,
+ format_[format_idx + 9],
+ format_[format_idx + 8],
+ format_[format_idx + 7],
+ format_[format_idx + 6],
+ format_[format_idx + 5],
+ format_[format_idx + 4],
+ format_[format_idx + 3],
+ format_[format_idx + 2]);
+ }
+
+ len += img_len;
+ format_idx += 10;
+ }
+
+ old_attr = attr;
+ }
+
+ /* Doklej znak zachowujÄ…c htmlowe escapowanie. */
+
+ switch (src[i]) {
+ case '&':
+ gg_append(dst, &len, "&amp;", 5);
+ break;
+ case '<':
+ gg_append(dst, &len, "&lt;", 4);
+ break;
+ case '>':
+ gg_append(dst, &len, "&gt;", 4);
+ break;
+ case '\'':
+ gg_append(dst, &len, "&apos;", 6);
+ break;
+ case '\"':
+ gg_append(dst, &len, "&quot;", 6);
+ break;
+ case '\n':
+ gg_append(dst, &len, "<br>", 4);
+ break;
+ case '\r':
+ case 0:
+ break;
+ default:
+ if (dst != NULL)
+ dst[len] = src[i];
+ len++;
+ }
+
+ /* SprawdĹş, czy bajt nie jest kontynuacjÄ… znaku unikodowego. */
+
+ if ((src[i] & 0xc0) != 0xc0)
+ char_pos++;
+
+ if (src[i] == 0)
+ break;
+ }
+
+ /* Zamknij tagi. */
+
+ if ((old_attr & GG_FONT_UNDERLINE) != 0)
+ gg_append(dst, &len, "</u>", 4);
+
+ if ((old_attr & GG_FONT_ITALIC) != 0)
+ gg_append(dst, &len, "</i>", 4);
+
+ if ((old_attr & GG_FONT_BOLD) != 0)
+ gg_append(dst, &len, "</b>", 4);
+
+ if (src[0] != 0)
+ gg_append(dst, &len, "</span>", 7);
+
+ if (dst != NULL)
+ dst[len] = 0;
+
+ return len;
+}
+
+/**
+ * Wysyła wiadomość formatowaną w ramach konferencji.
+ *
+ * Zwraca losowy numer sekwencyjny, który można zignorować albo wykorzystać
+ * do potwierdzenia.
+ *
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomości
+ * \param recipients_count Liczba adresatĂłw
+ * \param recipients WskaĹşnik do tablicy z numerami adresatĂłw
+ * \param message Treść wiadomości
+ * \param format Informacje o formatowaniu
+ * \param formatlen Długość informacji o formatowaniu
+ *
+ * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ *
+ * \ingroup messages
+ */
+int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen)
+{
+ struct gg_send_msg s;
+ struct gg_send_msg80 s80;
+ struct gg_msg_recipients r;
+ char *cp_msg = NULL;
+ char *utf_msg = NULL;
+ char *html_msg = NULL;
+ int seq_no;
+ int i, j, k;
+ uin_t *recps;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ 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;
+
+ 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 = (int)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 + 3, formatlen - 3);
+
+ html_msg = malloc(len + 1);
+
+ if (html_msg == NULL) {
+ seq_no = -1;
+ goto cleanup;
+ }
+
+ gg_convert_to_html(html_msg, utf_msg, format + 3, formatlen - 3);
+
+ s80.seq = gg_fix32(seq_no);
+ s80.msgclass = gg_fix32(msgclass);
+ s80.offset_plain = gg_fix32(sizeof(s80) + (uint32_t)strlen(html_msg) + 1);
+ s80.offset_attr = gg_fix32(sizeof(s80) + (uint32_t)strlen(html_msg) + 1 + (uint32_t)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]);
+
+ 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;
+}
+
+/**
+ * Wysyła wiadomość binarną przeznaczoną dla klienta.
+ *
+ * Wiadomości między klientami przesyła się np. w celu wywołania zwrotnego
+ * połączenia bezpośredniego. Funkcja zwraca losowy numer sekwencyjny,
+ * który można zignorować albo wykorzystać do potwierdzenia.
+ *
+ * \param sess Struktura sesji
+ * \param msgclass Klasa wiadomości
+ * \param recipient Numer adresata
+ * \param message Treść wiadomości
+ * \param message_len Długość wiadomości
+ *
+ * \return Numer sekwencyjny wiadomości lub -1 w przypadku błędu.
+ *
+ * \ingroup messages
+ */
+int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len)
+{
+ struct gg_send_msg s;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_ctcp(%p, %d, %u, ...);\n", sess, msgclass, recipient);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ s.recipient = gg_fix32(recipient);
+ s.seq = gg_fix32(0);
+ s.msgclass = gg_fix32(msgclass);
+
+ return gg_send_packet(sess, GG_SEND_MSG, &s, sizeof(s), message, message_len, NULL);
+}
+
+/**
+ * Wysyła żądanie obrazka o podanych parametrach.
+ *
+ * Wiadomości obrazkowe nie zawierają samych obrazków, a tylko ich rozmiary
+ * i sumy kontrolne. Odbiorca najpierw szuka obrazków w swojej pamięci
+ * podręcznej i dopiero gdy ich nie znajdzie, wysyła żądanie do nadawcy.
+ * Wynik zostanie przekazany zdarzeniem \c GG_EVENT_IMAGE_REPLY.
+ *
+ * \param sess Struktura sesji
+ * \param recipient Numer adresata
+ * \param size Rozmiar obrazka w bajtach
+ * \param crc32 Suma kontrola obrazka
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup messages
+ */
+int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32)
+{
+ struct gg_send_msg s;
+ struct gg_msg_image_request r;
+ char dummy = 0;
+ int res;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_request(%p, %d, %u, 0x%.4x);\n", sess, recipient, size, crc32);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ if (size < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ s.recipient = gg_fix32(recipient);
+ s.seq = gg_fix32(0);
+ s.msgclass = gg_fix32(GG_CLASS_MSG);
+
+ r.flag = 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) {
+ struct gg_image_queue *q = malloc(sizeof(*q));
+ char *buf;
+
+ if (!q) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image queue\n");
+ return -1;
+ }
+
+ buf = malloc(size);
+ if (size && !buf)
+ {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_image_request() not enough memory for image\n");
+ free(q);
+ return -1;
+ }
+
+ memset(q, 0, sizeof(*q));
+
+ q->sender = recipient;
+ q->size = size;
+ q->crc32 = crc32;
+ q->image = buf;
+
+ if (!sess->images)
+ sess->images = q;
+ else {
+ struct gg_image_queue *qq;
+
+ for (qq = sess->images; qq->next; qq = qq->next)
+ ;
+
+ qq->next = q;
+ }
+ }
+
+ return res;
+}
+
+/**
+ * Wysyła żądany obrazek.
+ *
+ * \param sess Struktura sesji
+ * \param recipient Numer adresata
+ * \param filename Nazwa pliku
+ * \param image Bufor z obrazkiem
+ * \param size Rozmiar obrazka
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup messages
+ */
+int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size)
+{
+ struct gg_msg_image_reply *r;
+ struct gg_send_msg s;
+ const char *tmp;
+ char buf[1910];
+ int res = -1;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_image_reply(%p, %d, \"%s\", %p, %d);\n", sess, recipient, filename, image, size);
+
+ if (!sess || !filename || !image) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ if (size < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* wytnij ścieżki, zostaw tylko nazwę pliku */
+ while ((tmp = strrchr(filename, '/')) || (tmp = strrchr(filename, '\\')))
+ filename = tmp + 1;
+
+ if (strlen(filename) < 1 || strlen(filename) > 1024) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ s.recipient = gg_fix32(recipient);
+ s.seq = gg_fix32(0);
+ s.msgclass = gg_fix32(GG_CLASS_MSG);
+
+ buf[0] = 0;
+ r = (void*) &buf[1];
+
+ r->flag = 0x05;
+ r->size = gg_fix32(size);
+ r->crc32 = gg_fix32(gg_crc32(0, (unsigned char*) image, size));
+
+ while (size > 0) {
+ int buflen, chunklen;
+
+ /* \0 + struct gg_msg_image_reply */
+ buflen = sizeof(struct gg_msg_image_reply) + 1;
+
+ /* w pierwszym kawałku jest nazwa pliku */
+ if (r->flag == 0x05) {
+ strcpy(buf + buflen, filename);
+ buflen += (int)strlen(filename) + 1;
+ }
+
+ chunklen = (size >= (int)sizeof(buf) - buflen) ? ((int)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)
+ break;
+
+ r->flag = 0x06;
+ }
+
+ return res;
+}
+
+/**
+ * Wysyła do serwera listę kontaktów.
+ *
+ * Funkcja informuje serwer o liście kontaktów, których statusy będą
+ * obserwowane lub kontaktĂłw, ktĂłre bedÄ… blokowane. Dla kaĹĽdego z \c count
+ * kontaktĂłw tablica \c userlist zawiera numer, a tablica \c types rodzaj
+ * kontaktu (\c GG_USER_NORMAL, \c GG_USER_OFFLINE, \c GG_USER_BLOCKED).
+ *
+ * Listę kontaktów należy \b zawsze wysyłać po połączeniu, nawet jeśli
+ * jest pusta.
+ *
+ * \param sess Struktura sesji
+ * \param userlist WskaĹşnik do tablicy numerĂłw kontaktĂłw
+ * \param types WskaĹşnik do tablicy rodzajĂłw kontaktĂłw
+ * \param count Liczba kontaktĂłw
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup contacts
+ */
+int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count)
+{
+ struct gg_notify *n;
+ uin_t *u;
+ char *t;
+ int i, res = 0;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_notify_ex(%p, %p, %p, %d);\n", sess, userlist, types, count);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ if (!userlist || !count)
+ return gg_send_packet(sess, GG_LIST_EMPTY, NULL);
+
+ while (count > 0) {
+ int part_count, packet_type;
+
+ if (count > 400) {
+ part_count = 400;
+ packet_type = GG_NOTIFY_FIRST;
+ } else {
+ part_count = count;
+ packet_type = GG_NOTIFY_LAST;
+ }
+
+ if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count)))
+ return -1;
+
+ for (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;
+ break;
+ }
+
+ count -= part_count;
+ userlist += part_count;
+ types += part_count;
+
+ free(n);
+ }
+
+ return res;
+}
+
+/**
+ * Wysyła do serwera listę kontaktów.
+ *
+ * Funkcja jest odpowiednikiem \c gg_notify_ex(), gdzie wszystkie kontakty
+ * sÄ… rodzaju \c GG_USER_NORMAL.
+ *
+ * \param sess Struktura sesji
+ * \param userlist WskaĹşnik do tablicy numerĂłw kontaktĂłw
+ * \param count Liczba kontaktĂłw
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup contacts
+ */
+int gg_notify(struct gg_session *sess, uin_t *userlist, int count)
+{
+ struct gg_notify *n;
+ uin_t *u;
+ int i, res = 0;
+
+ 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;
+ }
+
+ if (!userlist || !count)
+ return gg_send_packet(sess, GG_LIST_EMPTY, NULL);
+
+ while (count > 0) {
+ int part_count, packet_type;
+
+ if (count > 400) {
+ part_count = 400;
+ packet_type = GG_NOTIFY_FIRST;
+ } else {
+ part_count = count;
+ packet_type = GG_NOTIFY_LAST;
+ }
+
+ if (!(n = (struct gg_notify*) malloc(sizeof(*n) * part_count)))
+ return -1;
+
+ for (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);
+ break;
+ }
+
+ free(n);
+
+ userlist += part_count;
+ count -= part_count;
+ }
+
+ return res;
+}
+
+/**
+ * Dodaje kontakt.
+ *
+ * Dodaje do listy kontaktów dany numer w trakcie połączenia. Aby zmienić
+ * rodzaj kontaktu (np. z normalnego na zablokowany), należy najpierw usunąć
+ * poprzedni rodzaj, poniewaĹĽ serwer operuje na maskach bitowych.
+ *
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
+ * \param type Rodzaj kontaktu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup contacts
+ */
+int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type)
+{
+ struct gg_add_remove a;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_add_notify_ex(%p, %u, %d);\n", sess, uin, type);
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ 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);
+}
+
+/**
+ * Usuwa kontakt.
+ *
+ * Funkcja jest odpowiednikiem \c gg_add_notify_ex(), gdzie rodzaj wszystkich
+ * kontaktĂłw to \c GG_USER_NORMAL.
+ *
+ * \param sess Struktura sesji
+ * \param uin Numer kontaktu
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup contacts
+ */
+int gg_remove_notify(struct gg_session *sess, uin_t uin)
+{
+ return gg_remove_notify_ex(sess, uin, GG_USER_NORMAL);
+}
+
+/**
+ * Wysyła do serwera zapytanie dotyczące listy kontaktów.
+ *
+ * Funkcja służy do importu lub eksportu listy kontaktów do serwera.
+ * W odróżnieniu od funkcji \c gg_notify(), ta lista kontaktów jest przez
+ * serwer jedynie przechowywana i nie ma wpływu na połączenie. Format
+ * listy kontaktów jest ignorowany przez serwer, ale ze względu na
+ * kompatybilność z innymi klientami, należy przechowywać dane w tym samym
+ * formacie co oryginalny klient Gadu-Gadu.
+ *
+ * Program nie musi się przejmować fragmentacją listy kontaktów wynikającą
+ * z protokołu -- wysyła i odbiera kompletną listę.
+ *
+ * \param sess Struktura sesji
+ * \param type Rodzaj zapytania
+ * \param request Treść zapytania (może być równe NULL)
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup importexport
+ */
+int gg_userlist_request(struct gg_session *sess, char type, const char *request)
+{
+ int len;
+
+ if (!sess) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ errno = ENOTCONN;
+ return -1;
+ }
+
+ if (!request) {
+ sess->userlist_blocks = 1;
+ return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), NULL);
+ }
+
+ len = (int)strlen(request);
+
+ sess->userlist_blocks = 0;
+
+ while (len > 2047) {
+ sess->userlist_blocks++;
+
+ if (gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, 2047, NULL) == -1)
+ return -1;
+
+ if (type == GG_USERLIST_PUT)
+ type = GG_USERLIST_PUT_MORE;
+
+ request += 2047;
+ len -= 2047;
+ }
+
+ sess->userlist_blocks++;
+
+ return gg_send_packet(sess, GG_USERLIST_REQUEST, &type, sizeof(type), request, len, NULL);
+}
+
+/**
+ * Informuje rozmówcę o pisaniu wiadomości.
+ *
+ * \param sess Struktura sesji
+ * \param recipient Numer adresata
+ * \param length Długość wiadomości lub 0 jeśli jest pusta
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup messages
+ */
+int gg_typing_notification(struct gg_session *sess, uin_t recipient, int length){
+ struct gg_typing_notification pkt;
+ uin_t uin;
+
+ pkt.length = gg_fix16((uint16_t)length);
+ uin = gg_fix32(recipient);
+ memcpy(&pkt.uin, &uin, sizeof(uin_t));
+
+ return gg_send_packet(sess, GG_TYPING_NOTIFICATION, &pkt, sizeof(pkt), NULL);
+}
+
+/**
+ * Rozłącza inną sesję multilogowania.
+ *
+ * \param gs Struktura sesji
+ * \param conn_id Sesja do rozłączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup login
+ */
+int gg_multilogon_disconnect(struct gg_session *gs, gg_multilogon_id_t conn_id)
+{
+ struct gg_multilogon_disconnect pkt;
+
+ pkt.conn_id = conn_id;
+
+ return gg_send_packet(gs, GG_MULTILOGON_DISCONNECT, &pkt, sizeof(pkt), NULL);
+}
+
+/* @} */
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/protocols/Gadu-Gadu/libgadu/libgadu.h b/protocols/Gadu-Gadu/libgadu/libgadu.h
new file mode 100644
index 0000000000..08d8bb6912
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/libgadu.h
@@ -0,0 +1,2311 @@
+/* coding: UTF-8 */
+/* $Id: libgadu.h 13762 2011-08-09 12:35:16Z dezred $ */
+
+/*
+ * (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Robert J. WoĹşny <speedy@ziew.org>
+ * Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ * Tomasz Chiliński <chilek@chilan.com>
+ * Piotr Wysocki <wysek@linux.bydg.org>
+ * Dawid Jarosz <dawjar@poczta.onet.pl>
+ * Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/**
+ * \file libgadu.h
+ *
+ * \brief Główny plik nagłówkowy biblioteki
+ */
+
+#ifndef __GG_LIBGADU_H
+#define __GG_LIBGADU_H
+
+/* Defined if libgadu should be compatible with Miranda. */
+#define GG_CONFIG_MIRANDA
+
+#ifdef GG_CONFIG_MIRANDA
+#include <m_ssl.h>
+#endif
+
+#if defined(__cplusplus) || defined(_WIN32)
+#pragma pack(push, 1)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdarg.h>
+
+/** \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. */
+#define GG_CONFIG_HAVE_PTHREAD
+
+/* Defined if pthread resolver is the default one. */
+#define 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. */
+/* Visual C++ 6.0 has no long long */
+#if !defined(_MSC_VER) || (_MSC_VER >= 1300)
+#define GG_CONFIG_HAVE_LONG_LONG
+#endif
+
+/* Defined if libgadu was compiled and linked with OpenSSL support. */
+#undef GG_CONFIG_HAVE_OPENSSL
+
+/* Defined if uintX_t types are defined in <stdint.h>. */
+#undef GG_CONFIG_HAVE_STDINT_H
+
+/* Defined if uintX_t types are defined in <inttypes.h>. */
+#undef GG_CONFIG_HAVE_INTTYPES_H
+
+/* Defined if uintX_t types are defined in <sys/inttypes.h>. */
+#undef GG_CONFIG_HAVE_SYS_INTTYPES_H
+
+/* Defined if uintX_t types are defined in <sys/int_types.h>. */
+#undef GG_CONFIG_HAVE_SYS_INT_TYPES_H
+
+/* Defined if uintX_t types are defined in <sys/types.h>. */
+#undef GG_CONFIG_HAVE_SYS_TYPES_H
+
+/* MSC have no va_copy */
+#ifndef _MSC_VER
+#define GG_CONFIG_HAVE_VA_COPY
+#define GG_CONFIG_HAVE___VA_COPY
+#endif
+
+#if defined(GG_CONFIG_HAVE_OPENSSL) && !defined(GG_CONFIG_MIRANDA)
+#include <openssl/ssl.h>
+#endif
+
+#ifdef GG_CONFIG_HAVE_STDINT_H
+#include <stdint.h>
+#else
+# ifdef GG_CONFIG_HAVE_INTTYPES_H
+# include <inttypes.h>
+# else
+# ifdef GG_CONFIG_HAVE_SYS_INTTYPES_H
+# include <sys/inttypes.h>
+# else
+# ifdef GG_CONFIG_HAVE_SYS_INT_TYPES_H
+# include <sys/int_types.h>
+# else
+# ifdef GG_CONFIG_HAVE_SYS_TYPES_H
+# include <sys/types.h>
+# else
+
+#ifndef __AC_STDINT_H
+#define __AC_STDINT_H
+
+/* ISO C 9X: 7.18 Integer types <stdint.h> */
+
+typedef unsigned char uint8_t;
+typedef unsigned short uint16_t;
+typedef unsigned int uint32_t;
+#ifdef GG_CONFIG_HAVE_LONG_LONG
+typedef unsigned long long uint64_t;
+#define GG_CONFIG_HAVE_UINT64_T
+#endif
+
+#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
+
+#ifdef _WIN32
+# define kill(pid,sig)
+# ifdef _MSC_VER
+# define vsnprintf _vsnprintf
+# define stat _stat
+# ifndef strdup
+# define strdup _strdup
+# endif
+# define strncasecmp _strnicmp
+# define vsnprintf _vsnprintf
+# define snprintf _snprintf
+# define strcasecmp _stricmp
+# define GG_CONFIG_HAVE_STRTOULL
+# define strtoull _strtoui64
+# endif
+# define gg_sock_write(sock,buf,len) send(sock,(void *)(buf),len,0)
+# define gg_sock_read(sock,buf,len) recv(sock,(void *)(buf),len,0)
+# define gg_sock_close(sock) closesocket(sock)
+# define gg_getsockopt(sock,level,name,val,len) getsockopt(sock,level,name,(char *)val,len)
+#else
+ typedef int SOCKET;
+# define gg_sock_write write
+# define gg_sock_read read
+# define gg_sock_close close
+# define gg_getsockopt getsockopt
+#endif
+
+/** \endcond */
+
+/**
+ * Numer Gadu-Gadu.
+ */
+typedef uint32_t uin_t;
+
+/**
+ * Identyfikator połączenia bezpośredniego Gadu-Gadu 7.x.
+ */
+typedef struct {
+ uint8_t id[8];
+} gg_dcc7_id_t;
+
+/**
+ * Identyfikator sesji multilogowania.
+ */
+typedef struct {
+ uint8_t id[8];
+} gg_multilogon_id_t;
+
+/**
+ * Makro deklarujÄ…ce pola wspĂłlne dla struktur sesji.
+ */
+#define gg_common_head(x) \
+ SOCKET fd; /**< Obserwowany deskryptor */ \
+ int check; /**< Informacja o ĹĽÄ…daniu odczytu/zapisu (patrz \ref gg_check_t) */ \
+ int state; /**< Aktualny stan połączenia (patrz \ref gg_state_t) */ \
+ int error; /**< Kod błędu dla \c GG_STATE_ERROR (patrz \ref gg_error_t) */ \
+ int type; /**< Rodzaj sesji (patrz \ref gg_session_t) */ \
+ int id; /**< Identyfikator sesji */ \
+ int timeout; /**< Czas pozostały do zakończenia stanu */ \
+ int (*callback)(x*); /**< Funkcja zwrotna */ \
+ void (*destroy)(x*); /**< Funkcja zwalniania zasobĂłw */
+
+/**
+ * Struktura wspólna dla wszystkich sesji i połączeń. Pozwala na proste
+ * rzutowanie niezależne od rodzaju połączenia.
+ */
+struct gg_common {
+ gg_common_head(struct gg_common)
+};
+
+struct gg_image_queue;
+
+struct gg_dcc7;
+
+struct gg_dcc7_relay;
+
+/**
+ * SposĂłb rozwiÄ…zywania nazw serwerĂłw.
+ */
+typedef enum {
+ GG_RESOLVER_DEFAULT = 0, /**< Domyślny sposób rozwiązywania nazw (jeden z poniższych) */
+ GG_RESOLVER_FORK, /**< RozwiÄ…zywanie nazw bazujÄ…ce na procesach */
+ GG_RESOLVER_PTHREAD, /**< RozwiÄ…zywanie nazw bazujÄ…ce na wÄ…tkach */
+ GG_RESOLVER_CUSTOM, /**< Funkcje rozwiÄ…zywania nazw dostarczone przed aplikacjÄ™ */
+ GG_RESOLVER_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.
+ *
+ * Tworzona przez funkcjÄ™ \c gg_login(), zwalniana przez \c gg_free_session().
+ *
+ * \ingroup login
+ */
+struct gg_session {
+ gg_common_head(struct gg_session)
+
+ int async; /**< Flaga połączenia asynchronicznego */
+ int pid; /**< Numer procesu rozwiÄ…zujÄ…cego nazwÄ™ serwera */
+ int port; /**< Port serwera */
+ int seq; /**< Numer sekwencyjny ostatniej wiadomości */
+ int last_pong; /**< Czas otrzymania ostatniej ramki utrzymaniowej */
+ int last_event; /**< Czas otrzymania ostatniego pakietu */
+
+ struct gg_event *event; /**< Zdarzenie po wywołaniu \c callback */
+
+ uint32_t proxy_addr; /**< Adres serwera pośredniczącego */
+ uint16_t proxy_port; /**< Port serwera pośredniczącego */
+
+ uint32_t hub_addr; /**< Adres huba po rozwiÄ…zaniu nazwy */
+ uint32_t server_addr; /**< Adres serwera otrzymany od huba */
+
+ uint32_t client_addr; /**< Adres gniazda dla połączeń bezpośrednich 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; /**< 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; /**< WĹ‚asny numer Gadu-Gadu */
+ char *password; /**< Hasło (zwalniane po użyciu) */
+
+ int initial_status; /**< PoczÄ…tkowy status */
+ int status; /**< Aktualny status */
+
+ char *recv_buf; /**< Bufor na 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 protokołu (bez flag) */
+ char *client_version; /**< Wersja klienta */
+ int last_sysmsg; /**< Numer ostatniej wiadomości systemowej */
+
+ char *initial_descr; /**< PoczÄ…tkowy opis statusu */
+
+ void *resolver; /**< Dane prywatne procesu lub wÄ…tku rozwiÄ…zujÄ…cego nazwÄ™ serwera */
+
+ char *header_buf; /**< Bufor na początek nagłówka pakietu */
+ unsigned int header_done; /**< Liczba wczytanych bajtów nagłówka pakietu */
+
+#ifdef GG_CONFIG_MIRANDA
+ HSSL ssl;
+ int tls; /**< Flaga połączenia szyfrowanego */
+#elif GG_CONFIG_HAVE_OPENSSL
+ SSL *ssl; /**< Struktura TLS */
+ SSL_CTX *ssl_ctx; /**< Kontekst sesji TLS */
+#else
+ void *ssl; /**< Struktura TLS */
+ void *ssl_ctx; /**< Kontekst sesji TLS */
+#endif
+
+ int image_size; /**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
+
+ char *userlist_reply; /**< Bufor z odbieranÄ… listÄ… kontaktĂłw */
+
+ int userlist_blocks; /**< Liczba części listy kontaktów */
+
+ struct gg_image_queue *images; /**< Lista wczytywanych obrazkĂłw */
+
+ int hash_type; /**< Rodzaj funkcji skrótu hasła (\c GG_LOGIN_HASH_GG32 lub \c GG_LOGIN_HASH_SHA1) */
+
+ char *send_buf; /**< Bufor z danymi do wysłania */
+ int send_left; /**< Liczba bajtów do wysłania */
+
+ struct gg_dcc7 *dcc7_list; /**< Lista połączeń bezpośrednich skojarzonych z sesją */
+
+ int soft_timeout; /**< Flaga mówiąca, że po przekroczeniu \c timeout należy wywołać \c gg_watch_fd() */
+
+ int protocol_flags; /**< Flagi protokołu */
+
+ gg_encoding_t encoding; /**< Rodzaj kodowania znakĂłw */
+
+ gg_resolver_t resolver_type; /**< SposĂłb rozwiÄ…zywania nazw serwerĂłw */
+ int (*resolver_start)(SOCKET *fd, void **private_data, const char *hostname); /**< Funkcja rozpoczynajÄ…ca rozwiÄ…zywanie nazwy */
+ void (*resolver_cleanup)(void **private_data, int force); /**< Funkcja zwalniajÄ…ca zasoby po rozwiÄ…zaniu nazwy */
+
+ int protocol_features; /**< Opcje protokołu */
+ int status_flags; /**< Flagi statusu */
+};
+
+/**
+ * Połączenie HTTP.
+ *
+ * Tworzone przez \c gg_http_connect(), zwalniane przez \c gg_http_free().
+ *
+ * \ingroup http
+ */
+struct gg_http {
+ gg_common_head(struct gg_http)
+
+ int async; /**< Flaga połączenia asynchronicznego */
+ int pid; /**< Identyfikator procesu rozwiÄ…zujÄ…cego nazwÄ™ serwera */
+ int port; /**< Port */
+
+ char *query; /**< Zapytanie HTTP */
+ char *header; /**< Odebrany nagłówek */
+ int header_size; /**< Rozmiar wczytanego nagłówka */
+ char *body; /**< Odebrana strona */
+ unsigned int body_size; /**< Rozmiar strony */
+
+ void *data; /**< Dane prywatne usługi HTTP */
+
+ char *user_data; /**< Dane prywatne uĹĽytkownika (nie sÄ… zwalniane) */
+
+ void *resolver; /**< Dane prywatne procesu lub wÄ…tku rozwiÄ…zujÄ…cego nazwÄ™ */
+
+ unsigned int body_done; /**< Liczba odebranych bajtĂłw strony */
+
+ gg_resolver_t resolver_type; /**< SposĂłb rozwiÄ…zywania nazw serwerĂłw */
+ int (*resolver_start)(SOCKET *fd, void **private_data, const char *hostname); /**< Funkcja rozpoczynajÄ…ca rozwiÄ…zywanie nazwy */
+ void (*resolver_cleanup)(void **private_data, int force); /**< Funkcja zwalniajÄ…ca zasoby po rozwiÄ…zaniu nazwy */
+};
+
+/** \cond ignore */
+
+#ifdef __GNUC__
+#define GG_PACKED __attribute__ ((packed))
+#ifndef GG_IGNORE_DEPRECATED
+#define GG_DEPRECATED __attribute__ ((deprecated))
+#else
+#define GG_DEPRECATED
+#endif
+#else
+#define GG_PACKED
+#define GG_DEPRECATED
+#endif
+
+/** \endcond */
+
+#define GG_MAX_PATH 276 /**< Maksymalny rozmiar nazwy pliku w strukturze \c gg_file_info */
+
+/**
+ * Odpowiednik struktury WIN32_FIND_DATA z API WIN32.
+ *
+ * Wykorzystywana przy połączeniach bezpośrednich do wersji Gadu-Gadu 6.x.
+ */
+struct gg_file_info {
+ uint32_t mode; /**< dwFileAttributes */
+ uint32_t ctime[2]; /**< ftCreationTime */
+ uint32_t atime[2]; /**< ftLastAccessTime */
+ uint32_t mtime[2]; /**< ftLastWriteTime */
+ uint32_t size_hi; /**< nFileSizeHigh */
+ uint32_t size; /**< nFileSizeLow */
+ uint32_t reserved0; /**< dwReserved0 */
+ uint32_t reserved1; /**< dwReserved1 */
+ unsigned char filename[GG_MAX_PATH - 14]; /**< cFileName */
+ unsigned char short_filename[14]; /**< cAlternateFileName */
+} /** \cond ignore */ GG_PACKED /** \endcond */;
+
+/**
+ * Połączenie bezpośrednie do wersji Gadu-Gadu 6.x.
+ *
+ * Tworzone przez \c gg_dcc_socket_create(), \c gg_dcc_get_file(),
+ * \c gg_dcc_send_file() lub \c gg_dcc_voice_chat(), zwalniane przez
+ * \c gg_dcc_free().
+ *
+ * \ingroup dcc6
+ */
+struct gg_dcc {
+ gg_common_head(struct gg_dcc)
+
+ struct gg_event *event; /**< Zdarzenie po wywołaniu \c callback */
+
+ int active; /**< Flaga połączenia aktywnego (nieużywana) */
+ int port; /**< Port gniazda nasłuchującego */
+ uin_t uin; /**< WĹ‚asny numer Gadu-Gadu */
+ uin_t peer_uin; /**< Numer Gadu-Gadu drugiej strony połączenia */
+ int file_fd; /**< deskryptor pliku */
+ unsigned int offset; /**< Położenie w pliku */
+ unsigned int chunk_size;
+ /**< Rozmiar kawałka pliku */
+ unsigned int chunk_offset;
+ /**< Położenie w aktualnym kawałku pliku */
+ struct gg_file_info file_info;
+ /**< Informacje o pliku */
+ int established; /**< Flaga ustanowienia połączenia */
+ char *voice_buf; /**< Bufor na pakiet połączenia głosowego */
+ int incoming; /**< Flaga połączenia przychodzącego */
+ char *chunk_buf; /**< Bufor na fragment danych */
+ uint32_t remote_addr; /**< Adres drugiej strony */
+ uint16_t remote_port; /**< Port drugiej strony */
+
+#ifdef GG_CONFIG_MIRANDA
+ void *contact;
+ char *folder;
+ uint32_t tick;
+#endif
+};
+
+#define GG_DCC7_HASH_LEN 20 /**< Maksymalny rozmiar skrótu pliku w połączeniach bezpośrenich */
+#define GG_DCC7_FILENAME_LEN 255 /**< Maksymalny rozmiar nazwy pliku w połączeniach bezpośrednich */
+#define GG_DCC7_INFO_LEN 32 /**< Maksymalny rozmiar informacji o połączeniach bezpośrednich */
+#define GG_DCC7_INFO_HASH_LEN 32 /**< Maksymalny rozmiar skrótu ip informacji o połączeniach bezpośrednich */
+
+/**
+ * Połączenie bezpośrednie od wersji Gadu-Gadu 7.x.
+ *
+ * \ingroup dcc7
+ */
+struct gg_dcc7 {
+ gg_common_head(struct gg_dcc7)
+
+ gg_dcc7_id_t cid; /**< Identyfikator połączenia */
+
+ struct gg_event *event; /**< Struktura zdarzenia */
+
+ uin_t uin; /**< WĹ‚asny numer Gadu-Gadu */
+ uin_t peer_uin; /**< Numer Gadu-Gadu drugiej strony połączenia */
+
+ int file_fd; /**< Deskryptor przesyłanego pliku */
+ unsigned int offset; /**< Aktualne położenie w przesyłanym pliku */
+ unsigned int size; /**< Rozmiar przesyłanego pliku */
+ unsigned char filename[GG_DCC7_FILENAME_LEN + 1];
+ /**< Nazwa przesyłanego pliku */
+ unsigned char hash[GG_DCC7_HASH_LEN];
+ /**< Skrót SHA1 przesyłanego pliku */
+
+ int dcc_type; /**< Rodzaj połączenia bezpośredniego */
+ int established; /**< Flaga ustanowienia połączenia */
+ int incoming; /**< Flaga połączenia przychodzącego */
+ int reverse; /**< Flaga połączenia zwrotnego */
+
+ uint32_t local_addr; /**< Adres lokalny */
+ uint16_t local_port; /**< Port lokalny */
+
+ uint32_t remote_addr; /**< Adres drugiej strony */
+ uint16_t remote_port; /**< Port drugiej strony */
+
+ struct gg_session *sess;
+ /**< Sesja do której przypisano połączenie */
+ struct gg_dcc7 *next; /**< Następne połączenie w liście */
+
+ int soft_timeout; /**< Flaga mówiąca, że po przekroczeniu \c timeout należy wywołać \c gg_dcc7_watch_fd() */
+ int seek; /**< Flaga mówiąca, że można zmieniać położenie w wysyłanym pliku */
+
+ void *resolver; /**< Dane prywatne procesu lub wÄ…tku rozwiÄ…zujÄ…cego nazwÄ™ serwera */
+
+ int relay; /**< Flaga mĂłwiÄ…ca, ĹĽe laczymy sie przez serwer */
+ int relay_index; /**< Numer serwera pośredniczącego, do którego się łączymy */
+ int relay_count; /**< Rozmiar listy serwerów pośredniczących */
+ struct gg_dcc7_relay *relay_list; /**< Lista serwerów pośredniczących */
+
+#ifdef GG_CONFIG_MIRANDA
+ void *contact;
+ char *folder;
+ uint32_t tick;
+#endif
+};
+
+/**
+ * Rodzaj sesji.
+ */
+enum gg_session_t {
+ GG_SESSION_GG = 1, /**< Połączenie z serwerem Gadu-Gadu */
+ GG_SESSION_HTTP, /**< Połączenie HTTP */
+ GG_SESSION_SEARCH, /**< Wyszukiwanie w katalogu publicznym (nieaktualne) */
+ GG_SESSION_REGISTER, /**< Rejestracja nowego konta */
+ GG_SESSION_REMIND, /**< Przypominanie hasła */
+ GG_SESSION_PASSWD, /**< Zmiana hasła */
+ GG_SESSION_CHANGE, /**< Zmiana informacji w katalogu publicznym (nieaktualne) */
+ GG_SESSION_DCC, /**< Połączenie bezpośrednie (do wersji 6.x) */
+ GG_SESSION_DCC_SOCKET, /**< Gniazdo nasłuchujące (do wersji 6.x) */
+ GG_SESSION_DCC_SEND, /**< Wysyłanie pliku (do wersji 6.x) */
+ GG_SESSION_DCC_GET, /**< Odbieranie pliku (do wersji 6.x) */
+ GG_SESSION_DCC_VOICE, /**< Rozmowa głosowa (do wersji 6.x) */
+ GG_SESSION_USERLIST_GET, /**< Import listy kontaktĂłw z serwera (nieaktualne) */
+ GG_SESSION_USERLIST_PUT, /**< Eksport listy kontaktĂłw do serwera (nieaktualne) */
+ GG_SESSION_UNREGISTER, /**< Usuwanie konta */
+ GG_SESSION_USERLIST_REMOVE, /**< Usuwanie listy kontaktĂłw z serwera (nieaktualne) */
+ GG_SESSION_TOKEN, /**< Pobieranie tokenu */
+ GG_SESSION_DCC7_SOCKET, /**< Gniazdo nasłuchujące (od wersji 7.x) */
+ GG_SESSION_DCC7_SEND, /**< Wysyłanie pliku (od wersji 7.x) */
+ GG_SESSION_DCC7_GET, /**< Odbieranie pliku (od wersji 7.x) */
+ GG_SESSION_DCC7_VOICE, /**< Rozmowa głosowa (od wersji 7.x) */
+
+ GG_SESSION_USER0 = 256, /**< Rodzaj zadeklarowany dla uĹĽytkownika */
+ GG_SESSION_USER1, /**< Rodzaj zadeklarowany dla uĹĽytkownika */
+ GG_SESSION_USER2, /**< Rodzaj zadeklarowany dla uĹĽytkownika */
+ GG_SESSION_USER3, /**< Rodzaj zadeklarowany dla uĹĽytkownika */
+ GG_SESSION_USER4, /**< Rodzaj zadeklarowany dla uĹĽytkownika */
+ GG_SESSION_USER5, /**< Rodzaj zadeklarowany dla uĹĽytkownika */
+ GG_SESSION_USER6, /**< Rodzaj zadeklarowany dla uĹĽytkownika */
+ GG_SESSION_USER7 /**< Rodzaj zadeklarowany dla uĹĽytkownika */
+};
+
+/**
+ * Aktualny stan sesji.
+ */
+enum gg_state_t {
+ /* wspĂłlne */
+ GG_STATE_IDLE = 0, /**< Nie dzieje siÄ™ nic */
+ GG_STATE_RESOLVING, /**< Oczekiwanie na rozwiÄ…zanie nazwy serwera */
+ GG_STATE_CONNECTING, /**< Oczekiwanie na połączenie */
+ GG_STATE_READING_DATA, /**< Oczekiwanie na dane */
+ GG_STATE_ERROR, /**< Kod błędu w polu \c error */
+
+ /* gg_session */
+ GG_STATE_CONNECTING_HUB, /**< Oczekiwanie na połączenie z hubem */
+ GG_STATE_CONNECTING_GG, /**< Oczekiwanie na połączenie z serwerem */
+ GG_STATE_READING_KEY, /**< Oczekiwanie na klucz */
+ GG_STATE_READING_REPLY, /**< Oczekiwanie na odpowiedĹş serwera */
+ GG_STATE_CONNECTED, /**< Połączono z serwerem */
+
+ /* gg_http */
+ GG_STATE_SENDING_QUERY, /**< Wysłano zapytanie HTTP */
+ GG_STATE_READING_HEADER, /**< Oczekiwanie na nagłówek HTTP */
+ GG_STATE_PARSING, /**< Przetwarzanie danych */
+ GG_STATE_DONE, /**< Połączenie zakończone */
+
+ /* gg_dcc */
+ GG_STATE_LISTENING, /* czeka na połączenia */
+ GG_STATE_READING_UIN_1, /* czeka na uin peera */
+ GG_STATE_READING_UIN_2, /* czeka na swĂłj uin */
+ GG_STATE_SENDING_ACK, /* wysyła potwierdzenie dcc */
+ GG_STATE_READING_ACK, /* czeka na potwierdzenie dcc */
+ GG_STATE_READING_REQUEST, /* czeka na komendÄ™ */
+ GG_STATE_SENDING_REQUEST, /* wysyła komendę */
+ GG_STATE_SENDING_FILE_INFO, /* wysyła informacje o pliku */
+ GG_STATE_READING_PRE_FILE_INFO, /* czeka na pakiet przed file_info */
+ GG_STATE_READING_FILE_INFO, /* czeka na informacje o pliku */
+ GG_STATE_SENDING_FILE_ACK, /* wysyła potwierdzenie pliku */
+ GG_STATE_READING_FILE_ACK, /* czeka na potwierdzenie pliku */
+ GG_STATE_SENDING_FILE_HEADER, /* wysyła nagłówek pliku */
+ GG_STATE_READING_FILE_HEADER, /* czeka na nagłówek */
+ GG_STATE_GETTING_FILE, /* odbiera plik */
+ GG_STATE_SENDING_FILE, /* wysyła plik */
+ GG_STATE_READING_VOICE_ACK, /* czeka na potwierdzenie voip */
+ GG_STATE_READING_VOICE_HEADER, /* czeka na rodzaj bloku voip */
+ GG_STATE_READING_VOICE_SIZE, /* czeka na rozmiar bloku voip */
+ GG_STATE_READING_VOICE_DATA, /* czeka na dane voip */
+ GG_STATE_SENDING_VOICE_ACK, /* wysyła potwierdzenie voip */
+ GG_STATE_SENDING_VOICE_REQUEST, /* wysyła żądanie voip */
+ GG_STATE_READING_TYPE, /* czeka na typ połączenia */
+
+ /* nowe. bez sensu jest to API. */
+ GG_STATE_TLS_NEGOTIATION, /**< Negocjacja połączenia szyfrowanego */
+
+ GG_STATE_REQUESTING_ID, /**< Oczekiwanie na nadanie identyfikatora połączenia bezpośredniego */
+ GG_STATE_WAITING_FOR_ACCEPT, /**< Oczekiwanie na potwierdzenie lub odrzucenie połączenia bezpośredniego */
+ GG_STATE_WAITING_FOR_INFO, /**< Oczekiwanie na informacje o połączeniu bezpośrednim */
+
+ GG_STATE_READING_ID, /**< Odebranie identyfikatora połączenia bezpośredniego */
+ GG_STATE_SENDING_ID, /**< Wysłano identyfikator połączenia bezpośredniego */
+ GG_STATE_RESOLVING_GG, /**< Oczekiwanie na rozwiÄ…zanie nazwy serwera Gadu-Gadu */
+
+ GG_STATE_RESOLVING_RELAY, /**< Oczekiwanie na rozwiązanie nazwy serwera pośredniczącego */
+ GG_STATE_CONNECTING_RELAY, /**< Oczekiwanie na połączenie z serwerem pośredniczącym */
+ GG_STATE_READING_RELAY /**< Odbieranie danych */
+};
+
+/**
+ * Informacja o tym, czy biblioteka chce zapisywać i/lub czytać
+ * z deskryptora. Maska bitowa.
+ *
+ * \ingroup events
+ */
+enum gg_check_t {
+ GG_CHECK_NONE = 0, /**< Nie sprawdzaj niczego */
+ GG_CHECK_WRITE = 1, /**< Sprawdź możliwość zapisu */
+ GG_CHECK_READ = 2 /**< Sprawdź możliwość odczytu */
+};
+
+/**
+ * Parametry połączenia z serwerem Gadu-Gadu. Parametry zostały przeniesione
+ * do struktury, by uniknąć zmian API po rozszerzeniu protokołu i dodaniu
+ * kolejnych opcji połączenia. Część parametrów, które nie są już aktualne
+ * lub nie mają znaczenia, została usunięta z dokumentacji.
+ *
+ * \ingroup login
+ */
+struct gg_login_params {
+ uin_t uin; /**< Numer Gadu-Gadu */
+ char *password; /**< Hasło */
+ int async; /**< Flaga asynchronicznego połączenia (domyślnie nie) */
+ int status; /**< Początkowy status użytkownika (domyślnie \c GG_STATUS_AVAIL) */
+ char *status_descr; /**< Początkowy opis użytkownika (domyślnie brak) */
+ uint32_t server_addr; /**< Adres serwera Gadu-Gadu (domyślnie pobierany automatycznie) */
+ uint16_t server_port; /**< Port serwera Gadu-Gadu (domyślnie pobierany automatycznie) */
+#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_*). */
+ int status_flags; /**< Flagi statusu (flagi GG_STATUS_FLAG_*, patrz \ref status). */
+
+#ifndef DOXYGEN
+ char dummy[1 * sizeof(int)]; /**< \internal Miejsce na kilka kolejnych
+ parametrĂłw, ĹĽeby wraz z dodawaniem kolejnych
+ parametrów nie zmieniał się rozmiar struktury */
+#endif
+
+};
+
+#ifdef GG_CONFIG_MIRANDA
+struct gg_session *gg_login(const struct gg_login_params *p, SOCKET *gg_sock, int *gg_failno);
+#else
+struct gg_session *gg_login(const struct gg_login_params *p);
+#endif
+void gg_free_session(struct gg_session *sess);
+void gg_logoff(struct gg_session *sess);
+int gg_change_status(struct gg_session *sess, int status);
+int gg_change_status_descr(struct gg_session *sess, int status, const char *descr);
+int gg_change_status_descr_time(struct gg_session *sess, int status, const char *descr, int time);
+int gg_change_status_flags(struct gg_session *sess, int flags);
+int gg_send_message(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message);
+int gg_send_message_richtext(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, const unsigned char *format, int formatlen);
+int gg_send_message_confer(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message);
+int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen);
+int gg_send_message_ctcp(struct gg_session *sess, int msgclass, uin_t recipient, const unsigned char *message, int message_len);
+int gg_ping(struct gg_session *sess);
+int gg_userlist_request(struct gg_session *sess, char type, const char *request);
+int gg_image_request(struct gg_session *sess, uin_t recipient, int size, uint32_t crc32);
+int gg_image_reply(struct gg_session *sess, uin_t recipient, const char *filename, const char *image, int size);
+int gg_typing_notification(struct gg_session *sess, uin_t recipient, int length);
+
+uint32_t gg_crc32(uint32_t crc, const unsigned char *buf, int len);
+
+int gg_session_set_resolver(struct gg_session *gs, gg_resolver_t type);
+gg_resolver_t gg_session_get_resolver(struct gg_session *gs);
+int gg_session_set_custom_resolver(struct gg_session *gs, int (*resolver_start)(SOCKET*, 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)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int));
+
+int gg_global_set_resolver(gg_resolver_t type);
+gg_resolver_t gg_global_get_resolver(void);
+int gg_global_set_custom_resolver(int (*resolver_start)(SOCKET*, void**, const char*), void (*resolver_cleanup)(void**, int));
+
+int gg_multilogon_disconnect(struct gg_session *gs, gg_multilogon_id_t conn_id);
+
+/**
+ * Rodzaj zdarzenia.
+ *
+ * \ingroup events
+ */
+enum gg_event_t {
+ GG_EVENT_NONE = 0, /**< Nie wydarzyło się nic wartego uwagi */
+ GG_EVENT_MSG, /**< \brief Otrzymano wiadomość. Przekazuje również wiadomości systemowe od numeru 0. */
+ GG_EVENT_NOTIFY, /**< \brief Informacja o statusach osób z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. Ostatni element tablicy zawiera uin równy 0, a pozostałe pola są niezainicjowane. */
+ GG_EVENT_NOTIFY_DESCR, /**< \brief Informacja o statusie opisowym osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */
+ GG_EVENT_STATUS, /**< \brief Zmiana statusu osoby z listy kontaktów (przed 6.0). Zdarzenie należy obsługiwać, jeśli planuje się używać protokołu w wersji starszej niż domyślna. */
+ GG_EVENT_ACK, /**< Potwierdzenie doręczenia wiadomości */
+ GG_EVENT_PONG, /**< \brief Utrzymanie połączenia. Obecnie serwer nie wysyła już do klienta ramek utrzymania połączenia, polega wyłącznie na wysyłaniu ramek przez klienta. */
+ GG_EVENT_CONN_FAILED, /**< \brief Nie udało się połączyć */
+ GG_EVENT_CONN_SUCCESS, /**< \brief Połączono z serwerem. Pierwszą rzeczą, jaką należy zrobić jest wysłanie listy kontaktów. */
+ GG_EVENT_DISCONNECT, /**< \brief Serwer zrywa połączenie. Zdarza się, gdy równolegle do serwera podłączy się druga sesja i trzeba zerwać połączenie z pierwszą. */
+
+ GG_EVENT_DCC_NEW, /**< Nowe połączenie bezpośrednie (6.x) */
+ GG_EVENT_DCC_ERROR, /**< Błąd połączenia bezpośredniego (6.x) */
+ GG_EVENT_DCC_DONE, /**< Zakończono połączenie bezpośrednie (6.x) */
+ GG_EVENT_DCC_CLIENT_ACCEPT, /**< Moment akceptacji klienta w połączeniu bezpośrednim (6.x) */
+ GG_EVENT_DCC_CALLBACK, /**< Zwrotne połączenie bezpośrednie (6.x) */
+ GG_EVENT_DCC_NEED_FILE_INFO, /**< Należy wypełnić \c file_info dla połączenia bezpośredniego (6.x) */
+ GG_EVENT_DCC_NEED_FILE_ACK, /**< Czeka na potwierdzenie pliku w połączeniu bezpośrednim (6.x) */
+ GG_EVENT_DCC_NEED_VOICE_ACK, /**< Czeka na potwierdzenie rozmowy w połączeniu bezpośrednim (6.x) */
+ GG_EVENT_DCC_VOICE_DATA, /**< Dane bezpośredniego połączenia głosowego (6.x) */
+
+ GG_EVENT_PUBDIR50_SEARCH_REPLY, /**< OdpowiedĹş katalogu publicznego */
+ GG_EVENT_PUBDIR50_READ, /**< Odczytano własne dane z katalogu publicznego */
+ GG_EVENT_PUBDIR50_WRITE, /**< Zmieniono własne dane w katalogu publicznym */
+
+ GG_EVENT_STATUS60, /**< Zmiana statusu osoby z listy kontaktĂłw */
+ GG_EVENT_NOTIFY60, /**< Informacja o statusach osób z listy kontaktów. Ostatni element tablicy zawiera uin równy 0, a pozostałe pola są niezainicjowane. */
+ GG_EVENT_USERLIST, /**< Wynik importu lub eksportu listy kontaktĂłw */
+ GG_EVENT_IMAGE_REQUEST, /**< Żądanie przesłania obrazka z wiadomości */
+ GG_EVENT_IMAGE_REPLY, /**< Przysłano obrazek z wiadomości */
+ GG_EVENT_DCC_ACK, /**< Potwierdzenie transmisji w połączeniu bezpośrednim (6.x) */
+
+ GG_EVENT_DCC7_NEW, /**< Nowe połączenie bezpośrednie (7.x) */
+ GG_EVENT_DCC7_ACCEPT, /**< Zaakceptowano połączenie bezpośrednie (7.x), nowy deskryptor */
+ GG_EVENT_DCC7_REJECT, /**< Odrzucono połączenie bezpośrednie (7.x) */
+ GG_EVENT_DCC7_CONNECTED, /**< Zestawiono połączenie bezpośrednie (7.x), nowy deskryptor */
+ GG_EVENT_DCC7_ERROR, /**< Błąd połączenia bezpośredniego (7.x) */
+ GG_EVENT_DCC7_DONE, /**< Zakończono połączenie bezpośrednie (7.x) */
+ GG_EVENT_DCC7_PENDING, /**< Trwa próba połączenia bezpośredniego (7.x), nowy deskryptor */
+
+ GG_EVENT_XML_EVENT, /**< Otrzymano komunikat systemowy (7.7) */
+ GG_EVENT_DISCONNECT_ACK, /**< \brief Potwierdzenie zakończenia sesji. Informuje o tym, że zmiana stanu na niedostępny z opisem dotarła do serwera i można zakończyć połączenie TCP. */
+ GG_EVENT_XML_ACTION,
+ GG_EVENT_TYPING_NOTIFICATION, /**< Powiadomienie o pisaniu */
+ GG_EVENT_USER_DATA, /**< Informacja o kontaktach */
+ GG_EVENT_MULTILOGON_MSG, /**< Wiadomość wysłana z innej sesji multilogowania */
+ GG_EVENT_MULTILOGON_INFO /**< Informacja o innych sesjach multilogowania */
+};
+
+#define GG_EVENT_SEARCH50_REPLY GG_EVENT_PUBDIR50_SEARCH_REPLY
+
+/**
+ * Powód nieudanego połączenia.
+ */
+enum gg_failure_t {
+ GG_FAILURE_RESOLVING = 1, /**< Nie znaleziono serwera */
+ GG_FAILURE_CONNECTING, /**< Błąd połączenia */
+ GG_FAILURE_INVALID, /**< Serwer zwrócił nieprawidłowe dane */
+ GG_FAILURE_READING, /**< Zerwano połączenie podczas odczytu */
+ GG_FAILURE_WRITING, /**< Zerwano połączenie podczas zapisu */
+ GG_FAILURE_PASSWORD, /**< Nieprawidłowe hasło */
+ GG_FAILURE_404, /**< NieuĹĽywane */
+ GG_FAILURE_TLS, /**< Błąd negocjacji szyfrowanego połączenia */
+ GG_FAILURE_NEED_EMAIL, /**< Serwer rozłączył nas z prośbą o zmianę adresu e-mail */
+ GG_FAILURE_INTRUDER, /**< Zbyt wiele prób połączenia z nieprawidłowym hasłem */
+ GG_FAILURE_UNAVAILABLE /**< Serwery są wyłączone */
+};
+
+/**
+ * Kod błędu danej operacji.
+ *
+ * Nie zawiera przesadnie szczegółowych informacji o powodach błędów, by nie
+ * komplikować ich obsługi. Jeśli wymagana jest większa dokładność, należy
+ * sprawdzić zawartość zmiennej systemowej \c errno.
+ */
+enum gg_error_t {
+ GG_ERROR_RESOLVING = 1, /**< Nie znaleziono hosta */
+ GG_ERROR_CONNECTING, /**< Błąd połączenia */
+ GG_ERROR_READING, /**< BĹ‚Ä…d odczytu/odbierania */
+ GG_ERROR_WRITING, /**< Błąd zapisu/wysyłania */
+
+ GG_ERROR_DCC_HANDSHAKE, /**< BĹ‚Ä…d negocjacji */
+ GG_ERROR_DCC_FILE, /**< BĹ‚Ä…d odczytu/zapisu pliku */
+ GG_ERROR_DCC_EOF, /**< Przedwczesny koniec pliku */
+ GG_ERROR_DCC_NET, /**< Błąd wysyłania/odbierania */
+ GG_ERROR_DCC_REFUSED, /**< Połączenie odrzucone */
+
+ GG_ERROR_DCC7_HANDSHAKE, /**< BĹ‚Ä…d negocjacji */
+ GG_ERROR_DCC7_FILE, /**< BĹ‚Ä…d odczytu/zapisu pliku */
+ GG_ERROR_DCC7_EOF, /**< Przedwczesny koniec pliku */
+ GG_ERROR_DCC7_NET, /**< Błąd wysyłania/odbierania */
+ GG_ERROR_DCC7_REFUSED, /**< Połączenie odrzucone */
+ GG_ERROR_DCC7_RELAY /**< Problem z serwerem pośredniczącym */
+};
+
+/**
+ * Pole zapytania lub odpowiedzi katalogu publicznego.
+ */
+struct gg_pubdir50_entry {
+ int num; /**< Numer wyniku */
+ char *field; /**< Nazwa pola */
+ char *value; /**< Wartość pola */
+} /* GG_DEPRECATED */;
+
+/**
+ * Zapytanie lub odpowiedĹş katalogu publicznego.
+ *
+ * Patrz \c gg_pubdir50_t.
+ */
+struct gg_pubdir50_s {
+ int count; /**< Liczba wynikĂłw odpowiedzi */
+ uin_t next; /**< Numer początkowy następnego zapytania */
+ int type; /**< Rodzaj zapytania */
+ uint32_t seq; /**< Numer sekwencyjny */
+ struct gg_pubdir50_entry *entries; /**< Pola zapytania lub odpowiedzi */
+ int entries_count; /**< Liczba pĂłl */
+} /* GG_DEPRECATED */;
+
+/**
+ * Zapytanie lub odpowiedĹş katalogu publicznego.
+ *
+ * Do pól nie należy się odwoływać bezpośrednio -- wszystkie niezbędne
+ * informacje są dostępne za pomocą funkcji \c gg_pubdir50_*
+ */
+typedef struct gg_pubdir50_s *gg_pubdir50_t;
+
+/**
+ * Opis zdarzeń \c GG_EVENT_MSG i \c GG_EVENT_MULTILOGON_MSG.
+ */
+struct gg_event_msg {
+ uin_t sender; /**< Numer nadawcy/odbiorcy */
+ int msgclass; /**< Klasa wiadomości */
+ 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. W ostatnim elemencie jest równy 0, a pozostałe pola są niezainicjowane. */
+ int status; /**< Nowy status */
+ uint32_t remote_ip; /**< Adres IP dla połączeń bezpośrednich */
+ uint16_t remote_port; /**< Port dla połączeń bezpośrednich */
+ int version; /**< Wersja protokołu */
+ int image_size; /**< Maksymalny rozmiar obsługiwanych obrazków w KiB */
+ char *descr; /**< Opis statusu */
+ 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_XML_ACTION.
+ */
+struct gg_event_xml_action {
+ char *data; /**< Bufor z komunikatem */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_CONNECTED.
+ */
+struct gg_event_dcc7_connected {
+ struct gg_dcc7 *dcc7; /**< Struktura połączenia */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_PENDING.
+ */
+struct gg_event_dcc7_pending {
+ struct gg_dcc7 *dcc7; /**< Struktura połączenia */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_REJECT.
+ */
+struct gg_event_dcc7_reject {
+ struct gg_dcc7 *dcc7; /**< Struktura połączenia */
+ int reason; /**< powĂłd odrzucenia */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_ACCEPT.
+ */
+struct gg_event_dcc7_accept {
+ struct gg_dcc7 *dcc7; /**< Struktura połączenia */
+ int type; /**< Sposób połączenia (P2P, przez serwer) */
+ uint32_t remote_ip; /**< Adres zdalnego klienta */
+ uint16_t remote_port; /**< Port zdalnego klienta */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_DONE.
+ */
+struct gg_event_dcc7_done {
+ struct gg_dcc7 *dcc7; /**< Struktura połączenia */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_DCC7_ERROR.
+ *
+ * \note Odwrotna kolejność pól ma na celu zachowanie ABI.
+ */
+struct gg_event_dcc7_error {
+ enum gg_error_t error; /**< Kod błędu */
+ struct gg_dcc7 *dcc7; /**< Struktura połączenia */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_TYPING_NOTIFICATION.
+ */
+struct gg_event_typing_notification {
+ uin_t uin; /**< Numer rozmĂłwcy */
+ int length; /**< Długość tekstu */
+};
+
+/**
+ * Atrybut uĹĽytkownika.
+ */
+struct gg_event_user_data_attr {
+ int type; /**< Typ atrybutu */
+ char *key; /**< Klucz */
+ char *value; /**< Wartość */
+};
+
+/**
+ * Struktura opisujÄ…ca kontakt w zdarzeniu GG_EVENT_USER_DATA.
+ */
+struct gg_event_user_data_user {
+ uin_t uin; /**< Numer kontaktu */
+ size_t attr_count; /**< Liczba atrybutĂłw */
+ struct gg_event_user_data_attr *attrs; /**< Lista atrybutĂłw */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_USER_DATA.
+ */
+struct gg_event_user_data {
+ int type; /**< Rodzaj informacji o kontaktach */
+ size_t user_count; /**< Liczba kontaktĂłw */
+ struct gg_event_user_data_user *users; /**< Lista kontaktĂłw */
+};
+
+/**
+ * Struktura opisujÄ…ca sesjÄ™ multilogowania.
+ */
+struct gg_multilogon_session {
+ gg_multilogon_id_t id; /**< Identyfikator sesji */
+ char *name; /**< Nazwa sesji (podana w \c gg_login_params.client_version) */
+ uint32_t remote_addr; /**< Adres sesji */
+ int status_flags; /**< Flagi statusu sesji */
+ int protocol_features; /**< Opcje protokolu sesji */
+ time_t logon_time; /**< Czas zalogowania */
+};
+
+/**
+ * Opis zdarzenia \c GG_EVENT_MULTILOGON_INFO.
+ */
+struct gg_event_multilogon_info {
+ int count; /**< Liczba sesji */
+ struct gg_multilogon_session *sessions; /** Lista sesji */
+};
+
+/**
+ * Unia wszystkich zdarzeń zwracanych przez funkcje \c gg_watch_fd(),
+ * \c gg_dcc_watch_fd() i \c gg_dcc7_watch_fd().
+ *
+ * \ingroup events
+ */
+union gg_event_union {
+ enum gg_failure_t failure; /**< Błąd połączenia (\c GG_EVENT_CONN_FAILED) */
+ struct gg_notify_reply *notify; /**< Zmiana statusu kontaktĂłw (\c GG_EVENT_NOTIFY) */
+ struct gg_event_notify_descr notify_descr; /**< Zmiana statusu kontaktĂłw (\c GG_EVENT_NOTIFY_DESCR) */
+ struct gg_event_status status; /**< Zmiana statusu kontaktĂłw (\c GG_EVENT_STATUS) */
+ struct gg_event_status60 status60; /**< Zmiana statusu kontaktĂłw (\c GG_EVENT_STATUS60) */
+ struct gg_event_notify60 *notify60; /**< Zmiana statusu kontaktĂłw (\c GG_EVENT_NOTIFY60) */
+ struct gg_event_msg msg; /**< Otrzymano wiadomość (\c GG_EVENT_MSG) */
+ struct gg_event_ack ack; /**< Potwierdzenie wiadomości (\c GG_EVENT_ACK) */
+ struct gg_event_image_request image_request; /**< Żądanie wysłania obrazka (\c GG_EVENT_IMAGE_REQUEST) */
+ struct gg_event_image_reply image_reply; /**< OdpowiedĹş z obrazkiem (\c GG_EVENT_IMAGE_REPLY) */
+ struct gg_event_userlist userlist; /**< OdpowiedĹş listy kontaktĂłw (\c GG_EVENT_USERLIST) */
+ gg_pubdir50_t pubdir50; /**< OdpowiedĹş katalogu publicznego (\c GG_EVENT_PUBDIR50_*) */
+ struct gg_event_xml_event xml_event; /**< Zdarzenie systemowe (\c GG_EVENT_XML_EVENT) */
+ struct gg_event_xml_action xml_action; /**< Zdarzenie XML (\c GG_EVENT_XML_ACTION) */
+ 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_error dcc7_error_ex; /**< Błąd połączenia bezpośredniego ze wskaźnikiem na strukturę połączenia (\c GG_EVENT_DCC7_ERROR) */
+ struct gg_event_dcc7_connected dcc7_connected; /**< Informacja o zestawieniu połączenia bezpośredniego (\c GG_EVENT_DCC7_CONNECTED) */
+ struct gg_event_dcc7_pending dcc7_pending; /**< Trwa próba połączenia bezpośredniego (\c GG_EVENT_DCC7_PENDING) */
+ struct gg_event_dcc7_reject dcc7_reject; /**< Odrzucono połączenia bezpośredniego (\c GG_EVENT_DCC7_REJECT) */
+ struct gg_event_dcc7_accept dcc7_accept; /**< Zaakceptowano połączenie bezpośrednie (\c GG_EVENT_DCC7_ACCEPT) */
+ struct gg_event_dcc7_done dcc7_done; /**< Zakończono połączenie bezpośrednie (\c GG_EVENT_DCC7_DONE) */
+ struct gg_event_typing_notification typing_notification; /**< Powiadomienie o pisaniu (\c GG_EVENT_TYPING_NOTIFICATION) */
+ struct gg_event_user_data user_data; /**< Informacje o kontaktach */
+ struct gg_event_msg multilogon_msg; /**< Inna sesja wysłała wiadomość (\c GG_EVENT_MULTILOGON_MSG) */
+ struct gg_event_multilogon_info multilogon_info; /**< Informacja o innych sesjach multilogowania (\c GG_EVENT_MULTILOGON_INFO) */
+};
+
+/**
+ * Opis zdarzenia.
+ *
+ * Zwracany przez funkcje \c gg_watch_fd(), \c gg_dcc_watch_fd()
+ * i \c gg_dcc7_watch_fd(). Po przeanalizowaniu należy zwolnić
+ * za pomocÄ… \c gg_event_free().
+ *
+ * \ingroup events
+ */
+struct gg_event {
+ int type; /**< Rodzaj zdarzenia */
+ union gg_event_union event; /**< Informacja o zdarzeniu */
+};
+
+struct gg_event *gg_watch_fd(struct gg_session *sess);
+void gg_event_free(struct gg_event *e);
+
+int gg_notify_ex(struct gg_session *sess, uin_t *userlist, char *types, int count);
+int gg_notify(struct gg_session *sess, uin_t *userlist, int count);
+int gg_add_notify_ex(struct gg_session *sess, uin_t uin, char type);
+int gg_add_notify(struct gg_session *sess, uin_t uin);
+int gg_remove_notify_ex(struct gg_session *sess, uin_t uin, char type);
+int gg_remove_notify(struct gg_session *sess, uin_t uin);
+
+struct gg_http *gg_http_connect(const char *hostname, int port, int async, const char *method, const char *path, const char *header);
+int gg_http_watch_fd(struct gg_http *h);
+void gg_http_stop(struct gg_http *h);
+void gg_http_free(struct gg_http *h);
+
+uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req);
+gg_pubdir50_t gg_pubdir50_new(int type);
+int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value);
+int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq);
+const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field);
+int gg_pubdir50_type(gg_pubdir50_t res);
+int gg_pubdir50_count(gg_pubdir50_t res);
+uin_t gg_pubdir50_next(gg_pubdir50_t res);
+uint32_t gg_pubdir50_seq(gg_pubdir50_t res);
+void gg_pubdir50_free(gg_pubdir50_t res);
+
+#ifndef DOXYGEN
+
+#define GG_PUBDIR50_UIN "FmNumber"
+#define GG_PUBDIR50_STATUS "FmStatus"
+#define GG_PUBDIR50_FIRSTNAME "firstname"
+#define GG_PUBDIR50_LASTNAME "lastname"
+#define GG_PUBDIR50_NICKNAME "nickname"
+#define GG_PUBDIR50_BIRTHYEAR "birthyear"
+#define GG_PUBDIR50_CITY "city"
+#define GG_PUBDIR50_GENDER "gender"
+#define GG_PUBDIR50_GENDER_FEMALE "1"
+#define GG_PUBDIR50_GENDER_MALE "2"
+#define GG_PUBDIR50_GENDER_SET_FEMALE "2"
+#define GG_PUBDIR50_GENDER_SET_MALE "1"
+#define GG_PUBDIR50_ACTIVE "ActiveOnly"
+#define GG_PUBDIR50_ACTIVE_TRUE "1"
+#define GG_PUBDIR50_START "fmstart"
+#define GG_PUBDIR50_FAMILYNAME "familyname"
+#define GG_PUBDIR50_FAMILYCITY "familycity"
+
+#else
+
+/**
+ * \ingroup pubdir50
+ *
+ * Rodzaj pola zapytania.
+ */
+enum {
+ GG_PUBDIR50_UIN, /**< Numer Gadu-Gadu */
+ GG_PUBDIR50_STATUS, /**< Status (tylko wynik wyszukiwania) */
+ GG_PUBDIR50_FIRSTNAME, /**< ImiÄ™ */
+ GG_PUBDIR50_LASTNAME, /**< Nazwisko */
+ GG_PUBDIR50_NICKNAME, /**< Pseudonim */
+ GG_PUBDIR50_BIRTHYEAR, /**< Rok urodzenia lub przedział lat oddzielony spacją */
+ GG_PUBDIR50_CITY, /**< Miejscowość */
+ GG_PUBDIR50_GENDER, /**< Płeć */
+ GG_PUBDIR50_ACTIVE, /**< Osoba dostępna (tylko wyszukiwanie) */
+ GG_PUBDIR50_START, /**< Numer poczÄ…tkowy wyszukiwania (tylko wyszukiwanie) */
+ GG_PUBDIR50_FAMILYNAME, /**< Nazwisko rodowe (tylko wysyłanie informacji o sobie) */
+ GG_PUBDIR50_FAMILYCITY, /**< Miejscowość pochodzenia (tylko wysyłanie informacji o sobie) */
+};
+
+/**
+ * \ingroup pubdir50
+ *
+ * Wartość pola GG_PUBDIR50_GENDER przy wyszukiwaniu. Brak pola oznacza dowolną płeć.
+ */
+enum {
+ GG_PUBDIR50_GENDER_FEMALE, /**< Kobieta */
+ GG_PUBDIR50_GENDER_MALE, /**< Mężczyzna */
+};
+
+/**
+ * \ingroup pubdir50
+ *
+ * Wartość pola GG_PUBDIR50_GENDER przy wysyłaniu informacji o sobie.
+ */
+enum {
+ GG_PUBDIR50_GENDER_SET_FEMALE, /**< Kobieta */
+ GG_PUBDIR50_GENDER_SET_MALE, /**< Mężczyzna */
+};
+
+/**
+ * \ingroup pubdir50
+ *
+ * Wartość pola GG_PUBDIR50_ACTIVE.
+ */
+enum {
+ GG_PUBDIR50_ACTIVE_TRUE, /**< Wyszukaj tylko osoby dostępne */
+};
+
+#endif /* DOXYGEN */
+
+/**
+ * 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);
+
+/**
+ * Token autoryzacji niektĂłrych operacji HTTP.
+ *
+ * \ingroup token
+ */
+struct gg_token {
+ int width; /**< Szerokość obrazka */
+ int height; /**< Wysokość obrazka */
+ int length; /**< Liczba znakĂłw w tokenie */
+ char *tokenid; /**< Identyfikator tokenu */
+};
+
+struct gg_http *gg_token(int async);
+int gg_token_watch_fd(struct gg_http *h);
+void gg_token_free(struct gg_http *h);
+
+struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async);
+#ifndef DOXYGEN
+#define gg_register_watch_fd gg_pubdir_watch_fd
+#define gg_register_free gg_pubdir_free
+#endif
+
+struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async);
+#ifndef DOXYGEN
+#define gg_unregister_watch_fd gg_pubdir_watch_fd
+#define gg_unregister_free gg_pubdir_free
+#endif
+
+struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async);
+#ifndef DOXYGEN
+#define gg_remind_passwd_watch_fd gg_pubdir_watch_fd
+#define gg_remind_passwd_free gg_pubdir_free
+#endif
+
+struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async);
+#ifndef DOXYGEN
+#define gg_change_passwd_watch_fd gg_pubdir_watch_fd
+#define gg_change_passwd_free gg_pubdir_free
+#endif
+
+extern int gg_dcc_port;
+extern unsigned long gg_dcc_ip;
+
+int gg_dcc_request(struct gg_session *sess, uin_t uin);
+
+struct gg_dcc *gg_dcc_send_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
+struct gg_dcc *gg_dcc_get_file(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
+struct gg_dcc *gg_dcc_voice_chat(uint32_t ip, uint16_t port, uin_t my_uin, uin_t peer_uin);
+void gg_dcc_set_type(struct gg_dcc *d, int type);
+int gg_dcc_fill_file_info(struct gg_dcc *d, const char *filename);
+int gg_dcc_fill_file_info2(struct gg_dcc *d, const char *filename, const char *local_filename);
+int gg_dcc_voice_send(struct gg_dcc *d, char *buf, int length);
+
+#define GG_DCC_VOICE_FRAME_LENGTH 195 /**< Rozmiar pakietu głosowego przed wersją Gadu-Gadu 5.0.5 */
+#define GG_DCC_VOICE_FRAME_LENGTH_505 326 /**< Rozmiar pakietu głosowego od wersji Gadu-Gadu 5.0.5 */
+
+struct gg_dcc *gg_dcc_socket_create(uin_t uin, uint16_t port);
+#ifndef DOXYGEN
+#define gg_dcc_socket_free gg_dcc_free
+#define gg_dcc_socket_watch_fd gg_dcc_watch_fd
+#endif
+
+struct gg_event *gg_dcc_watch_fd(struct gg_dcc *d);
+
+void gg_dcc_free(struct gg_dcc *c);
+
+struct gg_event *gg_dcc7_watch_fd(struct gg_dcc7 *d);
+struct gg_dcc7 *gg_dcc7_send_file(struct gg_session *sess, uin_t rcpt, const char *filename, const char *filename1250, const char *hash);
+struct gg_dcc7 *gg_dcc7_send_file_fd(struct gg_session *sess, uin_t rcpt, int fd, size_t size, const char *filename1250, const char *hash);
+int gg_dcc7_accept(struct gg_dcc7 *dcc, unsigned int offset);
+int gg_dcc7_reject(struct gg_dcc7 *dcc, int reason);
+int gg_dcc7_abort(struct gg_dcc7 *dcc);
+void gg_dcc7_free(struct gg_dcc7 *d);
+
+extern int gg_debug_level;
+
+extern void (*gg_debug_handler)(int level, const char *format, va_list ap);
+extern void (*gg_debug_handler_session)(struct gg_session *sess, int level, const char *format, va_list ap);
+
+extern FILE *gg_debug_file;
+
+/**
+ * \ingroup debug
+ * @{
+ */
+#define GG_DEBUG_NET 1 /**< Rejestracja zdarzeń związanych z siecią */
+#define GG_DEBUG_TRAFFIC 2 /**< Rejestracja ruchu sieciowego */
+#define GG_DEBUG_DUMP 4 /**< Rejestracja zawartości pakietów */
+#define GG_DEBUG_FUNCTION 8 /**< Rejestracja wywołań funkcji */
+#define GG_DEBUG_MISC 16 /**< Rejestracja różnych informacji */
+/** @} */
+
+#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);
+
+extern int gg_proxy_enabled;
+extern char *gg_proxy_host;
+extern int gg_proxy_port;
+extern char *gg_proxy_username;
+extern char *gg_proxy_password;
+extern int gg_proxy_http_only;
+
+extern unsigned long gg_local_ip;
+
+#define GG_LOGIN_HASH_GG32 0x01 /**< Algorytm Gadu-Gadu */
+#define GG_LOGIN_HASH_SHA1 0x02 /**< Algorytm SHA1 */
+
+#ifndef DOXYGEN
+
+#define GG_PUBDIR50_WRITE 0x01
+#define GG_PUBDIR50_READ 0x02
+#define GG_PUBDIR50_SEARCH 0x03
+#define GG_PUBDIR50_SEARCH_REQUEST GG_PUBDIR50_SEARCH
+#define GG_PUBDIR50_SEARCH_REPLY 0x05
+
+#else
+
+/**
+ * \ingroup pubdir50
+ *
+ * Rodzaj zapytania lub odpowiedzi katalogu publicznego.
+ */
+enum {
+ GG_PUBDIR50_WRITE, /**< Wysłanie do serwera informacji o sobie */
+ GG_PUBDIR50_READ, /**< Pobranie z serwera informacji o sobie */
+ GG_PUBDIR50_SEARCH, /**< Wyszukiwanie w katalogu publicznym */
+ GG_PUBDIR50_SEARCH_REPLY, /**< Wynik wyszukiwania w katalogu publicznym */
+};
+
+#endif /* DOXYGEN */
+
+/** \cond obsolete */
+
+#define gg_free_event gg_event_free
+#define gg_free_http gg_http_free
+#define gg_free_pubdir gg_pubdir_free
+#define gg_free_register gg_pubdir_free
+#define gg_free_remind_passwd gg_pubdir_free
+#define gg_free_dcc gg_dcc_free
+#define gg_free_change_passwd gg_pubdir_free
+
+struct gg_search_request {
+ int active;
+ unsigned int start;
+ char *nickname;
+ char *first_name;
+ char *last_name;
+ char *city;
+ int gender;
+ int min_birth;
+ int max_birth;
+ char *email;
+ char *phone;
+ uin_t uin;
+} /* GG_DEPRECATED */;
+
+struct gg_search {
+ int count;
+ struct gg_search_result *results;
+} GG_DEPRECATED;
+
+struct gg_search_result {
+ uin_t uin;
+ char *first_name;
+ char *last_name;
+ char *nickname;
+ int born;
+ int gender;
+ char *city;
+ int active;
+} GG_DEPRECATED;
+
+#define GG_GENDER_NONE 0
+#define GG_GENDER_FEMALE 1
+#define GG_GENDER_MALE 2
+
+struct gg_http *gg_search(const struct gg_search_request *r, int async) GG_DEPRECATED;
+int gg_search_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_free_search(struct gg_http *f) GG_DEPRECATED;
+#define gg_search_free gg_free_search
+
+const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start) GG_DEPRECATED;
+const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start) GG_DEPRECATED;
+const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start) GG_DEPRECATED;
+const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start) GG_DEPRECATED;
+void gg_search_request_free(struct gg_search_request *r) GG_DEPRECATED;
+
+struct gg_http *gg_register(const char *email, const char *password, int async) GG_DEPRECATED;
+struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async) GG_DEPRECATED;
+
+struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async) GG_DEPRECATED;
+struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async) GG_DEPRECATED;
+
+struct gg_http *gg_remind_passwd(uin_t uin, int async) GG_DEPRECATED;
+struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async) GG_DEPRECATED;
+
+struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async) GG_DEPRECATED;
+struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async) GG_DEPRECATED;
+struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async) GG_DEPRECATED;
+
+struct gg_change_info_request {
+ char *first_name;
+ char *last_name;
+ char *nickname;
+ char *email;
+ int born;
+ int gender;
+ char *city;
+} /* GG_DEPRECATED */;
+
+struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city) GG_DEPRECATED;
+void gg_change_info_request_free(struct gg_change_info_request *r) GG_DEPRECATED;
+
+struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async) GG_DEPRECATED;
+#define gg_change_pubdir_watch_fd gg_pubdir_watch_fd
+#define gg_change_pubdir_free gg_pubdir_free
+#define gg_free_change_pubdir gg_pubdir_free
+
+struct gg_http *gg_userlist_get(uin_t uin, const char *password, int async) GG_DEPRECATED;
+int gg_userlist_get_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_userlist_get_free(struct gg_http *f) GG_DEPRECATED;
+
+struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async) GG_DEPRECATED;
+int gg_userlist_put_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_userlist_put_free(struct gg_http *f) GG_DEPRECATED;
+
+struct gg_http *gg_userlist_remove(uin_t uin, const char *password, int async) GG_DEPRECATED;
+int gg_userlist_remove_watch_fd(struct gg_http *f) GG_DEPRECATED;
+void gg_userlist_remove_free(struct gg_http *f) GG_DEPRECATED;
+
+int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length) GG_DEPRECATED;
+
+/** \endcond */
+
+int gg_file_hash_sha1(int fd, uint8_t *result) GG_DEPRECATED;
+
+#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
+
+char *gg_vsaprintf(const char *format, va_list ap) GG_DEPRECATED;
+
+#define gg_alloc_sprintf gg_saprintf
+
+char *gg_get_line(char **ptr) GG_DEPRECATED;
+
+SOCKET gg_connect(void *addr, int port, int async) GG_DEPRECATED;
+#ifdef GG_CONFIG_MIRANDA
+SOCKET gg_connect_internal(void *addr, int port, int async, SOCKET *gg_sock);
+#endif
+struct in_addr *gg_gethostbyname(const char *hostname) GG_DEPRECATED;
+char *gg_read_line(SOCKET sock, char *buf, int length) GG_DEPRECATED;
+void gg_chomp(char *line) GG_DEPRECATED;
+char *gg_urlencode(const char *str) GG_DEPRECATED;
+int gg_http_hash(const char *format, ...) GG_DEPRECATED;
+void gg_http_free_fields(struct gg_http *h) GG_DEPRECATED;
+int gg_read(struct gg_session *sess, char *buf, int length) GG_DEPRECATED;
+int gg_write(struct gg_session *sess, const char *buf, int length) GG_DEPRECATED;
+void *gg_recv_packet(struct gg_session *sess) GG_DEPRECATED;
+int gg_send_packet(struct gg_session *sess, int type, ...) GG_DEPRECATED;
+unsigned int gg_login_hash(const unsigned char *password, unsigned int seed) GG_DEPRECATED;
+void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result) GG_DEPRECATED;
+uint32_t gg_fix32(uint32_t x);
+uint16_t gg_fix16(uint16_t x);
+#define fix16 gg_fix16
+#define fix32 gg_fix32
+char *gg_proxy_auth(void) GG_DEPRECATED;
+char *gg_base64_encode(const char *buf) GG_DEPRECATED;
+char *gg_base64_decode(const char *buf) GG_DEPRECATED;
+int gg_image_queue_remove(struct gg_session *s, struct gg_image_queue *q, int freeq) GG_DEPRECATED;
+
+/**
+ * Kolejka odbieranych obrazkĂłw.
+ */
+struct gg_image_queue {
+ uin_t sender; /**< Nadawca obrazka */
+ uint32_t size; /**< Rozmiar obrazka */
+ uint32_t crc32; /**< Suma kontrolna CRC32 */
+ char *filename; /**< Nazwa pliku */
+ char *image; /**< Bufor z odebranymi danymi */
+ uint32_t done; /**< Rozmiar odebranych danych */
+
+ struct gg_image_queue *next; /**< Kolejny element listy */
+} 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;
+int gg_dcc7_handle_abort(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
+#define GG_PUBDIR_HOST "pubdir.gadu-gadu.pl"
+#define GG_PUBDIR_PORT 80
+#define GG_REGISTER_HOST "register.gadu-gadu.pl"
+#define GG_REGISTER_PORT 80
+#define GG_REMIND_HOST "retr.gadu-gadu.pl"
+#define GG_REMIND_PORT 80
+#define GG_RELAY_HOST "relay.gadu-gadu.pl"
+#define GG_RELAY_PORT 80
+
+#define GG_DEFAULT_PORT 8074
+#define GG_HTTPS_PORT 443
+#define GG_HTTP_USERAGENT "Mozilla/4.7 [en] (Win98; I)"
+
+#define GG_DEFAULT_CLIENT_VERSION "10.1.0.11070"
+#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.10.0"
+
+#ifndef DOXYGEN
+
+#define GG_FEATURE_MSG77 0x0001
+#define GG_FEATURE_STATUS77 0x0002
+#define GG_FEATURE_UNKNOWN_4 0x0004
+#define GG_FEATURE_UNKNOWN_8 0x0008
+#define GG_FEATURE_DND_FFC 0x0010
+#define GG_FEATURE_IMAGE_DESCR 0x0020
+#define GG_FEATURE_UNKNOWN_40 0x0040
+#define GG_FEATURE_UNKNOWN_80 0x0080
+#define GG_FEATURE_UNKNOWN_100 0x0100
+#define GG_FEATURE_USER_DATA 0x0200
+#define GG_FEATURE_MSG_ACK 0x0400
+#define GG_FEATURE_UNKNOWN_800 0x0800
+#define GG_FEATURE_UNKNOWN_1000 0x1000
+#define GG_FEATURE_TYPING_NOTIFICATION 0x2000
+#define GG_FEATURE_MULTILOGON 0x4000
+
+/* Poniższe makra zostały zachowane dla zgodności API */
+#define GG_FEATURE_MSG80 0
+#define GG_FEATURE_STATUS80 0
+#define GG_FEATURE_STATUS80BETA 0
+
+#define GG_FEATURE_ALL (GG_FEATURE_MSG80 | GG_FEATURE_STATUS80 | GG_FEATURE_DND_FFC | GG_FEATURE_IMAGE_DESCR | GG_FEATURE_UNKNOWN_100 | GG_FEATURE_USER_DATA | GG_FEATURE_MSG_ACK | GG_FEATURE_TYPING_NOTIFICATION)
+
+#else
+
+/**
+ * \ingroup login
+ *
+ * Flagi opcji protokołu.
+ */
+enum {
+ GG_FEATURE_MSG77, /**< Klient życzy sobie otrzymywać wiadomości zgodnie z protokołem 7.7 */
+ GG_FEATURE_STATUS77, /**< Klient życzy sobie otrzymywać zmiany stanu zgodnie z protokołem 7.7 */
+ GG_FEATURE_DND_FFC, /**< Klient obsługuje statusy "nie przeszkadzać" i "poGGadaj ze mną" */
+ GG_FEATURE_IMAGE_DESCR, /**< Klient obsługuje opisy graficzne oraz flagę \c GG_STATUS80_DESCR_MASK */
+};
+
+
+#endif
+
+#define GG_DEFAULT_DCC_PORT 1550
+
+struct gg_header {
+ uint32_t type; /* typ pakietu */
+ uint32_t length; /* długość reszty pakietu */
+} GG_PACKED;
+
+#define GG_WELCOME 0x0001
+#define GG_NEED_EMAIL 0x0014
+
+struct gg_welcome {
+ uint32_t key; /* klucz szyfrowania hasła */
+} GG_PACKED;
+
+#define GG_LOGIN 0x000c
+
+struct gg_login {
+ uint32_t uin; /* mĂłj numerek */
+ uint32_t hash; /* hash hasła */
+ uint32_t status; /* status na dzień dobry */
+ uint32_t version; /* moja wersja klienta */
+ uint32_t local_ip; /* mĂłj adres ip */
+ uint16_t local_port; /* port, na którym słucham */
+} GG_PACKED;
+
+#define GG_LOGIN_EXT 0x0013
+
+struct gg_login_ext {
+ uint32_t uin; /* mĂłj numerek */
+ uint32_t hash; /* hash hasła */
+ uint32_t status; /* status na dzień dobry */
+ uint32_t version; /* moja wersja klienta */
+ uint32_t local_ip; /* mĂłj adres ip */
+ uint16_t local_port; /* port, na którym słucham */
+ uint32_t external_ip; /* zewnętrzny adres ip */
+ uint16_t external_port; /* zewnętrzny port */
+} GG_PACKED;
+
+#define GG_LOGIN60 0x0015
+
+struct gg_login60 {
+ uint32_t uin; /* mĂłj numerek */
+ uint32_t hash; /* hash hasła */
+ uint32_t status; /* status na dzień dobry */
+ uint32_t version; /* moja wersja klienta */
+ uint8_t dunno1; /* 0x00 */
+ uint32_t local_ip; /* mĂłj adres ip */
+ uint16_t local_port; /* port, na którym słucham */
+ uint32_t external_ip; /* zewnętrzny adres ip */
+ uint16_t external_port; /* zewnętrzny port */
+ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
+ uint8_t dunno2; /* 0xbe */
+} GG_PACKED;
+
+#define GG_LOGIN70 0x0019
+
+struct gg_login70 {
+ uint32_t uin; /* mĂłj numerek */
+ uint8_t hash_type; /* rodzaj hashowania hasła */
+ uint8_t hash[64]; /* hash hasła dopełniony zerami */
+ uint32_t status; /* status na dzień dobry */
+ uint32_t version; /* moja wersja klienta */
+ uint8_t dunno1; /* 0x00 */
+ uint32_t local_ip; /* mĂłj adres ip */
+ uint16_t local_port; /* port, na którym słucham */
+ uint32_t external_ip; /* zewnętrzny adres ip (???) */
+ uint16_t external_port; /* zewnętrzny port (???) */
+ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
+ uint8_t dunno2; /* 0xbe */
+} GG_PACKED;
+
+#define GG_LOGIN_OK 0x0003
+
+#define GG_LOGIN_FAILED 0x0009
+
+#define GG_PUBDIR50_REQUEST 0x0014
+
+struct gg_pubdir50_request {
+ uint8_t type; /* GG_PUBDIR50_* */
+ uint32_t seq; /* czas wysłania zapytania */
+} GG_PACKED;
+
+#define GG_PUBDIR50_REPLY 0x000e
+
+struct gg_pubdir50_reply {
+ uint8_t type; /* GG_PUBDIR50_* */
+ uint32_t seq; /* czas wysłania zapytania */
+} GG_PACKED;
+
+#define GG_NEW_STATUS 0x0002
+
+#ifndef DOXYGEN
+
+#define GG_STATUS_NOT_AVAIL 0x0001
+#define GG_STATUS_NOT_AVAIL_DESCR 0x0015
+#define GG_STATUS_FFC 0x0017
+#define GG_STATUS_FFC_DESCR 0x0018
+#define GG_STATUS_AVAIL 0x0002
+#define GG_STATUS_AVAIL_DESCR 0x0004
+#define GG_STATUS_BUSY 0x0003
+#define GG_STATUS_BUSY_DESCR 0x0005
+#define GG_STATUS_DND 0x0021
+#define GG_STATUS_DND_DESCR 0x0022
+#define GG_STATUS_INVISIBLE 0x0014
+#define GG_STATUS_INVISIBLE_DESCR 0x0016
+#define GG_STATUS_BLOCKED 0x0006
+
+#define GG_STATUS_IMAGE_MASK 0x0100
+#define GG_STATUS_DESCR_MASK 0x4000
+#define GG_STATUS_FRIENDS_MASK 0x8000
+
+#define GG_STATUS_FLAG_UNKNOWN 0x00000001
+#define GG_STATUS_FLAG_VIDEO 0x00000002
+#define GG_STATUS_FLAG_MOBILE 0x00100000
+#define GG_STATUS_FLAG_SPAM 0x00800000
+
+#else
+
+/**
+ * Rodzaje statusĂłw uĹĽytkownika.
+ *
+ * \ingroup status
+ */
+enum {
+ GG_STATUS_NOT_AVAIL, /**< Niedostępny */
+ GG_STATUS_NOT_AVAIL_DESCR, /**< Niedostępny z opisem */
+ GG_STATUS_FFC, /**< PoGGadaj ze mnÄ… */
+ GG_STATUS_FFC_DESCR, /**< PoGGadaj ze mnÄ… z opisem */
+ GG_STATUS_AVAIL, /**< Dostępny */
+ GG_STATUS_AVAIL_DESCR, /**< Dostępny z opisem */
+ GG_STATUS_BUSY, /**< Zajęty */
+ GG_STATUS_BUSY_DESCR, /**< Zajęty z opisem */
+ GG_STATUS_DND, /**< Nie przeszkadzać */
+ GG_STATUS_DND_DESCR, /**< Nie przeszakdzać z opisem */
+ GG_STATUS_INVISIBLE, /**< Niewidoczny (tylko własny status) */
+ GG_STATUS_INVISIBLE_DESCR, /**< Niewidoczny z opisem (tylko własny status) */
+ GG_STATUS_BLOCKED, /**< Zablokowany (tylko status innych) */
+ GG_STATUS_IMAGE_MASK, /**< Flaga bitowa oznaczająca opis graficzny (tylko jeśli wybrano \c GG_FEATURE_IMAGE_DESCR) */
+ GG_STATUS_DESCR_MASK, /**< Flaga bitowa oznaczająca status z opisem (tylko jeśli wybrano \c GG_FEATURE_IMAGE_DESCR) */
+ GG_STATUS_FRIENDS_MASK, /**< Flaga bitowa dostępności tylko dla znajomych */
+};
+
+/**
+ * Rodzaje statusĂłw uĹĽytkownika. Mapa bitowa.
+ *
+ * \ingroup status
+ */
+enum {
+ GG_STATUS_FLAG_UNKNOWN, /**< Przeznaczenie nieznane, ale występuje zawsze */
+ GG_STATUS_FLAG_VIDEO, /**< Klient obsługuje wideorozmowy */
+ GG_STATUS_FLAG_MOBILE, /**< Klient mobilny (ikona telefonu komĂłrkowego) */
+ GG_STATUS_FLAG_SPAM, /**< Klient chce otrzymywać linki od nieznajomych */
+};
+
+#endif /* DOXYGEN */
+
+/**
+ * \ingroup status
+ *
+ * Flaga bitowa dostepnosci informujaca ze mozemy voipowac
+ */
+
+#define GG_STATUS_VOICE_MASK 0x20000 /**< czy ma wlaczone audio (7.7) */
+
+/**
+ * \ingroup status
+ *
+ * Maksymalna długośc opisu.
+ */
+#define GG_STATUS_DESCR_MAXSIZE 255
+#define GG_STATUS_DESCR_MAXSIZE_PRE_8_0 70
+
+#define GG_STATUS_MASK 0xff
+
+/* GG_S_F() tryb tylko dla znajomych */
+#define GG_S_F(x) (((x) & GG_STATUS_FRIENDS_MASK) != 0)
+
+/* GG_S() stan bez uwzględnienia dodatkowych flag */
+#define GG_S(x) ((x) & GG_STATUS_MASK)
+
+
+/* GG_S_FF() chętny do rozmowy */
+#define GG_S_FF(x) (GG_S(x) == GG_STATUS_FFC || GG_S(x) == GG_STATUS_FFC_DESCR)
+
+/* GG_S_AV() dostępny */
+#define GG_S_AV(x) (GG_S(x) == GG_STATUS_AVAIL || GG_S(x) == GG_STATUS_AVAIL_DESCR)
+
+/* GG_S_AW() zaraz wracam */
+#define GG_S_AW(x) (GG_S(x) == GG_STATUS_BUSY || GG_S(x) == GG_STATUS_BUSY_DESCR)
+
+/* GG_S_DD() nie przeszkadzać */
+#define GG_S_DD(x) (GG_S(x) == GG_STATUS_DND || GG_S(x) == GG_STATUS_DND_DESCR)
+
+/* GG_S_NA() niedostępny */
+#define GG_S_NA(x) (GG_S(x) == GG_STATUS_NOT_AVAIL || GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR)
+
+/* GG_S_I() niewidoczny */
+#define GG_S_I(x) (GG_S(x) == GG_STATUS_INVISIBLE || GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
+
+
+/* GG_S_A() dostępny lub chętny do rozmowy */
+#define GG_S_A(x) (GG_S_FF(x) || GG_S_AV(x))
+
+/* GG_S_B() zajęty lub nie przeszkadzać */
+#define GG_S_B(x) (GG_S_AW(x) || GG_S_DD(x))
+
+
+/* GG_S_D() stan opisowy */
+#define GG_S_D(x) (GG_S(x) == GG_STATUS_NOT_AVAIL_DESCR || \
+ GG_S(x) == GG_STATUS_FFC_DESCR || \
+ GG_S(x) == GG_STATUS_AVAIL_DESCR || \
+ GG_S(x) == GG_STATUS_BUSY_DESCR || \
+ GG_S(x) == GG_STATUS_DND_DESCR || \
+ GG_S(x) == GG_STATUS_INVISIBLE_DESCR)
+
+/* GG_S_BL() blokowany lub blokujÄ…cy */
+#define GG_S_BL(x) (GG_S(x) == GG_STATUS_BLOCKED)
+
+/**
+ * Zmiana statusu (pakiet \c GG_NEW_STATUS i \c GG_NEW_STATUS80BETA)
+ */
+struct gg_new_status {
+ uint32_t status; /**< Nowy status */
+} GG_PACKED;
+
+#define GG_NOTIFY_FIRST 0x000f
+#define GG_NOTIFY_LAST 0x0010
+
+#define GG_NOTIFY 0x0010
+
+struct gg_notify {
+ uint32_t uin; /* numerek danej osoby */
+ uint8_t dunno1; /* rodzaj wpisu w liście */
+} GG_PACKED;
+
+#ifndef DOXYGEN
+
+#define GG_USER_OFFLINE 0x01
+#define GG_USER_NORMAL 0x03
+#define GG_USER_BLOCKED 0x04
+
+#else
+
+/**
+ * \ingroup contacts
+ *
+ * Rodzaj kontaktu.
+ */
+enum {
+ GG_USER_NORMAL, /**< Zwykły kontakt */
+ GG_USER_BLOCKED, /**< Zablokowany */
+ GG_USER_OFFLINE, /**< Niewidoczny dla kontaktu */
+};
+
+#endif /* DOXYGEN */
+
+#define GG_LIST_EMPTY 0x0012
+
+#define GG_NOTIFY_REPLY 0x000c /* tak, to samo co GG_LOGIN */
+
+struct gg_notify_reply {
+ uint32_t uin; /* numerek */
+ uint32_t status; /* status danej osoby */
+ uint32_t remote_ip; /* adres ip delikwenta */
+ uint16_t remote_port; /* port, na którym słucha klient */
+ uint32_t version; /* wersja klienta */
+ uint16_t dunno2; /* znowu port? */
+} GG_PACKED;
+
+#define GG_NOTIFY_REPLY60 0x0011
+
+struct gg_notify_reply60 {
+ uint32_t uin; /* numerek plus flagi w MSB */
+ uint8_t status; /* status danej osoby */
+ uint32_t remote_ip; /* adres ip delikwenta */
+ uint16_t remote_port; /* port, na którym słucha klient */
+ uint8_t version; /* wersja klienta */
+ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
+ uint8_t dunno1; /* 0x00 */
+} GG_PACKED;
+
+#define GG_STATUS60 0x000f
+
+struct gg_status60 {
+ uint32_t uin; /* numerek plus flagi w MSB */
+ uint8_t status; /* status danej osoby */
+ uint32_t remote_ip; /* adres ip delikwenta */
+ uint16_t remote_port; /* port, na którym słucha klient */
+ uint8_t version; /* wersja klienta */
+ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
+ uint8_t dunno1; /* 0x00 */
+} GG_PACKED;
+
+#define GG_NOTIFY_REPLY77 0x0018
+
+struct gg_notify_reply77 {
+ uint32_t uin; /* numerek plus flagi w MSB */
+ uint8_t status; /* status danej osoby */
+ uint32_t remote_ip; /* adres ip delikwenta */
+ uint16_t remote_port; /* port, na którym słucha klient */
+ uint8_t version; /* wersja klienta */
+ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
+ uint8_t dunno1; /* 0x00 */
+ uint32_t dunno2; /* ? */
+} GG_PACKED;
+
+#define GG_STATUS77 0x0017
+
+struct gg_status77 {
+ uint32_t uin; /* numerek plus flagi w MSB */
+ uint8_t status; /* status danej osoby */
+ uint32_t remote_ip; /* adres ip delikwenta */
+ uint16_t remote_port; /* port, na którym słucha klient */
+ uint8_t version; /* wersja klienta */
+ uint8_t image_size; /* maksymalny rozmiar grafiki w KiB */
+ uint8_t dunno1; /* 0x00 */
+ uint32_t dunno2; /* ? */
+} GG_PACKED;
+
+#define GG_ADD_NOTIFY 0x000d
+#define GG_REMOVE_NOTIFY 0x000e
+
+struct gg_add_remove {
+ uint32_t uin; /* numerek */
+ uint8_t dunno1; /* bitmapa */
+} GG_PACKED;
+
+#define GG_STATUS 0x0002
+
+struct gg_status {
+ uint32_t uin; /* numerek */
+ uint32_t status; /* nowy stan */
+} GG_PACKED;
+
+#define GG_SEND_MSG 0x000b
+
+#ifndef DOXYGEN
+
+#define GG_CLASS_QUEUED 0x0001
+#define GG_CLASS_OFFLINE GG_CLASS_QUEUED
+#define GG_CLASS_MSG 0x0004
+#define GG_CLASS_CHAT 0x0008
+#define GG_CLASS_CTCP 0x0010
+#define GG_CLASS_ACK 0x0020
+#define GG_CLASS_EXT GG_CLASS_ACK /**< Dla kompatybilności wstecz */
+
+#else
+
+/**
+ * Klasy wiadomości. Wartości są maskami bitowymi, które w większości
+ * przypadków można łączyć (połączenie \c GG_CLASS_MSG i \c GG_CLASS_CHAT
+ * nie ma sensu).
+ *
+ * \ingroup messages
+ */
+enum {
+ GG_CLASS_MSG, /**< Wiadomość ma pojawić się w osobnym oknie */
+ GG_CLASS_CHAT, /**< Wiadomość ma pojawić się w oknie rozmowy */
+ GG_CLASS_CTCP, /**< Wiadomość przeznaczona dla klienta Gadu-Gadu */
+ GG_CLASS_ACK, /**< Klient nie ĹĽyczy sobie potwierdzenia */
+ GG_CLASS_QUEUED, /**< Wiadomość zakolejkowana na serwerze (tylko przy odbieraniu) */
+};
+
+#endif /* DOXYGEN */
+
+/**
+ * Maksymalna długość wiadomości.
+ *
+ * \ingroup messages
+ */
+#define GG_MSG_MAXSIZE 1989
+
+struct gg_send_msg {
+ uint32_t recipient;
+ uint32_t seq;
+ uint32_t msgclass;
+} GG_PACKED;
+
+struct gg_msg_richtext {
+ uint8_t flag;
+ uint16_t length;
+} GG_PACKED;
+
+/**
+ * Struktura opisująca formatowanie tekstu. W zależności od wartości pola
+ * \c font, zaraz za tą strukturą może wystąpić \c gg_msg_richtext_color
+ * lub \c gg_msg_richtext_image.
+ *
+ * \ingroup messages
+ */
+struct gg_msg_richtext_format {
+ uint16_t position; /**< PoczÄ…tkowy znak formatowania (liczony od 0) */
+ uint8_t font; /**< Atrybuty formatowania */
+} GG_PACKED;
+
+#ifndef DOXYGEN
+
+#define GG_FONT_BOLD 0x01
+#define GG_FONT_ITALIC 0x02
+#define GG_FONT_UNDERLINE 0x04
+#define GG_FONT_COLOR 0x08
+#define GG_FONT_IMAGE 0x80
+
+#else
+
+/**
+ * Atrybuty formatowania wiadomości.
+ *
+ * \ingroup messages
+ */
+enum {
+ GG_FONT_BOLD,
+ GG_FONT_ITALIC,
+ GG_FONT_UNDERLINE,
+ GG_FONT_COLOR,
+ GG_FONT_IMAGE
+};
+
+#endif /* DOXYGEN */
+
+/**
+ * Struktura opisujÄ…cÄ… kolor tekstu dla atrybutu \c GG_FONT_COLOR.
+ *
+ * \ingroup messages
+ */
+struct gg_msg_richtext_color {
+ uint8_t red; /**< Składowa czerwona koloru */
+ uint8_t green; /**< Składowa zielona koloru */
+ uint8_t blue; /**< Składowa niebieska koloru */
+} GG_PACKED;
+
+/**
+ * Strukturya opisująca obrazek wstawiony do wiadomości dla atrubutu
+ * \c GG_FONT_IMAGE.
+ *
+ * \ingroup messages
+ */
+struct gg_msg_richtext_image {
+ uint16_t unknown1; /**< Nieznane pole o wartości 0x0109 */
+ uint32_t size; /**< Rozmiar obrazka */
+ uint32_t crc32; /**< Suma kontrolna CRC32 obrazka */
+} GG_PACKED;
+
+struct gg_msg_recipients {
+ uint8_t flag;
+ uint32_t count;
+} GG_PACKED;
+
+struct gg_msg_image_request {
+ uint8_t flag;
+ uint32_t size;
+ uint32_t crc32;
+} GG_PACKED;
+
+struct gg_msg_image_reply {
+ uint8_t flag;
+ uint32_t size;
+ uint32_t crc32;
+ /* char filename[]; */
+ /* char image[]; */
+} GG_PACKED;
+
+#define GG_SEND_MSG_ACK 0x0005
+
+#ifndef DOXYGEN
+
+#define GG_ACK_BLOCKED 0x0001
+#define GG_ACK_DELIVERED 0x0002
+#define GG_ACK_QUEUED 0x0003
+#define GG_ACK_MBOXFULL 0x0004
+#define GG_ACK_NOT_DELIVERED 0x0006
+
+#else
+
+/**
+ * Status doręczenia wiadomości.
+ *
+ * \ingroup messages
+ */
+enum
+{
+ GG_ACK_DELIVERED, /**< Wiadomość dostarczono. */
+ GG_ACK_QUEUED, /**< Wiadomość zakolejkowano z powodu niedostępności odbiorcy. */
+ GG_ACK_BLOCKED, /**< Wiadomość zablokowana przez serwer (spam, świąteczne ograniczenia itd.) */
+ GG_ACK_MBOXFULL, /**< Wiadomości nie dostarczono z powodu zapełnionej kolejki wiadomości odbiorcy. */
+ GG_ACK_NOT_DELIVERED /**< Wiadomości nie dostarczono (tylko dla \c GG_CLASS_CTCP). */
+};
+
+#endif /* DOXYGEN */
+
+struct gg_send_msg_ack {
+ uint32_t status;
+ uint32_t recipient;
+ uint32_t seq;
+} GG_PACKED;
+
+#define GG_RECV_MSG 0x000a
+
+struct gg_recv_msg {
+ uint32_t sender;
+ uint32_t seq;
+ uint32_t time;
+ uint32_t msgclass;
+} GG_PACKED;
+
+#define GG_PING 0x0008
+
+#define GG_PONG 0x0007
+
+#define GG_DISCONNECTING 0x000b
+
+#define GG_USERLIST_REQUEST 0x0016
+
+#define GG_XML_EVENT 0x0027
+
+#ifndef DOXYGEN
+
+#define GG_USERLIST_PUT 0x00
+#define GG_USERLIST_PUT_MORE 0x01
+#define GG_USERLIST_GET 0x02
+
+#else
+
+/**
+ * \ingroup importexport
+ *
+ * Rodzaj zapytania.
+ */
+enum {
+ GG_USERLIST_PUT, /**< Eksport listy kontaktĂłw. */
+ GG_USERLIST_GET, /**< Import listy kontaktĂłw. */
+};
+
+#endif /* DOXYGEN */
+
+struct gg_userlist_request {
+ uint8_t type;
+} GG_PACKED;
+
+#define GG_USERLIST_REPLY 0x0010
+
+#ifndef DOXYGEN
+
+#define GG_USERLIST_PUT_REPLY 0x00
+#define GG_USERLIST_PUT_MORE_REPLY 0x02
+#define GG_USERLIST_GET_REPLY 0x06
+#define GG_USERLIST_GET_MORE_REPLY 0x04
+
+#else
+
+/**
+ * \ingroup importexport
+ *
+ * Rodzaj odpowiedzi.
+ */
+enum {
+ GG_USERLIST_PUT_REPLY, /**< Wyeksportowano listy kontaktĂłw. */
+ GG_USERLIST_GET_REPLY, /**< Zaimportowano listÄ™ kontaktĂłw. */
+};
+
+#endif /* DOXYGEN */
+
+struct gg_userlist_reply {
+ uint8_t type;
+} GG_PACKED;
+
+struct gg_dcc_tiny_packet {
+ uint8_t type; /* rodzaj pakietu */
+} GG_PACKED;
+
+struct gg_dcc_small_packet {
+ uint32_t type; /* rodzaj pakietu */
+} GG_PACKED;
+
+struct gg_dcc_big_packet {
+ uint32_t type; /* rodzaj pakietu */
+ uint32_t dunno1; /* niewiadoma */
+ uint32_t dunno2; /* niewiadoma */
+} GG_PACKED;
+
+/*
+ * póki co, nie znamy dokładnie protokołu. nie wiemy, co czemu odpowiada.
+ * nazwy sÄ… niepowaĹĽne i tymczasowe.
+ */
+#define GG_DCC_WANT_FILE 0x0003 /* peer chce plik */
+#define GG_DCC_HAVE_FILE 0x0001 /* więc mu damy */
+#define GG_DCC_HAVE_FILEINFO 0x0003 /* niech ma informacje o pliku */
+#define GG_DCC_GIMME_FILE 0x0006 /* peer jest pewny */
+#define GG_DCC_CATCH_FILE 0x0002 /* wysyłamy plik */
+
+#define GG_DCC_FILEATTR_READONLY 0x0020
+
+#define GG_DCC_TIMEOUT_SEND 1800 /* 30 minut */
+#define GG_DCC_TIMEOUT_GET 1800 /* 30 minut */
+#define GG_DCC_TIMEOUT_FILE_ACK 300 /* 5 minut */
+#define GG_DCC_TIMEOUT_VOICE_ACK 300 /* 5 minut */
+
+#define GG_DCC7_INFO 0x1f
+
+struct gg_dcc7_info {
+ uint32_t uin; /* numer nadawcy */
+ uint32_t type; /* sposób połączenia */
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+ char info[GG_DCC7_INFO_LEN]; /* informacje o połączeniu "ip port" */
+ char hash[GG_DCC7_INFO_HASH_LEN];/* skrĂłt "ip" */
+} GG_PACKED;
+
+#define GG_DCC7_NEW 0x20
+
+struct gg_dcc7_new {
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+ uint32_t uin_from; /* numer nadawcy */
+ uint32_t uin_to; /* numer odbiorcy */
+ uint32_t type; /* rodzaj transmisji */
+ unsigned char filename[GG_DCC7_FILENAME_LEN]; /* nazwa pliku */
+ uint32_t size; /* rozmiar pliku */
+ uint32_t size_hi; /* rozmiar pliku (starsze bajty) */
+ unsigned char hash[GG_DCC7_HASH_LEN]; /* hash SHA1 */
+} GG_PACKED;
+
+#define GG_DCC7_ACCEPT 0x21
+
+struct gg_dcc7_accept {
+ uint32_t uin; /* numer przyjmującego połączenie */
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+ uint32_t offset; /* offset przy wznawianiu transmisji */
+ uint32_t dunno1; /* 0x00000000 */
+} GG_PACKED;
+
+// XXX API
+#define GG_DCC7_TYPE_P2P 0x00000001 /**< Połączenie bezpośrednie */
+#define GG_DCC7_TYPE_SERVER 0x00000002 /**< Połączenie przez serwer */
+
+#define GG_DCC7_REJECT 0x22
+
+struct gg_dcc7_reject {
+ uint32_t uin; /**< Numer odrzucającego połączenie */
+ gg_dcc7_id_t id; /**< Identyfikator połączenia */
+ uint32_t reason; /**< Powód rozłączenia */
+} GG_PACKED;
+
+// XXX API
+#define GG_DCC7_REJECT_BUSY 0x00000001 /**< Połączenie bezpośrednie już trwa, nie umiem obsłużyć więcej */
+#define GG_DCC7_REJECT_USER 0x00000002 /**< Użytkownik odrzucił połączenie */
+#define GG_DCC7_REJECT_HIDDEN 0x00000003 /* użytkownik ojest ukryty i nie możesz mu wysłać pliku */
+#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_ABORT 0x0025
+
+struct gg_dcc7_abort {
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+ uint32_t uin_from; /* numer nadawcy */
+ uint32_t uin_to; /* numer odbiorcy */
+} GG_PACKED;
+
+struct gg_dcc7_aborted {
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+} 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
+}
+#endif
+
+#if defined(__cplusplus) || defined(_WIN32)
+#ifdef _WIN32
+#pragma pack(pop)
+#endif
+#endif
+
+#endif /* __GG_LIBGADU_H */
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/protocols/Gadu-Gadu/libgadu/obsolete.c b/protocols/Gadu-Gadu/libgadu/obsolete.c
new file mode 100644
index 0000000000..f8fe4dc5de
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/obsolete.c
@@ -0,0 +1,238 @@
+/* coding: UTF-8 */
+/* $Id: obsolete.c 854 2009-10-12 21:06:28Z wojtekka $ */
+
+/*
+ * (C) Copyright 2001-2003 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/**
+ * \file obsolete.c
+ *
+ * \brief Nieaktualne funkcje
+ *
+ * Plik zawiera definicje funkcji, które są już nieaktualne ze względu
+ * na zmiany w protokole. Programy konsolidowane ze starszych wersjami
+ * bibliotek powinny nadal mieć możliwość działania, mimo ograniczonej
+ * funkcjonalności.
+ */
+
+/** \cond obsolete */
+
+#include <errno.h>
+
+#include "libgadu.h"
+#include "internal.h"
+
+struct gg_http *gg_userlist_get(uin_t uin, const char *passwd, int async)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_userlist_get() is obsolete. use gg_userlist_request() instead!\n");
+ errno = EINVAL;
+ return NULL;
+}
+
+int gg_userlist_get_watch_fd(struct gg_http *h)
+{
+ errno = EINVAL;
+ return -1;
+}
+
+void gg_userlist_get_free(struct gg_http *h)
+{
+
+}
+
+struct gg_http *gg_userlist_put(uin_t uin, const char *password, const char *contacts, int async)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_userlist_put() is obsolete. use gg_userlist_request() instead!\n");
+ errno = EINVAL;
+ return NULL;
+}
+
+int gg_userlist_put_watch_fd(struct gg_http *h)
+{
+ errno = EINVAL;
+ return -1;
+}
+
+void gg_userlist_put_free(struct gg_http *h)
+{
+
+}
+
+struct gg_http *gg_userlist_remove(uin_t uin, const char *passwd, int async)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_userlist_remove() is obsolete. use gg_userlist_request() instead!\n");
+ errno = EINVAL;
+ return NULL;
+}
+
+int gg_userlist_remove_watch_fd(struct gg_http *h)
+{
+ errno = EINVAL;
+ return -1;
+}
+
+void gg_userlist_remove_free(struct gg_http *h)
+{
+
+}
+
+struct gg_http *gg_search(const struct gg_search_request *r, int async)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_search() is obsolete. use gg_search50() instead!\n");
+ errno = EINVAL;
+ return NULL;
+}
+
+int gg_search_watch_fd(struct gg_http *h)
+{
+ errno = EINVAL;
+ return -1;
+}
+
+void gg_search_free(struct gg_http *h)
+{
+
+}
+
+const struct gg_search_request *gg_search_request_mode_0(char *nickname, char *first_name, char *last_name, char *city, int gender, int min_birth, int max_birth, int active, int start)
+{
+ return NULL;
+}
+
+const struct gg_search_request *gg_search_request_mode_1(char *email, int active, int start)
+{
+ return NULL;
+}
+
+const struct gg_search_request *gg_search_request_mode_2(char *phone, int active, int start)
+{
+ return NULL;
+}
+
+const struct gg_search_request *gg_search_request_mode_3(uin_t uin, int active, int start)
+{
+ return NULL;
+}
+
+void gg_search_request_free(struct gg_search_request *r)
+{
+
+}
+
+struct gg_http *gg_register(const char *email, const char *password, int async)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_register() is obsolete. use gg_register3() instead!\n");
+ errno = EINVAL;
+ return NULL;
+}
+
+struct gg_http *gg_register2(const char *email, const char *password, const char *qa, int async)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_register2() is obsolete. use gg_register3() instead!\n");
+ errno = EINVAL;
+ return NULL;
+}
+
+struct gg_http *gg_unregister(uin_t uin, const char *password, const char *email, int async)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_unregister() is obsolete. use gg_unregister3() instead!\n");
+ errno = EINVAL;
+ return NULL;
+}
+
+struct gg_http *gg_unregister2(uin_t uin, const char *password, const char *qa, int async)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_unregister2() is obsolete. use gg_unregister3() instead!\n");
+ errno = EINVAL;
+ return NULL;
+}
+
+
+struct gg_http *gg_change_passwd(uin_t uin, const char *passwd, const char *newpasswd, const char *newemail, int async)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_change_passwd() is obsolete. use gg_change_passwd4() instead!\n");
+ errno = EINVAL;
+ return NULL;
+}
+
+struct gg_http *gg_change_passwd2(uin_t uin, const char *passwd, const char *newpasswd, const char *email, const char *newemail, int async)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_change_passwd2() is obsolete. use gg_change_passwd4() instead!\n");
+ errno = EINVAL;
+ return NULL;
+}
+
+struct gg_http *gg_change_passwd3(uin_t uin, const char *passwd, const char *newpasswd, const char *qa, int async)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_change_passwd3() is obsolete. use gg_change_passwd4() instead!\n");
+ errno = EINVAL;
+ return NULL;
+}
+
+struct gg_http *gg_remind_passwd(uin_t uin, int async)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_remind_passwd() is obsolete. use gg_remind_passwd3() instead!\n");
+ errno = EINVAL;
+ return NULL;
+}
+
+struct gg_http *gg_remind_passwd2(uin_t uin, const char *tokenid, const char *tokenval, int async)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_remind_passwd2() is obsolete. use gg_remind_passwd3() instead!\n");
+ errno = EINVAL;
+ return NULL;
+}
+
+struct gg_http *gg_change_info(uin_t uin, const char *passwd, const struct gg_change_info_request *request, int async)
+{
+ gg_debug(GG_DEBUG_MISC, "// gg_change_info() is obsolete. use gg_pubdir50() instead\n");
+ errno = EINVAL;
+ return NULL;
+}
+
+struct gg_change_info_request *gg_change_info_request_new(const char *first_name, const char *last_name, const char *nickname, const char *email, int born, int gender, const char *city)
+{
+ return NULL;
+}
+
+void gg_change_info_request_free(struct gg_change_info_request *r)
+{
+
+}
+
+int gg_resolve(int *fd, int *pid, const char *hostname)
+{
+ return -1;
+}
+
+void gg_resolve_pthread_cleanup(void *arg, int kill)
+{
+
+}
+
+int gg_resolve_pthread(int *fd, void **resolver, const char *hostname)
+{
+ return -1;
+}
+
+int gg_pubdir50_handle_reply(struct gg_event *e, const char *packet, int length)
+{
+ return -1;
+}
+
+/** \endcond */
diff --git a/protocols/Gadu-Gadu/libgadu/protocol.h b/protocols/Gadu-Gadu/libgadu/protocol.h
new file mode 100644
index 0000000000..5b4895c260
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/protocol.h
@@ -0,0 +1,277 @@
+/* coding: UTF-8 */
+/* $Id$ */
+
+/*
+ * (C) Copyright 2009-2010 Jakub Zawadzki <darkjames@darkjames.ath.cx>
+ * Bartłomiej Zimoń <uzi18@o2.pl>
+ * Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef LIBGADU_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 "
+
+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
+
+/**
+ * Logowanie powiodło się (pakiet \c GG_LOGIN80_OK)
+ */
+struct gg_login80_ok {
+ uint32_t unknown1; /* 0x00000001 */
+} GG_PACKED;
+
+/**
+ * Logowanie nie powiodło się (pakiet \c GG_LOGIN80_FAILED)
+ */
+#define GG_LOGIN80_FAILED 0x0043
+
+struct gg_login80_failed {
+ uint32_t unknown1; /* 0x00000001 */
+} GG_PACKED;
+
+#define GG_NEW_STATUS80BETA 0x0028
+
+#define GG_NEW_STATUS80 0x0038
+
+/**
+ * Zmiana stanu (pakiet \c GG_NEW_STATUS80)
+ */
+struct gg_new_status80 {
+ uint32_t status; /**< Nowy status */
+ uint32_t flags; /**< flagi (nieznane przeznaczenie) */
+ uint32_t description_size; /**< rozmiar opisu */
+} GG_PACKED;
+
+#define GG_STATUS80BETA 0x002a
+#define GG_NOTIFY_REPLY80BETA 0x002b
+
+#define GG_STATUS80 0x0036
+#define GG_NOTIFY_REPLY80 0x0037
+
+struct gg_notify_reply80 {
+ uint32_t uin; /* numerek plus flagi w najstarszym bajcie */
+ uint32_t status; /* status danej osoby */
+ uint32_t features; /* opcje protokołu */
+ uint32_t remote_ip; /* adres IP bezpośrednich połączeń */
+ uint16_t remote_port; /* port bezpośrednich połączeń */
+ uint8_t image_size; /* maksymalny rozmiar obrazkĂłw w KB */
+ uint8_t unknown1; /* 0x00 */
+ uint32_t flags; /* flagi połączenia */
+ uint32_t descr_len; /* rozmiar opisu */
+} GG_PACKED;
+
+#define GG_SEND_MSG80 0x002d
+
+struct gg_send_msg80 {
+ uint32_t recipient;
+ uint32_t seq;
+ uint32_t msgclass;
+ uint32_t offset_plain;
+ uint32_t offset_attr;
+} GG_PACKED;
+
+#define GG_RECV_MSG80 0x002e
+
+struct gg_recv_msg80 {
+ uint32_t sender;
+ uint32_t seq;
+ uint32_t time;
+ uint32_t msgclass;
+ uint32_t offset_plain;
+ uint32_t offset_attr;
+} GG_PACKED;
+
+#define GG_DISCONNECT_ACK 0x000d
+
+#define GG_RECV_MSG_ACK 0x0046
+
+struct gg_recv_msg_ack {
+ uint32_t seq;
+} GG_PACKED;
+
+#define GG_USER_DATA 0x0044
+
+struct gg_user_data {
+ uint32_t type;
+ uint32_t user_count;
+} GG_PACKED;
+
+struct gg_user_data_user {
+ uint32_t uin;
+ uint32_t attr_count;
+} GG_PACKED;
+
+#define GG_TYPING_NOTIFICATION 0x0059
+
+struct gg_typing_notification {
+ uint16_t length;
+ uint32_t uin;
+} GG_PACKED;
+
+#define GG_XML_ACTION 0x002c
+
+#define GG_RECV_OWN_MSG 0x005a
+
+#define GG_MULTILOGON_INFO 0x005b
+
+struct gg_multilogon_info {
+ uint32_t count;
+} GG_PACKED;
+
+struct gg_multilogon_info_item {
+ uint32_t addr;
+ uint32_t flags;
+ uint32_t features;
+ uint32_t logon_time;
+ gg_multilogon_id_t conn_id;
+ uint32_t unknown1;
+ uint32_t name_size;
+} GG_PACKED;
+
+#define GG_MULTILOGON_DISCONNECT 0x0062
+
+struct gg_multilogon_disconnect {
+ gg_multilogon_id_t conn_id;
+} GG_PACKED;
+
+#define GG_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;
+
+#define GG_DCC7_RELAY_TYPE_SERVER 0x01 /* adres serwera, na który spytać o proxy */
+#define GG_DCC7_RELAY_TYPE_PROXY 0x08 /* adresy proxy, na które sie łączyć */
+
+#define GG_DCC7_RELAY_DUNNO1 0x02
+
+#define GG_DCC7_RELAY_REQUEST 0x0a
+
+struct gg_dcc7_relay_req {
+ uint32_t magic; /* 0x0a */
+ uint32_t len; /* długość całego pakietu */
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+ uint16_t type; /* typ zapytania */
+ uint16_t dunno1; /* 0x02 */
+} GG_PACKED;
+
+#define GG_DCC7_RELAY_REPLY_RCOUNT 0x02
+
+#define GG_DCC7_RELAY_REPLY 0x0b
+
+struct gg_dcc7_relay_reply {
+ uint32_t magic; /* 0x0b */
+ uint32_t len; /* długość całego pakietu */
+ uint32_t rcount; /* ilość serwerów */
+} GG_PACKED;
+
+struct gg_dcc7_relay_reply_server {
+ uint32_t addr; /* adres ip serwera */
+ uint16_t port; /* port serwera */
+ uint8_t family; /* rodzina adresów (na końcu?!) AF_INET=2 */
+} GG_PACKED;
+
+#define GG_DCC7_WELCOME_SERVER 0xc0debabe
+
+struct gg_dcc7_welcome_server {
+ uint32_t magic; /* 0xc0debabe */
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+} GG_PACKED;
+
+struct gg_dcc7_welcome_p2p {
+ gg_dcc7_id_t id; /* identyfikator połączenia */
+} GG_PACKED;
+
+#ifdef _WIN32
+#pragma pack(pop)
+#endif
+
+#endif /* LIBGADU_PROTOCOL_H */
diff --git a/protocols/Gadu-Gadu/libgadu/pthread.c b/protocols/Gadu-Gadu/libgadu/pthread.c
new file mode 100644
index 0000000000..ecaab64a4f
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/pthread.c
@@ -0,0 +1,78 @@
+/*
+AOL Instant Messenger Plugin for Miranda IM
+
+Copyright (c) 2003-2006 Robert Rainwater
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#include "pthread.h"
+#include <process.h>
+#include <newpluginapi.h>
+#include <m_system.h>
+
+/****************************************/
+/* Portable pthread code for Miranda IM */
+
+/* create thread */
+int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(__stdcall * thread_start) (void *), void *param)
+{
+ tid->hThread = (HANDLE)mir_forkthreadex((pThreadFuncEx)*(void**)&thread_start, param, 0, (unsigned *)&tid->dwThreadId);
+
+ return 0;
+}
+
+/* detach a thread */
+int pthread_detach(pthread_t *tid)
+{
+ CloseHandle(tid->hThread);
+ return 0;
+}
+
+/* wait for thread termination */
+int pthread_join(pthread_t *tid, void **value_ptr)
+{
+ if(tid->dwThreadId == GetCurrentThreadId())
+ return 35 /*EDEADLK*/;
+
+ WaitForSingleObject(tid->hThread, INFINITE);
+ return 0;
+}
+
+/* get calling thread's ID */
+pthread_t *pthread_self(void)
+{
+ static int poolId = 0;
+ static pthread_t tidPool[32];
+ /* mark & round pool to 32 items */
+ pthread_t *tid = &tidPool[poolId ++];
+ poolId %= 32;
+
+ tid->hThread = GetCurrentThread();
+ tid->dwThreadId = GetCurrentThreadId();
+ return tid;
+}
+
+/* cancel execution of a thread */
+int pthread_cancel(pthread_t *thread)
+{
+ return TerminateThread(thread->hThread, 0) ? 0 : 3 /*ESRCH*/;
+}
+
+/* terminate thread */
+void pthread_exit(void *value_ptr)
+{
+// _endthreadex((unsigned)value_ptr);
+}
diff --git a/protocols/Gadu-Gadu/libgadu/pthread.h b/protocols/Gadu-Gadu/libgadu/pthread.h
new file mode 100644
index 0000000000..4de1053e0e
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/pthread.h
@@ -0,0 +1,56 @@
+/*
+pthread Portable implementation
+
+Copyright (c) 2003 Robert Rainwater
+Copyright (c) 2003-2005 Adam Strzelecki
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef PTHREAD_H
+#define PTHREAD_H
+
+#include <windows.h>
+
+// Minipthread code from Miranda IM source
+typedef struct
+{
+ HANDLE hThread;
+ DWORD dwThreadId;
+}
+pthread_t;
+
+typedef int pthread_attr_t;
+typedef CRITICAL_SECTION pthread_mutex_t;
+
+/* create thread */
+int pthread_create(pthread_t *tid, const pthread_attr_t *attr, void *(__stdcall *thread_start) (void *), void *param);
+/* wait for thread termination */
+int pthread_join(pthread_t *tid, void **value_ptr);
+/* detach a thread */
+int pthread_detach(pthread_t *tid);
+/* get calling thread's ID */
+pthread_t *pthread_self(void);
+/* cancel execution of a thread */
+int pthread_cancel(pthread_t *thread);
+/* terminate thread */
+void pthread_exit(void *value_ptr);
+
+#define pthread_mutex_init(pmutex, pattr) InitializeCriticalSection(pmutex)
+#define pthread_mutex_destroy(pmutex) DeleteCriticalSection(pmutex)
+#define pthread_mutex_lock(pmutex) EnterCriticalSection(pmutex)
+#define pthread_mutex_unlock(pmutex) LeaveCriticalSection(pmutex)
+
+#endif
diff --git a/protocols/Gadu-Gadu/libgadu/pubdir.c b/protocols/Gadu-Gadu/libgadu/pubdir.c
new file mode 100644
index 0000000000..f9d656dfe3
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/pubdir.c
@@ -0,0 +1,862 @@
+/* coding: UTF-8 */
+/* $Id: pubdir.c 11370 2010-03-13 16:17:54Z dezred $ */
+
+/*
+ * (C) Copyright 2001-2006 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Dawid Jarosz <dawjar@poczta.onet.pl>
+ * Adam Wysocki <gophi@ekg.chmurka.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/**
+ * \file pubdir.c
+ *
+ * \brief Obsługa katalogu publicznego
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+#include "win32.h"
+#define random() rand()
+#else
+#include <unistd.h>
+#endif
+
+#include "libgadu.h"
+
+/**
+ * Rejestruje nowego uĹĽytkownika.
+ *
+ * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
+ *
+ * \param email Adres e-mail
+ * \param password Hasło
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartość tokenu
+ * \param async Flaga połączenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup register
+ */
+struct gg_http *gg_register3(const char *email, const char *password, const char *tokenid, const char *tokenval, int async)
+{
+ struct gg_http *h;
+ char *__pwd, *__email, *__tokenid, *__tokenval, *form, *query;
+
+ if (!email || !password || !tokenid || !tokenval) {
+ gg_debug(GG_DEBUG_MISC, "=> register, NULL parameter\n");
+ errno = EFAULT;
+ return NULL;
+ }
+
+ __pwd = gg_urlencode(password);
+ __email = gg_urlencode(email);
+ __tokenid = gg_urlencode(tokenid);
+ __tokenval = gg_urlencode(tokenval);
+
+ if (!__pwd || !__email || !__tokenid || !__tokenval) {
+ gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for form fields\n");
+ free(__pwd);
+ free(__email);
+ free(__tokenid);
+ free(__tokenval);
+ return NULL;
+ }
+
+ form = gg_saprintf("pwd=%s&email=%s&tokenid=%s&tokenval=%s&code=%u",
+ __pwd, __email, __tokenid, __tokenval,
+ gg_http_hash("ss", email, password));
+
+ free(__pwd);
+ free(__email);
+ free(__tokenid);
+ free(__tokenval);
+
+ if (!form) {
+ gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for form query\n");
+ return NULL;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "=> register, %s\n", form);
+
+ query = gg_saprintf(
+ "Host: " GG_REGISTER_HOST "\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "User-Agent: " GG_HTTP_USERAGENT "\r\n"
+ "Content-Length: %d\r\n"
+ "Pragma: no-cache\r\n"
+ "\r\n"
+ "%s",
+ (int) strlen(form), form);
+
+ free(form);
+
+ if (!query) {
+ gg_debug(GG_DEBUG_MISC, "=> register, not enough memory for query\n");
+ return NULL;
+ }
+
+ if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/fmregister3.asp", query))) {
+ gg_debug(GG_DEBUG_MISC, "=> register, gg_http_connect() failed mysteriously\n");
+ free(query);
+ return NULL;
+ }
+
+ h->type = GG_SESSION_REGISTER;
+
+ free(query);
+
+ h->callback = gg_pubdir_watch_fd;
+ h->destroy = gg_pubdir_free;
+
+ if (!async)
+ gg_pubdir_watch_fd(h);
+
+ return h;
+}
+
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie siÄ™ w polu \c error.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
+ *
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup register
+ */
+int gg_register_watch_fd(struct gg_httpd *h)
+{
+ return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura połączenia
+ *
+ * \ingroup register
+ */
+void gg_register_free(struct gg_http *h)
+{
+ return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Usuwa uĹĽytkownika.
+ *
+ * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
+ *
+ * \param uin Numer Gadu-Gadu
+ * \param password Hasło
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartość tokenu
+ * \param async Flaga połączenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup unregister
+ */
+struct gg_http *gg_unregister3(uin_t uin, const char *password, const char *tokenid, const char *tokenval, int async)
+{
+ struct gg_http *h;
+ char *__fmpwd, *__pwd, *__tokenid, *__tokenval, *form, *query;
+
+ if (!password || !tokenid || !tokenval) {
+ gg_debug(GG_DEBUG_MISC, "=> unregister, NULL parameter\n");
+ errno = EFAULT;
+ return NULL;
+ }
+
+ __pwd = gg_saprintf("%ld", random());
+ __fmpwd = gg_urlencode(password);
+ __tokenid = gg_urlencode(tokenid);
+ __tokenval = gg_urlencode(tokenval);
+
+ if (!__fmpwd || !__pwd || !__tokenid || !__tokenval) {
+ gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for form fields\n");
+ free(__pwd);
+ free(__fmpwd);
+ free(__tokenid);
+ free(__tokenval);
+ return NULL;
+ }
+
+ form = gg_saprintf("fmnumber=%d&fmpwd=%s&delete=1&pwd=%s&email=deletedaccount@gadu-gadu.pl&tokenid=%s&tokenval=%s&code=%u", uin, __fmpwd, __pwd, __tokenid, __tokenval, gg_http_hash("ss", "deletedaccount@gadu-gadu.pl", __pwd));
+
+ free(__fmpwd);
+ free(__pwd);
+ free(__tokenid);
+ free(__tokenval);
+
+ if (!form) {
+ gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for form query\n");
+ return NULL;
+ }
+
+ gg_debug(GG_DEBUG_MISC, "=> unregister, %s\n", form);
+
+ query = gg_saprintf(
+ "Host: " GG_REGISTER_HOST "\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "User-Agent: " GG_HTTP_USERAGENT "\r\n"
+ "Content-Length: %d\r\n"
+ "Pragma: no-cache\r\n"
+ "\r\n"
+ "%s",
+ (int) strlen(form), form);
+
+ free(form);
+
+ if (!query) {
+ gg_debug(GG_DEBUG_MISC, "=> unregister, not enough memory for query\n");
+ return NULL;
+ }
+
+ if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/fmregister3.asp", query))) {
+ gg_debug(GG_DEBUG_MISC, "=> unregister, gg_http_connect() failed mysteriously\n");
+ free(query);
+ return NULL;
+ }
+
+ h->type = GG_SESSION_UNREGISTER;
+
+ free(query);
+
+ h->callback = gg_pubdir_watch_fd;
+ h->destroy = gg_pubdir_free;
+
+ if (!async)
+ gg_pubdir_watch_fd(h);
+
+ return h;
+}
+
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie siÄ™ w polu \c error.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
+ *
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup unregister
+ */
+int gg_unregister_watch_fd(struct gg_httpd *h)
+{
+ return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura połączenia
+ *
+ * \ingroup unregister
+ */
+void gg_unregister_free(struct gg_http *h)
+{
+ return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Zmienia hasło użytkownika.
+ *
+ * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
+ *
+ * \param uin Numer Gadu-Gadu
+ * \param email Adres e-mail
+ * \param passwd Obecne hasło
+ * \param newpasswd Nowe hasło
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartość tokenu
+ * \param async Flaga połączenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup passwd
+ */
+struct gg_http *gg_change_passwd4(uin_t uin, const char *email, const char *passwd, const char *newpasswd, const char *tokenid, const char *tokenval, int async)
+{
+ struct gg_http *h;
+ char *form, *query, *__email, *__fmpwd, *__pwd, *__tokenid, *__tokenval;
+
+ if (!uin || !email || !passwd || !newpasswd || !tokenid || !tokenval) {
+ gg_debug(GG_DEBUG_MISC, "=> change, NULL parameter\n");
+ errno = EFAULT;
+ return NULL;
+ }
+
+ __fmpwd = gg_urlencode(passwd);
+ __pwd = gg_urlencode(newpasswd);
+ __email = gg_urlencode(email);
+ __tokenid = gg_urlencode(tokenid);
+ __tokenval = gg_urlencode(tokenval);
+
+ if (!__fmpwd || !__pwd || !__email || !__tokenid || !__tokenval) {
+ gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for form fields\n");
+ free(__fmpwd);
+ free(__pwd);
+ free(__email);
+ free(__tokenid);
+ free(__tokenval);
+ return NULL;
+ }
+
+ if (!(form = gg_saprintf("fmnumber=%d&fmpwd=%s&pwd=%s&email=%s&tokenid=%s&tokenval=%s&code=%u", uin, __fmpwd, __pwd, __email, __tokenid, __tokenval, gg_http_hash("ss", email, newpasswd)))) {
+ gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for form fields\n");
+ free(__fmpwd);
+ free(__pwd);
+ free(__email);
+ free(__tokenid);
+ free(__tokenval);
+
+ return NULL;
+ }
+
+ free(__fmpwd);
+ free(__pwd);
+ free(__email);
+ free(__tokenid);
+ free(__tokenval);
+
+ gg_debug(GG_DEBUG_MISC, "=> change, %s\n", form);
+
+ query = gg_saprintf(
+ "Host: " GG_REGISTER_HOST "\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "User-Agent: " GG_HTTP_USERAGENT "\r\n"
+ "Content-Length: %d\r\n"
+ "Pragma: no-cache\r\n"
+ "\r\n"
+ "%s",
+ (int) strlen(form), form);
+
+ free(form);
+
+ if (!query) {
+ gg_debug(GG_DEBUG_MISC, "=> change, not enough memory for query\n");
+ return NULL;
+ }
+
+ if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/fmregister3.asp", query))) {
+ gg_debug(GG_DEBUG_MISC, "=> change, gg_http_connect() failed mysteriously\n");
+ free(query);
+ return NULL;
+ }
+
+ h->type = GG_SESSION_PASSWD;
+
+ free(query);
+
+ h->callback = gg_pubdir_watch_fd;
+ h->destroy = gg_pubdir_free;
+
+ if (!async)
+ gg_pubdir_watch_fd(h);
+
+ return h;
+}
+
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie siÄ™ w polu \c error.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
+ *
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup passwd
+ */
+int gg_change_passwd_watch_fd(struct gg_httpd *h)
+{
+ return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura połączenia
+ *
+ * \ingroup passwd
+ */
+void gg_change_passwd_free(struct gg_http *h)
+{
+ return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Wysyła hasło użytkownika na e-mail.
+ *
+ * Wymaga wcześniejszego pobrania tokenu za pomocą \c gg_token().
+ *
+ * \param uin Numer Gadu-Gadu
+ * \param email Adres e-mail (podany przy rejestracji)
+ * \param tokenid Identyfikator tokenu
+ * \param tokenval Zawartość tokenu
+ * \param async Flaga połączenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup remind
+ */
+struct gg_http *gg_remind_passwd3(uin_t uin, const char *email, const char *tokenid, const char *tokenval, int async)
+{
+ struct gg_http *h;
+ char *form, *query, *__tokenid, *__tokenval, *__email;
+
+ if (!tokenid || !tokenval || !email) {
+ gg_debug(GG_DEBUG_MISC, "=> remind, NULL parameter\n");
+ errno = EFAULT;
+ return NULL;
+ }
+
+ __tokenid = gg_urlencode(tokenid);
+ __tokenval = gg_urlencode(tokenval);
+ __email = gg_urlencode(email);
+
+ if (!__tokenid || !__tokenval || !__email) {
+ gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for form fields\n");
+ free(__tokenid);
+ free(__tokenval);
+ free(__email);
+ return NULL;
+ }
+
+ if (!(form = gg_saprintf("userid=%d&code=%u&tokenid=%s&tokenval=%s&email=%s", uin, gg_http_hash("u", uin), __tokenid, __tokenval, __email))) {
+ gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for form fields\n");
+ free(__tokenid);
+ free(__tokenval);
+ free(__email);
+ return NULL;
+ }
+
+ free(__tokenid);
+ free(__tokenval);
+ free(__email);
+
+ gg_debug(GG_DEBUG_MISC, "=> remind, %s\n", form);
+
+ query = gg_saprintf(
+ "Host: " GG_REMIND_HOST "\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "User-Agent: " GG_HTTP_USERAGENT "\r\n"
+ "Content-Length: %d\r\n"
+ "Pragma: no-cache\r\n"
+ "\r\n"
+ "%s",
+ (int) strlen(form), form);
+
+ free(form);
+
+ if (!query) {
+ gg_debug(GG_DEBUG_MISC, "=> remind, not enough memory for query\n");
+ return NULL;
+ }
+
+ if (!(h = gg_http_connect(GG_REMIND_HOST, GG_REMIND_PORT, async, "POST", "/appsvc/fmsendpwd3.asp", query))) {
+ gg_debug(GG_DEBUG_MISC, "=> remind, gg_http_connect() failed mysteriously\n");
+ free(query);
+ return NULL;
+ }
+
+ h->type = GG_SESSION_REMIND;
+
+ free(query);
+
+ h->callback = gg_pubdir_watch_fd;
+ h->destroy = gg_pubdir_free;
+
+ if (!async)
+ gg_pubdir_watch_fd(h);
+
+ return h;
+}
+
+#ifdef DOXYGEN
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie siÄ™ w polu \c error.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do
+ * \c gg_pubdir_watch_fd().
+ *
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup remind
+ */
+int gg_remind_watch_fd(struct gg_httpd *h)
+{
+ return gg_pubdir_watch_fd(h);
+}
+
+/**
+ * Zwalnia zasoby po operacji.
+ *
+ * \note W rzeczywistości funkcja jest makrem rozwijanym do \c gg_pubdir_free().
+ *
+ * \param h Struktura połączenia
+ *
+ * \ingroup remind
+ */
+void gg_remind_free(struct gg_http *h)
+{
+ return gg_pubdir_free(h);
+}
+
+#endif /* DOXYGEN */
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie siÄ™ w polu \c error.
+ *
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_pubdir_watch_fd(struct gg_http *h)
+{
+ struct gg_pubdir *p;
+ char *tmp;
+
+ if (!h) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (h->state == GG_STATE_ERROR) {
+ gg_debug(GG_DEBUG_MISC, "=> pubdir, watch_fd issued on failed session\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (h->state != GG_STATE_PARSING) {
+ if (gg_http_watch_fd(h) == -1) {
+ gg_debug(GG_DEBUG_MISC, "=> pubdir, http failure\n");
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ if (h->state != GG_STATE_PARSING)
+ return 0;
+
+ h->state = GG_STATE_DONE;
+
+ if (!(h->data = p = malloc(sizeof(struct gg_pubdir)))) {
+ gg_debug(GG_DEBUG_MISC, "=> pubdir, not enough memory for results\n");
+ return -1;
+ }
+
+ p->success = 0;
+ p->uin = 0;
+
+ gg_debug(GG_DEBUG_MISC, "=> pubdir, let's parse \"%s\"\n", h->body);
+
+ if ((tmp = strstr(h->body, "Tokens okregisterreply_packet.reg.dwUserId="))) {
+ p->success = 1;
+ p->uin = strtol(tmp + sizeof("Tokens okregisterreply_packet.reg.dwUserId=") - 1, NULL, 0);
+ 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);
+ gg_debug(GG_DEBUG_MISC, "=> pubdir, success (uin=%d)\n", p->uin);
+ } else
+ gg_debug(GG_DEBUG_MISC, "=> pubdir, error.\n");
+
+ return 0;
+}
+
+/**
+ * Zwalnia zasoby po operacji na katalogu publicznym.
+ *
+ * \param h Struktura połączenia
+ */
+void gg_pubdir_free(struct gg_http *h)
+{
+ if (!h)
+ return;
+
+ free(h->data);
+ gg_http_free(h);
+}
+
+/**
+ * Pobiera token do autoryzacji operacji na katalogu publicznym.
+ *
+ * Token jest niezbędny do tworzenia nowego i usuwania użytkownika,
+ * zmiany hasła itd.
+ *
+ * \param async Flaga połączenia asynchronicznego
+ *
+ * \return Struktura \c gg_http lub \c NULL w przypadku błędu
+ *
+ * \ingroup token
+ */
+struct gg_http *gg_token(int async)
+{
+ struct gg_http *h;
+ const char *query;
+
+ query = "Host: " GG_REGISTER_HOST "\r\n"
+ "Content-Type: application/x-www-form-urlencoded\r\n"
+ "User-Agent: " GG_HTTP_USERAGENT "\r\n"
+ "Content-Length: 0\r\n"
+ "Pragma: no-cache\r\n"
+ "\r\n";
+
+ if (!(h = gg_http_connect(GG_REGISTER_HOST, GG_REGISTER_PORT, async, "POST", "/appsvc/regtoken.asp", query))) {
+ gg_debug(GG_DEBUG_MISC, "=> token, gg_http_connect() failed mysteriously\n");
+ return NULL;
+ }
+
+ h->type = GG_SESSION_TOKEN;
+
+ h->callback = gg_token_watch_fd;
+ h->destroy = gg_token_free;
+
+ if (!async)
+ gg_token_watch_fd(h);
+
+ return h;
+}
+
+/**
+ * Funkcja wywoływana po zaobserwowaniu zmian na deskryptorze połączenia.
+ *
+ * Operacja będzie zakończona, gdy pole \c state będzie równe \c GG_STATE_DONE.
+ * Jeśli wystąpi błąd, \c state będzie równe \c GG_STATE_ERROR, a kod błędu
+ * znajdzie siÄ™ w polu \c error.
+ *
+ * \param h Struktura połączenia
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup token
+ */
+int gg_token_watch_fd(struct gg_http *h)
+{
+ if (!h) {
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (h->state == GG_STATE_ERROR) {
+ gg_debug(GG_DEBUG_MISC, "=> token, watch_fd issued on failed session\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (h->state != GG_STATE_PARSING) {
+ if (gg_http_watch_fd(h) == -1) {
+ gg_debug(GG_DEBUG_MISC, "=> token, http failure\n");
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ if (h->state != GG_STATE_PARSING)
+ return 0;
+
+ /* jeśli h->data jest puste, to ściągaliśmy tokenid i url do niego,
+ * ale jeśli coś tam jest, to znaczy, że mamy drugi etap polegający
+ * na pobieraniu tokenu. */
+ if (!h->data) {
+ int width, height, length;
+ char *url = NULL, *tokenid = NULL, *path, *headers;
+ const char *host;
+ struct gg_http *h2;
+ struct gg_token *t;
+
+ gg_debug(GG_DEBUG_MISC, "=> token body \"%s\"\n", h->body);
+
+ if (h->body && (!(url = malloc(strlen(h->body))) || !(tokenid = malloc(strlen(h->body))))) {
+ gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for results\n");
+ free(url);
+ return -1;
+ }
+
+ if (!h->body || sscanf(h->body, "%d %d %d\r\n%s\r\n%s", &width, &height, &length, tokenid, url) != 5) {
+ gg_debug(GG_DEBUG_MISC, "=> token, parsing failed\n");
+ free(url);
+ free(tokenid);
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* dostaliśmy tokenid i wszystkie niezbędne informacje,
+ * więc pobierzmy obrazek z tokenem */
+
+ if (strncmp(url, "http://", 7)) {
+ path = gg_saprintf("%s?tokenid=%s", url, tokenid);
+ host = GG_REGISTER_HOST;
+ } else {
+ char *slash = strchr(url + 7, '/');
+
+ if (slash) {
+ path = gg_saprintf("%s?tokenid=%s", slash, tokenid);
+ *slash = 0;
+ host = url + 7;
+ } else {
+ gg_debug(GG_DEBUG_MISC, "=> token, url parsing failed\n");
+ free(url);
+ free(tokenid);
+ errno = EINVAL;
+ return -1;
+ }
+ }
+
+ if (!path) {
+ gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token url\n");
+ free(url);
+ free(tokenid);
+ return -1;
+ }
+
+ if (!(headers = gg_saprintf("Host: %s\r\nUser-Agent: " GG_HTTP_USERAGENT "\r\n\r\n", host))) {
+ gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token url\n");
+ free(path);
+ free(url);
+ free(tokenid);
+ return -1;
+ }
+
+ if (!(h2 = gg_http_connect(host, GG_REGISTER_PORT, h->async, "GET", path, headers))) {
+ gg_debug(GG_DEBUG_MISC, "=> token, gg_http_connect() failed mysteriously\n");
+ free(headers);
+ free(url);
+ free(path);
+ free(tokenid);
+ return -1;
+ }
+
+ free(headers);
+ free(path);
+ free(url);
+
+ gg_http_free_fields(h);
+
+ memcpy(h, h2, sizeof(struct gg_http));
+ free(h2);
+
+ h->type = GG_SESSION_TOKEN;
+
+ h->callback = gg_token_watch_fd;
+ h->destroy = gg_token_free;
+
+ if (!h->async)
+ gg_token_watch_fd(h);
+
+ if (!(h->data = t = malloc(sizeof(struct gg_token)))) {
+ gg_debug(GG_DEBUG_MISC, "=> token, not enough memory for token data\n");
+ free(tokenid);
+ return -1;
+ }
+
+ t->width = width;
+ t->height = height;
+ t->length = length;
+ t->tokenid = tokenid;
+ } else {
+ /* obrazek mamy w h->body */
+ h->state = GG_STATE_DONE;
+ }
+
+ return 0;
+}
+
+/**
+ * Zwalnia zasoby po operacji pobierania tokenu.
+ *
+ * \param h Struktura połączenia
+ *
+ * \ingroup token
+ */
+void gg_token_free(struct gg_http *h)
+{
+ struct gg_token *t;
+
+ if (!h)
+ return;
+
+ if ((t = h->data))
+ free(t->tokenid);
+
+ free(h->data);
+ gg_http_free(h);
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/protocols/Gadu-Gadu/libgadu/pubdir50.c b/protocols/Gadu-Gadu/libgadu/pubdir50.c
new file mode 100644
index 0000000000..6914cbd01a
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/pubdir50.c
@@ -0,0 +1,557 @@
+/* coding: UTF-8 */
+/* $Id: pubdir50.c 11370 2010-03-13 16:17:54Z dezred $ */
+
+/*
+ * (C) Copyright 2003 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/**
+ * \file pubdir50.c
+ *
+ * \brief Obsługa katalogu publicznego od wersji Gadu-Gadu 5.x
+ */
+
+#ifndef _WIN64
+#define _USE_32BIT_TIME_T
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#ifdef _WIN32
+#include "win32.h"
+#undef small
+#endif
+
+#include "libgadu.h"
+#include "internal.h"
+
+/**
+ * Tworzy nowe zapytanie katalogu publicznego.
+ *
+ * \param type Rodzaj zapytania
+ *
+ * \return Zmienna \c gg_pubdir50_t lub \c NULL w przypadku błędu.
+ *
+ * \ingroup pubdir50
+ */
+gg_pubdir50_t gg_pubdir50_new(int type)
+{
+ gg_pubdir50_t res = malloc(sizeof(struct gg_pubdir50_s));
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_new(%d);\n", type);
+
+ if (!res) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_new() out of memory\n");
+ return NULL;
+ }
+
+ memset(res, 0, sizeof(struct gg_pubdir50_s));
+
+ res->type = type;
+
+ return res;
+}
+
+/**
+ * \internal Dodaje lub zastępuje pole zapytania lub odpowiedzi katalogu
+ * publicznego.
+ *
+ * \param req Zapytanie lub odpowiedĹş
+ * \param num Numer wyniku odpowiedzi (0 dla zapytania)
+ * \param field Nazwa pola
+ * \param value Wartość pola
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+static int gg_pubdir50_add_n(gg_pubdir50_t req, int num, const char *field, const char *value)
+{
+ struct gg_pubdir50_entry *tmp = NULL, *entry;
+ char *dupfield, *dupvalue;
+ int i;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_add_n(%p, %d, \"%s\", \"%s\");\n", req, num, field, value);
+
+ if (!(dupvalue = strdup(value))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
+ return -1;
+ }
+
+ for (i = 0; i < req->entries_count; i++) {
+ if (req->entries[i].num != num || strcmp(req->entries[i].field, field))
+ continue;
+
+ free(req->entries[i].value);
+ req->entries[i].value = dupvalue;
+
+ return 0;
+ }
+
+ if (!(dupfield = strdup(field))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
+ free(dupvalue);
+ return -1;
+ }
+
+ if (!(tmp = realloc(req->entries, sizeof(struct gg_pubdir50_entry) * (req->entries_count + 1)))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_add_n() out of memory\n");
+ free(dupfield);
+ free(dupvalue);
+ return -1;
+ }
+
+ req->entries = tmp;
+
+ entry = &req->entries[req->entries_count];
+ entry->num = num;
+ entry->field = dupfield;
+ entry->value = dupvalue;
+
+ req->entries_count++;
+
+ return 0;
+}
+
+/**
+ * Dodaje pole zapytania.
+ *
+ * \param req Zapytanie
+ * \param field Nazwa pola
+ * \param value Wartość pola
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup pubdir50
+ */
+int gg_pubdir50_add(gg_pubdir50_t req, const char *field, const char *value)
+{
+ return gg_pubdir50_add_n(req, 0, field, value);
+}
+
+/**
+ * Ustawia numer sekwencyjny zapytania.
+ *
+ * \param req Zapytanie
+ * \param seq Numer sekwencyjny
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ *
+ * \ingroup pubdir50
+ */
+int gg_pubdir50_seq_set(gg_pubdir50_t req, uint32_t seq)
+{
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_seq_set(%p, %d);\n", req, seq);
+
+ if (!req) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_seq_set() invalid arguments\n");
+ errno = EFAULT;
+ return -1;
+ }
+
+ req->seq = seq;
+
+ return 0;
+}
+
+/**
+ * Zwalnia zasoby po zapytaniu lub odpowiedzi katalogu publicznego.
+ *
+ * \param s Zapytanie lub odpowiedĹş
+ *
+ * \ingroup pubdir50
+ */
+void gg_pubdir50_free(gg_pubdir50_t s)
+{
+ int i;
+
+ if (!s)
+ return;
+
+ for (i = 0; i < s->entries_count; i++) {
+ free(s->entries[i].field);
+ free(s->entries[i].value);
+ }
+
+ free(s->entries);
+ free(s);
+}
+
+/**
+ * Wysyła zapytanie katalogu publicznego do serwera.
+ *
+ * \param sess Struktura sesji
+ * \param req Zapytanie
+ *
+ * \return Numer sekwencyjny zapytania lub 0 w przypadku błędu
+ *
+ * \ingroup pubdir50
+ */
+uint32_t gg_pubdir50(struct gg_session *sess, gg_pubdir50_t req)
+{
+ size_t size = 5;
+ int i;
+ uint32_t res;
+ char *buf, *p;
+ struct gg_pubdir50_request *r;
+
+ gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_pubdir50(%p, %p);\n", sess, req);
+
+ if (!sess || !req) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() invalid arguments\n");
+ errno = EFAULT;
+ return 0;
+ }
+
+ if (sess->state != GG_STATE_CONNECTED) {
+ gg_debug_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() not connected\n");
+ errno = ENOTCONN;
+ return 0;
+ }
+
+ for (i = 0; i < req->entries_count; i++) {
+ /* wyszukiwanie bierze tylko pierwszy wpis */
+ if (req->entries[i].num)
+ continue;
+
+ if (sess->encoding == GG_ENCODING_CP1250) {
+ size += strlen(req->entries[i].field) + 1;
+ size += strlen(req->entries[i].value) + 1;
+ } else {
+ char *tmp;
+
+ 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_session(sess, GG_DEBUG_MISC, "// gg_pubdir50() out of memory (%d bytes)\n", size);
+ return 0;
+ }
+
+ if (!req->seq)
+ req->seq = (uint32_t)time(NULL);
+
+ res = req->seq;
+
+ r = (struct gg_pubdir50_request*) buf;
+ r->type = req->type;
+ r->seq = gg_fix32(req->seq);
+
+ for (i = 0, p = buf + 5; i < req->entries_count; i++) {
+ if (req->entries[i].num)
+ continue;
+
+ if (sess->encoding == GG_ENCODING_CP1250) {
+ strcpy(p, req->entries[i].field);
+ p += strlen(p) + 1;
+
+ strcpy(p, req->entries[i].value);
+ p += strlen(p) + 1;
+ } else {
+ char *tmp;
+
+ tmp = gg_utf8_to_cp(req->entries[i].field);
+
+ if (tmp == NULL) {
+ free(buf);
+ return -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)
+ res = 0;
+
+ free(buf);
+
+ return res;
+}
+
+/*
+ * \internal Analizuje przychodzÄ…cy pakiet odpowiedzi i zapisuje wynik
+ * w strukturze \c gg_event.
+ *
+ * \param sess Struktura sesji
+ * \param e Struktura zdarzenia
+ * \param packet Pakiet odpowiedzi
+ * \param length Długość pakietu odpowiedzi
+ *
+ * \return 0 jeśli się powiodło, -1 w przypadku błędu
+ */
+int gg_pubdir50_handle_reply_sess(struct gg_session *sess, struct gg_event *e, const char *packet, int length)
+{
+ const char *end = packet + length, *p;
+ 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_sess(%p, %p, %p, %d);\n", sess, e, packet, length);
+
+ if (!sess || !e || !packet) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() invalid arguments\n");
+ errno = EFAULT;
+ return -1;
+ }
+
+ if (length < 5) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() packet too short\n");
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (!(res = gg_pubdir50_new(r->type))) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() unable to allocate reply\n");
+ return -1;
+ }
+
+ e->event.pubdir50 = res;
+
+ res->seq = gg_fix32(r->seq);
+
+ switch (res->type) {
+ case GG_PUBDIR50_READ:
+ e->type = GG_EVENT_PUBDIR50_READ;
+ break;
+
+ case GG_PUBDIR50_WRITE:
+ e->type = GG_EVENT_PUBDIR50_WRITE;
+ break;
+
+ default:
+ e->type = GG_EVENT_PUBDIR50_SEARCH_REPLY;
+ break;
+ }
+
+ /* brak wynikĂłw? */
+ if (length == 5)
+ return 0;
+
+ /* pomiń początek odpowiedzi */
+ p = packet + 5;
+
+ while (p < end) {
+ const char *field, *value;
+
+ field = p;
+
+ /* sprawdź, czy nie mamy podziału na kolejne pole */
+ if (!*field) {
+ num++;
+ field++;
+ }
+
+ value = NULL;
+
+ for (p = field; p < end; p++) {
+ /* jeśli mamy koniec tekstu... */
+ if (!*p) {
+ /* ...i jeszcze nie mieliśmy wartości pola to
+ * wiemy, że po tym zerze jest wartość... */
+ if (!value)
+ value = p + 1;
+ else
+ /* ...w przeciwym wypadku koniec
+ * wartości i możemy wychodzić
+ * grzecznie z pętli */
+ break;
+ }
+ }
+
+ /* sprawdĹşmy, czy pole nie wychodzi poza pakiet, ĹĽeby nie
+ * mieć segfaultów, jeśli serwer przestanie zakańczać pakietów
+ * przez \0 */
+
+ if (p == end) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_handle_reply() premature end of packet\n");
+ goto failure;
+ }
+
+ p++;
+
+ /* jeśli dostaliśmy namier na następne wyniki, to znaczy że
+ * mamy koniec wynikĂłw i nie jest to kolejna osoba. */
+ if (!strcasecmp(field, "nextstart")) {
+ res->next = atoi(value);
+ num--;
+ } else {
+ if (sess->encoding == GG_ENCODING_CP1250) {
+ if (gg_pubdir50_add_n(res, num, field, value) == -1)
+ goto failure;
+ } else {
+ char *tmp;
+
+ tmp = gg_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);
+ }
+ }
+ }
+
+ res->count = num + 1;
+
+ return 0;
+
+failure:
+ gg_pubdir50_free(res);
+ return -1;
+}
+
+/**
+ * Pobiera pole z odpowiedzi katalogu publicznego.
+ *
+ * \param res OdpowiedĹş
+ * \param num Numer wyniku odpowiedzi
+ * \param field Nazwa pola (wielkość liter nie ma znaczenia)
+ *
+ * \return Wartość pola lub \c NULL jeśli nie znaleziono
+ *
+ * \ingroup pubdir50
+ */
+const char *gg_pubdir50_get(gg_pubdir50_t res, int num, const char *field)
+{
+ char *value = NULL;
+ int i;
+
+ gg_debug(GG_DEBUG_FUNCTION, "** gg_pubdir50_get(%p, %d, \"%s\");\n", res, num, field);
+
+ if (!res || num < 0 || !field) {
+ gg_debug(GG_DEBUG_MISC, "// gg_pubdir50_get() invalid arguments\n");
+ errno = EINVAL;
+ return NULL;
+ }
+
+ for (i = 0; i < res->entries_count; i++) {
+ if (res->entries[i].num == num && !strcasecmp(res->entries[i].field, field)) {
+ value = res->entries[i].value;
+ break;
+ }
+ }
+
+ return value;
+}
+
+/**
+ * Zwraca liczbÄ™ wynikĂłw odpowiedzi.
+ *
+ * \param res OdpowiedĹş
+ *
+ * \return Liczba wyników lub -1 w przypadku błędu
+ *
+ * \ingroup pubdir50
+ */
+int gg_pubdir50_count(gg_pubdir50_t res)
+{
+ return (!res) ? -1 : res->count;
+}
+
+/**
+ * Zwraca rodzaj zapytania lub odpowiedzi.
+ *
+ * \param res Zapytanie lub odpowiedĹş
+ *
+ * \return Rodzaj lub -1 w przypadku błędu
+ *
+ * \ingroup pubdir50
+ */
+int gg_pubdir50_type(gg_pubdir50_t res)
+{
+ return (!res) ? -1 : res->type;
+}
+
+/**
+ * Zwraca numer, od ktĂłrego naleĹĽy rozpoczÄ…c kolejne wyszukiwanie.
+ *
+ * Dłuższe odpowiedzi katalogu publicznego są wysyłane przez serwer
+ * w mniejszych paczkach. Po otrzymaniu odpowiedzi, jeśli numer kolejnego
+ * wyszukiwania jest większy od zera, dalsze wyniki można otrzymać przez
+ * wywołanie kolejnego zapytania z określonym numerem początkowym.
+ *
+ * \param res OdpowiedĹş
+ *
+ * \return Numer lub -1 w przypadku błędu
+ *
+ * \ingroup pubdir50
+ */
+uin_t gg_pubdir50_next(gg_pubdir50_t res)
+{
+ return (!res) ? (unsigned) -1 : res->next;
+}
+
+/**
+ * Zwraca numer sekwencyjny zapytania lub odpowiedzi.
+ *
+ * \param res Zapytanie lub odpowiedĹş
+ *
+ * \return Numer sekwencyjny lub -1 w przypadku błędu
+ *
+ * \ingroup pubdir50
+ */
+uint32_t gg_pubdir50_seq(gg_pubdir50_t res)
+{
+ return (!res) ? (unsigned) -1 : res->seq;
+}
+
+/*
+ * Local variables:
+ * c-indentation-style: k&r
+ * c-basic-offset: 8
+ * indent-tabs-mode: notnil
+ * End:
+ *
+ * vim: shiftwidth=8:
+ */
diff --git a/protocols/Gadu-Gadu/libgadu/resolver.c b/protocols/Gadu-Gadu/libgadu/resolver.c
new file mode 100644
index 0000000000..1adef3ef9d
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/resolver.c
@@ -0,0 +1,766 @@
+/* coding: UTF-8 */
+/* $Id$ */
+
+/*
+ * (C) Copyright 2001-2009 Wojtek Kaniewski <wojtekka@irc.pl>
+ * Robert J. WoĹşny <speedy@ziew.org>
+ * Arkadiusz Miśkiewicz <arekm@pld-linux.org>
+ * Tomasz Chiliński <chilek@chilan.com>
+ * Adam Wysocki <gophi@ekg.chmurka.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/**
+ * \file resolver.c
+ *
+ * \brief Funkcje rozwiÄ…zywania nazw
+ */
+
+#ifdef _WIN32
+#include "win32.h"
+#else
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif /* _WIN32 */
+
+#ifndef _WIN32
+#include <netdb.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+#include <signal.h>
+
+#include "libgadu.h"
+#include "resolver.h"
+#include "compat.h"
+
+/** SposĂłb rozwiÄ…zywania nazw serwerĂłw */
+static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT;
+
+/** Funkcja rozpoczynajÄ…ca rozwiÄ…zywanie nazwy */
+static int (*gg_global_resolver_start)(SOCKET *fd, void **private_data, const char *hostname);
+
+/** Funkcja zwalniajÄ…ca zasoby po rozwiÄ…zaniu nazwy */
+static void (*gg_global_resolver_cleanup)(void **private_data, int force);
+
+#ifdef GG_CONFIG_HAVE_PTHREAD
+
+#include <pthread.h>
+
+#ifdef GG_CONFIG_HAVE_GETHOSTBYNAME_R
+/**
+ * \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
+#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 */
+};
+
+/**
+ * \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(SOCKET *fd, void **priv_data, const char *hostname)
+{
+ struct gg_resolver_fork_data *data = NULL;
+ struct in_addr addr;
+ int new_errno;
+ SOCKET pipes[2];
+
+ 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) {
+ gg_sock_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 (gg_sock_write(pipes[1], &addr, sizeof(addr)) != sizeof(addr))
+ exit(1);
+
+ exit(0);
+ }
+
+ gg_sock_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);
+ gg_sock_close(pipes[0]);
+ gg_sock_close(pipes[1]);
+
+ errno = new_errno;
+
+ return -1;
+}
+
+/**
+ * \internal Usuwanie zasobĂłw po procesie rozwiÄ…zywaniu nazwy.
+ *
+ * Funkcja wywoływana po zakończeniu rozwiązanywania nazwy lub przy zwalnianiu
+ * zasobĂłw sesji podczas rozwiÄ…zywania nazwy.
+ *
+ * \param priv_data WskaĹşnik na zmiennÄ… przechowujÄ…cÄ… wskaĹşnik do prywatnych
+ * danych
+ * \param force Flaga usuwania zasobów przed zakończeniem działania
+ */
+static void gg_resolver_fork_cleanup(void **priv_data, int force)
+{
+ struct gg_resolver_fork_data *data;
+
+ if (priv_data == NULL || *priv_data == NULL)
+ return;
+
+ data = (struct gg_resolver_fork_data*) *priv_data;
+ *priv_data = NULL;
+
+ if (force)
+ kill(data->pid, SIGKILL);
+
+ waitpid(data->pid, NULL, WNOHANG);
+
+ free(data);
+}
+
+#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 */
+ SOCKET rfd; /*< Deskryptor do odczytu */
+ SOCKET 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) {
+ gg_sock_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 *__stdcall 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 (gg_sock_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(SOCKET *fd, void **priv_data, const char *hostname)
+{
+ struct gg_resolver_pthread_data *data = NULL;
+ int new_errno;
+ SOCKET pipes[2];
+
+ 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);
+ }
+
+ gg_sock_close(pipes[0]);
+ gg_sock_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;
+ }
+
+#if !defined(GG_CONFIG_HAVE_PTHREAD) || !defined(GG_CONFIG_PTHREAD_DEFAULT)
+ type = GG_RESOLVER_FORK;
+#else
+ type = GG_RESOLVER_PTHREAD;
+#endif
+ }
+
+ switch (type) {
+ case GG_RESOLVER_FORK:
+ gs->resolver_type = type;
+ gs->resolver_start = gg_resolver_fork_start;
+ gs->resolver_cleanup = gg_resolver_fork_cleanup;
+ return 0;
+
+#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)(SOCKET*, 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;
+ }
+
+#if !defined(GG_CONFIG_HAVE_PTHREAD) || !defined(GG_CONFIG_PTHREAD_DEFAULT)
+ type = GG_RESOLVER_FORK;
+#else
+ type = GG_RESOLVER_PTHREAD;
+#endif
+ }
+
+ switch (type) {
+ case GG_RESOLVER_FORK:
+ gh->resolver_type = type;
+ gh->resolver_start = gg_resolver_fork_start;
+ gh->resolver_cleanup = gg_resolver_fork_cleanup;
+ return 0;
+
+#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)(SOCKET*, 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;
+
+ 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;
+
+#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 "SOCKET *fd" &mdash; wskaĹşnik na zmiennÄ…, gdzie zostanie umieszczony deskryptor potoku
+ * - \c "void **priv_data" &mdash; wskaźnik na zmienną, gdzie można umieścić wskaźnik do prywatnych danych na potrzeby rozwiązywania nazwy
+ * - \c "const char *name" &mdash; nazwa serwera do rozwiÄ…zania
+ *
+ * Parametry funkcji zwalniającej zasoby wyglądają następująco:
+ * - \c "void **priv_data" &mdash; wskaźnik na zmienną przechowującą wskaźnik do prywatnych danych, należy go ustawić na \c NULL po zakończeniu
+ * - \c "int force" &mdash; 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)(SOCKET*, 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 --git a/protocols/Gadu-Gadu/libgadu/resolver.h b/protocols/Gadu-Gadu/libgadu/resolver.h
new file mode 100644
index 0000000000..145c5178a4
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/resolver.h
@@ -0,0 +1,33 @@
+/* coding: UTF-8 */
+/* $Id$ */
+
+/*
+ * (C) Copyright 2008 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef LIBGADU_RESOLVER_H
+#define LIBGADU_RESOLVER_H
+
+#ifdef _WIN32
+#include "win32.h"
+#else
+#include <arpa/inet.h>
+#endif /* _WIN32 */
+
+int gg_gethostbyname_real(const char *hostname, struct in_addr *result, int pthread);
+
+#endif /* LIBGADU_RESOLVER_H */
diff --git a/protocols/Gadu-Gadu/libgadu/sha1.c b/protocols/Gadu-Gadu/libgadu/sha1.c
new file mode 100644
index 0000000000..d68efc5b98
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/sha1.c
@@ -0,0 +1,308 @@
+/* coding: UTF-8 */
+/* $Id: sha1.c,v 1.4 2007-07-20 23:00:50 wojtekka Exp $ */
+
+/*
+ * (C) Copyright 2007 Wojtek Kaniewski <wojtekka@irc.pl>
+ *
+ * Public domain SHA-1 implementation by Steve Reid <steve@edmweb.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License Version
+ * 2.1 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/**
+ * \file sha1.c
+ *
+ * \brief Funkcje wyznaczania skrĂłtu SHA1
+ */
+
+#include <string.h>
+#include <sys/types.h>
+#ifdef _WIN32
+#include "win32.h"
+#else
+#include <unistd.h>
+#endif
+
+#include "libgadu.h"
+
+/** \cond ignore */
+
+#if defined(GG_CONFIG_HAVE_OPENSSL) && !defined(GG_CONFIG_MIRANDA)
+
+#include <openssl/sha.h>
+
+#else
+
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+Modified by Wojtek Kaniewski <wojtekka@toxygen.net> for compatibility
+with libgadu and OpenSSL API.
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+ A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+ 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+ 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define LITTLE_ENDIAN * This should be #define'd if true. */
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#include <string.h>
+
+typedef struct {
+ uint32_t state[5];
+ uint32_t count[2];
+ unsigned char buffer[64];
+} SHA_CTX;
+
+static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64]);
+static void SHA1_Init(SHA_CTX* context);
+static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len);
+static void SHA1_Final(unsigned char digest[20], SHA_CTX* context);
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#ifndef GG_CONFIG_BIGENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+ |(rol(block->l[i],8)&0x00FF00FF))
+#else
+#define blk0(i) block->l[i]
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+ ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64])
+{
+uint32_t a, b, c, d, e;
+typedef union {
+ unsigned char c[64];
+ uint32_t l[16];
+} CHAR64LONG16;
+CHAR64LONG16* block;
+static unsigned char workspace[64];
+ block = (CHAR64LONG16*)workspace;
+ memcpy(block, buffer, 64);
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+ R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+ R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+ R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+ R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+ R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+ R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+ R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+ R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+ R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+ R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+ R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+ R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+ R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+ R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+ R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+ R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+ R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+ R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+ R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+}
+
+
+/* SHA1_Init - Initialize new context */
+
+static void SHA1_Init(SHA_CTX* context)
+{
+ /* SHA1 initialization constants */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xEFCDAB89;
+ context->state[2] = 0x98BADCFE;
+ context->state[3] = 0x10325476;
+ context->state[4] = 0xC3D2E1F0;
+ context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len)
+{
+unsigned int i, j;
+
+ j = (context->count[0] >> 3) & 63;
+ if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++;
+ context->count[1] += (len >> 29);
+ if ((j + len) > 63) {
+ memcpy(&context->buffer[j], data, (i = 64-j));
+ SHA1_Transform(context->state, context->buffer);
+ for ( ; i + 63 < len; i += 64) {
+ SHA1_Transform(context->state, &data[i]);
+ }
+ j = 0;
+ }
+ else i = 0;
+ memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+static void SHA1_Final(unsigned char digest[20], SHA_CTX* context)
+{
+uint32_t i, j;
+unsigned char finalcount[8];
+
+ for (i = 0; i < 8; i++) {
+ finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+ >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
+ }
+ SHA1_Update(context, (unsigned char *)"\200", 1);
+ while ((context->count[0] & 504) != 448) {
+ SHA1_Update(context, (unsigned char *)"\0", 1);
+ }
+ SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */
+ for (i = 0; i < 20; i++) {
+ digest[i] = (unsigned char)
+ ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+ }
+ /* Wipe variables */
+ i = j = 0;
+ memset(context->buffer, 0, 64);
+ memset(context->state, 0, 20);
+ memset(context->count, 0, 8);
+ memset(&finalcount, 0, 8);
+#ifdef SHA1HANDSOFF /* make SHA1_Transform overwrite it's own static vars */
+ SHA1_Transform(context->state, context->buffer);
+#endif
+}
+
+#endif /* GG_CONFIG_HAVE_OPENSSL */
+
+/** \endcond */
+
+/** \cond internal */
+
+/**
+ * \internal Liczy skrót SHA1 z ziarna i hasła.
+ *
+ * \param password Hasło
+ * \param seed Ziarno
+ * \param result Bufor na wynik funkcji skrĂłtu (20 bajtĂłw)
+ */
+void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result)
+{
+ SHA_CTX ctx;
+
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, (const unsigned char*) password, (unsigned int)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 --git a/protocols/Gadu-Gadu/libgadu/win32.c b/protocols/Gadu-Gadu/libgadu/win32.c
new file mode 100644
index 0000000000..4f5ad3ff5d
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/win32.c
@@ -0,0 +1,65 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2009 Adam Strzelecki <ono+miranda@java.pl>
+// Copyright (c) 2009-2010 Bartosz Białek
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#ifdef _WIN32
+#include "win32.h"
+
+int sockpipe(SOCKET filedes[2])
+{
+ SOCKET sock;
+ struct sockaddr_in sin;
+ unsigned int len = sizeof(sin);
+
+ filedes[0] = filedes[1] = INVALID_SOCKET;
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
+ return -1;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = htons(0);
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ if (bind(sock, (SOCKADDR *)&sin, len) == SOCKET_ERROR ||
+ listen(sock, 1) == SOCKET_ERROR ||
+ getsockname(sock, (SOCKADDR *)&sin, &len) == SOCKET_ERROR) {
+ closesocket(sock);
+ return -1;
+ }
+
+ if ((filedes[1] = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET ||
+ connect(filedes[1], (SOCKADDR *)&sin, len) == SOCKET_ERROR) {
+ closesocket(sock);
+ return -1;
+ }
+
+ if ((filedes[0] = accept(sock, (SOCKADDR *)&sin, &len)) == INVALID_SOCKET) {
+ closesocket(filedes[1]);
+ filedes[1] = INVALID_SOCKET;
+ closesocket(sock);
+ return -1;
+ }
+
+ closesocket(sock);
+ return 0;
+}
+
+#endif /* _WIN32 */
diff --git a/protocols/Gadu-Gadu/libgadu/win32.h b/protocols/Gadu-Gadu/libgadu/win32.h
new file mode 100644
index 0000000000..d432a359ac
--- /dev/null
+++ b/protocols/Gadu-Gadu/libgadu/win32.h
@@ -0,0 +1,75 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2009 Adam Strzelecki <ono+miranda@java.pl>
+// Copyright (c) 2009-2010 Bartosz Białek
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+/* Windows wrappers for missing POSIX functions */
+
+#ifndef __GG_WIN32_H
+#define __GG_WIN32_H
+
+#include <winsock2.h>
+#include <io.h>
+
+/* Some Visual C++ overrides having no problems with MinGW */
+#ifdef _MSC_VER
+#define S_IWUSR 0x0080
+/* Make sure we included errno before that */
+#include <errno.h>
+#endif
+
+#ifdef EINPROGRESS
+# undef EINPROGRESS
+#endif
+#ifdef ENOTCONN
+# undef ENOTCONN
+#endif
+#ifdef EINTR
+# undef EINTR
+#endif
+#ifdef ECONNRESET
+# undef ECONNRESET
+#endif
+#ifdef ETIMEDOUT
+# undef ETIMEDOUT
+#endif
+
+#define EINPROGRESS WSAEINPROGRESS
+#define ENOTCONN WSAENOTCONN
+#define EINTR WSAEINTR
+#define ECONNRESET WSAECONNRESET
+#define ETIMEDOUT WSAETIMEDOUT
+
+#define WNOHANG WHOHANG
+#define SHUT_RDWR 2
+
+/* Defined in gg.c custom error reporting function */
+#ifdef GG_CONFIG_MIRANDA
+char *ws_strerror(int code);
+#define strerror(x) ws_strerror(x)
+#endif
+
+#define fork() (-1)
+int sockpipe(SOCKET filedes[2]);
+#define pipe(filedes) sockpipe(filedes)
+#define wait(x) (-1)
+#define waitpid(x,y,z) (-1)
+#define ioctl(fd,request,val) ioctlsocket(fd,request,(unsigned long *)val)
+
+#endif /* __GG_WIN32_H */
diff --git a/protocols/Gadu-Gadu/links.c b/protocols/Gadu-Gadu/links.c
new file mode 100644
index 0000000000..6141358475
--- /dev/null
+++ b/protocols/Gadu-Gadu/links.c
@@ -0,0 +1,173 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2009-2012 Bartosz Białek
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+#include "m_assocmgr.h"
+
+//////////////////////////////////////////////////////////
+// File Association Manager support
+
+#define GGS_PARSELINK "%s/ParseLink"
+#define GGS_MENUCHOOSE "%s/MenuChoose"
+
+static HANDLE hInstanceMenu;
+static HANDLE hServiceMenuChoose;
+static HANDLE hServiceParseLink;
+
+static INT_PTR gg_menuchoose(WPARAM wParam, LPARAM lParam)
+{
+ if (lParam)
+ *(void**)lParam = (void*)wParam;
+ return 0;
+}
+
+static INT_PTR gg_parselink(WPARAM wParam, LPARAM lParam)
+{
+ char *arg = (char*)lParam;
+ list_t l = g_Instances;
+ GGPROTO *gg = NULL;
+ uin_t uin;
+ CLISTMENUITEM mi = {0};
+ int items = 0;
+
+ if (list_count(l) == 0)
+ return 0;
+
+ if (arg == NULL)
+ return 1;
+
+ arg = strchr(arg, ':');
+
+ if (arg == NULL)
+ return 1;
+
+ for (++arg; *arg == '/'; ++arg);
+ uin = atoi(arg);
+
+ if (!uin)
+ return 1;
+
+ for (mi.cbSize = sizeof(mi); l; l = l->next)
+ {
+ GGPROTO *gginst = l->data;
+
+ mi.flags = CMIM_FLAGS;
+ if (gginst->proto.m_iStatus > ID_STATUS_OFFLINE)
+ {
+ ++items;
+ gg = l->data;
+ mi.flags |= CMIM_ICON;
+ mi.hIcon = LoadSkinnedProtoIcon(GG_PROTO, gg->proto.m_iStatus);
+ }
+ else
+ {
+ mi.flags |= CMIF_HIDDEN;
+ mi.hIcon = NULL;
+ }
+
+ CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)gginst->hInstanceMenuItem, (LPARAM)&mi);
+ if (mi.hIcon)
+ CallService(MS_SKIN2_RELEASEICON, (WPARAM)mi.hIcon, 0);
+ }
+
+ if (items > 1)
+ {
+ ListParam param = {0};
+ HMENU hMenu = CreatePopupMenu();
+ POINT pt;
+ int cmd = 0;
+
+ param.MenuObjectHandle = hInstanceMenu;
+ CallService(MO_BUILDMENU, (WPARAM)hMenu, (LPARAM)&param);
+
+ GetCursorPos(&pt);
+ cmd = TrackPopupMenu(hMenu, TPM_RETURNCMD, pt.x, pt.y, 0, pcli->hwndContactList, NULL);
+ DestroyMenu(hMenu);
+
+ if (cmd)
+ CallService(MO_PROCESSCOMMANDBYMENUIDENT, cmd, (LPARAM)&gg);
+ }
+
+ if (gg == NULL)
+ return 0;
+
+ if (ServiceExists(MS_MSG_SENDMESSAGE))
+ {
+ HANDLE hContact = gg_getcontact(gg, uin, 1, 0, NULL);
+ if (hContact != NULL)
+ CallService(MS_MSG_SENDMESSAGE, (WPARAM)hContact, 0);
+ }
+
+ return 0;
+}
+
+void gg_links_instancemenu_init()
+{
+ char service[MAXMODULELABELLENGTH];
+ TMenuParam mnu = {0};
+ TMO_MenuItem tmi = {0};
+
+ mir_snprintf(service, sizeof(service), GGS_MENUCHOOSE, GGDEF_PROTO);
+ hServiceMenuChoose = CreateServiceFunction(service, gg_menuchoose);
+ mnu.cbSize = sizeof(mnu);
+ mnu.name = "GGAccountChooser";
+ mnu.ExecService = service;
+ hInstanceMenu = (HANDLE)CallService(MO_CREATENEWMENUOBJECT, 0, (LPARAM)&mnu);
+
+ tmi.cbSize = sizeof(tmi);
+ tmi.flags = CMIF_ICONFROMICOLIB;
+ tmi.pszName = "Cancel";
+ tmi.position = 9999999;
+ tmi.hIcolibItem = LoadSkinnedIconHandle(SKINICON_OTHER_DELETE);
+ CallService(MO_ADDNEWMENUITEM, (WPARAM)hInstanceMenu, (LPARAM)&tmi);
+}
+
+void gg_links_init()
+{
+ if (ServiceExists(MS_ASSOCMGR_ADDNEWURLTYPE))
+ {
+ char service[MAXMODULELABELLENGTH];
+
+ mir_snprintf(service, sizeof(service), GGS_PARSELINK, GGDEF_PROTO);
+ hServiceParseLink = CreateServiceFunction(service, gg_parselink);
+ AssocMgr_AddNewUrlType("gg:", Translate("Gadu-Gadu Link Protocol"), hInstance, IDI_GG, service, 0);
+ }
+}
+
+void gg_links_destroy()
+{
+ DestroyServiceFunction(hServiceMenuChoose);
+ if (ServiceExists(MS_ASSOCMGR_ADDNEWURLTYPE))
+ DestroyServiceFunction(hServiceParseLink);
+}
+
+void gg_links_instance_init(GGPROTO *gg)
+{
+ if (ServiceExists(MS_ASSOCMGR_ADDNEWURLTYPE))
+ {
+ TMO_MenuItem tmi = {0};
+ tmi.cbSize = sizeof(tmi);
+ tmi.flags = CMIF_TCHAR;
+ tmi.ownerdata = gg;
+ tmi.position = list_count(g_Instances);
+ tmi.ptszName = GG_PROTONAME;
+ gg->hInstanceMenuItem = (HANDLE)CallService(MO_ADDNEWMENUITEM, (WPARAM)hInstanceMenu, (LPARAM)&tmi);
+ }
+}
diff --git a/protocols/Gadu-Gadu/m_assocmgr.h b/protocols/Gadu-Gadu/m_assocmgr.h
new file mode 100644
index 0000000000..4e15168643
--- /dev/null
+++ b/protocols/Gadu-Gadu/m_assocmgr.h
@@ -0,0 +1,301 @@
+/*
+
+'File Association Manager'-Plugin for
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright (C) 2005-2007 H. Herkenrath
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program (AssocMgr-License.txt); if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_ASSOCMGR_H__
+#define M_ASSOCMGR_H__
+
+#if defined (_MSC_VER) && (_MSC_VER >= 1020)
+ #pragma once
+#endif
+
+#if !defined(_TCHAR_DEFINED)
+ #include <tchar.h>
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning(push) /* save warning settings */
+#pragma warning(disable:4201) /* nonstandard extension used : nameless struct/union */
+#endif
+
+/*
+ File Association Manager v0.1.1.0
+*/
+
+/* interface id */
+#if !defined(MIID_ASSOCMGR)
+ #define MIID_ASSOCMGR {0xa05b56c0,0xcf7b,0x4389,{0xa1,0xe9,0xf1,0x3d,0xb9,0x36,0xe,0xf1}}
+#endif
+#if !defined(MIID_AUTORUN)
+ #define MIID_AUTORUN {0xeb0465e2,0xceee,0x11db,{0x83,0xef,0xc1,0xbf,0x55,0xd8,0x95,0x93}}
+#endif
+
+/* Add a new file type v0.1.0.0+
+Add a new file type to be registered with Windows.
+You probably want to call this event when
+ME_SYSTEM_MODULESLOADED is fired.
+ wParam=0
+ lParam=(LPARAM)(FILETYPEDESC*)ftd
+Returns 0 on success, nonzero otherwise.
+*/
+#define MS_ASSOCMGR_ADDNEWFILETYPE "AssocMgr/AddNewFileType"
+
+typedef struct {
+ int cbSize; // set to sizeof(FILETYPEDESC), in bytes
+ union {
+ const char *pszDescription; // description for options dialog and in registry.
+ const TCHAR *ptszDescription; // please Translate(), use singular form here.
+ const WCHAR *pwszDescription;
+ };
+ HINSTANCE hInstance; // instance of the calling module and where the icon
+ // resource is located.
+ // always be sure you set this to your own hInstance even if
+ // you use the generic default icon
+
+ UINT nIconResID; // resource id of an icon to use for the file type.
+ // this icon should contain icons of all sizes and color depths
+ // needed by Windows.
+ // set this to 0 to use the generic 'miranda file' icon
+ // provided by assocmgr.
+
+ const char *pszService; // service to call when a file is opened
+ // this service will be called with lParam set to
+ // the file name being opened including path.
+ // it can be assumed that the provided file name
+ // is always the long path name.
+ // return zero on suceess, nonzero on error.
+ // Note: set this to NULL to pass the file name as
+ // commandline argument to miranda32.exe (db file).
+
+ DWORD flags; // see FTDF_* flags below
+
+ const char *pszFileExt; // file extension, e.g. ".ext"
+ // first character must be a dot, assumed to be all lower case.
+ // may only consist of ascii characters.
+
+ const char *pszMimeType; // MIME type of the file, e.g. "application/x-icq"
+ // may only consist of ascii characters.
+ union {
+ const char *pszVerbDesc; // description for the open verb e.g. "&Install".
+ const TCHAR *ptszVerbDesc; // set this to NULL to use the default description.
+ const WCHAR *pwszVerbDesc; // include an ampersand (&) character for a mnemonic key.
+ }; // please Translate().
+} FILETYPEDESC;
+
+#define FTDF_UNICODE 0x0001 // pszDescription and pszVerbDesc in struct are Unicode.
+ // the specified service is called with Unicode parameters.
+
+#define FTDF_DEFAULTDISABLED 0x0002 // file type is not registered by default, it needs to be
+ // enabled explicitly on the options page.
+
+#define FTDF_BROWSERAUTOOPEN 0x0004 // tells the browser to download and open the file directly
+ // without prompt (currently IE and Opera6+) - be careful!
+ // use only in conjunction with pszMimeType set.
+ // this tells Windows that open can be safely invoked for
+ // downloaded files.
+ // Note that this flag may create a security risk,
+ // because downloaded files could contain malicious content.
+ // you need to protect against such an exploit.
+
+#define FTDF_ISTEXT 0x0008 // tells Windows that this file can be opened
+ // as a text file using e.g Notepad.
+ // only has an effect on Windows XP and higher.
+
+#define FTDF_ISSHORTCUT 0x0010 // file type behaves as shortcut, this means a
+ // small overlay arrow is applied and the extension is never shown
+
+#if defined(_UNICODE)
+ #define FTDF_TCHAR FTDF_UNICODE // strings in struct are WCHAR*, service accepts WCHAR*
+#else
+ #define FTDF_TCHAR 0 // strings in struct are char*, service accepts char*
+#endif
+
+#if !defined(ASSOCMGR_NOHELPERFUNCTIONS)
+__inline static int AssocMgr_AddNewFileType(const char *ext,const char *mime,const char *desc,const char *verb,HINSTANCE hinst,UINT iconid,const char *service,DWORD flags)
+{
+ FILETYPEDESC ftd;
+ ftd.cbSize=sizeof(FILETYPEDESC);
+ ftd.pszFileExt=ext;
+ ftd.pszMimeType=mime;
+ ftd.pszDescription=desc;
+ ftd.pszVerbDesc=verb;
+ ftd.hInstance=hinst;
+ ftd.nIconResID=iconid;
+ ftd.pszService=service;
+ ftd.flags=flags&~FTDF_UNICODE;
+ return CallService(MS_ASSOCMGR_ADDNEWFILETYPE,0,(LPARAM)&ftd);
+}
+__inline static int AssocMgr_AddNewFileTypeW(const char *ext,const char *mime,const WCHAR *desc,const WCHAR *verb,HINSTANCE hinst,UINT iconid,const char *service,DWORD flags)
+{
+ FILETYPEDESC ftd;
+ ftd.cbSize=sizeof(FILETYPEDESC);
+ ftd.pszFileExt=ext;
+ ftd.pszMimeType=mime;
+ ftd.pwszDescription=desc;
+ ftd.pwszVerbDesc=verb;
+ ftd.hInstance=hinst;
+ ftd.nIconResID=iconid;
+ ftd.pszService=service;
+ ftd.flags=flags|FTDF_UNICODE;
+ return CallService(MS_ASSOCMGR_ADDNEWFILETYPE,0,(LPARAM)&ftd);
+}
+#if defined(_UNICODE)
+ #define AssocMgr_AddNewFileTypeT AssocMgr_AddNewFileTypeW
+#else
+ #define AssocMgr_AddNewFileTypeT AssocMgr_AddNewFileType
+#endif
+#endif
+
+/* Remove a file type v0.1.0.0+
+Remove a file type registered previously using
+MS_ASSOCMGR_ADDNEWFILETYPE.
+This removes all settings in database and in registry
+associated with the file type.
+ wParam=0
+ lParam=(WPARAM)(char*)pszFileExt
+Returns 0 on success, nonzero otherwise.
+*/
+#define MS_ASSOCMGR_REMOVEFILETYPE "AssocMgr/RemoveFileType"
+
+/* Add a new url protocol type v0.1.0.0+
+Add a new url type to be registered with Windows.
+You probably want to call this event when
+ME_SYSTEM_MODULESLOADED is fired.
+ wParam=0
+ lParam=(LPARAM)(URLTYPEDESC*)utd
+Returns 0 on success, nonzero otherwise.
+*/
+#define MS_ASSOCMGR_ADDNEWURLTYPE "AssocMgr/AddNewUrlType"
+
+typedef struct {
+ int cbSize; // set to sizeof(URLTYPEDESC), in bytes
+ union {
+ const char *pszDescription; // description for options dialog and in registry.
+ const TCHAR *ptszDescription; // please Translate(), use singular form here.
+ const WCHAR *pwszDescription;
+ };
+ HINSTANCE hInstance; // instance of the calling module and where the icon
+ // resource is located.
+ // always be sure you set this to your own hInstance even if
+ // you use the generic default icon
+
+ UINT nIconResID; // resource id of an icon to use for the url type.
+ // only a small one (16x16) is needed by Windows,
+ // e.g. proto icon as used in Miranda.
+ // set this to 0 to use the default miranda icon.
+
+ const char *pszService; // service to call when a url is opened (can't be NULL)
+ // this service will be called with lParam set to
+ // the url being opened including the prefix.
+ // return zero on suceess, nonzero on error.
+
+ DWORD flags; // see UTDF_* flags below
+
+ const char *pszProtoPrefix; // protocol prefix, e.g. "http:"
+ // last character must be a colon, assumed to be all lower case.
+ // may only consist of ascii characters.
+} URLTYPEDESC;
+
+#define UTDF_UNICODE 0x0001 // pszDescription in struct is Unicode.
+ // the specified service is called with Unicode parameters.
+
+#define UTDF_DEFAULTDISABLED 0x0002 // url type is not registered by default, it needs to be
+ // enabled explicitly on the options page.
+#if defined(_UNICODE)
+ #define UTDF_TCHAR UTDF_UNICODE // strings in struct are WCHAR*, service accepts WCHAR*
+#else
+ #define UTDF_TCHAR 0 // strings in struct are char*, service accepts char*
+#endif
+
+#if !defined(ASSOCMGR_NOHELPERFUNCTIONS)
+static int __inline AssocMgr_AddNewUrlType(const char *prefix,const char *desc,HINSTANCE hinst,UINT iconid,const char *service,DWORD flags)
+{
+ URLTYPEDESC utd;
+ utd.cbSize=sizeof(URLTYPEDESC);
+ utd.pszProtoPrefix=prefix;
+ utd.pszDescription=desc;
+ utd.hInstance=hinst;
+ utd.nIconResID=iconid;
+ utd.pszService=service;
+ utd.flags=flags&~UTDF_UNICODE;
+ return CallService(MS_ASSOCMGR_ADDNEWURLTYPE,0,(LPARAM)&utd);
+}
+static int __inline AssocMgr_AddNewUrlTypeW(const char *prefix,const WCHAR *desc,HINSTANCE hinst,UINT iconid,const char *service,DWORD flags)
+{
+ URLTYPEDESC utd;
+ utd.cbSize=sizeof(URLTYPEDESC);
+ utd.pszProtoPrefix=prefix;
+ utd.pwszDescription=desc;
+ utd.hInstance=hinst;
+ utd.nIconResID=iconid;
+ utd.pszService=service;
+ utd.flags=flags|UTDF_UNICODE;
+ return CallService(MS_ASSOCMGR_ADDNEWURLTYPE,0,(LPARAM)&utd);
+}
+#if defined(_UNICODE)
+ #define AssocMgr_AddNewUrlTypeT AssocMgr_AddNewUrlTypeW
+#else
+ #define AssocMgr_AddNewUrlTypeT AssocMgr_AddNewUrlType
+#endif
+#endif
+
+/* Remove an url protocol type v0.1.0.0+
+Remove an url registered previously using
+MS_ASSOCMGR_ADDNEWURLTYPE.
+This removes all settings in database and in registry
+associated with the url type.
+ wParam=0
+ lParam=(WPARAM)(char*)pszProtoPrefix
+Returns 0 on success, nonzero otherwise.
+*/
+#define MS_ASSOCMGR_REMOVEURLTYPE "AssocMgr/RemoveUrlType"
+
+/* utility which should be moved as service into m_netlib.h (MS_NETLIB_URLENCODE already exists) */
+#if defined(MoveMemory) && defined(lstrlen)
+static __inline char *Netlib_UrlDecode(char *str)
+{
+ char *psz=str;
+ for(;*psz;++psz)
+ switch(*psz) {
+ case '+':
+ *psz=' ';
+ break;
+ case '%':
+ if(!psz[1] || !psz[2]) break;
+ MoveMemory(psz,&psz[1],2);
+ psz[2]=0;
+ *psz=(char)strtol(psz,NULL,16);
+ MoveMemory(&psz[1],&psz[3],lstrlenA(&psz[3])+1);
+ break;
+ }
+ return str;
+}
+#endif
+
+#if defined(_MSC_VER)
+#pragma warning(pop) /* restore warning settings */
+#endif
+
+#ifndef ASSOCMGR_NOSETTINGS
+#define SETTING_ONLYWHILERUNNING_DEFAULT 0
+#endif
+
+#endif // M_ASSOCMGR_H \ No newline at end of file
diff --git a/protocols/Gadu-Gadu/m_folders.h b/protocols/Gadu-Gadu/m_folders.h
new file mode 100644
index 0000000000..5971cff81a
--- /dev/null
+++ b/protocols/Gadu-Gadu/m_folders.h
@@ -0,0 +1,284 @@
+/*
+Custom profile folders plugin for Miranda IM
+
+Copyright © 2005 Cristian Libotean
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_CUSTOM_FOLDERS_H
+#define M_CUSTOM_FOLDERS_H
+
+#define FOLDERS_API 501 //dunno why it's here but it is :)
+
+#define PROFILE_PATH "%profile_path%"
+#define CURRENT_PROFILE "%current_profile%"
+#define MIRANDA_PATH "%miranda_path%"
+#define PLUGINS_PATH "%miranda_path%" "\\plugins"
+#define MIRANDA_USERDATA "%miranda_userdata%"
+
+#define TO_WIDE(x) L ## x
+
+#define PROFILE_PATHW L"%profile_path%"
+#define CURRENT_PROFILEW L"%current_profile%"
+#define MIRANDA_PATHW L"%miranda_path%"
+#define MIRANDA_USERDATAW L"%miranda_userdata%"
+
+#define FOLDER_AVATARS PROFILE_PATH "\\" CURRENT_PROFILE "\\avatars"
+#define FOLDER_VCARDS PROFILE_PATH "\\" CURRENT_PROFILE "\\vcards"
+#define FOLDER_LOGS PROFILE_PATH "\\" CURRENT_PROFILE "\\logs"
+#define FOLDER_RECEIVED_FILES PROFILE_PATH "\\" CURRENT_PROFILE "\\received files"
+#define FOLDER_DOCS MIRANDA_PATH "\\" "docs"
+
+#define FOLDER_CONFIG PLUGINS_PATH "\\" "config"
+
+#define FOLDER_SCRIPTS MIRANDA_PATH "\\" "scripts"
+
+#define FOLDER_UPDATES MIRANDA_PATH "\\" "updates"
+
+#define FOLDER_CUSTOMIZE MIRANDA_PATH "\\" "customize"
+#define FOLDER_CUSTOMIZE_SOUNDS FOLDER_CUSTOMIZE "\\sounds"
+#define FOLDER_CUSTOMIZE_ICONS FOLDER_CUSTOMIZE "\\icons"
+#define FOLDER_CUSTOMIZE_SMILEYS FOLDER_CUSTOMIZE "\\smileys"
+#define FOLDER_CUSTOMIZE_SKINS FOLDER_CUSTOMIZE "\\skins"
+#define FOLDER_CUSTOMIZE_THEMES FOLDER_CUSTOMIZE "\\themes"
+
+
+#define FOLDERS_NAME_MAX_SIZE 64 //maximum name and section size
+
+#define FF_UNICODE 0x00000001
+
+#if defined (UNICODE)
+ #define FF_TCHAR FF_UNICODE
+#else
+ #define FF_TCHAR 0
+#endif
+
+typedef struct{
+ int cbSize; //size of struct
+ char szSection[FOLDERS_NAME_MAX_SIZE]; //section name, if it doesn't exist it will be created otherwise it will just add this entry to it
+ char szName[FOLDERS_NAME_MAX_SIZE]; //entry name - will be shown in options
+ union{
+ const char *szFormat; //default string format. Fallback string in case there's no entry in the database for this folder. This should be the initial value for the path, users will be able to change it later.
+ const wchar_t *szFormatW; //String is dup()'d so you can free it later. If you set the unicode string don't forget to set the flag accordingly.
+ const TCHAR *szFormatT;
+ };
+ DWORD flags; //FF_* flags
+} FOLDERSDATA;
+
+/*Folders/Register/Path service
+ wParam - not used, must be 0
+ lParam - (LPARAM) (const FOLDERDATA *) - Data structure filled with
+ the necessary information.
+ Returns a handle to the registered path or 0 on error.
+ You need to use this to call the other services.
+*/
+#define MS_FOLDERS_REGISTER_PATH "Folders/Register/Path"
+
+/*Folders/Get/PathSize service
+ wParam - (WPARAM) (int) - handle to registered path
+ lParam - (LPARAM) (int *) - pointer to the variable that receives the size of the path
+ string (not including the null character). Depending on the flags set when creating the path
+ it will either call strlen() or wcslen() to get the length of the string.
+ Returns the size of the buffer.
+*/
+#define MS_FOLDERS_GET_SIZE "Folders/Get/PathSize"
+
+typedef struct{
+ int cbSize;
+ int nMaxPathSize; //maximum size of buffer. This represents the number of characters that can be copied to it (so for unicode strings you don't send the number of bytes but the length of the string).
+ union{
+ char *szPath; //pointer to the buffer that receives the path without the last "\\"
+ wchar_t *szPathW; //unicode version of the buffer.
+ TCHAR *szPathT;
+ };
+} FOLDERSGETDATA;
+
+/*Folders/Get/Path service
+ wParam - (WPARAM) (int) - handle to registered path
+ lParam - (LPARAM) (FOLDERSGETDATA *) pointer to a FOLDERSGETDATA that has all the relevant fields filled.
+ Should return 0 on success, or nonzero otherwise.
+*/
+#define MS_FOLDERS_GET_PATH "Folders/Get/Path"
+
+typedef struct{
+ int cbSize;
+ union{
+ char **szPath; //address of a string variable (char *) or (wchar_t*) where the path should be stored (the last \ won't be copied).
+ wchar_t **szPathW; //unicode version of string.
+ TCHAR **szPathT;
+ };
+} FOLDERSGETALLOCDATA;
+
+/*Folders/GetRelativePath/Alloc service
+ wParam - (WPARAM) (int) - Handle to registered path
+ lParam - (LPARAM) (FOLDERSALLOCDATA *) data
+ This service is the same as MS_FOLDERS_GET_PATH with the difference that this service
+ allocates the needed space for the buffer. It uses miranda's memory functions for that and you need
+ to use those to free the resulting buffer.
+ Should return 0 on success, or nonzero otherwise. Currently it only returns 0.
+*/
+#define MS_FOLDERS_GET_PATH_ALLOC "Folders/Get/Path/Alloc"
+
+
+/*Folders/On/Path/Changed
+ wParam - (WPARAM) 0
+ lParam - (LPARAM) 0
+ Triggered when the folders change, you should reget the paths you registered.
+*/
+#define ME_FOLDERS_PATH_CHANGED "Folders/On/Path/Changed"
+
+#ifndef FOLDERS_NO_HELPER_FUNCTIONS
+
+#ifndef M_UTILS_H__
+#error The helper functions require that m_utils.h be included in the project. Please include that file if you want to use the helper functions. If you don''t want to use the functions just define FOLDERS_NO_HELPER_FUNCTIONS.
+#endif
+//#include "../../../include/newpluginapi.h"
+
+__inline static HANDLE FoldersRegisterCustomPath(const char *section, const char *name, const char *defaultPath)
+{
+ FOLDERSDATA fd = {0};
+ if (!ServiceExists(MS_FOLDERS_REGISTER_PATH)) return 0;
+ fd.cbSize = sizeof(FOLDERSDATA);
+ strncpy(fd.szSection, section, FOLDERS_NAME_MAX_SIZE);
+ fd.szSection[FOLDERS_NAME_MAX_SIZE - 1] = '\0';
+ strncpy(fd.szName, name, FOLDERS_NAME_MAX_SIZE);
+ fd.szName[FOLDERS_NAME_MAX_SIZE - 1] = '\0';
+ fd.szFormat = defaultPath;
+ return (HANDLE) CallService(MS_FOLDERS_REGISTER_PATH, 0, (LPARAM) &fd);
+}
+
+__inline static HANDLE FoldersRegisterCustomPathW(const char *section, const char *name, const wchar_t *defaultPathW)
+{
+ FOLDERSDATA fd = {0};
+ if (!ServiceExists(MS_FOLDERS_REGISTER_PATH)) return 0;
+ fd.cbSize = sizeof(FOLDERSDATA);
+ strncpy(fd.szSection, section, FOLDERS_NAME_MAX_SIZE);
+ fd.szSection[FOLDERS_NAME_MAX_SIZE - 1] = '\0'; //make sure it's NULL terminated
+ strncpy(fd.szName, name, FOLDERS_NAME_MAX_SIZE);
+ fd.szName[FOLDERS_NAME_MAX_SIZE - 1] = '\0'; //make sure it's NULL terminated
+ fd.szFormatW = defaultPathW;
+ fd.flags = FF_UNICODE;
+ return (HANDLE) CallService(MS_FOLDERS_REGISTER_PATH, 0, (LPARAM) &fd);
+}
+
+__inline static INT_PTR FoldersGetCustomPath(HANDLE hFolderEntry, char *path, const int size, const char *notFound)
+{
+ FOLDERSGETDATA fgd = {0};
+ INT_PTR res;
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = size;
+ fgd.szPath = path;
+ res = CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolderEntry, (LPARAM) &fgd);
+ if (res)
+ {
+ char buffer[MAX_PATH];
+ CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM) notFound, (LPARAM) buffer);
+ mir_snprintf(path, size, "%s", buffer);
+ }
+
+ return res;
+}
+
+__inline static INT_PTR FoldersGetCustomPathW(HANDLE hFolderEntry, wchar_t *pathW, const int count, const wchar_t *notFoundW)
+{
+ FOLDERSGETDATA fgd = {0};
+ INT_PTR res;
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = count;
+ fgd.szPathW = pathW;
+ res = CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolderEntry, (LPARAM) &fgd);
+ if (res)
+ {
+ wcsncpy(pathW, notFoundW, count);
+ pathW[count - 1] = '\0';
+ }
+
+ return res;
+}
+
+__inline static INT_PTR FoldersGetCustomPathEx(HANDLE hFolderEntry, char *path, const int size, char *notFound, char *fileName)
+{
+ FOLDERSGETDATA fgd = {0};
+ INT_PTR res;
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = size;
+ fgd.szPath = path;
+ res = CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolderEntry, (LPARAM) &fgd);
+ if (res)
+ {
+ char buffer[MAX_PATH];
+ CallService(MS_UTILS_PATHTOABSOLUTE, (WPARAM) notFound, (LPARAM) buffer);
+ mir_snprintf(path, size, "%s", buffer);
+ }
+ if (strlen(path) > 0)
+ {
+ strcat(path, "\\");
+ }
+ else{
+ path[0] = '\0';
+ }
+
+ if (fileName)
+ {
+ strcat(path, fileName);
+ }
+
+ return res;
+}
+
+__inline static INT_PTR FoldersGetCustomPathExW(HANDLE hFolderEntry, wchar_t *pathW, const int count, wchar_t *notFoundW, wchar_t *fileNameW)
+{
+ FOLDERSGETDATA fgd = {0};
+ INT_PTR res;
+ fgd.cbSize = sizeof(FOLDERSGETDATA);
+ fgd.nMaxPathSize = count;
+ fgd.szPathW = pathW;
+ res = CallService(MS_FOLDERS_GET_PATH, (WPARAM) hFolderEntry, (LPARAM) &fgd);
+ if (res)
+ {
+ wcsncpy(pathW, notFoundW, count);
+ pathW[count - 1] = '\0';
+ }
+
+ if (wcslen(pathW) > 0)
+ {
+ wcscat(pathW, L"\\");
+ }
+ else{
+ pathW[0] = L'\0';
+ }
+
+ if (fileNameW)
+ {
+ wcscat(pathW, fileNameW);
+ }
+
+ return res;
+}
+
+# ifdef _UNICODE
+# define FoldersGetCustomPathT FoldersGetCustomPathW
+# define FoldersGetCustomPathExT FoldersGetCustomPathExW
+# define FoldersRegisterCustomPathT FoldersRegisterCustomPathW
+#else
+# define FoldersGetCustomPathT FoldersGetCustomPath
+# define FoldersGetCustomPathExT FoldersGetCustomPath
+# define FoldersRegisterCustomPathT FoldersRegisterCustomPath
+#endif
+
+#endif
+
+#endif //M_CUSTOM_FOLDERS_H \ No newline at end of file
diff --git a/protocols/Gadu-Gadu/m_metacontacts.h b/protocols/Gadu-Gadu/m_metacontacts.h
new file mode 100644
index 0000000000..9f348bd2c6
--- /dev/null
+++ b/protocols/Gadu-Gadu/m_metacontacts.h
@@ -0,0 +1,166 @@
+/*
+
+Miranda IM: the free IM client for Microsoft* Windows*
+
+Copyright © 2004 Universite Louis PASTEUR, STRASBOURG.
+Copyright © 2004 Scott Ellis (www.scottellis.com.au mail@scottellis.com.au)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+*/
+
+#ifndef M_METACONTACTS_H__
+#define M_METACONTACTS_H__ 1
+
+#ifndef MIID_METACONTACTS
+#define MIID_METACONTACTS {0xc0325019, 0xc1a7, 0x40f5, { 0x83, 0x65, 0x4f, 0x46, 0xbe, 0x21, 0x86, 0x3e}}
+#endif
+
+//get the handle for a contact's parent metacontact
+//wParam=(HANDLE)hSubContact
+//lParam=0
+//returns a handle to the parent metacontact, or null if this contact is not a subcontact
+#define MS_MC_GETMETACONTACT "MetaContacts/GetMeta"
+
+//gets the handle for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the default contact, or null on failure
+#define MS_MC_GETDEFAULTCONTACT "MetaContacts/GetDefault"
+
+//gets the contact number for the default contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD contact number, or -1 on failure
+#define MS_MC_GETDEFAULTCONTACTNUM "MetaContacts/GetDefaultNum"
+
+//gets the handle for the 'most online' contact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a handle to the 'most online' contact
+#define MS_MC_GETMOSTONLINECONTACT "MetaContacts/GetMostOnline"
+
+//gets the number of subcontacts for a metacontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns a DWORD representing the number of subcontacts for the given metacontact
+#define MS_MC_GETNUMCONTACTS "MetaContacts/GetNumContacts"
+
+//gets the handle of a subcontact, using the subcontact's number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns a handle to the specified subcontact
+#define MS_MC_GETSUBCONTACT "MetaContacts/GetSubContact"
+
+//sets the default contact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACTNUM "MetaContacts/SetDefault"
+
+//sets the default contact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success
+#define MS_MC_SETDEFAULTCONTACT "MetaContacts/SetDefaultByHandle"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's contact number
+//wParam=(HANDLE)hMetaContact
+//lParam=(DWORD)contact number
+//returns 0 on success
+#define MS_MC_FORCESENDCONTACTNUM "MetaContacts/ForceSendContact"
+
+//forces the metacontact to send using a specific subcontact, using the subcontact's handle
+//wParam=(HANDLE)hMetaContact
+//lParam=(HANDLE)hSubcontact
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_FORCESENDCONTACT "MetaContacts/ForceSendContactByHandle"
+
+//'unforces' the metacontact to send using a specific subcontact
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 0 on success (will fail if 'force default' is in effect)
+#define MS_MC_UNFORCESENDCONTACT "MetaContacts/UnforceSendContact"
+
+//'forces' or 'unforces' (i.e. toggles) the metacontact to send using it's default contact
+// overrides (and clears) 'force send' above, and will even force use of offline contacts
+// will send ME_MC_FORCESEND or ME_MC_UNFORCESEND event
+//wParam=(HANDLE)hMetaContact
+//lParam=0
+//returns 1(true) or 0(false) representing new state of 'force default'
+#define MS_MC_FORCEDEFAULT "MetaContacts/ForceSendDefault"
+
+// method to get state of 'force' for a metacontact
+// wParam=(HANDLE)hMetaContact
+// lParam= (DWORD)&contact_number or NULL
+//
+// if lparam supplied, the contact_number of the contatct 'in force' will be copied to the address it points to,
+// or if none is in force, the value (DWORD)-1 will be copied
+// (v0.8.0.8+ returns 1 if 'force default' is true with *lParam == default contact number, else returns 0 with *lParam as above)
+#define MS_MC_GETFORCESTATE "MetaContacts/GetForceState"
+
+// fired when a metacontact's default contact changes (fired upon creation of metacontact also, when default is initially set)
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hDefaultContact
+#define ME_MC_DEFAULTTCHANGED "MetaContacts/DefaultChanged"
+
+// fired when a metacontact's subcontacts change (fired upon creation of metacontact, when contacts are added or removed, and when
+// contacts are reordered) - a signal to re-read metacontact data
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_SUBCONTACTSCHANGED "MetaContacts/SubcontactsChanged"
+
+// fired when a metacontact is forced to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=(HANDLE)hForceContact
+#define ME_MC_FORCESEND "MetaContacts/ForceSend"
+
+// fired when a metacontact is 'unforced' to send using a specific subcontact
+// wParam=(HANDLE)hMetaContact
+// lParam=0
+#define ME_MC_UNFORCESEND "MetaContacts/UnforceSend"
+
+// method to get protocol name - used to be sure you're dealing with a "real" metacontacts plugin :)
+// wParam=lParam=0
+#define MS_MC_GETPROTOCOLNAME "MetaContacts/GetProtoName"
+
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=0
+// convert a given contact into a metacontact
+#define MS_MC_CONVERTTOMETA "MetaContacts/ConvertToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=(HANDLE)hContact
+// lParam=(HANDLE)hMeta
+// add an existing contact to a metacontact
+#define MS_MC_ADDTOMETA "MetaContacts/AddToMetacontact"
+
+// added 0.9.5.0 (22/3/05)
+// wParam=0
+// lParam=(HANDLE)hContact
+// remove a contact from a metacontact
+#define MS_MC_REMOVEFROMMETA "MetaContacts/RemoveFromMetacontact"
+
+
+// added 0.9.13.2 (6/10/05)
+// wParam=(BOOL)disable
+// lParam=0
+// enable/disable the 'hidden group hack' - for clists that support subcontact hiding using 'IsSubcontact' setting
+// should be called once in the clist 'onmodulesloaded' event handler (which, since it's loaded after the db, will be called
+// before the metacontact onmodulesloaded handler where the subcontact hiding is usually done)
+#define MS_MC_DISABLEHIDDENGROUP "MetaContacts/DisableHiddenGroup"
+
+#endif
diff --git a/protocols/Gadu-Gadu/oauth.c b/protocols/Gadu-Gadu/oauth.c
new file mode 100644
index 0000000000..2e496de677
--- /dev/null
+++ b/protocols/Gadu-Gadu/oauth.c
@@ -0,0 +1,586 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2010 Bartosz Białek
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+#include <io.h>
+#include <fcntl.h>
+#include "protocol.h"
+
+//////////////////////////////////////////////////////////
+// OAuth 1.0 implementation
+
+// Service Provider must accept the HTTP Authorization header
+
+// RSA-SHA1 signature method (see RFC 3447 section 8.2
+// and RSASSA-PKCS1-v1_5 algorithm) is unimplemented
+
+typedef struct
+{
+ char *name;
+ char *value;
+} OAUTHPARAMETER;
+
+typedef enum
+{
+ HMACSHA1,
+ RSASHA1,
+ PLAINTEXT
+} OAUTHSIGNMETHOD;
+
+static int paramsortFunc(OAUTHPARAMETER *p1, OAUTHPARAMETER *p2)
+{
+ int res = strcmp(p1->name, p2->name);
+ return res != 0 ? res : strcmp(p1->value, p2->value);
+}
+
+// HMAC-SHA1 (see RFC 2104 for details)
+void hmacsha1_hash(mir_sha1_byte_t *text, int text_len, mir_sha1_byte_t *key, int key_len,
+ mir_sha1_byte_t digest[MIR_SHA1_HASH_SIZE])
+{
+ mir_sha1_ctx context;
+ mir_sha1_byte_t k_ipad[64];
+ mir_sha1_byte_t k_opad[64];
+ int i;
+
+ if (key_len > 64) {
+ mir_sha1_ctx tctx;
+ mir_sha1_byte_t tk[MIR_SHA1_HASH_SIZE];
+
+ mir_sha1_init(&tctx);
+ mir_sha1_append(&tctx, key, key_len);
+ mir_sha1_finish(&tctx, tk);
+
+ key = tk;
+ key_len = MIR_SHA1_HASH_SIZE;
+ }
+
+ memset(k_ipad, 0x36, 64);
+ memset(k_opad, 0x5c, 64);
+
+ for (i = 0; i < key_len; i++) {
+ k_ipad[i] ^= key[i];
+ k_opad[i] ^= key[i];
+ }
+
+ mir_sha1_init(&context);
+ mir_sha1_append(&context, k_ipad, 64);
+ mir_sha1_append(&context, text, text_len);
+ mir_sha1_finish(&context, digest);
+
+ mir_sha1_init(&context);
+ mir_sha1_append(&context, k_opad, 64);
+ mir_sha1_append(&context, digest, MIR_SHA1_HASH_SIZE);
+ mir_sha1_finish(&context, digest);
+}
+
+// see RFC 3986 for details
+#define isunreserved(c) ( isalnum((unsigned char)c) || c == '-' || c == '.' || c == '_' || c == '~')
+char *oauth_uri_escape(const char *str)
+{
+ char *res;
+ int size, ix = 0;
+
+ if (str == NULL) return mir_strdup("");
+
+ size = (int)strlen(str) + 1;
+ res = (char *)mir_alloc(size);
+
+ while (*str) {
+ if (!isunreserved(*str)) {
+ size += 2;
+ res = (char *)mir_realloc(res, size);
+ mir_snprintf(&res[ix], 4, "%%%X%X", (*str >> 4) & 15, *str & 15);
+ ix += 3;
+ }
+ else
+ res[ix++] = *str;
+ str++;
+ }
+ res[ix] = 0;
+
+ return res;
+}
+
+// generates Signature Base String
+char *oauth_generate_signature(SortedList *params, const char *httpmethod, const char *url)
+{
+ char *res, *urlenc, *urlnorm;
+ OAUTHPARAMETER *p;
+ int i, ix = 0, size;
+
+ if (httpmethod == NULL || url == NULL || !params->realCount) return mir_strdup("");
+
+ urlnorm = (char *)mir_alloc(strlen(url) + 1);
+ while (*url) {
+ if (*url == '?' || *url == '#') break; // see RFC 3986 section 3
+ urlnorm[ix++] = tolower(*url);
+ url++;
+ }
+ urlnorm[ix] = 0;
+ if ((res = strstr(urlnorm, ":80")) != NULL)
+ memmove(res, res + 3, strlen(res) - 2);
+ else if ((res = strstr(urlnorm, ":443")) != NULL)
+ memmove(res, res + 4, strlen(res) - 3);
+
+ urlenc = oauth_uri_escape(urlnorm);
+ mir_free(urlnorm);
+ size = (int)strlen(httpmethod) + (int)strlen(urlenc) + 1 + 2;
+
+ for (i = 0; i < params->realCount; i++) {
+ p = params->items[i];
+ if (!strcmp(p->name, "oauth_signature")) continue;
+ if (i > 0) size += 3;
+ size += (int)strlen(p->name) + (int)strlen(p->value) + 3;
+ }
+
+ res = (char *)mir_alloc(size);
+ strcpy(res, httpmethod);
+ strcat(res, "&");
+ strcat(res, urlenc);
+ mir_free(urlenc);
+ strcat(res, "&");
+
+ for (i = 0; i < params->realCount; i++) {
+ p = params->items[i];
+ if (!strcmp(p->name, "oauth_signature")) continue;
+ if (i > 0) strcat(res, "%26");
+ strcat(res, p->name);
+ strcat(res, "%3D");
+ strcat(res, p->value);
+ }
+
+ return res;
+}
+
+char *oauth_getparam(SortedList *params, const char *name)
+{
+ OAUTHPARAMETER *p;
+ int i;
+
+ if (name == NULL) return NULL;
+
+ for (i = 0; i < params->realCount; i++) {
+ p = params->items[i];
+ if (!strcmp(p->name, name))
+ return p->value;
+ }
+
+ return NULL;
+}
+
+void oauth_setparam(SortedList *params, const char *name, const char *value)
+{
+ OAUTHPARAMETER *p;
+ int i;
+
+ if (name == NULL) return;
+
+ for (i = 0; i < params->realCount; i++) {
+ p = params->items[i];
+ if (!strcmp(p->name, name)) {
+ mir_free(p->value);
+ p->value = oauth_uri_escape(value);
+ return;
+ }
+ }
+
+ p = mir_alloc(sizeof(OAUTHPARAMETER));
+ p->name = oauth_uri_escape(name);
+ p->value = oauth_uri_escape(value);
+ li.List_InsertPtr(params, p);
+}
+
+void oauth_freeparams(SortedList *params)
+{
+ OAUTHPARAMETER *p;
+ int i;
+
+ for (i = 0; i < params->realCount; i++) {
+ p = params->items[i];
+ mir_free(p->name);
+ mir_free(p->value);
+ }
+}
+
+int oauth_sign_request(SortedList *params, const char *httpmethod, const char *url,
+ const char *consumer_secret, const char *token_secret)
+{
+ char *sign = NULL, *signmethod;
+
+ if (!params->realCount) return -1;
+
+ signmethod = oauth_getparam(params, "oauth_signature_method");
+ if (signmethod == NULL) return -1;
+
+ if (!strcmp(signmethod, "HMAC-SHA1")) {
+ char *text = oauth_generate_signature(params, httpmethod, url);
+ char *key;
+ char *csenc = oauth_uri_escape(consumer_secret);
+ char *tsenc = oauth_uri_escape(token_secret);
+ mir_sha1_byte_t digest[MIR_SHA1_HASH_SIZE];
+ NETLIBBASE64 nlb64 = {0};
+ int signlen;
+
+ key = (char *)mir_alloc(strlen(csenc) + strlen(tsenc) + 2);
+ strcpy(key, csenc);
+ strcat(key, "&");
+ strcat(key, tsenc);
+ mir_free(csenc);
+ mir_free(tsenc);
+
+ hmacsha1_hash(text, (int)strlen(text), key, (int)strlen(key), digest);
+
+ signlen = Netlib_GetBase64EncodedBufferSize(MIR_SHA1_HASH_SIZE);
+ sign = (char *)mir_alloc(signlen);
+ nlb64.pszEncoded = sign;
+ nlb64.cchEncoded = signlen;
+ nlb64.pbDecoded = digest;
+ nlb64.cbDecoded = MIR_SHA1_HASH_SIZE;
+ CallService(MS_NETLIB_BASE64ENCODE, 0, (LPARAM)&nlb64);
+
+ mir_free(text);
+ mir_free(key);
+ }
+// else if (!strcmp(signmethod, "RSA-SHA1")) { // unimplemented
+// }
+ else { // PLAINTEXT
+ char *csenc = oauth_uri_escape(consumer_secret);
+ char *tsenc = oauth_uri_escape(token_secret);
+
+ sign = (char *)mir_alloc(strlen(csenc) + strlen(tsenc) + 2);
+ strcpy(sign, csenc);
+ strcat(sign, "&");
+ strcat(sign, tsenc);
+ mir_free(csenc);
+ mir_free(tsenc);
+ }
+
+ oauth_setparam(params, "oauth_signature", sign);
+ mir_free(sign);
+
+ return 0;
+}
+
+char *oauth_generate_nonce()
+{
+ mir_md5_byte_t digest[16];
+ char *result, *str, timestamp[22], randnum[16];
+ int i;
+
+ mir_snprintf(timestamp, sizeof(timestamp), "%ld", time(NULL));
+ CallService(MS_UTILS_GETRANDOM, (WPARAM)sizeof(randnum), (LPARAM)randnum);
+
+ str = (char *)mir_alloc(strlen(timestamp) + strlen(randnum) + 1);
+ strcpy(str, timestamp);
+ strcat(str, randnum);
+ mir_md5_hash(str, (int)strlen(str), digest);
+ mir_free(str);
+
+ result = (char *)mir_alloc(32 + 1);
+ for (i = 0; i < 16; i++)
+ sprintf(result + (i<<1), "%02x", digest[i]);
+
+ return result;
+}
+
+char *oauth_auth_header(const char *httpmethod, const char *url, OAUTHSIGNMETHOD signmethod,
+ const char *consumer_key, const char *consumer_secret,
+ const char *token, const char *token_secret)
+{
+ OAUTHPARAMETER *p;
+ int i, size;
+ char *res, timestamp[22], *nonce;
+ SortedList oauth_parameters = {0};
+
+ if (httpmethod == NULL || url == NULL) return NULL;
+
+ oauth_parameters.sortFunc = paramsortFunc;
+ oauth_parameters.increment = 1;
+
+ oauth_setparam(&oauth_parameters, "oauth_consumer_key", consumer_key);
+ oauth_setparam(&oauth_parameters, "oauth_version", "1.0");
+ switch (signmethod) {
+ case HMACSHA1: oauth_setparam(&oauth_parameters, "oauth_signature_method", "HMAC-SHA1"); break;
+ case RSASHA1: oauth_setparam(&oauth_parameters, "oauth_signature_method", "RSA-SHA1"); break;
+ default: oauth_setparam(&oauth_parameters, "oauth_signature_method", "PLAINTEXT"); break;
+ };
+ mir_snprintf(timestamp, sizeof(timestamp), "%ld", time(NULL));
+ oauth_setparam(&oauth_parameters, "oauth_timestamp", timestamp);
+ nonce = oauth_generate_nonce();
+ oauth_setparam(&oauth_parameters, "oauth_nonce", nonce);
+ mir_free(nonce);
+ if (token != NULL && *token)
+ oauth_setparam(&oauth_parameters, "oauth_token", token);
+
+ if (oauth_sign_request(&oauth_parameters, httpmethod, url, consumer_secret, token_secret)) {
+ oauth_freeparams(&oauth_parameters);
+ li.List_Destroy(&oauth_parameters);
+ return NULL;
+ }
+
+ size = 7;
+ for (i = 0; i < oauth_parameters.realCount; i++) {
+ p = oauth_parameters.items[i];
+ if (i > 0) size++;
+ size += (int)strlen(p->name) + (int)strlen(p->value) + 3;
+ }
+
+ res = (char *)mir_alloc(size);
+ strcpy(res, "OAuth ");
+
+ for (i = 0; i < oauth_parameters.realCount; i++) {
+ p = oauth_parameters.items[i];
+ if (i > 0) strcat(res, ",");
+ strcat(res, p->name);
+ strcat(res, "=\"");
+ strcat(res, p->value);
+ strcat(res, "\"");
+ }
+
+ oauth_freeparams(&oauth_parameters);
+ li.List_Destroy(&oauth_parameters);
+
+ return res;
+}
+
+char *gg_oauth_header(GGPROTO *gg, const char *httpmethod, const char *url)
+{
+ char *res, uin[32], *password = NULL, *token = NULL, *token_secret = NULL;
+ DBVARIANT dbv;
+
+ UIN2ID(DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0), uin);
+ if(!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) {
+ CallService(MS_DB_CRYPT_DECODESTRING, (WPARAM)(int)strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal);
+ password = mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ if(!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_TOKEN, &dbv)) {
+ token = mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ if(!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_TOKENSECRET, &dbv)) {
+ CallService(MS_DB_CRYPT_DECODESTRING, (WPARAM)(int)strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal);
+ token_secret = mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+
+ res = oauth_auth_header(httpmethod, url, HMACSHA1, uin, password, token, token_secret);
+ mir_free(password);
+ mir_free(token);
+ mir_free(token_secret);
+
+ return res;
+}
+
+int gg_oauth_receivetoken(GGPROTO *gg)
+{
+ NETLIBHTTPHEADER httpHeaders[3];
+ NETLIBHTTPREQUEST req = {0};
+ NETLIBHTTPREQUEST *resp;
+ char szUrl[256], uin[32], *password = NULL, *str, *token = NULL, *token_secret = NULL;
+ DBVARIANT dbv;
+ int res = 0;
+
+ UIN2ID(DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0), uin);
+ if(!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, &dbv)) {
+ CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal);
+ password = mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+
+ // 1. Obtaining an Unauthorized Request Token
+ gg_netlog(gg, "gg_oauth_receivetoken(): Obtaining an Unauthorized Request Token...");
+ strcpy(szUrl, "http://api.gadu-gadu.pl/request_token");
+ str = oauth_auth_header("POST", szUrl, HMACSHA1, uin, password, NULL, NULL);
+
+ req.cbSize = sizeof(req);
+ req.requestType = REQUEST_POST;
+ req.szUrl = szUrl;
+ req.flags = NLHRF_NODUMP | NLHRF_HTTP11;
+ req.headersCount = 3;
+ req.headers = httpHeaders;
+ httpHeaders[0].szName = "User-Agent";
+ httpHeaders[0].szValue = GG8_VERSION;
+ httpHeaders[1].szName = "Authorization";
+ httpHeaders[1].szValue = str;
+ httpHeaders[2].szName = "Accept";
+ httpHeaders[2].szValue = "*/*";
+
+ resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)gg->netlib, (LPARAM)&req);
+ if (resp) {
+ if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) {
+ HXML hXml;
+ TCHAR *xmlAction;
+ TCHAR *tag;
+
+ xmlAction = gg_a2t(resp->pData);
+ tag = gg_a2t("result");
+ hXml = xi.parseString(xmlAction, 0, tag);
+ if (hXml != NULL) {
+ HXML node;
+
+ mir_free(tag); tag = gg_a2t("oauth_token");
+ node = xi.getChildByPath(hXml, tag, 0);
+ token = node != NULL ? gg_t2a(xi.getText(node)) : NULL;
+
+ mir_free(tag); tag = gg_a2t("oauth_token_secret");
+ node = xi.getChildByPath(hXml, tag, 0);
+ token_secret = node != NULL ? gg_t2a(xi.getText(node)) : NULL;
+
+ xi.destroyNode(hXml);
+ }
+ mir_free(tag);
+ mir_free(xmlAction);
+ }
+ else gg_netlog(gg, "gg_oauth_receivetoken(): Invalid response code from HTTP request");
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp);
+ }
+ else gg_netlog(gg, "gg_oauth_receivetoken(): No response from HTTP request");
+
+ // 2. Obtaining User Authorization
+ gg_netlog(gg, "gg_oauth_receivetoken(): Obtaining User Authorization...");
+ mir_free(str);
+ str = oauth_uri_escape("http://www.mojageneracja.pl");
+
+ mir_snprintf(szUrl, 256, "callback_url=%s&request_token=%s&uin=%s&password=%s",
+ str, token, uin, password);
+ mir_free(str);
+ str = mir_strdup(szUrl);
+
+ ZeroMemory(&req, sizeof(req));
+ req.cbSize = sizeof(req);
+ req.requestType = REQUEST_POST;
+ req.szUrl = szUrl;
+ req.flags = NLHRF_NODUMP | NLHRF_HTTP11;
+ req.headersCount = 3;
+ req.headers = httpHeaders;
+ strcpy(szUrl, "https://login.gadu-gadu.pl/authorize");
+ httpHeaders[1].szName = "Content-Type";
+ httpHeaders[1].szValue = "application/x-www-form-urlencoded";
+ req.pData = str;
+ req.dataLength = (int)strlen(str);
+
+ resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)gg->netlib, (LPARAM)&req);
+ if (resp) CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp);
+ else gg_netlog(gg, "gg_oauth_receivetoken(): No response from HTTP request");
+
+ // 3. Obtaining an Access Token
+ gg_netlog(gg, "gg_oauth_receivetoken(): Obtaining an Access Token...");
+ strcpy(szUrl, "http://api.gadu-gadu.pl/access_token");
+ mir_free(str);
+ str = oauth_auth_header("POST", szUrl, HMACSHA1, uin, password, token, token_secret);
+ mir_free(token);
+ mir_free(token_secret);
+ token = NULL;
+ token_secret = NULL;
+
+ ZeroMemory(&req, sizeof(req));
+ req.cbSize = sizeof(req);
+ req.requestType = REQUEST_POST;
+ req.szUrl = szUrl;
+ req.flags = NLHRF_NODUMP | NLHRF_HTTP11;
+ req.headersCount = 3;
+ req.headers = httpHeaders;
+ httpHeaders[1].szName = "Authorization";
+ httpHeaders[1].szValue = str;
+ req.pData = NULL;
+ req.dataLength = 0;
+
+ resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)gg->netlib, (LPARAM)&req);
+ if (resp) {
+ if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) {
+ HXML hXml;
+ TCHAR *xmlAction;
+ TCHAR *tag;
+
+ xmlAction = gg_a2t(resp->pData);
+ tag = gg_a2t("result");
+ hXml = xi.parseString(xmlAction, 0, tag);
+ if (hXml != NULL) {
+ HXML node;
+
+ mir_free(tag); tag = gg_a2t("oauth_token");
+ node = xi.getChildByPath(hXml, tag, 0);
+ token = node != NULL ? gg_t2a(xi.getText(node)) : NULL;
+
+ mir_free(tag); tag = gg_a2t("oauth_token_secret");
+ node = xi.getChildByPath(hXml, tag, 0);
+ token_secret = node != NULL ? gg_t2a(xi.getText(node)) : NULL;
+
+ xi.destroyNode(hXml);
+ }
+ mir_free(tag);
+ mir_free(xmlAction);
+ }
+ else gg_netlog(gg, "gg_oauth_receivetoken(): Invalid response code from HTTP request");
+ CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp);
+ }
+ else gg_netlog(gg, "gg_oauth_receivetoken(): No response from HTTP request");
+
+ mir_free(password);
+ mir_free(str);
+
+ if (token != NULL && token_secret != NULL) {
+ DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_TOKEN, token);
+ CallService(MS_DB_CRYPT_ENCODESTRING, (WPARAM)(int)strlen(token_secret) + 1, (LPARAM) token_secret);
+ DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_TOKENSECRET, token_secret);
+ gg_netlog(gg, "gg_oauth_receivetoken(): Access Token obtained successfully.");
+ res = 1;
+ }
+ else {
+ DBDeleteContactSetting(NULL, GG_PROTO, GG_KEY_TOKEN);
+ DBDeleteContactSetting(NULL, GG_PROTO, GG_KEY_TOKENSECRET);
+ gg_netlog(gg, "gg_oauth_receivetoken(): Failed to obtain Access Token.");
+ }
+ mir_free(token);
+ mir_free(token_secret);
+
+ return res;
+}
+
+int gg_oauth_checktoken(GGPROTO *gg, int force)
+{
+ if (!force) {
+ char *token = NULL, *token_secret = NULL;
+ DBVARIANT dbv;
+ int res = 1;
+
+ if(!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_TOKEN, &dbv)) {
+ token = mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ if(!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_TOKENSECRET, &dbv)) {
+ CallService(MS_DB_CRYPT_DECODESTRING, (WPARAM)(int)strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal);
+ token_secret = mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+
+ if (token == NULL || token_secret == NULL) {
+ res = gg_oauth_receivetoken(gg);
+ }
+
+ mir_free(token);
+ mir_free(token_secret);
+
+ return res;
+ }
+
+ return gg_oauth_receivetoken(gg);
+}
diff --git a/protocols/Gadu-Gadu/ownerinfo.c b/protocols/Gadu-Gadu/ownerinfo.c
new file mode 100644
index 0000000000..aeeb7f1a9b
--- /dev/null
+++ b/protocols/Gadu-Gadu/ownerinfo.c
@@ -0,0 +1,88 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2006 Adam Strzelecki <ono+miranda@java.pl>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+
+//////////////////////////////////////////////////////////
+// remind password
+
+typedef struct
+{
+ uin_t uin;
+ const char *email;
+} GG_REMIND_PASS;
+
+void __cdecl gg_remindpasswordthread(GGPROTO *gg, void *param)
+{
+ // Connection handle
+ struct gg_http *h;
+ GG_REMIND_PASS *rp = (GG_REMIND_PASS *)param;
+ GGTOKEN token;
+
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_remindpasswordthread(): Starting.");
+#endif
+ if(!rp || !rp->email || !rp->uin || !strlen(rp->email))
+ {
+ if(rp) free(rp);
+ return;
+ }
+
+ // Get token
+ if(!gg_gettoken(gg, &token)) return;
+
+ if (!(h = gg_remind_passwd3(rp->uin, rp->email, token.id, token.val, 0)))
+ {
+ char error[128];
+ mir_snprintf(error, sizeof(error), Translate("Password could not be reminded because of error:\n\t%s"), strerror(errno));
+ MessageBox(
+ NULL,
+ error,
+ GG_PROTONAME,
+ MB_OK | MB_ICONSTOP
+ );
+ gg_netlog(gg, "gg_remindpasswordthread(): Password could not be reminded because of \"%s\".", strerror(errno));
+ }
+ else
+ {
+ gg_pubdir_free(h);
+ gg_netlog(gg, "gg_remindpasswordthread(): Password remind successful.");
+ MessageBox(
+ NULL,
+ Translate("Password was sent to your e-mail."),
+ GG_PROTONAME,
+ MB_OK | MB_ICONINFORMATION
+ );
+ }
+
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_remindpasswordthread(): End.");
+#endif
+ if(rp) free(rp);
+}
+
+void gg_remindpassword(GGPROTO *gg, uin_t uin, const char *email)
+{
+ GG_REMIND_PASS *rp = malloc(sizeof(GG_REMIND_PASS));
+
+ rp->uin = uin;
+ rp->email = email;
+ gg_forkthread(gg, gg_remindpasswordthread, rp);
+}
diff --git a/protocols/Gadu-Gadu/popups.c b/protocols/Gadu-Gadu/popups.c
new file mode 100644
index 0000000000..323bf44590
--- /dev/null
+++ b/protocols/Gadu-Gadu/popups.c
@@ -0,0 +1,172 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2011-2012 Bartosz Białek
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+
+typedef struct _tag_PopupData
+{
+ unsigned flags;
+ char* title;
+ char* text;
+ GGPROTO* gg;
+} PopupData;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Popup plugin window proc
+
+LRESULT CALLBACK PopupWindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg)
+ {
+ case WM_COMMAND:
+ {
+ PopupData* puData = (PopupData*)PUGetPluginData(hWnd);
+ if (puData != NULL)
+ {
+ if (puData->flags & GG_POPUP_MULTILOGON)
+ gg_sessions_view(puData->gg, 0, 0);
+ }
+ PUDeletePopUp(hWnd);
+ break;
+ }
+
+ case WM_CONTEXTMENU:
+ PUDeletePopUp(hWnd);
+ break;
+
+ case UM_FREEPLUGINDATA:
+ {
+ PopupData* puData = (PopupData*)PUGetPluginData(hWnd);
+ if (puData != NULL && puData != (PopupData*)CALLSERVICE_NOTFOUND)
+ {
+ mir_free(puData->title);
+ mir_free(puData->text);
+ mir_free(puData);
+ }
+ break;
+ }
+ }
+
+ return DefWindowProc(hWnd, msg, wParam, lParam);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Popup plugin class registration
+
+void gg_initpopups(GGPROTO* gg)
+{
+ char szDescr[256], szName[256];
+ POPUPCLASS puc = {0};
+
+ puc.cbSize = sizeof(puc);
+ puc.PluginWindowProc = PopupWindowProc;
+
+ puc.ptszDescription = szDescr;
+ puc.pszName = szName;
+ puc.colorBack = RGB(173, 206, 247);
+ puc.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ puc.hIcon = CopyIcon(LoadIconEx("main", FALSE));
+ ReleaseIconEx("main", FALSE);
+ puc.iSeconds = 4;
+ mir_snprintf(szDescr, SIZEOF(szDescr), "%s/%s", GG_PROTONAME, Translate("Notify"));
+ mir_snprintf(szName, SIZEOF(szName), "%s_%s", GG_PROTO, "Notify");
+ CallService(MS_POPUP_REGISTERCLASS, 0, (WPARAM)&puc);
+
+ puc.ptszDescription = szDescr;
+ puc.pszName = szName;
+ puc.colorBack = RGB(191, 0, 0); // Red
+ puc.colorText = RGB(255, 245, 225); // Yellow
+ puc.iSeconds = 60;
+ puc.hIcon = (HICON)LoadImage(NULL, IDI_WARNING, IMAGE_ICON, 0, 0, LR_SHARED);
+ mir_snprintf(szDescr, SIZEOF(szDescr), "%s/%s", GG_PROTONAME, Translate("Error"));
+ mir_snprintf(szName, SIZEOF(szName), "%s_%s", GG_PROTO, "Error");
+ CallService(MS_POPUP_REGISTERCLASS, 0, (WPARAM)&puc);
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// Show popup - popup plugin support
+
+void CALLBACK sttMainThreadCallback(PVOID dwParam)
+{
+ PopupData* puData = (PopupData*)dwParam;
+ GGPROTO* gg = puData->gg;
+
+ if (ServiceExists(MS_POPUP_ADDPOPUPCLASS))
+ {
+ char szName[256];
+ POPUPDATACLASS ppd = {sizeof(ppd)};
+ ppd.ptszTitle = puData->title;
+ ppd.ptszText = puData->text;
+ ppd.PluginData = puData;
+ ppd.pszClassName = szName;
+
+ if (puData->flags & GG_POPUP_ERROR || puData->flags & GG_POPUP_WARNING)
+ mir_snprintf(szName, SIZEOF(szName), "%s_%s", GG_PROTO, "Error");
+ else
+ mir_snprintf(szName, SIZEOF(szName), "%s_%s", GG_PROTO, "Notify");
+
+ CallService(MS_POPUP_ADDPOPUPCLASS, 0, (LPARAM)&ppd);
+ }
+ else
+ {
+ if (puData->flags & GG_POPUP_ALLOW_MSGBOX)
+ {
+ BOOL bShow = TRUE;
+
+ if (puData->flags & GG_POPUP_ONCE)
+ {
+ HWND hWnd = FindWindow(NULL, GG_PROTONAME);
+ while (hWnd != NULL)
+ {
+ if (FindWindowEx(hWnd, NULL, NULL, puData->text) != NULL)
+ {
+ bShow = FALSE;
+ break;
+ }
+ hWnd = FindWindowEx(NULL, hWnd, NULL, GG_PROTONAME);
+ }
+ }
+
+ if (bShow)
+ {
+ UINT uIcon = puData->flags & GG_POPUP_ERROR ? MB_ICONERROR : puData->flags & GG_POPUP_WARNING ? MB_ICONEXCLAMATION : MB_ICONINFORMATION;
+ MessageBox(NULL, puData->text, GG_PROTONAME, MB_OK | uIcon);
+ }
+ }
+ mir_free(puData->title);
+ mir_free(puData->text);
+ mir_free(puData);
+ }
+}
+
+void gg_showpopup(GGPROTO* gg, const char* nickname, const char* msg, int flags)
+{
+ PopupData* puData;
+
+ if (Miranda_Terminated()) return;
+
+ puData = (PopupData*)mir_alloc(sizeof(PopupData));
+ puData->flags = flags;
+ puData->title = mir_strdup(nickname);
+ puData->text = mir_strdup(msg);
+ puData->gg = gg;
+
+ CallFunctionAsync(sttMainThreadCallback, puData);
+}
diff --git a/protocols/Gadu-Gadu/resource.h b/protocols/Gadu-Gadu/resource.h
new file mode 100644
index 0000000000..86ec31eadb
--- /dev/null
+++ b/protocols/Gadu-Gadu/resource.h
@@ -0,0 +1,147 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2006 Adam Strzelecki <ono+miranda@java.pl>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef IDC_STATIC
+#define IDC_STATIC -1
+#endif
+#ifndef IDOK
+#define IDOK 1
+#endif
+#ifndef IDCANCEL
+#define IDCANCEL 2
+#endif
+
+#define IDI_GG 251
+#define IDI_IMPORT_TEXT 252
+#define IDI_IMPORT_SERVER 253
+#define IDI_EXPORT_TEXT 254
+#define IDI_EXPORT_SERVER 255
+#define IDI_REMOVE_SERVER 256
+#define IDI_SETTINGS 257
+#define IDI_LIST 258
+#define IDI_NEXT 259
+#define IDI_PREV 260
+#define IDI_SCALE 261
+#define IDI_IMAGE 262
+#define IDI_DELETE 263
+#define IDI_SAVE 264
+#define IDI_CONFERENCE 265
+#define IDI_CLEAR_CONFERENCE 266
+#define IDI_SESSIONS 267
+#define IDI_BLOCK 268
+
+#define IDD_INFO_GG 301
+#define IDD_CHPASS 302
+#define IDD_CHINFO_GG 303
+#define IDD_GGADVANCEDSEARCH 304
+#define IDD_CREATEACCOUNT 305
+#define IDD_REMOVEACCOUNT 306
+#define IDD_CHEMAIL 307
+#define IDD_OPT_GG_ADVANCED 308
+#define IDD_TOKEN 309
+#define IDD_CONFERENCE 310
+#define IDD_OPT_GG_GENERAL 311
+#define IDD_OPT_GG_CONFERENCE 312
+#define IDD_IMAGE_RECV 313
+#define IDD_IMAGE_SEND 314
+#define IDD_ACCMGRUI 315
+#define IDD_SESSIONS 316
+
+#define IDC_UIN 401
+#define IDC_PASSWORD 402
+#define IDC_LOSTPASS 403
+#define IDC_FRIENDSONLY 404
+#define IDC_SHOWINVISIBLE 405
+#define IDC_KEEPALIVE 406
+#define IDC_SAFESTATUS 407
+#define IDC_MANUALHOST 408
+#define IDC_HOST 409
+#define IDC_PORT 410
+#define IDC_RELOADREQD 411
+#define IDC_IP 412
+#define IDC_REALIP 413
+#define IDC_FIRSTNAME 420
+#define IDC_LASTNAME 421
+#define IDC_NICKNAME 422
+#define IDC_GENDER 423
+#define IDC_BIRTHYEAR 424
+#define IDC_CITY 425
+#define IDC_FAMILYNAME 426
+#define IDC_CITYORIGIN 427
+#define IDC_STATUSDESCR 428
+#define IDC_EMAIL 429
+#define IDC_CPASSWORD 430
+#define IDC_SHOWCERRORS 431
+#define IDC_ARECONNECT 432
+#define IDC_LEAVESTATUSMSG 433
+#define IDC_LEAVESTATUS 434
+#define IDC_AGEFROM 435
+#define IDC_AGETO 436
+#define IDC_ONLYCONNECTED 437
+#define IDC_WHITERECT 438
+#define IDC_CREATEACCOUNT 439
+#define IDC_REMOVEACCOUNT 440
+#define IDC_CONFIRM 441
+#define IDC_SAVE 442
+#define IDC_CHPASS 443
+#define IDC_CHEMAIL 444
+#define IDC_DIRECTCONNS 445
+#define IDC_DIRECTPORT 446
+#define IDC_FORWARDHOST 447
+#define IDC_FORWARDPORT 448
+#define IDC_FORWARDING 449
+#define IDC_MSGACK 450
+#define IDC_SSLCONN 451
+#define IDC_VERSION 452
+#define IDC_TOKEN 453
+#define IDC_IGNORECONF 454
+#define IDC_SHOWLINKS 455
+#define IDC_IMGRECEIVE 456
+#define IDC_IMGMETHOD 457
+
+#define IDC_IMAGECLOSE 458
+#define IDC_TABCONTROL 459
+#define IDC_IMAGE_SEND 460
+#define IDC_IMAGE_SAVE 461
+
+#define IDC_GC_POLICY_TOTAL 462
+#define IDC_GC_POLICY_UNKNOWN 463
+#define IDC_GC_POLICY_DEFAULT 464
+#define IDC_GC_COUNT_TOTAL 465
+#define IDC_GC_COUNT_UNKNOWN 466
+
+#define IDC_OPTIONSTAB 467
+#define IDC_ENABLEAVATARS 468
+
+#define IDC_HEADERBAR 1001
+#define IDC_SESSIONS 1002
+#define IDC_SIGNOUTALL 1003
+
+#define IDC_IMG_DELETE 1010
+#define IDC_IMG_SEND 1011
+#define IDC_IMG_PREV 1012
+#define IDC_IMG_NEXT 1013
+#define IDC_IMG_SCALE 1014
+#define IDC_IMG_SAVE 1015
+#define IDC_IMG_CANCEL 1016
+#define IDC_IMG_IMAGE 1017
+#define IDC_IMG_NAME 1018
+
+#define IDC_CLIST 1200
diff --git a/protocols/Gadu-Gadu/resource.rc b/protocols/Gadu-Gadu/resource.rc
new file mode 100644
index 0000000000..4a70fcc1a5
--- /dev/null
+++ b/protocols/Gadu-Gadu/resource.rc
@@ -0,0 +1,355 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2006 Adam Strzelecki <ono+miranda@java.pl>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include <windows.h>
+#include "resource.h"
+#include "version.rc"
+
+IDI_GG ICON DISCARDABLE "icons/gg.ico"
+IDI_IMPORT_SERVER ICON DISCARDABLE "icons/import_list_from_server.ico"
+IDI_IMPORT_TEXT ICON DISCARDABLE "icons/import_list_from_txt_file.ico"
+IDI_REMOVE_SERVER ICON DISCARDABLE "icons/remove_list_from_server.ico"
+IDI_EXPORT_SERVER ICON DISCARDABLE "icons/export_list_to_server.ico"
+IDI_EXPORT_TEXT ICON DISCARDABLE "icons/export_list_to_txt_file.ico"
+IDI_SETTINGS ICON DISCARDABLE "icons/settings.ico"
+IDI_LIST ICON DISCARDABLE "icons/list.ico"
+IDI_BLOCK ICON DISCARDABLE "icons/block.ico"
+IDI_PREV ICON DISCARDABLE "icons/previous.ico"
+IDI_NEXT ICON DISCARDABLE "icons/next.ico"
+IDI_IMAGE ICON DISCARDABLE "icons/image.ico"
+IDI_SAVE ICON DISCARDABLE "icons/save.ico"
+IDI_DELETE ICON DISCARDABLE "icons/delete.ico"
+IDI_CONFERENCE ICON DISCARDABLE "icons/conference.ico"
+IDI_CLEAR_CONFERENCE ICON DISCARDABLE "icons/clear_ignored_conference.ico"
+IDI_SESSIONS ICON DISCARDABLE "icons/sessions.ico"
+
+IDD_OPT_GG_GENERAL DIALOGEX 0,0,304,207
+STYLE DS_FIXEDSYS|WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Gadu-Gadu User Details",IDC_STATIC,4,2,298,71
+ RTEXT "Gadu-Gadu Number:",IDC_STATIC,9,18,64,8
+ RTEXT "Password:",IDC_STATIC,9,37,64,8
+ RTEXT "E-mail:",IDC_STATIC,9,56,64,8
+ EDITTEXT IDC_UIN,84,16,85,12,ES_AUTOHSCROLL|ES_NUMBER
+ EDITTEXT IDC_PASSWORD,84,35,85,12,ES_PASSWORD|ES_AUTOHSCROLL
+ EDITTEXT IDC_EMAIL,84,54,85,12,ES_AUTOHSCROLL
+ CONTROL "Create new account",IDC_CREATEACCOUNT,"Hyperlink",WS_TABSTOP,175,18,90,8
+ CONTROL "Remove account",IDC_REMOVEACCOUNT,"Hyperlink",WS_TABSTOP,175,18,90,8
+ CONTROL "Change password",IDC_CHPASS,"Hyperlink",WS_TABSTOP,175,31,90,8
+ CONTROL "Retrieve password",IDC_LOSTPASS,"Hyperlink",WS_TABSTOP,175,44,90,8
+// CONTROL "Change e-mail",IDC_CHEMAIL,"Hyperlink",WS_TABSTOP,175,62,90,8
+ GROUPBOX "Options",IDC_STATIC,4,75,298,106
+ CONTROL "Friends only",IDC_FRIENDSONLY,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,12,90,255,10
+ CONTROL "Show offline users with status message as invisible in contact list",IDC_SHOWINVISIBLE,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,12,105,255,10
+ CONTROL "After disconnection leave away message of status:",IDC_LEAVESTATUSMSG,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,12,120,195,10
+ COMBOBOX IDC_LEAVESTATUS,210,118,82,90,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+// CONTROL "Ignore incoming conference messages",IDC_IGNORECONF,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,12,145,255,10
+ CONTROL "Receive image and after image is received use:",IDC_IMGRECEIVE,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,12,135,175,10
+ COMBOBOX IDC_IMGMETHOD,190,133,102,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ CONTROL "Show links from unknown contacts",IDC_SHOWLINKS,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,12,150,255,10
+ CONTROL "Enable avatars",IDC_ENABLEAVATARS,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,12,165,255,10
+
+ CTEXT "You will need to reconnect for the changes you have made on this page to take effect.",IDC_RELOADREQD,4,215,290,8,NOT WS_VISIBLE
+END
+
+IDD_OPT_GG_CONFERENCE DIALOGEX 0,0,304,207
+STYLE DS_FIXEDSYS|WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Conference policy",IDC_STATIC,4,2,298,66
+ LTEXT "if total participant count greater than:",IDC_STATIC,85,18,143,8
+ COMBOBOX IDC_GC_POLICY_TOTAL,11,15,70,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "if unknown participant count greater than:",IDC_STATIC,85,34,143,8
+ EDITTEXT IDC_GC_COUNT_TOTAL,234,16,35,13,ES_AUTOHSCROLL|ES_NUMBER
+ COMBOBOX IDC_GC_POLICY_UNKNOWN,11,31,70,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "in other case",IDC_STATIC,85,50,143,8
+ EDITTEXT IDC_GC_COUNT_UNKNOWN,234,32,35,13,ES_AUTOHSCROLL|ES_NUMBER
+ COMBOBOX IDC_GC_POLICY_DEFAULT,11,47,70,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+END
+
+IDD_OPT_GG_ADVANCED DIALOGEX 0,0,304,207
+STYLE DS_FIXEDSYS|WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ GROUPBOX "Advanced Configuration",IDC_STATIC,4,2,298,146
+ CONTROL "Keep connection alive",IDC_KEEPALIVE,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,12,17,255,10
+ CONTROL "Show connection errors",IDC_SHOWCERRORS,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,12,32,255,10
+ CONTROL "Automatically reconnect after unintentional disconnection",IDC_ARECONNECT,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,12,47,255,10
+ CONTROL "Send messages slower, but with full acknowledgement",IDC_MSGACK,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,12,62,255,10
+ CONTROL "Manually specify connection servers' hosts",IDC_MANUALHOST,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,12,77,255,10
+// LTEXT "Host:",IDC_STATIC,31,97,26,8
+ EDITTEXT IDC_HOST,22,91,130,36,ES_MULTILINE|ES_AUTOHSCROLL|ES_WANTRETURN|WS_DISABLED|WS_VSCROLL
+// LTEXT "Port:",IDC_STATIC,161,97,21,8
+// EDITTEXT IDC_PORT,186,96,31,12,ES_AUTOHSCROLL|WS_DISABLED|WS_TABSTOP|ES_NUMBER
+ LTEXT "* new line is separator\n** hostname:port format",IDC_STATIC,161,92,93,32
+ CONTROL "Use SSL secure connection",IDC_SSLCONN,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,12,132,255,10
+
+ GROUPBOX "File Transfer",IDC_STATIC,4,150,298,61
+ CONTROL "Use direct connections",IDC_DIRECTCONNS,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,12,165,148,10
+ LTEXT "Port:",IDC_STATIC,161,164,21,8
+ EDITTEXT IDC_DIRECTPORT,186,165,31,12,ES_AUTOHSCROLL|WS_DISABLED|WS_TABSTOP|ES_NUMBER
+ CONTROL "Use forwarding",IDC_FORWARDING,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,12,178,255,10
+ LTEXT "Host:",IDC_STATIC,31,192,26,8
+ EDITTEXT IDC_FORWARDHOST,62,191,90,12,ES_AUTOHSCROLL|WS_DISABLED|WS_TABSTOP
+ LTEXT "Port:",IDC_STATIC,161,192,21,8
+ EDITTEXT IDC_FORWARDPORT,186,191,31,12,ES_AUTOHSCROLL|WS_DISABLED|WS_TABSTOP|ES_NUMBER
+
+ CTEXT "You will need to reconnect for the changes you have made on this page to take effect.",IDC_RELOADREQD,4,215,290,8,NOT WS_VISIBLE
+END
+
+IDD_INFO_GG DIALOGEX 0, 0, 222, 132
+STYLE DS_3DLOOK | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ LTEXT "Number:",IDC_STATIC,5,5,61,8
+ EDITTEXT IDC_UIN,74,5,60,8,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP
+// LTEXT "Version:",IDC_STATIC,144,5,56,8
+// EDITTEXT IDC_VERSION,175,5,60,8,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP
+ LTEXT "Internal IP:",IDC_STATIC,5,18,61,8
+ EDITTEXT IDC_REALIP,74,18,60,8,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP
+ LTEXT "Port:",IDC_STATIC,144,18,56,8
+ EDITTEXT IDC_PORT,175,18,60,8,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP
+
+ LTEXT "First name:",IDC_STATIC,5,31,61,8
+ EDITTEXT IDC_FIRSTNAME,74,31,161,8,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP
+ LTEXT "Last name:",IDC_STATIC,5,44,61,8
+ EDITTEXT IDC_LASTNAME,74,44,161,8,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP
+ LTEXT "Family name:",IDC_STATIC,5,57,61,8
+ EDITTEXT IDC_FAMILYNAME,74,57,161,8,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP
+ LTEXT "Nickname:",IDC_STATIC,5,70,61,8
+ EDITTEXT IDC_NICKNAME,74,70,60,8,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP
+ LTEXT "Gender:",IDC_STATIC,144,70,56,8
+ EDITTEXT IDC_GENDER,165,70,60,8,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP
+
+ LTEXT "City:",IDC_STATIC,5,83,61,8
+ EDITTEXT IDC_CITY,74,83,161,8,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP
+ LTEXT "Origin city:",IDC_STATIC,5,96,61,8
+ EDITTEXT IDC_CITYORIGIN,74,96,161,8,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP
+ LTEXT "Birth year:",IDC_STATIC,5,109,61,8
+ EDITTEXT IDC_BIRTHYEAR,74,109,161,8,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP
+ LTEXT "Description:",IDC_STATIC,5,122,61,8
+ EDITTEXT IDC_STATUSDESCR,74,122,161,8,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP
+END
+
+IDD_CHINFO_GG DIALOGEX 0, 0, 222, 132
+STYLE DS_3DLOOK | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ LTEXT "First name:",IDC_STATIC,5,7,51,8
+ EDITTEXT IDC_FIRSTNAME,64,5,100,12,ES_AUTOHSCROLL | WS_TABSTOP
+ LTEXT "Last name:",IDC_STATIC,5,22,51,8
+ EDITTEXT IDC_LASTNAME,64,20,100,12,ES_AUTOHSCROLL | WS_TABSTOP
+ LTEXT "Family name:",IDC_STATIC,5,37,51,8
+ EDITTEXT IDC_FAMILYNAME,64,35,100,12,ES_AUTOHSCROLL | WS_TABSTOP
+ LTEXT "Nickname:",IDC_STATIC,5,52,51,8
+ EDITTEXT IDC_NICKNAME,64,50,100,12,ES_AUTOHSCROLL | WS_TABSTOP
+ LTEXT "Gender:",IDC_STATIC,5,67,51,8
+ COMBOBOX IDC_GENDER,64,65,80,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ LTEXT "City:",IDC_STATIC,5,82,51,8
+ EDITTEXT IDC_CITY,64,80,100,12,ES_AUTOHSCROLL | WS_TABSTOP
+ LTEXT "Origin city:",IDC_STATIC,5,97,51,8
+ EDITTEXT IDC_CITYORIGIN,64,95,100,12,ES_AUTOHSCROLL | WS_TABSTOP
+ LTEXT "Birth year:",IDC_STATIC,5,112,51,8
+ EDITTEXT IDC_BIRTHYEAR,64,110,50,12,ES_AUTOHSCROLL | WS_TABSTOP | ES_NUMBER
+
+ PUSHBUTTON "&Save changes",IDC_SAVE,135,115,80,13,WS_DISABLED
+END
+
+IDD_TOKEN DIALOGEX 0, 0, 130, 75
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_TOOLWINDOW
+CAPTION "Enter token to continue"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ LTEXT "",IDC_WHITERECT,0,0,130,26,NOT WS_VISIBLE
+ CONTROL "",IDC_STATIC,"Static",SS_ETCHEDHORZ,0,26,130,1
+
+ EDITTEXT IDC_TOKEN,25,35,80,12,ES_AUTOHSCROLL | WS_TABSTOP
+
+ PUSHBUTTON "OK",IDOK,10,53,50,14,WS_TABSTOP
+ PUSHBUTTON "Cancel",IDCANCEL,70,53,50,14,WS_TABSTOP
+END
+
+IDD_CREATEACCOUNT DIALOGEX 0, 0, 238, 104
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Create Gadu-Gadu account"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "Create Gadu-Gadu account\nThis will create new Gadu-Gadu account",IDC_HEADERBAR,"MHeaderbarCtrl",0x0,0,0,238,25
+
+ RTEXT "New password:",IDC_STATIC,7,33,81,8
+ EDITTEXT IDC_PASSWORD,95,32,79,12,ES_PASSWORD | ES_AUTOHSCROLL | WS_TABSTOP
+ RTEXT "Confirm password:",IDC_STATIC,7,47,81,8
+ EDITTEXT IDC_CPASSWORD,95,46,79,12,ES_PASSWORD | ES_AUTOHSCROLL | WS_TABSTOP
+ RTEXT "New e-mail:",IDC_STATIC,7,61,81,8
+ EDITTEXT IDC_EMAIL,95,60,99,12,ES_AUTOHSCROLL | WS_TABSTOP
+
+ PUSHBUTTON "&Create",IDOK,125,82,50,14,WS_TABSTOP
+ PUSHBUTTON "Cancel",IDCANCEL,181,82,50,14,WS_TABSTOP
+END
+
+IDD_REMOVEACCOUNT DIALOGEX 0, 0, 238, 104
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Remove Gadu-Gadu account"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "Remove Gadu-Gadu account\nThis will remove your Gadu-Gadu account",IDC_HEADERBAR,"MHeaderbarCtrl",0x0,0,0,238,25
+
+ RTEXT "Password:",IDC_STATIC,7,32,81,8
+ EDITTEXT IDC_PASSWORD,95,31,79,12,ES_PASSWORD | ES_AUTOHSCROLL | WS_TABSTOP
+ RTEXT "Confirm password:",IDC_STATIC,7,46,81,8
+ EDITTEXT IDC_CPASSWORD,95,45,79,12,ES_PASSWORD | ES_AUTOHSCROLL | WS_TABSTOP
+
+ CONTROL "Yes, I want to remove my account",IDC_CONFIRM,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,32,64,150,10
+
+ PUSHBUTTON "Remove",IDOK,125,82,50,14,WS_TABSTOP
+ PUSHBUTTON "Cancel",IDCANCEL,181,82,50,14,WS_TABSTOP
+END
+
+IDD_CHPASS DIALOGEX 0, 0, 238, 104
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Change Gadu-Gadu password"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "Change Gadu-Gadu password\nChanges current Gadu-Gadu user password",IDC_HEADERBAR,"MHeaderbarCtrl",0x0,0,0,238,25
+
+ RTEXT "New password:",IDC_STATIC,7,42,81,8
+ EDITTEXT IDC_PASSWORD,95,41,79,12,ES_PASSWORD | ES_AUTOHSCROLL | WS_TABSTOP
+ RTEXT "Confirm password:",IDC_STATIC,7,56,81,8
+ EDITTEXT IDC_CPASSWORD,95,55,79,12,ES_PASSWORD | ES_AUTOHSCROLL | WS_TABSTOP
+
+ PUSHBUTTON "OK",IDOK,125,82,50,14,WS_TABSTOP
+ PUSHBUTTON "Cancel",IDCANCEL,181,82,50,14,WS_TABSTOP
+END
+
+IDD_CHEMAIL DIALOGEX 0, 0, 238, 104
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Change Gadu-Gadu e-mail"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "Change Gadu-Gadu e-mail\nChanges current Gadu-Gadu user e-mail",IDC_HEADERBAR,"MHeaderbarCtrl",0x0,0,0,238,25
+
+ RTEXT "New e-mail:",IDC_STATIC,7,47,81,8
+ EDITTEXT IDC_EMAIL,95,46,79,12,ES_AUTOHSCROLL | WS_TABSTOP
+
+ PUSHBUTTON "OK",IDOK,125,82,50,14,WS_TABSTOP
+ PUSHBUTTON "Cancel",IDCANCEL,181,82,50,14,WS_TABSTOP
+END
+
+IDD_GGADVANCEDSEARCH DIALOGEX 0, 0, 285, 75
+STYLE DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_BORDER
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ RTEXT "First name:",IDC_STATIC,5,7,50,8
+ EDITTEXT IDC_FIRSTNAME,60,5,80,12,ES_AUTOHSCROLL | WS_TABSTOP
+ RTEXT "Last name:",IDC_STATIC,145,7,50,8
+ EDITTEXT IDC_LASTNAME,200,5,80,12,ES_AUTOHSCROLL | WS_TABSTOP
+ RTEXT "Nickname:",IDC_STATIC,5,22,50,8
+ EDITTEXT IDC_NICKNAME,60,20,80,12,ES_AUTOHSCROLL | WS_TABSTOP
+ RTEXT "Gender:",IDC_STATIC,145,22,50,8
+ COMBOBOX IDC_GENDER,200,20,80,60,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ RTEXT "Age from:",IDC_STATIC,5,37,50,8
+ EDITTEXT IDC_AGEFROM,60,35,30,12,ES_AUTOHSCROLL | WS_TABSTOP
+ RTEXT "to:",IDC_STATIC,91,37,18,8
+ EDITTEXT IDC_AGETO,110,35,30,12,ES_AUTOHSCROLL | WS_TABSTOP
+ RTEXT "City:",IDC_STATIC,145,37,50,8
+ EDITTEXT IDC_CITY,200,35,80,12,ES_AUTOHSCROLL | WS_TABSTOP
+ CONTROL "Search online users only",IDC_ONLYCONNECTED,"Button",BS_AUTOCHECKBOX|WS_TABSTOP,15,60,270,8
+END
+
+IDD_IMAGE_RECV DIALOGEX 0, 0, 226, 148
+STYLE WS_THICKFRAME | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP |
+ WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ LTEXT "",IDC_IMG_NAME,3, 7, 140, 8
+ CONTROL "",IDC_IMG_PREV,"MButtonClass",WS_TABSTOP,147,4,16,14,0x18000000L
+ CONTROL "",IDC_IMG_NEXT,"MButtonClass",WS_TABSTOP,167,4,16,14,0x18000000L
+ CONTROL "",IDC_IMG_DELETE,"MButtonClass",WS_TABSTOP,187,4,16,14,0x18000000L
+ CONTROL "",IDC_IMG_SAVE,"MButtonClass",WS_TABSTOP,207,4,16,15,0x18000000L
+// CONTROL "",IDC_IMG_SCALE,"MButtonClass",WS_TABSTOP,207,4,16,15,0x18000000L
+
+// LTEXT "",IDC_IMG_IMAGE,3,22,220,103,WS_BORDER
+ CONTROL "",IDC_IMG_IMAGE,"Static",SS_GRAYFRAME,3,22,220,103
+
+ PUSHBUTTON "&Close",IDC_IMG_CANCEL,171,130,50,14
+END
+
+IDD_IMAGE_SEND DIALOGEX 0, 0, 226, 148
+STYLE WS_THICKFRAME | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP |
+ WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ LTEXT "",IDC_IMG_NAME,3, 7, 200, 8
+/*
+ CONTROL "",IDC_IMG_SAVE,"MButtonClass",WS_TABSTOP,187,4,16,15,0x18000000L
+ CONTROL "",IDC_IMG_SCALE,"MButtonClass",WS_TABSTOP,207,4,16,15,0x18000000L
+*/
+// LTEXT "",IDC_IMG_IMAGE,3,22,220,103,WS_BORDER
+ CONTROL "",IDC_IMG_IMAGE,"Static",SS_GRAYFRAME,3,22,220,103
+
+ DEFPUSHBUTTON "&Send",IDC_IMG_SEND,117,130,50,14
+ PUSHBUTTON "&Close",IDC_IMG_CANCEL,171,130,50,14
+END
+
+IDD_CONFERENCE DIALOGEX 0, 0, 162, 198
+STYLE DS_MODALFRAME | DS_SETFOREGROUND | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU
+CAPTION "Open new conference"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "Open new conference\nSelect conference participants",IDC_HEADERBAR,"MHeaderbarCtrl",0x0,0,0,162,25
+ CONTROL "",IDC_CLIST,"CListControl",WS_TABSTOP | 0x2c8, 7, 32, 148, 140, WS_EX_CLIENTEDGE
+ PUSHBUTTON "Open",IDOK,50,177,50,14,WS_TABSTOP|WS_DISABLED
+ PUSHBUTTON "Cancel",IDCANCEL,105,177,50,14,WS_TABSTOP
+END
+
+IDD_ACCMGRUI DIALOGEX 0, 0, 186, 134
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD
+EXSTYLE WS_EX_CONTROLPARENT
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ RTEXT "Gadu-Gadu Number:",IDC_STATIC,0,14,70,12,SS_CENTERIMAGE
+ EDITTEXT IDC_UIN,77,14,108,12,ES_AUTOHSCROLL
+ RTEXT "Password:",IDC_STATIC,0,30,70,12,SS_CENTERIMAGE
+ EDITTEXT IDC_PASSWORD,77,30,108,12,ES_PASSWORD | ES_AUTOHSCROLL
+ RTEXT "E-mail:",IDC_STATIC,0,50,70,8
+ EDITTEXT IDC_EMAIL,77,46,108,12,ES_AUTOHSCROLL
+ CONTROL "Create new account",IDC_CREATEACCOUNT,"Hyperlink",WS_TABSTOP,0,76,90,8
+END
+
+IDD_SESSIONS DIALOGEX 0, 0, 414, 136
+STYLE DS_SETFONT | DS_3DLOOK | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
+EXSTYLE WS_EX_CONTROLPARENT
+CAPTION "Concurrent Sessions"
+FONT 8, "MS Shell Dlg", 0, 0, 0x1
+BEGIN
+ CONTROL "Concurrent %s Login Sessions\nView information on active concurrent sessions",IDC_HEADERBAR,"MHeaderbarCtrl",0x0,0,0,414,25
+ CONTROL "",IDC_SESSIONS,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_SORTASCENDING | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,5,30,403,81
+ PUSHBUTTON "Sign out all sessions",IDC_SIGNOUTALL,317,116,91,15,WS_DISABLED
+END
diff --git a/protocols/Gadu-Gadu/services.c b/protocols/Gadu-Gadu/services.c
new file mode 100644
index 0000000000..bbb7434f6b
--- /dev/null
+++ b/protocols/Gadu-Gadu/services.c
@@ -0,0 +1,1020 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2009 Adam Strzelecki <ono+miranda@java.pl>
+// Copyright (c) 2009-2012 Bartosz Białek
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+#include <io.h>
+
+//////////////////////////////////////////////////////////
+// Status mode -> DB
+char *gg_status2db(int status, const char *suffix)
+{
+ char *prefix;
+ static char str[64];
+
+ switch(status) {
+ case ID_STATUS_AWAY: prefix = "Away"; break;
+ case ID_STATUS_NA: prefix = "Na"; break;
+ case ID_STATUS_DND: prefix = "Dnd"; break;
+ case ID_STATUS_OCCUPIED: prefix = "Occupied"; break;
+ case ID_STATUS_FREECHAT: prefix = "FreeChat"; break;
+ case ID_STATUS_ONLINE: prefix = "On"; break;
+ case ID_STATUS_OFFLINE: prefix = "Off"; break;
+ case ID_STATUS_INVISIBLE: prefix = "Inv"; break;
+ case ID_STATUS_ONTHEPHONE: prefix = "Otp"; break;
+ case ID_STATUS_OUTTOLUNCH: prefix = "Otl"; break;
+ default: return NULL;
+ }
+ strncpy(str, prefix, sizeof(str));
+ strncat(str, suffix, sizeof(str) - strlen(str));
+ return str;
+}
+
+//////////////////////////////////////////////////////////
+// checks proto capabilities
+DWORD_PTR gg_getcaps(PROTO_INTERFACE *proto, int type, HANDLE hContact)
+{
+ switch (type) {
+ case PFLAGNUM_1:
+ return PF1_IM | PF1_BASICSEARCH | PF1_EXTSEARCH | PF1_EXTSEARCHUI | PF1_SEARCHBYNAME |
+ PF1_MODEMSG | PF1_NUMERICUSERID | PF1_VISLIST | PF1_FILE;
+ case PFLAGNUM_2:
+ return PF2_ONLINE | PF2_SHORTAWAY | PF2_HEAVYDND | PF2_FREECHAT | PF2_INVISIBLE |
+ PF2_LONGAWAY;
+ case PFLAGNUM_3:
+ return PF2_ONLINE | PF2_SHORTAWAY | PF2_HEAVYDND | PF2_FREECHAT | PF2_INVISIBLE;
+ case PFLAGNUM_4:
+ return PF4_NOCUSTOMAUTH | PF4_SUPPORTTYPING | PF4_AVATARS | PF4_IMSENDOFFLINE;
+ case PFLAGNUM_5:
+ return PF2_LONGAWAY;
+ case PFLAG_UNIQUEIDTEXT:
+ return (DWORD_PTR) Translate("Gadu-Gadu Number");
+ case PFLAG_UNIQUEIDSETTING:
+ return (DWORD_PTR) GG_KEY_UIN;
+ }
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// loads protocol icon
+HICON gg_geticon(PROTO_INTERFACE *proto, int iconIndex)
+{
+ if (LOWORD(iconIndex) == PLI_PROTOCOL)
+ {
+ HICON hIcon;
+ BOOL big;
+
+ if (iconIndex & PLIF_ICOLIBHANDLE)
+ return (HICON)GetIconHandle(IDI_GG);
+
+ big = (iconIndex & PLIF_SMALL) == 0;
+ hIcon = LoadIconEx("main", big);
+
+ if (iconIndex & PLIF_ICOLIB)
+ return hIcon;
+
+ hIcon = CopyIcon(hIcon);
+ ReleaseIconEx("main", big);
+ return hIcon;
+ }
+
+ return (HICON)NULL;
+}
+
+//////////////////////////////////////////////////////////
+// gets protocol status
+GGINLINE char *gg_getstatusmsg(GGPROTO *gg, int status)
+{
+ switch(status)
+ {
+ case ID_STATUS_ONLINE:
+ return gg->modemsg.online;
+ break;
+ case ID_STATUS_DND:
+ return gg->modemsg.dnd;
+ break;
+ case ID_STATUS_FREECHAT:
+ return gg->modemsg.freechat;
+ break;
+ case ID_STATUS_INVISIBLE:
+ return gg->modemsg.invisible;
+ break;
+ case ID_STATUS_AWAY:
+ default:
+ return gg->modemsg.away;
+ }
+}
+
+//////////////////////////////////////////////////////////
+// sets specified protocol status
+int gg_refreshstatus(GGPROTO *gg, int status)
+{
+ if(status == ID_STATUS_OFFLINE)
+ {
+ gg_disconnect(gg);
+ return TRUE;
+ }
+
+ if(!gg_isonline(gg))
+ {
+ DWORD exitCode = 0;
+ GetExitCodeThread(gg->pth_sess.hThread, &exitCode);
+ if (exitCode == STILL_ACTIVE)
+ return TRUE;
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_refreshstatus(): Going to connect...");
+#endif
+ gg_threadwait(gg, &gg->pth_sess);
+ gg->pth_sess.hThread = gg_forkthreadex(gg, gg_mainthread, NULL, &gg->pth_sess.dwThreadId);
+ }
+ else
+ {
+ char *szMsg = NULL;
+ // Select proper msg
+ EnterCriticalSection(&gg->modemsg_mutex);
+ szMsg = mir_strdup(gg_getstatusmsg(gg, status));
+ LeaveCriticalSection(&gg->modemsg_mutex);
+ if(szMsg)
+ {
+ gg_netlog(gg, "gg_refreshstatus(): Setting status and away message.");
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_change_status_descr(gg->sess, status_m2gg(gg, status, szMsg != NULL), szMsg);
+ LeaveCriticalSection(&gg->sess_mutex);
+ }
+ else
+ {
+ gg_netlog(gg, "gg_refreshstatus(): Setting just status.");
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_change_status(gg->sess, status_m2gg(gg, status, 0));
+ LeaveCriticalSection(&gg->sess_mutex);
+ }
+ // Change status of the contact with our own UIN (if got yourself added to the contact list)
+ gg_changecontactstatus(gg, DBGetContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, 0), status_m2gg(gg, status, szMsg != NULL), szMsg, 0, 0, 0, 0);
+ gg_broadcastnewstatus(gg, status);
+ mir_free(szMsg);
+ }
+
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////
+// normalize gg status
+int gg_normalizestatus(int status)
+{
+ switch(status)
+ {
+ case ID_STATUS_ONLINE:
+ return ID_STATUS_ONLINE;
+ case ID_STATUS_DND:
+ return ID_STATUS_DND;
+ case ID_STATUS_FREECHAT:
+ return ID_STATUS_FREECHAT;
+ case ID_STATUS_OFFLINE:
+ return ID_STATUS_OFFLINE;
+ case ID_STATUS_INVISIBLE:
+ return ID_STATUS_INVISIBLE;
+ default:
+ return ID_STATUS_AWAY;
+ }
+}
+
+//////////////////////////////////////////////////////////
+// sets protocol status
+int gg_setstatus(PROTO_INTERFACE *proto, int iNewStatus)
+{
+ GGPROTO *gg = (GGPROTO *)proto;
+ int nNewStatus = gg_normalizestatus(iNewStatus);
+
+ EnterCriticalSection(&gg->modemsg_mutex);
+ gg->proto.m_iDesiredStatus = nNewStatus;
+ LeaveCriticalSection(&gg->modemsg_mutex);
+
+ // If waiting for connection retry attempt then signal to stop that
+ if (gg->hConnStopEvent) SetEvent(gg->hConnStopEvent);
+
+ if (gg->proto.m_iStatus == nNewStatus) return 0;
+ gg_netlog(gg, "gg_setstatus(): PS_SETSTATUS(%d) normalized to %d.", iNewStatus, nNewStatus);
+ gg_refreshstatus(gg, nNewStatus);
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// when messsage received
+int gg_recvmessage(PROTO_INTERFACE *proto, HANDLE hContact, PROTORECVEVENT *pre)
+{
+ CCSDATA ccs = { hContact, PSR_MESSAGE, 0, ( LPARAM )pre };
+ return CallService(MS_PROTO_RECVMSG, 0, ( LPARAM )&ccs);
+}
+
+//////////////////////////////////////////////////////////
+// when messsage sent
+typedef struct
+{
+ HANDLE hContact;
+ int seq;
+} GG_SEQ_ACK;
+void __cdecl gg_sendackthread(GGPROTO *gg, void *ack)
+{
+ SleepEx(100, FALSE);
+ ProtoBroadcastAck(GG_PROTO, ((GG_SEQ_ACK *)ack)->hContact,
+ ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE) ((GG_SEQ_ACK *)ack)->seq, 0);
+ mir_free(ack);
+}
+int gg_sendmessage(PROTO_INTERFACE *proto, HANDLE hContact, int flags, const char *msg)
+{
+ GGPROTO *gg = (GGPROTO *)proto;
+ uin_t uin;
+
+ if (msg && gg_isonline(gg) && (uin = (uin_t)DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0)))
+ {
+ int seq;
+ EnterCriticalSection(&gg->sess_mutex);
+ seq = gg_send_message(gg->sess, GG_CLASS_CHAT, uin, msg);
+ LeaveCriticalSection(&gg->sess_mutex);
+ if (!DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_MSGACK, GG_KEYDEF_MSGACK))
+ {
+ // Auto-ack message without waiting for server ack
+ GG_SEQ_ACK *ack = mir_alloc(sizeof(GG_SEQ_ACK));
+ if (ack)
+ {
+ ack->seq = seq;
+ ack->hContact = hContact;
+ gg_forkthread(gg, gg_sendackthread, ack);
+ }
+ }
+ return seq;
+ }
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// when basic search
+void __cdecl gg_searchthread(GGPROTO *gg, void *empty)
+{
+ SleepEx(100, FALSE);
+ gg_netlog(gg, "gg_searchthread(): Failed search.");
+ ProtoBroadcastAck(GG_PROTO, NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, (HANDLE)1, 0);
+}
+HANDLE gg_basicsearch(PROTO_INTERFACE *proto, const PROTOCHAR *id)
+{
+ GGPROTO *gg = (GGPROTO *)proto;
+ gg_pubdir50_t req;
+ char *ida;
+
+ if(!gg_isonline(gg))
+ return (HANDLE)0;
+
+ if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH)))
+ {
+ gg_forkthread(gg, gg_searchthread, NULL);
+ return (HANDLE)1;
+ }
+
+ ida = gg_t2a(id);
+
+ // Add uin and search it
+ gg_pubdir50_add(req, GG_PUBDIR50_UIN, ida);
+ gg_pubdir50_seq_set(req, GG_SEQ_SEARCH);
+
+ mir_free(ida);
+
+ EnterCriticalSection(&gg->sess_mutex);
+ if(!gg_pubdir50(gg->sess, req))
+ {
+ LeaveCriticalSection(&gg->sess_mutex);
+ gg_forkthread(gg, gg_searchthread, NULL);
+ return (HANDLE)1;
+ }
+ LeaveCriticalSection(&gg->sess_mutex);
+ gg_netlog(gg, "gg_basicsearch(): Seq %d.", req->seq);
+ gg_pubdir50_free(req);
+
+ return (HANDLE)1;
+}
+static HANDLE gg_searchbydetails(PROTO_INTERFACE *proto, const PROTOCHAR *nick, const PROTOCHAR *firstName, const PROTOCHAR *lastName)
+{
+ GGPROTO *gg = (GGPROTO *)proto;
+ gg_pubdir50_t req;
+ unsigned long crc;
+ char data[512] = "\0";
+
+ // Check if connected and if there's a search data
+ if(!gg_isonline(gg))
+ return 0;
+
+ if(!nick && !firstName && !lastName)
+ return 0;
+
+ if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH)))
+ {
+ gg_forkthread(gg, gg_searchthread, NULL);
+ return (HANDLE)1;
+ }
+
+ // Add uin and search it
+ if(nick)
+ {
+ char *nickA = gg_t2a(nick);
+ gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, nickA);
+ strncat(data, nickA, sizeof(data) - strlen(data));
+ mir_free(nickA);
+ }
+ strncat(data, ".", sizeof(data) - strlen(data));
+
+ if(firstName)
+ {
+ char *firstNameA = gg_t2a(firstName);
+ gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, firstNameA);
+ strncat(data, firstNameA, sizeof(data) - strlen(data));
+ mir_free(firstNameA);
+ }
+ strncat(data, ".", sizeof(data) - strlen(data));
+
+ if(lastName)
+ {
+ char *lastNameA = gg_t2a(lastName);
+ gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, lastNameA);
+ strncat(data, lastNameA, sizeof(data) - strlen(data));
+ mir_free(lastNameA);
+ }
+ strncat(data, ".", sizeof(data) - strlen(data));
+
+ // Count crc & check if the data was equal if yes do same search with shift
+ crc = crc_get(data);
+
+ if(crc == gg->last_crc && gg->next_uin)
+ gg_pubdir50_add(req, GG_PUBDIR50_START, ditoa(gg->next_uin));
+ else
+ gg->last_crc = crc;
+
+ gg_pubdir50_seq_set(req, GG_SEQ_SEARCH);
+
+ EnterCriticalSection(&gg->sess_mutex);
+ if(!gg_pubdir50(gg->sess, req))
+ {
+ LeaveCriticalSection(&gg->sess_mutex);
+ gg_forkthread(gg, gg_searchthread, NULL);
+ return (HANDLE)1;
+ }
+ LeaveCriticalSection(&gg->sess_mutex);
+ gg_netlog(gg, "gg_searchbyname(): Seq %d.", req->seq);
+ gg_pubdir50_free(req);
+
+ return (HANDLE)1;
+}
+
+//////////////////////////////////////////////////////////
+// when contact is added to list
+HANDLE gg_addtolist(PROTO_INTERFACE *proto, int flags, PROTOSEARCHRESULT *psr)
+{
+ GGPROTO *gg = (GGPROTO *)proto;
+ GGSEARCHRESULT *sr = (GGSEARCHRESULT *)psr;
+ char *szNick = psr->flags & PSR_UNICODE ? mir_u2a((wchar_t *)sr->hdr.nick) : mir_strdup(sr->hdr.nick);
+ uin_t uin;
+ HANDLE hContact;
+
+ if (psr->cbSize == sizeof(GGSEARCHRESULT))
+ uin = sr->uin;
+ else
+ uin = psr->flags & PSR_UNICODE ? _wtoi((wchar_t*)psr->id) : atoi(psr->id);
+
+ hContact = gg_getcontact(gg, uin, 1, flags & PALF_TEMPORARY ? 0 : 1, szNick);
+ mir_free(szNick);
+
+ return hContact;
+}
+
+//////////////////////////////////////////////////////////
+// user info request
+void __cdecl gg_cmdgetinfothread(GGPROTO *gg, void *hContact)
+{
+ SleepEx(100, FALSE);
+ gg_netlog(gg, "gg_cmdgetinfothread(): Failed info retreival.");
+ ProtoBroadcastAck(GG_PROTO, hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE) 1, 0);
+}
+int gg_getinfo(PROTO_INTERFACE *proto, HANDLE hContact, int infoType)
+{
+ GGPROTO *gg = (GGPROTO *)proto;
+ gg_pubdir50_t req;
+
+ // Custom contact info
+ if(hContact)
+ {
+ if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH)))
+ {
+ gg_forkthread(gg, gg_cmdgetinfothread, hContact);
+ return 1;
+ }
+
+ // Add uin and search it
+ gg_pubdir50_add(req, GG_PUBDIR50_UIN, ditoa((uin_t)DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0)));
+ gg_pubdir50_seq_set(req, GG_SEQ_INFO);
+
+ gg_netlog(gg, "gg_getinfo(): Requesting user info.", req->seq);
+ if(gg_isonline(gg))
+ {
+ EnterCriticalSection(&gg->sess_mutex);
+ if(!gg_pubdir50(gg->sess, req))
+ {
+ LeaveCriticalSection(&gg->sess_mutex);
+ gg_forkthread(gg, gg_cmdgetinfothread, hContact);
+ return 1;
+ }
+ LeaveCriticalSection(&gg->sess_mutex);
+ }
+ }
+ // Own contact info
+ else
+ {
+ if (!(req = gg_pubdir50_new(GG_PUBDIR50_READ)))
+ {
+ gg_forkthread(gg, gg_cmdgetinfothread, hContact);
+ return 1;
+ }
+
+ // Add seq
+ gg_pubdir50_seq_set(req, GG_SEQ_CHINFO);
+
+ gg_netlog(gg, "gg_getinfo(): Requesting owner info.", req->seq);
+ if(gg_isonline(gg))
+ {
+ EnterCriticalSection(&gg->sess_mutex);
+ if(!gg_pubdir50(gg->sess, req))
+ {
+ LeaveCriticalSection(&gg->sess_mutex);
+ gg_forkthread(gg, gg_cmdgetinfothread, hContact);
+ return 1;
+ }
+ LeaveCriticalSection(&gg->sess_mutex);
+ }
+ }
+ gg_netlog(gg, "gg_getinfo(): Seq %d.", req->seq);
+ gg_pubdir50_free(req);
+
+ return 1;
+}
+
+//////////////////////////////////////////////////////////
+// when away message is requested
+void __cdecl gg_getawaymsgthread(GGPROTO *gg, void *hContact)
+{
+ DBVARIANT dbv;
+
+ SleepEx(100, FALSE);
+ if (!DBGetContactSettingString(hContact, "CList", GG_KEY_STATUSDESCR, &dbv))
+ {
+ ProtoBroadcastAck(GG_PROTO, hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE) 1, (LPARAM) dbv.pszVal);
+ gg_netlog(gg, "gg_getawaymsg(): Reading away msg \"%s\".", dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+ else
+ ProtoBroadcastAck(GG_PROTO, hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE) 1, (LPARAM) NULL);
+}
+HANDLE gg_getawaymsg(PROTO_INTERFACE *proto, HANDLE hContact)
+{
+ gg_forkthread((GGPROTO *)proto, gg_getawaymsgthread, hContact);
+
+ return (HANDLE)1;
+}
+
+//////////////////////////////////////////////////////////
+// when away message is being set
+int gg_setawaymsg(PROTO_INTERFACE *proto, int iStatus, const PROTOCHAR *msgt)
+{
+ GGPROTO *gg = (GGPROTO *)proto;
+ int status = gg_normalizestatus(iStatus);
+ char **szMsg;
+ char *msg = gg_t2a(msgt);
+
+ gg_netlog(gg, "gg_setawaymsg(): PS_SETAWAYMSG(%d, \"%s\").", iStatus, msg);
+
+ EnterCriticalSection(&gg->modemsg_mutex);
+ // Select proper msg
+ switch(status)
+ {
+ case ID_STATUS_ONLINE:
+ szMsg = &gg->modemsg.online;
+ break;
+ case ID_STATUS_AWAY:
+ szMsg = &gg->modemsg.away;
+ break;
+ case ID_STATUS_DND:
+ szMsg = &gg->modemsg.dnd;
+ break;
+ case ID_STATUS_FREECHAT:
+ szMsg = &gg->modemsg.freechat;
+ break;
+ case ID_STATUS_INVISIBLE:
+ szMsg = &gg->modemsg.invisible;
+ break;
+ default:
+ LeaveCriticalSection(&gg->modemsg_mutex);
+ mir_free(msg);
+ return 1;
+ }
+
+ // Check if we change status here somehow
+ if (*szMsg && msg && !strcmp(*szMsg, msg)
+ || !*szMsg && (!msg || !*msg))
+ {
+ if (status == gg->proto.m_iDesiredStatus && gg->proto.m_iDesiredStatus == gg->proto.m_iStatus)
+ {
+ gg_netlog(gg, "gg_setawaymsg(): Message hasn't been changed, return.");
+ LeaveCriticalSection(&gg->modemsg_mutex);
+ mir_free(msg);
+ return 0;
+ }
+ }
+ else
+ {
+ if (*szMsg)
+ mir_free(*szMsg);
+ *szMsg = msg && *msg ? mir_strdup(msg) : NULL;
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_setawaymsg(): Message changed.");
+#endif
+ }
+ LeaveCriticalSection(&gg->modemsg_mutex);
+
+ // Change the status if it was desired by PS_SETSTATUS
+ if (status == gg->proto.m_iDesiredStatus)
+ gg_refreshstatus(gg, status);
+
+ mir_free(msg);
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// visible lists
+int gg_setapparentmode(PROTO_INTERFACE *proto, HANDLE hContact, int mode)
+{
+ GGPROTO *gg = (GGPROTO *)proto;
+ DBWriteContactSettingWord(hContact, GG_PROTO, GG_KEY_APPARENT, (WORD)mode);
+ gg_notifyuser(gg, hContact, 1);
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// create adv search dialog proc
+INT_PTR CALLBACK gg_advancedsearchdlgproc(HWND hwndDlg,UINT message,WPARAM wParam,LPARAM lParam)
+{
+ switch(message)
+ {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)_T("")); // 0
+ SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)Translate("Female")); // 1
+ SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)Translate("Male")); // 2
+ return TRUE;
+ case WM_COMMAND:
+ switch(LOWORD(wParam)) {
+ case IDOK:
+ SendMessage(GetParent(hwndDlg), WM_COMMAND,MAKEWPARAM(IDOK,BN_CLICKED), (LPARAM)GetDlgItem(GetParent(hwndDlg),IDOK));
+ break;
+ case IDCANCEL:
+// CheckDlgButton(GetParent(hwndDlg),IDC_ADVANCED,BST_UNCHECKED);
+// SendMessage(GetParent(hwndDlg),WM_COMMAND,MAKEWPARAM(IDC_ADVANCED,BN_CLICKED),(LPARAM)GetDlgItem(GetParent(hwndDlg),IDC_ADVANCED));
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+//////////////////////////////////////////////////////////
+// create adv search dialog
+HWND gg_createadvsearchui(PROTO_INTERFACE *proto, HWND owner)
+{
+ return CreateDialogParam(hInstance,
+ MAKEINTRESOURCE(IDD_GGADVANCEDSEARCH), owner, gg_advancedsearchdlgproc, (LPARAM)(GGPROTO *)proto);
+}
+
+//////////////////////////////////////////////////////////
+// search by advanced
+HWND gg_searchbyadvanced(PROTO_INTERFACE *proto, HWND hwndDlg)
+{
+ GGPROTO *gg = (GGPROTO *)proto;
+ gg_pubdir50_t req;
+ char text[64], data[512] = "\0";
+ unsigned long crc;
+
+ // Check if connected
+ if(!gg_isonline(gg)) return (HWND)0;
+
+ if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH)))
+ {
+ gg_forkthread(gg, gg_searchthread, NULL);
+ return (HWND)1;
+ }
+
+ // Fetch search data
+ GetDlgItemText(hwndDlg, IDC_FIRSTNAME, text, sizeof(text));
+ if(strlen(text))
+ {
+ gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, text);
+ strncat(data, text, sizeof(data) - strlen(data));
+ }
+ /* 1 */ strncat(data, ".", sizeof(data) - strlen(data));
+
+ GetDlgItemText(hwndDlg, IDC_LASTNAME, text, sizeof(text));
+ if(strlen(text))
+ {
+ gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, text);
+ strncat(data, text, sizeof(data) - strlen(data));
+ }
+ /* 2 */ strncat(data, ".", sizeof(data) - strlen(data));
+
+ GetDlgItemText(hwndDlg, IDC_NICKNAME, text, sizeof(text));
+ if(strlen(text))
+ {
+ gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, text);
+ strncat(data, text, sizeof(data) - strlen(data));
+ }
+ /* 3 */ strncat(data, ".", sizeof(data) - strlen(data));
+
+ GetDlgItemText(hwndDlg, IDC_CITY, text, sizeof(text));
+ if(strlen(text))
+ {
+ gg_pubdir50_add(req, GG_PUBDIR50_CITY, text);
+ strncat(data, text, sizeof(data) - strlen(data));
+ }
+ /* 4 */ strncat(data, ".", sizeof(data) - strlen(data));
+
+ GetDlgItemText(hwndDlg, IDC_AGEFROM, text, sizeof(text));
+ if(strlen(text))
+ {
+ int yearTo = atoi(text);
+ int yearFrom;
+ time_t t = time(NULL);
+ struct tm *lt = localtime(&t);
+ int ay = lt->tm_year + 1900;
+ char age[16];
+
+ GetDlgItemText(hwndDlg, IDC_AGETO, age, sizeof(age));
+ yearFrom = atoi(age);
+
+ // Count & fix ranges
+ if(!yearTo)
+ yearTo = ay;
+ else
+ yearTo = ay - yearTo;
+ if(!yearFrom)
+ yearFrom = 0;
+ else
+ yearFrom = ay - yearFrom;
+ mir_snprintf(text, sizeof(text), "%d %d", yearFrom, yearTo);
+
+ gg_pubdir50_add(req, GG_PUBDIR50_BIRTHYEAR, text);
+ strncat(data, text, sizeof(data) - strlen(data));
+ }
+ /* 5 */ strncat(data, ".", sizeof(data) - strlen(data));
+
+ switch(SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_GETCURSEL, 0, 0))
+ {
+ case 1:
+ gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_FEMALE);
+ strncat(data, GG_PUBDIR50_GENDER_MALE, sizeof(data) - strlen(data));
+ break;
+ case 2:
+ gg_pubdir50_add(req, GG_PUBDIR50_GENDER, GG_PUBDIR50_GENDER_MALE);
+ strncat(data, GG_PUBDIR50_GENDER_FEMALE, sizeof(data) - strlen(data));
+ break;
+ }
+ /* 6 */ strncat(data, ".", sizeof(data) - strlen(data));
+
+ if(IsDlgButtonChecked(hwndDlg, IDC_ONLYCONNECTED))
+ {
+ gg_pubdir50_add(req, GG_PUBDIR50_ACTIVE, GG_PUBDIR50_ACTIVE_TRUE);
+ strncat(data, GG_PUBDIR50_ACTIVE_TRUE, sizeof(data) - strlen(data));
+ }
+ /* 7 */ strncat(data, ".", sizeof(data) - strlen(data));
+
+ // No data entered
+ if(strlen(data) <= 7 || (strlen(data) == 8 && IsDlgButtonChecked(hwndDlg, IDC_ONLYCONNECTED))) return (HWND)0;
+
+ // Count crc & check if the data was equal if yes do same search with shift
+ crc = crc_get(data);
+
+ if(crc == gg->last_crc && gg->next_uin)
+ gg_pubdir50_add(req, GG_PUBDIR50_START, ditoa(gg->next_uin));
+ else
+ gg->last_crc = crc;
+
+ gg_pubdir50_seq_set(req, GG_SEQ_SEARCH);
+
+ if(gg_isonline(gg))
+ {
+ EnterCriticalSection(&gg->sess_mutex);
+ if(!gg_pubdir50(gg->sess, req))
+ {
+ LeaveCriticalSection(&gg->sess_mutex);
+ gg_forkthread(gg, gg_searchthread, NULL);
+ return (HWND)1;
+ }
+ LeaveCriticalSection(&gg->sess_mutex);
+ }
+ gg_netlog(gg, "gg_searchbyadvanced(): Seq %d.", req->seq);
+ gg_pubdir50_free(req);
+
+ return (HWND)1;
+}
+
+//////////////////////////////////////////////////////////
+// gets avatar capabilities
+INT_PTR gg_getavatarcaps(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ switch (wParam) {
+ case AF_MAXSIZE:
+ ((POINT *)lParam)->x = ((POINT *)lParam)->y = 200;
+ return 0;
+ case AF_FORMATSUPPORTED:
+ return (lParam == PA_FORMAT_JPEG || lParam == PA_FORMAT_GIF || lParam == PA_FORMAT_PNG);
+ case AF_ENABLED:
+ return DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS);
+ case AF_DONTNEEDDELAYS:
+ return 1;
+ case AF_MAXFILESIZE:
+ return 307200;
+ case AF_FETCHALWAYS:
+ return 1;
+ }
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// gets avatar information
+INT_PTR gg_getavatarinfo(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ PROTO_AVATAR_INFORMATION *pai = (PROTO_AVATAR_INFORMATION *)lParam;
+ char *AvatarURL = NULL, *AvatarHash = NULL, *AvatarSavedHash = NULL;
+ INT_PTR result = GAIR_NOAVATAR;
+ DBVARIANT dbv;
+ uin_t uin = (uin_t)DBGetContactSettingDword(pai->hContact, GG_PROTO, GG_KEY_UIN, 0);
+
+ gg_netlog(gg, "gg_getavatarinfo(): Requesting avatar information for %d.", uin);
+
+ pai->filename[0] = 0;
+ pai->format = PA_FORMAT_UNKNOWN;
+
+ if (!uin || !DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS))
+ return GAIR_NOAVATAR;
+
+ if (!DBGetContactSettingByte(pai->hContact, GG_PROTO, GG_KEY_AVATARREQUESTED, GG_KEYDEF_AVATARREQUESTED)) {
+ gg_requestavatar(gg, pai->hContact, 1);
+ return (wParam & GAIF_FORCE) != 0 ? GAIR_WAITFOR : GAIR_NOAVATAR;
+ }
+ DBDeleteContactSetting(pai->hContact, GG_PROTO, GG_KEY_AVATARREQUESTED);
+
+ pai->format = DBGetContactSettingByte(pai->hContact, GG_PROTO, GG_KEY_AVATARTYPE, GG_KEYDEF_AVATARTYPE);
+
+ if (!DBGetContactSettingString(pai->hContact, GG_PROTO, GG_KEY_AVATARURL, &dbv)) {
+ AvatarURL = mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+
+ if (AvatarURL != NULL && strlen(AvatarURL) > 0) {
+ char *AvatarName = strrchr(AvatarURL, '/');
+ AvatarName++;
+ AvatarHash = gg_avatarhash(AvatarName);
+ }
+
+ if (!DBGetContactSettingString(pai->hContact, GG_PROTO, GG_KEY_AVATARHASH, &dbv)) {
+ AvatarSavedHash = mir_strdup(dbv.pszVal);
+ DBFreeVariant(&dbv);
+ }
+
+ if (AvatarHash != NULL && AvatarSavedHash != NULL) {
+ gg_getavatarfilename(gg, pai->hContact, pai->filename, sizeof(pai->filename));
+ if (!strcmp(AvatarHash, AvatarSavedHash) && !_access(pai->filename, 0)) {
+ result = GAIR_SUCCESS;
+ }
+ else if ((wParam & GAIF_FORCE) != 0) {
+ gg_netlog(gg, "gg_getavatarinfo(): Contact %d changed avatar.", uin);
+ remove(pai->filename);
+ DBWriteContactSettingString(pai->hContact, GG_PROTO, GG_KEY_AVATARHASH, AvatarHash);
+ gg_getavatar(gg, pai->hContact, AvatarURL);
+ result = GAIR_WAITFOR;
+ }
+ }
+ else if ((wParam & GAIF_FORCE) != 0) {
+ if (AvatarHash == NULL && AvatarSavedHash != NULL) {
+ gg_netlog(gg, "gg_getavatarinfo(): Contact %d deleted avatar.", uin);
+ gg_getavatarfilename(gg, pai->hContact, pai->filename, sizeof(pai->filename));
+ remove(pai->filename);
+ DBDeleteContactSetting(pai->hContact, GG_PROTO, GG_KEY_AVATARHASH);
+ DBDeleteContactSetting(pai->hContact, GG_PROTO, GG_KEY_AVATARURL);
+ DBDeleteContactSetting(pai->hContact, GG_PROTO, GG_KEY_AVATARTYPE);
+ }
+ else if (AvatarHash != NULL && AvatarSavedHash == NULL) {
+ gg_netlog(gg, "gg_getavatarinfo(): Contact %d set avatar.", uin);
+ DBWriteContactSettingString(pai->hContact, GG_PROTO, GG_KEY_AVATARHASH, AvatarHash);
+ gg_getavatar(gg, pai->hContact, AvatarURL);
+ result = GAIR_WAITFOR;
+ }
+ }
+
+ mir_free(AvatarHash);
+ mir_free(AvatarSavedHash);
+ mir_free(AvatarURL);
+
+ return result;
+}
+
+//////////////////////////////////////////////////////////
+// gets avatar
+INT_PTR gg_getmyavatar(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ char *szFilename = (char *)wParam;
+ int len = (int)lParam;
+
+ gg_netlog(gg, "gg_getmyavatar(): Requesting user avatar.");
+
+ if (szFilename == NULL || len <= 0)
+ return -1;
+
+ if (!DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS))
+ return -2;
+
+ gg_getavatarfilename(gg, NULL, szFilename, len);
+ return _access(szFilename, 0);
+}
+
+//////////////////////////////////////////////////////////
+// sets avatar
+INT_PTR gg_setmyavatar(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ char *szFilename = (char *)lParam;
+
+ if (!DBGetContactSettingByte(NULL, GG_PROTO, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS))
+ return -2;
+
+ if (szFilename == NULL) {
+ MessageBox(
+ NULL,
+ Translate("To remove your Gadu-Gadu avatar, you must use the MojaGeneracja.pl website."),
+ GG_PROTONAME, MB_OK | MB_ICONINFORMATION);
+ return -1;
+ }
+ else {
+ char szMyFilename[MAX_PATH];
+ gg_getavatarfilename(gg, NULL, szMyFilename, sizeof(szMyFilename));
+ if (strcmp(szFilename, szMyFilename) && !CopyFileA(szFilename, szMyFilename, FALSE)) {
+ gg_netlog(gg, "gg_setmyavatar(): Failed to set user avatar. File %s could not be created/overwritten.", szMyFilename);
+ return -1;
+ }
+ gg_setavatar(gg, szMyFilename);
+ }
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// gets protocol status message
+INT_PTR gg_getmyawaymsg(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ INT_PTR res = 0;
+ char *szMsg;
+
+ EnterCriticalSection(&gg->modemsg_mutex);
+ szMsg = gg_getstatusmsg(gg, wParam ? gg_normalizestatus(wParam) : gg->proto.m_iStatus);
+ if(gg_isonline(gg) && szMsg)
+ res = (lParam & SGMA_UNICODE) ? (INT_PTR)mir_a2u(szMsg) : (INT_PTR)mir_strdup(szMsg);
+ LeaveCriticalSection(&gg->modemsg_mutex);
+ return res;
+}
+
+//////////////////////////////////////////////////////////
+// gets account manager GUI
+extern INT_PTR CALLBACK gg_acc_mgr_guidlgproc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
+
+static INT_PTR gg_get_acc_mgr_gui(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ return (INT_PTR) CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_ACCMGRUI), (HWND)lParam, gg_acc_mgr_guidlgproc, (LPARAM) gg);
+}
+
+//////////////////////////////////////////////////////////
+// leaves (terminates) conference
+INT_PTR gg_leavechat(GGPROTO *gg, WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+ if(hContact)
+ CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0);
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// sends a notification that the user is typing a message
+int gg_useristyping(PROTO_INTERFACE *proto, HANDLE hContact, int type)
+{
+ GGPROTO *gg = (GGPROTO *)proto;
+ uin_t uin = DBGetContactSettingDword(hContact, GG_PROTO, GG_KEY_UIN, 0);
+
+ if (!uin || !gg_isonline(gg)) return 0;
+
+ if (type == PROTOTYPE_SELFTYPING_ON || type == PROTOTYPE_SELFTYPING_OFF) {
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_typing_notification(gg->sess, uin, (type == PROTOTYPE_SELFTYPING_ON));
+ LeaveCriticalSection(&gg->sess_mutex);
+ }
+
+ return 0;
+}
+
+//////////////////////////////////////////////////////////
+// Dummies for function that have to be implemented
+
+HANDLE gg_dummy_addtolistbyevent(PROTO_INTERFACE *proto, int flags, int iContact, HANDLE hDbEvent) { return NULL; }
+int gg_dummy_authorize(PROTO_INTERFACE *proto, HANDLE hContact) { return 0; }
+int gg_dummy_authdeny(PROTO_INTERFACE *proto, HANDLE hContact, const TCHAR *szReason) { return 0; }
+int gg_dummy_authrecv(PROTO_INTERFACE *proto, HANDLE hContact, PROTORECVEVENT *pre) { return 0; }
+int gg_dummy_authrequest(PROTO_INTERFACE *proto, HANDLE hContact, const TCHAR *szMessage) { return 0; }
+HANDLE gg_dummy_changeinfo(PROTO_INTERFACE *proto, int iInfoType, void *pInfoData) { return NULL; }
+int gg_dummy_fileresume(PROTO_INTERFACE *proto, HANDLE hTransfer, int *action, const PROTOCHAR** szFilename) { return 0; }
+HANDLE gg_dummy_searchbyemail(PROTO_INTERFACE *proto, const PROTOCHAR *email) { return NULL; }
+int gg_dummy_recvcontacts(PROTO_INTERFACE *proto, HANDLE hContact, PROTORECVEVENT *pre) { return 0; }
+int gg_dummy_recvurl(PROTO_INTERFACE *proto, HANDLE hContact, PROTORECVEVENT *pre) { return 0; }
+int gg_dummy_sendcontacts(PROTO_INTERFACE *proto, HANDLE hContact, int flags, int nContacts, HANDLE *hContactsList) { return 0; }
+int gg_dummy_sendurl(PROTO_INTERFACE *proto, HANDLE hContact, int flags, const char *url) { return 0; }
+int gg_dummy_recvawaymsg(PROTO_INTERFACE *proto, HANDLE hContact, int mode, PROTORECVEVENT *evt) { return 0; }
+int gg_dummy_sendawaymsg(PROTO_INTERFACE *proto, HANDLE hContact, HANDLE hProcess, const char *msg) { return 0; }
+
+//////////////////////////////////////////////////////////
+// Register services
+void gg_registerservices(GGPROTO *gg)
+{
+ gg->proto.vtbl->AddToList = gg_addtolist;
+ gg->proto.vtbl->AddToListByEvent = gg_dummy_addtolistbyevent;
+
+ gg->proto.vtbl->Authorize = gg_dummy_authorize;
+ gg->proto.vtbl->AuthDeny = gg_dummy_authdeny;
+ gg->proto.vtbl->AuthRecv = gg_dummy_authrecv;
+ gg->proto.vtbl->AuthRequest = gg_dummy_authrequest;
+
+ gg->proto.vtbl->ChangeInfo = gg_dummy_changeinfo;
+
+ gg->proto.vtbl->FileAllow = gg_fileallow;
+ gg->proto.vtbl->FileCancel = gg_filecancel;
+ gg->proto.vtbl->FileDeny = gg_filedeny;
+ gg->proto.vtbl->FileResume = gg_dummy_fileresume;
+
+ gg->proto.vtbl->GetCaps = gg_getcaps;
+ gg->proto.vtbl->GetIcon = gg_geticon;
+ gg->proto.vtbl->GetInfo = gg_getinfo;
+
+ gg->proto.vtbl->SearchBasic = gg_basicsearch;
+ gg->proto.vtbl->SearchByEmail = gg_dummy_searchbyemail;
+ gg->proto.vtbl->SearchByName = gg_searchbydetails;
+ gg->proto.vtbl->SearchAdvanced = gg_searchbyadvanced;
+ gg->proto.vtbl->CreateExtendedSearchUI = gg_createadvsearchui;
+
+ gg->proto.vtbl->RecvContacts = gg_dummy_recvcontacts;
+ gg->proto.vtbl->RecvFile = gg_recvfile;
+ gg->proto.vtbl->RecvMsg = gg_recvmessage;
+ gg->proto.vtbl->RecvUrl = gg_dummy_recvurl;
+
+ gg->proto.vtbl->SendContacts = gg_dummy_sendcontacts;
+ gg->proto.vtbl->SendFile = gg_sendfile;
+ gg->proto.vtbl->SendMsg = gg_sendmessage;
+ gg->proto.vtbl->SendUrl = gg_dummy_sendurl;
+
+ gg->proto.vtbl->SetApparentMode = gg_setapparentmode;
+ gg->proto.vtbl->SetStatus = gg_setstatus;
+
+ gg->proto.vtbl->GetAwayMsg = gg_getawaymsg;
+ gg->proto.vtbl->RecvAwayMsg = gg_dummy_recvawaymsg;
+ gg->proto.vtbl->SendAwayMsg = gg_dummy_sendawaymsg;
+ gg->proto.vtbl->SetAwayMsg = gg_setawaymsg;
+
+ gg->proto.vtbl->UserIsTyping = gg_useristyping;
+
+ gg->proto.vtbl->OnEvent = gg_event;
+
+ CreateProtoService(PS_GETAVATARCAPS, gg_getavatarcaps, gg);
+ CreateProtoService(PS_GETAVATARINFO, gg_getavatarinfo, gg);
+ CreateProtoService(PS_GETMYAVATAR, gg_getmyavatar, gg);
+ CreateProtoService(PS_SETMYAVATAR, gg_setmyavatar, gg);
+
+ CreateProtoService(PS_GETMYAWAYMSG, gg_getmyawaymsg, gg);
+ CreateProtoService(PS_CREATEACCMGRUI, gg_get_acc_mgr_gui, gg);
+
+ CreateProtoService(PS_LEAVECHAT, gg_leavechat, gg);
+}
diff --git a/protocols/Gadu-Gadu/sessions.c b/protocols/Gadu-Gadu/sessions.c
new file mode 100644
index 0000000000..39348fef61
--- /dev/null
+++ b/protocols/Gadu-Gadu/sessions.c
@@ -0,0 +1,445 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2009-2012 Bartosz Białek
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+
+#define GGS_CONCUR_SESS "%s/ConcurSess"
+
+static void gg_clearsessionslist(HWND hwndDlg)
+{
+ HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS);
+ LV_COLUMN column = {0};
+ RECT rc;
+ int iWidth;
+
+ if (!hList) return;
+
+ ListView_DeleteAllItems(hList);
+ while (ListView_DeleteColumn(hList, 0));
+
+ column.mask = LVCF_TEXT;
+ column.cx = 500;
+ column.pszText = TranslateT("Client Name");
+ ListView_InsertColumn(hList, 1, &column);
+
+ column.pszText=TranslateT("IP Address");
+ ListView_InsertColumn(hList, 2, &column);
+
+ column.pszText = TranslateT("Login Time");
+ ListView_InsertColumn(hList, 3, &column);
+
+ column.pszText = TranslateT("Action");
+ ListView_InsertColumn(hList, 4, &column);
+
+ GetClientRect(hList, &rc);
+ iWidth = rc.right - rc.left;
+ ListView_SetColumnWidth(hList, 0, iWidth * 45 / 100);
+ ListView_SetColumnWidth(hList, 1, iWidth * 20 / 100);
+ ListView_SetColumnWidth(hList, 2, iWidth * 20 / 100);
+ ListView_SetColumnWidth(hList, 3, LVSCW_AUTOSIZE_USEHEADER);
+}
+
+static void ListView_SetItemTextA(HWND hwndLV, int i, int iSubItem, char* pszText)
+{
+ LV_ITEMA _ms_lvi;
+ _ms_lvi.iSubItem = iSubItem;
+ _ms_lvi.pszText = pszText;
+ SendMessageA(hwndLV, LVM_SETITEMTEXTA, i, (LPARAM)&_ms_lvi);
+}
+
+static int gg_insertlistitem(HWND hList, gg_multilogon_id_t* id, const char* clientName, const char* ip, const char* loginTime)
+{
+ LVITEM item = {0};
+ int index;
+
+ item.iItem = ListView_GetItemCount(hList);
+ item.mask = LVIF_PARAM;
+ item.lParam = (LPARAM)id;
+
+ index = ListView_InsertItem(hList, &item);
+ if (index < 0) return index;
+
+ ListView_SetItemTextA(hList, index, 0, (char*)clientName);
+ ListView_SetItemTextA(hList, index, 1, (char*)ip);
+ ListView_SetItemTextA(hList, index, 2, (char*)loginTime);
+ ListView_SetItemText(hList, index, 3, TranslateT("sign out"));
+
+ return index;
+}
+
+static void gg_listsessions(GGPROTO* gg, HWND hwndDlg)
+{
+ HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS);
+ list_t l;
+
+ if (!hList) return;
+
+ EnterCriticalSection(&gg->sessions_mutex);
+ for (l = gg->sessions; l; l = l->next)
+ {
+ struct gg_multilogon_session* sess = (struct gg_multilogon_session*)l->data;
+ struct in_addr ia;
+ char* ip;
+ char loginTime[20];
+ ia.S_un.S_addr = sess->remote_addr;
+ ip = inet_ntoa(ia);
+ strftime(loginTime, sizeof(loginTime), "%d-%m-%Y %H:%M:%S", localtime(&sess->logon_time));
+ gg_insertlistitem(hList, &sess->id, sess->name, ip, loginTime);
+ }
+ LeaveCriticalSection(&gg->sessions_mutex);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SIGNOUTALL), ListView_GetItemCount(hList) > 0);
+}
+
+static int sttSessionsDlgResizer(HWND hwndDlg, LPARAM lParam, UTILRESIZECONTROL* urc)
+{
+ switch (urc->wId)
+ {
+ case IDC_HEADERBAR:
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH;
+ case IDC_SESSIONS:
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORY_HEIGHT | RD_ANCHORX_WIDTH;
+ case IDC_SIGNOUTALL:
+ return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+static BOOL IsOverAction(HWND hwndDlg)
+{
+ HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS);
+ LVHITTESTINFO hti;
+ RECT rc;
+ HDC hdc;
+ TCHAR szText[256];
+ SIZE textSize;
+ int textPosX;
+
+ GetCursorPos(&hti.pt);
+ ScreenToClient(hList, &hti.pt);
+ GetClientRect(hList, &rc);
+ if (!PtInRect(&rc, hti.pt) || ListView_SubItemHitTest(hList, &hti) == -1
+ || hti.iSubItem != 3 || !(hti.flags & LVHT_ONITEMLABEL))
+ return FALSE;
+
+ ListView_GetSubItemRect(hList, hti.iItem, hti.iSubItem, LVIR_LABEL, &rc);
+ szText[0] = 0;
+ ListView_GetItemText(hList, hti.iItem, hti.iSubItem, szText, SIZEOF(szText));
+ hdc = GetDC(hList);
+ GetTextExtentPoint32(hdc, szText, lstrlen(szText), &textSize);
+ ReleaseDC(hList, hdc);
+ textPosX = rc.left + (((rc.right - rc.left) - textSize.cx) / 2);
+ return (hti.pt.x > textPosX && hti.pt.x < textPosX + textSize.cx);
+}
+
+static HCURSOR hHandCursor = NULL;
+#define WM_MULTILOGONINFO (WM_USER + 10)
+#define HM_PROTOACK (WM_USER + 11)
+
+static INT_PTR CALLBACK gg_sessions_viewdlg(HWND hwndDlg, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ GGPROTO* gg = (GGPROTO*)GetWindowLongPtr(hwndDlg, DWLP_USER);
+ switch (message)
+ {
+ case WM_INITDIALOG:
+ {
+ GGPROTO* gg = (GGPROTO*)lParam;
+ TCHAR oldTitle[256], newTitle[256];
+ HANDLE hProtoAckEvent;
+
+ SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)lParam);
+ TranslateDialogDefault(hwndDlg);
+ GetDlgItemText(hwndDlg, IDC_HEADERBAR, oldTitle, SIZEOF(oldTitle));
+ mir_snprintf(newTitle, SIZEOF(newTitle), oldTitle, GG_PROTONAME);
+ SetDlgItemText(hwndDlg, IDC_HEADERBAR, newTitle);
+ WindowSetIcon(hwndDlg, "sessions");
+ gg->hwndSessionsDlg = hwndDlg;
+
+ if (hHandCursor == NULL)
+ hHandCursor = LoadCursor(NULL, IDC_HAND);
+ hProtoAckEvent = HookEventMessage(ME_PROTO_ACK, hwndDlg, HM_PROTOACK);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)hProtoAckEvent);
+
+ ListView_SetExtendedListViewStyle(GetDlgItem(hwndDlg, IDC_SESSIONS), LVS_EX_FULLROWSELECT);
+ SendMessage(hwndDlg, WM_MULTILOGONINFO, 0, 0);
+ return TRUE;
+ }
+
+ case HM_PROTOACK:
+ {
+ ACKDATA* ack = (ACKDATA*)lParam;
+ if (!strcmp(ack->szModule, GG_PROTO) && !ack->hContact && ack->type == ACKTYPE_STATUS
+ && ack->result == ACKRESULT_SUCCESS && (ack->lParam == ID_STATUS_OFFLINE
+ || (ack->hProcess == (HANDLE)ID_STATUS_CONNECTING && ack->lParam != ID_STATUS_OFFLINE
+ && !ListView_GetItemCount(GetDlgItem(hwndDlg, IDC_SESSIONS)))))
+ {
+ gg_clearsessionslist(hwndDlg);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SIGNOUTALL), FALSE);
+ }
+ break;
+ }
+
+ case WM_MULTILOGONINFO:
+ gg_clearsessionslist(hwndDlg);
+ gg_listsessions(gg, hwndDlg);
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_SIGNOUTALL:
+ {
+ HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS);
+ LVITEM lvi = {0};
+ int iCount = ListView_GetItemCount(hList), i;
+ lvi.mask = LVIF_PARAM;
+ for (i = 0; i < iCount; i++)
+ {
+ lvi.iItem = i;
+ ListView_GetItem(hList, &lvi);
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_multilogon_disconnect(gg->sess, *((gg_multilogon_id_t*)lvi.lParam));
+ LeaveCriticalSection(&gg->sess_mutex);
+ }
+ break;
+ }
+ }
+ break;
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->idFrom == IDC_SESSIONS)
+ {
+ switch (((LPNMHDR)lParam)->code)
+ {
+ case NM_CUSTOMDRAW:
+ {
+ LPNMLVCUSTOMDRAW nm = (LPNMLVCUSTOMDRAW)lParam;
+ switch (nm->nmcd.dwDrawStage)
+ {
+ case CDDS_PREPAINT:
+ if (ListView_GetItemCount(nm->nmcd.hdr.hwndFrom) == 0)
+ {
+ const LPCTSTR szText = gg_isonline(gg)
+ ? TranslateT("There are no active concurrent sessions for this account.")
+ : TranslateT("You have to be logged in to view concurrent sessions.");
+ RECT rc;
+ HWND hwndHeader = ListView_GetHeader(nm->nmcd.hdr.hwndFrom);
+ SIZE textSize;
+ int textPosX;
+ GetClientRect(nm->nmcd.hdr.hwndFrom, &rc);
+ if (hwndHeader != NULL)
+ {
+ RECT rcHeader;
+ GetClientRect(hwndHeader, &rcHeader);
+ rc.top += rcHeader.bottom;
+ }
+ GetTextExtentPoint32(nm->nmcd.hdc, szText, lstrlen(szText), &textSize);
+ textPosX = rc.left + (((rc.right - rc.left) - textSize.cx) / 2);
+ ExtTextOut(nm->nmcd.hdc, textPosX, rc.top + textSize.cy, ETO_OPAQUE, &rc, szText, lstrlen(szText), NULL);
+ }
+ // FALL THROUGH
+
+ case CDDS_ITEMPREPAINT:
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT , CDRF_NOTIFYSUBITEMDRAW);
+ return TRUE;
+
+ case CDDS_SUBITEM | CDDS_ITEMPREPAINT:
+ {
+ RECT rc;
+ ListView_GetSubItemRect(nm->nmcd.hdr.hwndFrom, nm->nmcd.dwItemSpec, nm->iSubItem, LVIR_LABEL, &rc);
+ if (nm->nmcd.hdr.idFrom == IDC_SESSIONS && nm->iSubItem == 3)
+ {
+ TCHAR szText[256];
+ szText[0] = 0;
+ ListView_GetItemText(nm->nmcd.hdr.hwndFrom, nm->nmcd.dwItemSpec, nm->iSubItem, szText, SIZEOF(szText));
+ FillRect(nm->nmcd.hdc, &rc, GetSysColorBrush(COLOR_WINDOW));
+ SetTextColor(nm->nmcd.hdc, RGB(0, 0, 255));
+ DrawText(nm->nmcd.hdc, szText, -1, &rc, DT_END_ELLIPSIS | DT_CENTER | DT_NOPREFIX | DT_SINGLELINE | DT_TOP);
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, CDRF_SKIPDEFAULT);
+ return TRUE;
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ case NM_CLICK:
+ if (IsOverAction(hwndDlg))
+ {
+ LPNMITEMACTIVATE nm = (LPNMITEMACTIVATE)lParam;
+ LVITEM lvi = {0};
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = nm->iItem;
+ ListView_GetItem(nm->hdr.hwndFrom, &lvi);
+ EnterCriticalSection(&gg->sess_mutex);
+ gg_multilogon_disconnect(gg->sess, *((gg_multilogon_id_t*)lvi.lParam));
+ LeaveCriticalSection(&gg->sess_mutex);
+ }
+ break;
+ }
+ }
+ break;
+
+ case WM_CONTEXTMENU:
+ {
+ HWND hList = GetDlgItem(hwndDlg, IDC_SESSIONS);
+ POINT pt = {(short)LOWORD(lParam), (short)HIWORD(lParam)}, ptDlg = pt;
+ LVHITTESTINFO lvhti = {0};
+
+ ScreenToClient(hwndDlg, &ptDlg);
+ if (ChildWindowFromPoint(hwndDlg, ptDlg) == hList)
+ {
+ HMENU hMenu;
+ int iSelection;
+
+ lvhti.pt = pt;
+ ScreenToClient(hList, &lvhti.pt);
+ if (ListView_HitTest(hList, &lvhti) == -1) break;
+
+ hMenu = CreatePopupMenu();
+ AppendMenu(hMenu, MFT_STRING, 10001, TranslateT("Copy Text"));
+ AppendMenu(hMenu, MFT_STRING, 10002, TranslateT("Whois"));
+ iSelection = TrackPopupMenu(hMenu, TPM_RIGHTBUTTON | TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL);
+ switch (iSelection)
+ {
+ case 10001:
+ {
+ char szText[512], szClientName[256], szIP[64], szLoginTime[64];
+ HGLOBAL hData;
+ if (!OpenClipboard(hwndDlg)) break;
+ EmptyClipboard();
+ szClientName[0] = szIP[0] = szLoginTime[0] = 0;
+ ListView_GetItemText(hList, lvhti.iItem, 0, szClientName, SIZEOF(szClientName));
+ ListView_GetItemText(hList, lvhti.iItem, 1, szIP, SIZEOF(szIP));
+ ListView_GetItemText(hList, lvhti.iItem, 2, szLoginTime, SIZEOF(szLoginTime));
+ mir_snprintf(szText, SIZEOF(szText), "%s\t%s\t%s", szClientName, szIP, szLoginTime);
+ if ((hData = GlobalAlloc(GMEM_MOVEABLE, lstrlenA(szText) + 1)) != NULL)
+ {
+ lstrcpyA((char*)GlobalLock(hData), szText);
+ GlobalUnlock(hData);
+ SetClipboardData(CF_TEXT, hData);
+ }
+ CloseClipboard();
+ break;
+ }
+
+ case 10002:
+ {
+ char szUrl[256], szIP[64];
+ szIP[0] = 0;
+ ListView_GetItemText(hList, lvhti.iItem, 1, szIP, SIZEOF(szIP));
+ mir_snprintf(szUrl, SIZEOF(szUrl), "http://whois.domaintools.com/%s", szIP);
+ CallService(MS_UTILS_OPENURL, 1, (LPARAM)szUrl);
+ break;
+ }
+ }
+ DestroyMenu(hMenu);
+ }
+ break;
+ }
+
+ case WM_GETMINMAXINFO:
+ ((LPMINMAXINFO)lParam)->ptMinTrackSize.x = 620;
+ ((LPMINMAXINFO)lParam)->ptMinTrackSize.y = 220;
+ return 0;
+
+ case WM_SIZE:
+ {
+ UTILRESIZEDIALOG urd = {0};
+ urd.cbSize = sizeof(urd);
+ urd.hInstance = hInstance;
+ urd.hwndDlg = hwndDlg;
+ urd.lpTemplate = MAKEINTRESOURCEA(IDD_SESSIONS);
+ urd.pfnResizer = sttSessionsDlgResizer;
+ CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd);
+ return 0;
+ }
+
+ case WM_SETCURSOR:
+ if (LOWORD(lParam) == HTCLIENT && IsOverAction(hwndDlg))
+ {
+ SetCursor(hHandCursor);
+ SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE);
+ return TRUE;
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ {
+ HANDLE hProtoAckEvent = (HANDLE)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ if (hProtoAckEvent) UnhookEvent(hProtoAckEvent);
+ gg->hwndSessionsDlg = NULL;
+ WindowFreeIcon(hwndDlg);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+INT_PTR gg_sessions_view(GGPROTO* gg, WPARAM wParam, LPARAM lParam)
+{
+ if (gg->hwndSessionsDlg && IsWindow(gg->hwndSessionsDlg))
+ {
+ ShowWindow(gg->hwndSessionsDlg, SW_SHOWNORMAL);
+ SetForegroundWindow(gg->hwndSessionsDlg);
+ SetFocus(gg->hwndSessionsDlg);
+ }
+ else
+ CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_SESSIONS), NULL, gg_sessions_viewdlg, (LPARAM)gg);
+ return 0;
+}
+
+void gg_sessions_updatedlg(GGPROTO* gg)
+{
+ if (gg->hwndSessionsDlg && IsWindow(gg->hwndSessionsDlg))
+ SendMessage(gg->hwndSessionsDlg, WM_MULTILOGONINFO, 0, 0);
+}
+
+BOOL gg_sessions_closedlg(GGPROTO* gg)
+{
+ if (gg->hwndSessionsDlg && IsWindow(gg->hwndSessionsDlg))
+ return PostMessage(gg->hwndSessionsDlg, WM_CLOSE, 0, 0);
+ return FALSE;
+}
+
+void gg_sessions_menus_init(GGPROTO* gg, HGENMENU hRoot)
+{
+ CLISTMENUITEM mi = {0};
+ char service[64];
+
+ mi.cbSize = sizeof(mi);
+ mi.flags = CMIF_ICONFROMICOLIB | CMIF_ROOTHANDLE | CMIF_TCHAR;
+ mi.hParentMenu = hRoot;
+
+ mir_snprintf(service, sizeof(service), GGS_CONCUR_SESS, GG_PROTO);
+ CreateProtoServiceFunction(service, gg_sessions_view, gg);
+ if (gg->hMenuRoot)
+ mi.position = 2050000001;
+ else
+ mi.position = 200003;
+ mi.icolibItem = GetIconHandle(IDI_SESSIONS);
+ mi.ptszName = LPGENT("Concurrent &sessions");
+ mi.pszService = service;
+ CallService(MS_CLIST_ADDPROTOMENUITEM, 0, (LPARAM)&mi);
+}
diff --git a/protocols/Gadu-Gadu/token.c b/protocols/Gadu-Gadu/token.c
new file mode 100644
index 0000000000..44bfb1f794
--- /dev/null
+++ b/protocols/Gadu-Gadu/token.c
@@ -0,0 +1,178 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2006 Adam Strzelecki <ono+miranda@java.pl>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+
+#define MAX_LOADSTRING 100
+#define HIMETRIC_INCH 2540
+#define MAP_LOGHIM_TO_PIX(x,ppli) ( ((ppli)*(x) + HIMETRIC_INCH/2) / HIMETRIC_INCH )
+
+////////////////////////////////////////////////////////////////////////////////
+// User Util Dlg Page : Data
+
+typedef struct _GGTOKENDLGDATA
+{
+ int width;
+ int height;
+ char id[256];
+ char val[256];
+ HBITMAP hBitmap;
+} GGTOKENDLGDATA;
+
+INT_PTR CALLBACK gg_tokendlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ GGTOKENDLGDATA *dat = (GGTOKENDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch(msg)
+ {
+ case WM_INITDIALOG:
+ {
+ RECT rc;
+ TranslateDialogDefault(hwndDlg);
+ GetClientRect(GetDlgItem(hwndDlg, IDC_WHITERECT), &rc);
+ InvalidateRect(hwndDlg, &rc, TRUE);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam);
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case IDOK:
+ {
+ GetDlgItemText(hwndDlg, IDC_TOKEN, dat->val, sizeof(dat->val));
+ EndDialog(hwndDlg, IDOK);
+ break;
+ }
+ case IDCANCEL:
+ EndDialog(hwndDlg, IDCANCEL);
+ break;
+ }
+ break;
+
+ case WM_PAINT:
+ {
+ PAINTSTRUCT paintStruct;
+ HDC hdc = BeginPaint(hwndDlg, &paintStruct);
+ RECT rc; GetClientRect(GetDlgItem(hwndDlg, IDC_WHITERECT), &rc);
+ FillRect(hdc, &rc, (HBRUSH)GetStockObject(WHITE_BRUSH));
+
+ if(dat && dat->hBitmap)
+ {
+ HDC hdcBmp = NULL;
+ int nWidth, nHeight;
+ BITMAP bmp;
+
+ GetObject(dat->hBitmap, sizeof(bmp), &bmp);
+ nWidth = bmp.bmWidth; nHeight = bmp.bmHeight;
+
+ if(hdcBmp = CreateCompatibleDC(hdc))
+ {
+ SelectObject(hdcBmp, dat->hBitmap);
+ SetStretchBltMode(hdc, HALFTONE);
+ BitBlt(hdc,
+ (rc.left + rc.right - nWidth) / 2,
+ (rc.top + rc.bottom - nHeight) / 2,
+ nWidth, nHeight,
+ hdcBmp, 0, 0, SRCCOPY);
+ DeleteDC(hdcBmp);
+ }
+ }
+ EndPaint(hwndDlg, &paintStruct);
+ return 0;
+ }
+ }
+ return FALSE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Gets GG token
+int gg_gettoken(GGPROTO *gg, GGTOKEN *token)
+{
+ struct gg_http *h = NULL;
+ struct gg_token *t = NULL;
+ IMGSRVC_MEMIO memio = {0};
+ GGTOKENDLGDATA dat = {0};
+
+ // Zero tokens
+ strcpy(token->id, "");
+ strcpy(token->val, "");
+
+ if(!(h = gg_token(0)) || gg_token_watch_fd(h) || h->state == GG_STATE_ERROR || h->state != GG_STATE_DONE)
+ {
+ char error[128];
+ mir_snprintf(error, sizeof(error), Translate("Token retrieval failed because of error:\n\t%s"), http_error_string(h ? h->error : 0));
+ MessageBox(
+ NULL,
+ error,
+ GG_PROTONAME,
+ MB_OK | MB_ICONSTOP
+ );
+ gg_free_pubdir(h);
+ return FALSE;
+ }
+
+ if (!(t = (struct gg_token *)h->data) || (!h->body))
+ {
+ char error[128];
+ mir_snprintf(error, sizeof(error), Translate("Token retrieval failed because of error:\n\t%s"), http_error_string(h ? h->error : 0));
+ MessageBox(
+ NULL,
+ error,
+ GG_PROTONAME,
+ MB_OK | MB_ICONSTOP
+ );
+ gg_free_pubdir(h);
+ return FALSE;
+ }
+
+ // Return token id
+ strncpy(dat.id, t->tokenid, sizeof(dat.id));
+ dat.width = t->width;
+ dat.height = t->height;
+
+ // Load bitmap
+ memio.iLen = h->body_size;
+ memio.pBuf = (void *)h->body;
+ memio.fif = -1; /* detect */
+ memio.flags = 0;
+ dat.hBitmap = (HBITMAP) CallService(MS_IMG_LOADFROMMEM, (WPARAM) &memio, 0);
+ if(dat.hBitmap == NULL)
+ {
+ MessageBox(
+ NULL,
+ Translate("Could not load token image."),
+ GG_PROTONAME,
+ MB_OK | MB_ICONSTOP
+ );
+ gg_free_pubdir(h);
+ return FALSE;
+ }
+
+ // Load token dialog
+ if(DialogBoxParam(hInstance, MAKEINTRESOURCE(IDD_TOKEN), NULL, gg_tokendlgproc, (LPARAM)&dat) == IDCANCEL)
+ return FALSE;
+
+ // Fillup patterns
+ strncpy(token->id, dat.id, sizeof(token->id));
+ strncpy(token->val, dat.val, sizeof(token->val));
+
+ return TRUE;
+}
diff --git a/protocols/Gadu-Gadu/userutils.c b/protocols/Gadu-Gadu/userutils.c
new file mode 100644
index 0000000000..1217488c93
--- /dev/null
+++ b/protocols/Gadu-Gadu/userutils.c
@@ -0,0 +1,307 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2003-2006 Adam Strzelecki <ono+miranda@java.pl>
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "gg.h"
+
+////////////////////////////////////////////////////////////////////////////////
+// Create New Account : Proc
+void *gg_doregister(GGPROTO *gg, char *newPass, char *newEmail)
+{
+ // Connection handles
+ struct gg_http *h = NULL;
+ struct gg_pubdir *s = NULL;
+ GGTOKEN token;
+
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_doregister(): Starting.");
+#endif
+ if(!newPass || !newEmail) return NULL;
+
+ // Load token
+ if(!gg_gettoken(gg, &token)) return NULL;
+
+ if (!(h = gg_register3(newEmail, newPass, token.id, token.val, 0)) || !(s = h->data) || !s->success || !s->uin)
+ {
+ char error[128];
+ mir_snprintf(error, sizeof(error), Translate("Cannot register new account because of error:\n\t%s"),
+ (h && !s) ? http_error_string(h ? h->error : 0) :
+ (s ? Translate("Registration rejected") : strerror(errno)));
+ MessageBox(
+ NULL,
+ error,
+ GG_PROTONAME,
+ MB_OK | MB_ICONSTOP
+ );
+ gg_netlog(gg, "gg_doregister(): Cannot register because of \"%s\".", strerror(errno));
+ }
+ else
+ {
+ DBWriteContactSettingDword(NULL, GG_PROTO, GG_KEY_UIN, s->uin);
+ CallService(MS_DB_CRYPT_ENCODESTRING, strlen(newPass) + 1, (LPARAM) newPass);
+ gg_checknewuser(gg, s->uin, newPass);
+ DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, newPass);
+ DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, newEmail);
+ gg_pubdir_free(h);
+ gg_netlog(gg, "gg_doregister(): Account registration succesful.");
+ MessageBox(
+ NULL,
+ Translate("You have registered new account.\nPlease fill up your personal details in \"M->View/Change My Details...\""),
+ GG_PROTONAME,
+ MB_OK | MB_ICONINFORMATION
+ );
+ }
+
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_doregister(): End.");
+#endif
+
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Remove Account : Proc
+void *gg_dounregister(GGPROTO *gg, uin_t uin, char *password)
+{
+ // Connection handles
+ struct gg_http *h;
+ struct gg_pubdir *s;
+ GGTOKEN token;
+
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_dounregister(): Starting.");
+#endif
+ if(!uin || !password) return NULL;
+
+ // Load token
+ if(!gg_gettoken(gg, &token)) return NULL;
+
+ if (!(h = gg_unregister3(uin, password, token.id, token.val, 0)) || !(s = h->data) || !s->success || s->uin != uin)
+ {
+ char error[128];
+ mir_snprintf(error, sizeof(error), Translate("Your account cannot be removed because of error:\n\t%s"),
+ (h && !s) ? http_error_string(h ? h->error : 0) :
+ (s ? Translate("Bad number or password") : strerror(errno)));
+ MessageBox(
+ NULL,
+ error,
+ GG_PROTONAME,
+ MB_OK | MB_ICONSTOP
+ );
+ gg_netlog(gg, "gg_dounregister(): Cannot remove account because of \"%s\".", strerror(errno));
+ }
+ else
+ {
+ gg_pubdir_free(h);
+ DBDeleteContactSetting(NULL, GG_PROTO, GG_KEY_PASSWORD);
+ DBDeleteContactSetting(NULL, GG_PROTO, GG_KEY_UIN);
+ gg_netlog(gg, "gg_dounregister(): Account %d has been removed.", uin);
+ MessageBox(
+ NULL,
+ Translate("Your account has been removed."),
+ GG_PROTONAME,
+ MB_OK | MB_ICONINFORMATION
+ );
+ }
+
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_dounregister(): End.");
+#endif
+
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Change Password Page : Proc
+void *gg_dochpass(GGPROTO *gg, uin_t uin, char *password, char *newPass)
+{
+ // Readup email
+ char email[255] = "\0"; DBVARIANT dbv_email;
+ // Connection handles
+ struct gg_http *h;
+ struct gg_pubdir *s;
+ GGTOKEN token;
+
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_dochpass(): Starting.");
+#endif
+ if(!uin || !password || !newPass) return NULL;
+
+ if (!DBGetContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, &dbv_email))
+ {
+ strncpy(email, dbv_email.pszVal, sizeof(email));
+ DBFreeVariant(&dbv_email);
+ }
+
+ // Load token
+ if(!gg_gettoken(gg, &token)) return NULL;
+
+ if (!(h = gg_change_passwd4(uin, email, password, newPass, token.id, token.val, 0)) || !(s = h->data) || !s->success)
+ {
+ char error[128];
+ mir_snprintf(error, sizeof(error), Translate("Your password cannot be changed because of error:\n\t%s"),
+ (h && !s) ? http_error_string(h ? h->error : 0) :
+ (s ? Translate("Invalid data entered") : strerror(errno)));
+ MessageBox(
+ NULL,
+ error,
+ GG_PROTONAME,
+ MB_OK | MB_ICONSTOP
+ );
+ gg_netlog(gg, "gg_dochpass(): Cannot change password because of \"%s\".", strerror(errno));
+ }
+ else
+ {
+ gg_pubdir_free(h);
+ CallService(MS_DB_CRYPT_ENCODESTRING, strlen(newPass) + 1, (LPARAM) newPass);
+ DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_PASSWORD, newPass);
+ gg_netlog(gg, "gg_dochpass(): Password change succesful.");
+ MessageBox(
+ NULL,
+ Translate("Your password has been changed."),
+ GG_PROTONAME,
+ MB_OK | MB_ICONINFORMATION
+ );
+ }
+
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_dochpass(): End.");
+#endif
+
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Change E-mail Page : Proc
+void *gg_dochemail(GGPROTO *gg, uin_t uin, char *password, char *email, char *newEmail)
+{
+ // Connection handles
+ struct gg_http *h;
+ struct gg_pubdir *s;
+ GGTOKEN token;
+
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_doemail(): Starting.");
+#endif
+ if(!uin || !email || !newEmail) return NULL;
+
+ // Load token
+ if(!gg_gettoken(gg, &token)) return NULL;
+
+ if (!(h = gg_change_passwd4(uin, newEmail, password, password, token.id, token.val, 0)) || !(s = h->data) || !s->success)
+ {
+ char error[128];
+ mir_snprintf(error, sizeof(error), Translate("Your e-mail cannot be changed because of error:\n\t%s"),
+ (h && !s) ? http_error_string(h ? h->error : 0) :
+ (s ? Translate("Bad old e-mail or password") : strerror(errno)));
+ MessageBox(
+ NULL,
+ error,
+ GG_PROTONAME,
+ MB_OK | MB_ICONSTOP
+ );
+ gg_netlog(gg, "gg_dochpass(): Cannot change e-mail because of \"%s\".", strerror(errno));
+ }
+ else
+ {
+ gg_pubdir_free(h);
+ DBWriteContactSettingString(NULL, GG_PROTO, GG_KEY_EMAIL, newEmail);
+ gg_netlog(gg, "gg_doemail(): E-mail change succesful.");
+ MessageBox(
+ NULL,
+ Translate("Your e-mail has been changed."),
+ GG_PROTONAME,
+ MB_OK | MB_ICONINFORMATION
+ );
+ }
+
+#ifdef DEBUGMODE
+ gg_netlog(gg, "gg_doemail(): End.");
+#endif
+
+ return NULL;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// User Util Dlg Page : Data
+INT_PTR CALLBACK gg_userutildlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ GGUSERUTILDLGDATA *dat = (GGUSERUTILDLGDATA *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (msg)
+ {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ WindowSetIcon(hwndDlg, "settings");
+ dat = (GGUSERUTILDLGDATA *)lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam);
+ if (dat) SetDlgItemText(hwndDlg, IDC_EMAIL, dat->email); // Readup email
+ return TRUE;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam))
+ {
+ case IDC_PASSWORD:
+ case IDC_CPASSWORD:
+ case IDC_CONFIRM:
+ {
+ char pass[128], cpass[128];
+ BOOL enable;
+ GetDlgItemText(hwndDlg, IDC_PASSWORD, pass, sizeof(pass));
+ GetDlgItemText(hwndDlg, IDC_CPASSWORD, cpass, sizeof(cpass));
+ enable = strlen(pass) && strlen(cpass) && !strcmp(cpass, pass);
+ if (dat && dat->mode == GG_USERUTIL_REMOVE)
+ EnableWindow(GetDlgItem(hwndDlg, IDOK), IsDlgButtonChecked(hwndDlg, IDC_CONFIRM) ? enable : FALSE);
+ else
+ EnableWindow(GetDlgItem(hwndDlg, IDOK), enable);
+ break;
+ }
+
+ case IDOK:
+ {
+ char pass[128], cpass[128], email[128];
+ GetDlgItemText(hwndDlg, IDC_PASSWORD, pass, sizeof(pass));
+ GetDlgItemText(hwndDlg, IDC_CPASSWORD, cpass, sizeof(cpass));
+ GetDlgItemText(hwndDlg, IDC_EMAIL, email, sizeof(email));
+ EndDialog(hwndDlg, IDOK);
+
+ // Check dialog box mode
+ if (!dat) break;
+ switch (dat->mode)
+ {
+ case GG_USERUTIL_CREATE: gg_doregister(dat->gg, pass, email); break;
+ case GG_USERUTIL_REMOVE: gg_dounregister(dat->gg, dat->uin, pass); break;
+ case GG_USERUTIL_PASS: gg_dochpass(dat->gg, dat->uin, dat->pass, pass); break;
+ case GG_USERUTIL_EMAIL: gg_dochemail(dat->gg, dat->uin, dat->pass, dat->email, email); break;
+ }
+ break;
+ }
+
+ case IDCANCEL:
+ EndDialog(hwndDlg, IDCANCEL);
+ break;
+ }
+ break;
+
+ case WM_DESTROY:
+ WindowFreeIcon(hwndDlg);
+ break;
+ }
+ return FALSE;
+}
diff --git a/protocols/Gadu-Gadu/version.h b/protocols/Gadu-Gadu/version.h
new file mode 100644
index 0000000000..00f0e6bb6d
--- /dev/null
+++ b/protocols/Gadu-Gadu/version.h
@@ -0,0 +1,25 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2010 Bartosz Białek
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include "../../include/m_version.h"
+
+#define __FILEVERSION_STRING MIRANDA_VERSION_FILEVERSION
+#define __VERSION_STRING MIRANDA_VERSION_STRING
+#define __VERSION_DWORD MIRANDA_VERSION_DWORD
diff --git a/protocols/Gadu-Gadu/version.rc b/protocols/Gadu-Gadu/version.rc
new file mode 100644
index 0000000000..01ce13fd19
--- /dev/null
+++ b/protocols/Gadu-Gadu/version.rc
@@ -0,0 +1,56 @@
+////////////////////////////////////////////////////////////////////////////////
+// Gadu-Gadu Plugin for Miranda IM
+//
+// Copyright (c) 2010-2012 Bartosz Białek
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+////////////////////////////////////////////////////////////////////////////////
+
+#include <windows.h>
+#include "version.h"
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION __FILEVERSION_STRING
+ PRODUCTVERSION __FILEVERSION_STRING
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000004b0"
+ BEGIN
+ VALUE "Comments", "Licensed under the terms of the GNU General Public License"
+ VALUE "CompanyName", "Bartosz Białek, Adam Strzelecki"
+ VALUE "FileDescription", "Gadu-Gadu Protocol Plugin for Miranda IM"
+ VALUE "FileVersion", __VERSION_STRING
+ VALUE "InternalName", "gg"
+ VALUE "LegalCopyright", "Copyright © 2009-2012 Bartosz Białek, 2003-2009 Adam Strzelecki"
+ VALUE "OriginalFilename", "gg.dll"
+ VALUE "ProductName", "Gadu-Gadu Protocol Plugin for Miranda IM"
+ VALUE "ProductVersion", __VERSION_STRING
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0, 1200
+ END
+END