From 9c6d8a640911d336cc1e758310bbfccaf547d2d2 Mon Sep 17 00:00:00 2001 From: sje Date: Wed, 1 Nov 2006 14:40:04 +0000 Subject: git-svn-id: https://server.scottellis.com.au/svn/mim_plugs@10 4f64403b-2f21-0410-a795-97e2b3489a10 --- otr/IcoLib.h | 37 ++ otr/Makefile.win | 46 ++ otr/common.h | 71 ++++ otr/dll.h | 10 + otr/dllmain.cpp | 1200 ++++++++++++++++++++++++++++++++++++++++++++++++++++ otr/libotr.def | 5 + otr/menu.cpp | 102 +++++ otr/menu.h | 9 + otr/options.cpp | 418 ++++++++++++++++++ otr/options.h | 24 ++ otr/otr.layout | 87 ++++ otr/otr.mdsp | 108 +++++ otr/otr_private.h | 24 ++ otr/otr_private.rc | 35 ++ otr/resource.h | 20 + otr/resource.rc | 111 +++++ otr/start.ico | Bin 0 -> 2550 bytes otr/stop.ico | Bin 0 -> 2550 bytes otr/utils.cpp | 282 ++++++++++++ otr/utils.h | 15 + 20 files changed, 2604 insertions(+) create mode 100644 otr/IcoLib.h create mode 100644 otr/Makefile.win create mode 100644 otr/common.h create mode 100644 otr/dll.h create mode 100644 otr/dllmain.cpp create mode 100644 otr/libotr.def create mode 100644 otr/menu.cpp create mode 100644 otr/menu.h create mode 100644 otr/options.cpp create mode 100644 otr/options.h create mode 100644 otr/otr.layout create mode 100644 otr/otr.mdsp create mode 100644 otr/otr_private.h create mode 100644 otr/otr_private.rc create mode 100644 otr/resource.h create mode 100644 otr/resource.rc create mode 100644 otr/start.ico create mode 100644 otr/stop.ico create mode 100644 otr/utils.cpp create mode 100644 otr/utils.h diff --git a/otr/IcoLib.h b/otr/IcoLib.h new file mode 100644 index 0000000..1817df6 --- /dev/null +++ b/otr/IcoLib.h @@ -0,0 +1,37 @@ +typedef struct { + int cbSize; + char *pszSection; //section name used to group icons + char *pszDescription; //description for options dialog + char *pszName; //name to refer to icon when playing and in db + //this name is miranda-wide. so use prefixes of your plugin + //e.g: "isee_connect", "clist_delete", etc + char *pszDefaultFile; //default icon file to use + int iDefaultIndex; +} SKINICONDESC; + +typedef struct { + int cbSize; + char *pszSection; + char *pszDescription; + char *pszName; + char *pszDefaultFile; + int iDefaultIndex; + HICON hDefaultIcon; +} SKINICONDESC2; + +// +// Add a icon into options UI +// +// wParam = (WPARAM)0 +// lParam = (LPARAM)(SKINICONDESC*)sid; +// +#define MS_SKIN2_ADDICON "Skin2/Icons/AddIcon" +// +// Retrieve HICON with name specified in lParam +// Returned HICON SHOULDN'T be destroyed, it managed by IcoLib +// +#define MS_SKIN2_GETICON "Skin2/Icons/GetIcon" +// +// Icons change notification +// +#define ME_SKIN2_ICONSCHANGED "Skin2/IconsChanged" diff --git a/otr/Makefile.win b/otr/Makefile.win new file mode 100644 index 0000000..c40937a --- /dev/null +++ b/otr/Makefile.win @@ -0,0 +1,46 @@ +# Project: otr +# Makefile created by Dev-C++ 4.9.9.2 + +CPP = g++.exe +CC = gcc.exe +WINDRES = windres.exe +RES = otr_private.res +OBJ = dllmain.o options.o menu.o utils.o $(RES) +LINKOBJ = dllmain.o options.o menu.o utils.o $(RES) +LIBS = -L"lib" -L"C:/rcslib-2005.June/lib" -L"C:/wxWidgets-2.6.2/lib" -L"C:/SDL-1.2.9/src/.libs" -L"C:/msys/1.0/local/lib" -lcomctl32 -lotr -lgcrypt -lgpg-error -s -s +INCS = -I"include" -I"C:/rcslib-2005.June/include" -I"C:/msys/1.0/local/include" -I"C:/msys/1.0/local/include/libotr" +CXXINCS = -I"lib/gcc/mingw32/3.4.2/include" -I"include/c++/3.4.2/backward" -I"include/c++/3.4.2/mingw32" -I"include/c++/3.4.2" -I"include" -I"C:/rcslib-2005.June/include" -I"C:/wxWidgets-2.6.2/lib/wx/include/msw-ansi-release-2.6" -I"C:/wxWidgets-2.6.2/include" -I"C:/SDL-1.2.9/include" -I"C:/msys/1.0/local/include" -I"C:/msys/1.0/local/include/libotr" +BIN = otr.dll +CXXFLAGS = $(CXXINCS) -DBUILDING_DLL=1 -O3 -s -fexpensive-optimizations -O3 +CFLAGS = $(INCS) -DBUILDING_DLL=1 -fexpensive-optimizations -O3 +RM = rm -f + +.PHONY: all all-before all-after clean clean-custom + +all: all-before otr.dll all-after + + +clean: clean-custom + ${RM} $(OBJ) $(BIN) + +DLLWRAP=dllwrap.exe +DEFFILE=libotr.def +STATICLIB=libotr.a + +$(BIN): $(LINKOBJ) + $(DLLWRAP) --output-def $(DEFFILE) --driver-name c++ --implib $(STATICLIB) $(LINKOBJ) $(LIBS) -o $(BIN) + +dllmain.o: dllmain.cpp + $(CPP) -c dllmain.cpp -o dllmain.o $(CXXFLAGS) + +options.o: options.cpp + $(CPP) -c options.cpp -o options.o $(CXXFLAGS) + +menu.o: menu.cpp + $(CPP) -c menu.cpp -o menu.o $(CXXFLAGS) + +utils.o: utils.cpp + $(CPP) -c utils.cpp -o utils.o $(CXXFLAGS) + +otr_private.res: otr_private.rc resource.rc + $(WINDRES) -i otr_private.rc --input-format=rc -o otr_private.res -O coff diff --git a/otr/common.h b/otr/common.h new file mode 100644 index 0000000..062ae78 --- /dev/null +++ b/otr/common.h @@ -0,0 +1,71 @@ +#ifndef _COMMON_INC +#define _COMMON_INC + +#define _WIN32_WINNT 0x0500 +#define WINVER 0x0500 +#define _WIN32_IE 0x0300 + +#include +//#include +//#include +#include +#include +#include +#include + +#include "resource.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include "IcoLib.h" + +#define MODULE "OTR" + +extern "C" { +#include "proto.h" +#include "message.h" +#include "privkey.h" +}; + +// modified manual policy - so that users set to 'opportunistic' will automatically start OTR with users set to 'manual' +#define OTRL_POLICY_MANUAL_MOD (OTRL_POLICY_MANUAL | OTRL_POLICY_WHITESPACE_START_AKE | OTRL_POLICY_ERROR_START_AKE) + +// use the same filenames as the gaim plugin, for compatibility +#define PRIVATE_KEY_FILENAME "otr.private_key" +#define FINGERPRINT_STORE_FILENAME "otr.fingerprints" + + +extern HINSTANCE hInst; +extern HANDLE mainThread; +extern DWORD mainThreadId; + +extern OtrlUserState otr_user_state; +extern char private_key_filename[MAX_PATH]; + +#define MS_OTR_MENUSTART "OTR/Start" +#define MS_OTR_MENUSTOP "OTR/Stop" + +#define INLINE_PREFIX "[OTR Message] " +#define INLINE_PREFIX_LEN 14 + +#endif diff --git a/otr/dll.h b/otr/dll.h new file mode 100644 index 0000000..f735bc0 --- /dev/null +++ b/otr/dll.h @@ -0,0 +1,10 @@ +#ifndef _DLL_H_ +#define _DLL_H_ + +#if BUILDING_DLL +# define DLLIMPORT __declspec (dllexport) +#else /* Not BUILDING_DLL */ +# define DLLIMPORT __declspec (dllimport) +#endif /* Not BUILDING_DLL */ + +#endif /* _DLL_H_ */ diff --git a/otr/dllmain.cpp b/otr/dllmain.cpp new file mode 100644 index 0000000..522a3bc --- /dev/null +++ b/otr/dllmain.cpp @@ -0,0 +1,1200 @@ +/* Replace "dll.h" with the name of your header */ + +#include "common.h" +#include "dll.h" +#include "options.h" +#include "menu.h" +#include "utils.h" + +#include "otr_private.h" + +/////////////////////////////////////////////// +// Common Plugin Stuff +/////////////////////////////////////////////// +HINSTANCE hInst; +PLUGINLINK *pluginLink; + +HANDLE mainThread; +DWORD mainThreadId; + +HANDLE hEventDbEventAdded, hEventDbEventAddedFilter, hEventWindow, hEventIconPressed; + +CRITICAL_SECTION lib_cs; + +int code_page = CP_ACP; + +#include +std::list alloc_list; + +// plugin stuff +PLUGININFO pluginInfo={ + sizeof(PLUGININFO), + MODULE, + PLUGIN_MAKE_VERSION(VER_MAJOR, VER_MINOR, VER_RELEASE, VER_BUILD), + DESC_STRING, + "Scott Ellis", + "mail@scottellis.com.au", + "© 2005 Scott Ellis", + "http://www.scottellis.com.au/", + 0, //not transient + 0 //doesn't replace anything built-in +}; + +extern "C" BOOL APIENTRY DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved) { + hInst=hinstDLL; + return TRUE; +} + +extern "C" DLLIMPORT PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion) { + return &pluginInfo; +} + +///////////////////////// +// dodgy, INNEFFICIENT, utility function +//////////////////////// +HANDLE get_contact(const char *protocol, const char *disp_name) { + HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + while ( hContact != NULL ) + { + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + if(proto && strcmp(proto, protocol) == 0) { + char *name = (char *)CallService( MS_CLIST_GETCONTACTDISPLAYNAME, ( WPARAM )hContact, 0 ); + if(name && strcmp(name, disp_name) == 0) { + return hContact; + } + } + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } + + return 0; +} + +// wish there was an EVENTTYPE_INLINEMSG :) +void ShowMessageInline(const HANDLE hContact, const char *msg) { + char buff[1024]; + mir_snprintf(buff, 1024, "%s%s", INLINE_PREFIX, msg); + + /* + // bypasses filters etc - including metacontacts + DBEVENTINFO dbei = {0}; + dbei.cbSize = sizeof(DBEVENTINFO); + dbei.szModule = MODULE; + dbei.timestamp = time(0); + //dbei.flags = DBEF_READ; + dbei.eventType = EVENTTYPE_MESSAGE; + dbei.cbBlob = strlen(buff); + dbei.pBlob = (BYTE *)buff; + + CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&dbei); + */ + + PROTORECVEVENT pre = {0}; + pre.timestamp = time(0); + pre.szMessage = buff; + //CallContactService(hContact, PSR_MESSAGE, 0, (LPARAM)&pre); + + CCSDATA ccs = {0}; + ccs.hContact = hContact; + ccs.szProtoService = PSR_MESSAGE; + ccs.lParam = (LPARAM)⪯ + + CallService(MS_PROTO_CHAINRECV, 0, (LPARAM)&ccs); +} + +// set SRMM icon status, if applicable +void SetEncryptionStatus(HANDLE hContact, bool encrypted) { + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + bool chat_room = (proto && DBGetContactSettingByte(hContact, proto, "ChatRoom", 0)); + + if(!chat_room) DBWriteContactSettingByte(hContact, MODULE, "Encrypted", (encrypted ? 1 : 0)); + + if(ServiceExists(MS_MSG_MODIFYICON)) { + StatusIconData sid = {0}; + sid.cbSize = sizeof(sid); + sid.szModule = MODULE; + sid.dwId = 0; + sid.flags = (chat_room ? MBF_HIDDEN : (encrypted ? 0 : MBF_DISABLED)); + CallService(MS_MSG_MODIFYICON, (WPARAM)hContact, (LPARAM)&sid); + + if(!chat_room && ServiceExists(MS_MC_GETMETACONTACT)) { + HANDLE hMeta = (HANDLE)CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, 0); + if(hContact == (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hMeta, 0)) + SetEncryptionStatus(hMeta, encrypted); + } + } +} + +/////////////////////////////////////////////// +// OTR stuff +/////////////////////////////////////////////// + +void add_appdata(void *data, ConnContext *context) { + if(context) context->app_data = data; // data is hContact +} + +void lib_cs_lock() { + EnterCriticalSection(&lib_cs); +} + +void lib_cs_unlock() { + LeaveCriticalSection(&lib_cs); +} + +char fingerprint_store_filename[MAX_PATH]; +char private_key_filename[MAX_PATH]; + +OtrlUserState otr_user_state = 0; + +/* Return the OTR policy for the given context. */ +extern "C" OtrlPolicy otr_gui_policy(void *opdata, ConnContext *context) { + HANDLE hContact = (HANDLE)opdata; + if(hContact) { + WORD pol = DBGetContactSettingWord(hContact, MODULE, "Policy", CONTACT_DEFAULT_POLICY); + if(pol == CONTACT_DEFAULT_POLICY) + return options.default_policy | OTRL_POLICY_NOHTML; + else return pol | OTRL_POLICY_NOHTML; + } + + return options.default_policy | OTRL_POLICY_NOHTML; +} + +typedef struct { + char *account_name; + char *protocol; +} NewKeyData; + +void CALLBACK newKeyAPC(DWORD data) { +//DWORD CALLBACK newKeyThread(VOID *data) { + CallService(MS_SYSTEM_THREAD_PUSH, 0, 0); + + NewKeyData *nkd = (NewKeyData *)data; + otrl_privkey_generate(otr_user_state, private_key_filename, nkd->account_name, nkd->protocol); + + free(nkd->account_name); + free(nkd->protocol); + free(nkd); + + CallService(MS_SYSTEM_THREAD_POP, 0, 0); + //return 0; +} + +BOOL CALLBACK NullDlgFunc(HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { + switch(msg) { + case WM_INITDIALOG: + TranslateDialogDefault(hWndDlg); + break; + } + return FALSE; +} + +/* Create a private key for the given accountname/protocol if + * desired. */ +extern "C" void otr_gui_create_privkey(void *opdata, const char *account_name, const char *protocol) { + //if(MessageBox(0, Translate("Would you like to generate a new private key for this protocol?"), Translate("OTR"), MB_YESNO) == IDYES) + //if(options.err_method == ED_POP) + //ShowPopup(Translate("Generating new private key."), 0 /*Translate("Please wait.")*/, 5); + + //NewKeyData *nkd = (NewKeyData *)malloc(sizeof(NewKeyData)); + //nkd->account_name = strdup(account_name); + //nkd->protocol = strdup(protocol); + + //DWORD tid; + //CloseHandle(CreateThread(0, 0, newKeyThread, (VOID *)nkd, 0, &tid)); + //QueueUserAPC(newKeyAPC, mainThread, (DWORD)nkd); + HWND hWndNotifyDlg = CreateDialog(hInst, MAKEINTRESOURCE(IDD_GENKEYNOTIFY), GetDesktopWindow(), NullDlgFunc); + SetClassLong(hWndNotifyDlg, GCL_HICON, (LONG)hLockIcon); + ShowWindow(hWndNotifyDlg, SW_SHOW); + UpdateWindow(hWndNotifyDlg); + otrl_privkey_generate(otr_user_state, private_key_filename, account_name, protocol); + DestroyWindow(hWndNotifyDlg); + +} + +/* Report whether you think the given user is online. Return 1 if + * you think he is, 0 if you think he isn't, -1 if you're not sure. + * + * If you return 1, messages such as heartbeats or other + * notifications may be sent to the user, which could result in "not + * logged in" errors if you're wrong. */ +extern "C" int otr_gui_is_logged_in(void *opdata, const char *accountname, const char *protocol, const char *recipient) { + HANDLE hContact = (HANDLE)opdata; + if(hContact) { + WORD status = DBGetContactSettingWord(hContact, (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0), "Status", ID_STATUS_OFFLINE); + if(status == ID_STATUS_OFFLINE) return 0; + else return 1; + } + + return -1; +} + +/* Send the given IM to the given recipient from the given + * accountname/protocol. */ +extern "C" void otr_gui_inject_message(void *opdata, const char *accountname, const char *protocol, const char *recipient, const char *message) { + //MessageBox(0, message, "OTR Inject", MB_OK); + HANDLE hContact = (HANDLE)opdata; + + // bypass filters (including this module, metacontacts, etc) - go direct to proto level + if(protocol && DBGetContactSettingWord(hContact, protocol, "Status", ID_STATUS_OFFLINE) != ID_STATUS_OFFLINE) { + CCSDATA ccs = {0}; + ccs.hContact = hContact; + ccs.szProtoService = PSS_MESSAGE; + ccs.lParam = (LPARAM)Translate(message); + CallProtoService(protocol, PSS_MESSAGE, 0, (LPARAM)&ccs); + } +} + +/* Display a notification message for a particular accountname / + * protocol / username conversation. */ +extern "C" void otr_gui_notify(void *opdata, OtrlNotifyLevel level, const char *accountname, const char *protocol, const char *username, const char *title, const char *primary, const char *secondary) { + char buff1[512], buff2[512]; + mir_snprintf(buff1, 512, "%s: %s", username, title); + mir_snprintf(buff2, 512, "%s\n%s", primary, secondary); + ShowPopup(buff1, buff2, 0); +} + +/* Display an OTR control message for a particular accountname / + * protocol / username conversation. Return 0 if you are able to + * successfully display it. If you return non-0 (or if this + * function is NULL), the control message will be displayed inline, + * as a received message, or else by using the above notify() + * callback. */ +extern "C" int otr_gui_display_otr_message(void *opdata, const char *accountname, const char *protocol, const char *username, const char *msg) { + /* + * char buff[512]; + * mir_snprintf(buff, 512, Translate("%s(%s)"), protocol, username); + *ShowPopup(buff, msg, 0); + + return 0; + */ + //return 1; + + ShowMessageInline((HANDLE)opdata, msg); + return 0; +} + +/* When the list of ConnContexts changes (including a change in + * state), this is called so the UI can be updated. */ +extern "C" void otr_gui_update_context_list(void *opdata) { + //MessageBox(0, "Update Context List", "OTR Callback", MB_OK); +} + +/* Return a newly-allocated string containing a human-friendly name + * for the given protocol id */ +extern "C" const char *otr_gui_protocol_name(void *opdata, const char *protocol) { + //return strdup(protocol); + return protocol; +} + +/* Deallocate a string allocated by protocol_name */ +extern "C" void otr_gui_protocol_name_free(void *opdata, const char *protocol_name) { + //free((void *)protocol_name); +} + +unsigned int CALLBACK trust_fp_thread(void *param) { + CallService(MS_SYSTEM_THREAD_PUSH, 0, 0); + + Fingerprint *fp = (Fingerprint *)param; + + char hash[45]; + otrl_privkey_hash_to_human(hash, fp->fingerprint); + + char msg[1024]; + mir_snprintf(msg, 1024, Translate("A new fingerprint has been recieved from '%s'\n\n%s\n\nDo you trust it?"), fp->context->username, hash); + if(MessageBox(0, msg, Translate("OTR New Fingerprint"), MB_YESNO) == IDYES) { + lib_cs_lock(); + otrl_context_set_trust(fp, "verified"); + lib_cs_unlock(); + } else { + lib_cs_lock(); + otrl_context_set_trust(fp, "unknown"); + lib_cs_unlock(); + } + + CallService(MS_SYSTEM_THREAD_POP, 0, 0); + return 0; +} + +/* A new fingerprint for the given user has been received. */ +extern "C" void otr_gui_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname, const char *protocol, const char *username, unsigned char fingerprint[20]) { + //MessageBox(0, username, Translate("OTR New Fingerprint"), MB_OK); + ConnContext *context = otrl_context_find(us, username, accountname, protocol, TRUE, 0, add_appdata, opdata); + Fingerprint *fp = otrl_context_find_fingerprint(context, fingerprint, TRUE, 0); + + //CloseHandle((HANDLE)_beginthreadex(0, 0, trust_fp_thread, (void *)fp, 0, 0)); + + otrl_context_set_trust(fp, "unknown"); + otrl_privkey_write_fingerprints(otr_user_state, fingerprint_store_filename); +} + +/* The list of known fingerprints has changed. Write them to disk. */ +extern "C" void otr_gui_write_fingerprints(void *opdata) { + //if(MessageBox(0, Translate("Would you like to save the current fingerprint list?"), Translate(MODULE), MB_YESNO) == IDYES) + otrl_privkey_write_fingerprints(otr_user_state, fingerprint_store_filename); +} + +// forward dec +void Disconnect(ConnContext *context); + +unsigned int CALLBACK verify_fp_thread(void *param) { + CallService(MS_SYSTEM_THREAD_PUSH, 0, 0); + + ConnContext *context = (ConnContext *)param; + Fingerprint *fp = context->active_fingerprint; + + char hash[45]; + otrl_privkey_hash_to_human(hash, fp->fingerprint); + + char buff[1024]; + mir_snprintf(buff, 1024, Translate("OTR encrypted session with '%s'.\n\nFingerprint is UNVERIFIED.\n\n%s\n\nVerify?"), context->username, hash); + if(MessageBox(0, buff, Translate("OTR Information"), MB_YESNO) == IDNO) { + //Disconnect(context); + return 0; + } + + lib_cs_lock(); + otrl_context_set_trust(context->active_fingerprint, "verified"); + otrl_privkey_write_fingerprints(otr_user_state, fingerprint_store_filename); + lib_cs_unlock(); + + CallService(MS_SYSTEM_THREAD_POP, 0, 0); + return 0; +} +/* A ConnContext has entered a secure state. */ +extern "C" void otr_gui_gone_secure(void *opdata, ConnContext *context) { + bool trusted = (context->active_fingerprint && context->active_fingerprint->trust && strcmp(context->active_fingerprint->trust, "verified") == 0); + char buff[1024]; + if(trusted) { + mir_snprintf(buff, 1024, Translate("Beggining OTR encrypted session with '%s'"), context->username); + //MessageBox(0, buff, Translate("OTR Information"), MB_OK); + if(options.msg_inline) + ShowMessageInline((HANDLE)opdata, buff); + else + ShowPopup(Translate("OTR Information"), buff, 0); + + // opdata is hContact + SetEncryptionStatus((HANDLE)opdata, true); + } else { + CloseHandle((HANDLE)_beginthreadex(0, 0, verify_fp_thread, context, 0, 0)); + + mir_snprintf(buff, 1024, Translate("Beggining OTR encrypted session with '%s'"), context->username); + //MessageBox(0, buff, Translate("OTR Information"), MB_OK); + if(options.msg_inline) + ShowMessageInline((HANDLE)opdata, buff); + else + ShowPopup(Translate("OTR Information"), buff, 0); + + // opdata is hContact + SetEncryptionStatus((HANDLE)opdata, true); + } +} + +/* A ConnContext has left a secure state. */ +extern "C" void otr_gui_gone_insecure(void *opdata, ConnContext *context) { + char buff[256]; + mir_snprintf(buff, 256, Translate("OTR encrypted session with '%s' has ended"), context->username); + //MessageBox(0, buff, Translate("OTR Information"), MB_OK); + if(options.msg_inline) + ShowMessageInline((HANDLE)opdata, buff); + else + ShowPopup(Translate("OTR Information"), buff, 0); + + // opdata is hContact + SetEncryptionStatus((HANDLE)opdata, false); +} + +/* We have completed an authentication, using the D-H keys we + * already knew. is_reply indicates whether we initiated the AKE. */ +extern "C" void otr_gui_still_secure(void *opdata, ConnContext *context, int is_reply) { + if(is_reply) { + char buff[256]; + mir_snprintf(buff, 256, Translate("OTR encrypted session with '%s' is being continued"), context->username); + //MessageBox(0, buff, Translate("OTR Information"), MB_OK); + if(options.msg_inline) + ShowMessageInline((HANDLE)opdata, buff); + else + ShowPopup(Translate("OTR Information"), buff, 0); + } + + // opdata is hContact + SetEncryptionStatus((HANDLE)opdata, true); +} + +/* Log a message. The passed message will end in "\n". */ +extern "C" void otr_gui_log_message(void *opdata, const char *message) { + //MessageBox(0, message, Translate("OTR Log Message"), MB_OK); + //ShowMessageInline((HANDLE)opdata, message); +} + +OtrlMessageAppOps ops = { + otr_gui_policy, + otr_gui_create_privkey, + otr_gui_is_logged_in, + otr_gui_inject_message, + otr_gui_notify, + otr_gui_display_otr_message, + otr_gui_update_context_list, + otr_gui_protocol_name, + otr_gui_protocol_name_free, + otr_gui_new_fingerprint, + otr_gui_write_fingerprints, + otr_gui_gone_secure, + otr_gui_gone_insecure, + otr_gui_still_secure, + otr_gui_log_message +}; + +void Disconnect(ConnContext *context) { + otrl_message_disconnect(otr_user_state, &ops, 0, MODULE, context->protocol, context->username); +} + +////////////////////////////////////////////////// +/// Miranda filter plugin stuff +////////////////////////////////////////////////// + +int SettingChanged(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *)lParam; + + // only care about contacts + if(!hContact) return 0; + + // to which this filter is attached + if(!CallService(MS_PROTO_ISPROTOONCONTACT, (WPARAM)hContact, (LPARAM)MODULE)) + return 0; + + // and who are changing status to offline + if(strcmp(cws->szSetting, "Status") == 0 && cws->value.type != DBVT_DELETED && cws->value.wVal == ID_STATUS_OFFLINE) { + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + char *uname = (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0); + if(!proto || !uname) return 0; // error - just bail + + lib_cs_lock(); + ConnContext *context = otrl_context_find(otr_user_state, uname, MODULE, proto, FALSE, 0, 0, 0); + + if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED) { + otrl_message_disconnect(otr_user_state, &ops, hContact, MODULE, proto, uname); + + // removed - don't need a popup everytime an OTR user goes offline! + //char buff[512]; + //mir_snprintf(buff, 512, Translate("User '%s' ended encrypted session"), uname); + //ShowPopup(Translate("OTR Information"), buff, 0); + + // opdata is hContact + SetEncryptionStatus(hContact, false); + } + lib_cs_unlock(); + } + + return 0; +} + +int OTRSendMessage(WPARAM wParam,LPARAM lParam){ + if(!lParam) return 0; + + CCSDATA *ccs = (CCSDATA *) lParam; + + //MessageBox(0, (char *)ccs->lParam, "OTR - sending raw message", MB_OK); + + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ccs->hContact, 0); + if(proto && strcmp(proto, "MetaContacts") == 0) // bypass for metacontacts + return CallService(MS_PROTO_CHAINSEND, wParam, lParam); + if(!proto) return 1; // error + + gcry_error_t err; + char *newmessage = 0; + char *oldmessage = (char *)ccs->lParam; + char *oldmessage_utf = 0; + + //MessageBox(0, "Send message - converting to UTF-8", "msg", MB_OK); + + if(ccs->wParam & PREF_UNICODE) { + int size = WideCharToMultiByte(CP_UTF8, 0, (wchar_t *)&oldmessage[strlen(oldmessage)+1], -1, 0, 0, 0, 0); + oldmessage_utf = (char *)malloc(size); + if(!oldmessage_utf) return 1; + WideCharToMultiByte(CP_UTF8, 0, (wchar_t *)&oldmessage[strlen(oldmessage)+1], -1, oldmessage_utf, size, 0, 0); + } else { + wchar_t *temp = 0; + int size_temp, size; + size_temp = MultiByteToWideChar(code_page, 0, oldmessage, -1, 0, 0); + temp = (wchar_t *)malloc(size_temp * sizeof(wchar_t)); + if(!temp) return 1; + + MultiByteToWideChar(code_page, 0, oldmessage, -1, temp, size_temp); + + size = WideCharToMultiByte(CP_UTF8, 0, temp, -1, 0, 0, 0, 0); + oldmessage_utf = (char *)malloc(size); + if(!oldmessage_utf) { + free(temp); + return 1; + } + WideCharToMultiByte(CP_UTF8, 0, temp, -1, oldmessage_utf, size, 0, 0); + + free(temp); + } + + char *username = (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)ccs->hContact, 0); + // check if we're waiting for the other side to accept a new key from us + lib_cs_lock(); + /* + ConnContext *context = otrl_context_find(otr_user_state, username, MODULE, proto, FALSE, 0, 0, 0); + if(context && context->otr_offer == context::OFFER_SENT) { + lib_cs_unlock(); + if(MessageBox(0, Translate("The receiver has not yet accepted or rejected your fingerprint.\nSend the message unencrypted?"), Translate("OTR Warning"), MB_ICONWARNING | MB_YESNO) == IDYES) + return CallService(MS_PROTO_CHAINSEND, wParam, lParam); + else + return 1; + } + */ + //MessageBox(0, "Send message - passing through OTR library", "msg", MB_OK); + err = otrl_message_sending(otr_user_state, &ops, ccs->hContact, MODULE, proto, username, oldmessage_utf, 0, &newmessage, add_appdata, ccs->hContact); + lib_cs_unlock(); + free(oldmessage_utf); + if(err) { + // failure to encrypt message! + return 1; + } + + if(newmessage) { + //MessageBox(0, "Send message - message encoded - decoding UTF-8", "msg", MB_OK); + // decode utf8 into unicode message + wchar_t *temp; + char *text; + + // forward message + int size = MultiByteToWideChar(CP_UTF8, 0, (const char *)newmessage, -1, 0, 0); + temp = (wchar_t *)malloc(size * sizeof(wchar_t)); + if(!temp) return 1; + MultiByteToWideChar(CP_UTF8, 0, (const char *)newmessage, -1, temp, size); + + size = WideCharToMultiByte(code_page, 0, temp, -1, 0, 0, 0, 0); + text = (char *)malloc(size); + if(!text) { + free(temp); + return 1; + } + WideCharToMultiByte(code_page, 0, temp, -1, text, size, 0, 0); + + lib_cs_lock(); + otrl_message_free(newmessage); + lib_cs_unlock(); + + int tMsgBodyLen = strlen(text); + int tRealBodyLen = wcslen(temp); + + int tMsgBufLen = (tMsgBodyLen+1) * sizeof(char) + (tRealBodyLen+1)*sizeof( wchar_t ); + char* tMsgBuf = ( char* )malloc( tMsgBufLen ); + if(!tMsgBuf) { + free(temp); + free(text); + return 1; + } + + char* p = tMsgBuf; + + strcpy(p, text); + p += (tMsgBodyLen+1); + free(text); + + WPARAM old_flags = ccs->wParam; + if ( tRealBodyLen != 0 ) { + wcscpy((wchar_t *)p, temp); + ccs->wParam |= PREF_UNICODE; + } + free(temp); + //MessageBox(0, "Send message - forwarding message", "msg", MB_OK); + // forward new message + ccs->lParam = (LPARAM)tMsgBuf; + int ret = CallService(MS_PROTO_CHAINSEND, wParam, lParam); + // free decrypted message and conversion buffer + free(tMsgBuf); + + // restore state of chain message, for freeing, db, etc etc. + ccs->wParam = old_flags; + //ccs->szProtoService=PSS_MESSAGE"W"; + ccs->lParam = (LPARAM)oldmessage; + + return ret; + } //else + //MessageBox(0, "Send message - no action", "msg", MB_OK); + + return CallService(MS_PROTO_CHAINSEND, wParam, lParam); +} + +int OTRSendMessageW(WPARAM wParam, LPARAM lParam){ + if(!lParam) return 0; + + CCSDATA *ccs = (CCSDATA *) lParam; + ccs->wParam |= PREF_UNICODE; + + return OTRSendMessage(wParam, lParam); +} + +/* +#define MESSAGE_PREFIX "(OTR) " +#define MESSAGE_PREFIXW L"(OTR) " +#define MESSAGE_PREFIX_LEN 6 +*/ + +int RecvMessage(WPARAM wParam,LPARAM lParam){ + CCSDATA *ccs = (CCSDATA *) lParam; + PROTORECVEVENT *pre = (PROTORECVEVENT *) ccs->lParam; + + char MESSAGE_PREFIX[64]; + wchar_t MESSAGE_PREFIXW[64]; + MultiByteToWideChar(CP_UTF8, 0, options.prefix, -1, MESSAGE_PREFIXW, 64); + WideCharToMultiByte(code_page, 0, MESSAGE_PREFIXW, -1, MESSAGE_PREFIX, 64, 0, 0); + + int ignore_message; + char *newmessage = 0; + + char *oldmessage = pre->szMessage; + char *oldmessage_utf = 0; + OtrlTLV *tlvs = 0; + OtrlTLV *tlv = 0; + + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ccs->hContact, 0); + if(proto && strcmp(proto, "MetaContacts") == 0) // bypass for metacontacts + return CallService(MS_PROTO_CHAINRECV, wParam, lParam); + + if(strlen(oldmessage) > INLINE_PREFIX_LEN && strncmp(oldmessage, INLINE_PREFIX, INLINE_PREFIX_LEN) == 0) // bypass for our inline messages + return CallService(MS_PROTO_CHAINRECV, wParam, lParam); + + char *uname = (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)ccs->hContact, 0); + if(!proto || !uname) return 1; // error + + // convert oldmessage to utf-8 + if(ccs->wParam & PREF_UNICODE) { + int size = WideCharToMultiByte(CP_UTF8, 0, (wchar_t *)&oldmessage[strlen(oldmessage)+1], -1, 0, 0, 0, 0); + oldmessage_utf = (char *)malloc(size * sizeof(char)); + if(!oldmessage_utf) return 1; + WideCharToMultiByte(CP_UTF8, 0, (wchar_t *)&oldmessage[strlen(oldmessage)+1], -1, oldmessage_utf, size, 0, 0); + } else { + wchar_t *temp = 0; + int size_temp, size; + size_temp = MultiByteToWideChar(code_page, 0, oldmessage, -1, 0, 0); + temp = (wchar_t *)malloc(size_temp * sizeof(wchar_t)); + if(!temp) return 1; + MultiByteToWideChar(code_page, 0, oldmessage, -1, temp, size_temp); + + size = WideCharToMultiByte(CP_UTF8, 0, temp, -1, 0, 0, 0, 0); + oldmessage_utf = (char *)malloc(size * sizeof(char)); + if(!oldmessage_utf) { + free(temp); + return 1; + } + WideCharToMultiByte(CP_UTF8, 0, temp, -1, oldmessage_utf, size, 0, 0); + + free(temp); + } + + lib_cs_lock(); + ignore_message = otrl_message_receiving(otr_user_state, &ops, ccs->hContact, MODULE, proto, uname, oldmessage_utf, &newmessage, &tlvs, add_appdata, ccs->hContact); + lib_cs_unlock(); + free(oldmessage_utf); + + tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED); + if (tlv) { + lib_cs_lock(); + otrl_message_disconnect(otr_user_state, &ops, 0, MODULE, proto, uname); + lib_cs_unlock(); + //MessageBox(0, Translate("User ended encrypted session"), Translate("OTR Information"), MB_OK); + char buff[512]; + mir_snprintf(buff, 512, Translate("User '%s' ended encrypted session"), uname); + ShowPopup(Translate("OTR Information"), buff, 0); + if(tlvs) otrl_tlv_free(tlvs); // do we need these? (can be used to tell that the other side has terminated encrypted connection) + + SetEncryptionStatus(ccs->hContact, false); + + return 1; + } + + if(tlvs) otrl_tlv_free(tlvs); // do we need these? (can be used to tell that the other side has terminated encrypted connection) + + if(ignore_message) // internal OTR library message - ignore it + //if(ignore_message == 1) // internal OTR library message - ignore it + return 1; + + if(newmessage) { + // user should receive newmessage instead of message + + // decode utf8 into unicode message + wchar_t *temp; + char *text; + + int size = MultiByteToWideChar(CP_UTF8, 0, (const char *)newmessage, -1, 0, 0); + temp = (wchar_t *)malloc(size * sizeof(wchar_t)); + if(!temp) return 1; + MultiByteToWideChar(CP_UTF8, 0, (const char *)newmessage, -1, temp, size); + + size = WideCharToMultiByte(code_page, 0, temp, -1, 0, 0, 0, 0); + text = (char *)malloc(size); + if(!text) { + free(temp); + return 1; + } + WideCharToMultiByte(code_page, 0, temp, -1, text, size, 0, 0); + + lib_cs_lock(); + otrl_message_free(newmessage); + lib_cs_unlock(); + + int tMsgBodyLen = strlen(text); + int tRealBodyLen = wcslen(temp); + + // it may not be encrypted however (e.g. tagged plaintext with tags stripped) + ConnContext *context = otrl_context_find(otr_user_state, uname, MODULE, proto, FALSE, 0, 0, 0); + bool encrypted = context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED; + + if(options.prefix_messages && encrypted) { + tMsgBodyLen += strlen(MESSAGE_PREFIX); + tRealBodyLen += wcslen(MESSAGE_PREFIXW); + } + + int tMsgBufLen = (tMsgBodyLen+1) * sizeof(char) + (tRealBodyLen+1)*sizeof( wchar_t ); + char* tMsgBuf = ( char* )malloc( tMsgBufLen ); + if(!tMsgBuf) { + free(temp); + free(text); + return 1; + } + + char* p = tMsgBuf; + + if(options.prefix_messages && encrypted) { + strcpy(p, MESSAGE_PREFIX); + strcat(p + strlen(MESSAGE_PREFIX), text); + } else + strcpy(p, text); + p += (tMsgBodyLen+1); + free(text); + + WPARAM old_flags = pre->flags; + if ( tRealBodyLen != 0 ) { + if(options.prefix_messages && encrypted) { + wcscpy((wchar_t *)p, MESSAGE_PREFIXW); + wcscat((wchar_t *)p + wcslen(MESSAGE_PREFIXW), temp ); + } else + wcscpy((wchar_t *)p, temp); + pre->flags |= PREF_UNICODE; + } + free(temp); + + pre->szMessage = tMsgBuf; + int ret = CallService(MS_PROTO_CHAINRECV, wParam, lParam); + // free decrypted message and conversion buffer + free(tMsgBuf); + + // restore old message, so it can be freed etc + pre->flags = old_flags; + pre->szMessage = oldmessage; + + return ret; + } + + return CallService(MS_PROTO_CHAINRECV, wParam, lParam); +} + +// add prefix to sent messages +int OnDatabaseEventPreAdd(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + DBEVENTINFO *dbei = (DBEVENTINFO *)lParam; + + if(!options.prefix_messages) + return 0; + + if ((dbei->eventType != EVENTTYPE_MESSAGE) || !(dbei->flags & DBEF_SENT)) + return 0; + + if(dbei->cbBlob == 0 || dbei->pBlob == 0) + return 0; // just to be safe + + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + char *uname = (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0); + + if(!proto || !uname || DBGetContactSettingByte(hContact, proto, "ChatRoom", 0) == 1) { + return 0; + } + + if(strcmp(proto, "MetaContacts") == 0) { + HANDLE hSubContact = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0); + if(!hSubContact) + return 0; + uname = (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hSubContact, 0); + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hSubContact, 0); + } + + ConnContext *context = otrl_context_find(otr_user_state, uname, MODULE, proto, FALSE, 0, 0, 0); + bool encrypted = context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED; + + if(!encrypted) return 0; + + char MESSAGE_PREFIX[64]; + wchar_t MESSAGE_PREFIXW[64]; + MultiByteToWideChar(CP_UTF8, 0, options.prefix, -1, MESSAGE_PREFIXW, 64); + WideCharToMultiByte(code_page, 0, MESSAGE_PREFIXW, -1, MESSAGE_PREFIX, 64, 0, 0); + + DBEVENTINFO my_dbei = *dbei; // copy the other event + + char *msg = (char *)dbei->pBlob; + wchar_t *msgw = 0; + unsigned int msglen = strlen(msg) + 1; + + if((msglen > strlen(MESSAGE_PREFIX) && strncmp(msg, MESSAGE_PREFIX, strlen(MESSAGE_PREFIX)) == 0) || (msglen > INLINE_PREFIX_LEN && strncmp(msg, INLINE_PREFIX, INLINE_PREFIX_LEN) == 0)) + return 0; // already contains prefix, or it's an inline message + + // here we detect the double-zero wide char zero terminator - in case of messages that are not unicode but have + // other data after the message (e.g. metacontact copied messages with source identifier) + bool dz = false; + for(unsigned int i = msglen; i < dbei->cbBlob; i++) { + if(msg[i] == 0 && msg[i - 1] == 0) { // safe since msglen + 1 above + dz = true; + break; + } + } + + // does blob contain unicode message? + if(msglen < dbei->cbBlob && dz) { + // yes + msgw = (wchar_t *)&msg[msglen]; + } else { + // no, convert to unciode (allocate stack memory); + int size = MultiByteToWideChar(code_page, 0, (char *) msg, -1, 0, 0); + msgw = (wchar_t *) _alloca(sizeof(wchar_t) * size); + MultiByteToWideChar(code_page, 0, msg, -1, msgw, size); + } + + int tMsgBodyLen = strlen(msg) + strlen(MESSAGE_PREFIX); + int tRealBodyLen = wcslen(msgw) + wcslen(MESSAGE_PREFIXW); + + int tMsgBufLen = (tMsgBodyLen+1) * sizeof(char) + (tRealBodyLen+1)*sizeof( wchar_t ); + char* tMsgBuf = ( char* )_alloca( tMsgBufLen ); + + char* p = tMsgBuf; + + strcpy(p, MESSAGE_PREFIX); + strcat(p + strlen(MESSAGE_PREFIX), msg); + p += (tMsgBodyLen+1); + + if ( tRealBodyLen != 0 ) { + wcscpy((wchar_t *)p, MESSAGE_PREFIXW); + wcscat((wchar_t *)p + wcslen(MESSAGE_PREFIXW), msgw); + } + + my_dbei.pBlob = (BYTE *)tMsgBuf; + my_dbei.cbBlob = tMsgBufLen; + + CallService(MS_DB_EVENT_ADD, (WPARAM)hContact, (LPARAM)&my_dbei); + + // stop original event from being added + return 1; +} + + +int OnDatabaseEventAdd(WPARAM wParam, LPARAM lParam) { + // DISABLED - we should only remove sent events and received events when they are marked read - e.g. NoHistory plugin + /* + if(!options.delete_history) return 0; + + // remove the event - hopefully SRMM has received the event by now (since we hooked this in 'modules loaded')! + HANDLE hContact = (HANDLE)wParam, hDbEvent = (HANDLE)lParam; + + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + char *uname = (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0); + + if(!proto || !uname || DBGetContactSettingByte(hContact, proto, "ChatRoom", 0) == 1) { + return 0; + } + + if(strcmp(proto, "MetaContacts") == 0) { + HANDLE hSubContact = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0); + if(!hSubContact) + return 0; + uname = (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hSubContact, 0); + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hSubContact, 0); + } + + ConnContext *context = otrl_context_find(otr_user_state, uname, MODULE, proto, FALSE, 0, 0, 0); + bool encrypted = context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED; + + if(encrypted) + CallService(MS_DB_EVENT_DELETE, (WPARAM)hContact, (LPARAM)hDbEvent); + + // continuing processing of this event could cause trouble + return 1; + */ + + return 0; +} + +//////////////////////////////// +///////// Menu Services +/////////////////////// +int StartOTR(WPARAM wParam, LPARAM lParam) { + // prevent this filter from acting on injeceted messages for metas, when they are passed though the subcontact's proto send chain + HANDLE hContact = (HANDLE)wParam, hSub; + if(ServiceExists(MS_MC_GETMOSTONLINECONTACT) && (hSub = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0)) != 0) { + hContact = hSub; + } + + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + char *uname = (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0); + if(!proto || !uname) return 1; // error + + WORD pol = DBGetContactSettingWord(hContact, MODULE, "Policy", CONTACT_DEFAULT_POLICY); + if(pol == CONTACT_DEFAULT_POLICY) pol = options.default_policy | OTRL_POLICY_NOHTML; + + lib_cs_lock(); + char *msg = otrl_proto_default_query_msg(MODULE, pol); + otr_gui_inject_message(hContact, MODULE, proto, uname, msg ? msg : "?OTRv2?"); + lib_cs_unlock(); + free(msg); + + return 0; +} + +int StopOTR(WPARAM wParam, LPARAM lParam) { + + // prevent this filter from acting on injeceted messages for metas, when they are passed though the subcontact's proto send chain + HANDLE hContact = (HANDLE)wParam, hSub; + if(ServiceExists(MS_MC_GETMOSTONLINECONTACT) && (hSub = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0)) != 0) { + hContact = hSub; + } + + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + char *uname = (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0); + if(!proto || !uname) return 1; // error + + lib_cs_lock(); + otrl_message_disconnect(otr_user_state, &ops, hContact, MODULE, proto, uname); + lib_cs_unlock(); + + SetEncryptionStatus(hContact, false); + + return 0; +} + +/////////////////////////////////////////////// +/////// Plugin init and deinit +//////////////////////////////////////////////// + + +int WindowEvent(WPARAM wParam, LPARAM lParam) { + MessageWindowEventData *mwd = (MessageWindowEventData *)lParam; + if(mwd->uType != MSG_WINDOW_EVT_OPEN) return 0; + if(!ServiceExists(MS_MSG_MODIFYICON)) return 0; + + if(!CallService(MS_PROTO_ISPROTOONCONTACT, (WPARAM)mwd->hContact, (LPARAM)MODULE)) + return 0; + + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)mwd->hContact, 0); + char *uname = (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)mwd->hContact, 0); + if(!proto || !uname) return 0; // error - just bail + + lib_cs_lock(); + ConnContext *context = otrl_context_find(otr_user_state, uname, MODULE, proto, FALSE, 0, 0, 0); + + bool encrypted = (context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED); + lib_cs_unlock(); + + SetEncryptionStatus(mwd->hContact, encrypted); + + return 0; +} + +int IconPressed(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + StatusIconClickData *sicd = (StatusIconClickData *)lParam; + if(sicd->cbSize < (int)sizeof(StatusIconClickData)) + return 0; + + if(sicd->flags & MBCF_RIGHTBUTTON) return 0; // ignore right clicks + + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + if(proto && DBGetContactSettingByte(hContact, proto, "ChatRoom", 0)) + return 0; + + + if(strcmp(sicd->szModule, MODULE) == 0) { + if(DBGetContactSettingByte(hContact, MODULE, "Encrypted", 0) == 1) + StopOTR((WPARAM)hContact, 0); + else + StartOTR((WPARAM)hContact, 0); + } + + return 0; +} + +int ModulesLoaded(WPARAM wParam, LPARAM lParam) { + 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); + + update.szUpdateURL = UPDATER_AUTOREGISTER; + + // 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://www.scottellis.com.au/miranda_plugins/otr.zip"; + update.szBetaVersionURL = "http://www.scottellis.com.au/miranda_plugins/ver_otr.html"; + update.pbBetaVersionPrefix = (BYTE *)"OTR (Off The Record) encryption plugin, version "; + + update.cpbBetaVersionPrefix = strlen((char *)update.pbBetaVersionPrefix); + + CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update); + } + + InitUtils(); + InitMenu(); + + // filter added db events, so that we can add the prefix if the option is enabled + // (done here rather than in Load, so that MessageNotify and other such plugins can hook first - this hooks + // later, so it's at the start of the event chain, and can replace db events without e.g. double popups) + hEventDbEventAddedFilter = HookEvent(ME_DB_EVENT_FILTER_ADD, OnDatabaseEventPreAdd); + + // hooked so we can remove OFF THE RECORD items from the database + hEventDbEventAdded = HookEvent(ME_DB_EVENT_ADDED, OnDatabaseEventAdd); + + // add icon to srmm status icons + if(ServiceExists(MS_MSG_ADDICON)) { + StatusIconData sid = {0}; + sid.cbSize = sizeof(sid); + sid.szModule = MODULE; + sid.dwId = 0; + sid.hIcon = hLockIcon; + sid.hIconDisabled = hUnlockIcon; + sid.flags = MBF_DISABLED; + sid.szTooltip = Translate("OTR Encryption Status"); + CallService(MS_MSG_ADDICON, 0, (LPARAM)&sid); + + // hook the window events so that we can can change the status of the icon + hEventWindow = HookEvent(ME_MSG_WINDOWEVENT, WindowEvent); + hEventIconPressed = HookEvent(ME_MSG_ICONPRESSED, IconPressed); + } + + + return 0; +} + +int NewContact(WPARAM wParam, LPARAM lParam) { + // add filter + HANDLE hContact = (HANDLE)wParam; + CallService( MS_PROTO_ADDTOCONTACT, ( WPARAM )hContact, ( LPARAM )MODULE ); + + return 0; +} + +extern "C" DLLIMPORT int Load(PLUGINLINK *link) { + pluginLink=link; + + DuplicateHandle( GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &mainThread, THREAD_SET_CONTEXT, FALSE, 0 ); + mainThreadId = GetCurrentThreadId(); + + InitializeCriticalSection(&lib_cs); + + INITCOMMONCONTROLSEX icex; + // Ensure that the common control DLL is loaded (for listview) + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_LISTVIEW_CLASSES; + InitCommonControlsEx(&icex); + + if(ServiceExists(MS_DB_SETSETTINGRESIDENT)) { // 0.6+ + CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)(MODULE "/Encrypted")); + } + ////////////// + /// create secure data filenames, prefixed with profile name (minus extention) and an underscore + char mod_fname[MAX_PATH]; + if(CallService(MS_DB_GETPROFILEPATH, (WPARAM)MAX_PATH, (LPARAM)mod_fname)) { + GetModuleFileName(0, mod_fname, MAX_PATH); + char *p = strrchr(mod_fname, '\\'); + if(p) *p = 0; + } + + strcat(mod_fname, "\\OTR_data"); + CreatePath(mod_fname); + strcat(mod_fname, "\\"); + + strcpy(fingerprint_store_filename, mod_fname); + strcpy(private_key_filename, mod_fname); + + char mod_prefix[128]; + CallService(MS_DB_GETPROFILENAME, 128, (LPARAM)mod_prefix); + char *p = strrchr(mod_prefix, '.'); // cut off extention if present + if(p) *p = 0; + + strcat(fingerprint_store_filename, mod_prefix); + strcat(private_key_filename, mod_prefix); + strcat(fingerprint_store_filename, "_"); + strcat(private_key_filename, "_"); + + strcat(fingerprint_store_filename, FINGERPRINT_STORE_FILENAME); + strcat(private_key_filename, PRIVATE_KEY_FILENAME); + ////////////// + /////// init OTR // no need to lock critical section here - only one thread + OTRL_INIT; + otr_user_state = otrl_userstate_create(); + otrl_privkey_read(otr_user_state, private_key_filename); + otrl_privkey_read_fingerprints(otr_user_state, fingerprint_store_filename, 0, 0); + + ///////////// + ////// init plugin + PROTOCOLDESCRIPTOR pd = {0}; + pd.cbSize = sizeof(pd); + pd.szName = MODULE; + pd.type = PROTOTYPE_ENCRYPTION; + CallService(MS_PROTO_REGISTERMODULE,0,(LPARAM)&pd); + + // add us as a filter to all contacts + HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + while ( hContact != NULL ) { + CallService( MS_PROTO_ADDTOCONTACT, ( WPARAM )hContact, ( LPARAM )MODULE ); + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } + HookEvent(ME_DB_CONTACT_ADDED, NewContact); + + // create our services + CreateProtoServiceFunction(MODULE, PSS_MESSAGE, OTRSendMessage); + CreateProtoServiceFunction(MODULE, PSS_MESSAGE"W", OTRSendMessageW); + CreateProtoServiceFunction(MODULE, PSR_MESSAGE, RecvMessage); + + CreateServiceFunction(MS_OTR_MENUSTART, StartOTR); + CreateServiceFunction(MS_OTR_MENUSTOP, StopOTR); + + // init options + LoadOptions(); + HookEvent(ME_OPT_INITIALISE, OptInit); + + if(ServiceExists(MS_LANGPACK_GETCODEPAGE)) + code_page = CallService(MS_LANGPACK_GETCODEPAGE, 0, 0); + + // hook modules loaded for updater support + HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded); + + // hook setting changed to monitor status + HookEvent(ME_DB_CONTACT_SETTINGCHANGED, SettingChanged); + + return 0; +} + +extern "C" DLLIMPORT int Unload(void) { + UnhookEvent(hEventWindow); + UnhookEvent(hEventDbEventAddedFilter); + UnhookEvent(hEventDbEventAdded); + + DeinitMenu(); + DeinitUtils(); + + lib_cs_lock(); + otrl_userstate_free(otr_user_state); + lib_cs_unlock(); + + DeleteCriticalSection(&lib_cs); + return 0; +} diff --git a/otr/libotr.def b/otr/libotr.def new file mode 100644 index 0000000..74ac50a --- /dev/null +++ b/otr/libotr.def @@ -0,0 +1,5 @@ +; c:\Dev-Cpp\Bin\dlltool --base-file C:\DOCUME~1\sje\LOCALS~1\Temp/cca03408.base --output-exp otr.exp --dllname otr.dll --output-def libotr.def --exclude-symbol=DllMainCRTStartup@12 --def C:\DOCUME~1\sje\LOCALS~1\Temp/cca03408.def --output-lib libotr.a +EXPORTS + MirandaPluginInfo @ 1 + Unload @ 2 + Load @ 3 diff --git a/otr/menu.cpp b/otr/menu.cpp new file mode 100644 index 0000000..88c385d --- /dev/null +++ b/otr/menu.cpp @@ -0,0 +1,102 @@ +#include "common.h" +#include "menu.h" +#include "options.h" +#include "utils.h" + +HANDLE hMenuBuildEvent, hStartItem, hStopItem; + +void FixMenuIcons() { + // fix menu icons + CLISTMENUITEM menu; + ZeroMemory(&menu,sizeof(menu)); + menu.cbSize=sizeof(menu); + menu.flags = CMIM_ICON; + + menu.hIcon = hLockIcon; + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hStartItem, (LPARAM)&menu); + menu.hIcon = hUnlockIcon; + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hStopItem, (LPARAM)&menu); +} + +int PrebuildContactMenu(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + + CLISTMENUITEM mi = {0}; + mi.cbSize = sizeof(mi); + mi.flags = CMIM_FLAGS | CMIF_NOTOFFLINE; + + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + char *uname = (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0); + WORD pol = CONTACT_DEFAULT_POLICY; + + if(!proto || !uname || DBGetContactSettingByte(hContact, proto, "ChatRoom", 0) == 1) { + goto hide_all; + } + + if(strcmp(proto, "MetaContacts") == 0) { + // make menu act as per most online subcontact + hContact = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0); + if(!hContact) + goto hide_all; + uname = (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0); + proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + } + + pol = DBGetContactSettingWord(hContact, MODULE, "Policy", CONTACT_DEFAULT_POLICY); + if(pol == CONTACT_DEFAULT_POLICY) pol = options.default_policy; + + if(pol == OTRL_POLICY_NEVER || pol == OTRL_POLICY_ALWAYS) { + goto hide_all; + } + + { + ConnContext *context = otrl_context_find(otr_user_state, uname, MODULE, proto, FALSE, 0, 0, 0); + bool encrypted = context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED; + if(encrypted) { + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hStopItem, (LPARAM)&mi); + mi.flags |= CMIF_HIDDEN; + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hStartItem, (LPARAM)&mi); + } else { + if(pol == OTRL_POLICY_MANUAL_MOD) { + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hStartItem, (LPARAM)&mi); + mi.flags |= CMIF_HIDDEN; + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hStopItem, (LPARAM)&mi); + } else { // should only be 'opportunistic' + goto hide_all; + } + } + } + + return 0; + +hide_all: + mi.flags |= CMIF_HIDDEN; + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hStopItem, (LPARAM)&mi); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hStartItem, (LPARAM)&mi); + return 0; +} + +void InitMenu() { + CLISTMENUITEM mi = {0}; + mi.cbSize = sizeof(mi); + mi.flags = CMIF_NOTOFFLINE; + mi.position = -400000; + + mi.pszName = Translate("Stop OTR"); + mi.pszService = MS_OTR_MENUSTOP; + mi.hIcon = hUnlockIcon; + + hStopItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi); + + mi.pszName = Translate("Start OTR"); + mi.pszService = MS_OTR_MENUSTART; + mi.hIcon = hLockIcon; + + hStartItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi); + + hMenuBuildEvent = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PrebuildContactMenu); + +} + +void DeinitMenu() { +} diff --git a/otr/menu.h b/otr/menu.h new file mode 100644 index 0000000..bd1c190 --- /dev/null +++ b/otr/menu.h @@ -0,0 +1,9 @@ +#ifndef _MENU_INC +#define _MENU_INC + +void FixMenuIcons(); + +void InitMenu(); +void DeinitMenu(); + +#endif diff --git a/otr/options.cpp b/otr/options.cpp new file mode 100644 index 0000000..4e81895 --- /dev/null +++ b/otr/options.cpp @@ -0,0 +1,418 @@ +#include "common.h" +#include "options.h" + +Options options; + +#define WMU_REFRESHLIST (WM_USER + 241) +#define CONTACT_DEFAULT_POLICY 0xFFFF + +#include +typedef std::map ContactPolicyMap; + +void LoadOptions() { + options.default_policy = DBGetContactSettingWord(0, MODULE, "DefaultPolicy", OTRL_POLICY_OPPORTUNISTIC); + // deal with changed flags in proto.h and new interpretation of 'manual' mode (see common.h) + switch(options.default_policy) { + case OTRL_POLICY_MANUAL: + options.default_policy = OTRL_POLICY_MANUAL_MOD; + break; + case OTRL_POLICY_OPPORTUNISTIC: + case OTRL_POLICY_MANUAL_MOD: + case OTRL_POLICY_ALWAYS: + case OTRL_POLICY_NEVER: + break; + default: + options.default_policy = OTRL_POLICY_OPPORTUNISTIC; + break; + } + if(options.default_policy == OTRL_POLICY_MANUAL) + options.default_policy = OTRL_POLICY_MANUAL_MOD; + options.err_method = (ErrorDisplay)DBGetContactSettingWord(0, MODULE, "ErrorDisplay", ED_POP); + options.prefix_messages = (DBGetContactSettingByte(0, MODULE, "PrefixMessages", 0) == 1); + options.msg_inline = (DBGetContactSettingByte(0, MODULE, "MsgInline", 0) == 1); + + DBVARIANT dbv; + if(!DBGetContactSetting(0, MODULE, "Prefix", &dbv)) { + strncpy(options.prefix, dbv.pszVal, 64); + options.prefix[63] = 0; + DBFreeVariant(&dbv); + } else + strcpy(options.prefix, "OTR: "); + + options.delete_history = (DBGetContactSettingByte(0, MODULE, "DeleteHistory", 1) == 1); +} + +void SaveOptions(ContactPolicyMap *contact_policies) { + DBWriteContactSettingWord(0, MODULE, "DefaultPolicy", options.default_policy); + DBWriteContactSettingWord(0, MODULE, "ErrorDisplay", (int)options.err_method); + DBWriteContactSettingByte(0, MODULE, "PrefixMessages", options.prefix_messages ? 1 : 0); + DBWriteContactSettingByte(0, MODULE, "MsgInline", options.msg_inline ? 1 : 0); + + for(ContactPolicyMap::iterator i = contact_policies->begin(); i != contact_policies->end(); i++) { + DBWriteContactSettingWord(i->first, MODULE, "Policy", i->second); + } + + DBWriteContactSettingStringUtf(0, MODULE, "Prefix", options.prefix); + + DBWriteContactSettingByte(0, MODULE, "DeleteHistory", options.delete_history ? 1 : 0); +} + +INT_PTR CALLBACK DlgProcOpts1(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { + + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + { + // set contact to policy map + ContactPolicyMap *contact_policies = new ContactPolicyMap(); + SetWindowLong(hwndDlg, GWL_USERDATA, (LONG)contact_policies); + HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + char *proto; + while ( hContact != NULL ) { + proto = ( char* )CallService( MS_PROTO_GETCONTACTBASEPROTO, ( WPARAM )hContact,0 ); + if(proto && DBGetContactSettingByte(hContact, proto, "ChatRoom", 0) == 0 && CallService(MS_PROTO_ISPROTOONCONTACT, (WPARAM)hContact, (LPARAM)MODULE)) { + WORD pol = DBGetContactSettingWord(hContact, MODULE, "Policy", CONTACT_DEFAULT_POLICY); + // deal with changed flags in proto.h and new interpretation of 'manual' mode (see common.h) + switch(pol) { + case OTRL_POLICY_MANUAL: + pol = OTRL_POLICY_MANUAL_MOD; + break; + case OTRL_POLICY_OPPORTUNISTIC: + case OTRL_POLICY_MANUAL_MOD: + case OTRL_POLICY_ALWAYS: + case OTRL_POLICY_NEVER: + break; + default: + pol = CONTACT_DEFAULT_POLICY; + break; + } + + contact_policies->operator[](hContact) = pol; + } + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } + } + switch(options.default_policy) { + case OTRL_POLICY_OPPORTUNISTIC: + CheckDlgButton(hwndDlg, IDC_RAD_OPP, TRUE); + break; + case OTRL_POLICY_MANUAL_MOD: + CheckDlgButton(hwndDlg, IDC_RAD_MANUAL, TRUE); + break; + case OTRL_POLICY_ALWAYS: + CheckDlgButton(hwndDlg, IDC_RAD_ALWAYS, TRUE); + break; + case OTRL_POLICY_NEVER: + CheckDlgButton(hwndDlg, IDC_RAD_NEVER, TRUE); + break; + } + + SendMessage(GetDlgItem(hwndDlg, IDC_CLIST),LVM_SETEXTENDEDLISTVIEWSTYLE, 0,LVS_EX_FULLROWSELECT);// | LVS_EX_CHECKBOXES); + + { + // add list columns + 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 = Translate("Contact"); + lvc.cx = 150; // width of column in pixels + ListView_InsertColumn(GetDlgItem(hwndDlg, IDC_CLIST), 0, &lvc); + + lvc.iSubItem = 1; + lvc.pszText = Translate("Protocol"); + lvc.cx = 100; // width of column in pixels + ListView_InsertColumn(GetDlgItem(hwndDlg, IDC_CLIST), 1, &lvc); + + lvc.iSubItem = 2; + lvc.pszText = Translate("Policy"); + lvc.cx = 100; // width of column in pixels + ListView_InsertColumn(GetDlgItem(hwndDlg, IDC_CLIST), 2, &lvc); + } + SendMessage(hwndDlg, WMU_REFRESHLIST, 0, 0); + + // fill proto list box + { + int num_protocols; + PROTOCOLDESCRIPTOR **pppDesc; + + CallService(MS_PROTO_ENUMPROTOCOLS, (LPARAM)&num_protocols, (WPARAM)&pppDesc); + HWND hw = GetDlgItem(hwndDlg, IDC_LIST_PROTOS); + for(int i = 0; i < num_protocols; i++) { + if(pppDesc[i]->type == PROTOTYPE_PROTOCOL && strcmp(pppDesc[i]->szName, "MetaContacts") != 0 + && CallProtoService(pppDesc[i]->szName, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_IM == PF1_IM) + { + SendMessage(hw, LB_INSERTSTRING, (WPARAM)-1, (LPARAM)pppDesc[i]->szName); + } + } + } + + CheckDlgButton(hwndDlg, IDC_CHK_PREFIX, options.prefix_messages ? TRUE : FALSE); + CheckDlgButton(hwndDlg, IDC_CHK_MSGINLINE, options.msg_inline ? TRUE : FALSE); + + SetDlgItemText(hwndDlg, IDC_ED_PREFIX, options.prefix); + return TRUE; + + case WMU_REFRESHLIST: + //enumerate contacts, fill in list + { + ListView_DeleteAllItems(GetDlgItem(hwndDlg, IDC_CLIST)); + + LVITEM lvI = {0}; + + // 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 && DBGetContactSettingByte(hContact, proto, "ChatRoom", 0) == 0 && CallService(MS_PROTO_ISPROTOONCONTACT, (WPARAM)hContact, (LPARAM)MODULE) // ignore chatrooms + && (proto && strcmp(proto, "MetaContacts") != 0)) // and MetaContacts + { + lvI.iSubItem = 0; + lvI.lParam = (LPARAM)hContact; + lvI.pszText = (char *)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)hContact, 0); + lvI.iItem = ListView_InsertItem(GetDlgItem(hwndDlg, IDC_CLIST), &lvI); + + lvI.iSubItem = 1; + lvI.pszText = LPSTR_TEXTCALLBACK; + ListView_SetItem(GetDlgItem(hwndDlg, IDC_CLIST), &lvI); + + lvI.iSubItem = 2; + lvI.pszText = LPSTR_TEXTCALLBACK; + ListView_SetItem(GetDlgItem(hwndDlg, IDC_CLIST), &lvI); + } + + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } + } + + return TRUE; + case WM_COMMAND: + if ( HIWORD( wParam ) == EN_CHANGE && ( HWND )lParam == GetFocus()) { + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } else + if (HIWORD( wParam ) == LBN_SELCHANGE && LOWORD(wParam) == IDC_LIST_PROTOS) { + HWND hw = GetDlgItem(hwndDlg, IDC_LIST_PROTOS); + int sel = -1; + if((sel = SendMessage(hw, LB_GETCURSEL, 0, 0)) != -1) { + char proto[512]; + SendMessage(hw, LB_GETTEXT, (WPARAM)sel, (LPARAM)proto); + + char fingerprint[45]; + if(otrl_privkey_fingerprint(otr_user_state, fingerprint, MODULE, proto)) + SetDlgItemText(hwndDlg, IDC_ED_FINGERPRINT, fingerprint); + else + SetDlgItemText(hwndDlg, IDC_ED_FINGERPRINT, ""); + + hw = GetDlgItem(hwndDlg, IDC_BUT_NEWKEY); + EnableWindow(hw, TRUE); + + return TRUE; + } else { + hw = GetDlgItem(hwndDlg, IDC_BUT_NEWKEY); + EnableWindow(hw, FALSE); + } + return FALSE; + } else + if ( HIWORD( wParam ) == BN_CLICKED ) { + switch( LOWORD( wParam )) { + case IDC_BUT_NEWKEY: + { + HWND hw = GetDlgItem(hwndDlg, IDC_LIST_PROTOS); + int sel = -1; + if((sel = SendMessage(hw, LB_GETCURSEL, 0, 0)) != -1) { + char proto[512]; + SendMessage(hw, LB_GETTEXT, (WPARAM)sel, (LPARAM)proto); + + SetDlgItemText(hwndDlg, IDC_ED_FINGERPRINT, Translate("Generating new key...please wait")); + otrl_privkey_generate(otr_user_state, private_key_filename, MODULE, proto); + + char fingerprint[45]; + if(otrl_privkey_fingerprint(otr_user_state, fingerprint, MODULE, proto)) + SetDlgItemText(hwndDlg, IDC_ED_FINGERPRINT, fingerprint); + else + SetDlgItemText(hwndDlg, IDC_ED_FINGERPRINT, ""); + + return TRUE; + } + } + + break; + + case IDC_RAD_OPP: + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 ); + return TRUE; + case IDC_RAD_MANUAL: + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 ); + return TRUE; + case IDC_RAD_ALWAYS: + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 ); + return TRUE; + case IDC_RAD_NEVER: + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 ); + return TRUE; + case IDC_CHK_PREFIX: + case IDC_CHK_MSGINLINE: + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 ); + return TRUE; + } + } + break; + case WM_NOTIFY: + if(((LPNMHDR) lParam)->hwndFrom == GetDlgItem(hwndDlg, IDC_CLIST)) { + switch (((LPNMHDR) lParam)->code) { + case NM_CLICK: + if (((LPNMLISTVIEW)lParam)->iSubItem == 2) { + LVITEM lvi; + lvi.mask = LVIF_PARAM; + lvi.iItem = ((LPNMLISTVIEW)lParam)->iItem; + lvi.iSubItem = 0; + SendMessage(GetDlgItem(hwndDlg, IDC_CLIST), LVM_GETITEM, 0, (LPARAM)&lvi); + + HANDLE hContact = (HANDLE)lvi.lParam; + ContactPolicyMap *contact_policies = (ContactPolicyMap *)GetWindowLong(hwndDlg, GWL_USERDATA); + ContactPolicyMap::iterator i = contact_policies->find(hContact); + WORD pol = CONTACT_DEFAULT_POLICY; + if(i != contact_policies->end()) + pol = i->second; + + switch(pol) { + case CONTACT_DEFAULT_POLICY: + pol = OTRL_POLICY_MANUAL_MOD; + break; + case OTRL_POLICY_OPPORTUNISTIC: + pol = OTRL_POLICY_ALWAYS; + break; + case OTRL_POLICY_MANUAL_MOD: + pol = OTRL_POLICY_OPPORTUNISTIC; + break; + case OTRL_POLICY_ALWAYS: + pol = OTRL_POLICY_NEVER; + break; + case OTRL_POLICY_NEVER: + pol = CONTACT_DEFAULT_POLICY; + break; + } + + contact_policies->operator[](hContact) = pol; + ListView_RedrawItems(GetDlgItem(hwndDlg, IDC_CLIST), lvi.iItem, lvi.iItem); + + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 ); + } + break; + case LVN_GETDISPINFO: + { + LVITEM lvi; + lvi.mask = LVIF_PARAM; + lvi.iItem = ((NMLVDISPINFO *)lParam)->item.iItem; + lvi.iSubItem = 0; + SendMessage(GetDlgItem(hwndDlg, IDC_CLIST), LVM_GETITEM, 0, (LPARAM)&lvi); + switch (((NMLVDISPINFO *)lParam)->item.iSubItem) { + case 0: + MessageBox(0, "LVN_GETDISPINFO (0)", "msg", MB_OK); + break; + case 1: + { + HANDLE hContact = (HANDLE)lvi.lParam; + ((NMLVDISPINFO *)lParam)->item.pszText = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + } + break; + case 2: + { + + char *text = 0; + HANDLE hContact = (HANDLE)lvi.lParam; + ContactPolicyMap *contact_policies = (ContactPolicyMap *)GetWindowLong(hwndDlg, GWL_USERDATA); + ContactPolicyMap::iterator i = contact_policies->find(hContact); + WORD pol = CONTACT_DEFAULT_POLICY; + if(i != contact_policies->end()) + pol = i->second; + switch(pol) { + case CONTACT_DEFAULT_POLICY: + text = Translate("Default"); + break; + case OTRL_POLICY_OPPORTUNISTIC: + text = Translate("Opportunistic"); + break; + case OTRL_POLICY_MANUAL_MOD: + text = Translate("Manual"); + break; + case OTRL_POLICY_ALWAYS: + text = Translate("Always"); + break; + case OTRL_POLICY_NEVER: + text = Translate("Never"); + break; + } + ((NMLVDISPINFO *)lParam)->item.pszText = text; + + } + break; + } + } + break; + } + + return FALSE; + } else + if (((LPNMHDR)lParam)->code == (unsigned)PSN_APPLY ) { + if(IsDlgButtonChecked(hwndDlg, IDC_RAD_OPP)) + options.default_policy = OTRL_POLICY_OPPORTUNISTIC; + else if(IsDlgButtonChecked(hwndDlg, IDC_RAD_MANUAL)) + options.default_policy = OTRL_POLICY_MANUAL_MOD; + else if(IsDlgButtonChecked(hwndDlg, IDC_RAD_ALWAYS)) + options.default_policy = OTRL_POLICY_ALWAYS; + else if(IsDlgButtonChecked(hwndDlg, IDC_RAD_NEVER)) + options.default_policy = OTRL_POLICY_NEVER; + + options.prefix_messages = IsDlgButtonChecked(hwndDlg, IDC_CHK_PREFIX) ? true : false; + options.msg_inline = IsDlgButtonChecked(hwndDlg, IDC_CHK_MSGINLINE) ? true : false; + GetDlgItemText(hwndDlg, IDC_ED_PREFIX, options.prefix, 64); + + ContactPolicyMap *contact_policies = (ContactPolicyMap *)GetWindowLong(hwndDlg, GWL_USERDATA); + + SaveOptions(contact_policies); + return TRUE; + } + break; + + case WM_DESTROY: + { + ContactPolicyMap *contact_policies = (ContactPolicyMap *)GetWindowLong(hwndDlg, GWL_USERDATA); + SetWindowLong(hwndDlg, GWL_USERDATA, 0); + delete contact_policies; + } + } + + return FALSE; +} + +int OptInit(WPARAM wParam, LPARAM lParam) { + OPTIONSDIALOGPAGE odp = { 0 }; +#define OPTIONPAGE_OLD_SIZE2 60 + //odp.cbSize = sizeof(odp); + odp.cbSize = OPTIONPAGE_OLD_SIZE2; + odp.position = 100; + odp.hInstance = hInst; + odp.pszTemplate = MAKEINTRESOURCE(IDD_OPT1); + odp.pszTitle = Translate(MODULE); + odp.pszGroup = Translate("Plugins"); + odp.flags = ODPF_BOLDGROUPS; + odp.nIDBottomSimpleControl = 0; + odp.pfnDlgProc = DlgProcOpts1; + CallService( MS_OPT_ADDPAGE, wParam,( LPARAM )&odp ); + + return 0; +} + +void OptDeinit() { +} diff --git a/otr/options.h b/otr/options.h new file mode 100644 index 0000000..475e566 --- /dev/null +++ b/otr/options.h @@ -0,0 +1,24 @@ +#ifndef _OPTIONS_INC +#define _OPTIONS_INC + +#define CONTACT_DEFAULT_POLICY 0xFFFF + +typedef enum {ED_POP, ED_BAL, ED_MB} ErrorDisplay; + +typedef struct { + OtrlPolicy default_policy; + ErrorDisplay err_method; + bool prefix_messages; + bool msg_inline; + char prefix[64]; + + bool delete_history; +} Options; + +extern Options options; + +int OptInit(WPARAM wParam, LPARAM lParam); + +void LoadOptions(); + +#endif diff --git a/otr/otr.layout b/otr/otr.layout new file mode 100644 index 0000000..40a374f --- /dev/null +++ b/otr/otr.layout @@ -0,0 +1,87 @@ +[Editor_0] +CursorCol=69 +CursorRow=479 +TopLine=451 +LeftChar=1 +Open=1 +Top=1 +[Editor_1] +CursorCol=19 +CursorRow=7 +TopLine=1 +LeftChar=1 +Open=0 +Top=0 +[Editor_2] +CursorCol=8 +CursorRow=52 +TopLine=14 +LeftChar=1 +Open=1 +Top=0 +[Editor_3] +CursorCol=11 +CursorRow=13 +TopLine=1 +LeftChar=1 +Open=1 +Top=0 +[Editor_4] +CursorCol=4 +CursorRow=219 +TopLine=216 +LeftChar=1 +Open=1 +Top=0 +[Editor_5] +CursorCol=54 +CursorRow=5 +TopLine=1 +LeftChar=1 +Open=0 +Top=0 +[Editor_6] +CursorCol=27 +CursorRow=10 +TopLine=1 +LeftChar=1 +Open=0 +Top=0 +[Editors] +Focused=0 +Order=2,10,9,3,4,0 +[Editor_7] +Open=0 +Top=0 +CursorCol=1 +CursorRow=6 +TopLine=1 +LeftChar=1 +[Editor_8] +Open=0 +Top=0 +CursorCol=1 +CursorRow=81 +TopLine=36 +LeftChar=1 +[Editor_9] +Open=1 +Top=0 +CursorCol=25 +CursorRow=72 +TopLine=58 +LeftChar=1 +[Editor_10] +Open=1 +Top=0 +CursorCol=1 +CursorRow=12 +TopLine=1 +LeftChar=1 +[Editor_11] +Open=0 +Top=0 +CursorCol=18 +CursorRow=12 +TopLine=1 +LeftChar=1 diff --git a/otr/otr.mdsp b/otr/otr.mdsp new file mode 100644 index 0000000..f09edad --- /dev/null +++ b/otr/otr.mdsp @@ -0,0 +1,108 @@ +[Project] +name=otr +type=2 +defaultConfig=0 + + +[Debug] +// compiler +workingDirectory= +arguments= +intermediateFilesDirectory=Debug +outputFilesDirectory=Debug +compilerPreprocessor=BUILDING_DLL +extraCompilerOptions= +compilerIncludeDirectory=c:\msys\local\include\libotr,..\..\include +noWarning=0 +defaultWarning=0 +allWarning=1 +extraWarning=0 +isoWarning=0 +warningsAsErrors=0 +debugType=1 +debugLevel=3 +exceptionEnabled=0 +runtimeTypeEnabled=0 +optimizeLevel=0 + +// linker +libraryPath= +outputFilename=..\..\bin\debug\plugins\otr.dll +libraries=otr, gcrypt, gpg-error, comctl32, shell32 +extraLinkerOptions=-shared +ignoreStartupFile=0 +ignoreDefaultLibs=0 +stripExecutableFile=0 + +// archive +extraArchiveOptions= + +//resource +resourcePreprocessor= +resourceIncludeDirectory= +extraResourceOptions= + +[Release] +// compiler +workingDirectory= +arguments= +intermediateFilesDirectory=Release +outputFilesDirectory=Release +compilerPreprocessor=BUILDING_DLL +extraCompilerOptions= +compilerIncludeDirectory=c:\msys\local\include\libotr,..\..\include +noWarning=0 +defaultWarning=0 +allWarning=1 +extraWarning=0 +isoWarning=0 +warningAsErrors=0 +debugType=0 +debugLevel=1 +exceptionEnabled=0 +runtimeTypeEnabled=0 +optimizeLevel=4 + +// linker +libraryPath= +outputFilename=..\..\bin\release\plugins\otr.dll +libraries=otr, gcrypt, gpg-error, comctl32, shell32 +extraLinkerOptions= +ignoreStartupFile=0 +ignoreDefaultLibs=0 +stripExecutableFile=1 + +// archive +extraArchiveOptions= + +//resource +resourcePreprocessor= +resourceIncludeDirectory= +extraResourceOptions= + +[Source] +1=dllmain.cpp +2=menu.cpp +3=options.cpp +4=utils.cpp +[Header] +1=common.h +2=dll.h +3=menu.h +4=options.h +5=otr_private.h +6=resource.h +7=utils.h +8=IcoLib.h +[Resource] +1=otr_private.rc +2=resource.rc +[Other] +[History] +libotr\libotr\src\context.h,0 +common.h,263 +options.h,0 +..\..\include\m_message.h,1556 +..\MetaContacts\meta_main.c,6362 +otr_private.h,149 +dllmain.cpp,35050 diff --git a/otr/otr_private.h b/otr/otr_private.h new file mode 100644 index 0000000..c6f9c2e --- /dev/null +++ b/otr/otr_private.h @@ -0,0 +1,24 @@ +#ifndef OTR_PRIVATE_H +#define OTR_PRIVATE_H + +/* VERSION DEFINITIONS */ +#define VER_MAJOR 0 +#define VER_MINOR 4 +#define VER_RELEASE 3 +#define VER_BUILD 0 + +#define __STRINGIZE(x) #x +#define VER_STRING __STRINGIZE( VER_MAJOR.VER_MINOR.VER_RELEASE.VER_BUILD ) + +#define DESC_STRING "OTR (Off-the-Record) plugin for Miranda IM" +#define COMPANY_NAME "" +#define FILE_VERSION VER_STRING +#define FILE_DESCRIPTION DESC_STRING +#define INTERNAL_NAME "" +#define LEGAL_COPYRIGHT "© Scott Ellis 2005" +#define LEGAL_TRADEMARKS "" +#define ORIGINAL_FILENAME "otr.dll" +#define PRODUCT_NAME DESC_STRING +#define PRODUCT_VERSION VER_STRING + +#endif /*OTR_PRIVATE_H*/ diff --git a/otr/otr_private.rc b/otr/otr_private.rc new file mode 100644 index 0000000..0f774cb --- /dev/null +++ b/otr/otr_private.rc @@ -0,0 +1,35 @@ +/* THIS FILE WILL BE OVERWRITTEN BY DEV-C++ */ +/* DO NOT EDIT! */ + +#include "otr_private.h" +#include "resource.rc" + +// +// TO CHANGE VERSION INFORMATION, EDIT PROJECT OPTIONS... +// +1 VERSIONINFO +FILEVERSION VER_MAJOR,VER_MINOR,VER_RELEASE,VER_BUILD +PRODUCTVERSION VER_MAJOR,VER_MINOR,VER_RELEASE,VER_BUILD +FILETYPE VFT_DLL +{ + BLOCK "StringFileInfo" + { + BLOCK "0C0904E4" + { + VALUE "CompanyName", COMPANY_NAME + VALUE "FileVersion", FILE_VERSION + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", LEGAL_COPYRIGHT + VALUE "LegalTrademarks", LEGAL_TRADEMARKS + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION + } + } + BLOCK "VarFileInfo" + { + VALUE "Translation", 0x0C09, 1252 + } +} + diff --git a/otr/resource.h b/otr/resource.h new file mode 100644 index 0000000..45e58dc --- /dev/null +++ b/otr/resource.h @@ -0,0 +1,20 @@ + +#define IDD_OPT1 101 +#define IDC_RAD_OPP 1002 +#define IDC_RAD_ALWAYS 1003 +#define IDC_RAD_NEVER 1004 +#define IDC_CLIST 1005 +#define IDC_RAD_MANUAL 1006 +#define IDC_LIST_PROTOS 1007 +#define IDC_ED_FINGERPRINT 1008 +#define IDC_BUT_NEWKEY 1009 +#define IDC_CHK_PREFIX 1010 +#define IDC_CHK_MSGINLINE 1011 +#define IDC_ED_PREFIX 1012 + +#define IDD_GENKEYNOTIFY 102 + +#define IDI_START 2000 +#define IDI_STOP 2001 + +#define IDC_STATIC -1 diff --git a/otr/resource.rc b/otr/resource.rc new file mode 100644 index 0000000..c52fe28 --- /dev/null +++ b/otr/resource.rc @@ -0,0 +1,111 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#define APSTUDIO_HIDDEN_SYMBOLS +#include "windows.h" +#undef APSTUDIO_HIDDEN_SYMBOLS +#include "resource.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 + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_START ICON "start.ico" +IDI_STOP ICON "stop.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_OPT1 DIALOGEX 0, 0, 288, 217 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +FONT 8, "MS Shell Dlg", 0, 0, 0x0 +BEGIN + CONTROL "List1",IDC_CLIST,"SysListView32",LVS_REPORT | LVS_SORTASCENDING | WS_BORDER | WS_TABSTOP,8,7,271,112 + GROUPBOX "Default Policy",IDC_STATIC,8,150,109,59 + CONTROL "Manual",IDC_RAD_MANUAL,"Button",BS_AUTORADIOBUTTON | WS_GROUP | WS_TABSTOP,16,160,95,10 + CONTROL "Opportunistic",IDC_RAD_OPP,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,16,172,95,10 + CONTROL "Always",IDC_RAD_ALWAYS,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,16,184,95,10 + CONTROL "Never",IDC_RAD_NEVER,"Button",BS_AUTORADIOBUTTON | WS_TABSTOP,16,195,95,10 + LISTBOX IDC_LIST_PROTOS,127,124,152,60,WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_ED_FINGERPRINT,127,185,152,12,ES_AUTOHSCROLL | ES_READONLY + PUSHBUTTON "New Private Key",IDC_BUT_NEWKEY,127,200,152,10,WS_DISABLED + CONTROL "Prefix:",IDC_CHK_PREFIX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,125,48,10 + CONTROL "Show start/stop inline",IDC_CHK_MSGINLINE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,137,110,10 + EDITTEXT IDC_ED_PREFIX,64,124,53,12,ES_AUTOHSCROLL +END + +IDD_GENKEYNOTIFY DIALOG 0, 0, 200, 80 +STYLE DS_SETFONT | DS_MODALFRAME | DS_SETFOREGROUND | DS_CENTER | WS_POPUP | WS_CHILD | WS_CAPTION | WS_SYSMENU | WS_GROUP +CAPTION "OTR Generating Private Key" +FONT 8, "MS Shell Dlg" +BEGIN + CTEXT "Generating new private key - please wait.",IDC_STATIC,10,35,180,10 +END + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resrc1.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#define APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""windows.h""\r\n" + "#undef APSTUDIO_HIDDEN_SYMBOLS\r\n" + "#include ""resource.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/otr/start.ico b/otr/start.ico new file mode 100644 index 0000000..d6abe72 Binary files /dev/null and b/otr/start.ico differ diff --git a/otr/stop.ico b/otr/stop.ico new file mode 100644 index 0000000..90b38e5 Binary files /dev/null and b/otr/stop.ico differ diff --git a/otr/utils.cpp b/otr/utils.cpp new file mode 100644 index 0000000..2950504 --- /dev/null +++ b/otr/utils.cpp @@ -0,0 +1,282 @@ +#include "common.h" +#include "utils.h" +#include "../../include/m_popup.h" +#include "options.h" +#include "menu.h" + +#include + +HICON hProtoIcon, hLockIcon, hUnlockIcon; +HANDLE hIcoLibIconsChanged = 0; + +LRESULT CALLBACK NullWindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) +{ + switch( message ) { + case WM_COMMAND: { + PUDeletePopUp( hWnd ); + break; + } + + case WM_CONTEXTMENU: + PUDeletePopUp( hWnd ); + break; + } + + return DefWindowProc(hWnd, message, wParam, lParam); +} + +void CALLBACK sttMainThreadCallback( ULONG dwParam ) +{ + POPUPDATAEX* ppd = ( POPUPDATAEX* )dwParam; + + 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 ); +} + +void ShowPopup( const char* line1, const char* line2, int timeout ) +{ + if(CallService(MS_SYSTEM_TERMINATED, 0, 0)) return; + + if ( !ServiceExists( MS_POPUP_ADDPOPUP )) { + char title[256]; + sprintf(title, "%s Message", MODULE); + + if(line1 && line2) { + char *message = new char[strlen(line1) + strlen(line2) + 2]; // newline and null terminator + sprintf(message, "%s\n%s", line1, line2); + MessageBox( NULL, message, title, MB_OK | MB_ICONINFORMATION ); + delete message; + } else if(line1) { + MessageBox( NULL, line1, title, MB_OK | MB_ICONINFORMATION ); + } else if(line2) { + MessageBox( NULL, line2, title, MB_OK | MB_ICONINFORMATION ); + } + return; + } + + POPUPDATAEX* ppd = ( POPUPDATAEX* )calloc( sizeof( POPUPDATAEX ), 1 ); + memset((void *)ppd, 0, sizeof(POPUPDATAEX)); + + ppd->lchContact = NULL; + ppd->lchIcon = hProtoIcon; + + if(line1 && line2) { + strcpy( ppd->lpzContactName, line1 ); + strcpy( ppd->lpzText, line2 ); + } else if(line1) strcpy( ppd->lpzText, line1 ); + else if(line2) strcpy( ppd->lpzText, line2 ); + + //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 = timeout; + + //ppd->lpzClass = POPUP_CLASS_DEFAULT; + + ppd->PluginWindowProc = ( WNDPROC )NullWindowProc; + ppd->PluginData = NULL; + + QueueUserAPC( sttMainThreadCallback , mainThread, ( ULONG )ppd ); +} + +void ShowWarning(char *msg) { + char 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; + + sprintf(buffer, "%s Warning", MODULE); + + + 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 = MODULE; + sn.szInfoTitle = buffer; + sn.szInfo = msg; + sn.dwInfoFlags = NIIF_WARNING; + sn.uTimeout = 10; + + CallService(MS_CLIST_SYSTRAY_NOTIFY, 0, (LPARAM)&sn); + } + + break; + } +} + +void ShowError(char *msg) { + char 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; + + sprintf(buffer, "%s Error", MODULE); + + + 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 = MODULE; + sn.szInfoTitle = buffer; + sn.szInfo = msg; + sn.dwInfoFlags = NIIF_ERROR; + sn.uTimeout = 10; + + CallService(MS_CLIST_SYSTRAY_NOTIFY, 0, (LPARAM)&sn); + } + + break; + } +} + +bool CreatePath(const char *path) { + if(!path) return false; + + char folder[MAX_PATH]; + strcpy(folder, path); + int i = 0; + + SetLastError(ERROR_SUCCESS); + char *p = &folder[i]; + while(folder[i]) { + p = strchr(p, _T('\\')); + if(p) { + i = p - folder; + p++; + + if(folder[i]) folder[i] = 0; + } else + i = strlen(folder); + + /* + // this fails when the user does not have permission to create intermediate folders + if(!CreateDirectory(folder, 0)) { + DWORD err = GetLastError(); + if(err != ERROR_ALREADY_EXISTS) { + //MessageBox(0, Translate("Could not create temporary folder."), Translate("Error"), MB_OK | MB_ICONERROR); + //return 1; + return false; + } + } + */ + CreateDirectory(folder, 0); + + folder[i] = path[i]; + } + + DWORD lerr = GetLastError(); + return (lerr == ERROR_SUCCESS || lerr == ERROR_ALREADY_EXISTS); + + /* + // this function seems to be unavailable in shell32.dll under NT4 + int ret = SHCreateDirectoryEx(0, path, 0); + return (ret == ERROR_SUCCESS) || (ret == ERROR_FILE_EXISTS) || (ret == ERROR_ALREADY_EXISTS); + */ +} + + +int ReloadIcons(WPARAM wParam, LPARAM lParam) { + hProtoIcon = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"otr_popups"); + hLockIcon = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"otr_secure"); + hUnlockIcon = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"otr_insecure"); + + if(ServiceExists(MS_MSG_MODIFYICON)) { + StatusIconData sid = {0}; + sid.cbSize = sizeof(sid); + sid.szModule = MODULE; + sid.dwId = 0; + sid.hIcon = hLockIcon; + sid.hIconDisabled = hUnlockIcon; + sid.flags = MBF_DISABLED; + sid.szTooltip = Translate("OTR Encryption Status"); + CallService(MS_MSG_MODIFYICON, 0, (LPARAM)&sid); + } + + FixMenuIcons(); + + return 0; +} + +void InitUtils() { + if(ServiceExists(MS_SKIN2_ADDICON)) { + SKINICONDESC2 sid; + + sid.cbSize = sizeof(SKINICONDESC2); + sid.pszSection = "OTR"; + sid.hDefaultIcon = 0; + + sid.pszDescription = Translate("Popups Icon"); + sid.pszName = "otr_popups"; + sid.pszDefaultFile = "otr.dll"; + sid.iDefaultIndex = 0; + sid.hDefaultIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_START), IMAGE_ICON, 0, 0, 0); + CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid); + + sid.pszDescription = Translate("Secure"); + sid.pszName = "otr_secure"; + sid.pszDefaultFile = "otr.dll"; + sid.iDefaultIndex = 1; + sid.hDefaultIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_START), IMAGE_ICON, 0, 0, 0);//LR_LOADTRANSPARENT | LR_LOADMAP3DCOLORS ); + CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid); + + sid.pszDescription = Translate("Insecure"); + sid.pszName = "otr_insecure"; + sid.pszDefaultFile = "otr.dll"; + sid.iDefaultIndex = 2; + sid.hDefaultIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_STOP), IMAGE_ICON, 0, 0, 0);//LR_LOADTRANSPARENT | LR_LOADMAP3DCOLORS ); + CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid); + + hProtoIcon = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"otr_popups"); + hLockIcon = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"otr_secure"); + hUnlockIcon = (HICON)CallService(MS_SKIN2_GETICON, 0, (LPARAM)"otr_insecure"); + + hIcoLibIconsChanged = HookEvent(ME_SKIN2_ICONSCHANGED, ReloadIcons); + } else { + hProtoIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_START), IMAGE_ICON, 16, 16, 0); + hLockIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_START), IMAGE_ICON, 16, 16, 0); + hUnlockIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_STOP), IMAGE_ICON, 16, 16, 0); + } +} + +void DeinitUtils() { + if(hIcoLibIconsChanged) UnhookEvent(hIcoLibIconsChanged); + else { + DestroyIcon(hProtoIcon); + DestroyIcon(hLockIcon); + DestroyIcon(hUnlockIcon); + } +} diff --git a/otr/utils.h b/otr/utils.h new file mode 100644 index 0000000..4b78e28 --- /dev/null +++ b/otr/utils.h @@ -0,0 +1,15 @@ +#ifndef _UTILS_INC +#define _UTILS_INC + +extern HICON hProtoIcon, hLockIcon, hUnlockIcon; + +void InitUtils(); +void DeinitUtils(); + +void ShowPopup( const char* line1, const char* line2, int timeout); +void ShowWarning(char *msg); +void ShowError(char *msg); + +bool CreatePath(const char *path); + +#endif -- cgit v1.2.3