Updating ChangeLog for 4.22.10
[centerim.git] / src / imotr.cc
blob3564a2e3f0952ec8cd4e16c026cb185d0a531f19
1 #include "imotr.h"
2 #include "icqconf.h"
3 #include "icqface.h"
4 #include "icqcontacts.h"
5 #include "abstracthook.h"
6 #include "centerim.h"
7 #include "hooks/jabberhook.h"
8 #include "imevents.h"
10 /* libotr headers */
11 #ifdef __cplusplus
12 extern "C" {
13 #endif
14 #include <libotr/privkey.h>
15 #include <libotr/proto.h>
16 #include <libotr/message.h>
17 #include <libotr/userstate.h>
18 #ifdef __cplusplus
20 #endif
23 imotr otr;
25 //#define OTRDEBUG
28 static OtrlPolicy policy_cb(void *opdata, ConnContext *context)
30 #ifdef OTRDEBUG
31 // face.log(_("[OTR-DEBUG] policy_cb(...) - returning OTRL_POLICY_DEFAULT"));
32 #endif
33 return OTRL_POLICY_DEFAULT; // TODO: add user specific policies
35 return (OTRL_POLICY_ALLOW_V2 | OTRL_POLICY_REQUIRE_ENCRYPTION | OTRL_POLICY_SEND_WHITESPACE_TAG |
36 OTRL_POLICY_WHITESPACE_START_AKE | OTRL_POLICY_ERROR_START_AKE);
40 static void create_privkey_cb(void *opdata, const char *accountname,
41 const char *protocol)
43 #ifdef OTRDEBUG
44 face.log("[OTR-DEBUG] create_privkey_cb(..., " + string(accountname) + ")");
45 #endif
46 // Generate the key
47 otrl_privkey_generate(otr.get_userstate(), (string(conf->getdirname()) + PRIVKEYFNAME).c_str(), accountname, protocol);
49 // gaim-otr: otrg_ui_update_fingerprint();
53 static int is_logged_in_cb(void *opdata, const char *accountname, const char *protocol, const char *recipient)
55 icqcontact *c;
56 string recipientname = recipient;
57 recipientname = recipientname.substr(0, recipientname.find_last_of('/')); // removing "/<instandmessager>"
59 if (c = clist.get(imcontact(recipientname, jabber)))
61 if (c->getstatus() == offline)
63 #ifdef OTRDEBUG
64 face.log("[OTR-DEBUG] is_logged_in_cb(..., " + string(accountname) + ", " + string(protocol)+ ", " + string(recipient) + ") - returning 0");
65 #endif
66 return 0; // 0: offline
67 } else
69 #ifdef OTRDEBUG
70 face.log("[OTR-DEBUG] is_logged_in_cb(..., " + string(accountname) + ", " + string(protocol)+ ", " + string(recipient) + ") - returning 1");
71 #endif
72 return 1; // 1: online
74 } else
75 { // recipient not found in contact list
76 #ifdef OTRDEBUG
77 face.log("[OTR-DEBUG] is_logged_in_cb(..., " + string(accountname) + ", " + string(protocol)+ ", " + string(recipient) + ") - returning -1");
78 #endif
79 return -1; // -1: no idea if he's online...
84 static void inject_message_cb(void *opdata, const char *accountname, const char *protocol, const char *recipient, const char *message)
86 icqcontact *c;
87 string recipientname = recipient;
88 recipientname = recipientname.substr(0, recipientname.find_last_of('/')); // removing "/<instandmessager>"
90 #ifdef OTRDEBUG
91 face.log("[OTR-DEBUG] inject_message_cp(" + string(accountname) + ", " + string(protocol) + ", " + recipientname + ", " + string(message) + ")");
92 #endif
94 if (c = clist.get(imcontact(recipientname, jabber)))
96 jhook.send(immessage(c->getdesc(), imevent::outgoing, string(message)));
97 } else
99 face.log(_("[OTR] Error: inject_message_cb, recipient \"") + recipientname + _("\" not found"));
104 static void notify_cb(void *opdata, OtrlNotifyLevel level,
105 const char *accountname, const char *protocol, const char *username,
106 const char *title, const char *primary, const char *secondary)
107 { // Display a notification message for a particular accountname / protocol / username conversation.
108 #ifdef OTRDEBUG
109 face.log("[OTR-DEBUG] notify_cb");
110 #endif
111 string notify_level = "";
112 switch (level)
114 case OTRL_NOTIFY_ERROR: notify_level = _("Error");
115 break;
116 case OTRL_NOTIFY_WARNING: notify_level = _("Warning");
117 break;
118 case OTRL_NOTIFY_INFO: notify_level = _("Info");
119 break;
121 face.log("[OTR] " + notify_level);
122 if (accountname) face.log(_(" accountname: ") + string(accountname));
123 if (protocol) face.log(_(" protocol: ") + string(protocol));
124 if (username) face.log(_(" username: ") + string(username));
125 if (title) face.log(_(" title: ") + string(title));
126 if (primary) face.log(_(" primary: ") + string(primary));
127 if (secondary) face.log(_(" secondary: ") + string(secondary));
131 static int display_otr_message_cb(void *opdata, const char *accountname,
132 const char *protocol, const char *username, const char *msg)
133 { /* Display an OTR control message for a particular accountname /
134 * protocol / username conversation. Return 0 if you are able to
135 * successfully display it. If you return non-0 (or if this
136 * function is NULL), the control message will be displayed inline,
137 * as a received message, or else by using the above notify()
138 * callback. */
139 #ifdef OTRDEBUG
140 face.log("[OTR-DEBUG] display_otr_message_cb(...) - returning -1");
141 #endif
142 return -1;
146 static void update_context_list_cb(void *opdata)
147 { /* When the list of ConnContexts changes (including a change in
148 * state), this is called so the UI can be updated. */
149 #ifdef OTRDEBUG
150 face.log("[OTR-DEBUG] update_context_list_cb(...)");
151 #endif
155 static const char *protocol_name_cb(void *opdata, const char *protocol)
156 { /* Return a newly-allocated string containing a human-friendly name
157 * for the given protocol id */
158 #ifdef OTRDEBUG
159 face.log("[OTR-DEBUG] protocol_name_cb(...) - returning NULL");
160 #endif
161 return NULL;
165 static void protocol_name_free_cb(void *opdata, const char *protocol_name)
166 { // Deallocate a string allocated by protocol_name
167 // Do nothing, since we didn't actually allocate any memory in protocol_name_cb.
168 #ifdef OTRDEBUG
169 face.log("[OTR-DEBUG] protocol_name_free_cb");
170 #endif
174 static void confirm_fingerprint_cb(void *opdata, OtrlUserState us,
175 const char *accountname, const char *protocol, const char *username,
176 unsigned char fingerprint[20])
177 { // A new fingerprint for the given user has been received.
178 #ifdef OTRDEBUG
179 face.log("[OTR-DEBUG] confirm_fingerprint_cb");
180 #endif
181 face.log(_("[OTR] Received unknown fingerprint from \"") + string(username) + "\".");
182 face.log(_(" You can verify it in the OTR options."));
186 static void write_fingerprints()
188 otrl_privkey_write_fingerprints(otr.get_userstate(), (string(conf->getdirname()) + STOREFNAME).c_str());
191 static void write_fingerprints_cb(void *opdata)
192 { // The list of known fingerprints has changed. Write them to disk.
193 #ifdef OTRDEBUG
194 face.log("[OTR-DEBUG] write_fingerprints_cb");
195 #endif
196 write_fingerprints();
200 static void gone_secure_cb(void *opdata, ConnContext *context)
201 { // A ConnContext has entered a secure state.
202 #ifdef OTRDEBUG
203 face.log("[OTR-DEBUG] gone_secure_cb");
204 #endif
205 face.log(_("[OTR] Connection is now secure..."));
209 static void gone_insecure_cb(void *opdata, ConnContext *context)
210 { // A ConnContext has left a secure state.
211 #ifdef OTRDEBUG
212 face.log("[OTR-DEBUG] gone_insecure_cb");
213 #endif
214 face.log(_("[OTR] Connection is insecure..."));
218 static void still_secure_cb(void *opdata, ConnContext *context, int is_reply)
219 { /* We have completed an authentication, using the D-H keys we
220 * already knew. is_reply indicates whether we initiated the AKE. */
221 #ifdef OTRDEBUG
222 face.log("[OTR-DEBUG] still_secure_cb");
223 #endif
227 static void log_message_cb(void *opdata, const char *message)
228 { // Log a message. The passed message will end in "\n".
229 #ifdef OTRDEBUG
230 face.log("[OTR-DEBUG] log_message_cb(..., " + string(message) + ")");
231 #endif
232 face.log(_("[OTR] Log: ") + string(message));
236 static OtrlMessageAppOps ui_ops =
238 policy_cb,
239 create_privkey_cb,
240 is_logged_in_cb,
241 inject_message_cb,
242 notify_cb,
243 display_otr_message_cb,
244 update_context_list_cb,
245 protocol_name_cb,
246 protocol_name_free_cb,
247 confirm_fingerprint_cb,
248 write_fingerprints_cb,
249 gone_secure_cb,
250 gone_insecure_cb,
251 still_secure_cb,
252 log_message_cb
260 void imotr::init()
262 OTRL_INIT; // Initialize the OTR library
264 userstate = otrl_userstate_create(); // Make our OtrlUserState; we'll only use the one.
267 otrl_privkey_read(userstate, (string(conf->getdirname()) + PRIVKEYFNAME).c_str());
268 otrl_privkey_read_fingerprints(userstate, (string(conf->getdirname()) + STOREFNAME).c_str(), NULL, NULL);
272 imotr::imotr()
273 : userstate(0)
277 imotr::~imotr()
279 if (0 != userstate)
280 otrl_userstate_free(userstate);
284 OtrlUserState imotr::get_userstate()
286 return userstate;
291 bool imotr::send_message(const protocolname pname, const string &recipient, string &text)
293 if (pname != jabber) // at the moment, otr is only with jabber supported...
295 return true;
298 gcry_error_t err = 0;
299 char *newmessage = NULL;
301 string accountname = conf->getourid(pname).nickname + "@" + conf->getourid(pname).server;
302 string protocolname = "jabber";
304 #ifdef OTRDEBUG
305 face.log("[OTR-DEBUG] otrl_message_sending(" + accountname + ", " + protocolname + ", " + recipient + ", " + text + ")");
306 #endif
307 if (otrl_proto_message_type(text.c_str()) == OTRL_MSGTYPE_NOTOTR)
309 err = otrl_message_sending(userstate, &ui_ops, NULL, accountname.c_str(), protocolname.c_str(),
310 recipient.c_str(), text.c_str(), NULL, &newmessage, NULL, NULL);
312 if (err)
314 face.log(_("[OTR] Error while encrypting message... no message sent!"));
315 face.log(_(" accountname: ") + accountname);
316 face.log(_(" recipient: ") + recipient);
317 if (newmessage) otrl_message_free(newmessage);
318 return false;
321 if (newmessage)
323 text = newmessage;
325 #ifdef OTRDEBUG
326 face.log("[OTR-DEBUG] otrl_message_sending - newmessage: \"" + text + "\")");
327 #endif
328 otrl_message_free(newmessage);
331 return true;
335 bool imotr::receive_message(const protocolname pname, const string &from, string &text)
337 if (pname != jabber) // at the moment, otr is only with jabber supported...
339 return true;
342 int ignore_message;
343 char *newmessage = NULL;
345 string accountname = conf->getourid(pname).nickname + "@" + conf->getourid(pname).server;
346 string protocolname = "jabber";
347 string sendername = from.substr(0, from.find_last_of('/')); // remove "/<im-name>"
349 #ifdef OTRDEBUG
350 face.log("[OTR-DEBUG] otrl_message_receive(" + accountname + ", " + protocolname + ", " + sendername + ", " + text + ")");
351 #endif
353 ignore_message = otrl_message_receiving(userstate, &ui_ops, NULL, accountname.c_str(), protocolname.c_str(),
354 sendername.c_str(), text.c_str(), &newmessage, NULL, NULL, NULL);
356 if (ignore_message == 1) return false;
357 if (newmessage == NULL) return true;
359 text = newmessage;
361 #ifdef OTRDEBUG
362 face.log("[OTR-DEBUG] otrl_message_sending - newmessage: \"" + text + "\")");
363 #endif
365 otrl_message_free(newmessage);
366 return true;
371 void imotr::start_session(icqcontact *contact)
373 #ifdef OTRDEBUG
374 face.log("[OTR-DEBUG] imotr::start_session(contact)");
375 #endif
376 if (contact->getdesc().pname != jabber) // at the moment, otr is only with jabber supported...
378 face.log(_("[OTR] Error: At the moment, only jabber is supported"));
379 return;
381 ConnContext *context;
382 string accountname = conf->getourid(contact->getdesc().pname).nickname + "@" + conf->getourid(contact->getdesc().pname).server;
383 string protocolname = "jabber";
384 string username = contact->getdesc().nickname;
386 face.log(_("[OTR] Trying to start a secure session with \"") + username +"\"...");
387 inject_message_cb(NULL, accountname.c_str(), protocolname.c_str(), username.c_str(), "?OTRv2?");
390 void imotr::end_session(icqcontact *contact)
392 #ifdef OTRDEBUG
393 face.log("[OTR-DEBUG] imotr::end_session(contact)");
394 #endif
395 if (contact->getdesc().pname != jabber) // at the moment, otr is only with jabber supported...
397 face.log(_("[OTR] Error: At the moment, only jabber is supported"));
398 return;
400 ConnContext *context;
401 string accountname = conf->getourid(contact->getdesc().pname).nickname + "@" + conf->getourid(contact->getdesc().pname).server;
402 string protocolname = "jabber";
403 string username = contact->getdesc().nickname;
405 face.log(_("[OTR] Ending secure session with \"") + username +"\"...");
406 otrl_message_disconnect(userstate, &ui_ops, NULL, accountname.c_str(), protocolname.c_str(), username.c_str());
410 int imotr::yesno(const char *question)
412 string tmp;
413 tmp = face.inputstr(" " + string(question) + _("yes/no") + " ", _("no"));
414 while ((face.getlastinputkey() != KEY_ESC) && (tmp != _("yes")) && (tmp != _("no")))
416 tmp = face.inputstr(_(" Please type 'yes' or 'no': "), _("no"));
418 if (face.getlastinputkey() == KEY_ESC) return -1;
419 if (tmp == _("yes")) return 1;
420 if (tmp == _("no")) return 0;
425 void imotr::dialog()
427 dialogbox db;
428 int n, b, citem, node;
429 string tmp;
430 bool fin;
431 protocolname pname;
433 vector< pair< int, void* > > action;
435 face.blockmainscreen();
437 db.setwindow(new textwindow(0, 0, face.sizeDlg.width, face.sizeDlg.height,
438 conf->getcolor(cp_dialog_frame), TW_CENTERED,
439 conf->getcolor(cp_dialog_highlight), _(" IM account manager ")));
441 db.settree(new treeview(conf->getcolor(cp_dialog_text), conf->getcolor(cp_dialog_selected),
442 conf->getcolor(cp_dialog_highlight), conf->getcolor(cp_dialog_text)));
444 db.setbar(new horizontalbar(conf->getcolor(cp_dialog_text),
445 conf->getcolor(cp_dialog_selected), _("Change"), _("Done"), 0));
447 db.addautokeys();
448 db.idle = &face.dialogidle;
450 treeview &t = *db.gettree();
452 map<protocolname, bool> mod;
453 for(protocolname pname = icq; pname != protocolname_size; pname++)
454 mod[pname] = false;
456 for(fin = false; !fin; ) {
457 t.clear();
459 citem = 1;
460 action.clear();
462 node = t.addnode(0, 0, 0, _(" Private keys "));
463 for (OtrlPrivKey *privkey = userstate->privkey_root; privkey != NULL; privkey = privkey->next)
465 string accountname = privkey->accountname;
466 string protocol = privkey->protocol;
467 char hash[45];
468 if (!otrl_privkey_fingerprint(userstate, hash, accountname.c_str(), protocol.c_str()))
470 strncpy(hash, _("Error calculating Fingerprint"), 45);
472 n = t.addnode(node, 0, 0, (_(" Account: ") + accountname + " ").c_str());
473 t.addleaff(n, 0, 0, (_(" Protocol: ") + protocol + " ").c_str());
474 t.addleaff(n, 0, 0, (_(" Fingerprint: ") + string(hash) + " ").c_str());
475 t.addleaff(n, 0, citem++, _(" Forget key "));
476 action.push_back(pair<int, void*>(0x100, privkey));
477 // t.addleaff(n, 0, citem++, " Generate new key ");
478 // action.push_back(pair<int, void*>(0x101, privkey));
479 } // for (all privkeys)
481 node = t.addnode(0, 0, 0, _(" Public keys "));
482 for (ConnContext *context = userstate->context_root; context != NULL; context = context->next)
484 char hash[45];
485 string username = context->username;
486 string accountname = context->accountname;
487 string protocol = context->protocol;
488 n = t.addnode(node, 0, 0, (_(" User: ") + username + " ").c_str());
489 t.addleaff(n, 0, 0, (_(" Protocol: ") + protocol + " ").c_str());
490 t.addleaff(n, 0, 0, (_(" Account: ") + accountname + " ").c_str());
492 for (Fingerprint *fingerprint = context->fingerprint_root.next; fingerprint != NULL; fingerprint = fingerprint->next)
494 int newnode;
495 string verified = (fingerprint->trust && (string(fingerprint->trust) == "yes")) ? _("Yes") : _("No");
497 otrl_privkey_hash_to_human(hash, fingerprint->fingerprint);
498 newnode = t.addnode(n, 0, 0, (_(" Fingerprint: ") + string(hash) + " ").c_str());
499 t.addleaff(newnode, 0, citem++, (_(" Verified: ") + verified).c_str());
500 action.push_back(pair<int, void*>(0x200, fingerprint));
501 t.addleaff(newnode, 0, citem++, _(" Forget key "));
502 action.push_back(pair<int, void*>(0x201, fingerprint));
504 if (context->active_fingerprint)
506 otrl_privkey_hash_to_human(hash, context->active_fingerprint->fingerprint);
507 t.addleaff(n, 0, 0, (_(" Active fingerprint: ") + string(hash) + " ").c_str());
514 fin = !db.open(n, b, (void **) &citem) || (b == 1);
516 if(!fin && citem)
518 int answer;
519 citem--;
520 switch (action[citem].first)
522 case (0x100): // forget privkey
523 answer = yesno(_("Do you want to forget the selected key?"));
524 if (answer == 1)
526 otrl_privkey_forget((OtrlPrivKey*)action[citem].second);
527 write_fingerprints();
529 break;
531 case (0x101): // generate new privkey
532 break;
535 case (0x200): // verify fingerprint
536 answer = yesno(_("Do you want verify the selected key?"));
537 if (answer == -1) break;
538 otrl_context_set_trust((Fingerprint*)action[citem].second, ((answer) ? "yes" : "no"));
539 write_fingerprints();
540 break;
542 case (0x201): // forget fingerprint
543 answer = yesno(_("Do you want to forget the selected key?"));
544 if (answer == 1)
546 otrl_context_forget_fingerprint((Fingerprint*)action[citem].second, 1);
547 write_fingerprints();
549 break;
552 default: break;
558 db.close();
559 face.unblockmainscreen();
561 face.relaxedupdate();
566 string imotr::get_msg_state(const protocolname pname, const string user)
568 if (pname != jabber) // at the moment, otr is only with jabber supported...
570 return _("No Jabber");
572 ConnContext *context;
573 string accountname = conf->getourid(pname).nickname + "@" + conf->getourid(pname).server;
574 string protocolname = "jabber";
576 context = otrl_context_find(userstate, user.c_str(), accountname.c_str(), protocolname.c_str(),
577 0, NULL, NULL, NULL);
578 if (!context)
580 return _("No OTR");
583 switch (context->msgstate)
585 case (OTRL_MSGSTATE_PLAINTEXT): return _("Plaintext");
586 case (OTRL_MSGSTATE_ENCRYPTED): return _("Encrypted");
587 case (OTRL_MSGSTATE_FINISHED): return _("Finished");
588 default: return _("Unknown");
593 string imotr::is_verified(const protocolname pname, const string user)
595 if (pname != jabber) // at the moment, otr is only with jabber supported...
597 return _("No Jabber");
599 ConnContext *context;
600 string accountname = conf->getourid(pname).nickname + "@" + conf->getourid(pname).server;
601 string protocolname = "jabber";
603 context = otrl_context_find(userstate, user.c_str(), accountname.c_str(), protocolname.c_str(),
604 0, NULL, NULL, NULL);
605 if (context == NULL)
607 return _("No OTR");
609 if (context->active_fingerprint == NULL)
611 return _("No Encryption");
613 if (string(context->active_fingerprint->trust) == "yes")
615 return _("Verified");
617 return _("Unverified");