cosmetix
[dyskinesia.git] / src / chat_otr.cpp
blobba4a50a0e6b970e551b537c55beb5e1ba290a365
1 #ifdef USE_OTR
2 # define C_FUNCTION extern "C"
3 # define PROTO_NAME "psyc"
4 /*
5 # define dlogf(...)
6 */
7 ///////////////////////////////////////////////////////////////////////////////
8 # include "dlogf.h"
10 static char *strToC (const QString &s) {
11 //QTextCodec *tc = QTextCodec::codecForCStrings();
12 QTextCodec *tc = QTextCodec::codecForLocale();
13 QByteArray ba;
14 if (tc) ba = tc->fromUnicode(s); else ba = s.toLatin1();
15 char *res = (char *)malloc(ba.size()+8);
16 strcpy(res, ba.constData());
17 res[ba.size()] = '\0';
18 return res;
22 /* frees fname */
23 static FILE *qxfopen (char *fname, bool readOnly) {
24 QFile mQF(fname);
25 free(fname);
26 if (!mQF.open(readOnly ? QIODevice::ReadOnly : QIODevice::ReadWrite)) return 0;
27 int hh = dup(mQF.handle());
28 mQF.close();
29 FILE *res = fdopen(hh, readOnly ? "rb" : "r+b");
30 if (!res) close(hh);
31 return res;
36 char *ChatForm::getOTRKeyFile (void) {
37 QDir d;
38 QString otp(settingsDBPath(mAccName));
39 otp.append("otr/");
40 d.mkdir(otp);
41 otp.append("keys.otr");
42 return strToC(otp);
46 char *ChatForm::getOTRFingerFile (void) {
47 QDir d;
48 QString otp(settingsDBPath(mAccName));
49 otp.append("otr/");
50 d.mkdir(otp);
51 otp.append("fingers.otr");
52 return strToC(otp);
56 /* Return a pointer to a newly-allocated OTR query message, customized
57 * with our name. The caller should free() the result when he's done
58 * with it. */
59 static char *otrDefaultQueryMsg (const char *ourname, OtrlPolicy policy) {
60 char *msg = otrl_proto_default_query_msg(ourname, policy);
61 char *e = strchr(msg, '\n');
62 if (!e) return msg;
63 char *res = (char *)malloc(8192); //???
64 *e = '\0';
65 sprintf(res,
66 "%s\n%s has requested an Off-the-Record private conversation.\n"
67 "However, you do not have a plugin to support that.\n"
68 "See http://otr.cypherpunks.ca/ for more information.",
69 msg, ourname
71 free(msg);
72 return res;
76 ///////////////////////////////////////////////////////////////////////////////
77 // return the OTR policy for the given context
78 C_FUNCTION OtrlPolicy otrPolicyCB (void *opdata, ConnContext *context) {
79 //ChatForm *mForm = static_cast<ChatForm *>(opdata);
80 Q_UNUSED(opdata)
81 Q_UNUSED(context)
82 //!dlogf("otrPolicyCB()\n");
83 return OTRL_POLICY_OPPORTUNISTIC;
87 ///////////////////////////////////////////////////////////////////////////////
88 class GenKeyThread : public QThread {
89 public:
90 void run ();
92 ChatForm *mForm;
93 const char *accName;
94 const char *protoName;
96 bool mError;
98 void GenKeyThread::run () {
99 mError = true;
100 FILE *fl = qxfopen(mForm->getOTRKeyFile(), false);
101 if (fl) {
102 mError = otrl_privkey_generate_FILEp(mForm->mOTRState, fl, accName, protoName) != 0;
103 fclose(fl);
108 void ChatForm::genOTRKey (const char *accName, const char *protoName) {
109 GenKeyWin *gkw = new GenKeyWin(accName, this);
110 gkw->setModal(true);
111 gkw->show();
112 //!dlogf("dialog shown");
113 QEventLoop eloop;
114 GenKeyThread gtrd;
115 gtrd.mForm = this;
116 gtrd.accName = accName;
117 gtrd.protoName = protoName;
118 connect(&gtrd, SIGNAL(finished()), &eloop, SLOT(quit()));
119 gtrd.start();
120 //!dlogf("thread started");
121 //gtrd.wait();
122 eloop.exec();
123 //!dlogf("all done");
124 //TODO: check for error!
125 delete gkw;
129 // create a private key for the given accountname/protocol if desired
130 C_FUNCTION void otrCreateKeyCB (void *opdata, const char *accName, const char *protoName) {
131 Q_UNUSED(opdata)
132 Q_UNUSED(accName)
133 Q_UNUSED(protoName)
134 ChatForm *mForm = static_cast<ChatForm *>(opdata);
135 //!dlogf("otrCreateKeyCB(): acc:[%s]; proto:[%s]\n", accName, protoName);
136 //!dlogf(" otrl_privkey_generate()...");
137 mForm->genOTRKey(accName, protoName);
138 //otrl_privkey_generate(mOTRState, mForm->getOTRKeyFile(), accName, protoName);
139 //!dlogf("done\n");
143 ///////////////////////////////////////////////////////////////////////////////
144 /* Report whether you think the given user is online. Return 1 if
145 * you think he is, 0 if you think he isn't, -1 if you're not sure.
147 * If you return 1, messages such as heartbeats or other
148 * notifications may be sent to the user, which could result in "not
149 * logged in" errors if you're wrong. */
150 C_FUNCTION int otrIsLoggedInCB (void *opdata, const char *accName, const char *protoName, const char *recipient) {
151 Q_UNUSED(opdata)
152 Q_UNUSED(accName)
153 Q_UNUSED(protoName)
154 Q_UNUSED(recipient)
155 ChatForm *mForm = static_cast<ChatForm *>(opdata);
156 //!dlogf("otrIsLoggedInCB(): acc:[%s]; proto:[%s]; rcp:[%s]\n", accName, protoName, recipient);
157 if (!mForm || !recipient || !recipient[0]) return 0;
158 PsycContact *cc = mForm->findContact(recipient);
159 if (!cc || cc->isPlace()) return 0;
160 switch (cc->status()) {
161 case PsycProto::Internal:
162 case PsycProto::Offline:
163 return 0;
164 default: ;
166 //!dlogf(" online");
167 return 1;
171 ///////////////////////////////////////////////////////////////////////////////
172 // send the given IM to the given recipient from the given accountname/protocol
173 C_FUNCTION void otrSendMessageCB (void *opdata, const char *accName, const char *protoName,
174 const char *recipient, const char *message)
176 Q_UNUSED(opdata)
177 Q_UNUSED(accName)
178 Q_UNUSED(protoName)
179 Q_UNUSED(recipient)
180 Q_UNUSED(message)
181 ChatForm *mForm = static_cast<ChatForm *>(opdata);
182 //dlogf("otrSendMessageCB(): acc:[%s]; proto:[%s]; rcp:[%s]\nmsg:[%s]\n", accName, protoName, recipient, message);
183 //!dlogf("otrSendMessageCB(): acc:[%s]; proto:[%s]; rcp:[%s]\n", accName, protoName, recipient);
184 if (!mForm->proto()) return;
185 mForm->proto()->sendMessage(QString(recipient), QString::fromUtf8(message), QString());
189 ///////////////////////////////////////////////////////////////////////////////
190 // display a notification message for a particular accountname/protocol/username conversation
191 C_FUNCTION void otrNotifyCB (void *opdata, OtrlNotifyLevel level, const char *accName, const char *protoName,
192 const char *userName, const char *title, const char *primary, const char *secondary)
194 Q_UNUSED(opdata)
195 Q_UNUSED(level)
196 Q_UNUSED(accName)
197 Q_UNUSED(protoName)
198 Q_UNUSED(userName)
199 Q_UNUSED(title)
200 Q_UNUSED(primary)
201 Q_UNUSED(secondary)
202 //ChatForm *mForm = static_cast<ChatForm *>(opdata);
203 dlogf("otrNotifyCB(): acc:[%s]; proto:[%s]; user:[%s]\ntitle:[%s]\nprim:[%s]\nsec:[%s]\n",
204 accName, protoName, userName, title, primary, secondary);
205 switch (level) {
206 case OTRL_NOTIFY_ERROR:
207 dlogf(" OTRL_NOTIFY_ERROR\n");
208 break;
209 case OTRL_NOTIFY_WARNING:
210 dlogf(" OTRL_NOTIFY_WARNING\n");
211 break;
212 case OTRL_NOTIFY_INFO:
213 dlogf(" OTRL_NOTIFY_INFO\n");
214 break;
215 default: ;
220 ///////////////////////////////////////////////////////////////////////////////
221 /* Display an OTR control message for a particular accountname /
222 * protocol / username conversation. Return 0 if you are able to
223 * successfully display it. If you return non-0 (or if this
224 * function is NULL), the control message will be displayed inline,
225 * as a received message, or else by using the above notify()
226 * callback. */
227 C_FUNCTION int otrDisplayMessageCB (void *opdata, const char *accName, const char *protoName,
228 const char *userName, const char *msg)
230 Q_UNUSED(opdata)
231 Q_UNUSED(accName)
232 Q_UNUSED(protoName)
233 Q_UNUSED(userName)
234 Q_UNUSED(msg)
235 //ChatForm *mForm = static_cast<ChatForm *>(opdata);
236 dlogf("otrDisplayMessageCB(): acc:[%s]; proto:[%s]; user:[%s]\nmsg:[%s]\n",
237 accName, protoName, userName, msg);
238 return 0;
242 ///////////////////////////////////////////////////////////////////////////////
243 // When the list of ConnContexts changes (including a change in state), this is called so the UI can be updated
244 C_FUNCTION void otrUpdateContextListCB (void *opdata) {
245 Q_UNUSED(opdata)
246 ChatForm *mForm = static_cast<ChatForm *>(opdata);
247 //!dlogf("otrUpdateContextListCB()\n");
248 ConnContext *context = mForm->mOTRState->context_root;
249 while (context) {
250 ConnContext *next = context->next;
251 PsycContact *cc = mForm->findContact(context->username);
252 if (cc) {
253 cc->setOTRVerified(context->active_fingerprint && context->active_fingerprint->trust &&
254 (!strcmp(context->active_fingerprint->trust, "verified") || !strcmp(context->active_fingerprint->trust, "smp"))
256 if (cc->isOTRActive() != (context->msgstate == OTRL_MSGSTATE_ENCRYPTED)) {
257 cc->setOTRActive(context->msgstate == OTRL_MSGSTATE_ENCRYPTED);
258 mForm->redrawContact(cc);
261 context = next;
266 ///////////////////////////////////////////////////////////////////////////////
267 /* Return a newly-allocated string containing a human-friendly name
268 * for the given protocol id */
269 C_FUNCTION const char *otrProtoNameCB (void *opdata, const char *protoName) {
270 Q_UNUSED(opdata)
271 Q_UNUSED(protoName)
272 //ChatForm *mForm = static_cast<ChatForm *>(opdata);
273 //!dlogf("otrProtoNameCB(): proto:[%s]\n", protoName);
274 return protoName;
278 ///////////////////////////////////////////////////////////////////////////////
279 /* Deallocate a string allocated by protocol_name */
280 C_FUNCTION void otrProtoNameFreeCB (void *opdata, const char *protoName) {
281 Q_UNUSED(opdata)
282 Q_UNUSED(protoName)
283 //ChatForm *mForm = static_cast<ChatForm *>(opdata);
284 //!dlogf("otrProtoNameFreeCB(): proto:[%s]\n", protoName);
288 ///////////////////////////////////////////////////////////////////////////////
289 /* A new fingerprint for the given user has been received. */
290 C_FUNCTION void otrNewFingerCB (void *opdata, OtrlUserState us, const char *accName, const char *protoName,
291 const char *userName, unsigned char fingerprint[20])
293 Q_UNUSED(opdata)
294 Q_UNUSED(us)
295 Q_UNUSED(accName)
296 Q_UNUSED(protoName)
297 Q_UNUSED(userName)
298 Q_UNUSED(fingerprint)
299 ChatForm *mForm = static_cast<ChatForm *>(opdata);
300 //!dlogf("otrNewFingerCB(): acc:[%s]; proto:[%s]; user:[%s]\n", accName, protoName, userName);
301 ConnContext *context = otrl_context_find(us, userName, accName, protoName, TRUE, 0, NULL/*add_appdata*/, NULL/*opdata*/);
302 Fingerprint *fp = otrl_context_find_fingerprint(context, fingerprint, TRUE, 0);
303 if (fp) {
304 char hash[45];
305 otrl_privkey_hash_to_human(hash, fp->fingerprint);
306 //!dlogf(" hash: [%s]\n", hash);
307 QMessageBox::StandardButton qres =
308 QMessageBox::question(mForm, "Dyskinesia: new OTR fingerprint",
309 QString("New OTR fingerpring received from %1.\n%2\nDo you trust it?").arg(userName).arg(hash),
310 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
311 if (qres == QMessageBox::Yes) otrl_context_set_trust(fp, "verified");
312 else otrl_context_set_trust(fp, "unknown");
313 FILE *fl = qxfopen(mForm->getOTRFingerFile(), false);
314 if (fl) {
315 otrl_privkey_write_fingerprints_FILEp(mForm->mOTRState, fl);
316 fclose(fl);
322 ///////////////////////////////////////////////////////////////////////////////
323 /* The list of known fingerprints has changed. Write them to disk. */
324 C_FUNCTION void otrWriteFingersCB (void *opdata) {
325 Q_UNUSED(opdata)
326 ChatForm *mForm = static_cast<ChatForm *>(opdata);
327 //!dlogf("otrWriteFingersCB()\n");
328 FILE *fl = qxfopen(mForm->getOTRFingerFile(), false);
329 if (fl) {
330 otrl_privkey_write_fingerprints_FILEp(mForm->mOTRState, fl);
331 fclose(fl);
336 ///////////////////////////////////////////////////////////////////////////////
337 /* A ConnContext has entered a secure state. */
338 C_FUNCTION void otrGoneSecureCB (void *opdata, ConnContext *context) {
339 Q_UNUSED(opdata)
340 Q_UNUSED(context)
341 ChatForm *mForm = static_cast<ChatForm *>(opdata);
342 dlogf("otrGoneSecureCB()\n");
343 int trusted = (context->active_fingerprint && context->active_fingerprint->trust &&
344 (!strcmp(context->active_fingerprint->trust, "verified") || !strcmp(context->active_fingerprint->trust, "smp"))
346 PsycContact *cc = mForm->findContact(context->username);
347 //!k8:if (cc) cc->setOTRActive(true);
348 if (trusted) {
349 dlogf(" Beginning OTR encrypted session with [%s]", context->username);
350 if (cc) cc->setOTRVerified(true);
351 // opdata is hContact
352 //SetEncryptionStatus((HANDLE)opdata, true);
353 } else {
354 //!!CloseHandle((HANDLE)_beginthreadex(0, 0, verify_fp_thread, context, 0, 0));
355 dlogf(" Beginning OTR encrypted session with [%s] (NOT VERIFIED)", context->username);
356 if (cc) cc->setOTRVerified(false);
357 // opdata is hContact
358 //SetEncryptionStatus((HANDLE)opdata, true);
360 //!k8:if (cc) mForm->redrawContact(cc);
364 ///////////////////////////////////////////////////////////////////////////////
365 // a ConnContext has left a secure state
366 C_FUNCTION void otrGoneInsecureCB (void *opdata, ConnContext *context) {
367 Q_UNUSED(opdata)
368 Q_UNUSED(context)
369 ChatForm *mForm = static_cast<ChatForm *>(opdata);
370 //dlogf("otrGoneInsecureCB()\n");
371 dlogf(" OTR encrypted session with [%s] has ended", context->username);
373 PsycContact *cc = mForm->findContact(context->username);
374 if (cc && cc->isOTRActive()) {
375 cc->setOTRActive(false);
376 mForm->redrawContact(cc);
379 mForm->otrDisconnect(context->username);
380 // opdata is hContact
381 //SetEncryptionStatus((HANDLE)opdata, false);
385 ///////////////////////////////////////////////////////////////////////////////
386 /* We have completed an authentication, using the D-H keys we
387 * already knew. isReply indicates whether we initiated the AKE. */
388 C_FUNCTION void otrStillSecureCB (void *opdata, ConnContext *context, int isReply) {
389 Q_UNUSED(opdata)
390 Q_UNUSED(context)
391 Q_UNUSED(isReply)
392 //ChatForm *mForm = static_cast<ChatForm *>(opdata);
394 dlogf("otrStillSecureCB()\n");
395 if (isReply) {
396 dlogf(" OTR encrypted session with [%s] is being continued", context->username);
400 PsycContact *cc = mForm->findContact(context->username);
401 if (cc && !cc->isOTRActive()) {
402 cc->setOTRActive(true);
403 mForm->redrawContact(cc);
406 // opdata is hContact
407 //SetEncryptionStatus((HANDLE)opdata, true);
411 ///////////////////////////////////////////////////////////////////////////////
412 /* Log a message. The passed message will end in "\n". */
413 C_FUNCTION void otrLogCB (void *opdata, const char *message) {
414 //ChatForm *mForm = static_cast<ChatForm *>(opdata);
415 Q_UNUSED(opdata)
416 Q_UNUSED(message)
417 dlogf("otrLogCB: >>%s", message);
421 ///////////////////////////////////////////////////////////////////////////////
422 C_FUNCTION int otrMaxMessageSizeCB (void *opdata, ConnContext *context) {
423 //ChatForm *mForm = static_cast<ChatForm *>(opdata);
424 Q_UNUSED(opdata)
425 Q_UNUSED(context)
426 //dlogf("max_message_size()\n");
427 return 4096;
432 ///////////////////////////////////////////////////////////////////////////////
433 C_FUNCTION const char *otrAccountNameCB (void *opdata, const char *accName, const char *protoName) {
434 ChatForm *mForm = static_cast<ChatForm *>(opdata);
435 dlogf("account_name(): acc:[%s]; proto:[%s]\n", accName, protoName);
436 return ACC_NAME;
439 ///////////////////////////////////////////////////////////////////////////////
440 C_FUNCTION void otrAccountNameFreeCB(void *opdata, const char *accName) {
441 ChatForm *mForm = static_cast<ChatForm *>(opdata);
442 dlogf("account_name_free(): acc:[%s]\n", accName);
447 static OtrlMessageAppOps ops = {
448 otrPolicyCB,
449 otrCreateKeyCB,
450 otrIsLoggedInCB,
451 otrSendMessageCB,
452 otrNotifyCB,
453 otrDisplayMessageCB,
454 otrUpdateContextListCB,
455 otrProtoNameCB,
456 otrProtoNameFreeCB,
457 otrNewFingerCB,
458 otrWriteFingersCB,
459 otrGoneSecureCB,
460 otrGoneInsecureCB,
461 otrStillSecureCB,
462 otrLogCB,
463 otrMaxMessageSizeCB,
464 NULL, /* account_name */
465 NULL /* account_name_free */
467 otrAccountNameCB,
468 otrAccountNameFreeCB
473 ///////////////////////////////////////////////////////////////////////////////
474 void ChatForm::otrSendMessage (const QString &auni, const QString &txt, const QString &act) {
475 QString uni(auni.toLower());
476 PsycContact *cc = findContact(uni);
477 if (!cc || cc->isPlace() || !cc->isOTRActive() || txt.startsWith("?OTR")) {
478 // internal OTR message or room message
479 mProto->sendMessage(uni, txt, act);
480 } else {
481 // not OTR message, encrypt it
482 gcry_error_t err;
483 char *newMsg = 0;
484 QByteArray me(mProto->uni().toLower().toUtf8());
485 QByteArray to(uni.toUtf8());
486 QByteArray msg(txt.toUtf8());
487 err = otrl_message_sending(mOTRState, &ops, (void *)this, me, PROTO_NAME, to, msg, 0, &newMsg, 0, 0);
488 if (err) {
489 dlogf("failure to encrypt message!\n");
490 return;
492 if (!newMsg) {
493 dlogf("WTF?! an empty OTR resulting message!\n");
494 mProto->sendMessage(uni, txt, act);
495 return;
497 //QByteArray mx(newMsg);
498 //mProto->sendMessage(uni, mx, act);
499 ConnContext *context = otrl_context_find(mOTRState, to, me, PROTO_NAME, FALSE, 0, 0, 0);
500 if (context) {
501 char *frag = NULL;
502 err = otrl_message_fragment_and_send(&ops, (void *)this, context, newMsg, OTRL_FRAGMENT_SEND_ALL, &frag);
503 if (frag) free(frag);
504 } else {
505 dlogf("WTF?! no context found for otrl_message_fragment_and_send()\n");
507 otrl_message_free(newMsg);
512 ///////////////////////////////////////////////////////////////////////////////
513 QString ChatForm::otrGotMessage (const QString &auni, const QString &txt) {
514 int isInternal;
515 char *newMsg = NULL;
516 OtrlTLV *tlvs = 0;
517 OtrlTLV *tlv = 0;
519 QString uni(auni.toLower());
520 QByteArray me(mProto->uni().toLower().toUtf8());
521 QByteArray sender(uni.toUtf8());
522 QByteArray msg(txt.toUtf8());
523 isInternal = otrl_message_receiving(mOTRState, &ops, (void *)this, me, PROTO_NAME, sender, msg,
524 &newMsg, &tlvs, NULL, NULL);
525 ConnContext *context = otrl_context_find(mOTRState, sender, me, PROTO_NAME, FALSE, 0, 0, 0);
527 bool disconnected = false;
528 tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED);
529 if (tlv) disconnected = true;
531 if (!disconnected) {
533 if (context->smstate->sm_prog_state == OTRL_SMP_PROG_CHEATED) {
534 otrg_plugin_abort_smp(context);
535 otrg_dialog_update_smp(context, 0.0);
536 context->smstate->nextExpected = OTRL_SMP_EXPECT1;
537 context->smstate->sm_prog_state = OTRL_SMP_PROG_OK;
538 goto skip_smp;
541 if (context) {
542 NextExpectedSMP nextMsg = context->smstate->nextExpected;
543 bool abortIt = false;
544 QString smpQuestion, smpSecret;
545 tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1Q);
546 if (tlv) {
547 // SMP query with question
548 if (nextMsg != OTRL_SMP_EXPECT1) {
549 // error
550 dlogf("OTR: SMP error! aborted!\n");
551 abortIt = true;
552 } else {
553 // question should be here
554 const char *question = (const char *)tlv->data;
555 const char *eoq = (const char *)memchr(question, '\0', tlv->len);
556 if (eoq) {
557 if (question[0]) smpQuestion = QString("secret question:\n%1").arg(QString::fromUtf8(question));
558 else smpQuestion = tr("input secret:");
559 } else abortIt = true;
562 tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
563 if (tlv) {
564 // SMP query without question
565 if (nextMsg != OTRL_SMP_EXPECT1) {
566 // error
567 dlogf("OTR: SMP error! aborted!\n");
568 abortIt = true;
569 } else {
570 smpQuestion = tr("input secret:");
574 if (!abortIt && !smpQuestion.isEmpty()) {
575 QString reply;
576 bool ok = false;
577 smpQuestion = QString("OTR SMP query from %1\n%2").arg(auni).arg(smpQuestion);
578 reply = QInputDialog::getText(this, "OTR SMP query", smpQuestion, QLineEdit::Normal, QString(), &ok);
579 if (!ok || reply.isEmpty()) abortIt = true;
580 else {
581 smpSecret = reply;
582 QByteArray ba(smpSecret.toUtf8());
583 otrl_message_respond_smp(mOTRState, &ops, (void *)this, context, (const unsigned char *)ba.constData(), ba.size());
587 tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
588 if (tlv) {
589 if (nextMsg != OTRL_SMP_EXPECT2) abortIt = true;
590 else {
591 // If we received TLV2, we will send TLV3 and expect TLV4
592 context->smstate->nextExpected = OTRL_SMP_EXPECT4;
596 tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
597 if (tlv) {
598 // SMP initiated by the other side
599 if (nextMsg != OTRL_SMP_EXPECT3) abortIt = true;
600 else {
601 // If we received TLV3, we will send TLV4
602 // We will not expect more messages, so prepare for next SMP
603 context->smstate->nextExpected = OTRL_SMP_EXPECT1;
604 // Report result to user
605 dlogf("1: OTR SMP: [%s]\n", context->active_fingerprint->trust ? context->active_fingerprint->trust : "(null)");
606 otrSetTrust(auni, context->active_fingerprint->trust &&
607 (!strcmp(context->active_fingerprint->trust, "verified") || !strcmp(context->active_fingerprint->trust, "smp"))
610 if (context->active_fingerprint->trust && context->active_fingerprint->trust[0]) otrSetTrust(auni, true);
611 else otrSetTrust(auni, false);
616 tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
617 if (tlv) {
618 // SMP initiated by us
619 if (nextMsg != OTRL_SMP_EXPECT4) abortIt = true;
620 else {
621 // We will not expect more messages, so prepare for next SMP
622 context->smstate->nextExpected = OTRL_SMP_EXPECT1;
623 // Report result to user
624 dlogf("0: OTR SMP: [%s]\n", context->active_fingerprint->trust ? context->active_fingerprint->trust : "(null)");
625 if (context->active_fingerprint->trust && context->active_fingerprint->trust[0]) otrSetTrust(auni, true);
626 else otrSetTrust(auni, false);
630 tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
631 if (tlv) {
632 // The message we are waiting for will not arrive, so reset and prepare for the next SMP
633 otrl_message_abort_smp(mOTRState, &ops, (void *)this, context);
634 context->smstate->nextExpected = OTRL_SMP_EXPECT1;
637 if (abortIt) otrl_message_abort_smp(mOTRState, &ops, (void *)this, context);
641 if (tlvs) otrl_tlv_free(tlvs);
643 if (isInternal) {
644 dlogf("internal message\n");
645 if (newMsg) otrl_message_free(newMsg);
646 if (disconnected) {
647 dlogf("[%s] has terminated the OTR session\n", me.constData());
648 otrDisconnect(uni);
650 return QString();
653 // it may not be encrypted however (e.g. tagged plaintext with tags stripped)
654 //!if (context && context->msgstate == OTRL_MSGSTATE_PLAINTEXT) dlogf("PLAIN TEXT!\n");
656 QString res;
657 if (!newMsg) {
658 QByteArray ba(txt.toUtf8());
659 dlogf("plain message: [%s]\n", ba.constData());
660 res = txt;
661 } else {
662 res = QString::fromUtf8(newMsg);
663 dlogf("decrypted message: [%s]\n", newMsg);
666 if (newMsg) otrl_message_free(newMsg);
668 if (disconnected) {
669 dlogf("[%s] has terminated the OTR session\n", me.constData());
670 otrDisconnect(uni);
673 return res;
675 ///////////////////////////////////////////////////////////////////////////////
676 #endif
679 const QString ChatForm::otrGetFinger (const QString &aUNI) {
680 Q_UNUSED(aUNI)
681 #ifdef USE_OTR
682 PsycContact *cc = findContact(aUNI);
683 if (!cc || cc->isPlace()) return QString();
684 QByteArray me(mProto->uni().toLower().toUtf8());
685 QByteArray usr(aUNI.toLower().toUtf8());
686 ConnContext *context = otrl_context_find(mOTRState, usr, me, PROTO_NAME, false, 0, NULL, NULL);
687 if (context && context->active_fingerprint) {
688 char hash[45];
689 otrl_privkey_hash_to_human(hash, context->active_fingerprint->fingerprint);
690 return QString(hash);
692 #endif
693 return QString();
697 void ChatForm::otrSetTrust (const QString &aUNI, bool tflag) {
698 Q_UNUSED(aUNI)
699 Q_UNUSED(tflag)
700 #ifdef USE_OTR
701 PsycContact *cc = findContact(aUNI);
702 if (!cc || cc->isPlace()) return;
703 QByteArray me(mProto->uni().toLower().toUtf8());
704 QByteArray usr(aUNI.toLower().toUtf8());
705 ConnContext *context = otrl_context_find(mOTRState, usr, me, PROTO_NAME, false, 0, NULL, NULL);
706 if (context && context->active_fingerprint) {
707 otrl_context_set_trust(context->active_fingerprint, tflag ? "verified" : "unknown");
709 if (cc->isOTRVerified() != tflag) {
710 cc->setOTRVerified(tflag);
711 redrawContact(cc);
713 #endif
717 void ChatForm::otrForget (const QString &aUNI) {
718 Q_UNUSED(aUNI)
719 #ifdef USE_OTR
720 PsycContact *cc = findContact(aUNI);
721 if (!cc || cc->isPlace()) return;
723 QByteArray me(mProto->uni().toLower().toUtf8());
724 QByteArray usr(aUNI.toLower().toUtf8());
725 ConnContext *context = otrl_context_find(mOTRState, usr, me, PROTO_NAME, false, 0, NULL, NULL);
726 if (context) {
727 otrl_context_forget(context);
728 cc->setOTRVerified(false);
729 redrawContact(cc);
731 #endif
735 void ChatForm::otrInitiateSMP (const QString &aUNI, const QString &secret, const QString &question) {
736 Q_UNUSED(aUNI)
737 Q_UNUSED(secret)
738 Q_UNUSED(question)
739 #ifdef USE_OTR
740 PsycContact *cc = findContact(aUNI);
741 if (!cc || cc->isPlace() || !cc->isOTRActive()) return;
742 //cc->setOTRVerified(false);
743 //redrawContact(cc);
744 QByteArray me(mProto->uni().toLower().toUtf8());
745 QByteArray user(aUNI.toLower().toUtf8());
746 ConnContext *context = otrl_context_find(mOTRState, user, me, PROTO_NAME, FALSE, 0, 0, 0);
747 if (context && !secret.isEmpty()) {
748 otrSetTrust(aUNI, false);
749 QByteArray ba(secret.toUtf8());
750 if (!question.isEmpty()) {
751 QByteArray ba1(question.toUtf8());
752 otrl_message_initiate_smp_q(mOTRState, &ops, (void *)this, context, ba1.constData(), (const unsigned char *)ba.constData(), ba.size());
753 } else {
754 otrl_message_initiate_smp(mOTRState, &ops, (void *)this, context, (const unsigned char *)ba.constData(), ba.size());
757 #endif
761 void ChatForm::deactivateAllOTR () {
762 foreach (PsycContact *cc, mContactList) {
763 cc->setOTRVerified(false);
764 if (cc->isOTRActive()) {
765 cc->setOTRActive(false);
766 redrawContact(cc);
772 void ChatForm::disconnectAllOTR () {
773 deactivateAllOTR();
774 #ifdef USE_OTR
775 if (mOTRState) {
776 ConnContext *context = mOTRState->context_root;
777 while (context) {
778 ConnContext *next = context->next;
779 if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED && context->protocol_version > 1) {
780 otrDisconnect(context->username);
781 //otrl_message_disconnect(mOTRState, &ops, (void *)this, context->accountname, context->protocol, context->username);
783 context = next;
786 #endif
790 void ChatForm::otrDisconnect (const QString &uni) {
791 #ifdef USE_OTR
792 QByteArray user(uni.toLower().toUtf8());
793 QByteArray me(mProto->uni().toLower().toUtf8());
794 dlogf(" sending OTR disconnect to [%s]", user.constData());
795 otrl_message_disconnect(mOTRState, &ops, (void *)this, me, PROTO_NAME, user);
796 #else
797 Q_UNUSED(uni)
798 #endif
800 PsycContact *cc = findContact(uni);
801 if (cc && cc->isOTRActive()) {
802 cc->setOTRActive(false);
803 redrawContact(cc);
809 void ChatForm::otrConnect (const QString &uni) {
810 #ifdef USE_OTR
811 QByteArray me(mProto->uni().toLower().toUtf8());
812 QByteArray to(uni.toLower().toUtf8());
813 char *msg = otrDefaultQueryMsg(me.constData(), OTRL_POLICY_OPPORTUNISTIC);
814 otrSendMessageCB((void *)this, me.constData(), PROTO_NAME, to.constData(), msg ? msg : "?OTRv2?");
815 if (msg) free(msg);
816 #else
817 Q_UNUSED(uni)
818 #endif