• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.8.5 API Reference
  • KDE Home
  • Contact Us
 

KIO

  • kio
  • kio
slavebase.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the KDE libraries
3  * Copyright (c) 2000 Waldo Bastian <bastian@kde.org>
4  * Copyright (c) 2000 David Faure <faure@kde.org>
5  * Copyright (c) 2000 Stephan Kulow <coolo@kde.org>
6  * Copyright (c) 2007 Thiago Macieira <thiago@kde.org>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License version 2 as published by the Free Software Foundation.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this library; see the file COPYING.LIB. If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  *
22  **/
23 
24 #include "slavebase.h"
25 
26 #include <config.h>
27 
28 #include <sys/time.h>
29 
30 #include <kdebug.h>
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <unistd.h>
34 #include <signal.h>
35 #include <time.h>
36 
37 #include <QtCore/QFile>
38 #include <QtCore/QList>
39 #include <QtCore/QDateTime>
40 #include <QtCore/QCoreApplication>
41 
42 #include <kcrash.h>
43 #include <kconfig.h>
44 #include <kconfiggroup.h>
45 #include <kde_file.h>
46 #include <klocale.h>
47 
48 #include "kremoteencoding.h"
49 
50 #include "connection.h"
51 #include "ioslave_defaults.h"
52 #include "slaveinterface.h"
53 #include "kpasswdserver_p.h"
54 
55 #ifndef NDEBUG
56 #ifdef HAVE_BACKTRACE
57 #include <execinfo.h>
58 #endif
59 #endif
60 
61 extern "C" {
62  static void sigsegv_handler(int sig);
63  static void sigpipe_handler(int sig);
64 }
65 
66 using namespace KIO;
67 
68 typedef QList<QByteArray> AuthKeysList;
69 typedef QMap<QString,QByteArray> AuthKeysMap;
70 #define KIO_DATA QByteArray data; QDataStream stream( &data, QIODevice::WriteOnly ); stream
71 #define KIO_FILESIZE_T(x) quint64(x)
72 
73 namespace KIO {
74 
75 class SlaveBasePrivate {
76 public:
77  SlaveBase* q;
78  SlaveBasePrivate(SlaveBase* owner): q(owner), m_passwdServer(0) {}
79  ~SlaveBasePrivate() { delete m_passwdServer; }
80 
81  UDSEntryList pendingListEntries;
82  int listEntryCurrentSize;
83  long listEntry_sec, listEntry_usec;
84  Connection appConnection;
85  QString poolSocket;
86  bool isConnectedToApp;
87  static qlonglong s_seqNr;
88 
89  QString slaveid;
90  bool resume:1;
91  bool needSendCanResume:1;
92  bool onHold:1;
93  bool wasKilled:1;
94  bool inOpenLoop:1;
95  bool exit_loop:1;
96  MetaData configData;
97  KConfig *config;
98  KConfigGroup *configGroup;
99  KUrl onHoldUrl;
100 
101  struct timeval last_tv;
102  KIO::filesize_t totalSize;
103  KIO::filesize_t sentListEntries;
104  KRemoteEncoding *remotefile;
105  time_t timeout;
106  enum { Idle, InsideMethod, FinishedCalled, ErrorCalled } m_state;
107  QByteArray timeoutData;
108 
109  KPasswdServer* m_passwdServer;
110 
111  // Reconstructs configGroup from configData and mIncomingMetaData
112  void rebuildConfig()
113  {
114  configGroup->deleteGroup(KConfigGroup::WriteConfigFlags());
115 
116  // mIncomingMetaData cascades over config, so we write config first,
117  // to let it be overwritten
118  MetaData::ConstIterator end = configData.constEnd();
119  for (MetaData::ConstIterator it = configData.constBegin(); it != end; ++it)
120  configGroup->writeEntry(it.key(), it->toUtf8(), KConfigGroup::WriteConfigFlags());
121 
122  end = q->mIncomingMetaData.constEnd();
123  for (MetaData::ConstIterator it = q->mIncomingMetaData.constBegin(); it != end; ++it)
124  configGroup->writeEntry(it.key(), it->toUtf8(), KConfigGroup::WriteConfigFlags());
125  }
126 
127  void verifyState(const char* cmdName)
128  {
129  if ((m_state != FinishedCalled) && (m_state != ErrorCalled)){
130  kWarning(7019) << cmdName << "did not call finished() or error()! Please fix the KIO slave.";
131  }
132  }
133 
134  void verifyErrorFinishedNotCalled(const char* cmdName)
135  {
136  if (m_state == FinishedCalled || m_state == ErrorCalled) {
137  kWarning(7019) << cmdName << "called finished() or error(), but it's not supposed to! Please fix the KIO slave.";
138  }
139  }
140 
141  KPasswdServer* passwdServer()
142  {
143  if (!m_passwdServer) {
144  m_passwdServer = new KPasswdServer;
145  }
146 
147  return m_passwdServer;
148  }
149 };
150 
151 }
152 
153 static SlaveBase *globalSlave;
154 qlonglong SlaveBasePrivate::s_seqNr;
155 
156 static volatile bool slaveWriteError = false;
157 
158 static const char *s_protocol;
159 
160 #ifdef Q_OS_UNIX
161 extern "C" {
162 static void genericsig_handler(int sigNumber)
163 {
164  KDE_signal(sigNumber,SIG_IGN);
165  //WABA: Don't do anything that requires malloc, we can deadlock on it since
166  //a SIGTERM signal can come in while we are in malloc/free.
167  //kDebug()<<"kioslave : exiting due to signal "<<sigNumber;
168  //set the flag which will be checked in dispatchLoop() and which *should* be checked
169  //in lengthy operations in the various slaves
170  if (globalSlave!=0)
171  globalSlave->setKillFlag();
172  KDE_signal(SIGALRM,SIG_DFL);
173  alarm(5); //generate an alarm signal in 5 seconds, in this time the slave has to exit
174 }
175 }
176 #endif
177 
179 
180 SlaveBase::SlaveBase( const QByteArray &protocol,
181  const QByteArray &pool_socket,
182  const QByteArray &app_socket )
183  : mProtocol(protocol),
184  d(new SlaveBasePrivate(this))
185 
186 {
187  d->poolSocket = QFile::decodeName(pool_socket);
188  s_protocol = protocol.data();
189 #ifdef Q_OS_UNIX
190  if (qgetenv("KDE_DEBUG").isEmpty())
191  {
192  KCrash::setCrashHandler( sigsegv_handler );
193  KDE_signal(SIGILL,&sigsegv_handler);
194  KDE_signal(SIGTRAP,&sigsegv_handler);
195  KDE_signal(SIGABRT,&sigsegv_handler);
196  KDE_signal(SIGBUS,&sigsegv_handler);
197  KDE_signal(SIGALRM,&sigsegv_handler);
198  KDE_signal(SIGFPE,&sigsegv_handler);
199 #ifdef SIGPOLL
200  KDE_signal(SIGPOLL, &sigsegv_handler);
201 #endif
202 #ifdef SIGSYS
203  KDE_signal(SIGSYS, &sigsegv_handler);
204 #endif
205 #ifdef SIGVTALRM
206  KDE_signal(SIGVTALRM, &sigsegv_handler);
207 #endif
208 #ifdef SIGXCPU
209  KDE_signal(SIGXCPU, &sigsegv_handler);
210 #endif
211 #ifdef SIGXFSZ
212  KDE_signal(SIGXFSZ, &sigsegv_handler);
213 #endif
214  }
215 
216  struct sigaction act;
217  act.sa_handler = sigpipe_handler;
218  sigemptyset( &act.sa_mask );
219  act.sa_flags = 0;
220  sigaction( SIGPIPE, &act, 0 );
221 
222  KDE_signal(SIGINT,&genericsig_handler);
223  KDE_signal(SIGQUIT,&genericsig_handler);
224  KDE_signal(SIGTERM,&genericsig_handler);
225 #endif
226 
227  globalSlave=this;
228 
229  d->listEntryCurrentSize = 100;
230  struct timeval tp;
231  gettimeofday(&tp, 0);
232  d->listEntry_sec = tp.tv_sec;
233  d->listEntry_usec = tp.tv_usec;
234  d->isConnectedToApp = true;
235 
236  // by kahl for netmgr (need a way to identify slaves)
237  d->slaveid = protocol;
238  d->slaveid += QString::number(getpid());
239  d->resume = false;
240  d->needSendCanResume = false;
241  d->config = new KConfig(QString(), KConfig::SimpleConfig);
242  // The KConfigGroup needs the KConfig to exist during its whole lifetime.
243  d->configGroup = new KConfigGroup(d->config, QString());
244  d->onHold = false;
245  d->wasKilled=false;
246  d->last_tv.tv_sec = 0;
247  d->last_tv.tv_usec = 0;
248 // d->processed_size = 0;
249  d->totalSize=0;
250  d->sentListEntries=0;
251  d->timeout = 0;
252  connectSlave(QFile::decodeName(app_socket));
253 
254  d->remotefile = 0;
255  d->inOpenLoop = false;
256  d->exit_loop = false;
257 }
258 
259 SlaveBase::~SlaveBase()
260 {
261  delete d->configGroup;
262  delete d->config;
263  delete d->remotefile;
264  delete d;
265  s_protocol = "";
266 }
267 
268 void SlaveBase::dispatchLoop()
269 {
270  while (!d->exit_loop) {
271  if (d->timeout && (d->timeout < time(0))) {
272  QByteArray data = d->timeoutData;
273  d->timeout = 0;
274  d->timeoutData = QByteArray();
275  special(data);
276  }
277 
278  Q_ASSERT(d->appConnection.inited());
279 
280  int ms = -1;
281  if (d->timeout)
282  ms = 1000 * qMax<time_t>(d->timeout - time(0), 1);
283 
284  int ret = -1;
285  if (d->appConnection.hasTaskAvailable() || d->appConnection.waitForIncomingTask(ms)) {
286  // dispatch application messages
287  int cmd;
288  QByteArray data;
289  ret = d->appConnection.read(&cmd, data);
290 
291  if (ret != -1) {
292  if (d->inOpenLoop)
293  dispatchOpenCommand(cmd, data);
294  else
295  dispatch(cmd, data);
296  }
297  } else {
298  ret = d->appConnection.isConnected() ? 0 : -1;
299  }
300 
301  if (ret == -1) { // some error occurred, perhaps no more application
302  // When the app exits, should the slave be put back in the pool ?
303  if (!d->exit_loop && d->isConnectedToApp && !d->poolSocket.isEmpty()) {
304  disconnectSlave();
305  d->isConnectedToApp = false;
306  closeConnection();
307  connectSlave(d->poolSocket);
308  } else {
309  break;
310  }
311  }
312 
313  //I think we get here when we were killed in dispatch() and not in select()
314  if (wasKilled()) {
315  kDebug(7019) << "slave was killed, returning";
316  break;
317  }
318 
319  // execute deferred deletes
320  QCoreApplication::sendPostedEvents(NULL, QEvent::DeferredDelete);
321  }
322 
323  // execute deferred deletes
324  QCoreApplication::sendPostedEvents(NULL, QEvent::DeferredDelete);
325 }
326 
327 void SlaveBase::connectSlave(const QString &address)
328 {
329  d->appConnection.connectToRemote(address);
330 
331  if (!d->appConnection.inited())
332  {
333  kDebug(7019) << "failed to connect to" << address << endl
334  << "Reason:" << d->appConnection.errorString();
335  exit();
336  return;
337  }
338 
339  d->inOpenLoop = false;
340 }
341 
342 void SlaveBase::disconnectSlave()
343 {
344  d->appConnection.close();
345 }
346 
347 void SlaveBase::setMetaData(const QString &key, const QString &value)
348 {
349  mOutgoingMetaData.insert(key, value); // replaces existing key if already there
350 }
351 
352 QString SlaveBase::metaData(const QString &key) const
353 {
354  if (mIncomingMetaData.contains(key))
355  return mIncomingMetaData[key];
356  if (d->configData.contains(key))
357  return d->configData[key];
358  return QString();
359 }
360 
361 MetaData SlaveBase::allMetaData() const
362 {
363  return mIncomingMetaData;
364 }
365 
366 bool SlaveBase::hasMetaData(const QString &key) const
367 {
368  if (mIncomingMetaData.contains(key))
369  return true;
370  if (d->configData.contains(key))
371  return true;
372  return false;
373 }
374 
375 KConfigGroup *SlaveBase::config()
376 {
377  return d->configGroup;
378 }
379 
380 void SlaveBase::sendMetaData()
381 {
382  sendAndKeepMetaData();
383  mOutgoingMetaData.clear();
384 }
385 
386 void SlaveBase::sendAndKeepMetaData()
387 {
388  if (!mOutgoingMetaData.isEmpty()) {
389  KIO_DATA << mOutgoingMetaData;
390 
391  send(INF_META_DATA, data);
392  }
393 }
394 
395 KRemoteEncoding *SlaveBase::remoteEncoding()
396 {
397  if (d->remotefile)
398  return d->remotefile;
399 
400  const QByteArray charset (metaData(QLatin1String("Charset")).toLatin1());
401  return (d->remotefile = new KRemoteEncoding( charset ));
402 }
403 
404 void SlaveBase::data( const QByteArray &data )
405 {
406  sendMetaData();
407  send( MSG_DATA, data );
408 }
409 
410 void SlaveBase::dataReq( )
411 {
412  //sendMetaData();
413  if (d->needSendCanResume)
414  canResume(0);
415  send( MSG_DATA_REQ );
416 }
417 
418 void SlaveBase::opened()
419 {
420  sendMetaData();
421  send( MSG_OPENED );
422  d->inOpenLoop = true;
423 }
424 
425 void SlaveBase::error( int _errid, const QString &_text )
426 {
427  if (d->m_state == d->ErrorCalled) {
428  kWarning(7019) << "error() called twice! Please fix the KIO slave.";
429  return;
430  } else if (d->m_state == d->FinishedCalled) {
431  kWarning(7019) << "error() called after finished()! Please fix the KIO slave.";
432  return;
433  }
434 
435  d->m_state = d->ErrorCalled;
436  mIncomingMetaData.clear(); // Clear meta data
437  d->rebuildConfig();
438  mOutgoingMetaData.clear();
439  KIO_DATA << (qint32) _errid << _text;
440 
441  send( MSG_ERROR, data );
442  //reset
443  d->listEntryCurrentSize = 100;
444  d->sentListEntries=0;
445  d->totalSize=0;
446  d->inOpenLoop=false;
447 }
448 
449 void SlaveBase::connected()
450 {
451  send( MSG_CONNECTED );
452 }
453 
454 void SlaveBase::finished()
455 {
456  if (d->m_state == d->FinishedCalled) {
457  kWarning(7019) << "finished() called twice! Please fix the KIO slave.";
458  return;
459  } else if (d->m_state == d->ErrorCalled) {
460  kWarning(7019) << "finished() called after error()! Please fix the KIO slave.";
461  return;
462  }
463 
464  d->m_state = d->FinishedCalled;
465  mIncomingMetaData.clear(); // Clear meta data
466  d->rebuildConfig();
467  sendMetaData();
468  send( MSG_FINISHED );
469 
470  // reset
471  d->listEntryCurrentSize = 100;
472  d->sentListEntries=0;
473  d->totalSize=0;
474  d->inOpenLoop=false;
475 }
476 
477 void SlaveBase::needSubUrlData()
478 {
479  send( MSG_NEED_SUBURL_DATA );
480 }
481 
482 /*
483  * Map pid_t to a signed integer type that makes sense for QByteArray;
484  * only the most common sizes 16 bit and 32 bit are special-cased.
485  */
486 template<int T> struct PIDType { typedef pid_t PID_t; } ;
487 template<> struct PIDType<2> { typedef qint16 PID_t; } ;
488 template<> struct PIDType<4> { typedef qint32 PID_t; } ;
489 
490 void SlaveBase::slaveStatus( const QString &host, bool connected )
491 {
492  pid_t pid = getpid();
493  qint8 b = connected ? 1 : 0;
494  KIO_DATA << (PIDType<sizeof(pid_t)>::PID_t)pid << mProtocol << host << b;
495  if (d->onHold)
496  stream << d->onHoldUrl;
497  send( MSG_SLAVE_STATUS, data );
498 }
499 
500 void SlaveBase::canResume()
501 {
502  send( MSG_CANRESUME );
503 }
504 
505 void SlaveBase::totalSize( KIO::filesize_t _bytes )
506 {
507  KIO_DATA << KIO_FILESIZE_T(_bytes);
508  send( INF_TOTAL_SIZE, data );
509 
510  //this one is usually called before the first item is listed in listDir()
511  struct timeval tp;
512  gettimeofday(&tp, 0);
513  d->listEntry_sec = tp.tv_sec;
514  d->listEntry_usec = tp.tv_usec;
515  d->totalSize=_bytes;
516  d->sentListEntries=0;
517 }
518 
519 void SlaveBase::processedSize( KIO::filesize_t _bytes )
520 {
521  bool emitSignal=false;
522  struct timeval tv;
523  int gettimeofday_res=gettimeofday( &tv, 0L );
524 
525  if( _bytes == d->totalSize )
526  emitSignal=true;
527  else if ( gettimeofday_res == 0 ) {
528  time_t msecdiff = 2000;
529  if (d->last_tv.tv_sec) {
530  // Compute difference, in ms
531  msecdiff = 1000 * ( tv.tv_sec - d->last_tv.tv_sec );
532  time_t usecdiff = tv.tv_usec - d->last_tv.tv_usec;
533  if ( usecdiff < 0 ) {
534  msecdiff--;
535  msecdiff += 1000;
536  }
537  msecdiff += usecdiff / 1000;
538  }
539  emitSignal=msecdiff >= 100; // emit size 10 times a second
540  }
541 
542  if( emitSignal ) {
543  KIO_DATA << KIO_FILESIZE_T(_bytes);
544  send( INF_PROCESSED_SIZE, data );
545  if ( gettimeofday_res == 0 ) {
546  d->last_tv.tv_sec = tv.tv_sec;
547  d->last_tv.tv_usec = tv.tv_usec;
548  }
549  }
550 // d->processed_size = _bytes;
551 }
552 
553 void SlaveBase::written( KIO::filesize_t _bytes )
554 {
555  KIO_DATA << KIO_FILESIZE_T(_bytes);
556  send( MSG_WRITTEN, data );
557 }
558 
559 void SlaveBase::position( KIO::filesize_t _pos )
560 {
561  KIO_DATA << KIO_FILESIZE_T(_pos);
562  send( INF_POSITION, data );
563 }
564 
565 void SlaveBase::processedPercent( float /* percent */ )
566 {
567  kDebug(7019) << "STUB";
568 }
569 
570 
571 void SlaveBase::speed( unsigned long _bytes_per_second )
572 {
573  KIO_DATA << (quint32) _bytes_per_second;
574  send( INF_SPEED, data );
575 }
576 
577 void SlaveBase::redirection( const KUrl& _url )
578 {
579  KIO_DATA << _url;
580  send( INF_REDIRECTION, data );
581 }
582 
583 void SlaveBase::errorPage()
584 {
585  send( INF_ERROR_PAGE );
586 }
587 
588 static bool isSubCommand(int cmd)
589 {
590  return ( (cmd == CMD_REPARSECONFIGURATION) ||
591  (cmd == CMD_META_DATA) ||
592  (cmd == CMD_CONFIG) ||
593  (cmd == CMD_SUBURL) ||
594  (cmd == CMD_SLAVE_STATUS) ||
595  (cmd == CMD_SLAVE_CONNECT) ||
596  (cmd == CMD_SLAVE_HOLD) ||
597  (cmd == CMD_MULTI_GET));
598 }
599 
600 void SlaveBase::mimeType( const QString &_type)
601 {
602  kDebug(7019) << _type;
603  int cmd;
604  do
605  {
606  // Send the meta-data each time we send the mime-type.
607  if (!mOutgoingMetaData.isEmpty())
608  {
609  // kDebug(7019) << "emitting meta data";
610  KIO_DATA << mOutgoingMetaData;
611  send( INF_META_DATA, data );
612  }
613  KIO_DATA << _type;
614  send( INF_MIME_TYPE, data );
615  while(true)
616  {
617  cmd = 0;
618  int ret = -1;
619  if (d->appConnection.hasTaskAvailable() || d->appConnection.waitForIncomingTask(-1)) {
620  ret = d->appConnection.read( &cmd, data );
621  }
622  if (ret == -1) {
623  kDebug(7019) << "read error";
624  exit();
625  return;
626  }
627  // kDebug(7019) << "got" << cmd;
628  if ( cmd == CMD_HOST) // Ignore.
629  continue;
630  if (!isSubCommand(cmd))
631  break;
632 
633  dispatch( cmd, data );
634  }
635  }
636  while (cmd != CMD_NONE);
637  mOutgoingMetaData.clear();
638 }
639 
640 void SlaveBase::exit()
641 {
642  d->exit_loop = true;
643  // Using ::exit() here is too much (crashes in qdbus's qglobalstatic object),
644  // so let's cleanly exit dispatchLoop() instead.
645  // Update: we do need to call exit(), otherwise a long download (get()) would
646  // keep going until it ends, even though the application exited.
647  ::exit(255);
648 }
649 
650 void SlaveBase::warning( const QString &_msg)
651 {
652  KIO_DATA << _msg;
653  send( INF_WARNING, data );
654 }
655 
656 void SlaveBase::infoMessage( const QString &_msg)
657 {
658  KIO_DATA << _msg;
659  send( INF_INFOMESSAGE, data );
660 }
661 
662 bool SlaveBase::requestNetwork(const QString& host)
663 {
664  KIO_DATA << host << d->slaveid;
665  send( MSG_NET_REQUEST, data );
666 
667  if ( waitForAnswer( INF_NETWORK_STATUS, 0, data ) != -1 )
668  {
669  bool status;
670  QDataStream stream( data );
671  stream >> status;
672  return status;
673  } else
674  return false;
675 }
676 
677 void SlaveBase::dropNetwork(const QString& host)
678 {
679  KIO_DATA << host << d->slaveid;
680  send( MSG_NET_DROP, data );
681 }
682 
683 void SlaveBase::statEntry( const UDSEntry& entry )
684 {
685  KIO_DATA << entry;
686  send( MSG_STAT_ENTRY, data );
687 }
688 
689 void SlaveBase::listEntry( const UDSEntry& entry, bool _ready )
690 {
691  static struct timeval tp;
692  static const int maximum_updatetime = 300;
693  static const int minimum_updatetime = 100;
694 
695  if (!_ready) {
696  d->pendingListEntries.append(entry);
697 
698  if (d->pendingListEntries.count() > d->listEntryCurrentSize) {
699  gettimeofday(&tp, 0);
700 
701  long diff = ((tp.tv_sec - d->listEntry_sec) * 1000000 +
702  tp.tv_usec - d->listEntry_usec) / 1000;
703  if (diff==0) diff=1;
704 
705  if (diff > maximum_updatetime) {
706  d->listEntryCurrentSize = d->listEntryCurrentSize * 3 / 4;
707  _ready = true;
708  }
709 //if we can send all list entries of this dir which have not yet been sent
710 //within maximum_updatetime, then make d->listEntryCurrentSize big enough for all of them
711  else if (((d->pendingListEntries.count()*maximum_updatetime)/diff) > static_cast<long>(d->totalSize-d->sentListEntries))
712  d->listEntryCurrentSize=d->totalSize-d->sentListEntries+1;
713 //if we are below minimum_updatetime, estimate how much we will get within
714 //maximum_updatetime
715  else if (diff < minimum_updatetime)
716  d->listEntryCurrentSize = (d->pendingListEntries.count() * maximum_updatetime) / diff;
717  else
718  _ready=true;
719  }
720  }
721  if (_ready) { // may happen when we started with !ready
722  listEntries( d->pendingListEntries );
723  d->pendingListEntries.clear();
724 
725  gettimeofday(&tp, 0);
726  d->listEntry_sec = tp.tv_sec;
727  d->listEntry_usec = tp.tv_usec;
728  }
729 }
730 
731 void SlaveBase::listEntries( const UDSEntryList& list )
732 {
733  KIO_DATA << (quint32)list.count();
734  UDSEntryList::ConstIterator it = list.begin();
735  const UDSEntryList::ConstIterator end = list.end();
736  for (; it != end; ++it)
737  stream << *it;
738  send( MSG_LIST_ENTRIES, data);
739  d->sentListEntries+=(uint)list.count();
740 }
741 
742 static void sigsegv_handler(int sig)
743 {
744 #ifdef Q_OS_UNIX
745  KDE_signal(sig,SIG_DFL); // Next one kills
746 
747  //Kill us if we deadlock
748  KDE_signal(SIGALRM,SIG_DFL);
749  alarm(5); //generate an alarm signal in 5 seconds, in this time the slave has to exit
750 
751  // Debug and printf should be avoided because they might
752  // call malloc.. and get in a nice recursive malloc loop
753  char buffer[120];
754  qsnprintf(buffer, sizeof(buffer), "kioslave: ####### CRASH ###### protocol = %s pid = %d signal = %d\n", s_protocol, getpid(), sig);
755  write(2, buffer, strlen(buffer));
756 #ifndef NDEBUG
757 #ifdef HAVE_BACKTRACE
758  void* trace[256];
759  int n = backtrace(trace, 256);
760  if (n)
761  backtrace_symbols_fd(trace, n, 2);
762 #endif
763 #endif
764  ::exit(1);
765 #endif
766 }
767 
768 static void sigpipe_handler (int)
769 {
770  // We ignore a SIGPIPE in slaves.
771  // A SIGPIPE can happen in two cases:
772  // 1) Communication error with application.
773  // 2) Communication error with network.
774  slaveWriteError = true;
775 
776  // Don't add anything else here, especially no debug output
777 }
778 
779 void SlaveBase::setHost(QString const &, quint16, QString const &, QString const &)
780 {
781 }
782 
783 void SlaveBase::openConnection(void)
784 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CONNECT)); }
785 void SlaveBase::closeConnection(void)
786 { } // No response!
787 void SlaveBase::stat(KUrl const &)
788 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_STAT)); }
789 void SlaveBase::put(KUrl const &, int, JobFlags )
790 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_PUT)); }
791 void SlaveBase::special(const QByteArray &)
792 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SPECIAL)); }
793 void SlaveBase::listDir(KUrl const &)
794 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_LISTDIR)); }
795 void SlaveBase::get(KUrl const & )
796 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_GET)); }
797 void SlaveBase::open(KUrl const &, QIODevice::OpenMode)
798 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_OPEN)); }
799 void SlaveBase::read(KIO::filesize_t)
800 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_READ)); }
801 void SlaveBase::write(const QByteArray &)
802 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_WRITE)); }
803 void SlaveBase::seek(KIO::filesize_t)
804 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SEEK)); }
805 void SlaveBase::close()
806 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CLOSE)); }
807 void SlaveBase::mimetype(KUrl const &url)
808 { get(url); }
809 void SlaveBase::rename(KUrl const &, KUrl const &, JobFlags)
810 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_RENAME)); }
811 void SlaveBase::symlink(QString const &, KUrl const &, JobFlags)
812 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SYMLINK)); }
813 void SlaveBase::copy(KUrl const &, KUrl const &, int, JobFlags)
814 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_COPY)); }
815 void SlaveBase::del(KUrl const &, bool)
816 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_DEL)); }
817 void SlaveBase::setLinkDest(const KUrl &, const QString&)
818 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SETLINKDEST)); }
819 void SlaveBase::mkdir(KUrl const &, int)
820 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_MKDIR)); }
821 void SlaveBase::chmod(KUrl const &, int)
822 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CHMOD)); }
823 void SlaveBase::setModificationTime(KUrl const &, const QDateTime&)
824 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SETMODIFICATIONTIME)); }
825 void SlaveBase::chown(KUrl const &, const QString &, const QString &)
826 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CHOWN)); }
827 void SlaveBase::setSubUrl(KUrl const &)
828 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SUBURL)); }
829 void SlaveBase::multiGet(const QByteArray &)
830 { error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_MULTI_GET)); }
831 
832 
833 void SlaveBase::slave_status()
834 { slaveStatus( QString(), false ); }
835 
836 void SlaveBase::reparseConfiguration()
837 {
838  delete d->remotefile;
839  d->remotefile = 0;
840 }
841 
842 bool SlaveBase::openPasswordDialog( AuthInfo& info, const QString &errorMsg )
843 {
844  const long windowId = metaData(QLatin1String("window-id")).toLong();
845  const unsigned long userTimestamp = metaData(QLatin1String("user-timestamp")).toULong();
846  QString errorMessage;
847  if (metaData(QLatin1String("no-auth-prompt")).compare(QLatin1String("true"), Qt::CaseInsensitive) == 0) {
848  errorMessage = QLatin1String("<NoAuthPrompt>");
849  } else {
850  errorMessage = errorMsg;
851  }
852 
853  AuthInfo dlgInfo (info);
854  // Make sure the modified flag is not set.
855  dlgInfo.setModified(false);
856  // Prevent queryAuthInfo from caching the user supplied password since
857  // we need the ioslaves to first authenticate against the server with
858  // it to ensure it is valid.
859  dlgInfo.setExtraField(QLatin1String("skip-caching-on-query"), true);
860 
861  KPasswdServer* passwdServer = d->passwdServer();
862 
863  if (passwdServer) {
864  qlonglong seqNr = passwdServer->queryAuthInfo(dlgInfo, errorMessage, windowId,
865  SlaveBasePrivate::s_seqNr, userTimestamp);
866  if (seqNr > 0) {
867  SlaveBasePrivate::s_seqNr = seqNr;
868  if (dlgInfo.isModified()) {
869  info = dlgInfo;
870  return true;
871  }
872  }
873  }
874 
875  return false;
876 }
877 
878 int SlaveBase::messageBox( MessageBoxType type, const QString &text, const QString &caption,
879  const QString &buttonYes, const QString &buttonNo )
880 {
881  return messageBox( text, type, caption, buttonYes, buttonNo, QString() );
882 }
883 
884 int SlaveBase::messageBox( const QString &text, MessageBoxType type, const QString &caption,
885  const QString &buttonYes, const QString &buttonNo,
886  const QString &dontAskAgainName )
887 {
888  kDebug(7019) << "messageBox " << type << " " << text << " - " << caption << buttonYes << buttonNo;
889  KIO_DATA << (qint32)type << text << caption << buttonYes << buttonNo << dontAskAgainName;
890  send( INF_MESSAGEBOX, data );
891  if ( waitForAnswer( CMD_MESSAGEBOXANSWER, 0, data ) != -1 )
892  {
893  QDataStream stream( data );
894  int answer;
895  stream >> answer;
896  kDebug(7019) << "got messagebox answer" << answer;
897  return answer;
898  } else
899  return 0; // communication failure
900 }
901 
902 bool SlaveBase::canResume( KIO::filesize_t offset )
903 {
904  kDebug(7019) << "offset=" << KIO::number(offset);
905  d->needSendCanResume = false;
906  KIO_DATA << KIO_FILESIZE_T(offset);
907  send( MSG_RESUME, data );
908  if ( offset )
909  {
910  int cmd;
911  if ( waitForAnswer( CMD_RESUMEANSWER, CMD_NONE, data, &cmd ) != -1 )
912  {
913  kDebug(7019) << "returning" << (cmd == CMD_RESUMEANSWER);
914  return cmd == CMD_RESUMEANSWER;
915  } else
916  return false;
917  }
918  else // No resuming possible -> no answer to wait for
919  return true;
920 }
921 
922 
923 
924 int SlaveBase::waitForAnswer( int expected1, int expected2, QByteArray & data, int *pCmd )
925 {
926  int cmd, result = -1;
927  for (;;)
928  {
929  if (d->appConnection.hasTaskAvailable() || d->appConnection.waitForIncomingTask(-1)) {
930  result = d->appConnection.read( &cmd, data );
931  }
932  if (result == -1) {
933  kDebug(7019) << "read error.";
934  return -1;
935  }
936 
937  if ( cmd == expected1 || cmd == expected2 )
938  {
939  if ( pCmd ) *pCmd = cmd;
940  return result;
941  }
942  if ( isSubCommand(cmd) )
943  {
944  dispatch( cmd, data );
945  }
946  else
947  {
948  kFatal(7019) << "Got cmd " << cmd << " while waiting for an answer!";
949  }
950  }
951 }
952 
953 
954 int SlaveBase::readData( QByteArray &buffer)
955 {
956  int result = waitForAnswer( MSG_DATA, 0, buffer );
957  //kDebug(7019) << "readData: length = " << result << " ";
958  return result;
959 }
960 
961 void SlaveBase::setTimeoutSpecialCommand(int timeout, const QByteArray &data)
962 {
963  if (timeout > 0)
964  d->timeout = time(0)+(time_t)timeout;
965  else if (timeout == 0)
966  d->timeout = 1; // Immediate timeout
967  else
968  d->timeout = 0; // Canceled
969 
970  d->timeoutData = data;
971 }
972 
973 void SlaveBase::dispatch( int command, const QByteArray &data )
974 {
975  QDataStream stream( data );
976 
977  KUrl url;
978  int i;
979 
980  switch( command ) {
981  case CMD_HOST: {
982  // Reset s_seqNr, see kpasswdserver/DESIGN
983  SlaveBasePrivate::s_seqNr = 0;
984  QString passwd;
985  QString host, user;
986  quint16 port;
987  stream >> host >> port >> user >> passwd;
988  d->m_state = d->InsideMethod;
989  setHost( host, port, user, passwd );
990  d->verifyErrorFinishedNotCalled("setHost()");
991  d->m_state = d->Idle;
992  } break;
993  case CMD_CONNECT: {
994  openConnection( );
995  } break;
996  case CMD_DISCONNECT: {
997  closeConnection( );
998  } break;
999  case CMD_SLAVE_STATUS: {
1000  d->m_state = d->InsideMethod;
1001  slave_status();
1002  // TODO verify that the slave has called slaveStatus()?
1003  d->verifyErrorFinishedNotCalled("slave_status()");
1004  d->m_state = d->Idle;
1005  } break;
1006  case CMD_SLAVE_CONNECT: {
1007  d->onHold = false;
1008  QString app_socket;
1009  QDataStream stream( data );
1010  stream >> app_socket;
1011  d->appConnection.send( MSG_SLAVE_ACK );
1012  disconnectSlave();
1013  d->isConnectedToApp = true;
1014  connectSlave(app_socket);
1015  virtual_hook(AppConnectionMade, 0);
1016  } break;
1017  case CMD_SLAVE_HOLD: {
1018  KUrl url;
1019  QDataStream stream( data );
1020  stream >> url;
1021  d->onHoldUrl = url;
1022  d->onHold = true;
1023  disconnectSlave();
1024  d->isConnectedToApp = false;
1025  // Do not close connection!
1026  connectSlave(d->poolSocket);
1027  } break;
1028  case CMD_REPARSECONFIGURATION: {
1029  d->m_state = d->InsideMethod;
1030  reparseConfiguration();
1031  d->verifyErrorFinishedNotCalled("reparseConfiguration()");
1032  d->m_state = d->Idle;
1033  } break;
1034  case CMD_CONFIG: {
1035  stream >> d->configData;
1036  d->rebuildConfig();
1037 #if 0 //TODO: decide what to do in KDE 4.1
1038  KSocks::setConfig(d->configGroup);
1039 #endif
1040  delete d->remotefile;
1041  d->remotefile = 0;
1042  } break;
1043  case CMD_GET: {
1044  stream >> url;
1045  d->m_state = d->InsideMethod;
1046  get( url );
1047  d->verifyState("get()");
1048  d->m_state = d->Idle;
1049  } break;
1050  case CMD_OPEN: {
1051  stream >> url >> i;
1052  QIODevice::OpenMode mode = QFlag(i);
1053  d->m_state = d->InsideMethod;
1054  open(url, mode); //krazy:exclude=syscalls
1055  d->m_state = d->Idle;
1056  } break;
1057  case CMD_PUT: {
1058  int permissions;
1059  qint8 iOverwrite, iResume;
1060  stream >> url >> iOverwrite >> iResume >> permissions;
1061  JobFlags flags;
1062  if ( iOverwrite != 0 ) flags |= Overwrite;
1063  if ( iResume != 0 ) flags |= Resume;
1064 
1065  // Remember that we need to send canResume(), TransferJob is expecting
1066  // it. Well, in theory this shouldn't be done if resume is true.
1067  // (the resume bool is currently unused)
1068  d->needSendCanResume = true /* !resume */;
1069 
1070  d->m_state = d->InsideMethod;
1071  put( url, permissions, flags);
1072  d->verifyState("put()");
1073  d->m_state = d->Idle;
1074  } break;
1075  case CMD_STAT: {
1076  stream >> url;
1077  d->m_state = d->InsideMethod;
1078  stat( url ); //krazy:exclude=syscalls
1079  d->verifyState("stat()");
1080  d->m_state = d->Idle;
1081  } break;
1082  case CMD_MIMETYPE: {
1083  stream >> url;
1084  d->m_state = d->InsideMethod;
1085  mimetype( url );
1086  d->verifyState("mimetype()");
1087  d->m_state = d->Idle;
1088  } break;
1089  case CMD_LISTDIR: {
1090  stream >> url;
1091  d->m_state = d->InsideMethod;
1092  listDir( url );
1093  d->verifyState("listDir()");
1094  d->m_state = d->Idle;
1095  } break;
1096  case CMD_MKDIR: {
1097  stream >> url >> i;
1098  d->m_state = d->InsideMethod;
1099  mkdir( url, i ); //krazy:exclude=syscalls
1100  d->verifyState("mkdir()");
1101  d->m_state = d->Idle;
1102  } break;
1103  case CMD_RENAME: {
1104  qint8 iOverwrite;
1105  KUrl url2;
1106  stream >> url >> url2 >> iOverwrite;
1107  JobFlags flags;
1108  if ( iOverwrite != 0 ) flags |= Overwrite;
1109  d->m_state = d->InsideMethod;
1110  rename( url, url2, flags ); //krazy:exclude=syscalls
1111  d->verifyState("rename()");
1112  d->m_state = d->Idle;
1113  } break;
1114  case CMD_SYMLINK: {
1115  qint8 iOverwrite;
1116  QString target;
1117  stream >> target >> url >> iOverwrite;
1118  JobFlags flags;
1119  if ( iOverwrite != 0 ) flags |= Overwrite;
1120  d->m_state = d->InsideMethod;
1121  symlink( target, url, flags );
1122  d->verifyState("symlink()");
1123  d->m_state = d->Idle;
1124  } break;
1125  case CMD_COPY: {
1126  int permissions;
1127  qint8 iOverwrite;
1128  KUrl url2;
1129  stream >> url >> url2 >> permissions >> iOverwrite;
1130  JobFlags flags;
1131  if ( iOverwrite != 0 ) flags |= Overwrite;
1132  d->m_state = d->InsideMethod;
1133  copy( url, url2, permissions, flags );
1134  d->verifyState("copy()");
1135  d->m_state = d->Idle;
1136  } break;
1137  case CMD_DEL: {
1138  qint8 isFile;
1139  stream >> url >> isFile;
1140  d->m_state = d->InsideMethod;
1141  del( url, isFile != 0);
1142  d->verifyState("del()");
1143  d->m_state = d->Idle;
1144  } break;
1145  case CMD_CHMOD: {
1146  stream >> url >> i;
1147  d->m_state = d->InsideMethod;
1148  chmod( url, i);
1149  d->verifyState("chmod()");
1150  d->m_state = d->Idle;
1151  } break;
1152  case CMD_CHOWN: {
1153  QString owner, group;
1154  stream >> url >> owner >> group;
1155  d->m_state = d->InsideMethod;
1156  chown(url, owner, group);
1157  d->verifyState("chown()");
1158  d->m_state = d->Idle;
1159  } break;
1160  case CMD_SETMODIFICATIONTIME: {
1161  QDateTime dt;
1162  stream >> url >> dt;
1163  d->m_state = d->InsideMethod;
1164  setModificationTime(url, dt);
1165  d->verifyState("setModificationTime()");
1166  d->m_state = d->Idle;
1167  } break;
1168  case CMD_SPECIAL: {
1169  d->m_state = d->InsideMethod;
1170  special( data );
1171  d->verifyState("special()");
1172  d->m_state = d->Idle;
1173  } break;
1174  case CMD_META_DATA: {
1175  //kDebug(7019) << "(" << getpid() << ") Incoming meta-data...";
1176  stream >> mIncomingMetaData;
1177  d->rebuildConfig();
1178  } break;
1179  case CMD_SUBURL: {
1180  stream >> url;
1181  d->m_state = d->InsideMethod;
1182  setSubUrl(url);
1183  d->verifyErrorFinishedNotCalled("setSubUrl()");
1184  d->m_state = d->Idle;
1185  } break;
1186  case CMD_NONE: {
1187  kWarning(7019) << "Got unexpected CMD_NONE!";
1188  } break;
1189  case CMD_MULTI_GET: {
1190  d->m_state = d->InsideMethod;
1191  multiGet( data );
1192  d->verifyState("multiGet()");
1193  d->m_state = d->Idle;
1194  } break;
1195  default: {
1196  // Some command we don't understand.
1197  // Just ignore it, it may come from some future version of KDE.
1198  } break;
1199  }
1200 }
1201 
1202 bool SlaveBase::checkCachedAuthentication( AuthInfo& info )
1203 {
1204  KPasswdServer* passwdServer = d->passwdServer();
1205  return (passwdServer &&
1206  passwdServer->checkAuthInfo(info, metaData(QLatin1String("window-id")).toLong(),
1207  metaData(QLatin1String("user-timestamp")).toULong()));
1208 }
1209 
1210 void SlaveBase::dispatchOpenCommand( int command, const QByteArray &data )
1211 {
1212  QDataStream stream( data );
1213 
1214  switch( command ) {
1215  case CMD_READ: {
1216  KIO::filesize_t bytes;
1217  stream >> bytes;
1218  read(bytes);
1219  break;
1220  }
1221  case CMD_WRITE: {
1222  write(data);
1223  break;
1224  }
1225  case CMD_SEEK: {
1226  KIO::filesize_t offset;
1227  stream >> offset;
1228  seek(offset);
1229  }
1230  case CMD_NONE:
1231  break;
1232  case CMD_CLOSE:
1233  close(); // must call finish(), which will set d->inOpenLoop=false
1234  break;
1235  default:
1236  // Some command we don't understand.
1237  // Just ignore it, it may come from some future version of KDE.
1238  break;
1239  }
1240 }
1241 
1242 bool SlaveBase::cacheAuthentication( const AuthInfo& info )
1243 {
1244  KPasswdServer* passwdServer = d->passwdServer();
1245 
1246  if (!passwdServer) {
1247  return false;
1248  }
1249 
1250  passwdServer->addAuthInfo(info, metaData(QLatin1String("window-id")).toLongLong());
1251  return true;
1252 }
1253 
1254 int SlaveBase::connectTimeout()
1255 {
1256  bool ok;
1257  QString tmp = metaData(QLatin1String("ConnectTimeout"));
1258  int result = tmp.toInt(&ok);
1259  if (ok)
1260  return result;
1261  return DEFAULT_CONNECT_TIMEOUT;
1262 }
1263 
1264 int SlaveBase::proxyConnectTimeout()
1265 {
1266  bool ok;
1267  QString tmp = metaData(QLatin1String("ProxyConnectTimeout"));
1268  int result = tmp.toInt(&ok);
1269  if (ok)
1270  return result;
1271  return DEFAULT_PROXY_CONNECT_TIMEOUT;
1272 }
1273 
1274 
1275 int SlaveBase::responseTimeout()
1276 {
1277  bool ok;
1278  QString tmp = metaData(QLatin1String("ResponseTimeout"));
1279  int result = tmp.toInt(&ok);
1280  if (ok)
1281  return result;
1282  return DEFAULT_RESPONSE_TIMEOUT;
1283 }
1284 
1285 
1286 int SlaveBase::readTimeout()
1287 {
1288  bool ok;
1289  QString tmp = metaData(QLatin1String("ReadTimeout"));
1290  int result = tmp.toInt(&ok);
1291  if (ok)
1292  return result;
1293  return DEFAULT_READ_TIMEOUT;
1294 }
1295 
1296 bool SlaveBase::wasKilled() const
1297 {
1298  return d->wasKilled;
1299 }
1300 
1301 void SlaveBase::setKillFlag()
1302 {
1303  d->wasKilled=true;
1304 }
1305 
1306 void SlaveBase::send(int cmd, const QByteArray& arr )
1307 {
1308  slaveWriteError = false;
1309  if (!d->appConnection.send(cmd, arr))
1310  // Note that slaveWriteError can also be set by sigpipe_handler
1311  slaveWriteError = true;
1312  if (slaveWriteError) exit();
1313 }
1314 
1315 void SlaveBase::virtual_hook( int, void* )
1316 { /*BASE::virtual_hook( id, data );*/ }
1317 
1318 void SlaveBase::lookupHost(const QString& host)
1319 {
1320  KIO_DATA << host;
1321  send(MSG_HOST_INFO_REQ, data);
1322 }
1323 
1324 int SlaveBase::waitForHostInfo(QHostInfo& info)
1325 {
1326  QByteArray data;
1327  int result = waitForAnswer(CMD_HOST_INFO, 0, data);
1328 
1329  if (result == -1) {
1330  info.setError(QHostInfo::UnknownError);
1331  info.setErrorString(i18n("Unknown Error"));
1332  return result;
1333  }
1334 
1335  QDataStream stream(data);
1336  QString hostName;
1337  QList<QHostAddress> addresses;
1338  int error;
1339  QString errorString;
1340 
1341  stream >> hostName >> addresses >> error >> errorString;
1342 
1343  info.setHostName(hostName);
1344  info.setAddresses(addresses);
1345  info.setError(QHostInfo::HostInfoError(error));
1346  info.setErrorString(errorString);
1347 
1348  return result;
1349 }
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Thu Feb 21 2013 11:10:12 by doxygen 1.8.1 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.8.5 API Reference

Skip menu "kdelibs-4.8.5 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal