unauthorizedAccept option
[dyskinesia.git] / src / chat_otr.cpp
blobbf055f8c712ed904768cf20d99629fc62c31e352
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 char *ChatForm::getOTRInsTagFile (void) {
57 QDir d;
58 QString otp(settingsDBPath(mAccName));
59 otp.append("otr/");
60 d.mkdir(otp);
61 otp.append("instags.otr");
62 return strToC(otp);
66 /* Return a pointer to a newly-allocated OTR query message, customized
67 * with our name. The caller should free() the result when he's done
68 * with it. */
69 static char *otrDefaultQueryMsg (const char *ourname, OtrlPolicy policy) {
70 char *msg = otrl_proto_default_query_msg(ourname, policy);
71 char *e = strchr(msg, '\n');
72 if (!e) return msg;
73 char *res = (char *)malloc(8192); //???
74 *e = '\0';
75 sprintf(res,
76 "%s\n%s has requested an Off-the-Record private conversation.\n"
77 "However, you do not have a plugin to support that.\n"
78 "See http://otr.cypherpunks.ca/ for more information.",
79 msg, ourname
81 free(msg);
82 dlogf("otrDefaultQueryMsg:\n%s\n====", res);
83 return res;
87 ///////////////////////////////////////////////////////////////////////////////
88 #define GET_FORM ChatForm *mForm = static_cast<ChatForm *>(opdata)
91 // return the OTR policy for the given context
92 C_FUNCTION OtrlPolicy otrPolicyCB (void *opdata, ConnContext *context) {
93 Q_UNUSED(opdata)
94 Q_UNUSED(context)
95 //!dlogf("otrPolicyCB()\n");
96 return OTRL_POLICY_DEFAULT;
100 ///////////////////////////////////////////////////////////////////////////////
101 class GenKeyThread : public QThread {
102 public:
103 void run ();
105 ChatForm *mForm;
106 const char *accName;
107 //const char *protoName;
108 void *keyp;
110 bool mError;
112 void GenKeyThread::run () {
114 mError = true;
115 FILE *fl = qxfopen(mForm->getOTRKeyFile(), false);
116 if (fl) {
117 mError = otrl_privkey_generate_FILEp(mForm->mOTRState, fl, accName, protoName) != 0;
118 fclose(fl);
121 if (otrl_privkey_generate_calculate(keyp)) {
122 dlogf("ERROR: can't generate new private key for '%s'!\n", accName);
123 mError = true;
124 } else {
125 mError = false;
130 void ChatForm::genOTRKey (const char *accName, const char *protoName) {
131 void *keyp;
132 if (otrl_privkey_generate_start(mOTRState, accName, protoName, &keyp)) {
133 dlogf("ERROR: can't generate new private key for '%s'!\n", accName);
134 return;
136 GenKeyWin *gkw = new GenKeyWin(accName, this);
137 gkw->setModal(true);
138 gkw->show();
139 //!dlogf("dialog shown");
140 QEventLoop eloop;
141 GenKeyThread gtrd;
142 gtrd.mForm = this;
143 gtrd.accName = accName;
144 //gtrd.protoName = protoName;
145 gtrd.keyp = keyp;
146 connect(&gtrd, SIGNAL(finished()), &eloop, SLOT(quit()));
147 gtrd.start();
148 //!dlogf("thread started");
149 //gtrd.wait();
150 eloop.exec();
151 //!dlogf("all done");
152 //TODO: check for error!
153 if (!gtrd.mError) {
154 FILE *fl = qxfopen(getOTRKeyFile(), false);
155 if (fl) {
156 if (otrl_privkey_generate_finish_FILEp(mOTRState, keyp, fl)) {
157 dlogf("ERROR: can't generate new private key for '%s'!\n", accName);
159 fclose(fl);
162 delete gkw;
166 // create a private key for the given accountname/protocol if desired
167 C_FUNCTION void otrCreateKeyCB (void *opdata, const char *accName, const char *protoName) {
168 Q_UNUSED(opdata)
169 Q_UNUSED(accName)
170 Q_UNUSED(protoName)
171 GET_FORM;
172 dlogf("otrCreateKeyCB(): acc:[%s]; proto:[%s]\n", accName, protoName);
173 //!dlogf(" otrl_privkey_generate()...");
174 mForm->genOTRKey(accName, protoName);
175 //otrl_privkey_generate(mOTRState, mForm->getOTRKeyFile(), accName, protoName);
176 //!dlogf("done\n");
180 ///////////////////////////////////////////////////////////////////////////////
181 /* Report whether you think the given user is online. Return 1 if
182 * you think he is, 0 if you think he isn't, -1 if you're not sure.
184 * If you return 1, messages such as heartbeats or other
185 * notifications may be sent to the user, which could result in "not
186 * logged in" errors if you're wrong. */
187 C_FUNCTION int otrIsLoggedInCB (void *opdata, const char *accName, const char *protoName, const char *recipient) {
188 Q_UNUSED(opdata)
189 Q_UNUSED(accName)
190 Q_UNUSED(protoName)
191 Q_UNUSED(recipient)
192 GET_FORM;
193 //!dlogf("otrIsLoggedInCB(): acc:[%s]; proto:[%s]; rcp:[%s]\n", accName, protoName, recipient);
194 if (!mForm || !recipient || !recipient[0]) return 0;
195 PsycContact *cc = mForm->findContact(recipient);
196 if (!cc || cc->isPlace()) return 0;
197 switch (cc->status()) {
198 case PsycProto::Internal:
199 case PsycProto::Offline:
200 return 0;
201 default: ;
203 //!dlogf(" online");
204 return 1;
208 ///////////////////////////////////////////////////////////////////////////////
209 // send the given IM to the given recipient from the given accountname/protocol
210 C_FUNCTION void otrSendMessageCB (void *opdata, const char *accName, const char *protoName,
211 const char *recipient, const char *message)
213 Q_UNUSED(opdata)
214 Q_UNUSED(accName)
215 Q_UNUSED(protoName)
216 Q_UNUSED(recipient)
217 Q_UNUSED(message)
218 GET_FORM;
219 dlogf("otrSendMessageCB(): acc:[%s]; proto:[%s]; rcp:[%s]\nmsg:[%s]\n=========", accName, protoName, recipient, message);
220 //!dlogf("otrSendMessageCB(): acc:[%s]; proto:[%s]; rcp:[%s]\n", accName, protoName, recipient);
221 if (!mForm->proto()) return;
222 mForm->proto()->sendMessage(QString(recipient), QString::fromUtf8(message), QString());
226 ///////////////////////////////////////////////////////////////////////////////
227 // When the list of ConnContexts changes (including a change in state), this is called so the UI can be updated
228 C_FUNCTION void otrUpdateContextListCB (void *opdata) {
229 Q_UNUSED(opdata)
230 ChatForm *mForm = static_cast<ChatForm *>(opdata);
231 //!dlogf("otrUpdateContextListCB()\n");
232 ConnContext *context = mForm->mOTRState->context_root;
233 while (context) {
234 ConnContext *next = context->next;
235 PsycContact *cc = mForm->findContact(context->username);
236 if (cc) {
237 cc->setOTRVerified(context->active_fingerprint && context->active_fingerprint->trust &&
238 (!strcmp(context->active_fingerprint->trust, "verified") || !strcmp(context->active_fingerprint->trust, "smp"))
240 if (cc->isOTRActive() != (context->msgstate == OTRL_MSGSTATE_ENCRYPTED)) {
241 cc->setOTRActive(context->msgstate == OTRL_MSGSTATE_ENCRYPTED);
242 mForm->redrawContact(cc);
245 context = next;
250 ///////////////////////////////////////////////////////////////////////////////
251 /* Return a newly-allocated string containing a human-friendly name
252 * for the given protocol id */
254 C_FUNCTION const char *otrProtoNameCB (void *opdata, const char *protoName) {
255 Q_UNUSED(opdata)
256 Q_UNUSED(protoName)
257 //ChatForm *mForm = static_cast<ChatForm *>(opdata);
258 //!dlogf("otrProtoNameCB(): proto:[%s]\n", protoName);
259 return protoName;
264 /* Deallocate a string allocated by protocol_name */
266 C_FUNCTION void otrProtoNameFreeCB (void *opdata, const char *protoName) {
267 Q_UNUSED(opdata)
268 Q_UNUSED(protoName)
269 //ChatForm *mForm = static_cast<ChatForm *>(opdata);
270 //!dlogf("otrProtoNameFreeCB(): proto:[%s]\n", protoName);
275 ///////////////////////////////////////////////////////////////////////////////
276 /* A new fingerprint for the given user has been received. */
277 C_FUNCTION void otrNewFingerCB (void *opdata, OtrlUserState us, const char *accName, const char *protoName,
278 const char *userName, unsigned char fingerprint[20])
280 Q_UNUSED(opdata)
281 Q_UNUSED(us)
282 Q_UNUSED(accName)
283 Q_UNUSED(protoName)
284 Q_UNUSED(userName)
285 Q_UNUSED(fingerprint)
286 ChatForm *mForm = static_cast<ChatForm *>(opdata);
287 //!dlogf("otrNewFingerCB(): acc:[%s]; proto:[%s]; user:[%s]\n", accName, protoName, userName);
288 ConnContext *context = otrl_context_find(us, userName, accName, protoName, OTRL_INSTAG_BEST, TRUE, 0, NULL/*add_appdata*/, NULL/*opdata*/);
289 char hash[OTRL_PRIVKEY_FPRINT_HUMAN_LEN];
290 // don't add unknown fingerprint
291 Fingerprint *fp = otrl_context_find_fingerprint(context, fingerprint, 0/*FALSE*/, NULL);
292 if (fp) {
293 otrl_privkey_hash_to_human(hash, fp->fingerprint);
294 dlogf(" known %strusted fingerprint: %s\n", (otrl_context_is_fingerprint_trusted(fp) ? "" : "un"), hash);
295 // trust it (user query?)
296 otrl_context_set_trust(fp, "verified");
297 } else {
298 otrl_privkey_hash_to_human(hash, fingerprint);
299 dlogf(" new fingerprint: %s\n", hash);
300 QMessageBox::StandardButton qres =
301 QMessageBox::question(mForm, "Dyskinesia: new OTR fingerprint",
302 QString("New OTR fingerpring received from %1.\n%2\nDo you trust it?").arg(userName).arg(hash),
303 QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes);
304 if (qres == QMessageBox::Yes) {
305 otrl_context_set_trust(fp, "verified");
306 } else {
307 otrl_context_set_trust(fp, "unknown");
313 ///////////////////////////////////////////////////////////////////////////////
314 /* The list of known fingerprints has changed. Write them to disk. */
315 C_FUNCTION void otrWriteFingersCB (void *opdata) {
316 GET_FORM;
317 //!dlogf("otrWriteFingersCB()\n");
318 FILE *fl = qxfopen(mForm->getOTRFingerFile(), false);
319 if (fl) {
320 otrl_privkey_write_fingerprints_FILEp(mForm->mOTRState, fl);
321 fclose(fl);
326 ///////////////////////////////////////////////////////////////////////////////
327 /* A ConnContext has entered a secure state. */
328 C_FUNCTION void otrGoneSecureCB (void *opdata, ConnContext *context) {
329 Q_UNUSED(opdata)
330 Q_UNUSED(context)
331 GET_FORM;
332 dlogf("otrGoneSecureCB()\n");
333 bool trusted = (context->active_fingerprint && context->active_fingerprint->trust &&
334 (!strcmp(context->active_fingerprint->trust, "verified") || !strcmp(context->active_fingerprint->trust, "smp"))
336 PsycContact *cc = mForm->findContact(context->username);
337 //!k8:if (cc) cc->setOTRActive(true);
338 if (trusted) {
339 dlogf(" Beginning OTR encrypted session with [%s]", context->username);
340 if (cc) cc->setOTRVerified(true);
341 // opdata is hContact
342 //SetEncryptionStatus((HANDLE)opdata, true);
343 } else {
344 //!!CloseHandle((HANDLE)_beginthreadex(0, 0, verify_fp_thread, context, 0, 0));
345 dlogf(" Beginning OTR encrypted session with [%s] (NOT VERIFIED)", context->username);
346 if (cc) cc->setOTRVerified(false);
347 // opdata is hContact
348 //SetEncryptionStatus((HANDLE)opdata, true);
350 //!k8:if (cc) mForm->redrawContact(cc);
354 ///////////////////////////////////////////////////////////////////////////////
355 // a ConnContext has left a secure state
356 C_FUNCTION void otrGoneInsecureCB (void *opdata, ConnContext *context) {
357 Q_UNUSED(opdata)
358 Q_UNUSED(context)
359 ChatForm *mForm = static_cast<ChatForm *>(opdata);
360 //dlogf("otrGoneInsecureCB()\n");
361 dlogf(" OTR encrypted session with [%s] has ended", context->username);
363 PsycContact *cc = mForm->findContact(context->username);
364 if (cc && cc->isOTRActive()) {
365 cc->setOTRActive(false);
366 mForm->redrawContact(cc);
369 mForm->otrDisconnect(context->username);
370 // opdata is hContact
371 //SetEncryptionStatus((HANDLE)opdata, false);
375 ///////////////////////////////////////////////////////////////////////////////
376 /* We have completed an authentication, using the D-H keys we
377 * already knew. isReply indicates whether we initiated the AKE. */
378 C_FUNCTION void otrStillSecureCB (void *opdata, ConnContext *context, int isReply) {
379 Q_UNUSED(opdata)
380 Q_UNUSED(context)
381 Q_UNUSED(isReply)
382 //ChatForm *mForm = static_cast<ChatForm *>(opdata);
384 dlogf("otrStillSecureCB()\n");
385 if (isReply) {
386 dlogf(" OTR encrypted session with [%s] is being continued", context->username);
390 PsycContact *cc = mForm->findContact(context->username);
391 if (cc && !cc->isOTRActive()) {
392 cc->setOTRActive(true);
393 mForm->redrawContact(cc);
396 // opdata is hContact
397 //SetEncryptionStatus((HANDLE)opdata, true);
401 ///////////////////////////////////////////////////////////////////////////////
402 C_FUNCTION int otrMaxMessageSizeCB (void *opdata, ConnContext *context) {
403 //ChatForm *mForm = static_cast<ChatForm *>(opdata);
404 Q_UNUSED(opdata)
405 Q_UNUSED(context)
406 //dlogf("max_message_size()\n");
407 return 65530;
412 ///////////////////////////////////////////////////////////////////////////////
413 C_FUNCTION const char *otrAccountNameCB (void *opdata, const char *accName, const char *protoName) {
414 ChatForm *mForm = static_cast<ChatForm *>(opdata);
415 dlogf("account_name(): acc:[%s]; proto:[%s]\n", accName, protoName);
416 return ACC_NAME;
419 ///////////////////////////////////////////////////////////////////////////////
420 C_FUNCTION void otrAccountNameFreeCB(void *opdata, const char *accName) {
421 ChatForm *mForm = static_cast<ChatForm *>(opdata);
422 dlogf("account_name_free(): acc:[%s]\n", accName);
427 ///////////////////////////////////////////////////////////////////////////////
428 C_FUNCTION const char *otrErrorMessageCB (void *opdata, ConnContext *context, OtrlErrorCode err_code) {
429 Q_UNUSED(opdata)
430 Q_UNUSED(context)
431 Q_UNUSED(err_code)
432 switch (err_code) {
433 case OTRL_ERRCODE_ENCRYPTION_ERROR: return "ENCRYPTION_ERROR";
434 case OTRL_ERRCODE_MSG_NOT_IN_PRIVATE: return "MSG_NOT_IN_PRIVATE";
435 case OTRL_ERRCODE_MSG_UNREADABLE: return "MSG_UNREADABLE";
436 case OTRL_ERRCODE_MSG_MALFORMED: return "MSG_MALFORMED";
437 default: return "UNKNOWN";
442 C_FUNCTION void otrErrorMessageFreeCB (void *opdata, const char *err_msg) {
443 Q_UNUSED(opdata)
444 Q_UNUSED(err_msg)
448 ///////////////////////////////////////////////////////////////////////////////
449 C_FUNCTION void otrHandleSMPEvent (void *opdata, OtrlSMPEvent smp_event,
450 ConnContext *context, unsigned short progress_percent, char *question);
453 C_FUNCTION void otrTimerControlCB (void *opdata, unsigned int interval) {
454 dlogf("otrTimerControlCB: interval=%u", interval);
455 ChatForm *frm = (ChatForm *)opdata;
456 frm->restartOTRTimer(interval);
460 ///////////////////////////////////////////////////////////////////////////////
461 C_FUNCTION void otrHandleMsgEvent (void *opdata, OtrlMessageEvent msg_event, ConnContext *context,
462 const char *message, gcry_error_t err)
464 Q_UNUSED(opdata)
465 Q_UNUSED(msg_event)
466 Q_UNUSED(context)
467 Q_UNUSED(message)
468 Q_UNUSED(err)
469 switch (msg_event) {
470 case OTRL_MSGEVENT_NONE:
471 dlogf("OTRL_MSGEVENT_NONE\nNo event");
472 break;
473 case OTRL_MSGEVENT_ENCRYPTION_REQUIRED:
474 dlogf("OTRL_MSGEVENT_ENCRYPTION_REQUIRED\nOur policy requires encryption but we are trying to send an unencrypted message out.");
475 break;
476 case OTRL_MSGEVENT_ENCRYPTION_ERROR:
477 dlogf("OTRL_MSGEVENT_ENCRYPTION_ERROR\nAn error occured while encrypting a message and the message was not sent.");
478 break;
479 case OTRL_MSGEVENT_CONNECTION_ENDED:
480 dlogf("OTRL_MSGEVENT_CONNECTION_ENDED\nMessage has not been sent because our buddy has ended the private conversation. We should either close the connection, or refresh it.");
481 break;
482 case OTRL_MSGEVENT_SETUP_ERROR:
483 dlogf("OTRL_MSGEVENT_SETUP_ERROR\nA private conversation could not be set up. A gcry_error_t will be passed.");
484 break;
485 case OTRL_MSGEVENT_MSG_REFLECTED:
486 dlogf("OTRL_MSGEVENT_MSG_REFLECTED\nReceived our own OTR messages.");
487 break;
488 case OTRL_MSGEVENT_MSG_RESENT:
489 dlogf("OTRL_MSGEVENT_MSG_RESENT\nThe previous message was resent.");
490 break;
491 case OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE:
492 dlogf("OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE\nReceived an encrypted message but cannot read it because no private connection is established yet.");
493 break;
494 case OTRL_MSGEVENT_RCVDMSG_UNREADABLE:
495 dlogf("OTRL_MSGEVENT_RCVDMSG_UNREADABLE\nCannot read the received message.");
496 break;
497 case OTRL_MSGEVENT_RCVDMSG_MALFORMED:
498 dlogf("OTRL_MSGEVENT_RCVDMSG_MALFORMED\nThe message received contains malformed data.");
499 break;
500 case OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD:
501 dlogf("OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD\nReceived a heartbeat.");
502 break;
503 case OTRL_MSGEVENT_LOG_HEARTBEAT_SENT:
504 dlogf("OTRL_MSGEVENT_LOG_HEARTBEAT_SENT\nSent a heartbeat.");
505 break;
506 case OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR:
507 dlogf("OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR\nReceived a general OTR error. The argument 'message' will also be passed and it will contain the OTR error message."
508 "\nmessage=%s\n====", message);
509 break;
510 case OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED:
511 dlogf("OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED\nReceived an unencrypted message. The argument 'message' will also be passed and it will contain the plaintext message."
512 "\nmessage=%s\n====", message);
513 break;
514 case OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED:
515 dlogf("OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED\nCannot recognize the type of OTR message received.");
516 break;
517 case OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE:
518 dlogf("OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE\nReceived and discarded a message intended for another instance.");
519 break;
524 ////////////////////////////////////////////////////////////////////////////////
525 C_FUNCTION void otrCreateInsTagCB (void *opdata, const char *accountname, const char *protocol) {
526 dlogf("otrCreateInsTagCB: acc=[%s]; proto=[%s]\n", accountname, protocol);
527 GET_FORM;
528 FILE *fl = qxfopen(mForm->getOTRInsTagFile(), false);
529 if (fl) {
530 if (otrl_instag_generate_FILEp(mForm->mOTRState, fl, accountname, protocol)) {
531 dlogf("ERROR: can't generate new private key for '%s'!\n", accountname);
533 fclose(fl);
538 ///////////////////////////////////////////////////////////////////////////////
539 static OtrlMessageAppOps ops = {
540 otrPolicyCB, /* Return the OTR policy for the given context. */
541 otrCreateKeyCB, /* Create a private key for the given accountname/protocol if desired. */
542 /* Report whether you think the given user is online. Return 1 if
543 * you think he is, 0 if you think he isn't, -1 if you're not sure.
545 * If you return 1, messages such as heartbeats or other
546 * notifications may be sent to the user, which could result in "not
547 * logged in" errors if you're wrong. */
548 otrIsLoggedInCB,
549 /* Send the given IM to the given recipient from the given
550 * accountname/protocol. */
551 otrSendMessageCB,
552 /* When the list of ConnContexts changes (including a change in
553 * state), this is called so the UI can be updated. */
554 otrUpdateContextListCB, // can be NULL
555 /* A new fingerprint for the given user has been received. */
556 otrNewFingerCB,
557 /* The list of known fingerprints has changed. Write them to disk. */
558 otrWriteFingersCB,
559 /* A ConnContext has entered a secure state. */
560 otrGoneSecureCB,
561 /* A ConnContext has left a secure state. */
562 otrGoneInsecureCB,
563 /* We have completed an authentication, using the D-H keys we
564 * already knew. is_reply indicates whether we initiated the AKE. */
565 otrStillSecureCB,
566 /* Find the maximum message size supported by this protocol. */
567 otrMaxMessageSizeCB,
568 /* Return a newly allocated string containing a human-friendly
569 * representation for the given account */
570 NULL, /* account_name */
571 /* Deallocate a string returned by account_name */
572 NULL, /* account_name_free */
573 /* We received a request from the buddy to use the current "extra"
574 * symmetric key. The key will be passed in symkey, of length
575 * OTRL_EXTRAKEY_BYTES. The requested use, as well as use-specific
576 * data will be passed so that the applications can communicate other
577 * information (some id for the data transfer, for example). */
578 NULL,
579 /* Return a string according to the error event. This string will then
580 * be concatenated to an OTR header to produce an OTR protocol error
581 * message. The following are the possible error events:
582 * - OTRL_ERRCODE_ENCRYPTION_ERROR
583 * occured while encrypting a message
584 * - OTRL_ERRCODE_MSG_NOT_IN_PRIVATE
585 * sent encrypted message to somebody who is not in a mutual OTR session
586 * - OTRL_ERRCODE_MSG_UNREADABLE
587 * sent an unreadable encrypted message
588 * - OTRL_ERRCODE_MSG_MALFORMED
589 * message sent is malformed */
590 otrErrorMessageCB,
591 /* Deallocate a string returned by otr_error_message */
592 otrErrorMessageFreeCB, // can be NULL
593 /* Return a string that will be prefixed to any resent message. If this
594 * function is not provided by the application then the default prefix,
595 * "[resent]", will be used.
596 * */
597 //const char *(*resent_msg_prefix)(void *opdata, ConnContext *context);
598 NULL,
599 /* Deallocate a string returned by resent_msg_prefix */
600 //void (*resent_msg_prefix_free)(void *opdata, const char *prefix);
601 NULL,
602 /* Update the authentication UI with respect to SMP events
603 * These are the possible events:
604 * - OTRL_SMPEVENT_ASK_FOR_SECRET
605 * prompt the user to enter a shared secret. The sender application
606 * should call otrl_message_initiate_smp, passing NULL as the question.
607 * When the receiver application resumes the SM protocol by calling
608 * otrl_message_respond_smp with the secret answer.
609 * - OTRL_SMPEVENT_ASK_FOR_ANSWER
610 * (same as OTRL_SMPEVENT_ASK_FOR_SECRET but sender calls
611 * otrl_message_initiate_smp_q instead)
612 * - OTRL_SMPEVENT_CHEATED
613 * abort the current auth and update the auth progress dialog
614 * with progress_percent. otrl_message_abort_smp should be called to
615 * stop the SM protocol.
616 * - OTRL_SMPEVENT_INPROGRESS and
617 * OTRL_SMPEVENT_SUCCESS and
618 * OTRL_SMPEVENT_FAILURE and
619 * OTRL_SMPEVENT_ABORT
620 * update the auth progress dialog with progress_percent
621 * - OTRL_SMPEVENT_ERROR
622 * (same as OTRL_SMPEVENT_CHEATED)
623 * */
624 otrHandleSMPEvent,
625 /* Handle and send the appropriate message(s) to the sender/recipient
626 * depending on the message events. All the events only require an opdata,
627 * the event, and the context. The message and err will be NULL except for
628 * some events (see below). The possible events are:
629 * - OTRL_MSGEVENT_ENCRYPTION_REQUIRED
630 * Our policy requires encryption but we are trying to send
631 * an unencrypted message out.
632 * - OTRL_MSGEVENT_ENCRYPTION_ERROR
633 * An error occured while encrypting a message and the message
634 * was not sent.
635 * - OTRL_MSGEVENT_CONNECTION_ENDED
636 * Message has not been sent because our buddy has ended the
637 * private conversation. We should either close the connection,
638 * or refresh it.
639 * - OTRL_MSGEVENT_SETUP_ERROR
640 * A private conversation could not be set up. A gcry_error_t
641 * will be passed.
642 * - OTRL_MSGEVENT_MSG_REFLECTED
643 * Received our own OTR messages.
644 * - OTRL_MSGEVENT_MSG_RESENT
645 * The previous message was resent.
646 * - OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE
647 * Received an encrypted message but cannot read
648 * it because no private connection is established yet.
649 * - OTRL_MSGEVENT_RCVDMSG_UNREADABLE
650 * Cannot read the received message.
651 * - OTRL_MSGEVENT_RCVDMSG_MALFORMED
652 * The message received contains malformed data.
653 * - OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD
654 * Received a heartbeat.
655 * - OTRL_MSGEVENT_LOG_HEARTBEAT_SENT
656 * Sent a heartbeat.
657 * - OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR
658 * Received a general OTR error. The argument 'message' will
659 * also be passed and it will contain the OTR error message.
660 * - OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED
661 * Received an unencrypted message. The argument 'message' will
662 * also be passed and it will contain the plaintext message.
663 * - OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED
664 * Cannot recognize the type of OTR message received.
665 * - OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE
666 * Received and discarded a message intended for another instance. */
667 otrHandleMsgEvent,
668 /* Create a instance tag for the given accountname/protocol if
669 * desired. */
670 otrCreateInsTagCB,
671 /* Called immediately before a data message is encrypted, and after a data
672 * message is decrypted. The OtrlConvertType parameter has the value
673 * OTRL_CONVERT_SENDING or OTRL_CONVERT_RECEIVING to differentiate these
674 * cases. */
675 //void (*convert_msg)(void *opdata, ConnContext *context, OtrlConvertType convert_type, char ** dest, const char *src);
676 NULL,
677 /* Deallocate a string returned by convert_msg. */
678 //void (*convert_free)(void *opdata, ConnContext *context, char *dest);
679 NULL,
680 /* When timer_control is called, turn off any existing periodic
681 * timer.
683 * Additionally, if interval > 0, set a new periodic timer
684 * to go off every interval seconds. When that timer fires, you
685 * must call otrl_message_poll(userstate, uiops, uiopdata); from the
686 * main libotr thread.
688 * The timing does not have to be exact; this timer is used to
689 * provide forward secrecy by cleaning up stale private state that
690 * may otherwise stick around in memory. Note that the
691 * timer_control callback may be invoked from otrl_message_poll
692 * itself, possibly to indicate that interval == 0 (that is, that
693 * there's no more periodic work to be done at this time).
695 * If you set this callback to NULL, then you must ensure that your
696 * application calls otrl_message_poll(userstate, uiops, uiopdata);
697 * from the main libotr thread every definterval seconds (where
698 * definterval can be obtained by calling
699 * definterval = otrl_message_poll_get_default_interval(userstate);
700 * right after creating the userstate). The advantage of
701 * implementing the timer_control callback is that the timer can be
702 * turned on by libotr only when it's needed.
704 * It is not a problem (except for a minor performance hit) to call
705 * otrl_message_poll more often than requested, whether
706 * timer_control is implemented or not.
708 * If you fail to implement the timer_control callback, and also
709 * fail to periodically call otrl_message_poll, then you open your
710 * users to a possible forward secrecy violation: an attacker that
711 * compromises the user's computer may be able to decrypt a handful
712 * of long-past messages (the first messages of an OTR
713 * conversation).
715 otrTimerControlCB,
719 ///////////////////////////////////////////////////////////////////////////////
720 C_FUNCTION void otrHandleSMPEvent (void *opdata, OtrlSMPEvent smp_event,
721 ConnContext *context, unsigned short progress_percent, char *question)
723 Q_UNUSED(context)
724 Q_UNUSED(progress_percent)
725 ChatForm *frm = (ChatForm *)opdata;
726 dlogf("otrHandleSMPEvent: event=%u; user=[%s]; account=[%s]; proto=[%s]", (unsigned)smp_event, context->username, context->accountname, context->protocol);
727 if (smp_event == OTRL_SMPEVENT_ASK_FOR_SECRET) {
728 // need UNI here
729 dlogf("otrHandleSMPEvent: ask_for_secret; user=[%s]; account=[%s]; proto=[%s]", context->username, context->accountname, context->protocol);
730 bool ok = false;
731 QString reply = QInputDialog::getText(frm, "OTR SMP query",
732 QString("OTR SMP query from %1:\nsecret question:").arg(context->username),
733 QLineEdit::Normal, QString(), &ok);
734 if (!ok || reply.isEmpty()) {
735 otrl_message_abort_smp(frm->mOTRState, &ops, opdata, context);
736 } else {
737 QByteArray ba(reply.toUtf8());
738 otrl_message_respond_smp(frm->mOTRState, &ops, opdata, context, (const unsigned char *)ba.constData(), ba.size());
740 return;
742 if (smp_event == OTRL_SMPEVENT_ASK_FOR_ANSWER) {
743 bool ok = false;
744 QString reply = QInputDialog::getText(frm, "OTR SMP query",
745 QString("OTR SMP query from %1:\nsecret question is: %2").arg(context->username).arg(question),
746 QLineEdit::Normal, QString(), &ok);
747 if (!ok || reply.isEmpty()) {
748 otrl_message_abort_smp(frm->mOTRState, &ops, opdata, context);
749 } else {
750 QByteArray ba(reply.toUtf8());
751 otrl_message_respond_smp(frm->mOTRState, &ops, opdata, context, (const unsigned char *)ba.constData(), ba.size());
753 return;
755 if (smp_event == OTRL_SMPEVENT_CHEATED /*|| smp_event == OTRL_SMPEVENT_ERROR*/) {
756 dlogf(" ** protocol violated, aborting");
757 otrl_message_abort_smp(frm->mOTRState, &ops, opdata, context);
758 otrl_sm_state_free(context->smstate);
759 return;
761 if (smp_event == OTRL_SMPEVENT_SUCCESS) {
762 if (context->smstate->received_question) {
763 dlogf(" ** correct answer, you are trusted");
764 } else {
765 dlogf(" ** secrets proved equal, fingerprint trusted");
767 otrl_sm_state_free(context->smstate);
768 return;
770 if (smp_event == OTRL_SMPEVENT_FAILURE) {
771 if (context->smstate->received_question) {
772 dlogf(" ** wrong answer, you are not trusted");
773 } else {
774 dlogf(" ** secrets did not match, fingerprint not trusted");
776 otrl_sm_state_free(context->smstate);
777 return;
779 if (smp_event == OTRL_SMPEVENT_FAILURE) {
780 dlogf(" ** received abort");
781 otrl_sm_state_free(context->smstate);
782 return;
784 if (smp_event == OTRL_SMPEVENT_ERROR) {
785 dlogf(" ** protocol error, aborting");
786 otrl_message_abort_smp(frm->mOTRState, &ops, opdata, context);
787 otrl_sm_state_free(context->smstate);
788 return;
793 ///////////////////////////////////////////////////////////////////////////////
794 void ChatForm::otrSendMessage (const QString &auni, const QString &txt, const QString &act) {
795 QString uni(auni.toLower());
796 PsycContact *cc = findContact(uni);
797 if (!cc || cc->isPlace() || !cc->isOTRActive() || txt.startsWith("?OTR")) {
798 // internal OTR message or room message
799 mProto->sendMessage(uni, txt, act);
800 } else {
801 // not OTR message, encrypt it
802 gcry_error_t err;
803 char *newMsg = 0;
804 //ConnContext *context;
805 QByteArray me(mProto->uni().toLower().toUtf8());
806 QByteArray to(uni.toUtf8());
807 QByteArray msg(txt.toUtf8());
808 err = otrl_message_sending(mOTRState, &ops, (void *)this, me, PROTO_NAME, to,
809 OTRL_INSTAG_BEST, // instag
810 msg,
811 NULL, // no additional TLVs
812 &newMsg,
813 /*OTRL_FRAGMENT_SEND_SKIP*/OTRL_FRAGMENT_SEND_ALL, // fragmentation policy
814 NULL, //&context,
815 NULL, // add_appdata callback
816 NULL // data
818 if (err) {
819 dlogf("failure to encrypt message!\n");
820 return;
822 if (!newMsg) {
823 dlogf("WTF?! an empty OTR resulting message!\n");
824 //mProto->sendMessage(uni, txt, act);
825 return;
827 //QByteArray mx(newMsg);
828 //mProto->sendMessage(uni, mx, act);
830 ConnContext *context = otrl_context_find(mOTRState, to, me, PROTO_NAME, OTRL_INSTAG_BEST, FALSE, 0, 0, 0);
831 if (context) {
832 char *frag = NULL;
833 err = otrl_message_fragment_and_send(&ops, (void *)this, context, newMsg, OTRL_FRAGMENT_SEND_ALL, &frag);
834 if (frag) free(frag);
835 } else {
836 dlogf("WTF?! no context found for otrl_message_fragment_and_send()\n");
839 otrl_message_free(newMsg);
844 ///////////////////////////////////////////////////////////////////////////////
845 QString ChatForm::otrGotMessage (const QString &auni, const QString &txt) {
846 int isInternal;
847 char *newMsg = NULL;
848 OtrlTLV *tlvs = 0;
850 QString uni(auni.toLower());
851 QByteArray me(mProto->uni().toLower().toUtf8());
852 QByteArray sender(uni.toUtf8());
853 QByteArray msg(txt.toUtf8());
855 ConnContext *context;
856 isInternal = otrl_message_receiving(mOTRState, &ops, (void *)this, me, PROTO_NAME, sender, msg,
857 &newMsg, &tlvs, &context, NULL, NULL);
858 //ConnContext *context = otrl_context_find(mOTRState, sender, me, PROTO_NAME, OTRL_INSTAG_BEST, FALSE, 0, 0, 0);
859 if (context) {
860 dlogf("otrGotMessage: user=[%s]; account=[%s]; proto=[%s]", context->username, context->accountname, context->protocol);
861 } else {
862 dlogf("otrGotMessage: NO CONTEXT!");
866 bool disconnected = false;
867 if (tlvs) {
868 OtrlTLV *tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED);
869 if (tlv) disconnected = true;
870 if (tlvs) otrl_tlv_free(tlvs);
874 if (isInternal) {
875 dlogf("internal message\n");
876 if (newMsg) {
877 dlogf("MSG:===\n%s\n===", newMsg);
878 otrl_message_free(newMsg);
879 } else {
880 dlogf("ORIGMSG:===\n%s\n===", msg.constData());
883 if (disconnected) {
884 dlogf("[%s] has terminated the OTR session\n", me.constData());
885 otrDisconnect(uni);
888 return QString();
891 // it may not be encrypted however (e.g. tagged plaintext with tags stripped)
892 //!if (context && context->msgstate == OTRL_MSGSTATE_PLAINTEXT) dlogf("PLAIN TEXT!\n");
894 QString res;
895 if (!newMsg) {
896 QByteArray ba(txt.toUtf8());
897 dlogf("plain message: [%s]\n", ba.constData());
898 res = txt;
899 } else {
900 res = QString::fromUtf8(newMsg);
901 dlogf("decrypted message: [%s]\n", newMsg);
904 if (newMsg) otrl_message_free(newMsg);
907 if (disconnected) {
908 dlogf("[%s] has terminated the OTR session\n", me.constData());
909 otrDisconnect(uni);
913 return res;
915 ///////////////////////////////////////////////////////////////////////////////
916 #endif
919 const QString ChatForm::otrGetFinger (const QString &aUNI) {
920 Q_UNUSED(aUNI)
921 #ifdef USE_OTR
922 PsycContact *cc = findContact(aUNI);
923 if (!cc || cc->isPlace()) return QString();
924 QByteArray me(mProto->uni().toLower().toUtf8());
925 QByteArray usr(aUNI.toLower().toUtf8());
926 ConnContext *context = otrl_context_find(mOTRState, usr, me, PROTO_NAME, OTRL_INSTAG_BEST, FALSE, 0, NULL, NULL);
927 if (context && context->active_fingerprint) {
928 char hash[OTRL_PRIVKEY_FPRINT_HUMAN_LEN];
929 otrl_privkey_hash_to_human(hash, context->active_fingerprint->fingerprint);
930 return QString(hash);
931 } else {
932 dlogf("no finger for [%s]", usr.constData());
934 #endif
935 return QString();
939 void ChatForm::otrSetTrust (const QString &aUNI, bool tflag) {
940 Q_UNUSED(aUNI)
941 Q_UNUSED(tflag)
942 #ifdef USE_OTR
943 PsycContact *cc = findContact(aUNI);
944 if (!cc || cc->isPlace()) return;
945 QByteArray me(mProto->uni().toLower().toUtf8());
946 QByteArray usr(aUNI.toLower().toUtf8());
947 ConnContext *context = otrl_context_find(mOTRState, usr, me, PROTO_NAME, OTRL_INSTAG_BEST, FALSE, 0, NULL, NULL);
948 if (context && context->active_fingerprint) {
949 otrl_context_set_trust(context->active_fingerprint, tflag ? "verified" : "unknown");
951 if (cc->isOTRVerified() != tflag) {
952 cc->setOTRVerified(tflag);
953 redrawContact(cc);
955 #endif
959 void ChatForm::otrForget (const QString &aUNI) {
960 Q_UNUSED(aUNI)
961 #ifdef USE_OTR
962 PsycContact *cc = findContact(aUNI);
963 if (!cc || cc->isPlace()) return;
964 QByteArray me(mProto->uni().toLower().toUtf8());
965 QByteArray usr(aUNI.toLower().toUtf8());
966 ConnContext *context = otrl_context_find(mOTRState, usr, me, PROTO_NAME, OTRL_INSTAG_BEST, FALSE, 0, NULL, NULL);
967 if (context) {
968 otrl_context_forget(context);
969 cc->setOTRVerified(false);
970 redrawContact(cc);
972 #endif
976 void ChatForm::otrInitiateSMP (const QString &aUNI, const QString &secret, const QString &question) {
977 Q_UNUSED(aUNI)
978 Q_UNUSED(secret)
979 Q_UNUSED(question)
980 #ifdef USE_OTR
981 PsycContact *cc = findContact(aUNI);
982 if (!cc || cc->isPlace() || !cc->isOTRActive()) return;
983 //cc->setOTRVerified(false);
984 //redrawContact(cc);
985 QByteArray me(mProto->uni().toLower().toUtf8());
986 QByteArray user(aUNI.toLower().toUtf8());
987 ConnContext *context = otrl_context_find(mOTRState, user, me, PROTO_NAME, OTRL_INSTAG_BEST, FALSE, 0, 0, 0);
988 if (context && !secret.isEmpty()) {
989 otrSetTrust(aUNI, false);
990 QByteArray ba(secret.toUtf8());
991 if (!question.isEmpty()) {
992 QByteArray ba1(question.toUtf8());
993 otrl_message_initiate_smp_q(mOTRState, &ops, (void *)this, context, ba1.constData(), (const unsigned char *)ba.constData(), ba.size());
994 } else {
995 otrl_message_initiate_smp(mOTRState, &ops, (void *)this, context, (const unsigned char *)ba.constData(), ba.size());
998 #endif
1002 void ChatForm::deactivateAllOTR () {
1003 foreach (PsycContact *cc, mContactList) {
1004 cc->setOTRVerified(false);
1005 if (cc->isOTRActive()) {
1006 cc->setOTRActive(false);
1007 redrawContact(cc);
1013 void ChatForm::disconnectAllOTR () {
1014 deactivateAllOTR();
1015 #ifdef USE_OTR
1016 if (mOTRState) {
1017 ConnContext *context = mOTRState->context_root;
1018 while (context) {
1019 ConnContext *next = context->next;
1020 if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED && context->protocol_version > 1) {
1021 otrDisconnect(context->username);
1022 //otrl_message_disconnect(mOTRState, &ops, (void *)this, context->accountname, context->protocol, context->username);
1024 context = next;
1027 #endif
1031 void ChatForm::otrDisconnect (const QString &uni) {
1032 #ifdef USE_OTR
1033 QByteArray user(uni.toLower().toUtf8());
1034 QByteArray me(mProto->uni().toLower().toUtf8());
1035 dlogf(" sending OTR disconnect to [%s]", user.constData());
1036 otrl_message_disconnect(mOTRState, &ops, (void *)this, me, PROTO_NAME, user, OTRL_INSTAG_BEST);
1037 #else
1038 Q_UNUSED(uni)
1039 #endif
1041 PsycContact *cc = findContact(uni);
1042 if (cc && cc->isOTRActive()) {
1043 cc->setOTRActive(false);
1044 redrawContact(cc);
1050 void ChatForm::otrConnect (const QString &uni) {
1051 #ifdef USE_OTR
1052 QByteArray me(mProto->uni().toLower().toUtf8());
1053 QByteArray to(uni.toLower().toUtf8());
1054 dlogf("------------------- STARTING OTR WITH %s -------------------", to.constData());
1055 //char *msg = otrDefaultQueryMsg(me.constData(), OTRL_POLICY_OPPORTUNISTIC);
1056 //otrSendMessageCB((void *)this, me.constData(), PROTO_NAME, to.constData(), (msg ? msg : "?OTRv2?"));
1057 //if (msg) free(msg);
1058 gcry_error_t err;
1059 char *newMsg = 0;
1060 //ConnContext *context;
1061 err = otrl_message_sending(mOTRState, &ops, (void *)this, me, PROTO_NAME, to,
1062 OTRL_INSTAG_BEST, // instag
1063 "?OTR?",
1064 NULL, // no additional TLVs
1065 &newMsg,
1066 /*OTRL_FRAGMENT_SEND_SKIP*/OTRL_FRAGMENT_SEND_ALL, // fragmentation policy
1067 NULL, //&context,
1068 NULL, // add_appdata callback
1069 NULL // data
1071 if (err) {
1072 dlogf("failure to encrypt message!\n");
1073 return;
1075 if (!newMsg) {
1076 dlogf("WTF?! an empty OTR resulting message!\n");
1077 //mProto->sendMessage(uni, txt, act);
1078 return;
1080 //QByteArray mx(newMsg);
1081 //mProto->sendMessage(uni, mx, act);
1083 ConnContext *context = otrl_context_find(mOTRState, to, me, PROTO_NAME, OTRL_INSTAG_BEST, FALSE, 0, 0, 0);
1084 if (context) {
1085 char *frag = NULL;
1086 err = otrl_message_fragment_and_send(&ops, (void *)this, context, newMsg, OTRL_FRAGMENT_SEND_ALL, &frag);
1087 if (frag) free(frag);
1088 } else {
1089 dlogf("WTF?! no context found for otrl_message_fragment_and_send()\n");
1092 otrl_message_free(newMsg);
1093 #else
1094 Q_UNUSED(uni)
1095 #endif
1099 #ifdef USE_OTR
1100 void ChatForm::restartOTRTimer (unsigned int interval) {
1101 mOTRTimer->stop();
1102 if (interval > 0) mOTRTimer->start(interval*1000);
1106 void ChatForm::onOTRTimer () {
1107 if (mOTRState) otrl_message_poll(mOTRState, &ops, (void *)this);
1109 #endif