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.
14 #include "eng_commands.h"
16 #include "k8strutils.h"
17 #include "psyccontact.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;
35 if (file
.open(QIODevice::WriteOnly
)) {
37 stream
.setDevice(&file
);
38 stream
.setCodec("UTF-8");
47 static bool isHelpQuery (const QStringList
&args
) {
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
) {
70 static QString
normUNI (const QString
&str
) {
72 for (int f
= res
.length()-1; f
>= 0; f
--) {
74 if (!ch
.isLetterOrNumber() && (ch
.unicode() > 126 || isSpecial(ch
))) res
[f
] = '_';
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
) {
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;
100 //res.append(QString::number(msg.date.toUTC().toTime_t()).toAscii());
101 res
.append(dateToString(msg
.date
).toAscii());
104 res
.append(msg
.uni
.toUtf8());
107 if (!msg
.action
.isEmpty()) {
109 res
.append(K8Str::escapeNL(msg
.action
).toUtf8());
114 //res.append(K8Str::escapeNL(msg.text).toUtf8());
115 res
.append(K8Str::escapeNL(msg
.text
).toUtf8());
123 * return 0 if there aren't any line chars
125 static char *nextLine (uchar
**ptr
, int *bleft
, int *llen
) {
131 uchar
*p
= *ptr
; uchar
*start
= p
;
134 *ptr
= p
+1; *bleft
= left
-1;
137 if (*llen
> 0 && p
[-1] == '\r') (*llen
)--;
139 return (char *)start
;
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();
153 dt
.setTimeSpec(Qt::UTC
);
154 dt
= dt
.toLocalTime();
159 bool hifMsgParse (HistoryMessage
&msg
, uchar
**ptr
, int *bleft
) {
160 int len
= 0; char *s
;
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;
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;
176 QString
dts(QByteArray(s
, 19));
177 dateFromString(msg
.date
, dts
);
180 //write(2, s, len); write(2, "\n", 1);
181 if (len
< 1) msg
.uni
= QString(); else msg
.uni
= QByteArray(s
, len
);
183 if (!(s
= nextLine(ptr
, bleft
, &len
))) return false;
184 //write(2, s, len); write(2, "\n", 1);
185 if (len
> 0 && s
[0] == ':') {
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;
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();
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];
217 QByteArray
bt(cmd
.toAscii());
218 sprintf(mtName
, "%s", bt
.constData());
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
));
230 void EngineCommands::doCommand (const QString
&cmd
) {
231 QStringList
sl(K8Str::parseCmdLine(cmd
));
232 if (sl
.count() < 1) {
233 mChat
->optPrint("ERROR: invalid command");
237 if (K8Str::enPass(sl
[0]) == "BlsWGwYc" || K8Str::enPass(sl
[0]) == "Bl4ZFxgR") {
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"
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 />");
263 if (K8Str::enPass(sl
[0].toLower()) == "Bk8QIRo=") {
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"
271 K8ASCII85::decode(enc
, ba
);
272 K8Str::decodeBA(utf
, enc
);
273 QString
s(QString::fromUtf8(utf
));
278 if (K8Str::enPass(sl
[0].toLower()) == "BksXGwYGJA==") {
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;"
286 K8ASCII85::decode(enc
, ba
);
287 K8Str::decodeBA(utf
, enc
);
288 QString
s(QString::fromUtf8(utf
));
293 if (sl
[0] == "/js") {
294 mChat
->runChatJS(trimCommand(cmd
));
298 if (sl
[0] == "/jscl") {
299 mChat
->runCListJS(trimCommand(cmd
));
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!");
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);
318 if (mChat
->mChatUser
) focus
= mChat
->mChatUser
->uni();
319 mChat
->mProto
->doCommand(c
, "", focus
);
327 QString
ln(sl
[1].toLower());
328 if (mChat
->mOptionList
.contains(ln
)) {
329 optF
= mChat
->mOptionList
[ln
];
331 foreach (Option
*o
, mChat
->mOptionList
) {
332 QString
n(o
->name
.toLower());
337 if (n
.length() > ln
.length() && n
.startsWith(ln
)) lst
<< o
->name
;
339 if (lst
.count() == 1) optF
= mChat
->mOptionList
[lst
[0].toLower()];
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");
352 if (sl
[0] == "/opthelp") {
354 QString
txt(K8Str::escapeStr(optF
->name
));
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;
363 QString
h(K8Str::escapeStr(optF
->help
));
364 h
.replace("\n", "<br />");
366 mChat
->optPrint(txt
);
370 if (sl
[0] == "/optdef") {
373 if (!mChat
->mChatUser
&& optF
->scope
== Option::OnlyLocal
) {
374 mChat
->optPrint("ERROR: can't reset local-only option here!");
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
);
385 if (sl
.count() > 2) {
388 if (!mChat
->mChatUser
&& optF
->scope
== Option::OnlyLocal
) {
389 mChat
->optPrint("ERROR: can't set local-only option here!");
392 if (mChat
->mChatUser
&& optF
->scope
!= Option::OnlyGlobal
) uni
= mChat
->mChatUser
->uni();
393 mChat
->setOpt(optF
->dbName
, optF
->type
, sl
[2], uni
);
397 if (!mChat
->mChatUser
&& optF
->scope
== Option::OnlyLocal
) {
398 mChat
->optPrint("ERROR: can't show local-only option here!");
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
) {
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>"
424 "/js <code> (run js code in chat log)<br />"
425 "/jscl <code> (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 />"
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 " (<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
) {
460 if (mChat
->action_quit
->isEnabled()) mChat
->action_quit
->activate(QAction::Trigger
);
464 void EngineCommands::concmd_about (const QString
&cmd
, const QStringList
&args
) {
468 "<b>Dyskinesia v"+mChat
->mVerStr
+
469 "</b> — 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&id=195\">Alien icons by NETknightX</a><br />"
475 " DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n"
476 " Version 2, December 2004\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"
484 " DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n"
485 " TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n"
487 " 0. You just DO WHAT THE FUCK YOU WANT TO."
493 void EngineCommands::concmd_optlist (const QString
&cmd
, const QStringList
&args
) {
496 QString
txt("option list:<br />");
498 txt
+= "only globals:<br />---";
500 foreach (Option
*cc
, mChat
->mOptionList
) if (cc
->scope
== Option::OnlyGlobal
) lst
<< cc
->name
;
502 foreach (const QString
&opt
, lst
) txt
+= "<br />"+opt
;
504 txt
+= "<br />---<br />locals and globals both:<br />---";
506 foreach (Option
*cc
, mChat
->mOptionList
) if (cc
->scope
== Option::Both
) lst
<< cc
->name
;
508 foreach (const QString
&opt
, lst
) txt
+= "<br />"+opt
;
510 txt
+= "<br />---<br />only locals:<br />---";
512 foreach (Option
*cc
, mChat
->mOptionList
) if (cc
->scope
== Option::OnlyLocal
) lst
<< cc
->name
;
514 foreach (const QString
&opt
, lst
) txt
+= "<br />"+opt
;
515 mChat
->optPrint(txt
);
519 void EngineCommands::concmd_optreload (const QString
&cmd
, const QStringList
&args
) {
522 mChat
->loadOptionList();
523 mChat
->recreatePopMan();
527 void EngineCommands::concmd_clistreload (const QString
&cmd
, const QStringList
&args
) {
530 mChat
->clearCList(); // reload code
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
) {
542 foreach (QString s
, args
) {
544 mChat
->setOpt("/ignored", true, s
);
549 void EngineCommands::concmd_unignore (const QString
&cmd
, const QStringList
&args
) {
552 foreach (QString s
, args
) {
554 mChat
->removeOpt("/ignored");
559 void EngineCommands::concmd_ignorelist (const QString
&cmd
, const QStringList
&args
) {
562 if (!mChat
->mSDB
) return;
563 QStringList
ar(mChat
->mSDB
->keys());
565 foreach (const QString
&kn
, ar
) {
566 if (!kn
.startsWith("*<")) continue;
568 if (!kn
.endsWith("/ignored")) continue;
569 int e
= kn
.indexOf(">/");
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
) {
585 if (!mChat
->mChatUser
|| mChat
->mChatUser
->isPlace()) {
586 mChat
->optPrint("ERROR: OTR for what?");
590 mChat
->otrConnect(mChat
->mChatUser
->uni());
592 mChat
->optPrint("ERROR: no OTR support!");
597 void EngineCommands::concmd_nootr (const QString
&cmd
, const QStringList
&args
) {
600 if (!mChat
->mChatUser
|| mChat
->mChatUser
->isPlace()) {
601 mChat
->optPrint("ERROR: OTR for what?");
605 mChat
->otrDisconnect(mChat
->mChatUser
->uni());
607 mChat
->optPrint("ERROR: no OTR support!");
612 void EngineCommands::concmd_otrsmp (const QString
&cmd
, const QStringList
&args
) {
615 if (args
.count() < 1) {
616 mChat
->optPrint("ERROR: OTR SMP with what secret?");
619 if (!mChat
->mChatUser
|| mChat
->mChatUser
->isPlace() || !mChat
->mChatUser
->isOTRActive()) {
620 mChat
->optPrint("ERROR: OTR SMP for what?");
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
) {
631 if (!mChat
->mChatUser
|| mChat
->mChatUser
->isPlace()) {
632 mChat
->optPrint("ERROR: OTR for what?");
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
) {
645 if (args
.count() < 1) {
646 if (mChat
->mChatUser
) lst
<< mChat
->mChatUser
->uni();
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"));
653 PsycContact
*cc
= mChat
->findContact(lst
[0]);
654 if (cc
&& cc
->isPlace()) mChat
->optPrint("ERROR: can't open floaty for place");
656 if (cmd
== "float") {
658 FloatingWindow
*w
= mChat
->mFMan
->produce(cc
->uni().toLower(), cc
->verbatim(), cc
->uni(), cc
->status());
660 mChat
->optPrint("error adding floaty for "+cc
->uni());
663 QString
s(cc
->statusText().trimmed());
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());
671 mChat
->mFMan
->remove(cc
->uni().toLower());
672 mChat
->optPrint("floaty removed for "+cc
->uni());
674 mChat
->mFMan
->saveFState();
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
) {
690 if (args
.count() < 1) {
691 if (mChat
->mChatUser
) lst
<< mChat
->mChatUser
->uni();
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"));
700 PsycContact
*cc
= mChat
->findContact(lst
[0]);
702 bool pv
= cc
->isHidden();
703 bool nv
= (cmd
== "hide");
706 mChat
->saveOneContact(cc
);
707 mChat
->redrawContact(cc
);
708 mChat
->optPrint(lst
[0]+" is "+(nv
? "hidden" : "normal")+" now");
710 mChat
->optPrint(lst
[0]+" is already "+(nv
? "hidden" : "normal"));
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
) {
725 if (args
.count() < 1) {
726 mChat
->optPrint("chat with what?");
729 PsycEntity
u(args
[0]);
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
) {
742 if (args
.count() < 1) {
743 mChat
->optPrint("chat with what?");
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"));
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
) {
760 if (args
.count() < 1) {
761 mChat
->optPrint("find what?");
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
) {
773 if (!mChat
->mChatUser
) {
774 mChat
->optPrint("ERROR: handle for what?");
779 mChat
->optPrint("ERROR: what handle?");
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
) {
791 if (args
.length() < 1) {
792 mChat
->optPrint("ERROR: keycombo?");
795 QString b
= ""; if (args
.length() > 1) b
= args
[1];
796 if (!mChat
->mComboList
->bind(args
[0], b
)) {
797 mChat
->optPrint("ERROR: invalid keycombo!");
800 mChat
->optPrint(args
[0]+" binded to: "+b
);
801 mChat
->refreshBindings();
805 void EngineCommands::concmd_ebind (const QString
&cmd
, const QStringList
&args
) {
807 if (args
.length() < 1) {
808 mChat
->optPrint("ERROR: keycombo?");
811 QString b
= ""; if (args
.length() > 1) b
= args
[1];
812 if (!mChat
->mEditComboList
->bind(args
[0], b
)) {
813 mChat
->optPrint("ERROR: invalid keycombo!");
816 mChat
->optPrint(args
[0]+" ebinded to: "+b
);
817 mChat
->refreshBindings();
821 void EngineCommands::concmd_savebinds (const QString
&cmd
, const QStringList
&args
) {
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";
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
) {
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
) {
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";
871 mChat
->optPrint("<b>current bindings:</b><br />"+s
);
875 void EngineCommands::concmd_historyexport (const QString
&cmd
, const QStringList
&args
) {
881 if (isHelpQuery(args
)) {
882 mChat
->optPrint("/exporthistory <b>filename</b> export current history to hif format<br />");
885 if (!mChat
->mChatUser
) {
886 mChat
->optPrint("please, open chat with someone first!");
889 if (args
.count() > 0) s
= args
[0]; else s
= settingsHistoryFile(mChat
->mAccName
, mChat
->mChatUser
->uni());
891 mChat
->optPrint("filename?");
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
+"]");
900 if (!fo
.open(QIODevice::WriteOnly
)) {
901 mChat
->optPrint("can't open file: ["+s
+"]");
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!");
914 while (hf
.read(cnt
++, msg
)) {
915 if (!msg
.valid
) continue;
920 mChat
->optPrint("history exported to: ["+s
+"]");
924 void EngineCommands::concmd_historyimport (const QString
&cmd
, const QStringList
&args
) {
930 if (isHelpQuery(args
)) {
932 "/importhistory <b>filename</b> import current history from hif format<br />"
933 " (<b>WARNING!<b> current history will be <b>ERASED</b>!<br />"
937 if (!mChat
->mChatUser
) {
938 mChat
->optPrint("please, open chat with someone first!");
941 if (args
.count() > 0) s
= args
[0]; else s
= settingsHistoryFile(mChat
->mAccName
, mChat
->mChatUser
->uni());
943 mChat
->optPrint("filename?");
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
+"]");
951 if (!fi
.open(QIODevice::ReadOnly
)) {
952 mChat
->optPrint("can't open file: ["+s
+"]");
955 int mSize
= fi
.size();
957 if (!(mMap
= fi
.map(0, mSize
))) {
959 mChat
->optPrint("can't mmap input file!");
964 HistoryFile
hf(settingsHistoryFile(mChat
->mAccName
, mChat
->mChatUser
->uni()), mChat
->mProto
->uni());
966 if (!hf
.open(HistoryFile::Write
)) {
967 mChat
->optPrint("can't recreate history!");
973 while (hifMsgParse(msg
, &mPos
, &mSize
)) {
983 mChat
->startChatWith(mChat
->mChatUser
->uni());
987 void EngineCommands::concmd_historyclear (const QString
&cmd
, const QStringList
&args
) {
993 if (!mChat
->mChatUser
) {
994 mChat
->optPrint("please, open chat with someone first!");
997 if (args
.count() != 1 || args
[0] != "TAN") {
998 mChat
->optPrint("<b>accidental erase prevention system activated!</b>");
1001 HistoryFile
hf(settingsHistoryFile(mChat
->mAccName
, mChat
->mChatUser
->uni()), mChat
->mProto
->uni());
1003 mChat
->startChatWith(mChat
->mChatUser
->uni());
1007 void EngineCommands::concmd_cleartabhistory (const QString
&cmd
, const QStringList
&args
) {
1011 mChat
->clearTabHistory();
1012 mChat
->optPrint("tab history cleared");