From 0e80ad0d4c150fa947849cdad07a7d0d34d7340e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20P=C3=B6sel?= Date: Mon, 18 Nov 2013 20:37:19 +0000 Subject: Add sametime protocol sources (not adopted) git-svn-id: http://svn.miranda-ng.org/main/trunk@6935 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/!NotAdopted/sametime/StdAfx.cpp | 8 + plugins/!NotAdopted/sametime/StdAfx.h | 24 + plugins/!NotAdopted/sametime/announce.ico | Bin 0 -> 1406 bytes plugins/!NotAdopted/sametime/clist_util.cpp | 23 + plugins/!NotAdopted/sametime/clist_util.h | 9 + plugins/!NotAdopted/sametime/common.cpp | 12 + plugins/!NotAdopted/sametime/common.h | 106 +++ plugins/!NotAdopted/sametime/conference.cpp | 565 +++++++++++++ plugins/!NotAdopted/sametime/conference.h | 18 + plugins/!NotAdopted/sametime/files.cpp | 421 ++++++++++ plugins/!NotAdopted/sametime/files.h | 37 + plugins/!NotAdopted/sametime/invite.ico | Bin 0 -> 318 bytes plugins/!NotAdopted/sametime/messaging.cpp | 240 ++++++ plugins/!NotAdopted/sametime/messaging.h | 18 + plugins/!NotAdopted/sametime/online.ico | Bin 0 -> 1150 bytes plugins/!NotAdopted/sametime/options.cpp | 328 ++++++++ plugins/!NotAdopted/sametime/options.h | 44 + plugins/!NotAdopted/sametime/part.ico | Bin 0 -> 2294 bytes plugins/!NotAdopted/sametime/places.cpp | 54 ++ plugins/!NotAdopted/sametime/places.h | 11 + plugins/!NotAdopted/sametime/resource.h | 57 ++ plugins/!NotAdopted/sametime/resource.rc | 175 ++++ plugins/!NotAdopted/sametime/sametime.cpp | 845 +++++++++++++++++++ plugins/!NotAdopted/sametime/sametime.h | 14 + plugins/!NotAdopted/sametime/sametime.sln | 20 + plugins/!NotAdopted/sametime/sametime.vcproj | 575 +++++++++++++ plugins/!NotAdopted/sametime/session.cpp | 566 +++++++++++++ plugins/!NotAdopted/sametime/session.h | 32 + .../!NotAdopted/sametime/session_announce_win.cpp | 136 +++ .../!NotAdopted/sametime/session_announce_win.h | 15 + plugins/!NotAdopted/sametime/userlist.cpp | 909 +++++++++++++++++++++ plugins/!NotAdopted/sametime/userlist.h | 59 ++ plugins/!NotAdopted/sametime/utils.cpp | 335 ++++++++ plugins/!NotAdopted/sametime/utils.h | 37 + 34 files changed, 5693 insertions(+) create mode 100644 plugins/!NotAdopted/sametime/StdAfx.cpp create mode 100644 plugins/!NotAdopted/sametime/StdAfx.h create mode 100644 plugins/!NotAdopted/sametime/announce.ico create mode 100644 plugins/!NotAdopted/sametime/clist_util.cpp create mode 100644 plugins/!NotAdopted/sametime/clist_util.h create mode 100644 plugins/!NotAdopted/sametime/common.cpp create mode 100644 plugins/!NotAdopted/sametime/common.h create mode 100644 plugins/!NotAdopted/sametime/conference.cpp create mode 100644 plugins/!NotAdopted/sametime/conference.h create mode 100644 plugins/!NotAdopted/sametime/files.cpp create mode 100644 plugins/!NotAdopted/sametime/files.h create mode 100644 plugins/!NotAdopted/sametime/invite.ico create mode 100644 plugins/!NotAdopted/sametime/messaging.cpp create mode 100644 plugins/!NotAdopted/sametime/messaging.h create mode 100644 plugins/!NotAdopted/sametime/online.ico create mode 100644 plugins/!NotAdopted/sametime/options.cpp create mode 100644 plugins/!NotAdopted/sametime/options.h create mode 100644 plugins/!NotAdopted/sametime/part.ico create mode 100644 plugins/!NotAdopted/sametime/places.cpp create mode 100644 plugins/!NotAdopted/sametime/places.h create mode 100644 plugins/!NotAdopted/sametime/resource.h create mode 100644 plugins/!NotAdopted/sametime/resource.rc create mode 100644 plugins/!NotAdopted/sametime/sametime.cpp create mode 100644 plugins/!NotAdopted/sametime/sametime.h create mode 100644 plugins/!NotAdopted/sametime/sametime.sln create mode 100644 plugins/!NotAdopted/sametime/sametime.vcproj create mode 100644 plugins/!NotAdopted/sametime/session.cpp create mode 100644 plugins/!NotAdopted/sametime/session.h create mode 100644 plugins/!NotAdopted/sametime/session_announce_win.cpp create mode 100644 plugins/!NotAdopted/sametime/session_announce_win.h create mode 100644 plugins/!NotAdopted/sametime/userlist.cpp create mode 100644 plugins/!NotAdopted/sametime/userlist.h create mode 100644 plugins/!NotAdopted/sametime/utils.cpp create mode 100644 plugins/!NotAdopted/sametime/utils.h (limited to 'plugins') diff --git a/plugins/!NotAdopted/sametime/StdAfx.cpp b/plugins/!NotAdopted/sametime/StdAfx.cpp new file mode 100644 index 0000000000..97f31a3db4 --- /dev/null +++ b/plugins/!NotAdopted/sametime/StdAfx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : source file that includes just the standard includes +// sametime.pch will be the pre-compiled header +// stdafx.obj will contain the pre-compiled type information + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/plugins/!NotAdopted/sametime/StdAfx.h b/plugins/!NotAdopted/sametime/StdAfx.h new file mode 100644 index 0000000000..60556805db --- /dev/null +++ b/plugins/!NotAdopted/sametime/StdAfx.h @@ -0,0 +1,24 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#if !defined(AFX_STDAFX_H__3A534A59_8FD6_49FB_BB74_4F88440A6B32__INCLUDED_) +#define AFX_STDAFX_H__3A534A59_8FD6_49FB_BB74_4F88440A6B32__INCLUDED_ + +#if _MSC_VER > 1000 +#pragma once +#endif // _MSC_VER > 1000 + + +// Insert your headers here +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#include + +// TODO: reference additional headers your program requires here + +//{{AFX_INSERT_LOCATION}} +// Microsoft Visual C++ will insert additional declarations immediately before the previous line. + +#endif // !defined(AFX_STDAFX_H__3A534A59_8FD6_49FB_BB74_4F88440A6B32__INCLUDED_) diff --git a/plugins/!NotAdopted/sametime/announce.ico b/plugins/!NotAdopted/sametime/announce.ico new file mode 100644 index 0000000000..ac9673a7dc Binary files /dev/null and b/plugins/!NotAdopted/sametime/announce.ico differ diff --git a/plugins/!NotAdopted/sametime/clist_util.cpp b/plugins/!NotAdopted/sametime/clist_util.cpp new file mode 100644 index 0000000000..013f38d144 --- /dev/null +++ b/plugins/!NotAdopted/sametime/clist_util.cpp @@ -0,0 +1,23 @@ +#include "clist_util.h" + +int GroupNameExists(const char *name, int skipGroup) +{ + char idstr[33]; + DBVARIANT dbv; + int i; + + for (i = 0;; i++) { + if (i == skipGroup) + continue; + itoa(i, idstr, 10); + if (DBGetContactSettingStringUtf(NULL, "CListGroups", idstr, &dbv)) + break; + if (strcmp(dbv.pszVal + 1, name) == 0) { + DBFreeVariant(&dbv); + return i + 1; + } + DBFreeVariant(&dbv); + } + return 0; +} + diff --git a/plugins/!NotAdopted/sametime/clist_util.h b/plugins/!NotAdopted/sametime/clist_util.h new file mode 100644 index 0000000000..5fd5aed6c6 --- /dev/null +++ b/plugins/!NotAdopted/sametime/clist_util.h @@ -0,0 +1,9 @@ +#ifndef _CLIST_UTIL_INC +#define _CLIST_UTIL_INC + +#include "common.h" + +int GroupNameExists(const char *name, int skipGroup); +int RenameGroupWithMove(int groupId, const wchar_t *szName, int move); + +#endif diff --git a/plugins/!NotAdopted/sametime/common.cpp b/plugins/!NotAdopted/sametime/common.cpp new file mode 100644 index 0000000000..c2a503f8d6 --- /dev/null +++ b/plugins/!NotAdopted/sametime/common.cpp @@ -0,0 +1,12 @@ +#include "common.h" + +int DBGetContactSettingUtf(HANDLE hContact, char *module, char *setting, DBVARIANT *dbv) { + DBCONTACTGETSETTING cgs; + cgs.szModule = module; + cgs.szSetting = setting; + cgs.pValue = dbv; + + dbv->type = DBVT_UTF8; + + return CallService(MS_DB_CONTACT_GETSETTING_STR, (WPARAM)hContact, (LPARAM)&cgs); +} \ No newline at end of file diff --git a/plugins/!NotAdopted/sametime/common.h b/plugins/!NotAdopted/sametime/common.h new file mode 100644 index 0000000000..2249b4b0da --- /dev/null +++ b/plugins/!NotAdopted/sametime/common.h @@ -0,0 +1,106 @@ +#ifndef _COMMON_H +#define _COMMON_H + +#ifdef SAMETIME_EXPORTS +#define SAMETIME_API __declspec(dllexport) +#else +#define SAMETIME_API __declspec(dllimport) +#endif +#define _WIN32_WINNT 0x400 // for QueueUserAPC + +#pragma warning( disable : 4503 4786 ) + +#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 + +#include "resource.h" + +// sametime stuff +extern "C" { +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +}; + +// globals + +extern char PROTO[64]; +extern char PROTO_GROUPS[128]; + +extern HINSTANCE hInst; + +extern PLUGINLINK *pluginLink; +extern PLUGININFOEX pluginInfo; +extern MM_INTERFACE mmi; +extern UTF8_INTERFACE utfi; + +extern HANDLE mainThread; +extern DWORD mainThreadId; + +extern "C" SAMETIME_API PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion); +extern "C" SAMETIME_API int Load(PLUGINLINK *link); +extern "C" SAMETIME_API int Unload(void); + +extern HANDLE hNetlibUser; + +static inline void NLog(char *msg) { + CallService(MS_NETLIB_LOG, (WPARAM)hNetlibUser, (LPARAM)msg); +} + +extern int previous_status, current_status; + +extern bool is_idle; + +int DBGetContactSettingUtf(HANDLE hContact, char *module, char *setting, DBVARIANT *dbv); + +void SetAllOffline(); + +extern bool unicode_chat; +extern int code_page; + +#define MAX_MESSAGE_SIZE (10 * 1024) // verified limit in official client, thx Periferral + +#endif diff --git a/plugins/!NotAdopted/sametime/conference.cpp b/plugins/!NotAdopted/sametime/conference.cpp new file mode 100644 index 0000000000..52e6a29c30 --- /dev/null +++ b/plugins/!NotAdopted/sametime/conference.cpp @@ -0,0 +1,565 @@ +#include "conference.h" + +typedef std::queue InviteQueue; + +InviteQueue invite_queue; + +mwServiceConference *service_conference = 0; + +HANDLE hGcEvent = 0; + +mwLoginInfo *my_login_info = 0; + +mwConference *my_conference = 0; + +HANDLE hChatDeletedEvent = 0, hMenuBuildEvent = 0; + +HANDLE hLeaveChatMenuItem = 0, hCreateChatMenuItem = 0; + +HICON hIconLeave = 0, hIconCreate = 0; + +#define MS_SAMETIME_MENULEAVECHAT "/LeaveChat" +#define MS_SAMETIME_MENUCREATECHAT "/CreateChat" + +void CloseMyConference() { + mwConference_destroy(my_conference, 0, Translate("I'm outa here.")); + my_conference = 0; +} + +/** triggered when we receive a conference invitation. Call + mwConference_accept to accept the invitation and join the + conference, or mwConference_close to reject the invitation. + + @param conf the newly created conference + @param inviter the indentity of the user who sent the invitation + @param invite the invitation text +*/ +void mwServiceConf_on_invited(mwConference *conf, mwLoginInfo *inviter, const char *invite) { + GList *members, *mem; + members = mem = mwConference_getMembers(conf); + for(;mem;mem=mem->next) { + if(my_login_info && strcmp(my_login_info->login_id, ((mwLoginInfo *)mem->data)->login_id) == 0) { + char *utfs = t2u(TranslateT("Invitation rejected - already present.")); + mwConference_reject(conf, 0, utfs); + free(utfs); + return; + } + } + g_list_free(members); + + wchar_t ws_username[128]; + MultiByteToWideChar(CP_UTF8, 0, (const char *)inviter->user_name, -1, ws_username, 128); + + wchar_t ws_invite[512]; + MultiByteToWideChar(CP_UTF8, 0, (const char *)invite, -1, ws_invite, 128); + + if(MessageBoxW(0, ws_invite, ws_username, MB_OKCANCEL) == IDOK) { + mwConference_accept(conf); + } else { + char *temp = t2u(TranslateT("Your invitation has been rejected.")); + mwConference_reject(conf, 0, temp); + free(temp); + } + +} + +void ClearInviteQueue() { + if(!my_conference) return; + + mwIdBlock idb; + idb.community = 0; + + while(invite_queue.size()) { + idb.user = (char *)invite_queue.front().c_str(); + + HANDLE hContact = FindContactByUserId(idb.user); + if(!hContact) { + mwSametimeList *user_list = mwSametimeList_new(); + char *utfs = t2u(TranslateT("None")); + mwSametimeGroup *stgroup = mwSametimeGroup_new(user_list, mwSametimeGroup_NORMAL, utfs); + mwSametimeUser *stuser = mwSametimeUser_new(stgroup, mwSametimeUser_NORMAL, &idb); + + hContact = AddContact(stuser, (options.add_contacts ? false : true)); + mwSametimeList_free(user_list); + free(utfs); + + } + + bool found = false; + GList *members, *mem; + members = mem = mwConference_getMembers(my_conference); + for(;mem;mem=mem->next) { + if(my_login_info && strcmp(idb.user, ((mwLoginInfo *)mem->data)->user_id) == 0) { + found = true; + break; + } + } + g_list_free(members); + + if(!found) { + char *temp = t2u(TranslateT("Please join this meeting.")); + mwConference_invite(my_conference, &idb, temp); + free(temp); + } + + invite_queue.pop(); + } +} +/** triggered when we enter the conference. Provides the initial + conference membership list as a GList of mwLoginInfo structures + + @param conf the conference just joined + @param members mwLoginInfo list of existing conference members +*/ +void mwServiceConf_conf_opened(mwConference *conf, GList *members) { + //MessageBox(0, "Conference Opened", "msg", MB_OK); + + TCHAR *szId = u2t(mwConference_getName(conf)); + + // create new chat session + GCSESSION gcs = {0}; + gcs.dwFlags = GC_TCHAR; + gcs.cbSize = sizeof(gcs); + gcs.iType = GCW_CHATROOM; + gcs.pszModule = PROTO; + gcs.ptszID = szId; + gcs.ptszName = u2t(mwConference_getTitle(conf)); + gcs.dwItemData = 0; + + CallServiceSync(MS_GC_NEWSESSION, 0, (LPARAM)(GCSESSION *) &gcs); + free((char *)gcs.pszName); + + //add a group + GCDEST gcd = {0}; + gcd.pszModule = PROTO; + gcd.ptszID = szId; + gcd.iType = GC_EVENT_ADDGROUP; + + GCEVENT gce = {0}; + gce.dwFlags = GC_TCHAR | GCEF_ADDTOLOG; + gce.cbSize = sizeof(gce); + gce.pDest = &gcd; + gce.ptszStatus = TranslateT("Normal"); + + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)&gce); + + // add users + gcd.iType = GC_EVENT_JOIN; + + GList *user = members; + for(;user; user=user->next) { + + gce.ptszNick = u2t(((mwLoginInfo *)user->data)->user_name); + gce.ptszUID = u2t(((mwLoginInfo *)user->data)->login_id); + gce.bIsMe = (strcmp(((mwLoginInfo *)user->data)->login_id, my_login_info->login_id) == 0); + + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)(GCEVENT *) &gce); + + free((TCHAR *)gce.ptszNick); + free((TCHAR *)gce.ptszUID); + } + + // finalize setup (show window) + gcd.iType = GC_EVENT_CONTROL; + CallServiceSync(MS_GC_EVENT, SESSION_INITDONE, (LPARAM)&gce); + + gcd.iType = GC_EVENT_CONTROL; + CallServiceSync(MS_GC_EVENT, SESSION_ONLINE, (LPARAM)&gce); + + if(conf == my_conference) { + ClearInviteQueue(); + } + + free(szId); +} + +/** triggered when a conference is closed. This is typically when + we've left it */ +void mwServiceConf_conf_closed(mwConference *conf, guint32 reason) { + //MessageBox(0, "Conference closed", "msg", MB_OK); + + TCHAR *szId = u2t(mwConference_getName(conf)); + + GCDEST gcd = {0}; + gcd.pszModule = PROTO; + gcd.ptszID = szId; + + GCEVENT gce = {0}; + gce.dwFlags = GC_TCHAR | GCEF_ADDTOLOG; + gce.cbSize = sizeof(gce); + gce.pDest = &gcd; + gcd.iType = GC_EVENT_CONTROL; + + CallService(MS_GC_EVENT, SESSION_TERMINATE, (LPARAM)&gce); + free(szId); +} + +/** triggered when someone joins the conference */ +void mwServiceConf_on_peer_joined(mwConference *conf, mwLoginInfo *user) { + TCHAR *szId = u2t(mwConference_getName(conf)); + + HANDLE hContact = FindContactByUserId(((mwLoginInfo *)user)->user_id); + if(!hContact) { + mwIdBlock idb; + idb.user = ((mwLoginInfo *)user)->user_id; + idb.community = 0; + + mwSametimeList *user_list = mwSametimeList_new(); + char *utfs = t2u(TranslateT("None")); + mwSametimeGroup *stgroup = mwSametimeGroup_new(user_list, mwSametimeGroup_NORMAL, utfs); + mwSametimeUser *stuser = mwSametimeUser_new(stgroup, mwSametimeUser_NORMAL, &idb); + + hContact = AddContact(stuser, (options.add_contacts ? false : true)); + + mwSametimeList_free(user_list); + free(utfs); + } + + // add user + GCDEST gcd; + gcd.pszModule = PROTO; + gcd.ptszID = szId; + gcd.iType = GC_EVENT_JOIN; + + GCEVENT gce = {0}; + gce.dwFlags = GC_TCHAR | GCEF_ADDTOLOG; + gce.cbSize = sizeof(gce); + gce.pDest = &gcd; + gce.ptszNick = u2t(((mwLoginInfo *)user)->user_name); + gce.ptszUID = u2t(((mwLoginInfo *)user)->login_id); + gce.pszStatus = "Normal"; + gce.time = (DWORD)time(0); + + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)(GCEVENT *) &gce); + + free((TCHAR *)gce.ptszNick); + free((TCHAR *)gce.ptszUID); + free(szId); +} + +/** triggered when someone leaves the conference */ +void mwServiceConf_on_peer_parted(mwConference *conf, mwLoginInfo *user) { + TCHAR *szId = u2t(mwConference_getName(conf)); + + // remove user + GCDEST gcd; + gcd.pszModule = PROTO; + gcd.pszID = (char *)szId; + gcd.iType = GC_EVENT_PART; + + GCEVENT gce = {0}; + gce.dwFlags = GC_TCHAR | GCEF_ADDTOLOG; + gce.cbSize = sizeof(gce); + gce.pDest = &gcd; + gce.ptszNick = u2t(((mwLoginInfo *)user)->user_name); + gce.ptszUID = u2t(((mwLoginInfo *)user)->login_id); + gce.pszStatus = "Normal"; + gce.time = (DWORD)time(0); + + CallServiceSync(MS_GC_EVENT, 0, (LPARAM)(GCEVENT *) &gce); + + free((TCHAR *)gce.ptszNick); + free((TCHAR *)gce.ptszUID); + free(szId); +} + +/** triggered when someone says something */ +void mwServiceConf_on_text(mwConference *conf, mwLoginInfo *user, const char *what) { + TCHAR *szId = u2t(mwConference_getName(conf)); + + GCDEST gcd; + gcd.pszModule = PROTO; + gcd.pszID = (char *)szId; + gcd.iType = GC_EVENT_MESSAGE; + + GCEVENT gce = {0}; + gce.dwFlags = GC_TCHAR | GCEF_ADDTOLOG; + gce.cbSize = sizeof(gce); + gce.pDest = &gcd; + + + //char msg[MAX_MESSAGE_SIZE]; + //wchar_t ws_msg[MAX_MESSAGE_SIZE]; + + //MultiByteToWideChar(CP_UTF8, 0, what, -1, ws_msg, MAX_MESSAGE_SIZE); + //WideCharToMultiByte(CallService(MS_LANGPACK_GETCODEPAGE, 0, 0), WC_COMPOSITECHECK, ws_msg, -1, msg, MAX_MESSAGE_SIZE * sizeof(char), 0, 0); + + if(unicode_chat) + gce.ptszText = u2w(what); + else + gce.pszText = u2a(what); + + gce.ptszNick = u2t(((mwLoginInfo *)user)->user_name); + gce.ptszUID = u2t(((mwLoginInfo *)user)->login_id); + gce.time = (DWORD)time(0); + + CallService(MS_GC_EVENT, 0, (LPARAM)(GCEVENT *) &gce); + + free((char *)gce.pszText); + free((TCHAR *)gce.ptszNick); + free((TCHAR *)gce.ptszUID); + free(szId); +} + +/** typing notification */ +void mwServiceConf_on_typing(mwConference *conf, mwLoginInfo *who, gboolean typing) { +} + +/** optional. called from mwService_free */ +void mwServiceConf_clear(mwServiceConference *srvc) { +} + +mwConferenceHandler mwConference_handler = { + mwServiceConf_on_invited, + mwServiceConf_conf_opened, + mwServiceConf_conf_closed, + mwServiceConf_on_peer_joined, + mwServiceConf_on_peer_parted, + mwServiceConf_on_text, + mwServiceConf_on_typing, + mwServiceConf_clear +}; + +void TerminateConference(char *name) { + //MessageBox(0, name, "Terminating Conference", MB_OK); + + GList *conferences, *conf; + conferences = conf = mwServiceConference_getConferences(service_conference); + for(;conf;conf = conf->next) { + if(strcmp(name, mwConference_getName((mwConference *)conf->data)) == 0) { + GCDEST gcd = {0}; + gcd.pszModule = PROTO; + gcd.ptszID = u2t(name); + + GCEVENT gce = {0}; + gce.dwFlags = GC_TCHAR | GCEF_ADDTOLOG; + gce.cbSize = sizeof(gce); + gce.pDest = &gcd; + gcd.iType = GC_EVENT_CONTROL; + + CallService(MS_GC_EVENT, SESSION_TERMINATE, (LPARAM)&gce); + + free((TCHAR *)gcd.ptszID); + } + } + g_list_free(conferences); +} + + +int GcEventHook(WPARAM wParam, LPARAM lParam) { + GCHOOK *gch = (GCHOOK *)lParam; + + if(strcmp(gch->pDest->pszModule, PROTO) != 0) return 0; + + GList *conferences, *conf; + conferences = conf = mwServiceConference_getConferences(service_conference); + for(;conf;conf = conf->next) { + TCHAR *szId = u2t(mwConference_getName((mwConference *)conf->data)); + if(_tcscmp(gch->pDest->ptszID, szId) == 0) { + + switch(gch->pDest->iType) { + case GC_USER_MESSAGE: + { + //MessageBox(0, "Sending message", "Sending", MB_OK); + //wchar_t msg[MAX_MESSAGE_SIZE]; + //char utf_msg[MAX_MESSAGE_SIZE]; + //MultiByteToWideChar(CallService(MS_LANGPACK_GETCODEPAGE, 0, 0), MB_PRECOMPOSED, gch->pszText, -1, msg, MAX_MESSAGE_SIZE); + //WideCharToMultiByte(CP_UTF8, 0, msg, -1, utf_msg, MAX_MESSAGE_SIZE, 0, 0); + + char *utf_msg; + if(unicode_chat) + utf_msg = w2u(gch->ptszText); + else + utf_msg = a2u(gch->pszText); + + mwConference_sendText((mwConference *)conf->data, utf_msg); + + free(utf_msg); + } + break; + case GC_SESSION_TERMINATE: + { + //MessageBox(0, "Terminate chat event", "msg", MB_OK); + if(my_conference == conf->data) CloseMyConference(); + else { + char *utfs = t2u(TranslateT("I'm outa here.")); + mwConference_destroy((mwConference *)conf->data, 0, utfs); + free(utfs); + } + } + break; + } + + break; + } + free(szId); + } + + g_list_free(conferences); + + return 0; +} + +int ChatDeleted(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + + if(DBGetContactSettingByte(hContact, PROTO, "ChatRoom", 0) == 0) + return 0; + + DBVARIANT dbv; + if(!DBGetContactSetting(hContact, PROTO, "ChatRoomID", &dbv)) { + TerminateConference(dbv.pszVal); + DBFreeVariant(&dbv); + } + + return 0; +} + +int CreateChat(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + mwAwareIdBlock id_block; + mwIdBlock idb; + if(my_login_info && GetAwareIdFromContact(hContact, &id_block)) { + TCHAR title[512]; + TCHAR *ts = u2t(my_login_info->user_name); + +#ifdef _UNICODE + swprintf(title, TranslateT("%s's Conference"), ts); // TODO: FIX potential buffer overflow +#else + snprintf(title, 256, Translate("%s's Conference"), ts); +#endif + free(ts); + + idb.user = id_block.user; + idb.community = id_block.community; + + invite_queue.push(idb.user); + + if(!my_conference) { + char *utfs; + my_conference = mwConference_new(service_conference, utfs = t2u(title)); + mwConference_open(my_conference); + free(utfs); + } else { + ClearInviteQueue(); + } + + free(id_block.user); + } + + return 0; +} + +int PrebuildContactMenu(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + + + CLISTMENUITEM mi = {0}; + mi.cbSize = sizeof(mi); + mi.flags = CMIM_FLAGS | (DBGetContactSettingByte(hContact, PROTO, "ChatRoom", 0) == 1 ? 0 : CMIF_HIDDEN); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hLeaveChatMenuItem, (LPARAM)&mi); + + // if user is already in our meeting, + bool not_present = true; + DBVARIANT dbv; + if(my_conference && !DBGetContactSettingUtf(hContact, PROTO, "stid", &dbv)) { + char *user_id = dbv.pszVal; + + GList *members, *mem; + members = mem = mwConference_getMembers(my_conference); + for(;mem;mem=mem->next) { + if(my_login_info && strcmp(user_id, ((mwLoginInfo *)mem->data)->user_id) == 0) { + not_present = false; + break; + } + } + g_list_free(members); + + DBFreeVariant(&dbv); + } + mi.flags = CMIM_FLAGS | CMIF_NOTOFFLINE | (DBGetContactSettingByte(hContact, PROTO, "ChatRoom", 0) == 0 && not_present ? 0 : CMIF_HIDDEN); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hCreateChatMenuItem, (LPARAM)&mi); + + return 0; +} + +void InitConference(mwSession *session) { + my_login_info = mwSession_getLoginInfo(session); + + mwSession_addService(session, (mwService *)(service_conference = mwServiceConference_new(session, &mwConference_handler))); + + hGcEvent = HookEvent(ME_GC_EVENT, GcEventHook); + + hChatDeletedEvent = HookEvent(ME_DB_CONTACT_DELETED, ChatDeleted); + +} + +void DeinitConference(mwSession *session) { + GList *conferences, *conf; + conferences = conf = mwServiceConference_getConferences(service_conference); + for(;conf;conf = conf->next) { + if(my_conference == conf->data) CloseMyConference(); + else { + char *utfs = t2u(TranslateT("I'm outa here.")); + mwConference_destroy((mwConference *)conf->data, 0, utfs); + free(utfs); + } + + } + g_list_free(conferences); + + + UnhookEvent(hMenuBuildEvent); + hMenuBuildEvent = 0; + UnhookEvent(hChatDeletedEvent); + hChatDeletedEvent = 0; + + my_login_info = 0; + + UnhookEvent(hGcEvent); + hGcEvent = 0; + + mwSession_removeService(session, mwService_CONFERENCE); + mwService_free((mwService *)service_conference); + service_conference = 0; +} + +void InitConferenceMenu() { + CreateProtoServiceFunction(PROTO, MS_SAMETIME_MENULEAVECHAT, ChatDeleted); + CreateProtoServiceFunction(PROTO, MS_SAMETIME_MENUCREATECHAT, CreateChat); + + hIconLeave = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON_LEAVE)); + hIconCreate = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON_INVITE)); + + char service_function[128]; + strcpy(service_function, PROTO); + char *d = service_function + strlen(service_function); + + CLISTMENUITEM mi = {0}; + mi.cbSize = sizeof(mi); + mi.pszContactOwner = PROTO; + + mi.pszName = Translate("Leave Conference"); + strcpy(d, MS_SAMETIME_MENULEAVECHAT); + mi.pszService = service_function; + mi.hIcon = hIconLeave; + + hLeaveChatMenuItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi); + + mi.flags = CMIF_NOTOFFLINE; + mi.pszName = Translate("Start Conference"); + strcpy(d, MS_SAMETIME_MENUCREATECHAT); + mi.pszService = service_function; + mi.hIcon = hIconCreate; + + hCreateChatMenuItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi); + + hMenuBuildEvent = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PrebuildContactMenu); + +} + +void DeinitConferenceMenu() { + DestroyIcon(hIconLeave); + DestroyIcon(hIconCreate); +} + diff --git a/plugins/!NotAdopted/sametime/conference.h b/plugins/!NotAdopted/sametime/conference.h new file mode 100644 index 0000000000..55a74a1605 --- /dev/null +++ b/plugins/!NotAdopted/sametime/conference.h @@ -0,0 +1,18 @@ +#ifndef _CONFERENCE_INC +#define _CONFERENCE_INC + +#include "common.h" +#include "userlist.h" +#include "options.h" +#include "utils.h" + +#include +#include + +void InitConference(mwSession *session); +void DeinitConference(mwSession *session); + +void InitConferenceMenu(); +void DeinitConferenceMenu(); + +#endif \ No newline at end of file diff --git a/plugins/!NotAdopted/sametime/files.cpp b/plugins/!NotAdopted/sametime/files.cpp new file mode 100644 index 0000000000..d89cf401d8 --- /dev/null +++ b/plugins/!NotAdopted/sametime/files.cpp @@ -0,0 +1,421 @@ +#include "files.h" + +mwServiceFileTransfer *service_files = 0; + +/** an incoming file transfer has been offered */ +void mwFileTransfer_offered(mwFileTransfer *ft) { + //MessageBox(0, "Offered", "File Transfer", MB_OK); + + const mwIdBlock *idb = mwFileTransfer_getUser(ft); + HANDLE hContact = FindContactByUserId(idb->user); + + if(!hContact) { + mwSametimeList *user_list = mwSametimeList_new(); + mwSametimeGroup *stgroup = mwSametimeGroup_new(user_list, mwSametimeGroup_NORMAL, Translate("None")); + mwSametimeUser *stuser = mwSametimeUser_new(stgroup, mwSametimeUser_NORMAL, (mwIdBlock *)idb); + + hContact = AddContact(stuser, (options.add_contacts ? false : true)); + } + + CCSDATA ccs = {0}; + PROTORECVEVENT pre = {0}; + + char filename[MAX_PATH]; + char desc[512]; + + strncpy(filename, mwFileTransfer_getFileName(ft), MAX_PATH); + + NLog("Sametime mwFileTransfer_offered"); + NLog(filename); + + const char *msg = mwFileTransfer_getMessage(ft); + if(msg) strncpy(desc, msg, 512); + else desc[0] = 0; + + ProtoBroadcastAck(PROTO, hContact, ACKTYPE_FILE, ACKRESULT_INITIALISING, (HANDLE)ft, 0); + + char *blob = (char *)malloc(sizeof(DWORD) + strlen(filename) + strlen(desc) + 2); + + *(DWORD *)blob = (DWORD)(ft); + strcpy(blob + sizeof(DWORD), filename); + strcpy(blob + sizeof(DWORD) + strlen(filename) + 1, desc); + + // Send chain event + + ccs.szProtoService = PSR_FILE; + ccs.hContact = hContact; + ccs.wParam = (WPARAM)ft; + ccs.lParam = (LPARAM)⪯ + + pre.flags = 0; + pre.timestamp = (DWORD)time(0); + pre.szMessage = blob; + pre.lParam = 0; + + CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); + + free(blob); +} + +//returns 0 if finished with current file +int SendFileChunk(mwFileTransfer *ft, FileTransferClientData *ftcd) { + DWORD bytes_read; + mwOpaque o; + + if(!ftcd || !ftcd->buffer) + return 0; + + if(!ReadFile(ftcd->hFile, ftcd->buffer, FILE_BUFF_SIZE, &bytes_read, 0)) { + //MessageBox(0, "Closing FT - read failed", "FT Opened", MB_OK); + //mwFileTransfer_cancel(ft); + NLog("Sametime closing file transfer (SendFileChunk)"); + mwFileTransfer_close(ft, mwFileTransfer_SUCCESS); + return 0; + } + o.data = (unsigned char *)ftcd->buffer; + o.len = bytes_read; + + mwFileTransfer_send(ft, &o); + + return bytes_read; +} + +unsigned long __stdcall SendThread(LPVOID param) { + CallService(MS_SYSTEM_THREAD_PUSH, 0, 0); + NLog("Sametime send thread starting"); + + mwFileTransfer *ft = (mwFileTransfer *)param; + FileTransferClientData *ftcd = 0; + + if(ft) ftcd = (FileTransferClientData *)mwFileTransfer_getClientData(ft); + + if(!ft || !ftcd) return 1; + + PROTOFILETRANSFERSTATUS pfts = {0}; + + pfts.cbSize = sizeof(pfts); + pfts.hContact = ftcd->hContact; + pfts.sending = (ftcd->sending ? 1 : 0); + + pfts.files = 0; + pfts.totalFiles = ftcd->first->ft_count; + pfts.totalBytes = ftcd->first->totalSize; + + while(SendFileChunk(ft, ftcd) && !Miranda_Terminated()) { + + pfts.currentFileNumber = ftcd->ft_number; + pfts.totalProgress = ftcd->sizeToHere + mwFileTransfer_getSent(ft); + + pfts.workingDir = ftcd->save_path; + pfts.currentFile = (char *)mwFileTransfer_getFileName(ft); + pfts.currentFileSize = mwFileTransfer_getFileSize(ft); + pfts.currentFileProgress = mwFileTransfer_getSent(ft); + pfts.currentFileTime = 0; //? + + ProtoBroadcastAck(PROTO, ftcd->hContact, ACKTYPE_FILE, ACKRESULT_DATA, ftcd->hFt, (LPARAM)&pfts); + + SleepEx(500,TRUE); + } + + ProtoBroadcastAck(PROTO, ftcd->hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ftcd->hFt, 0); + + mwFileTransfer_removeClientData(ft); + if(ftcd->save_path) free(ftcd->save_path); + if(ftcd->buffer) delete[] ftcd->buffer; + delete ftcd; + + NLog("Sametime send thread exiting"); + CallService(MS_SYSTEM_THREAD_POP, 0, 0); + + return 0; +} + +/** a file transfer has been fully initiated */ +void mwFileTransfer_opened(mwFileTransfer *ft) { + //MessageBox(0, "Opened", "File Transfer", MB_OK); + NLog("Sametime mwFileTransfer_opened"); + + FileTransferClientData *ftcd = (FileTransferClientData *)mwFileTransfer_getClientData(ft); + + if(ftcd->sending) { + // create a thread to send chunks - since it seems not all clients send acks for each of our chunks! + DWORD tid; + CloseHandle(CreateThread(0, 0, SendThread, (void *)ft, 0, &tid)); + } +} + +/** a file transfer has been closed. Check the status of the file + transfer to determine if the transfer was complete or if it had + been interrupted */ +void mwFileTransfer_closed(mwFileTransfer *ft, guint32 code) { + //MessageBox(0, "Closed", "File Transfer", MB_OK); + NLog("Sametime mwFileTransfer_closed"); + + FileTransferClientData *ftcd = (FileTransferClientData *)mwFileTransfer_getClientData(ft); + + if(ftcd) { + if(ftcd->hFile != INVALID_HANDLE_VALUE) + CloseHandle(ftcd->hFile); + + if(code != mwFileTransfer_SUCCESS || !mwFileTransfer_isDone(ft)) { + if(!ftcd->sending) { + char fn[MAX_PATH]; + if(ftcd->save_path) strcpy(fn, ftcd->save_path); + else fn[0] = 0; + strcat(fn, mwFileTransfer_getFileName(ft)); + + DeleteFileA(fn); + } + + if(code == mwFileTransfer_REJECTED) { + ProtoBroadcastAck(PROTO, ftcd->hContact, ACKTYPE_FILE, ACKRESULT_DENIED, ftcd->hFt, 0); + } else { + ProtoBroadcastAck(PROTO, ftcd->hContact, ACKTYPE_FILE, ACKRESULT_FAILED, ftcd->hFt, 0); + } + + if(ftcd->sending) { + FileTransferClientData *ftcd_next = ftcd->next, *ftcd_temp; + while(ftcd_next) { + mwFileTransfer_free((mwFileTransfer *)ftcd_next->ft); + ftcd_temp = ftcd_next->next; + + if(ftcd_next->hFile != INVALID_HANDLE_VALUE) + CloseHandle(ftcd->hFile); + + if(ftcd_next->save_path) free(ftcd_next->save_path); + if(ftcd_next->buffer) delete[] ftcd_next->buffer; + delete ftcd_next; + ftcd_next = ftcd_temp; + } + } else { + mwFileTransfer_removeClientData(ft); + if(ftcd->save_path) free(ftcd->save_path); + if(ftcd->buffer) delete[] ftcd->buffer; + delete ftcd; + + mwFileTransfer_free(ft); + } + + } else { + if(ftcd->sending) { + // check if we have more files to send... + if(ftcd->next) { + ProtoBroadcastAck(PROTO, ftcd->hContact, ACKTYPE_FILE, ACKRESULT_NEXTFILE, ftcd->hFt, 0); + mwFileTransfer_offer(ftcd->next->ft); + } + } else { + ProtoBroadcastAck(PROTO, ftcd->hContact, ACKTYPE_FILE, ACKRESULT_SUCCESS, ftcd->hFt, 0); + + mwFileTransfer_removeClientData(ft); + if(ftcd->save_path) free(ftcd->save_path); + if(ftcd->buffer) delete[] ftcd->buffer; + delete ftcd; + + mwFileTransfer_free(ft); + } + } + + } + +} + +/** receive a chunk of a file from an inbound file transfer. */ +void mwFileTransfer_recv(mwFileTransfer *ft, struct mwOpaque *data) { + //MessageBox(0, "Recv", "File Transfer", MB_OK); + + FileTransferClientData *ftcd = (FileTransferClientData *)mwFileTransfer_getClientData(ft); + + DWORD bytes_written; + if(!WriteFile(ftcd->hFile, data->data, data->len, &bytes_written, 0)) { + //MessageBox(0, "Write failed", "msg", MB_OK); + mwFileTransfer_cancel(ft); + } else { + //if(mwFileTransfer_isOpen(ft)) + mwFileTransfer_ack(ft); // acknowledge chunk + + PROTOFILETRANSFERSTATUS pfts = {0}; + + pfts.cbSize = sizeof(pfts); + pfts.hContact = ftcd->hContact; + pfts.sending = (ftcd->sending ? 1 : 0); + pfts.files = 0; + pfts.totalFiles = 1; + pfts.currentFileNumber = 0; + pfts.totalBytes = mwFileTransfer_getFileSize(ft); + pfts.totalProgress = mwFileTransfer_getSent(ft); + pfts.workingDir = ftcd->save_path; + pfts.currentFile = (char *)mwFileTransfer_getFileName(ft); + pfts.currentFileSize = mwFileTransfer_getFileSize(ft); + pfts.currentFileProgress = mwFileTransfer_getSent(ft); + pfts.currentFileTime = 0; //? + + ProtoBroadcastAck(PROTO, ftcd->hContact, ACKTYPE_FILE, ACKRESULT_DATA, ftcd->hFt, (LPARAM)&pfts); + } + +} + +/** received an ack for a sent chunk on an outbound file transfer. + this indicates that a previous call to mwFileTransfer_send has + reached the target and that the target has responded. */ +void mwFileTransfer_handle_ack(mwFileTransfer *ft) { + //MessageBox(0, "Handle ack", "File Transfer", MB_OK); + + // see SendThread above - not all clients send us acks +} + +/** optional. called from mwService_free */ +void mwFileTransfer_clear(mwServiceFileTransfer *srvc) { +} + +mwFileTransferHandler mwFileTransfer_handler = { + mwFileTransfer_offered, + mwFileTransfer_opened, + mwFileTransfer_closed, + mwFileTransfer_recv, + mwFileTransfer_handle_ack, + mwFileTransfer_clear +}; + +HANDLE SendFilesToUser(HANDLE hContact, char **files, char *pszDesc) { + mwAwareIdBlock id_block; + if(GetAwareIdFromContact(hContact, &id_block)) { + mwIdBlock idb; + idb.user = id_block.user; + idb.community = id_block.community; + + HANDLE hFile; + DWORD filesize; + FileTransferClientData *ftcd, *prev_ftcd = 0, *first_ftcd = 0; + mwFileTransfer *ft, *first_ft = 0; + + char *fn; + + for(int i = 0; files[i]; i++) { + hFile = CreateFileA(files[i], GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0); + if(hFile != INVALID_HANDLE_VALUE) { + filesize = GetFileSize(hFile, 0); + + fn = strrchr(files[i], '\\'); + if(fn) fn++; + + ft = mwFileTransfer_new(service_files, &idb, pszDesc, (fn ? fn : files[i]), filesize); + + ftcd = new FileTransferClientData; + memset((void *)ftcd, 0, sizeof(FileTransferClientData)); + + ftcd->ft = ft; + ftcd->hContact = hContact; + + ftcd->next = 0; + if(prev_ftcd) { + prev_ftcd->next = ftcd; // link into list + + // each node contains a pointer to the first - it will contain infor linke the count etc + ftcd->first = prev_ftcd->first; + } else { + ftcd->first = ftcd; + } + + if(!first_ft) first_ft = ft; + + ftcd->sending = true; + ftcd->hFile = hFile; + ftcd->hFt = (HANDLE)first_ft; + + ftcd->save_path = 0; + ftcd->buffer = new char[FILE_BUFF_SIZE]; + + ftcd->ft_number = ftcd->first->ft_count; + ftcd->first->ft_count++; + ftcd->sizeToHere = ftcd->first->totalSize; + ftcd->first->totalSize += filesize; + + mwFileTransfer_setClientData(ft, (gpointer)ftcd, 0); + + prev_ftcd = ftcd; + } + } + + free(id_block.user); + + if(first_ft) { + mwFileTransfer_offer(first_ft); + return (HANDLE)first_ft; + } + + } + + return 0; +} + +HANDLE AcceptFileTransfer(HANDLE hContact, HANDLE hFt, char *save_path) { + mwFileTransfer *ft = (mwFileTransfer *)(hFt); + + FileTransferClientData *ftcd = new FileTransferClientData; + memset((void *)ftcd, 0, sizeof(FileTransferClientData)); + ftcd->ft = ft; + ftcd->sending = false; + ftcd->hFt = (HANDLE)ft; + + if(save_path) // save path + ftcd->save_path = _strdup(save_path); + else + ftcd->save_path = 0; + + mwFileTransfer_setClientData(ft, (gpointer)ftcd, 0); + + char fp[MAX_PATH]; + char *fn = strrchr((char *)mwFileTransfer_getFileName(ft), '\\'); + if(fn) fn++; + + if(ftcd->save_path) + strcpy(fp, ftcd->save_path); + else + fp[0] = 0; + + if(fn) strcat(fp, fn); + else strcat(fp, mwFileTransfer_getFileName(ft)); + + ftcd->hFile = CreateFileA(fp, GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0); + if(ftcd->hFile == INVALID_HANDLE_VALUE) { + //MessageBox(0, fp, "Accept - invalid handle", MB_OK); + mwFileTransfer_close(ft, mwFileTransfer_ERROR); + return 0; + } + + ftcd->hContact = hContact; + mwFileTransfer_setClientData(ft, (gpointer)ftcd, 0); + + mwFileTransfer_accept(ft); + return hFt; +} + +void RejectFileTransfer(HANDLE hFt) { + mwFileTransfer *ft = (mwFileTransfer *)hFt; + mwFileTransfer_reject(ft); +} + +void CancelFileTransfer(HANDLE hFt) { + mwFileTransfer *ft = (mwFileTransfer *)hFt; + + FileTransferClientData *ftcd = (FileTransferClientData *)mwFileTransfer_getClientData(ft); + + if(ftcd) { + while(mwFileTransfer_isDone(ftcd->ft) && ftcd) + ftcd = ftcd->next; + + if(ftcd) mwFileTransfer_cancel(ftcd->ft); + } else + mwFileTransfer_cancel(ft); +} + +void InitFiles(mwSession *session) { + mwSession_addService(session, (mwService *)(service_files = mwServiceFileTransfer_new(session, &mwFileTransfer_handler))); +} + +void DeinitFiles(mwSession *session) { + mwSession_removeService(session, mwService_FILE_TRANSFER); + mwService_free((mwService *)service_files); + service_files = 0; +} + diff --git a/plugins/!NotAdopted/sametime/files.h b/plugins/!NotAdopted/sametime/files.h new file mode 100644 index 0000000000..f240c7fa61 --- /dev/null +++ b/plugins/!NotAdopted/sametime/files.h @@ -0,0 +1,37 @@ +#ifndef _FILES_INC +#define _FILES_INC + +#include "common.h" +#include "userlist.h" +#include "options.h" + +#define FILE_BUFF_SIZE (1024 * 32) + +typedef struct FileTransferClientData_tag { + char *save_path; + HANDLE hFile; + bool sending; + HANDLE hContact; + struct FileTransferClientData_tag *next, *first; + HANDLE hFt; + char *buffer; + + int ft_number; + + int ft_count; // number of nodes in list - only valid in first node + int totalSize; // total for all files in the list - only valid in first node + + int sizeToHere; // in a link list of file transfers, the sum of the filesizes of all prior nodes in the list + mwFileTransfer *ft; +} FileTransferClientData; + +HANDLE SendFilesToUser(HANDLE hContact, char **files, char *pszDesc); + +HANDLE AcceptFileTransfer(HANDLE hContact, HANDLE hFt, char *save_path); +void RejectFileTransfer(HANDLE hFt); +void CancelFileTransfer(HANDLE hFt); + +void InitFiles(mwSession *session); +void DeinitFiles(mwSession *session); + +#endif diff --git a/plugins/!NotAdopted/sametime/invite.ico b/plugins/!NotAdopted/sametime/invite.ico new file mode 100644 index 0000000000..944ebf818c Binary files /dev/null and b/plugins/!NotAdopted/sametime/invite.ico differ diff --git a/plugins/!NotAdopted/sametime/messaging.cpp b/plugins/!NotAdopted/sametime/messaging.cpp new file mode 100644 index 0000000000..1c015de153 --- /dev/null +++ b/plugins/!NotAdopted/sametime/messaging.cpp @@ -0,0 +1,240 @@ +#include "messaging.h" + +#include +#include + +typedef std::queue MessageQueue; +typedef std::map ContactMessageQueue; + +ContactMessageQueue contact_message_queue; +CRITICAL_SECTION q_cs; + +mwServiceIm *service_im = 0; + +void mwIm_conversation_opened(mwConversation *conv) { + mwIdBlock *idb = mwConversation_getTarget(conv); + HANDLE hContact = FindContactByUserId(idb->user); + + if(!hContact) { + mwSametimeList *user_list = mwSametimeList_new(); + mwSametimeGroup *stgroup = mwSametimeGroup_new(user_list, mwSametimeGroup_NORMAL, Translate("None")); + mwSametimeUser *stuser = mwSametimeUser_new(stgroup, mwSametimeUser_NORMAL, idb); + + AddContact(stuser, (options.add_contacts ? false : true)); + GetMoreDetails(idb->user); + } + + ContactMessageQueue::iterator i; + EnterCriticalSection(&q_cs); + if((i = contact_message_queue.find(hContact)) != contact_message_queue.end()) { + while(i->second.size()) { + mwConversation_send(conv, mwImSend_PLAIN, (gconstpointer)i->second.front().c_str()); + i->second.pop(); + } + contact_message_queue.erase(i); + } + LeaveCriticalSection(&q_cs); + + // gives linker error 'unresolved external symbol' :( So instead we will either add ciphers to the session or not (see session.cpp) + //mwConversation_setEncrypted(conv, options.encrypt_session); +} + +/** A conversation has been closed */ +void mwIm_conversation_closed(mwConversation *conv, guint32 err) { + if(err & ERR_FAILURE && err != CONNECTION_RESET) { + char *msg = mwError(err); + TCHAR *ts = u2t(msg); + //MessageBox(0, ts, TranslateT("Sametime Error"), MB_OK | MB_ICONWARNING); + ShowError(TranslateTS(ts)); + g_free(msg); + free(ts); + } + + mwIdBlock *idb = mwConversation_getTarget(conv); + HANDLE hContact = FindContactByUserId(idb->user); + if(hContact) { + ContactMessageQueue::iterator i; + EnterCriticalSection(&q_cs); + if((i = contact_message_queue.find(hContact)) != contact_message_queue.end()) { + contact_message_queue.erase(i); + } + LeaveCriticalSection(&q_cs); + } +} + +/** A message has been received on a conversation */ +void mwIm_conversation_recv(mwConversation *conv, mwImSendType type, gconstpointer msg) { + mwIdBlock *idb = mwConversation_getTarget(conv); + HANDLE hContact = FindContactByUserId(idb->user); + + if(type == mwImSend_TYPING) { + CallService(MS_PROTO_CONTACTISTYPING, (WPARAM)hContact, (LPARAM)(GPOINTER_TO_UINT(msg) == 0 ? 0 : 2)); + return; + } + + if(type != mwImSend_PLAIN) return; + + wchar_t temp[MAX_MESSAGE_SIZE]; + char text[MAX_MESSAGE_SIZE]; + + MultiByteToWideChar(CP_UTF8, 0, (const char *)msg, -1, temp, MAX_MESSAGE_SIZE); + WideCharToMultiByte(CallService(MS_LANGPACK_GETCODEPAGE, 0, 0), WC_COMPOSITECHECK, temp, -1, text, MAX_MESSAGE_SIZE * sizeof(char), 0, 0); + + CCSDATA ccs = {0}; + PROTORECVEVENT pre = {0}; + + ccs.hContact = hContact; + ccs.szProtoService = PSR_MESSAGE; + ccs.wParam = 0; + ccs.lParam = (LPARAM)⪯ + + pre.timestamp = (DWORD)time(0); + + int tMsgBodyLen = strlen(text); + int tRealBodyLen = wcslen(temp); + + int tMsgBufLen = (tMsgBodyLen+1) * sizeof(char) + (tRealBodyLen+1)*sizeof( wchar_t ); + char* tMsgBuf = ( char* )malloc( tMsgBufLen ); + + char* p = tMsgBuf; + + strcpy( p, text ); + p += (tMsgBodyLen+1); + + if ( tRealBodyLen != 0 ) { + wcscpy((wchar_t *)p, temp); + pre.flags = PREF_UNICODE; + } + + pre.szMessage = tMsgBuf; + + DBDeleteContactSetting(hContact, "CList", "Hidden"); + + CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); + + free(tMsgBuf); +} + +mwImHandler mwIm_handler = { + mwIm_conversation_opened, + mwIm_conversation_closed, + mwIm_conversation_recv +}; + +HANDLE SendMessageToUser(HANDLE hContact, char *msg) { + mwIdBlock idb; + mwAwareIdBlock id_block; + + wchar_t temp[MAX_MESSAGE_SIZE]; + char text[MAX_MESSAGE_SIZE]; + + MultiByteToWideChar(CallService(MS_LANGPACK_GETCODEPAGE, 0, 0), MB_PRECOMPOSED, msg, -1, temp, MAX_MESSAGE_SIZE); + WideCharToMultiByte(CP_UTF8, 0, temp, -1, text, MAX_MESSAGE_SIZE * sizeof(char), 0, 0); + + if(GetAwareIdFromContact(hContact, &id_block)) { + idb.user = id_block.user; + idb.community = id_block.community; + + mwConversation *conv = mwServiceIm_getConversation(service_im, &idb); + if(conv) { + if(!mwConversation_isOpen(conv)) { + EnterCriticalSection(&q_cs); + contact_message_queue[hContact].push(text); + LeaveCriticalSection(&q_cs); + mwConversation_open(conv); + } else + mwConversation_send(conv, mwImSend_PLAIN, (gconstpointer)msg); + + free(id_block.user); + return (HANDLE)conv; + } + + free(id_block.user); + } + + return 0; +} + +HANDLE SendMessageToUserW(HANDLE hContact, wchar_t *msg) { + mwIdBlock idb; + mwAwareIdBlock id_block; + + char text[MAX_MESSAGE_SIZE]; + + WideCharToMultiByte(CP_UTF8, 0, msg, -1, text, MAX_MESSAGE_SIZE * sizeof(char), 0, 0); + + if(GetAwareIdFromContact(hContact, &id_block)) { + idb.user = id_block.user; + idb.community = id_block.community; + + mwConversation *conv = mwServiceIm_getConversation(service_im, &idb); + if(conv) { + if(!mwConversation_isOpen(conv)) { + EnterCriticalSection(&q_cs); + contact_message_queue[hContact].push(text); + LeaveCriticalSection(&q_cs); + + mwConversation_open(conv); + } else + mwConversation_send(conv, mwImSend_PLAIN, (gconstpointer)text); + + free(id_block.user); + return (HANDLE)conv; + } + + free(id_block.user); + } + + return 0; +} + +void SendTyping(HANDLE hContact, bool typing) { + mwIdBlock idb; + mwAwareIdBlock id_block; + + if(GetAwareIdFromContact(hContact, &id_block)) { + idb.user = id_block.user; + idb.community = id_block.community; + + mwConversation *conv = mwServiceIm_getConversation(service_im, &idb); + if(conv) { + if(mwConversation_isOpen(conv)) + mwConversation_send(conv, mwImSend_TYPING, (gconstpointer)GUINT_TO_POINTER(typing ? 1 : 0)); + } + + free(id_block.user); + } +} + +void CloseIm(HANDLE hContact) { + mwIdBlock idb; + mwAwareIdBlock id_block; + + if(GetAwareIdFromContact(hContact, &id_block)) { + idb.user = id_block.user; + idb.community = id_block.community; + + mwConversation *conv = mwServiceIm_getConversation(service_im, &idb); + if(conv) { + if(mwConversation_isOpen(conv)) + mwConversation_close(conv, 0); + } + free(id_block.user); + } +} + +void InitMessaging(mwSession *session) { + InitializeCriticalSection(&q_cs); + + mwSession_addService(session, (mwService *)(service_im = mwServiceIm_new(session, &mwIm_handler))); + mwServiceIm_setClientType(service_im, mwImClient_PLAIN); +} + +void DeinitMessaging(mwSession *session) { + mwSession_removeService(session, mwService_IM); + mwService_free((mwService *)service_im); + service_im = 0; + + DeleteCriticalSection(&q_cs); +} + diff --git a/plugins/!NotAdopted/sametime/messaging.h b/plugins/!NotAdopted/sametime/messaging.h new file mode 100644 index 0000000000..d2860b220a --- /dev/null +++ b/plugins/!NotAdopted/sametime/messaging.h @@ -0,0 +1,18 @@ +#ifndef _MESSAGING_INC +#define _MESSAGING_INC + +#include "common.h" +#include "userlist.h" +#include "options.h" +#include "utils.h" + +void SendTyping(HANDLE hContact, bool typing); +HANDLE SendMessageToUser(HANDLE hContact, char *msg); +HANDLE SendMessageToUserW(HANDLE hContact, wchar_t *msg); + +void CloseIm(HANDLE hContact); + +void InitMessaging(mwSession *session); +void DeinitMessaging(mwSession *session); + +#endif diff --git a/plugins/!NotAdopted/sametime/online.ico b/plugins/!NotAdopted/sametime/online.ico new file mode 100644 index 0000000000..8362c83159 Binary files /dev/null and b/plugins/!NotAdopted/sametime/online.ico differ diff --git a/plugins/!NotAdopted/sametime/options.cpp b/plugins/!NotAdopted/sametime/options.cpp new file mode 100644 index 0000000000..438fab2282 --- /dev/null +++ b/plugins/!NotAdopted/sametime/options.cpp @@ -0,0 +1,328 @@ +#include "options.h" + +HWND hWndOptions = 0; + +Options options = {0}; + +#define DEFAULT_ID (0x1800) + +#define NUM_IDS 20 +TCHAR *client_names[NUM_IDS] = {_T("Official Binary Library"), _T("Official Java Applet"), _T("Official Binary Application"), + _T("Official Java Application"), _T("Notes v6.5"), _T("Notes v7.0"), _T("ICT"), _T("NotesBuddy"), _T("NotesBuddy v4.15"), _T("Sanity"), + _T("Perl"), _T("PMR Alert"), _T("Trillian (SourceForge)"), _T("Trillian (IBM)"), _T("Meanwhile Library"), _T("Meanwhile (Python)"), + _T("Meanwhile (Gaim)"), _T("Meanwhile (Adium)"), _T("Meanwhile (Kopete)"), _T("Custom")}; +int client_ids[NUM_IDS] = {0x1000, 0x1001, 0x1002, 0x1003, 0x1200, 0x1210, 0x1300, 0x1400, 0x1405, 0x1600, 0x1625, + 0x1650, 0x16aa, 0x16bb, 0x1700, 0x1701, 0x1702, 0x1703, 0x1704, 0xFFFF}; + +static BOOL CALLBACK DlgProcOptNet(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch ( msg ) { + case WM_INITDIALOG: { + TranslateDialogDefault( hwndDlg ); + + { + TCHAR verbuf[100]; + WORD client_ver = GetClientVersion(), server_ver = GetServerVersion(); + if(client_ver) + _stprintf(verbuf, _T("Client proto version: %03d.%03d"), (client_ver & 0xFF00) >> 8, client_ver & 0xFF); + else + _stprintf(verbuf, _T("Disconnected")); + SetDlgItemText(hwndDlg, IDC_ST_CLIENTVER, verbuf); + if(server_ver) + _stprintf(verbuf, _T("Server proto version: %03d.%03d"), (server_ver & 0xFF00) >> 8, server_ver & 0xFF); + else + _stprintf(verbuf, _T("Disconnected")); + SetDlgItemText(hwndDlg, IDC_ST_SERVERVER, verbuf); + } + + hWndOptions = hwndDlg; + + wchar_t *s; + s = u2w(options.server_name); SetDlgItemTextW(hwndDlg, IDC_ED_SNAME, s); free(s); + s = u2w(options.id); SetDlgItemTextW(hwndDlg, IDC_ED_NAME, s); free(s); + s = u2w(options.pword); SetDlgItemTextW(hwndDlg, IDC_ED_PWORD, s); free(s); + + SetDlgItemInt(hwndDlg, IDC_ED_PORT, options.port, FALSE); + CheckDlgButton(hwndDlg, IDC_CHK_GETSERVERCONTACTS, options.get_server_contacts ? TRUE : FALSE); + CheckDlgButton(hwndDlg, IDC_CHK_ADDCONTACTS, options.add_contacts ? TRUE : FALSE); + CheckDlgButton(hwndDlg, IDC_CHK_IDLEAWAY, options.idle_as_away ? TRUE : FALSE); + + CheckDlgButton(hwndDlg, IDC_CHK_OLDDEFAULTVER, options.use_old_default_client_ver ? TRUE : FALSE); + { + SendDlgItemMessage(hwndDlg, IDC_CMB_CLIENT, CB_RESETCONTENT, 0, 0); + int pos = 0; + bool found = false; + + for(int i = 0; i < NUM_IDS; i++) { + pos = SendDlgItemMessage(hwndDlg, IDC_CMB_CLIENT, CB_ADDSTRING, -1, (LPARAM)client_names[i]); + SendDlgItemMessage(hwndDlg, IDC_CMB_CLIENT, CB_SETITEMDATA, pos, client_ids[i]); + if(client_ids[i] == options.client_id) { + found = true; + SendDlgItemMessage(hwndDlg, IDC_CMB_CLIENT, CB_SETCURSEL, pos, 0); + SetDlgItemInt(hwndDlg, IDC_ED_CLIENTID, client_ids[i], FALSE); + if(i != sizeof(client_ids) / sizeof(int) - 1) { + HWND hw = GetDlgItem(hwndDlg, IDC_ED_CLIENTID); + EnableWindow(hw, false); + } + } + } + + if(!found) { + SendDlgItemMessage(hwndDlg, IDC_CMB_CLIENT, CB_SETCURSEL, pos, 0); // pos is last item, i.e. custom + SetDlgItemInt(hwndDlg, IDC_ED_CLIENTID, options.client_id, FALSE); + } + } + + if(!ServiceExists(MS_POPUP_ADDPOPUP)) { + HWND hw = GetDlgItem(hwndDlg, IDC_RAD_ERRPOP); + EnableWindow(hw, FALSE); + } + + if(!ServiceExists(MS_CLIST_SYSTRAY_NOTIFY)) { + HWND hw = GetDlgItem(hwndDlg, IDC_RAD_ERRBAL); + EnableWindow(hw, FALSE); + } + + switch(options.err_method) { + case ED_POP: CheckDlgButton(hwndDlg, IDC_RAD_ERRPOP, TRUE); break; + case ED_MB: CheckDlgButton(hwndDlg, IDC_RAD_ERRMB, TRUE); break; + case ED_BAL: CheckDlgButton(hwndDlg, IDC_RAD_ERRBAL, TRUE); break; + } + + if(options.encrypt_session) + CheckDlgButton(hwndDlg, IDC_RAD_ENC, TRUE); + else + CheckDlgButton(hwndDlg, IDC_RAD_NOENC, TRUE); + + return FALSE; + } + case WM_COMMAND: + if ( HIWORD( wParam ) == EN_CHANGE && ( HWND )lParam == GetFocus()) { + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + if ( HIWORD( wParam ) == CBN_SELCHANGE) { + int sel = SendDlgItemMessage(hwndDlg, IDC_CMB_CLIENT, CB_GETCURSEL, 0, 0); + int id = SendDlgItemMessage(hwndDlg, IDC_CMB_CLIENT, CB_GETITEMDATA, sel, 0); + bool custom = (id == client_ids[sizeof(client_ids) / sizeof(int) - 1]); + + if(!custom) + SetDlgItemInt(hwndDlg, IDC_ED_CLIENTID, id, FALSE); + else + SetDlgItemInt(hwndDlg, IDC_ED_CLIENTID, DEFAULT_ID, FALSE); + + HWND hw = GetDlgItem(hwndDlg, IDC_ED_CLIENTID); + EnableWindow(hw, custom); + + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + + if ( HIWORD( wParam ) == BN_CLICKED ) { + switch( LOWORD( wParam )) { + case IDC_BTN_UPLOADCONTACTS: + { + HWND hBut = GetDlgItem(hwndDlg, IDC_BTN_UPLOADCONTACTS); + EnableWindow(hBut, FALSE); + hBut = GetDlgItem(hwndDlg, IDC_BTN_IMPORTCONTACTS); + EnableWindow(hBut, FALSE); + + ExportContactsToServer(); + + SendMessage(hwndDlg, WMU_STORECOMPLETE, 0, 0); + } + return TRUE; + case IDC_BTN_IMPORTCONTACTS: + { + OPENFILENAME ofn = {0}; + TCHAR import_filename[MAX_PATH]; + import_filename[0] = 0; + + ofn.lStructSize = sizeof(ofn); + ofn.lpstrFile = import_filename; + ofn.hwndOwner = hwndDlg; + ofn.Flags = CC_FULLOPEN; + ofn.nMaxFile = MAX_PATH; + ofn.lpstrFilter = _T("All\0*.*\0"); + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.Flags = OFN_PATHMUSTEXIST; + + if(GetOpenFileName(&ofn) == TRUE) { + HWND hBut = GetDlgItem(hwndDlg, IDC_BTN_UPLOADCONTACTS); + EnableWindow(hBut, FALSE); + hBut = GetDlgItem(hwndDlg, IDC_BTN_IMPORTCONTACTS); + EnableWindow(hBut, FALSE); + + ImportContactsFromFile(ofn.lpstrFile); + + SendMessage(hwndDlg, WMU_STORECOMPLETE, 0, 0); + } + } + return TRUE; + case IDC_CHK_GETSERVERCONTACTS: + case IDC_CHK_ENCMESSAGES: + case IDC_RAD_ERRMB: + case IDC_RAD_ERRBAL: + case IDC_RAD_ERRPOP: + case IDC_CHK_USERCP: + case IDC_CHK_ADDCONTACTS: + case IDC_CHK_IDLEAWAY: + case IDC_CHK_OLDDEFAULTVER: + case IDC_RAD_ENC: + case IDC_RAD_NOENC: + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + return TRUE; + case IDC_RAD_ANSI: + case IDC_RAD_UTF8: + case IDC_RAD_OEM: + case IDC_RAD_UTF7: + case IDC_RAD_USERCP: + { + HWND hw = GetDlgItem(hwndDlg, IDC_CHK_USERCP); + EnableWindow(hw, !IsDlgButtonChecked(hwndDlg, IDC_RAD_USERCP)); + } + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + return TRUE; + } + } + break; + + case WMU_STORECOMPLETE: + { + HWND hBut = GetDlgItem(hwndDlg, IDC_BTN_UPLOADCONTACTS); + EnableWindow(hBut, TRUE); + hBut = GetDlgItem(hwndDlg, IDC_BTN_IMPORTCONTACTS); + EnableWindow(hBut, TRUE); + } + return TRUE; + case WM_NOTIFY: + if (((LPNMHDR)lParam)->code == PSN_APPLY ) { + wchar_t ws[2048]; + char *utf; + + GetDlgItemTextW(hwndDlg, IDC_ED_SNAME, ws, LSTRINGLEN); + strcpy(options.server_name, utf = w2u(ws)); free(utf); + GetDlgItemTextW(hwndDlg, IDC_ED_NAME, ws, LSTRINGLEN); + strcpy(options.id, utf = w2u(ws)); free(utf); + GetDlgItemTextW(hwndDlg, IDC_ED_PWORD, ws, LSTRINGLEN); + strcpy(options.pword, utf = w2u(ws)); free(utf); + + BOOL translated; + int port = GetDlgItemInt(hwndDlg, IDC_ED_PORT, &translated, FALSE); + if(translated) + options.port = port; + + options.get_server_contacts = (IsDlgButtonChecked(hwndDlg, IDC_CHK_GETSERVERCONTACTS) != FALSE); + + int sel = SendDlgItemMessage(hwndDlg, IDC_CMB_CLIENT, CB_GETCURSEL, 0, 0); + int id = SendDlgItemMessage(hwndDlg, IDC_CMB_CLIENT, CB_GETITEMDATA, sel, 0); + + if(id == client_ids[sizeof(client_ids) / sizeof(int) - 1]) { + BOOL trans; + id = GetDlgItemInt(hwndDlg, IDC_ED_CLIENTID, &trans, FALSE); + if(trans) + options.client_id = id; + } else + options.client_id = id; + + if(IsDlgButtonChecked(hwndDlg, IDC_RAD_ERRMB)) options.err_method = ED_MB; + else if(IsDlgButtonChecked(hwndDlg, IDC_RAD_ERRBAL)) options.err_method = ED_BAL; + else if(IsDlgButtonChecked(hwndDlg, IDC_RAD_ERRPOP)) options.err_method = ED_POP; + + options.add_contacts = (IsDlgButtonChecked(hwndDlg, IDC_CHK_ADDCONTACTS) != FALSE); + options.encrypt_session = (IsDlgButtonChecked(hwndDlg, IDC_RAD_ENC) != FALSE); + options.idle_as_away = (IsDlgButtonChecked(hwndDlg, IDC_CHK_IDLEAWAY) != FALSE); + + options.use_old_default_client_ver = (IsDlgButtonChecked(hwndDlg, IDC_CHK_OLDDEFAULTVER) != FALSE); + + SaveOptions(); + + return TRUE; + } + break; + case WM_DESTROY: + hWndOptions = 0; + break; + } + + return FALSE; +} + +int OptInit(WPARAM wParam,LPARAM lParam) +{ + OPTIONSDIALOGPAGE odp = { 0 }; + TCHAR *temp_proto = a2t(PROTO); + + odp.flags = ODPF_BOLDGROUPS; +#ifdef _UNICODE + odp.flags |= ODPF_UNICODE; +#endif + odp.cbSize = sizeof(odp); + odp.position = -790000000; + odp.hInstance = hInst; + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTNET); + odp.ptszTitle = TranslateTS(temp_proto); + odp.ptszGroup = TranslateT("Network"); + odp.pfnDlgProc = DlgProcOptNet; + CallService( MS_OPT_ADDPAGE, wParam,( LPARAM )&odp ); + + free(temp_proto); + + return 0; +} + +void LoadOptions() { + DBVARIANT dbv; + + if(!DBGetContactSettingUtf(0, PROTO, "ServerName", &dbv)) { + strncpy(options.server_name, dbv.pszVal, LSTRINGLEN); + DBFreeVariant(&dbv); + } + if(!DBGetContactSettingUtf(0, PROTO, "stid", &dbv)) { + strncpy(options.id, dbv.pszVal, LSTRINGLEN); + DBFreeVariant(&dbv); + } + if(!DBGetContactSettingUtf(0, PROTO, "PWord", &dbv)) { + strncpy(options.pword, dbv.pszVal, LSTRINGLEN); + DBFreeVariant(&dbv); + + CallService(MS_DB_CRYPT_DECODESTRING, (WPARAM)LSTRINGLEN, (LPARAM)options.pword); + } + + options.port = DBGetContactSettingDword(0, PROTO, "ServerPort", DEFAULT_PORT); + options.get_server_contacts = (DBGetContactSettingByte(0, PROTO, "GetServerContacts", 1) == 1); + options.client_id = DBGetContactSettingDword(0, PROTO, "ClientID", DEFAULT_ID); + + // if popups not installed, will be changed to 'ED_BAL' (balloons) in main.cpp, modules loaded + options.err_method = (ErrorDisplay)DBGetContactSettingByte(0, PROTO, "ErrorDisplay", ED_POP); + + options.add_contacts = (DBGetContactSettingByte(0, PROTO, "AutoAddContacts", 0) == 1); + options.encrypt_session = (DBGetContactSettingByte(0, PROTO, "EncryptSession", 0) == 1); + options.idle_as_away = (DBGetContactSettingByte(0, PROTO, "IdleAsAway", 1) == 1); + + options.use_old_default_client_ver = (DBGetContactSettingByte(0, PROTO, "UseOldClientVer", 0) == 1); +} + +void SaveOptions() { + DBWriteContactSettingStringUtf(0, PROTO, "ServerName", options.server_name); + + DBWriteContactSettingStringUtf(0, PROTO, "stid", options.id); + //DBWriteContactSettingString(0, PROTO, "Nick", options.id); + + char buff[LSTRINGLEN]; + strcpy(buff, options.pword); + CallService(MS_DB_CRYPT_ENCODESTRING, (WPARAM)LSTRINGLEN, (LPARAM)buff); + DBWriteContactSettingStringUtf(0, PROTO, "PWord", buff); + + DBWriteContactSettingDword(0, PROTO, "ServerPort", options.port); + DBWriteContactSettingByte(0, PROTO, "GetServerContacts", options.get_server_contacts ? 1 : 0); + DBWriteContactSettingDword(0, PROTO, "ClientID", options.client_id); + DBWriteContactSettingByte(0, PROTO, "ErrorDisplay", options.err_method); + + DBWriteContactSettingByte(0, PROTO, "AutoAddContacts", options.add_contacts ? 1 : 0); + DBWriteContactSettingByte(0, PROTO, "EncryptSession", options.encrypt_session ? 1 : 0); + DBWriteContactSettingByte(0, PROTO, "IdleAsAway", options.idle_as_away ? 1 : 0); + + DBWriteContactSettingByte(0, PROTO, "UseOldClientVer", options.use_old_default_client_ver ? 1 : 0); +} diff --git a/plugins/!NotAdopted/sametime/options.h b/plugins/!NotAdopted/sametime/options.h new file mode 100644 index 0000000000..b5569c0e52 --- /dev/null +++ b/plugins/!NotAdopted/sametime/options.h @@ -0,0 +1,44 @@ +#ifndef _OPTIONS_INC +#define _OPTIONS_INC + +#include "common.h" +#include "session.h" +#include "userlist.h" +#include "utils.h" + +#include "resource.h" + +#define LSTRINGLEN 256 + +typedef enum {ED_MB, ED_POP, ED_BAL} ErrorDisplay; +typedef enum {CPT_USER, CPT_ANSI, CPT_UTF8, CPT_OEM, CPT_UTF7} CodePageType; + +typedef struct Options_tag { + char server_name[LSTRINGLEN]; + char id[LSTRINGLEN]; + char pword[LSTRINGLEN]; + int port; + bool get_server_contacts; + int client_id; + ErrorDisplay err_method; + bool add_contacts; + bool encrypt_session; + bool idle_as_away; + bool use_old_default_client_ver; +} Options; + +extern Options options; + +#define DEFAULT_PORT 1533 + +BOOL CALLBACK DlgProcOptNet(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); + +int OptInit(WPARAM wParam,LPARAM lParam); +void LoadOptions(); +void SaveOptions(); + +extern HWND hWndOptions; + +#define WMU_STORECOMPLETE (WM_USER + 110) + +#endif diff --git a/plugins/!NotAdopted/sametime/part.ico b/plugins/!NotAdopted/sametime/part.ico new file mode 100644 index 0000000000..1b698a76b3 Binary files /dev/null and b/plugins/!NotAdopted/sametime/part.ico differ diff --git a/plugins/!NotAdopted/sametime/places.cpp b/plugins/!NotAdopted/sametime/places.cpp new file mode 100644 index 0000000000..9012c177f0 --- /dev/null +++ b/plugins/!NotAdopted/sametime/places.cpp @@ -0,0 +1,54 @@ +#include "places.h" + +mwServicePlace *service_places = 0; + +void mwServicePlace_opened(struct mwPlace *place) { +} + +void mwServicePlace_closed(struct mwPlace *place, guint32 code) { +} + +void mwServicePlace_peerJoined(struct mwPlace *place, const struct mwIdBlock *peer) { +} + + +void mwServicePlace_peerParted(struct mwPlace *place, const struct mwIdBlock *peer) { +} + + +void mwServicePlace_peerSetAttribute(struct mwPlace *place, const struct mwIdBlock *peer, guint32 attr, struct mwOpaque *o) { +} + + +void mwServicePlace_peerUnsetAttribute(struct mwPlace *place, const struct mwIdBlock *peer, guint32 attr) { +} + + +void mwServicePlace_message(struct mwPlace *place, const struct mwIdBlock *who, const char *msg) { +} + + +void mwServicePlace_clear(struct mwServicePlace *srvc) { +} + + +mwPlaceHandler mwPlace_handler = { + mwServicePlace_opened, + mwServicePlace_closed, + mwServicePlace_peerJoined, + mwServicePlace_peerParted, + mwServicePlace_peerSetAttribute, + mwServicePlace_peerUnsetAttribute, + mwServicePlace_message, + mwServicePlace_clear +}; + +void InitPlaces(mwSession *session) { + mwSession_addService(session, (mwService *)(service_places = mwServicePlace_new(session, &mwPlace_handler))); +} + +void DeinitPlaces(mwSession *session) { + mwSession_removeService(session, mwService_PLACE); + mwService_free((mwService *)service_places); + service_places = 0; +} diff --git a/plugins/!NotAdopted/sametime/places.h b/plugins/!NotAdopted/sametime/places.h new file mode 100644 index 0000000000..a058fc6b43 --- /dev/null +++ b/plugins/!NotAdopted/sametime/places.h @@ -0,0 +1,11 @@ +#ifndef _PLACES_INC +#define _PLACES_INC + +#include "common.h" +#include "userlist.h" +#include "options.h" + +void InitPlaces(mwSession *session); +void DeinitPlaces(mwSession *session); + +#endif \ No newline at end of file diff --git a/plugins/!NotAdopted/sametime/resource.h b/plugins/!NotAdopted/sametime/resource.h new file mode 100644 index 0000000000..ab630bd238 --- /dev/null +++ b/plugins/!NotAdopted/sametime/resource.h @@ -0,0 +1,57 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by resource.rc +// +#define IDD_DIALOG1 101 +#define IDD_OPTNET 101 +#define IDI_ICON_PROTO 102 +#define IDI_ICON_LEAVE 104 +#define IDI_ICON_INVITE 105 +#define IDI_ANNOUNCE 107 +#define IDD_SESSIONANNOUNCE 108 +#define IDD_USERSEARCH 109 +#define IDC_ED_SNAME 1000 +#define IDC_ED_NAME 1001 +#define IDC_ED_PWORD 1002 +#define IDC_ED_PORT 1003 +#define IDC_CHK_GETSERVERCONTACTS 1004 +#define IDC_BTN_UPLOADCONTACTS 1005 +#define IDC_CHK_ENCMESSAGES 1006 +#define IDC_BTN_IMPORTCONTACTS 1007 +#define IDC_CHK_UTF8 1008 +#define IDC_CHK_ADDCONTACTS 1008 +#define IDC_CHK_IDLEAWAY 1009 +#define IDC_CMB_CLIENT 1010 +#define IDC_ED_CLIENTID 1011 +#define IDC_RAD_ERRMB 1012 +#define IDC_RAD_ERRPOP 1013 +#define IDC_RAD_ERRBAL 1014 +#define IDC_RAD_ANSI 1015 +#define IDC_CHK_OLDDEFAULTVER 1016 +#define IDC_RAD_UTF8 1017 +#define IDC_CHK_USERCP 1018 +#define IDC_RAD_OEM 1019 +#define IDC_RAD_ENC 1020 +#define IDC_RAD_UTF7 1021 +#define IDC_RAD_NOENC 1022 +#define IDC_RAD_ANSI2 1023 +#define IDC_RAD_USERCP 1024 +#define IDC_ED_ANMSG 1025 +#define IDC_LST_ANTO 1026 +#define IDC_BUT_SELALL 1027 +#define IDC_BUT_SELINV 1028 +#define IDC_ST_CLIENTVER 1029 +#define IDC_ST_SERVERVER 1030 +#define IDC_EDIT1 1031 +#define IDC_ST_LIBVER 1032 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 111 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1033 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/plugins/!NotAdopted/sametime/resource.rc b/plugins/!NotAdopted/sametime/resource.rc new file mode 100644 index 0000000000..69992e24a9 --- /dev/null +++ b/plugins/!NotAdopted/sametime/resource.rc @@ -0,0 +1,175 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_OPTNET DIALOGEX 0, 0, 314, 203 +STYLE DS_SETFONT | WS_CHILD +FONT 8, "MS Sans Serif", 0, 0, 0x0 +BEGIN + RTEXT "Server name:",IDC_STATIC,14,61,49,8 + EDITTEXT IDC_ED_SNAME,70,57,92,14,ES_AUTOHSCROLL + RTEXT "Port:",IDC_STATIC,14,81,49,8 + EDITTEXT IDC_ED_PORT,70,77,36,14,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER + CONTROL "Get contacts from server",IDC_CHK_GETSERVERCONTACTS, + "Button",BS_AUTOCHECKBOX | BS_MULTILINE | WS_TABSTOP,180,15,115,15 + PUSHBUTTON "Upload Contacts",IDC_BTN_UPLOADCONTACTS,180,29,115,15 + PUSHBUTTON "Import from file...",IDC_BTN_IMPORTCONTACTS,180,48,115,15 + COMBOBOX IDC_CMB_CLIENT,10,109,114,107,CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_ED_CLIENTID,126,109,37,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER + RTEXT "Id:",IDC_STATIC,14,17,49,8 + EDITTEXT IDC_ED_NAME,70,13,92,14,ES_AUTOHSCROLL + RTEXT "Password:",IDC_STATIC,14,39,49,8 + EDITTEXT IDC_ED_PWORD,70,35,92,14,ES_PASSWORD | ES_AUTOHSCROLL + GROUPBOX "Error Display",IDC_STATIC,7,143,161,53 + CONTROL "Use message boxes",IDC_RAD_ERRMB,"Button",BS_AUTORADIOBUTTON | WS_GROUP,20,156,122,10 + CONTROL "Use popups",IDC_RAD_ERRPOP,"Button",BS_AUTORADIOBUTTON,20,169,122,10 + CONTROL "Use system tray baloons",IDC_RAD_ERRBAL,"Button",BS_AUTORADIOBUTTON,21,182,122,10 + GROUPBOX "Connection",IDC_STATIC,7,7,161,91 + GROUPBOX "Contacts",IDC_STATIC,172,7,135,91 + GROUPBOX "Client ID",IDC_STATIC,7,98,161,44 + CONTROL "Auto-add new contacts",IDC_CHK_ADDCONTACTS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,180,67,115,11 + GROUPBOX "Encryption",IDC_STATIC,172,98,135,44 + CONTROL "40 or 128 bit",IDC_RAD_ENC,"Button",BS_AUTORADIOBUTTON | WS_GROUP,190,113,88,10 + CONTROL "None",IDC_RAD_NOENC,"Button",BS_AUTORADIOBUTTON,190,126,88,10 + CTEXT "Static",IDC_ST_CLIENTVER,185,165,102,12,SS_SUNKEN + CTEXT "Static",IDC_ST_SERVERVER,185,180,102,12,SS_SUNKEN + CONTROL "Treat 'idle' as 'away'",IDC_CHK_IDLEAWAY,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,180,81,115,11 + CTEXT "Meanwhile lib ver: 1.0.2",IDC_ST_LIBVER,185,150,102,12 + GROUPBOX "",IDC_STATIC,172,143,135,53 + CONTROL "Use old client version",IDC_CHK_OLDDEFAULTVER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,33,127,115,11 +END + +IDD_SESSIONANNOUNCE DIALOG 0, 0, 286, 263 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Send Announcement" +FONT 8, "MS Sans Serif" +BEGIN + DEFPUSHBUTTON "OK",IDOK,95,242,50,14 + PUSHBUTTON "Cancel",IDCANCEL,150,242,50,14 + EDITTEXT IDC_ED_ANMSG,10,20,264,60,ES_MULTILINE | ES_AUTOVSCROLL | ES_WANTRETURN + LTEXT "Message Text:",IDC_STATIC,9,9,142,8 + CONTROL "List1",IDC_LST_ANTO,"SysListView32",LVS_REPORT | WS_BORDER | WS_TABSTOP,10,92,197,132 + PUSHBUTTON "Select All",IDC_BUT_SELALL,214,122,58,36 + PUSHBUTTON "Invert\n Selection",IDC_BUT_SELINV,214,167,58,36,BS_MULTILINE +END + +IDD_USERSEARCH DIALOG 0, 0, 155, 86 +STYLE DS_SETFONT | WS_POPUP +FONT 8, "MS Sans Serif" +BEGIN + EDITTEXT IDC_EDIT1,34,32,103,14,ES_AUTOHSCROLL + LTEXT "ID:",IDC_STATIC,18,35,10,8 +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_OPTNET, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 307 + TOPMARGIN, 7 + BOTTOMMARGIN, 196 + END + + IDD_SESSIONANNOUNCE, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 279 + TOPMARGIN, 7 + BOTTOMMARGIN, 256 + END + + IDD_USERSEARCH, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 148 + TOPMARGIN, 7 + BOTTOMMARGIN, 79 + END +END +#endif // APSTUDIO_INVOKED + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON_PROTO ICON "online.ico" +IDI_ICON_LEAVE ICON "part.ico" +IDI_ICON_INVITE ICON "invite.ico" +IDI_ANNOUNCE ICON "announce.ico" +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/plugins/!NotAdopted/sametime/sametime.cpp b/plugins/!NotAdopted/sametime/sametime.cpp new file mode 100644 index 0000000000..dbc26851d7 --- /dev/null +++ b/plugins/!NotAdopted/sametime/sametime.cpp @@ -0,0 +1,845 @@ +// sametime.cpp: Defines the entry point for the DLL application. + +#include "common.h" +#include "options.h" +#include "session.h" +#include "userlist.h" +#include "messaging.h" +#include "files.h" +#include "conference.h" +#include "utils.h" + +#define FAILED_MESSAGE_HANDLE 99998 + +char PROTO[64]; +char PROTO_GROUPS[128]; + +bool unicode_chat = false; +int code_page = CP_ACP; +MM_INTERFACE mmi; +UTF8_INTERFACE utfi; + +HINSTANCE hInst; +PLUGINLINK *pluginLink; + +HANDLE hNetlibUser = 0; + +HANDLE hWindowEventHook = 0, hIdleEventHook = 0; + +HANDLE mainThread; +DWORD mainThreadId; + +int previous_status, current_status; + +bool is_idle = false; + +// plugin stuff +PLUGININFOEX pluginInfo={ + sizeof(PLUGININFOEX), + "Sametime Protocol", + PLUGIN_MAKE_VERSION(0, 5, 9, 1), + "Implementation of Instant Messaging for the Lotus Sametime protocol.", + "Scott Ellis", + "mail@scottellis.com.au", + "© 2005 Scott Ellis", + "http://www.scottellis.com.au/", + UNICODE_AWARE, //not transient + 0, //doesn't replace anything built-in + { 0xf1b0ba1b, 0xc91, 0x4313, { 0x85, 0xeb, 0x22, 0x50, 0x69, 0xd4, 0x4d, 0x1 } } // {F1B0BA1B-0C91-4313-85EB-225069D44D01} +}; + +extern "C" BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved) +{ + hInst=hinstDLL; + return TRUE; +} + +extern "C" SAMETIME_API PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion) +{ + pluginInfo.cbSize = sizeof(PLUGININFO); + return (PLUGININFO*)&pluginInfo; +} + +static const MUUID interfaces[] = {MIID_PROTOCOL, MIID_LAST}; +extern "C" __declspec(dllexport) const MUUID* MirandaPluginInterfaces(void) +{ + return interfaces; +} + + +extern "C" SAMETIME_API PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion) +{ + return &pluginInfo; +} + + + +// protocol related services +int GetCaps(WPARAM wParam,LPARAM lParam) +{ + int ret = 0; + switch (wParam) { + case PFLAGNUM_1: + //ret = PF1_IM | PF1_BASICSEARCH | PF1_ADDSEARCHRES | PF1_MODEMSG | PF1_FILE | PF1_CHAT; + ret = PF1_IM | PF1_BASICSEARCH | PF1_EXTSEARCHUI | PF1_ADDSEARCHRES | PF1_MODEMSG | PF1_FILE | PF1_CHAT; + break; + case PFLAGNUM_2: + ret = PF2_ONLINE | PF2_SHORTAWAY | PF2_HEAVYDND | PF2_LIGHTDND; + break; + case PFLAGNUM_3: + ret = PF2_ONLINE | PF2_SHORTAWAY | PF2_HEAVYDND; + break; + case PFLAGNUM_4: + ret = PF4_SUPPORTTYPING; + break; + case PFLAGNUM_5: + ret = PF2_LIGHTDND; + break; + case PFLAG_UNIQUEIDTEXT: + ret = (int) Translate("Id"); + break; + case PFLAG_MAXLENOFMESSAGE: + ret = MAX_MESSAGE_SIZE; + break; + case PFLAG_UNIQUEIDSETTING: + ret = (int) "stid"; + break; + } + return ret; +} + +/** Copy the name of the protocole into lParam +* @param wParam : max size of the name +* @param lParam : reference to a char *, which will hold the name +*/ +int GetName(WPARAM wParam,LPARAM lParam) +{ + char *name = (char *)Translate(PROTO); + size_t size = min(strlen(name),wParam-1); // copy only the first size bytes. + if(strncpy((char *)lParam,name,size)==NULL) + return 1; + ((char *)lParam)[size]='\0'; + return 0; +} + +/** Loads the icon corresponding to the status +* Called by the CList when the status changes. +* @param wParam : one of the following values : \n + PLI_PROTOCOL | PLI_ONLINE | PLI_OFFLINE +* @return an \c HICON in which the icon has been loaded. +*/ +int LoadIcon(WPARAM wParam,LPARAM lParam) +{ + + UINT id; + switch (wParam & 0xFFFF) + { + case PLI_PROTOCOL: + id = IDI_ICON_PROTO; + break; + case PLI_ONLINE: + id = IDI_ICON_PROTO; + break; + case PLI_OFFLINE: + id = IDI_ICON_PROTO; + break; + default: + return (int) (HICON) NULL; + } + + return (int) LoadImage(hInst, MAKEINTRESOURCE(id), IMAGE_ICON, + GetSystemMetrics(wParam & PLIF_SMALL ? SM_CXSMICON : SM_CXICON), + GetSystemMetrics(wParam & PLIF_SMALL ? SM_CYSMICON : SM_CYICON), 0); + return 0; +} + + +/** Changes the status and notifies everybody +* @param wParam : The new mode +* @param lParam : Allways set to 0. +*/ +int SetStatus(WPARAM wParam,LPARAM lParam) +{ + if((int)wParam != ID_STATUS_OFFLINE) { + if(current_status == ID_STATUS_OFFLINE) + LogIn((int)wParam, hNetlibUser); + else + SetSessionStatus((int)wParam); + } else if(current_status != ID_STATUS_OFFLINE && (int)wParam == ID_STATUS_OFFLINE) { + LogOut(); + } + + return 0; +} + +/** Returns the current status +*/ +int GetStatus(WPARAM wParam,LPARAM lParam) +{ + return current_status; +} + + +////////////////////////////////////////////////////////// +/// Copied from MSN plugin - sent acks need to be from different thread :( +////////////////////////////////////////////////////////// +typedef struct tag_TFakeAckParams +{ + HANDLE hEvent; + HANDLE hContact; + LPARAM lParam; +} TFakeAckParams; + + +static DWORD CALLBACK sttFakeAckInfoSuccess( LPVOID param ) +{ + TFakeAckParams *tParam = ( TFakeAckParams* )param; + WaitForSingleObject( tParam->hEvent, INFINITE ); + + Sleep( 100 ); + ProtoBroadcastAck(PROTO, tParam->hContact, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, ( HANDLE )1, 0 ); + + CloseHandle( tParam->hEvent ); + free(tParam); + return 0; +} + +static DWORD CALLBACK sttFakeAckMessageSuccess( LPVOID param ) +{ + TFakeAckParams *tParam = ( TFakeAckParams* )param; + WaitForSingleObject( tParam->hEvent, INFINITE ); + + Sleep( 100 ); + ProtoBroadcastAck(PROTO, tParam->hContact, ACKTYPE_MESSAGE, ACKRESULT_SUCCESS, ( HANDLE )tParam->lParam, 0 ); + + CloseHandle( tParam->hEvent ); + free(tParam); + return 0; +} + +static DWORD CALLBACK sttFakeAckMessageFailed( LPVOID param ) +{ + TFakeAckParams *tParam = ( TFakeAckParams* )param; + WaitForSingleObject( tParam->hEvent, INFINITE ); + + Sleep( 100 ); + ProtoBroadcastAck(PROTO, tParam->hContact, ACKTYPE_MESSAGE, ACKRESULT_FAILED, (HANDLE)FAILED_MESSAGE_HANDLE, tParam->lParam); + + CloseHandle( tParam->hEvent ); + free(tParam); + return 0; +} + +int GetInfo(WPARAM wParam, LPARAM lParam) { + CCSDATA *ccs = (CCSDATA *) lParam; + DWORD dwThreadId; + HANDLE hEvent; + TFakeAckParams *tfap; + + hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + + tfap = (TFakeAckParams *)malloc(sizeof(TFakeAckParams)); + tfap->hContact = ccs->hContact; + tfap->hEvent = hEvent; + + CloseHandle( CreateThread( NULL, 0, sttFakeAckInfoSuccess, tfap, 0, &dwThreadId )); + SetEvent( hEvent ); + + return 0; +} + +int BasicSearch(WPARAM wParam, LPARAM lParam) { + const char *szId = (const char *)lParam; + + return (int)SearchForUser(szId); +} + +int AddToList(WPARAM wParam, LPARAM lParam) { + MYPROTOSEARCHRESULT *mpsr = (MYPROTOSEARCHRESULT *)lParam; + + return (int)AddSearchedUser(mpsr, wParam & PALF_TEMPORARY); +} + +int RecvMessage(WPARAM wParam, LPARAM lParam) { + DBEVENTINFO dbei; + CCSDATA* ccs = (CCSDATA*)lParam; + PROTORECVEVENT* pre = (PROTORECVEVENT*)ccs->lParam; + + DBDeleteContactSetting(ccs->hContact, "CList", "Hidden"); + + ZeroMemory(&dbei, sizeof(dbei)); + dbei.cbSize = sizeof(dbei); + dbei.szModule = PROTO; + dbei.timestamp = pre->timestamp; + dbei.flags = (pre->flags & PREF_CREATEREAD) ? DBEF_READ : 0; + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.cbBlob = strlen(pre->szMessage) + 1; + if ( pre->flags & PREF_UNICODE ) + dbei.cbBlob *= ( sizeof( wchar_t )+1 ); + dbei.pBlob = (PBYTE)pre->szMessage; + + CallService(MS_DB_EVENT_ADD, (WPARAM)ccs->hContact, (LPARAM)&dbei); + return 0; +} + +int STSendMessage(WPARAM wParam, LPARAM lParam) { + if (lParam) + { + CCSDATA* ccs = (CCSDATA*)lParam; + + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ccs->hContact, 0); + + if(!proto || strcmp(proto, PROTO) != 0 || DBGetContactSettingWord(ccs->hContact, PROTO, "Status", ID_STATUS_OFFLINE) == ID_STATUS_OFFLINE) { + HANDLE hEvent; + TFakeAckParams *tfap; + + hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + + tfap = (TFakeAckParams *)malloc(sizeof(TFakeAckParams)); + tfap->hContact = ccs->hContact; + tfap->hEvent = hEvent; + tfap->lParam = 0; + + CloseHandle( CreateThread( NULL, 0, sttFakeAckMessageFailed, tfap, 0, 0 )); + SetEvent( hEvent ); + return FAILED_MESSAGE_HANDLE; + } + + int ret = 0; + char *p = (char *)ccs->lParam; + if(ccs->wParam & PREF_UNICODE) + ret = (int)SendMessageToUserW(ccs->hContact, (wchar_t *)&p[strlen(p) + 1]); + else + ret = (int)SendMessageToUser(ccs->hContact, (char *)ccs->lParam); + + DWORD dwThreadId; + HANDLE hEvent; + TFakeAckParams *tfap; + + hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + + tfap = (TFakeAckParams *)malloc(sizeof(TFakeAckParams)); + tfap->hContact = ccs->hContact; + tfap->hEvent = hEvent; + tfap->lParam = (LPARAM)ret; + + CloseHandle( CreateThread( NULL, 0, sttFakeAckMessageSuccess, tfap, 0, &dwThreadId )); + SetEvent( hEvent ); + + return ret; + } + return 0; +} + +int STSendMessageW(WPARAM wParam, LPARAM lParam) { + if (lParam) + { + CCSDATA* ccs = (CCSDATA*)lParam; + + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ccs->hContact, 0); + + if(!proto || strcmp(proto, PROTO) != 0 || DBGetContactSettingWord(ccs->hContact, PROTO, "Status", ID_STATUS_OFFLINE) == ID_STATUS_OFFLINE) { + DWORD dwThreadId; + HANDLE hEvent; + TFakeAckParams *tfap; + + hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + + tfap = (TFakeAckParams *)malloc(sizeof(TFakeAckParams)); + tfap->hContact = ccs->hContact; + tfap->hEvent = hEvent; + tfap->lParam = 0; + + CloseHandle( CreateThread( NULL, 0, sttFakeAckMessageFailed, tfap, 0, &dwThreadId )); + SetEvent( hEvent ); + return FAILED_MESSAGE_HANDLE; + } + + int ret = 0; + char *p = (char *)ccs->lParam; + ret = (int)SendMessageToUserW(ccs->hContact, (wchar_t *)&p[strlen(p) + 1]); + + DWORD dwThreadId; + HANDLE hEvent; + TFakeAckParams *tfap; + + hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + + tfap = (TFakeAckParams *)malloc(sizeof(TFakeAckParams)); + tfap->hContact = ccs->hContact; + tfap->hEvent = hEvent; + tfap->lParam = (LPARAM)ret; + + CloseHandle( CreateThread( NULL, 0, sttFakeAckMessageSuccess, tfap, 0, &dwThreadId )); + SetEvent( hEvent ); + + return ret; + } + return 0; +} + +int SetAwayMessage(WPARAM wParam, LPARAM lParam) { + SetSessionAwayMessage(wParam, (char *)lParam); + return 0; +} + +static DWORD CALLBACK sttRecvAway( LPVOID param ) +{ + TFakeAckParams *tParam = ( TFakeAckParams* )param; + WaitForSingleObject( tParam->hEvent, INFINITE ); + + Sleep( 100 ); + UserRecvAwayMessage(tParam->hContact); + + CloseHandle( tParam->hEvent ); + free(tParam); + return 0; +} + +int GetAwayMessage(WPARAM wParam, LPARAM lParam) { + if (lParam && current_status != ID_STATUS_OFFLINE) + { + CCSDATA* ccs = (CCSDATA*)lParam; + + DWORD dwThreadId; + HANDLE hEvent; + TFakeAckParams *tfap; + + hEvent = CreateEvent( NULL, TRUE, FALSE, NULL ); + + tfap = (TFakeAckParams *)malloc(sizeof(TFakeAckParams)); + tfap->hContact = ccs->hContact; + tfap->hEvent = hEvent; + + CloseHandle( CreateThread( NULL, 0, sttRecvAway, tfap, 0, &dwThreadId )); + SetEvent( hEvent ); + + return 1; + } + return 0; +} + +int RecvAwayMessage(WPARAM wParam, LPARAM lParam) { + CCSDATA* ccs = (CCSDATA*)lParam; + PROTORECVEVENT* pre = (PROTORECVEVENT*)ccs->lParam; + + char *msg = utfi.utf8_decodecp(pre->szMessage, code_page, 0); + ProtoBroadcastAck(PROTO, ccs->hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, (LPARAM)msg); + mir_free(msg); + + return 0; +} + +int SendTyping(WPARAM wParam, LPARAM lParam) { + SendTyping((HANDLE)wParam, lParam == PROTOTYPE_SELFTYPING_ON); + return 0; +} + +int RecvFile(WPARAM wParam, LPARAM lParam) +{ + DBEVENTINFO dbei; + CCSDATA* ccs = (CCSDATA*)lParam; + PROTORECVEVENT* pre = (PROTORECVEVENT*)ccs->lParam; + char* szDesc; + char* szFile; + + DBDeleteContactSetting(ccs->hContact, "CList", "Hidden"); + + szFile = pre->szMessage + sizeof(DWORD); + szDesc = szFile + strlen(szFile) + 1; + + ZeroMemory(&dbei, sizeof(dbei)); + dbei.cbSize = sizeof(dbei); + dbei.szModule = PROTO; + dbei.timestamp = pre->timestamp; + dbei.flags = (pre->flags & PREF_CREATEREAD) ? DBEF_READ : 0; + dbei.eventType = EVENTTYPE_FILE; + dbei.cbBlob = sizeof(DWORD) + strlen(szFile) + strlen(szDesc) + 2; + dbei.pBlob = (PBYTE)pre->szMessage; + + CallService(MS_DB_EVENT_ADD, (WPARAM)ccs->hContact, (LPARAM)&dbei); + + return 0; +} + +int AllowFile(WPARAM wParam, LPARAM lParam) { + if (lParam) + { + CCSDATA* ccs = (CCSDATA*)lParam; + return (int)AcceptFileTransfer(ccs->hContact, (HANDLE)(ccs->wParam), (char *)ccs->lParam); + + } + return 0; +} + +int DenyFile(WPARAM wParam, LPARAM lParam) { + if (lParam) + { + CCSDATA* ccs = (CCSDATA*)lParam; + RejectFileTransfer((HANDLE)(ccs->wParam)); + } + return 0; +} + +int SendFiles(WPARAM wParam, LPARAM lParam) { + if (lParam && current_status != ID_STATUS_OFFLINE) + { + CCSDATA* ccs = (CCSDATA*)lParam; + + if (ccs->hContact && ccs->lParam && ccs->wParam) { + HANDLE hContact = ccs->hContact; + char** files = (char**)ccs->lParam; + char* pszDesc = (char*)ccs->wParam; + + if (DBGetContactSettingWord(hContact, PROTO, "Status", ID_STATUS_OFFLINE) != ID_STATUS_OFFLINE) { + return (int)SendFilesToUser(hContact, files, pszDesc); + } + + } + } + return 0; // failure +} + +int CancelFile(WPARAM wParam, LPARAM lParam) { + if (lParam) + { + CCSDATA* ccs = (CCSDATA*)lParam; + CancelFileTransfer((HANDLE)(ccs->wParam)); + } + return 0; +} + +void CreatePluginServices() { + // protocol + CreateProtoServiceFunction(PROTO, PS_GETCAPS, GetCaps); + CreateProtoServiceFunction(PROTO, PS_GETNAME, GetName); + CreateProtoServiceFunction(PROTO, PS_SETSTATUS, SetStatus); + CreateProtoServiceFunction(PROTO, PS_GETSTATUS, GetStatus); + CreateProtoServiceFunction(PROTO, PS_LOADICON,LoadIcon); + CreateProtoServiceFunction(PROTO, PSS_GETINFO,GetInfo); + + CreateProtoServiceFunction(PROTO, PS_BASICSEARCH,BasicSearch); + CreateProtoServiceFunction(PROTO, PS_ADDTOLIST,AddToList); + + CreateProtoServiceFunction(PROTO, PSR_MESSAGE, RecvMessage); + + CreateProtoServiceFunction(PROTO, PSS_MESSAGE, STSendMessage); + CreateProtoServiceFunction(PROTO, PSS_MESSAGE"W", STSendMessageW); + + CreateProtoServiceFunction(PROTO, PS_SETAWAYMSG,SetAwayMessage); + CreateProtoServiceFunction(PROTO, PSS_GETAWAYMSG,GetAwayMessage); + CreateProtoServiceFunction(PROTO, PSR_AWAYMSG,RecvAwayMessage); + + CreateProtoServiceFunction(PROTO, PSS_USERISTYPING, SendTyping); + + CreateProtoServiceFunction(PROTO, PSR_FILE, RecvFile); + CreateProtoServiceFunction(PROTO, PSS_FILEALLOW, AllowFile); + CreateProtoServiceFunction(PROTO, PSS_FILEDENY, DenyFile); + CreateProtoServiceFunction(PROTO, PSS_FILE, SendFiles); + + CreateProtoServiceFunction(PROTO, PSS_FILECANCEL, CancelFile); + + CreateProtoServiceFunction(PROTO, PS_CREATEADVSEARCHUI, CreateSearchDialog); + CreateProtoServiceFunction(PROTO, PS_SEARCHBYADVANCED, SearchFromDialog); +} + +void DestroyProtoServiceFunction(char *szModule, char *szService) { + char szFullServiceName[512]; + strcpy(szFullServiceName, szModule); + strcat(szFullServiceName, szService); + DestroyServiceFunction(szFullServiceName); +} + +void DestroyPluginServices() { + DestroyProtoServiceFunction(PROTO,PSS_USERISTYPING); + + DestroyProtoServiceFunction(PROTO, PSR_FILE); + DestroyProtoServiceFunction(PROTO, PSS_FILEALLOW); + DestroyProtoServiceFunction(PROTO, PSS_FILEDENY); + DestroyProtoServiceFunction(PROTO, PSS_FILE); + + DestroyProtoServiceFunction(PROTO, PSS_FILECANCEL); + + DestroyProtoServiceFunction(PROTO, PS_BASICSEARCH); + DestroyProtoServiceFunction(PROTO, PS_ADDTOLIST); + + DestroyProtoServiceFunction(PROTO, PSR_MESSAGE); + + DestroyProtoServiceFunction(PROTO, PSS_MESSAGE); + DestroyProtoServiceFunction(PROTO, PSS_MESSAGE"W"); + + DestroyProtoServiceFunction(PROTO, PS_SETAWAYMSG); + DestroyProtoServiceFunction(PROTO, PSS_GETAWAYMSG); + DestroyProtoServiceFunction(PROTO, PSR_AWAYMSG); + + DestroyProtoServiceFunction(PROTO, PSS_USERISTYPING); + + DestroyProtoServiceFunction(PROTO, PS_GETCAPS); + DestroyProtoServiceFunction(PROTO, PS_GETNAME); + DestroyProtoServiceFunction(PROTO, PS_SETSTATUS); + DestroyProtoServiceFunction(PROTO, PS_GETSTATUS); + DestroyProtoServiceFunction(PROTO, PS_LOADICON); + DestroyProtoServiceFunction(PROTO, PSS_GETINFO); +} + +int WindowEvent(WPARAM wParam, LPARAM lParam) { + MessageWindowEventData *mwed = (MessageWindowEventData *)lParam; + + if(DBGetContactSettingByte(mwed->hContact, PROTO, "ChatRoom", 0)) + return 0; + + if(mwed && (mwed->uType == MSG_WINDOW_EVT_CLOSING || mwed->uType == MSG_WINDOW_EVT_CLOSE)) + CloseIm(mwed->hContact); + + return 0; +} + +int IdleChanged(WPARAM wParam, LPARAM lParam) { + if(!(lParam & IDF_PRIVACY)) { + is_idle = lParam & IDF_ISIDLE ? true : false; + SetIdle(is_idle); + } + + return 0; +} + +int OnModulesLoaded(WPARAM wParam, LPARAM lParam) { + + // register with chat module + + GCREGISTER gcr = {0}; + gcr.cbSize = sizeof(gcr); + gcr.pszModule = PROTO; + gcr.pszModuleDispName = Translate(PROTO); + gcr.dwFlags = GC_TCHAR; + int ret = CallService(MS_GC_REGISTER, 0, (LPARAM)(GCREGISTER *) &gcr); + if(ret == GC_REGISTER_NOUNICODE) { + gcr.dwFlags = 0; + ret = CallService(MS_GC_REGISTER, 0, (LPARAM)(GCREGISTER *) &gcr); + if(ret) + MessageBox(0, TranslateT("You must install chat.dll to enable group chat."), TranslateT("Sametime Error"), MB_OK); + } else { + if(ret) + MessageBox(0, TranslateT("You must install chat.dll to enable group chat."), TranslateT("Sametime Error"), MB_OK); + else { + //MessageBox(0, _T("Unicode chat"), _T("msg"), MB_OK); + unicode_chat = true; + } + } + //int ret = CallService(MS_GC_REGISTER, 0, (LPARAM)(GCREGISTER *) &gcr); + //if(ret) MessageBox(0, TranslateT("You must install chat.dll to enable group chat."), TranslateT("Sametime Error"), MB_OK); + + if(ServiceExists(MS_UPDATE_REGISTER)) { + // register with updater + Update update = {0}; + char szVersion[16]; + + update.cbSize = sizeof(Update); + + update.szComponentName = pluginInfo.shortName; + update.pbVersion = (BYTE *)CreateVersionString(pluginInfo.version, szVersion); + update.cpbVersion = strlen((char *)update.pbVersion); + + // these are the three lines that matter - the archive, the page containing the version string, and the text (or data) + // before the version that we use to locate it on the page + // (note that if the update URL and the version URL point to standard file listing entries, the backend xml + // data will be used to check for updates rather than the actual web page - this is not true for beta urls) + update.szBetaUpdateURL = "http://sje.twosx.net/sametime.zip"; + update.szBetaVersionURL = "http://sje.twosx.net/ver_sametime.html"; + update.pbBetaVersionPrefix = (BYTE *)"Sametime Protocol version "; + + update.cpbBetaVersionPrefix = strlen((char *)update.pbBetaVersionPrefix); + + CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update); + } + + NETLIBUSER nl_user = {0}; + nl_user.cbSize = sizeof(nl_user); + nl_user.szSettingsModule = PROTO; + //nl_user.flags = NUF_OUTGOING | (http_proxying_enabled ? NUF_HTTPCONNS : 0); + nl_user.flags = NUF_OUTGOING | NUF_HTTPCONNS; + nl_user.szDescriptiveName = PROTO; + + hNetlibUser = (HANDLE)CallService(MS_NETLIB_REGISTERUSER, 0, (LPARAM)&nl_user); + + hWindowEventHook = HookEvent(ME_MSG_WINDOWEVENT, WindowEvent); + + hIdleEventHook = HookEvent(ME_IDLE_CHANGED, IdleChanged); + + // funny logic :) ... try to avoid message boxes + // if want baloons but no balloons, try popups + // if want popups but no popups, try baloons + // if, after that, you want balloons but no balloons, revert to message boxes + if(options.err_method == ED_BAL && !ServiceExists(MS_CLIST_SYSTRAY_NOTIFY)) options.err_method = ED_POP; + if(options.err_method == ED_POP && !ServiceExists(MS_POPUP_SHOWMESSAGE)) options.err_method = ED_BAL; + if(options.err_method == ED_BAL && !ServiceExists(MS_CLIST_SYSTRAY_NOTIFY)) options.err_method = ED_MB; + + InitSessionMenu(); + InitConferenceMenu(); + + return 0; +} + +int OnPreShutdown(WPARAM wParam, LPARAM lParam) { + if(current_status != ID_STATUS_OFFLINE) LogOut(); + + UnhookEvent(hIdleEventHook); + UnhookEvent(hWindowEventHook); + + return 0; +} + +int OnShutdown(WPARAM wParam, LPARAM lParam) { + + //DeinitUtils(); + DestroyPluginServices(); + + DeinitConferenceMenu(); + DeinitSessionMenu(); + + Netlib_CloseHandle(hNetlibUser); + + return 0; +} + +void SetAllOffline() { + char *proto; + HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ), hContactNext; + while ( hContact != NULL ) + { + proto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM )hContact,0 ); + if ( proto && !strcmp( PROTO, proto)) { + if(DBGetContactSettingByte(hContact, PROTO, "ChatRoom", 0)) { + // clean up after chat plugin + hContactNext = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0); + hContact = hContactNext; + continue; + } else { + DBWriteContactSettingWord( hContact, PROTO, "Status", ID_STATUS_OFFLINE); + DBWriteContactSettingDword(hContact, PROTO, "IdleTS", 0); + } + } + + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } +} + +void DeleteAllContacts() { + char *proto; + DBVARIANT dbv; + HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ), hContactNext; + while ( hContact != NULL ) + { + hContactNext = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + + if(!DBGetContactSetting(hContact, "Protocol", "p", &dbv)) { + //proto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM )hContact,0 ); + proto = dbv.pszVal; + if ( proto && !strcmp( PROTO, proto)) { + CallService(MS_DB_CONTACT_DELETE, (WPARAM)hContact, 0); + } + DBFreeVariant(&dbv); + } + + hContact = hContactNext; + } +} + +bool IsUnicodeOS() +{ + OSVERSIONINFOW os; + memset(&os, 0, sizeof(OSVERSIONINFOW)); + os.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW); + return (GetVersionExW(&os) != 0); +} + +extern "C" SAMETIME_API int Load(PLUGINLINK *link) +{ + + pluginLink=link; + + if(!IsUnicodeOS()) { + MessageBox(0, TranslateT("This plugin requires a Unicode capable Windows installation."), TranslateT("Sametime Error"), MB_OK | MB_ICONERROR); + return 1; + } + + if ( !ServiceExists( MS_DB_CONTACT_GETSETTING_STR )) { + MessageBox( 0, TranslateT( "This plugin requires db3x plugin version 0.5.1.0 or later." ), TranslateT("Sametime Error"), MB_OK ); + return 1; + } + + DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &mainThread, THREAD_SET_CONTEXT, FALSE, 0 ); + mainThreadId = GetCurrentThreadId(); + + previous_status = current_status = ID_STATUS_OFFLINE; + + if(ServiceExists(MS_LANGPACK_GETCODEPAGE)) + code_page = CallService(MS_LANGPACK_GETCODEPAGE, 0, 0); + + mir_getMMI(&mmi); + mir_getUTFI(&utfi); + + + if(ServiceExists(MS_DB_SETSETTINGRESIDENT)) { // 0.6+ + char buff[256]; + mir_snprintf(buff, 256, "%s/%s", PROTO, "Status"); + CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)buff); + mir_snprintf(buff, 256, "%s/%s", PROTO, "IdleTS"); + CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)buff); + } + + InitAwayMsg(); + InitCritSection(); + + // Get module name from DLL file name + { + char* str1; + char str2[MAX_PATH]; + + GetModuleFileNameA(hInst, str2, MAX_PATH); + str1 = strrchr(str2, '\\'); + if (str1 != NULL && strlen(str1+1) > 4) { + strncpy(PROTO, str1+1, strlen(str1+1)-4); + PROTO[strlen(str1+1)-3] = 0; + } + CharUpperA(PROTO); + + strcpy(PROTO_GROUPS, PROTO); + strcat(PROTO_GROUPS, "_GROUPS"); + } + + PROTOCOLDESCRIPTOR pd = {0}; + pd.cbSize = sizeof(pd); + pd.szName = PROTO; + pd.type = PROTOTYPE_PROTOCOL; + CallService(MS_PROTO_REGISTERMODULE,0,(LPARAM)&pd); + + InitUtils(); + + CreatePluginServices(); + + LoadOptions(); + + //DeleteAllContacts(); //!!! + + SetAllOffline(); + + HookEvent(ME_SYSTEM_MODULESLOADED, OnModulesLoaded); + HookEvent(ME_OPT_INITIALISE, OptInit ); + HookEvent(ME_SYSTEM_PRESHUTDOWN, OnPreShutdown); + HookEvent(ME_SYSTEM_SHUTDOWN, OnShutdown); + + return 0; +} + +extern "C" SAMETIME_API int Unload(void) +{ + DeinitUtils(); + + DeinitAwayMsg(); + DeinitCritSection(); + + return 0; +} + diff --git a/plugins/!NotAdopted/sametime/sametime.h b/plugins/!NotAdopted/sametime/sametime.h new file mode 100644 index 0000000000..14438b2c4f --- /dev/null +++ b/plugins/!NotAdopted/sametime/sametime.h @@ -0,0 +1,14 @@ + +// The following ifdef block is the standard way of creating macros which make exporting +// from a DLL simpler. All files within this DLL are compiled with the SAMETIME_EXPORTS +// symbol defined on the command line. this symbol should not be defined on any project +// that uses this DLL. This way any other project whose source files include this file see +// SAMETIME_API functions as being imported from a DLL, wheras this DLL sees symbols +// defined with this macro as being exported. +#ifdef SAMETIME_EXPORTS +#define SAMETIME_API __declspec(dllexport) +#else +#define SAMETIME_API __declspec(dllimport) +#endif + + diff --git a/plugins/!NotAdopted/sametime/sametime.sln b/plugins/!NotAdopted/sametime/sametime.sln new file mode 100644 index 0000000000..40d9d775e1 --- /dev/null +++ b/plugins/!NotAdopted/sametime/sametime.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "sametime", "sametime.vcproj", "{BE22A21F-73B7-453F-86A0-B867F0056490}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BE22A21F-73B7-453F-86A0-B867F0056490}.Debug|Win32.ActiveCfg = Debug|Win32 + {BE22A21F-73B7-453F-86A0-B867F0056490}.Debug|Win32.Build.0 = Debug|Win32 + {BE22A21F-73B7-453F-86A0-B867F0056490}.Release|Win32.ActiveCfg = Release|Win32 + {BE22A21F-73B7-453F-86A0-B867F0056490}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/plugins/!NotAdopted/sametime/sametime.vcproj b/plugins/!NotAdopted/sametime/sametime.vcproj new file mode 100644 index 0000000000..164d37a650 --- /dev/null +++ b/plugins/!NotAdopted/sametime/sametime.vcproj @@ -0,0 +1,575 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/!NotAdopted/sametime/session.cpp b/plugins/!NotAdopted/sametime/session.cpp new file mode 100644 index 0000000000..33cc2f7468 --- /dev/null +++ b/plugins/!NotAdopted/sametime/session.cpp @@ -0,0 +1,566 @@ +#include "session.h" +#include + +HANDLE sessionThread = 0; +DWORD session_thread_id = 0; + +CRITICAL_SECTION session_cs; + +HANDLE server_connection = 0; +mwSession *session = 0; + +HANDLE hSessionSyncEvent = 0; + +int idle_timerid = 0; +bool idle_status = false; +int login_status; +bool first_online = true; // set our status after the first online status comes from the server + + +#define MS_SAMETIME_MENUANNOUNCESESSION "/SessionAnnounce" +HICON hIconProto, hIconAnnounce; +HANDLE hSessionAnnounceMenuItem; + +struct { + char *szOnline; + char *szAway; + char *szDND; +} AwayMessages; + +void CALLBACK sttMainThreadStatusCallback( ULONG dwParam ) { + if(current_status != dwParam) { + previous_status = current_status; + current_status = dwParam; + ProtoBroadcastAck(PROTO,NULL,ACKTYPE_STATUS,ACKRESULT_SUCCESS, (HANDLE)previous_status, current_status); + } +} + +void __cdecl SessionClear(struct mwSession *) { +} + +int __cdecl SessionWrite(mwSession *, const unsigned char *buf, gsize len) { + if(!server_connection) return 1; + if(Netlib_Send(server_connection, (const char *)buf, len, 0) == SOCKET_ERROR) + return 1; + return 0; +} + +void __cdecl SessionClose(mwSession *) { + Netlib_CloseHandle(server_connection); + server_connection = 0; +} + +void SessionStarted() { + //PUShowMessage("Session started", SM_NOTIFY); + UserListCreate(); + if(options.get_server_contacts) UserListAddStored(); +} + +void SessionStopping() { + UserListDestroy(); +} + +void InitMeanwhileServices() { + if(options.encrypt_session) { + mwSession_addCipher(session, mwCipher_new_RC2_128(session)); + mwSession_addCipher(session, mwCipher_new_RC2_40(session)); + } + + InitUserList(session); + InitMessaging(session); + InitFiles(session); + InitConference(session); +} + +void DeinitMeanwhileServices() { + DeinitConference(session); + DeinitFiles(session); + DeinitMessaging(session); + DeinitUserList(session); + + mwCipher_free(mwSession_getCipher(session, mwCipher_RC2_40)); + mwCipher_free(mwSession_getCipher(session, mwCipher_RC2_128)); +} + +void __cdecl SessionStateChange(mwSession *s, mwSessionState state, gpointer info) { + + switch(state) { + case mwSession_STARTING: + break; + case mwSession_HANDSHAKE: + break; + case mwSession_HANDSHAKE_ACK: + break; + case mwSession_STARTED: + SessionStarted(); + break; + case mwSession_STOPPING: + if((int)info) {// & ERR_FAILURE) { + char *msg = mwError((int)info); + //MessageBoxA(0, Translate(msg), Translate("Sametime Error"), MB_OK | MB_ICONWARNING); + TCHAR *ts = u2t(msg); + ShowError(TranslateTS(ts)); + g_free(msg); + free(ts); + } + + SessionStopping(); + break; + case mwSession_STOPPED: + break; + + case mwSession_LOGIN_REDIR: + //options.server_name = str((char *)info); + strcpy(options.server_name, (char *)info); + LogOut(); + LogIn(login_status, hNetlibUser); + break; + case mwSession_LOGIN_CONT: + break; + + case mwSession_LOGIN: + break; + case mwSession_LOGIN_ACK: + break; + + case mwSession_UNKNOWN: + break; + } +} + +void __cdecl SessionAdmin(struct mwSession *, const char *text) { + TCHAR *tt = u2t(text); + MessageBox(0, tt, TranslateT("Sametime Administrator Message"), MB_OK); + free(tt); +} + +void __cdecl SessionAnnounce(struct mwSession *, struct mwLoginInfo *from, gboolean may_reply, const char *text) { + TCHAR buff[256], *tt1, *tt2; +#ifdef _UNICODE + _snwprintf(buff, 256, TranslateT("Session Announcement - from '%s'"), tt1 = u2t(from->user_name)); +#else + _snprintf(buff, 256, TranslateT("Session Announcement - from '%s'"), tt1 = u2t(from->user_name)); +#endif + MessageBox(0, TranslateTS(tt2 = u2t(text)), buff, MB_OK); + free(tt1); free(tt2); +} + +void __cdecl SessionSetPrivacyInfo(struct mwSession *) { +} + +void __cdecl SessionSetUserStatus(struct mwSession *session) { + int new_status; + struct mwUserStatus us; + mwUserStatus_clone(&us, mwSession_getUserStatus(session)); + + switch(us.status) { + case mwStatus_ACTIVE: + new_status = ID_STATUS_ONLINE; + break; + case mwStatus_AWAY: + new_status = ID_STATUS_AWAY; + if(idle_status) { + // ignore setting to away by idle module, after we've set ourselves idle + // most standard clients represent idle and away the same way anyway, + // but this allows miranda users to make use of the idle timestamp + + // but show our status in clist as away + QueueUserAPC(sttMainThreadStatusCallback, mainThread, new_status); + + mwUserStatus_clear(&us); + return; + } + break; + case mwStatus_BUSY: + new_status = ID_STATUS_DND; + break; + case mwStatus_IDLE: + new_status = ID_STATUS_AWAY; + if(!first_online && !options.idle_as_away) { // show our status in clist as away if idle when going online or treating idle as away + mwUserStatus_clear(&us); + return; + } + break; + case 8: // new 'in a meeting' status, not handled by meanwhile lib + new_status = ID_STATUS_OCCUPIED; + break; + default: + { + char buff[512]; + sprintf(buff, "Unknown user status: %d", us.status); + PUShowMessage(buff, SM_WARNING); + } + mwUserStatus_clear(&us); + // just go online...to prevent us getting stuck 'connecting' + new_status = ID_STATUS_ONLINE; + break; + } + + if(first_online) { + first_online = false; + //PUShowMessage("Setting login status", SM_NOTIFY); + SetSessionStatus(login_status); + } else + QueueUserAPC(sttMainThreadStatusCallback, mainThread, new_status); + + mwUserStatus_clear(&us); +} + +void UpdateSelfStatus() { + EnterCriticalSection(&session_cs); + if(session) SessionSetUserStatus(session); + LeaveCriticalSection(&session_cs); +} + +int SetSessionStatus(int status) { + struct mwUserStatus us; + + if(idle_timerid) KillTimer(0, idle_timerid); + + us.time = (DWORD)time(0); + //us.time = 0; + + switch(status) { + case ID_STATUS_FREECHAT: + case ID_STATUS_ONLINE: + us.desc = AwayMessages.szOnline; us.status = mwStatus_ACTIVE; + break; + case ID_STATUS_NA: + case ID_STATUS_INVISIBLE: + case ID_STATUS_ONTHEPHONE: + case ID_STATUS_OUTTOLUNCH: + case ID_STATUS_AWAY: + us.desc = AwayMessages.szAway; us.status = mwStatus_AWAY; + break; + case ID_STATUS_OCCUPIED: + case ID_STATUS_DND: + us.desc = AwayMessages.szDND; us.status = mwStatus_BUSY; + break; + default: + // act as online for unsupported status + us.desc = AwayMessages.szOnline; us.status = mwStatus_ACTIVE; break; + } + + mwSession_setUserStatus(session, &us); + + return 0; +} + +VOID CALLBACK IdleTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { + KillTimer(0, idle_timerid); + idle_timerid = 0; + + if(idle_status) { + struct mwUserStatus us; + + us.time = (DWORD)time(0); + us.status = mwStatus_IDLE; + us.desc = 0; + mwSession_setUserStatus(session, &us); + } else + SetSessionStatus(current_status); +} + +int SetIdle(bool idle) { + + // set a timer, to wait for any autoaway module which might set our status + if(idle && !idle_status) { + idle_status = true; + if(!idle_timerid) + idle_timerid = SetTimer(0, 0, 200, IdleTimerProc); + } else if(idle_status) { + idle_status = false; + if(!idle_timerid) + idle_timerid = SetTimer(0, 0, 200, IdleTimerProc); + } + + return 0; +} + +void SetSessionAwayMessage(int status, char *msg) { + if(status == ID_STATUS_ONLINE) { + if(AwayMessages.szOnline) free(AwayMessages.szOnline); + if(msg) { + AwayMessages.szOnline = _strdup(msg); + } else AwayMessages.szOnline = 0; + } else if(status == ID_STATUS_AWAY) { + if(AwayMessages.szAway) free(AwayMessages.szAway); + if(msg) { + AwayMessages.szAway = _strdup(msg); + } else AwayMessages.szAway = 0; + } else if(status == ID_STATUS_DND) { + if(AwayMessages.szDND) free(AwayMessages.szDND); + if(msg) { + AwayMessages.szDND = _strdup(msg); + } else AwayMessages.szDND = 0; + } else + return; // unsupported status + + SetSessionStatus(status); // update current away message +} + +void WINAPI NullAPC (DWORD dwData) { + // This function intentionally left blank +} + +void WakeThread(HANDLE hThread) { + QueueUserAPC(NullAPC, hThread, 0); +} + +unsigned long __stdcall KeepAliveThread(LPVOID param) { + CallService(MS_SYSTEM_THREAD_PUSH, 0, 0); + //PUShowMessage("KA Thread start", SM_NOTIFY); + + while(1) {// && !mwSession_isStopped(session)) { + SleepEx(30000, TRUE); + + EnterCriticalSection(&session_cs); + if(!session) { + LeaveCriticalSection(&session_cs); + break; + } + if(mwSession_isStarted(session) && !Miranda_Terminated() && session) { + mwSession_sendKeepalive(session); + //PUShowMessage("KA", SM_NOTIFY); + } + LeaveCriticalSection(&session_cs); + } + + //PUShowMessage("KA Thread end", SM_NOTIFY); + CallService(MS_SYSTEM_THREAD_POP, 0, 0); + return 0; +} + +bool continue_connect; + +int waitcallback(unsigned int *timeout) { + return continue_connect ? 1 : 0; +} + +unsigned long __stdcall SessionThread(LPVOID param) { + HANDLE hNetlibUser = (HANDLE)param; + + CallService(MS_SYSTEM_THREAD_PUSH, 0, 0); + + continue_connect = true; + + //setup + NETLIBOPENCONNECTION conn_data = {0}; + conn_data.cbSize = sizeof(NETLIBOPENCONNECTION); + conn_data.flags = NLOCF_V2; + conn_data.szHost = options.server_name; + conn_data.wPort = options.port; + conn_data.timeout = 20; + conn_data.waitcallback = waitcallback; + + QueueUserAPC(sttMainThreadStatusCallback, mainThread, ID_STATUS_CONNECTING); + + server_connection = (HANDLE)CallService(MS_NETLIB_OPENCONNECTION, (WPARAM)hNetlibUser, (LPARAM)&conn_data); + + if(!server_connection) { + QueueUserAPC(sttMainThreadStatusCallback, mainThread, ID_STATUS_OFFLINE); + if(continue_connect) { // real timeout - not user cancelled + //MessageBox(0, "No server connection!", "Error", MB_OK); + ShowError(TranslateT("No server connection!")); + } + CallService(MS_SYSTEM_THREAD_POP, 0, 0); + return 1; + } + + mwSessionHandler handler = {0}; + handler.clear = SessionClear; + handler.io_write = SessionWrite; + handler.io_close = SessionClose; + handler.on_stateChange = SessionStateChange; + handler.on_admin = SessionAdmin; + handler.on_announce = SessionAnnounce; + handler.on_setPrivacyInfo = SessionSetPrivacyInfo; + handler.on_setUserStatus = SessionSetUserStatus; + + EnterCriticalSection(&session_cs); + session = mwSession_new(&handler); + + InitMeanwhileServices(); + + mwSession_setProperty(session, mwSession_AUTH_USER_ID, options.id, NULL); + mwSession_setProperty(session, mwSession_AUTH_PASSWORD, options.pword, NULL); + mwSession_setProperty(session, mwSession_CLIENT_TYPE_ID, (void *)options.client_id, NULL); + + if(options.use_old_default_client_ver) { + mwSession_setProperty(session, mwSession_CLIENT_VER_MAJOR, GUINT_TO_POINTER(DBGetContactSettingWord(0, PROTO, "ClientVersionMajor", MW_PROTOCOL_VERSION_MAJOR)), 0); + mwSession_setProperty(session, mwSession_CLIENT_VER_MINOR, GUINT_TO_POINTER(DBGetContactSettingWord(0, PROTO, "ClientVersionMinor", MW_PROTOCOL_VERSION_MINOR)), 0); + } else { + mwSession_setProperty(session, mwSession_CLIENT_VER_MAJOR, GUINT_TO_POINTER(DBGetContactSettingWord(0, PROTO, "ClientVersionMajor", 0x001e)), 0); + mwSession_setProperty(session, mwSession_CLIENT_VER_MINOR, GUINT_TO_POINTER(DBGetContactSettingWord(0, PROTO, "ClientVersionMinor", 0x196f)), 0); + } + + mwSession_start(session); + LeaveCriticalSection(&session_cs); + + DWORD tid; + HANDLE hKAThread = CreateThread(0, 0, KeepAliveThread, 0, 0, &tid); + + //SetEvent(hSessionSyncEvent); + + unsigned char *recv_buffer = new unsigned char[1024 * 32]; + int bytes; + //while(session && server_connection && mwSession_getState(session) != mwSession_STOPPED) { + while(server_connection) {// && session) {// && !mwSession_isStopped(session)) { // break on error + bytes = Netlib_Recv(server_connection, (char *)recv_buffer, 1024 * 32, 0); + + if(bytes == 0) { + break; + } else if(bytes == SOCKET_ERROR) { + // this is normal - e.g. socket closed due to log off, during blocking read above + break; + } else { + EnterCriticalSection(&session_cs); + mwSession_recv(session, recv_buffer, bytes); + LeaveCriticalSection(&session_cs); + } + } + delete recv_buffer; + + EnterCriticalSection(&session_cs); + DeinitMeanwhileServices(); + + mwSession *old_session = session; + session = 0; // kills keepalive thread, if awake + + mwSession_free(old_session); + LeaveCriticalSection(&session_cs); + + // wake alive thread + + WakeThread(hKAThread); + WaitForSingleObject(hKAThread, INFINITE); + CloseHandle(hKAThread); + + QueueUserAPC(sttMainThreadStatusCallback, mainThread, ID_STATUS_OFFLINE); + SetAllOffline(); + + first_online = true; + + CallService(MS_SYSTEM_THREAD_POP, 0, 0); + return 0; +} + +WORD GetClientVersion() { + if(!session) return 0; + + WORD retval = 0; + + retval = (int)mwSession_getProperty(session, mwSession_CLIENT_VER_MAJOR) << 8; + retval |= (int)mwSession_getProperty(session, mwSession_CLIENT_VER_MINOR); + + return retval; +} + +WORD GetServerVersion() { + if(!session) return 0; + + WORD retval = 0; + + retval = (int)mwSession_getProperty(session, mwSession_SERVER_VER_MAJOR) << 8; + retval |= (int)mwSession_getProperty(session, mwSession_SERVER_VER_MINOR); + + return retval; +} + +int LogIn(int ls, HANDLE hNetlibUser) { + EnterCriticalSection(&session_cs); + if(session) { + LeaveCriticalSection(&session_cs); + return 0; + } + LeaveCriticalSection(&session_cs); + + login_status = ls; + + //hSessionSyncEvent = CreateEvent(NULL, true, false, NULL); + sessionThread = CreateThread(0, 0, SessionThread, (void *)hNetlibUser, 0, &session_thread_id); + + + // we can't wait for the thread - critical section (csHooks) from core is locked during call to this function, + // and hooks are created in the individual init sections... + + CloseHandle(sessionThread); + //WaitForSingleObject(hSessionSyncEvent, INFINITE); + //CloseHandle(hSessionSyncEvent); + //hSessionSyncEvent = 0; + + return 0; +} + +int LogOut() { + continue_connect = false; + + EnterCriticalSection(&session_cs); + if(session && server_connection && current_status != ID_STATUS_OFFLINE && !mwSession_isStopped(session) && !mwSession_isStopping(session)) + mwSession_stop(session, 0); + LeaveCriticalSection(&session_cs); + + return 0; +} + +void InitAwayMsg() { + AwayMessages.szOnline = 0; + AwayMessages.szAway = 0; + AwayMessages.szDND = 0; +} + +void DeinitAwayMsg() { + if(AwayMessages.szOnline) free(AwayMessages.szOnline); + if(AwayMessages.szAway) free(AwayMessages.szAway); + if(AwayMessages.szDND) free(AwayMessages.szDND); +} + +void SendAnnouncement(AnnouncementData *ad) { + char *utfs = t2u(ad->msg); + if(session && ad && ad->recipients) mwSession_sendAnnounce(session, false , utfs, ad->recipients); + free(utfs); +} + +int SessionAnnounce(WPARAM wParam, LPARAM lParam) { + CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_SESSIONANNOUNCE), GetDesktopWindow(), SessionAnnounceDialogProc, (LPARAM)SendAnnouncement); + return 0; +} + +void InitSessionMenu() { + CreateProtoServiceFunction(PROTO, MS_SAMETIME_MENUANNOUNCESESSION, SessionAnnounce); + + hIconAnnounce = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ANNOUNCE)); + hIconProto = LoadIcon(hInst, MAKEINTRESOURCE(IDI_ICON_PROTO)); + + char service_function[128]; + strcpy(service_function, PROTO); + char *d = service_function + strlen(service_function); + + CLISTMENUITEM mi = {0}; + mi.cbSize = sizeof(mi); + mi.popupPosition = 500085001; + mi.pszPopupName = PROTO; + mi.position = 2000060000; + + mi.pszName = Translate("Send Announcement..."); + strcpy(d, MS_SAMETIME_MENUANNOUNCESESSION); + mi.pszService = service_function; + + mi.hIcon = hIconProto; + //mi.hIcon = hIconAnnounce; // first submenu icon is used as icon for main menu popup... + + hSessionAnnounceMenuItem = (HANDLE)CallService(MS_CLIST_ADDMAINMENUITEM, 0, (LPARAM)&mi); +} + +void DeinitSessionMenu() { + DestroyIcon(hIconProto); + DestroyIcon(hIconAnnounce); +} + +void InitCritSection() { + InitializeCriticalSection(&session_cs); + +} +void DeinitCritSection() { + DeleteCriticalSection(&session_cs); +} diff --git a/plugins/!NotAdopted/sametime/session.h b/plugins/!NotAdopted/sametime/session.h new file mode 100644 index 0000000000..afe1d03c2c --- /dev/null +++ b/plugins/!NotAdopted/sametime/session.h @@ -0,0 +1,32 @@ +#ifndef _SESSION_INC +#define _SESSION_INC + +#include "common.h" +#include "options.h" +#include "userlist.h" +#include "messaging.h" +#include "files.h" +#include "conference.h" +#include "utils.h" +#include "session_announce_win.h" + +WORD GetClientVersion(); +WORD GetServerVersion(); + +int SetIdle(bool idle); +int LogIn(int status, HANDLE hNetlibUser); +int SetSessionStatus(int status); +void SetSessionAwayMessage(int status, char *msg); +int LogOut(); + +void UpdateSelfStatus(); + +void InitAwayMsg(); +void DeinitAwayMsg(); + +void InitSessionMenu(); +void DeinitSessionMenu(); + +void InitCritSection(); +void DeinitCritSection(); +#endif \ No newline at end of file diff --git a/plugins/!NotAdopted/sametime/session_announce_win.cpp b/plugins/!NotAdopted/sametime/session_announce_win.cpp new file mode 100644 index 0000000000..144922881c --- /dev/null +++ b/plugins/!NotAdopted/sametime/session_announce_win.cpp @@ -0,0 +1,136 @@ +#include "session_announce_win.h" + + +INT_PTR CALLBACK SessionAnnounceDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { + switch(uMsg) { + case WM_INITDIALOG: + TranslateDialogDefault(hwndDlg); + SetWindowLong(hwndDlg, GWL_USERDATA, lParam); // save lParam + SendMessage(GetDlgItem(hwndDlg, IDC_LST_ANTO),LVM_SETEXTENDEDLISTVIEWSTYLE, 0,LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES); + + { + LVCOLUMN lvc; + // Initialize the LVCOLUMN structure. + // The mask specifies that the format, width, text, and + // subitem members of the structure are valid. + lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; + lvc.fmt = LVCFMT_LEFT; + + lvc.iSubItem = 0; + lvc.pszText = TranslateT("Recipients"); + lvc.cx = 300; // width of column in pixels + ListView_InsertColumn(GetDlgItem(hwndDlg, IDC_LST_ANTO), 0, &lvc); + } + + //enumerate plugins, fill in list + { + ListView_DeleteAllItems(GetDlgItem(hwndDlg, IDC_LST_ANTO)); + + LVITEM lvI; + + // Some code to create the list-view control. + // Initialize LVITEM members that are common to all + // items. + lvI.mask = LVIF_TEXT | LVIF_PARAM;// | LVIF_NORECOMPUTE;// | LVIF_IMAGE; + + char *proto; + HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + while ( hContact != NULL ) + { + proto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM )hContact,0 ); + if ( proto && !strcmp( PROTO, proto)) { + if(DBGetContactSettingByte(hContact, PROTO, "ChatRoom", 0) == 0 + && DBGetContactSettingWord(hContact, PROTO, "Status", ID_STATUS_OFFLINE) != ID_STATUS_OFFLINE) + { + lvI.iSubItem = 0; + lvI.lParam = (LPARAM)hContact; + lvI.pszText = (TCHAR *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, GCDNF_TCHAR); + lvI.iItem = ListView_InsertItem(GetDlgItem(hwndDlg, IDC_LST_ANTO), &lvI); + + } + } + + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } + } + + return 0; + + case WM_CLOSE: + DestroyWindow(hwndDlg); + break; + + case WM_COMMAND: + if ( HIWORD( wParam ) == BN_CLICKED ) { + switch( LOWORD( wParam )) { + case IDC_BUT_SELALL: { + int size = ListView_GetItemCount(GetDlgItem(hwndDlg, IDC_LST_ANTO)); + for(int i = 0; i < size; i++) ListView_SetCheckState(GetDlgItem(hwndDlg, IDC_LST_ANTO), i, true); + } + return 0; + case IDC_BUT_SELINV: { + int size = ListView_GetItemCount(GetDlgItem(hwndDlg, IDC_LST_ANTO)); + for(int i = 0; i < size; i++) + ListView_SetCheckState(GetDlgItem(hwndDlg, IDC_LST_ANTO), i, + !ListView_GetCheckState(GetDlgItem(hwndDlg, IDC_LST_ANTO), i) + ); + } + return 0; + case IDOK: { + // build AnnouncementData + AnnouncementData ad; + DBVARIANT dbv; + LVITEM lvI = {0}; + + char id[1024]; + strcpy(id, "@U"); // documentation says prepend '@U' to usernames and '@G' to notes group names - but + char *p = id + 2; // it's wrong - it works for a list of user id's with no prefix - so we'll do both + + // build recipient list + ad.recipients = 0; + + int size = ListView_GetItemCount(GetDlgItem(hwndDlg, IDC_LST_ANTO)); + int send_count = 0; + for(int i = 0; i < size; i++) { + if(ListView_GetCheckState(GetDlgItem(hwndDlg, IDC_LST_ANTO), i)) { + lvI.iItem = i; + lvI.iSubItem = 0; + lvI.mask = LVIF_PARAM; + ListView_GetItem(GetDlgItem(hwndDlg, IDC_LST_ANTO), &lvI); + + if(!DBGetContactSettingUtf((HANDLE)lvI.lParam, PROTO, "stid", &dbv)) { + ad.recipients = g_list_prepend(ad.recipients, _strdup(dbv.pszVal)); + strcpy(p, dbv.pszVal); + ad.recipients = g_list_prepend(ad.recipients, _strdup(id)); + send_count++; + DBFreeVariant(&dbv); + } + } + } + + // call function pointed to by lParam + if(send_count > 0) { + GetWindowText(GetDlgItem(hwndDlg, IDC_ED_ANMSG), ad.msg, MAX_MESSAGE_SIZE); + SendAnnounceFunc f = (SendAnnounceFunc)GetWindowLong(hwndDlg, GWL_USERDATA); + f(&ad); + } + + // clean up recipient list + for(GList *rit = ad.recipients; rit; rit = rit->next) { + free(rit->data); + } + if(ad.recipients) g_list_free(ad.recipients); + + DestroyWindow(hwndDlg); + } + return 0; + case IDCANCEL: + DestroyWindow(hwndDlg); + return 0; + } + } + break; + } + + return 0; +} \ No newline at end of file diff --git a/plugins/!NotAdopted/sametime/session_announce_win.h b/plugins/!NotAdopted/sametime/session_announce_win.h new file mode 100644 index 0000000000..e311af9b9f --- /dev/null +++ b/plugins/!NotAdopted/sametime/session_announce_win.h @@ -0,0 +1,15 @@ +#ifndef _SESSION_ANNOUNCE_WIN_INC +#define _SESSION_ANNOUNCE_WIN_INC + +#include "common.h" + +typedef struct { + TCHAR msg[MAX_MESSAGE_SIZE]; + GList *recipients; +} AnnouncementData; + +typedef void (*SendAnnounceFunc)(AnnouncementData *ad); + +INT_PTR CALLBACK SessionAnnounceDialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam); + +#endif diff --git a/plugins/!NotAdopted/sametime/userlist.cpp b/plugins/!NotAdopted/sametime/userlist.cpp new file mode 100644 index 0000000000..416e521e5f --- /dev/null +++ b/plugins/!NotAdopted/sametime/userlist.cpp @@ -0,0 +1,909 @@ +#include "userlist.h" +#include "session.h" + +mwServiceStorage *service_storage = 0; +mwServiceAware *service_aware = 0; +mwServiceResolve *service_resolve = 0; + +mwAwareList *aware_list = 0; + +HANDLE hContactDeletedEvent = 0; + +HANDLE FindContactByUserId(const char *id) { + char *proto; + DBVARIANT dbv; + HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + while ( hContact != NULL ) + { + proto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM )hContact,0 ); + if ( proto && !strcmp( PROTO, proto)) { + if(!DBGetContactSettingUtf(hContact, PROTO, "stid", &dbv)) { + if(dbv.pszVal && strcmp(id, dbv.pszVal) == 0) { + DBFreeVariant(&dbv); + return hContact; + } + DBFreeVariant(&dbv); + } + } + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } + + return 0; +} + +bool GetAwareIdFromContact(HANDLE hContact, mwAwareIdBlock *id_block) { + char *proto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM )hContact,0 ); + DBVARIANT dbv; + if ( proto && !strcmp( PROTO, proto)) { + if(!DBGetContactSettingUtf(hContact, PROTO, "stid", &dbv)) { + if(dbv.pszVal) { + id_block->type = mwAware_USER; + id_block->user = _strdup(dbv.pszVal); + id_block->community = 0; + DBFreeVariant(&dbv); + return true; + } + DBFreeVariant(&dbv); + } + } + return false; +} + +void SetContactGroup(HANDLE hContact, const char *name) { + if(ServiceExists(MS_CLIST_GROUPRENAME"W")) + DBWriteContactSettingStringUtf(hContact, "CList", "Group", name); + else { + wchar_t buff[512]; + char mb[512]; + + MultiByteToWideChar(CP_UTF8, 0, name, -1, buff, 512); + WideCharToMultiByte(CallService(MS_LANGPACK_GETCODEPAGE, 0, 0), 0, buff, -1, mb, 512, 0, 0); + + DBWriteContactSettingString(hContact, "CList", "Group", mb); + } + +} + +void AddGroup(const char *name, bool expanded) { + if(name && strcmp(name, "MetaContacts Hidden Group") == 0) + return; + + if(name && strcmp(name, Translate("None")) == 0) + return; + + HANDLE hGroup = (HANDLE)GroupNameExists(name, -1); + if(!hGroup) { + wchar_t namew[512]; + MultiByteToWideChar(CP_UTF8, 0, name, -1, namew, 512); + + hGroup = (HANDLE)CallService(MS_CLIST_GROUPCREATE, 0, 0); + + if(ServiceExists(MS_CLIST_GROUPRENAME"W")) { + CallService(MS_CLIST_GROUPRENAME"W", (WPARAM)hGroup, (LPARAM)namew); + } else { + char mb[512]; + WideCharToMultiByte(CallService(MS_LANGPACK_GETCODEPAGE, 0, 0), 0, namew, -1, mb, 512, 0, 0); + + CallService(MS_CLIST_GROUPRENAME, (WPARAM)hGroup, (LPARAM)mb); + } + + } + + // doesn't call clui! arg! + //CallService(MS_CLIST_GROUPSETEXPANDED, (WPARAM)hGroup, (LPARAM)(expanded ? 1 : 0)); + + HWND hwndClist = (HWND)CallService(MS_CLUI_GETHWND, 0, 0); + HWND hwndClc = FindWindowEx(hwndClist, 0, CLISTCONTROL_CLASS, 0); + + HANDLE hItem = (HANDLE)SendMessage(hwndClc, CLM_FINDGROUP, (WPARAM)hGroup, 0); + SendMessage(hwndClc, CLM_EXPAND, (WPARAM)hItem, (LPARAM) (expanded ? 1 : 0)); +} + + +HANDLE AddContact(mwSametimeUser *user, bool temporary) { + const char *id = mwSametimeUser_getUser(user); + const char *name = mwSametimeUser_getShortName(user); + const char *nick = mwSametimeUser_getAlias(user); + //const char *nick = mwSametimeUser_getShortName(user); + mwSametimeUserType type = mwSametimeUser_getType(user); + + HANDLE hContact = FindContactByUserId(id); + bool new_contact = false; + if(!hContact) { + hContact = (HANDLE)CallService(MS_DB_CONTACT_ADD, 0, 0); + new_contact = true; + } else if(!temporary) { + DBDeleteContactSetting(hContact, "CList", "NotOnList"); + DBDeleteContactSetting(hContact, "CList", "Hidden"); + } + + if(hContact) { + // add to miranda + if(new_contact) DBWriteContactSettingStringUtf(hContact, PROTO, "stid", id); + + if(name && strlen(name)) + DBWriteContactSettingStringUtf(hContact, PROTO, "Name", name); + + if(nick && strlen(nick)) { + DBWriteContactSettingStringUtf(hContact, PROTO, "Nick", nick); + } else if(name && strlen(name)) { + DBWriteContactSettingStringUtf(hContact, PROTO, "Nick", name); + } else { + DBWriteContactSettingStringUtf(hContact, PROTO, "Nick", id); + } + + DBWriteContactSettingByte(hContact, PROTO, "type", (BYTE)type); + + if(new_contact) { + CallService(MS_PROTO_ADDTOCONTACT, (WPARAM)hContact, (LPARAM)PROTO); + + //add to our awareness list + mwAwareIdBlock id_block; + if(GetAwareIdFromContact(hContact, &id_block)) { + + GList *gl; + + gl = g_list_prepend(NULL, &id_block); + mwAwareList_addAware(aware_list, gl); + + g_list_free(gl); + + free(id_block.user); + } + } + + if(temporary) { + DBWriteContactSettingByte(hContact, "CList", "NotOnList", 1); + DBWriteContactSettingByte(hContact, "CList", "Hidden", 1); + } else { + DBDeleteContactSetting(hContact, "CList", "NotOnList"); + DBDeleteContactSetting(hContact, "CList", "Hidden"); + } + } + + return hContact; +} + +void ImportContactsFromList(mwSametimeList *user_list, bool temporary) { + // add contacts + mwSametimeGroup *stgroup; + mwSametimeUser *stuser; + + GList *gl, *gtl, *ul, *utl; + + const char *group_name; + const char *group_alias; + mwSametimeGroupType group_type; + bool group_open; + + gl = gtl = mwSametimeList_getGroups(user_list); + for(; gl; gl = gl->next) { + char buff[256]; + + stgroup = (mwSametimeGroup *) gl->data; + + group_name = mwSametimeGroup_getName(stgroup); + group_alias = mwSametimeGroup_getAlias(stgroup); + if(!group_alias) group_alias = group_name; + + group_type = mwSametimeGroup_getType(stgroup); + group_open = (mwSametimeGroup_isOpen(stgroup) != 0); + + mir_snprintf(buff, 256, "GN_%s", group_alias); + DBWriteContactSettingStringUtf(0, PROTO_GROUPS, buff, group_name); + mir_snprintf(buff, 256, "GT_%s", group_alias); + DBWriteContactSettingByte(0, PROTO_GROUPS, buff, (BYTE)group_type); + mir_snprintf(buff, 256, "GO_%s", group_alias); + DBWriteContactSettingByte(0, PROTO_GROUPS, buff, (BYTE)(group_open ? 1 : 0)); + + // inverse mapping + mir_snprintf(buff, 256, "GA_%s", group_name); + DBWriteContactSettingStringUtf(0, PROTO_GROUPS, buff, group_alias); + + AddGroup(group_alias, group_open); + + if(group_type == mwSametimeGroup_DYNAMIC) { + + mwAwareIdBlock id_block; + id_block.type = mwAware_GROUP; + id_block.user = (char *)group_name; + id_block.community = 0; + + GList *gl; + + gl = g_list_prepend(NULL, &id_block); + mwAwareList_addAware(aware_list, gl); + + g_list_free(gl); + } + + ul = utl = mwSametimeGroup_getUsers(stgroup); + for(; ul; ul = ul->next) { + + stuser = (mwSametimeUser *) ul->data; + HANDLE hContact = AddContact(stuser, temporary); + + if(hContact && group_alias && strcmp(group_alias, Translate("None")) != 0 && strcmp(group_alias, "MetaContacts Hidden Group") != 0) { + SetContactGroup(hContact, group_alias); + // mark contact as belonging to dynamic group + } + } + g_list_free(utl); + } + g_list_free(gtl); + + +} + +void ExportContactsToList(mwSametimeList *user_list) { + mwSametimeGroup *stgroup = 0; + char *group_name; + char *group_alias; + mwSametimeGroupType group_type; + bool group_open; + + mwSametimeUser *stuser; + char *user_alias; + char *user_shortName; + mwSametimeUserType user_type; + + char *proto; + DBVARIANT dbv, dbv2; + char buff[256]; + HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + mwAwareIdBlock id_block; + + mwIdBlock uid; + + GList *gl = 0; + while ( hContact != NULL ) + { + proto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM )hContact,0 ); + if ( proto && !strcmp( PROTO, proto)) { + if(!DBGetContactSettingUtf(hContact, PROTO, "stid", &dbv)) { + if(dbv.pszVal) { + if(GetAwareIdFromContact(hContact, &id_block)) { + if(!DBGetContactSettingUtf(hContact, "CList", "Group", &dbv2)) { + group_alias = _strdup(dbv2.pszVal); + DBFreeVariant(&dbv2); + } else + group_alias = _strdup(Translate("None")); + + if(group_alias) { + mir_snprintf(buff, 256, "GT_%s", group_alias); + group_type = (mwSametimeGroupType)DBGetContactSettingByte(0, PROTO_GROUPS, buff, (BYTE)mwSametimeGroup_NORMAL); + // apparently we don't want to upload contacts in dynamic groups - see gaim sametime plugin comments + if(group_type == mwSametimeGroup_DYNAMIC) { + DBFreeVariant(&dbv); + free(id_block.user); + free(group_alias); + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + continue; + } + + + mir_snprintf(buff, 256, "GN_%s", group_alias); + if(!DBGetContactSettingUtf(0, PROTO_GROUPS, buff, &dbv2)) { + group_name = _strdup(dbv2.pszVal); + DBFreeVariant(&dbv2); + } else + group_name = _strdup(group_alias); + + //group_open = (DBGetContactSettingByte(0, PROTO_GROUPS, buff, 0) == 1); + HANDLE hGroup = (HANDLE)GroupNameExists(group_alias, -1); + if(hGroup) { + int expanded; + CallService(MS_CLIST_GROUPGETNAME, (WPARAM)hGroup, (LPARAM)&expanded); + group_open = (expanded != 0); + } else { + mir_snprintf(buff, 256, "GO_%s", group_alias); + group_open = (DBGetContactSettingByte(0, PROTO_GROUPS, buff, 0) == 1); + } + + + stgroup = 0; + stgroup = mwSametimeList_findGroup(user_list, group_name); + if(!stgroup) { + if(group_name) stgroup = mwSametimeGroup_new(user_list, group_type, group_name); + mwSametimeGroup_setAlias(stgroup, group_alias); + mwSametimeGroup_setOpen(stgroup, group_open); + } + + free(group_name); + free(group_alias); + + if(!DBGetContactSettingUtf(hContact, PROTO, "Name", &dbv2)) { + user_shortName = _strdup(dbv2.pszVal); + DBFreeVariant(&dbv2); + } else + user_shortName = 0; + + if(!DBGetContactSettingUtf(hContact, "CList", "MyHandle", &dbv2)) { + user_alias = _strdup(dbv2.pszVal); + DBFreeVariant(&dbv2); + } else + user_alias = 0; + + user_type = (mwSametimeUserType)DBGetContactSettingByte(hContact, PROTO, "type", (BYTE)mwSametimeUser_NORMAL); + + uid.user = id_block.user; + uid.community = id_block.community; + + stuser = mwSametimeUser_new(stgroup, user_type, &uid); + if(user_shortName) { + mwSametimeUser_setShortName(stuser, user_shortName); + free(user_shortName); + } + if(user_alias) { + mwSametimeUser_setAlias(stuser, user_alias); + free(user_alias); + } + } + + free(id_block.user); + } + } + DBFreeVariant(&dbv); + } + } + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } +} + +void ImportContactsFromFile(TCHAR *filename) { +#ifdef _UNICODE +#else + std::ifstream in(filename); + std::string text; + std::string line; + if(in.is_open()) { + while(!in.eof()) { + std::getline(in, line); + text += line; + text += "\r\n"; + } + in.close(); + + mwSametimeList *new_list = mwSametimeList_load(text.c_str()); + + ImportContactsFromList(new_list, false); + + mwSametimeList_free(new_list); + + } +#endif + +} + +void ExportContactsToServer() { + mwSametimeList *user_list; + mwStorageUnit *unit; + + mwPutBuffer *buff; + mwOpaque *op; + + if(MW_SERVICE_IS_DEAD(service_storage)) { + //MessageBox(0, "Failed to upload contacts - Storage service unavailable.", "Error", MB_OK); + ShowError(TranslateT("Failed to upload contacts - Storage service unavailable.")); + return; + } + + user_list = mwSametimeList_new(); + ExportContactsToList(user_list); + + buff = mwPutBuffer_new(); + mwSametimeList_put(buff, user_list); + mwSametimeList_free(user_list); + + /* put the buffer contents into a storage unit */ + unit = mwStorageUnit_new(mwStore_AWARE_LIST); + op = mwStorageUnit_asOpaque(unit); + mwPutBuffer_finalize(op, buff); + + /* save the storage unit to the service */ + mwServiceStorage_save(service_storage, unit, NULL, NULL, NULL); +} + +void load_users_callback(mwServiceStorage *srvc, guint32 result, mwStorageUnit *item, gpointer data) { + if(mwStorageUnit_getKey(item) == mwStore_AWARE_LIST) { + mwGetBuffer *buff = mwGetBuffer_wrap(mwStorageUnit_asOpaque(item)); + + if(mwGetBuffer_remaining(buff)) { + mwSametimeList *user_list = mwSametimeList_new(); + + mwSametimeList_get(buff, user_list); + ImportContactsFromList(user_list, false); + + mwSametimeList_free(user_list); + } + } +} + + +void UserListAddStored() { + mwStorageUnit *unit; + + unit = mwStorageUnit_new(mwStore_AWARE_LIST); + mwServiceStorage_load(service_storage, unit, load_users_callback, 0, 0); +} + +int ContactDeleted(WPARAM wParam, LPARAM lParam) { + mwAwareIdBlock id_block; + HANDLE hContact = (HANDLE)wParam; + + if(DBGetContactSettingByte(hContact, PROTO, "ChatRoom", 0)) + return 0; + + if(GetAwareIdFromContact(hContact, &id_block)) { + GList *gl; + + gl = g_list_prepend(NULL, &id_block); + mwAwareList_removeAware(aware_list, gl); + + g_list_free(gl); + + free(id_block.user); + } + + return 0; +} + +void mwServiceAware_on_attrib(mwServiceAware *srvc, mwAwareAttribute *attrib) { + +} + + +void mwServiceAware_clear(mwServiceAware *srvc) { +} + + +mwAwareHandler mwAware_handler = { + mwServiceAware_on_attrib, + mwServiceAware_clear +}; + +void mwResolve_handler_dyngroup(mwServiceResolve *srvc, guint32 id, guint32 code, GList *results, gpointer data) { + mwResolveResult *result; + mwResolveMatch *match; + + mwSametimeGroup *stgroup = (mwSametimeGroup *)data; + + g_return_if_fail(results != NULL); + + if(results) { + result = (mwResolveResult *)results->data; + if(result && result->matches) { + + match = (mwResolveMatch *)result->matches->data; + if(match) { + mwIdBlock uid; + uid.user = match->id; + uid.community = 0; + mwSametimeUser *stuser = mwSametimeUser_new(stgroup, mwSametimeUser_NORMAL, &uid); + mwSametimeUser_setShortName(stuser, match->name); + + HANDLE hContact = AddContact(stuser, false); + + const char *group_name = mwSametimeGroup_getName(stgroup); + const char *group_alias = mwSametimeGroup_getAlias(stgroup); + if(!group_alias) group_alias = group_name; + if(hContact && group_alias && strcmp(group_alias, Translate("None")) && strcmp(group_alias, "MetaContacts Hidden Group")) { + SetContactGroup(hContact, group_alias); + } + } + } + } + + + if(stgroup) + mwSametimeList_free(mwSametimeGroup_getList(stgroup)); +} + +void mwAwareList_on_aware(mwAwareList *list, mwAwareSnapshot *aware) { + HANDLE hContact = FindContactByUserId(aware->id.user); + char *group = 0; + DBVARIANT dbv; + + // update self - necessary for some servers + if(aware->online && !DBGetContactSettingUtf(0, PROTO, "stid", &dbv) && strcmp(aware->id.user, dbv.pszVal) == 0) { + int new_status = ID_STATUS_OFFLINE; + + switch(aware->status.status) { + case mwStatus_ACTIVE: + new_status = ID_STATUS_ONLINE; + break; + case mwStatus_AWAY: + new_status = ID_STATUS_AWAY; + break; + case mwStatus_IDLE: + new_status = ID_STATUS_IDLE; + break; + case mwStatus_BUSY: + new_status = ID_STATUS_DND; + break; + } + if(new_status != ID_STATUS_IDLE) //SetSessionStatus(new_status); + UpdateSelfStatus(); + + DBFreeVariant(&dbv); + } + + if(hContact && !DBGetContactSettingUtf(hContact, "CList", "Group", &dbv)) { + group = _strdup(dbv.pszVal); + DBFreeVariant(&dbv); + } + + if(aware->group && (!group || strcmp(aware->group, group) || !hContact)) { + // dynamic group member we're not already aware of + // resolve server alias to user id via resolver + mwSametimeList *user_list = mwSametimeList_new(); + mwSametimeGroup *stgroup = mwSametimeGroup_new(user_list, mwSametimeGroup_DYNAMIC, aware->group); + char buff[256]; + mir_snprintf(buff, 256, "GA_%s", aware->group); + if(!DBGetContactSettingUtf(0, PROTO_GROUPS, buff, &dbv)) { + mwSametimeGroup_setAlias(stgroup, dbv.pszVal); + DBFreeVariant(&dbv); + } + + GList *query = g_list_prepend(0, (void *)aware->id.user); + + mwServiceResolve_resolve(service_resolve, query, mwResolveFlag_USERS, mwResolve_handler_dyngroup, (gpointer)stgroup, 0); + + g_list_free(query); + } else if(hContact) { + + if(aware->online) { + int new_status = ID_STATUS_OFFLINE; + + switch(aware->status.status) { + case mwStatus_ACTIVE: + new_status = ID_STATUS_ONLINE; + DBWriteContactSettingDword(hContact, PROTO, "IdleTS", 0); + DBWriteContactSettingWord(hContact, PROTO, "Status", new_status); + break; + case mwStatus_AWAY: + new_status = ID_STATUS_AWAY; + DBWriteContactSettingDword(hContact, PROTO, "IdleTS", 0); + DBWriteContactSettingWord(hContact, PROTO, "Status", new_status); + break; + case mwStatus_IDLE: + if(options.idle_as_away) { + new_status = ID_STATUS_AWAY; + DBWriteContactSettingWord(hContact, PROTO, "Status", new_status); + } + DBWriteContactSettingDword(hContact, PROTO, "IdleTS", (DWORD)time(0)); + break; + case mwStatus_BUSY: + new_status = ID_STATUS_DND; + DBWriteContactSettingWord(hContact, PROTO, "Status", new_status); + DBWriteContactSettingDword(hContact, PROTO, "IdleTS", 0); + break; + } + } else + DBWriteContactSettingWord(hContact, PROTO, "Status", ID_STATUS_OFFLINE); + + if(service_aware) { + const char *desc = mwServiceAware_getText(service_aware, &aware->id); + if(desc) + //DBWriteContactSettingStringUtf(hContact, PROTO, "StatusMsg", desc); + DBWriteContactSettingStringUtf(hContact, "CList", "StatusMsg", desc); + else + //DBWriteContactSettingStringUtf(hContact, PROTO, "StatusMsg", ""); + //DBDeleteContactSetting(hContact, PROTO, "StatusMsg"); + DBDeleteContactSetting(hContact, "CList", "StatusMsg"); + } + } + + if(group) free(group); +} + + +void mwAwareList_on_attrib(mwAwareList *list, mwAwareIdBlock *id, mwAwareAttribute *attrib) { +} + + +void mwAwareList_clear(mwAwareList *list) { +} + + +mwAwareListHandler mwAwareList_handler = { + mwAwareList_on_aware, + mwAwareList_on_attrib, + mwAwareList_clear +}; + +void UserListCreate() { + mwServiceAware_unsetAttribute(service_aware, mwAttribute_SPEAKERS); + mwServiceAware_unsetAttribute(service_aware, mwAttribute_MICROPHONE); + mwServiceAware_unsetAttribute(service_aware, mwAttribute_VIDEO_CAMERA); + + mwServiceAware_setAttributeBoolean(service_aware, mwAttribute_AV_PREFS_SET, TRUE); + + mwServiceAware_setAttributeBoolean(service_aware, mwAttribute_FILE_TRANSFER, TRUE); + + + aware_list = mwAwareList_new(service_aware, &mwAwareList_handler); + + // add all contacts + + char *proto; + DBVARIANT dbv; + HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + mwAwareIdBlock id_block; + GList *gl = 0; + + while ( hContact != NULL ) + { + proto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM )hContact,0 ); + if (DBGetContactSettingByte(hContact, PROTO, "ChatRoom", 0) == 0 && proto && !strcmp( PROTO, proto)) { + if(!DBGetContactSettingUtf(hContact, PROTO, "stid", &dbv)) { + if(dbv.pszVal) { + if(GetAwareIdFromContact(hContact, &id_block)) { + // add user to aware list + gl = g_list_prepend(0, &id_block); + + mwAwareList_addAware(aware_list, gl); + + free(id_block.user); + g_list_free(gl); + } + } + DBFreeVariant(&dbv); + } + } + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } + + + // add self - might be necessary for some servers + if(!DBGetContactSettingUtf(0, PROTO, "stid", &dbv)) { + id_block.type = mwAware_USER; + id_block.user = dbv.pszVal; + id_block.community = 0; + + gl = g_list_prepend(0, &id_block); + mwAwareList_addAware(aware_list, gl); + g_list_free(gl); + + DBFreeVariant(&dbv); + } +} + +void UserListDestroy() { + mwAwareList_free(aware_list); + aware_list = 0; +} + +void UserRecvAwayMessage(HANDLE hContact) { + + DBVARIANT dbv; + char buff[512]; + buff[0] = 0; + + if(!DBGetContactSettingUtf(hContact, "CList", "StatusMsg", &dbv) && strlen(dbv.pszVal)) { + strncpy(buff, dbv.pszVal, 512); + buff[511] = 0; + DBFreeVariant(&dbv); + } + + CCSDATA ccs = {0}; + PROTORECVEVENT pre = {0}; + + ccs.hContact = hContact; + ccs.szProtoService = PSR_AWAYMSG; + ccs.wParam = 0; + ccs.lParam = (LPARAM)⪯ + + pre.timestamp = (DWORD)time(0); + if(strlen(buff)) + pre.szMessage = buff; + else + pre.szMessage = 0; + + CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); +} + +void mwResolve_handler(mwServiceResolve *srvc, guint32 id, guint32 code, GList *results, gpointer data) { + bool advanced = (data != 0); + + MYCUSTOMSEARCHRESULTS mcsr = {0}; + mcsr.nSize = sizeof(MYCUSTOMSEARCHRESULTS); + //MYPROTOSEARCHRESULT mpsr = {0}; + //mpsr.cbSize = sizeof(MYPROTOSEARCHRESULT); + mcsr.psr.nick = mcsr.psr.name; + + mcsr.nFieldCount = 4; + TCHAR fields[4][512]; + TCHAR *fields_addr[4]; + mcsr.pszFields = fields_addr; + mcsr.pszFields[0] = fields[0]; + mcsr.pszFields[1] = fields[1]; + mcsr.pszFields[2] = fields[2]; + mcsr.pszFields[3] = fields[3]; + + if(advanced) { + // send column names + mcsr.psr.cbSize = 0; + _tcsncpy(mcsr.pszFields[0], TranslateT("Id"), 512); + _tcsncpy(mcsr.pszFields[1], TranslateT("Name"), 512); + _tcsncpy(mcsr.pszFields[2], TranslateT("Description"), 512); + _tcsncpy(mcsr.pszFields[3], TranslateT("Group?"), 512); + ProtoBroadcastAck(PROTO,NULL,ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, (HANDLE)id, (LPARAM)&mcsr); + } + + mcsr.psr.cbSize = sizeof(MYPROTOSEARCHRESULT); + + if(code == mwResolveCode_SUCCESS) { + GList *ri = results, *mri; + for(;ri;ri = ri->next) { + mri = ((mwResolveResult *)ri->data)->matches; + for(;mri;mri = mri->next) { + strncpy(mcsr.psr.stid, ((mwResolveMatch *)mri->data)->id, 256); + mcsr.psr.stid[255] = 0; + MultiByteToWideChar(CP_UTF8, 0, mcsr.psr.stid, -1, mcsr.pszFields[0], 512); + + strncpy(mcsr.psr.name, ((mwResolveMatch *)mri->data)->name, 256); + mcsr.psr.name[255] = 0; + MultiByteToWideChar(CP_UTF8, 0, mcsr.psr.name, -1, mcsr.pszFields[1], 512); + + if(((mwResolveMatch *)mri->data)->desc) + MultiByteToWideChar(CP_UTF8, 0, ((mwResolveMatch *)mri->data)->desc, -1, mcsr.pszFields[2], 512); + else + mcsr.pszFields[2][0] = 0; + + mcsr.psr.group = (((mwResolveMatch *)mri->data)->type == mwResolveMatch_GROUP); + //MultiByteToWideChar(CP_UTF8, 0, mcsr.psr.name, -1, mcsr.pszFields[1], 512); + _tcsncpy(mcsr.pszFields[3], mcsr.psr.group ? TranslateT("True") : TranslateT("False"), 512); + + if(advanced) + ProtoBroadcastAck(PROTO,NULL,ACKTYPE_SEARCH, ACKRESULT_SEARCHRESULT, (HANDLE)id, (LPARAM)&mcsr); + else + ProtoBroadcastAck(PROTO,NULL,ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)id, (LPARAM)&mcsr.psr); + } + } + ProtoBroadcastAck(PROTO,NULL,ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)id, 0); + } +} + +void mwResolve_handler_details(mwServiceResolve *srvc, guint32 id, guint32 code, GList *results, gpointer data) { + MYPROTOSEARCHRESULT mpsr = {0}; + mpsr.cbSize = sizeof(mpsr); + mpsr.nick = mpsr.name; + + if(code == mwResolveCode_SUCCESS) { + GList *ri = results, *mri; + for(;ri;ri = ri->next) { + mri = ((mwResolveResult *)ri->data)->matches; + for(;mri;mri = mri->next) { + + HANDLE hContact = FindContactByUserId(((mwResolveMatch *)mri->data)->id); + if(hContact) { + char *name = ((mwResolveMatch *)mri->data)->name; + if(name && strlen(name)) { + DBWriteContactSettingStringUtf(hContact, PROTO, "Name", name); + DBWriteContactSettingStringUtf(hContact, PROTO, "Nick", name); + } + } + } + } + } +} + +int SearchForUser(const char *name) { + if(current_status != ID_STATUS_OFFLINE && service_resolve) { + GList *query = g_list_prepend(0, (void *)name); + + guint32 id = mwServiceResolve_resolve(service_resolve, query, (mwResolveFlag)(mwResolveFlag_USERS | mwResolveFlag_GROUPS), mwResolve_handler, 0, 0); + + g_list_free(query); + return id; // search handle + } + + return 0; // fail +} + +int GetMoreDetails(const char *name) { + if(current_status != ID_STATUS_OFFLINE && service_resolve) { + GList *query = g_list_prepend(0, (void *)name); + + guint32 id = mwServiceResolve_resolve(service_resolve, query, (mwResolveFlag)(mwResolveFlag_USERS | mwResolveFlag_UNIQUE), mwResolve_handler_details, 0, 0); + + g_list_free(query); + return id; // search handle + } + + return 0; // fail +} + +static BOOL CALLBACK SearchDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch ( msg ) { + case WM_INITDIALOG: + { + TranslateDialogDefault( hwndDlg ); + break; + } + } + return 0; +} + +int CreateSearchDialog(WPARAM wParam, LPARAM lParam) { + //MessageBox(0, _T("Creating Dialog"), _T("CreateSearchDialog"), MB_OK); + return (int)CreateDialog(hInst, MAKEINTRESOURCE(IDD_USERSEARCH), (HWND)lParam, SearchDialogFunc); +} + +int SearchFromDialog(WPARAM wParam, LPARAM lParam) { + //MessageBox(0, _T("Searching..."), _T("SearchFromDialog"), MB_OK); + HWND hWnd = (HWND)lParam; + TCHAR buf[512]; + if(GetDlgItemText(hWnd, IDC_EDIT1, buf, 512)) { + if(current_status != ID_STATUS_OFFLINE && service_resolve) { + char name[512]; +#ifdef _UNICODE + WideCharToMultiByte(CP_UTF8, 0, buf, -1, name, 512, 0, 0); +#else + strncpy(name, buf, 512); +#endif + + GList *query = g_list_prepend(0, (void *)name); + + guint32 id = mwServiceResolve_resolve(service_resolve, query, (mwResolveFlag)(mwResolveFlag_USERS | mwResolveFlag_GROUPS), mwResolve_handler, (void *)1, 0); + + g_list_free(query); + return id; // search handle + } + } + return 0; +} + +HANDLE AddSearchedUser(MYPROTOSEARCHRESULT *mpsr, bool temporary) { + HANDLE hContact = 0; + + mwSametimeList *user_list = mwSametimeList_new(); + mwSametimeGroup *stgroup = 0; + if(mpsr->group) { + stgroup = mwSametimeGroup_new(user_list, mwSametimeGroup_DYNAMIC, mpsr->stid); + mwSametimeGroup_setAlias(stgroup, mpsr->name); + ImportContactsFromList(user_list, temporary); + } else { + stgroup = mwSametimeGroup_new(user_list, mwSametimeGroup_NORMAL, Translate("None")); + + mwIdBlock uid; + uid.user = mpsr->stid; + uid.community = 0; + mwSametimeUser *stuser = mwSametimeUser_new(stgroup, mwSametimeUser_NORMAL, &uid); + mwSametimeUser_setShortName(stuser, mpsr->name); + + hContact = AddContact(stuser, temporary); + mwSametimeList_free(mwSametimeGroup_getList(stgroup)); + } + + + return hContact; +} + +void InitUserList(mwSession *session) { + mwSession_addService(session, (mwService *)(service_storage = mwServiceStorage_new(session))); + mwSession_addService(session, (mwService *)(service_resolve = mwServiceResolve_new(session))); + mwSession_addService(session, (mwService *)(service_aware = mwServiceAware_new(session, &mwAware_handler))); + + hContactDeletedEvent = HookEvent(ME_DB_CONTACT_DELETED, ContactDeleted); +} + +void DeinitUserList(mwSession *session) { + UnhookEvent(hContactDeletedEvent); + hContactDeletedEvent = 0; + + mwSession_removeService(session, mwService_AWARE); + mwService_free((mwService *)service_aware); + service_aware = 0; + + mwSession_removeService(session, mwService_RESOLVE); + mwService_free((mwService *)service_resolve); + service_resolve = 0; + + mwSession_removeService(session, mwService_STORAGE); + mwService_free((mwService *)service_storage); + service_storage = 0; +} + diff --git a/plugins/!NotAdopted/sametime/userlist.h b/plugins/!NotAdopted/sametime/userlist.h new file mode 100644 index 0000000000..be674b0e5f --- /dev/null +++ b/plugins/!NotAdopted/sametime/userlist.h @@ -0,0 +1,59 @@ +#ifndef _USERLIST_INC +#define _USERLIST_INC + +#include "common.h" +#include "utils.h" + +#include +#include +#include + +#include "clist_util.h" + +typedef struct { + int cbSize; + char *nick; + char *firstName; + char *lastName; + char *email; + char reserved[16]; + + char name[256]; + char stid[256]; + bool group; + +} MYPROTOSEARCHRESULT; + +typedef struct { + size_t nSize; + int nFieldCount; + TCHAR ** pszFields; + MYPROTOSEARCHRESULT psr; +} MYCUSTOMSEARCHRESULTS; + +HANDLE FindContactByUserId(const char *id); +bool GetAwareIdFromContact(HANDLE hContact, mwAwareIdBlock *id_block); + +int SearchForUser(const char *name); +int GetMoreDetails(const char *name); + +int CreateSearchDialog(WPARAM wParam, LPARAM lParam); +int SearchFromDialog(WPARAM wParam, LPARAM lParam); + +HANDLE AddSearchedUser(MYPROTOSEARCHRESULT *mpsr, bool temporary); + +HANDLE AddContact(mwSametimeUser *user, bool temporary); + +void UserListCreate(); +void UserListAddStored(); +void UserListDestroy(); + +void ImportContactsFromFile(TCHAR *filename); +void ExportContactsToServer(); + +void UserRecvAwayMessage(HANDLE hContact); + +void InitUserList(mwSession *session); +void DeinitUserList(mwSession *session); + +#endif \ No newline at end of file diff --git a/plugins/!NotAdopted/sametime/utils.cpp b/plugins/!NotAdopted/sametime/utils.cpp new file mode 100644 index 0000000000..bac35f5009 --- /dev/null +++ b/plugins/!NotAdopted/sametime/utils.cpp @@ -0,0 +1,335 @@ +#include "utils.h" + +HICON hProtoIcon; + +//LRESULT CALLBACK NullWindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) +static int NullWindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + switch( message ) { + case WM_COMMAND: { + if (HIWORD(wParam) == STN_CLICKED) { //It was a click on the Popup. + PUDeletePopUp( hWnd ); + return TRUE; + } + break; + } + + case WM_CONTEXTMENU: + PUDeletePopUp( hWnd ); + return TRUE; + } + + return DefWindowProc(hWnd, message, wParam, lParam); +} + +void CALLBACK sttPopupProcA( ULONG param ) { +//DWORD sttPopupProcA(LPVOID param) { + POPUPDATAEX* ppd = ( POPUPDATAEX* )param; + + if ( ServiceExists(MS_POPUP_ADDPOPUPEX) ) + CallService( MS_POPUP_ADDPOPUPEX, ( WPARAM )ppd, 0 ); + else + if ( ServiceExists(MS_POPUP_ADDPOPUP) ) + CallService( MS_POPUP_ADDPOPUP, ( WPARAM )ppd, 0 ); + + free( ppd ); + + //return 0; +} + +void CALLBACK sttPopupProcW( ULONG param ) { +//DWORD sttPopupProcW(LPVOID param) { + POPUPDATAW* ppd = ( POPUPDATAW* )param; + + if ( ServiceExists(MS_POPUP_ADDPOPUPW) ) + CallService( MS_POPUP_ADDPOPUPW, ( WPARAM )ppd, 0 ); + + free( ppd ); + + //return 0; +} + +void ShowPopupA( const char* line1, const char* line2, int flags ) +{ + if(CallService(MS_SYSTEM_TERMINATED, 0, 0)) return; + + if ( !ServiceExists( MS_POPUP_ADDPOPUP )) { + char title[256]; + sprintf(title, "%s Message", PROTO); + + if(line1 && line2) { + char *message = new char[strlen(line1) + strlen(line2) + 2]; // newline and null terminator + sprintf(message, "%s\n%s", line1, line2); + MessageBoxA( NULL, message, title, MB_OK | MB_ICONINFORMATION ); + delete message; + } else if(line1) { + MessageBoxA( NULL, line1, title, MB_OK | MB_ICONINFORMATION ); + } else if(line2) { + MessageBoxA( NULL, line2, title, MB_OK | MB_ICONINFORMATION ); + } + return; + } + + POPUPDATAEX* ppd = ( POPUPDATAEX* )malloc( sizeof( POPUPDATAEX )); + memset((void *)ppd, 0, sizeof(POPUPDATAEX)); + + ppd->lchContact = NULL; + ppd->lchIcon = hProtoIcon; + + if(line1 && line2) { + strncpy( ppd->lpzContactName, line1, MAX_CONTACTNAME ); + strncpy( ppd->lpzText, line2, MAX_SECONDLINE ); + } else if(line1) strncpy( ppd->lpzText, line1, MAX_SECONDLINE ); + else if(line2) strncpy( ppd->lpzText, line2, MAX_SECONDLINE ); + + //ppd->colorBack = GetSysColor( COLOR_BTNFACE ); + //ppd->colorText = GetSysColor( COLOR_WINDOWTEXT ); + //ppd->colorText = 0x00FFFFFF; // for old popup modules + //ppd->colorBack = POPUP_USE_SKINNED_BG; + + ppd->iSeconds = 0; + + ppd->PluginWindowProc = ( WNDPROC )NullWindowProc; + ppd->PluginData = NULL; + + QueueUserAPC( sttPopupProcA , mainThread, ( ULONG )ppd ); + //CloseHandle(CreateThread(0, 0, (LPTHREAD_START_ROUTINE)sttPopupProcA, (LPVOID)ppd, 0, 0)); + //sttPopupProcA((ULONG)ppd); +} + +void ShowPopupW( const wchar_t* line1, const wchar_t* line2, int flags ) +{ + if(CallService(MS_SYSTEM_TERMINATED, 0, 0)) return; + + if ( !ServiceExists( MS_POPUP_ADDPOPUPW )) { + wchar_t title[256], *ws; + swprintf(title, L"%s Message", TranslateW(ws = a2w(PROTO))); + free(ws); + + if(line1 && line2) { + wchar_t *message = new wchar_t[wcslen(line1) + wcslen(line2) + 2]; // newline and null terminator + swprintf(message, L"%s\n%s", line1, line2); + MessageBoxW( NULL, message, title, MB_OK | MB_ICONINFORMATION ); + delete message; + } else if(line1) { + MessageBoxW( NULL, line1, title, MB_OK | MB_ICONINFORMATION ); + } else if(line2) { + MessageBoxW( NULL, line2, title, MB_OK | MB_ICONINFORMATION ); + } + return; + } + + POPUPDATAW* ppd = ( POPUPDATAW* )malloc( sizeof( POPUPDATAW )); + memset((void *)ppd, 0, sizeof(POPUPDATAW)); + + ppd->lchContact = NULL; + ppd->lchIcon = hProtoIcon; + + if(line1 && line2) { + wcsncpy( ppd->lpwzContactName, line1, MAX_CONTACTNAME ); + wcsncpy( ppd->lpwzText, line2, MAX_SECONDLINE ); + } else if(line1) wcsncpy( ppd->lpwzText, line1, MAX_SECONDLINE ); + else if(line2) wcsncpy( ppd->lpwzText, line2, MAX_SECONDLINE ); + + //ppd->colorBack = GetSysColor( COLOR_BTNFACE ); + //ppd->colorText = GetSysColor( COLOR_WINDOWTEXT ); + //ppd->colorText = 0x00FFFFFF; // for old popup modules + //ppd->colorBack = POPUP_USE_SKINNED_BG; + + ppd->iSeconds = 0; + + ppd->PluginWindowProc = ( WNDPROC )NullWindowProc; + ppd->PluginData = NULL; + + QueueUserAPC( sttPopupProcW , mainThread, ( ULONG )ppd ); + //CloseHandle(CreateThread(0, 0, (LPTHREAD_START_ROUTINE)sttPopupProcW, (LPVOID)ppd, 0, 0)); + //sttPopupProcW((ULONG)ppd); +} + +void ShowWarning(TCHAR *msg) { + TCHAR buffer[512]; + ErrorDisplay disp = options.err_method; + // funny logic :) ... try to avoid message boxes + // if want baloons but no balloons, try popups + // if want popups but no popups, try baloons + // if, after that, you want balloons but no balloons, revert to message boxes + if(disp == ED_BAL && !ServiceExists(MS_CLIST_SYSTRAY_NOTIFY)) disp = ED_POP; + if(disp == ED_POP && !ServiceExists(MS_POPUP_SHOWMESSAGE)) disp = ED_BAL; + if(disp == ED_BAL && !ServiceExists(MS_CLIST_SYSTRAY_NOTIFY)) disp = ED_MB; + +#ifdef _UNICODE + wsprintf(buffer, L"%s Warning", TranslateTS(PROTO)); +#else + sprintf(buffer, "%s Warning", Translate(PROTO)); +#endif + + + switch(disp) { + case ED_POP: + ShowPopup(buffer, msg, 0); + break; + case ED_MB: + MessageBox(0, msg, buffer, MB_OK | MB_ICONWARNING); + break; + case ED_BAL: + { + MIRANDASYSTRAYNOTIFY sn = {0}; + sn.cbSize = sizeof(sn); + sn.szProto = PROTO; + sn.szInfoTitle = t2a(buffer); + sn.szInfo = t2a(msg); + sn.dwInfoFlags = NIIF_WARNING; + sn.uTimeout = 10; + + CallService(MS_CLIST_SYSTRAY_NOTIFY, 0, (LPARAM)&sn); + + free(sn.szInfoTitle); + free(sn.szInfo); + } + + break; + } +} + +void ShowError(TCHAR *msg) { + TCHAR buffer[512]; + ErrorDisplay disp = options.err_method; + // funny logic :) ... try to avoid message boxes + // if want baloons but no balloons, try popups + // if want popups but no popups, try baloons + // if, after that, you want balloons but no balloons, revert to message boxes + if(disp == ED_BAL && !ServiceExists(MS_CLIST_SYSTRAY_NOTIFY)) disp = ED_POP; + if(disp == ED_POP && !ServiceExists(MS_POPUP_SHOWMESSAGE)) disp = ED_BAL; + if(disp == ED_BAL && !ServiceExists(MS_CLIST_SYSTRAY_NOTIFY)) disp = ED_MB; + +#ifdef _UNICODE + wchar_t *ws = a2w(PROTO); + wsprintf(buffer, L"%s Error", TranslateW(ws)); + free(ws); +#else + sprintf(buffer, "%s Error", Translate(PROTO)); +#endif + + + switch(disp) { + case ED_POP: + ShowPopup(buffer, msg, 0); + break; + case ED_MB: + MessageBox(0, msg, buffer, MB_OK | MB_ICONWARNING); + break; + case ED_BAL: + { + MIRANDASYSTRAYNOTIFY sn = {0}; + sn.cbSize = sizeof(sn); + sn.szProto = PROTO; + sn.szInfoTitle = t2a(buffer); + sn.szInfo = t2a(msg); + sn.dwInfoFlags = NIIF_ERROR; + sn.uTimeout = 10; + + CallService(MS_CLIST_SYSTRAY_NOTIFY, 0, (LPARAM)&sn); + + free(sn.szInfoTitle); + free(sn.szInfo); + } + + break; + } +} + + +void InitUtils() { + hProtoIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON_PROTO), IMAGE_ICON, SM_CXSMICON, SM_CYSMICON, 0); + //hProtoIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON_PROTO), IMAGE_ICON, SM_CXICON, SM_CYICON, 0); +} + +void DeinitUtils() { + DestroyIcon(hProtoIcon); +} + +char *w2u(const wchar_t *ws) { + if(ws) { + int size = WideCharToMultiByte(CP_UTF8, 0, ws, -1, 0, 0, 0, 0); + char *buff = (char *)malloc(size); + WideCharToMultiByte(CP_UTF8, 0, ws, -1, buff, size, 0, 0); + return buff; + } else + return 0; +} + +wchar_t *u2w(const char *utfs) { + if(utfs) { + int size = MultiByteToWideChar(CP_UTF8, 0, utfs, -1, 0, 0); + wchar_t *buff = (wchar_t *)malloc(size * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, utfs, -1, buff, size); + return buff; + } else + return 0; +} + +wchar_t *a2w(const char *as) { + int size = MultiByteToWideChar(code_page, 0, as, -1, 0, 0); + wchar_t *buff = (wchar_t *)malloc(size * sizeof(wchar_t)); + MultiByteToWideChar(code_page, 0, as, -1, buff, size); + return buff; +} + +char *w2a(const wchar_t *ws) { + int size = WideCharToMultiByte(code_page, 0, ws, -1, 0, 0, 0, 0); + char *buff = (char *)malloc(size); + WideCharToMultiByte(code_page, 0, ws, -1, buff, size, 0, 0); + return buff; +} + +char *t2a(const TCHAR *ts) { +#ifdef _UNICODE + return w2a(ts); +#else + return _strdup(ts); +#endif +} + +TCHAR *a2t(const char *as) { +#ifdef _UNICODE + return a2w(as); +#else + return _strdup(as); +#endif +} + +TCHAR *u2t(const char *utfs) { +#ifdef _UNICODE + return u2w(utfs); +#else + wchar_t *ws = u2w(utfs); + char *ret = w2a(ws); + free(ws); + return ret; +#endif +} + +char *t2u(const TCHAR *ts) { +#ifdef _UNICODE + return w2u(ts); +#else + wchar_t *ws = a2w(ts); + char *ret = w2u(ws); + free(ws); + return ret; +#endif +} + +char *u2a(const char *utfs) { + wchar_t *ws = u2w(utfs); + char *ret = w2a(ws); + free(ws); + return ret; +} + +char *a2u(const char *as) { + wchar_t *ws = a2w(as); + char *ret = w2u(ws); + free(ws); + return ret; +} diff --git a/plugins/!NotAdopted/sametime/utils.h b/plugins/!NotAdopted/sametime/utils.h new file mode 100644 index 0000000000..600562cf60 --- /dev/null +++ b/plugins/!NotAdopted/sametime/utils.h @@ -0,0 +1,37 @@ +#ifndef _UTILS_INC +#define _UTILS_INC + +#include "common.h" +#include "options.h" + +wchar_t *a2w(const char *as); +char *w2a(const wchar_t *ws); + +char *w2u(const wchar_t *ws); +wchar_t *u2w(const char *ws); + +TCHAR *u2t(const char *utfs); +char *t2u(const TCHAR *ts); + +char *t2a(const TCHAR *ts); +TCHAR *a2t(const char *as); + +char *u2a(const char *utfs); +char *a2u(const char *as); + +void InitUtils(); +void DeinitUtils(); + +void ShowPopupA( const char* line1, const char* line2, int flags = 0); +void ShowPopupW( const wchar_t* line1, const wchar_t* line2, int flags = 0); + +#ifdef _UNICODE +#define ShowPopup ShowPopupW +#else +#define ShowPopup ShowPopupA +#endif + +void ShowWarning(TCHAR *msg); +void ShowError(TCHAR *msg); + +#endif -- cgit v1.2.3