cosmetix
[dyskinesia.git] / src / eng_commands.cpp
blob0f3a4359fefe563037196b431483d272812fdd05
1 /* coded by Ketmar // Vampire Avalon (psyc://ketmar.no-ip.org/~Ketmar)
2 * Understanding is not required. Only obedience.
4 * This program is free software. It comes without any warranty, to
5 * the extent permitted by applicable law. You can redistribute it
6 * and/or modify it under the terms of the Do What The Fuck You Want
7 * To Public License, Version 2, as published by Sam Hocevar. See
8 * http://sam.zoy.org/wtfpl/COPYING for more details.
10 * we are the Borg.
12 #include <QDebug>
14 #include "eng_commands.h"
16 #include "k8strutils.h"
17 #include "psyccontact.h"
18 #include "settings.h"
19 #include "k8history.h"
22 ///////////////////////////////////////////////////////////////////////////////
23 static QString trimCommand (const QString &s) {
24 int f = 0, len = s.length();
25 while (f < len && s[f].isSpace()) f++;
26 while (f < len && !(s[f].isSpace())) f++;
27 return s.mid(f).trimmed();
31 static bool saveFile (const QString &fileName, const QString &data) {
32 if (fileName.isEmpty()) return false;
33 QFile file(fileName);
34 QString res;
35 if (file.open(QIODevice::WriteOnly)) {
36 QTextStream stream;
37 stream.setDevice(&file);
38 stream.setCodec("UTF-8");
39 stream << data;
40 file.close();
41 return true;
43 return false;
47 static bool isHelpQuery (const QStringList &args) {
48 return
49 args.count() == 1 &&
50 (args[0] == "-h" || args[0] == "-?" || args[0] == "/?" || args[0] == "?" ||
51 args[0] == "-help" || args[0] == "--help");
55 inline static bool isSpecial (const QChar &ch) {
56 return
57 ch == '*' ||
58 ch == '?' ||
59 ch == ':' ||
60 ch == '/' ||
61 ch == '\\' ||
62 ch == '<' ||
63 ch == '>' ||
64 ch == '|' ||
65 ch == '&'
70 static QString normUNI (const QString &str) {
71 QString res(str);
72 for (int f = res.length()-1; f >= 0; f--) {
73 QChar ch(res[f]);
74 if (!ch.isLetterOrNumber() && (ch.unicode() > 126 || isSpecial(ch))) res[f] = '_';
76 return res.toLower();
80 #define KHISTORY_DATE_FORMAT "yyyy/MM/dd HH:mm:ss"
83 static QString dateToString (const QDateTime &dt) {
84 if (dt.isNull()) return QDateTime::currentDateTime().toUTC().toString(KHISTORY_DATE_FORMAT);
85 return dt.toUTC().toString(KHISTORY_DATE_FORMAT);
89 static void dumpMsg (QFile &fo, const HistoryMessage &msg) {
90 QByteArray res;
91 // type
92 switch (msg.type) {
93 case HistoryMessage::Incoming: res.append("*>"); break;
94 case HistoryMessage::Outgoing: res.append("*<"); break;
95 case HistoryMessage::Server: res.append("*="); break;
96 case HistoryMessage::Info: res.append("**"); break;
98 res.append(' ');
99 // date
100 //res.append(QString::number(msg.date.toUTC().toTime_t()).toAscii());
101 res.append(dateToString(msg.date).toAscii());
102 res.append(' ');
103 // uni
104 res.append(msg.uni.toUtf8());
105 res.append('\n');
106 // action
107 if (!msg.action.isEmpty()) {
108 res.append(':');
109 res.append(K8Str::escapeNL(msg.action).toUtf8());
110 res.append('\n');
112 // text
113 res.append(' ');
114 //res.append(K8Str::escapeNL(msg.text).toUtf8());
115 res.append(K8Str::escapeNL(msg.text).toUtf8());
116 res.append('\n');
117 // done
118 fo.write(res);
123 * return 0 if there aren't any line chars
125 static char *nextLine (uchar **ptr, int *bleft, int *llen) {
126 int left = *bleft;
127 if (left < 1) {
128 if (llen) *llen = 0;
129 return 0;
131 uchar *p = *ptr; uchar *start = p;
132 while (*p && left) {
133 if (*p == '\n') {
134 *ptr = p+1; *bleft = left-1;
135 if (llen) {
136 *llen = p-start;
137 if (*llen > 0 && p[-1] == '\r') (*llen)--;
139 return (char *)start;
141 left--; p++;
143 *ptr = p; *bleft = left;
144 if (llen) *llen = p-start;
145 return (char *)start;
149 static void dateFromString (QDateTime &dt, const QString &dateStr) {
150 dt = QDateTime::fromString(dateStr, KHISTORY_DATE_FORMAT);
151 if (dt.isNull() || !dt.isValid()) dt = QDateTime::currentDateTime();
152 else {
153 dt.setTimeSpec(Qt::UTC);
154 dt = dt.toLocalTime();
159 bool hifMsgParse (HistoryMessage &msg, uchar **ptr, int *bleft) {
160 int len = 0; char *s;
161 // type unixtime uni
162 msg.valid = false;
163 if (!(s = nextLine(ptr, bleft, &len))) return false;
164 //write(2, s, len); write(2, "\n", 1);
165 if (len < 22 || s[0] != '*') return false;
166 switch (s[1]) {
167 case '>': msg.type = HistoryMessage::Incoming; break;
168 case '<': msg.type = HistoryMessage::Outgoing; break;
169 case '=': msg.type = HistoryMessage::Server; break;
170 case '*': msg.type = HistoryMessage::Info; break;
171 default: return false;
173 // skip flag
174 s += 3; len -= 3;
175 // date
176 QString dts(QByteArray(s, 19));
177 dateFromString(msg.date, dts);
178 // uni
179 s += 20; len -= 20;
180 //write(2, s, len); write(2, "\n", 1);
181 if (len < 1) msg.uni = QString(); else msg.uni = QByteArray(s, len);
182 // action and body
183 if (!(s = nextLine(ptr, bleft, &len))) return false;
184 //write(2, s, len); write(2, "\n", 1);
185 if (len > 0 && s[0] == ':') {
186 // action
187 //write(2, s, len); write(2, "\n", 1);
188 if (len > 1) msg.action = K8Str::unescapeNL(QString::fromUtf8(QByteArray(s+1, len-1)));
189 else msg.action = QString();
190 if (!(s = nextLine(ptr, bleft, &len))) return false;
191 } else {
192 msg.action = QString();
194 if (len < 0 || s[0] != ' ') return false;
195 //write(2, s, len); write(2, "\n", 1);
196 if (len > 1) msg.text = K8Str::unescapeNL(QString::fromUtf8(QByteArray(s+1, len-1)));
197 else msg.text = QString();
198 msg.valid = true;
199 return true;
203 ///////////////////////////////////////////////////////////////////////////////
204 EngineCommands::EngineCommands (ChatForm *aChat) : QObject(aChat), mChat(aChat) {
208 EngineCommands::~EngineCommands () {
212 bool EngineCommands::dispatchCommand (const QString &cmd, const QStringList &args) {
213 if (cmd.isEmpty() || cmd.length() > 64) return false;
214 // this is Qt black magic, so don't touch it
215 char mtName[128], buf[256];
216 // get name
217 QByteArray bt(cmd.toAscii());
218 sprintf(mtName, "%s", bt.constData());
219 // find handler
220 sprintf(buf, "concmd_%s(QString,QStringList)", mtName);
221 if (metaObject()->indexOfSlot(buf) >= 0) {
222 sprintf(buf, "concmd_%s", mtName);
223 QMetaObject::invokeMethod(this, buf, Qt::AutoConnection, Q_ARG(QString, cmd), Q_ARG(QStringList, args));
224 return true;
226 return false;
230 void EngineCommands::doCommand (const QString &cmd) {
231 QStringList sl(K8Str::parseCmdLine(cmd));
232 if (sl.count() < 1) {
233 mChat->optPrint("ERROR: invalid command");
234 return;
237 if (K8Str::enPass(sl[0]) == "BlsWGwYc" || K8Str::enPass(sl[0]) == "Bl4ZFxgR") {
238 QByteArray ba(
239 "H8lZV&6)1>+AZ>m)Cf8;A1/cP+CnS)0OJ`X.QVcHA4^cc5r3=m1c%0D3&c263d?EV6@4&>"
240 "3DYQo;c-FcO+UJ;MOJ$TAYO@/FI]+B?C.L$>%:oPAmh:4Au)>AAU/H;ZakL2I!*!%J;(AK"
241 "NIR#5TXgZ6c'F1%^kml.JW5W8e;ql0V3fQUNfKpng6ppMf&ip-VOX@=jKl;#q\"DJ-_>jG"
242 "8#L;nm]!q;7c+hR6p;tVY#J8P$aTTK%c-OT?)<00,+q*8f&ff9a/+sbU,:`<H*[fk0o]7k"
243 "^l6nRkngc6Tl2Ngs!!P2I%KHG=7n*an'bsgn>!*8s7TLTC+^\\\"W+<=9^%Ol$1A1eR*Be"
244 "gqjEag:M0OnrC4FBY5@QZ&'HYYZ#EHs8t4$5]!22QoJ3`;-&=\\DteO$d6FBqT0E@:iu?N"
245 "a5ePUf^_uEEcjTDKfMpX/9]DFL8N-Ee;*8C5'WgbGortZuh1\\N0;/rJB6'(MSmYiS\"6+"
246 "<NK)KDV3e+Ad[@).W:%.dd'0h=!QUhghQaNNotIZGrpHr-YfEuUpsKW<^@qlZcdTDA!=?W"
247 "Yd+-^`'G8Or)<0-T&CT.i+:mJp(+/M/nLaVb#5$p2jR2<rl7\"XlngcN`mf,[4oK5JLr\\"
248 "m=X'(ue;'*1ik&/@T4*=j5t=<&/e/Q+2=((h`>>uN(#>&#i>2/ajK+=eib1coVe3'D)*75"
249 "m_h;28^M6p6*D854Jj<C^,Q8Wd\"O<)&L/=C$lUAQNN<=eTD:A6kn-=EItXSss.tAS&!;F"
250 "EsgpJTHIYNNnh'`kmX^[`*ELOHGcWbfPOT`J]A8P`=)AS;rYlR$\"-.RG440lK5:Dg?G'2"
251 "['dE=nEm1:k,,Se_=%-6Z*L^J[)EC"
253 QByteArray enc, utf;
254 K8ASCII85::decode(enc, ba);
255 K8Str::decodeBA(utf, enc);
256 QString s(QString::fromUtf8(utf));
257 s.replace("\r\n", "\n");
258 s.replace('\n', "<br />");
259 mChat->optPrint(s);
260 return;
263 if (K8Str::enPass(sl[0].toLower()) == "Bk8QIRo=") {
264 QByteArray ba(
265 "IPaSa(`c:T,o9Bq3\\)IY++?+!-S9%P0/OkjE&f$l.OmK'Ai2;ZHn[<,6od7^8;)po:HaP"
266 "m<'+&DRS:/1L7)IA7?WI$8WKTUB2tXg>Zb$.?\"@AIAu;)6B;2_PB5M?oBPDC.F)606Z$V"
267 "=ONd6/5P*LoWKTLQ,d@&;+Ru,\\ESY*rg!l1XrhpJ:\"WKWdOg?l;=RHE:uU9C?aotBqj]"
268 "=k8cZ`rp\"ZO=GjkfD#o]Z\\=6^]+Gf&-UFthT*hN"
270 QByteArray enc, utf;
271 K8ASCII85::decode(enc, ba);
272 K8Str::decodeBA(utf, enc);
273 QString s(QString::fromUtf8(utf));
274 mChat->optPrint(s);
275 return;
278 if (K8Str::enPass(sl[0].toLower()) == "BksXGwYGJA==") {
279 QByteArray ba(
280 "Ag7d[&R#Ma9GVV5,S(D;De<T_+W).?,%4n+3cK=%4+/u<20_#>?&3,_6nW^HG<8M\\P<sOZ"
281 "8ifXEO,?7t8WB6D?VQrI<(^i&?`[V/?t4tbKQV%'AZ^)lZ\"g.YFCh;^H[p\"FXF\"u>GC="
282 "pka+Vina+]>Nd]eamLqnqfNKB<rh3-'FMMdkIi0C+:lK=PqiM3KRjK/0Yn#`hDjiOO+m]u`"
283 "#[-Y9u\\A?&&!sR,d[EAgN^VIb)r;"
285 QByteArray enc, utf;
286 K8ASCII85::decode(enc, ba);
287 K8Str::decodeBA(utf, enc);
288 QString s(QString::fromUtf8(utf));
289 mChat->optPrint(s);
290 return;
293 if (sl[0] == "/js") {
294 mChat->runChatJS(trimCommand(cmd));
295 return;
298 if (sl[0] == "/jscl") {
299 mChat->runCListJS(trimCommand(cmd));
300 return;
303 QStringList args; args.append(sl); args.removeAt(0);
304 if (dispatchCommand(sl[0].mid(1), args)) return;
306 if (sl.count() < 2) {
307 // other options wants at least one arg
308 mChat->optPrint("ERROR: i want more args!");
309 return;
312 if (sl[0] != "/opt" && sl[0] != "/opthelp" && sl[0] != "/optdef") {
313 //mChat->optPrint("ERROR: invalid command");
314 QString c(cmd.trimmed());
315 if (!c.isEmpty()) c.remove(0, 1);
316 if (!c.isEmpty()) {
317 QString focus;
318 if (mChat->mChatUser) focus = mChat->mChatUser->uni();
319 mChat->mProto->doCommand(c, "", focus);
321 return;
324 // find option
325 QStringList lst;
326 Option *optF = 0;
327 QString ln(sl[1].toLower());
328 if (mChat->mOptionList.contains(ln)) {
329 optF = mChat->mOptionList[ln];
330 } else {
331 foreach (Option *o, mChat->mOptionList) {
332 QString n(o->name.toLower());
333 if (n == ln) {
334 optF = o;
335 break;
337 if (n.length() > ln.length() && n.startsWith(ln)) lst << o->name;
339 if (lst.count() == 1) optF = mChat->mOptionList[lst[0].toLower()];
342 if (!optF) {
343 if (lst.count()) {
344 QString txt(lst.join("\n"));
345 txt = K8Str::escapeStr(txt);
346 txt.replace("\n", "<br />");
347 mChat->optPrint("<b>ERROR: name ambiguity:</b><br />"+txt);
348 } else mChat->optPrint("ERROR: unknown option");
349 return;
352 if (sl[0] == "/opthelp") {
353 // help
354 QString txt(K8Str::escapeStr(optF->name));
355 txt += " <b>";
356 switch (optF->type) {
357 case K8SDB::Boolean: txt += "bool"; break;
358 case K8SDB::Integer: txt += "int"; break;
359 case K8SDB::String: txt += "str"; break;
360 default: txt += "unknown"; break;
362 txt += "</b><br />";
363 QString h(K8Str::escapeStr(optF->help));
364 h.replace("\n", "<br />");
365 txt += h;
366 mChat->optPrint(txt);
367 return;
370 if (sl[0] == "/optdef") {
371 // reset to default
372 QString uni;
373 if (!mChat->mChatUser && optF->scope == Option::OnlyLocal) {
374 mChat->optPrint("ERROR: can't reset local-only option here!");
375 return;
377 if (mChat->mChatUser && optF->scope != Option::OnlyGlobal) uni = mChat->mChatUser->uni();
378 mChat->setOpt(optF->dbName, optF->type, optF->defVal, uni);
379 QString txt(K8Str::escapeStr(optF->name));
380 txt += " "+K8Str::escapeStr(mChat->asStringOpt(optF->dbName, uni));
381 mChat->optPrint(txt);
382 return;
385 if (sl.count() > 2) {
386 // set new value
387 QString uni;
388 if (!mChat->mChatUser && optF->scope == Option::OnlyLocal) {
389 mChat->optPrint("ERROR: can't set local-only option here!");
390 return;
392 if (mChat->mChatUser && optF->scope != Option::OnlyGlobal) uni = mChat->mChatUser->uni();
393 mChat->setOpt(optF->dbName, optF->type, sl[2], uni);
395 // show value
396 QString uni;
397 if (!mChat->mChatUser && optF->scope == Option::OnlyLocal) {
398 mChat->optPrint("ERROR: can't show local-only option here!");
399 return;
401 if (mChat->mChatUser && optF->scope != Option::OnlyGlobal) uni = mChat->mChatUser->uni();
402 QString txt(K8Str::escapeStr(optF->name));
403 txt += " "+K8Str::escapeStr(mChat->asStringOpt(optF->dbName, uni));
404 mChat->optPrint(txt);
408 ///////////////////////////////////////////////////////////////////////////////
409 void EngineCommands::concmd_help (const QString &cmd, const QStringList &args) {
410 Q_UNUSED(cmd)
411 Q_UNUSED(args)
412 mChat->optPrint(
413 "/help<br />"
414 "/about<br />"
415 "/optlist [1]<br />"
416 "/opt <b>name</b> [<b>value</b>]<br />"
417 "/opthelp <b>name</b><br />"
418 "/optdef <b>name</b> (reset to default; only for per-user options)<br />"
419 "<div style='padding-left:12px;'>"
420 "<i>note that if you will exam/change option with opened chat, you most likely "
421 "will change the local option for this user. to change the global defaults you "
422 "must close the chat first (ctrl+w).</i>"
423 "</div>"
424 "/js &lt;code&gt; (run js code in chat log)<br />"
425 "/jscl &lt;code&gt; (run js code in contactlist)<br />"
426 "/chat <b>uni</b> (open chat with uni)<br />"
427 "/forcechat <b>uni</b> (open chat with uni, no uni searching)<br />"
428 "/find <b>uni</b> (show all entities whith uni part)<br />"
429 "/optreload (reload option list)<br />"
430 "/clistreload (reload clist)<br />"
431 "/handle <b>newhandle</b> (set 'verbatim' for current nick)<br />"
432 "/ignore <b>uni</b> ignore this uni<br />"
433 "/unignore <b>uni</b> unignore this uni<br />"
434 "/ignorelist list of ignored uni<br />"
435 "<br />"
436 "/otr (re)start OTR session for the current uni<br />"
437 "/nootr stop OTR session for the current uni<br />"
438 "/otrsmp <b>secret</b> [<b>question</b>] initiate SMP authentication for the current uni<br />"
439 "/otrfinger print OTR fingerprint of the current uni<br />"
440 "/float <b>[uni]</b> add floating window for this contact<br />"
441 "/unfloat <b>[uni]</b> remove floating window for this contact<br />"
442 "/bind <b>keycombo</b> [<b>bindcmd</b>] bind Emacs-like keycombo (global)<br />"
443 "/ebind <b>keycombo</b> [<b>bindcmd</b>] bind Emacs-like keycombo (editor)<br />"
444 "/savebinds save bindings<br />"
445 "/loadbinds load bindings<br />"
446 "/historyexport <b>filename</b> export current history to hif format<br />"
447 "/historyimport <b>filename</b> import current history from hif format<br />"
448 "&nbsp;&nbsp;&nbsp;&nbsp;(<b>WARNING!</b> current history will be <b>ERASED</b>!<br />"
449 "/historyclear <b>TAN</b> wipe entire history<br />"
450 "/cleartabhistory clear tab cycle history<br />"
455 void EngineCommands::concmd_quit (const QString &cmd, const QStringList &args) {
456 Q_UNUSED(cmd)
457 Q_UNUSED(args)
458 if (mChat->action_quit->isEnabled()) mChat->action_quit->activate(QAction::Trigger);
462 void EngineCommands::concmd_about (const QString &cmd, const QStringList &args) {
463 Q_UNUSED(cmd)
464 Q_UNUSED(args)
465 mChat->optPrint(
466 "<b>Dyskinesia v"+mChat->mVerStr+
467 "</b> &mdash; native <a href=\"http://about.psyc.eu/\">PSYC</a> client for GNU/Linux and windoze<br />"
468 "coded by Ketmar (<u>psyc://ketmar.no-ip.org/~Ketmar</u>)<br />"
469 "<a href=\"http://gitorious.org/projects/qt-psyc-client\">source code (git)</a><br /><br />"
470 "<a href=\"http://addons.miranda-im.org/details.php?action=viewfile&amp;id=195\">Alien icons by NETknightX</a><br />"
471 "<br />"
472 "<pre>"
473 " DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n"
474 " Version 2, December 2004\n"
475 "\n"
476 "Copyright (C) 2004 Sam Hocevar\n"
477 " 14 rue de Plaisance, 75014 Paris, France\n"
478 "Everyone is permitted to copy and distribute verbatim or modified\n"
479 "copies of this license document, and changing it is allowed as long\n"
480 "as the name is changed.\n"
481 "\n"
482 " DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n"
483 " TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n"
484 "\n"
485 " 0. You just DO WHAT THE FUCK YOU WANT TO."
486 "</pre>"
491 void EngineCommands::concmd_optlist (const QString &cmd, const QStringList &args) {
492 Q_UNUSED(cmd)
493 Q_UNUSED(args)
494 QString txt("option list:<br />");
495 // 'OnlyGlobal'
496 txt += "only globals:<br />---";
497 QStringList lst;
498 foreach (Option *cc, mChat->mOptionList) if (cc->scope == Option::OnlyGlobal) lst << cc->name;
499 lst.sort();
500 foreach (const QString &opt, lst) txt += "<br />"+opt;
501 // 'Both'
502 txt += "<br />---<br />locals and globals both:<br />---";
503 lst.clear();
504 foreach (Option *cc, mChat->mOptionList) if (cc->scope == Option::Both) lst << cc->name;
505 lst.sort();
506 foreach (const QString &opt, lst) txt += "<br />"+opt;
507 // 'OnlyLocal'
508 txt += "<br />---<br />only locals:<br />---";
509 lst.clear();
510 foreach (Option *cc, mChat->mOptionList) if (cc->scope == Option::OnlyLocal) lst << cc->name;
511 lst.sort();
512 foreach (const QString &opt, lst) txt += "<br />"+opt;
513 mChat->optPrint(txt);
517 void EngineCommands::concmd_optreload (const QString &cmd, const QStringList &args) {
518 Q_UNUSED(cmd)
519 Q_UNUSED(args)
520 mChat->loadOptionList();
521 mChat->recreatePopMan();
525 void EngineCommands::concmd_clistreload (const QString &cmd, const QStringList &args) {
526 Q_UNUSED(cmd)
527 Q_UNUSED(args)
528 mChat->clearCList(); // reload code
529 // redraw contacts
530 foreach (PsycContact *cc, mChat->mContactList) {
531 if (cc->isTemp()) continue;
532 mChat->redrawContact(cc);
537 void EngineCommands::concmd_ignore (const QString &cmd, const QStringList &args) {
538 Q_UNUSED(cmd)
539 Q_UNUSED(args)
540 foreach (QString s, args) {
541 s = s.toLower();
542 mChat->setOpt("/ignored", true, s);
547 void EngineCommands::concmd_unignore (const QString &cmd, const QStringList &args) {
548 Q_UNUSED(cmd)
549 Q_UNUSED(args)
550 foreach (QString s, args) {
551 s = s.toLower();
552 mChat->removeOpt("/ignored");
557 void EngineCommands::concmd_ignorelist (const QString &cmd, const QStringList &args) {
558 Q_UNUSED(cmd)
559 Q_UNUSED(args)
560 if (!mChat->mSDB) return;
561 QStringList ar(mChat->mSDB->keys());
562 QString txt;
563 foreach (const QString &kn, ar) {
564 if (!kn.startsWith("*<")) continue;
565 //qDebug() << kn;
566 if (!kn.endsWith("/ignored")) continue;
567 int e = kn.indexOf(">/");
568 if (e < 2) continue;
569 QString u(kn.mid(2, e-2));
570 if (!txt.isEmpty()) txt += "<br />\n";
571 txt += K8Str::escapeStr(u);
573 if (!txt.isEmpty()) mChat->optPrint(txt);
577 void EngineCommands::concmd_ilist (const QString &cmd, const QStringList &args) { concmd_ignorelist(cmd, args); }
580 void EngineCommands::concmd_otr (const QString &cmd, const QStringList &args) {
581 Q_UNUSED(cmd)
582 Q_UNUSED(args)
583 if (!mChat->mChatUser || mChat->mChatUser->isPlace()) {
584 mChat->optPrint("ERROR: OTR for what?");
585 return;
587 #ifdef USE_OTR
588 mChat->otrConnect(mChat->mChatUser->uni());
589 #else
590 mChat->optPrint("ERROR: no OTR support!");
591 #endif
595 void EngineCommands::concmd_nootr (const QString &cmd, const QStringList &args) {
596 Q_UNUSED(cmd)
597 Q_UNUSED(args)
598 if (!mChat->mChatUser || mChat->mChatUser->isPlace()) {
599 mChat->optPrint("ERROR: OTR for what?");
600 return;
602 #ifdef USE_OTR
603 mChat->otrDisconnect(mChat->mChatUser->uni());
604 #else
605 mChat->optPrint("ERROR: no OTR support!");
606 #endif
610 void EngineCommands::concmd_otrsmp (const QString &cmd, const QStringList &args) {
611 Q_UNUSED(cmd)
612 Q_UNUSED(args)
613 if (args.count() < 1) {
614 mChat->optPrint("ERROR: OTR SMP with what secret?");
615 return;
617 if (!mChat->mChatUser || mChat->mChatUser->isPlace() || !mChat->mChatUser->isOTRActive()) {
618 mChat->optPrint("ERROR: OTR SMP for what?");
619 return;
621 QString qq; if (args.count() > 1) qq = args[1];
622 mChat->otrInitiateSMP(mChat->mChatUser->uni(), args[0], qq);
626 void EngineCommands::concmd_otrfinger (const QString &cmd, const QStringList &args) {
627 Q_UNUSED(cmd)
628 Q_UNUSED(args)
629 if (!mChat->mChatUser || mChat->mChatUser->isPlace()) {
630 mChat->optPrint("ERROR: OTR for what?");
631 return;
633 QString ff(mChat->otrGetFinger(mChat->mChatUser->uni()));
634 mChat->optPrint(QString("OTR fp: %1").arg(ff));
638 void EngineCommands::concmd_float (const QString &cmd, const QStringList &args) {
639 Q_UNUSED(cmd)
640 Q_UNUSED(args)
641 QStringList lst;
643 if (args.count() < 1) {
644 if (mChat->mChatUser) lst << mChat->mChatUser->uni();
645 } else lst = mChat->findByUniPart(cmd);
646 if (lst.size() < 1) mChat->optPrint("person/place not found");
647 else if (lst.size() > 1) mChat->optPrint("found persons/places:<br />\n"+lst.join("<br />\n"));
648 else {
649 PsycContact *cc = mChat->findContact(lst[0]);
650 if (cc && cc->isPlace()) mChat->optPrint("ERROR: can't open floaty for place");
651 else if (cc) {
652 if (cmd == "float") {
653 // add
654 FloatingWindow *w = mChat->mFMan->produce(cc->uni().toLower(), cc->verbatim(), cc->uni(), cc->status());
655 if (!w) {
656 mChat->optPrint("error adding floaty for "+cc->uni());
657 return;
659 QString s(cc->statusText().trimmed());
660 if (!s.isEmpty()) {
661 s = K8Str::wrap(s, 60);
662 mChat->mFMan->newTip(cc->uni(), cc->uni()+"<hr>"+K8Str::escapeStr(s));
664 mChat->optPrint("floaty added for "+cc->uni());
665 } else {
666 // remove
667 mChat->mFMan->remove(cc->uni().toLower());
668 mChat->optPrint("floaty removed for "+cc->uni());
670 mChat->mFMan->saveFState();
671 } else mChat->optPrint("ERROR: can't find contact");
676 void EngineCommands::concmd_unfloat (const QString &cmd, const QStringList &args) { concmd_float(cmd, args); }
679 void EngineCommands::concmd_forcechat (const QString &cmd, const QStringList &args) {
680 Q_UNUSED(cmd)
681 Q_UNUSED(args)
682 if (args.count() < 1) {
683 mChat->optPrint("chat with what?");
684 return;
686 PsycEntity u(args[0]);
687 if (!u.isValid()) {
688 u.setUNI(mChat->mProto->uni());
689 if (!u.setNick(args[0])) u.clear();
691 if (u.isValid()) mChat->startChatWith(u.uni());
692 else mChat->optPrint("ERROR: can't open chat");
696 void EngineCommands::concmd_chat (const QString &cmd, const QStringList &args) {
697 Q_UNUSED(cmd)
698 Q_UNUSED(args)
699 if (args.count() < 1) {
700 mChat->optPrint("chat with what?");
701 return;
703 QStringList lst(mChat->findByUniPart(args[0]));
704 if (lst.size() < 1) mChat->optPrint("person/place not found");
705 else if (lst.size() > 1) mChat->optPrint("found persons/places:<br />\n"+lst.join("<br />\n"));
706 else {
707 PsycContact *cc = mChat->findContact(lst[0]);
708 if (cc) mChat->startChatWith(cc->uni());
709 else mChat->optPrint("ERROR: can't open chat");
714 void EngineCommands::concmd_find (const QString &cmd, const QStringList &args) {
715 Q_UNUSED(cmd)
716 Q_UNUSED(args)
717 if (args.count() < 1) {
718 mChat->optPrint("find what?");
719 return;
721 QStringList lst(mChat->findByUniPart(args[0]));
722 if (lst.size() < 1) mChat->optPrint("nothing's found");
723 else mChat->optPrint(lst.join("<br />\n"));
727 void EngineCommands::concmd_handle (const QString &cmd, const QStringList &args) {
728 Q_UNUSED(cmd)
729 Q_UNUSED(args)
730 if (!mChat->mChatUser) {
731 mChat->optPrint("ERROR: handle for what?");
732 return;
734 QString t(args[0]);
735 if (t.isEmpty()) {
736 mChat->optPrint("ERROR: what handle?");
737 return;
739 mChat->mChatUser->setVerbatim(t);
740 mChat->mFMan->newTitle(mChat->mChatUser->uni(), t);
741 mChat->saveOneContact(mChat->mChatUser);
742 mChat->redrawContact(mChat->mChatUser);
746 void EngineCommands::concmd_bind (const QString &cmd, const QStringList &args) {
747 Q_UNUSED(cmd)
748 if (args.length() < 1) {
749 mChat->optPrint("ERROR: keycombo?");
750 return;
752 QString b = ""; if (args.length() > 1) b = args[1];
753 if (!mChat->mComboList->bind(args[0], b)) {
754 mChat->optPrint("ERROR: invalid keycombo!");
755 return;
757 mChat->optPrint(args[0]+" binded to: "+b);
758 mChat->refreshBindings();
762 void EngineCommands::concmd_ebind (const QString &cmd, const QStringList &args) {
763 Q_UNUSED(cmd)
764 if (args.length() < 1) {
765 mChat->optPrint("ERROR: keycombo?");
766 return;
768 QString b = ""; if (args.length() > 1) b = args[1];
769 if (!mChat->mEditComboList->bind(args[0], b)) {
770 mChat->optPrint("ERROR: invalid keycombo!");
771 return;
773 mChat->optPrint(args[0]+" ebinded to: "+b);
774 mChat->refreshBindings();
778 void EngineCommands::concmd_savebinds (const QString &cmd, const QStringList &args) {
779 Q_UNUSED(cmd)
780 Q_UNUSED(args)
781 QStringList bl;
782 QString s;
783 if (mChat->mAccName.isEmpty()) return;
784 bl = mChat->mComboList->toString();
785 for (int f = 0; f < bl.length()-1; f += 2) {
786 s += "(global-key-bind "+bl[f]+" "+bl[f+1]+")\n";
788 bl = mChat->mEditComboList->toString();
789 for (int f = 0; f < bl.length()-1; f += 2) {
790 s += "(editor-key-bind "+bl[f]+" "+bl[f+1]+")\n";
792 //qDebug() << mChat->settingsDBPath(mChat->mAccName)+"keybinds.txt";
793 //qDebug() << s;
794 if (saveFile(settingsDBPath(mChat->mAccName)+"keybinds.txt", s)) {
795 mChat->optPrint("keybindings saved");
800 void EngineCommands::concmd_loadbinds (const QString &cmd, const QStringList &args) {
801 Q_UNUSED(cmd)
802 Q_UNUSED(args)
803 if (mChat->mAccName.isEmpty()) return;
804 if (!mChat->loadBindings(settingsDBPath(mChat->mAccName)+"keybinds.txt")) mChat->loadBindings(":/data/keybinds.txt");
805 mChat->optPrint("keybindings loaded");
806 mChat->refreshBindings();
810 void EngineCommands::concmd_showbinds (const QString &cmd, const QStringList &args) {
811 Q_UNUSED(cmd)
812 Q_UNUSED(args)
813 QStringList bl;
814 QString s;
816 if (mChat->mAccName.isEmpty()) return;
817 bl = mChat->mComboList->toString();
818 for (int f = 0; f < bl.length()-1; f += 2) {
819 s += "(global-key-bind "+bl[f]+" "+bl[f+1]+")<br />";
821 if (bl.length() > 0) s += "<hr />";
822 bl = mChat->mEditComboList->toString();
823 for (int f = 0; f < bl.length()-1; f += 2) {
824 s += "(editor-key-bind "+bl[f]+" "+bl[f+1]+")<br />\n";
826 //qDebug() << mChat->settingsDBPath(mChat->mAccName)+"keybinds.txt";
827 //qDebug() << s;
828 mChat->optPrint("<b>current bindings:</b><br />"+s);
832 void EngineCommands::concmd_historyexport (const QString &cmd, const QStringList &args) {
833 Q_UNUSED(cmd)
834 Q_UNUSED(args)
835 QString s;
836 QFile fo;
838 if (isHelpQuery(args)) {
839 mChat->optPrint("/exporthistory <b>filename</b> export current history to hif format<br />");
840 return;
842 if (!mChat->mChatUser) {
843 mChat->optPrint("please, open chat with someone first!");
844 return;
846 if (args.count() > 0) s = args[0]; else s = settingsHistoryFile(mChat->mAccName, mChat->mChatUser->uni());
847 if (s.isEmpty()) {
848 mChat->optPrint("filename?");
849 return;
851 if (s.at(s.length()-1) == '/') s += normUNI(mChat->mChatUser->uni());
852 if (s.right(4).toLower() != ".hif") s += ".hif";
853 if (s.indexOf('/') < 0) s = settingsHistoryPath(mChat->mChatUser->uni())+s;
854 mChat->optPrint("exporting history to: ["+s+"]");
855 fo.setFileName(s);
856 fo.remove();
857 if (!fo.open(QIODevice::WriteOnly)) {
858 mChat->optPrint("can't open file: ["+s+"]");
859 return;
862 HistoryFile hf(settingsHistoryFile(mChat->mAccName, mChat->mChatUser->uni()), mChat->mProto->uni());
863 if (!hf.open(HistoryFile::Read)) {
864 mChat->optPrint("can't open history!");
865 fo.close();
866 fo.remove();
867 return;
869 HistoryMessage msg;
870 int cnt = 0;
871 while (hf.read(cnt++, msg)) {
872 if (!msg.valid) continue;
873 dumpMsg(fo, msg);
875 hf.close();
876 fo.close();
877 mChat->optPrint("history exported to: ["+s+"]");
881 void EngineCommands::concmd_historyimport (const QString &cmd, const QStringList &args) {
882 Q_UNUSED(cmd)
883 Q_UNUSED(args)
884 QString s;
885 QFile fi;
887 if (isHelpQuery(args)) {
888 mChat->optPrint(
889 "/importhistory <b>filename</b> import current history from hif format<br />"
890 "&nbsp;&nbsp;&nbsp;&nbsp;(<b>WARNING!<b> current history will be <b>ERASED</b>!<br />"
892 return;
894 if (!mChat->mChatUser) {
895 mChat->optPrint("please, open chat with someone first!");
896 return;
898 if (args.count() > 0) s = args[0]; else s = settingsHistoryFile(mChat->mAccName, mChat->mChatUser->uni());
899 if (s.isEmpty()) {
900 mChat->optPrint("filename?");
901 return;
903 if (s.at(s.length()-1) == '/') s += normUNI(mChat->mChatUser->uni());
904 if (s.right(4).toLower() != ".hif") s += ".hif";
905 if (s.indexOf('/') < 0) s = settingsHistoryPath(mChat->mChatUser->uni())+s;
906 mChat->optPrint("importing history from: ["+s+"]");
907 fi.setFileName(s);
908 if (!fi.open(QIODevice::ReadOnly)) {
909 mChat->optPrint("can't open file: ["+s+"]");
910 return;
912 int mSize = fi.size();
913 uchar *mMap;
914 if (!(mMap = fi.map(0, mSize))) {
915 fi.close();
916 mChat->optPrint("can't mmap input file!");
917 return;
919 uchar *mPos = mMap;
921 HistoryFile hf(settingsHistoryFile(mChat->mAccName, mChat->mChatUser->uni()), mChat->mProto->uni());
922 hf.remove();
923 if (!hf.open(HistoryFile::Write)) {
924 mChat->optPrint("can't recreate history!");
925 fi.close();
926 return;
928 int mTotal = 0;
929 HistoryMessage msg;
930 while (hifMsgParse(msg, &mPos, &mSize)) {
931 if (msg.valid) {
932 ++mTotal;
933 hf.append(msg);
936 hf.close();
937 fi.unmap(mMap);
938 fi.close();
940 mChat->startChatWith(mChat->mChatUser->uni());
944 void EngineCommands::concmd_historyclear (const QString &cmd, const QStringList &args) {
945 Q_UNUSED(cmd)
946 Q_UNUSED(args)
947 QString s;
948 QFile fi;
950 if (!mChat->mChatUser) {
951 mChat->optPrint("please, open chat with someone first!");
952 return;
954 if (args.count() != 1 || args[0] != "TAN") {
955 mChat->optPrint("<b>accidental erase prevention system activated!</b>");
956 return;
958 HistoryFile hf(settingsHistoryFile(mChat->mAccName, mChat->mChatUser->uni()), mChat->mProto->uni());
959 hf.remove();
960 mChat->startChatWith(mChat->mChatUser->uni());
964 void EngineCommands::concmd_cleartabhistory (const QString &cmd, const QStringList &args) {
965 Q_UNUSED(cmd)
966 Q_UNUSED(args)
968 mChat->clearTabHistory();
969 mChat->optPrint("tab history cleared");