27 #include <sys/types.h>
37 #include <tqfileinfo.h>
40 #include <tqstringlist.h>
41 #include <tqtextstream.h>
42 #include <tqvariant.h>
44 #include "../dcopclient.h"
45 #include "../dcopref.h"
46 #include "../kdatastream.h"
48 #include "marshall.cpp"
52 #include <X11/Xatom.h>
55 typedef TQMap<TQString, TQString> UserList;
59 static TQTextStream cin_ ( stdin, IO_ReadOnly );
60 static TQTextStream cout_( stdout, IO_WriteOnly );
61 static TQTextStream cerr_( stderr, IO_WriteOnly );
72 enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession };
74 bool startsWith(
const TQCString &
id,
const char *str,
int n)
76 return !n || (strncmp(
id.data(), str, n) == 0);
79 bool endsWith(TQCString &
id,
char c)
81 if (
id.length() && (
id[
id.length()-1] == c))
83 id.truncate(
id.length()-1);
89 void queryApplications(
const TQCString &filter)
91 int filterLen = filter.length();
93 for ( QCStringList::Iterator it = apps.begin(); it != apps.end(); ++it )
95 TQCString &clientId = *it;
96 if ( (clientId != dcop->
appId()) &&
97 !startsWith(clientId,
"anonymous",9) &&
98 startsWith(clientId, filter, filterLen)
100 printf(
"%s\n", clientId.data() );
105 tqWarning(
"server not accessible" );
110 void queryObjects(
const TQCString &app,
const TQCString &filter )
112 int filterLen = filter.length();
114 bool isDefault =
false;
116 for ( QCStringList::Iterator it = objs.begin(); it != objs.end(); ++it )
118 TQCString &objId = *it;
120 if (objId ==
"default")
126 if (startsWith(objId, filter, filterLen))
129 printf(
"%s (default)\n", objId.data() );
131 printf(
"%s\n", objId.data() );
138 tqWarning(
"No such application: '%s'", app.data());
140 tqWarning(
"Application '%s' not accessible", app.data() );
145 void queryFunctions(
const char* app,
const char* obj )
149 for ( QCStringList::Iterator it = funcs.begin(); it != funcs.end(); ++it ) {
150 printf(
"%s\n", (*it).data() );
154 tqWarning(
"object '%s' in application '%s' not accessible", obj, app );
159 int callFunction(
const char* app,
const char* obj,
const char* func,
const QCStringList args )
162 int left = f.find(
'(' );
163 int right = f.find(
')' );
167 tqWarning(
"parentheses do not match" );
176 if ( !ok && args.isEmpty() )
180 tqWarning(
"object not accessible" );
183 for ( QCStringList::Iterator it = funcs.begin(); it != funcs.end(); ++it ) {
184 int l = (*it).find(
'(' );
187 s = (*it).findRev(
' ', l);
189 s = (*it).find(
' ' );
196 if ( l > 0 && (*it).mid( s, l - s ) == func ) {
197 realfunc = (*it).mid( s );
198 const TQString arguments = (*it).mid(l+1,(*it).find(
')' )-l-1);
199 uint a = arguments.contains(
',');
200 if ( (a==0 && !arguments.isEmpty()) || a>0)
202 if ( a == args.count() )
206 if ( realfunc.isEmpty() )
208 tqWarning(
"no such function");
212 left = f.find(
'(' );
213 right = f.find(
')' );
224 TQStringList intTypes;
225 intTypes <<
"int" <<
"unsigned" <<
"long" <<
"bool" ;
228 if ( left >0 && left + 1 < right - 1) {
229 types = TQStringList::split(
',', f.mid( left + 1, right - left - 1) );
230 for ( TQStringList::Iterator it = types.begin(); it != types.end(); ++it ) {
231 TQString lt = (*it).simplifyWhiteSpace();
233 int s = lt.find(
' ');
243 TQStringList partl = TQStringList::split(
' ' , lt);
253 while (s <
static_cast<int>(partl.count()) && intTypes.contains(partl[s]))
258 if ( s <
static_cast<int>(partl.count())-1)
260 tqWarning(
"The argument `%s' seems syntactically wrong.",
263 if ( s ==
static_cast<int>(partl.count())-1)
265 partl.remove(partl.at(s));
268 lt = partl.join(
" ");
269 lt = lt.simplifyWhiteSpace();
274 TQString fc = f.left( left );
277 for ( TQStringList::Iterator it = types.begin(); it != types.end(); ++it ) {
287 TQByteArray data, replyData;
289 TQDataStream arg(data, IO_WriteOnly);
292 for( TQStringList::Iterator it = types.begin(); it != types.end(); ++it ) {
293 marshall( arg, args, i, *it );
296 if ( i != args.count() )
298 tqWarning(
"arguments do not match" );
302 if ( !dcop->
call( app, obj, f.latin1(), data, replyType, replyData) ) {
303 tqWarning(
"call failed");
306 TQDataStream reply(replyData, IO_ReadOnly);
308 if ( replyType !=
"void" && replyType !=
"ASYNC" )
310 TQCString replyString = demarshal( reply, replyType );
311 if ( !replyString.isEmpty() )
312 printf(
"%s\n", replyString.data() );
323 void showHelp(
int exitCode = 0 )
326 cout_ <<
"Usage: dcopquit [options] [application]" <<
endl
328 cout_ <<
"Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" <<
endl
331 <<
"Console DCOP client" <<
endl
333 <<
"Generic options:" <<
endl
334 <<
" --help Show help about options" <<
endl
336 <<
"Options:" <<
endl
337 <<
" --pipe Call DCOP for each line read from stdin. The string '%1'" <<
endl
338 <<
" will be used in the argument list as a placeholder for" <<
endl
339 <<
" the substituted line." <<
endl
340 <<
" For example," <<
endl
341 <<
" dcop --pipe konqueror html-widget1 evalJS %1" <<
endl
342 <<
" is equivalent to calling" <<
endl
343 <<
" while read line ; do" <<
endl
344 <<
" dcop konqueror html-widget1 evalJS \"$line\"" <<
endl
346 <<
" in bash, but because no new dcop instance has to be started" <<
endl
347 <<
" for each line this is generally much faster, especially for" <<
endl
348 <<
" the slower GNU dynamic linkers." <<
endl
349 <<
" The '%1' placeholder cannot be used to replace e.g. the" <<
endl
350 <<
" program, object or method name." <<
endl
351 <<
" --user <user> Connect to the given user's DCOP server. This option will" <<
endl
352 <<
" ignore the values of the environment vars $DCOPSERVER and" <<
endl
353 <<
" $ICEAUTHORITY, even if they are set." <<
endl
354 <<
" If the user has more than one open session, you must also" <<
endl
355 <<
" use one of the --list-sessions, --session or --all-sessions" <<
endl
356 <<
" command-line options." <<
endl
357 <<
" --all-users Send the same DCOP call to all users with a running DCOP" <<
endl
358 <<
" server. Only failed calls to existing DCOP servers will" <<
endl
359 <<
" generate an error message. If no DCOP server is available" <<
endl
360 <<
" at all, no error will be generated." <<
endl
361 <<
" --session <ses> Send to the given TDE session. This option can only be" <<
endl
362 <<
" used in combination with the --user option." <<
endl
363 <<
" --all-sessions Send to all sessions found. Only works with the --user" <<
endl
364 <<
" and --all-users options." <<
endl
365 <<
" --list-sessions List all active TDE session for a user or all users." <<
endl
366 <<
" --no-user-time Don't update the user activity timestamp in the called" <<
endl
367 <<
" application (for usage in scripts running" <<
endl
368 <<
" in the background)." <<
endl
378 static UserList userList()
382 while( passwd* pstruct = getpwent() )
384 result[ TQString::fromLocal8Bit(pstruct->pw_name) ] = TQFile::decodeName(pstruct->pw_dir);
394 TQStringList dcopSessionList(
const TQString &user,
const TQString &home )
398 cerr_ <<
"WARNING: Cannot determine home directory for user "
399 << user <<
"!" <<
endl
400 <<
"Please check permissions or set the $DCOPSERVER variable manually before" <<
endl
401 <<
"calling dcop." <<
endl;
402 return TQStringList();
406 TQFileInfo dirInfo( home );
407 if( !dirInfo.exists() || !dirInfo.isReadable() )
411 d.setFilter( TQDir::Files | TQDir::Hidden | TQDir::NoSymLinks );
412 d.setNameFilter(
".DCOPserver*" );
414 const TQFileInfoList *list = d.entryInfoList();
418 TQFileInfoListIterator it( *list );
421 while ( ( fi = it.current() ) != 0 )
423 if( fi->isReadable() )
424 result.append( fi->fileName() );
430 void sendUserTime(
const char* app )
432 #if defined TQ_WS_X11
433 static unsigned long time = 0;
436 Display* dpy = XOpenDisplay( NULL );
439 Window w = XCreateSimpleWindow( dpy, DefaultRootWindow( dpy ), 0, 0, 1, 1, 0, 0, 0 );
440 XSelectInput( dpy, w, PropertyChangeMask );
441 unsigned char data[ 1 ];
442 XChangeProperty( dpy, w, XA_ATOM, XA_ATOM, 8, PropModeAppend, data, 1 );
444 XWindowEvent( dpy, w, PropertyChangeMask, &ev );
445 time = ev.xproperty.time;
446 XDestroyWindow( dpy, w );
449 DCOPRef( app,
"MainApplication-Interface" ).
call(
"updateUserTimestamp", time );
458 int runDCOP( QCStringList args, UserList users, Session session,
459 const TQString sessionName,
bool readStdin,
bool updateUserTime )
461 bool DCOPrefmode=
false;
468 if ( !args.isEmpty() && args[ 0 ].find(
"DCOPRef(" ) == 0 )
470 int delimPos = args[ 0 ].findRev(
',' );
473 cerr_ <<
"Error: '" << args[ 0 ]
474 <<
"' is not a valid DCOP reference." <<
endl;
477 app = args[ 0 ].mid( 8, delimPos-8 );
479 objid = args[ 0 ].mid( delimPos, args[ 0 ].length()-delimPos-1 );
480 if( args.count() > 1 )
481 function = args[ 1 ];
482 if( args.count() > 2 )
485 params.remove( params.begin() );
486 params.remove( params.begin() );
492 if( !args.isEmpty() )
494 if( args.count() > 1 )
496 if( args.count() > 2 )
497 function = args[ 2 ];
498 if( args.count() > 3)
501 params.remove( params.begin() );
502 params.remove( params.begin() );
503 params.remove( params.begin() );
507 bool firstRun =
true;
508 UserList::Iterator it;
509 TQStringList sessions;
510 bool presetDCOPServer =
false;
514 for( it = users.begin(); it != users.end() || firstRun; ++it )
520 if( session == QuerySessions )
522 TQStringList sessions = dcopSessionList( it.key(), it.data() );
523 if( sessions.isEmpty() )
525 if( users.count() <= 1 )
527 cout_ <<
"No active sessions";
528 if( !( *it ).isEmpty() )
529 cout_ <<
" for user " << *it;
535 cout_ <<
"Active sessions ";
536 if( !( *it ).isEmpty() )
537 cout_ <<
"for user " << *it <<
" ";
538 cout_ <<
":" <<
endl;
540 TQStringList::Iterator sIt = sessions.begin();
541 for( ; sIt != sessions.end(); ++sIt )
542 cout_ <<
" " << *sIt <<
endl;
549 if( getenv(
"DCOPSERVER" ) )
551 sessions.append( getenv(
"DCOPSERVER" ) );
552 presetDCOPServer =
true;
555 if( users.count() > 1 || ( users.count() == 1 &&
556 ( getenv(
"DCOPSERVER" ) == 0 ) ) )
558 sessions = dcopSessionList( it.key(), it.data() );
559 if( sessions.isEmpty() )
561 if( users.count() > 1 )
565 cerr_ <<
"ERROR: No active TDE sessions!" <<
endl
566 <<
"If you are sure there is one, please set the $DCOPSERVER variable manually" <<
endl
567 <<
"before calling dcop." <<
endl;
571 else if( !sessionName.isEmpty() )
573 if( sessions.contains( sessionName ) )
576 sessions.append( sessionName );
580 cerr_ <<
"ERROR: The specified session doesn't exist!" <<
endl;
584 else if( sessions.count() > 1 && session != AllSessions )
586 cerr_ <<
"ERROR: Multiple available TDE sessions!" <<
endl
587 <<
"Please specify the correct session to use with --session or use the" <<
endl
588 <<
"--all-sessions option to broadcast to all sessions." <<
endl;
593 if ((users.count() > 1) || ((users.count() == 1) &&
594 ((getenv(
"ICEAUTHORITY") == 0) || (getenv(
"DISPLAY") == 0))))
597 TQString iceFileBase =
"ICEauthority";
601 TQString xdgRuntimeDir = TQString::fromLocal8Bit(getenv(
"XDG_RUNTIME_DIR"));
602 if (xdgRuntimeDir.isEmpty())
604 xdgRuntimeDir =
"/run/user/<uid>";
606 if (!xdgRuntimeDir.isEmpty())
608 TQFileInfo xdgRuntime(xdgRuntimeDir);
609 passwd* pstruct = getpwnam(it.key().local8Bit());
612 iceFile = TQString(
"%1/%2/%3").arg(xdgRuntime.dirPath()).arg(pstruct->pw_uid).arg(iceFileBase);
615 if (!pstruct || !fi.exists())
617 iceFile = TQString::null;
620 if (iceFile.isEmpty())
622 iceFile = TQString(
"%1/.%2").arg(it.data()).arg(iceFileBase);
625 if (iceFile.isEmpty())
627 cerr_ <<
"WARNING: Cannot determine home directory for user "
628 << it.key() <<
"!" <<
endl
629 <<
"Please check permissions or set the $ICEAUTHORITY variable manually before" <<
endl
630 <<
"calling dcop." <<
endl;
632 else if (fi.exists())
636 char *envStr = strdup((
"ICEAUTHORITY=" + iceFile).local8Bit());
642 cerr_ <<
"WARNING: ICE authority file " << iceFile
643 <<
"is not readable by you!" <<
endl
644 <<
"Please check permissions or set the $ICEAUTHORITY variable manually before" <<
endl
645 <<
"calling dcop." <<
endl;
650 if (users.count() > 1)
656 cerr_ <<
"WARNING: Cannot find ICE authority file "
657 << iceFile <<
"!" <<
endl
658 <<
"Please check permissions or set the $ICEAUTHORITY"
659 <<
" variable manually before" <<
endl
660 <<
"calling dcop." <<
endl;
669 TQStringList::Iterator sIt = sessions.begin();
670 for( ; sIt != sessions.end() || users.isEmpty(); ++sIt )
672 if( !presetDCOPServer && !users.isEmpty() )
674 TQString dcopFile = it.data() +
"/" + *sIt;
675 TQFile f( dcopFile );
676 if( !f.open( IO_ReadOnly ) )
678 cerr_ <<
"Can't open " << dcopFile <<
" for reading!" <<
endl;
682 TQStringList l( TQStringList::split(
'\n', f.readAll() ) );
683 dcopServer = l.first();
685 if( dcopServer.isEmpty() )
687 cerr_ <<
"WARNING: Unable to determine DCOP server for session "
688 << *sIt <<
"!" <<
endl
689 <<
"Please check permissions or set the $DCOPSERVER variable manually before" <<
endl
690 <<
"calling dcop." <<
endl;
697 if( !dcopServer.isEmpty() )
699 bool success = client->
attach();
702 cerr_ <<
"ERROR: Couldn't attach to DCOP server!" <<
endl;
703 retval = TQMAX( retval, 1 );
704 if( users.isEmpty() )
711 int argscount = args.count();
717 queryApplications(
"");
720 if (endsWith(app,
'*'))
721 queryApplications(app);
723 queryObjects( app,
"" );
726 if (endsWith(objid,
'*'))
727 queryObjects(app, objid);
729 queryFunctions( app, objid );
737 QCStringList::Iterator replaceArg = params.end();
739 QCStringList::Iterator it = params.begin();
740 for( ; it != params.end(); ++it )
746 while ( !cin_.atEnd() )
748 TQString buf = cin_.readLine();
750 if( replaceArg != params.end() )
751 *replaceArg = buf.local8Bit();
755 int res = callFunction( app, objid,
function, params );
756 retval = TQMAX( retval, res );
764 int res = callFunction( app, objid,
function, params );
765 retval = TQMAX( retval, res );
770 if( users.isEmpty() )
775 if( it == users.end() )
783 # define main kdemain
786 int main(
int argc,
char** argv )
788 bool readStdin =
false;
791 Session session = DefaultSession;
792 TQString sessionName;
793 bool updateUserTime =
true;
795 cin_.setEncoding( TQTextStream::Locale );
798 for(
int pos = 1 ; pos <= argc - 1 ; pos++ )
800 if( strcmp( argv[ pos ],
"--help" ) == 0 )
802 else if( strcmp( argv[ pos ],
"--pipe" ) == 0 )
807 else if( strcmp( argv[ pos ],
"--user" ) == 0 )
809 if( pos <= argc - 2 )
811 user = TQString::fromLocal8Bit( argv[ pos + 1] );
817 cerr_ <<
"Missing username for '--user' option!" <<
endl <<
endl;
821 else if( strcmp( argv[ pos ],
"--session" ) == 0 )
823 if( session == AllSessions )
825 cerr_ <<
"ERROR: --session cannot be mixed with --all-sessions!" <<
endl <<
endl;
828 else if( pos <= argc - 2 )
830 sessionName = TQString::fromLocal8Bit( argv[ pos + 1] );
836 cerr_ <<
"Missing session name for '--session' option!" <<
endl <<
endl;
840 else if( strcmp( argv[ pos ],
"--all-users" ) == 0 )
845 else if( strcmp( argv[ pos ],
"--list-sessions" ) == 0 )
847 session = QuerySessions;
850 else if( strcmp( argv[ pos ],
"--all-sessions" ) == 0 )
852 if( !sessionName.isEmpty() )
854 cerr_ <<
"ERROR: --session cannot be mixed with --all-sessions!" <<
endl <<
endl;
857 session = AllSessions;
860 else if( strcmp( argv[ pos ],
"--no-user-time" ) == 0 )
862 updateUserTime =
false;
865 else if( argv[ pos ][ 0 ] ==
'-' )
867 cerr_ <<
"Unknown command-line option '" << argv[ pos ]
882 TQCString prog = argv[ numOptions + 1 ];
889 if (prog[prog.length()-1] !=
'*')
892 int i = prog.findRev(
'-');
893 if ((i >= 0) && prog.mid(i+1).toLong())
897 args.append(
"qt/"+prog );
898 args.append(
"quit()" );
903 for(
int i = numOptions; i < argc + numOptions - 1; i++ )
904 args.append( argv[ i + 1 ] );
907 if( readStdin && args.count() < 3 )
909 cerr_ <<
"--pipe option only supported for function calls!" <<
endl <<
endl;
913 if( user ==
"*" && args.count() < 3 && session != QuerySessions )
915 cerr_ <<
"ERROR: The --all-users option is only supported for function calls!" <<
endl <<
endl;
919 if( session == QuerySessions && !args.isEmpty() )
921 cerr_ <<
"ERROR: The --list-sessions option cannot be used for actual DCOP calls!" <<
endl <<
endl;
925 if( session == QuerySessions && user.isEmpty() )
927 cerr_ <<
"ERROR: The --list-sessions option can only be used with the --user or" <<
endl
928 <<
"--all-users options!" <<
endl <<
endl;
932 if( session != DefaultSession && session != QuerySessions &&
935 cerr_ <<
"ERROR: The --session and --all-sessions options are only supported for function" <<
endl
943 else if( !user.isEmpty() )
944 users[ user ] = userList()[ user ];
946 int retval = runDCOP( args, users, session, sessionName, readStdin, updateUserTime );
Inter-process communication and remote procedure calls for KDE applications.
QCStringList remoteFunctions(const TQCString &remApp, const TQCString &remObj, bool *ok=0)
Retrieves the list of functions of the remote object remObj of application remApp.
TQCString appId() const
Returns the current app id or a null string if the application hasn't yet been registered.
bool isApplicationRegistered(const TQCString &remApp)
Checks whether remApp is registered with the DCOP server.
QCStringList registeredApplications()
Retrieves the list of all currently registered applications from dcopserver.
bool attach()
Attaches to the DCOP server.
bool isAttached() const
Returns whether or not the client is attached to the server.
bool call(const TQCString &remApp, const TQCString &remObj, const TQCString &remFun, const TQByteArray &data, TQCString &replyType, TQByteArray &replyData, bool useEventLoop, int timeout, bool forceRemote)
Performs a synchronous send and receive.
static void setServerAddress(const TQCString &addr)
Sets the address of a server to use upon attaching.
QCStringList remoteObjects(const TQCString &remApp, bool *ok=0)
Retrieves the list of objects of the remote application remApp.
A DCOPRef(erence) encapsulates a remote DCOP object as a triple <app,obj,type> where type is optional...
DCOPReply call(const TQCString &fun)
Calls the function fun on the object referenced by this reference.
kndbgstream & endl(kndbgstream &s)
const TDEShortcut & home()