From 0ecadfc45326fce5fc4ba28b27a0a7ad484e5b84 Mon Sep 17 00:00:00 2001 From: Vadim Dashevskiy Date: Fri, 12 Oct 2012 14:18:20 +0000 Subject: Gadu-Gadu: folders restructurization git-svn-id: http://svn.miranda-ng.org/main/trunk@1888 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj | 122 +- protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj.filters | 148 +- protocols/Gadu-Gadu/avatar.cpp | 459 ---- protocols/Gadu-Gadu/core.cpp | 1756 ------------ protocols/Gadu-Gadu/dialogs.cpp | 1016 ------- protocols/Gadu-Gadu/docs/gadu-gadu-translation.txt | 233 ++ protocols/Gadu-Gadu/dynstuff.cpp | 612 ----- protocols/Gadu-Gadu/dynstuff.h | 70 - protocols/Gadu-Gadu/filetransfer.cpp | 977 ------- protocols/Gadu-Gadu/gadu-gadu-translation.txt | 233 -- protocols/Gadu-Gadu/gg.cpp | 523 ---- protocols/Gadu-Gadu/gg.h | 341 --- protocols/Gadu-Gadu/gg_proto.cpp | 804 ------ protocols/Gadu-Gadu/gg_proto.h | 295 -- protocols/Gadu-Gadu/groupchat.cpp | 669 ----- protocols/Gadu-Gadu/icolib.cpp | 109 - protocols/Gadu-Gadu/icons/block.ico | Bin 2550 -> 0 bytes .../Gadu-Gadu/icons/clear_ignored_conference.ico | Bin 2038 -> 0 bytes protocols/Gadu-Gadu/icons/conference.ico | Bin 9062 -> 0 bytes protocols/Gadu-Gadu/icons/delete.ico | Bin 2550 -> 0 bytes .../Gadu-Gadu/icons/export_list_to_server.ico | Bin 2038 -> 0 bytes .../Gadu-Gadu/icons/export_list_to_txt_file.ico | Bin 2038 -> 0 bytes protocols/Gadu-Gadu/icons/gg.ico | Bin 6830 -> 0 bytes protocols/Gadu-Gadu/icons/image.ico | Bin 2038 -> 0 bytes .../Gadu-Gadu/icons/import_list_from_server.ico | Bin 2038 -> 0 bytes .../Gadu-Gadu/icons/import_list_from_txt_file.ico | Bin 2038 -> 0 bytes protocols/Gadu-Gadu/icons/list.ico | Bin 2550 -> 0 bytes protocols/Gadu-Gadu/icons/next.ico | Bin 2038 -> 0 bytes protocols/Gadu-Gadu/icons/previous.ico | Bin 2038 -> 0 bytes .../Gadu-Gadu/icons/remove_list_from_server.ico | Bin 2038 -> 0 bytes protocols/Gadu-Gadu/icons/save.ico | Bin 2038 -> 0 bytes protocols/Gadu-Gadu/icons/sessions.ico | Bin 6830 -> 0 bytes protocols/Gadu-Gadu/icons/settings.ico | Bin 6830 -> 0 bytes protocols/Gadu-Gadu/image.cpp | 1191 -------- protocols/Gadu-Gadu/import.cpp | 651 ----- protocols/Gadu-Gadu/keepalive.cpp | 92 - protocols/Gadu-Gadu/libgadu/COPYING | 504 ---- protocols/Gadu-Gadu/libgadu/common.c | 975 ------- protocols/Gadu-Gadu/libgadu/compat.h | 36 - protocols/Gadu-Gadu/libgadu/dcc.c | 1363 ---------- protocols/Gadu-Gadu/libgadu/dcc7.c | 1655 ----------- protocols/Gadu-Gadu/libgadu/events.c | 2864 -------------------- protocols/Gadu-Gadu/libgadu/http.c | 544 ---- protocols/Gadu-Gadu/libgadu/internal.h | 48 - protocols/Gadu-Gadu/libgadu/libgadu.c | 2353 ---------------- protocols/Gadu-Gadu/libgadu/libgadu.h | 2311 ---------------- protocols/Gadu-Gadu/libgadu/obsolete.c | 238 -- protocols/Gadu-Gadu/libgadu/protocol.h | 277 -- protocols/Gadu-Gadu/libgadu/pthread.c | 78 - protocols/Gadu-Gadu/libgadu/pthread.h | 56 - protocols/Gadu-Gadu/libgadu/pubdir.c | 862 ------ protocols/Gadu-Gadu/libgadu/pubdir50.c | 557 ---- protocols/Gadu-Gadu/libgadu/resolver.c | 766 ------ protocols/Gadu-Gadu/libgadu/resolver.h | 33 - protocols/Gadu-Gadu/libgadu/sha1.c | 308 --- protocols/Gadu-Gadu/libgadu/win32.c | 65 - protocols/Gadu-Gadu/libgadu/win32.h | 75 - protocols/Gadu-Gadu/links.cpp | 173 -- protocols/Gadu-Gadu/oauth.cpp | 584 ---- protocols/Gadu-Gadu/ownerinfo.cpp | 78 - protocols/Gadu-Gadu/popups.cpp | 174 -- protocols/Gadu-Gadu/proto_gg/Proto_GG.rc | Bin 4042 -> 0 bytes protocols/Gadu-Gadu/proto_gg/Proto_GG.vcxproj | 4 +- .../Gadu-Gadu/proto_gg/Proto_GG.vcxproj.filters | 4 +- protocols/Gadu-Gadu/proto_gg/icos/Away.ico | Bin 5430 -> 0 bytes protocols/Gadu-Gadu/proto_gg/icos/DND.ico | Bin 5430 -> 0 bytes protocols/Gadu-Gadu/proto_gg/icos/FFC.ico | Bin 5430 -> 0 bytes protocols/Gadu-Gadu/proto_gg/icos/Invisible.ico | Bin 5430 -> 0 bytes protocols/Gadu-Gadu/proto_gg/icos/NA.ico | Bin 5430 -> 0 bytes protocols/Gadu-Gadu/proto_gg/icos/Offline.ico | Bin 5430 -> 0 bytes protocols/Gadu-Gadu/proto_gg/icos/Online.ico | Bin 5430 -> 0 bytes protocols/Gadu-Gadu/proto_gg/res/Away.ico | Bin 0 -> 5430 bytes protocols/Gadu-Gadu/proto_gg/res/DND.ico | Bin 0 -> 5430 bytes protocols/Gadu-Gadu/proto_gg/res/FFC.ico | Bin 0 -> 5430 bytes protocols/Gadu-Gadu/proto_gg/res/Invisible.ico | Bin 0 -> 5430 bytes protocols/Gadu-Gadu/proto_gg/res/NA.ico | Bin 0 -> 5430 bytes protocols/Gadu-Gadu/proto_gg/res/Offline.ico | Bin 0 -> 5430 bytes protocols/Gadu-Gadu/proto_gg/res/Online.ico | Bin 0 -> 5430 bytes protocols/Gadu-Gadu/proto_gg/res/Proto_GG.rc | Bin 0 -> 3990 bytes protocols/Gadu-Gadu/proto_gg/resource.h | Bin 1444 -> 0 bytes protocols/Gadu-Gadu/proto_gg/src/resource.h | Bin 0 -> 1444 bytes protocols/Gadu-Gadu/res/block.ico | Bin 0 -> 2550 bytes .../Gadu-Gadu/res/clear_ignored_conference.ico | Bin 0 -> 2038 bytes protocols/Gadu-Gadu/res/conference.ico | Bin 0 -> 9062 bytes protocols/Gadu-Gadu/res/delete.ico | Bin 0 -> 2550 bytes protocols/Gadu-Gadu/res/export_list_to_server.ico | Bin 0 -> 2038 bytes .../Gadu-Gadu/res/export_list_to_txt_file.ico | Bin 0 -> 2038 bytes protocols/Gadu-Gadu/res/gg.ico | Bin 0 -> 6830 bytes protocols/Gadu-Gadu/res/image.ico | Bin 0 -> 2038 bytes .../Gadu-Gadu/res/import_list_from_server.ico | Bin 0 -> 2038 bytes .../Gadu-Gadu/res/import_list_from_txt_file.ico | Bin 0 -> 2038 bytes protocols/Gadu-Gadu/res/list.ico | Bin 0 -> 2550 bytes protocols/Gadu-Gadu/res/next.ico | Bin 0 -> 2038 bytes protocols/Gadu-Gadu/res/previous.ico | Bin 0 -> 2038 bytes .../Gadu-Gadu/res/remove_list_from_server.ico | Bin 0 -> 2038 bytes protocols/Gadu-Gadu/res/resource.rc | 354 +++ protocols/Gadu-Gadu/res/save.ico | Bin 0 -> 2038 bytes protocols/Gadu-Gadu/res/sessions.ico | Bin 0 -> 6830 bytes protocols/Gadu-Gadu/res/settings.ico | Bin 0 -> 6830 bytes protocols/Gadu-Gadu/res/version.rc | 60 + protocols/Gadu-Gadu/resource.h | 147 - protocols/Gadu-Gadu/resource.rc | 355 --- protocols/Gadu-Gadu/services.cpp | 330 --- protocols/Gadu-Gadu/sessions.cpp | 445 --- protocols/Gadu-Gadu/src/avatar.cpp | 459 ++++ protocols/Gadu-Gadu/src/core.cpp | 1756 ++++++++++++ protocols/Gadu-Gadu/src/dialogs.cpp | 1016 +++++++ protocols/Gadu-Gadu/src/dynstuff.cpp | 612 +++++ protocols/Gadu-Gadu/src/dynstuff.h | 70 + protocols/Gadu-Gadu/src/filetransfer.cpp | 977 +++++++ protocols/Gadu-Gadu/src/gg.cpp | 523 ++++ protocols/Gadu-Gadu/src/gg.h | 341 +++ protocols/Gadu-Gadu/src/gg_proto.cpp | 804 ++++++ protocols/Gadu-Gadu/src/gg_proto.h | 295 ++ protocols/Gadu-Gadu/src/groupchat.cpp | 669 +++++ protocols/Gadu-Gadu/src/icolib.cpp | 109 + protocols/Gadu-Gadu/src/image.cpp | 1191 ++++++++ protocols/Gadu-Gadu/src/import.cpp | 651 +++++ protocols/Gadu-Gadu/src/keepalive.cpp | 92 + protocols/Gadu-Gadu/src/libgadu/COPYING | 504 ++++ protocols/Gadu-Gadu/src/libgadu/common.c | 975 +++++++ protocols/Gadu-Gadu/src/libgadu/compat.h | 36 + protocols/Gadu-Gadu/src/libgadu/dcc.c | 1363 ++++++++++ protocols/Gadu-Gadu/src/libgadu/dcc7.c | 1655 +++++++++++ protocols/Gadu-Gadu/src/libgadu/events.c | 2864 ++++++++++++++++++++ protocols/Gadu-Gadu/src/libgadu/http.c | 544 ++++ protocols/Gadu-Gadu/src/libgadu/internal.h | 48 + protocols/Gadu-Gadu/src/libgadu/libgadu.c | 2353 ++++++++++++++++ protocols/Gadu-Gadu/src/libgadu/libgadu.h | 2311 ++++++++++++++++ protocols/Gadu-Gadu/src/libgadu/obsolete.c | 238 ++ protocols/Gadu-Gadu/src/libgadu/protocol.h | 277 ++ protocols/Gadu-Gadu/src/libgadu/pthread.c | 78 + protocols/Gadu-Gadu/src/libgadu/pthread.h | 56 + protocols/Gadu-Gadu/src/libgadu/pubdir.c | 862 ++++++ protocols/Gadu-Gadu/src/libgadu/pubdir50.c | 557 ++++ protocols/Gadu-Gadu/src/libgadu/resolver.c | 766 ++++++ protocols/Gadu-Gadu/src/libgadu/resolver.h | 33 + protocols/Gadu-Gadu/src/libgadu/sha1.c | 308 +++ protocols/Gadu-Gadu/src/libgadu/win32.c | 65 + protocols/Gadu-Gadu/src/libgadu/win32.h | 75 + protocols/Gadu-Gadu/src/links.cpp | 173 ++ protocols/Gadu-Gadu/src/oauth.cpp | 584 ++++ protocols/Gadu-Gadu/src/ownerinfo.cpp | 78 + protocols/Gadu-Gadu/src/popups.cpp | 174 ++ protocols/Gadu-Gadu/src/resource.h | 147 + protocols/Gadu-Gadu/src/services.cpp | 330 +++ protocols/Gadu-Gadu/src/sessions.cpp | 445 +++ protocols/Gadu-Gadu/src/token.cpp | 161 ++ protocols/Gadu-Gadu/src/userutils.cpp | 329 +++ protocols/Gadu-Gadu/src/version.h | 23 + protocols/Gadu-Gadu/token.cpp | 161 -- protocols/Gadu-Gadu/userutils.cpp | 329 --- protocols/Gadu-Gadu/version.h | 23 - protocols/Gadu-Gadu/version.rc | 60 - 154 files changed, 28725 insertions(+), 28802 deletions(-) delete mode 100644 protocols/Gadu-Gadu/avatar.cpp delete mode 100644 protocols/Gadu-Gadu/core.cpp delete mode 100644 protocols/Gadu-Gadu/dialogs.cpp create mode 100644 protocols/Gadu-Gadu/docs/gadu-gadu-translation.txt delete mode 100644 protocols/Gadu-Gadu/dynstuff.cpp delete mode 100644 protocols/Gadu-Gadu/dynstuff.h delete mode 100644 protocols/Gadu-Gadu/filetransfer.cpp delete mode 100644 protocols/Gadu-Gadu/gadu-gadu-translation.txt delete mode 100644 protocols/Gadu-Gadu/gg.cpp delete mode 100644 protocols/Gadu-Gadu/gg.h delete mode 100644 protocols/Gadu-Gadu/gg_proto.cpp delete mode 100644 protocols/Gadu-Gadu/gg_proto.h delete mode 100644 protocols/Gadu-Gadu/groupchat.cpp delete mode 100644 protocols/Gadu-Gadu/icolib.cpp delete mode 100644 protocols/Gadu-Gadu/icons/block.ico delete mode 100644 protocols/Gadu-Gadu/icons/clear_ignored_conference.ico delete mode 100644 protocols/Gadu-Gadu/icons/conference.ico delete mode 100644 protocols/Gadu-Gadu/icons/delete.ico delete mode 100644 protocols/Gadu-Gadu/icons/export_list_to_server.ico delete mode 100644 protocols/Gadu-Gadu/icons/export_list_to_txt_file.ico delete mode 100644 protocols/Gadu-Gadu/icons/gg.ico delete mode 100644 protocols/Gadu-Gadu/icons/image.ico delete mode 100644 protocols/Gadu-Gadu/icons/import_list_from_server.ico delete mode 100644 protocols/Gadu-Gadu/icons/import_list_from_txt_file.ico delete mode 100644 protocols/Gadu-Gadu/icons/list.ico delete mode 100644 protocols/Gadu-Gadu/icons/next.ico delete mode 100644 protocols/Gadu-Gadu/icons/previous.ico delete mode 100644 protocols/Gadu-Gadu/icons/remove_list_from_server.ico delete mode 100644 protocols/Gadu-Gadu/icons/save.ico delete mode 100644 protocols/Gadu-Gadu/icons/sessions.ico delete mode 100644 protocols/Gadu-Gadu/icons/settings.ico delete mode 100644 protocols/Gadu-Gadu/image.cpp delete mode 100644 protocols/Gadu-Gadu/import.cpp delete mode 100644 protocols/Gadu-Gadu/keepalive.cpp delete mode 100644 protocols/Gadu-Gadu/libgadu/COPYING delete mode 100644 protocols/Gadu-Gadu/libgadu/common.c delete mode 100644 protocols/Gadu-Gadu/libgadu/compat.h delete mode 100644 protocols/Gadu-Gadu/libgadu/dcc.c delete mode 100644 protocols/Gadu-Gadu/libgadu/dcc7.c delete mode 100644 protocols/Gadu-Gadu/libgadu/events.c delete mode 100644 protocols/Gadu-Gadu/libgadu/http.c delete mode 100644 protocols/Gadu-Gadu/libgadu/internal.h delete mode 100644 protocols/Gadu-Gadu/libgadu/libgadu.c delete mode 100644 protocols/Gadu-Gadu/libgadu/libgadu.h delete mode 100644 protocols/Gadu-Gadu/libgadu/obsolete.c delete mode 100644 protocols/Gadu-Gadu/libgadu/protocol.h delete mode 100644 protocols/Gadu-Gadu/libgadu/pthread.c delete mode 100644 protocols/Gadu-Gadu/libgadu/pthread.h delete mode 100644 protocols/Gadu-Gadu/libgadu/pubdir.c delete mode 100644 protocols/Gadu-Gadu/libgadu/pubdir50.c delete mode 100644 protocols/Gadu-Gadu/libgadu/resolver.c delete mode 100644 protocols/Gadu-Gadu/libgadu/resolver.h delete mode 100644 protocols/Gadu-Gadu/libgadu/sha1.c delete mode 100644 protocols/Gadu-Gadu/libgadu/win32.c delete mode 100644 protocols/Gadu-Gadu/libgadu/win32.h delete mode 100644 protocols/Gadu-Gadu/links.cpp delete mode 100644 protocols/Gadu-Gadu/oauth.cpp delete mode 100644 protocols/Gadu-Gadu/ownerinfo.cpp delete mode 100644 protocols/Gadu-Gadu/popups.cpp delete mode 100644 protocols/Gadu-Gadu/proto_gg/Proto_GG.rc delete mode 100644 protocols/Gadu-Gadu/proto_gg/icos/Away.ico delete mode 100644 protocols/Gadu-Gadu/proto_gg/icos/DND.ico delete mode 100644 protocols/Gadu-Gadu/proto_gg/icos/FFC.ico delete mode 100644 protocols/Gadu-Gadu/proto_gg/icos/Invisible.ico delete mode 100644 protocols/Gadu-Gadu/proto_gg/icos/NA.ico delete mode 100644 protocols/Gadu-Gadu/proto_gg/icos/Offline.ico delete mode 100644 protocols/Gadu-Gadu/proto_gg/icos/Online.ico create mode 100644 protocols/Gadu-Gadu/proto_gg/res/Away.ico create mode 100644 protocols/Gadu-Gadu/proto_gg/res/DND.ico create mode 100644 protocols/Gadu-Gadu/proto_gg/res/FFC.ico create mode 100644 protocols/Gadu-Gadu/proto_gg/res/Invisible.ico create mode 100644 protocols/Gadu-Gadu/proto_gg/res/NA.ico create mode 100644 protocols/Gadu-Gadu/proto_gg/res/Offline.ico create mode 100644 protocols/Gadu-Gadu/proto_gg/res/Online.ico create mode 100644 protocols/Gadu-Gadu/proto_gg/res/Proto_GG.rc delete mode 100644 protocols/Gadu-Gadu/proto_gg/resource.h create mode 100644 protocols/Gadu-Gadu/proto_gg/src/resource.h create mode 100644 protocols/Gadu-Gadu/res/block.ico create mode 100644 protocols/Gadu-Gadu/res/clear_ignored_conference.ico create mode 100644 protocols/Gadu-Gadu/res/conference.ico create mode 100644 protocols/Gadu-Gadu/res/delete.ico create mode 100644 protocols/Gadu-Gadu/res/export_list_to_server.ico create mode 100644 protocols/Gadu-Gadu/res/export_list_to_txt_file.ico create mode 100644 protocols/Gadu-Gadu/res/gg.ico create mode 100644 protocols/Gadu-Gadu/res/image.ico create mode 100644 protocols/Gadu-Gadu/res/import_list_from_server.ico create mode 100644 protocols/Gadu-Gadu/res/import_list_from_txt_file.ico create mode 100644 protocols/Gadu-Gadu/res/list.ico create mode 100644 protocols/Gadu-Gadu/res/next.ico create mode 100644 protocols/Gadu-Gadu/res/previous.ico create mode 100644 protocols/Gadu-Gadu/res/remove_list_from_server.ico create mode 100644 protocols/Gadu-Gadu/res/resource.rc create mode 100644 protocols/Gadu-Gadu/res/save.ico create mode 100644 protocols/Gadu-Gadu/res/sessions.ico create mode 100644 protocols/Gadu-Gadu/res/settings.ico create mode 100644 protocols/Gadu-Gadu/res/version.rc delete mode 100644 protocols/Gadu-Gadu/resource.h delete mode 100644 protocols/Gadu-Gadu/resource.rc delete mode 100644 protocols/Gadu-Gadu/services.cpp delete mode 100644 protocols/Gadu-Gadu/sessions.cpp create mode 100644 protocols/Gadu-Gadu/src/avatar.cpp create mode 100644 protocols/Gadu-Gadu/src/core.cpp create mode 100644 protocols/Gadu-Gadu/src/dialogs.cpp create mode 100644 protocols/Gadu-Gadu/src/dynstuff.cpp create mode 100644 protocols/Gadu-Gadu/src/dynstuff.h create mode 100644 protocols/Gadu-Gadu/src/filetransfer.cpp create mode 100644 protocols/Gadu-Gadu/src/gg.cpp create mode 100644 protocols/Gadu-Gadu/src/gg.h create mode 100644 protocols/Gadu-Gadu/src/gg_proto.cpp create mode 100644 protocols/Gadu-Gadu/src/gg_proto.h create mode 100644 protocols/Gadu-Gadu/src/groupchat.cpp create mode 100644 protocols/Gadu-Gadu/src/icolib.cpp create mode 100644 protocols/Gadu-Gadu/src/image.cpp create mode 100644 protocols/Gadu-Gadu/src/import.cpp create mode 100644 protocols/Gadu-Gadu/src/keepalive.cpp create mode 100644 protocols/Gadu-Gadu/src/libgadu/COPYING create mode 100644 protocols/Gadu-Gadu/src/libgadu/common.c create mode 100644 protocols/Gadu-Gadu/src/libgadu/compat.h create mode 100644 protocols/Gadu-Gadu/src/libgadu/dcc.c create mode 100644 protocols/Gadu-Gadu/src/libgadu/dcc7.c create mode 100644 protocols/Gadu-Gadu/src/libgadu/events.c create mode 100644 protocols/Gadu-Gadu/src/libgadu/http.c create mode 100644 protocols/Gadu-Gadu/src/libgadu/internal.h create mode 100644 protocols/Gadu-Gadu/src/libgadu/libgadu.c create mode 100644 protocols/Gadu-Gadu/src/libgadu/libgadu.h create mode 100644 protocols/Gadu-Gadu/src/libgadu/obsolete.c create mode 100644 protocols/Gadu-Gadu/src/libgadu/protocol.h create mode 100644 protocols/Gadu-Gadu/src/libgadu/pthread.c create mode 100644 protocols/Gadu-Gadu/src/libgadu/pthread.h create mode 100644 protocols/Gadu-Gadu/src/libgadu/pubdir.c create mode 100644 protocols/Gadu-Gadu/src/libgadu/pubdir50.c create mode 100644 protocols/Gadu-Gadu/src/libgadu/resolver.c create mode 100644 protocols/Gadu-Gadu/src/libgadu/resolver.h create mode 100644 protocols/Gadu-Gadu/src/libgadu/sha1.c create mode 100644 protocols/Gadu-Gadu/src/libgadu/win32.c create mode 100644 protocols/Gadu-Gadu/src/libgadu/win32.h create mode 100644 protocols/Gadu-Gadu/src/links.cpp create mode 100644 protocols/Gadu-Gadu/src/oauth.cpp create mode 100644 protocols/Gadu-Gadu/src/ownerinfo.cpp create mode 100644 protocols/Gadu-Gadu/src/popups.cpp create mode 100644 protocols/Gadu-Gadu/src/resource.h create mode 100644 protocols/Gadu-Gadu/src/services.cpp create mode 100644 protocols/Gadu-Gadu/src/sessions.cpp create mode 100644 protocols/Gadu-Gadu/src/token.cpp create mode 100644 protocols/Gadu-Gadu/src/userutils.cpp create mode 100644 protocols/Gadu-Gadu/src/version.h delete mode 100644 protocols/Gadu-Gadu/token.cpp delete mode 100644 protocols/Gadu-Gadu/userutils.cpp delete mode 100644 protocols/Gadu-Gadu/version.h delete mode 100644 protocols/Gadu-Gadu/version.rc diff --git a/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj b/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj index 227f421cf2..129352f88d 100644 --- a/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj +++ b/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj @@ -72,7 +72,7 @@ Disabled - ..\..\include;..\..\plugins\ExternalAPI;libgadu;%(AdditionalIncludeDirectories) + ..\..\include;..\..\plugins\ExternalAPI;src\libgadu;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;GG_EXPORTS;%(PreprocessorDefinitions) true EnableFastChecks @@ -101,7 +101,7 @@ Disabled - ..\..\include;..\..\plugins\ExternalAPI;libgadu;%(AdditionalIncludeDirectories) + ..\..\include;..\..\plugins\ExternalAPI;src\libgadu;%(AdditionalIncludeDirectories) WIN64;_DEBUG;_WINDOWS;_USRDLL;GG_EXPORTS;%(PreprocessorDefinitions) true EnableFastChecks @@ -131,7 +131,7 @@ Full AnySuitable Size - ..\..\include;..\..\plugins\ExternalAPI;libgadu;%(AdditionalIncludeDirectories) + ..\..\include;..\..\plugins\ExternalAPI;src\libgadu;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;_USRDLL;GG_EXPORTS;%(PreprocessorDefinitions) true false @@ -164,7 +164,7 @@ Full AnySuitable Size - ..\..\include;..\..\plugins\ExternalAPI;libgadu;%(AdditionalIncludeDirectories) + ..\..\include;..\..\plugins\ExternalAPI;src\libgadu;%(AdditionalIncludeDirectories) WIN64;NDEBUG;_WINDOWS;_USRDLL;GG_EXPORTS;%(PreprocessorDefinitions) true false @@ -193,104 +193,82 @@ - - - - - - + + + + + + Create - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + NotUsing - + NotUsing - + NotUsing - + NotUsing - + NotUsing - + NotUsing - + NotUsing - + NotUsing - + NotUsing - + NotUsing - + NotUsing - + NotUsing - - - - - - - - - - - - + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj.filters b/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj.filters index 0057590422..227066cb83 100644 --- a/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj.filters +++ b/protocols/Gadu-Gadu/Gadu-Gadu_10.vcxproj.filters @@ -18,200 +18,146 @@ - + Source Files\libgadu - + Source Files\libgadu - + Source Files\libgadu - + Source Files\libgadu - + Source Files\libgadu - + Source Files\libgadu - + Source Files\libgadu - + Source Files\libgadu - + Source Files\libgadu - + Source Files\libgadu - + Source Files\libgadu - + Source Files\libgadu - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files - + Source Files\libgadu - + Source Files\libgadu - + Source Files\libgadu - + Source Files\libgadu - + Source Files\libgadu - + Source Files\libgadu - + Source Files\libgadu - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - Resource Files - - - - - - - - + + Resource Files diff --git a/protocols/Gadu-Gadu/avatar.cpp b/protocols/Gadu-Gadu/avatar.cpp deleted file mode 100644 index ace7871b6a..0000000000 --- a/protocols/Gadu-Gadu/avatar.cpp +++ /dev/null @@ -1,459 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// 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 -#include -#include "protocol.h" - -////////////////////////////////////////////////////////// -// Avatars support -void GGPROTO::getAvatarFilename(HANDLE hContact, TCHAR *pszDest, int cbLen) -{ - int tPathLen; - TCHAR *path = (TCHAR*)alloca(cbLen * sizeof(TCHAR)); - TCHAR *avatartype = NULL; - - if (hAvatarsFolder == NULL || FoldersGetCustomPathT(hAvatarsFolder, path, cbLen, _T(""))) { - mir_ptr tmpPath( Utils_ReplaceVarsT( _T("%miranda_avatarcache%"))); - tPathLen = mir_sntprintf(pszDest, cbLen, _T("%s\\%s"), (TCHAR*)tmpPath, m_tszUserName); - } - else { - _tcscpy(pszDest, path); - tPathLen = (int)_tcslen(pszDest); - } - - if (_taccess(pszDest, 0)) - CallService(MS_UTILS_CREATEDIRTREE, 0, (LPARAM)pszDest); - - switch (db_get_b(hContact, m_szModuleName, GG_KEY_AVATARTYPE, GG_KEYDEF_AVATARTYPE)) { - case PA_FORMAT_JPEG: avatartype = _T("jpg"); break; - case PA_FORMAT_GIF: avatartype = _T("gif"); break; - case PA_FORMAT_PNG: avatartype = _T("png"); break; - } - - if (hContact != NULL) { - DBVARIANT dbv; - if (!db_get_s(hContact, m_szModuleName, GG_KEY_AVATARHASH, &dbv, DBVT_ASCIIZ)) { - mir_sntprintf(pszDest + tPathLen, cbLen - tPathLen, _T("\\%s.%s"), dbv.pszVal, avatartype); - DBFreeVariant(&dbv); - } - } - else mir_sntprintf(pszDest + tPathLen, cbLen - tPathLen, _T("\\%s avatar.%s"), m_szModuleName, avatartype); -} - -void GGPROTO::getAvatarFileInfo(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)netlib, (LPARAM)&req); - if (resp) { - if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { - HXML hXml; - TCHAR *xmlAction; - TCHAR *tag; - - xmlAction = mir_a2t(resp->pData); - tag = mir_a2t("result"); - hXml = xi.parseString(xmlAction, 0, tag); - - if (hXml != NULL) { - HXML node; - char *blank; - - mir_free(tag); tag = mir_a2t("users/user/avatars/avatar"); - node = xi.getChildByPath(hXml, tag, 0); - mir_free(tag); tag = mir_a2t("blank"); - blank = (node != NULL) ? mir_t2a(xi.getAttrValue(node, tag)) : NULL; - - if (blank != NULL && strcmp(blank, "1")) { - mir_free(tag); tag = mir_a2t("users/user/avatars/avatar/bigAvatar"); - node = xi.getChildByPath(hXml, tag, 0); - *avatarurl = node != NULL ? mir_t2a(xi.getText(node)) : NULL; - - mir_free(tag); tag = mir_a2t("users/user/avatars/avatar/originBigAvatar"); - node = xi.getChildByPath(hXml, tag, 0); - if (node != NULL) { - char *orgavurl = mir_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 netlog("gg_getavatarfileinfo(): Invalid response code from HTTP request"); - CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); - } - else netlog("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((BYTE*)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 GGPROTO::getAvatar(HANDLE hContact, char *szAvatarURL) -{ - if (pth_avatar.dwThreadId) { - GGGETAVATARDATA *data = (GGGETAVATARDATA*)mir_alloc(sizeof(GGGETAVATARDATA)); - data->hContact = hContact; - data->AvatarURL = mir_strdup(szAvatarURL); - EnterCriticalSection(&avatar_mutex); - list_add(&avatar_transfers, data, 0); - LeaveCriticalSection(&avatar_mutex); - } -} - -typedef struct -{ - HANDLE hContact; - int iWaitFor; -} GGREQUESTAVATARDATA; - -void GGPROTO::requestAvatar(HANDLE hContact, int iWaitFor) -{ - if (db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS) - && pth_avatar.dwThreadId) { - GGREQUESTAVATARDATA *data = (GGREQUESTAVATARDATA*)mir_alloc(sizeof(GGREQUESTAVATARDATA)); - data->hContact = hContact; - data->iWaitFor = iWaitFor; - EnterCriticalSection(&avatar_mutex); - list_add(&avatar_requests, data, 0); - LeaveCriticalSection(&avatar_mutex); - } -} - -void __cdecl GGPROTO::avatarrequestthread(void*) -{ - list_t l; - - netlog("gg_avatarrequestthread(): Avatar Request Thread Starting"); - while (pth_avatar.dwThreadId) - { - EnterCriticalSection(&avatar_mutex); - if (avatar_requests) { - GGREQUESTAVATARDATA *data = (GGREQUESTAVATARDATA *)avatar_requests->data; - char *AvatarURL; - int AvatarType, iWaitFor = data->iWaitFor; - HANDLE hContact = data->hContact; - - list_remove(&avatar_requests, data, 0); - mir_free(data); - LeaveCriticalSection(&avatar_mutex); - - getAvatarFileInfo( db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0), &AvatarURL, &AvatarType); - if (AvatarURL != NULL && strlen(AvatarURL) > 0) - db_set_s(hContact, m_szModuleName, GG_KEY_AVATARURL, AvatarURL); - else - db_unset(hContact, m_szModuleName, GG_KEY_AVATARURL); - db_set_b(hContact, m_szModuleName, GG_KEY_AVATARTYPE, (BYTE)AvatarType); - db_set_b(hContact, m_szModuleName, GG_KEY_AVATARREQUESTED, 1); - - if (iWaitFor) { - PROTO_AVATAR_INFORMATIONT pai = {0}; - pai.cbSize = sizeof(pai); - pai.hContact = hContact; - if (getavatarinfo((WPARAM)GAIF_FORCE, (LPARAM)&pai) != GAIR_WAITFOR) - ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, (HANDLE)&pai, 0); - } - else ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, 0, 0); - } - else LeaveCriticalSection(&avatar_mutex); - - EnterCriticalSection(&avatar_mutex); - if (avatar_transfers) { - GGGETAVATARDATA *data = (GGGETAVATARDATA *)avatar_transfers->data; - NETLIBHTTPREQUEST req = {0}; - NETLIBHTTPREQUEST *resp; - PROTO_AVATAR_INFORMATIONT pai = {0}; - int result = 0; - - pai.cbSize = sizeof(pai); - pai.hContact = data->hContact; - pai.format = db_get_b(pai.hContact, m_szModuleName, 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)netlib, (LPARAM)&req); - if (resp) { - if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { - int file_fd; - - getAvatarFilename(pai.hContact, pai.filename, sizeof(pai.filename)); - file_fd = _topen(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 netlog("gg_avatarrequestthread(): Invalid response code from HTTP request"); - CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); - } - else netlog("gg_avatarrequestthread(): No response from HTTP request"); - - ProtoBroadcastAck(m_szModuleName, pai.hContact, ACKTYPE_AVATAR, - result ? ACKRESULT_SUCCESS : ACKRESULT_FAILED, (HANDLE)&pai, 0); - - if (!pai.hContact) - CallService(MS_AV_REPORTMYAVATARCHANGED, (WPARAM)m_szModuleName, 0); - - list_remove(&avatar_transfers, data, 0); - mir_free(data->AvatarURL); - mir_free(data); - } - LeaveCriticalSection(&avatar_mutex); - SleepEx(100, FALSE); - } - - for (l = avatar_requests; l; l = l->next) { - GGREQUESTAVATARDATA *data = (GGREQUESTAVATARDATA *)l->data; - mir_free(data); - } - for (l = avatar_transfers; l; l = l->next) { - GGGETAVATARDATA *data = (GGGETAVATARDATA *)l->data; - mir_free(data->AvatarURL); - mir_free(data); - } - list_destroy(avatar_requests, 0); - list_destroy(avatar_transfers, 0); - netlog("gg_avatarrequestthread(): Avatar Request Thread Ending"); -} - -void GGPROTO::initavatarrequestthread() -{ - DWORD exitCode = 0; - - GetExitCodeThread(pth_avatar.hThread, &exitCode); - if (exitCode != STILL_ACTIVE) { - avatar_requests = avatar_transfers = NULL; - pth_avatar.hThread = forkthreadex(&GGPROTO::avatarrequestthread, NULL, &pth_avatar.dwThreadId); - } -} - -void GGPROTO::uninitavatarrequestthread() -{ - pth_avatar.dwThreadId = 0; -#ifdef DEBUGMODE - netlog("gg_uninitavatarrequestthread(): Waiting until Avatar Request Thread finished, if needed."); -#endif - threadwait(&pth_avatar); -} - -void __cdecl GGPROTO::getuseravatarthread(void*) -{ - char *AvatarURL; - int AvatarType; - - getAvatarFileInfo( db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0), &AvatarURL, &AvatarType); - if (AvatarURL != NULL && strlen(AvatarURL) > 0) - db_set_s(NULL, m_szModuleName, GG_KEY_AVATARURL, AvatarURL); - else - db_unset(NULL, m_szModuleName, GG_KEY_AVATARURL); - db_set_b(NULL, m_szModuleName, GG_KEY_AVATARTYPE, (BYTE)AvatarType); - db_set_b(NULL, m_szModuleName, GG_KEY_AVATARREQUESTED, 1); - mir_free(AvatarURL); - - PROTO_AVATAR_INFORMATIONT pai = {0}; - pai.cbSize = sizeof(pai); - getavatarinfo((WPARAM)GAIF_FORCE, (LPARAM)&pai); -} - -void GGPROTO::getUserAvatar() -{ - if (db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS) - && db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0)) - forkthread(&GGPROTO::getuseravatarthread, NULL); -} - -void __cdecl GGPROTO::setavatarthread(void *param) -{ - NETLIBHTTPHEADER httpHeaders[4]; - NETLIBHTTPREQUEST req = {0}; - NETLIBHTTPREQUEST *resp; - TCHAR *szFilename = (TCHAR*)param; - const char *contentend = "\r\n--AaB03x--\r\n"; - char szUrl[128], uin[32], *authHeader, *data, *avatardata, content[256], image_ext[4], image_type[11]; - int file_fd, avatardatalen, datalen, contentlen, contentendlen, res = 0, repeat = 0; - - netlog("gg_setavatar(): Trying to set user avatar using %s...", szFilename); - UIN2ID( db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0), uin); - - file_fd = _topen(szFilename, _O_RDONLY | _O_BINARY, _S_IREAD); - if (file_fd == -1) { - netlog("gg_setavatar(): Failed to open avatar file (%s).", strerror(errno)); - mir_free(szFilename); - getUserAvatar(); - return; - } - avatardatalen = _filelength(file_fd); - avatardata = (char *)mir_alloc(avatardatalen); - - _read(file_fd, avatardata, avatardatalen); - _close(file_fd); - - TCHAR *fileext = _tcsrchr(szFilename, '.'); - fileext++; - if (!_tcsicmp(fileext, _T("jpg"))) { - strcpy(image_ext, "jpg"); - strcpy(image_type, "image/jpeg"); - } - else if (!_tcsicmp(fileext, _T("gif"))) { - strcpy(image_ext, "gif"); - strcpy(image_type, "image/gif"); - } - else { - 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); - oauth_checktoken(0); - authHeader = oauth_header("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)netlib, (LPARAM)&req); - if (resp) { - if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { -#ifdef DEBUGMODE - netlog("%s", resp->pData); -#endif - res = 1; - } - else netlog("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 netlog("gg_setavatar(): No response from HTTP request"); - - if (repeat) { // Access Token expired - we need to obtain new - mir_free(authHeader); - oauth_checktoken(1); - authHeader = oauth_header("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)netlib, (LPARAM)&req); - if (resp) { - if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { -#ifdef DEBUGMODE - netlog("%s", resp->pData); -#endif - res = 1; - } - else netlog("gg_setavatar(): Invalid response code from HTTP request"); - CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); - } - else netlog("gg_setavatar(): No response from HTTP request"); - } - - mir_free(authHeader); - mir_free(avatardata); - mir_free(data); - - if (res) - netlog("gg_setavatar(): User avatar set successfully."); - else - netlog("gg_setavatar(): Failed to set user avatar."); - - mir_free(szFilename); - getUserAvatar(); -} - -void GGPROTO::setAvatar(const TCHAR *szFilename) -{ - forkthread(&GGPROTO::setavatarthread, mir_tstrdup(szFilename)); -} diff --git a/protocols/Gadu-Gadu/core.cpp b/protocols/Gadu-Gadu/core.cpp deleted file mode 100644 index 63d5d37b24..0000000000 --- a/protocols/Gadu-Gadu/core.cpp +++ /dev/null @@ -1,1756 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2009 Adam Strzelecki -// 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 -#include - -//////////////////////////////////////////////////////////// -// 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 - -int GGPROTO::isonline() -{ - mir_cslock lck(sess_mutex); - return (sess != NULL); -} - -//////////////////////////////////////////////////////////// -// Send disconnect request and wait for server thread to die -void GGPROTO::disconnect() -{ - // If main loop then send disconnect request - if (isonline()) - { - // Fetch proper status msg - char *szMsg = NULL; - - // Loadup status - if (db_get_b(NULL, m_szModuleName, GG_KEY_LEAVESTATUSMSG, GG_KEYDEF_LEAVESTATUSMSG)) - { - DBVARIANT dbv; - switch (db_get_w(NULL, m_szModuleName, GG_KEY_LEAVESTATUS, GG_KEYDEF_LEAVESTATUS)) { - case ID_STATUS_ONLINE: - EnterCriticalSection(&modemsg_mutex); - szMsg = mir_strdup(modemsg.online); - LeaveCriticalSection(&modemsg_mutex); - if (!szMsg && !db_get_s(NULL, "SRAway", gg_status2db(ID_STATUS_ONLINE, "Default"), &dbv, DBVT_ASCIIZ)) { - if (dbv.pszVal && *(dbv.pszVal)) - szMsg = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - break; - case ID_STATUS_AWAY: - EnterCriticalSection(&modemsg_mutex); - szMsg = mir_strdup(modemsg.away); - LeaveCriticalSection(&modemsg_mutex); - if (!szMsg && !db_get_s(NULL, "SRAway", gg_status2db(ID_STATUS_AWAY, "Default"), &dbv, DBVT_ASCIIZ)) { - if (dbv.pszVal && *(dbv.pszVal)) - szMsg = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - break; - case ID_STATUS_DND: - EnterCriticalSection(&modemsg_mutex); - szMsg = mir_strdup(modemsg.dnd); - LeaveCriticalSection(&modemsg_mutex); - if (!szMsg && !db_get_s(NULL, "SRAway", gg_status2db(ID_STATUS_DND, "Default"), &dbv, DBVT_ASCIIZ)) { - if (dbv.pszVal && *(dbv.pszVal)) - szMsg = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - break; - case ID_STATUS_FREECHAT: - EnterCriticalSection(&modemsg_mutex); - szMsg = mir_strdup(modemsg.freechat); - LeaveCriticalSection(&modemsg_mutex); - if (!szMsg && !db_get_s(NULL, "SRAway", gg_status2db(ID_STATUS_FREECHAT, "Default"), &dbv, DBVT_ASCIIZ)) { - if (dbv.pszVal && *(dbv.pszVal)) - szMsg = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - break; - case ID_STATUS_INVISIBLE: - EnterCriticalSection(&modemsg_mutex); - szMsg = mir_strdup(modemsg.invisible); - LeaveCriticalSection(&modemsg_mutex); - if (!szMsg && !db_get_s(NULL, "SRAway", gg_status2db(ID_STATUS_INVISIBLE, "Default"), &dbv, DBVT_ASCIIZ)) { - if (dbv.pszVal && *(dbv.pszVal)) - szMsg = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - break; - default: - // Set last status - EnterCriticalSection(&modemsg_mutex); - szMsg = mir_strdup(getstatusmsg(m_iStatus)); - LeaveCriticalSection(&modemsg_mutex); - } - } - - EnterCriticalSection(&sess_mutex); - // Check if it has message - if (szMsg) - { - gg_change_status_descr(sess, GG_STATUS_NOT_AVAIL_DESCR, szMsg); - mir_free(szMsg); - // Wait for disconnection acknowledge - } - else - { - gg_change_status(sess, GG_STATUS_NOT_AVAIL); - // Send logoff immediately - gg_logoff(sess); - } - LeaveCriticalSection(&sess_mutex); - } - // Else cancel connection attempt - else if (sock) - closesocket(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_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_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_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 GGPROTO::mainthread(void *) -{ - // Miranda variables - NETLIBUSERSETTINGS nlus = {0}; - DBVARIANT dbv; - // Gadu-Gadu variables - gg_login_params p = {0}; - gg_event *e; - // Host cycling variables - int hostnum = 0, hostcount = 0; - GGHOST hosts[64]; - // Gadu-gadu login errors - static const struct tagReason { int type; TCHAR *str; } reason[] = { - { GG_FAILURE_RESOLVING, LPGENT("Miranda was unable to resolve the name of the Gadu-Gadu server to its numeric address.") }, - { GG_FAILURE_CONNECTING, LPGENT("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, LPGENT("Received invalid server response.") }, - { GG_FAILURE_READING, LPGENT("The connection with the server was abortively closed during the connection attempt. You may have lost your local network connection.") }, - { GG_FAILURE_WRITING, LPGENT("The connection with the server was abortively closed during the connection attempt. You may have lost your local network connection.") }, - { GG_FAILURE_PASSWORD, LPGENT("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, LPGENT("Connecting to Gadu-Gadu hub failed.") }, - { GG_FAILURE_TLS, LPGENT("Cannot establish secure connection.") }, - { GG_FAILURE_NEED_EMAIL, LPGENT("Server disconnected asking you for changing your e-mail.") }, - { GG_FAILURE_INTRUDER, LPGENT("Too many login attempts with invalid password.") }, - { GG_FAILURE_UNAVAILABLE, LPGENT("Gadu-Gadu servers are now down. Try again later.") }, - { 0, LPGENT("Unknown") } - }; - time_t logonTime = 0; - time_t timeDeviation = db_get_w(NULL, m_szModuleName, GG_KEY_TIMEDEVIATION, GG_KEYDEF_TIMEDEVIATION); - int gg_failno = 0; - - netlog("gg_mainthread(%x): Server Thread Starting", this); -#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 - broadcastnewstatus(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 (db_get_b(NULL, m_szModuleName, 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 = db_get_b(NULL, m_szModuleName, "EraOmnix", 0); - - // Setup proxy - nlus.cbSize = sizeof(nlus); - if (CallService(MS_NETLIB_GETUSERSETTINGS, (WPARAM)netlib, (LPARAM)&nlus)) - { - if (nlus.useProxy) - netlog("gg_mainthread(%x): Using proxy %s:%d.", this, 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 - { - netlog("gg_mainthread(%x): Failed loading proxy settings.", this); - gg_proxy_enabled = 0; - } - - // Check out manual host setting - if (db_get_b(NULL, m_szModuleName, GG_KEY_MANUALHOST, GG_KEYDEF_MANUALHOST)) - { - if (!db_get_s(NULL, m_szModuleName, GG_KEY_SERVERHOSTS, &dbv, DBVT_ASCIIZ)) - { - hostcount = gg_decodehosts(dbv.pszVal, hosts, 64); - DBFreeVariant(&dbv); - } - } - - // Readup password - if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) - { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); - p.password = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - else - { - netlog("gg_mainthread(%x): No password specified. Exiting.", this); - broadcastnewstatus(ID_STATUS_OFFLINE); - return; - } - - // Readup number - if (!(p.uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0))) - { - netlog("gg_mainthread(%x): No Gadu-Gadu number specified. Exiting.", this); - broadcastnewstatus(ID_STATUS_OFFLINE); - mir_free(p.password); - return; - } - - // Readup SSL/TLS setting - if (p.tls = db_get_b(NULL, m_szModuleName, GG_KEY_SSLCONN, GG_KEYDEF_SSLCONN)) - netlog("gg_mainthread(%x): Using TLS/SSL for connections.", this); - - // Gadu-Gadu accepts image sizes upto 255 - p.image_size = 255; - - ////////////////////////////// DCC STARTUP ///////////////////////////// - // Uin is ok so startup dcc if not started already - if (!dcc) - { - hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - dccstart(); - - // Wait for DCC -#ifdef DEBUGMODE - netlog("gg_mainthread(%x): Waiting DCC service to start...", this); -#endif - while (WaitForSingleObjectEx(hEvent, INFINITE, TRUE) != WAIT_OBJECT_0); - CloseHandle(hEvent); hEvent = NULL; - } - // Check if dcc is running and setup forwarding port - if (dcc && db_get_b(NULL, m_szModuleName, GG_KEY_FORWARDING, GG_KEYDEF_FORWARDING)) - { - if (!db_get_s(NULL, m_szModuleName, GG_KEY_FORWARDHOST, &dbv, DBVT_ASCIIZ)) - { - if (!(p.external_addr = gg_dnslookup(this, dbv.pszVal))) - { - TCHAR error[128]; - mir_sntprintf(error, SIZEOF(error), TranslateT("External direct connections hostname %S is invalid. Disabling external host forwarding."), dbv.pszVal); - showpopup(m_tszUserName, error, GG_POPUP_WARNING | GG_POPUP_ALLOW_MSGBOX); - } - else - netlog("gg_mainthread(%x): Loading forwarding host %s and port %d.", dbv.pszVal, p.external_port, this); - if (p.external_addr) p.external_port = db_get_w(NULL, m_szModuleName, GG_KEY_FORWARDPORT, GG_KEYDEF_FORWARDPORT); - DBFreeVariant(&dbv); - } - } - // Setup client port - if (dcc) p.client_port = dcc->port; - -retry: - // Loadup startup status & description - EnterCriticalSection(&modemsg_mutex); - p.status_descr = mir_strdup(getstatusmsg(m_iDesiredStatus)); - p.status = status_m2gg(m_iDesiredStatus, p.status_descr != NULL); - - netlog("gg_mainthread(%x): Connecting with number %d, status %d and description \"%s\".", this, p.uin, m_iDesiredStatus, - p.status_descr ? p.status_descr : ""); - LeaveCriticalSection(&modemsg_mutex); - - // Check manual hosts - if (hostnum < hostcount) - { - if (!(p.server_addr = gg_dnslookup(this, hosts[hostnum].hostname))) - { - TCHAR error[128]; - mir_sntprintf(error, SIZEOF(error), TranslateT("Server hostname %S is invalid. Using default hostname provided by the network."), hosts[hostnum].hostname); - showpopup(m_tszUserName, error, GG_POPUP_WARNING | GG_POPUP_ALLOW_MSGBOX); - } - else - { - p.server_port = hosts[hostnum].port; - netlog("gg_mainthread(%x): Connecting to manually specified host %s (%d.%d.%d.%d) and port %d.", this, - 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, &sock, &gg_failno))) - { - broadcastnewstatus(ID_STATUS_OFFLINE); - // Check if connection attempt wasn't cancelled by the user - if (m_iDesiredStatus != ID_STATUS_OFFLINE) - { - TCHAR error[128], *perror = NULL; - // Lookup for error desciption - if (errno == EACCES) { - for (int i = 0; reason[i].type; i++) if (reason[i].type == gg_failno) { - perror = TranslateTS(reason[i].str); - break; - } - } - if (!perror) { - mir_sntprintf(error, SIZEOF(error), TranslateT("Connection cannot be established because of error:\n\t%s"), _tcserror(errno)); - perror = error; - } - netlog("gg_mainthread(%x): %s", this, perror); - if (db_get_b(NULL, m_szModuleName, GG_KEY_SHOWCERRORS, GG_KEYDEF_SHOWCERRORS)) - showpopup(m_tszUserName, 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 - && (db_get_b(NULL, m_szModuleName, GG_KEY_ARECONNECT, GG_KEYDEF_ARECONNECT) || (hostnum < hostcount - 1))) - { - DWORD dwInterval = db_get_dw(NULL, m_szModuleName, GG_KEY_RECONNINTERVAL, GG_KEYDEF_RECONNINTERVAL), dwResult; - BOOL bRetry = TRUE; - - hConnStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - dwResult = WaitForSingleObjectEx(hConnStopEvent, dwInterval, TRUE); - if ((dwResult == WAIT_OBJECT_0 && m_iDesiredStatus == ID_STATUS_OFFLINE) - || (dwResult == WAIT_IO_COMPLETION && Miranda_Terminated())) - bRetry = FALSE; - CloseHandle(hConnStopEvent); - hConnStopEvent = NULL; - - // Reconnect to the next server on the list - if (bRetry) - { - if (hostnum < hostcount - 1) hostnum++; - mir_free(p.status_descr); - broadcastnewstatus(ID_STATUS_CONNECTING); - goto retry; - } - } - // We cannot do more about this - EnterCriticalSection(&modemsg_mutex); - m_iDesiredStatus = ID_STATUS_OFFLINE; - LeaveCriticalSection(&modemsg_mutex); - } - else - netlog("gg_mainthread(%x)): Connection attempt cancelled by the user.", this); - } - else - { - // Successfully connected - logonTime = time(NULL); - db_set_dw(NULL, m_szModuleName, GG_KEY_LOGONTIME, logonTime); - EnterCriticalSection(&sess_mutex); - sess = sess; - LeaveCriticalSection(&sess_mutex); - // Subscribe users status notifications - notifyall(); - // Set startup status - if (m_iDesiredStatus != status_gg2m(p.status)) - refreshstatus(m_iDesiredStatus); - else - { - broadcastnewstatus(m_iDesiredStatus); - // Change status of the contact with our own UIN (if got yourself added to the contact list) - changecontactstatus(p.uin, p.status, p.status_descr, 0, 0, 0, 0); - } - if (check_first_conn) // First connection to the account - { - // Start search for user data - GetInfo(NULL, 0); - // Fetch user avatar - getUserAvatar(); - check_first_conn = 0; - } - } - - ////////////////////////////////////////////////////////////////////////////////// - // Main loop - while(isonline()) - { - // Connection broken/closed - if (!(e = gg_watch_fd(sess))) - { - netlog("gg_mainthread(%x): Connection closed.", this); - EnterCriticalSection(&sess_mutex); - gg_free_session(sess); - sess = NULL; - LeaveCriticalSection(&sess_mutex); - break; - } - else - netlog("gg_mainthread(%x): Event: %s", this, 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(&sess_mutex); - gg_free_session(sess); - sess = NULL; - LeaveCriticalSection(&sess_mutex); - break; - - // Client allowed to disconnect - case GG_EVENT_DISCONNECT_ACK: - // Send logoff - gg_logoff(sess); - break; - - // Received ackowledge - case GG_EVENT_ACK: - if (e->event.ack.seq && e->event.ack.recipient) - { - ProtoBroadcastAck(m_szModuleName, getcontact((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; - changecontactstatus(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)db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0); - int i; - for(i = 0; e->event.notify60[i].uin; i++) { - if (e->event.notify60[i].uin == uin) continue; - changecontactstatus(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); - requestAvatar(getcontact(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) - { - netlog("gg_mainthread(%x): Got user info.", this); - // Store next search UIN - if (res->seq == GG_SEQ_SEARCH) - next_uin = gg_pubdir50_next(res); - } - else if (e->type == GG_EVENT_PUBDIR50_READ) - { - netlog("gg_mainthread(%x): Got owner info.", this); - } - else if (e->type == GG_EVENT_PUBDIR50_WRITE) - { - netlog("gg_mainthread(%x): Public directory save succesful.", this); - // Update user details - GetInfo(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 : getcontact(uin, 0, 0, NULL); - netlog("gg_mainthread(%x): Search result for uin %d, seq %d.", this, uin, res->seq); - if (res->seq == GG_SEQ_SEARCH) - { - char strFmt1[64]; - char strFmt2[64]; - - mir_snprintf(strFmt2, sizeof(strFmt2), "%s", (char *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, status_gg2m(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)); - } - } - - GGSEARCHRESULT sr; - memset(&sr, 0, sizeof(sr)); - sr.cbSize = sizeof(sr); - sr.nick = mir_a2t(__nick); - sr.firstName = mir_a2t(__firstname); - sr.lastName = mir_a2t(__lastname); - sr.email = mir_a2t(strFmt2); - sr.id = mir_a2t(_ultoa(uin, strFmt1, 10)); - sr.uin = uin; - ProtoBroadcastAck(m_szModuleName, NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE) 1, (LPARAM)&sr); - mir_free(sr.nick); - mir_free(sr.firstName); - mir_free(sr.lastName); - mir_free(sr.email); - mir_free(sr.id); - } - - 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)) - db_set_s(hContact, m_szModuleName, GG_KEY_NICK, __nick); - - if (__nick) - db_set_s(hContact, m_szModuleName, "NickName", __nick); - else if (res->seq == GG_SEQ_CHINFO) - db_unset(NULL, m_szModuleName, "NickName"); - - // Change other info - if (__city) - db_set_s(hContact, m_szModuleName, "City", __city); - else if (res->seq == GG_SEQ_CHINFO) - db_unset(NULL, m_szModuleName, "City"); - - if (__firstname) - db_set_s(hContact, m_szModuleName, "FirstName", __firstname); - else if (res->seq == GG_SEQ_CHINFO) - db_unset(NULL, m_szModuleName, "FirstName"); - - if (__lastname) - db_set_s(hContact, m_szModuleName, "LastName", __lastname); - else if (res->seq == GG_SEQ_CHINFO) - db_unset(NULL, m_szModuleName, "LastName"); - - if (__familyname) - db_set_s(hContact, m_szModuleName, "FamilyName", __familyname); - else if (res->seq == GG_SEQ_CHINFO) - db_unset(NULL, m_szModuleName, "FamilyName"); - - if (__origincity) - db_set_s(hContact, m_szModuleName, "CityOrigin", __origincity); - else if (res->seq == GG_SEQ_CHINFO) - db_unset(NULL, m_szModuleName, "CityOrigin"); - - if (__birthyear) - { - time_t t = time(NULL); - struct tm *lt = localtime(&t); - int br = atoi(__birthyear); - if (br > 0) - { - db_set_w(hContact, m_szModuleName, "Age", (WORD)(lt->tm_year + 1900 - br)); - db_set_w(hContact, m_szModuleName, "BirthYear", (WORD)br); - } - } - else if (res->seq == GG_SEQ_CHINFO) - { - db_unset(NULL, m_szModuleName, "Age"); - db_unset(NULL, m_szModuleName, "BirthYear"); - } - - // Gadu-Gadu Male <-> Female - if (__gender) - { - if (res->seq == GG_SEQ_CHINFO) - db_set_b(hContact, m_szModuleName, "Gender", - (BYTE)(!strcmp(__gender, GG_PUBDIR50_GENDER_SET_MALE) ? 'M' : - (!strcmp(__gender, GG_PUBDIR50_GENDER_SET_FEMALE) ? 'F' : '?'))); - else - db_set_b(hContact, m_szModuleName, "Gender", - (BYTE)(!strcmp(__gender, GG_PUBDIR50_GENDER_MALE) ? 'M' : - (!strcmp(__gender, GG_PUBDIR50_GENDER_FEMALE) ? 'F' : '?'))); - } - else if (res->seq == GG_SEQ_CHINFO) - { - db_unset(NULL, m_szModuleName, "Gender"); - } - - netlog("gg_mainthread(%x): Setting user info for uin %d.", this, uin); - ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, (HANDLE) 1, 0); - } - } - } - if (res->seq == GG_SEQ_SEARCH) - ProtoBroadcastAck(m_szModuleName, NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE) 1, 0); - break; - } - - // Status (deprecated) - case GG_EVENT_STATUS: - changecontactstatus(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 = getcontact(e->event.status60.uin, 0, 0, NULL); - int oldstatus = db_get_w(hContact, m_szModuleName, GG_KEY_STATUS, (WORD)ID_STATUS_OFFLINE); - uin_t uin = (uin_t)db_get_dw(NULL, m_szModuleName, 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(e->event.status60.status); - CallProtoService(m_szModuleName, PS_SETAWAYMSG, iStatus, (LPARAM)e->event.status60.descr); - CallProtoService(m_szModuleName, PS_SETSTATUS, iStatus, 0); - } - - changecontactstatus(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 && db_get_w(hContact, m_szModuleName, GG_KEY_STATUS, (WORD)ID_STATUS_OFFLINE) != ID_STATUS_OFFLINE) - requestAvatar(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) { - parsecontacts(e->event.userlist.reply); - MessageBox(NULL, TranslateT("List import successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION); - } - break; - - case GG_USERLIST_PUT_REPLY: - if (is_list_remove) - MessageBox(NULL, TranslateT("List remove successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION); - else - MessageBox(NULL, TranslateT("List export successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION); - break; - } - break; - - // Received message - case GG_EVENT_MSG: - // This is CTCP request - if ((e->event.msg.msgclass & GG_CLASS_CTCP)) - { - dccconnect(e->event.msg.sender); - } - // Check if not conference and block - else if (!e->event.msg.recipients_count || gc_enabled) - { - // Check if groupchat - if (e->event.msg.recipients_count && gc_enabled && !db_get_b(NULL, m_szModuleName, GG_KEY_IGNORECONF, GG_KEYDEF_IGNORECONF)) - { - char *chat = gc_getchat(e->event.msg.sender, e->event.msg.recipients, e->event.msg.recipients_count); - if (chat) - { - char id[32]; - GCDEST gcdest = {m_szModuleName, 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) getcontact(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; - netlog("gg_mainthread(%x): Conference message to room %s & id %s.", this, 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 = getcontact(e->event.msg.sender, 1, 0, NULL); - ccs.lParam = (LPARAM)⪯ - 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 && - db_get_b(NULL, m_szModuleName, GG_KEY_IMGRECEIVE, GG_KEYDEF_IMGRECEIVE) && - !(db_get_dw(getcontact(e->event.msg.sender, 1, 0, NULL), "Ignore", "Mask1", 0) & IGNOREEVENT_MESSAGE)) - { - char *formats = (char*)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(&sess_mutex); - gg_image_request(sess, e->event.msg.sender, image->size, image->crc32); - LeaveCriticalSection(&sess_mutex); - - netlog("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 && gc_enabled && !db_get_b(NULL, m_szModuleName, GG_KEY_IGNORECONF, GG_KEYDEF_IGNORECONF)) - { - char *chat = gc_getchat(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 = {m_szModuleName, chat, GC_EVENT_MESSAGE}; - GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; - - UIN2ID( db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0), id); - - gcevent.pszUID = id; - gcevent.pszText = e->event.multilogon_msg.message; - if (!db_get_s(NULL, m_szModuleName, GG_KEY_NICK, &dbv, DBVT_ASCIIZ)) - gcevent.pszNick = dbv.pszVal; - else - gcevent.pszNick = Translate("Me"); - gcevent.time = e->event.multilogon_msg.time; - gcevent.bIsMe = 1; - gcevent.dwFlags = GCEF_ADDTOLOG; - netlog("gg_mainthread(%x): Sent conference message to room %s.", this, 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 = m_szModuleName; - 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)getcontact(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; - netlog("gg_mainthread(): Concurrent sessions count: %d.", e->event.multilogon_info.count); - if (e->event.multilogon_info.count > 0) - iIndexes = (int*)mir_calloc(e->event.multilogon_info.count * sizeof(int)); - EnterCriticalSection(&sessions_mutex); - for (l = 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(sessions, 0); - sessions = NULL; - for (i = 0; i < e->event.multilogon_info.count; i++) - { - gg_multilogon_session* sess = (gg_multilogon_session*)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(&sessions, sess, 0); - } - LeaveCriticalSection(&sessions_mutex); - sessions_updatedlg(); - if (ServiceExists(MS_POPUP_ADDPOPUPCLASS)) - { - const TCHAR* szText = time(NULL) - logonTime > 3 - ? TranslateT("You have logged in at another location") - : TranslateT("You are logged in at another location"); - for (i = 0; i < e->event.multilogon_info.count; i++) - { - TCHAR szMsg[MAX_SECONDLINE]; - if (iIndexes && iIndexes[i]) - continue; - - mir_sntprintf(szMsg, SIZEOF(szMsg), _T("%s (%s)"), szText, - *e->event.multilogon_info.sessions[i].name != '\0' ? - _A2T(e->event.multilogon_info.sessions[i].name) : TranslateT("Unknown client")); - showpopup(m_tszUserName, 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 = getcontact(e->event.image_reply.sender, 1, 0, NULL); - void *img = (void *)img_loadpicture(e, 0); - - if (!img) - break; - - if (db_get_b(NULL, m_szModuleName, GG_KEY_IMGMETHOD, GG_KEYDEF_IMGMETHOD) == 1 || img_opened(e->event.image_reply.sender)) - { - img_display(hContact, img); - } - else if (db_get_b(NULL, m_szModuleName, GG_KEY_IMGMETHOD, GG_KEYDEF_IMGMETHOD) == 2) - { - img_displayasmsg(hContact, img); - } - else - { - CLISTEVENT cle = {0}; - char service[128]; - mir_snprintf(service, sizeof(service), GGS_RECVIMAGE, m_szModuleName); - - 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: - img_sendonrequest(e); - break; - - // Incoming direct connection - case GG_EVENT_DCC7_NEW: - { - struct gg_dcc7 *dcc7 = e->event.dcc7_new; - netlog("gg_mainthread(%x): Incoming direct connection.", this); - dcc7->contact = getcontact(dcc7->peer_uin, 0, 0, NULL); - - // Check if user is on the list and if it is my uin - if (!dcc7->contact || db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, -1) != dcc7->uin) { - gg_dcc7_free(dcc7); - e->event.dcc7_new = NULL; - break; - } - - // Add to waiting transfers - EnterCriticalSection(&ft_mutex); - list_add(&transfers, dcc7, 0); - LeaveCriticalSection(&ft_mutex); - - ////////////////////////////////////////////////// - // Add file recv request - - netlog("gg_mainthread(%x): Client: %d, File ack filename \"%s\" size %d.", this, dcc7->peer_uin, - dcc7->filename, dcc7->size); - - TCHAR* filenameT = mir_utf8decodeT((char*)dcc7->filename); - - PROTORECVFILET pre = {0}; - pre.flags = PREF_TCHAR; - pre.fileCount = 1; - pre.timestamp = time(NULL); - pre.tszDescription = filenameT; - pre.ptszFiles = &filenameT; - pre.lParam = (LPARAM)dcc7; - - CCSDATA ccs = { dcc7->contact, PSR_FILE, 0, (LPARAM)&pre }; - CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); - - mir_free(filenameT); - 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) - { - netlog("gg_mainthread(%x): File transfer denied by client %d (reason = %d).", this, dcc7->peer_uin, e->event.dcc7_reject.reason); - ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DENIED, dcc7, 0); - - // Remove from watches and free - EnterCriticalSection(&ft_mutex); - list_remove(&watches, dcc7, 0); - LeaveCriticalSection(&ft_mutex); - gg_dcc7_free(dcc7); - } - else - { - netlog("gg_mainthread(%x): File transfer aborted by client %d.", this, dcc7->peer_uin); - - // Remove transfer from waiting list - EnterCriticalSection(&ft_mutex); - list_remove(&transfers, dcc7, 0); - LeaveCriticalSection(&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: - netlog("gg_mainthread(%x): Client: %d, Handshake error.", this, dcc7 ? dcc7->peer_uin : 0); - break; - case GG_ERROR_DCC7_NET: - netlog("gg_mainthread(%x): Client: %d, Network error.", this, dcc7 ? dcc7->peer_uin : 0); - break; - case GG_ERROR_DCC7_FILE: - netlog("gg_mainthread(%x): Client: %d, File read/write error.", this, dcc7 ? dcc7->peer_uin : 0); - break; - case GG_ERROR_DCC7_EOF: - netlog("gg_mainthread(%x): Client: %d, End of file/connection error.", this, dcc7 ? dcc7->peer_uin : 0); - break; - case GG_ERROR_DCC7_REFUSED: - netlog("gg_mainthread(%x): Client: %d, Connection refused error.", this, dcc7 ? dcc7->peer_uin : 0); - break; - case GG_ERROR_DCC7_RELAY: - netlog("gg_mainthread(%x): Client: %d, Relay connection error.", this, dcc7 ? dcc7->peer_uin : 0); - break; - default: - netlog("gg_mainthread(%x): Client: %d, Unknown error.", this, dcc7 ? dcc7->peer_uin : 0); - } - if (!dcc7) break; - - // Remove from watches - list_remove(&watches, dcc7, 0); - - // Close file & fail - if (dcc7->file_fd != -1) - { - _close(dcc7->file_fd); - dcc7->file_fd = -1; - } - - if (dcc7->contact) - ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0); - - // Free dcc - gg_dcc7_free(dcc7); - } - break; - - case GG_EVENT_XML_ACTION: - if (db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) { - HXML hXml; - TCHAR *xmlAction; - TCHAR *tag; - - xmlAction = mir_a2t(e->event.xml_action.data); - tag = mir_a2t("events"); - hXml = xi.parseString(xmlAction, 0, tag); - - if (hXml != NULL) { - HXML node; - char *type, *sender; - - mir_free(tag); - tag = mir_a2t("event/type"); - node = xi.getChildByPath(hXml, tag, 0); - type = node != NULL ? mir_t2a(xi.getText(node)) : NULL; - - mir_free(tag); - tag = mir_a2t("event/sender"); - node = xi.getChildByPath(hXml, tag, 0); - sender = node != NULL ? mir_t2a(xi.getText(node)) : NULL; - netlog("gg_mainthread(%x): XML Action type: %s.", this, type != NULL ? type : "unknown"); - // Avatar change notify - if (type != NULL && !strcmp(type, "28")) { - netlog("gg_mainthread(%x): Client %s changed his avatar.", this, sender); - requestAvatar(getcontact(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 = getcontact(e->event.typing_notification.uin, 0, 0, NULL); -#ifdef DEBUGMODE - netlog("gg_mainthread(%x): Typing notification from %d (%d).", this, - 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); - } - - broadcastnewstatus(ID_STATUS_OFFLINE); - setalloffline(); - db_set_dw(NULL, m_szModuleName, GG_KEY_LOGONTIME, 0); - - // If it was unwanted disconnection reconnect - if (m_iDesiredStatus != ID_STATUS_OFFLINE - && db_get_b(NULL, m_szModuleName, GG_KEY_ARECONNECT, GG_KEYDEF_ARECONNECT)) - { - netlog("gg_mainthread(%x): Unintentional disconnection detected. Going to reconnect...", this); - hostnum = 0; - broadcastnewstatus(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(&sessions_mutex); - for (l = 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(sessions, 0); - sessions = NULL; - LeaveCriticalSection(&sessions_mutex); - } - - // Stop dcc server - pth_dcc.dwThreadId = 0; -#ifdef DEBUGMODE - netlog("gg_mainthread(%x): Waiting until DCC Server Thread finished, if needed.", this); -#endif - threadwait(&pth_dcc); - - netlog("gg_mainthread(%x): Server Thread Ending", this); - return; -} - -//////////////////////////////////////////////////////////// -// Change status function -void GGPROTO::broadcastnewstatus(int newStatus) -{ - int oldStatus; - - EnterCriticalSection(&modemsg_mutex); - oldStatus = m_iStatus; - if (oldStatus == newStatus) - { - LeaveCriticalSection(&modemsg_mutex); - return; - } - m_iStatus = newStatus; - LeaveCriticalSection(&modemsg_mutex); - - ProtoBroadcastAck(m_szModuleName, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldStatus, newStatus); - - netlog("gg_broadcastnewstatus(): Broadcast new status: %d.", newStatus); -} - -//////////////////////////////////////////////////////////// -// When contact is deleted -int GGPROTO::contactdeleted(WPARAM wParam, LPARAM lParam) -{ - HANDLE hContact = (HANDLE) wParam; - uin_t uin; int type; - DBVARIANT dbv; - - uin = (uin_t)db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0); - type = db_get_b(hContact, m_szModuleName, "ChatRoom", 0); - - // Terminate conference if contact is deleted - if (type && !db_get_s(hContact, m_szModuleName, "ChatRoomID", &dbv, DBVT_ASCIIZ) && gc_enabled) - { - GCDEST gcdest = {m_szModuleName, dbv.pszVal, GC_EVENT_CONTROL}; - GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; - GGGC *chat = gc_lookup(dbv.pszVal); - - netlog("gg_gc_event(): Terminating chat %x, id %s from contact list...", chat, dbv.pszVal); - if (chat) - { - // Destroy chat entry - free(chat->recipients); - list_remove(&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 && isonline()) - { - EnterCriticalSection(&sess_mutex); - gg_remove_notify_ex(sess, uin, GG_USER_NORMAL); - LeaveCriticalSection(&sess_mutex); - } - - return 0; -} - -//////////////////////////////////////////////////////////// -// When db settings changed - -int GGPROTO::dbsettingchanged(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 (!isonline()) - return 0; - - // If contact has been blocked - if (!strcmp(cws->szModule, m_szModuleName) && !strcmp(cws->szSetting, GG_KEY_BLOCK)) - { - notifyuser(hContact, 1); - return 0; - } - - // Contact is being renamed - if (gc_enabled && !strcmp(cws->szModule, m_szModuleName) && !strcmp(cws->szSetting, GG_KEY_NICK) - && cws->value.pszVal) - { - // Groupchat window contact is being renamed - DBVARIANT dbv; - int type = db_get_b(hContact, m_szModuleName, "ChatRoom", 0); - if (type && !db_get_s(hContact, m_szModuleName, "ChatRoomID", &dbv, DBVT_ASCIIZ)) - { - // Most important... check redundancy (fucking cascading) - static int cascade = 0; - if (!cascade && dbv.pszVal) - { - GCDEST gcdest = {m_szModuleName, dbv.pszVal, GC_EVENT_CHANGESESSIONAME}; - GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; - gcevent.pszText = cws->value.pszVal; - netlog("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 - gc_changenick(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) - db_set_s(hContact, m_szModuleName, GG_KEY_NICK, cws->value.pszVal); - - // If not on list changed - if (!strcmp(cws->szSetting, "NotOnList")) - { - if (db_get_b(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)) - notifyuser(hContact, 1); - } - } - return 0; -} - -//////////////////////////////////////////////////////////// -// All users set offline - -void GGPROTO::setalloffline() -{ - netlog("gg_setalloffline(): Setting buddies offline"); - db_set_w(NULL, m_szModuleName, GG_KEY_STATUS, ID_STATUS_OFFLINE); - HANDLE hContact = db_find_first(); - while (hContact) - { - char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); - if (szProto != NULL && !strcmp(szProto, m_szModuleName)) - { - db_set_w(hContact, m_szModuleName, GG_KEY_STATUS, ID_STATUS_OFFLINE); - // Clear IP and port settings - db_unset(hContact, m_szModuleName, GG_KEY_CLIENTIP); - db_unset(hContact, m_szModuleName, GG_KEY_CLIENTPORT); - // Delete status descr - db_unset(hContact, "CList", GG_KEY_STATUSDESCR); - } - hContact = db_find_next(hContact); - } -#ifdef DEBUGMODE - netlog("gg_setalloffline(): End"); -#endif -} - -//////////////////////////////////////////////////////////// -// All users set offline - -void GGPROTO::notifyuser(HANDLE hContact, int refresh) -{ - uin_t uin; - if (!hContact) return; - if (isonline() && (uin = (uin_t)db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0))) - { - // Check if user should be invisible - // Or be blocked ? - if ((db_get_w(hContact, m_szModuleName, GG_KEY_APPARENT, (WORD) ID_STATUS_ONLINE) == ID_STATUS_OFFLINE) || - db_get_b(hContact, "CList", "NotOnList", 0)) - { - mir_cslock l(sess_mutex); - if (refresh) { - gg_remove_notify_ex(sess, uin, GG_USER_NORMAL); - gg_remove_notify_ex(sess, uin, GG_USER_BLOCKED); - } - - gg_add_notify_ex(sess, uin, GG_USER_OFFLINE); - } - else if (db_get_b(hContact, m_szModuleName, GG_KEY_BLOCK, 0)) - { - mir_cslock l(sess_mutex); - if (refresh) - gg_remove_notify_ex(sess, uin, GG_USER_OFFLINE); - - gg_add_notify_ex(sess, uin, GG_USER_BLOCKED); - } - else { - mir_cslock l(sess_mutex); - if (refresh) - gg_remove_notify_ex(sess, uin, GG_USER_BLOCKED); - - gg_add_notify_ex(sess, uin, GG_USER_NORMAL); - } - } -} - -void GGPROTO::notifyall() -{ - HANDLE hContact; - char *szProto; - int count = 0, cc = 0; - uin_t *uins; - char *types; - - netlog("gg_notifyall(): Subscribing notification to all users"); - // Readup count - hContact = db_find_first(); - while (hContact) - { - szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); - if (szProto != NULL && !strcmp(szProto, m_szModuleName)) count ++; - hContact = db_find_next(hContact); - } - - // 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 (isonline()) - { - EnterCriticalSection(&sess_mutex); - gg_notify_ex(sess, NULL, NULL, 0); - LeaveCriticalSection(&sess_mutex); - } - return; - } - uins = (uin_t*)calloc(sizeof(uin_t), count); - types = (char*)calloc(sizeof(char), count); - - hContact = db_find_first(); - while (hContact && cc < count) - { - szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); - if (szProto != NULL && !strcmp(szProto, m_szModuleName) && (uins[cc] = db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0))) - { - if ((db_get_w(hContact, m_szModuleName, GG_KEY_APPARENT, (WORD) ID_STATUS_ONLINE) == ID_STATUS_OFFLINE) || - db_get_b(hContact, "CList", "NotOnList", 0)) - types[cc] = GG_USER_OFFLINE; - else if (db_get_b(hContact, m_szModuleName, GG_KEY_BLOCK, 0)) - types[cc] = GG_USER_BLOCKED; - else - types[cc] = GG_USER_NORMAL; - cc ++; - } - hContact = db_find_next(hContact); - } - if (cc < count) count = cc; - - // Send notification - if (isonline()) - { - EnterCriticalSection(&sess_mutex); - gg_notify_ex(sess, uins, types, count); - LeaveCriticalSection(&sess_mutex); - } - - // Free variables - free(uins); free(types); -} - -//////////////////////////////////////////////////////////// -// Get contact by uin - -HANDLE GGPROTO::getcontact(uin_t uin, int create, int inlist, TCHAR *szNick) -{ - // Look for contact in DB - HANDLE hContact = db_find_first(); - while (hContact) { - char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); - if (szProto != NULL && !strcmp(szProto, m_szModuleName)) { - if ((uin_t)db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0) == uin - && db_get_b(hContact, m_szModuleName, "ChatRoom", 0) == 0) - { - if (inlist) { - db_unset(hContact, "CList", "NotOnList"); - db_unset(hContact, "CList", "Hidden"); - } - return hContact; - } - } - hContact = db_find_next(hContact); - } - if (!create) return NULL; - - hContact = (HANDLE) CallService(MS_DB_CONTACT_ADD, 0, 0); - if (!hContact) { - netlog("gg_getcontact(): Failed to create Gadu-Gadu contact %s", szNick); - return NULL; - } - - if (CallService(MS_PROTO_ADDTOCONTACT, (WPARAM) hContact, (LPARAM) m_szModuleName) != 0) { - // For some reason we failed to register the protocol for this contact - CallService(MS_DB_CONTACT_DELETE, (WPARAM) hContact, 0); - netlog("Failed to register GG contact %d", uin); - return NULL; - } - - netlog("gg_getcontact(): Added buddy: %d", uin); - if (!inlist) - db_set_b(hContact, "CList", "NotOnList", 1); - - db_set_dw(hContact, m_szModuleName, GG_KEY_UIN, (DWORD) uin); - db_set_w(hContact, m_szModuleName, GG_KEY_STATUS, ID_STATUS_OFFLINE); - - // If nick specified use it - if (szNick) - db_set_ts(hContact, m_szModuleName, GG_KEY_NICK, szNick); - else if (isonline()) { - 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(&sess_mutex); - gg_pubdir50(sess, req); - LeaveCriticalSection(&sess_mutex); - gg_pubdir50_free(req); - db_set_s(hContact, m_szModuleName, GG_KEY_NICK, ditoa(uin)); - netlog("gg_getcontact(): Search for nick on uin: %d", uin); - } - } - - // Add to notify list and pull avatar for the new contact - if (isonline()) - { - PROTO_AVATAR_INFORMATIONT pai = {0}; - - EnterCriticalSection(&sess_mutex); - gg_add_notify_ex(sess, uin, (char)(inlist ? GG_USER_NORMAL : GG_USER_OFFLINE)); - LeaveCriticalSection(&sess_mutex); - - pai.cbSize = sizeof(pai); - pai.hContact = hContact; - getavatarinfo((WPARAM)GAIF_FORCE, (LPARAM)&pai); - - // Change status of the contact with our own UIN (if got yourself added to the contact list) - if (db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0) == uin) { - char *szMsg; - EnterCriticalSection(&modemsg_mutex); - szMsg = mir_strdup(getstatusmsg(m_iStatus)); - LeaveCriticalSection(&modemsg_mutex); - changecontactstatus(uin, status_m2gg(m_iStatus, szMsg != NULL), szMsg, 0, 0, 0, 0); - mir_free(szMsg); - } - } - - // TODO server side list & add buddy - return hContact; -} - -//////////////////////////////////////////////////////////// -// Status conversion - -int GGPROTO::status_m2gg(int status, int descr) -{ - // check frends only - int mask = db_get_b(NULL, m_szModuleName, 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 GGPROTO::status_gg2m(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 && db_get_b(NULL, m_szModuleName, 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 GGPROTO::changecontactstatus(uin_t uin, int status, const char *idescr, int time, uint32_t remote_ip, uint16_t remote_port, uint32_t version) -{ - HANDLE hContact = getcontact(uin, 0, 0, NULL); - - // Check if contact is on list - if (!hContact) return; - - // Write contact status - db_set_w(hContact, m_szModuleName, GG_KEY_STATUS, (WORD)status_gg2m(status)); - - // Check if there's description and if it's not empty - if (idescr && *idescr) - { - netlog("gg_changecontactstatus(): Saving for %d status descr \"%s\".", uin, idescr); - db_set_s(hContact, "CList", GG_KEY_STATUSDESCR, idescr); - } - else - // Remove status if there's nothing - db_unset(hContact, "CList", GG_KEY_STATUSDESCR); - - // Store contact ip and port - if (remote_ip) db_set_dw(hContact, m_szModuleName, GG_KEY_CLIENTIP, (DWORD) swap32(remote_ip)); - if (remote_port) db_set_w(hContact, m_szModuleName, GG_KEY_CLIENTPORT, (WORD) remote_port); - if (version) - { - char sversion[48]; - db_set_dw(hContact, m_szModuleName, GG_KEY_CLIENTVERSION, (DWORD) version); - mir_snprintf(sversion, sizeof(sversion), "%sGadu-Gadu %s", (version & 0x00ffffff) > 0x2b ? "Nowe " : "", gg_version2string(version)); - db_set_s(hContact, m_szModuleName, "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.cpp b/protocols/Gadu-Gadu/dialogs.cpp deleted file mode 100644 index 345624715b..0000000000 --- a/protocols/Gadu-Gadu/dialogs.cpp +++ /dev/null @@ -1,1016 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 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. -//////////////////////////////////////////////////////////////////////////////// - -#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; - GetLocaleInfoA(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; lstrcpyA(str, "???"); break; - } - } - - if (disableIfUndef) { - EnableWindow(GetDlgItem(hwndDlg, idCtrl), !unspecified); - if (unspecified) - SetDlgItemText(hwndDlg, idCtrl, TranslateT("")); - else - SetDlgItemTextA(hwndDlg, idCtrl, pstr); - } - else { - EnableWindow(GetDlgItem(hwndDlg, idCtrl), TRUE); - if (!unspecified) - SetDlgItemTextA(hwndDlg, idCtrl, pstr); - } - DBFreeVariant(&dbv); -} - -//////////////////////////////////////////////////////////////////////////////// -// Options Page : Init - -int GGPROTO::options_init(WPARAM wParam, LPARAM lParam) -{ - OPTIONSDIALOGPAGE odp = { 0 }; - odp.cbSize = sizeof(odp); - odp.flags = ODPF_TCHAR; - odp.position = 1003000; - odp.hInstance = hInstance; - odp.ptszGroup = LPGENT("Network"); - odp.ptszTitle = m_tszUserName; - odp.dwInitParam = (LPARAM)this; - - odp.ptszTab = LPGENT("General"); - odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_GG_GENERAL); - odp.pfnDlgProc = gg_genoptsdlgproc; - odp.flags = ODPF_TCHAR | ODPF_BOLDGROUPS | ODPF_DONTTRANSLATE; - Options_AddPage(wParam, &odp); - - odp.ptszTab = LPGENT("Conference"); - odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_GG_CONFERENCE); - odp.pfnDlgProc = gg_confoptsdlgproc; - Options_AddPage(wParam, &odp); - - odp.ptszTab = LPGENT("Advanced"); - odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_GG_ADVANCED); - odp.pfnDlgProc = gg_advoptsdlgproc; - odp.flags |= ODPF_EXPERTONLY; - Options_AddPage(wParam, &odp); - - return 0; -} - -//////////////////////////////////////////////////////////////////////////////// -// Check if new user data has been filled in for specified account -void GGPROTO::checknewuser(uin_t uin, const char* passwd) -{ - char oldpasswd[128]; - DBVARIANT dbv; - uin_t olduin = (uin_t)db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0); - - oldpasswd[0] = '\0'; - if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) - { - if (dbv.pszVal) strcpy(oldpasswd, dbv.pszVal); - DBFreeVariant(&dbv); - } - - if (uin > 0 && strlen(passwd) > 0 && (uin != olduin || strcmp(oldpasswd, passwd))) - check_first_conn = 1; -} - -//////////////////////////////////////////////////////////////////////////////// -// Options Page : Proc - -static void gg_optsdlgcheck(HWND hwndDlg) -{ - TCHAR text[128]; - GetDlgItemText(hwndDlg, IDC_UIN, text, SIZEOF(text)); - if (text[0]) { - GetDlgItemText(hwndDlg, IDC_EMAIL, text, SIZEOF(text)); - if (text[0]) - 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) -{ - GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - switch (msg) { - case WM_INITDIALOG: - { - DBVARIANT dbv; - DWORD num; - GGPROTO *gg = (GGPROTO *)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); - - TranslateDialogDefault(hwndDlg); - if (num = db_get_dw(NULL, gg->m_szModuleName, GG_KEY_UIN, 0)) - { - SetDlgItemTextA(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 (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); - SetDlgItemTextA(hwndDlg, IDC_PASSWORD, dbv.pszVal); - DBFreeVariant(&dbv); - } - if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, &dbv, DBVT_ASCIIZ)) { - SetDlgItemTextA(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, db_get_b(NULL, gg->m_szModuleName, GG_KEY_FRIENDSONLY, GG_KEYDEF_FRIENDSONLY)); - CheckDlgButton(hwndDlg, IDC_SHOWINVISIBLE, db_get_b(NULL, gg->m_szModuleName, GG_KEY_SHOWINVISIBLE, GG_KEYDEF_SHOWINVISIBLE)); - CheckDlgButton(hwndDlg, IDC_LEAVESTATUSMSG, db_get_b(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUSMSG, GG_KEYDEF_LEAVESTATUSMSG)); - if (gg->gc_enabled) - CheckDlgButton(hwndDlg, IDC_IGNORECONF, db_get_b(NULL, gg->m_szModuleName, GG_KEY_IGNORECONF, GG_KEYDEF_IGNORECONF)); - else - { - EnableWindow(GetDlgItem(hwndDlg, IDC_IGNORECONF), FALSE); - CheckDlgButton(hwndDlg, IDC_IGNORECONF, TRUE); - } - CheckDlgButton(hwndDlg, IDC_IMGRECEIVE, db_get_b(NULL, gg->m_szModuleName, GG_KEY_IMGRECEIVE, GG_KEYDEF_IMGRECEIVE)); - CheckDlgButton(hwndDlg, IDC_SHOWLINKS, db_get_b(NULL, gg->m_szModuleName, GG_KEY_SHOWLINKS, GG_KEYDEF_SHOWLINKS)); - CheckDlgButton(hwndDlg, IDC_ENABLEAVATARS, db_get_b(NULL, gg->m_szModuleName, 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)TranslateT("")); // 0 - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)TranslateT("Online")); // ID_STATUS_ONLINE - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)TranslateT("Away")); // ID_STATUS_AWAY - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)TranslateT("DND")); // ID_STATUS_DND - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)TranslateT("Free for chat")); // ID_STATUS_FREECHAT - SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)TranslateT("Invisible")); // ID_STATUS_INVISIBLE - switch(db_get_w(NULL, gg->m_szModuleName, 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)TranslateT("System tray icon")); - SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_ADDSTRING, 0, (LPARAM)TranslateT("Popup window")); - SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_ADDSTRING, 0, (LPARAM)TranslateT("Message with [img] BBCode")); - SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_SETCURSEL, - db_get_b(NULL, gg->m_szModuleName, 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; - GetDlgItemTextA(hwndDlg, IDC_UIN, email, sizeof(email)); - uin = atoi(email); - GetDlgItemTextA(hwndDlg, IDC_EMAIL, email, sizeof(email)); - if (!strlen(email)) - MessageBox(NULL, TranslateT("You need to specify your registration e-mail first."), - gg->m_tszUserName, MB_OK | MB_ICONEXCLAMATION); - else if (MessageBox(NULL, - TranslateT("Your password will be sent to your registration e-mail.\nDo you want to continue ?"), - gg->m_tszUserName, - MB_OKCANCEL | MB_ICONQUESTION) == IDOK) - gg->remindpassword(uin, email); - return FALSE; - } - case IDC_CREATEACCOUNT: - case IDC_REMOVEACCOUNT: - if (gg->isonline()) - { - if (MessageBox( - NULL, - TranslateT("You should disconnect before making any permanent changes with your account.\nDo you want to disconnect now ?"), - gg->m_tszUserName, - MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL) - break; - else - gg->disconnect(); - } - case IDC_CHPASS: - case IDC_CHEMAIL: - { - // Readup data - GGUSERUTILDLGDATA dat; - int ret; - char pass[128], email[128]; - GetDlgItemTextA(hwndDlg, IDC_UIN, pass, sizeof(pass)); - dat.uin = atoi(pass); - GetDlgItemTextA(hwndDlg, IDC_PASSWORD, pass, sizeof(pass)); - GetDlgItemTextA(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; - // Show reload required window - ShowWindow(GetDlgItem(hwndDlg, IDC_RELOADREQD), SW_SHOW); - - // Update uin - if (num = db_get_dw(NULL, gg->m_szModuleName, GG_KEY_UIN, 0)) - SetDlgItemTextA(hwndDlg, IDC_UIN, ditoa(num)); - else - SetDlgItemTextA(hwndDlg, IDC_UIN, ""); - - // Update password - if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); - SetDlgItemTextA(hwndDlg, IDC_PASSWORD, dbv.pszVal); - DBFreeVariant(&dbv); - } - else SetDlgItemTextA(hwndDlg, IDC_PASSWORD, ""); - - // Update e-mail - if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, &dbv, DBVT_ASCIIZ)) { - SetDlgItemTextA(hwndDlg, IDC_EMAIL, dbv.pszVal); - DBFreeVariant(&dbv); - } - else SetDlgItemTextA(hwndDlg, IDC_EMAIL, ""); - - // Update links - gg_optsdlgcheck(hwndDlg); - - // Remove details - if (LOWORD(wParam) != IDC_CHPASS && LOWORD(wParam) != IDC_CHEMAIL) - { - db_unset(NULL, gg->m_szModuleName, GG_KEY_NICK); - db_unset(NULL, gg->m_szModuleName, "NickName"); - db_unset(NULL, gg->m_szModuleName, "City"); - db_unset(NULL, gg->m_szModuleName, "FirstName"); - db_unset(NULL, gg->m_szModuleName, "LastName"); - db_unset(NULL, gg->m_szModuleName, "FamilyName"); - db_unset(NULL, gg->m_szModuleName, "CityOrigin"); - db_unset(NULL, gg->m_szModuleName, "Age"); - db_unset(NULL, gg->m_szModuleName, "BirthYear"); - db_unset(NULL, gg->m_szModuleName, "Gender"); - } - } - } - break; - } - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - break; - } - case WM_NOTIFY: - { - switch (((LPNMHDR) lParam)->code) { - case PSN_APPLY: - { - int status_flags = GG_STATUS_FLAG_UNKNOWN; - char str[128]; - uin_t uin; - - // Write Gadu-Gadu number & password - GetDlgItemTextA(hwndDlg, IDC_UIN, str, sizeof(str)); - uin = atoi(str); - GetDlgItemTextA(hwndDlg, IDC_PASSWORD, str, sizeof(str)); - CallService(MS_DB_CRYPT_ENCODESTRING, sizeof(str), (LPARAM) str); - gg->checknewuser(uin, str); - db_set_dw(NULL, gg->m_szModuleName, GG_KEY_UIN, uin); - db_set_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, str); - - // Write Gadu-Gadu email - GetDlgItemTextA(hwndDlg, IDC_EMAIL, str, sizeof(str)); - db_set_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, str); - - // Write checkboxes - db_set_b(NULL, gg->m_szModuleName, GG_KEY_FRIENDSONLY, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_FRIENDSONLY)); - db_set_b(NULL, gg->m_szModuleName, GG_KEY_SHOWINVISIBLE, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWINVISIBLE)); - db_set_b(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUSMSG, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_LEAVESTATUSMSG)); - if (gg->gc_enabled) - db_set_b(NULL, gg->m_szModuleName, GG_KEY_IGNORECONF, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_IGNORECONF)); - db_set_b(NULL, gg->m_szModuleName, GG_KEY_IMGRECEIVE, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_IMGRECEIVE)); - db_set_b(NULL, gg->m_szModuleName, 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); - db_set_b(NULL, gg->m_szModuleName, GG_KEY_ENABLEAVATARS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ENABLEAVATARS)); - - db_set_b(NULL, gg->m_szModuleName, 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: - db_set_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, ID_STATUS_ONLINE); - break; - case 2: - db_set_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, ID_STATUS_AWAY); - break; - case 3: - db_set_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, ID_STATUS_DND); - break; - case 4: - db_set_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, ID_STATUS_FREECHAT); - break; - case 5: - db_set_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, ID_STATUS_INVISIBLE); - break; - default: - db_set_w(NULL, gg->m_szModuleName, 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) -{ - GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - 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)TranslateT("Allow")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ask")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ignore")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_SETCURSEL, - db_get_w(NULL, gg->m_szModuleName, GG_KEY_GC_POLICY_TOTAL, GG_KEYDEF_GC_POLICY_TOTAL), 0); - - if (num = db_get_w(NULL, gg->m_szModuleName, GG_KEY_GC_COUNT_TOTAL, GG_KEYDEF_GC_COUNT_TOTAL)) - SetDlgItemTextA(hwndDlg, IDC_GC_COUNT_TOTAL, ditoa(num)); - - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_ADDSTRING, 0, (LPARAM)TranslateT("Allow")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ask")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ignore")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_SETCURSEL, - db_get_w(NULL, gg->m_szModuleName, GG_KEY_GC_POLICY_UNKNOWN, GG_KEYDEF_GC_POLICY_UNKNOWN), 0); - - if (num = db_get_w(NULL, gg->m_szModuleName, GG_KEY_GC_COUNT_UNKNOWN, GG_KEYDEF_GC_COUNT_UNKNOWN)) - SetDlgItemTextA(hwndDlg, IDC_GC_COUNT_UNKNOWN, ditoa(num)); - - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_ADDSTRING, 0, (LPARAM)TranslateT("Allow")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ask")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ignore")); - SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_SETCURSEL, - db_get_w(NULL, gg->m_szModuleName, 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]; - - // Write groupchat policy - db_set_w(NULL, gg->m_szModuleName, GG_KEY_GC_POLICY_TOTAL, - (WORD)SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_GETCURSEL, 0, 0)); - db_set_w(NULL, gg->m_szModuleName, GG_KEY_GC_POLICY_UNKNOWN, - (WORD)SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_GETCURSEL, 0, 0)); - db_set_w(NULL, gg->m_szModuleName, GG_KEY_GC_POLICY_DEFAULT, - (WORD)SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_GETCURSEL, 0, 0)); - - GetDlgItemTextA(hwndDlg, IDC_GC_COUNT_TOTAL, str, sizeof(str)); - db_set_w(NULL, gg->m_szModuleName, GG_KEY_GC_COUNT_TOTAL, (WORD)atoi(str)); - GetDlgItemTextA(hwndDlg, IDC_GC_COUNT_UNKNOWN, str, sizeof(str)); - db_set_w(NULL, gg->m_szModuleName, 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) -{ - GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - switch (msg) { - case WM_INITDIALOG: - { - DBVARIANT dbv; - DWORD num; - GGPROTO *gg = (GGPROTO *)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); - - TranslateDialogDefault(hwndDlg); - if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_SERVERHOSTS, &dbv, DBVT_ASCIIZ)) { - SetDlgItemTextA(hwndDlg, IDC_HOST, dbv.pszVal); - DBFreeVariant(&dbv); - } - else SetDlgItemTextA(hwndDlg, IDC_HOST, GG_KEYDEF_SERVERHOSTS); - - CheckDlgButton(hwndDlg, IDC_KEEPALIVE, db_get_b(NULL, gg->m_szModuleName, GG_KEY_KEEPALIVE, GG_KEYDEF_KEEPALIVE)); - CheckDlgButton(hwndDlg, IDC_SHOWCERRORS, db_get_b(NULL, gg->m_szModuleName, GG_KEY_SHOWCERRORS, GG_KEYDEF_SHOWCERRORS)); - CheckDlgButton(hwndDlg, IDC_ARECONNECT, db_get_b(NULL, gg->m_szModuleName, GG_KEY_ARECONNECT, GG_KEYDEF_ARECONNECT)); - CheckDlgButton(hwndDlg, IDC_MSGACK, db_get_b(NULL, gg->m_szModuleName, GG_KEY_MSGACK, GG_KEYDEF_MSGACK)); - CheckDlgButton(hwndDlg, IDC_MANUALHOST, db_get_b(NULL, gg->m_szModuleName, GG_KEY_MANUALHOST, GG_KEYDEF_MANUALHOST)); - CheckDlgButton(hwndDlg, IDC_SSLCONN, db_get_b(NULL, gg->m_szModuleName, 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, db_get_b(NULL, gg->m_szModuleName, GG_KEY_DIRECTCONNS, GG_KEYDEF_DIRECTCONNS)); - if (num = db_get_w(NULL, gg->m_szModuleName, GG_KEY_DIRECTPORT, GG_KEYDEF_DIRECTPORT)) - SetDlgItemTextA(hwndDlg, IDC_DIRECTPORT, ditoa(num)); - CheckDlgButton(hwndDlg, IDC_FORWARDING, db_get_b(NULL, gg->m_szModuleName, GG_KEY_FORWARDING, GG_KEYDEF_FORWARDING)); - if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_FORWARDHOST, &dbv, DBVT_ASCIIZ)) { - SetDlgItemTextA(hwndDlg, IDC_FORWARDHOST, dbv.pszVal); - DBFreeVariant(&dbv); - } - if (num = db_get_w(NULL, gg->m_szModuleName, GG_KEY_FORWARDPORT, GG_KEYDEF_FORWARDPORT)) - SetDlgItemTextA(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]; - db_set_b(NULL, gg->m_szModuleName, GG_KEY_KEEPALIVE, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_KEEPALIVE)); - db_set_b(NULL, gg->m_szModuleName, GG_KEY_SHOWCERRORS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWCERRORS)); - db_set_b(NULL, gg->m_szModuleName, GG_KEY_ARECONNECT, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ARECONNECT)); - db_set_b(NULL, gg->m_szModuleName, GG_KEY_MSGACK, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_MSGACK)); - db_set_b(NULL, gg->m_szModuleName, GG_KEY_MANUALHOST, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST)); - db_set_b(NULL, gg->m_szModuleName, GG_KEY_SSLCONN, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SSLCONN)); - - // Transfer settings - db_set_b(NULL, gg->m_szModuleName, GG_KEY_DIRECTCONNS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); - db_set_b(NULL, gg->m_szModuleName, GG_KEY_FORWARDING, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_FORWARDING)); - - // Write custom servers - GetDlgItemTextA(hwndDlg, IDC_HOST, str, sizeof(str)); - db_set_s(NULL, gg->m_szModuleName, GG_KEY_SERVERHOSTS, str); - - // Write direct port - GetDlgItemTextA(hwndDlg, IDC_DIRECTPORT, str, sizeof(str)); - db_set_w(NULL, gg->m_szModuleName, GG_KEY_DIRECTPORT, (WORD)atoi(str)); - // Write forwarding host - GetDlgItemTextA(hwndDlg, IDC_FORWARDHOST, str, sizeof(str)); - db_set_s(NULL, gg->m_szModuleName, GG_KEY_FORWARDHOST, str); - GetDlgItemTextA(hwndDlg, IDC_FORWARDPORT, str, sizeof(str)); - db_set_w(NULL, gg->m_szModuleName, 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)TranslateT("Female")); // 1 - SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)TranslateT("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, TranslateT("Your details has been uploaded to the public directory."), - gg->m_tszUserName, MB_OK | MB_ICONINFORMATION); - dat->updating = FALSE; - break; - } - - if (hContact == NULL) - szProto = gg->m_szModuleName; - 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)db_get_b(hContact, gg->m_szModuleName, "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()) - { - MessageBox(NULL, - TranslateT("You have to be logged in before you can change your details."), - gg->m_tszUserName, MB_OK | MB_ICONSTOP); - break; - } - - EnableWindow(GetDlgItem(hwndDlg, IDC_SAVE), FALSE); - - req = gg_pubdir50_new(GG_PUBDIR50_WRITE); - - GetDlgItemTextA(hwndDlg, IDC_FIRSTNAME, text, sizeof(text)); - if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, text); - - GetDlgItemTextA(hwndDlg, IDC_LASTNAME, text, sizeof(text)); - if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, text); - - GetDlgItemTextA(hwndDlg, IDC_NICKNAME, text, sizeof(text)); - if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, text); - - GetDlgItemTextA(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, ""); - } - - GetDlgItemTextA(hwndDlg, IDC_BIRTHYEAR, text, sizeof(text)); - if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_BIRTHYEAR, text); - - GetDlgItemTextA(hwndDlg, IDC_FAMILYNAME, text, sizeof(text)); - if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_FAMILYNAME, text); - - GetDlgItemTextA(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 GGPROTO::details_init(WPARAM wParam, LPARAM lParam) -{ - char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, lParam, 0); - if ((szProto == NULL || strcmp(szProto, m_szModuleName)) && lParam || lParam && db_get_b((HANDLE)lParam, m_szModuleName, "ChatRoom", 0)) - return 0; - - // Here goes init - { - OPTIONSDIALOGPAGE odp = {0}; - - odp.cbSize = sizeof(odp); - odp.flags = ODPF_DONTTRANSLATE | ODPF_TCHAR; - odp.hInstance = hInstance; - odp.pfnDlgProc = gg_detailsdlgproc; - odp.position = -1900000000; - odp.pszTemplate = ((HANDLE)lParam != NULL) ? MAKEINTRESOURCEA(IDD_INFO_GG) : MAKEINTRESOURCEA(IDD_CHINFO_GG); - odp.ptszTitle = m_tszUserName; - odp.dwInitParam = (LPARAM)this; - UserInfo_AddPage(wParam, &odp); - } - - // Start search for user data - if ((HANDLE)lParam == NULL) - GetInfo(NULL, 0); - - return 0; -} - -//////////////////////////////////////////////////////////////////////////////////////////// -// Proc: Account manager options dialog -INT_PTR CALLBACK gg_acc_mgr_guidlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -//////////////////////////////////////////////////////////////////////////////////////////// -{ - GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - switch (msg) { - case WM_INITDIALOG: - { - DBVARIANT dbv; - DWORD num; - GGPROTO *gg = (GGPROTO *)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); - - TranslateDialogDefault(hwndDlg); - if (num = db_get_dw(NULL, gg->m_szModuleName, GG_KEY_UIN, 0)) - SetDlgItemTextA(hwndDlg, IDC_UIN, ditoa(num)); - if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); - SetDlgItemTextA(hwndDlg, IDC_PASSWORD, dbv.pszVal); - DBFreeVariant(&dbv); - } - if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, &dbv, DBVT_ASCIIZ)) { - SetDlgItemTextA(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]; - GetDlgItemTextA(hwndDlg, IDC_UIN, pass, sizeof(pass)); - dat.uin = atoi(pass); - GetDlgItemTextA(hwndDlg, IDC_PASSWORD, pass, sizeof(pass)); - GetDlgItemTextA(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; - // Show reload required window - ShowWindow(GetDlgItem(hwndDlg, IDC_RELOADREQD), SW_SHOW); - - // Update uin - if (num = db_get_dw(NULL, gg->m_szModuleName, GG_KEY_UIN, 0)) - SetDlgItemTextA(hwndDlg, IDC_UIN, ditoa(num)); - else - SetDlgItemTextA(hwndDlg, IDC_UIN, ""); - - // Update password - if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); - SetDlgItemTextA(hwndDlg, IDC_PASSWORD, dbv.pszVal); - DBFreeVariant(&dbv); - } - else SetDlgItemTextA(hwndDlg, IDC_PASSWORD, ""); - - // Update e-mail - if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, &dbv, DBVT_ASCIIZ)) { - SetDlgItemTextA(hwndDlg, IDC_EMAIL, dbv.pszVal); - DBFreeVariant(&dbv); - } - else SetDlgItemTextA(hwndDlg, IDC_EMAIL, ""); - } - } - } - break; - - case WM_NOTIFY: - switch(((LPNMHDR)lParam)->idFrom) { - case 0: - switch (((LPNMHDR) lParam)->code) { - case PSN_APPLY: - { - char str[128]; - uin_t uin; - - // Write Gadu-Gadu number & password - GetDlgItemTextA(hwndDlg, IDC_UIN, str, sizeof(str)); - uin = atoi(str); - GetDlgItemTextA(hwndDlg, IDC_PASSWORD, str, sizeof(str)); - CallService(MS_DB_CRYPT_ENCODESTRING, sizeof(str), (LPARAM) str); - gg->checknewuser(uin, str); - db_set_dw(NULL, gg->m_szModuleName, GG_KEY_UIN, uin); - db_set_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, str); - - // Write Gadu-Gadu email - GetDlgItemTextA(hwndDlg, IDC_EMAIL, str, sizeof(str)); - db_set_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, str); - } - } - } - break; - } - return FALSE; -} diff --git a/protocols/Gadu-Gadu/docs/gadu-gadu-translation.txt b/protocols/Gadu-Gadu/docs/gadu-gadu-translation.txt new file mode 100644 index 0000000000..168fe833e4 --- /dev/null +++ b/protocols/Gadu-Gadu/docs/gadu-gadu-translation.txt @@ -0,0 +1,233 @@ +; Common strings that belong to many files +;[City:] +;[Conference] +;[Contact list] +;[Female] +;[List export successful.] +;[List import successful.] +;[Male] +;[Me] +;[Next image] +;[Open new conference] +;[Previous image] + +; ../../protocols/Gadu-Gadu/core.c +;[Age:] +;[Connection cannot be established because of error:\n\t%s] +;[External direct connections hostname %s is invalid. Disabling external host forwarding.] +;[Incoming image] +;[List remove successful.] +;[Server hostname %s is invalid. Using default hostname provided by the network.] +;[Unknown client] +;[You are logged in at another location] +;[You have logged in at another location] + +; ../../protocols/Gadu-Gadu/dialogs.c +;[] +;[] +;[Advanced] +;[Allow] +;[Ask] +;[Away] +;[DND] +;[Free for chat] +;[General] +;[Ignore] +;[Invisible] +;[Message with [img] BBCode] +;[Network] +;[Online] +;[Popup window] +;[System tray icon] +;[You have to be logged in before you can change your details.] +;[You need to specify your registration e-mail first.] +;[You should disconnect before making any permanent changes with your account.\nDo you want to disconnect now ?] +;[Your details has been uploaded to the public directory.] +;[Your password will be sent to your registration e-mail.\nDo you want to continue ?] + +; ../../protocols/Gadu-Gadu/gg.c +;[%s connection] +;[&Block] +;[&Unblock] +;[HTTP failed connecting] +;[HTTP failed reading] +;[HTTP failed resolving] +;[HTTP failed writing] +;[Unknown HTTP error] + +; ../../protocols/Gadu-Gadu/groupchat.c +;[%s has initiated conference with %d participants (%d unknowns).\nDo you want do participate ?] +;[%s initiated the conference.] +;[&Clear ignored conferences] +;['Unknown'] +;[All ignored conferences are now unignored and the conference policy will act again.] +;[Open &conference...] +;[Participants] +;[There are no ignored conferences.] +;[This is my own conference.] +;[Unknown] +;[You have to be connected to open new conference.] + +; ../../protocols/Gadu-Gadu/icolib.c +;[Account settings] +;[Block user] +;[Clear ignored conferences] +;[Concurrent sessions] +;[Delete image] +;[Export list to server] +;[Export list to text file] +;[Import list from server] +;[Import list from text file] +;[Protocol icon] +;[Protocols] +;[Remove list from server] +;[Save image] +;[Send image] + +; ../../protocols/Gadu-Gadu/image.c +;[&Image] +;[Delete image from the list] +;[Image cannot be written to disk.] +;[Image exceeds maximum allowed size of 255 KB.] +;[Image files (*.bmp,*.gif,*.jpeg,*.jpg,*.png)] +;[Image for %s] +;[Image from %s] +;[Save image to disk] +;[Select picture to send] + +; ../../protocols/Gadu-Gadu/import.c +;[&Remove List From Server] +;[All Files] +;[Export List To &Server] +;[Export List To &Text File...] +;[Import List From &Server] +;[Import List From &Text File...] +;[List cannot be exported because of error:\n\t%s] +;[List cannot be exported to file \"%s\" because of error:\n\t%s] +;[List cannot be imported because of error:\n\t%s] +;[List cannot be imported from file \"%s\" because of error:\n\t%s] +;[List cannot be removeed because of error:\n\t%s] +;[Text files] +;[You have to be connected before you can import/export contacts from/to server.] +;[contacts] + +; ../../protocols/Gadu-Gadu/links.c +;[Gadu-Gadu Link Protocol] + +; ../../protocols/Gadu-Gadu/ownerinfo.c +;[Password could not be reminded because of error:\n\t%s] +;[Password was sent to your e-mail.] + +; ../../protocols/Gadu-Gadu/popups.c +;[Error] +;[Notify] + +; ../../protocols/Gadu-Gadu/resource.rc +;[&Close] +;[&Create] +;[&Save changes] +;[&Send] +;[* new line is separator\n** hostname:port format] +;[Advanced Configuration] +;[After disconnection leave away message of status:] +;[Age from:] +;[Automatically reconnect after unintentional disconnection] +;[Birth year:] +;[Cancel] +;[Change Gadu-Gadu e-mail] +;[Change Gadu-Gadu e-mail\nChanges current Gadu-Gadu user e-mail] +;[Change Gadu-Gadu password] +;[Change Gadu-Gadu password\nChanges current Gadu-Gadu user password] +;[Change e-mail] +;[Change password] +;[Concurrent %s Login Sessions\nView information on active concurrent sessions] +;[Concurrent Sessions] +;[Conference policy] +;[Confirm password:] +;[Create Gadu-Gadu account] +;[Create Gadu-Gadu account\nThis will create new Gadu-Gadu account] +;[Create new account] +;[Description:] +;[E-mail:] +;[Enable avatars] +;[Enter token to continue] +;[Family name:] +;[File Transfer] +;[First name:] +;[Friends only] +;[Gadu-Gadu Number:] +;[Gadu-Gadu User Details] +;[Gender:] +;[Host:] +;[Ignore incoming conference messages] +;[Internal IP:] +;[Keep connection alive] +;[Last name:] +;[Manually specify connection servers' hosts] +;[New e-mail:] +;[New password:] +;[Nickname:] +;[Number:] +;[OK] +;[Open] +;[Open new conference\nSelect conference participants] +;[Options] +;[Origin city:] +;[Password:] +;[Port:] +;[Receive image and after image is received use:] +;[Remove] +;[Remove Gadu-Gadu account] +;[Remove Gadu-Gadu account\nThis will remove your Gadu-Gadu account] +;[Remove account] +;[Retrieve password] +;[Search online users only] +;[Send messages slower, but with full acknowledgement] +;[Show connection errors] +;[Show links from unknown contacts] +;[Show offline users with status message as invisible in contact list] +;[Sign out all sessions] +;[Use SSL secure connection] +;[Use direct connections] +;[Use forwarding] +;[Version:] +;[Yes, I want to remove my account] +;[You will need to reconnect for the changes you have made on this page to take effect.] +;[if total participant count greater than:] +;[if unknown participant count greater than:] +;[in other case] +;[to:] + +; ../../protocols/Gadu-Gadu/services.c +;[Gadu-Gadu Number] +;[To remove your Gadu-Gadu avatar, you must use the MojaGeneracja.pl website.] + +; ../../protocols/Gadu-Gadu/sessions.c +;[Action] +;[Client Name] +;[Concurrent &sessions] +;[Copy Text] +;[IP Address] +;[Login Time] +;[There are no active concurrent sessions for this account.] +;[Whois] +;[You have to be logged in to view concurrent sessions.] +;[sign out] + +; ../../protocols/Gadu-Gadu/token.c +;[Could not load token image.] +;[Token retrieval failed because of error:\n\t%s] + +; ../../protocols/Gadu-Gadu/userutils.c +;[Bad number or password] +;[Bad old e-mail or password] +;[Cannot register new account because of error:\n\t%s] +;[Invalid data entered] +;[Registration rejected] +;[You have registered new account.\nPlease fill up your personal details in \"M->View/Change My Details...\"] +;[Your account cannot be removed because of error:\n\t%s] +;[Your account has been removed.] +;[Your e-mail cannot be changed because of error:\n\t%s] +;[Your e-mail has been changed.] +;[Your password cannot be changed because of error:\n\t%s] +;[Your password has been changed.] diff --git a/protocols/Gadu-Gadu/dynstuff.cpp b/protocols/Gadu-Gadu/dynstuff.cpp deleted file mode 100644 index 9ae41daf59..0000000000 --- a/protocols/Gadu-Gadu/dynstuff.cpp +++ /dev/null @@ -1,612 +0,0 @@ -/* $Id: dynstuff.c 11259 2010-02-17 04:47:22Z borkra $ */ - -/* - * (C) Copyright 2001-2003 Wojtek Kaniewski - * Dawid Jarosz - * - * 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 -#include -#include -#include - -/* - * 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 *)) -{ - if (!list) { - errno = EFAULT; - return NULL; - } - - list_t newlist = (list_t)malloc(sizeof(struct list)); - - newlist->data = data; - newlist->next = NULL; - - if (alloc_size) { - newlist->data = malloc(alloc_size); - memcpy(newlist->data, data, alloc_size); - } - - list_t tmp; - if (!(tmp = *list)) { - *list = newlist; - } else { - if (!comparision) { - while (tmp->next) - tmp = tmp->next; - tmp->next = newlist; - } else { - list_t prev = NULL; - - while (comparision(newlist->data, tmp->data) > 0) { - prev = tmp; - tmp = tmp->next; - if (!tmp) - break; - } - - if (!prev) { - tmp = *list; - *list = newlist; - newlist->next = tmp; - } else { - prev->next = newlist; - newlist->next = tmp; - } - } - } - - return newlist->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 = (char*)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 = (string_t)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 = (char*)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 = (char*)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 = (char*)calloc(1, len + 1); - strncpy(token, p, len); - token[len] = 0; - p = q; - } - - result = (char**)realloc(result, (items + 2) * sizeof(char*)); - result[items] = token; - result[++items] = NULL; - - if (!*p) - break; - - p++; - } - -failure: - if (!items) - result = (char**)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 = (char**)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 deleted file mode 100644 index 8e36c67ede..0000000000 --- a/protocols/Gadu-Gadu/dynstuff.h +++ /dev/null @@ -1,70 +0,0 @@ -/* $Id: dynstuff.h 4366 2006-12-20 22:40:37Z ono $ */ - -/* - * (C) Copyright 2001-2002 Wojtek Kaniewski - * Dawid Jarosz - * - * 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.cpp b/protocols/Gadu-Gadu/filetransfer.cpp deleted file mode 100644 index e7560e16a2..0000000000 --- a/protocols/Gadu-Gadu/filetransfer.cpp +++ /dev/null @@ -1,977 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 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. -//////////////////////////////////////////////////////////////////////////////// - -#include "gg.h" -#include -#include -#include - -void GGPROTO::dccstart() -{ - DWORD exitCode = 0; - - if (dcc) return; - - // Startup dcc thread - GetExitCodeThread(pth_dcc.hThread, &exitCode); - // Check if dcc thread isn't running already - if (exitCode == STILL_ACTIVE) - { -#ifdef DEBUGMODE - netlog("gg_dccstart(): DCC thread still active. Exiting..."); -#endif - // Signalize mainthread it's started - if (hEvent) SetEvent(hEvent); - return; - } - - // Check if we wan't direct connections - if (!db_get_b(NULL, m_szModuleName, GG_KEY_DIRECTCONNS, GG_KEYDEF_DIRECTCONNS)) - { - netlog("gg_dccstart(): No direct connections setup."); - if (hEvent) SetEvent(hEvent); - return; - } - - // Start thread - pth_dcc.hThread = forkthreadex(&GGPROTO::dccmainthread, NULL, &pth_dcc.dwThreadId); -} - -void GGPROTO::dccconnect(uin_t uin) -{ - struct gg_dcc *dcc; - HANDLE hContact = getcontact(uin, 0, 0, NULL); - DWORD ip, myuin; WORD port; - - netlog("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(db_get_dw(hContact, m_szModuleName, GG_KEY_CLIENTIP, 0)); - port = db_get_w(hContact, m_szModuleName, GG_KEY_CLIENTPORT, 0); - myuin = db_get_dw(NULL, m_szModuleName, 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(&ft_mutex); - list_add(&watches, dcc, 0); - LeaveCriticalSection(&ft_mutex); -} - -////////////////////////////////////////////////////////// -// THREAD: File transfer fail -struct ftfaildata -{ - HANDLE hContact; - HANDLE hProcess; -}; - -void __cdecl GGPROTO::ftfailthread(void *param) -{ - struct ftfaildata *ft = (struct ftfaildata *)param; - SleepEx(100, FALSE); - netlog("gg_ftfailthread(): Sending failed file transfer."); - ProtoBroadcastAck(m_szModuleName, ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft->hProcess, 0); - free(ft); -} - -HANDLE ftfail(GGPROTO *gg, HANDLE hContact) -{ - ftfaildata *ft = (ftfaildata*)malloc(sizeof(struct ftfaildata)); -#ifdef DEBUGMODE - gg->netlog("gg_ftfail(): Failing file transfer..."); -#endif - srand(time(NULL)); - ft->hProcess = (HANDLE)rand(); - ft->hContact = hContact; - gg->forkthread(&GGPROTO::ftfailthread, ft); - return ft->hProcess; -} - -//////////////////////////////////////////////////////////// -// Main DCC connection session thread - -// Info refresh min time (msec) / half-sec -#define GGSTATREFRESHEVERY 500 - -void __cdecl GGPROTO::dccmainthread(void*) -{ - uin_t uin; - 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 - watches = transfers = requests = l = NULL; - - netlog("gg_dccmainthread(): DCC Server Thread Starting"); - - // Readup number - if (!(uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0))) - { - netlog("gg_dccmainthread(): No Gadu-Gadu number specified. Exiting."); - if (hEvent) SetEvent(hEvent); - return; - } - - // Create listen socket on config direct port - if (!(dcc = gg_dcc_socket_create(uin, (uint16_t)db_get_w(NULL, m_szModuleName, GG_KEY_DIRECTPORT, GG_KEYDEF_DIRECTPORT)))) - { - netlog("gg_dccmainthread(): Cannot create DCC listen socket. Exiting."); - // Signalize mainthread we haven't start - if (hEvent) SetEvent(hEvent); - return; - } - - gg_dcc_port = dcc->port; - gg_dcc_ip = inet_addr("255.255.255.255"); - netlog("gg_dccmainthread(): Listening on port %d.", gg_dcc_port); - - // Signalize mainthread we started - if (hEvent) SetEvent(hEvent); - - // Add main dcc handler to watches - list_add(&watches, dcc, 0); - - // Do while we are in the main server thread - while(pth_dcc.dwThreadId && dcc) - { - // Timeouts - tv.tv_sec = 1; - tv.tv_usec = 0; - - // Prepare descriptiors for select - FD_ZERO(&rd); - FD_ZERO(&wd); - - for (maxfd = 0, l = watches; l; l = l->next) - { - gg_common *w = (gg_common*)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) - netlog("gg_dccmainthread(): Bad descriptor on select()."); - else if (errno != EINTR) - netlog("gg_dccmainthread(): Unknown error on select()."); - continue; - } - - // Process watches (carefull with l) - l = watches; - EnterCriticalSection(&ft_mutex); - while (l) - { - struct gg_common *c = (gg_common*)l->data; - struct gg_dcc *dcc = (gg_dcc*)l->data; - struct gg_dcc7 *dcc7 = (gg_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))) - { - netlog("gg_dccmainthread(): Socket closed."); - // Remove socket and _close - list_remove(&watches, dcc, 0); - gg_dcc_socket_free(dcc); - - // Check if it's main socket - if (dcc == dcc) dcc = NULL; - continue; - } - else netlog("gg_dccmainthread(): Event: %s", ggdebug_eventtype(e)); - - switch(e->type) - { - // Client connected - case GG_EVENT_DCC_NEW: - list_add(&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, (char*)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(m_szModuleName, dcc->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc, (LPARAM)&pfts); - } - break; - - // Connection was successfuly ended - case GG_EVENT_DCC_DONE: - netlog("gg_dccmainthread(): Client: %d, Transfer done ! Closing connection.", dcc->peer_uin); - // Remove from watches - list_remove(&watches, dcc, 0); - // Close file & success - if (dcc->file_fd != -1) - { - PROTOFILETRANSFERSTATUS pfts; - strncpy(filename, dcc->folder, sizeof(filename)); - strncat(filename, (char*)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(m_szModuleName, dcc->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc, (LPARAM)&pfts); - _close(dcc->file_fd); dcc->file_fd = -1; - ProtoBroadcastAck(m_szModuleName, dcc->contact, ACKTYPE_FILE, ACKRESULT_SUCCESS, dcc, 0); - } - // Free dcc - gg_free_dcc(dcc); if (dcc == dcc) dcc = NULL; - break; - - // Client error - case GG_EVENT_DCC_ERROR: - switch (e->event.dcc_error) - { - case GG_ERROR_DCC_HANDSHAKE: - netlog("gg_dccmainthread(): Client: %d, Handshake error.", dcc->peer_uin); - break; - case GG_ERROR_DCC_NET: - netlog("gg_dccmainthread(): Client: %d, Network error.", dcc->peer_uin); - break; - case GG_ERROR_DCC_FILE: - netlog("gg_dccmainthread(): Client: %d, File read/write error.", dcc->peer_uin); - break; - case GG_ERROR_DCC_EOF: - netlog("gg_dccmainthread(): Client: %d, End of file/connection error.", dcc->peer_uin); - break; - case GG_ERROR_DCC_REFUSED: - netlog("gg_dccmainthread(): Client: %d, Connection refused error.", dcc->peer_uin); - break; - default: - netlog("gg_dccmainthread(): Client: %d, Unknown error.", dcc->peer_uin); - } - // Don't do anything if it's main socket - if (dcc == dcc) break; - - // Remove from watches - list_remove(&watches, dcc, 0); - - // Close file & fail - if (dcc->contact) - { - _close(dcc->file_fd); dcc->file_fd = -1; - ProtoBroadcastAck(m_szModuleName, dcc->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc, 0); - } - // Free dcc - gg_free_dcc(dcc); if (dcc == dcc) dcc = NULL; - break; - - // Need file acknowledgement - case GG_EVENT_DCC_NEED_FILE_ACK: - netlog("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(&watches, dcc, 0); - // Add to waiting transfers - list_add(&transfers, dcc, 0); - - ////////////////////////////////////////////////// - // Add file recv request - { - // Make new ggtransfer struct - dcc->contact = getcontact(dcc->peer_uin, 0, 0, NULL); - TCHAR* filenameT = mir_utf8decodeT((char*)dcc->file_info.filename); - - PROTORECVFILET pre = {0}; - pre.flags = PREF_TCHAR; - pre.fileCount = 1; - pre.timestamp = time(NULL); - pre.tszDescription = filenameT; - pre.ptszFiles = &filenameT; - pre.lParam = (LPARAM)dcc7; - - CCSDATA ccs = { dcc7->contact, PSR_FILE, 0, (LPARAM)&pre }; - CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); - - mir_free(filenameT); - } - break; - - // Need client accept - case GG_EVENT_DCC_CLIENT_ACCEPT: - netlog("gg_dccmainthread(): Client: %d, Client accept.", dcc->peer_uin); - // Check if user is on the list and if it is my uin - if (getcontact(dcc->peer_uin, 0, 0, NULL) && - db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, -1) == dcc->uin) - break; - - // Kill unauthorized dcc - list_remove(&watches, dcc, 0); - gg_free_dcc(dcc); if (dcc == dcc) dcc = NULL; - break; - - // Client connected as we wished to (callback) - case GG_EVENT_DCC_CALLBACK: - { - int found = 0; - netlog("gg_dccmainthread(): Callback from client %d.", dcc->peer_uin); - // Seek for stored callback request - for (l = requests; l; l = l->next) - { - struct gg_dcc *req = (gg_dcc*)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(&requests, req, 0); - // Remove dcc from watches - list_remove(&watches, dcc, 0); - // Add request to watches - list_add(&watches, req, 0); - // Free old dat - gg_free_dcc(dcc); - netlog("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) - { - netlog("gg_dccmainthread(): Unknown request to client %d.", dcc->peer_uin); - // Kill unauthorized dcc - list_remove(&watches, dcc, 0); - gg_free_dcc(dcc); if (dcc == dcc) 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))) - { - netlog("gg_dccmainthread(): Socket closed."); - // Remove socket and _close - list_remove(&watches, dcc7, 0); - gg_dcc7_free(dcc7); - continue; - } - else netlog("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, (char*)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(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc7, (LPARAM)&pfts); - } - break; - - // Connection was successfuly ended - case GG_EVENT_DCC7_DONE: - netlog("gg_dccmainthread(): Client: %d, Transfer done ! Closing connection.", dcc->peer_uin); - // Remove from watches - list_remove(&watches, dcc7, 0); - // Close file & success - if (dcc7->file_fd != -1) - { - PROTOFILETRANSFERSTATUS pfts; - strncpy(filename, dcc7->folder, sizeof(filename)); - strncat(filename, (char*)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(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc7, (LPARAM)&pfts); - _close(dcc7->file_fd); dcc7->file_fd = -1; - ProtoBroadcastAck(m_szModuleName, 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: - netlog("gg_dccmainthread(): Client: %d, Handshake error.", dcc7->peer_uin); - break; - case GG_ERROR_DCC7_NET: - netlog("gg_dccmainthread(): Client: %d, Network error.", dcc7->peer_uin); - break; - case GG_ERROR_DCC7_FILE: - netlog("gg_dccmainthread(): Client: %d, File read/write error.", dcc7->peer_uin); - break; - case GG_ERROR_DCC7_EOF: - netlog("gg_dccmainthread(): Client: %d, End of file/connection error.", dcc7->peer_uin); - break; - case GG_ERROR_DCC7_REFUSED: - netlog("gg_dccmainthread(): Client: %d, Connection refused error.", dcc7->peer_uin); - break; - case GG_ERROR_DCC7_RELAY: - netlog("gg_dccmainthread(): Client: %d, Relay connection error.", dcc7->peer_uin); - break; - default: - netlog("gg_dccmainthread(): Client: %d, Unknown error.", dcc7->peer_uin); - } - // Remove from watches - list_remove(&watches, dcc7, 0); - - // Close file & fail - if (dcc7->file_fd != -1) - { - _close(dcc7->file_fd); - dcc7->file_fd = -1; - } - - if (dcc7->contact) - ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0); - - // Free dcc - gg_dcc7_free(dcc7); - break; - } - - // Free event - gg_free_event(e); - break; - } - } - LeaveCriticalSection(&ft_mutex); - } - - // Close all dcc client sockets - for (l = watches; l; l = l->next) - { - struct gg_common *c = (gg_common*)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 = (gg_dcc7*)l->data; - gg_dcc7_free(dcc7); - } - else - { - struct gg_dcc *dcc = (gg_dcc*)l->data; - gg_dcc_socket_free(dcc); - - // Check if it's main socket - if (dcc == dcc) dcc = NULL; - } - } - // Close all waiting for aknowledgle transfers - for (l = transfers; l; l = l->next) - { - struct gg_common *c = (gg_common*)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 = (gg_dcc7*)l->data; - gg_dcc7_free(dcc7); - } - else - { - struct gg_dcc *dcc = (gg_dcc*)l->data; - gg_dcc_socket_free(dcc); - } - } - // Close all waiting dcc requests - for (l = requests; l; l = l->next) - { - struct gg_dcc *dcc = (gg_dcc*)l->data; - if (dcc) gg_free_dcc(dcc); - } - list_destroy(watches, 0); - list_destroy(transfers, 0); - list_destroy(requests, 0); - - gg_dcc_port = 0; - gg_dcc_ip = 0; - netlog("gg_dccmainthread(): DCC Server Thread Ending"); -} - -HANDLE GGPROTO::dccfileallow(HANDLE hTransfer, const PROTOCHAR* szPath) -{ - struct gg_dcc *dcc = (struct gg_dcc *) hTransfer; - char fileName[MAX_PATH], *path = mir_t2a(szPath); - strncpy(fileName, path, sizeof(fileName)); - strncat(fileName, (char*)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(&ft_mutex); - list_remove(&transfers, dcc, 0); - LeaveCriticalSection(&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) - { - netlog("gg_dccfileallow(): Failed to create file \"%s\".", fileName); - ProtoBroadcastAck(m_szModuleName, 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(&ft_mutex); - list_add(&watches, dcc, 0); - LeaveCriticalSection(&ft_mutex); - - netlog("gg_dccfileallow(): Receiving file \"%s\" from %d.", dcc->file_info.filename, dcc->peer_uin); - - return hTransfer; -} - -HANDLE GGPROTO::dcc7fileallow(HANDLE hTransfer, const PROTOCHAR* szPath) -{ - struct gg_dcc7 *dcc7 = (struct gg_dcc7 *) hTransfer; - char fileName[MAX_PATH], *path = mir_t2a(szPath); - int iFtRemoveRes; - strncpy(fileName, path, sizeof(fileName)); - strncat(fileName, (char*)dcc7->filename, sizeof(fileName) - strlen(fileName)); - dcc7->folder = _strdup((char *) path); - dcc7->tick = 0; - mir_free(path); - - // Remove transfer from waiting list - EnterCriticalSection(&ft_mutex); - iFtRemoveRes = list_remove(&transfers, dcc7, 0); - LeaveCriticalSection(&ft_mutex); - - if (iFtRemoveRes == -1) - { - netlog("gg_dcc7fileallow(): File transfer denied."); - ProtoBroadcastAck(m_szModuleName, 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) - { - netlog("gg_dcc7fileallow(): Failed to create file \"%s\".", fileName); - gg_dcc7_reject(dcc7, GG_DCC7_REJECT_USER); - ProtoBroadcastAck(m_szModuleName, 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(&ft_mutex); - list_add(&watches, dcc7, 0); - LeaveCriticalSection(&ft_mutex); - - netlog("gg_dcc7fileallow(): Receiving file \"%s\" from %d.", dcc7->filename, dcc7->peer_uin); - - return hTransfer; -} - -int GGPROTO::dccfiledeny(HANDLE hTransfer) -{ - struct gg_dcc *dcc = (struct gg_dcc *) hTransfer; - - // Remove transfer from any list - EnterCriticalSection(&ft_mutex); - if (watches) list_remove(&watches, dcc, 0); - if (requests) list_remove(&requests, dcc, 0); - if (transfers) list_remove(&transfers, dcc, 0); - LeaveCriticalSection(&ft_mutex); - - netlog("gg_dccfiledeny(): Rejected file \"%s\" from/to %d.", dcc->file_info.filename, dcc->peer_uin); - - // Free transfer - gg_free_dcc(dcc); - - return 0; -} - -int GGPROTO::dcc7filedeny(HANDLE hTransfer) -{ - struct gg_dcc7 *dcc7 = (struct gg_dcc7 *) hTransfer; - - gg_dcc7_reject(dcc7, GG_DCC7_REJECT_USER); - - // Remove transfer from any list - EnterCriticalSection(&ft_mutex); - if (watches) list_remove(&watches, dcc7, 0); - if (transfers) list_remove(&transfers, dcc7, 0); - LeaveCriticalSection(&ft_mutex); - - netlog("gg_dcc7filedeny(): Rejected file \"%s\" from/to %d.", dcc7->filename, dcc7->peer_uin); - - // Free transfer - gg_dcc7_free(dcc7); - - return 0; -} - -int GGPROTO::dccfilecancel(HANDLE hTransfer) -{ - struct gg_dcc *dcc = (struct gg_dcc *) hTransfer; - - // Remove transfer from any list - EnterCriticalSection(&ft_mutex); - if (watches) list_remove(&watches, dcc, 0); - if (requests) list_remove(&requests, dcc, 0); - if (transfers) list_remove(&transfers, dcc, 0); - LeaveCriticalSection(&ft_mutex); - - // Send failed info - ProtoBroadcastAck(m_szModuleName, dcc->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc, 0); - // Close file - if (dcc->file_fd != -1) - { - _close(dcc->file_fd); - dcc->file_fd = -1; - } - - netlog("gg_dccfilecancel(): Canceled file \"%s\" from/to %d.", dcc->file_info.filename, dcc->peer_uin); - - // Free transfer - gg_free_dcc(dcc); - - return 0; -} - -int GGPROTO::dcc7filecancel(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(&ft_mutex); - if (watches) list_remove(&watches, dcc7, 0); - if (transfers) list_remove(&transfers, dcc7, 0); - LeaveCriticalSection(&ft_mutex); - - // Send failed info - ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0); - // Close file - if (dcc7->file_fd != -1) - { - _close(dcc7->file_fd); - dcc7->file_fd = -1; - } - - netlog("gg_dcc7filecancel(): Canceled file \"%s\" from/to %d.", dcc7->filename, dcc7->peer_uin); - - // Free transfer - gg_dcc7_free(dcc7); - - return 0; -} - -//////////////////////////////////////////////////////////// -// File receiving allowed - -HANDLE GGPROTO::FileAllow(HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath) -{ - // Check if its proper dcc - struct gg_common *c = (struct gg_common *) hTransfer; - if (!c) - return NULL; - - if (c->type == GG_SESSION_DCC7_GET) - return dcc7fileallow(hTransfer, szPath); - - return dccfileallow(hTransfer, szPath); -} - -//////////////////////////////////////////////////////////// -// File transfer canceled - -int GGPROTO::FileCancel(HANDLE hContact, HANDLE hTransfer) -{ - // Check if its proper dcc - struct gg_common *c = (struct gg_common *) hTransfer; - if (!c) - return 0; - - if (c->type == GG_SESSION_DCC7_SEND || c->type == GG_SESSION_DCC7_GET) - return dcc7filecancel(hTransfer); - - return dccfilecancel(hTransfer); -} - -//////////////////////////////////////////////////////////// -// File receiving denied - -int GGPROTO::FileDeny(HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szReason) -{ - // Check if its proper dcc - struct gg_common *c = (struct gg_common *) hTransfer; - if (!c) - return 0; - - if (c->type == GG_SESSION_DCC7_GET) - return dcc7filedeny(hTransfer); - - return dccfiledeny(hTransfer); -} - -//////////////////////////////////////////////////////////// -// Called when received an file - -int GGPROTO::RecvFile(HANDLE hContact, PROTOFILEEVENT* pre) -{ - return Proto_RecvFile(hContact, pre); -} - -//////////////////////////////////////////////////////////// -// Called when user sends a file - -HANDLE GGPROTO::SendFile(HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles) -{ - char *bslash, *filename; - struct gg_dcc *dcc; - DWORD ip, ver; - WORD port; - uin_t myuin, uin; - - // Check if main dcc thread is on - if (!isonline()) - return ftfail(this, hContact); - - filename = mir_t2a(ppszFiles[0]); - - // Read user IP and port - ip = swap32(db_get_dw(hContact, m_szModuleName, GG_KEY_CLIENTIP, 0)); - port = db_get_w(hContact, m_szModuleName, GG_KEY_CLIENTPORT, 0); - myuin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0); - uin = db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0); - ver = db_get_dw(hContact, m_szModuleName, 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(&sess_mutex); - if (!(dcc7 = gg_dcc7_send_file(sess, uin, filename, NULL, NULL))) { - LeaveCriticalSection(&sess_mutex); - netlog("gg_sendfile(): Failed to send file \"%s\".", filename); - mir_free(filename); - return ftfail(this, hContact); - } - LeaveCriticalSection(&sess_mutex); - - netlog("gg_sendfile(): Sending file \"%s\" to %d.", filename, uin); - - // Add dcc to watches - list_add(&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) - { - netlog("gg_sendfile(): Bad contact uin or my uin. Exit."); - mir_free(filename); - return ftfail(this, 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 = (gg_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; - netlog("gg_sendfile(): Requesting user to connect us and scheduling gg_dcc struct for a later use."); - EnterCriticalSection(&sess_mutex); - gg_dcc_request(sess, uin); - LeaveCriticalSection(&sess_mutex); - list_add(&requests, dcc, 0); - } - - // Write filename - if (gg_dcc_fill_file_info(dcc, filename) == -1) - { - netlog("gg_sendfile(): Cannot open and file fileinfo \"%s\".", filename); - gg_free_dcc(dcc); - mir_free(filename); - return ftfail(this, hContact); - } - - netlog("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(&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; -} - diff --git a/protocols/Gadu-Gadu/gadu-gadu-translation.txt b/protocols/Gadu-Gadu/gadu-gadu-translation.txt deleted file mode 100644 index 168fe833e4..0000000000 --- a/protocols/Gadu-Gadu/gadu-gadu-translation.txt +++ /dev/null @@ -1,233 +0,0 @@ -; Common strings that belong to many files -;[City:] -;[Conference] -;[Contact list] -;[Female] -;[List export successful.] -;[List import successful.] -;[Male] -;[Me] -;[Next image] -;[Open new conference] -;[Previous image] - -; ../../protocols/Gadu-Gadu/core.c -;[Age:] -;[Connection cannot be established because of error:\n\t%s] -;[External direct connections hostname %s is invalid. Disabling external host forwarding.] -;[Incoming image] -;[List remove successful.] -;[Server hostname %s is invalid. Using default hostname provided by the network.] -;[Unknown client] -;[You are logged in at another location] -;[You have logged in at another location] - -; ../../protocols/Gadu-Gadu/dialogs.c -;[] -;[] -;[Advanced] -;[Allow] -;[Ask] -;[Away] -;[DND] -;[Free for chat] -;[General] -;[Ignore] -;[Invisible] -;[Message with [img] BBCode] -;[Network] -;[Online] -;[Popup window] -;[System tray icon] -;[You have to be logged in before you can change your details.] -;[You need to specify your registration e-mail first.] -;[You should disconnect before making any permanent changes with your account.\nDo you want to disconnect now ?] -;[Your details has been uploaded to the public directory.] -;[Your password will be sent to your registration e-mail.\nDo you want to continue ?] - -; ../../protocols/Gadu-Gadu/gg.c -;[%s connection] -;[&Block] -;[&Unblock] -;[HTTP failed connecting] -;[HTTP failed reading] -;[HTTP failed resolving] -;[HTTP failed writing] -;[Unknown HTTP error] - -; ../../protocols/Gadu-Gadu/groupchat.c -;[%s has initiated conference with %d participants (%d unknowns).\nDo you want do participate ?] -;[%s initiated the conference.] -;[&Clear ignored conferences] -;['Unknown'] -;[All ignored conferences are now unignored and the conference policy will act again.] -;[Open &conference...] -;[Participants] -;[There are no ignored conferences.] -;[This is my own conference.] -;[Unknown] -;[You have to be connected to open new conference.] - -; ../../protocols/Gadu-Gadu/icolib.c -;[Account settings] -;[Block user] -;[Clear ignored conferences] -;[Concurrent sessions] -;[Delete image] -;[Export list to server] -;[Export list to text file] -;[Import list from server] -;[Import list from text file] -;[Protocol icon] -;[Protocols] -;[Remove list from server] -;[Save image] -;[Send image] - -; ../../protocols/Gadu-Gadu/image.c -;[&Image] -;[Delete image from the list] -;[Image cannot be written to disk.] -;[Image exceeds maximum allowed size of 255 KB.] -;[Image files (*.bmp,*.gif,*.jpeg,*.jpg,*.png)] -;[Image for %s] -;[Image from %s] -;[Save image to disk] -;[Select picture to send] - -; ../../protocols/Gadu-Gadu/import.c -;[&Remove List From Server] -;[All Files] -;[Export List To &Server] -;[Export List To &Text File...] -;[Import List From &Server] -;[Import List From &Text File...] -;[List cannot be exported because of error:\n\t%s] -;[List cannot be exported to file \"%s\" because of error:\n\t%s] -;[List cannot be imported because of error:\n\t%s] -;[List cannot be imported from file \"%s\" because of error:\n\t%s] -;[List cannot be removeed because of error:\n\t%s] -;[Text files] -;[You have to be connected before you can import/export contacts from/to server.] -;[contacts] - -; ../../protocols/Gadu-Gadu/links.c -;[Gadu-Gadu Link Protocol] - -; ../../protocols/Gadu-Gadu/ownerinfo.c -;[Password could not be reminded because of error:\n\t%s] -;[Password was sent to your e-mail.] - -; ../../protocols/Gadu-Gadu/popups.c -;[Error] -;[Notify] - -; ../../protocols/Gadu-Gadu/resource.rc -;[&Close] -;[&Create] -;[&Save changes] -;[&Send] -;[* new line is separator\n** hostname:port format] -;[Advanced Configuration] -;[After disconnection leave away message of status:] -;[Age from:] -;[Automatically reconnect after unintentional disconnection] -;[Birth year:] -;[Cancel] -;[Change Gadu-Gadu e-mail] -;[Change Gadu-Gadu e-mail\nChanges current Gadu-Gadu user e-mail] -;[Change Gadu-Gadu password] -;[Change Gadu-Gadu password\nChanges current Gadu-Gadu user password] -;[Change e-mail] -;[Change password] -;[Concurrent %s Login Sessions\nView information on active concurrent sessions] -;[Concurrent Sessions] -;[Conference policy] -;[Confirm password:] -;[Create Gadu-Gadu account] -;[Create Gadu-Gadu account\nThis will create new Gadu-Gadu account] -;[Create new account] -;[Description:] -;[E-mail:] -;[Enable avatars] -;[Enter token to continue] -;[Family name:] -;[File Transfer] -;[First name:] -;[Friends only] -;[Gadu-Gadu Number:] -;[Gadu-Gadu User Details] -;[Gender:] -;[Host:] -;[Ignore incoming conference messages] -;[Internal IP:] -;[Keep connection alive] -;[Last name:] -;[Manually specify connection servers' hosts] -;[New e-mail:] -;[New password:] -;[Nickname:] -;[Number:] -;[OK] -;[Open] -;[Open new conference\nSelect conference participants] -;[Options] -;[Origin city:] -;[Password:] -;[Port:] -;[Receive image and after image is received use:] -;[Remove] -;[Remove Gadu-Gadu account] -;[Remove Gadu-Gadu account\nThis will remove your Gadu-Gadu account] -;[Remove account] -;[Retrieve password] -;[Search online users only] -;[Send messages slower, but with full acknowledgement] -;[Show connection errors] -;[Show links from unknown contacts] -;[Show offline users with status message as invisible in contact list] -;[Sign out all sessions] -;[Use SSL secure connection] -;[Use direct connections] -;[Use forwarding] -;[Version:] -;[Yes, I want to remove my account] -;[You will need to reconnect for the changes you have made on this page to take effect.] -;[if total participant count greater than:] -;[if unknown participant count greater than:] -;[in other case] -;[to:] - -; ../../protocols/Gadu-Gadu/services.c -;[Gadu-Gadu Number] -;[To remove your Gadu-Gadu avatar, you must use the MojaGeneracja.pl website.] - -; ../../protocols/Gadu-Gadu/sessions.c -;[Action] -;[Client Name] -;[Concurrent &sessions] -;[Copy Text] -;[IP Address] -;[Login Time] -;[There are no active concurrent sessions for this account.] -;[Whois] -;[You have to be logged in to view concurrent sessions.] -;[sign out] - -; ../../protocols/Gadu-Gadu/token.c -;[Could not load token image.] -;[Token retrieval failed because of error:\n\t%s] - -; ../../protocols/Gadu-Gadu/userutils.c -;[Bad number or password] -;[Bad old e-mail or password] -;[Cannot register new account because of error:\n\t%s] -;[Invalid data entered] -;[Registration rejected] -;[You have registered new account.\nPlease fill up your personal details in \"M->View/Change My Details...\"] -;[Your account cannot be removed because of error:\n\t%s] -;[Your account has been removed.] -;[Your e-mail cannot be changed because of error:\n\t%s] -;[Your e-mail has been changed.] -;[Your password cannot be changed because of error:\n\t%s] -;[Your password has been changed.] diff --git a/protocols/Gadu-Gadu/gg.cpp b/protocols/Gadu-Gadu/gg.cpp deleted file mode 100644 index 5bd9f6ceb5..0000000000 --- a/protocols/Gadu-Gadu/gg.cpp +++ /dev/null @@ -1,523 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2009 Adam Strzelecki -// 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 - -// 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://miranda-ng.org/", - UNICODE_AWARE, - // {F3FF65F3-250E-416A-BEE9-58C93F85AB33} - { 0xf3ff65f3, 0x250e, 0x416a, { 0xbe, 0xe9, 0x58, 0xc9, 0x3f, 0x85, 0xab, 0x33 } } -}; - -// Other variables -HINSTANCE hInstance; - -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 - -TCHAR* ws_strerror(int code) -{ - static TCHAR err_desc[160]; - - // Not a windows error display WinSock - if (code == 0) - { - TCHAR buff[128]; - int len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, WSAGetLastError(), 0, buff, SIZEOF(buff), NULL); - if (len == 0) - mir_sntprintf(err_desc, SIZEOF(err_desc), _T("WinSock %u: Unknown error."), WSAGetLastError()); - else - mir_sntprintf(err_desc, SIZEOF(err_desc), _T("WinSock %d: %s"), WSAGetLastError(), buff); - return err_desc; - } - - // Return normal error - return _tcserror(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 - -extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion) -{ - return &pluginInfo; -} - -extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_PROTOCOL, MIID_LAST}; - -////////////////////////////////////////////////////////// -// Cleanups from last plugin - -void GGPROTO::cleanuplastplugin(DWORD version) -{ - HANDLE hContact; - char *szProto; - - // Remove bad e-mail and phones from - if (version < PLUGIN_MAKE_VERSION(0, 0, 1, 4)) - { -#ifdef DEBUGMODE - netlog("gg_cleanuplastplugin(%d): Cleaning junk Phone settings from < 0.0.1.4 ...", version); -#endif - // Look for contact in DB - hContact = db_find_first(); - while (hContact) - { - szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); - if (szProto != NULL && !strcmp(szProto, m_szModuleName)) - { - // Do contact cleanup - db_unset(hContact, m_szModuleName, GG_KEY_EMAIL); - db_unset(hContact, m_szModuleName, "Phone"); - } - hContact = db_find_next(hContact); - } - } - - // Remove GG entries for non GG contacts - if (version < PLUGIN_MAKE_VERSION(0, 0, 3, 5)) - { -#ifdef DEBUGMODE - netlog("gg_cleanuplastplugin(%d): Cleaning junk Nick settings from < 0.0.3.5 ...", version); -#endif - // Look for contact in DB - hContact = db_find_first(); - while (hContact) - { - szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); - if (szProto != NULL && strcmp(szProto, m_szModuleName)) - { - // Do nick entry cleanup - db_unset(hContact, m_szModuleName, GG_KEY_NICK); - } - hContact = db_find_next(hContact); - } - } - - // Remove old unneeded entry - if (version < PLUGIN_MAKE_VERSION(0, 0, 5, 3)) - db_unset(NULL, m_szModuleName, "ShowNotOnMyList"); - - // Store this plugin version - db_set_dw(NULL, m_szModuleName, GG_PLUGINVERSION, pluginInfo.version); -} - -////////////////////////////////////////////////////////// -// 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 = (GGPROTO*)l->data; - if (strcmp(szProto, gg->m_szModuleName) == 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 ( db_get_dw(hContact, gg->m_szModuleName, GG_KEY_UIN, 0) == db_get_b(NULL, gg->m_szModuleName, GG_KEY_UIN, 0) || - db_get_b(hContact, gg->m_szModuleName, "ChatRoom", 0) || - db_get_b(hContact, "CList", "NotOnList", 0)) - mi.flags |= CMIF_HIDDEN; - mi.pszName = db_get_b(hContact, gg->m_szModuleName, 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 GGPROTO::blockuser(WPARAM wParam, LPARAM lParam) -{ - const HANDLE hContact = (HANDLE)wParam; - db_set_b(hContact, m_szModuleName, GG_KEY_BLOCK, !db_get_b(hContact, m_szModuleName, GG_KEY_BLOCK, 0)); - notifyuser(hContact, 1); - return 0; -} - - -////////////////////////////////////////////////////////// -// Contact blocking initialization - -#define GGS_BLOCKUSER "%s/BlockUser" -void GGPROTO::block_init() -{ - CLISTMENUITEM mi = {0}; - char service[64]; - - mi.cbSize = sizeof(mi); - mi.flags = CMIF_ICONFROMICOLIB; - - mir_snprintf(service, sizeof(service), GGS_BLOCKUSER, m_szModuleName); - createObjService(service, &GGPROTO::blockuser); - mi.position = -500050000; - mi.icolibItem = GetIconHandle(IDI_BLOCK); - mi.pszName = LPGEN("&Block"); - mi.pszService = service; - mi.pszContactOwner = m_szModuleName; - hBlockMenuItem = Menu_AddContactMenuItem(&mi); - - hPrebuildMenuHook = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, gg_prebuildcontactmenu); -} - -////////////////////////////////////////////////////////// -// Contact blocking uninitialization - -void GGPROTO::block_uninit() -{ - UnhookEvent(hPrebuildMenuHook); - CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)hBlockMenuItem, 0); -} - -////////////////////////////////////////////////////////// -// Menus initialization -void GGPROTO::menus_init() -{ - HGENMENU hGCRoot, hCLRoot, hRoot = MO_GetProtoRootMenu(m_szModuleName); - CLISTMENUITEM mi = {0}; - - mi.cbSize = sizeof(mi); - if (hRoot == NULL) - { - mi.ptszName = m_tszUserName; - 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 = hMenuRoot = Menu_AddProtoMenuItem(&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 = Menu_AddProtoMenuItem(&mi); - - mi.ptszName = LPGENT("Contact list"); - mi.position = 200002; - mi.icolibItem = GetIconHandle(IDI_LIST); - hCLRoot = Menu_AddProtoMenuItem(&mi); - - if (hMenuRoot) - CallService(MS_CLIST_REMOVEMAINMENUITEM, (WPARAM)hMenuRoot, 0); - hMenuRoot = NULL; - } - - gc_menus_init(hGCRoot); - import_init(hCLRoot); - sessions_menus_init(hRoot); -} - -////////////////////////////////////////////////////////// -// Module instance initialization - -static GGPROTO *gg_proto_init(const char* pszProtoName, const TCHAR* tszUserName) -{ - GGPROTO *gg = new GGPROTO(pszProtoName, tszUserName); - list_add(&g_Instances, gg, 0); - return gg; -} - -////////////////////////////////////////////////////////// -// Module instance uninitialization - -static int gg_proto_uninit(PROTO_INTERFACE *proto) -{ - GGPROTO *gg = (GGPROTO *)proto; - list_remove(&g_Instances, gg, 0); - delete gg; - return 0; -} - -////////////////////////////////////////////////////////// -// When plugin is loaded - -extern "C" int __declspec(dllexport) Load(void) -{ - mir_getXI(&xi); - mir_getLP(&pluginInfo); - - 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 - PROTOCOLDESCRIPTOR pd = { 0 }; - 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 - -extern "C" int __declspec(dllexport) Unload() -{ - LocalEventUnhook(hHookModulesLoaded); - LocalEventUnhook(hHookPreShutdown); - - // Cleanup WinSock - WSACleanup(); - return 0; -} - -////////////////////////////////////////////////////////// -// 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, ""} -}; - -const char *ggdebug_eventtype(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 GGPROTO::netlog(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)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 deleted file mode 100644 index 742f06b093..0000000000 --- a/protocols/Gadu-Gadu/gg.h +++ /dev/null @@ -1,341 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2009 Adam Strzelecki -// 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 - -// Windows headers -// Visual C++ .NET tries to include winsock.h -// which is very ver bad -#if (_MSC_VER >= 1300) -#include -#else -#include -#endif -#include -#include -#include -#include -#include -#include - -// Miranda IM headers -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// 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 -extern "C" { -#include "libgadu/libgadu.h" -#include "dynstuff.h" -}; - -// Search -// Extended search result structure, used for all searches -struct GGSEARCHRESULT : public PROTOSEARCHRESULT -{ - uin_t uin; -}; - -typedef struct -{ - HANDLE hThread; - UINT dwThreadId; -} GGTHREAD; - -typedef struct -{ - uin_t *recipients; - int recipients_count; - char id[32]; - BOOL ignore; -} GGGC; - -typedef struct -{ - char id[256]; - char val[256]; -} GGTOKEN; - -#if 0 /* #ifdef DEBUGMODE */ -#define EnterCriticalSection(lpCS) {netlog(gg,"EnterCriticalSection @ %s:%d", __FILE__, __LINE__); EnterCriticalSection(lpCS);} -#define LeaveCriticalSection(lpCS) {netlog(gg,"LeaveCriticalSection @ %s:%d", __FILE__, __LINE__); LeaveCriticalSection(lpCS);} -#endif - - -// Wrappers of the old interface -#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 CLIST_INTERFACE *pcli; -extern list_t g_Instances; -extern PLUGININFOEX pluginInfo; - -// 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 gg_normalizestatus(int status); -char *gg_status2db(int status, const char *suffix); -TCHAR *ws_strerror(int code); -uint32_t swap32(uint32_t x); -const char *gg_version2string(int v); - -/* Avatar functions */ -char *gg_avatarhash(char *param); - -/* 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(); - -#define UIN2ID(uin,id) _itoa(uin,id,10) - -// Debug functions -const char *ggdebug_eventtype(gg_event *e); - -#include "gg_proto.h" - -#endif diff --git a/protocols/Gadu-Gadu/gg_proto.cpp b/protocols/Gadu-Gadu/gg_proto.cpp deleted file mode 100644 index 0a0db3a173..0000000000 --- a/protocols/Gadu-Gadu/gg_proto.cpp +++ /dev/null @@ -1,804 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2009 Adam Strzelecki -// 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" - -GGPROTO::GGPROTO(const char* pszProtoName, const TCHAR* tszUserName) -{ - // Init mutexes - InitializeCriticalSection(&sess_mutex); - InitializeCriticalSection(&ft_mutex); - InitializeCriticalSection(&img_mutex); - InitializeCriticalSection(&modemsg_mutex); - InitializeCriticalSection(&avatar_mutex); - InitializeCriticalSection(&sessions_mutex); - - // Init instance names - m_szModuleName = mir_strdup(pszProtoName); - m_tszUserName = mir_tstrdup(tszUserName); - m_szProtoName = GGDEF_PROTONAME; - m_iVersion = 2; - - // Register netlib user - TCHAR name[128]; - mir_sntprintf(name, SIZEOF(name), TranslateT("%s connection"), m_tszUserName); - - NETLIBUSER nlu = { 0 }; - nlu.cbSize = sizeof(nlu); - nlu.flags = NUF_TCHAR | NUF_OUTGOING | NUF_INCOMING | NUF_HTTPCONNS; - nlu.szSettingsModule = m_szModuleName; - nlu.ptszDescriptiveName = name; - - netlib = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu); - - // Register services - createProtoService(PS_GETAVATARCAPS, &GGPROTO::getavatarcaps); - createProtoService(PS_GETAVATARINFOT, &GGPROTO::getavatarinfo); - createProtoService(PS_GETMYAVATAR, &GGPROTO::getmyavatar); - createProtoService(PS_SETMYAVATAR, &GGPROTO::setmyavatar); - - createProtoService(PS_GETMYAWAYMSG, &GGPROTO::getmyawaymsg); - createProtoService(PS_CREATEACCMGRUI, &GGPROTO::get_acc_mgr_gui); - - createProtoService(PS_LEAVECHAT, &GGPROTO::leavechat); - - // Offline contacts and clear logon time - setalloffline(); - db_set_dw(NULL, m_szModuleName, GG_KEY_LOGONTIME, 0); - - DWORD dwVersion; - if ((dwVersion = db_get_dw(NULL, m_szModuleName, GG_PLUGINVERSION, 0)) < pluginInfo.version) - cleanuplastplugin(dwVersion); - - links_instance_init(); - initavatarrequestthread(); - - TCHAR szPath[MAX_PATH]; - TCHAR *tmpPath = Utils_ReplaceVarsT( _T("%miranda_avatarcache%")); - mir_sntprintf(szPath, MAX_PATH, _T("%s\\%s"), tmpPath, m_szModuleName); - mir_free(tmpPath); - hAvatarsFolder = FoldersRegisterCustomPathT(m_szModuleName, "Avatars", szPath); - - tmpPath = Utils_ReplaceVarsT( _T("%miranda_userdata%")); - mir_sntprintf(szPath, MAX_PATH, _T("%s\\%s\\ImageCache"), tmpPath, m_szModuleName); - mir_free(tmpPath); - hImagesFolder = FoldersRegisterCustomPathT(m_szModuleName, "Images", szPath); -} - -GGPROTO::~GGPROTO() -{ -#ifdef DEBUGMODE - netlog("gg_proto_uninit(): destroying protocol interface"); -#endif - - // Destroy modules - block_uninit(); - img_destroy(); - keepalive_destroy(); - gc_destroy(); - - if (hMenuRoot) - CallService(MS_CLIST_REMOVEMAINMENUITEM, (WPARAM)hMenuRoot, 0); - - // Close handles - Netlib_CloseHandle(netlib); - - // Destroy mutexes - DeleteCriticalSection(&sess_mutex); - DeleteCriticalSection(&ft_mutex); - DeleteCriticalSection(&img_mutex); - DeleteCriticalSection(&modemsg_mutex); - DeleteCriticalSection(&avatar_mutex); - DeleteCriticalSection(&sessions_mutex); - - // Free status messages - if (modemsg.online) mir_free(modemsg.online); - if (modemsg.away) mir_free(modemsg.away); - if (modemsg.dnd) mir_free(modemsg.dnd); - if (modemsg.freechat) mir_free(modemsg.freechat); - if (modemsg.invisible) mir_free(modemsg.invisible); - if (modemsg.offline) mir_free(modemsg.offline); - - mir_free(m_szModuleName); - mir_free(m_tszUserName); -} - -////////////////////////////////////////////////////////// -// Dummies for function that have to be implemented - -HANDLE GGPROTO::AddToListByEvent(int flags, int iContact, HANDLE hDbEvent) { return NULL; } -int GGPROTO::Authorize(HANDLE hDbEvent) { return 1; } -int GGPROTO::AuthDeny(HANDLE hDbEvent, const TCHAR *szReason) { return 1; } -int GGPROTO::AuthRecv(HANDLE hContact, PROTORECVEVENT *pre) { return 1; } -int GGPROTO::AuthRequest(HANDLE hContact, const TCHAR *szMessage) { return 1; } -HANDLE GGPROTO::ChangeInfo(int iInfoType, void *pInfoData) { return NULL; } -int GGPROTO::FileResume(HANDLE hTransfer, int *action, const PROTOCHAR** szFilename) { return 1; } -HANDLE GGPROTO::SearchByEmail(const PROTOCHAR *email) { return NULL; } -int GGPROTO::RecvContacts(HANDLE hContact, PROTORECVEVENT *pre) { return 1; } -int GGPROTO::RecvUrl(HANDLE hContact, PROTORECVEVENT *pre) { return 1; } -int GGPROTO::SendContacts(HANDLE hContact, int flags, int nContacts, HANDLE *hContactsList) { return 1; } -int GGPROTO::SendUrl(HANDLE hContact, int flags, const char *url) { return 1; } -int GGPROTO::RecvAwayMsg(HANDLE hContact, int mode, PROTORECVEVENT *evt) { return 1; } -int GGPROTO::SendAwayMsg(HANDLE hContact, HANDLE hProcess, const char *msg) { return 1; } - -////////////////////////////////////////////////////////// -// when contact is added to list - -HANDLE GGPROTO::AddToList(int flags, PROTOSEARCHRESULT *psr) -{ - GGSEARCHRESULT *sr = (GGSEARCHRESULT *)psr; - uin_t uin; - - if (psr->cbSize == sizeof(GGSEARCHRESULT)) - uin = sr->uin; - else - uin = _ttoi(psr->id); - - return getcontact(uin, 1, flags & PALF_TEMPORARY ? 0 : 1, sr->nick); -} - -////////////////////////////////////////////////////////// -// checks proto capabilities - -DWORD_PTR GGPROTO::GetCaps(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 GGPROTO::GetIcon(int iconIndex) -{ - if (LOWORD(iconIndex) == PLI_PROTOCOL) - { - if (iconIndex & PLIF_ICOLIBHANDLE) - return (HICON)GetIconHandle(IDI_GG); - - BOOL big = (iconIndex & PLIF_SMALL) == 0; - HICON hIcon = LoadIconEx("main", big); - - if (iconIndex & PLIF_ICOLIB) - return hIcon; - - hIcon = CopyIcon(hIcon); - ReleaseIconEx("main", big); - return hIcon; - } - - return (HICON)NULL; -} - -////////////////////////////////////////////////////////// -// user info request - -void __cdecl GGPROTO::cmdgetinfothread(void *hContact) -{ - SleepEx(100, FALSE); - netlog("gg_cmdgetinfothread(): Failed info retreival."); - ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE) 1, 0); -} - -int GGPROTO::GetInfo(HANDLE hContact, int infoType) -{ - gg_pubdir50_t req; - - // Custom contact info - if (hContact) - { - if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH))) - { - forkthread(&GGPROTO::cmdgetinfothread, hContact); - return 1; - } - - // Add uin and search it - gg_pubdir50_add(req, GG_PUBDIR50_UIN, ditoa((uin_t)db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0))); - gg_pubdir50_seq_set(req, GG_SEQ_INFO); - - netlog("gg_getinfo(): Requesting user info.", req->seq); - if (isonline()) - { - EnterCriticalSection(&sess_mutex); - if (!gg_pubdir50(sess, req)) - { - LeaveCriticalSection(&sess_mutex); - forkthread(&GGPROTO::cmdgetinfothread, hContact); - return 1; - } - LeaveCriticalSection(&sess_mutex); - } - } - // Own contact info - else - { - if (!(req = gg_pubdir50_new(GG_PUBDIR50_READ))) - { - forkthread(&GGPROTO::cmdgetinfothread, hContact); - return 1; - } - - // Add seq - gg_pubdir50_seq_set(req, GG_SEQ_CHINFO); - - netlog("gg_getinfo(): Requesting owner info.", req->seq); - if (isonline()) - { - EnterCriticalSection(&sess_mutex); - if (!gg_pubdir50(sess, req)) - { - LeaveCriticalSection(&sess_mutex); - forkthread(&GGPROTO::cmdgetinfothread, hContact); - return 1; - } - LeaveCriticalSection(&sess_mutex); - } - } - netlog("gg_getinfo(): Seq %d.", req->seq); - gg_pubdir50_free(req); - - return 1; -} - -////////////////////////////////////////////////////////// -// when basic search - -void __cdecl GGPROTO::searchthread(void *) -{ - SleepEx(100, FALSE); - netlog("gg_searchthread(): Failed search."); - ProtoBroadcastAck(m_szModuleName, NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, (HANDLE)1, 0); -} - -HANDLE GGPROTO::SearchBasic(const PROTOCHAR *id) -{ - if (!isonline()) - return (HANDLE)0; - - gg_pubdir50_t req; - if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH))) { - forkthread(&GGPROTO::searchthread, NULL); - return (HANDLE)1; - } - - TCHAR *ida = mir_tstrdup(id); - - // Add uin and search it - gg_pubdir50_add(req, GG_PUBDIR50_UIN, _T2A(ida)); - gg_pubdir50_seq_set(req, GG_SEQ_SEARCH); - - mir_free(ida); - - EnterCriticalSection(&sess_mutex); - if (!gg_pubdir50(sess, req)) - { - LeaveCriticalSection(&sess_mutex); - forkthread(&GGPROTO::searchthread, NULL); - return (HANDLE)1; - } - LeaveCriticalSection(&sess_mutex); - netlog("gg_basicsearch(): Seq %d.", req->seq); - gg_pubdir50_free(req); - - return (HANDLE)1; -} - -////////////////////////////////////////////////////////// -// search by details - -HANDLE GGPROTO::SearchByName(const PROTOCHAR *nick, const PROTOCHAR *firstName, const PROTOCHAR *lastName) -{ - gg_pubdir50_t req; - unsigned long crc; - char data[512] = "\0"; - - // Check if connected and if there's a search data - if (!isonline()) - return 0; - - if (!nick && !firstName && !lastName) - return 0; - - if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH))) - { - forkthread(&GGPROTO::searchthread, NULL); - return (HANDLE)1; - } - - // Add uin and search it - if (nick) - { - char *nickA = mir_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 = mir_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 = mir_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 == last_crc && next_uin) - gg_pubdir50_add(req, GG_PUBDIR50_START, ditoa(next_uin)); - else - last_crc = crc; - - gg_pubdir50_seq_set(req, GG_SEQ_SEARCH); - - EnterCriticalSection(&sess_mutex); - if (!gg_pubdir50(sess, req)) - { - LeaveCriticalSection(&sess_mutex); - forkthread(&GGPROTO::searchthread, NULL); - return (HANDLE)1; - } - LeaveCriticalSection(&sess_mutex); - netlog("gg_searchbyname(): Seq %d.", req->seq); - gg_pubdir50_free(req); - - return (HANDLE)1; -} - -////////////////////////////////////////////////////////// -// search by advanced - -HWND GGPROTO::SearchAdvanced(HWND hwndDlg) -{ - gg_pubdir50_t req; - char text[64], data[512] = "\0"; - unsigned long crc; - - // Check if connected - if (!isonline()) return (HWND)0; - - if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH))) - { - forkthread(&GGPROTO::searchthread, NULL); - return (HWND)1; - } - - // Fetch search data - GetDlgItemTextA(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)); - - GetDlgItemTextA(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)); - - GetDlgItemTextA(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)); - - GetDlgItemTextA(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)); - - GetDlgItemTextA(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]; - - GetDlgItemTextA(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 == last_crc && next_uin) - gg_pubdir50_add(req, GG_PUBDIR50_START, ditoa(next_uin)); - else - last_crc = crc; - - gg_pubdir50_seq_set(req, GG_SEQ_SEARCH); - - if (isonline()) - { - EnterCriticalSection(&sess_mutex); - if (!gg_pubdir50(sess, req)) - { - LeaveCriticalSection(&sess_mutex); - forkthread(&GGPROTO::searchthread, NULL); - return (HWND)1; - } - LeaveCriticalSection(&sess_mutex); - } - netlog("gg_searchbyadvanced(): Seq %d.", req->seq); - gg_pubdir50_free(req); - - return (HWND)1; -} - -////////////////////////////////////////////////////////// -// create adv search dialog - -static 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)TranslateT("Female")); // 1 - SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)TranslateT("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; - } - break; - } - return FALSE; -} - -HWND GGPROTO::CreateExtendedSearchUI(HWND owner) -{ - return CreateDialogParam(hInstance, - MAKEINTRESOURCE(IDD_GGADVANCEDSEARCH), owner, gg_advancedsearchdlgproc, (LPARAM)this); -} - -////////////////////////////////////////////////////////// -// when messsage received - -int GGPROTO::RecvMsg(HANDLE hContact, PROTORECVEVENT *pre) -{ - return Proto_RecvMessage(hContact, pre); -} - -////////////////////////////////////////////////////////// -// when messsage sent - -typedef struct -{ - HANDLE hContact; - int seq; -} GG_SEQ_ACK; - -void __cdecl GGPROTO::sendackthread(void *ack) -{ - SleepEx(100, FALSE); - ProtoBroadcastAck(m_szModuleName, ((GG_SEQ_ACK *)ack)->hContact, - ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE) ((GG_SEQ_ACK *)ack)->seq, 0); - mir_free(ack); -} - -int GGPROTO::SendMsg(HANDLE hContact, int flags, const char *msg) -{ - uin_t uin; - - if (msg && isonline() && (uin = (uin_t)db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0))) - { - int seq; - EnterCriticalSection(&sess_mutex); - seq = gg_send_message(sess, GG_CLASS_CHAT, uin, (BYTE*)msg); - LeaveCriticalSection(&sess_mutex); - if (!db_get_b(NULL, m_szModuleName, GG_KEY_MSGACK, GG_KEYDEF_MSGACK)) - { - // Auto-ack message without waiting for server ack - GG_SEQ_ACK *ack = (GG_SEQ_ACK*)mir_alloc(sizeof(GG_SEQ_ACK)); - if (ack) - { - ack->seq = seq; - ack->hContact = hContact; - forkthread(&GGPROTO::sendackthread, ack); - } - } - return seq; - } - return 0; -} - -////////////////////////////////////////////////////////// -// visible lists - -int GGPROTO::SetApparentMode(HANDLE hContact, int mode) -{ - db_set_w(hContact, m_szModuleName, GG_KEY_APPARENT, (WORD)mode); - notifyuser(hContact, 1); - return 0; -} - -////////////////////////////////////////////////////////// -// sets protocol status - -int GGPROTO::SetStatus(int iNewStatus) -{ - int nNewStatus = gg_normalizestatus(iNewStatus); - - EnterCriticalSection(&modemsg_mutex); - m_iDesiredStatus = nNewStatus; - LeaveCriticalSection(&modemsg_mutex); - - // If waiting for connection retry attempt then signal to stop that - if (hConnStopEvent) SetEvent(hConnStopEvent); - - if (m_iStatus == nNewStatus) return 0; - netlog("gg_setstatus(): PS_SETSTATUS(%d) normalized to %d.", iNewStatus, nNewStatus); - refreshstatus(nNewStatus); - - return 0; -} - -////////////////////////////////////////////////////////// -// when away message is requested - -void __cdecl GGPROTO::getawaymsgthread(void *hContact) -{ - DBVARIANT dbv; - - SleepEx(100, FALSE); - if (!db_get_s(hContact, "CList", GG_KEY_STATUSDESCR, &dbv, DBVT_TCHAR)) - { - ProtoBroadcastAck(m_szProtoName, hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE) 1, (LPARAM) dbv.ptszVal); - netlog("gg_getawaymsg(): Reading away msg <" TCHAR_STR_PARAM ">.", dbv.ptszVal); - DBFreeVariant(&dbv); - } - else - ProtoBroadcastAck(m_szProtoName, hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE) 1, (LPARAM) NULL); -} - -HANDLE GGPROTO::GetAwayMsg(HANDLE hContact) -{ - forkthread(&GGPROTO::getawaymsgthread, hContact); - return (HANDLE)1; -} - -////////////////////////////////////////////////////////// -// when away message is being set - -int GGPROTO::SetAwayMsg(int iStatus, const PROTOCHAR *msgt) -{ - int status = gg_normalizestatus(iStatus); - char **szMsg; - char *msg = mir_t2a(msgt); - - netlog("gg_setawaymsg(): PS_SETAWAYMSG(%d, \"%s\").", iStatus, msg); - - EnterCriticalSection(&modemsg_mutex); - // Select proper msg - switch(status) - { - case ID_STATUS_ONLINE: - szMsg = &modemsg.online; - break; - case ID_STATUS_AWAY: - szMsg = &modemsg.away; - break; - case ID_STATUS_DND: - szMsg = &modemsg.dnd; - break; - case ID_STATUS_FREECHAT: - szMsg = &modemsg.freechat; - break; - case ID_STATUS_INVISIBLE: - szMsg = &modemsg.invisible; - break; - default: - LeaveCriticalSection(&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 == m_iDesiredStatus && m_iDesiredStatus == m_iStatus) - { - netlog("gg_setawaymsg(): Message hasn't been changed, return."); - LeaveCriticalSection(&modemsg_mutex); - mir_free(msg); - return 0; - } - } - else - { - if (*szMsg) - mir_free(*szMsg); - *szMsg = msg && *msg ? mir_strdup(msg) : NULL; -#ifdef DEBUGMODE - netlog("gg_setawaymsg(): Message changed."); -#endif - } - LeaveCriticalSection(&modemsg_mutex); - - // Change the status if it was desired by PS_SETSTATUS - if (status == m_iDesiredStatus) - refreshstatus(status); - - mir_free(msg); - return 0; -} - -////////////////////////////////////////////////////////// -// sends a notification that the user is typing a message - -int GGPROTO::UserIsTyping(HANDLE hContact, int type) -{ - uin_t uin = db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0); - - if (!uin || !isonline()) return 0; - - if (type == PROTOTYPE_SELFTYPING_ON || type == PROTOTYPE_SELFTYPING_OFF) { - EnterCriticalSection(&sess_mutex); - gg_typing_notification(sess, uin, (type == PROTOTYPE_SELFTYPING_ON)); - LeaveCriticalSection(&sess_mutex); - } - - return 0; -} - -////////////////////////////////////////////////////////// -// Custom protocol event - -int GGPROTO::OnEvent(PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam) -{ - switch( eventType ) { - case EV_PROTO_ONLOAD: - { - hookProtoEvent(ME_OPT_INITIALISE, &GGPROTO::options_init); - hookProtoEvent(ME_USERINFO_INITIALISE, &GGPROTO::details_init); - - // Init misc stuff - gg_icolib_init(); - initpopups(); - gc_init(); - keepalive_init(); - img_init(); - block_init(); - - // Try to fetch user avatar - getUserAvatar(); - break; - } - case EV_PROTO_ONEXIT: - // Stop avatar request thread - uninitavatarrequestthread(); - - // Stop main connection session thread - threadwait(&pth_sess); - img_shutdown(); - sessions_closedlg(); - break; - - case EV_PROTO_ONOPTIONS: - return options_init(wParam, lParam); - - case EV_PROTO_ONMENU: - menus_init(); - break; - - case EV_PROTO_ONRENAME: - if (hMenuRoot) { - CLISTMENUITEM mi = {0}; - mi.cbSize = sizeof(mi); - mi.flags = CMIM_NAME | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED; - mi.ptszName = m_tszUserName; - CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuRoot, (LPARAM)&mi); - } - break; - - case EV_PROTO_ONCONTACTDELETED: - return contactdeleted(wParam, lParam); - - case EV_PROTO_DBSETTINGSCHANGED: - return dbsettingchanged(wParam, lParam); - } - return TRUE; -} diff --git a/protocols/Gadu-Gadu/gg_proto.h b/protocols/Gadu-Gadu/gg_proto.h deleted file mode 100644 index 8765392328..0000000000 --- a/protocols/Gadu-Gadu/gg_proto.h +++ /dev/null @@ -1,295 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2009 Adam Strzelecki -// 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 GGPROTO_H -#define GGPROTO_H - -struct GGPROTO; -typedef void ( __cdecl GGPROTO::*GGThreadFunc )( void* ); -typedef int ( __cdecl GGPROTO::*GGEventFunc )( WPARAM, LPARAM ); -typedef INT_PTR ( __cdecl GGPROTO::*GGServiceFunc )( WPARAM, LPARAM ); - -struct GGPROTO : public PROTO_INTERFACE, public MZeroedObject -{ - GGPROTO( const char*, const TCHAR* ); - ~GGPROTO(); - - //==================================================================================== - // PROTO_INTERFACE - //==================================================================================== - - virtual HANDLE __cdecl AddToList( int flags, PROTOSEARCHRESULT* psr ); - virtual HANDLE __cdecl AddToListByEvent( int flags, int iContact, HANDLE hDbEvent ); - - virtual int __cdecl Authorize( HANDLE hDbEvent ); - virtual int __cdecl AuthDeny( HANDLE hDbEvent, const TCHAR* szReason ); - virtual int __cdecl AuthRecv( HANDLE hContact, PROTORECVEVENT* ); - virtual int __cdecl AuthRequest( HANDLE hContact, const TCHAR* szMessage ); - - virtual HANDLE __cdecl ChangeInfo( int iInfoType, void* pInfoData ); - - virtual HANDLE __cdecl FileAllow( HANDLE hContact, HANDLE hTransfer, const TCHAR* szPath ); - virtual int __cdecl FileCancel( HANDLE hContact, HANDLE hTransfer ); - virtual int __cdecl FileDeny( HANDLE hContact, HANDLE hTransfer, const TCHAR* szReason ); - virtual int __cdecl FileResume( HANDLE hTransfer, int* action, const TCHAR** szFilename ); - - virtual DWORD_PTR __cdecl GetCaps( int type, HANDLE hContact = NULL ); - virtual HICON __cdecl GetIcon( int iconIndex ); - virtual int __cdecl GetInfo( HANDLE hContact, int infoType ); - - virtual HANDLE __cdecl SearchBasic( const TCHAR* id ); - virtual HANDLE __cdecl SearchByEmail( const TCHAR* email ); - virtual HANDLE __cdecl SearchByName( const TCHAR* nick, const TCHAR* firstName, const TCHAR* lastName ); - virtual HWND __cdecl SearchAdvanced( HWND owner ); - virtual HWND __cdecl CreateExtendedSearchUI( HWND owner ); - - virtual int __cdecl RecvContacts( HANDLE hContact, PROTORECVEVENT* ); - virtual int __cdecl RecvFile( HANDLE hContact, PROTORECVFILET* ); - virtual int __cdecl RecvMsg( HANDLE hContact, PROTORECVEVENT* ); - virtual int __cdecl RecvUrl( HANDLE hContact, PROTORECVEVENT* ); - - virtual int __cdecl SendContacts( HANDLE hContact, int flags, int nContacts, HANDLE* hContactsList ); - virtual HANDLE __cdecl SendFile( HANDLE hContact, const TCHAR* szDescription, TCHAR** ppszFiles ); - virtual int __cdecl SendMsg( HANDLE hContact, int flags, const char* msg ); - virtual int __cdecl SendUrl( HANDLE hContact, int flags, const char* url ); - - virtual int __cdecl SetApparentMode( HANDLE hContact, int mode ); - virtual int __cdecl SetStatus( int iNewStatus ); - - virtual HANDLE __cdecl GetAwayMsg( HANDLE hContact ); - virtual int __cdecl RecvAwayMsg( HANDLE hContact, int mode, PROTORECVEVENT* evt ); - virtual int __cdecl SendAwayMsg( HANDLE hContact, HANDLE hProcess, const char* msg ); - virtual int __cdecl SetAwayMsg( int m_iStatus, const TCHAR* msg ); - - virtual int __cdecl UserIsTyping( HANDLE hContact, int type ); - - virtual int __cdecl OnEvent( PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam ); - - ////////////////////////////////////////////////////////////////////////////////////// - // Services - - INT_PTR __cdecl blockuser(WPARAM wParam, LPARAM lParam); - INT_PTR __cdecl getmyawaymsg(WPARAM wParam, LPARAM lParam); - INT_PTR __cdecl get_acc_mgr_gui(WPARAM wParam, LPARAM lParam); - INT_PTR __cdecl leavechat(WPARAM wParam, LPARAM lParam); - - void __cdecl sendackthread(void *); - void __cdecl searchthread(void *); - void __cdecl cmdgetinfothread(void *hContact); - void __cdecl getawaymsgthread(void *hContact); - void __cdecl dccmainthread(void *); - void __cdecl ftfailthread(void *param); - void __cdecl remindpasswordthread(void *param); - - ////////////////////////////////////////////////////////////////////////////////////// - - /* Helper functions */ - int status_m2gg(int status, int descr); - int status_gg2m(int status); - void checknewuser(uin_t uin, const char* passwd); - - /* Thread functions */ - void forkthread(GGThreadFunc pFunc, void *param); - HANDLE forkthreadex(GGThreadFunc pFunc, void *param, UINT *threadId); - void threadwait(GGTHREAD *thread); - - /* Global GG functions */ - void notifyuser(HANDLE hContact, int refresh); - void setalloffline(); - void disconnect(); - HANDLE getcontact(uin_t uin, int create, int inlist, TCHAR *nick); - void __cdecl mainthread(void *empty); - int isonline(); - int refreshstatus(int status); - - void broadcastnewstatus(int newStatus); - void cleanuplastplugin(DWORD version); - int contactdeleted(WPARAM wParam, LPARAM lParam); - int dbsettingchanged(WPARAM wParam, LPARAM lParam); - void notifyall(); - void changecontactstatus(uin_t uin, int status, const char *idescr, int time, uint32_t remote_ip, uint16_t remote_port, uint32_t version); - char *getstatusmsg(int status); - void dccstart(); - void dccconnect(uin_t uin); - int gettoken(GGTOKEN *token); - void parsecontacts(char *contacts); - void remindpassword(uin_t uin, const char *email); - void menus_init(); - - /* Avatar functions */ - void getAvatarFilename(HANDLE hContact, TCHAR *pszDest, int cbLen); - void getAvatar(HANDLE hContact, char *szAvatarURL); - void requestAvatar(HANDLE hContact, int iWaitFor); - void getUserAvatar(); - void setAvatar(const TCHAR *szFilename); - void getAvatarFileInfo(uin_t uin, char **avatarurl, int *type); - - INT_PTR __cdecl getavatarcaps(WPARAM wParam, LPARAM lParam); - INT_PTR __cdecl getavatarinfo(WPARAM wParam, LPARAM lParam); - INT_PTR __cdecl getmyavatar(WPARAM wParam, LPARAM lParam); - INT_PTR __cdecl setmyavatar(WPARAM wParam, LPARAM lParam); - - void initavatarrequestthread(); - void uninitavatarrequestthread(); - - void __cdecl avatarrequestthread(void*); - void __cdecl getuseravatarthread(void*); - void __cdecl setavatarthread(void*); - - /* File transfer functions */ - HANDLE fileallow(HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath); - int filecancel(HANDLE hContact, HANDLE hTransfer); - int filedeny(HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szReason); - int recvfile(HANDLE hContact, PROTOFILEEVENT* pre); - HANDLE sendfile(HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles); - - HANDLE dccfileallow(HANDLE hTransfer, const PROTOCHAR* szPath); - HANDLE dcc7fileallow(HANDLE hTransfer, const PROTOCHAR* szPath); - - int dccfiledeny(HANDLE hTransfer); - int dcc7filedeny(HANDLE hTransfer); - - int dccfilecancel(HANDLE hTransfer); - int dcc7filecancel(HANDLE hTransfer); - - /* Import module */ - void import_init(HGENMENU hRoot); - - INT_PTR __cdecl import_server(WPARAM wParam, LPARAM lParam); - INT_PTR __cdecl import_text(WPARAM wParam, LPARAM lParam); - INT_PTR __cdecl remove_server(WPARAM wParam, LPARAM lParam); - INT_PTR __cdecl export_server(WPARAM wParam, LPARAM lParam); - INT_PTR __cdecl export_text(WPARAM wParam, LPARAM lParam); - - /* Keep-alive module */ - void keepalive_init(); - void keepalive_destroy(); - - /* Image reception functions */ - int img_init(); - int img_destroy(); - int img_shutdown(); - int img_sendonrequest(gg_event* e); - BOOL img_opened(uin_t uin); - void *img_loadpicture(gg_event* e, TCHAR *szFileName); - int img_display(HANDLE hContact, void *img); - int img_displayasmsg(HANDLE hContact, void *img); - - void __cdecl img_dlgcallthread(void *param); - - INT_PTR __cdecl img_recvimage(WPARAM wParam, LPARAM lParam); - INT_PTR __cdecl img_sendimg(WPARAM wParam, LPARAM lParam); - - void links_instance_init(); - - /* OAuth functions */ - char *oauth_header(const char *httpmethod, const char *url); - int oauth_checktoken(int force); - int oauth_receivetoken(); - - /* UI page initializers */ - int __cdecl options_init(WPARAM wParam, LPARAM lParam); - int __cdecl details_init(WPARAM wParam, LPARAM lParam); - - /* Groupchat functions */ - int gc_init(); - void gc_menus_init(HGENMENU hRoot); - int gc_destroy(); - char * gc_getchat(uin_t sender, uin_t *recipients, int recipients_count); - GGGC *gc_lookup(char *id); - int gc_changenick(HANDLE hContact, char *pszNick); - #define UIN2ID(uin,id) _itoa(uin,id,10) - - int __cdecl gc_event(WPARAM wParam, LPARAM lParam); - - INT_PTR __cdecl gc_openconf(WPARAM wParam, LPARAM lParam); - INT_PTR __cdecl gc_clearignored(WPARAM wParam, LPARAM lParam); - - /* Popups functions */ - void initpopups(); - void showpopup(const TCHAR* nickname, const TCHAR* msg, int flags); - - /* Sessions functions */ - INT_PTR __cdecl sessions_view(WPARAM wParam, LPARAM lParam); - void sessions_updatedlg(); - BOOL sessions_closedlg(); - void sessions_menus_init(HGENMENU hRoot); - - /* Event helpers */ - void createObjService(const char* szService, GGServiceFunc serviceProc); - void createProtoService(const char* szService, GGServiceFunc serviceProc); - HANDLE hookProtoEvent(const char*, GGEventFunc); - void forkThread(GGThreadFunc, void* ); - HANDLE forkThreadEx(GGThreadFunc, void*, UINT* threadID = NULL); - - // Debug functions - int netlog(const char *fmt, ...); - - void block_init(); - void block_uninit(); - - ////////////////////////////////////////////////////////////////////////////////////// - - 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, is_list_remove, 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 hEvent; - HANDLE hConnStopEvent; - SOCKET sock; - UINT_PTR timer; - struct - { - char *online; - char *away; - char *dnd; - char *freechat; - char *invisible; - char *offline; - } modemsg; - HANDLE netlib; - HGENMENU hMenuRoot; - HGENMENU hMainMenu[7]; - HANDLE hPrebuildMenuHook; - HANDLE hBlockMenuItem; - HANDLE hImageMenuItem; - HANDLE hInstanceMenuItem; - HANDLE hAvatarsFolder; - HANDLE hImagesFolder; - HWND hwndSessionsDlg; -}; - -typedef struct -{ - int mode; - uin_t uin; - char *pass; - char *email; - GGPROTO *gg; -} GGUSERUTILDLGDATA; - -#endif diff --git a/protocols/Gadu-Gadu/groupchat.cpp b/protocols/Gadu-Gadu/groupchat.cpp deleted file mode 100644 index 4089c8fac3..0000000000 --- a/protocols/Gadu-Gadu/groupchat.cpp +++ /dev/null @@ -1,669 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 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. -//////////////////////////////////////////////////////////////////////////////// - -#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" - -//////////////////////////////////////////////////////////////////////////////// -// Inits Gadu-Gadu groupchat module using chat.dll - -int GGPROTO::gc_init() -{ - 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 = m_tszUserName; - gcr.pszModule = m_szModuleName; - CallServiceSync(MS_GC_REGISTER, 0, (LPARAM)&gcr); - hookProtoEvent(ME_GC_EVENT, &GGPROTO::gc_event); - gc_enabled = TRUE; - // create & hook event - mir_snprintf(service, 64, GG_GC_GETCHAT, m_szModuleName); - netlog("gg_gc_init(): Registered with groupchat plugin."); - } - else - netlog("gg_gc_init(): Cannot register with groupchat plugin !!!"); - - return 1; -} - -//////////////////////////////////////////////////////////////////////////////// -// Groupchat menus initialization - -void GGPROTO::gc_menus_init(HGENMENU hRoot) -{ - if (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, m_szModuleName); - createObjService(service, &GGPROTO::gc_openconf); - mi.position = 2000050001; - mi.icolibItem = GetIconHandle(IDI_CONFERENCE); - mi.pszName = LPGEN("Open &conference..."); - mi.pszService = service; - hMainMenu[0] = Menu_AddProtoMenuItem(&mi); - - // Clear ignored conferences - mir_snprintf(service, sizeof(service), GGS_CLEAR_IGNORED, m_szModuleName); - createObjService(service, &GGPROTO::gc_clearignored); - mi.position = 2000050002; - mi.icolibItem = GetIconHandle(IDI_CLEAR_CONFERENCE); - mi.pszName = LPGEN("&Clear ignored conferences"); - mi.pszService = service; - hMainMenu[1] = Menu_AddProtoMenuItem(&mi); - } -} - -//////////////////////////////////////////////////////////////////////////////// -// Releases Gadu-Gadu groupchat module using chat.dll - -int GGPROTO::gc_destroy() -{ - list_t l; - for(l = chats; l; l = l->next) - { - GGGC *chat = (GGGC *)l->data; - if (chat->recipients) free(chat->recipients); - } - list_destroy(chats, 1); chats = NULL; - return 1; -} - -GGGC* GGPROTO::gc_lookup(char *id) -{ - GGGC *chat; - list_t l; - - for(l = chats; l; l = l->next) - { - chat = (GGGC *)l->data; - if (chat && !strcmp(chat->id, id)) - return chat; - } - - return NULL; -} - -int GGPROTO::gc_event(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 - || lstrcmpiA(gch->pDest->pszModule, m_szModuleName) - || !(uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0)) - || !(chat = gc_lookup(gch->pDest->pszID))) - return 0; - - // Window terminated - if (gch->pDest->iType == SESSION_TERMINATE) - { - HANDLE hContact = NULL; - netlog("gg_gc_event(): Terminating chat %x, id %s from chat window...", chat, gch->pDest->pszID); - // Destroy chat entry - free(chat->recipients); - list_remove(&chats, chat, 1); - // Remove contact from contact list (duh!) should be done by chat.dll !! - hContact = db_find_first(); - while (hContact) - { - DBVARIANT dbv; - if (!db_get_s(hContact, m_szModuleName, "ChatRoomID", &dbv, DBVT_ASCIIZ)) - { - if (dbv.pszVal && !strcmp(gch->pDest->pszID, dbv.pszVal)) - CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0); - DBFreeVariant(&dbv); - } - hContact = db_find_next(hContact); - } - return 1; - } - - // Message typed / send only if online - if (isonline() && (gch->pDest->iType == GC_USER_MESSAGE) && gch->pszText) - { - char id[32]; - DBVARIANT dbv; - GCDEST gcdest = {m_szModuleName, gch->pDest->pszID, GC_EVENT_MESSAGE}; - GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; - int lc; - - UIN2ID(uin, id); - - gcevent.pszUID = id; - gcevent.pszText = gch->pszText; - if (!db_get_s(NULL, m_szModuleName, GG_KEY_NICK, &dbv, DBVT_ASCIIZ)) - 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; - netlog("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(&sess_mutex); - gg_send_message_confer(sess, GG_CLASS_CHAT, chat->recipients_count, chat->recipients, (BYTE*)gch->pszText); - LeaveCriticalSection(&sess_mutex); - return 1; - } - - // Privmessage selected - if (gch->pDest->iType == GC_USER_PRIVMESS) - { - HANDLE hContact = NULL; - if ((uin = atoi(gch->pszUID)) && (hContact = getcontact(uin, 1, 0, NULL))) - CallService(MS_MSG_SENDMESSAGE, (WPARAM)hContact, (LPARAM)0); - } - netlog("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* GGPROTO::gc_getchat(uin_t sender, uin_t *recipients, int recipients_count) -{ - list_t l; int i; - GGGC *chat; - char id[32]; - uin_t uin; DBVARIANT dbv; - GCDEST gcdest = {m_szModuleName, 0, GC_EVENT_ADDGROUP}; - GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; - - netlog("gg_gc_getchat(): Count %d.", recipients_count); - if (!recipients) return NULL; - - // Look for existing chat - for(l = 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) - netlog("gg_gc_getchat(): Ignoring existing id %s, size %d.", chat->id, chat->recipients_count); - else - netlog("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(gc_id ++, chat->id); chat->ignore = FALSE; - - // Check groupchat policy (new) / only for incoming - if (sender) - { - int unknown = (getcontact(sender, 0, 0, NULL) == NULL), - unknownSender = unknown; - for(i = 0; i < recipients_count; i++) - if (!getcontact(recipients[i], 0, 0, NULL)) - unknown ++; - if ((db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_DEFAULT, GG_KEYDEF_GC_POLICY_DEFAULT) == 2) || - (db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_TOTAL, GG_KEYDEF_GC_POLICY_TOTAL) == 2 && - recipients_count >= db_get_w(NULL, m_szModuleName, GG_KEY_GC_COUNT_TOTAL, GG_KEYDEF_GC_COUNT_TOTAL)) || - (db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_UNKNOWN, GG_KEYDEF_GC_POLICY_UNKNOWN) == 2 && - unknown >= db_get_w(NULL, m_szModuleName, GG_KEY_GC_COUNT_UNKNOWN, GG_KEYDEF_GC_COUNT_UNKNOWN))) - chat->ignore = TRUE; - if (!chat->ignore && ((db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_DEFAULT, GG_KEYDEF_GC_POLICY_DEFAULT) == 1) || - (db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_TOTAL, GG_KEYDEF_GC_POLICY_TOTAL) == 1 && - recipients_count >= db_get_w(NULL, m_szModuleName, GG_KEY_GC_COUNT_TOTAL, GG_KEYDEF_GC_COUNT_TOTAL)) || - (db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_UNKNOWN, GG_KEYDEF_GC_POLICY_UNKNOWN) == 1 && - unknown >= db_get_w(NULL, m_szModuleName, GG_KEY_GC_COUNT_UNKNOWN, GG_KEYDEF_GC_COUNT_UNKNOWN)))) - { - TCHAR *senderName = unknownSender ? - TranslateT("Unknown") : pcli->pfnGetContactDisplayName(getcontact(sender, 0, 0, NULL), 0); - TCHAR error[256]; - mir_sntprintf(error, SIZEOF(error), TranslateT("%s has initiated conference with %d participants (%d unknowns).\nDo you want do participate ?"), - senderName, recipients_count + 1, unknown); - chat->ignore = MessageBox(NULL, error, m_tszUserName, 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; - netlog("gg_gc_getchat(): Ignoring new chat %s, count %d.", chat->id, chat->recipients_count); - list_add(&chats, chat, 0); - return NULL; - } - } - - // Create new chat window - TCHAR status[256]; - TCHAR *senderName = sender ? pcli->pfnGetContactDisplayName(getcontact(sender, 1, 0, NULL), 0) : NULL; - mir_sntprintf(status, 255, (sender) ? TranslateT("%s initiated the conference.") : TranslateT("This is my own conference."), senderName); - GCSESSION gcwindow = { 0 }; - gcwindow.cbSize = sizeof(GCSESSION); - gcwindow.iType = GCW_CHATROOM; - gcwindow.pszModule = m_szModuleName; - gcwindow.ptszName = sender ? senderName : TranslateT("Conference"); - gcwindow.pszID = chat->id; - gcwindow.dwFlags = GC_TCHAR; - gcwindow.dwItemData = (DWORD)chat; - gcwindow.ptszStatusbarText = status; - - // Here we put nice new hash sign - TCHAR *name = (TCHAR*)calloc(_tcslen(gcwindow.ptszName) + 2, sizeof(TCHAR)); - *name = '#'; _tcscpy(name + 1, gcwindow.ptszName); - gcwindow.ptszName = name; - // Create new room - if (CallServiceSync(MS_GC_NEWSESSION, 0, (LPARAM) &gcwindow)) - { - netlog("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 = GC_TCHAR | GCEF_ADDTOLOG; - gcevent.time = 0; - - // Add normal group - gcevent.ptszStatus = TranslateT("Participants"); - CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); - gcdest.iType = GC_EVENT_JOIN; - - // Add myself - if (uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0)) - { - UIN2ID(uin, id); - if (!db_get_s(NULL, m_szModuleName, GG_KEY_NICK, &dbv, DBVT_TCHAR)) { - gcevent.ptszNick = NEWTSTR_ALLOCA(dbv.ptszVal); - db_free(&dbv); - } - else gcevent.ptszNick = TranslateT("Me"); - gcevent.bIsMe = 1; - CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); - netlog("gg_gc_getchat(): Myself %s: %S (%S) to the list...", gcevent.pszUID, gcevent.ptszNick, gcevent.ptszStatus); - } - else netlog("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 = getcontact(chat->recipients[i], 1, 0, NULL); - UIN2ID(chat->recipients[i], id); - if (hContact && (name = pcli->pfnGetContactDisplayName(hContact, 0)) != NULL) - gcevent.ptszNick = name; - else - gcevent.ptszNick = TranslateT("'Unknown'"); - gcevent.bIsMe = 0; - gcevent.dwFlags = GC_TCHAR; - netlog("gg_gc_getchat(): Added %s: %S (%S) to the list...", gcevent.pszUID, gcevent.ptszNick, 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); - - netlog("gg_gc_getchat(): Returning new chat window %s, count %d.", chat->id, chat->recipients_count); - list_add(&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 && !lstrcmpA(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 && !lstrcmpA(szProto, gg->m_szModuleName)) - 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 = db_find_first(); - while (hContact) - { - hItem = (HANDLE)SendMessage(hwndList, CLM_FINDCONTACT, (WPARAM)hContact, 0); - if (hItem && SendMessage(hwndList, CLM_GETCHECKMARK, (WPARAM)hItem, 0)) - count++; - hContact = db_find_next(hContact); - } - 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()) - { - MessageBox(NULL, - TranslateT("You have to be connected to open new conference."), - gg->m_tszUserName, MB_OK | MB_ICONSTOP); - } - else if (hwndList && (count = gg_gc_countcheckmarks(hwndList)) >= 2) - { - // Create new participiants table - char* chat; - uin_t* participants = (uin_t*)calloc(count, sizeof(uin_t)); - HANDLE hItem, hContact = db_find_first(); - gg->netlog("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++] = db_get_dw(hMetaContact ? hMetaContact : hContact, gg->m_szModuleName, GG_KEY_UIN, 0); - } - hContact = db_find_next(hContact); - } - if (count > i) i = count; - chat = gg->gc_getchat(0, participants, count); - if (chat) - { - GCDEST gcdest = {gg->m_szModuleName, 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 = db_find_first(); - 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->m_szModuleName; - uin = (uin_t)db_get_dw(hMetaContact, gg->m_szModuleName, GG_KEY_UIN, 0); - } - else - { - szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); - uin = (uin_t)db_get_dw(hContact, gg->m_szModuleName, GG_KEY_UIN, 0); - } - - if (szProto == NULL || lstrcmpA(szProto, gg->m_szModuleName) || !uin || uin == db_get_dw(NULL, gg->m_szModuleName, GG_KEY_UIN, 0)) - SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_DELETEITEM, (WPARAM)hItem, 0); - } - hContact = db_find_next(hContact); - } - } - 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 GGPROTO::gc_clearignored(WPARAM wParam, LPARAM lParam) -{ - list_t l = 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(&chats, chat, 1); - cleared = TRUE; - } - } - MessageBox( NULL, - cleared ? - TranslateT("All ignored conferences are now unignored and the conference policy will act again.") : - TranslateT("There are no ignored conferences."), - m_tszUserName, MB_OK | MB_ICONINFORMATION - ); - - return 0; -} - -INT_PTR GGPROTO::gc_openconf(WPARAM wParam, LPARAM lParam) -{ - // Check if connected - if (!isonline()) - { - MessageBox(NULL, - TranslateT("You have to be connected to open new conference."), - m_tszUserName, MB_OK | MB_ICONSTOP - ); - return 0; - } - - CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_CONFERENCE), NULL, gg_gc_openconfdlg, (LPARAM)this); - return 1; -} - -int GGPROTO::gc_changenick(HANDLE hContact, char *pszNick) -{ - list_t l; - uin_t uin = db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0); - if (!uin || !pszNick) return 0; - - netlog("gg_gc_changenick(): Nickname for uin %d changed to %s.", uin, pszNick); - // Lookup for chats having this nick - for(l = chats; l; l = l->next) { - GGGC *chat = (GGGC *)l->data; - if (chat->recipients && chat->recipients_count) - for(int 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 = m_szModuleName; - gce.pDest = &gcd; - gcd.pszID = chat->id; - gce.pszUID = id; - gce.pszText = pszNick; - netlog("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.cpp b/protocols/Gadu-Gadu/icolib.cpp deleted file mode 100644 index f99540ac76..0000000000 --- a/protocols/Gadu-Gadu/icolib.cpp +++ /dev/null @@ -1,109 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// 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. -//////////////////////////////////////////////////////////////////////////////// - -#include "gg.h" - -struct tagiconList -{ - char* szDescr; - 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() -{ - TCHAR szFile[MAX_PATH]; - GetModuleFileName(hInstance, szFile, MAX_PATH); - - char szSectionName[100]; - mir_snprintf(szSectionName, sizeof( szSectionName ), "%s/%s", LPGEN("Protocols"), LPGEN(GGDEF_PROTO)); - - SKINICONDESC sid = {0}; - sid.cbSize = sizeof(SKINICONDESC); - sid.ptszDefaultFile = szFile; - sid.pszSection = szSectionName; - sid.flags = SIDF_PATH_TCHAR; - - for (int 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] = Skin_AddIcon(&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 deleted file mode 100644 index 6f59309853..0000000000 Binary files a/protocols/Gadu-Gadu/icons/block.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/icons/clear_ignored_conference.ico b/protocols/Gadu-Gadu/icons/clear_ignored_conference.ico deleted file mode 100644 index f883585899..0000000000 Binary files a/protocols/Gadu-Gadu/icons/clear_ignored_conference.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/icons/conference.ico b/protocols/Gadu-Gadu/icons/conference.ico deleted file mode 100644 index 3e4cfcc606..0000000000 Binary files a/protocols/Gadu-Gadu/icons/conference.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/icons/delete.ico b/protocols/Gadu-Gadu/icons/delete.ico deleted file mode 100644 index 9108e38cfd..0000000000 Binary files a/protocols/Gadu-Gadu/icons/delete.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/icons/export_list_to_server.ico b/protocols/Gadu-Gadu/icons/export_list_to_server.ico deleted file mode 100644 index 8c0f660788..0000000000 Binary files a/protocols/Gadu-Gadu/icons/export_list_to_server.ico and /dev/null 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 deleted file mode 100644 index fc239f9a02..0000000000 Binary files a/protocols/Gadu-Gadu/icons/export_list_to_txt_file.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/icons/gg.ico b/protocols/Gadu-Gadu/icons/gg.ico deleted file mode 100644 index a49986a12d..0000000000 Binary files a/protocols/Gadu-Gadu/icons/gg.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/icons/image.ico b/protocols/Gadu-Gadu/icons/image.ico deleted file mode 100644 index 785cd8ba9e..0000000000 Binary files a/protocols/Gadu-Gadu/icons/image.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/icons/import_list_from_server.ico b/protocols/Gadu-Gadu/icons/import_list_from_server.ico deleted file mode 100644 index 21ecf6159d..0000000000 Binary files a/protocols/Gadu-Gadu/icons/import_list_from_server.ico and /dev/null 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 deleted file mode 100644 index 2acc738fc7..0000000000 Binary files a/protocols/Gadu-Gadu/icons/import_list_from_txt_file.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/icons/list.ico b/protocols/Gadu-Gadu/icons/list.ico deleted file mode 100644 index 86139d5e23..0000000000 Binary files a/protocols/Gadu-Gadu/icons/list.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/icons/next.ico b/protocols/Gadu-Gadu/icons/next.ico deleted file mode 100644 index 8ddc389b85..0000000000 Binary files a/protocols/Gadu-Gadu/icons/next.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/icons/previous.ico b/protocols/Gadu-Gadu/icons/previous.ico deleted file mode 100644 index 8e6dc1ef08..0000000000 Binary files a/protocols/Gadu-Gadu/icons/previous.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/icons/remove_list_from_server.ico b/protocols/Gadu-Gadu/icons/remove_list_from_server.ico deleted file mode 100644 index 44fe7a833a..0000000000 Binary files a/protocols/Gadu-Gadu/icons/remove_list_from_server.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/icons/save.ico b/protocols/Gadu-Gadu/icons/save.ico deleted file mode 100644 index a8251f70e4..0000000000 Binary files a/protocols/Gadu-Gadu/icons/save.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/icons/sessions.ico b/protocols/Gadu-Gadu/icons/sessions.ico deleted file mode 100644 index 9f9383e22f..0000000000 Binary files a/protocols/Gadu-Gadu/icons/sessions.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/icons/settings.ico b/protocols/Gadu-Gadu/icons/settings.ico deleted file mode 100644 index 6706ec2f6c..0000000000 Binary files a/protocols/Gadu-Gadu/icons/settings.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/image.cpp b/protocols/Gadu-Gadu/image.cpp deleted file mode 100644 index 0ad225ad06..0000000000 --- a/protocols/Gadu-Gadu/image.cpp +++ /dev/null @@ -1,1191 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2009 Adam Strzelecki -// 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 - -#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; - TCHAR *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); - -//////////////////////////////////////////////////////////////////////////// -// Image Module : Adding item to contact menu, creating sync objects - -int GGPROTO::img_init() -{ - 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, m_szModuleName); - createObjService(service, &GGPROTO::img_sendimg); - mi.position = -2000010000; - mi.icolibItem = GetIconHandle(IDI_IMAGE); - mi.pszName = LPGEN("&Image"); - mi.pszService = service; - mi.pszContactOwner = m_szModuleName; - hImageMenuItem = Menu_AddContactMenuItem(&mi); - - // Receive image - mir_snprintf(service, sizeof(service), GGS_RECVIMAGE, m_szModuleName); - createObjService(service, &GGPROTO::img_recvimage); - - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////// -// Image Module : closing dialogs, sync objects -int GGPROTO::img_shutdown() -{ - list_t l; -#ifdef DEBUGMODE - netlog("gg_img_shutdown(): Closing all dialogs..."); -#endif - // Rather destroy window instead of just removing structures - for (l = 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)) - netlog("gg_img_shutdown(): Image dlg %x cannot be released !!", dat->hWnd); - } - else - netlog("gg_img_shutdown(): Image dlg %x not exists, but structure does !!", dat->hWnd); - } - } - - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////// -// Image Module : destroying list - -int GGPROTO::img_destroy() -{ - // Release all dialogs - while (imagedlgs && gg_img_remove((GGIMAGEDLGDATA *)imagedlgs->data)); - - // Destroy list - list_destroy(imagedlgs, 1); - CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)hImageMenuItem, (LPARAM) 0); - - return FALSE; -} - -//////////////////////////////////////////////////////////////////////////// -// Image Window : Frees image entry structure - -static int 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; -} - -//////////////////////////////////////////////////////////////////////////// -// 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 - -TCHAR *gg_img_getfilter(TCHAR *szFilter, int nSize) -{ - TCHAR *szFilterName, *szFilterMask; - TCHAR *pFilter = szFilter; - - // Match relative to ImgDecoder presence - szFilterName = TranslateT("Image files (*.bmp,*.gif,*.jpeg,*.jpg,*.png)"); - szFilterMask = _T("*.bmp;*.gif;*.jpeg;*.jpg;*.png"); - - // Make up filter - _tcsncpy(pFilter, szFilterName, nSize); - pFilter += _tcslen(pFilter) + 1; - if (pFilter >= szFilter + nSize) return NULL; - _tcsncpy(pFilter, szFilterMask, nSize - (pFilter - szFilter)); - pFilter += _tcslen(pFilter) + 1; - if (pFilter >= szFilter + nSize) return NULL; - *pFilter = 0; - - return szFilter; -} - -//////////////////////////////////////////////////////////////////////////////// -// Save specified image entry - -int gg_img_saveimage(HWND hwnd, GGIMAGEENTRY *dat) -{ - if (!dat) - return FALSE; - - GGPROTO* gg = ((GGIMAGEDLGDATA *)GetWindowLongPtr(hwnd, GWLP_USERDATA))->gg; - - TCHAR szFilter[128]; - gg_img_getfilter(szFilter, SIZEOF(szFilter)); - - TCHAR szFileName[MAX_PATH]; - _tcsncpy(szFileName, dat->lpszFileName, SIZEOF(szFileName)); - - OPENFILENAME ofn = {0}; - 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 = _tfopen(szFileName, _T("w+b")); - if (fp) - { - fwrite(dat->lpData, dat->nSize, 1, fp); - fclose(fp); - gg->netlog("gg_img_saveimage(): Image saved to %s.", szFileName); - } - else - { - gg->netlog("gg_img_saveimage(): Cannot save image to %s.", szFileName); - MessageBox(hwnd, TranslateT("Image cannot be written to disk."), gg->m_tszUserName, 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: - { - 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 dat->gg->netlog("gg_img_dlgproc(): Creation event not found, but someone might be waiting."); - - // Making buttons flat - SendDlgItemMessage(hwndDlg, IDC_IMG_PREV, BUTTONSETASFLATBTN, TRUE, 0); - SendDlgItemMessage(hwndDlg, IDC_IMG_NEXT, BUTTONSETASFLATBTN, TRUE, 0); - SendDlgItemMessage(hwndDlg, IDC_IMG_DELETE, BUTTONSETASFLATBTN, TRUE, 0); - SendDlgItemMessage(hwndDlg, IDC_IMG_SAVE, BUTTONSETASFLATBTN, TRUE, 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"); - - TCHAR *szName = pcli->pfnGetContactDisplayName(dat->hContact, 0), szTitle[128]; - if (dat->bReceiving) - mir_sntprintf(szTitle, SIZEOF(szTitle), TranslateT("Image from %s"), szName); - else - mir_sntprintf(szTitle, SIZEOF(szTitle), TranslateT("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) - { - dat->gg->netlog("gg_img_dlgproc(): Image was not found on the list. Cannot paint the window."); - return FALSE; - } - - if (dat->bReceiving) - { - TCHAR szTitle[128]; - mir_sntprintf(szTitle, SIZEOF(szTitle), _T("%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) - { - dat->gg->netlog("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) - { - dat->gg->netlog("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()) - { - uin_t uin = (uin_t)db_get_dw(dat->hContact, gg->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, (BYTE*)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: - { - TCHAR szFilter[128]; - TCHAR 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 = TranslateT("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 *)dat->gg->img_loadpicture(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 GGPROTO::img_dlgcallthread(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(&GGPROTO::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(TCHAR *szPath, GGIMAGEENTRY *dat) -{ - struct _stat st; - - if (_tstat(szPath, &st) != 0) - return 0; - - if (st.st_size == dat->nSize) - { - FILE *fp = _tfopen(szPath, _T("rb")); - if (!fp) return 0; - char *lpData = (char*)mir_alloc(dat->nSize); - if (fread(lpData, 1, dat->nSize, fp) == dat->nSize) - { - if (dat->crc32 == gg_fix32(gg_crc32(0, (BYTE*)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 -TCHAR *gg_img_hasextension(TCHAR *filename) -{ - if (filename != NULL && *filename != '\0') - { - TCHAR *imgtype = _tcsrchr(filename, '.'); - if (imgtype != NULL) - { - size_t len = _tcslen(imgtype); - imgtype++; - if (len == 4 && (_tcsicmp(imgtype, _T("bmp")) == 0 || - _tcsicmp(imgtype, _T("gif")) == 0 || - _tcsicmp(imgtype, _T("jpg")) == 0 || - _tcsicmp(imgtype, _T("png")) == 0)) - return --imgtype; - if (len == 5 && _tcsicmp(imgtype, _T("jpeg")) == 0) - return --imgtype; - } - } - return NULL; -} - -//////////////////////////////////////////////////////////////////////////////// -// Display received image using message with [img] BBCode - -int GGPROTO::img_displayasmsg(HANDLE hContact, void *img) -{ - GGIMAGEENTRY *dat = (GGIMAGEENTRY *)img; - TCHAR szPath[MAX_PATH], path[MAX_PATH], *pImgext, imgext[6]; - size_t tPathLen; - int i, res; - - if (hImagesFolder == NULL || FoldersGetCustomPathT(hImagesFolder, path, MAX_PATH, _T(""))) { - TCHAR *tmpPath = Utils_ReplaceVarsT( _T("%miranda_userdata%")); - tPathLen = mir_sntprintf(szPath, MAX_PATH, _T("%s\\%s\\ImageCache"), tmpPath, m_tszUserName); - mir_free(tmpPath); - } - else { - _tcscpy(szPath, path); - tPathLen = _tcslen(szPath); - } - - if ( _taccess(szPath, 0)) - CallService(MS_UTILS_CREATEDIRTREET, 0, (LPARAM)szPath); - - mir_sntprintf(szPath + tPathLen, MAX_PATH - tPathLen, _T("\\%s"), dat->lpszFileName); - if ((pImgext = gg_img_hasextension(szPath)) == NULL) - pImgext = szPath + _tcslen(szPath); - mir_sntprintf(imgext, SIZEOF(imgext), _T("%s"), pImgext); - for (i = 1; ; ++i) - { - if ((res = gg_img_isexists(szPath, dat)) != -1) break; - mir_sntprintf(szPath, MAX_PATH, _T("%.*s (%u)%s"), pImgext - szPath, szPath, i, imgext); - } - - if (res == 0) { - // Image file not found, thus create it - FILE *fp = _tfopen(szPath, _T("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)⪯ - 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); - netlog("gg_img_displayasmsg: Image saved to %s.", szPath); - } - else - { - netlog("gg_img_displayasmsg: Cannot save image to %s.", szPath); - } - - return 0; -} - -//////////////////////////////////////////////////////////////////////////// -// Return if uin has it's window already opened - -BOOL GGPROTO::img_opened(uin_t uin) -{ - list_t l = 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 - -int GGPROTO::img_display(HANDLE hContact, void *img) -{ - list_t l = imagedlgs; - GGIMAGEDLGDATA *dat; - - if (!img) return FALSE; - - // Look for already open dialog - EnterCriticalSection(&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(this, hContact); - dat->uin = db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0); - - while (WaitForSingleObjectEx(dat->hEvent, INFINITE, TRUE) != WAIT_OBJECT_0); - CloseHandle(dat->hEvent); - dat->hEvent = NULL; - - list_add(&imagedlgs, dat, 0); - } - LeaveCriticalSection(&img_mutex); - - SendMessage(dat->hWnd, WM_ADDIMAGE, 0, (LPARAM)img); - if (/*db_get_b(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; -} - -//////////////////////////////////////////////////////////////////////////// -// Helper function to determine image file format and the right extension - -const TCHAR *gg_img_guessfileextension(const char *lpData) -{ - if (lpData != NULL) - { - if (memcmp(lpData, "BM", 2) == 0) - return _T(".bmp"); - if (memcmp(lpData, "GIF8", 4) == 0) - return _T(".gif"); - if (memcmp(lpData, "\xFF\xD8", 2) == 0) - return _T(".jpg"); - if (memcmp(lpData, "\x89PNG", 4) == 0) - return _T(".png"); - } - return _T(""); -} - -//////////////////////////////////////////////////////////////////////////// -// Image Window : Loading picture and sending for display - -void* GGPROTO::img_loadpicture(gg_event* e, TCHAR *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 = _tfopen(szFileName, _T("rb")); - if (!fp) { - free(dat); - netlog("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); - netlog("gg_img_loadpicture(): Zero file size \"%s\" failed.", szFileName); - return NULL; - } - // Maximum acceptable image size - if (dat->nSize > 255 * 1024) - { - fclose(fp); - free(dat); - netlog("gg_img_loadpicture(): Image size of \"%s\" exceeds 255 KB.", szFileName); - MessageBox(NULL, TranslateT("Image exceeds maximum allowed size of 255 KB."), m_tszUserName, MB_OK | MB_ICONEXCLAMATION); - return NULL; - } - fseek(fp, 0, SEEK_SET); - dat->lpData = (char*)malloc(dat->nSize); - if (fread(dat->lpData, 1, dat->nSize, fp) < dat->nSize) - { - free(dat->lpData); - fclose(fp); - free(dat); - netlog("gg_img_loadpicture(): Reading file \"%s\" failed.", szFileName); - return NULL; - } - fclose(fp); - dat->lpszFileName = _tcsdup(szFileName); - } - // Copy picture from packet - else if (e && e->event.image_reply.filename) - { - dat->nSize = e->event.image_reply.size; - dat->lpData = (char*)malloc(dat->nSize); - memcpy(dat->lpData, e->event.image_reply.image, dat->nSize); - - mir_ptr tmpFileName( mir_a2t(e->event.image_reply.filename)); - if (!gg_img_hasextension(tmpFileName)) { - // Add missing file extension - const TCHAR *szImgType = gg_img_guessfileextension(dat->lpData); - if (*szImgType) { - dat->lpszFileName = (TCHAR*)calloc(sizeof(TCHAR), lstrlen(tmpFileName) + lstrlen(szImgType) + 1); - if (dat->lpszFileName != NULL) { - _tcscpy(dat->lpszFileName, tmpFileName); - _tcscat(dat->lpszFileName, szImgType); - } - } - } - - if (dat->lpszFileName == NULL) - dat->lpszFileName = _tcsdup( _A2T( 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; - - netlog("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 GGPROTO::img_recvimage(WPARAM wParam, LPARAM lParam) -{ - CLISTEVENT *cle = (CLISTEVENT *)lParam; - GGIMAGEENTRY *img = (GGIMAGEENTRY *)cle->lParam; - - netlog("gg_img_recvimage(%x, %x): Popup new image.", wParam, lParam); - if (!img) return FALSE; - - img_display(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 = db_get_dw(dat->hContact, dat->gg->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_img_find(): Image not found on the list. It might be released before calling this function."); - return NULL; -} - - -//////////////////////////////////////////////////////////////////////////// -// Image Module : Send on Request - -BOOL GGPROTO::img_sendonrequest(gg_event* e) -{ - GGIMAGEDLGDATA *dat = gg_img_find(this, e->event.image_request.sender, e->event.image_request.crc32); - if (!this || !dat || !isonline()) - return FALSE; - - EnterCriticalSection(&sess_mutex); - gg_image_reply(sess, e->event.image_request.sender, dat->lpImages->lpszFileName, dat->lpImages->lpData, dat->lpImages->nSize); - LeaveCriticalSection(&sess_mutex); - - gg_img_remove(dat); - - return TRUE; -} - -//////////////////////////////////////////////////////////////////////////// -// Send Image : Run (Thread and main) - -INT_PTR GGPROTO::img_sendimg(WPARAM wParam, LPARAM lParam) -{ - HANDLE hContact = (HANDLE)wParam; - GGIMAGEDLGDATA *dat = NULL; - - EnterCriticalSection(&img_mutex); - if (!dat) - { - dat = (GGIMAGEDLGDATA *)calloc(1, sizeof(GGIMAGEDLGDATA)); - dat->hContact = hContact; - dat->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); - dat->gg = this; - ResetEvent(dat->hEvent); - - // Create new dialog - forkthread(&GGPROTO::img_dlgcallthread, dat); - - while (WaitForSingleObjectEx(dat->hEvent, INFINITE, TRUE) != WAIT_OBJECT_0); - CloseHandle(dat->hEvent); - dat->hEvent = NULL; - - list_add(&imagedlgs, dat, 0); - } - - // Request choose dialog - SendMessage(dat->hWnd, WM_CHOOSEIMG, 0, 0); - LeaveCriticalSection(&img_mutex); - - return 0; -} diff --git a/protocols/Gadu-Gadu/import.cpp b/protocols/Gadu-Gadu/import.cpp deleted file mode 100644 index e4ff066d11..0000000000 --- a/protocols/Gadu-Gadu/import.cpp +++ /dev/null @@ -1,651 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 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. -//////////////////////////////////////////////////////////////////////////////// - -#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 (db_get_s(NULL, "CListGroups", idstr, &dbv, DBVT_ASCIIZ)) 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)) - { - lstrcpynA(groupName2 + 1, groupName, (int)strlen(groupName) + 1); - - // Find an unused id - for (groupId = 0; ; groupId++) { - _itoa(groupId, groupIdStr,10); - if (db_get_s(NULL, "CListGroups", groupIdStr, &dbv, DBVT_ASCIIZ)) - break; - DBFreeVariant(&dbv); - } - - groupName2[0] = 1|GROUPF_EXPANDED; // 1 is required so we never get '\0' - db_set_s(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 = db_find_first(); - while (hContact) - { - char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); - if (szProto != NULL && !strcmp(szProto, gg->m_szModuleName) && !db_get_b(hContact, gg->m_szModuleName, "ChatRoom", 0)) - { - DBVARIANT dbv; - - // Readup FirstName - if (!db_get_s(hContact, gg->m_szModuleName, "FirstName", &dbv, DBVT_ASCIIZ)) - { - string_append(s, dbv.pszVal); - DBFreeVariant(&dbv); - } - string_append_c(s, ';'); - // Readup LastName - if (!db_get_s(hContact, gg->m_szModuleName, "LastName", &dbv, DBVT_ASCIIZ)) - { - string_append(s, dbv.pszVal); - DBFreeVariant(&dbv); - } - string_append_c(s, ';'); - - // Readup Nick - if (!db_get_s(hContact, "CList", "MyHandle", &dbv, DBVT_ASCIIZ) || !db_get_s(hContact, gg->m_szModuleName, GG_KEY_NICK, &dbv, DBVT_ASCIIZ)) - { - DBVARIANT dbv2; - if (!db_get_s(hContact, gg->m_szModuleName, "NickName", &dbv2, DBVT_ASCIIZ)) - { - 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 (!db_get_s(hContact, "UserInfo", "MyPhone0", &dbv, DBVT_ASCIIZ)) - { - // 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 (!db_get_s(hContact, "CList", "Group", &dbv, DBVT_ASCIIZ)) - { - string_append(s, dbv.pszVal); - DBFreeVariant(&dbv); - } - string_append_c(s, ';'); - // Readup Uin - string_append(s, ditoa(db_get_dw(hContact, gg->m_szModuleName, GG_KEY_UIN, 0))); - string_append_c(s, ';'); - // Readup Mail (fixed: uses stored editable mails) - if (!db_get_s(hContact, "UserInfo", "Mye-mail0", &dbv, DBVT_ASCIIZ)) - { - string_append(s, dbv.pszVal); - DBFreeVariant(&dbv); - } - if (cr) - string_append(s, ";0;;0;\r\n"); - else - string_append(s, ";0;;0;\n"); - } - hContact = db_find_next(hContact); - } - - contacts = string_free(s, 0); - -#ifdef DEBUGMODE - gg->netlog("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 GGPROTO::parsecontacts(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 = (char*)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 = getcontact(uin, 1, 1, _A2T(strNick)); -#ifdef DEBUGMODE - netlog("gg_parsecontacts(): Found contact %d with nickname \"%s\".", uin, strNick); -#endif - // Write group - if (hContact && strGroup) - db_set_s(hContact, "CList", "Group", CreateGroup(strGroup)); - - // Write misc data - if (hContact && strFirstName) db_set_s(hContact, m_szModuleName, "FirstName", strFirstName); - if (hContact && strLastName) db_set_s(hContact, m_szModuleName, "LastName", strLastName); - if (hContact && strPhone) db_set_s(hContact, "UserInfo", "MyPhone0", strPhone); // Store now in User Info - if (hContact && strMail) db_set_s(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 - -INT_PTR GGPROTO::import_server(WPARAM wParam, LPARAM lParam) -{ - char *password; - uin_t uin; - DBVARIANT dbv; - - // Check if connected - if (!isonline()) - { - MessageBox(NULL, - TranslateT("You have to be connected before you can import/export contacts from/to server."), - m_tszUserName, MB_OK | MB_ICONSTOP - ); - return 0; - } - - // Readup password - if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) - { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); - password = _strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - else return 0; - - if (!(uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0))) - return 0; - - // Making contacts list - EnterCriticalSection(&sess_mutex); - if (gg_userlist_request(sess, GG_USERLIST_GET, NULL) == -1) - { - TCHAR error[128]; - LeaveCriticalSection(&sess_mutex); - mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be imported because of error:\n\t%s"), _tcserror(errno)); - MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); - netlog("gg_import_server(): Cannot import list because of \"%s\".", strerror(errno)); - } - LeaveCriticalSection(&sess_mutex); - free(password); - - return 0; -} - -////////////////////////////////////////////////////////// -// remove from server - -INT_PTR GGPROTO::remove_server(WPARAM wParam, LPARAM lParam) -{ - char *password; - uin_t uin; - DBVARIANT dbv; - - // Check if connected - if (!isonline()) - { - MessageBox(NULL, - TranslateT("You have to be connected before you can import/export contacts from/to server."), - m_tszUserName, MB_OK | MB_ICONSTOP - ); - return 0; - } - - // Readup password - if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) - { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); - password = _strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - else return 0; - - if (!(uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0))) - return 0; - - // Making contacts list - EnterCriticalSection(&sess_mutex); - if (gg_userlist_request(sess, GG_USERLIST_PUT, NULL) == -1) - { - TCHAR error[128]; - LeaveCriticalSection(&sess_mutex); - mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be removeed because of error:\n\t%s"), strerror(errno)); - MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); - netlog("gg_remove_server(): Cannot remove list because of \"%s\".", strerror(errno)); - } - LeaveCriticalSection(&sess_mutex); - - // Set list removal - is_list_remove = TRUE; - free(password); - - return 0; -} - -INT_PTR GGPROTO::import_text(WPARAM wParam, LPARAM lParam) -{ - TCHAR str[MAX_PATH]; - TCHAR filter[512], *pfilter; - struct _stat st; - FILE *f; - - OPENFILENAME ofn = {0}; - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - _tcsncpy(filter, TranslateT("Text files"), SIZEOF(filter)); - _tcsncat(filter, _T(" (*.txt)"), SIZEOF(filter) - _tcslen(filter)); - pfilter = filter + _tcslen(filter) + 1; - if (pfilter >= filter + SIZEOF(filter)) - return 0; - - _tcsncpy(pfilter, _T("*.TXT"), SIZEOF(filter) - (pfilter - filter)); - pfilter = pfilter + _tcslen(pfilter) + 1; - if (pfilter >= filter + SIZEOF(filter)) - return 0; - _tcsncpy(pfilter, TranslateT("All Files"), SIZEOF(filter) - (pfilter - filter)); - _tcsncat(pfilter, _T(" (*)"), SIZEOF(filter) - (pfilter - filter) - _tcslen(pfilter)); - pfilter = pfilter + _tcslen(pfilter) + 1; - - if (pfilter >= filter + SIZEOF(filter)) - return 0; - - _tcsncpy(pfilter, _T("*"), SIZEOF(filter) - (pfilter - filter)); - pfilter = pfilter + _tcslen(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 = _T("txt"); - -#ifdef DEBUGMODE - netlog("gg_import_text()"); -#endif - if (!GetOpenFileName(&ofn)) return 0; - - f = _tfopen(str, _T("r")); - _tstat(str, &st); - - if (f && st.st_size) - { - char *contacts = (char*)malloc(st.st_size * sizeof(char)); - fread(contacts, sizeof(char), st.st_size, f); - fclose(f); - parsecontacts(contacts); - free(contacts); - - MessageBox(NULL, TranslateT("List import successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION); - } - else - { - TCHAR error[128]; - mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be imported from file \"%s\" because of error:\n\t%s"), str, _tcserror(errno)); - MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); - netlog("gg_import_text(): Cannot import list from file \"%S\" because of \"%s\".", str, strerror(errno)); - } - - return 0; -} - -INT_PTR GGPROTO::export_text(WPARAM wParam, LPARAM lParam) -{ - TCHAR str[MAX_PATH]; - OPENFILENAME ofn = {0}; - TCHAR filter[512], *pfilter; - FILE *f; - - _tcsncpy(str, TranslateT("contacts"), sizeof(str)); - _tcsncat(str, _T(".txt"), sizeof(str) - _tcslen(str)); - - ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; - _tcsncpy(filter, TranslateT("Text files"), SIZEOF(filter)); - _tcsncat(filter, _T(" (*.txt)"), SIZEOF(filter) - _tcslen(filter)); - pfilter = filter + _tcslen(filter) + 1; - if (pfilter >= filter + SIZEOF(filter)) - return 0; - _tcsncpy(pfilter, _T("*.TXT"), SIZEOF(filter) - (pfilter - filter)); - pfilter = pfilter + _tcslen(pfilter) + 1; - if (pfilter >= filter + SIZEOF(filter)) - return 0; - _tcsncpy(pfilter, TranslateT("All Files"), SIZEOF(filter) - (pfilter - filter)); - _tcsncat(pfilter, _T(" (*)"), SIZEOF(filter) - (pfilter - filter) - _tcslen(pfilter)); - pfilter = pfilter + _tcslen(pfilter) + 1; - if (pfilter >= filter + SIZEOF(filter)) - return 0; - _tcsncpy(pfilter, _T("*"), SIZEOF(filter) - (pfilter - filter)); - pfilter = pfilter + _tcslen(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 = _T("txt"); - -#ifdef DEBUGMODE - netlog("gg_export_text(%s).", str); -#endif - if (!GetSaveFileName(&ofn)) return 0; - - if (f = _tfopen(str, _T("w"))) { - char *contacts = gg_makecontacts(this, 0); - fwrite(contacts, sizeof(char), strlen(contacts), f); - fclose(f); - free(contacts); - - MessageBox(NULL, TranslateT("List export successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION); - } - else - { - TCHAR error[128]; - mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be exported to file \"%s\" because of error:\n\t%s"), str, _tcserror(errno)); - MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); - netlog("gg_import_text(): Cannot export list to file \"%s\" because of \"%s\".", str, strerror(errno)); - } - - return 0; -} - -////////////////////////////////////////////////////////// -// export to server - -INT_PTR GGPROTO::export_server(WPARAM wParam, LPARAM lParam) -{ - char *password, *contacts; - uin_t uin; - DBVARIANT dbv; - - // Check if connected - if (!isonline()) - { - MessageBox(NULL, - TranslateT("You have to be connected before you can import/export contacts from/to server."), - m_tszUserName, MB_OK | MB_ICONSTOP - ); - return 0; - } - - // Readup password - if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) - { - CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); - password = _strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - else return 0; - - if (!(uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0))) - return 0; - - // Making contacts list - contacts = gg_makecontacts(this, 1); - -#ifdef DEBUGMODE - netlog("gg_userlist_request(%s).", contacts); -#endif - - EnterCriticalSection(&sess_mutex); - if (gg_userlist_request(sess, GG_USERLIST_PUT, contacts) == -1) - { - TCHAR error[128]; - LeaveCriticalSection(&sess_mutex); - mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be exported because of error:\n\t%s"), _tcserror(errno)); - MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); - netlog("gg_export_server(): Cannot export list because of \"%s\".", strerror(errno)); - } - LeaveCriticalSection(&sess_mutex); - - // Set list removal - is_list_remove = FALSE; - free(contacts); - free(password); - - return 0; -} - -////////////////////////////////////////////////////////// -// Import menus and stuff - -void GGPROTO::import_init(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, m_szModuleName); - createObjService(service, &GGPROTO::import_server); - mi.position = 2000500001; - mi.icolibItem = GetIconHandle(IDI_IMPORT_SERVER); - mi.pszName = LPGEN("Import List From &Server"); - mi.pszService = service; - hMainMenu[2] = Menu_AddProtoMenuItem(&mi); - - // Import from textfile - mir_snprintf(service, sizeof(service), GGS_IMPORT_TEXT, m_szModuleName); - createObjService(service, &GGPROTO::import_text); - mi.position = 2000500002; - mi.icolibItem = GetIconHandle(IDI_IMPORT_TEXT); - mi.pszName = LPGEN("Import List From &Text File..."); - mi.pszService = service; - hMainMenu[3] = Menu_AddProtoMenuItem(&mi); - - // Remove from server - mir_snprintf(service, sizeof(service), GGS_REMOVE_SERVER, m_szModuleName); - createObjService(service, &GGPROTO::remove_server); - mi.position = 2000500003; - mi.icolibItem = GetIconHandle(IDI_REMOVE_SERVER); - mi.pszName = LPGEN("&Remove List From Server"); - mi.pszService = service; - hMainMenu[4] = Menu_AddProtoMenuItem(&mi); - - // Export to server - mir_snprintf(service, sizeof(service), GGS_EXPORT_SERVER, m_szModuleName); - createObjService(service, &GGPROTO::export_server); - mi.position = 2005000001; - mi.icolibItem = GetIconHandle(IDI_EXPORT_SERVER); - mi.pszName = LPGEN("Export List To &Server"); - mi.pszService = service; - hMainMenu[5] = Menu_AddProtoMenuItem(&mi); - - // Export to textfile - mir_snprintf(service, sizeof(service), GGS_EXPORT_TEXT, m_szModuleName); - createObjService(service, &GGPROTO::export_text); - mi.position = 2005000002; - mi.icolibItem = GetIconHandle(IDI_EXPORT_TEXT); - mi.pszName = LPGEN("Export List To &Text File..."); - mi.pszService = service; - hMainMenu[6] = Menu_AddProtoMenuItem(&mi); -} diff --git a/protocols/Gadu-Gadu/keepalive.cpp b/protocols/Gadu-Gadu/keepalive.cpp deleted file mode 100644 index 02b661c222..0000000000 --- a/protocols/Gadu-Gadu/keepalive.cpp +++ /dev/null @@ -1,92 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 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. -//////////////////////////////////////////////////////////////////////////////// - -#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()) - { - #ifdef DEBUGMODE - gg->netlog("Sending keep-alive"); - #endif - EnterCriticalSection(&gg->sess_mutex); - gg_ping(gg->sess); - LeaveCriticalSection(&gg->sess_mutex); - } - } -} - -void GGPROTO::keepalive_init() -{ - if (db_get_b(NULL, m_szModuleName, 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 - netlog("gg_keepalive_init(): Initializing Timer %d", i); - #endif - timer = SetTimer(NULL, 0, 1000 * 30, gg_keepalive); - g_timers[i] = this; - } - } -} - -void GGPROTO::keepalive_destroy() -{ -#ifdef DEBUGMODE - netlog("gg_destroykeepalive(): Killing Timer"); -#endif - if (timer) - { - int i; - KillTimer(NULL, timer); - for(i = 0; i < MAX_TIMERS; i++) - if (g_timers[i] == this) { - g_timers[i] = NULL; - break; - } - timer = 0; -#ifdef DEBUGMODE - netlog("gg_destroykeepalive(): Killed Timer %d", i); -#endif - } -#ifdef DEBUGMODE - netlog("gg_destroykeepalive(): End"); -#endif -} diff --git a/protocols/Gadu-Gadu/libgadu/COPYING b/protocols/Gadu-Gadu/libgadu/COPYING deleted file mode 100644 index 6e816402b7..0000000000 --- a/protocols/Gadu-Gadu/libgadu/COPYING +++ /dev/null @@ -1,504 +0,0 @@ - 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. - - - Copyright (C) - - 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. - - , 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 deleted file mode 100644 index b9b41c0547..0000000000 --- a/protocols/Gadu-Gadu/libgadu/common.c +++ /dev/null @@ -1,975 +0,0 @@ -/* coding: UTF-8 */ -/* $Id: common.c 13762 2011-08-09 12:35:16Z dezred $ */ - -/* - * (C) Copyright 2001-2002 Wojtek Kaniewski - * Robert J. WoĹşny - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 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 -#ifdef _WIN32 -#include "win32.h" -#else -#include -#include -#include -#include -#ifdef sun -# include -#endif -#endif /* _WIN32 */ - -#include -#include -#ifndef _WIN32 -#include -#endif /* _WIN32 */ -#include -#include -#include -#include -#ifndef _WIN32 -#include -#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 deleted file mode 100644 index 1aa17d1e4a..0000000000 --- a/protocols/Gadu-Gadu/libgadu/compat.h +++ /dev/null @@ -1,36 +0,0 @@ -/* coding: UTF-8 */ -/* $Id: compat.h 11075 2009-12-20 15:01:43Z dezred $ */ - -/* - * (C) Copyright 2001-2002 Wojtek Kaniewski - * Robert J. WoĹşny - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 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 deleted file mode 100644 index 2fbe1b430c..0000000000 --- a/protocols/Gadu-Gadu/libgadu/dcc.c +++ /dev/null @@ -1,1363 +0,0 @@ -/* coding: UTF-8 */ -/* $Id: dcc.c 12145 2010-07-07 23:49:04Z dezred $ */ - -/* - * (C) Copyright 2001-2008 Wojtek Kaniewski - * Tomasz ChiliĹ„ski - * Adam Wysocki - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 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 -#include -#ifdef _WIN32 -#include "win32.h" -#else -#include -#include -#include -#include -#ifdef sun -# include -#endif -#endif /* _WIN32 */ - -#include -#include -#include -#include -#include -#include -#include -#ifndef _WIN32 -#include -#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 deleted file mode 100644 index f3813e6e83..0000000000 --- a/protocols/Gadu-Gadu/libgadu/dcc7.c +++ /dev/null @@ -1,1655 +0,0 @@ -/* coding: UTF-8 */ -/* $Id: dcc7.c,v 1.2 2007-07-20 23:00:49 wojtekka Exp $ */ - -/* - * (C) Copyright 2001-2010 Wojtek Kaniewski - * Tomasz ChiliĹ„ski - * Adam Wysocki - * BartĹ‚omiej ZimoĹ„ - * - * Thanks to Jakub Zawadzki - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, - * USA. - */ - -/** - * \file dcc7.c - * - * \brief ObsĹ‚uga poĹ‚Ä…czeĹ„ bezpoĹ›rednich od wersji Gadu-Gadu 7.x - */ - -#ifndef _WIN64 -#define _USE_32BIT_TIME_T -#endif - -#include -#include -#ifdef _WIN32 -#include "win32.h" -#else -#include -#include -#include -#include -#ifdef sun -# include -#endif -#endif /* _WIN32 */ -#include - -#include -#include -#include -#include -#include -#include -#include -#ifndef _WIN32 -#include -#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 deleted file mode 100644 index a730e9d620..0000000000 --- a/protocols/Gadu-Gadu/libgadu/events.c +++ /dev/null @@ -1,2864 +0,0 @@ -/* coding: UTF-8 */ -/* $Id: events.c 13583 2011-04-12 12:51:18Z dezred $ */ - -/* - * (C) Copyright 2001-2006 Wojtek Kaniewski - * Robert J. WoĹşny - * Arkadiusz MiĹ›kiewicz - * Adam Wysocki - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 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 -#ifdef _WIN32 -#include "win32.h" -#else -#include -#include -#include -#include -#endif /* _WIN32 */ - -#include "compat.h" -#include "libgadu.h" -#include "protocol.h" -#include "internal.h" - -#include -#include -#include -#include -#include -#ifndef _WIN32 -#include -#include -#endif /* _WIN32 */ -#ifndef GG_CONFIG_MIRANDA -#ifdef GG_CONFIG_HAVE_OPENSSL -# include -# include -#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, "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 deleted file mode 100644 index c070b3ee53..0000000000 --- a/protocols/Gadu-Gadu/libgadu/http.c +++ /dev/null @@ -1,544 +0,0 @@ -/* coding: UTF-8 */ -/* $Id: http.c 11370 2010-03-13 16:17:54Z dezred $ */ - -/* - * (C) Copyright 2001-2002 Wojtek Kaniewski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - * USA. - */ - -/** - * \file http.c - * - * \brief ObsĹ‚uga poĹ‚Ä…czeĹ„ HTTP - */ - -#include -#ifdef _WIN32 -#include "win32.h" -#else -#include -#include -#include -#endif /* _WIN32 */ - -#include "compat.h" -#include "libgadu.h" -#include "resolver.h" - -#include -#include -#ifndef _WIN32 -#include -#endif /* _WIN32 */ -#include -#include -#include -#include -#include -#ifndef _WIN32 -#include -#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 deleted file mode 100644 index 0ffd39fdd5..0000000000 --- a/protocols/Gadu-Gadu/libgadu/internal.h +++ /dev/null @@ -1,48 +0,0 @@ -/* coding: UTF-8 */ -/* $Id$ */ - -/* - * (C) Copyright 2009 Jakub Zawadzki - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#ifndef LIBGADU_INTERNAL_H -#define LIBGADU_INTERNAL_H - -#include "libgadu.h" - -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 deleted file mode 100644 index f06f8a5b84..0000000000 --- a/protocols/Gadu-Gadu/libgadu/libgadu.c +++ /dev/null @@ -1,2353 +0,0 @@ -/* coding: UTF-8 */ -/* $Id: libgadu.c 13762 2011-08-09 12:35:16Z dezred $ */ - -/* - * (C) Copyright 2001-2010 Wojtek Kaniewski - * Robert J. WoĹşny - * Arkadiusz MiĹ›kiewicz - * Tomasz ChiliĹ„ski - * Adam Wysocki - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - * USA. - */ - -/** - * \file libgadu.c - * - * \brief GĹ‚Ăłwny moduĹ‚ biblioteki - */ - -#ifndef _WIN64 -#define _USE_32BIT_TIME_T -#endif - -#include -#ifdef _WIN32 -#include "win32.h" -#else -#include -#include -#include -#ifdef sun -# include -#endif -#endif /* _WIN32 */ - -#include "compat.h" -#include "libgadu.h" -#include "protocol.h" -#include "resolver.h" -#include "internal.h" - -#include -#ifndef _WIN32 -#include -#endif /* _WIN32 */ -#include -#include -#include -#include -#include -#include -#ifndef _WIN32 -#include -#endif /* _WIN32 */ -#if !defined(GG_CONFIG_MIRANDA) && defined(GG_CONFIG_HAVE_OPENSSL) -# include -# include -#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[] = ""; - const int span_len = 75; - const char img_fmt[] = ""; - 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ć . */ - - 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, "", 4); - - if ((old_attr & GG_FONT_ITALIC) != 0) - gg_append(dst, &len, "", 4); - - if ((old_attr & GG_FONT_BOLD) != 0) - gg_append(dst, &len, "", 4); - - if (src[i] != 0) - gg_append(dst, &len, "", 7); - } - - if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) { - color = &format_[format_idx]; - format_idx += 3; - } else { - color = (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, "", 3); - - if ((attr & GG_FONT_ITALIC) != 0) - gg_append(dst, &len, "", 3); - - if ((attr & GG_FONT_UNDERLINE) != 0) - gg_append(dst, &len, "", 3); - - if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) { - if (dst != NULL) { - sprintf(&dst[len], img_fmt, - format_[format_idx + 9], - format_[format_idx + 8], - format_[format_idx + 7], - format_[format_idx + 6], - format_[format_idx + 5], - format_[format_idx + 4], - format_[format_idx + 3], - format_[format_idx + 2]); - } - - len += img_len; - format_idx += 10; - } - - old_attr = attr; - } - - /* Doklej znak zachowujÄ…c htmlowe escapowanie. */ - - switch (src[i]) { - case '&': - gg_append(dst, &len, "&", 5); - break; - case '<': - gg_append(dst, &len, "<", 4); - break; - case '>': - gg_append(dst, &len, ">", 4); - break; - case '\'': - gg_append(dst, &len, "'", 6); - break; - case '\"': - gg_append(dst, &len, """, 6); - break; - case '\n': - gg_append(dst, &len, "
", 4); - break; - case '\r': - 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, "
", 4); - - if ((old_attr & GG_FONT_ITALIC) != 0) - gg_append(dst, &len, "
", 4); - - if ((old_attr & GG_FONT_BOLD) != 0) - gg_append(dst, &len, "
", 4); - - if (src[0] != 0) - gg_append(dst, &len, "
", 7); - - if (dst != NULL) - dst[len] = 0; - - return len; -} - -/** - * WysyĹ‚a wiadomość formatowanÄ… w ramach konferencji. - * - * Zwraca losowy numer sekwencyjny, ktĂłry moĹĽna zignorować albo wykorzystać - * do potwierdzenia. - * - * \param sess Struktura sesji - * \param msgclass Klasa wiadomoĹ›ci - * \param recipients_count Liczba adresatĂłw - * \param recipients WskaĹşnik do tablicy z numerami adresatĂłw - * \param message Treść wiadomoĹ›ci - * \param format Informacje o formatowaniu - * \param formatlen DĹ‚ugość informacji o formatowaniu - * - * \return Numer sekwencyjny wiadomoĹ›ci lub -1 w przypadku bĹ‚Ä™du. - * - * \ingroup messages - */ -int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen) -{ - struct gg_send_msg s; - struct gg_send_msg80 s80; - struct gg_msg_recipients r; - char *cp_msg = NULL; - char *utf_msg = NULL; - char *html_msg = NULL; - int seq_no; - int i, j, k; - uin_t *recps; - - gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen); - - if (!sess) { - errno = EFAULT; - return -1; - } - - 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 deleted file mode 100644 index d273d998e1..0000000000 --- a/protocols/Gadu-Gadu/libgadu/libgadu.h +++ /dev/null @@ -1,2311 +0,0 @@ -/* coding: UTF-8 */ -/* $Id: libgadu.h 13762 2011-08-09 12:35:16Z dezred $ */ - -/* - * (C) Copyright 2001-2009 Wojtek Kaniewski - * Robert J. WoĹşny - * Arkadiusz MiĹ›kiewicz - * Tomasz ChiliĹ„ski - * Piotr Wysocki - * Dawid Jarosz - * Jakub Zawadzki - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 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 -#endif - -#if defined(__cplusplus) || defined(_WIN32) -#pragma pack(push, 1) -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include - -/** \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 . */ -#undef GG_CONFIG_HAVE_STDINT_H - -/* Defined if uintX_t types are defined in . */ -#undef GG_CONFIG_HAVE_INTTYPES_H - -/* Defined if uintX_t types are defined in . */ -#undef GG_CONFIG_HAVE_SYS_INTTYPES_H - -/* Defined if uintX_t types are defined in . */ -#undef GG_CONFIG_HAVE_SYS_INT_TYPES_H - -/* Defined if uintX_t types are defined in . */ -#undef GG_CONFIG_HAVE_SYS_TYPES_H - -/* 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 -#endif - -#ifdef GG_CONFIG_HAVE_STDINT_H -#include -#else -# ifdef GG_CONFIG_HAVE_INTTYPES_H -# include -# else -# ifdef GG_CONFIG_HAVE_SYS_INTTYPES_H -# include -# else -# ifdef GG_CONFIG_HAVE_SYS_INT_TYPES_H -# include -# else -# ifdef GG_CONFIG_HAVE_SYS_TYPES_H -# include -# else - -#ifndef __AC_STDINT_H -#define __AC_STDINT_H - -/* ISO C 9X: 7.18 Integer types */ - -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -#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 TCHAR *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 */ - 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 deleted file mode 100644 index f8fe4dc5de..0000000000 --- a/protocols/Gadu-Gadu/libgadu/obsolete.c +++ /dev/null @@ -1,238 +0,0 @@ -/* coding: UTF-8 */ -/* $Id: obsolete.c 854 2009-10-12 21:06:28Z wojtekka $ */ - -/* - * (C) Copyright 2001-2003 Wojtek Kaniewski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - * USA. - */ - -/** - * \file obsolete.c - * - * \brief Nieaktualne funkcje - * - * Plik zawiera definicje funkcji, ktĂłre sÄ… juĹĽ nieaktualne ze wzglÄ™du - * na zmiany w protokole. Programy konsolidowane ze starszych wersjami - * bibliotek powinny nadal mieć moĹĽliwość dziaĹ‚ania, mimo ograniczonej - * funkcjonalnoĹ›ci. - */ - -/** \cond obsolete */ - -#include - -#include "libgadu.h" -#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 deleted file mode 100644 index 5b4895c260..0000000000 --- a/protocols/Gadu-Gadu/libgadu/protocol.h +++ /dev/null @@ -1,277 +0,0 @@ -/* coding: UTF-8 */ -/* $Id$ */ - -/* - * (C) Copyright 2009-2010 Jakub Zawadzki - * BartĹ‚omiej ZimoĹ„ - * Wojtek Kaniewski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#ifndef LIBGADU_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 deleted file mode 100644 index 9a8988a358..0000000000 --- a/protocols/Gadu-Gadu/libgadu/pthread.c +++ /dev/null @@ -1,78 +0,0 @@ -/* -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 -#include -#include - -/****************************************/ -/* 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, (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 deleted file mode 100644 index 4de1053e0e..0000000000 --- a/protocols/Gadu-Gadu/libgadu/pthread.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -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 - -// 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 deleted file mode 100644 index f9d656dfe3..0000000000 --- a/protocols/Gadu-Gadu/libgadu/pubdir.c +++ /dev/null @@ -1,862 +0,0 @@ -/* coding: UTF-8 */ -/* $Id: pubdir.c 11370 2010-03-13 16:17:54Z dezred $ */ - -/* - * (C) Copyright 2001-2006 Wojtek Kaniewski - * Dawid Jarosz - * Adam Wysocki - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 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 -#include -#include -#include -#include -#include -#ifdef _WIN32 -#include "win32.h" -#define random() rand() -#else -#include -#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 deleted file mode 100644 index 6914cbd01a..0000000000 --- a/protocols/Gadu-Gadu/libgadu/pubdir50.c +++ /dev/null @@ -1,557 +0,0 @@ -/* coding: UTF-8 */ -/* $Id: pubdir50.c 11370 2010-03-13 16:17:54Z dezred $ */ - -/* - * (C) Copyright 2003 Wojtek Kaniewski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - * USA. - */ - -/** - * \file pubdir50.c - * - * \brief ObsĹ‚uga katalogu publicznego od wersji Gadu-Gadu 5.x - */ - -#ifndef _WIN64 -#define _USE_32BIT_TIME_T -#endif - -#include -#include -#include -#include - -#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 deleted file mode 100644 index 1adef3ef9d..0000000000 --- a/protocols/Gadu-Gadu/libgadu/resolver.c +++ /dev/null @@ -1,766 +0,0 @@ -/* coding: UTF-8 */ -/* $Id$ */ - -/* - * (C) Copyright 2001-2009 Wojtek Kaniewski - * Robert J. WoĹşny - * Arkadiusz MiĹ›kiewicz - * Tomasz ChiliĹ„ski - * Adam Wysocki - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - * USA. - */ - -/** - * \file resolver.c - * - * \brief Funkcje rozwiÄ…zywania nazw - */ - -#ifdef _WIN32 -#include "win32.h" -#else -#include -#include -#include -#endif /* _WIN32 */ - -#ifndef _WIN32 -#include -#endif -#include -#include -#include -#ifndef _WIN32 -#include -#endif -#include - -#include "libgadu.h" -#include "resolver.h" -#include "compat.h" - -/** SposĂłb rozwiÄ…zywania nazw serwerĂłw */ -static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT; - -/** Funkcja rozpoczynajÄ…ca rozwiÄ…zywanie nazwy */ -static int (*gg_global_resolver_start)(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 - -#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" — wskaĹşnik na zmiennÄ…, gdzie zostanie umieszczony deskryptor potoku - * - \c "void **priv_data" — wskaĹşnik na zmiennÄ…, gdzie moĹĽna umieĹ›cić wskaĹşnik do prywatnych danych na potrzeby rozwiÄ…zywania nazwy - * - \c "const char *name" — nazwa serwera do rozwiÄ…zania - * - * Parametry funkcji zwalniajÄ…cej zasoby wyglÄ…dajÄ… nastÄ™pujÄ…co: - * - \c "void **priv_data" — wskaĹşnik na zmiennÄ… przechowujÄ…cÄ… wskaĹşnik do prywatnych danych, naleĹĽy go ustawić na \c NULL po zakoĹ„czeniu - * - \c "int force" — flaga mĂłwiÄ…ca o tym, ĹĽe zasoby sÄ… zwalniane przed zakoĹ„czeniem rozwiÄ…zywania nazwy, np. z powodu zamkniÄ™cia sesji. - * - * WĹ‚asny kod rozwiÄ…zywania nazwy powinien stworzyć potok, parÄ™ gniazd lub - * inny deskryptor pozwalajÄ…cy na co najmniej jednostronnÄ… komunikacjÄ™ i - * przekazać go w parametrze \c fd. Po zakoĹ„czeniu rozwiÄ…zywania nazwy, - * powinien wysĹ‚ać otrzymany adres IP w postaci sieciowej (big-endian) do - * deskryptora. JeĹ›li rozwiÄ…zywanie nazwy siÄ™ nie powiedzie, naleĹĽy wysĹ‚ać - * \c INADDR_NONE. NastÄ™pnie zostanie wywoĹ‚ana funkcja zwalniajÄ…ca zasoby - * z parametrem \c force rĂłwnym \c 0. Gdyby sesja zostaĹ‚a zakoĹ„czona przed - * rozwiÄ…zaniem nazwy, np. za pomocÄ… funkcji \c gg_logoff(), funkcja - * zwalniajÄ…ca zasoby zostanie wywoĹ‚ana z parametrem \c force rĂłwnym \c 1. - * - * \return 0 jeĹ›li siÄ™ powiodĹ‚o, -1 w przypadku bĹ‚Ä™du - */ -int gg_global_set_custom_resolver(int (*resolver_start)(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 deleted file mode 100644 index 145c5178a4..0000000000 --- a/protocols/Gadu-Gadu/libgadu/resolver.h +++ /dev/null @@ -1,33 +0,0 @@ -/* coding: UTF-8 */ -/* $Id$ */ - -/* - * (C) Copyright 2008 Wojtek Kaniewski - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - * USA. - */ - -#ifndef LIBGADU_RESOLVER_H -#define LIBGADU_RESOLVER_H - -#ifdef _WIN32 -#include "win32.h" -#else -#include -#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 deleted file mode 100644 index 124a1cffaa..0000000000 --- a/protocols/Gadu-Gadu/libgadu/sha1.c +++ /dev/null @@ -1,308 +0,0 @@ -/* coding: UTF-8 */ -/* $Id: sha1.c,v 1.4 2007-07-20 23:00:50 wojtekka Exp $ */ - -/* - * (C) Copyright 2007 Wojtek Kaniewski - * - * Public domain SHA-1 implementation by Steve Reid - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License Version - * 2.1 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, - * USA. - */ - -/** - * \file sha1.c - * - * \brief Funkcje wyznaczania skrÄ‚Ĺ‚tu SHA1 - */ - -#include -#include -#ifdef _WIN32 -#include "win32.h" -#else -#include -#endif - -#include "libgadu.h" - -/** \cond ignore */ - -#if defined(GG_CONFIG_HAVE_OPENSSL) && !defined(GG_CONFIG_MIRANDA) - -#include - -#else - -/* -SHA-1 in C -By Steve Reid -100% Public Domain - -Modified by Wojtek Kaniewski for compatibility -with libgadu and OpenSSL API. - -Test Vectors (from FIPS PUB 180-1) -"abc" - A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D -"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" - 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 -A million repetitions of "a" - 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F -*/ - -/* #define LITTLE_ENDIAN * This should be #define'd if true. */ -/* #define SHA1HANDSOFF * Copies data before messing with it. */ - -#include - -typedef struct { - uint32_t state[5]; - uint32_t count[2]; - unsigned char buffer[64]; -} SHA_CTX; - -static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64]); -static void SHA1_Init(SHA_CTX* context); -static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len); -static void SHA1_Final(unsigned char digest[20], SHA_CTX* context); - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -/* blk0() and blk() perform the initial expand. */ -/* I got the idea of expanding during the round function from SSLeay */ -#ifndef GG_CONFIG_BIGENDIAN -#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ - |(rol(block->l[i],8)&0x00FF00FF)) -#else -#define blk0(i) block->l[i] -#endif -#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ - ^block->l[(i+2)&15]^block->l[i&15],1)) - -/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); -#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); -#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); -#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); - - -/* Hash a single 512-bit block. This is the core of the algorithm. */ - -static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64]) -{ -uint32_t a, b, c, d, e; -typedef union { - unsigned char c[64]; - uint32_t l[16]; -} CHAR64LONG16; -CHAR64LONG16* block; -static unsigned char workspace[64]; - block = (CHAR64LONG16*)workspace; - memcpy(block, buffer, 64); - /* Copy context->state[] to working vars */ - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - /* Add the working vars back into context.state[] */ - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - /* Wipe variables */ - a = b = c = d = e = 0; -} - - -/* SHA1_Init - Initialize new context */ - -static void SHA1_Init(SHA_CTX* context) -{ - /* SHA1 initialization constants */ - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - - -/* Run your data through this. */ - -static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len) -{ -unsigned int i, j; - - j = (context->count[0] >> 3) & 63; - if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; - context->count[1] += (len >> 29); - if ((j + len) > 63) { - memcpy(&context->buffer[j], data, (i = 64-j)); - SHA1_Transform(context->state, context->buffer); - for ( ; i + 63 < len; i += 64) { - SHA1_Transform(context->state, &data[i]); - } - j = 0; - } - else i = 0; - memcpy(&context->buffer[j], &data[i], len - i); -} - - -/* Add padding and return the message digest. */ - -static void SHA1_Final(unsigned char digest[20], SHA_CTX* context) -{ -uint32_t i, j; -unsigned char finalcount[8]; - - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] - >> ((3-(i & 3)) * 8)) & 255); /* Endian independent */ - } - SHA1_Update(context, (unsigned char *)"\200", 1); - while ((context->count[0] & 504) != 448) { - SHA1_Update(context, (unsigned char *)"\0", 1); - } - SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */ - for (i = 0; i < 20; i++) { - digest[i] = (unsigned char) - ((context->state[i>>2] >> ((3-(i & 3)) * 8)) & 255); - } - /* Wipe variables */ - i = j = 0; - memset(context->buffer, 0, 64); - memset(context->state, 0, 20); - memset(context->count, 0, 8); - memset(&finalcount, 0, 8); -#ifdef SHA1HANDSOFF /* make SHA1_Transform overwrite it's own static vars */ - SHA1_Transform(context->state, context->buffer); -#endif -} - -#endif /* GG_CONFIG_HAVE_OPENSSL */ - -/** \endcond */ - -/** \cond internal */ - -/** - * \internal Liczy skrÄ‚Ĺ‚t SHA1 z ziarna i hasła. - * - * \param password Hasło - * \param seed Ziarno - * \param result Bufor na wynik funkcji skrÄ‚Ĺ‚tu (20 bajtÄ‚Ĺ‚w) - */ -void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result) -{ - SHA_CTX ctx; - - SHA1_Init(&ctx); - SHA1_Update(&ctx, (const unsigned char*) password, (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 deleted file mode 100644 index 4f5ad3ff5d..0000000000 --- a/protocols/Gadu-Gadu/libgadu/win32.c +++ /dev/null @@ -1,65 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2009 Adam Strzelecki -// 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 deleted file mode 100644 index d432a359ac..0000000000 --- a/protocols/Gadu-Gadu/libgadu/win32.h +++ /dev/null @@ -1,75 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2009 Adam Strzelecki -// 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 -#include - -/* Some Visual C++ overrides having no problems with MinGW */ -#ifdef _MSC_VER -#define S_IWUSR 0x0080 -/* Make sure we included errno before that */ -#include -#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.cpp b/protocols/Gadu-Gadu/links.cpp deleted file mode 100644 index 53770f0bcd..0000000000 --- a/protocols/Gadu-Gadu/links.cpp +++ /dev/null @@ -1,173 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// 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 = (GGPROTO*)l->data; - - mi.flags = CMIM_FLAGS; - if (gginst->m_iStatus > ID_STATUS_OFFLINE) - { - ++items; - gg = (GGPROTO*)l->data; - mi.flags |= CMIM_ICON; - mi.hIcon = LoadSkinnedProtoIcon(gg->m_szModuleName, gg->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)¶m); - - 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(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 GGPROTO::links_instance_init() -{ - if (ServiceExists(MS_ASSOCMGR_ADDNEWURLTYPE)) - { - TMO_MenuItem tmi = {0}; - tmi.cbSize = sizeof(tmi); - tmi.flags = CMIF_TCHAR; - tmi.ownerdata = this; - tmi.position = list_count(g_Instances); - tmi.ptszName = m_tszUserName; - hInstanceMenuItem = (HANDLE)CallService(MO_ADDNEWMENUITEM, (WPARAM)hInstanceMenu, (LPARAM)&tmi); - } -} diff --git a/protocols/Gadu-Gadu/oauth.cpp b/protocols/Gadu-Gadu/oauth.cpp deleted file mode 100644 index 43983edb6d..0000000000 --- a/protocols/Gadu-Gadu/oauth.cpp +++ /dev/null @@ -1,584 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// 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 -#include -#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(const OAUTHPARAMETER *p1, const 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(LIST ¶ms, const char *httpmethod, const char *url) -{ - char *res, *urlenc, *urlnorm; - OAUTHPARAMETER *p; - int i, ix = 0, size; - - if (httpmethod == NULL || url == NULL || !params.getCount()) 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.getCount(); i++) { - p = params[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.getCount(); i++) { - p = params[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(LIST ¶ms, const char *name) -{ - OAUTHPARAMETER *p; - int i; - - if (name == NULL) return NULL; - - for (i = 0; i < params.getCount(); i++) { - p = params[i]; - if (!strcmp(p->name, name)) - return p->value; - } - - return NULL; -} - -void oauth_setparam(LIST ¶ms, const char *name, const char *value) -{ - OAUTHPARAMETER *p; - int i; - - if (name == NULL) return; - - for (i = 0; i < params.getCount(); i++) { - p = params[i]; - if (!strcmp(p->name, name)) { - mir_free(p->value); - p->value = oauth_uri_escape(value); - return; - } - } - - p = (OAUTHPARAMETER*)mir_alloc(sizeof(OAUTHPARAMETER)); - p->name = oauth_uri_escape(name); - p->value = oauth_uri_escape(value); - params.insert(p); -} - -void oauth_freeparams(LIST ¶ms) -{ - OAUTHPARAMETER *p; - int i; - - for (i = 0; i < params.getCount(); i++) { - p = params[i]; - mir_free(p->name); - mir_free(p->value); - } -} - -int oauth_sign_request(LIST ¶ms, const char *httpmethod, const char *url, - const char *consumer_secret, const char *token_secret) -{ - char *sign = NULL, *signmethod; - - if (!params.getCount()) 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((BYTE*)text, (int)strlen(text), (BYTE*)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((BYTE*)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) -{ - int i, size; - char *res, timestamp[22], *nonce; - - if (httpmethod == NULL || url == NULL) return NULL; - - LIST oauth_parameters(1, paramsortFunc); - 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); - oauth_parameters.destroy(); - return NULL; - } - - size = 7; - for (i = 0; i < oauth_parameters.getCount(); i++) { - OAUTHPARAMETER *p = oauth_parameters[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.getCount(); i++) { - OAUTHPARAMETER *p = oauth_parameters[i]; - if (i > 0) strcat(res, ","); - strcat(res, p->name); - strcat(res, "=\""); - strcat(res, p->value); - strcat(res, "\""); - } - - oauth_freeparams(oauth_parameters); - oauth_parameters.destroy(); - return res; -} - -char* GGPROTO::oauth_header(const char *httpmethod, const char *url) -{ - char *res, uin[32], *password = NULL, *token = NULL, *token_secret = NULL; - DBVARIANT dbv; - - UIN2ID( db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0), uin); - if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { - CallService(MS_DB_CRYPT_DECODESTRING, (WPARAM)(int)strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal); - password = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - if (!db_get_s(NULL, m_szModuleName, GG_KEY_TOKEN, &dbv, DBVT_ASCIIZ)) { - token = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - if (!db_get_s(NULL, m_szModuleName, GG_KEY_TOKENSECRET, &dbv, DBVT_ASCIIZ)) { - 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 GGPROTO::oauth_receivetoken() -{ - 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; - HANDLE nlc = NULL; - - UIN2ID( db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0), uin); - if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { - 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 - netlog("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 | NLHRF_PERSISTENT; - 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)netlib, (LPARAM)&req); - if (resp) { - nlc = resp->nlc; - if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { - HXML hXml; - TCHAR *xmlAction; - TCHAR *tag; - - xmlAction = mir_a2t(resp->pData); - tag = mir_a2t("result"); - hXml = xi.parseString(xmlAction, 0, tag); - if (hXml != NULL) { - HXML node; - - mir_free(tag); tag = mir_a2t("oauth_token"); - node = xi.getChildByPath(hXml, tag, 0); - token = node != NULL ? mir_t2a(xi.getText(node)) : NULL; - - mir_free(tag); tag = mir_a2t("oauth_token_secret"); - node = xi.getChildByPath(hXml, tag, 0); - token_secret = node != NULL ? mir_t2a(xi.getText(node)) : NULL; - - xi.destroyNode(hXml); - } - mir_free(tag); - mir_free(xmlAction); - } - else netlog("gg_oauth_receivetoken(): Invalid response code from HTTP request"); - CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); - } - else netlog("gg_oauth_receivetoken(): No response from HTTP request"); - - // 2. Obtaining User Authorization - netlog("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)netlib, (LPARAM)&req); - if (resp) CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); - else netlog("gg_oauth_receivetoken(): No response from HTTP request"); - - // 3. Obtaining an Access Token - netlog("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 | NLHRF_PERSISTENT; - req.nlc = nlc; - req.headersCount = 3; - req.headers = httpHeaders; - httpHeaders[1].szName = "Authorization"; - httpHeaders[1].szValue = str; - - resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)netlib, (LPARAM)&req); - if (resp) { - if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { - HXML hXml; - TCHAR *xmlAction; - TCHAR *tag; - - xmlAction = mir_a2t(resp->pData); - tag = mir_a2t("result"); - hXml = xi.parseString(xmlAction, 0, tag); - if (hXml != NULL) { - HXML node; - - mir_free(tag); tag = mir_a2t("oauth_token"); - node = xi.getChildByPath(hXml, tag, 0); - token = node != NULL ? mir_t2a(xi.getText(node)) : NULL; - - mir_free(tag); tag = mir_a2t("oauth_token_secret"); - node = xi.getChildByPath(hXml, tag, 0); - token_secret = node != NULL ? mir_t2a(xi.getText(node)) : NULL; - - xi.destroyNode(hXml); - } - mir_free(tag); - mir_free(xmlAction); - } - else netlog("gg_oauth_receivetoken(): Invalid response code from HTTP request"); - Netlib_CloseHandle(resp->nlc); - CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); - } - else netlog("gg_oauth_receivetoken(): No response from HTTP request"); - - mir_free(password); - mir_free(str); - - if (token != NULL && token_secret != NULL) { - db_set_s(NULL, m_szModuleName, GG_KEY_TOKEN, token); - CallService(MS_DB_CRYPT_ENCODESTRING, (WPARAM)(int)strlen(token_secret) + 1, (LPARAM) token_secret); - db_set_s(NULL, m_szModuleName, GG_KEY_TOKENSECRET, token_secret); - netlog("gg_oauth_receivetoken(): Access Token obtained successfully."); - res = 1; - } - else { - db_unset(NULL, m_szModuleName, GG_KEY_TOKEN); - db_unset(NULL, m_szModuleName, GG_KEY_TOKENSECRET); - netlog("gg_oauth_receivetoken(): Failed to obtain Access Token."); - } - mir_free(token); - mir_free(token_secret); - - return res; -} - -int GGPROTO::oauth_checktoken(int force) -{ - if (!force) { - char *token = NULL, *token_secret = NULL; - DBVARIANT dbv; - int res = 1; - - if (!db_get_s(NULL, m_szModuleName, GG_KEY_TOKEN, &dbv, DBVT_ASCIIZ)) { - token = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - if (!db_get_s(NULL, m_szModuleName, GG_KEY_TOKENSECRET, &dbv, DBVT_ASCIIZ)) { - 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 = oauth_receivetoken(); - } - - mir_free(token); - mir_free(token_secret); - - return res; - } - - return oauth_receivetoken(); -} diff --git a/protocols/Gadu-Gadu/ownerinfo.cpp b/protocols/Gadu-Gadu/ownerinfo.cpp deleted file mode 100644 index 6c37cdb180..0000000000 --- a/protocols/Gadu-Gadu/ownerinfo.cpp +++ /dev/null @@ -1,78 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 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. -//////////////////////////////////////////////////////////////////////////////// - -#include "gg.h" - -////////////////////////////////////////////////////////// -// remind password - -typedef struct -{ - uin_t uin; - const char *email; -} GG_REMIND_PASS; - -void __cdecl GGPROTO::remindpasswordthread(void *param) -{ - // Connection handle - struct gg_http *h; - GG_REMIND_PASS *rp = (GG_REMIND_PASS *)param; - GGTOKEN token; - -#ifdef DEBUGMODE - netlog("gg_remindpasswordthread(): Starting."); -#endif - if (!rp || !rp->email || !rp->uin || !strlen(rp->email)) - { - if (rp) free(rp); - return; - } - - // Get token - if (!gettoken(&token)) return; - - if (!(h = gg_remind_passwd3(rp->uin, rp->email, token.id, token.val, 0))) - { - TCHAR error[128]; - mir_sntprintf(error, SIZEOF(error), TranslateT("Password could not be reminded because of error:\n\t%s"), _tcserror(errno)); - MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); - netlog("gg_remindpasswordthread(): Password could not be reminded because of \"%s\".", strerror(errno)); - } - else - { - gg_pubdir_free(h); - netlog("gg_remindpasswordthread(): Password remind successful."); - MessageBox(NULL, TranslateT("Password was sent to your e-mail."), m_tszUserName, MB_OK | MB_ICONINFORMATION); - } - -#ifdef DEBUGMODE - netlog("gg_remindpasswordthread(): End."); -#endif - if (rp) free(rp); -} - -void GGPROTO::remindpassword(uin_t uin, const char *email) -{ - GG_REMIND_PASS *rp = (GG_REMIND_PASS*)malloc(sizeof(GG_REMIND_PASS)); - - rp->uin = uin; - rp->email = email; - forkthread(&GGPROTO::remindpasswordthread, rp); -} diff --git a/protocols/Gadu-Gadu/popups.cpp b/protocols/Gadu-Gadu/popups.cpp deleted file mode 100644 index 69ac1c350d..0000000000 --- a/protocols/Gadu-Gadu/popups.cpp +++ /dev/null @@ -1,174 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// 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" - -struct PopupData -{ - unsigned flags; - TCHAR* title; - TCHAR* text; - GGPROTO* gg; -}; - -///////////////////////////////////////////////////////////////////////////////////////// -// 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) - puData->gg->sessions_view(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 GGPROTO::initpopups() -{ - TCHAR szDescr[256]; - char szName[256]; - - POPUPCLASS puc = {0}; - puc.cbSize = sizeof(puc); - puc.PluginWindowProc = PopupWindowProc; - puc.flags = PCF_TCHAR; - - 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_sntprintf(szDescr, SIZEOF(szDescr), _T("%s/%s"), m_tszUserName, TranslateT("Notify")); - mir_snprintf(szName, SIZEOF(szName), "%s_%s", m_szModuleName, "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_sntprintf(szDescr, SIZEOF(szDescr), _T("%s/%s"), m_tszUserName, TranslateT("Error")); - mir_snprintf(szName, SIZEOF(szName), "%s_%s", m_szModuleName, "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->m_szModuleName, "Error"); - else - mir_snprintf(szName, SIZEOF(szName), "%s_%s", gg->m_szModuleName, "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->m_tszUserName); - while (hWnd != NULL) - { - if (FindWindowEx(hWnd, NULL, NULL, puData->text) != NULL) - { - bShow = FALSE; - break; - } - hWnd = FindWindowEx(NULL, hWnd, NULL, gg->m_tszUserName); - } - } - - 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->m_tszUserName, MB_OK | uIcon); - } - } - mir_free(puData->title); - mir_free(puData->text); - mir_free(puData); - } -} - -void GGPROTO::showpopup(const TCHAR* nickname, const TCHAR* msg, int flags) -{ - PopupData* puData; - - if (Miranda_Terminated()) return; - - puData = (PopupData*)mir_alloc(sizeof(PopupData)); - puData->flags = flags; - puData->title = mir_tstrdup(nickname); - puData->text = mir_tstrdup(msg); - puData->gg = this; - - CallFunctionAsync(sttMainThreadCallback, puData); -} diff --git a/protocols/Gadu-Gadu/proto_gg/Proto_GG.rc b/protocols/Gadu-Gadu/proto_gg/Proto_GG.rc deleted file mode 100644 index b3188f3d4a..0000000000 Binary files a/protocols/Gadu-Gadu/proto_gg/Proto_GG.rc and /dev/null differ diff --git a/protocols/Gadu-Gadu/proto_gg/Proto_GG.vcxproj b/protocols/Gadu-Gadu/proto_gg/Proto_GG.vcxproj index 250beed3aa..7ff48e0a01 100644 --- a/protocols/Gadu-Gadu/proto_gg/Proto_GG.vcxproj +++ b/protocols/Gadu-Gadu/proto_gg/Proto_GG.vcxproj @@ -119,10 +119,10 @@
- + - + diff --git a/protocols/Gadu-Gadu/proto_gg/Proto_GG.vcxproj.filters b/protocols/Gadu-Gadu/proto_gg/Proto_GG.vcxproj.filters index 5e666a0fc0..c5b7b23e4b 100644 --- a/protocols/Gadu-Gadu/proto_gg/Proto_GG.vcxproj.filters +++ b/protocols/Gadu-Gadu/proto_gg/Proto_GG.vcxproj.filters @@ -11,12 +11,12 @@ - + Header Files - + Resource Files diff --git a/protocols/Gadu-Gadu/proto_gg/icos/Away.ico b/protocols/Gadu-Gadu/proto_gg/icos/Away.ico deleted file mode 100644 index 33cdbf482e..0000000000 Binary files a/protocols/Gadu-Gadu/proto_gg/icos/Away.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/proto_gg/icos/DND.ico b/protocols/Gadu-Gadu/proto_gg/icos/DND.ico deleted file mode 100644 index f4aee0d05b..0000000000 Binary files a/protocols/Gadu-Gadu/proto_gg/icos/DND.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/proto_gg/icos/FFC.ico b/protocols/Gadu-Gadu/proto_gg/icos/FFC.ico deleted file mode 100644 index 13821f419d..0000000000 Binary files a/protocols/Gadu-Gadu/proto_gg/icos/FFC.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/proto_gg/icos/Invisible.ico b/protocols/Gadu-Gadu/proto_gg/icos/Invisible.ico deleted file mode 100644 index d9e9d2913a..0000000000 Binary files a/protocols/Gadu-Gadu/proto_gg/icos/Invisible.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/proto_gg/icos/NA.ico b/protocols/Gadu-Gadu/proto_gg/icos/NA.ico deleted file mode 100644 index bc1b4b4c10..0000000000 Binary files a/protocols/Gadu-Gadu/proto_gg/icos/NA.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/proto_gg/icos/Offline.ico b/protocols/Gadu-Gadu/proto_gg/icos/Offline.ico deleted file mode 100644 index 60f3d1d330..0000000000 Binary files a/protocols/Gadu-Gadu/proto_gg/icos/Offline.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/proto_gg/icos/Online.ico b/protocols/Gadu-Gadu/proto_gg/icos/Online.ico deleted file mode 100644 index 98794a4123..0000000000 Binary files a/protocols/Gadu-Gadu/proto_gg/icos/Online.ico and /dev/null differ diff --git a/protocols/Gadu-Gadu/proto_gg/res/Away.ico b/protocols/Gadu-Gadu/proto_gg/res/Away.ico new file mode 100644 index 0000000000..33cdbf482e Binary files /dev/null and b/protocols/Gadu-Gadu/proto_gg/res/Away.ico differ diff --git a/protocols/Gadu-Gadu/proto_gg/res/DND.ico b/protocols/Gadu-Gadu/proto_gg/res/DND.ico new file mode 100644 index 0000000000..f4aee0d05b Binary files /dev/null and b/protocols/Gadu-Gadu/proto_gg/res/DND.ico differ diff --git a/protocols/Gadu-Gadu/proto_gg/res/FFC.ico b/protocols/Gadu-Gadu/proto_gg/res/FFC.ico new file mode 100644 index 0000000000..13821f419d Binary files /dev/null and b/protocols/Gadu-Gadu/proto_gg/res/FFC.ico differ diff --git a/protocols/Gadu-Gadu/proto_gg/res/Invisible.ico b/protocols/Gadu-Gadu/proto_gg/res/Invisible.ico new file mode 100644 index 0000000000..d9e9d2913a Binary files /dev/null and b/protocols/Gadu-Gadu/proto_gg/res/Invisible.ico differ diff --git a/protocols/Gadu-Gadu/proto_gg/res/NA.ico b/protocols/Gadu-Gadu/proto_gg/res/NA.ico new file mode 100644 index 0000000000..bc1b4b4c10 Binary files /dev/null and b/protocols/Gadu-Gadu/proto_gg/res/NA.ico differ diff --git a/protocols/Gadu-Gadu/proto_gg/res/Offline.ico b/protocols/Gadu-Gadu/proto_gg/res/Offline.ico new file mode 100644 index 0000000000..60f3d1d330 Binary files /dev/null and b/protocols/Gadu-Gadu/proto_gg/res/Offline.ico differ diff --git a/protocols/Gadu-Gadu/proto_gg/res/Online.ico b/protocols/Gadu-Gadu/proto_gg/res/Online.ico new file mode 100644 index 0000000000..98794a4123 Binary files /dev/null and b/protocols/Gadu-Gadu/proto_gg/res/Online.ico differ diff --git a/protocols/Gadu-Gadu/proto_gg/res/Proto_GG.rc b/protocols/Gadu-Gadu/proto_gg/res/Proto_GG.rc new file mode 100644 index 0000000000..03cfefcd8b Binary files /dev/null and b/protocols/Gadu-Gadu/proto_gg/res/Proto_GG.rc differ diff --git a/protocols/Gadu-Gadu/proto_gg/resource.h b/protocols/Gadu-Gadu/proto_gg/resource.h deleted file mode 100644 index cc58929a32..0000000000 Binary files a/protocols/Gadu-Gadu/proto_gg/resource.h and /dev/null differ diff --git a/protocols/Gadu-Gadu/proto_gg/src/resource.h b/protocols/Gadu-Gadu/proto_gg/src/resource.h new file mode 100644 index 0000000000..cc58929a32 Binary files /dev/null and b/protocols/Gadu-Gadu/proto_gg/src/resource.h differ diff --git a/protocols/Gadu-Gadu/res/block.ico b/protocols/Gadu-Gadu/res/block.ico new file mode 100644 index 0000000000..6f59309853 Binary files /dev/null and b/protocols/Gadu-Gadu/res/block.ico differ diff --git a/protocols/Gadu-Gadu/res/clear_ignored_conference.ico b/protocols/Gadu-Gadu/res/clear_ignored_conference.ico new file mode 100644 index 0000000000..f883585899 Binary files /dev/null and b/protocols/Gadu-Gadu/res/clear_ignored_conference.ico differ diff --git a/protocols/Gadu-Gadu/res/conference.ico b/protocols/Gadu-Gadu/res/conference.ico new file mode 100644 index 0000000000..3e4cfcc606 Binary files /dev/null and b/protocols/Gadu-Gadu/res/conference.ico differ diff --git a/protocols/Gadu-Gadu/res/delete.ico b/protocols/Gadu-Gadu/res/delete.ico new file mode 100644 index 0000000000..9108e38cfd Binary files /dev/null and b/protocols/Gadu-Gadu/res/delete.ico differ diff --git a/protocols/Gadu-Gadu/res/export_list_to_server.ico b/protocols/Gadu-Gadu/res/export_list_to_server.ico new file mode 100644 index 0000000000..8c0f660788 Binary files /dev/null and b/protocols/Gadu-Gadu/res/export_list_to_server.ico differ diff --git a/protocols/Gadu-Gadu/res/export_list_to_txt_file.ico b/protocols/Gadu-Gadu/res/export_list_to_txt_file.ico new file mode 100644 index 0000000000..fc239f9a02 Binary files /dev/null and b/protocols/Gadu-Gadu/res/export_list_to_txt_file.ico differ diff --git a/protocols/Gadu-Gadu/res/gg.ico b/protocols/Gadu-Gadu/res/gg.ico new file mode 100644 index 0000000000..a49986a12d Binary files /dev/null and b/protocols/Gadu-Gadu/res/gg.ico differ diff --git a/protocols/Gadu-Gadu/res/image.ico b/protocols/Gadu-Gadu/res/image.ico new file mode 100644 index 0000000000..785cd8ba9e Binary files /dev/null and b/protocols/Gadu-Gadu/res/image.ico differ diff --git a/protocols/Gadu-Gadu/res/import_list_from_server.ico b/protocols/Gadu-Gadu/res/import_list_from_server.ico new file mode 100644 index 0000000000..21ecf6159d Binary files /dev/null and b/protocols/Gadu-Gadu/res/import_list_from_server.ico differ diff --git a/protocols/Gadu-Gadu/res/import_list_from_txt_file.ico b/protocols/Gadu-Gadu/res/import_list_from_txt_file.ico new file mode 100644 index 0000000000..2acc738fc7 Binary files /dev/null and b/protocols/Gadu-Gadu/res/import_list_from_txt_file.ico differ diff --git a/protocols/Gadu-Gadu/res/list.ico b/protocols/Gadu-Gadu/res/list.ico new file mode 100644 index 0000000000..86139d5e23 Binary files /dev/null and b/protocols/Gadu-Gadu/res/list.ico differ diff --git a/protocols/Gadu-Gadu/res/next.ico b/protocols/Gadu-Gadu/res/next.ico new file mode 100644 index 0000000000..8ddc389b85 Binary files /dev/null and b/protocols/Gadu-Gadu/res/next.ico differ diff --git a/protocols/Gadu-Gadu/res/previous.ico b/protocols/Gadu-Gadu/res/previous.ico new file mode 100644 index 0000000000..8e6dc1ef08 Binary files /dev/null and b/protocols/Gadu-Gadu/res/previous.ico differ diff --git a/protocols/Gadu-Gadu/res/remove_list_from_server.ico b/protocols/Gadu-Gadu/res/remove_list_from_server.ico new file mode 100644 index 0000000000..44fe7a833a Binary files /dev/null and b/protocols/Gadu-Gadu/res/remove_list_from_server.ico differ diff --git a/protocols/Gadu-Gadu/res/resource.rc b/protocols/Gadu-Gadu/res/resource.rc new file mode 100644 index 0000000000..b336bf71fb --- /dev/null +++ b/protocols/Gadu-Gadu/res/resource.rc @@ -0,0 +1,354 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda NG +// +// Copyright (c) 2003-2006 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. +//////////////////////////////////////////////////////////////////////////////// + +#include +#include "..\src\resource.h" + +IDI_GG ICON "gg.ico" +IDI_IMPORT_SERVER ICON "import_list_from_server.ico" +IDI_IMPORT_TEXT ICON "import_list_from_txt_file.ico" +IDI_REMOVE_SERVER ICON "remove_list_from_server.ico" +IDI_EXPORT_SERVER ICON "export_list_to_server.ico" +IDI_EXPORT_TEXT ICON "export_list_to_txt_file.ico" +IDI_SETTINGS ICON "settings.ico" +IDI_LIST ICON "list.ico" +IDI_BLOCK ICON "block.ico" +IDI_PREV ICON "previous.ico" +IDI_NEXT ICON "next.ico" +IDI_IMAGE ICON "image.ico" +IDI_SAVE ICON "save.ico" +IDI_DELETE ICON "delete.ico" +IDI_CONFERENCE ICON "conference.ico" +IDI_CLEAR_CONFERENCE ICON "clear_ignored_conference.ico" +IDI_SESSIONS ICON "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/res/save.ico b/protocols/Gadu-Gadu/res/save.ico new file mode 100644 index 0000000000..a8251f70e4 Binary files /dev/null and b/protocols/Gadu-Gadu/res/save.ico differ diff --git a/protocols/Gadu-Gadu/res/sessions.ico b/protocols/Gadu-Gadu/res/sessions.ico new file mode 100644 index 0000000000..9f9383e22f Binary files /dev/null and b/protocols/Gadu-Gadu/res/sessions.ico differ diff --git a/protocols/Gadu-Gadu/res/settings.ico b/protocols/Gadu-Gadu/res/settings.ico new file mode 100644 index 0000000000..6706ec2f6c Binary files /dev/null and b/protocols/Gadu-Gadu/res/settings.ico differ diff --git a/protocols/Gadu-Gadu/res/version.rc b/protocols/Gadu-Gadu/res/version.rc new file mode 100644 index 0000000000..9c1f0fcb02 --- /dev/null +++ b/protocols/Gadu-Gadu/res/version.rc @@ -0,0 +1,60 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda NG +// +// 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. +//////////////////////////////////////////////////////////////////////////////// + +#ifdef APSTUDIO_INVOKED +#error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + +#include +#include "..\src\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 NG" + 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 NG" + VALUE "ProductVersion", __VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END diff --git a/protocols/Gadu-Gadu/resource.h b/protocols/Gadu-Gadu/resource.h deleted file mode 100644 index 86ec31eadb..0000000000 --- a/protocols/Gadu-Gadu/resource.h +++ /dev/null @@ -1,147 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 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 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 deleted file mode 100644 index 22633e1804..0000000000 --- a/protocols/Gadu-Gadu/resource.rc +++ /dev/null @@ -1,355 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda NG -// -// Copyright (c) 2003-2006 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. -//////////////////////////////////////////////////////////////////////////////// - -#include -#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.cpp b/protocols/Gadu-Gadu/services.cpp deleted file mode 100644 index a172d10609..0000000000 --- a/protocols/Gadu-Gadu/services.cpp +++ /dev/null @@ -1,330 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2009 Adam Strzelecki -// 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 - -////////////////////////////////////////////////////////// -// 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; -} - -////////////////////////////////////////////////////////// -// gets protocol status - -char* GGPROTO::getstatusmsg(int status) -{ - switch(status) { - case ID_STATUS_ONLINE: - return modemsg.online; - break; - case ID_STATUS_DND: - return modemsg.dnd; - break; - case ID_STATUS_FREECHAT: - return modemsg.freechat; - break; - case ID_STATUS_INVISIBLE: - return modemsg.invisible; - break; - case ID_STATUS_AWAY: - default: - return modemsg.away; - } -} - -////////////////////////////////////////////////////////// -// sets specified protocol status - -int GGPROTO::refreshstatus(int status) -{ - if (status == ID_STATUS_OFFLINE) - { - disconnect(); - return TRUE; - } - - if (!isonline()) - { - DWORD exitCode = 0; - GetExitCodeThread(pth_sess.hThread, &exitCode); - if (exitCode == STILL_ACTIVE) - return TRUE; -#ifdef DEBUGMODE - netlog("gg_refreshstatus(): Going to connect..."); -#endif - threadwait(&pth_sess); - pth_sess.hThread = forkthreadex(&GGPROTO::mainthread, NULL, &pth_sess.dwThreadId); - } - else - { - char *szMsg = NULL; - // Select proper msg - EnterCriticalSection(&modemsg_mutex); - szMsg = mir_strdup(getstatusmsg(status)); - LeaveCriticalSection(&modemsg_mutex); - if (szMsg) - { - netlog("gg_refreshstatus(): Setting status and away message."); - EnterCriticalSection(&sess_mutex); - gg_change_status_descr(sess, status_m2gg(status, szMsg != NULL), szMsg); - LeaveCriticalSection(&sess_mutex); - } - else - { - netlog("gg_refreshstatus(): Setting just status."); - EnterCriticalSection(&sess_mutex); - gg_change_status(sess, status_m2gg(status, 0)); - LeaveCriticalSection(&sess_mutex); - } - // Change status of the contact with our own UIN (if got yourself added to the contact list) - changecontactstatus( db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0), status_m2gg(status, szMsg != NULL), szMsg, 0, 0, 0, 0); - broadcastnewstatus(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; - } - return ID_STATUS_AWAY; -} - -////////////////////////////////////////////////////////// -// gets avatar capabilities - -INT_PTR GGPROTO::getavatarcaps(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 db_get_b(NULL, m_szModuleName, 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 GGPROTO::getavatarinfo(WPARAM wParam, LPARAM lParam) -{ - PROTO_AVATAR_INFORMATIONT *pai = (PROTO_AVATAR_INFORMATIONT *)lParam; - char *AvatarHash = NULL, *AvatarSavedHash = NULL; - char *AvatarURL = NULL; - INT_PTR result = GAIR_NOAVATAR; - DBVARIANT dbv; - uin_t uin = (uin_t)db_get_dw(pai->hContact, m_szModuleName, GG_KEY_UIN, 0); - - netlog("gg_getavatarinfo(): Requesting avatar information for %d.", uin); - - pai->filename[0] = 0; - pai->format = PA_FORMAT_UNKNOWN; - - if (!uin || !db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) - return GAIR_NOAVATAR; - - if (!db_get_b(pai->hContact, m_szModuleName, GG_KEY_AVATARREQUESTED, GG_KEYDEF_AVATARREQUESTED)) { - requestAvatar(pai->hContact, 1); - return (wParam & GAIF_FORCE) != 0 ? GAIR_WAITFOR : GAIR_NOAVATAR; - } - db_unset(pai->hContact, m_szModuleName, GG_KEY_AVATARREQUESTED); - - pai->format = db_get_b(pai->hContact, m_szModuleName, GG_KEY_AVATARTYPE, GG_KEYDEF_AVATARTYPE); - - if (!db_get_s(pai->hContact, m_szModuleName, GG_KEY_AVATARURL, &dbv, DBVT_ASCIIZ)) { - AvatarURL = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - - if (AvatarURL != NULL && strlen(AvatarURL) > 0) { - char *AvatarName = strrchr(AvatarURL, '/'); - AvatarName++; - AvatarHash = gg_avatarhash(AvatarName); - } - - if (!db_get_s(pai->hContact, m_szModuleName, GG_KEY_AVATARHASH, &dbv, DBVT_ASCIIZ)) { - AvatarSavedHash = mir_strdup(dbv.pszVal); - DBFreeVariant(&dbv); - } - - if (AvatarHash != NULL && AvatarSavedHash != NULL) { - getAvatarFilename(pai->hContact, pai->filename, SIZEOF(pai->filename)); - if (!strcmp(AvatarHash, AvatarSavedHash) && !_taccess(pai->filename, 0)) { - result = GAIR_SUCCESS; - } - else if ((wParam & GAIF_FORCE) != 0) { - netlog("gg_getavatarinfo(): Contact %d changed avatar.", uin); - _tremove(pai->filename); - db_set_s(pai->hContact, m_szModuleName, GG_KEY_AVATARHASH, AvatarHash); - getAvatar(pai->hContact, AvatarURL); - result = GAIR_WAITFOR; - } - } - else if ((wParam & GAIF_FORCE) != 0) { - if (AvatarHash == NULL && AvatarSavedHash != NULL) { - netlog("gg_getavatarinfo(): Contact %d deleted avatar.", uin); - getAvatarFilename(pai->hContact, pai->filename, sizeof(pai->filename)); - _tremove(pai->filename); - db_unset(pai->hContact, m_szModuleName, GG_KEY_AVATARHASH); - db_unset(pai->hContact, m_szModuleName, GG_KEY_AVATARURL); - db_unset(pai->hContact, m_szModuleName, GG_KEY_AVATARTYPE); - } - else if (AvatarHash != NULL && AvatarSavedHash == NULL) { - netlog("gg_getavatarinfo(): Contact %d set avatar.", uin); - db_set_s(pai->hContact, m_szModuleName, GG_KEY_AVATARHASH, AvatarHash); - getAvatar(pai->hContact, AvatarURL); - result = GAIR_WAITFOR; - } - } - - mir_free(AvatarHash); - mir_free(AvatarSavedHash); - mir_free(AvatarURL); - - return result; -} - -////////////////////////////////////////////////////////// -// gets avatar - -INT_PTR GGPROTO::getmyavatar(WPARAM wParam, LPARAM lParam) -{ - TCHAR *szFilename = (TCHAR*)wParam; - int len = (int)lParam; - - netlog("gg_getmyavatar(): Requesting user avatar."); - - if (szFilename == NULL || len <= 0) - return -1; - - if (!db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) - return -2; - - getAvatarFilename(NULL, szFilename, len); - return _taccess(szFilename, 0); -} - -////////////////////////////////////////////////////////// -// sets avatar - -INT_PTR GGPROTO::setmyavatar(WPARAM wParam, LPARAM lParam) -{ - TCHAR *szFilename = (TCHAR*)lParam; - - if (!db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) - return -2; - - if (szFilename == NULL) { - MessageBox(NULL, - TranslateT("To remove your Gadu-Gadu avatar, you must use the MojaGeneracja.pl website."), - m_tszUserName, MB_OK | MB_ICONINFORMATION); - return -1; - } - - TCHAR szMyFilename[MAX_PATH]; - getAvatarFilename(NULL, szMyFilename, SIZEOF(szMyFilename)); - if ( _tcscmp(szFilename, szMyFilename) && !CopyFile(szFilename, szMyFilename, FALSE)) { - netlog("gg_setmyavatar(): Failed to set user avatar. File %s could not be created/overwritten.", szMyFilename); - return -1; - } - - setAvatar(szMyFilename); - return 0; -} - -////////////////////////////////////////////////////////// -// gets protocol status message - -INT_PTR GGPROTO::getmyawaymsg(WPARAM wParam, LPARAM lParam) -{ - INT_PTR res = 0; - char *szMsg; - - EnterCriticalSection(&modemsg_mutex); - szMsg = getstatusmsg(wParam ? gg_normalizestatus(wParam) : m_iStatus); - if (isonline() && szMsg) - res = (lParam & SGMA_UNICODE) ? (INT_PTR)mir_a2u(szMsg) : (INT_PTR)mir_strdup(szMsg); - LeaveCriticalSection(&modemsg_mutex); - return res; -} - -////////////////////////////////////////////////////////// -// gets account manager GUI - -extern INT_PTR CALLBACK gg_acc_mgr_guidlgproc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); - -INT_PTR GGPROTO::get_acc_mgr_gui(WPARAM wParam, LPARAM lParam) -{ - return (INT_PTR) CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_ACCMGRUI), (HWND)lParam, gg_acc_mgr_guidlgproc, (LPARAM)this); -} - -////////////////////////////////////////////////////////// -// leaves (terminates) conference - -INT_PTR GGPROTO::leavechat(WPARAM wParam, LPARAM lParam) -{ - HANDLE hContact = (HANDLE)wParam; - if (hContact) - CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0); - - return 0; -} diff --git a/protocols/Gadu-Gadu/sessions.cpp b/protocols/Gadu-Gadu/sessions.cpp deleted file mode 100644 index 6f047cd783..0000000000 --- a/protocols/Gadu-Gadu/sessions.cpp +++ /dev/null @@ -1,445 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// 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: - TranslateDialogDefault(hwndDlg); - - gg = (GGPROTO*)lParam; - gg->hwndSessionsDlg = hwndDlg; - - SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)lParam); - { - TCHAR oldTitle[256], newTitle[256]; - HANDLE hProtoAckEvent; - - GetDlgItemText(hwndDlg, IDC_HEADERBAR, oldTitle, SIZEOF(oldTitle)); - mir_sntprintf(newTitle, SIZEOF(newTitle), oldTitle, gg->m_tszUserName); - SetDlgItemText(hwndDlg, IDC_HEADERBAR, newTitle); - WindowSetIcon(hwndDlg, "sessions"); - - 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->m_szModuleName) && !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() - ? 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: - { - TCHAR 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_sntprintf(szText, SIZEOF(szText), _T("%s\t%s\t%s"), szClientName, szIP, szLoginTime); - if ((hData = GlobalAlloc(GMEM_MOVEABLE, lstrlen(szText) + 1)) != NULL) - { - lstrcpy((TCHAR*)GlobalLock(hData), szText); - GlobalUnlock(hData); - SetClipboardData(CF_TEXT, hData); - } - CloseClipboard(); - break; - } - - case 10002: - { - TCHAR szUrl[256], szIP[64]; - szIP[0] = 0; - ListView_GetItemText(hList, lvhti.iItem, 1, szIP, SIZEOF(szIP)); - mir_sntprintf(szUrl, SIZEOF(szUrl), _T("http://whois.domaintools.com/%s"), szIP); - CallService(MS_UTILS_OPENURL, OUF_TCHAR, (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 GGPROTO::sessions_view(WPARAM wParam, LPARAM lParam) -{ - if (hwndSessionsDlg && IsWindow(hwndSessionsDlg)) { - ShowWindow(hwndSessionsDlg, SW_SHOWNORMAL); - SetForegroundWindow(hwndSessionsDlg); - SetFocus(hwndSessionsDlg); - } - else CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_SESSIONS), NULL, gg_sessions_viewdlg, (LPARAM)this); - return 0; -} - -void GGPROTO::sessions_updatedlg() -{ - if (hwndSessionsDlg && IsWindow(hwndSessionsDlg)) - SendMessage(hwndSessionsDlg, WM_MULTILOGONINFO, 0, 0); -} - -BOOL GGPROTO::sessions_closedlg() -{ - if (hwndSessionsDlg && IsWindow(hwndSessionsDlg)) - return PostMessage(hwndSessionsDlg, WM_CLOSE, 0, 0); - return FALSE; -} - -void GGPROTO::sessions_menus_init(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, m_szModuleName); - createObjService(service, &GGPROTO::sessions_view); - if (hMenuRoot) - mi.position = 2050000001; - else - mi.position = 200003; - mi.icolibItem = GetIconHandle(IDI_SESSIONS); - mi.ptszName = LPGENT("Concurrent &sessions"); - mi.pszService = service; - Menu_AddProtoMenuItem(&mi); -} diff --git a/protocols/Gadu-Gadu/src/avatar.cpp b/protocols/Gadu-Gadu/src/avatar.cpp new file mode 100644 index 0000000000..ace7871b6a --- /dev/null +++ b/protocols/Gadu-Gadu/src/avatar.cpp @@ -0,0 +1,459 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 +#include +#include "protocol.h" + +////////////////////////////////////////////////////////// +// Avatars support +void GGPROTO::getAvatarFilename(HANDLE hContact, TCHAR *pszDest, int cbLen) +{ + int tPathLen; + TCHAR *path = (TCHAR*)alloca(cbLen * sizeof(TCHAR)); + TCHAR *avatartype = NULL; + + if (hAvatarsFolder == NULL || FoldersGetCustomPathT(hAvatarsFolder, path, cbLen, _T(""))) { + mir_ptr tmpPath( Utils_ReplaceVarsT( _T("%miranda_avatarcache%"))); + tPathLen = mir_sntprintf(pszDest, cbLen, _T("%s\\%s"), (TCHAR*)tmpPath, m_tszUserName); + } + else { + _tcscpy(pszDest, path); + tPathLen = (int)_tcslen(pszDest); + } + + if (_taccess(pszDest, 0)) + CallService(MS_UTILS_CREATEDIRTREE, 0, (LPARAM)pszDest); + + switch (db_get_b(hContact, m_szModuleName, GG_KEY_AVATARTYPE, GG_KEYDEF_AVATARTYPE)) { + case PA_FORMAT_JPEG: avatartype = _T("jpg"); break; + case PA_FORMAT_GIF: avatartype = _T("gif"); break; + case PA_FORMAT_PNG: avatartype = _T("png"); break; + } + + if (hContact != NULL) { + DBVARIANT dbv; + if (!db_get_s(hContact, m_szModuleName, GG_KEY_AVATARHASH, &dbv, DBVT_ASCIIZ)) { + mir_sntprintf(pszDest + tPathLen, cbLen - tPathLen, _T("\\%s.%s"), dbv.pszVal, avatartype); + DBFreeVariant(&dbv); + } + } + else mir_sntprintf(pszDest + tPathLen, cbLen - tPathLen, _T("\\%s avatar.%s"), m_szModuleName, avatartype); +} + +void GGPROTO::getAvatarFileInfo(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)netlib, (LPARAM)&req); + if (resp) { + if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { + HXML hXml; + TCHAR *xmlAction; + TCHAR *tag; + + xmlAction = mir_a2t(resp->pData); + tag = mir_a2t("result"); + hXml = xi.parseString(xmlAction, 0, tag); + + if (hXml != NULL) { + HXML node; + char *blank; + + mir_free(tag); tag = mir_a2t("users/user/avatars/avatar"); + node = xi.getChildByPath(hXml, tag, 0); + mir_free(tag); tag = mir_a2t("blank"); + blank = (node != NULL) ? mir_t2a(xi.getAttrValue(node, tag)) : NULL; + + if (blank != NULL && strcmp(blank, "1")) { + mir_free(tag); tag = mir_a2t("users/user/avatars/avatar/bigAvatar"); + node = xi.getChildByPath(hXml, tag, 0); + *avatarurl = node != NULL ? mir_t2a(xi.getText(node)) : NULL; + + mir_free(tag); tag = mir_a2t("users/user/avatars/avatar/originBigAvatar"); + node = xi.getChildByPath(hXml, tag, 0); + if (node != NULL) { + char *orgavurl = mir_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 netlog("gg_getavatarfileinfo(): Invalid response code from HTTP request"); + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); + } + else netlog("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((BYTE*)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 GGPROTO::getAvatar(HANDLE hContact, char *szAvatarURL) +{ + if (pth_avatar.dwThreadId) { + GGGETAVATARDATA *data = (GGGETAVATARDATA*)mir_alloc(sizeof(GGGETAVATARDATA)); + data->hContact = hContact; + data->AvatarURL = mir_strdup(szAvatarURL); + EnterCriticalSection(&avatar_mutex); + list_add(&avatar_transfers, data, 0); + LeaveCriticalSection(&avatar_mutex); + } +} + +typedef struct +{ + HANDLE hContact; + int iWaitFor; +} GGREQUESTAVATARDATA; + +void GGPROTO::requestAvatar(HANDLE hContact, int iWaitFor) +{ + if (db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS) + && pth_avatar.dwThreadId) { + GGREQUESTAVATARDATA *data = (GGREQUESTAVATARDATA*)mir_alloc(sizeof(GGREQUESTAVATARDATA)); + data->hContact = hContact; + data->iWaitFor = iWaitFor; + EnterCriticalSection(&avatar_mutex); + list_add(&avatar_requests, data, 0); + LeaveCriticalSection(&avatar_mutex); + } +} + +void __cdecl GGPROTO::avatarrequestthread(void*) +{ + list_t l; + + netlog("gg_avatarrequestthread(): Avatar Request Thread Starting"); + while (pth_avatar.dwThreadId) + { + EnterCriticalSection(&avatar_mutex); + if (avatar_requests) { + GGREQUESTAVATARDATA *data = (GGREQUESTAVATARDATA *)avatar_requests->data; + char *AvatarURL; + int AvatarType, iWaitFor = data->iWaitFor; + HANDLE hContact = data->hContact; + + list_remove(&avatar_requests, data, 0); + mir_free(data); + LeaveCriticalSection(&avatar_mutex); + + getAvatarFileInfo( db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0), &AvatarURL, &AvatarType); + if (AvatarURL != NULL && strlen(AvatarURL) > 0) + db_set_s(hContact, m_szModuleName, GG_KEY_AVATARURL, AvatarURL); + else + db_unset(hContact, m_szModuleName, GG_KEY_AVATARURL); + db_set_b(hContact, m_szModuleName, GG_KEY_AVATARTYPE, (BYTE)AvatarType); + db_set_b(hContact, m_szModuleName, GG_KEY_AVATARREQUESTED, 1); + + if (iWaitFor) { + PROTO_AVATAR_INFORMATIONT pai = {0}; + pai.cbSize = sizeof(pai); + pai.hContact = hContact; + if (getavatarinfo((WPARAM)GAIF_FORCE, (LPARAM)&pai) != GAIR_WAITFOR) + ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, (HANDLE)&pai, 0); + } + else ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, 0, 0); + } + else LeaveCriticalSection(&avatar_mutex); + + EnterCriticalSection(&avatar_mutex); + if (avatar_transfers) { + GGGETAVATARDATA *data = (GGGETAVATARDATA *)avatar_transfers->data; + NETLIBHTTPREQUEST req = {0}; + NETLIBHTTPREQUEST *resp; + PROTO_AVATAR_INFORMATIONT pai = {0}; + int result = 0; + + pai.cbSize = sizeof(pai); + pai.hContact = data->hContact; + pai.format = db_get_b(pai.hContact, m_szModuleName, 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)netlib, (LPARAM)&req); + if (resp) { + if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { + int file_fd; + + getAvatarFilename(pai.hContact, pai.filename, sizeof(pai.filename)); + file_fd = _topen(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 netlog("gg_avatarrequestthread(): Invalid response code from HTTP request"); + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); + } + else netlog("gg_avatarrequestthread(): No response from HTTP request"); + + ProtoBroadcastAck(m_szModuleName, pai.hContact, ACKTYPE_AVATAR, + result ? ACKRESULT_SUCCESS : ACKRESULT_FAILED, (HANDLE)&pai, 0); + + if (!pai.hContact) + CallService(MS_AV_REPORTMYAVATARCHANGED, (WPARAM)m_szModuleName, 0); + + list_remove(&avatar_transfers, data, 0); + mir_free(data->AvatarURL); + mir_free(data); + } + LeaveCriticalSection(&avatar_mutex); + SleepEx(100, FALSE); + } + + for (l = avatar_requests; l; l = l->next) { + GGREQUESTAVATARDATA *data = (GGREQUESTAVATARDATA *)l->data; + mir_free(data); + } + for (l = avatar_transfers; l; l = l->next) { + GGGETAVATARDATA *data = (GGGETAVATARDATA *)l->data; + mir_free(data->AvatarURL); + mir_free(data); + } + list_destroy(avatar_requests, 0); + list_destroy(avatar_transfers, 0); + netlog("gg_avatarrequestthread(): Avatar Request Thread Ending"); +} + +void GGPROTO::initavatarrequestthread() +{ + DWORD exitCode = 0; + + GetExitCodeThread(pth_avatar.hThread, &exitCode); + if (exitCode != STILL_ACTIVE) { + avatar_requests = avatar_transfers = NULL; + pth_avatar.hThread = forkthreadex(&GGPROTO::avatarrequestthread, NULL, &pth_avatar.dwThreadId); + } +} + +void GGPROTO::uninitavatarrequestthread() +{ + pth_avatar.dwThreadId = 0; +#ifdef DEBUGMODE + netlog("gg_uninitavatarrequestthread(): Waiting until Avatar Request Thread finished, if needed."); +#endif + threadwait(&pth_avatar); +} + +void __cdecl GGPROTO::getuseravatarthread(void*) +{ + char *AvatarURL; + int AvatarType; + + getAvatarFileInfo( db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0), &AvatarURL, &AvatarType); + if (AvatarURL != NULL && strlen(AvatarURL) > 0) + db_set_s(NULL, m_szModuleName, GG_KEY_AVATARURL, AvatarURL); + else + db_unset(NULL, m_szModuleName, GG_KEY_AVATARURL); + db_set_b(NULL, m_szModuleName, GG_KEY_AVATARTYPE, (BYTE)AvatarType); + db_set_b(NULL, m_szModuleName, GG_KEY_AVATARREQUESTED, 1); + mir_free(AvatarURL); + + PROTO_AVATAR_INFORMATIONT pai = {0}; + pai.cbSize = sizeof(pai); + getavatarinfo((WPARAM)GAIF_FORCE, (LPARAM)&pai); +} + +void GGPROTO::getUserAvatar() +{ + if (db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS) + && db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0)) + forkthread(&GGPROTO::getuseravatarthread, NULL); +} + +void __cdecl GGPROTO::setavatarthread(void *param) +{ + NETLIBHTTPHEADER httpHeaders[4]; + NETLIBHTTPREQUEST req = {0}; + NETLIBHTTPREQUEST *resp; + TCHAR *szFilename = (TCHAR*)param; + const char *contentend = "\r\n--AaB03x--\r\n"; + char szUrl[128], uin[32], *authHeader, *data, *avatardata, content[256], image_ext[4], image_type[11]; + int file_fd, avatardatalen, datalen, contentlen, contentendlen, res = 0, repeat = 0; + + netlog("gg_setavatar(): Trying to set user avatar using %s...", szFilename); + UIN2ID( db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0), uin); + + file_fd = _topen(szFilename, _O_RDONLY | _O_BINARY, _S_IREAD); + if (file_fd == -1) { + netlog("gg_setavatar(): Failed to open avatar file (%s).", strerror(errno)); + mir_free(szFilename); + getUserAvatar(); + return; + } + avatardatalen = _filelength(file_fd); + avatardata = (char *)mir_alloc(avatardatalen); + + _read(file_fd, avatardata, avatardatalen); + _close(file_fd); + + TCHAR *fileext = _tcsrchr(szFilename, '.'); + fileext++; + if (!_tcsicmp(fileext, _T("jpg"))) { + strcpy(image_ext, "jpg"); + strcpy(image_type, "image/jpeg"); + } + else if (!_tcsicmp(fileext, _T("gif"))) { + strcpy(image_ext, "gif"); + strcpy(image_type, "image/gif"); + } + else { + 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); + oauth_checktoken(0); + authHeader = oauth_header("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)netlib, (LPARAM)&req); + if (resp) { + if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { +#ifdef DEBUGMODE + netlog("%s", resp->pData); +#endif + res = 1; + } + else netlog("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 netlog("gg_setavatar(): No response from HTTP request"); + + if (repeat) { // Access Token expired - we need to obtain new + mir_free(authHeader); + oauth_checktoken(1); + authHeader = oauth_header("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)netlib, (LPARAM)&req); + if (resp) { + if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { +#ifdef DEBUGMODE + netlog("%s", resp->pData); +#endif + res = 1; + } + else netlog("gg_setavatar(): Invalid response code from HTTP request"); + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); + } + else netlog("gg_setavatar(): No response from HTTP request"); + } + + mir_free(authHeader); + mir_free(avatardata); + mir_free(data); + + if (res) + netlog("gg_setavatar(): User avatar set successfully."); + else + netlog("gg_setavatar(): Failed to set user avatar."); + + mir_free(szFilename); + getUserAvatar(); +} + +void GGPROTO::setAvatar(const TCHAR *szFilename) +{ + forkthread(&GGPROTO::setavatarthread, mir_tstrdup(szFilename)); +} diff --git a/protocols/Gadu-Gadu/src/core.cpp b/protocols/Gadu-Gadu/src/core.cpp new file mode 100644 index 0000000000..63d5d37b24 --- /dev/null +++ b/protocols/Gadu-Gadu/src/core.cpp @@ -0,0 +1,1756 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2009 Adam Strzelecki +// 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 +#include + +//////////////////////////////////////////////////////////// +// 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 + +int GGPROTO::isonline() +{ + mir_cslock lck(sess_mutex); + return (sess != NULL); +} + +//////////////////////////////////////////////////////////// +// Send disconnect request and wait for server thread to die +void GGPROTO::disconnect() +{ + // If main loop then send disconnect request + if (isonline()) + { + // Fetch proper status msg + char *szMsg = NULL; + + // Loadup status + if (db_get_b(NULL, m_szModuleName, GG_KEY_LEAVESTATUSMSG, GG_KEYDEF_LEAVESTATUSMSG)) + { + DBVARIANT dbv; + switch (db_get_w(NULL, m_szModuleName, GG_KEY_LEAVESTATUS, GG_KEYDEF_LEAVESTATUS)) { + case ID_STATUS_ONLINE: + EnterCriticalSection(&modemsg_mutex); + szMsg = mir_strdup(modemsg.online); + LeaveCriticalSection(&modemsg_mutex); + if (!szMsg && !db_get_s(NULL, "SRAway", gg_status2db(ID_STATUS_ONLINE, "Default"), &dbv, DBVT_ASCIIZ)) { + if (dbv.pszVal && *(dbv.pszVal)) + szMsg = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + break; + case ID_STATUS_AWAY: + EnterCriticalSection(&modemsg_mutex); + szMsg = mir_strdup(modemsg.away); + LeaveCriticalSection(&modemsg_mutex); + if (!szMsg && !db_get_s(NULL, "SRAway", gg_status2db(ID_STATUS_AWAY, "Default"), &dbv, DBVT_ASCIIZ)) { + if (dbv.pszVal && *(dbv.pszVal)) + szMsg = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + break; + case ID_STATUS_DND: + EnterCriticalSection(&modemsg_mutex); + szMsg = mir_strdup(modemsg.dnd); + LeaveCriticalSection(&modemsg_mutex); + if (!szMsg && !db_get_s(NULL, "SRAway", gg_status2db(ID_STATUS_DND, "Default"), &dbv, DBVT_ASCIIZ)) { + if (dbv.pszVal && *(dbv.pszVal)) + szMsg = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + break; + case ID_STATUS_FREECHAT: + EnterCriticalSection(&modemsg_mutex); + szMsg = mir_strdup(modemsg.freechat); + LeaveCriticalSection(&modemsg_mutex); + if (!szMsg && !db_get_s(NULL, "SRAway", gg_status2db(ID_STATUS_FREECHAT, "Default"), &dbv, DBVT_ASCIIZ)) { + if (dbv.pszVal && *(dbv.pszVal)) + szMsg = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + break; + case ID_STATUS_INVISIBLE: + EnterCriticalSection(&modemsg_mutex); + szMsg = mir_strdup(modemsg.invisible); + LeaveCriticalSection(&modemsg_mutex); + if (!szMsg && !db_get_s(NULL, "SRAway", gg_status2db(ID_STATUS_INVISIBLE, "Default"), &dbv, DBVT_ASCIIZ)) { + if (dbv.pszVal && *(dbv.pszVal)) + szMsg = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + break; + default: + // Set last status + EnterCriticalSection(&modemsg_mutex); + szMsg = mir_strdup(getstatusmsg(m_iStatus)); + LeaveCriticalSection(&modemsg_mutex); + } + } + + EnterCriticalSection(&sess_mutex); + // Check if it has message + if (szMsg) + { + gg_change_status_descr(sess, GG_STATUS_NOT_AVAIL_DESCR, szMsg); + mir_free(szMsg); + // Wait for disconnection acknowledge + } + else + { + gg_change_status(sess, GG_STATUS_NOT_AVAIL); + // Send logoff immediately + gg_logoff(sess); + } + LeaveCriticalSection(&sess_mutex); + } + // Else cancel connection attempt + else if (sock) + closesocket(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_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_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_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 GGPROTO::mainthread(void *) +{ + // Miranda variables + NETLIBUSERSETTINGS nlus = {0}; + DBVARIANT dbv; + // Gadu-Gadu variables + gg_login_params p = {0}; + gg_event *e; + // Host cycling variables + int hostnum = 0, hostcount = 0; + GGHOST hosts[64]; + // Gadu-gadu login errors + static const struct tagReason { int type; TCHAR *str; } reason[] = { + { GG_FAILURE_RESOLVING, LPGENT("Miranda was unable to resolve the name of the Gadu-Gadu server to its numeric address.") }, + { GG_FAILURE_CONNECTING, LPGENT("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, LPGENT("Received invalid server response.") }, + { GG_FAILURE_READING, LPGENT("The connection with the server was abortively closed during the connection attempt. You may have lost your local network connection.") }, + { GG_FAILURE_WRITING, LPGENT("The connection with the server was abortively closed during the connection attempt. You may have lost your local network connection.") }, + { GG_FAILURE_PASSWORD, LPGENT("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, LPGENT("Connecting to Gadu-Gadu hub failed.") }, + { GG_FAILURE_TLS, LPGENT("Cannot establish secure connection.") }, + { GG_FAILURE_NEED_EMAIL, LPGENT("Server disconnected asking you for changing your e-mail.") }, + { GG_FAILURE_INTRUDER, LPGENT("Too many login attempts with invalid password.") }, + { GG_FAILURE_UNAVAILABLE, LPGENT("Gadu-Gadu servers are now down. Try again later.") }, + { 0, LPGENT("Unknown") } + }; + time_t logonTime = 0; + time_t timeDeviation = db_get_w(NULL, m_szModuleName, GG_KEY_TIMEDEVIATION, GG_KEYDEF_TIMEDEVIATION); + int gg_failno = 0; + + netlog("gg_mainthread(%x): Server Thread Starting", this); +#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 + broadcastnewstatus(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 (db_get_b(NULL, m_szModuleName, 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 = db_get_b(NULL, m_szModuleName, "EraOmnix", 0); + + // Setup proxy + nlus.cbSize = sizeof(nlus); + if (CallService(MS_NETLIB_GETUSERSETTINGS, (WPARAM)netlib, (LPARAM)&nlus)) + { + if (nlus.useProxy) + netlog("gg_mainthread(%x): Using proxy %s:%d.", this, 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 + { + netlog("gg_mainthread(%x): Failed loading proxy settings.", this); + gg_proxy_enabled = 0; + } + + // Check out manual host setting + if (db_get_b(NULL, m_szModuleName, GG_KEY_MANUALHOST, GG_KEYDEF_MANUALHOST)) + { + if (!db_get_s(NULL, m_szModuleName, GG_KEY_SERVERHOSTS, &dbv, DBVT_ASCIIZ)) + { + hostcount = gg_decodehosts(dbv.pszVal, hosts, 64); + DBFreeVariant(&dbv); + } + } + + // Readup password + if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) + { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); + p.password = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + else + { + netlog("gg_mainthread(%x): No password specified. Exiting.", this); + broadcastnewstatus(ID_STATUS_OFFLINE); + return; + } + + // Readup number + if (!(p.uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0))) + { + netlog("gg_mainthread(%x): No Gadu-Gadu number specified. Exiting.", this); + broadcastnewstatus(ID_STATUS_OFFLINE); + mir_free(p.password); + return; + } + + // Readup SSL/TLS setting + if (p.tls = db_get_b(NULL, m_szModuleName, GG_KEY_SSLCONN, GG_KEYDEF_SSLCONN)) + netlog("gg_mainthread(%x): Using TLS/SSL for connections.", this); + + // Gadu-Gadu accepts image sizes upto 255 + p.image_size = 255; + + ////////////////////////////// DCC STARTUP ///////////////////////////// + // Uin is ok so startup dcc if not started already + if (!dcc) + { + hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + dccstart(); + + // Wait for DCC +#ifdef DEBUGMODE + netlog("gg_mainthread(%x): Waiting DCC service to start...", this); +#endif + while (WaitForSingleObjectEx(hEvent, INFINITE, TRUE) != WAIT_OBJECT_0); + CloseHandle(hEvent); hEvent = NULL; + } + // Check if dcc is running and setup forwarding port + if (dcc && db_get_b(NULL, m_szModuleName, GG_KEY_FORWARDING, GG_KEYDEF_FORWARDING)) + { + if (!db_get_s(NULL, m_szModuleName, GG_KEY_FORWARDHOST, &dbv, DBVT_ASCIIZ)) + { + if (!(p.external_addr = gg_dnslookup(this, dbv.pszVal))) + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("External direct connections hostname %S is invalid. Disabling external host forwarding."), dbv.pszVal); + showpopup(m_tszUserName, error, GG_POPUP_WARNING | GG_POPUP_ALLOW_MSGBOX); + } + else + netlog("gg_mainthread(%x): Loading forwarding host %s and port %d.", dbv.pszVal, p.external_port, this); + if (p.external_addr) p.external_port = db_get_w(NULL, m_szModuleName, GG_KEY_FORWARDPORT, GG_KEYDEF_FORWARDPORT); + DBFreeVariant(&dbv); + } + } + // Setup client port + if (dcc) p.client_port = dcc->port; + +retry: + // Loadup startup status & description + EnterCriticalSection(&modemsg_mutex); + p.status_descr = mir_strdup(getstatusmsg(m_iDesiredStatus)); + p.status = status_m2gg(m_iDesiredStatus, p.status_descr != NULL); + + netlog("gg_mainthread(%x): Connecting with number %d, status %d and description \"%s\".", this, p.uin, m_iDesiredStatus, + p.status_descr ? p.status_descr : ""); + LeaveCriticalSection(&modemsg_mutex); + + // Check manual hosts + if (hostnum < hostcount) + { + if (!(p.server_addr = gg_dnslookup(this, hosts[hostnum].hostname))) + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("Server hostname %S is invalid. Using default hostname provided by the network."), hosts[hostnum].hostname); + showpopup(m_tszUserName, error, GG_POPUP_WARNING | GG_POPUP_ALLOW_MSGBOX); + } + else + { + p.server_port = hosts[hostnum].port; + netlog("gg_mainthread(%x): Connecting to manually specified host %s (%d.%d.%d.%d) and port %d.", this, + 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, &sock, &gg_failno))) + { + broadcastnewstatus(ID_STATUS_OFFLINE); + // Check if connection attempt wasn't cancelled by the user + if (m_iDesiredStatus != ID_STATUS_OFFLINE) + { + TCHAR error[128], *perror = NULL; + // Lookup for error desciption + if (errno == EACCES) { + for (int i = 0; reason[i].type; i++) if (reason[i].type == gg_failno) { + perror = TranslateTS(reason[i].str); + break; + } + } + if (!perror) { + mir_sntprintf(error, SIZEOF(error), TranslateT("Connection cannot be established because of error:\n\t%s"), _tcserror(errno)); + perror = error; + } + netlog("gg_mainthread(%x): %s", this, perror); + if (db_get_b(NULL, m_szModuleName, GG_KEY_SHOWCERRORS, GG_KEYDEF_SHOWCERRORS)) + showpopup(m_tszUserName, 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 + && (db_get_b(NULL, m_szModuleName, GG_KEY_ARECONNECT, GG_KEYDEF_ARECONNECT) || (hostnum < hostcount - 1))) + { + DWORD dwInterval = db_get_dw(NULL, m_szModuleName, GG_KEY_RECONNINTERVAL, GG_KEYDEF_RECONNINTERVAL), dwResult; + BOOL bRetry = TRUE; + + hConnStopEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + dwResult = WaitForSingleObjectEx(hConnStopEvent, dwInterval, TRUE); + if ((dwResult == WAIT_OBJECT_0 && m_iDesiredStatus == ID_STATUS_OFFLINE) + || (dwResult == WAIT_IO_COMPLETION && Miranda_Terminated())) + bRetry = FALSE; + CloseHandle(hConnStopEvent); + hConnStopEvent = NULL; + + // Reconnect to the next server on the list + if (bRetry) + { + if (hostnum < hostcount - 1) hostnum++; + mir_free(p.status_descr); + broadcastnewstatus(ID_STATUS_CONNECTING); + goto retry; + } + } + // We cannot do more about this + EnterCriticalSection(&modemsg_mutex); + m_iDesiredStatus = ID_STATUS_OFFLINE; + LeaveCriticalSection(&modemsg_mutex); + } + else + netlog("gg_mainthread(%x)): Connection attempt cancelled by the user.", this); + } + else + { + // Successfully connected + logonTime = time(NULL); + db_set_dw(NULL, m_szModuleName, GG_KEY_LOGONTIME, logonTime); + EnterCriticalSection(&sess_mutex); + sess = sess; + LeaveCriticalSection(&sess_mutex); + // Subscribe users status notifications + notifyall(); + // Set startup status + if (m_iDesiredStatus != status_gg2m(p.status)) + refreshstatus(m_iDesiredStatus); + else + { + broadcastnewstatus(m_iDesiredStatus); + // Change status of the contact with our own UIN (if got yourself added to the contact list) + changecontactstatus(p.uin, p.status, p.status_descr, 0, 0, 0, 0); + } + if (check_first_conn) // First connection to the account + { + // Start search for user data + GetInfo(NULL, 0); + // Fetch user avatar + getUserAvatar(); + check_first_conn = 0; + } + } + + ////////////////////////////////////////////////////////////////////////////////// + // Main loop + while(isonline()) + { + // Connection broken/closed + if (!(e = gg_watch_fd(sess))) + { + netlog("gg_mainthread(%x): Connection closed.", this); + EnterCriticalSection(&sess_mutex); + gg_free_session(sess); + sess = NULL; + LeaveCriticalSection(&sess_mutex); + break; + } + else + netlog("gg_mainthread(%x): Event: %s", this, 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(&sess_mutex); + gg_free_session(sess); + sess = NULL; + LeaveCriticalSection(&sess_mutex); + break; + + // Client allowed to disconnect + case GG_EVENT_DISCONNECT_ACK: + // Send logoff + gg_logoff(sess); + break; + + // Received ackowledge + case GG_EVENT_ACK: + if (e->event.ack.seq && e->event.ack.recipient) + { + ProtoBroadcastAck(m_szModuleName, getcontact((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; + changecontactstatus(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)db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0); + int i; + for(i = 0; e->event.notify60[i].uin; i++) { + if (e->event.notify60[i].uin == uin) continue; + changecontactstatus(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); + requestAvatar(getcontact(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) + { + netlog("gg_mainthread(%x): Got user info.", this); + // Store next search UIN + if (res->seq == GG_SEQ_SEARCH) + next_uin = gg_pubdir50_next(res); + } + else if (e->type == GG_EVENT_PUBDIR50_READ) + { + netlog("gg_mainthread(%x): Got owner info.", this); + } + else if (e->type == GG_EVENT_PUBDIR50_WRITE) + { + netlog("gg_mainthread(%x): Public directory save succesful.", this); + // Update user details + GetInfo(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 : getcontact(uin, 0, 0, NULL); + netlog("gg_mainthread(%x): Search result for uin %d, seq %d.", this, uin, res->seq); + if (res->seq == GG_SEQ_SEARCH) + { + char strFmt1[64]; + char strFmt2[64]; + + mir_snprintf(strFmt2, sizeof(strFmt2), "%s", (char *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, status_gg2m(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)); + } + } + + GGSEARCHRESULT sr; + memset(&sr, 0, sizeof(sr)); + sr.cbSize = sizeof(sr); + sr.nick = mir_a2t(__nick); + sr.firstName = mir_a2t(__firstname); + sr.lastName = mir_a2t(__lastname); + sr.email = mir_a2t(strFmt2); + sr.id = mir_a2t(_ultoa(uin, strFmt1, 10)); + sr.uin = uin; + ProtoBroadcastAck(m_szModuleName, NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE) 1, (LPARAM)&sr); + mir_free(sr.nick); + mir_free(sr.firstName); + mir_free(sr.lastName); + mir_free(sr.email); + mir_free(sr.id); + } + + 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)) + db_set_s(hContact, m_szModuleName, GG_KEY_NICK, __nick); + + if (__nick) + db_set_s(hContact, m_szModuleName, "NickName", __nick); + else if (res->seq == GG_SEQ_CHINFO) + db_unset(NULL, m_szModuleName, "NickName"); + + // Change other info + if (__city) + db_set_s(hContact, m_szModuleName, "City", __city); + else if (res->seq == GG_SEQ_CHINFO) + db_unset(NULL, m_szModuleName, "City"); + + if (__firstname) + db_set_s(hContact, m_szModuleName, "FirstName", __firstname); + else if (res->seq == GG_SEQ_CHINFO) + db_unset(NULL, m_szModuleName, "FirstName"); + + if (__lastname) + db_set_s(hContact, m_szModuleName, "LastName", __lastname); + else if (res->seq == GG_SEQ_CHINFO) + db_unset(NULL, m_szModuleName, "LastName"); + + if (__familyname) + db_set_s(hContact, m_szModuleName, "FamilyName", __familyname); + else if (res->seq == GG_SEQ_CHINFO) + db_unset(NULL, m_szModuleName, "FamilyName"); + + if (__origincity) + db_set_s(hContact, m_szModuleName, "CityOrigin", __origincity); + else if (res->seq == GG_SEQ_CHINFO) + db_unset(NULL, m_szModuleName, "CityOrigin"); + + if (__birthyear) + { + time_t t = time(NULL); + struct tm *lt = localtime(&t); + int br = atoi(__birthyear); + if (br > 0) + { + db_set_w(hContact, m_szModuleName, "Age", (WORD)(lt->tm_year + 1900 - br)); + db_set_w(hContact, m_szModuleName, "BirthYear", (WORD)br); + } + } + else if (res->seq == GG_SEQ_CHINFO) + { + db_unset(NULL, m_szModuleName, "Age"); + db_unset(NULL, m_szModuleName, "BirthYear"); + } + + // Gadu-Gadu Male <-> Female + if (__gender) + { + if (res->seq == GG_SEQ_CHINFO) + db_set_b(hContact, m_szModuleName, "Gender", + (BYTE)(!strcmp(__gender, GG_PUBDIR50_GENDER_SET_MALE) ? 'M' : + (!strcmp(__gender, GG_PUBDIR50_GENDER_SET_FEMALE) ? 'F' : '?'))); + else + db_set_b(hContact, m_szModuleName, "Gender", + (BYTE)(!strcmp(__gender, GG_PUBDIR50_GENDER_MALE) ? 'M' : + (!strcmp(__gender, GG_PUBDIR50_GENDER_FEMALE) ? 'F' : '?'))); + } + else if (res->seq == GG_SEQ_CHINFO) + { + db_unset(NULL, m_szModuleName, "Gender"); + } + + netlog("gg_mainthread(%x): Setting user info for uin %d.", this, uin); + ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, (HANDLE) 1, 0); + } + } + } + if (res->seq == GG_SEQ_SEARCH) + ProtoBroadcastAck(m_szModuleName, NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE) 1, 0); + break; + } + + // Status (deprecated) + case GG_EVENT_STATUS: + changecontactstatus(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 = getcontact(e->event.status60.uin, 0, 0, NULL); + int oldstatus = db_get_w(hContact, m_szModuleName, GG_KEY_STATUS, (WORD)ID_STATUS_OFFLINE); + uin_t uin = (uin_t)db_get_dw(NULL, m_szModuleName, 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(e->event.status60.status); + CallProtoService(m_szModuleName, PS_SETAWAYMSG, iStatus, (LPARAM)e->event.status60.descr); + CallProtoService(m_szModuleName, PS_SETSTATUS, iStatus, 0); + } + + changecontactstatus(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 && db_get_w(hContact, m_szModuleName, GG_KEY_STATUS, (WORD)ID_STATUS_OFFLINE) != ID_STATUS_OFFLINE) + requestAvatar(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) { + parsecontacts(e->event.userlist.reply); + MessageBox(NULL, TranslateT("List import successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION); + } + break; + + case GG_USERLIST_PUT_REPLY: + if (is_list_remove) + MessageBox(NULL, TranslateT("List remove successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION); + else + MessageBox(NULL, TranslateT("List export successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION); + break; + } + break; + + // Received message + case GG_EVENT_MSG: + // This is CTCP request + if ((e->event.msg.msgclass & GG_CLASS_CTCP)) + { + dccconnect(e->event.msg.sender); + } + // Check if not conference and block + else if (!e->event.msg.recipients_count || gc_enabled) + { + // Check if groupchat + if (e->event.msg.recipients_count && gc_enabled && !db_get_b(NULL, m_szModuleName, GG_KEY_IGNORECONF, GG_KEYDEF_IGNORECONF)) + { + char *chat = gc_getchat(e->event.msg.sender, e->event.msg.recipients, e->event.msg.recipients_count); + if (chat) + { + char id[32]; + GCDEST gcdest = {m_szModuleName, 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) getcontact(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; + netlog("gg_mainthread(%x): Conference message to room %s & id %s.", this, 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 = getcontact(e->event.msg.sender, 1, 0, NULL); + ccs.lParam = (LPARAM)⪯ + 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 && + db_get_b(NULL, m_szModuleName, GG_KEY_IMGRECEIVE, GG_KEYDEF_IMGRECEIVE) && + !(db_get_dw(getcontact(e->event.msg.sender, 1, 0, NULL), "Ignore", "Mask1", 0) & IGNOREEVENT_MESSAGE)) + { + char *formats = (char*)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(&sess_mutex); + gg_image_request(sess, e->event.msg.sender, image->size, image->crc32); + LeaveCriticalSection(&sess_mutex); + + netlog("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 && gc_enabled && !db_get_b(NULL, m_szModuleName, GG_KEY_IGNORECONF, GG_KEYDEF_IGNORECONF)) + { + char *chat = gc_getchat(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 = {m_szModuleName, chat, GC_EVENT_MESSAGE}; + GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; + + UIN2ID( db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0), id); + + gcevent.pszUID = id; + gcevent.pszText = e->event.multilogon_msg.message; + if (!db_get_s(NULL, m_szModuleName, GG_KEY_NICK, &dbv, DBVT_ASCIIZ)) + gcevent.pszNick = dbv.pszVal; + else + gcevent.pszNick = Translate("Me"); + gcevent.time = e->event.multilogon_msg.time; + gcevent.bIsMe = 1; + gcevent.dwFlags = GCEF_ADDTOLOG; + netlog("gg_mainthread(%x): Sent conference message to room %s.", this, 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 = m_szModuleName; + 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)getcontact(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; + netlog("gg_mainthread(): Concurrent sessions count: %d.", e->event.multilogon_info.count); + if (e->event.multilogon_info.count > 0) + iIndexes = (int*)mir_calloc(e->event.multilogon_info.count * sizeof(int)); + EnterCriticalSection(&sessions_mutex); + for (l = 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(sessions, 0); + sessions = NULL; + for (i = 0; i < e->event.multilogon_info.count; i++) + { + gg_multilogon_session* sess = (gg_multilogon_session*)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(&sessions, sess, 0); + } + LeaveCriticalSection(&sessions_mutex); + sessions_updatedlg(); + if (ServiceExists(MS_POPUP_ADDPOPUPCLASS)) + { + const TCHAR* szText = time(NULL) - logonTime > 3 + ? TranslateT("You have logged in at another location") + : TranslateT("You are logged in at another location"); + for (i = 0; i < e->event.multilogon_info.count; i++) + { + TCHAR szMsg[MAX_SECONDLINE]; + if (iIndexes && iIndexes[i]) + continue; + + mir_sntprintf(szMsg, SIZEOF(szMsg), _T("%s (%s)"), szText, + *e->event.multilogon_info.sessions[i].name != '\0' ? + _A2T(e->event.multilogon_info.sessions[i].name) : TranslateT("Unknown client")); + showpopup(m_tszUserName, 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 = getcontact(e->event.image_reply.sender, 1, 0, NULL); + void *img = (void *)img_loadpicture(e, 0); + + if (!img) + break; + + if (db_get_b(NULL, m_szModuleName, GG_KEY_IMGMETHOD, GG_KEYDEF_IMGMETHOD) == 1 || img_opened(e->event.image_reply.sender)) + { + img_display(hContact, img); + } + else if (db_get_b(NULL, m_szModuleName, GG_KEY_IMGMETHOD, GG_KEYDEF_IMGMETHOD) == 2) + { + img_displayasmsg(hContact, img); + } + else + { + CLISTEVENT cle = {0}; + char service[128]; + mir_snprintf(service, sizeof(service), GGS_RECVIMAGE, m_szModuleName); + + 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: + img_sendonrequest(e); + break; + + // Incoming direct connection + case GG_EVENT_DCC7_NEW: + { + struct gg_dcc7 *dcc7 = e->event.dcc7_new; + netlog("gg_mainthread(%x): Incoming direct connection.", this); + dcc7->contact = getcontact(dcc7->peer_uin, 0, 0, NULL); + + // Check if user is on the list and if it is my uin + if (!dcc7->contact || db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, -1) != dcc7->uin) { + gg_dcc7_free(dcc7); + e->event.dcc7_new = NULL; + break; + } + + // Add to waiting transfers + EnterCriticalSection(&ft_mutex); + list_add(&transfers, dcc7, 0); + LeaveCriticalSection(&ft_mutex); + + ////////////////////////////////////////////////// + // Add file recv request + + netlog("gg_mainthread(%x): Client: %d, File ack filename \"%s\" size %d.", this, dcc7->peer_uin, + dcc7->filename, dcc7->size); + + TCHAR* filenameT = mir_utf8decodeT((char*)dcc7->filename); + + PROTORECVFILET pre = {0}; + pre.flags = PREF_TCHAR; + pre.fileCount = 1; + pre.timestamp = time(NULL); + pre.tszDescription = filenameT; + pre.ptszFiles = &filenameT; + pre.lParam = (LPARAM)dcc7; + + CCSDATA ccs = { dcc7->contact, PSR_FILE, 0, (LPARAM)&pre }; + CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); + + mir_free(filenameT); + 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) + { + netlog("gg_mainthread(%x): File transfer denied by client %d (reason = %d).", this, dcc7->peer_uin, e->event.dcc7_reject.reason); + ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DENIED, dcc7, 0); + + // Remove from watches and free + EnterCriticalSection(&ft_mutex); + list_remove(&watches, dcc7, 0); + LeaveCriticalSection(&ft_mutex); + gg_dcc7_free(dcc7); + } + else + { + netlog("gg_mainthread(%x): File transfer aborted by client %d.", this, dcc7->peer_uin); + + // Remove transfer from waiting list + EnterCriticalSection(&ft_mutex); + list_remove(&transfers, dcc7, 0); + LeaveCriticalSection(&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: + netlog("gg_mainthread(%x): Client: %d, Handshake error.", this, dcc7 ? dcc7->peer_uin : 0); + break; + case GG_ERROR_DCC7_NET: + netlog("gg_mainthread(%x): Client: %d, Network error.", this, dcc7 ? dcc7->peer_uin : 0); + break; + case GG_ERROR_DCC7_FILE: + netlog("gg_mainthread(%x): Client: %d, File read/write error.", this, dcc7 ? dcc7->peer_uin : 0); + break; + case GG_ERROR_DCC7_EOF: + netlog("gg_mainthread(%x): Client: %d, End of file/connection error.", this, dcc7 ? dcc7->peer_uin : 0); + break; + case GG_ERROR_DCC7_REFUSED: + netlog("gg_mainthread(%x): Client: %d, Connection refused error.", this, dcc7 ? dcc7->peer_uin : 0); + break; + case GG_ERROR_DCC7_RELAY: + netlog("gg_mainthread(%x): Client: %d, Relay connection error.", this, dcc7 ? dcc7->peer_uin : 0); + break; + default: + netlog("gg_mainthread(%x): Client: %d, Unknown error.", this, dcc7 ? dcc7->peer_uin : 0); + } + if (!dcc7) break; + + // Remove from watches + list_remove(&watches, dcc7, 0); + + // Close file & fail + if (dcc7->file_fd != -1) + { + _close(dcc7->file_fd); + dcc7->file_fd = -1; + } + + if (dcc7->contact) + ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0); + + // Free dcc + gg_dcc7_free(dcc7); + } + break; + + case GG_EVENT_XML_ACTION: + if (db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) { + HXML hXml; + TCHAR *xmlAction; + TCHAR *tag; + + xmlAction = mir_a2t(e->event.xml_action.data); + tag = mir_a2t("events"); + hXml = xi.parseString(xmlAction, 0, tag); + + if (hXml != NULL) { + HXML node; + char *type, *sender; + + mir_free(tag); + tag = mir_a2t("event/type"); + node = xi.getChildByPath(hXml, tag, 0); + type = node != NULL ? mir_t2a(xi.getText(node)) : NULL; + + mir_free(tag); + tag = mir_a2t("event/sender"); + node = xi.getChildByPath(hXml, tag, 0); + sender = node != NULL ? mir_t2a(xi.getText(node)) : NULL; + netlog("gg_mainthread(%x): XML Action type: %s.", this, type != NULL ? type : "unknown"); + // Avatar change notify + if (type != NULL && !strcmp(type, "28")) { + netlog("gg_mainthread(%x): Client %s changed his avatar.", this, sender); + requestAvatar(getcontact(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 = getcontact(e->event.typing_notification.uin, 0, 0, NULL); +#ifdef DEBUGMODE + netlog("gg_mainthread(%x): Typing notification from %d (%d).", this, + 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); + } + + broadcastnewstatus(ID_STATUS_OFFLINE); + setalloffline(); + db_set_dw(NULL, m_szModuleName, GG_KEY_LOGONTIME, 0); + + // If it was unwanted disconnection reconnect + if (m_iDesiredStatus != ID_STATUS_OFFLINE + && db_get_b(NULL, m_szModuleName, GG_KEY_ARECONNECT, GG_KEYDEF_ARECONNECT)) + { + netlog("gg_mainthread(%x): Unintentional disconnection detected. Going to reconnect...", this); + hostnum = 0; + broadcastnewstatus(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(&sessions_mutex); + for (l = 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(sessions, 0); + sessions = NULL; + LeaveCriticalSection(&sessions_mutex); + } + + // Stop dcc server + pth_dcc.dwThreadId = 0; +#ifdef DEBUGMODE + netlog("gg_mainthread(%x): Waiting until DCC Server Thread finished, if needed.", this); +#endif + threadwait(&pth_dcc); + + netlog("gg_mainthread(%x): Server Thread Ending", this); + return; +} + +//////////////////////////////////////////////////////////// +// Change status function +void GGPROTO::broadcastnewstatus(int newStatus) +{ + int oldStatus; + + EnterCriticalSection(&modemsg_mutex); + oldStatus = m_iStatus; + if (oldStatus == newStatus) + { + LeaveCriticalSection(&modemsg_mutex); + return; + } + m_iStatus = newStatus; + LeaveCriticalSection(&modemsg_mutex); + + ProtoBroadcastAck(m_szModuleName, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE) oldStatus, newStatus); + + netlog("gg_broadcastnewstatus(): Broadcast new status: %d.", newStatus); +} + +//////////////////////////////////////////////////////////// +// When contact is deleted +int GGPROTO::contactdeleted(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = (HANDLE) wParam; + uin_t uin; int type; + DBVARIANT dbv; + + uin = (uin_t)db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0); + type = db_get_b(hContact, m_szModuleName, "ChatRoom", 0); + + // Terminate conference if contact is deleted + if (type && !db_get_s(hContact, m_szModuleName, "ChatRoomID", &dbv, DBVT_ASCIIZ) && gc_enabled) + { + GCDEST gcdest = {m_szModuleName, dbv.pszVal, GC_EVENT_CONTROL}; + GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; + GGGC *chat = gc_lookup(dbv.pszVal); + + netlog("gg_gc_event(): Terminating chat %x, id %s from contact list...", chat, dbv.pszVal); + if (chat) + { + // Destroy chat entry + free(chat->recipients); + list_remove(&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 && isonline()) + { + EnterCriticalSection(&sess_mutex); + gg_remove_notify_ex(sess, uin, GG_USER_NORMAL); + LeaveCriticalSection(&sess_mutex); + } + + return 0; +} + +//////////////////////////////////////////////////////////// +// When db settings changed + +int GGPROTO::dbsettingchanged(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 (!isonline()) + return 0; + + // If contact has been blocked + if (!strcmp(cws->szModule, m_szModuleName) && !strcmp(cws->szSetting, GG_KEY_BLOCK)) + { + notifyuser(hContact, 1); + return 0; + } + + // Contact is being renamed + if (gc_enabled && !strcmp(cws->szModule, m_szModuleName) && !strcmp(cws->szSetting, GG_KEY_NICK) + && cws->value.pszVal) + { + // Groupchat window contact is being renamed + DBVARIANT dbv; + int type = db_get_b(hContact, m_szModuleName, "ChatRoom", 0); + if (type && !db_get_s(hContact, m_szModuleName, "ChatRoomID", &dbv, DBVT_ASCIIZ)) + { + // Most important... check redundancy (fucking cascading) + static int cascade = 0; + if (!cascade && dbv.pszVal) + { + GCDEST gcdest = {m_szModuleName, dbv.pszVal, GC_EVENT_CHANGESESSIONAME}; + GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; + gcevent.pszText = cws->value.pszVal; + netlog("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 + gc_changenick(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) + db_set_s(hContact, m_szModuleName, GG_KEY_NICK, cws->value.pszVal); + + // If not on list changed + if (!strcmp(cws->szSetting, "NotOnList")) + { + if (db_get_b(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)) + notifyuser(hContact, 1); + } + } + return 0; +} + +//////////////////////////////////////////////////////////// +// All users set offline + +void GGPROTO::setalloffline() +{ + netlog("gg_setalloffline(): Setting buddies offline"); + db_set_w(NULL, m_szModuleName, GG_KEY_STATUS, ID_STATUS_OFFLINE); + HANDLE hContact = db_find_first(); + while (hContact) + { + char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); + if (szProto != NULL && !strcmp(szProto, m_szModuleName)) + { + db_set_w(hContact, m_szModuleName, GG_KEY_STATUS, ID_STATUS_OFFLINE); + // Clear IP and port settings + db_unset(hContact, m_szModuleName, GG_KEY_CLIENTIP); + db_unset(hContact, m_szModuleName, GG_KEY_CLIENTPORT); + // Delete status descr + db_unset(hContact, "CList", GG_KEY_STATUSDESCR); + } + hContact = db_find_next(hContact); + } +#ifdef DEBUGMODE + netlog("gg_setalloffline(): End"); +#endif +} + +//////////////////////////////////////////////////////////// +// All users set offline + +void GGPROTO::notifyuser(HANDLE hContact, int refresh) +{ + uin_t uin; + if (!hContact) return; + if (isonline() && (uin = (uin_t)db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0))) + { + // Check if user should be invisible + // Or be blocked ? + if ((db_get_w(hContact, m_szModuleName, GG_KEY_APPARENT, (WORD) ID_STATUS_ONLINE) == ID_STATUS_OFFLINE) || + db_get_b(hContact, "CList", "NotOnList", 0)) + { + mir_cslock l(sess_mutex); + if (refresh) { + gg_remove_notify_ex(sess, uin, GG_USER_NORMAL); + gg_remove_notify_ex(sess, uin, GG_USER_BLOCKED); + } + + gg_add_notify_ex(sess, uin, GG_USER_OFFLINE); + } + else if (db_get_b(hContact, m_szModuleName, GG_KEY_BLOCK, 0)) + { + mir_cslock l(sess_mutex); + if (refresh) + gg_remove_notify_ex(sess, uin, GG_USER_OFFLINE); + + gg_add_notify_ex(sess, uin, GG_USER_BLOCKED); + } + else { + mir_cslock l(sess_mutex); + if (refresh) + gg_remove_notify_ex(sess, uin, GG_USER_BLOCKED); + + gg_add_notify_ex(sess, uin, GG_USER_NORMAL); + } + } +} + +void GGPROTO::notifyall() +{ + HANDLE hContact; + char *szProto; + int count = 0, cc = 0; + uin_t *uins; + char *types; + + netlog("gg_notifyall(): Subscribing notification to all users"); + // Readup count + hContact = db_find_first(); + while (hContact) + { + szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); + if (szProto != NULL && !strcmp(szProto, m_szModuleName)) count ++; + hContact = db_find_next(hContact); + } + + // 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 (isonline()) + { + EnterCriticalSection(&sess_mutex); + gg_notify_ex(sess, NULL, NULL, 0); + LeaveCriticalSection(&sess_mutex); + } + return; + } + uins = (uin_t*)calloc(sizeof(uin_t), count); + types = (char*)calloc(sizeof(char), count); + + hContact = db_find_first(); + while (hContact && cc < count) + { + szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); + if (szProto != NULL && !strcmp(szProto, m_szModuleName) && (uins[cc] = db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0))) + { + if ((db_get_w(hContact, m_szModuleName, GG_KEY_APPARENT, (WORD) ID_STATUS_ONLINE) == ID_STATUS_OFFLINE) || + db_get_b(hContact, "CList", "NotOnList", 0)) + types[cc] = GG_USER_OFFLINE; + else if (db_get_b(hContact, m_szModuleName, GG_KEY_BLOCK, 0)) + types[cc] = GG_USER_BLOCKED; + else + types[cc] = GG_USER_NORMAL; + cc ++; + } + hContact = db_find_next(hContact); + } + if (cc < count) count = cc; + + // Send notification + if (isonline()) + { + EnterCriticalSection(&sess_mutex); + gg_notify_ex(sess, uins, types, count); + LeaveCriticalSection(&sess_mutex); + } + + // Free variables + free(uins); free(types); +} + +//////////////////////////////////////////////////////////// +// Get contact by uin + +HANDLE GGPROTO::getcontact(uin_t uin, int create, int inlist, TCHAR *szNick) +{ + // Look for contact in DB + HANDLE hContact = db_find_first(); + while (hContact) { + char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); + if (szProto != NULL && !strcmp(szProto, m_szModuleName)) { + if ((uin_t)db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0) == uin + && db_get_b(hContact, m_szModuleName, "ChatRoom", 0) == 0) + { + if (inlist) { + db_unset(hContact, "CList", "NotOnList"); + db_unset(hContact, "CList", "Hidden"); + } + return hContact; + } + } + hContact = db_find_next(hContact); + } + if (!create) return NULL; + + hContact = (HANDLE) CallService(MS_DB_CONTACT_ADD, 0, 0); + if (!hContact) { + netlog("gg_getcontact(): Failed to create Gadu-Gadu contact %s", szNick); + return NULL; + } + + if (CallService(MS_PROTO_ADDTOCONTACT, (WPARAM) hContact, (LPARAM) m_szModuleName) != 0) { + // For some reason we failed to register the protocol for this contact + CallService(MS_DB_CONTACT_DELETE, (WPARAM) hContact, 0); + netlog("Failed to register GG contact %d", uin); + return NULL; + } + + netlog("gg_getcontact(): Added buddy: %d", uin); + if (!inlist) + db_set_b(hContact, "CList", "NotOnList", 1); + + db_set_dw(hContact, m_szModuleName, GG_KEY_UIN, (DWORD) uin); + db_set_w(hContact, m_szModuleName, GG_KEY_STATUS, ID_STATUS_OFFLINE); + + // If nick specified use it + if (szNick) + db_set_ts(hContact, m_szModuleName, GG_KEY_NICK, szNick); + else if (isonline()) { + 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(&sess_mutex); + gg_pubdir50(sess, req); + LeaveCriticalSection(&sess_mutex); + gg_pubdir50_free(req); + db_set_s(hContact, m_szModuleName, GG_KEY_NICK, ditoa(uin)); + netlog("gg_getcontact(): Search for nick on uin: %d", uin); + } + } + + // Add to notify list and pull avatar for the new contact + if (isonline()) + { + PROTO_AVATAR_INFORMATIONT pai = {0}; + + EnterCriticalSection(&sess_mutex); + gg_add_notify_ex(sess, uin, (char)(inlist ? GG_USER_NORMAL : GG_USER_OFFLINE)); + LeaveCriticalSection(&sess_mutex); + + pai.cbSize = sizeof(pai); + pai.hContact = hContact; + getavatarinfo((WPARAM)GAIF_FORCE, (LPARAM)&pai); + + // Change status of the contact with our own UIN (if got yourself added to the contact list) + if (db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0) == uin) { + char *szMsg; + EnterCriticalSection(&modemsg_mutex); + szMsg = mir_strdup(getstatusmsg(m_iStatus)); + LeaveCriticalSection(&modemsg_mutex); + changecontactstatus(uin, status_m2gg(m_iStatus, szMsg != NULL), szMsg, 0, 0, 0, 0); + mir_free(szMsg); + } + } + + // TODO server side list & add buddy + return hContact; +} + +//////////////////////////////////////////////////////////// +// Status conversion + +int GGPROTO::status_m2gg(int status, int descr) +{ + // check frends only + int mask = db_get_b(NULL, m_szModuleName, 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 GGPROTO::status_gg2m(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 && db_get_b(NULL, m_szModuleName, 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 GGPROTO::changecontactstatus(uin_t uin, int status, const char *idescr, int time, uint32_t remote_ip, uint16_t remote_port, uint32_t version) +{ + HANDLE hContact = getcontact(uin, 0, 0, NULL); + + // Check if contact is on list + if (!hContact) return; + + // Write contact status + db_set_w(hContact, m_szModuleName, GG_KEY_STATUS, (WORD)status_gg2m(status)); + + // Check if there's description and if it's not empty + if (idescr && *idescr) + { + netlog("gg_changecontactstatus(): Saving for %d status descr \"%s\".", uin, idescr); + db_set_s(hContact, "CList", GG_KEY_STATUSDESCR, idescr); + } + else + // Remove status if there's nothing + db_unset(hContact, "CList", GG_KEY_STATUSDESCR); + + // Store contact ip and port + if (remote_ip) db_set_dw(hContact, m_szModuleName, GG_KEY_CLIENTIP, (DWORD) swap32(remote_ip)); + if (remote_port) db_set_w(hContact, m_szModuleName, GG_KEY_CLIENTPORT, (WORD) remote_port); + if (version) + { + char sversion[48]; + db_set_dw(hContact, m_szModuleName, GG_KEY_CLIENTVERSION, (DWORD) version); + mir_snprintf(sversion, sizeof(sversion), "%sGadu-Gadu %s", (version & 0x00ffffff) > 0x2b ? "Nowe " : "", gg_version2string(version)); + db_set_s(hContact, m_szModuleName, "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/src/dialogs.cpp b/protocols/Gadu-Gadu/src/dialogs.cpp new file mode 100644 index 0000000000..345624715b --- /dev/null +++ b/protocols/Gadu-Gadu/src/dialogs.cpp @@ -0,0 +1,1016 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 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. +//////////////////////////////////////////////////////////////////////////////// + +#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; + GetLocaleInfoA(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; lstrcpyA(str, "???"); break; + } + } + + if (disableIfUndef) { + EnableWindow(GetDlgItem(hwndDlg, idCtrl), !unspecified); + if (unspecified) + SetDlgItemText(hwndDlg, idCtrl, TranslateT("")); + else + SetDlgItemTextA(hwndDlg, idCtrl, pstr); + } + else { + EnableWindow(GetDlgItem(hwndDlg, idCtrl), TRUE); + if (!unspecified) + SetDlgItemTextA(hwndDlg, idCtrl, pstr); + } + DBFreeVariant(&dbv); +} + +//////////////////////////////////////////////////////////////////////////////// +// Options Page : Init + +int GGPROTO::options_init(WPARAM wParam, LPARAM lParam) +{ + OPTIONSDIALOGPAGE odp = { 0 }; + odp.cbSize = sizeof(odp); + odp.flags = ODPF_TCHAR; + odp.position = 1003000; + odp.hInstance = hInstance; + odp.ptszGroup = LPGENT("Network"); + odp.ptszTitle = m_tszUserName; + odp.dwInitParam = (LPARAM)this; + + odp.ptszTab = LPGENT("General"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_GG_GENERAL); + odp.pfnDlgProc = gg_genoptsdlgproc; + odp.flags = ODPF_TCHAR | ODPF_BOLDGROUPS | ODPF_DONTTRANSLATE; + Options_AddPage(wParam, &odp); + + odp.ptszTab = LPGENT("Conference"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_GG_CONFERENCE); + odp.pfnDlgProc = gg_confoptsdlgproc; + Options_AddPage(wParam, &odp); + + odp.ptszTab = LPGENT("Advanced"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_GG_ADVANCED); + odp.pfnDlgProc = gg_advoptsdlgproc; + odp.flags |= ODPF_EXPERTONLY; + Options_AddPage(wParam, &odp); + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// Check if new user data has been filled in for specified account +void GGPROTO::checknewuser(uin_t uin, const char* passwd) +{ + char oldpasswd[128]; + DBVARIANT dbv; + uin_t olduin = (uin_t)db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0); + + oldpasswd[0] = '\0'; + if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) + { + if (dbv.pszVal) strcpy(oldpasswd, dbv.pszVal); + DBFreeVariant(&dbv); + } + + if (uin > 0 && strlen(passwd) > 0 && (uin != olduin || strcmp(oldpasswd, passwd))) + check_first_conn = 1; +} + +//////////////////////////////////////////////////////////////////////////////// +// Options Page : Proc + +static void gg_optsdlgcheck(HWND hwndDlg) +{ + TCHAR text[128]; + GetDlgItemText(hwndDlg, IDC_UIN, text, SIZEOF(text)); + if (text[0]) { + GetDlgItemText(hwndDlg, IDC_EMAIL, text, SIZEOF(text)); + if (text[0]) + 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) +{ + GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch (msg) { + case WM_INITDIALOG: + { + DBVARIANT dbv; + DWORD num; + GGPROTO *gg = (GGPROTO *)lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); + + TranslateDialogDefault(hwndDlg); + if (num = db_get_dw(NULL, gg->m_szModuleName, GG_KEY_UIN, 0)) + { + SetDlgItemTextA(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 (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); + SetDlgItemTextA(hwndDlg, IDC_PASSWORD, dbv.pszVal); + DBFreeVariant(&dbv); + } + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, &dbv, DBVT_ASCIIZ)) { + SetDlgItemTextA(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, db_get_b(NULL, gg->m_szModuleName, GG_KEY_FRIENDSONLY, GG_KEYDEF_FRIENDSONLY)); + CheckDlgButton(hwndDlg, IDC_SHOWINVISIBLE, db_get_b(NULL, gg->m_szModuleName, GG_KEY_SHOWINVISIBLE, GG_KEYDEF_SHOWINVISIBLE)); + CheckDlgButton(hwndDlg, IDC_LEAVESTATUSMSG, db_get_b(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUSMSG, GG_KEYDEF_LEAVESTATUSMSG)); + if (gg->gc_enabled) + CheckDlgButton(hwndDlg, IDC_IGNORECONF, db_get_b(NULL, gg->m_szModuleName, GG_KEY_IGNORECONF, GG_KEYDEF_IGNORECONF)); + else + { + EnableWindow(GetDlgItem(hwndDlg, IDC_IGNORECONF), FALSE); + CheckDlgButton(hwndDlg, IDC_IGNORECONF, TRUE); + } + CheckDlgButton(hwndDlg, IDC_IMGRECEIVE, db_get_b(NULL, gg->m_szModuleName, GG_KEY_IMGRECEIVE, GG_KEYDEF_IMGRECEIVE)); + CheckDlgButton(hwndDlg, IDC_SHOWLINKS, db_get_b(NULL, gg->m_szModuleName, GG_KEY_SHOWLINKS, GG_KEYDEF_SHOWLINKS)); + CheckDlgButton(hwndDlg, IDC_ENABLEAVATARS, db_get_b(NULL, gg->m_szModuleName, 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)TranslateT("")); // 0 + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)TranslateT("Online")); // ID_STATUS_ONLINE + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)TranslateT("Away")); // ID_STATUS_AWAY + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)TranslateT("DND")); // ID_STATUS_DND + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)TranslateT("Free for chat")); // ID_STATUS_FREECHAT + SendDlgItemMessage(hwndDlg, IDC_LEAVESTATUS, CB_ADDSTRING, 0, (LPARAM)TranslateT("Invisible")); // ID_STATUS_INVISIBLE + switch(db_get_w(NULL, gg->m_szModuleName, 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)TranslateT("System tray icon")); + SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_ADDSTRING, 0, (LPARAM)TranslateT("Popup window")); + SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_ADDSTRING, 0, (LPARAM)TranslateT("Message with [img] BBCode")); + SendDlgItemMessage(hwndDlg, IDC_IMGMETHOD, CB_SETCURSEL, + db_get_b(NULL, gg->m_szModuleName, 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; + GetDlgItemTextA(hwndDlg, IDC_UIN, email, sizeof(email)); + uin = atoi(email); + GetDlgItemTextA(hwndDlg, IDC_EMAIL, email, sizeof(email)); + if (!strlen(email)) + MessageBox(NULL, TranslateT("You need to specify your registration e-mail first."), + gg->m_tszUserName, MB_OK | MB_ICONEXCLAMATION); + else if (MessageBox(NULL, + TranslateT("Your password will be sent to your registration e-mail.\nDo you want to continue ?"), + gg->m_tszUserName, + MB_OKCANCEL | MB_ICONQUESTION) == IDOK) + gg->remindpassword(uin, email); + return FALSE; + } + case IDC_CREATEACCOUNT: + case IDC_REMOVEACCOUNT: + if (gg->isonline()) + { + if (MessageBox( + NULL, + TranslateT("You should disconnect before making any permanent changes with your account.\nDo you want to disconnect now ?"), + gg->m_tszUserName, + MB_OKCANCEL | MB_ICONEXCLAMATION) == IDCANCEL) + break; + else + gg->disconnect(); + } + case IDC_CHPASS: + case IDC_CHEMAIL: + { + // Readup data + GGUSERUTILDLGDATA dat; + int ret; + char pass[128], email[128]; + GetDlgItemTextA(hwndDlg, IDC_UIN, pass, sizeof(pass)); + dat.uin = atoi(pass); + GetDlgItemTextA(hwndDlg, IDC_PASSWORD, pass, sizeof(pass)); + GetDlgItemTextA(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; + // Show reload required window + ShowWindow(GetDlgItem(hwndDlg, IDC_RELOADREQD), SW_SHOW); + + // Update uin + if (num = db_get_dw(NULL, gg->m_szModuleName, GG_KEY_UIN, 0)) + SetDlgItemTextA(hwndDlg, IDC_UIN, ditoa(num)); + else + SetDlgItemTextA(hwndDlg, IDC_UIN, ""); + + // Update password + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); + SetDlgItemTextA(hwndDlg, IDC_PASSWORD, dbv.pszVal); + DBFreeVariant(&dbv); + } + else SetDlgItemTextA(hwndDlg, IDC_PASSWORD, ""); + + // Update e-mail + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, &dbv, DBVT_ASCIIZ)) { + SetDlgItemTextA(hwndDlg, IDC_EMAIL, dbv.pszVal); + DBFreeVariant(&dbv); + } + else SetDlgItemTextA(hwndDlg, IDC_EMAIL, ""); + + // Update links + gg_optsdlgcheck(hwndDlg); + + // Remove details + if (LOWORD(wParam) != IDC_CHPASS && LOWORD(wParam) != IDC_CHEMAIL) + { + db_unset(NULL, gg->m_szModuleName, GG_KEY_NICK); + db_unset(NULL, gg->m_szModuleName, "NickName"); + db_unset(NULL, gg->m_szModuleName, "City"); + db_unset(NULL, gg->m_szModuleName, "FirstName"); + db_unset(NULL, gg->m_szModuleName, "LastName"); + db_unset(NULL, gg->m_szModuleName, "FamilyName"); + db_unset(NULL, gg->m_szModuleName, "CityOrigin"); + db_unset(NULL, gg->m_szModuleName, "Age"); + db_unset(NULL, gg->m_szModuleName, "BirthYear"); + db_unset(NULL, gg->m_szModuleName, "Gender"); + } + } + } + break; + } + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + } + case WM_NOTIFY: + { + switch (((LPNMHDR) lParam)->code) { + case PSN_APPLY: + { + int status_flags = GG_STATUS_FLAG_UNKNOWN; + char str[128]; + uin_t uin; + + // Write Gadu-Gadu number & password + GetDlgItemTextA(hwndDlg, IDC_UIN, str, sizeof(str)); + uin = atoi(str); + GetDlgItemTextA(hwndDlg, IDC_PASSWORD, str, sizeof(str)); + CallService(MS_DB_CRYPT_ENCODESTRING, sizeof(str), (LPARAM) str); + gg->checknewuser(uin, str); + db_set_dw(NULL, gg->m_szModuleName, GG_KEY_UIN, uin); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, str); + + // Write Gadu-Gadu email + GetDlgItemTextA(hwndDlg, IDC_EMAIL, str, sizeof(str)); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, str); + + // Write checkboxes + db_set_b(NULL, gg->m_szModuleName, GG_KEY_FRIENDSONLY, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_FRIENDSONLY)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_SHOWINVISIBLE, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWINVISIBLE)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUSMSG, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_LEAVESTATUSMSG)); + if (gg->gc_enabled) + db_set_b(NULL, gg->m_szModuleName, GG_KEY_IGNORECONF, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_IGNORECONF)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_IMGRECEIVE, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_IMGRECEIVE)); + db_set_b(NULL, gg->m_szModuleName, 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); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_ENABLEAVATARS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ENABLEAVATARS)); + + db_set_b(NULL, gg->m_szModuleName, 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: + db_set_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, ID_STATUS_ONLINE); + break; + case 2: + db_set_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, ID_STATUS_AWAY); + break; + case 3: + db_set_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, ID_STATUS_DND); + break; + case 4: + db_set_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, ID_STATUS_FREECHAT); + break; + case 5: + db_set_w(NULL, gg->m_szModuleName, GG_KEY_LEAVESTATUS, ID_STATUS_INVISIBLE); + break; + default: + db_set_w(NULL, gg->m_szModuleName, 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) +{ + GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + 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)TranslateT("Allow")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ask")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ignore")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_SETCURSEL, + db_get_w(NULL, gg->m_szModuleName, GG_KEY_GC_POLICY_TOTAL, GG_KEYDEF_GC_POLICY_TOTAL), 0); + + if (num = db_get_w(NULL, gg->m_szModuleName, GG_KEY_GC_COUNT_TOTAL, GG_KEYDEF_GC_COUNT_TOTAL)) + SetDlgItemTextA(hwndDlg, IDC_GC_COUNT_TOTAL, ditoa(num)); + + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_ADDSTRING, 0, (LPARAM)TranslateT("Allow")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ask")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ignore")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_SETCURSEL, + db_get_w(NULL, gg->m_szModuleName, GG_KEY_GC_POLICY_UNKNOWN, GG_KEYDEF_GC_POLICY_UNKNOWN), 0); + + if (num = db_get_w(NULL, gg->m_szModuleName, GG_KEY_GC_COUNT_UNKNOWN, GG_KEYDEF_GC_COUNT_UNKNOWN)) + SetDlgItemTextA(hwndDlg, IDC_GC_COUNT_UNKNOWN, ditoa(num)); + + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_ADDSTRING, 0, (LPARAM)TranslateT("Allow")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ask")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_ADDSTRING, 0, (LPARAM)TranslateT("Ignore")); + SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_SETCURSEL, + db_get_w(NULL, gg->m_szModuleName, 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]; + + // Write groupchat policy + db_set_w(NULL, gg->m_szModuleName, GG_KEY_GC_POLICY_TOTAL, + (WORD)SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_TOTAL, CB_GETCURSEL, 0, 0)); + db_set_w(NULL, gg->m_szModuleName, GG_KEY_GC_POLICY_UNKNOWN, + (WORD)SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_UNKNOWN, CB_GETCURSEL, 0, 0)); + db_set_w(NULL, gg->m_szModuleName, GG_KEY_GC_POLICY_DEFAULT, + (WORD)SendDlgItemMessage(hwndDlg, IDC_GC_POLICY_DEFAULT, CB_GETCURSEL, 0, 0)); + + GetDlgItemTextA(hwndDlg, IDC_GC_COUNT_TOTAL, str, sizeof(str)); + db_set_w(NULL, gg->m_szModuleName, GG_KEY_GC_COUNT_TOTAL, (WORD)atoi(str)); + GetDlgItemTextA(hwndDlg, IDC_GC_COUNT_UNKNOWN, str, sizeof(str)); + db_set_w(NULL, gg->m_szModuleName, 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) +{ + GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch (msg) { + case WM_INITDIALOG: + { + DBVARIANT dbv; + DWORD num; + GGPROTO *gg = (GGPROTO *)lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); + + TranslateDialogDefault(hwndDlg); + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_SERVERHOSTS, &dbv, DBVT_ASCIIZ)) { + SetDlgItemTextA(hwndDlg, IDC_HOST, dbv.pszVal); + DBFreeVariant(&dbv); + } + else SetDlgItemTextA(hwndDlg, IDC_HOST, GG_KEYDEF_SERVERHOSTS); + + CheckDlgButton(hwndDlg, IDC_KEEPALIVE, db_get_b(NULL, gg->m_szModuleName, GG_KEY_KEEPALIVE, GG_KEYDEF_KEEPALIVE)); + CheckDlgButton(hwndDlg, IDC_SHOWCERRORS, db_get_b(NULL, gg->m_szModuleName, GG_KEY_SHOWCERRORS, GG_KEYDEF_SHOWCERRORS)); + CheckDlgButton(hwndDlg, IDC_ARECONNECT, db_get_b(NULL, gg->m_szModuleName, GG_KEY_ARECONNECT, GG_KEYDEF_ARECONNECT)); + CheckDlgButton(hwndDlg, IDC_MSGACK, db_get_b(NULL, gg->m_szModuleName, GG_KEY_MSGACK, GG_KEYDEF_MSGACK)); + CheckDlgButton(hwndDlg, IDC_MANUALHOST, db_get_b(NULL, gg->m_szModuleName, GG_KEY_MANUALHOST, GG_KEYDEF_MANUALHOST)); + CheckDlgButton(hwndDlg, IDC_SSLCONN, db_get_b(NULL, gg->m_szModuleName, 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, db_get_b(NULL, gg->m_szModuleName, GG_KEY_DIRECTCONNS, GG_KEYDEF_DIRECTCONNS)); + if (num = db_get_w(NULL, gg->m_szModuleName, GG_KEY_DIRECTPORT, GG_KEYDEF_DIRECTPORT)) + SetDlgItemTextA(hwndDlg, IDC_DIRECTPORT, ditoa(num)); + CheckDlgButton(hwndDlg, IDC_FORWARDING, db_get_b(NULL, gg->m_szModuleName, GG_KEY_FORWARDING, GG_KEYDEF_FORWARDING)); + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_FORWARDHOST, &dbv, DBVT_ASCIIZ)) { + SetDlgItemTextA(hwndDlg, IDC_FORWARDHOST, dbv.pszVal); + DBFreeVariant(&dbv); + } + if (num = db_get_w(NULL, gg->m_szModuleName, GG_KEY_FORWARDPORT, GG_KEYDEF_FORWARDPORT)) + SetDlgItemTextA(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]; + db_set_b(NULL, gg->m_szModuleName, GG_KEY_KEEPALIVE, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_KEEPALIVE)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_SHOWCERRORS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SHOWCERRORS)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_ARECONNECT, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_ARECONNECT)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_MSGACK, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_MSGACK)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_MANUALHOST, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_MANUALHOST)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_SSLCONN, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_SSLCONN)); + + // Transfer settings + db_set_b(NULL, gg->m_szModuleName, GG_KEY_DIRECTCONNS, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_DIRECTCONNS)); + db_set_b(NULL, gg->m_szModuleName, GG_KEY_FORWARDING, (BYTE) IsDlgButtonChecked(hwndDlg, IDC_FORWARDING)); + + // Write custom servers + GetDlgItemTextA(hwndDlg, IDC_HOST, str, sizeof(str)); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_SERVERHOSTS, str); + + // Write direct port + GetDlgItemTextA(hwndDlg, IDC_DIRECTPORT, str, sizeof(str)); + db_set_w(NULL, gg->m_szModuleName, GG_KEY_DIRECTPORT, (WORD)atoi(str)); + // Write forwarding host + GetDlgItemTextA(hwndDlg, IDC_FORWARDHOST, str, sizeof(str)); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_FORWARDHOST, str); + GetDlgItemTextA(hwndDlg, IDC_FORWARDPORT, str, sizeof(str)); + db_set_w(NULL, gg->m_szModuleName, 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)TranslateT("Female")); // 1 + SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)TranslateT("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, TranslateT("Your details has been uploaded to the public directory."), + gg->m_tszUserName, MB_OK | MB_ICONINFORMATION); + dat->updating = FALSE; + break; + } + + if (hContact == NULL) + szProto = gg->m_szModuleName; + 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)db_get_b(hContact, gg->m_szModuleName, "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()) + { + MessageBox(NULL, + TranslateT("You have to be logged in before you can change your details."), + gg->m_tszUserName, MB_OK | MB_ICONSTOP); + break; + } + + EnableWindow(GetDlgItem(hwndDlg, IDC_SAVE), FALSE); + + req = gg_pubdir50_new(GG_PUBDIR50_WRITE); + + GetDlgItemTextA(hwndDlg, IDC_FIRSTNAME, text, sizeof(text)); + if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_FIRSTNAME, text); + + GetDlgItemTextA(hwndDlg, IDC_LASTNAME, text, sizeof(text)); + if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_LASTNAME, text); + + GetDlgItemTextA(hwndDlg, IDC_NICKNAME, text, sizeof(text)); + if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_NICKNAME, text); + + GetDlgItemTextA(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, ""); + } + + GetDlgItemTextA(hwndDlg, IDC_BIRTHYEAR, text, sizeof(text)); + if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_BIRTHYEAR, text); + + GetDlgItemTextA(hwndDlg, IDC_FAMILYNAME, text, sizeof(text)); + if (strlen(text)) gg_pubdir50_add(req, GG_PUBDIR50_FAMILYNAME, text); + + GetDlgItemTextA(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 GGPROTO::details_init(WPARAM wParam, LPARAM lParam) +{ + char* szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, lParam, 0); + if ((szProto == NULL || strcmp(szProto, m_szModuleName)) && lParam || lParam && db_get_b((HANDLE)lParam, m_szModuleName, "ChatRoom", 0)) + return 0; + + // Here goes init + { + OPTIONSDIALOGPAGE odp = {0}; + + odp.cbSize = sizeof(odp); + odp.flags = ODPF_DONTTRANSLATE | ODPF_TCHAR; + odp.hInstance = hInstance; + odp.pfnDlgProc = gg_detailsdlgproc; + odp.position = -1900000000; + odp.pszTemplate = ((HANDLE)lParam != NULL) ? MAKEINTRESOURCEA(IDD_INFO_GG) : MAKEINTRESOURCEA(IDD_CHINFO_GG); + odp.ptszTitle = m_tszUserName; + odp.dwInitParam = (LPARAM)this; + UserInfo_AddPage(wParam, &odp); + } + + // Start search for user data + if ((HANDLE)lParam == NULL) + GetInfo(NULL, 0); + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////////////////// +// Proc: Account manager options dialog +INT_PTR CALLBACK gg_acc_mgr_guidlgproc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +//////////////////////////////////////////////////////////////////////////////////////////// +{ + GGPROTO *gg = (GGPROTO *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch (msg) { + case WM_INITDIALOG: + { + DBVARIANT dbv; + DWORD num; + GGPROTO *gg = (GGPROTO *)lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)lParam); + + TranslateDialogDefault(hwndDlg); + if (num = db_get_dw(NULL, gg->m_szModuleName, GG_KEY_UIN, 0)) + SetDlgItemTextA(hwndDlg, IDC_UIN, ditoa(num)); + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); + SetDlgItemTextA(hwndDlg, IDC_PASSWORD, dbv.pszVal); + DBFreeVariant(&dbv); + } + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, &dbv, DBVT_ASCIIZ)) { + SetDlgItemTextA(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]; + GetDlgItemTextA(hwndDlg, IDC_UIN, pass, sizeof(pass)); + dat.uin = atoi(pass); + GetDlgItemTextA(hwndDlg, IDC_PASSWORD, pass, sizeof(pass)); + GetDlgItemTextA(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; + // Show reload required window + ShowWindow(GetDlgItem(hwndDlg, IDC_RELOADREQD), SW_SHOW); + + // Update uin + if (num = db_get_dw(NULL, gg->m_szModuleName, GG_KEY_UIN, 0)) + SetDlgItemTextA(hwndDlg, IDC_UIN, ditoa(num)); + else + SetDlgItemTextA(hwndDlg, IDC_UIN, ""); + + // Update password + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); + SetDlgItemTextA(hwndDlg, IDC_PASSWORD, dbv.pszVal); + DBFreeVariant(&dbv); + } + else SetDlgItemTextA(hwndDlg, IDC_PASSWORD, ""); + + // Update e-mail + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, &dbv, DBVT_ASCIIZ)) { + SetDlgItemTextA(hwndDlg, IDC_EMAIL, dbv.pszVal); + DBFreeVariant(&dbv); + } + else SetDlgItemTextA(hwndDlg, IDC_EMAIL, ""); + } + } + } + break; + + case WM_NOTIFY: + switch(((LPNMHDR)lParam)->idFrom) { + case 0: + switch (((LPNMHDR) lParam)->code) { + case PSN_APPLY: + { + char str[128]; + uin_t uin; + + // Write Gadu-Gadu number & password + GetDlgItemTextA(hwndDlg, IDC_UIN, str, sizeof(str)); + uin = atoi(str); + GetDlgItemTextA(hwndDlg, IDC_PASSWORD, str, sizeof(str)); + CallService(MS_DB_CRYPT_ENCODESTRING, sizeof(str), (LPARAM) str); + gg->checknewuser(uin, str); + db_set_dw(NULL, gg->m_szModuleName, GG_KEY_UIN, uin); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, str); + + // Write Gadu-Gadu email + GetDlgItemTextA(hwndDlg, IDC_EMAIL, str, sizeof(str)); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, str); + } + } + } + break; + } + return FALSE; +} diff --git a/protocols/Gadu-Gadu/src/dynstuff.cpp b/protocols/Gadu-Gadu/src/dynstuff.cpp new file mode 100644 index 0000000000..9ae41daf59 --- /dev/null +++ b/protocols/Gadu-Gadu/src/dynstuff.cpp @@ -0,0 +1,612 @@ +/* $Id: dynstuff.c 11259 2010-02-17 04:47:22Z borkra $ */ + +/* + * (C) Copyright 2001-2003 Wojtek Kaniewski + * Dawid Jarosz + * + * 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 +#include +#include +#include + +/* + * 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 *)) +{ + if (!list) { + errno = EFAULT; + return NULL; + } + + list_t newlist = (list_t)malloc(sizeof(struct list)); + + newlist->data = data; + newlist->next = NULL; + + if (alloc_size) { + newlist->data = malloc(alloc_size); + memcpy(newlist->data, data, alloc_size); + } + + list_t tmp; + if (!(tmp = *list)) { + *list = newlist; + } else { + if (!comparision) { + while (tmp->next) + tmp = tmp->next; + tmp->next = newlist; + } else { + list_t prev = NULL; + + while (comparision(newlist->data, tmp->data) > 0) { + prev = tmp; + tmp = tmp->next; + if (!tmp) + break; + } + + if (!prev) { + tmp = *list; + *list = newlist; + newlist->next = tmp; + } else { + prev->next = newlist; + newlist->next = tmp; + } + } + } + + return newlist->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 = (char*)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 = (string_t)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 = (char*)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 = (char*)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 = (char*)calloc(1, len + 1); + strncpy(token, p, len); + token[len] = 0; + p = q; + } + + result = (char**)realloc(result, (items + 2) * sizeof(char*)); + result[items] = token; + result[++items] = NULL; + + if (!*p) + break; + + p++; + } + +failure: + if (!items) + result = (char**)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 = (char**)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/src/dynstuff.h b/protocols/Gadu-Gadu/src/dynstuff.h new file mode 100644 index 0000000000..8e36c67ede --- /dev/null +++ b/protocols/Gadu-Gadu/src/dynstuff.h @@ -0,0 +1,70 @@ +/* $Id: dynstuff.h 4366 2006-12-20 22:40:37Z ono $ */ + +/* + * (C) Copyright 2001-2002 Wojtek Kaniewski + * Dawid Jarosz + * + * 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/src/filetransfer.cpp b/protocols/Gadu-Gadu/src/filetransfer.cpp new file mode 100644 index 0000000000..e7560e16a2 --- /dev/null +++ b/protocols/Gadu-Gadu/src/filetransfer.cpp @@ -0,0 +1,977 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 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. +//////////////////////////////////////////////////////////////////////////////// + +#include "gg.h" +#include +#include +#include + +void GGPROTO::dccstart() +{ + DWORD exitCode = 0; + + if (dcc) return; + + // Startup dcc thread + GetExitCodeThread(pth_dcc.hThread, &exitCode); + // Check if dcc thread isn't running already + if (exitCode == STILL_ACTIVE) + { +#ifdef DEBUGMODE + netlog("gg_dccstart(): DCC thread still active. Exiting..."); +#endif + // Signalize mainthread it's started + if (hEvent) SetEvent(hEvent); + return; + } + + // Check if we wan't direct connections + if (!db_get_b(NULL, m_szModuleName, GG_KEY_DIRECTCONNS, GG_KEYDEF_DIRECTCONNS)) + { + netlog("gg_dccstart(): No direct connections setup."); + if (hEvent) SetEvent(hEvent); + return; + } + + // Start thread + pth_dcc.hThread = forkthreadex(&GGPROTO::dccmainthread, NULL, &pth_dcc.dwThreadId); +} + +void GGPROTO::dccconnect(uin_t uin) +{ + struct gg_dcc *dcc; + HANDLE hContact = getcontact(uin, 0, 0, NULL); + DWORD ip, myuin; WORD port; + + netlog("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(db_get_dw(hContact, m_szModuleName, GG_KEY_CLIENTIP, 0)); + port = db_get_w(hContact, m_szModuleName, GG_KEY_CLIENTPORT, 0); + myuin = db_get_dw(NULL, m_szModuleName, 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(&ft_mutex); + list_add(&watches, dcc, 0); + LeaveCriticalSection(&ft_mutex); +} + +////////////////////////////////////////////////////////// +// THREAD: File transfer fail +struct ftfaildata +{ + HANDLE hContact; + HANDLE hProcess; +}; + +void __cdecl GGPROTO::ftfailthread(void *param) +{ + struct ftfaildata *ft = (struct ftfaildata *)param; + SleepEx(100, FALSE); + netlog("gg_ftfailthread(): Sending failed file transfer."); + ProtoBroadcastAck(m_szModuleName, ft->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ft->hProcess, 0); + free(ft); +} + +HANDLE ftfail(GGPROTO *gg, HANDLE hContact) +{ + ftfaildata *ft = (ftfaildata*)malloc(sizeof(struct ftfaildata)); +#ifdef DEBUGMODE + gg->netlog("gg_ftfail(): Failing file transfer..."); +#endif + srand(time(NULL)); + ft->hProcess = (HANDLE)rand(); + ft->hContact = hContact; + gg->forkthread(&GGPROTO::ftfailthread, ft); + return ft->hProcess; +} + +//////////////////////////////////////////////////////////// +// Main DCC connection session thread + +// Info refresh min time (msec) / half-sec +#define GGSTATREFRESHEVERY 500 + +void __cdecl GGPROTO::dccmainthread(void*) +{ + uin_t uin; + 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 + watches = transfers = requests = l = NULL; + + netlog("gg_dccmainthread(): DCC Server Thread Starting"); + + // Readup number + if (!(uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0))) + { + netlog("gg_dccmainthread(): No Gadu-Gadu number specified. Exiting."); + if (hEvent) SetEvent(hEvent); + return; + } + + // Create listen socket on config direct port + if (!(dcc = gg_dcc_socket_create(uin, (uint16_t)db_get_w(NULL, m_szModuleName, GG_KEY_DIRECTPORT, GG_KEYDEF_DIRECTPORT)))) + { + netlog("gg_dccmainthread(): Cannot create DCC listen socket. Exiting."); + // Signalize mainthread we haven't start + if (hEvent) SetEvent(hEvent); + return; + } + + gg_dcc_port = dcc->port; + gg_dcc_ip = inet_addr("255.255.255.255"); + netlog("gg_dccmainthread(): Listening on port %d.", gg_dcc_port); + + // Signalize mainthread we started + if (hEvent) SetEvent(hEvent); + + // Add main dcc handler to watches + list_add(&watches, dcc, 0); + + // Do while we are in the main server thread + while(pth_dcc.dwThreadId && dcc) + { + // Timeouts + tv.tv_sec = 1; + tv.tv_usec = 0; + + // Prepare descriptiors for select + FD_ZERO(&rd); + FD_ZERO(&wd); + + for (maxfd = 0, l = watches; l; l = l->next) + { + gg_common *w = (gg_common*)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) + netlog("gg_dccmainthread(): Bad descriptor on select()."); + else if (errno != EINTR) + netlog("gg_dccmainthread(): Unknown error on select()."); + continue; + } + + // Process watches (carefull with l) + l = watches; + EnterCriticalSection(&ft_mutex); + while (l) + { + struct gg_common *c = (gg_common*)l->data; + struct gg_dcc *dcc = (gg_dcc*)l->data; + struct gg_dcc7 *dcc7 = (gg_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))) + { + netlog("gg_dccmainthread(): Socket closed."); + // Remove socket and _close + list_remove(&watches, dcc, 0); + gg_dcc_socket_free(dcc); + + // Check if it's main socket + if (dcc == dcc) dcc = NULL; + continue; + } + else netlog("gg_dccmainthread(): Event: %s", ggdebug_eventtype(e)); + + switch(e->type) + { + // Client connected + case GG_EVENT_DCC_NEW: + list_add(&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, (char*)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(m_szModuleName, dcc->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc, (LPARAM)&pfts); + } + break; + + // Connection was successfuly ended + case GG_EVENT_DCC_DONE: + netlog("gg_dccmainthread(): Client: %d, Transfer done ! Closing connection.", dcc->peer_uin); + // Remove from watches + list_remove(&watches, dcc, 0); + // Close file & success + if (dcc->file_fd != -1) + { + PROTOFILETRANSFERSTATUS pfts; + strncpy(filename, dcc->folder, sizeof(filename)); + strncat(filename, (char*)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(m_szModuleName, dcc->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc, (LPARAM)&pfts); + _close(dcc->file_fd); dcc->file_fd = -1; + ProtoBroadcastAck(m_szModuleName, dcc->contact, ACKTYPE_FILE, ACKRESULT_SUCCESS, dcc, 0); + } + // Free dcc + gg_free_dcc(dcc); if (dcc == dcc) dcc = NULL; + break; + + // Client error + case GG_EVENT_DCC_ERROR: + switch (e->event.dcc_error) + { + case GG_ERROR_DCC_HANDSHAKE: + netlog("gg_dccmainthread(): Client: %d, Handshake error.", dcc->peer_uin); + break; + case GG_ERROR_DCC_NET: + netlog("gg_dccmainthread(): Client: %d, Network error.", dcc->peer_uin); + break; + case GG_ERROR_DCC_FILE: + netlog("gg_dccmainthread(): Client: %d, File read/write error.", dcc->peer_uin); + break; + case GG_ERROR_DCC_EOF: + netlog("gg_dccmainthread(): Client: %d, End of file/connection error.", dcc->peer_uin); + break; + case GG_ERROR_DCC_REFUSED: + netlog("gg_dccmainthread(): Client: %d, Connection refused error.", dcc->peer_uin); + break; + default: + netlog("gg_dccmainthread(): Client: %d, Unknown error.", dcc->peer_uin); + } + // Don't do anything if it's main socket + if (dcc == dcc) break; + + // Remove from watches + list_remove(&watches, dcc, 0); + + // Close file & fail + if (dcc->contact) + { + _close(dcc->file_fd); dcc->file_fd = -1; + ProtoBroadcastAck(m_szModuleName, dcc->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc, 0); + } + // Free dcc + gg_free_dcc(dcc); if (dcc == dcc) dcc = NULL; + break; + + // Need file acknowledgement + case GG_EVENT_DCC_NEED_FILE_ACK: + netlog("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(&watches, dcc, 0); + // Add to waiting transfers + list_add(&transfers, dcc, 0); + + ////////////////////////////////////////////////// + // Add file recv request + { + // Make new ggtransfer struct + dcc->contact = getcontact(dcc->peer_uin, 0, 0, NULL); + TCHAR* filenameT = mir_utf8decodeT((char*)dcc->file_info.filename); + + PROTORECVFILET pre = {0}; + pre.flags = PREF_TCHAR; + pre.fileCount = 1; + pre.timestamp = time(NULL); + pre.tszDescription = filenameT; + pre.ptszFiles = &filenameT; + pre.lParam = (LPARAM)dcc7; + + CCSDATA ccs = { dcc7->contact, PSR_FILE, 0, (LPARAM)&pre }; + CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); + + mir_free(filenameT); + } + break; + + // Need client accept + case GG_EVENT_DCC_CLIENT_ACCEPT: + netlog("gg_dccmainthread(): Client: %d, Client accept.", dcc->peer_uin); + // Check if user is on the list and if it is my uin + if (getcontact(dcc->peer_uin, 0, 0, NULL) && + db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, -1) == dcc->uin) + break; + + // Kill unauthorized dcc + list_remove(&watches, dcc, 0); + gg_free_dcc(dcc); if (dcc == dcc) dcc = NULL; + break; + + // Client connected as we wished to (callback) + case GG_EVENT_DCC_CALLBACK: + { + int found = 0; + netlog("gg_dccmainthread(): Callback from client %d.", dcc->peer_uin); + // Seek for stored callback request + for (l = requests; l; l = l->next) + { + struct gg_dcc *req = (gg_dcc*)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(&requests, req, 0); + // Remove dcc from watches + list_remove(&watches, dcc, 0); + // Add request to watches + list_add(&watches, req, 0); + // Free old dat + gg_free_dcc(dcc); + netlog("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) + { + netlog("gg_dccmainthread(): Unknown request to client %d.", dcc->peer_uin); + // Kill unauthorized dcc + list_remove(&watches, dcc, 0); + gg_free_dcc(dcc); if (dcc == dcc) 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))) + { + netlog("gg_dccmainthread(): Socket closed."); + // Remove socket and _close + list_remove(&watches, dcc7, 0); + gg_dcc7_free(dcc7); + continue; + } + else netlog("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, (char*)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(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc7, (LPARAM)&pfts); + } + break; + + // Connection was successfuly ended + case GG_EVENT_DCC7_DONE: + netlog("gg_dccmainthread(): Client: %d, Transfer done ! Closing connection.", dcc->peer_uin); + // Remove from watches + list_remove(&watches, dcc7, 0); + // Close file & success + if (dcc7->file_fd != -1) + { + PROTOFILETRANSFERSTATUS pfts; + strncpy(filename, dcc7->folder, sizeof(filename)); + strncat(filename, (char*)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(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_DATA, dcc7, (LPARAM)&pfts); + _close(dcc7->file_fd); dcc7->file_fd = -1; + ProtoBroadcastAck(m_szModuleName, 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: + netlog("gg_dccmainthread(): Client: %d, Handshake error.", dcc7->peer_uin); + break; + case GG_ERROR_DCC7_NET: + netlog("gg_dccmainthread(): Client: %d, Network error.", dcc7->peer_uin); + break; + case GG_ERROR_DCC7_FILE: + netlog("gg_dccmainthread(): Client: %d, File read/write error.", dcc7->peer_uin); + break; + case GG_ERROR_DCC7_EOF: + netlog("gg_dccmainthread(): Client: %d, End of file/connection error.", dcc7->peer_uin); + break; + case GG_ERROR_DCC7_REFUSED: + netlog("gg_dccmainthread(): Client: %d, Connection refused error.", dcc7->peer_uin); + break; + case GG_ERROR_DCC7_RELAY: + netlog("gg_dccmainthread(): Client: %d, Relay connection error.", dcc7->peer_uin); + break; + default: + netlog("gg_dccmainthread(): Client: %d, Unknown error.", dcc7->peer_uin); + } + // Remove from watches + list_remove(&watches, dcc7, 0); + + // Close file & fail + if (dcc7->file_fd != -1) + { + _close(dcc7->file_fd); + dcc7->file_fd = -1; + } + + if (dcc7->contact) + ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0); + + // Free dcc + gg_dcc7_free(dcc7); + break; + } + + // Free event + gg_free_event(e); + break; + } + } + LeaveCriticalSection(&ft_mutex); + } + + // Close all dcc client sockets + for (l = watches; l; l = l->next) + { + struct gg_common *c = (gg_common*)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 = (gg_dcc7*)l->data; + gg_dcc7_free(dcc7); + } + else + { + struct gg_dcc *dcc = (gg_dcc*)l->data; + gg_dcc_socket_free(dcc); + + // Check if it's main socket + if (dcc == dcc) dcc = NULL; + } + } + // Close all waiting for aknowledgle transfers + for (l = transfers; l; l = l->next) + { + struct gg_common *c = (gg_common*)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 = (gg_dcc7*)l->data; + gg_dcc7_free(dcc7); + } + else + { + struct gg_dcc *dcc = (gg_dcc*)l->data; + gg_dcc_socket_free(dcc); + } + } + // Close all waiting dcc requests + for (l = requests; l; l = l->next) + { + struct gg_dcc *dcc = (gg_dcc*)l->data; + if (dcc) gg_free_dcc(dcc); + } + list_destroy(watches, 0); + list_destroy(transfers, 0); + list_destroy(requests, 0); + + gg_dcc_port = 0; + gg_dcc_ip = 0; + netlog("gg_dccmainthread(): DCC Server Thread Ending"); +} + +HANDLE GGPROTO::dccfileallow(HANDLE hTransfer, const PROTOCHAR* szPath) +{ + struct gg_dcc *dcc = (struct gg_dcc *) hTransfer; + char fileName[MAX_PATH], *path = mir_t2a(szPath); + strncpy(fileName, path, sizeof(fileName)); + strncat(fileName, (char*)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(&ft_mutex); + list_remove(&transfers, dcc, 0); + LeaveCriticalSection(&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) + { + netlog("gg_dccfileallow(): Failed to create file \"%s\".", fileName); + ProtoBroadcastAck(m_szModuleName, 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(&ft_mutex); + list_add(&watches, dcc, 0); + LeaveCriticalSection(&ft_mutex); + + netlog("gg_dccfileallow(): Receiving file \"%s\" from %d.", dcc->file_info.filename, dcc->peer_uin); + + return hTransfer; +} + +HANDLE GGPROTO::dcc7fileallow(HANDLE hTransfer, const PROTOCHAR* szPath) +{ + struct gg_dcc7 *dcc7 = (struct gg_dcc7 *) hTransfer; + char fileName[MAX_PATH], *path = mir_t2a(szPath); + int iFtRemoveRes; + strncpy(fileName, path, sizeof(fileName)); + strncat(fileName, (char*)dcc7->filename, sizeof(fileName) - strlen(fileName)); + dcc7->folder = _strdup((char *) path); + dcc7->tick = 0; + mir_free(path); + + // Remove transfer from waiting list + EnterCriticalSection(&ft_mutex); + iFtRemoveRes = list_remove(&transfers, dcc7, 0); + LeaveCriticalSection(&ft_mutex); + + if (iFtRemoveRes == -1) + { + netlog("gg_dcc7fileallow(): File transfer denied."); + ProtoBroadcastAck(m_szModuleName, 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) + { + netlog("gg_dcc7fileallow(): Failed to create file \"%s\".", fileName); + gg_dcc7_reject(dcc7, GG_DCC7_REJECT_USER); + ProtoBroadcastAck(m_szModuleName, 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(&ft_mutex); + list_add(&watches, dcc7, 0); + LeaveCriticalSection(&ft_mutex); + + netlog("gg_dcc7fileallow(): Receiving file \"%s\" from %d.", dcc7->filename, dcc7->peer_uin); + + return hTransfer; +} + +int GGPROTO::dccfiledeny(HANDLE hTransfer) +{ + struct gg_dcc *dcc = (struct gg_dcc *) hTransfer; + + // Remove transfer from any list + EnterCriticalSection(&ft_mutex); + if (watches) list_remove(&watches, dcc, 0); + if (requests) list_remove(&requests, dcc, 0); + if (transfers) list_remove(&transfers, dcc, 0); + LeaveCriticalSection(&ft_mutex); + + netlog("gg_dccfiledeny(): Rejected file \"%s\" from/to %d.", dcc->file_info.filename, dcc->peer_uin); + + // Free transfer + gg_free_dcc(dcc); + + return 0; +} + +int GGPROTO::dcc7filedeny(HANDLE hTransfer) +{ + struct gg_dcc7 *dcc7 = (struct gg_dcc7 *) hTransfer; + + gg_dcc7_reject(dcc7, GG_DCC7_REJECT_USER); + + // Remove transfer from any list + EnterCriticalSection(&ft_mutex); + if (watches) list_remove(&watches, dcc7, 0); + if (transfers) list_remove(&transfers, dcc7, 0); + LeaveCriticalSection(&ft_mutex); + + netlog("gg_dcc7filedeny(): Rejected file \"%s\" from/to %d.", dcc7->filename, dcc7->peer_uin); + + // Free transfer + gg_dcc7_free(dcc7); + + return 0; +} + +int GGPROTO::dccfilecancel(HANDLE hTransfer) +{ + struct gg_dcc *dcc = (struct gg_dcc *) hTransfer; + + // Remove transfer from any list + EnterCriticalSection(&ft_mutex); + if (watches) list_remove(&watches, dcc, 0); + if (requests) list_remove(&requests, dcc, 0); + if (transfers) list_remove(&transfers, dcc, 0); + LeaveCriticalSection(&ft_mutex); + + // Send failed info + ProtoBroadcastAck(m_szModuleName, dcc->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc, 0); + // Close file + if (dcc->file_fd != -1) + { + _close(dcc->file_fd); + dcc->file_fd = -1; + } + + netlog("gg_dccfilecancel(): Canceled file \"%s\" from/to %d.", dcc->file_info.filename, dcc->peer_uin); + + // Free transfer + gg_free_dcc(dcc); + + return 0; +} + +int GGPROTO::dcc7filecancel(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(&ft_mutex); + if (watches) list_remove(&watches, dcc7, 0); + if (transfers) list_remove(&transfers, dcc7, 0); + LeaveCriticalSection(&ft_mutex); + + // Send failed info + ProtoBroadcastAck(m_szModuleName, dcc7->contact, ACKTYPE_FILE, ACKRESULT_FAILED, dcc7, 0); + // Close file + if (dcc7->file_fd != -1) + { + _close(dcc7->file_fd); + dcc7->file_fd = -1; + } + + netlog("gg_dcc7filecancel(): Canceled file \"%s\" from/to %d.", dcc7->filename, dcc7->peer_uin); + + // Free transfer + gg_dcc7_free(dcc7); + + return 0; +} + +//////////////////////////////////////////////////////////// +// File receiving allowed + +HANDLE GGPROTO::FileAllow(HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath) +{ + // Check if its proper dcc + struct gg_common *c = (struct gg_common *) hTransfer; + if (!c) + return NULL; + + if (c->type == GG_SESSION_DCC7_GET) + return dcc7fileallow(hTransfer, szPath); + + return dccfileallow(hTransfer, szPath); +} + +//////////////////////////////////////////////////////////// +// File transfer canceled + +int GGPROTO::FileCancel(HANDLE hContact, HANDLE hTransfer) +{ + // Check if its proper dcc + struct gg_common *c = (struct gg_common *) hTransfer; + if (!c) + return 0; + + if (c->type == GG_SESSION_DCC7_SEND || c->type == GG_SESSION_DCC7_GET) + return dcc7filecancel(hTransfer); + + return dccfilecancel(hTransfer); +} + +//////////////////////////////////////////////////////////// +// File receiving denied + +int GGPROTO::FileDeny(HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szReason) +{ + // Check if its proper dcc + struct gg_common *c = (struct gg_common *) hTransfer; + if (!c) + return 0; + + if (c->type == GG_SESSION_DCC7_GET) + return dcc7filedeny(hTransfer); + + return dccfiledeny(hTransfer); +} + +//////////////////////////////////////////////////////////// +// Called when received an file + +int GGPROTO::RecvFile(HANDLE hContact, PROTOFILEEVENT* pre) +{ + return Proto_RecvFile(hContact, pre); +} + +//////////////////////////////////////////////////////////// +// Called when user sends a file + +HANDLE GGPROTO::SendFile(HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles) +{ + char *bslash, *filename; + struct gg_dcc *dcc; + DWORD ip, ver; + WORD port; + uin_t myuin, uin; + + // Check if main dcc thread is on + if (!isonline()) + return ftfail(this, hContact); + + filename = mir_t2a(ppszFiles[0]); + + // Read user IP and port + ip = swap32(db_get_dw(hContact, m_szModuleName, GG_KEY_CLIENTIP, 0)); + port = db_get_w(hContact, m_szModuleName, GG_KEY_CLIENTPORT, 0); + myuin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0); + uin = db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0); + ver = db_get_dw(hContact, m_szModuleName, 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(&sess_mutex); + if (!(dcc7 = gg_dcc7_send_file(sess, uin, filename, NULL, NULL))) { + LeaveCriticalSection(&sess_mutex); + netlog("gg_sendfile(): Failed to send file \"%s\".", filename); + mir_free(filename); + return ftfail(this, hContact); + } + LeaveCriticalSection(&sess_mutex); + + netlog("gg_sendfile(): Sending file \"%s\" to %d.", filename, uin); + + // Add dcc to watches + list_add(&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) + { + netlog("gg_sendfile(): Bad contact uin or my uin. Exit."); + mir_free(filename); + return ftfail(this, 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 = (gg_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; + netlog("gg_sendfile(): Requesting user to connect us and scheduling gg_dcc struct for a later use."); + EnterCriticalSection(&sess_mutex); + gg_dcc_request(sess, uin); + LeaveCriticalSection(&sess_mutex); + list_add(&requests, dcc, 0); + } + + // Write filename + if (gg_dcc_fill_file_info(dcc, filename) == -1) + { + netlog("gg_sendfile(): Cannot open and file fileinfo \"%s\".", filename); + gg_free_dcc(dcc); + mir_free(filename); + return ftfail(this, hContact); + } + + netlog("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(&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; +} + diff --git a/protocols/Gadu-Gadu/src/gg.cpp b/protocols/Gadu-Gadu/src/gg.cpp new file mode 100644 index 0000000000..5bd9f6ceb5 --- /dev/null +++ b/protocols/Gadu-Gadu/src/gg.cpp @@ -0,0 +1,523 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2009 Adam Strzelecki +// 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 + +// 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://miranda-ng.org/", + UNICODE_AWARE, + // {F3FF65F3-250E-416A-BEE9-58C93F85AB33} + { 0xf3ff65f3, 0x250e, 0x416a, { 0xbe, 0xe9, 0x58, 0xc9, 0x3f, 0x85, 0xab, 0x33 } } +}; + +// Other variables +HINSTANCE hInstance; + +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 + +TCHAR* ws_strerror(int code) +{ + static TCHAR err_desc[160]; + + // Not a windows error display WinSock + if (code == 0) + { + TCHAR buff[128]; + int len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, WSAGetLastError(), 0, buff, SIZEOF(buff), NULL); + if (len == 0) + mir_sntprintf(err_desc, SIZEOF(err_desc), _T("WinSock %u: Unknown error."), WSAGetLastError()); + else + mir_sntprintf(err_desc, SIZEOF(err_desc), _T("WinSock %d: %s"), WSAGetLastError(), buff); + return err_desc; + } + + // Return normal error + return _tcserror(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 + +extern "C" __declspec(dllexport) PLUGININFOEX *MirandaPluginInfoEx(DWORD mirandaVersion) +{ + return &pluginInfo; +} + +extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = {MIID_PROTOCOL, MIID_LAST}; + +////////////////////////////////////////////////////////// +// Cleanups from last plugin + +void GGPROTO::cleanuplastplugin(DWORD version) +{ + HANDLE hContact; + char *szProto; + + // Remove bad e-mail and phones from + if (version < PLUGIN_MAKE_VERSION(0, 0, 1, 4)) + { +#ifdef DEBUGMODE + netlog("gg_cleanuplastplugin(%d): Cleaning junk Phone settings from < 0.0.1.4 ...", version); +#endif + // Look for contact in DB + hContact = db_find_first(); + while (hContact) + { + szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); + if (szProto != NULL && !strcmp(szProto, m_szModuleName)) + { + // Do contact cleanup + db_unset(hContact, m_szModuleName, GG_KEY_EMAIL); + db_unset(hContact, m_szModuleName, "Phone"); + } + hContact = db_find_next(hContact); + } + } + + // Remove GG entries for non GG contacts + if (version < PLUGIN_MAKE_VERSION(0, 0, 3, 5)) + { +#ifdef DEBUGMODE + netlog("gg_cleanuplastplugin(%d): Cleaning junk Nick settings from < 0.0.3.5 ...", version); +#endif + // Look for contact in DB + hContact = db_find_first(); + while (hContact) + { + szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); + if (szProto != NULL && strcmp(szProto, m_szModuleName)) + { + // Do nick entry cleanup + db_unset(hContact, m_szModuleName, GG_KEY_NICK); + } + hContact = db_find_next(hContact); + } + } + + // Remove old unneeded entry + if (version < PLUGIN_MAKE_VERSION(0, 0, 5, 3)) + db_unset(NULL, m_szModuleName, "ShowNotOnMyList"); + + // Store this plugin version + db_set_dw(NULL, m_szModuleName, GG_PLUGINVERSION, pluginInfo.version); +} + +////////////////////////////////////////////////////////// +// 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 = (GGPROTO*)l->data; + if (strcmp(szProto, gg->m_szModuleName) == 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 ( db_get_dw(hContact, gg->m_szModuleName, GG_KEY_UIN, 0) == db_get_b(NULL, gg->m_szModuleName, GG_KEY_UIN, 0) || + db_get_b(hContact, gg->m_szModuleName, "ChatRoom", 0) || + db_get_b(hContact, "CList", "NotOnList", 0)) + mi.flags |= CMIF_HIDDEN; + mi.pszName = db_get_b(hContact, gg->m_szModuleName, 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 GGPROTO::blockuser(WPARAM wParam, LPARAM lParam) +{ + const HANDLE hContact = (HANDLE)wParam; + db_set_b(hContact, m_szModuleName, GG_KEY_BLOCK, !db_get_b(hContact, m_szModuleName, GG_KEY_BLOCK, 0)); + notifyuser(hContact, 1); + return 0; +} + + +////////////////////////////////////////////////////////// +// Contact blocking initialization + +#define GGS_BLOCKUSER "%s/BlockUser" +void GGPROTO::block_init() +{ + CLISTMENUITEM mi = {0}; + char service[64]; + + mi.cbSize = sizeof(mi); + mi.flags = CMIF_ICONFROMICOLIB; + + mir_snprintf(service, sizeof(service), GGS_BLOCKUSER, m_szModuleName); + createObjService(service, &GGPROTO::blockuser); + mi.position = -500050000; + mi.icolibItem = GetIconHandle(IDI_BLOCK); + mi.pszName = LPGEN("&Block"); + mi.pszService = service; + mi.pszContactOwner = m_szModuleName; + hBlockMenuItem = Menu_AddContactMenuItem(&mi); + + hPrebuildMenuHook = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, gg_prebuildcontactmenu); +} + +////////////////////////////////////////////////////////// +// Contact blocking uninitialization + +void GGPROTO::block_uninit() +{ + UnhookEvent(hPrebuildMenuHook); + CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)hBlockMenuItem, 0); +} + +////////////////////////////////////////////////////////// +// Menus initialization +void GGPROTO::menus_init() +{ + HGENMENU hGCRoot, hCLRoot, hRoot = MO_GetProtoRootMenu(m_szModuleName); + CLISTMENUITEM mi = {0}; + + mi.cbSize = sizeof(mi); + if (hRoot == NULL) + { + mi.ptszName = m_tszUserName; + 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 = hMenuRoot = Menu_AddProtoMenuItem(&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 = Menu_AddProtoMenuItem(&mi); + + mi.ptszName = LPGENT("Contact list"); + mi.position = 200002; + mi.icolibItem = GetIconHandle(IDI_LIST); + hCLRoot = Menu_AddProtoMenuItem(&mi); + + if (hMenuRoot) + CallService(MS_CLIST_REMOVEMAINMENUITEM, (WPARAM)hMenuRoot, 0); + hMenuRoot = NULL; + } + + gc_menus_init(hGCRoot); + import_init(hCLRoot); + sessions_menus_init(hRoot); +} + +////////////////////////////////////////////////////////// +// Module instance initialization + +static GGPROTO *gg_proto_init(const char* pszProtoName, const TCHAR* tszUserName) +{ + GGPROTO *gg = new GGPROTO(pszProtoName, tszUserName); + list_add(&g_Instances, gg, 0); + return gg; +} + +////////////////////////////////////////////////////////// +// Module instance uninitialization + +static int gg_proto_uninit(PROTO_INTERFACE *proto) +{ + GGPROTO *gg = (GGPROTO *)proto; + list_remove(&g_Instances, gg, 0); + delete gg; + return 0; +} + +////////////////////////////////////////////////////////// +// When plugin is loaded + +extern "C" int __declspec(dllexport) Load(void) +{ + mir_getXI(&xi); + mir_getLP(&pluginInfo); + + 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 + PROTOCOLDESCRIPTOR pd = { 0 }; + 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 + +extern "C" int __declspec(dllexport) Unload() +{ + LocalEventUnhook(hHookModulesLoaded); + LocalEventUnhook(hHookPreShutdown); + + // Cleanup WinSock + WSACleanup(); + return 0; +} + +////////////////////////////////////////////////////////// +// 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, ""} +}; + +const char *ggdebug_eventtype(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 GGPROTO::netlog(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)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/src/gg.h b/protocols/Gadu-Gadu/src/gg.h new file mode 100644 index 0000000000..742f06b093 --- /dev/null +++ b/protocols/Gadu-Gadu/src/gg.h @@ -0,0 +1,341 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2009 Adam Strzelecki +// 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 + +// Windows headers +// Visual C++ .NET tries to include winsock.h +// which is very ver bad +#if (_MSC_VER >= 1300) +#include +#else +#include +#endif +#include +#include +#include +#include +#include +#include + +// Miranda IM headers +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 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 +extern "C" { +#include "libgadu/libgadu.h" +#include "dynstuff.h" +}; + +// Search +// Extended search result structure, used for all searches +struct GGSEARCHRESULT : public PROTOSEARCHRESULT +{ + uin_t uin; +}; + +typedef struct +{ + HANDLE hThread; + UINT dwThreadId; +} GGTHREAD; + +typedef struct +{ + uin_t *recipients; + int recipients_count; + char id[32]; + BOOL ignore; +} GGGC; + +typedef struct +{ + char id[256]; + char val[256]; +} GGTOKEN; + +#if 0 /* #ifdef DEBUGMODE */ +#define EnterCriticalSection(lpCS) {netlog(gg,"EnterCriticalSection @ %s:%d", __FILE__, __LINE__); EnterCriticalSection(lpCS);} +#define LeaveCriticalSection(lpCS) {netlog(gg,"LeaveCriticalSection @ %s:%d", __FILE__, __LINE__); LeaveCriticalSection(lpCS);} +#endif + + +// Wrappers of the old interface +#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 CLIST_INTERFACE *pcli; +extern list_t g_Instances; +extern PLUGININFOEX pluginInfo; + +// 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 gg_normalizestatus(int status); +char *gg_status2db(int status, const char *suffix); +TCHAR *ws_strerror(int code); +uint32_t swap32(uint32_t x); +const char *gg_version2string(int v); + +/* Avatar functions */ +char *gg_avatarhash(char *param); + +/* 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(); + +#define UIN2ID(uin,id) _itoa(uin,id,10) + +// Debug functions +const char *ggdebug_eventtype(gg_event *e); + +#include "gg_proto.h" + +#endif diff --git a/protocols/Gadu-Gadu/src/gg_proto.cpp b/protocols/Gadu-Gadu/src/gg_proto.cpp new file mode 100644 index 0000000000..0a0db3a173 --- /dev/null +++ b/protocols/Gadu-Gadu/src/gg_proto.cpp @@ -0,0 +1,804 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2009 Adam Strzelecki +// 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" + +GGPROTO::GGPROTO(const char* pszProtoName, const TCHAR* tszUserName) +{ + // Init mutexes + InitializeCriticalSection(&sess_mutex); + InitializeCriticalSection(&ft_mutex); + InitializeCriticalSection(&img_mutex); + InitializeCriticalSection(&modemsg_mutex); + InitializeCriticalSection(&avatar_mutex); + InitializeCriticalSection(&sessions_mutex); + + // Init instance names + m_szModuleName = mir_strdup(pszProtoName); + m_tszUserName = mir_tstrdup(tszUserName); + m_szProtoName = GGDEF_PROTONAME; + m_iVersion = 2; + + // Register netlib user + TCHAR name[128]; + mir_sntprintf(name, SIZEOF(name), TranslateT("%s connection"), m_tszUserName); + + NETLIBUSER nlu = { 0 }; + nlu.cbSize = sizeof(nlu); + nlu.flags = NUF_TCHAR | NUF_OUTGOING | NUF_INCOMING | NUF_HTTPCONNS; + nlu.szSettingsModule = m_szModuleName; + nlu.ptszDescriptiveName = name; + + netlib = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nlu); + + // Register services + createProtoService(PS_GETAVATARCAPS, &GGPROTO::getavatarcaps); + createProtoService(PS_GETAVATARINFOT, &GGPROTO::getavatarinfo); + createProtoService(PS_GETMYAVATAR, &GGPROTO::getmyavatar); + createProtoService(PS_SETMYAVATAR, &GGPROTO::setmyavatar); + + createProtoService(PS_GETMYAWAYMSG, &GGPROTO::getmyawaymsg); + createProtoService(PS_CREATEACCMGRUI, &GGPROTO::get_acc_mgr_gui); + + createProtoService(PS_LEAVECHAT, &GGPROTO::leavechat); + + // Offline contacts and clear logon time + setalloffline(); + db_set_dw(NULL, m_szModuleName, GG_KEY_LOGONTIME, 0); + + DWORD dwVersion; + if ((dwVersion = db_get_dw(NULL, m_szModuleName, GG_PLUGINVERSION, 0)) < pluginInfo.version) + cleanuplastplugin(dwVersion); + + links_instance_init(); + initavatarrequestthread(); + + TCHAR szPath[MAX_PATH]; + TCHAR *tmpPath = Utils_ReplaceVarsT( _T("%miranda_avatarcache%")); + mir_sntprintf(szPath, MAX_PATH, _T("%s\\%s"), tmpPath, m_szModuleName); + mir_free(tmpPath); + hAvatarsFolder = FoldersRegisterCustomPathT(m_szModuleName, "Avatars", szPath); + + tmpPath = Utils_ReplaceVarsT( _T("%miranda_userdata%")); + mir_sntprintf(szPath, MAX_PATH, _T("%s\\%s\\ImageCache"), tmpPath, m_szModuleName); + mir_free(tmpPath); + hImagesFolder = FoldersRegisterCustomPathT(m_szModuleName, "Images", szPath); +} + +GGPROTO::~GGPROTO() +{ +#ifdef DEBUGMODE + netlog("gg_proto_uninit(): destroying protocol interface"); +#endif + + // Destroy modules + block_uninit(); + img_destroy(); + keepalive_destroy(); + gc_destroy(); + + if (hMenuRoot) + CallService(MS_CLIST_REMOVEMAINMENUITEM, (WPARAM)hMenuRoot, 0); + + // Close handles + Netlib_CloseHandle(netlib); + + // Destroy mutexes + DeleteCriticalSection(&sess_mutex); + DeleteCriticalSection(&ft_mutex); + DeleteCriticalSection(&img_mutex); + DeleteCriticalSection(&modemsg_mutex); + DeleteCriticalSection(&avatar_mutex); + DeleteCriticalSection(&sessions_mutex); + + // Free status messages + if (modemsg.online) mir_free(modemsg.online); + if (modemsg.away) mir_free(modemsg.away); + if (modemsg.dnd) mir_free(modemsg.dnd); + if (modemsg.freechat) mir_free(modemsg.freechat); + if (modemsg.invisible) mir_free(modemsg.invisible); + if (modemsg.offline) mir_free(modemsg.offline); + + mir_free(m_szModuleName); + mir_free(m_tszUserName); +} + +////////////////////////////////////////////////////////// +// Dummies for function that have to be implemented + +HANDLE GGPROTO::AddToListByEvent(int flags, int iContact, HANDLE hDbEvent) { return NULL; } +int GGPROTO::Authorize(HANDLE hDbEvent) { return 1; } +int GGPROTO::AuthDeny(HANDLE hDbEvent, const TCHAR *szReason) { return 1; } +int GGPROTO::AuthRecv(HANDLE hContact, PROTORECVEVENT *pre) { return 1; } +int GGPROTO::AuthRequest(HANDLE hContact, const TCHAR *szMessage) { return 1; } +HANDLE GGPROTO::ChangeInfo(int iInfoType, void *pInfoData) { return NULL; } +int GGPROTO::FileResume(HANDLE hTransfer, int *action, const PROTOCHAR** szFilename) { return 1; } +HANDLE GGPROTO::SearchByEmail(const PROTOCHAR *email) { return NULL; } +int GGPROTO::RecvContacts(HANDLE hContact, PROTORECVEVENT *pre) { return 1; } +int GGPROTO::RecvUrl(HANDLE hContact, PROTORECVEVENT *pre) { return 1; } +int GGPROTO::SendContacts(HANDLE hContact, int flags, int nContacts, HANDLE *hContactsList) { return 1; } +int GGPROTO::SendUrl(HANDLE hContact, int flags, const char *url) { return 1; } +int GGPROTO::RecvAwayMsg(HANDLE hContact, int mode, PROTORECVEVENT *evt) { return 1; } +int GGPROTO::SendAwayMsg(HANDLE hContact, HANDLE hProcess, const char *msg) { return 1; } + +////////////////////////////////////////////////////////// +// when contact is added to list + +HANDLE GGPROTO::AddToList(int flags, PROTOSEARCHRESULT *psr) +{ + GGSEARCHRESULT *sr = (GGSEARCHRESULT *)psr; + uin_t uin; + + if (psr->cbSize == sizeof(GGSEARCHRESULT)) + uin = sr->uin; + else + uin = _ttoi(psr->id); + + return getcontact(uin, 1, flags & PALF_TEMPORARY ? 0 : 1, sr->nick); +} + +////////////////////////////////////////////////////////// +// checks proto capabilities + +DWORD_PTR GGPROTO::GetCaps(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 GGPROTO::GetIcon(int iconIndex) +{ + if (LOWORD(iconIndex) == PLI_PROTOCOL) + { + if (iconIndex & PLIF_ICOLIBHANDLE) + return (HICON)GetIconHandle(IDI_GG); + + BOOL big = (iconIndex & PLIF_SMALL) == 0; + HICON hIcon = LoadIconEx("main", big); + + if (iconIndex & PLIF_ICOLIB) + return hIcon; + + hIcon = CopyIcon(hIcon); + ReleaseIconEx("main", big); + return hIcon; + } + + return (HICON)NULL; +} + +////////////////////////////////////////////////////////// +// user info request + +void __cdecl GGPROTO::cmdgetinfothread(void *hContact) +{ + SleepEx(100, FALSE); + netlog("gg_cmdgetinfothread(): Failed info retreival."); + ProtoBroadcastAck(m_szModuleName, hContact, ACKTYPE_GETINFO, ACKRESULT_FAILED, (HANDLE) 1, 0); +} + +int GGPROTO::GetInfo(HANDLE hContact, int infoType) +{ + gg_pubdir50_t req; + + // Custom contact info + if (hContact) + { + if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH))) + { + forkthread(&GGPROTO::cmdgetinfothread, hContact); + return 1; + } + + // Add uin and search it + gg_pubdir50_add(req, GG_PUBDIR50_UIN, ditoa((uin_t)db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0))); + gg_pubdir50_seq_set(req, GG_SEQ_INFO); + + netlog("gg_getinfo(): Requesting user info.", req->seq); + if (isonline()) + { + EnterCriticalSection(&sess_mutex); + if (!gg_pubdir50(sess, req)) + { + LeaveCriticalSection(&sess_mutex); + forkthread(&GGPROTO::cmdgetinfothread, hContact); + return 1; + } + LeaveCriticalSection(&sess_mutex); + } + } + // Own contact info + else + { + if (!(req = gg_pubdir50_new(GG_PUBDIR50_READ))) + { + forkthread(&GGPROTO::cmdgetinfothread, hContact); + return 1; + } + + // Add seq + gg_pubdir50_seq_set(req, GG_SEQ_CHINFO); + + netlog("gg_getinfo(): Requesting owner info.", req->seq); + if (isonline()) + { + EnterCriticalSection(&sess_mutex); + if (!gg_pubdir50(sess, req)) + { + LeaveCriticalSection(&sess_mutex); + forkthread(&GGPROTO::cmdgetinfothread, hContact); + return 1; + } + LeaveCriticalSection(&sess_mutex); + } + } + netlog("gg_getinfo(): Seq %d.", req->seq); + gg_pubdir50_free(req); + + return 1; +} + +////////////////////////////////////////////////////////// +// when basic search + +void __cdecl GGPROTO::searchthread(void *) +{ + SleepEx(100, FALSE); + netlog("gg_searchthread(): Failed search."); + ProtoBroadcastAck(m_szModuleName, NULL, ACKTYPE_SEARCH, ACKRESULT_FAILED, (HANDLE)1, 0); +} + +HANDLE GGPROTO::SearchBasic(const PROTOCHAR *id) +{ + if (!isonline()) + return (HANDLE)0; + + gg_pubdir50_t req; + if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH))) { + forkthread(&GGPROTO::searchthread, NULL); + return (HANDLE)1; + } + + TCHAR *ida = mir_tstrdup(id); + + // Add uin and search it + gg_pubdir50_add(req, GG_PUBDIR50_UIN, _T2A(ida)); + gg_pubdir50_seq_set(req, GG_SEQ_SEARCH); + + mir_free(ida); + + EnterCriticalSection(&sess_mutex); + if (!gg_pubdir50(sess, req)) + { + LeaveCriticalSection(&sess_mutex); + forkthread(&GGPROTO::searchthread, NULL); + return (HANDLE)1; + } + LeaveCriticalSection(&sess_mutex); + netlog("gg_basicsearch(): Seq %d.", req->seq); + gg_pubdir50_free(req); + + return (HANDLE)1; +} + +////////////////////////////////////////////////////////// +// search by details + +HANDLE GGPROTO::SearchByName(const PROTOCHAR *nick, const PROTOCHAR *firstName, const PROTOCHAR *lastName) +{ + gg_pubdir50_t req; + unsigned long crc; + char data[512] = "\0"; + + // Check if connected and if there's a search data + if (!isonline()) + return 0; + + if (!nick && !firstName && !lastName) + return 0; + + if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH))) + { + forkthread(&GGPROTO::searchthread, NULL); + return (HANDLE)1; + } + + // Add uin and search it + if (nick) + { + char *nickA = mir_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 = mir_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 = mir_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 == last_crc && next_uin) + gg_pubdir50_add(req, GG_PUBDIR50_START, ditoa(next_uin)); + else + last_crc = crc; + + gg_pubdir50_seq_set(req, GG_SEQ_SEARCH); + + EnterCriticalSection(&sess_mutex); + if (!gg_pubdir50(sess, req)) + { + LeaveCriticalSection(&sess_mutex); + forkthread(&GGPROTO::searchthread, NULL); + return (HANDLE)1; + } + LeaveCriticalSection(&sess_mutex); + netlog("gg_searchbyname(): Seq %d.", req->seq); + gg_pubdir50_free(req); + + return (HANDLE)1; +} + +////////////////////////////////////////////////////////// +// search by advanced + +HWND GGPROTO::SearchAdvanced(HWND hwndDlg) +{ + gg_pubdir50_t req; + char text[64], data[512] = "\0"; + unsigned long crc; + + // Check if connected + if (!isonline()) return (HWND)0; + + if (!(req = gg_pubdir50_new(GG_PUBDIR50_SEARCH))) + { + forkthread(&GGPROTO::searchthread, NULL); + return (HWND)1; + } + + // Fetch search data + GetDlgItemTextA(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)); + + GetDlgItemTextA(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)); + + GetDlgItemTextA(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)); + + GetDlgItemTextA(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)); + + GetDlgItemTextA(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]; + + GetDlgItemTextA(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 == last_crc && next_uin) + gg_pubdir50_add(req, GG_PUBDIR50_START, ditoa(next_uin)); + else + last_crc = crc; + + gg_pubdir50_seq_set(req, GG_SEQ_SEARCH); + + if (isonline()) + { + EnterCriticalSection(&sess_mutex); + if (!gg_pubdir50(sess, req)) + { + LeaveCriticalSection(&sess_mutex); + forkthread(&GGPROTO::searchthread, NULL); + return (HWND)1; + } + LeaveCriticalSection(&sess_mutex); + } + netlog("gg_searchbyadvanced(): Seq %d.", req->seq); + gg_pubdir50_free(req); + + return (HWND)1; +} + +////////////////////////////////////////////////////////// +// create adv search dialog + +static 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)TranslateT("Female")); // 1 + SendDlgItemMessage(hwndDlg, IDC_GENDER, CB_ADDSTRING, 0, (LPARAM)TranslateT("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; + } + break; + } + return FALSE; +} + +HWND GGPROTO::CreateExtendedSearchUI(HWND owner) +{ + return CreateDialogParam(hInstance, + MAKEINTRESOURCE(IDD_GGADVANCEDSEARCH), owner, gg_advancedsearchdlgproc, (LPARAM)this); +} + +////////////////////////////////////////////////////////// +// when messsage received + +int GGPROTO::RecvMsg(HANDLE hContact, PROTORECVEVENT *pre) +{ + return Proto_RecvMessage(hContact, pre); +} + +////////////////////////////////////////////////////////// +// when messsage sent + +typedef struct +{ + HANDLE hContact; + int seq; +} GG_SEQ_ACK; + +void __cdecl GGPROTO::sendackthread(void *ack) +{ + SleepEx(100, FALSE); + ProtoBroadcastAck(m_szModuleName, ((GG_SEQ_ACK *)ack)->hContact, + ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, (HANDLE) ((GG_SEQ_ACK *)ack)->seq, 0); + mir_free(ack); +} + +int GGPROTO::SendMsg(HANDLE hContact, int flags, const char *msg) +{ + uin_t uin; + + if (msg && isonline() && (uin = (uin_t)db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0))) + { + int seq; + EnterCriticalSection(&sess_mutex); + seq = gg_send_message(sess, GG_CLASS_CHAT, uin, (BYTE*)msg); + LeaveCriticalSection(&sess_mutex); + if (!db_get_b(NULL, m_szModuleName, GG_KEY_MSGACK, GG_KEYDEF_MSGACK)) + { + // Auto-ack message without waiting for server ack + GG_SEQ_ACK *ack = (GG_SEQ_ACK*)mir_alloc(sizeof(GG_SEQ_ACK)); + if (ack) + { + ack->seq = seq; + ack->hContact = hContact; + forkthread(&GGPROTO::sendackthread, ack); + } + } + return seq; + } + return 0; +} + +////////////////////////////////////////////////////////// +// visible lists + +int GGPROTO::SetApparentMode(HANDLE hContact, int mode) +{ + db_set_w(hContact, m_szModuleName, GG_KEY_APPARENT, (WORD)mode); + notifyuser(hContact, 1); + return 0; +} + +////////////////////////////////////////////////////////// +// sets protocol status + +int GGPROTO::SetStatus(int iNewStatus) +{ + int nNewStatus = gg_normalizestatus(iNewStatus); + + EnterCriticalSection(&modemsg_mutex); + m_iDesiredStatus = nNewStatus; + LeaveCriticalSection(&modemsg_mutex); + + // If waiting for connection retry attempt then signal to stop that + if (hConnStopEvent) SetEvent(hConnStopEvent); + + if (m_iStatus == nNewStatus) return 0; + netlog("gg_setstatus(): PS_SETSTATUS(%d) normalized to %d.", iNewStatus, nNewStatus); + refreshstatus(nNewStatus); + + return 0; +} + +////////////////////////////////////////////////////////// +// when away message is requested + +void __cdecl GGPROTO::getawaymsgthread(void *hContact) +{ + DBVARIANT dbv; + + SleepEx(100, FALSE); + if (!db_get_s(hContact, "CList", GG_KEY_STATUSDESCR, &dbv, DBVT_TCHAR)) + { + ProtoBroadcastAck(m_szProtoName, hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE) 1, (LPARAM) dbv.ptszVal); + netlog("gg_getawaymsg(): Reading away msg <" TCHAR_STR_PARAM ">.", dbv.ptszVal); + DBFreeVariant(&dbv); + } + else + ProtoBroadcastAck(m_szProtoName, hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE) 1, (LPARAM) NULL); +} + +HANDLE GGPROTO::GetAwayMsg(HANDLE hContact) +{ + forkthread(&GGPROTO::getawaymsgthread, hContact); + return (HANDLE)1; +} + +////////////////////////////////////////////////////////// +// when away message is being set + +int GGPROTO::SetAwayMsg(int iStatus, const PROTOCHAR *msgt) +{ + int status = gg_normalizestatus(iStatus); + char **szMsg; + char *msg = mir_t2a(msgt); + + netlog("gg_setawaymsg(): PS_SETAWAYMSG(%d, \"%s\").", iStatus, msg); + + EnterCriticalSection(&modemsg_mutex); + // Select proper msg + switch(status) + { + case ID_STATUS_ONLINE: + szMsg = &modemsg.online; + break; + case ID_STATUS_AWAY: + szMsg = &modemsg.away; + break; + case ID_STATUS_DND: + szMsg = &modemsg.dnd; + break; + case ID_STATUS_FREECHAT: + szMsg = &modemsg.freechat; + break; + case ID_STATUS_INVISIBLE: + szMsg = &modemsg.invisible; + break; + default: + LeaveCriticalSection(&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 == m_iDesiredStatus && m_iDesiredStatus == m_iStatus) + { + netlog("gg_setawaymsg(): Message hasn't been changed, return."); + LeaveCriticalSection(&modemsg_mutex); + mir_free(msg); + return 0; + } + } + else + { + if (*szMsg) + mir_free(*szMsg); + *szMsg = msg && *msg ? mir_strdup(msg) : NULL; +#ifdef DEBUGMODE + netlog("gg_setawaymsg(): Message changed."); +#endif + } + LeaveCriticalSection(&modemsg_mutex); + + // Change the status if it was desired by PS_SETSTATUS + if (status == m_iDesiredStatus) + refreshstatus(status); + + mir_free(msg); + return 0; +} + +////////////////////////////////////////////////////////// +// sends a notification that the user is typing a message + +int GGPROTO::UserIsTyping(HANDLE hContact, int type) +{ + uin_t uin = db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0); + + if (!uin || !isonline()) return 0; + + if (type == PROTOTYPE_SELFTYPING_ON || type == PROTOTYPE_SELFTYPING_OFF) { + EnterCriticalSection(&sess_mutex); + gg_typing_notification(sess, uin, (type == PROTOTYPE_SELFTYPING_ON)); + LeaveCriticalSection(&sess_mutex); + } + + return 0; +} + +////////////////////////////////////////////////////////// +// Custom protocol event + +int GGPROTO::OnEvent(PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam) +{ + switch( eventType ) { + case EV_PROTO_ONLOAD: + { + hookProtoEvent(ME_OPT_INITIALISE, &GGPROTO::options_init); + hookProtoEvent(ME_USERINFO_INITIALISE, &GGPROTO::details_init); + + // Init misc stuff + gg_icolib_init(); + initpopups(); + gc_init(); + keepalive_init(); + img_init(); + block_init(); + + // Try to fetch user avatar + getUserAvatar(); + break; + } + case EV_PROTO_ONEXIT: + // Stop avatar request thread + uninitavatarrequestthread(); + + // Stop main connection session thread + threadwait(&pth_sess); + img_shutdown(); + sessions_closedlg(); + break; + + case EV_PROTO_ONOPTIONS: + return options_init(wParam, lParam); + + case EV_PROTO_ONMENU: + menus_init(); + break; + + case EV_PROTO_ONRENAME: + if (hMenuRoot) { + CLISTMENUITEM mi = {0}; + mi.cbSize = sizeof(mi); + mi.flags = CMIM_NAME | CMIF_TCHAR | CMIF_KEEPUNTRANSLATED; + mi.ptszName = m_tszUserName; + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuRoot, (LPARAM)&mi); + } + break; + + case EV_PROTO_ONCONTACTDELETED: + return contactdeleted(wParam, lParam); + + case EV_PROTO_DBSETTINGSCHANGED: + return dbsettingchanged(wParam, lParam); + } + return TRUE; +} diff --git a/protocols/Gadu-Gadu/src/gg_proto.h b/protocols/Gadu-Gadu/src/gg_proto.h new file mode 100644 index 0000000000..8765392328 --- /dev/null +++ b/protocols/Gadu-Gadu/src/gg_proto.h @@ -0,0 +1,295 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2009 Adam Strzelecki +// 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 GGPROTO_H +#define GGPROTO_H + +struct GGPROTO; +typedef void ( __cdecl GGPROTO::*GGThreadFunc )( void* ); +typedef int ( __cdecl GGPROTO::*GGEventFunc )( WPARAM, LPARAM ); +typedef INT_PTR ( __cdecl GGPROTO::*GGServiceFunc )( WPARAM, LPARAM ); + +struct GGPROTO : public PROTO_INTERFACE, public MZeroedObject +{ + GGPROTO( const char*, const TCHAR* ); + ~GGPROTO(); + + //==================================================================================== + // PROTO_INTERFACE + //==================================================================================== + + virtual HANDLE __cdecl AddToList( int flags, PROTOSEARCHRESULT* psr ); + virtual HANDLE __cdecl AddToListByEvent( int flags, int iContact, HANDLE hDbEvent ); + + virtual int __cdecl Authorize( HANDLE hDbEvent ); + virtual int __cdecl AuthDeny( HANDLE hDbEvent, const TCHAR* szReason ); + virtual int __cdecl AuthRecv( HANDLE hContact, PROTORECVEVENT* ); + virtual int __cdecl AuthRequest( HANDLE hContact, const TCHAR* szMessage ); + + virtual HANDLE __cdecl ChangeInfo( int iInfoType, void* pInfoData ); + + virtual HANDLE __cdecl FileAllow( HANDLE hContact, HANDLE hTransfer, const TCHAR* szPath ); + virtual int __cdecl FileCancel( HANDLE hContact, HANDLE hTransfer ); + virtual int __cdecl FileDeny( HANDLE hContact, HANDLE hTransfer, const TCHAR* szReason ); + virtual int __cdecl FileResume( HANDLE hTransfer, int* action, const TCHAR** szFilename ); + + virtual DWORD_PTR __cdecl GetCaps( int type, HANDLE hContact = NULL ); + virtual HICON __cdecl GetIcon( int iconIndex ); + virtual int __cdecl GetInfo( HANDLE hContact, int infoType ); + + virtual HANDLE __cdecl SearchBasic( const TCHAR* id ); + virtual HANDLE __cdecl SearchByEmail( const TCHAR* email ); + virtual HANDLE __cdecl SearchByName( const TCHAR* nick, const TCHAR* firstName, const TCHAR* lastName ); + virtual HWND __cdecl SearchAdvanced( HWND owner ); + virtual HWND __cdecl CreateExtendedSearchUI( HWND owner ); + + virtual int __cdecl RecvContacts( HANDLE hContact, PROTORECVEVENT* ); + virtual int __cdecl RecvFile( HANDLE hContact, PROTORECVFILET* ); + virtual int __cdecl RecvMsg( HANDLE hContact, PROTORECVEVENT* ); + virtual int __cdecl RecvUrl( HANDLE hContact, PROTORECVEVENT* ); + + virtual int __cdecl SendContacts( HANDLE hContact, int flags, int nContacts, HANDLE* hContactsList ); + virtual HANDLE __cdecl SendFile( HANDLE hContact, const TCHAR* szDescription, TCHAR** ppszFiles ); + virtual int __cdecl SendMsg( HANDLE hContact, int flags, const char* msg ); + virtual int __cdecl SendUrl( HANDLE hContact, int flags, const char* url ); + + virtual int __cdecl SetApparentMode( HANDLE hContact, int mode ); + virtual int __cdecl SetStatus( int iNewStatus ); + + virtual HANDLE __cdecl GetAwayMsg( HANDLE hContact ); + virtual int __cdecl RecvAwayMsg( HANDLE hContact, int mode, PROTORECVEVENT* evt ); + virtual int __cdecl SendAwayMsg( HANDLE hContact, HANDLE hProcess, const char* msg ); + virtual int __cdecl SetAwayMsg( int m_iStatus, const TCHAR* msg ); + + virtual int __cdecl UserIsTyping( HANDLE hContact, int type ); + + virtual int __cdecl OnEvent( PROTOEVENTTYPE eventType, WPARAM wParam, LPARAM lParam ); + + ////////////////////////////////////////////////////////////////////////////////////// + // Services + + INT_PTR __cdecl blockuser(WPARAM wParam, LPARAM lParam); + INT_PTR __cdecl getmyawaymsg(WPARAM wParam, LPARAM lParam); + INT_PTR __cdecl get_acc_mgr_gui(WPARAM wParam, LPARAM lParam); + INT_PTR __cdecl leavechat(WPARAM wParam, LPARAM lParam); + + void __cdecl sendackthread(void *); + void __cdecl searchthread(void *); + void __cdecl cmdgetinfothread(void *hContact); + void __cdecl getawaymsgthread(void *hContact); + void __cdecl dccmainthread(void *); + void __cdecl ftfailthread(void *param); + void __cdecl remindpasswordthread(void *param); + + ////////////////////////////////////////////////////////////////////////////////////// + + /* Helper functions */ + int status_m2gg(int status, int descr); + int status_gg2m(int status); + void checknewuser(uin_t uin, const char* passwd); + + /* Thread functions */ + void forkthread(GGThreadFunc pFunc, void *param); + HANDLE forkthreadex(GGThreadFunc pFunc, void *param, UINT *threadId); + void threadwait(GGTHREAD *thread); + + /* Global GG functions */ + void notifyuser(HANDLE hContact, int refresh); + void setalloffline(); + void disconnect(); + HANDLE getcontact(uin_t uin, int create, int inlist, TCHAR *nick); + void __cdecl mainthread(void *empty); + int isonline(); + int refreshstatus(int status); + + void broadcastnewstatus(int newStatus); + void cleanuplastplugin(DWORD version); + int contactdeleted(WPARAM wParam, LPARAM lParam); + int dbsettingchanged(WPARAM wParam, LPARAM lParam); + void notifyall(); + void changecontactstatus(uin_t uin, int status, const char *idescr, int time, uint32_t remote_ip, uint16_t remote_port, uint32_t version); + char *getstatusmsg(int status); + void dccstart(); + void dccconnect(uin_t uin); + int gettoken(GGTOKEN *token); + void parsecontacts(char *contacts); + void remindpassword(uin_t uin, const char *email); + void menus_init(); + + /* Avatar functions */ + void getAvatarFilename(HANDLE hContact, TCHAR *pszDest, int cbLen); + void getAvatar(HANDLE hContact, char *szAvatarURL); + void requestAvatar(HANDLE hContact, int iWaitFor); + void getUserAvatar(); + void setAvatar(const TCHAR *szFilename); + void getAvatarFileInfo(uin_t uin, char **avatarurl, int *type); + + INT_PTR __cdecl getavatarcaps(WPARAM wParam, LPARAM lParam); + INT_PTR __cdecl getavatarinfo(WPARAM wParam, LPARAM lParam); + INT_PTR __cdecl getmyavatar(WPARAM wParam, LPARAM lParam); + INT_PTR __cdecl setmyavatar(WPARAM wParam, LPARAM lParam); + + void initavatarrequestthread(); + void uninitavatarrequestthread(); + + void __cdecl avatarrequestthread(void*); + void __cdecl getuseravatarthread(void*); + void __cdecl setavatarthread(void*); + + /* File transfer functions */ + HANDLE fileallow(HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szPath); + int filecancel(HANDLE hContact, HANDLE hTransfer); + int filedeny(HANDLE hContact, HANDLE hTransfer, const PROTOCHAR* szReason); + int recvfile(HANDLE hContact, PROTOFILEEVENT* pre); + HANDLE sendfile(HANDLE hContact, const PROTOCHAR* szDescription, PROTOCHAR** ppszFiles); + + HANDLE dccfileallow(HANDLE hTransfer, const PROTOCHAR* szPath); + HANDLE dcc7fileallow(HANDLE hTransfer, const PROTOCHAR* szPath); + + int dccfiledeny(HANDLE hTransfer); + int dcc7filedeny(HANDLE hTransfer); + + int dccfilecancel(HANDLE hTransfer); + int dcc7filecancel(HANDLE hTransfer); + + /* Import module */ + void import_init(HGENMENU hRoot); + + INT_PTR __cdecl import_server(WPARAM wParam, LPARAM lParam); + INT_PTR __cdecl import_text(WPARAM wParam, LPARAM lParam); + INT_PTR __cdecl remove_server(WPARAM wParam, LPARAM lParam); + INT_PTR __cdecl export_server(WPARAM wParam, LPARAM lParam); + INT_PTR __cdecl export_text(WPARAM wParam, LPARAM lParam); + + /* Keep-alive module */ + void keepalive_init(); + void keepalive_destroy(); + + /* Image reception functions */ + int img_init(); + int img_destroy(); + int img_shutdown(); + int img_sendonrequest(gg_event* e); + BOOL img_opened(uin_t uin); + void *img_loadpicture(gg_event* e, TCHAR *szFileName); + int img_display(HANDLE hContact, void *img); + int img_displayasmsg(HANDLE hContact, void *img); + + void __cdecl img_dlgcallthread(void *param); + + INT_PTR __cdecl img_recvimage(WPARAM wParam, LPARAM lParam); + INT_PTR __cdecl img_sendimg(WPARAM wParam, LPARAM lParam); + + void links_instance_init(); + + /* OAuth functions */ + char *oauth_header(const char *httpmethod, const char *url); + int oauth_checktoken(int force); + int oauth_receivetoken(); + + /* UI page initializers */ + int __cdecl options_init(WPARAM wParam, LPARAM lParam); + int __cdecl details_init(WPARAM wParam, LPARAM lParam); + + /* Groupchat functions */ + int gc_init(); + void gc_menus_init(HGENMENU hRoot); + int gc_destroy(); + char * gc_getchat(uin_t sender, uin_t *recipients, int recipients_count); + GGGC *gc_lookup(char *id); + int gc_changenick(HANDLE hContact, char *pszNick); + #define UIN2ID(uin,id) _itoa(uin,id,10) + + int __cdecl gc_event(WPARAM wParam, LPARAM lParam); + + INT_PTR __cdecl gc_openconf(WPARAM wParam, LPARAM lParam); + INT_PTR __cdecl gc_clearignored(WPARAM wParam, LPARAM lParam); + + /* Popups functions */ + void initpopups(); + void showpopup(const TCHAR* nickname, const TCHAR* msg, int flags); + + /* Sessions functions */ + INT_PTR __cdecl sessions_view(WPARAM wParam, LPARAM lParam); + void sessions_updatedlg(); + BOOL sessions_closedlg(); + void sessions_menus_init(HGENMENU hRoot); + + /* Event helpers */ + void createObjService(const char* szService, GGServiceFunc serviceProc); + void createProtoService(const char* szService, GGServiceFunc serviceProc); + HANDLE hookProtoEvent(const char*, GGEventFunc); + void forkThread(GGThreadFunc, void* ); + HANDLE forkThreadEx(GGThreadFunc, void*, UINT* threadID = NULL); + + // Debug functions + int netlog(const char *fmt, ...); + + void block_init(); + void block_uninit(); + + ////////////////////////////////////////////////////////////////////////////////////// + + 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, is_list_remove, 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 hEvent; + HANDLE hConnStopEvent; + SOCKET sock; + UINT_PTR timer; + struct + { + char *online; + char *away; + char *dnd; + char *freechat; + char *invisible; + char *offline; + } modemsg; + HANDLE netlib; + HGENMENU hMenuRoot; + HGENMENU hMainMenu[7]; + HANDLE hPrebuildMenuHook; + HANDLE hBlockMenuItem; + HANDLE hImageMenuItem; + HANDLE hInstanceMenuItem; + HANDLE hAvatarsFolder; + HANDLE hImagesFolder; + HWND hwndSessionsDlg; +}; + +typedef struct +{ + int mode; + uin_t uin; + char *pass; + char *email; + GGPROTO *gg; +} GGUSERUTILDLGDATA; + +#endif diff --git a/protocols/Gadu-Gadu/src/groupchat.cpp b/protocols/Gadu-Gadu/src/groupchat.cpp new file mode 100644 index 0000000000..4089c8fac3 --- /dev/null +++ b/protocols/Gadu-Gadu/src/groupchat.cpp @@ -0,0 +1,669 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 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. +//////////////////////////////////////////////////////////////////////////////// + +#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" + +//////////////////////////////////////////////////////////////////////////////// +// Inits Gadu-Gadu groupchat module using chat.dll + +int GGPROTO::gc_init() +{ + 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 = m_tszUserName; + gcr.pszModule = m_szModuleName; + CallServiceSync(MS_GC_REGISTER, 0, (LPARAM)&gcr); + hookProtoEvent(ME_GC_EVENT, &GGPROTO::gc_event); + gc_enabled = TRUE; + // create & hook event + mir_snprintf(service, 64, GG_GC_GETCHAT, m_szModuleName); + netlog("gg_gc_init(): Registered with groupchat plugin."); + } + else + netlog("gg_gc_init(): Cannot register with groupchat plugin !!!"); + + return 1; +} + +//////////////////////////////////////////////////////////////////////////////// +// Groupchat menus initialization + +void GGPROTO::gc_menus_init(HGENMENU hRoot) +{ + if (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, m_szModuleName); + createObjService(service, &GGPROTO::gc_openconf); + mi.position = 2000050001; + mi.icolibItem = GetIconHandle(IDI_CONFERENCE); + mi.pszName = LPGEN("Open &conference..."); + mi.pszService = service; + hMainMenu[0] = Menu_AddProtoMenuItem(&mi); + + // Clear ignored conferences + mir_snprintf(service, sizeof(service), GGS_CLEAR_IGNORED, m_szModuleName); + createObjService(service, &GGPROTO::gc_clearignored); + mi.position = 2000050002; + mi.icolibItem = GetIconHandle(IDI_CLEAR_CONFERENCE); + mi.pszName = LPGEN("&Clear ignored conferences"); + mi.pszService = service; + hMainMenu[1] = Menu_AddProtoMenuItem(&mi); + } +} + +//////////////////////////////////////////////////////////////////////////////// +// Releases Gadu-Gadu groupchat module using chat.dll + +int GGPROTO::gc_destroy() +{ + list_t l; + for(l = chats; l; l = l->next) + { + GGGC *chat = (GGGC *)l->data; + if (chat->recipients) free(chat->recipients); + } + list_destroy(chats, 1); chats = NULL; + return 1; +} + +GGGC* GGPROTO::gc_lookup(char *id) +{ + GGGC *chat; + list_t l; + + for(l = chats; l; l = l->next) + { + chat = (GGGC *)l->data; + if (chat && !strcmp(chat->id, id)) + return chat; + } + + return NULL; +} + +int GGPROTO::gc_event(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 + || lstrcmpiA(gch->pDest->pszModule, m_szModuleName) + || !(uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0)) + || !(chat = gc_lookup(gch->pDest->pszID))) + return 0; + + // Window terminated + if (gch->pDest->iType == SESSION_TERMINATE) + { + HANDLE hContact = NULL; + netlog("gg_gc_event(): Terminating chat %x, id %s from chat window...", chat, gch->pDest->pszID); + // Destroy chat entry + free(chat->recipients); + list_remove(&chats, chat, 1); + // Remove contact from contact list (duh!) should be done by chat.dll !! + hContact = db_find_first(); + while (hContact) + { + DBVARIANT dbv; + if (!db_get_s(hContact, m_szModuleName, "ChatRoomID", &dbv, DBVT_ASCIIZ)) + { + if (dbv.pszVal && !strcmp(gch->pDest->pszID, dbv.pszVal)) + CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0); + DBFreeVariant(&dbv); + } + hContact = db_find_next(hContact); + } + return 1; + } + + // Message typed / send only if online + if (isonline() && (gch->pDest->iType == GC_USER_MESSAGE) && gch->pszText) + { + char id[32]; + DBVARIANT dbv; + GCDEST gcdest = {m_szModuleName, gch->pDest->pszID, GC_EVENT_MESSAGE}; + GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; + int lc; + + UIN2ID(uin, id); + + gcevent.pszUID = id; + gcevent.pszText = gch->pszText; + if (!db_get_s(NULL, m_szModuleName, GG_KEY_NICK, &dbv, DBVT_ASCIIZ)) + 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; + netlog("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(&sess_mutex); + gg_send_message_confer(sess, GG_CLASS_CHAT, chat->recipients_count, chat->recipients, (BYTE*)gch->pszText); + LeaveCriticalSection(&sess_mutex); + return 1; + } + + // Privmessage selected + if (gch->pDest->iType == GC_USER_PRIVMESS) + { + HANDLE hContact = NULL; + if ((uin = atoi(gch->pszUID)) && (hContact = getcontact(uin, 1, 0, NULL))) + CallService(MS_MSG_SENDMESSAGE, (WPARAM)hContact, (LPARAM)0); + } + netlog("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* GGPROTO::gc_getchat(uin_t sender, uin_t *recipients, int recipients_count) +{ + list_t l; int i; + GGGC *chat; + char id[32]; + uin_t uin; DBVARIANT dbv; + GCDEST gcdest = {m_szModuleName, 0, GC_EVENT_ADDGROUP}; + GCEVENT gcevent = {sizeof(GCEVENT), &gcdest}; + + netlog("gg_gc_getchat(): Count %d.", recipients_count); + if (!recipients) return NULL; + + // Look for existing chat + for(l = 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) + netlog("gg_gc_getchat(): Ignoring existing id %s, size %d.", chat->id, chat->recipients_count); + else + netlog("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(gc_id ++, chat->id); chat->ignore = FALSE; + + // Check groupchat policy (new) / only for incoming + if (sender) + { + int unknown = (getcontact(sender, 0, 0, NULL) == NULL), + unknownSender = unknown; + for(i = 0; i < recipients_count; i++) + if (!getcontact(recipients[i], 0, 0, NULL)) + unknown ++; + if ((db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_DEFAULT, GG_KEYDEF_GC_POLICY_DEFAULT) == 2) || + (db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_TOTAL, GG_KEYDEF_GC_POLICY_TOTAL) == 2 && + recipients_count >= db_get_w(NULL, m_szModuleName, GG_KEY_GC_COUNT_TOTAL, GG_KEYDEF_GC_COUNT_TOTAL)) || + (db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_UNKNOWN, GG_KEYDEF_GC_POLICY_UNKNOWN) == 2 && + unknown >= db_get_w(NULL, m_szModuleName, GG_KEY_GC_COUNT_UNKNOWN, GG_KEYDEF_GC_COUNT_UNKNOWN))) + chat->ignore = TRUE; + if (!chat->ignore && ((db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_DEFAULT, GG_KEYDEF_GC_POLICY_DEFAULT) == 1) || + (db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_TOTAL, GG_KEYDEF_GC_POLICY_TOTAL) == 1 && + recipients_count >= db_get_w(NULL, m_szModuleName, GG_KEY_GC_COUNT_TOTAL, GG_KEYDEF_GC_COUNT_TOTAL)) || + (db_get_w(NULL, m_szModuleName, GG_KEY_GC_POLICY_UNKNOWN, GG_KEYDEF_GC_POLICY_UNKNOWN) == 1 && + unknown >= db_get_w(NULL, m_szModuleName, GG_KEY_GC_COUNT_UNKNOWN, GG_KEYDEF_GC_COUNT_UNKNOWN)))) + { + TCHAR *senderName = unknownSender ? + TranslateT("Unknown") : pcli->pfnGetContactDisplayName(getcontact(sender, 0, 0, NULL), 0); + TCHAR error[256]; + mir_sntprintf(error, SIZEOF(error), TranslateT("%s has initiated conference with %d participants (%d unknowns).\nDo you want do participate ?"), + senderName, recipients_count + 1, unknown); + chat->ignore = MessageBox(NULL, error, m_tszUserName, 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; + netlog("gg_gc_getchat(): Ignoring new chat %s, count %d.", chat->id, chat->recipients_count); + list_add(&chats, chat, 0); + return NULL; + } + } + + // Create new chat window + TCHAR status[256]; + TCHAR *senderName = sender ? pcli->pfnGetContactDisplayName(getcontact(sender, 1, 0, NULL), 0) : NULL; + mir_sntprintf(status, 255, (sender) ? TranslateT("%s initiated the conference.") : TranslateT("This is my own conference."), senderName); + GCSESSION gcwindow = { 0 }; + gcwindow.cbSize = sizeof(GCSESSION); + gcwindow.iType = GCW_CHATROOM; + gcwindow.pszModule = m_szModuleName; + gcwindow.ptszName = sender ? senderName : TranslateT("Conference"); + gcwindow.pszID = chat->id; + gcwindow.dwFlags = GC_TCHAR; + gcwindow.dwItemData = (DWORD)chat; + gcwindow.ptszStatusbarText = status; + + // Here we put nice new hash sign + TCHAR *name = (TCHAR*)calloc(_tcslen(gcwindow.ptszName) + 2, sizeof(TCHAR)); + *name = '#'; _tcscpy(name + 1, gcwindow.ptszName); + gcwindow.ptszName = name; + // Create new room + if (CallServiceSync(MS_GC_NEWSESSION, 0, (LPARAM) &gcwindow)) + { + netlog("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 = GC_TCHAR | GCEF_ADDTOLOG; + gcevent.time = 0; + + // Add normal group + gcevent.ptszStatus = TranslateT("Participants"); + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); + gcdest.iType = GC_EVENT_JOIN; + + // Add myself + if (uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0)) + { + UIN2ID(uin, id); + if (!db_get_s(NULL, m_szModuleName, GG_KEY_NICK, &dbv, DBVT_TCHAR)) { + gcevent.ptszNick = NEWTSTR_ALLOCA(dbv.ptszVal); + db_free(&dbv); + } + else gcevent.ptszNick = TranslateT("Me"); + gcevent.bIsMe = 1; + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gcevent); + netlog("gg_gc_getchat(): Myself %s: %S (%S) to the list...", gcevent.pszUID, gcevent.ptszNick, gcevent.ptszStatus); + } + else netlog("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 = getcontact(chat->recipients[i], 1, 0, NULL); + UIN2ID(chat->recipients[i], id); + if (hContact && (name = pcli->pfnGetContactDisplayName(hContact, 0)) != NULL) + gcevent.ptszNick = name; + else + gcevent.ptszNick = TranslateT("'Unknown'"); + gcevent.bIsMe = 0; + gcevent.dwFlags = GC_TCHAR; + netlog("gg_gc_getchat(): Added %s: %S (%S) to the list...", gcevent.pszUID, gcevent.ptszNick, 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); + + netlog("gg_gc_getchat(): Returning new chat window %s, count %d.", chat->id, chat->recipients_count); + list_add(&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 && !lstrcmpA(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 && !lstrcmpA(szProto, gg->m_szModuleName)) + 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 = db_find_first(); + while (hContact) + { + hItem = (HANDLE)SendMessage(hwndList, CLM_FINDCONTACT, (WPARAM)hContact, 0); + if (hItem && SendMessage(hwndList, CLM_GETCHECKMARK, (WPARAM)hItem, 0)) + count++; + hContact = db_find_next(hContact); + } + 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()) + { + MessageBox(NULL, + TranslateT("You have to be connected to open new conference."), + gg->m_tszUserName, MB_OK | MB_ICONSTOP); + } + else if (hwndList && (count = gg_gc_countcheckmarks(hwndList)) >= 2) + { + // Create new participiants table + char* chat; + uin_t* participants = (uin_t*)calloc(count, sizeof(uin_t)); + HANDLE hItem, hContact = db_find_first(); + gg->netlog("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++] = db_get_dw(hMetaContact ? hMetaContact : hContact, gg->m_szModuleName, GG_KEY_UIN, 0); + } + hContact = db_find_next(hContact); + } + if (count > i) i = count; + chat = gg->gc_getchat(0, participants, count); + if (chat) + { + GCDEST gcdest = {gg->m_szModuleName, 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 = db_find_first(); + 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->m_szModuleName; + uin = (uin_t)db_get_dw(hMetaContact, gg->m_szModuleName, GG_KEY_UIN, 0); + } + else + { + szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + uin = (uin_t)db_get_dw(hContact, gg->m_szModuleName, GG_KEY_UIN, 0); + } + + if (szProto == NULL || lstrcmpA(szProto, gg->m_szModuleName) || !uin || uin == db_get_dw(NULL, gg->m_szModuleName, GG_KEY_UIN, 0)) + SendDlgItemMessage(hwndDlg, IDC_CLIST, CLM_DELETEITEM, (WPARAM)hItem, 0); + } + hContact = db_find_next(hContact); + } + } + 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 GGPROTO::gc_clearignored(WPARAM wParam, LPARAM lParam) +{ + list_t l = 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(&chats, chat, 1); + cleared = TRUE; + } + } + MessageBox( NULL, + cleared ? + TranslateT("All ignored conferences are now unignored and the conference policy will act again.") : + TranslateT("There are no ignored conferences."), + m_tszUserName, MB_OK | MB_ICONINFORMATION + ); + + return 0; +} + +INT_PTR GGPROTO::gc_openconf(WPARAM wParam, LPARAM lParam) +{ + // Check if connected + if (!isonline()) + { + MessageBox(NULL, + TranslateT("You have to be connected to open new conference."), + m_tszUserName, MB_OK | MB_ICONSTOP + ); + return 0; + } + + CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_CONFERENCE), NULL, gg_gc_openconfdlg, (LPARAM)this); + return 1; +} + +int GGPROTO::gc_changenick(HANDLE hContact, char *pszNick) +{ + list_t l; + uin_t uin = db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0); + if (!uin || !pszNick) return 0; + + netlog("gg_gc_changenick(): Nickname for uin %d changed to %s.", uin, pszNick); + // Lookup for chats having this nick + for(l = chats; l; l = l->next) { + GGGC *chat = (GGGC *)l->data; + if (chat->recipients && chat->recipients_count) + for(int 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 = m_szModuleName; + gce.pDest = &gcd; + gcd.pszID = chat->id; + gce.pszUID = id; + gce.pszText = pszNick; + netlog("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/src/icolib.cpp b/protocols/Gadu-Gadu/src/icolib.cpp new file mode 100644 index 0000000000..f99540ac76 --- /dev/null +++ b/protocols/Gadu-Gadu/src/icolib.cpp @@ -0,0 +1,109 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// 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. +//////////////////////////////////////////////////////////////////////////////// + +#include "gg.h" + +struct tagiconList +{ + char* szDescr; + 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() +{ + TCHAR szFile[MAX_PATH]; + GetModuleFileName(hInstance, szFile, MAX_PATH); + + char szSectionName[100]; + mir_snprintf(szSectionName, sizeof( szSectionName ), "%s/%s", LPGEN("Protocols"), LPGEN(GGDEF_PROTO)); + + SKINICONDESC sid = {0}; + sid.cbSize = sizeof(SKINICONDESC); + sid.ptszDefaultFile = szFile; + sid.pszSection = szSectionName; + sid.flags = SIDF_PATH_TCHAR; + + for (int 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] = Skin_AddIcon(&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/src/image.cpp b/protocols/Gadu-Gadu/src/image.cpp new file mode 100644 index 0000000000..0ad225ad06 --- /dev/null +++ b/protocols/Gadu-Gadu/src/image.cpp @@ -0,0 +1,1191 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2009 Adam Strzelecki +// 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 + +#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; + TCHAR *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); + +//////////////////////////////////////////////////////////////////////////// +// Image Module : Adding item to contact menu, creating sync objects + +int GGPROTO::img_init() +{ + 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, m_szModuleName); + createObjService(service, &GGPROTO::img_sendimg); + mi.position = -2000010000; + mi.icolibItem = GetIconHandle(IDI_IMAGE); + mi.pszName = LPGEN("&Image"); + mi.pszService = service; + mi.pszContactOwner = m_szModuleName; + hImageMenuItem = Menu_AddContactMenuItem(&mi); + + // Receive image + mir_snprintf(service, sizeof(service), GGS_RECVIMAGE, m_szModuleName); + createObjService(service, &GGPROTO::img_recvimage); + + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////// +// Image Module : closing dialogs, sync objects +int GGPROTO::img_shutdown() +{ + list_t l; +#ifdef DEBUGMODE + netlog("gg_img_shutdown(): Closing all dialogs..."); +#endif + // Rather destroy window instead of just removing structures + for (l = 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)) + netlog("gg_img_shutdown(): Image dlg %x cannot be released !!", dat->hWnd); + } + else + netlog("gg_img_shutdown(): Image dlg %x not exists, but structure does !!", dat->hWnd); + } + } + + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////// +// Image Module : destroying list + +int GGPROTO::img_destroy() +{ + // Release all dialogs + while (imagedlgs && gg_img_remove((GGIMAGEDLGDATA *)imagedlgs->data)); + + // Destroy list + list_destroy(imagedlgs, 1); + CallService(MS_CLIST_REMOVECONTACTMENUITEM, (WPARAM)hImageMenuItem, (LPARAM) 0); + + return FALSE; +} + +//////////////////////////////////////////////////////////////////////////// +// Image Window : Frees image entry structure + +static int 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; +} + +//////////////////////////////////////////////////////////////////////////// +// 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 + +TCHAR *gg_img_getfilter(TCHAR *szFilter, int nSize) +{ + TCHAR *szFilterName, *szFilterMask; + TCHAR *pFilter = szFilter; + + // Match relative to ImgDecoder presence + szFilterName = TranslateT("Image files (*.bmp,*.gif,*.jpeg,*.jpg,*.png)"); + szFilterMask = _T("*.bmp;*.gif;*.jpeg;*.jpg;*.png"); + + // Make up filter + _tcsncpy(pFilter, szFilterName, nSize); + pFilter += _tcslen(pFilter) + 1; + if (pFilter >= szFilter + nSize) return NULL; + _tcsncpy(pFilter, szFilterMask, nSize - (pFilter - szFilter)); + pFilter += _tcslen(pFilter) + 1; + if (pFilter >= szFilter + nSize) return NULL; + *pFilter = 0; + + return szFilter; +} + +//////////////////////////////////////////////////////////////////////////////// +// Save specified image entry + +int gg_img_saveimage(HWND hwnd, GGIMAGEENTRY *dat) +{ + if (!dat) + return FALSE; + + GGPROTO* gg = ((GGIMAGEDLGDATA *)GetWindowLongPtr(hwnd, GWLP_USERDATA))->gg; + + TCHAR szFilter[128]; + gg_img_getfilter(szFilter, SIZEOF(szFilter)); + + TCHAR szFileName[MAX_PATH]; + _tcsncpy(szFileName, dat->lpszFileName, SIZEOF(szFileName)); + + OPENFILENAME ofn = {0}; + 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 = _tfopen(szFileName, _T("w+b")); + if (fp) + { + fwrite(dat->lpData, dat->nSize, 1, fp); + fclose(fp); + gg->netlog("gg_img_saveimage(): Image saved to %s.", szFileName); + } + else + { + gg->netlog("gg_img_saveimage(): Cannot save image to %s.", szFileName); + MessageBox(hwnd, TranslateT("Image cannot be written to disk."), gg->m_tszUserName, 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: + { + 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 dat->gg->netlog("gg_img_dlgproc(): Creation event not found, but someone might be waiting."); + + // Making buttons flat + SendDlgItemMessage(hwndDlg, IDC_IMG_PREV, BUTTONSETASFLATBTN, TRUE, 0); + SendDlgItemMessage(hwndDlg, IDC_IMG_NEXT, BUTTONSETASFLATBTN, TRUE, 0); + SendDlgItemMessage(hwndDlg, IDC_IMG_DELETE, BUTTONSETASFLATBTN, TRUE, 0); + SendDlgItemMessage(hwndDlg, IDC_IMG_SAVE, BUTTONSETASFLATBTN, TRUE, 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"); + + TCHAR *szName = pcli->pfnGetContactDisplayName(dat->hContact, 0), szTitle[128]; + if (dat->bReceiving) + mir_sntprintf(szTitle, SIZEOF(szTitle), TranslateT("Image from %s"), szName); + else + mir_sntprintf(szTitle, SIZEOF(szTitle), TranslateT("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) + { + dat->gg->netlog("gg_img_dlgproc(): Image was not found on the list. Cannot paint the window."); + return FALSE; + } + + if (dat->bReceiving) + { + TCHAR szTitle[128]; + mir_sntprintf(szTitle, SIZEOF(szTitle), _T("%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) + { + dat->gg->netlog("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) + { + dat->gg->netlog("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()) + { + uin_t uin = (uin_t)db_get_dw(dat->hContact, gg->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, (BYTE*)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: + { + TCHAR szFilter[128]; + TCHAR 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 = TranslateT("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 *)dat->gg->img_loadpicture(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 GGPROTO::img_dlgcallthread(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(&GGPROTO::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(TCHAR *szPath, GGIMAGEENTRY *dat) +{ + struct _stat st; + + if (_tstat(szPath, &st) != 0) + return 0; + + if (st.st_size == dat->nSize) + { + FILE *fp = _tfopen(szPath, _T("rb")); + if (!fp) return 0; + char *lpData = (char*)mir_alloc(dat->nSize); + if (fread(lpData, 1, dat->nSize, fp) == dat->nSize) + { + if (dat->crc32 == gg_fix32(gg_crc32(0, (BYTE*)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 +TCHAR *gg_img_hasextension(TCHAR *filename) +{ + if (filename != NULL && *filename != '\0') + { + TCHAR *imgtype = _tcsrchr(filename, '.'); + if (imgtype != NULL) + { + size_t len = _tcslen(imgtype); + imgtype++; + if (len == 4 && (_tcsicmp(imgtype, _T("bmp")) == 0 || + _tcsicmp(imgtype, _T("gif")) == 0 || + _tcsicmp(imgtype, _T("jpg")) == 0 || + _tcsicmp(imgtype, _T("png")) == 0)) + return --imgtype; + if (len == 5 && _tcsicmp(imgtype, _T("jpeg")) == 0) + return --imgtype; + } + } + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +// Display received image using message with [img] BBCode + +int GGPROTO::img_displayasmsg(HANDLE hContact, void *img) +{ + GGIMAGEENTRY *dat = (GGIMAGEENTRY *)img; + TCHAR szPath[MAX_PATH], path[MAX_PATH], *pImgext, imgext[6]; + size_t tPathLen; + int i, res; + + if (hImagesFolder == NULL || FoldersGetCustomPathT(hImagesFolder, path, MAX_PATH, _T(""))) { + TCHAR *tmpPath = Utils_ReplaceVarsT( _T("%miranda_userdata%")); + tPathLen = mir_sntprintf(szPath, MAX_PATH, _T("%s\\%s\\ImageCache"), tmpPath, m_tszUserName); + mir_free(tmpPath); + } + else { + _tcscpy(szPath, path); + tPathLen = _tcslen(szPath); + } + + if ( _taccess(szPath, 0)) + CallService(MS_UTILS_CREATEDIRTREET, 0, (LPARAM)szPath); + + mir_sntprintf(szPath + tPathLen, MAX_PATH - tPathLen, _T("\\%s"), dat->lpszFileName); + if ((pImgext = gg_img_hasextension(szPath)) == NULL) + pImgext = szPath + _tcslen(szPath); + mir_sntprintf(imgext, SIZEOF(imgext), _T("%s"), pImgext); + for (i = 1; ; ++i) + { + if ((res = gg_img_isexists(szPath, dat)) != -1) break; + mir_sntprintf(szPath, MAX_PATH, _T("%.*s (%u)%s"), pImgext - szPath, szPath, i, imgext); + } + + if (res == 0) { + // Image file not found, thus create it + FILE *fp = _tfopen(szPath, _T("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)⪯ + 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); + netlog("gg_img_displayasmsg: Image saved to %s.", szPath); + } + else + { + netlog("gg_img_displayasmsg: Cannot save image to %s.", szPath); + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////// +// Return if uin has it's window already opened + +BOOL GGPROTO::img_opened(uin_t uin) +{ + list_t l = 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 + +int GGPROTO::img_display(HANDLE hContact, void *img) +{ + list_t l = imagedlgs; + GGIMAGEDLGDATA *dat; + + if (!img) return FALSE; + + // Look for already open dialog + EnterCriticalSection(&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(this, hContact); + dat->uin = db_get_dw(hContact, m_szModuleName, GG_KEY_UIN, 0); + + while (WaitForSingleObjectEx(dat->hEvent, INFINITE, TRUE) != WAIT_OBJECT_0); + CloseHandle(dat->hEvent); + dat->hEvent = NULL; + + list_add(&imagedlgs, dat, 0); + } + LeaveCriticalSection(&img_mutex); + + SendMessage(dat->hWnd, WM_ADDIMAGE, 0, (LPARAM)img); + if (/*db_get_b(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; +} + +//////////////////////////////////////////////////////////////////////////// +// Helper function to determine image file format and the right extension + +const TCHAR *gg_img_guessfileextension(const char *lpData) +{ + if (lpData != NULL) + { + if (memcmp(lpData, "BM", 2) == 0) + return _T(".bmp"); + if (memcmp(lpData, "GIF8", 4) == 0) + return _T(".gif"); + if (memcmp(lpData, "\xFF\xD8", 2) == 0) + return _T(".jpg"); + if (memcmp(lpData, "\x89PNG", 4) == 0) + return _T(".png"); + } + return _T(""); +} + +//////////////////////////////////////////////////////////////////////////// +// Image Window : Loading picture and sending for display + +void* GGPROTO::img_loadpicture(gg_event* e, TCHAR *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 = _tfopen(szFileName, _T("rb")); + if (!fp) { + free(dat); + netlog("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); + netlog("gg_img_loadpicture(): Zero file size \"%s\" failed.", szFileName); + return NULL; + } + // Maximum acceptable image size + if (dat->nSize > 255 * 1024) + { + fclose(fp); + free(dat); + netlog("gg_img_loadpicture(): Image size of \"%s\" exceeds 255 KB.", szFileName); + MessageBox(NULL, TranslateT("Image exceeds maximum allowed size of 255 KB."), m_tszUserName, MB_OK | MB_ICONEXCLAMATION); + return NULL; + } + fseek(fp, 0, SEEK_SET); + dat->lpData = (char*)malloc(dat->nSize); + if (fread(dat->lpData, 1, dat->nSize, fp) < dat->nSize) + { + free(dat->lpData); + fclose(fp); + free(dat); + netlog("gg_img_loadpicture(): Reading file \"%s\" failed.", szFileName); + return NULL; + } + fclose(fp); + dat->lpszFileName = _tcsdup(szFileName); + } + // Copy picture from packet + else if (e && e->event.image_reply.filename) + { + dat->nSize = e->event.image_reply.size; + dat->lpData = (char*)malloc(dat->nSize); + memcpy(dat->lpData, e->event.image_reply.image, dat->nSize); + + mir_ptr tmpFileName( mir_a2t(e->event.image_reply.filename)); + if (!gg_img_hasextension(tmpFileName)) { + // Add missing file extension + const TCHAR *szImgType = gg_img_guessfileextension(dat->lpData); + if (*szImgType) { + dat->lpszFileName = (TCHAR*)calloc(sizeof(TCHAR), lstrlen(tmpFileName) + lstrlen(szImgType) + 1); + if (dat->lpszFileName != NULL) { + _tcscpy(dat->lpszFileName, tmpFileName); + _tcscat(dat->lpszFileName, szImgType); + } + } + } + + if (dat->lpszFileName == NULL) + dat->lpszFileName = _tcsdup( _A2T( 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; + + netlog("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 GGPROTO::img_recvimage(WPARAM wParam, LPARAM lParam) +{ + CLISTEVENT *cle = (CLISTEVENT *)lParam; + GGIMAGEENTRY *img = (GGIMAGEENTRY *)cle->lParam; + + netlog("gg_img_recvimage(%x, %x): Popup new image.", wParam, lParam); + if (!img) return FALSE; + + img_display(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 = db_get_dw(dat->hContact, dat->gg->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_img_find(): Image not found on the list. It might be released before calling this function."); + return NULL; +} + + +//////////////////////////////////////////////////////////////////////////// +// Image Module : Send on Request + +BOOL GGPROTO::img_sendonrequest(gg_event* e) +{ + GGIMAGEDLGDATA *dat = gg_img_find(this, e->event.image_request.sender, e->event.image_request.crc32); + if (!this || !dat || !isonline()) + return FALSE; + + EnterCriticalSection(&sess_mutex); + gg_image_reply(sess, e->event.image_request.sender, dat->lpImages->lpszFileName, dat->lpImages->lpData, dat->lpImages->nSize); + LeaveCriticalSection(&sess_mutex); + + gg_img_remove(dat); + + return TRUE; +} + +//////////////////////////////////////////////////////////////////////////// +// Send Image : Run (Thread and main) + +INT_PTR GGPROTO::img_sendimg(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = (HANDLE)wParam; + GGIMAGEDLGDATA *dat = NULL; + + EnterCriticalSection(&img_mutex); + if (!dat) + { + dat = (GGIMAGEDLGDATA *)calloc(1, sizeof(GGIMAGEDLGDATA)); + dat->hContact = hContact; + dat->hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); + dat->gg = this; + ResetEvent(dat->hEvent); + + // Create new dialog + forkthread(&GGPROTO::img_dlgcallthread, dat); + + while (WaitForSingleObjectEx(dat->hEvent, INFINITE, TRUE) != WAIT_OBJECT_0); + CloseHandle(dat->hEvent); + dat->hEvent = NULL; + + list_add(&imagedlgs, dat, 0); + } + + // Request choose dialog + SendMessage(dat->hWnd, WM_CHOOSEIMG, 0, 0); + LeaveCriticalSection(&img_mutex); + + return 0; +} diff --git a/protocols/Gadu-Gadu/src/import.cpp b/protocols/Gadu-Gadu/src/import.cpp new file mode 100644 index 0000000000..e4ff066d11 --- /dev/null +++ b/protocols/Gadu-Gadu/src/import.cpp @@ -0,0 +1,651 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 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. +//////////////////////////////////////////////////////////////////////////////// + +#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 (db_get_s(NULL, "CListGroups", idstr, &dbv, DBVT_ASCIIZ)) 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)) + { + lstrcpynA(groupName2 + 1, groupName, (int)strlen(groupName) + 1); + + // Find an unused id + for (groupId = 0; ; groupId++) { + _itoa(groupId, groupIdStr,10); + if (db_get_s(NULL, "CListGroups", groupIdStr, &dbv, DBVT_ASCIIZ)) + break; + DBFreeVariant(&dbv); + } + + groupName2[0] = 1|GROUPF_EXPANDED; // 1 is required so we never get '\0' + db_set_s(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 = db_find_first(); + while (hContact) + { + char *szProto = (char *) CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM) hContact, 0); + if (szProto != NULL && !strcmp(szProto, gg->m_szModuleName) && !db_get_b(hContact, gg->m_szModuleName, "ChatRoom", 0)) + { + DBVARIANT dbv; + + // Readup FirstName + if (!db_get_s(hContact, gg->m_szModuleName, "FirstName", &dbv, DBVT_ASCIIZ)) + { + string_append(s, dbv.pszVal); + DBFreeVariant(&dbv); + } + string_append_c(s, ';'); + // Readup LastName + if (!db_get_s(hContact, gg->m_szModuleName, "LastName", &dbv, DBVT_ASCIIZ)) + { + string_append(s, dbv.pszVal); + DBFreeVariant(&dbv); + } + string_append_c(s, ';'); + + // Readup Nick + if (!db_get_s(hContact, "CList", "MyHandle", &dbv, DBVT_ASCIIZ) || !db_get_s(hContact, gg->m_szModuleName, GG_KEY_NICK, &dbv, DBVT_ASCIIZ)) + { + DBVARIANT dbv2; + if (!db_get_s(hContact, gg->m_szModuleName, "NickName", &dbv2, DBVT_ASCIIZ)) + { + 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 (!db_get_s(hContact, "UserInfo", "MyPhone0", &dbv, DBVT_ASCIIZ)) + { + // 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 (!db_get_s(hContact, "CList", "Group", &dbv, DBVT_ASCIIZ)) + { + string_append(s, dbv.pszVal); + DBFreeVariant(&dbv); + } + string_append_c(s, ';'); + // Readup Uin + string_append(s, ditoa(db_get_dw(hContact, gg->m_szModuleName, GG_KEY_UIN, 0))); + string_append_c(s, ';'); + // Readup Mail (fixed: uses stored editable mails) + if (!db_get_s(hContact, "UserInfo", "Mye-mail0", &dbv, DBVT_ASCIIZ)) + { + string_append(s, dbv.pszVal); + DBFreeVariant(&dbv); + } + if (cr) + string_append(s, ";0;;0;\r\n"); + else + string_append(s, ";0;;0;\n"); + } + hContact = db_find_next(hContact); + } + + contacts = string_free(s, 0); + +#ifdef DEBUGMODE + gg->netlog("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 GGPROTO::parsecontacts(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 = (char*)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 = getcontact(uin, 1, 1, _A2T(strNick)); +#ifdef DEBUGMODE + netlog("gg_parsecontacts(): Found contact %d with nickname \"%s\".", uin, strNick); +#endif + // Write group + if (hContact && strGroup) + db_set_s(hContact, "CList", "Group", CreateGroup(strGroup)); + + // Write misc data + if (hContact && strFirstName) db_set_s(hContact, m_szModuleName, "FirstName", strFirstName); + if (hContact && strLastName) db_set_s(hContact, m_szModuleName, "LastName", strLastName); + if (hContact && strPhone) db_set_s(hContact, "UserInfo", "MyPhone0", strPhone); // Store now in User Info + if (hContact && strMail) db_set_s(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 + +INT_PTR GGPROTO::import_server(WPARAM wParam, LPARAM lParam) +{ + char *password; + uin_t uin; + DBVARIANT dbv; + + // Check if connected + if (!isonline()) + { + MessageBox(NULL, + TranslateT("You have to be connected before you can import/export contacts from/to server."), + m_tszUserName, MB_OK | MB_ICONSTOP + ); + return 0; + } + + // Readup password + if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) + { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); + password = _strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + else return 0; + + if (!(uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0))) + return 0; + + // Making contacts list + EnterCriticalSection(&sess_mutex); + if (gg_userlist_request(sess, GG_USERLIST_GET, NULL) == -1) + { + TCHAR error[128]; + LeaveCriticalSection(&sess_mutex); + mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be imported because of error:\n\t%s"), _tcserror(errno)); + MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); + netlog("gg_import_server(): Cannot import list because of \"%s\".", strerror(errno)); + } + LeaveCriticalSection(&sess_mutex); + free(password); + + return 0; +} + +////////////////////////////////////////////////////////// +// remove from server + +INT_PTR GGPROTO::remove_server(WPARAM wParam, LPARAM lParam) +{ + char *password; + uin_t uin; + DBVARIANT dbv; + + // Check if connected + if (!isonline()) + { + MessageBox(NULL, + TranslateT("You have to be connected before you can import/export contacts from/to server."), + m_tszUserName, MB_OK | MB_ICONSTOP + ); + return 0; + } + + // Readup password + if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) + { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); + password = _strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + else return 0; + + if (!(uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0))) + return 0; + + // Making contacts list + EnterCriticalSection(&sess_mutex); + if (gg_userlist_request(sess, GG_USERLIST_PUT, NULL) == -1) + { + TCHAR error[128]; + LeaveCriticalSection(&sess_mutex); + mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be removeed because of error:\n\t%s"), strerror(errno)); + MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); + netlog("gg_remove_server(): Cannot remove list because of \"%s\".", strerror(errno)); + } + LeaveCriticalSection(&sess_mutex); + + // Set list removal + is_list_remove = TRUE; + free(password); + + return 0; +} + +INT_PTR GGPROTO::import_text(WPARAM wParam, LPARAM lParam) +{ + TCHAR str[MAX_PATH]; + TCHAR filter[512], *pfilter; + struct _stat st; + FILE *f; + + OPENFILENAME ofn = {0}; + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + _tcsncpy(filter, TranslateT("Text files"), SIZEOF(filter)); + _tcsncat(filter, _T(" (*.txt)"), SIZEOF(filter) - _tcslen(filter)); + pfilter = filter + _tcslen(filter) + 1; + if (pfilter >= filter + SIZEOF(filter)) + return 0; + + _tcsncpy(pfilter, _T("*.TXT"), SIZEOF(filter) - (pfilter - filter)); + pfilter = pfilter + _tcslen(pfilter) + 1; + if (pfilter >= filter + SIZEOF(filter)) + return 0; + _tcsncpy(pfilter, TranslateT("All Files"), SIZEOF(filter) - (pfilter - filter)); + _tcsncat(pfilter, _T(" (*)"), SIZEOF(filter) - (pfilter - filter) - _tcslen(pfilter)); + pfilter = pfilter + _tcslen(pfilter) + 1; + + if (pfilter >= filter + SIZEOF(filter)) + return 0; + + _tcsncpy(pfilter, _T("*"), SIZEOF(filter) - (pfilter - filter)); + pfilter = pfilter + _tcslen(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 = _T("txt"); + +#ifdef DEBUGMODE + netlog("gg_import_text()"); +#endif + if (!GetOpenFileName(&ofn)) return 0; + + f = _tfopen(str, _T("r")); + _tstat(str, &st); + + if (f && st.st_size) + { + char *contacts = (char*)malloc(st.st_size * sizeof(char)); + fread(contacts, sizeof(char), st.st_size, f); + fclose(f); + parsecontacts(contacts); + free(contacts); + + MessageBox(NULL, TranslateT("List import successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION); + } + else + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be imported from file \"%s\" because of error:\n\t%s"), str, _tcserror(errno)); + MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); + netlog("gg_import_text(): Cannot import list from file \"%S\" because of \"%s\".", str, strerror(errno)); + } + + return 0; +} + +INT_PTR GGPROTO::export_text(WPARAM wParam, LPARAM lParam) +{ + TCHAR str[MAX_PATH]; + OPENFILENAME ofn = {0}; + TCHAR filter[512], *pfilter; + FILE *f; + + _tcsncpy(str, TranslateT("contacts"), sizeof(str)); + _tcsncat(str, _T(".txt"), sizeof(str) - _tcslen(str)); + + ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; + _tcsncpy(filter, TranslateT("Text files"), SIZEOF(filter)); + _tcsncat(filter, _T(" (*.txt)"), SIZEOF(filter) - _tcslen(filter)); + pfilter = filter + _tcslen(filter) + 1; + if (pfilter >= filter + SIZEOF(filter)) + return 0; + _tcsncpy(pfilter, _T("*.TXT"), SIZEOF(filter) - (pfilter - filter)); + pfilter = pfilter + _tcslen(pfilter) + 1; + if (pfilter >= filter + SIZEOF(filter)) + return 0; + _tcsncpy(pfilter, TranslateT("All Files"), SIZEOF(filter) - (pfilter - filter)); + _tcsncat(pfilter, _T(" (*)"), SIZEOF(filter) - (pfilter - filter) - _tcslen(pfilter)); + pfilter = pfilter + _tcslen(pfilter) + 1; + if (pfilter >= filter + SIZEOF(filter)) + return 0; + _tcsncpy(pfilter, _T("*"), SIZEOF(filter) - (pfilter - filter)); + pfilter = pfilter + _tcslen(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 = _T("txt"); + +#ifdef DEBUGMODE + netlog("gg_export_text(%s).", str); +#endif + if (!GetSaveFileName(&ofn)) return 0; + + if (f = _tfopen(str, _T("w"))) { + char *contacts = gg_makecontacts(this, 0); + fwrite(contacts, sizeof(char), strlen(contacts), f); + fclose(f); + free(contacts); + + MessageBox(NULL, TranslateT("List export successful."), m_tszUserName, MB_OK | MB_ICONINFORMATION); + } + else + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be exported to file \"%s\" because of error:\n\t%s"), str, _tcserror(errno)); + MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); + netlog("gg_import_text(): Cannot export list to file \"%s\" because of \"%s\".", str, strerror(errno)); + } + + return 0; +} + +////////////////////////////////////////////////////////// +// export to server + +INT_PTR GGPROTO::export_server(WPARAM wParam, LPARAM lParam) +{ + char *password, *contacts; + uin_t uin; + DBVARIANT dbv; + + // Check if connected + if (!isonline()) + { + MessageBox(NULL, + TranslateT("You have to be connected before you can import/export contacts from/to server."), + m_tszUserName, MB_OK | MB_ICONSTOP + ); + return 0; + } + + // Readup password + if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) + { + CallService(MS_DB_CRYPT_DECODESTRING, strlen(dbv.pszVal) + 1, (LPARAM) dbv.pszVal); + password = _strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + else return 0; + + if (!(uin = db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0))) + return 0; + + // Making contacts list + contacts = gg_makecontacts(this, 1); + +#ifdef DEBUGMODE + netlog("gg_userlist_request(%s).", contacts); +#endif + + EnterCriticalSection(&sess_mutex); + if (gg_userlist_request(sess, GG_USERLIST_PUT, contacts) == -1) + { + TCHAR error[128]; + LeaveCriticalSection(&sess_mutex); + mir_sntprintf(error, SIZEOF(error), TranslateT("List cannot be exported because of error:\n\t%s"), _tcserror(errno)); + MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); + netlog("gg_export_server(): Cannot export list because of \"%s\".", strerror(errno)); + } + LeaveCriticalSection(&sess_mutex); + + // Set list removal + is_list_remove = FALSE; + free(contacts); + free(password); + + return 0; +} + +////////////////////////////////////////////////////////// +// Import menus and stuff + +void GGPROTO::import_init(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, m_szModuleName); + createObjService(service, &GGPROTO::import_server); + mi.position = 2000500001; + mi.icolibItem = GetIconHandle(IDI_IMPORT_SERVER); + mi.pszName = LPGEN("Import List From &Server"); + mi.pszService = service; + hMainMenu[2] = Menu_AddProtoMenuItem(&mi); + + // Import from textfile + mir_snprintf(service, sizeof(service), GGS_IMPORT_TEXT, m_szModuleName); + createObjService(service, &GGPROTO::import_text); + mi.position = 2000500002; + mi.icolibItem = GetIconHandle(IDI_IMPORT_TEXT); + mi.pszName = LPGEN("Import List From &Text File..."); + mi.pszService = service; + hMainMenu[3] = Menu_AddProtoMenuItem(&mi); + + // Remove from server + mir_snprintf(service, sizeof(service), GGS_REMOVE_SERVER, m_szModuleName); + createObjService(service, &GGPROTO::remove_server); + mi.position = 2000500003; + mi.icolibItem = GetIconHandle(IDI_REMOVE_SERVER); + mi.pszName = LPGEN("&Remove List From Server"); + mi.pszService = service; + hMainMenu[4] = Menu_AddProtoMenuItem(&mi); + + // Export to server + mir_snprintf(service, sizeof(service), GGS_EXPORT_SERVER, m_szModuleName); + createObjService(service, &GGPROTO::export_server); + mi.position = 2005000001; + mi.icolibItem = GetIconHandle(IDI_EXPORT_SERVER); + mi.pszName = LPGEN("Export List To &Server"); + mi.pszService = service; + hMainMenu[5] = Menu_AddProtoMenuItem(&mi); + + // Export to textfile + mir_snprintf(service, sizeof(service), GGS_EXPORT_TEXT, m_szModuleName); + createObjService(service, &GGPROTO::export_text); + mi.position = 2005000002; + mi.icolibItem = GetIconHandle(IDI_EXPORT_TEXT); + mi.pszName = LPGEN("Export List To &Text File..."); + mi.pszService = service; + hMainMenu[6] = Menu_AddProtoMenuItem(&mi); +} diff --git a/protocols/Gadu-Gadu/src/keepalive.cpp b/protocols/Gadu-Gadu/src/keepalive.cpp new file mode 100644 index 0000000000..02b661c222 --- /dev/null +++ b/protocols/Gadu-Gadu/src/keepalive.cpp @@ -0,0 +1,92 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 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. +//////////////////////////////////////////////////////////////////////////////// + +#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()) + { + #ifdef DEBUGMODE + gg->netlog("Sending keep-alive"); + #endif + EnterCriticalSection(&gg->sess_mutex); + gg_ping(gg->sess); + LeaveCriticalSection(&gg->sess_mutex); + } + } +} + +void GGPROTO::keepalive_init() +{ + if (db_get_b(NULL, m_szModuleName, 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 + netlog("gg_keepalive_init(): Initializing Timer %d", i); + #endif + timer = SetTimer(NULL, 0, 1000 * 30, gg_keepalive); + g_timers[i] = this; + } + } +} + +void GGPROTO::keepalive_destroy() +{ +#ifdef DEBUGMODE + netlog("gg_destroykeepalive(): Killing Timer"); +#endif + if (timer) + { + int i; + KillTimer(NULL, timer); + for(i = 0; i < MAX_TIMERS; i++) + if (g_timers[i] == this) { + g_timers[i] = NULL; + break; + } + timer = 0; +#ifdef DEBUGMODE + netlog("gg_destroykeepalive(): Killed Timer %d", i); +#endif + } +#ifdef DEBUGMODE + netlog("gg_destroykeepalive(): End"); +#endif +} diff --git a/protocols/Gadu-Gadu/src/libgadu/COPYING b/protocols/Gadu-Gadu/src/libgadu/COPYING new file mode 100644 index 0000000000..6e816402b7 --- /dev/null +++ b/protocols/Gadu-Gadu/src/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. + + + Copyright (C) + + 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. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/protocols/Gadu-Gadu/src/libgadu/common.c b/protocols/Gadu-Gadu/src/libgadu/common.c new file mode 100644 index 0000000000..b9b41c0547 --- /dev/null +++ b/protocols/Gadu-Gadu/src/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 + * Robert J. WoĹşny + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 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 +#ifdef _WIN32 +#include "win32.h" +#else +#include +#include +#include +#include +#ifdef sun +# include +#endif +#endif /* _WIN32 */ + +#include +#include +#ifndef _WIN32 +#include +#endif /* _WIN32 */ +#include +#include +#include +#include +#ifndef _WIN32 +#include +#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/src/libgadu/compat.h b/protocols/Gadu-Gadu/src/libgadu/compat.h new file mode 100644 index 0000000000..1aa17d1e4a --- /dev/null +++ b/protocols/Gadu-Gadu/src/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 + * Robert J. WoĹşny + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 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/src/libgadu/dcc.c b/protocols/Gadu-Gadu/src/libgadu/dcc.c new file mode 100644 index 0000000000..2fbe1b430c --- /dev/null +++ b/protocols/Gadu-Gadu/src/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 + * Tomasz ChiliĹ„ski + * Adam Wysocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/** + * \file dcc.c + * + * \brief ObsĹ‚uga poĹ‚Ä…czeĹ„ bezpoĹ›rednich do wersji Gadu-Gadu 6.x + */ + +#ifndef _WIN64 +#define _USE_32BIT_TIME_T +#endif + +#include +#include +#ifdef _WIN32 +#include "win32.h" +#else +#include +#include +#include +#include +#ifdef sun +# include +#endif +#endif /* _WIN32 */ + +#include +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#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/src/libgadu/dcc7.c b/protocols/Gadu-Gadu/src/libgadu/dcc7.c new file mode 100644 index 0000000000..f3813e6e83 --- /dev/null +++ b/protocols/Gadu-Gadu/src/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 + * Tomasz ChiliĹ„ski + * Adam Wysocki + * BartĹ‚omiej ZimoĹ„ + * + * Thanks to Jakub Zawadzki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110, + * USA. + */ + +/** + * \file dcc7.c + * + * \brief ObsĹ‚uga poĹ‚Ä…czeĹ„ bezpoĹ›rednich od wersji Gadu-Gadu 7.x + */ + +#ifndef _WIN64 +#define _USE_32BIT_TIME_T +#endif + +#include +#include +#ifdef _WIN32 +#include "win32.h" +#else +#include +#include +#include +#include +#ifdef sun +# include +#endif +#endif /* _WIN32 */ +#include + +#include +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#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/src/libgadu/events.c b/protocols/Gadu-Gadu/src/libgadu/events.c new file mode 100644 index 0000000000..a730e9d620 --- /dev/null +++ b/protocols/Gadu-Gadu/src/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 + * Robert J. WoĹşny + * Arkadiusz MiĹ›kiewicz + * Adam Wysocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 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 +#ifdef _WIN32 +#include "win32.h" +#else +#include +#include +#include +#include +#endif /* _WIN32 */ + +#include "compat.h" +#include "libgadu.h" +#include "protocol.h" +#include "internal.h" + +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#include +#endif /* _WIN32 */ +#ifndef GG_CONFIG_MIRANDA +#ifdef GG_CONFIG_HAVE_OPENSSL +# include +# include +#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, "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/src/libgadu/http.c b/protocols/Gadu-Gadu/src/libgadu/http.c new file mode 100644 index 0000000000..c070b3ee53 --- /dev/null +++ b/protocols/Gadu-Gadu/src/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 + * + * 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 +#ifdef _WIN32 +#include "win32.h" +#else +#include +#include +#include +#endif /* _WIN32 */ + +#include "compat.h" +#include "libgadu.h" +#include "resolver.h" + +#include +#include +#ifndef _WIN32 +#include +#endif /* _WIN32 */ +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#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/src/libgadu/internal.h b/protocols/Gadu-Gadu/src/libgadu/internal.h new file mode 100644 index 0000000000..0ffd39fdd5 --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/internal.h @@ -0,0 +1,48 @@ +/* coding: UTF-8 */ +/* $Id$ */ + +/* + * (C) Copyright 2009 Jakub Zawadzki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef LIBGADU_INTERNAL_H +#define LIBGADU_INTERNAL_H + +#include "libgadu.h" + +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/src/libgadu/libgadu.c b/protocols/Gadu-Gadu/src/libgadu/libgadu.c new file mode 100644 index 0000000000..f06f8a5b84 --- /dev/null +++ b/protocols/Gadu-Gadu/src/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 + * Robert J. WoĹşny + * Arkadiusz MiĹ›kiewicz + * Tomasz ChiliĹ„ski + * Adam Wysocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/** + * \file libgadu.c + * + * \brief GĹ‚Ăłwny moduĹ‚ biblioteki + */ + +#ifndef _WIN64 +#define _USE_32BIT_TIME_T +#endif + +#include +#ifdef _WIN32 +#include "win32.h" +#else +#include +#include +#include +#ifdef sun +# include +#endif +#endif /* _WIN32 */ + +#include "compat.h" +#include "libgadu.h" +#include "protocol.h" +#include "resolver.h" +#include "internal.h" + +#include +#ifndef _WIN32 +#include +#endif /* _WIN32 */ +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#endif /* _WIN32 */ +#if !defined(GG_CONFIG_MIRANDA) && defined(GG_CONFIG_HAVE_OPENSSL) +# include +# include +#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[] = ""; + const int span_len = 75; + const char img_fmt[] = ""; + 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ć . */ + + 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, "", 4); + + if ((old_attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "", 4); + + if ((old_attr & GG_FONT_BOLD) != 0) + gg_append(dst, &len, "", 4); + + if (src[i] != 0) + gg_append(dst, &len, "", 7); + } + + if (((attr & GG_FONT_COLOR) != 0) && (format_idx + 3 <= format_len)) { + color = &format_[format_idx]; + format_idx += 3; + } else { + color = (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, "", 3); + + if ((attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "", 3); + + if ((attr & GG_FONT_UNDERLINE) != 0) + gg_append(dst, &len, "", 3); + + if (((attr & GG_FONT_IMAGE) != 0) && (format_idx + 10 <= format_len)) { + if (dst != NULL) { + sprintf(&dst[len], img_fmt, + format_[format_idx + 9], + format_[format_idx + 8], + format_[format_idx + 7], + format_[format_idx + 6], + format_[format_idx + 5], + format_[format_idx + 4], + format_[format_idx + 3], + format_[format_idx + 2]); + } + + len += img_len; + format_idx += 10; + } + + old_attr = attr; + } + + /* Doklej znak zachowujÄ…c htmlowe escapowanie. */ + + switch (src[i]) { + case '&': + gg_append(dst, &len, "&", 5); + break; + case '<': + gg_append(dst, &len, "<", 4); + break; + case '>': + gg_append(dst, &len, ">", 4); + break; + case '\'': + gg_append(dst, &len, "'", 6); + break; + case '\"': + gg_append(dst, &len, """, 6); + break; + case '\n': + gg_append(dst, &len, "
", 4); + break; + case '\r': + 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, "
", 4); + + if ((old_attr & GG_FONT_ITALIC) != 0) + gg_append(dst, &len, "
", 4); + + if ((old_attr & GG_FONT_BOLD) != 0) + gg_append(dst, &len, "
", 4); + + if (src[0] != 0) + gg_append(dst, &len, "
", 7); + + if (dst != NULL) + dst[len] = 0; + + return len; +} + +/** + * WysyĹ‚a wiadomość formatowanÄ… w ramach konferencji. + * + * Zwraca losowy numer sekwencyjny, ktĂłry moĹĽna zignorować albo wykorzystać + * do potwierdzenia. + * + * \param sess Struktura sesji + * \param msgclass Klasa wiadomoĹ›ci + * \param recipients_count Liczba adresatĂłw + * \param recipients WskaĹşnik do tablicy z numerami adresatĂłw + * \param message Treść wiadomoĹ›ci + * \param format Informacje o formatowaniu + * \param formatlen DĹ‚ugość informacji o formatowaniu + * + * \return Numer sekwencyjny wiadomoĹ›ci lub -1 w przypadku bĹ‚Ä™du. + * + * \ingroup messages + */ +int gg_send_message_confer_richtext(struct gg_session *sess, int msgclass, int recipients_count, uin_t *recipients, const unsigned char *message, const unsigned char *format, int formatlen) +{ + struct gg_send_msg s; + struct gg_send_msg80 s80; + struct gg_msg_recipients r; + char *cp_msg = NULL; + char *utf_msg = NULL; + char *html_msg = NULL; + int seq_no; + int i, j, k; + uin_t *recps; + + gg_debug_session(sess, GG_DEBUG_FUNCTION, "** gg_send_message_confer_richtext(%p, %d, %d, %p, %p, %p, %d);\n", sess, msgclass, recipients_count, recipients, message, format, formatlen); + + if (!sess) { + errno = EFAULT; + return -1; + } + + 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/src/libgadu/libgadu.h b/protocols/Gadu-Gadu/src/libgadu/libgadu.h new file mode 100644 index 0000000000..d273d998e1 --- /dev/null +++ b/protocols/Gadu-Gadu/src/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 + * Robert J. WoĹşny + * Arkadiusz MiĹ›kiewicz + * Tomasz ChiliĹ„ski + * Piotr Wysocki + * Dawid Jarosz + * Jakub Zawadzki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 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 +#endif + +#if defined(__cplusplus) || defined(_WIN32) +#pragma pack(push, 1) +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +/** \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 . */ +#undef GG_CONFIG_HAVE_STDINT_H + +/* Defined if uintX_t types are defined in . */ +#undef GG_CONFIG_HAVE_INTTYPES_H + +/* Defined if uintX_t types are defined in . */ +#undef GG_CONFIG_HAVE_SYS_INTTYPES_H + +/* Defined if uintX_t types are defined in . */ +#undef GG_CONFIG_HAVE_SYS_INT_TYPES_H + +/* Defined if uintX_t types are defined in . */ +#undef GG_CONFIG_HAVE_SYS_TYPES_H + +/* 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 +#endif + +#ifdef GG_CONFIG_HAVE_STDINT_H +#include +#else +# ifdef GG_CONFIG_HAVE_INTTYPES_H +# include +# else +# ifdef GG_CONFIG_HAVE_SYS_INTTYPES_H +# include +# else +# ifdef GG_CONFIG_HAVE_SYS_INT_TYPES_H +# include +# else +# ifdef GG_CONFIG_HAVE_SYS_TYPES_H +# include +# else + +#ifndef __AC_STDINT_H +#define __AC_STDINT_H + +/* ISO C 9X: 7.18 Integer types */ + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +#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 TCHAR *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 */ + 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/src/libgadu/obsolete.c b/protocols/Gadu-Gadu/src/libgadu/obsolete.c new file mode 100644 index 0000000000..f8fe4dc5de --- /dev/null +++ b/protocols/Gadu-Gadu/src/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 + * + * 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 + +#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/src/libgadu/protocol.h b/protocols/Gadu-Gadu/src/libgadu/protocol.h new file mode 100644 index 0000000000..5b4895c260 --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/protocol.h @@ -0,0 +1,277 @@ +/* coding: UTF-8 */ +/* $Id$ */ + +/* + * (C) Copyright 2009-2010 Jakub Zawadzki + * BartĹ‚omiej ZimoĹ„ + * Wojtek Kaniewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef LIBGADU_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/src/libgadu/pthread.c b/protocols/Gadu-Gadu/src/libgadu/pthread.c new file mode 100644 index 0000000000..9a8988a358 --- /dev/null +++ b/protocols/Gadu-Gadu/src/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 +#include +#include + +/****************************************/ +/* 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, (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/src/libgadu/pthread.h b/protocols/Gadu-Gadu/src/libgadu/pthread.h new file mode 100644 index 0000000000..4de1053e0e --- /dev/null +++ b/protocols/Gadu-Gadu/src/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 + +// 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/src/libgadu/pubdir.c b/protocols/Gadu-Gadu/src/libgadu/pubdir.c new file mode 100644 index 0000000000..f9d656dfe3 --- /dev/null +++ b/protocols/Gadu-Gadu/src/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 + * Dawid Jarosz + * Adam Wysocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 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 +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include "win32.h" +#define random() rand() +#else +#include +#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/src/libgadu/pubdir50.c b/protocols/Gadu-Gadu/src/libgadu/pubdir50.c new file mode 100644 index 0000000000..6914cbd01a --- /dev/null +++ b/protocols/Gadu-Gadu/src/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 + * + * 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 +#include +#include +#include + +#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/src/libgadu/resolver.c b/protocols/Gadu-Gadu/src/libgadu/resolver.c new file mode 100644 index 0000000000..1adef3ef9d --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/resolver.c @@ -0,0 +1,766 @@ +/* coding: UTF-8 */ +/* $Id$ */ + +/* + * (C) Copyright 2001-2009 Wojtek Kaniewski + * Robert J. WoĹşny + * Arkadiusz MiĹ›kiewicz + * Tomasz ChiliĹ„ski + * Adam Wysocki + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/** + * \file resolver.c + * + * \brief Funkcje rozwiÄ…zywania nazw + */ + +#ifdef _WIN32 +#include "win32.h" +#else +#include +#include +#include +#endif /* _WIN32 */ + +#ifndef _WIN32 +#include +#endif +#include +#include +#include +#ifndef _WIN32 +#include +#endif +#include + +#include "libgadu.h" +#include "resolver.h" +#include "compat.h" + +/** SposĂłb rozwiÄ…zywania nazw serwerĂłw */ +static gg_resolver_t gg_global_resolver_type = GG_RESOLVER_DEFAULT; + +/** Funkcja rozpoczynajÄ…ca rozwiÄ…zywanie nazwy */ +static int (*gg_global_resolver_start)(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 + +#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" — wskaĹşnik na zmiennÄ…, gdzie zostanie umieszczony deskryptor potoku + * - \c "void **priv_data" — wskaĹşnik na zmiennÄ…, gdzie moĹĽna umieĹ›cić wskaĹşnik do prywatnych danych na potrzeby rozwiÄ…zywania nazwy + * - \c "const char *name" — nazwa serwera do rozwiÄ…zania + * + * Parametry funkcji zwalniajÄ…cej zasoby wyglÄ…dajÄ… nastÄ™pujÄ…co: + * - \c "void **priv_data" — wskaĹşnik na zmiennÄ… przechowujÄ…cÄ… wskaĹşnik do prywatnych danych, naleĹĽy go ustawić na \c NULL po zakoĹ„czeniu + * - \c "int force" — flaga mĂłwiÄ…ca o tym, ĹĽe zasoby sÄ… zwalniane przed zakoĹ„czeniem rozwiÄ…zywania nazwy, np. z powodu zamkniÄ™cia sesji. + * + * WĹ‚asny kod rozwiÄ…zywania nazwy powinien stworzyć potok, parÄ™ gniazd lub + * inny deskryptor pozwalajÄ…cy na co najmniej jednostronnÄ… komunikacjÄ™ i + * przekazać go w parametrze \c fd. Po zakoĹ„czeniu rozwiÄ…zywania nazwy, + * powinien wysĹ‚ać otrzymany adres IP w postaci sieciowej (big-endian) do + * deskryptora. JeĹ›li rozwiÄ…zywanie nazwy siÄ™ nie powiedzie, naleĹĽy wysĹ‚ać + * \c INADDR_NONE. NastÄ™pnie zostanie wywoĹ‚ana funkcja zwalniajÄ…ca zasoby + * z parametrem \c force rĂłwnym \c 0. Gdyby sesja zostaĹ‚a zakoĹ„czona przed + * rozwiÄ…zaniem nazwy, np. za pomocÄ… funkcji \c gg_logoff(), funkcja + * zwalniajÄ…ca zasoby zostanie wywoĹ‚ana z parametrem \c force rĂłwnym \c 1. + * + * \return 0 jeĹ›li siÄ™ powiodĹ‚o, -1 w przypadku bĹ‚Ä™du + */ +int gg_global_set_custom_resolver(int (*resolver_start)(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/src/libgadu/resolver.h b/protocols/Gadu-Gadu/src/libgadu/resolver.h new file mode 100644 index 0000000000..145c5178a4 --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/resolver.h @@ -0,0 +1,33 @@ +/* coding: UTF-8 */ +/* $Id$ */ + +/* + * (C) Copyright 2008 Wojtek Kaniewski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#ifndef LIBGADU_RESOLVER_H +#define LIBGADU_RESOLVER_H + +#ifdef _WIN32 +#include "win32.h" +#else +#include +#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/src/libgadu/sha1.c b/protocols/Gadu-Gadu/src/libgadu/sha1.c new file mode 100644 index 0000000000..124a1cffaa --- /dev/null +++ b/protocols/Gadu-Gadu/src/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 + * + * Public domain SHA-1 implementation by Steve Reid + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License Version + * 2.1 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, + * USA. + */ + +/** + * \file sha1.c + * + * \brief Funkcje wyznaczania skrÄ‚Ĺ‚tu SHA1 + */ + +#include +#include +#ifdef _WIN32 +#include "win32.h" +#else +#include +#endif + +#include "libgadu.h" + +/** \cond ignore */ + +#if defined(GG_CONFIG_HAVE_OPENSSL) && !defined(GG_CONFIG_MIRANDA) + +#include + +#else + +/* +SHA-1 in C +By Steve Reid +100% Public Domain + +Modified by Wojtek Kaniewski for compatibility +with libgadu and OpenSSL API. + +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +/* #define LITTLE_ENDIAN * This should be #define'd if true. */ +/* #define SHA1HANDSOFF * Copies data before messing with it. */ + +#include + +typedef struct { + uint32_t state[5]; + uint32_t count[2]; + unsigned char buffer[64]; +} SHA_CTX; + +static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64]); +static void SHA1_Init(SHA_CTX* context); +static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len); +static void SHA1_Final(unsigned char digest[20], SHA_CTX* context); + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#ifndef GG_CONFIG_BIGENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ + |(rol(block->l[i],8)&0x00FF00FF)) +#else +#define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ + ^block->l[(i+2)&15]^block->l[i&15],1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); +#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); +#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); +#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); + + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +static void SHA1_Transform(uint32_t state[5], const unsigned char buffer[64]) +{ +uint32_t a, b, c, d, e; +typedef union { + unsigned char c[64]; + uint32_t l[16]; +} CHAR64LONG16; +CHAR64LONG16* block; +static unsigned char workspace[64]; + block = (CHAR64LONG16*)workspace; + memcpy(block, buffer, 64); + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +} + + +/* SHA1_Init - Initialize new context */ + +static void SHA1_Init(SHA_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +static void SHA1_Update(SHA_CTX* context, const unsigned char* data, unsigned int len) +{ +unsigned int i, j; + + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1_Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1_Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + memcpy(&context->buffer[j], &data[i], len - i); +} + + +/* Add padding and return the message digest. */ + +static void SHA1_Final(unsigned char digest[20], SHA_CTX* context) +{ +uint32_t i, j; +unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] + >> ((3-(i & 3)) * 8)) & 255); /* Endian independent */ + } + SHA1_Update(context, (unsigned char *)"\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1_Update(context, (unsigned char *)"\0", 1); + } + SHA1_Update(context, finalcount, 8); /* Should cause a SHA1_Transform() */ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i>>2] >> ((3-(i & 3)) * 8)) & 255); + } + /* Wipe variables */ + i = j = 0; + memset(context->buffer, 0, 64); + memset(context->state, 0, 20); + memset(context->count, 0, 8); + memset(&finalcount, 0, 8); +#ifdef SHA1HANDSOFF /* make SHA1_Transform overwrite it's own static vars */ + SHA1_Transform(context->state, context->buffer); +#endif +} + +#endif /* GG_CONFIG_HAVE_OPENSSL */ + +/** \endcond */ + +/** \cond internal */ + +/** + * \internal Liczy skrÄ‚Ĺ‚t SHA1 z ziarna i hasła. + * + * \param password Hasło + * \param seed Ziarno + * \param result Bufor na wynik funkcji skrÄ‚Ĺ‚tu (20 bajtÄ‚Ĺ‚w) + */ +void gg_login_hash_sha1(const char *password, uint32_t seed, uint8_t *result) +{ + SHA_CTX ctx; + + SHA1_Init(&ctx); + SHA1_Update(&ctx, (const unsigned char*) password, (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/src/libgadu/win32.c b/protocols/Gadu-Gadu/src/libgadu/win32.c new file mode 100644 index 0000000000..4f5ad3ff5d --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/win32.c @@ -0,0 +1,65 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2009 Adam Strzelecki +// 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/src/libgadu/win32.h b/protocols/Gadu-Gadu/src/libgadu/win32.h new file mode 100644 index 0000000000..d432a359ac --- /dev/null +++ b/protocols/Gadu-Gadu/src/libgadu/win32.h @@ -0,0 +1,75 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2009 Adam Strzelecki +// 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 +#include + +/* Some Visual C++ overrides having no problems with MinGW */ +#ifdef _MSC_VER +#define S_IWUSR 0x0080 +/* Make sure we included errno before that */ +#include +#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/src/links.cpp b/protocols/Gadu-Gadu/src/links.cpp new file mode 100644 index 0000000000..53770f0bcd --- /dev/null +++ b/protocols/Gadu-Gadu/src/links.cpp @@ -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 = (GGPROTO*)l->data; + + mi.flags = CMIM_FLAGS; + if (gginst->m_iStatus > ID_STATUS_OFFLINE) + { + ++items; + gg = (GGPROTO*)l->data; + mi.flags |= CMIM_ICON; + mi.hIcon = LoadSkinnedProtoIcon(gg->m_szModuleName, gg->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)¶m); + + 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(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 GGPROTO::links_instance_init() +{ + if (ServiceExists(MS_ASSOCMGR_ADDNEWURLTYPE)) + { + TMO_MenuItem tmi = {0}; + tmi.cbSize = sizeof(tmi); + tmi.flags = CMIF_TCHAR; + tmi.ownerdata = this; + tmi.position = list_count(g_Instances); + tmi.ptszName = m_tszUserName; + hInstanceMenuItem = (HANDLE)CallService(MO_ADDNEWMENUITEM, (WPARAM)hInstanceMenu, (LPARAM)&tmi); + } +} diff --git a/protocols/Gadu-Gadu/src/oauth.cpp b/protocols/Gadu-Gadu/src/oauth.cpp new file mode 100644 index 0000000000..43983edb6d --- /dev/null +++ b/protocols/Gadu-Gadu/src/oauth.cpp @@ -0,0 +1,584 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 +#include +#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(const OAUTHPARAMETER *p1, const 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(LIST ¶ms, const char *httpmethod, const char *url) +{ + char *res, *urlenc, *urlnorm; + OAUTHPARAMETER *p; + int i, ix = 0, size; + + if (httpmethod == NULL || url == NULL || !params.getCount()) 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.getCount(); i++) { + p = params[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.getCount(); i++) { + p = params[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(LIST ¶ms, const char *name) +{ + OAUTHPARAMETER *p; + int i; + + if (name == NULL) return NULL; + + for (i = 0; i < params.getCount(); i++) { + p = params[i]; + if (!strcmp(p->name, name)) + return p->value; + } + + return NULL; +} + +void oauth_setparam(LIST ¶ms, const char *name, const char *value) +{ + OAUTHPARAMETER *p; + int i; + + if (name == NULL) return; + + for (i = 0; i < params.getCount(); i++) { + p = params[i]; + if (!strcmp(p->name, name)) { + mir_free(p->value); + p->value = oauth_uri_escape(value); + return; + } + } + + p = (OAUTHPARAMETER*)mir_alloc(sizeof(OAUTHPARAMETER)); + p->name = oauth_uri_escape(name); + p->value = oauth_uri_escape(value); + params.insert(p); +} + +void oauth_freeparams(LIST ¶ms) +{ + OAUTHPARAMETER *p; + int i; + + for (i = 0; i < params.getCount(); i++) { + p = params[i]; + mir_free(p->name); + mir_free(p->value); + } +} + +int oauth_sign_request(LIST ¶ms, const char *httpmethod, const char *url, + const char *consumer_secret, const char *token_secret) +{ + char *sign = NULL, *signmethod; + + if (!params.getCount()) 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((BYTE*)text, (int)strlen(text), (BYTE*)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((BYTE*)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) +{ + int i, size; + char *res, timestamp[22], *nonce; + + if (httpmethod == NULL || url == NULL) return NULL; + + LIST oauth_parameters(1, paramsortFunc); + 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); + oauth_parameters.destroy(); + return NULL; + } + + size = 7; + for (i = 0; i < oauth_parameters.getCount(); i++) { + OAUTHPARAMETER *p = oauth_parameters[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.getCount(); i++) { + OAUTHPARAMETER *p = oauth_parameters[i]; + if (i > 0) strcat(res, ","); + strcat(res, p->name); + strcat(res, "=\""); + strcat(res, p->value); + strcat(res, "\""); + } + + oauth_freeparams(oauth_parameters); + oauth_parameters.destroy(); + return res; +} + +char* GGPROTO::oauth_header(const char *httpmethod, const char *url) +{ + char *res, uin[32], *password = NULL, *token = NULL, *token_secret = NULL; + DBVARIANT dbv; + + UIN2ID( db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0), uin); + if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { + CallService(MS_DB_CRYPT_DECODESTRING, (WPARAM)(int)strlen(dbv.pszVal) + 1, (LPARAM)dbv.pszVal); + password = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + if (!db_get_s(NULL, m_szModuleName, GG_KEY_TOKEN, &dbv, DBVT_ASCIIZ)) { + token = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + if (!db_get_s(NULL, m_szModuleName, GG_KEY_TOKENSECRET, &dbv, DBVT_ASCIIZ)) { + 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 GGPROTO::oauth_receivetoken() +{ + 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; + HANDLE nlc = NULL; + + UIN2ID( db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0), uin); + if (!db_get_s(NULL, m_szModuleName, GG_KEY_PASSWORD, &dbv, DBVT_ASCIIZ)) { + 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 + netlog("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 | NLHRF_PERSISTENT; + 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)netlib, (LPARAM)&req); + if (resp) { + nlc = resp->nlc; + if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { + HXML hXml; + TCHAR *xmlAction; + TCHAR *tag; + + xmlAction = mir_a2t(resp->pData); + tag = mir_a2t("result"); + hXml = xi.parseString(xmlAction, 0, tag); + if (hXml != NULL) { + HXML node; + + mir_free(tag); tag = mir_a2t("oauth_token"); + node = xi.getChildByPath(hXml, tag, 0); + token = node != NULL ? mir_t2a(xi.getText(node)) : NULL; + + mir_free(tag); tag = mir_a2t("oauth_token_secret"); + node = xi.getChildByPath(hXml, tag, 0); + token_secret = node != NULL ? mir_t2a(xi.getText(node)) : NULL; + + xi.destroyNode(hXml); + } + mir_free(tag); + mir_free(xmlAction); + } + else netlog("gg_oauth_receivetoken(): Invalid response code from HTTP request"); + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); + } + else netlog("gg_oauth_receivetoken(): No response from HTTP request"); + + // 2. Obtaining User Authorization + netlog("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)netlib, (LPARAM)&req); + if (resp) CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); + else netlog("gg_oauth_receivetoken(): No response from HTTP request"); + + // 3. Obtaining an Access Token + netlog("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 | NLHRF_PERSISTENT; + req.nlc = nlc; + req.headersCount = 3; + req.headers = httpHeaders; + httpHeaders[1].szName = "Authorization"; + httpHeaders[1].szValue = str; + + resp = (NETLIBHTTPREQUEST *)CallService(MS_NETLIB_HTTPTRANSACTION, (WPARAM)netlib, (LPARAM)&req); + if (resp) { + if (resp->resultCode == 200 && resp->dataLength > 0 && resp->pData) { + HXML hXml; + TCHAR *xmlAction; + TCHAR *tag; + + xmlAction = mir_a2t(resp->pData); + tag = mir_a2t("result"); + hXml = xi.parseString(xmlAction, 0, tag); + if (hXml != NULL) { + HXML node; + + mir_free(tag); tag = mir_a2t("oauth_token"); + node = xi.getChildByPath(hXml, tag, 0); + token = node != NULL ? mir_t2a(xi.getText(node)) : NULL; + + mir_free(tag); tag = mir_a2t("oauth_token_secret"); + node = xi.getChildByPath(hXml, tag, 0); + token_secret = node != NULL ? mir_t2a(xi.getText(node)) : NULL; + + xi.destroyNode(hXml); + } + mir_free(tag); + mir_free(xmlAction); + } + else netlog("gg_oauth_receivetoken(): Invalid response code from HTTP request"); + Netlib_CloseHandle(resp->nlc); + CallService(MS_NETLIB_FREEHTTPREQUESTSTRUCT, 0, (LPARAM)resp); + } + else netlog("gg_oauth_receivetoken(): No response from HTTP request"); + + mir_free(password); + mir_free(str); + + if (token != NULL && token_secret != NULL) { + db_set_s(NULL, m_szModuleName, GG_KEY_TOKEN, token); + CallService(MS_DB_CRYPT_ENCODESTRING, (WPARAM)(int)strlen(token_secret) + 1, (LPARAM) token_secret); + db_set_s(NULL, m_szModuleName, GG_KEY_TOKENSECRET, token_secret); + netlog("gg_oauth_receivetoken(): Access Token obtained successfully."); + res = 1; + } + else { + db_unset(NULL, m_szModuleName, GG_KEY_TOKEN); + db_unset(NULL, m_szModuleName, GG_KEY_TOKENSECRET); + netlog("gg_oauth_receivetoken(): Failed to obtain Access Token."); + } + mir_free(token); + mir_free(token_secret); + + return res; +} + +int GGPROTO::oauth_checktoken(int force) +{ + if (!force) { + char *token = NULL, *token_secret = NULL; + DBVARIANT dbv; + int res = 1; + + if (!db_get_s(NULL, m_szModuleName, GG_KEY_TOKEN, &dbv, DBVT_ASCIIZ)) { + token = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + if (!db_get_s(NULL, m_szModuleName, GG_KEY_TOKENSECRET, &dbv, DBVT_ASCIIZ)) { + 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 = oauth_receivetoken(); + } + + mir_free(token); + mir_free(token_secret); + + return res; + } + + return oauth_receivetoken(); +} diff --git a/protocols/Gadu-Gadu/src/ownerinfo.cpp b/protocols/Gadu-Gadu/src/ownerinfo.cpp new file mode 100644 index 0000000000..6c37cdb180 --- /dev/null +++ b/protocols/Gadu-Gadu/src/ownerinfo.cpp @@ -0,0 +1,78 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 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. +//////////////////////////////////////////////////////////////////////////////// + +#include "gg.h" + +////////////////////////////////////////////////////////// +// remind password + +typedef struct +{ + uin_t uin; + const char *email; +} GG_REMIND_PASS; + +void __cdecl GGPROTO::remindpasswordthread(void *param) +{ + // Connection handle + struct gg_http *h; + GG_REMIND_PASS *rp = (GG_REMIND_PASS *)param; + GGTOKEN token; + +#ifdef DEBUGMODE + netlog("gg_remindpasswordthread(): Starting."); +#endif + if (!rp || !rp->email || !rp->uin || !strlen(rp->email)) + { + if (rp) free(rp); + return; + } + + // Get token + if (!gettoken(&token)) return; + + if (!(h = gg_remind_passwd3(rp->uin, rp->email, token.id, token.val, 0))) + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("Password could not be reminded because of error:\n\t%s"), _tcserror(errno)); + MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); + netlog("gg_remindpasswordthread(): Password could not be reminded because of \"%s\".", strerror(errno)); + } + else + { + gg_pubdir_free(h); + netlog("gg_remindpasswordthread(): Password remind successful."); + MessageBox(NULL, TranslateT("Password was sent to your e-mail."), m_tszUserName, MB_OK | MB_ICONINFORMATION); + } + +#ifdef DEBUGMODE + netlog("gg_remindpasswordthread(): End."); +#endif + if (rp) free(rp); +} + +void GGPROTO::remindpassword(uin_t uin, const char *email) +{ + GG_REMIND_PASS *rp = (GG_REMIND_PASS*)malloc(sizeof(GG_REMIND_PASS)); + + rp->uin = uin; + rp->email = email; + forkthread(&GGPROTO::remindpasswordthread, rp); +} diff --git a/protocols/Gadu-Gadu/src/popups.cpp b/protocols/Gadu-Gadu/src/popups.cpp new file mode 100644 index 0000000000..69ac1c350d --- /dev/null +++ b/protocols/Gadu-Gadu/src/popups.cpp @@ -0,0 +1,174 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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" + +struct PopupData +{ + unsigned flags; + TCHAR* title; + TCHAR* text; + GGPROTO* gg; +}; + +///////////////////////////////////////////////////////////////////////////////////////// +// 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) + puData->gg->sessions_view(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 GGPROTO::initpopups() +{ + TCHAR szDescr[256]; + char szName[256]; + + POPUPCLASS puc = {0}; + puc.cbSize = sizeof(puc); + puc.PluginWindowProc = PopupWindowProc; + puc.flags = PCF_TCHAR; + + 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_sntprintf(szDescr, SIZEOF(szDescr), _T("%s/%s"), m_tszUserName, TranslateT("Notify")); + mir_snprintf(szName, SIZEOF(szName), "%s_%s", m_szModuleName, "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_sntprintf(szDescr, SIZEOF(szDescr), _T("%s/%s"), m_tszUserName, TranslateT("Error")); + mir_snprintf(szName, SIZEOF(szName), "%s_%s", m_szModuleName, "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->m_szModuleName, "Error"); + else + mir_snprintf(szName, SIZEOF(szName), "%s_%s", gg->m_szModuleName, "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->m_tszUserName); + while (hWnd != NULL) + { + if (FindWindowEx(hWnd, NULL, NULL, puData->text) != NULL) + { + bShow = FALSE; + break; + } + hWnd = FindWindowEx(NULL, hWnd, NULL, gg->m_tszUserName); + } + } + + 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->m_tszUserName, MB_OK | uIcon); + } + } + mir_free(puData->title); + mir_free(puData->text); + mir_free(puData); + } +} + +void GGPROTO::showpopup(const TCHAR* nickname, const TCHAR* msg, int flags) +{ + PopupData* puData; + + if (Miranda_Terminated()) return; + + puData = (PopupData*)mir_alloc(sizeof(PopupData)); + puData->flags = flags; + puData->title = mir_tstrdup(nickname); + puData->text = mir_tstrdup(msg); + puData->gg = this; + + CallFunctionAsync(sttMainThreadCallback, puData); +} diff --git a/protocols/Gadu-Gadu/src/resource.h b/protocols/Gadu-Gadu/src/resource.h new file mode 100644 index 0000000000..86ec31eadb --- /dev/null +++ b/protocols/Gadu-Gadu/src/resource.h @@ -0,0 +1,147 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 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 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/src/services.cpp b/protocols/Gadu-Gadu/src/services.cpp new file mode 100644 index 0000000000..a172d10609 --- /dev/null +++ b/protocols/Gadu-Gadu/src/services.cpp @@ -0,0 +1,330 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2009 Adam Strzelecki +// 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 + +////////////////////////////////////////////////////////// +// 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; +} + +////////////////////////////////////////////////////////// +// gets protocol status + +char* GGPROTO::getstatusmsg(int status) +{ + switch(status) { + case ID_STATUS_ONLINE: + return modemsg.online; + break; + case ID_STATUS_DND: + return modemsg.dnd; + break; + case ID_STATUS_FREECHAT: + return modemsg.freechat; + break; + case ID_STATUS_INVISIBLE: + return modemsg.invisible; + break; + case ID_STATUS_AWAY: + default: + return modemsg.away; + } +} + +////////////////////////////////////////////////////////// +// sets specified protocol status + +int GGPROTO::refreshstatus(int status) +{ + if (status == ID_STATUS_OFFLINE) + { + disconnect(); + return TRUE; + } + + if (!isonline()) + { + DWORD exitCode = 0; + GetExitCodeThread(pth_sess.hThread, &exitCode); + if (exitCode == STILL_ACTIVE) + return TRUE; +#ifdef DEBUGMODE + netlog("gg_refreshstatus(): Going to connect..."); +#endif + threadwait(&pth_sess); + pth_sess.hThread = forkthreadex(&GGPROTO::mainthread, NULL, &pth_sess.dwThreadId); + } + else + { + char *szMsg = NULL; + // Select proper msg + EnterCriticalSection(&modemsg_mutex); + szMsg = mir_strdup(getstatusmsg(status)); + LeaveCriticalSection(&modemsg_mutex); + if (szMsg) + { + netlog("gg_refreshstatus(): Setting status and away message."); + EnterCriticalSection(&sess_mutex); + gg_change_status_descr(sess, status_m2gg(status, szMsg != NULL), szMsg); + LeaveCriticalSection(&sess_mutex); + } + else + { + netlog("gg_refreshstatus(): Setting just status."); + EnterCriticalSection(&sess_mutex); + gg_change_status(sess, status_m2gg(status, 0)); + LeaveCriticalSection(&sess_mutex); + } + // Change status of the contact with our own UIN (if got yourself added to the contact list) + changecontactstatus( db_get_dw(NULL, m_szModuleName, GG_KEY_UIN, 0), status_m2gg(status, szMsg != NULL), szMsg, 0, 0, 0, 0); + broadcastnewstatus(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; + } + return ID_STATUS_AWAY; +} + +////////////////////////////////////////////////////////// +// gets avatar capabilities + +INT_PTR GGPROTO::getavatarcaps(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 db_get_b(NULL, m_szModuleName, 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 GGPROTO::getavatarinfo(WPARAM wParam, LPARAM lParam) +{ + PROTO_AVATAR_INFORMATIONT *pai = (PROTO_AVATAR_INFORMATIONT *)lParam; + char *AvatarHash = NULL, *AvatarSavedHash = NULL; + char *AvatarURL = NULL; + INT_PTR result = GAIR_NOAVATAR; + DBVARIANT dbv; + uin_t uin = (uin_t)db_get_dw(pai->hContact, m_szModuleName, GG_KEY_UIN, 0); + + netlog("gg_getavatarinfo(): Requesting avatar information for %d.", uin); + + pai->filename[0] = 0; + pai->format = PA_FORMAT_UNKNOWN; + + if (!uin || !db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) + return GAIR_NOAVATAR; + + if (!db_get_b(pai->hContact, m_szModuleName, GG_KEY_AVATARREQUESTED, GG_KEYDEF_AVATARREQUESTED)) { + requestAvatar(pai->hContact, 1); + return (wParam & GAIF_FORCE) != 0 ? GAIR_WAITFOR : GAIR_NOAVATAR; + } + db_unset(pai->hContact, m_szModuleName, GG_KEY_AVATARREQUESTED); + + pai->format = db_get_b(pai->hContact, m_szModuleName, GG_KEY_AVATARTYPE, GG_KEYDEF_AVATARTYPE); + + if (!db_get_s(pai->hContact, m_szModuleName, GG_KEY_AVATARURL, &dbv, DBVT_ASCIIZ)) { + AvatarURL = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + + if (AvatarURL != NULL && strlen(AvatarURL) > 0) { + char *AvatarName = strrchr(AvatarURL, '/'); + AvatarName++; + AvatarHash = gg_avatarhash(AvatarName); + } + + if (!db_get_s(pai->hContact, m_szModuleName, GG_KEY_AVATARHASH, &dbv, DBVT_ASCIIZ)) { + AvatarSavedHash = mir_strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + + if (AvatarHash != NULL && AvatarSavedHash != NULL) { + getAvatarFilename(pai->hContact, pai->filename, SIZEOF(pai->filename)); + if (!strcmp(AvatarHash, AvatarSavedHash) && !_taccess(pai->filename, 0)) { + result = GAIR_SUCCESS; + } + else if ((wParam & GAIF_FORCE) != 0) { + netlog("gg_getavatarinfo(): Contact %d changed avatar.", uin); + _tremove(pai->filename); + db_set_s(pai->hContact, m_szModuleName, GG_KEY_AVATARHASH, AvatarHash); + getAvatar(pai->hContact, AvatarURL); + result = GAIR_WAITFOR; + } + } + else if ((wParam & GAIF_FORCE) != 0) { + if (AvatarHash == NULL && AvatarSavedHash != NULL) { + netlog("gg_getavatarinfo(): Contact %d deleted avatar.", uin); + getAvatarFilename(pai->hContact, pai->filename, sizeof(pai->filename)); + _tremove(pai->filename); + db_unset(pai->hContact, m_szModuleName, GG_KEY_AVATARHASH); + db_unset(pai->hContact, m_szModuleName, GG_KEY_AVATARURL); + db_unset(pai->hContact, m_szModuleName, GG_KEY_AVATARTYPE); + } + else if (AvatarHash != NULL && AvatarSavedHash == NULL) { + netlog("gg_getavatarinfo(): Contact %d set avatar.", uin); + db_set_s(pai->hContact, m_szModuleName, GG_KEY_AVATARHASH, AvatarHash); + getAvatar(pai->hContact, AvatarURL); + result = GAIR_WAITFOR; + } + } + + mir_free(AvatarHash); + mir_free(AvatarSavedHash); + mir_free(AvatarURL); + + return result; +} + +////////////////////////////////////////////////////////// +// gets avatar + +INT_PTR GGPROTO::getmyavatar(WPARAM wParam, LPARAM lParam) +{ + TCHAR *szFilename = (TCHAR*)wParam; + int len = (int)lParam; + + netlog("gg_getmyavatar(): Requesting user avatar."); + + if (szFilename == NULL || len <= 0) + return -1; + + if (!db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) + return -2; + + getAvatarFilename(NULL, szFilename, len); + return _taccess(szFilename, 0); +} + +////////////////////////////////////////////////////////// +// sets avatar + +INT_PTR GGPROTO::setmyavatar(WPARAM wParam, LPARAM lParam) +{ + TCHAR *szFilename = (TCHAR*)lParam; + + if (!db_get_b(NULL, m_szModuleName, GG_KEY_ENABLEAVATARS, GG_KEYDEF_ENABLEAVATARS)) + return -2; + + if (szFilename == NULL) { + MessageBox(NULL, + TranslateT("To remove your Gadu-Gadu avatar, you must use the MojaGeneracja.pl website."), + m_tszUserName, MB_OK | MB_ICONINFORMATION); + return -1; + } + + TCHAR szMyFilename[MAX_PATH]; + getAvatarFilename(NULL, szMyFilename, SIZEOF(szMyFilename)); + if ( _tcscmp(szFilename, szMyFilename) && !CopyFile(szFilename, szMyFilename, FALSE)) { + netlog("gg_setmyavatar(): Failed to set user avatar. File %s could not be created/overwritten.", szMyFilename); + return -1; + } + + setAvatar(szMyFilename); + return 0; +} + +////////////////////////////////////////////////////////// +// gets protocol status message + +INT_PTR GGPROTO::getmyawaymsg(WPARAM wParam, LPARAM lParam) +{ + INT_PTR res = 0; + char *szMsg; + + EnterCriticalSection(&modemsg_mutex); + szMsg = getstatusmsg(wParam ? gg_normalizestatus(wParam) : m_iStatus); + if (isonline() && szMsg) + res = (lParam & SGMA_UNICODE) ? (INT_PTR)mir_a2u(szMsg) : (INT_PTR)mir_strdup(szMsg); + LeaveCriticalSection(&modemsg_mutex); + return res; +} + +////////////////////////////////////////////////////////// +// gets account manager GUI + +extern INT_PTR CALLBACK gg_acc_mgr_guidlgproc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam); + +INT_PTR GGPROTO::get_acc_mgr_gui(WPARAM wParam, LPARAM lParam) +{ + return (INT_PTR) CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_ACCMGRUI), (HWND)lParam, gg_acc_mgr_guidlgproc, (LPARAM)this); +} + +////////////////////////////////////////////////////////// +// leaves (terminates) conference + +INT_PTR GGPROTO::leavechat(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = (HANDLE)wParam; + if (hContact) + CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0); + + return 0; +} diff --git a/protocols/Gadu-Gadu/src/sessions.cpp b/protocols/Gadu-Gadu/src/sessions.cpp new file mode 100644 index 0000000000..6f047cd783 --- /dev/null +++ b/protocols/Gadu-Gadu/src/sessions.cpp @@ -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: + TranslateDialogDefault(hwndDlg); + + gg = (GGPROTO*)lParam; + gg->hwndSessionsDlg = hwndDlg; + + SetWindowLongPtr(hwndDlg, DWLP_USER, (LONG_PTR)lParam); + { + TCHAR oldTitle[256], newTitle[256]; + HANDLE hProtoAckEvent; + + GetDlgItemText(hwndDlg, IDC_HEADERBAR, oldTitle, SIZEOF(oldTitle)); + mir_sntprintf(newTitle, SIZEOF(newTitle), oldTitle, gg->m_tszUserName); + SetDlgItemText(hwndDlg, IDC_HEADERBAR, newTitle); + WindowSetIcon(hwndDlg, "sessions"); + + 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->m_szModuleName) && !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() + ? 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: + { + TCHAR 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_sntprintf(szText, SIZEOF(szText), _T("%s\t%s\t%s"), szClientName, szIP, szLoginTime); + if ((hData = GlobalAlloc(GMEM_MOVEABLE, lstrlen(szText) + 1)) != NULL) + { + lstrcpy((TCHAR*)GlobalLock(hData), szText); + GlobalUnlock(hData); + SetClipboardData(CF_TEXT, hData); + } + CloseClipboard(); + break; + } + + case 10002: + { + TCHAR szUrl[256], szIP[64]; + szIP[0] = 0; + ListView_GetItemText(hList, lvhti.iItem, 1, szIP, SIZEOF(szIP)); + mir_sntprintf(szUrl, SIZEOF(szUrl), _T("http://whois.domaintools.com/%s"), szIP); + CallService(MS_UTILS_OPENURL, OUF_TCHAR, (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 GGPROTO::sessions_view(WPARAM wParam, LPARAM lParam) +{ + if (hwndSessionsDlg && IsWindow(hwndSessionsDlg)) { + ShowWindow(hwndSessionsDlg, SW_SHOWNORMAL); + SetForegroundWindow(hwndSessionsDlg); + SetFocus(hwndSessionsDlg); + } + else CreateDialogParam(hInstance, MAKEINTRESOURCE(IDD_SESSIONS), NULL, gg_sessions_viewdlg, (LPARAM)this); + return 0; +} + +void GGPROTO::sessions_updatedlg() +{ + if (hwndSessionsDlg && IsWindow(hwndSessionsDlg)) + SendMessage(hwndSessionsDlg, WM_MULTILOGONINFO, 0, 0); +} + +BOOL GGPROTO::sessions_closedlg() +{ + if (hwndSessionsDlg && IsWindow(hwndSessionsDlg)) + return PostMessage(hwndSessionsDlg, WM_CLOSE, 0, 0); + return FALSE; +} + +void GGPROTO::sessions_menus_init(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, m_szModuleName); + createObjService(service, &GGPROTO::sessions_view); + if (hMenuRoot) + mi.position = 2050000001; + else + mi.position = 200003; + mi.icolibItem = GetIconHandle(IDI_SESSIONS); + mi.ptszName = LPGENT("Concurrent &sessions"); + mi.pszService = service; + Menu_AddProtoMenuItem(&mi); +} diff --git a/protocols/Gadu-Gadu/src/token.cpp b/protocols/Gadu-Gadu/src/token.cpp new file mode 100644 index 0000000000..d2946c12ef --- /dev/null +++ b/protocols/Gadu-Gadu/src/token.cpp @@ -0,0 +1,161 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 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. +//////////////////////////////////////////////////////////////////////////////// + +#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: + { + GetDlgItemTextA(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 GGPROTO::gettoken(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) { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("Token retrieval failed because of error:\n\t%S"), http_error_string(h ? h->error : 0)); + MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); + gg_free_pubdir(h); + return FALSE; + } + + if (!(t = (struct gg_token *)h->data) || (!h->body)) { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("Token retrieval failed because of error:\n\t%S"), http_error_string(h ? h->error : 0)); + MessageBox(NULL, error, m_tszUserName, 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 = FIF_UNKNOWN; /* detect */ + memio.flags = 0; + dat.hBitmap = (HBITMAP) CallService(MS_IMG_LOADFROMMEM, (WPARAM) &memio, 0); + if (dat.hBitmap == NULL) + { + MessageBox(NULL, TranslateT("Could not load token image."), m_tszUserName, 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/src/userutils.cpp b/protocols/Gadu-Gadu/src/userutils.cpp new file mode 100644 index 0000000000..c9681c56ef --- /dev/null +++ b/protocols/Gadu-Gadu/src/userutils.cpp @@ -0,0 +1,329 @@ +//////////////////////////////////////////////////////////////////////////////// +// Gadu-Gadu Plugin for Miranda IM +// +// Copyright (c) 2003-2006 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. +//////////////////////////////////////////////////////////////////////////////// + +#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_doregister(): Starting."); +#endif + if (!newPass || !newEmail) return NULL; + + // Load token + if (!gg->gettoken(&token)) return NULL; + + if (!(h = gg_register3(newEmail, newPass, token.id, token.val, 0)) || !(s = (gg_pubdir*)h->data) || !s->success || !s->uin) + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("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->m_tszUserName, + MB_OK | MB_ICONSTOP + ); + gg->netlog("gg_doregister(): Cannot register because of \"%s\".", strerror(errno)); + } + else + { + db_set_dw(NULL, gg->m_szModuleName, GG_KEY_UIN, s->uin); + CallService(MS_DB_CRYPT_ENCODESTRING, strlen(newPass) + 1, (LPARAM) newPass); + gg->checknewuser(s->uin, newPass); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, newPass); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, newEmail); + gg_pubdir_free(h); + gg->netlog("gg_doregister(): Account registration succesful."); + MessageBox( NULL, + TranslateT("You have registered new account.\nPlease fill up your personal details in \"M->View/Change My Details...\""), + gg->m_tszUserName, MB_OK | MB_ICONINFORMATION); + } + +#ifdef DEBUGMODE + gg->netlog("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_dounregister(): Starting."); +#endif + if (!uin || !password) return NULL; + + // Load token + if (!gg->gettoken(&token)) return NULL; + + if (!(h = gg_unregister3(uin, password, token.id, token.val, 0)) || !(s = (gg_pubdir*)h->data) || !s->success || s->uin != uin) + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("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->m_tszUserName, MB_OK | MB_ICONSTOP); + gg->netlog("gg_dounregister(): Cannot remove account because of \"%s\".", strerror(errno)); + } + else + { + gg_pubdir_free(h); + db_unset(NULL, gg->m_szModuleName, GG_KEY_PASSWORD); + db_unset(NULL, gg->m_szModuleName, GG_KEY_UIN); + gg->netlog("gg_dounregister(): Account %d has been removed.", uin); + MessageBox(NULL, TranslateT("Your account has been removed."), gg->m_tszUserName, MB_OK | MB_ICONINFORMATION); + } + +#ifdef DEBUGMODE + gg->netlog("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_dochpass(): Starting."); +#endif + if (!uin || !password || !newPass) return NULL; + + if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, &dbv_email, DBVT_ASCIIZ)) + { + strncpy(email, dbv_email.pszVal, sizeof(email)); + DBFreeVariant(&dbv_email); + } + + // Load token + if (!gg->gettoken(&token)) + return NULL; + + if (!(h = gg_change_passwd4(uin, email, password, newPass, token.id, token.val, 0)) || !(s = (gg_pubdir*)h->data) || !s->success) + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("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->m_tszUserName, MB_OK | MB_ICONSTOP); + gg->netlog("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); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, newPass); + gg->netlog("gg_dochpass(): Password change succesful."); + MessageBox(NULL, TranslateT("Your password has been changed."), gg->m_tszUserName, MB_OK | MB_ICONINFORMATION); + } + +#ifdef DEBUGMODE + gg->netlog("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_doemail(): Starting."); +#endif + if (!uin || !email || !newEmail) return NULL; + + // Load token + if (!gg->gettoken(&token)) return NULL; + + if (!(h = gg_change_passwd4(uin, newEmail, password, password, token.id, token.val, 0)) || !(s = (gg_pubdir*)h->data) || !s->success) + { + TCHAR error[128]; + mir_sntprintf(error, SIZEOF(error), TranslateT("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->m_tszUserName, MB_OK | MB_ICONSTOP); + gg->netlog("gg_dochpass(): Cannot change e-mail because of \"%s\".", strerror(errno)); + } + else + { + gg_pubdir_free(h); + db_set_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, newEmail); + gg->netlog("gg_doemail(): E-mail change succesful."); + MessageBox(NULL, TranslateT("Your e-mail has been changed."), gg->m_tszUserName, MB_OK | MB_ICONINFORMATION); + } + +#ifdef DEBUGMODE + gg->netlog("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) SetDlgItemTextA(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; + GetDlgItemTextA(hwndDlg, IDC_PASSWORD, pass, sizeof(pass)); + GetDlgItemTextA(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]; + GetDlgItemTextA(hwndDlg, IDC_PASSWORD, pass, sizeof(pass)); + GetDlgItemTextA(hwndDlg, IDC_CPASSWORD, cpass, sizeof(cpass)); + GetDlgItemTextA(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; +} + +////////////////////////////////////////////////////////// +// Hooks protocol event + +HANDLE GGPROTO::hookProtoEvent(const char* szEvent, GGEventFunc handler) +{ + return ::HookEventObj(szEvent, ( MIRANDAHOOKOBJ )*( void** )&handler, this); +} + +////////////////////////////////////////////////////////// +// Adds a new protocol specific service function + +void GGPROTO::createObjService(const char* szService, GGServiceFunc serviceProc) +{ + CreateServiceFunctionObj(szService, (MIRANDASERVICEOBJ)*( void** )&serviceProc, this); +} + +void GGPROTO::createProtoService(const char* szService, GGServiceFunc serviceProc) +{ + char str[MAXMODULELABELLENGTH]; + mir_snprintf(str, sizeof(str), "%s%s", m_szModuleName, szService); + CreateServiceFunctionObj(str, (MIRANDASERVICEOBJ)*( void** )&serviceProc, this); +} + +////////////////////////////////////////////////////////// +// Forks a thread + +void GGPROTO::forkthread(GGThreadFunc pFunc, void *param) +{ + UINT threadId; + CloseHandle( mir_forkthreadowner((pThreadFuncOwner)*(void**)&pFunc, this, param, &threadId)); +} + +////////////////////////////////////////////////////////// +// Forks a thread and returns a pseudo handle for it + +HANDLE GGPROTO::forkthreadex(GGThreadFunc pFunc, void *param, UINT *threadId) +{ + return mir_forkthreadowner((pThreadFuncOwner)*(void**)&pFunc, this, param, threadId); +} + +////////////////////////////////////////////////////////// +// Wait for thread to stop + +void GGPROTO::threadwait(GGTHREAD *thread) +{ + if (!thread->hThread) return; + while (WaitForSingleObjectEx(thread->hThread, INFINITE, TRUE) != WAIT_OBJECT_0); + CloseHandle(thread->hThread); + ZeroMemory(thread, sizeof(GGTHREAD)); +} + diff --git a/protocols/Gadu-Gadu/src/version.h b/protocols/Gadu-Gadu/src/version.h new file mode 100644 index 0000000000..ce9e719bd6 --- /dev/null +++ b/protocols/Gadu-Gadu/src/version.h @@ -0,0 +1,23 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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. +//////////////////////////////////////////////////////////////////////////////// + +#define __FILEVERSION_STRING 0,11,0,1 +#define __VERSION_STRING "0.11.0.1" +#define __VERSION_DWORD PLUGIN_MAKE_VERSION(0, 11, 0, 1) diff --git a/protocols/Gadu-Gadu/token.cpp b/protocols/Gadu-Gadu/token.cpp deleted file mode 100644 index d2946c12ef..0000000000 --- a/protocols/Gadu-Gadu/token.cpp +++ /dev/null @@ -1,161 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 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. -//////////////////////////////////////////////////////////////////////////////// - -#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: - { - GetDlgItemTextA(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 GGPROTO::gettoken(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) { - TCHAR error[128]; - mir_sntprintf(error, SIZEOF(error), TranslateT("Token retrieval failed because of error:\n\t%S"), http_error_string(h ? h->error : 0)); - MessageBox(NULL, error, m_tszUserName, MB_OK | MB_ICONSTOP); - gg_free_pubdir(h); - return FALSE; - } - - if (!(t = (struct gg_token *)h->data) || (!h->body)) { - TCHAR error[128]; - mir_sntprintf(error, SIZEOF(error), TranslateT("Token retrieval failed because of error:\n\t%S"), http_error_string(h ? h->error : 0)); - MessageBox(NULL, error, m_tszUserName, 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 = FIF_UNKNOWN; /* detect */ - memio.flags = 0; - dat.hBitmap = (HBITMAP) CallService(MS_IMG_LOADFROMMEM, (WPARAM) &memio, 0); - if (dat.hBitmap == NULL) - { - MessageBox(NULL, TranslateT("Could not load token image."), m_tszUserName, 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.cpp b/protocols/Gadu-Gadu/userutils.cpp deleted file mode 100644 index c9681c56ef..0000000000 --- a/protocols/Gadu-Gadu/userutils.cpp +++ /dev/null @@ -1,329 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda IM -// -// Copyright (c) 2003-2006 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. -//////////////////////////////////////////////////////////////////////////////// - -#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_doregister(): Starting."); -#endif - if (!newPass || !newEmail) return NULL; - - // Load token - if (!gg->gettoken(&token)) return NULL; - - if (!(h = gg_register3(newEmail, newPass, token.id, token.val, 0)) || !(s = (gg_pubdir*)h->data) || !s->success || !s->uin) - { - TCHAR error[128]; - mir_sntprintf(error, SIZEOF(error), TranslateT("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->m_tszUserName, - MB_OK | MB_ICONSTOP - ); - gg->netlog("gg_doregister(): Cannot register because of \"%s\".", strerror(errno)); - } - else - { - db_set_dw(NULL, gg->m_szModuleName, GG_KEY_UIN, s->uin); - CallService(MS_DB_CRYPT_ENCODESTRING, strlen(newPass) + 1, (LPARAM) newPass); - gg->checknewuser(s->uin, newPass); - db_set_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, newPass); - db_set_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, newEmail); - gg_pubdir_free(h); - gg->netlog("gg_doregister(): Account registration succesful."); - MessageBox( NULL, - TranslateT("You have registered new account.\nPlease fill up your personal details in \"M->View/Change My Details...\""), - gg->m_tszUserName, MB_OK | MB_ICONINFORMATION); - } - -#ifdef DEBUGMODE - gg->netlog("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_dounregister(): Starting."); -#endif - if (!uin || !password) return NULL; - - // Load token - if (!gg->gettoken(&token)) return NULL; - - if (!(h = gg_unregister3(uin, password, token.id, token.val, 0)) || !(s = (gg_pubdir*)h->data) || !s->success || s->uin != uin) - { - TCHAR error[128]; - mir_sntprintf(error, SIZEOF(error), TranslateT("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->m_tszUserName, MB_OK | MB_ICONSTOP); - gg->netlog("gg_dounregister(): Cannot remove account because of \"%s\".", strerror(errno)); - } - else - { - gg_pubdir_free(h); - db_unset(NULL, gg->m_szModuleName, GG_KEY_PASSWORD); - db_unset(NULL, gg->m_szModuleName, GG_KEY_UIN); - gg->netlog("gg_dounregister(): Account %d has been removed.", uin); - MessageBox(NULL, TranslateT("Your account has been removed."), gg->m_tszUserName, MB_OK | MB_ICONINFORMATION); - } - -#ifdef DEBUGMODE - gg->netlog("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_dochpass(): Starting."); -#endif - if (!uin || !password || !newPass) return NULL; - - if (!db_get_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, &dbv_email, DBVT_ASCIIZ)) - { - strncpy(email, dbv_email.pszVal, sizeof(email)); - DBFreeVariant(&dbv_email); - } - - // Load token - if (!gg->gettoken(&token)) - return NULL; - - if (!(h = gg_change_passwd4(uin, email, password, newPass, token.id, token.val, 0)) || !(s = (gg_pubdir*)h->data) || !s->success) - { - TCHAR error[128]; - mir_sntprintf(error, SIZEOF(error), TranslateT("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->m_tszUserName, MB_OK | MB_ICONSTOP); - gg->netlog("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); - db_set_s(NULL, gg->m_szModuleName, GG_KEY_PASSWORD, newPass); - gg->netlog("gg_dochpass(): Password change succesful."); - MessageBox(NULL, TranslateT("Your password has been changed."), gg->m_tszUserName, MB_OK | MB_ICONINFORMATION); - } - -#ifdef DEBUGMODE - gg->netlog("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_doemail(): Starting."); -#endif - if (!uin || !email || !newEmail) return NULL; - - // Load token - if (!gg->gettoken(&token)) return NULL; - - if (!(h = gg_change_passwd4(uin, newEmail, password, password, token.id, token.val, 0)) || !(s = (gg_pubdir*)h->data) || !s->success) - { - TCHAR error[128]; - mir_sntprintf(error, SIZEOF(error), TranslateT("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->m_tszUserName, MB_OK | MB_ICONSTOP); - gg->netlog("gg_dochpass(): Cannot change e-mail because of \"%s\".", strerror(errno)); - } - else - { - gg_pubdir_free(h); - db_set_s(NULL, gg->m_szModuleName, GG_KEY_EMAIL, newEmail); - gg->netlog("gg_doemail(): E-mail change succesful."); - MessageBox(NULL, TranslateT("Your e-mail has been changed."), gg->m_tszUserName, MB_OK | MB_ICONINFORMATION); - } - -#ifdef DEBUGMODE - gg->netlog("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) SetDlgItemTextA(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; - GetDlgItemTextA(hwndDlg, IDC_PASSWORD, pass, sizeof(pass)); - GetDlgItemTextA(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]; - GetDlgItemTextA(hwndDlg, IDC_PASSWORD, pass, sizeof(pass)); - GetDlgItemTextA(hwndDlg, IDC_CPASSWORD, cpass, sizeof(cpass)); - GetDlgItemTextA(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; -} - -////////////////////////////////////////////////////////// -// Hooks protocol event - -HANDLE GGPROTO::hookProtoEvent(const char* szEvent, GGEventFunc handler) -{ - return ::HookEventObj(szEvent, ( MIRANDAHOOKOBJ )*( void** )&handler, this); -} - -////////////////////////////////////////////////////////// -// Adds a new protocol specific service function - -void GGPROTO::createObjService(const char* szService, GGServiceFunc serviceProc) -{ - CreateServiceFunctionObj(szService, (MIRANDASERVICEOBJ)*( void** )&serviceProc, this); -} - -void GGPROTO::createProtoService(const char* szService, GGServiceFunc serviceProc) -{ - char str[MAXMODULELABELLENGTH]; - mir_snprintf(str, sizeof(str), "%s%s", m_szModuleName, szService); - CreateServiceFunctionObj(str, (MIRANDASERVICEOBJ)*( void** )&serviceProc, this); -} - -////////////////////////////////////////////////////////// -// Forks a thread - -void GGPROTO::forkthread(GGThreadFunc pFunc, void *param) -{ - UINT threadId; - CloseHandle( mir_forkthreadowner((pThreadFuncOwner)*(void**)&pFunc, this, param, &threadId)); -} - -////////////////////////////////////////////////////////// -// Forks a thread and returns a pseudo handle for it - -HANDLE GGPROTO::forkthreadex(GGThreadFunc pFunc, void *param, UINT *threadId) -{ - return mir_forkthreadowner((pThreadFuncOwner)*(void**)&pFunc, this, param, threadId); -} - -////////////////////////////////////////////////////////// -// Wait for thread to stop - -void GGPROTO::threadwait(GGTHREAD *thread) -{ - if (!thread->hThread) return; - while (WaitForSingleObjectEx(thread->hThread, INFINITE, TRUE) != WAIT_OBJECT_0); - CloseHandle(thread->hThread); - ZeroMemory(thread, sizeof(GGTHREAD)); -} - diff --git a/protocols/Gadu-Gadu/version.h b/protocols/Gadu-Gadu/version.h deleted file mode 100644 index ce9e719bd6..0000000000 --- a/protocols/Gadu-Gadu/version.h +++ /dev/null @@ -1,23 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// 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. -//////////////////////////////////////////////////////////////////////////////// - -#define __FILEVERSION_STRING 0,11,0,1 -#define __VERSION_STRING "0.11.0.1" -#define __VERSION_DWORD PLUGIN_MAKE_VERSION(0, 11, 0, 1) diff --git a/protocols/Gadu-Gadu/version.rc b/protocols/Gadu-Gadu/version.rc deleted file mode 100644 index f594ffa6b4..0000000000 --- a/protocols/Gadu-Gadu/version.rc +++ /dev/null @@ -1,60 +0,0 @@ -//////////////////////////////////////////////////////////////////////////////// -// Gadu-Gadu Plugin for Miranda NG -// -// 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. -//////////////////////////////////////////////////////////////////////////////// - -#ifdef APSTUDIO_INVOKED -#error this file is not editable by Microsoft Visual C++ -#endif //APSTUDIO_INVOKED - -#include -#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 NG" - 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 NG" - VALUE "ProductVersion", __VERSION_STRING - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x0, 1200 - END -END -- cgit v1.2.3