You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
491 lines
16 KiB
491 lines
16 KiB
/*
|
|
kopetecommandhandler.cpp - Command Handler
|
|
|
|
Copyright (c) 2003 by Jason Keirstead <jason@keirstead.org>
|
|
Kopete (c) 2002-2003 by the Kopete developers <kopete-devel@kde.org>
|
|
|
|
*************************************************************************
|
|
* *
|
|
* This library is free software; you can redistribute it and/or *
|
|
* modify it under the terms of the GNU Lesser General Public *
|
|
* License as published by the Free Software Foundation; either *
|
|
* version 2 of the License, or (at your option) any later version. *
|
|
* *
|
|
*************************************************************************
|
|
*/
|
|
|
|
#include <kapplication.h>
|
|
#include <tqregexp.h>
|
|
#include <kdebug.h>
|
|
#include <klocale.h>
|
|
#include <kprocess.h>
|
|
#include <kdeversion.h>
|
|
#include <kxmlguiclient.h>
|
|
#include <kaction.h>
|
|
#include <tqdom.h>
|
|
|
|
#include "kopetechatsessionmanager.h"
|
|
#include "kopeteprotocol.h"
|
|
#include "kopetepluginmanager.h"
|
|
#include "kopeteview.h"
|
|
#include "kopeteaccountmanager.h"
|
|
#include "kopeteaccount.h"
|
|
#include "kopetecommandhandler.h"
|
|
#include "kopetecontact.h"
|
|
#include "kopetecommand.h"
|
|
|
|
using Kopete::CommandList;
|
|
|
|
typedef TQMap<TQObject*, CommandList> PluginCommandMap;
|
|
typedef TQMap<TQString,TQString> CommandMap;
|
|
typedef TQPair<Kopete::ChatSession*, Kopete::Message::MessageDirection> ManagerPair;
|
|
|
|
class KopeteCommandGUIClient : public TQObject, public KXMLGUIClient
|
|
{
|
|
public:
|
|
KopeteCommandGUIClient( Kopete::ChatSession *manager ) : TQObject(manager), KXMLGUIClient(manager)
|
|
{
|
|
setXMLFile( TQString::tqfromLatin1("kopetecommandui.rc") );
|
|
|
|
TQDomDocument doc = domDocument();
|
|
TQDomNode menu = doc.documentElement().firstChild().firstChild().firstChild();
|
|
CommandList mCommands = Kopete::CommandHandler::commandHandler()->commands(
|
|
manager->protocol()
|
|
);
|
|
|
|
for( TQDictIterator<Kopete::Command> it( mCommands ); it.current(); ++it )
|
|
{
|
|
KAction *a = static_cast<KAction*>( it.current() );
|
|
actionCollection()->insert( a );
|
|
TQDomElement newNode = doc.createElement( TQString::tqfromLatin1("Action") );
|
|
newNode.setAttribute( TQString::tqfromLatin1("name"),
|
|
TQString::tqfromLatin1( a->name() ) );
|
|
|
|
bool added = false;
|
|
for( TQDomElement n = menu.firstChild().toElement();
|
|
!n.isNull(); n = n.nextSibling().toElement() )
|
|
{
|
|
if( TQString::tqfromLatin1(a->name()) < n.attribute(TQString::tqfromLatin1("name")))
|
|
{
|
|
menu.insertBefore( newNode, n );
|
|
added = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !added )
|
|
{
|
|
menu.appendChild( newNode );
|
|
}
|
|
}
|
|
|
|
setDOMDocument( doc );
|
|
}
|
|
};
|
|
|
|
struct CommandHandlerPrivate
|
|
{
|
|
PluginCommandMap pluginCommands;
|
|
Kopete::CommandHandler *s_handler;
|
|
TQMap<KProcess*,ManagerPair> processMap;
|
|
bool inCommand;
|
|
TQPtrList<KAction> m_commands;
|
|
};
|
|
|
|
CommandHandlerPrivate *Kopete::CommandHandler::p = 0L;
|
|
|
|
Kopete::CommandHandler::CommandHandler() : TQObject( tqApp )
|
|
{
|
|
p->s_handler = this;
|
|
p->inCommand = false;
|
|
|
|
CommandList mCommands(31, false);
|
|
mCommands.setAutoDelete( true );
|
|
p->pluginCommands.insert( this, mCommands );
|
|
|
|
registerCommand( this, TQString::tqfromLatin1("help"), TQT_SLOT( slotHelpCommand( const TQString &, Kopete::ChatSession * ) ),
|
|
i18n( "USAGE: /help [<command>] - Used to list available commands, or show help for a specified command." ), 0, 1 );
|
|
|
|
registerCommand( this, TQString::tqfromLatin1("close"), TQT_SLOT( slotCloseCommand( const TQString &, Kopete::ChatSession * ) ),
|
|
i18n( "USAGE: /close - Closes the current view." ) );
|
|
|
|
// FIXME: What's the difference with /close? The help doesn't explain it - Martijn
|
|
registerCommand( this, TQString::tqfromLatin1("part"), TQT_SLOT( slotPartCommand( const TQString &, Kopete::ChatSession * ) ),
|
|
i18n( "USAGE: /part - Closes the current view." ) );
|
|
|
|
registerCommand( this, TQString::tqfromLatin1("clear"), TQT_SLOT( slotClearCommand( const TQString &, Kopete::ChatSession * ) ),
|
|
i18n( "USAGE: /clear - Clears the active view's chat buffer." ) );
|
|
|
|
//registerCommand( this, TQString::tqfromLatin1("me"), TQT_SLOT( slotMeCommand( const TQString &, Kopete::ChatSession * ) ),
|
|
// i18n( "USAGE: /me <text> - Formats message as in '<nickname> went to the store'." ) );
|
|
|
|
registerCommand( this, TQString::tqfromLatin1("away"), TQT_SLOT( slotAwayCommand( const TQString &, Kopete::ChatSession * ) ),
|
|
i18n( "USAGE: /away [<reason>] - Marks you as away/back for the current account only." ) );
|
|
|
|
registerCommand( this, TQString::tqfromLatin1("awayall"), TQT_SLOT( slotAwayAllCommand( const TQString &, Kopete::ChatSession * ) ),
|
|
i18n( "USAGE: /awayall [<reason>] - Marks you as away/back for all accounts." ) );
|
|
|
|
registerCommand( this, TQString::tqfromLatin1("say"), TQT_SLOT( slotSayCommand( const TQString &, Kopete::ChatSession * ) ),
|
|
i18n( "USAGE: /say <text> - Say text in this chat. This is the same as just typing a message, but is very "
|
|
"useful for scripts." ), 1 );
|
|
|
|
registerCommand( this, TQString::tqfromLatin1("exec"), TQT_SLOT( slotExecCommand( const TQString &, Kopete::ChatSession * ) ),
|
|
i18n( "USAGE: /exec [-o] <command> - Executes the specified command and displays the output in the chat buffer. "
|
|
"If -o is specified, the output is sent to all members of the chat."), 1 );
|
|
|
|
connect( Kopete::PluginManager::self(), TQT_SIGNAL( pluginLoaded( Kopete::Plugin*) ),
|
|
this, TQT_SLOT(slotPluginLoaded(Kopete::Plugin*) ) );
|
|
|
|
connect( Kopete::ChatSessionManager::self(), TQT_SIGNAL( viewCreated( KopeteView * ) ),
|
|
this, TQT_SLOT( slotViewCreated( KopeteView* ) ) );
|
|
}
|
|
|
|
Kopete::CommandHandler::~CommandHandler()
|
|
{
|
|
delete p;
|
|
}
|
|
|
|
Kopete::CommandHandler *Kopete::CommandHandler::commandHandler()
|
|
{
|
|
if( !p )
|
|
{
|
|
p = new CommandHandlerPrivate;
|
|
p->s_handler = new Kopete::CommandHandler();
|
|
}
|
|
|
|
return p->s_handler;
|
|
}
|
|
|
|
void Kopete::CommandHandler::registerCommand( TQObject *parent, const TQString &command, const char* handlerSlot,
|
|
const TQString &help, uint minArgs, int maxArgs, const KShortcut &cut, const TQString &pix )
|
|
{
|
|
TQString lowerCommand = command.lower();
|
|
|
|
Kopete::Command *mCommand = new Kopete::Command( parent, lowerCommand, handlerSlot, help,
|
|
Normal, TQString(), minArgs, maxArgs, cut, pix);
|
|
p->pluginCommands[ parent ].insert( lowerCommand, mCommand );
|
|
}
|
|
|
|
void Kopete::CommandHandler::unregisterCommand( TQObject *parent, const TQString &command )
|
|
{
|
|
if( p->pluginCommands[ parent ].find(command) )
|
|
p->pluginCommands[ parent ].remove( command );
|
|
}
|
|
|
|
void Kopete::CommandHandler::registerAlias( TQObject *parent, const TQString &alias, const TQString &formatString,
|
|
const TQString &help, CommandType type, uint minArgs, int maxArgs, const KShortcut &cut, const TQString &pix )
|
|
{
|
|
TQString lowerAlias = alias.lower();
|
|
|
|
Kopete::Command *mCommand = new Kopete::Command( parent, lowerAlias, 0L, help, type,
|
|
formatString, minArgs, maxArgs, cut, pix );
|
|
p->pluginCommands[ parent ].insert( lowerAlias, mCommand );
|
|
}
|
|
|
|
void Kopete::CommandHandler::unregisterAlias( TQObject *parent, const TQString &alias )
|
|
{
|
|
if( p->pluginCommands[ parent ].find(alias) )
|
|
p->pluginCommands[ parent ].remove( alias );
|
|
}
|
|
|
|
bool Kopete::CommandHandler::processMessage( const TQString &msg, Kopete::ChatSession *manager )
|
|
{
|
|
if( p->inCommand )
|
|
return false;
|
|
TQRegExp splitRx( TQString::tqfromLatin1("^/([\\S]+)(.*)") );
|
|
TQString command;
|
|
TQString args;
|
|
if(splitRx.search(msg) != -1)
|
|
{
|
|
command = splitRx.cap(1);
|
|
args = splitRx.cap(2).mid(1);
|
|
}
|
|
else
|
|
return false;
|
|
|
|
CommandList mCommands = commands( manager->protocol() );
|
|
Kopete::Command *c = mCommands[ command ];
|
|
if(c)
|
|
{
|
|
kdDebug(14010) << k_funcinfo << "Handled Command" << endl;
|
|
if( c->type() != SystemAlias && c->type() != UserAlias )
|
|
p->inCommand = true;
|
|
|
|
c->processCommand( args, manager );
|
|
p->inCommand = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Kopete::CommandHandler::processMessage( Kopete::Message &msg, Kopete::ChatSession *manager )
|
|
{
|
|
TQString messageBody = msg.plainBody();
|
|
|
|
return processMessage( messageBody, manager );
|
|
}
|
|
|
|
void Kopete::CommandHandler::slotHelpCommand( const TQString &args, Kopete::ChatSession *manager )
|
|
{
|
|
TQString output;
|
|
if( args.isEmpty() )
|
|
{
|
|
int commandCount = 0;
|
|
output = i18n( "Available Commands:\n" );
|
|
|
|
CommandList mCommands = commands( manager->myself()->protocol() );
|
|
TQDictIterator<Kopete::Command> it( mCommands );
|
|
for( ; it.current(); ++it )
|
|
{
|
|
output.append( it.current()->command().upper() + '\t' );
|
|
if( commandCount++ == 5 )
|
|
{
|
|
commandCount = 0;
|
|
output.append( '\n' );
|
|
}
|
|
}
|
|
output.append( i18n( "\nType /help <command> for more information." ) );
|
|
}
|
|
else
|
|
{
|
|
TQString command = parseArguments( args ).front().lower();
|
|
Kopete::Command *c = commands( manager->myself()->protocol() )[ command ];
|
|
if( c && !c->help().isNull() )
|
|
output = c->help();
|
|
else
|
|
output = i18n("There is no help available for '%1'.").tqarg( command );
|
|
}
|
|
|
|
Kopete::Message msg(manager->myself(), manager->members(), output, Kopete::Message::Internal, Kopete::Message::PlainText);
|
|
manager->appendMessage(msg);
|
|
}
|
|
|
|
void Kopete::CommandHandler::slotSayCommand( const TQString &args, Kopete::ChatSession *manager )
|
|
{
|
|
//Just say whatever is passed
|
|
Kopete::Message msg(manager->myself(), manager->members(), args,
|
|
Kopete::Message::Outbound, Kopete::Message::PlainText);
|
|
manager->sendMessage(msg);
|
|
}
|
|
|
|
void Kopete::CommandHandler::slotExecCommand( const TQString &args, Kopete::ChatSession *manager )
|
|
{
|
|
if( !args.isEmpty() )
|
|
{
|
|
KProcess *proc = 0L;
|
|
if ( kapp->authorize( TQString::tqfromLatin1( "shell_access" ) ) )
|
|
proc = new KProcess(manager);
|
|
|
|
if( proc )
|
|
{
|
|
*proc << TQString::tqfromLatin1("sh") << TQString::tqfromLatin1("-c");
|
|
|
|
TQStringList argsList = parseArguments( args );
|
|
if( argsList.front() == TQString::tqfromLatin1("-o") )
|
|
{
|
|
p->processMap.insert( proc, ManagerPair(manager, Kopete::Message::Outbound) );
|
|
*proc << args.section(TQRegExp(TQString::tqfromLatin1("\\s+")), 1);
|
|
}
|
|
else
|
|
{
|
|
p->processMap.insert( proc, ManagerPair(manager, Kopete::Message::Internal) );
|
|
*proc << args;
|
|
}
|
|
|
|
connect(proc, TQT_SIGNAL(receivedStdout(KProcess *, char *, int)), this, TQT_SLOT(slotExecReturnedData(KProcess *, char *, int)));
|
|
connect(proc, TQT_SIGNAL(receivedStderr(KProcess *, char *, int)), this, TQT_SLOT(slotExecReturnedData(KProcess *, char *, int)));
|
|
proc->start( KProcess::NotifyOnExit, KProcess::AllOutput );
|
|
}
|
|
else
|
|
{
|
|
Kopete::Message msg(manager->myself(), manager->members(),
|
|
i18n( "ERROR: Shell access has been restricted on your system. The /exec command will not function." ),
|
|
Kopete::Message::Internal, Kopete::Message::PlainText );
|
|
manager->sendMessage( msg );
|
|
}
|
|
}
|
|
}
|
|
|
|
void Kopete::CommandHandler::slotClearCommand( const TQString &, Kopete::ChatSession *manager )
|
|
{
|
|
if( manager->view() )
|
|
manager->view()->clear();
|
|
}
|
|
|
|
void Kopete::CommandHandler::slotPartCommand( const TQString &, Kopete::ChatSession *manager )
|
|
{
|
|
if( manager->view() )
|
|
manager->view()->closeView();
|
|
}
|
|
|
|
void Kopete::CommandHandler::slotAwayCommand( const TQString &args, Kopete::ChatSession *manager )
|
|
{
|
|
bool goAway = !manager->account()->isAway();
|
|
|
|
if( args.isEmpty() )
|
|
manager->account()->setAway( goAway );
|
|
else
|
|
manager->account()->setAway( goAway, args );
|
|
}
|
|
|
|
void Kopete::CommandHandler::slotAwayAllCommand( const TQString &args, Kopete::ChatSession *manager )
|
|
{
|
|
if( manager->account()->isAway() )
|
|
Kopete::AccountManager::self()->setAvailableAll();
|
|
|
|
else
|
|
{
|
|
if( args.isEmpty() )
|
|
Kopete::AccountManager::self()->setAwayAll();
|
|
else
|
|
Kopete::AccountManager::self()->setAwayAll( args );
|
|
}
|
|
}
|
|
|
|
void Kopete::CommandHandler::slotCloseCommand( const TQString &, Kopete::ChatSession *manager )
|
|
{
|
|
if( manager->view() )
|
|
manager->view()->closeView();
|
|
}
|
|
|
|
void Kopete::CommandHandler::slotExecReturnedData(KProcess *proc, char *buff, int bufflen )
|
|
{
|
|
kdDebug(14010) << k_funcinfo << endl;
|
|
TQString buffer = TQString::fromLocal8Bit( buff, bufflen );
|
|
ManagerPair mgrPair = p->processMap[ proc ];
|
|
Kopete::Message msg( mgrPair.first->myself(), mgrPair.first->members(), buffer, mgrPair.second, Kopete::Message::PlainText );
|
|
if( mgrPair.second == Kopete::Message::Outbound )
|
|
mgrPair.first->sendMessage( msg );
|
|
else
|
|
mgrPair.first->appendMessage( msg );
|
|
}
|
|
|
|
void Kopete::CommandHandler::slotExecFinished(KProcess *proc)
|
|
{
|
|
delete proc;
|
|
p->processMap.remove( proc );
|
|
}
|
|
|
|
TQStringList Kopete::CommandHandler::parseArguments( const TQString &args )
|
|
{
|
|
TQStringList arguments;
|
|
TQRegExp quotedArgs( TQString::tqfromLatin1("\"(.*)\"") );
|
|
quotedArgs.setMinimal( true );
|
|
|
|
if ( quotedArgs.search( args ) != -1 )
|
|
{
|
|
for( int i = 0; i< quotedArgs.numCaptures(); i++ )
|
|
arguments.append( quotedArgs.cap(i) );
|
|
}
|
|
|
|
TQStringList otherArgs = TQStringList::split( TQRegExp(TQString::tqfromLatin1("\\s+")), args.section( quotedArgs, 0 ) );
|
|
for( TQStringList::Iterator it = otherArgs.begin(); it != otherArgs.end(); ++it )
|
|
arguments.append( *it );
|
|
|
|
return arguments;
|
|
}
|
|
|
|
bool Kopete::CommandHandler::commandHandled( const TQString &command )
|
|
{
|
|
for( PluginCommandMap::Iterator it = p->pluginCommands.begin(); it != p->pluginCommands.end(); ++it )
|
|
{
|
|
if( it.data()[ command ] )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Kopete::CommandHandler::commandHandledByProtocol( const TQString &command, Kopete::Protocol *protocol )
|
|
{
|
|
// Make sure the protocol is not NULL
|
|
if(!protocol)
|
|
return false;
|
|
|
|
// Fetch the commands for the protocol
|
|
CommandList commandList = commands( protocol );
|
|
TQDictIterator<Kopete::Command> it ( commandList );
|
|
|
|
// Loop through commands and check if they match the supplied command
|
|
for( ; it.current(); ++it )
|
|
{
|
|
if( it.current()->command().lower() == command )
|
|
return true;
|
|
}
|
|
|
|
// No commands found
|
|
return false;
|
|
}
|
|
|
|
CommandList Kopete::CommandHandler::commands( Kopete::Protocol *protocol )
|
|
{
|
|
CommandList commandList(63, false);
|
|
|
|
//Add plugin user aliases first
|
|
addCommands( p->pluginCommands[protocol], commandList, UserAlias );
|
|
|
|
//Add plugin system aliases next
|
|
addCommands( p->pluginCommands[protocol], commandList, SystemAlias );
|
|
|
|
//Add the commands for this protocol next
|
|
addCommands( p->pluginCommands[protocol], commandList );
|
|
|
|
//Add plugin commands
|
|
for( PluginCommandMap::Iterator it = p->pluginCommands.begin(); it != p->pluginCommands.end(); ++it )
|
|
{
|
|
if( !it.key()->inherits("Kopete::Protocol") && it.key()->inherits("Kopete::Plugin") )
|
|
addCommands( it.data(), commandList );
|
|
}
|
|
|
|
//Add global user aliases first
|
|
addCommands( p->pluginCommands[this], commandList, UserAlias );
|
|
|
|
//Add global system aliases next
|
|
addCommands( p->pluginCommands[this], commandList, SystemAlias );
|
|
|
|
//Add the internal commands *last*
|
|
addCommands( p->pluginCommands[this], commandList );
|
|
|
|
return commandList;
|
|
}
|
|
|
|
void Kopete::CommandHandler::addCommands( CommandList &from, CommandList &to, CommandType type )
|
|
{
|
|
TQDictIterator<Kopete::Command> itDict( from );
|
|
for( ; itDict.current(); ++itDict )
|
|
{
|
|
if( !to[ itDict.currentKey() ] &&
|
|
( type == Undefined || itDict.current()->type() == type ) )
|
|
to.insert( itDict.currentKey(), itDict.current() );
|
|
}
|
|
}
|
|
|
|
void Kopete::CommandHandler::slotViewCreated( KopeteView *view )
|
|
{
|
|
new KopeteCommandGUIClient( view->msgManager() );
|
|
}
|
|
|
|
void Kopete::CommandHandler::slotPluginLoaded( Kopete::Plugin *plugin )
|
|
{
|
|
connect( plugin, TQT_SIGNAL( destroyed( TQObject * ) ), this, TQT_SLOT( slotPluginDestroyed( TQObject * ) ) );
|
|
if( !p->pluginCommands.contains( plugin ) )
|
|
{
|
|
//Create a TQDict optomized for a larger # of commands, and case insensitive
|
|
CommandList mCommands(31, false);
|
|
mCommands.setAutoDelete( true );
|
|
p->pluginCommands.insert( plugin, mCommands );
|
|
}
|
|
}
|
|
|
|
void Kopete::CommandHandler::slotPluginDestroyed( TQObject *plugin )
|
|
{
|
|
p->pluginCommands.remove( static_cast<Kopete::Plugin*>(plugin) );
|
|
}
|
|
|
|
#include "kopetecommandhandler.moc"
|
|
|
|
// vim: set noet ts=4 sts=4 sw=4:
|
|
|