From 11e5e8749eb7d4d3cfebfa49bbb7ea4624608647 Mon Sep 17 00:00:00 2001 From: "admin@progandy.co.cc" Date: Thu, 19 Aug 2010 20:12:06 +0000 Subject: initial commit with v0.8.6.1 git-svn-id: http://mirotr.googlecode.com/svn/trunk@2 eced67a3-f377-a0ae-92ae-d6de1850b05a --- MirOTR/DLG_smp.cpp | 96 +++++ MirOTR/MirOTR.vcproj | 510 +++++++++++++++++++++++ MirOTR/ReadMe.txt | 45 +++ MirOTR/dbfilter.cpp | 236 +++++++++++ MirOTR/dbfilter.h | 4 + MirOTR/dialogs.cpp | 153 +++++++ MirOTR/dialogs.h | 2 + MirOTR/dllmain.cpp | 202 ++++++++++ MirOTR/dllmain.h | 9 + MirOTR/icons.cpp | 59 +++ MirOTR/icons.h | 12 + MirOTR/language.h | 84 ++++ MirOTR/options.cpp | 875 ++++++++++++++++++++++++++++++++++++++++ MirOTR/options.h | 66 +++ MirOTR/otr.cpp | 383 ++++++++++++++++++ MirOTR/otr.h | 53 +++ MirOTR/resource.h | 103 +++++ MirOTR/resources/finished.ico | Bin 0 -> 2550 bytes MirOTR/resources/insecure.ico | Bin 0 -> 2550 bytes MirOTR/resources/otr.ico | Bin 0 -> 17542 bytes MirOTR/resources/private.ico | Bin 0 -> 2550 bytes MirOTR/resources/refresh.ico | Bin 0 -> 7886 bytes MirOTR/resources/resource.rc | 133 ++++++ MirOTR/resources/unverified.ico | Bin 0 -> 2550 bytes MirOTR/resources/version.rc | 35 ++ MirOTR/stdafx.cpp | 8 + MirOTR/stdafx.h | 89 ++++ MirOTR/svcs_menu.cpp | 169 ++++++++ MirOTR/svcs_menu.h | 7 + MirOTR/svcs_proto.cpp | 370 +++++++++++++++++ MirOTR/svcs_proto.h | 13 + MirOTR/svcs_srmm.cpp | 224 ++++++++++ MirOTR/svcs_srmm.h | 7 + MirOTR/targetver.h | 24 ++ MirOTR/utils.cpp | 400 ++++++++++++++++++ MirOTR/utils.h | 47 +++ MirOTR/version.h | 37 ++ 37 files changed, 4455 insertions(+) create mode 100644 MirOTR/DLG_smp.cpp create mode 100644 MirOTR/MirOTR.vcproj create mode 100644 MirOTR/ReadMe.txt create mode 100644 MirOTR/dbfilter.cpp create mode 100644 MirOTR/dbfilter.h create mode 100644 MirOTR/dialogs.cpp create mode 100644 MirOTR/dialogs.h create mode 100644 MirOTR/dllmain.cpp create mode 100644 MirOTR/dllmain.h create mode 100644 MirOTR/icons.cpp create mode 100644 MirOTR/icons.h create mode 100644 MirOTR/language.h create mode 100644 MirOTR/options.cpp create mode 100644 MirOTR/options.h create mode 100644 MirOTR/otr.cpp create mode 100644 MirOTR/otr.h create mode 100644 MirOTR/resource.h create mode 100644 MirOTR/resources/finished.ico create mode 100644 MirOTR/resources/insecure.ico create mode 100644 MirOTR/resources/otr.ico create mode 100644 MirOTR/resources/private.ico create mode 100644 MirOTR/resources/refresh.ico create mode 100644 MirOTR/resources/resource.rc create mode 100644 MirOTR/resources/unverified.ico create mode 100644 MirOTR/resources/version.rc create mode 100644 MirOTR/stdafx.cpp create mode 100644 MirOTR/stdafx.h create mode 100644 MirOTR/svcs_menu.cpp create mode 100644 MirOTR/svcs_menu.h create mode 100644 MirOTR/svcs_proto.cpp create mode 100644 MirOTR/svcs_proto.h create mode 100644 MirOTR/svcs_srmm.cpp create mode 100644 MirOTR/svcs_srmm.h create mode 100644 MirOTR/targetver.h create mode 100644 MirOTR/utils.cpp create mode 100644 MirOTR/utils.h create mode 100644 MirOTR/version.h (limited to 'MirOTR') diff --git a/MirOTR/DLG_smp.cpp b/MirOTR/DLG_smp.cpp new file mode 100644 index 0000000..b701b7f --- /dev/null +++ b/MirOTR/DLG_smp.cpp @@ -0,0 +1,96 @@ +#include "stdafx.h" +#include +//TODO: Social Millionaire Protocol +typedef std::map SmpForContactMap; +SmpForContactMap smp_for_contact; + +HWND smp_find_for_contact(HANDLE hContact) { + SmpForContactMap::iterator iter = smp_for_contact.find(hContact); + if (iter == smp_for_contact.end()) return null; + return iter->second; +} + +/* Create the SMP dialog. responder is true if this is called in + * response to someone else's run of SMP. */ +static void dialog_socialist_millionaires(ConnContext *context, + TCHAR *question, bool responder) +{ + if (context == NULL) return; + TCHAR primary[1024]; + + if (responder && question) { + (HANDLE)context->app_data + mir_sntprintf(primary, 1024, TranslateT(LANG_SMP_AUTH_FROM), + contact_get_nameT((HANDLE)context->app_data)); + } else { + mir_sntprintf(primary, 1024, TranslateT(LANG_SMP_AUTH), + contact_get_nameT((HANDLE)context->app_data)); + } + + /* fprintf(stderr, "Question = ``%s''\n", question); */ + //TCHAR* proto_name = mir_a2t(context->protocol); + //if (!proto_name) proto_name = mir_tstrdup(TranslateT(LANG_UNKNOWN)); + + + dialog = create_smp_dialog(_("Authenticate Buddy"), + primary, context, responder, question); + + //mir_free(proto_name); +} + +/* Call this to update the status of an ongoing socialist millionaires + * protocol. Progress_level is a percentage, from 0.0 (aborted) to + * 1.0 (complete). Any other value represents an intermediate state. */ +static void otrg_gtk_dialog_update_smp(ConnContext *context, + double progress_level) +{ + PurpleConversation *conv = otrg_plugin_context_to_conv(context, 0); + GtkProgressBar *bar; + SMPData *smp_data = purple_conversation_get_data(conv, "otr-smpdata"); + + if (!smp_data) return; + + bar = GTK_PROGRESS_BAR(smp_data->smp_progress_bar); + gtk_progress_bar_set_fraction(bar, progress_level); + + /* If the counter is reset to absolute zero, the protocol has aborted */ + if (progress_level == 0.0) { + GtkDialog *dialog = GTK_DIALOG(smp_data->smp_progress_dialog); + + gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_ACCEPT, 1); + gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_REJECT, 0); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), + GTK_RESPONSE_ACCEPT); + + gtk_label_set_text(GTK_LABEL(smp_data->smp_progress_label), + _("An error occurred during authentication.")); + return; + } else if (progress_level == 1.0) { + /* If the counter reaches 1.0, the protocol is complete */ + GtkDialog *dialog = GTK_DIALOG(smp_data->smp_progress_dialog); + + gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_ACCEPT, 1); + gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_REJECT, 0); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), + GTK_RESPONSE_ACCEPT); + + if (context->smstate->sm_prog_state == OTRL_SMP_PROG_SUCCEEDED) { + if (context->active_fingerprint->trust && + context->active_fingerprint->trust[0]) { + gtk_label_set_text(GTK_LABEL(smp_data->smp_progress_label), + _("Authentication successful.")); + } else { + gtk_label_set_text(GTK_LABEL(smp_data->smp_progress_label), + _("Your buddy has successfully authenticated you. " + "You may want to authenticate your buddy as " + "well by asking your own question.")); + } + } else { + gtk_label_set_text(GTK_LABEL(smp_data->smp_progress_label), + _("Authentication failed.")); + } + } else { + /* Clear the progress label */ + gtk_label_set_text(GTK_LABEL(smp_data->smp_progress_label), ""); + } +} \ No newline at end of file diff --git a/MirOTR/MirOTR.vcproj b/MirOTR/MirOTR.vcproj new file mode 100644 index 0000000..4b46162 --- /dev/null +++ b/MirOTR/MirOTR.vcproj @@ -0,0 +1,510 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MirOTR/ReadMe.txt b/MirOTR/ReadMe.txt new file mode 100644 index 0000000..2c41343 --- /dev/null +++ b/MirOTR/ReadMe.txt @@ -0,0 +1,45 @@ +======================================================================== + DYNAMIC LINK LIBRARY: MirOTR-Projektübersicht +======================================================================== + +Diese MirOTR-DLL wurde vom Anwendungs-Assistenten für Sie +erstellt. + +Die Datei enthält eine Zusammenfassung des Inhalts der Dateien, +aus denen die MirOTR-Anwendung besteht. + + +MirOTR.vcproj + Dies ist die Hauptprojektdatei für VC++-Projekte, die mit dem Anwendungs- + Assistenten generiert werden. + Sie enthält Informationen zu der Version von Visual C++, mit der die Datei + generiert wurde, sowie Informationen zu Plattformen, Konfigurationen und + Projektfeatures, die mit dem dem Anwendungs-Assistenten generiert werden. + +MirOTR.cpp + Dies ist die Hauptquelldatei der DLL. + + Diese DLL exportiert keine Symbole, deshalb wird beim Erstellen keine + LIB-Datei generiert. Wenn dieses Projekt eine Projektabhängigkeit + eines anderen Projekts sein soll, müssen Sie Code hinzufügen, um + Symbole aus der DLL zu exportieren, damit eine Exportbibliothek + erstellt wird, oder Sie können die Eigenschaft "Eingabebibliothek + ignorieren" auf der Eigenschaftenseite "Allgemein" in den + Projekteigenschaften auf "Ja" festlegen. + Dialogfeld "Seiten". + +///////////////////////////////////////////////////////////////////////////// +Andere Standarddateien: + +StdAfx.h, StdAfx.cpp + Mit diesen Dateien werden eine vorkompilierte Headerdatei (PCH) + mit dem Namen MirOTR.pch sowie eine vorkompilierte + Typendatei mit dem Namen StdAfx.obj erstellt. + +///////////////////////////////////////////////////////////////////////////// +Weitere Hinweise: + +Der Anwendungs-Assistent verwendet "TODO:"-Kommentare, um die Teile des +Quellcodes anzugeben, die hinzugefügt oder bearbeitet werden müssen. + +///////////////////////////////////////////////////////////////////////////// diff --git a/MirOTR/dbfilter.cpp b/MirOTR/dbfilter.cpp new file mode 100644 index 0000000..cfd7537 --- /dev/null +++ b/MirOTR/dbfilter.cpp @@ -0,0 +1,236 @@ +#include "stdafx.h" +#include "dbfilter.h" +static HANDLE hDBEventPreAdd, hDBEventAdded; +static CRITICAL_SECTION RemoveChainCS={0}, *lpRemoveChainCS = &RemoveChainCS; +static UINT_PTR timerId = 0; + +struct DeleteEventNode { + DeleteEventNode *next; + time_t timestamp; + HANDLE hContact; + HANDLE hDbEvent; +}; +struct DeleteEventHead { + DeleteEventNode *first; + DeleteEventNode *last; +}; +static DeleteEventHead DeleteEvents = {0,0}; + +VOID CALLBACK DeleteTimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime) { + if (!DeleteEvents.first) return; + EnterCriticalSection(lpRemoveChainCS); + DeleteEventNode *prev =0, *current, *next; + DBEVENTINFO info = {0}; + info.cbSize = sizeof(info); + next = DeleteEvents.first; + while (current = next) { + if (difftime(time(0), current->timestamp) < 1) break; + if(!CallService(MS_DB_EVENT_GET, (WPARAM)current->hDbEvent, (LPARAM)&info)) // && info.flags&DBEF_READ) + { + CallService(MS_DB_EVENT_DELETE, (WPARAM)current->hContact, (LPARAM)current->hDbEvent); + next = current->next; + if (prev) prev->next = next; + else if (DeleteEvents.first == current) DeleteEvents.first = next; + delete current; + } else { + prev = current; + next = current->next; + } + } + if (!DeleteEvents.first) DeleteEvents.last = 0; + LeaveCriticalSection(lpRemoveChainCS); +} + + +// add prefix to sent messages +INT_PTR OnDatabaseEventPreAdd(WPARAM wParam, LPARAM lParam) { + if(!options.prefix_messages || !lParam) return 0; + HANDLE hContact = (HANDLE)wParam; + DBEVENTINFO *dbei = (DBEVENTINFO *)lParam; + if ((dbei->eventType != EVENTTYPE_MESSAGE) || !(dbei->flags & DBEF_SENT) || (dbei->flags & DBEF_OTR_PREFIXED)) + return 0; + if(dbei->cbBlob == 0 || dbei->pBlob == 0) + return 0; // just to be safe + + const char *proto = contact_get_proto(hContact); + if(!proto ) return 0; + if (DBGetContactSettingByte(hContact, proto, "ChatRoom", 0) == 1) + return 0; + + if(g_metaproto && strcmp(proto, g_metaproto) == 0) { + hContact = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0); + if(!hContact) return 0; + proto = contact_get_proto(hContact); + if(!proto ) return 0; + } + + + ConnContext *context = otrl_context_find_miranda(otr_user_state, hContact); + bool encrypted = otr_context_get_trust(context) != TRUST_NOT_PRIVATE; + if(!encrypted) return 0; + + DBEVENTINFO my_dbei = *dbei; // copy the other event + + char *msg = (char *)dbei->pBlob; + char *newmsg = 0; + DWORD alloclen = 0; + DWORD msglen = strlen(msg); + if (dbei->flags & DBEF_UTF) { + int prefixlen = strnlen(options.prefix, 64); + if (strncmp(msg, options.prefix, prefixlen) == 0) return 0; + alloclen = (msglen+prefixlen+1)*sizeof(char); + + // get additional data + DWORD len2 = alloclen- prefixlen*sizeof(char); + int datalen = dbei->cbBlob - len2; + if (datalen < 0) datalen = 0; + + newmsg = (char*)mir_alloc(alloclen); + memset(newmsg, 0, alloclen+datalen); + strncpy(newmsg, options.prefix, prefixlen); + strncat(newmsg, msg, msglen); + // append additional data + if (datalen) { + memcpy(newmsg+alloclen, msg+len2, datalen); + alloclen += datalen; + } + } else { + bool dz = false; + if(msglen < dbei->cbBlob) { + for(unsigned int i = msglen; i < dbei->cbBlob; i++) { + if(msg[i] == 0 && msg[i - 1] == 0) { + dz = true; + break; + } + } + } + if(dz) { + // yes, unicode + wchar_t* msgw = (wchar_t *)&msg[msglen]; + wchar_t *prefixw = mir_utf8decodeW(options.prefix); + int prefixlenw = wcslen(prefixw); + if (wcsncmp(msgw, prefixw, prefixlenw) == 0) { + mir_free(prefixw); + return 0; + } + int msglenw = wcslen(msgw); + + char *prefix = mir_utf8decodeA(options.prefix); + int prefixlen = strlen(prefix); + + alloclen = (msglen+prefixlen+1)* sizeof(char) + (msglenw + prefixlenw +1) * sizeof(wchar_t); + // get additional data + DWORD len2 = (msglen+1)* sizeof(char) + (msglenw+1) * sizeof(wchar_t); + int datalen = dbei->cbBlob - len2; + if (datalen < 0) datalen = 0; + + newmsg = (char*)mir_alloc(alloclen+datalen); + memset(newmsg, 0, alloclen+datalen); + strncpy(newmsg, prefix, prefixlen); + strncat(newmsg, msg, msglen); + wchar_t *p = (wchar_t*) newmsg + (msglen + prefixlen + 1) * sizeof(char); + wcsncpy(p, prefixw, prefixlenw); + wcsncat(p, msgw, msglenw); + mir_free(prefix); + mir_free(prefixw); + // append additional data + if (datalen) { + memcpy(newmsg+alloclen, msg+len2, datalen); + alloclen += datalen; + } + + } else { + char *prefix = mir_utf8decodeA(options.prefix); + int prefixlen = strlen(prefix); + if (strncmp(msg, prefix, prefixlen) == 0) { + mir_free(prefix); + return 0; + } + alloclen = msglen+prefixlen+sizeof(char); + // get additional data + DWORD len2 = alloclen-prefixlen; + int datalen = dbei->cbBlob - len2; + if (datalen < 0) datalen = 0; + + newmsg = (char*)mir_alloc(alloclen+datalen); + memset(newmsg, 0, alloclen+datalen); + strncpy(newmsg, prefix, prefixlen); + strncat(newmsg, msg, msglen); + mir_free(prefix); + // append additional data + if (datalen) { + memcpy(newmsg+alloclen, msg+len2, datalen); + alloclen += datalen; + } + } + + } + + my_dbei.pBlob = (BYTE *)newmsg; + my_dbei.cbBlob = alloclen; + my_dbei.flags |= DBEF_OTR_PREFIXED; + + CallService(MS_DB_EVENT_ADD, wParam, (LPARAM)&my_dbei); + if (newmsg) mir_free(newmsg); + + // stop original event from being added + return 1; +} + +INT_PTR OnDatabaseEventAdded(WPARAM wParam, LPARAM lParam) { + if(!options.no_history) return 0; + + DBEVENTINFO info = {0}; + info.cbSize = sizeof(info); + + static char* prefixutf = mir_utf8encodeT(TranslateT(LANG_INLINE_PREFIX)); + static char* prefix = Translate(LANG_INLINE_PREFIX); + static DWORD lenutf = strlen(prefixutf); + static DWORD len = strlen(prefix); + info.cbBlob = lenutf*2; + info.pBlob = (PBYTE)mir_alloc(info.cbBlob); + if(!CallService(MS_DB_EVENT_GET, (WPARAM)lParam, (LPARAM)&info)) { + if(info.eventType == EVENTTYPE_MESSAGE) { + HANDLE hContact = (HANDLE)wParam; + ConnContext *context = otrl_context_find_miranda(otr_user_state, hContact); + if (context && otr_context_get_trust(context) != TRUST_NOT_PRIVATE + && ( (info.flags&DBEF_UTF && !(info.cbBlob >lenutf && 0==strncmp((char*)info.pBlob, prefixutf, lenutf))) + || (!(info.flags&DBEF_UTF) && !(info.cbBlob >len && 0==strncmp((char*)info.pBlob, prefix, len))) + ) + ) // only delete encrypted messages that are no OTR system messages + { + DeleteEventNode *node = new DeleteEventNode(); + node->hContact = hContact; + node->hDbEvent = (HANDLE) lParam; + node->timestamp = time(0); + node->next = 0; + EnterCriticalSection(lpRemoveChainCS); + if (DeleteEvents.last) + DeleteEvents.last->next = node; + else + DeleteEvents.first = node; + DeleteEvents.last = node; + LeaveCriticalSection(lpRemoveChainCS); + } + } + } + mir_free(prefixutf); + mir_free(info.pBlob); + return 0; +} + +void InitDBFilter() { + InitializeCriticalSectionAndSpinCount(lpRemoveChainCS, 500); + hDBEventPreAdd = HookEvent(ME_DB_EVENT_FILTER_ADD, OnDatabaseEventPreAdd); + hDBEventAdded = HookEvent(ME_DB_EVENT_ADDED, OnDatabaseEventAdded); + timerId = SetTimer(0, 0, 1000, DeleteTimerProc); +} +void DeinitDBFilter() { + UnhookEvent(hDBEventPreAdd); + hDBEventPreAdd = 0; + UnhookEvent(hDBEventAdded); + hDBEventAdded = 0; + if (timerId) KillTimer(0, timerId); + DeleteTimerProc(0,0,0,0); + DeleteCriticalSection(lpRemoveChainCS); +} \ No newline at end of file diff --git a/MirOTR/dbfilter.h b/MirOTR/dbfilter.h new file mode 100644 index 0000000..99a6d50 --- /dev/null +++ b/MirOTR/dbfilter.h @@ -0,0 +1,4 @@ +#define DBEF_OTR_PREFIXED 0x8000 + +void InitDBFilter(); +void DeinitDBFilter(); \ No newline at end of file diff --git a/MirOTR/dialogs.cpp b/MirOTR/dialogs.cpp new file mode 100644 index 0000000..15630cf --- /dev/null +++ b/MirOTR/dialogs.cpp @@ -0,0 +1,153 @@ +#include "stdafx.h" +#include "dialogs.h" +#include +#include +#include + +unsigned int CALLBACK verify_context_thread(void *param); +void VerifyContextDialog(ConnContext* context) { + if (!context) return; + CloseHandle((HANDLE)_beginthreadex(0, 0, verify_context_thread, context, 0, 0)); +} + +INT_PTR CALLBACK DlgProcVerifyContext(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch ( msg ) { + case WM_INITDIALOG: + { + if (!lParam) { + EndDialog(hwndDlg, IDCANCEL); + return FALSE; + } + SendMessage(hwndDlg, WM_SETTEXT, 0, (LPARAM)_T(LANG_OTR_FPVERIFY_TITLE)); + SetDlgItemText(hwndDlg, IDC_STC_SMP_HEAD, _T(LANG_OTR_FPVERIFY_TITLE)); + TranslateDialogDefault( hwndDlg ); + SetWindowLongPtr(hwndDlg, GWL_USERDATA, lParam); + + // Move window to screen center + // Get the owner window and dialog box rectangles. + HWND hwndOwner; RECT rcOwner, rcDlg, rc; + if ((hwndOwner = GetParent(hwndDlg)) == NULL) + { + hwndOwner = GetDesktopWindow(); + } + + GetWindowRect(hwndOwner, &rcOwner); + GetWindowRect(hwndDlg, &rcDlg); + CopyRect(&rc, &rcOwner); + + // Offset the owner and dialog box rectangles so that right and bottom + // values represent the width and height, and then offset the owner again + // to discard space taken up by the dialog box. + + OffsetRect(&rcDlg, -rcDlg.left, -rcDlg.top); + OffsetRect(&rc, -rc.left, -rc.top); + OffsetRect(&rc, -rcDlg.right, -rcDlg.bottom); + + // The new position is the sum of half the remaining space and the owner's + // original position. + + SetWindowPos(hwndDlg, + HWND_TOP, + rcOwner.left + (rc.right / 2), + rcOwner.top + (rc.bottom / 2), + 0, 0, // Ignores size arguments. + SWP_NOSIZE); + + // end center dialog + + ConnContext *context = (ConnContext*)lParam; + Fingerprint *fp = context->active_fingerprint; + if (!fp) { + EndDialog(hwndDlg, IDCANCEL); + return FALSE; + } + TCHAR buff[512]; + if (!fp->trust || fp->trust[0] == '\0') + mir_sntprintf(buff, 512, TranslateT(LANG_OTR_FPVERIFY_DESC), contact_get_nameT((HANDLE)context->app_data)); + else + mir_sntprintf(buff, 512, TranslateT(LANG_OTR_FPVERIFIED_DESC), contact_get_nameT((HANDLE)context->app_data)); + + SetDlgItemText(hwndDlg, IDC_STC_SMP_INFO, buff); + + unsigned char hash[20]; + lib_cs_lock(); + if (!otrl_privkey_fingerprint_raw(otr_user_state, hash, context->accountname, context->protocol)) { + lib_cs_unlock(); + EndDialog(hwndDlg, IDCANCEL); + return FALSE; + } + otrl_privkey_hash_to_humanT(buff, hash); + lib_cs_unlock(); + SetDlgItemText(hwndDlg, IDC_EDT_SMP_FIELD1, buff); + SendDlgItemMessage(hwndDlg, IDC_EDT_SMP_FIELD1, EM_SETREADONLY, TRUE, 0); + SetDlgItemText(hwndDlg, IDC_STC_SMP_FIELD1, TranslateT(LANG_YOUR_PRIVKEY)); + + otrl_privkey_hash_to_humanT(buff, fp->fingerprint); + SetDlgItemText(hwndDlg, IDC_EDT_SMP_FIELD2, buff); + SendDlgItemMessage(hwndDlg, IDC_EDT_SMP_FIELD2, EM_SETREADONLY, TRUE, 0); + SetDlgItemText(hwndDlg, IDC_STC_SMP_FIELD2, TranslateT(LANG_CONTACT_FINGERPRINT)); + + EnableWindow(GetDlgItem(hwndDlg, IDC_CBO_SMP_CHOOSE), FALSE); + + ShowWindow(GetDlgItem(hwndDlg, IDOK), SW_HIDE); + ShowWindow(GetDlgItem(hwndDlg, IDYES), SW_SHOWNA); + ShowWindow(GetDlgItem(hwndDlg, IDNO), SW_SHOWNA); + SetFocus(GetDlgItem(hwndDlg, IDCANCEL)); + + return FALSE; + } + + case WM_COMMAND: + switch ( HIWORD( wParam )) { + case BN_CLICKED: + switch ( LOWORD( wParam )) { + case IDYES: + case IDNO: + case IDCANCEL: + case IDOK: + EndDialog(hwndDlg, LOWORD( wParam )); + break; + } + } + break; + + } + + return FALSE; +} + +unsigned int CALLBACK verify_context_thread(void *param) { + CallService(MS_SYSTEM_THREAD_PUSH, 0, 0); + + if (param) { + ConnContext *context = (ConnContext *)param; + TCHAR msg[1024]; + switch ( DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_SMP_INPUT), 0, DlgProcVerifyContext, (LPARAM)param) ) { + case IDOK: + case IDYES: + lib_cs_lock(); + otrl_context_set_trust(context->active_fingerprint, "verified"); + otrl_privkey_write_fingerprints(otr_user_state, g_fingerprint_store_filename); + lib_cs_unlock(); + mir_sntprintf(msg, 1024, TranslateT(LANG_FINGERPRINT_VERIFIED), contact_get_nameT((HANDLE)context->app_data)); + msg[1023] = '\0'; + ShowMessage((HANDLE)context->app_data, msg); + SetEncryptionStatus(context->app_data, otr_context_get_trust(context)); + break; + case IDNO: + lib_cs_lock(); + otrl_context_set_trust(context->active_fingerprint, NULL); + otrl_privkey_write_fingerprints(otr_user_state, g_fingerprint_store_filename); + lib_cs_unlock(); + mir_sntprintf(msg, 1024, TranslateT(LANG_FINGERPRINT_NOT_VERIFIED), contact_get_nameT((HANDLE)context->app_data)); + msg[1023] = '\0'; + ShowMessage((HANDLE)context->app_data, msg); + SetEncryptionStatus(context->app_data, otr_context_get_trust(context)); + break; + } + } + + CallService(MS_SYSTEM_THREAD_POP, 0, 0); + return 0; +} \ No newline at end of file diff --git a/MirOTR/dialogs.h b/MirOTR/dialogs.h new file mode 100644 index 0000000..528622f --- /dev/null +++ b/MirOTR/dialogs.h @@ -0,0 +1,2 @@ +void VerifyContextDialog(ConnContext* context); +INT_PTR CALLBACK DlgProcVerifyContext(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); \ No newline at end of file diff --git a/MirOTR/dllmain.cpp b/MirOTR/dllmain.cpp new file mode 100644 index 0000000..38f6bac --- /dev/null +++ b/MirOTR/dllmain.cpp @@ -0,0 +1,202 @@ +// dllmain.cpp : Definiert den Einstiegspunkt für die DLL-Anwendung. +#include "stdafx.h" +#include "dllmain.h" +#include "otr.h" +#include "dbfilter.h" +#include + +HANDLE hEventWindow; +HINSTANCE hInst; +PLUGINLINK *pluginLink; +MM_INTERFACE mmi; +UTF8_INTERFACE utfi; +char* g_metaproto; + + +PLUGININFOEX pluginInfo={ + sizeof(PLUGININFOEX), + SHORT_NAME_STRING, + PLUGIN_MAKE_VERSION(VER_MAJOR,VER_MINOR,VER_RELEASE,VER_BUILD), + LONGDESC_STRING, + AUTHOR, + AUTHOR_MAIL, + LEGAL_COPYRIGHT_LONG, + HOMEPAGE, + UNICODE_AWARE, //not transient + 0, //doesn't replace anything built-in + MIID_OTRPLUGIN // ANSI and Unicode have different IDs +}; + +BOOL APIENTRY DllMain( HMODULE hModule, + DWORD ul_reason_for_call, + LPVOID lpReserved + ) +{ + if (ul_reason_for_call == DLL_PROCESS_ATTACH) { + hInst=hModule; + OTRL_INIT; + INITCOMMONCONTROLSEX icce = {0}; + icce.dwSize = sizeof(icce); + icce.dwICC = ICC_LISTVIEW_CLASSES|ICC_PROGRESS_CLASS; + InitCommonControlsEx(&icce); + } + return TRUE; +} + +DLLFUNC PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion) +{ + return &pluginInfo; +} + +static const MUUID interfaces[] = {MIID_OTR, MIID_LAST}; +DLLFUNC const MUUID* MirandaPluginInterfaces(void) +{ + return interfaces; +} + + +int ModulesLoaded(WPARAM wParam, LPARAM lParam) { + if(ServiceExists(MS_MC_GETPROTOCOLNAME)) + g_metaproto = (char *)CallService(MS_MC_GETPROTOCOLNAME, 0, 0); + + 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.szBetaChangelogURL = "https://server.scottellis.com.au/wsvn/mim_plugs/otr/?op=log&rev=0&sc=0&isdir=1"; + + 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); + + + // DISABLED UPDATE CHECK FOR NOW + // CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update); + } + + InitUtils(); + + lib_cs_lock(); + otr_user_state = otrl_userstate_create(); + lib_cs_unlock(); + + // this calls ReadPrivkeyFiles (above) to set filename values (also called on ME_FOLDERS_PATH_CHANGED) + InitOptions(); + + InitDBFilter(); + InitIcons(); + InitMenu(); + + InitSRMM(); + + hEventWindow = HookEvent(ME_MSG_WINDOWEVENT, WindowEvent); + + // HookEvent(ME_OPT_INITIALISE, OptInit); + + // hook setting changed to monitor status + //hSettingChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, SettingChanged); + + // hook status mode changes to terminate sessions when we go offline + // (this would be hooked as the ME_CLIST_STATUSMODECHANGE handler except that event is sent *after* the proto goes offline) + // (instead, it's called from the SettingChanged handler for protocol status db setting changes) + //HookEvent(ME_CLIST_STATUSMODECHANGE, StatusModeChange); + + return 0; +} + +int NewContact(WPARAM wParam, LPARAM lParam) { + // add filter + HANDLE hContact = (HANDLE)wParam; + CallService( MS_PROTO_ADDTOCONTACT, ( WPARAM )hContact, ( LPARAM )MODULENAME ); + + return 0; +} + +DLLFUNC int Load(PLUGINLINK *link) +{ + DEBUGOUT_T("LOAD MIROTR") + pluginLink=link; + mir_getMMI(&mmi); + mir_getUTFI(&utfi); + + CallService(MS_DB_SETSETTINGRESIDENT, TRUE, (LPARAM)(MODULENAME "/TrustLevel")); + + ///////////// + ////// init plugin + PROTOCOLDESCRIPTOR pd = {0}; + pd.cbSize = sizeof(pd); + pd.szName = MODULENAME; + pd.type = PROTOTYPE_ENCRYPTION; + CallService(MS_PROTO_REGISTERMODULE,0,(LPARAM)&pd); + + // remove us as a filter to all contacts - fix filter type problem + /* + if(DBGetContactSettingByte(0, MODULENAME, "FilterOrderFix", 0) != 2) { + HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + while ( hContact != NULL ) { + CallService( MS_PROTO_REMOVEFROMCONTACT, ( WPARAM )hContact, ( LPARAM )MODULENAME ); + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } + DBWriteContactSettingByte(0, MODULENAME, "FilterOrderFix", 2); + } + */ + // add us as a filter to all contacts + 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)) ) + CallService( MS_PROTO_ADDTOCONTACT, ( WPARAM )hContact, ( LPARAM )MODULENAME ); + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } + HookEvent(ME_DB_CONTACT_ADDED, NewContact); + + // create our services + CreateProtoServiceFunction(MODULENAME, PSS_MESSAGE, SVC_OTRSendMessage); + CreateProtoServiceFunction(MODULENAME, PSS_MESSAGE"W", SVC_OTRSendMessageW); + CreateProtoServiceFunction(MODULENAME, PSR_MESSAGE, SVC_OTRRecvMessage); + + CreateServiceFunction(MS_OTR_MENUSTART, SVC_StartOTR); + CreateServiceFunction(MS_OTR_MENUSTOP, SVC_StopOTR); + + // hook modules loaded for updater support + HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded); + + return 0; +} + +DLLFUNC int Unload(void) +{ + //UnhookEvent(hSettingChanged); + UnhookEvent(hEventWindow); + //UnhookEvent(hEventDbEventAddedFilter); + //UnhookEvent(hEventDbEventAdded); + DEBUGOUT_T("UNLOAD MIROTR") + DeinitSRMM(); + DeinitOptions(); + DeinitMenu(); + DeinitDBFilter(); + + lib_cs_lock(); + otrl_userstate_free(otr_user_state); + lib_cs_unlock(); + + DeinitIcons(); + DeinitUtils(); + + return 0; +} \ No newline at end of file diff --git a/MirOTR/dllmain.h b/MirOTR/dllmain.h new file mode 100644 index 0000000..7a99b05 --- /dev/null +++ b/MirOTR/dllmain.h @@ -0,0 +1,9 @@ +#pragma once + +#include "stdafx.h" +#include "version.h" + +#define DLLFUNC extern "C" __declspec(dllexport) + +extern HINSTANCE hInst; +extern PLUGINLINK *pluginLink; \ No newline at end of file diff --git a/MirOTR/icons.cpp b/MirOTR/icons.cpp new file mode 100644 index 0000000..7b26df9 --- /dev/null +++ b/MirOTR/icons.cpp @@ -0,0 +1,59 @@ +#include "stdafx.h" +#include "icons.h" +TCHAR g_dllpath[1024]; + +struct +{ + const TCHAR* stzDescr; + const char* szName; + int defIconID; +} +static iconList[] = { + { _T(LANG_ICON_OTR), ICON_OTR, IDI_OTR }, + { _T(LANG_ICON_PRIVATE), ICON_PRIVATE, IDI_PRIVATE }, + { _T(LANG_ICON_UNVERIFIED), ICON_UNVERIFIED, IDI_UNVERIFIED }, + { _T(LANG_ICON_FINISHED), ICON_FINISHED, IDI_FINISHED }, + { _T(LANG_ICON_NOT_PRIVATE), ICON_NOT_PRIVATE, IDI_INSECURE } + +}; + +HANDLE hIconLibItem[SIZEOF(iconList)]; + +void InitIcons() { + TCHAR szFile[MAX_PATH+500]; + + GetModuleFileName(hInst, szFile, SIZEOF(szFile)); + + SKINICONDESC sid = {0}; + sid.cbSize = sizeof(SKINICONDESC); + sid.ptszDefaultFile = szFile; + sid.ptszSection = _T(MODULENAME); + sid.flags = SIDF_ALL_TCHAR; + + for ( int i = 0; i < SIZEOF(iconList); i++ ) { + sid.pszName = (char*)iconList[i].szName; + sid.ptszDescription = (TCHAR*)iconList[i].stzDescr; + sid.iDefaultIndex = -iconList[i].defIconID; + hIconLibItem[i] = (HANDLE)CallService(MS_SKIN2_ADDICON, 0, (LPARAM)&sid); + } + +} + +void DeinitIcons() { + for ( int i = 0; i < SIZEOF(iconList); i++ ) { + (HANDLE)CallService(MS_SKIN2_REMOVEICON, 0, (LPARAM)iconList[i].szName); + hIconLibItem[i] = NULL; + } +} + +HICON LoadIcon(const char* name, int big) { + return ( HICON )CallService( MS_SKIN2_GETICON, big, (LPARAM)name ); +} + +HANDLE GetIconHandle(const char* name) { + return ( HANDLE )CallService( MS_SKIN2_GETICONHANDLE, 0, (LPARAM)name ); +} + +void ReleaseIcon(const char* name, int big) { + CallService( big ? MS_SKIN2_RELEASEICONBIG : MS_SKIN2_RELEASEICON, 0, (LPARAM)name ); +} diff --git a/MirOTR/icons.h b/MirOTR/icons.h new file mode 100644 index 0000000..86605d4 --- /dev/null +++ b/MirOTR/icons.h @@ -0,0 +1,12 @@ +#pragma once +#define ICON_FINISHED (MODULENAME"_finished") +#define ICON_UNVERIFIED (MODULENAME"_unverified") +#define ICON_PRIVATE (MODULENAME"_private") +#define ICON_NOT_PRIVATE (MODULENAME"_not_private") +#define ICON_OTR (MODULENAME"_otr") + +void InitIcons(); +void DeinitIcons(); +HICON LoadIcon(const char* name, int big); +HANDLE GetIconHandle(const char* name); +void ReleaseIcon(const char* name, int big); \ No newline at end of file diff --git a/MirOTR/language.h b/MirOTR/language.h new file mode 100644 index 0000000..41f4525 --- /dev/null +++ b/MirOTR/language.h @@ -0,0 +1,84 @@ +#pragma once + +#include "m_langpack.h" + +#define LANG_START_OTR LPGEN("Start OTR") +#define LANG_STOP_OTR LPGEN("Stop OTR") + +#define LANG_YES LPGEN("Yes") +#define LANG_NO LPGEN("No") + +#define LANG_OTR_TOOLTIP LPGEN("OTR status (Click for more options)") +#define LANG_STATUS_DISABLED LPGEN("OTR Encryption: Disabled") +#define LANG_STATUS_UNVERIFIED LPGEN("OTR Encryption: UNVERIFIED") +#define LANG_STATUS_PRIVATE LPGEN("OTR Encryption: Private") +#define LANG_STATUS_FINISHED LPGEN("OTR Encryption: Finished") + +#define LANG_SESSION_TERMINATED_OTR LPGEN("OTR encrypted session with '%s' has been terminated") +#define LANG_SESSION_REQUEST_OTR LPGEN("Requesting OTR encrypted session with '%s'") +#define LANG_SESSION_START_OTR LPGEN("Beginning OTR encrypted session with '%s'") +#define LANG_SESSION_START_OTR_VERIFY LPGEN("Beginning OTR encrypted session with '%s' (NOT VERIFIED)") +#define LANG_SESSION_TRY_CONTINUE_OTR LPGEN("Trying to refresh OTR encrypted session with '%s'") +#define LANG_SESSION_HAS_CONTINUE_OTR LPGEN("OTR encrypted session was refreshed by '%s'") +#define LANG_SESSION_HAS_CONTINUE_OTR_VERIFY LPGEN("OTR encrypted session was refreshed by '%s' (NOT VERIFIED)") +#define LANG_SESSION_REQUEST_CONTINUE_OTR LPGEN("OTR encrypted session with '%s' sucessfully refreshed") +#define LANG_SESSION_CONTINUE_OTR LPGEN("OTR encrypted session with '%s' sucessfully refreshed") +#define LANG_SESSION_CONTINUE_OTR_VERIFY LPGEN("OTR encrypted session with '%s' sucessfully refreshed (NOT VERIFIED)") +#define LANG_SESSION_TERMINATED_BY_OTR LPGEN("OTR encrypted session has been terminated by '%s'. You should do that, too!") +#define LANG_SESSION_NOT_STARTED_OTR LPGEN("OTR encrypted session with '%s' could not be started") + +#define LANG_ENCRYPTION_ERROR LPGEN("OTR could not encrypt when sending message") + +#define LANG_OTR_INFO LPGEN("OTR Information") +#define LANG_INLINE_PREFIX LPGEN("[OTR INFO] ") + +#define LANG_SMP_AUTH_FROM LPGEN("Authentication from %s") +#define LANG_SMP_AUTH LPGEN("Authenticae %s") + +#define LANG_UNKNOWN LPGEN("Unknown") +#define LANG_OTR_USERMESSAGE LPGEN("OTR: %s (%s)") +#define LANG_OTR_NOTIFY LPGEN("OTR %s (%s)") + +#define LANG_FINGERPRINT_CAPTION LPGEN("OTR New Fingerprint") +#define LANG_FINGERPRINT_ASK_VERIFY LPGEN("OTR encrypted session with '%s'.\n\nFingerprint is UNVERIFIED.\n\n%s\n\nVerify?") +#define LANG_FINGERPRINT_ASK_NEW LPGEN("A new fingerprint has been recieved from '%s'\n\n%s\n\nDo you trust it?") +#define LANG_FINGERPRINT_VERIFIED LPGEN("OTR encrypted session with '%s' is now using a VERIFIED fingerprint") +#define LANG_FINGERPRINT_NOT_VERIFIED LPGEN("OTR encrypted session with '%s' is now using a NOT VERIFIED fingerprint") + +#define LANG_ICON_UNVERIFIED LPGEN("OTR unverified") +#define LANG_ICON_FINISHED LPGEN("OTR finished") +#define LANG_ICON_PRIVATE LPGEN("OTR private") +#define LANG_ICON_NOT_PRIVATE LPGEN("OTR not secure") +#define LANG_ICON_OTR LPGEN("OTR") + +#define LANG_GENERATE_KEY LPGEN("Generating new private key for protocol '%s'.\nPlease Wait...") + +#define LANG_OPT_GENERAL LPGEN("General") +#define LANG_OPT_PROTO LPGEN("Protocols") +#define LANG_OPT_CONTACTS LPGEN("Contacts") +#define LANG_OPT_FINGER LPGEN("Fingerprints") + +#define LANG_CONTACT LPGEN("Contact") +#define LANG_PROTO LPGEN("Protocol") +#define LANG_POLICY LPGEN("Policy") +#define LANG_VERIFIED LPGEN("Verified") +#define LANG_ACTIVE LPGEN("Active") +#define LANG_FINGERPRINT LPGEN("Fingerprint") + +#define LANG_POLICY_DEFAULT LPGEN("Default") +#define LANG_POLICY_ALWAYS LPGEN("Always") +#define LANG_POLICY_OPP LPGEN("Opportunistic") +#define LANG_POLICY_MANUAL LPGEN("Manual") +#define LANG_POLICY_NEVER LPGEN("Never") + +#define LANG_OTR_ASK_NEWKEY LPGEN("Generating new key for '%s'. Continue?") +#define LANG_OTR_ASK_REMOVEKEY LPGEN("Removing key for '%s'. Continue?") + +#define LANG_OTR_FPVERIFY_TITLE LPGEN("OTR Fingerprint Verification") +#define LANG_OTR_FPVERIFY_DESC LPGEN("OTR encrypted session with '%s'.\nThe OTR fingerprint used by your contact is NOT VERIFIED.\nDo you trust it?") +#define LANG_OTR_FPVERIFIED_DESC LPGEN("OTR encrypted session with '%s'.\nThe OTR fingerprint used by your contact is already verified.\nDo you still trust it?") +#define LANG_YOUR_PRIVKEY LPGEN("Your Fingerprint to tell your contact (use a trusted channel!)") +#define LANG_CONTACT_FINGERPRINT LPGEN("VERIFY: Fingerprint from contact") + +#define LANG_FINGERPRINT_STILL_IN_USE LPGEN("Fingerprint '%s' still in use in conversation with '%s'. You cannot delete it!") +#define LANG_FINGERPRINT_NOT_DELETED LPGEN("Fingerprint '%s' in use in conversation with '%s'. It could not be deleted!") \ No newline at end of file diff --git a/MirOTR/options.cpp b/MirOTR/options.cpp new file mode 100644 index 0000000..993f7fd --- /dev/null +++ b/MirOTR/options.cpp @@ -0,0 +1,875 @@ +#include "stdafx.h" +#include "options.h" +#include "utils.h" +#include "m_options.h" +#include +#include +#include + +char g_fingerprint_store_filename[MAX_PATH]; +char g_private_key_filename[MAX_PATH]; +HANDLE hPATH_MIROTR; +Options options; +#define DATA_DIRECTORY MIRANDA_USERDATA "\\" MODULENAME +HANDLE hHookOptions; +struct PROTOREGENKEYOPTIONS { + HWND refresh; + TCHAR proto[129]; +}; + +void SetFilenames(const char *path) { + if (!path || !path[0]) return; + strcpy(g_fingerprint_store_filename, path); + strcpy(g_private_key_filename, path); + strcat(g_fingerprint_store_filename, ("\\")); + strcat(g_private_key_filename, ("\\")); + + strcat(g_fingerprint_store_filename, FINGERPRINT_STORE_FILENAME); + strcat(g_private_key_filename, PRIVATE_KEY_FILENAME); +} + +int FoldersChanged(WPARAM wParam, LPARAM lParam) { + char path[MAX_PATH]; + + FOLDERSGETDATA fgd = {0}; + fgd.cbSize = sizeof(FOLDERSGETDATA); + fgd.nMaxPathSize = MAX_PATH; + fgd.szPath = path; + + CallService(MS_FOLDERS_GET_PATH, (LPARAM)hPATH_MIROTR, (LPARAM)&fgd); + + SetFilenames(path); + ReadPrivkeyFiles(); + return 0; +} + +void LoadFilenames() { + char *path = Utils_ReplaceVars(DATA_DIRECTORY); + CallService(MS_UTILS_CREATEDIRTREE, 0, (LPARAM) path); + SetFilenames(path); + mir_free(path); + + if(ServiceExists(MS_FOLDERS_REGISTER_PATH)) { + FoldersRegisterCustomPath(MODULENAME, "Private Data", DATA_DIRECTORY); + HookEvent(ME_FOLDERS_PATH_CHANGED, FoldersChanged); + + // get the path - above are only defaults - there may be a different value in the db + FoldersChanged(0, 0); + } else { + ReadPrivkeyFiles(); + } +} + +void LoadOptions() { + options.default_policy = DBGetContactSettingWord(0, MODULENAME, "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; + } + options.err_method = (ErrorDisplay)DBGetContactSettingWord(0, MODULENAME, "ErrorDisplay", ED_POP); + options.prefix_messages = (DBGetContactSettingByte(0, MODULENAME, "PrefixMessages", 0) == 1); + options.msg_inline = (DBGetContactSettingByte(0, MODULENAME, "MsgInline", 0) == 1); + options.msg_popup = (DBGetContactSettingByte(0, MODULENAME, "MsgPopup", 1) == 1); + options.no_history = (DBGetContactSettingByte(0, MODULENAME, "NoHistory", 0) == 1); + options.autoshow_verify = (DBGetContactSettingByte(0, MODULENAME, "AutoShowVerify", 1) == 1); + + DBVARIANT dbv; + if(!DBGetContactSettingUTF8String(0, MODULENAME, "Prefix", &dbv)) { + strncpy(options.prefix, dbv.pszVal, OPTIONS_PREFIXLEN); + options.prefix[OPTIONS_PREFIXLEN-1] = 0; + DBFreeVariant(&dbv); + } else + strcpy(options.prefix, ("OTR: ")); + + options.timeout_finished = (DBGetContactSettingByte(0, MODULENAME, "TimeoutFinished", 0) == 1); + + options.end_offline = (DBGetContactSettingByte(0, MODULENAME, "EndOffline", 1) == 1); + options.end_window_close = (DBGetContactSettingByte(0, MODULENAME, "EndWindowClose", 0) == 1); + + options.bHaveMetaContacts = 0!=ServiceExists(MS_MC_GETMETACONTACT); + options.bHavePopups = 0!=ServiceExists(MS_POPUP_ADDPOPUPT) && ServiceExists(MS_POPUP_SHOWMESSAGE); + options.bHaveSRMMIcons = 0!=ServiceExists(MS_MSG_MODIFYICON); + + LoadFilenames(); +} + +extern "C" INT_PTR OpenOptions(WPARAM wParam, LPARAM lParam) +{ + OPTIONSDIALOGPAGE odp = { 0 }; + + odp.cbSize = sizeof(odp); + odp.position = 100; + odp.hInstance = hInst; + odp.ptszGroup = LPGENT("Plugins"); + odp.ptszTitle = _T(MODULENAME); + odp.flags = ODPF_BOLDGROUPS|ODPF_TCHAR; + + odp.ptszTab = _T(LANG_OPT_GENERAL); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_GENERAL); + odp.pfnDlgProc = DlgProcMirOTROpts; + CallService( MS_OPT_ADDPAGE, wParam,( LPARAM )&odp ); + + odp.ptszTab = _T(LANG_OPT_PROTO); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_PROTO); + odp.pfnDlgProc = DlgProcMirOTROptsProto; + CallService( MS_OPT_ADDPAGE, wParam,( LPARAM )&odp ); + + odp.ptszTab = _T(LANG_OPT_CONTACTS); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_CONTACTS); + odp.pfnDlgProc = DlgProcMirOTROptsContacts; + CallService( MS_OPT_ADDPAGE, wParam,( LPARAM )&odp ); + + odp.ptszTab = _T(LANG_OPT_FINGER); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_FINGER); + odp.pfnDlgProc = DlgProcMirOTROptsFinger; + CallService( MS_OPT_ADDPAGE, wParam,( LPARAM )&odp ); + + return 0; +} + +void InitOptions() { + LoadOptions(); + hHookOptions = HookEvent(ME_OPT_INITIALISE, OpenOptions); +} + +void DeinitOptions() { + UnhookEvent(hHookOptions); +} + +void SaveOptions() { + DBWriteContactSettingWord(0, MODULENAME, "DefaultPolicy", options.default_policy); + DBWriteContactSettingWord(0, MODULENAME, "ErrorDisplay", (int)options.err_method); + DBWriteContactSettingByte(0, MODULENAME, "PrefixMessages", options.prefix_messages ? 1 : 0); + DBWriteContactSettingByte(0, MODULENAME, "MsgInline", options.msg_inline ? 1 : 0); + DBWriteContactSettingByte(0, MODULENAME, "MsgPopup", options.msg_popup ? 1 : 0); + + DBWriteContactSettingByte(0, MODULENAME, "NoHistory", options.no_history ? 1 : 0); + DBWriteContactSettingByte(0, MODULENAME, "AutoShowVerify", options.autoshow_verify ? 1 : 0); + + /* + if (contact_policies) { + for(ContactPolicyMap::iterator i = contact_policies->begin(); i != contact_policies->end(); i++) { + DBWriteContactSettingWord(i->first, MODULENAME, "Policy", i->second); + } + } + */ + + DBWriteContactSettingStringUtf(0, MODULENAME, "Prefix", options.prefix); + + DBWriteContactSettingByte(0, MODULENAME, "TimeoutFinished", options.timeout_finished ? 1 : 0); + + DBWriteContactSettingByte(0, MODULENAME, "EndOffline", options.end_offline ? 1 : 0); + DBWriteContactSettingByte(0, MODULENAME, "EndWindowClose", options.end_window_close ? 1 : 0); +} + +extern "C" void set_context_contact(void *data, ConnContext *context) { + HANDLE hContact = find_contact(context->username, context->protocol); + context->app_data = hContact; + } + +void ReadPrivkeyFiles() { + DEBUGOUT_T("READ privkey"); + lib_cs_lock(); + otrl_privkey_read(otr_user_state, g_private_key_filename); + otrl_privkey_read_fingerprints(otr_user_state, g_fingerprint_store_filename, set_context_contact, 0); + lib_cs_unlock(); +} + +static INT_PTR CALLBACK DlgProcMirOTROpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + TCHAR *prefix; char* prefix_utf; + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + + // set default policy radio + 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; + } + + CheckDlgButton(hwndDlg, IDC_CHK_PREFIX, options.prefix_messages ? TRUE : FALSE); + CheckDlgButton(hwndDlg, IDC_CHK_MSGINLINE, options.msg_inline ? TRUE : FALSE); + CheckDlgButton(hwndDlg, IDC_CHK_MSGPOPUP, options.msg_popup ? TRUE : FALSE); + CheckDlgButton(hwndDlg, IDC_CHK_TIMEFINISH, options.timeout_finished ? TRUE : FALSE); + CheckDlgButton(hwndDlg, IDC_CHK_PREVENTSAVE, options.no_history ? TRUE : FALSE); + CheckDlgButton(hwndDlg, IDC_CHK_AUTOSHOW_VERIFY, options.autoshow_verify ? TRUE : FALSE); + CheckDlgButton(hwndDlg, IDC_CHK_ENDOFFLINE, options.end_offline ? TRUE : FALSE); + CheckDlgButton(hwndDlg, IDC_CHK_ENDCLOSE, options.end_window_close ? TRUE : FALSE); + + prefix = mir_utf8decodeT(options.prefix); + SetDlgItemText(hwndDlg, IDC_ED_PREFIX, prefix); + mir_free(prefix); + + return TRUE; + break; + + case WM_COMMAND: + switch ( HIWORD( wParam )) { + case BN_CLICKED: + switch ( LOWORD( wParam )) { + case IDC_RAD_OPP: + case IDC_RAD_MANUAL: + case IDC_RAD_ALWAYS: + case IDC_RAD_NEVER: + case IDC_CHK_PREFIX: + case IDC_CHK_MSGINLINE: + case IDC_CHK_MSGPOPUP: + case IDC_CHK_TIMEFINISH: + case IDC_CHK_PREVENTSAVE: + case IDC_CHK_AUTOSHOW_VERIFY: + case IDC_CHK_ENDOFFLINE: + case IDC_CHK_ENDCLOSE: + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + } + case EN_CHANGE: + if (LOWORD( wParam ) == IDC_ED_PREFIX && ( HWND )lParam == GetFocus()) + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + break; + } + break; + + case WM_NOTIFY: + + if (((LPNMHDR)lParam)->code == (UINT) PSN_APPLY ) { + // handle 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 + options.default_policy = OTRL_POLICY_NEVER; + + options.prefix_messages = (TRUE==IsDlgButtonChecked(hwndDlg, IDC_CHK_PREFIX)); + options.msg_inline = (TRUE==IsDlgButtonChecked(hwndDlg, IDC_CHK_MSGINLINE)); + options.msg_popup = (TRUE==IsDlgButtonChecked(hwndDlg, IDC_CHK_MSGPOPUP)); + options.timeout_finished = (TRUE==IsDlgButtonChecked(hwndDlg, IDC_CHK_TIMEFINISH)); + options.no_history = (TRUE==IsDlgButtonChecked(hwndDlg, IDC_CHK_PREVENTSAVE)); + options.autoshow_verify = (TRUE==IsDlgButtonChecked(hwndDlg, IDC_CHK_AUTOSHOW_VERIFY)); + options.end_offline = (TRUE==IsDlgButtonChecked(hwndDlg, IDC_CHK_ENDOFFLINE)); + options.end_window_close = (TRUE==IsDlgButtonChecked(hwndDlg, IDC_CHK_ENDCLOSE)); + + prefix = (TCHAR*)mir_alloc(sizeof(TCHAR)*OPTIONS_PREFIXLEN); + GetDlgItemText(hwndDlg, IDC_ED_PREFIX, prefix, OPTIONS_PREFIXLEN); + prefix_utf = mir_utf8encodeT(prefix); + mir_free(prefix); + strncpy(options.prefix, prefix_utf, OPTIONS_PREFIXLEN); + mir_free(prefix_utf); + + SaveOptions(); + + return TRUE; + } + + break; + } + return FALSE; +} + +static unsigned int CALLBACK regen_key_thread(void* param) { + CallService(MS_SYSTEM_THREAD_PUSH, 0, 0); + PROTOREGENKEYOPTIONS *opts = (PROTOREGENKEYOPTIONS *)param; + TCHAR *buff = (TCHAR*) mir_alloc(512*sizeof(TCHAR)); + mir_sntprintf(buff, 512, TranslateT(LANG_OTR_ASK_NEWKEY), opts->proto); + EnableWindow(opts->refresh, FALSE); + if (IDYES == MessageBox(opts->refresh, buff, TranslateT(LANG_OTR_INFO), MB_ICONQUESTION|MB_YESNO)) + { + mir_free(buff); + + char* proto = mir_t2a(opts->proto); + otr_gui_create_privkey(0, proto, proto); + SendMessage(opts->refresh, WMU_REFRESHPROTOLIST, 0, 0); + /* + char *fp = (char*)mir_alloc(20); + otrl_privkey_fingerprint(otr_user_state, fp, proto, proto); + TCHAR *fpt = mir_a2t(fp); + mir_free(fp); + ListView_SetItemText(GetDlgItem(hwndDlg, IDC_LV_PROTO_PROTOS), sel, 2, fpt); + mir_free(fpt); + mir_free(proto); + */ + } else { + mir_free(buff); + } + EnableWindow(opts->refresh, TRUE); + delete opts; + CallService(MS_SYSTEM_THREAD_POP, 0, 0); + return 0; +} + +static INT_PTR CALLBACK DlgProcMirOTROptsProto(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + HWND lv; + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + lv = GetDlgItem(hwndDlg, IDC_LV_PROTO_PROTOS); + + { + HWND cmb = GetDlgItem(hwndDlg, IDC_CMB_PROTO_POLICY); + SendMessage(cmb, CB_ADDSTRING, 0, (WPARAM)TranslateT(LANG_POLICY_DEFAULT)); + SendMessage(cmb, CB_ADDSTRING, 0, (WPARAM)TranslateT(LANG_POLICY_ALWAYS)); + SendMessage(cmb, CB_ADDSTRING, 0, (WPARAM)TranslateT(LANG_POLICY_OPP)); + SendMessage(cmb, CB_ADDSTRING, 0, (WPARAM)TranslateT(LANG_POLICY_MANUAL)); + SendMessage(cmb, CB_ADDSTRING, 0, (WPARAM)TranslateT(LANG_POLICY_NEVER)); + SendMessage(GetDlgItem(hwndDlg, IDC_CMB_PROTO_POLICY), CB_SETCURSEL, (LPARAM)-1, 0); + EnableWindow(GetDlgItem(hwndDlg, IDC_CMB_PROTO_POLICY), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_PROTO_NEWKEY), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_PROTO_FORGET), FALSE); + } + + SendMessage(lv,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 = TranslateT(LANG_PROTO); + lvc.cx = 85; // width of column in pixels + ListView_InsertColumn(lv, 0, &lvc); + + lvc.iSubItem = 1; + lvc.pszText = TranslateT(LANG_POLICY); + lvc.cx = 80; // width of column in pixels + ListView_InsertColumn(lv, 1, &lvc); + + lvc.iSubItem = 2; + lvc.pszText = TranslateT(LANG_FINGERPRINT); + lvc.cx = 275; // width of column in pixels + ListView_InsertColumn(lv, 2, &lvc); + } + PostMessage(hwndDlg, WMU_REFRESHPROTOLIST, 0, 0); + + return TRUE; + case WMU_REFRESHPROTOLIST: + { + lv = GetDlgItem(hwndDlg, IDC_LV_PROTO_PROTOS); + ListView_DeleteAllItems(lv); + int num_protocols; + PROTOCOLDESCRIPTOR **pppDesc; + int ilvItem; + LV_ITEM item = {0}; + char fprint[45]; + TCHAR* temp; + //BOOL unicode = ListView_GetUnicodeFormat(lv); + + CallService(MS_PROTO_ENUMPROTOCOLS, (LPARAM)&num_protocols, (WPARAM)&pppDesc); + for(int i = 0; i < num_protocols; i++) { + if(pppDesc[i]->type == PROTOTYPE_PROTOCOL && (!g_metaproto || strcmp(pppDesc[i]->szName, g_metaproto) != 0) + && (CallProtoService(pppDesc[i]->szName, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_IM) == PF1_IM) + { + //if (unicode) { + item.mask = LVIF_TEXT; + temp = mir_a2t(pppDesc[i]->szName); + item.pszText = temp; + ilvItem = ListView_InsertItem(lv, &item); + mir_free(temp); + + + ListView_SetItemText(lv, ilvItem, 1, (TCHAR*)policy_to_string(db_dword_get(0,MODULENAME"_ProtoPol", pppDesc[i]->szName, CONTACT_DEFAULT_POLICY)) ); + if(otrl_privkey_fingerprint(otr_user_state, fprint, pppDesc[i]->szName, pppDesc[i]->szName)) { + temp = mir_a2t(fprint); + ListView_SetItemText(lv, ilvItem, 2, temp); + mir_free(temp); + } + /*} else { //using ASCII + item.pszText = (TCHAR*)pppDesc[i]->szName; + ilvItem = ListView_InsertItem(lv, &item); + if(otrl_privkey_fingerprint(otr_user_state, fprint, pppDesc[i]->szName, pppDesc[i]->szName)) { + ListView_SetItemText(lv, ilvItem, 2, (TCHAR*)fprint); + } + }*/ + + + } + } + } + return TRUE; + case WM_COMMAND: + switch ( HIWORD( wParam )) { + case BN_CLICKED: + switch ( LOWORD( wParam )) { + case IDC_BTN_PROTO_NEWKEY: + { + int sel = ListView_GetSelectionMark(GetDlgItem(hwndDlg, IDC_LV_PROTO_PROTOS)); + if (sel != -1) { + PROTOREGENKEYOPTIONS *opts = new PROTOREGENKEYOPTIONS(); + opts->refresh = hwndDlg; + ListView_GetItemText(GetDlgItem(hwndDlg, IDC_LV_PROTO_PROTOS), sel, 0, opts->proto, 128); + CloseHandle((HANDLE)_beginthreadex(0, 0, regen_key_thread, opts, 0, 0)); + } + }break; + case IDC_BTN_PROTO_FORGET: + { + int sel = ListView_GetSelectionMark(GetDlgItem(hwndDlg, IDC_LV_PROTO_PROTOS)); + if (sel != -1) { + TCHAR buff_proto[128]; + ListView_GetItemText(GetDlgItem(hwndDlg, IDC_LV_PROTO_PROTOS), sel, 0, buff_proto, 128); + TCHAR buff[512]; + mir_sntprintf(buff, 512, TranslateT(LANG_OTR_ASK_REMOVEKEY), buff_proto); + if (IDYES == MessageBox(hwndDlg, buff, TranslateT(LANG_OTR_INFO), MB_ICONQUESTION|MB_YESNO)) + { + char* proto = mir_t2a(buff_proto); + OtrlPrivKey *key = otrl_privkey_find(otr_user_state, proto, proto); + mir_free(proto); + if (key) { + otrl_privkey_forget(key); + otrl_privkey_write(otr_user_state, g_private_key_filename); + ListView_SetItemText(GetDlgItem(hwndDlg, IDC_LV_PROTO_PROTOS), sel, 2, _T("")); + } + } + } + }break; + } + case CBN_SELCHANGE: + switch ( LOWORD( wParam )) { + case IDC_CMB_PROTO_POLICY: + { + int proto = ListView_GetSelectionMark(GetDlgItem(hwndDlg, IDC_LV_PROTO_PROTOS)); + if (proto == -1) break; + int sel = SendDlgItemMessage(hwndDlg, IDC_CMB_PROTO_POLICY, CB_GETCURSEL, 0, 0); + if (sel==CB_ERR) break; + int len = SendDlgItemMessage(hwndDlg, IDC_CMB_PROTO_POLICY, CB_GETLBTEXTLEN, sel, 0); + if (len < 0) break; + TCHAR *text = new TCHAR[len+1]; + SendDlgItemMessage(hwndDlg, IDC_CMB_PROTO_POLICY, CB_GETLBTEXT, sel, (LPARAM)text); + ListView_SetItemText(GetDlgItem(hwndDlg, IDC_LV_PROTO_PROTOS), proto, 1, text); + delete text; + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + }break; + } + break; + } + break; + + case WM_NOTIFY: + if (((LPNMHDR) lParam)->code == (UINT) LVN_ITEMCHANGED && ((LPNMHDR) lParam)->hwndFrom == GetDlgItem(hwndDlg, IDC_LV_PROTO_PROTOS) + && (((LPNMLISTVIEW)lParam)->uNewState & LVIS_SELECTED )) { + int sel = ListView_GetSelectionMark(((LPNMHDR) lParam)->hwndFrom); + if (sel == -1) { + SendMessage(GetDlgItem(hwndDlg, IDC_CMB_PROTO_POLICY), CB_SETCURSEL, (LPARAM)-1, 0); + EnableWindow(GetDlgItem(hwndDlg, IDC_CMB_PROTO_POLICY), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_PROTO_NEWKEY), FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_PROTO_FORGET), FALSE); + } else { + EnableWindow(GetDlgItem(hwndDlg, IDC_CMB_PROTO_POLICY), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_PROTO_NEWKEY), TRUE); + EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_PROTO_FORGET), TRUE); + TCHAR buff[50]; + ListView_GetItemText(((LPNMHDR)lParam)->hwndFrom, sel, 1, buff, 50); + SendMessage(GetDlgItem(hwndDlg, IDC_CMB_PROTO_POLICY), CB_SELECTSTRING, (LPARAM)-1, (WPARAM)buff); + } + + } else if (((LPNMHDR)lParam)->code == (UINT) PSN_APPLY ) { + lv = GetDlgItem(hwndDlg, IDC_LV_PROTO_PROTOS); + int cnt = ListView_GetItemCount(lv); + TCHAR proto_t[128], policy[64]; + char* proto; + for (int i = 0; i < cnt; ++i) { + ListView_GetItemText(lv, i, 0, proto_t, 128); + ListView_GetItemText(lv, i, 1, policy, 64); + proto = mir_t2a(proto_t); + db_dword_set(0, MODULENAME"_ProtoPol", proto, policy_from_string(policy)); + mir_free(proto); + } + // handle apply + return TRUE; + } + break; + } + return FALSE; +} + +static INT_PTR CALLBACK DlgProcMirOTROptsContacts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch ( msg ) { + case WM_INITDIALOG: + { + TranslateDialogDefault( hwndDlg ); + + SetWindowLongPtr(hwndDlg, GWL_USERDATA, (ULONG_PTR) new ContactPolicyMap()); + + HWND cmb = GetDlgItem(hwndDlg, IDC_CMB_CONT_POLICY); + SendMessage(cmb, CB_ADDSTRING, 0, (WPARAM)TranslateT(LANG_POLICY_DEFAULT)); + SendMessage(cmb, CB_ADDSTRING, 0, (WPARAM)TranslateT(LANG_POLICY_ALWAYS)); + SendMessage(cmb, CB_ADDSTRING, 0, (WPARAM)TranslateT(LANG_POLICY_OPP)); + SendMessage(cmb, CB_ADDSTRING, 0, (WPARAM)TranslateT(LANG_POLICY_MANUAL)); + SendMessage(cmb, CB_ADDSTRING, 0, (WPARAM)TranslateT(LANG_POLICY_NEVER)); + EnableWindow(GetDlgItem(hwndDlg, IDC_CMB_CONT_POLICY), FALSE); + + SendDlgItemMessage(hwndDlg, IDC_LV_CONT_CONTACTS ,LVM_SETEXTENDEDLISTVIEWSTYLE, 0,LVS_EX_FULLROWSELECT);// | LVS_EX_CHECKBOXES); + + + HWND lv = GetDlgItem(hwndDlg, IDC_LV_CONT_CONTACTS); + + + // 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 = TranslateT(LANG_CONTACT); + lvc.cx = 150; // width of column in pixels + ListView_InsertColumn(lv, 0, &lvc); + + lvc.iSubItem = 1; + lvc.pszText = TranslateT(LANG_PROTO); + lvc.cx = 150; // width of column in pixels + ListView_InsertColumn(lv, 1, &lvc); + + lvc.iSubItem = 2; + lvc.pszText = TranslateT(LANG_POLICY); + lvc.cx = 100; // width of column in pixels + ListView_InsertColumn(lv, 2, &lvc); + } + SendMessage(hwndDlg, WMU_REFRESHLIST, 0, 0); + + return TRUE; + case WMU_REFRESHLIST: + { + HWND lv = GetDlgItem(hwndDlg, IDC_LV_CONT_CONTACTS); + + ListView_DeleteAllItems(lv); + + 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; + + const char *proto; + TCHAR *proto_t; + HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + while ( hContact != NULL ) + { + proto = contact_get_proto(hContact); + if(proto && db_byte_get(hContact, proto, "ChatRoom", 0) == 0 && CallService(MS_PROTO_ISPROTOONCONTACT, (WPARAM)hContact, (LPARAM)MODULENAME) // ignore chatrooms + && (g_metaproto == 0 || strcmp(proto, g_metaproto) != 0)) // and MetaContacts + { + lvI.iItem = 0; + lvI.iSubItem = 0; + lvI.lParam = (LPARAM)hContact; + lvI.pszText = (TCHAR*)contact_get_nameT(hContact); + lvI.iItem = ListView_InsertItem(lv , &lvI); + + proto_t = mir_a2t(proto); + ListView_SetItemText(lv, lvI.iItem, 1, proto_t); + mir_free(proto_t); + + ListView_SetItemText(lv, lvI.iItem, 2, (TCHAR*)policy_to_string((OtrlPolicy)db_dword_get(hContact, MODULENAME, "Policy", CONTACT_DEFAULT_POLICY)) ); + } + + + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } + } + return TRUE; + break; + case WM_COMMAND: + switch ( HIWORD( wParam )) { + case CBN_SELCHANGE: + switch ( LOWORD( wParam )) { + case IDC_CMB_CONT_POLICY: + { + HANDLE hContact = 0; + int iUser = ListView_GetSelectionMark(GetDlgItem(hwndDlg, IDC_LV_CONT_CONTACTS)); + if (iUser == -1) break; + int sel = SendDlgItemMessage(hwndDlg, IDC_CMB_CONT_POLICY, CB_GETCURSEL, 0, 0); + if (sel==CB_ERR) break; + int len = SendDlgItemMessage(hwndDlg, IDC_CMB_CONT_POLICY, CB_GETLBTEXTLEN, sel, 0); + if (len < 0) break; + TCHAR *text = new TCHAR[len+1]; + SendDlgItemMessage(hwndDlg, IDC_CMB_CONT_POLICY, CB_GETLBTEXT, sel, (LPARAM)text); + ListView_SetItemText(GetDlgItem(hwndDlg, IDC_LV_CONT_CONTACTS), iUser, 2, text); + OtrlPolicy policy = policy_from_string(text); + delete text; + LVITEM lvi = {0}; + lvi.mask = LVIF_PARAM; + lvi.iItem = iUser; + lvi.iSubItem = 0; + ListView_GetItem(GetDlgItem(hwndDlg, IDC_LV_CONT_CONTACTS), &lvi); + ContactPolicyMap* cpm = (ContactPolicyMap*) GetWindowLongPtr(hwndDlg, GWL_USERDATA); + hContact = (HANDLE)lvi.lParam; + (*cpm)[hContact] = policy; + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + }break; + } + break; + } + break; + + case WM_NOTIFY: + if (((LPNMHDR) lParam)->code == (UINT) LVN_ITEMCHANGED && ((LPNMHDR) lParam)->hwndFrom == GetDlgItem(hwndDlg, IDC_LV_CONT_CONTACTS) + && (((LPNMLISTVIEW)lParam)->uNewState & LVIS_SELECTED )) { + int sel = ListView_GetSelectionMark(((LPNMHDR) lParam)->hwndFrom); + if (sel == -1) { + SendMessage(GetDlgItem(hwndDlg, IDC_CMB_CONT_POLICY), CB_SETCURSEL, (LPARAM)-1, 0); + EnableWindow(GetDlgItem(hwndDlg, IDC_CMB_CONT_POLICY), FALSE); + } else { + EnableWindow(GetDlgItem(hwndDlg, IDC_CMB_CONT_POLICY), TRUE); + TCHAR buff[50]; + ListView_GetItemText(((LPNMHDR)lParam)->hwndFrom, sel, 2, buff, 50); + SendMessage(GetDlgItem(hwndDlg, IDC_CMB_CONT_POLICY), CB_SELECTSTRING, (LPARAM)-1, (WPARAM)buff); + } + + } else if (((LPNMHDR)lParam)->code == (UINT) PSN_APPLY ) { + // handle apply + + ContactPolicyMap *cpm = (ContactPolicyMap*) GetWindowLongPtr(hwndDlg, GWL_USERDATA); + // Iterate over the map and print out all key/value pairs. + // Using a const_iterator since we are not going to change the values. + for(ContactPolicyMap::const_iterator it = cpm->begin(); it != cpm->end(); ++it) + { + if (!it->first) continue; + DBWriteContactSettingDword(it->first, MODULENAME, "Policy", (DWORD)it->second); + } + return TRUE; + } + break; + case WM_DESTROY: + ContactPolicyMap *cpm = (ContactPolicyMap*) GetWindowLongPtr(hwndDlg, GWL_USERDATA); + cpm->clear(); + delete cpm; + break; + } + return FALSE; +} + +static INT_PTR CALLBACK DlgProcMirOTROptsFinger(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + SetWindowLongPtr(hwndDlg, GWL_USERDATA, (ULONG_PTR) new FPModifyMap()); + + SendDlgItemMessage(hwndDlg, IDC_LV_FINGER_LIST ,LVM_SETEXTENDEDLISTVIEWSTYLE, 0,LVS_EX_FULLROWSELECT);// | LVS_EX_CHECKBOXES); + + { + HWND lv = GetDlgItem(hwndDlg, IDC_LV_FINGER_LIST); + // 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 = TranslateT(LANG_CONTACT); + lvc.cx = 100; // width of column in pixels + ListView_InsertColumn(lv, 0, &lvc); + + lvc.iSubItem = 1; + lvc.pszText = TranslateT(LANG_PROTO); + lvc.cx = 90; // width of column in pixels + ListView_InsertColumn(lv, 1, &lvc); + + lvc.iSubItem = 2; + lvc.pszText = TranslateT(LANG_ACTIVE); + lvc.cx = 50; // width of column in pixels + ListView_InsertColumn(lv, 2, &lvc); + + lvc.iSubItem = 3; + lvc.pszText = TranslateT(LANG_VERIFIED); + lvc.cx = 50; // width of column in pixels + ListView_InsertColumn(lv, 3, &lvc); + + lvc.iSubItem = 4; + lvc.pszText = TranslateT(LANG_FINGERPRINT); + lvc.cx = 300; // width of column in pixels + ListView_InsertColumn(lv, 4, &lvc); + } + SendMessage(hwndDlg, WMU_REFRESHLIST, 0, 0); + + return TRUE; + + case WMU_REFRESHLIST: + //enumerate contacts, fill in list + { + HWND lv = GetDlgItem(hwndDlg, IDC_LV_FINGER_LIST); + ListView_DeleteAllItems(lv); + + 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; + ConnContext * context = otr_user_state->context_root; + TCHAR *proto, *user, hash[45] = {0}; + Fingerprint *fp; + while (context) { + if (context->app_data) { + user = (TCHAR*)contact_get_nameT((HANDLE)context->app_data); + if (user) { + proto = mir_a2t(context->protocol); + fp = context->fingerprint_root.next; + while(fp) { + otrl_privkey_hash_to_humanT(hash, fp->fingerprint); + if (hash) { + lvI.iSubItem = 0; + lvI.lParam = (LPARAM)fp; + lvI.pszText = user; + int d = ListView_InsertItem(lv, &lvI); + + ListView_SetItemText(lv,d, 1, proto); + ListView_SetItemText(lv,d, 2, (context->active_fingerprint == fp)? TranslateT(LANG_YES) : TranslateT(LANG_NO)); + ListView_SetItemText(lv,d, 3, (fp->trust && fp->trust != '\0')? TranslateT(LANG_YES) : TranslateT(LANG_NO)); + ListView_SetItemText(lv,d, 4, hash ); + } + fp = fp->next; + } + mir_free(proto); + } + } + context = context->next; + } + } + + return TRUE; + case WM_COMMAND: + switch ( HIWORD( wParam )) { + case BN_CLICKED: + switch ( LOWORD( wParam )) { + case IDC_BTN_FINGER_DONTTRUST: + { + int sel = ListView_GetSelectionMark(GetDlgItem(hwndDlg, IDC_LV_FINGER_LIST)); + if (sel != -1) { + LVITEM lvi = {0}; + lvi.mask = LVIF_PARAM; + lvi.iItem = sel; + Fingerprint *fp = NULL; + ListView_GetItem(GetDlgItem(hwndDlg, IDC_LV_FINGER_LIST), &lvi); + fp = (Fingerprint*) lvi.lParam; + FPModifyMap* fpm = (FPModifyMap*) GetWindowLongPtr(hwndDlg, GWL_USERDATA); + (*fpm)[fp] = FPM_NOTRUST; + ListView_SetItemText(GetDlgItem(hwndDlg, IDC_LV_FINGER_LIST), sel, 3, TranslateT(LANG_NO)); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + }break; + case IDC_BTN_FINGER_TRUST: + { + int sel = ListView_GetSelectionMark(GetDlgItem(hwndDlg, IDC_LV_FINGER_LIST)); + if (sel != -1) { + LVITEM lvi = {0}; + lvi.mask = LVIF_PARAM; + lvi.iItem = sel; + Fingerprint *fp = NULL; + ListView_GetItem(GetDlgItem(hwndDlg, IDC_LV_FINGER_LIST), &lvi); + fp = (Fingerprint*) lvi.lParam; + FPModifyMap* fpm = (FPModifyMap*) GetWindowLongPtr(hwndDlg, GWL_USERDATA); + (*fpm)[fp] = FPM_VERIFY; + ListView_SetItemText(GetDlgItem(hwndDlg, IDC_LV_FINGER_LIST), sel, 3, TranslateT(LANG_YES)); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + }break; + case IDC_BTN_FINGER_FORGET: + { + int sel = ListView_GetSelectionMark(GetDlgItem(hwndDlg, IDC_LV_FINGER_LIST)); + if (sel != -1) { + LVITEM lvi = {0}; + lvi.mask = LVIF_PARAM; + lvi.iItem = sel; + Fingerprint *fp = NULL; + ListView_GetItem(GetDlgItem(hwndDlg, IDC_LV_FINGER_LIST), &lvi); + fp = (Fingerprint*) lvi.lParam; + if (fp->context->active_fingerprint == fp) { + TCHAR buff[1024], hash[45]; + otrl_privkey_hash_to_humanT(hash, fp->fingerprint); + mir_sntprintf(buff, 1024, TranslateT(LANG_FINGERPRINT_STILL_IN_USE), hash, contact_get_nameT((HANDLE)fp->context->app_data)); + ShowError(buff); + } else { + FPModifyMap* fpm = (FPModifyMap*) GetWindowLongPtr(hwndDlg, GWL_USERDATA); + (*fpm)[fp] = FPM_DELETE; + ListView_DeleteItem(GetDlgItem(hwndDlg, IDC_LV_FINGER_LIST), sel); + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + } + }break; + } + } + break; + + case WM_NOTIFY: + if (((LPNMHDR)lParam)->code == (UINT) PSN_APPLY ) { + // handle apply + + FPModifyMap *fpm = (FPModifyMap*) GetWindowLongPtr(hwndDlg, GWL_USERDATA); + // Iterate over the map and print out all key/value pairs. + // Using a const_iterator since we are not going to change the values. + for(FPModifyMap::const_iterator it = fpm->begin(); it != fpm->end(); ++it) + { + if (!it->first) continue; + switch (it->second) { + case FPM_DELETE: + if (it->first->context->active_fingerprint == it->first) { + TCHAR buff[1024], hash[45]; + otrl_privkey_hash_to_humanT(hash, it->first->fingerprint); + mir_sntprintf(buff, 1024, TranslateT(LANG_FINGERPRINT_NOT_DELETED), hash, contact_get_nameT((HANDLE)it->first->context->app_data)); + ShowError(buff); + } else { + otrl_context_forget_fingerprint(it->first, 1); + } + break; + case FPM_VERIFY: + otrl_context_set_trust(it->first, "verified"); + if (it->first == it->first->context->active_fingerprint) + SetEncryptionStatus((HANDLE)it->first->context->app_data, otr_context_get_trust(it->first->context)); + break; + case FPM_NOTRUST: + otrl_context_set_trust(it->first, NULL); + if (it->first == it->first->context->active_fingerprint) + SetEncryptionStatus((HANDLE)it->first->context->app_data, otr_context_get_trust(it->first->context)); + break; + } + } + if (!fpm->empty()) otr_gui_write_fingerprints(0); + fpm->clear(); + SendMessage(hwndDlg, WMU_REFRESHLIST, 0, 0); + + return TRUE; + } + break; + case WM_DESTROY: + FPModifyMap *fpm = (FPModifyMap*) GetWindowLongPtr(hwndDlg, GWL_USERDATA); + fpm->clear(); + delete fpm; + break; + } + + return FALSE; +} \ No newline at end of file diff --git a/MirOTR/options.h b/MirOTR/options.h new file mode 100644 index 0000000..94149d1 --- /dev/null +++ b/MirOTR/options.h @@ -0,0 +1,66 @@ +#ifndef _OPTIONS_INC +#define _OPTIONS_INC + +//#include "stdafx.h" + +extern char* g_metaproto; + +#define PRIVATE_KEY_FILENAME "otr.private_key" +#define FINGERPRINT_STORE_FILENAME "otr.fingerprints" + +extern char g_fingerprint_store_filename[MAX_PATH]; +extern char g_private_key_filename[MAX_PATH]; + +#define CONTACT_DEFAULT_POLICY 0xFFFF + +typedef enum {ED_POP, ED_BAL, ED_MB} ErrorDisplay; + +#define OPTIONS_PREFIXLEN 64 + +typedef struct { + OtrlPolicy default_policy; + ErrorDisplay err_method; + bool prefix_messages; + bool msg_inline; + bool msg_popup; + char prefix[OPTIONS_PREFIXLEN]; + + bool timeout_finished; + + bool no_history; + bool autoshow_verify; + + bool end_offline, end_window_close; + + // temporary options + bool bHaveMetaContacts, bHavePopups, bHaveSRMMIcons; + +} Options; + +extern Options options; + +#define WMU_REFRESHLIST (WM_USER + 241) +#define WMU_REFRESHPROTOLIST (WM_USER + 242) +#define CONTACT_DEFAULT_POLICY 0xFFFF + +#include +typedef std::map ContactPolicyMap; +typedef enum {FPM_VERIFY, FPM_NOTRUST, FPM_DELETE } FPModify; +typedef std::map FPModifyMap; + +int OptInit(WPARAM wParam, LPARAM lParam); + +void LoadOptions(); +void SaveOptions(ContactPolicyMap *contact_policies); +extern "C" INT_PTR OpenOptions(WPARAM wParam, LPARAM lParam); +void InitOptions(); +void DeinitOptions(); + +void ReadPrivkeyFiles(); + +static INT_PTR CALLBACK DlgProcMirOTROpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); +static INT_PTR CALLBACK DlgProcMirOTROptsProto(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); +static INT_PTR CALLBACK DlgProcMirOTROptsContacts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); +static INT_PTR CALLBACK DlgProcMirOTROptsFinger(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); + +#endif diff --git a/MirOTR/otr.cpp b/MirOTR/otr.cpp new file mode 100644 index 0000000..9779800 --- /dev/null +++ b/MirOTR/otr.cpp @@ -0,0 +1,383 @@ +#include +#include "otr.h" +#include + +OtrlUserState otr_user_state; +#define WMU_ENDDIALOG (WM_USER+244) + +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, + max_message_size, + account_name, + account_name_free +}; + +struct GenKeyData{ + HWND dialog; + char *proto; +}; + +static unsigned int CALLBACK generate_key_thread(void* param) { + CallService(MS_SYSTEM_THREAD_PUSH, 0, 0); + GenKeyData *data = (GenKeyData *)param; + lib_cs_lock(); + otrl_privkey_generate(otr_user_state, g_private_key_filename, data->proto, data->proto); + lib_cs_unlock(); + PostMessage(data->dialog, WMU_ENDDIALOG, 0, 0); + CallService(MS_SYSTEM_THREAD_POP, 0, 0); + return 0; +} + +INT_PTR CALLBACK GenKeyDlgFunc(HWND hWndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { + switch(msg) { + case WM_INITDIALOG: + { + if (!lParam) { + EndDialog(hWndDlg, 0); + return 0; + } + TranslateDialogDefault(hWndDlg); + SetClassLongPtr(hWndDlg, GCL_HICON, (LONG_PTR)LoadIcon(ICON_OTR,1) ); + TCHAR buff[256]; + TCHAR *proto = mir_a2t((char*)lParam); + mir_sntprintf(buff, 256, TranslateT(LANG_GENERATE_KEY), proto); + mir_free(proto); + SetDlgItemText(hWndDlg, IDC_GENERATE, buff); + GenKeyData *data = new GenKeyData(); + data->dialog = hWndDlg; + data->proto = (char*)lParam; + CloseHandle((HANDLE)_beginthreadex(0, 0, generate_key_thread, (void*)data, 0, 0)); + }break; + case WMU_ENDDIALOG: + EndDialog(hWndDlg, 0); + return TRUE; + case WM_DESTROY: + SetClassLongPtr(hWndDlg, GCL_HICON, 0); + ReleaseIcon(ICON_OTR,1); + } + return FALSE; +} + +extern "C" { + /* Return the OTR policy for the given context. */ + OtrlPolicy otr_gui_policy(void *opdata, ConnContext *context) { + DEBUGOUT_T("OTR_GUI_POLICY") + HANDLE hContact = (HANDLE)opdata; + DWORD pol; + if(hContact) { + pol = DBGetContactSettingDword(hContact, MODULENAME, "Policy", CONTACT_DEFAULT_POLICY); + if(pol != CONTACT_DEFAULT_POLICY) return pol ; + } + if(context->protocol) { + pol = db_dword_get(0,MODULENAME"_ProtoPol", context->protocol, CONTACT_DEFAULT_POLICY); + if(pol != CONTACT_DEFAULT_POLICY) return pol ; + } + + return options.default_policy ; + } + + /* Create a private key for the given accountname/protocol if + * desired. */ + void otr_gui_create_privkey(void *opdata, const char *account_name, const char *protocol) { + DEBUGOUT_T("OTR_GUI_CREATE_PRIVKEY") + //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, Global::mainThread, (DWORD)nkd); + DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_GENKEYNOTIFY), GetDesktopWindow(), GenKeyDlgFunc, (LPARAM)protocol ); + + } + + /* 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. */ + int otr_gui_is_logged_in(void *opdata, const char *accountname, const char *protocol, const char *recipient) { + DEBUGOUT_T("OTR_GUI_IS_LOGGED_IN") + HANDLE hContact = (HANDLE)opdata; + if(hContact) { + WORD status = DBGetContactSettingWord(hContact, contact_get_proto(hContact), "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. */ + void otr_gui_inject_message(void *opdata, const char *accountname, const char *protocol, const char *recipient, const char *message) { + DEBUGOUT_T("OTR_GUI_INJECT_MESSAGE") + //MessageBox(0, message, "OTR Inject", MB_OK); + HANDLE hContact = (HANDLE)opdata; + + if(protocol && DBGetContactSettingWord(hContact, protocol, "Status", ID_STATUS_OFFLINE) != ID_STATUS_OFFLINE) { + /* this would be with translation + TCHAR *decode = mir_utf8decodeT(message); + TCHAR *encode = mir_utf8encodeT(TranslateT(decide)); + mir_free(decode); + CallContactService(hContact, PSS_MESSAGE, PREF_UTF|PREF_BYPASS_OTR, (LPARAM)encode); + mir_free(encode); + */ + CallContactService(hContact, PSS_MESSAGE, PREF_UTF|PREF_BYPASS_OTR, (LPARAM)message); + //DEBUGOUT_T("INJECT") + } + } + + /* Display a notification message for a particular accountname / + * protocol / username conversation. */ + 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) { + DEBUGOUT_T("OTR_GUI_NOTIFY") + const TCHAR* uname = contact_get_nameT((HANDLE)opdata); + TCHAR* title_t = mir_utf8decodeT(title); + TCHAR *notify = TranslateT(LANG_OTR_NOTIFY); + + int len = _tcslen(uname) + _tcslen(title_t) + _tcslen(notify); + TCHAR *buff1 = new TCHAR[len]; + mir_sntprintf(buff1, len, notify, TranslateTS(title_t), uname); + mir_free(title_t); + + len = strlen(primary) + strlen(secondary) + 5; + char *buff2 = new char[len]; + mir_snprintf(buff2, len, "%s\n%s", Translate(primary), Translate(secondary)); + TCHAR* buff2_t = mir_utf8decodeT(buff2); + delete buff2; + + DEBUGOUT_T("OTR_GUI_NOTIFY") + + ShowPopup(buff1, buff2_t, 0); + mir_free(buff2_t); + delete buff1; + } + + /* 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. */ + int otr_gui_display_otr_message(void *opdata, const char *accountname, const char *protocol, const char *username, const char *msg) { + DEBUGOUT_T("OTR_GUI_DISPLAY_OTR_MESSAGE") + if(options.msg_inline) + ShowMessageInlineUtf((HANDLE)opdata, Translate(msg)); + if(options.msg_popup) { + TCHAR buff[512]; + TCHAR* proto = mir_a2t(protocol); + mir_sntprintf(buff, 512, TranslateT(LANG_OTR_USERMESSAGE), contact_get_nameT((HANDLE)opdata), proto); + mir_free(proto); + TCHAR *msg_t = mir_utf8decodeT(msg); + ShowPopup(buff, TranslateTS(msg_t), 0); + mir_free(msg_t); + } + return 0; + } + + /* When the list of ConnContexts changes (including a change in + * state), this is called so the UI can be updated. */ + void otr_gui_update_context_list(void *opdata) { + //MessageBox(0, "Update Context List", "OTR Callback", MB_OK); + DEBUGOUT_T("OTR: Update Context List"); + } + + /* Return a newly-allocated string containing a human-friendly name + * for the given protocol id */ + const char *otr_gui_protocol_name(void *opdata, const char *protocol) { + //return strdup(protocol); + DEBUGOUT_T("OTR_GUI_PROTOCOL_NAME") + return protocol; + } + + /* Deallocate a string allocated by protocol_name */ + void otr_gui_protocol_name_free(void *opdata, const char *protocol_name) { + //free((void *)protocol_name); + } + + /* A new fingerprint for the given user has been received. */ + void otr_gui_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname, const char *protocol, const char *username, unsigned char fingerprint[20]) { + DEBUGOUT_T("OTR_GUI_NEW_FUNGERPRINT") + //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, NULL); + otrl_privkey_write_fingerprints(otr_user_state, g_fingerprint_store_filename); + } + + /* The list of known fingerprints has changed. Write them to disk. */ + void otr_gui_write_fingerprints(void *opdata) { + DEBUGOUT_T("OTR_GUI_WRITE_FINGERPRINTS") + //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, g_fingerprint_store_filename); + } + + /* A ConnContext has entered a secure state. */ + void otr_gui_gone_secure(void *opdata, ConnContext *context) { + DEBUGOUT_T("OTR_GUI_GONE_SECURE") + TrustLevel trusted = otr_context_get_trust(context); + // opdata is hContact + SetEncryptionStatus((HANDLE)opdata, trusted); + TCHAR buff[1024]; + if(trusted == TRUST_PRIVATE) { + mir_sntprintf(buff, 1024, TranslateT(LANG_SESSION_START_OTR), contact_get_nameT((HANDLE)opdata)); + } else if (trusted == TRUST_UNVERIFIED) { + if (options.autoshow_verify) VerifyContextDialog(context); + mir_sntprintf(buff, 1024, TranslateT(LANG_SESSION_START_OTR_VERIFY), contact_get_nameT((HANDLE)opdata)); + } else { // should never happen + mir_sntprintf(buff, 1024, TranslateT(LANG_SESSION_NOT_STARTED_OTR), contact_get_nameT((HANDLE)opdata)); + } + // opdata is hContact + ShowMessage((HANDLE)opdata, buff); + + } + + /* A ConnContext has left a secure state. */ + void otr_gui_gone_insecure(void *opdata, ConnContext *context) { + DEBUGOUT_T("OTR_GUI_GONE_INSECURE") + TCHAR buff[512]; + mir_sntprintf(buff, 512, TranslateT(LANG_SESSION_TERMINATED_BY_OTR), contact_get_nameT((HANDLE)opdata)); + //MessageBox(0, buff, Translate("OTR Information"), MB_OK); + ShowMessage((HANDLE)opdata, buff); + + // opdata is hContact + SetEncryptionStatus((HANDLE)opdata, otr_context_get_trust(context)); + } + + /* We have completed an authentication, using the D-H keys we + * already knew. is_reply indicates whether we initiated the AKE. */ + void otr_gui_still_secure(void *opdata, ConnContext *context, int is_reply) { + DEBUGOUT_T("OTR_GUI_STILL_SECURE") + TrustLevel trusted = otr_context_get_trust(context); + SetEncryptionStatus((HANDLE)opdata, trusted); + TCHAR buff[1024]; + if(!is_reply) { + if(trusted == TRUST_PRIVATE) { + mir_sntprintf(buff, 1024, TranslateT(LANG_SESSION_CONTINUE_OTR), contact_get_nameT((HANDLE)opdata)); + } else if (trusted == TRUST_UNVERIFIED) { + if (options.autoshow_verify) VerifyContextDialog(context); + mir_sntprintf(buff, 1024, TranslateT(LANG_SESSION_CONTINUE_OTR_VERIFY), contact_get_nameT((HANDLE)opdata)); + } else { // should never happen + mir_sntprintf(buff, 1024, TranslateT(LANG_SESSION_NOT_STARTED_OTR), contact_get_nameT((HANDLE)opdata)); + } + // opdata is hContact + ShowMessage((HANDLE)opdata, buff); + } else { + if(trusted == TRUST_PRIVATE) { + mir_sntprintf(buff, 1024, TranslateT(LANG_SESSION_HAS_CONTINUE_OTR), contact_get_nameT((HANDLE)opdata)); + } else if (trusted == TRUST_UNVERIFIED) { + mir_sntprintf(buff, 1024, TranslateT(LANG_SESSION_HAS_CONTINUE_OTR_VERIFY), contact_get_nameT((HANDLE)opdata)); + } else { // should never happen + mir_sntprintf(buff, 1024, TranslateT(LANG_SESSION_NOT_STARTED_OTR), contact_get_nameT((HANDLE)opdata)); + } + + } + SetEncryptionStatus((HANDLE)opdata, trusted); + } + + /* Log a message. The passed message will end in "\n". */ + void otr_gui_log_message(void *opdata, const char *message) { + //MessageBox(0, message, Translate("OTR Log Message"), MB_OK); + //ShowMessageInline((HANDLE)opdata, message); +#ifdef _DEBUG + char* msg = strcpy((char*)mir_alloc(strlen(message)+15), "OTR message: "); + strcat(msg, message); + DEBUGOUTA(msg) + mir_free(msg); +#endif + } + + int max_message_size(void *opdata, ConnContext *context) { + int s = CallProtoService(context->protocol, PS_GETCAPS, PFLAG_MAXLENOFMESSAGE, 0); + return s; + } + + const char *account_name(void *opdata, const char *account, const char *protocol) { + return account; + } + + void account_name_free(void *opdata, const char *account_name) { + } + + void add_appdata(void *data, ConnContext *context) { + if(context) context->app_data = data; + } +} + +// Forward decl +gcry_error_t otrl_privkey_write_FILEp(OtrlUserState us, FILE *privf); +/* Generate a private DSA key for a given account, storing it into a + * file on disk, and loading it into the given OtrlUserState. Overwrite any + * previously generated keys for that account in that OtrlUserState. */ +gcry_error_t otrl_privkey_write(OtrlUserState us, const char *filename) +{ + gcry_error_t err; + FILE *privf; +#ifndef WIN32 + mode_t oldmask; +#endif + +#ifndef WIN32 + oldmask = umask(077); +#endif + privf = fopen(filename, "w+b"); + if (!privf) { +#ifndef WIN32 + umask(oldmask); +#endif + err = gcry_error_from_errno(errno); + return err; + } + + err = otrl_privkey_write_FILEp(us, privf); + + fclose(privf); +#ifndef WIN32 + umask(oldmask); +#endif + return err; +} + +/* Just store the private keys of an OtrlUserState. + * The FILE* must be open for reading and writing. */ +gcry_error_t otrl_privkey_write_FILEp(OtrlUserState us, FILE *privf) +{ + OtrlPrivKey *p; + + if (!privf) return gcry_error(GPG_ERR_NO_ERROR); + + + + /* Output the other keys we know */ + fprintf(privf, "(privkeys\n"); + for (p=us->privkey_root; p; p=p->next) { + otrl_account_write(privf, p->accountname, p->protocol, p->privkey); + } + if ( fprintf(privf, ")\n") < 0 ) + return gcry_error_from_errno(errno); + return gcry_error(GPG_ERR_NO_ERROR); + +} \ No newline at end of file diff --git a/MirOTR/otr.h b/MirOTR/otr.h new file mode 100644 index 0000000..933a9c8 --- /dev/null +++ b/MirOTR/otr.h @@ -0,0 +1,53 @@ +#pragma once + +#include "stdafx.h" + +extern OtrlUserState otr_user_state; +extern OtrlMessageAppOps ops; + +extern "C" { + + /* Return the OTR policy for the given context. */ + OtrlPolicy otr_gui_policy(void *opdata, ConnContext *context); + + /* Create a private key for the given accountname/protocol if + * desired. */ + void otr_gui_create_privkey(void *opdata, const char *account_name, const char *protocol); + + int otr_gui_is_logged_in(void *opdata, const char *accountname, const char *protocol, const char *recipient); + + void otr_gui_inject_message(void *opdata, const char *accountname, const char *protocol, const char *recipient, const char *message); + + 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); + + int otr_gui_display_otr_message(void *opdata, const char *accountname, const char *protocol, const char *username, const char *msg); + + void otr_gui_update_context_list(void *opdata); + + const char *otr_gui_protocol_name(void *opdata, const char *protocol); + + void otr_gui_protocol_name_free(void *opdata, const char *protocol_name); + + void otr_gui_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname, const char *protocol, const char *username, unsigned char fingerprint[20]); + + void otr_gui_write_fingerprints(void *opdata); + + void otr_gui_gone_secure(void *opdata, ConnContext *context); + + void otr_gui_gone_insecure(void *opdata, ConnContext *context); + + void otr_gui_still_secure(void *opdata, ConnContext *context, int is_reply); + + void otr_gui_log_message(void *opdata, const char *message); + + int max_message_size(void *opdata, ConnContext *context); + + const char *account_name(void *opdata, const char *account, const char *protocol); + + void account_name_free(void *opdata, const char *account_name); + + void add_appdata(void *data, ConnContext *context); + +} + +gcry_error_t otrl_privkey_write(OtrlUserState us, const char *filename); \ No newline at end of file diff --git a/MirOTR/resource.h b/MirOTR/resource.h new file mode 100644 index 0000000..c810a6d --- /dev/null +++ b/MirOTR/resource.h @@ -0,0 +1,103 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by resource.rc +// +#ifndef IDOK + #define IDOK 1 +#endif +#ifndef IDCANCEL + #define IDCANCEL 2 +#endif +#define IDD_GENKEYNOTIFY 101 +#define IDD_OPT_GENERAL 102 +#define IDD_OPT_PROTO 103 +#define IDD_OPT_CONTACTS 104 +#define IDD_OPT_FINGER 105 +#define IDD_SMP_INPUT 106 +#define IDD_SMP_PROGRESS 107 + +// General Options +#define IDC_GRP_POLICY 1051 +#define IDC_RAD_ALWAYS 1052 +#define IDC_RAD_OPP 1053 +#define IDC_RAD_MANUAL 1054 +#define IDC_RAD_NEVER 1055 + +#define IDC_CHK_PREFIX 1056 +#define IDC_ED_PREFIX 1057 +#define IDC_CHK_MSGINLINE 1058 +#define IDC_CHK_MSGPOPUP 1059 +#define IDC_CHK_PREVENTSAVE 1060 +#define IDC_CHK_AUTOSHOW_VERIFY 1061 +#define IDC_CHK_TIMEFINISH 1062 +#define IDC_CHK_ENDOFFLINE 1063 +#define IDC_CHK_ENDCLOSE 1064 + +// Proto policy & keys +#define IDC_LV_PROTO_PROTOS 1101 +#define IDC_BTN_PROTO_NEWKEY 1102 +#define IDC_CMB_PROTO_POLICY 1103 +#define IDC_BTN_PROTO_FORGET 1104 +#define IDC_STC_PROTO1 1151 +#define IDC_STC_PROTO2 1152 + +// User policy +#define IDC_LV_CONT_CONTACTS 1201 +#define IDC_CMB_CONT_POLICY 1202 +#define IDC_STC_CONT1 1251 +#define IDC_STC_CONT2 1252 + +// User fingerprints +#define IDC_LV_FINGER_LIST 1301 +#define IDC_BTN_FINGER_FORGET 1302 +#define IDC_BTN_FINGER_TRUST 1303 +#define IDC_BTN_FINGER_DONTTRUST 1304 +#define IDC_STC_FINGER1 1351 + +// Generate dialog +#define IDC_GENERATE 1401 +#define IDC_ICOOTR 1402 +#define IDC_ICOREFRESH 1403 + +// SMP Progress +#define IDC_PGB_SMP 1501 +#define IDC_STC_SMP_HEADPRO 1502 +#define IDC_ICO_SMP 1503 +#define IDC_STC_SMP_PROGRESS 1504 + +// SMP Dialog +#define IDC_STC_SMP_HEAD 1602 +#define IDC_CBO_SMP_CHOOSE 1603 +#define IDC_STC_SMP_INFO 1604 +#define IDC_STC_SMP_FIELD1 1605 +#define IDC_EDT_SMP_FIELD1 1606 +#define IDC_STC_SMP_FIELD2 1607 +#define IDC_EDT_SMP_FIELD2 1608 + +#define IDI_OTR 2000 +#define IDI_REFRESH 2001 +#define IDI_INSECURE 2002 +#define IDI_UNVERIFIED 2003 +#define IDI_PRIVATE 2004 +#define IDI_FINISHED 2005 + +#define IDC_STATIC -1 + +#define IDM_OTR_MENU 10001 +#define IDR_CONTEXT 10000 +#define IDM_OTR_STATUS 10001 +#define IDM_OTR_START 10002 +#define IDM_OTR_REFRESH 10003 +#define IDM_OTR_STOP 10004 +#define IDM_OTR_VERIFY 10005 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/MirOTR/resources/finished.ico b/MirOTR/resources/finished.ico new file mode 100644 index 0000000..870cd0a Binary files /dev/null and b/MirOTR/resources/finished.ico differ diff --git a/MirOTR/resources/insecure.ico b/MirOTR/resources/insecure.ico new file mode 100644 index 0000000..4a72fe3 Binary files /dev/null and b/MirOTR/resources/insecure.ico differ diff --git a/MirOTR/resources/otr.ico b/MirOTR/resources/otr.ico new file mode 100644 index 0000000..298a3ba Binary files /dev/null and b/MirOTR/resources/otr.ico differ diff --git a/MirOTR/resources/private.ico b/MirOTR/resources/private.ico new file mode 100644 index 0000000..b7c8585 Binary files /dev/null and b/MirOTR/resources/private.ico differ diff --git a/MirOTR/resources/refresh.ico b/MirOTR/resources/refresh.ico new file mode 100644 index 0000000..45fbbf2 Binary files /dev/null and b/MirOTR/resources/refresh.ico differ diff --git a/MirOTR/resources/resource.rc b/MirOTR/resources/resource.rc new file mode 100644 index 0000000..35564ac --- /dev/null +++ b/MirOTR/resources/resource.rc @@ -0,0 +1,133 @@ +#ifndef IDCANCEL + #define IDCANCEL 2 +#endif +#ifndef IDOK + #define IDOK 1 +#endif + +#include "windows.h" +#include "../resource.h" +#include "version.rc" + +LANGUAGE 9,0 + +IDI_OTR ICON DISCARDABLE "otr.ico" +IDI_REFRESH ICON DISCARDABLE "refresh.ico" +IDI_INSECURE ICON DISCARDABLE "insecure.ico" +IDI_UNVERIFIED ICON DISCARDABLE "unverified.ico" +IDI_PRIVATE ICON DISCARDABLE "private.ico" +IDI_FINISHED ICON DISCARDABLE "finished.ico" + +IDD_GENKEYNOTIFY DIALOGEX 0,0,270,33 +CAPTION "OTR Generating Private Key" +FONT 8,"MS Shell Dlg",0,0,0 +STYLE WS_VISIBLE|WS_CAPTION|WS_SYSMENU|WS_MINIMIZEBOX|DS_CENTER|DS_SETFOREGROUND|DS_MODALFRAME|DS_SETFONT|0xC0000000 +BEGIN + CONTROL "Generating new private key - please wait.",IDC_GENERATE,"Static",WS_CHILDWINDOW|WS_VISIBLE|WS_GROUP|SS_CENTER,33,3,201,27 + CONTROL IDI_OTR,IDC_ICOOTR,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_CENTERIMAGE|SS_ICON,0,0,32,32 + CONTROL IDI_REFRESH,IDC_ICOREFRESH,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_CENTERIMAGE|SS_ICON,237,0,32,32 +END + +IDD_OPT_GENERAL DIALOGEX 0,0,296,220 +FONT 8,"MS Shell Dlg",0,0,1 +STYLE WS_CHILDWINDOW|WS_VISIBLE|DS_SHELLFONT +EXSTYLE WS_EX_CONTROLPARENT +BEGIN + CONTROL "",IDC_STATIC,"Static",WS_VISIBLE|WS_TABSTOP,0,0,1,1 + CONTROL "Default Policy",IDC_GRP_POLICY,"Button",WS_CHILDWINDOW|WS_VISIBLE|BS_GROUPBOX,5,3,139,60 + CONTROL "Always",IDC_RAD_ALWAYS,"Button",WS_VISIBLE|WS_TABSTOP|BS_AUTORADIOBUTTON,12,12,126,10 + CONTROL "Opportunistic",IDC_RAD_OPP,"Button",WS_VISIBLE|WS_TABSTOP|BS_AUTORADIOBUTTON,12,24,126,10 + CONTROL "Manual",IDC_RAD_MANUAL,"Button",WS_VISIBLE|WS_GROUP|WS_TABSTOP|BS_AUTORADIOBUTTON,12,36,126,10 + CONTROL "Never",IDC_RAD_NEVER,"Button",WS_VISIBLE|WS_TABSTOP|BS_AUTORADIOBUTTON,12,48,126,10 + CONTROL "End sessions on window close",IDC_CHK_ENDCLOSE,"Button",WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX,12,70,265,10 + CONTROL "End sessions when contacts go offline",IDC_CHK_ENDOFFLINE,"Button",WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX,12,82,265,10 + CONTROL "Prefix encrypted IMs:",IDC_CHK_PREFIX,"Button",WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX,12,94,108,10 + CONTROL "",IDC_ED_PREFIX,"Edit",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|ES_AUTOHSCROLL,120,93,159,12,WS_EX_CLIENTEDGE + CONTROL "Don't add encrypted messages to the history",IDC_CHK_PREVENTSAVE,"Button",WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX,12,106,265,10 + CONTROL "Show OTR system messages inline",IDC_CHK_MSGINLINE,"Button",WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX,12,118,265,10 + CONTROL "Show OTR system messages as popup",IDC_CHK_MSGPOPUP,"Button",WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX,12,130,265,10 + CONTROL "Timeout FINISHED sessions back to PLAINTEXT (TAKE CARE!)",IDC_CHK_TIMEFINISH,"Button",WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX,12,142,265,10 + CONTROL "Show verification dialog when receiving unverified fingerprint",IDC_CHK_AUTOSHOW_VERIFY,"Button",WS_VISIBLE|WS_TABSTOP|BS_AUTOCHECKBOX,12,154,265,10 +END + +IDD_OPT_PROTO DIALOGEX 0,0,296,220 +FONT 8,"MS Shell Dlg",0,0,1 +STYLE WS_CHILDWINDOW|WS_VISIBLE|DS_SHELLFONT +EXSTYLE WS_EX_CONTROLPARENT +BEGIN + CONTROL "Set OTR policy per protocol",IDC_STC_PROTO1,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_NOPREFIX,3,3,291,12 + CONTROL "",IDC_LV_PROTO_PROTOS,"SysListView32",WS_VISIBLE|WS_BORDER|WS_TABSTOP|LVS_SORTASCENDING|LVS_SHOWSELALWAYS|LVS_SINGLESEL|LVS_REPORT,0,18,296,165 + CONTROL "Current policy:",IDC_STC_PROTO2,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_NOPREFIX,3,189,138,9 + CONTROL "",IDC_CMB_PROTO_POLICY,"ComboBox",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|CBS_HASSTRINGS|CBS_DROPDOWNLIST,3,201,138,12 + CONTROL "Generate new private key",IDC_BTN_PROTO_NEWKEY,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,162,186,123,18 + CONTROL "Forget private key",IDC_BTN_PROTO_FORGET,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,162,207,123,12 +END + +IDD_OPT_CONTACTS DIALOGEX 0,0,296,220 +FONT 8,"MS Shell Dlg",0,0,1 +STYLE WS_CHILDWINDOW|WS_VISIBLE|DS_SHELLFONT +EXSTYLE WS_EX_CONTROLPARENT +BEGIN + CONTROL "Set OTR policy per contact",IDC_STC_CONT1,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_NOPREFIX,3,3,291,12 + CONTROL "",IDC_LV_CONT_CONTACTS,"SysListView32",WS_VISIBLE|WS_BORDER|WS_TABSTOP|LVS_SORTASCENDING|LVS_SHOWSELALWAYS|LVS_SINGLESEL|LVS_REPORT,0,18,296,180 + CONTROL "Current policy:",IDC_STC_CONT2,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_NOPREFIX,3,204,129,9 + CONTROL "",IDC_CMB_CONT_POLICY,"ComboBox",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|CBS_HASSTRINGS|CBS_DROPDOWNLIST,138,201,153,12 +END + +IDD_OPT_FINGER DIALOGEX 0,0,296,220 +FONT 8,"MS Shell Dlg",0,0,1 +STYLE WS_CHILDWINDOW|WS_VISIBLE|DS_SHELLFONT +EXSTYLE WS_EX_CONTROLPARENT +BEGIN + CONTROL "View & Modify status of known fingerprints",IDC_STC_FINGER1,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_NOPREFIX,3,3,291,12 + CONTROL "",IDC_LV_FINGER_LIST,"SysListView32",WS_VISIBLE|WS_BORDER|WS_TABSTOP|LVS_SORTASCENDING|LVS_SHOWSELALWAYS|LVS_SINGLESEL|LVS_REPORT,0,18,296,171 + CONTROL "Set verified",IDC_BTN_FINGER_TRUST,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,3,195,66,18 + CONTROL "Set unknown",IDC_BTN_FINGER_DONTTRUST,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,72,195,66,18 + CONTROL "Forget",IDC_BTN_FINGER_FORGET,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,225,195,66,18 +END + +IDR_CONTEXT MENU +BEGIN + POPUP "MirOTR" + BEGIN + MENUITEM "OTR status",IDM_OTR_STATUS,GRAYED + MENUITEM SEPARATOR + MENUITEM "&Start OTR session",IDM_OTR_START + MENUITEM "&Refresh OTR session",IDM_OTR_REFRESH + MENUITEM "Sto&p OTR session",IDM_OTR_STOP + MENUITEM SEPARATOR + MENUITEM "&Verify Fingerprint",IDM_OTR_VERIFY + END +END + +IDD_SMP_PROGRESS DIALOGEX 10,10,200,100 +CAPTION "Socialist Millionaires Protocol" +FONT 8,"MS Sans Serif",0,0,0 +STYLE WS_POPUP|WS_VISIBLE|WS_CAPTION|DS_CENTER|DS_SETFOREGROUND|DS_MODALFRAME +BEGIN + CONTROL IDI_OTR,IDC_ICO_SMP,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_CENTERIMAGE|SS_ICON,3,3,32,32 + CONTROL "Socialist Millionaires Protocol\r\nVerification progress",IDC_STC_SMP_HEADPRO,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_NOPREFIX,39,9,156,21 + CONTROL "",IDC_PGB_SMP,"msctls_progress32",WS_CHILDWINDOW|WS_VISIBLE|PBS_SMOOTH,3,36,194,18 + CONTROL "",IDC_STC_SMP_PROGRESS,"Static",WS_CHILDWINDOW|WS_VISIBLE,3,57,194,20 + CONTROL "&Cancel",IDCANCEL,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,70,80,60,16 +END + +IDD_SMP_INPUT DIALOGEX 10,10,200,200 +CAPTION "Socialist Millionaires Protocol" +FONT 8,"MS Sans Serif",0,0,0 +STYLE WS_VISIBLE|WS_CAPTION|WS_SYSMENU|DS_SETFOREGROUND|DS_MODALFRAME +BEGIN + CONTROL IDI_OTR,IDC_ICO_SMP,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_CENTERIMAGE|SS_ICON,3,3,32,32 + CONTROL "Socialist Millionaires Protocol\r\nVerification",IDC_STC_SMP_HEAD,"Static",WS_CHILDWINDOW|WS_VISIBLE|SS_NOPREFIX,39,9,156,21 + CONTROL "",IDC_CBO_SMP_CHOOSE,"ComboBox",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP|CBS_HASSTRINGS|CBS_DROPDOWNLIST,3,36,194,60 + CONTROL "",IDC_STC_SMP_INFO,"Static",WS_CHILDWINDOW|WS_VISIBLE,3,60,195,35 + CONTROL "",IDC_STC_SMP_FIELD1,"Static",WS_CHILDWINDOW|WS_VISIBLE,3,105,194,10 + CONTROL "",IDC_EDT_SMP_FIELD1,"Edit",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,3,117,194,13,WS_EX_CLIENTEDGE + CONTROL "",IDC_STC_SMP_FIELD2,"Static",WS_CHILDWINDOW|WS_VISIBLE,3,141,194,10 + CONTROL "",IDC_EDT_SMP_FIELD2,"Edit",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,3,153,194,13,WS_EX_CLIENTEDGE + CONTROL "&Cancel",IDCANCEL,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,3,180,60,16 + CONTROL "&OK",IDOK,"Button",WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,135,180,60,16 + CONTROL "&No",IDNO,"Button",NOT WS_VISIBLE|WS_CHILDWINDOW|WS_TABSTOP,70,180,60,16 + CONTROL "&Yes",IDYES,"Button",NOT WS_VISIBLE|WS_CHILDWINDOW|WS_TABSTOP,135,180,60,16 +END + diff --git a/MirOTR/resources/unverified.ico b/MirOTR/resources/unverified.ico new file mode 100644 index 0000000..95cc662 Binary files /dev/null and b/MirOTR/resources/unverified.ico differ diff --git a/MirOTR/resources/version.rc b/MirOTR/resources/version.rc new file mode 100644 index 0000000..b7007af --- /dev/null +++ b/MirOTR/resources/version.rc @@ -0,0 +1,35 @@ +/* THIS FILE WILL BE OVERWRITTEN BY DEV-C++ */ +/* DO NOT EDIT! */ + +#include "version.h" +#include "windows.h" + +// +// 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/MirOTR/stdafx.cpp b/MirOTR/stdafx.cpp new file mode 100644 index 0000000..98540cb --- /dev/null +++ b/MirOTR/stdafx.cpp @@ -0,0 +1,8 @@ +// stdafx.cpp : Quelldatei, die nur die Standard-Includes einbindet. +// MirOTR.pch ist der vorkompilierte Header. +// stdafx.obj enthält die vorkompilierten Typinformationen. + +#include "stdafx.h" + +// TODO: Auf zusätzliche Header verweisen, die in STDAFX.H +// und nicht in dieser Datei erforderlich sind. diff --git a/MirOTR/stdafx.h b/MirOTR/stdafx.h new file mode 100644 index 0000000..ebb3f07 --- /dev/null +++ b/MirOTR/stdafx.h @@ -0,0 +1,89 @@ +// stdafx.h : Includedatei für Standardsystem-Includedateien +// oder häufig verwendete projektspezifische Includedateien, +// die nur in unregelmäßigen Abständen geändert werden. +// + +#pragma once + +#include "targetver.h" + +#define WIN32_LEAN_AND_MEAN // Selten verwendete Teile der Windows-Header nicht einbinden. +// Windows-Headerdateien: +#include +#include +#include + +#ifdef _DEBUG + #define DEBUGOUTA(x) OutputDebugStringA(x); + #define DEBUGOUT(x) OutputDebugString(x); + #define DEBUGOUT_T(x) OutputDebugString(__T(x)); +#else + #define DEBUGOUTA(x); + #define DEBUGOUT(x) + #define DEBUGOUT_T(x) +#endif +#define MIRANDA_VER 0x0800 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +extern "C" { + #include + #include + #include + #include + #include +} + +#include "dllmain.h" +#include "language.h" +#include "options.h" +#include "utils.h" +#include "svcs_menu.h" +#include "svcs_proto.h" +#include "svcs_srmm.h" +#include "resource.h" +#include "otr.h" +#include "icons.h" +#include "dialogs.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) + +// {030F37D6-DD32-434d-BC64-5B6541EB9299} +static const MUUID MIID_OTR = { 0x30f37d6, 0xdd32, 0x434d, { 0xbc, 0x64, 0x5b, 0x65, 0x41, 0xeb, 0x92, 0x99 } }; +#ifdef _UNICODE + // {12D8FAAD-78AB-4e3c-9854-320E9EA5CC9F} + static const MUUID MIID_OTRPLUGIN = { 0x12d8faad, 0x78ab, 0x4e3c, { 0x98, 0x54, 0x32, 0xe, 0x9e, 0xa5, 0xcc, 0x9f } }; +#else + // {16E2E7B0-D398-4ea8-A5CC-A0F3689C7608} + static const MUUID MIID_OTRPLUGIN = { 0x16e2e7b0, 0xd398, 0x4ea8, { 0xa5, 0xcc, 0xa0, 0xf3, 0x68, 0x9c, 0x76, 0x8 } }; +#endif + +#define MODULENAME "MirOTR" + +#define MS_OTR_MENUSTART MODULENAME"/Start" +#define MS_OTR_MENUSTOP MODULENAME"/Stop" + +#define PREF_BYPASS_OTR 0x8000 +#define PREF_NO_HISTORY 0x10000 + +#define SIZEOF(X) (sizeof(X)/sizeof(X[0])) + +// TODO: Hier auf zusätzliche Header, die das Programm erfordert, verweisen. diff --git a/MirOTR/svcs_menu.cpp b/MirOTR/svcs_menu.cpp new file mode 100644 index 0000000..5a2944c --- /dev/null +++ b/MirOTR/svcs_menu.cpp @@ -0,0 +1,169 @@ +#include "stdafx.h" +#include "otr.h" + +HANDLE hMenuBuildEvent, hStopItem, hStartItem; + +//////////////////////////////// +///////// Menu Services +/////////////////////// +int StartOTR(HANDLE hContact) { + + const char *proto = contact_get_proto(hContact); + if(!proto) return 1; // error + char *uname = contact_get_id(hContact); + if(!uname) return 1; // error + WORD pol = DBGetContactSettingWord(hContact, MODULENAME, "Policy", CONTACT_DEFAULT_POLICY); + if(pol == CONTACT_DEFAULT_POLICY) pol = options.default_policy; + + lib_cs_lock(); + char *msg = otrl_proto_default_query_msg(MODULENAME, pol); + otr_gui_inject_message(hContact, proto, proto, uname, msg ? msg : "?OTRv2?"); + lib_cs_unlock(); + otrl_message_free(msg); + mir_free(uname); + return 0; +} +int SVC_StartOTR(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam, hSub; + if(options.bHaveMetaContacts && (hSub = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0)) != 0) { + hContact = hSub; + } + + TCHAR buff[512]; + mir_sntprintf(buff, 512, TranslateT(LANG_SESSION_REQUEST_OTR), contact_get_nameT(hContact)); + ShowMessage(hContact, buff); + + int res = StartOTR(hContact); + if (res) return res; + + return 0; +} +int SVC_RefreshOTR(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam, hSub; + if(options.bHaveMetaContacts && (hSub = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0)) != 0) { + hContact = hSub; + } + + TCHAR buff[512]; + mir_sntprintf(buff, 512, TranslateT(LANG_SESSION_TRY_CONTINUE_OTR), contact_get_nameT(hContact)); + ShowMessage(hContact, buff); + + int res = StartOTR(hContact); + if (res) return res; + + return 0; +} +int otr_disconnect_contact(HANDLE hContact) { + HANDLE hSub; + if(ServiceExists(MS_MC_GETMOSTONLINECONTACT) && (hSub = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0)) != 0) { + hContact = hSub; + } + + const char *proto = contact_get_proto(hContact); + if(!proto) return 1; // error + char *uname = contact_get_id(hContact); + if(!uname) return 1; // error + + lib_cs_lock(); + otrl_message_disconnect(otr_user_state, &ops, hContact, proto, proto, uname); + lib_cs_unlock(); + mir_free(uname); + return 0; +} + +int SVC_StopOTR(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + + // prevent this filter from acting on injeceted messages for metas, when they are passed though the subcontact's proto send chain + if (otr_disconnect_contact(hContact)) return 0; + + SetEncryptionStatus(hContact, TRUST_NOT_PRIVATE); + + TCHAR buff[512]; + mir_sntprintf(buff, 512, TranslateT(LANG_SESSION_TERMINATED_OTR), contact_get_nameT(hContact)); + ShowMessage(hContact, buff); + + return 0; +} + +void InitMenu() { + CLISTMENUITEM mi = {0}; + mi.cbSize = sizeof(mi); + mi.flags = CMIF_NOTOFFLINE | CMIF_TCHAR | CMIF_ICONFROMICOLIB; + mi.position = -400000; + + mi.ptszName = _T(LANG_STOP_OTR); + mi.pszService = MS_OTR_MENUSTOP; + mi.icolibItem = GetIconHandle(ICON_PRIVATE); + + hStopItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi); + + mi.ptszName = _T(LANG_START_OTR); + mi.pszService = MS_OTR_MENUSTART; + mi.icolibItem = GetIconHandle(ICON_NOT_PRIVATE); + + hStartItem = (HANDLE)CallService(MS_CLIST_ADDCONTACTMENUITEM, 0, (LPARAM)&mi); + + hMenuBuildEvent = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, SVC_PrebuildContactMenu); + +} + +void DeinitMenu() { + UnhookEvent(hMenuBuildEvent); +} + +int SVC_PrebuildContactMenu(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + + CLISTMENUITEM mi = {0}; + mi.cbSize = sizeof(mi); + mi.flags = CMIM_FLAGS | CMIF_NOTOFFLINE | CMIF_TCHAR | CMIF_ICONFROMICOLIB; + + const char *proto = contact_get_proto(hContact); + WORD pol = CONTACT_DEFAULT_POLICY; + + if(!proto || DBGetContactSettingByte(hContact, proto, "ChatRoom", 0) == 1) { + goto hide_all; + } + + if(proto && g_metaproto && strcmp(proto, g_metaproto) == 0) { + // make menu act as per most online subcontact + hContact = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0); + if(!hContact) + goto hide_all; + proto = contact_get_proto(hContact); + } + + pol = DBGetContactSettingWord(hContact, MODULENAME, "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_miranda(otr_user_state, hContact); + TrustLevel encrypted = otr_context_get_trust(context); + if(encrypted != TRUST_NOT_PRIVATE) { + 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; +} \ No newline at end of file diff --git a/MirOTR/svcs_menu.h b/MirOTR/svcs_menu.h new file mode 100644 index 0000000..43e6d03 --- /dev/null +++ b/MirOTR/svcs_menu.h @@ -0,0 +1,7 @@ +#pragma once +int SVC_StartOTR(WPARAM wParam, LPARAM lParam); +int SVC_RefreshOTR(WPARAM wParam, LPARAM lParam); +int SVC_StopOTR(WPARAM wParam, LPARAM lParam); +int SVC_PrebuildContactMenu(WPARAM wParam, LPARAM lParam); +void InitMenu(); +void DeinitMenu(); \ No newline at end of file diff --git a/MirOTR/svcs_proto.cpp b/MirOTR/svcs_proto.cpp new file mode 100644 index 0000000..b46b33f --- /dev/null +++ b/MirOTR/svcs_proto.cpp @@ -0,0 +1,370 @@ +#include "stdafx.h" +#include "svcs_proto.h" + +//TODO: Social-Millionaire-Dialoge +int SVC_OTRSendMessage(WPARAM wParam,LPARAM lParam){ + if(!lParam) return 0; + + CCSDATA *ccs = (CCSDATA *) lParam; + + DEBUGOUT_T("SENDING") + + if (ccs->wParam & PREF_BYPASS_OTR) // bypass for OTR-messages + return CallService(MS_PROTO_CHAINSEND, wParam, lParam); + + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ccs->hContact, 0); + if(proto && g_metaproto && strcmp(proto, g_metaproto) == 0) // bypass for metacontacts + return CallService(MS_PROTO_CHAINSEND, wParam, lParam); + char *username = contact_get_id(ccs->hContact); + if(!proto || !username) 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_UTF) { + oldmessage_utf = oldmessage; + } else if(ccs->wParam & PREF_UNICODE) { + //TODO: check if this is correct or oldmessage[strlen(oldmessage)+1] is needed + oldmessage_utf = mir_utf8encodeW((wchar_t*)oldmessage); + } else { + oldmessage_utf = mir_utf8encode(oldmessage); + } + if(!oldmessage_utf) return 1; + + // don't filter OTR messages being sent (OTR messages should only happen *after* the otrl_message_sending call below) + if(strncmp(oldmessage_utf, "?OTR", 4) == 0) { + DEBUGOUT_T("OTR message without PREF_BYPASS_OTR") + if (!(ccs->wParam & PREF_UTF)) mir_free(oldmessage_utf); + return CallService(MS_PROTO_CHAINSEND, wParam, lParam); + } + + err = otrl_message_sending(otr_user_state, &ops, (void*)ccs->hContact, + proto, proto, username, oldmessage_utf, NULL, &newmessage, + add_appdata, (void*)ccs->hContact); + if (!(ccs->wParam & PREF_UTF)) mir_free(oldmessage_utf); + oldmessage_utf = NULL; + mir_free(username); + + if (err && newmessage == NULL) { + /* Be *sure* not to send out plaintext */ + ShowError(TranslateT(LANG_ENCRYPTION_ERROR)); + /* should not be needed + char t[3] = {0, 0, 0}; + if(ccs->wParam & PREF_UNICODE) { //TODO!! lstrlen(pre->szMessage)+1 ?? + if (strlen(oldmessage)) memcpy(oldmessage, t,3 ); + } else { + if (strlen(oldmessage)) memcpy(oldmessage, t, 1); + } + */ + return 1; + } else if (newmessage) { + /* Fragment the message if necessary, and send all but the last + * fragment over the network. We will send the last segment later */ + ConnContext *context = otrl_context_find_miranda(otr_user_state, + (void*)ccs->hContact); + + // oldmessage_utf is not used anymore, so use it as buffer + err = otrl_message_fragment_and_send(&ops, (void*)ccs->hContact, context, + newmessage, OTRL_FRAGMENT_SEND_ALL_BUT_LAST, &oldmessage_utf); + // newmessage is not needed anymore + otrl_message_free(newmessage); + // move the last fragment to newmessage, oldmessage_utf will be buffer later + newmessage = oldmessage_utf; + + WPARAM oldflags = ccs->wParam; + if(ccs->wParam & PREF_UTF) { + ccs->lParam = (LPARAM)newmessage; + } else if(ccs->wParam & PREF_UNICODE) { + // just send UTF, hope that proto supports it. + // don't want to create the Unicode-Format + // oldmessage_utf = (char*)mir_utf8decodeW(newmessage); + ccs->lParam = (LPARAM)newmessage; + ccs->wParam &= ~PREF_UNICODE; + ccs->wParam |= PREF_UTF; + } else { + mir_utf8decode(newmessage, NULL); + ccs->lParam = (LPARAM)newmessage; + } + if (otr_context_get_trust(context) >= TRUST_UNVERIFIED) ccs->wParam; + int ret = CallService(MS_PROTO_CHAINSEND, wParam, lParam); + +/*#ifdef _DEBUG + if(ccs->wParam & PREF_UNICODE) + MessageBox(0, (wchar_t *)ccs->lParam, _T("OTR - sending raw message"), MB_OK); + else + MessageBoxA(0, (char *)ccs->lParam, ("OTR - sending raw message"), MB_OK); +#endif*/ + + // reset to original values + ccs->lParam = (LPARAM)oldmessage; + ccs->wParam = oldflags; + otrl_message_free(newmessage); + return ret; + + } + + return CallService(MS_PROTO_CHAINSEND, wParam, lParam); +} + +int SVC_OTRSendMessageW(WPARAM wParam, LPARAM lParam){ + if(!lParam) return 0; + + CCSDATA *ccs = (CCSDATA *) lParam; + if (!(ccs->wParam & PREF_UTF)) ccs->wParam |= PREF_UNICODE; + + return SVC_OTRSendMessage(wParam, lParam); +} + +/* +#define MESSAGE_PREFIX "(OTR) " +#define MESSAGE_PREFIXW L"(OTR) " +#define MESSAGE_PREFIX_LEN 6 +*/ + +int SVC_OTRRecvMessage(WPARAM wParam,LPARAM lParam){ + //PUShowMessage("OTR Recv Message", SM_NOTIFY); + CCSDATA *ccs = (CCSDATA *) lParam; + PROTORECVEVENT *pre = (PROTORECVEVENT *) ccs->lParam; + +#ifdef _DEBUG + if(pre->flags & PREF_UNICODE) + MessageBox(0, (wchar_t *)pre->szMessage, _T("OTR - receiving message"), MB_OK); + else + MessageBoxA(0, (char *)pre->szMessage, ("OTR - receiving message"), MB_OK); +#endif + + if (pre->flags & PREF_BYPASS_OTR) { // bypass for our inline messages + //TODO: check, whether this can be removed (then OTR status info should be still saved) + return CallService(MS_PROTO_CHAINRECV, wParam, lParam); + } + + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)ccs->hContact, 0); + if(!proto) + return 1; //error + else if(proto && g_metaproto && strcmp(proto, g_metaproto) == 0) // bypass for metacontacts + return CallService(MS_PROTO_CHAINRECV, wParam, lParam); + + char *uname = contact_get_id(ccs->hContact); + if(!uname) return 1; // error + + char *oldmessage = pre->szMessage; + char *oldmessage_utf = NULL; + // convert oldmessage to utf-8 + if(pre->flags & PREF_UTF) { + oldmessage_utf = oldmessage; + } else if(pre->flags & PREF_UNICODE) { //UNSURE!! + oldmessage_utf = mir_utf8encodeW((wchar_t*)(&oldmessage[strlen(oldmessage)+1])); + } else { + oldmessage_utf = mir_utf8encode(oldmessage); + } + if(!oldmessage_utf) return 1; + + + char *newmessage = NULL; + OtrlTLV *tlvs = NULL; + OtrlTLV *tlv = NULL; + BOOL ignore_msg; + ConnContext *context; + //NextExpectedSMP nextMsg; + + lib_cs_lock(); + ignore_msg = otrl_message_receiving(otr_user_state, &ops, (void*)ccs->hContact, + proto, proto, uname, oldmessage_utf, + &newmessage, &tlvs, add_appdata, (void*)ccs->hContact); + lib_cs_unlock(); + + if( !(pre->flags & PREF_UTF)) mir_free(oldmessage_utf); + oldmessage_utf = NULL; + + + tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED); + if (tlv) { + /* Notify the user that the other side disconnected. */ + TCHAR buff[256]; + mir_sntprintf(buff, 256, TranslateT(LANG_SESSION_TERMINATED_BY_OTR), contact_get_nameT(ccs->hContact)); + //MessageBox(0, buff, Translate("OTR Information"), MB_OK); + ShowMessage((HANDLE)ccs->hContact, buff); + + //if(options.timeout_finished) ResetFinishedTimer(); + } + + lib_cs_lock(); + context = otrl_context_find_miranda(otr_user_state, ccs->hContact); + lib_cs_unlock(); + /* Keep track of our current progress in the Socialist Millionaires' + * Protocol. */ + if (context && ( (context->smstate->sm_prog_state == OTRL_SMP_PROG_CHEATED) || + otrl_tlv_find(tlvs, OTRL_TLV_SMP1Q) || + otrl_tlv_find(tlvs, OTRL_TLV_SMP1) || + otrl_tlv_find(tlvs, OTRL_TLV_SMP2) || + otrl_tlv_find(tlvs, OTRL_TLV_SMP3) || + otrl_tlv_find(tlvs, OTRL_TLV_SMP4) ) + ) + { + otr_abort_smp(context); // we do not support it (yet), notify partner to shorten wait time + } + + + /* + if (context) { + nextMsg = context->smstate->nextExpected; + + if (context->smstate->sm_prog_state == OTRL_SMP_PROG_CHEATED) { + otr_abort_smp(context); + otrg_dialog_update_smp(context, 0.0); + context->smstate->nextExpected = OTRL_SMP_EXPECT1; + context->smstate->sm_prog_state = OTRL_SMP_PROG_OK; + } else { + + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1Q); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT1) + otr_abort_smp(context); + else { + char *question = (char *)tlv->data; + char *eoq = (char*)memchr(question, '\0', tlv->len); + if (eoq) { + otrg_dialog_socialist_millionaires_q(context, + question); + } + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT1) + otr_abort_smp(context); + else { + otrg_dialog_socialist_millionaires(context); + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT2) + otr_abort_smp(context); + else { + otrg_dialog_update_smp(context, 0.6); + context->smstate->nextExpected = OTRL_SMP_EXPECT4; + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT3) + otr_abort_smp(context); + else { + otrg_dialog_update_smp(context, 1.0); + context->smstate->nextExpected = OTRL_SMP_EXPECT1; + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT4) + otr_abort_smp(context); + else { + otrg_dialog_update_smp(context, 1.0); + context->smstate->nextExpected = OTRL_SMP_EXPECT1; + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT); + if (tlv) { + otrg_dialog_update_smp(context, 0.0); + context->smstate->nextExpected = OTRL_SMP_EXPECT1; + } + } + } + */ + + otrl_tlv_free(tlvs); + + mir_free(uname); + + /* If we're supposed to ignore this incoming message (because it's a + * protocol message), set it to NULL, so that other plugins that + * catch receiving-im-msg don't return 0, and cause it to be + * displayed anyway. */ + if (ignore_msg) { + /* should not be required ;) + char t[3] = {0, 0, 0}; + if(pre->flags & PREF_UNICODE) { //TODO!! lstrlen(pre->szMessage)+1 ?? + if (pre->szMessage && strlen(pre->szMessage)) memcpy(pre->szMessage, t,3 ); + } else { + if (pre->szMessage && strlen(pre->szMessage)) memcpy(pre->szMessage, t, 1); + } + */ + if (newmessage) otrl_message_free(newmessage); + return 1; + } else if (newmessage) { + //bool free=false; + DWORD flags = pre->flags; + /* if(pre->flags & PREF_UTF) { + oldmessage_utf = newmessage; + } else if(pre->flags & PREF_UNICODE) { //TODO!! lstrlen(pre->szMessage)+1 ?? + oldmessage_utf = (char*)mir_utf8decodeW(newmessage); + free = true; + } else { + mir_utf8decode(newmessage, NULL); + oldmessage_utf = newmessage; + } */ + + //pre->szMessage = oldmessage_utf; + pre->flags &= ~PREF_UNICODE; + pre->flags |= PREF_UTF; // just use UTF, so we do not have to recode the message + + bool is_miralloc = false; + if (context) { + TrustLevel level = otr_context_get_trust(context); + if (options.prefix_messages && (level == TRUST_PRIVATE || level == TRUST_UNVERIFIED)) { + DWORD len = (strlen(options.prefix)+strlen(newmessage)+1)*sizeof(char); + char* premsg = (char*)mir_alloc( len ); + memset(premsg, 0, len); + strcpy(premsg, options.prefix); + strcat(premsg, newmessage); + otrl_message_free(newmessage); + newmessage = premsg; + is_miralloc = true; + } + } + pre->szMessage = newmessage; + BOOL ret = CallService(MS_PROTO_CHAINRECV, wParam, lParam); + pre->flags = flags; + pre->szMessage = oldmessage; + if (is_miralloc) + mir_free(newmessage); + else + otrl_message_free(newmessage); + + // if (free) mir_free(oldmessage_utf); + return ret; + } + return CallService(MS_PROTO_CHAINRECV, wParam, lParam); + +} + +/* Abort the SMP protocol. Used when malformed or unexpected messages + * are received. */ +void otr_abort_smp(ConnContext *context) +{ + otrl_message_abort_smp(otr_user_state, &ops, context->app_data, context); +} + +/* Start the Socialist Millionaires' Protocol over the current connection, + * using the given initial secret, and optionally a question to pass to + * the buddy. */ +void otr_start_smp(ConnContext *context, const char *question, + const unsigned char *secret, size_t secretlen) +{ + otrl_message_initiate_smp_q(otr_user_state, &ops, context->app_data, + context, question, secret, secretlen); +} + +/* Continue the Socialist Millionaires' Protocol over the current connection, + * using the given initial secret (ie finish step 2). */ +void otr_continue_smp(ConnContext *context, + const unsigned char *secret, size_t secretlen) +{ + otrl_message_respond_smp(otr_user_state, &ops, context->app_data, + context, secret, secretlen); +} \ No newline at end of file diff --git a/MirOTR/svcs_proto.h b/MirOTR/svcs_proto.h new file mode 100644 index 0000000..7040d79 --- /dev/null +++ b/MirOTR/svcs_proto.h @@ -0,0 +1,13 @@ +#pragma once + +int SVC_OTRSendMessage(WPARAM wParam,LPARAM lParam); +int SVC_OTRSendMessageW(WPARAM wParam, LPARAM lParam); +int SVC_OTRRecvMessage(WPARAM wParam,LPARAM lParam); + +void otr_abort_smp(ConnContext *context); + +void otr_start_smp(ConnContext *context, const char *question, + const unsigned char *secret, size_t secretlen); + +void otr_continue_smp(ConnContext *context, + const unsigned char *secret, size_t secretlen); \ No newline at end of file diff --git a/MirOTR/svcs_srmm.cpp b/MirOTR/svcs_srmm.cpp new file mode 100644 index 0000000..e562725 --- /dev/null +++ b/MirOTR/svcs_srmm.cpp @@ -0,0 +1,224 @@ +#include "stdafx.h" +HANDLE hEventIconPressed; +HICON hIconNotSecure, hIconFinished, hIconPrivate, hIconUnverified; + +int WindowEvent(WPARAM wParam, LPARAM lParam) { + MessageWindowEventData *mwd = (MessageWindowEventData *)lParam; + + if(mwd->uType == MSG_WINDOW_EVT_CLOSE && options.end_window_close) { + // FinishSession(mwd->hContact); + return 0; + } + if(mwd->uType != MSG_WINDOW_EVT_OPEN) return 0; + if(!options.bHaveSRMMIcons) return 0; + + HANDLE hContact = mwd->hContact, hTemp; + if(options.bHaveMetaContacts && (hTemp = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hContact, 0)) != 0) + hContact = hTemp; + + if(!CallService(MS_PROTO_ISPROTOONCONTACT, (WPARAM)hContact, (LPARAM)MODULENAME)) + return 0; + + lib_cs_lock(); + ConnContext *context = otrl_context_find_miranda(otr_user_state, hContact); + lib_cs_unlock(); + + SetEncryptionStatus(hContact, otr_context_get_trust(context)); + + return 0; +} + +int SVC_IconPressed(WPARAM wParam, LPARAM lParam) { + HANDLE hContact = (HANDLE)wParam; + StatusIconClickData *sicd = (StatusIconClickData *)lParam; + if(sicd->cbSize < (int)sizeof(StatusIconClickData)) + return 0; + + if(strcmp(sicd->szModule, MODULENAME) == 0) { + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + if(proto && DBGetContactSettingByte(hContact, proto, "ChatRoom", 0)) + return 0; + HMENU hMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDR_CONTEXT)); + CallService(MS_LANGPACK_TRANSLATEMENU, (WPARAM)hMenu, 0); + TrustLevel level = (TrustLevel) db_dword_get(hContact, MODULENAME, "TrustLevel", TRUST_NOT_PRIVATE); + MENUITEMINFO minfo = {0}; + minfo.cbSize = sizeof(MENUITEMINFO); + minfo.fMask = MIIM_STRING|MIIM_STATE|MIIM_BITMAP; + minfo.fState = MF_DISABLED; + + HWND wnd = GetFocus(); + HDC dc = GetDC(wnd), hdc = CreateCompatibleDC(dc); + HBITMAP bmpIconPrivate = CreateCompatibleBitmap(dc, 16, 16); + HGDIOBJ old = SelectObject(hdc, bmpIconPrivate); + DrawIconEx(hdc, 0, 0, hIconPrivate,16, 16, 0, GetSysColorBrush(COLOR_MENU), DI_NORMAL); + + HBITMAP bmpIconUnverified = CreateCompatibleBitmap(dc, 16, 16); + SelectObject(hdc, bmpIconUnverified); + DrawIconEx(hdc, 0, 0, hIconUnverified,16, 16, 0, GetSysColorBrush(COLOR_MENU), DI_NORMAL); + + HBITMAP bmpIconFinished = CreateCompatibleBitmap(dc, 16, 16); + SelectObject(hdc, bmpIconFinished); + DrawIconEx(hdc, 0, 0, hIconFinished,16, 16, 0, GetSysColorBrush(COLOR_MENU), DI_NORMAL); + + HBITMAP bmpIconNotSecure = CreateCompatibleBitmap(dc, 16, 16); + SelectObject(hdc, bmpIconNotSecure); + DrawIconEx(hdc, 0, 0, hIconNotSecure,16, 16, 0, GetSysColorBrush(COLOR_MENU), DI_NORMAL); + + switch (level) { + case TRUST_PRIVATE: + minfo.hbmpItem = bmpIconPrivate; + minfo.dwTypeData = TranslateT(LANG_STATUS_PRIVATE); + break; + case TRUST_UNVERIFIED: + minfo.hbmpItem = bmpIconUnverified; + minfo.dwTypeData = TranslateT(LANG_STATUS_UNVERIFIED); + break; + case TRUST_FINISHED: + minfo.hbmpItem = bmpIconFinished; + minfo.dwTypeData = TranslateT(LANG_STATUS_FINISHED); + break; + default: + minfo.hbmpItem = bmpIconNotSecure; + minfo.dwTypeData = TranslateT(LANG_STATUS_DISABLED); + } + + SelectObject(hdc, old); + DeleteDC(hdc); + ReleaseDC(wnd, dc); + + SetMenuItemInfo(hMenu, IDM_OTR_STATUS, FALSE, &minfo); + + minfo.fMask = MIIM_STATE|MIIM_BITMAP; + minfo.fState = (level==TRUST_NOT_PRIVATE)?MF_ENABLED:MF_GRAYED; + minfo.hbmpItem = bmpIconUnverified; + SetMenuItemInfo(hMenu, IDM_OTR_START, FALSE, &minfo); + + minfo.fState = (level==TRUST_NOT_PRIVATE)?MF_GRAYED:MF_ENABLED; + minfo.hbmpItem = bmpIconNotSecure; + SetMenuItemInfo(hMenu, IDM_OTR_STOP, FALSE, &minfo); + + minfo.fState = (level==TRUST_NOT_PRIVATE)?MF_GRAYED:MF_ENABLED; + minfo.hbmpItem = bmpIconFinished; + SetMenuItemInfo(hMenu, IDM_OTR_REFRESH, FALSE, &minfo); + + minfo.fState = (level==TRUST_NOT_PRIVATE||level==TRUST_FINISHED)?MF_GRAYED:MF_ENABLED; + minfo.hbmpItem = bmpIconPrivate; + SetMenuItemInfo(hMenu, IDM_OTR_VERIFY, FALSE, &minfo); + + switch ( (DWORD) TrackPopupMenu(GetSubMenu(hMenu, 0), TPM_NONOTIFY|TPM_RETURNCMD, sicd->clickLocation.x, sicd->clickLocation.y, 0, GetFocus(), 0) ) + { + case IDM_OTR_START: + SVC_StartOTR(wParam, 0); + break; + case IDM_OTR_REFRESH: + SVC_RefreshOTR(wParam, 0); + break; + case IDM_OTR_STOP: + SVC_StopOTR(wParam, 0); + break; + case IDM_OTR_VERIFY: + ConnContext *context = otrl_context_find_miranda(otr_user_state, (HANDLE)wParam); + if (context) VerifyContextDialog(context); + break; + } + DeleteObject(bmpIconPrivate); + DeleteObject(bmpIconFinished); + DeleteObject(bmpIconNotSecure); + DeleteObject(bmpIconUnverified); + } + + return 0; +} + +// set SRMM icon status, if applicable +void SetEncryptionStatus(HANDLE hContact, TrustLevel level) { + //char dbg_msg[2048]; + //dbg_msg[0] = 0; + + //strcat(dbg_msg, "Set encyption status: "); + //strcat(dbg_msg, (encrypted ? "true" : "false")); + + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + bool chat_room = (proto && DBGetContactSettingByte(hContact, proto, "ChatRoom", 0)); + + // if(!chat_room) DBWriteContactSettingByte(hContact, MODULENAME, "Encrypted", (encrypted ? 1 : 0)); + + if(options.bHaveSRMMIcons) { + //strcat(dbg_msg, "\nchanging icon"); + StatusIconData sid = {0}, sid2={0}; + sid.cbSize = sizeof(sid); + sid.szModule = MODULENAME; + sid.dwId = 0; + sid.flags = MBF_HIDDEN; + + sid2.cbSize = sid.cbSize; + sid2.szModule = MODULENAME; + sid2.dwId = 1; + sid2.flags = MBF_HIDDEN; + if (!chat_room) { + switch (level) { + case TRUST_FINISHED: + sid.flags = 0; + break; + case TRUST_UNVERIFIED: + sid2.flags = MBF_DISABLED; + break; + case TRUST_PRIVATE: + sid2.flags = 0; + break; + default: + sid.flags = MBF_DISABLED; + break; + } + } + CallService(MS_MSG_MODIFYICON, (WPARAM)hContact, (LPARAM)&sid); + CallService(MS_MSG_MODIFYICON, (WPARAM)hContact, (LPARAM)&sid2); + db_dword_set(hContact, MODULENAME, "TrustLevel", level); + + if(!chat_room && options.bHaveMetaContacts) { + HANDLE hMeta = (HANDLE)CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, 0); + if(hMeta && hContact == (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hMeta, 0)) { + //strcat(dbg_msg, "\nrecursing for meta"); + SetEncryptionStatus(hMeta, level); + } + } + } + //PUShowMessage(dbg_msg, SM_NOTIFY); +} + +void InitSRMM() { + // add icon to srmm status icons + if(options.bHaveSRMMIcons) { + hIconNotSecure = LoadIcon(ICON_NOT_PRIVATE, 0); + hIconFinished = LoadIcon(ICON_FINISHED, 0); + hIconPrivate = LoadIcon(ICON_PRIVATE, 0); + hIconUnverified = LoadIcon(ICON_UNVERIFIED, 0); + + StatusIconData sid = {0}; + sid.cbSize = sizeof(sid); + sid.szModule = MODULENAME; + sid.dwId = 0; + sid.hIcon = hIconFinished; + sid.hIconDisabled = hIconNotSecure; + sid.flags = MBF_DISABLED | MBF_HIDDEN; + sid.szTooltip = Translate(LANG_OTR_TOOLTIP); + CallService(MS_MSG_ADDICON, 0, (LPARAM)&sid); + + sid.dwId = 1; + sid.hIcon = hIconPrivate; + sid.hIconDisabled = hIconUnverified; + CallService(MS_MSG_ADDICON, 0, (LPARAM)&sid); + + // hook the window events so that we can can change the status of the icon + + hEventIconPressed = HookEvent(ME_MSG_ICONPRESSED, SVC_IconPressed); + } +} +void DeinitSRMM() { + UnhookEvent(hEventIconPressed); + ReleaseIcon(ICON_NOT_PRIVATE, 0); + ReleaseIcon(ICON_FINISHED, 0); + ReleaseIcon(ICON_PRIVATE, 0); + ReleaseIcon(ICON_UNVERIFIED, 0); + hIconNotSecure = hIconFinished = hIconPrivate = hIconUnverified =0; +} \ No newline at end of file diff --git a/MirOTR/svcs_srmm.h b/MirOTR/svcs_srmm.h new file mode 100644 index 0000000..4bc2046 --- /dev/null +++ b/MirOTR/svcs_srmm.h @@ -0,0 +1,7 @@ +#pragma once + +int WindowEvent(WPARAM wParam, LPARAM lParam); +int SVC_IconPressed(WPARAM wParam, LPARAM lParam); +void SetEncryptionStatus(HANDLE hContact, TrustLevel level); +void InitSRMM(); +void DeinitSRMM(); \ No newline at end of file diff --git a/MirOTR/targetver.h b/MirOTR/targetver.h new file mode 100644 index 0000000..c448111 --- /dev/null +++ b/MirOTR/targetver.h @@ -0,0 +1,24 @@ +#pragma once + +// Die folgenden Makros definieren die mindestens erforderliche Plattform. Die mindestens erforderliche Plattform +// ist die früheste Windows-, Internet Explorer-Version usw., die über die erforderlichen Features zur Ausführung +// Ihrer Anwendung verfügt. Die Makros aktivieren alle Funktionen, die auf den Plattformversionen bis +// einschließlich der angegebenen Version verfügbar sind. + +// Ändern Sie folgende Definitionen für Plattformen, die älter als die unten angegebenen sind. +// Unter MSDN finden Sie die neuesten Informationen über die entsprechenden Werte für die unterschiedlichen Plattformen. +#ifndef WINVER // Gibt an, dass Windows 2000 die mindestens erforderliche Plattform ist. +#define WINVER 0x0500 // Ändern Sie den entsprechenden Wert, um auf andere Versionen von Windows abzuzielen. +#endif + +#ifndef _WIN32_WINNT // Gibt an, dass Windows 2000 die mindestens erforderliche Plattform ist. +#define _WIN32_WINNT 0x0500 // Ändern Sie den entsprechenden Wert, um auf andere Versionen von Windows abzuzielen. +#endif + +#ifndef _WIN32_WINDOWS // Gibt an, dass Windows 98 die mindestens erforderliche Plattform ist. +#define _WIN32_WINDOWS 0x0410 // Ändern Sie den entsprechenden Wert, um auf mindestens Windows Me abzuzielen. +#endif + +#ifndef _WIN32_IE // Gibt an, dass Internet Explorer 7.0 die mindestens erforderliche Plattform ist. +#define _WIN32_IE 0x0700 // Ändern Sie den entsprechenden Wert, um auf andere Versionen von IE abzuzielen. +#endif diff --git a/MirOTR/utils.cpp b/MirOTR/utils.cpp new file mode 100644 index 0000000..59dd5b2 --- /dev/null +++ b/MirOTR/utils.cpp @@ -0,0 +1,400 @@ +#include "stdafx.h" +#include "utils.h" + +CRITICAL_SECTION lib_cs; + +void InitUtils() { + InitializeCriticalSection(&lib_cs); +} +void DeinitUtils() { + DeleteCriticalSection(&lib_cs); +} + +void lib_cs_lock() { + EnterCriticalSection(&lib_cs); +} + +void lib_cs_unlock() { + LeaveCriticalSection(&lib_cs); +} + +HANDLE find_contact(const char* userid, const char* protocol) { + HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); + while ( hContact != NULL ) + { + const char *proto = contact_get_proto(hContact); + if(proto && strcmp(proto, protocol) == 0) { + char *name = contact_get_id(hContact); + if(name && strcmp(name, userid) == 0) { + mir_free(name); + return hContact; + } + mir_free(name); + } + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); + } + + return 0; +} + +/* Look up a connection context by hContact from the given +* OtrlUserState. If add_if_missing is true, allocate and return a new +* context if one does not currently exist. In that event, call +* add_app_data(data, context) so that app_data and app_data_free can be +* filled in by the application, and set *addedp to 1. */ +ConnContext * otrl_context_find_miranda(OtrlUserState us, HANDLE hContact) +{ + ConnContext ** curp; + if (!hContact) return NULL; + for (curp = &(us->context_root); *curp; curp = &((*curp)->next)) { + if ((*curp)->app_data == hContact ) return *curp; + } + return NULL; +} + +/* What level of trust do we have in the privacy of this ConnContext? */ +TrustLevel otr_context_get_trust(ConnContext *context) +{ + TrustLevel level = TRUST_NOT_PRIVATE; + + if (context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED) { + if (context->active_fingerprint->trust && + context->active_fingerprint->trust[0] != '\0') { + level = TRUST_PRIVATE; + } else { + level = TRUST_UNVERIFIED; + } + } else if (context && context->msgstate == OTRL_MSGSTATE_FINISHED) { + level = TRUST_FINISHED; + } + + return level; +} + +/* Convert a 20-byte hash value to a 45-byte human-readable value */ +void otrl_privkey_hash_to_humanT(TCHAR human[45], const unsigned char hash[20]) +{ + int word, byte; + TCHAR *p = human; + + for(word=0; word<5; ++word) { + for(byte=0; byte<4; ++byte) { + _stprintf(p, _T("%02X"), hash[word*4+byte]); + p += 2; + } + *(p++) = ' '; + } + /* Change that last ' ' to a '\0' */ + --p; + *p = '\0'; +} + +char* contact_get_id(HANDLE hContact, bool bNameOnError) { + char* pszUniqueID = NULL; + CONTACTINFO ci; + ZeroMemory(&ci, sizeof(ci)); + ci.cbSize = sizeof(ci); + ci.hContact = hContact; + ci.dwFlag = CNF_UNIQUEID; + + if (CallService(MS_CONTACT_GETCONTACTINFO, 0, (LPARAM)&ci) == 0) + { + if (ci.type == CNFT_ASCIIZ) { + pszUniqueID = (char*)ci.pszVal; // MS_CONTACT_GETCONTACTINFO uses mir_alloc + } else if (ci.type == CNFT_DWORD) { + pszUniqueID = (char*)mir_alloc(15); + if (pszUniqueID) + mir_snprintf(pszUniqueID, 15, ("%u"), ci.dVal); + } else if (ci.type == CNFT_WORD) { + pszUniqueID = (char*)mir_alloc(15); + if (pszUniqueID) + mir_snprintf(pszUniqueID, 15, ("%u"), ci.wVal); + } else if (ci.type == CNFT_BYTE) { + pszUniqueID = (char*)mir_alloc(15); + if (pszUniqueID) + mir_snprintf(pszUniqueID, 15, ("%u"), ci.bVal); + } + } + if (!pszUniqueID && bNameOnError) { + char *name = (char *)CallService( MS_CLIST_GETCONTACTDISPLAYNAME, ( WPARAM )hContact, 0 ); + if (name) pszUniqueID = mir_strdup(name); + } + return pszUniqueID; +} + +__inline const TCHAR* contact_get_nameT(HANDLE hContact) { + return (TCHAR*)CallService( MS_CLIST_GETCONTACTDISPLAYNAME, ( WPARAM )hContact, GCDNF_TCHAR ); +} + +__inline const char* contact_get_proto(HANDLE hContact) { + char *uproto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + return uproto; +} + +__inline const char* contact_get_account(HANDLE hContact) { + char *uacc = (char *)CallService(MS_PROTO_GETCONTACTBASEACCOUNT, (WPARAM)hContact, 0); + return uacc; +} + +void ShowPopup(const TCHAR* line1, const TCHAR* line2, int timeout, const HANDLE hContact) { + if(CallService(MS_SYSTEM_TERMINATED, 0, 0)) return; + + if ( !options.bHavePopups) { + TCHAR title[256]; + _stprintf(title, _T("%s Message"), _T(MODULENAME)); + + if(line1 && line2) { + TCHAR *message = new TCHAR[_tcslen(line1) + _tcslen(line2) + 3]; // newline and null terminator + _stprintf(message, _T("%s\r\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; + } + + POPUPDATAT ppd = {0}; + //memset((void *)&ppd, 0, sizeof(POPUPDATAT)); + + ppd.lchContact = hContact; + ppd.lchIcon = NULL; + + if(line1 && line2) { + _tcsncpy( ppd.lptzContactName, line1, MAX_CONTACTNAME-1 ); + _tcsncpy( ppd.lptzText, line2, MAX_SECONDLINE-1 ); + } else if(line1) { + if (PUIsSecondLineShown()) _tcsncpy( ppd.lptzText, line1, MAX_SECONDLINE-1 ); + else _tcsncpy( ppd.lptzContactName, line1, MAX_CONTACTNAME-1 ); + } + else if(line2) { + if (PUIsSecondLineShown()) _tcsncpy( ppd.lptzText, line2, MAX_SECONDLINE-1 ); + else _tcsncpy( ppd.lptzContactName, line2, MAX_CONTACTNAME-1 ); + } + + ppd.iSeconds = timeout; + + ppd.PluginWindowProc = NULL; + ppd.PluginData = NULL; + + PUAddPopUpT(&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 && !options.bHavePopups) disp = ED_BAL; + if(disp == ED_BAL && !ServiceExists(MS_CLIST_SYSTRAY_NOTIFY)) disp = ED_MB; + + _stprintf(buffer, _T("%s Warning"), _T(MODULENAME)); + + TCHAR *message; + switch(disp) { + case ED_POP: + message = new TCHAR[_tcslen(msg) + 515]; // newline and null terminator + _stprintf(message, _T("%s\r\n%s"), buffer, msg); + PUShowMessageT(message, SM_WARNING); + delete message; + 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= MODULENAME; + sn.tszInfoTitle = buffer; + sn.tszInfo = msg; +#ifdef _UNICODE + sn.dwInfoFlags = NIIF_WARNING | NIIF_INTERN_UNICODE; +#else + sn.dwInfoFlags = NIIF_WARNING; +#endif + sn.uTimeout = 10; + + CallService(MS_CLIST_SYSTRAY_NOTIFY, 0, (LPARAM)&sn); + } + + 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 && !options.bHavePopups) disp = ED_BAL; + if(disp == ED_BAL && !ServiceExists(MS_CLIST_SYSTRAY_NOTIFY)) disp = ED_MB; + + _stprintf(buffer, _T("%s Error"), _T(MODULENAME)); + + + TCHAR *message; + switch(disp) { + case ED_POP: + message = new TCHAR[_tcslen(msg) + 515]; // newline and null terminator + _stprintf(message, _T("%s\r\n%s"), buffer, msg); + PUShowMessageT(message, SM_WARNING); + delete message; + break; + case ED_MB: + MessageBox(0, msg, buffer, MB_OK | MB_ICONERROR); + break; + case ED_BAL: + { + MIRANDASYSTRAYNOTIFY sn = {0}; + sn.cbSize = sizeof(sn); + sn.szProto = MODULENAME; + sn.tszInfoTitle = buffer; + sn.tszInfo = msg; +#ifdef _UNICODE + sn.dwInfoFlags = NIIF_ERROR | NIIF_INTERN_UNICODE; +#else + sn.dwInfoFlags = NIIF_ERROR; +#endif + sn.uTimeout = 10; + + CallService(MS_CLIST_SYSTRAY_NOTIFY, 0, (LPARAM)&sn); + } + + break; + } +} + + +void ShowPopupUtf(const char* line1, const char* line2, int timeout, const HANDLE hContact) { + TCHAR* l1 = (line1) ? mir_utf8decodeT(line1) : NULL; + TCHAR* l2 = (line2) ? mir_utf8decodeT(line2) : NULL; + ShowPopup(l1, l2, timeout, hContact); + if (l1) mir_free(l1); + if (l2) mir_free(l2); +} + +void ShowWarningUtf(char* msg) { + TCHAR* m = (msg) ? mir_utf8decodeT(msg) : NULL; + ShowWarning(m); + if (m) mir_free(m); +} +void ShowErrorUtf(char* msg) { + TCHAR* m = (msg) ? mir_utf8decodeT(msg) : NULL; + ShowError(m); + if (m) mir_free(m); +} + +void ShowMessageInline(const HANDLE hContact, const TCHAR *msg) { + TCHAR buff[1024]; + mir_sntprintf(buff, 1024, _T("%s%s"), TranslateT(LANG_INLINE_PREFIX), msg); + + PROTORECVEVENT pre = {0}; + pre.timestamp = time(0); + char *utf = mir_utf8encodeT(buff); + pre.szMessage = utf; + pre.flags = PREF_UTF|PREF_BYPASS_OTR; + //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); + mir_free(utf); +} + +void ShowMessageInlineUtf(const HANDLE hContact, const char *msg) { + char buff[1024]; + mir_snprintf(buff, 1024, "%s%s", Translate(LANG_INLINE_PREFIX), msg); + + PROTORECVEVENT pre = {0}; + pre.timestamp = time(0); + pre.szMessage = buff; + pre.flags = PREF_UTF|PREF_BYPASS_OTR; + //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); +} + + +void ShowMessageUtf(const HANDLE hContact, const char *msg) { + if(options.msg_inline) + ShowMessageInlineUtf(hContact, msg); + if(options.msg_popup) + ShowPopupUtf(Translate(LANG_OTR_INFO), msg, 0, hContact); +} + +void ShowMessage(const HANDLE hContact, const TCHAR *msg) { + if(options.msg_inline) + ShowMessageInline(hContact, msg); + if(options.msg_popup) + ShowPopup(TranslateT(LANG_OTR_INFO), msg, 0, hContact); +} + + +/* +bool GetEncryptionStatus(HANDLE hContact) { + char *proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + bool chat_room = (proto && DBGetContactSettingByte(hContact, proto, "ChatRoom", 0)); + + if(!chat_room) { + if (options.bHaveMetaContacts) { + HANDLE hMeta = (HANDLE)CallService(MS_MC_GETMETACONTACT, (WPARAM)hContact, 0); + if(hMeta && hContact == (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT, (WPARAM)hMeta, 0)) { + //strcat(dbg_msg, "\nrecursing for meta"); + return GetEncryptionStatus(hMeta); + } + return 0!=DBGetContactSettingByte(hContact, MODULENAME, "Encrypted", 0 ); + } + } + return 0; +} +*/ + +const TCHAR *policy_to_string(OtrlPolicy policy) { + switch (policy) { + case OTRL_POLICY_NEVER: + return TranslateT(LANG_POLICY_NEVER); + break; + case OTRL_POLICY_OPPORTUNISTIC: + return TranslateT(LANG_POLICY_OPP); + break; + case OTRL_POLICY_MANUAL: + case OTRL_POLICY_MANUAL_MOD: + return TranslateT(LANG_POLICY_MANUAL); + break; + case OTRL_POLICY_ALWAYS: + return TranslateT(LANG_POLICY_ALWAYS); + break; + } + return TranslateT(LANG_POLICY_DEFAULT); +} + +OtrlPolicy policy_from_string(const TCHAR *polstring) { + if (_tcscmp(polstring, TranslateT(LANG_POLICY_NEVER)) == 0) + return OTRL_POLICY_NEVER; + else if (_tcscmp(polstring, TranslateT(LANG_POLICY_OPP)) == 0) + return OTRL_POLICY_OPPORTUNISTIC; + else if (_tcscmp(polstring, TranslateT(LANG_POLICY_MANUAL)) == 0) + return OTRL_POLICY_MANUAL_MOD; + else if (_tcscmp(polstring, TranslateT(LANG_POLICY_ALWAYS)) == 0) + return OTRL_POLICY_ALWAYS; + else + return CONTACT_DEFAULT_POLICY; +} \ No newline at end of file diff --git a/MirOTR/utils.h b/MirOTR/utils.h new file mode 100644 index 0000000..ac1ad1b --- /dev/null +++ b/MirOTR/utils.h @@ -0,0 +1,47 @@ +#pragma once +#ifndef __HEADER_UTILS_H +#define __HEADER_UTILS_H +#include "stdafx.h" + + +void lib_cs_lock(); +void lib_cs_unlock(); + +HANDLE find_contact(const char* userid, const char* protocol); +ConnContext * otrl_context_find_miranda(OtrlUserState us, HANDLE hContact); + +typedef enum { + TRUST_NOT_PRIVATE, + TRUST_FINISHED, + TRUST_UNVERIFIED, + TRUST_PRIVATE +} TrustLevel; +TrustLevel otr_context_get_trust(ConnContext *context); + +void otrl_privkey_hash_to_humanT(TCHAR human[45], const unsigned char hash[20]); + +char* contact_get_id(HANDLE hContact, bool bNameOnError=true); +__inline const char* contact_get_proto(HANDLE hContact); +__inline const char* contact_get_account(HANDLE hContact); +extern __inline const TCHAR* contact_get_nameT(HANDLE hContact); + +void ShowPopup(const TCHAR* line1, const TCHAR* line2, int timeout, const HANDLE hContact = NULL); +void ShowWarning(TCHAR* msg); +void ShowError(TCHAR* msg); +void ShowPopupUtf(const char* line1, const char* line2, int timeout, const HANDLE hContact = NULL); +void ShowWarningUtf(char* msg); +void ShowErrorUtf(char* msg); + +void ShowMessageInline(const HANDLE hContact, const TCHAR *msg); +void ShowMessageInlineUtf(const HANDLE hContact, const char *msg); + +void ShowMessage(const HANDLE hContact, const TCHAR *msg); +void ShowMessageUtf(const HANDLE hContact, const char *msg); + +void InitUtils(); +void DeinitUtils(); + +const TCHAR *policy_to_string(OtrlPolicy policy); +OtrlPolicy policy_from_string(const TCHAR *polstring); + +#endif \ No newline at end of file diff --git a/MirOTR/version.h b/MirOTR/version.h new file mode 100644 index 0000000..bdfa60a --- /dev/null +++ b/MirOTR/version.h @@ -0,0 +1,37 @@ +#ifndef OTR_PRIVATE_H +#define OTR_PRIVATE_H + +/* VERSION DEFINITIONS */ +#define VER_MAJOR 0 +#define VER_MINOR 8 +#define VER_RELEASE 6 +#define VER_BUILD 1 + +#define __STRINGIZE(x) #x +#define VER_STRING "0.8.6.1" + +#ifdef _UNICODE + #define SHORT_NAME_STRING "Miranda OTR" + #define DESC_STRING "OTR (Off-the-Record) plugin for Miranda IM" +#else + #define SHORT_NAME_STRING "Miranda OTR (ANSI)" + #define DESC_STRING "OTR (Off-the-Record) plugin for Miranda IM (ANSI)" +#endif +#define LONGDESC_STRING DESC_STRING" ("__DATE__")\r\n(using some code and ideas from SecureIM, Pidgin-OTR and old Miranda OTR (by SJE))" +#define COMPANY_NAME "" +#define FILE_VERSION VER_STRING +#define FILE_DESCRIPTION DESC_STRING +#define INTERNAL_NAME "" +#define LEGAL_COPYRIGHT "© ProgAndy" +#define LEGAL_COPYRIGHT_LONG "© ProgAndy" +#define LEGAL_TRADEMARKS "" +#define ORIGINAL_FILENAME "mirotr.dll" +#define PRODUCT_NAME DESC_STRING +#define PRODUCT_VERSION VER_STRING + + +#define AUTHOR "ProgAndy" +#define AUTHOR_MAIL "" +#define HOMEPAGE "http://progandy.co.cc" + +#endif /*OTR_PRIVATE_H*/ -- cgit v1.2.3