Show invite menu in wlm chat window immediately
[kdenetwork.git] / kopete / libkopete / kopetecommandhandler.cpp
blob376b769a4eb116e8cd28eaa40282475db372b032
1 /*
2 kopetecommandhandler.cpp - Command Handler
4 Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
5 Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
7 *************************************************************************
8 * *
9 * This library is free software; you can redistribute it and/or *
10 * modify it under the terms of the GNU Lesser General Public *
11 * License as published by the Free Software Foundation; either *
12 * version 2 of the License, or (at your option) any later version. *
13 * *
14 *************************************************************************
17 #include <qregexp.h>
18 #include <QList>
19 #include <QApplication>
20 #include <kdebug.h>
21 #include <klocale.h>
22 #include <kdeversion.h>
23 #include <kxmlguiclient.h>
24 #include <kaction.h>
25 #include <qdom.h>
26 #include <kauthorized.h>
27 #include <kactioncollection.h>
28 #ifndef Q_OS_WIN
29 #include <k3process.h>
30 #else
31 class K3Process : public QObject {
32 public:
33 enum RunMode {
34 NotifyOnExit,
36 enum Communication {
37 NoCommunication,
38 AllOutput,
40 K3Process(QObject *) {}
42 K3Process &operator<< (const QString &arg) { return *this; }
43 K3Process &operator<< (const char *arg) { return *this; }
44 K3Process &operator<< (const QByteArray &arg) { return *this; }
45 K3Process &operator<< (const QStringList &args) { return *this; }
46 virtual bool start (RunMode runmode=NotifyOnExit, Communication comm=NoCommunication) { return true; }
48 #endif
50 #include "kopetechatsessionmanager.h"
51 #include "kopeteprotocol.h"
52 #include "kopetepluginmanager.h"
53 #include "kopeteview.h"
54 #include "kopeteaccountmanager.h"
55 #include "kopeteaccount.h"
56 #include "kopetecommandhandler.h"
57 #include "kopetecontact.h"
58 #include "kopetecommand.h"
59 #include "kopeteonlinestatusmanager.h"
61 using Kopete::CommandList;
63 typedef QMap<QObject*, CommandList> PluginCommandMap;
64 typedef QMap<QString,QString> CommandMap;
65 typedef QPair<Kopete::ChatSession*, Kopete::Message::MessageDirection> ManagerPair;
67 class KopeteCommandGUIClient : public QObject, public KXMLGUIClient
69 public:
70 KopeteCommandGUIClient( Kopete::ChatSession *manager ) : QObject(manager), KXMLGUIClient(manager)
72 setXMLFile( QString::fromLatin1("kopetecommandui.rc") );
74 QDomDocument doc = domDocument();
75 QDomNode menu = doc.documentElement().firstChild().firstChild().firstChild();
76 CommandList mCommands = Kopete::CommandHandler::commandHandler()->commands(
77 manager->protocol()
80 CommandList::Iterator it, itEnd = mCommands.end();
81 for( it = mCommands.begin(); it != itEnd; ++it )
83 KAction *a = static_cast<KAction*>( it.value() );
84 actionCollection()->addAction( a->objectName(), a );
85 QDomElement newNode = doc.createElement( QString::fromLatin1("Action") );
86 newNode.setAttribute( QString::fromLatin1("name"), a->objectName() );
88 bool added = false;
89 for( QDomElement n = menu.firstChild().toElement();
90 !n.isNull(); n = n.nextSibling().toElement() )
92 if( a->objectName() < n.attribute(QString::fromLatin1("name")))
94 menu.insertBefore( newNode, n );
95 added = true;
96 break;
100 if( !added )
102 menu.appendChild( newNode );
106 setDOMDocument( doc );
110 struct CommandHandlerPrivate
112 PluginCommandMap pluginCommands;
113 Kopete::CommandHandler *s_handler;
114 QMap<K3Process*,ManagerPair> processMap;
115 bool inCommand;
116 QList<KAction *> m_commands;
119 CommandHandlerPrivate *Kopete::CommandHandler::p = 0L;
121 Kopete::CommandHandler::CommandHandler() : QObject( qApp )
123 p->s_handler = this;
124 p->inCommand = false;
126 CommandList mCommands;
127 mCommands.reserve(31);
128 p->pluginCommands.insert( this, mCommands );
130 registerCommand( this, QString::fromLatin1("help"), SLOT( slotHelpCommand( const QString &, Kopete::ChatSession * ) ),
131 i18n( "USAGE: /help [<command>] - Used to list available commands, or show help for a specified command." ), 0, 1 );
133 registerCommand( this, QString::fromLatin1("close"), SLOT( slotCloseCommand( const QString &, Kopete::ChatSession * ) ),
134 i18n( "USAGE: /close - Closes the current view." ) );
136 // FIXME: What's the difference with /close? The help doesn't explain it - Martijn
137 registerCommand( this, QString::fromLatin1("part"), SLOT( slotPartCommand( const QString &, Kopete::ChatSession * ) ),
138 i18n( "USAGE: /part - Closes the current view." ) );
140 registerCommand( this, QString::fromLatin1("clear"), SLOT( slotClearCommand( const QString &, Kopete::ChatSession * ) ),
141 i18n( "USAGE: /clear - Clears the active view's chat buffer." ) );
143 //registerCommand( this, QString::fromLatin1("me"), SLOT( slotMeCommand( const QString &, Kopete::ChatSession * ) ),
144 // i18n( "USAGE: /me <text> - Formats message as in '<nickname> went to the store'." ) );
146 registerCommand( this, QString::fromLatin1("away"), SLOT( slotAwayCommand( const QString &, Kopete::ChatSession * ) ),
147 i18n( "USAGE: /away [<reason>] - Marks you as away/back for the current account only." ) );
149 registerCommand( this, QString::fromLatin1("awayall"), SLOT( slotAwayAllCommand( const QString &, Kopete::ChatSession * ) ),
150 i18n( "USAGE: /awayall [<reason>] - Marks you as away/back for all accounts." ) );
152 registerCommand( this, QString::fromLatin1("say"), SLOT( slotSayCommand( const QString &, Kopete::ChatSession * ) ),
153 i18n( "USAGE: /say <text> - Say text in this chat. This is the same as just typing a message, but is very "
154 "useful for scripts." ), 1 );
156 registerCommand( this, QString::fromLatin1("exec"), SLOT( slotExecCommand( const QString &, Kopete::ChatSession * ) ),
157 i18n( "USAGE: /exec [-o] <command> - Executes the specified command and displays the output in the chat buffer. "
158 "If -o is specified, the output is sent to all members of the chat."), 1 );
160 connect( Kopete::PluginManager::self(), SIGNAL( pluginLoaded( Kopete::Plugin*) ),
161 this, SLOT(slotPluginLoaded(Kopete::Plugin*) ) );
163 connect( Kopete::ChatSessionManager::self(), SIGNAL( viewCreated( KopeteView * ) ),
164 this, SLOT( slotViewCreated( KopeteView* ) ) );
167 Kopete::CommandHandler::~CommandHandler()
169 CommandList commandList = p->pluginCommands[this];
170 while (!commandList.isEmpty())
172 Kopete::Command *value = *commandList.begin();
173 commandList.erase(commandList.begin());
174 delete value;
177 delete p;
180 Kopete::CommandHandler *Kopete::CommandHandler::commandHandler()
182 if( !p )
184 p = new CommandHandlerPrivate;
185 p->s_handler = new Kopete::CommandHandler();
188 return p->s_handler;
191 void Kopete::CommandHandler::registerCommand( QObject *parent, const QString &command, const char* handlerSlot,
192 const QString &help, uint minArgs, int maxArgs, const KShortcut &cut, const QString &pix )
194 QString lowerCommand = command.toLower();
196 Kopete::Command *mCommand = new Kopete::Command( parent, lowerCommand, handlerSlot, help,
197 Normal, QString::null, minArgs, maxArgs, cut, pix); //krazy:exclude=nullstrassign for old broken gcc
198 p->pluginCommands[ parent ].insert( lowerCommand, mCommand );
201 void Kopete::CommandHandler::unregisterCommand( QObject *parent, const QString &command )
203 if( p->pluginCommands[ parent ].contains(command) )
204 p->pluginCommands[ parent ].remove( command );
207 void Kopete::CommandHandler::registerAlias( QObject *parent, const QString &alias, const QString &formatString,
208 const QString &help, CommandType type, uint minArgs, int maxArgs, const KShortcut &cut, const QString &pix )
210 QString lowerAlias = alias.toLower();
212 Kopete::Command *mCommand = new Kopete::Command( parent, lowerAlias, 0L, help, type,
213 formatString, minArgs, maxArgs, cut, pix );
214 p->pluginCommands[ parent ].insert( lowerAlias, mCommand );
217 void Kopete::CommandHandler::unregisterAlias( QObject *parent, const QString &alias )
219 if( p->pluginCommands[ parent ].contains(alias) )
220 p->pluginCommands[ parent ].remove( alias );
223 bool Kopete::CommandHandler::processMessage( const QString &msg, Kopete::ChatSession *manager )
225 if( p->inCommand )
226 return false;
227 QRegExp splitRx( QString::fromLatin1("^/([\\S]+)(.*)") );
228 QString command;
229 QString args;
230 if(splitRx.indexIn(msg) != -1)
232 command = splitRx.cap(1);
233 args = splitRx.cap(2).mid(1);
235 else
236 return false;
238 CommandList mCommands = commands( manager->protocol() );
239 Kopete::Command *c = mCommands.value(command);
240 if(c)
242 kDebug(14010) << "Handled Command";
243 if( c->type() != SystemAlias && c->type() != UserAlias )
244 p->inCommand = true;
246 c->processCommand( args, manager );
247 p->inCommand = false;
249 return true;
252 return false;
255 bool Kopete::CommandHandler::processMessage( Kopete::Message &msg, Kopete::ChatSession *manager )
257 QString messageBody = msg.plainBody();
259 return processMessage( messageBody, manager );
262 void Kopete::CommandHandler::slotHelpCommand( const QString &args, Kopete::ChatSession *manager )
264 QString output;
265 if( args.isEmpty() )
267 int commandCount = 0;
268 output = i18n( "Available Commands:\n" );
270 CommandList mCommands = commands( manager->myself()->protocol() );
271 CommandList::Iterator it, itEnd = mCommands.end();
272 for( it = mCommands.begin(); it != itEnd; ++it )
274 output.append( it.value()->command().toUpper() + '\t' );
275 if( commandCount++ == 5 )
277 commandCount = 0;
278 output.append( '\n' );
281 output.append( i18n( "\nType /help <command> for more information." ) );
283 else
285 QString command = parseArguments( args ).front().toLower();
286 Kopete::Command *c = commands( manager->myself()->protocol() ).value( command );
287 if( c && !c->help().isNull() )
288 output = c->help();
289 else
290 output = i18n("There is no help available for '%1'.", command );
293 Kopete::Message msg(manager->myself(), manager->members());
294 msg.setDirection( Kopete::Message::Internal );
295 msg.setPlainBody( output );
297 manager->appendMessage(msg);
300 void Kopete::CommandHandler::slotSayCommand( const QString &args, Kopete::ChatSession *manager )
302 //Just say whatever is passed
303 Kopete::Message msg(manager->myself(), manager->members());
304 msg.setPlainBody( args );
305 msg.setDirection( Kopete::Message::Outbound );
307 manager->sendMessage(msg);
310 void Kopete::CommandHandler::slotExecCommand( const QString &args, Kopete::ChatSession *manager )
312 if( !args.isEmpty() )
314 K3Process *proc = 0L;
315 if ( KAuthorized::authorizeKAction( "shell_access" ) )
316 proc = new K3Process(manager);
317 if( proc )
319 *proc << QString::fromLatin1("sh") << QString::fromLatin1("-c");
321 QStringList argsList = parseArguments( args );
322 if( argsList.front() == QString::fromLatin1("-o") )
324 p->processMap.insert( proc, ManagerPair(manager, Kopete::Message::Outbound) );
325 *proc << args.section(QRegExp(QString::fromLatin1("\\s+")), 1);
327 else
329 p->processMap.insert( proc, ManagerPair(manager, Kopete::Message::Internal) );
330 *proc << args;
333 connect(proc, SIGNAL(receivedStdout(K3Process *, char *, int)), this, SLOT(slotExecReturnedData(K3Process *, char *, int)));
334 connect(proc, SIGNAL(receivedStderr(K3Process *, char *, int)), this, SLOT(slotExecReturnedData(K3Process *, char *, int)));
335 proc->start( K3Process::NotifyOnExit, K3Process::AllOutput );
337 else
339 Kopete::Message msg(manager->myself(), manager->members() );
340 msg.setDirection( Kopete::Message::Internal );
341 msg.setPlainBody( i18n( "ERROR: Shell access has been restricted on your system. The /exec command will not function." ) );
342 manager->sendMessage( msg );
347 void Kopete::CommandHandler::slotClearCommand( const QString &, Kopete::ChatSession *manager )
349 if( manager->view() )
350 manager->view()->clear();
353 void Kopete::CommandHandler::slotPartCommand( const QString &, Kopete::ChatSession *manager )
355 if( manager->view() )
356 manager->view()->closeView();
359 void Kopete::CommandHandler::slotAwayCommand( const QString &args, Kopete::ChatSession *manager )
361 bool goAway = !manager->account()->isAway();
363 if( args.isEmpty() )
364 manager->account()->setOnlineStatus( OnlineStatusManager::self()->onlineStatus(manager->account()->protocol() , goAway ? OnlineStatusManager::Away : OnlineStatusManager::Online) );
365 else
366 manager->account()->setOnlineStatus( OnlineStatusManager::self()->onlineStatus(manager->account()->protocol() , goAway ? OnlineStatusManager::Away : OnlineStatusManager::Online) , args);
369 void Kopete::CommandHandler::slotAwayAllCommand( const QString &args, Kopete::ChatSession *manager )
371 if( manager->account()->isAway() ) {
372 Kopete::AccountManager::self()->setOnlineStatus( Kopete::OnlineStatusManager::Online );
374 else {
375 Kopete::AccountManager::self()->setOnlineStatus( Kopete::OnlineStatusManager::Away, args );
379 void Kopete::CommandHandler::slotCloseCommand( const QString &, Kopete::ChatSession *manager )
381 if( manager->view() )
382 manager->view()->closeView();
385 void Kopete::CommandHandler::slotExecReturnedData(K3Process *proc, char *buff, int bufflen )
387 kDebug(14010) ;
388 QString buffer = QString::fromLocal8Bit( buff, bufflen );
389 ManagerPair mgrPair = p->processMap[ proc ];
390 Kopete::Message msg( mgrPair.first->myself(), mgrPair.first->members() );
391 msg.setDirection( mgrPair.second );
392 msg.setPlainBody( buffer );
394 if( mgrPair.second == Kopete::Message::Outbound )
395 mgrPair.first->sendMessage( msg );
396 else
397 mgrPair.first->appendMessage( msg );
400 void Kopete::CommandHandler::slotExecFinished(K3Process *proc)
402 delete proc;
403 p->processMap.remove( proc );
406 QStringList Kopete::CommandHandler::parseArguments( const QString &args )
408 QStringList arguments;
409 QRegExp quotedArgs( QString::fromLatin1("\"(.*)\"") );
410 quotedArgs.setMinimal( true );
412 if ( quotedArgs.indexIn( args ) != -1 )
414 for( int i = 0; i< quotedArgs.numCaptures(); i++ )
415 arguments.append( quotedArgs.cap(i) );
418 QStringList otherArgs = args.section( quotedArgs, 0 ).split( QRegExp(QString::fromLatin1("\\s+")), QString::SkipEmptyParts);
419 for( QStringList::Iterator it = otherArgs.begin(); it != otherArgs.end(); ++it )
420 arguments.append( *it );
422 return arguments;
425 bool Kopete::CommandHandler::commandHandled( const QString &command )
427 for( PluginCommandMap::Iterator it = p->pluginCommands.begin(); it != p->pluginCommands.end(); ++it )
429 if( it.value().value( command ) )
430 return true;
433 return false;
436 bool Kopete::CommandHandler::commandHandledByProtocol( const QString &command, Kopete::Protocol *protocol )
438 // Make sure the protocol is not NULL
439 if(!protocol)
440 return false;
442 // Fetch the commands for the protocol
443 CommandList commandList = commands( protocol );
444 CommandList::Iterator it, itEnd = commandList.end();
446 // Loop through commands and check if they match the supplied command
447 for( it = commandList.begin(); it != itEnd; ++it )
449 if( it.value()->command().toLower() == command )
450 return true;
453 // No commands found
454 return false;
457 CommandList Kopete::CommandHandler::commands( Kopete::Protocol *protocol )
459 CommandList commandList;
460 commandList.reserve(63);
462 //Add plugin user aliases first
463 addCommands( p->pluginCommands[protocol], commandList, UserAlias );
465 //Add plugin system aliases next
466 addCommands( p->pluginCommands[protocol], commandList, SystemAlias );
468 //Add the commands for this protocol next
469 addCommands( p->pluginCommands[protocol], commandList );
471 //Add plugin commands
472 for( PluginCommandMap::Iterator it = p->pluginCommands.begin(); it != p->pluginCommands.end(); ++it )
474 if( !it.key()->inherits("Kopete::Protocol") && it.key()->inherits("Kopete::Plugin") )
475 addCommands( it.value(), commandList );
478 //Add global user aliases first
479 addCommands( p->pluginCommands[this], commandList, UserAlias );
481 //Add global system aliases next
482 addCommands( p->pluginCommands[this], commandList, SystemAlias );
484 //Add the internal commands *last*
485 addCommands( p->pluginCommands[this], commandList );
487 return commandList;
490 void Kopete::CommandHandler::addCommands( CommandList &from, CommandList &to, CommandType type )
492 CommandList::Iterator itDict, itDictEnd = from.end();
493 for( itDict = from.begin(); itDict != itDictEnd; ++itDict )
495 if( !to.value( itDict.key() ) &&
496 ( type == Undefined || itDict.value()->type() == type ) )
497 to.insert( itDict.key(), itDict.value() );
501 void Kopete::CommandHandler::slotViewCreated( KopeteView *view )
503 new KopeteCommandGUIClient( view->msgManager() );
506 void Kopete::CommandHandler::slotPluginLoaded( Kopete::Plugin *plugin )
508 connect( plugin, SIGNAL( destroyed( QObject * ) ), this, SLOT( slotPluginDestroyed( QObject * ) ) );
509 if( !p->pluginCommands.contains( plugin ) )
511 //Create a QDict optomized for a larger # of commands, and case insensitive
512 CommandList mCommands;
513 mCommands.reserve(31);
514 p->pluginCommands.insert( plugin, mCommands );
518 void Kopete::CommandHandler::slotPluginDestroyed( QObject *plugin )
520 p->pluginCommands.remove( static_cast<Kopete::Plugin*>(plugin) );
523 #include "kopetecommandhandler.moc"
525 // vim: set noet ts=4 sts=4 sw=4: