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 "/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 " (<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
) {
458 if (mChat
->action_quit
->isEnabled()) mChat
->action_quit
->activate(QAction::Trigger
);
462 void EngineCommands::concmd_about (const QString
&cmd
, const QStringList
&args
) {
466 "<b>Dyskinesia v"+mChat
->mVerStr
+
467 "</b> — 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&id=195\">Alien icons by NETknightX</a><br />"
473 " DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n"
474 " Version 2, December 2004\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"
482 " DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\n"
483 " TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION\n"
485 " 0. You just DO WHAT THE FUCK YOU WANT TO."
491 void EngineCommands::concmd_optlist (const QString
&cmd
, const QStringList
&args
) {
494 QString
txt("option list:<br />");
496 txt
+= "only globals:<br />---";
498 foreach (Option
*cc
, mChat
->mOptionList
) if (cc
->scope
== Option::OnlyGlobal
) lst
<< cc
->name
;
500 foreach (const QString
&opt
, lst
) txt
+= "<br />"+opt
;
502 txt
+= "<br />---<br />locals and globals both:<br />---";
504 foreach (Option
*cc
, mChat
->mOptionList
) if (cc
->scope
== Option::Both
) lst
<< cc
->name
;
506 foreach (const QString
&opt
, lst
) txt
+= "<br />"+opt
;
508 txt
+= "<br />---<br />only locals:<br />---";
510 foreach (Option
*cc
, mChat
->mOptionList
) if (cc
->scope
== Option::OnlyLocal
) lst
<< cc
->name
;
512 foreach (const QString
&opt
, lst
) txt
+= "<br />"+opt
;
513 mChat
->optPrint(txt
);
517 void EngineCommands::concmd_optreload (const QString
&cmd
, const QStringList
&args
) {
520 mChat
->loadOptionList();
521 mChat
->recreatePopMan();
525 void EngineCommands::concmd_clistreload (const QString
&cmd
, const QStringList
&args
) {
528 mChat
->clearCList(); // reload code
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
) {
540 foreach (QString s
, args
) {
542 mChat
->setOpt("/ignored", true, s
);
547 void EngineCommands::concmd_unignore (const QString
&cmd
, const QStringList
&args
) {
550 foreach (QString s
, args
) {
552 mChat
->removeOpt("/ignored");
557 void EngineCommands::concmd_ignorelist (const QString
&cmd
, const QStringList
&args
) {
560 if (!mChat
->mSDB
) return;
561 QStringList
ar(mChat
->mSDB
->keys());
563 foreach (const QString
&kn
, ar
) {
564 if (!kn
.startsWith("*<")) continue;
566 if (!kn
.endsWith("/ignored")) continue;
567 int e
= kn
.indexOf(">/");
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
) {
583 if (!mChat
->mChatUser
|| mChat
->mChatUser
->isPlace()) {
584 mChat
->optPrint("ERROR: OTR for what?");
588 mChat
->otrConnect(mChat
->mChatUser
->uni());
590 mChat
->optPrint("ERROR: no OTR support!");
595 void EngineCommands::concmd_nootr (const QString
&cmd
, const QStringList
&args
) {
598 if (!mChat
->mChatUser
|| mChat
->mChatUser
->isPlace()) {
599 mChat
->optPrint("ERROR: OTR for what?");
603 mChat
->otrDisconnect(mChat
->mChatUser
->uni());
605 mChat
->optPrint("ERROR: no OTR support!");
610 void EngineCommands::concmd_otrsmp (const QString
&cmd
, const QStringList
&args
) {
613 if (args
.count() < 1) {
614 mChat
->optPrint("ERROR: OTR SMP with what secret?");
617 if (!mChat
->mChatUser
|| mChat
->mChatUser
->isPlace() || !mChat
->mChatUser
->isOTRActive()) {
618 mChat
->optPrint("ERROR: OTR SMP for what?");
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
) {
629 if (!mChat
->mChatUser
|| mChat
->mChatUser
->isPlace()) {
630 mChat
->optPrint("ERROR: OTR for what?");
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
) {
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"));
649 PsycContact
*cc
= mChat
->findContact(lst
[0]);
650 if (cc
&& cc
->isPlace()) mChat
->optPrint("ERROR: can't open floaty for place");
652 if (cmd
== "float") {
654 FloatingWindow
*w
= mChat
->mFMan
->produce(cc
->uni().toLower(), cc
->verbatim(), cc
->uni(), cc
->status());
656 mChat
->optPrint("error adding floaty for "+cc
->uni());
659 QString
s(cc
->statusText().trimmed());
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());
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
) {
682 if (args
.count() < 1) {
683 mChat
->optPrint("chat with what?");
686 PsycEntity
u(args
[0]);
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
) {
699 if (args
.count() < 1) {
700 mChat
->optPrint("chat with what?");
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"));
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
) {
717 if (args
.count() < 1) {
718 mChat
->optPrint("find what?");
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
) {
730 if (!mChat
->mChatUser
) {
731 mChat
->optPrint("ERROR: handle for what?");
736 mChat
->optPrint("ERROR: what handle?");
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
) {
748 if (args
.length() < 1) {
749 mChat
->optPrint("ERROR: keycombo?");
752 QString b
= ""; if (args
.length() > 1) b
= args
[1];
753 if (!mChat
->mComboList
->bind(args
[0], b
)) {
754 mChat
->optPrint("ERROR: invalid keycombo!");
757 mChat
->optPrint(args
[0]+" binded to: "+b
);
758 mChat
->refreshBindings();
762 void EngineCommands::concmd_ebind (const QString
&cmd
, const QStringList
&args
) {
764 if (args
.length() < 1) {
765 mChat
->optPrint("ERROR: keycombo?");
768 QString b
= ""; if (args
.length() > 1) b
= args
[1];
769 if (!mChat
->mEditComboList
->bind(args
[0], b
)) {
770 mChat
->optPrint("ERROR: invalid keycombo!");
773 mChat
->optPrint(args
[0]+" ebinded to: "+b
);
774 mChat
->refreshBindings();
778 void EngineCommands::concmd_savebinds (const QString
&cmd
, const QStringList
&args
) {
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";
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
) {
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
) {
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";
828 mChat
->optPrint("<b>current bindings:</b><br />"+s
);
832 void EngineCommands::concmd_historyexport (const QString
&cmd
, const QStringList
&args
) {
838 if (isHelpQuery(args
)) {
839 mChat
->optPrint("/exporthistory <b>filename</b> export current history to hif format<br />");
842 if (!mChat
->mChatUser
) {
843 mChat
->optPrint("please, open chat with someone first!");
846 if (args
.count() > 0) s
= args
[0]; else s
= settingsHistoryFile(mChat
->mAccName
, mChat
->mChatUser
->uni());
848 mChat
->optPrint("filename?");
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
+"]");
857 if (!fo
.open(QIODevice::WriteOnly
)) {
858 mChat
->optPrint("can't open file: ["+s
+"]");
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!");
871 while (hf
.read(cnt
++, msg
)) {
872 if (!msg
.valid
) continue;
877 mChat
->optPrint("history exported to: ["+s
+"]");
881 void EngineCommands::concmd_historyimport (const QString
&cmd
, const QStringList
&args
) {
887 if (isHelpQuery(args
)) {
889 "/importhistory <b>filename</b> import current history from hif format<br />"
890 " (<b>WARNING!<b> current history will be <b>ERASED</b>!<br />"
894 if (!mChat
->mChatUser
) {
895 mChat
->optPrint("please, open chat with someone first!");
898 if (args
.count() > 0) s
= args
[0]; else s
= settingsHistoryFile(mChat
->mAccName
, mChat
->mChatUser
->uni());
900 mChat
->optPrint("filename?");
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
+"]");
908 if (!fi
.open(QIODevice::ReadOnly
)) {
909 mChat
->optPrint("can't open file: ["+s
+"]");
912 int mSize
= fi
.size();
914 if (!(mMap
= fi
.map(0, mSize
))) {
916 mChat
->optPrint("can't mmap input file!");
921 HistoryFile
hf(settingsHistoryFile(mChat
->mAccName
, mChat
->mChatUser
->uni()), mChat
->mProto
->uni());
923 if (!hf
.open(HistoryFile::Write
)) {
924 mChat
->optPrint("can't recreate history!");
930 while (hifMsgParse(msg
, &mPos
, &mSize
)) {
940 mChat
->startChatWith(mChat
->mChatUser
->uni());
944 void EngineCommands::concmd_historyclear (const QString
&cmd
, const QStringList
&args
) {
950 if (!mChat
->mChatUser
) {
951 mChat
->optPrint("please, open chat with someone first!");
954 if (args
.count() != 1 || args
[0] != "TAN") {
955 mChat
->optPrint("<b>accidental erase prevention system activated!</b>");
958 HistoryFile
hf(settingsHistoryFile(mChat
->mAccName
, mChat
->mChatUser
->uni()), mChat
->mProto
->uni());
960 mChat
->startChatWith(mChat
->mChatUser
->uni());
964 void EngineCommands::concmd_cleartabhistory (const QString
&cmd
, const QStringList
&args
) {
968 mChat
->clearTabHistory();
969 mChat
->optPrint("tab history cleared");