unauthorizedAccept option
[dyskinesia.git] / src / eng_commands.cpp
blobf93340ce7aa7b4b1206316e25a6d93b9659701f3
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 "/hide <b>[uni]</b> hide this contact<br />"
443 "/show <b>[uni]</b> show this contact<br />"
444 "/bind <b>keycombo</b> [<b>bindcmd</b>] bind Emacs-like keycombo (global)<br />"
445 "/ebind <b>keycombo</b> [<b>bindcmd</b>] bind Emacs-like keycombo (editor)<br />"
446 "/savebinds save bindings<br />"
447 "/loadbinds load bindings<br />"
448 "/historyexport <b>filename</b> export current history to hif format<br />"
449 "/historyimport <b>filename</b> import current history from hif format<br />"
450 "&nbsp;&nbsp;&nbsp;&nbsp;(<b>WARNING!</b> current history will be <b>ERASED</b>!<br />"
451 "/historyclear <b>TAN</b> wipe entire history<br />"
452 "/cleartabhistory clear tab cycle history<br />"
457 void EngineCommands::concmd_quit (const QString &cmd, const QStringList &args) {
458 Q_UNUSED(cmd)
459 Q_UNUSED(args)
460 if (mChat->action_quit->isEnabled()) mChat->action_quit->activate(QAction::Trigger);
464 void EngineCommands::concmd_about (const QString &cmd, const QStringList &args) {
465 Q_UNUSED(cmd)
466 Q_UNUSED(args)
467 mChat->optPrint(
468 "<b>Dyskinesia v"+mChat->mVerStr+
469 "</b> &mdash; native <a href=\"http://about.psyc.eu/\">PSYC</a> client for GNU/Linux and windoze<br />"
470 "coded by Ketmar (<u>psyc://ketmar.no-ip.org/~Ketmar</u>)<br />"
471 "<a href=\"http://gitorious.org/projects/qt-psyc-client\">source code (git)</a><br /><br />"
472 "<a href=\"http://addons.miranda-im.org/details.php?action=viewfile&amp;id=195\">Alien icons by NETknightX</a><br />"
473 "<br />"
474 "<pre>"
475 " DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n"
476 " Version 2, December 2004\n"
477 "\n"
478 "Copyright (C) 2004 Sam Hocevar\n"
479 " 14 rue de Plaisance, 75014 Paris, France\n"
480 "Everyone is permitted to copy and distribute verbatim or modified\n"
481 "copies of this license document, and changing it is allowed as long\n"
482 "as the name is changed.\n"
483 "\n"
484 " DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n"
485 " TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n"
486 "\n"
487 " 0. You just DO WHAT THE FUCK YOU WANT TO."
488 "</pre>"
493 void EngineCommands::concmd_optlist (const QString &cmd, const QStringList &args) {
494 Q_UNUSED(cmd)
495 Q_UNUSED(args)
496 QString txt("option list:<br />");
497 // 'OnlyGlobal'
498 txt += "only globals:<br />---";
499 QStringList lst;
500 foreach (Option *cc, mChat->mOptionList) if (cc->scope == Option::OnlyGlobal) lst << cc->name;
501 lst.sort();
502 foreach (const QString &opt, lst) txt += "<br />"+opt;
503 // 'Both'
504 txt += "<br />---<br />locals and globals both:<br />---";
505 lst.clear();
506 foreach (Option *cc, mChat->mOptionList) if (cc->scope == Option::Both) lst << cc->name;
507 lst.sort();
508 foreach (const QString &opt, lst) txt += "<br />"+opt;
509 // 'OnlyLocal'
510 txt += "<br />---<br />only locals:<br />---";
511 lst.clear();
512 foreach (Option *cc, mChat->mOptionList) if (cc->scope == Option::OnlyLocal) lst << cc->name;
513 lst.sort();
514 foreach (const QString &opt, lst) txt += "<br />"+opt;
515 mChat->optPrint(txt);
519 void EngineCommands::concmd_optreload (const QString &cmd, const QStringList &args) {
520 Q_UNUSED(cmd)
521 Q_UNUSED(args)
522 mChat->loadOptionList();
523 mChat->recreatePopMan();
527 void EngineCommands::concmd_clistreload (const QString &cmd, const QStringList &args) {
528 Q_UNUSED(cmd)
529 Q_UNUSED(args)
530 mChat->clearCList(); // reload code
531 // redraw contacts
532 foreach (PsycContact *cc, mChat->mContactList) {
533 if (cc->isTemp()) continue;
534 mChat->redrawContact(cc);
539 void EngineCommands::concmd_ignore (const QString &cmd, const QStringList &args) {
540 Q_UNUSED(cmd)
541 Q_UNUSED(args)
542 foreach (QString s, args) {
543 s = s.toLower();
544 mChat->setOpt("/ignored", true, s);
549 void EngineCommands::concmd_unignore (const QString &cmd, const QStringList &args) {
550 Q_UNUSED(cmd)
551 Q_UNUSED(args)
552 foreach (QString s, args) {
553 s = s.toLower();
554 mChat->removeOpt("/ignored");
559 void EngineCommands::concmd_ignorelist (const QString &cmd, const QStringList &args) {
560 Q_UNUSED(cmd)
561 Q_UNUSED(args)
562 if (!mChat->mSDB) return;
563 QStringList ar(mChat->mSDB->keys());
564 QString txt;
565 foreach (const QString &kn, ar) {
566 if (!kn.startsWith("*<")) continue;
567 //qDebug() << kn;
568 if (!kn.endsWith("/ignored")) continue;
569 int e = kn.indexOf(">/");
570 if (e < 2) continue;
571 QString u(kn.mid(2, e-2));
572 if (!txt.isEmpty()) txt += "<br />\n";
573 txt += K8Str::escapeStr(u);
575 if (!txt.isEmpty()) mChat->optPrint(txt);
579 void EngineCommands::concmd_ilist (const QString &cmd, const QStringList &args) { concmd_ignorelist(cmd, args); }
582 void EngineCommands::concmd_otr (const QString &cmd, const QStringList &args) {
583 Q_UNUSED(cmd)
584 Q_UNUSED(args)
585 if (!mChat->mChatUser || mChat->mChatUser->isPlace()) {
586 mChat->optPrint("ERROR: OTR for what?");
587 return;
589 #ifdef USE_OTR
590 mChat->otrConnect(mChat->mChatUser->uni());
591 #else
592 mChat->optPrint("ERROR: no OTR support!");
593 #endif
597 void EngineCommands::concmd_nootr (const QString &cmd, const QStringList &args) {
598 Q_UNUSED(cmd)
599 Q_UNUSED(args)
600 if (!mChat->mChatUser || mChat->mChatUser->isPlace()) {
601 mChat->optPrint("ERROR: OTR for what?");
602 return;
604 #ifdef USE_OTR
605 mChat->otrDisconnect(mChat->mChatUser->uni());
606 #else
607 mChat->optPrint("ERROR: no OTR support!");
608 #endif
612 void EngineCommands::concmd_otrsmp (const QString &cmd, const QStringList &args) {
613 Q_UNUSED(cmd)
614 Q_UNUSED(args)
615 if (args.count() < 1) {
616 mChat->optPrint("ERROR: OTR SMP with what secret?");
617 return;
619 if (!mChat->mChatUser || mChat->mChatUser->isPlace() || !mChat->mChatUser->isOTRActive()) {
620 mChat->optPrint("ERROR: OTR SMP for what?");
621 return;
623 QString qq; if (args.count() > 1) qq = args[1];
624 mChat->otrInitiateSMP(mChat->mChatUser->uni(), args[0], qq);
628 void EngineCommands::concmd_otrfinger (const QString &cmd, const QStringList &args) {
629 Q_UNUSED(cmd)
630 Q_UNUSED(args)
631 if (!mChat->mChatUser || mChat->mChatUser->isPlace()) {
632 mChat->optPrint("ERROR: OTR for what?");
633 return;
635 QString ff(mChat->otrGetFinger(mChat->mChatUser->uni()));
636 mChat->optPrint(QString("OTR fp: %1").arg(ff));
640 void EngineCommands::concmd_float (const QString &cmd, const QStringList &args) {
641 Q_UNUSED(cmd)
642 Q_UNUSED(args)
643 QStringList lst;
645 if (args.count() < 1) {
646 if (mChat->mChatUser) lst << mChat->mChatUser->uni();
647 } else {
648 lst = mChat->findByUniPart(args[0]);
650 if (lst.size() < 1) mChat->optPrint("person/place not found");
651 else if (lst.size() > 1) mChat->optPrint("found persons/places:<br />\n"+lst.join("<br />\n"));
652 else {
653 PsycContact *cc = mChat->findContact(lst[0]);
654 if (cc && cc->isPlace()) mChat->optPrint("ERROR: can't open floaty for place");
655 else if (cc) {
656 if (cmd == "float") {
657 // add
658 FloatingWindow *w = mChat->mFMan->produce(cc->uni().toLower(), cc->verbatim(), cc->uni(), cc->status());
659 if (!w) {
660 mChat->optPrint("error adding floaty for "+cc->uni());
661 return;
663 QString s(cc->statusText().trimmed());
664 if (!s.isEmpty()) {
665 s = K8Str::wrap(s, 60);
666 mChat->mFMan->newTip(cc->uni(), cc->uni()+"<hr>"+K8Str::escapeStr(s));
668 mChat->optPrint("floaty added for "+cc->uni());
669 } else {
670 // remove
671 mChat->mFMan->remove(cc->uni().toLower());
672 mChat->optPrint("floaty removed for "+cc->uni());
674 mChat->mFMan->saveFState();
675 } else {
676 mChat->optPrint("ERROR: can't find contact");
682 void EngineCommands::concmd_unfloat (const QString &cmd, const QStringList &args) { concmd_float(cmd, args); }
685 void EngineCommands::concmd_hide (const QString &cmd, const QStringList &args) {
686 Q_UNUSED(cmd)
687 Q_UNUSED(args)
688 QStringList lst;
690 if (args.count() < 1) {
691 if (mChat->mChatUser) lst << mChat->mChatUser->uni();
692 } else {
693 lst = mChat->findByUniPart(args[0]);
695 if (lst.size() < 1) {
696 mChat->optPrint("person/place not found");
697 } else if (lst.size() > 1) {
698 mChat->optPrint("found persons/places:<br />\n"+lst.join("<br />\n"));
699 } else {
700 PsycContact *cc = mChat->findContact(lst[0]);
701 if (cc) {
702 bool pv = cc->isHidden();
703 bool nv = (cmd == "hide");
704 if (pv != nv) {
705 cc->setHidden(nv);
706 mChat->saveOneContact(cc);
707 mChat->redrawContact(cc);
708 mChat->optPrint(lst[0]+" is "+(nv ? "hidden" : "normal")+" now");
709 } else {
710 mChat->optPrint(lst[0]+" is already "+(nv ? "hidden" : "normal"));
712 } else {
713 mChat->optPrint("ERROR: can't find contact");
719 void EngineCommands::concmd_show (const QString &cmd, const QStringList &args) { concmd_hide(cmd, args); }
722 void EngineCommands::concmd_forcechat (const QString &cmd, const QStringList &args) {
723 Q_UNUSED(cmd)
724 Q_UNUSED(args)
725 if (args.count() < 1) {
726 mChat->optPrint("chat with what?");
727 return;
729 PsycEntity u(args[0]);
730 if (!u.isValid()) {
731 u.setUNI(mChat->mProto->uni());
732 if (!u.setNick(args[0])) u.clear();
734 if (u.isValid()) mChat->startChatWith(u.uni());
735 else mChat->optPrint("ERROR: can't open chat");
739 void EngineCommands::concmd_chat (const QString &cmd, const QStringList &args) {
740 Q_UNUSED(cmd)
741 Q_UNUSED(args)
742 if (args.count() < 1) {
743 mChat->optPrint("chat with what?");
744 return;
746 QStringList lst(mChat->findByUniPart(args[0]));
747 if (lst.size() < 1) mChat->optPrint("person/place not found");
748 else if (lst.size() > 1) mChat->optPrint("found persons/places:<br />\n"+lst.join("<br />\n"));
749 else {
750 PsycContact *cc = mChat->findContact(lst[0]);
751 if (cc) mChat->startChatWith(cc->uni());
752 else mChat->optPrint("ERROR: can't open chat");
757 void EngineCommands::concmd_find (const QString &cmd, const QStringList &args) {
758 Q_UNUSED(cmd)
759 Q_UNUSED(args)
760 if (args.count() < 1) {
761 mChat->optPrint("find what?");
762 return;
764 QStringList lst(mChat->findByUniPart(args[0]));
765 if (lst.size() < 1) mChat->optPrint("nothing's found");
766 else mChat->optPrint(lst.join("<br />\n"));
770 void EngineCommands::concmd_handle (const QString &cmd, const QStringList &args) {
771 Q_UNUSED(cmd)
772 Q_UNUSED(args)
773 if (!mChat->mChatUser) {
774 mChat->optPrint("ERROR: handle for what?");
775 return;
777 QString t(args[0]);
778 if (t.isEmpty()) {
779 mChat->optPrint("ERROR: what handle?");
780 return;
782 mChat->mChatUser->setVerbatim(t);
783 mChat->mFMan->newTitle(mChat->mChatUser->uni(), t);
784 mChat->saveOneContact(mChat->mChatUser);
785 mChat->redrawContact(mChat->mChatUser);
789 void EngineCommands::concmd_bind (const QString &cmd, const QStringList &args) {
790 Q_UNUSED(cmd)
791 if (args.length() < 1) {
792 mChat->optPrint("ERROR: keycombo?");
793 return;
795 QString b = ""; if (args.length() > 1) b = args[1];
796 if (!mChat->mComboList->bind(args[0], b)) {
797 mChat->optPrint("ERROR: invalid keycombo!");
798 return;
800 mChat->optPrint(args[0]+" binded to: "+b);
801 mChat->refreshBindings();
805 void EngineCommands::concmd_ebind (const QString &cmd, const QStringList &args) {
806 Q_UNUSED(cmd)
807 if (args.length() < 1) {
808 mChat->optPrint("ERROR: keycombo?");
809 return;
811 QString b = ""; if (args.length() > 1) b = args[1];
812 if (!mChat->mEditComboList->bind(args[0], b)) {
813 mChat->optPrint("ERROR: invalid keycombo!");
814 return;
816 mChat->optPrint(args[0]+" ebinded to: "+b);
817 mChat->refreshBindings();
821 void EngineCommands::concmd_savebinds (const QString &cmd, const QStringList &args) {
822 Q_UNUSED(cmd)
823 Q_UNUSED(args)
824 QStringList bl;
825 QString s;
826 if (mChat->mAccName.isEmpty()) return;
827 bl = mChat->mComboList->toString();
828 for (int f = 0; f < bl.length()-1; f += 2) {
829 s += "(global-key-bind "+bl[f]+" "+bl[f+1]+")\n";
831 bl = mChat->mEditComboList->toString();
832 for (int f = 0; f < bl.length()-1; f += 2) {
833 s += "(editor-key-bind "+bl[f]+" "+bl[f+1]+")\n";
835 //qDebug() << mChat->settingsDBPath(mChat->mAccName)+"keybinds.txt";
836 //qDebug() << s;
837 if (saveFile(settingsDBPath(mChat->mAccName)+"keybinds.txt", s)) {
838 mChat->optPrint("keybindings saved");
843 void EngineCommands::concmd_loadbinds (const QString &cmd, const QStringList &args) {
844 Q_UNUSED(cmd)
845 Q_UNUSED(args)
846 if (mChat->mAccName.isEmpty()) return;
847 if (!mChat->loadBindings(settingsDBPath(mChat->mAccName)+"keybinds.txt")) mChat->loadBindings(":/data/keybinds.txt");
848 mChat->optPrint("keybindings loaded");
849 mChat->refreshBindings();
853 void EngineCommands::concmd_showbinds (const QString &cmd, const QStringList &args) {
854 Q_UNUSED(cmd)
855 Q_UNUSED(args)
856 QStringList bl;
857 QString s;
859 if (mChat->mAccName.isEmpty()) return;
860 bl = mChat->mComboList->toString();
861 for (int f = 0; f < bl.length()-1; f += 2) {
862 s += "(global-key-bind "+bl[f]+" "+bl[f+1]+")<br />";
864 if (bl.length() > 0) s += "<hr />";
865 bl = mChat->mEditComboList->toString();
866 for (int f = 0; f < bl.length()-1; f += 2) {
867 s += "(editor-key-bind "+bl[f]+" "+bl[f+1]+")<br />\n";
869 //qDebug() << mChat->settingsDBPath(mChat->mAccName)+"keybinds.txt";
870 //qDebug() << s;
871 mChat->optPrint("<b>current bindings:</b><br />"+s);
875 void EngineCommands::concmd_historyexport (const QString &cmd, const QStringList &args) {
876 Q_UNUSED(cmd)
877 Q_UNUSED(args)
878 QString s;
879 QFile fo;
881 if (isHelpQuery(args)) {
882 mChat->optPrint("/exporthistory <b>filename</b> export current history to hif format<br />");
883 return;
885 if (!mChat->mChatUser) {
886 mChat->optPrint("please, open chat with someone first!");
887 return;
889 if (args.count() > 0) s = args[0]; else s = settingsHistoryFile(mChat->mAccName, mChat->mChatUser->uni());
890 if (s.isEmpty()) {
891 mChat->optPrint("filename?");
892 return;
894 if (s.at(s.length()-1) == '/') s += normUNI(mChat->mChatUser->uni());
895 if (s.right(4).toLower() != ".hif") s += ".hif";
896 if (s.indexOf('/') < 0) s = settingsHistoryPath(mChat->mChatUser->uni())+s;
897 mChat->optPrint("exporting history to: ["+s+"]");
898 fo.setFileName(s);
899 fo.remove();
900 if (!fo.open(QIODevice::WriteOnly)) {
901 mChat->optPrint("can't open file: ["+s+"]");
902 return;
905 HistoryFile hf(settingsHistoryFile(mChat->mAccName, mChat->mChatUser->uni()), mChat->mProto->uni());
906 if (!hf.open(HistoryFile::Read)) {
907 mChat->optPrint("can't open history!");
908 fo.close();
909 fo.remove();
910 return;
912 HistoryMessage msg;
913 int cnt = 0;
914 while (hf.read(cnt++, msg)) {
915 if (!msg.valid) continue;
916 dumpMsg(fo, msg);
918 hf.close();
919 fo.close();
920 mChat->optPrint("history exported to: ["+s+"]");
924 void EngineCommands::concmd_historyimport (const QString &cmd, const QStringList &args) {
925 Q_UNUSED(cmd)
926 Q_UNUSED(args)
927 QString s;
928 QFile fi;
930 if (isHelpQuery(args)) {
931 mChat->optPrint(
932 "/importhistory <b>filename</b> import current history from hif format<br />"
933 "&nbsp;&nbsp;&nbsp;&nbsp;(<b>WARNING!<b> current history will be <b>ERASED</b>!<br />"
935 return;
937 if (!mChat->mChatUser) {
938 mChat->optPrint("please, open chat with someone first!");
939 return;
941 if (args.count() > 0) s = args[0]; else s = settingsHistoryFile(mChat->mAccName, mChat->mChatUser->uni());
942 if (s.isEmpty()) {
943 mChat->optPrint("filename?");
944 return;
946 if (s.at(s.length()-1) == '/') s += normUNI(mChat->mChatUser->uni());
947 if (s.right(4).toLower() != ".hif") s += ".hif";
948 if (s.indexOf('/') < 0) s = settingsHistoryPath(mChat->mChatUser->uni())+s;
949 mChat->optPrint("importing history from: ["+s+"]");
950 fi.setFileName(s);
951 if (!fi.open(QIODevice::ReadOnly)) {
952 mChat->optPrint("can't open file: ["+s+"]");
953 return;
955 int mSize = fi.size();
956 uchar *mMap;
957 if (!(mMap = fi.map(0, mSize))) {
958 fi.close();
959 mChat->optPrint("can't mmap input file!");
960 return;
962 uchar *mPos = mMap;
964 HistoryFile hf(settingsHistoryFile(mChat->mAccName, mChat->mChatUser->uni()), mChat->mProto->uni());
965 hf.remove();
966 if (!hf.open(HistoryFile::Write)) {
967 mChat->optPrint("can't recreate history!");
968 fi.close();
969 return;
971 int mTotal = 0;
972 HistoryMessage msg;
973 while (hifMsgParse(msg, &mPos, &mSize)) {
974 if (msg.valid) {
975 ++mTotal;
976 hf.append(msg);
979 hf.close();
980 fi.unmap(mMap);
981 fi.close();
983 mChat->startChatWith(mChat->mChatUser->uni());
987 void EngineCommands::concmd_historyclear (const QString &cmd, const QStringList &args) {
988 Q_UNUSED(cmd)
989 Q_UNUSED(args)
990 QString s;
991 QFile fi;
993 if (!mChat->mChatUser) {
994 mChat->optPrint("please, open chat with someone first!");
995 return;
997 if (args.count() != 1 || args[0] != "TAN") {
998 mChat->optPrint("<b>accidental erase prevention system activated!</b>");
999 return;
1001 HistoryFile hf(settingsHistoryFile(mChat->mAccName, mChat->mChatUser->uni()), mChat->mProto->uni());
1002 hf.remove();
1003 mChat->startChatWith(mChat->mChatUser->uni());
1007 void EngineCommands::concmd_cleartabhistory (const QString &cmd, const QStringList &args) {
1008 Q_UNUSED(cmd)
1009 Q_UNUSED(args)
1011 mChat->clearTabHistory();
1012 mChat->optPrint("tab history cleared");