summaryrefslogtreecommitdiff
path: root/plugins/MirOTR/src
diff options
context:
space:
mode:
authorRené Schümann <white06tiger@gmail.com>2015-03-14 19:58:15 +0000
committerRené Schümann <white06tiger@gmail.com>2015-03-14 19:58:15 +0000
commit5a97f4b7c941dc704ab4dd2c370d19bb556c1ce6 (patch)
tree7ca8e397891238535958645ae5af8427068bcfd9 /plugins/MirOTR/src
parent02eaf116807ce4b8b49dfaee47910d2dffbfef8f (diff)
MirOTR: part 3, file/folder structure change
git-svn-id: http://svn.miranda-ng.org/main/trunk@12404 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c
Diffstat (limited to 'plugins/MirOTR/src')
-rw-r--r--plugins/MirOTR/src/dbfilter.cpp351
-rw-r--r--plugins/MirOTR/src/dbfilter.h4
-rw-r--r--plugins/MirOTR/src/dialogs.cpp759
-rw-r--r--plugins/MirOTR/src/dialogs.h8
-rw-r--r--plugins/MirOTR/src/dllmain.cpp121
-rw-r--r--plugins/MirOTR/src/entities.cpp424
-rw-r--r--plugins/MirOTR/src/entities.h24
-rw-r--r--plugins/MirOTR/src/icons.cpp36
-rw-r--r--plugins/MirOTR/src/icons.h13
-rw-r--r--plugins/MirOTR/src/language.h121
-rw-r--r--plugins/MirOTR/src/mirotrmenu.cpp260
-rw-r--r--plugins/MirOTR/src/mirotrmenu.h45
-rw-r--r--plugins/MirOTR/src/options.cpp868
-rw-r--r--plugins/MirOTR/src/options.h69
-rw-r--r--plugins/MirOTR/src/otr.cpp395
-rw-r--r--plugins/MirOTR/src/otr.h51
-rw-r--r--plugins/MirOTR/src/resource.h95
-rw-r--r--plugins/MirOTR/src/stdafx.cpp8
-rw-r--r--plugins/MirOTR/src/stdafx.h96
-rw-r--r--plugins/MirOTR/src/striphtml.cpp122
-rw-r--r--plugins/MirOTR/src/striphtml.h8
-rw-r--r--plugins/MirOTR/src/svcs_menu.cpp189
-rw-r--r--plugins/MirOTR/src/svcs_menu.h13
-rw-r--r--plugins/MirOTR/src/svcs_proto.cpp384
-rw-r--r--plugins/MirOTR/src/svcs_proto.h12
-rw-r--r--plugins/MirOTR/src/svcs_srmm.cpp156
-rw-r--r--plugins/MirOTR/src/svcs_srmm.h7
-rw-r--r--plugins/MirOTR/src/utils.cpp382
-rw-r--r--plugins/MirOTR/src/utils.h48
-rw-r--r--plugins/MirOTR/src/version.h14
30 files changed, 5083 insertions, 0 deletions
diff --git a/plugins/MirOTR/src/dbfilter.cpp b/plugins/MirOTR/src/dbfilter.cpp
new file mode 100644
index 0000000000..9f2223100d
--- /dev/null
+++ b/plugins/MirOTR/src/dbfilter.cpp
@@ -0,0 +1,351 @@
+#include "stdafx.h"
+
+static HANDLE hDBEventPreAdd, hDBEventAdded, hContactSettingChanged;
+static CRITICAL_SECTION RemoveChainCS={0}, *lpRemoveChainCS = &RemoveChainCS;
+static UINT_PTR timerId = 0;
+
+struct DeleteEventNode {
+ DeleteEventNode *next;
+ time_t timestamp;
+ MCONTACT hContact;
+ MEVENT 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 = { sizeof(info) };
+ next = DeleteEvents.first;
+ while (current = next) {
+ if (difftime(time(0), current->timestamp) < 1) break;
+ if (!db_event_get(current->hDbEvent, &info)) // && info.flags&DBEF_READ)
+ {
+ db_event_delete(current->hContact, 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 OnDatabaseEventPreAdd(WPARAM hContact, LPARAM lParam)
+{
+ if (!options.prefix_messages || !lParam)
+ return 0;
+
+ 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 = GetContactProto(hContact);
+ if (!proto)
+ return 0;
+ if (db_get_b(hContact, proto, "ChatRoom", 0) == 1)
+ return 0;
+
+ if(strcmp(proto, META_PROTO) == 0) {
+ hContact = db_mc_getMostOnline(hContact);
+ if (!hContact) return 0;
+ proto = GetContactProto(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 = (DWORD)strlen(msg);
+ if (dbei->flags & DBEF_UTF) {
+ int prefixlen = (int)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 = (int)wcslen(prefixw);
+ if (wcsncmp(msgw, prefixw, prefixlenw) == 0) {
+ mir_free(prefixw);
+ return 0;
+ }
+ int msglenw = (int)wcslen(msgw);
+
+ char *prefix = mir_utf8decodeA(options.prefix);
+ int prefixlen = (int)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);
+ 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 = (int)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;
+
+ db_event_add(hContact, &my_dbei);
+ if (newmsg) mir_free(newmsg);
+
+ // stop original event from being added
+ return 1;
+}
+
+ int OnDatabaseEventAdded(WPARAM hContact, LPARAM lParam)
+ {
+ if (!options.delete_history) return 0;
+
+ static char* prefixutf = mir_utf8encodeT(TranslateT(LANG_INLINE_PREFIX));
+ static char* prefix = Translate(LANG_INLINE_PREFIX);
+ static DWORD lenutf = (DWORD)strlen(prefixutf);
+ static DWORD len = (DWORD)strlen(prefix);
+
+ DBEVENTINFO info = { sizeof(info) };
+ info.cbBlob = lenutf*2;
+ info.pBlob = (PBYTE)mir_alloc(info.cbBlob);
+ if (!db_event_get(lParam, &info)) {
+ if(info.eventType == EVENTTYPE_MESSAGE) {
+ MCONTACT hSub;
+ if((hSub = db_mc_getMostOnline(hContact)) != 0)
+ hContact = hSub;
+
+ ConnContext *context = otrl_context_find_miranda(otr_user_state, hContact);
+ if (context && otr_context_get_trust(context) != TRUST_NOT_PRIVATE ) {
+ // only delete encrypted messages that are no OTR system messages
+ if ( options.delete_systeminfo ||
+ ((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)))))
+ {
+ DeleteEventNode *node = new DeleteEventNode();
+ node->hContact = hContact;
+ node->hDbEvent = 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 FinishSession(MCONTACT hContact) {
+ if (!hContact) return;
+ ConnContext *context = otrl_context_find_miranda(otr_user_state, hContact);
+ TrustLevel level = otr_context_get_trust(context);
+ if (level == TRUST_UNVERIFIED || level == TRUST_PRIVATE) {
+ otrl_context_force_finished(context);
+ //SetEncryptionStatus(hContact, TRUST_FINISHED);
+ otr_gui_gone_insecure(context->app_data, context);
+ //otrl_message_disconnect(otr_user_state, &ops, hContact, context->accountname, context->protocol, context->username);
+ //SetEncryptionStatus(hContact, TRUST_NOT_PRIVATE);
+ }
+ return;
+}
+
+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;
+
+ MCONTACT hContact = mwd->hContact, hTemp;
+ if((hTemp = db_mc_getMostOnline(hContact)) != 0)
+ hContact = hTemp;
+
+ if (!CallService(MS_PROTO_ISPROTOONCONTACT, 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;
+}
+
+// if it's a protocol going offline, attempt to send terminate session to all contacts of that protocol
+// (this would be hooked as the ME_CLIST_STATUSMODECHANGE handler except that event is sent *after* the proto goes offline)
+int StatusModeChange(WPARAM wParam, LPARAM lParam) {
+ int status = (int)wParam;
+
+ if(status != ID_STATUS_OFFLINE ) return 0;
+
+ const char *proto = (char *)lParam;
+ MCONTACT hContact;
+
+ lib_cs_lock();
+
+ ConnContext *context = otr_user_state->context_root;
+ while(context) {
+ if(context->msgstate == OTRL_MSGSTATE_ENCRYPTED && (proto == 0 || strcmp(proto, context->protocol) == 0)) {
+ hContact = (MCONTACT)context->app_data;
+
+ if(hContact) {
+ otrl_message_disconnect(otr_user_state, &ops, (void*)hContact, context->accountname, context->protocol, context->username);
+ SetEncryptionStatus(hContact, TRUST_NOT_PRIVATE);
+ }
+
+ }
+ context = context->next;
+ }
+ lib_cs_unlock();
+
+ return 0;
+}
+
+int OnContactSettingChanged(WPARAM hContact, LPARAM lParam)
+{
+ if (!options.end_offline)
+ return 0;
+ DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *)lParam;
+ if (!lParam || strcmp(cws->szSetting, "Status") != 0) return 0;
+ int status=0;
+ switch (cws->value.type){
+ case DBVT_WORD:
+ status = cws->value.wVal;
+ break;
+ case DBVT_BYTE:
+ status = cws->value.bVal;
+ break;
+ case DBVT_DWORD:
+ status = cws->value.dVal;
+ break;
+ }
+ if (status == ID_STATUS_OFFLINE) {
+ if (!hContact) {
+ // Protocol is going offline
+ // Terminate sessions with all contacts of that proto
+ StatusModeChange((WPARAM) ID_STATUS_OFFLINE, (LPARAM)cws->szModule);
+ return 0;
+ }else if(CallService(MS_PROTO_ISPROTOONCONTACT, hContact, (LPARAM)MODULENAME)) {
+ // only care about contacts to which this filter is attached
+ FinishSession(hContact);
+ }
+ }
+
+
+ return 0;
+}
+
+void InitDBFilter() {
+ InitializeCriticalSectionAndSpinCount(lpRemoveChainCS, 500);
+ hDBEventPreAdd = HookEvent(ME_DB_EVENT_FILTER_ADD, OnDatabaseEventPreAdd);
+ hDBEventAdded = HookEvent(ME_DB_EVENT_ADDED, OnDatabaseEventAdded);
+ hContactSettingChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED, OnContactSettingChanged);
+ timerId = SetTimer(0, 0, 1000, DeleteTimerProc);
+}
+void DeinitDBFilter() {
+ UnhookEvent(hDBEventPreAdd);
+ hDBEventPreAdd = 0;
+ UnhookEvent(hDBEventAdded);
+ hDBEventAdded = 0;
+ UnhookEvent(hContactSettingChanged);
+ hContactSettingChanged=0;
+ if (timerId) KillTimer(0, timerId);
+ DeleteTimerProc(0,0,0,0);
+ DeleteCriticalSection(lpRemoveChainCS);
+} \ No newline at end of file
diff --git a/plugins/MirOTR/src/dbfilter.h b/plugins/MirOTR/src/dbfilter.h
new file mode 100644
index 0000000000..99a6d50428
--- /dev/null
+++ b/plugins/MirOTR/src/dbfilter.h
@@ -0,0 +1,4 @@
+#define DBEF_OTR_PREFIXED 0x8000
+
+void InitDBFilter();
+void DeinitDBFilter(); \ No newline at end of file
diff --git a/plugins/MirOTR/src/dialogs.cpp b/plugins/MirOTR/src/dialogs.cpp
new file mode 100644
index 0000000000..d2609bff09
--- /dev/null
+++ b/plugins/MirOTR/src/dialogs.cpp
@@ -0,0 +1,759 @@
+#include "stdafx.h"
+
+struct SmpData {
+ HWND dialog;
+ TrustLevel oldlevel;
+ ConnContext *context;
+ bool responder;
+ TCHAR *question;
+};
+typedef std::map<HANDLE, SmpData> SmpForContactMap;
+SmpForContactMap smp_for_contact;
+
+
+INT_PTR CALLBACK DlgSMPUpdateProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ {
+ if (!lParam) {
+ EndDialog(hwndDlg, IDCANCEL);
+ return FALSE;
+ }
+ TranslateDialogDefault( hwndDlg );
+
+ SmpData *data = (SmpData*)lParam;
+ ConnContext *context = data->context;
+ MCONTACT hContact = (MCONTACT)context->app_data;
+ data->dialog = hwndDlg;
+ //smp_for_contact.insert(SmpForContactMap::value_type(context->app_data, *data));
+ if (smp_for_contact[context->app_data].dialog) SendMessage(smp_for_contact[context->app_data].dialog, WMU_REFRESHSMP, 0, 0);
+ smp_for_contact[context->app_data].context = data->context;
+ smp_for_contact[context->app_data].dialog = hwndDlg;
+ smp_for_contact[context->app_data].oldlevel = data->oldlevel;
+ smp_for_contact[context->app_data].responder = data->responder;
+ mir_free(data);
+
+ TCHAR title[512], *proto = mir_a2t(GetContactProto(hContact));
+ const TCHAR *name =contact_get_nameT(hContact);
+ mir_sntprintf(title, SIZEOF(title), TranslateT(LANG_SMP_PROGRESS_TITLE), name, proto);
+ SetWindowText(hwndDlg, title);
+ mir_sntprintf(title, SIZEOF(title), TranslateT(LANG_SMP_PROGRESS_DESC), name, proto);
+ mir_free(proto);
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_HEADPRO, title);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)context);
+
+ // 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
+
+ SendDlgItemMessage(hwndDlg, IDC_PGB_SMP, PBM_SETRANGE, 0, MAKELONG(0, 100));
+ SendDlgItemMessage(hwndDlg, IDC_PGB_SMP, PBM_SETPOS, 10, 0);
+
+ EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), true);
+ EnableWindow(GetDlgItem(hwndDlg, IDOK), false);
+
+ return TRUE;
+ }
+
+ case WMU_REFRESHSMP:
+ {
+ ConnContext *context = (ConnContext*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ SendDlgItemMessage(hwndDlg, IDC_PGB_SMP, PBM_SETPOS, wParam, 0);
+ switch (wParam) {
+ case 0:
+ EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), false);
+ EnableWindow(GetDlgItem(hwndDlg, IDOK), true);
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_PROGRESS, TranslateT(LANG_SMP_ERROR));
+ smp_for_contact.erase(context->app_data);
+ break;
+ case 100:
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDCANCEL), false);
+ EnableWindow(GetDlgItem(hwndDlg, IDOK), true);
+ smp_for_contact.erase(context->app_data);
+ if (context->smstate->sm_prog_state == OTRL_SMP_PROG_SUCCEEDED) {
+ if (context->active_fingerprint->trust && context->active_fingerprint->trust[0]) {
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_PROGRESS, TranslateT(LANG_SMP_SUCCESS));
+ }
+ else {
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_PROGRESS, TranslateT(LANG_SMP_SUCCESS_VERIFY));
+ }
+ }
+ else {
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_PROGRESS, TranslateT(LANG_SMP_FAILED));
+ }
+ }
+ break;
+ default:
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_PROGRESS, _T(""));
+ }
+ }
+ break;
+
+ case WM_COMMAND:
+ switch ( HIWORD( wParam )) {
+ case BN_CLICKED:
+ {
+ ConnContext *context = (ConnContext*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ switch ( LOWORD( wParam )) {
+ case IDCANCEL:
+ otr_abort_smp(context);
+ //break;
+ case IDOK:
+ smp_for_contact.erase(context->app_data);
+ EndDialog(hwndDlg, LOWORD( wParam ));
+ break;
+ }
+ }break;
+ }
+
+ }
+
+ return FALSE;
+}
+
+void SMPInitUpdateDialog(ConnContext *context, bool responder) {
+ if (!context) return;
+ SmpData *data = (SmpData*)mir_calloc(sizeof(SmpData));
+ data->context = context;
+ data->oldlevel = otr_context_get_trust(context);
+ data->responder = responder;
+ CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_SMP_PROGRESS), 0, DlgSMPUpdateProc, (LPARAM) data);
+}
+
+INT_PTR CALLBACK DlgSMPResponseProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ {
+ if (!lParam) {
+ EndDialog(hwndDlg, IDCANCEL);
+ return FALSE;
+ }
+ TranslateDialogDefault( hwndDlg );
+
+ SmpData *data = (SmpData*)lParam;
+ ConnContext *context = data->context;
+ MCONTACT hContact = (MCONTACT)context->app_data;
+ data->dialog = hwndDlg;
+ //smp_for_contact.insert(SmpForContactMap::value_type(context->app_data, *data));
+ if (smp_for_contact[context->app_data].dialog) SendMessage(smp_for_contact[context->app_data].dialog, WMU_REFRESHSMP, 0, 0);
+ smp_for_contact[context->app_data].context = data->context;
+ smp_for_contact[context->app_data].dialog = hwndDlg;
+ smp_for_contact[context->app_data].oldlevel = data->oldlevel;
+ smp_for_contact[context->app_data].responder = data->responder;
+
+ TCHAR buff[512], *proto = mir_a2t(GetContactProto(hContact));
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_SMP_VERIFY_TITLE), contact_get_nameT(hContact), proto);
+ mir_free(proto);
+ SetWindowText(hwndDlg, buff);
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_HEAD, buff);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)context);
+
+ if (data->question) {
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_OTR_SMPQUESTION_RESPOND_DESC), contact_get_nameT(hContact));
+
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_INFO, buff);
+
+ SetDlgItemText(hwndDlg, IDC_EDT_SMP_FIELD1, data->question);
+ SendDlgItemMessage(hwndDlg, IDC_EDT_SMP_FIELD1, EM_SETREADONLY, TRUE, 0);
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_FIELD1, TranslateT(LANG_SMP_QUESTION));
+
+ SetDlgItemText(hwndDlg, IDC_EDT_SMP_FIELD2, _T(""));
+ SendDlgItemMessage(hwndDlg, IDC_EDT_SMP_FIELD2, EM_SETREADONLY, FALSE, 0);
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_FIELD2, TranslateT(LANG_SMP_ANSWER));
+
+
+ ShowWindow(GetDlgItem(hwndDlg, IDOK), SW_SHOWNA);
+ ShowWindow(GetDlgItem(hwndDlg, IDYES), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDNO), SW_HIDE);
+
+ mir_free(data->question);
+ } else {
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_OTR_SMPPASSWORD_RESPOND_DESC), contact_get_nameT(hContact));
+
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_INFO, buff);
+
+ SetDlgItemText(hwndDlg, IDC_EDT_SMP_FIELD1, _T(""));
+ SendDlgItemMessage(hwndDlg, IDC_EDT_SMP_FIELD1, EM_SETREADONLY, TRUE, 0);
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_FIELD1, _T(""));
+
+ SetDlgItemText(hwndDlg, IDC_EDT_SMP_FIELD2, _T(""));
+ SendDlgItemMessage(hwndDlg, IDC_EDT_SMP_FIELD2, EM_SETREADONLY, FALSE, 0);
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_FIELD2, TranslateT(LANG_SMP_PASSWORD));
+
+
+ ShowWindow(GetDlgItem(hwndDlg, IDOK), SW_SHOWNA);
+ ShowWindow(GetDlgItem(hwndDlg, IDYES), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDNO), SW_HIDE);
+ }
+ mir_free(data);
+
+ // 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
+
+
+
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ switch ( HIWORD( wParam )) {
+ case BN_CLICKED:
+ {
+ ConnContext *context = (ConnContext *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ switch ( LOWORD( wParam )) {
+ case IDOK:
+ {
+ SMPInitUpdateDialog(context, true);
+
+ int len = SendDlgItemMessage(hwndDlg, IDC_EDT_SMP_FIELD2, WM_GETTEXTLENGTH, 0, 0);
+ TCHAR *answer = new TCHAR[len+1];
+ GetDlgItemText(hwndDlg, IDC_EDT_SMP_FIELD2, answer, len+1);
+ char *ans = mir_utf8encodeT(answer);
+ delete[] answer;
+
+ otr_continue_smp(context, (const unsigned char *)ans, strlen(ans));
+ mir_free(ans);
+
+ EndDialog(hwndDlg, LOWORD( wParam ));
+ }break;
+ case IDCANCEL:
+ smp_for_contact.erase(context->app_data);
+ EndDialog(hwndDlg, LOWORD( wParam ));
+ break;
+ }
+ }
+ }
+ break;
+
+ }
+
+ return FALSE;
+}
+
+/*
+void SMPInitResponseDialog(ConnContext *context, const TCHAR *question) {
+ if (!context) return;
+ SmpData *data = (SmpData*)mir_calloc(sizeof(SmpData));
+ data->context = context;
+ data->oldlevel = TRUST_NOT_PRIVATE;
+ data->responder = true;
+ data->question = (question) ? mir_tstrdup(question) : NULL;
+ CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_SMP_INPUT), 0, DlgSMPResponseProc, (LPARAM) data);
+}
+*/
+
+INT_PTR CALLBACK DlgProcSMPInitProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch ( msg ) {
+ case WM_INITDIALOG:
+ {
+ if (!lParam) {
+ EndDialog(hwndDlg, IDCANCEL);
+ return FALSE;
+ }
+ TranslateDialogDefault( hwndDlg );
+
+ ConnContext *context = (ConnContext*)lParam;
+ if (smp_for_contact.find(context->app_data) != smp_for_contact.end()) {
+ EndDialog(hwndDlg, IDCANCEL);
+ return FALSE;
+ }
+
+ MCONTACT hContact = (MCONTACT)context->app_data;
+ TCHAR title[512], *proto = mir_a2t(GetContactProto(hContact));
+ mir_sntprintf(title, SIZEOF(title), TranslateT(LANG_SMP_VERIFY_TITLE), contact_get_nameT(hContact), proto);
+ mir_free(proto);
+ SetWindowText(hwndDlg, title);
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_HEAD, title);
+ SetWindowLongPtr(hwndDlg, GWLP_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
+
+
+ HWND cmb = GetDlgItem(hwndDlg, IDC_CBO_SMP_CHOOSE);
+ SendMessage(cmb, CB_ADDSTRING, 0, (WPARAM)TranslateT(LANG_SMPTYPE_QUESTION));
+ SendMessage(cmb, CB_ADDSTRING, 0, (WPARAM)TranslateT(LANG_SMPTYPE_PASSWORD));
+ SendMessage(cmb, CB_ADDSTRING, 0, (WPARAM)TranslateT(LANG_SMPTYPE_FINGERPRINT));
+ SendMessage(cmb, CB_SELECTSTRING, -1, (WPARAM)TranslateT(LANG_SMPTYPE_QUESTION));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CBO_SMP_CHOOSE), TRUE);
+
+
+ Fingerprint *fp = context->active_fingerprint;
+ if (!fp) {
+ EndDialog(hwndDlg, IDCANCEL);
+ return FALSE;
+ }
+ TCHAR buff[1024];
+ if (!fp->trust || fp->trust[0] == '\0')
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_OTR_SMPQUESTION_VERIFY_DESC), contact_get_nameT(hContact));
+ else
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_OTR_SMPQUESTION_VERIFIED_DESC), contact_get_nameT(hContact));
+
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_INFO, buff);
+
+ SetDlgItemText(hwndDlg, IDC_EDT_SMP_FIELD1, _T(""));
+ SendDlgItemMessage(hwndDlg, IDC_EDT_SMP_FIELD1, EM_SETREADONLY, FALSE, 0);
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_FIELD1, TranslateT(LANG_SMP_QUESTION));
+
+ SetDlgItemText(hwndDlg, IDC_EDT_SMP_FIELD2, _T(""));
+ SendDlgItemMessage(hwndDlg, IDC_EDT_SMP_FIELD2, EM_SETREADONLY, FALSE, 0);
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_FIELD2, TranslateT(LANG_SMP_ANSWER));
+
+
+ ShowWindow(GetDlgItem(hwndDlg, IDOK), SW_SHOWNA);
+ ShowWindow(GetDlgItem(hwndDlg, IDYES), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDNO), SW_HIDE);
+ SetFocus(GetDlgItem(hwndDlg, IDC_CBO_SMP_CHOOSE));
+
+ return FALSE;
+ }
+
+ case WM_COMMAND:
+ switch ( HIWORD( wParam )) {
+ case BN_CLICKED:
+ {
+ ConnContext *context = (ConnContext*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ MCONTACT hContact = (MCONTACT)context->app_data;
+ TCHAR msg[1024];
+ switch ( LOWORD( wParam )) {
+ case IDCANCEL:
+ EndDialog(hwndDlg, LOWORD( wParam ));
+ break;
+ case IDOK:
+ GetDlgItemText(hwndDlg, IDC_CBO_SMP_CHOOSE, msg, 255);
+ if (_tcsncmp(msg, TranslateT(LANG_SMPTYPE_QUESTION), 255)==0) {
+ if (smp_for_contact.find(context->app_data) != smp_for_contact.end()) {
+ TCHAR msg[512];
+ mir_sntprintf(msg, SIZEOF(msg), TranslateT(LANG_SMP_IN_PROGRESS), contact_get_nameT(hContact));
+ ShowError(msg);
+ }else {
+ int len = SendDlgItemMessage(hwndDlg, IDC_EDT_SMP_FIELD1, WM_GETTEXTLENGTH, 0, 0);
+ TCHAR *question = new TCHAR[len+1];
+ GetDlgItemText(hwndDlg, IDC_EDT_SMP_FIELD1, question, len+1);
+ char *quest = mir_utf8encodeT(question);
+ delete question;
+
+ len = SendDlgItemMessage(hwndDlg, IDC_EDT_SMP_FIELD2, WM_GETTEXTLENGTH, 0, 0);
+ TCHAR *answer = new TCHAR[len+1];
+ GetDlgItemText(hwndDlg, IDC_EDT_SMP_FIELD2, answer, len+1);
+ char *ans = mir_utf8encodeT(answer);
+ delete answer;
+
+ SMPInitUpdateDialog(context, false);
+ otr_start_smp(context, quest, (const unsigned char*)ans, strlen(ans));
+ mir_free(quest);
+ mir_free(ans);
+ }
+
+ }else if (_tcsncmp(msg, TranslateT(LANG_SMPTYPE_PASSWORD), 255)==0) {
+ if (smp_for_contact.find(context->app_data) != smp_for_contact.end()) {
+ TCHAR msg[512];
+ mir_sntprintf(msg, SIZEOF(msg), TranslateT(LANG_SMP_IN_PROGRESS), contact_get_nameT(hContact));
+ ShowError(msg);
+ }else {
+ int len = SendDlgItemMessage(hwndDlg, IDC_EDT_SMP_FIELD2, WM_GETTEXTLENGTH, 0, 0);
+ TCHAR *answer = new TCHAR[len+1];
+ GetDlgItemText(hwndDlg, IDC_EDT_SMP_FIELD2, answer, len+1);
+ char *ans = mir_utf8encodeT(answer);
+ delete[] answer;
+
+ SMPInitUpdateDialog(context, false);
+ otr_start_smp(context, NULL, (const unsigned char*)ans, strlen(ans));
+ mir_free(ans);
+ }
+
+ }else break;
+ EndDialog(hwndDlg, LOWORD( wParam ));
+ break;
+ case IDYES:
+ VerifyFingerprint(context, true);
+ EndDialog(hwndDlg, LOWORD( wParam ));
+ break;
+ case IDNO:
+ VerifyFingerprint(context, false);
+ EndDialog(hwndDlg, LOWORD( wParam ));
+ break;
+ }
+ }
+ break;
+ case CBN_SELCHANGE:
+ switch ( LOWORD( wParam )) {
+ case IDC_CBO_SMP_CHOOSE:
+ {
+ ConnContext *context = (ConnContext*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ MCONTACT hContact = (MCONTACT)context->app_data;
+ Fingerprint *fp = context->active_fingerprint;
+ if (!fp) {
+ EndDialog(hwndDlg, IDCANCEL);
+ return TRUE;
+ }
+ BOOL trusted = false;
+ if (fp->trust && fp->trust[0] != '\0') trusted = true;
+
+ TCHAR buff[512];
+ GetDlgItemText(hwndDlg, IDC_CBO_SMP_CHOOSE, buff, 255);
+ if (_tcsncmp(buff, TranslateT(LANG_SMPTYPE_QUESTION), 255)==0) {
+ if (trusted)
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_OTR_SMPQUESTION_VERIFIED_DESC), contact_get_nameT(hContact));
+ else
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_OTR_SMPQUESTION_VERIFY_DESC), contact_get_nameT(hContact));
+
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_INFO, buff);
+
+ SetDlgItemText(hwndDlg, IDC_EDT_SMP_FIELD1, _T(""));
+ SendDlgItemMessage(hwndDlg, IDC_EDT_SMP_FIELD1, EM_SETREADONLY, FALSE, 0);
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_FIELD1, TranslateT(LANG_SMP_QUESTION));
+
+ SetDlgItemText(hwndDlg, IDC_EDT_SMP_FIELD2, _T(""));
+ SendDlgItemMessage(hwndDlg, IDC_EDT_SMP_FIELD2, EM_SETREADONLY, FALSE, 0);
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_FIELD2, TranslateT(LANG_SMP_ANSWER));
+
+
+ ShowWindow(GetDlgItem(hwndDlg, IDOK), SW_SHOWNA);
+ ShowWindow(GetDlgItem(hwndDlg, IDYES), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDNO), SW_HIDE);
+ } else if (_tcsncmp(buff, TranslateT(LANG_SMPTYPE_PASSWORD), 255)==0) {
+ if (trusted)
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_OTR_SMPPASSWORD_VERIFIED_DESC), contact_get_nameT(hContact));
+ else
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_OTR_SMPPASSWORD_VERIFY_DESC), contact_get_nameT(hContact));
+
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_INFO, buff);
+
+ SetDlgItemText(hwndDlg, IDC_EDT_SMP_FIELD1, _T(""));
+ SendDlgItemMessage(hwndDlg, IDC_EDT_SMP_FIELD1, EM_SETREADONLY, TRUE, 0);
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_FIELD1, _T(""));
+
+ SetDlgItemText(hwndDlg, IDC_EDT_SMP_FIELD2, _T(""));
+ SendDlgItemMessage(hwndDlg, IDC_EDT_SMP_FIELD2, EM_SETREADONLY, FALSE, 0);
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_FIELD2, TranslateT(LANG_SMP_PASSWORD));
+
+
+ ShowWindow(GetDlgItem(hwndDlg, IDOK), SW_SHOWNA);
+ ShowWindow(GetDlgItem(hwndDlg, IDYES), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDNO), SW_HIDE);
+ } else if (_tcsncmp(buff, TranslateT(LANG_SMPTYPE_FINGERPRINT), 255)==0) {
+ if (trusted)
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_OTR_FPVERIFIED_DESC), contact_get_nameT(hContact));
+ else
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_OTR_FPVERIFY_DESC), contact_get_nameT(hContact));
+
+ 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));
+
+ ShowWindow(GetDlgItem(hwndDlg, IDOK), SW_HIDE);
+ ShowWindow(GetDlgItem(hwndDlg, IDYES), SW_SHOWNA);
+ ShowWindow(GetDlgItem(hwndDlg, IDNO), SW_SHOWNA);
+
+ }
+ }break;
+ }
+ }
+ break;
+
+ }
+
+ return FALSE;
+}
+void SMPInitDialog(ConnContext *context) {
+ if (context)
+ CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_SMP_INPUT), 0, DlgProcSMPInitProc, (LPARAM) context);
+}
+
+void SMPDialogUpdate(ConnContext *context, int percent) {
+ if (!context) return;
+ SmpForContactMap::iterator it = smp_for_contact.find(context->app_data);
+ if (it == smp_for_contact.end()) return;
+ if (it->second.dialog) PostMessage(it->second.dialog, WMU_REFRESHSMP, percent, 0);
+ TrustLevel level = otr_context_get_trust(context);
+ if (!it->second.responder && it->second.oldlevel != level) {
+ if (level == TRUST_PRIVATE)
+ VerifyFingerprintMessage(context, true);
+ else if (level == TRUST_UNVERIFIED)
+ VerifyFingerprintMessage(context, false);
+ }
+ //if (percent == 100)
+ /*
+ switch (percent){
+ case 0:
+ VerifyFingerprint(context, false);
+ ShowWarning(_T("SMP failed"));
+ break;
+ case 100:
+ VerifyFingerprint(context, true);
+ ShowWarning(_T("SMP successful"));
+ break;
+ default:
+ ShowWarning(_T("Received an SMP update"));
+ }
+ */
+}
+void SMPDialogReply(ConnContext *context, const char* question){
+ SmpData *data = (SmpData*)mir_calloc(sizeof(SmpData));
+ data->context = context;
+ data->oldlevel = TRUST_NOT_PRIVATE;
+ data->responder = true;
+ data->question = (question) ? mir_utf8decodeT(question) : NULL;
+ CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_SMP_INPUT), 0, DlgSMPResponseProc, (LPARAM) data);
+ /*
+ ShowError(_T("SMP requires user password (NOT IMPL YET)"));
+ otr_abort_smp(context);
+ */
+ //otr_continue_smp(context, pass, strlen(pass));
+}
+
+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;
+ }
+ SetWindowText(hwndDlg, _T(LANG_OTR_FPVERIFY_TITLE));
+ SetDlgItemText(hwndDlg, IDC_STC_SMP_HEAD, _T(LANG_OTR_FPVERIFY_TITLE));
+ TranslateDialogDefault( hwndDlg );
+ SetWindowLongPtr(hwndDlg, GWLP_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;
+ MCONTACT hContact = (MCONTACT) context->app_data;
+ 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, SIZEOF(buff), TranslateT(LANG_OTR_FPVERIFY_DESC), contact_get_nameT(hContact));
+ else
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_OTR_FPVERIFIED_DESC), contact_get_nameT(hContact));
+
+ 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)
+{
+ Thread_Push( 0 );
+
+ if (param) {
+ ConnContext *context = (ConnContext *)param;
+ MCONTACT hContact = (MCONTACT)context->app_data;
+ 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, SIZEOF(msg), TranslateT(LANG_FINGERPRINT_VERIFIED), contact_get_nameT(hContact));
+ ShowMessage(hContact, msg);
+ SetEncryptionStatus(hContact, 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, SIZEOF(msg), TranslateT(LANG_FINGERPRINT_NOT_VERIFIED), contact_get_nameT(hContact));
+ ShowMessage(hContact, msg);
+ SetEncryptionStatus(hContact, otr_context_get_trust(context));
+ break;
+ }
+ }
+
+ Thread_Pop();
+ return 0;
+} \ No newline at end of file
diff --git a/plugins/MirOTR/src/dialogs.h b/plugins/MirOTR/src/dialogs.h
new file mode 100644
index 0000000000..0422dc3095
--- /dev/null
+++ b/plugins/MirOTR/src/dialogs.h
@@ -0,0 +1,8 @@
+#pragma once
+void VerifyContextDialog(ConnContext* context);
+void SMPInitDialog(ConnContext* context);
+INT_PTR CALLBACK DlgProcVerifyContext(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+void SMPDialogUpdate(ConnContext *context, int percent);
+void SMPDialogReply(ConnContext *context, const char* question);
+
+#define WMU_REFRESHSMP (WM_USER + 245) \ No newline at end of file
diff --git a/plugins/MirOTR/src/dllmain.cpp b/plugins/MirOTR/src/dllmain.cpp
new file mode 100644
index 0000000000..55f8eef436
--- /dev/null
+++ b/plugins/MirOTR/src/dllmain.cpp
@@ -0,0 +1,121 @@
+// dllmain.cpp : Definiert den Einstiegspunkt fr die DLL-Anwendung.
+#include "stdafx.h"
+
+HANDLE hEventWindow;
+HINSTANCE hInst;
+
+int hLangpack;
+
+PLUGININFOEX pluginInfo={
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __AUTHOREMAIL,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {12D8FAAD-78AB-4E3C-9854-320E9EA5CC9F}
+ {0x12d8faad, 0x78ab, 0x4e3c, {0x98, 0x54, 0x32, 0xe, 0x9e, 0xa5, 0xcc, 0x9f}}
+};
+
+BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
+{
+ hInst = hModule;
+ return TRUE;
+}
+
+extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion)
+{
+ return &pluginInfo;
+}
+
+int ModulesLoaded(WPARAM wParam, LPARAM lParam)
+{
+ 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();
+ InitMenu();
+
+ InitSRMM();
+
+ hEventWindow = HookEvent(ME_MSG_WINDOWEVENT, WindowEvent);
+
+ if (options.bHaveSecureIM && !db_get_b(0, MODULENAME, "sim_warned", 0)) {
+ db_set_b(0, MODULENAME, "sim_warned", 1);
+ options.default_policy = OTRL_POLICY_MANUAL_MOD;
+ SaveOptions();
+ MessageBox(0, TranslateT(LANG_OTR_SECUREIM_TEXT), TranslateT(LANG_OTR_SECUREIM_TITLE), 0x30);
+ }
+ return 0;
+}
+
+extern "C" __declspec(dllexport) int Load(void)
+{
+ DEBUGOUT_T("LOAD MIROTR")
+
+ mir_getLP( &pluginInfo );
+ /* for timezones
+ mir_getTMI(&tmi); */
+
+ InitIcons();
+
+ OTRL_INIT;
+ INITCOMMONCONTROLSEX icce = {0};
+ icce.dwSize = sizeof(icce);
+ icce.dwICC = ICC_LISTVIEW_CLASSES|ICC_PROGRESS_CLASS;
+ InitCommonControlsEx(&icce);
+
+ db_set_resident(MODULENAME, "TrustLevel");
+
+ /////////////
+ ////// init plugin
+ PROTOCOLDESCRIPTOR pd = { 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(db_get_b(0, MODULENAME, "FilterOrderFix", 0) != 2) {
+ for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact))
+ CallService(MS_PROTO_REMOVEFROMCONTACT, hContact, (LPARAM)MODULENAME);
+ db_set_b(0, MODULENAME, "FilterOrderFix", 2);
+ }
+
+ // create our services
+ CreateProtoServiceFunction(MODULENAME, PSS_MESSAGE, SVC_OTRSendMessage);
+ CreateProtoServiceFunction(MODULENAME, PSR_MESSAGE, SVC_OTRRecvMessage);
+
+ // hook modules loaded for updater support
+ HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+
+ return 0;
+}
+
+extern "C" __declspec(dllexport) int Unload(void)
+{
+ //UnhookEvent(hSettingChanged);
+ UnhookEvent(hEventWindow);
+ //UnhookEvent(hEventDbEventAddedFilter);
+ //UnhookEvent(hEventDbEventAdded);
+ DEBUGOUT_T("UNLOAD MIROTR")
+ DeinitSRMM();
+ DeinitOptions();
+ DeinitDBFilter();
+
+ lib_cs_lock();
+ otrl_userstate_free(otr_user_state);
+ lib_cs_unlock();
+
+ DeinitUtils();
+
+ return 0;
+} \ No newline at end of file
diff --git a/plugins/MirOTR/src/entities.cpp b/plugins/MirOTR/src/entities.cpp
new file mode 100644
index 0000000000..084c05b2d0
--- /dev/null
+++ b/plugins/MirOTR/src/entities.cpp
@@ -0,0 +1,424 @@
+// (C) of entities.cpp: Christoph
+// http://mercurial.intuxication.org/hg/cstuff/raw-file/tip/entities.c
+// http://stackoverflow.com/questions/1082162/how-to-unescape-html-in-c/1082191#1082191
+// modified by ProgAndy
+
+#include "stdafx.h"
+
+#define UNICODE_MAX 0x10FFFFul
+
+
+static const char *named_entities[][2] =
+{
+ { "AElig;", "Æ" },
+ { "Aacute;", "Á" },
+ { "Acirc;", "Â" },
+ { "Agrave;", "À" },
+ { "Alpha;", "Α" },
+ { "Aring;", "Å" },
+ { "Atilde;", "Ã" },
+ { "Auml;", "Ä" },
+ { "Beta;", "Β" },
+ { "Ccedil;", "Ç" },
+ { "Chi;", "Χ" },
+ { "Dagger;", "‡" },
+ { "Delta;", "Δ" },
+ { "ETH;", "Ð" },
+ { "Eacute;", "É" },
+ { "Ecirc;", "Ê" },
+ { "Egrave;", "È" },
+ { "Epsilon;", "Ε" },
+ { "Eta;", "Η" },
+ { "Euml;", "Ë" },
+ { "Gamma;", "Γ" },
+ { "Iacute;", "Í" },
+ { "Icirc;", "Î" },
+ { "Igrave;", "Ì" },
+ { "Iota;", "Ι" },
+ { "Iuml;", "Ï" },
+ { "Kappa;", "Κ" },
+ { "Lambda;", "Λ" },
+ { "Mu;", "Μ" },
+ { "Ntilde;", "Ñ" },
+ { "Nu;", "Ν" },
+ { "OElig;", "Œ" },
+ { "Oacute;", "Ó" },
+ { "Ocirc;", "Ô" },
+ { "Ograve;", "Ò" },
+ { "Omega;", "Ω" },
+ { "Omicron;", "Ο" },
+ { "Oslash;", "Ø" },
+ { "Otilde;", "Õ" },
+ { "Ouml;", "Ö" },
+ { "Phi;", "Φ" },
+ { "Pi;", "Π" },
+ { "Prime;", "″" },
+ { "Psi;", "Ψ" },
+ { "Rho;", "Ρ" },
+ { "Scaron;", "Š" },
+ { "Sigma;", "Σ" },
+ { "THORN;", "Þ" },
+ { "Tau;", "Τ" },
+ { "Theta;", "Θ" },
+ { "Uacute;", "Ú" },
+ { "Ucirc;", "Û" },
+ { "Ugrave;", "Ù" },
+ { "Upsilon;", "Υ" },
+ { "Uuml;", "Ü" },
+ { "Xi;", "Ξ" },
+ { "Yacute;", "Ý" },
+ { "Yuml;", "Ÿ" },
+ { "Zeta;", "Ζ" },
+ { "aacute;", "á" },
+ { "acirc;", "â" },
+ { "acute;", "´" },
+ { "aelig;", "æ" },
+ { "agrave;", "à" },
+ { "alefsym;", "ℵ" },
+ { "alpha;", "α" },
+ { "amp;", "&" },
+ { "and;", "∧" },
+ { "ang;", "∠" },
+ { "apos;", "'" },
+ { "aring;", "å" },
+ { "asymp;", "≈" },
+ { "atilde;", "ã" },
+ { "auml;", "ä" },
+ { "bdquo;", "„" },
+ { "beta;", "β" },
+ { "brvbar;", "¦" },
+ { "bull;", "•" },
+ { "cap;", "∩" },
+ { "ccedil;", "ç" },
+ { "cedil;", "¸" },
+ { "cent;", "¢" },
+ { "chi;", "χ" },
+ { "circ;", "ˆ" },
+ { "clubs;", "♣" },
+ { "cong;", "≅" },
+ { "copy;", "©" },
+ { "crarr;", "↵" },
+ { "cup;", "∪" },
+ { "curren;", "¤" },
+ { "dArr;", "⇓" },
+ { "dagger;", "†" },
+ { "darr;", "↓" },
+ { "deg;", "°" },
+ { "delta;", "δ" },
+ { "diams;", "♦" },
+ { "divide;", "÷" },
+ { "eacute;", "é" },
+ { "ecirc;", "ê" },
+ { "egrave;", "è" },
+ { "empty;", "∅" },
+ { "emsp;", " " },
+ { "ensp;", " " },
+ { "epsilon;", "ε" },
+ { "equiv;", "≡" },
+ { "eta;", "η" },
+ { "eth;", "ð" },
+ { "euml;", "ë" },
+ { "euro;", "€" },
+ { "exist;", "∃" },
+ { "fnof;", "ƒ" },
+ { "forall;", "∀" },
+ { "frac12;", "½" },
+ { "frac14;", "¼" },
+ { "frac34;", "¾" },
+ { "frasl;", "⁄" },
+ { "gamma;", "γ" },
+ { "ge;", "≥" },
+ { "gt;", ">" },
+ { "hArr;", "⇔" },
+ { "harr;", "↔" },
+ { "hearts;", "♥" },
+ { "hellip;", "…" },
+ { "iacute;", "í" },
+ { "icirc;", "î" },
+ { "iexcl;", "¡" },
+ { "igrave;", "ì" },
+ { "image;", "ℑ" },
+ { "infin;", "∞" },
+ { "int;", "∫" },
+ { "iota;", "ι" },
+ { "iquest;", "¿" },
+ { "isin;", "∈" },
+ { "iuml;", "ï" },
+ { "kappa;", "κ" },
+ { "lArr;", "⇐" },
+ { "lambda;", "λ" },
+ { "lang;", "〈" },
+ { "laquo;", "«" },
+ { "larr;", "←" },
+ { "lceil;", "⌈" },
+ { "ldquo;", "“" },
+ { "le;", "≤" },
+ { "lfloor;", "⌊" },
+ { "lowast;", "∗" },
+ { "loz;", "◊" },
+ { "lrm;", "\xE2\x80\x8E" },
+ { "lsaquo;", "‹" },
+ { "lsquo;", "‘" },
+ { "lt;", "<" },
+ { "macr;", "¯" },
+ { "mdash;", "—" },
+ { "micro;", "µ" },
+ { "middot;", "·" },
+ { "minus;", "−" },
+ { "mu;", "μ" },
+ { "nabla;", "∇" },
+ { "nbsp;", " " },
+ { "ndash;", "–" },
+ { "ne;", "≠" },
+ { "ni;", "∋" },
+ { "not;", "¬" },
+ { "notin;", "∉" },
+ { "nsub;", "⊄" },
+ { "ntilde;", "ñ" },
+ { "nu;", "ν" },
+ { "oacute;", "ó" },
+ { "ocirc;", "ô" },
+ { "oelig;", "œ" },
+ { "ograve;", "ò" },
+ { "oline;", "‾" },
+ { "omega;", "ω" },
+ { "omicron;", "ο" },
+ { "oplus;", "⊕" },
+ { "or;", "∨" },
+ { "ordf;", "ª" },
+ { "ordm;", "º" },
+ { "oslash;", "ø" },
+ { "otilde;", "õ" },
+ { "otimes;", "⊗" },
+ { "ouml;", "ö" },
+ { "para;", "¶" },
+ { "part;", "∂" },
+ { "permil;", "‰" },
+ { "perp;", "⊥" },
+ { "phi;", "φ" },
+ { "pi;", "π" },
+ { "piv;", "ϖ" },
+ { "plusmn;", "±" },
+ { "pound;", "£" },
+ { "prime;", "′" },
+ { "prod;", "∏" },
+ { "prop;", "∝" },
+ { "psi;", "ψ" },
+ { "quot;", "\"" },
+ { "rArr;", "⇒" },
+ { "radic;", "√" },
+ { "rang;", "〉" },
+ { "raquo;", "»" },
+ { "rarr;", "→" },
+ { "rceil;", "⌉" },
+ { "rdquo;", "”" },
+ { "real;", "ℜ" },
+ { "reg;", "®" },
+ { "rfloor;", "⌋" },
+ { "rho;", "ρ" },
+ { "rlm;", "\xE2\x80\x8F" },
+ { "rsaquo;", "›" },
+ { "rsquo;", "’" },
+ { "sbquo;", "‚" },
+ { "scaron;", "š" },
+ { "sdot;", "⋅" },
+ { "sect;", "§" },
+ { "shy;", "\xC2\xAD" },
+ { "sigma;", "σ" },
+ { "sigmaf;", "ς" },
+ { "sim;", "∼" },
+ { "spades;", "♠" },
+ { "sub;", "⊂" },
+ { "sube;", "⊆" },
+ { "sum;", "∑" },
+ { "sup;", "⊃" },
+ { "sup1;", "¹" },
+ { "sup2;", "²" },
+ { "sup3;", "³" },
+ { "supe;", "⊇" },
+ { "szlig;", "ß" },
+ { "tau;", "τ" },
+ { "there4;", "∴" },
+ { "theta;", "θ" },
+ { "thetasym;", "ϑ" },
+ { "thinsp;", " " },
+ { "thorn;", "þ" },
+ { "tilde;", "˜" },
+ { "times;", "×" },
+ { "trade;", "™" },
+ { "uArr;", "⇑" },
+ { "uacute;", "ú" },
+ { "uarr;", "↑" },
+ { "ucirc;", "û" },
+ { "ugrave;", "ù" },
+ { "uml;", "¨" },
+ { "upsih;", "ϒ" },
+ { "upsilon;", "υ" },
+ { "uuml;", "ü" },
+ { "weierp;", "℘" },
+ { "xi;", "ξ" },
+ { "yacute;", "ý" },
+ { "yen;", "¥" },
+ { "yuml;", "ÿ" },
+ { "zeta;", "ζ" },
+ { "zwj;", "\xE2\x80\x8D" },
+ { "zwnj;", "\xE2\x80\x8C" }
+};
+
+static int cmp(const void *key, const void *element)
+{
+ return strncmp((const char *)key, *(const char **)element,
+ strlen(*(const char **)element));
+}
+
+static const char *get_named_entity(const char *name)
+{
+ const char **entity = (const char **)bsearch(name, named_entities,
+ sizeof(named_entities) / sizeof(*named_entities),
+ sizeof(*named_entities), cmp);
+
+ return entity ? entity[1] : NULL;
+}
+
+static size_t putc_utf8(unsigned long cp, char *buffer)
+{
+ unsigned char *bytes = (unsigned char *)buffer;
+
+ if(cp <= 0x007Ful)
+ {
+ bytes[0] = (unsigned char)cp;
+ return 1;
+ }
+
+ if(cp <= 0x07FFul)
+ {
+ bytes[1] = (unsigned char)((2u << 6) | (cp & 0x3Fu));
+ bytes[0] = (unsigned char)((6u << 5) | (cp >> 6));
+ return 2;
+ }
+
+ if(cp <= 0xFFFFul)
+ {
+ bytes[2] = (unsigned char)(( 2u << 6) | ( cp & 0x3Fu));
+ bytes[1] = (unsigned char)(( 2u << 6) | ((cp >> 6) & 0x3Fu));
+ bytes[0] = (unsigned char)((14u << 4) | (cp >> 12));
+ return 3;
+ }
+
+ if(cp <= 0x10FFFFul)
+ {
+ bytes[3] = (unsigned char)(( 2u << 6) | ( cp & 0x3Fu));
+ bytes[2] = (unsigned char)(( 2u << 6) | ((cp >> 6) & 0x3Fu));
+ bytes[1] = (unsigned char)(( 2u << 6) | ((cp >> 12) & 0x3Fu));
+ bytes[0] = (unsigned char)((30u << 3) | (cp >> 18));
+ return 4;
+ }
+
+ return 0;
+}
+
+static _Bool parse_entity(const char *current, char **to,
+ const char **from, size_t maxlen)
+{
+ const char *end = (const char *)memchr(current, ';', maxlen);
+ if (!end) return 0;
+
+ if(current[1] == '#')
+ {
+ char *tail = NULL;
+ errno = 0;
+
+ _Bool hex = current[2] == 'x' || current[2] == 'X';
+
+ unsigned long cp = strtoul(
+ current + (hex ? 3 : 2), &tail, hex ? 16 : 10);
+
+ if(tail == end && !errno && cp <= UNICODE_MAX)
+ {
+ *to += putc_utf8(cp, *to);
+ *from = end + 1;
+
+ return 1;
+ }
+ }
+ else
+ {
+ const char *entity = get_named_entity(&current[1]);
+ if(entity)
+ {
+ size_t len = strlen(entity);
+ memcpy(*to, entity, len);
+
+ *to += len;
+ *from = end + 1;
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+size_t decode_html_entities_utf8(char *dest, const char *src, size_t len)
+{
+ if (!src) src = dest;
+
+ char *to = dest;
+ const char *from = src;
+
+ const char *current;
+ if (!len) len = strlen(src);
+ size_t remain = len;
+ while((current = (const char*)memchr(from, '&', len-(from-src))))
+ {
+ memcpy(to, from, (size_t)(current - from));
+ to += current - from;
+ //remain = len-(current-src);
+
+ if(parse_entity(current, &to, &from, len-(current-src)))
+ continue;
+
+ from = current;
+ *to++ = *from++;
+ }
+
+ remain = strnlen(from, len-(from-src));
+
+ memcpy(to, from, remain);
+ to += remain;
+
+ if (src!=dest || (size_t)(to-dest) < len ) *to = 0;
+ return (size_t)(to - dest);
+}
+
+char * encode_html_entities_utf8(const char *src) {
+ const char *pos, *start=src;
+ std::string buf;
+ if (! (pos = strpbrk(start, "&<>\"\r")) ) return NULL;
+ while (pos) {
+ buf.append(start, pos-start);
+ start = pos+1;
+ switch (*pos) {
+ case '"':
+ buf.append("&quot;");
+ break;
+ case '<':
+ buf.append("&lt;");
+ break;
+ case '>':
+ buf.append("&gt;");
+ break;
+ case '&':
+ buf.append("&amp;");
+ break;
+ case '\r':
+ buf.append("<br>\r");
+ break;
+ }
+ pos = strpbrk(start, "&<>\"\r");
+ }
+ if (strlen(start)) buf.append(start);
+ pos = mir_strdup(buf.c_str());
+ buf.clear();
+ return (char*)pos;
+} \ No newline at end of file
diff --git a/plugins/MirOTR/src/entities.h b/plugins/MirOTR/src/entities.h
new file mode 100644
index 0000000000..0d3cc1b822
--- /dev/null
+++ b/plugins/MirOTR/src/entities.h
@@ -0,0 +1,24 @@
+// (C) of entities.h: Christoph
+// http://mercurial.intuxication.org/hg/cstuff/raw-file/tip/entities.h
+// http://stackoverflow.com/questions/1082162/how-to-unescape-html-in-c/1082191#1082191
+// modified by ProgAndy
+
+#ifndef DECODE_HTML_ENTITIES_UTF8
+#define DECODE_HTML_ENTITIES_UTF8
+
+extern size_t decode_html_entities_utf8(char *dest, const char *src, size_t len);
+/* if `src` is `NULL`, input will be taken from `dest`, decoding
+ the entities in-place
+
+ otherwise, the output will be placed in `dest`, which should point
+ to a buffer big enough to hold `strlen(src) + 1` characters, while
+ `src` remains unchanged
+ if `len` is given, `dest` must be at least big enough
+ to hold `len + 1` characters.
+
+ the function returns the length of the decoded string
+*/
+
+extern char * encode_html_entities_utf8(const char *src);
+
+#endif
diff --git a/plugins/MirOTR/src/icons.cpp b/plugins/MirOTR/src/icons.cpp
new file mode 100644
index 0000000000..ca38927383
--- /dev/null
+++ b/plugins/MirOTR/src/icons.cpp
@@ -0,0 +1,36 @@
+#include "stdafx.h"
+
+TCHAR g_dllpath[1024];
+
+static IconItem iconList[] = {
+ { LPGEN(LANG_ICON_OTR), ICON_OTR, IDI_OTR },
+ { LPGEN(LANG_ICON_PRIVATE), ICON_PRIVATE, IDI_PRIVATE },
+ { LPGEN(LANG_ICON_UNVERIFIED), ICON_UNVERIFIED, IDI_UNVERIFIED },
+ { LPGEN(LANG_ICON_FINISHED), ICON_FINISHED, IDI_FINISHED },
+ { LPGEN(LANG_ICON_NOT_PRIVATE), ICON_NOT_PRIVATE, IDI_INSECURE },
+ { LPGEN(LANG_ICON_REFRESH), ICON_REFRESH, IDI_REFRESH }
+};
+
+void InitIcons()
+{
+ Icon_Register(hInst, "OTR", iconList, SIZEOF(iconList));
+}
+
+HICON LoadIcon(const char* name, int big) {
+ return Skin_GetIcon(name, big);
+}
+
+HANDLE GetIconHandle(const char* name)
+{
+ return Skin_GetIconHandle(name);
+}
+
+void ReleaseIcon(const char* name, int big)
+{
+ Skin_ReleaseIcon(name, big);
+}
+
+void ReleaseIcon(HICON handle, int big)
+{
+ Skin_ReleaseIcon(handle);
+}
diff --git a/plugins/MirOTR/src/icons.h b/plugins/MirOTR/src/icons.h
new file mode 100644
index 0000000000..039b455773
--- /dev/null
+++ b/plugins/MirOTR/src/icons.h
@@ -0,0 +1,13 @@
+#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")
+#define ICON_REFRESH (MODULENAME"_refresh")
+
+void InitIcons();
+HICON LoadIcon(const char* name, int big);
+HANDLE GetIconHandle(const char* name);
+void ReleaseIcon(const char* name, int big);
+void ReleaseIcon(HICON handle, int big); \ No newline at end of file
diff --git a/plugins/MirOTR/src/language.h b/plugins/MirOTR/src/language.h
new file mode 100644
index 0000000000..cadf0b510d
--- /dev/null
+++ b/plugins/MirOTR/src/language.h
@@ -0,0 +1,121 @@
+#pragma once
+
+#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_MENU_START LPGENT("&Start OTR session")
+#define LANG_MENU_REFRESH LPGENT("&Refresh OTR session")
+#define LANG_MENU_STOP LPGENT("Sto&p OTR session")
+#define LANG_MENU_VERIFY LPGENT("&Verify Fingerprint")
+#define LANG_MENU_TOGGLEHTML LPGENT("&Convert HTML (for Pidgin)")
+
+#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' successfully refreshed")
+#define LANG_SESSION_CONTINUE_OTR LPGEN("OTR encrypted session with '%s' successfully refreshed")
+#define LANG_SESSION_CONTINUE_OTR_VERIFY LPGEN("OTR encrypted session with '%s' successfully 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("Authenticate %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_ICON_REFRESH LPGEN("Refresh")
+
+#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_HTMLCONV LPGEN("Conv. HTML")
+
+#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_SMP_VERIFY_TITLE LPGEN("OTR Authenticate: %s (%s)")
+#define LANG_SMPTYPE_QUESTION LPGEN("Challenge Question")
+#define LANG_SMPTYPE_PASSWORD LPGEN("Known Password")
+#define LANG_SMPTYPE_FINGERPRINT LPGEN("Manual fingerprint comparison")
+#define LANG_SMP_ANSWER LPGEN("Secret Answer")
+#define LANG_SMP_QUESTION LPGEN("Question")
+#define LANG_SMP_PASSWORD LPGEN("Password")
+
+#define LANG_SMP_SUCCESS LPGEN("Authentication successful.")
+#define LANG_SMP_SUCCESS_VERIFY LPGEN("Your contact authenticated you successfully. You can send your own request to authenticate him.")
+#define LANG_SMP_ERROR LPGEN("Error during authentication.")
+#define LANG_SMP_FAILED LPGEN("Authentication failed")
+#define LANG_SMP_IN_PROGRESS LPGEN("Authentication for '%s' is already in progress.")
+
+#define LANG_SMP_PROGRESS_TITLE LPGEN("OTR Authenticating: %s (%s)")
+#define LANG_SMP_PROGRESS_DESC LPGEN("Authenticating contact:\n%s (%s)")
+
+#define LANG_OTR_SMPQUESTION_VERIFY_DESC LPGEN("OTR encrypted session with '%s' (UNVERIFIED).\nUse a question only your partner can answer.")
+#define LANG_OTR_SMPQUESTION_VERIFIED_DESC LPGEN("OTR encrypted session with '%s' (VERIFIED).\nVerify the session again using a question only your partner can answer.")
+
+#define LANG_OTR_SMPPASSWORD_VERIFY_DESC LPGEN("OTR encrypted session with '%s' (UNVERIFIED).\nUse a known password.")
+#define LANG_OTR_SMPPASSWORD_VERIFIED_DESC LPGEN("OTR encrypted session with '%s' (VERIFIED).\nVerify the session again using a known password.")
+
+#define LANG_OTR_SMPQUESTION_RESPOND_DESC LPGEN("Your contact '%s' wants to verify your identity with a question only you can answer.")
+
+#define LANG_OTR_SMPPASSWORD_RESPOND_DESC LPGEN("Your contact '%s' wants to verify your identity with a secret password you should know.")
+
+#define LANG_FINGERPRINT_STILL_IN_USE LPGEN("Fingerprint '%s' still in use in conversation with '%s' (%s). You cannot delete it!")
+#define LANG_FINGERPRINT_NOT_DELETED LPGEN("Fingerprint '%s' in use in conversation with '%s' (%s). It could not be deleted!")
+
+#define LANG_OTR_SECUREIM_STARTED LPGEN("Cannot start OTR for '%s'. SecureIM is already running")
+#define LANG_OTR_SECUREIM_TITLE LPGEN("OTR: SecureIM installed")
+#define LANG_OTR_SECUREIM_TEXT LPGEN("You are using SecureIM. MirOTR will only work for contacts with SecureIM disabled") \ No newline at end of file
diff --git a/plugins/MirOTR/src/mirotrmenu.cpp b/plugins/MirOTR/src/mirotrmenu.cpp
new file mode 100644
index 0000000000..534d8fbfea
--- /dev/null
+++ b/plugins/MirOTR/src/mirotrmenu.cpp
@@ -0,0 +1,260 @@
+#include "stdafx.h"
+
+static HANDLE hMirOTRMenuObject;
+static HGENMENU hStatusInfoItem, hHTMLConvMenuItem;
+HWND hDummyPaintWin;
+
+//contactmenu exec param(ownerdata)
+//also used in checkservice
+typedef struct
+{
+ char *szServiceName;
+}
+MirOTRMenuExecParam,*lpMirOTRMenuExecParam;
+
+////////////////////////////////////////////
+// MirOTR MENU
+///////////////////////////////////////////
+static INT_PTR RemoveMirOTRMenuItem(WPARAM wParam, LPARAM)
+{
+ CallService(MO_REMOVEMENUITEM,wParam,0);
+ return 0;
+}
+
+static INT_PTR AddMirOTRMenuItem(WPARAM, LPARAM lParam)
+{
+ MIROTRMENUITEM *mi=(MIROTRMENUITEM*)lParam;
+ if ( mi->cbSize != sizeof( MIROTRMENUITEM ))
+ return 0;
+
+ TMO_MenuItem tmi = { 0 };
+ tmi.cbSize = sizeof(tmi);
+ tmi.flags = mi->flags;
+ tmi.hIcon = mi->hIcon;
+ tmi.hIcolibItem = mi->icolibItem;
+ tmi.hotKey = mi->hotKey;
+ tmi.position = mi->position;
+ tmi.ptszName = mi->ptszName;
+ tmi.root = mi->root;
+
+ //owner data
+ lpMirOTRMenuExecParam cmep = ( lpMirOTRMenuExecParam )mir_calloc(sizeof(MirOTRMenuExecParam));
+ cmep->szServiceName = mir_strdup( mi->pszService );
+ tmi.ownerdata = cmep;
+
+ INT_PTR menuHandle = CallService(MO_ADDNEWMENUITEM, (WPARAM) hMirOTRMenuObject, (LPARAM)&tmi );
+
+ return menuHandle;
+}
+
+static INT_PTR BuildMirOTRMenu(WPARAM hContact, LPARAM)
+{
+ ListParam param = { 0 };
+ param.MenuObjectHandle = hMirOTRMenuObject;
+ param.wParam = hContact;
+ HMENU hMenu = CreatePopupMenu();
+ CallService(MO_BUILDMENU,(WPARAM)hMenu,(LPARAM)&param);
+
+ return (INT_PTR)hMenu;
+}
+
+//called with:
+//wparam - ownerdata
+//lparam - lparam from winproc
+INT_PTR MirOTRMenuExecService(WPARAM wParam,LPARAM lParam)
+{
+ if (wParam!=0) {
+ lpMirOTRMenuExecParam cmep=(lpMirOTRMenuExecParam)wParam;
+ //call with wParam=(MCONTACT)hContact
+ CallService(cmep->szServiceName,lParam,0);
+ }
+ return 0;
+}
+
+//true - ok,false ignore
+INT_PTR MirOTRMenuCheckService(WPARAM wParam,LPARAM)
+{
+ PCheckProcParam pcpp = ( PCheckProcParam )wParam;
+ TMO_MenuItem mi;
+
+ if ( pcpp == NULL )
+ return FALSE;
+
+ lpMirOTRMenuExecParam cmep = ( lpMirOTRMenuExecParam )pcpp->MenuItemOwnerData;
+ if ( cmep == NULL ) //this is rootsection...build it
+ return TRUE;
+
+ MCONTACT hContact = (MCONTACT)pcpp->wParam, hSub;
+ if((hSub = db_mc_getMostOnline(hContact)) != 0)
+ hContact = hSub;
+
+ TrustLevel level = ( TrustLevel )otr_context_get_trust(otrl_context_find_miranda(otr_user_state, hContact));
+
+ mi.cbSize = sizeof(mi);
+ if ( CallService(MO_GETMENUITEM, (WPARAM)pcpp->MenuItemHandle, (LPARAM)&mi) == 0) {
+
+ if ( mi.flags & CMIF_HIDDEN ) return FALSE;
+ if ( mi.flags & CMIF_NOTPRIVATE && level==TRUST_PRIVATE ) return FALSE;
+ if ( mi.flags & CMIF_NOTFINISHED && level==TRUST_FINISHED ) return FALSE;
+ if ( mi.flags & CMIF_NOTUNVERIFIED && level==TRUST_UNVERIFIED ) return FALSE;
+ if ( mi.flags & CMIF_NOTNOTPRIVATE && level==TRUST_NOT_PRIVATE ) return FALSE;
+
+ if (pcpp->MenuItemHandle == hStatusInfoItem) {
+ mi.flags = CMIM_NAME | CMIM_ICON | CMIF_TCHAR;
+ switch (level) {
+ case TRUST_PRIVATE:
+ mi.hIcolibItem = GetIconHandle(ICON_PRIVATE);
+ mi.ptszName = TranslateT(LANG_STATUS_PRIVATE);
+ break;
+ case TRUST_UNVERIFIED:
+ mi.hIcolibItem = GetIconHandle(ICON_UNVERIFIED);
+ mi.ptszName = TranslateT(LANG_STATUS_UNVERIFIED);
+ break;
+ case TRUST_FINISHED:
+ mi.hIcolibItem = GetIconHandle(ICON_FINISHED);
+ mi.ptszName = TranslateT(LANG_STATUS_FINISHED);
+ break;
+ default:
+ mi.hIcolibItem = GetIconHandle(ICON_NOT_PRIVATE);
+ mi.ptszName = TranslateT(LANG_STATUS_DISABLED);
+ }
+ CallService(MO_MODIFYMENUITEM, (WPARAM)hStatusInfoItem, (LPARAM)&mi);
+ } else if (pcpp->MenuItemHandle == hHTMLConvMenuItem) {
+ if (db_get_b(hContact, MODULENAME, "HTMLConv", 0) )
+ mi.flags |= CMIM_FLAGS|CMIF_CHECKED;
+ else
+ mi.flags = CMIM_FLAGS|(mi.flags &~CMIF_CHECKED);
+ CallService(MO_MODIFYMENUITEM, (WPARAM)hHTMLConvMenuItem, (LPARAM)&mi);
+ }
+ }
+ return TRUE;
+}
+
+INT_PTR FreeOwnerDataMirOTRMenu (WPARAM, LPARAM lParam)
+{
+ lpMirOTRMenuExecParam cmep = ( lpMirOTRMenuExecParam )lParam;
+ if ( cmep != NULL ) {
+ if (cmep->szServiceName) mir_free(cmep->szServiceName);
+ mir_free(cmep);
+ }
+ return 0;
+}
+
+INT_PTR OnAddMenuItemMirOTRMenu (WPARAM wParam, LPARAM lParam)
+{
+ MENUITEMINFO *mii = (MENUITEMINFO*)wParam;
+ if (!mii || mii->cbSize != sizeof(MENUITEMINFO)) return 0;
+
+ TMO_MenuItem mi;
+ mi.cbSize = sizeof(mi);
+ if ( CallService(MO_GETMENUITEM, (WPARAM)lParam, (LPARAM)&mi) == 0) {
+ if (mi.flags & CMIF_DISABLED) {
+ mii->fMask |= MIIM_STATE;
+ mii->fState |= MF_DISABLED;
+ }
+ }
+ return 1;
+}
+
+
+LRESULT CALLBACK PopupMenuWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message)
+ {
+ case WM_MEASUREITEM:
+ if (CallService(MS_CLIST_MENUMEASUREITEM, wParam, lParam)) return TRUE;
+ break;
+ case WM_DRAWITEM:
+ if (CallService(MS_CLIST_MENUDRAWITEM, wParam, lParam)) return TRUE;
+ break;
+ case WM_COMMAND:
+ if (CallService(MO_PROCESSCOMMANDBYMENUIDENT, wParam, GetWindowLongPtr(hwnd, GWLP_USERDATA))) return TRUE;
+ break;
+ }
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+void ShowOTRMenu(MCONTACT hContact, POINT pt){
+ HMENU menu = (HMENU) CallService(MS_MIROTR_MENUBUILDMIROTR, hContact, 0);
+ SetWindowLongPtr(hDummyPaintWin, GWLP_USERDATA, (LONG_PTR)hContact);
+ TrackPopupMenu(menu, 0, pt.x, pt.y, 0, hDummyPaintWin, 0);
+ DestroyMenu(menu);
+}
+
+void InitMirOTRMenu(void)
+{
+ WNDCLASS wc = {0};
+ wc.hInstance = hInst;
+ wc.lpfnWndProc = PopupMenuWndProc;
+ wc.lpszClassName = _T("MirOTRPopupMenuProcessor");
+ RegisterClass(&wc);
+ hDummyPaintWin = CreateWindowEx(0, _T("MirOTRPopupMenuProcessor"), NULL, 0, 0, 0, 1, 1, 0, 0, hInst, 0);
+
+ CreateServiceFunction("MirOTRMenuExecService",MirOTRMenuExecService);
+ CreateServiceFunction("MirOTRMenuCheckService",MirOTRMenuCheckService);
+
+ // menu object
+ CreateServiceFunction("MIROTRMENUS/FreeOwnerDataMirOTRMenu",FreeOwnerDataMirOTRMenu);
+ CreateServiceFunction("MIROTRMENUS/OnAddMenuItemMirOTRMenu",OnAddMenuItemMirOTRMenu);
+
+ CreateServiceFunction(MS_MIROTR_ADDMIROTRMENUITEM,AddMirOTRMenuItem);
+ CreateServiceFunction(MS_MIROTR_MENUBUILDMIROTR,BuildMirOTRMenu);
+ CreateServiceFunction(MS_MIROTR_REMOVEMIROTRMENUITEM,RemoveMirOTRMenuItem);
+
+ hMirOTRMenuObject = MO_CreateMenuObject("MirOTRMenu", LPGEN("MirOTR menu"), "MirOTRMenuCheckService", "MirOTRMenuExecService");
+ MO_SetMenuObjectParam(hMirOTRMenuObject, OPT_USERDEFINEDITEMS, FALSE);
+ MO_SetMenuObjectParam(hMirOTRMenuObject, OPT_MENUOBJECT_SET_FREE_SERVICE, "MIROTRMENUS/FreeOwnerDataMirOTRMenu");
+ MO_SetMenuObjectParam(hMirOTRMenuObject, OPT_MENUOBJECT_SET_ONADD_SERVICE, "MIROTRMENUS/OnAddMenuItemMirOTRMenu");
+
+ // menu items
+ MIROTRMENUITEM mi = {0};
+ mi.cbSize = sizeof(mi);
+
+ mi.flags = CMIF_DISABLED | CMIF_TCHAR;
+ mi.ptszName = LPGENT("OTR Status");
+ mi.position = 0;
+ hStatusInfoItem = (HGENMENU) AddMirOTRMenuItem(0, (LPARAM) &mi);
+
+ mi.flags = CMIF_TCHAR | CMIF_NOTPRIVATE | CMIF_NOTUNVERIFIED;
+ mi.ptszName = LANG_MENU_START;
+ mi.position = 100001;
+ mi.pszService = MS_OTR_MENUSTART;
+ mi.icolibItem = GetIconHandle(ICON_UNVERIFIED);
+ AddMirOTRMenuItem(0, (LPARAM) &mi);
+
+ mi.flags = CMIF_TCHAR | CMIF_NOTNOTPRIVATE | CMIF_NOTFINISHED;
+ mi.ptszName = LANG_MENU_REFRESH;
+ mi.position = 100002;
+ mi.pszService = MS_OTR_MENUREFRESH;
+ mi.icolibItem = GetIconHandle(ICON_FINISHED);
+ AddMirOTRMenuItem(0, (LPARAM) &mi);
+
+ mi.flags = CMIF_TCHAR | CMIF_NOTNOTPRIVATE;
+ mi.ptszName = LANG_MENU_STOP;
+ mi.position = 100003;
+ mi.pszService = MS_OTR_MENUSTOP;
+ mi.icolibItem = GetIconHandle(ICON_NOT_PRIVATE);
+ AddMirOTRMenuItem(0, (LPARAM) &mi);
+
+ mi.flags = CMIF_TCHAR | CMIF_NOTNOTPRIVATE | CMIF_NOTFINISHED;
+ mi.ptszName = LANG_MENU_VERIFY;
+ mi.position = 200001;
+ mi.pszService = MS_OTR_MENUVERIFY;
+ mi.icolibItem = GetIconHandle(ICON_PRIVATE);
+ AddMirOTRMenuItem(0, (LPARAM) &mi);
+
+ mi.flags = CMIF_TCHAR|CMIF_CHECKED;
+ mi.ptszName = LANG_MENU_TOGGLEHTML;
+ mi.position = 300001;
+ mi.pszService = MS_OTR_MENUTOGGLEHTML;
+ hHTMLConvMenuItem = (HGENMENU) AddMirOTRMenuItem(0, (LPARAM) &mi);
+}
+
+void UninitMirOTRMenu(void)
+{
+ DestroyWindow(hDummyPaintWin);
+ hDummyPaintWin = 0;
+ UnregisterClass(_T("MirOTRPopupMenuProcessor"), hInst);
+ if ( hMirOTRMenuObject ) CallService(MO_REMOVEMENUOBJECT, (WPARAM)hMirOTRMenuObject, 0 );
+ hMirOTRMenuObject = 0;
+}
diff --git a/plugins/MirOTR/src/mirotrmenu.h b/plugins/MirOTR/src/mirotrmenu.h
new file mode 100644
index 0000000000..e57e954745
--- /dev/null
+++ b/plugins/MirOTR/src/mirotrmenu.h
@@ -0,0 +1,45 @@
+#pragma once
+#define MS_MIROTR_ADDMIROTRMENUITEM MODULENAME"/AddMirOTRMenuItem"
+#define MS_MIROTR_MENUBUILDMIROTR MODULENAME"/MenuBuildMirOTR"
+#define MS_MIROTR_REMOVEMIROTRMENUITEM MODULENAME"/RemoveMirOTRMenuItem"
+
+#define CMIF_NOTNOTPRIVATE CMIF_NOTOFFLINE
+#define CMIF_NOTUNVERIFIED CMIF_NOTONLINE
+#define CMIF_NOTPRIVATE CMIF_NOTONLIST
+#define CMIF_NOTFINISHED CMIF_NOTOFFLIST
+#define CMIF_DISABLED 0x8000
+
+//add a new item to the MirOTR Menu
+//wParam=0
+//lParam=(LPARAM)(CLISTMENUITEM*)&mi
+//returns a handle to the new item, or NULL on failure
+//the service that is called when the item is clicked is called with
+//wParam=0, lParam=hContact
+//dividers are inserted every 100000 positions
+//pszContactOwner is ignored for this service.
+//
+// WARNING: do not use Translate(TS) for p(t)szName or p(t)szPopupName as they
+// are translated by the core, which may lead to double translation.
+// Use LPGEN instead which are just dummy wrappers/markers for "lpgen.pl".
+typedef struct {
+ int cbSize; //size in bytes of this structure
+ union {
+ char* pszName; //[TRANSLATED-BY-CORE] text of the menu item
+ TCHAR* ptszName; //Unicode text of the menu item
+ };
+ int position; //approx position on the menu. lower numbers go nearer the top
+ HGENMENU root; //submenu where the item will be added, NULL for root
+ DWORD flags; //set of MOMIF_* flags
+ union {
+ HICON hIcon; //icon to put by the item. If this was not loaded from
+ //a resource, you can delete it straight after the call
+ HANDLE icolibItem; //set CMIF_ICONFROMICOLIB to pass this value
+ };
+ DWORD hotKey; //keyboard accelerator, same as lParam of WM_HOTKEY,0 for none
+ char* pszService; //name of service to call when the item gets selected
+} MIROTRMENUITEM;
+
+void InitMirOTRMenu(void);
+void UninitMirOTRMenu(void);
+
+void ShowOTRMenu(MCONTACT hContact, POINT pt);
diff --git a/plugins/MirOTR/src/options.cpp b/plugins/MirOTR/src/options.cpp
new file mode 100644
index 0000000000..7a4bce4afc
--- /dev/null
+++ b/plugins/MirOTR/src/options.cpp
@@ -0,0 +1,868 @@
+#include "stdafx.h"
+
+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;
+
+ CreateDirectoryTree(path);
+ 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];
+ if ( FoldersGetCustomPath(hPATH_MIROTR, path, SIZEOF(path), ""))
+ SetFilenames( VARS(DATA_DIRECTORY));
+ else
+ SetFilenames(path);
+
+ ReadPrivkeyFiles();
+ return 0;
+}
+
+void LoadFilenames()
+{
+ if (hPATH_MIROTR = FoldersRegisterCustomPath("OTR", LPGEN("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 {
+ SetFilenames( VARS(DATA_DIRECTORY));
+ ReadPrivkeyFiles();
+ }
+}
+
+void LoadOptions() {
+ options.default_policy = db_get_w(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)db_get_w(0, MODULENAME, "ErrorDisplay", ED_POP);
+ options.prefix_messages = (db_get_b(0, MODULENAME, "PrefixMessages", 0) == 1);
+ options.msg_inline = (db_get_b(0, MODULENAME, "MsgInline", 0) == 1);
+ options.msg_popup = (db_get_b(0, MODULENAME, "MsgPopup", 1) == 1);
+ options.delete_history = (db_get_b(0, MODULENAME, "NoHistory", 0) == 1);
+ options.delete_systeminfo = (db_get_b(0, MODULENAME, "NoSystemHistory", 0) == 1);
+ options.autoshow_verify = (db_get_b(0, MODULENAME, "AutoShowVerify", 1) == 1);
+
+ DBVARIANT dbv;
+ if (!db_get_utf(0, MODULENAME, "Prefix", &dbv)) {
+ strncpy(options.prefix, dbv.pszVal, OPTIONS_PREFIXLEN-1);
+ options.prefix[OPTIONS_PREFIXLEN-1] = 0;
+ db_free(&dbv);
+ } else
+ strncpy(options.prefix, ("OTR: "), OPTIONS_PREFIXLEN-1);
+
+ options.end_offline = (db_get_b(0, MODULENAME, "EndOffline", 1) == 1);
+ options.end_window_close = (db_get_b(0, MODULENAME, "EndWindowClose", 0) == 1);
+
+ options.bHavePopups = 0 != ServiceExists(MS_POPUP_ADDPOPUPT) && ServiceExists(MS_POPUP_SHOWMESSAGE);
+ options.bHaveSecureIM = 0 != ServiceExists("SecureIM/IsContactSecured");
+ options.bHaveButtonsBar = 0 != ServiceExists(MS_BB_ADDBUTTON);
+
+ LoadFilenames();
+}
+
+extern "C" int OpenOptions(WPARAM wParam, LPARAM lParam)
+{
+ OPTIONSDIALOGPAGE odp = { sizeof(odp) };
+ odp.position = 100;
+ odp.hInstance = hInst;
+ odp.ptszGroup = LPGENT("Services");
+ odp.ptszTitle = _T("OTR");
+ odp.flags = ODPF_BOLDGROUPS|ODPF_TCHAR;
+
+ odp.ptszTab = _T(LANG_OPT_GENERAL);
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_GENERAL);
+ odp.pfnDlgProc = DlgProcMirOTROpts;
+ Options_AddPage(wParam, &odp);
+
+ odp.ptszTab = _T(LANG_OPT_PROTO);
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_PROTO);
+ odp.pfnDlgProc = DlgProcMirOTROptsProto;
+ Options_AddPage(wParam, &odp);
+
+ odp.ptszTab = _T(LANG_OPT_CONTACTS);
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_CONTACTS);
+ odp.pfnDlgProc = DlgProcMirOTROptsContacts;
+ Options_AddPage(wParam, &odp);
+
+ odp.ptszTab = _T(LANG_OPT_FINGER);
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_FINGER);
+ odp.pfnDlgProc = DlgProcMirOTROptsFinger;
+ Options_AddPage(wParam, &odp);
+ return 0;
+}
+
+void InitOptions() {
+ LoadOptions();
+ hHookOptions = HookEvent(ME_OPT_INITIALISE, OpenOptions);
+}
+
+void DeinitOptions() {
+ UnhookEvent(hHookOptions);
+}
+
+void SaveOptions() {
+ db_set_w(0, MODULENAME, "DefaultPolicy", options.default_policy);
+ db_set_w(0, MODULENAME, "ErrorDisplay", (int)options.err_method);
+ db_set_b(0, MODULENAME, "PrefixMessages", options.prefix_messages ? 1 : 0);
+ db_set_b(0, MODULENAME, "MsgInline", options.msg_inline ? 1 : 0);
+ db_set_b(0, MODULENAME, "MsgPopup", options.msg_popup ? 1 : 0);
+
+ db_set_b(0, MODULENAME, "NoHistory", options.delete_history ? 1 : 0);
+ db_set_b(0, MODULENAME, "NoSystemHistory", options.delete_systeminfo ? 1 : 0);
+ db_set_b(0, MODULENAME, "AutoShowVerify", options.autoshow_verify ? 1 : 0);
+
+ db_set_utf(0, MODULENAME, "Prefix", options.prefix);
+
+ db_set_b(0, MODULENAME, "EndOffline", options.end_offline ? 1 : 0);
+ db_set_b(0, MODULENAME, "EndWindowClose", options.end_window_close ? 1 : 0);
+}
+
+extern "C" void set_context_contact(void *data, ConnContext *context)
+{
+ MCONTACT hContact = find_contact(context->username, context->protocol);
+ context->app_data = (void*)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, BST_CHECKED);
+ break;
+ case OTRL_POLICY_MANUAL_MOD:
+ CheckDlgButton(hwndDlg, IDC_RAD_MANUAL, BST_CHECKED);
+ break;
+ case OTRL_POLICY_ALWAYS:
+ CheckDlgButton(hwndDlg, IDC_RAD_ALWAYS, BST_CHECKED);
+ break;
+ case OTRL_POLICY_NEVER:
+ CheckDlgButton(hwndDlg, IDC_RAD_NEVER, BST_CHECKED);
+ break;
+ }
+
+ CheckDlgButton(hwndDlg, IDC_CHK_PREFIX, options.prefix_messages ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHK_MSGINLINE, options.msg_inline ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHK_MSGPOPUP, options.msg_popup ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHK_PREVENTSAVE, options.delete_history ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHK_PREVENTSYSSAVE, options.delete_systeminfo ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHK_AUTOSHOW_VERIFY, options.autoshow_verify ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHK_ENDOFFLINE, options.end_offline ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHK_ENDCLOSE, options.end_window_close ? BST_CHECKED : BST_UNCHECKED);
+
+ 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_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.delete_history = (TRUE==IsDlgButtonChecked(hwndDlg, IDC_CHK_PREVENTSAVE));
+ options.delete_systeminfo = (TRUE==IsDlgButtonChecked(hwndDlg, IDC_CHK_PREVENTSYSSAVE));
+ 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-1);
+ mir_free(prefix_utf);
+
+ SaveOptions();
+
+ return TRUE;
+ }
+
+ break;
+ }
+ return FALSE;
+}
+
+static unsigned int CALLBACK regen_key_thread(void* param)
+{
+ Thread_Push(0);
+ PROTOREGENKEYOPTIONS *opts = (PROTOREGENKEYOPTIONS *)param;
+ TCHAR buff[512];
+
+ mir_sntprintf(buff, SIZEOF(buff), 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))
+ {
+ 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);
+ */
+ }
+ EnableWindow(opts->refresh, TRUE);
+ delete opts;
+ Thread_Pop();
+ 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));
+ SendDlgItemMessage(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;
+ PROTOACCOUNT **pppDesc;
+ int ilvItem;
+ LV_ITEM item = {0};
+ char fprint[45];
+ TCHAR* temp;
+ //BOOL unicode = ListView_GetUnicodeFormat(lv);
+
+ ProtoEnumAccounts(&num_protocols, &pppDesc);
+ for(int i = 0; i < num_protocols; i++) {
+ if((strcmp(pppDesc[i]->szModuleName, META_PROTO) != 0)
+ && (CallProtoService(pppDesc[i]->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_IM) == PF1_IM)
+ {
+ //if (unicode) {
+ item.mask = LVIF_TEXT;
+ temp = mir_a2t(pppDesc[i]->szModuleName);
+ item.pszText = temp;
+ ilvItem = ListView_InsertItem(lv, &item);
+ mir_free(temp);
+
+
+ ListView_SetItemText(lv, ilvItem, 1, (TCHAR*)policy_to_string(db_get_dw(0,MODULENAME"_ProtoPol", pppDesc[i]->szModuleName, CONTACT_DEFAULT_POLICY)) );
+ if(otrl_privkey_fingerprint(otr_user_state, fprint, pppDesc[i]->szModuleName, pppDesc[i]->szModuleName)) {
+ temp = mir_a2t(fprint);
+ ListView_SetItemText(lv, ilvItem, 2, temp);
+ mir_free(temp);
+ }
+ }
+ }
+ }
+ 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, SIZEOF(opts->proto));
+ 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, SIZEOF(buff_proto));
+ TCHAR buff[512];
+ mir_sntprintf(buff, SIZEOF(buff), 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;
+ }
+ 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) {
+ SendDlgItemMessage(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, SIZEOF(buff));
+ SendDlgItemMessage(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, SIZEOF(proto_t));
+ ListView_GetItemText(lv, i, 1, policy, SIZEOF(policy));
+ proto = mir_t2a(proto_t);
+ db_set_dw(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, GWLP_USERDATA, (LONG_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 = 100; // width of column in pixels
+ ListView_InsertColumn(lv, 1, &lvc);
+
+ lvc.iSubItem = 2;
+ lvc.pszText = TranslateT(LANG_POLICY);
+ lvc.cx = 90; // width of column in pixels
+ ListView_InsertColumn(lv, 2, &lvc);
+
+ lvc.iSubItem = 3;
+ lvc.pszText = TranslateT(LANG_HTMLCONV);
+ lvc.cx = 80; // width of column in pixels
+ ListView_InsertColumn(lv, 3, &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;
+ for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) {
+ proto = GetContactProto(hContact);
+ if(proto && db_get_b(hContact, proto, "ChatRoom", 0) == 0 && CallService(MS_PROTO_ISPROTOONCONTACT, hContact, (LPARAM)MODULENAME) // ignore chatrooms
+ && strcmp(proto, META_PROTO) != 0) // and MetaContacts
+ {
+ lvI.iItem = 0;
+ lvI.iSubItem = 0;
+ lvI.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_get_dw(hContact, MODULENAME, "Policy", CONTACT_DEFAULT_POLICY)) );
+ ListView_SetItemText(lv, lvI.iItem, 3, (db_get_b(hContact, MODULENAME, "HTMLConv", 0))?TranslateT(LANG_YES):TranslateT(LANG_NO) );
+ }
+ }
+ }
+ return TRUE;
+ break;
+
+ case WM_COMMAND:
+ switch ( HIWORD( wParam )) {
+ case CBN_SELCHANGE:
+ switch ( LOWORD( wParam )) {
+ case IDC_CMB_CONT_POLICY:
+ {
+ MCONTACT 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, GWLP_USERDATA);
+ hContact = (MCONTACT)lvi.lParam;
+ (*cpm)[hContact].policy = policy;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }break;
+ }
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ {
+ UINT code = ((LPNMHDR) lParam)->code;
+ if (code == (UINT) PSN_APPLY ) {
+ // handle apply
+
+ ContactPolicyMap *cpm = (ContactPolicyMap*) GetWindowLongPtr(hwndDlg, GWLP_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;
+ if (it->second.policy) db_set_dw(it->first, MODULENAME, "Policy", (DWORD)it->second.policy);
+ if (it->second.htmlconv) db_set_b(it->first, MODULENAME, "HTMLConv", it->second.htmlconv-1);
+ }
+ return TRUE;
+ } else if (((LPNMHDR) lParam)->hwndFrom == GetDlgItem(hwndDlg, IDC_LV_CONT_CONTACTS)) {
+ if (code == (UINT) LVN_ITEMCHANGED && (((LPNMLISTVIEW)lParam)->uNewState & LVIS_SELECTED )) {
+ int sel = ListView_GetSelectionMark(((LPNMHDR) lParam)->hwndFrom);
+ if (sel == -1) {
+ SendDlgItemMessage(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, SIZEOF(buff));
+ SendDlgItemMessage(hwndDlg, IDC_CMB_CONT_POLICY, CB_SELECTSTRING, (LPARAM)-1, (WPARAM)buff);
+ }
+ } else if (code == (UINT) NM_CLICK) {
+ if (((LPNMLISTVIEW)lParam)->iSubItem == 3) {
+ LVITEM lvi;
+ lvi.mask = LVIF_PARAM;
+ lvi.iItem = ((LPNMLISTVIEW)lParam)->iItem;
+ if (lvi.iItem < 0) return FALSE;
+ lvi.iSubItem = 0;
+ SendDlgItemMessage(hwndDlg, IDC_LV_CONT_CONTACTS, LVM_GETITEM, 0, (LPARAM)&lvi);
+
+ MCONTACT hContact = (MCONTACT)lvi.lParam;
+ ContactPolicyMap *cp = (ContactPolicyMap *)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ TCHAR buff[50];
+ ListView_GetItemText(((LPNMHDR)lParam)->hwndFrom, lvi.iItem, 3, buff, SIZEOF(buff));
+ if (_tcsncmp(buff, TranslateT(LANG_YES), 50)==0){
+ (*cp)[hContact].htmlconv = HTMLCONV_DISABLE;
+ ListView_SetItemText(((LPNMHDR)lParam)->hwndFrom, lvi.iItem, 3, TranslateT(LANG_NO));
+ }else {
+ (*cp)[hContact].htmlconv = HTMLCONV_ENABLE;
+ ListView_SetItemText(((LPNMHDR)lParam)->hwndFrom, lvi.iItem, 3, TranslateT(LANG_YES));
+ }
+ (*cp)[hContact].htmlconv += 1;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+
+
+ }
+ }
+ }break;
+ case WM_DESTROY:
+ ContactPolicyMap *cpm = (ContactPolicyMap*) GetWindowLongPtr(hwndDlg, GWLP_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, GWLP_USERDATA, (LONG_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;
+
+ TCHAR *proto, *user, hash[45] = {0};
+ for (ConnContext *context = otr_user_state->context_root;context;context = context->next) {
+ if (context->app_data) {
+ user = (TCHAR*)contact_get_nameT((MCONTACT)context->app_data);
+ if (user) {
+ proto = mir_a2t(context->protocol);
+
+ for(Fingerprint *fp = context->fingerprint_root.next;fp;fp = fp->next) {
+ otrl_privkey_hash_to_humanT(hash, fp->fingerprint);
+ 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] != '\0')? TranslateT(LANG_YES) : TranslateT(LANG_NO));
+ ListView_SetItemText(lv,d, 4, hash );
+ }
+ mir_free(proto);
+ }
+ }
+ }
+ }
+
+ 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;
+ ListView_GetItem(GetDlgItem(hwndDlg, IDC_LV_FINGER_LIST), &lvi);
+ Fingerprint *fp = (Fingerprint*) lvi.lParam;
+ FPModifyMap* fpm = (FPModifyMap*) GetWindowLongPtr(hwndDlg, GWLP_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;
+ ListView_GetItem(GetDlgItem(hwndDlg, IDC_LV_FINGER_LIST), &lvi);
+ Fingerprint *fp = (Fingerprint*) lvi.lParam;
+ FPModifyMap* fpm = (FPModifyMap*) GetWindowLongPtr(hwndDlg, GWLP_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;
+ ListView_GetItem(GetDlgItem(hwndDlg, IDC_LV_FINGER_LIST), &lvi);
+ Fingerprint *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, SIZEOF(buff), TranslateT(LANG_FINGERPRINT_STILL_IN_USE), hash, contact_get_nameT((MCONTACT)fp->context->app_data));
+ ShowError(buff);
+ } else {
+ FPModifyMap* fpm = (FPModifyMap*) GetWindowLongPtr(hwndDlg, GWLP_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, GWLP_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, SIZEOF(buff), TranslateT(LANG_FINGERPRINT_NOT_DELETED), hash, contact_get_nameT((MCONTACT)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)
+ VerifyFingerprint(it->first->context, true);
+ //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)
+ VerifyFingerprint(it->first->context, false);
+ //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, GWLP_USERDATA);
+ fpm->clear();
+ delete fpm;
+ break;
+ }
+
+ return FALSE;
+} \ No newline at end of file
diff --git a/plugins/MirOTR/src/options.h b/plugins/MirOTR/src/options.h
new file mode 100644
index 0000000000..772d0c5d45
--- /dev/null
+++ b/plugins/MirOTR/src/options.h
@@ -0,0 +1,69 @@
+#ifndef _OPTIONS_INC
+#define _OPTIONS_INC
+
+#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 delete_history;
+ bool delete_systeminfo;
+ bool autoshow_verify;
+
+ bool end_offline, end_window_close;
+
+ // temporary options
+ bool bHavePopups, bHaveSecureIM, bHaveButtonsBar;
+
+} Options;
+
+extern Options options;
+
+#define WMU_REFRESHLIST (WM_USER + 241)
+#define WMU_REFRESHPROTOLIST (WM_USER + 242)
+#define CONTACT_DEFAULT_POLICY 0xFFFF
+
+#include <map>
+enum HTMLConvSetting {
+ HTMLCONV_DISABLE,
+ HTMLCONV_ENABLE
+};
+struct CONTACT_DATA {
+ OtrlPolicy policy;
+ int htmlconv;
+};
+typedef std::map<MCONTACT, CONTACT_DATA> ContactPolicyMap;
+typedef enum {FPM_VERIFY, FPM_NOTRUST, FPM_DELETE } FPModify;
+typedef std::map<Fingerprint*, FPModify> FPModifyMap;
+
+int OptInit(WPARAM wParam, LPARAM lParam);
+
+void LoadOptions();
+void SaveOptions();
+extern "C" int 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/plugins/MirOTR/src/otr.cpp b/plugins/MirOTR/src/otr.cpp
new file mode 100644
index 0000000000..de3f7e42ed
--- /dev/null
+++ b/plugins/MirOTR/src/otr.cpp
@@ -0,0 +1,395 @@
+#include "stdafx.h"
+
+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;
+ const char *proto;
+};
+
+static unsigned int CALLBACK generate_key_thread(void* param)
+{
+ Thread_Push(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);
+ mir_free(data);
+ Thread_Pop();
+ 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, GCLP_HICON, (LONG_PTR)LoadIcon(ICON_OTR,1) );
+ TCHAR buff[256];
+ TCHAR *proto = mir_a2t((char*)lParam);
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_GENERATE_KEY), proto);
+ mir_free(proto);
+ SetDlgItemText(hWndDlg, IDC_GENERATE, buff);
+ GenKeyData *data = (GenKeyData *)mir_calloc(sizeof(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, GCLP_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")
+ MCONTACT hContact = (MCONTACT)opdata;
+ DWORD pol;
+ if(hContact) {
+ pol = db_get_dw(hContact, MODULENAME, "Policy", CONTACT_DEFAULT_POLICY);
+ if (options.bHaveSecureIM && pol != OTRL_POLICY_MANUAL_MOD && pol != OTRL_POLICY_NEVER && db_get_b(hContact, "SecureIM" , "StatusID", 0)) {
+ // if SecureIM is not disabled for this contact, MirOTR will be set to manual
+ db_set_dw(hContact, MODULENAME, "Policy", OTRL_POLICY_MANUAL_MOD);
+ return OTRL_POLICY_MANUAL_MOD;
+ }
+ if(pol != CONTACT_DEFAULT_POLICY) return pol ;
+ }
+ if(context->protocol) {
+ pol = db_get_dw(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);
+ if (opdata) protocol = GetContactProto((MCONTACT)opdata);
+ if (!protocol) return;
+ DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_GENKEYNOTIFY), 0, 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")
+ MCONTACT hContact = (MCONTACT)opdata;
+ if(hContact) {
+ WORD status = db_get_w(hContact, GetContactProto(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);
+ MCONTACT hContact = (MCONTACT)opdata;
+
+ if(protocol && db_get_w(hContact, protocol, "Status", ID_STATUS_OFFLINE) != ID_STATUS_OFFLINE)
+ CallContactService(hContact, PSS_MESSAGE, PREF_UTF|PREF_BYPASS_OTR, (LPARAM)message);
+ }
+
+ /* 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")
+ MCONTACT hContact = (MCONTACT) opdata;
+ const TCHAR* uname = contact_get_nameT(hContact);
+ TCHAR* title_t = mir_utf8decodeT(title);
+ TCHAR *notify = TranslateT(LANG_OTR_NOTIFY);
+
+ size_t 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) {
+ MCONTACT hContact = (MCONTACT) opdata;
+ DEBUGOUT_T("OTR_GUI_DISPLAY_OTR_MESSAGE")
+ if(options.msg_inline)
+ ShowMessageInlineUtf(hContact, Translate(msg));
+ if(options.msg_popup) {
+ TCHAR buff[512];
+ TCHAR* proto = mir_a2t(protocol);
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_OTR_USERMESSAGE), contact_get_nameT(hContact), 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")
+ 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")
+ MCONTACT hContact = (MCONTACT) opdata;
+ TrustLevel trusted = otr_context_get_trust(context);
+ // opdata is hContact
+ SetEncryptionStatus(hContact, trusted);
+ TCHAR buff[1024];
+ if(trusted == TRUST_PRIVATE) {
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_SESSION_START_OTR), contact_get_nameT(hContact));
+ } else if (trusted == TRUST_UNVERIFIED) {
+ if (options.autoshow_verify) SMPInitDialog(context); //VerifyContextDialog(context);
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_SESSION_START_OTR_VERIFY), contact_get_nameT(hContact));
+ } else { // should never happen
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_SESSION_NOT_STARTED_OTR), contact_get_nameT(hContact));
+ }
+ // opdata is hContact
+ ShowMessage(hContact, buff);
+
+ }
+
+ /* A ConnContext has left a secure state. */
+ void otr_gui_gone_insecure(void *opdata, ConnContext *context) {
+ MCONTACT hContact = (MCONTACT) opdata;
+ DEBUGOUT_T("OTR_GUI_GONE_INSECURE")
+ TCHAR buff[512];
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_SESSION_TERMINATED_BY_OTR), contact_get_nameT(hContact));
+ //MessageBox(0, buff, Translate("OTR Information"), MB_OK);
+ if (!Miranda_Terminated()) {
+ ShowMessage(hContact, buff);
+ }
+
+ // opdata is hContact
+ SetEncryptionStatus(hContact, 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) {
+ MCONTACT hContact = (MCONTACT) opdata;
+ DEBUGOUT_T("OTR_GUI_STILL_SECURE")
+ TrustLevel trusted = otr_context_get_trust(context);
+ SetEncryptionStatus(hContact, trusted);
+ TCHAR buff[1024];
+ if (!is_reply) {
+ if(trusted == TRUST_PRIVATE) {
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_SESSION_CONTINUE_OTR), contact_get_nameT(hContact));
+ } else if (trusted == TRUST_UNVERIFIED) {
+ if (options.autoshow_verify) SMPInitDialog(context); //VerifyContextDialog(context);
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_SESSION_CONTINUE_OTR_VERIFY), contact_get_nameT(hContact));
+ } else { // should never happen
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_SESSION_NOT_STARTED_OTR), contact_get_nameT(hContact));
+ }
+ // opdata is hContact
+ ShowMessage(hContact, buff);
+ } else {
+ if(trusted == TRUST_PRIVATE) {
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_SESSION_HAS_CONTINUE_OTR), contact_get_nameT(hContact));
+ } else if (trusted == TRUST_UNVERIFIED) {
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_SESSION_HAS_CONTINUE_OTR_VERIFY), contact_get_nameT(hContact));
+ } else { // should never happen
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_SESSION_NOT_STARTED_OTR), contact_get_nameT(hContact));
+ }
+
+ }
+ SetEncryptionStatus(hContact, 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((MCONTACT)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) {
+ const char *proto;
+ if (context && context->protocol)
+ proto = context->protocol;
+ else
+ proto = GetContactProto((MCONTACT)opdata);
+ // ugly wokaround for ICQ. ICQ protocol reports more than 7k, but in SMP this is too long.
+ // possibly ICQ doesn't allow single words without spaces to become longer than ~2340?
+ if (strcmp("ICQ", proto)==0 || strncmp("ICQ_", proto, 4)==0)
+ return 2340;
+ return CallProtoService(proto, PS_GETCAPS, PFLAG_MAXLENOFMESSAGE, (LPARAM)opdata);
+ }
+
+ 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/plugins/MirOTR/src/otr.h b/plugins/MirOTR/src/otr.h
new file mode 100644
index 0000000000..795991ffdb
--- /dev/null
+++ b/plugins/MirOTR/src/otr.h
@@ -0,0 +1,51 @@
+#pragma once
+
+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/plugins/MirOTR/src/resource.h b/plugins/MirOTR/src/resource.h
new file mode 100644
index 0000000000..3b117f8699
--- /dev/null
+++ b/plugins/MirOTR/src/resource.h
@@ -0,0 +1,95 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by resource.rc
+//
+#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_ENDOFFLINE 1063
+#define IDC_CHK_ENDCLOSE 1064
+#define IDC_CHK_PREVENTSYSSAVE 1065
+
+// 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 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/plugins/MirOTR/src/stdafx.cpp b/plugins/MirOTR/src/stdafx.cpp
new file mode 100644
index 0000000000..98540cbccf
--- /dev/null
+++ b/plugins/MirOTR/src/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : Quelldatei, die nur die Standard-Includes einbindet.
+// MirOTR.pch ist der vorkompilierte Header.
+// stdafx.obj enthlt die vorkompilierten Typinformationen.
+
+#include "stdafx.h"
+
+// TODO: Auf zustzliche Header verweisen, die in STDAFX.H
+// und nicht in dieser Datei erforderlich sind.
diff --git a/plugins/MirOTR/src/stdafx.h b/plugins/MirOTR/src/stdafx.h
new file mode 100644
index 0000000000..d5e557c907
--- /dev/null
+++ b/plugins/MirOTR/src/stdafx.h
@@ -0,0 +1,96 @@
+// stdafx.h : Includedatei fr Standardsystem-Includedateien
+// oder hufig verwendete projektspezifische Includedateien,
+// die nur in unregelmigen Abstnden gendert werden.
+//
+
+#pragma once
+
+#define _CRT_NON_CONFORMING_SWPRINTFS
+#define _CRT_SECURE_NO_WARNINGS
+#define WIN32_LEAN_AND_MEAN // Selten verwendete Teile der Windows-Header nicht einbinden.
+
+#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_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
+
+#include <windows.h>
+#include <commctrl.h>
+#include <process.h>
+#include <stack>
+#include <gcrypt.h>
+extern "C" {
+ #include <privkey.h>
+ #include <proto.h>
+ #include <tlv.h>
+ #include <message.h>
+ #include <userstate.h>
+}
+using namespace std;
+
+#include <newpluginapi.h>
+#include <m_langpack.h>
+#include <m_database.h>
+#include <m_message.h>
+#include <m_protomod.h>
+#include <m_popup.h>
+#include <m_contacts.h>
+#include <m_icolib.h>
+#include <m_skin.h>
+#include <win2k.h>
+#include <m_genmenu.h>
+#include <m_options.h>
+
+//ExternalAPI
+#include <m_folders.h>
+#include <m_msg_buttonsbar.h>
+#include <m_metacontacts.h>
+
+#include "otr.h"
+#include "dbfilter.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"
+#include "version.h"
+#include "entities.h"
+#include "icons.h"
+#include "mirotrmenu.h"
+#include "striphtml.h"
+#include "ekhtml.h"
+#include "svcs_proto.h"
+
+#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
+
+// 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)
+
+#define MODULENAME "MirOTR"
+
+#define PREF_BYPASS_OTR 0x8000
+#define PREF_NO_HISTORY 0x10000
+
+extern HINSTANCE hInst;
+
+// TODO: Hier auf zustzliche Header, die das Programm erfordert, verweisen.
diff --git a/plugins/MirOTR/src/striphtml.cpp b/plugins/MirOTR/src/striphtml.cpp
new file mode 100644
index 0000000000..4a3c3759ff
--- /dev/null
+++ b/plugins/MirOTR/src/striphtml.cpp
@@ -0,0 +1,122 @@
+#include "stdafx.h"
+
+void starttag_cb (void *cbdata, ekhtml_string_t *tag, ekhtml_attr_t *attrs) {
+ STRIPHTML_DATA *data = (STRIPHTML_DATA *)cbdata;
+ switch (tag->len) {
+ case 1:
+ switch (tag->str[0]) {
+ case 'a':
+ case 'A':
+ for(ekhtml_attr_t *attr=attrs; attr; attr=attr->next) {
+ if (_strnicmp(attr->name.str, "href", attr->name.len)==0) {
+ data->stack.push(strncpy((char*)mir_calloc(attr->val.len+1), attr->val.str, attr->val.len));
+ break;
+ }
+ }
+ break;
+ case 'i':
+ case 'I':
+ data->buffer.append(" *");
+ break;
+ case 'b':
+ case 'B':
+ data->buffer.append(" _");
+ break;
+ }
+ break;
+ case 2:
+ if (toupper(tag->str[0]) == 'B' && toupper(tag->str[1]) == 'R')
+ data->buffer.append("\r\n");
+ break;
+ case 3:
+ if (_strnicmp(tag->str, "img", 3) == 0) {
+ data->buffer.append("IMAGE [ ");
+ for(ekhtml_attr_t *attr=attrs; attr; attr=attr->next) {
+ if (_strnicmp(attr->name.str, "src", attr->name.len)==0) {
+ data->buffer.append(attr->val.str, attr->val.len);
+ break;
+ }
+ }
+ data->buffer.append(" ] ");
+ }
+ break;
+ }
+}
+
+void endtag_cb (void *cbdata, ekhtml_string_t *tag) {
+ STRIPHTML_DATA *data = (STRIPHTML_DATA *)cbdata;
+ switch (tag->len) {
+ case 1:
+ switch (*(tag->str)) {
+ case 'a':
+ case 'A':
+ if (data->stack.empty()) break;
+ data->buffer.append(" [ ");
+ data->buffer.append(data->stack.top());
+ mir_free(data->stack.top());
+ data->stack.pop();
+ data->buffer.append(" ] ");
+ break;
+ case 'i':
+ case 'I':
+ data->buffer.append("* ");
+ break;
+ case 'b':
+ case 'B':
+ data->buffer.append("_ ");
+ break;
+ }
+ }
+
+}
+
+void data_cb (void *cbdata, ekhtml_string_t *text) {
+ STRIPHTML_DATA *data = (STRIPHTML_DATA *)cbdata;
+ if(text->len <= 0)
+ return;
+ char* s = (char*) mir_calloc(text->len+1);
+ decode_html_entities_utf8(s, text->str, text->len);
+
+ if (!data->stack.empty()) {
+ char *top = data->stack.top();
+ if (_stricmp(s, top)==0) {
+ mir_free(top);
+ data->stack.pop();
+ }
+ }
+
+
+ data->buffer.append(s);
+ mir_free(s);
+}
+
+char * striphtml(char *html) {
+ STRIPHTML_DATA data;
+ ekhtml_string_t ekstring;
+
+ ekstring.len = strlen(html);
+ ekstring.str = html;
+
+ data.buffer.clear();
+ data.buffer.reserve(ekstring.len);
+
+ ekhtml_parser_t *parser = ekhtml_parser_new(&data);
+ ekhtml_parser_datacb_set(parser, &data_cb);
+ ekhtml_parser_startcb_add(parser, NULL, &starttag_cb);
+ ekhtml_parser_endcb_add(parser, NULL, &endtag_cb);
+
+ ekhtml_parser_feed(parser, &ekstring);
+ ekhtml_parser_flush(parser, 1);
+
+ while (!data.stack.empty()) {
+ mir_free(data.stack.top());
+ data.stack.pop();
+ }
+
+ ekhtml_parser_destroy(parser);
+
+ char *s = mir_strdup(data.buffer.c_str());
+ data.buffer.erase();
+ return s;
+ //ekhtml_parser_create(
+}
diff --git a/plugins/MirOTR/src/striphtml.h b/plugins/MirOTR/src/striphtml.h
new file mode 100644
index 0000000000..3b2d9b6cbc
--- /dev/null
+++ b/plugins/MirOTR/src/striphtml.h
@@ -0,0 +1,8 @@
+#pragma once
+
+struct STRIPHTML_DATA {
+ std::stack<char*> stack;
+ std::string buffer;
+};
+
+char * striphtml(char *html); \ No newline at end of file
diff --git a/plugins/MirOTR/src/svcs_menu.cpp b/plugins/MirOTR/src/svcs_menu.cpp
new file mode 100644
index 0000000000..6699c00adc
--- /dev/null
+++ b/plugins/MirOTR/src/svcs_menu.cpp
@@ -0,0 +1,189 @@
+#include "stdafx.h"
+
+static HGENMENU hStopItem, hStartItem;
+
+////////////////////////////////
+///////// Menu Services ////////
+///////////////////////////////
+int StartOTR(MCONTACT hContact) {
+ const char *proto = GetContactProto(hContact);
+ if (!proto) return 1; // error
+ char *uname = contact_get_id(hContact);
+ if (!uname) return 1; // error
+ DWORD pol = db_get_dw(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((void*)hContact, proto, proto, uname, msg ? msg : "?OTRv2?");
+ lib_cs_unlock();
+ otrl_message_free(msg);
+ mir_free(uname);
+ return 0;
+}
+
+INT_PTR SVC_StartOTR(WPARAM hContact, LPARAM lParam)
+{
+ TCHAR buff[512];
+
+ MCONTACT hSub = db_mc_getMostOnline(hContact);
+ if(hSub != 0)
+ hContact = hSub;
+
+ if ( options.bHaveSecureIM && CallService("SecureIM/IsContactSecured", hContact, 0) != 0 ) {
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_OTR_SECUREIM_STARTED), contact_get_nameT(hContact));
+ ShowError(buff);
+ return 0;
+ }
+
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_SESSION_REQUEST_OTR), contact_get_nameT(hContact));
+ ShowMessage(hContact, buff);
+
+ return StartOTR(hContact);
+}
+
+INT_PTR SVC_RefreshOTR(WPARAM hContact, LPARAM lParam)
+{
+ TCHAR buff[512];
+
+ MCONTACT hSub = db_mc_getMostOnline(hContact);
+ if(hSub != 0)
+ hContact = hSub;
+
+ if ( options.bHaveSecureIM && CallService("SecureIM/IsContactSecured", hContact, 0) != 0 ) {
+ mir_sntprintf(buff, 512, TranslateT(LANG_OTR_SECUREIM_STARTED), contact_get_nameT(hContact));
+ ShowError(buff);
+ return 0;
+ }
+
+ mir_sntprintf(buff, SIZEOF(buff), 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(MCONTACT hContact)
+{
+ MCONTACT hSub = db_mc_getMostOnline(hContact);
+ if (hSub != 0)
+ hContact = hSub;
+
+ const char *proto = GetContactProto(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, (void*)hContact, proto, proto, uname);
+ lib_cs_unlock();
+ mir_free(uname);
+ return 0;
+}
+
+INT_PTR SVC_StopOTR(WPARAM hContact, LPARAM lParam)
+{
+ // 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, SIZEOF(buff), TranslateT(LANG_SESSION_TERMINATED_OTR), contact_get_nameT(hContact));
+ ShowMessage(hContact, buff);
+ return 0;
+}
+
+INT_PTR SVC_VerifyOTR(WPARAM hContact, LPARAM lParam)
+{
+ MCONTACT hSub = db_mc_getMostOnline(hContact);
+ if(hSub != 0)
+ hContact = hSub;
+
+ ConnContext *context = otrl_context_find_miranda(otr_user_state, hContact);
+ if (!context)
+ return 1;
+
+ //VerifyContextDialog(context);
+ SMPInitDialog(context);
+ return 0;
+}
+
+INT_PTR SVC_ToggleHTMLOTR(WPARAM hContact, LPARAM lParam)
+{
+ MCONTACT hSub = db_mc_getMostOnline(hContact);
+ if (hSub != 0)
+ hContact = hSub;
+
+ if (db_get_b(hContact, MODULENAME, "HTMLConv", 0))
+ db_set_b(hContact, MODULENAME, "HTMLConv", 0);
+ else
+ db_set_b(hContact, MODULENAME, "HTMLConv", 1);
+
+ return 0;
+}
+
+void InitMenu()
+{
+ CreateServiceFunction(MS_OTR_MENUSTART, SVC_StartOTR);
+ CreateServiceFunction(MS_OTR_MENUSTOP, SVC_StopOTR);
+ CreateServiceFunction(MS_OTR_MENUREFRESH, SVC_RefreshOTR);
+ CreateServiceFunction(MS_OTR_MENUVERIFY, SVC_VerifyOTR);
+ CreateServiceFunction(MS_OTR_MENUTOGGLEHTML, SVC_ToggleHTMLOTR);
+
+ CLISTMENUITEM mi = { sizeof(mi) };
+ mi.flags = CMIF_NOTOFFLINE | CMIF_TCHAR;
+ mi.position = -400000;
+
+ mi.ptszName = _T(LANG_STOP_OTR);
+ mi.pszService = MS_OTR_MENUSTOP;
+ mi.icolibItem = GetIconHandle(ICON_PRIVATE);
+
+ hStopItem = Menu_AddContactMenuItem(&mi);
+
+ mi.ptszName = _T(LANG_START_OTR);
+ mi.pszService = MS_OTR_MENUSTART;
+ mi.icolibItem = GetIconHandle(ICON_NOT_PRIVATE);
+
+ hStartItem = Menu_AddContactMenuItem(&mi);
+
+ HookEvent(ME_CLIST_PREBUILDCONTACTMENU, SVC_PrebuildContactMenu);
+}
+
+int SVC_PrebuildContactMenu(WPARAM hContact, LPARAM lParam)
+{
+ CLISTMENUITEM mi = { sizeof(mi) };
+ mi.flags = CMIM_FLAGS | CMIF_NOTOFFLINE | CMIF_TCHAR;
+
+ const char *proto = GetContactProto(hContact);
+ DWORD pol = CONTACT_DEFAULT_POLICY;
+
+ if (!proto || db_get_b(hContact, proto, "ChatRoom", 0) == 1) {
+hide_all:
+ Menu_ShowItem(hStartItem, false);
+ Menu_ShowItem(hStopItem, false);
+ return 0;
+ }
+
+ if(proto && strcmp(proto, META_PROTO) == 0) {
+ // make menu act as per most online subcontact
+ hContact = db_mc_getMostOnline(hContact);
+ if (!hContact)
+ goto hide_all;
+ proto = GetContactProto(hContact);
+ }
+
+ pol = db_get_dw(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);
+ Menu_ShowItem(hStartItem, encrypted == TRUST_NOT_PRIVATE);
+ Menu_ShowItem(hStopItem, encrypted != TRUST_NOT_PRIVATE);
+ return 0;
+} \ No newline at end of file
diff --git a/plugins/MirOTR/src/svcs_menu.h b/plugins/MirOTR/src/svcs_menu.h
new file mode 100644
index 0000000000..145a080be9
--- /dev/null
+++ b/plugins/MirOTR/src/svcs_menu.h
@@ -0,0 +1,13 @@
+#pragma once
+INT_PTR SVC_StartOTR(WPARAM wParam, LPARAM lParam);
+INT_PTR SVC_RefreshOTR(WPARAM wParam, LPARAM lParam);
+INT_PTR SVC_StopOTR(WPARAM wParam, LPARAM lParam);
+INT_PTR SVC_VerifyOTR(WPARAM wParam, LPARAM lParam);
+int SVC_PrebuildContactMenu(WPARAM wParam, LPARAM lParam);
+void InitMenu();
+
+#define MS_OTR_MENUSTART MODULENAME"/Start"
+#define MS_OTR_MENUSTOP MODULENAME"/Stop"
+#define MS_OTR_MENUREFRESH MODULENAME"/Refresh"
+#define MS_OTR_MENUVERIFY MODULENAME"/Verify"
+#define MS_OTR_MENUTOGGLEHTML MODULENAME"/ToggleHTML" \ No newline at end of file
diff --git a/plugins/MirOTR/src/svcs_proto.cpp b/plugins/MirOTR/src/svcs_proto.cpp
new file mode 100644
index 0000000000..99592f354b
--- /dev/null
+++ b/plugins/MirOTR/src/svcs_proto.cpp
@@ -0,0 +1,384 @@
+#include "stdafx.h"
+
+//TODO: Social-Millionaire-Dialoge
+INT_PTR 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 = GetContactProto(ccs->hContact);
+ if(proto && strcmp(proto, META_PROTO) == 0) // bypass for metacontacts
+ return CallService(MS_PROTO_CHAINSEND, wParam, lParam);
+
+ if (!proto || !ccs->hContact)
+ return 1; // error
+
+ 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);
+ //should be the thing with strlen
+ oldmessage_utf = mir_utf8encodeW((wchar_t*)&oldmessage[strlen(oldmessage)+1]);
+ }
+ 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);
+ }
+
+ char *tmpencode = NULL;
+ ConnContext *context = otrl_context_find_miranda(otr_user_state, ccs->hContact);
+ if (db_get_b(ccs->hContact, MODULENAME, "HTMLConv", 0) && otr_context_get_trust(context) >= TRUST_UNVERIFIED) {
+ tmpencode = encode_html_entities_utf8(oldmessage_utf);
+ if (tmpencode != NULL) {
+ if (!(ccs->wParam & PREF_UTF)) mir_free(oldmessage_utf);
+ oldmessage_utf = tmpencode;
+ }
+ }
+
+ char *newmessage = 0;
+ char *username = contact_get_id(ccs->hContact);
+ gcry_error_t err = otrl_message_sending(otr_user_state, &ops, (void*)ccs->hContact,
+ proto, proto, username, oldmessage_utf, NULL, &newmessage,
+ add_appdata, (void*)ccs->hContact);
+ if (tmpencode!= NULL || !(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));
+ 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 */
+
+ // 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;
+ }
+ INT_PTR ret = CallService(MS_PROTO_CHAINSEND, wParam, lParam);
+
+#ifdef _DEBUG
+ if(ccs->wParam & PREF_UNICODE)
+ {
+ TCHAR *mes = mir_a2t((const char *)ccs->lParam);
+ MessageBox(0, mes, _T("OTR - sending raw message"), MB_OK);
+ mir_free(mes);
+ }
+ 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);
+}
+
+/*
+#define MESSAGE_PREFIX "(OTR) "
+#define MESSAGE_PREFIXW L"(OTR) "
+#define MESSAGE_PREFIX_LEN 6
+*/
+
+INT_PTR 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)
+ {
+ TCHAR *mes = mir_a2t(pre->szMessage);
+ MessageBox(0, mes, _T("OTR - receiving message"), MB_OK);
+ mir_free(mes);
+ }
+ else
+ MessageBoxA(0, (char *)pre->szMessage, ("OTR - receiving message"), MB_OK);
+#endif
+
+ if (pre->flags & PREF_BYPASS_OTR) { // bypass for our inline messages
+ return CallService(MS_PROTO_CHAINRECV, wParam, lParam);
+ }
+
+ char *proto = GetContactProto(ccs->hContact);
+ if (!proto)
+ return 1; //error
+ else if(proto && strcmp(proto, META_PROTO) == 0) // bypass for metacontacts
+ return CallService(MS_PROTO_CHAINRECV, wParam, lParam);
+
+ if (!ccs->hContact)
+ 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;
+
+ char *uname = contact_get_id(ccs->hContact);
+ lib_cs_lock();
+ BOOL 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();
+ mir_free(uname);
+
+ if ( !(pre->flags & PREF_UTF))
+ mir_free(oldmessage_utf);
+ oldmessage_utf = NULL;
+
+
+ OtrlTLV *tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED);
+ if (tlv && !Miranda_Terminated()) {
+ /* Notify the user that the other side disconnected. */
+ TCHAR buff[256];
+ mir_sntprintf(buff, SIZEOF(buff), TranslateT(LANG_SESSION_TERMINATED_BY_OTR), contact_get_nameT(ccs->hContact));
+ //MessageBox(0, buff, Translate("OTR Information"), MB_OK);
+ ShowMessage(ccs->hContact, buff);
+ }
+
+ lib_cs_lock();
+ ConnContext *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) {
+ NextExpectedSMP 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);
+ SMPDialogUpdate(context, 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) {
+ SMPDialogReply(context, question);
+ }
+ }
+ }
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
+ if (tlv) {
+ if (nextMsg != OTRL_SMP_EXPECT1)
+ otr_abort_smp(context);
+ else {
+ SMPDialogReply(context, NULL);
+ }
+ }
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
+ if (tlv) {
+ if (nextMsg != OTRL_SMP_EXPECT2)
+ otr_abort_smp(context);
+ else {
+ context->smstate->nextExpected = OTRL_SMP_EXPECT4;
+ SMPDialogUpdate(context, 60);
+ }
+ }
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
+ if (tlv) {
+ if (nextMsg != OTRL_SMP_EXPECT3)
+ otr_abort_smp(context);
+ else {
+ context->smstate->nextExpected = OTRL_SMP_EXPECT1;
+ SMPDialogUpdate(context, 100);
+ //otrg_dialog_update_smp(context, 1.0);
+ }
+ }
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
+ if (tlv) {
+ if (nextMsg != OTRL_SMP_EXPECT4)
+ otr_abort_smp(context);
+ else {
+ SMPDialogUpdate(context, 100);
+ //otrg_dialog_update_smp(context, 1.0);
+ context->smstate->nextExpected = OTRL_SMP_EXPECT1;
+ }
+ }
+ tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
+ if (tlv) {
+ SMPDialogUpdate(context, 0);
+ context->smstate->nextExpected = OTRL_SMP_EXPECT1;
+ }
+ }
+ }
+ otrl_tlv_free(tlvs);
+
+ /* 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};
+ char t[3] = {0, 0, 0};
+ if(pre->flags & PREF_UNICODE) { //TODO!! mir_tstrlen(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!! mir_tstrlen(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 (level >= TRUST_UNVERIFIED) {
+ char *premsg;
+ if (db_get_b(ccs->hContact, MODULENAME, "HTMLConv", 0)) {
+ premsg = striphtml(newmessage);
+ otrl_message_free(newmessage);
+ newmessage = premsg;
+ is_miralloc = true;
+ }
+
+ if (options.prefix_messages) {
+ size_t len = (strlen(options.prefix)+strlen(newmessage)+1)*sizeof(char);
+ premsg = (char*)mir_alloc( len );
+ memset(premsg, 0, len);
+ strcpy(premsg, options.prefix);
+ strcat(premsg, newmessage);
+ (is_miralloc) ? mir_free(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/plugins/MirOTR/src/svcs_proto.h b/plugins/MirOTR/src/svcs_proto.h
new file mode 100644
index 0000000000..a260560cc6
--- /dev/null
+++ b/plugins/MirOTR/src/svcs_proto.h
@@ -0,0 +1,12 @@
+#pragma once
+
+INT_PTR SVC_OTRSendMessage(WPARAM wParam,LPARAM lParam);
+INT_PTR 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/plugins/MirOTR/src/svcs_srmm.cpp b/plugins/MirOTR/src/svcs_srmm.cpp
new file mode 100644
index 0000000000..56e3f44d9b
--- /dev/null
+++ b/plugins/MirOTR/src/svcs_srmm.cpp
@@ -0,0 +1,156 @@
+#include "stdafx.h"
+
+HICON hIconNotSecure, hIconFinished, hIconPrivate, hIconUnverified;
+BBButton OTRButton;
+
+int SVC_IconPressed(WPARAM hContact, LPARAM lParam)
+{
+ StatusIconClickData *sicd = (StatusIconClickData *)lParam;
+ if(sicd->cbSize < sizeof(StatusIconClickData))
+ return 0;
+
+ if(strcmp(sicd->szModule, MODULENAME) == 0) {
+ char *proto = GetContactProto(hContact);
+ if(proto && db_get_b(hContact, proto, "ChatRoom", 0))
+ return 0;
+ ShowOTRMenu(hContact, sicd->clickLocation);
+ }
+
+ return 0;
+}
+
+// set SRMM icon status, if applicable
+void SetEncryptionStatus(MCONTACT hContact, TrustLevel level)
+{
+ char *proto = GetContactProto(hContact);
+ bool chat_room = (proto && db_get_b(hContact, proto, "ChatRoom", 0));
+
+ BBButton button = OTRButton;
+
+ StatusIconData sid = { sizeof(sid) }, sid2 = { sizeof(sid) };
+ sid.szModule = MODULENAME;
+ sid.dwId = 0;
+ sid.flags = MBF_HIDDEN;
+
+ sid2.szModule = MODULENAME;
+ sid2.dwId = 1;
+ sid2.flags = MBF_HIDDEN;
+
+ if (!chat_room) {
+ switch (level) {
+ case TRUST_FINISHED:
+ sid.flags = 0;
+ button.ptszTooltip = TranslateT(LANG_STATUS_FINISHED);
+ button.hIcon = GetIconHandle(ICON_FINISHED);
+ break;
+ case TRUST_UNVERIFIED:
+ sid2.flags = MBF_DISABLED;
+ button.ptszTooltip = TranslateT(LANG_STATUS_UNVERIFIED);
+ button.hIcon = GetIconHandle(ICON_UNVERIFIED);
+ break;
+ case TRUST_PRIVATE:
+ sid2.flags = 0;
+ button.ptszTooltip = TranslateT(LANG_STATUS_PRIVATE);
+ button.hIcon = GetIconHandle(ICON_PRIVATE);
+ break;
+ default:
+ sid.flags = MBF_DISABLED;
+ button.ptszTooltip = TranslateT(LANG_STATUS_DISABLED);
+ button.hIcon = GetIconHandle(ICON_NOT_PRIVATE);
+ break;
+ }
+ button.bbbFlags = 0;
+ }
+ else button.bbbFlags = BBSF_HIDDEN;
+
+ Srmm_ModifyIcon(hContact, &sid);
+ Srmm_ModifyIcon(hContact, &sid2);
+
+ if (options.bHaveButtonsBar) CallService(MS_BB_SETBUTTONSTATE, hContact, (LPARAM)&button);
+ db_set_dw(hContact, MODULENAME, "TrustLevel", level);
+
+ if (!chat_room) {
+ MCONTACT hMeta = db_mc_getMeta(hContact);
+ MCONTACT hMostOnline = db_mc_getMostOnline(hMeta);
+ if(hMeta && hContact == hMostOnline)
+ SetEncryptionStatus(hMeta, level);
+ else if(hMeta) {
+ /* in case the new most online contact has changed
+ (e.g. when the otr subcontact goes offline) */
+ ConnContext *context = otrl_context_find_miranda(otr_user_state, hMostOnline);
+ TrustLevel encrypted = otr_context_get_trust(context);
+ SetEncryptionStatus(hMeta, encrypted);
+ }
+ }
+}
+
+int SVC_ButtonsBarLoaded(WPARAM, LPARAM)
+{
+ CallService(MS_BB_ADDBUTTON, 0, (LPARAM)&OTRButton);
+ return 0;
+}
+
+int SVC_ButtonsBarPressed(WPARAM w, LPARAM l)
+{
+ CustomButtonClickData* cbcd = (CustomButtonClickData *)l;
+ if (cbcd->cbSize == sizeof(CustomButtonClickData) && cbcd->dwButtonId == 0 && strcmp(cbcd->pszModule, MODULENAME)==0) {
+ MCONTACT hContact = (MCONTACT)w;
+
+ char *proto = GetContactProto(hContact);
+ if(proto && db_get_b(hContact, proto, "ChatRoom", 0))
+ return 0;
+ ShowOTRMenu(hContact, cbcd->pt);
+
+ }
+ return 0;
+}
+
+void InitSRMM()
+{
+ // add icon to srmm status icons
+ InitMirOTRMenu();
+
+ hIconNotSecure = LoadIcon(ICON_NOT_PRIVATE, 0);
+ hIconFinished = LoadIcon(ICON_FINISHED, 0);
+ hIconPrivate = LoadIcon(ICON_PRIVATE, 0);
+ hIconUnverified = LoadIcon(ICON_UNVERIFIED, 0);
+
+ StatusIconData sid = { sizeof(sid) };
+ sid.szModule = MODULENAME;
+ sid.hIcon = hIconFinished;
+ sid.hIconDisabled = hIconNotSecure;
+ sid.flags = MBF_DISABLED | MBF_HIDDEN;
+ sid.szTooltip = LANG_OTR_TOOLTIP;
+ Srmm_AddIcon(&sid);
+
+ sid.dwId = 1;
+ sid.hIcon = hIconPrivate;
+ sid.hIconDisabled = hIconUnverified;
+ Srmm_AddIcon(&sid);
+
+ // hook the window events so that we can can change the status of the icon
+ HookEvent(ME_MSG_ICONPRESSED, SVC_IconPressed);
+
+ if (options.bHaveButtonsBar) {
+ memset(&OTRButton, 0, sizeof(OTRButton));
+ OTRButton.cbSize = sizeof(OTRButton);
+ OTRButton.dwButtonID = 0;
+ OTRButton.pszModuleName = MODULENAME;
+ OTRButton.dwDefPos = 200;
+ OTRButton.bbbFlags = BBBF_ISRSIDEBUTTON|BBBF_CANBEHIDDEN|BBBF_ISIMBUTTON;
+ OTRButton.ptszTooltip = TranslateT(LANG_OTR_TOOLTIP);
+ OTRButton.hIcon = GetIconHandle(ICON_NOT_PRIVATE);
+ HookEvent(ME_MSG_TOOLBARLOADED, SVC_ButtonsBarLoaded);
+ HookEvent(ME_MSG_BUTTONPRESSED, SVC_ButtonsBarPressed);
+ }
+}
+void DeinitSRMM()
+{
+ ReleaseIcon(ICON_NOT_PRIVATE, 0);
+ ReleaseIcon(ICON_FINISHED, 0);
+ ReleaseIcon(ICON_PRIVATE, 0);
+ ReleaseIcon(ICON_UNVERIFIED, 0);
+ hIconNotSecure = hIconFinished = hIconPrivate = hIconUnverified =0;
+
+ UninitMirOTRMenu();
+} \ No newline at end of file
diff --git a/plugins/MirOTR/src/svcs_srmm.h b/plugins/MirOTR/src/svcs_srmm.h
new file mode 100644
index 0000000000..dc3bbc9672
--- /dev/null
+++ b/plugins/MirOTR/src/svcs_srmm.h
@@ -0,0 +1,7 @@
+#pragma once
+
+int WindowEvent(WPARAM wParam, LPARAM lParam);
+int SVC_IconPressed(WPARAM wParam, LPARAM lParam);
+void SetEncryptionStatus(MCONTACT hContact, TrustLevel level);
+void InitSRMM();
+void DeinitSRMM(); \ No newline at end of file
diff --git a/plugins/MirOTR/src/utils.cpp b/plugins/MirOTR/src/utils.cpp
new file mode 100644
index 0000000000..d834fb84a1
--- /dev/null
+++ b/plugins/MirOTR/src/utils.cpp
@@ -0,0 +1,382 @@
+#include "stdafx.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);
+}
+
+MCONTACT find_contact(const char* userid, const char* protocol)
+{
+ for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) {
+ const char *proto = GetContactProto(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);
+ }
+ }
+
+ 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, MCONTACT hContact)
+{
+ ConnContext ** curp;
+ if (!hContact) return NULL;
+ for (curp = &(us->context_root); *curp; curp = &((*curp)->next)) {
+ if ((*curp)->app_data == (void*)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;
+}
+
+/* Set verification of fingerprint */
+void VerifyFingerprint(ConnContext *context, bool verify) {
+ lib_cs_lock();
+ otrl_context_set_trust(context->active_fingerprint, (verify)?"verified":NULL);
+ otrl_privkey_write_fingerprints(otr_user_state, g_fingerprint_store_filename);
+ lib_cs_unlock();
+ VerifyFingerprintMessage(context, verify);
+}
+
+void VerifyFingerprintMessage(ConnContext *context, bool verify) {
+ MCONTACT hContact = (MCONTACT)context->app_data;
+ TCHAR msg[1024];
+
+ mir_sntprintf(msg, SIZEOF(msg), (verify)?TranslateT(LANG_FINGERPRINT_VERIFIED):TranslateT(LANG_FINGERPRINT_NOT_VERIFIED), contact_get_nameT(hContact));
+ ShowMessage(hContact, msg);
+ SetEncryptionStatus(hContact, otr_context_get_trust(context));
+}
+
+/* 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(MCONTACT hContact, bool bNameOnError) {
+ char* pszUniqueID = NULL;
+ CONTACTINFO ci;
+ memset(&ci, 0, 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, hContact, 0);
+ if (name) pszUniqueID = mir_strdup(name);
+ }
+ return pszUniqueID;
+}
+
+__inline const TCHAR* contact_get_nameT(MCONTACT hContact) {
+ return (TCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, hContact, GCDNF_TCHAR);
+}
+
+
+__inline const char* contact_get_account(MCONTACT hContact) {
+ char *uacc = (char *)CallService(MS_PROTO_GETCONTACTBASEACCOUNT, hContact, 0);
+ return uacc;
+}
+
+void ShowPopup(const TCHAR* line1, const TCHAR* line2, int timeout, const MCONTACT hContact) {
+ if(CallService(MS_SYSTEM_TERMINATED, 0, 0)) return;
+
+ if ( !options.bHavePopups) {
+ TCHAR title[256];
+ mir_sntprintf(title, SIZEOF(title), _T("%s Message"), _T(MODULENAME));
+
+ if(line1 && line2) {
+ int size = int(_tcslen(line1) + _tcslen(line2) + 3);
+ TCHAR *message = new TCHAR[size]; // newline and null terminator
+ mir_sntprintf(message, size, _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)
+ _tcsncpy( ppd.lptzText, line1, MAX_SECONDLINE-1 );
+ else if(line2)
+ _tcsncpy( ppd.lptzText, line2, MAX_SECONDLINE-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;
+
+ mir_sntprintf(buffer, SIZEOF(buffer), _T("%s Warning"), _T(MODULENAME));
+
+ TCHAR *message;
+ switch(disp) {
+ case ED_POP:
+ {
+ int size = int(_tcslen(msg) + 515);
+ message = new TCHAR[size]; // newline and null terminator
+ mir_sntprintf(message, size, _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;
+
+ sn.dwInfoFlags = NIIF_WARNING | NIIF_INTERN_UNICODE;
+
+ 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;
+
+ mir_sntprintf(buffer, SIZEOF(buffer), _T("%s Error"), _T(MODULENAME));
+
+
+ TCHAR *message;
+ switch(disp) {
+ case ED_POP:
+ {
+ int size = int(_tcslen(msg) + 515);
+ message = new TCHAR[size]; // newline and null terminator
+ mir_sntprintf(message, size, _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;
+
+ sn.dwInfoFlags = NIIF_ERROR | NIIF_INTERN_UNICODE;
+
+ sn.uTimeout = 10;
+
+ CallService(MS_CLIST_SYSTRAY_NOTIFY, 0, (LPARAM)&sn);
+ }
+
+ break;
+ }
+}
+
+
+void ShowPopupUtf(const char* line1, const char* line2, int timeout, const MCONTACT 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 MCONTACT hContact, const TCHAR *msg) {
+ TCHAR buff[1024];
+ mir_sntprintf(buff, SIZEOF(buff), _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;
+ ProtoChainRecvMsg(hContact, &pre);
+
+ mir_free(utf);
+}
+
+void ShowMessageInlineUtf(const MCONTACT hContact, const char *msg) {
+ char buff[1024];
+ mir_snprintf(buff, SIZEOF(buff), "%s%s", Translate(LANG_INLINE_PREFIX), msg);
+
+ PROTORECVEVENT pre = {0};
+ pre.timestamp = time(0);
+ pre.szMessage = buff;
+ pre.flags = PREF_UTF|PREF_BYPASS_OTR;
+ ProtoChainRecvMsg(hContact, &pre);
+}
+
+void ShowMessageUtf(const MCONTACT 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 MCONTACT hContact, const TCHAR *msg) {
+ if(options.msg_inline)
+ ShowMessageInline(hContact, msg);
+ if(options.msg_popup)
+ ShowPopup(TranslateT(LANG_OTR_INFO), msg, 0, hContact);
+}
+
+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/plugins/MirOTR/src/utils.h b/plugins/MirOTR/src/utils.h
new file mode 100644
index 0000000000..71890a77eb
--- /dev/null
+++ b/plugins/MirOTR/src/utils.h
@@ -0,0 +1,48 @@
+#pragma once
+#ifndef __HEADER_UTILS_H
+#define __HEADER_UTILS_H
+#include "stdafx.h"
+
+
+void lib_cs_lock();
+void lib_cs_unlock();
+
+MCONTACT find_contact(const char* userid, const char* protocol);
+ConnContext * otrl_context_find_miranda(OtrlUserState us, MCONTACT hContact);
+
+typedef enum {
+ TRUST_NOT_PRIVATE,
+ TRUST_FINISHED,
+ TRUST_UNVERIFIED,
+ TRUST_PRIVATE
+} TrustLevel;
+TrustLevel otr_context_get_trust(ConnContext *context);
+void VerifyFingerprint(ConnContext *context, bool verify);
+void VerifyFingerprintMessage(ConnContext *context, bool verify);
+
+void otrl_privkey_hash_to_humanT(TCHAR human[45], const unsigned char hash[20]);
+
+char* contact_get_id(MCONTACT hContact, bool bNameOnError=true);
+__inline const char* contact_get_account(MCONTACT hContact);
+extern __inline const TCHAR* contact_get_nameT(MCONTACT hContact);
+
+void ShowPopup(const TCHAR* line1, const TCHAR* line2, int timeout, const MCONTACT hContact = NULL);
+void ShowWarning(TCHAR* msg);
+void ShowError(TCHAR* msg);
+void ShowPopupUtf(const char* line1, const char* line2, int timeout, const MCONTACT hContact = NULL);
+void ShowWarningUtf(char* msg);
+void ShowErrorUtf(char* msg);
+
+void ShowMessageInline(const MCONTACT hContact, const TCHAR *msg);
+void ShowMessageInlineUtf(const MCONTACT hContact, const char *msg);
+
+void ShowMessage(const MCONTACT hContact, const TCHAR *msg);
+void ShowMessageUtf(const MCONTACT 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/plugins/MirOTR/src/version.h b/plugins/MirOTR/src/version.h
new file mode 100644
index 0000000000..48205e676a
--- /dev/null
+++ b/plugins/MirOTR/src/version.h
@@ -0,0 +1,14 @@
+#define __MAJOR_VERSION 0
+#define __MINOR_VERSION 11
+#define __RELEASE_NUM 0
+#define __BUILD_NUM 5
+
+#include <stdver.h>
+
+#define __PLUGIN_NAME "Miranda OTR"
+#define __FILENAME "MirOTR.dll"
+#define __DESCRIPTION "OTR (Off-the-Record) plugin for Miranda NG (using some code and ideas from SecureIM, Pidgin-OTR and old Miranda OTR (by SJE))."
+#define __AUTHOR "ProgAndy & CHEF-KOCH"
+#define __AUTHOREMAIL ""
+#define __AUTHORWEB "http://miranda-ng.org/p/MirOTR/"
+#define __COPYRIGHT " ProgAndy & CHEF-KOCH"