From 5a97f4b7c941dc704ab4dd2c370d19bb556c1ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Sch=C3=BCmann?= Date: Sat, 14 Mar 2015 19:58:15 +0000 Subject: MirOTR: part 3, file/folder structure change git-svn-id: http://svn.miranda-ng.org/main/trunk@12404 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- plugins/MirOTR/src/otr.cpp | 395 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 plugins/MirOTR/src/otr.cpp (limited to 'plugins/MirOTR/src/otr.cpp') 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 -- cgit v1.2.3