From 649818c0d9d8d2a6751da668fed6df342ded96fb Mon Sep 17 00:00:00 2001 From: "admin@progandy.co.cc" Date: Fri, 10 Sep 2010 11:47:55 +0000 Subject: - [ issue 2 ] halfway implemented SMP (can send but not receive) - implemented finish session when contact goes offline git-svn-id: http://mirotr.googlecode.com/svn/trunk@5 eced67a3-f377-a0ae-92ae-d6de1850b05a --- MirOTR/dbfilter.cpp | 82 ++++++++++++++++++++++++++++++++++++- MirOTR/dialogs.cpp | 111 ++++++++++++++++++++++++++++++++++++++++++-------- MirOTR/dialogs.h | 5 ++- MirOTR/dllmain.cpp | 23 ++++++----- MirOTR/svcs_menu.cpp | 3 +- MirOTR/svcs_proto.cpp | 26 ++++++------ MirOTR/version.h | 6 +-- 7 files changed, 212 insertions(+), 44 deletions(-) (limited to 'MirOTR') diff --git a/MirOTR/dbfilter.cpp b/MirOTR/dbfilter.cpp index cfd7537..c93b168 100644 --- a/MirOTR/dbfilter.cpp +++ b/MirOTR/dbfilter.cpp @@ -1,6 +1,6 @@ #include "stdafx.h" #include "dbfilter.h" -static HANDLE hDBEventPreAdd, hDBEventAdded; +static HANDLE hDBEventPreAdd, hDBEventAdded, hContactSettingChanged; static CRITICAL_SECTION RemoveChainCS={0}, *lpRemoveChainCS = &RemoveChainCS; static UINT_PTR timerId = 0; @@ -219,10 +219,88 @@ INT_PTR OnDatabaseEventAdded(WPARAM wParam, LPARAM lParam) { return 0; } +void FinishSession(HANDLE 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; +} + + +// 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(Miranda_Terminated() || status != ID_STATUS_OFFLINE ) return 0; + + const char *proto = (char *)lParam; + HANDLE 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 = context->app_data; + + if(hContact) { + otrl_message_disconnect(otr_user_state, &ops, hContact, context->accountname, context->protocol, context->username); + SetEncryptionStatus(hContact, TRUST_NOT_PRIVATE); + } + + } + context = context->next; + } + lib_cs_unlock(); + + return 0; +} + +INT_PTR OnContactSettingChanged(WPARAM wParam, LPARAM lParam) { + DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING *)lParam; + if (!lParam || strcmp(cws->szSetting, "Status") != 0) return 0; + HANDLE hContact = (HANDLE)wParam; + 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, (WPARAM)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() { @@ -230,6 +308,8 @@ void DeinitDBFilter() { hDBEventPreAdd = 0; UnhookEvent(hDBEventAdded); hDBEventAdded = 0; + UnhookEvent(hContactSettingChanged); + hContactSettingChanged=0; if (timerId) KillTimer(0, timerId); DeleteTimerProc(0,0,0,0); DeleteCriticalSection(lpRemoveChainCS); diff --git a/MirOTR/dialogs.cpp b/MirOTR/dialogs.cpp index 5b5c6f1..5946686 100644 --- a/MirOTR/dialogs.cpp +++ b/MirOTR/dialogs.cpp @@ -4,6 +4,25 @@ #include #include +static void VerifyFingerprint(ConnContext *context, bool verify) { + TCHAR msg[1024]; + if (verify) { + lib_cs_lock(); + otrl_context_set_trust(context->active_fingerprint, "verified"); + otrl_privkey_write_fingerprints(otr_user_state, g_fingerprint_store_filename); + lib_cs_unlock(); + mir_sntprintf(msg, 1024, TranslateT(LANG_FINGERPRINT_VERIFIED), contact_get_nameT((HANDLE)context->app_data)); + } else { + lib_cs_lock(); + otrl_context_set_trust(context->active_fingerprint, NULL); + otrl_privkey_write_fingerprints(otr_user_state, g_fingerprint_store_filename); + lib_cs_unlock(); + mir_sntprintf(msg, 1024, TranslateT(LANG_FINGERPRINT_NOT_VERIFIED), contact_get_nameT((HANDLE)context->app_data)); + } + msg[1023] = '\0'; + ShowMessage((HANDLE)context->app_data, msg); + SetEncryptionStatus(context->app_data, otr_context_get_trust(context)); +} INT_PTR CALLBACK DlgProcSMPInitProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -97,14 +116,56 @@ INT_PTR CALLBACK DlgProcSMPInitProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARA case WM_COMMAND: switch ( HIWORD( wParam )) { case BN_CLICKED: - switch ( LOWORD( wParam )) { - case IDYES: - case IDNO: - case IDCANCEL: - case IDOK: - EndDialog(hwndDlg, LOWORD( wParam )); + { + ConnContext* context = (ConnContext*)GetWindowLongPtr(hwndDlg, GWL_USERDATA); + 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) { + 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; + + 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) { + 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_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: @@ -120,7 +181,7 @@ INT_PTR CALLBACK DlgProcSMPInitProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARA TCHAR buff[512]; GetDlgItemText(hwndDlg, IDC_CBO_SMP_CHOOSE, buff, 255); - if (_tcsncmp(buff, TranslateT(LANG_SMPTYPE_QUESTION), 255)) { + if (_tcsncmp(buff, TranslateT(LANG_SMPTYPE_QUESTION), 255)==0) { if (trusted) mir_sntprintf(buff, 512, TranslateT(LANG_OTR_SMPQUESTION_VERIFY_DESC), contact_get_nameT((HANDLE)context->app_data)); else @@ -140,7 +201,7 @@ INT_PTR CALLBACK DlgProcSMPInitProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARA 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)) { + } else if (_tcsncmp(buff, TranslateT(LANG_SMPTYPE_PASSWORD), 255)==0) { if (trusted) mir_sntprintf(buff, 512, TranslateT(LANG_OTR_SMPPASSWORD_VERIFY_DESC), contact_get_nameT((HANDLE)context->app_data)); else @@ -149,18 +210,18 @@ INT_PTR CALLBACK DlgProcSMPInitProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARA 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_PASSWORD)); + 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, TRUE, 0); - SetDlgItemText(hwndDlg, IDC_STC_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)) { + } else if (_tcsncmp(buff, TranslateT(LANG_SMPTYPE_FINGERPRINT), 255)==0) { if (trusted) mir_sntprintf(buff, 512, TranslateT(LANG_OTR_FPVERIFY_DESC), contact_get_nameT((HANDLE)context->app_data)); else @@ -186,8 +247,6 @@ INT_PTR CALLBACK DlgProcSMPInitProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARA 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); @@ -207,6 +266,26 @@ void SMPInitDialog(ConnContext* context) { CreateDialogParamW(hInst, MAKEINTRESOURCE(IDD_SMP_INPUT), 0, DlgProcSMPInitProc, (LPARAM) context); } +void SMPDialogUpdate(ConnContext *context, int percent) { + 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){ + 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; diff --git a/MirOTR/dialogs.h b/MirOTR/dialogs.h index 0dfd955..27593db 100644 --- a/MirOTR/dialogs.h +++ b/MirOTR/dialogs.h @@ -1,3 +1,6 @@ #pragma once void VerifyContextDialog(ConnContext* context); -INT_PTR CALLBACK DlgProcVerifyContext(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); \ No newline at end of file +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); \ No newline at end of file diff --git a/MirOTR/dllmain.cpp b/MirOTR/dllmain.cpp index 1f3c131..2982789 100644 --- a/MirOTR/dllmain.cpp +++ b/MirOTR/dllmain.cpp @@ -59,6 +59,8 @@ int ModulesLoaded(WPARAM wParam, LPARAM lParam) { if(ServiceExists(MS_MC_GETPROTOCOLNAME)) g_metaproto = (char *)CallService(MS_MC_GETPROTOCOLNAME, 0, 0); + // DISABLED UPDATE CHECK FOR NOW + /* if(ServiceExists(MS_UPDATE_REGISTER)) { // register with updater Update update = {0}; @@ -69,7 +71,7 @@ int ModulesLoaded(WPARAM wParam, LPARAM lParam) { update.szComponentName = pluginInfo.shortName; update.pbVersion = (BYTE *)CreateVersionString(pluginInfo.version, szVersion); update.cpbVersion = strlen((char *)update.pbVersion); - update.szBetaChangelogURL = "https://server.scottellis.com.au/wsvn/mim_plugs/otr/?op=log&rev=0&sc=0&isdir=1"; + update.szBetaChangelogURL = "http://code.google.com/p/mirotr/source/list"; update.szUpdateURL = UPDATER_AUTOREGISTER; @@ -77,16 +79,15 @@ int ModulesLoaded(WPARAM wParam, LPARAM lParam) { // before the version that we use to locate it on the page // (note that if the update URL and the version URL point to standard file listing entries, the backend xml // data will be used to check for updates rather than the actual web page - this is not true for beta urls) - update.szBetaUpdateURL = "http://www.scottellis.com.au/miranda_plugins/otr.zip"; - update.szBetaVersionURL = "http://www.scottellis.com.au/miranda_plugins/ver_otr.html"; - update.pbBetaVersionPrefix = (BYTE *)"OTR (Off The Record) encryption plugin, version "; + update.szBetaUpdateURL = "http://www.progandy.co.cc/mirotr/download.php"; // redirection script to current release on GoogleCode + update.szBetaVersionURL = "http://www.progandy.co.cc/mirotr/version.txt"; + update.pbBetaVersionPrefix = "MirOTR version: "; - update.cpbBetaVersionPrefix = strlen((char *)update.pbBetaVersionPrefix); + update.cpbBetaVersionPrefix = strlen(update.pbBetaVersionPrefix); - - // DISABLED UPDATE CHECK FOR NOW - // CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update); + CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update); } + */ InitUtils(); @@ -144,7 +145,6 @@ DLLFUNC int Load(PLUGINLINK *link) CallService(MS_PROTO_REGISTERMODULE,0,(LPARAM)&pd); // remove us as a filter to all contacts - fix filter type problem - /* if(DBGetContactSettingByte(0, MODULENAME, "FilterOrderFix", 0) != 2) { HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); while ( hContact != NULL ) { @@ -153,14 +153,17 @@ DLLFUNC int Load(PLUGINLINK *link) } DBWriteContactSettingByte(0, MODULENAME, "FilterOrderFix", 2); } - */ + // add us as a filter to all contacts HANDLE hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDFIRST, 0, 0 ); char *proto; while ( hContact != NULL ) { proto = (char *)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, 0); + + // do not add filter to chatoom if ( !(proto && DBGetContactSettingByte(hContact, proto, "ChatRoom", 0)) ) CallService( MS_PROTO_ADDTOCONTACT, ( WPARAM )hContact, ( LPARAM )MODULENAME ); + hContact = ( HANDLE )CallService( MS_DB_CONTACT_FINDNEXT,( WPARAM )hContact, 0 ); } HookEvent(ME_DB_CONTACT_ADDED, NewContact); diff --git a/MirOTR/svcs_menu.cpp b/MirOTR/svcs_menu.cpp index da1104a..3f99834 100644 --- a/MirOTR/svcs_menu.cpp +++ b/MirOTR/svcs_menu.cpp @@ -90,7 +90,8 @@ int SVC_VerifyOTR(WPARAM wParam, LPARAM lParam) { HANDLE hContact = (HANDLE)wParam; ConnContext *context = otrl_context_find_miranda(otr_user_state, (HANDLE)wParam); if (!context) return 1; - VerifyContextDialog(context); + //VerifyContextDialog(context); + SMPInitDialog(context); return 0; } diff --git a/MirOTR/svcs_proto.cpp b/MirOTR/svcs_proto.cpp index 8bbb553..2d2bde6 100644 --- a/MirOTR/svcs_proto.cpp +++ b/MirOTR/svcs_proto.cpp @@ -198,6 +198,8 @@ int SVC_OTRRecvMessage(WPARAM wParam,LPARAM lParam){ 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) || @@ -208,15 +210,16 @@ int SVC_OTRRecvMessage(WPARAM wParam,LPARAM lParam){ { otr_abort_smp(context); // we do not support it (yet), notify partner to shorten wait time } + */ - /* if (context) { - nextMsg = context->smstate->nextExpected; + 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); + //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 { @@ -229,8 +232,7 @@ int SVC_OTRRecvMessage(WPARAM wParam,LPARAM lParam){ char *question = (char *)tlv->data; char *eoq = (char*)memchr(question, '\0', tlv->len); if (eoq) { - otrg_dialog_socialist_millionaires_q(context, - question); + SMPDialogReply(context, question); } } } @@ -239,7 +241,7 @@ int SVC_OTRRecvMessage(WPARAM wParam,LPARAM lParam){ if (nextMsg != OTRL_SMP_EXPECT1) otr_abort_smp(context); else { - otrg_dialog_socialist_millionaires(context); + SMPDialogReply(context, NULL); } } tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2); @@ -247,8 +249,8 @@ int SVC_OTRRecvMessage(WPARAM wParam,LPARAM lParam){ if (nextMsg != OTRL_SMP_EXPECT2) otr_abort_smp(context); else { - otrg_dialog_update_smp(context, 0.6); context->smstate->nextExpected = OTRL_SMP_EXPECT4; + SMPDialogUpdate(context, 60); } } tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3); @@ -256,8 +258,9 @@ int SVC_OTRRecvMessage(WPARAM wParam,LPARAM lParam){ if (nextMsg != OTRL_SMP_EXPECT3) otr_abort_smp(context); else { - otrg_dialog_update_smp(context, 1.0); context->smstate->nextExpected = OTRL_SMP_EXPECT1; + SMPDialogUpdate(context, 100); + //otrg_dialog_update_smp(context, 1.0); } } tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4); @@ -265,19 +268,18 @@ int SVC_OTRRecvMessage(WPARAM wParam,LPARAM lParam){ if (nextMsg != OTRL_SMP_EXPECT4) otr_abort_smp(context); else { - otrg_dialog_update_smp(context, 1.0); + 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) { - otrg_dialog_update_smp(context, 0.0); + SMPDialogUpdate(context, 0); context->smstate->nextExpected = OTRL_SMP_EXPECT1; } } } - */ - otrl_tlv_free(tlvs); mir_free(uname); diff --git a/MirOTR/version.h b/MirOTR/version.h index bdfa60a..04ace48 100644 --- a/MirOTR/version.h +++ b/MirOTR/version.h @@ -3,12 +3,12 @@ /* VERSION DEFINITIONS */ #define VER_MAJOR 0 -#define VER_MINOR 8 -#define VER_RELEASE 6 +#define VER_MINOR 9 +#define VER_RELEASE 1 #define VER_BUILD 1 #define __STRINGIZE(x) #x -#define VER_STRING "0.8.6.1" +#define VER_STRING "0.9.1.1" #ifdef _UNICODE #define SHORT_NAME_STRING "Miranda OTR" -- cgit v1.2.3