summaryrefslogtreecommitdiffstats
path: root/tdeio/tdeio
diff options
context:
space:
mode:
Diffstat (limited to 'tdeio/tdeio')
-rw-r--r--tdeio/tdeio/CMakeLists.txt138
-rw-r--r--tdeio/tdeio/KFILEMETAINFO_ISSUES4
-rw-r--r--tdeio/tdeio/Makefile.am129
-rw-r--r--tdeio/tdeio/authinfo.cpp332
-rw-r--r--tdeio/tdeio/authinfo.h320
-rw-r--r--tdeio/tdeio/chmodjob.cpp258
-rw-r--r--tdeio/tdeio/chmodjob.h109
-rw-r--r--tdeio/tdeio/configure.in.in167
-rw-r--r--tdeio/tdeio/connection.cpp273
-rw-r--r--tdeio/tdeio/connection.h158
-rw-r--r--tdeio/tdeio/dataprotocol.cpp339
-rw-r--r--tdeio/tdeio/dataprotocol.h71
-rw-r--r--tdeio/tdeio/dataslave.cpp213
-rw-r--r--tdeio/tdeio/dataslave.h126
-rw-r--r--tdeio/tdeio/davjob.cpp142
-rw-r--r--tdeio/tdeio/davjob.h127
-rw-r--r--tdeio/tdeio/defaultprogress.cpp507
-rw-r--r--tdeio/tdeio/defaultprogress.h164
-rw-r--r--tdeio/tdeio/forwardingslavebase.cpp475
-rw-r--r--tdeio/tdeio/forwardingslavebase.h204
-rw-r--r--tdeio/tdeio/global.cpp2009
-rw-r--r--tdeio/tdeio/global.h547
-rw-r--r--tdeio/tdeio/http_slave_defaults.h49
-rw-r--r--tdeio/tdeio/ioslave_defaults.h53
-rw-r--r--tdeio/tdeio/job.cpp4814
-rw-r--r--tdeio/tdeio/job.h532
-rw-r--r--tdeio/tdeio/jobclasses.h1909
-rw-r--r--tdeio/tdeio/kacl.cpp682
-rw-r--r--tdeio/tdeio/kacl.h207
-rw-r--r--tdeio/tdeio/kar.cpp170
-rw-r--r--tdeio/tdeio/kar.h103
-rw-r--r--tdeio/tdeio/karchive.cpp717
-rw-r--r--tdeio/tdeio/karchive.h639
-rw-r--r--tdeio/tdeio/kautomount.cpp117
-rw-r--r--tdeio/tdeio/kautomount.h122
-rw-r--r--tdeio/tdeio/kdatatool.cpp285
-rw-r--r--tdeio/tdeio/kdatatool.h303
-rw-r--r--tdeio/tdeio/kdcopservicestarter.cpp97
-rw-r--r--tdeio/tdeio/kdcopservicestarter.h103
-rw-r--r--tdeio/tdeio/kdirlister.cpp2538
-rw-r--r--tdeio/tdeio/kdirlister.h634
-rw-r--r--tdeio/tdeio/kdirlister_p.h358
-rw-r--r--tdeio/tdeio/kdirnotify.cpp40
-rw-r--r--tdeio/tdeio/kdirnotify.h84
-rw-r--r--tdeio/tdeio/kdirnotify_stub.cpp101
-rw-r--r--tdeio/tdeio/kdirnotify_stub.h32
-rw-r--r--tdeio/tdeio/kdirwatch.cpp1774
-rw-r--r--tdeio/tdeio/kdirwatch.h290
-rw-r--r--tdeio/tdeio/kdirwatch_p.h158
-rw-r--r--tdeio/tdeio/kemailsettings.cpp272
-rw-r--r--tdeio/tdeio/kemailsettings.h147
-rw-r--r--tdeio/tdeio/kfilterbase.cpp76
-rw-r--r--tdeio/tdeio/kfilterbase.h116
-rw-r--r--tdeio/tdeio/kfilterdev.cpp484
-rw-r--r--tdeio/tdeio/kfilterdev.h204
-rw-r--r--tdeio/tdeio/kimageio.cpp566
-rw-r--r--tdeio/tdeio/kimageio.h170
-rw-r--r--tdeio/tdeio/kimageiofactory.h142
-rw-r--r--tdeio/tdeio/klimitediodevice.h105
-rw-r--r--tdeio/tdeio/kmdbase.h6
-rw-r--r--tdeio/tdeio/kmessageboxwrapper.h56
-rw-r--r--tdeio/tdeio/kmimemagic.cpp2317
-rw-r--r--tdeio/tdeio/kmimemagic.h218
-rw-r--r--tdeio/tdeio/kmimetype.cpp1172
-rw-r--r--tdeio/tdeio/kmimetype.h641
-rw-r--r--tdeio/tdeio/kmimetypechooser.cpp298
-rw-r--r--tdeio/tdeio/kmimetypechooser.h180
-rw-r--r--tdeio/tdeio/kmimetyperesolver.h255
-rw-r--r--tdeio/tdeio/knfsshare.cpp219
-rw-r--r--tdeio/tdeio/knfsshare.h86
-rw-r--r--tdeio/tdeio/kprotocolinfo.cpp257
-rw-r--r--tdeio/tdeio/kprotocolinfo.h688
-rw-r--r--tdeio/tdeio/kprotocolmanager.cpp534
-rw-r--r--tdeio/tdeio/kprotocolmanager.h389
-rw-r--r--tdeio/tdeio/kremoteencoding.cpp95
-rw-r--r--tdeio/tdeio/kremoteencoding.h127
-rw-r--r--tdeio/tdeio/krun.cpp1574
-rw-r--r--tdeio/tdeio/krun.h512
-rw-r--r--tdeio/tdeio/ksambashare.cpp239
-rw-r--r--tdeio/tdeio/ksambashare.h85
-rw-r--r--tdeio/tdeio/kscan.cpp185
-rw-r--r--tdeio/tdeio/kscan.h370
-rw-r--r--tdeio/tdeio/kservice.cpp934
-rw-r--r--tdeio/tdeio/kservice.h562
-rw-r--r--tdeio/tdeio/kservice_p.h41
-rw-r--r--tdeio/tdeio/kservicefactory.cpp291
-rw-r--r--tdeio/tdeio/kservicefactory.h113
-rw-r--r--tdeio/tdeio/kservicegroup.cpp724
-rw-r--r--tdeio/tdeio/kservicegroup.h353
-rw-r--r--tdeio/tdeio/kservicegroupfactory.cpp148
-rw-r--r--tdeio/tdeio/kservicegroupfactory.h80
-rw-r--r--tdeio/tdeio/kservicetype.cpp366
-rw-r--r--tdeio/tdeio/kservicetype.h251
-rw-r--r--tdeio/tdeio/kservicetypefactory.cpp300
-rw-r--r--tdeio/tdeio/kservicetypefactory.h123
-rw-r--r--tdeio/tdeio/kshellcompletion.cpp311
-rw-r--r--tdeio/tdeio/kshellcompletion.h85
-rw-r--r--tdeio/tdeio/kshred.cpp276
-rw-r--r--tdeio/tdeio/kshred.h156
-rw-r--r--tdeio/tdeio/ktar.cpp980
-rw-r--r--tdeio/tdeio/ktar.h171
-rw-r--r--tdeio/tdeio/ktrader.cpp186
-rw-r--r--tdeio/tdeio/ktrader.h295
-rw-r--r--tdeio/tdeio/ktraderparse.cpp159
-rw-r--r--tdeio/tdeio/ktraderparse.h52
-rw-r--r--tdeio/tdeio/ktraderparsetree.cpp714
-rw-r--r--tdeio/tdeio/ktraderparsetree.h371
-rw-r--r--tdeio/tdeio/kurifilter.cpp451
-rw-r--r--tdeio/tdeio/kurifilter.h667
-rw-r--r--tdeio/tdeio/kurlcompletion.cpp1604
-rw-r--r--tdeio/tdeio/kurlcompletion.h236
-rw-r--r--tdeio/tdeio/kurlpixmapprovider.cpp33
-rw-r--r--tdeio/tdeio/kurlpixmapprovider.h59
-rw-r--r--tdeio/tdeio/kuserprofile.cpp355
-rw-r--r--tdeio/tdeio/kuserprofile.h282
-rw-r--r--tdeio/tdeio/kzip.cpp1460
-rw-r--r--tdeio/tdeio/kzip.h284
-rw-r--r--tdeio/tdeio/lex.c1759
-rw-r--r--tdeio/tdeio/lex.l126
-rw-r--r--tdeio/tdeio/metainfojob.cpp184
-rw-r--r--tdeio/tdeio/metainfojob.h119
-rw-r--r--tdeio/tdeio/netaccess.cpp536
-rw-r--r--tdeio/tdeio/netaccess.h540
-rw-r--r--tdeio/tdeio/observer.cpp417
-rw-r--r--tdeio/tdeio/observer.h213
-rw-r--r--tdeio/tdeio/passdlg.cpp367
-rw-r--r--tdeio/tdeio/passdlg.h175
-rw-r--r--tdeio/tdeio/paste.cpp308
-rw-r--r--tdeio/tdeio/paste.h125
-rw-r--r--tdeio/tdeio/pastedialog.cpp84
-rw-r--r--tdeio/tdeio/pastedialog.h64
-rw-r--r--tdeio/tdeio/posixacladdons.cpp236
-rw-r--r--tdeio/tdeio/posixacladdons.h47
-rw-r--r--tdeio/tdeio/previewjob.cpp597
-rw-r--r--tdeio/tdeio/previewjob.h182
-rw-r--r--tdeio/tdeio/progressbase.cpp180
-rw-r--r--tdeio/tdeio/progressbase.h271
-rw-r--r--tdeio/tdeio/renamedlg.cpp574
-rw-r--r--tdeio/tdeio/renamedlg.h153
-rw-r--r--tdeio/tdeio/renamedlgplugin.h59
-rw-r--r--tdeio/tdeio/scheduler.cpp922
-rw-r--r--tdeio/tdeio/scheduler.h364
-rw-r--r--tdeio/tdeio/sessiondata.cpp311
-rw-r--r--tdeio/tdeio/sessiondata.h67
-rw-r--r--tdeio/tdeio/skipdlg.cpp143
-rw-r--r--tdeio/tdeio/skipdlg.h61
-rw-r--r--tdeio/tdeio/slave.cpp519
-rw-r--r--tdeio/tdeio/slave.h270
-rw-r--r--tdeio/tdeio/slavebase.cpp1315
-rw-r--r--tdeio/tdeio/slavebase.h847
-rw-r--r--tdeio/tdeio/slaveconfig.cpp225
-rw-r--r--tdeio/tdeio/slaveconfig.h106
-rw-r--r--tdeio/tdeio/slaveinterface.cpp550
-rw-r--r--tdeio/tdeio/slaveinterface.h290
-rw-r--r--tdeio/tdeio/statusbarprogress.cpp166
-rw-r--r--tdeio/tdeio/statusbarprogress.h112
-rw-r--r--tdeio/tdeio/tcpslavebase.cpp1343
-rw-r--r--tdeio/tdeio/tcpslavebase.h389
-rw-r--r--tdeio/tdeio/tdefilefilter.cpp134
-rw-r--r--tdeio/tdeio/tdefilefilter.h170
-rw-r--r--tdeio/tdeio/tdefileitem.cpp1202
-rw-r--r--tdeio/tdeio/tdefileitem.h671
-rw-r--r--tdeio/tdeio/tdefilemetainfo.cpp1859
-rw-r--r--tdeio/tdeio/tdefilemetainfo.h1738
-rw-r--r--tdeio/tdeio/tdefileshare.cpp346
-rw-r--r--tdeio/tdeio/tdefileshare.h165
-rw-r--r--tdeio/tdeio/tdelficon.cpp89
-rw-r--r--tdeio/tdeio/tdelficon.h54
-rw-r--r--tdeio/tdeio/thumbcreator.h124
-rw-r--r--tdeio/tdeio/yacc.c1522
-rw-r--r--tdeio/tdeio/yacc.h93
-rw-r--r--tdeio/tdeio/yacc.y126
172 files changed, 73590 insertions, 0 deletions
diff --git a/tdeio/tdeio/CMakeLists.txt b/tdeio/tdeio/CMakeLists.txt
new file mode 100644
index 000000000..8aaf395b6
--- /dev/null
+++ b/tdeio/tdeio/CMakeLists.txt
@@ -0,0 +1,138 @@
+#################################################
+#
+# (C) 2010 Serghei Amelian
+# serghei (DOT) amelian (AT) gmail.com
+#
+# Improvements and feedback are welcome
+#
+# This file is released under GPL >= 2
+#
+#################################################
+
+include_directories(
+ ${TQT_INCLUDE_DIRS}
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${CMAKE_BINARY_DIR}/tdeio/kssl
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_BINARY_DIR}/tdecore
+ ${CMAKE_SOURCE_DIR}
+ ${CMAKE_SOURCE_DIR}/dcop
+ ${CMAKE_SOURCE_DIR}/tdecore
+ ${CMAKE_SOURCE_DIR}/tdecore/network
+ ${CMAKE_SOURCE_DIR}/tdeui
+ ${CMAKE_SOURCE_DIR}/tdeio
+ ${CMAKE_SOURCE_DIR}/tdeio/kssl
+ ${CMAKE_SOURCE_DIR}/interfaces
+ ${LIBR_INCLUDEDIR}
+ ${GAMIN_INCLUDEDIR}
+)
+
+link_directories(
+ ${GAMIN_LIBDIR}
+)
+
+##### headers ###################################
+
+install( FILES
+ kservicetype.h kmimetype.h kmimemagic.h kservice.h
+ krun.h kdirwatch.h kautomount.h kuserprofile.h
+ kshred.h kar.h ktar.h kzip.h ktrader.h kurifilter.h
+ kurlcompletion.h kshellcompletion.h tdefileitem.h
+ tdefileshare.h ksambashare.h knfsshare.h kdirlister.h
+ kservicegroup.h kimageio.h kdirnotify.h kdirnotify_stub.h
+ kurlpixmapprovider.h kprotocolinfo.h kprotocolmanager.h
+ kfilterbase.h kfilterdev.h kemailsettings.h kscan.h
+ kdatatool.h karchive.h tdefilefilter.h tdefilemetainfo.h
+ renamedlgplugin.h kmimetyperesolver.h kdcopservicestarter.h
+ kremoteencoding.h kmimetypechooser.h
+ DESTINATION ${INCLUDE_INSTALL_DIR} )
+
+# FIXME seems that ACL is no longer optional
+#if( USE_POSIX_ACL )
+ install( FILES kacl.h DESTINATION ${INCLUDE_INSTALL_DIR} )
+#endif( USE_POSIX_ACL )
+
+install( FILES
+ connection.h slaveinterface.h slave.h slaveconfig.h
+ sessiondata.h global.h passdlg.h netaccess.h job.h
+ scheduler.h jobclasses.h paste.h slavebase.h
+ progressbase.h defaultprogress.h statusbarprogress.h
+ tcpslavebase.h forwardingslavebase.h observer.h
+ chmodjob.h kmdbase.h authinfo.h ioslave_defaults.h
+ http_slave_defaults.h previewjob.h thumbcreator.h
+ metainfojob.h davjob.h renamedlg.h skipdlg.h
+ ${CMAKE_CURRENT_BINARY_DIR}/uiserver_stub.h
+ DESTINATION ${INCLUDE_INSTALL_DIR}/tdeio )
+
+
+##### tdeiocore ###################################
+
+set( target tdeiocore )
+
+set( ${target}_SRCS
+ authinfo.cpp kshred.cpp kprotocolmanager.cpp slave.cpp
+ slaveinterface.cpp observer.stub sessiondata.cpp
+ scheduler.cpp connection.cpp job.cpp global.cpp
+ slaveconfig.cpp kurlpixmapprovider.cpp netaccess.cpp
+ paste.cpp pastedialog.cpp kmimemagic.cpp tcpslavebase.cpp
+ slavebase.cpp passdlg.cpp forwardingslavebase.cpp
+ progressbase.cpp defaultprogress.cpp statusbarprogress.cpp
+ kdirnotify.cpp kdirnotify.skel kdirnotify_stub.cpp
+ observer.cpp ../misc/uiserver.stub observer.skel kemailsettings.cpp
+ kprotocolinfo.cpp renamedlg.cpp skipdlg.cpp kremoteencoding.cpp
+ kmimetypechooser.cpp
+)
+
+tde_add_library( ${target} STATIC_PIC AUTOMOC
+ SOURCES ${${target}_SRCS}
+ DEPENDENCIES dcopidl
+)
+
+
+##### tdesycoca ###################################
+
+set( target tdesycoca )
+
+set( ${target}_SRCS
+ kdirwatch.cpp tdefileshare.cpp ksambashare.cpp
+ knfsshare.cpp ktrader.cpp ktraderparse.cpp
+ ktraderparsetree.cpp kservicetypefactory.cpp
+ kservicetype.cpp kmimetype.cpp kservicegroup.cpp
+ kservice.cpp kservicefactory.cpp kuserprofile.cpp
+ kservicegroupfactory.cpp kurifilter.cpp kfilterbase.cpp
+ kfilterdev.cpp kshellcompletion.cpp kurlcompletion.cpp
+ kautomount.cpp krun.cpp tdefileitem.cpp kdirlister.cpp
+ kimageio.cpp yacc.c lex.c chmodjob.cpp kscan.cpp
+ kar.cpp ktar.cpp kzip.cpp previewjob.cpp metainfojob.cpp
+ davjob.cpp kdatatool.cpp karchive.cpp tdefilefilter.cpp
+ tdefilemetainfo.cpp kdcopservicestarter.cpp dataslave.cpp
+ dataprotocol.cpp
+)
+
+# FIXME seems that ACL is no longer optional
+#if( USE_POSIX_ACL )
+ set( ${target}_SRCS ${${target}_SRCS} kacl.cpp posixacladdons.cpp )
+#endif( USE_POSIX_ACL )
+
+tde_add_library( ${target} STATIC_PIC AUTOMOC
+ SOURCES ${${target}_SRCS}
+ LINK ${GAMIN_LIBRARIES}
+)
+
+
+##### tdelficon ###################################
+
+if( HAVE_ELFICON )
+
+ set( target tdelficon )
+
+ set( ${target}_SRCS
+ tdelficon.cpp
+ )
+
+ tde_add_library( ${target} STATIC_PIC AUTOMOC
+ SOURCES ${${target}_SRCS}
+ )
+
+endif( HAVE_ELFICON )
diff --git a/tdeio/tdeio/KFILEMETAINFO_ISSUES b/tdeio/tdeio/KFILEMETAINFO_ISSUES
new file mode 100644
index 000000000..a4486f85f
--- /dev/null
+++ b/tdeio/tdeio/KFILEMETAINFO_ISSUES
@@ -0,0 +1,4 @@
+Bugs:
+=====
+
+currently none known
diff --git a/tdeio/tdeio/Makefile.am b/tdeio/tdeio/Makefile.am
new file mode 100644
index 000000000..f0766f5c8
--- /dev/null
+++ b/tdeio/tdeio/Makefile.am
@@ -0,0 +1,129 @@
+# This file is part of the KDE libraries
+# Copyright (C) 1997 Torben Weis (weis@kde.org)
+
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Library General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Library General Public License for more details.
+
+# You should have received a copy of the GNU Library General Public License
+# along with this library; see the file COPYING.LIB. If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+
+AM_CPPFLAGS = -D_LARGEFILE64_SOURCE
+
+INCLUDES= -I$(top_srcdir) -I$(srcdir)/.. -I$(top_srcdir)/tdecore/network -I$(srcdir)/../kssl -I../kssl -I$(srcdir)/../../interfaces $(all_includes) $(SSL_INCLUDES)
+
+noinst_LTLIBRARIES = libtdeiocore.la libtdesycoca.la
+
+# convenience lib - no LDFLAGS or LIBADD !
+
+libtdesycoca_la_SOURCES = \
+ kdirwatch.cpp \
+ tdefileshare.cpp ksambashare.cpp knfsshare.cpp \
+ ktrader.cpp ktraderparse.cpp ktraderparsetree.cpp \
+ kservicetypefactory.cpp kservicetype.cpp \
+ kmimetype.cpp kservicegroup.cpp \
+ kservice.cpp kservicefactory.cpp \
+ kuserprofile.cpp kservicegroupfactory.cpp \
+ kurifilter.cpp \
+ kfilterbase.cpp kfilterdev.cpp \
+ kshellcompletion.cpp kurlcompletion.cpp \
+ kautomount.cpp krun.cpp \
+ tdefileitem.cpp kdirlister.cpp kimageio.cpp \
+ yacc.c lex.c \
+ chmodjob.cpp kscan.cpp kar.cpp ktar.cpp kzip.cpp previewjob.cpp metainfojob.cpp davjob.cpp \
+ kdatatool.cpp karchive.cpp tdefilefilter.cpp \
+ tdefilemetainfo.cpp kdcopservicestarter.cpp \
+ dataslave.cpp dataprotocol.cpp
+#if USE_POSIX_ACL
+ libtdesycoca_la_SOURCES += kacl.cpp posixacladdons.cpp
+#endif
+
+include_HEADERS = \
+ kservicetype.h kmimetype.h kmimemagic.h kservice.h \
+ krun.h kdirwatch.h kautomount.h kuserprofile.h \
+ kshred.h kar.h ktar.h kzip.h ktrader.h kurifilter.h kurlcompletion.h \
+ kshellcompletion.h tdefileitem.h tdefileshare.h ksambashare.h knfsshare.h \
+ kdirlister.h kservicegroup.h \
+ kimageio.h kdirnotify.h kdirnotify_stub.h \
+ kurlpixmapprovider.h kprotocolinfo.h kprotocolmanager.h \
+ kfilterbase.h kfilterdev.h kemailsettings.h kscan.h kdatatool.h \
+ karchive.h tdefilefilter.h tdefilemetainfo.h renamedlgplugin.h \
+ kmimetyperesolver.h kdcopservicestarter.h kremoteencoding.h \
+ kmimetypechooser.h
+#if USE_POSIX_ACL
+include_HEADERS += kacl.h
+#endif
+
+#libtdeiocore_la_LDFLAGS = $(all_libraries)
+#libtdeiocore_la_LIBADD = ../../tdeui/libtdeui.la ../../tdesu/libtdesu.la $(LIBZ) $(LIBFAM) $(LIBVOLMGT)
+
+libtdeiocore_la_SOURCES = authinfo.cpp \
+ kshred.cpp \
+ kprotocolmanager.cpp \
+ slave.cpp slaveinterface.cpp observer.stub \
+ sessiondata.cpp scheduler.cpp \
+ connection.cpp \
+ job.cpp global.cpp \
+ slaveconfig.cpp kurlpixmapprovider.cpp \
+ netaccess.cpp paste.cpp pastedialog.cpp \
+ kmimemagic.cpp \
+ tcpslavebase.cpp slavebase.cpp passdlg.cpp \
+ forwardingslavebase.cpp \
+ progressbase.cpp defaultprogress.cpp \
+ statusbarprogress.cpp \
+ kdirnotify.cpp kdirnotify.skel kdirnotify_stub.cpp \
+ observer.cpp uiserver.stub observer.skel \
+ kemailsettings.cpp \
+ kprotocolinfo.cpp \
+ renamedlg.cpp skipdlg.cpp kremoteencoding.cpp \
+ kmimetypechooser.cpp
+
+uiserver_DIR = $(top_srcdir)/tdeio/misc
+
+METASOURCES = AUTO
+
+tdeioincludedir = $(includedir)/tdeio
+tdeioinclude_HEADERS = connection.h \
+ slaveinterface.h slave.h slaveconfig.h \
+ sessiondata.h global.h passdlg.h \
+ netaccess.h job.h scheduler.h \
+ jobclasses.h paste.h slavebase.h \
+ progressbase.h defaultprogress.h \
+ statusbarprogress.h tcpslavebase.h \
+ forwardingslavebase.h \
+ observer.h chmodjob.h uiserver_stub.h \
+ kmdbase.h authinfo.h \
+ ioslave_defaults.h http_slave_defaults.h previewjob.h thumbcreator.h \
+ metainfojob.h davjob.h renamedlg.h skipdlg.h
+
+# Internal
+noinst_HEADERS = kservicetypefactory.h kservicefactory.h \
+ kmessageboxwrapper.h \
+ ktraderparse.h ktraderparsetree.h yacc.h \
+ kimageiofactory.h kdirwatch_p.h kdirlister_p.h \
+ renamedlg.h skipdlg.h dataslave.h dataprotocol.h \
+ kservice_p.h
+#if USE_POSIX_ACL
+noinst_HEADERS += posixacladdons.h
+#endif
+
+parserfiles = yacc.y lex.l
+
+EXTRA_DIST = $(parserfiles)
+
+parser: $(parserfiles)
+ cd $(srcdir) ;\
+ flex -olex.c -Pkiotrader lex.l ;\
+ bison -d -p kiotrader yacc.y && mv yacc.tab.c yacc.c; mv yacc.tab.h yacc.h
+
+.PHONY: parser
+
+include ../../admin/Doxyfile.am
diff --git a/tdeio/tdeio/authinfo.cpp b/tdeio/tdeio/authinfo.cpp
new file mode 100644
index 000000000..b95a40585
--- /dev/null
+++ b/tdeio/tdeio/authinfo.cpp
@@ -0,0 +1,332 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2000-2001 Dawit Alemayehu <adawit@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <tqdir.h>
+#include <tqfile.h>
+
+#include <kdebug.h>
+#include <kstandarddirs.h>
+#include <ksavefile.h>
+#include <kstaticdeleter.h>
+#include <kde_file.h>
+
+#include "tdeio/authinfo.h"
+
+#define NETRC_READ_BUF_SIZE 4096
+
+using namespace TDEIO;
+
+AuthInfo::AuthInfo()
+{
+ modified = false;
+ readOnly = false;
+ verifyPath = false;
+ keepPassword = false;
+}
+
+AuthInfo::AuthInfo( const AuthInfo& info )
+{
+ (*this) = info;
+}
+
+AuthInfo& AuthInfo::operator= ( const AuthInfo& info )
+{
+ url = info.url;
+ username = info.username;
+ password = info.password;
+ prompt = info.prompt;
+ caption = info.caption;
+ comment = info.comment;
+ commentLabel = info.commentLabel;
+ realmValue = info.realmValue;
+ digestInfo = info.digestInfo;
+ verifyPath = info.verifyPath;
+ readOnly = info.readOnly;
+ keepPassword = info.keepPassword;
+ modified = info.modified;
+ return *this;
+}
+
+TQDataStream& TDEIO::operator<< (TQDataStream& s, const AuthInfo& a)
+{
+ s << a.url << a.username << a.password << a.prompt << a.caption
+ << a.comment << a.commentLabel << a.realmValue << a.digestInfo
+ << TQ_UINT8(a.verifyPath ? 1:0) << TQ_UINT8(a.readOnly ? 1:0)
+ << TQ_UINT8(a.keepPassword ? 1:0) << TQ_UINT8(a.modified ? 1:0);
+ return s;
+}
+
+TQDataStream& TDEIO::operator>> (TQDataStream& s, AuthInfo& a)
+{
+ TQ_UINT8 verify = 0;
+ TQ_UINT8 ro = 0;
+ TQ_UINT8 keep = 0;
+ TQ_UINT8 mod = 0;
+
+ s >> a.url >> a.username >> a.password >> a.prompt >> a.caption
+ >> a.comment >> a.commentLabel >> a.realmValue >> a.digestInfo
+ >> verify >> ro >> keep >> mod;
+ a.verifyPath = (verify != 0);
+ a.readOnly = (ro != 0);
+ a.keepPassword = (keep != 0);
+ a.modified = (mod != 0);
+ return s;
+}
+
+
+NetRC* NetRC::instance = 0L;
+
+NetRC::NetRC()
+{
+ isDirty = false;
+}
+
+NetRC::~NetRC()
+{
+ delete instance;
+ instance = 0L;
+}
+
+NetRC* NetRC::self()
+{
+ if ( !instance )
+ instance = new NetRC();
+ return instance;
+}
+
+bool NetRC::lookup( const KURL& url, AutoLogin& login, bool userealnetrc,
+ TQString type, int mode )
+{
+ // kdDebug() << "AutoLogin lookup for: " << url.host() << endl;
+ if ( !url.isValid() )
+ return false;
+
+ if ( type.isEmpty() )
+ type = url.protocol();
+
+ if ( loginMap.isEmpty() || isDirty )
+ {
+ loginMap.clear();
+
+ TQString filename = locateLocal("config", "kionetrc");
+ bool status = parse (openf (filename));
+
+ if ( userealnetrc )
+ {
+ filename = TQDir::homeDirPath()+ TQDir::separator() + ".netrc";
+ status |= parse (openf(filename));
+ }
+
+ if ( !status )
+ return false;
+ }
+
+ if ( !loginMap.contains( type ) )
+ return false;
+
+ LoginList l = loginMap[type];
+ if ( l.isEmpty() )
+ return false;
+
+ for (LoginList::Iterator it = l.begin(); it != l.end(); ++it)
+ {
+ AutoLogin &log = *it;
+
+ if ( (mode & defaultOnly) == defaultOnly &&
+ log.machine == TQString::fromLatin1("default") &&
+ (login.login.isEmpty() || login.login == log.login) )
+ {
+ login.type = log.type;
+ login.machine = log.machine;
+ login.login = log.login;
+ login.password = log.password;
+ login.macdef = log.macdef;
+ }
+
+ if ( (mode & presetOnly) == presetOnly &&
+ log.machine == TQString::fromLatin1("preset") &&
+ (login.login.isEmpty() || login.login == log.login) )
+ {
+ login.type = log.type;
+ login.machine = log.machine;
+ login.login = log.login;
+ login.password = log.password;
+ login.macdef = log.macdef;
+ }
+
+ if ( (mode & exactOnly) == exactOnly &&
+ log.machine == url.host() &&
+ (login.login.isEmpty() || login.login == log.login) )
+ {
+ login.type = log.type;
+ login.machine = log.machine;
+ login.login = log.login;
+ login.password = log.password;
+ login.macdef = log.macdef;
+ break;
+ }
+ }
+
+ return true;
+}
+
+int NetRC::openf( const TQString& f )
+{
+ KDE_struct_stat sbuff;
+ TQCString ef = TQFile::encodeName(f);
+ if ( KDE_stat(ef, &sbuff) != 0 )
+ return -1;
+
+ // Security check!!
+ if ( sbuff.st_mode != (S_IFREG|S_IRUSR|S_IWUSR) ||
+ sbuff.st_uid != geteuid() )
+ return -1;
+
+ return KDE_open( ef, O_RDONLY );
+}
+
+TQString NetRC::extract( const char* buf, const char* key, int& pos )
+{
+ int idx = pos;
+ int m_len = strlen(key);
+ int b_len = strlen(buf);
+
+ while( idx < b_len )
+ {
+ while( buf[idx] == ' ' || buf[idx] == '\t' )
+ idx++;
+
+ if ( strncasecmp( buf+idx, key, m_len ) != 0 )
+ idx++;
+ else
+ {
+ idx += m_len;
+ while( buf[idx] == ' ' || buf[idx] == '\t' )
+ idx++;
+
+ int start = idx;
+ while( buf[idx] != ' ' && buf[idx] != '\t' &&
+ buf[idx] != '\n' && buf[idx] != '\r' )
+ idx++;
+
+ if ( idx > start )
+ {
+ pos = idx;
+ return TQString::fromLatin1( buf+start, idx-start);
+ }
+ }
+ }
+
+ return TQString::null;
+}
+
+bool NetRC::parse( int fd )
+{
+ if (fd == -1)
+ return false;
+
+ TQString type;
+ TQString macro;
+
+ uint index = 0;
+ bool isMacro = false;
+ char* buf = new char[NETRC_READ_BUF_SIZE];
+ FILE* fstream = KDE_fdopen( fd,"rb" );
+
+ while ( fgets (buf, NETRC_READ_BUF_SIZE, fstream) != 0L )
+ {
+ int pos = 0;
+
+ while ( buf[pos] == ' ' || buf[pos] == '\t' )
+ pos++;
+
+ if ( buf[pos] == '#' || buf[pos] == '\n' ||
+ buf[pos] == '\r' || buf[pos] == '\0' )
+ {
+ if ( buf[pos] != '#' && isMacro )
+ isMacro = false;
+
+ continue;
+ }
+
+ if ( isMacro )
+ {
+ int tail = strlen(buf);
+ while( buf[tail-1] == '\n' || buf[tail-1] =='\r' )
+ tail--;
+
+ TQString mac = TQString::fromLatin1(buf, tail).stripWhiteSpace();
+ if ( !mac.isEmpty() )
+ loginMap[type][index].macdef[macro].append( mac );
+
+ continue;
+ }
+
+ AutoLogin l;
+ l.machine = extract( buf, "machine", pos );
+ if ( l.machine.isEmpty() )
+ {
+ if (strncasecmp(buf+pos, "default", 7) == 0 )
+ {
+ pos += 7;
+ l.machine = TQString::fromLatin1("default");
+ }
+ else if (strncasecmp(buf+pos, "preset", 6) == 0 )
+ {
+ pos += 6;
+ l.machine = TQString::fromLatin1("preset");
+ }
+ }
+ // kdDebug() << "Machine: " << l.machine << endl;
+
+ l.login = extract( buf, "login", pos );
+ // kdDebug() << "Login: " << l.login << endl;
+
+ l.password = extract( buf, "password", pos );
+ if ( l.password.isEmpty() )
+ l.password = extract( buf, "account", pos );
+ // kdDebug() << "Password: " << l.password << endl;
+
+ type = l.type = extract( buf, "type", pos );
+ if ( l.type.isEmpty() && !l.machine.isEmpty() )
+ type = l.type = TQString::fromLatin1("ftp");
+ // kdDebug() << "Type: " << l.type << endl;
+
+ macro = extract( buf, "macdef", pos );
+ isMacro = !macro.isEmpty();
+ // kdDebug() << "Macro: " << macro << endl;
+
+ loginMap[l.type].append(l);
+ index = loginMap[l.type].count()-1;
+ }
+
+ delete [] buf;
+ fclose (fstream);
+ close (fd);
+ return true;
+}
diff --git a/tdeio/tdeio/authinfo.h b/tdeio/tdeio/authinfo.h
new file mode 100644
index 000000000..019311ef8
--- /dev/null
+++ b/tdeio/tdeio/authinfo.h
@@ -0,0 +1,320 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2000-2001 Dawit Alemayehu <adawit@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __KIO_AUTHINFO_H
+#define __KIO_AUTHINFO_H
+
+#include <tqmap.h>
+#include <tqvaluelist.h>
+#include <kurl.h>
+
+
+namespace TDEIO {
+
+/**
+ * This class is intended to make it easier to prompt for, cache
+ * and retrieve authorization information.
+ *
+ * When using this class to cache, retrieve or prompt authentication
+ * information, you only need to set the necessary attributes. For
+ * example, to check whether a password is already cached, the only
+ * required information is the URL of the resource and optionally
+ * whether or not a path match should be performed. Similarly, to
+ * prompt for password you only need to optionally set the prompt,
+ * username (if already supplied), comment and commentLabel fields.
+ *
+ * <em>SPECIAL NOTE:</em> If you extend this class to add additional
+ * parameters do not forget to overload the stream insertion and
+ * extraction operators ("<<" and ">>") so that the added data can
+ * be correctly serialzed.
+ *
+ * @short A two way messaging class for passing authentication information.
+ * @author Dawit Alemayehu <adawit@kde.org>
+ */
+class TDEIO_EXPORT AuthInfo
+{
+ TDEIO_EXPORT friend TQDataStream& operator<< (TQDataStream& s, const AuthInfo& a);
+ TDEIO_EXPORT friend TQDataStream& operator>> (TQDataStream& s, AuthInfo& a);
+
+public:
+ /**
+ * Default constructor.
+ */
+ AuthInfo();
+
+ /**
+ * Copy constructor.
+ */
+ AuthInfo( const AuthInfo& info );
+
+ /**
+ * Overloaded equal to operator.
+ */
+ AuthInfo& operator=( const AuthInfo& info );
+
+ /**
+ * Use this method to check if the object was modified.
+ * @return true if the object has been modified
+ */
+ bool isModified() const { return modified; }
+
+ /**
+ * Use this method to indicate that this object has been modified.
+ * @param flag true to mark the object as modified, false to clear
+ */
+ void setModified( bool flag ) { modified = flag; }
+
+ /**
+ * The URL for which authentication is to be stored.
+ *
+ * This field is required when attempting to cache authorization
+ * and retrieve it. However, it is not needed when prompting
+ * the user for authorization info.
+ *
+ * This setting is @em required except when prompting the
+ * user for password.
+ */
+ KURL url;
+
+ /**
+ * This is @em required for caching.
+ */
+ TQString username;
+
+ /**
+ * This is @em required for caching.
+ */
+ TQString password;
+
+ /**
+ * Information to be displayed when prompting
+ * the user for authentication information.
+ *
+ * @note If this field is not set, the authentication
+ * dialog simply displays the preset default prompt.
+ *
+ * This setting is @em optional and empty by default.
+ */
+ TQString prompt;
+
+ /**
+ * The text to displayed in the title bar of
+ * the password prompting dialog.
+ *
+ * @note If this field is not set, the authentication
+ * dialog simply displays the preset default caption.
+ *
+ * This setting is @em optional and empty by default.
+ */
+ TQString caption;
+
+ /**
+ * Additional comment to be displayed when prompting
+ * the user for authentication information.
+ *
+ * This field allows you to display a short (no more than
+ * 80 characters) extra description in the password prompt
+ * dialog. For example, this field along with the
+ * commentLabel can be used to describe the server that
+ * requested the authentication:
+ *
+ * \code
+ * Server: Squid Proxy @ foo.com
+ * \endcode
+ *
+ * where "Server:" is the commentLabel and the rest is the
+ * actual comment. Note that it is always better to use
+ * the @p commentLabel field as it will be placed properly
+ * in the dialog rather than to include it within the actual
+ * comment.
+ *
+ * This setting is @em optional and empty by default.
+ */
+ TQString comment;
+
+ /**
+ * Descriptive label to be displayed in front of the
+ * comment when prompting the user for password.
+ *
+ * This setting is @em optional and only applicable when
+ * the comment field is also set.
+ */
+ TQString commentLabel;
+
+ /**
+ * A unique identifier that allows caching of multiple
+ * passwords for different resources in the same server.
+ *
+ * Mostly this setting is applicable to the HTTP protocol
+ * whose authentication scheme explicitly defines the use
+ * of such a unique key. However, any protocol that can
+ * generate or supply a unique id can effectively use it
+ * to distinguish passwords.
+ *
+ * (If you are instead interested in caching the authentication
+ * info for multiple users to the same server, refer to
+ * multipleUserCaching below)
+ *
+ * This setting is @em optional and not set by default.
+ */
+ TQString realmValue;
+
+ /**
+ * Field to store any extra authentication information for
+ * protocols that need it (ex: http).
+ *
+ * This setting is @em optional and mostly applicable for HTTP
+ * protocol. However, any protocol can make use of it to
+ * store extra info.
+ */
+ TQString digestInfo;
+
+ /**
+ * Flag that, if set, indicates whether a path match should be
+ * performed when requesting for cached authorization.
+ *
+ * A path is deemed to be a match if it is equal to or is a subset
+ * of the cached path. For example, if stored path is "/foo/bar"
+ * and the request's path set to "/foo/bar/acme", then it is a match
+ * whereas it would not if the request's path was set to "/foo".
+ *
+ * This setting is @em optional and false by default.
+ */
+ bool verifyPath;
+
+ /**
+ * Flag which if set forces the username field to be read-only.
+ *
+ * This setting is @em optional and false by default.
+ */
+ bool readOnly;
+
+ /**
+ * Flag to indicate the persistence of the given password.
+ *
+ * This is a two-way flag, when set before calling openPassDlg
+ * it makes the "keep Password" check box visible to the user.
+ * In return the flag will indicate the state of the check box.
+ * By default if the flag is checked the password will be cached
+ * for the entire life of the current KDE session otherwise the
+ * cached password is deleted right after the application using
+ * it has been closed.
+ */
+ bool keepPassword;
+
+protected:
+ bool modified;
+private:
+ class AuthInfoPrivate* d;
+};
+
+TDEIO_EXPORT TQDataStream& operator<< (TQDataStream& s, const AuthInfo& a);
+TDEIO_EXPORT TQDataStream& operator>> (TQDataStream& s, AuthInfo& a);
+
+/**
+ * A Singleton class that provides access to passwords
+ * stored in .netrc files for automatic login purposes.
+ * This is only meant to address backward compatability
+ * with old automated ftp client style logins...
+ *
+ * @short An interface to the ftp .netrc files
+ * @author Dawit Alemayehu <adawit@kde.org>
+ */
+class TDEIO_EXPORT NetRC
+{
+public:
+
+ /**
+ * Specifies the mode to be used when searching for a
+ * matching automatic login info for a given site :
+ *
+ * @li exactOnly search entries with exact host name matches.
+ * @li defaultOnly search entries that are specified as "default".
+ * @li presetOnly search entries that are specified as "preset".
+ *
+ * @see lookup
+ */
+ enum LookUpMode
+ {
+ exactOnly = 0x0002,
+ defaultOnly = 0x0004,
+ presetOnly = 0x0008
+ };
+
+ /**
+ * Contains auto login information.
+ * @see lookup()
+ */
+ struct AutoLogin
+ {
+ TQString type;
+ TQString machine;
+ TQString login;
+ TQString password;
+ TQMap<TQString, TQStringList> macdef;
+ };
+
+ /**
+ * A reference to the instance of the class.
+ * @return the class
+ */
+ static NetRC* self();
+
+ /**
+ * Looks up the @p login information for the given @p url.
+ *
+ * @param url the url whose login information will be checked
+ * @param login the login information will be writte here
+ * @param userealnetrc if true, use $HOME/.netrc fle
+ * @param type the type of the login. If null, the @p url's protocol
+ * will be taken
+ * @param mode the LookUpMode flags (ORed) for the query
+ */
+ bool lookup( const KURL& url, AutoLogin& login,
+ bool userealnetrc = false,
+ TQString type = TQString::null,
+ int mode = (exactOnly|defaultOnly) );
+ /**
+ * Reloads the auto login information.
+ */
+ void reload() { isDirty = true; }
+
+protected:
+ TQString extract( const char*, const char*, int& );
+ int openf( const TQString& );
+ bool parse( int );
+
+private:
+ NetRC();
+ ~NetRC();
+
+private:
+ bool isDirty;
+
+ typedef TQValueList<AutoLogin> LoginList;
+ typedef TQMap<TQString, LoginList> LoginMap;
+ LoginMap loginMap;
+
+ static NetRC* instance;
+ class NetRCPrivate;
+ NetRCPrivate* d;
+};
+}
+#endif
diff --git a/tdeio/tdeio/chmodjob.cpp b/tdeio/tdeio/chmodjob.cpp
new file mode 100644
index 000000000..434466d77
--- /dev/null
+++ b/tdeio/tdeio/chmodjob.cpp
@@ -0,0 +1,258 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
+ David Faure <faure@kde.org>
+ Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <config.h>
+
+#include <pwd.h>
+#include <grp.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <assert.h>
+
+#include <tqtimer.h>
+#include <tqfile.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kmessagebox.h>
+
+#include "tdeio/job.h"
+#include "tdeio/chmodjob.h"
+
+#include <kdirnotify_stub.h>
+
+using namespace TDEIO;
+
+ChmodJob::ChmodJob( const KFileItemList& lstItems, int permissions, int mask,
+ int newOwner, int newGroup,
+ bool recursive, bool showProgressInfo )
+ : TDEIO::Job( showProgressInfo ), state( STATE_LISTING ),
+ m_permissions( permissions ), m_mask( mask ),
+ m_newOwner( newOwner ), m_newGroup( newGroup ),
+ m_recursive( recursive ), m_lstItems( lstItems )
+{
+ TQTimer::singleShot( 0, this, TQT_SLOT(processList()) );
+}
+
+void ChmodJob::processList()
+{
+ while ( !m_lstItems.isEmpty() )
+ {
+ KFileItem * item = m_lstItems.first();
+ if ( !item->isLink() ) // don't do anything with symlinks
+ {
+ // File or directory -> remember to chmod
+ ChmodInfo info;
+ info.url = item->url();
+ // This is a toplevel file, we apply changes directly (no +X emulation here)
+ info.permissions = ( m_permissions & m_mask ) | ( item->permissions() & ~m_mask );
+ /*kdDebug(7007) << "\n current permissions=" << TQString::number(item->permissions(),8)
+ << "\n wanted permission=" << TQString::number(m_permissions,8)
+ << "\n with mask=" << TQString::number(m_mask,8)
+ << "\n with ~mask (mask bits we keep) =" << TQString::number((uint)~m_mask,8)
+ << "\n bits we keep =" << TQString::number(item->permissions() & ~m_mask,8)
+ << "\n new permissions = " << TQString::number(info.permissions,8)
+ << endl;*/
+ m_infos.prepend( info );
+ //kdDebug(7007) << "processList : Adding info for " << info.url.prettyURL() << endl;
+ // Directory and recursive -> list
+ if ( item->isDir() && m_recursive )
+ {
+ //kdDebug(7007) << "ChmodJob::processList dir -> listing" << endl;
+ TDEIO::ListJob * listJob = TDEIO::listRecursive( item->url(), false /* no GUI */ );
+ connect( listJob, TQT_SIGNAL(entries( TDEIO::Job *,
+ const TDEIO::UDSEntryList& )),
+ TQT_SLOT( slotEntries( TDEIO::Job*,
+ const TDEIO::UDSEntryList& )));
+ addSubjob( listJob );
+ return; // we'll come back later, when this one's finished
+ }
+ }
+ m_lstItems.removeFirst();
+ }
+ kdDebug(7007) << "ChmodJob::processList -> going to STATE_CHMODING" << endl;
+ // We have finished, move on
+ state = STATE_CHMODING;
+ chmodNextFile();
+}
+
+void ChmodJob::slotEntries( TDEIO::Job*, const TDEIO::UDSEntryList & list )
+{
+ TDEIO::UDSEntryListConstIterator it = list.begin();
+ TDEIO::UDSEntryListConstIterator end = list.end();
+ for (; it != end; ++it) {
+ TDEIO::UDSEntry::ConstIterator it2 = (*it).begin();
+ mode_t permissions = 0;
+ bool isDir = false;
+ bool isLink = false;
+ TQString relativePath;
+ for( ; it2 != (*it).end(); it2++ ) {
+ switch( (*it2).m_uds ) {
+ case TDEIO::UDS_NAME:
+ relativePath = (*it2).m_str;
+ break;
+ case TDEIO::UDS_FILE_TYPE:
+ isDir = S_ISDIR((*it2).m_long);
+ break;
+ case TDEIO::UDS_LINK_DEST:
+ isLink = !(*it2).m_str.isEmpty();
+ break;
+ case TDEIO::UDS_ACCESS:
+ permissions = (mode_t)((*it2).m_long);
+ break;
+ default:
+ break;
+ }
+ }
+ if ( !isLink && relativePath != TQString::fromLatin1("..") )
+ {
+ ChmodInfo info;
+ info.url = m_lstItems.first()->url(); // base directory
+ info.url.addPath( relativePath );
+ int mask = m_mask;
+ // Emulate -X: only give +x to files that had a +x bit already
+ // So the check is the opposite : if the file had no x bit, don't touch x bits
+ // For dirs this doesn't apply
+ if ( !isDir )
+ {
+ int newPerms = m_permissions & mask;
+ if ( (newPerms & 0111) && !(permissions & 0111) )
+ {
+ // don't interfere with mandatory file locking
+ if ( newPerms & 02000 )
+ mask = mask & ~0101;
+ else
+ mask = mask & ~0111;
+ }
+ }
+ info.permissions = ( m_permissions & mask ) | ( permissions & ~mask );
+ /*kdDebug(7007) << "\n current permissions=" << TQString::number(permissions,8)
+ << "\n wanted permission=" << TQString::number(m_permissions,8)
+ << "\n with mask=" << TQString::number(mask,8)
+ << "\n with ~mask (mask bits we keep) =" << TQString::number((uint)~mask,8)
+ << "\n bits we keep =" << TQString::number(permissions & ~mask,8)
+ << "\n new permissions = " << TQString::number(info.permissions,8)
+ << endl;*/
+ // Prepend this info in our todo list.
+ // This way, the toplevel dirs are done last.
+ m_infos.prepend( info );
+ }
+ }
+}
+
+void ChmodJob::chmodNextFile()
+{
+ if ( !m_infos.isEmpty() )
+ {
+ ChmodInfo info = m_infos.first();
+ m_infos.remove( m_infos.begin() );
+ // First update group / owner (if local file)
+ // (permissions have to set after, in case of suid and sgid)
+ if ( info.url.isLocalFile() && ( m_newOwner != -1 || m_newGroup != -1 ) )
+ {
+ TQString path = info.url.path();
+ if ( chown( TQFile::encodeName(path), m_newOwner, m_newGroup ) != 0 )
+ {
+ int answer = KMessageBox::warningContinueCancel( 0, i18n( "<qt>Could not modify the ownership of file <b>%1</b>. You have insufficient access to the file to perform the change.</qt>" ).arg(path), TQString::null, i18n("&Skip File") );
+ if (answer == KMessageBox::Cancel)
+ {
+ m_error = ERR_USER_CANCELED;
+ emitResult();
+ return;
+ }
+ }
+ }
+
+ kdDebug(7007) << "ChmodJob::chmodNextFile chmod'ing " << info.url.prettyURL()
+ << " to " << TQString::number(info.permissions,8) << endl;
+ TDEIO::SimpleJob * job = TDEIO::chmod( info.url, info.permissions );
+ // copy the metadata for acl and default acl
+ const TQString aclString = queryMetaData( "ACL_STRING" );
+ const TQString defaultAclString = queryMetaData( "DEFAULT_ACL_STRING" );
+ if ( !aclString.isEmpty() )
+ job->addMetaData( "ACL_STRING", aclString );
+ if ( !defaultAclString.isEmpty() )
+ job->addMetaData( "DEFAULT_ACL_STRING", defaultAclString );
+ addSubjob(job);
+ }
+ else
+ // We have finished
+ emitResult();
+}
+
+void ChmodJob::slotResult( TDEIO::Job * job )
+{
+ if ( job->error() )
+ {
+ m_error = job->error();
+ m_errorText = job->errorText();
+ emitResult();
+ return;
+ }
+ //kdDebug(7007) << " ChmodJob::slotResult( TDEIO::Job * job ) m_lstItems:" << m_lstItems.count() << endl;
+ switch ( state )
+ {
+ case STATE_LISTING:
+ subjobs.remove(job);
+ m_lstItems.removeFirst();
+ kdDebug(7007) << "ChmodJob::slotResult -> processList" << endl;
+ processList();
+ return;
+ case STATE_CHMODING:
+ subjobs.remove(job);
+ kdDebug(7007) << "ChmodJob::slotResult -> chmodNextFile" << endl;
+ chmodNextFile();
+ return;
+ default:
+ assert(0);
+ return;
+ }
+}
+
+// antlarr: KDE 4: Make owner and group be const TQString &
+TDEIO_EXPORT ChmodJob *TDEIO::chmod( const KFileItemList& lstItems, int permissions, int mask,
+ TQString owner, TQString group,
+ bool recursive, bool showProgressInfo )
+{
+ uid_t newOwnerID = (uid_t)-1; // chown(2) : -1 means no change
+ if ( !owner.isEmpty() )
+ {
+ struct passwd* pw = getpwnam(TQFile::encodeName(owner));
+ if ( pw == 0L )
+ kdError(250) << " ERROR: No user " << owner << endl;
+ else
+ newOwnerID = pw->pw_uid;
+ }
+ gid_t newGroupID = (gid_t)-1; // chown(2) : -1 means no change
+ if ( !group.isEmpty() )
+ {
+ struct group* g = getgrnam(TQFile::encodeName(group));
+ if ( g == 0L )
+ kdError(250) << " ERROR: No group " << group << endl;
+ else
+ newGroupID = g->gr_gid;
+ }
+ return new ChmodJob( lstItems, permissions, mask, newOwnerID, newGroupID, recursive, showProgressInfo );
+}
+
+void ChmodJob::virtual_hook( int id, void* data )
+{ TDEIO::Job::virtual_hook( id, data ); }
+
+#include "chmodjob.moc"
diff --git a/tdeio/tdeio/chmodjob.h b/tdeio/tdeio/chmodjob.h
new file mode 100644
index 000000000..105df0e4d
--- /dev/null
+++ b/tdeio/tdeio/chmodjob.h
@@ -0,0 +1,109 @@
+// -*- c++ -*-
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kio_chmodjob_h__
+#define __kio_chmodjob_h__
+
+#include <kurl.h>
+#include <tqstring.h>
+
+#include <tdeio/global.h>
+#include <tdeio/job.h>
+#include <tdefileitem.h>
+
+namespace TDEIO {
+
+ /**
+ * This job changes permissions on a list of files or directories,
+ * optionally in a recursive manner.
+ * @see TDEIO::chmod()
+ */
+ class TDEIO_EXPORT ChmodJob : public TDEIO::Job
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Create new ChmodJobs using the TDEIO::chmod() function.
+ */
+ ChmodJob( const KFileItemList & lstItems, int permissions, int mask,
+ int newOwner, int newGroup,
+ bool recursive, bool showProgressInfo );
+
+ protected:
+ void chmodNextFile();
+
+ protected slots:
+
+ virtual void slotResult( TDEIO::Job *job );
+ void slotEntries( TDEIO::Job * , const TDEIO::UDSEntryList & );
+ void processList();
+
+ private:
+ struct ChmodInfo
+ {
+ KURL url;
+ int permissions;
+ };
+ enum { STATE_LISTING, STATE_CHMODING } state;
+ int m_permissions;
+ int m_mask;
+ int m_newOwner;
+ int m_newGroup;
+ bool m_recursive;
+ KFileItemList m_lstItems;
+ TQValueList<ChmodInfo> m_infos;
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ private:
+ class ChmodJobPrivate* d;
+ };
+
+
+ /**
+ * Creates a job that changes permissions/ownership on several files or directories,
+ * optionally recursively.
+ * This version of chmod uses a KFileItemList so that it directly knows
+ * what to do with the items. TODO: a version that takes a KURL::List,
+ * and a general job that stats each url and returns a KFileItemList.
+ *
+ * Note that change of ownership is only supported for local files.
+ *
+ * Inside directories, the "x" bits will only be changed for files that had
+ * at least one "x" bit before, and for directories.
+ * This emulates the behavior of chmod +X.
+ *
+ * @param lstItems The file items representing several files or directories.
+ * @param permissions the permissions we want to set
+ * @param mask the bits we are allowed to change.
+ * For instance, if mask is 0077, we don't change
+ * the "user" bits, only "group" and "others".
+ * @param newOwner If non-empty, the new owner for the files
+ * @param newGroup If non-empty, the new group for the files
+ * @param recursive whether to open directories recursively
+ * @param showProgressInfo true to show progess information
+ * @return The job handling the operation.
+ */
+ TDEIO_EXPORT ChmodJob * chmod( const KFileItemList& lstItems, int permissions, int mask,
+ TQString newOwner, TQString newGroup,
+ bool recursive, bool showProgressInfo = true );
+
+}
+
+#endif
diff --git a/tdeio/tdeio/configure.in.in b/tdeio/tdeio/configure.in.in
new file mode 100644
index 000000000..8683dfec1
--- /dev/null
+++ b/tdeio/tdeio/configure.in.in
@@ -0,0 +1,167 @@
+dnl ------------------------------------------------------------------------
+dnl Try to find if FAM is installed
+dnl ------------------------------------------------------------------------
+dnl
+kde_have_fam=yes
+AC_ARG_ENABLE(libfam,
+ AC_HELP_STRING([--disable-libfam],[don't search for libfam and do not use it]),
+[ kde_have_fam=$enableval ], [])dnl
+
+dnl Bloody libfam is C++ and certainly compiled by GNU C++. This means,
+dnl we can't use it, when compiling with another C++ compiler, as the
+dnl runtime systems would conflict (e.g. in KAI C++) (matz)
+test "$GXX" = yes || kde_have_fam=no
+
+if test "$kde_have_fam" = "yes" ; then
+ AC_LANG_SAVE
+ AC_LANG_CPLUSPLUS
+ KDE_CHECK_LIB(fam, FAMOpen, [LIBFAM="-lfam"; kde_have_fam=yes],kde_have_fam=no)
+ if test $kde_have_fam = yes; then
+ AC_DEFINE_UNQUOTED(HAVE_FAM, 1, [Define if your system has libfam])
+ fi
+ AC_LANG_RESTORE
+fi
+AC_SUBST(LIBFAM)
+dnl ------------------------------------------------------------------------
+dnl Try to find if LIBZ is installed
+dnl ------------------------------------------------------------------------
+dnl
+
+AC_FIND_ZLIB
+
+AC_CHECK_HEADERS(sys/mnttab.h sys/mntent.h mntent.h fstab.h sys/ucred.h sys/mount.h)
+AC_CHECK_FUNCS(setmntent getmntinfo)
+
+AH_VERBATIM(_GETMNTINFO, [
+#ifdef __osf__
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include <sys/mount.h>
+int getmntinfo(struct statfs **mntbufp, int flags);
+#include <sys/fs_types.h> /* for mnt_names[] */
+#ifdef __cplusplus
+}
+#endif
+#endif
+])
+
+dnl ------------------------------------------------------------------------
+dnl Try to find if libvolmgt is installed (Solaris)
+dnl ------------------------------------------------------------------------
+kde_have_volmgt=yes
+AC_CHECK_LIB(volmgt, volmgt_running, [LIBVOLMGT=-lvolmgt], kde_have_volmgt=no)
+AC_SUBST(LIBVOLMGT)
+if test "$kde_have_volmgt" = "yes"; then
+ AC_DEFINE_UNQUOTED(HAVE_VOLMGT, 1, [Define, to enable volume management (Solaris 2.x), if you have -lvolmgt])
+fi
+
+dnl ------------------------------------------------------------------------
+dnl Try to find if we have Linux Dir Notification
+dnl ------------------------------------------------------------------------
+
+AC_ARG_ENABLE(dnotify,
+AC_HELP_STRING([--enable-dnotify],[enable use of Linux directory notifications]),
+[ kde_enable_dnotify=$enableval ], [])dnl
+
+AC_CHECK_GNU_EXTENSIONS
+
+if test "x$kde_enable_dnotify" = "xyes"; then
+ AC_MSG_CHECKING([for Linux Directory Notification])
+ AC_CACHE_VAL(kde_cv_have_dnotify,
+ [
+ kde_cv_have_dnotify=no
+ AC_LANG_SAVE
+ AC_LANG_C
+
+ AC_TRY_COMPILE(
+ [
+#include <fcntl.h>
+#include <signal.h>
+ ],
+ [
+#ifndef F_NOTIFY
+#error no dir notification
+#endif
+ int fd;
+ siginfo_t *t = 0;
+
+ fcntl(fd, F_SETSIG, SIGRTMIN);
+ fcntl(fd, F_NOTIFY, DN_DELETE|DN_CREATE|DN_MULTISHOT);
+
+ ],kde_cv_have_dnotify=yes)
+
+ AC_LANG_RESTORE
+ ])
+
+ if test "$kde_cv_have_dnotify" = "yes" ; then
+ AC_DEFINE_UNQUOTED(HAVE_DNOTIFY, 1, [Define if your system has Linux Directory Notification])
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+fi
+
+dnl ------------------------------------------------------------------------
+dnl Try to find if we have Linux Inode based Dir Notification
+dnl ------------------------------------------------------------------------
+
+AC_ARG_ENABLE(inotify,
+AC_HELP_STRING([--disable-inotify],[enable use of Linux inode notifications]),
+[ kde_enable_inotify=$enableval ], [kde_enable_inotify=yes])dnl
+
+AC_CHECK_GNU_EXTENSIONS
+
+if test "x$kde_enable_inotify" = "xyes"; then
+ AC_MSG_CHECKING([for Linux Inotify Notification])
+ AC_CACHE_VAL(kde_cv_have_inotify,
+ [
+ kde_cv_have_inotify=no
+ AC_LANG_SAVE
+ AC_LANG_C
+
+ AC_TRY_COMPILE(
+ [
+#include <asm/unistd.h>
+#define _S390_BITOPS_H
+#include <linux/inotify.h>
+ ],
+ [
+#ifndef IN_ALL_EVENTS
+#error no inotify notification
+#endif
+
+ ],kde_cv_have_inotify=yes,kde_cv_have_inotify=no)
+
+ AC_LANG_RESTORE
+ ])
+
+ AC_CACHE_VAL(kde_cv_have_sys_inotify,
+ [
+ kde_cv_have_sys_inotify=no
+ AC_LANG_SAVE
+ AC_LANG_C
+
+ AC_TRY_COMPILE(
+ [
+#include <sys/inotify.h>
+ ],
+ [
+#ifndef IN_ALL_EVENTS
+#error no inotify notification
+#endif
+ ],kde_cv_have_sys_inotify=yes,kde_cv_have_sys_inotify=no)
+
+ AC_LANG_RESTORE
+ ])
+
+ if test "$kde_cv_have_inotify" = "yes" -o "$kde_cv_have_sys_inotify" = "yes"; then
+ AC_DEFINE_UNQUOTED(HAVE_INOTIFY, 1, [Define if your system has Linux Inode Notification])
+ if test "$kde_cv_have_sys_inotify" = "yes"; then
+ AC_DEFINE_UNQUOTED(HAVE_SYS_INOTIFY, 1, [Define if your system has glibc support for inotify])
+ fi
+ AC_MSG_RESULT(yes)
+ else
+ AC_MSG_RESULT(no)
+ fi
+fi
diff --git a/tdeio/tdeio/connection.cpp b/tdeio/tdeio/connection.cpp
new file mode 100644
index 000000000..cbe737693
--- /dev/null
+++ b/tdeio/tdeio/connection.cpp
@@ -0,0 +1,273 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
+ David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+// $Id$
+
+#include <config.h>
+
+#include <kde_file.h>
+#include <ksock.h>
+#include <tqtimer.h>
+
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/time.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "tdeio/connection.h"
+
+#include <kdebug.h>
+#include <tqsocketnotifier.h>
+
+using namespace TDEIO;
+
+Connection::Connection()
+{
+ f_out = 0;
+ fd_in = -1;
+ socket = 0;
+ notifier = 0;
+ receiver = 0;
+ member = 0;
+ m_suspended = false;
+ tasks.setAutoDelete(true);
+}
+
+Connection::~Connection()
+{
+ close();
+}
+
+void Connection::suspend()
+{
+ m_suspended = true;
+ if (notifier)
+ notifier->setEnabled(false);
+}
+
+void Connection::resume()
+{
+ m_suspended = false;
+ if (notifier)
+ notifier->setEnabled(true);
+}
+
+void Connection::close()
+{
+ delete notifier;
+ notifier = 0;
+ delete socket;
+ socket = 0;
+
+ // TDESocket has already closed the file descriptor, but we need to
+ // close the file-stream as well otherwise we leak memory.
+ // As a result we close the file descriptor twice, but that should
+ // be harmless
+ // KDE4: fix this
+ if (f_out)
+ fclose(f_out);
+ f_out = 0;
+ fd_in = -1;
+ tasks.clear();
+}
+
+void Connection::send(int cmd, const TQByteArray& data)
+{
+ if (!inited() || tasks.count() > 0) {
+ Task *task = new Task();
+ task->cmd = cmd;
+ task->data = data;
+ tasks.append(task);
+ } else {
+ sendnow( cmd, data );
+ }
+}
+
+void Connection::dequeue()
+{
+ if (!inited())
+ return;
+
+ while (tasks.count())
+ {
+ tasks.first();
+ Task *task = tasks.take();
+ sendnow( task->cmd, task->data );
+ delete task;
+ }
+}
+
+void Connection::init(TDESocket *sock)
+{
+ delete notifier;
+ notifier = 0;
+#ifdef Q_OS_UNIX //TODO: not yet available on WIN32
+ delete socket;
+ socket = sock;
+ fd_in = socket->socket();
+ f_out = KDE_fdopen( socket->socket(), "wb" );
+#endif
+ if (receiver && ( fd_in != -1 )) {
+ notifier = new TQSocketNotifier(fd_in, TQSocketNotifier::Read);
+ if ( m_suspended ) {
+ suspend();
+ }
+ TQObject::connect(notifier, TQT_SIGNAL(activated(int)), receiver, member);
+ }
+ dequeue();
+}
+
+void Connection::init(int _fd_in, int fd_out)
+{
+ delete notifier;
+ notifier = 0;
+ fd_in = _fd_in;
+ f_out = KDE_fdopen( fd_out, "wb" );
+ if (receiver && ( fd_in != -1 )) {
+ notifier = new TQSocketNotifier(fd_in, TQSocketNotifier::Read);
+ if ( m_suspended ) {
+ suspend();
+ }
+ TQObject::connect(notifier, TQT_SIGNAL(activated(int)), receiver, member);
+ }
+ dequeue();
+}
+
+
+void Connection::connect(TQObject *_receiver, const char *_member)
+{
+ receiver = _receiver;
+ member = _member;
+ delete notifier;
+ notifier = 0;
+ if (receiver && (fd_in != -1 )) {
+ notifier = new TQSocketNotifier(fd_in, TQSocketNotifier::Read);
+ if ( m_suspended )
+ suspend();
+ TQObject::connect(notifier, TQT_SIGNAL(activated(int)), receiver, member);
+ }
+}
+
+bool Connection::sendnow( int _cmd, const TQByteArray &data )
+{
+ if (f_out == 0) {
+ return false;
+ }
+
+ if (data.size() > 0xffffff)
+ return false;
+
+ static char buffer[ 64 ];
+ sprintf( buffer, "%6x_%2x_", data.size(), _cmd );
+
+ size_t n = fwrite( buffer, 1, 10, f_out );
+
+ if ( n != 10 ) {
+ kdError(7017) << "Could not send header" << endl;
+ return false;
+ }
+
+ n = fwrite( data.data(), 1, data.size(), f_out );
+
+ if ( n != data.size() ) {
+ kdError(7017) << "Could not write data" << endl;
+ return false;
+ }
+
+ if (fflush( f_out )) {
+ kdError(7017) << "Could not write data" << endl;
+ return false;
+ }
+
+ return true;
+}
+
+int Connection::read( int* _cmd, TQByteArray &data )
+{
+ if (fd_in == -1 ) {
+ kdError(7017) << "read: not yet inited" << endl;
+ return -1;
+ }
+
+ static char buffer[ 10 ];
+
+ again1:
+ ssize_t n = ::read( fd_in, buffer, 10);
+ if ( n == -1 && errno == EINTR )
+ goto again1;
+
+ if ( n == -1) {
+ kdError(7017) << "Header read failed, errno=" << errno << endl;
+ }
+
+ if ( n != 10 ) {
+ if ( n ) // 0 indicates end of file
+ kdError(7017) << "Header has invalid size (" << n << ")" << endl;
+ return -1;
+ }
+
+ buffer[ 6 ] = 0;
+ buffer[ 9 ] = 0;
+
+ char *p = buffer;
+ while( *p == ' ' ) p++;
+ long int len = strtol( p, 0L, 16 );
+
+ p = buffer + 7;
+ while( *p == ' ' ) p++;
+ long int cmd = strtol( p, 0L, 16 );
+
+ data.resize( len );
+
+ if ( len > 0L ) {
+ size_t bytesToGo = len;
+ size_t bytesRead = 0;
+ do {
+ n = ::read(fd_in, data.data()+bytesRead, bytesToGo);
+ if (n == -1) {
+ if (errno == EINTR)
+ continue;
+
+ kdError(7017) << "Data read failed, errno=" << errno << endl;
+ return -1;
+ }
+ if ( !n ) { // 0 indicates end of file
+ kdError(7017) << "Connection ended unexpectedly (" << n << "/" << bytesToGo << ")" << endl;
+ return -1;
+ }
+
+ bytesRead += n;
+ bytesToGo -= n;
+ }
+ while(bytesToGo);
+ }
+
+ *_cmd = cmd;
+ return len;
+}
+
+#include "connection.moc"
diff --git a/tdeio/tdeio/connection.h b/tdeio/tdeio/connection.h
new file mode 100644
index 000000000..fb0b50e15
--- /dev/null
+++ b/tdeio/tdeio/connection.h
@@ -0,0 +1,158 @@
+// -*- c++ -*-
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
+ David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __connection_h__
+#define __connection_h__
+
+#include <tdelibs_export.h>
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <tqptrlist.h>
+#include <tqobject.h>
+
+class TDESocket;
+class TQSocketNotifier;
+
+namespace TDEIO {
+
+ struct TDEIO_EXPORT Task {
+ int cmd;
+ TQByteArray data;
+ };
+
+ /**
+ * This class provides a simple means for IPC between two applications
+ * via a pipe.
+ * It handles a queue of commands to be sent which makes it possible to
+ * queue data before an actual connection has been established.
+ */
+ class TDEIO_EXPORT Connection : public TQObject
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Creates a new connection.
+ * @see init()
+ */
+ Connection();
+ virtual ~Connection();
+
+ /**
+ * Initialize this connection to use the given socket.
+ * @param sock the socket to use
+ * @see inited()
+ */
+ void init(TDESocket *sock);
+ /**
+ * Initialize the connection to use the given file
+ * descriptors.
+ * @param fd_in the input file descriptor to use
+ * @param fd_out the output file descriptor to use
+ * @see inited()
+ */
+ void init(int fd_in, int fd_out); // Used by KDENOX
+ void connect(TQObject *receiver = 0, const char *member = 0);
+ /// Closes the connection.
+ void close();
+
+ /**
+ * Returns the input file descriptor.
+ * @return the input file descriptor
+ */
+ int fd_from() const { return fd_in; }
+ /**
+ * Returns the output file descriptor.
+ * @return the output file descriptor
+ */
+ int fd_to() const { return fileno( f_out ); }
+
+ /**
+ * Checks whether the connection has been initialized.
+ * @return true if the initialized
+ * @see init()
+ */
+ bool inited() const { return (fd_in != -1) && (f_out != 0); }
+
+ /**
+ * Sends/queues the given command to be sent.
+ * @param cmd the command to set
+ * @param arr the bytes to send
+ */
+ void send(int cmd, const TQByteArray &arr = TQByteArray());
+
+ /**
+ * Sends the given command immediately.
+ * @param _cmd the command to set
+ * @param data the bytes to send
+ * @return true if successful, false otherwise
+ */
+ bool sendnow( int _cmd, const TQByteArray &data );
+
+ /**
+ * Receive data.
+ *
+ * @param _cmd the received command will be written here
+ * @param data the received data will be written here
+ * @return >=0 indicates the received data size upon success
+ * -1 indicates error
+ */
+ int read( int* _cmd, TQByteArray &data );
+
+ /**
+ * Don't handle incoming data until resumed.
+ */
+ void suspend();
+
+ /**
+ * Resume handling of incoming data.
+ */
+ void resume();
+
+ /**
+ * Returns status of connection.
+ * @return true if suspended, false otherwise
+ */
+ bool suspended() const { return m_suspended; }
+
+ protected slots:
+ void dequeue();
+
+ protected:
+
+
+ private:
+ int fd_in;
+ FILE *f_out;
+ TDESocket *socket;
+ TQSocketNotifier *notifier;
+ TQObject *receiver;
+ const char *member;
+ TQPtrList<Task> tasks;
+ bool m_suspended;
+ private:
+ class ConnectionPrivate* d;
+ };
+
+}
+
+#endif
diff --git a/tdeio/tdeio/dataprotocol.cpp b/tdeio/tdeio/dataprotocol.cpp
new file mode 100644
index 000000000..acc7b28e9
--- /dev/null
+++ b/tdeio/tdeio/dataprotocol.cpp
@@ -0,0 +1,339 @@
+// dataprotocol.cpp
+// ==================
+//
+// Implementation of the data protocol (rfc 2397)
+//
+// Author: Leo Savernik
+// Email: l.savernik@aon.at
+// (C) 2002, 2003 by Leo Savernik
+// Created: Sam Dez 28 14:11:18 CET 2002
+
+/***************************************************************************
+ * *
+ * This program 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; version 2. *
+ * *
+ ***************************************************************************/
+
+#include "dataprotocol.h"
+
+#include <kdebug.h>
+#include <kmdcodec.h>
+#include <kurl.h>
+#include <tdeio/global.h>
+
+#include <tqcstring.h>
+#include <tqstring.h>
+#include <tqstringlist.h>
+#include <tqtextcodec.h>
+
+#ifdef DATAKIOSLAVE
+# include <kinstance.h>
+# include <stdlib.h>
+#endif
+#ifdef TESTKIO
+# include <iostream.h>
+#endif
+
+#if !defined(DATAKIOSLAVE) && !defined(TESTKIO)
+# define DISPATCH(f) dispatch_##f
+#else
+# define DISPATCH(f) f
+#endif
+
+using namespace TDEIO;
+#ifdef DATAKIOSLAVE
+extern "C" {
+
+ int kdemain( int argc, char **argv ) {
+ TDEInstance instance( "kio_data" );
+
+ kdDebug(7101) << "*** Starting kio_data " << endl;
+
+ if (argc != 4) {
+ kdDebug(7101) << "Usage: kio_data protocol domain-socket1 domain-socket2" << endl;
+ exit(-1);
+ }
+
+ DataProtocol slave(argv[2], argv[3]);
+ slave.dispatchLoop();
+
+ kdDebug(7101) << "*** kio_data Done" << endl;
+ return 0;
+ }
+}
+#endif
+
+/** structure containing header information */
+struct DataHeader {
+ TQString mime_type; // mime type of content (lowercase)
+ MetaData attributes; // attribute/value pairs (attribute lowercase,
+ // value unchanged)
+ bool is_base64; // true if data is base64 encoded
+ TQString url; // reference to decoded url
+ int data_offset; // zero-indexed position within url
+ // where the real data begins. May point beyond
+ // the end to indicate that there is no data
+ TQString *charset; // shortcut to charset (it always exists)
+};
+
+// constant string data
+const TQChar text_plain_str[] = { 't','e','x','t','/','p','l','a','i','n' };
+const TQChar charset_str[] = { 'c','h','a','r','s','e','t' };
+const TQChar us_ascii_str[] = { 'u','s','-','a','s','c','i','i' };
+const TQChar base64_str[] = { 'b','a','s','e','6','4' };
+
+/** returns the position of the first occurrence of any of the given characters
+ * @p c1 to @p c3 or buf.length() if none is contained.
+ * @param buf buffer where to look for c
+ * @param begin zero-indexed starting position
+ * @param c1 character to find
+ * @param c2 alternative character to find or '\0' to ignore
+ * @param c3 alternative character to find or '\0' to ignore
+ */
+static int find(const TQString &buf, int begin, TQChar c1, TQChar c2 = '\0',
+ TQChar c3 = '\0') {
+ int pos = begin;
+ int size = (int)buf.length();
+ while (pos < size) {
+ TQChar ch = buf[pos];
+ if (ch == c1
+ || (c2 != '\0' && ch == c2)
+ || (c3 != '\0' && ch == c3))
+ break;
+ pos++;
+ }/*wend*/
+ return pos;
+}
+
+/** extracts the string between the current position @p pos and the first
+ * occurrence of either @p c1 to @p c3 exclusively and updates @p pos
+ * to point at the found delimiter or at the end of the buffer if
+ * neither character occurred.
+ * @param buf buffer where to look for
+ * @param pos zero-indexed position within buffer
+ * @param c1 character to find
+ * @param c2 alternative character to find or 0 to ignore
+ * @param c3 alternative character to find or 0 to ignore
+ */
+inline TQString extract(const TQString &buf, int &pos, TQChar c1,
+ TQChar c2 = '\0', TQChar c3 = '\0') {
+ int oldpos = pos;
+ pos = find(buf,oldpos,c1,c2,c3);
+ return TQString(buf.unicode() + oldpos, pos - oldpos);
+}
+
+/** ignores all whitespaces
+ * @param buf buffer to operate on
+ * @param pos position to shift to first non-whitespace character
+ * Upon return @p pos will either point to the first non-whitespace
+ * character or to the end of the buffer.
+ */
+inline void ignoreWS(const TQString &buf, int &pos) {
+ int size = (int)buf.length();
+ TQChar ch = buf[pos];
+ while (pos < size && (ch == ' ' || ch == '\t' || ch == '\n'
+ || ch == '\r'))
+ ch = buf[++pos];
+}
+
+/** parses a quoted string as per rfc 822.
+ *
+ * If trailing quote is missing, the whole rest of the buffer is returned.
+ * @param buf buffer to operate on
+ * @param pos position pointing to the leading quote
+ * @return the extracted string. @p pos will be updated to point to the
+ * character following the trailing quote.
+ */
+static TQString parseQuotedString(const TQString &buf, int &pos) {
+ int size = (int)buf.length();
+ TQString res;
+ pos++; // jump over leading quote
+ bool escaped = false; // if true means next character is literal
+ bool parsing = true; // true as long as end quote not found
+ while (parsing && pos < size) {
+ TQChar ch = buf[pos++];
+ if (escaped) {
+ res += ch;
+ escaped = false;
+ } else {
+ switch (ch) {
+ case '"': parsing = false; break;
+ case '\\': escaped = true; break;
+ default: res += ch; break;
+ }/*end switch*/
+ }/*end if*/
+ }/*wend*/
+ return res;
+}
+
+/** parses the header of a data url
+ * @param url the data url
+ * @param header_info fills the given DataHeader structure with the header
+ * information
+ */
+static void parseDataHeader(const KURL &url, DataHeader &header_info) {
+ TQConstString text_plain(text_plain_str,sizeof text_plain_str/sizeof text_plain_str[0]);
+ TQConstString charset(charset_str,sizeof charset_str/sizeof charset_str[0]);
+ TQConstString us_ascii(us_ascii_str,sizeof us_ascii_str/sizeof us_ascii_str[0]);
+ TQConstString base64(base64_str,sizeof base64_str/sizeof base64_str[0]);
+ // initialize header info members
+ header_info.mime_type = text_plain.string();
+ header_info.charset = &header_info.attributes.insert(
+ charset.string(),us_ascii.string())
+ .data();
+ header_info.is_base64 = false;
+
+ // decode url and save it
+ TQString &raw_url = header_info.url = TQString::fromLatin1("data:") + url.path();
+ int raw_url_len = (int)raw_url.length();
+
+ // jump over scheme part (must be "data:", we don't even check that)
+ header_info.data_offset = raw_url.find(':');
+ header_info.data_offset++; // jump over colon or to begin if scheme was missing
+
+ // read mime type
+ if (header_info.data_offset >= raw_url_len) return;
+ TQString mime_type = extract(raw_url,header_info.data_offset,';',',')
+ .stripWhiteSpace();
+ if (!mime_type.isEmpty()) header_info.mime_type = mime_type;
+
+ if (header_info.data_offset >= raw_url_len) return;
+ // jump over delimiter token and return if data reached
+ if (raw_url[header_info.data_offset++] == ',') return;
+
+ // read all attributes and store them
+ bool data_begin_reached = false;
+ while (!data_begin_reached && header_info.data_offset < raw_url_len) {
+ // read attribute
+ TQString attribute = extract(raw_url,header_info.data_offset,'=',';',',')
+ .stripWhiteSpace();
+ if (header_info.data_offset >= raw_url_len
+ || raw_url[header_info.data_offset] != '=') {
+ // no assigment, must be base64 option
+ if (attribute == base64.string())
+ header_info.is_base64 = true;
+ } else {
+ header_info.data_offset++; // jump over '=' token
+
+ // read value
+ ignoreWS(raw_url,header_info.data_offset);
+ if (header_info.data_offset >= raw_url_len) return;
+
+ TQString value;
+ if (raw_url[header_info.data_offset] == '"') {
+ value = parseQuotedString(raw_url,header_info.data_offset);
+ ignoreWS(raw_url,header_info.data_offset);
+ } else
+ value = extract(raw_url,header_info.data_offset,';',',')
+ .stripWhiteSpace();
+
+ // add attribute to map
+ header_info.attributes[attribute.lower()] = value;
+
+ }/*end if*/
+ if (header_info.data_offset < raw_url_len
+ && raw_url[header_info.data_offset] == ',')
+ data_begin_reached = true;
+ header_info.data_offset++; // jump over separator token
+ }/*wend*/
+}
+
+#ifdef DATAKIOSLAVE
+DataProtocol::DataProtocol(const TQCString &pool_socket, const TQCString &app_socket)
+ : SlaveBase("kio_data", pool_socket, app_socket) {
+#else
+DataProtocol::DataProtocol() {
+#endif
+ kdDebug() << "DataProtocol::DataProtocol()" << endl;
+}
+
+/* --------------------------------------------------------------------- */
+
+DataProtocol::~DataProtocol() {
+ kdDebug() << "DataProtocol::~DataProtocol()" << endl;
+}
+
+/* --------------------------------------------------------------------- */
+
+void DataProtocol::get(const KURL& url) {
+ ref();
+ //kdDebug() << "===============================================================================================================================================================================" << endl;
+ kdDebug() << "kio_data@"<<this<<"::get(const KURL& url)" << endl ;
+
+ DataHeader hdr;
+ parseDataHeader(url,hdr);
+
+ int size = (int)hdr.url.length();
+ int data_ofs = QMIN(hdr.data_offset,size);
+ // FIXME: string is copied, would be nice if we could have a reference only
+ TQString url_data = hdr.url.mid(data_ofs);
+ TQCString outData;
+
+#ifdef TESTKIO
+// cout << "current charset: \"" << *hdr.charset << "\"" << endl;
+#endif
+ if (hdr.is_base64) {
+ // base64 stuff is expected to contain the correct charset, so we just
+ // decode it and pass it to the receiver
+ KCodecs::base64Decode(url_data.local8Bit(),outData);
+ } else {
+ // FIXME: This is all flawed, must be reworked thoroughly
+ // non encoded data must be converted to the given charset
+ TQTextCodec *codec = TQTextCodec::codecForName(hdr.charset->latin1());
+ if (codec != 0) {
+ outData = codec->fromUnicode(url_data);
+ } else {
+ // if there is no approprate codec, just use local encoding. This
+ // should work for >90% of all cases.
+ outData = url_data.local8Bit();
+ }/*end if*/
+ }/*end if*/
+
+ //kdDebug() << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;
+ //kdDebug() << "emit mimeType@"<<this << endl ;
+ mimeType(hdr.mime_type);
+ //kdDebug() << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;
+ //kdDebug() << "emit totalSize@"<<this << endl ;
+ totalSize(outData.size());
+
+ //kdDebug() << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;
+ //kdDebug() << "emit setMetaData@"<<this << endl ;
+#if defined(TESTKIO) || defined(DATAKIOSLAVE)
+ MetaData::ConstIterator it;
+ for (it = hdr.attributes.begin(); it != hdr.attributes.end(); ++it) {
+ setMetaData(it.key(),it.data());
+ }/*next it*/
+#else
+ setAllMetaData(hdr.attributes);
+#endif
+
+ //kdDebug() << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;
+ //kdDebug() << "emit sendMetaData@"<<this << endl ;
+ sendMetaData();
+ //kdDebug() << "^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C^[[C" << endl;
+// kdDebug() << "(1) queue size " << dispatchQueue.size() << endl;
+ // empiric studies have shown that this shouldn't be queued & dispatched
+ /*DISPATCH*/(data(outData));
+// kdDebug() << "(2) queue size " << dispatchQueue.size() << endl;
+ DISPATCH(data(TQByteArray()));
+// kdDebug() << "(3) queue size " << dispatchQueue.size() << endl;
+ DISPATCH(finished());
+// kdDebug() << "(4) queue size " << dispatchQueue.size() << endl;
+ deref();
+}
+
+/* --------------------------------------------------------------------- */
+
+void DataProtocol::mimetype(const KURL &url) {
+ ref();
+ DataHeader hdr;
+ parseDataHeader(url,hdr);
+ mimeType(hdr.mime_type);
+ finished();
+ deref();
+}
+
+/* --------------------------------------------------------------------- */
+
diff --git a/tdeio/tdeio/dataprotocol.h b/tdeio/tdeio/dataprotocol.h
new file mode 100644
index 000000000..ed9bfc325
--- /dev/null
+++ b/tdeio/tdeio/dataprotocol.h
@@ -0,0 +1,71 @@
+// dataprotocol.h
+// ================
+//
+// Interface of the KDE data protocol core operations
+//
+// Author: Leo Savernik
+// Email: l.savernik@aon.at
+// (C) 2002 by Leo Savernik
+// Created: Sam Dez 28 14:11:18 CET 2002
+
+/***************************************************************************
+ * *
+ * This program 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; version 2. *
+ * *
+ ***************************************************************************/
+
+#ifndef __dataprotocol_h__
+#define __dataprotocol_h__
+
+// dataprotocol.* interprets the following defines
+// DATAKIOSLAVE: define if you want to compile this into a stand-alone
+// tdeioslave
+// TESTKIO: define for test-driving
+// Both defines are mutually exclusive. Defining none of them compiles
+// DataProtocol for internal usage within libtdeiocore.
+
+class TQString;
+class TQCString;
+
+class KURL;
+
+#if defined(DATAKIOSLAVE)
+# include <tdeio/slavebase.h>
+#elif !defined(TESTKIO)
+# include "tdeio/dataslave.h"
+#endif
+
+namespace TDEIO {
+
+/** This tdeioslave provides support of data urls as specified by rfc 2397
+ * @see http://www.ietf.org/rfc/rfc2397.txt
+ * @author Leo Savernik
+ */
+#if defined(DATAKIOSLAVE)
+class DataProtocol : public TDEIO::SlaveBase {
+#elif defined(TESTKIO)
+class DataProtocol : public TestSlave {
+#else
+class DataProtocol : public DataSlave {
+#endif
+
+public:
+#if defined(DATAKIOSLAVE)
+ DataProtocol(const TQCString &pool_socket, const TQCString &app_socket);
+#else
+ DataProtocol();
+#endif
+ virtual ~DataProtocol();
+ virtual void mimetype(const KURL &url);
+ virtual void get(const KURL &url);
+#if defined(TESTKIO)
+ void ref() {}
+ void deref() {}
+#endif
+};
+
+}/*end namespace*/
+
+#endif
diff --git a/tdeio/tdeio/dataslave.cpp b/tdeio/tdeio/dataslave.cpp
new file mode 100644
index 000000000..528368ba5
--- /dev/null
+++ b/tdeio/tdeio/dataslave.cpp
@@ -0,0 +1,213 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (c) 2003 Leo Savernik <l.savernik@aon.at>
+ * Derived from slave.cpp
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include <config.h>
+
+#include "dataslave.h"
+#include "dataprotocol.h"
+
+#include <klocale.h>
+#include <kdebug.h>
+
+#include <tqtimer.h>
+
+using namespace TDEIO;
+
+#define KIO_DATA_POLL_INTERVAL 0
+
+// don't forget to sync DISPATCH_DECL in dataslave.h
+#define DISPATCH_IMPL(type) \
+ void DataSlave::dispatch_##type() { \
+ if (_suspended) { \
+ QueueStruct q(Queue_##type); \
+ dispatchQueue.push_back(q); \
+ if (!timer->isActive()) timer->start(KIO_DATA_POLL_INTERVAL); \
+ } else \
+ type(); \
+ }
+
+// don't forget to sync DISPATCH_DECL1 in dataslave.h
+#define DISPATCH_IMPL1(type, paramtype, paramname) \
+ void DataSlave::dispatch_##type(paramtype paramname) { \
+ if (_suspended) { \
+ QueueStruct q(Queue_##type); \
+ q.paramname = paramname; \
+ dispatchQueue.push_back(q); \
+ if (!timer->isActive()) timer->start(KIO_DATA_POLL_INTERVAL); \
+ } else \
+ type(paramname); \
+ }
+
+
+DataSlave::DataSlave() :
+ Slave(true, 0, "data", TQString::null)
+{
+ //kdDebug() << this << k_funcinfo << endl;
+ _suspended = false;
+ timer = new TQTimer(this);
+ connect(timer, TQT_SIGNAL(timeout()), TQT_SLOT(dispatchNext()));
+}
+
+DataSlave::~DataSlave() {
+ //kdDebug() << this << k_funcinfo << endl;
+}
+
+void DataSlave::hold(const KURL &/*url*/) {
+ // ignored
+}
+
+void DataSlave::suspend() {
+ _suspended = true;
+ //kdDebug() << this << k_funcinfo << endl;
+ timer->stop();
+}
+
+void DataSlave::resume() {
+ _suspended = false;
+ //kdDebug() << this << k_funcinfo << endl;
+ // aarrrgh! This makes the once hyper fast and efficient data protocol
+ // implementation slow as molasses. But it wouldn't work otherwise,
+ // and I don't want to start messing around with threads
+ timer->start(KIO_DATA_POLL_INTERVAL);
+}
+
+// finished is a special case. If we emit it right away, then
+// TransferJob::start can delete the job even before the end of the method
+void DataSlave::dispatch_finished() {
+ QueueStruct q(Queue_finished);
+ dispatchQueue.push_back(q);
+ if (!timer->isActive()) timer->start(KIO_DATA_POLL_INTERVAL);
+}
+
+void DataSlave::dispatchNext() {
+ if (dispatchQueue.empty()) {
+ timer->stop();
+ return;
+ }
+
+ const QueueStruct &q = dispatchQueue.front();
+ //kdDebug() << this << k_funcinfo << "dispatching " << q.type << " " << dispatchQueue.size() << " left" << endl;
+ switch (q.type) {
+ case Queue_mimeType: mimeType(q.s); break;
+ case Queue_totalSize: totalSize(q.size); break;
+ case Queue_sendMetaData: sendMetaData(); break;
+ case Queue_data: data(q.ba); break;
+ case Queue_finished: finished(); break;
+ }/*end switch*/
+
+ dispatchQueue.pop_front();
+}
+
+void DataSlave::send(int cmd, const TQByteArray &arr) {
+ TQDataStream stream(arr, IO_ReadOnly);
+
+ KURL url;
+
+ switch (cmd) {
+ case CMD_GET: {
+ stream >> url;
+ get(url);
+ break;
+ }
+ case CMD_MIMETYPE: {
+ stream >> url;
+ mimetype(url);
+ break;
+ }
+ // ignore these (must not emit error, otherwise SIGSEGV occurs)
+ case CMD_META_DATA:
+ case CMD_SUBURL:
+ break;
+ default:
+ error(ERR_UNSUPPORTED_ACTION,
+ unsupportedActionErrorString(TQString::fromLatin1("data"),cmd));
+ }/*end switch*/
+}
+
+bool DataSlave::suspended() {
+ return _suspended;
+}
+
+void DataSlave::setHost(const TQString &/*host*/, int /*port*/,
+ const TQString &/*user*/, const TQString &/*passwd*/) {
+ // irrelevant -> will be ignored
+}
+
+void DataSlave::setConfig(const MetaData &/*config*/) {
+ // FIXME: decide to handle this directly or not at all
+#if 0
+ TQByteArray data;
+ TQDataStream stream( data, IO_WriteOnly );
+ stream << config;
+ slaveconn.send( CMD_CONFIG, data );
+#endif
+}
+
+void DataSlave::setAllMetaData(const MetaData &md) {
+ meta_data = md;
+}
+
+void DataSlave::sendMetaData() {
+ emit metaData(meta_data);
+}
+
+void DataSlave::virtual_hook( int id, void* data ) {
+ switch (id) {
+ case VIRTUAL_SUSPEND: suspend(); return;
+ case VIRTUAL_RESUME: resume(); return;
+ case VIRTUAL_SEND: {
+ SendParams *params = reinterpret_cast<SendParams *>(data);
+ send(params->cmd, *params->arr);
+ return;
+ }
+ case VIRTUAL_HOLD: {
+ HoldParams *params = reinterpret_cast<HoldParams *>(data);
+ hold(*params->url);
+ return;
+ }
+ case VIRTUAL_SUSPENDED: {
+ SuspendedParams *params = reinterpret_cast<SuspendedParams *>(data);
+ params->retval = suspended();
+ return;
+ }
+ case VIRTUAL_SET_HOST: {
+ SetHostParams *params = reinterpret_cast<SetHostParams *>(data);
+ setHost(*params->host,params->port,*params->user,*params->passwd);
+ return;
+ }
+ case VIRTUAL_SET_CONFIG: {
+ SetConfigParams *params = reinterpret_cast<SetConfigParams *>(data);
+ setConfig(*params->config);
+ return;
+ }
+ default:
+ TDEIO::Slave::virtual_hook( id, data );
+ }
+}
+
+DISPATCH_IMPL1(mimeType, const TQString &, s)
+DISPATCH_IMPL1(totalSize, TDEIO::filesize_t, size)
+DISPATCH_IMPL(sendMetaData)
+DISPATCH_IMPL1(data, const TQByteArray &, ba)
+
+#undef DISPATCH_IMPL
+#undef DISPATCH_IMPL1
+
+#include "dataslave.moc"
diff --git a/tdeio/tdeio/dataslave.h b/tdeio/tdeio/dataslave.h
new file mode 100644
index 000000000..40cfef126
--- /dev/null
+++ b/tdeio/tdeio/dataslave.h
@@ -0,0 +1,126 @@
+// -*- c++ -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (c) 2003 Leo Savernik <l.savernik@aon.at>
+ * Derived from slave.h
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#ifndef __KIO_DATASLAVE_H__
+#define __KIO_DATASLAVE_H__
+
+#include <tdeio/global.h>
+#include <tdeio/slave.h>
+
+class TQTimer;
+
+// don't forget to sync DISPATCH_IMPL in dataslave.h
+#define DISPATCH_DECL(type) \
+ void dispatch_##type();
+
+// don't forget to sync DISPATCH_IMPL1 in dataslave.h
+#define DISPATCH_DECL1(type, paramtype, param) \
+ void dispatch_##type(paramtype param);
+
+namespace TDEIO {
+
+ /**
+ * This class provides a high performance implementation for the data
+ * url scheme (rfc2397).
+ *
+ * @internal
+ * Do not use this class in external applications. It is an implementation
+ * detail of KIO and subject to change without notice.
+ * @author Leo Savernik
+ */
+ class DataSlave : public TDEIO::Slave {
+ Q_OBJECT
+ public:
+ DataSlave();
+
+ virtual ~DataSlave();
+
+ virtual void setHost(const TQString &host, int port,
+ const TQString &user, const TQString &passwd);
+ virtual void setConfig(const MetaData &config);
+
+ virtual void suspend();
+ virtual void resume();
+ virtual bool suspended();
+ virtual void send(int cmd, const TQByteArray &data = TQByteArray());
+
+ virtual void hold(const KURL &url);
+
+ // pure virtual methods that are defined by the actual protocol
+ virtual void get(const KURL &url) = 0;
+ virtual void mimetype(const KURL &url) = 0;
+
+ protected:
+ /**
+ * Sets metadata
+ * @internal
+ */
+ void setAllMetaData(const MetaData &);
+ /**
+ * Sends metadata set with setAllMetaData
+ * @internal
+ */
+ void sendMetaData();
+
+ // queueing methods
+ /** identifiers of functions to be queued */
+ enum QueueType { Queue_mimeType = 1, Queue_totalSize,
+ Queue_sendMetaData, Queue_data, Queue_finished };
+ /** structure for queueing. It is very primitive, it doesn't
+ * even try to conserve memory.
+ */
+ struct QueueStruct {
+ QueueType type;
+ TQString s;
+ TDEIO::filesize_t size;
+ TQByteArray ba;
+
+ QueueStruct() {}
+ QueueStruct(QueueType type) : type(type) {}
+ };
+ typedef TQValueList<QueueStruct> DispatchQueue;
+ DispatchQueue dispatchQueue;
+
+ DISPATCH_DECL1(mimeType, const TQString &, s)
+ DISPATCH_DECL1(totalSize, TDEIO::filesize_t, size)
+ DISPATCH_DECL(sendMetaData)
+ DISPATCH_DECL1(data, const TQByteArray &, ba)
+ DISPATCH_DECL(finished)
+
+ protected slots:
+ /** dispatches next queued method. Does nothing if there are no
+ * queued methods.
+ */
+ void dispatchNext();
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ private:
+ MetaData meta_data;
+ bool _suspended;
+ TQTimer *timer;
+ };
+
+}
+
+#undef DISPATCH_DECL
+#undef DISPATCH_DECL1
+
+#endif /*__KIO_DATASLAVE_H__*/
diff --git a/tdeio/tdeio/davjob.cpp b/tdeio/tdeio/davjob.cpp
new file mode 100644
index 000000000..986e76342
--- /dev/null
+++ b/tdeio/tdeio/davjob.cpp
@@ -0,0 +1,142 @@
+// -*- c++ -*-
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <kurl.h>
+
+#include <tqobject.h>
+#include <tqptrlist.h>
+#include <tqstring.h>
+#include <tqstringlist.h>
+#include <tqguardedptr.h>
+#include <tqdom.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <kdebug.h>
+#include <tdeio/jobclasses.h>
+#include <tdeio/global.h>
+#include <tdeio/http.h>
+#include <tdeio/davjob.h>
+#include <tdeio/job.h>
+#include <tdeio/slaveinterface.h>
+
+#define KIO_ARGS TQByteArray packedArgs; TQDataStream stream( packedArgs, IO_WriteOnly ); stream
+
+using namespace TDEIO;
+
+class DavJob::DavJobPrivate
+{
+public:
+ TQByteArray savedStaticData;
+ TQByteArray str_response; // replaces the TQString previously used in DavJob itself
+};
+
+DavJob::DavJob( const KURL& url, int method, const TQString& request, bool showProgressInfo )
+ : TransferJob( url, TDEIO::CMD_SPECIAL, TQByteArray(), TQByteArray(), showProgressInfo )
+{
+ d = new DavJobPrivate;
+ // We couldn't set the args when calling the parent constructor,
+ // so do it now.
+ TQDataStream stream( m_packedArgs, IO_WriteOnly );
+ stream << (int) 7 << url << method;
+ // Same for static data
+ if ( ! request.isEmpty() && ! request.isNull() ) {
+ staticData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + request.utf8();
+ staticData.truncate( staticData.size() - 1 );
+ d->savedStaticData = staticData.copy();
+ }
+}
+
+void DavJob::slotData( const TQByteArray& data )
+{
+ if(m_redirectionURL.isEmpty() || !m_redirectionURL.isValid() || m_error) {
+ unsigned int oldSize = d->str_response.size();
+ d->str_response.resize( oldSize + data.size() );
+ memcpy( d->str_response.data() + oldSize, data.data(), data.size() );
+ }
+}
+
+void DavJob::slotFinished()
+{
+ // kdDebug(7113) << "DavJob::slotFinished()" << endl;
+ // kdDebug(7113) << d->str_response << endl;
+ if (!m_redirectionURL.isEmpty() && m_redirectionURL.isValid() && (m_command == CMD_SPECIAL)) {
+ TQDataStream istream( m_packedArgs, IO_ReadOnly );
+ int s_cmd, s_method;
+ KURL s_url;
+ istream >> s_cmd;
+ istream >> s_url;
+ istream >> s_method;
+ // PROPFIND
+ if ( (s_cmd == 7) && (s_method == (int)TDEIO::DAV_PROPFIND) ) {
+ m_packedArgs.truncate(0);
+ TQDataStream stream( m_packedArgs, IO_WriteOnly );
+ stream << (int)7 << m_redirectionURL << (int)TDEIO::DAV_PROPFIND;
+ }
+ } else if ( ! m_response.setContent( d->str_response, true ) ) {
+ // An error occurred parsing the XML response
+ TQDomElement root = m_response.createElementNS( "DAV:", "error-report" );
+ m_response.appendChild( root );
+
+ TQDomElement el = m_response.createElementNS( "DAV:", "offending-response" );
+ TQDomText textnode = m_response.createTextNode( d->str_response );
+ el.appendChild( textnode );
+ root.appendChild( el );
+ delete d; // Should be in virtual destructor
+ d = 0;
+ } else {
+ delete d; // Should be in virtual destructor
+ d = 0;
+ }
+ // kdDebug(7113) << m_response.toString() << endl;
+ TransferJob::slotFinished();
+ if( d ) staticData = d->savedStaticData.copy(); // Need to send DAV request to this host too
+}
+
+/* Convenience methods */
+
+// KDE 4: Make it const TQString &
+DavJob* TDEIO::davPropFind( const KURL& url, const TQDomDocument& properties, TQString depth, bool showProgressInfo )
+{
+ DavJob *job = new DavJob( url, (int) TDEIO::DAV_PROPFIND, properties.toString(), showProgressInfo );
+ job->addMetaData( "davDepth", depth );
+ return job;
+}
+
+
+DavJob* TDEIO::davPropPatch( const KURL& url, const TQDomDocument& properties, bool showProgressInfo )
+{
+ return new DavJob( url, (int) TDEIO::DAV_PROPPATCH, properties.toString(), showProgressInfo );
+}
+
+DavJob* TDEIO::davSearch( const KURL& url, const TQString& nsURI, const TQString& qName, const TQString& query, bool showProgressInfo )
+{
+ TQDomDocument doc;
+ TQDomElement searchrequest = doc.createElementNS( "DAV:", "searchrequest" );
+ TQDomElement searchelement = doc.createElementNS( nsURI, qName );
+ TQDomText text = doc.createTextNode( query );
+ searchelement.appendChild( text );
+ searchrequest.appendChild( searchelement );
+ doc.appendChild( searchrequest );
+ return new DavJob( url, TDEIO::DAV_SEARCH, doc.toString(), showProgressInfo );
+}
+
+#include "davjob.moc"
diff --git a/tdeio/tdeio/davjob.h b/tdeio/tdeio/davjob.h
new file mode 100644
index 000000000..1bbb722d0
--- /dev/null
+++ b/tdeio/tdeio/davjob.h
@@ -0,0 +1,127 @@
+// -*- c++ -*-
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 Jan-Pascal van Best <janpascal@vanbest.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kio_davjob_h__
+#define __kio_davjob_h__
+
+#include <kurl.h>
+
+#include <tqobject.h>
+#include <tqptrlist.h>
+#include <tqstring.h>
+#include <tqstringlist.h>
+#include <tqguardedptr.h>
+#include <tqdom.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <tdeio/jobclasses.h>
+#include <tdeio/global.h>
+
+class Observer;
+class TQTimer;
+
+namespace TDEIO {
+
+ class Slave;
+ class SlaveInterface;
+
+ /**
+ * The transfer job pumps data into and/or out of a Slave.
+ * Data is sent to the slave on request of the slave ( dataReq).
+ * If data coming from the slave can not be handled, the
+ * reading of data from the slave should be suspended.
+ * @see TDEIO::davPropFind()
+ * @see TDEIO::davPropPatch()
+ * @see TDEIO::davSearch()
+ * @since 3.1
+ */
+ class TDEIO_EXPORT DavJob : public TransferJob {
+ Q_OBJECT
+
+ public:
+ /**
+ * Use TDEIO::davPropFind(), TDEIO::davPropPatch() and
+ * TDEIO::davSearch() to create a new DavJob.
+ */
+ DavJob(const KURL& url, int method,
+ const TQString& request, bool showProgressInfo);
+ /**
+ * Returns the response as a TQDomDocument.
+ * @return the response document
+ */
+ TQDomDocument& response() { return m_response; }
+
+ protected slots:
+ virtual void slotFinished();
+ virtual void slotData( const TQByteArray &data);
+
+ protected:
+ bool m_suspended;
+ TransferJob *m_subJob;
+ private:
+ class DavJobPrivate;
+ DavJobPrivate *d;
+ TQString dummy; // kept around for BC reasons
+ TQDomDocument m_response;
+ };
+
+ /**
+ * Creates a new DavJob that issues a PROPFIND command. PROPFIND retrieves
+ * the properties of the resource identified by the given @p url.
+ *
+ * @param url the URL of the resource
+ * @param properties a propfind document that describes the properties that
+ * should be retrieved
+ * @param depth the depth of the request. Can be "0", "1" or "infinity"
+ * @param showProgressInfo true to show progress information
+ * @return the new DavJob
+ */
+ TDEIO_EXPORT DavJob* davPropFind( const KURL& url, const TQDomDocument& properties, TQString depth, bool showProgressInfo=true );
+
+ /**
+ * Creates a new DavJob that issues a PROPPATCH command. PROPPATCH sets
+ * the properties of the resource identified by the given @p url.
+ *
+ * @param url the URL of the resource
+ * @param properties a PROPPACTCH document that describes the properties that
+ * should be modified and its new values
+ * @param showProgressInfo true to show progress information
+ * @return the new DavJob
+ */
+ TDEIO_EXPORT DavJob* davPropPatch( const KURL& url, const TQDomDocument& properties, bool showProgressInfo=true );
+
+ /**
+ * Creates a new DavJob that issues a SEARCH command.
+ *
+ * @param url the URL of the resource
+ * @param nsURI the URI of the search method's qualified name
+ * @param qName the local part of the search method's qualified name
+ * @param query the search string
+ * @param showProgressInfo true to show progress information
+ * @return the new DavJob
+ */
+ TDEIO_EXPORT DavJob* davSearch( const KURL &url, const TQString& nsURI, const TQString& qName, const TQString& query, bool showProgressInfo=true );
+
+}
+
+#endif
+
diff --git a/tdeio/tdeio/defaultprogress.cpp b/tdeio/tdeio/defaultprogress.cpp
new file mode 100644
index 000000000..a4de9c31b
--- /dev/null
+++ b/tdeio/tdeio/defaultprogress.cpp
@@ -0,0 +1,507 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Matej Koss <koss@miesto.sk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <tqtimer.h>
+#include <tqlayout.h>
+#include <tqtooltip.h>
+#include <tqdatetime.h>
+#include <tqcheckbox.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kdialog.h>
+#include <kstringhandler.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kprocess.h>
+#include <kpushbutton.h>
+#include <kstandarddirs.h>
+#include <kstdguiitem.h>
+#include <klineedit.h>
+
+#ifdef Q_WS_X11
+#include <twin.h>
+#endif
+
+#include "jobclasses.h"
+#include "defaultprogress.h"
+
+namespace TDEIO {
+
+class DefaultProgress::DefaultProgressPrivate
+{
+public:
+ bool keepOpenChecked;
+ bool noCaptionYet;
+ KPushButton *cancelClose;
+ KPushButton *openFile;
+ KPushButton *openLocation;
+ TQCheckBox *keepOpen;
+ KURL location;
+ TQTime startTime;
+};
+
+DefaultProgress::DefaultProgress( bool showNow )
+ : ProgressBase( 0 ),
+ m_iTotalSize(0), m_iTotalFiles(0), m_iTotalDirs(0),
+ m_iProcessedSize(0), m_iProcessedDirs(0), m_iProcessedFiles(0)
+{
+ init();
+
+ if ( showNow ) {
+ show();
+ }
+}
+
+DefaultProgress::DefaultProgress( TQWidget* parent, const char* /*name*/ )
+ : ProgressBase( parent ),
+ m_iTotalSize(0), m_iTotalFiles(0), m_iTotalDirs(0),
+ m_iProcessedSize(0), m_iProcessedDirs(0), m_iProcessedFiles(0)
+{
+ init();
+}
+
+bool DefaultProgress::keepOpen() const
+{
+ return d->keepOpenChecked;
+}
+
+void DefaultProgress::init()
+{
+ d = new DefaultProgressPrivate;
+
+#ifdef Q_WS_X11 //FIXME(E): Remove once all the KWin::foo calls have been ported to QWS
+ // Set a useful icon for this window!
+ KWin::setIcons( winId(),
+ TDEGlobal::iconLoader()->loadIcon( "filesave", KIcon::NoGroup, 32 ),
+ TDEGlobal::iconLoader()->loadIcon( "filesave", KIcon::NoGroup, 16 ) );
+#endif
+
+ TQVBoxLayout *topLayout = new TQVBoxLayout( this, KDialog::marginHint(),
+ KDialog::spacingHint() );
+ topLayout->addStrut( 360 ); // makes dlg at least that wide
+
+ TQGridLayout *grid = new TQGridLayout( 2, 3 );
+ topLayout->addLayout(TQT_TQLAYOUT(grid));
+ grid->addColSpacing(1, KDialog::spacingHint());
+ // filenames or action name
+ grid->addWidget(new TQLabel(i18n("Source:"), this), 0, 0);
+
+ sourceEdit = new KLineEdit(this);
+ sourceEdit->setReadOnly(true);
+ sourceEdit->setEnableSqueezedText(true);
+ grid->addWidget(sourceEdit, 0, 2);
+
+ destInvite = new TQLabel(i18n("Destination:"), this);
+ grid->addWidget(destInvite, 1, 0);
+
+ destEdit = new KLineEdit(this);
+ destEdit->setReadOnly (true);
+ destEdit->setEnableSqueezedText(true);
+ grid->addWidget(destEdit, 1, 2);
+
+ m_pProgressBar = new KProgress(this);
+ topLayout->addWidget( m_pProgressBar );
+
+ // processed info
+ TQHBoxLayout *hBox = new TQHBoxLayout();
+ topLayout->addLayout(hBox);
+
+ sizeLabel = new TQLabel(this);
+ hBox->addWidget(sizeLabel);
+
+ resumeLabel = new TQLabel(this);
+ hBox->addWidget(resumeLabel);
+
+ progressLabel = new TQLabel( this );
+/* progressLabel->setSizePolicy( TQSizePolicy( TQSizePolicy::MinimumExpanding,
+ TQSizePolicy::Preferred ) );*/
+ progressLabel->setAlignment( TQLabel::AlignRight );
+ hBox->addWidget( progressLabel );
+
+ hBox = new TQHBoxLayout();
+ topLayout->addLayout(hBox);
+
+ speedLabel = new TQLabel(this);
+ hBox->addWidget(speedLabel, 1);
+
+ TQFrame *line = new TQFrame( this );
+ line->setFrameShape( TQFrame::HLine );
+ line->setFrameShadow( TQFrame::Sunken );
+ topLayout->addWidget( line );
+
+ d->keepOpen = new TQCheckBox( i18n("&Keep this window open after transfer is complete"), this);
+ connect( d->keepOpen, TQT_SIGNAL( toggled(bool) ), TQT_SLOT( slotKeepOpenToggled(bool) ) );
+ topLayout->addWidget(d->keepOpen);
+ d->keepOpen->hide();
+
+ hBox = new TQHBoxLayout();
+ topLayout->addLayout(hBox);
+
+ d->openFile = new KPushButton( i18n("Open &File"), this );
+ connect( d->openFile, TQT_SIGNAL( clicked() ), TQT_SLOT( slotOpenFile() ) );
+ hBox->addWidget( d->openFile );
+ d->openFile->setEnabled(false);
+ d->openFile->hide();
+
+ d->openLocation = new KPushButton( i18n("Open &Destination"), this );
+ connect( d->openLocation, TQT_SIGNAL( clicked() ), TQT_SLOT( slotOpenLocation() ) );
+ hBox->addWidget( d->openLocation );
+ d->openLocation->hide();
+
+ hBox->addStretch(1);
+
+ d->cancelClose = new KPushButton( KStdGuiItem::cancel(), this );
+ connect( d->cancelClose, TQT_SIGNAL( clicked() ), TQT_SLOT( slotStop() ) );
+ hBox->addWidget( d->cancelClose );
+
+ resize( sizeHint() );
+ setMaximumHeight(sizeHint().height());
+
+ d->keepOpenChecked = false;
+ d->noCaptionYet = true;
+ setCaption(i18n("Progress Dialog")); // show something better than tdeio_uiserver
+}
+
+DefaultProgress::~DefaultProgress()
+{
+ delete d;
+}
+
+void DefaultProgress::slotTotalSize( TDEIO::Job*, TDEIO::filesize_t size )
+{
+ // size is measured in bytes
+ if ( m_iTotalSize == size )
+ return;
+ m_iTotalSize = size;
+ if (d->startTime.isNull())
+ d->startTime.start();
+}
+
+
+void DefaultProgress::slotTotalFiles( TDEIO::Job*, unsigned long files )
+{
+ if ( m_iTotalFiles == files )
+ return;
+ m_iTotalFiles = files;
+ showTotals();
+}
+
+
+void DefaultProgress::slotTotalDirs( TDEIO::Job*, unsigned long dirs )
+{
+ if ( m_iTotalDirs == dirs )
+ return;
+ m_iTotalDirs = dirs;
+ showTotals();
+}
+
+void DefaultProgress::showTotals()
+{
+ // Show the totals in the progress label, if we still haven't
+ // processed anything. This is useful when the stat'ing phase
+ // of CopyJob takes a long time (e.g. over networks).
+ if ( m_iProcessedFiles == 0 && m_iProcessedDirs == 0 )
+ {
+ TQString tmps;
+ if ( m_iTotalDirs > 1 )
+ // that we have a singular to translate looks weired but is only logical
+ // xgettext: no-c-format
+ tmps = i18n("%n folder", "%n folders", m_iTotalDirs) + " ";
+ // xgettext: no-c-format
+ tmps += i18n("%n file", "%n files", m_iTotalFiles);
+ progressLabel->setText( tmps );
+ }
+}
+
+//static
+TQString DefaultProgress::makePercentString( unsigned long percent,
+ TDEIO::filesize_t totalSize,
+ unsigned long totalFiles )
+{
+ if ( totalSize )
+ return i18n( "%1 % of %2 " ).arg( TQString::number(percent) , TDEIO::convertSize( totalSize ) );
+ else if ( totalFiles )
+ return i18n( "%1 % of 1 file", "%1 % of %n files", totalFiles ).arg( percent );
+ else
+ return i18n( "%1 %" ).arg( percent );
+}
+
+void DefaultProgress::slotPercent( TDEIO::Job*, unsigned long percent )
+{
+ TQString caption = makePercentString( percent, m_iTotalSize, m_iTotalFiles );
+ m_pProgressBar->setValue( percent );
+ switch(mode) {
+ case Copy:
+ caption.append(i18n(" (Copying)"));
+ break;
+ case Move:
+ caption.append(i18n(" (Moving)"));
+ break;
+ case Delete:
+ caption.append(i18n(" (Deleting)"));
+ break;
+ case Create:
+ caption.append(i18n(" (Creating)"));
+ break;
+ case Done:
+ caption.append(i18n(" (Done)"));
+ break;
+ }
+
+ setCaption( caption );
+ d->noCaptionYet = false;
+}
+
+
+void DefaultProgress::slotInfoMessage( TDEIO::Job*, const TQString & msg )
+{
+ speedLabel->setText( msg );
+ speedLabel->setAlignment( speedLabel->alignment() & ~TQt::WordBreak );
+}
+
+
+void DefaultProgress::slotProcessedSize( TDEIO::Job*, TDEIO::filesize_t bytes ) {
+ if ( m_iProcessedSize == bytes )
+ return;
+ m_iProcessedSize = bytes;
+
+ TQString tmp = i18n( "%1 of %2 complete")
+ .arg( TDEIO::convertSize(bytes) )
+ .arg( TDEIO::convertSize(m_iTotalSize));
+ sizeLabel->setText( tmp );
+}
+
+
+void DefaultProgress::slotProcessedDirs( TDEIO::Job*, unsigned long dirs )
+{
+ if ( m_iProcessedDirs == dirs )
+ return;
+ m_iProcessedDirs = dirs;
+
+ TQString tmps;
+ tmps = i18n("%1 / %n folder", "%1 / %n folders", m_iTotalDirs).arg( m_iProcessedDirs );
+ tmps += " ";
+ tmps += i18n("%1 / %n file", "%1 / %n files", m_iTotalFiles).arg( m_iProcessedFiles );
+ progressLabel->setText( tmps );
+}
+
+
+void DefaultProgress::slotProcessedFiles( TDEIO::Job*, unsigned long files )
+{
+ if ( m_iProcessedFiles == files )
+ return;
+ m_iProcessedFiles = files;
+
+ TQString tmps;
+ if ( m_iTotalDirs > 1 ) {
+ tmps = i18n("%1 / %n folder", "%1 / %n folders", m_iTotalDirs).arg( m_iProcessedDirs );
+ tmps += " ";
+ }
+ tmps += i18n("%1 / %n file", "%1 / %n files", m_iTotalFiles).arg( m_iProcessedFiles );
+ progressLabel->setText( tmps );
+}
+
+
+void DefaultProgress::slotSpeed( TDEIO::Job*, unsigned long speed )
+{
+ if ( speed == 0 ) {
+ speedLabel->setText( i18n( "Stalled") );
+ } else {
+ speedLabel->setText( i18n( "%1/s ( %2 remaining )").arg( TDEIO::convertSize( speed ))
+ .arg( TDEIO::convertSeconds( TDEIO::calculateRemainingSeconds( m_iTotalSize, m_iProcessedSize, speed ))) );
+ }
+}
+
+
+void DefaultProgress::slotCopying( TDEIO::Job*, const KURL& from, const KURL& to )
+{
+ if ( d->noCaptionYet ) {
+ setCaption(i18n("Copy File(s) Progress"));
+ d->noCaptionYet = false;
+ }
+ mode = Copy;
+ sourceEdit->setText(from.prettyURL());
+ setDestVisible( true );
+ checkDestination( to );
+ destEdit->setText(to.prettyURL());
+}
+
+
+void DefaultProgress::slotMoving( TDEIO::Job*, const KURL& from, const KURL& to )
+{
+ if ( d->noCaptionYet ) {
+ setCaption(i18n("Move File(s) Progress"));
+ d->noCaptionYet = false;
+ }
+ mode = Move;
+ sourceEdit->setText(from.prettyURL());
+ setDestVisible( true );
+ checkDestination( to );
+ destEdit->setText(to.prettyURL());
+}
+
+
+void DefaultProgress::slotCreatingDir( TDEIO::Job*, const KURL& dir )
+{
+ if ( d->noCaptionYet ) {
+ setCaption(i18n("Creating Folder"));
+ d->noCaptionYet = false;
+ }
+ mode = Create;
+ sourceEdit->setText(dir.prettyURL());
+ setDestVisible( false );
+}
+
+
+void DefaultProgress::slotDeleting( TDEIO::Job*, const KURL& url )
+{
+ if ( d->noCaptionYet ) {
+ setCaption(i18n("Delete File(s) Progress"));
+ d->noCaptionYet = false;
+ }
+ mode = Delete;
+ sourceEdit->setText(url.prettyURL());
+ setDestVisible( false );
+}
+
+void DefaultProgress::slotTransferring( TDEIO::Job*, const KURL& url )
+{
+ if ( d->noCaptionYet ) {
+ setCaption(i18n("Loading Progress"));
+ d->noCaptionYet = false;
+ }
+ sourceEdit->setText(url.prettyURL());
+ setDestVisible( false );
+}
+
+void DefaultProgress::slotStating( TDEIO::Job*, const KURL& url )
+{
+ setCaption(i18n("Examining File Progress"));
+ sourceEdit->setText(url.prettyURL());
+ setDestVisible( false );
+}
+
+void DefaultProgress::slotMounting( TDEIO::Job*, const TQString & dev, const TQString & point )
+{
+ setCaption(i18n("Mounting %1").arg(dev));
+ sourceEdit->setText(point);
+ setDestVisible( false );
+}
+
+void DefaultProgress::slotUnmounting( TDEIO::Job*, const TQString & point )
+{
+ setCaption(i18n("Unmounting"));
+ sourceEdit->setText(point);
+ setDestVisible( false );
+}
+
+void DefaultProgress::slotCanResume( TDEIO::Job*, TDEIO::filesize_t resume )
+{
+ if ( resume ) {
+ resumeLabel->setText( i18n("Resuming from %1").arg(TDEIO::number(resume)) );
+ } else {
+ resumeLabel->setText( i18n("Not resumable") );
+ }
+}
+
+void DefaultProgress::setDestVisible( bool visible )
+{
+ // We can't hide the destInvite/destEdit labels,
+ // because it screws up the TQGridLayout.
+ if (visible)
+ {
+ destInvite->show();
+ destEdit->show();
+
+ destInvite->setText( i18n("Destination:") );
+ }
+ else
+ {
+ destInvite->hide();
+ destEdit->hide();
+ destInvite->setText( TQString::null );
+ destEdit->setText( TQString::null );
+ }
+}
+
+void DefaultProgress::slotClean() {
+ if (d->keepOpenChecked) {
+ mode = Done;
+ slotPercent(0, 100);
+ d->cancelClose->setGuiItem( KStdGuiItem::close() );
+ d->openFile->setEnabled(true);
+ slotProcessedSize(0, m_iTotalSize);
+ d->keepOpen->setEnabled(false);
+ if (!d->startTime.isNull()) {
+ int s = d->startTime.elapsed();
+ if (!s)
+ s = 1;
+ speedLabel->setText(i18n("%1/s (done)").arg(TDEIO::convertSize(1000 * m_iTotalSize / s)));
+ }
+ setOnlyClean(false);
+ }
+ else
+ hide();
+}
+
+void DefaultProgress::slotKeepOpenToggled(bool keepopen)
+{
+ d->keepOpenChecked=keepopen;
+}
+
+void DefaultProgress::checkDestination(const KURL& dest) {
+ bool ok = true;
+ if ( dest.isLocalFile() ) {
+ TQString path = dest.path( -1 );
+ TQStringList tmpDirs = TDEGlobal::dirs()->resourceDirs( "tmp" );
+ for ( TQStringList::Iterator it = tmpDirs.begin() ; ok && it != tmpDirs.end() ; ++it )
+ if ( path.contains( *it ) )
+ ok = false; // it's in the tmp resource
+ }
+
+ if ( ok ) {
+ d->openFile->show();
+ d->openLocation->show();
+ d->keepOpen->show();
+ d->location=dest;
+ }
+}
+
+void DefaultProgress::slotOpenFile()
+{
+ TDEProcess proc;
+ proc << "konqueror" << d->location.prettyURL();
+ proc.start(TDEProcess::DontCare);
+}
+
+void DefaultProgress::slotOpenLocation()
+{
+ TDEProcess proc;
+ d->location.setFileName("");
+ proc << "konqueror" << d->location.prettyURL();
+ proc.start(TDEProcess::DontCare);
+}
+
+void DefaultProgress::virtual_hook( int id, void* data )
+{ ProgressBase::virtual_hook( id, data ); }
+
+} /* namespace */
+
+#include "defaultprogress.moc"
diff --git a/tdeio/tdeio/defaultprogress.h b/tdeio/tdeio/defaultprogress.h
new file mode 100644
index 000000000..de0dfd093
--- /dev/null
+++ b/tdeio/tdeio/defaultprogress.h
@@ -0,0 +1,164 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Matej Koss <koss@miesto.sk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __defaultprogress_h__
+#define __defaultprogress_h__
+
+#include <tqlabel.h>
+
+#include <tdeio/global.h>
+
+#include <kprogress.h>
+
+#include "progressbase.h"
+
+class KLineEdit;
+
+namespace TDEIO {
+
+/*
+ * A default implementation of the progress dialog ProgressBase.
+ * ProgressBase
+ */
+class TDEIO_EXPORT DefaultProgress : public ProgressBase {
+
+ Q_OBJECT
+
+public:
+ /**
+ * Creates a new default progress dialog.
+ * @param showNow true to show immediately, false to show when
+ * needed
+ */
+ DefaultProgress( bool showNow = true );
+ /**
+ * Creates a new default progress dialog.
+ * @param parent the parent of the dialog (or 0 for top-level)
+ * @param name the name of the dialog, can be 0
+ * @since 3.1
+ */
+ DefaultProgress( TQWidget* parent, const char* name = 0 );
+ ~DefaultProgress();
+
+ bool keepOpen() const;
+
+ /// Shared with uiserver.cpp
+ static TQString makePercentString( unsigned long percent,
+ TDEIO::filesize_t totalSize,
+ unsigned long totalFiles );
+
+public slots:
+ virtual void slotTotalSize( TDEIO::Job *job, TDEIO::filesize_t size );
+ virtual void slotTotalFiles( TDEIO::Job *job, unsigned long files );
+ virtual void slotTotalDirs( TDEIO::Job *job, unsigned long dirs );
+
+ virtual void slotProcessedSize( TDEIO::Job *job, TDEIO::filesize_t bytes );
+ virtual void slotProcessedFiles( TDEIO::Job *job, unsigned long files );
+ virtual void slotProcessedDirs( TDEIO::Job *job, unsigned long dirs );
+
+ virtual void slotSpeed( TDEIO::Job *job, unsigned long speed );
+ virtual void slotPercent( TDEIO::Job *job, unsigned long percent );
+ /**
+ * Called to set an information message.
+ * @param job the TDEIO::Job
+ * @param msg the message to set
+ */
+ virtual void slotInfoMessage( TDEIO::Job *job, const TQString & msg );
+
+ virtual void slotCopying( TDEIO::Job* job, const KURL& src, const KURL& dest );
+ virtual void slotMoving( TDEIO::Job* job, const KURL& src, const KURL& dest );
+ virtual void slotDeleting( TDEIO::Job* job, const KURL& url );
+ /**
+ * Called when the job is transferring.
+ * @param job the TDEIO::Job
+ * @param url the url to transfer
+ * @since 3.1
+ */
+ void slotTransferring( TDEIO::Job* job, const KURL& url );
+ virtual void slotCreatingDir( TDEIO::Job* job, const KURL& dir );
+ /**
+ * Called when the job is requesting a stat.
+ * @param job the TDEIO::Job
+ * @param dir the dir to stat
+ * @since 3.1
+ */
+ virtual void slotStating( TDEIO::Job* job, const KURL& dir );
+ /**
+ * Called when the job is mounting.
+ * @param job the TDEIO::Job
+ * @param dev the device to mount
+ * @param point the mount point
+ */
+ virtual void slotMounting( TDEIO::Job* job, const TQString & dev, const TQString & point );
+ /**
+ * Called when the job is unmounting.
+ * @param job the TDEIO::Job
+ * @param point the mount point
+ */
+ virtual void slotUnmounting( TDEIO::Job* job, const TQString & point );
+ virtual void slotCanResume( TDEIO::Job* job, TDEIO::filesize_t from);
+
+ /**
+ * Called when the job is cleaned.
+ * @since 3.1
+ */
+ void slotClean();
+
+protected:
+ /// @since 3.1
+ void init();
+ void showTotals();
+ void setDestVisible( bool visible );
+ /// @since 3.1
+ void checkDestination( const KURL& dest);
+
+ KLineEdit* sourceEdit;
+ KLineEdit* destEdit;
+ TQLabel* progressLabel;
+ TQLabel* destInvite;
+ TQLabel* speedLabel;
+ TQLabel* sizeLabel;
+ TQLabel* resumeLabel;
+
+ KProgress* m_pProgressBar;
+
+ TDEIO::filesize_t m_iTotalSize;
+ unsigned long m_iTotalFiles;
+ unsigned long m_iTotalDirs;
+
+ TDEIO::filesize_t m_iProcessedSize;
+ unsigned long m_iProcessedDirs;
+ unsigned long m_iProcessedFiles;
+
+ enum ModeType { Copy, Move, Delete, Create, Done };
+ ModeType mode;
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class DefaultProgressPrivate;
+ DefaultProgressPrivate* d;
+private slots:
+ void slotKeepOpenToggled(bool);
+ void slotOpenFile();
+ void slotOpenLocation();
+};
+
+} /* namespace */
+
+#endif // __defaultprogress_h__
+
diff --git a/tdeio/tdeio/forwardingslavebase.cpp b/tdeio/tdeio/forwardingslavebase.cpp
new file mode 100644
index 000000000..a55f68249
--- /dev/null
+++ b/tdeio/tdeio/forwardingslavebase.cpp
@@ -0,0 +1,475 @@
+/* This file is part of the KDE project
+ Copyright (c) 2004 Kevin Ottens <ervin ipsquad net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <kdebug.h>
+#include <tdeio/job.h>
+#include <kmimetype.h>
+#include <kprotocolinfo.h>
+
+#include <tqapplication.h>
+#include <tqeventloop.h>
+
+#include "forwardingslavebase.h"
+
+namespace TDEIO
+{
+
+class ForwardingSlaveBasePrivate
+{
+};
+
+ForwardingSlaveBase::ForwardingSlaveBase(const TQCString &protocol,
+ const TQCString &poolSocket,
+ const TQCString &appSocket)
+ : TQObject(), SlaveBase(protocol, poolSocket, appSocket)
+{
+}
+
+ForwardingSlaveBase::~ForwardingSlaveBase()
+{
+}
+
+bool ForwardingSlaveBase::internalRewriteURL(const KURL &url, KURL &newURL)
+{
+ bool result = true;
+
+ if ( url.protocol().ascii()==mProtocol )
+ {
+ result = rewriteURL(url, newURL);
+ }
+ else
+ {
+ newURL = url;
+ }
+
+ m_processedURL = newURL;
+ m_requestedURL = url;
+ return result;
+}
+
+void ForwardingSlaveBase::prepareUDSEntry(TDEIO::UDSEntry &entry,
+ bool listing) const
+{
+ kdDebug() << "ForwardingSlaveBase::prepareUDSEntry: listing=="
+ << listing << endl;
+
+ bool url_found = false;
+ TQString name;
+ KURL url;
+
+ TDEIO::UDSEntry::iterator it = entry.begin();
+ TDEIO::UDSEntry::iterator end = entry.end();
+
+ for(; it!=end; ++it)
+ {
+ KURL new_url = m_requestedURL;
+
+ switch( (*it).m_uds )
+ {
+ case TDEIO::UDS_NAME:
+ name = (*it).m_str;
+ kdDebug() << "Name = " << name << endl;
+ break;
+ case TDEIO::UDS_URL:
+ url_found = true;
+ url = (*it).m_str;
+ if (listing)
+ {
+ new_url.addPath(url.fileName());
+ }
+ (*it).m_str = new_url.url();
+ kdDebug() << "URL = " << url << endl;
+ kdDebug() << "New URL = " << (*it).m_str << endl;
+ break;
+ }
+ }
+
+ if ( m_processedURL.isLocalFile() )
+ {
+ KURL new_url = m_processedURL;
+ if (listing)
+ {
+ new_url.addPath( name );
+ }
+
+ TDEIO::UDSAtom atom;
+ atom.m_uds = TDEIO::UDS_LOCAL_PATH;
+ atom.m_long = 0;
+ atom.m_str = new_url.path();
+ entry.append(atom);
+ }
+}
+
+void ForwardingSlaveBase::get(const KURL &url)
+{
+ kdDebug() << "ForwardingSlaveBase::get: " << url << endl;
+
+ KURL new_url;
+ if ( internalRewriteURL(url, new_url) )
+ {
+ TDEIO::TransferJob *job = TDEIO::get(new_url, false, false);
+ connectTransferJob(job);
+
+ tqApp->eventLoop()->enterLoop();
+ }
+}
+
+void ForwardingSlaveBase::put(const KURL &url, int permissions,
+ bool overwrite, bool resume )
+{
+ kdDebug() << "ForwardingSlaveBase::put: " << url << endl;
+
+ KURL new_url;
+ if ( internalRewriteURL(url, new_url) )
+ {
+ TDEIO::TransferJob *job = TDEIO::put(new_url, permissions, overwrite,
+ resume, false);
+ connectTransferJob(job);
+
+ tqApp->eventLoop()->enterLoop();
+ }
+}
+
+void ForwardingSlaveBase::stat(const KURL &url)
+{
+ kdDebug() << "ForwardingSlaveBase::stat: " << url << endl;
+
+ KURL new_url;
+ if ( internalRewriteURL(url, new_url) )
+ {
+ TDEIO::SimpleJob *job = TDEIO::stat(new_url, false);
+ connectSimpleJob(job);
+
+ tqApp->eventLoop()->enterLoop();
+ }
+}
+
+void ForwardingSlaveBase::mimetype(const KURL &url)
+{
+ kdDebug() << "ForwardingSlaveBase::mimetype: " << url << endl;
+
+ KURL new_url;
+ if ( internalRewriteURL(url, new_url) )
+ {
+ TDEIO::TransferJob *job = TDEIO::mimetype(new_url, false);
+ connectTransferJob(job);
+
+ tqApp->eventLoop()->enterLoop();
+ }
+}
+
+void ForwardingSlaveBase::listDir(const KURL &url)
+{
+ kdDebug() << "ForwardingSlaveBase::listDir: " << url << endl;
+
+ KURL new_url;
+ if ( internalRewriteURL(url, new_url) )
+ {
+ TDEIO::ListJob *job = TDEIO::listDir(new_url, false);
+ connectListJob(job);
+
+ tqApp->eventLoop()->enterLoop();
+ }
+}
+
+void ForwardingSlaveBase::mkdir(const KURL &url, int permissions)
+{
+ kdDebug() << "ForwardingSlaveBase::mkdir: " << url << endl;
+
+ KURL new_url;
+ if ( internalRewriteURL(url, new_url) )
+ {
+ TDEIO::SimpleJob *job = TDEIO::mkdir(new_url, permissions);
+ connectSimpleJob(job);
+
+ tqApp->eventLoop()->enterLoop();
+ }
+}
+
+void ForwardingSlaveBase::rename(const KURL &src, const KURL &dest,
+ bool overwrite)
+{
+ kdDebug() << "ForwardingSlaveBase::rename: " << src << ", " << dest << endl;
+
+ KURL new_src, new_dest;
+ if ( internalRewriteURL(src, new_src) && internalRewriteURL(dest, new_dest) )
+ {
+ TDEIO::Job *job = TDEIO::rename(new_src, new_dest, overwrite);
+ connectJob(job);
+
+ tqApp->eventLoop()->enterLoop();
+ }
+}
+
+void ForwardingSlaveBase::symlink(const TQString &target, const KURL &dest,
+ bool overwrite)
+{
+ kdDebug() << "ForwardingSlaveBase::symlink: " << target << ", " << dest << endl;
+
+ KURL new_dest;
+ if ( internalRewriteURL(dest, new_dest) )
+ {
+ TDEIO::SimpleJob *job = TDEIO::symlink(target, new_dest, overwrite, false);
+ connectSimpleJob(job);
+
+ tqApp->eventLoop()->enterLoop();
+ }
+}
+
+void ForwardingSlaveBase::chmod(const KURL &url, int permissions)
+{
+ kdDebug() << "ForwardingSlaveBase::chmod: " << url << endl;
+
+ KURL new_url;
+ if ( internalRewriteURL(url, new_url) )
+ {
+ TDEIO::SimpleJob *job = TDEIO::chmod(new_url, permissions);
+ connectSimpleJob(job);
+
+ tqApp->eventLoop()->enterLoop();
+ }
+}
+
+void ForwardingSlaveBase::copy(const KURL &src, const KURL &dest,
+ int permissions, bool overwrite)
+{
+ kdDebug() << "ForwardingSlaveBase::copy: " << src << ", " << dest << endl;
+
+ KURL new_src, new_dest;
+ if ( internalRewriteURL(src, new_src) && internalRewriteURL(dest, new_dest) )
+ {
+ TDEIO::Job *job = TDEIO::file_copy(new_src, new_dest, permissions,
+ overwrite, false);
+ connectJob(job);
+
+ tqApp->eventLoop()->enterLoop();
+ }
+}
+
+void ForwardingSlaveBase::del(const KURL &url, bool isfile)
+{
+ kdDebug() << "ForwardingSlaveBase::del: " << url << endl;
+
+ KURL new_url;
+ if ( internalRewriteURL(url, new_url) )
+ {
+ if (isfile)
+ {
+ TDEIO::DeleteJob *job = TDEIO::del(new_url, false, false);
+ connectJob(job);
+ }
+ else
+ {
+ TDEIO::SimpleJob *job = TDEIO::rmdir(new_url);
+ connectSimpleJob(job);
+ }
+
+ tqApp->eventLoop()->enterLoop();
+ }
+}
+
+void ForwardingSlaveBase::localURL(const KURL& remoteURL)
+{
+ kdDebug() << "ForwardingSlaveBase::localURL: " << remoteURL << endl;
+
+ KURL new_url;
+ if ( internalRewriteURL(remoteURL, new_url) )
+ {
+ TDEIO::LocalURLJob *job = TDEIO::localURL(new_url);
+ connectLocalURLJob(job);
+
+ tqApp->eventLoop()->enterLoop();
+ }
+ else
+ {
+ // Let the slave base emit the required signals
+ SlaveBase::localURL(remoteURL);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void ForwardingSlaveBase::connectJob(TDEIO::Job *job)
+{
+ // We will forward the warning message, no need to let the job
+ // display it itself
+ job->setInteractive(false);
+
+ // Forward metadata (e.g. modification time for put())
+ job->setMetaData( allMetaData() );
+#if 0 // debug code
+ kdDebug() << k_funcinfo << "transferring metadata:" << endl;
+ const MetaData md = allMetaData();
+ for ( MetaData::const_iterator it = md.begin(); it != md.end(); ++it )
+ kdDebug() << it.key() << " = " << it.data() << endl;
+#endif
+
+ connect( job, TQT_SIGNAL( result(TDEIO::Job *) ),
+ this, TQT_SLOT( slotResult(TDEIO::Job *) ) );
+ connect( job, TQT_SIGNAL( warning(TDEIO::Job *, const TQString &) ),
+ this, TQT_SLOT( slotWarning(TDEIO::Job *, const TQString &) ) );
+ connect( job, TQT_SIGNAL( infoMessage(TDEIO::Job *, const TQString &) ),
+ this, TQT_SLOT( slotInfoMessage(TDEIO::Job *, const TQString &) ) );
+ connect( job, TQT_SIGNAL( totalSize(TDEIO::Job *, TDEIO::filesize_t) ),
+ this, TQT_SLOT( slotTotalSize(TDEIO::Job *, TDEIO::filesize_t) ) );
+ connect( job, TQT_SIGNAL( processedSize(TDEIO::Job *, TDEIO::filesize_t) ),
+ this, TQT_SLOT( slotProcessedSize(TDEIO::Job *, TDEIO::filesize_t) ) );
+ connect( job, TQT_SIGNAL( speed(TDEIO::Job *, unsigned long) ),
+ this, TQT_SLOT( slotSpeed(TDEIO::Job *, unsigned long) ) );
+}
+
+void ForwardingSlaveBase::connectSimpleJob(TDEIO::SimpleJob *job)
+{
+ connectJob(job);
+ connect( job, TQT_SIGNAL( redirection(TDEIO::Job *, const KURL &) ),
+ this, TQT_SLOT( slotRedirection(TDEIO::Job *, const KURL &) ) );
+}
+
+void ForwardingSlaveBase::connectListJob(TDEIO::ListJob *job)
+{
+ connectSimpleJob(job);
+ connect( job, TQT_SIGNAL( entries(TDEIO::Job *, const TDEIO::UDSEntryList &) ),
+ this, TQT_SLOT( slotEntries(TDEIO::Job *, const TDEIO::UDSEntryList &) ) );
+}
+
+void ForwardingSlaveBase::connectTransferJob(TDEIO::TransferJob *job)
+{
+ connectSimpleJob(job);
+ connect( job, TQT_SIGNAL( data(TDEIO::Job *, const TQByteArray &) ),
+ this, TQT_SLOT( slotData(TDEIO::Job *, const TQByteArray &) ) );
+ connect( job, TQT_SIGNAL( dataReq(TDEIO::Job *, TQByteArray &) ),
+ this, TQT_SLOT( slotDataReq(TDEIO::Job *, TQByteArray &) ) );
+ connect( job, TQT_SIGNAL( mimetype(TDEIO::Job *, const TQString &) ),
+ this, TQT_SLOT( slotMimetype(TDEIO::Job *, const TQString &) ) );
+ connect( job, TQT_SIGNAL( canResume(TDEIO::Job *, TDEIO::filesize_t) ),
+ this, TQT_SLOT( slotCanResume(TDEIO::Job *, TDEIO::filesize_t) ) );
+}
+
+void ForwardingSlaveBase::connectLocalURLJob(TDEIO::LocalURLJob *job)
+{
+ connectJob(job);
+ connect( job, TQT_SIGNAL( localURL(TDEIO::Job *, const KURL&, bool) ),
+ this, TQT_SLOT( slotLocalURL(TDEIO::Job *, const KURL&, bool) ) );
+}
+
+//////////////////////////////////////////////////////////////////////////////
+
+void ForwardingSlaveBase::slotResult(TDEIO::Job *job)
+{
+ if ( job->error() != 0)
+ {
+ error( job->error(), job->errorText() );
+ }
+ else
+ {
+ TDEIO::StatJob *stat_job = dynamic_cast<TDEIO::StatJob *>(job);
+ if ( stat_job!=0L )
+ {
+ TDEIO::UDSEntry entry = stat_job->statResult();
+ prepareUDSEntry(entry);
+ statEntry( entry );
+ }
+ finished();
+ }
+
+ tqApp->eventLoop()->exitLoop();
+}
+
+void ForwardingSlaveBase::slotWarning(TDEIO::Job* /*job*/, const TQString &msg)
+{
+ warning(msg);
+}
+
+void ForwardingSlaveBase::slotInfoMessage(TDEIO::Job* /*job*/, const TQString &msg)
+{
+ infoMessage(msg);
+}
+
+void ForwardingSlaveBase::slotTotalSize(TDEIO::Job* /*job*/, TDEIO::filesize_t size)
+{
+ totalSize(size);
+}
+
+void ForwardingSlaveBase::slotProcessedSize(TDEIO::Job* /*job*/, TDEIO::filesize_t size)
+{
+ processedSize(size);
+}
+
+void ForwardingSlaveBase::slotSpeed(TDEIO::Job* /*job*/, unsigned long bytesPerSecond)
+{
+ speed(bytesPerSecond);
+}
+
+void ForwardingSlaveBase::slotRedirection(TDEIO::Job *job, const KURL &url)
+{
+ redirection(url);
+
+ // We've been redirected stop everything.
+ job->kill( true );
+ finished();
+
+ tqApp->eventLoop()->exitLoop();
+}
+
+void ForwardingSlaveBase::slotEntries(TDEIO::Job* /*job*/,
+ const TDEIO::UDSEntryList &entries)
+{
+ TDEIO::UDSEntryList final_entries = entries;
+
+ TDEIO::UDSEntryList::iterator it = final_entries.begin();
+ TDEIO::UDSEntryList::iterator end = final_entries.end();
+
+ for(; it!=end; ++it)
+ {
+ prepareUDSEntry(*it, true);
+ }
+
+ listEntries( final_entries );
+}
+
+void ForwardingSlaveBase::slotData(TDEIO::Job* /*job*/, const TQByteArray &d)
+{
+ data(d);
+}
+
+void ForwardingSlaveBase::slotDataReq(TDEIO::Job* /*job*/, TQByteArray &data)
+{
+ dataReq();
+ readData(data);
+}
+
+void ForwardingSlaveBase::slotMimetype (TDEIO::Job* /*job*/, const TQString &type)
+{
+ mimeType(type);
+}
+
+void ForwardingSlaveBase::slotCanResume (TDEIO::Job* /*job*/, TDEIO::filesize_t offset)
+{
+ canResume(offset);
+}
+
+void ForwardingSlaveBase::slotLocalURL(TDEIO::Job *, const KURL& url, bool)
+{
+ SlaveBase::localURL(url);
+}
+
+}
+
+#include "forwardingslavebase.moc"
+
diff --git a/tdeio/tdeio/forwardingslavebase.h b/tdeio/tdeio/forwardingslavebase.h
new file mode 100644
index 000000000..4d84089bf
--- /dev/null
+++ b/tdeio/tdeio/forwardingslavebase.h
@@ -0,0 +1,204 @@
+/* This file is part of the KDE project
+ Copyright (c) 2004 Kevin Ottens <ervin ipsquad net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _FORWARDING_SLAVE_BASE_H_
+#define _FORWARDING_SLAVE_BASE_H_
+
+#include <tdeio/slavebase.h>
+#include <tdeio/jobclasses.h>
+
+#include <tqobject.h>
+
+namespace TDEIO
+{
+
+class ForwardingSlaveBasePrivate;
+
+/**
+ * This class should be used as a base for ioslaves acting as a
+ * forwarder to other ioslaves. It has been designed to support only
+ * local filesystem like ioslaves.
+ *
+ * If the resulting ioslave should be a simple proxy, you only need
+ * to implement the ForwardingSlaveBase::rewriteURL() method.
+ *
+ * For more advanced behavior, the classic ioslave methods should
+ * be reimplemented, because their default behavior in this class
+ * is to forward using the ForwardingSlaveBase::rewriteURL() method.
+ *
+ * A possible code snippet for an advanced stat() behavior would look
+ * like this in the child class:
+ *
+ * \code
+ * void ChildProtocol::stat(const KURL &url)
+ * {
+ * bool is_special = false;
+ *
+ * // Process the URL to see if it should have
+ * // a special treatment
+ *
+ * if ( is_special )
+ * {
+ * // Handle the URL ourselves
+ * TDEIO::UDSEntry entry;
+ * // Fill entry with UDSAtom instances
+ * statEntry(entry);
+ * finished();
+ * }
+ * else
+ * {
+ * // Setup the ioslave internal state if
+ * // required by ChildProtocol::rewriteURL()
+ * ForwardingSlaveBase::stat(url);
+ * }
+ * }
+ * \endcode
+ *
+ * Of course in this case, you surely need to reimplement listDir()
+ * and get() accordingly.
+ *
+ * If you want view on directories to be correctly refreshed when
+ * something changes on a forwarded URL, you'll need a companion kded
+ * module to emit the KDirNotify Files*() DCOP signals.
+ *
+ * This class was initially used for media:/ ioslave. This ioslave code
+ * and the MediaDirNotify class of its companion kded module can be a
+ * good source of inspiration.
+ *
+ * @see ForwardingSlaveBase::rewriteURL()
+ * @since 3.4
+ * @author Kevin Ottens <ervin@ipsquad.net>
+ */
+class TDEIO_EXPORT ForwardingSlaveBase : public TQObject, public SlaveBase
+{
+Q_OBJECT
+public:
+ ForwardingSlaveBase(const TQCString &protocol,
+ const TQCString &poolSocket,
+ const TQCString &appSocket);
+ virtual ~ForwardingSlaveBase();
+
+ virtual void get(const KURL &url);
+
+ virtual void put(const KURL &url, int permissions,
+ bool overwrite, bool resume);
+
+ virtual void stat(const KURL &url);
+
+ virtual void mimetype(const KURL &url);
+
+ virtual void listDir(const KURL &url);
+
+ virtual void mkdir(const KURL &url, int permissions);
+
+ virtual void rename(const KURL &src, const KURL &dest, bool overwrite);
+
+ virtual void symlink(const TQString &target, const KURL &dest,
+ bool overwrite);
+
+ virtual void chmod(const KURL &url, int permissions);
+
+ virtual void copy(const KURL &src, const KURL &dest,
+ int permissions, bool overwrite);
+
+ virtual void del(const KURL &url, bool isfile);
+
+ virtual void localURL(const KURL& remoteURL);
+
+protected:
+ /**
+ * Rewrite an url to it's forwarded counterpart. It should return
+ * true if everything was ok, and false otherwise.
+ *
+ * If a problem is detected it's up to this method to trigger error()
+ * before returning. Returning false silently cancel the current
+ * slave operation.
+ *
+ * @param url The URL as given during the slave call
+ * @param newURL The new URL to forward the slave call to
+ * @return true if the given url could be correctly rewritten
+ */
+ virtual bool rewriteURL(const KURL &url, KURL &newURL)=0;
+
+ /**
+ * Allow to modify a UDSEntry before it's sent to the ioslave enpoint.
+ * This is the default implementation working in most case, but sometimes
+ * you could make use of more forwarding black magic (for example
+ * dynamically transform any desktop file into a fake directory...)
+ *
+ * @param entry the UDSEntry to post-process
+ * @param listing indicate if this entry it created during a listDir
+ * operation
+ */
+ virtual void prepareUDSEntry(TDEIO::UDSEntry &entry,
+ bool listing=false) const;
+
+ /**
+ * Return the URL being processed by the ioslave
+ * Only access it inside prepareUDSEntry()
+ */
+ KURL processedURL() const { return m_processedURL; }
+
+ /**
+ * Return the URL asked to the ioslave
+ * Only access it inside prepareUDSEntry()
+ */
+ KURL requestedURL() const { return m_requestedURL; }
+
+private:
+ KURL m_processedURL;
+ KURL m_requestedURL;
+ ForwardingSlaveBasePrivate *d;
+
+ bool internalRewriteURL(const KURL &url, KURL &newURL);
+
+ void connectJob(Job *job);
+ void connectSimpleJob(SimpleJob *job);
+ void connectListJob(ListJob *job);
+ void connectTransferJob(TransferJob *job);
+ void connectLocalURLJob(LocalURLJob *job);
+
+private slots:
+ // TDEIO::Job
+ void slotResult(TDEIO::Job *job);
+ void slotWarning(TDEIO::Job *job, const TQString &msg);
+ void slotInfoMessage(TDEIO::Job *job, const TQString &msg);
+ void slotTotalSize(TDEIO::Job *job, TDEIO::filesize_t size);
+ void slotProcessedSize(TDEIO::Job *job, TDEIO::filesize_t size);
+ void slotSpeed(TDEIO::Job *job, unsigned long bytesPerSecond);
+
+ // TDEIO::SimpleJob subclasses
+ void slotRedirection(TDEIO::Job *job, const KURL &url);
+
+ // TDEIO::ListJob
+ void slotEntries(TDEIO::Job *job, const TDEIO::UDSEntryList &entries);
+
+ // TDEIO::TransferJob
+ void slotData(TDEIO::Job *job, const TQByteArray &data);
+ void slotDataReq(TDEIO::Job *job, TQByteArray &data);
+ void slotMimetype (TDEIO::Job *job, const TQString &type);
+ void slotCanResume (TDEIO::Job *job, TDEIO::filesize_t offset);
+
+ // TDEIO::LocalURLJob
+ void slotLocalURL(TDEIO::Job *, const KURL&, bool);
+};
+
+}
+
+#endif
diff --git a/tdeio/tdeio/global.cpp b/tdeio/tdeio/global.cpp
new file mode 100644
index 000000000..e4bfec5f6
--- /dev/null
+++ b/tdeio/tdeio/global.cpp
@@ -0,0 +1,2009 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+
+#include <assert.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdio.h>
+
+#include "tdeio/global.h"
+#include "tdeio/job.h"
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kprotocolmanager.h>
+#include <kde_file.h>
+
+#ifdef HAVE_VOLMGT
+#include <volmgt.h>
+#endif
+
+TDEIO_EXPORT TQString TDEIO::convertSizeWithBytes( TDEIO::filesize_t size )
+{
+ if ( size >= 1024 )
+ return convertSize( size ) + " (" + i18n( "%1 B" ).arg( TDEGlobal::locale()->formatNumber(size, 0) ) + ")";
+ else
+ return convertSize( size );
+}
+
+TDEIO_EXPORT TQString TDEIO::convertSize( TDEIO::filesize_t size )
+{
+ double fsize = size;
+ TQString s;
+ // Giga-byte
+ if ( size >= 1073741824 )
+ {
+ fsize /= 1073741824.0;
+ if ( fsize > 1024 ) // Tera-byte
+ s = i18n( "%1 TB" ).arg( TDEGlobal::locale()->formatNumber(fsize / 1024.0, 1));
+ else
+ s = i18n( "%1 GB" ).arg( TDEGlobal::locale()->formatNumber(fsize, 1));
+ }
+ // Mega-byte
+ else if ( size >= 1048576 )
+ {
+ fsize /= 1048576.0;
+ s = i18n( "%1 MB" ).arg( TDEGlobal::locale()->formatNumber(fsize, 1));
+ }
+ // Kilo-byte
+ else if ( size >= 1024 )
+ {
+ fsize /= 1024.0;
+ s = i18n( "%1 KB" ).arg( TDEGlobal::locale()->formatNumber(fsize, 1));
+ }
+ // Just byte
+ else if ( size > 0 )
+ {
+ s = i18n( "%1 B" ).arg( TDEGlobal::locale()->formatNumber(fsize, 0));
+ }
+ // Nothing
+ else
+ {
+ s = i18n( "0 B" );
+ }
+ return s;
+}
+
+TDEIO_EXPORT TQString TDEIO::convertSizeFromKB( TDEIO::filesize_t kbSize )
+{
+ return convertSize(kbSize * 1024);
+}
+
+TDEIO_EXPORT TQString TDEIO::number( TDEIO::filesize_t size )
+{
+ char charbuf[256];
+ sprintf(charbuf, "%lld", size);
+ return TQString::fromLatin1(charbuf);
+}
+
+TDEIO_EXPORT unsigned int TDEIO::calculateRemainingSeconds( TDEIO::filesize_t totalSize,
+ TDEIO::filesize_t processedSize, TDEIO::filesize_t speed )
+{
+ if ( (speed != 0) && (totalSize != 0) )
+ return ( totalSize - processedSize ) / speed;
+ else
+ return 0;
+}
+
+TDEIO_EXPORT TQString TDEIO::convertSeconds( unsigned int seconds )
+{
+ unsigned int days = seconds / 86400;
+ unsigned int hours = (seconds - (days * 86400)) / 3600;
+ unsigned int mins = (seconds - (days * 86400) - (hours * 3600)) / 60;
+ seconds = (seconds - (days * 86400) - (hours * 3600) - (mins * 60));
+
+ const TQTime time(hours, mins, seconds);
+ const TQString timeStr( TDEGlobal::locale()->formatTime(time, true /*with seconds*/, true /*duration*/) );
+ if ( days > 0 )
+ return i18n("1 day %1", "%n days %1", days).arg(timeStr);
+ else
+ return timeStr;
+}
+
+TDEIO_EXPORT TQTime TDEIO::calculateRemaining( TDEIO::filesize_t totalSize, TDEIO::filesize_t processedSize, TDEIO::filesize_t speed )
+{
+ TQTime remainingTime;
+
+ if ( speed != 0 ) {
+ TDEIO::filesize_t secs;
+ if ( totalSize == 0 ) {
+ secs = 0;
+ } else {
+ secs = ( totalSize - processedSize ) / speed;
+ }
+ if (secs >= (24*60*60)) // Limit to 23:59:59
+ secs = (24*60*60)-1;
+ int hr = secs / ( 60 * 60 );
+ int mn = ( secs - hr * 60 * 60 ) / 60;
+ int sc = ( secs - hr * 60 * 60 - mn * 60 );
+
+ remainingTime.setHMS( hr, mn, sc );
+ }
+
+ return remainingTime;
+}
+
+TDEIO_EXPORT TQString TDEIO::itemsSummaryString(uint items, uint files, uint dirs, TDEIO::filesize_t size, bool showSize)
+{
+ TQString text = items == 0 ? i18n( "No Items" ) : i18n( "One Item", "%n Items", items );
+ text += " - ";
+ text += files == 0 ? i18n( "No Files" ) : i18n( "One File", "%n Files", files );
+ if ( showSize && files > 0 )
+ {
+ text += " ";
+ text += i18n("(%1 Total)").arg(TDEIO::convertSize( size ) );
+ }
+ text += " - ";
+ text += dirs == 0 ? i18n( "No Folders" ) : i18n("One Folder", "%n Folders", dirs);
+ return text;
+}
+
+TDEIO_EXPORT TQString TDEIO::encodeFileName( const TQString & _str )
+{
+ TQString str( _str );
+
+ int i = 0;
+ while ( ( i = str.find( "%", i ) ) != -1 )
+ {
+ str.replace( i, 1, "%%");
+ i += 2;
+ }
+ while ( ( i = str.find( "/" ) ) != -1 )
+ str.replace( i, 1, "%2f");
+ return str;
+}
+
+TDEIO_EXPORT TQString TDEIO::decodeFileName( const TQString & _str )
+{
+ TQString str;
+
+ unsigned int i = 0;
+ for ( ; i < _str.length() ; ++i )
+ {
+ if ( _str[i]=='%' )
+ {
+ if ( _str[i+1]=='%' ) // %% -> %
+ {
+ str.append('%');
+ ++i;
+ }
+ else if ( _str[i+1]=='2' && (i+2<_str.length()) && _str[i+2].lower()=='f' ) // %2f -> /
+ {
+ str.append('/');
+ i += 2;
+ }
+ else
+ str.append('%');
+ } else
+ str.append(_str[i]);
+ }
+
+ return str;
+}
+
+TDEIO_EXPORT TQString TDEIO::Job::errorString() const
+{
+ return TDEIO::buildErrorString(m_error, m_errorText);
+}
+
+TDEIO_EXPORT TQString TDEIO::buildErrorString(int errorCode, const TQString &errorText)
+{
+ TQString result;
+
+ switch( errorCode )
+ {
+ case TDEIO::ERR_CANNOT_OPEN_FOR_READING:
+ result = i18n( "Could not read %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_CANNOT_OPEN_FOR_WRITING:
+ result = i18n( "Could not write to %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_CANNOT_LAUNCH_PROCESS:
+ result = i18n( "Could not start process %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_INTERNAL:
+ result = i18n( "Internal Error\nPlease send a full bug report at http://bugs.kde.org\n%1" ).arg( errorText );
+ break;
+ case TDEIO::ERR_MALFORMED_URL:
+ result = i18n( "Malformed URL %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_UNSUPPORTED_PROTOCOL:
+ result = i18n( "The protocol %1 is not supported." ).arg( errorText );
+ break;
+ case TDEIO::ERR_NO_SOURCE_PROTOCOL:
+ result = i18n( "The protocol %1 is only a filter protocol.").arg( errorText );
+ break;
+ case TDEIO::ERR_UNSUPPORTED_ACTION:
+ result = errorText;
+// result = i18n( "Unsupported action %1" ).arg( errorText );
+ break;
+ case TDEIO::ERR_IS_DIRECTORY:
+ result = i18n( "%1 is a folder, but a file was expected." ).arg( errorText );
+ break;
+ case TDEIO::ERR_IS_FILE:
+ result = i18n( "%1 is a file, but a folder was expected." ).arg( errorText );
+ break;
+ case TDEIO::ERR_DOES_NOT_EXIST:
+ result = i18n( "The file or folder %1 does not exist." ).arg( errorText );
+ break;
+ case TDEIO::ERR_FILE_ALREADY_EXIST:
+ result = i18n( "A file named %1 already exists." ).arg( errorText );
+ break;
+ case TDEIO::ERR_DIR_ALREADY_EXIST:
+ result = i18n( "A folder named %1 already exists." ).arg( errorText );
+ break;
+ case TDEIO::ERR_UNKNOWN_HOST:
+ result = errorText.isEmpty() ? i18n( "No hostname specified." ) : i18n( "Unknown host %1" ).arg( errorText );
+ break;
+ case TDEIO::ERR_ACCESS_DENIED:
+ result = i18n( "Access denied to %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_WRITE_ACCESS_DENIED:
+ result = i18n( "Access denied.\nCould not write to %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_CANNOT_ENTER_DIRECTORY:
+ result = i18n( "Could not enter folder %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_PROTOCOL_IS_NOT_A_FILESYSTEM:
+ result = i18n( "The protocol %1 does not implement a folder service." ).arg( errorText );
+ break;
+ case TDEIO::ERR_CYCLIC_LINK:
+ result = i18n( "Found a cyclic link in %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_USER_CANCELED:
+ // Do nothing in this case. The user doesn't need to be told what he just did.
+ break;
+ case TDEIO::ERR_CYCLIC_COPY:
+ result = i18n( "Found a cyclic link while copying %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_COULD_NOT_CREATE_SOCKET:
+ result = i18n( "Could not create socket for accessing %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_COULD_NOT_CONNECT:
+ result = i18n( "Could not connect to host %1." ).arg( errorText.isEmpty() ? TQString::fromLatin1("localhost") : errorText );
+ break;
+ case TDEIO::ERR_CONNECTION_BROKEN:
+ result = i18n( "Connection to host %1 is broken." ).arg( errorText );
+ break;
+ case TDEIO::ERR_NOT_FILTER_PROTOCOL:
+ result = i18n( "The protocol %1 is not a filter protocol." ).arg( errorText );
+ break;
+ case TDEIO::ERR_COULD_NOT_MOUNT:
+ result = i18n( "Could not mount device.\nThe reported error was:\n%1" ).arg( errorText );
+ break;
+ case TDEIO::ERR_COULD_NOT_UNMOUNT:
+ result = i18n( "Could not unmount device.\nThe reported error was:\n%1" ).arg( errorText );
+ break;
+ case TDEIO::ERR_COULD_NOT_READ:
+ result = i18n( "Could not read file %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_COULD_NOT_WRITE:
+ result = i18n( "Could not write to file %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_COULD_NOT_BIND:
+ result = i18n( "Could not bind %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_COULD_NOT_LISTEN:
+ result = i18n( "Could not listen %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_COULD_NOT_ACCEPT:
+ result = i18n( "Could not accept %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_COULD_NOT_LOGIN:
+ result = errorText;
+ break;
+ case TDEIO::ERR_COULD_NOT_STAT:
+ result = i18n( "Could not access %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_COULD_NOT_CLOSEDIR:
+ result = i18n( "Could not terminate listing %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_COULD_NOT_MKDIR:
+ result = i18n( "Could not make folder %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_COULD_NOT_RMDIR:
+ result = i18n( "Could not remove folder %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_CANNOT_RESUME:
+ result = i18n( "Could not resume file %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_CANNOT_RENAME:
+ result = i18n( "Could not rename file %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_CANNOT_CHMOD:
+ result = i18n( "Could not change permissions for %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_CANNOT_DELETE:
+ result = i18n( "Could not delete file %1." ).arg( errorText );
+ break;
+ case TDEIO::ERR_SLAVE_DIED:
+ result = i18n( "The process for the %1 protocol died unexpectedly." ).arg( errorText );
+ break;
+ case TDEIO::ERR_OUT_OF_MEMORY:
+ result = i18n( "Error. Out of memory.\n%1" ).arg( errorText );
+ break;
+ case TDEIO::ERR_UNKNOWN_PROXY_HOST:
+ result = i18n( "Unknown proxy host\n%1" ).arg( errorText );
+ break;
+ case TDEIO::ERR_COULD_NOT_AUTHENTICATE:
+ result = i18n( "Authorization failed, %1 authentication not supported" ).arg( errorText );
+ break;
+ case TDEIO::ERR_ABORTED:
+ result = i18n( "User canceled action\n%1" ).arg( errorText );
+ break;
+ case TDEIO::ERR_INTERNAL_SERVER:
+ result = i18n( "Internal error in server\n%1" ).arg( errorText );
+ break;
+ case TDEIO::ERR_SERVER_TIMEOUT:
+ result = i18n( "Timeout on server\n%1" ).arg( errorText );
+ break;
+ case TDEIO::ERR_UNKNOWN:
+ result = i18n( "Unknown error\n%1" ).arg( errorText );
+ break;
+ case TDEIO::ERR_UNKNOWN_INTERRUPT:
+ result = i18n( "Unknown interrupt\n%1" ).arg( errorText );
+ break;
+/*
+ case TDEIO::ERR_CHECKSUM_MISMATCH:
+ if (errorText)
+ result = i18n( "Warning: MD5 Checksum for %1 does not match checksum returned from server" ).arg(errorText);
+ else
+ result = i18n( "Warning: MD5 Checksum for %1 does not match checksum returned from server" ).arg("document");
+ break;
+*/
+ case TDEIO::ERR_CANNOT_DELETE_ORIGINAL:
+ result = i18n( "Could not delete original file %1.\nPlease check permissions." ).arg( errorText );
+ break;
+ case TDEIO::ERR_CANNOT_DELETE_PARTIAL:
+ result = i18n( "Could not delete partial file %1.\nPlease check permissions." ).arg( errorText );
+ break;
+ case TDEIO::ERR_CANNOT_RENAME_ORIGINAL:
+ result = i18n( "Could not rename original file %1.\nPlease check permissions." ).arg( errorText );
+ break;
+ case TDEIO::ERR_CANNOT_RENAME_PARTIAL:
+ result = i18n( "Could not rename partial file %1.\nPlease check permissions." ).arg( errorText );
+ break;
+ case TDEIO::ERR_CANNOT_SYMLINK:
+ result = i18n( "Could not create symlink %1.\nPlease check permissions." ).arg( errorText );
+ break;
+ case TDEIO::ERR_NO_CONTENT:
+ result = errorText;
+ break;
+ case TDEIO::ERR_DISK_FULL:
+ result = i18n( "Could not write file %1.\nDisk full." ).arg( errorText );
+ break;
+ case TDEIO::ERR_IDENTICAL_FILES:
+ result = i18n( "The source and destination are the same file.\n%1" ).arg( errorText );
+ break;
+ case TDEIO::ERR_SLAVE_DEFINED:
+ result = errorText;
+ break;
+ case TDEIO::ERR_UPGRADE_REQUIRED:
+ result = i18n( "%1 is required by the server, but is not available." ).arg(errorText);
+ break;
+ case TDEIO::ERR_POST_DENIED:
+ result = i18n( "Access to restricted port in POST denied.");
+ break;
+ case TDEIO::ERR_OFFLINE_MODE:
+ result = i18n( "Could not access %1.\nOffline mode active.").arg( errorText );
+ break;
+ default:
+ result = i18n( "Unknown error code %1\n%2\nPlease send a full bug report at http://bugs.kde.org." ).arg( errorCode ).arg( errorText );
+ break;
+ }
+
+ return result;
+}
+
+TDEIO_EXPORT TQString TDEIO::unsupportedActionErrorString(const TQString &protocol, int cmd) {
+ switch (cmd) {
+ case CMD_CONNECT:
+ return i18n("Opening connections is not supported with the protocol %1." ).arg(protocol);
+ case CMD_DISCONNECT:
+ return i18n("Closing connections is not supported with the protocol %1." ).arg(protocol);
+ case CMD_STAT:
+ return i18n("Accessing files is not supported with the protocol %1.").arg(protocol);
+ case CMD_PUT:
+ return i18n("Writing to %1 is not supported.").arg(protocol);
+ case CMD_SPECIAL:
+ return i18n("There are no special actions available for protocol %1.").arg(protocol);
+ case CMD_LISTDIR:
+ return i18n("Listing folders is not supported for protocol %1.").arg(protocol);
+ case CMD_GET:
+ return i18n("Retrieving data from %1 is not supported.").arg(protocol);
+ case CMD_MIMETYPE:
+ return i18n("Retrieving mime type information from %1 is not supported.").arg(protocol);
+ case CMD_RENAME:
+ return i18n("Renaming or moving files within %1 is not supported.").arg(protocol);
+ case CMD_SYMLINK:
+ return i18n("Creating symlinks is not supported with protocol %1.").arg(protocol);
+ case CMD_COPY:
+ return i18n("Copying files within %1 is not supported.").arg(protocol);
+ case CMD_DEL:
+ return i18n("Deleting files from %1 is not supported.").arg(protocol);
+ case CMD_MKDIR:
+ return i18n("Creating folders is not supported with protocol %1.").arg(protocol);
+ case CMD_CHMOD:
+ return i18n("Changing the attributes of files is not supported with protocol %1.").arg(protocol);
+ case CMD_SUBURL:
+ return i18n("Using sub-URLs with %1 is not supported.").arg(protocol);
+ case CMD_MULTI_GET:
+ return i18n("Multiple get is not supported with protocol %1.").arg(protocol);
+ default:
+ return i18n("Protocol %1 does not support action %2.").arg(protocol).arg(cmd);
+ }/*end switch*/
+}
+
+TDEIO_EXPORT TQStringList TDEIO::Job::detailedErrorStrings( const KURL *reqUrl /*= 0L*/,
+ int method /*= -1*/ ) const
+{
+ TQString errorName, techName, description, ret2;
+ TQStringList causes, solutions, ret;
+
+ TQByteArray raw = rawErrorDetail( m_error, m_errorText, reqUrl, method );
+ TQDataStream stream(raw, IO_ReadOnly);
+
+ stream >> errorName >> techName >> description >> causes >> solutions;
+
+ TQString url, protocol, datetime;
+ if ( reqUrl ) {
+ url = reqUrl->htmlURL();
+ protocol = reqUrl->protocol();
+ } else {
+ url = i18n( "(unknown)" );
+ }
+
+ datetime = TDEGlobal::locale()->formatDateTime( TQDateTime::currentDateTime(),
+ false );
+
+ ret << errorName;
+ ret << TQString::fromLatin1( "<qt><p><b>" ) + errorName +
+ TQString::fromLatin1( "</b></p><p>" ) + description +
+ TQString::fromLatin1( "</p>" );
+ ret2 = TQString::fromLatin1( "<qt><p>" );
+ if ( !techName.isEmpty() )
+ ret2 += i18n( "<b>Technical reason</b>: " ) + techName + TQString::fromLatin1( "</p>" );
+ ret2 += i18n( "</p><p><b>Details of the request</b>:" );
+ ret2 += i18n( "</p><ul><li>URL: %1</li>" ).arg( url );
+ if ( !protocol.isEmpty() ) {
+ ret2 += i18n( "<li>Protocol: %1</li>" ).arg( protocol );
+ }
+ ret2 += i18n( "<li>Date and time: %1</li>" ).arg( datetime );
+ ret2 += i18n( "<li>Additional information: %1</li></ul>" ).arg( m_errorText );
+ if ( !causes.isEmpty() ) {
+ ret2 += i18n( "<p><b>Possible causes</b>:</p><ul><li>" );
+ ret2 += causes.join( "</li><li>" );
+ ret2 += TQString::fromLatin1( "</li></ul>" );
+ }
+ if ( !solutions.isEmpty() ) {
+ ret2 += i18n( "<p><b>Possible solutions</b>:</p><ul><li>" );
+ ret2 += solutions.join( "</li><li>" );
+ ret2 += TQString::fromLatin1( "</li></ul>" );
+ }
+ ret << ret2;
+ return ret;
+}
+
+TDEIO_EXPORT TQByteArray TDEIO::rawErrorDetail(int errorCode, const TQString &errorText,
+ const KURL *reqUrl /*= 0L*/, int /*method = -1*/ )
+{
+ TQString url, host, protocol, datetime, domain, path, dir, filename;
+ bool isSlaveNetwork = false;
+ if ( reqUrl ) {
+ url = reqUrl->prettyURL();
+ host = reqUrl->host();
+ protocol = reqUrl->protocol();
+
+ if ( host.left(4) == "www." )
+ domain = host.mid(4);
+ else
+ domain = host;
+
+ path = reqUrl->path(1);
+ filename = reqUrl->fileName();
+ dir = path + filename;
+
+ // detect if protocol is a network protocol...
+ // add your hacks here...
+ if ( protocol == "http" ||
+ protocol == "https" ||
+ protocol == "ftp" ||
+ protocol == "sftp" ||
+ protocol == "webdav" ||
+ protocol == "webdavs" ||
+ protocol == "finger" ||
+ protocol == "fish" ||
+ protocol == "gopher" ||
+ protocol == "imap" ||
+ protocol == "imaps" ||
+ protocol == "lan" ||
+ protocol == "ldap" ||
+ protocol == "mailto" ||
+ protocol == "news" ||
+ protocol == "nntp" ||
+ protocol == "pop3" ||
+ protocol == "pop3s" ||
+ protocol == "smtp" ||
+ protocol == "smtps" ||
+ protocol == "telnet"
+ ) {
+ isSlaveNetwork = false;
+ }
+ } else {
+ // assume that the errorText has the location we are interested in
+ url = host = domain = path = filename = dir = errorText;
+ protocol = i18n( "(unknown)" );
+ }
+
+ datetime = TDEGlobal::locale()->formatDateTime( TQDateTime::currentDateTime(),
+ false );
+
+ TQString errorName, techName, description;
+ TQStringList causes, solutions;
+
+ // c == cause, s == solution
+ TQString sSysadmin = i18n( "Contact your appropriate computer support system, "
+ "whether the system administrator, or technical support group for further "
+ "assistance." );
+ TQString sServeradmin = i18n( "Contact the administrator of the server "
+ "for further assistance." );
+ // FIXME active link to permissions dialog
+ TQString sAccess = i18n( "Check your access permissions on this resource." );
+ TQString cAccess = i18n( "Your access permissions may be inadequate to "
+ "perform the requested operation on this resource." );
+ TQString cLocked = i18n( "The file may be in use (and thus locked) by "
+ "another user or application." );
+ TQString sQuerylock = i18n( "Check to make sure that no other "
+ "application or user is using the file or has locked the file." );
+ TQString cHardware = i18n( "Although unlikely, a hardware error may have "
+ "occurred." );
+ TQString cBug = i18n( "You may have encountered a bug in the program." );
+ TQString cBuglikely = i18n( "This is most likely to be caused by a bug in the "
+ "program. Please consider submitting a full bug report as detailed below." );
+ TQString sUpdate = i18n( "Update your software to the latest version. "
+ "Your distribution should provide tools to update your software." );
+ TQString sBugreport = i18n( "When all else fails, please consider helping the "
+ "TDE team or the third party maintainer of this software by submitting a "
+ "high quality bug report. If the software is provided by a third party, "
+ "please contact them directly. Otherwise, first look to see if "
+ "the same bug has been submitted by someone else by searching at the "
+ "<a href=\"http://bugs.pearsoncomputing.net//\">TDE bug reporting website</a>. If not, take "
+ "note of the details given above, and include them in your bug report, along "
+ "with as many other details as you think might help." );
+ TQString cNetwork = i18n( "There may have been a problem with your network "
+ "connection." );
+ // FIXME netconf kcontrol link
+ TQString cNetconf = i18n( "There may have been a problem with your network "
+ "configuration. If you have been accessing the Internet with no problems "
+ "recently, this is unlikely." );
+ TQString cNetpath = i18n( "There may have been a problem at some point along "
+ "the network path between the server and this computer." );
+ TQString sTryagain = i18n( "Try again, either now or at a later time." );
+ TQString cProtocol = i18n( "A protocol error or incompatibility may have occurred." );
+ TQString sExists = i18n( "Ensure that the resource exists, and try again." );
+ TQString cExists = i18n( "The specified resource may not exist." );
+ TQString cTypo = i18n( "You may have incorrectly typed the location." );
+ TQString sTypo = i18n( "Double-check that you have entered the correct location "
+ "and try again." );
+ TQString sNetwork = i18n( "Check your network connection status." );
+
+ switch( errorCode ) {
+ case TDEIO::ERR_CANNOT_OPEN_FOR_READING:
+ errorName = i18n( "Cannot Open Resource For Reading" );
+ description = i18n( "This means that the contents of the requested file "
+ "or folder <strong>%1</strong> could not be retrieved, as read "
+ "access could not be obtained." ).arg( dir );
+ causes << i18n( "You may not have permissions to read the file or open "
+ "the folder.") << cLocked << cHardware;
+ solutions << sAccess << sQuerylock << sSysadmin;
+ break;
+
+ case TDEIO::ERR_CANNOT_OPEN_FOR_WRITING:
+ errorName = i18n( "Cannot Open Resource For Writing" );
+ description = i18n( "This means that the file, <strong>%1</strong>, could "
+ "not be written to as requested, because access with permission to "
+ "write could not be obtained." ).arg( filename );
+ causes << cAccess << cLocked << cHardware;
+ solutions << sAccess << sQuerylock << sSysadmin;
+ break;
+
+ case TDEIO::ERR_CANNOT_LAUNCH_PROCESS:
+ errorName = i18n( "Cannot Initiate the %1 Protocol" ).arg( protocol );
+ techName = i18n( "Unable to Launch Process" );
+ description = i18n( "The program on your computer which provides access "
+ "to the <strong>%1</strong> protocol could not be started. This is "
+ "usually due to technical reasons." ).arg( protocol );
+ causes << i18n( "The program which provides compatibility with this "
+ "protocol may not have been updated with your last update of TDE. "
+ "This can cause the program to be incompatible with the current version "
+ "and thus not start." ) << cBug;
+ solutions << sUpdate << sSysadmin;
+ break;
+
+ case TDEIO::ERR_INTERNAL:
+ errorName = i18n( "Internal Error" );
+ description = i18n( "The program on your computer which provides access "
+ "to the <strong>%1</strong> protocol has reported an internal error." )
+ .arg( protocol );
+ causes << cBuglikely;
+ solutions << sUpdate << sBugreport;
+ break;
+
+ case TDEIO::ERR_MALFORMED_URL:
+ errorName = i18n( "Improperly Formatted URL" );
+ description = i18n( "The <strong>U</strong>niform <strong>R</strong>esource "
+ "<strong>L</strong>ocator (URL) that you entered was not properly "
+ "formatted. The format of a URL is generally as follows:"
+ "<blockquote><strong>protocol://user:password@www.example.org:port/folder/"
+ "filename.extension?query=value</strong></blockquote>" );
+ solutions << sTypo;
+ break;
+
+ case TDEIO::ERR_UNSUPPORTED_PROTOCOL:
+ errorName = i18n( "Unsupported Protocol %1" ).arg( protocol );
+ description = i18n( "The protocol <strong>%1</strong> is not supported "
+ "by the TDE programs currently installed on this computer." )
+ .arg( protocol );
+ causes << i18n( "The requested protocol may not be supported." )
+ << i18n( "The versions of the %1 protocol supported by this computer and "
+ "the server may be incompatible." ).arg( protocol );
+ solutions << i18n( "You may perform a search on the Internet for a TDE "
+ "program (called a tdeioslave or ioslave) which supports this protocol. "
+ "Places to search include <a href=\"http://kde-apps.org/\">"
+ "http://kde-apps.org/</a> and <a href=\"http://freshmeat.net/\">"
+ "http://freshmeat.net/</a>." )
+ << sUpdate << sSysadmin;
+ break;
+
+ case TDEIO::ERR_NO_SOURCE_PROTOCOL:
+ errorName = i18n( "URL Does Not Refer to a Resource." );
+ techName = i18n( "Protocol is a Filter Protocol" );
+ description = i18n( "The <strong>U</strong>niform <strong>R</strong>esource "
+ "<strong>L</strong>ocator (URL) that you entered did not refer to a "
+ "specific resource." );
+ causes << i18n( "TDE is able to communicate through a protocol within a "
+ "protocol; the protocol specified is only for use in such situations, "
+ "however this is not one of these situations. This is a rare event, and "
+ "is likely to indicate a programming error." );
+ solutions << sTypo;
+ break;
+
+ case TDEIO::ERR_UNSUPPORTED_ACTION:
+ errorName = i18n( "Unsupported Action: %1" ).arg( errorText );
+ description = i18n( "The requested action is not supported by the TDE "
+ "program which is implementing the <strong>%1</strong> protocol." )
+ .arg( protocol );
+ causes << i18n( "This error is very much dependent on the TDE program. The "
+ "additional information should give you more information than is available "
+ "to the TDE input/output architecture." );
+ solutions << i18n( "Attempt to find another way to accomplish the same "
+ "outcome." );
+ break;
+
+ case TDEIO::ERR_IS_DIRECTORY:
+ errorName = i18n( "File Expected" );
+ description = i18n( "The request expected a file, however the "
+ "folder <strong>%1</strong> was found instead." ).arg( dir );
+ causes << i18n( "This may be an error on the server side." ) << cBug;
+ solutions << sUpdate << sSysadmin;
+ break;
+
+ case TDEIO::ERR_IS_FILE:
+ errorName = i18n( "Folder Expected" );
+ description = i18n( "The request expected a folder, however "
+ "the file <strong>%1</strong> was found instead." ).arg( filename );
+ causes << cBug;
+ solutions << sUpdate << sSysadmin;
+ break;
+
+ case TDEIO::ERR_DOES_NOT_EXIST:
+ errorName = i18n( "File or Folder Does Not Exist" );
+ description = i18n( "The specified file or folder <strong>%1</strong> "
+ "does not exist." ).arg( dir );
+ causes << cBug;
+ solutions << sUpdate << sSysadmin;
+ break;
+
+ case TDEIO::ERR_FILE_ALREADY_EXIST:
+ errorName = i18n( "File Already Exists" );
+ description = i18n( "The requested file could not be created because a "
+ "file with the same name already exists." );
+ solutions << i18n ( "Try moving the current file out of the way first, "
+ "and then try again." )
+ << i18n ( "Delete the current file and try again." )
+ << i18n( "Choose an alternate filename for the new file." );
+ break;
+
+ case TDEIO::ERR_DIR_ALREADY_EXIST:
+ errorName = i18n( "Folder Already Exists" );
+ description = i18n( "The requested folder could not be created because "
+ "a folder with the same name already exists." );
+ solutions << i18n( "Try moving the current folder out of the way first, "
+ "and then try again." )
+ << i18n( "Delete the current folder and try again." )
+ << i18n( "Choose an alternate name for the new folder." );
+ break;
+
+ case TDEIO::ERR_UNKNOWN_HOST:
+ errorName = i18n( "Unknown Host" );
+ description = i18n( "An unknown host error indicates that the server with "
+ "the requested name, <strong>%1</strong>, could not be "
+ "located on the Internet." ).arg( host );
+ causes << i18n( "The name that you typed, %1, may not exist: it may be "
+ "incorrectly typed." ).arg( host )
+ << cNetwork << cNetconf;
+ solutions << sNetwork << sSysadmin;
+ break;
+
+ case TDEIO::ERR_ACCESS_DENIED:
+ errorName = i18n( "Access Denied" );
+ description = i18n( "Access was denied to the specified resource, "
+ "<strong>%1</strong>." ).arg( url );
+ causes << i18n( "You may have supplied incorrect authentication details or "
+ "none at all." )
+ << i18n( "Your account may not have permission to access the "
+ "specified resource." );
+ solutions << i18n( "Retry the request and ensure your authentication details "
+ "are entered correctly." ) << sSysadmin;
+ if ( !isSlaveNetwork ) solutions << sServeradmin;
+ break;
+
+ case TDEIO::ERR_WRITE_ACCESS_DENIED:
+ errorName = i18n( "Write Access Denied" );
+ description = i18n( "This means that an attempt to write to the file "
+ "<strong>%1</strong> was rejected." ).arg( filename );
+ causes << cAccess << cLocked << cHardware;
+ solutions << sAccess << sQuerylock << sSysadmin;
+ break;
+
+ case TDEIO::ERR_CANNOT_ENTER_DIRECTORY:
+ errorName = i18n( "Unable to Enter Folder" );
+ description = i18n( "This means that an attempt to enter (in other words, "
+ "to open) the requested folder <strong>%1</strong> was rejected." )
+ .arg( dir );
+ causes << cAccess << cLocked;
+ solutions << sAccess << sQuerylock << sSysadmin;
+ break;
+
+ case TDEIO::ERR_PROTOCOL_IS_NOT_A_FILESYSTEM:
+ errorName = i18n( "Folder Listing Unavailable" );
+ techName = i18n( "Protocol %1 is not a Filesystem" ).arg( protocol );
+ description = i18n( "This means that a request was made which requires "
+ "determining the contents of the folder, and the TDE program supporting "
+ "this protocol is unable to do so." );
+ causes << cBug;
+ solutions << sUpdate << sBugreport;
+ break;
+
+ case TDEIO::ERR_CYCLIC_LINK:
+ errorName = i18n( "Cyclic Link Detected" );
+ description = i18n( "UNIX environments are commonly able to link a file or "
+ "folder to a separate name and/or location. TDE detected a link or "
+ "series of links that results in an infinite loop - i.e. the file was "
+ "(perhaps in a roundabout way) linked to itself." );
+ solutions << i18n( "Delete one part of the loop in order that it does not "
+ "cause an infinite loop, and try again." ) << sSysadmin;
+ break;
+
+ case TDEIO::ERR_USER_CANCELED:
+ // Do nothing in this case. The user doesn't need to be told what he just did.
+ // rodda: However, if we have been called, an application is about to display
+ // this information anyway. If we don't return sensible information, the
+ // user sees a blank dialog (I have seen this myself)
+ errorName = i18n( "Request Aborted By User" );
+ description = i18n( "The request was not completed because it was "
+ "aborted." );
+ solutions << i18n( "Retry the request." );
+ break;
+
+ case TDEIO::ERR_CYCLIC_COPY:
+ errorName = i18n( "Cyclic Link Detected During Copy" );
+ description = i18n( "UNIX environments are commonly able to link a file or "
+ "folder to a separate name and/or location. During the requested copy "
+ "operation, TDE detected a link or series of links that results in an "
+ "infinite loop - i.e. the file was (perhaps in a roundabout way) linked "
+ "to itself." );
+ solutions << i18n( "Delete one part of the loop in order that it does not "
+ "cause an infinite loop, and try again." ) << sSysadmin;
+ break;
+
+ case TDEIO::ERR_COULD_NOT_CREATE_SOCKET:
+ errorName = i18n( "Could Not Create Network Connection" );
+ techName = i18n( "Could Not Create Socket" );
+ description = i18n( "This is a fairly technical error in which a required "
+ "device for network communications (a socket) could not be created." );
+ causes << i18n( "The network connection may be incorrectly configured, or "
+ "the network interface may not be enabled." );
+ solutions << sNetwork << sSysadmin;
+ break;
+
+ case TDEIO::ERR_COULD_NOT_CONNECT:
+ errorName = i18n( "Connection to Server Refused" );
+ description = i18n( "The server <strong>%1</strong> refused to allow this "
+ "computer to make a connection." ).arg( host );
+ causes << i18n( "The server, while currently connected to the Internet, "
+ "may not be configured to allow requests." )
+ << i18n( "The server, while currently connected to the Internet, "
+ "may not be running the requested service (%1)." ).arg( protocol )
+ << i18n( "A network firewall (a device which restricts Internet "
+ "requests), either protecting your network or the network of the server, "
+ "may have intervened, preventing this request." );
+ solutions << sTryagain << sServeradmin << sSysadmin;
+ break;
+
+ case TDEIO::ERR_CONNECTION_BROKEN:
+ errorName = i18n( "Connection to Server Closed Unexpectedly" );
+ description = i18n( "Although a connection was established to "
+ "<strong>%1</strong>, the connection was closed at an unexpected point "
+ "in the communication." ).arg( host );
+ causes << cNetwork << cNetpath << i18n( "A protocol error may have occurred, "
+ "causing the server to close the connection as a response to the error." );
+ solutions << sTryagain << sServeradmin << sSysadmin;
+ break;
+
+ case TDEIO::ERR_NOT_FILTER_PROTOCOL:
+ errorName = i18n( "URL Resource Invalid" );
+ techName = i18n( "Protocol %1 is not a Filter Protocol" ).arg( protocol );
+ description = i18n( "The <strong>U</strong>niform <strong>R</strong>esource "
+ "<strong>L</strong>ocator (URL) that you entered did not refer to "
+ "a valid mechanism of accessing the specific resource, "
+ "<strong>%1%2</strong>." )
+ .arg( !host.isNull() ? host + '/' : TQString::null ).arg( dir );
+ causes << i18n( "TDE is able to communicate through a protocol within a "
+ "protocol. This request specified a protocol be used as such, however "
+ "this protocol is not capable of such an action. This is a rare event, "
+ "and is likely to indicate a programming error." );
+ solutions << sTypo << sSysadmin;
+ break;
+
+ case TDEIO::ERR_COULD_NOT_MOUNT:
+ errorName = i18n( "Unable to Initialize Input/Output Device" );
+ techName = i18n( "Could Not Mount Device" );
+ description = i18n( "The requested device could not be initialized "
+ "(\"mounted\"). The reported error was: <strong>%1</strong>" )
+ .arg( errorText );
+ causes << i18n( "The device may not be ready, for example there may be "
+ "no media in a removable media device (i.e. no CD-ROM in a CD drive), "
+ "or in the case of a peripheral/portable device, the device may not "
+ "be correctly connected." )
+ << i18n( "You may not have permissions to initialize (\"mount\") the "
+ "device. On UNIX systems, often system administrator privileges are "
+ "required to initialize a device." )
+ << cHardware;
+ solutions << i18n( "Check that the device is ready; removable drives "
+ "must contain media, and portable devices must be connected and powered "
+ "on.; and try again." ) << sAccess << sSysadmin;
+ break;
+
+ case TDEIO::ERR_COULD_NOT_UNMOUNT:
+ errorName = i18n( "Unable to Uninitialize Input/Output Device" );
+ techName = i18n( "Could Not Unmount Device" );
+ description = i18n( "The requested device could not be uninitialized "
+ "(\"unmounted\"). The reported error was: <strong>%1</strong>" )
+ .arg( errorText );
+ causes << i18n( "The device may be busy, that is, still in use by "
+ "another application or user. Even such things as having an open "
+ "browser window on a location on this device may cause the device to "
+ "remain in use." )
+ << i18n( "You may not have permissions to uninitialize (\"unmount\") "
+ "the device. On UNIX systems, system administrator privileges are "
+ "often required to uninitialize a device." )
+ << cHardware;
+ solutions << i18n( "Check that no applications are accessing the device, "
+ "and try again." ) << sAccess << sSysadmin;
+ break;
+
+ case TDEIO::ERR_COULD_NOT_READ:
+ errorName = i18n( "Cannot Read From Resource" );
+ description = i18n( "This means that although the resource, "
+ "<strong>%1</strong>, was able to be opened, an error occurred while "
+ "reading the contents of the resource." ).arg( url );
+ causes << i18n( "You may not have permissions to read from the resource." );
+ if ( !isSlaveNetwork ) causes << cNetwork;
+ causes << cHardware;
+ solutions << sAccess;
+ if ( !isSlaveNetwork ) solutions << sNetwork;
+ solutions << sSysadmin;
+ break;
+
+ case TDEIO::ERR_COULD_NOT_WRITE:
+ errorName = i18n( "Cannot Write to Resource" );
+ description = i18n( "This means that although the resource, <strong>%1</strong>"
+ ", was able to be opened, an error occurred while writing to the resource." )
+ .arg( url );
+ causes << i18n( "You may not have permissions to write to the resource." );
+ if ( !isSlaveNetwork ) causes << cNetwork;
+ causes << cHardware;
+ solutions << sAccess;
+ if ( !isSlaveNetwork ) solutions << sNetwork;
+ solutions << sSysadmin;
+ break;
+
+ case TDEIO::ERR_COULD_NOT_BIND:
+ errorName = i18n( "Could Not Listen for Network Connections" );
+ techName = i18n( "Could Not Bind" );
+ description = i18n( "This is a fairly technical error in which a required "
+ "device for network communications (a socket) could not be established "
+ "to listen for incoming network connections." );
+ causes << i18n( "The network connection may be incorrectly configured, or "
+ "the network interface may not be enabled." );
+ solutions << sNetwork << sSysadmin;
+ break;
+
+ case TDEIO::ERR_COULD_NOT_LISTEN:
+ errorName = i18n( "Could Not Listen for Network Connections" );
+ techName = i18n( "Could Not Listen" );
+ description = i18n( "This is a fairly technical error in which a required "
+ "device for network communications (a socket) could not be established "
+ "to listen for incoming network connections." );
+ causes << i18n( "The network connection may be incorrectly configured, or "
+ "the network interface may not be enabled." );
+ solutions << sNetwork << sSysadmin;
+ break;
+
+ case TDEIO::ERR_COULD_NOT_ACCEPT:
+ errorName = i18n( "Could Not Accept Network Connection" );
+ description = i18n( "This is a fairly technical error in which an error "
+ "occurred while attempting to accept an incoming network connection." );
+ causes << i18n( "The network connection may be incorrectly configured, or "
+ "the network interface may not be enabled." )
+ << i18n( "You may not have permissions to accept the connection." );
+ solutions << sNetwork << sSysadmin;
+ break;
+
+ case TDEIO::ERR_COULD_NOT_LOGIN:
+ errorName = i18n( "Could Not Login: %1" ).arg( errorText );
+ description = i18n( "An attempt to login to perform the requested "
+ "operation was unsuccessful." );
+ causes << i18n( "You may have supplied incorrect authentication details or "
+ "none at all." )
+ << i18n( "Your account may not have permission to access the "
+ "specified resource." ) << cProtocol;
+ solutions << i18n( "Retry the request and ensure your authentication details "
+ "are entered correctly." ) << sServeradmin << sSysadmin;
+ break;
+
+ case TDEIO::ERR_COULD_NOT_STAT:
+ errorName = i18n( "Could Not Determine Resource Status" );
+ techName = i18n( "Could Not Stat Resource" );
+ description = i18n( "An attempt to determine information about the status "
+ "of the resource <strong>%1</strong>, such as the resource name, type, "
+ "size, etc., was unsuccessful." ).arg( url );
+ causes << i18n( "The specified resource may not have existed or may "
+ "not be accessible." ) << cProtocol << cHardware;
+ solutions << i18n( "Retry the request and ensure your authentication details "
+ "are entered correctly." ) << sSysadmin;
+ break;
+
+ case TDEIO::ERR_COULD_NOT_CLOSEDIR:
+ //result = i18n( "Could not terminate listing %1" ).arg( errorText );
+ errorName = i18n( "Could Not Cancel Listing" );
+ techName = i18n( "FIXME: Document this" );
+ break;
+
+ case TDEIO::ERR_COULD_NOT_MKDIR:
+ errorName = i18n( "Could Not Create Folder" );
+ description = i18n( "An attempt to create the requested folder failed." );
+ causes << cAccess << i18n( "The location where the folder was to be created "
+ "may not exist." );
+ if ( !isSlaveNetwork ) causes << cProtocol;
+ solutions << i18n( "Retry the request." ) << sAccess;
+ break;
+
+ case TDEIO::ERR_COULD_NOT_RMDIR:
+ errorName = i18n( "Could Not Remove Folder" );
+ description = i18n( "An attempt to remove the specified folder, "
+ "<strong>%1</strong>, failed." ).arg( dir );
+ causes << i18n( "The specified folder may not exist." )
+ << i18n( "The specified folder may not be empty." )
+ << cAccess;
+ if ( !isSlaveNetwork ) causes << cProtocol;
+ solutions << i18n( "Ensure that the folder exists and is empty, and try "
+ "again." ) << sAccess;
+ break;
+
+ case TDEIO::ERR_CANNOT_RESUME:
+ errorName = i18n( "Could Not Resume File Transfer" );
+ description = i18n( "The specified request asked that the transfer of "
+ "file <strong>%1</strong> be resumed at a certain point of the "
+ "transfer. This was not possible." ).arg( filename );
+ causes << i18n( "The protocol, or the server, may not support file "
+ "resuming." );
+ solutions << i18n( "Retry the request without attempting to resume "
+ "transfer." );
+ break;
+
+ case TDEIO::ERR_CANNOT_RENAME:
+ errorName = i18n( "Could Not Rename Resource" );
+ description = i18n( "An attempt to rename the specified resource "
+ "<strong>%1</strong> failed." ).arg( url );
+ causes << cAccess << cExists;
+ if ( !isSlaveNetwork ) causes << cProtocol;
+ solutions << sAccess << sExists;
+ break;
+
+ case TDEIO::ERR_CANNOT_CHMOD:
+ errorName = i18n( "Could Not Alter Permissions of Resource" );
+ description = i18n( "An attempt to alter the permissions on the specified "
+ "resource <strong>%1</strong> failed." ).arg( url );
+ causes << cAccess << cExists;
+ solutions << sAccess << sExists;
+ break;
+
+ case TDEIO::ERR_CANNOT_DELETE:
+ errorName = i18n( "Could Not Delete Resource" );
+ description = i18n( "An attempt to delete the specified resource "
+ "<strong>%1</strong> failed." ).arg( url );
+ causes << cAccess << cExists;
+ solutions << sAccess << sExists;
+ break;
+
+ case TDEIO::ERR_SLAVE_DIED:
+ errorName = i18n( "Unexpected Program Termination" );
+ description = i18n( "The program on your computer which provides access "
+ "to the <strong>%1</strong> protocol has unexpectedly terminated." )
+ .arg( url );
+ causes << cBuglikely;
+ solutions << sUpdate << sBugreport;
+ break;
+
+ case TDEIO::ERR_OUT_OF_MEMORY:
+ errorName = i18n( "Out of Memory" );
+ description = i18n( "The program on your computer which provides access "
+ "to the <strong>%1</strong> protocol could not obtain the memory "
+ "required to continue." ).arg( protocol );
+ causes << cBuglikely;
+ solutions << sUpdate << sBugreport;
+ break;
+
+ case TDEIO::ERR_UNKNOWN_PROXY_HOST:
+ errorName = i18n( "Unknown Proxy Host" );
+ description = i18n( "While retrieving information about the specified "
+ "proxy host, <strong>%1</strong>, an Unknown Host error was encountered. "
+ "An unknown host error indicates that the requested name could not be "
+ "located on the Internet." ).arg( errorText );
+ causes << i18n( "There may have been a problem with your network "
+ "configuration, specifically your proxy's hostname. If you have been "
+ "accessing the Internet with no problems recently, this is unlikely." )
+ << cNetwork;
+ solutions << i18n( "Double-check your proxy settings and try again." )
+ << sSysadmin;
+ break;
+
+ case TDEIO::ERR_COULD_NOT_AUTHENTICATE:
+ errorName = i18n( "Authentication Failed: Method %1 Not Supported" )
+ .arg( errorText );
+ description = i18n( "Although you may have supplied the correct "
+ "authentication details, the authentication failed because the "
+ "method that the server is using is not supported by the TDE "
+ "program implementing the protocol %1." ).arg( protocol );
+ solutions << i18n( "Please file a bug at <a href=\"http://bugs.kde.org/\">"
+ "http://bugs.pearsoncomputing.net/</a> to inform the TDE team of the unsupported "
+ "authentication method." ) << sSysadmin;
+ break;
+
+ case TDEIO::ERR_ABORTED:
+ errorName = i18n( "Request Aborted" );
+ description = i18n( "The request was not completed because it was "
+ "aborted." );
+ solutions << i18n( "Retry the request." );
+ break;
+
+ case TDEIO::ERR_INTERNAL_SERVER:
+ errorName = i18n( "Internal Error in Server" );
+ description = i18n( "The program on the server which provides access "
+ "to the <strong>%1</strong> protocol has reported an internal error: "
+ "%0." ).arg( protocol );
+ causes << i18n( "This is most likely to be caused by a bug in the "
+ "server program. Please consider submitting a full bug report as "
+ "detailed below." );
+ solutions << i18n( "Contact the administrator of the server "
+ "to advise them of the problem." )
+ << i18n( "If you know who the authors of the server software are, "
+ "submit the bug report directly to them." );
+ break;
+
+ case TDEIO::ERR_SERVER_TIMEOUT:
+ errorName = i18n( "Timeout Error" );
+ description = i18n( "Although contact was made with the server, a "
+ "response was not received within the amount of time allocated for "
+ "the request as follows:<ul>"
+ "<li>Timeout for establishing a connection: %1 seconds</li>"
+ "<li>Timeout for receiving a response: %2 seconds</li>"
+ "<li>Timeout for accessing proxy servers: %3 seconds</li></ul>"
+ "Please note that you can alter these timeout settings in the TDE "
+ "Control Center, by selecting Network -> Preferences." )
+ .arg( KProtocolManager::connectTimeout() )
+ .arg( KProtocolManager::responseTimeout() )
+ .arg( KProtocolManager::proxyConnectTimeout() );
+ causes << cNetpath << i18n( "The server was too busy responding to other "
+ "requests to respond." );
+ solutions << sTryagain << sServeradmin;
+ break;
+
+ case TDEIO::ERR_UNKNOWN:
+ errorName = i18n( "Unknown Error" );
+ description = i18n( "The program on your computer which provides access "
+ "to the <strong>%1</strong> protocol has reported an unknown error: "
+ "%2." ).arg( protocol ).arg( errorText );
+ causes << cBug;
+ solutions << sUpdate << sBugreport;
+ break;
+
+ case TDEIO::ERR_UNKNOWN_INTERRUPT:
+ errorName = i18n( "Unknown Interruption" );
+ description = i18n( "The program on your computer which provides access "
+ "to the <strong>%1</strong> protocol has reported an interruption of "
+ "an unknown type: %2." ).arg( protocol ).arg( errorText );
+ causes << cBug;
+ solutions << sUpdate << sBugreport;
+ break;
+
+ case TDEIO::ERR_CANNOT_DELETE_ORIGINAL:
+ errorName = i18n( "Could Not Delete Original File" );
+ description = i18n( "The requested operation required the deleting of "
+ "the original file, most likely at the end of a file move operation. "
+ "The original file <strong>%1</strong> could not be deleted." )
+ .arg( errorText );
+ causes << cAccess;
+ solutions << sAccess;
+ break;
+
+ case TDEIO::ERR_CANNOT_DELETE_PARTIAL:
+ errorName = i18n( "Could Not Delete Temporary File" );
+ description = i18n( "The requested operation required the creation of "
+ "a temporary file in which to save the new file while being "
+ "downloaded. This temporary file <strong>%1</strong> could not be "
+ "deleted." ).arg( errorText );
+ causes << cAccess;
+ solutions << sAccess;
+ break;
+
+ case TDEIO::ERR_CANNOT_RENAME_ORIGINAL:
+ errorName = i18n( "Could Not Rename Original File" );
+ description = i18n( "The requested operation required the renaming of "
+ "the original file <strong>%1</strong>, however it could not be "
+ "renamed." ).arg( errorText );
+ causes << cAccess;
+ solutions << sAccess;
+ break;
+
+ case TDEIO::ERR_CANNOT_RENAME_PARTIAL:
+ errorName = i18n( "Could Not Rename Temporary File" );
+ description = i18n( "The requested operation required the creation of "
+ "a temporary file <strong>%1</strong>, however it could not be "
+ "created." ).arg( errorText );
+ causes << cAccess;
+ solutions << sAccess;
+ break;
+
+ case TDEIO::ERR_CANNOT_SYMLINK:
+ errorName = i18n( "Could Not Create Link" );
+ techName = i18n( "Could Not Create Symbolic Link" );
+ description = i18n( "The requested symbolic link %1 could not be created." )
+ .arg( errorText );
+ causes << cAccess;
+ solutions << sAccess;
+ break;
+
+ case TDEIO::ERR_NO_CONTENT:
+ errorName = i18n( "No Content" );
+ description = errorText;
+ break;
+
+ case TDEIO::ERR_DISK_FULL:
+ errorName = i18n( "Disk Full" );
+ description = i18n( "The requested file <strong>%1</strong> could not be "
+ "written to as there is inadequate disk space." ).arg( errorText );
+ solutions << i18n( "Free up enough disk space by 1) deleting unwanted and "
+ "temporary files; 2) archiving files to removable media storage such as "
+ "CD-Recordable discs; or 3) obtain more storage capacity." )
+ << sSysadmin;
+ break;
+
+ case TDEIO::ERR_IDENTICAL_FILES:
+ errorName = i18n( "Source and Destination Files Identical" );
+ description = i18n( "The operation could not be completed because the "
+ "source and destination files are the same file." );
+ solutions << i18n( "Choose a different filename for the destination file." );
+ break;
+
+ // We assume that the slave has all the details
+ case TDEIO::ERR_SLAVE_DEFINED:
+ errorName = TQString::null;
+ description = errorText;
+ break;
+
+ default:
+ // fall back to the plain error...
+ errorName = i18n( "Undocumented Error" );
+ description = buildErrorString( errorCode, errorText );
+ }
+
+ TQByteArray ret;
+ TQDataStream stream(ret, IO_WriteOnly);
+ stream << errorName << techName << description << causes << solutions;
+ return ret;
+}
+
+#ifdef Q_OS_UNIX
+
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <tqfile.h>
+
+#include <config.h>
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#include <sys/param.h>
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+#ifdef HAVE_SYS_MNTTAB_H
+#include <sys/mnttab.h>
+#endif
+#ifdef HAVE_MNTENT_H
+#include <mntent.h>
+#elif defined(HAVE_SYS_MNTENT_H)
+#include <sys/mntent.h>
+#endif
+#ifdef HAVE_SYS_UCRED_H
+#include <sys/ucred.h>
+#endif
+#ifdef HAVE_SYS_MOUNT_H
+#include <sys/mount.h>
+#endif
+#ifdef HAVE_FSTAB_H
+#include <fstab.h>
+#endif
+#if defined(_AIX)
+#include <sys/mntctl.h>
+#include <sys/vmount.h>
+#include <sys/vfs.h>
+
+/* AIX does not prototype mntctl anywhere that I can find */
+#ifndef mntctl
+extern "C" {
+int mntctl(int command, int size, void* buffer);
+}
+#endif
+extern "C" struct vfs_ent *getvfsbytype(int vfsType);
+extern "C" void endvfsent( );
+#endif
+
+/***************************************************************
+ *
+ * Utility functions
+ *
+ ***************************************************************/
+
+#ifndef HAVE_GETMNTINFO
+
+#ifdef _PATH_MOUNTED
+// On some Linux, MNTTAB points to /etc/fstab !
+# undef MNTTAB
+# define MNTTAB _PATH_MOUNTED
+#else
+# ifndef MNTTAB
+# ifdef MTAB_FILE
+# define MNTTAB MTAB_FILE
+# else
+# define MNTTAB "/etc/mnttab"
+# endif
+# endif
+#endif
+
+#ifndef FSTAB
+# ifdef _PATH_FSTAB
+# define FSTAB _PATH_FSTAB
+# else
+# define FSTAB "/etc/fstab"
+# endif
+#endif
+
+#ifdef __CYGWIN__
+#define hasmntopt(var,opt) (0)
+#endif
+
+// There are (at least) four kind of APIs:
+// setmntent + getmntent + struct mntent (linux...)
+// getmntent + struct mnttab
+// mntctl + struct vmount (AIX)
+// getmntinfo + struct statfs&flags (BSD 4.4 and friends)
+// getfsent + char* (BSD 4.3 and friends)
+
+#ifdef HAVE_SETMNTENT
+#define SETMNTENT setmntent
+#define ENDMNTENT endmntent
+#define STRUCT_MNTENT struct mntent *
+#define STRUCT_SETMNTENT FILE *
+#define GETMNTENT(file, var) ((var = getmntent(file)) != 0)
+#define MOUNTPOINT(var) var->mnt_dir
+#define MOUNTTYPE(var) var->mnt_type
+#define HASMNTOPT(var, opt) hasmntopt(var, opt)
+#define FSNAME(var) var->mnt_fsname
+#elif defined(_AIX)
+/* we don't need this stuff */
+#else
+#define SETMNTENT fopen
+#define ENDMNTENT fclose
+#define STRUCT_MNTENT struct mnttab
+#define STRUCT_SETMNTENT FILE *
+#define GETMNTENT(file, var) (getmntent(file, &var) == 0)
+#define MOUNTPOINT(var) var.mnt_mountp
+#define MOUNTTYPE(var) var.mnt_fstype
+#define HASMNTOPT(var, opt) hasmntopt(&var, opt)
+#define FSNAME(var) var.mnt_special
+#endif
+
+#endif /* HAVE_GETMNTINFO */
+
+TQString TDEIO::findDeviceMountPoint( const TQString& filename )
+{
+ TQString result;
+
+#ifdef HAVE_VOLMGT
+ /*
+ * support for Solaris volume management
+ */
+ const char *volpath;
+ FILE *mnttab;
+ struct mnttab mnt;
+ int len;
+ TQCString devname;
+
+ if( (volpath = volmgt_root()) == NULL ) {
+ kdDebug( 7007 ) << "findDeviceMountPoint: "
+ << "VOLMGT: can't find volmgt root dir" << endl;
+ return TQString::null;
+ }
+
+ if( (mnttab = fopen( MNTTAB, "r" )) == NULL ) {
+ kdDebug( 7007 ) << "findDeviceMountPoint: "
+ << "VOLMGT: can't open mnttab" << endl;
+ return TQString::null;
+ }
+
+ devname = volpath;
+ devname += TQFile::encodeName( filename );
+ devname += '/';
+ len = devname.length();
+// kdDebug( 7007 ) << "findDeviceMountPoint: "
+// << "VOLMGT: searching mountpoint for \"" << devname << "\""
+// << endl;
+
+ /*
+ * find the mountpoint
+ * floppies:
+ * /dev/disketteN => <volpath>/dev/disketteN
+ * CDROM, ZIP, and other media:
+ * /dev/dsk/cXtYdZs2 => <volpath>/dev/dsk/cXtYdZ (without slice#)
+ */
+ rewind( mnttab );
+ result = TQString::null;
+ while( getmntent( mnttab, &mnt ) == 0 ) {
+ /*
+ * either match the exact device name (floppies),
+ * or the device name without the slice#
+ */
+ if( strncmp( devname.data(), mnt.mnt_special, len ) == 0
+ || (strncmp( devname.data(), mnt.mnt_special, len - 3 ) == 0
+ && mnt.mnt_special[len - 3] == '/' )
+ || (strcmp(TQFile::encodeName(filename).data()
+ , mnt.mnt_special)==0)) {
+ result = mnt.mnt_mountp;
+ break;
+ }
+ }
+ fclose( mnttab );
+#else
+
+ char realpath_buffer[MAXPATHLEN];
+ TQCString realname;
+
+ realname = TQFile::encodeName(filename);
+ /* If the path contains symlinks, get the real name */
+ if (realpath(realname, realpath_buffer) != 0)
+ // succes, use result from realpath
+ realname = realpath_buffer;
+
+ //kdDebug(7007) << "findDeviceMountPoint realname=" << realname << endl;
+
+#ifdef HAVE_GETMNTINFO
+
+#ifdef GETMNTINFO_USES_STATVFS
+ struct statvfs *mounted;
+#else
+ struct statfs *mounted;
+#endif
+
+ int num_fs = getmntinfo(&mounted, MNT_NOWAIT);
+
+ for (int i=0;i<num_fs;i++) {
+
+ TQCString device_name = mounted[i].f_mntfromname;
+
+ // If the path contains symlinks, get
+ // the real name
+ if (realpath(device_name, realpath_buffer) != 0)
+ // succes, use result from realpath
+ device_name = realpath_buffer;
+
+ if (realname == device_name) {
+ result = mounted[i].f_mntonname;
+ break;
+ }
+ }
+
+#elif defined(_AIX)
+
+ struct vmount *mntctl_buffer;
+ struct vmount *vm;
+ char *mountedfrom;
+ char *mountedto;
+ int fsname_len, num;
+ int buf_sz = 4096;
+
+ /* mntctl can be used to query mounted file systems.
+ * mntctl takes only the command MCTL_QUERY so far.
+ * The buffer is filled with an array of vmount structures, but these
+ * vmount structures have variable size.
+ * mntctl return values:
+ * -1 error
+ * 0 look in first word of buffer for required bytes, 4096 may be
+ * a good starting size, but if tables grow too large, look here.
+ * >0 number of vmount structures
+ */
+ mntctl_buffer = (struct vmount*)malloc(buf_sz);
+ num = mntctl(MCTL_QUERY, buf_sz, mntctl_buffer);
+ if (num == 0)
+ {
+ buf_sz = *(int*)mntctl_buffer;
+ free(mntctl_buffer);
+ mntctl_buffer = (struct vmount*)malloc(buf_sz);
+ num = mntctl(MCTL_QUERY, buf_sz, mntctl_buffer);
+ }
+
+ if (num > 0)
+ {
+ /* iterate through items in the vmount structure: */
+ vm = mntctl_buffer;
+ for ( ; num > 0; num-- )
+ {
+ /* get the name of the mounted file systems: */
+ fsname_len = vmt2datasize(vm, VMT_STUB);
+ mountedto = (char*)malloc(fsname_len + 1);
+ mountedto[fsname_len] = '\0';
+ strncpy(mountedto, (char *)vmt2dataptr(vm, VMT_STUB), fsname_len);
+
+ /* get the mount-from information: */
+ fsname_len = vmt2datasize(vm, VMT_OBJECT);
+ mountedfrom = (char*)malloc(fsname_len + 1);
+ mountedfrom[fsname_len] = '\0';
+ strncpy(mountedfrom, (char *)vmt2dataptr(vm, VMT_OBJECT), fsname_len);
+
+ TQCString device_name = mountedfrom;
+
+ if (realpath(device_name, realpath_buffer) != 0)
+ // success, use result from realpath
+ device_name = realpath_buffer;
+
+ free(mountedfrom);
+
+ if (realname == device_name) {
+ result = mountedto;
+ free(mountedto);
+ break;
+ }
+
+ free(mountedto);
+
+ /* goto the next vmount structure: */
+ vm = (struct vmount *)((char *)vm + vm->vmt_length);
+ }
+ }
+
+ free( mntctl_buffer );
+
+#else
+
+ STRUCT_SETMNTENT mtab;
+
+ /* Get the list of mounted file systems */
+
+ if ((mtab = SETMNTENT(MNTTAB, "r")) == 0) {
+ perror("setmntent");
+ return TQString::null;
+ }
+
+ /* Loop over all file systems and see if we can find our
+ * mount point.
+ * Note that this is the mount point with the longest match.
+ * XXX: Fails if me->mnt_dir is not a realpath but goes
+ * through a symlink, e.g. /foo/bar where /foo is a symlink
+ * pointing to /local/foo.
+ *
+ * How kinky can you get with a filesystem?
+ */
+
+ STRUCT_MNTENT me;
+
+ while (GETMNTENT(mtab, me))
+ {
+ // There may be symbolic links into the /etc/mnttab
+ // So we have to find the real device name here as well!
+ TQCString device_name = FSNAME(me);
+ if (device_name.isEmpty() || (device_name == "none"))
+ continue;
+
+ //kdDebug( 7007 ) << "device_name=" << device_name << endl;
+
+ // If the path contains symlinks, get
+ // the real name
+ if (realpath(device_name, realpath_buffer) != 0)
+ // succes, use result from realpath
+ device_name = realpath_buffer;
+
+ //kdDebug( 7007 ) << "device_name after realpath =" << device_name << endl;
+
+ if (realname == device_name)
+ {
+ result = MOUNTPOINT(me);
+ break;
+ }
+ }
+
+ ENDMNTENT(mtab);
+
+#endif /* GET_MNTINFO */
+#endif /* HAVE_VOLMGT */
+
+ //kdDebug( 7007 ) << "Returning result " << result << endl;
+ return result;
+}
+
+// Don't just trust the return value, keep iterating to check for a better match (bigger max)
+static bool is_my_mountpoint( const char *mountpoint, const char *realname, int &max )
+{
+ int length = strlen(mountpoint);
+
+ if (!strncmp(mountpoint, realname, length)
+ && length > max) {
+ max = length;
+ if (length == 1 || realname[length] == '/' || realname[length] == '\0')
+ return true;
+ }
+ return false;
+}
+
+typedef enum { Unseen, Right, Wrong } MountState;
+
+/**
+ * Idea and code base by Olaf Kirch <okir@caldera.de>
+ **/
+static void check_mount_point(const char *mounttype,
+ const char *fsname,
+ MountState &isslow, MountState &isautofs)
+{
+ bool nfs = !strcmp(mounttype, "nfs");
+ bool autofs = !strcmp(mounttype, "autofs") || !strcmp(mounttype,"subfs");
+ bool pid = (strstr(fsname, ":(pid") != 0);
+
+ if (nfs && !pid)
+ isslow = Right;
+ else if (isslow == Right)
+ isslow = Wrong;
+
+ /* Does this look like automounted? */
+ if (autofs || (nfs && pid)) {
+ isautofs = Right;
+ isslow = Right;
+ }
+}
+
+// returns the mount point, checks the mount state.
+// if ismanual == Wrong this function does not check the manual mount state
+static TQString get_mount_info(const TQString& filename,
+ MountState& isautofs, MountState& isslow, MountState& ismanual,
+ TQString& fstype)
+{
+ static bool gotRoot = false;
+ static dev_t rootDevice;
+
+ struct cachedDevice_t
+ {
+ dev_t device;
+ TQString mountPoint;
+ MountState isautofs;
+ MountState isslow;
+ MountState ismanual;
+ TQString fstype;
+ };
+ static struct cachedDevice_t *cachedDevice = 0;
+
+ if (!gotRoot)
+ {
+ KDE_struct_stat stat_buf;
+ KDE_stat("/", &stat_buf);
+ gotRoot = true;
+ rootDevice = stat_buf.st_dev;
+ }
+
+ bool gotDevice = false;
+ KDE_struct_stat stat_buf;
+ if (KDE_stat(TQFile::encodeName(filename), &stat_buf) == 0)
+ {
+ gotDevice = true;
+ if (stat_buf.st_dev == rootDevice)
+ {
+ static const TQString &root = TDEGlobal::staticQString("/");
+ isautofs = Wrong;
+ isslow = Wrong;
+ ismanual = Wrong;
+ fstype = TQString::null; // ### do we need it?
+ return root;
+ }
+ if (cachedDevice && (stat_buf.st_dev == cachedDevice->device))
+ {
+ bool interestedInIsManual = ismanual != Wrong;
+ isautofs = cachedDevice->isautofs;
+ isslow = cachedDevice->isslow;
+ ismanual = cachedDevice->ismanual;
+ fstype = cachedDevice->fstype;
+ // Don't use the cache if it doesn't have the information we're looking for
+ if ( !interestedInIsManual || ismanual != Unseen )
+ return cachedDevice->mountPoint;
+ }
+ }
+
+ char realname[MAXPATHLEN];
+
+ memset(realname, 0, MAXPATHLEN);
+
+ /* If the path contains symlinks, get the real name */
+ if (realpath(TQFile::encodeName(filename), realname) == 0) {
+ if( strlcpy(realname, TQFile::encodeName(filename), MAXPATHLEN)>=MAXPATHLEN)
+ return TQString::null;
+ }
+
+ int max = 0;
+ TQString mountPoint;
+
+ /* Loop over all file systems and see if we can find our
+ * mount point.
+ * Note that this is the mount point with the longest match.
+ * XXX: Fails if me->mnt_dir is not a realpath but goes
+ * through a symlink, e.g. /foo/bar where /foo is a symlink
+ * pointing to /local/foo.
+ *
+ * How kinky can you get with a filesystem?
+ */
+
+#ifdef HAVE_GETMNTINFO
+
+#ifdef GETMNTINFO_USES_STATVFS
+ struct statvfs *mounted;
+#else
+ struct statfs *mounted;
+#endif
+
+ char realpath_buffer[MAXPATHLEN];
+
+ int num_fs = getmntinfo(&mounted, MNT_NOWAIT);
+
+ for (int i=0;i<num_fs;i++) {
+
+ TQCString device_name = mounted[i].f_mntfromname;
+
+ // If the path contains symlinks, get
+ // the real name
+ if (realpath(device_name, realpath_buffer) != 0)
+ // succes, use result from realpath
+ device_name = realpath_buffer;
+#ifdef __osf__
+ char * mounttype = mnt_names[mounted[i].f_type];
+#else
+ char * mounttype = mounted[i].f_fstypename;
+#endif
+ if ( is_my_mountpoint( mounted[i].f_mntonname, realname, max ) )
+ {
+ mountPoint = TQFile::decodeName(mounted[i].f_mntonname);
+ fstype = TQString::fromLatin1(mounttype);
+ check_mount_point( mounttype, mounted[i].f_mntfromname,
+ isautofs, isslow );
+ // keep going, looking for a potentially better one
+
+ if (ismanual == Unseen)
+ {
+ struct fstab *ft = getfsfile(mounted[i].f_mntonname);
+ if (!ft || strstr(ft->fs_mntops, "noauto"))
+ ismanual = Right;
+ }
+ }
+ }
+
+#elif defined(_AIX)
+
+ struct vmount *mntctl_buffer;
+ struct vmount *vm;
+ char *mountedfrom;
+ char *mountedto;
+ int fsname_len, num;
+ char realpath_buffer[MAXPATHLEN];
+ int buf_sz = 4096;
+
+ mntctl_buffer = (struct vmount*)malloc(buf_sz);
+ num = mntctl(MCTL_QUERY, buf_sz, mntctl_buffer);
+ if (num == 0)
+ {
+ buf_sz = *(int*)mntctl_buffer;
+ free(mntctl_buffer);
+ mntctl_buffer = (struct vmount*)malloc(buf_sz);
+ num = mntctl(MCTL_QUERY, buf_sz, mntctl_buffer);
+ }
+
+ if (num > 0)
+ {
+ /* iterate through items in the vmount structure: */
+ vm = (struct vmount *)mntctl_buffer;
+ for ( ; num > 0; num-- )
+ {
+ /* get the name of the mounted file systems: */
+ fsname_len = vmt2datasize(vm, VMT_STUB);
+ mountedto = (char*)malloc(fsname_len + 1);
+ mountedto[fsname_len] = '\0';
+ strncpy(mountedto, (char *)vmt2dataptr(vm, VMT_STUB), fsname_len);
+
+ fsname_len = vmt2datasize(vm, VMT_OBJECT);
+ mountedfrom = (char*)malloc(fsname_len + 1);
+ mountedfrom[fsname_len] = '\0';
+ strncpy(mountedfrom, (char *)vmt2dataptr(vm, VMT_OBJECT), fsname_len);
+
+ /* get the mount-from information: */
+ TQCString device_name = mountedfrom;
+
+ if (realpath(device_name, realpath_buffer) != 0)
+ // success, use result from realpath
+ device_name = realpath_buffer;
+
+ /* Look up the string for the file system type,
+ * as listed in /etc/vfs.
+ * ex.: nfs,jfs,afs,cdrfs,sfs,cachefs,nfs3,autofs
+ */
+ struct vfs_ent* ent = getvfsbytype(vm->vmt_gfstype);
+
+ if ( is_my_mountpoint( mountedto, realname, max ) )
+ {
+ mountPoint = TQFile::decodeName(mountedto);
+ fstype = TQString::fromLatin1(ent->vfsent_name);
+ check_mount_point(ent->vfsent_name, device_name, isautofs, isslow);
+
+ if (ismanual == Unseen)
+ {
+ // TODO: add check for ismanual, I couldn't find any way
+ // how to get the mount attribute from /etc/filesystems
+ ismanual == Wrong;
+ }
+ }
+
+ free(mountedfrom);
+ free(mountedto);
+
+ /* goto the next vmount structure: */
+ vm = (struct vmount *)((char *)vm + vm->vmt_length);
+ }
+
+ endvfsent( );
+ }
+
+ free( mntctl_buffer );
+
+#else
+
+ STRUCT_SETMNTENT mtab;
+ /* Get the list of mounted file systems */
+
+ if ((mtab = SETMNTENT(MNTTAB, "r")) == 0) {
+ perror("setmntent");
+ return TQString::null;
+ }
+
+ STRUCT_MNTENT me;
+
+ while (true) {
+ if (!GETMNTENT(mtab, me))
+ break;
+
+ if ( is_my_mountpoint( MOUNTPOINT(me), realname, max ) )
+ {
+ mountPoint = TQFile::decodeName( MOUNTPOINT(me) );
+ fstype = MOUNTTYPE(me);
+ check_mount_point(MOUNTTYPE(me), FSNAME(me), isautofs, isslow);
+ // we don't check if ismanual is Right, if /a/b is manually
+ // mounted /a/b/c can't be automounted. At least IMO.
+ if (ismanual == Unseen)
+ {
+ // The next GETMNTENT call may destroy 'me'
+ // Copy out the info that we need
+ TQCString fsname_me = FSNAME(me);
+ TQCString mounttype_me = MOUNTTYPE(me);
+
+ STRUCT_SETMNTENT fstab;
+ if ((fstab = SETMNTENT(FSTAB, "r")) == 0) {
+ continue;
+ }
+
+ bool found = false;
+ STRUCT_MNTENT fe;
+ while (GETMNTENT(fstab, fe))
+ {
+ if (fsname_me == FSNAME(fe))
+ {
+ found = true;
+ if (HASMNTOPT(fe, "noauto") ||
+ !strcmp(MOUNTTYPE(fe), "supermount"))
+ ismanual = Right;
+ break;
+ }
+ }
+ if (!found || (mounttype_me == "supermount"))
+ ismanual = Right;
+
+ ENDMNTENT(fstab);
+ }
+ }
+ }
+
+ ENDMNTENT(mtab);
+
+#endif
+
+ if (isautofs == Right && isslow == Unseen)
+ isslow = Right;
+
+ if (gotDevice)
+ {
+ if (!cachedDevice)
+ cachedDevice = new cachedDevice_t;
+
+ cachedDevice->device = stat_buf.st_dev;
+ cachedDevice->mountPoint = mountPoint;
+ cachedDevice->isautofs = isautofs;
+ cachedDevice->isslow = isslow;
+ cachedDevice->ismanual = ismanual;
+ cachedDevice->fstype = fstype;
+ }
+
+ return mountPoint;
+}
+
+#else //!Q_OS_UNIX
+//dummy
+TQString TDEIO::findDeviceMountPoint( const TQString& filename )
+{
+ return TQString::null;
+}
+#endif
+
+TQString TDEIO::findPathMountPoint(const TQString& filename)
+{
+#ifdef Q_OS_UNIX
+ MountState isautofs = Unseen, isslow = Unseen, ismanual = Wrong;
+ TQString fstype;
+ return get_mount_info(filename, isautofs, isslow, ismanual, fstype);
+#else //!Q_OS_UNIX
+ return TQString::null;
+#endif
+}
+
+bool TDEIO::manually_mounted(const TQString& filename)
+{
+#ifdef Q_OS_UNIX
+ MountState isautofs = Unseen, isslow = Unseen, ismanual = Unseen;
+ TQString fstype;
+ TQString mountPoint = get_mount_info(filename, isautofs, isslow, ismanual, fstype);
+ return !mountPoint.isNull() && (ismanual == Right);
+#else //!Q_OS_UNIX
+ return false;
+#endif
+}
+
+bool TDEIO::probably_slow_mounted(const TQString& filename)
+{
+#ifdef Q_OS_UNIX
+ MountState isautofs = Unseen, isslow = Unseen, ismanual = Wrong;
+ TQString fstype;
+ TQString mountPoint = get_mount_info(filename, isautofs, isslow, ismanual, fstype);
+ return !mountPoint.isNull() && (isslow == Right);
+#else //!Q_OS_UNIX
+ return false;
+#endif
+}
+
+bool TDEIO::testFileSystemFlag(const TQString& filename, FileSystemFlag flag)
+{
+#ifdef Q_OS_UNIX
+ MountState isautofs = Unseen, isslow = Unseen, ismanual = Wrong;
+ TQString fstype;
+ TQString mountPoint = get_mount_info(filename, isautofs, isslow, ismanual, fstype);
+ kdDebug() << "testFileSystemFlag: fstype=" << fstype << endl;
+ if (mountPoint.isNull())
+ return false;
+ bool isMsDos = ( fstype == "msdos" || fstype == "fat" || fstype == "vfat" );
+ switch (flag) {
+ case SupportsChmod:
+ case SupportsChown:
+ case SupportsUTime:
+ case SupportsSymlinks:
+ return !isMsDos; // it's amazing the number of things FAT doesn't support :)
+ case CaseInsensitive:
+ return isMsDos;
+ }
+#endif
+ return false;
+}
+
+TDEIO::CacheControl TDEIO::parseCacheControl(const TQString &cacheControl)
+{
+ TQString tmp = cacheControl.lower();
+
+ if (tmp == "cacheonly")
+ return TDEIO::CC_CacheOnly;
+ if (tmp == "cache")
+ return TDEIO::CC_Cache;
+ if (tmp == "verify")
+ return TDEIO::CC_Verify;
+ if (tmp == "refresh")
+ return TDEIO::CC_Refresh;
+ if (tmp == "reload")
+ return TDEIO::CC_Reload;
+
+ kdDebug() << "unrecognized Cache control option:"<<cacheControl<<endl;
+ return TDEIO::CC_Verify;
+}
+
+TQString TDEIO::getCacheControlString(TDEIO::CacheControl cacheControl)
+{
+ if (cacheControl == TDEIO::CC_CacheOnly)
+ return "CacheOnly";
+ if (cacheControl == TDEIO::CC_Cache)
+ return "Cache";
+ if (cacheControl == TDEIO::CC_Verify)
+ return "Verify";
+ if (cacheControl == TDEIO::CC_Refresh)
+ return "Refresh";
+ if (cacheControl == TDEIO::CC_Reload)
+ return "Reload";
+ kdDebug() << "unrecognized Cache control enum value:"<<cacheControl<<endl;
+ return TQString::null;
+}
diff --git a/tdeio/tdeio/global.h b/tdeio/tdeio/global.h
new file mode 100644
index 000000000..44b9df616
--- /dev/null
+++ b/tdeio/tdeio/global.h
@@ -0,0 +1,547 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __kio_global_h__
+#define __kio_global_h__
+
+#include <tqstring.h>
+#include <tqvaluelist.h>
+#include <tqptrlist.h>
+#include <tqdatastream.h>
+#include <tqdatetime.h>
+#include <tqmap.h>
+
+#include <kurl.h>
+
+/**
+ * @short A namespace for KIO globals
+ *
+ */
+namespace TDEIO
+{
+ /// 64-bit file offset
+ typedef TQ_LLONG fileoffset_t;
+ /// 64-bit file size
+ typedef TQ_ULLONG filesize_t;
+
+ /**
+ * Converts @p size from bytes to the string representation.
+ *
+ * @param size size in bytes
+ * @return converted size as a string - e.g. 123.4 kB , 12.0 MB
+ */
+ TDEIO_EXPORT TQString convertSize( TDEIO::filesize_t size );
+
+ /**
+ * Converts @p size from bytes to a string representation with includes
+ * the size in bytes.
+ * e.g. 90 B, 240 B, 1.4 KB (1495 B), 2.6MB (2,734,344 B), 0 B
+ * @param size size in bytes
+ * @return converted size as a string - e.g. 1.4 KB (1495 B), 45 B
+ */
+ TDEIO_EXPORT TQString convertSizeWithBytes( TDEIO::filesize_t size );
+ /**
+ * Converts a size to a string representation
+ * Not unlike TQString::number(...)
+ *
+ * @param size size in bytes
+ * @return converted size as a string - e.g. 123456789
+ */
+ TDEIO_EXPORT TQString number( TDEIO::filesize_t size );
+
+ /**
+ * Converts size from kilo-bytes to the string representation.
+ *
+ * @param kbSize size in kilo-bytes
+ * @return converted size as a string - e.g. 123.4 kB , 12.0 MB
+ */
+ TDEIO_EXPORT TQString convertSizeFromKB( TDEIO::filesize_t kbSize );
+
+ /**
+ * Calculates remaining time in seconds from total size, processed size and speed.
+ *
+ * @param totalSize total size in bytes
+ * @param processedSize processed size in bytes
+ * @param speed speed in bytes per second
+ * @return calculated remaining time in seconds
+ *
+ * @since 3.4
+ */
+ TDEIO_EXPORT unsigned int calculateRemainingSeconds( TDEIO::filesize_t totalSize,
+ TDEIO::filesize_t processedSize, TDEIO::filesize_t speed );
+
+ /**
+ * Convert @p seconds to a string representing number of days, hours, minutes and seconds
+ *
+ * @param seconds number of seconds to convert
+ * @return string representation in a locale depending format
+ *
+ * @since 3.4
+ */
+ TDEIO_EXPORT TQString convertSeconds( unsigned int seconds );
+
+ /**
+ * Calculates remaining time from total size, processed size and speed.
+ * Warning: As TQTime is limited to 23:59:59, use calculateRemainingSeconds() instead
+ *
+ * @param totalSize total size in bytes
+ * @param processedSize processed size in bytes
+ * @param speed speed in bytes per second
+ * @return calculated remaining time
+ */
+ TDEIO_EXPORT TQTime calculateRemaining( TDEIO::filesize_t totalSize, TDEIO::filesize_t processedSize, TDEIO::filesize_t speed ) KDE_DEPRECATED;
+
+ /**
+ * Helper for showing information about a set of files and directories
+ * @param items the number of items (= @p files + @p dirs + number of symlinks :)
+ * @param files the number of files
+ * @param dirs the number of dirs
+ * @param size the sum of the size of the @p files
+ * @param showSize whether to show the size in the result
+ * @return the summary string
+ */
+ TDEIO_EXPORT TQString itemsSummaryString(uint items, uint files, uint dirs, TDEIO::filesize_t size, bool showSize);
+
+ /**
+ * Encodes (from the text displayed to the real filename)
+ * This translates % into %% and / into %2f
+ * Used by TDEIO::link, for instance.
+ * @param str the file name to encode
+ * @return the encoded file name
+ */
+ TDEIO_EXPORT TQString encodeFileName( const TQString & str );
+ /**
+ * Decodes (from the filename to the text displayed)
+ * This translates %2[fF] into / and %% into %
+ * @param str the file name to decode
+ * @return the decoded file name
+ */
+ TDEIO_EXPORT TQString decodeFileName( const TQString & str );
+
+ /**
+ * Commands that can be invoked by a job.
+ */
+ enum Command {
+ CMD_HOST = '0', // 48
+ CMD_CONNECT = '1', // 49
+ CMD_DISCONNECT = '2', // 50
+ CMD_SLAVE_STATUS = '3', // 51
+ CMD_SLAVE_CONNECT = '4', // 52
+ CMD_SLAVE_HOLD = '5', // 53
+ CMD_NONE = 'A', // 65
+ CMD_TESTDIR = 'B', // 66
+ CMD_GET = 'C', // 67
+ CMD_PUT = 'D', // 68
+ CMD_STAT = 'E', // 69
+ CMD_MIMETYPE = 'F', // 70
+ CMD_LISTDIR = 'G', // 71
+ CMD_MKDIR = 'H', // 72
+ CMD_RENAME = 'I', // 73
+ CMD_COPY = 'J', // 74
+ CMD_DEL = 'K', // 75
+ CMD_CHMOD = 'L', // 76
+ CMD_SPECIAL = 'M', // 77
+ CMD_USERPASS = 'N', // 78
+ CMD_REPARSECONFIGURATION = 'O', // 79
+ CMD_META_DATA = 'P', // 80
+ CMD_SYMLINK = 'Q', // 81
+ CMD_SUBURL = 'R', // 82 Inform the slave about the url it is streaming on.
+ CMD_MESSAGEBOXANSWER = 'S', // 83
+ CMD_RESUMEANSWER = 'T', // 84
+ CMD_CONFIG = 'U', // 85
+ CMD_MULTI_GET = 'V', // 86
+ CMD_LOCALURL = 'W' // 87
+ // Add new ones here once a release is done, to avoid breaking binary compatibility.
+ // Note that protocol-specific commands shouldn't be added here, but should use special.
+ };
+
+ /**
+ * Error codes that can be emitted by KIO.
+ */
+ enum Error {
+ ERR_CANNOT_OPEN_FOR_READING = 1,
+ ERR_CANNOT_OPEN_FOR_WRITING = 2,
+ ERR_CANNOT_LAUNCH_PROCESS = 3,
+ ERR_INTERNAL = 4,
+ ERR_MALFORMED_URL = 5,
+ ERR_UNSUPPORTED_PROTOCOL = 6,
+ ERR_NO_SOURCE_PROTOCOL = 7,
+ ERR_UNSUPPORTED_ACTION = 8,
+ ERR_IS_DIRECTORY = 9, // ... where a file was expected
+ ERR_IS_FILE = 10, // ... where a directory was expected (e.g. listing)
+ ERR_DOES_NOT_EXIST = 11,
+ ERR_FILE_ALREADY_EXIST = 12,
+ ERR_DIR_ALREADY_EXIST = 13,
+ ERR_UNKNOWN_HOST = 14,
+ ERR_ACCESS_DENIED = 15,
+ ERR_WRITE_ACCESS_DENIED = 16,
+ ERR_CANNOT_ENTER_DIRECTORY = 17,
+ ERR_PROTOCOL_IS_NOT_A_FILESYSTEM = 18,
+ ERR_CYCLIC_LINK = 19,
+ ERR_USER_CANCELED = 20,
+ ERR_CYCLIC_COPY = 21,
+ ERR_COULD_NOT_CREATE_SOCKET = 22, // KDE4: s/COULD_NOT/CANNOT/ or the other way round
+ ERR_COULD_NOT_CONNECT = 23,
+ ERR_CONNECTION_BROKEN = 24,
+ ERR_NOT_FILTER_PROTOCOL = 25,
+ ERR_COULD_NOT_MOUNT = 26,
+ ERR_COULD_NOT_UNMOUNT = 27,
+ ERR_COULD_NOT_READ = 28,
+ ERR_COULD_NOT_WRITE = 29,
+ ERR_COULD_NOT_BIND = 30,
+ ERR_COULD_NOT_LISTEN = 31,
+ ERR_COULD_NOT_ACCEPT = 32,
+ ERR_COULD_NOT_LOGIN = 33,
+ ERR_COULD_NOT_STAT = 34,
+ ERR_COULD_NOT_CLOSEDIR = 35,
+ ERR_COULD_NOT_MKDIR = 37,
+ ERR_COULD_NOT_RMDIR = 38,
+ ERR_CANNOT_RESUME = 39,
+ ERR_CANNOT_RENAME = 40,
+ ERR_CANNOT_CHMOD = 41,
+ ERR_CANNOT_DELETE = 42,
+ // The text argument is the protocol that the dead slave supported.
+ // This means for example: file, ftp, http, ...
+ ERR_SLAVE_DIED = 43,
+ ERR_OUT_OF_MEMORY = 44,
+ ERR_UNKNOWN_PROXY_HOST = 45,
+ ERR_COULD_NOT_AUTHENTICATE = 46,
+ ERR_ABORTED = 47, // Action got aborted from application side
+ ERR_INTERNAL_SERVER = 48,
+ ERR_SERVER_TIMEOUT = 49,
+ ERR_SERVICE_NOT_AVAILABLE = 50,
+ ERR_UNKNOWN = 51,
+ // (was a warning) ERR_CHECKSUM_MISMATCH = 52,
+ ERR_UNKNOWN_INTERRUPT = 53,
+ ERR_CANNOT_DELETE_ORIGINAL = 54,
+ ERR_CANNOT_DELETE_PARTIAL = 55,
+ ERR_CANNOT_RENAME_ORIGINAL = 56,
+ ERR_CANNOT_RENAME_PARTIAL = 57,
+ ERR_NEED_PASSWD = 58,
+ ERR_CANNOT_SYMLINK = 59,
+ ERR_NO_CONTENT = 60, // Action succeeded but no content will follow.
+ ERR_DISK_FULL = 61,
+ ERR_IDENTICAL_FILES = 62, // src==dest when moving/copying
+ ERR_SLAVE_DEFINED = 63, // for slave specified errors that can be
+ // rich text. Email links will be handled
+ // by the standard email app and all hrefs
+ // will be handled by the standard browser.
+ // <a href="exec:/khelpcenter ?" will be
+ // forked.
+ ERR_UPGRADE_REQUIRED = 64, // A transport upgrade is required to access this
+ // object. For instance, TLS is demanded by
+ // the server in order to continue.
+ ERR_POST_DENIED = 65, // Issued when trying to POST data to a certain Ports
+ // see job.cpp
+ ERR_OFFLINE_MODE = 66 // Used when an app is in offline mode and a
+ // requested document is unavailable.
+ };
+
+ /**
+ * Returns a translated error message for @p errorCode using the
+ * additional error information provided by @p errorText.
+ * @param errorCode the error code
+ * @param errorText the additional error text
+ * @return the created error string
+ */
+ TDEIO_EXPORT TQString buildErrorString(int errorCode, const TQString &errorText);
+
+ /**
+ * Returns a translated html error message for @p errorCode using the
+ * additional error information provided by @p errorText , @p reqUrl
+ * (the request URL), and the ioslave @p method .
+ * @param errorCode the error code
+ * @param errorText the additional error text
+ * @param reqUrl the request URL
+ * @param method the ioslave method
+ * @return the created error string
+ */
+ TDEIO_EXPORT TQString buildHTMLErrorString(int errorCode, const TQString &errorText,
+ const KURL *reqUrl = 0L, int method = -1 );
+
+ /**
+ * Returns translated error details for @p errorCode using the
+ * additional error information provided by @p errorText , @p reqUrl
+ * (the request URL), and the ioslave @p method .
+ *
+ * @param errorCode the error code
+ * @param errorText the additional error text
+ * @param reqUrl the request URL
+ * @param method the ioslave method
+ * @return the following data:
+ * @li TQString errorName - the name of the error
+ * @li TQString techName - if not null, the more technical name of the error
+ * @li TQString description - a description of the error
+ * @li TQStringList causes - a list of possible causes of the error
+ * @li TQStringList solutions - a liso of solutions for the error
+ */
+ TDEIO_EXPORT TQByteArray rawErrorDetail(int errorCode, const TQString &errorText,
+ const KURL *reqUrl = 0L, int method = -1 );
+
+ /**
+ * Returns an appropriate error message if the given command @p cmd
+ * is an unsupported action (ERR_UNSUPPORTED_ACTION).
+ * @param protocol name of the protocol
+ * @param cmd given command
+ * @see enum Command
+ * @since 3.2
+ */
+ TDEIO_EXPORT TQString unsupportedActionErrorString(const TQString &protocol, int cmd);
+
+ /**
+ * Constants used to specify the type of a KUDSAtom.
+ */
+ enum UDSAtomTypes {
+ /// First let's define the item types
+ UDS_STRING = 1,
+ UDS_LONG = 2,
+ UDS_TIME = 4 | UDS_LONG,
+
+ // To add new UDS entries below, you can use a step of 8
+ // (i.e. 8, 16, 24, 32, etc.) Only the last 3 bits are a bitfield,
+ // the rest isn't.
+
+ /// Size of the file
+ UDS_SIZE = 8 | UDS_LONG,
+ UDS_SIZE_LARGE = 32768 | UDS_LONG, // For internal use only
+ /// User ID of the file owner
+ UDS_USER = 16 | UDS_STRING,
+ /// Name of the icon, that should be used for displaying.
+ /// It overrides all other detection mechanisms
+ /// @since 3.2
+ UDS_ICON_NAME = 24 | UDS_STRING,
+ /// Group ID of the file owner
+ UDS_GROUP = 32 | UDS_STRING,
+ /// Extra data (used only if you specified Columns/ColumnsTypes)
+ /// This is the only UDS entry that can be repeated.
+ /// @since 3.2
+ UDS_EXTRA = 48 | UDS_STRING,
+ /// Filename - as displayed in directory listings etc.
+ /// "." has the usual special meaning of "current directory"
+ UDS_NAME = 64 | UDS_STRING,
+ /// A local file path if the ioslave display files sitting
+ /// on the local filesystem (but in another hierarchy, e.g. media:/)
+ UDS_LOCAL_PATH = 72 | UDS_STRING,
+ /// Treat the file as a hidden file or as a normal file,
+ /// regardless of (the absence of) a leading dot in the filename.
+ UDS_HIDDEN = 80 | UDS_LONG,
+ /// Indicates that the entry has extended ACL entries
+ /// @since 3.5
+ UDS_EXTENDED_ACL = 88 | UDS_LONG,
+ /// The access control list serialized into a single string.
+ /// @since 3.5
+ UDS_ACL_STRING = 96 | UDS_STRING,
+ /// The default access control list serialized into a single string.
+ /// Only available for directories.
+ /// @since 3.5
+ UDS_DEFAULT_ACL_STRING = 104 | UDS_STRING,
+
+ // available: 112, 120
+
+ /// Access permissions (part of the mode returned by stat)
+ UDS_ACCESS = 128 | UDS_LONG,
+ /// The last time the file was modified
+ UDS_MODIFICATION_TIME = 256 | UDS_TIME,
+ /// The last time the file was opened
+ UDS_ACCESS_TIME = 512 | UDS_TIME,
+ /// The time the file was created
+ UDS_CREATION_TIME = 1024 | UDS_TIME,
+ /// File type, part of the mode returned by stat
+ /// (for a link, this returns the file type of the pointed item)
+ /// check UDS_LINK_DEST to know if this is a link
+ UDS_FILE_TYPE = 2048 | UDS_LONG,
+ /// Name of the file where the link points to
+ /// Allows to check for a symlink (don't use S_ISLNK !)
+ UDS_LINK_DEST = 4096 | UDS_STRING,
+ /// An alternative URL (If different from the caption)
+ UDS_URL = 8192 | UDS_STRING,
+ /// A mime type; prevents guessing
+ UDS_MIME_TYPE = 16384 | UDS_STRING,
+ /// A mime type to be used for displaying only.
+ /// But when 'running' the file, the mimetype is re-determined
+ UDS_GUESSED_MIME_TYPE = 16392 | UDS_STRING,
+ /// XML properties, e.g. for WebDAV
+ /// @since 3.1
+ UDS_XML_PROPERTIES = 0x8000 | UDS_STRING
+ };
+
+ /**
+ * Specifies how to use the cache.
+ * @see parseCacheControl()
+ * @see getCacheControlString()
+ */
+ enum CacheControl
+ {
+ CC_CacheOnly, ///< Fail request if not in cache
+ CC_Cache, ///< Use cached entry if available
+ CC_Verify, ///< Validate cached entry with remote site if expired
+ CC_Refresh, ///< Always validate cached entry with remote site
+ ///< @since 3.1
+ CC_Reload ///< Always fetch from remote site.
+ };
+
+ /**
+ * Parses the string representation of the cache control option.
+ *
+ * @param cacheControl the string representation
+ * @return the cache control value
+ * @see getCacheControlString()
+ */
+ TDEIO_EXPORT TDEIO::CacheControl parseCacheControl(const TQString &cacheControl);
+
+ /**
+ * Returns a string representation of the given cache control method.
+ *
+ * @param cacheControl the cache control method
+ * @return the string representation
+ * @see parseCacheControl()
+ */
+ TDEIO_EXPORT TQString getCacheControlString(TDEIO::CacheControl cacheControl);
+
+ /**
+ * Returns the mount point where @p device is mounted
+ * right now. This means, it has to be mounted, not just
+ * defined in fstab.
+ */
+ TDEIO_EXPORT TQString findDeviceMountPoint( const TQString& device );
+
+ /**
+ * Returns the mount point on which resides @p filename.
+ * For instance if /home is a separate partition, findPathMountPoint("/home/user/blah")
+ * will return /home
+ * @param filename the file name to check
+ * @return the mount point of the given @p filename
+ */
+ TDEIO_EXPORT TQString findPathMountPoint( const TQString & filename );
+
+ /**
+ * Checks if the path belongs to a filesystem that is probably
+ * slow. It checks for NFS or for paths belonging to automounted
+ * paths not yet mounted
+ * @param filename the file name to check
+ * @return true if the filesystem is probably slow
+ */
+ TDEIO_EXPORT bool probably_slow_mounted(const TQString& filename);
+
+ /**
+ * Checks if the path belongs to a filesystem that is manually
+ * mounted.
+ * @param filename the file name to check
+ * @return true if the filesystem is manually mounted
+ */
+ TDEIO_EXPORT bool manually_mounted(const TQString& filename);
+
+ enum FileSystemFlag { SupportsChmod, SupportsChown, SupportsUTime,
+ SupportsSymlinks, CaseInsensitive };
+ /**
+ * Checks the capabilities of the filesystem to which a given file belongs.
+ * given feature (e.g. chmod).
+ * @param filename the file name to check
+ * @param flag the flag to check
+ * @return true if the filesystem has that flag, false if not (or some error occurred)
+ *
+ * The availables flags are:
+ * @li SupportsChmod: returns true if the filesystem supports chmod
+ * (e.g. msdos filesystems return false)
+ * @li SupportsChown: returns true if the filesystem supports chown
+ * (e.g. msdos filesystems return false)
+ * @li SupportsUtime: returns true if the filesystems supports utime
+ * (e.g. msdos filesystems return false)
+ * @li SupportsSymlinks: returns true if the filesystems supports symlinks
+ * (e.g. msdos filesystems return false)
+ * @li CaseInsensitive: returns true if the filesystem treats
+ * "foo" and "FOO" as being the same file (true for msdos systems)
+ *
+ */
+ TDEIO_EXPORT bool testFileSystemFlag(const TQString& filename, FileSystemFlag flag);
+
+
+/************
+ *
+ * Universal Directory Service
+ *
+ * Any file or URL can be represented by the UDSEntry type below
+ * A UDSEntry is a list of atoms
+ * Each atom contains a specific bit of information for the file
+ *
+ * The following UDS constants represent the different possible values
+ * for m_uds in the UDS atom structure below
+ *
+ * Each atom contains a specific bit of information for the file
+ */
+class TDEIO_EXPORT UDSAtom
+{
+public:
+ /**
+ * Whether 'm_str' or 'm_long' is used depends on the value of 'm_uds'.
+ */
+ TQString m_str;
+ /**
+ * Whether 'm_str' or 'm_long' is used depends on the value of 'm_uds'.
+ */
+ long long m_long;
+
+ /**
+ * Holds one of the UDS_XXX constants
+ */
+ unsigned int m_uds;
+};
+
+/**
+ * An entry is the list of atoms containing all the informations for a file or URL
+ */
+typedef TQValueList<UDSAtom> UDSEntry;
+typedef TQValueList<UDSEntry> UDSEntryList;
+typedef TQValueListIterator<UDSEntry> UDSEntryListIterator;
+typedef TQValueListConstIterator<UDSEntry> UDSEntryListConstIterator;
+
+/**
+ * MetaData is a simple map of key/value strings.
+ */
+class TDEIO_EXPORT MetaData : public TQMap<TQString, TQString>
+{
+public:
+ /**
+ * Creates an empty meta data map.
+ */
+ MetaData() : TQMap<TQString, TQString>() { };
+ /**
+ * Copy constructor.
+ */
+ MetaData(const TQMap<TQString, TQString>&metaData) :
+ TQMap<TQString, TQString>(metaData) { };
+
+ /**
+ * Adds the given meta data map to this map.
+ * @param metaData the map to add
+ * @return this map
+ */
+ MetaData & operator+= ( const TQMap<TQString,TQString> &metaData )
+ {
+ TQMap<TQString,TQString>::ConstIterator it;
+ for( it = metaData.begin();
+ it != metaData.end();
+ ++it)
+ {
+ replace(it.key(), it.data());
+ }
+ return *this;
+ }
+};
+
+}
+#endif
diff --git a/tdeio/tdeio/http_slave_defaults.h b/tdeio/tdeio/http_slave_defaults.h
new file mode 100644
index 000000000..e3247e39b
--- /dev/null
+++ b/tdeio/tdeio/http_slave_defaults.h
@@ -0,0 +1,49 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _KIO_HTTP_SLAVE_DEFAULTS_H
+#define _KIO_HTTP_SLAVE_DEFAULTS_H
+
+// CONNECTION
+#define DEFAULT_KEEP_ALIVE_TIMEOUT 60 // 60 seconds
+
+// CACHE SETTINGS
+#define DEFAULT_MAX_CACHE_SIZE 5120 // 5 MB
+#define DEFAULT_MAX_CACHE_AGE 60*60*24*14 // 14 DAYS
+#define DEFAULT_CACHE_EXPIRE 3*60 // 3 MINS
+#define DEFAULT_CLEAN_CACHE_INTERVAL 30*60 // 30 MINS
+#define DEFAULT_CACHE_CONTROL TDEIO::CC_Refresh // Verify with remote
+#define CACHE_REVISION "7\n" // Cache version
+
+// DEFAULT USER AGENT KEY - ENABLES OS NAME
+#define DEFAULT_USER_AGENT_KEYS "o" // Show OS
+
+// MAXIMUM AMOUNT OF DATA THAT CAN BE SAFELY SENT OVER IPC
+#define MAX_IPC_SIZE 1024*8
+
+// AMOUNT OF DATA TO OBTAIN FROM THE SERVER BY DEFAULT
+#define DEFAULT_BUF_SIZE 1024*4
+
+// SOME DEFAULT HEADER VALUES
+#define DEFAULT_LANGUAGE_HEADER "en"
+#define DEFAULT_MIME_TYPE "text/html"
+#define DEFAULT_PARTIAL_CHARSET_HEADER ", utf-8;q=0.5, *;q=0.5"
+
+#define DEFAULT_ACCEPT_HEADER "text/html, image/jpeg, image/png, text/*, image/*, */*"
+
+#endif
diff --git a/tdeio/tdeio/ioslave_defaults.h b/tdeio/tdeio/ioslave_defaults.h
new file mode 100644
index 000000000..ddf5b6af5
--- /dev/null
+++ b/tdeio/tdeio/ioslave_defaults.h
@@ -0,0 +1,53 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _KIO_IOSLAVE_DEFAULTS_H
+#define _KIO_IOSLAVE_DEFAULTS_H
+
+// TIMEOUT VALUES
+#define DEFAULT_RESPONSE_TIMEOUT 600 // 10 min.
+#define DEFAULT_CONNECT_TIMEOUT 20 // 20 secs.
+#define DEFAULT_READ_TIMEOUT 15 // 15 secs.
+#define DEFAULT_PROXY_CONNECT_TIMEOUT 10 // 10 secs.
+#define MIN_TIMEOUT_VALUE 2 // 2 secs.
+
+// MINMUM SIZE FOR ABORTED DOWNLOAD TO BE KEPT
+#define DEFAULT_MINIMUM_KEEP_SIZE 5120 // 5 Kbs
+
+// NORMAL PORT DEFAULTS
+#define DEFAULT_FTP_PORT 21
+#define DEFAULT_SMTP_PORT 25
+#define DEFAULT_HTTP_PORT 80
+#define DEFAULT_POP3_PORT 110
+#define DEFAULT_NNTP_PORT 119
+#define DEFAULT_IMAP_PORT 143
+#define DEFAULT_IMAP3_PORT 220
+#define DEFAULT_LDAP_PORT 389
+
+// SECURE PORT DEFAULTS
+#define DEFAULT_HTTPS_PORT 443
+#define DEFAULT_NNTPS_PORT 563
+#define DEFAULT_LDAPS_PORT 389
+#define DEFAULT_IMAPS_PORT 993
+#define DEFAULT_POP3S_PORT 995
+
+// OTHER GENERIC PORT DEFAULTS
+#define DEFAULT_PROXY_PORT 8080
+#define MAX_PORT_VALUE 65535
+
+#endif
diff --git a/tdeio/tdeio/job.cpp b/tdeio/tdeio/job.cpp
new file mode 100644
index 000000000..7bea676c5
--- /dev/null
+++ b/tdeio/tdeio/job.cpp
@@ -0,0 +1,4814 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
+ David Faure <faure@kde.org>
+ Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "tdeio/job.h"
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+
+#include <signal.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+extern "C" {
+#include <pwd.h>
+#include <grp.h>
+}
+#include <tqtimer.h>
+#include <tqfile.h>
+
+#include <kapplication.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <ksimpleconfig.h>
+#include <kdebug.h>
+#include <kdialog.h>
+#include <kmessagebox.h>
+#include <kdatastream.h>
+#include <kmainwindow.h>
+#include <kde_file.h>
+
+#include <errno.h>
+
+#include "kmimetype.h"
+#include "slave.h"
+#include "scheduler.h"
+#include "kdirwatch.h"
+#include "kmimemagic.h"
+#include "kprotocolinfo.h"
+#include "kprotocolmanager.h"
+
+#include "tdeio/observer.h"
+
+#include "kssl/ksslcsessioncache.h"
+
+#include <kdirnotify_stub.h>
+#include <ktempfile.h>
+#include <dcopclient.h>
+
+#ifdef Q_OS_UNIX
+#include <utime.h>
+#endif
+#if defined Q_WS_X11
+#include <netwm.h>
+#include <fixx11h.h>
+#endif
+
+using namespace TDEIO;
+template class TQPtrList<TDEIO::Job>;
+
+//this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX
+#define REPORT_TIMEOUT 200
+
+#define KIO_ARGS TQByteArray packedArgs; TQDataStream stream( packedArgs, IO_WriteOnly ); stream
+
+class Job::JobPrivate
+{
+public:
+ JobPrivate() : m_autoErrorHandling( false ), m_autoWarningHandling( true ),
+ m_interactive( true ), m_parentJob( 0L ), m_extraFlags(0),
+ m_processedSize(0), m_userTimestamp(0)
+ {}
+
+ bool m_autoErrorHandling;
+ bool m_autoWarningHandling;
+ bool m_interactive;
+ TQGuardedPtr<TQWidget> m_errorParentWidget;
+ // Maybe we could use the TQObject parent/child mechanism instead
+ // (requires a new ctor, and moving the ctor code to some init()).
+ Job* m_parentJob;
+ int m_extraFlags;
+ TDEIO::filesize_t m_processedSize;
+ unsigned long m_userTimestamp;
+};
+
+Job::Job(bool showProgressInfo) : TQObject(0, "job"), m_error(0), m_percent(0)
+ , m_progressId(0), m_speedTimer(0), d( new JobPrivate )
+{
+ // All jobs delete themselves after emiting 'result'.
+
+ // Notify the UI Server and get a progress id
+ if ( showProgressInfo )
+ {
+ m_progressId = Observer::self()->newJob( this, true );
+ addMetaData("progress-id", TQString::number(m_progressId));
+ //kdDebug(7007) << "Created job " << this << " with progress info -- m_progressId=" << m_progressId << endl;
+ // Connect global progress info signals
+ connect( this, TQT_SIGNAL( percent( TDEIO::Job*, unsigned long ) ),
+ Observer::self(), TQT_SLOT( slotPercent( TDEIO::Job*, unsigned long ) ) );
+ connect( this, TQT_SIGNAL( infoMessage( TDEIO::Job*, const TQString & ) ),
+ Observer::self(), TQT_SLOT( slotInfoMessage( TDEIO::Job*, const TQString & ) ) );
+ connect( this, TQT_SIGNAL( totalSize( TDEIO::Job*, TDEIO::filesize_t ) ),
+ Observer::self(), TQT_SLOT( slotTotalSize( TDEIO::Job*, TDEIO::filesize_t ) ) );
+ connect( this, TQT_SIGNAL( processedSize( TDEIO::Job*, TDEIO::filesize_t ) ),
+ Observer::self(), TQT_SLOT( slotProcessedSize( TDEIO::Job*, TDEIO::filesize_t ) ) );
+ connect( this, TQT_SIGNAL( speed( TDEIO::Job*, unsigned long ) ),
+ Observer::self(), TQT_SLOT( slotSpeed( TDEIO::Job*, unsigned long ) ) );
+ }
+ // Don't exit while this job is running
+ if (kapp)
+ kapp->ref();
+ if (kapp)
+ updateUserTimestamp( kapp->userTimestamp());
+}
+
+Job::~Job()
+{
+ delete m_speedTimer;
+ delete d;
+ if (kapp)
+ kapp->deref();
+}
+
+int& Job::extraFlags()
+{
+ return d->m_extraFlags;
+}
+
+void Job::setProcessedSize(TDEIO::filesize_t size)
+{
+ d->m_processedSize = size;
+}
+
+TDEIO::filesize_t Job::getProcessedSize()
+{
+ return d->m_processedSize;
+}
+
+void Job::addSubjob(Job *job, bool inheritMetaData)
+{
+ //kdDebug(7007) << "addSubjob(" << job << ") this = " << this << endl;
+ subjobs.append(job);
+
+ connect( job, TQT_SIGNAL(result(TDEIO::Job*)),
+ TQT_SLOT(slotResult(TDEIO::Job*)) );
+
+ // Forward information from that subjob.
+ connect( job, TQT_SIGNAL(speed( TDEIO::Job*, unsigned long )),
+ TQT_SLOT(slotSpeed(TDEIO::Job*, unsigned long)) );
+
+ connect( job, TQT_SIGNAL(infoMessage( TDEIO::Job*, const TQString & )),
+ TQT_SLOT(slotInfoMessage(TDEIO::Job*, const TQString &)) );
+
+ if (inheritMetaData)
+ job->mergeMetaData(m_outgoingMetaData);
+
+ job->setWindow( m_window );
+ job->updateUserTimestamp( d->m_userTimestamp );
+}
+
+void Job::removeSubjob( Job *job )
+{
+ removeSubjob( job, false, true );
+}
+
+void Job::removeSubjob( Job *job, bool mergeMetaData, bool emitResultIfLast )
+{
+ //kdDebug(7007) << "removeSubjob(" << job << ") this = " << this << " subjobs = " << subjobs.count() << endl;
+ // Merge metadata from subjob
+ if ( mergeMetaData )
+ m_incomingMetaData += job->metaData();
+ subjobs.remove(job);
+ if ( subjobs.isEmpty() && emitResultIfLast )
+ emitResult();
+}
+
+void Job::emitPercent( TDEIO::filesize_t processedSize, TDEIO::filesize_t totalSize )
+{
+ // calculate percents
+ unsigned long ipercent = m_percent;
+
+ if ( totalSize == 0 )
+ m_percent = 100;
+ else
+ m_percent = (unsigned long)(( (float)(processedSize) / (float)(totalSize) ) * 100.0);
+
+ if ( m_percent != ipercent || m_percent == 100 /* for those buggy total sizes that grow */ ) {
+ emit percent( this, m_percent );
+ //kdDebug(7007) << "Job::emitPercent - percent = " << (unsigned int) m_percent << endl;
+ }
+}
+
+void Job::emitSpeed( unsigned long bytes_per_second )
+{
+ //kdDebug(7007) << "Job " << this << " emitSpeed " << bytes_per_second << endl;
+ if ( !m_speedTimer )
+ {
+ m_speedTimer = new TQTimer();
+ connect( m_speedTimer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotSpeedTimeout() ) );
+ }
+ emit speed( this, bytes_per_second );
+ m_speedTimer->start( 5000 ); // 5 seconds interval should be enough
+}
+
+void Job::emitResult()
+{
+ // If we are displaying a progress dialog, remove it first.
+ if ( m_progressId ) // Did we get an ID from the observer ?
+ Observer::self()->jobFinished( m_progressId );
+ if ( m_error && d->m_interactive && d->m_autoErrorHandling )
+ showErrorDialog( d->m_errorParentWidget );
+ emit result(this);
+ deleteLater();
+}
+
+void Job::kill( bool quietly )
+{
+ kdDebug(7007) << "Job::kill this=" << this << " " << className() << " m_progressId=" << m_progressId << " quietly=" << quietly << endl;
+ // kill all subjobs, without triggering their result slot
+ TQPtrListIterator<Job> it( subjobs );
+ for ( ; it.current() ; ++it )
+ (*it)->kill( true );
+ subjobs.clear();
+
+ if ( ! quietly ) {
+ m_error = ERR_USER_CANCELED;
+ emit canceled( this ); // Not very useful (deprecated)
+ emitResult();
+ } else
+ {
+ if ( m_progressId ) // in both cases we want to hide the progress window
+ Observer::self()->jobFinished( m_progressId );
+ deleteLater();
+ }
+}
+
+void Job::slotResult( Job *job )
+{
+ // Did job have an error ?
+ if ( job->error() && !m_error )
+ {
+ // Store it in the parent only if first error
+ m_error = job->error();
+ m_errorText = job->errorText();
+ }
+ removeSubjob(job);
+}
+
+void Job::slotSpeed( TDEIO::Job*, unsigned long speed )
+{
+ //kdDebug(7007) << "Job::slotSpeed " << speed << endl;
+ emitSpeed( speed );
+}
+
+void Job::slotInfoMessage( TDEIO::Job*, const TQString & msg )
+{
+ emit infoMessage( this, msg );
+}
+
+void Job::slotSpeedTimeout()
+{
+ //kdDebug(7007) << "slotSpeedTimeout()" << endl;
+ // send 0 and stop the timer
+ // timer will be restarted only when we receive another speed event
+ emit speed( this, 0 );
+ m_speedTimer->stop();
+}
+
+//Job::errorString is implemented in global.cpp
+
+void Job::showErrorDialog( TQWidget * parent )
+{
+ //kdDebug(7007) << "Job::showErrorDialog parent=" << parent << endl;
+ kapp->enableStyles();
+ // Show a message box, except for "user canceled" or "no content"
+ if ( (m_error != ERR_USER_CANCELED) && (m_error != ERR_NO_CONTENT) ) {
+ //old plain error message
+ //kdDebug(7007) << "Default language: " << TDEGlobal::locale()->defaultLanguage() << endl;
+ if ( 1 )
+ KMessageBox::queuedMessageBox( parent, KMessageBox::Error, errorString() );
+#if 0
+ } else {
+ TQStringList errors = detailedErrorStrings();
+ TQString caption, err, detail;
+ TQStringList::const_iterator it = errors.begin();
+ if ( it != errors.end() )
+ caption = *(it++);
+ if ( it != errors.end() )
+ err = *(it++);
+ if ( it != errors.end() )
+ detail = *it;
+ KMessageBox::queuedDetailedError( parent, err, detail, caption );
+ }
+#endif
+ }
+}
+
+void Job::setAutoErrorHandlingEnabled( bool enable, TQWidget *parentWidget )
+{
+ d->m_autoErrorHandling = enable;
+ d->m_errorParentWidget = parentWidget;
+}
+
+bool Job::isAutoErrorHandlingEnabled() const
+{
+ return d->m_autoErrorHandling;
+}
+
+void Job::setAutoWarningHandlingEnabled( bool enable )
+{
+ d->m_autoWarningHandling = enable;
+}
+
+bool Job::isAutoWarningHandlingEnabled() const
+{
+ return d->m_autoWarningHandling;
+}
+
+void Job::setInteractive(bool enable)
+{
+ d->m_interactive = enable;
+}
+
+bool Job::isInteractive() const
+{
+ return d->m_interactive;
+}
+
+void Job::setWindow(TQWidget *window)
+{
+ m_window = window;
+ TDEIO::Scheduler::registerWindow(window);
+}
+
+TQWidget *Job::window() const
+{
+ return m_window;
+}
+
+void Job::updateUserTimestamp( unsigned long time )
+{
+#if defined Q_WS_X11
+ if( d->m_userTimestamp == 0 || NET::timestampCompare( time, d->m_userTimestamp ) > 0 )
+ d->m_userTimestamp = time;
+#endif
+}
+
+unsigned long Job::userTimestamp() const
+{
+ return d->m_userTimestamp;
+}
+
+void Job::setParentJob(Job* job)
+{
+ Q_ASSERT(d->m_parentJob == 0L);
+ Q_ASSERT(job);
+ d->m_parentJob = job;
+}
+
+Job* Job::parentJob() const
+{
+ return d->m_parentJob;
+}
+
+MetaData Job::metaData() const
+{
+ return m_incomingMetaData;
+}
+
+TQString Job::queryMetaData(const TQString &key)
+{
+ if (!m_incomingMetaData.contains(key))
+ return TQString::null;
+ return m_incomingMetaData[key];
+}
+
+void Job::setMetaData( const TDEIO::MetaData &_metaData)
+{
+ m_outgoingMetaData = _metaData;
+}
+
+void Job::addMetaData( const TQString &key, const TQString &value)
+{
+ m_outgoingMetaData.insert(key, value);
+}
+
+void Job::addMetaData( const TQMap<TQString,TQString> &values)
+{
+ TQMapConstIterator<TQString,TQString> it = values.begin();
+ for(;it != values.end(); ++it)
+ m_outgoingMetaData.insert(it.key(), it.data());
+}
+
+void Job::mergeMetaData( const TQMap<TQString,TQString> &values)
+{
+ TQMapConstIterator<TQString,TQString> it = values.begin();
+ for(;it != values.end(); ++it)
+ m_outgoingMetaData.insert(it.key(), it.data(), false);
+}
+
+MetaData Job::outgoingMetaData() const
+{
+ return m_outgoingMetaData;
+}
+
+
+SimpleJob::SimpleJob(const KURL& url, int command, const TQByteArray &packedArgs,
+ bool showProgressInfo )
+ : Job(showProgressInfo), m_slave(0), m_packedArgs(packedArgs),
+ m_url(url), m_command(command), m_totalSize(0)
+{
+ if (m_url.hasSubURL())
+ {
+ KURL::List list = KURL::split(m_url);
+ KURL::List::Iterator it = list.fromLast();
+ list.remove(it);
+ m_subUrl = KURL::join(list);
+ //kdDebug(7007) << "New URL = " << m_url.url() << endl;
+ //kdDebug(7007) << "Sub URL = " << m_subUrl.url() << endl;
+ }
+
+ Scheduler::doJob(this);
+
+ if (!m_url.isValid())
+ {
+ kdDebug() << "ERR_MALFORMED_URL" << endl;
+ m_error = ERR_MALFORMED_URL;
+ m_errorText = m_url.url();
+ TQTimer::singleShot(0, this, TQT_SLOT(slotFinished()) );
+ return;
+ }
+}
+
+void SimpleJob::kill( bool quietly )
+{
+ Scheduler::cancelJob( this ); // deletes the slave if not 0
+ m_slave = 0; // -> set to 0
+ Job::kill( quietly );
+}
+
+void SimpleJob::putOnHold()
+{
+ Q_ASSERT( m_slave );
+ if ( m_slave )
+ {
+ Scheduler::putSlaveOnHold(this, m_url);
+ m_slave = 0;
+ }
+ kill(true);
+}
+
+void SimpleJob::removeOnHold()
+{
+ Scheduler::removeSlaveOnHold();
+}
+
+SimpleJob::~SimpleJob()
+{
+ if (m_slave) // was running
+ {
+ kdDebug(7007) << "SimpleJob::~SimpleJob: Killing running job in destructor!" << endl;
+#if 0
+ m_slave->kill();
+ Scheduler::jobFinished( this, m_slave ); // deletes the slave
+#endif
+ Scheduler::cancelJob( this );
+ m_slave = 0; // -> set to 0
+ }
+}
+
+void SimpleJob::start(Slave *slave)
+{
+ m_slave = slave;
+
+ connect( m_slave, TQT_SIGNAL( error( int , const TQString & ) ),
+ TQT_SLOT( slotError( int , const TQString & ) ) );
+
+ connect( m_slave, TQT_SIGNAL( warning( const TQString & ) ),
+ TQT_SLOT( slotWarning( const TQString & ) ) );
+
+ connect( m_slave, TQT_SIGNAL( infoMessage( const TQString & ) ),
+ TQT_SLOT( slotInfoMessage( const TQString & ) ) );
+
+ connect( m_slave, TQT_SIGNAL( connected() ),
+ TQT_SLOT( slotConnected() ) );
+
+ connect( m_slave, TQT_SIGNAL( finished() ),
+ TQT_SLOT( slotFinished() ) );
+
+ if ((extraFlags() & EF_TransferJobDataSent) == 0)
+ {
+ connect( m_slave, TQT_SIGNAL( totalSize( TDEIO::filesize_t ) ),
+ TQT_SLOT( slotTotalSize( TDEIO::filesize_t ) ) );
+
+ connect( m_slave, TQT_SIGNAL( processedSize( TDEIO::filesize_t ) ),
+ TQT_SLOT( slotProcessedSize( TDEIO::filesize_t ) ) );
+
+ connect( m_slave, TQT_SIGNAL( speed( unsigned long ) ),
+ TQT_SLOT( slotSpeed( unsigned long ) ) );
+ }
+
+ connect( slave, TQT_SIGNAL( needProgressId() ),
+ TQT_SLOT( slotNeedProgressId() ) );
+
+ connect( slave, TQT_SIGNAL(metaData( const TDEIO::MetaData& ) ),
+ TQT_SLOT( slotMetaData( const TDEIO::MetaData& ) ) );
+
+ if (m_window)
+ {
+ TQString id;
+ addMetaData("window-id", id.setNum((ulong)m_window->winId()));
+ }
+ if (userTimestamp())
+ {
+ TQString id;
+ addMetaData("user-timestamp", id.setNum(userTimestamp()));
+ }
+
+ TQString sslSession = KSSLCSessionCache::getSessionForURL(m_url);
+ if ( !sslSession.isNull() )
+ {
+ addMetaData("ssl_session_id", sslSession);
+ }
+
+ if (!isInteractive())
+ {
+ addMetaData("no-auth-prompt", "true");
+ }
+
+ if (!m_outgoingMetaData.isEmpty())
+ {
+ KIO_ARGS << m_outgoingMetaData;
+ slave->send( CMD_META_DATA, packedArgs );
+ }
+
+ if (!m_subUrl.isEmpty())
+ {
+ KIO_ARGS << m_subUrl;
+ m_slave->send( CMD_SUBURL, packedArgs );
+ }
+
+ m_slave->send( m_command, m_packedArgs );
+}
+
+void SimpleJob::slaveDone()
+{
+ if (!m_slave) return;
+ disconnect(m_slave); // Remove all signals between slave and job
+ Scheduler::jobFinished( this, m_slave );
+ m_slave = 0;
+}
+
+void SimpleJob::slotFinished( )
+{
+ // Return slave to the scheduler
+ slaveDone();
+
+ if (subjobs.isEmpty())
+ {
+ if ( !m_error && (m_command == CMD_MKDIR || m_command == CMD_RENAME ) )
+ {
+ KDirNotify_stub allDirNotify( "*", "KDirNotify*" );
+ if ( m_command == CMD_MKDIR )
+ {
+ KURL urlDir( url() );
+ urlDir.setPath( urlDir.directory() );
+ allDirNotify.FilesAdded( urlDir );
+ }
+ else /*if ( m_command == CMD_RENAME )*/
+ {
+ KURL src, dst;
+ TQDataStream str( m_packedArgs, IO_ReadOnly );
+ str >> src >> dst;
+ if ( src.directory() == dst.directory() ) // For the user, moving isn't renaming. Only renaming is.
+ allDirNotify.FileRenamed( src, dst );
+ }
+ }
+ emitResult();
+ }
+}
+
+void SimpleJob::slotError( int error, const TQString & errorText )
+{
+ m_error = error;
+ m_errorText = errorText;
+ if ((m_error == ERR_UNKNOWN_HOST) && m_url.host().isEmpty())
+ m_errorText = TQString::null;
+ // error terminates the job
+ slotFinished();
+}
+
+void SimpleJob::slotWarning( const TQString & errorText )
+{
+ TQGuardedPtr<SimpleJob> guard( this );
+ if (isInteractive() && isAutoWarningHandlingEnabled())
+ {
+ static uint msgBoxDisplayed = 0;
+ if ( msgBoxDisplayed == 0 ) // don't bomb the user with message boxes, only one at a time
+ {
+ msgBoxDisplayed++;
+ KMessageBox::information( 0L, errorText );
+ msgBoxDisplayed--;
+ }
+ // otherwise just discard it.
+ }
+
+ if ( !guard.isNull() )
+ emit warning( this, errorText );
+}
+
+void SimpleJob::slotInfoMessage( const TQString & msg )
+{
+ emit infoMessage( this, msg );
+}
+
+void SimpleJob::slotConnected()
+{
+ emit connected( this );
+}
+
+void SimpleJob::slotNeedProgressId()
+{
+ if ( !m_progressId )
+ m_progressId = Observer::self()->newJob( this, false );
+ m_slave->setProgressId( m_progressId );
+}
+
+void SimpleJob::slotTotalSize( TDEIO::filesize_t size )
+{
+ if (size > m_totalSize)
+ {
+ m_totalSize = size;
+ emit totalSize( this, size );
+ }
+}
+
+void SimpleJob::slotProcessedSize( TDEIO::filesize_t size )
+{
+ //kdDebug(7007) << "SimpleJob::slotProcessedSize " << TDEIO::number(size) << endl;
+ setProcessedSize(size);
+ emit processedSize( this, size );
+ if ( size > m_totalSize ) {
+ slotTotalSize(size); // safety
+ }
+ emitPercent( size, m_totalSize );
+}
+
+void SimpleJob::slotSpeed( unsigned long speed )
+{
+ //kdDebug(7007) << "SimpleJob::slotSpeed( " << speed << " )" << endl;
+ emitSpeed( speed );
+}
+
+void SimpleJob::slotMetaData( const TDEIO::MetaData &_metaData)
+{
+ m_incomingMetaData += _metaData;
+}
+
+void SimpleJob::storeSSLSessionFromJob(const KURL &m_redirectionURL) {
+ TQString sslSession = queryMetaData("ssl_session_id");
+
+ if ( !sslSession.isNull() ) {
+ const KURL &queryURL = m_redirectionURL.isEmpty()?m_url:m_redirectionURL;
+ KSSLCSessionCache::putSessionForURL(queryURL, sslSession);
+ }
+}
+
+//////////
+MkdirJob::MkdirJob( const KURL& url, int command,
+ const TQByteArray &packedArgs, bool showProgressInfo )
+ : SimpleJob(url, command, packedArgs, showProgressInfo)
+{
+}
+
+void MkdirJob::start(Slave *slave)
+{
+ connect( slave, TQT_SIGNAL( redirection(const KURL &) ),
+ TQT_SLOT( slotRedirection(const KURL &) ) );
+
+ SimpleJob::start(slave);
+}
+
+// Slave got a redirection request
+void MkdirJob::slotRedirection( const KURL &url)
+{
+ kdDebug(7007) << "MkdirJob::slotRedirection(" << url << ")" << endl;
+ if (!kapp->authorizeURLAction("redirect", m_url, url))
+ {
+ kdWarning(7007) << "MkdirJob: Redirection from " << m_url << " to " << url << " REJECTED!" << endl;
+ m_error = ERR_ACCESS_DENIED;
+ m_errorText = url.prettyURL();
+ return;
+ }
+ m_redirectionURL = url; // We'll remember that when the job finishes
+ if (m_url.hasUser() && !url.hasUser() && (m_url.host().lower() == url.host().lower()))
+ m_redirectionURL.setUser(m_url.user()); // Preserve user
+ // Tell the user that we haven't finished yet
+ emit redirection(this, m_redirectionURL);
+}
+
+void MkdirJob::slotFinished()
+{
+ if ( m_redirectionURL.isEmpty() || !m_redirectionURL.isValid())
+ {
+ // Return slave to the scheduler
+ SimpleJob::slotFinished();
+ } else {
+ //kdDebug(7007) << "MkdirJob: Redirection to " << m_redirectionURL << endl;
+ if (queryMetaData("permanent-redirect")=="true")
+ emit permanentRedirection(this, m_url, m_redirectionURL);
+ KURL dummyUrl;
+ int permissions;
+ TQDataStream istream( m_packedArgs, IO_ReadOnly );
+ istream >> dummyUrl >> permissions;
+
+ m_url = m_redirectionURL;
+ m_redirectionURL = KURL();
+ m_packedArgs.truncate(0);
+ TQDataStream stream( m_packedArgs, IO_WriteOnly );
+ stream << m_url << permissions;
+
+ // Return slave to the scheduler
+ slaveDone();
+ Scheduler::doJob(this);
+ }
+}
+
+SimpleJob *TDEIO::mkdir( const KURL& url, int permissions )
+{
+ //kdDebug(7007) << "mkdir " << url << endl;
+ KIO_ARGS << url << permissions;
+ return new MkdirJob(url, CMD_MKDIR, packedArgs, false);
+}
+
+SimpleJob *TDEIO::rmdir( const KURL& url )
+{
+ //kdDebug(7007) << "rmdir " << url << endl;
+ KIO_ARGS << url << TQ_INT8(false); // isFile is false
+ return new SimpleJob(url, CMD_DEL, packedArgs, false);
+}
+
+SimpleJob *TDEIO::chmod( const KURL& url, int permissions )
+{
+ //kdDebug(7007) << "chmod " << url << endl;
+ KIO_ARGS << url << permissions;
+ return new SimpleJob(url, CMD_CHMOD, packedArgs, false);
+}
+
+SimpleJob *TDEIO::rename( const KURL& src, const KURL & dest, bool overwrite )
+{
+ //kdDebug(7007) << "rename " << src << " " << dest << endl;
+ KIO_ARGS << src << dest << (TQ_INT8) overwrite;
+ return new SimpleJob(src, CMD_RENAME, packedArgs, false);
+}
+
+SimpleJob *TDEIO::symlink( const TQString& target, const KURL & dest, bool overwrite, bool showProgressInfo )
+{
+ //kdDebug(7007) << "symlink target=" << target << " " << dest << endl;
+ KIO_ARGS << target << dest << (TQ_INT8) overwrite;
+ return new SimpleJob(dest, CMD_SYMLINK, packedArgs, showProgressInfo);
+}
+
+SimpleJob *TDEIO::special(const KURL& url, const TQByteArray & data, bool showProgressInfo)
+{
+ //kdDebug(7007) << "special " << url << endl;
+ return new SimpleJob(url, CMD_SPECIAL, data, showProgressInfo);
+}
+
+SimpleJob *TDEIO::mount( bool ro, const char *fstype, const TQString& dev, const TQString& point, bool showProgressInfo )
+{
+ KIO_ARGS << int(1) << TQ_INT8( ro ? 1 : 0 )
+ << TQString::fromLatin1(fstype) << dev << point;
+ SimpleJob *job = special( KURL("file:/"), packedArgs, showProgressInfo );
+ if ( showProgressInfo )
+ Observer::self()->mounting( job, dev, point );
+ return job;
+}
+
+SimpleJob *TDEIO::unmount( const TQString& point, bool showProgressInfo )
+{
+ KIO_ARGS << int(2) << point;
+ SimpleJob *job = special( KURL("file:/"), packedArgs, showProgressInfo );
+ if ( showProgressInfo )
+ Observer::self()->unmounting( job, point );
+ return job;
+}
+
+//////////
+LocalURLJob::LocalURLJob( const KURL& url, int command,
+ const TQByteArray &packedArgs, bool showProgressInfo )
+ : SimpleJob(url, command, packedArgs, showProgressInfo)
+{
+
+}
+
+void LocalURLJob::start(Slave *slave)
+{
+ connect( slave, TQT_SIGNAL( localURL(const KURL &, bool) ),
+ TQT_SLOT( slotLocalURL(const KURL &, bool) ) );
+
+ SimpleJob::start(slave);
+}
+
+// Slave sent a response!
+void LocalURLJob::slotLocalURL(const KURL &url, bool isLocal)
+{
+ kdDebug(7007) << "LocalURLJob::slotLocalURL(" << url << ")" << endl;
+ emit localURL(this, url, isLocal);
+}
+
+void LocalURLJob::slotFinished()
+{
+ // Return slave to the scheduler
+ SimpleJob::slotFinished();
+}
+
+LocalURLJob *TDEIO::localURL( const KURL& remoteUrl )
+{
+ KIO_ARGS << remoteUrl;
+ return new LocalURLJob(remoteUrl, CMD_LOCALURL, packedArgs, false);
+}
+
+
+//////////
+
+StatJob::StatJob( const KURL& url, int command,
+ const TQByteArray &packedArgs, bool showProgressInfo )
+ : SimpleJob(url, command, packedArgs, showProgressInfo),
+ m_bSource(true), m_details(2)
+{
+}
+
+void StatJob::start(Slave *slave)
+{
+ m_outgoingMetaData.replace( "statSide", m_bSource ? "source" : "dest" );
+ m_outgoingMetaData.replace( "details", TQString::number(m_details) );
+
+ connect( slave, TQT_SIGNAL( statEntry( const TDEIO::UDSEntry& ) ),
+ TQT_SLOT( slotStatEntry( const TDEIO::UDSEntry & ) ) );
+ connect( slave, TQT_SIGNAL( redirection(const KURL &) ),
+ TQT_SLOT( slotRedirection(const KURL &) ) );
+
+ SimpleJob::start(slave);
+}
+
+void StatJob::slotStatEntry( const TDEIO::UDSEntry & entry )
+{
+ //kdDebug(7007) << "StatJob::slotStatEntry" << endl;
+ m_statResult = entry;
+}
+
+// Slave got a redirection request
+void StatJob::slotRedirection( const KURL &url)
+{
+ kdDebug(7007) << "StatJob::slotRedirection(" << url << ")" << endl;
+ if (!kapp->authorizeURLAction("redirect", m_url, url))
+ {
+ kdWarning(7007) << "StatJob: Redirection from " << m_url << " to " << url << " REJECTED!" << endl;
+ m_error = ERR_ACCESS_DENIED;
+ m_errorText = url.prettyURL();
+ return;
+ }
+ m_redirectionURL = url; // We'll remember that when the job finishes
+ if (m_url.hasUser() && !url.hasUser() && (m_url.host().lower() == url.host().lower()))
+ m_redirectionURL.setUser(m_url.user()); // Preserve user
+ // Tell the user that we haven't finished yet
+ emit redirection(this, m_redirectionURL);
+}
+
+void StatJob::slotFinished()
+{
+ if ( m_redirectionURL.isEmpty() || !m_redirectionURL.isValid())
+ {
+ // Return slave to the scheduler
+ SimpleJob::slotFinished();
+ } else {
+ //kdDebug(7007) << "StatJob: Redirection to " << m_redirectionURL << endl;
+ if (queryMetaData("permanent-redirect")=="true")
+ emit permanentRedirection(this, m_url, m_redirectionURL);
+ m_url = m_redirectionURL;
+ m_redirectionURL = KURL();
+ m_packedArgs.truncate(0);
+ TQDataStream stream( m_packedArgs, IO_WriteOnly );
+ stream << m_url;
+
+ // Return slave to the scheduler
+ slaveDone();
+ Scheduler::doJob(this);
+ }
+}
+
+void StatJob::slotMetaData( const TDEIO::MetaData &_metaData) {
+ SimpleJob::slotMetaData(_metaData);
+ storeSSLSessionFromJob(m_redirectionURL);
+}
+
+StatJob *TDEIO::stat(const KURL& url, bool showProgressInfo)
+{
+ // Assume sideIsSource. Gets are more common than puts.
+ return stat( url, true, 2, showProgressInfo );
+}
+
+StatJob *TDEIO::stat(const KURL& url, bool sideIsSource, short int details, bool showProgressInfo)
+{
+ kdDebug(7007) << "stat " << url << endl;
+ KIO_ARGS << url;
+ StatJob * job = new StatJob(url, CMD_STAT, packedArgs, showProgressInfo );
+ job->setSide( sideIsSource );
+ job->setDetails( details );
+ if ( showProgressInfo )
+ Observer::self()->stating( job, url );
+ return job;
+}
+
+SimpleJob *TDEIO::http_update_cache( const KURL& url, bool no_cache, time_t expireDate)
+{
+ assert( (url.protocol() == "http") || (url.protocol() == "https") );
+ // Send http update_cache command (2)
+ KIO_ARGS << (int)2 << url << no_cache << expireDate;
+ SimpleJob * job = new SimpleJob( url, CMD_SPECIAL, packedArgs, false );
+ Scheduler::scheduleJob(job);
+ return job;
+}
+
+//////////
+
+TransferJob::TransferJob( const KURL& url, int command,
+ const TQByteArray &packedArgs,
+ const TQByteArray &_staticData,
+ bool showProgressInfo)
+ : SimpleJob(url, command, packedArgs, showProgressInfo), staticData( _staticData)
+{
+ m_suspended = false;
+ m_errorPage = false;
+ m_subJob = 0L;
+ if ( showProgressInfo )
+ Observer::self()->slotTransferring( this, url );
+}
+
+// Slave sends data
+void TransferJob::slotData( const TQByteArray &_data)
+{
+ if(m_redirectionURL.isEmpty() || !m_redirectionURL.isValid() || m_error)
+ emit data( this, _data);
+}
+
+// Slave got a redirection request
+void TransferJob::slotRedirection( const KURL &url)
+{
+ kdDebug(7007) << "TransferJob::slotRedirection(" << url << ")" << endl;
+ if (!kapp->authorizeURLAction("redirect", m_url, url))
+ {
+ kdWarning(7007) << "TransferJob: Redirection from " << m_url << " to " << url << " REJECTED!" << endl;
+ return;
+ }
+
+ // Some websites keep redirecting to themselves where each redirection
+ // acts as the stage in a state-machine. We define "endless redirections"
+ // as 5 redirections to the same URL.
+ if (m_redirectionList.contains(url) > 5)
+ {
+ kdDebug(7007) << "TransferJob::slotRedirection: CYCLIC REDIRECTION!" << endl;
+ m_error = ERR_CYCLIC_LINK;
+ m_errorText = m_url.prettyURL();
+ }
+ else
+ {
+ m_redirectionURL = url; // We'll remember that when the job finishes
+ if (m_url.hasUser() && !url.hasUser() && (m_url.host().lower() == url.host().lower()))
+ m_redirectionURL.setUser(m_url.user()); // Preserve user
+ m_redirectionList.append(url);
+ m_outgoingMetaData["ssl_was_in_use"] = m_incomingMetaData["ssl_in_use"];
+ // Tell the user that we haven't finished yet
+ emit redirection(this, m_redirectionURL);
+ }
+}
+
+void TransferJob::slotFinished()
+{
+ //kdDebug(7007) << "TransferJob::slotFinished(" << this << ", " << m_url << ")" << endl;
+ if (m_redirectionURL.isEmpty() || !m_redirectionURL.isValid())
+ SimpleJob::slotFinished();
+ else {
+ //kdDebug(7007) << "TransferJob: Redirection to " << m_redirectionURL << endl;
+ if (queryMetaData("permanent-redirect")=="true")
+ emit permanentRedirection(this, m_url, m_redirectionURL);
+ // Honour the redirection
+ // We take the approach of "redirecting this same job"
+ // Another solution would be to create a subjob, but the same problem
+ // happens (unpacking+repacking)
+ staticData.truncate(0);
+ m_incomingMetaData.clear();
+ if (queryMetaData("cache") != "reload")
+ addMetaData("cache","refresh");
+ m_suspended = false;
+ m_url = m_redirectionURL;
+ m_redirectionURL = KURL();
+ // The very tricky part is the packed arguments business
+ TQString dummyStr;
+ KURL dummyUrl;
+ TQDataStream istream( m_packedArgs, IO_ReadOnly );
+ switch( m_command ) {
+ case CMD_GET: {
+ m_packedArgs.truncate(0);
+ TQDataStream stream( m_packedArgs, IO_WriteOnly );
+ stream << m_url;
+ break;
+ }
+ case CMD_PUT: {
+ int permissions;
+ TQ_INT8 iOverwrite, iResume;
+ istream >> dummyUrl >> iOverwrite >> iResume >> permissions;
+ m_packedArgs.truncate(0);
+ TQDataStream stream( m_packedArgs, IO_WriteOnly );
+ stream << m_url << iOverwrite << iResume << permissions;
+ break;
+ }
+ case CMD_SPECIAL: {
+ int specialcmd;
+ istream >> specialcmd;
+ if (specialcmd == 1) // HTTP POST
+ {
+ addMetaData("cache","reload");
+ m_packedArgs.truncate(0);
+ TQDataStream stream( m_packedArgs, IO_WriteOnly );
+ stream << m_url;
+ m_command = CMD_GET;
+ }
+ break;
+ }
+ }
+
+ // Return slave to the scheduler
+ slaveDone();
+ Scheduler::doJob(this);
+ }
+}
+
+void TransferJob::setAsyncDataEnabled(bool enabled)
+{
+ if (enabled)
+ extraFlags() |= EF_TransferJobAsync;
+ else
+ extraFlags() &= ~EF_TransferJobAsync;
+}
+
+void TransferJob::sendAsyncData(const TQByteArray &dataForSlave)
+{
+ if (extraFlags() & EF_TransferJobNeedData)
+ {
+ m_slave->send( MSG_DATA, dataForSlave );
+ if (extraFlags() & EF_TransferJobDataSent)
+ {
+ TDEIO::filesize_t size = getProcessedSize()+dataForSlave.size();
+ setProcessedSize(size);
+ emit processedSize( this, size );
+ if ( size > m_totalSize ) {
+ slotTotalSize(size); // safety
+ }
+ emitPercent( size, m_totalSize );
+ }
+ }
+
+ extraFlags() &= ~EF_TransferJobNeedData;
+}
+
+void TransferJob::setReportDataSent(bool enabled)
+{
+ if (enabled)
+ extraFlags() |= EF_TransferJobDataSent;
+ else
+ extraFlags() &= ~EF_TransferJobDataSent;
+}
+
+bool TransferJob::reportDataSent()
+{
+ return (extraFlags() & EF_TransferJobDataSent);
+}
+
+
+// Slave requests data
+void TransferJob::slotDataReq()
+{
+ TQByteArray dataForSlave;
+
+ extraFlags() |= EF_TransferJobNeedData;
+
+ if (!staticData.isEmpty())
+ {
+ dataForSlave = staticData;
+ staticData = TQByteArray();
+ }
+ else
+ {
+ emit dataReq( this, dataForSlave);
+
+ if (extraFlags() & EF_TransferJobAsync)
+ return;
+ }
+
+ static const size_t max_size = 14 * 1024 * 1024;
+ if (dataForSlave.size() > max_size)
+ {
+ kdDebug(7007) << "send " << dataForSlave.size() / 1024 / 1024 << "MB of data in TransferJob::dataReq. This needs to be splitted, which requires a copy. Fix the application.\n";
+ staticData.duplicate(dataForSlave.data() + max_size , dataForSlave.size() - max_size);
+ dataForSlave.truncate(max_size);
+ }
+
+ sendAsyncData(dataForSlave);
+
+ if (m_subJob)
+ {
+ // Bitburger protocol in action
+ suspend(); // Wait for more data from subJob.
+ m_subJob->resume(); // Ask for more!
+ }
+}
+
+void TransferJob::slotMimetype( const TQString& type )
+{
+ m_mimetype = type;
+ emit mimetype( this, m_mimetype);
+}
+
+
+void TransferJob::suspend()
+{
+ m_suspended = true;
+ if (m_slave)
+ m_slave->suspend();
+}
+
+void TransferJob::resume()
+{
+ m_suspended = false;
+ if (m_slave)
+ m_slave->resume();
+}
+
+void TransferJob::start(Slave *slave)
+{
+ assert(slave);
+ connect( slave, TQT_SIGNAL( data( const TQByteArray & ) ),
+ TQT_SLOT( slotData( const TQByteArray & ) ) );
+
+ connect( slave, TQT_SIGNAL( dataReq() ),
+ TQT_SLOT( slotDataReq() ) );
+
+ connect( slave, TQT_SIGNAL( redirection(const KURL &) ),
+ TQT_SLOT( slotRedirection(const KURL &) ) );
+
+ connect( slave, TQT_SIGNAL(mimeType( const TQString& ) ),
+ TQT_SLOT( slotMimetype( const TQString& ) ) );
+
+ connect( slave, TQT_SIGNAL(errorPage() ),
+ TQT_SLOT( slotErrorPage() ) );
+
+ connect( slave, TQT_SIGNAL( needSubURLData() ),
+ TQT_SLOT( slotNeedSubURLData() ) );
+
+ connect( slave, TQT_SIGNAL(canResume( TDEIO::filesize_t ) ),
+ TQT_SLOT( slotCanResume( TDEIO::filesize_t ) ) );
+
+ if (slave->suspended())
+ {
+ m_mimetype = "unknown";
+ // WABA: The slave was put on hold. Resume operation.
+ slave->resume();
+ }
+
+ SimpleJob::start(slave);
+ if (m_suspended)
+ slave->suspend();
+}
+
+void TransferJob::slotNeedSubURLData()
+{
+ // Job needs data from subURL.
+ m_subJob = TDEIO::get( m_subUrl, false, false);
+ suspend(); // Put job on hold until we have some data.
+ connect(m_subJob, TQT_SIGNAL( data(TDEIO::Job*,const TQByteArray &)),
+ TQT_SLOT( slotSubURLData(TDEIO::Job*,const TQByteArray &)));
+ addSubjob(m_subJob);
+}
+
+void TransferJob::slotSubURLData(TDEIO::Job*, const TQByteArray &data)
+{
+ // The Alternating Bitburg protocol in action again.
+ staticData = data;
+ m_subJob->suspend(); // Put job on hold until we have delivered the data.
+ resume(); // Activate ourselves again.
+}
+
+void TransferJob::slotMetaData( const TDEIO::MetaData &_metaData) {
+ SimpleJob::slotMetaData(_metaData);
+ storeSSLSessionFromJob(m_redirectionURL);
+}
+
+void TransferJob::slotErrorPage()
+{
+ m_errorPage = true;
+}
+
+void TransferJob::slotCanResume( TDEIO::filesize_t offset )
+{
+ emit canResume(this, offset);
+}
+
+void TransferJob::slotResult( TDEIO::Job *job)
+{
+ // This can only be our suburl.
+ assert(job == m_subJob);
+ // Did job have an error ?
+ if ( job->error() )
+ {
+ m_error = job->error();
+ m_errorText = job->errorText();
+
+ emitResult();
+ return;
+ }
+
+ if (job == m_subJob)
+ {
+ m_subJob = 0; // No action required
+ resume(); // Make sure we get the remaining data.
+ }
+ removeSubjob( job, false, false ); // Remove job, but don't kill this job.
+}
+
+TransferJob *TDEIO::get( const KURL& url, bool reload, bool showProgressInfo )
+{
+ // Send decoded path and encoded query
+ KIO_ARGS << url;
+ TransferJob * job = new TransferJob( url, CMD_GET, packedArgs, TQByteArray(), showProgressInfo );
+ if (reload)
+ job->addMetaData("cache", "reload");
+ return job;
+}
+
+class PostErrorJob : public TransferJob
+{
+public:
+
+ PostErrorJob(int _error, const TQString& url, const TQByteArray &packedArgs, const TQByteArray &postData, bool showProgressInfo)
+ : TransferJob(KURL(), CMD_SPECIAL, packedArgs, postData, showProgressInfo)
+ {
+ m_error = _error;
+ m_errorText = url;
+ }
+
+};
+
+TransferJob *TDEIO::http_post( const KURL& url, const TQByteArray &postData, bool showProgressInfo )
+{
+ int _error = 0;
+
+ // filter out some malicious ports
+ static const int bad_ports[] = {
+ 1, // tcpmux
+ 7, // echo
+ 9, // discard
+ 11, // systat
+ 13, // daytime
+ 15, // netstat
+ 17, // qotd
+ 19, // chargen
+ 20, // ftp-data
+ 21, // ftp-cntl
+ 22, // ssh
+ 23, // telnet
+ 25, // smtp
+ 37, // time
+ 42, // name
+ 43, // nicname
+ 53, // domain
+ 77, // priv-rjs
+ 79, // finger
+ 87, // ttylink
+ 95, // supdup
+ 101, // hostriame
+ 102, // iso-tsap
+ 103, // gppitnp
+ 104, // acr-nema
+ 109, // pop2
+ 110, // pop3
+ 111, // sunrpc
+ 113, // auth
+ 115, // sftp
+ 117, // uucp-path
+ 119, // nntp
+ 123, // NTP
+ 135, // loc-srv / epmap
+ 139, // netbios
+ 143, // imap2
+ 179, // BGP
+ 389, // ldap
+ 512, // print / exec
+ 513, // login
+ 514, // shell
+ 515, // printer
+ 526, // tempo
+ 530, // courier
+ 531, // Chat
+ 532, // netnews
+ 540, // uucp
+ 556, // remotefs
+ 587, // sendmail
+ 601, //
+ 989, // ftps data
+ 990, // ftps
+ 992, // telnets
+ 993, // imap/SSL
+ 995, // pop3/SSL
+ 1080, // SOCKS
+ 2049, // nfs
+ 4045, // lockd
+ 6000, // x11
+ 6667, // irc
+ 0};
+ for (int cnt=0; bad_ports[cnt]; ++cnt)
+ if (url.port() == bad_ports[cnt])
+ {
+ _error = TDEIO::ERR_POST_DENIED;
+ break;
+ }
+
+ if( _error )
+ {
+ static bool override_loaded = false;
+ static TQValueList< int >* overriden_ports = NULL;
+ if( !override_loaded )
+ {
+ TDEConfig cfg( "kio_httprc", true );
+ overriden_ports = new TQValueList< int >;
+ *overriden_ports = cfg.readIntListEntry( "OverriddenPorts" );
+ override_loaded = true;
+ }
+ for( TQValueList< int >::ConstIterator it = overriden_ports->begin();
+ it != overriden_ports->end();
+ ++it )
+ if( overriden_ports->contains( url.port()))
+ _error = 0;
+ }
+
+ // filter out non https? protocols
+ if ((url.protocol() != "http") && (url.protocol() != "https" ))
+ _error = TDEIO::ERR_POST_DENIED;
+
+ bool redirection = false;
+ KURL _url(url);
+ if (_url.path().isEmpty())
+ {
+ redirection = true;
+ _url.setPath("/");
+ }
+
+ if (!_error && !kapp->authorizeURLAction("open", KURL(), _url))
+ _error = TDEIO::ERR_ACCESS_DENIED;
+
+ // if request is not valid, return an invalid transfer job
+ if (_error)
+ {
+ KIO_ARGS << (int)1 << url;
+ TransferJob * job = new PostErrorJob(_error, url.prettyURL(), packedArgs, postData, showProgressInfo);
+ return job;
+ }
+
+ // Send http post command (1), decoded path and encoded query
+ KIO_ARGS << (int)1 << _url;
+ TransferJob * job = new TransferJob( _url, CMD_SPECIAL,
+ packedArgs, postData, showProgressInfo );
+
+ if (redirection)
+ TQTimer::singleShot(0, job, TQT_SLOT(slotPostRedirection()) );
+
+ return job;
+}
+
+// http post got redirected from http://host to http://host/ by TransferJob
+// We must do this redirection ourselves because redirections by the
+// slave change post jobs into get jobs.
+void TransferJob::slotPostRedirection()
+{
+ kdDebug(7007) << "TransferJob::slotPostRedirection(" << m_url << ")" << endl;
+ // Tell the user about the new url.
+ emit redirection(this, m_url);
+}
+
+
+TransferJob *TDEIO::put( const KURL& url, int permissions,
+ bool overwrite, bool resume, bool showProgressInfo )
+{
+ KIO_ARGS << url << TQ_INT8( overwrite ? 1 : 0 ) << TQ_INT8( resume ? 1 : 0 ) << permissions;
+ TransferJob * job = new TransferJob( url, CMD_PUT, packedArgs, TQByteArray(), showProgressInfo );
+ return job;
+}
+
+//////////
+
+StoredTransferJob::StoredTransferJob(const KURL& url, int command,
+ const TQByteArray &packedArgs,
+ const TQByteArray &_staticData,
+ bool showProgressInfo)
+ : TransferJob( url, command, packedArgs, _staticData, showProgressInfo ),
+ m_uploadOffset( 0 )
+{
+ connect( this, TQT_SIGNAL( data( TDEIO::Job *, const TQByteArray & ) ),
+ TQT_SLOT( slotStoredData( TDEIO::Job *, const TQByteArray & ) ) );
+ connect( this, TQT_SIGNAL( dataReq( TDEIO::Job *, TQByteArray & ) ),
+ TQT_SLOT( slotStoredDataReq( TDEIO::Job *, TQByteArray & ) ) );
+}
+
+void StoredTransferJob::setData( const TQByteArray& arr )
+{
+ Q_ASSERT( m_data.isNull() ); // check that we're only called once
+ Q_ASSERT( m_uploadOffset == 0 ); // no upload started yet
+ m_data = arr;
+}
+
+void StoredTransferJob::slotStoredData( TDEIO::Job *, const TQByteArray &data )
+{
+ // check for end-of-data marker:
+ if ( data.size() == 0 )
+ return;
+ unsigned int oldSize = m_data.size();
+ m_data.resize( oldSize + data.size(), TQGArray::SpeedOptim );
+ memcpy( m_data.data() + oldSize, data.data(), data.size() );
+}
+
+void StoredTransferJob::slotStoredDataReq( TDEIO::Job *, TQByteArray &data )
+{
+ // Inspired from kmail's KMKernel::byteArrayToRemoteFile
+ // send the data in 64 KB chunks
+ const int MAX_CHUNK_SIZE = 64*1024;
+ int remainingBytes = m_data.size() - m_uploadOffset;
+ if( remainingBytes > MAX_CHUNK_SIZE ) {
+ // send MAX_CHUNK_SIZE bytes to the receiver (deep copy)
+ data.duplicate( m_data.data() + m_uploadOffset, MAX_CHUNK_SIZE );
+ m_uploadOffset += MAX_CHUNK_SIZE;
+ //kdDebug() << "Sending " << MAX_CHUNK_SIZE << " bytes ("
+ // << remainingBytes - MAX_CHUNK_SIZE << " bytes remain)\n";
+ } else {
+ // send the remaining bytes to the receiver (deep copy)
+ data.duplicate( m_data.data() + m_uploadOffset, remainingBytes );
+ m_data = TQByteArray();
+ m_uploadOffset = 0;
+ //kdDebug() << "Sending " << remainingBytes << " bytes\n";
+ }
+}
+
+StoredTransferJob *TDEIO::storedGet( const KURL& url, bool reload, bool showProgressInfo )
+{
+ // Send decoded path and encoded query
+ KIO_ARGS << url;
+ StoredTransferJob * job = new StoredTransferJob( url, CMD_GET, packedArgs, TQByteArray(), showProgressInfo );
+ if (reload)
+ job->addMetaData("cache", "reload");
+ return job;
+}
+
+StoredTransferJob *TDEIO::storedPut( const TQByteArray& arr, const KURL& url, int permissions,
+ bool overwrite, bool resume, bool showProgressInfo )
+{
+ KIO_ARGS << url << TQ_INT8( overwrite ? 1 : 0 ) << TQ_INT8( resume ? 1 : 0 ) << permissions;
+ StoredTransferJob * job = new StoredTransferJob( url, CMD_PUT, packedArgs, TQByteArray(), showProgressInfo );
+ job->setData( arr );
+ return job;
+}
+
+//////////
+
+MimetypeJob::MimetypeJob( const KURL& url, int command,
+ const TQByteArray &packedArgs, bool showProgressInfo )
+ : TransferJob(url, command, packedArgs, TQByteArray(), showProgressInfo)
+{
+}
+
+void MimetypeJob::start(Slave *slave)
+{
+ TransferJob::start(slave);
+}
+
+
+void MimetypeJob::slotFinished( )
+{
+ //kdDebug(7007) << "MimetypeJob::slotFinished()" << endl;
+ if ( m_error == TDEIO::ERR_IS_DIRECTORY )
+ {
+ // It is in fact a directory. This happens when HTTP redirects to FTP.
+ // Due to the "protocol doesn't support listing" code in KRun, we
+ // assumed it was a file.
+ kdDebug(7007) << "It is in fact a directory!" << endl;
+ m_mimetype = TQString::fromLatin1("inode/directory");
+ emit TransferJob::mimetype( this, m_mimetype );
+ m_error = 0;
+ }
+ if ( m_redirectionURL.isEmpty() || !m_redirectionURL.isValid() || m_error )
+ {
+ // Return slave to the scheduler
+ TransferJob::slotFinished();
+ } else {
+ //kdDebug(7007) << "MimetypeJob: Redirection to " << m_redirectionURL << endl;
+ if (queryMetaData("permanent-redirect")=="true")
+ emit permanentRedirection(this, m_url, m_redirectionURL);
+ staticData.truncate(0);
+ m_suspended = false;
+ m_url = m_redirectionURL;
+ m_redirectionURL = KURL();
+ m_packedArgs.truncate(0);
+ TQDataStream stream( m_packedArgs, IO_WriteOnly );
+ stream << m_url;
+
+ // Return slave to the scheduler
+ slaveDone();
+ Scheduler::doJob(this);
+ }
+}
+
+MimetypeJob *TDEIO::mimetype(const KURL& url, bool showProgressInfo )
+{
+ KIO_ARGS << url;
+ MimetypeJob * job = new MimetypeJob(url, CMD_MIMETYPE, packedArgs, showProgressInfo);
+ if ( showProgressInfo )
+ Observer::self()->stating( job, url );
+ return job;
+}
+
+//////////////////////////
+
+DirectCopyJob::DirectCopyJob( const KURL& url, int command,
+ const TQByteArray &packedArgs, bool showProgressInfo )
+ : SimpleJob(url, command, packedArgs, showProgressInfo)
+{
+}
+
+void DirectCopyJob::start( Slave* slave )
+{
+ connect( slave, TQT_SIGNAL(canResume( TDEIO::filesize_t ) ),
+ TQT_SLOT( slotCanResume( TDEIO::filesize_t ) ) );
+ SimpleJob::start(slave);
+}
+
+void DirectCopyJob::slotCanResume( TDEIO::filesize_t offset )
+{
+ emit canResume(this, offset);
+}
+
+//////////////////////////
+
+
+class FileCopyJob::FileCopyJobPrivate
+{
+public:
+ TDEIO::filesize_t m_sourceSize;
+ time_t m_modificationTime;
+ SimpleJob *m_delJob;
+};
+
+/*
+ * The FileCopyJob works according to the famous Bayern
+ * 'Alternating Bitburger Protocol': we either drink a beer or we
+ * we order a beer, but never both at the same time.
+ * Tranlated to io-slaves: We alternate between receiving a block of data
+ * and sending it away.
+ */
+FileCopyJob::FileCopyJob( const KURL& src, const KURL& dest, int permissions,
+ bool move, bool overwrite, bool resume, bool showProgressInfo)
+ : Job(showProgressInfo), m_src(src), m_dest(dest),
+ m_permissions(permissions), m_move(move), m_overwrite(overwrite), m_resume(resume),
+ m_totalSize(0)
+{
+ if (showProgressInfo && !move)
+ Observer::self()->slotCopying( this, src, dest );
+ else if (showProgressInfo && move)
+ Observer::self()->slotMoving( this, src, dest );
+
+ //kdDebug(7007) << "FileCopyJob::FileCopyJob()" << endl;
+ m_moveJob = 0;
+ m_copyJob = 0;
+ m_getJob = 0;
+ m_putJob = 0;
+ d = new FileCopyJobPrivate;
+ d->m_delJob = 0;
+ d->m_sourceSize = (TDEIO::filesize_t) -1;
+ d->m_modificationTime = static_cast<time_t>( -1 );
+ TQTimer::singleShot(0, this, TQT_SLOT(slotStart()));
+}
+
+void FileCopyJob::slotStart()
+{
+ if ( m_move )
+ {
+ // The if() below must be the same as the one in startBestCopyMethod
+ if ((m_src.protocol() == m_dest.protocol()) &&
+ (m_src.host() == m_dest.host()) &&
+ (m_src.port() == m_dest.port()) &&
+ (m_src.user() == m_dest.user()) &&
+ (m_src.pass() == m_dest.pass()) &&
+ !m_src.hasSubURL() && !m_dest.hasSubURL())
+ {
+ startRenameJob(m_src);
+ return;
+ }
+ else if (m_src.isLocalFile() && KProtocolInfo::canRenameFromFile(m_dest))
+ {
+ startRenameJob(m_dest);
+ return;
+ }
+ else if (m_dest.isLocalFile() && KProtocolInfo::canRenameToFile(m_src))
+ {
+ startRenameJob(m_src);
+ return;
+ }
+ // No fast-move available, use copy + del.
+ }
+ startBestCopyMethod();
+}
+
+void FileCopyJob::startBestCopyMethod()
+{
+ if ((m_src.protocol() == m_dest.protocol()) &&
+ (m_src.host() == m_dest.host()) &&
+ (m_src.port() == m_dest.port()) &&
+ (m_src.user() == m_dest.user()) &&
+ (m_src.pass() == m_dest.pass()) &&
+ !m_src.hasSubURL() && !m_dest.hasSubURL())
+ {
+ startCopyJob();
+ }
+ else if (m_src.isLocalFile() && KProtocolInfo::canCopyFromFile(m_dest))
+ {
+ startCopyJob(m_dest);
+ }
+ else if (m_dest.isLocalFile() && KProtocolInfo::canCopyToFile(m_src))
+ {
+ startCopyJob(m_src);
+ }
+ else
+ {
+ startDataPump();
+ }
+}
+
+FileCopyJob::~FileCopyJob()
+{
+ delete d;
+}
+
+void FileCopyJob::setSourceSize( off_t size )
+{
+ d->m_sourceSize = size;
+ if (size != (off_t) -1)
+ m_totalSize = size;
+}
+
+void FileCopyJob::setSourceSize64( TDEIO::filesize_t size )
+{
+ d->m_sourceSize = size;
+ if (size != (TDEIO::filesize_t) -1)
+ m_totalSize = size;
+}
+
+void FileCopyJob::setModificationTime( time_t mtime )
+{
+ d->m_modificationTime = mtime;
+}
+
+void FileCopyJob::startCopyJob()
+{
+ startCopyJob(m_src);
+}
+
+void FileCopyJob::startCopyJob(const KURL &slave_url)
+{
+ //kdDebug(7007) << "FileCopyJob::startCopyJob()" << endl;
+ KIO_ARGS << m_src << m_dest << m_permissions << (TQ_INT8) m_overwrite;
+ m_copyJob = new DirectCopyJob(slave_url, CMD_COPY, packedArgs, false);
+ addSubjob( m_copyJob );
+ connectSubjob( m_copyJob );
+ connect( m_copyJob, TQT_SIGNAL(canResume(TDEIO::Job *, TDEIO::filesize_t)),
+ TQT_SLOT( slotCanResume(TDEIO::Job *, TDEIO::filesize_t)));
+}
+
+void FileCopyJob::startRenameJob(const KURL &slave_url)
+{
+ KIO_ARGS << m_src << m_dest << (TQ_INT8) m_overwrite;
+ m_moveJob = new SimpleJob(slave_url, CMD_RENAME, packedArgs, false);
+ addSubjob( m_moveJob );
+ connectSubjob( m_moveJob );
+}
+
+void FileCopyJob::connectSubjob( SimpleJob * job )
+{
+ connect( job, TQT_SIGNAL(totalSize( TDEIO::Job*, TDEIO::filesize_t )),
+ this, TQT_SLOT( slotTotalSize(TDEIO::Job*, TDEIO::filesize_t)) );
+
+ connect( job, TQT_SIGNAL(processedSize( TDEIO::Job*, TDEIO::filesize_t )),
+ this, TQT_SLOT( slotProcessedSize(TDEIO::Job*, TDEIO::filesize_t)) );
+
+ connect( job, TQT_SIGNAL(percent( TDEIO::Job*, unsigned long )),
+ this, TQT_SLOT( slotPercent(TDEIO::Job*, unsigned long)) );
+
+}
+
+void FileCopyJob::slotProcessedSize( TDEIO::Job *, TDEIO::filesize_t size )
+{
+ setProcessedSize(size);
+ emit processedSize( this, size );
+ if ( size > m_totalSize ) {
+ slotTotalSize( this, size ); // safety
+ }
+ emitPercent( size, m_totalSize );
+}
+
+void FileCopyJob::slotTotalSize( TDEIO::Job*, TDEIO::filesize_t size )
+{
+ if (size > m_totalSize)
+ {
+ m_totalSize = size;
+ emit totalSize( this, m_totalSize );
+ }
+}
+
+void FileCopyJob::slotPercent( TDEIO::Job*, unsigned long pct )
+{
+ if ( pct > m_percent )
+ {
+ m_percent = pct;
+ emit percent( this, m_percent );
+ }
+}
+
+void FileCopyJob::startDataPump()
+{
+ //kdDebug(7007) << "FileCopyJob::startDataPump()" << endl;
+
+ m_canResume = false;
+ m_resumeAnswerSent = false;
+ m_getJob = 0L; // for now
+ m_putJob = put( m_dest, m_permissions, m_overwrite, m_resume, false /* no GUI */);
+ if ( d->m_modificationTime != static_cast<time_t>( -1 ) ) {
+ TQDateTime dt; dt.setTime_t( d->m_modificationTime );
+ m_putJob->addMetaData( "modified", dt.toString( Qt::ISODate ) );
+ }
+ //kdDebug(7007) << "FileCopyJob: m_putJob = " << m_putJob << " m_dest=" << m_dest << endl;
+
+ // The first thing the put job will tell us is whether we can
+ // resume or not (this is always emitted)
+ connect( m_putJob, TQT_SIGNAL(canResume(TDEIO::Job *, TDEIO::filesize_t)),
+ TQT_SLOT( slotCanResume(TDEIO::Job *, TDEIO::filesize_t)));
+ connect( m_putJob, TQT_SIGNAL(dataReq(TDEIO::Job *, TQByteArray&)),
+ TQT_SLOT( slotDataReq(TDEIO::Job *, TQByteArray&)));
+ addSubjob( m_putJob );
+}
+
+void FileCopyJob::slotCanResume( TDEIO::Job* job, TDEIO::filesize_t offset )
+{
+ if ( job == m_putJob || job == m_copyJob )
+ {
+ //kdDebug(7007) << "FileCopyJob::slotCanResume from PUT job. offset=" << TDEIO::number(offset) << endl;
+ if (offset)
+ {
+ RenameDlg_Result res = R_RESUME;
+
+ if (!KProtocolManager::autoResume() && !m_overwrite)
+ {
+ TQString newPath;
+ TDEIO::Job* job = ( !m_progressId && parentJob() ) ? parentJob() : this;
+ // Ask confirmation about resuming previous transfer
+ res = Observer::self()->open_RenameDlg(
+ job, i18n("File Already Exists"),
+ m_src.url(),
+ m_dest.url(),
+ (RenameDlg_Mode) (M_OVERWRITE | M_RESUME | M_NORENAME), newPath,
+ d->m_sourceSize, offset );
+ }
+
+ if ( res == R_OVERWRITE || m_overwrite )
+ offset = 0;
+ else if ( res == R_CANCEL )
+ {
+ if ( job == m_putJob )
+ m_putJob->kill(true);
+ else
+ m_copyJob->kill(true);
+ m_error = ERR_USER_CANCELED;
+ emitResult();
+ return;
+ }
+ }
+ else
+ m_resumeAnswerSent = true; // No need for an answer
+
+ if ( job == m_putJob )
+ {
+ m_getJob = get( m_src, false, false /* no GUI */ );
+ //kdDebug(7007) << "FileCopyJob: m_getJob = " << m_getJob << endl;
+ m_getJob->addMetaData( "errorPage", "false" );
+ m_getJob->addMetaData( "AllowCompressedPage", "false" );
+ // Set size in subjob. This helps if the slave doesn't emit totalSize.
+ if ( d->m_sourceSize != (TDEIO::filesize_t)-1 )
+ m_getJob->slotTotalSize( d->m_sourceSize );
+ if (offset)
+ {
+ //kdDebug(7007) << "Setting metadata for resume to " << (unsigned long) offset << endl;
+ // TODO KDE4: rename to seek or offset and document it
+ // This isn't used only for resuming, but potentially also for extracting (#72302).
+ m_getJob->addMetaData( "resume", TDEIO::number(offset) );
+
+ // Might or might not get emitted
+ connect( m_getJob, TQT_SIGNAL(canResume(TDEIO::Job *, TDEIO::filesize_t)),
+ TQT_SLOT( slotCanResume(TDEIO::Job *, TDEIO::filesize_t)));
+ }
+ m_putJob->slave()->setOffset( offset );
+
+ m_putJob->suspend();
+ addSubjob( m_getJob );
+ connectSubjob( m_getJob ); // Progress info depends on get
+ m_getJob->resume(); // Order a beer
+
+ connect( m_getJob, TQT_SIGNAL(data(TDEIO::Job*,const TQByteArray&)),
+ TQT_SLOT( slotData(TDEIO::Job*,const TQByteArray&)) );
+ connect( m_getJob, TQT_SIGNAL(mimetype(TDEIO::Job*,const TQString&) ),
+ TQT_SLOT(slotMimetype(TDEIO::Job*,const TQString&)) );
+ }
+ else // copyjob
+ {
+ m_copyJob->slave()->sendResumeAnswer( offset != 0 );
+ }
+ }
+ else if ( job == m_getJob )
+ {
+ // Cool, the get job said ok, we can resume
+ m_canResume = true;
+ //kdDebug(7007) << "FileCopyJob::slotCanResume from the GET job -> we can resume" << endl;
+
+ m_getJob->slave()->setOffset( m_putJob->slave()->offset() );
+ }
+ else
+ kdWarning(7007) << "FileCopyJob::slotCanResume from unknown job=" << job
+ << " m_getJob=" << m_getJob << " m_putJob=" << m_putJob << endl;
+}
+
+void FileCopyJob::slotData( TDEIO::Job * , const TQByteArray &data)
+{
+ //kdDebug(7007) << "FileCopyJob::slotData" << endl;
+ //kdDebug(7007) << " data size : " << data.size() << endl;
+ assert(m_putJob);
+ if (!m_putJob) return; // Don't crash
+ m_getJob->suspend();
+ m_putJob->resume(); // Drink the beer
+ m_buffer = data;
+
+ // On the first set of data incoming, we tell the "put" slave about our
+ // decision about resuming
+ if (!m_resumeAnswerSent)
+ {
+ m_resumeAnswerSent = true;
+ //kdDebug(7007) << "FileCopyJob::slotData (first time) -> send resume answer " << m_canResume << endl;
+ m_putJob->slave()->sendResumeAnswer( m_canResume );
+ }
+}
+
+void FileCopyJob::slotDataReq( TDEIO::Job * , TQByteArray &data)
+{
+ //kdDebug(7007) << "FileCopyJob::slotDataReq" << endl;
+ if (!m_resumeAnswerSent && !m_getJob)
+ {
+ // This can't happen (except as a migration bug on 12/10/2000)
+ m_error = ERR_INTERNAL;
+ m_errorText = "'Put' job didn't send canResume or 'Get' job didn't send data!";
+ m_putJob->kill(true);
+ emitResult();
+ return;
+ }
+ if (m_getJob)
+ {
+ m_getJob->resume(); // Order more beer
+ m_putJob->suspend();
+ }
+ data = m_buffer;
+ m_buffer = TQByteArray();
+}
+
+void FileCopyJob::slotMimetype( TDEIO::Job*, const TQString& type )
+{
+ emit mimetype( this, type );
+}
+
+void FileCopyJob::slotResult( TDEIO::Job *job)
+{
+ //kdDebug(7007) << "FileCopyJob this=" << this << " ::slotResult(" << job << ")" << endl;
+ // Did job have an error ?
+ if ( job->error() )
+ {
+ if ((job == m_moveJob) && (job->error() == ERR_UNSUPPORTED_ACTION))
+ {
+ m_moveJob = 0;
+ startBestCopyMethod();
+ removeSubjob(job);
+ return;
+ }
+ else if ((job == m_copyJob) && (job->error() == ERR_UNSUPPORTED_ACTION))
+ {
+ m_copyJob = 0;
+ startDataPump();
+ removeSubjob(job);
+ return;
+ }
+ else if (job == m_getJob)
+ {
+ m_getJob = 0L;
+ if (m_putJob)
+ m_putJob->kill(true);
+ }
+ else if (job == m_putJob)
+ {
+ m_putJob = 0L;
+ if (m_getJob)
+ m_getJob->kill(true);
+ }
+ m_error = job->error();
+ m_errorText = job->errorText();
+ emitResult();
+ return;
+ }
+
+ if (job == m_moveJob)
+ {
+ m_moveJob = 0; // Finished
+ }
+
+ if (job == m_copyJob)
+ {
+ m_copyJob = 0;
+ if (m_move)
+ {
+ d->m_delJob = file_delete( m_src, false/*no GUI*/ ); // Delete source
+ addSubjob(d->m_delJob);
+ }
+ }
+
+ if (job == m_getJob)
+ {
+ m_getJob = 0; // No action required
+ if (m_putJob)
+ m_putJob->resume();
+ }
+
+ if (job == m_putJob)
+ {
+ //kdDebug(7007) << "FileCopyJob: m_putJob finished " << endl;
+ m_putJob = 0;
+ if (m_getJob)
+ {
+ kdWarning(7007) << "WARNING ! Get still going on..." << endl;
+ m_getJob->resume();
+ }
+ if (m_move)
+ {
+ d->m_delJob = file_delete( m_src, false/*no GUI*/ ); // Delete source
+ addSubjob(d->m_delJob);
+ }
+ }
+
+ if (job == d->m_delJob)
+ {
+ d->m_delJob = 0; // Finished
+ }
+ removeSubjob(job);
+}
+
+FileCopyJob *TDEIO::file_copy( const KURL& src, const KURL& dest, int permissions,
+ bool overwrite, bool resume, bool showProgressInfo)
+{
+ return new FileCopyJob( src, dest, permissions, false, overwrite, resume, showProgressInfo );
+}
+
+FileCopyJob *TDEIO::file_move( const KURL& src, const KURL& dest, int permissions,
+ bool overwrite, bool resume, bool showProgressInfo)
+{
+ return new FileCopyJob( src, dest, permissions, true, overwrite, resume, showProgressInfo );
+}
+
+SimpleJob *TDEIO::file_delete( const KURL& src, bool showProgressInfo)
+{
+ KIO_ARGS << src << TQ_INT8(true); // isFile
+ return new SimpleJob(src, CMD_DEL, packedArgs, showProgressInfo );
+}
+
+//////////
+
+// KDE 4: Make it const TQString & _prefix
+ListJob::ListJob(const KURL& u, bool showProgressInfo, bool _recursive, TQString _prefix, bool _includeHidden) :
+ SimpleJob(u, CMD_LISTDIR, TQByteArray(), showProgressInfo),
+ recursive(_recursive), includeHidden(_includeHidden), prefix(_prefix), m_processedEntries(0)
+{
+ // We couldn't set the args when calling the parent constructor,
+ // so do it now.
+ TQDataStream stream( m_packedArgs, IO_WriteOnly );
+ stream << u;
+}
+
+void ListJob::slotListEntries( const TDEIO::UDSEntryList& list )
+{
+ // Emit progress info (takes care of emit processedSize and percent)
+ m_processedEntries += list.count();
+ slotProcessedSize( m_processedEntries );
+
+ if (recursive) {
+ UDSEntryListConstIterator it = list.begin();
+ UDSEntryListConstIterator end = list.end();
+
+ for (; it != end; ++it) {
+ bool isDir = false;
+ bool isLink = false;
+ KURL itemURL;
+
+ UDSEntry::ConstIterator it2 = (*it).begin();
+ UDSEntry::ConstIterator end2 = (*it).end();
+ for( ; it2 != end2; it2++ ) {
+ switch( (*it2).m_uds ) {
+ case UDS_FILE_TYPE:
+ isDir = S_ISDIR((*it2).m_long);
+ break;
+ case UDS_NAME:
+ if( itemURL.isEmpty() ) {
+ itemURL = url();
+ itemURL.addPath( (*it2).m_str );
+ }
+ break;
+ case UDS_URL:
+ itemURL = (*it2).m_str;
+ break;
+ case UDS_LINK_DEST:
+ // This is a link !!! Don't follow !
+ isLink = !(*it2).m_str.isEmpty();
+ break;
+ default:
+ break;
+ }
+ }
+ if (isDir && !isLink) {
+ const TQString filename = itemURL.fileName();
+ // skip hidden dirs when listing if requested
+ if (filename != ".." && filename != "." && (includeHidden || filename[0] != '.')) {
+ ListJob *job = new ListJob(itemURL,
+ false /*no progress info!*/,
+ true /*recursive*/,
+ prefix + filename + "/",
+ includeHidden);
+ Scheduler::scheduleJob(job);
+ connect(job, TQT_SIGNAL(entries( TDEIO::Job *,
+ const TDEIO::UDSEntryList& )),
+ TQT_SLOT( gotEntries( TDEIO::Job*,
+ const TDEIO::UDSEntryList& )));
+ addSubjob(job);
+ }
+ }
+ }
+ }
+
+ // Not recursive, or top-level of recursive listing : return now (send . and .. as well)
+ // exclusion of hidden files also requires the full sweep, but the case for full-listing
+ // a single dir is probably common enough to justify the shortcut
+ if (prefix.isNull() && includeHidden) {
+ emit entries(this, list);
+ } else {
+ // cull the unwanted hidden dirs and/or parent dir references from the listing, then emit that
+ UDSEntryList newlist;
+
+ UDSEntryListConstIterator it = list.begin();
+ UDSEntryListConstIterator end = list.end();
+ for (; it != end; ++it) {
+
+ UDSEntry newone = *it;
+ UDSEntry::Iterator it2 = newone.begin();
+ TQString filename;
+ for( ; it2 != newone.end(); it2++ ) {
+ if ((*it2).m_uds == UDS_NAME) {
+ filename = (*it2).m_str;
+ (*it2).m_str = prefix + filename;
+ }
+ }
+ // Avoid returning entries like subdir/. and subdir/.., but include . and .. for
+ // the toplevel dir, and skip hidden files/dirs if that was requested
+ if ( (prefix.isNull() || (filename != ".." && filename != ".") )
+ && (includeHidden || (filename[0] != '.') ) )
+ newlist.append(newone);
+ }
+
+ emit entries(this, newlist);
+ }
+}
+
+void ListJob::gotEntries(TDEIO::Job *, const TDEIO::UDSEntryList& list )
+{
+ // Forward entries received by subjob - faking we received them ourselves
+ emit entries(this, list);
+}
+
+void ListJob::slotResult( TDEIO::Job * job )
+{
+ // If we can't list a subdir, the result is still ok
+ // This is why we override Job::slotResult() - to skip error checking
+ removeSubjob( job );
+}
+
+void ListJob::slotRedirection( const KURL & url )
+{
+ if (!kapp->authorizeURLAction("redirect", m_url, url))
+ {
+ kdWarning(7007) << "ListJob: Redirection from " << m_url << " to " << url << " REJECTED!" << endl;
+ return;
+ }
+ m_redirectionURL = url; // We'll remember that when the job finishes
+ if (m_url.hasUser() && !url.hasUser() && (m_url.host().lower() == url.host().lower()))
+ m_redirectionURL.setUser(m_url.user()); // Preserve user
+ emit redirection( this, m_redirectionURL );
+}
+
+void ListJob::slotFinished()
+{
+ // Support for listing archives as directories
+ if ( m_error == TDEIO::ERR_IS_FILE && m_url.isLocalFile() ) {
+ KMimeType::Ptr ptr = KMimeType::findByURL( m_url, 0, true, true );
+ if ( ptr ) {
+ TQString proto = ptr->property("X-TDE-LocalProtocol").toString();
+ if ( !proto.isEmpty() && KProtocolInfo::isKnownProtocol(proto) ) {
+ m_redirectionURL = m_url;
+ m_redirectionURL.setProtocol( proto );
+ m_error = 0;
+ emit redirection(this,m_redirectionURL);
+ }
+ }
+ }
+ if ( m_redirectionURL.isEmpty() || !m_redirectionURL.isValid() || m_error ) {
+ // Return slave to the scheduler
+ SimpleJob::slotFinished();
+ } else {
+
+ //kdDebug(7007) << "ListJob: Redirection to " << m_redirectionURL << endl;
+ if (queryMetaData("permanent-redirect")=="true")
+ emit permanentRedirection(this, m_url, m_redirectionURL);
+ m_url = m_redirectionURL;
+ m_redirectionURL = KURL();
+ m_packedArgs.truncate(0);
+ TQDataStream stream( m_packedArgs, IO_WriteOnly );
+ stream << m_url;
+
+ // Return slave to the scheduler
+ slaveDone();
+ Scheduler::doJob(this);
+ }
+}
+
+void ListJob::slotMetaData( const TDEIO::MetaData &_metaData) {
+ SimpleJob::slotMetaData(_metaData);
+ storeSSLSessionFromJob(m_redirectionURL);
+}
+
+ListJob *TDEIO::listDir( const KURL& url, bool showProgressInfo, bool includeHidden )
+{
+ ListJob * job = new ListJob(url, showProgressInfo,false,TQString::null,includeHidden);
+ return job;
+}
+
+ListJob *TDEIO::listRecursive( const KURL& url, bool showProgressInfo, bool includeHidden )
+{
+ ListJob * job = new ListJob(url, showProgressInfo, true,TQString::null,includeHidden);
+ return job;
+}
+
+void ListJob::setUnrestricted(bool unrestricted)
+{
+ if (unrestricted)
+ extraFlags() |= EF_ListJobUnrestricted;
+ else
+ extraFlags() &= ~EF_ListJobUnrestricted;
+}
+
+void ListJob::start(Slave *slave)
+{
+ if (kapp && !kapp->authorizeURLAction("list", m_url, m_url) && !(extraFlags() & EF_ListJobUnrestricted))
+ {
+ m_error = ERR_ACCESS_DENIED;
+ m_errorText = m_url.url();
+ TQTimer::singleShot(0, this, TQT_SLOT(slotFinished()) );
+ return;
+ }
+ connect( slave, TQT_SIGNAL( listEntries( const TDEIO::UDSEntryList& )),
+ TQT_SLOT( slotListEntries( const TDEIO::UDSEntryList& )));
+ connect( slave, TQT_SIGNAL( totalSize( TDEIO::filesize_t ) ),
+ TQT_SLOT( slotTotalSize( TDEIO::filesize_t ) ) );
+ connect( slave, TQT_SIGNAL( redirection(const KURL &) ),
+ TQT_SLOT( slotRedirection(const KURL &) ) );
+
+ SimpleJob::start(slave);
+}
+
+class CopyJob::CopyJobPrivate
+{
+public:
+ CopyJobPrivate() {
+ m_defaultPermissions = false;
+ m_bURLDirty = false;
+ }
+ // This is the dest URL that was initially given to CopyJob
+ // It is copied into m_dest, which can be changed for a given src URL
+ // (when using the RENAME dialog in slotResult),
+ // and which will be reset for the next src URL.
+ KURL m_globalDest;
+ // The state info about that global dest
+ CopyJob::DestinationState m_globalDestinationState;
+ // See setDefaultPermissions
+ bool m_defaultPermissions;
+ // Whether URLs changed (and need to be emitted by the next slotReport call)
+ bool m_bURLDirty;
+ // Used after copying all the files into the dirs, to set mtime (TODO: and permissions?)
+ // after the copy is done
+ TQValueList<CopyInfo> m_directoriesCopied;
+};
+
+CopyJob::CopyJob( const KURL::List& src, const KURL& dest, CopyMode mode, bool asMethod, bool showProgressInfo )
+ : Job(showProgressInfo), m_mode(mode), m_asMethod(asMethod),
+ destinationState(DEST_NOT_STATED), state(STATE_STATING),
+ m_totalSize(0), m_processedSize(0), m_fileProcessedSize(0),
+ m_processedFiles(0), m_processedDirs(0),
+ m_srcList(src), m_currentStatSrc(m_srcList.begin()),
+ m_bCurrentOperationIsLink(false), m_bSingleFileCopy(false), m_bOnlyRenames(mode==Move),
+ m_dest(dest), m_bAutoSkip( false ), m_bOverwriteAll( false ),
+ m_conflictError(0), m_reportTimer(0)
+{
+ d = new CopyJobPrivate;
+ d->m_globalDest = dest;
+ d->m_globalDestinationState = destinationState;
+
+ if ( showProgressInfo ) {
+ connect( this, TQT_SIGNAL( totalFiles( TDEIO::Job*, unsigned long ) ),
+ Observer::self(), TQT_SLOT( slotTotalFiles( TDEIO::Job*, unsigned long ) ) );
+
+ connect( this, TQT_SIGNAL( totalDirs( TDEIO::Job*, unsigned long ) ),
+ Observer::self(), TQT_SLOT( slotTotalDirs( TDEIO::Job*, unsigned long ) ) );
+ }
+ TQTimer::singleShot(0, this, TQT_SLOT(slotStart()));
+ /**
+ States:
+ STATE_STATING for the dest
+ STATE_STATING for each src url (statNextSrc)
+ for each: if dir -> STATE_LISTING (filling 'dirs' and 'files')
+ but if direct rename possible: STATE_RENAMING instead.
+ STATE_CREATING_DIRS (createNextDir, iterating over 'dirs')
+ if conflict: STATE_CONFLICT_CREATING_DIRS
+ STATE_COPYING_FILES (copyNextFile, iterating over 'files')
+ if conflict: STATE_CONFLICT_COPYING_FILES
+ STATE_DELETING_DIRS (deleteNextDir) (if moving)
+ STATE_SETTING_DIR_ATTRIBUTES (setNextDirAttribute, iterating over d->m_directoriesCopied)
+ done.
+ */
+}
+
+CopyJob::~CopyJob()
+{
+ delete d;
+}
+
+void CopyJob::slotStart()
+{
+ /**
+ We call the functions directly instead of using signals.
+ Calling a function via a signal takes approx. 65 times the time
+ compared to calling it directly (at least on my machine). aleXXX
+ */
+ m_reportTimer = new TQTimer(this);
+
+ connect(m_reportTimer,TQT_SIGNAL(timeout()),this,TQT_SLOT(slotReport()));
+ m_reportTimer->start(REPORT_TIMEOUT,false);
+
+ // Stat the dest
+ TDEIO::Job * job = TDEIO::stat( m_dest, false, 2, false );
+ //kdDebug(7007) << "CopyJob:stating the dest " << m_dest << endl;
+ addSubjob(job);
+}
+
+// For unit test purposes
+TDEIO_EXPORT bool kio_resolve_local_urls = true;
+
+void CopyJob::slotResultStating( Job *job )
+{
+ //kdDebug(7007) << "CopyJob::slotResultStating" << endl;
+ // Was there an error while stating the src ?
+ if (job->error() && destinationState != DEST_NOT_STATED )
+ {
+ KURL srcurl = ((SimpleJob*)job)->url();
+ if ( !srcurl.isLocalFile() )
+ {
+ // Probably : src doesn't exist. Well, over some protocols (e.g. FTP)
+ // this info isn't really reliable (thanks to MS FTP servers).
+ // We'll assume a file, and try to download anyway.
+ kdDebug(7007) << "Error while stating source. Activating hack" << endl;
+ subjobs.remove( job );
+ assert ( subjobs.isEmpty() ); // We should have only one job at a time ...
+ struct CopyInfo info;
+ info.permissions = (mode_t) -1;
+ info.mtime = (time_t) -1;
+ info.ctime = (time_t) -1;
+ info.size = (TDEIO::filesize_t)-1;
+ info.uSource = srcurl;
+ info.uDest = m_dest;
+ // Append filename or dirname to destination URL, if allowed
+ if ( destinationState == DEST_IS_DIR && !m_asMethod )
+ info.uDest.addPath( srcurl.fileName() );
+
+ files.append( info );
+ statNextSrc();
+ return;
+ }
+ // Local file. If stat fails, the file definitely doesn't exist.
+ Job::slotResult( job ); // will set the error and emit result(this)
+ return;
+ }
+
+ // Is it a file or a dir ? Does it have a local path?
+ UDSEntry entry = ((StatJob*)job)->statResult();
+ bool bDir = false;
+ bool bLink = false;
+ TQString sName;
+ TQString sLocalPath;
+ UDSEntry::ConstIterator it2 = entry.begin();
+ for( ; it2 != entry.end(); it2++ ) {
+ if ( ((*it2).m_uds) == UDS_FILE_TYPE )
+ bDir = S_ISDIR( (mode_t)(*it2).m_long );
+ else if ( ((*it2).m_uds) == UDS_LINK_DEST )
+ bLink = !((*it2).m_str.isEmpty());
+ else if ( ((*it2).m_uds) == UDS_NAME )
+ sName = (*it2).m_str;
+ else if ( ((*it2).m_uds) == UDS_LOCAL_PATH )
+ sLocalPath = (*it2).m_str;
+ }
+
+ if ( destinationState == DEST_NOT_STATED )
+ // we were stating the dest
+ {
+ if (job->error())
+ destinationState = DEST_DOESNT_EXIST;
+ else {
+ // Treat symlinks to dirs as dirs here, so no test on bLink
+ destinationState = bDir ? DEST_IS_DIR : DEST_IS_FILE;
+ //kdDebug(7007) << "CopyJob::slotResultStating dest is dir:" << bDir << endl;
+ }
+ const bool isGlobalDest = m_dest == d->m_globalDest;
+ if ( isGlobalDest )
+ d->m_globalDestinationState = destinationState;
+
+ if ( !sLocalPath.isEmpty() && kio_resolve_local_urls ) {
+ m_dest = KURL();
+ m_dest.setPath(sLocalPath);
+ if ( isGlobalDest )
+ d->m_globalDest = m_dest;
+ }
+
+ subjobs.remove( job );
+ assert ( subjobs.isEmpty() );
+
+ // After knowing what the dest is, we can start stat'ing the first src.
+ statCurrentSrc();
+ return;
+ }
+ // We were stating the current source URL
+ m_currentDest = m_dest; // used by slotEntries
+ // Create a dummy list with it, for slotEntries
+ UDSEntryList lst;
+ lst.append(entry);
+
+ // There 6 cases, and all end up calling slotEntries(job, lst) first :
+ // 1 - src is a dir, destination is a directory,
+ // slotEntries will append the source-dir-name to the destination
+ // 2 - src is a dir, destination is a file, ERROR (done later on)
+ // 3 - src is a dir, destination doesn't exist, then it's the destination dirname,
+ // so slotEntries will use it as destination.
+
+ // 4 - src is a file, destination is a directory,
+ // slotEntries will append the filename to the destination.
+ // 5 - src is a file, destination is a file, m_dest is the exact destination name
+ // 6 - src is a file, destination doesn't exist, m_dest is the exact destination name
+ // Tell slotEntries not to alter the src url
+ m_bCurrentSrcIsDir = false;
+ slotEntries(job, lst);
+
+ KURL srcurl;
+ if (!sLocalPath.isEmpty())
+ srcurl.setPath(sLocalPath);
+ else
+ srcurl = ((SimpleJob*)job)->url();
+
+ subjobs.remove( job );
+ assert ( subjobs.isEmpty() ); // We should have only one job at a time ...
+
+ if ( bDir
+ && !bLink // treat symlinks as files (no recursion)
+ && m_mode != Link ) // No recursion in Link mode either.
+ {
+ //kdDebug(7007) << " Source is a directory " << endl;
+
+ m_bCurrentSrcIsDir = true; // used by slotEntries
+ if ( destinationState == DEST_IS_DIR ) // (case 1)
+ {
+ if ( !m_asMethod )
+ {
+ // Use <desturl>/<directory_copied> as destination, from now on
+ TQString directory = srcurl.fileName();
+ if ( !sName.isEmpty() && KProtocolInfo::fileNameUsedForCopying( srcurl ) == KProtocolInfo::Name )
+ {
+ directory = sName;
+ }
+ m_currentDest.addPath( directory );
+ }
+ }
+ else if ( destinationState == DEST_IS_FILE ) // (case 2)
+ {
+ m_error = ERR_IS_FILE;
+ m_errorText = m_dest.prettyURL();
+ emitResult();
+ return;
+ }
+ else // (case 3)
+ {
+ // otherwise dest is new name for toplevel dir
+ // so the destination exists, in fact, from now on.
+ // (This even works with other src urls in the list, since the
+ // dir has effectively been created)
+ destinationState = DEST_IS_DIR;
+ if ( m_dest == d->m_globalDest )
+ d->m_globalDestinationState = destinationState;
+ }
+
+ startListing( srcurl );
+ }
+ else
+ {
+ //kdDebug(7007) << " Source is a file (or a symlink), or we are linking -> no recursive listing " << endl;
+ statNextSrc();
+ }
+}
+
+void CopyJob::slotReport()
+{
+ // If showProgressInfo was set, m_progressId is > 0.
+ Observer * observer = m_progressId ? Observer::self() : 0L;
+ switch (state) {
+ case STATE_COPYING_FILES:
+ emit processedFiles( this, m_processedFiles );
+ if (observer) observer->slotProcessedFiles(this, m_processedFiles);
+ if (d->m_bURLDirty)
+ {
+ // Only emit urls when they changed. This saves time, and fixes #66281
+ d->m_bURLDirty = false;
+ if (m_mode==Move)
+ {
+ if (observer) observer->slotMoving( this, m_currentSrcURL, m_currentDestURL);
+ emit moving( this, m_currentSrcURL, m_currentDestURL);
+ }
+ else if (m_mode==Link)
+ {
+ if (observer) observer->slotCopying( this, m_currentSrcURL, m_currentDestURL ); // we don't have a slotLinking
+ emit linking( this, m_currentSrcURL.path(), m_currentDestURL );
+ }
+ else
+ {
+ if (observer) observer->slotCopying( this, m_currentSrcURL, m_currentDestURL );
+ emit copying( this, m_currentSrcURL, m_currentDestURL );
+ }
+ }
+ break;
+
+ case STATE_CREATING_DIRS:
+ if (observer) observer->slotProcessedDirs( this, m_processedDirs );
+ emit processedDirs( this, m_processedDirs );
+ if (d->m_bURLDirty)
+ {
+ d->m_bURLDirty = false;
+ emit creatingDir( this, m_currentDestURL );
+ if (observer) observer->slotCreatingDir( this, m_currentDestURL);
+ }
+ break;
+
+ case STATE_STATING:
+ case STATE_LISTING:
+ if (d->m_bURLDirty)
+ {
+ d->m_bURLDirty = false;
+ if (observer) observer->slotCopying( this, m_currentSrcURL, m_currentDestURL );
+ }
+ emit totalSize( this, m_totalSize );
+ emit totalFiles( this, files.count() );
+ emit totalDirs( this, dirs.count() );
+ break;
+
+ default:
+ break;
+ }
+}
+
+void CopyJob::slotEntries(TDEIO::Job* job, const UDSEntryList& list)
+{
+ UDSEntryListConstIterator it = list.begin();
+ UDSEntryListConstIterator end = list.end();
+ for (; it != end; ++it) {
+ UDSEntry::ConstIterator it2 = (*it).begin();
+ struct CopyInfo info;
+ info.permissions = -1;
+ info.mtime = (time_t) -1;
+ info.ctime = (time_t) -1;
+ info.size = (TDEIO::filesize_t)-1;
+ TQString displayName;
+ KURL url;
+ TQString localPath;
+ bool isDir = false;
+ for( ; it2 != (*it).end(); it2++ ) {
+ switch ((*it2).m_uds) {
+ case UDS_FILE_TYPE:
+ //info.type = (mode_t)((*it2).m_long);
+ isDir = S_ISDIR( (mode_t)((*it2).m_long) );
+ break;
+ case UDS_NAME: // recursive listing, displayName can be a/b/c/d
+ displayName = (*it2).m_str;
+ break;
+ case UDS_URL: // optional
+ url = KURL((*it2).m_str);
+ break;
+ case UDS_LOCAL_PATH:
+ localPath = (*it2).m_str;
+ break;
+ case UDS_LINK_DEST:
+ info.linkDest = (*it2).m_str;
+ break;
+ case UDS_ACCESS:
+ info.permissions = ((*it2).m_long);
+ break;
+ case UDS_SIZE:
+ info.size = (TDEIO::filesize_t)((*it2).m_long);
+ m_totalSize += info.size;
+ break;
+ case UDS_MODIFICATION_TIME:
+ info.mtime = (time_t)((*it2).m_long);
+ break;
+ case UDS_CREATION_TIME:
+ info.ctime = (time_t)((*it2).m_long);
+ default:
+ break;
+ }
+ }
+ if (displayName != ".." && displayName != ".")
+ {
+ bool hasCustomURL = !url.isEmpty() || !localPath.isEmpty();
+ if( !hasCustomURL ) {
+ // Make URL from displayName
+ url = ((SimpleJob *)job)->url();
+ if ( m_bCurrentSrcIsDir ) { // Only if src is a directory. Otherwise uSource is fine as is
+ //kdDebug(7007) << "adding path " << displayName << endl;
+ url.addPath( displayName );
+ }
+ }
+ //kdDebug(7007) << "displayName=" << displayName << " url=" << url << endl;
+ if (!localPath.isEmpty() && kio_resolve_local_urls) {
+ url = KURL();
+ url.setPath(localPath);
+ }
+
+ info.uSource = url;
+ info.uDest = m_currentDest;
+ //kdDebug(7007) << " uSource=" << info.uSource << " uDest(1)=" << info.uDest << endl;
+ // Append filename or dirname to destination URL, if allowed
+ if ( destinationState == DEST_IS_DIR &&
+ // "copy/move as <foo>" means 'foo' is the dest for the base srcurl
+ // (passed here during stating) but not its children (during listing)
+ ( ! ( m_asMethod && state == STATE_STATING ) ) )
+ {
+ TQString destFileName;
+ if ( hasCustomURL &&
+ KProtocolInfo::fileNameUsedForCopying( url ) == KProtocolInfo::FromURL ) {
+ //destFileName = url.fileName(); // Doesn't work for recursive listing
+ // Count the number of prefixes used by the recursive listjob
+ int numberOfSlashes = displayName.contains( '/' ); // don't make this a find()!
+ TQString path = url.path();
+ int pos = 0;
+ for ( int n = 0; n < numberOfSlashes + 1; ++n ) {
+ pos = path.findRev( '/', pos - 1 );
+ if ( pos == -1 ) { // error
+ kdWarning(7007) << "tdeioslave bug: not enough slashes in UDS_URL " << path << " - looking for " << numberOfSlashes << " slashes" << endl;
+ break;
+ }
+ }
+ if ( pos >= 0 ) {
+ destFileName = path.mid( pos + 1 );
+ }
+
+ } else { // destination filename taken from UDS_NAME
+ destFileName = displayName;
+ }
+
+ // Here we _really_ have to add some filename to the dest.
+ // Otherwise, we end up with e.g. dest=..../Desktop/ itself.
+ // (This can happen when dropping a link to a webpage with no path)
+ if ( destFileName.isEmpty() )
+ destFileName = TDEIO::encodeFileName( info.uSource.prettyURL() );
+
+ //kdDebug(7007) << " adding destFileName=" << destFileName << endl;
+ info.uDest.addPath( destFileName );
+ }
+ //kdDebug(7007) << " uDest(2)=" << info.uDest << endl;
+ //kdDebug(7007) << " " << info.uSource << " -> " << info.uDest << endl;
+ if ( info.linkDest.isEmpty() && isDir && m_mode != Link ) // Dir
+ {
+ dirs.append( info ); // Directories
+ if (m_mode == Move)
+ dirsToRemove.append( info.uSource );
+ }
+ else {
+ files.append( info ); // Files and any symlinks
+ }
+ }
+ }
+}
+
+void CopyJob::skipSrc()
+{
+ m_dest = d->m_globalDest;
+ destinationState = d->m_globalDestinationState;
+ ++m_currentStatSrc;
+ skip( m_currentSrcURL );
+ statCurrentSrc();
+}
+
+void CopyJob::statNextSrc()
+{
+ /* Revert to the global destination, the one that applies to all source urls.
+ * Imagine you copy the items a b and c into /d, but /d/b exists so the user uses "Rename" to put it in /foo/b instead.
+ * m_dest is /foo/b for b, but we have to revert to /d for item c and following.
+ */
+ m_dest = d->m_globalDest;
+ destinationState = d->m_globalDestinationState;
+ ++m_currentStatSrc;
+ statCurrentSrc();
+}
+
+void CopyJob::statCurrentSrc()
+{
+ if ( m_currentStatSrc != m_srcList.end() )
+ {
+ m_currentSrcURL = (*m_currentStatSrc);
+ d->m_bURLDirty = true;
+ if ( m_mode == Link )
+ {
+ // Skip the "stating the source" stage, we don't need it for linking
+ m_currentDest = m_dest;
+ struct CopyInfo info;
+ info.permissions = -1;
+ info.mtime = (time_t) -1;
+ info.ctime = (time_t) -1;
+ info.size = (TDEIO::filesize_t)-1;
+ info.uSource = m_currentSrcURL;
+ info.uDest = m_currentDest;
+ // Append filename or dirname to destination URL, if allowed
+ if ( destinationState == DEST_IS_DIR && !m_asMethod )
+ {
+ if (
+ (m_currentSrcURL.protocol() == info.uDest.protocol()) &&
+ (m_currentSrcURL.host() == info.uDest.host()) &&
+ (m_currentSrcURL.port() == info.uDest.port()) &&
+ (m_currentSrcURL.user() == info.uDest.user()) &&
+ (m_currentSrcURL.pass() == info.uDest.pass()) )
+ {
+ // This is the case of creating a real symlink
+ info.uDest.addPath( m_currentSrcURL.fileName() );
+ }
+ else
+ {
+ // Different protocols, we'll create a .desktop file
+ // We have to change the extension anyway, so while we're at it,
+ // name the file like the URL
+ info.uDest.addPath( TDEIO::encodeFileName( m_currentSrcURL.prettyURL() )+".desktop" );
+ }
+ }
+ files.append( info ); // Files and any symlinks
+ statNextSrc(); // we could use a loop instead of a recursive call :)
+ return;
+ }
+ else if ( m_mode == Move && (
+ // Don't go renaming right away if we need a stat() to find out the destination filename
+ KProtocolInfo::fileNameUsedForCopying( m_currentSrcURL ) == KProtocolInfo::FromURL ||
+ destinationState != DEST_IS_DIR || m_asMethod )
+ )
+ {
+ // If moving, before going for the full stat+[list+]copy+del thing, try to rename
+ // The logic is pretty similar to FileCopyJob::slotStart()
+ if ( (m_currentSrcURL.protocol() == m_dest.protocol()) &&
+ (m_currentSrcURL.host() == m_dest.host()) &&
+ (m_currentSrcURL.port() == m_dest.port()) &&
+ (m_currentSrcURL.user() == m_dest.user()) &&
+ (m_currentSrcURL.pass() == m_dest.pass()) )
+ {
+ startRenameJob( m_currentSrcURL );
+ return;
+ }
+ else if ( m_currentSrcURL.isLocalFile() && KProtocolInfo::canRenameFromFile( m_dest ) )
+ {
+ startRenameJob( m_dest );
+ return;
+ }
+ else if ( m_dest.isLocalFile() && KProtocolInfo::canRenameToFile( m_currentSrcURL ) )
+ {
+ startRenameJob( m_currentSrcURL );
+ return;
+ }
+ }
+
+ // if the file system doesn't support deleting, we do not even stat
+ if (m_mode == Move && !KProtocolInfo::supportsDeleting(m_currentSrcURL)) {
+ TQGuardedPtr<CopyJob> that = this;
+ if (isInteractive())
+ KMessageBox::information( 0, buildErrorString(ERR_CANNOT_DELETE, m_currentSrcURL.prettyURL()));
+ if (that)
+ statNextSrc(); // we could use a loop instead of a recursive call :)
+ return;
+ }
+
+ // Stat the next src url
+ Job * job = TDEIO::stat( m_currentSrcURL, true, 2, false );
+ //kdDebug(7007) << "TDEIO::stat on " << m_currentSrcURL << endl;
+ state = STATE_STATING;
+ addSubjob(job);
+ m_currentDestURL=m_dest;
+ m_bOnlyRenames = false;
+ d->m_bURLDirty = true;
+ }
+ else
+ {
+ // Finished the stat'ing phase
+ // First make sure that the totals were correctly emitted
+ state = STATE_STATING;
+ d->m_bURLDirty = true;
+ slotReport();
+ if (!dirs.isEmpty())
+ emit aboutToCreate( this, dirs );
+ if (!files.isEmpty())
+ emit aboutToCreate( this, files );
+ // Check if we are copying a single file
+ m_bSingleFileCopy = ( files.count() == 1 && dirs.isEmpty() );
+ // Then start copying things
+ state = STATE_CREATING_DIRS;
+ createNextDir();
+ }
+}
+
+void CopyJob::startRenameJob( const KURL& slave_url )
+{
+ KURL dest = m_dest;
+ // Append filename or dirname to destination URL, if allowed
+ if ( destinationState == DEST_IS_DIR && !m_asMethod )
+ dest.addPath( m_currentSrcURL.fileName() );
+ kdDebug(7007) << "This seems to be a suitable case for trying to rename before stat+[list+]copy+del" << endl;
+ state = STATE_RENAMING;
+
+ struct CopyInfo info;
+ info.permissions = -1;
+ info.mtime = (time_t) -1;
+ info.ctime = (time_t) -1;
+ info.size = (TDEIO::filesize_t)-1;
+ info.uSource = m_currentSrcURL;
+ info.uDest = dest;
+ TQValueList<CopyInfo> files;
+ files.append(info);
+ emit aboutToCreate( this, files );
+
+ KIO_ARGS << m_currentSrcURL << dest << (TQ_INT8) false /*no overwrite*/;
+ SimpleJob * newJob = new SimpleJob(slave_url, CMD_RENAME, packedArgs, false);
+ Scheduler::scheduleJob(newJob);
+ addSubjob( newJob );
+ if ( m_currentSrcURL.directory() != dest.directory() ) // For the user, moving isn't renaming. Only renaming is.
+ m_bOnlyRenames = false;
+}
+
+void CopyJob::startListing( const KURL & src )
+{
+ state = STATE_LISTING;
+ d->m_bURLDirty = true;
+ ListJob * newjob = listRecursive( src, false );
+ newjob->setUnrestricted(true);
+ connect(newjob, TQT_SIGNAL(entries( TDEIO::Job *,
+ const TDEIO::UDSEntryList& )),
+ TQT_SLOT( slotEntries( TDEIO::Job*,
+ const TDEIO::UDSEntryList& )));
+ addSubjob( newjob );
+}
+
+void CopyJob::skip( const KURL & sourceUrl )
+{
+ // Check if this is one if toplevel sources
+ // If yes, remove it from m_srcList, for a correct FilesRemoved() signal
+ //kdDebug(7007) << "CopyJob::skip: looking for " << sourceUrl << endl;
+ KURL::List::Iterator sit = m_srcList.find( sourceUrl );
+ if ( sit != m_srcList.end() )
+ {
+ //kdDebug(7007) << "CopyJob::skip: removing " << sourceUrl << " from list" << endl;
+ m_srcList.remove( sit );
+ }
+ dirsToRemove.remove( sourceUrl );
+}
+
+bool CopyJob::shouldOverwrite( const TQString& path ) const
+{
+ if ( m_bOverwriteAll )
+ return true;
+ TQStringList::ConstIterator sit = m_overwriteList.begin();
+ for( ; sit != m_overwriteList.end(); ++sit )
+ if ( path.startsWith( *sit ) )
+ return true;
+ return false;
+}
+
+bool CopyJob::shouldSkip( const TQString& path ) const
+{
+ TQStringList::ConstIterator sit = m_skipList.begin();
+ for( ; sit != m_skipList.end(); ++sit )
+ if ( path.startsWith( *sit ) )
+ return true;
+ return false;
+}
+
+void CopyJob::slotResultCreatingDirs( Job * job )
+{
+ // The dir we are trying to create:
+ TQValueList<CopyInfo>::Iterator it = dirs.begin();
+ // Was there an error creating a dir ?
+ if ( job->error() )
+ {
+ m_conflictError = job->error();
+ if ( (m_conflictError == ERR_DIR_ALREADY_EXIST)
+ || (m_conflictError == ERR_FILE_ALREADY_EXIST) ) // can't happen?
+ {
+ KURL oldURL = ((SimpleJob*)job)->url();
+ // Should we skip automatically ?
+ if ( m_bAutoSkip ) {
+ // We don't want to copy files in this directory, so we put it on the skip list
+ m_skipList.append( oldURL.path( 1 ) );
+ skip( oldURL );
+ dirs.remove( it ); // Move on to next dir
+ } else {
+ // Did the user choose to overwrite already?
+ const TQString destFile = (*it).uDest.path();
+ if ( shouldOverwrite( destFile ) ) { // overwrite => just skip
+ emit copyingDone( this, ( *it ).uSource, ( *it ).uDest, true /* directory */, false /* renamed */ );
+ dirs.remove( it ); // Move on to next dir
+ } else {
+ if ( !isInteractive() ) {
+ Job::slotResult( job ); // will set the error and emit result(this)
+ return;
+ }
+
+ assert( ((SimpleJob*)job)->url().url() == (*it).uDest.url() );
+ subjobs.remove( job );
+ assert ( subjobs.isEmpty() ); // We should have only one job at a time ...
+
+ // We need to stat the existing dir, to get its last-modification time
+ KURL existingDest( (*it).uDest );
+ SimpleJob * newJob = TDEIO::stat( existingDest, false, 2, false );
+ Scheduler::scheduleJob(newJob);
+ kdDebug(7007) << "TDEIO::stat for resolving conflict on " << existingDest << endl;
+ state = STATE_CONFLICT_CREATING_DIRS;
+ addSubjob(newJob);
+ return; // Don't move to next dir yet !
+ }
+ }
+ }
+ else
+ {
+ // Severe error, abort
+ Job::slotResult( job ); // will set the error and emit result(this)
+ return;
+ }
+ }
+ else // no error : remove from list, to move on to next dir
+ {
+ //this is required for the undo feature
+ emit copyingDone( this, (*it).uSource, (*it).uDest, true, false );
+ d->m_directoriesCopied.append( *it );
+ dirs.remove( it );
+ }
+
+ m_processedDirs++;
+ //emit processedDirs( this, m_processedDirs );
+ subjobs.remove( job );
+ assert( subjobs.isEmpty() ); // We should have only one job at a time ...
+ createNextDir();
+}
+
+void CopyJob::slotResultConflictCreatingDirs( TDEIO::Job * job )
+{
+ // We come here after a conflict has been detected and we've stated the existing dir
+
+ // The dir we were trying to create:
+ TQValueList<CopyInfo>::Iterator it = dirs.begin();
+ // Its modification time:
+ time_t destmtime = (time_t)-1;
+ time_t destctime = (time_t)-1;
+ TDEIO::filesize_t destsize = 0;
+ TQString linkDest;
+
+ UDSEntry entry = ((TDEIO::StatJob*)job)->statResult();
+ TDEIO::UDSEntry::ConstIterator it2 = entry.begin();
+ for( ; it2 != entry.end(); it2++ ) {
+ switch ((*it2).m_uds) {
+ case UDS_MODIFICATION_TIME:
+ destmtime = (time_t)((*it2).m_long);
+ break;
+ case UDS_CREATION_TIME:
+ destctime = (time_t)((*it2).m_long);
+ break;
+ case UDS_SIZE:
+ destsize = (*it2).m_long;
+ break;
+ case UDS_LINK_DEST:
+ linkDest = (*it2).m_str;
+ break;
+ }
+ }
+ subjobs.remove( job );
+ assert ( subjobs.isEmpty() ); // We should have only one job at a time ...
+
+ // Always multi and skip (since there are files after that)
+ RenameDlg_Mode mode = (RenameDlg_Mode)( M_MULTI | M_SKIP );
+ // Overwrite only if the existing thing is a dir (no chance with a file)
+ if ( m_conflictError == ERR_DIR_ALREADY_EXIST )
+ {
+ if( (*it).uSource == (*it).uDest ||
+ ((*it).uSource.protocol() == (*it).uDest.protocol() &&
+ (*it).uSource.path(-1) == linkDest) )
+ mode = (RenameDlg_Mode)( mode | M_OVERWRITE_ITSELF);
+ else
+ mode = (RenameDlg_Mode)( mode | M_OVERWRITE );
+ }
+
+ TQString existingDest = (*it).uDest.path();
+ TQString newPath;
+ if (m_reportTimer)
+ m_reportTimer->stop();
+ RenameDlg_Result r = Observer::self()->open_RenameDlg( this, i18n("Folder Already Exists"),
+ (*it).uSource.url(),
+ (*it).uDest.url(),
+ mode, newPath,
+ (*it).size, destsize,
+ (*it).ctime, destctime,
+ (*it).mtime, destmtime );
+ if (m_reportTimer)
+ m_reportTimer->start(REPORT_TIMEOUT,false);
+ switch ( r ) {
+ case R_CANCEL:
+ m_error = ERR_USER_CANCELED;
+ emitResult();
+ return;
+ case R_RENAME:
+ {
+ TQString oldPath = (*it).uDest.path( 1 );
+ KURL newUrl( (*it).uDest );
+ newUrl.setPath( newPath );
+ emit renamed( this, (*it).uDest, newUrl ); // for e.g. kpropsdlg
+
+ // Change the current one and strip the trailing '/'
+ (*it).uDest.setPath( newUrl.path( -1 ) );
+ newPath = newUrl.path( 1 ); // With trailing slash
+ TQValueList<CopyInfo>::Iterator renamedirit = it;
+ ++renamedirit;
+ // Change the name of subdirectories inside the directory
+ for( ; renamedirit != dirs.end() ; ++renamedirit )
+ {
+ TQString path = (*renamedirit).uDest.path();
+ if ( path.left(oldPath.length()) == oldPath ) {
+ TQString n = path;
+ n.replace( 0, oldPath.length(), newPath );
+ kdDebug(7007) << "dirs list: " << (*renamedirit).uSource.path()
+ << " was going to be " << path
+ << ", changed into " << n << endl;
+ (*renamedirit).uDest.setPath( n );
+ }
+ }
+ // Change filenames inside the directory
+ TQValueList<CopyInfo>::Iterator renamefileit = files.begin();
+ for( ; renamefileit != files.end() ; ++renamefileit )
+ {
+ TQString path = (*renamefileit).uDest.path();
+ if ( path.left(oldPath.length()) == oldPath ) {
+ TQString n = path;
+ n.replace( 0, oldPath.length(), newPath );
+ kdDebug(7007) << "files list: " << (*renamefileit).uSource.path()
+ << " was going to be " << path
+ << ", changed into " << n << endl;
+ (*renamefileit).uDest.setPath( n );
+ }
+ }
+ if (!dirs.isEmpty())
+ emit aboutToCreate( this, dirs );
+ if (!files.isEmpty())
+ emit aboutToCreate( this, files );
+ }
+ break;
+ case R_AUTO_SKIP:
+ m_bAutoSkip = true;
+ // fall through
+ case R_SKIP:
+ m_skipList.append( existingDest );
+ skip( (*it).uSource );
+ // Move on to next dir
+ dirs.remove( it );
+ m_processedDirs++;
+ break;
+ case R_OVERWRITE:
+ m_overwriteList.append( existingDest );
+ emit copyingDone( this, ( *it ).uSource, ( *it ).uDest, true /* directory */, false /* renamed */ );
+ // Move on to next dir
+ dirs.remove( it );
+ m_processedDirs++;
+ break;
+ case R_OVERWRITE_ALL:
+ m_bOverwriteAll = true;
+ emit copyingDone( this, ( *it ).uSource, ( *it ).uDest, true /* directory */, false /* renamed */ );
+ // Move on to next dir
+ dirs.remove( it );
+ m_processedDirs++;
+ break;
+ default:
+ assert( 0 );
+ }
+ state = STATE_CREATING_DIRS;
+ //emit processedDirs( this, m_processedDirs );
+ createNextDir();
+}
+
+void CopyJob::createNextDir()
+{
+ KURL udir;
+ if ( !dirs.isEmpty() )
+ {
+ // Take first dir to create out of list
+ TQValueList<CopyInfo>::Iterator it = dirs.begin();
+ // Is this URL on the skip list or the overwrite list ?
+ while( it != dirs.end() && udir.isEmpty() )
+ {
+ const TQString dir = (*it).uDest.path();
+ if ( shouldSkip( dir ) ) {
+ dirs.remove( it );
+ it = dirs.begin();
+ } else
+ udir = (*it).uDest;
+ }
+ }
+ if ( !udir.isEmpty() ) // any dir to create, finally ?
+ {
+ // Create the directory - with default permissions so that we can put files into it
+ // TODO : change permissions once all is finished; but for stuff coming from CDROM it sucks...
+ TDEIO::SimpleJob *newjob = TDEIO::mkdir( udir, -1 );
+ Scheduler::scheduleJob(newjob);
+
+ m_currentDestURL = udir;
+ d->m_bURLDirty = true;
+
+ addSubjob(newjob);
+ return;
+ }
+ else // we have finished creating dirs
+ {
+ emit processedDirs( this, m_processedDirs ); // make sure final number appears
+ if (m_progressId) Observer::self()->slotProcessedDirs( this, m_processedDirs );
+
+ state = STATE_COPYING_FILES;
+ m_processedFiles++; // Ralf wants it to start at 1, not 0
+ copyNextFile();
+ }
+}
+
+void CopyJob::slotResultCopyingFiles( Job * job )
+{
+ // The file we were trying to copy:
+ TQValueList<CopyInfo>::Iterator it = files.begin();
+ if ( job->error() )
+ {
+ // Should we skip automatically ?
+ if ( m_bAutoSkip )
+ {
+ skip( (*it).uSource );
+ m_fileProcessedSize = (*it).size;
+ files.remove( it ); // Move on to next file
+ }
+ else
+ {
+ if ( !isInteractive() ) {
+ Job::slotResult( job ); // will set the error and emit result(this)
+ return;
+ }
+
+ m_conflictError = job->error(); // save for later
+ // Existing dest ?
+ if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST )
+ || ( m_conflictError == ERR_DIR_ALREADY_EXIST )
+ || ( m_conflictError == ERR_IDENTICAL_FILES ) )
+ {
+ subjobs.remove( job );
+ assert ( subjobs.isEmpty() );
+ // We need to stat the existing file, to get its last-modification time
+ KURL existingFile( (*it).uDest );
+ SimpleJob * newJob = TDEIO::stat( existingFile, false, 2, false );
+ Scheduler::scheduleJob(newJob);
+ kdDebug(7007) << "TDEIO::stat for resolving conflict on " << existingFile << endl;
+ state = STATE_CONFLICT_COPYING_FILES;
+ addSubjob(newJob);
+ return; // Don't move to next file yet !
+ }
+ else
+ {
+ if ( m_bCurrentOperationIsLink && ::tqqt_cast<TDEIO::DeleteJob*>( job ) )
+ {
+ // Very special case, see a few lines below
+ // We are deleting the source of a symlink we successfully moved... ignore error
+ m_fileProcessedSize = (*it).size;
+ files.remove( it );
+ } else {
+ // Go directly to the conflict resolution, there is nothing to stat
+ slotResultConflictCopyingFiles( job );
+ return;
+ }
+ }
+ }
+ } else // no error
+ {
+ // Special case for moving links. That operation needs two jobs, unlike others.
+ if ( m_bCurrentOperationIsLink && m_mode == Move
+ && !::tqqt_cast<TDEIO::DeleteJob *>( job ) // Deleting source not already done
+ )
+ {
+ subjobs.remove( job );
+ assert ( subjobs.isEmpty() );
+ // The only problem with this trick is that the error handling for this del operation
+ // is not going to be right... see 'Very special case' above.
+ TDEIO::Job * newjob = TDEIO::del( (*it).uSource, false /*don't shred*/, false /*no GUI*/ );
+ addSubjob( newjob );
+ return; // Don't move to next file yet !
+ }
+
+ if ( m_bCurrentOperationIsLink )
+ {
+ TQString target = ( m_mode == Link ? (*it).uSource.path() : (*it).linkDest );
+ //required for the undo feature
+ emit copyingLinkDone( this, (*it).uSource, target, (*it).uDest );
+ }
+ else
+ //required for the undo feature
+ emit copyingDone( this, (*it).uSource, (*it).uDest, false, false );
+ // remove from list, to move on to next file
+ files.remove( it );
+ }
+ m_processedFiles++;
+
+ // clear processed size for last file and add it to overall processed size
+ m_processedSize += m_fileProcessedSize;
+ m_fileProcessedSize = 0;
+
+ //kdDebug(7007) << files.count() << " files remaining" << endl;
+
+ removeSubjob( job, true, false ); // merge metadata
+ assert ( subjobs.isEmpty() ); // We should have only one job at a time ...
+ copyNextFile();
+}
+
+void CopyJob::slotResultConflictCopyingFiles( TDEIO::Job * job )
+{
+ // We come here after a conflict has been detected and we've stated the existing file
+ // The file we were trying to create:
+ TQValueList<CopyInfo>::Iterator it = files.begin();
+
+ RenameDlg_Result res;
+ TQString newPath;
+
+ if (m_reportTimer)
+ m_reportTimer->stop();
+
+ if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST )
+ || ( m_conflictError == ERR_DIR_ALREADY_EXIST )
+ || ( m_conflictError == ERR_IDENTICAL_FILES ) )
+ {
+ // Its modification time:
+ time_t destmtime = (time_t)-1;
+ time_t destctime = (time_t)-1;
+ TDEIO::filesize_t destsize = 0;
+ TQString linkDest;
+ UDSEntry entry = ((TDEIO::StatJob*)job)->statResult();
+ TDEIO::UDSEntry::ConstIterator it2 = entry.begin();
+ for( ; it2 != entry.end(); it2++ ) {
+ switch ((*it2).m_uds) {
+ case UDS_MODIFICATION_TIME:
+ destmtime = (time_t)((*it2).m_long);
+ break;
+ case UDS_CREATION_TIME:
+ destctime = (time_t)((*it2).m_long);
+ break;
+ case UDS_SIZE:
+ destsize = (*it2).m_long;
+ break;
+ case UDS_LINK_DEST:
+ linkDest = (*it2).m_str;
+ break;
+ }
+ }
+
+ // Offer overwrite only if the existing thing is a file
+ // If src==dest, use "overwrite-itself"
+ RenameDlg_Mode mode;
+ bool isDir = true;
+
+ if( m_conflictError == ERR_DIR_ALREADY_EXIST )
+ mode = (RenameDlg_Mode) 0;
+ else
+ {
+ if ( (*it).uSource == (*it).uDest ||
+ ((*it).uSource.protocol() == (*it).uDest.protocol() &&
+ (*it).uSource.path(-1) == linkDest) )
+ mode = M_OVERWRITE_ITSELF;
+ else
+ mode = M_OVERWRITE;
+ isDir = false;
+ }
+
+ if ( m_bSingleFileCopy )
+ mode = (RenameDlg_Mode) ( mode | M_SINGLE );
+ else
+ mode = (RenameDlg_Mode) ( mode | M_MULTI | M_SKIP );
+
+ res = Observer::self()->open_RenameDlg( this, !isDir ?
+ i18n("File Already Exists") : i18n("Already Exists as Folder"),
+ (*it).uSource.url(),
+ (*it).uDest.url(),
+ mode, newPath,
+ (*it).size, destsize,
+ (*it).ctime, destctime,
+ (*it).mtime, destmtime );
+
+ }
+ else
+ {
+ if ( job->error() == ERR_USER_CANCELED )
+ res = R_CANCEL;
+ else if ( !isInteractive() ) {
+ Job::slotResult( job ); // will set the error and emit result(this)
+ return;
+ }
+ else
+ {
+ SkipDlg_Result skipResult = Observer::self()->open_SkipDlg( this, files.count() > 1,
+ job->errorString() );
+
+ // Convert the return code from SkipDlg into a RenameDlg code
+ res = ( skipResult == S_SKIP ) ? R_SKIP :
+ ( skipResult == S_AUTO_SKIP ) ? R_AUTO_SKIP :
+ R_CANCEL;
+ }
+ }
+
+ if (m_reportTimer)
+ m_reportTimer->start(REPORT_TIMEOUT,false);
+
+ subjobs.remove( job );
+ assert ( subjobs.isEmpty() );
+ switch ( res ) {
+ case R_CANCEL:
+ m_error = ERR_USER_CANCELED;
+ emitResult();
+ return;
+ case R_RENAME:
+ {
+ KURL newUrl( (*it).uDest );
+ newUrl.setPath( newPath );
+ emit renamed( this, (*it).uDest, newUrl ); // for e.g. kpropsdlg
+ (*it).uDest = newUrl;
+
+ TQValueList<CopyInfo> files;
+ files.append(*it);
+ emit aboutToCreate( this, files );
+ }
+ break;
+ case R_AUTO_SKIP:
+ m_bAutoSkip = true;
+ // fall through
+ case R_SKIP:
+ // Move on to next file
+ skip( (*it).uSource );
+ m_processedSize += (*it).size;
+ files.remove( it );
+ m_processedFiles++;
+ break;
+ case R_OVERWRITE_ALL:
+ m_bOverwriteAll = true;
+ break;
+ case R_OVERWRITE:
+ // Add to overwrite list, so that copyNextFile knows to overwrite
+ m_overwriteList.append( (*it).uDest.path() );
+ break;
+ default:
+ assert( 0 );
+ }
+ state = STATE_COPYING_FILES;
+ //emit processedFiles( this, m_processedFiles );
+ copyNextFile();
+}
+
+void CopyJob::copyNextFile()
+{
+ bool bCopyFile = false;
+ //kdDebug(7007) << "CopyJob::copyNextFile()" << endl;
+ // Take the first file in the list
+ TQValueList<CopyInfo>::Iterator it = files.begin();
+ // Is this URL on the skip list ?
+ while (it != files.end() && !bCopyFile)
+ {
+ const TQString destFile = (*it).uDest.path();
+ bCopyFile = !shouldSkip( destFile );
+ if ( !bCopyFile ) {
+ files.remove( it );
+ it = files.begin();
+ }
+ }
+
+ if (bCopyFile) // any file to create, finally ?
+ {
+ // Do we set overwrite ?
+ bool bOverwrite;
+ const TQString destFile = (*it).uDest.path();
+ kdDebug(7007) << "copying " << destFile << endl;
+ if ( (*it).uDest == (*it).uSource )
+ bOverwrite = false;
+ else
+ bOverwrite = shouldOverwrite( destFile );
+
+ m_bCurrentOperationIsLink = false;
+ TDEIO::Job * newjob = 0L;
+ if ( m_mode == Link )
+ {
+ //kdDebug(7007) << "Linking" << endl;
+ if (
+ ((*it).uSource.protocol() == (*it).uDest.protocol()) &&
+ ((*it).uSource.host() == (*it).uDest.host()) &&
+ ((*it).uSource.port() == (*it).uDest.port()) &&
+ ((*it).uSource.user() == (*it).uDest.user()) &&
+ ((*it).uSource.pass() == (*it).uDest.pass()) )
+ {
+ // This is the case of creating a real symlink
+ TDEIO::SimpleJob *newJob = TDEIO::symlink( (*it).uSource.path(), (*it).uDest, bOverwrite, false /*no GUI*/ );
+ newjob = newJob;
+ Scheduler::scheduleJob(newJob);
+ //kdDebug(7007) << "CopyJob::copyNextFile : Linking target=" << (*it).uSource.path() << " link=" << (*it).uDest << endl;
+ //emit linking( this, (*it).uSource.path(), (*it).uDest );
+ m_bCurrentOperationIsLink = true;
+ m_currentSrcURL=(*it).uSource;
+ m_currentDestURL=(*it).uDest;
+ d->m_bURLDirty = true;
+ //Observer::self()->slotCopying( this, (*it).uSource, (*it).uDest ); // should be slotLinking perhaps
+ } else {
+ //kdDebug(7007) << "CopyJob::copyNextFile : Linking URL=" << (*it).uSource << " link=" << (*it).uDest << endl;
+ if ( (*it).uDest.isLocalFile() )
+ {
+ bool devicesOk=false;
+
+ // if the source is a devices url, handle it a littlebit special
+ if ((*it).uSource.protocol()==TQString::fromLatin1("devices"))
+ {
+ TQByteArray data;
+ TQByteArray param;
+ TQCString retType;
+ TQDataStream streamout(param,IO_WriteOnly);
+ streamout<<(*it).uSource;
+ streamout<<(*it).uDest;
+ if ( kapp && kapp->dcopClient()->call( "kded",
+ "mountwatcher", "createLink(KURL, KURL)", param,retType,data,false ) )
+ {
+ TQDataStream streamin(data,IO_ReadOnly);
+ streamin>>devicesOk;
+ }
+ if (devicesOk)
+ {
+ files.remove( it );
+ m_processedFiles++;
+ //emit processedFiles( this, m_processedFiles );
+ copyNextFile();
+ return;
+ }
+ }
+
+ if (!devicesOk)
+ {
+ TQString path = (*it).uDest.path();
+ //kdDebug(7007) << "CopyJob::copyNextFile path=" << path << endl;
+ TQFile f( path );
+ if ( f.open( IO_ReadWrite ) )
+ {
+ f.close();
+ KSimpleConfig config( path );
+ config.setDesktopGroup();
+ KURL url = (*it).uSource;
+ url.setPass( "" );
+ config.writePathEntry( TQString::fromLatin1("URL"), url.url() );
+ config.writeEntry( TQString::fromLatin1("Name"), url.url() );
+ config.writeEntry( TQString::fromLatin1("Type"), TQString::fromLatin1("Link") );
+ TQString protocol = (*it).uSource.protocol();
+ if ( protocol == TQString::fromLatin1("ftp") )
+ config.writeEntry( TQString::fromLatin1("Icon"), TQString::fromLatin1("ftp") );
+ else if ( protocol == TQString::fromLatin1("http") )
+ config.writeEntry( TQString::fromLatin1("Icon"), TQString::fromLatin1("www") );
+ else if ( protocol == TQString::fromLatin1("info") )
+ config.writeEntry( TQString::fromLatin1("Icon"), TQString::fromLatin1("info") );
+ else if ( protocol == TQString::fromLatin1("mailto") ) // sven:
+ config.writeEntry( TQString::fromLatin1("Icon"), TQString::fromLatin1("kmail") ); // added mailto: support
+ else
+ config.writeEntry( TQString::fromLatin1("Icon"), TQString::fromLatin1("unknown") );
+ config.sync();
+ files.remove( it );
+ m_processedFiles++;
+ //emit processedFiles( this, m_processedFiles );
+ copyNextFile();
+ return;
+ }
+ else
+ {
+ kdDebug(7007) << "CopyJob::copyNextFile ERR_CANNOT_OPEN_FOR_WRITING" << endl;
+ m_error = ERR_CANNOT_OPEN_FOR_WRITING;
+ m_errorText = (*it).uDest.path();
+ emitResult();
+ return;
+ }
+ }
+ } else {
+ // Todo: not show "link" on remote dirs if the src urls are not from the same protocol+host+...
+ m_error = ERR_CANNOT_SYMLINK;
+ m_errorText = (*it).uDest.prettyURL();
+ emitResult();
+ return;
+ }
+ }
+ }
+ else if ( !(*it).linkDest.isEmpty() &&
+ ((*it).uSource.protocol() == (*it).uDest.protocol()) &&
+ ((*it).uSource.host() == (*it).uDest.host()) &&
+ ((*it).uSource.port() == (*it).uDest.port()) &&
+ ((*it).uSource.user() == (*it).uDest.user()) &&
+ ((*it).uSource.pass() == (*it).uDest.pass()))
+ // Copying a symlink - only on the same protocol/host/etc. (#5601, downloading an FTP file through its link),
+ {
+ TDEIO::SimpleJob *newJob = TDEIO::symlink( (*it).linkDest, (*it).uDest, bOverwrite, false /*no GUI*/ );
+ Scheduler::scheduleJob(newJob);
+ newjob = newJob;
+ //kdDebug(7007) << "CopyJob::copyNextFile : Linking target=" << (*it).linkDest << " link=" << (*it).uDest << endl;
+ //emit linking( this, (*it).linkDest, (*it).uDest );
+ m_currentSrcURL=(*it).linkDest;
+ m_currentDestURL=(*it).uDest;
+ d->m_bURLDirty = true;
+ //Observer::self()->slotCopying( this, (*it).linkDest, (*it).uDest ); // should be slotLinking perhaps
+ m_bCurrentOperationIsLink = true;
+ // NOTE: if we are moving stuff, the deletion of the source will be done in slotResultCopyingFiles
+ } else if (m_mode == Move) // Moving a file
+ {
+ TDEIO::FileCopyJob * moveJob = TDEIO::file_move( (*it).uSource, (*it).uDest, (*it).permissions, bOverwrite, false, false/*no GUI*/ );
+ moveJob->setSourceSize64( (*it).size );
+ newjob = moveJob;
+ //kdDebug(7007) << "CopyJob::copyNextFile : Moving " << (*it).uSource << " to " << (*it).uDest << endl;
+ //emit moving( this, (*it).uSource, (*it).uDest );
+ m_currentSrcURL=(*it).uSource;
+ m_currentDestURL=(*it).uDest;
+ d->m_bURLDirty = true;
+ //Observer::self()->slotMoving( this, (*it).uSource, (*it).uDest );
+ }
+ else // Copying a file
+ {
+ // If source isn't local and target is local, we ignore the original permissions
+ // Otherwise, files downloaded from HTTP end up with -r--r--r--
+ bool remoteSource = !KProtocolInfo::supportsListing((*it).uSource);
+ int permissions = (*it).permissions;
+ if ( d->m_defaultPermissions || ( remoteSource && (*it).uDest.isLocalFile() ) )
+ permissions = -1;
+ TDEIO::FileCopyJob * copyJob = TDEIO::file_copy( (*it).uSource, (*it).uDest, permissions, bOverwrite, false, false/*no GUI*/ );
+ copyJob->setParentJob( this ); // in case of rename dialog
+ copyJob->setSourceSize64( (*it).size );
+ copyJob->setModificationTime( (*it).mtime );
+ newjob = copyJob;
+ //kdDebug(7007) << "CopyJob::copyNextFile : Copying " << (*it).uSource << " to " << (*it).uDest << endl;
+ m_currentSrcURL=(*it).uSource;
+ m_currentDestURL=(*it).uDest;
+ d->m_bURLDirty = true;
+ }
+ addSubjob(newjob);
+ connect( newjob, TQT_SIGNAL( processedSize( TDEIO::Job*, TDEIO::filesize_t ) ),
+ this, TQT_SLOT( slotProcessedSize( TDEIO::Job*, TDEIO::filesize_t ) ) );
+ connect( newjob, TQT_SIGNAL( totalSize( TDEIO::Job*, TDEIO::filesize_t ) ),
+ this, TQT_SLOT( slotTotalSize( TDEIO::Job*, TDEIO::filesize_t ) ) );
+ }
+ else
+ {
+ // We're done
+ //kdDebug(7007) << "copyNextFile finished" << endl;
+ deleteNextDir();
+ }
+}
+
+void CopyJob::deleteNextDir()
+{
+ if ( m_mode == Move && !dirsToRemove.isEmpty() ) // some dirs to delete ?
+ {
+ state = STATE_DELETING_DIRS;
+ d->m_bURLDirty = true;
+ // Take first dir to delete out of list - last ones first !
+ KURL::List::Iterator it = dirsToRemove.fromLast();
+ SimpleJob *job = TDEIO::rmdir( *it );
+ Scheduler::scheduleJob(job);
+ dirsToRemove.remove(it);
+ addSubjob( job );
+ }
+ else
+ {
+ // This step is done, move on
+ setNextDirAttribute();
+ }
+}
+
+void CopyJob::setNextDirAttribute()
+{
+ if ( !d->m_directoriesCopied.isEmpty() )
+ {
+ state = STATE_SETTING_DIR_ATTRIBUTES;
+#ifdef Q_OS_UNIX
+ // TODO KDE4: this should use a SlaveBase method, but we have none yet in KDE3.
+ TQValueList<CopyInfo>::Iterator it = d->m_directoriesCopied.begin();
+ for ( ; it != d->m_directoriesCopied.end() ; ++it ) {
+ const KURL& url = (*it).uDest;
+ if ( url.isLocalFile() && (*it).mtime != (time_t)-1 ) {
+ const TQCString path = TQFile::encodeName( url.path() );
+ KDE_struct_stat statbuf;
+ if (KDE_lstat(path, &statbuf) == 0) {
+ struct utimbuf utbuf;
+ utbuf.actime = statbuf.st_atime; // access time, unchanged
+ utbuf.modtime = (*it).mtime; // modification time
+ utime( path, &utbuf );
+ }
+
+ }
+ }
+#endif
+ d->m_directoriesCopied.clear();
+ }
+
+ // No "else" here, since the above is a simple sync loop
+
+ {
+ // Finished - tell the world
+ if ( !m_bOnlyRenames )
+ {
+ KDirNotify_stub allDirNotify("*", "KDirNotify*");
+ KURL url( d->m_globalDest );
+ if ( d->m_globalDestinationState != DEST_IS_DIR || m_asMethod )
+ url.setPath( url.directory() );
+ //kdDebug(7007) << "KDirNotify'ing FilesAdded " << url << endl;
+ allDirNotify.FilesAdded( url );
+
+ if ( m_mode == Move && !m_srcList.isEmpty() ) {
+ //kdDebug(7007) << "KDirNotify'ing FilesRemoved " << m_srcList.toStringList() << endl;
+ allDirNotify.FilesRemoved( m_srcList );
+ }
+ }
+ if (m_reportTimer)
+ m_reportTimer->stop();
+ --m_processedFiles; // undo the "start at 1" hack
+ slotReport(); // display final numbers, important if progress dialog stays up
+
+ emitResult();
+ }
+}
+
+void CopyJob::slotProcessedSize( TDEIO::Job*, TDEIO::filesize_t data_size )
+{
+ //kdDebug(7007) << "CopyJob::slotProcessedSize " << data_size << endl;
+ m_fileProcessedSize = data_size;
+ setProcessedSize(m_processedSize + m_fileProcessedSize);
+
+ if ( m_processedSize + m_fileProcessedSize > m_totalSize )
+ {
+ m_totalSize = m_processedSize + m_fileProcessedSize;
+ //kdDebug(7007) << "Adjusting m_totalSize to " << m_totalSize << endl;
+ emit totalSize( this, m_totalSize ); // safety
+ }
+ //kdDebug(7007) << "emit processedSize " << (unsigned long) (m_processedSize + m_fileProcessedSize) << endl;
+ emit processedSize( this, m_processedSize + m_fileProcessedSize );
+ emitPercent( m_processedSize + m_fileProcessedSize, m_totalSize );
+}
+
+void CopyJob::slotTotalSize( TDEIO::Job*, TDEIO::filesize_t size )
+{
+ //kdDebug(7007) << "slotTotalSize: " << size << endl;
+ // Special case for copying a single file
+ // This is because some protocols don't implement stat properly
+ // (e.g. HTTP), and don't give us a size in some cases (redirection)
+ // so we'd rather rely on the size given for the transfer
+ if ( m_bSingleFileCopy && size > m_totalSize)
+ {
+ //kdDebug(7007) << "slotTotalSize: updating totalsize to " << size << endl;
+ m_totalSize = size;
+ emit totalSize( this, size );
+ }
+}
+
+void CopyJob::slotResultDeletingDirs( Job * job )
+{
+ if (job->error())
+ {
+ // Couldn't remove directory. Well, perhaps it's not empty
+ // because the user pressed Skip for a given file in it.
+ // Let's not display "Could not remove dir ..." for each of those dir !
+ }
+ subjobs.remove( job );
+ assert ( subjobs.isEmpty() );
+ deleteNextDir();
+}
+
+#if 0 // TODO KDE4
+void CopyJob::slotResultSettingDirAttributes( Job * job )
+{
+ if (job->error())
+ {
+ // Couldn't set directory attributes. Ignore the error, it can happen
+ // with inferior file systems like VFAT.
+ // Let's not display warnings for each dir like "cp -a" does.
+ }
+ subjobs.remove( job );
+ assert ( subjobs.isEmpty() );
+ setNextDirAttribute();
+}
+#endif
+
+void CopyJob::slotResultRenaming( Job* job )
+{
+ int err = job->error();
+ const TQString errText = job->errorText();
+ removeSubjob( job, true, false ); // merge metadata
+ assert ( subjobs.isEmpty() );
+ // Determine dest again
+ KURL dest = m_dest;
+ if ( destinationState == DEST_IS_DIR && !m_asMethod )
+ dest.addPath( m_currentSrcURL.fileName() );
+ if ( err )
+ {
+ // Direct renaming didn't work. Try renaming to a temp name,
+ // this can help e.g. when renaming 'a' to 'A' on a VFAT partition.
+ // In that case it's the _same_ dir, we don't want to copy+del (data loss!)
+ if ( m_currentSrcURL.isLocalFile() && m_currentSrcURL.url(-1) != dest.url(-1) &&
+ m_currentSrcURL.url(-1).lower() == dest.url(-1).lower() &&
+ ( err == ERR_FILE_ALREADY_EXIST ||
+ err == ERR_DIR_ALREADY_EXIST ||
+ err == ERR_IDENTICAL_FILES ) )
+ {
+ kdDebug(7007) << "Couldn't rename directly, dest already exists. Detected special case of lower/uppercase renaming in same dir, try with 2 rename calls" << endl;
+ TQCString _src( TQFile::encodeName(m_currentSrcURL.path()) );
+ TQCString _dest( TQFile::encodeName(dest.path()) );
+ KTempFile tmpFile( m_currentSrcURL.directory(false) );
+ TQCString _tmp( TQFile::encodeName(tmpFile.name()) );
+ kdDebug(7007) << "CopyJob::slotResult KTempFile status:" << tmpFile.status() << " using " << _tmp << " as intermediary" << endl;
+ tmpFile.unlink();
+ if ( ::rename( _src, _tmp ) == 0 )
+ {
+ if ( !TQFile::exists( _dest ) && ::rename( _tmp, _dest ) == 0 )
+ {
+ kdDebug(7007) << "Success." << endl;
+ err = 0;
+ }
+ else
+ {
+ // Revert back to original name!
+ if ( ::rename( _tmp, _src ) != 0 ) {
+ kdError(7007) << "Couldn't rename " << tmpFile.name() << " back to " << _src << " !" << endl;
+ // Severe error, abort
+ Job::slotResult( job ); // will set the error and emit result(this)
+ return;
+ }
+ }
+ }
+ }
+ }
+ if ( err )
+ {
+ // This code is similar to CopyJob::slotResultConflictCopyingFiles
+ // but here it's about the base src url being moved/renamed
+ // (*m_currentStatSrc) and its dest (m_dest), not about a single file.
+ // It also means we already stated the dest, here.
+ // On the other hand we haven't stated the src yet (we skipped doing it
+ // to save time, since it's not necessary to rename directly!)...
+
+ Q_ASSERT( m_currentSrcURL == *m_currentStatSrc );
+
+ // Existing dest?
+ if ( ( err == ERR_DIR_ALREADY_EXIST ||
+ err == ERR_FILE_ALREADY_EXIST ||
+ err == ERR_IDENTICAL_FILES )
+ && isInteractive() )
+ {
+ if (m_reportTimer)
+ m_reportTimer->stop();
+
+ // Should we skip automatically ?
+ if ( m_bAutoSkip ) {
+ // Move on to next file
+ skipSrc();
+ return;
+ } else if ( m_bOverwriteAll ) {
+ ; // nothing to do, stat+copy+del will overwrite
+ } else {
+ TQString newPath;
+ // If src==dest, use "overwrite-itself"
+ RenameDlg_Mode mode = (RenameDlg_Mode)
+ ( ( m_currentSrcURL == dest ) ? M_OVERWRITE_ITSELF : M_OVERWRITE );
+
+ if ( m_srcList.count() > 1 )
+ mode = (RenameDlg_Mode) ( mode | M_MULTI | M_SKIP );
+ else
+ mode = (RenameDlg_Mode) ( mode | M_SINGLE );
+
+ // we lack mtime info for both the src (not stated)
+ // and the dest (stated but this info wasn't stored)
+ // Let's do it for local files, at least
+ TDEIO::filesize_t sizeSrc = (TDEIO::filesize_t) -1;
+ TDEIO::filesize_t sizeDest = (TDEIO::filesize_t) -1;
+ time_t ctimeSrc = (time_t) -1;
+ time_t ctimeDest = (time_t) -1;
+ time_t mtimeSrc = (time_t) -1;
+ time_t mtimeDest = (time_t) -1;
+
+ KDE_struct_stat stat_buf;
+ if ( m_currentSrcURL.isLocalFile() &&
+ KDE_stat(TQFile::encodeName(m_currentSrcURL.path()), &stat_buf) == 0 ) {
+ sizeSrc = stat_buf.st_size;
+ ctimeSrc = stat_buf.st_ctime;
+ mtimeSrc = stat_buf.st_mtime;
+ }
+ if ( dest.isLocalFile() &&
+ KDE_stat(TQFile::encodeName(dest.path()), &stat_buf) == 0 ) {
+ sizeDest = stat_buf.st_size;
+ ctimeDest = stat_buf.st_ctime;
+ mtimeDest = stat_buf.st_mtime;
+ }
+
+ RenameDlg_Result r = Observer::self()->open_RenameDlg(
+ this,
+ err != ERR_DIR_ALREADY_EXIST ? i18n("File Already Exists") : i18n("Already Exists as Folder"),
+ m_currentSrcURL.url(),
+ dest.url(),
+ mode, newPath,
+ sizeSrc, sizeDest,
+ ctimeSrc, ctimeDest,
+ mtimeSrc, mtimeDest );
+ if (m_reportTimer)
+ m_reportTimer->start(REPORT_TIMEOUT,false);
+
+ switch ( r )
+ {
+ case R_CANCEL:
+ {
+ m_error = ERR_USER_CANCELED;
+ emitResult();
+ return;
+ }
+ case R_RENAME:
+ {
+ // Set m_dest to the chosen destination
+ // This is only for this src url; the next one will revert to d->m_globalDest
+ m_dest.setPath( newPath );
+ TDEIO::Job* job = TDEIO::stat( m_dest, false, 2, false );
+ state = STATE_STATING;
+ destinationState = DEST_NOT_STATED;
+ addSubjob(job);
+ return;
+ }
+ case R_AUTO_SKIP:
+ m_bAutoSkip = true;
+ // fall through
+ case R_SKIP:
+ // Move on to next file
+ skipSrc();
+ return;
+ case R_OVERWRITE_ALL:
+ m_bOverwriteAll = true;
+ break;
+ case R_OVERWRITE:
+ // Add to overwrite list
+ // Note that we add dest, not m_dest.
+ // This ensures that when moving several urls into a dir (m_dest),
+ // we only overwrite for the current one, not for all.
+ // When renaming a single file (m_asMethod), it makes no difference.
+ kdDebug(7007) << "adding to overwrite list: " << dest.path() << endl;
+ m_overwriteList.append( dest.path() );
+ break;
+ default:
+ //assert( 0 );
+ break;
+ }
+ }
+ } else if ( err != TDEIO::ERR_UNSUPPORTED_ACTION ) {
+ kdDebug(7007) << "Couldn't rename " << m_currentSrcURL << " to " << dest << ", aborting" << endl;
+ m_error = err;
+ m_errorText = errText;
+ emitResult();
+ return;
+ }
+ kdDebug(7007) << "Couldn't rename " << m_currentSrcURL << " to " << dest << ", reverting to normal way, starting with stat" << endl;
+ //kdDebug(7007) << "TDEIO::stat on " << m_currentSrcURL << endl;
+ TDEIO::Job* job = TDEIO::stat( m_currentSrcURL, true, 2, false );
+ state = STATE_STATING;
+ addSubjob(job);
+ m_bOnlyRenames = false;
+ }
+ else
+ {
+ //kdDebug(7007) << "Renaming succeeded, move on" << endl;
+ emit copyingDone( this, *m_currentStatSrc, dest, true, true );
+ statNextSrc();
+ }
+}
+
+void CopyJob::slotResult( Job *job )
+{
+ //kdDebug(7007) << "CopyJob::slotResult() state=" << (int) state << endl;
+ // In each case, what we have to do is :
+ // 1 - check for errors and treat them
+ // 2 - subjobs.remove(job);
+ // 3 - decide what to do next
+
+ switch ( state ) {
+ case STATE_STATING: // We were trying to stat a src url or the dest
+ slotResultStating( job );
+ break;
+ case STATE_RENAMING: // We were trying to do a direct renaming, before even stat'ing
+ {
+ slotResultRenaming( job );
+ break;
+ }
+ case STATE_LISTING: // recursive listing finished
+ //kdDebug(7007) << "totalSize: " << (unsigned int) m_totalSize << " files: " << files.count() << " dirs: " << dirs.count() << endl;
+ // Was there an error ?
+ if (job->error())
+ {
+ Job::slotResult( job ); // will set the error and emit result(this)
+ return;
+ }
+
+ subjobs.remove( job );
+ assert ( subjobs.isEmpty() );
+
+ statNextSrc();
+ break;
+ case STATE_CREATING_DIRS:
+ slotResultCreatingDirs( job );
+ break;
+ case STATE_CONFLICT_CREATING_DIRS:
+ slotResultConflictCreatingDirs( job );
+ break;
+ case STATE_COPYING_FILES:
+ slotResultCopyingFiles( job );
+ break;
+ case STATE_CONFLICT_COPYING_FILES:
+ slotResultConflictCopyingFiles( job );
+ break;
+ case STATE_DELETING_DIRS:
+ slotResultDeletingDirs( job );
+ break;
+ case STATE_SETTING_DIR_ATTRIBUTES: // TODO KDE4
+ assert( 0 );
+ //slotResultSettingDirAttributes( job );
+ break;
+ default:
+ assert( 0 );
+ }
+}
+
+void TDEIO::CopyJob::setDefaultPermissions( bool b )
+{
+ d->m_defaultPermissions = b;
+}
+
+// KDE4: remove
+void TDEIO::CopyJob::setInteractive( bool b )
+{
+ Job::setInteractive( b );
+}
+
+CopyJob *TDEIO::copy(const KURL& src, const KURL& dest, bool showProgressInfo )
+{
+ //kdDebug(7007) << "TDEIO::copy src=" << src << " dest=" << dest << endl;
+ KURL::List srcList;
+ srcList.append( src );
+ return new CopyJob( srcList, dest, CopyJob::Copy, false, showProgressInfo );
+}
+
+CopyJob *TDEIO::copyAs(const KURL& src, const KURL& dest, bool showProgressInfo )
+{
+ //kdDebug(7007) << "TDEIO::copyAs src=" << src << " dest=" << dest << endl;
+ KURL::List srcList;
+ srcList.append( src );
+ return new CopyJob( srcList, dest, CopyJob::Copy, true, showProgressInfo );
+}
+
+CopyJob *TDEIO::copy( const KURL::List& src, const KURL& dest, bool showProgressInfo )
+{
+ //kdDebug(7007) << src << " " << dest << endl;
+ return new CopyJob( src, dest, CopyJob::Copy, false, showProgressInfo );
+}
+
+CopyJob *TDEIO::move(const KURL& src, const KURL& dest, bool showProgressInfo )
+{
+ //kdDebug(7007) << src << " " << dest << endl;
+ KURL::List srcList;
+ srcList.append( src );
+ return new CopyJob( srcList, dest, CopyJob::Move, false, showProgressInfo );
+}
+
+CopyJob *TDEIO::moveAs(const KURL& src, const KURL& dest, bool showProgressInfo )
+{
+ //kdDebug(7007) << src << " " << dest << endl;
+ KURL::List srcList;
+ srcList.append( src );
+ return new CopyJob( srcList, dest, CopyJob::Move, true, showProgressInfo );
+}
+
+CopyJob *TDEIO::move( const KURL::List& src, const KURL& dest, bool showProgressInfo )
+{
+ //kdDebug(7007) << src << " " << dest << endl;
+ return new CopyJob( src, dest, CopyJob::Move, false, showProgressInfo );
+}
+
+CopyJob *TDEIO::link(const KURL& src, const KURL& destDir, bool showProgressInfo )
+{
+ KURL::List srcList;
+ srcList.append( src );
+ return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo );
+}
+
+CopyJob *TDEIO::link(const KURL::List& srcList, const KURL& destDir, bool showProgressInfo )
+{
+ return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo );
+}
+
+CopyJob *TDEIO::linkAs(const KURL& src, const KURL& destDir, bool showProgressInfo )
+{
+ KURL::List srcList;
+ srcList.append( src );
+ return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo );
+}
+
+CopyJob *TDEIO::trash(const KURL& src, bool showProgressInfo )
+{
+ KURL::List srcList;
+ srcList.append( src );
+ return new CopyJob( srcList, KURL( "trash:/" ), CopyJob::Move, false, showProgressInfo );
+}
+
+CopyJob *TDEIO::trash(const KURL::List& srcList, bool showProgressInfo )
+{
+ return new CopyJob( srcList, KURL( "trash:/" ), CopyJob::Move, false, showProgressInfo );
+}
+
+//////////
+
+DeleteJob::DeleteJob( const KURL::List& src, bool /*shred*/, bool showProgressInfo )
+: Job(showProgressInfo), m_totalSize( 0 ), m_processedSize( 0 ), m_fileProcessedSize( 0 ),
+ m_processedFiles( 0 ), m_processedDirs( 0 ), m_totalFilesDirs( 0 ),
+ m_srcList(src), m_currentStat(m_srcList.begin()), m_reportTimer(0)
+{
+ if ( showProgressInfo ) {
+
+ connect( this, TQT_SIGNAL( totalFiles( TDEIO::Job*, unsigned long ) ),
+ Observer::self(), TQT_SLOT( slotTotalFiles( TDEIO::Job*, unsigned long ) ) );
+
+ connect( this, TQT_SIGNAL( totalDirs( TDEIO::Job*, unsigned long ) ),
+ Observer::self(), TQT_SLOT( slotTotalDirs( TDEIO::Job*, unsigned long ) ) );
+
+ // See slotReport
+ /*connect( this, TQT_SIGNAL( processedFiles( TDEIO::Job*, unsigned long ) ),
+ m_observer, TQT_SLOT( slotProcessedFiles( TDEIO::Job*, unsigned long ) ) );
+
+ connect( this, TQT_SIGNAL( processedDirs( TDEIO::Job*, unsigned long ) ),
+ m_observer, TQT_SLOT( slotProcessedDirs( TDEIO::Job*, unsigned long ) ) );
+
+ connect( this, TQT_SIGNAL( deleting( TDEIO::Job*, const KURL& ) ),
+ m_observer, TQT_SLOT( slotDeleting( TDEIO::Job*, const KURL& ) ) );*/
+
+ m_reportTimer=new TQTimer(this);
+ connect(m_reportTimer,TQT_SIGNAL(timeout()),this,TQT_SLOT(slotReport()));
+ //this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX
+ m_reportTimer->start(REPORT_TIMEOUT,false);
+ }
+
+ TQTimer::singleShot(0, this, TQT_SLOT(slotStart()));
+}
+
+void DeleteJob::slotStart()
+{
+ statNextSrc();
+}
+
+//this is called often, so calling the functions
+//from Observer here directly might improve the performance a little bit
+//aleXXX
+void DeleteJob::slotReport()
+{
+ if (m_progressId==0)
+ return;
+
+ Observer * observer = Observer::self();
+
+ emit deleting( this, m_currentURL );
+ observer->slotDeleting(this,m_currentURL);
+
+ switch( state ) {
+ case STATE_STATING:
+ case STATE_LISTING:
+ emit totalSize( this, m_totalSize );
+ emit totalFiles( this, files.count() );
+ emit totalDirs( this, dirs.count() );
+ break;
+ case STATE_DELETING_DIRS:
+ emit processedDirs( this, m_processedDirs );
+ observer->slotProcessedDirs(this,m_processedDirs);
+ emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs );
+ break;
+ case STATE_DELETING_FILES:
+ observer->slotProcessedFiles(this,m_processedFiles);
+ emit processedFiles( this, m_processedFiles );
+ emitPercent( m_processedFiles, m_totalFilesDirs );
+ break;
+ }
+}
+
+
+void DeleteJob::slotEntries(TDEIO::Job* job, const UDSEntryList& list)
+{
+ UDSEntryListConstIterator it = list.begin();
+ UDSEntryListConstIterator end = list.end();
+ for (; it != end; ++it)
+ {
+ UDSEntry::ConstIterator it2 = (*it).begin();
+ bool bDir = false;
+ bool bLink = false;
+ TQString displayName;
+ KURL url;
+ int atomsFound(0);
+ for( ; it2 != (*it).end(); it2++ )
+ {
+ switch ((*it2).m_uds)
+ {
+ case UDS_FILE_TYPE:
+ bDir = S_ISDIR((*it2).m_long);
+ atomsFound++;
+ break;
+ case UDS_NAME:
+ displayName = (*it2).m_str;
+ atomsFound++;
+ break;
+ case UDS_URL:
+ url = KURL((*it2).m_str);
+ atomsFound++;
+ break;
+ case UDS_LINK_DEST:
+ bLink = !(*it2).m_str.isEmpty();
+ atomsFound++;
+ break;
+ case UDS_SIZE:
+ m_totalSize += (TDEIO::filesize_t)((*it2).m_long);
+ atomsFound++;
+ break;
+ default:
+ break;
+ }
+ if (atomsFound==5) break;
+ }
+ assert(!displayName.isEmpty());
+ if (displayName != ".." && displayName != ".")
+ {
+ if( url.isEmpty() ) {
+ url = ((SimpleJob *)job)->url(); // assumed to be a dir
+ url.addPath( displayName );
+ }
+ //kdDebug(7007) << "DeleteJob::slotEntries " << displayName << " (" << url << ")" << endl;
+ if ( bLink )
+ symlinks.append( url );
+ else if ( bDir )
+ dirs.append( url );
+ else
+ files.append( url );
+ }
+ }
+}
+
+
+void DeleteJob::statNextSrc()
+{
+ //kdDebug(7007) << "statNextSrc" << endl;
+ if ( m_currentStat != m_srcList.end() )
+ {
+ m_currentURL = (*m_currentStat);
+
+ // if the file system doesn't support deleting, we do not even stat
+ if (!KProtocolInfo::supportsDeleting(m_currentURL)) {
+ TQGuardedPtr<DeleteJob> that = this;
+ ++m_currentStat;
+ if (isInteractive())
+ KMessageBox::information( 0, buildErrorString(ERR_CANNOT_DELETE, m_currentURL.prettyURL()));
+ if (that)
+ statNextSrc();
+ return;
+ }
+ // Stat it
+ state = STATE_STATING;
+ TDEIO::SimpleJob * job = TDEIO::stat( m_currentURL, true, 1, false );
+ Scheduler::scheduleJob(job);
+ //kdDebug(7007) << "TDEIO::stat (DeleteJob) " << m_currentURL << endl;
+ addSubjob(job);
+ //if ( m_progressId ) // Did we get an ID from the observer ?
+ // Observer::self()->slotDeleting( this, *it ); // show asap
+ } else
+ {
+ m_totalFilesDirs = files.count()+symlinks.count() + dirs.count();
+ slotReport();
+ // Now we know which dirs hold the files we're going to delete.
+ // To speed things up and prevent double-notification, we disable KDirWatch
+ // on those dirs temporarily (using KDirWatch::self, that's the instanced
+ // used by e.g. kdirlister).
+ for ( TQStringList::Iterator it = m_parentDirs.begin() ; it != m_parentDirs.end() ; ++it )
+ KDirWatch::self()->stopDirScan( *it );
+ state = STATE_DELETING_FILES;
+ deleteNextFile();
+ }
+}
+
+void DeleteJob::deleteNextFile()
+{
+ //kdDebug(7007) << "deleteNextFile" << endl;
+ if ( !files.isEmpty() || !symlinks.isEmpty() )
+ {
+ SimpleJob *job;
+ do {
+ // Take first file to delete out of list
+ KURL::List::Iterator it = files.begin();
+ bool isLink = false;
+ if ( it == files.end() ) // No more files
+ {
+ it = symlinks.begin(); // Pick up a symlink to delete
+ isLink = true;
+ }
+ // Normal deletion
+ // If local file, try do it directly
+ if ( (*it).isLocalFile() && unlink( TQFile::encodeName((*it).path()) ) == 0 ) {
+ //kdDebug(7007) << "DeleteJob deleted " << (*it).path() << endl;
+ job = 0;
+ m_processedFiles++;
+ if ( m_processedFiles % 300 == 0 || m_totalFilesDirs < 300) { // update progress info every 300 files
+ m_currentURL = *it;
+ slotReport();
+ }
+ } else
+ { // if remote - or if unlink() failed (we'll use the job's error handling in that case)
+ job = TDEIO::file_delete( *it, false /*no GUI*/);
+ Scheduler::scheduleJob(job);
+ m_currentURL=(*it);
+ }
+ if ( isLink )
+ symlinks.remove(it);
+ else
+ files.remove(it);
+ if ( job ) {
+ addSubjob(job);
+ return;
+ }
+ // loop only if direct deletion worked (job=0) and there is something else to delete
+ } while (!job && (!files.isEmpty() || !symlinks.isEmpty()));
+ }
+ state = STATE_DELETING_DIRS;
+ deleteNextDir();
+}
+
+void DeleteJob::deleteNextDir()
+{
+ if ( !dirs.isEmpty() ) // some dirs to delete ?
+ {
+ do {
+ // Take first dir to delete out of list - last ones first !
+ KURL::List::Iterator it = dirs.fromLast();
+ // If local dir, try to rmdir it directly
+ if ( (*it).isLocalFile() && ::rmdir( TQFile::encodeName((*it).path()) ) == 0 ) {
+
+ m_processedDirs++;
+ if ( m_processedDirs % 100 == 0 ) { // update progress info every 100 dirs
+ m_currentURL = *it;
+ slotReport();
+ }
+ } else {
+ SimpleJob* job;
+ if ( KProtocolInfo::canDeleteRecursive( *it ) ) {
+ // If the ioslave supports recursive deletion of a directory, then
+ // we only need to send a single CMD_DEL command, so we use file_delete :)
+ job = TDEIO::file_delete( *it, false /*no gui*/ );
+ } else {
+ job = TDEIO::rmdir( *it );
+ }
+ Scheduler::scheduleJob(job);
+ dirs.remove(it);
+ addSubjob( job );
+ return;
+ }
+ dirs.remove(it);
+ } while ( !dirs.isEmpty() );
+ }
+
+ // Re-enable watching on the dirs that held the deleted files
+ for ( TQStringList::Iterator it = m_parentDirs.begin() ; it != m_parentDirs.end() ; ++it )
+ KDirWatch::self()->restartDirScan( *it );
+
+ // Finished - tell the world
+ if ( !m_srcList.isEmpty() )
+ {
+ KDirNotify_stub allDirNotify("*", "KDirNotify*");
+ //kdDebug(7007) << "KDirNotify'ing FilesRemoved " << m_srcList.toStringList() << endl;
+ allDirNotify.FilesRemoved( m_srcList );
+ }
+ if (m_reportTimer!=0)
+ m_reportTimer->stop();
+ emitResult();
+}
+
+void DeleteJob::slotProcessedSize( TDEIO::Job*, TDEIO::filesize_t data_size )
+{
+ // Note: this is the same implementation as CopyJob::slotProcessedSize but
+ // it's different from FileCopyJob::slotProcessedSize - which is why this
+ // is not in Job.
+
+ m_fileProcessedSize = data_size;
+ setProcessedSize(m_processedSize + m_fileProcessedSize);
+
+ //kdDebug(7007) << "DeleteJob::slotProcessedSize " << (unsigned int) (m_processedSize + m_fileProcessedSize) << endl;
+
+ emit processedSize( this, m_processedSize + m_fileProcessedSize );
+
+ // calculate percents
+ unsigned long ipercent = m_percent;
+
+ if ( m_totalSize == 0 )
+ m_percent = 100;
+ else
+ m_percent = (unsigned long)(( (float)(m_processedSize + m_fileProcessedSize) / (float)m_totalSize ) * 100.0);
+
+ if ( m_percent > ipercent )
+ {
+ emit percent( this, m_percent );
+ //kdDebug(7007) << "DeleteJob::slotProcessedSize - percent = " << (unsigned int) m_percent << endl;
+ }
+
+}
+
+void DeleteJob::slotResult( Job *job )
+{
+ switch ( state )
+ {
+ case STATE_STATING:
+ {
+ // Was there an error while stating ?
+ if (job->error() )
+ {
+ // Probably : doesn't exist
+ Job::slotResult( job ); // will set the error and emit result(this)
+ return;
+ }
+
+ // Is it a file or a dir ?
+ UDSEntry entry = ((StatJob*)job)->statResult();
+ bool bDir = false;
+ bool bLink = false;
+ TDEIO::filesize_t size = (TDEIO::filesize_t)-1;
+ UDSEntry::ConstIterator it2 = entry.begin();
+ int atomsFound(0);
+ for( ; it2 != entry.end(); it2++ )
+ {
+ if ( ((*it2).m_uds) == UDS_FILE_TYPE )
+ {
+ bDir = S_ISDIR( (mode_t)(*it2).m_long );
+ atomsFound++;
+ }
+ else if ( ((*it2).m_uds) == UDS_LINK_DEST )
+ {
+ bLink = !((*it2).m_str.isEmpty());
+ atomsFound++;
+ }
+ else if ( ((*it2).m_uds) == UDS_SIZE )
+ {
+ size = (*it2).m_long;
+ atomsFound++;
+ }
+ if (atomsFound==3) break;
+ }
+
+ KURL url = ((SimpleJob*)job)->url();
+
+ subjobs.remove( job );
+ assert( subjobs.isEmpty() );
+
+ if (bDir && !bLink)
+ {
+ // Add toplevel dir in list of dirs
+ dirs.append( url );
+ if ( url.isLocalFile() && !m_parentDirs.contains( url.path(-1) ) )
+ m_parentDirs.append( url.path(-1) );
+
+ if ( !KProtocolInfo::canDeleteRecursive( url ) ) {
+ //kdDebug(7007) << " Target is a directory " << endl;
+ // List it
+ state = STATE_LISTING;
+ ListJob *newjob = listRecursive( url, false );
+ newjob->setUnrestricted(true); // No KIOSK restrictions
+ Scheduler::scheduleJob(newjob);
+ connect(newjob, TQT_SIGNAL(entries( TDEIO::Job *,
+ const TDEIO::UDSEntryList& )),
+ TQT_SLOT( slotEntries( TDEIO::Job*,
+ const TDEIO::UDSEntryList& )));
+ addSubjob(newjob);
+ } else {
+ ++m_currentStat;
+ statNextSrc();
+ }
+ }
+ else
+ {
+ if ( bLink ) {
+ //kdDebug(7007) << " Target is a symlink" << endl;
+ symlinks.append( url );
+ } else {
+ //kdDebug(7007) << " Target is a file" << endl;
+ files.append( url );
+ }
+ if ( url.isLocalFile() && !m_parentDirs.contains( url.directory(false) ) )
+ m_parentDirs.append( url.directory(false) );
+ ++m_currentStat;
+ statNextSrc();
+ }
+ }
+ break;
+ case STATE_LISTING:
+ if ( job->error() )
+ {
+ // Try deleting nonetheless, it may be empty (and non-listable)
+ }
+ subjobs.remove( job );
+ assert( subjobs.isEmpty() );
+ ++m_currentStat;
+ statNextSrc();
+ break;
+ case STATE_DELETING_FILES:
+ if ( job->error() )
+ {
+ Job::slotResult( job ); // will set the error and emit result(this)
+ return;
+ }
+ subjobs.remove( job );
+ assert( subjobs.isEmpty() );
+ m_processedFiles++;
+
+ deleteNextFile();
+ break;
+ case STATE_DELETING_DIRS:
+ if ( job->error() )
+ {
+ Job::slotResult( job ); // will set the error and emit result(this)
+ return;
+ }
+ subjobs.remove( job );
+ assert( subjobs.isEmpty() );
+ m_processedDirs++;
+ //emit processedDirs( this, m_processedDirs );
+ //if (!m_shred)
+ //emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs );
+
+ deleteNextDir();
+ break;
+ default:
+ assert(0);
+ }
+}
+
+DeleteJob *TDEIO::del( const KURL& src, bool shred, bool showProgressInfo )
+{
+ KURL::List srcList;
+ srcList.append( src );
+ DeleteJob *job = new DeleteJob( srcList, shred, showProgressInfo );
+ return job;
+}
+
+DeleteJob *TDEIO::del( const KURL::List& src, bool shred, bool showProgressInfo )
+{
+ DeleteJob *job = new DeleteJob( src, shred, showProgressInfo );
+ return job;
+}
+
+MultiGetJob::MultiGetJob(const KURL& url,
+ bool showProgressInfo)
+ : TransferJob(url, 0, TQByteArray(), TQByteArray(), showProgressInfo)
+{
+ m_waitQueue.setAutoDelete(true);
+ m_activeQueue.setAutoDelete(true);
+ m_currentEntry = 0;
+}
+
+void MultiGetJob::get(long id, const KURL &url, const MetaData &metaData)
+{
+ GetRequest *entry = new GetRequest(id, url, metaData);
+ entry->metaData["request-id"] = TQString("%1").arg(id);
+ m_waitQueue.append(entry);
+}
+
+void MultiGetJob::flushQueue(TQPtrList<GetRequest> &queue)
+{
+ GetRequest *entry;
+ // Use multi-get
+ // Scan all jobs in m_waitQueue
+ for(entry = m_waitQueue.first(); entry; )
+ {
+ if ((m_url.protocol() == entry->url.protocol()) &&
+ (m_url.host() == entry->url.host()) &&
+ (m_url.port() == entry->url.port()) &&
+ (m_url.user() == entry->url.user()))
+ {
+ m_waitQueue.take();
+ queue.append(entry);
+ entry = m_waitQueue.current();
+ }
+ else
+ {
+ entry = m_waitQueue.next();
+ }
+ }
+ // Send number of URLs, (URL, metadata)*
+ KIO_ARGS << (TQ_INT32) queue.count();
+ for(entry = queue.first(); entry; entry = queue.next())
+ {
+ stream << entry->url << entry->metaData;
+ }
+ m_packedArgs = packedArgs;
+ m_command = CMD_MULTI_GET;
+ m_outgoingMetaData.clear();
+}
+
+void MultiGetJob::start(Slave *slave)
+{
+ // Add first job from m_waitQueue and add it to m_activeQueue
+ GetRequest *entry = m_waitQueue.take(0);
+ m_activeQueue.append(entry);
+
+ m_url = entry->url;
+
+ if (!entry->url.protocol().startsWith("http"))
+ {
+ // Use normal get
+ KIO_ARGS << entry->url;
+ m_packedArgs = packedArgs;
+ m_outgoingMetaData = entry->metaData;
+ m_command = CMD_GET;
+ b_multiGetActive = false;
+ }
+ else
+ {
+ flushQueue(m_activeQueue);
+ b_multiGetActive = true;
+ }
+
+ TransferJob::start(slave); // Anything else to do??
+}
+
+bool MultiGetJob::findCurrentEntry()
+{
+ if (b_multiGetActive)
+ {
+ long id = m_incomingMetaData["request-id"].toLong();
+ for(GetRequest *entry = m_activeQueue.first(); entry; entry = m_activeQueue.next())
+ {
+ if (entry->id == id)
+ {
+ m_currentEntry = entry;
+ return true;
+ }
+ }
+ m_currentEntry = 0;
+ return false;
+ }
+ else
+ {
+ m_currentEntry = m_activeQueue.first();
+ return (m_currentEntry != 0);
+ }
+}
+
+void MultiGetJob::slotRedirection( const KURL &url)
+{
+ if (!findCurrentEntry()) return; // Error
+ if (kapp && !kapp->authorizeURLAction("redirect", m_url, url))
+ {
+ kdWarning(7007) << "MultiGetJob: Redirection from " << m_currentEntry->url << " to " << url << " REJECTED!" << endl;
+ return;
+ }
+ m_redirectionURL = url;
+ if (m_currentEntry->url.hasUser() && !url.hasUser() && (m_currentEntry->url.host().lower() == url.host().lower()))
+ m_redirectionURL.setUser(m_currentEntry->url.user()); // Preserve user
+ get(m_currentEntry->id, m_redirectionURL, m_currentEntry->metaData); // Try again
+}
+
+
+void MultiGetJob::slotFinished()
+{
+ if (!findCurrentEntry()) return;
+ if (m_redirectionURL.isEmpty())
+ {
+ // No redirection, tell the world that we are finished.
+ emit result(m_currentEntry->id);
+ }
+ m_redirectionURL = KURL();
+ m_error = 0;
+ m_incomingMetaData.clear();
+ m_activeQueue.removeRef(m_currentEntry);
+ if (m_activeQueue.count() == 0)
+ {
+ if (m_waitQueue.count() == 0)
+ {
+ // All done
+ TransferJob::slotFinished();
+ }
+ else
+ {
+ // return slave to pool
+ // fetch new slave for first entry in m_waitQueue and call start
+ // again.
+ GetRequest *entry = m_waitQueue.at(0);
+ m_url = entry->url;
+ slaveDone();
+ Scheduler::doJob(this);
+ }
+ }
+}
+
+void MultiGetJob::slotData( const TQByteArray &_data)
+{
+ if(!m_currentEntry) return;// Error, unknown request!
+ if(m_redirectionURL.isEmpty() || !m_redirectionURL.isValid() || m_error)
+ emit data(m_currentEntry->id, _data);
+}
+
+void MultiGetJob::slotMimetype( const TQString &_mimetype )
+{
+ if (b_multiGetActive)
+ {
+ TQPtrList<GetRequest> newQueue;
+ flushQueue(newQueue);
+ if (!newQueue.isEmpty())
+ {
+ while(!newQueue.isEmpty())
+ m_activeQueue.append(newQueue.take(0));
+ m_slave->send( m_command, m_packedArgs );
+ }
+ }
+ if (!findCurrentEntry()) return; // Error, unknown request!
+ emit mimetype(m_currentEntry->id, _mimetype);
+}
+
+MultiGetJob *TDEIO::multi_get(long id, const KURL &url, const MetaData &metaData)
+{
+ MultiGetJob * job = new MultiGetJob( url, false );
+ job->get(id, url, metaData);
+ return job;
+}
+
+
+#ifdef CACHE_INFO
+CacheInfo::CacheInfo(const KURL &url)
+{
+ m_url = url;
+}
+
+TQString CacheInfo::cachedFileName()
+{
+ const TQChar separator = '_';
+
+ TQString CEF = m_url.path();
+
+ int p = CEF.find('/');
+
+ while(p != -1)
+ {
+ CEF[p] = separator;
+ p = CEF.find('/', p);
+ }
+
+ TQString host = m_url.host().lower();
+ CEF = host + CEF + '_';
+
+ TQString dir = KProtocolManager::cacheDir();
+ if (dir[dir.length()-1] != '/')
+ dir += "/";
+
+ int l = m_url.host().length();
+ for(int i = 0; i < l; i++)
+ {
+ if (host[i].isLetter() && (host[i] != 'w'))
+ {
+ dir += host[i];
+ break;
+ }
+ }
+ if (dir[dir.length()-1] == '/')
+ dir += "0";
+
+ unsigned long hash = 0x00000000;
+ TQCString u = m_url.url().latin1();
+ for(int i = u.length(); i--;)
+ {
+ hash = (hash * 12211 + u[i]) % 2147483563;
+ }
+
+ TQString hashString;
+ hashString.sprintf("%08lx", hash);
+
+ CEF = CEF + hashString;
+
+ CEF = dir + "/" + CEF;
+
+ return CEF;
+}
+
+TQFile *CacheInfo::cachedFile()
+{
+#ifdef Q_WS_WIN
+ const char *mode = (readWrite ? "rb+" : "rb");
+#else
+ const char *mode = (readWrite ? "r+" : "r");
+#endif
+
+ FILE *fs = fopen(TQFile::encodeName(CEF), mode); // Open for reading and writing
+ if (!fs)
+ return 0;
+
+ char buffer[401];
+ bool ok = true;
+
+ // CacheRevision
+ if (ok && (!fgets(buffer, 400, fs)))
+ ok = false;
+ if (ok && (strcmp(buffer, CACHE_REVISION) != 0))
+ ok = false;
+
+ time_t date;
+ time_t currentDate = time(0);
+
+ // URL
+ if (ok && (!fgets(buffer, 400, fs)))
+ ok = false;
+ if (ok)
+ {
+ int l = strlen(buffer);
+ if (l>0)
+ buffer[l-1] = 0; // Strip newline
+ if (m_.url.url() != buffer)
+ {
+ ok = false; // Hash collision
+ }
+ }
+
+ // Creation Date
+ if (ok && (!fgets(buffer, 400, fs)))
+ ok = false;
+ if (ok)
+ {
+ date = (time_t) strtoul(buffer, 0, 10);
+ if (m_maxCacheAge && (difftime(currentDate, date) > m_maxCacheAge))
+ {
+ m_bMustRevalidate = true;
+ m_expireDate = currentDate;
+ }
+ }
+
+ // Expiration Date
+ m_cacheExpireDateOffset = ftell(fs);
+ if (ok && (!fgets(buffer, 400, fs)))
+ ok = false;
+ if (ok)
+ {
+ if (m_request.cache == CC_Verify)
+ {
+ date = (time_t) strtoul(buffer, 0, 10);
+ // After the expire date we need to revalidate.
+ if (!date || difftime(currentDate, date) >= 0)
+ m_bMustRevalidate = true;
+ m_expireDate = date;
+ }
+ }
+
+ // ETag
+ if (ok && (!fgets(buffer, 400, fs)))
+ ok = false;
+ if (ok)
+ {
+ m_etag = TQString(buffer).stripWhiteSpace();
+ }
+
+ // Last-Modified
+ if (ok && (!fgets(buffer, 400, fs)))
+ ok = false;
+ if (ok)
+ {
+ m_lastModified = TQString(buffer).stripWhiteSpace();
+ }
+
+ fclose(fs);
+
+ if (ok)
+ return fs;
+
+ unlink( TQFile::encodeName(CEF) );
+ return 0;
+
+}
+
+void CacheInfo::flush()
+{
+ cachedFile().remove();
+}
+
+void CacheInfo::touch()
+{
+
+}
+void CacheInfo::setExpireDate(int);
+void CacheInfo::setExpireTimeout(int);
+
+
+int CacheInfo::creationDate();
+int CacheInfo::expireDate();
+int CacheInfo::expireTimeout();
+#endif
+
+void Job::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+void SimpleJob::virtual_hook( int id, void* data )
+{ TDEIO::Job::virtual_hook( id, data ); }
+
+void MkdirJob::virtual_hook( int id, void* data )
+{ SimpleJob::virtual_hook( id, data ); }
+
+void StatJob::virtual_hook( int id, void* data )
+{ SimpleJob::virtual_hook( id, data ); }
+
+void TransferJob::virtual_hook( int id, void* data )
+{ SimpleJob::virtual_hook( id, data ); }
+
+void MultiGetJob::virtual_hook( int id, void* data )
+{ TransferJob::virtual_hook( id, data ); }
+
+void MimetypeJob::virtual_hook( int id, void* data )
+{ TransferJob::virtual_hook( id, data ); }
+
+void FileCopyJob::virtual_hook( int id, void* data )
+{ Job::virtual_hook( id, data ); }
+
+void ListJob::virtual_hook( int id, void* data )
+{ SimpleJob::virtual_hook( id, data ); }
+
+void CopyJob::virtual_hook( int id, void* data )
+{ Job::virtual_hook( id, data ); }
+
+void DeleteJob::virtual_hook( int id, void* data )
+{ Job::virtual_hook( id, data ); }
+
+void LocalURLJob::virtual_hook( int id, void* data )
+{ Job::virtual_hook( id, data ); }
+
+
+#include "jobclasses.moc"
diff --git a/tdeio/tdeio/job.h b/tdeio/tdeio/job.h
new file mode 100644
index 000000000..d4d7dd41d
--- /dev/null
+++ b/tdeio/tdeio/job.h
@@ -0,0 +1,532 @@
+// -*- c++ -*-
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
+ David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kio_job_h__
+#define __kio_job_h__
+
+#include <tdeio/jobclasses.h>
+
+namespace TDEIO {
+
+
+ /**
+ * Creates a single directory.
+ *
+ *
+ *
+ *
+ * @param url The URL of the directory to create.
+ * @param permissions The permissions to set after creating the
+ * directory (unix-style), -1 for default permissions.
+ * @return A pointer to the job handling the operation.
+ */
+ TDEIO_EXPORT SimpleJob * mkdir( const KURL& url, int permissions = -1 );
+
+ /**
+ * Removes a single directory.
+ *
+ * The directory is assumed to be empty.
+ *
+ *
+ *
+ * @param url The URL of the directory to remove.
+ * @return A pointer to the job handling the operation.
+ */
+ TDEIO_EXPORT SimpleJob * rmdir( const KURL& url );
+
+ /**
+ * Changes permissions on a file or directory.
+ * See the other chmod below for changing many files
+ * or directories.
+ *
+ * @param url The URL of file or directory.
+ * @param permissions The permissions to set.
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT SimpleJob * chmod( const KURL& url, int permissions );
+
+ /**
+ * Rename a file or directory.
+ * Warning: this operation fails if a direct renaming is not
+ * possible (like with files or dirs on separate partitions)
+ * Use move or file_move in this case.
+ *
+ * @param src The original URL
+ * @param dest The final URL
+ * @param overwrite whether to automatically overwrite if the dest exists
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT SimpleJob * rename( const KURL& src, const KURL & dest, bool overwrite );
+
+ /**
+ * Create or move a symlink.
+ * This is the lowlevel operation, similar to file_copy and file_move.
+ * It doesn't do any check (other than those the slave does)
+ * and it doesn't show rename and skip dialogs - use TDEIO::link for that.
+ * @param target The string that will become the "target" of the link (can be relative)
+ * @param dest The symlink to create.
+ * @param overwrite whether to automatically overwrite if the dest exists
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT SimpleJob * symlink( const TQString & target, const KURL& dest, bool overwrite, bool showProgressInfo = true );
+
+ /**
+ * Execute any command that is specific to one slave (protocol).
+ *
+ * Examples are : HTTP POST, mount and unmount (kio_file)
+ *
+ * @param url The URL isn't passed to the slave, but is used to know
+ * which slave to send it to :-)
+ * @param data Packed data. The meaning is completely dependent on the
+ * slave, but usually starts with an int for the command number.
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT SimpleJob * special( const KURL& url, const TQByteArray & data, bool showProgressInfo = true );
+
+ /**
+ * Mount filesystem.
+ *
+ * Special job for @p kio_file.
+ *
+ * @param ro Mount read-only if @p true.
+ * @param fstype File system type (e.g. "ext2", can be 0L).
+ * @param dev Device (e.g. /dev/sda0).
+ * @param point Mount point, can be @p null.
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT SimpleJob *mount( bool ro, const char *fstype, const TQString& dev, const TQString& point, bool showProgressInfo = true );
+
+ /**
+ * Unmount filesystem.
+ *
+ * Special job for @p kio_file.
+ *
+ * @param point Point to unmount.
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT SimpleJob *unmount( const TQString & point, bool showProgressInfo = true );
+
+ /**
+ * Retrieve local URL if available
+ *
+ * @param remoteURL the remote URL to get the local URL for
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT LocalURLJob *localURL( const KURL& remoteUrl );
+
+ /**
+ * HTTP cache update
+ *
+ * @param url Url to update, protocol must be "http".
+ * @param no_cache If true, cache entry for @p url is deleted.
+ * @param expireDate Local machine time indicating when the entry is
+ * supposed to expire.
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT SimpleJob *http_update_cache( const KURL& url, bool no_cache, time_t expireDate);
+
+ /**
+ * Find all details for one file or directory.
+ *
+ * @param url the URL of the file
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT StatJob * stat( const KURL& url, bool showProgressInfo = true );
+ /**
+ * Find all details for one file or directory.
+ * This version of the call includes two additional booleans, @p sideIsSource and @p details.
+ *
+ * @param url the URL of the file
+ * @param sideIsSource is true when stating a source file (we will do a get on it if
+ * the stat works) and false when stating a destination file (target of a copy).
+ * The reason for this parameter is that in some cases the tdeioslave might not
+ * be able to determine a file's existence (e.g. HTTP doesn't allow it, FTP
+ * has issues with case-sensitivity on some systems).
+ * When the slave can't reliably determine the existence of a file, it will:
+ * @li be optimistic if sideIsSource=true, i.e. it will assume the file exists,
+ * and if it doesn't this will appear when actually trying to download it
+ * @li be pessimistic if sideIsSource=false, i.e. it will assume the file
+ * doesn't exist, to prevent showing "about to overwrite" errors to the user.
+ * If you simply want to check for existence without downloading/uploading afterwards,
+ * then you should use sideIsSource=false.
+ *
+ * @param details selects the level of details we want.
+ * By default this is 2 (all details wanted, including modification time, size, etc.),
+ * setDetails(1) is used when deleting: we don't need all the information if it takes
+ * too much time, no need to follow symlinks etc.
+ * setDetails(0) is used for very simple probing: we'll only get the answer
+ * "it's a file or a directory, or it doesn't exist". This is used by KRun.
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT StatJob * stat( const KURL& url, bool sideIsSource, short int details, bool showProgressInfo = true );
+
+ /**
+ * Get (a.k.a. read).
+ *
+ * The slave emits the data through data().
+ * @param url the URL of the file
+ * @param reload true to reload the file, false if it can be taken from the cache
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT TransferJob *get( const KURL& url, bool reload=false, bool showProgressInfo = true );
+
+ /**
+ * Put (a.k.a. write)
+ *
+ * @param url Where to write data.
+ * @param permissions May be -1. In this case no special permission mode is set.
+ * @param overwrite If true, any existing file will be overwritten.
+ * @param resume true to resume an operation. Warning, setting this to true means
+ * that the data will be appended to @p dest if @p dest exists.
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation.
+ * @see multi_get()
+ */
+ TDEIO_EXPORT TransferJob *put( const KURL& url, int permissions,
+ bool overwrite, bool resume, bool showProgressInfo = true );
+
+ /**
+ * HTTP POST (for form data).
+ *
+ * Example:
+ * \code
+ * job = TDEIO::http_post( url, postData, false );
+ * job->addMetaData("content-type", contentType );
+ * job->addMetaData("referrer", referrerURL);
+ * \endcode
+ *
+ * @p postData is the data that you want to send and
+ * @p contentType is the complete HTTP header line that
+ * specifies the content's MIME type, for example
+ * "Content-Type: text/xml".
+ *
+ * You MUST specify content-type!
+ *
+ * Often @p contentType is
+ * "Content-Type: application/x-www-form-urlencoded" and
+ * the @p postData is then an ASCII string (without null-termination!)
+ * with characters like space, linefeed and percent escaped like %20,
+ * %0A and %25.
+ *
+ * @param url Where to write the data.
+ * @param postData Encoded data to post.
+ * @param showProgressInfo true to display
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT TransferJob *http_post( const KURL& url, const TQByteArray &postData,
+ bool showProgressInfo = true );
+
+ /**
+ * Get (a.k.a. read), into a single TQByteArray.
+ * @see StoredTransferJob
+ *
+ * @param url the URL of the file
+ * @param reload true to reload the file, false if it can be taken from the cache
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation.
+ * @since 3.3
+ */
+ TDEIO_EXPORT StoredTransferJob *storedGet( const KURL& url, bool reload=false, bool showProgressInfo = true );
+
+ /**
+ * Put (a.k.a. write) data from a single TQByteArray.
+ * @see StoredTransferJob
+ *
+ * @param arr The data to write
+ * @param url Where to write data.
+ * @param permissions May be -1. In this case no special permission mode is set.
+ * @param overwrite If true, any existing file will be overwritten.
+ * @param resume true to resume an operation. Warning, setting this to true means
+ * that the data will be appended to @p dest if @p dest exists.
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation.
+ * @since 3.3
+ */
+ TDEIO_EXPORT StoredTransferJob *storedPut( const TQByteArray& arr, const KURL& url, int permissions,
+ bool overwrite, bool resume, bool showProgressInfo = true );
+
+ /**
+ * Creates a new multiple get job.
+ *
+ * @param id the id of the get operation
+ * @param url the URL of the file
+ * @param metaData the MetaData associated with the file
+ *
+ * @return the job handling the operation.
+ * @see get()
+ */
+ TDEIO_EXPORT MultiGetJob *multi_get( long id, const KURL &url, const MetaData &metaData);
+
+ /**
+ * Find mimetype for one file or directory.
+ *
+ * @param url the URL of the file
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT MimetypeJob * mimetype( const KURL& url,
+ bool showProgressInfo = true );
+
+ /**
+ * Copy a single file.
+ *
+ * Uses either SlaveBase::copy() if the slave supports that
+ * or get() and put() otherwise.
+ * @param src Where to get the file.
+ * @param dest Where to put the file.
+ * @param permissions May be -1. In this case no special permission mode is set.
+ * @param overwrite If true, any existing file will be overwritten.
+ * @param resume true to resume an operation. Warning, setting this to true means
+ * that @p src will be appended to @p dest if @p dest exists.
+ * You probably don't want that, so leave it to false :)
+ *
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT FileCopyJob *file_copy( const KURL& src, const KURL& dest, int permissions=-1,
+ bool overwrite=false, bool resume=false,
+ bool showProgressInfo = true);
+
+ /**
+ * Move a single file.
+ *
+ * Use either SlaveBase::rename() if the slave supports that,
+ * or copy() and del() otherwise, or eventually get() & put() & del()
+ * @param src Where to get the file.
+ * @param dest Where to put the file.
+ * @param permissions May be -1. In this case no special permission mode is set.
+ * @param overwrite If @p true, any existing file will be overwritten.
+ * @param resume true to resume an operation. Warning, setting this to true means
+ * that @p src will be appended to @p dest if @p dest exists.
+ * You probably don't want that, so leave it to false :)
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT FileCopyJob *file_move( const KURL& src, const KURL& dest, int permissions=-1,
+ bool overwrite=false, bool resume=false,
+ bool showProgressInfo = true);
+
+ /**
+ * Delete a single file.
+ *
+ * @param src File to delete.
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT SimpleJob *file_delete( const KURL& src, bool showProgressInfo = true);
+
+ /**
+ * List the contents of @p url, which is assumed to be a directory.
+ *
+ * "." and ".." are returned, filter them out if you don't want them.
+ *
+ *
+ * @param url the url of the directory
+ * @param showProgressInfo true to show progress information
+ * @param includeHidden true for all files, false to cull out UNIX hidden
+ * files/dirs (whose names start with dot)
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT ListJob *listDir( const KURL& url, bool showProgressInfo = true,
+ bool includeHidden = true );
+
+ /**
+ * The same as the previous method, but recurses subdirectories.
+ * Directory links are not followed.
+ *
+ * "." and ".." are returned but only for the toplevel directory.
+ * Filter them out if you don't want them.
+ *
+ * @param url the url of the directory
+ * @param showProgressInfo true to show progress information
+ * @param includeHidden true for all files, false to cull out UNIX hidden
+ * files/dirs (whose names start with dot)
+ * @return the job handling the operation.
+ */
+ TDEIO_EXPORT ListJob *listRecursive( const KURL& url, bool showProgressInfo = true,
+ bool includeHidden = true );
+
+ /**
+ * Copy a file or directory @p src into the destination @p dest,
+ * which can be a file (including the final filename) or a directory
+ * (into which @p src will be copied).
+ *
+ * This emulates the cp command completely.
+ *
+ * @param src the file or directory to copy
+ * @param dest the destination
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation
+ * @see copyAs()
+ */
+ TDEIO_EXPORT CopyJob *copy( const KURL& src, const KURL& dest, bool showProgressInfo = true );
+
+ /**
+ * Copy a file or directory @p src into the destination @p dest,
+ * which is the destination name in any case, even for a directory.
+ *
+ * As opposed to copy(), this doesn't emulate cp, but is the only
+ * way to copy a directory, giving it a new name and getting an error
+ * box if a directory already exists with the same name.
+ *
+ * @param src the file or directory to copy
+ * @param dest the destination
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation
+ */
+ TDEIO_EXPORT CopyJob *copyAs( const KURL& src, const KURL& dest, bool showProgressInfo = true );
+
+ /**
+ * Copy a list of file/dirs @p src into a destination directory @p dest.
+ *
+ * @param src the list of files and/or directories
+ * @param dest the destination
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation
+ */
+ TDEIO_EXPORT CopyJob *copy( const KURL::List& src, const KURL& dest, bool showProgressInfo = true );
+
+ /**
+ * Moves a file or directory @p src to the given destination @p dest.
+ *
+ * @param src the file or directory to copy
+ * @param dest the destination
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation
+ * @see copy()
+ * @see moveAs()
+ */
+ TDEIO_EXPORT CopyJob *move( const KURL& src, const KURL& dest, bool showProgressInfo = true );
+ /**
+ * Moves a file or directory @p src to the given destination @p dest. Unlike move()
+ * this operation will fail when the directory already exists.
+ *
+ * @param src the file or directory to copy
+ * @param dest the destination
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation
+ * @see copyAs()
+ */
+ TDEIO_EXPORT CopyJob *moveAs( const KURL& src, const KURL& dest, bool showProgressInfo = true );
+ /**
+ * Moves a list of files or directories @p src to the given destination @p dest.
+ *
+ * @param src the list of files or directories to copy
+ * @param dest the destination
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation
+ * @see copy()
+ */
+ TDEIO_EXPORT CopyJob *move( const KURL::List& src, const KURL& dest, bool showProgressInfo = true );
+
+ /**
+ * Create a link.
+ * If the protocols and hosts are the same, a Unix symlink will be created.
+ * Otherwise, a .desktop file of Type Link and pointing to the src URL will be created.
+ *
+ * @param src The existing file or directory, 'target' of the link.
+ * @param destDir Destination directory where the link will be created.
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation
+ */
+ TDEIO_EXPORT CopyJob *link( const KURL& src, const KURL& destDir, bool showProgressInfo = true );
+
+ /**
+ * Create several links
+ * If the protocols and hosts are the same, a Unix symlink will be created.
+ * Otherwise, a .desktop file of Type Link and pointing to the src URL will be created.
+ *
+ * @param src The existing files or directories, 'targets' of the link.
+ * @param destDir Destination directory where the links will be created.
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation
+ * @see link()
+ */
+ TDEIO_EXPORT CopyJob *link( const KURL::List& src, const KURL& destDir, bool showProgressInfo = true );
+
+ /**
+ * Create a link. Unlike link() this operation will fail when the directory already
+ * exists.
+ * If the protocols and hosts are the same, a Unix symlink will be created.
+ * Otherwise, a .desktop file of Type Link and pointing to the src URL will be created.
+ *
+ * @param src The existing file or directory, 'target' of the link.
+ * @param dest Destination directory where the link will be created.
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation
+ * @see link ()
+ * @see copyAs()
+ */
+ TDEIO_EXPORT CopyJob *linkAs( const KURL& src, const KURL& dest, bool showProgressInfo = true );
+
+ /**
+ * Trash a file or directory.
+ * This is currently only supported for local files and directories.
+ * Use "KURL src; src.setPath( path );" to create a URL from a path.
+ *
+ * @param src file to delete
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation
+ * @since 3.4
+ */
+ TDEIO_EXPORT CopyJob *trash( const KURL& src, bool showProgressInfo = true );
+
+ /**
+ * Trash a list of files or directories.
+ * This is currently only supported for local files and directories.
+ *
+ * @param src the files to delete
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation
+ * @since 3.4
+ */
+ TDEIO_EXPORT CopyJob *trash( const KURL::List& src, bool showProgressInfo = true );
+
+ /**
+ * Delete a file or directory.
+ *
+ * @param src file to delete
+ * @param shred obsolete (TODO remove in KDE4)
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation
+ */
+ TDEIO_EXPORT DeleteJob *del( const KURL& src, bool shred = false, bool showProgressInfo = true );
+
+ /**
+ * Deletes a list of files or directories.
+ *
+ * @param src the files to delete
+ * @param shred obsolete (TODO remove in KDE4)
+ * @param showProgressInfo true to show progress information
+ * @return the job handling the operation
+ */
+ TDEIO_EXPORT DeleteJob *del( const KURL::List& src, bool shred = false, bool showProgressInfo = true );
+}
+
+#endif
+
diff --git a/tdeio/tdeio/jobclasses.h b/tdeio/tdeio/jobclasses.h
new file mode 100644
index 000000000..7f5ec1f85
--- /dev/null
+++ b/tdeio/tdeio/jobclasses.h
@@ -0,0 +1,1909 @@
+// -*- c++ -*-
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
+ David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kio_jobclasses_h__
+#define __kio_jobclasses_h__
+
+#include <tqobject.h>
+#include <tqptrlist.h>
+#include <tqstring.h>
+#include <tqstringlist.h>
+#include <tqguardedptr.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <kurl.h>
+#include <tdeio/global.h>
+
+class Observer;
+class TQTimer;
+
+#define KIO_COPYJOB_HAS_SETINTERACTIVE // new in 3.4. Used by kio_trash.
+
+namespace TDEIO {
+
+ class Slave;
+ class SlaveInterface;
+
+
+ /**
+ * The base class for all jobs.
+ * For all jobs created in an application, the code looks like
+ *
+ * \code
+ * TDEIO::Job * job = TDEIO::someoperation( some parameters );
+ * connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ),
+ * this, TQT_SLOT( slotResult( TDEIO::Job * ) ) );
+ * \endcode
+ * (other connects, specific to the job)
+ *
+ * And slotResult is usually at least:
+ *
+ * \code
+ * if ( job->error() )
+ * job->showErrorDialog( this or 0L );
+ * \endcode
+ * @see TDEIO::Scheduler
+ * @see TDEIO::Slave
+ */
+ class TDEIO_EXPORT Job : public TQObject {
+ Q_OBJECT
+
+ protected:
+ Job( bool showProgressInfo );
+
+ public:
+ virtual ~Job();
+
+ /**
+ * Abort this job.
+ * This kills all subjobs and deletes the job.
+ *
+ * @param quietly if false, Job will emit signal result
+ * and ask tdeio_uiserver to close the progress window.
+ * @p quietly is set to true for subjobs. Whether applications
+ * should call with true or false depends on whether they rely
+ * on result being emitted or not.
+ */
+ virtual void kill( bool quietly = true );
+
+ /**
+ * Returns the error code, if there has been an error.
+ * Only call this method from the slot connected to result().
+ * @return the error code for this job, 0 if no error.
+ * Error codes are defined in TDEIO::Error.
+ */
+ int error() const { return m_error; }
+
+ /**
+ * Returns the progress id for this job.
+ * @return the progress id for this job, as returned by uiserver
+ */
+ int progressId() const { return m_progressId; }
+
+ /**
+ * Returns the error text if there has been an error.
+ * Only call if error is not 0.
+ * This is really internal, better use errorString() or errorDialog().
+ *
+ * @return a string to help understand the error, usually the url
+ * related to the error. Only valid if error() is not 0.
+ */
+ const TQString & errorText() const { return m_errorText; }
+
+ /**
+ * Converts an error code and a non-i18n error message into an
+ * error message in the current language. The low level (non-i18n)
+ * error message (usually a url) is put into the translated error
+ * message using %1.
+ *
+ * Example for errid == ERR_CANNOT_OPEN_FOR_READING:
+ * \code
+ * i18n( "Could not read\n%1" ).arg( errortext );
+ * \endcode
+ * Use this to display the error yourself, but for a dialog box
+ * use Job::showErrorDialog. Do not call it if error()
+ * is not 0.
+ * @return the error message and if there is no error, a message
+ * telling the user that the app is broken, so check with
+ * error() whether there is an error
+ */
+ TQString errorString() const;
+
+ /**
+ * Converts an error code and a non-i18n error message into i18n
+ * strings suitable for presentation in a detailed error message box.
+ *
+ * @param reqUrl the request URL that generated this error message
+ * @param method the method that generated this error message
+ * (unimplemented)
+ * @return the following strings: caption, error + description,
+ * causes+solutions
+ */
+ TQStringList detailedErrorStrings(const KURL *reqUrl = 0L,
+ int method = -1) const;
+
+ /**
+ * Display a dialog box to inform the user of the error given by
+ * this job.
+ * Only call if error is not 0, and only in the slot connected
+ * to result.
+ * @param parent the parent widget for the dialog box, can be 0 for
+ * top-level
+ */
+ void showErrorDialog( TQWidget * parent = 0L );
+
+ /**
+ * Enable or disable the automatic error handling. When automatic
+ * error handling is enabled and an error occurs, then showErrorDialog()
+ * is called with the specified @p parentWidget (if supplied) , right before
+ * the emission of the result signal.
+ *
+ * The default is false.
+ *
+ * @param enable enable or disable automatic error handling
+ * @param parentWidget the parent widget, passed to showErrorDialog.
+ * Can be 0 for top-level
+ * @see isAutoErrorHandlingEnabled(), showErrorDialog()
+ */
+ void setAutoErrorHandlingEnabled( bool enable, TQWidget *parentWidget = 0 );
+
+ /**
+ * Returns whether automatic error handling is enabled or disabled.
+ * @return true if automatic error handling is enabled
+ * @see setAutoErrorHandlingEnabled()
+ */
+ bool isAutoErrorHandlingEnabled() const;
+
+ /**
+ * Enable or disable the automatic warning handling. When automatic
+ * warning handling is enabled and an error occurs, then a message box
+ * is displayed with the warning message
+ *
+ * The default is true.
+ *
+ * See also isAutoWarningHandlingEnabled , showErrorDialog
+ *
+ * @param enable enable or disable automatic warning handling
+ * @see isAutoWarningHandlingEnabled()
+ * @since 3.5
+ */
+ void setAutoWarningHandlingEnabled( bool enable );
+
+ /**
+ * Returns whether automatic warning handling is enabled or disabled.
+ * See also setAutoWarningHandlingEnabled .
+ * @return true if automatic warning handling is enabled
+ * @see setAutoWarningHandlingEnabled()
+ * @since 3.5
+ */
+ bool isAutoWarningHandlingEnabled() const;
+
+ /**
+ * Enable or disable the message display from the job.
+ *
+ * The default is true.
+ * @param enable enable or disable message display
+ * @since 3.4.1
+ */
+ void setInteractive(bool enable);
+
+ /**
+ * Returns whether message display is enabled or disabled.
+ * @return true if message display is enabled
+ * @see setInteractive()
+ * @since 3.4.1
+ */
+ bool isInteractive() const;
+ /**
+ * Associate this job with a window given by @p window.
+ * @param window the window to associate to
+ * @see window()
+ */
+ void setWindow(TQWidget *window);
+
+ /**
+ * Returns the window this job is associated with.
+ * @return the associated window
+ * @see setWindow()
+ */
+ TQWidget *window() const;
+
+ /**
+ * Updates the last user action timestamp to the given time.
+ * See TDEApplication::updateUserTimestamp() .
+ * @since 3.5.6
+ */
+ void updateUserTimestamp( unsigned long time );
+
+ /**
+ * Set the parent Job.
+ * One example use of this is when FileCopyJob calls open_RenameDlg,
+ * it must pass the correct progress ID of the parent CopyJob
+ * (to hide the progress dialog).
+ * You can set the parent job only once. By default a job does not
+ * have a parent job.
+ * @param parentJob the new parent job
+ * @since 3.1
+ */
+ void setParentJob( Job* parentJob );
+
+ /**
+ * Returns the parent job, if there is one.
+ * @return the parent job, or 0 if there is none
+ * @see setParentJob
+ * @since 3.1
+ */
+ Job* parentJob() const;
+
+ /**
+ * Set meta data to be sent to the slave, replacing existing
+ * meta data.
+ * @param metaData the meta data to set
+ * @see addMetaData()
+ * @see mergeMetaData()
+ */
+ void setMetaData( const TDEIO::MetaData &metaData);
+
+ /**
+ * Add key/value pair to the meta data that is sent to the slave.
+ * @param key the key of the meta data
+ * @param value the value of the meta data
+ * @see setMetaData()
+ * @see mergeMetaData()
+ */
+ void addMetaData(const TQString &key, const TQString &value);
+
+ /**
+ * Add key/value pairs to the meta data that is sent to the slave.
+ * If a certain key already existed, it will be overridden.
+ * @param values the meta data to add
+ * @see setMetaData()
+ * @see mergeMetaData()
+ */
+ void addMetaData(const TQMap<TQString,TQString> &values);
+
+ /**
+ * Add key/value pairs to the meta data that is sent to the slave.
+ * If a certain key already existed, it will remain unchanged.
+ * @param values the meta data to merge
+ * @see setMetaData()
+ * @see addMetaData()
+ */
+ void mergeMetaData(const TQMap<TQString,TQString> &values);
+
+ /**
+ * @internal. For the scheduler. Do not use.
+ */
+ MetaData outgoingMetaData() const;
+
+ /**
+ * Get meta data received from the slave.
+ * (Valid when first data is received and/or slave is finished)
+ * @return the job's meta data
+ */
+ MetaData metaData() const;
+
+ /**
+ * Query meta data received from the slave.
+ * (Valid when first data is received and/or slave is finished)
+ * @param key the key of the meta data to retrieve
+ * @return the value of the meta data, or TQString::null if the
+ * @p key does not exist
+ */
+ TQString queryMetaData(const TQString &key);
+
+ /**
+ * Returns the processed size for this job.
+ * @see processedSize
+ * @since 3.2
+ */
+ TDEIO::filesize_t getProcessedSize();
+
+ signals:
+ /**
+ * Emitted when the job is finished, in any case (completed, canceled,
+ * failed...). Use error to know the result.
+ * @param job the job that emitted this signal
+ */
+ void result( TDEIO::Job *job );
+
+ /**
+ * @deprecated. Don't use !
+ * Emitted when the job is canceled.
+ * Signal result() is emitted as well, and error() is,
+ * in this case, ERR_USER_CANCELED.
+ * @param job the job that emitted this signal
+ */
+ void canceled( TDEIO::Job *job );
+
+ /**
+ * Emitted to display information about this job, as sent by the slave.
+ * Examples of message are "Resolving host", "Connecting to host...", etc.
+ * @param job the job that emitted this signal
+ * @param msg the info message
+ */
+ void infoMessage( TDEIO::Job *job, const TQString & msg );
+ // KDE4: Separate rich-text string from plain-text string, for different widgets.
+
+ /**
+ * Emitted to display a warning about this job, as sent by the slave.
+ * @param job the job that emitted this signal
+ * @param msg the info message
+ * @since 3.5
+ */
+ void warning( TDEIO::Job *job, const TQString & msg );
+ // KDE4: Separate rich-text string from plain-text string, for different widgets.
+
+ /**
+ * Emitted when the slave successfully connected to the host.
+ * There is no guarantee the slave will send this, and this is
+ * currently unused (in the applications).
+ * @param job the job that emitted this signal
+ */
+ void connected( TDEIO::Job *job );
+
+ /**
+ * Progress signal showing the overall progress of the job
+ * This is valid for any kind of job, and allows using a
+ * a progress bar very easily. (see KProgress).
+ * Note that this signal is not emitted for finished jobs.
+ * @param job the job that emitted this signal
+ * @param percent the percentage
+ */
+ void percent( TDEIO::Job *job, unsigned long percent );
+
+ /**
+ * Emitted when we know the size of this job (data size for transfers,
+ * number of entries for listings).
+ * @param job the job that emitted this signal
+ * @param size the total size in bytes
+ */
+ void totalSize( TDEIO::Job *job, TDEIO::filesize_t size );
+
+ /**
+ * Regularly emitted to show the progress of this job
+ * (current data size for transfers, entries listed).
+ * @param job the job that emitted this signal
+ * @param size the processed size in bytes
+ */
+ void processedSize( TDEIO::Job *job, TDEIO::filesize_t size );
+
+ /**
+ * Emitted to display information about the speed of this job.
+ * @param job the job that emitted this signal
+ * @param speed the speed in bytes/s
+ */
+ void speed( TDEIO::Job *job, unsigned long speed );
+
+ protected slots:
+ /**
+ * Called whenever a subjob finishes.
+ * Default implementation checks for errors and propagates
+ * to parent job, then calls removeSubjob.
+ * Override if you don't want subjobs errors to be propagated.
+ * @param job the subjob
+ * @see result()
+ */
+ virtual void slotResult( TDEIO::Job *job );
+
+ /**
+ * Forward signal from subjob.
+ * @param job the subjob
+ * @param speed the speed in bytes/s
+ * @see speed()
+ */
+ void slotSpeed( TDEIO::Job *job, unsigned long speed );
+ /**
+ * Forward signal from subjob.
+ * @param job the subjob
+ * @param msg the info message
+ * @see infoMessage()
+ */
+ void slotInfoMessage( TDEIO::Job *job, const TQString &msg );
+
+ /**
+ * Remove speed information.
+ */
+ void slotSpeedTimeout();
+
+ protected:
+ /**
+ * Add a job that has to be finished before a result
+ * is emitted. This has obviously to be called before
+ * the finish signal is emitted by the slave.
+ *
+ * @param job the subjob to add
+ * @param inheritMetaData if true, the subjob will
+ * inherit the meta data from this job.
+ */
+ virtual void addSubjob( Job *job, bool inheritMetaData=true );
+
+ /**
+ * Mark a sub job as being done. If it's the last to
+ * wait on the job will emit a result - jobs with
+ * two steps might want to override slotResult
+ * in order to avoid calling this method.
+ *
+ * @param job the subjob to add
+ */
+ virtual void removeSubjob( Job *job );
+ /**
+ * Overloaded version of removeSubjob
+ * @param job the subjob to remove
+ * @param mergeMetaData if set, the metadata received by the subjob is
+ * merged into this job.
+ * @param emitResultIfLast if this was the last subjob, emit result,
+ * i.e. terminate this job.
+ */
+ void removeSubjob( Job *job, bool mergeMetaData, bool emitResultIfLast ); // KDE4: merge with above, with =true to both
+
+ /**
+ * Utility function for inherited jobs.
+ * Emits the percent signal if bigger than m_percent,
+ * after calculating it from the parameters.
+ *
+ * @param processedSize the processed size in bytes
+ * @param totalSize the total size in bytes
+ */
+ void emitPercent( TDEIO::filesize_t processedSize, TDEIO::filesize_t totalSize );
+
+ /**
+ * Utility function for inherited jobs.
+ * Emits the speed signal and starts the timer for removing that info
+ *
+ * @param speed the speed in bytes/s
+ */
+ void emitSpeed( unsigned long speed );
+
+ /**
+ * Utility function to emit the result signal, and suicide this job.
+ * It first tells the observer to hide the progress dialog for this job.
+ */
+ void emitResult();
+
+ /**
+ * Set the processed size, does not emit processedSize
+ * @since 3.2
+ */
+ void setProcessedSize(TDEIO::filesize_t size);
+
+ /**
+ * @internal
+ */
+ unsigned long userTimestamp() const;
+
+ /**
+ * @internal
+ * Some extra storage space for jobs that don't have their own
+ * private d pointer.
+ */
+ enum { EF_TransferJobAsync = (1 << 0),
+ EF_TransferJobNeedData = (1 << 1),
+ EF_TransferJobDataSent = (1 << 2),
+ EF_ListJobUnrestricted = (1 << 3) };
+ int &extraFlags();
+
+ TQPtrList<Job> subjobs;
+ int m_error;
+ TQString m_errorText;
+ unsigned long m_percent;
+ int m_progressId; // for uiserver
+ TQTimer *m_speedTimer;
+ TQGuardedPtr<TQWidget> m_window;
+ MetaData m_outgoingMetaData;
+ MetaData m_incomingMetaData;
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ private:
+ class JobPrivate;
+ JobPrivate *d;
+ };
+
+ /**
+ * A simple job (one url and one command).
+ * This is the base class for all jobs that are scheduled.
+ * Other jobs are high-level jobs (CopyJob, DeleteJob, FileCopyJob...)
+ * that manage subjobs but aren't scheduled directly.
+ */
+ class TDEIO_EXPORT SimpleJob : public TDEIO::Job {
+ Q_OBJECT
+
+ public:
+ /**
+ * Creates a new simple job. You don't need to use this constructor,
+ * unless you create a new job that inherits from SimpleJob.
+ * @param url the url of the job
+ * @param command the command of the job
+ * @param packedArgs the arguments
+ * @param showProgressInfo true to show progress information to the user
+ */
+ SimpleJob(const KURL& url, int command, const TQByteArray &packedArgs,
+ bool showProgressInfo);
+
+ ~SimpleJob();
+
+ /**
+ * Returns the SimpleJob's URL
+ * @return the url
+ */
+ const KURL& url() const { return m_url; }
+
+ /**
+ * Abort job.
+ * This kills all subjobs and deletes the job.
+ * @param quietly if true, Job will emit signal result
+ * Should only be set to false when the user kills the job
+ * (from tdeio_uiserver), not when you want to abort a job.
+ */
+ virtual void kill( bool quietly = true );
+
+ /**
+ * Abort job.
+ * Suspends slave to be reused by another job for the same request.
+ */
+ virtual void putOnHold();
+
+ /**
+ * Discard suspended slave.
+ */
+ static void removeOnHold();
+
+ /**
+ * @internal
+ * Called by the scheduler when a slave gets to
+ * work on this job.
+ **/
+ virtual void start( Slave *slave );
+
+ /**
+ * @internal
+ * Called to detach a slave from a job.
+ **/
+ void slaveDone();
+
+ /**
+ * @internal
+ * Slave in use by this job.
+ */
+ Slave *slave() const { return m_slave; }
+
+ /**
+ * @internal
+ */
+ int command() const { return m_command; }
+
+ public slots:
+ /**
+ * Forward signal from the slave
+ * Can also be called by the parent job, when it knows the size.
+ * @param data_size the total size
+ */
+ void slotTotalSize( TDEIO::filesize_t data_size );
+
+ protected slots:
+ /**
+ * Called when the slave marks the job
+ * as finished.
+ */
+ virtual void slotFinished( );
+
+ /**
+ * @internal
+ * Called on a slave's warning.
+ */
+ void slotWarning( const TQString & ); // KDE4: make virtual
+
+ /**
+ * Called on a slave's info message.
+ * @param s the info message
+ * @see infoMessage()
+ */
+ void slotInfoMessage( const TQString &s ); // KDE4: make virtual
+
+ /**
+ * Called on a slave's connected signal.
+ * @see connected()
+ */
+ void slotConnected();
+
+ /**
+ * Forward signal from the slave.
+ * @param data_size the processed size in bytes
+ * @see processedSize()
+ */
+ void slotProcessedSize( TDEIO::filesize_t data_size );
+ /**
+ * Forward signal from the slave.
+ * @param speed the speed in bytes/s
+ * @see speed()
+ */
+ void slotSpeed( unsigned long speed );
+
+ /**
+ * MetaData from the slave is received.
+ * @param _metaData the meta data
+ * @see metaData()
+ */
+ virtual void slotMetaData( const TDEIO::MetaData &_metaData);
+
+ public slots:
+ /**
+ * @internal
+ * Called on a slave's error.
+ * Made public for the scheduler.
+ */
+ virtual void slotError( int , const TQString & );
+
+ protected slots:
+ /**
+ * @internal
+ */
+ void slotNeedProgressId();
+
+ protected:
+ Slave * m_slave;
+ TQByteArray m_packedArgs;
+ KURL m_url;
+ KURL m_subUrl;
+ int m_command;
+ TDEIO::filesize_t m_totalSize;
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ /*
+ * Allow jobs that inherit SimpleJob and are aware
+ * of redirections to store the SSL session used.
+ * Retrieval is handled by SimpleJob::start
+ * @param m_redirectionURL Reference to redirection URL,
+ * used instead of m_url if not empty
+ */
+ void storeSSLSessionFromJob(const KURL &m_redirectionURL);
+ private:
+ class SimpleJobPrivate* d;
+ };
+
+ /**
+ * A KIO job that retrieves information about a file or directory.
+ * @see TDEIO::stat()
+ */
+ class TDEIO_EXPORT StatJob : public SimpleJob {
+
+ Q_OBJECT
+
+ public:
+ /**
+ * Do not use this constructor to create a StatJob, use TDEIO::stat() instead.
+ * @param url the url of the file or directory to check
+ * @param command the command to issue
+ * @param packedArgs the arguments
+ * @param showProgressInfo true to show progress information to the user
+ */
+ StatJob(const KURL& url, int command, const TQByteArray &packedArgs, bool showProgressInfo);
+
+ /**
+ * A stat() can have two meanings. Either we want to read from this URL,
+ * or to check if we can write to it. First case is "source", second is "dest".
+ * It is necessary to know what the StatJob is for, to tune the tdeioslave's behavior
+ * (e.g. with FTP).
+ * @param source true for "source" mode, false for "dest" mode
+ */
+ void setSide( bool source ) { m_bSource = source; }
+
+ /**
+ * Selects the level of @p details we want.
+ * By default this is 2 (all details wanted, including modification time, size, etc.),
+ * setDetails(1) is used when deleting: we don't need all the information if it takes
+ * too much time, no need to follow symlinks etc.
+ * setDetails(0) is used for very simple probing: we'll only get the answer
+ * "it's a file or a directory, or it doesn't exist". This is used by KRun.
+ * @param details 2 for all details, 1 for simple, 0 for very simple
+ */
+ void setDetails( short int details ) { m_details = details; }
+
+ /**
+ * Call this in the slot connected to result,
+ * and only after making sure no error happened.
+ * @return the result of the stat
+ */
+ const UDSEntry & statResult() const { return m_statResult; }
+
+ /**
+ * @internal
+ * Called by the scheduler when a @p slave gets to
+ * work on this job.
+ * @param slave the slave that starts working on this job
+ */
+ virtual void start( Slave *slave );
+
+ signals:
+ /**
+ * Signals a redirection.
+ * Use to update the URL shown to the user.
+ * The redirection itself is handled internally.
+ * @param job the job that is redirected
+ * @param url the new url
+ */
+ void redirection( TDEIO::Job *job, const KURL &url );
+
+ /**
+ * Signals a permanent redirection.
+ * The redirection itself is handled internally.
+ * @param job the job that is redirected
+ * @param fromUrl the original URL
+ * @param toUrl the new URL
+ * @since 3.1
+ */
+ void permanentRedirection( TDEIO::Job *job, const KURL &fromUrl, const KURL &toUrl );
+
+ protected slots:
+ void slotStatEntry( const TDEIO::UDSEntry & entry );
+ void slotRedirection( const KURL &url);
+ virtual void slotFinished();
+ virtual void slotMetaData( const TDEIO::MetaData &_metaData);
+
+ protected:
+ UDSEntry m_statResult;
+ KURL m_redirectionURL;
+ bool m_bSource;
+ short int m_details;
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ private:
+ class StatJobPrivate;
+ StatJobPrivate *d;
+ };
+
+ /**
+ * A KIO job that creates a directory
+ * @see TDEIO::mkdir()
+ * @since 3.3
+ */
+ class TDEIO_EXPORT MkdirJob : public SimpleJob {
+
+ Q_OBJECT
+
+ public:
+ /**
+ * Do not use this constructor to create a MkdirJob, use TDEIO::mkdir() instead.
+ * @param url the url of the file or directory to check
+ * @param command the command to issue
+ * @param packedArgs the arguments
+ * @param showProgressInfo true to show progress information to the user
+ */
+ MkdirJob(const KURL& url, int command, const TQByteArray &packedArgs, bool showProgressInfo);
+
+ /**
+ * @internal
+ * Called by the scheduler when a @p slave gets to
+ * work on this job.
+ * @param slave the slave that starts working on this job
+ */
+ virtual void start( Slave *slave );
+
+ signals:
+ /**
+ * Signals a redirection.
+ * Use to update the URL shown to the user.
+ * The redirection itself is handled internally.
+ * @param job the job that is redirected
+ * @param url the new url
+ */
+ void redirection( TDEIO::Job *job, const KURL &url );
+
+ /**
+ * Signals a permanent redirection.
+ * The redirection itself is handled internally.
+ * @param job the job that is redirected
+ * @param fromUrl the original URL
+ * @param toUrl the new URL
+ */
+ void permanentRedirection( TDEIO::Job *job, const KURL &fromUrl, const KURL &toUrl );
+
+ protected slots:
+ void slotRedirection( const KURL &url);
+ virtual void slotFinished();
+
+ protected:
+ KURL m_redirectionURL;
+
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ private:
+ class MkdirJobPrivate;
+ MkdirJobPrivate *d;
+ };
+
+ /**
+ * @internal
+ * Used for direct copy from or to the local filesystem (i.e. SlaveBase::copy())
+ */
+ class TDEIO_EXPORT DirectCopyJob : public SimpleJob {
+ Q_OBJECT
+
+ public:
+ /**
+ * Do not create a DirectCopyJob. Use TDEIO::copy() or TDEIO::file_copy() instead.
+ */
+ DirectCopyJob(const KURL& url, int command, const TQByteArray &packedArgs,
+ bool showProgressInfo);
+ /**
+ * @internal
+ * Called by the scheduler when a @p slave gets to
+ * work on this job.
+ * @param slave the slave that starts working on this job
+ */
+ virtual void start(Slave *slave);
+
+ signals:
+ /**
+ * @internal
+ * Emitted if the job found an existing partial file
+ * and supports resuming. Used by FileCopyJob.
+ */
+ void canResume( TDEIO::Job *job, TDEIO::filesize_t offset );
+
+ private slots:
+ void slotCanResume( TDEIO::filesize_t offset );
+ };
+
+
+ /**
+ * The transfer job pumps data into and/or out of a Slave.
+ * Data is sent to the slave on request of the slave ( dataReq).
+ * If data coming from the slave can not be handled, the
+ * reading of data from the slave should be suspended.
+ */
+ class TDEIO_EXPORT TransferJob : public SimpleJob {
+ Q_OBJECT
+
+ public:
+ /**
+ * Do not create a TransferJob. Use TDEIO::get() or TDEIO::put()
+ * instead.
+ * @param url the url to get or put
+ * @param command the command to issue
+ * @param packedArgs the arguments
+ * @param _staticData additional data to transmit (e.g. in a HTTP Post)
+ * @param showProgressInfo true to show progress information to the user
+ */
+ TransferJob(const KURL& url, int command,
+ const TQByteArray &packedArgs,
+ const TQByteArray &_staticData,
+ bool showProgressInfo);
+
+ /**
+ * @internal
+ * Called by the scheduler when a @p slave gets to
+ * work on this job.
+ * @param slave the slave that starts working on this job
+ */
+ virtual void start(Slave *slave);
+
+ /**
+ * Called when m_subJob finishes.
+ * @param job the job that finished
+ */
+ virtual void slotResult( TDEIO::Job *job );
+
+ /**
+ * Flow control. Suspend data processing from the slave.
+ */
+ void suspend();
+
+ /**
+ * Flow control. Resume data processing from the slave.
+ */
+ void resume();
+
+ /**
+ * Flow control.
+ * @return true if the job is suspended
+ */
+ bool isSuspended() const { return m_suspended; }
+
+
+ /**
+ * Checks whether we got an error page. This currently only happens
+ * with HTTP urls. Call this from your slot connected to result().
+ *
+ * @return true if we got an (HTML) error page from the server
+ * instead of what we asked for.
+ */
+ bool isErrorPage() const { return m_errorPage; }
+
+ /**
+ * Enable the async data mode.
+ * When async data is enabled, data should be provided to the job by
+ * calling sendAsyncData() instead of returning data in the
+ * dataReq() signal.
+ * @since 3.2
+ */
+ void setAsyncDataEnabled(bool enabled);
+
+ /**
+ * Provide data to the job when async data is enabled.
+ * Should be called exactly once after receiving a dataReq signal
+ * Sending an empty block indicates end of data.
+ * @since 3.2
+ */
+ void sendAsyncData(const TQByteArray &data);
+
+ /**
+ * When enabled, the job reports the amount of data that has been sent,
+ * instead of the amount of data that that has been received.
+ * @see slotProcessedSize
+ * @see slotSpeed
+ * @since 3.2
+ */
+ void setReportDataSent(bool enabled);
+
+ /**
+ * Returns whether the job reports the amount of data that has been
+ * sent (true), or whether the job reports the amount of data that
+ * has been received (false)
+ * @since 3.2
+ */
+ bool reportDataSent();
+
+ signals:
+ /**
+ * Data from the slave has arrived.
+ * @param job the job that emitted this signal
+ * @param data data received from the slave.
+ *
+ * End of data (EOD) has been reached if data.size() == 0, however, you
+ * should not be certain of data.size() == 0 ever happening (e.g. in case
+ * of an error), so you should rely on result() instead.
+ */
+ void data( TDEIO::Job *job, const TQByteArray &data );
+
+ /**
+ * Request for data.
+ * Please note, that you shouldn't put too large chunks
+ * of data in it as this requires copies within the frame
+ * work, so you should rather split the data you want
+ * to pass here in reasonable chunks (about 1MB maximum)
+ *
+ * @param job the job that emitted this signal
+ * @param data buffer to fill with data to send to the
+ * slave. An empty buffer indicates end of data. (EOD)
+ */
+ void dataReq( TDEIO::Job *job, TQByteArray &data );
+
+ /**
+ * Signals a redirection.
+ * Use to update the URL shown to the user.
+ * The redirection itself is handled internally.
+ * @param job the job that emitted this signal
+ * @param url the new URL
+ */
+ void redirection( TDEIO::Job *job, const KURL &url );
+
+ /**
+ * Signals a permanent redirection.
+ * The redirection itself is handled internally.
+ * @param job the job that emitted this signal
+ * @param fromUrl the original URL
+ * @param toUrl the new URL
+ * @since 3.1
+ */
+ void permanentRedirection( TDEIO::Job *job, const KURL &fromUrl, const KURL &toUrl );
+
+ /**
+ * Mimetype determined.
+ * @param job the job that emitted this signal
+ * @param type the mime type
+ */
+ void mimetype( TDEIO::Job *job, const TQString &type );
+
+ /**
+ * @internal
+ * Emitted if the "put" job found an existing partial file
+ * (in which case offset is the size of that file)
+ * and emitted by the "get" job if it supports resuming to
+ * the given offset - in this case @p offset is unused)
+ */
+ void canResume( TDEIO::Job *job, TDEIO::filesize_t offset );
+
+
+ protected slots:
+ virtual void slotRedirection( const KURL &url);
+ virtual void slotFinished();
+ virtual void slotData( const TQByteArray &data);
+ virtual void slotDataReq();
+ virtual void slotMimetype( const TQString &mimetype );
+ virtual void slotNeedSubURLData();
+ virtual void slotSubURLData(TDEIO::Job*, const TQByteArray &);
+ virtual void slotMetaData( const TDEIO::MetaData &_metaData);
+ void slotErrorPage();
+ void slotCanResume( TDEIO::filesize_t offset );
+ void slotPostRedirection();
+
+ protected:
+ bool m_suspended;
+ bool m_errorPage;
+ TQByteArray staticData;
+ KURL m_redirectionURL;
+ KURL::List m_redirectionList;
+ TQString m_mimetype;
+ TransferJob *m_subJob;
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ private:
+ class TransferJobPrivate *d;
+ };
+
+ /**
+ * StoredTransferJob is a TransferJob (for downloading or uploading data) that
+ * also stores a TQByteArray with the data, making it simpler to use than the
+ * standard TransferJob.
+ *
+ * For TDEIO::storedGet it puts the data into the member TQByteArray, so the user
+ * of this class can get hold of the whole data at once by calling data()
+ * when the result signal is emitted.
+ * You should only use StoredTransferJob to download data if you cannot
+ * process the data by chunks while it's being downloaded, since storing
+ * everything in a TQByteArray can potentially require a lot of memory.
+ *
+ * For TDEIO::storedPut the user of this class simply provides the bytearray from
+ * the start, and the job takes care of uploading it.
+ * You should only use StoredTransferJob to upload data if you cannot
+ * provide the in chunks while it's being uploaded, since storing
+ * everything in a TQByteArray can potentially require a lot of memory.
+ *
+ * @since 3.3
+ */
+ class TDEIO_EXPORT StoredTransferJob : public TDEIO::TransferJob {
+ Q_OBJECT
+
+ public:
+ /**
+ * Do not create a StoredTransferJob. Use storedGet() or storedPut()
+ * instead.
+ * @param url the url to get or put
+ * @param command the command to issue
+ * @param packedArgs the arguments
+ * @param _staticData additional data to transmit (e.g. in a HTTP Post)
+ * @param showProgressInfo true to show progress information to the user
+ */
+ StoredTransferJob(const KURL& url, int command,
+ const TQByteArray &packedArgs,
+ const TQByteArray &_staticData,
+ bool showProgressInfo);
+
+ /**
+ * Set data to be uploaded. This is for put jobs.
+ * Automatically called by TDEIO::storedPut(const TQByteArray &, ...),
+ * do not call this yourself.
+ */
+ void setData( const TQByteArray& arr );
+
+ /**
+ * Get hold of the downloaded data. This is for get jobs.
+ * You're supposed to call this only from the slot connected to the result() signal.
+ */
+ TQByteArray data() const { return m_data; }
+
+ private slots:
+ void slotStoredData( TDEIO::Job *job, const TQByteArray &data );
+ void slotStoredDataReq( TDEIO::Job *job, TQByteArray &data );
+ private:
+ TQByteArray m_data;
+ int m_uploadOffset;
+ };
+
+ /**
+ * The MultiGetJob is a TransferJob that allows you to get
+ * several files from a single server. Don't create directly,
+ * but use TDEIO::multi_get() instead.
+ * @see TDEIO::multi_get()
+ */
+ class TDEIO_EXPORT MultiGetJob : public TransferJob {
+ Q_OBJECT
+
+ public:
+ /**
+ * Do not create a MultiGetJob directly, use TDEIO::multi_get()
+ * instead.
+ *
+ * @param url the first url to get
+ * @param showProgressInfo true to show progress information to the user
+ */
+ MultiGetJob(const KURL& url, bool showProgressInfo);
+
+ /**
+ * @internal
+ * Called by the scheduler when a @p slave gets to
+ * work on this job.
+ * @param slave the slave that starts working on this job
+ */
+ virtual void start(Slave *slave);
+
+ /**
+ * Get an additional file.
+ *
+ * @param id the id of the file
+ * @param url the url of the file to get
+ * @param metaData the meta data for this request
+ */
+ void get(long id, const KURL &url, const MetaData &metaData);
+
+ signals:
+ /**
+ * Data from the slave has arrived.
+ * @param id the id of the request
+ * @param data data received from the slave.
+ * End of data (EOD) has been reached if data.size() == 0
+ */
+ void data( long id, const TQByteArray &data);
+
+ /**
+ * Mimetype determined
+ * @param id the id of the request
+ * @param type the mime type
+ */
+ void mimetype( long id, const TQString &type );
+
+ /**
+ * File transfer completed.
+ *
+ * When all files have been processed, result(TDEIO::Job *) gets
+ * emitted.
+ * @param id the id of the request
+ */
+ void result( long id);
+
+ protected slots:
+ virtual void slotRedirection( const KURL &url);
+ virtual void slotFinished();
+ virtual void slotData( const TQByteArray &data);
+ virtual void slotMimetype( const TQString &mimetype );
+ private:
+ struct GetRequest {
+ public:
+ GetRequest(long _id, const KURL &_url, const MetaData &_metaData)
+ : id(_id), url(_url), metaData(_metaData) { }
+ long id;
+ KURL url;
+ MetaData metaData;
+ };
+ bool findCurrentEntry();
+ void flushQueue(TQPtrList<GetRequest> &queue);
+
+ TQPtrList<GetRequest> m_waitQueue;
+ TQPtrList<GetRequest> m_activeQueue;
+ bool b_multiGetActive;
+ GetRequest *m_currentEntry;
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ private:
+ class MultiGetJobPrivate* d;
+ };
+
+ /**
+ * A MimetypeJob is a TransferJob that allows you to get
+ * the mime type of an URL. Don't create directly,
+ * but use TDEIO::mimetype() instead.
+ * @see TDEIO::mimetype()
+ */
+ class TDEIO_EXPORT MimetypeJob : public TransferJob {
+ Q_OBJECT
+
+ public:
+ /**
+ * Do not create a MimetypeJob directly. Use TDEIO::mimetype()
+ * instead.
+ * @param url the url to get
+ * @param command the command to issue
+ * @param packedArgs the arguments
+ * @param showProgressInfo true to show progress information to the user
+ */
+ MimetypeJob(const KURL& url, int command, const TQByteArray &packedArgs, bool showProgressInfo);
+
+ /**
+ * Call this in the slot connected to result,
+ * and only after making sure no error happened.
+ * @return the mimetype of the URL
+ */
+ TQString mimetype() const { return m_mimetype; }
+
+ /**
+ * @internal
+ * Called by the scheduler when a slave gets to
+ * work on this job.
+ * @param slave the slave that works on the job
+ */
+ virtual void start( Slave *slave );
+
+ protected slots:
+ virtual void slotFinished( );
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ private:
+ class MimetypeJobPrivate* d;
+ };
+
+ /**
+ * The FileCopyJob copies data from one place to another.
+ * @see TDEIO::file_copy()
+ * @see TDEIO::file_move()
+ */
+ class TDEIO_EXPORT FileCopyJob : public Job {
+ Q_OBJECT
+
+ public:
+ /**
+ * Do not create a FileCopyJob directly. Use TDEIO::file_move()
+ * or TDEIO::file_copy() instead.
+ * @param src the source URL
+ * @param dest the destination URL
+ * @param permissions the permissions of the resulting resource
+ * @param move true to move, false to copy
+ * @param overwrite true to allow overwriting, false otherwise
+ * @param resume true to resume an operation, false otherwise
+ * @param showProgressInfo true to show progress information to the user
+ */
+ FileCopyJob( const KURL& src, const KURL& dest, int permissions,
+ bool move, bool overwrite, bool resume, bool showProgressInfo);
+
+ ~FileCopyJob();
+ /**
+ * If you know the size of the source file, call this method
+ * to inform this job. It will be displayed in the "resume" dialog.
+ * @param size the size of the source file
+ * @since 3.2
+ */
+ void setSourceSize64(TDEIO::filesize_t size);
+
+ /**
+ * Sets the modification time of the file
+ *
+ * Note that this is ignored if a direct copy (SlaveBase::copy) can be done,
+ * in which case the mtime of the source is applied to the destination (if the protocol
+ * supports the concept).
+ */
+ void setModificationTime( time_t mtime );
+
+ /**
+ * @deprecated
+ */
+ void setSourceSize( off_t size ) KDE_DEPRECATED;
+
+ /**
+ * Returns the source URL.
+ * @return the source URL
+ */
+ KURL srcURL() const { return m_src; }
+
+ /**
+ * Returns the destination URL.
+ * @return the destination URL
+ */
+ KURL destURL() const { return m_dest; }
+
+ signals:
+ /**
+ * Mimetype determined during a file copy.
+ * This is never emitted during a move, and might not be emitted during
+ * a copy, depending on the slave.
+ * @param job the job that emitted this signal
+ * @param type the mime type
+ *
+ * @since 3.5.7
+ */
+ void mimetype( TDEIO::Job *job, const TQString &type );
+
+ public slots:
+ void slotStart();
+ void slotData( TDEIO::Job *, const TQByteArray &data);
+ void slotDataReq( TDEIO::Job *, TQByteArray &data);
+ void slotMimetype( TDEIO::Job *, const TQString& type );
+
+ protected slots:
+ /**
+ * Called whenever a subjob finishes.
+ * @param job the job that emitted this signal
+ */
+ virtual void slotResult( TDEIO::Job *job );
+
+ /**
+ * Forward signal from subjob
+ * @param job the job that emitted this signal
+ * @param size the processed size in bytes
+ */
+ void slotProcessedSize( TDEIO::Job *job, TDEIO::filesize_t size );
+ /**
+ * Forward signal from subjob
+ * @param job the job that emitted this signal
+ * @param size the total size
+ */
+ void slotTotalSize( TDEIO::Job *job, TDEIO::filesize_t size );
+ /**
+ * Forward signal from subjob
+ * @param job the job that emitted this signal
+ * @param pct the percentage
+ */
+ void slotPercent( TDEIO::Job *job, unsigned long pct );
+ /**
+ * Forward signal from subjob
+ * @param job the job that emitted this signal
+ * @param offset the offset to resume from
+ */
+ void slotCanResume( TDEIO::Job *job, TDEIO::filesize_t offset );
+
+ protected:
+ void startCopyJob();
+ void startCopyJob(const KURL &slave_url);
+ void startRenameJob(const KURL &slave_url);
+ void startDataPump();
+ void connectSubjob( SimpleJob * job );
+
+ private:
+ void startBestCopyMethod();
+
+ protected:
+ KURL m_src;
+ KURL m_dest;
+ int m_permissions;
+ bool m_move:1;
+ bool m_overwrite:1;
+ bool m_resume:1;
+ bool m_canResume:1;
+ bool m_resumeAnswerSent:1;
+ TQByteArray m_buffer;
+ SimpleJob *m_moveJob;
+ SimpleJob *m_copyJob;
+ TransferJob *m_getJob;
+ TransferJob *m_putJob;
+ TDEIO::filesize_t m_totalSize;
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ private:
+ class FileCopyJobPrivate;
+ FileCopyJobPrivate* d;
+ };
+
+ /**
+ * A ListJob is allows you to get the get the content of a directory.
+ * Don't create the job directly, but use TDEIO::listRecursive() or
+ * TDEIO::listDir() instead.
+ * @see TDEIO::listRecursive()
+ * @see TDEIO::listDir()
+ */
+ class TDEIO_EXPORT ListJob : public SimpleJob {
+ Q_OBJECT
+
+ public:
+ /**
+ * Do not create a ListJob directly. Use TDEIO::listDir() or
+ * TDEIO::listRecursive() instead.
+ * @param url the url of the directory
+ * @param showProgressInfo true to show progress information to the user
+ * @param recursive true to get the data recursively from child directories,
+ * false to get only the content of the specified dir
+ * @param prefix the prefix of the files, or TQString::null for no prefix
+ * @param includeHidden true to include hidden files (those starting with '.')
+ */
+ ListJob(const KURL& url, bool showProgressInfo,
+ bool recursive = false, TQString prefix = TQString::null,
+ bool includeHidden = true);
+
+ /**
+ * @internal
+ * Called by the scheduler when a @p slave gets to
+ * work on this job.
+ * @param slave the slave that starts working on this job
+ */
+ virtual void start( Slave *slave );
+
+ /**
+ * Returns the ListJob's redirection URL. This will be invalid if there
+ * was no redirection.
+ * @return the redirection url
+ * @since 3.4.1
+ */
+ const KURL& redirectionURL() const { return m_redirectionURL; }
+
+ /**
+ * Do not apply any KIOSK restrictions to this job.
+ * @since 3.2
+ */
+ void setUnrestricted(bool unrestricted);
+
+ signals:
+ /**
+ * This signal emits the entry found by the job while listing.
+ * The progress signals aren't specific to ListJob. It simply
+ * uses SimpleJob's processedSize (number of entries listed) and
+ * totalSize (total number of entries, if known),
+ * as well as percent.
+ * @param job the job that emitted this signal
+ * @param list the list of UDSEntries
+ */
+ void entries( TDEIO::Job *job, const TDEIO::UDSEntryList& list);
+
+ /**
+ * Signals a redirection.
+ * Use to update the URL shown to the user.
+ * The redirection itself is handled internally.
+ * @param job the job that is redirected
+ * @param url the new url
+ */
+ void redirection( TDEIO::Job *job, const KURL &url );
+
+ /**
+ * Signals a permanent redirection.
+ * The redirection itself is handled internally.
+ * @param job the job that emitted this signal
+ * @param fromUrl the original URL
+ * @param toUrl the new URL
+ * @since 3.1
+ */
+ void permanentRedirection( TDEIO::Job *job, const KURL &fromUrl, const KURL &toUrl );
+
+ protected slots:
+ virtual void slotFinished( );
+ virtual void slotMetaData( const TDEIO::MetaData &_metaData);
+ virtual void slotResult( TDEIO::Job *job );
+ void slotListEntries( const TDEIO::UDSEntryList& list );
+ void slotRedirection( const KURL &url );
+ void gotEntries( TDEIO::Job * subjob, const TDEIO::UDSEntryList& list );
+
+ private:
+ bool recursive;
+ bool includeHidden;
+ TQString prefix;
+ unsigned long m_processedEntries;
+ KURL m_redirectionURL;
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ private:
+ class ListJobPrivate* d;
+ };
+
+ /// @internal
+ struct TDEIO_EXPORT CopyInfo
+ {
+ KURL uSource;
+ KURL uDest;
+ TQString linkDest; // for symlinks only
+ int permissions;
+ //mode_t type;
+ time_t ctime;
+ time_t mtime;
+ TDEIO::filesize_t size; // 0 for dirs
+ };
+
+ /**
+ * CopyJob is used to move, copy or symlink files and directories.
+ * Don't create the job directly, but use TDEIO::copy(),
+ * TDEIO::move(), TDEIO::link() and friends.
+ *
+ * @see TDEIO::copy()
+ * @see TDEIO::copyAs()
+ * @see TDEIO::move()
+ * @see TDEIO::moveAs()
+ * @see TDEIO::link()
+ * @see TDEIO::linkAs()
+ */
+ class TDEIO_EXPORT CopyJob : public Job {
+ Q_OBJECT
+
+ public:
+ /**
+ * Defines the mode of the operation
+ */
+ enum CopyMode{ Copy, Move, Link };
+
+ /**
+ * Do not create a CopyJob directly. Use TDEIO::copy(),
+ * TDEIO::move(), TDEIO::link() and friends instead.
+ *
+ * @param src the list of source URLs
+ * @param dest the destination URL
+ * @param mode specifies whether the job should copy, move or link
+ * @param asMethod if true, behaves like TDEIO::copyAs(),
+ * TDEIO::moveAs() or TDEIO::linkAs()
+ * @param showProgressInfo true to show progress information to the user
+ * @see TDEIO::copy()
+ * @see TDEIO::copyAs()
+ * @see TDEIO::move()
+ * @see TDEIO::moveAs()
+ * @see TDEIO::link()
+ * @see TDEIO::linkAs()
+ */
+ CopyJob( const KURL::List& src, const KURL& dest, CopyMode mode, bool asMethod, bool showProgressInfo );
+
+ virtual ~CopyJob();
+
+ /**
+ * Returns the list of source URLs.
+ * @return the list of source URLs.
+ */
+ KURL::List srcURLs() const { return m_srcList; }
+
+ /**
+ * Returns the destination URL.
+ * @return the destination URL
+ */
+ KURL destURL() const { return m_dest; }
+
+ /**
+ * By default the permissions of the copied files will be those of the source files.
+ *
+ * But when copying "template" files to "new" files, people prefer the umask
+ * to apply, rather than the template's permissions.
+ * For that case, call setDefaultPermissions(true)
+ *
+ * TODO KDE4: consider adding this as bool to copy/copyAs?
+ * @since 3.2.3
+ */
+ void setDefaultPermissions( bool b );
+
+ /**
+ * When an error happens while copying/moving a file, the user will be presented with
+ * a dialog for skipping the file that can't be copied/moved.
+ * Or if the error is that the destination file already exists, the standard
+ * rename dialog is shown.
+ * If the program doesn't want CopyJob to show dialogs, but to simply fail on error,
+ * call setInteractive( false ).
+ *
+ * KDE4: remove, already in Job
+ * @since 3.4
+ */
+ void setInteractive( bool b );
+
+ signals:
+
+ /**
+ * Emitted when the total number of files is known.
+ * @param job the job that emitted this signal
+ * @param files the total number of files
+ */
+ void totalFiles( TDEIO::Job *job, unsigned long files );
+ /**
+ * Emitted when the toal number of direcotries is known.
+ * @param job the job that emitted this signal
+ * @param dirs the total number of directories
+ */
+ void totalDirs( TDEIO::Job *job, unsigned long dirs );
+
+ /**
+ * Emitted when it is known which files / directories are going
+ * to be created. Note that this may still change e.g. when
+ * existing files with the same name are discovered.
+ * @param job the job that emitted this signal
+ * @param files a list of items that are about to be created.
+ */
+ void aboutToCreate( TDEIO::Job *job, const TQValueList<TDEIO::CopyInfo> &files);
+
+ /**
+ * Sends the number of processed files.
+ * @param job the job that emitted this signal
+ * @param files the number of processed files
+ */
+ void processedFiles( TDEIO::Job *job, unsigned long files );
+ /**
+ * Sends the number of processed directories.
+ * @param job the job that emitted this signal
+ * @param dirs the number of processed dirs
+ */
+ void processedDirs( TDEIO::Job *job, unsigned long dirs );
+
+ /**
+ * The job is copying a file or directory.
+ * @param job the job that emitted this signal
+ * @param from the URl of the file or directory that is currently
+ * being copied
+ * @param to the destination of the current operation
+ */
+ void copying( TDEIO::Job *job, const KURL& from, const KURL& to );
+ /**
+ * The job is creating a symbolic link.
+ * @param job the job that emitted this signal
+ * @param target the URl of the file or directory that is currently
+ * being linked
+ * @param to the destination of the current operation
+ */
+ void linking( TDEIO::Job *job, const TQString& target, const KURL& to );
+ /**
+ * The job is moving a file or directory.
+ * @param job the job that emitted this signal
+ * @param from the URl of the file or directory that is currently
+ * being moved
+ * @param to the destination of the current operation
+ */
+ void moving( TDEIO::Job *job, const KURL& from, const KURL& to );
+ /**
+ * The job is creating the directory @p dir.
+ * @param job the job that emitted this signal
+ * @param dir the directory that is currently being created
+ */
+ void creatingDir( TDEIO::Job *job, const KURL& dir );
+ /**
+ * The user chose to rename @p from to @p to.
+ * @param job the job that emitted this signal
+ * @param from the original name
+ * @param to the new name
+ */
+ void renamed( TDEIO::Job *job, const KURL& from, const KURL& to );
+
+ /**
+ * The job emits this signal when copying or moving a file or directory successfully finished.
+ * This signal is mainly for the Undo feature.
+ *
+ * @param job the job that emitted this signal
+ * @param from the source URL
+ * @param to the destination URL
+ * @param directory indicates whether a file or directory was successfully copied/moved.
+ * true for a directoy, false for file
+ * @param renamed indicates that the destination URL was created using a
+ * rename operation (i.e. fast directory moving). true if is has been renamed
+ */
+ void copyingDone( TDEIO::Job *job, const KURL &from, const KURL &to, bool directory, bool renamed );
+ /**
+ * The job is copying or moving a symbolic link, that points to target.
+ * The new link is created in @p to. The existing one is/was in @p from.
+ * This signal is mainly for the Undo feature.
+ * @param job the job that emitted this signal
+ * @param from the source URL
+ * @param target the target
+ * @param to the destination URL
+ */
+ void copyingLinkDone( TDEIO::Job *job, const KURL &from, const TQString& target, const KURL& to );
+
+ protected:
+ void statCurrentSrc();
+ void statNextSrc();
+
+ // Those aren't slots but submethods for slotResult.
+ void slotResultStating( TDEIO::Job * job );
+ void startListing( const KURL & src );
+ void slotResultCreatingDirs( TDEIO::Job * job );
+ void slotResultConflictCreatingDirs( TDEIO::Job * job );
+ void createNextDir();
+ void slotResultCopyingFiles( TDEIO::Job * job );
+ void slotResultConflictCopyingFiles( TDEIO::Job * job );
+ void copyNextFile();
+ void slotResultDeletingDirs( TDEIO::Job * job );
+ void deleteNextDir();
+ void skip( const KURL & sourceURL );
+ void slotResultRenaming( TDEIO::Job * job );
+ //void slotResultSettingDirAttributes( TDEIO::Job * job );
+ void setNextDirAttribute();
+ private:
+ void startRenameJob(const KURL &slave_url);
+ bool shouldOverwrite( const TQString& path ) const;
+ bool shouldSkip( const TQString& path ) const;
+ void skipSrc();
+
+ protected slots:
+ void slotStart();
+ void slotEntries( TDEIO::Job*, const TDEIO::UDSEntryList& list );
+ virtual void slotResult( TDEIO::Job *job );
+ /**
+ * Forward signal from subjob
+ */
+ void slotProcessedSize( TDEIO::Job*, TDEIO::filesize_t data_size );
+ /**
+ * Forward signal from subjob
+ * @param size the total size
+ */
+ void slotTotalSize( TDEIO::Job*, TDEIO::filesize_t size );
+
+ void slotReport();
+ private:
+ CopyMode m_mode;
+ bool m_asMethod;
+ enum DestinationState { DEST_NOT_STATED, DEST_IS_DIR, DEST_IS_FILE, DEST_DOESNT_EXIST };
+ DestinationState destinationState;
+ enum { STATE_STATING, STATE_RENAMING, STATE_LISTING, STATE_CREATING_DIRS,
+ STATE_CONFLICT_CREATING_DIRS, STATE_COPYING_FILES, STATE_CONFLICT_COPYING_FILES,
+ STATE_DELETING_DIRS, STATE_SETTING_DIR_ATTRIBUTES } state;
+ TDEIO::filesize_t m_totalSize;
+ TDEIO::filesize_t m_processedSize;
+ TDEIO::filesize_t m_fileProcessedSize;
+ int m_processedFiles;
+ int m_processedDirs;
+ TQValueList<CopyInfo> files;
+ TQValueList<CopyInfo> dirs;
+ KURL::List dirsToRemove;
+ KURL::List m_srcList;
+ KURL::List::Iterator m_currentStatSrc;
+ bool m_bCurrentSrcIsDir;
+ bool m_bCurrentOperationIsLink;
+ bool m_bSingleFileCopy;
+ bool m_bOnlyRenames;
+ KURL m_dest;
+ KURL m_currentDest;
+ //
+ TQStringList m_skipList;
+ TQStringList m_overwriteList;
+ bool m_bAutoSkip;
+ bool m_bOverwriteAll;
+ int m_conflictError;
+
+ TQTimer *m_reportTimer;
+ //these both are used for progress dialog reporting
+ KURL m_currentSrcURL;
+ KURL m_currentDestURL;
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ private:
+ class CopyJobPrivate;
+ CopyJobPrivate* d;
+ friend class CopyJobPrivate; // for DestinationState
+ };
+
+ /**
+ * A more complex Job to delete files and directories.
+ * Don't create the job directly, but use TDEIO::del() instead.
+ *
+ * @see TDEIO::del()
+ */
+ class TDEIO_EXPORT DeleteJob : public Job {
+ Q_OBJECT
+
+ public:
+ /**
+ * Do not create a DeleteJob directly. Use TDEIO::del()
+ * instead.
+ *
+ * @param src the list of URLs to delete
+ * @param shred true to shred (make sure that data is not recoverable)a
+ * @param showProgressInfo true to show progress information to the user
+ * @see TDEIO::del()
+ */
+ DeleteJob( const KURL::List& src, bool shred, bool showProgressInfo );
+
+ /**
+ * Returns the list of URLs.
+ * @return the list of URLs.
+ */
+ KURL::List urls() const { return m_srcList; }
+
+ signals:
+
+ /**
+ * Emitted when the total number of files is known.
+ * @param job the job that emitted this signal
+ * @param files the total number of files
+ */
+ void totalFiles( TDEIO::Job *job, unsigned long files );
+ /**
+ * Emitted when the toal number of direcotries is known.
+ * @param job the job that emitted this signal
+ * @param dirs the total number of directories
+ */
+ void totalDirs( TDEIO::Job *job, unsigned long dirs );
+
+ /**
+ * Sends the number of processed files.
+ * @param job the job that emitted this signal
+ * @param files the number of processed files
+ */
+ void processedFiles( TDEIO::Job *job, unsigned long files );
+ /**
+ * Sends the number of processed directories.
+ * @param job the job that emitted this signal
+ * @param dirs the number of processed dirs
+ */
+ void processedDirs( TDEIO::Job *job, unsigned long dirs );
+
+ /**
+ * Sends the URL of the file that is currently being deleted.
+ * @param job the job that emitted this signal
+ * @param file the URL of the file or directory that is being
+ * deleted
+ */
+ void deleting( TDEIO::Job *job, const KURL& file );
+
+ protected slots:
+ void slotStart();
+ void slotEntries( TDEIO::Job*, const TDEIO::UDSEntryList& list );
+ virtual void slotResult( TDEIO::Job *job );
+
+ /**
+ * Forward signal from subjob
+ */
+ void slotProcessedSize( TDEIO::Job*, TDEIO::filesize_t data_size );
+ void slotReport();
+
+ private:
+ void statNextSrc();
+ void deleteNextFile();
+ void deleteNextDir();
+
+ private:
+ enum { STATE_STATING, STATE_LISTING,
+ STATE_DELETING_FILES, STATE_DELETING_DIRS } state;
+ TDEIO::filesize_t m_totalSize;
+ TDEIO::filesize_t m_processedSize;
+ TDEIO::filesize_t m_fileProcessedSize;
+ int m_processedFiles;
+ int m_processedDirs;
+ int m_totalFilesDirs;
+ KURL m_currentURL;
+ KURL::List files;
+ KURL::List symlinks;
+ KURL::List dirs;
+ KURL::List m_srcList;
+ KURL::List::Iterator m_currentStat;
+ TQStringList m_parentDirs;
+ bool m_shred; // BIC: remove in KDE4
+ TQTimer *m_reportTimer;
+ protected:
+ /** \internal */
+ virtual void virtual_hook( int id, void* data );
+ private:
+ class DeleteJobPrivate* d;
+ };
+
+ /**
+ * A KIO job that finds a local URL
+ * @see TDEIO::localURL()
+ * @since R14.0.0
+ */
+ class TDEIO_EXPORT LocalURLJob : public SimpleJob {
+
+ Q_OBJECT
+
+ public:
+ /**
+ * Do not use this constructor to create a LocalURLJob, use TDEIO::localURL() instead.
+ * @param url the url of the file or directory to check
+ * @param command the command to issue
+ * @param packedArgs the arguments
+ * @param showProgressInfo true to show progress information to the user
+ */
+ LocalURLJob(const KURL& url, int command, const TQByteArray &packedArgs, bool showProgressInfo);
+
+ /**
+ * @internal
+ * Called by the scheduler when a @p slave gets to
+ * work on this job.
+ * @param slave the slave that starts working on this job
+ */
+ virtual void start( Slave *slave );
+
+ signals:
+ /**
+ * @param job the job that emitted this signal
+ * @param url the local url
+ * @param isLocal true if the returned URL is local, false if not
+ */
+ void localURL( TDEIO::Job *job, const KURL &url, bool isLocal );
+
+ protected slots:
+ void slotLocalURL( const KURL &url, bool isLocal );
+ virtual void slotFinished();
+
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ private:
+ class LocalURLJobPrivate;
+ LocalURLJobPrivate *d;
+ };
+
+}
+
+#endif
diff --git a/tdeio/tdeio/kacl.cpp b/tdeio/tdeio/kacl.cpp
new file mode 100644
index 000000000..432a50d31
--- /dev/null
+++ b/tdeio/tdeio/kacl.cpp
@@ -0,0 +1,682 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Till Adam <adam@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+// $Id: kacl.cpp 424977 2005-06-13 15:13:22Z tilladam $
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/stat.h>
+#ifdef USE_POSIX_ACL
+#ifdef HAVE_NON_POSIX_ACL_EXTENSIONS
+#include <acl/libacl.h>
+#else
+#include <posixacladdons.h>
+#endif
+#endif
+#include <tqintdict.h>
+
+#include <kdebug.h>
+
+#include "kacl.h"
+
+
+#ifdef USE_POSIX_ACL
+static void printACL( acl_t acl, const TQString &comment );
+static TQString aclAsString(const acl_t acl);
+#endif
+
+class KACL::KACLPrivate {
+public:
+ KACLPrivate() : m_acl( 0 ) { init(); }
+#ifdef USE_POSIX_ACL
+ KACLPrivate( acl_t acl )
+ : m_acl( acl ) { init(); }
+ ~KACLPrivate() { if ( m_acl ) acl_free( m_acl ); }
+#endif
+ void init() {
+ m_usercache.setAutoDelete( true );
+ m_groupcache.setAutoDelete( true );
+ }
+ // helpers
+#ifdef USE_POSIX_ACL
+ bool setMaskPermissions( unsigned short v );
+ TQString getUserName( uid_t uid ) const;
+ TQString getGroupName( gid_t gid ) const;
+ bool setAllUsersOrGroups( const TQValueList< QPair<TQString, unsigned short> > &list, acl_tag_t type );
+ bool setNamedUserOrGroupPermissions( const TQString& name, unsigned short permissions, acl_tag_t type );
+
+ acl_t m_acl;
+#else
+ int m_acl;
+#endif
+ mutable TQIntDict<TQString> m_usercache;
+ mutable TQIntDict<TQString> m_groupcache;
+};
+
+KACL::KACL( const TQString &aclString )
+ : d( new KACLPrivate )
+{
+ setACL( aclString );
+}
+
+KACL::KACL( mode_t basePermissions )
+#ifdef USE_POSIX_ACL
+ : d( new KACLPrivate( acl_from_mode( basePermissions ) ) )
+#else
+ : d( new KACLPrivate )
+#endif
+{
+#ifndef USE_POSIX_ACL
+ Q_UNUSED( basePermissions );
+#endif
+}
+
+KACL::KACL()
+ : d( new KACLPrivate )
+{
+}
+
+KACL::KACL( const KACL& rhs )
+ : d( new KACLPrivate )
+{
+ setACL( rhs.asString() );
+}
+
+KACL::~KACL()
+{
+ delete d;
+}
+
+bool KACL::operator==( const KACL& rhs ) const {
+#ifdef USE_POSIX_ACL
+ return ( acl_cmp( d->m_acl, rhs.d->m_acl ) == 0 );
+#else
+ Q_UNUSED( rhs );
+ return true;
+#endif
+}
+
+bool KACL::isValid() const
+{
+ bool valid = false;
+#ifdef USE_POSIX_ACL
+ if ( d->m_acl ) {
+ valid = ( acl_valid( d->m_acl ) == 0 );
+ }
+#endif
+ return valid;
+}
+
+bool KACL::isExtended() const
+{
+#ifdef USE_POSIX_ACL
+ return ( acl_equiv_mode( d->m_acl, NULL ) != 0 );
+#else
+ return false;
+#endif
+}
+
+#ifdef USE_POSIX_ACL
+static acl_entry_t entryForTag( acl_t acl, acl_tag_t tag )
+{
+ acl_entry_t entry;
+ int ret = acl_get_entry( acl, ACL_FIRST_ENTRY, &entry );
+ while ( ret == 1 ) {
+ acl_tag_t currentTag;
+ acl_get_tag_type( entry, &currentTag );
+ if ( currentTag == tag )
+ return entry;
+ ret = acl_get_entry( acl, ACL_NEXT_ENTRY, &entry );
+ }
+ return 0;
+}
+
+static unsigned short entryToPermissions( acl_entry_t entry )
+{
+ if ( entry == 0 ) return 0;
+ acl_permset_t permset;
+ if ( acl_get_permset( entry, &permset ) != 0 ) return 0;
+ return( acl_get_perm( permset, ACL_READ ) << 2 |
+ acl_get_perm( permset, ACL_WRITE ) << 1 |
+ acl_get_perm( permset, ACL_EXECUTE ) );
+}
+
+static void permissionsToEntry( acl_entry_t entry, unsigned short v )
+{
+ if ( entry == 0 ) return;
+ acl_permset_t permset;
+ if ( acl_get_permset( entry, &permset ) != 0 ) return;
+ acl_clear_perms( permset );
+ if ( v & 4 ) acl_add_perm( permset, ACL_READ );
+ if ( v & 2 ) acl_add_perm( permset, ACL_WRITE );
+ if ( v & 1 ) acl_add_perm( permset, ACL_EXECUTE );
+}
+
+static void printACL( acl_t acl, const TQString &comment )
+{
+ kdDebug() << comment << aclAsString( acl ) << endl;
+}
+
+static int getUidForName( const TQString& name )
+{
+ struct passwd *user = getpwnam( name.latin1() );
+ if ( user )
+ return user->pw_uid;
+ else
+ return -1;
+}
+
+static int getGidForName( const TQString& name )
+{
+ struct group *group = getgrnam( name.latin1() );
+ if ( group )
+ return group->gr_gid;
+ else
+ return -1;
+}
+#endif
+// ------------------ begin API implementation ------------
+
+unsigned short KACL::ownerPermissions() const
+{
+#ifdef USE_POSIX_ACL
+ return entryToPermissions( entryForTag( d->m_acl, ACL_USER_OBJ ) );
+#else
+ return 0;
+#endif
+}
+
+bool KACL::setOwnerPermissions( unsigned short v )
+{
+#ifdef USE_POSIX_ACL
+ permissionsToEntry( entryForTag( d->m_acl, ACL_USER_OBJ ), v );
+#else
+ Q_UNUSED( v );
+#endif
+ return true;
+}
+
+unsigned short KACL::owningGroupPermissions() const
+{
+#ifdef USE_POSIX_ACL
+ return entryToPermissions( entryForTag( d->m_acl, ACL_GROUP_OBJ ) );
+#else
+ return 0;
+#endif
+}
+
+bool KACL::setOwningGroupPermissions( unsigned short v )
+{
+#ifdef USE_POSIX_ACL
+ permissionsToEntry( entryForTag( d->m_acl, ACL_GROUP_OBJ ), v );
+#else
+ Q_UNUSED( v );
+#endif
+ return true;
+}
+
+unsigned short KACL::othersPermissions() const
+{
+#ifdef USE_POSIX_ACL
+ return entryToPermissions( entryForTag( d->m_acl, ACL_OTHER ) );
+#else
+ return 0;
+#endif
+}
+
+bool KACL::setOthersPermissions( unsigned short v )
+{
+#ifdef USE_POSIX_ACL
+ permissionsToEntry( entryForTag( d->m_acl, ACL_OTHER ), v );
+#else
+ Q_UNUSED( v );
+#endif
+ return true;
+}
+
+mode_t KACL::basePermissions() const
+{
+ mode_t perms( 0 );
+#ifdef USE_POSIX_ACL
+ if ( ownerPermissions() & ACL_READ ) perms |= S_IRUSR;
+ if ( ownerPermissions() & ACL_WRITE ) perms |= S_IWUSR;
+ if ( ownerPermissions() & ACL_EXECUTE ) perms |= S_IXUSR;
+ if ( owningGroupPermissions() & ACL_READ ) perms |= S_IRGRP;
+ if ( owningGroupPermissions() & ACL_WRITE ) perms |= S_IWGRP;
+ if ( owningGroupPermissions() & ACL_EXECUTE ) perms |= S_IXGRP;
+ if ( othersPermissions() & ACL_READ ) perms |= S_IROTH;
+ if ( othersPermissions() & ACL_WRITE ) perms |= S_IWOTH;
+ if ( othersPermissions() & ACL_EXECUTE ) perms |= S_IXOTH;
+#endif
+ return perms;
+}
+
+unsigned short KACL::maskPermissions( bool &exists ) const
+{
+ exists = true;
+#ifdef USE_POSIX_ACL
+ acl_entry_t entry = entryForTag( d->m_acl, ACL_MASK );
+ if ( entry == 0 ) {
+ exists = false;
+ return 0;
+ }
+ return entryToPermissions( entry );
+#else
+ return 0;
+#endif
+}
+
+#ifdef USE_POSIX_ACL
+bool KACL::KACLPrivate::setMaskPermissions( unsigned short v )
+{
+ acl_entry_t entry = entryForTag( m_acl, ACL_MASK );
+ if ( entry == 0 ) {
+ acl_create_entry( &m_acl, &entry );
+ acl_set_tag_type( entry, ACL_MASK );
+ }
+ permissionsToEntry( entry, v );
+ return true;
+}
+#endif
+
+bool KACL::setMaskPermissions( unsigned short v )
+{
+#ifdef USE_POSIX_ACL
+ return d->setMaskPermissions( v );
+#else
+ Q_UNUSED( v );
+ return true;
+#endif
+}
+
+/**************************
+ * Deal with named users *
+ **************************/
+unsigned short KACL::namedUserPermissions( const TQString& name, bool *exists ) const
+{
+#ifdef USE_POSIX_ACL
+ acl_entry_t entry;
+ uid_t id;
+ *exists = false;
+ int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry );
+ while ( ret == 1 ) {
+ acl_tag_t currentTag;
+ acl_get_tag_type( entry, &currentTag );
+ if ( currentTag == ACL_USER ) {
+ id = *( (uid_t*) acl_get_qualifier( entry ) );
+ if ( d->getUserName( id ) == name ) {
+ *exists = true;
+ return entryToPermissions( entry );
+ }
+ }
+ ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry );
+ }
+#else
+ Q_UNUSED( name );
+ Q_UNUSED( exists );
+#endif
+ return 0;
+}
+
+#ifdef USE_POSIX_ACL
+bool KACL::KACLPrivate::setNamedUserOrGroupPermissions( const TQString& name, unsigned short permissions, acl_tag_t type )
+{
+ bool allIsWell = true;
+ acl_t newACL = acl_dup( m_acl );
+ acl_entry_t entry;
+ bool createdNewEntry = false;
+ bool found = false;
+ int ret = acl_get_entry( newACL, ACL_FIRST_ENTRY, &entry );
+ while ( ret == 1 ) {
+ acl_tag_t currentTag;
+ acl_get_tag_type( entry, &currentTag );
+ if ( currentTag == type ) {
+ int id = * (int*)acl_get_qualifier( entry );
+ const TQString entryName = type == ACL_USER? getUserName( id ): getGroupName( id );
+ if ( entryName == name ) {
+ // found him, update
+ permissionsToEntry( entry, permissions );
+ found = true;
+ break;
+ }
+ }
+ ret = acl_get_entry( newACL, ACL_NEXT_ENTRY, &entry );
+ }
+ if ( !found ) {
+ acl_create_entry( &newACL, &entry );
+ acl_set_tag_type( entry, type );
+ int id = type == ACL_USER? getUidForName( name ): getGidForName( name );
+ if ( id == -1 || acl_set_qualifier( entry, &id ) != 0 ) {
+ acl_delete_entry( newACL, entry );
+ allIsWell = false;
+ } else {
+ permissionsToEntry( entry, permissions );
+ createdNewEntry = true;
+ }
+ }
+ if ( allIsWell && createdNewEntry ) {
+ // 23.1.1 of 1003.1e states that as soon as there is a named user or
+ // named group entry, there needs to be a mask entry as well, so add
+ // one, if the user hasn't explicitely set one.
+ if ( entryForTag( newACL, ACL_MASK ) == 0 ) {
+ acl_calc_mask( &newACL );
+ }
+ }
+
+ if ( !allIsWell || acl_valid( newACL ) != 0 ) {
+ acl_free( newACL );
+ allIsWell = false;
+ } else {
+ acl_free( m_acl );
+ m_acl = newACL;
+ }
+ return allIsWell;
+}
+#endif
+
+bool KACL::setNamedUserPermissions( const TQString& name, unsigned short permissions )
+{
+#ifdef USE_POSIX_ACL
+ return d->setNamedUserOrGroupPermissions( name, permissions, ACL_USER );
+#else
+ Q_UNUSED( name );
+ Q_UNUSED( permissions );
+ return true;
+#endif
+}
+
+ACLUserPermissionsList KACL::allUserPermissions() const
+{
+ ACLUserPermissionsList list;
+#ifdef USE_POSIX_ACL
+ acl_entry_t entry;
+ uid_t id;
+ int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry );
+ while ( ret == 1 ) {
+ acl_tag_t currentTag;
+ acl_get_tag_type( entry, &currentTag );
+ if ( currentTag == ACL_USER ) {
+ id = *( (uid_t*) acl_get_qualifier( entry ) );
+ TQString name = d->getUserName( id );
+ unsigned short permissions = entryToPermissions( entry );
+ ACLUserPermissions pair = qMakePair( name, permissions );
+ list.append( pair );
+ }
+ ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry );
+ }
+#endif
+ return list;
+}
+
+#ifdef USE_POSIX_ACL
+bool KACL::KACLPrivate::setAllUsersOrGroups( const TQValueList< QPair<TQString, unsigned short> > &list, acl_tag_t type )
+{
+ bool allIsWell = true;
+ bool atLeastOneUserOrGroup = false;
+
+ // make working copy, in case something goes wrong
+ acl_t newACL = acl_dup( m_acl );
+ acl_entry_t entry;
+
+//printACL( newACL, "Before cleaning: " );
+ // clear user entries
+ int ret = acl_get_entry( newACL, ACL_FIRST_ENTRY, &entry );
+ while ( ret == 1 ) {
+ acl_tag_t currentTag;
+ acl_get_tag_type( entry, &currentTag );
+ if ( currentTag == type ) {
+ acl_delete_entry( newACL, entry );
+ // we have to start from the beginning, the iterator is
+ // invalidated, on deletion
+ ret = acl_get_entry( newACL, ACL_FIRST_ENTRY, &entry );
+ } else {
+ ret = acl_get_entry( newACL, ACL_NEXT_ENTRY, &entry );
+ }
+ }
+//printACL( newACL, "After cleaning out entries: " );
+
+ // now add the entries from the list
+ TQValueList< QPair<TQString, unsigned short> >::const_iterator it = list.constBegin();
+ while ( it != list.constEnd() ) {
+ acl_create_entry( &newACL, &entry );
+ acl_set_tag_type( entry, type );
+ int id = type == ACL_USER? getUidForName( (*it).first):getGidForName( (*it).first );
+ if ( id == -1 || acl_set_qualifier( entry, &id ) != 0 ) {
+ // user or group doesn't exist => error
+ acl_delete_entry( newACL, entry );
+ allIsWell = false;
+ break;
+ } else {
+ permissionsToEntry( entry, (*it).second );
+ atLeastOneUserOrGroup = true;
+ }
+ ++it;
+ }
+//printACL( newACL, "After adding entries: " );
+ if ( allIsWell && atLeastOneUserOrGroup ) {
+ // 23.1.1 of 1003.1e states that as soon as there is a named user or
+ // named group entry, there needs to be a mask entry as well, so add
+ // one, if the user hasn't explicitely set one.
+ if ( entryForTag( newACL, ACL_MASK ) == 0 ) {
+ acl_calc_mask( &newACL );
+ }
+ }
+ if ( allIsWell && ( acl_valid( newACL ) == 0 ) ) {
+ acl_free( m_acl );
+ m_acl = newACL;
+ } else {
+ acl_free( newACL );
+ }
+ return allIsWell;
+}
+#endif
+
+bool KACL::setAllUserPermissions( const ACLUserPermissionsList &users )
+{
+#ifdef USE_POSIX_ACL
+ return d->setAllUsersOrGroups( users, ACL_USER );
+#else
+ Q_UNUSED( users );
+ return true;
+#endif
+}
+
+
+/**************************
+ * Deal with named groups *
+ **************************/
+
+unsigned short KACL::namedGroupPermissions( const TQString& name, bool *exists ) const
+{
+ *exists = false;
+#ifdef USE_POSIX_ACL
+ acl_entry_t entry;
+ gid_t id;
+ int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry );
+ while ( ret == 1 ) {
+ acl_tag_t currentTag;
+ acl_get_tag_type( entry, &currentTag );
+ if ( currentTag == ACL_GROUP ) {
+ id = *( (gid_t*) acl_get_qualifier( entry ) );
+ if ( d->getGroupName( id ) == name ) {
+ *exists = true;
+ return entryToPermissions( entry );
+ }
+ }
+ ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry );
+ }
+#else
+ Q_UNUSED( name );
+#endif
+ return 0;
+}
+
+bool KACL::setNamedGroupPermissions( const TQString& name, unsigned short permissions )
+{
+#ifdef USE_POSIX_ACL
+ return d->setNamedUserOrGroupPermissions( name, permissions, ACL_GROUP );
+#else
+ Q_UNUSED( name );
+ Q_UNUSED( permissions );
+ return true;
+#endif
+}
+
+
+ACLGroupPermissionsList KACL::allGroupPermissions() const
+{
+ ACLGroupPermissionsList list;
+#ifdef USE_POSIX_ACL
+ acl_entry_t entry;
+ gid_t id;
+ int ret = acl_get_entry( d->m_acl, ACL_FIRST_ENTRY, &entry );
+ while ( ret == 1 ) {
+ acl_tag_t currentTag;
+ acl_get_tag_type( entry, &currentTag );
+ if ( currentTag == ACL_GROUP ) {
+ id = *( (gid_t*) acl_get_qualifier( entry ) );
+ TQString name = d->getGroupName( id );
+ unsigned short permissions = entryToPermissions( entry );
+ ACLGroupPermissions pair = qMakePair( name, permissions );
+ list.append( pair );
+ }
+ ret = acl_get_entry( d->m_acl, ACL_NEXT_ENTRY, &entry );
+ }
+#endif
+ return list;
+}
+
+bool KACL::setAllGroupPermissions( const ACLGroupPermissionsList &groups )
+{
+#ifdef USE_POSIX_ACL
+ return d->setAllUsersOrGroups( groups, ACL_GROUP );
+#else
+ Q_UNUSED( groups );
+ return true;
+#endif
+}
+
+/**************************
+ * from and to string *
+ **************************/
+
+bool KACL::setACL( const TQString &aclStr )
+{
+ bool ret = false;
+#ifdef USE_POSIX_ACL
+ if ( aclStr.isEmpty() )
+ return false;
+
+ acl_t temp = acl_from_text( aclStr.latin1() );
+ if ( acl_valid( temp ) != 0 ) {
+ // TODO errno is set, what to do with it here?
+ acl_free( temp );
+ } else {
+ if ( d->m_acl )
+ acl_free( d->m_acl );
+ d->m_acl = temp;
+ ret = true;
+ }
+#else
+ Q_UNUSED( aclStr );
+#endif
+ return ret;
+}
+
+TQString KACL::asString() const
+{
+#ifdef USE_POSIX_ACL
+ return aclAsString( d->m_acl );
+#else
+ return TQString::null;
+#endif
+}
+
+
+// helpers
+
+#ifdef USE_POSIX_ACL
+TQString KACL::KACLPrivate::getUserName( uid_t uid ) const
+{
+ TQString *temp;
+ temp = m_usercache.find( uid );
+ if ( !temp ) {
+ struct passwd *user = getpwuid( uid );
+ if ( user ) {
+ m_usercache.insert( uid, new TQString(TQString::fromLatin1(user->pw_name)) );
+ return TQString::fromLatin1( user->pw_name );
+ }
+ else
+ return TQString::number( uid );
+ }
+ else
+ return *temp;
+}
+
+
+TQString KACL::KACLPrivate::getGroupName( gid_t gid ) const
+{
+ TQString *temp;
+ temp = m_groupcache.find( gid );
+ if ( !temp ) {
+ struct group *grp = getgrgid( gid );
+ if ( grp ) {
+ m_groupcache.insert( gid, new TQString(TQString::fromLatin1(grp->gr_name)) );
+ return TQString::fromLatin1( grp->gr_name );
+ }
+ else
+ return TQString::number( gid );
+ }
+ else
+ return *temp;
+}
+
+static TQString aclAsString(const acl_t acl)
+{
+ char *aclString = acl_to_text( acl, 0 );
+ TQString ret = TQString::fromLatin1( aclString );
+ acl_free( (void*)aclString );
+ return ret;
+}
+
+
+#endif
+
+void KACL::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+TQDataStream & operator<< ( TQDataStream & s, const KACL & a )
+{
+ s << a.asString();
+ return s;
+}
+
+TQDataStream & operator>> ( TQDataStream & s, KACL & a )
+{
+ TQString str;
+ s >> str;
+ a.setACL( str );
+ return s;
+}
+
+// vim:set ts=8 sw=4:
diff --git a/tdeio/tdeio/kacl.h b/tdeio/tdeio/kacl.h
new file mode 100644
index 000000000..f581f7a8e
--- /dev/null
+++ b/tdeio/tdeio/kacl.h
@@ -0,0 +1,207 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Till Adam <adam@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kacl_h__
+#define __kacl_h__
+
+#include <sys/types.h>
+#include <tdeio/global.h>
+
+typedef QPair<TQString, unsigned short> ACLUserPermissions;
+typedef TQValueList<ACLUserPermissions> ACLUserPermissionsList;
+typedef TQValueListIterator<ACLUserPermissions> ACLUserPermissionsIterator;
+typedef TQValueListConstIterator<ACLUserPermissions> ACLUserPermissionsConstIterator;
+
+typedef QPair<TQString, unsigned short> ACLGroupPermissions;
+typedef TQValueList<ACLGroupPermissions> ACLGroupPermissionsList;
+typedef TQValueListIterator<ACLGroupPermissions> ACLGroupPermissionsIterator;
+typedef TQValueListConstIterator<ACLGroupPermissions> ACLGroupPermissionsConstIterator;
+
+/**
+ * The KCAL class encapsulates a POSIX Access Control List. It follows the
+ * little standard that couldn't, 1003.1e/1003.2c, which died in draft status.
+ * @short a POSIX ACL encapsulation
+ * @author Till Adam <adam@kde.org>
+ */
+class TDEIO_EXPORT KACL
+{
+public:
+ /**
+ * Creates a new KACL from @p aclString. If the string is a valid acl
+ * string, isValid() will afterwards return true.
+ */
+ KACL( const TQString & aclString );
+
+ /** Copy ctor */
+ KACL( const KACL& rhs );
+
+ /**
+ * Creates a new KACL from the basic permissions passed in @p basicPermissions.
+ * isValid() will return true, afterwards.
+ */
+ KACL( mode_t basicPermissions );
+
+ /**
+ * Creates an empty KACL. Until a valid acl string is set via setACL,
+ * isValid() will return false.
+ */
+ KACL();
+
+ virtual ~KACL();
+
+ KACL& operator=( const KACL& rhs ) {
+ if ( this != &rhs )
+ setACL( rhs.asString() );
+ return *this;
+ }
+
+ bool operator==( const KACL& rhs ) const;
+
+ bool operator!=( const KACL& rhs ) const {
+ return !operator==( rhs );
+ }
+
+ /**
+ * Returns whether the KACL object represents a valid acl.
+ * @return whether the KACL object represents a valid acl.
+ */
+ bool isValid() const;
+
+ /** The standard (non-extended) part of an ACL. These map directly to
+ * standard unix file permissions. Setting them will never make a valid
+ * ACL invalid. */
+
+ /** @return the owner's premissions entry */
+ unsigned short ownerPermissions() const;
+
+ /** Set the owner's permissions entry.
+ * @return success or failure */
+ bool setOwnerPermissions( unsigned short );
+
+ /** @return the owning group's premissions entry */
+ unsigned short owningGroupPermissions() const;
+
+ /** Set the owning group's permissions entry.
+ * @return success or failure */
+ bool setOwningGroupPermissions( unsigned short );
+
+ /** @return the premissions entry for others */
+ unsigned short othersPermissions() const;
+
+ /** Set the permissions entry for others.
+ * @return success or failure */
+ bool setOthersPermissions( unsigned short );
+
+ /** @return the basic (owner/group/others) part of the ACL as a mode_t */
+ mode_t basePermissions() const;
+
+ /** The interface to the extended ACL. This is a mask, permissions for
+ * n named users and permissions for m named groups. */
+
+ /**
+ * Return whether the ACL contains extended entries or can be expressed
+ * using only basic file permissions.
+ * @return whether the ACL contains extended entries */
+ bool isExtended() const;
+
+ /**
+ * Return the entry for the permissions mask if there is one and sets
+ * @p exists to true. If there is no such entry, @p exists is set to false.
+ * @return the permissions mask entry */
+ unsigned short maskPermissions( bool &exists ) const;
+
+ /** Set the permissions mask for the ACL. Permissions set for individual
+ * entries will be masked with this, such that their effective permissions
+ * are the result of the logical and of their entry and the mask.
+ * @return success or failure */
+ bool setMaskPermissions( unsigned short );
+
+ /**
+ * Access to the permissions entry for a named user, if such an entry
+ * exists. @p exists is set to true if a matching entry exists and
+ * to false otherwise.
+ * @return the permissions for a user entry with the name in @p name */
+ unsigned short namedUserPermissions( const TQString& name, bool *exists ) const;
+
+
+ /** Set the permissions for a user with the name @p name. Will fail
+ * if the user doesn't exist, in which case the ACL will be unchanged.
+ * @return success or failure. */
+ bool setNamedUserPermissions( const TQString& name, unsigned short );
+
+ /** Returns the list of all group permission entries. Each entry consists
+ * of a name/permissions pair. This is a QPair, therefore access is provided
+ * via the .first and .next members.
+ * @return the list of all group permission entries. */
+ ACLUserPermissionsList allUserPermissions() const;
+
+ /** Replace the list of all user permissions with @p list. If one
+ * of the entries in the list does not exists, or setting of the ACL
+ * entry fails for any reason, the ACL will be left unchanged.
+ * @return success or failure */
+ bool setAllUserPermissions( const ACLUserPermissionsList &list );
+
+ /**
+ * Access to the permissions entry for a named group, if such an entry
+ * exists. @p exists is set to true if a matching entry exists and
+ * to false otherwise.
+ * @return the permissions for a group with the name in @p name */
+ unsigned short namedGroupPermissions( const TQString& name, bool *exists ) const;
+
+ /** Set the permissions for a group with the name @p name. Will fail
+ * if the group doesn't exist, in which case the ACL be unchanged.
+ * @return success or failure. */
+ bool setNamedGroupPermissions( const TQString& name, unsigned short );
+
+ /** Returns the list of all group permission entries. Each entry consists
+ * of a name/permissions pair. This is a QPair, therefor access is provided
+ * via the .first and .next members.
+ * @return the list of all group permission entries. */
+
+ ACLGroupPermissionsList allGroupPermissions() const;
+ /** Replace the list of all user permissions with @p list. If one
+ * of the entries in the list does not exists, or setting of the ACL
+ * entry fails for any reason, the ACL will be left unchanged.
+ * @return success or failure */
+ bool setAllGroupPermissions( const ACLGroupPermissionsList & );
+
+ /** Sets the whole list from a string. If the string in @p aclStr represents
+ * a valid ACL, it will be set, otherwise the ACL remains unchanged.
+ * @return whether setting the ACL was successful. */
+ bool setACL( const TQString &aclStr );
+
+ /** Return a string representation of the ACL.
+ * @return a string version of the ACL in the format compatible with libacl and
+ * POSIX 1003.1e. Implementations conforming to that standard should be able
+ * to take such strings as input. */
+ TQString asString() const;
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KACLPrivate;
+ KACLPrivate * d;
+ TDEIO_EXPORT friend TQDataStream & operator<< ( TQDataStream & s, const KACL & a );
+ TDEIO_EXPORT friend TQDataStream & operator>> ( TQDataStream & s, KACL & a );
+};
+
+TDEIO_EXPORT TQDataStream & operator<< ( TQDataStream & s, const KACL & a );
+TDEIO_EXPORT TQDataStream & operator>> ( TQDataStream & s, KACL & a );
+
+#endif
diff --git a/tdeio/tdeio/kar.cpp b/tdeio/tdeio/kar.cpp
new file mode 100644
index 000000000..07072d0c6
--- /dev/null
+++ b/tdeio/tdeio/kar.cpp
@@ -0,0 +1,170 @@
+
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 Laurence Anderson <l.d.anderson@warwick.ac.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <tqfile.h>
+#include <tqdir.h>
+#include <time.h>
+#include <kdebug.h>
+#include <tqptrlist.h>
+#include <kmimetype.h>
+#include <tqregexp.h>
+
+#include "kfilterdev.h"
+#include "kar.h"
+//#include "klimitediodevice.h"
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// KAr ///////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+class KAr::KArPrivate
+{
+public:
+ KArPrivate() {}
+};
+
+KAr::KAr( const TQString& filename )
+ : KArchive( 0L )
+{
+ //kdDebug(7042) << "KAr(filename) reached." << endl;
+ m_filename = filename;
+ d = new KArPrivate;
+ setDevice( TQT_TQIODEVICE(new TQFile( filename )) );
+}
+
+KAr::KAr( TQIODevice * dev )
+ : KArchive( dev )
+{
+ //kdDebug(7042) << "KAr::KAr( TQIODevice * dev) reached." << endl;
+ d = new KArPrivate;
+}
+
+KAr::~KAr()
+{
+ // mjarrett: Closes to prevent ~KArchive from aborting w/o device
+ //kdDebug(7042) << "~KAr reached." << endl;
+ if( isOpened() )
+ close();
+ if ( !m_filename.isEmpty() )
+ delete device(); // we created it ourselves
+ delete d;
+}
+
+bool KAr::openArchive( int mode )
+{
+ // Open archive
+
+ //kdDebug(7042) << "openarchive reached." << endl;
+
+ if ( mode == IO_WriteOnly )
+ return true;
+ if ( mode != IO_ReadOnly && mode != IO_ReadWrite )
+ {
+ kdWarning(7042) << "Unsupported mode " << mode << endl;
+ return false;
+ }
+
+ TQIODevice* dev = device();
+ if ( !dev )
+ return false;
+
+ char magic[8];
+ dev->readBlock (magic, 8);
+ if (tqstrncmp(magic, "!<arch>", 7) != 0) {
+ kdWarning(7042) << "Invalid main magic" << endl;
+ return false;
+ }
+
+ char *ar_longnames = 0;
+ while (! dev->atEnd()) {
+ TQCString ar_header;
+ ar_header.resize(61);
+ TQCString name;
+ int date, uid, gid, mode, size;
+
+ dev->at( dev->at() + (2 - (dev->at() % 2)) % 2 ); // Ar headers are padded to byte boundary
+
+ if ( dev->readBlock (ar_header.data(), 60) != 60 ) { // Read ar header
+ kdWarning(7042) << "Couldn't read header" << endl;
+ delete[] ar_longnames;
+ //return false;
+ return true; // Probably EOF / trailing junk
+ }
+
+ if (ar_header.right(2) != "`\n") { // Check header magic
+ kdWarning(7042) << "Invalid magic" << endl;
+ delete[] ar_longnames;
+ return false;
+ }
+
+ name = ar_header.mid( 0, 16 ); // Process header
+ date = ar_header.mid( 16, 12 ).toInt();
+ uid = ar_header.mid( 28, 6 ).toInt();
+ gid = ar_header.mid( 34, 6 ).toInt();
+ mode = ar_header.mid( 40, 8 ).toInt();
+ size = ar_header.mid( 48, 10 ).toInt();
+
+ bool skip_entry = false; // Deal with special entries
+ if (name.mid(0, 1) == "/") {
+ if (name.mid(1, 1) == "/") { // Longfilename table entry
+ delete[] ar_longnames;
+ ar_longnames = new char[size + 1];
+ ar_longnames[size] = '\0';
+ dev->readBlock (ar_longnames, size);
+ skip_entry = true;
+ kdDebug(7042) << "Read in longnames entry" << endl;
+ } else if (name.mid(1, 1) == " ") { // Symbol table entry
+ kdDebug(7042) << "Skipped symbol entry" << endl;
+ dev->at( dev->at() + size );
+ skip_entry = true;
+ } else { // Longfilename
+ kdDebug(7042) << "Longfilename #" << name.mid(1, 15).toInt() << endl;
+ if (! ar_longnames) {
+ kdWarning(7042) << "Invalid longfilename reference" << endl;
+ return false;
+ }
+ name = &ar_longnames[name.mid(1, 15).toInt()];
+ name = name.left(name.find("/"));
+ }
+ }
+ if (skip_entry) continue;
+
+ name = name.stripWhiteSpace(); // Process filename
+ name.replace( "/", "" );
+ kdDebug(7042) << "Filename: " << name << " Size: " << size << endl;
+
+ KArchiveEntry* entry;
+ entry = new KArchiveFile(this, name, mode, date, /*uid*/ 0, /*gid*/ 0, 0, dev->at(), size);
+ rootDir()->addEntry(entry); // Ar files don't support directorys, so everything in root
+
+ dev->at( dev->at() + size ); // Skip contents
+ }
+ delete[] ar_longnames;
+
+ return true;
+}
+
+bool KAr::closeArchive()
+{
+ // Close the archive
+ return true;
+}
+
+void KAr::virtual_hook( int id, void* data )
+{ KArchive::virtual_hook( id, data ); }
diff --git a/tdeio/tdeio/kar.h b/tdeio/tdeio/kar.h
new file mode 100644
index 000000000..83d94e75c
--- /dev/null
+++ b/tdeio/tdeio/kar.h
@@ -0,0 +1,103 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 Laurence Anderson <l.d.anderson@warwick.ac.uk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __kar_h
+#define __kar_h
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <tqdatetime.h>
+#include <tqstring.h>
+#include <tqstringlist.h>
+#include <tqdict.h>
+
+#include <karchive.h>
+
+/**
+ * KAr is a class for reading archives in ar format. Writing
+ * is not supported.
+ * @short A class for reading ar archives.
+ * @author Laurence Anderson <l.d.anderson@warwick.ac.uk>
+ * @since 3.1
+ */
+class TDEIO_EXPORT KAr : public KArchive
+{
+public:
+ /**
+ * Creates an instance that operates on the given filename.
+ *
+ * @param filename is a local path (e.g. "/home/holger/myfile.ar")
+ */
+ KAr( const TQString& filename );
+
+ /**
+ * Creates an instance that operates on the given device.
+ * The device can be compressed (KFilterDev) or not (TQFile, etc.).
+ * @param dev the device to read from
+ */
+ KAr( TQIODevice * dev );
+
+ /**
+ * If the ar file is still opened, then it will be
+ * closed automatically by the destructor.
+ */
+ virtual ~KAr();
+
+ /**
+ * The name of the ar file, as passed to the constructor.
+ * @return the filename. Null if you used the TQIODevice constructor
+ */
+ TQString fileName() { return m_filename; }
+
+ /*
+ * Writing not supported by this class, will always fail.
+ * @return always false
+ */
+ virtual bool prepareWriting( const TQString& name, const TQString& user, const TQString& group, uint size ) { Q_UNUSED(name); Q_UNUSED(user); Q_UNUSED(group); Q_UNUSED(size); return false; }
+
+ /*
+ * Writing not supported by this class, will always fail.
+ * @return always false
+ */
+ virtual bool doneWriting( uint size ) { Q_UNUSED(size); return false; }
+
+ /*
+ * Writing not supported by this class, will always fail.
+ * @return always false
+ */
+ virtual bool writeDir( const TQString& name, const TQString& user, const TQString& group ) { Q_UNUSED(name); Q_UNUSED(user); Q_UNUSED(group); return false; }
+
+protected:
+ /**
+ * Opens the archive for reading.
+ * Parses the directory listing of the archive
+ * and creates the KArchiveDirectory/KArchiveFile entries.
+ *
+ */
+ virtual bool openArchive( int mode );
+ virtual bool closeArchive();
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ TQString m_filename;
+ class KArPrivate;
+ KArPrivate * d;
+};
+
+#endif
diff --git a/tdeio/tdeio/karchive.cpp b/tdeio/tdeio/karchive.cpp
new file mode 100644
index 000000000..0e8d6789d
--- /dev/null
+++ b/tdeio/tdeio/karchive.cpp
@@ -0,0 +1,717 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+ Copyright (C) 2003 Leo Savernik <l.savernik@aon.at>
+
+ Moved from ktar.cpp by Roberto Teixeira <maragato@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <tqptrlist.h>
+#include <tqptrstack.h>
+#include <tqvaluestack.h>
+#include <tqmap.h>
+#include <tqcstring.h>
+#include <tqdir.h>
+#include <tqfile.h>
+
+#include <kdebug.h>
+#include <kfilterdev.h>
+#include <kfilterbase.h>
+#include <kde_file.h>
+
+#include "karchive.h"
+#include "klimitediodevice.h"
+
+template class TQDict<KArchiveEntry>;
+
+
+class KArchive::KArchivePrivate
+{
+public:
+ KArchiveDirectory* rootDir;
+ bool closeSucceeded;
+};
+
+class PosSortedPtrList : public TQPtrList<KArchiveFile> {
+protected:
+ int compareItems( TQPtrCollection::Item i1,
+ TQPtrCollection::Item i2 )
+ {
+ int pos1 = static_cast<KArchiveFile*>( i1 )->position();
+ int pos2 = static_cast<KArchiveFile*>( i2 )->position();
+ return ( pos1 - pos2 );
+ }
+};
+
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// KArchive ///////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+KArchive::KArchive( TQIODevice * dev )
+{
+ d = new KArchivePrivate;
+ d->rootDir = 0;
+ m_dev = dev;
+ m_open = false;
+}
+
+KArchive::~KArchive()
+{
+ if ( m_open )
+ close();
+ delete d->rootDir;
+ delete d;
+}
+
+bool KArchive::open( int mode )
+{
+ if ( m_dev && !m_dev->open( mode ) )
+ return false;
+
+ if ( m_open )
+ close();
+
+ m_mode = mode;
+ m_open = true;
+
+ Q_ASSERT( d->rootDir == 0L );
+ d->rootDir = 0L;
+
+ return openArchive( mode );
+}
+
+void KArchive::close()
+{
+ if ( !m_open )
+ return;
+ // moved by holger to allow kzip to write the zip central dir
+ // to the file in closeArchive()
+ d->closeSucceeded = closeArchive();
+
+ if ( m_dev )
+ m_dev->close();
+
+ delete d->rootDir;
+ d->rootDir = 0;
+ m_open = false;
+}
+
+bool KArchive::closeSucceeded() const
+{
+ return d->closeSucceeded;
+}
+
+const KArchiveDirectory* KArchive::directory() const
+{
+ // rootDir isn't const so that parsing-on-demand is possible
+ return const_cast<KArchive *>(this)->rootDir();
+}
+
+
+bool KArchive::addLocalFile( const TQString& fileName, const TQString& destName )
+{
+ TQFileInfo fileInfo( fileName );
+ if ( !fileInfo.isFile() && !fileInfo.isSymLink() )
+ {
+ kdWarning() << "KArchive::addLocalFile " << fileName << " doesn't exist or is not a regular file." << endl;
+ return false;
+ }
+
+ KDE_struct_stat fi;
+ if (KDE_lstat(TQFile::encodeName(fileName),&fi) == -1) {
+ kdWarning() << "KArchive::addLocalFile stating " << fileName
+ << " failed: " << strerror(errno) << endl;
+ return false;
+ }
+
+ if (fileInfo.isSymLink()) {
+ return writeSymLink(destName, fileInfo.readLink(), fileInfo.owner(),
+ fileInfo.group(), fi.st_mode, fi.st_atime, fi.st_mtime,
+ fi.st_ctime);
+ }/*end if*/
+
+ uint size = fileInfo.size();
+
+ // the file must be opened before prepareWriting is called, otherwise
+ // if the opening fails, no content will follow the already written
+ // header and the tar file is effectively f*cked up
+ TQFile file( fileName );
+ if ( !file.open( IO_ReadOnly ) )
+ {
+ kdWarning() << "KArchive::addLocalFile couldn't open file " << fileName << endl;
+ return false;
+ }
+
+ if ( !prepareWriting( destName, fileInfo.owner(), fileInfo.group(), size,
+ fi.st_mode, fi.st_atime, fi.st_mtime, fi.st_ctime ) )
+ {
+ kdWarning() << "KArchive::addLocalFile prepareWriting " << destName << " failed" << endl;
+ return false;
+ }
+
+ // Read and write data in chunks to minimize memory usage
+ TQByteArray array(8*1024);
+ int n;
+ uint total = 0;
+ while ( ( n = file.readBlock( array.data(), array.size() ) ) > 0 )
+ {
+ if ( !writeData( array.data(), n ) )
+ {
+ kdWarning() << "KArchive::addLocalFile writeData failed" << endl;
+ return false;
+ }
+ total += n;
+ }
+ Q_ASSERT( total == size );
+
+ if ( !doneWriting( size ) )
+ {
+ kdWarning() << "KArchive::addLocalFile doneWriting failed" << endl;
+ return false;
+ }
+ return true;
+}
+
+bool KArchive::addLocalDirectory( const TQString& path, const TQString& destName )
+{
+ TQString dot = ".";
+ TQString dotdot = "..";
+ TQDir dir( path );
+ if ( !dir.exists() )
+ return false;
+ dir.setFilter(dir.filter() | TQDir::Hidden);
+ TQStringList files = dir.entryList();
+ for ( TQStringList::Iterator it = files.begin(); it != files.end(); ++it )
+ {
+ if ( *it != dot && *it != dotdot )
+ {
+ TQString fileName = path + "/" + *it;
+// kdDebug() << "storing " << fileName << endl;
+ TQString dest = destName.isEmpty() ? *it : (destName + "/" + *it);
+ TQFileInfo fileInfo( fileName );
+
+ if ( fileInfo.isFile() || fileInfo.isSymLink() )
+ addLocalFile( fileName, dest );
+ else if ( fileInfo.isDir() )
+ addLocalDirectory( fileName, dest );
+ // We omit sockets
+ }
+ }
+ return true;
+}
+
+bool KArchive::writeFile( const TQString& name, const TQString& user, const TQString& group, uint size, const char* data )
+{
+ mode_t perm = 0100644;
+ time_t the_time = time(0);
+ return writeFile(name,user,group,size,perm,the_time,the_time,the_time,data);
+}
+
+bool KArchive::prepareWriting( const TQString& name, const TQString& user,
+ const TQString& group, uint size, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime ) {
+ PrepareWritingParams params;
+ params.name = &name;
+ params.user = &user;
+ params.group = &group;
+ params.size = size;
+ params.perm = perm;
+ params.atime = atime;
+ params.mtime = mtime;
+ params.ctime = ctime;
+ virtual_hook(VIRTUAL_PREPARE_WRITING,&params);
+ return params.retval;
+}
+
+bool KArchive::prepareWriting_impl(const TQString &name, const TQString &user,
+ const TQString &group, uint size, mode_t /*perm*/,
+ time_t /*atime*/, time_t /*mtime*/, time_t /*ctime*/ ) {
+ kdWarning(7040) << "New prepareWriting API not implemented in this class." << endl
+ << "Falling back to old API (metadata information will be lost)" << endl;
+ return prepareWriting(name,user,group,size);
+}
+
+bool KArchive::writeFile( const TQString& name, const TQString& user,
+ const TQString& group, uint size, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime,
+ const char* data ) {
+ WriteFileParams params;
+ params.name = &name;
+ params.user = &user;
+ params.group = &group;
+ params.size = size;
+ params.perm = perm;
+ params.atime = atime;
+ params.mtime = mtime;
+ params.ctime = ctime;
+ params.data = data;
+ virtual_hook(VIRTUAL_WRITE_FILE,&params);
+ return params.retval;
+}
+
+bool KArchive::writeFile_impl( const TQString& name, const TQString& user,
+ const TQString& group, uint size, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime,
+ const char* data ) {
+
+ if ( !prepareWriting( name, user, group, size, perm, atime, mtime, ctime ) )
+ {
+ kdWarning() << "KArchive::writeFile prepareWriting failed" << endl;
+ return false;
+ }
+
+ // Write data
+ // Note: if data is 0L, don't call writeBlock, it would terminate the KFilterDev
+ if ( data && size && !writeData( data, size ) )
+ {
+ kdWarning() << "KArchive::writeFile writeData failed" << endl;
+ return false;
+ }
+
+ if ( !doneWriting( size ) )
+ {
+ kdWarning() << "KArchive::writeFile doneWriting failed" << endl;
+ return false;
+ }
+ return true;
+}
+
+bool KArchive::writeDir(const TQString& name, const TQString& user,
+ const TQString& group, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime) {
+ WriteDirParams params;
+ params.name = &name;
+ params.user = &user;
+ params.group = &group;
+ params.perm = perm;
+ params.atime = atime;
+ params.mtime = mtime;
+ params.ctime = ctime;
+ virtual_hook(VIRTUAL_WRITE_DIR,&params);
+ return params.retval;
+}
+
+bool KArchive::writeDir_impl(const TQString &name, const TQString &user,
+ const TQString &group, mode_t /*perm*/,
+ time_t /*atime*/, time_t /*mtime*/, time_t /*ctime*/ ) {
+ kdWarning(7040) << "New writeDir API not implemented in this class." << endl
+ << "Falling back to old API (metadata information will be lost)" << endl;
+ return writeDir(name,user,group);
+}
+
+bool KArchive::writeSymLink(const TQString &name, const TQString &target,
+ const TQString &user, const TQString &group,
+ mode_t perm, time_t atime, time_t mtime, time_t ctime) {
+ WriteSymlinkParams params;
+ params.name = &name;
+ params.target = &target;
+ params.user = &user;
+ params.group = &group;
+ params.perm = perm;
+ params.atime = atime;
+ params.mtime = mtime;
+ params.ctime = ctime;
+ virtual_hook(VIRTUAL_WRITE_SYMLINK,&params);
+ return params.retval;
+}
+
+bool KArchive::writeSymLink_impl(const TQString &/*name*/,const TQString &/*target*/,
+ const TQString &/*user*/, const TQString &/*group*/,
+ mode_t /*perm*/, time_t /*atime*/, time_t /*mtime*/,
+ time_t /*ctime*/) {
+ kdWarning(7040) << "writeSymLink not implemented in this class." << endl
+ << "No fallback available." << endl;
+ // FIXME: better return true here for compatibility with KDE < 3.2
+ return false;
+}
+
+bool KArchive::writeData( const char* data, uint size )
+{
+ WriteDataParams params;
+ params.data = data;
+ params.size = size;
+ virtual_hook( VIRTUAL_WRITE_DATA, &params );
+ return params.retval;
+}
+
+bool KArchive::writeData_impl( const char* data, uint size )
+{
+ Q_ASSERT( device() );
+ return device()->writeBlock( data, size ) == (TQ_LONG)size;
+}
+
+KArchiveDirectory * KArchive::rootDir()
+{
+ if ( !d->rootDir )
+ {
+ //kdDebug() << "Making root dir " << endl;
+ struct passwd* pw = getpwuid( getuid() );
+ struct group* grp = getgrgid( getgid() );
+ TQString username = pw ? TQFile::decodeName(pw->pw_name) : TQString::number( getuid() );
+ TQString groupname = grp ? TQFile::decodeName(grp->gr_name) : TQString::number( getgid() );
+
+ d->rootDir = new KArchiveDirectory( this, TQString::fromLatin1("/"), (int)(0777 + S_IFDIR), 0, username, groupname, TQString::null );
+ }
+ return d->rootDir;
+}
+
+KArchiveDirectory * KArchive::findOrCreate( const TQString & path )
+{
+ //kdDebug() << "KArchive::findOrCreate " << path << endl;
+ if ( path.isEmpty() || path == "/" || path == "." ) // root dir => found
+ {
+ //kdDebug() << "KArchive::findOrCreate returning rootdir" << endl;
+ return rootDir();
+ }
+ // Important note : for tar files containing absolute paths
+ // (i.e. beginning with "/"), this means the leading "/" will
+ // be removed (no KDirectory for it), which is exactly the way
+ // the "tar" program works (though it displays a warning about it)
+ // See also KArchiveDirectory::entry().
+
+ // Already created ? => found
+ KArchiveEntry* ent = rootDir()->entry( path );
+ if ( ent )
+ {
+ if ( ent->isDirectory() )
+ //kdDebug() << "KArchive::findOrCreate found it" << endl;
+ return (KArchiveDirectory *) ent;
+ else
+ kdWarning() << "Found " << path << " but it's not a directory" << endl;
+ }
+
+ // Otherwise go up and try again
+ int pos = path.findRev( '/' );
+ KArchiveDirectory * parent;
+ TQString dirname;
+ if ( pos == -1 ) // no more slash => create in root dir
+ {
+ parent = rootDir();
+ dirname = path;
+ }
+ else
+ {
+ TQString left = path.left( pos );
+ dirname = path.mid( pos + 1 );
+ parent = findOrCreate( left ); // recursive call... until we find an existing dir.
+ }
+
+ //kdDebug() << "KTar : found parent " << parent->name() << " adding " << dirname << " to ensure " << path << endl;
+ // Found -> add the missing piece
+ KArchiveDirectory * e = new KArchiveDirectory( this, dirname, d->rootDir->permissions(),
+ d->rootDir->date(), d->rootDir->user(),
+ d->rootDir->group(), TQString::null );
+ parent->addEntry( e );
+ return e; // now a directory to <path> exists
+}
+
+void KArchive::setDevice( TQIODevice * dev )
+{
+ m_dev = dev;
+}
+
+void KArchive::setRootDir( KArchiveDirectory *rootDir )
+{
+ Q_ASSERT( !d->rootDir ); // Call setRootDir only once during parsing please ;)
+ d->rootDir = rootDir;
+}
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////// KArchiveEntry //////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+KArchiveEntry::KArchiveEntry( KArchive* t, const TQString& name, int access, int date,
+ const TQString& user, const TQString& group, const
+ TQString& symlink)
+{
+ m_name = name;
+ m_access = access;
+ m_date = date;
+ m_user = user;
+ m_group = group;
+ m_symlink = symlink;
+ m_archive = t;
+
+}
+
+TQDateTime KArchiveEntry::datetime() const
+{
+ TQDateTime d;
+ d.setTime_t( m_date );
+ return d;
+}
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////// KArchiveFile ///////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+KArchiveFile::KArchiveFile( KArchive* t, const TQString& name, int access, int date,
+ const TQString& user, const TQString& group,
+ const TQString & symlink,
+ int pos, int size )
+ : KArchiveEntry( t, name, access, date, user, group, symlink )
+{
+ m_pos = pos;
+ m_size = size;
+}
+
+int KArchiveFile::position() const
+{
+ return m_pos;
+}
+
+int KArchiveFile::size() const
+{
+ return m_size;
+}
+
+TQByteArray KArchiveFile::data() const
+{
+ archive()->device()->at( m_pos );
+
+ // Read content
+ TQByteArray arr( m_size );
+ if ( m_size )
+ {
+ assert( arr.data() );
+ int n = archive()->device()->readBlock( arr.data(), m_size );
+ if ( n != m_size )
+ arr.resize( n );
+ }
+ return arr;
+}
+
+// ** This should be a virtual method, and this code should be in ktar.cpp
+TQIODevice *KArchiveFile::device() const
+{
+ return new KLimitedIODevice( archive()->device(), m_pos, m_size );
+}
+
+void KArchiveFile::copyTo(const TQString& dest) const
+{
+ TQFile f( dest + "/" + name() );
+ f.open( IO_ReadWrite | IO_Truncate );
+ f.writeBlock( data() );
+ f.close();
+}
+
+////////////////////////////////////////////////////////////////////////
+//////////////////////// KArchiveDirectory /////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+
+KArchiveDirectory::KArchiveDirectory( KArchive* t, const TQString& name, int access,
+ int date,
+ const TQString& user, const TQString& group,
+ const TQString &symlink)
+ : KArchiveEntry( t, name, access, date, user, group, symlink )
+{
+ m_entries.setAutoDelete( true );
+}
+
+TQStringList KArchiveDirectory::entries() const
+{
+ TQStringList l;
+
+ TQDictIterator<KArchiveEntry> it( m_entries );
+ for( ; it.current(); ++it )
+ l.append( it.currentKey() );
+
+ return l;
+}
+
+KArchiveEntry* KArchiveDirectory::entry( TQString name )
+ // not "const TQString & name" since we want a local copy
+ // (to remove leading slash if any)
+{
+ int pos = name.find( '/' );
+ if ( pos == 0 ) // ouch absolute path (see also KArchive::findOrCreate)
+ {
+ if (name.length()>1)
+ {
+ name = name.mid( 1 ); // remove leading slash
+ pos = name.find( '/' ); // look again
+ }
+ else // "/"
+ return this;
+ }
+ // trailing slash ? -> remove
+ if ( pos != -1 && pos == (int)name.length()-1 )
+ {
+ name = name.left( pos );
+ pos = name.find( '/' ); // look again
+ }
+ if ( pos != -1 )
+ {
+ TQString left = name.left( pos );
+ TQString right = name.mid( pos + 1 );
+
+ //kdDebug() << "KArchiveDirectory::entry left=" << left << " right=" << right << endl;
+
+ KArchiveEntry* e = m_entries[ left ];
+ if ( !e || !e->isDirectory() )
+ return 0;
+ return ((KArchiveDirectory*)e)->entry( right );
+ }
+
+ return m_entries[ name ];
+}
+
+const KArchiveEntry* KArchiveDirectory::entry( TQString name ) const
+{
+ return ((KArchiveDirectory*)this)->entry( name );
+}
+
+void KArchiveDirectory::addEntry( KArchiveEntry* entry )
+{
+ Q_ASSERT( !entry->name().isEmpty() );
+ if( m_entries[ entry->name() ] ) {
+ kdWarning() << "KArchiveDirectory::addEntry: directory " << name()
+ << " has entry " << entry->name() << " already" << endl;
+ }
+ m_entries.insert( entry->name(), entry );
+}
+
+void KArchiveDirectory::copyTo(const TQString& dest, bool recursiveCopy ) const
+{
+ TQDir root;
+
+ PosSortedPtrList fileList;
+ TQMap<int, TQString> fileToDir;
+
+ TQStringList::Iterator it;
+
+ // placeholders for iterated items
+ KArchiveDirectory* curDir;
+ TQString curDirName;
+
+ TQStringList dirEntries;
+ KArchiveEntry* curEntry;
+ KArchiveFile* curFile;
+
+
+ TQPtrStack<KArchiveDirectory> dirStack;
+ TQValueStack<TQString> dirNameStack;
+
+ dirStack.push( this ); // init stack at current directory
+ dirNameStack.push( dest ); // ... with given path
+ do {
+ curDir = dirStack.pop();
+ curDirName = dirNameStack.pop();
+ root.mkdir(curDirName);
+
+ dirEntries = curDir->entries();
+ for ( it = dirEntries.begin(); it != dirEntries.end(); ++it ) {
+ curEntry = curDir->entry(*it);
+ if (!curEntry->symlink().isEmpty()) {
+ const TQString linkName = curDirName+'/'+curEntry->name();
+ kdDebug() << "symlink(" << curEntry->symlink() << ',' << linkName << ')';
+#ifdef Q_OS_UNIX
+ if (!::symlink(curEntry->symlink().local8Bit(), linkName.local8Bit())) {
+ kdDebug() << "symlink(" << curEntry->symlink() << ',' << linkName << ") failed:" << strerror(errno);
+ }
+#endif
+ } else {
+ if ( curEntry->isFile() ) {
+ curFile = dynamic_cast<KArchiveFile*>( curEntry );
+ if (curFile) {
+ fileList.append( curFile );
+ fileToDir.insert( curFile->position(), curDirName );
+ }
+ }
+
+ if ( curEntry->isDirectory() )
+ if ( recursiveCopy ) {
+ KArchiveDirectory *ad = dynamic_cast<KArchiveDirectory*>( curEntry );
+ if (ad) {
+ dirStack.push( ad );
+ dirNameStack.push( curDirName + "/" + curEntry->name() );
+ }
+ }
+ }
+ }
+ } while (!dirStack.isEmpty());
+
+ fileList.sort(); // sort on m_pos, so we have a linear access
+
+ KArchiveFile* f;
+ for ( f = fileList.first(); f; f = fileList.next() ) {
+ int pos = f->position();
+ f->copyTo( fileToDir[pos] );
+ }
+}
+
+void KArchive::virtual_hook( int id, void* data )
+{
+ switch (id) {
+ case VIRTUAL_WRITE_DATA: {
+ WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data);
+ params->retval = writeData_impl( params->data, params->size );
+ break;
+ }
+ case VIRTUAL_WRITE_SYMLINK: {
+ WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
+ params->retval = writeSymLink_impl(*params->name,*params->target,
+ *params->user,*params->group,params->perm,
+ params->atime,params->mtime,params->ctime);
+ break;
+ }
+ case VIRTUAL_WRITE_DIR: {
+ WriteDirParams *params = reinterpret_cast<WriteDirParams *>(data);
+ params->retval = writeDir_impl(*params->name,*params->user,
+ *params->group,params->perm,
+ params->atime,params->mtime,params->ctime);
+ break;
+ }
+ case VIRTUAL_WRITE_FILE: {
+ WriteFileParams *params = reinterpret_cast<WriteFileParams *>(data);
+ params->retval = writeFile_impl(*params->name,*params->user,
+ *params->group,params->size,params->perm,
+ params->atime,params->mtime,params->ctime,
+ params->data);
+ break;
+ }
+ case VIRTUAL_PREPARE_WRITING: {
+ PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
+ params->retval = prepareWriting_impl(*params->name,*params->user,
+ *params->group,params->size,params->perm,
+ params->atime,params->mtime,params->ctime);
+ break;
+ }
+ default:
+ /*BASE::virtual_hook( id, data )*/;
+ }/*end switch*/
+}
+
+void KArchiveEntry::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+void KArchiveFile::virtual_hook( int id, void* data )
+{ KArchiveEntry::virtual_hook( id, data ); }
+
+void KArchiveDirectory::virtual_hook( int id, void* data )
+{ KArchiveEntry::virtual_hook( id, data ); }
diff --git a/tdeio/tdeio/karchive.h b/tdeio/tdeio/karchive.h
new file mode 100644
index 000000000..840c560b7
--- /dev/null
+++ b/tdeio/tdeio/karchive.h
@@ -0,0 +1,639 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+ Copyright (C) 2003 Leo Savernik <l.savernik@aon.at>
+
+ Moved from ktar.h by Roberto Teixeira <maragato@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __karchive_h
+#define __karchive_h
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <tqdatetime.h>
+#include <tqstring.h>
+#include <tqstringlist.h>
+#include <tqdict.h>
+
+#include <tdelibs_export.h>
+
+class KArchiveDirectory;
+class KArchiveFile;
+
+/**
+ * KArchive is a base class for reading and writing archives.
+ * @short generic class for reading/writing archives
+ * @author David Faure <faure@kde.org>
+ */
+class TDEIO_EXPORT KArchive
+{
+protected:
+ /**
+ * Base constructor (protected since this is a pure virtual class).
+ * @param dev the I/O device where the archive reads its data
+ * Note that this can be a file, but also a data buffer, a compression filter, etc.
+ */
+ KArchive( TQIODevice * dev );
+
+public:
+ virtual ~KArchive();
+
+ /**
+ * Opens the archive for reading or writing.
+ * Inherited classes might want to reimplement openArchive instead.
+ * @param mode may be IO_ReadOnly or IO_WriteOnly
+ * @see close
+ */
+ virtual bool open( int mode );
+
+ /**
+ * Closes the archive.
+ * Inherited classes might want to reimplement closeArchive instead.
+ *
+ * @see open
+ */
+ virtual void close();
+
+ /**
+ * Use to check if close had any problem
+ * @return true if close succeded without problems
+ * @since 3.5
+ */
+ // TODO KDE4 merge with above
+ bool closeSucceeded() const;
+
+ /**
+ * Checks whether the archive is open.
+ * @return true if the archive is opened
+ */
+ bool isOpened() const { return m_open; }
+
+ /**
+ * Returns the mode in which the archive was opened
+ * @return the mode in which the archive was opened (IO_ReadOnly or IO_WriteOnly)
+ * @see open()
+ */
+ int mode() const { return m_mode; }
+
+ /**
+ * The underlying device.
+ * @return the underlying device.
+ */
+ TQIODevice * device() const { return m_dev; }
+
+ /**
+ * If an archive is opened for reading, then the contents
+ * of the archive can be accessed via this function.
+ * @return the directory of the archive
+ */
+ const KArchiveDirectory* directory() const;
+
+ /**
+ * Writes a local file into the archive. The main difference with writeFile,
+ * is that this method minimizes memory usage, by not loading the whole file
+ * into memory in one go.
+ *
+ * If @p fileName is a symbolic link, it will be written as is, i. e.
+ * it will not be resolved before.
+ * @param fileName full path to an existing local file, to be added to the archive.
+ * @param destName the resulting name (or relative path) of the file in the archive.
+ */
+ bool addLocalFile( const TQString& fileName, const TQString& destName );
+
+ /**
+ * Writes a local directory into the archive, including all its contents, recursively.
+ * Calls addLocalFile for each file to be added.
+ *
+ * Since KDE 3.2 it will also add a @p path that is a symbolic link to a
+ * directory. The symbolic link will be dereferenced and the content of the
+ * directory it is pointing to added recursively. However, symbolic links
+ * *under* @p path will be stored as is.
+ * @param path full path to an existing local directory, to be added to the archive.
+ * @param destName the resulting name (or relative path) of the file in the archive.
+ */
+ bool addLocalDirectory( const TQString& path, const TQString& destName );
+
+ /**
+ * If an archive is opened for writing then you can add new directories
+ * using this function. KArchive won't write one directory twice.
+ *
+ * @param name the name of the directory
+ * @param user the user that owns the directory
+ * @param group the group that owns the directory
+ *
+ * @todo TODO(BIC): make this a thin wrapper around
+ * writeDir(name,user,group,perm,atime,mtime,ctime)
+ * or eliminate it
+ */
+ virtual bool writeDir( const TQString& name, const TQString& user, const TQString& group ) = 0;
+
+ /**
+ * If an archive is opened for writing then you can add new directories
+ * using this function. KArchive won't write one directory twice.
+ *
+ * This method also allows some file metadata to be
+ * set. However, depending on the archive type not all metadata might be
+ * regarded.
+ * @param name the name of the directory
+ * @param user the user that owns the directory
+ * @param group the group that owns the directory
+ * @param perm permissions of the directory
+ * @param atime time the file was last accessed
+ * @param mtime modification time of the file
+ * @param ctime creation time of the file
+ * @since 3.2
+ * @todo TODO(BIC): make this virtual. For now use virtual hook
+ */
+ bool writeDir( const TQString& name, const TQString& user, const TQString& group,
+ mode_t perm, time_t atime, time_t mtime, time_t ctime );
+
+ /**
+ * Writes a symbolic link to the archive if the archive must be opened for
+ * writing.
+ * @param name name of symbolic link
+ * @param target target of symbolic link
+ * @param user the user that owns the directory
+ * @param group the group that owns the directory
+ * @param perm permissions of the directory
+ * @param atime time the file was last accessed
+ * @param mtime modification time of the file
+ * @param ctime creation time of the file
+ * @since 3.2
+ * @todo TODO(BIC) make virtual. For now it must be implemented by virtual_hook.
+ */
+ bool writeSymLink(const TQString &name, const TQString &target,
+ const TQString &user, const TQString &group,
+ mode_t perm, time_t atime, time_t mtime, time_t ctime);
+
+ /**
+ * If an archive is opened for writing then you can add a new file
+ * using this function. If the file name is for example "mydir/test1" then
+ * the directory "mydir" is automatically appended first if that did not
+ * happen yet.
+ * @param name the name of the file
+ * @param user the user that owns the file
+ * @param group the group that owns the file
+ * @param size the size of the file
+ * @param data the data to write (@p size bytes)
+ * @todo TODO(BIC): make this a thin non-virtual wrapper around
+ * writeFile(name,user,group,size,perm,atime,mtime,ctime,data)
+ */
+ virtual bool writeFile( const TQString& name, const TQString& user, const TQString& group, uint size, const char* data );
+
+ /**
+ * If an archive is opened for writing then you can add a new file
+ * using this function. If the file name is for example "mydir/test1" then
+ * the directory "mydir" is automatically appended first if that did not
+ * happen yet.
+ *
+ * This method also allows some file metadata to be
+ * set. However, depending on the archive type not all metadata might be
+ * regarded.
+ * @param name the name of the file
+ * @param user the user that owns the file
+ * @param group the group that owns the file
+ * @param size the size of the file
+ * @param perm permissions of the file
+ * @param atime time the file was last accessed
+ * @param mtime modification time of the file
+ * @param ctime creation time of the file
+ * @param data the data to write (@p size bytes)
+ * @since 3.2
+ * @todo TODO(BIC): make virtual. For now use virtual hook
+ */
+ bool writeFile( const TQString& name, const TQString& user, const TQString& group,
+ uint size, mode_t perm, time_t atime, time_t mtime,
+ time_t ctime, const char* data );
+
+ /**
+ * Here's another way of writing a file into an archive:
+ * Call prepareWriting, then call writeData()
+ * as many times as wanted then call doneWriting( totalSize ).
+ * For tar.gz files, you need to know the size before hand, since it is needed in the header.
+ * For zip files, size isn't used.
+ *
+ * @param name the name of the file
+ * @param user the user that owns the file
+ * @param group the group that owns the file
+ * @param size the size of the file
+ *
+ * @todo TODO(BIC): make this a thin non-virtual wrapper around
+ * prepareWriting(name,user,group,size,perm,atime,mtime,ctime)
+ * or eliminate it.
+ */
+ virtual bool prepareWriting( const TQString& name, const TQString& user, const TQString& group, uint size ) = 0;
+
+ /**
+ * Here's another way of writing a file into an archive:
+ * Call prepareWriting, then call writeData()
+ * as many times as wanted then call doneWriting( totalSize ).
+ * For tar.gz files, you need to know the size before hand, it is needed in the header!
+ * For zip files, size isn't used.
+ *
+ * This method also allows some file metadata to be
+ * set. However, depending on the archive type not all metadata might be
+ * regarded.
+ * @param name the name of the file
+ * @param user the user that owns the file
+ * @param group the group that owns the file
+ * @param size the size of the file
+ * @param perm permissions of the file
+ * @param atime time the file was last accessed
+ * @param mtime modification time of the file
+ * @param ctime creation time of the file
+ * @since 3.2
+ * @todo TODO(BIC): make this virtual. For now use virtual hook.
+ */
+ bool prepareWriting( const TQString& name, const TQString& user,
+ const TQString& group, uint size, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime );
+
+ /**
+ * Write data into the current file - to be called after calling prepareWriting
+ * @todo TODO(BIC) make virtual. For now virtual_hook allows reimplementing it.
+ */
+ bool writeData( const char* data, uint size );
+
+ /**
+ * Call doneWriting after writing the data.
+ * @param size the size of the file
+ * @see prepareWriting()
+ */
+ virtual bool doneWriting( uint size ) = 0;
+
+protected:
+ /**
+ * Opens an archive for reading or writing.
+ * Called by open.
+ * @param mode may be IO_ReadOnly or IO_WriteOnly
+ */
+ virtual bool openArchive( int mode ) = 0;
+
+ /**
+ * Closes the archive.
+ * Called by close.
+ */
+ virtual bool closeArchive() = 0;
+
+ /**
+ * Retrieves or create the root directory.
+ * The default implementation assumes that openArchive() did the parsing,
+ * so it creates a dummy rootdir if none was set (write mode, or no '/' in the archive).
+ * Reimplement this to provide parsing/listing on demand.
+ * @return the root directory
+ */
+ virtual KArchiveDirectory* rootDir();
+
+ /**
+ * Ensures that @p path exists, create otherwise.
+ * This handles e.g. tar files missing directory entries, like mico-2.3.0.tar.gz :)
+ * @param path the path of the directory
+ * @return the directory with the given @p path
+ */
+ KArchiveDirectory * findOrCreate( const TQString & path );
+
+ /**
+ * @internal for inherited constructors
+ */
+ void setDevice( TQIODevice *dev );
+
+ /**
+ * @internal for inherited classes
+ */
+ void setRootDir( KArchiveDirectory *rootDir );
+
+private:
+ TQIODevice * m_dev;
+ bool m_open;
+ char m_mode;
+protected:
+ virtual void virtual_hook( int id, void* data );
+ /* @internal for virtual_hook */
+ enum { VIRTUAL_WRITE_DATA = 1, VIRTUAL_WRITE_SYMLINK, VIRTUAL_WRITE_DIR,
+ VIRTUAL_WRITE_FILE, VIRTUAL_PREPARE_WRITING };
+ bool prepareWriting_impl( const TQString& name, const TQString& user,
+ const TQString& group, uint size, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime );
+ struct PrepareWritingParams {
+ const TQString *name;
+ const TQString *user;
+ const TQString *group;
+ uint size;
+ mode_t perm;
+ time_t atime, mtime, ctime;
+ bool retval;
+ };
+ bool writeFile_impl( const TQString& name, const TQString& user,
+ const TQString& group, uint size, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime,
+ const char* data );
+ struct WriteFileParams {
+ const TQString *name;
+ const TQString *user;
+ const TQString *group;
+ uint size;
+ mode_t perm;
+ time_t atime, mtime, ctime;
+ const char *data;
+ bool retval;
+ };
+ bool writeDir_impl(const TQString& name, const TQString& user,
+ const TQString& group, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime);
+ struct WriteDirParams {
+ const TQString *name;
+ const TQString *user;
+ const TQString *group;
+ mode_t perm;
+ time_t atime, mtime, ctime;
+ bool retval;
+ };
+ bool writeSymLink_impl(const TQString &name, const TQString &target,
+ const TQString &user, const TQString &group,
+ mode_t perm, time_t atime, time_t mtime, time_t ctime);
+ struct WriteSymlinkParams {
+ const TQString *name;
+ const TQString *target;
+ const TQString *user;
+ const TQString *group;
+ mode_t perm;
+ time_t atime, mtime, ctime;
+ bool retval;
+ };
+ bool writeData_impl( const char* data, uint size );
+ struct WriteDataParams {
+ const char* data;
+ uint size;
+ bool retval;
+ };
+private:
+ class KArchivePrivate;
+ KArchivePrivate * d;
+};
+
+/**
+ * A base class for entries in an KArchive.
+ * @short Base class for the archive-file's directory structure.
+ *
+ * @see KArchiveFile
+ * @see KArchiveDirectory
+ */
+class TDEIO_EXPORT KArchiveEntry
+{
+public:
+ /**
+ * Creates a new entry.
+ * @param archive the entries archive
+ * @param name the name of the entry
+ * @param access the permissions in unix format
+ * @param date the date (in seconds since 1970)
+ * @param user the user that owns the entry
+ * @param group the group that owns the entry
+ * @param symlink the symlink, or TQString::null
+ */
+ KArchiveEntry( KArchive* archive, const TQString& name, int access, int date,
+ const TQString& user, const TQString& group,
+ const TQString &symlink );
+
+ virtual ~KArchiveEntry() { }
+
+ /**
+ * Creation date of the file.
+ * @return the creation date
+ */
+ TQDateTime datetime() const;
+
+ /**
+ * Creation date of the file.
+ * @return the creation date in seconds since 1970
+ */
+ int date() const { return m_date; }
+
+ /**
+ * Name of the file without path.
+ * @return the file name without path
+ */
+ TQString name() const { return m_name; }
+ /**
+ * The permissions and mode flags as returned by the stat() function
+ * in st_mode.
+ * @return the permissions
+ */
+ mode_t permissions() const { return m_access; }
+ /**
+ * User who created the file.
+ * @return the owner of the file
+ */
+ TQString user() const { return m_user; }
+ /**
+ * Group of the user who created the file.
+ * @return the group of the file
+ */
+ TQString group() const { return m_group; }
+
+ /**
+ * Symlink if there is one.
+ * @return the symlink, or TQString::null
+ */
+ TQString symlink() const { return m_symlink; }
+
+ /**
+ * Checks whether the entry is a file.
+ * @return true if this entry is a file
+ */
+ virtual bool isFile() const { return false; }
+
+ /**
+ * Checks whether the entry is a directory.
+ * @return true if this entry is a directory
+ */
+ virtual bool isDirectory() const { return false; }
+
+protected:
+ KArchive* archive() const { return m_archive; }
+
+private:
+ TQString m_name;
+ int m_date;
+ mode_t m_access;
+ TQString m_user;
+ TQString m_group;
+ TQString m_symlink;
+ KArchive* m_archive;
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KArchiveEntryPrivate* d;
+};
+
+/**
+ * Represents a file entry in a KArchive.
+ * @short A file in an archive.
+ *
+ * @see KArchive
+ * @see KArchiveDirectory
+ */
+class TDEIO_EXPORT KArchiveFile : public KArchiveEntry
+{
+public:
+ /**
+ * Creates a new file entry.
+ * @param archive the entries archive
+ * @param name the name of the entry
+ * @param access the permissions in unix format
+ * @param date the date (in seconds since 1970)
+ * @param user the user that owns the entry
+ * @param group the group that owns the entry
+ * @param symlink the symlink, or TQString::null
+ * @param pos the position of the file in the directory
+ * @param size the size of the file
+ */
+ KArchiveFile( KArchive* archive, const TQString& name, int access, int date,
+ const TQString& user, const TQString& group, const TQString &symlink,
+ int pos, int size );
+
+ virtual ~KArchiveFile() { }
+
+ /**
+ * Position of the data in the [uncompressed] archive.
+ * @return the position of the file
+ */
+ int position() const; // TODO use TQ_LONG in KDE-4.0
+ /**
+ * Size of the data.
+ * @return the size of the file
+ */
+ int size() const; // TODO use TQ_LONG in KDE-4.0
+ /**
+ * Set size of data, usually after writing the file.
+ * @param s the new size of the file
+ */
+ void setSize( int s ) { m_size = s; }
+
+ /**
+ * Returns the data of the file.
+ * Call data() with care (only once per file), this data isn't cached.
+ * @return the content of this file.
+ */
+ virtual TQByteArray data() const;
+
+ /**
+ * This method returns TQIODevice (internal class: KLimitedIODevice)
+ * on top of the underlying TQIODevice. This is obviously for reading only.
+ * Note that the ownership of the device is being transferred to the caller,
+ * who will have to delete it.
+ * The returned device auto-opens (in readonly mode), no need to open it.
+ * @return the TQIODevice of the file
+ */
+ TQIODevice *device() const; // TODO make virtual
+
+ /**
+ * Checks whether this entry is a file.
+ * @return true, since this entry is a file
+ */
+ virtual bool isFile() const { return true; }
+
+ /**
+ * Extracts the file to the directory @p dest
+ * @param dest the directory to extract to
+ * @since 3.1
+ */
+ void copyTo(const TQString& dest) const;
+
+private:
+ int m_pos; // TODO use TQ_LONG in KDE-4.0
+ int m_size; // TODO use TQ_LONG in KDE-4.0
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KArchiveFilePrivate* d;
+};
+
+/**
+ * Represents a directory entry in a KArchive.
+ * @short A directory in an archive.
+ *
+ * @see KArchive
+ * @see KArchiveFile
+ */
+class TDEIO_EXPORT KArchiveDirectory : public KArchiveEntry
+{
+public:
+ /**
+ * Creates a new directory entry.
+ * @param archive the entries archive
+ * @param name the name of the entry
+ * @param access the permissions in unix format
+ * @param date the date (in seconds since 1970)
+ * @param user the user that owns the entry
+ * @param group the group that owns the entry
+ * @param symlink the symlink, or TQString::null
+ */
+ KArchiveDirectory( KArchive* archive, const TQString& name, int access, int date,
+ const TQString& user, const TQString& group,
+ const TQString& symlink);
+
+ virtual ~KArchiveDirectory() { }
+
+ /**
+ * Returns a list of sub-entries.
+ * @return the names of all entries in this directory (filenames, no path).
+ */
+ TQStringList entries() const;
+ /**
+ * Returns the entry with the given name.
+ * @param name may be "test1", "mydir/test3", "mydir/mysubdir/test3", etc.
+ * @return a pointer to the entry in the directory.
+ */
+ KArchiveEntry* entry( TQString name );
+ /**
+ * Returns the entry with the given name.
+ * @param name may be "test1", "mydir/test3", "mydir/mysubdir/test3", etc.
+ * @return a pointer to the entry in the directory.
+ */
+ const KArchiveEntry* entry( TQString name ) const;
+
+ /**
+ * @internal
+ * Adds a new entry to the directory.
+ */
+ void addEntry( KArchiveEntry* );
+
+ /**
+ * Checks whether this entry is a directory.
+ * @return true, since this entry is a directory
+ */
+ virtual bool isDirectory() const { return true; }
+
+ /**
+ * Extracts all entries in this archive directory to the directory
+ * @p dest.
+ * @param dest the directory to extract to
+ * @param recursive if set to true, subdirectories are extracted as well
+ * @since 3.1
+ */
+ void copyTo(const TQString& dest, bool recursive = true) const;
+
+private:
+ TQDict<KArchiveEntry> m_entries;
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KArchiveDirectoryPrivate* d;
+};
+
+#endif
diff --git a/tdeio/tdeio/kautomount.cpp b/tdeio/tdeio/kautomount.cpp
new file mode 100644
index 000000000..f167a6ccb
--- /dev/null
+++ b/tdeio/tdeio/kautomount.cpp
@@ -0,0 +1,117 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kautomount.h"
+#include "krun.h"
+#include "kdirwatch.h"
+#include "tdeio/job.h"
+#include <kdirnotify_stub.h>
+#include <kdebug.h>
+
+/***********************************************************************
+ *
+ * Utility classes
+ *
+ ***********************************************************************/
+
+KAutoMount::KAutoMount( bool _readonly, const TQString& _format, const TQString& _device,
+ const TQString& _mountpoint, const TQString & _desktopFile,
+ bool _show_filemanager_window )
+ : m_strDevice( _device ),
+ m_desktopFile( _desktopFile )
+{
+ //kdDebug(7015) << "KAutoMount device=" << _device << " mountpoint=" << _mountpoint << endl;
+ m_bShowFilemanagerWindow = _show_filemanager_window;
+
+ TDEIO::Job* job = TDEIO::mount( _readonly, _format.ascii(), _device, _mountpoint );
+ connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ), this, TQT_SLOT( slotResult( TDEIO::Job * ) ) );
+}
+
+void KAutoMount::slotResult( TDEIO::Job * job )
+{
+ if ( job->error() ) {
+ emit error();
+ job->showErrorDialog();
+ }
+ else
+ {
+ KURL mountpoint;
+ mountpoint.setPath( TDEIO::findDeviceMountPoint( m_strDevice ) );
+ //kdDebug(7015) << "KAutoMount: m_strDevice=" << m_strDevice << " -> mountpoint=" << mountpoint << endl;
+ Q_ASSERT( mountpoint.isValid() );
+
+ if ( mountpoint.path().isEmpty() )
+ kdWarning(7015) << m_strDevice << " was correctly mounted, but TDEIO::findDeviceMountPoint didn't find it. "
+ << "This looks like a bug, please report it on http://bugs.kde.org, together with your /etc/fstab line" << endl;
+ else if ( m_bShowFilemanagerWindow )
+ KRun::runURL( mountpoint, "inode/directory" );
+
+ // Notify about the new stuff in that dir, in case of opened windows showing it
+ KDirNotify_stub allDirNotify("*", "KDirNotify*");
+ allDirNotify.FilesAdded( mountpoint );
+
+ // Update the desktop file which is used for mount/unmount (icon change)
+ kdDebug(7015) << " mount finished : updating " << m_desktopFile << endl;
+ KURL dfURL;
+ dfURL.setPath( m_desktopFile );
+ allDirNotify.FilesChanged( dfURL );
+ //KDirWatch::self()->setFileDirty( m_desktopFile );
+
+ emit finished();
+ }
+ delete this;
+}
+
+KAutoUnmount::KAutoUnmount( const TQString & _mountpoint, const TQString & _desktopFile )
+ : m_desktopFile( _desktopFile ), m_mountpoint( _mountpoint )
+{
+ TDEIO::Job * job = TDEIO::unmount( m_mountpoint );
+ connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ), this, TQT_SLOT( slotResult( TDEIO::Job * ) ) );
+}
+
+void KAutoUnmount::slotResult( TDEIO::Job * job )
+{
+ if ( job->error() ) {
+ emit error();
+ job->showErrorDialog();
+ }
+ else
+ {
+ KDirNotify_stub allDirNotify("*", "KDirNotify*");
+ // Update the desktop file which is used for mount/unmount (icon change)
+ kdDebug(7015) << "unmount finished : updating " << m_desktopFile << endl;
+ KURL dfURL;
+ dfURL.setPath( m_desktopFile );
+ allDirNotify.FilesChanged( dfURL );
+ //KDirWatch::self()->setFileDirty( m_desktopFile );
+
+ // Notify about the new stuff in that dir, in case of opened windows showing it
+ // You may think we removed files, but this may have also readded some
+ // (if the mountpoint wasn't empty). The only possible behavior on FilesAdded
+ // is to relist the directory anyway.
+ KURL mp;
+ mp.setPath( m_mountpoint );
+ allDirNotify.FilesAdded( mp );
+
+ emit finished();
+ }
+
+ delete this;
+}
+
+#include "kautomount.moc"
diff --git a/tdeio/tdeio/kautomount.h b/tdeio/tdeio/kautomount.h
new file mode 100644
index 000000000..203c037d9
--- /dev/null
+++ b/tdeio/tdeio/kautomount.h
@@ -0,0 +1,122 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __auto_mount_h__
+#define __auto_mount_h__
+
+#include <tqobject.h>
+#include <tqstring.h>
+
+#include <tdelibs_export.h>
+
+#ifdef Q_MOC_RUN
+#define Q_OS_UNIX
+#endif // Q_MOC_RUN
+
+#ifdef Q_OS_UNIX
+
+namespace TDEIO {
+class Job;
+}
+
+/**
+ * This class implements synchronous mounting of devices,
+ * as well as showing a file-manager window after mounting a device, optionally.
+ * It is a wrapper around the asychronous TDEIO::special() call for mount,
+ * used by KMimeType.
+ *
+ * @short This class implements synchronous mounting of devices.
+ */
+class TDEIO_EXPORT KAutoMount : public TQObject
+{
+ Q_OBJECT
+ friend class gcc_gives_a_warning_without_this;
+public:
+ /**
+ * Mounts a device.
+ * @param readonly if true, the device is mounted read-only
+ * @param format the file system (e.g. vfat, ext2...) [optional, fstab is used otherwise]
+ * @param device the path to the device (e.g. /dev/fd0)
+ * @param mountpoint the directory where to mount the device [optional, fstab is used otherwise]
+ * @param desktopFile the file the user clicked on - to notify KDirWatch of the fact that
+ * it should emit fileDirty for it (to have the icon change)
+ * @param show_filemanager_window if true, a file-manager window for that mountpoint is shown after
+ * the mount, if successful.
+ */
+ KAutoMount( bool readonly, const TQString& format, const TQString& device, const TQString& mountpoint,
+ const TQString & desktopFile, bool show_filemanager_window = true );
+
+signals:
+ /** Emitted when the directory has been mounted */
+ void finished();
+ /** Emitted in case the directory could not been mounted */
+ void error();
+
+protected slots:
+ void slotResult( TDEIO::Job * );
+
+protected:
+ TQString m_strDevice;
+ bool m_bShowFilemanagerWindow;
+ TQString m_desktopFile;
+private:
+ /** KAutoMount deletes itself. Don't delete it manually. */
+ ~KAutoMount() {}
+ class KAutoMountPrivate* d;
+};
+
+/**
+ * This class implements synchronous unmounting of devices,
+ * It is a wrapper around the asychronous TDEIO::special() call for unmount,
+ * used by KMimeType.
+ *
+ * @short This class implements synchronous unmounting of devices,
+ */
+class TDEIO_EXPORT KAutoUnmount : public TQObject
+{
+ Q_OBJECT
+ friend class gcc_gives_a_warning_without_this;
+public:
+ /**
+ * Unmounts a device.
+ * @param mountpoint the mount point - KAutoUnmount finds the device from that
+ * @param desktopFile the file the user clicked on - to notify KDirWatch of the fact that
+ * it should emit fileDirty for it (to have the icon change)
+ */
+ KAutoUnmount( const TQString & mountpoint, const TQString & desktopFile );
+
+signals:
+ /** Emitted when the directory has been unmounted */
+ void finished();
+ /** Emitted in case the directory could not been unmounted */
+ void error();
+
+protected slots:
+ void slotResult( TDEIO::Job * );
+private:
+ TQString m_desktopFile;
+ TQString m_mountpoint;
+private:
+ /** KAutoUnmount deletes itself. Don't delete it manually. */
+ ~KAutoUnmount() {}
+ class KAutoUnmountPrivate* d;
+};
+
+#endif //Q_OS_UNIX
+
+#endif
diff --git a/tdeio/tdeio/kdatatool.cpp b/tdeio/tdeio/kdatatool.cpp
new file mode 100644
index 000000000..08630d110
--- /dev/null
+++ b/tdeio/tdeio/kdatatool.cpp
@@ -0,0 +1,285 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org>
+ Copyright (C) 2001 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kdatatool.h"
+
+#include <kstandarddirs.h>
+#include <klibloader.h>
+#include <kdebug.h>
+#include <kinstance.h>
+
+#include <ktrader.h>
+#include <tdeparts/componentfactory.h>
+
+#include <tqpixmap.h>
+#include <tqfile.h>
+
+/*************************************************
+ *
+ * KDataToolInfo
+ *
+ *************************************************/
+
+KDataToolInfo::KDataToolInfo()
+{
+ m_service = 0;
+}
+
+KDataToolInfo::KDataToolInfo( const KService::Ptr& service, TDEInstance* instance )
+{
+ m_service = service;
+ m_instance = instance;
+
+ if ( !!m_service && !m_service->serviceTypes().contains( "KDataTool" ) )
+ {
+ kdDebug(30003) << "The service " << m_service->name().latin1()
+ << " does not feature the service type KDataTool" << endl;
+ m_service = 0;
+ }
+}
+
+KDataToolInfo::KDataToolInfo( const KDataToolInfo& info )
+{
+ m_service = info.service();
+ m_instance = info.instance();
+}
+
+KDataToolInfo& KDataToolInfo::operator= ( const KDataToolInfo& info )
+{
+ m_service = info.service();
+ m_instance = info.instance();
+ return *this;
+}
+
+TQString KDataToolInfo::dataType() const
+{
+ if ( !m_service )
+ return TQString::null;
+
+ return m_service->property( "DataType" ).toString();
+}
+
+TQStringList KDataToolInfo::mimeTypes() const
+{
+ if ( !m_service )
+ return TQStringList();
+
+ return m_service->property( "DataMimeTypes" ).toStringList();
+}
+
+bool KDataToolInfo::isReadOnly() const
+{
+ if ( !m_service )
+ return true;
+
+ return m_service->property( "ReadOnly" ).toBool();
+}
+
+TQPixmap KDataToolInfo::icon() const
+{
+ if ( !m_service )
+ return TQPixmap();
+
+ TQPixmap pix;
+ TQStringList lst = TDEGlobal::dirs()->resourceDirs("icon");
+ TQStringList::ConstIterator it = lst.begin();
+ while (!pix.load( *it + "/" + m_service->icon() ) && it != lst.end() )
+ it++;
+
+ return pix;
+}
+
+TQPixmap KDataToolInfo::miniIcon() const
+{
+ if ( !m_service )
+ return TQPixmap();
+
+ TQPixmap pix;
+ TQStringList lst = TDEGlobal::dirs()->resourceDirs("mini");
+ TQStringList::ConstIterator it = lst.begin();
+ while (!pix.load( *it + "/" + m_service->icon() ) && it != lst.end() )
+ it++;
+
+ return pix;
+}
+
+TQString KDataToolInfo::iconName() const
+{
+ if ( !m_service )
+ return TQString::null;
+ return m_service->icon();
+}
+
+TQStringList KDataToolInfo::commands() const
+{
+ if ( !m_service )
+ return TQString();
+
+ return m_service->property( "Commands" ).toStringList();
+}
+
+TQStringList KDataToolInfo::userCommands() const
+{
+ if ( !m_service )
+ return TQString();
+
+ return TQStringList::split( ',', m_service->comment() );
+}
+
+KDataTool* KDataToolInfo::createTool( TQObject* parent, const char* name ) const
+{
+ if ( !m_service )
+ return 0;
+
+ KDataTool* tool = KParts::ComponentFactory::createInstanceFromService<KDataTool>( m_service, parent, name );
+ if ( tool )
+ tool->setInstance( m_instance );
+ return tool;
+}
+
+KService::Ptr KDataToolInfo::service() const
+{
+ return m_service;
+}
+
+TQValueList<KDataToolInfo> KDataToolInfo::query( const TQString& datatype, const TQString& mimetype, TDEInstance* instance )
+{
+ TQValueList<KDataToolInfo> lst;
+
+ TQString constr;
+
+ if ( !datatype.isEmpty() )
+ {
+ constr = TQString::fromLatin1( "DataType == '%1'" ).arg( datatype );
+ }
+ if ( !mimetype.isEmpty() )
+ {
+ TQString tmp = TQString::fromLatin1( "'%1' in DataMimeTypes" ).arg( mimetype );
+ if ( constr.isEmpty() )
+ constr = tmp;
+ else
+ constr = constr + " and " + tmp;
+ }
+/* Bug in KTrader ? Test with HEAD-tdelibs!
+ if ( instance )
+ {
+ TQString tmp = TQString::fromLatin1( "not ('%1' in ExcludeFrom)" ).arg( instance->instanceName() );
+ if ( constr.isEmpty() )
+ constr = tmp;
+ else
+ constr = constr + " and " + tmp;
+ } */
+
+ // Query the trader
+ //kdDebug() << "KDataToolInfo::query " << constr << endl;
+ KTrader::OfferList offers = KTrader::self()->query( "KDataTool", constr );
+
+ KTrader::OfferList::ConstIterator it = offers.begin();
+ for( ; it != offers.end(); ++it )
+ {
+ // Temporary replacement for the non-working trader query above
+ if ( !instance || !(*it)->property("ExcludeFrom").toStringList()
+ .contains( instance->instanceName() ) )
+ lst.append( KDataToolInfo( *it, instance ) );
+ else
+ kdDebug() << (*it)->entryPath() << " excluded." << endl;
+ }
+
+ return lst;
+}
+
+bool KDataToolInfo::isValid() const
+{
+ return( m_service );
+}
+
+/*************************************************
+ *
+ * KDataToolAction
+ *
+ *************************************************/
+KDataToolAction::KDataToolAction( const TQString & text, const KDataToolInfo & info, const TQString & command,
+ TQObject * parent, const char * name )
+ : KAction( text, info.iconName(), 0, parent, name ),
+ m_command( command ),
+ m_info( info )
+{
+}
+
+void KDataToolAction::slotActivated()
+{
+ emit toolActivated( m_info, m_command );
+}
+
+TQPtrList<KAction> KDataToolAction::dataToolActionList( const TQValueList<KDataToolInfo> & tools, const TQObject *receiver, const char* slot )
+{
+ TQPtrList<KAction> actionList;
+ if ( tools.isEmpty() )
+ return actionList;
+
+ actionList.append( new KActionSeparator() );
+ TQValueList<KDataToolInfo>::ConstIterator entry = tools.begin();
+ for( ; entry != tools.end(); ++entry )
+ {
+ TQStringList userCommands = (*entry).userCommands();
+ TQStringList commands = (*entry).commands();
+ Q_ASSERT(!commands.isEmpty());
+ if ( commands.count() != userCommands.count() )
+ kdWarning() << "KDataTool desktop file error (" << (*entry).service()->entryPath()
+ << "). " << commands.count() << " commands and "
+ << userCommands.count() << " descriptions." << endl;
+ TQStringList::ConstIterator uit = userCommands.begin();
+ TQStringList::ConstIterator cit = commands.begin();
+ for (; uit != userCommands.end() && cit != commands.end(); ++uit, ++cit )
+ {
+ //kdDebug() << "creating action " << *uit << " " << *cit << endl;
+ KDataToolAction * action = new KDataToolAction( *uit, *entry, *cit );
+ connect( action, TQT_SIGNAL( toolActivated( const KDataToolInfo &, const TQString & ) ),
+ receiver, slot );
+ actionList.append( action );
+ }
+ }
+
+ return actionList;
+}
+
+/*************************************************
+ *
+ * KDataTool
+ *
+ *************************************************/
+
+KDataTool::KDataTool( TQObject* parent, const char* name )
+ : TQObject( parent, name ), m_instance( 0L )
+{
+}
+
+TDEInstance* KDataTool::instance() const
+{
+ return m_instance;
+}
+
+void KDataToolAction::virtual_hook( int id, void* data )
+{ KAction::virtual_hook( id, data ); }
+
+void KDataTool::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+#include "kdatatool.moc"
diff --git a/tdeio/tdeio/kdatatool.h b/tdeio/tdeio/kdatatool.h
new file mode 100644
index 000000000..1258767fc
--- /dev/null
+++ b/tdeio/tdeio/kdatatool.h
@@ -0,0 +1,303 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999, 2000 Torben Weis <weis@kde.org>
+ Copyright (C) 2001 David Faure <david@mandrakesoft.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KDATATOOL_H
+#define KDATATOOL_H
+
+#include <tqobject.h>
+#include <tqvaluelist.h>
+
+#include <kaction.h>
+#include <kservice.h>
+
+class KDataTool;
+class TQPixmap;
+class TQStringList;
+class TDEInstance;
+
+// If you're only looking at implementing a data-tool, skip directly to the last
+// class definition, KDataTool.
+
+/**
+ * This is a convenience class for KService. You can use it if you have
+ * a KService describing a KDataTool. In this case the KDataToolInfo class
+ * is more convenient to work with.
+ *
+ * Especially useful is the method createTool which creates the datatool
+ * described by the service.
+ * @see KDataTool
+ */
+class TDEIO_EXPORT KDataToolInfo
+{
+public:
+ /**
+ * Create an invalid KDataToolInfo.
+ */
+ KDataToolInfo();
+ /**
+ * Create a valid KDataToolInfo.
+ * @param service the corresponding service
+ * @param instance the instance to use
+ */
+ KDataToolInfo( const KService::Ptr& service, TDEInstance* instance );
+ /**
+ * Copy constructor.
+ */
+ KDataToolInfo( const KDataToolInfo& info );
+ /**
+ * Assignment operator.
+ */
+ KDataToolInfo& operator= ( const KDataToolInfo& info );
+
+ /**
+ * Returns the data type that the DataTool can accept.
+ * @return the C++ data type that this DataTool accepts.
+ * For example "TQString" or "TQImage" or something more
+ * complicated.
+ */
+ TQString dataType() const;
+ /**
+ * Returns a list of mime type that will be accepted by the DataTool.
+ * The mimetypes are only used if the dataType can be used to store
+ * different mimetypes. For example in a "TQString" you could save "text/plain"
+ * or "text/html" or "text/xml".
+ *
+ * @return the mime types accepted by this DataTool. For example
+ * "image/gif" or "text/plain". In some cases the dataType
+ * determines the accepted type of data perfectly. In this cases
+ * this list may be empty.
+ */
+ TQStringList mimeTypes() const;
+
+ /**
+ * Checks whether the DataTool is read-only.
+ * @return true if the DataTool does not modify the data passed to it by KDataTool::run.
+ */
+ bool isReadOnly() const;
+
+ /**
+ * Returns the icon of this data tool.
+ * @return a large pixmap for the DataTool.
+ * @deprecated, use iconName()
+ */
+ TQPixmap icon() const KDE_DEPRECATED;
+ /**
+ * Returns the mini icon of this data tool.
+ * @return a mini pixmap for the DataTool.
+ * @deprecated, use iconName()
+ */
+ TQPixmap miniIcon() const KDE_DEPRECATED;
+ /**
+ * Returns the icon name for this DataTool.
+ * @return the name of the icon for the DataTool
+ */
+ TQString iconName() const;
+ /**
+ * Returns a list of strings that you can put in a TQPopupMenu item, for example to
+ * offer the DataTools services to the user. The returned value
+ * is usually something like "Spell checking", "Shrink Image", "Rotate Image"
+ * or something like that.
+ * This list comes from the Comment field of the tool's desktop file
+ * (so that it can be translated).
+ *
+ * Each of the strings returned corresponds to a string in the list returned by
+ * commands.
+ *
+ * @return a list of strings that you can put in a TQPopupMenu item
+ */
+ TQStringList userCommands() const;
+ /**
+ * Returns the list of commands the DataTool can execute. The application
+ * passes the command to the KDataTool::run method.
+ *
+ * This list comes from the Commands field of the tool's desktop file.
+ *
+ * Each of the strings returned corresponds to a string in the list returned by
+ * userCommands.
+ * @return the list of commands the DataTool can execute, suitable for
+ * the KDataTool::run method.
+ */
+ TQStringList commands() const;
+
+ /**
+ * Creates the data tool described by this KDataToolInfo.
+ * @param parent the parent of the TQObject (or 0 for parent-less KDataTools)
+ * @param name the name of the TQObject, can be 0
+ * @return a pointer to the created data tool or 0 on error.
+ */
+ KDataTool* createTool( TQObject* parent = 0, const char* name = 0 ) const;
+
+ /**
+ * The KDataToolInfo's service that is represented by this class.
+ * @return the service
+ */
+ KService::Ptr service() const;
+
+ /**
+ * The instance of the service.
+ * @return the instance
+ */
+ TDEInstance* instance() const { return m_instance; }
+
+ /**
+ * A DataToolInfo may be invalid if the KService passed to its constructor does
+ * not feature the service type "KDataTool".
+ * @return true if valid, false otherwise
+ */
+ bool isValid() const;
+
+ /**
+ * Queries the KTrader about installed KDataTool implementations.
+ * @param datatype a type that the application can 'export' to the tools (e.g. TQString)
+ * @param mimetype the mimetype of the data (e.g. text/plain)
+ * @param instance the application (or the part)'s instance (to check if a tool is excluded from this part,
+ * and also used if the tool wants to read its configuration in the app's config file).
+ * @return the list of results
+ */
+ static TQValueList<KDataToolInfo> query( const TQString& datatype, const TQString& mimetype, TDEInstance * instance );
+
+private:
+ KService::Ptr m_service;
+ TDEInstance* m_instance;
+private:
+ class KDataToolInfoPrivate* d;
+};
+
+
+/**
+ * This class helps applications implement support for KDataTool.
+ * The steps to follow are simple:
+ * @li query for the available tools using KDataToolInfo::query
+ * @li pass the result to KDataToolAction::dataToolActionList (with a slot)
+ * @li plug the resulting actions, either using KXMLGUIClient::plugActionList, or by hand.
+ *
+ * The slot defined for step 2 is called when the action is activated, and
+ * that's where the tool should be created and run.
+ */
+class TDEIO_EXPORT KDataToolAction : public KAction
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Constructs a new KDataToolAction.
+ *
+ * @param text The text that will be displayed.
+ * @param info the corresponding KDataToolInfo
+ * @param command the command of the action
+ * @param parent This action's parent.
+ * @param name An internal name for this action.
+ */
+ KDataToolAction( const TQString & text, const KDataToolInfo & info, const TQString & command, TQObject * parent = 0, const char * name = 0);
+
+ /**
+ * Creates a list of actions from a list of information about data-tools.
+ * The slot must have a signature corresponding to the toolActivated signal.
+ *
+ * Note that it's the caller's responsibility to delete the actions when they're not needed anymore.
+ * @param tools the list of data tool descriptions
+ * @param receiver the receiver for toolActivated() signals
+ * @param slot the slot that will receive the toolActivated() signals
+ * @return the KActions
+ */
+ static TQPtrList<KAction> dataToolActionList( const TQValueList<KDataToolInfo> & tools, const TQObject *receiver, const char* slot );
+
+signals:
+ /**
+ * Emitted when a tool has been activated.
+ * @param info a description of the activated tools
+ * @param command the command for the tool
+ */
+ void toolActivated( const KDataToolInfo & info, const TQString & command );
+
+protected:
+ virtual void slotActivated();
+
+private:
+ TQString m_command;
+ KDataToolInfo m_info;
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KDataToolActionPrivate* d;
+
+};
+
+/**
+ * A generic tool that processes data.
+ *
+ * A data-tool is a "plugin" for an application, that acts (reads/modifies)
+ * on a portion of the data present in the document (e.g. a text document,
+ * a single word or paragraph, a KSpread cell, an image, etc.)
+ *
+ * The application has some generic code for presenting the tools in a popupmenu
+ * @see KDataToolAction, and for activating a tool, passing it the data
+ * (and possibly getting modified data from it).
+ */
+class TDEIO_EXPORT KDataTool : public TQObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Constructor
+ * The data-tool is only created when a menu-item, that relates to it, is activated.
+ * @param parent the parent of the TQObject (or 0 for parent-less KDataTools)
+ * @param name the name of the TQObject, can be 0
+ */
+ KDataTool( TQObject* parent = 0, const char* name = 0 );
+
+ /**
+ * @internal. Do not use under any circumstance (including bad weather).
+ */
+ void setInstance( TDEInstance* instance ) { m_instance = instance; }
+
+ /**
+ * Returns the instance of the part that created this tool.
+ * Usually used if the tool wants to read its configuration in the app's config file.
+ * @return the instance of the part that created this tool.
+ */
+ TDEInstance* instance() const;
+
+ /**
+ * Interface for 'running' this tool.
+ * This is the method that the data-tool must implement.
+ *
+ * @param command is the command that was selected (see KDataToolInfo::commands())
+ * @param data the data provided by the application, on which to run the tool.
+ * The application is responsible for setting that data before running the tool,
+ * and for getting it back and updating itself with it, after the tool ran.
+ * @param datatype defines the type of @p data.
+ * @param mimetype defines the mimetype of the data (for instance datatype may be
+ * TQString, but the mimetype can be text/plain, text/html etc.)
+ * @return true if successful, false otherwise
+ */
+ virtual bool run( const TQString& command, void* data, const TQString& datatype, const TQString& mimetype) = 0;
+
+private:
+ TDEInstance * m_instance;
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KDataToolPrivate;
+ KDataToolPrivate * d;
+};
+
+#endif
diff --git a/tdeio/tdeio/kdcopservicestarter.cpp b/tdeio/tdeio/kdcopservicestarter.cpp
new file mode 100644
index 000000000..3c9b55501
--- /dev/null
+++ b/tdeio/tdeio/kdcopservicestarter.cpp
@@ -0,0 +1,97 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kdcopservicestarter.h"
+#include "ktrader.h"
+#include <kapplication.h>
+#include "kservice.h"
+#include <kstaticdeleter.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <dcopclient.h>
+
+static KStaticDeleter<KDCOPServiceStarter> dss_sd;
+KDCOPServiceStarter* KDCOPServiceStarter::s_self;
+
+KDCOPServiceStarter* KDCOPServiceStarter::self()
+{
+ if ( !s_self )
+ dss_sd.setObject( s_self, new KDCOPServiceStarter );
+ return s_self;
+}
+
+KDCOPServiceStarter::KDCOPServiceStarter()
+{
+ // Set the singleton instance - useful when a derived KDCOPServiceStarter
+ // was created (before self() was called)
+ s_self = this;
+}
+
+KDCOPServiceStarter::~KDCOPServiceStarter()
+{
+}
+
+int KDCOPServiceStarter::findServiceFor( const TQString& serviceType,
+ const TQString& _constraint,
+ const TQString& preferences,
+ TQString *error, TQCString* pDcopService,
+ int flags )
+{
+ // Ask the trader which service is preferred for this servicetype
+ // We want one that provides a DCOP interface
+ TQString constraint = _constraint;
+ if ( !constraint.isEmpty() )
+ constraint += " and ";
+ constraint += "exist [X-DCOP-ServiceName]";
+ KTrader::OfferList offers = KTrader::self()->query(serviceType, "Application", constraint, preferences);
+ if ( offers.isEmpty() ) {
+ if ( error )
+ *error = i18n("No service implementing %1").arg( serviceType );
+ kdWarning() << "KDCOPServiceStarter: No service implementing " << serviceType << endl;
+ return -1;
+ }
+ KService::Ptr ptr = offers.first();
+ TQCString dcopService = ptr->property("X-DCOP-ServiceName").toString().latin1();
+
+ if ( !kapp->dcopClient()->isApplicationRegistered( dcopService ) )
+ {
+ TQString error;
+ if ( startServiceFor( serviceType, constraint, preferences, &error, &dcopService, flags ) != 0 )
+ {
+ kdDebug() << "KDCOPServiceStarter: Couldn't start service: " << error << endl;
+ return -2;
+ }
+ }
+ kdDebug() << "KDCOPServiceStarter: DCOP service is available now, as " << dcopService << endl;
+ if ( pDcopService )
+ *pDcopService = dcopService;
+ return 0;
+}
+
+int KDCOPServiceStarter::startServiceFor( const TQString& serviceType,
+ const TQString& constraint,
+ const TQString& preferences,
+ TQString *error, TQCString* dcopService, int /*flags*/ )
+{
+ KTrader::OfferList offers = KTrader::self()->query(serviceType, "Application", constraint, preferences);
+ if ( offers.isEmpty() )
+ return -1;
+ KService::Ptr ptr = offers.first();
+ kdDebug() << "KDCOPServiceStarter: starting " << ptr->desktopEntryPath() << endl;
+ return kapp->startServiceByDesktopPath( ptr->desktopEntryPath(), TQStringList(), error, dcopService );
+}
diff --git a/tdeio/tdeio/kdcopservicestarter.h b/tdeio/tdeio/kdcopservicestarter.h
new file mode 100644
index 000000000..f70f0173e
--- /dev/null
+++ b/tdeio/tdeio/kdcopservicestarter.h
@@ -0,0 +1,103 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KDCOPSERVICESTARTER_H
+#define KDCOPSERVICESTARTER_H
+
+#include <tqstring.h>
+#include <kstaticdeleter.h>
+
+class KDCOPServiceStarter;
+class TQCString;
+
+/**
+ * A generic DCOP service starter, using KTrader.
+ * The default implementation starts new processes, but this interface can
+ * also be reimplemented by specific applications to provide dlopened in-process DCOP objects.
+ * @author David Faure <faure@kde.org>
+ */
+class TDEIO_EXPORT KDCOPServiceStarter {
+ friend class KStaticDeleter<KDCOPServiceStarter>;
+public:
+
+ static KDCOPServiceStarter* self();
+
+ /**
+ * Check if a given DCOP interface is available - from the serviceType it's supposed to implement.
+ *
+ * The trader is queried to find the preferred application for this serviceType,
+ * with the constraint that its X-DCOP-ServiceName property must be defined.
+ * Then the DCOP server is checked. If the service is not available,
+ * this method will call startServiceFor to start it.
+ *
+ * @param serviceType the type of service we're looking for
+ * @param constraint see KTrader
+ * @param preferences see KTrader
+ * @param error On failure, @p error contains a description of the error
+ * that occurred. If the pointer is 0, the argument will be
+ * ignored
+ * @param dcopService On success, @p dcopService contains the DCOP name
+ * under which this service is available. If the pointer is 0 the argument
+ * will be ignored
+ * @param flags for future extensions (currently unused)
+ *
+ * @return an error code indicating success (== 0) or failure (> 0).
+ */
+ int findServiceFor( const TQString& serviceType,
+ const TQString& constraint = TQString::null,
+ const TQString& preferences = TQString::null,
+ TQString *error=0, TQCString* dcopService=0,
+ int flags=0 );
+
+ /**
+ * Find an implementation of the given @p serviceType,
+ * and start it, to use its DCOP interface.
+ * The default implementation uses KTrader to find the preferred Application,
+ * and then starts it using kapp->startService...
+ *
+ * However applications (like kontact) can reimplement this method, to provide
+ * an in-process way of loading the implementation for this service type.
+ *
+ * @param serviceType the type of service we're looking for
+ * @param constraint see KTrader
+ * @param preferences see KTrader
+ * @param error On failure, @p error contains a description of the error
+ * that occurred. If the pointer is 0, the argument will be
+ * ignored
+ * @param dcopService On success, @p dcopService contains the DCOP name
+ * under which this service is available. If the pointer is 0 the argument
+ * will be ignored
+ * @param flags for future extensions (currently unused)
+ *
+ * @return an error code indicating success (== 0) or failure (> 0).
+ */
+ virtual int startServiceFor( const TQString& serviceType,
+ const TQString& constraint = TQString::null,
+ const TQString& preferences = TQString::null,
+ TQString *error=0, TQCString* dcopService=0,
+ int flags=0 );
+protected:
+ KDCOPServiceStarter();
+ virtual ~KDCOPServiceStarter();
+
+private:
+ static KDCOPServiceStarter* s_self;
+};
+
+#endif
+
diff --git a/tdeio/tdeio/kdirlister.cpp b/tdeio/tdeio/kdirlister.cpp
new file mode 100644
index 000000000..0d3498aa7
--- /dev/null
+++ b/tdeio/tdeio/kdirlister.cpp
@@ -0,0 +1,2538 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ 2000 Carsten Pfeiffer <pfeiffer@kde.org>
+ 2001-2005 Michael Brade <brade@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kdirlister.h"
+
+#include <tqregexp.h>
+#include <tqptrlist.h>
+#include <tqtimer.h>
+#include <tqeventloop.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <tdeio/job.h>
+#include <kmessagebox.h>
+#include <kglobal.h>
+#include <kglobalsettings.h>
+#include <kstaticdeleter.h>
+#include <kprotocolinfo.h>
+
+#include "kdirlister_p.h"
+
+#include <assert.h>
+#include <unistd.h>
+
+KDirListerCache* KDirListerCache::s_pSelf = 0;
+static KStaticDeleter<KDirListerCache> sd_KDirListerCache;
+
+// Enable this to get printDebug() called often, to see the contents of the cache
+//#define DEBUG_CACHE
+
+// Make really sure it doesn't get activated in the final build
+#ifdef NDEBUG
+#undef DEBUG_CACHE
+#endif
+
+KDirListerCache::KDirListerCache( int maxCount )
+ : itemsCached( maxCount )
+{
+ kdDebug(7004) << "+KDirListerCache" << endl;
+
+ itemsInUse.setAutoDelete( false );
+ itemsCached.setAutoDelete( true );
+ urlsCurrentlyListed.setAutoDelete( true );
+ urlsCurrentlyHeld.setAutoDelete( true );
+ pendingUpdates.setAutoDelete( true );
+
+ connect( kdirwatch, TQT_SIGNAL( dirty( const TQString& ) ),
+ this, TQT_SLOT( slotFileDirty( const TQString& ) ) );
+ connect( kdirwatch, TQT_SIGNAL( created( const TQString& ) ),
+ this, TQT_SLOT( slotFileCreated( const TQString& ) ) );
+ connect( kdirwatch, TQT_SIGNAL( deleted( const TQString& ) ),
+ this, TQT_SLOT( slotFileDeleted( const TQString& ) ) );
+}
+
+KDirListerCache::~KDirListerCache()
+{
+ kdDebug(7004) << "-KDirListerCache" << endl;
+
+ itemsInUse.setAutoDelete( true );
+ itemsInUse.clear();
+ itemsCached.clear();
+ urlsCurrentlyListed.clear();
+ urlsCurrentlyHeld.clear();
+
+ if ( KDirWatch::exists() )
+ kdirwatch->disconnect( this );
+}
+
+// setting _reload to true will emit the old files and
+// call updateDirectory
+bool KDirListerCache::listDir( KDirLister *lister, const KURL& _u,
+ bool _keep, bool _reload )
+{
+ // like this we don't have to worry about trailing slashes any further
+ KURL _url = _u;
+ _url.cleanPath(); // kill consecutive slashes
+ _url.adjustPath(-1);
+ TQString urlStr = _url.url();
+
+ if ( !lister->validURL( _url ) )
+ return false;
+
+#ifdef DEBUG_CACHE
+ printDebug();
+#endif
+ kdDebug(7004) << k_funcinfo << lister << " url=" << _url
+ << " keep=" << _keep << " reload=" << _reload << endl;
+
+ if ( !_keep )
+ {
+ // stop any running jobs for lister
+ stop( lister );
+
+ // clear our internal list for lister
+ forgetDirs( lister );
+
+ lister->d->rootFileItem = 0;
+ }
+ else if ( lister->d->lstDirs.find( _url ) != lister->d->lstDirs.end() )
+ {
+ // stop the job listing _url for this lister
+ stop( lister, _url );
+
+ // clear _url for lister
+ forgetDirs( lister, _url, true );
+
+ if ( lister->d->url == _url )
+ lister->d->rootFileItem = 0;
+ }
+
+ lister->d->lstDirs.append( _url );
+
+ if ( lister->d->url.isEmpty() || !_keep ) // set toplevel URL only if not set yet
+ lister->d->url = _url;
+
+ DirItem *itemU = itemsInUse[urlStr];
+ DirItem *itemC;
+
+ if ( !urlsCurrentlyListed[urlStr] )
+ {
+ // if there is an update running for _url already we get into
+ // the following case - it will just be restarted by updateDirectory().
+
+ if ( itemU )
+ {
+ kdDebug(7004) << "listDir: Entry already in use: " << _url << endl;
+
+ bool oldState = lister->d->complete;
+ lister->d->complete = false;
+
+ emit lister->started( _url );
+
+ if ( !lister->d->rootFileItem && lister->d->url == _url )
+ lister->d->rootFileItem = itemU->rootItem;
+
+ lister->addNewItems( *(itemU->lstItems) );
+ lister->emitItems();
+
+ // _url is already in use, so there is already an entry in urlsCurrentlyHeld
+ assert( urlsCurrentlyHeld[urlStr] );
+ urlsCurrentlyHeld[urlStr]->append( lister );
+
+ lister->d->complete = oldState;
+
+ emit lister->completed( _url );
+ if ( lister->d->complete )
+ emit lister->completed();
+
+ if ( _reload || !itemU->complete )
+ updateDirectory( _url );
+ }
+ else if ( !_reload && (itemC = itemsCached.take( urlStr )) )
+ {
+ kdDebug(7004) << "listDir: Entry in cache: " << _url << endl;
+
+ itemC->decAutoUpdate();
+ itemsInUse.insert( urlStr, itemC );
+ itemU = itemC;
+
+ bool oldState = lister->d->complete;
+ lister->d->complete = false;
+
+ emit lister->started( _url );
+
+ if ( !lister->d->rootFileItem && lister->d->url == _url )
+ lister->d->rootFileItem = itemC->rootItem;
+
+ lister->addNewItems( *(itemC->lstItems) );
+ lister->emitItems();
+
+ Q_ASSERT( !urlsCurrentlyHeld[urlStr] );
+ TQPtrList<KDirLister> *list = new TQPtrList<KDirLister>;
+ list->append( lister );
+ urlsCurrentlyHeld.insert( urlStr, list );
+
+ lister->d->complete = oldState;
+
+ emit lister->completed( _url );
+ if ( lister->d->complete )
+ emit lister->completed();
+
+ if ( !itemC->complete )
+ updateDirectory( _url );
+ }
+ else // dir not in cache or _reload is true
+ {
+ kdDebug(7004) << "listDir: Entry not in cache or reloaded: " << _url << endl;
+
+ TQPtrList<KDirLister> *list = new TQPtrList<KDirLister>;
+ list->append( lister );
+ urlsCurrentlyListed.insert( urlStr, list );
+
+ itemsCached.remove( urlStr );
+ itemU = new DirItem( _url );
+ itemsInUse.insert( urlStr, itemU );
+
+// // we have a limit of MAX_JOBS_PER_LISTER concurrently running jobs
+// if ( lister->numJobs() >= MAX_JOBS_PER_LISTER )
+// {
+// lstPendingUpdates.append( _url );
+// }
+// else
+// {
+
+ if ( lister->d->url == _url )
+ lister->d->rootFileItem = 0;
+
+ TDEIO::ListJob* job = TDEIO::listDir( _url, false /* no default GUI */ );
+ jobs.insert( job, TQValueList<TDEIO::UDSEntry>() );
+
+ lister->jobStarted( job );
+ lister->connectJob( job );
+
+ if ( lister->d->window )
+ job->setWindow( lister->d->window );
+
+ connect( job, TQT_SIGNAL( entries( TDEIO::Job *, const TDEIO::UDSEntryList & ) ),
+ this, TQT_SLOT( slotEntries( TDEIO::Job *, const TDEIO::UDSEntryList & ) ) );
+ connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ),
+ this, TQT_SLOT( slotResult( TDEIO::Job * ) ) );
+ connect( job, TQT_SIGNAL( redirection( TDEIO::Job *, const KURL & ) ),
+ this, TQT_SLOT( slotRedirection( TDEIO::Job *, const KURL & ) ) );
+
+ emit lister->started( _url );
+
+// }
+ }
+ }
+ else
+ {
+ kdDebug(7004) << "listDir: Entry currently being listed: " << _url << endl;
+
+ emit lister->started( _url );
+
+ urlsCurrentlyListed[urlStr]->append( lister );
+
+ TDEIO::ListJob *job = jobForUrl( urlStr );
+ Q_ASSERT( job );
+
+ lister->jobStarted( job );
+ lister->connectJob( job );
+
+ Q_ASSERT( itemU );
+
+ if ( !lister->d->rootFileItem && lister->d->url == _url )
+ lister->d->rootFileItem = itemU->rootItem;
+
+ lister->addNewItems( *(itemU->lstItems) );
+ lister->emitItems();
+ }
+
+ // automatic updating of directories
+ if ( lister->d->autoUpdate )
+ itemU->incAutoUpdate();
+
+ return true;
+}
+
+bool KDirListerCache::validURL( const KDirLister *lister, const KURL& url ) const
+{
+ if ( !url.isValid() )
+ {
+ if ( lister->d->autoErrorHandling )
+ {
+ TQString tmp = i18n("Malformed URL\n%1").arg( url.prettyURL() );
+ KMessageBox::error( lister->d->errorParent, tmp );
+ }
+ return false;
+ }
+
+ if ( !KProtocolInfo::supportsListing( url ) )
+ {
+ if ( lister->d->autoErrorHandling )
+ {
+ // TODO: this message should be changed during next string unfreeze!
+ TQString tmp = i18n("Malformed URL\n%1").arg( url.prettyURL() );
+ KMessageBox::error( lister->d->errorParent, tmp );
+ }
+ return false;
+ }
+
+ return true;
+}
+
+void KDirListerCache::stop( KDirLister *lister )
+{
+#ifdef DEBUG_CACHE
+ printDebug();
+#endif
+ kdDebug(7004) << k_funcinfo << "lister: " << lister << endl;
+ bool stopped = false;
+
+ TQDictIterator< TQPtrList<KDirLister> > it( urlsCurrentlyListed );
+ TQPtrList<KDirLister> *listers;
+ while ( (listers = it.current()) )
+ {
+ if ( listers->findRef( lister ) > -1 )
+ {
+ // lister is listing url
+ TQString url = it.currentKey();
+
+ //kdDebug(7004) << k_funcinfo << " found lister in list - for " << url << endl;
+ bool ret = listers->removeRef( lister );
+ Q_ASSERT( ret );
+
+ TDEIO::ListJob *job = jobForUrl( url );
+ if ( job )
+ lister->jobDone( job );
+
+ // move lister to urlsCurrentlyHeld
+ TQPtrList<KDirLister> *holders = urlsCurrentlyHeld[url];
+ if ( !holders )
+ {
+ holders = new TQPtrList<KDirLister>;
+ urlsCurrentlyHeld.insert( url, holders );
+ }
+
+ holders->append( lister );
+
+ emit lister->canceled( KURL( url ) );
+
+ //kdDebug(7004) << k_funcinfo << "remaining list: " << listers->count() << " listers" << endl;
+
+ if ( listers->isEmpty() )
+ {
+ // kill the job since it isn't used any more
+ if ( job )
+ killJob( job );
+
+ urlsCurrentlyListed.remove( url );
+ }
+
+ stopped = true;
+ }
+ else
+ ++it;
+ }
+
+ if ( stopped )
+ {
+ emit lister->canceled();
+ lister->d->complete = true;
+ }
+
+ // this is wrong if there is still an update running!
+ //Q_ASSERT( lister->d->complete );
+}
+
+void KDirListerCache::stop( KDirLister *lister, const KURL& _u )
+{
+ TQString urlStr( _u.url(-1) );
+ KURL _url( urlStr );
+
+ // TODO: consider to stop all the "child jobs" of _url as well
+ kdDebug(7004) << k_funcinfo << lister << " url=" << _url << endl;
+
+ TQPtrList<KDirLister> *listers = urlsCurrentlyListed[urlStr];
+ if ( !listers || !listers->removeRef( lister ) )
+ return;
+
+ // move lister to urlsCurrentlyHeld
+ TQPtrList<KDirLister> *holders = urlsCurrentlyHeld[urlStr];
+ if ( !holders )
+ {
+ holders = new TQPtrList<KDirLister>;
+ urlsCurrentlyHeld.insert( urlStr, holders );
+ }
+
+ holders->append( lister );
+
+
+ TDEIO::ListJob *job = jobForUrl( urlStr );
+ if ( job )
+ lister->jobDone( job );
+
+ emit lister->canceled( _url );
+
+ if ( listers->isEmpty() )
+ {
+ // kill the job since it isn't used any more
+ if ( job )
+ killJob( job );
+
+ urlsCurrentlyListed.remove( urlStr );
+ }
+
+ if ( lister->numJobs() == 0 )
+ {
+ lister->d->complete = true;
+
+ // we killed the last job for lister
+ emit lister->canceled();
+ }
+}
+
+void KDirListerCache::setAutoUpdate( KDirLister *lister, bool enable )
+{
+ // IMPORTANT: this method does not check for the current autoUpdate state!
+
+ for ( KURL::List::Iterator it = lister->d->lstDirs.begin();
+ it != lister->d->lstDirs.end(); ++it )
+ {
+ if ( enable )
+ itemsInUse[(*it).url()]->incAutoUpdate();
+ else
+ itemsInUse[(*it).url()]->decAutoUpdate();
+ }
+}
+
+void KDirListerCache::forgetDirs( KDirLister *lister )
+{
+ kdDebug(7004) << k_funcinfo << lister << endl;
+
+ emit lister->clear();
+
+ // forgetDirs() will modify lstDirs, make a copy first
+ KURL::List lstDirsCopy = lister->d->lstDirs;
+ for ( KURL::List::Iterator it = lstDirsCopy.begin();
+ it != lstDirsCopy.end(); ++it )
+ {
+ forgetDirs( lister, *it, false );
+ }
+}
+
+void KDirListerCache::forgetDirs( KDirLister *lister, const KURL& _url, bool notify )
+{
+ kdDebug(7004) << k_funcinfo << lister << " _url: " << _url << endl;
+
+ KURL url( _url );
+ url.adjustPath( -1 );
+ TQString urlStr = url.url();
+ TQPtrList<KDirLister> *holders = urlsCurrentlyHeld[urlStr];
+ //Q_ASSERT( holders );
+ if ( holders )
+ {
+ holders->removeRef( lister );
+ }
+
+ // remove the dir from lister->d->lstDirs so that it doesn't contain things
+ // that itemsInUse doesn't. When emitting the canceled signals lstDirs must
+ // not contain anything that itemsInUse does not contain. (otherwise it
+ // might crash in findByName()).
+ lister->d->lstDirs.remove( lister->d->lstDirs.find( url ) );
+
+ DirItem *item = itemsInUse[urlStr];
+
+ if ( holders && holders->isEmpty() )
+ {
+ urlsCurrentlyHeld.remove( urlStr ); // this deletes the (empty) holders list
+ if ( !urlsCurrentlyListed[urlStr] )
+ {
+ // item not in use anymore -> move into cache if complete
+ itemsInUse.remove( urlStr );
+
+ // this job is a running update
+ TDEIO::ListJob *job = jobForUrl( urlStr );
+ if ( job )
+ {
+ lister->jobDone( job );
+ killJob( job );
+ kdDebug(7004) << k_funcinfo << "Killing update job for " << urlStr << endl;
+
+ emit lister->canceled( url );
+ if ( lister->numJobs() == 0 )
+ {
+ lister->d->complete = true;
+ emit lister->canceled();
+ }
+ }
+
+ if ( notify )
+ emit lister->clear( url );
+
+ if ( item && item->complete )
+ {
+ kdDebug(7004) << k_funcinfo << lister << " item moved into cache: " << url << endl;
+ itemsCached.insert( urlStr, item ); // TODO: may return false!!
+
+ // Should we forget the dir for good, or keep a watch on it?
+ // Generally keep a watch, except when it would prevent
+ // unmounting a removable device (#37780)
+ const bool isLocal = item->url.isLocalFile();
+ const bool isManuallyMounted = isLocal && TDEIO::manually_mounted( item->url.path() );
+ bool containsManuallyMounted = false;
+ if ( !isManuallyMounted && item->lstItems && isLocal )
+ {
+ // Look for a manually-mounted directory inside
+ // If there's one, we can't keep a watch either, FAM would prevent unmounting the CDROM
+ // I hope this isn't too slow (manually_mounted caches the last device so most
+ // of the time this is just a stat per subdir)
+ KFileItemListIterator kit( *item->lstItems );
+ for ( ; kit.current() && !containsManuallyMounted; ++kit )
+ if ( (*kit)->isDir() && TDEIO::manually_mounted( (*kit)->url().path() ) )
+ containsManuallyMounted = true;
+ }
+
+ if ( isManuallyMounted || containsManuallyMounted )
+ {
+ kdDebug(7004) << "Not adding a watch on " << item->url << " because it " <<
+ ( isManuallyMounted ? "is manually mounted" : "contains a manually mounted subdir" ) << endl;
+ item->complete = false; // set to "dirty"
+ }
+ else
+ item->incAutoUpdate(); // keep watch
+ }
+ else
+ {
+ delete item;
+ item = 0;
+ }
+ }
+ }
+
+ if ( item && lister->d->autoUpdate )
+ item->decAutoUpdate();
+}
+
+void KDirListerCache::updateDirectory( const KURL& _dir )
+{
+ kdDebug(7004) << k_funcinfo << _dir << endl;
+
+ TQString urlStr = _dir.url(-1);
+ if ( !checkUpdate( urlStr ) )
+ return;
+
+ // A job can be running to
+ // - only list a new directory: the listers are in urlsCurrentlyListed
+ // - only update a directory: the listers are in urlsCurrentlyHeld
+ // - update a currently running listing: the listers are in urlsCurrentlyListed
+ // and urlsCurrentlyHeld
+
+ TQPtrList<KDirLister> *listers = urlsCurrentlyListed[urlStr];
+ TQPtrList<KDirLister> *holders = urlsCurrentlyHeld[urlStr];
+
+ // restart the job for _dir if it is running already
+ bool killed = false;
+ TQWidget *window = 0;
+ TDEIO::ListJob *job = jobForUrl( urlStr );
+ if ( job )
+ {
+ window = job->window();
+
+ killJob( job );
+ killed = true;
+
+ if ( listers )
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ kdl->jobDone( job );
+
+ if ( holders )
+ for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
+ kdl->jobDone( job );
+ }
+ kdDebug(7004) << k_funcinfo << "Killed = " << killed << endl;
+
+ // we don't need to emit canceled signals since we only replaced the job,
+ // the listing is continuing.
+
+ Q_ASSERT( !listers || (listers && killed) );
+
+ job = TDEIO::listDir( _dir, false /* no default GUI */ );
+ jobs.insert( job, TQValueList<TDEIO::UDSEntry>() );
+
+ connect( job, TQT_SIGNAL(entries( TDEIO::Job *, const TDEIO::UDSEntryList & )),
+ this, TQT_SLOT(slotUpdateEntries( TDEIO::Job *, const TDEIO::UDSEntryList & )) );
+ connect( job, TQT_SIGNAL(result( TDEIO::Job * )),
+ this, TQT_SLOT(slotUpdateResult( TDEIO::Job * )) );
+
+ kdDebug(7004) << k_funcinfo << "update started in " << _dir << endl;
+
+ if ( listers )
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ kdl->jobStarted( job );
+
+ if ( holders )
+ {
+ if ( !killed )
+ {
+ bool first = true;
+ for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
+ {
+ kdl->jobStarted( job );
+ if ( first && kdl->d->window )
+ {
+ first = false;
+ job->setWindow( kdl->d->window );
+ }
+ emit kdl->started( _dir );
+ }
+ }
+ else
+ {
+ job->setWindow( window );
+
+ for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
+ kdl->jobStarted( job );
+ }
+ }
+}
+
+bool KDirListerCache::checkUpdate( const TQString& _dir )
+{
+ if ( !itemsInUse[_dir] )
+ {
+ DirItem *item = itemsCached[_dir];
+ if ( item && item->complete )
+ {
+ item->complete = false;
+ item->decAutoUpdate();
+ // Hmm, this debug output might include login/password from the _dir URL.
+ //kdDebug(7004) << k_funcinfo << "directory " << _dir << " not in use, marked dirty." << endl;
+ }
+ //else
+ //kdDebug(7004) << k_funcinfo << "aborted, directory " << _dir << " not in cache." << endl;
+
+ return false;
+ }
+ else
+ return true;
+}
+
+KFileItemList *KDirListerCache::itemsForDir( const KURL &_dir ) const
+{
+ TQString urlStr = _dir.url(-1);
+ DirItem *item = itemsInUse[ urlStr ];
+ if ( !item )
+ item = itemsCached[ urlStr ];
+ return item ? item->lstItems : 0;
+}
+
+KFileItem *KDirListerCache::findByName( const KDirLister *lister, const TQString& _name ) const
+{
+ Q_ASSERT( lister );
+
+ for ( KURL::List::Iterator it = lister->d->lstDirs.begin();
+ it != lister->d->lstDirs.end(); ++it )
+ {
+ KFileItemListIterator kit( *itemsInUse[(*it).url()]->lstItems );
+ for ( ; kit.current(); ++kit )
+ if ( (*kit)->name() == _name )
+ return (*kit);
+ }
+
+ return 0L;
+}
+
+KFileItem *KDirListerCache::findByURL( const KDirLister *lister, const KURL& _u ) const
+{
+ KURL _url = _u;
+ _url.adjustPath(-1);
+
+ KURL parentDir( _url );
+ parentDir.setPath( parentDir.directory() );
+
+ // If lister is set, check that it contains this dir
+ if ( lister && !lister->d->lstDirs.contains( parentDir ) )
+ return 0L;
+
+ KFileItemList *itemList = itemsForDir( parentDir );
+ if ( itemList )
+ {
+ KFileItemListIterator kit( *itemList );
+ for ( ; kit.current(); ++kit )
+ if ( (*kit)->url() == _url )
+ return (*kit);
+ }
+ return 0L;
+}
+
+void KDirListerCache::FilesAdded( const KURL &dir )
+{
+ kdDebug(7004) << k_funcinfo << dir << endl;
+ updateDirectory( dir );
+}
+
+void KDirListerCache::FilesRemoved( const KURL::List &fileList )
+{
+ kdDebug(7004) << k_funcinfo << endl;
+ KURL::List::ConstIterator it = fileList.begin();
+ for ( ; it != fileList.end() ; ++it )
+ {
+ // emit the deleteItem signal if this file was shown in any view
+ KFileItem *fileitem = 0L;
+ KURL parentDir( *it );
+ parentDir.setPath( parentDir.directory() );
+ KFileItemList *lstItems = itemsForDir( parentDir );
+ if ( lstItems )
+ {
+ KFileItem *fit = lstItems->first();
+ for ( ; fit; fit = lstItems->next() )
+ if ( fit->url() == *it ) {
+ fileitem = fit;
+ lstItems->take(); // remove fileitem from list
+ break;
+ }
+ }
+
+ // Tell the views about it before deleting the KFileItems. They might need the subdirs'
+ // file items (see the dirtree).
+ if ( fileitem )
+ {
+ TQPtrList<KDirLister> *listers = urlsCurrentlyHeld[parentDir.url()];
+ if ( listers )
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ kdl->emitDeleteItem( fileitem );
+ }
+
+ // If we found a fileitem, we can test if it's a dir. If not, we'll go to deleteDir just in case.
+ if ( !fileitem || fileitem->isDir() )
+ {
+ // in case of a dir, check if we have any known children, there's much to do in that case
+ // (stopping jobs, removing dirs from cache etc.)
+ deleteDir( *it );
+ }
+
+ // now remove the item itself
+ delete fileitem;
+ }
+}
+
+void KDirListerCache::FilesChanged( const KURL::List &fileList )
+{
+ KURL::List dirsToUpdate;
+ kdDebug(7004) << k_funcinfo << "only half implemented" << endl;
+ KURL::List::ConstIterator it = fileList.begin();
+ for ( ; it != fileList.end() ; ++it )
+ {
+ if ( ( *it ).isLocalFile() )
+ {
+ kdDebug(7004) << "KDirListerCache::FilesChanged " << *it << endl;
+ KFileItem *fileitem = findByURL( 0, *it );
+ if ( fileitem )
+ {
+ // we need to refresh the item, because e.g. the permissions can have changed.
+ aboutToRefreshItem( fileitem );
+ fileitem->refresh();
+ emitRefreshItem( fileitem );
+ }
+ else
+ kdDebug(7004) << "item not found" << endl;
+ } else {
+ // For remote files, refresh() won't be able to figure out the new information.
+ // Let's update the dir.
+ KURL dir( *it );
+ dir.setPath( dir.directory( true ) );
+ if ( dirsToUpdate.find( dir ) == dirsToUpdate.end() )
+ dirsToUpdate.prepend( dir );
+ }
+ }
+
+ KURL::List::ConstIterator itdir = dirsToUpdate.begin();
+ for ( ; itdir != dirsToUpdate.end() ; ++itdir )
+ updateDirectory( *itdir );
+ // ## TODO problems with current jobs listing/updating that dir
+ // ( see kde-2.2.2's kdirlister )
+}
+
+void KDirListerCache::FileRenamed( const KURL &src, const KURL &dst )
+{
+ kdDebug(7004) << k_funcinfo << src.prettyURL() << " -> " << dst.prettyURL() << endl;
+#ifdef DEBUG_CACHE
+ printDebug();
+#endif
+
+ // Somehow this should only be called if src is a dir. But how could we know if it is?
+ // (Note that looking into itemsInUse isn't good enough. One could rename a subdir in a view.)
+ renameDir( src, dst );
+
+ // Now update the KFileItem representing that file or dir (not exclusive with the above!)
+ KURL oldurl( src );
+ oldurl.adjustPath( -1 );
+ KFileItem *fileitem = findByURL( 0, oldurl );
+ if ( fileitem )
+ {
+ if ( !fileitem->isLocalFile() && !fileitem->localPath().isEmpty() ) // it uses UDS_LOCAL_PATH? ouch, needs an update then
+ FilesChanged( src );
+ else
+ {
+ aboutToRefreshItem( fileitem );
+ fileitem->setURL( dst );
+ fileitem->refreshMimeType();
+ emitRefreshItem( fileitem );
+ }
+ }
+#ifdef DEBUG_CACHE
+ printDebug();
+#endif
+}
+
+void KDirListerCache::aboutToRefreshItem( KFileItem *fileitem )
+{
+ // Look whether this item was shown in any view, i.e. held by any dirlister
+ KURL parentDir( fileitem->url() );
+ parentDir.setPath( parentDir.directory() );
+ TQString parentDirURL = parentDir.url();
+ TQPtrList<KDirLister> *listers = urlsCurrentlyHeld[parentDirURL];
+ if ( listers )
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ kdl->aboutToRefreshItem( fileitem );
+
+ // Also look in urlsCurrentlyListed, in case the user manages to rename during a listing
+ listers = urlsCurrentlyListed[parentDirURL];
+ if ( listers )
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ kdl->aboutToRefreshItem( fileitem );
+}
+
+void KDirListerCache::emitRefreshItem( KFileItem *fileitem )
+{
+ // Look whether this item was shown in any view, i.e. held by any dirlister
+ KURL parentDir( fileitem->url() );
+ parentDir.setPath( parentDir.directory() );
+ TQString parentDirURL = parentDir.url();
+ TQPtrList<KDirLister> *listers = urlsCurrentlyHeld[parentDirURL];
+ if ( listers )
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ {
+ kdl->addRefreshItem( fileitem );
+ kdl->emitItems();
+ }
+
+ // Also look in urlsCurrentlyListed, in case the user manages to rename during a listing
+ listers = urlsCurrentlyListed[parentDirURL];
+ if ( listers )
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ {
+ kdl->addRefreshItem( fileitem );
+ kdl->emitItems();
+ }
+}
+
+KDirListerCache* KDirListerCache::self()
+{
+ if ( !s_pSelf )
+ s_pSelf = sd_KDirListerCache.setObject( s_pSelf, new KDirListerCache );
+
+ return s_pSelf;
+}
+
+bool KDirListerCache::exists()
+{
+ return s_pSelf != 0;
+}
+
+
+// private slots
+
+// _file can also be a directory being currently held!
+void KDirListerCache::slotFileDirty( const TQString& _file )
+{
+ kdDebug(7004) << k_funcinfo << _file << endl;
+
+ if ( !pendingUpdates[_file] )
+ {
+ KURL dir;
+ dir.setPath( _file );
+ if ( checkUpdate( dir.url(-1) ) )
+ updateDirectory( dir );
+
+ // the parent directory of _file
+ dir.setPath( dir.directory() );
+ if ( checkUpdate( dir.url() ) )
+ {
+ // Nice hack to save memory: use the qt object name to store the filename
+ TQTimer *timer = new TQTimer( this, _file.utf8() );
+ connect( timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotFileDirtyDelayed()) );
+ pendingUpdates.insert( _file, timer );
+ timer->start( 500, true );
+ }
+ }
+}
+
+// delayed updating of files, FAM is flooding us with events
+void KDirListerCache::slotFileDirtyDelayed()
+{
+ TQString file = TQString::fromUtf8( TQT_TQOBJECT_CONST(sender())->name() );
+
+ kdDebug(7004) << k_funcinfo << file << endl;
+
+ // TODO: do it better: don't always create/delete the TQTimer but reuse it.
+ // Delete the timer after the parent directory is removed from the cache.
+ pendingUpdates.remove( file );
+
+ KURL u;
+ u.setPath( file );
+ KFileItem *item = findByURL( 0, u ); // search all items
+ if ( item )
+ {
+ // we need to refresh the item, because e.g. the permissions can have changed.
+ aboutToRefreshItem( item );
+ item->refresh();
+ emitRefreshItem( item );
+ }
+}
+
+void KDirListerCache::slotFileCreated( const TQString& _file )
+{
+ kdDebug(7004) << k_funcinfo << _file << endl;
+ // XXX: how to avoid a complete rescan here?
+ KURL u;
+ u.setPath( _file );
+ u.setPath( u.directory() );
+ FilesAdded( u );
+}
+
+void KDirListerCache::slotFileDeleted( const TQString& _file )
+{
+ kdDebug(7004) << k_funcinfo << _file << endl;
+ KURL u;
+ u.setPath( _file );
+ FilesRemoved( u );
+}
+
+void KDirListerCache::slotEntries( TDEIO::Job *job, const TDEIO::UDSEntryList &entries )
+{
+ KURL url = joburl( static_cast<TDEIO::ListJob *>(job) );
+ url.adjustPath(-1);
+ TQString urlStr = url.url();
+
+ kdDebug(7004) << k_funcinfo << "new entries for " << url << endl;
+
+ DirItem *dir = itemsInUse[urlStr];
+ Q_ASSERT( dir );
+
+ TQPtrList<KDirLister> *listers = urlsCurrentlyListed[urlStr];
+ Q_ASSERT( listers );
+ Q_ASSERT( !listers->isEmpty() );
+
+ // check if anyone wants the mimetypes immediately
+ bool delayedMimeTypes = true;
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ delayedMimeTypes = delayedMimeTypes && kdl->d->delayedMimeTypes;
+
+ // avoid creating these QStrings again and again
+ static const TQString& dot = TDEGlobal::staticQString(".");
+ static const TQString& dotdot = TDEGlobal::staticQString("..");
+
+ TDEIO::UDSEntryListConstIterator it = entries.begin();
+ TDEIO::UDSEntryListConstIterator end = entries.end();
+
+ for ( ; it != end; ++it )
+ {
+ TQString name;
+
+ // find out about the name
+ TDEIO::UDSEntry::ConstIterator entit = (*it).begin();
+ for( ; entit != (*it).end(); ++entit )
+ if ( (*entit).m_uds == TDEIO::UDS_NAME )
+ {
+ name = (*entit).m_str;
+ break;
+ }
+
+ Q_ASSERT( !name.isEmpty() );
+ if ( name.isEmpty() )
+ continue;
+
+ if ( name == dot )
+ {
+ Q_ASSERT( !dir->rootItem );
+ dir->rootItem = new KFileItem( *it, url, delayedMimeTypes, true );
+
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ if ( !kdl->d->rootFileItem && kdl->d->url == url )
+ kdl->d->rootFileItem = dir->rootItem;
+ }
+ else if ( name != dotdot )
+ {
+ KFileItem* item = new KFileItem( *it, url, delayedMimeTypes, true );
+ Q_ASSERT( item );
+
+ //kdDebug(7004)<< "Adding item: " << item->url() << endl;
+ dir->lstItems->append( item );
+
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ kdl->addNewItem( item );
+ }
+ }
+
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ kdl->emitItems();
+}
+
+void KDirListerCache::slotResult( TDEIO::Job *j )
+{
+ Q_ASSERT( j );
+ TDEIO::ListJob *job = static_cast<TDEIO::ListJob *>( j );
+ jobs.remove( job );
+
+ KURL jobUrl = joburl( job );
+ jobUrl.adjustPath(-1); // need remove trailing slashes again, in case of redirections
+ TQString jobUrlStr = jobUrl.url();
+
+ kdDebug(7004) << k_funcinfo << "finished listing " << jobUrl << endl;
+#ifdef DEBUG_CACHE
+ printDebug();
+#endif
+
+ TQPtrList<KDirLister> *listers = urlsCurrentlyListed.take( jobUrlStr );
+ Q_ASSERT( listers );
+
+ // move the directory to the held directories, do it before emitting
+ // the signals to make sure it exists in KDirListerCache in case someone
+ // calls listDir during the signal emission
+ Q_ASSERT( !urlsCurrentlyHeld[jobUrlStr] );
+ urlsCurrentlyHeld.insert( jobUrlStr, listers );
+
+ KDirLister *kdl;
+
+ if ( job->error() )
+ {
+ for ( kdl = listers->first(); kdl; kdl = listers->next() )
+ {
+ kdl->jobDone( job );
+ kdl->handleError( job );
+ emit kdl->canceled( jobUrl );
+ if ( kdl->numJobs() == 0 )
+ {
+ kdl->d->complete = true;
+ emit kdl->canceled();
+ }
+ }
+ }
+ else
+ {
+ DirItem *dir = itemsInUse[jobUrlStr];
+ Q_ASSERT( dir );
+ dir->complete = true;
+
+ for ( kdl = listers->first(); kdl; kdl = listers->next() )
+ {
+ kdl->jobDone( job );
+ emit kdl->completed( jobUrl );
+ if ( kdl->numJobs() == 0 )
+ {
+ kdl->d->complete = true;
+ emit kdl->completed();
+ }
+ }
+ }
+
+ // TODO: hmm, if there was an error and job is a parent of one or more
+ // of the pending urls we should cancel it/them as well
+ processPendingUpdates();
+
+#ifdef DEBUG_CACHE
+ printDebug();
+#endif
+}
+
+void KDirListerCache::slotRedirection( TDEIO::Job *j, const KURL& url )
+{
+ Q_ASSERT( j );
+ TDEIO::ListJob *job = static_cast<TDEIO::ListJob *>( j );
+
+ KURL oldUrl = job->url(); // here we really need the old url!
+ KURL newUrl = url;
+
+ // strip trailing slashes
+ oldUrl.adjustPath(-1);
+ newUrl.adjustPath(-1);
+
+ if ( oldUrl == newUrl )
+ {
+ kdDebug(7004) << k_funcinfo << "New redirection url same as old, giving up." << endl;
+ return;
+ }
+
+ kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << newUrl.prettyURL() << endl;
+
+#ifdef DEBUG_CACHE
+ printDebug();
+#endif
+
+ // I don't think there can be dirItems that are childs of oldUrl.
+ // Am I wrong here? And even if so, we don't need to delete them, right?
+ // DF: redirection happens before listDir emits any item. Makes little sense otherwise.
+
+ // oldUrl cannot be in itemsCached because only completed items are moved there
+ DirItem *dir = itemsInUse.take( oldUrl.url() );
+ Q_ASSERT( dir );
+
+ TQPtrList<KDirLister> *listers = urlsCurrentlyListed.take( oldUrl.url() );
+ Q_ASSERT( listers );
+ Q_ASSERT( !listers->isEmpty() );
+
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ {
+ // TODO: put in own method?
+ if ( kdl->d->url.equals( oldUrl, true ) )
+ {
+ kdl->d->rootFileItem = 0;
+ kdl->d->url = newUrl;
+ }
+
+ *kdl->d->lstDirs.find( oldUrl ) = newUrl;
+
+ if ( kdl->d->lstDirs.count() == 1 )
+ {
+ emit kdl->clear();
+ emit kdl->redirection( newUrl );
+ emit kdl->redirection( oldUrl, newUrl );
+ }
+ else
+ {
+ emit kdl->clear( oldUrl );
+ emit kdl->redirection( oldUrl, newUrl );
+ }
+ }
+
+ // when a lister was stopped before the job emits the redirection signal, the old url will
+ // also be in urlsCurrentlyHeld
+ TQPtrList<KDirLister> *holders = urlsCurrentlyHeld.take( oldUrl.url() );
+ if ( holders )
+ {
+ Q_ASSERT( !holders->isEmpty() );
+
+ for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
+ {
+ kdl->jobStarted( job );
+
+ // do it like when starting a new list-job that will redirect later
+ emit kdl->started( oldUrl );
+
+ // TODO: maybe don't emit started if there's an update running for newUrl already?
+
+ if ( kdl->d->url.equals( oldUrl, true ) )
+ {
+ kdl->d->rootFileItem = 0;
+ kdl->d->url = newUrl;
+ }
+
+ *kdl->d->lstDirs.find( oldUrl ) = newUrl;
+
+ if ( kdl->d->lstDirs.count() == 1 )
+ {
+ emit kdl->clear();
+ emit kdl->redirection( newUrl );
+ emit kdl->redirection( oldUrl, newUrl );
+ }
+ else
+ {
+ emit kdl->clear( oldUrl );
+ emit kdl->redirection( oldUrl, newUrl );
+ }
+ }
+ }
+
+ DirItem *newDir = itemsInUse[newUrl.url()];
+ if ( newDir )
+ {
+ kdDebug(7004) << "slotRedirection: " << newUrl.url() << " already in use" << endl;
+
+ // only in this case there can newUrl already be in urlsCurrentlyListed or urlsCurrentlyHeld
+ delete dir;
+
+ // get the job if one's running for newUrl already (can be a list-job or an update-job), but
+ // do not return this 'job', which would happen because of the use of redirectionURL()
+ TDEIO::ListJob *oldJob = jobForUrl( newUrl.url(), job );
+
+ // listers of newUrl with oldJob: forget about the oldJob and use the already running one
+ // which will be converted to an updateJob
+ TQPtrList<KDirLister> *curListers = urlsCurrentlyListed[newUrl.url()];
+ if ( curListers )
+ {
+ kdDebug(7004) << "slotRedirection: and it is currently listed" << endl;
+
+ Q_ASSERT( oldJob ); // ?!
+
+ for ( KDirLister *kdl = curListers->first(); kdl; kdl = curListers->next() ) // listers of newUrl
+ {
+ kdl->jobDone( oldJob );
+
+ kdl->jobStarted( job );
+ kdl->connectJob( job );
+ }
+
+ // append listers of oldUrl with newJob to listers of newUrl with oldJob
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ curListers->append( kdl );
+ }
+ else
+ urlsCurrentlyListed.insert( newUrl.url(), listers );
+
+ if ( oldJob ) // kill the old job, be it a list-job or an update-job
+ killJob( oldJob );
+
+ // holders of newUrl: use the already running job which will be converted to an updateJob
+ TQPtrList<KDirLister> *curHolders = urlsCurrentlyHeld[newUrl.url()];
+ if ( curHolders )
+ {
+ kdDebug(7004) << "slotRedirection: and it is currently held." << endl;
+
+ for ( KDirLister *kdl = curHolders->first(); kdl; kdl = curHolders->next() ) // holders of newUrl
+ {
+ kdl->jobStarted( job );
+ emit kdl->started( newUrl );
+ }
+
+ // append holders of oldUrl to holders of newUrl
+ if ( holders )
+ for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
+ curHolders->append( kdl );
+ }
+ else if ( holders )
+ urlsCurrentlyHeld.insert( newUrl.url(), holders );
+
+
+ // emit old items: listers, holders. NOT: newUrlListers/newUrlHolders, they already have them listed
+ // TODO: make this a separate method?
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ {
+ if ( !kdl->d->rootFileItem && kdl->d->url == newUrl )
+ kdl->d->rootFileItem = newDir->rootItem;
+
+ kdl->addNewItems( *(newDir->lstItems) );
+ kdl->emitItems();
+ }
+
+ if ( holders )
+ {
+ for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
+ {
+ if ( !kdl->d->rootFileItem && kdl->d->url == newUrl )
+ kdl->d->rootFileItem = newDir->rootItem;
+
+ kdl->addNewItems( *(newDir->lstItems) );
+ kdl->emitItems();
+ }
+ }
+ }
+ else if ( (newDir = itemsCached.take( newUrl.url() )) )
+ {
+ kdDebug(7004) << "slotRedirection: " << newUrl.url() << " is unused, but already in the cache." << endl;
+
+ delete dir;
+ itemsInUse.insert( newUrl.url(), newDir );
+ urlsCurrentlyListed.insert( newUrl.url(), listers );
+ if ( holders )
+ urlsCurrentlyHeld.insert( newUrl.url(), holders );
+
+ // emit old items: listers, holders
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ {
+ if ( !kdl->d->rootFileItem && kdl->d->url == newUrl )
+ kdl->d->rootFileItem = newDir->rootItem;
+
+ kdl->addNewItems( *(newDir->lstItems) );
+ kdl->emitItems();
+ }
+
+ if ( holders )
+ {
+ for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
+ {
+ if ( !kdl->d->rootFileItem && kdl->d->url == newUrl )
+ kdl->d->rootFileItem = newDir->rootItem;
+
+ kdl->addNewItems( *(newDir->lstItems) );
+ kdl->emitItems();
+ }
+ }
+ }
+ else
+ {
+ kdDebug(7004) << "slotRedirection: " << newUrl.url() << " has not been listed yet." << endl;
+
+ delete dir->rootItem;
+ dir->rootItem = 0;
+ dir->lstItems->clear();
+ dir->redirect( newUrl );
+ itemsInUse.insert( newUrl.url(), dir );
+ urlsCurrentlyListed.insert( newUrl.url(), listers );
+
+ if ( holders )
+ urlsCurrentlyHeld.insert( newUrl.url(), holders );
+ else
+ {
+#ifdef DEBUG_CACHE
+ printDebug();
+#endif
+ return; // only in this case the job doesn't need to be converted,
+ }
+ }
+
+ // make the job an update job
+ job->disconnect( this );
+
+ connect( job, TQT_SIGNAL(entries( TDEIO::Job *, const TDEIO::UDSEntryList & )),
+ this, TQT_SLOT(slotUpdateEntries( TDEIO::Job *, const TDEIO::UDSEntryList & )) );
+ connect( job, TQT_SIGNAL(result( TDEIO::Job * )),
+ this, TQT_SLOT(slotUpdateResult( TDEIO::Job * )) );
+
+ // FIXME: autoUpdate-Counts!!
+
+#ifdef DEBUG_CACHE
+ printDebug();
+#endif
+}
+
+void KDirListerCache::renameDir( const KURL &oldUrl, const KURL &newUrl )
+{
+ kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << newUrl.prettyURL() << endl;
+ TQString oldUrlStr = oldUrl.url(-1);
+ TQString newUrlStr = newUrl.url(-1);
+
+ // Not enough. Also need to look at any child dir, even sub-sub-sub-dir.
+ //DirItem *dir = itemsInUse.take( oldUrlStr );
+ //emitRedirections( oldUrl, url );
+
+ // Look at all dirs being listed/shown
+ TQDictIterator<DirItem> itu( itemsInUse );
+ bool goNext;
+ while ( itu.current() )
+ {
+ goNext = true;
+ DirItem *dir = itu.current();
+ KURL oldDirUrl ( itu.currentKey() );
+ //kdDebug(7004) << "itemInUse: " << oldDirUrl.prettyURL() << endl;
+ // Check if this dir is oldUrl, or a subfolder of it
+ if ( oldUrl.isParentOf( oldDirUrl ) )
+ {
+ // TODO should use KURL::cleanpath like isParentOf does
+ TQString relPath = oldDirUrl.path().mid( oldUrl.path().length() );
+
+ KURL newDirUrl( newUrl ); // take new base
+ if ( !relPath.isEmpty() )
+ newDirUrl.addPath( relPath ); // add unchanged relative path
+ //kdDebug(7004) << "KDirListerCache::renameDir new url=" << newDirUrl.prettyURL() << endl;
+
+ // Update URL in dir item and in itemsInUse
+ dir->redirect( newDirUrl );
+ itemsInUse.remove( itu.currentKey() ); // implies ++itu
+ itemsInUse.insert( newDirUrl.url(-1), dir );
+ goNext = false; // because of the implied ++itu above
+ if ( dir->lstItems )
+ {
+ // Rename all items under that dir
+ KFileItemListIterator kit( *dir->lstItems );
+ for ( ; kit.current(); ++kit )
+ {
+ KURL oldItemUrl = (*kit)->url();
+ TQString oldItemUrlStr( oldItemUrl.url(-1) );
+ KURL newItemUrl( oldItemUrl );
+ newItemUrl.setPath( newDirUrl.path() );
+ newItemUrl.addPath( oldItemUrl.fileName() );
+ kdDebug(7004) << "KDirListerCache::renameDir renaming " << oldItemUrlStr << " to " << newItemUrl.url() << endl;
+ (*kit)->setURL( newItemUrl );
+ }
+ }
+ emitRedirections( oldDirUrl, newDirUrl );
+ }
+ if ( goNext )
+ ++itu;
+ }
+
+ // Is oldUrl a directory in the cache?
+ // Remove any child of oldUrl from the cache - even if the renamed dir itself isn't in it!
+ removeDirFromCache( oldUrl );
+ // TODO rename, instead.
+}
+
+void KDirListerCache::emitRedirections( const KURL &oldUrl, const KURL &url )
+{
+ kdDebug(7004) << k_funcinfo << oldUrl.prettyURL() << " -> " << url.prettyURL() << endl;
+ TQString oldUrlStr = oldUrl.url(-1);
+ TQString urlStr = url.url(-1);
+
+ TDEIO::ListJob *job = jobForUrl( oldUrlStr );
+ if ( job )
+ killJob( job );
+
+ // Check if we were listing this dir. Need to abort and restart with new name in that case.
+ TQPtrList<KDirLister> *listers = urlsCurrentlyListed.take( oldUrlStr );
+ if ( listers )
+ {
+ // Tell the world that the job listing the old url is dead.
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ {
+ if ( job )
+ kdl->jobDone( job );
+
+ emit kdl->canceled( oldUrl );
+ }
+
+ urlsCurrentlyListed.insert( urlStr, listers );
+ }
+
+ // Check if we are currently displaying this directory (odds opposite wrt above)
+ // Update urlsCurrentlyHeld dict with new URL
+ TQPtrList<KDirLister> *holders = urlsCurrentlyHeld.take( oldUrlStr );
+ if ( holders )
+ {
+ if ( job )
+ for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
+ kdl->jobDone( job );
+
+ urlsCurrentlyHeld.insert( urlStr, holders );
+ }
+
+ if ( listers )
+ {
+ updateDirectory( url );
+
+ // Tell the world about the new url
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ emit kdl->started( url );
+ }
+
+ if ( holders )
+ {
+ // And notify the dirlisters of the redirection
+ for ( KDirLister *kdl = holders->first(); kdl; kdl = holders->next() )
+ {
+ *kdl->d->lstDirs.find( oldUrl ) = url;
+
+ if ( kdl->d->lstDirs.count() == 1 )
+ emit kdl->redirection( url );
+
+ emit kdl->redirection( oldUrl, url );
+ }
+ }
+}
+
+void KDirListerCache::removeDirFromCache( const KURL& dir )
+{
+ kdDebug(7004) << "KDirListerCache::removeDirFromCache " << dir.prettyURL() << endl;
+ TQCacheIterator<DirItem> itc( itemsCached );
+ while ( itc.current() )
+ {
+ if ( dir.isParentOf( KURL( itc.currentKey() ) ) )
+ itemsCached.remove( itc.currentKey() );
+ else
+ ++itc;
+ }
+}
+
+void KDirListerCache::slotUpdateEntries( TDEIO::Job* job, const TDEIO::UDSEntryList& list )
+{
+ jobs[static_cast<TDEIO::ListJob*>(job)] += list;
+}
+
+void KDirListerCache::slotUpdateResult( TDEIO::Job * j )
+{
+ Q_ASSERT( j );
+ TDEIO::ListJob *job = static_cast<TDEIO::ListJob *>( j );
+
+ KURL jobUrl = joburl( job );
+ jobUrl.adjustPath(-1); // need remove trailing slashes again, in case of redirections
+ TQString jobUrlStr = jobUrl.url();
+
+ kdDebug(7004) << k_funcinfo << "finished update " << jobUrl << endl;
+
+ KDirLister *kdl;
+
+ TQPtrList<KDirLister> *listers = urlsCurrentlyHeld[jobUrlStr];
+ TQPtrList<KDirLister> *tmpLst = urlsCurrentlyListed.take( jobUrlStr );
+
+ if ( tmpLst )
+ {
+ if ( listers )
+ for ( kdl = tmpLst->first(); kdl; kdl = tmpLst->next() )
+ {
+ Q_ASSERT( listers->containsRef( kdl ) == 0 );
+ listers->append( kdl );
+ }
+ else
+ {
+ listers = tmpLst;
+ urlsCurrentlyHeld.insert( jobUrlStr, listers );
+ }
+ }
+
+ // once we are updating dirs that are only in the cache this will fail!
+ Q_ASSERT( listers );
+
+ if ( job->error() )
+ {
+ for ( kdl = listers->first(); kdl; kdl = listers->next() )
+ {
+ kdl->jobDone( job );
+
+ //don't bother the user
+ //kdl->handleError( job );
+
+ emit kdl->canceled( jobUrl );
+ if ( kdl->numJobs() == 0 )
+ {
+ kdl->d->complete = true;
+ emit kdl->canceled();
+ }
+ }
+
+ jobs.remove( job );
+
+ // TODO: if job is a parent of one or more
+ // of the pending urls we should cancel them
+ processPendingUpdates();
+ return;
+ }
+
+ DirItem *dir = itemsInUse[jobUrlStr];
+ dir->complete = true;
+
+
+ // check if anyone wants the mimetypes immediately
+ bool delayedMimeTypes = true;
+ for ( kdl = listers->first(); kdl; kdl = listers->next() )
+ delayedMimeTypes = delayedMimeTypes && kdl->d->delayedMimeTypes;
+
+ // should be enough to get reasonable speed in most cases
+ TQDict<KFileItem> fileItems( 9973 );
+
+ KFileItemListIterator kit ( *(dir->lstItems) );
+
+ // Unmark all items in url
+ for ( ; kit.current(); ++kit )
+ {
+ (*kit)->unmark();
+ fileItems.insert( (*kit)->url().url(), *kit );
+ }
+
+ static const TQString& dot = TDEGlobal::staticQString(".");
+ static const TQString& dotdot = TDEGlobal::staticQString("..");
+
+ KFileItem *item = 0, *tmp;
+
+ TQValueList<TDEIO::UDSEntry> buf = jobs[job];
+ TQValueListIterator<TDEIO::UDSEntry> it = buf.begin();
+ for ( ; it != buf.end(); ++it )
+ {
+ // Form the complete url
+ if ( !item )
+ item = new KFileItem( *it, jobUrl, delayedMimeTypes, true );
+ else
+ item->setUDSEntry( *it, jobUrl, delayedMimeTypes, true );
+
+ // Find out about the name
+ TQString name = item->name();
+ Q_ASSERT( !name.isEmpty() );
+
+ // we duplicate the check for dotdot here, to avoid iterating over
+ // all items again and checking in matchesFilter() that way.
+ if ( name.isEmpty() || name == dotdot )
+ continue;
+
+ if ( name == dot )
+ {
+ // if the update was started before finishing the original listing
+ // there is no root item yet
+ if ( !dir->rootItem )
+ {
+ dir->rootItem = item;
+ item = 0;
+
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ if ( !kdl->d->rootFileItem && kdl->d->url == jobUrl )
+ kdl->d->rootFileItem = dir->rootItem;
+ }
+
+ continue;
+ }
+
+ // Find this item
+ if ( (tmp = fileItems[item->url().url()]) )
+ {
+ tmp->mark();
+
+ // check if something changed for this file
+ if ( !tmp->cmp( *item ) )
+ {
+ for ( kdl = listers->first(); kdl; kdl = listers->next() )
+ kdl->aboutToRefreshItem( tmp );
+
+ //kdDebug(7004) << "slotUpdateResult: file changed: " << tmp->name() << endl;
+ tmp->assign( *item );
+
+ for ( kdl = listers->first(); kdl; kdl = listers->next() )
+ kdl->addRefreshItem( tmp );
+ }
+ }
+ else // this is a new file
+ {
+ //kdDebug(7004) << "slotUpdateResult: new file: " << name << endl;
+
+ item->mark();
+ dir->lstItems->append( item );
+
+ for ( kdl = listers->first(); kdl; kdl = listers->next() )
+ kdl->addNewItem( item );
+
+ // item used, we need a new one for the next iteration
+ item = 0;
+ }
+ }
+
+ if ( item )
+ delete item;
+
+ jobs.remove( job );
+
+ deleteUnmarkedItems( listers, dir->lstItems );
+
+ for ( kdl = listers->first(); kdl; kdl = listers->next() )
+ {
+ kdl->emitItems();
+
+ kdl->jobDone( job );
+
+ emit kdl->completed( jobUrl );
+ if ( kdl->numJobs() == 0 )
+ {
+ kdl->d->complete = true;
+ emit kdl->completed();
+ }
+ }
+
+ // TODO: hmm, if there was an error and job is a parent of one or more
+ // of the pending urls we should cancel it/them as well
+ processPendingUpdates();
+}
+
+// private
+
+TDEIO::ListJob *KDirListerCache::jobForUrl( const TQString& url, TDEIO::ListJob *not_job )
+{
+ TDEIO::ListJob *job;
+ TQMap< TDEIO::ListJob *, TQValueList<TDEIO::UDSEntry> >::Iterator it = jobs.begin();
+ while ( it != jobs.end() )
+ {
+ job = it.key();
+ if ( joburl( job ).url(-1) == url && job != not_job )
+ return job;
+ ++it;
+ }
+ return 0;
+}
+
+const KURL& KDirListerCache::joburl( TDEIO::ListJob *job )
+{
+ if ( job->redirectionURL().isValid() )
+ return job->redirectionURL();
+ else
+ return job->url();
+}
+
+void KDirListerCache::killJob( TDEIO::ListJob *job )
+{
+ jobs.remove( job );
+ job->disconnect( this );
+ job->kill();
+}
+
+void KDirListerCache::deleteUnmarkedItems( TQPtrList<KDirLister> *listers, KFileItemList *lstItems )
+{
+ // Find all unmarked items and delete them
+ KFileItem* item;
+ lstItems->first();
+ while ( (item = lstItems->current()) )
+ if ( !item->isMarked() )
+ {
+ //kdDebug() << k_funcinfo << item->name() << endl;
+ for ( KDirLister *kdl = listers->first(); kdl; kdl = listers->next() )
+ kdl->emitDeleteItem( item );
+
+ if ( item->isDir() )
+ deleteDir( item->url() );
+
+ // finally actually delete the item
+ lstItems->take();
+ delete item;
+ }
+ else
+ lstItems->next();
+}
+
+void KDirListerCache::deleteDir( const KURL& dirUrl )
+{
+ //kdDebug() << k_funcinfo << dirUrl.prettyURL() << endl;
+ // unregister and remove the childs of the deleted item.
+ // Idea: tell all the KDirListers that they should forget the dir
+ // and then remove it from the cache.
+
+ TQDictIterator<DirItem> itu( itemsInUse );
+ while ( itu.current() )
+ {
+ KURL deletedUrl( itu.currentKey() );
+ if ( dirUrl.isParentOf( deletedUrl ) )
+ {
+ // stop all jobs for deletedUrl
+
+ TQPtrList<KDirLister> *kdls = urlsCurrentlyListed[deletedUrl.url()];
+ if ( kdls ) // yeah, I lack good names
+ {
+ // we need a copy because stop modifies the list
+ kdls = new TQPtrList<KDirLister>( *kdls );
+ for ( KDirLister *kdl = kdls->first(); kdl; kdl = kdls->next() )
+ stop( kdl, deletedUrl );
+
+ delete kdls;
+ }
+
+ // tell listers holding deletedUrl to forget about it
+ // this will stop running updates for deletedUrl as well
+
+ kdls = urlsCurrentlyHeld[deletedUrl.url()];
+ if ( kdls )
+ {
+ // we need a copy because forgetDirs modifies the list
+ kdls = new TQPtrList<KDirLister>( *kdls );
+
+ for ( KDirLister *kdl = kdls->first(); kdl; kdl = kdls->next() )
+ {
+ // lister's root is the deleted item
+ if ( kdl->d->url == deletedUrl )
+ {
+ // tell the view first. It might need the subdirs' items (which forgetDirs will delete)
+ if ( kdl->d->rootFileItem )
+ emit kdl->deleteItem( kdl->d->rootFileItem );
+ forgetDirs( kdl );
+ kdl->d->rootFileItem = 0;
+ }
+ else
+ {
+ bool treeview = kdl->d->lstDirs.count() > 1;
+ if ( !treeview )
+ emit kdl->clear();
+
+ forgetDirs( kdl, deletedUrl, treeview );
+ }
+ }
+
+ delete kdls;
+ }
+
+ // delete the entry for deletedUrl - should not be needed, it's in
+ // items cached now
+
+ DirItem *dir = itemsInUse.take( deletedUrl.url() );
+ Q_ASSERT( !dir );
+ if ( !dir ) // take didn't find it - move on
+ ++itu;
+ }
+ else
+ ++itu;
+ }
+
+ // remove the children from the cache
+ removeDirFromCache( dirUrl );
+}
+
+void KDirListerCache::processPendingUpdates()
+{
+ // TODO
+}
+
+#ifndef NDEBUG
+void KDirListerCache::printDebug()
+{
+ kdDebug(7004) << "Items in use: " << endl;
+ TQDictIterator<DirItem> itu( itemsInUse );
+ for ( ; itu.current() ; ++itu ) {
+ kdDebug(7004) << " " << itu.currentKey() << " URL: " << itu.current()->url
+ << " rootItem: " << ( itu.current()->rootItem ? itu.current()->rootItem->url() : KURL() )
+ << " autoUpdates refcount: " << itu.current()->autoUpdates
+ << " complete: " << itu.current()->complete
+ << ( itu.current()->lstItems ? TQString(" with %1 items.").arg(itu.current()->lstItems->count()) : TQString(" lstItems=NULL") ) << endl;
+ }
+
+ kdDebug(7004) << "urlsCurrentlyHeld: " << endl;
+ TQDictIterator< TQPtrList<KDirLister> > it( urlsCurrentlyHeld );
+ for ( ; it.current() ; ++it )
+ {
+ TQString list;
+ for ( TQPtrListIterator<KDirLister> listit( *it.current() ); listit.current(); ++listit )
+ list += " 0x" + TQString::number( (long)listit.current(), 16 );
+ kdDebug(7004) << " " << it.currentKey() << " " << it.current()->count() << " listers: " << list << endl;
+ }
+
+ kdDebug(7004) << "urlsCurrentlyListed: " << endl;
+ TQDictIterator< TQPtrList<KDirLister> > it2( urlsCurrentlyListed );
+ for ( ; it2.current() ; ++it2 )
+ {
+ TQString list;
+ for ( TQPtrListIterator<KDirLister> listit( *it2.current() ); listit.current(); ++listit )
+ list += " 0x" + TQString::number( (long)listit.current(), 16 );
+ kdDebug(7004) << " " << it2.currentKey() << " " << it2.current()->count() << " listers: " << list << endl;
+ }
+
+ TQMap< TDEIO::ListJob *, TQValueList<TDEIO::UDSEntry> >::Iterator jit = jobs.begin();
+ kdDebug(7004) << "Jobs: " << endl;
+ for ( ; jit != jobs.end() ; ++jit )
+ kdDebug(7004) << " " << jit.key() << " listing " << joburl( jit.key() ).prettyURL() << ": " << (*jit).count() << " entries." << endl;
+
+ kdDebug(7004) << "Items in cache: " << endl;
+ TQCacheIterator<DirItem> itc( itemsCached );
+ for ( ; itc.current() ; ++itc )
+ kdDebug(7004) << " " << itc.currentKey() << " rootItem: "
+ << ( itc.current()->rootItem ? itc.current()->rootItem->url().prettyURL() : TQString("NULL") )
+ << ( itc.current()->lstItems ? TQString(" with %1 items.").arg(itc.current()->lstItems->count()) : TQString(" lstItems=NULL") ) << endl;
+}
+#endif
+
+/*********************** -- The new KDirLister -- ************************/
+
+
+KDirLister::KDirLister( bool _delayedMimeTypes )
+{
+ kdDebug(7003) << "+KDirLister" << endl;
+
+ d = new KDirListerPrivate;
+
+ d->complete = true;
+ d->delayedMimeTypes = _delayedMimeTypes;
+
+ setAutoUpdate( true );
+ setDirOnlyMode( false );
+ setShowingDotFiles( false );
+
+ setAutoErrorHandlingEnabled( true, 0 );
+}
+
+KDirLister::~KDirLister()
+{
+ kdDebug(7003) << "-KDirLister" << endl;
+
+ if ( KDirListerCache::exists() )
+ {
+ // Stop all running jobs
+ stop();
+ s_pCache->forgetDirs( this );
+ }
+
+ delete d;
+}
+
+bool KDirLister::openURL( const KURL& _url, bool _keep, bool _reload )
+{
+ kdDebug(7003) << k_funcinfo << _url.prettyURL()
+ << " keep=" << _keep << " reload=" << _reload << endl;
+
+ // emit the current changes made to avoid an inconsistent treeview
+ if ( d->changes != NONE && _keep )
+ emitChanges();
+
+ d->changes = NONE;
+
+ // If a local path is available, monitor that instead of the given remote URL...
+ KURL realURL = _url;
+ if (!realURL.isLocalFile()) {
+ TDEIO::LocalURLJob* localURLJob = TDEIO::localURL(_url);
+ if (localURLJob) {
+ connect(localURLJob, TQT_SIGNAL(localURL(TDEIO::Job*, const KURL&, bool)), this, TQT_SLOT(slotLocalURL(TDEIO::Job*, const KURL&, bool)));
+ connect(localURLJob, TQT_SIGNAL(destroyed()), this, TQT_SLOT(slotLocalURLKIODestroyed()));
+ d->localURLSlotFired = false;
+ while (!d->localURLSlotFired) {
+ tqApp->eventLoop()->processEvents(TQEventLoop::ExcludeUserInput);
+ usleep(1000);
+ }
+ if (d->localURLResultIsLocal) {
+ realURL = d->localURLResultURL;
+ }
+ }
+ }
+
+ return s_pCache->listDir( this, realURL, _keep, _reload );
+}
+
+void KDirLister::slotLocalURL(TDEIO::Job *job, const KURL& url, bool isLocal) {
+ d->localURLSlotFired = true;
+ d->localURLResultURL = url;
+ d->localURLResultIsLocal = isLocal;
+}
+
+void KDirLister::slotLocalURLKIODestroyed() {
+ if (!d->localURLSlotFired) {
+ d->localURLSlotFired = true;
+ d->localURLResultURL = KURL();
+ d->localURLResultIsLocal = false;
+ }
+}
+
+void KDirLister::stop()
+{
+ kdDebug(7003) << k_funcinfo << endl;
+ s_pCache->stop( this );
+}
+
+void KDirLister::stop( const KURL& _url )
+{
+ kdDebug(7003) << k_funcinfo << _url.prettyURL() << endl;
+ s_pCache->stop( this, _url );
+}
+
+bool KDirLister::autoUpdate() const
+{
+ return d->autoUpdate;
+}
+
+void KDirLister::setAutoUpdate( bool _enable )
+{
+ if ( d->autoUpdate == _enable )
+ return;
+
+ d->autoUpdate = _enable;
+ s_pCache->setAutoUpdate( this, _enable );
+}
+
+bool KDirLister::showingDotFiles() const
+{
+ return d->isShowingDotFiles;
+}
+
+void KDirLister::setShowingDotFiles( bool _showDotFiles )
+{
+ if ( d->isShowingDotFiles == _showDotFiles )
+ return;
+
+ d->isShowingDotFiles = _showDotFiles;
+ d->changes ^= DOT_FILES;
+}
+
+bool KDirLister::dirOnlyMode() const
+{
+ return d->dirOnlyMode;
+}
+
+void KDirLister::setDirOnlyMode( bool _dirsOnly )
+{
+ if ( d->dirOnlyMode == _dirsOnly )
+ return;
+
+ d->dirOnlyMode = _dirsOnly;
+ d->changes ^= DIR_ONLY_MODE;
+}
+
+bool KDirLister::autoErrorHandlingEnabled() const
+{
+ return d->autoErrorHandling;
+}
+
+void KDirLister::setAutoErrorHandlingEnabled( bool enable, TQWidget* parent )
+{
+ d->autoErrorHandling = enable;
+ d->errorParent = parent;
+}
+
+const KURL& KDirLister::url() const
+{
+ return d->url;
+}
+
+const KURL::List& KDirLister::directories() const
+{
+ return d->lstDirs;
+}
+
+void KDirLister::emitChanges()
+{
+ if ( d->changes == NONE )
+ return;
+
+ static const TQString& dot = TDEGlobal::staticQString(".");
+ static const TQString& dotdot = TDEGlobal::staticQString("..");
+
+ for ( KURL::List::Iterator it = d->lstDirs.begin();
+ it != d->lstDirs.end(); ++it )
+ {
+ KFileItemListIterator kit( *s_pCache->itemsForDir( *it ) );
+ for ( ; kit.current(); ++kit )
+ {
+ if ( (*kit)->text() == dot || (*kit)->text() == dotdot )
+ continue;
+
+ bool oldMime = true, newMime = true;
+
+ if ( d->changes & MIME_FILTER )
+ {
+ oldMime = doMimeFilter( (*kit)->mimetype(), d->oldMimeFilter )
+ && doMimeExcludeFilter( (*kit)->mimetype(), d->oldMimeExcludeFilter );
+ newMime = doMimeFilter( (*kit)->mimetype(), d->mimeFilter )
+ && doMimeExcludeFilter( (*kit)->mimetype(), d->mimeExcludeFilter );
+
+ if ( oldMime && !newMime )
+ {
+ emit deleteItem( *kit );
+ continue;
+ }
+ }
+
+ if ( d->changes & DIR_ONLY_MODE )
+ {
+ // the lister switched to dirOnlyMode
+ if ( d->dirOnlyMode )
+ {
+ if ( !(*kit)->isDir() )
+ emit deleteItem( *kit );
+ }
+ else if ( !(*kit)->isDir() )
+ addNewItem( *kit );
+
+ continue;
+ }
+
+ if ( (*kit)->isHidden() )
+ {
+ if ( d->changes & DOT_FILES )
+ {
+ // the lister switched to dot files mode
+ if ( d->isShowingDotFiles )
+ addNewItem( *kit );
+ else
+ emit deleteItem( *kit );
+
+ continue;
+ }
+ }
+ else if ( d->changes & NAME_FILTER )
+ {
+ bool oldName = (*kit)->isDir() ||
+ d->oldFilters.isEmpty() ||
+ doNameFilter( (*kit)->text(), d->oldFilters );
+
+ bool newName = (*kit)->isDir() ||
+ d->lstFilters.isEmpty() ||
+ doNameFilter( (*kit)->text(), d->lstFilters );
+
+ if ( oldName && !newName )
+ {
+ emit deleteItem( *kit );
+ continue;
+ }
+ else if ( !oldName && newName )
+ addNewItem( *kit );
+ }
+
+ if ( (d->changes & MIME_FILTER) && !oldMime && newMime )
+ addNewItem( *kit );
+ }
+
+ emitItems();
+ }
+
+ d->changes = NONE;
+}
+
+void KDirLister::updateDirectory( const KURL& _u )
+{
+ s_pCache->updateDirectory( _u );
+}
+
+bool KDirLister::isFinished() const
+{
+ return d->complete;
+}
+
+KFileItem *KDirLister::rootItem() const
+{
+ return d->rootFileItem;
+}
+
+KFileItem *KDirLister::findByURL( const KURL& _url ) const
+{
+ return s_pCache->findByURL( this, _url );
+}
+
+KFileItem *KDirLister::findByName( const TQString& _name ) const
+{
+ return s_pCache->findByName( this, _name );
+}
+
+#ifndef KDE_NO_COMPAT
+KFileItem *KDirLister::find( const KURL& _url ) const
+{
+ return findByURL( _url );
+}
+#endif
+
+
+// ================ public filter methods ================ //
+
+void KDirLister::setNameFilter( const TQString& nameFilter )
+{
+ if ( !(d->changes & NAME_FILTER) )
+ {
+ d->oldFilters = d->lstFilters;
+ d->lstFilters.setAutoDelete( false );
+ }
+
+ d->lstFilters.clear();
+ d->lstFilters.setAutoDelete( true );
+
+ d->nameFilter = nameFilter;
+
+ // Split on white space
+ TQStringList list = TQStringList::split( ' ', nameFilter );
+ for ( TQStringList::Iterator it = list.begin(); it != list.end(); ++it )
+ d->lstFilters.append( new TQRegExp(*it, false, true ) );
+
+ d->changes |= NAME_FILTER;
+}
+
+const TQString& KDirLister::nameFilter() const
+{
+ return d->nameFilter;
+}
+
+void KDirLister::setMimeFilter( const TQStringList& mimeFilter )
+{
+ if ( !(d->changes & MIME_FILTER) )
+ d->oldMimeFilter = d->mimeFilter;
+
+ if ( mimeFilter.find("all/allfiles") != mimeFilter.end() ||
+ mimeFilter.find("all/all") != mimeFilter.end() )
+ d->mimeFilter.clear();
+ else
+ d->mimeFilter = mimeFilter;
+
+ d->changes |= MIME_FILTER;
+}
+
+void KDirLister::setMimeExcludeFilter( const TQStringList& mimeExcludeFilter )
+{
+ if ( !(d->changes & MIME_FILTER) )
+ d->oldMimeExcludeFilter = d->mimeExcludeFilter;
+
+ d->mimeExcludeFilter = mimeExcludeFilter;
+ d->changes |= MIME_FILTER;
+}
+
+
+void KDirLister::clearMimeFilter()
+{
+ if ( !(d->changes & MIME_FILTER) )
+ {
+ d->oldMimeFilter = d->mimeFilter;
+ d->oldMimeExcludeFilter = d->mimeExcludeFilter;
+ }
+ d->mimeFilter.clear();
+ d->mimeExcludeFilter.clear();
+ d->changes |= MIME_FILTER;
+}
+
+const TQStringList& KDirLister::mimeFilters() const
+{
+ return d->mimeFilter;
+}
+
+bool KDirLister::matchesFilter( const TQString& name ) const
+{
+ return doNameFilter( name, d->lstFilters );
+}
+
+bool KDirLister::matchesMimeFilter( const TQString& mime ) const
+{
+ return doMimeFilter( mime, d->mimeFilter ) && doMimeExcludeFilter(mime,d->mimeExcludeFilter);
+}
+
+// ================ protected methods ================ //
+
+bool KDirLister::matchesFilter( const KFileItem *item ) const
+{
+ Q_ASSERT( item );
+ static const TQString& dotdot = TDEGlobal::staticQString("..");
+
+ if ( item->text() == dotdot )
+ return false;
+
+ if ( !d->isShowingDotFiles && item->isHidden() )
+ return false;
+
+ if ( item->isDir() || d->lstFilters.isEmpty() )
+ return true;
+
+ return matchesFilter( item->text() );
+}
+
+bool KDirLister::matchesMimeFilter( const KFileItem *item ) const
+{
+ Q_ASSERT( item );
+ // Don't lose time determining the mimetype if there is no filter
+ if ( d->mimeFilter.isEmpty() && d->mimeExcludeFilter.isEmpty() )
+ return true;
+ return matchesMimeFilter( item->mimetype() );
+}
+
+bool KDirLister::doNameFilter( const TQString& name, const TQPtrList<TQRegExp>& filters ) const
+{
+ for ( TQPtrListIterator<TQRegExp> it( filters ); it.current(); ++it )
+ if ( it.current()->exactMatch( name ) )
+ return true;
+
+ return false;
+}
+
+bool KDirLister::doMimeFilter( const TQString& mime, const TQStringList& filters ) const
+{
+ if ( filters.isEmpty() )
+ return true;
+
+ KMimeType::Ptr mimeptr = KMimeType::mimeType(mime);
+ //kdDebug(7004) << "doMimeFilter: investigating: "<<mimeptr->name()<<endl;
+ TQStringList::ConstIterator it = filters.begin();
+ for ( ; it != filters.end(); ++it )
+ if ( mimeptr->is(*it) )
+ return true;
+ //else kdDebug(7004) << "doMimeFilter: compared without result to "<<*it<<endl;
+
+
+ return false;
+}
+
+bool KDirLister::doMimeExcludeFilter( const TQString& mime, const TQStringList& filters ) const
+{
+ if ( filters.isEmpty() )
+ return true;
+
+ TQStringList::ConstIterator it = filters.begin();
+ for ( ; it != filters.end(); ++it )
+ if ( (*it) == mime )
+ return false;
+
+ return true;
+}
+
+
+bool KDirLister::validURL( const KURL& _url ) const
+{
+ return s_pCache->validURL( this, _url );
+}
+
+void KDirLister::handleError( TDEIO::Job *job )
+{
+ if ( d->autoErrorHandling )
+ job->showErrorDialog( d->errorParent );
+}
+
+
+// ================= private methods ================= //
+
+void KDirLister::addNewItem( const KFileItem *item )
+{
+ if ( ( d->dirOnlyMode && !item->isDir() ) || !matchesFilter( item ) )
+ return; // No reason to continue... bailing out here prevents a mimetype scan.
+
+ if ( matchesMimeFilter( item ) )
+ {
+ if ( !d->lstNewItems )
+ d->lstNewItems = new KFileItemList;
+
+ d->lstNewItems->append( item ); // items not filtered
+ }
+ else
+ {
+ if ( !d->lstMimeFilteredItems )
+ d->lstMimeFilteredItems = new KFileItemList;
+
+ d->lstMimeFilteredItems->append( item ); // only filtered by mime
+ }
+}
+
+void KDirLister::addNewItems( const KFileItemList& items )
+{
+ // TODO: make this faster - test if we have a filter at all first
+ // DF: was this profiled? The matchesFoo() functions should be fast, w/o filters...
+ // Of course if there is no filter and we can do a range-insertion instead of a loop, that might be good.
+ // But that's for Qt4, not possible with TQPtrList.
+ for ( KFileItemListIterator kit( items ); kit.current(); ++kit )
+ addNewItem( *kit );
+}
+
+void KDirLister::aboutToRefreshItem( const KFileItem *item )
+{
+ // The code here follows the logic in addNewItem
+ if ( ( d->dirOnlyMode && !item->isDir() ) || !matchesFilter( item ) )
+ d->refreshItemWasFiltered = true;
+ else if ( !matchesMimeFilter( item ) )
+ d->refreshItemWasFiltered = true;
+ else
+ d->refreshItemWasFiltered = false;
+}
+
+void KDirLister::addRefreshItem( const KFileItem *item )
+{
+ bool isExcluded = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
+
+ if ( !isExcluded && matchesMimeFilter( item ) )
+ {
+ if ( d->refreshItemWasFiltered )
+ {
+ if ( !d->lstNewItems )
+ d->lstNewItems = new KFileItemList;
+
+ d->lstNewItems->append( item );
+ }
+ else
+ {
+ if ( !d->lstRefreshItems )
+ d->lstRefreshItems = new KFileItemList;
+
+ d->lstRefreshItems->append( item );
+ }
+ }
+ else if ( !d->refreshItemWasFiltered )
+ {
+ if ( !d->lstRemoveItems )
+ d->lstRemoveItems = new KFileItemList;
+
+ // notify the user that the mimetype of a file changed that doesn't match
+ // a filter or does match an exclude filter
+ d->lstRemoveItems->append( item );
+ }
+}
+
+void KDirLister::emitItems()
+{
+ KFileItemList *tmpNew = d->lstNewItems;
+ d->lstNewItems = 0;
+
+ KFileItemList *tmpMime = d->lstMimeFilteredItems;
+ d->lstMimeFilteredItems = 0;
+
+ KFileItemList *tmpRefresh = d->lstRefreshItems;
+ d->lstRefreshItems = 0;
+
+ KFileItemList *tmpRemove = d->lstRemoveItems;
+ d->lstRemoveItems = 0;
+
+ if ( tmpNew )
+ {
+ emit newItems( *tmpNew );
+ delete tmpNew;
+ }
+
+ if ( tmpMime )
+ {
+ emit itemsFilteredByMime( *tmpMime );
+ delete tmpMime;
+ }
+
+ if ( tmpRefresh )
+ {
+ emit refreshItems( *tmpRefresh );
+ delete tmpRefresh;
+ }
+
+ if ( tmpRemove )
+ {
+ for ( KFileItem *tmp = tmpRemove->first(); tmp; tmp = tmpRemove->next() )
+ emit deleteItem( tmp );
+ delete tmpRemove;
+ }
+}
+
+void KDirLister::emitDeleteItem( KFileItem *item )
+{
+ if ( ( d->dirOnlyMode && !item->isDir() ) || !matchesFilter( item ) )
+ return; // No reason to continue... bailing out here prevents a mimetype scan.
+ if ( matchesMimeFilter( item ) )
+ emit deleteItem( item );
+}
+
+
+// ================ private slots ================ //
+
+void KDirLister::slotInfoMessage( TDEIO::Job *, const TQString& message )
+{
+ emit infoMessage( message );
+}
+
+void KDirLister::slotPercent( TDEIO::Job *job, unsigned long pcnt )
+{
+ d->jobData[static_cast<TDEIO::ListJob *>(job)].percent = pcnt;
+
+ int result = 0;
+
+ TDEIO::filesize_t size = 0;
+
+ TQMap< TDEIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
+ while ( dataIt != d->jobData.end() )
+ {
+ result += (*dataIt).percent * (*dataIt).totalSize;
+ size += (*dataIt).totalSize;
+ ++dataIt;
+ }
+
+ if ( size != 0 )
+ result /= size;
+ else
+ result = 100;
+ emit percent( result );
+}
+
+void KDirLister::slotTotalSize( TDEIO::Job *job, TDEIO::filesize_t size )
+{
+ d->jobData[static_cast<TDEIO::ListJob *>(job)].totalSize = size;
+
+ TDEIO::filesize_t result = 0;
+ TQMap< TDEIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
+ while ( dataIt != d->jobData.end() )
+ {
+ result += (*dataIt).totalSize;
+ ++dataIt;
+ }
+
+ emit totalSize( result );
+}
+
+void KDirLister::slotProcessedSize( TDEIO::Job *job, TDEIO::filesize_t size )
+{
+ d->jobData[static_cast<TDEIO::ListJob *>(job)].processedSize = size;
+
+ TDEIO::filesize_t result = 0;
+ TQMap< TDEIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
+ while ( dataIt != d->jobData.end() )
+ {
+ result += (*dataIt).processedSize;
+ ++dataIt;
+ }
+
+ emit processedSize( result );
+}
+
+void KDirLister::slotSpeed( TDEIO::Job *job, unsigned long spd )
+{
+ d->jobData[static_cast<TDEIO::ListJob *>(job)].speed = spd;
+
+ int result = 0;
+ TQMap< TDEIO::ListJob *, KDirListerPrivate::JobData >::Iterator dataIt = d->jobData.begin();
+ while ( dataIt != d->jobData.end() )
+ {
+ result += (*dataIt).speed;
+ ++dataIt;
+ }
+
+ emit speed( result );
+}
+
+uint KDirLister::numJobs()
+{
+ return d->jobData.count();
+}
+
+void KDirLister::jobDone( TDEIO::ListJob *job )
+{
+ d->jobData.remove( job );
+}
+
+void KDirLister::jobStarted( TDEIO::ListJob *job )
+{
+ KDirListerPrivate::JobData jobData;
+ jobData.speed = 0;
+ jobData.percent = 0;
+ jobData.processedSize = 0;
+ jobData.totalSize = 0;
+
+ d->jobData.insert( job, jobData );
+ d->complete = false;
+}
+
+void KDirLister::connectJob( TDEIO::ListJob *job )
+{
+ connect( job, TQT_SIGNAL(infoMessage( TDEIO::Job *, const TQString& )),
+ this, TQT_SLOT(slotInfoMessage( TDEIO::Job *, const TQString& )) );
+ connect( job, TQT_SIGNAL(percent( TDEIO::Job *, unsigned long )),
+ this, TQT_SLOT(slotPercent( TDEIO::Job *, unsigned long )) );
+ connect( job, TQT_SIGNAL(totalSize( TDEIO::Job *, TDEIO::filesize_t )),
+ this, TQT_SLOT(slotTotalSize( TDEIO::Job *, TDEIO::filesize_t )) );
+ connect( job, TQT_SIGNAL(processedSize( TDEIO::Job *, TDEIO::filesize_t )),
+ this, TQT_SLOT(slotProcessedSize( TDEIO::Job *, TDEIO::filesize_t )) );
+ connect( job, TQT_SIGNAL(speed( TDEIO::Job *, unsigned long )),
+ this, TQT_SLOT(slotSpeed( TDEIO::Job *, unsigned long )) );
+}
+
+void KDirLister::setMainWindow( TQWidget *window )
+{
+ d->window = window;
+}
+
+TQWidget *KDirLister::mainWindow()
+{
+ return d->window;
+}
+
+KFileItemList KDirLister::items( WhichItems which ) const
+{
+ return itemsForDir( url(), which );
+}
+
+KFileItemList KDirLister::itemsForDir( const KURL& dir, WhichItems which ) const
+{
+ KFileItemList result;
+ KFileItemList *allItems = s_pCache->itemsForDir( dir );
+ if ( !allItems )
+ return result;
+
+ if ( which == AllItems )
+ result = *allItems; // shallow copy
+ else // only items passing the filters
+ {
+ for ( KFileItemListIterator kit( *allItems ); kit.current(); ++kit )
+ {
+ KFileItem *item = *kit;
+ bool isExcluded = (d->dirOnlyMode && !item->isDir()) || !matchesFilter( item );
+ if ( !isExcluded && matchesMimeFilter( item ) )
+ result.append( item );
+ }
+ }
+
+ return result;
+}
+
+// to keep BC changes
+
+void KDirLister::virtual_hook( int, void * )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+#include "kdirlister.moc"
+#include "kdirlister_p.moc"
diff --git a/tdeio/tdeio/kdirlister.h b/tdeio/tdeio/kdirlister.h
new file mode 100644
index 000000000..188f9ea7a
--- /dev/null
+++ b/tdeio/tdeio/kdirlister.h
@@ -0,0 +1,634 @@
+/* This file is part of the KDE project
+ Copyright (C) 1999 David Faure <faure@kde.org>
+ 2001, 2002, 2004, 2005 Michael Brade <brade@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef kdirlister_h
+#define kdirlister_h
+
+#include "tdefileitem.h"
+#include "kdirnotify.h"
+
+#include <tqstring.h>
+#include <tqstringlist.h>
+
+#include <kurl.h>
+
+namespace TDEIO { class Job; class ListJob; }
+
+/**
+ * The dir lister deals with the kiojob used to list and update a directory
+ * and has signals for the user of this class (e.g. konqueror view or
+ * kdesktop) to create/destroy its items when asked.
+ *
+ * This class is independent from the graphical representation of the dir
+ * (icon container, tree view, ...) and it stores the items (as KFileItems).
+ *
+ * Typical usage :
+ * @li Create an instance.
+ * @li Connect to at least update, clear, newItem, and deleteItem.
+ * @li Call openURL - the signals will be called.
+ * @li Reuse the instance when opening a new url (openURL).
+ * @li Destroy the instance when not needed anymore (usually destructor).
+ *
+ * Advanced usage : call openURL with _keep = true to list directories
+ * without forgetting the ones previously read (e.g. for a tree view)
+ *
+ * @short Helper class for the kiojob used to list and update a directory.
+ * @author Michael Brade <brade@kde.org>
+ */
+class TDEIO_EXPORT KDirLister : public TQObject
+{
+ class KDirListerPrivate;
+ friend class KDirListerPrivate;
+ friend class KDirListerCache;
+
+ Q_OBJECT
+ TQ_PROPERTY( bool autoUpdate READ autoUpdate WRITE setAutoUpdate )
+ TQ_PROPERTY( bool showingDotFiles READ showingDotFiles WRITE setShowingDotFiles )
+ TQ_PROPERTY( bool dirOnlyMode READ dirOnlyMode WRITE setDirOnlyMode )
+ TQ_PROPERTY( bool autoErrorHandlingEnabled READ autoErrorHandlingEnabled )
+ TQ_PROPERTY( TQString nameFilter READ nameFilter WRITE setNameFilter )
+ TQ_PROPERTY( TQStringList mimeFilter READ mimeFilters WRITE setMimeFilter RESET clearMimeFilter )
+
+public:
+ /**
+ * Create a directory lister.
+ * @param _delayedMimeTypes if true, mime types will be fetched on demand. If false,
+ * they will always be fetched immediately
+ */
+ KDirLister( bool _delayedMimeTypes = false );
+
+ /**
+ * Destroy the directory lister.
+ */
+ virtual ~KDirLister();
+
+ /**
+ * Run the directory lister on the given url.
+ *
+ * This method causes KDirLister to emit _all_ the items of @p _url, in any case.
+ * Depending on @p _keep either clear() or clear(const KURL &) will be
+ * emitted first.
+ *
+ * The newItems() signal may be emitted more than once to supply you
+ * with KFileItems, up until the signal completed() is emitted
+ * (and isFinished() returns true).
+ *
+ * @param _url the directory URL.
+ * @param _keep if true the previous directories aren't forgotten
+ * (they are still watched by kdirwatch and their items
+ * are kept for this KDirLister). This is useful for e.g.
+ * a treeview.
+ * @param _reload indicates wether to use the cache (false) or to reread the
+ * directory from the disk.
+ * Use only when opening a dir not yet listed by this lister
+ * without using the cache. Otherwise use updateDirectory.
+ * @return true if successful,
+ * false otherwise (e.g. invalid @p _url)
+ */
+ virtual bool openURL( const KURL& _url, bool _keep = false, bool _reload = false );
+
+ /**
+ * Stop listing all directories currently being listed.
+ *
+ * Emits canceled() if there was at least one job running.
+ * Emits canceled( const KURL& ) for each stopped job if
+ * there are at least two dirctories being watched by KDirLister.
+ */
+ virtual void stop();
+
+ /**
+ * Stop listing the given directory.
+ *
+ * Emits canceled() if the killed job was the last running one.
+ * Emits canceled( const KURL& ) for the killed job if
+ * there are at least two directories being watched by KDirLister.
+ * No signal is emitted if there was no job running for @p _url.
+ * @param _url the directory URL
+ */
+ virtual void stop( const KURL& _url );
+
+ /**
+ * Checks whether KDirWatch will automatically update directories. This is
+ * enabled by default.
+ * @return true if KDirWatch is used to automatically update directories.
+ */
+ bool autoUpdate() const;
+
+ /**
+ * Enable/disable automatic directory updating, when a directory changes
+ * (using KDirWatch).
+ * @param enable true to enable, false to disable
+ */
+ virtual void setAutoUpdate( bool enable );
+
+ /**
+ * Check whether auto error handling is enabled.
+ * If enabled, it will show an error dialog to the user when an
+ * error occurs. It is turned on by default.
+ * @return true if auto error handling is enabled, false otherwise
+ * @see setAutoErrorHandlingEnabled()
+ */
+ bool autoErrorHandlingEnabled() const;
+
+ /**
+ * Enable or disable auto error handling is enabled.
+ * If enabled, it will show an error dialog to the user when an
+ * error occurs. It is turned on by default.
+ * @param enable true to enable auto error handling, false to disable
+ * @param parent the parent widget for the error dialogs, can be 0 for
+ * top-level
+ * @see autoErrorHandlingEnabled()
+ */
+ void setAutoErrorHandlingEnabled( bool enable, TQWidget *parent );
+
+ /**
+ * Checks whether hidden files (files beginning with a dot) will be
+ * shown.
+ * By default this option is disabled (hidden files will be not shown).
+ * @return true if dot files are shown, false otherwise
+ * @see setShowingDotFiles()
+ */
+ bool showingDotFiles() const;
+
+ /**
+ * Changes the "is viewing dot files" setting.
+ * Calls updateDirectory() if setting changed.
+ * By default this option is disabled (hidden files will not be shown).
+ * @param _showDotFiles true to enable showing hidden files, false to
+ * disable
+ * @see showingDotFiles()
+ */
+ virtual void setShowingDotFiles( bool _showDotFiles );
+
+ /**
+ * Checks whether the KDirLister only lists directories or all
+ * files.
+ * By default this option is disabled (all files will be shown).
+ * @return true if setDirOnlyMode(true) was called
+ */
+ bool dirOnlyMode() const;
+
+ /**
+ * Call this to list only directories.
+ * By default this option is disabled (all files will be shown).
+ * @param dirsOnly true to list only directories
+ */
+ virtual void setDirOnlyMode( bool dirsOnly );
+
+ /**
+ * Returns the top level URL that is listed by this KDirLister.
+ * It might be different from the one given with openURL() if there was a
+ * redirection. If you called openURL() with @p _keep == true this is the
+ * first url opened (e.g. in a treeview this is the root).
+ *
+ * @return the url used by this instance to list the files.
+ */
+ const KURL& url() const;
+
+ /**
+ * Returns all URLs that are listed by this KDirLister. This is only
+ * useful if you called openURL() with @p _keep == true, as it happens in a
+ * treeview, for example. (Note that the base url is included in the list
+ * as well, of course.)
+ *
+ * @return the list of all listed URLs
+ * @since 3.4
+ */
+ const KURL::List& directories() const;
+
+ /**
+ * Actually emit the changes made with setShowingDotFiles, setDirOnlyMode,
+ * setNameFilter and setMimeFilter.
+ */
+ virtual void emitChanges();
+
+ /**
+ * Update the directory @p _dir. This method causes KDirLister to _only_ emit
+ * the items of @p _dir that actually changed compared to the current state in the
+ * cache and updates the cache.
+ *
+ * The current implementation calls updateDirectory automatically for
+ * local files, using KDirWatch (if autoUpdate() is true), but it might be
+ * useful to force an update manually.
+ *
+ * @param _dir the directory URL
+ */
+ virtual void updateDirectory( const KURL& _dir );
+
+ /**
+ * Returns true if no io operation is currently in progress.
+ * @return true if finished, false otherwise
+ */
+ bool isFinished() const;
+
+ /**
+ * Returns the file item of the URL.
+ * @return the file item for url() itself (".")
+ */
+ KFileItem *rootItem() const;
+
+ /**
+ * Find an item by its URL.
+ * @param _url the item URL
+ * @return the pointer to the KFileItem
+ */
+ virtual KFileItem *findByURL( const KURL& _url ) const;
+#ifndef KDE_NO_COMPAT
+ KFileItem *find( const KURL& _url ) const;
+#endif
+
+ /**
+ * Find an item by its name.
+ * @param name the item name
+ * @return the pointer to the KFileItem
+ */
+ virtual KFileItem *findByName( const TQString& name ) const;
+
+ /**
+ * Set a name filter to only list items matching this name, e.g. "*.cpp".
+ *
+ * You can set more than one filter by separating them with whitespace, e.g
+ * "*.cpp *.h".
+ * Note: the direcory is not automatically reloaded.
+ *
+ * @param filter the new filter, TQString::null to disable filtering
+ * @see matchesFilter
+ */
+ virtual void setNameFilter( const TQString &filter );
+
+ /**
+ * Returns the current name filter, as set via setNameFilter()
+ * @return the current name filter, can be TQString::null if filtering
+ * is turned off
+ */
+ const TQString& nameFilter() const;
+
+ /**
+ * Set mime-based filter to only list items matching the given mimetypes.
+ *
+ * NOTE: setting the filter does not automatically reload direcory.
+ * Also calling this function will not affect any named filter already set.
+ *
+ * @param mimeList a list of mime-types.
+ *
+ * @see clearMimeFilter
+ * @see matchesMimeFilter
+ */
+ virtual void setMimeFilter( const TQStringList &mimeList );
+
+ /**
+ * Filtering should be done with KFileFilter. This will be implemented in a later
+ * revision of KDirLister. This method may be removed then.
+ *
+ * Set mime-based exclude filter to only list items not matching the given mimetypes
+ *
+ * NOTE: setting the filter does not automatically reload direcory.
+ * Also calling this function will not affect any named filter already set.
+ *
+ * @param mimeList a list of mime-types.
+ * @see clearMimeFilter
+ * @see matchesMimeFilter
+ * @since 3.1
+ * @internal
+ */
+ void setMimeExcludeFilter(const TQStringList &mimeList );
+
+
+ /**
+ * Clears the mime based filter.
+ *
+ * @see setMimeFilter
+ */
+ virtual void clearMimeFilter();
+
+ /**
+ * Returns the list of mime based filters, as set via setMimeFilter().
+ * @return the list of mime based filters. Empty, when no mime filter is set.
+ */
+ const TQStringList& mimeFilters() const;
+
+ /**
+ * Checks whether @p name matches a filter in the list of name filters.
+ * @return true if @p name matches a filter in the list,
+ * otherwise false.
+ * @see setNameFilter
+ */
+ bool matchesFilter( const TQString& name ) const;
+
+ /**
+ * Checks whether @p mime matches a filter in the list of mime types
+ * @param mime the mimetype to find in the filter list.
+ * @return true if @p name matches a filter in the list,
+ * otherwise false.
+ * @see setMimeFilter.
+ */
+ bool matchesMimeFilter( const TQString& mime ) const;
+
+ /**
+ * Pass the main window this object is associated with
+ * this is used for caching authentication data
+ * @param window the window to associate with, 0 to disassociate
+ * @since 3.1
+ */
+ void setMainWindow( TQWidget *window );
+
+ /**
+ * Returns the main window associated with this object.
+ * @return the associated main window, or 0 if there is none
+ * @since 3.1
+ */
+ TQWidget *mainWindow();
+
+ /**
+ * Used by items() and itemsForDir() to specify whether you want
+ * all items for a directory or just the filtered ones.
+ */
+ enum WhichItems
+ {
+ AllItems = 0,
+ FilteredItems = 1
+ };
+
+ /**
+ * Returns the items listed for the current url().
+ * This method will NOT start listing a directory, you should only call
+ * this when receiving the finished() signal.
+ *
+ * The items in the KFileItemList are references to the items used
+ * by KDirLister, so e.g. an item gets destroyed when the deleteItem()
+ * signal is emitted.
+ *
+ * @param which specifies whether the returned list will contain all entries
+ * or only the ones that passed the nameFilter(), mimeFilter(),
+ * etc. Note that the latter causes iteration over all the
+ * items, filtering them. If this is too slow for you, use the
+ * newItems() signal, sending out filtered items in chunks.
+ * @return the items listed for the current url().
+ * @since 3.1
+ */
+ KFileItemList items( WhichItems which = FilteredItems ) const;
+
+ /**
+ * Returns the items listed for the given @p dir.
+ * This method will NOT start listing @p dir, you should only call
+ * this when receiving the finished() signal.
+ *
+ * The items in the KFileItemList are references to the items used
+ * by KDirLister, so e.g. an item gets destroyed when the deleteItem()
+ * signal is emitted.
+ *
+ * @param dir specifies the url for which the items should be returned. This
+ * is only useful if you use KDirLister with multiple URLs
+ * i.e. using bool keep = true in openURL().
+ * @param which specifies whether the returned list will contain all entries
+ * or only the ones that passed the nameFilter, mimeFilter, etc.
+ * Note that the latter causes iteration over all the items,
+ * filtering them. If this is too slow for you, use the
+ * newItems() signal, sending out filtered items in chunks.
+ * @return the items listed for @p dir.
+ * @since 3.1
+ */
+ KFileItemList itemsForDir( const KURL& dir,
+ WhichItems which = FilteredItems ) const;
+
+signals:
+ /**
+ * Tell the view that we started to list @p _url. NOTE: this does _not_ imply that there
+ * is really a job running! I.e. KDirLister::jobs() may return an empty list. In this case
+ * the items are taken from the cache.
+ *
+ * The view knows that openURL should start it, so it might seem useless,
+ * but the view also needs to know when an automatic update happens.
+ * @param _url the URL to list
+ */
+ void started( const KURL& _url );
+
+ /**
+ * Tell the view that listing is finished. There are no jobs running anymore.
+ */
+ void completed();
+
+ /**
+ * Tell the view that the listing of the directory @p _url is finished.
+ * There might be other running jobs left.
+ * @param _url the directory URL
+ */
+ void completed( const KURL& _url );
+
+ /**
+ * Tell the view that the user canceled the listing. No running jobs are left.
+ */
+ void canceled();
+
+ /**
+ * Tell the view that the listing of the directory @p _url was canceled.
+ * There might be other running jobs left.
+ * @param _url the directory URL
+ */
+ void canceled( const KURL& _url );
+
+ /**
+ * Signal a redirection.
+ * Only emitted if there's just one directory to list, i.e. most
+ * probably openURL() has been called with @p _keep == @p false.
+ * @param _url the new URL
+ */
+ void redirection( const KURL& _url );
+
+ /**
+ * Signal a redirection.
+ * @param oldUrl the original URL
+ * @param newUrl the new URL
+ */
+ void redirection( const KURL& oldUrl, const KURL& newUrl );
+
+ /**
+ * Signal to clear all items.
+ * It must always be connected to this signal to avoid doubled items!
+ */
+ void clear();
+
+ /**
+ * Signal to empty the directory @p _url.
+ * It is only emitted if the lister is holding more than one directory.
+ * @param _url the directory that will be emptied
+ */
+ void clear( const KURL& _url );
+
+ /**
+ * Signal new items.
+ * @param items a list of new items
+ */
+ void newItems( const KFileItemList& items );
+
+ /**
+ * Send a list of items filtered-out by mime-type.
+ * @param items the list of filtered items
+ */
+ void itemsFilteredByMime( const KFileItemList& items );
+
+ /**
+ * Signal an item to remove.
+ *
+ * ATTENTION: if @p _fileItem == rootItem() the directory this lister
+ * is holding was deleted and you HAVE to release especially the
+ * rootItem() of this lister, otherwise your app will CRASH!!
+ * The clear() signals have been emitted already.
+ * @param _fileItem the fileItem to delete
+ */
+ void deleteItem( KFileItem *_fileItem );
+
+ /**
+ * Signal an item to refresh (its mimetype/icon/name has changed).
+ * Note: KFileItem::refresh has already been called on those items.
+ * @param items the items to refresh
+ */
+ void refreshItems( const KFileItemList& items );
+
+ /**
+ * Emitted to display information about running jobs.
+ * Examples of message are "Resolving host", "Connecting to host...", etc.
+ * @param msg the info message
+ */
+ void infoMessage( const TQString& msg );
+
+ /**
+ * Progress signal showing the overall progress of the KDirLister.
+ * This allows using a progress bar very easily. (see KProgress)
+ * @param percent the progress in percent
+ */
+ void percent( int percent );
+
+ /**
+ * Emitted when we know the size of the jobs.
+ * @param size the total size in bytes
+ */
+ void totalSize( TDEIO::filesize_t size );
+
+ /**
+ * Regularly emitted to show the progress of this KDirLister.
+ * @param size the processed size in bytes
+ */
+ void processedSize( TDEIO::filesize_t size );
+
+ /**
+ * Emitted to display information about the speed of the jobs.
+ * @param bytes_per_second the speed in bytes/s
+ */
+ void speed( int bytes_per_second );
+
+protected:
+ enum Changes {
+ NONE=0, NAME_FILTER=1, MIME_FILTER=2, DOT_FILES=4, DIR_ONLY_MODE=8
+ };
+
+ /**
+ * Called for every new item before emitting newItems().
+ * You may reimplement this method in a subclass to implement your own
+ * filtering.
+ * The default implementation filters out ".." and everything not matching
+ * the name filter(s)
+ * @return true if the item is "ok".
+ * false if the item shall not be shown in a view, e.g.
+ * files not matching a pattern *.cpp ( KFileItem::isHidden())
+ * @see matchesFilter
+ * @see setNameFilter
+ */
+ virtual bool matchesFilter( const KFileItem * ) const;
+
+ /**
+ * Called for every new item before emitting newItems().
+ * You may reimplement this method in a subclass to implement your own
+ * filtering.
+ * The default implementation filters out ".." and everything not matching
+ * the name filter(s)
+ * @return true if the item is "ok".
+ * false if the item shall not be shown in a view, e.g.
+ * files not matching a pattern *.cpp ( KFileItem::isHidden())
+ * @see matchesMimeFilter
+ * @see setMimeFilter
+ */
+ virtual bool matchesMimeFilter( const KFileItem * ) const;
+
+ /**
+ * Called by the public matchesFilter() to do the
+ * actual filtering. Those methods may be reimplemented to customize
+ * filtering.
+ * @param name the name to filter
+ * @param filters a list of regular expressions for filtering
+ */
+ virtual bool doNameFilter( const TQString& name, const TQPtrList<TQRegExp>& filters ) const;
+
+ /**
+ * Called by the public matchesMimeFilter() to do the
+ * actual filtering. Those methods may be reimplemented to customize
+ * filtering.
+ * @param mime the mime type to filter
+ * @param filters the list of mime types to filter
+ */
+ virtual bool doMimeFilter( const TQString& mime, const TQStringList& filters ) const;
+
+ /**
+ * @internal
+ */
+ bool doMimeExcludeFilter( const TQString& mimeExclude, const TQStringList& filters ) const;
+
+ /**
+ * Checks if an url is malformed or not and displays an error message
+ * if it is and autoErrorHandling is set to true.
+ * @return true if url is valid, otherwise false.
+ */
+ virtual bool validURL( const KURL& ) const;
+
+ /** Reimplement to customize error handling */
+ virtual void handleError( TDEIO::Job * );
+
+protected:
+ virtual void virtual_hook( int id, void *data );
+
+private slots:
+ void slotInfoMessage( TDEIO::Job *, const TQString& );
+ void slotPercent( TDEIO::Job *, unsigned long );
+ void slotTotalSize( TDEIO::Job *, TDEIO::filesize_t );
+ void slotProcessedSize( TDEIO::Job *, TDEIO::filesize_t );
+ void slotSpeed( TDEIO::Job *, unsigned long );
+ void slotLocalURL( TDEIO::Job *, const KURL&, bool );
+ void slotLocalURLKIODestroyed( );
+
+private:
+ void jobStarted( TDEIO::ListJob * );
+ void connectJob( TDEIO::ListJob * );
+ void jobDone( TDEIO::ListJob * );
+
+ uint numJobs();
+
+private:
+ virtual void addNewItem( const KFileItem *item );
+ virtual void addNewItems( const KFileItemList& items );
+ /*virtual*/ void aboutToRefreshItem( const KFileItem *item ); // TODO: KDE 4.0 make virtual
+ virtual void addRefreshItem( const KFileItem *item );
+ virtual void emitItems();
+ virtual void emitDeleteItem( KFileItem *item );
+
+ KDirListerPrivate *d;
+};
+
+#endif
+
diff --git a/tdeio/tdeio/kdirlister_p.h b/tdeio/tdeio/kdirlister_p.h
new file mode 100644
index 000000000..d6dff7bcf
--- /dev/null
+++ b/tdeio/tdeio/kdirlister_p.h
@@ -0,0 +1,358 @@
+/* This file is part of the KDE project
+ Copyright (C) 2002 Michael Brade <brade@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef kdirlister_p_h
+#define kdirlister_p_h
+
+#include "tdefileitem.h"
+
+#include <tqmap.h>
+#include <tqdict.h>
+#include <tqcache.h>
+#include <tqwidget.h>
+
+#include <kurl.h>
+#include <tdeio/global.h>
+#include <kdirwatch.h>
+#include <dcopclient.h>
+
+class TQTimer;
+class KDirLister;
+namespace TDEIO { class Job; class ListJob; }
+
+
+class KDirLister::KDirListerPrivate
+{
+public:
+ KDirListerPrivate()
+ {
+ complete = false;
+
+ autoUpdate = false;
+ isShowingDotFiles = false;
+ dirOnlyMode = false;
+
+ autoErrorHandling = false;
+ errorParent = 0;
+
+ delayedMimeTypes = false;
+
+ rootFileItem = 0;
+ lstNewItems = 0;
+ lstRefreshItems = 0;
+ lstMimeFilteredItems = 0;
+ lstRemoveItems = 0;
+ refreshItemWasFiltered = false;
+
+ changes = NONE;
+
+ window = 0;
+
+ lstFilters.setAutoDelete( true );
+ oldFilters.setAutoDelete( true );
+ }
+
+ /**
+ * List of dirs handled by this dirlister. The first entry is the base URL.
+ * For a tree view, it contains all the dirs shown.
+ */
+ KURL::List lstDirs;
+
+ // toplevel URL
+ KURL url;
+
+ bool complete;
+
+ bool autoUpdate;
+ bool isShowingDotFiles;
+ bool dirOnlyMode;
+
+ bool autoErrorHandling;
+ TQWidget *errorParent;
+
+ bool delayedMimeTypes;
+
+ struct JobData {
+ long unsigned int percent, speed;
+ TDEIO::filesize_t processedSize, totalSize;
+ };
+
+ TQMap<TDEIO::ListJob *, JobData> jobData;
+
+ // file item for the root itself (".")
+ KFileItem *rootFileItem;
+
+ KFileItemList *lstNewItems, *lstRefreshItems;
+ KFileItemList *lstMimeFilteredItems, *lstRemoveItems;
+
+ bool refreshItemWasFiltered;
+
+ int changes;
+
+ TQWidget *window; // Main window ths lister is associated with
+
+ TQString nameFilter;
+ TQPtrList<TQRegExp> lstFilters, oldFilters;
+ TQStringList mimeFilter, oldMimeFilter;
+ TQStringList mimeExcludeFilter, oldMimeExcludeFilter;
+
+ bool localURLSlotFired;
+ KURL localURLResultURL;
+ bool localURLResultIsLocal;
+};
+
+/**
+ * Design of the cache:
+ * There is a single KDirListerCache for the whole process.
+ * It holds all the items used by the dir listers (itemsInUse)
+ * as well as a cache of the recently used items (itemsCached).
+ * Those items are grouped by directory (a DirItem represents a whole directory).
+ *
+ * KDirListerCache also runs all the jobs for listing directories, whether they are for
+ * normal listing or for updates.
+ * For faster lookups, it also stores two dicts:
+ * a URL -> dirlister holding that URL (urlsCurrentlyHeld)
+ * a URL -> dirlister currently listing that URL (urlsCurrentlyListed)
+ */
+class KDirListerCache : public TQObject, KDirNotify
+{
+ Q_OBJECT
+public:
+ KDirListerCache( int maxCount = 10 );
+ ~KDirListerCache();
+
+ bool listDir( KDirLister *lister, const KURL& _url, bool _keep, bool _reload );
+ bool validURL( const KDirLister *lister, const KURL& _url ) const;
+
+ // stop all running jobs for lister
+ void stop( KDirLister *lister );
+ // stop just the job listing url for lister
+ void stop( KDirLister *lister, const KURL &_url );
+
+ void setAutoUpdate( KDirLister *lister, bool enable );
+
+ void forgetDirs( KDirLister *lister );
+ void forgetDirs( KDirLister *lister, const KURL &_url, bool notify );
+
+ void updateDirectory( const KURL &_dir );
+
+ KFileItemList *itemsForDir( const KURL &_dir ) const;
+
+ KFileItem *findByName( const KDirLister *lister, const TQString &_name ) const;
+ // if lister is set, it is checked that the url is held by the lister
+ KFileItem *findByURL( const KDirLister *lister, const KURL &_url ) const;
+
+ /**
+ * Notify that files have been added in @p directory
+ * The receiver will list that directory again to find
+ * the new items (since it needs more than just the names anyway).
+ * Reimplemented from KDirNotify.
+ */
+ virtual void FilesAdded( const KURL &directory );
+
+ /**
+ * Notify that files have been deleted.
+ * This call passes the exact urls of the deleted files
+ * so that any view showing them can simply remove them
+ * or be closed (if its current dir was deleted)
+ * Reimplemented from KDirNotify.
+ */
+ virtual void FilesRemoved( const KURL::List &fileList );
+
+ /**
+ * Notify that files have been changed.
+ * At the moment, this is only used for new icon, but it could be
+ * used for size etc. as well.
+ * Note: this is ASYNC so that it can be used with a broadcast
+ */
+ virtual void FilesChanged( const KURL::List &fileList );
+ virtual void FileRenamed( const KURL &src, const KURL &dst );
+
+ static KDirListerCache *self();
+
+ static bool exists();
+
+private slots:
+ void slotFileDirty( const TQString &_file );
+ void slotFileCreated( const TQString &_file );
+ void slotFileDeleted( const TQString &_file );
+
+ void slotFileDirtyDelayed();
+
+ void slotEntries( TDEIO::Job *job, const TDEIO::UDSEntryList &entries );
+ void slotResult( TDEIO::Job *j );
+ void slotRedirection( TDEIO::Job *job, const KURL &url );
+
+ void slotUpdateEntries( TDEIO::Job *job, const TDEIO::UDSEntryList &entries );
+ void slotUpdateResult( TDEIO::Job *job );
+
+private:
+ TDEIO::ListJob *jobForUrl( const TQString& url, TDEIO::ListJob *not_job = 0 );
+ const KURL& joburl( TDEIO::ListJob *job );
+
+ void killJob( TDEIO::ListJob *job );
+
+ // check if _url is held by some lister and return true,
+ // otherwise schedule a delayed update and return false
+ bool checkUpdate( const TQString& _url );
+ // when there were items deleted from the filesystem all the listers holding
+ // the parent directory need to be notified, the unmarked items have to be deleted
+ // and removed from the cache including all the childs.
+ void deleteUnmarkedItems( TQPtrList<KDirLister> *, KFileItemList * );
+ void processPendingUpdates();
+ // common for slotRedirection and FileRenamed
+ void renameDir( const KURL &oldUrl, const KURL &url );
+ // common for deleteUnmarkedItems and FilesRemoved
+ void deleteDir( const KURL& dirUrl );
+ // remove directory from cache (itemsCached), including all child dirs
+ void removeDirFromCache( const KURL& dir );
+ // helper for renameDir
+ void emitRedirections( const KURL &oldUrl, const KURL &url );
+
+ void aboutToRefreshItem( KFileItem *fileitem );
+ void emitRefreshItem( KFileItem *fileitem );
+
+#ifndef NDEBUG
+ void printDebug();
+#endif
+
+ struct DirItem
+ {
+ DirItem( const KURL &dir )
+ : url(dir), rootItem(0), lstItems(new KFileItemList)
+ {
+ autoUpdates = 0;
+ complete = false;
+ lstItems->setAutoDelete( true );
+ }
+
+ ~DirItem()
+ {
+ if ( autoUpdates )
+ {
+ if ( KDirWatch::exists() && url.isLocalFile() )
+ kdirwatch->removeDir( url.path() );
+ sendSignal( false, url );
+ }
+ delete rootItem;
+ delete lstItems;
+ }
+
+ void sendSignal( bool entering, const KURL& url )
+ {
+ DCOPClient *client = DCOPClient::mainClient();
+ if ( !client )
+ return;
+ TQByteArray data;
+ TQDataStream arg( data, IO_WriteOnly );
+ arg << url;
+ client->emitDCOPSignal( "KDirNotify", entering ? "enteredDirectory(KURL)" : "leftDirectory(KURL)", data );
+ }
+
+ void redirect( const KURL& newUrl )
+ {
+ if ( autoUpdates )
+ {
+ if ( url.isLocalFile() )
+ kdirwatch->removeDir( url.path() );
+ sendSignal( false, url );
+
+ if ( newUrl.isLocalFile() )
+ kdirwatch->addDir( newUrl.path() );
+ sendSignal( true, newUrl );
+ }
+
+ url = newUrl;
+
+ if ( rootItem )
+ rootItem->setURL( newUrl );
+ }
+
+ void incAutoUpdate()
+ {
+ if ( autoUpdates++ == 0 )
+ {
+ if ( url.isLocalFile() )
+ kdirwatch->addDir( url.path() );
+ sendSignal( true, url );
+ }
+ }
+
+ void decAutoUpdate()
+ {
+ if ( --autoUpdates == 0 )
+ {
+ if ( url.isLocalFile() )
+ kdirwatch->removeDir( url.path() );
+ sendSignal( false, url );
+ }
+
+ else if ( autoUpdates < 0 )
+ autoUpdates = 0;
+ }
+
+ // number of KDirListers using autoUpdate for this dir
+ short autoUpdates;
+
+ // this directory is up-to-date
+ bool complete;
+
+ // the complete url of this directory
+ KURL url;
+
+ // KFileItem representing the root of this directory.
+ // Remember that this is optional. FTP sites don't return '.' in
+ // the list, so they give no root item
+ KFileItem *rootItem;
+ KFileItemList *lstItems;
+ };
+
+ static const unsigned short MAX_JOBS_PER_LISTER;
+ TQMap<TDEIO::ListJob *, TDEIO::UDSEntryList> jobs;
+
+ // an item is a complete directory
+ TQDict<DirItem> itemsInUse;
+ TQCache<DirItem> itemsCached;
+
+ // A lister can be EITHER in urlsCurrentlyListed OR urlsCurrentlyHeld but NOT
+ // in both at the same time.
+ // On the other hand there can be some listers in urlsCurrentlyHeld
+ // and some in urlsCurrentlyListed for the same url!
+ // Or differently said, there can be an entry for url in urlsCurrentlyListed
+ // and urlsCurrentlyHeld. This happens if more listers are requesting url at
+ // the same time and one lister was stopped during the listing of files.
+
+ // saves all urls that are currently being listed and maps them
+ // to their KDirListers
+ TQDict< TQPtrList<KDirLister> > urlsCurrentlyListed;
+
+ // saves all KDirListers that are just holding url
+ TQDict< TQPtrList<KDirLister> > urlsCurrentlyHeld;
+
+ // running timers for the delayed update
+ TQDict<TQTimer> pendingUpdates;
+
+ static KDirListerCache *s_pSelf;
+};
+
+const unsigned short KDirListerCache::MAX_JOBS_PER_LISTER = 5;
+
+#define s_pCache KDirListerCache::self()
+
+#endif
diff --git a/tdeio/tdeio/kdirnotify.cpp b/tdeio/tdeio/kdirnotify.cpp
new file mode 100644
index 000000000..fb98196f5
--- /dev/null
+++ b/tdeio/tdeio/kdirnotify.cpp
@@ -0,0 +1,40 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kdirnotify.h"
+
+// Needed since DCOP enforces object id uniqueness.
+int KDirNotify::s_serial = 0;
+
+KDirNotify::KDirNotify()
+ : DCOPObject( TQCString().sprintf("KDirNotify-%d", ++s_serial) )
+{
+ connectDCOPSignal(0, "KDirNotify", "FilesAdded(KURL)", "FilesAdded(KURL)", false);
+ connectDCOPSignal(0, "KDirNotify", "FilesRemoved(KURL::List)", "FilesRemoved(KURL::List)", false);
+ connectDCOPSignal(0, "KDirNotify", "FilesChanged(KURL::List)", "FilesChanged(KURL::List)", false);
+ connectDCOPSignal(0, "KDirNotify", "FileRenamed(KURL,KURL)", "FileRenamed(KURL,KURL)", false);
+}
+
+void KDirNotify::FileRenamed( const KURL &, const KURL & )
+{
+}
+
+void KDirNotify::virtual_hook( int id, void* data )
+{ DCOPObject::virtual_hook( id, data ); }
+
diff --git a/tdeio/tdeio/kdirnotify.h b/tdeio/tdeio/kdirnotify.h
new file mode 100644
index 000000000..14d864609
--- /dev/null
+++ b/tdeio/tdeio/kdirnotify.h
@@ -0,0 +1,84 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kdirnotify_h__
+#define __kdirnotify_h__
+
+#include <dcopobject.h>
+#include <kurl.h>
+
+/**
+ * An abstract class that receives notifications of added and removed files
+ * in any directory, local or remote.
+ * The information comes from the konqueror/kdesktop instance where the
+ * operation was done, and can interest KDirListers, bookmark handlers, etc.
+ */
+class TDEIO_EXPORT KDirNotify : public DCOPObject
+{
+ K_DCOP
+protected:
+ KDirNotify();
+ virtual ~KDirNotify() {}
+
+public:
+k_dcop:
+ /**
+ * Notify that files have been added in @p directory
+ * Note: this is ASYNC so that it can be used with a broadcast.
+ * @param directory the directory that contains the new files
+ */
+ virtual ASYNC FilesAdded( const KURL & directory ) = 0;
+
+ /**
+ * Notify that files have been deleted.
+ * Note: this is ASYNC so that it can be used with a broadcast
+ * @param fileList the files that have been deleted
+ */
+ virtual ASYNC FilesRemoved( const KURL::List & fileList ) = 0;
+
+ /**
+ * Notify that files have been changed.
+ * At the moment, this is only used for new icon, but it could be
+ * used for size etc. as well.
+ * Note: this is ASYNC so that it can be used with a broadcast.
+ * @param fileList the list of changed files
+ */
+ virtual ASYNC FilesChanged( const KURL::List & fileList ) = 0;
+
+ /**
+ * Notify that a file has been renamed.
+ * Note: this is ASYNC so that it can be used with a broadcast
+ * @param src a list containing original names of the renamed files
+ * @param dst a list of original names of the renamed files
+ */
+ virtual ASYNC FileRenamed( const KURL &src, const KURL &dst );
+
+ // WARNING: When adding new methods, make sure to update
+ // kdirnotify_stub.cpp and kdirnotify_stub.h manually.
+ // They are not automatically generated since they contain
+ // handcoded changes.
+
+private:
+ // @internal
+ static int s_serial;
+protected:
+ virtual void virtual_hook( int id, void* data );
+};
+
+#endif
diff --git a/tdeio/tdeio/kdirnotify_stub.cpp b/tdeio/tdeio/kdirnotify_stub.cpp
new file mode 100644
index 000000000..66988d6c9
--- /dev/null
+++ b/tdeio/tdeio/kdirnotify_stub.cpp
@@ -0,0 +1,101 @@
+/****************************************************************************
+**
+** DCOP Stub Implementation based on output of dcopidl2cpp from kdirnotify.kidl
+** but with hand coded changes!!
+**
+*****************************************************************************/
+/* This file is part of the KDE project
+ Copyright (C) 2000 David Faure <faure@kde.org>
+ Copyright (C) 2003 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kdirnotify_stub.h"
+#include <dcopclient.h>
+
+#include <kdatastream.h>
+
+
+KDirNotify_stub::KDirNotify_stub( const TQCString& app, const TQCString& obj )
+ : DCOPStub( app, obj )
+{
+}
+
+KDirNotify_stub::KDirNotify_stub( DCOPClient* client, const TQCString& app, const TQCString& obj )
+ : DCOPStub( client, app, obj )
+{
+}
+
+KDirNotify_stub::KDirNotify_stub( const DCOPRef& ref )
+ : DCOPStub( ref )
+{
+}
+
+void KDirNotify_stub::FilesAdded( const KURL& arg0 )
+{
+ if ( !dcopClient() ) {
+ setStatus( CallFailed );
+ return;
+ }
+ TQByteArray data;
+ TQDataStream arg( data, IO_WriteOnly );
+ arg << arg0;
+ dcopClient()->emitDCOPSignal( "KDirNotify", "FilesAdded(KURL)", data );
+ setStatus( CallSucceeded );
+}
+
+void KDirNotify_stub::FilesRemoved( const KURL::List& arg0 )
+{
+ if ( !dcopClient() ) {
+ setStatus( CallFailed );
+ return;
+ }
+ TQByteArray data;
+ TQDataStream arg( data, IO_WriteOnly );
+ arg << arg0;
+ dcopClient()->emitDCOPSignal( "KDirNotify", "FilesRemoved(KURL::List)", data );
+ setStatus( CallSucceeded );
+}
+
+void KDirNotify_stub::FilesChanged( const KURL::List& arg0 )
+{
+ if ( !dcopClient() ) {
+ setStatus( CallFailed );
+ return;
+ }
+ TQByteArray data;
+ TQDataStream arg( data, IO_WriteOnly );
+ arg << arg0;
+ dcopClient()->emitDCOPSignal( "KDirNotify", "FilesChanged(KURL::List)", data );
+ setStatus( CallSucceeded );
+}
+
+void KDirNotify_stub::FileRenamed( const KURL& arg0, const KURL& arg1 )
+{
+ if ( !dcopClient() ) {
+ setStatus( CallFailed );
+ return;
+ }
+ TQByteArray data;
+ TQDataStream arg( data, IO_WriteOnly );
+ arg << arg0;
+ arg << arg1;
+ dcopClient()->emitDCOPSignal( "KDirNotify", "FileRenamed(KURL,KURL)", data );
+ setStatus( CallSucceeded );
+}
+
+
diff --git a/tdeio/tdeio/kdirnotify_stub.h b/tdeio/tdeio/kdirnotify_stub.h
new file mode 100644
index 000000000..56ab168c4
--- /dev/null
+++ b/tdeio/tdeio/kdirnotify_stub.h
@@ -0,0 +1,32 @@
+/****************************************************************************
+**
+** DCOP Stub Definition created by dcopidl2cpp from kdirnotify.kidl
+**
+** WARNING! All changes made in this file will be lost!
+**
+*****************************************************************************/
+
+#ifndef __KDIRNOTIFY_STUB__
+#define __KDIRNOTIFY_STUB__
+
+#include <dcopstub.h>
+#include <dcopobject.h>
+#include <kurl.h>
+
+
+class TDEIO_EXPORT KDirNotify_stub : virtual public DCOPStub
+{
+public:
+ KDirNotify_stub( const TQCString& app, const TQCString& id );
+ KDirNotify_stub( DCOPClient* client, const TQCString& app, const TQCString& id );
+ explicit KDirNotify_stub( const DCOPRef& ref );
+ virtual ASYNC FilesAdded( const KURL& directory );
+ virtual ASYNC FilesRemoved( const KURL::List& fileList );
+ virtual ASYNC FilesChanged( const KURL::List& fileList );
+ virtual ASYNC FileRenamed( const KURL& src, const KURL& dst );
+protected:
+ KDirNotify_stub() : DCOPStub( never_use ) {};
+};
+
+
+#endif
diff --git a/tdeio/tdeio/kdirwatch.cpp b/tdeio/tdeio/kdirwatch.cpp
new file mode 100644
index 000000000..c4057264b
--- /dev/null
+++ b/tdeio/tdeio/kdirwatch.cpp
@@ -0,0 +1,1774 @@
+// -*- c-basic-offset: 2 -*-
+/* This file is part of the KDE libraries
+ Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+
+// CHANGES:
+// Oct 4, 2005 - Inotify support (Dirk Mueller)
+// Februar 2002 - Add file watching and remote mount check for STAT
+// Mar 30, 2001 - Native support for Linux dir change notification.
+// Jan 28, 2000 - Usage of FAM service on IRIX (Josef.Weidendorfer@in.tum.de)
+// May 24. 1998 - List of times introduced, and some bugs are fixed. (sven)1
+// May 23. 1998 - Removed static pointer - you can have more instances.
+// It was Needed for KRegistry. KDirWatch now emits signals and doesn't
+// call (or need) KFM. No more URL's - just plain paths. (sven)
+// Mar 29. 1998 - added docs, stop/restart for particular Dirs and
+// deep copies for list of dirs. (sven)
+// Mar 28. 1998 - Created. (sven)
+
+
+#include <config.h>
+#include <errno.h>
+
+#ifdef HAVE_DNOTIFY
+#include <unistd.h>
+#include <time.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <errno.h>
+#endif
+
+
+#include <sys/stat.h>
+#include <assert.h>
+#include <tqdir.h>
+#include <tqfile.h>
+#include <tqintdict.h>
+#include <tqptrlist.h>
+#include <tqsocketnotifier.h>
+#include <tqstringlist.h>
+#include <tqtimer.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <tdeconfig.h>
+#include <kglobal.h>
+#include <kstaticdeleter.h>
+#include <kde_file.h>
+
+// debug
+#include <sys/ioctl.h>
+
+#ifdef HAVE_INOTIFY
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/syscall.h>
+#include <linux/types.h>
+// Linux kernel headers are documented to not compile
+#define _S390_BITOPS_H
+#include <sys/inotify.h>
+
+#ifndef __NR_inotify_init
+#if defined(__i386__)
+#define __NR_inotify_init 291
+#define __NR_inotify_add_watch 292
+#define __NR_inotify_rm_watch 293
+#endif
+#if defined(__PPC__)
+#define __NR_inotify_init 275
+#define __NR_inotify_add_watch 276
+#define __NR_inotify_rm_watch 277
+#endif
+#if defined(__x86_64__)
+#define __NR_inotify_init 253
+#define __NR_inotify_add_watch 254
+#define __NR_inotify_rm_watch 255
+#endif
+#endif
+
+#ifndef IN_ONLYDIR
+#define IN_ONLYDIR 0x01000000
+#endif
+
+#ifndef IN_DONT_FOLLOW
+#define IN_DONT_FOLLOW 0x02000000
+#endif
+
+#ifndef IN_MOVE_SELF
+#define IN_MOVE_SELF 0x00000800
+#endif
+
+#endif
+
+#include <sys/utsname.h>
+
+#include "kdirwatch.h"
+#include "kdirwatch_p.h"
+#include "global.h" // TDEIO::probably_slow_mounted
+
+#define NO_NOTIFY (time_t) 0
+
+static KDirWatchPrivate* dwp_self = 0;
+
+#ifdef HAVE_DNOTIFY
+
+static int dnotify_signal = 0;
+
+/* DNOTIFY signal handler
+ *
+ * As this is called asynchronously, only a flag is set and
+ * a rescan is requested.
+ * This is done by writing into a pipe to trigger a TQSocketNotifier
+ * watching on this pipe: a timer is started and after a timeout,
+ * the rescan is done.
+ */
+void KDirWatchPrivate::dnotify_handler(int, siginfo_t *si, void *)
+{
+ if (!dwp_self) return;
+
+ // write might change errno, we have to save it and restore it
+ // (Richard Stevens, Advanced programming in the Unix Environment)
+ int saved_errno = errno;
+
+ Entry* e = dwp_self->fd_Entry.find(si->si_fd);
+
+// kdDebug(7001) << "DNOTIFY Handler: fd " << si->si_fd << " path "
+// << TQString(e ? e->path:"unknown") << endl;
+
+ if(e && e->dn_fd == si->si_fd)
+ e->dirty = true;
+
+ char c = 0;
+ write(dwp_self->mPipe[1], &c, 1);
+ errno = saved_errno;
+}
+
+static struct sigaction old_sigio_act;
+/* DNOTIFY SIGIO signal handler
+ *
+ * When the kernel queue for the dnotify_signal overflows, a SIGIO is send.
+ */
+void KDirWatchPrivate::dnotify_sigio_handler(int sig, siginfo_t *si, void *p)
+{
+ if (dwp_self)
+ {
+ // write might change errno, we have to save it and restore it
+ // (Richard Stevens, Advanced programming in the Unix Environment)
+ int saved_errno = errno;
+
+ dwp_self->rescan_all = true;
+ char c = 0;
+ write(dwp_self->mPipe[1], &c, 1);
+
+ errno = saved_errno;
+ }
+
+ // Call previous signal handler
+ if (old_sigio_act.sa_flags & SA_SIGINFO)
+ {
+ if (old_sigio_act.sa_sigaction)
+ (*old_sigio_act.sa_sigaction)(sig, si, p);
+ }
+ else
+ {
+ if ((old_sigio_act.sa_handler != SIG_DFL) &&
+ (old_sigio_act.sa_handler != SIG_IGN))
+ (*old_sigio_act.sa_handler)(sig);
+ }
+}
+#endif
+
+
+//
+// Class KDirWatchPrivate (singleton)
+//
+
+/* All entries (files/directories) to be watched in the
+ * application (coming from multiple KDirWatch instances)
+ * are registered in a single KDirWatchPrivate instance.
+ *
+ * At the moment, the following methods for file watching
+ * are supported:
+ * - Polling: All files to be watched are polled regularly
+ * using stat (more precise: TQFileInfo.lastModified()).
+ * The polling frequency is determined from global tdeconfig
+ * settings, defaulting to 500 ms for local directories
+ * and 5000 ms for remote mounts
+ * - FAM (File Alternation Monitor): first used on IRIX, SGI
+ * has ported this method to LINUX. It uses a kernel part
+ * (IMON, sending change events to /dev/imon) and a user
+ * level damon (fam), to which applications connect for
+ * notification of file changes. For NFS, the fam damon
+ * on the NFS server machine is used; if IMON is not built
+ * into the kernel, fam uses polling for local files.
+ * - DNOTIFY: In late LINUX 2.3.x, directory notification was
+ * introduced. By opening a directory, you can request for
+ * UNIX signals to be sent to the process when a directory
+ * is changed.
+ * - INOTIFY: In LINUX 2.6.13, inode change notification was
+ * introduced. You're now able to watch arbitrary inode's
+ * for changes, and even get notification when they're
+ * unmounted.
+ */
+
+KDirWatchPrivate::KDirWatchPrivate()
+ : rescan_timer(0, "KDirWatchPrivate::rescan_timer")
+{
+ timer = new TQTimer(this, "KDirWatchPrivate::timer");
+ connect (timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotRescan()));
+ freq = 3600000; // 1 hour as upper bound
+ statEntries = 0;
+ delayRemove = false;
+ m_ref = 0;
+
+ TDEConfigGroup config(TDEGlobal::config(), TQCString("DirWatch"));
+ m_nfsPollInterval = config.readNumEntry("NFSPollInterval", 5000);
+ m_PollInterval = config.readNumEntry("PollInterval", 500);
+
+ TQString available("Stat");
+
+ // used for FAM and DNOTIFY
+ rescan_all = false;
+ connect(&rescan_timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotRescan()));
+
+#ifdef HAVE_FAM
+ // It's possible that FAM server can't be started
+ if (FAMOpen(&fc) ==0) {
+ available += ", FAM";
+ use_fam=true;
+ sn = new TQSocketNotifier( FAMCONNECTION_GETFD(&fc),
+ TQSocketNotifier::Read, this);
+ connect( sn, TQT_SIGNAL(activated(int)),
+ this, TQT_SLOT(famEventReceived()) );
+ }
+ else {
+ kdDebug(7001) << "Can't use FAM (fam daemon not running?)" << endl;
+ use_fam=false;
+ }
+#endif
+
+#ifdef HAVE_INOTIFY
+ supports_inotify = true;
+
+ m_inotify_fd = inotify_init();
+
+ if ( m_inotify_fd <= 0 ) {
+ kdDebug(7001) << "Can't use Inotify, kernel doesn't support it" << endl;
+ supports_inotify = false;
+ }
+
+ {
+ struct utsname uts;
+ int major, minor, patch;
+ if (uname(&uts) < 0)
+ supports_inotify = false; // *shrug*
+ else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
+ supports_inotify = false; // *shrug*
+ else if( major * 1000000 + minor * 1000 + patch < 2006014 ) { // <2.6.14
+ kdDebug(7001) << "Can't use INotify, Linux kernel too old" << endl;
+ supports_inotify = false;
+ }
+ }
+
+ if ( supports_inotify ) {
+ available += ", Inotify";
+ fcntl(m_inotify_fd, F_SETFD, FD_CLOEXEC);
+
+ mSn = new TQSocketNotifier( m_inotify_fd, TQSocketNotifier::Read, this );
+ connect( mSn, TQT_SIGNAL(activated( int )), this, TQT_SLOT( slotActivated() ) );
+ }
+#endif
+
+#ifdef HAVE_DNOTIFY
+
+ // if we have inotify, disable dnotify.
+#ifdef HAVE_INOTIFY
+ supports_dnotify = !supports_inotify;
+#else
+ // otherwise, not guilty until proven guilty.
+ supports_dnotify = true;
+#endif
+
+ struct utsname uts;
+ int major, minor, patch;
+ if (uname(&uts) < 0)
+ supports_dnotify = false; // *shrug*
+ else if (sscanf(uts.release, "%d.%d.%d", &major, &minor, &patch) != 3)
+ supports_dnotify = false; // *shrug*
+ else if( major * 1000000 + minor * 1000 + patch < 2004019 ) { // <2.4.19
+ kdDebug(7001) << "Can't use DNotify, Linux kernel too old" << endl;
+ supports_dnotify = false;
+ }
+
+ if( supports_dnotify ) {
+ available += ", DNotify";
+
+ pipe(mPipe);
+ fcntl(mPipe[0], F_SETFD, FD_CLOEXEC);
+ fcntl(mPipe[1], F_SETFD, FD_CLOEXEC);
+ fcntl(mPipe[0], F_SETFL, O_NONBLOCK | fcntl(mPipe[0], F_GETFL));
+ fcntl(mPipe[1], F_SETFL, O_NONBLOCK | fcntl(mPipe[1], F_GETFL));
+ mSn = new TQSocketNotifier( mPipe[0], TQSocketNotifier::Read, this);
+ connect(mSn, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotActivated()));
+ // Install the signal handler only once
+ if ( dnotify_signal == 0 )
+ {
+ dnotify_signal = SIGRTMIN + 8;
+
+ struct sigaction act;
+ act.sa_sigaction = KDirWatchPrivate::dnotify_handler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_SIGINFO;
+#ifdef SA_RESTART
+ act.sa_flags |= SA_RESTART;
+#endif
+ sigaction(dnotify_signal, &act, NULL);
+
+ act.sa_sigaction = KDirWatchPrivate::dnotify_sigio_handler;
+ sigaction(SIGIO, &act, &old_sigio_act);
+ }
+ }
+ else
+ {
+ mPipe[0] = -1;
+ mPipe[1] = -1;
+ }
+#endif
+
+ kdDebug(7001) << "Available methods: " << available << endl;
+}
+
+/* This is called on app exit (KStaticDeleter) */
+KDirWatchPrivate::~KDirWatchPrivate()
+{
+ timer->stop();
+
+ /* remove all entries being watched */
+ removeEntries(0);
+
+#ifdef HAVE_FAM
+ if (use_fam) {
+ FAMClose(&fc);
+ kdDebug(7001) << "KDirWatch deleted (FAM closed)" << endl;
+ }
+#endif
+#ifdef HAVE_INOTIFY
+ if ( supports_inotify )
+ ::close( m_inotify_fd );
+#endif
+#ifdef HAVE_DNOTIFY
+ close(mPipe[0]);
+ close(mPipe[1]);
+#endif
+}
+
+#include <stdlib.h>
+
+void KDirWatchPrivate::slotActivated()
+{
+#ifdef HAVE_DNOTIFY
+ if ( supports_dnotify )
+ {
+ char dummy_buf[4096];
+ read(mPipe[0], &dummy_buf, 4096);
+
+ if (!rescan_timer.isActive())
+ rescan_timer.start(m_PollInterval, true /* singleshot */);
+
+ return;
+ }
+#endif
+
+#ifdef HAVE_INOTIFY
+ if ( !supports_inotify )
+ return;
+
+ int pending = -1;
+ int offset = 0;
+ char buf[4096];
+ assert( m_inotify_fd > -1 );
+ ioctl( m_inotify_fd, FIONREAD, &pending );
+
+ while ( pending > 0 ) {
+
+ if ( pending > (int)sizeof( buf ) )
+ pending = sizeof( buf );
+
+ pending = read( m_inotify_fd, buf, pending);
+
+ while ( pending > 0 ) {
+ struct inotify_event *event = (struct inotify_event *) &buf[offset];
+ pending -= sizeof( struct inotify_event ) + event->len;
+ offset += sizeof( struct inotify_event ) + event->len;
+
+ TQString path;
+ if ( event->len )
+ path = TQFile::decodeName( TQCString( event->name, event->len ) );
+
+ if ( path.length() && isNoisyFile( path.latin1() ) )
+ continue;
+
+ kdDebug(7001) << "ev wd: " << event->wd << " mask " << event->mask << " path: " << path << endl;
+
+ // now we're in deep trouble of finding the
+ // associated entries
+ // for now, we suck and iterate
+ for ( EntryMap::Iterator it = m_mapEntries.begin();
+ it != m_mapEntries.end(); ++it ) {
+ Entry* e = &( *it );
+ if ( e->wd == event->wd ) {
+ e->dirty = true;
+
+ if ( 1 || e->isDir) {
+ if( event->mask & IN_DELETE_SELF) {
+ kdDebug(7001) << "-->got deleteself signal for " << e->path << endl;
+ e->m_status = NonExistent;
+ if (e->isDir)
+ addEntry(0, TQDir::cleanDirPath(e->path+"/.."), e, true);
+ else
+ addEntry(0, TQFileInfo(e->path).dirPath(true), e, true);
+ }
+ if ( event->mask & IN_IGNORED ) {
+ e->wd = 0;
+ }
+ if ( event->mask & (IN_CREATE|IN_MOVED_TO) ) {
+ Entry *sub_entry = e->m_entries.first();
+ for(;sub_entry; sub_entry = e->m_entries.next())
+ if (sub_entry->path == e->path + "/" + path) break;
+
+ if (sub_entry /*&& sub_entry->isDir*/) {
+ removeEntry(0,e->path, sub_entry);
+ KDE_struct_stat stat_buf;
+ TQCString tpath = TQFile::encodeName(path);
+ KDE_stat(tpath, &stat_buf);
+
+ //sub_entry->isDir = S_ISDIR(stat_buf.st_mode);
+ //sub_entry->m_ctime = stat_buf.st_ctime;
+ //sub_entry->m_status = Normal;
+ //sub_entry->m_nlink = stat_buf.st_nlink;
+
+ if(!useINotify(sub_entry))
+ useStat(sub_entry);
+ sub_entry->dirty = true;
+ }
+ }
+ }
+
+ if (!rescan_timer.isActive())
+ rescan_timer.start(m_PollInterval, true /* singleshot */);
+
+ break; // there really should be only one matching wd
+ }
+ }
+
+ }
+ }
+#endif
+}
+
+/* In DNOTIFY/FAM mode, only entries which are marked dirty are scanned.
+ * We first need to mark all yet nonexistent, but possible created
+ * entries as dirty...
+ */
+void KDirWatchPrivate::Entry::propagate_dirty()
+{
+ for (TQPtrListIterator<Entry> sub_entry (m_entries);
+ sub_entry.current(); ++sub_entry)
+ {
+ if (!sub_entry.current()->dirty)
+ {
+ sub_entry.current()->dirty = true;
+ sub_entry.current()->propagate_dirty();
+ }
+ }
+}
+
+
+/* A KDirWatch instance is interested in getting events for
+ * this file/Dir entry.
+ */
+void KDirWatchPrivate::Entry::addClient(KDirWatch* instance)
+{
+ Client* client = m_clients.first();
+ for(;client; client = m_clients.next())
+ if (client->instance == instance) break;
+
+ if (client) {
+ client->count++;
+ return;
+ }
+
+ client = new Client;
+ client->instance = instance;
+ client->count = 1;
+ client->watchingStopped = instance->isStopped();
+ client->pending = NoChange;
+
+ m_clients.append(client);
+}
+
+void KDirWatchPrivate::Entry::removeClient(KDirWatch* instance)
+{
+ Client* client = m_clients.first();
+ for(;client; client = m_clients.next())
+ if (client->instance == instance) break;
+
+ if (client) {
+ client->count--;
+ if (client->count == 0) {
+ m_clients.removeRef(client);
+ delete client;
+ }
+ }
+}
+
+/* get number of clients */
+int KDirWatchPrivate::Entry::clients()
+{
+ int clients = 0;
+ Client* client = m_clients.first();
+ for(;client; client = m_clients.next())
+ clients += client->count;
+
+ return clients;
+}
+
+
+KDirWatchPrivate::Entry* KDirWatchPrivate::entry(const TQString& _path)
+{
+// we only support absolute paths
+ if (TQDir::isRelativePath(_path)) {
+ return 0;
+ }
+
+ TQString path = _path;
+
+ if ( path.length() > 1 && path.right(1) == "/" )
+ path.truncate( path.length() - 1 );
+
+ EntryMap::Iterator it = m_mapEntries.find( path );
+ if ( it == m_mapEntries.end() )
+ return 0;
+ else
+ return &(*it);
+}
+
+// set polling frequency for a entry and adjust global freq if needed
+void KDirWatchPrivate::useFreq(Entry* e, int newFreq)
+{
+ e->freq = newFreq;
+
+ // a reasonable frequency for the global polling timer
+ if (e->freq < freq) {
+ freq = e->freq;
+ if (timer->isActive()) timer->changeInterval(freq);
+ kdDebug(7001) << "Global Poll Freq is now " << freq << " msec" << endl;
+ }
+}
+
+
+#ifdef HAVE_FAM
+// setup FAM notification, returns false if not possible
+bool KDirWatchPrivate::useFAM(Entry* e)
+{
+ if (!use_fam) return false;
+
+ // handle FAM events to avoid deadlock
+ // (FAM sends back all files in a directory when monitoring)
+ famEventReceived();
+
+ e->m_mode = FAMMode;
+ e->dirty = false;
+
+ if (e->isDir) {
+ if (e->m_status == NonExistent) {
+ // If the directory does not exist we watch the parent directory
+ addEntry(0, TQDir::cleanDirPath(e->path+"/.."), e, true);
+ }
+ else {
+ int res =FAMMonitorDirectory(&fc, TQFile::encodeName(e->path),
+ &(e->fr), e);
+ if (res<0) {
+ e->m_mode = UnknownMode;
+ use_fam=false;
+ return false;
+ }
+ kdDebug(7001) << " Setup FAM (Req "
+ << FAMREQUEST_GETREQNUM(&(e->fr))
+ << ") for " << e->path << endl;
+ }
+ }
+ else {
+ if (e->m_status == NonExistent) {
+ // If the file does not exist we watch the directory
+ addEntry(0, TQFileInfo(e->path).dirPath(true), e, true);
+ }
+ else {
+ int res = FAMMonitorFile(&fc, TQFile::encodeName(e->path),
+ &(e->fr), e);
+ if (res<0) {
+ e->m_mode = UnknownMode;
+ use_fam=false;
+ return false;
+ }
+
+ kdDebug(7001) << " Setup FAM (Req "
+ << FAMREQUEST_GETREQNUM(&(e->fr))
+ << ") for " << e->path << endl;
+ }
+ }
+
+ // handle FAM events to avoid deadlock
+ // (FAM sends back all files in a directory when monitoring)
+ famEventReceived();
+
+ return true;
+}
+#endif
+
+
+#ifdef HAVE_DNOTIFY
+// setup DNotify notification, returns false if not possible
+bool KDirWatchPrivate::useDNotify(Entry* e)
+{
+ e->dn_fd = 0;
+ e->dirty = false;
+ if (!supports_dnotify) return false;
+
+ e->m_mode = DNotifyMode;
+
+ if (e->isDir) {
+ if (e->m_status == Normal) {
+ int fd = KDE_open(TQFile::encodeName(e->path).data(), O_RDONLY);
+ // Migrate fd to somewhere above 128. Some libraries have
+ // constructs like:
+ // fd = socket(...)
+ // if (fd > ARBITRARY_LIMIT)
+ // return error;
+ //
+ // Since programs might end up using a lot of KDirWatch objects
+ // for a rather long time the above braindamage could get
+ // triggered.
+ //
+ // By moving the kdirwatch fd's to > 128, calls like socket() will keep
+ // returning fd's < ARBITRARY_LIMIT for a bit longer.
+ int fd2 = fcntl(fd, F_DUPFD, 128);
+ if (fd2 >= 0)
+ {
+ close(fd);
+ fd = fd2;
+ }
+ if (fd<0) {
+ e->m_mode = UnknownMode;
+ return false;
+ }
+
+ int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
+ // if dependant is a file watch, we check for MODIFY & ATTRIB too
+ for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
+ if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
+
+ if(fcntl(fd, F_SETSIG, dnotify_signal) < 0 ||
+ fcntl(fd, F_NOTIFY, mask) < 0) {
+
+ kdDebug(7001) << "Not using Linux Directory Notifications."
+ << endl;
+ supports_dnotify = false;
+ ::close(fd);
+ e->m_mode = UnknownMode;
+ return false;
+ }
+
+ fd_Entry.replace(fd, e);
+ e->dn_fd = fd;
+
+ kdDebug(7001) << " Setup DNotify (fd " << fd
+ << ") for " << e->path << endl;
+ }
+ else { // NotExisting
+ addEntry(0, TQDir::cleanDirPath(e->path+"/.."), e, true);
+ }
+ }
+ else { // File
+ // we always watch the directory (DNOTIFY can't watch files alone)
+ // this notifies us about changes of files therein
+ addEntry(0, TQFileInfo(e->path).dirPath(true), e, true);
+ }
+
+ return true;
+}
+#endif
+
+#ifdef HAVE_INOTIFY
+// setup INotify notification, returns false if not possible
+bool KDirWatchPrivate::useINotify( Entry* e )
+{
+ e->wd = 0;
+ e->dirty = false;
+ if (!supports_inotify) return false;
+
+ e->m_mode = INotifyMode;
+
+ int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
+ if(!e->isDir)
+ mask |= IN_MODIFY|IN_ATTRIB;
+ else
+ mask |= IN_ONLYDIR;
+
+ // if dependant is a file watch, we check for MODIFY & ATTRIB too
+ for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next()) {
+ if (!dep->isDir) { mask |= IN_MODIFY|IN_ATTRIB; break; }
+ }
+
+ if ( ( e->wd = inotify_add_watch( m_inotify_fd,
+ TQFile::encodeName( e->path ), mask) ) > 0 )
+ return true;
+
+ if ( e->m_status == NonExistent ) {
+ if (e->isDir)
+ addEntry(0, TQDir::cleanDirPath(e->path+"/.."), e, true);
+ else
+ addEntry(0, TQFileInfo(e->path).dirPath(true), e, true);
+ return true;
+ }
+
+ return false;
+}
+#endif
+
+bool KDirWatchPrivate::useStat(Entry* e)
+{
+ if ( e->path.startsWith("/media/") || (e->path == "/media")
+ || (TDEIO::probably_slow_mounted(e->path)) )
+ useFreq(e, m_nfsPollInterval);
+ else
+ useFreq(e, m_PollInterval);
+
+ if (e->m_mode != StatMode) {
+ e->m_mode = StatMode;
+ statEntries++;
+
+ if ( statEntries == 1 ) {
+ // if this was first STAT entry (=timer was stopped)
+ timer->start(freq); // then start the timer
+ kdDebug(7001) << " Started Polling Timer, freq " << freq << endl;
+ }
+ }
+
+ kdDebug(7001) << " Setup Stat (freq " << e->freq
+ << ") for " << e->path << endl;
+
+ return true;
+}
+
+
+/* If <instance> !=0, this KDirWatch instance wants to watch at <_path>,
+ * providing in <isDir> the type of the entry to be watched.
+ * Sometimes, entries are dependant on each other: if <sub_entry> !=0,
+ * this entry needs another entry to watch himself (when notExistent).
+ */
+void KDirWatchPrivate::addEntry(KDirWatch* instance, const TQString& _path,
+ Entry* sub_entry, bool isDir)
+{
+ TQString path = _path;
+ if (path.startsWith("/dev/") || (path == "/dev"))
+ return; // Don't even go there.
+
+ if ( path.length() > 1 && path.right(1) == "/" )
+ path.truncate( path.length() - 1 );
+
+ EntryMap::Iterator it = m_mapEntries.find( path );
+ if ( it != m_mapEntries.end() )
+ {
+ if (sub_entry) {
+ (*it).m_entries.append(sub_entry);
+ kdDebug(7001) << "Added already watched Entry " << path
+ << " (for " << sub_entry->path << ")" << endl;
+
+#ifdef HAVE_DNOTIFY
+ {
+ Entry* e = &(*it);
+ if( (e->m_mode == DNotifyMode) && (e->dn_fd > 0) ) {
+ int mask = DN_DELETE|DN_CREATE|DN_RENAME|DN_MULTISHOT;
+ // if dependant is a file watch, we check for MODIFY & ATTRIB too
+ for(Entry* dep=e->m_entries.first();dep;dep=e->m_entries.next())
+ if (!dep->isDir) { mask |= DN_MODIFY|DN_ATTRIB; break; }
+ if( fcntl(e->dn_fd, F_NOTIFY, mask) < 0) { // shouldn't happen
+ ::close(e->dn_fd);
+ e->m_mode = UnknownMode;
+ fd_Entry.remove(e->dn_fd);
+ e->dn_fd = 0;
+ useStat( e );
+ }
+ }
+ }
+#endif
+
+#ifdef HAVE_INOTIFY
+ {
+ Entry* e = &(*it);
+ if( (e->m_mode == INotifyMode) && (e->wd > 0) ) {
+ int mask = IN_DELETE|IN_DELETE_SELF|IN_CREATE|IN_MOVE|IN_MOVE_SELF|IN_DONT_FOLLOW;
+ if(!e->isDir)
+ mask |= IN_MODIFY|IN_ATTRIB;
+ else
+ mask |= IN_ONLYDIR;
+
+ inotify_rm_watch (m_inotify_fd, e->wd);
+ e->wd = inotify_add_watch( m_inotify_fd, TQFile::encodeName( e->path ), mask);
+ }
+ }
+#endif
+
+ }
+ else {
+ (*it).addClient(instance);
+ kdDebug(7001) << "Added already watched Entry " << path
+ << " (now " << (*it).clients() << " clients)"
+ << TQString(TQString(" [%1]").arg(instance->name())) << endl;
+ }
+ return;
+ }
+
+ // we have a new path to watch
+
+ KDE_struct_stat stat_buf;
+ TQCString tpath = TQFile::encodeName(path);
+ bool exists = (KDE_stat(tpath, &stat_buf) == 0);
+
+ Entry newEntry;
+ m_mapEntries.insert( path, newEntry );
+ // the insert does a copy, so we have to use <e> now
+ Entry* e = &(m_mapEntries[path]);
+
+ if (exists) {
+ e->isDir = S_ISDIR(stat_buf.st_mode);
+
+ if (e->isDir && !isDir)
+ kdWarning() << "KDirWatch: " << path << " is a directory. Use addDir!" << endl;
+ else if (!e->isDir && isDir)
+ kdWarning() << "KDirWatch: " << path << " is a file. Use addFile!" << endl;
+
+ e->m_ctime = stat_buf.st_ctime;
+ e->m_status = Normal;
+ e->m_nlink = stat_buf.st_nlink;
+ }
+ else {
+ e->isDir = isDir;
+ e->m_ctime = invalid_ctime;
+ e->m_status = NonExistent;
+ e->m_nlink = 0;
+ }
+
+ e->path = path;
+ if (sub_entry)
+ e->m_entries.append(sub_entry);
+ else
+ e->addClient(instance);
+
+ kdDebug(7001) << "Added " << (e->isDir ? "Dir ":"File ") << path
+ << (e->m_status == NonExistent ? " NotExisting" : "")
+ << (sub_entry ? TQString(TQString(" for %1").arg(sub_entry->path)) : TQString(""))
+ << (instance ? TQString(TQString(" [%1]").arg(instance->name())) : TQString(""))
+ << endl;
+
+
+ // now setup the notification method
+ e->m_mode = UnknownMode;
+ e->msecLeft = 0;
+
+ if ( isNoisyFile( tpath ) )
+ return;
+
+#ifdef HAVE_FAM
+ if (useFAM(e)) return;
+#endif
+
+#ifdef HAVE_INOTIFY
+ if (useINotify(e)) return;
+#endif
+
+#ifdef HAVE_DNOTIFY
+ if (useDNotify(e)) return;
+#endif
+
+ useStat(e);
+}
+
+
+void KDirWatchPrivate::removeEntry( KDirWatch* instance,
+ const TQString& _path, Entry* sub_entry )
+{
+ kdDebug(7001) << "KDirWatchPrivate::removeEntry for '" << _path << "' sub_entry: " << sub_entry << endl;
+ Entry* e = entry(_path);
+ if (!e) {
+ kdDebug(7001) << "KDirWatchPrivate::removeEntry can't handle '" << _path << "'" << endl;
+ return;
+ }
+
+ if (sub_entry)
+ e->m_entries.removeRef(sub_entry);
+ else
+ e->removeClient(instance);
+
+ if (e->m_clients.count() || e->m_entries.count()) {
+ kdDebug(7001) << "removeEntry: unwatched " << e->path << " " << _path << endl;
+ return;
+ }
+
+ if (delayRemove) {
+ // removeList is allowed to contain any entry at most once
+ if (removeList.findRef(e)==-1)
+ removeList.append(e);
+ // now e->isValid() is false
+ return;
+ }
+
+#ifdef HAVE_FAM
+ if (e->m_mode == FAMMode) {
+ if ( e->m_status == Normal) {
+ FAMCancelMonitor(&fc, &(e->fr) );
+ kdDebug(7001) << "Cancelled FAM (Req "
+ << FAMREQUEST_GETREQNUM(&(e->fr))
+ << ") for " << e->path << endl;
+ }
+ else {
+ if (e->isDir)
+ removeEntry(0, TQDir::cleanDirPath(e->path+"/.."), e);
+ else
+ removeEntry(0, TQFileInfo(e->path).dirPath(true), e);
+ }
+ }
+#endif
+
+#ifdef HAVE_INOTIFY
+ kdDebug(7001) << "inotify remove " << ( e->m_mode == INotifyMode ) << " " << ( e->m_status == Normal ) << endl;
+ if (e->m_mode == INotifyMode) {
+ if ( e->m_status == Normal ) {
+ (void) inotify_rm_watch( m_inotify_fd, e->wd );
+ kdDebug(7001) << "Cancelled INotify (fd " <<
+ m_inotify_fd << ", " << e->wd <<
+ ") for " << e->path << endl;
+ }
+ else {
+ if (e->isDir)
+ removeEntry(0, TQDir::cleanDirPath(e->path+"/.."), e);
+ else
+ removeEntry(0, TQFileInfo(e->path).dirPath(true), e);
+ }
+ }
+#endif
+
+#ifdef HAVE_DNOTIFY
+ if (e->m_mode == DNotifyMode) {
+ if (!e->isDir) {
+ removeEntry(0, TQFileInfo(e->path).dirPath(true), e);
+ }
+ else { // isDir
+ // must close the FD.
+ if ( e->m_status == Normal) {
+ if (e->dn_fd) {
+ ::close(e->dn_fd);
+ fd_Entry.remove(e->dn_fd);
+
+ kdDebug(7001) << "Cancelled DNotify (fd " << e->dn_fd
+ << ") for " << e->path << endl;
+ e->dn_fd = 0;
+
+ }
+ }
+ else {
+ removeEntry(0, TQDir::cleanDirPath(e->path+"/.."), e);
+ }
+ }
+ }
+#endif
+
+ if (e->m_mode == StatMode) {
+ statEntries--;
+ if ( statEntries == 0 ) {
+ timer->stop(); // stop timer if lists are empty
+ kdDebug(7001) << " Stopped Polling Timer" << endl;
+ }
+ }
+
+ kdDebug(7001) << "Removed " << (e->isDir ? "Dir ":"File ") << e->path
+ << (sub_entry ? TQString(TQString(" for %1").arg(sub_entry->path)) : TQString(""))
+ << (instance ? TQString(TQString(" [%1]").arg(instance->name())) : TQString(""))
+ << endl;
+ m_mapEntries.remove( e->path ); // <e> not valid any more
+}
+
+
+/* Called from KDirWatch destructor:
+ * remove <instance> as client from all entries
+ */
+void KDirWatchPrivate::removeEntries( KDirWatch* instance )
+{
+ TQPtrList<Entry> list;
+ int minfreq = 3600000;
+
+ // put all entries where instance is a client in list
+ EntryMap::Iterator it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it ) {
+ Client* c = (*it).m_clients.first();
+ for(;c;c=(*it).m_clients.next())
+ if (c->instance == instance) break;
+ if (c) {
+ c->count = 1; // forces deletion of instance as client
+ list.append(&(*it));
+ }
+ else if ( (*it).m_mode == StatMode && (*it).freq < minfreq )
+ minfreq = (*it).freq;
+ }
+
+ for(Entry* e=list.first();e;e=list.next())
+ removeEntry(instance, e->path, 0);
+
+ if (minfreq > freq) {
+ // we can decrease the global polling frequency
+ freq = minfreq;
+ if (timer->isActive()) timer->changeInterval(freq);
+ kdDebug(7001) << "Poll Freq now " << freq << " msec" << endl;
+ }
+}
+
+// instance ==0: stop scanning for all instances
+bool KDirWatchPrivate::stopEntryScan( KDirWatch* instance, Entry* e)
+{
+ int stillWatching = 0;
+ Client* c = e->m_clients.first();
+ for(;c;c=e->m_clients.next()) {
+ if (!instance || instance == c->instance)
+ c->watchingStopped = true;
+ else if (!c->watchingStopped)
+ stillWatching += c->count;
+ }
+
+ kdDebug(7001) << instance->name() << " stopped scanning " << e->path
+ << " (now " << stillWatching << " watchers)" << endl;
+
+ if (stillWatching == 0) {
+ // if nobody is interested, we don't watch
+ e->m_ctime = invalid_ctime; // invalid
+ e->m_status = NonExistent;
+ // e->m_status = Normal;
+ }
+ return true;
+}
+
+// instance ==0: start scanning for all instances
+bool KDirWatchPrivate::restartEntryScan( KDirWatch* instance, Entry* e,
+ bool notify)
+{
+ int wasWatching = 0, newWatching = 0;
+ Client* c = e->m_clients.first();
+ for(;c;c=e->m_clients.next()) {
+ if (!c->watchingStopped)
+ wasWatching += c->count;
+ else if (!instance || instance == c->instance) {
+ c->watchingStopped = false;
+ newWatching += c->count;
+ }
+ }
+ if (newWatching == 0)
+ return false;
+
+ kdDebug(7001) << (instance ? instance->name() : "all") << " restarted scanning " << e->path
+ << " (now " << wasWatching+newWatching << " watchers)" << endl;
+
+ // restart watching and emit pending events
+
+ int ev = NoChange;
+ if (wasWatching == 0) {
+ if (!notify) {
+ KDE_struct_stat stat_buf;
+ bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
+ if (exists) {
+ e->m_ctime = stat_buf.st_ctime;
+ e->m_status = Normal;
+ e->m_nlink = stat_buf.st_nlink;
+ }
+ else {
+ e->m_ctime = invalid_ctime;
+ e->m_status = NonExistent;
+ e->m_nlink = 0;
+ }
+ }
+ e->msecLeft = 0;
+ ev = scanEntry(e);
+ }
+ emitEvent(e,ev);
+
+ return true;
+}
+
+// instance ==0: stop scanning for all instances
+void KDirWatchPrivate::stopScan(KDirWatch* instance)
+{
+ EntryMap::Iterator it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ stopEntryScan(instance, &(*it));
+}
+
+
+void KDirWatchPrivate::startScan(KDirWatch* instance,
+ bool notify, bool skippedToo )
+{
+ if (!notify)
+ resetList(instance,skippedToo);
+
+ EntryMap::Iterator it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ restartEntryScan(instance, &(*it), notify);
+
+ // timer should still be running when in polling mode
+}
+
+
+// clear all pending events, also from stopped
+void KDirWatchPrivate::resetList( KDirWatch* /*instance*/,
+ bool skippedToo )
+{
+ EntryMap::Iterator it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it ) {
+
+ Client* c = (*it).m_clients.first();
+ for(;c;c=(*it).m_clients.next())
+ if (!c->watchingStopped || skippedToo)
+ c->pending = NoChange;
+ }
+}
+
+// Return event happened on <e>
+//
+int KDirWatchPrivate::scanEntry(Entry* e)
+{
+#ifdef HAVE_FAM
+ if (e->m_mode == FAMMode) {
+ // we know nothing has changed, no need to stat
+ if(!e->dirty) return NoChange;
+ e->dirty = false;
+ }
+ if (e->isDir) return Changed;
+#endif
+
+ // Shouldn't happen: Ignore "unknown" notification method
+ if (e->m_mode == UnknownMode) return NoChange;
+
+#if defined ( HAVE_DNOTIFY ) || defined( HAVE_INOTIFY )
+ if (e->m_mode == DNotifyMode || e->m_mode == INotifyMode ) {
+ // we know nothing has changed, no need to stat
+ if(!e->dirty) return NoChange;
+ kdDebug(7001) << "scanning " << e->path << " " << e->m_status << " " << e->m_ctime << endl;
+ e->dirty = false;
+ }
+#endif
+
+ if (e->m_mode == StatMode) {
+ // only scan if timeout on entry timer happens;
+ // e.g. when using 500msec global timer, a entry
+ // with freq=5000 is only watched every 10th time
+
+ e->msecLeft -= freq;
+ if (e->msecLeft>0) return NoChange;
+ e->msecLeft += e->freq;
+ }
+
+ KDE_struct_stat stat_buf;
+ bool exists = (KDE_stat(TQFile::encodeName(e->path), &stat_buf) == 0);
+ if (exists) {
+
+ if (e->m_status == NonExistent) {
+ // ctime is the 'creation time' on windows, but with qMax
+ // we get the latest change of any kind, on any platform.
+ e->m_ctime = stat_buf.st_ctime;
+ e->m_status = Normal;
+ e->m_nlink = stat_buf.st_nlink;
+ return Created;
+ }
+
+ if ( (e->m_ctime != invalid_ctime) &&
+ ((stat_buf.st_ctime != e->m_ctime) ||
+ (stat_buf.st_nlink != (nlink_t) e->m_nlink)) ) {
+ e->m_ctime = stat_buf.st_ctime;
+ e->m_nlink = stat_buf.st_nlink;
+ return Changed;
+ }
+
+ return NoChange;
+ }
+
+ // dir/file doesn't exist
+
+ if (e->m_ctime == invalid_ctime && e->m_status == NonExistent) {
+ e->m_nlink = 0;
+ e->m_status = NonExistent;
+ return NoChange;
+ }
+
+ e->m_ctime = invalid_ctime;
+ e->m_nlink = 0;
+ e->m_status = NonExistent;
+
+ return Deleted;
+}
+
+/* Notify all interested KDirWatch instances about a given event on an entry
+ * and stored pending events. When watching is stopped, the event is
+ * added to the pending events.
+ */
+void KDirWatchPrivate::emitEvent(Entry* e, int event, const TQString &fileName)
+{
+ TQString path = e->path;
+ if (!fileName.isEmpty()) {
+ if (!TQDir::isRelativePath(fileName))
+ path = fileName;
+ else
+#ifdef Q_OS_UNIX
+ path += "/" + fileName;
+#elif defined(Q_WS_WIN)
+ //current drive is passed instead of /
+ path += TQDir::currentDirPath().left(2) + "/" + fileName;
+#endif
+ }
+
+ TQPtrListIterator<Client> cit( e->m_clients );
+ for ( ; cit.current(); ++cit )
+ {
+ Client* c = cit.current();
+
+ if (c->instance==0 || c->count==0) continue;
+
+ if (c->watchingStopped) {
+ // add event to pending...
+ if (event == Changed)
+ c->pending |= event;
+ else if (event == Created || event == Deleted)
+ c->pending = event;
+ continue;
+ }
+ // not stopped
+ if (event == NoChange || event == Changed)
+ event |= c->pending;
+ c->pending = NoChange;
+ if (event == NoChange) continue;
+
+ if (event & Deleted) {
+ c->instance->setDeleted(path);
+ // emit only Deleted event...
+ continue;
+ }
+
+ if (event & Created) {
+ c->instance->setCreated(path);
+ // possible emit Change event after creation
+ }
+
+ if (event & Changed)
+ c->instance->setDirty(path);
+ }
+}
+
+// Remove entries which were marked to be removed
+void KDirWatchPrivate::slotRemoveDelayed()
+{
+ Entry* e;
+ delayRemove = false;
+ for(e=removeList.first();e;e=removeList.next())
+ removeEntry(0, e->path, 0);
+ removeList.clear();
+}
+
+/* Scan all entries to be watched for changes. This is done regularly
+ * when polling and once after a DNOTIFY signal. This is NOT used by FAM.
+ */
+void KDirWatchPrivate::slotRescan()
+{
+ EntryMap::Iterator it;
+
+ // People can do very long things in the slot connected to dirty(),
+ // like showing a message box. We don't want to keep polling during
+ // that time, otherwise the value of 'delayRemove' will be reset.
+ bool timerRunning = timer->isActive();
+ if ( timerRunning )
+ timer->stop();
+
+ // We delay deletions of entries this way.
+ // removeDir(), when called in slotDirty(), can cause a crash otherwise
+ delayRemove = true;
+
+#if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
+ TQPtrList<Entry> dList, cList;
+#endif
+
+ if (rescan_all)
+ {
+ // mark all as dirty
+ it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ (*it).dirty = true;
+ rescan_all = false;
+ }
+ else
+ {
+ // progate dirty flag to dependant entries (e.g. file watches)
+ it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ if (((*it).m_mode == INotifyMode || (*it).m_mode == DNotifyMode) && (*it).dirty )
+ (*it).propagate_dirty();
+ }
+
+ it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it ) {
+ // we don't check invalid entries (i.e. remove delayed)
+ if (!(*it).isValid()) continue;
+
+ int ev = scanEntry( &(*it) );
+
+
+#ifdef HAVE_INOTIFY
+ if ((*it).m_mode == INotifyMode && ev == Created && (*it).wd == 0) {
+ cList.append( &(*it) );
+ if (! useINotify( &(*it) )) {
+ useStat( &(*it) );
+ }
+ }
+#endif
+
+#ifdef HAVE_DNOTIFY
+ if ((*it).m_mode == DNotifyMode) {
+ if ((*it).isDir && (ev == Deleted)) {
+ dList.append( &(*it) );
+
+ // must close the FD.
+ if ((*it).dn_fd) {
+ ::close((*it).dn_fd);
+ fd_Entry.remove((*it).dn_fd);
+ (*it).dn_fd = 0;
+ }
+ }
+
+ else if ((*it).isDir && (ev == Created)) {
+ // For created, but yet without DNOTIFYing ...
+ if ( (*it).dn_fd == 0) {
+ cList.append( &(*it) );
+ if (! useDNotify( &(*it) )) {
+ // if DNotify setup fails...
+ useStat( &(*it) );
+ }
+ }
+ }
+ }
+#endif
+
+ if ( ev != NoChange )
+ emitEvent( &(*it), ev);
+ }
+
+
+#if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
+ // Scan parent of deleted directories for new creation
+ Entry* e;
+ for(e=dList.first();e;e=dList.next())
+ addEntry(0, TQDir::cleanDirPath( e->path+"/.."), e, true);
+
+ // Remove watch of parent of new created directories
+ for(e=cList.first();e;e=cList.next())
+ removeEntry(0, TQDir::cleanDirPath( e->path+"/.."), e);
+#endif
+
+ if ( timerRunning )
+ timer->start(freq);
+
+ TQTimer::singleShot(0, this, TQT_SLOT(slotRemoveDelayed()));
+}
+
+bool KDirWatchPrivate::isNoisyFile( const char * filename )
+{
+ // $HOME/.X.err grows with debug output, so don't notify change
+ if ( *filename == '.') {
+ if (strncmp(filename, ".X.err", 6) == 0) return true;
+ if (strncmp(filename, ".xsession-errors", 16) == 0) return true;
+ // fontconfig updates the cache on every KDE app start
+ // (inclusive kio_thumbnail slaves)
+ if (strncmp(filename, ".fonts.cache", 12) == 0) return true;
+ }
+
+ return false;
+}
+
+#ifdef HAVE_FAM
+void KDirWatchPrivate::famEventReceived()
+{
+ static FAMEvent fe;
+
+ delayRemove = true;
+
+ while(use_fam && FAMPending(&fc)) {
+ if (FAMNextEvent(&fc, &fe) == -1) {
+ kdWarning(7001) << "FAM connection problem, switching to polling."
+ << endl;
+ use_fam = false;
+ delete sn; sn = 0;
+
+ // Replace all FAMMode entries with DNotify/Stat
+ EntryMap::Iterator it;
+ it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ if ((*it).m_mode == FAMMode && (*it).m_clients.count()>0) {
+#ifdef HAVE_INOTIFY
+ if (useINotify( &(*it) )) continue;
+#endif
+#ifdef HAVE_DNOTIFY
+ if (useDNotify( &(*it) )) continue;
+#endif
+ useStat( &(*it) );
+ }
+ }
+ else
+ checkFAMEvent(&fe);
+ }
+
+ TQTimer::singleShot(0, this, TQT_SLOT(slotRemoveDelayed()));
+}
+
+void KDirWatchPrivate::checkFAMEvent(FAMEvent* fe)
+{
+ // Don't be too verbose ;-)
+ if ((fe->code == FAMExists) ||
+ (fe->code == FAMEndExist) ||
+ (fe->code == FAMAcknowledge)) return;
+
+ if ( isNoisyFile( fe->filename ) )
+ return;
+
+ Entry* e = 0;
+ EntryMap::Iterator it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it )
+ if (FAMREQUEST_GETREQNUM(&( (*it).fr )) ==
+ FAMREQUEST_GETREQNUM(&(fe->fr)) ) {
+ e = &(*it);
+ break;
+ }
+
+ // Entry* e = static_cast<Entry*>(fe->userdata);
+
+#if 0 // #88538
+ kdDebug(7001) << "Processing FAM event ("
+ << ((fe->code == FAMChanged) ? "FAMChanged" :
+ (fe->code == FAMDeleted) ? "FAMDeleted" :
+ (fe->code == FAMStartExecuting) ? "FAMStartExecuting" :
+ (fe->code == FAMStopExecuting) ? "FAMStopExecuting" :
+ (fe->code == FAMCreated) ? "FAMCreated" :
+ (fe->code == FAMMoved) ? "FAMMoved" :
+ (fe->code == FAMAcknowledge) ? "FAMAcknowledge" :
+ (fe->code == FAMExists) ? "FAMExists" :
+ (fe->code == FAMEndExist) ? "FAMEndExist" : "Unknown Code")
+ << ", " << fe->filename
+ << ", Req " << FAMREQUEST_GETREQNUM(&(fe->fr))
+ << ")" << endl;
+#endif
+
+ if (!e) {
+ // this happens e.g. for FAMAcknowledge after deleting a dir...
+ // kdDebug(7001) << "No entry for FAM event ?!" << endl;
+ return;
+ }
+
+ if (e->m_status == NonExistent) {
+ kdDebug(7001) << "FAM event for nonExistent entry " << e->path << endl;
+ return;
+ }
+
+ // Delayed handling. This rechecks changes with own stat calls.
+ e->dirty = true;
+ if (!rescan_timer.isActive())
+ rescan_timer.start(m_PollInterval, true);
+
+ // needed FAM control actions on FAM events
+ if (e->isDir)
+ switch (fe->code)
+ {
+ case FAMDeleted:
+ // file absolute: watched dir
+ if (!TQDir::isRelativePath(fe->filename))
+ {
+ // a watched directory was deleted
+
+ e->m_status = NonExistent;
+ FAMCancelMonitor(&fc, &(e->fr) ); // needed ?
+ kdDebug(7001) << "Cancelled FAMReq "
+ << FAMREQUEST_GETREQNUM(&(e->fr))
+ << " for " << e->path << endl;
+ // Scan parent for a new creation
+ addEntry(0, TQDir::cleanDirPath( e->path+"/.."), e, true);
+ }
+ break;
+
+ case FAMCreated: {
+ // check for creation of a directory we have to watch
+ Entry *sub_entry = e->m_entries.first();
+ for(;sub_entry; sub_entry = e->m_entries.next())
+ if (sub_entry->path == e->path + "/" + fe->filename) break;
+ if (sub_entry && sub_entry->isDir) {
+ TQString path = e->path;
+ removeEntry(0,e->path,sub_entry); // <e> can be invalid here!!
+ sub_entry->m_status = Normal;
+ if (!useFAM(sub_entry))
+#ifdef HAVE_INOTIFY
+ if (!useINotify(sub_entry ))
+#endif
+ useStat(sub_entry);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+#else
+void KDirWatchPrivate::famEventReceived() {}
+#endif
+
+
+void KDirWatchPrivate::statistics()
+{
+ EntryMap::Iterator it;
+
+ kdDebug(7001) << "Entries watched:" << endl;
+ if (m_mapEntries.count()==0) {
+ kdDebug(7001) << " None." << endl;
+ }
+ else {
+ it = m_mapEntries.begin();
+ for( ; it != m_mapEntries.end(); ++it ) {
+ Entry* e = &(*it);
+ kdDebug(7001) << " " << e->path << " ("
+ << ((e->m_status==Normal)?"":"Nonexistent ")
+ << (e->isDir ? "Dir":"File") << ", using "
+ << ((e->m_mode == FAMMode) ? "FAM" :
+ (e->m_mode == INotifyMode) ? "INotify" :
+ (e->m_mode == DNotifyMode) ? "DNotify" :
+ (e->m_mode == StatMode) ? "Stat" : "Unknown Method")
+ << ")" << endl;
+
+ Client* c = e->m_clients.first();
+ for(;c; c = e->m_clients.next()) {
+ TQString pending;
+ if (c->watchingStopped) {
+ if (c->pending & Deleted) pending += "deleted ";
+ if (c->pending & Created) pending += "created ";
+ if (c->pending & Changed) pending += "changed ";
+ if (!pending.isEmpty()) pending = " (pending: " + pending + ")";
+ pending = ", stopped" + pending;
+ }
+ kdDebug(7001) << " by " << c->instance->name()
+ << " (" << c->count << " times)"
+ << pending << endl;
+ }
+ if (e->m_entries.count()>0) {
+ kdDebug(7001) << " dependent entries:" << endl;
+ Entry* d = e->m_entries.first();
+ for(;d; d = e->m_entries.next()) {
+ kdDebug(7001) << " " << d << endl;
+ kdDebug(7001) << " " << d->path << " (" << d << ") " << endl;
+ }
+ }
+ }
+ }
+}
+
+
+//
+// Class KDirWatch
+//
+
+static KStaticDeleter<KDirWatch> sd_dw;
+KDirWatch* KDirWatch::s_pSelf = 0L;
+
+KDirWatch* KDirWatch::self()
+{
+ if ( !s_pSelf ) {
+ sd_dw.setObject( s_pSelf, new KDirWatch );
+ }
+
+ return s_pSelf;
+}
+
+bool KDirWatch::exists()
+{
+ return s_pSelf != 0;
+}
+
+KDirWatch::KDirWatch (TQObject* parent, const char* name)
+ : TQObject(parent,name)
+{
+ if (!name) {
+ static int nameCounter = 0;
+
+ nameCounter++;
+ setName(TQString(TQString("KDirWatch-%1").arg(nameCounter)).ascii());
+ }
+
+ if (!dwp_self)
+ dwp_self = new KDirWatchPrivate;
+ d = dwp_self;
+ d->ref();
+
+ _isStopped = false;
+}
+
+KDirWatch::~KDirWatch()
+{
+ d->removeEntries(this);
+ if ( d->deref() )
+ {
+ // delete it if it's the last one
+ delete d;
+ dwp_self = 0L;
+ }
+}
+
+
+// TODO: add watchFiles/recursive support
+void KDirWatch::addDir( const TQString& _path,
+ bool watchFiles, bool recursive)
+{
+ if (watchFiles || recursive) {
+ kdDebug(7001) << "addDir - recursive/watchFiles not supported yet in KDE 3.x" << endl;
+ }
+ if (d) d->addEntry(this, _path, 0, true);
+}
+
+void KDirWatch::addFile( const TQString& _path )
+{
+ if (d) d->addEntry(this, _path, 0, false);
+}
+
+TQDateTime KDirWatch::ctime( const TQString &_path )
+{
+ KDirWatchPrivate::Entry* e = d->entry(_path);
+
+ if (!e)
+ return TQDateTime();
+
+ TQDateTime result;
+ result.setTime_t(e->m_ctime);
+ return result;
+}
+
+void KDirWatch::removeDir( const TQString& _path )
+{
+ if (d) d->removeEntry(this, _path, 0);
+}
+
+void KDirWatch::removeFile( const TQString& _path )
+{
+ if (d) d->removeEntry(this, _path, 0);
+}
+
+bool KDirWatch::stopDirScan( const TQString& _path )
+{
+ if (d) {
+ KDirWatchPrivate::Entry *e = d->entry(_path);
+ if (e && e->isDir) return d->stopEntryScan(this, e);
+ }
+ return false;
+}
+
+bool KDirWatch::restartDirScan( const TQString& _path )
+{
+ if (d) {
+ KDirWatchPrivate::Entry *e = d->entry(_path);
+ if (e && e->isDir)
+ // restart without notifying pending events
+ return d->restartEntryScan(this, e, false);
+ }
+ return false;
+}
+
+void KDirWatch::stopScan()
+{
+ if (d) d->stopScan(this);
+ _isStopped = true;
+}
+
+void KDirWatch::startScan( bool notify, bool skippedToo )
+{
+ _isStopped = false;
+ if (d) d->startScan(this, notify, skippedToo);
+}
+
+
+bool KDirWatch::contains( const TQString& _path ) const
+{
+ KDirWatchPrivate::Entry* e = d->entry(_path);
+ if (!e)
+ return false;
+
+ KDirWatchPrivate::Client* c = e->m_clients.first();
+ for(;c;c=e->m_clients.next())
+ if (c->instance == this) return true;
+
+ return false;
+}
+
+void KDirWatch::statistics()
+{
+ if (!dwp_self) {
+ kdDebug(7001) << "KDirWatch not used" << endl;
+ return;
+ }
+ dwp_self->statistics();
+}
+
+
+void KDirWatch::setCreated( const TQString & _file )
+{
+ kdDebug(7001) << name() << " emitting created " << _file << endl;
+ emit created( _file );
+}
+
+void KDirWatch::setDirty( const TQString & _file )
+{
+ kdDebug(7001) << name() << " emitting dirty " << _file << endl;
+ emit dirty( _file );
+}
+
+void KDirWatch::setDeleted( const TQString & _file )
+{
+ kdDebug(7001) << name() << " emitting deleted " << _file << endl;
+ emit deleted( _file );
+}
+
+KDirWatch::Method KDirWatch::internalMethod()
+{
+#ifdef HAVE_FAM
+ if (d->use_fam)
+ return KDirWatch::FAM;
+#endif
+#ifdef HAVE_INOTIFY
+ if (d->supports_inotify)
+ return KDirWatch::INotify;
+#endif
+#ifdef HAVE_DNOTIFY
+ if (d->supports_dnotify)
+ return KDirWatch::DNotify;
+#endif
+ return KDirWatch::Stat;
+}
+
+
+#include "kdirwatch.moc"
+#include "kdirwatch_p.moc"
+
+//sven
+
+// vim: sw=2 ts=8 et
diff --git a/tdeio/tdeio/kdirwatch.h b/tdeio/tdeio/kdirwatch.h
new file mode 100644
index 000000000..4abaa302e
--- /dev/null
+++ b/tdeio/tdeio/kdirwatch.h
@@ -0,0 +1,290 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1998 Sven Radej <sven@lisa.exp.univie.ac.at>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef _KDIRWATCH_H
+#define _KDIRWATCH_H
+
+#include <tqtimer.h>
+#include <tqdatetime.h>
+#include <tqmap.h>
+
+#include <tdelibs_export.h>
+
+#define kdirwatch KDirWatch::self()
+
+class KDirWatchPrivate;
+
+ /**
+ * Watch directories and files for changes.
+ * The watched directories or files don't have to exist yet.
+ *
+ * When a watched directory is changed, i.e. when files therein are
+ * created or deleted, KDirWatch will emit the signal dirty().
+ *
+ * When a watched, but previously not existing directory gets created,
+ * KDirWatch will emit the signal created().
+ *
+ * When a watched directory gets deleted, KDirWatch will emit the
+ * signal deleted(). The directory is still watched for new
+ * creation.
+ *
+ * When a watched file is changed, i.e. attributes changed or written
+ * to, KDirWatch will emit the signal dirty().
+ *
+ * Scanning of particular directories or files can be stopped temporarily
+ * and restarted. The whole class can be stopped and restarted.
+ * Directories and files can be added/removed from the list in any state.
+ *
+ * The implementation uses the FAM service when available;
+ * if FAM is not available, the DNOTIFY functionality is used on LINUX.
+ * As a last resort, a regular polling for change of modification times
+ * is done; the polling interval is a global config option:
+ * DirWatch/PollInterval and DirWatch/NFSPollInterval for NFS mounted
+ * directories.
+ *
+ * @see self()
+ * @short Class for watching directory and file changes.
+ * @author Sven Radej <sven@lisa.exp.univie.ac.at>
+ */
+class TDEIO_EXPORT KDirWatch : public TQObject
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Constructor.
+ *
+ * Scanning begins immediately when a dir/file watch
+ * is added.
+ * @param parent the parent of the TQObject (or 0 for parent-less KDataTools)
+ * @param name the name of the TQObject, can be 0
+ */
+ KDirWatch (TQObject* parent = 0, const char* name = 0);
+
+ /**
+ * Destructor.
+ *
+ * Stops scanning and cleans up.
+ */
+ ~KDirWatch();
+
+ /**
+ * Adds a directory to be watched.
+ *
+ * The directory does not have to exist. When @p watchFiles is
+ * false (the default), the signals dirty(), created(), deleted()
+ * can be emitted, all for the watched directory.
+ * When @p watchFiles is true, all files in the watched directory
+ * are watched for changes, too. Thus, the signals dirty(),
+ * created(), deleted() can be emitted.
+ *
+ * @param path the path to watch
+ * @param watchFiles if true, the KDirWatch will also watch files - NOT IMPLEMENTED YET
+ * @param recursive if true, all sub directories are also watched - NOT IMPLEMENTED YET
+ */
+ void addDir(const TQString& path,
+ bool watchFiles = false, bool recursive = false);
+
+ /**
+ * Adds a file to be watched.
+ * @param file the file to watch
+ */
+ void addFile(const TQString& file);
+
+ /**
+ * Returns the time the directory/file was last changed.
+ * @param path the file to check
+ * @return the date of the last modification
+ */
+ TQDateTime ctime(const TQString& path);
+
+ /**
+ * Removes a directory from the list of scanned directories.
+ *
+ * If specified path is not in the list this does nothing.
+ * @param path the path of the dir to be removed from the list
+ */
+ void removeDir(const TQString& path);
+
+ /**
+ * Removes a file from the list of watched files.
+ *
+ * If specified path is not in the list this does nothing.
+ * @param file the file to be removed from the list
+ */
+ void removeFile(const TQString& file);
+
+ /**
+ * Stops scanning the specified path.
+ *
+ * The @p path is not deleted from the interal just, it is just skipped.
+ * Call this function when you perform an huge operation
+ * on this directory (copy/move big files or many files). When finished,
+ * call restartDirScan(path).
+ *
+ * @param path the path to skip
+ * @return true if the @p path is being watched, otherwise false
+ * @see restartDirScanning()
+ */
+ bool stopDirScan(const TQString& path);
+
+ /**
+ * Restarts scanning for specified path.
+ *
+ * Resets ctime. It doesn't notify
+ * the change (by emitted a signal), since the ctime value is reset.
+ *
+ * Call it when you are finished with big operations on that path,
+ * @em and when @em you have refreshed that path.
+ *
+ * @param path the path to restart scanning
+ * @return true if the @p path is being watched, otherwise false
+ * @see stopDirScanning()
+ */
+ bool restartDirScan(const TQString& path);
+
+ /**
+ * Starts scanning of all dirs in list.
+ *
+ * @param notify If true, all changed directories (since
+ * stopScan() call) will be notified for refresh. If notify is
+ * false, all ctimes will be reset (except those who are stopped,
+ * but only if @p skippedToo is false) and changed dirs won't be
+ * notified. You can start scanning even if the list is
+ * empty. First call should be called with @p false or else all
+ * directories
+ * in list will be notified.
+ * @param skippedToo if true, the skipped directoris (scanning of which was
+ * stopped with stopDirScan() ) will be reset and notified
+ * for change. Otherwise, stopped directories will continue to be
+ * unnotified.
+ */
+ void startScan( bool notify=false, bool skippedToo=false );
+
+ /**
+ * Stops scanning of all directories in internal list.
+ *
+ * The timer is stopped, but the list is not cleared.
+ */
+ void stopScan();
+
+ /**
+ * Is scanning stopped?
+ * After creation of a KDirWatch instance, this is false.
+ * @return true when scanning stopped
+ */
+ bool isStopped() { return _isStopped; }
+
+ /**
+ * Check if a directory is being watched by this KDirWatch instance
+ * @param path the directory to check
+ * @return true if the directory is being watched
+ */
+ bool contains( const TQString& path ) const;
+
+ /**
+ * Dump statistic information about all KDirWatch instances.
+ * This checks for consistency, too.
+ */
+ static void statistics();
+
+ /**
+ * Emits created().
+ * @param path the path of the file or directory
+ */
+ void setCreated( const TQString &path );
+ /**
+ * Emits dirty().
+ * @param path the path of the file or directory
+ */
+ void setDirty( const TQString &path );
+ /**
+ * Emits deleted().
+ * @param path the path of the file or directory
+ */
+ void setDeleted( const TQString &path );
+
+ enum Method { FAM, DNotify, Stat, INotify };
+ /**
+ * Returns the preferred internal method to
+ * watch for changes.
+ * @since 3.2
+ */
+ Method internalMethod();
+
+ /**
+ * The KDirWatch instance usually globally used in an application.
+ * It is automatically deleted when the application exits.
+ *
+ * However, you can create an arbitrary number of KDirWatch instances
+ * aside from this one - for those you have to take care of memory management.
+ *
+ * This function returns an instance of KDirWatch. If there is none, it
+ * will be created.
+ *
+ * @return a KDirWatch instance
+ */
+ static KDirWatch* self();
+ /**
+ * Returns true if there is an instance of KDirWatch.
+ * @return true if there is an instance of KDirWatch.
+ * @see KDirWatch::self()
+ * @since 3.1
+ */
+ static bool exists();
+
+ signals:
+
+ /**
+ * Emitted when a watched object is changed.
+ * For a directory this signal is emitted when files
+ * therein are created or deleted.
+ * For a file this signal is emitted when its size or attributes change.
+ *
+ * When you watch a directory, changes in the size or attributes of
+ * contained files may or may not trigger this signal to be emitted
+ * depending on which backend is used by KDirWatch.
+ *
+ * The new ctime is set before the signal is emitted.
+ * @param path the path of the file or directory
+ */
+ void dirty (const TQString &path);
+
+ /**
+ * Emitted when a file or directory is created.
+ * @param path the path of the file or directory
+ */
+ void created (const TQString &path );
+
+ /**
+ * Emitted when a file or directory is deleted.
+ *
+ * The object is still watched for new creation.
+ * @param path the path of the file or directory
+ */
+ void deleted (const TQString &path );
+
+ private:
+ bool _isStopped;
+
+ KDirWatchPrivate *d;
+ static KDirWatch* s_pSelf;
+};
+
+#endif
+
+// vim: sw=3 et
diff --git a/tdeio/tdeio/kdirwatch_p.h b/tdeio/tdeio/kdirwatch_p.h
new file mode 100644
index 000000000..8777f56b2
--- /dev/null
+++ b/tdeio/tdeio/kdirwatch_p.h
@@ -0,0 +1,158 @@
+/* Private Header for class of KDirWatchPrivate
+ *
+ * this separate header file is needed for MOC processing
+ * because KDirWatchPrivate has signals and slots
+ */
+
+#ifndef _KDIRWATCH_P_H
+#define _KDIRWATCH_P_H
+
+#ifdef HAVE_FAM
+#include <fam.h>
+#endif
+
+#include <ctime>
+
+#define invalid_ctime ((time_t)-1)
+
+/* KDirWatchPrivate is a singleton and does the watching
+ * for every KDirWatch instance in the application.
+ */
+class KDirWatchPrivate : public TQObject
+{
+ Q_OBJECT
+public:
+
+ enum entryStatus { Normal = 0, NonExistent };
+ enum entryMode { UnknownMode = 0, StatMode, DNotifyMode, INotifyMode, FAMMode };
+ enum { NoChange=0, Changed=1, Created=2, Deleted=4 };
+
+ struct Client {
+ KDirWatch* instance;
+ int count;
+ // did the instance stop watching
+ bool watchingStopped;
+ // events blocked when stopped
+ int pending;
+ };
+
+ class Entry
+ {
+ public:
+ // the last observed modification time
+ time_t m_ctime;
+ // the last observed link count
+ int m_nlink;
+ entryStatus m_status;
+ entryMode m_mode;
+ bool isDir;
+ // instances interested in events
+ TQPtrList<Client> m_clients;
+ // nonexistent entries of this directory
+ TQPtrList<Entry> m_entries;
+ TQString path;
+
+ int msecLeft, freq;
+
+ void addClient(KDirWatch*);
+ void removeClient(KDirWatch*);
+ int clients();
+ bool isValid() { return m_clients.count() || m_entries.count(); }
+
+ bool dirty;
+ void propagate_dirty();
+
+#ifdef HAVE_FAM
+ FAMRequest fr;
+#endif
+
+#ifdef HAVE_DNOTIFY
+ int dn_fd;
+#endif
+#ifdef HAVE_INOTIFY
+ int wd;
+#endif
+ };
+
+ typedef TQMap<TQString,Entry> EntryMap;
+
+ KDirWatchPrivate();
+ ~KDirWatchPrivate();
+
+ void resetList (KDirWatch*,bool);
+ void useFreq(Entry* e, int newFreq);
+ void addEntry(KDirWatch*,const TQString&, Entry*, bool);
+ void removeEntry(KDirWatch*,const TQString&, Entry*);
+ bool stopEntryScan(KDirWatch*, Entry*);
+ bool restartEntryScan(KDirWatch*, Entry*, bool );
+ void stopScan(KDirWatch*);
+ void startScan(KDirWatch*, bool, bool);
+
+ void removeEntries(KDirWatch*);
+ void statistics();
+
+ Entry* entry(const TQString&);
+ int scanEntry(Entry* e);
+ void emitEvent(Entry* e, int event, const TQString &fileName = TQString::null);
+
+ // Memory management - delete when last KDirWatch gets deleted
+ void ref() { m_ref++; }
+ bool deref() { return ( --m_ref == 0 ); }
+
+ static bool isNoisyFile( const char *filename );
+
+public slots:
+ void slotRescan();
+ void famEventReceived(); // for FAM
+ void slotActivated(); // for DNOTIFY
+ void slotRemoveDelayed();
+
+public:
+ TQTimer *timer;
+ EntryMap m_mapEntries;
+
+ int freq;
+ int statEntries;
+ int m_nfsPollInterval, m_PollInterval;
+ int m_ref;
+ bool useStat(Entry*);
+
+ bool delayRemove;
+ TQPtrList<Entry> removeList;
+
+ bool rescan_all;
+ TQTimer rescan_timer;
+
+#ifdef HAVE_FAM
+ TQSocketNotifier *sn;
+ FAMConnection fc;
+ bool use_fam;
+
+ void checkFAMEvent(FAMEvent*);
+ bool useFAM(Entry*);
+#endif
+
+#if defined(HAVE_DNOTIFY) || defined(HAVE_INOTIFY)
+ TQSocketNotifier *mSn;
+#endif
+
+#ifdef HAVE_DNOTIFY
+ bool supports_dnotify;
+ int mPipe[2];
+ TQIntDict<Entry> fd_Entry;
+
+ static void dnotify_handler(int, siginfo_t *si, void *);
+ static void dnotify_sigio_handler(int, siginfo_t *si, void *);
+ bool useDNotify(Entry*);
+#endif
+
+#ifdef HAVE_INOTIFY
+ bool supports_inotify;
+ int m_inotify_fd;
+
+ bool useINotify(Entry*);
+#endif
+};
+
+#endif // KDIRWATCH_P_H
+
diff --git a/tdeio/tdeio/kemailsettings.cpp b/tdeio/tdeio/kemailsettings.cpp
new file mode 100644
index 000000000..296455253
--- /dev/null
+++ b/tdeio/tdeio/kemailsettings.cpp
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 2000 Alex Zepeda <zipzippy@sonic.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#include "kemailsettings.h"
+
+#include <tdeconfig.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+class KEMailSettingsPrivate {
+public:
+ KEMailSettingsPrivate() : m_pConfig( 0 ) {}
+ ~KEMailSettingsPrivate() { delete m_pConfig; }
+ TDEConfig *m_pConfig;
+ TQStringList profiles;
+ TQString m_sDefaultProfile, m_sCurrentProfile;
+};
+
+TQString KEMailSettings::defaultProfileName() const
+{
+ return p->m_sDefaultProfile;
+}
+
+TQString KEMailSettings::getSetting(KEMailSettings::Setting s)
+{
+ p->m_pConfig->setGroup(TQString("PROFILE_")+p->m_sCurrentProfile);
+ switch (s) {
+ case ClientProgram: {
+ return p->m_pConfig->readEntry("EmailClient");
+ break;
+ }
+ case ClientTerminal: {
+ return ((p->m_pConfig->readBoolEntry("TerminalClient")) ? TQString("true") : TQString("false") );
+ break;
+ }
+ case RealName: {
+ return p->m_pConfig->readEntry("FullName");
+ break;
+ }
+ case EmailAddress: {
+ return p->m_pConfig->readEntry("EmailAddress");
+ break;
+ }
+ case ReplyToAddress: {
+ return p->m_pConfig->readEntry("ReplyAddr");
+ break;
+ }
+ case Organization: {
+ return p->m_pConfig->readEntry("Organization");
+ break;
+ }
+ case OutServer: {
+ return p->m_pConfig->readEntry("OutgoingServer");
+ break;
+ }
+ case OutServerLogin: {
+ return p->m_pConfig->readEntry("OutgoingUserName");
+ break;
+ }
+ case OutServerPass: {
+ return p->m_pConfig->readEntry("OutgoingPassword");
+ break;
+ }
+ case OutServerType: {
+ return p->m_pConfig->readEntry("OutgoingServerType");
+ break;
+ }
+ case OutServerCommand: {
+ return p->m_pConfig->readEntry("OutgoingCommand");
+ break;
+ }
+ case OutServerTLS: {
+ return ((p->m_pConfig->readBoolEntry("OutgoingServerTLS")) ? TQString("true") : TQString("false") );
+ break;
+ }
+ case InServer: {
+ return p->m_pConfig->readEntry("IncomingServer");
+ break;
+ }
+ case InServerLogin: {
+ return p->m_pConfig->readEntry("IncomingUserName");
+ break;
+ }
+ case InServerPass: {
+ return p->m_pConfig->readEntry("IncomingPassword");
+ break;
+ }
+ case InServerType: {
+ return p->m_pConfig->readEntry("IncomingServerType");
+ break;
+ }
+ case InServerMBXType: {
+ return p->m_pConfig->readEntry("IncomingServerMBXType");
+ break;
+ }
+ case InServerTLS: {
+ return ((p->m_pConfig->readBoolEntry("IncomingServerTLS")) ? TQString("true") : TQString("false") );
+ break;
+ }
+ };
+ return TQString::null;
+}
+void KEMailSettings::setSetting(KEMailSettings::Setting s, const TQString &v)
+{
+ p->m_pConfig->setGroup(TQString("PROFILE_")+p->m_sCurrentProfile);
+ switch (s) {
+ case ClientProgram: {
+ p->m_pConfig->writePathEntry("EmailClient", v);
+ break;
+ }
+ case ClientTerminal: {
+ p->m_pConfig->writeEntry("TerminalClient", (v == "true") ? true : false );
+ break;
+ }
+ case RealName: {
+ p->m_pConfig->writeEntry("FullName", v);
+ break;
+ }
+ case EmailAddress: {
+ p->m_pConfig->writeEntry("EmailAddress", v);
+ break;
+ }
+ case ReplyToAddress: {
+ p->m_pConfig->writeEntry("ReplyAddr", v);
+ break;
+ }
+ case Organization: {
+ p->m_pConfig->writeEntry("Organization", v);
+ break;
+ }
+ case OutServer: {
+ p->m_pConfig->writeEntry("OutgoingServer", v);
+ break;
+ }
+ case OutServerLogin: {
+ p->m_pConfig->writeEntry("OutgoingUserName", v);
+ break;
+ }
+ case OutServerPass: {
+ p->m_pConfig->writeEntry("OutgoingPassword", v);
+ break;
+ }
+ case OutServerType: {
+ p->m_pConfig->writeEntry("OutgoingServerType", v);
+ break;
+ }
+ case OutServerCommand: {
+ p->m_pConfig->writeEntry("OutgoingCommand", v);
+ break;
+ }
+ case OutServerTLS: {
+ p->m_pConfig->writeEntry("OutgoingServerTLS", (v == "true") ? true : false );
+ break;
+ }
+ case InServer: {
+ p->m_pConfig->writeEntry("IncomingServer", v);
+ break;
+ }
+ case InServerLogin: {
+ p->m_pConfig->writeEntry("IncomingUserName", v);
+ break;
+ }
+ case InServerPass: {
+ p->m_pConfig->writeEntry("IncomingPassword", v);
+ break;
+ }
+ case InServerType: {
+ p->m_pConfig->writeEntry("IncomingServerType", v);
+ break;
+ }
+ case InServerMBXType: {
+ p->m_pConfig->writeEntry("IncomingServerMBXType", v);
+ break;
+ }
+ case InServerTLS: {
+ p->m_pConfig->writeEntry("IncomingServerTLS", (v == "true") ? true : false );
+ break;
+ }
+ };
+ p->m_pConfig->sync();
+}
+
+void KEMailSettings::setDefault(const TQString &s)
+{
+ p->m_pConfig->setGroup("Defaults");
+ p->m_pConfig->writeEntry("Profile", s);
+ p->m_pConfig->sync();
+ p->m_sDefaultProfile=s;
+
+}
+
+void KEMailSettings::setProfile (const TQString &s)
+{
+ TQString groupname="PROFILE_";
+ groupname.append(s);
+ p->m_sCurrentProfile=s;
+ if (!p->m_pConfig->hasGroup(groupname)) { // Create a group if it doesn't exist
+ p->m_pConfig->setGroup(groupname);
+ p->m_pConfig->writeEntry("ServerType", TQString::null);
+ p->m_pConfig->sync();
+ p->profiles+=s;
+ }
+}
+
+TQString KEMailSettings::currentProfileName() const
+{
+ return p->m_sCurrentProfile;
+}
+
+TQStringList KEMailSettings::profiles() const
+{
+ return p->profiles;
+}
+
+KEMailSettings::KEMailSettings()
+{
+ p = new KEMailSettingsPrivate();
+ p->m_sCurrentProfile=TQString::null;
+
+ p->m_pConfig = new TDEConfig("emaildefaults");
+
+ TQStringList groups = p->m_pConfig->groupList();
+ for (TQStringList::Iterator it = groups.begin(); it != groups.end(); ++it) {
+ if ( (*it).left(8) == "PROFILE_" )
+ p->profiles+= (*it).mid(8, (*it).length());
+ }
+
+ p->m_pConfig->setGroup("Defaults");
+ p->m_sDefaultProfile=p->m_pConfig->readEntry("Profile", i18n("Default"));
+ if (!p->m_sDefaultProfile.isNull()) {
+ if (!p->m_pConfig->hasGroup(TQString("PROFILE_")+p->m_sDefaultProfile))
+ setDefault(i18n("Default"));
+ else
+ setDefault(p->m_sDefaultProfile);
+ } else {
+ if (p->profiles.count()) {
+ setDefault(p->profiles[0]);
+ } else
+ setDefault(i18n("Default"));
+ }
+ setProfile(defaultProfileName());
+}
+
+KEMailSettings::~KEMailSettings()
+{
+ delete p;
+}
diff --git a/tdeio/tdeio/kemailsettings.h b/tdeio/tdeio/kemailsettings.h
new file mode 100644
index 000000000..0ade4520e
--- /dev/null
+++ b/tdeio/tdeio/kemailsettings.h
@@ -0,0 +1,147 @@
+/*-
+ * Copyright (c) 2000 Alex Zepeda <zipzippy@sonic.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef _KEMAILSETTINGS_H
+#define _KEMAILSETTINGS_H
+
+#include <tqstring.h>
+#include <tqstringlist.h>
+
+#include <tdelibs_export.h>
+
+class KEMailSettingsPrivate;
+
+
+/**
+ * This is just a small class to facilitate accessing e-mail settings in
+ * a sane way, and allowing any program to manage multiple e-mail
+ * profiles effortlessly
+ *
+ * @author Alex Zepeda zipzippy@sonic.net
+ **/
+class TDEIO_EXPORT KEMailSettings {
+public:
+ /**
+ * The list of settings that I thought of when I wrote this
+ * class. Any extra settings thought of later can be accessed
+ * easily with getExtendedSetting and setExtendedSetting.
+ * @see getSetting()
+ * @see setSetting()
+ * @see getExtendedSetting()
+ * @see setExtendedSetting()
+ **/
+ enum Setting {
+ ClientProgram,
+ ClientTerminal,
+ RealName,
+ EmailAddress,
+ ReplyToAddress,
+ Organization,
+ OutServer,
+ OutServerLogin,
+ OutServerPass,
+ OutServerType,
+ OutServerCommand,
+ OutServerTLS,
+ InServer,
+ InServerLogin,
+ InServerPass,
+ InServerType,
+ InServerMBXType,
+ InServerTLS
+ };
+
+ /**
+ * The various extensions allowed.
+ **/
+ enum Extension {
+ POP3,
+ SMTP,
+ OTHER
+ };
+
+ /**
+ * Default constructor, just sets things up.
+ **/
+ KEMailSettings();
+
+ /**
+ * Default destructor, nothing to see here.
+ **/
+ ~KEMailSettings();
+
+ /**
+ * List of profiles available.
+ * @return the list of profiles
+ **/
+ TQStringList profiles() const;
+
+ /**
+ * Returns the name of the current profile.
+ * @returns what profile we're currently using
+ **/
+ TQString currentProfileName() const;
+
+ /**
+ * Change the current profile.
+ * @param s the name of the new profile
+ **/
+ void setProfile (const TQString &s);
+
+ /**
+ * Returns the name of the default profile.
+ * @returns the name of the one that's currently default TQString::null if none
+ **/
+ TQString defaultProfileName() const;
+
+ /**
+ * Sets a new default.
+ * @param def the new default
+ **/
+ void setDefault(const TQString &def);
+
+ /**
+ * Get one of the predefined "basic" settings.
+ * @param s the setting to get
+ * @return the value of the setting, or TQString::null if not
+ * set
+ **/
+ TQString getSetting(KEMailSettings::Setting s);
+
+ /**
+ * Set one of the predefined "basic" settings.
+ * @param s the setting to set
+ * @param v the new value of the setting, or TQString::null to
+ * unset
+ **/
+ void setSetting(KEMailSettings::Setting s, const TQString &v);
+
+private:
+ KEMailSettingsPrivate *p;
+};
+
+#endif
diff --git a/tdeio/tdeio/kfilterbase.cpp b/tdeio/tdeio/kfilterbase.cpp
new file mode 100644
index 000000000..f9250cfe9
--- /dev/null
+++ b/tdeio/tdeio/kfilterbase.cpp
@@ -0,0 +1,76 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kfilterbase.h"
+#include <klibloader.h>
+#include <kmimetype.h>
+#include <ktrader.h>
+#include <kdebug.h>
+
+KFilterBase::KFilterBase()
+ : m_dev( 0L ), m_bAutoDel( false )
+{
+}
+
+KFilterBase::~KFilterBase()
+{
+ if ( m_bAutoDel )
+ delete m_dev;
+}
+
+void KFilterBase::setDevice( TQIODevice * dev, bool autodelete )
+{
+ m_dev = dev;
+ m_bAutoDel = autodelete;
+}
+
+KFilterBase * KFilterBase::findFilterByFileName( const TQString & fileName )
+{
+ KMimeType::Ptr mime = KMimeType::findByPath( fileName );
+ kdDebug(7005) << "KFilterBase::findFilterByFileName mime=" << mime->name() << endl;
+ return findFilterByMimeType(mime->name());
+}
+
+KFilterBase * KFilterBase::findFilterByMimeType( const TQString & mimeType )
+{
+ KTrader::OfferList offers = KTrader::self()->query( "TDECompressionFilter",
+ TQString("'") + mimeType + "' in ServiceTypes" );
+ KTrader::OfferList::ConstIterator it = offers.begin();
+ KTrader::OfferList::ConstIterator end = offers.end();
+
+ kdDebug(7005) << "KFilterBase::findFilterByMimeType(" << mimeType << ") got " << offers.count() << " offers" << endl;
+ for (; it != end; ++it )
+ {
+ if ((*it)->library().isEmpty()) { continue; }
+ KLibFactory *factory = KLibLoader::self()->factory((*it)->library().latin1());
+ if (!factory) { continue; }
+ KFilterBase *filter = static_cast<KFilterBase*>( factory->create(0, (*it)->desktopEntryName().latin1() ) );
+ if ( filter )
+ return filter;
+ }
+
+ if ( mimeType == "application/x-bzip2" || mimeType == "application/x-gzip" ) // #88574
+ kdWarning(7005) << "KFilterBase::findFilterByMimeType : no filter found for " << mimeType << endl;
+
+ return 0L;
+}
+
+void KFilterBase::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+#include "kfilterbase.moc"
diff --git a/tdeio/tdeio/kfilterbase.h b/tdeio/tdeio/kfilterbase.h
new file mode 100644
index 000000000..25613c101
--- /dev/null
+++ b/tdeio/tdeio/kfilterbase.h
@@ -0,0 +1,116 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kfilterbase__h
+#define __kfilterbase__h
+
+#include <tqobject.h>
+#include <tqstring.h>
+
+#include <tdelibs_export.h>
+
+#ifdef Q_WS_WIN
+#undef ERROR //avoid conflicts
+#endif
+
+class TQIODevice;
+
+/**
+ * This is the base class for compression filters
+ * such as gzip and bzip2. It's pretty much internal.
+ * Don't use directly, use KFilterDev instead.
+ */
+class TDEIO_EXPORT KFilterBase : public TQObject // needs to inherit TQObject for KLibFactory::create
+{
+ Q_OBJECT
+public:
+ KFilterBase();
+ virtual ~KFilterBase();
+
+ /**
+ * Sets the device on which the filter will work
+ * @param dev the device on which the filter will work
+ * @param autodelete if true, @p dev is deleted when the filter is deleted
+ */
+ void setDevice( TQIODevice * dev, bool autodelete = false );
+ // Note that this isn't in the constructor, because of KLibFactory::create,
+ // but it should be called before using the filterbase !
+
+ /**
+ * Returns the device on which the filter will work.
+ * @returns the device on which the filter will work
+ */
+ TQIODevice * device() { return m_dev; }
+ /** \internal */
+ virtual void init( int mode ) = 0;
+ /** \internal */
+ virtual int mode() const = 0;
+ /** \internal */
+ virtual void terminate() {}
+ /** \internal */
+ virtual void reset() {}
+ /** \internal */
+ virtual bool readHeader() = 0;
+ /** \internal */
+ virtual bool writeHeader( const TQCString & filename ) = 0;
+ /** \internal */
+ virtual void setOutBuffer( char * data, uint maxlen ) = 0;
+ /** \internal */
+ virtual void setInBuffer( const char * data, uint size ) = 0;
+ /** \internal */
+ virtual bool inBufferEmpty() const { return inBufferAvailable() == 0; }
+ /** \internal */
+ virtual int inBufferAvailable() const = 0;
+ /** \internal */
+ virtual bool outBufferFull() const { return outBufferAvailable() == 0; }
+ /** \internal */
+ virtual int outBufferAvailable() const = 0;
+
+ /** \internal */
+ enum Result { OK, END, ERROR };
+ /** \internal */
+ virtual Result uncompress() = 0;
+ /** \internal */
+ virtual Result compress( bool finish ) = 0;
+
+ /**
+ * Call this to create the appropriate filter for the file
+ * named @p fileName.
+ * @param fileName the name of the file to filter
+ * @return the filter for the @p fileName, or 0 if not found
+ */
+ static KFilterBase * findFilterByFileName( const TQString & fileName );
+
+ /**
+ * Call this to create the appropriate filter for the mimetype
+ * @p mimeType. For instance application/x-gzip.
+ * @param mimeType the mime type of the file to filter
+ * @return the filter for the @p mimeType, or 0 if not found
+ */
+ static KFilterBase * findFilterByMimeType( const TQString & mimeType );
+
+protected:
+ TQIODevice * m_dev;
+ bool m_bAutoDel;
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KFilterBasePrivate;
+};
+
+#endif
diff --git a/tdeio/tdeio/kfilterdev.cpp b/tdeio/tdeio/kfilterdev.cpp
new file mode 100644
index 000000000..87d54f5e3
--- /dev/null
+++ b/tdeio/tdeio/kfilterdev.cpp
@@ -0,0 +1,484 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kfilterdev.h"
+#include "kfilterbase.h"
+#include <kdebug.h>
+#include <stdio.h> // for EOF
+#include <stdlib.h>
+#include <assert.h>
+#include <tqfile.h>
+
+#define BUFFER_SIZE 8*1024
+
+class KFilterDev::KFilterDevPrivate
+{
+public:
+ KFilterDevPrivate() : bNeedHeader(true), bSkipHeaders(false),
+ autoDeleteFilterBase(false), bOpenedUnderlyingDevice(false),
+ bIgnoreData(false){}
+ bool bNeedHeader;
+ bool bSkipHeaders;
+ bool autoDeleteFilterBase;
+ bool bOpenedUnderlyingDevice;
+ bool bIgnoreData;
+ TQByteArray buffer; // Used as 'input buffer' when reading, as 'output buffer' when writing
+ TQCString ungetchBuffer;
+ TQCString origFileName;
+ KFilterBase::Result result;
+};
+
+KFilterDev::KFilterDev( KFilterBase * _filter, bool autoDeleteFilterBase )
+ : filter(_filter)
+{
+ assert(filter);
+ d = new KFilterDevPrivate;
+ d->autoDeleteFilterBase = autoDeleteFilterBase;
+}
+
+KFilterDev::~KFilterDev()
+{
+ if ( isOpen() )
+ close();
+ if ( d->autoDeleteFilterBase )
+ delete filter;
+ delete d;
+}
+
+#ifndef KDE_NO_COMPAT
+//this one is static
+// Cumbersome API. To be removed in KDE 3.0.
+TQIODevice* KFilterDev::createFilterDevice(KFilterBase* base, TQFile* file)
+{
+ if (file==0)
+ return 0;
+
+ //we don't need a filter
+ if (base==0)
+ return TQT_TQIODEVICE(new TQFile(file->name())); // A bit strange IMHO. We ask for a TQFile but we create another one !?! (DF)
+
+ base->setDevice(TQT_TQIODEVICE(file));
+ return new KFilterDev(base);
+}
+#endif
+
+//static
+TQIODevice * KFilterDev::deviceForFile( const TQString & fileName, const TQString & mimetype,
+ bool forceFilter )
+{
+ TQFile * f = new TQFile( fileName );
+ KFilterBase * base = mimetype.isEmpty() ? KFilterBase::findFilterByFileName( fileName )
+ : KFilterBase::findFilterByMimeType( mimetype );
+ if ( base )
+ {
+ base->setDevice(TQT_TQIODEVICE(f), true);
+ return new KFilterDev(base, true);
+ }
+ if(!forceFilter)
+ return TQT_TQIODEVICE(f);
+ else
+ {
+ delete f;
+ return 0L;
+ }
+}
+
+TQIODevice * KFilterDev::device( TQIODevice* inDevice, const TQString & mimetype)
+{
+ return device( inDevice, mimetype, true );
+}
+
+TQIODevice * KFilterDev::device( TQIODevice* inDevice, const TQString & mimetype, bool autoDeleteInDevice )
+{
+ if (inDevice==0)
+ return 0;
+ KFilterBase * base = KFilterBase::findFilterByMimeType(mimetype);
+ if ( base )
+ {
+ base->setDevice(inDevice, autoDeleteInDevice);
+ return new KFilterDev(base, true /* auto-delete "base" */);
+ }
+ return 0;
+}
+
+bool KFilterDev::open( TQ_OpenMode mode )
+{
+ //kdDebug(7005) << "KFilterDev::open " << mode << endl;
+ if ( mode == IO_ReadOnly )
+ {
+ d->buffer.resize(0);
+ d->ungetchBuffer.resize(0);
+ }
+ else
+ {
+ d->buffer.resize( BUFFER_SIZE );
+ filter->setOutBuffer( d->buffer.data(), d->buffer.size() );
+ }
+ d->bNeedHeader = !d->bSkipHeaders;
+ filter->init( mode );
+ d->bOpenedUnderlyingDevice = !filter->device()->isOpen();
+ bool ret = d->bOpenedUnderlyingDevice ? filter->device()->open( (TQ_OpenMode)mode ) : true;
+ d->result = KFilterBase::OK;
+
+ if ( !ret )
+ kdWarning(7005) << "KFilterDev::open: Couldn't open underlying device" << endl;
+ else
+ {
+ setState( IO_Open );
+ setMode( mode );
+ }
+ TQIODevice::at(0);
+ return ret;
+}
+
+void KFilterDev::close()
+{
+ if ( !isOpen() )
+ return;
+ //kdDebug(7005) << "KFilterDev::close" << endl;
+ if ( filter->mode() == IO_WriteOnly )
+ writeBlock( 0L, 0 ); // finish writing
+ //kdDebug(7005) << "KFilterDev::close. Calling terminate()." << endl;
+
+ filter->terminate();
+ if ( d->bOpenedUnderlyingDevice )
+ filter->device()->close();
+
+ setState( 0 ); // not IO_Open
+}
+
+void KFilterDev::flush()
+{
+ //kdDebug(7005) << "KFilterDev::flush" << endl;
+ filter->device()->flush();
+ // Hmm, might not be enough...
+}
+
+#ifdef USE_QT4
+qint64 KFilterDev::size() const
+#else // USE_QT4
+TQIODevice::Offset KFilterDev::size() const
+#endif // USE_QT4
+{
+ // Well, hmm, Houston, we have a problem.
+ // We can't know the size of the uncompressed data
+ // before uncompressing it.......
+
+ // But readAll, which is not virtual, needs the size.........
+
+ kdWarning(7005) << "KFilterDev::size - can't be implemented !!!!!!!! Returning -1 " << endl;
+ //abort();
+ return (uint)-1;
+}
+
+TQIODevice::Offset KFilterDev::at() const
+{
+ return TQIODevice::at();
+}
+
+bool KFilterDev::at( TQIODevice::Offset pos )
+{
+ //kdDebug(7005) << "KFilterDev::at " << pos << " currently at " << TQIODevice::at() << endl;
+
+ if ( TQIODevice::at() == pos )
+ return true;
+
+ Q_ASSERT ( filter->mode() == IO_ReadOnly );
+
+ if ( pos == 0 )
+ {
+ TQIODevice::at(0);
+ // We can forget about the cached data
+ d->ungetchBuffer.resize(0);
+ d->bNeedHeader = !d->bSkipHeaders;
+ d->result = KFilterBase::OK;
+ filter->setInBuffer(0L,0);
+ filter->reset();
+ return filter->device()->reset();
+ }
+
+ if ( TQIODevice::at() < pos ) // we can start from here
+ pos = pos - TQIODevice::at();
+ else
+ {
+ // we have to start from 0 ! Ugly and slow, but better than the previous
+ // solution (KTarGz was allocating everything into memory)
+ if (!at(0)) // sets ioIndex to 0
+ return false;
+ }
+
+ //kdDebug(7005) << "KFilterDev::at : reading " << pos << " dummy bytes" << endl;
+ TQByteArray dummy( TQMIN( pos, 3*BUFFER_SIZE ) );
+ d->bIgnoreData = true;
+ bool result = ( (TQIODevice::Offset)readBlock( dummy.data(), pos ) == pos );
+ d->bIgnoreData = false;
+ return result;
+}
+
+bool KFilterDev::atEnd() const
+{
+ return filter->device()->atEnd() && (d->result == KFilterBase::END)
+ && d->ungetchBuffer.isEmpty();
+}
+
+TQT_TQIO_LONG KFilterDev::tqreadBlock( char *data, TQT_TQIO_ULONG maxlen )
+{
+ Q_ASSERT ( filter->mode() == IO_ReadOnly );
+ //kdDebug(7005) << "KFilterDev::readBlock maxlen=" << maxlen << endl;
+
+ uint dataReceived = 0;
+ if ( !d->ungetchBuffer.isEmpty() )
+ {
+ uint len = d->ungetchBuffer.length();
+ if ( !d->bIgnoreData )
+ {
+ while ( ( dataReceived < len ) && ( dataReceived < maxlen ) )
+ {
+ *data = d->ungetchBuffer[ len - dataReceived - 1 ];
+ data++;
+ dataReceived++;
+ }
+ }
+ else
+ {
+ dataReceived = TQMIN( len, maxlen );
+ }
+ d->ungetchBuffer.truncate( len - dataReceived );
+ TQIODevice::at(TQIODevice::at() + dataReceived);
+ }
+
+ // If we came to the end of the stream
+ // return what we got from the ungetchBuffer.
+ if ( d->result == KFilterBase::END )
+ return dataReceived;
+
+ // If we had an error, return -1.
+ if ( d->result != KFilterBase::OK )
+ return -1;
+
+
+ TQ_ULONG outBufferSize;
+ if ( d->bIgnoreData )
+ {
+ outBufferSize = TQMIN( maxlen, 3*BUFFER_SIZE );
+ }
+ else
+ {
+ outBufferSize = maxlen;
+ }
+ outBufferSize -= dataReceived;
+ TQ_ULONG availOut = outBufferSize;
+ filter->setOutBuffer( data, outBufferSize );
+
+ bool decompressedAll = false;
+ while ( dataReceived < maxlen )
+ {
+ if (filter->inBufferEmpty())
+ {
+ // Not sure about the best size to set there.
+ // For sure, it should be bigger than the header size (see comment in readHeader)
+ d->buffer.resize( BUFFER_SIZE );
+ // Request data from underlying device
+ int size = filter->device()->readBlock( d->buffer.data(),
+ d->buffer.size() );
+ if ( size )
+ filter->setInBuffer( d->buffer.data(), size );
+ else {
+ if ( decompressedAll )
+ {
+ // We decoded everything there was to decode. So -> done.
+ //kdDebug(7005) << "Seems we're done. dataReceived=" << dataReceived << endl;
+ d->result = KFilterBase::END;
+ break;
+ }
+ }
+ //kdDebug(7005) << "KFilterDev::readBlock got " << size << " bytes from device" << endl;
+ }
+ if (d->bNeedHeader)
+ {
+ (void) filter->readHeader();
+ d->bNeedHeader = false;
+ }
+
+ d->result = filter->uncompress();
+
+ if (d->result == KFilterBase::ERROR)
+ {
+ kdWarning(7005) << "KFilterDev: Error when uncompressing data" << endl;
+ break;
+ }
+
+ // We got that much data since the last time we went here
+ uint outReceived = availOut - filter->outBufferAvailable();
+ //kdDebug(7005) << "avail_out = " << filter->outBufferAvailable() << " result=" << d->result << " outReceived=" << outReceived << endl;
+ if( availOut < (uint)filter->outBufferAvailable() )
+ kdWarning(7005) << " last availOut " << availOut << " smaller than new avail_out=" << filter->outBufferAvailable() << " !" << endl;
+
+ dataReceived += outReceived;
+ if ( !d->bIgnoreData ) // Move on in the output buffer
+ {
+ data += outReceived;
+ availOut = maxlen - dataReceived;
+ }
+ else if ( maxlen - dataReceived < outBufferSize )
+ {
+ availOut = maxlen - dataReceived;
+ }
+ TQIODevice::at(TQIODevice::at() + outReceived);
+ if (d->result == KFilterBase::END)
+ {
+ //kdDebug(7005) << "KFilterDev::readBlock got END. dataReceived=" << dataReceived << endl;
+ break; // Finished.
+ }
+ if (filter->inBufferEmpty() && filter->outBufferAvailable() != 0 )
+ {
+ decompressedAll = true;
+ }
+ filter->setOutBuffer( data, availOut );
+ }
+
+ return dataReceived;
+}
+
+TQT_TQIO_LONG KFilterDev::tqwriteBlock( const char *data /*0 to finish*/, TQT_TQIO_ULONG len )
+{
+ Q_ASSERT ( filter->mode() == IO_WriteOnly );
+ // If we had an error, return 0.
+ if ( d->result != KFilterBase::OK )
+ return 0;
+
+ bool finish = (data == 0L);
+ if (!finish)
+ {
+ filter->setInBuffer( data, len );
+ if (d->bNeedHeader)
+ {
+ (void)filter->writeHeader( d->origFileName );
+ d->bNeedHeader = false;
+ }
+ }
+
+ uint dataWritten = 0;
+ uint availIn = len;
+ while ( dataWritten < len || finish )
+ {
+
+ d->result = filter->compress( finish );
+
+ if (d->result == KFilterBase::ERROR)
+ {
+ kdWarning(7005) << "KFilterDev: Error when compressing data" << endl;
+ // What to do ?
+ break;
+ }
+
+ // Wrote everything ?
+ if (filter->inBufferEmpty() || (d->result == KFilterBase::END))
+ {
+ // We got that much data since the last time we went here
+ uint wrote = availIn - filter->inBufferAvailable();
+
+ //kdDebug(7005) << " Wrote everything for now. avail_in = " << filter->inBufferAvailable() << " result=" << d->result << " wrote=" << wrote << endl;
+
+ // Move on in the input buffer
+ data += wrote;
+ dataWritten += wrote;
+ TQIODevice::at(TQIODevice::at() + wrote);
+
+ availIn = len - dataWritten;
+ //kdDebug(7005) << " KFilterDev::writeBlock availIn=" << availIn << " dataWritten=" << dataWritten << " ioIndex=" << ioIndex << endl;
+ if ( availIn > 0 ) // Not sure this will ever happen
+ filter->setInBuffer( data, availIn );
+ }
+
+ if (filter->outBufferFull() || (d->result == KFilterBase::END))
+ {
+ //kdDebug(7005) << " KFilterDev::writeBlock writing to underlying. avail_out=" << filter->outBufferAvailable() << endl;
+ int towrite = d->buffer.size() - filter->outBufferAvailable();
+ if ( towrite > 0 )
+ {
+ // Write compressed data to underlying device
+ int size = filter->device()->writeBlock( d->buffer.data(), towrite );
+ if ( size != towrite ) {
+ kdWarning(7005) << "KFilterDev::writeBlock. Could only write " << size << " out of " << towrite << " bytes" << endl;
+ return 0; // indicate an error (happens on disk full)
+ }
+ //else
+ //kdDebug(7005) << " KFilterDev::writeBlock wrote " << size << " bytes" << endl;
+ }
+ d->buffer.resize( 8*1024 );
+ filter->setOutBuffer( d->buffer.data(), d->buffer.size() );
+ if (d->result == KFilterBase::END)
+ {
+ //kdDebug(7005) << " KFilterDev::writeBlock END" << endl;
+ Q_ASSERT(finish); // hopefully we don't get end before finishing
+ break;
+ }
+ }
+ }
+
+ return dataWritten;
+}
+
+int KFilterDev::getch()
+{
+ Q_ASSERT ( filter->mode() == IO_ReadOnly );
+ //kdDebug(7005) << "KFilterDev::getch" << endl;
+ if ( !d->ungetchBuffer.isEmpty() ) {
+ int len = d->ungetchBuffer.length();
+ int ch = d->ungetchBuffer[ len-1 ];
+ d->ungetchBuffer.truncate( len - 1 );
+ TQIODevice::at(TQIODevice::at() + 1);
+ //kdDebug(7005) << "KFilterDev::getch from ungetch: " << TQString(TQChar(ch)) << endl;
+ return ch;
+ }
+ char buf[1];
+ int ret = readBlock( buf, 1 ) == 1 ? buf[0] : EOF;
+ //kdDebug(7005) << "KFilterDev::getch ret=" << TQString(TQChar(ret)) << endl;
+ return ret;
+}
+
+int KFilterDev::putch( int c )
+{
+ //kdDebug(7005) << "KFilterDev::putch" << endl;
+ char buf[1];
+ buf[0] = c;
+ return writeBlock( buf, 1 ) == 1 ? c : -1;
+}
+
+int KFilterDev::ungetch( int ch )
+{
+ //kdDebug(7005) << "KFilterDev::ungetch " << TQString(TQChar(ch)) << endl;
+ if ( ch == EOF ) // cannot unget EOF
+ return ch;
+
+ // pipe or similar => we cannot ungetch, so do it manually
+ d->ungetchBuffer +=ch;
+ TQIODevice::at(TQIODevice::at() - 1);
+ return ch;
+}
+
+void KFilterDev::setOrigFileName( const TQCString & fileName )
+{
+ d->origFileName = fileName;
+}
+
+void KFilterDev::setSkipHeaders()
+{
+ d->bSkipHeaders = true;
+}
diff --git a/tdeio/tdeio/kfilterdev.h b/tdeio/tdeio/kfilterdev.h
new file mode 100644
index 000000000..8dd0999a9
--- /dev/null
+++ b/tdeio/tdeio/kfilterdev.h
@@ -0,0 +1,204 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __kfilterdev_h
+#define __kfilterdev_h
+
+#include <tqiodevice.h>
+#include <tqstring.h>
+#include <tdelibs_export.h>
+
+class TQFile;
+class KFilterBase;
+
+/**
+ * A class for reading and writing compressed data onto a device
+ * (e.g. file, but other usages are possible, like a buffer or a socket).
+ *
+ * To simply read/write compressed files, see deviceForFile.
+ *
+ * @author David Faure <faure@kde.org>
+ */
+class TDEIO_EXPORT KFilterDev : public TQIODevice
+{
+public:
+ /**
+ * Constructs a KFilterDev for a given filter (e.g. gzip, bzip2 etc.).
+ * @param filter the KFilterBase to use
+ * @param autoDeleteFilterBase when true this object will become the
+ * owner of @p filter.
+ */
+ KFilterDev( KFilterBase * filter, bool autoDeleteFilterBase = false );
+ /**
+ * Destructs the KFilterDev.
+ * Calls close() if the filter device is still open.
+ */
+ virtual ~KFilterDev();
+
+ /**
+ * Open for reading or writing.
+ * If the KFilterBase's device is not opened, it will be opened.
+ */
+#ifdef qdoc
+#else
+ virtual bool open( TQ_OpenMode mode );
+#endif
+ /**
+ * Close after reading or writing.
+ * If the KFilterBase's device was opened by open(), it will be closed.
+ */
+ virtual void close();
+ virtual void flush();
+
+ /**
+ * For writing gzip compressed files only:
+ * set the name of the original file, to be used in the gzip header.
+ * @param fileName the name of the original file
+ */
+ void setOrigFileName( const TQCString & fileName );
+
+ /**
+ * Call this let this device skip the gzip headers when reading/writing.
+ * This way KFilterDev (with gzip filter) can be used as a direct wrapper
+ * around zlib - this is used by KZip.
+ * @since 3.1
+ */
+ void setSkipHeaders();
+
+ // Not implemented
+#ifdef qdoc
+#else
+#ifdef USE_QT4
+ virtual qint64 size() const;
+#else // USE_QT4
+ virtual TQIODevice::Offset size() const;
+#endif // USE_QT4
+#endif
+
+ virtual TQIODevice::Offset at() const;
+ /**
+ * That one can be quite slow, when going back. Use with care.
+ */
+ virtual bool at( TQIODevice::Offset );
+
+ virtual bool atEnd() const;
+
+#ifdef qdoc
+#else
+ virtual TQT_TQIO_LONG tqreadBlock( char *data, TQT_TQIO_ULONG maxlen );
+ virtual TQT_TQIO_LONG tqwriteBlock( const char *data, TQT_TQIO_ULONG len );
+#endif
+ //int readLine( char *data, uint maxlen );
+
+ virtual int getch();
+ virtual int putch( int );
+ virtual int ungetch( int );
+
+#ifdef KDE_NO_COMPAT
+private:
+#endif
+ /**
+ * Call this to create the appropriate filter device for @p base
+ * working on @p file . The returned TQIODevice has to be deleted
+ * after using.
+ * @deprecated. Use deviceForFile instead.
+ * To be removed in KDE 3.0
+ */
+ static TQIODevice* createFilterDevice(KFilterBase* base, TQFile* file) KDE_DEPRECATED;
+public:
+
+ /**
+ * Creates an i/o device that is able to read from @p fileName,
+ * whether it's compressed or not. Available compression filters
+ * (gzip/bzip2 etc.) will automatically be used.
+ *
+ * The compression filter to be used is determined from the @p fileName
+ * if @p mimetype is empty. Pass "application/x-gzip" or "application/x-bzip2"
+ * to force the corresponding decompression filter, if available.
+ *
+ * Warning: application/x-bzip2 may not be available.
+ * In that case a TQFile opened on the compressed data will be returned !
+ * Use KFilterBase::findFilterByMimeType and code similar to what
+ * deviceForFile is doing, to better control what's happening.
+ *
+ * The returned TQIODevice has to be deleted after using.
+ *
+ * @param fileName the name of the file to filter
+ * @param mimetype the mime type of the file to filter, or TQString::null if unknown
+ * @param forceFilter if true, the function will either find a compression filter, or return 0.
+ * If false, it will always return a TQIODevice. If no
+ * filter is available it will return a simple TQFile.
+ * This can be useful if the file is usable without a filter.
+ * @return if a filter has been found, the TQIODevice for the filter. If the
+ * filter does not exist, the return value depends on @p forceFilter.
+ * The returned TQIODevice has to be deleted after using.
+ */
+ static TQIODevice * deviceForFile( const TQString & fileName, const TQString & mimetype = TQString::null,
+ bool forceFilter = false );
+
+ /**
+ * Creates an i/o device that is able to read from the TQIODevice @p inDevice,
+ * whether the data is compressed or not. Available compression filters
+ * (gzip/bzip2 etc.) will automatically be used.
+ *
+ * The compression filter to be used is determined @p mimetype .
+ * Pass "application/x-gzip" or "application/x-bzip2"
+ * to use the corresponding decompression filter.
+ *
+ * Warning: application/x-bzip2 may not be available.
+ * In that case 0 will be returned !
+ *
+ * The returned TQIODevice has to be deleted after using.
+ * @param inDevice input device, becomes owned by this device! Automatically deleted!
+ * @param mimetype the mime type for the filter
+ * @return a TQIODevice that filters the original stream. Must be deleted after
+ * using
+ */
+ static TQIODevice * device( TQIODevice* inDevice, const TQString & mimetype);
+ // BIC: merge with device() method below, using default value for autoDeleteInDevice
+
+ /**
+ * Creates an i/o device that is able to read from the TQIODevice @p inDevice,
+ * whether the data is compressed or not. Available compression filters
+ * (gzip/bzip2 etc.) will automatically be used.
+ *
+ * The compression filter to be used is determined @p mimetype .
+ * Pass "application/x-gzip" or "application/x-bzip2"
+ * to use the corresponding decompression filter.
+ *
+ * Warning: application/x-bzip2 may not be available.
+ * In that case 0 will be returned !
+ *
+ * The returned TQIODevice has to be deleted after using.
+ * @param inDevice input device. Won't be deleted if @p autoDeleteInDevice = false
+ * @param mimetype the mime type for the filter
+ * @param autoDeleteInDevice if true, @p inDevice will be deleted automatically
+ * @return a TQIODevice that filters the original stream. Must be deleted after
+ * using
+ * @since 3.1
+ */
+ static TQIODevice * device( TQIODevice* inDevice, const TQString & mimetype, bool autoDeleteInDevice );
+
+private:
+ KFilterBase *filter;
+ class KFilterDevPrivate;
+ KFilterDevPrivate * d;
+};
+
+
+#endif
+
diff --git a/tdeio/tdeio/kimageio.cpp b/tdeio/tdeio/kimageio.cpp
new file mode 100644
index 000000000..e983cb945
--- /dev/null
+++ b/tdeio/tdeio/kimageio.cpp
@@ -0,0 +1,566 @@
+
+/**
+* kimgio.h -- Implementation of interface to the KDE Image IO library.
+* Sirtaj Singh Kang <taj@kde.org>, 23 Sep 1998.
+*
+* $Id$
+*
+* This library is distributed under the conditions of the GNU LGPL.
+*/
+
+#include"config.h"
+
+#include <tqdir.h>
+#include <kapplication.h>
+#include <kstandarddirs.h>
+#include <tqstring.h>
+#include <tqregexp.h>
+#include <tqvaluelist.h>
+
+#include <ltdl.h>
+#include "kimageio.h"
+#include "kimageiofactory.h"
+#include <klocale.h>
+#include <klibloader.h>
+#include <kglobal.h>
+#include <kmimetype.h>
+#include <tdesycocaentry.h>
+#include <tdesycoca.h>
+#include <kdebug.h>
+#include <kstaticdeleter.h>
+
+#include <tqimage.h>
+
+KImageIOFormat::KImageIOFormat( const TQString &path)
+ : KSycocaEntry(path)
+{
+ bLibLoaded = false;
+ mReadFunc = 0;
+ mWriteFunc = 0;
+ TDEConfig config(path, true, false);
+
+ config.setGroup("Image Format");
+ mType = config.readEntry("Type");
+ mHeader = KURL::decode_string(config.readEntry("Header"), 4); // Latin1
+ mFlags = config.readEntry("Flags");
+ bRead = config.readBoolEntry("Read");
+ bWrite = config.readBoolEntry("Write");
+ mSuffices = config.readListEntry("Suffices");
+ mPattern = config.readEntry("Name");
+ mMimetype = config.readEntry("Mimetype");
+ mLib = config.readPathEntry("Library");
+ rPaths = config.readPathListEntry("rPaths");
+}
+
+KImageIOFormat::KImageIOFormat( TQDataStream& _str, int offset) :
+ KSycocaEntry( _str, offset)
+{
+ bLibLoaded = false;
+ mReadFunc = 0;
+ mWriteFunc = 0;
+ load( _str );
+}
+
+KImageIOFormat::~KImageIOFormat()
+{
+}
+
+void
+KImageIOFormat::load( TQDataStream& _str)
+{
+ TQ_INT8 iRead, iWrite;
+ KSycocaEntry::read(_str, mType);
+ KSycocaEntry::read(_str, mHeader);
+ KSycocaEntry::read(_str, mFlags);
+ _str >> iRead >> iWrite;
+ KSycocaEntry::read(_str, mSuffices);
+ KSycocaEntry::read(_str, mMimetype);
+ KSycocaEntry::read(_str, mLib);
+ KSycocaEntry::read(_str, mPattern);
+ KSycocaEntry::read(_str, rPaths);
+ bRead = (iRead != 0);
+ bWrite = (iWrite != 0);
+}
+
+void
+KImageIOFormat::save( TQDataStream& _str)
+{
+ KSycocaEntry::save( _str );
+ TQ_INT8 iRead = bRead ? 1 : 0;
+ TQ_INT8 iWrite = bWrite ? 1 : 0;
+
+ _str << mType << mHeader << mFlags << iRead << iWrite
+ << mSuffices << mMimetype << mLib << mPattern << rPaths;
+}
+
+void
+KImageIOFormat::callLibFunc( bool read, TQImageIO *iio)
+{
+ if (!bLibLoaded)
+ {
+ if (mLib.isEmpty())
+ {
+ iio->setStatus(1); // Error
+ return;
+ }
+ TQString libpath = KLibLoader::findLibrary(mLib.ascii());
+ if ( libpath.isEmpty())
+ {
+ iio->setStatus(1); // Error
+ return;
+ }
+ lt_dlhandle libhandle = lt_dlopen( TQFile::encodeName(libpath) );
+ if (libhandle == 0) {
+ iio->setStatus(1); // error
+ kdWarning() << "KImageIOFormat::callLibFunc: couldn't dlopen " << mLib << "(" << lt_dlerror() << ")" << endl;
+ return;
+ }
+ bLibLoaded = true;
+ TQString funcName;
+ if (bRead)
+ {
+ funcName = "kimgio_"+mType.lower()+"_read";
+ lt_ptr func = lt_dlsym(libhandle, funcName.ascii());
+
+ if (func == NULL) {
+ iio->setStatus(1); // error
+ kdWarning() << "couln't find " << funcName << " (" << lt_dlerror() << ")" << endl;
+ }
+ mReadFunc = (void (*)(TQImageIO *))func;
+ }
+ if (bWrite)
+ {
+ funcName = "kimgio_"+mType.lower()+"_write";
+ lt_ptr func = lt_dlsym(libhandle, funcName.ascii());
+
+ if (func == NULL) {
+ iio->setStatus(1); // error
+ kdWarning() << "couln't find " << funcName << " (" << lt_dlerror() << ")" << endl;
+ }
+ mWriteFunc = (void (*)(TQImageIO *))func;
+ }
+
+ }
+ if (read)
+ if (mReadFunc)
+ mReadFunc(iio);
+ else
+ iio->setStatus(1); // Error
+ else
+ if (mWriteFunc)
+ mWriteFunc(iio);
+ else
+ iio->setStatus(1); // Error
+}
+
+
+KImageIOFactory *KImageIOFactory::_self = 0;
+KImageIOFormatList *KImageIOFactory::formatList = 0;
+
+static KStaticDeleter<KImageIOFormatList> kiioflsd;
+
+KImageIOFactory::KImageIOFactory() : KSycocaFactory( KST_KImageIO )
+{
+ _self = this;
+ if (m_str)
+ {
+ // read from database
+ KSycocaEntry::read(*m_str, mReadPattern);
+ KSycocaEntry::read(*m_str, mWritePattern);
+ KSycocaEntry::read(*m_str, rPath);
+ if (!formatList)
+ {
+ kiioflsd.setObject( formatList, new KImageIOFormatList());
+ lt_dlinit(); // Do this only once!
+ // Add rPaths.
+ for(TQStringList::Iterator it = rPath.begin();
+ it != rPath.end(); ++it)
+ lt_dladdsearchdir( TQFile::encodeName(*it) );
+ }
+ load();
+ }
+ else
+ if (KSycoca::self()->isBuilding())
+ {
+ // Build database
+ if (!formatList)
+ {
+ formatList = new KImageIOFormatList();
+ }
+ } else
+ {
+ // We have no database at all.. uh-oh
+ }
+}
+
+TQString
+KImageIOFactory::createPattern( KImageIO::Mode _mode)
+{
+ TQStringList patterns;
+ TQString allPatterns;
+ TQString wildCard("*.");
+ TQString separator("|");
+ for( KImageIOFormatList::ConstIterator it = formatList->begin();
+ it != formatList->end();
+ ++it )
+ {
+ KImageIOFormat *format = (*it);
+ if (((_mode == KImageIO::Reading) && format->bRead) ||
+ ((_mode == KImageIO::Writing) && format->bWrite))
+ {
+ TQString pattern;
+ TQStringList suffices = format->mSuffices;
+ for( TQStringList::ConstIterator it = suffices.begin();
+ it != suffices.end();
+ ++it)
+ {
+ if (!pattern.isEmpty())
+ pattern += " ";
+ pattern = pattern + wildCard+(*it);
+ if (!allPatterns.isEmpty())
+ allPatterns += " ";
+ allPatterns = allPatterns + wildCard +(*it);
+ }
+ if (!pattern.isEmpty())
+ {
+ pattern = pattern + separator + format->mPattern;
+ patterns.append(pattern);
+ }
+ }
+ }
+ allPatterns = allPatterns + separator + i18n("All Pictures");
+ patterns.sort();
+ patterns.prepend(allPatterns);
+
+ TQString pattern = patterns.join(TQString::fromLatin1("\n"));
+ return pattern;
+}
+
+void
+KImageIOFactory::readImage( TQImageIO *iio)
+{
+ (void) self(); // Make sure we exist
+ const char *fm = iio->format();
+ if (!fm)
+ fm = TQImageIO::imageFormat( iio->ioDevice());
+ kdDebug() << "KImageIO: readImage() format = " << fm << endl;
+
+ KImageIOFormat *format = 0;
+ for( KImageIOFormatList::ConstIterator it = formatList->begin();
+ it != formatList->end();
+ ++it )
+ {
+ format = (*it);
+ if (format->mType == fm)
+ break;
+ }
+ if (!format || !format->bRead)
+ {
+ iio->setStatus(1); // error
+ return;
+ }
+
+ format->callLibFunc( true, iio);
+}
+
+void
+KImageIOFactory::writeImage( TQImageIO *iio)
+{
+ (void) self(); // Make sure we exist
+ const char *fm = iio->format();
+ if (!fm)
+ fm = TQImageIO::imageFormat( iio->ioDevice());
+ kdDebug () << "KImageIO: writeImage() format = "<< fm << endl;
+
+ KImageIOFormat *format = 0;
+ for( KImageIOFormatList::ConstIterator it = formatList->begin();
+ it != formatList->end();
+ ++it )
+ {
+ format = (*it);
+ if (format->mType == fm)
+ break;
+ }
+ if (!format || !format->bWrite)
+ {
+ iio->setStatus(1); // error
+ return;
+ }
+
+ format->callLibFunc( false, iio);
+}
+
+void
+KImageIOFactory::load()
+{
+ KSycocaEntry::List list = allEntries();
+ for( KSycocaEntry::List::Iterator it = list.begin();
+ it != list.end();
+ ++it)
+ {
+ KSycocaEntry *entry = static_cast<KSycocaEntry *>(*it);
+ KImageIOFormat *format = static_cast<KImageIOFormat *>(entry);
+
+ // Since Qt doesn't allow us to unregister image formats
+ // we have to make sure not to add them a second time.
+ // This typically happens when the sycoca database was updated
+ // we need to reread it.
+ for( KImageIOFormatList::ConstIterator it = formatList->begin();
+ it != formatList->end();
+ ++it )
+ {
+ KImageIOFormat *_format = (*it);
+ if (format->mType == _format->mType)
+ {
+ // Already in list
+ format = 0;
+ break;
+ }
+ }
+ if (!format)
+ continue;
+ if (!format->mHeader.isEmpty() && !format->mLib.isEmpty())
+ {
+ void (*readFunc)(TQImageIO *);
+ void (*writeFunc)(TQImageIO *);
+ if (format->bRead)
+ readFunc = readImage;
+ else
+ readFunc = 0;
+ if (format->bWrite)
+ writeFunc = writeImage;
+ else
+ writeFunc = 0;
+ TQImageIO::defineIOHandler( format->mType.ascii(),
+ format->mHeader.ascii(),
+ format->mFlags.ascii(),
+ readFunc, writeFunc);
+ }
+ formatList->append( format );
+ }
+}
+
+KImageIOFactory::~KImageIOFactory()
+{
+ _self = 0;
+
+ // We would like to:
+ // * Free all KImageIOFormats.
+ // * Unload libs
+ // * Remove Qt IO handlers.
+ // But we can't remove IO handlers, so we better keep all KImageIOFormats
+ // in memory so that we can make sure not register IO handlers again whenever
+ // the sycoca database updates (Such event deletes this factory)
+}
+
+KSycocaEntry*
+KImageIOFactory::createEntry(int offset)
+{
+ KImageIOFormat *format = 0;
+ KSycocaType type;
+ TQDataStream *str = KSycoca::self()->findEntry(offset, type);
+ switch (type)
+ {
+ case KST_KImageIOFormat:
+ format = new KImageIOFormat(*str, offset);
+ break;
+ default:
+ return 0;
+ }
+ if (!format->isValid())
+ {
+ delete format;
+ format = 0;
+ }
+ return format;
+}
+
+void KImageIO::registerFormats()
+{
+ (void) KImageIOFactory::self();
+}
+
+TQString
+KImageIO::pattern(Mode _mode)
+{
+ if (_mode == Reading)
+ return KImageIOFactory::self()->mReadPattern;
+ else
+ return KImageIOFactory::self()->mWritePattern;
+}
+
+bool KImageIO::canWrite(const TQString& type)
+{
+ KImageIOFormatList *formatList = KImageIOFactory::self()->formatList;
+
+ if(formatList)
+ {
+ for( KImageIOFormatList::ConstIterator it = formatList->begin();
+ it != formatList->end();
+ ++it )
+ {
+ KImageIOFormat *format = (*it);
+ if (format->mType == type)
+ return format->bWrite;
+ }
+ }
+
+ return false;
+}
+
+bool KImageIO::canRead(const TQString& type)
+{
+ KImageIOFormatList *formatList = KImageIOFactory::self()->formatList;
+
+ if(formatList)
+ {
+ for( KImageIOFormatList::ConstIterator it = formatList->begin();
+ it != formatList->end();
+ ++it )
+ {
+ KImageIOFormat *format = (*it);
+ if (format->mType == type)
+ return format->bRead;
+ }
+ }
+
+ return false;
+}
+
+TQStringList KImageIO::types(Mode _mode ) {
+ KImageIOFormatList *formatList = KImageIOFactory::self()->formatList;
+ TQStringList types;
+
+ if(formatList)
+ {
+ for( KImageIOFormatList::ConstIterator it = formatList->begin();
+ it != formatList->end();
+ ++it )
+ {
+ KImageIOFormat *format = (*it);
+ if (((_mode == Reading) && format->bRead) ||
+ ((_mode == Writing) && format->bWrite))
+ types.append(format->mType);
+ }
+ }
+
+ return types;
+}
+
+TQString KImageIO::suffix(const TQString& type)
+{
+ KImageIOFormatList *formatList = KImageIOFactory::self()->formatList;
+
+ if(formatList)
+ {
+ for( KImageIOFormatList::ConstIterator it = formatList->begin();
+ it != formatList->end();
+ ++it )
+ {
+ KImageIOFormat *format = (*it);
+ if (format->mType == type)
+ return format->mSuffices[0];
+ }
+ }
+
+ return TQString::null;
+}
+
+TQString KImageIO::typeForMime(const TQString& mimeType)
+{
+ KImageIOFormatList *formatList = KImageIOFactory::self()->formatList;
+
+ if(formatList)
+ {
+ for( KImageIOFormatList::ConstIterator it = formatList->begin();
+ it != formatList->end();
+ ++it )
+ {
+ KImageIOFormat *format = (*it);
+ if (format->mMimetype == mimeType)
+ return format->mType;
+ }
+ }
+
+ return TQString::null;
+}
+
+TQString KImageIO::type(const TQString& filename)
+{
+ KImageIOFormatList *formatList = KImageIOFactory::self()->formatList;
+ TQString suffix = filename;
+ int dot = suffix.findRev('.');
+ if (dot >= 0)
+ suffix = suffix.mid(dot + 1);
+
+ if(formatList)
+ {
+ for( KImageIOFormatList::ConstIterator it = formatList->begin();
+ it != formatList->end();
+ ++it )
+ {
+ KImageIOFormat *format = (*it);
+ if (format->mSuffices.contains(suffix))
+ return format->mType;
+ }
+ }
+
+ return TQString::null;
+}
+
+TQStringList KImageIO::mimeTypes( Mode _mode )
+{
+ KImageIOFormatList *formatList = KImageIOFactory::self()->formatList;
+ TQStringList mimeList;
+
+ if(formatList)
+ {
+ for( KImageIOFormatList::ConstIterator it = formatList->begin();
+ it != formatList->end();
+ ++it )
+ {
+ KImageIOFormat *format = (*it);
+ if (((_mode == Reading) && format->bRead) ||
+ ((_mode == Writing) && format->bWrite))
+ if ( !format->mMimetype.isEmpty() )
+ mimeList.append ( format->mMimetype );
+ }
+ }
+
+ return mimeList;
+}
+
+bool KImageIO::isSupported( const TQString& _mimeType, Mode _mode )
+{
+ KImageIOFormatList *formatList = KImageIOFactory::self()->formatList;
+
+ if(formatList)
+ {
+ for( KImageIOFormatList::ConstIterator it = formatList->begin();
+ it != formatList->end();
+ ++it )
+ {
+ KImageIOFormat *format = (*it);
+ if (format->mMimetype == _mimeType)
+ {
+ if (((_mode == Reading) && format->bRead) ||
+ ((_mode == Writing) && format->bWrite))
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+TQString KImageIO::mimeType( const TQString& _filename )
+{
+ return KMimeType::findByURL( KURL( _filename ) )->name();
+}
+
+void KImageIOFormat::virtual_hook( int id, void* data )
+{ KSycocaEntry::virtual_hook( id, data ); }
+
+void KImageIOFactory::virtual_hook( int id, void* data )
+{ KSycocaFactory::virtual_hook( id, data ); }
+
diff --git a/tdeio/tdeio/kimageio.h b/tdeio/tdeio/kimageio.h
new file mode 100644
index 000000000..671fb5f61
--- /dev/null
+++ b/tdeio/tdeio/kimageio.h
@@ -0,0 +1,170 @@
+/*
+* kimageio.h -- Declaration of interface to the KDE Image IO library.
+* Sirtaj Singh Kang <taj@kde.org>, 23 Sep 1998.
+*
+* This library is distributed under the conditions of the GNU LGPL.
+*/
+
+#ifndef SSK_KIMGIO_H
+#define SSK_KIMGIO_H
+
+#include <tqstringlist.h>
+
+#include <tdelibs_export.h>
+
+/**
+ * Interface to the KDE Image IO plugin architecture.
+ *
+ * This library allows KDE applications to read and write images in a
+ * variety of formats, transparently via the TQImage and TQPixmap load
+ * and save methods.
+ *
+ * The image processing backends are written as image handlers compatible
+ * with the TQImageIO handler format. The backends are loaded on demand
+ * when a particular format is requested. Each format can be identified
+ * by a unique type id string.
+ *
+ * \b Formats:
+ *
+ * Currently supported formats include:
+ * @li BMP \<read\> \<write\>
+ * @li EPS \<read\> \<write\>
+ * @li EXR \<read\>
+ * @li G3 \<read\>
+ * @li GIF \<read\>
+ * @li ICO \<read\>
+ * @li JP2 \<read\> \<write\>
+ * @li JPEG \<read\> \<write\>
+ * @li NETPBM \<read\> \<write\>
+ * @li PCX \<read\> \<write\>
+ * @li PNG \<read\> \<write, only with newer libraries\>
+ * @li TGA \<read\> \<write\>
+ * @li TIFF \<read\>
+ * @li XBM \<read\> \<write\>
+ * @li XPM \<read\> \<write\>
+ * @li XV \<read\> \<write\>
+ *
+ * \b Usage:
+ *
+ * Simply call the KImageIO::registerFormats() static method declared
+ * in kimageio.h.
+ *
+ * \b Example:
+ *
+ * \code
+ * #include<tqpixmap.h>
+ * #include<kimageio.h>
+ *
+ * int main( int argc, char **argv )
+ * {
+ * ....
+ * KImageIO::registerFormats();
+ * ... // start main program
+ * }
+ * \endcode
+ *
+ * @see KImageIO, TQPixmap, TQImage, QImageIO
+ * @author Sirtaj Singh Kang
+ */
+class TDEIO_EXPORT KImageIO
+{
+public:
+ /**
+ * Possible image file access modes.
+ *
+ * Used in various KImageIO static function.
+ **/
+ enum Mode { Reading, Writing };
+
+ /**
+ * Registers all KImageIO supported formats.
+ */
+ static void registerFormats();
+
+ /**
+ * Checks if a special type is supported for writing.
+ * @param type the type id of the image type
+ * @return true if the image format can be written
+ */
+ static bool canWrite(const TQString& type);
+
+ /**
+ * Checks if a special type is supported for reading.
+ * @param type the type id of the image type
+ * @return true if the image format can be read
+ */
+ static bool canRead(const TQString& type);
+
+ /**
+ * Returns a list of all KImageIO supported formats.
+ *
+ * @param mode Tells whether to retrieve modes that can be read or written.
+ * @return a list of the type ids
+ */
+ static TQStringList types(Mode mode = Writing);
+
+
+ /**
+ * Returns a list of patterns of all KImageIO supported formats.
+ *
+ * These patterns can be passed to KFileDialog::getOpenFileName()
+ * or KFileDialog::getSaveFileName(), for example.
+ *
+ * @param mode Tells whether to retrieve modes that can be read or written.
+ * @return a space-separated list of file globs that describe the
+ * supported formats
+ */
+ static TQString pattern(Mode mode = Reading);
+
+ /**
+ * Returns the suffix of an image type.
+ * @param type the type id of the file format
+ * @return the suffix of the file format or TQString::null if it does not
+ * exist
+ */
+ static TQString suffix(const TQString& type);
+
+ /**
+ * Returns the type of a MIME type.
+ * @param mimeType the MIME type to search
+ * @return type id of the MIME type or TQString::null if the MIME type
+ * is not supported
+ * @since 3.1
+ */
+ static TQString typeForMime(const TQString& mimeType);
+
+ /**
+ * Returns the type of given filename.
+ * @param filename the filename to check
+ * @return if the file name's suffix is known the type id of the
+ * file type, otherwise TQString::null
+ */
+ static TQString type(const TQString& filename);
+
+ /**
+ * Returns a list of MIME types for all KImageIO supported formats.
+ *
+ * @param mode Tells whether to retrieve modes that can be read or written.
+ * @return a list if MIME types of the supported formats
+ */
+ static TQStringList mimeTypes( Mode mode = Writing );
+
+ /**
+ * Test to see whether a MIME type is supported to reading/writing.
+ * @param _mimeType the MIME type to check
+ * @param _mode Tells whether to check for reading or writing capabilities
+ * @return true if the type is supported
+ **/
+ static bool isSupported( const TQString& _mimeType, Mode _mode = Writing );
+
+ /**
+ * Returns the MIME type of @p _filename.
+ * @param _filename the filename to check
+ * @return the MIME type of the file, or TQString::null
+ **/
+ static TQString mimeType( const TQString& _filename );
+};
+
+
+#endif
+
diff --git a/tdeio/tdeio/kimageiofactory.h b/tdeio/tdeio/kimageiofactory.h
new file mode 100644
index 000000000..6d2d15940
--- /dev/null
+++ b/tdeio/tdeio/kimageiofactory.h
@@ -0,0 +1,142 @@
+/*
+* kimgio.h -- Declaration of interface to the KDE Image IO library.
+* Sirtaj Singh Kang <taj@kde.org>, 23 Sep 1998.
+*
+* This library is distributed under the conditions of the GNU LGPL.
+*/
+
+#ifndef SSK_KIMGIOFACTORY_H
+#define SSK_KIMGIOFACTORY_H
+
+#include "tdesycocafactory.h"
+#include "kimageio.h"
+
+class KImageIOFormat;
+class KImageIOFormatList;
+
+/** \internal */
+class TDEIO_EXPORT KImageIOFormat : public KSycocaEntry
+{
+ K_SYCOCATYPE( KST_KImageIOFormat, KSycocaEntry )
+
+public:
+ typedef KSharedPtr<KImageIOFormat> Ptr;
+ typedef TQValueList<Ptr> List;
+public: // KDoc seems to barf on those typedefs and generates no docs after them
+ /**
+ * Read a KImageIOFormat description file
+ */
+ KImageIOFormat( const TQString & path);
+
+ /**
+ * @internal construct a ImageIOFormat from a stream
+ */
+ KImageIOFormat( TQDataStream& _str, int offset);
+
+ virtual ~KImageIOFormat();
+
+ virtual TQString name() const { return mType; }
+
+ virtual bool isValid() const { return true; }
+
+ /**
+ * @internal
+ * Load the image format from a stream.
+ */
+ virtual void load(TQDataStream& );
+
+ /**
+ * @internal
+ * Save the image format to a stream.
+ */
+ virtual void save(TQDataStream& );
+
+ /**
+ * @internal
+ * Calls image IO function
+ */
+ void callLibFunc( bool read, TQImageIO *);
+
+public:
+ TQString mType;
+ TQString mHeader;
+ TQString mFlags;
+ bool bRead;
+ bool bWrite;
+ TQStringList mSuffices;
+ TQString mPattern;
+ TQString mMimetype;
+ TQString mLib;
+ TQStringList rPaths;
+ bool bLibLoaded;
+ void (*mReadFunc)(TQImageIO *);
+ void (*mWriteFunc)(TQImageIO *);
+protected:
+ virtual void virtual_hook( int id, void* data );
+};
+
+/** \internal */
+class TDEIO_EXPORT KImageIOFormatList : public KImageIOFormat::List
+{
+public:
+ KImageIOFormatList() { }
+};
+
+
+/** \internal */
+class TDEIO_EXPORT KImageIOFactory : public KSycocaFactory
+{
+ friend class KImageIO;
+ K_SYCOCAFACTORY( KST_KImageIO )
+public:
+ static KImageIOFactory *self()
+ { if (!_self) new KImageIOFactory(); return _self; }
+ KImageIOFactory();
+ virtual ~KImageIOFactory();
+
+protected: // Internal stuff
+ /**
+ * @internal
+ *
+ * Load information from database
+ */
+ void load();
+
+ /**
+ * @internal Create pattern string
+ **/
+ TQString createPattern( KImageIO::Mode _mode);
+
+ /**
+ * @internal Not used.
+ */
+ virtual KSycocaEntry *createEntry(const TQString &, const char *)
+ { return 0; }
+
+ /**
+ * @internal
+ */
+ virtual KSycocaEntry *createEntry(int offset);
+
+ /**
+ * @internal Read an image
+ **/
+ static void readImage( TQImageIO *iio);
+
+ /**
+ * @internal Write an image
+ **/
+ static void writeImage( TQImageIO *iio);
+
+protected:
+ static KImageIOFactory *_self;
+ static KImageIOFormatList *formatList;
+ TQString mReadPattern;
+ TQString mWritePattern;
+ TQStringList rPath;
+protected:
+ virtual void virtual_hook( int id, void* data );
+};
+
+#endif
+
diff --git a/tdeio/tdeio/klimitediodevice.h b/tdeio/tdeio/klimitediodevice.h
new file mode 100644
index 000000000..602ba45a0
--- /dev/null
+++ b/tdeio/tdeio/klimitediodevice.h
@@ -0,0 +1,105 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001, 2002 David Faure <david@mandrakesoft.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef klimitediodevice_h
+#define klimitediodevice_h
+
+#include <kdebug.h>
+#include <tqiodevice.h>
+/**
+ * A readonly device that reads from an underlying device
+ * from a given point to another (e.g. to give access to a single
+ * file inside an archive).
+ * @author David Faure <david@mandrakesoft.com>
+ * @since 3.1
+ */
+class TDEIO_EXPORT KLimitedIODevice : public TQIODevice
+{
+public:
+ /**
+ * Creates a new KLimitedIODevice.
+ * @param dev the underlying device, opened or not
+ * This device itself auto-opens (in readonly mode), no need to open it.
+ * @param start where to start reading (position in bytes)
+ * @param length the length of the data to read (in bytes)
+ */
+ KLimitedIODevice( TQIODevice *dev, int start, int length )
+ : m_dev( dev ), m_start( start ), m_length( length )
+ {
+ //kdDebug(7005) << "KLimitedIODevice::KLimitedIODevice start=" << start << " length=" << length << endl;
+ setType( IO_Direct ); // we support sequential too, but then atEnd() tries getch/ungetch !
+ open( IO_ReadOnly );
+ }
+ virtual ~KLimitedIODevice() {}
+
+ virtual bool open( TQ_OpenMode m ) {
+ //kdDebug(7005) << "KLimitedIODevice::open m=" << m << endl;
+ if ( m & IO_ReadOnly ) {
+ /*bool ok = false;
+ if ( m_dev->isOpen() )
+ ok = ( m_dev->mode() == IO_ReadOnly );
+ else
+ ok = m_dev->open( m );
+ if ( ok )*/
+ m_dev->at( m_start ); // No concurrent access !
+ }
+ else
+ kdWarning(7005) << "KLimitedIODevice::open only supports IO_ReadOnly!" << endl;
+ setState( IO_Open );
+ setMode( m );
+ return true;
+ }
+ virtual void close() {}
+ virtual void flush() {}
+
+#ifdef USE_QT4
+ virtual qint64 size() const { return m_length; }
+#else // USE_QT4
+ virtual Offset size() const { return m_length; }
+#endif // USE_QT4
+
+ virtual TQT_TQIO_LONG tqreadBlock ( char * data, TQT_TQIO_ULONG maxlen )
+ {
+ maxlen = TQMIN( maxlen, m_length - at() ); // Apply upper limit
+ return m_dev->readBlock( data, maxlen );
+ }
+ virtual TQT_TQIO_LONG tqwriteBlock ( const char *, TQT_TQIO_ULONG ) { return -1; } // unsupported
+ virtual int putch( int ) { return -1; } // unsupported
+
+ virtual int getch() {
+ char c[2];
+ if ( tqreadBlock(c, 1) == -1)
+ return -1;
+ else
+ return c[0];
+ }
+ virtual int ungetch( int c ) { return m_dev->ungetch(c); } // ## apply lower limit ?
+ virtual Offset at() const { return m_dev->at() - m_start; }
+ virtual bool at( Offset pos ) {
+ Q_ASSERT( pos <= m_length );
+ pos = QMIN( pos, m_length ); // Apply upper limit
+ return m_dev->at( m_start + pos );
+ }
+ virtual bool atEnd() const { return m_dev->atEnd() || m_dev->at() >= m_start + m_length; }
+private:
+ TQIODevice* m_dev;
+ TQ_ULONG m_start;
+ TQ_ULONG m_length;
+};
+
+#endif
diff --git a/tdeio/tdeio/kmdbase.h b/tdeio/tdeio/kmdbase.h
new file mode 100644
index 000000000..b0c8f8b7d
--- /dev/null
+++ b/tdeio/tdeio/kmdbase.h
@@ -0,0 +1,6 @@
+// kmdbase.h is the old name. Use #include <kmdcodec.h> from now on
+#ifdef KDE_NO_COMPAT
+#error include <kmdcodec.h> instead of <kmdbase.h>
+#else
+#include <kmdcodec.h>
+#endif
diff --git a/tdeio/tdeio/kmessageboxwrapper.h b/tdeio/tdeio/kmessageboxwrapper.h
new file mode 100644
index 000000000..3590b5e89
--- /dev/null
+++ b/tdeio/tdeio/kmessageboxwrapper.h
@@ -0,0 +1,56 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 1999 David Faure <faure@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#ifndef KMESSAGEBOXWRAPPER_H
+#define KMESSAGEBOXWRAPPER_H
+#include <kmessagebox.h>
+#include <kapplication.h>
+#include <kdebug.h>
+
+/**
+ * @internal
+ * Allows KIO classes to display dialog boxes with the correct
+ * theme/style even in non-GUI apps like kded and kfmclient
+ */
+class TDEIO_EXPORT KMessageBoxWrapper : public KMessageBox
+{
+public:
+ static void error(TQWidget *parent,
+ const TQString &text,
+ const TQString &caption = TQString::null)
+ {
+ if (TDEApplication::guiEnabled()) {
+ kapp->enableStyles();
+ KMessageBox::error( parent, text, caption );
+ } else
+ kdWarning() << text << endl;
+ }
+
+ static void sorry(TQWidget *parent,
+ const TQString &text,
+ const TQString &caption = TQString::null)
+ {
+ if (TDEApplication::guiEnabled()) {
+ kapp->enableStyles();
+ KMessageBox::sorry( parent, text, caption );
+ } else
+ kdWarning() << text << endl;
+ }
+
+};
+#endif
diff --git a/tdeio/tdeio/kmimemagic.cpp b/tdeio/tdeio/kmimemagic.cpp
new file mode 100644
index 000000000..f639a7049
--- /dev/null
+++ b/tdeio/tdeio/kmimemagic.cpp
@@ -0,0 +1,2317 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Fritz Elfert <fritz@kde.org>
+ Copyright (C) 2004 Allan Sandfeld Jensen <kde@carewolf.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#include "kmimemagic.h"
+#include <kdebug.h>
+#include <kapplication.h>
+#include <tqfile.h>
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <kstaticdeleter.h>
+#include <klargefile.h>
+#include <assert.h>
+
+static int fsmagic(struct config_rec* conf, const char *fn, KDE_struct_stat *sb);
+static void process(struct config_rec* conf, const TQString &);
+static int ascmagic(struct config_rec* conf, unsigned char *buf, int nbytes);
+static int tagmagic(unsigned char *buf, int nbytes);
+static int textmagic(struct config_rec* conf, unsigned char *, int);
+
+static void tryit(struct config_rec* conf, unsigned char *buf, int nb);
+static int match(struct config_rec* conf, unsigned char *, int);
+
+KMimeMagic* KMimeMagic::s_pSelf;
+static KStaticDeleter<KMimeMagic> kmimemagicsd;
+
+KMimeMagic* KMimeMagic::self()
+{
+ if( !s_pSelf )
+ initStatic();
+ return s_pSelf;
+}
+
+void KMimeMagic::initStatic()
+{
+ s_pSelf = kmimemagicsd.setObject( s_pSelf, new KMimeMagic() );
+ s_pSelf->setFollowLinks( true );
+}
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <ctype.h>
+#include <time.h>
+#include <utime.h>
+#include <stdarg.h>
+#include <tqregexp.h>
+#include <tqstring.h>
+
+//#define MIME_MAGIC_DEBUG_TABLE // untested
+
+// Uncomment to debug the config-file parsing phase
+//#define DEBUG_APPRENTICE
+// Uncomment to debug the matching phase
+//#define DEBUG_MIMEMAGIC
+
+#if (defined DEBUG_MIMEMAGIC || defined DEBUG_APPRENTICE)
+#define DEBUG_LINENUMBERS
+#endif
+
+/*
+ * Buitltin Mime types
+ */
+#define MIME_BINARY_UNKNOWN "application/octet-stream"
+#define MIME_BINARY_UNREADABLE "application/x-unreadable"
+#define MIME_BINARY_ZEROSIZE "application/x-zerosize"
+#define MIME_TEXT_UNKNOWN "text/plain"
+#define MIME_TEXT_PLAIN "text/plain"
+#define MIME_INODE_DIR "inode/directory"
+#define MIME_INODE_CDEV "inode/chardevice"
+#define MIME_INODE_BDEV "inode/blockdevice"
+#define MIME_INODE_FIFO "inode/fifo"
+#define MIME_INODE_LINK "inode/link"
+#define MIME_INODE_SOCK "inode/socket"
+// Following should go in magic-file - Fritz
+#define MIME_APPL_TROFF "application/x-troff"
+#define MIME_APPL_TAR "application/x-tar"
+#define MIME_TEXT_FORTRAN "text/x-fortran"
+
+#define MAXMIMESTRING 256
+
+#define HOWMANY 4000 /* big enough to recognize most WWW files, and skip GPL-headers */
+#define MAXDESC 50 /* max leng of text description */
+#define MAXstring 64 /* max leng of "string" types */
+
+typedef union VALUETYPE {
+ unsigned char b;
+ unsigned short h;
+ unsigned long l;
+ char s[MAXstring];
+ unsigned char hs[2]; /* 2 bytes of a fixed-endian "short" */
+ unsigned char hl[4]; /* 2 bytes of a fixed-endian "long" */
+} VALUETYPE;
+
+struct magic {
+ struct magic *next; /* link to next entry */
+#ifdef DEBUG_LINENUMBERS
+ int lineno; /* line number from magic file - doesn't say from which one ;) */
+#endif
+
+ short flag;
+#define INDIR 1 /* if '>(...)' appears, */
+#define UNSIGNED 2 /* comparison is unsigned */
+ short cont_level; /* level of ">" */
+ struct {
+ char type; /* byte short long */
+ long offset; /* offset from indirection */
+ } in;
+ long offset; /* offset to magic number */
+ unsigned char reln; /* relation (0=eq, '>'=gt, etc) */
+ char type; /* int, short, long or string. */
+ char vallen; /* length of string value, if any */
+#define BYTE 1
+#define SHORT 2
+#define LONG 4
+#define STRING 5
+#define DATE 6
+#define BESHORT 7
+#define BELONG 8
+#define BEDATE 9
+#define LESHORT 10
+#define LELONG 11
+#define LEDATE 12
+ VALUETYPE value; /* either number or string */
+ unsigned long mask; /* mask before comparison with value */
+ char nospflag; /* suppress space character */
+
+ /* NOTE: this string is suspected of overrunning - find it! */
+ char desc[MAXDESC]; /* description */
+};
+
+/*
+ * data structures for tar file recognition
+ * --------------------------------------------------------------------------
+ * Header file for public domain tar (tape archive) program.
+ *
+ * @(#)tar.h 1.20 86/10/29 Public Domain. Created 25 August 1985 by John
+ * Gilmore, ihnp4!hoptoad!gnu.
+ *
+ * Header block on tape.
+ *
+ * I'm going to use traditional DP naming conventions here. A "block" is a big
+ * chunk of stuff that we do I/O on. A "record" is a piece of info that we
+ * care about. Typically many "record"s fit into a "block".
+ */
+#define RECORDSIZE 512
+#define NAMSIZ 100
+#define TUNMLEN 32
+#define TGNMLEN 32
+
+union record {
+ char charptr[RECORDSIZE];
+ struct header {
+ char name[NAMSIZ];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char size[12];
+ char mtime[12];
+ char chksum[8];
+ char linkflag;
+ char linkname[NAMSIZ];
+ char magic[8];
+ char uname[TUNMLEN];
+ char gname[TGNMLEN];
+ char devmajor[8];
+ char devminor[8];
+ } header;
+};
+
+/* The magic field is filled with this if uname and gname are valid. */
+#define TMAGIC "ustar " /* 7 chars and a null */
+
+/*
+ * file-function prototypes
+ */
+static int is_tar(unsigned char *, int);
+static unsigned long signextend(struct magic *, unsigned long);
+static int getvalue(struct magic *, char **);
+static int hextoint(int);
+static char *getstr(char *, char *, int, int *);
+static int mget(union VALUETYPE *, unsigned char *, struct magic *, int);
+static int mcheck(union VALUETYPE *, struct magic *);
+static int mconvert(union VALUETYPE *, struct magic *);
+static long from_oct(int, char *);
+
+/*
+ * includes for ASCII substring recognition formerly "names.h" in file
+ * command
+ *
+ * Original notes: names and types used by ascmagic in file(1).
+ * These tokens are
+ * here because they can appear anywhere in the first HOWMANY bytes, while
+ * tokens in /etc/magic must appear at fixed offsets into the file. Don't
+ * make HOWMANY too high unless you have a very fast CPU.
+ */
+
+/* these types are used calculate index to 'types': keep em in sync! */
+/* HTML inserted in first because this is a web server module now */
+/* ENG removed because stupid */
+#define L_HTML 0x001 /* HTML */
+#define L_C 0x002 /* first and foremost on UNIX */
+#define L_MAKE 0x004 /* Makefiles */
+#define L_PLI 0x008 /* PL/1 */
+#define L_MACH 0x010 /* some kinda assembler */
+#define L_PAS 0x020 /* Pascal */
+#define L_JAVA 0x040 /* Java source */
+#define L_CPP 0x080 /* C++ */
+#define L_MAIL 0x100 /* Electronic mail */
+#define L_NEWS 0x200 /* Usenet Netnews */
+#define L_DIFF 0x400 /* Output of diff */
+#define L_OBJC 0x800 /* Objective C */
+
+// Note: this is not a type, it's just used to mark items that should count more
+#define FLAG_STRONG 0x1000
+
+#define P_HTML 0 /* HTML */
+#define P_C 1 /* first and foremost on UNIX */
+#define P_MAKE 2 /* Makefiles */
+#define P_PLI 3 /* PL/1 */
+#define P_MACH 4 /* some kinda assembler */
+#define P_PAS 5 /* Pascal */
+#define P_JAVA 6 /* Java source */
+#define P_CPP 7 /* C++ */
+#define P_MAIL 8 /* Electronic mail */
+#define P_NEWS 9 /* Usenet Netnews */
+#define P_DIFF 10 /* Output of diff */
+#define P_OBJC 11 /* Objective C */
+
+typedef struct asc_type {
+ const char *type;
+ int kwords;
+ double weight;
+} asc_type;
+
+static const asc_type types[] = {
+ { "text/html", 19, 2 }, // 10 items but 10 different words only
+ { "text/x-c", 13, 1 },
+ { "text/x-makefile", 4, 1.9 },
+ { "text/x-pli", 1, 3 },
+ { "text/x-assembler", 6, 2.1 },
+ { "text/x-pascal", 1, 1 },
+ { "text/x-java", 12, 1 },
+ { "text/x-c++", 19, 1 },
+ { "message/rfc822", 4, 1.9 },
+ { "message/news", 3, 2 },
+ { "text/x-diff", 4, 2 },
+ { "text/x-objc", 10, 1 }
+};
+
+#define NTYPES (sizeof(types)/sizeof(asc_type))
+
+static struct names {
+ const char *name;
+ short type;
+} const names[] = {
+ {
+ "<html", L_HTML | FLAG_STRONG
+ },
+ {
+ "<HTML", L_HTML | FLAG_STRONG
+ },
+ {
+ "<head", L_HTML
+ },
+ {
+ "<HEAD", L_HTML
+ },
+ {
+ "<body", L_HTML
+ },
+ {
+ "<BODY", L_HTML
+ },
+ {
+ "<title", L_HTML
+ },
+ {
+ "<TITLE", L_HTML
+ },
+ {
+ "<h1", L_HTML
+ },
+ {
+ "<H1", L_HTML
+ },
+ {
+ "<a", L_HTML
+ },
+ {
+ "<A", L_HTML
+ },
+ {
+ "<img", L_HTML
+ },
+ {
+ "<IMG", L_HTML
+ },
+ {
+ "<!--", L_HTML
+ },
+ {
+ "<!doctype", L_HTML
+ },
+ {
+ "<!DOCTYPE", L_HTML
+ },
+ {
+ "<div", L_HTML
+ },
+ {
+ "<DIV", L_HTML
+ },
+ {
+ "<frame", L_HTML
+ },
+ {
+ "<FRAME", L_HTML
+ },
+ {
+ "<frameset", L_HTML
+ },
+ {
+ "<FRAMESET", L_HTML
+ },
+ {
+ "<script", L_HTML | FLAG_STRONG
+ },
+ {
+ "<SCRIPT", L_HTML | FLAG_STRONG
+ },
+ {
+ "/*", L_C|L_CPP|L_JAVA|L_OBJC
+ },
+ {
+ "//", L_C|L_CPP|L_JAVA|L_OBJC
+ },
+ {
+ "#include", L_C|L_CPP
+ },
+ {
+ "#ifdef", L_C|L_CPP
+ },
+ {
+ "#ifndef", L_C|L_CPP
+ },
+ {
+ "bool", L_C|L_CPP
+ },
+ {
+ "char", L_C|L_CPP|L_JAVA|L_OBJC
+ },
+ {
+ "int", L_C|L_CPP|L_JAVA|L_OBJC
+ },
+ {
+ "float", L_C|L_CPP|L_JAVA|L_OBJC
+ },
+ {
+ "void", L_C|L_CPP|L_JAVA|L_OBJC
+ },
+ {
+ "extern", L_C|L_CPP
+ },
+ {
+ "struct", L_C|L_CPP
+ },
+ {
+ "union", L_C|L_CPP
+ },
+ {
+ "implements", L_JAVA
+ },
+ {
+ "super", L_JAVA
+ },
+ {
+ "import", L_JAVA
+ },
+ {
+ "class", L_CPP|L_JAVA
+ },
+ {
+ "public", L_CPP|L_JAVA
+ },
+ {
+ "private", L_CPP|L_JAVA
+ },
+ {
+ "explicit", L_CPP
+ },
+ {
+ "virtual", L_CPP
+ },
+ {
+ "namespace", L_CPP
+ },
+ {
+ "#import", L_OBJC
+ },
+ {
+ "@interface", L_OBJC
+ },
+ {
+ "@implementation", L_OBJC
+ },
+ {
+ "@protocol", L_OBJC
+ },
+ {
+ "CFLAGS", L_MAKE
+ },
+ {
+ "LDFLAGS", L_MAKE
+ },
+ {
+ "all:", L_MAKE
+ },
+ {
+ ".PHONY:", L_MAKE
+ },
+ {
+ "srcdir", L_MAKE
+ },
+ {
+ "exec_prefix", L_MAKE
+ },
+ /*
+ * Too many files of text have these words in them. Find another way
+ * to recognize Fortrash.
+ */
+ {
+ ".ascii", L_MACH
+ },
+ {
+ ".asciiz", L_MACH
+ },
+ {
+ ".byte", L_MACH
+ },
+ {
+ ".even", L_MACH
+ },
+ {
+ ".globl", L_MACH
+ },
+ {
+ "clr", L_MACH
+ },
+ {
+ "(input", L_PAS
+ },
+ {
+ "dcl", L_PLI
+ },
+ {
+ "Received:", L_MAIL
+ },
+ /* we now stop at '>' for tokens, so this one won't work {
+ ">From", L_MAIL
+ },*/
+ {
+ "Return-Path:", L_MAIL
+ },
+ {
+ "Cc:", L_MAIL
+ },
+ {
+ "Newsgroups:", L_NEWS
+ },
+ {
+ "Path:", L_NEWS
+ },
+ {
+ "Organization:", L_NEWS
+ },
+ {
+ "---", L_DIFF
+ },
+ {
+ "+++", L_DIFF
+ },
+ {
+ "***", L_DIFF
+ },
+ {
+ "@@", L_DIFF
+ },
+ {
+ NULL, 0
+ }
+};
+
+/**
+ * Configuration for the utime() problem.
+ * Here's the problem:
+ * By looking into a file to determine its mimetype, we change its "last access"
+ * time (atime) and this can have side effects, like files in /tmp never being
+ * cleaned up because of that. So in temp directories, we restore the atime.
+ * Since this changes the ctime (last change of attributes), we don't do that
+ * anywhere else, because that breaks archiving programs, that check the ctime.
+ * Hence this class, to configure the directories where the atime should be restored.
+ */
+class KMimeMagicUtimeConf
+{
+public:
+ KMimeMagicUtimeConf()
+ {
+ tmpDirs << TQString::fromLatin1("/tmp"); // default value
+
+ // The trick is that we also don't want the user to override globally set
+ // directories. So we have to misuse KStandardDirs :}
+ TQStringList confDirs = TDEGlobal::dirs()->resourceDirs( "config" );
+ if ( !confDirs.isEmpty() )
+ {
+ TQString globalConf = confDirs.last() + "kmimemagicrc";
+ if ( TQFile::exists( globalConf ) )
+ {
+ KSimpleConfig cfg( globalConf );
+ cfg.setGroup( "Settings" );
+ tmpDirs = cfg.readListEntry( "atimeDirs" );
+ }
+ if ( confDirs.count() > 1 )
+ {
+ TQString localConf = confDirs.first() + "kmimemagicrc";
+ if ( TQFile::exists( localConf ) )
+ {
+ KSimpleConfig cfg( localConf );
+ cfg.setGroup( "Settings" );
+ tmpDirs += cfg.readListEntry( "atimeDirs" );
+ }
+ }
+ for ( TQStringList::Iterator it = tmpDirs.begin() ; it != tmpDirs.end() ; ++it )
+ {
+ TQString dir = *it;
+ if ( !dir.isEmpty() && dir[ dir.length()-1 ] != '/' )
+ (*it) += '/';
+ }
+ }
+#if 0
+ // debug code
+ for ( TQStringList::Iterator it = tmpDirs.begin() ; it != tmpDirs.end() ; ++it )
+ kdDebug(7018) << " atimeDir: " << *it << endl;
+#endif
+ }
+
+ bool restoreAccessTime( const TQString & file ) const
+ {
+ TQString dir = file.left( file.findRev( '/' ) );
+ bool res = tmpDirs.contains( dir );
+ //kdDebug(7018) << "restoreAccessTime " << file << " dir=" << dir << " result=" << res << endl;
+ return res;
+ }
+ TQStringList tmpDirs;
+};
+
+/* current config */
+struct config_rec {
+ bool followLinks;
+ TQString resultBuf;
+ int accuracy;
+
+ struct magic *magic, /* head of magic config list */
+ *last;
+ KMimeMagicUtimeConf * utimeConf;
+};
+
+#ifdef MIME_MAGIC_DEBUG_TABLE
+static void
+test_table()
+{
+ struct magic *m;
+ struct magic *prevm = NULL;
+
+ kdDebug(7018) << "test_table : started" << endl;
+ for (m = conf->magic; m; m = m->next) {
+ if (isprint((((unsigned long) m) >> 24) & 255) &&
+ isprint((((unsigned long) m) >> 16) & 255) &&
+ isprint((((unsigned long) m) >> 8) & 255) &&
+ isprint(((unsigned long) m) & 255)) {
+ //debug("test_table: POINTER CLOBBERED! "
+ //"m=\"%c%c%c%c\" line=%d",
+ (((unsigned long) m) >> 24) & 255,
+ (((unsigned long) m) >> 16) & 255,
+ (((unsigned long) m) >> 8) & 255,
+ ((unsigned long) m) & 255,
+ prevm ? prevm->lineno : -1);
+ break;
+ }
+ prevm = m;
+ }
+}
+#endif
+
+#define EATAB {while (isascii((unsigned char) *l) && \
+ isspace((unsigned char) *l)) ++l;}
+
+int KMimeMagic::parse_line(char *line, int *rule, int lineno)
+{
+ int ws_offset;
+
+ /* delete newline */
+ if (line[0]) {
+ line[strlen(line) - 1] = '\0';
+ }
+ /* skip leading whitespace */
+ ws_offset = 0;
+ while (line[ws_offset] && isspace(line[ws_offset])) {
+ ws_offset++;
+ }
+
+ /* skip blank lines */
+ if (line[ws_offset] == 0) {
+ return 0;
+ }
+ /* comment, do not parse */
+ if (line[ws_offset] == '#')
+ return 0;
+
+ /* if we get here, we're going to use it so count it */
+ (*rule)++;
+
+ /* parse it */
+ return (parse(line + ws_offset, lineno) != 0);
+}
+
+/*
+ * apprentice - load configuration from the magic file.
+ */
+int KMimeMagic::apprentice( const TQString& magicfile )
+{
+ FILE *f;
+ char line[BUFSIZ + 1];
+ int errs = 0;
+ int lineno;
+ int rule = 0;
+ TQCString fname;
+
+ if (magicfile.isEmpty())
+ return -1;
+ fname = TQFile::encodeName(magicfile);
+ f = fopen(fname, "r");
+ if (f == NULL) {
+ kdError(7018) << "can't read magic file " << fname.data() << ": " << strerror(errno) << endl;
+ return -1;
+ }
+
+ /* parse it */
+ for (lineno = 1; fgets(line, BUFSIZ, f) != NULL; lineno++)
+ if (parse_line(line, &rule, lineno))
+ errs++;
+
+ fclose(f);
+
+#ifdef DEBUG_APPRENTICE
+ kdDebug(7018) << "apprentice: conf=" << conf << " file=" << magicfile << " m=" << (conf->magic ? "set" : "NULL") << " m->next=" << ((conf->magic && conf->magic->next) ? "set" : "NULL") << " last=" << (conf->last ? "set" : "NULL") << endl;
+ kdDebug(7018) << "apprentice: read " << lineno << " lines, " << rule << " rules, " << errs << " errors" << endl;
+#endif
+
+#ifdef MIME_MAGIC_DEBUG_TABLE
+ test_table();
+#endif
+
+ return (errs ? -1 : 0);
+}
+
+int KMimeMagic::buff_apprentice(char *buff)
+{
+ char line[BUFSIZ + 2];
+ int errs = 0;
+ int lineno = 1;
+ char *start = buff;
+ char *end;
+ int count = 0;
+ int rule = 0;
+ int len = strlen(buff) + 1;
+
+ /* parse it */
+ do {
+ count = (len > BUFSIZ-1)?BUFSIZ-1:len;
+ strncpy(line, start, count);
+ line[count] = '\0';
+ if ((end = strchr(line, '\n'))) {
+ *(++end) = '\0';
+ count = strlen(line);
+ } else
+ strcat(line, "\n");
+ start += count;
+ len -= count;
+ if (parse_line(line, &rule, lineno))
+ errs++;
+ lineno++;
+ } while (len > 0);
+
+#ifdef DEBUG_APPRENTICE
+ kdDebug(7018) << "buff_apprentice: conf=" << conf << " m=" << (conf->magic ? "set" : "NULL") << " m->next=" << ((conf->magic && conf->magic->next) ? "set" : "NULL") << " last=" << (conf->last ? "set" : "NULL") << endl;
+ kdDebug(7018) << "buff_apprentice: read " << lineno << " lines, " << rule << " rules, " << errs << " errors" << endl;
+#endif
+
+#ifdef MIME_MAGIC_DEBUG_TABLE
+ test_table();
+#endif
+
+ return (errs ? -1 : 0);
+}
+
+/*
+ * extend the sign bit if the comparison is to be signed
+ */
+static unsigned long
+signextend(struct magic *m, unsigned long v)
+{
+ if (!(m->flag & UNSIGNED))
+ switch (m->type) {
+ /*
+ * Do not remove the casts below. They are vital.
+ * When later compared with the data, the sign
+ * extension must have happened.
+ */
+ case BYTE:
+ v = (char) v;
+ break;
+ case SHORT:
+ case BESHORT:
+ case LESHORT:
+ v = (short) v;
+ break;
+ case DATE:
+ case BEDATE:
+ case LEDATE:
+ case LONG:
+ case BELONG:
+ case LELONG:
+ v = (long) v;
+ break;
+ case STRING:
+ break;
+ default:
+ kdError(7018) << "" << "signextend" << ": can't happen: m->type=" << m->type << endl;
+ return 998; //good value
+ }
+ return v;
+}
+
+/*
+ * parse one line from magic file, put into magic[index++] if valid
+ */
+int KMimeMagic::parse(char *l, int
+#ifdef DEBUG_LINENUMBERS
+ lineno
+#endif
+ )
+{
+ int i = 0;
+ struct magic *m;
+ char *t,
+ *s;
+ /* allocate magic structure entry */
+ if ((m = (struct magic *) calloc(1, sizeof(struct magic))) == NULL) {
+ kdError(7018) << "parse: Out of memory." << endl;
+ return -1;
+ }
+ /* append to linked list */
+ m->next = NULL;
+ if (!conf->magic || !conf->last) {
+ conf->magic = conf->last = m;
+ } else {
+ conf->last->next = m;
+ conf->last = m;
+ }
+
+ /* set values in magic structure */
+ m->flag = 0;
+ m->cont_level = 0;
+#ifdef DEBUG_LINENUMBERS
+ m->lineno = lineno;
+#endif
+
+ while (*l == '>') {
+ ++l; /* step over */
+ m->cont_level++;
+ }
+
+ if (m->cont_level != 0 && *l == '(') {
+ ++l; /* step over */
+ m->flag |= INDIR;
+ }
+ /* get offset, then skip over it */
+ m->offset = (int) strtol(l, &t, 0);
+ if (l == t) {
+ kdError(7018) << "parse: offset " << l << " invalid" << endl;
+ }
+ l = t;
+
+ if (m->flag & INDIR) {
+ m->in.type = LONG;
+ m->in.offset = 0;
+ /*
+ * read [.lbs][+-]nnnnn)
+ */
+ if (*l == '.') {
+ switch (*++l) {
+ case 'l':
+ m->in.type = LONG;
+ break;
+ case 's':
+ m->in.type = SHORT;
+ break;
+ case 'b':
+ m->in.type = BYTE;
+ break;
+ default:
+ kdError(7018) << "parse: indirect offset type " << *l << " invalid" << endl;
+ break;
+ }
+ l++;
+ }
+ s = l;
+ if (*l == '+' || *l == '-')
+ l++;
+ if (isdigit((unsigned char) *l)) {
+ m->in.offset = strtol(l, &t, 0);
+ if (*s == '-')
+ m->in.offset = -m->in.offset;
+ } else
+ t = l;
+ if (*t++ != ')') {
+ kdError(7018) << "parse: missing ')' in indirect offset" << endl;
+ }
+ l = t;
+ }
+ while (isascii((unsigned char) *l) && isdigit((unsigned char) *l))
+ ++l;
+ EATAB;
+
+#define NBYTE 4
+#define NSHORT 5
+#define NLONG 4
+#define NSTRING 6
+#define NDATE 4
+#define NBESHORT 7
+#define NBELONG 6
+#define NBEDATE 6
+#define NLESHORT 7
+#define NLELONG 6
+#define NLEDATE 6
+
+ if (*l == 'u') {
+ ++l;
+ m->flag |= UNSIGNED;
+ }
+ /* get type, skip it */
+ if (strncmp(l, "byte", NBYTE) == 0) {
+ m->type = BYTE;
+ l += NBYTE;
+ } else if (strncmp(l, "short", NSHORT) == 0) {
+ m->type = SHORT;
+ l += NSHORT;
+ } else if (strncmp(l, "long", NLONG) == 0) {
+ m->type = LONG;
+ l += NLONG;
+ } else if (strncmp(l, "string", NSTRING) == 0) {
+ m->type = STRING;
+ l += NSTRING;
+ } else if (strncmp(l, "date", NDATE) == 0) {
+ m->type = DATE;
+ l += NDATE;
+ } else if (strncmp(l, "beshort", NBESHORT) == 0) {
+ m->type = BESHORT;
+ l += NBESHORT;
+ } else if (strncmp(l, "belong", NBELONG) == 0) {
+ m->type = BELONG;
+ l += NBELONG;
+ } else if (strncmp(l, "bedate", NBEDATE) == 0) {
+ m->type = BEDATE;
+ l += NBEDATE;
+ } else if (strncmp(l, "leshort", NLESHORT) == 0) {
+ m->type = LESHORT;
+ l += NLESHORT;
+ } else if (strncmp(l, "lelong", NLELONG) == 0) {
+ m->type = LELONG;
+ l += NLELONG;
+ } else if (strncmp(l, "ledate", NLEDATE) == 0) {
+ m->type = LEDATE;
+ l += NLEDATE;
+ } else {
+ kdError(7018) << "parse: type " << l << " invalid" << endl;
+ return -1;
+ }
+ /* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
+ if (*l == '&') {
+ ++l;
+ m->mask = signextend(m, strtol(l, &l, 0));
+ } else
+ m->mask = (unsigned long) ~0L;
+ EATAB;
+
+ switch (*l) {
+ case '>':
+ case '<':
+ /* Old-style anding: "0 byte &0x80 dynamically linked" */
+ case '&':
+ case '^':
+ case '=':
+ m->reln = *l;
+ ++l;
+ break;
+ case '!':
+ if (m->type != STRING) {
+ m->reln = *l;
+ ++l;
+ break;
+ }
+ /* FALL THROUGH */
+ default:
+ if (*l == 'x' && isascii((unsigned char) l[1]) &&
+ isspace((unsigned char) l[1])) {
+ m->reln = *l;
+ ++l;
+ goto GetDesc; /* Bill The Cat */
+ }
+ m->reln = '=';
+ break;
+ }
+ EATAB;
+
+ if (getvalue(m, &l))
+ return -1;
+ /*
+ * now get last part - the description
+ */
+ GetDesc:
+ EATAB;
+ if (l[0] == '\b') {
+ ++l;
+ m->nospflag = 1;
+ } else if ((l[0] == '\\') && (l[1] == 'b')) {
+ ++l;
+ ++l;
+ m->nospflag = 1;
+ } else
+ m->nospflag = 0;
+ // Copy description - until EOL or '#' (for comments)
+ while (*l != '\0' && *l != '#' && i < MAXDESC-1)
+ m->desc[i++] = *l++;
+ m->desc[i] = '\0';
+ // Remove trailing spaces
+ while (--i>0 && isspace( m->desc[i] ))
+ m->desc[i] = '\0';
+
+ // old code
+ //while ((m->desc[i++] = *l++) != '\0' && i < MAXDESC) /* NULLBODY */ ;
+
+#ifdef DEBUG_APPRENTICE
+ kdDebug(7018) << "parse: line=" << lineno << " m=" << m << " next=" << m->next << " cont=" << m->cont_level << " desc=" << (m->desc ? m->desc : "NULL") << endl;
+#endif
+ return 0;
+}
+
+/*
+ * Read a numeric value from a pointer, into the value union of a magic
+ * pointer, according to the magic type. Update the string pointer to point
+ * just after the number read. Return 0 for success, non-zero for failure.
+ */
+static int
+getvalue(struct magic *m, char **p)
+{
+ int slen;
+
+ if (m->type == STRING) {
+ *p = getstr(*p, m->value.s, sizeof(m->value.s), &slen);
+ m->vallen = slen;
+ } else if (m->reln != 'x')
+ m->value.l = signextend(m, strtol(*p, p, 0));
+ return 0;
+}
+
+/*
+ * Convert a string containing C character escapes. Stop at an unescaped
+ * space or tab. Copy the converted version to "p", returning its length in
+ * *slen. Return updated scan pointer as function result.
+ */
+static char *
+getstr(register char *s, register char *p, int plen, int *slen)
+{
+ char *origs = s,
+ *origp = p;
+ char *pmax = p + plen - 1;
+ register int c;
+ register int val;
+
+ while ((c = *s++) != '\0') {
+ if (isspace((unsigned char) c))
+ break;
+ if (p >= pmax) {
+ kdError(7018) << "String too long: " << origs << endl;
+ break;
+ }
+ if (c == '\\') {
+ switch (c = *s++) {
+
+ case '\0':
+ goto out;
+
+ default:
+ *p++ = (char) c;
+ break;
+
+ case 'n':
+ *p++ = '\n';
+ break;
+
+ case 'r':
+ *p++ = '\r';
+ break;
+
+ case 'b':
+ *p++ = '\b';
+ break;
+
+ case 't':
+ *p++ = '\t';
+ break;
+
+ case 'f':
+ *p++ = '\f';
+ break;
+
+ case 'v':
+ *p++ = '\v';
+ break;
+
+ /* \ and up to 3 octal digits */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ val = c - '0';
+ c = *s++; /* try for 2 */
+ if (c >= '0' && c <= '7') {
+ val = (val << 3) | (c - '0');
+ c = *s++; /* try for 3 */
+ if (c >= '0' && c <= '7')
+ val = (val << 3) | (c - '0');
+ else
+ --s;
+ } else
+ --s;
+ *p++ = (char) val;
+ break;
+
+ /* \x and up to 3 hex digits */
+ case 'x':
+ val = 'x'; /* Default if no digits */
+ c = hextoint(*s++); /* Get next char */
+ if (c >= 0) {
+ val = c;
+ c = hextoint(*s++);
+ if (c >= 0) {
+ val = (val << 4) + c;
+ c = hextoint(*s++);
+ if (c >= 0) {
+ val = (val << 4) + c;
+ } else
+ --s;
+ } else
+ --s;
+ } else
+ --s;
+ *p++ = (char) val;
+ break;
+ }
+ } else
+ *p++ = (char) c;
+ }
+ out:
+ *p = '\0';
+ *slen = p - origp;
+ //for ( char* foo = origp; foo < p ; ++foo )
+ // kdDebug(7018) << " " << *foo << endl;
+ return s;
+}
+
+
+/* Single hex char to int; -1 if not a hex char. */
+static int
+hextoint(int c)
+{
+ if (!isascii((unsigned char) c))
+ return -1;
+ if (isdigit((unsigned char) c))
+ return c - '0';
+ if ((c >= 'a') && (c <= 'f'))
+ return c + 10 - 'a';
+ if ((c >= 'A') && (c <= 'F'))
+ return c + 10 - 'A';
+ return -1;
+}
+
+/*
+ * Convert the byte order of the data we are looking at
+ */
+static int
+mconvert(union VALUETYPE *p, struct magic *m)
+{
+ switch (m->type) {
+ case BYTE:
+ return 1;
+ case STRING:
+ /* Null terminate */
+ p->s[sizeof(p->s) - 1] = '\0';
+ return 1;
+#ifndef WORDS_BIGENDIAN
+ case SHORT:
+#endif
+ case BESHORT:
+ p->h = (short) ((p->hs[0] << 8) | (p->hs[1]));
+ return 1;
+#ifndef WORDS_BIGENDIAN
+ case LONG:
+ case DATE:
+#endif
+ case BELONG:
+ case BEDATE:
+ p->l = (long)
+ ((p->hl[0] << 24) | (p->hl[1] << 16) | (p->hl[2] << 8) | (p->hl[3]));
+ return 1;
+#ifdef WORDS_BIGENDIAN
+ case SHORT:
+#endif
+ case LESHORT:
+ p->h = (short) ((p->hs[1] << 8) | (p->hs[0]));
+ return 1;
+#ifdef WORDS_BIGENDIAN
+ case LONG:
+ case DATE:
+#endif
+ case LELONG:
+ case LEDATE:
+ p->l = (long)
+ ((p->hl[3] << 24) | (p->hl[2] << 16) | (p->hl[1] << 8) | (p->hl[0]));
+ return 1;
+ default:
+ kdError(7018) << "mconvert: invalid type " << m->type << endl;
+ return 0;
+ }
+}
+
+
+static int
+mget(union VALUETYPE *p, unsigned char *s, struct magic *m,
+ int nbytes)
+{
+ long offset = m->offset;
+ switch ( m->type )
+ {
+ case BYTE:
+ if ( offset + 1 > nbytes-1 ) // nbytes = (size of file) + 1
+ return 0;
+ break;
+ case SHORT:
+ case BESHORT:
+ case LESHORT:
+ if ( offset + 2 > nbytes-1 )
+ return 0;
+ break;
+ case LONG:
+ case BELONG:
+ case LELONG:
+ case DATE:
+ case BEDATE:
+ case LEDATE:
+ if ( offset + 4 > nbytes-1 )
+ return 0;
+ break;
+ case STRING:
+ break;
+ }
+
+// The file length might be < sizeof(union VALUETYPE) (David)
+// -> pad with zeros (the 'file' command does it this way)
+// Thanks to Stan Covington <stan@calderasystems.com> for detailed report
+ if (offset + (int)sizeof(union VALUETYPE) > nbytes)
+ {
+ int have = nbytes - offset;
+ memset(p, 0, sizeof(union VALUETYPE));
+ if (have > 0)
+ memcpy(p, s + offset, have);
+ } else
+ memcpy(p, s + offset, sizeof(union VALUETYPE));
+
+ if (!mconvert(p, m))
+ return 0;
+
+ if (m->flag & INDIR) {
+
+ switch (m->in.type) {
+ case BYTE:
+ offset = p->b + m->in.offset;
+ break;
+ case SHORT:
+ offset = p->h + m->in.offset;
+ break;
+ case LONG:
+ offset = p->l + m->in.offset;
+ break;
+ }
+
+ if (offset + (int)sizeof(union VALUETYPE) > nbytes)
+ return 0;
+
+ memcpy(p, s + offset, sizeof(union VALUETYPE));
+
+ if (!mconvert(p, m))
+ return 0;
+ }
+ return 1;
+}
+
+static int
+mcheck(union VALUETYPE *p, struct magic *m)
+{
+ register unsigned long l = m->value.l;
+ register unsigned long v;
+ int matched;
+
+ if ((m->value.s[0] == 'x') && (m->value.s[1] == '\0')) {
+ kdError(7018) << "BOINK" << endl;
+ return 1;
+ }
+ switch (m->type) {
+ case BYTE:
+ v = p->b;
+ break;
+
+ case SHORT:
+ case BESHORT:
+ case LESHORT:
+ v = p->h;
+ break;
+
+ case LONG:
+ case BELONG:
+ case LELONG:
+ case DATE:
+ case BEDATE:
+ case LEDATE:
+ v = p->l;
+ break;
+
+ case STRING:
+ l = 0;
+ /*
+ * What we want here is: v = strncmp(m->value.s, p->s,
+ * m->vallen); but ignoring any nulls. bcmp doesn't give
+ * -/+/0 and isn't universally available anyway.
+ */
+ v = 0;
+ {
+ register unsigned char *a = (unsigned char *) m->value.s;
+ register unsigned char *b = (unsigned char *) p->s;
+ register int len = m->vallen;
+ Q_ASSERT(len);
+
+ while (--len >= 0)
+ if ((v = *b++ - *a++) != 0)
+ break;
+ }
+ break;
+ default:
+ kdError(7018) << "mcheck: invalid type " << m->type << endl;
+ return 0; /* NOTREACHED */
+ }
+#if 0
+ tqDebug("Before signextend %08x", v);
+#endif
+ v = signextend(m, v) & m->mask;
+#if 0
+ tqDebug("After signextend %08x", v);
+#endif
+
+ switch (m->reln) {
+ case 'x':
+ matched = 1;
+ break;
+
+ case '!':
+ matched = v != l;
+ break;
+
+ case '=':
+ matched = v == l;
+ break;
+
+ case '>':
+ if (m->flag & UNSIGNED)
+ matched = v > l;
+ else
+ matched = (long) v > (long) l;
+ break;
+
+ case '<':
+ if (m->flag & UNSIGNED)
+ matched = v < l;
+ else
+ matched = (long) v < (long) l;
+ break;
+
+ case '&':
+ matched = (v & l) == l;
+ break;
+
+ case '^':
+ matched = (v & l) != l;
+ break;
+
+ default:
+ matched = 0;
+ kdError(7018) << "mcheck: can't happen: invalid relation " << m->reln << "." << endl;
+ break; /* NOTREACHED */
+ }
+
+ return matched;
+}
+
+/*
+ * magic_process - process input file fn. Opens the file and reads a
+ * fixed-size buffer to begin processing the contents.
+ */
+
+void process(struct config_rec* conf, const TQString & fn)
+{
+ int fd = 0;
+ unsigned char buf[HOWMANY + 1]; /* one extra for terminating '\0' */
+ KDE_struct_stat sb;
+ int nbytes = 0; /* number of bytes read from a datafile */
+ int tagbytes = 0; /* size of prefixed tag */
+ TQCString fileName = TQFile::encodeName( fn );
+
+ /*
+ * first try judging the file based on its filesystem status
+ */
+ if (fsmagic(conf, fileName, &sb) != 0) {
+ //resultBuf += "\n";
+ return;
+ }
+ if ((fd = KDE_open(fileName, O_RDONLY)) < 0) {
+ /* We can't open it, but we were able to stat it. */
+ /*
+ * if (sb.st_mode & 0002) addResult("writable, ");
+ * if (sb.st_mode & 0111) addResult("executable, ");
+ */
+ //kdDebug(7018) << "can't read `" << fn << "' (" << strerror(errno) << ")." << endl;
+ conf->resultBuf = MIME_BINARY_UNREADABLE;
+ return;
+ }
+ /*
+ * try looking at the first HOWMANY bytes
+ */
+ if ((nbytes = read(fd, (char *) buf, HOWMANY)) == -1) {
+ kdError(7018) << "" << fn << " read failed (" << strerror(errno) << ")." << endl;
+ conf->resultBuf = MIME_BINARY_UNREADABLE;
+ (void)close(fd);
+ return;
+ }
+ if ((tagbytes = tagmagic(buf, nbytes))) {
+ // Read buffer at new position
+ lseek(fd, tagbytes, SEEK_SET);
+ nbytes = read(fd, (char*)buf, HOWMANY);
+ if (nbytes < 0) {
+ conf->resultBuf = MIME_BINARY_UNREADABLE;
+ (void)close(fd);
+ return;
+ }
+ }
+ if (nbytes == 0) {
+ conf->resultBuf = MIME_BINARY_ZEROSIZE;
+ } else {
+ buf[nbytes++] = '\0'; /* null-terminate it */
+ tryit(conf, buf, nbytes);
+ }
+
+ if ( conf->utimeConf && conf->utimeConf->restoreAccessTime( fn ) )
+ {
+ /*
+ * Try to restore access, modification times if read it.
+ * This changes the "change" time (ctime), but we can't do anything
+ * about that.
+ */
+ struct utimbuf utbuf;
+ utbuf.actime = sb.st_atime;
+ utbuf.modtime = sb.st_mtime;
+ (void) utime(fileName, &utbuf);
+ }
+ (void) close(fd);
+}
+
+
+static void tryit(struct config_rec* conf, unsigned char *buf, int nb)
+{
+ /* try tests in /etc/magic (or surrogate magic file) */
+ if (match(conf, buf, nb))
+ return;
+
+ /* try known keywords, check for ascii-ness too. */
+ if (ascmagic(conf, buf, nb) == 1)
+ return;
+
+ /* see if it's plain text */
+ if (textmagic(conf, buf, nb))
+ return;
+
+ /* abandon hope, all ye who remain here */
+ conf->resultBuf = MIME_BINARY_UNKNOWN;
+ conf->accuracy = 0;
+}
+
+static int
+fsmagic(struct config_rec* conf, const char *fn, KDE_struct_stat *sb)
+{
+ int ret = 0;
+
+ /*
+ * Fstat is cheaper but fails for files you don't have read perms on.
+ * On 4.2BSD and similar systems, use lstat() to identify symlinks.
+ */
+ ret = KDE_lstat(fn, sb); /* don't merge into if; see "ret =" above */
+
+ if (ret) {
+ return 1;
+
+ }
+ /*
+ * if (sb->st_mode & S_ISUID) resultBuf += "setuid ";
+ * if (sb->st_mode & S_ISGID) resultBuf += "setgid ";
+ * if (sb->st_mode & S_ISVTX) resultBuf += "sticky ";
+ */
+
+ switch (sb->st_mode & S_IFMT) {
+ case S_IFDIR:
+ conf->resultBuf = MIME_INODE_DIR;
+ return 1;
+ case S_IFCHR:
+ conf->resultBuf = MIME_INODE_CDEV;
+ return 1;
+ case S_IFBLK:
+ conf->resultBuf = MIME_INODE_BDEV;
+ return 1;
+ /* TODO add code to handle V7 MUX and Blit MUX files */
+#ifdef S_IFIFO
+ case S_IFIFO:
+ conf->resultBuf = MIME_INODE_FIFO;
+ return 1;
+#endif
+#ifdef S_IFLNK
+ case S_IFLNK:
+ {
+ char buf[BUFSIZ + BUFSIZ + 4];
+ register int nch;
+ KDE_struct_stat tstatbuf;
+
+ if ((nch = readlink(fn, buf, BUFSIZ - 1)) <= 0) {
+ conf->resultBuf = MIME_INODE_LINK;
+ //conf->resultBuf += "\nunreadable";
+ return 1;
+ }
+ buf[nch] = '\0'; /* readlink(2) forgets this */
+ /* If broken symlink, say so and quit early. */
+ if (*buf == '/') {
+ if (KDE_stat(buf, &tstatbuf) < 0) {
+ conf->resultBuf = MIME_INODE_LINK;
+ //conf->resultBuf += "\nbroken";
+ return 1;
+ }
+ } else {
+ char *tmp;
+ char buf2[BUFSIZ + BUFSIZ + 4];
+
+ strncpy(buf2, fn, BUFSIZ);
+ buf2[BUFSIZ] = 0;
+
+ if ((tmp = strrchr(buf2, '/')) == NULL) {
+ tmp = buf; /* in current dir */
+ } else {
+ /* dir part plus (rel.) link */
+ *++tmp = '\0';
+ strcat(buf2, buf);
+ tmp = buf2;
+ }
+ if (KDE_stat(tmp, &tstatbuf) < 0) {
+ conf->resultBuf = MIME_INODE_LINK;
+ //conf->resultBuf += "\nbroken";
+ return 1;
+ } else
+ strcpy(buf, tmp);
+ }
+ if (conf->followLinks)
+ process( conf, TQFile::decodeName( buf ) );
+ else
+ conf->resultBuf = MIME_INODE_LINK;
+ return 1;
+ }
+ return 1;
+#endif
+#ifdef S_IFSOCK
+#ifndef __COHERENT__
+ case S_IFSOCK:
+ conf->resultBuf = MIME_INODE_SOCK;
+ return 1;
+#endif
+#endif
+ case S_IFREG:
+ break;
+ default:
+ kdError(7018) << "KMimeMagic::fsmagic: invalid mode 0" << sb->st_mode << "." << endl;
+ /* NOTREACHED */
+ }
+
+ /*
+ * regular file, check next possibility
+ */
+ if (sb->st_size == 0) {
+ conf->resultBuf = MIME_BINARY_ZEROSIZE;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Go through the whole list, stopping if you find a match. Process all the
+ * continuations of that match before returning.
+ *
+ * We support multi-level continuations:
+ *
+ * At any time when processing a successful top-level match, there is a current
+ * continuation level; it represents the level of the last successfully
+ * matched continuation.
+ *
+ * Continuations above that level are skipped as, if we see one, it means that
+ * the continuation that controls them - i.e, the lower-level continuation
+ * preceding them - failed to match.
+ *
+ * Continuations below that level are processed as, if we see one, it means
+ * we've finished processing or skipping higher-level continuations under the
+ * control of a successful or unsuccessful lower-level continuation, and are
+ * now seeing the next lower-level continuation and should process it. The
+ * current continuation level reverts to the level of the one we're seeing.
+ *
+ * Continuations at the current level are processed as, if we see one, there's
+ * no lower-level continuation that may have failed.
+ *
+ * If a continuation matches, we bump the current continuation level so that
+ * higher-level continuations are processed.
+ */
+static int
+match(struct config_rec* conf, unsigned char *s, int nbytes)
+{
+ int cont_level = 0;
+ union VALUETYPE p;
+ struct magic *m;
+
+#ifdef DEBUG_MIMEMAGIC
+ kdDebug(7018) << "match: conf=" << conf << " m=" << (conf->magic ? "set" : "NULL") << " m->next=" << ((conf->magic && conf->magic->next) ? "set" : "NULL") << " last=" << (conf->last ? "set" : "NULL") << endl;
+ for (m = conf->magic; m; m = m->next) {
+ if (isprint((((unsigned long) m) >> 24) & 255) &&
+ isprint((((unsigned long) m) >> 16) & 255) &&
+ isprint((((unsigned long) m) >> 8) & 255) &&
+ isprint(((unsigned long) m) & 255)) {
+ kdDebug(7018) << "match: POINTER CLOBBERED! " << endl;
+ break;
+ }
+ }
+#endif
+
+ for (m = conf->magic; m; m = m->next) {
+#ifdef DEBUG_MIMEMAGIC
+ kdDebug(7018) << "match: line=" << m->lineno << " desc=" << m->desc << endl;
+#endif
+ memset(&p, 0, sizeof(union VALUETYPE));
+
+ /* check if main entry matches */
+ if (!mget(&p, s, m, nbytes) ||
+ !mcheck(&p, m)) {
+ struct magic *m_cont;
+
+ /*
+ * main entry didn't match, flush its continuations
+ */
+ if (!m->next || (m->next->cont_level == 0)) {
+ continue;
+ }
+ m_cont = m->next;
+ while (m_cont && (m_cont->cont_level != 0)) {
+#ifdef DEBUG_MIMEMAGIC
+ kdDebug(7018) << "match: line=" << m->lineno << " cont=" << m_cont->cont_level << " mc=" << m_cont->lineno << " mc->next=" << m_cont << " " << endl;
+#endif
+ /*
+ * this trick allows us to keep *m in sync
+ * when the continue advances the pointer
+ */
+ m = m_cont;
+ m_cont = m_cont->next;
+ }
+ continue;
+ }
+ /* if we get here, the main entry rule was a match */
+ /* this will be the last run through the loop */
+#ifdef DEBUG_MIMEMAGIC
+ kdDebug(7018) << "match: rule matched, line=" << m->lineno << " type=" << m->type << " " << ((m->type == STRING) ? m->value.s : "") << endl;
+#endif
+
+ /* remember the match */
+ conf->resultBuf = m->desc;
+
+ cont_level++;
+ /*
+ * while (m && m->next && m->next->cont_level != 0 && ( m =
+ * m->next ))
+ */
+ m = m->next;
+ while (m && (m->cont_level != 0)) {
+#ifdef DEBUG_MIMEMAGIC
+ kdDebug(7018) << "match: line=" << m->lineno << " cont=" << m->cont_level << " type=" << m->type << " " << ((m->type == STRING) ? m->value.s : "") << endl;
+#endif
+ if (cont_level >= m->cont_level) {
+ if (cont_level > m->cont_level) {
+ /*
+ * We're at the end of the level
+ * "cont_level" continuations.
+ */
+ cont_level = m->cont_level;
+ }
+ if (mget(&p, s, m, nbytes) &&
+ mcheck(&p, m)) {
+ /*
+ * This continuation matched. Print
+ * its message, with a blank before
+ * it if the previous item printed
+ * and this item isn't empty.
+ */
+#ifdef DEBUG_MIMEMAGIC
+ kdDebug(7018) << "continuation matched" << endl;
+#endif
+ conf->resultBuf = m->desc;
+ cont_level++;
+ }
+ }
+ /* move to next continuation record */
+ m = m->next;
+ }
+ // KDE-specific: need an actual mimetype for a real match
+ // If we only matched a rule with continuations but no mimetype, it's not a match
+ if ( !conf->resultBuf.isEmpty() )
+ {
+#ifdef DEBUG_MIMEMAGIC
+ kdDebug(7018) << "match: matched" << endl;
+#endif
+ return 1; /* all through */
+ }
+ }
+#ifdef DEBUG_MIMEMAGIC
+ kdDebug(7018) << "match: failed" << endl;
+#endif
+ return 0; /* no match at all */
+}
+
+// Try to parse prefixed tags before matching on content
+// Sofar only ID3v2 tags (<=.4) are handled
+static int tagmagic(unsigned char *buf, int nbytes)
+{
+ if(nbytes<40) return 0;
+ if(buf[0] == 'I' && buf[1] == 'D' && buf[2] == '3') {
+ int size = 10;
+ // Sanity (known version, no unknown flags)
+ if(buf[3] > 4) return 0;
+ if(buf[5] & 0x0F) return 0;
+ // Tag has v4 footer
+ if(buf[5] & 0x10) size += 10;
+ // Calculated syncsafe size
+ size += buf[9];
+ size += buf[8] << 7;
+ size += buf[7] << 14;
+ size += buf[6] << 21;
+ return size;
+ }
+ return 0;
+}
+
+struct Token {
+ char *data;
+ int length;
+};
+
+struct Tokenizer
+{
+ Tokenizer(char* buf, int nbytes) {
+ data = buf;
+ length = nbytes;
+ pos = 0;
+ }
+ bool isNewLine() {
+ return newline;
+ }
+ Token* nextToken() {
+ if (pos == 0)
+ newline = true;
+ else
+ newline = false;
+ token.data = data+pos;
+ token.length = 0;
+ while(pos<length) {
+ switch (data[pos]) {
+ case '\n':
+ newline = true;
+ case '\0':
+ case '\t':
+ case ' ':
+ case '\r':
+ case '\f':
+ case ',':
+ case ';':
+ case '>':
+ if (token.length == 0) token.data++;
+ else
+ return &token;
+ break;
+ default:
+ token.length++;
+ }
+ pos++;
+ }
+ return &token;
+ }
+
+private:
+ Token token;
+ char* data;
+ int length;
+ int pos;
+ bool newline;
+};
+
+
+/* an optimization over plain strcmp() */
+//#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+static inline bool STREQ(const Token *token, const char *b) {
+ const char *a = token->data;
+ int len = token->length;
+ if (a == b) return true;
+ while(*a && *b && len > 0) {
+ if (*a != *b) return false;
+ a++; b++; len--;
+ }
+ return (len == 0 && *b == 0);
+}
+
+static int ascmagic(struct config_rec* conf, unsigned char *buf, int nbytes)
+{
+ int i;
+ double pct, maxpct, pctsum;
+ double pcts[NTYPES];
+ int mostaccurate, tokencount;
+ int typeset, jonly, conly, jconly, objconly, cpponly;
+ int has_escapes = 0;
+ //unsigned char *s;
+ //char nbuf[HOWMANY + 1]; /* one extra for terminating '\0' */
+
+ /* these are easy, do them first */
+ conf->accuracy = 70;
+
+ /*
+ * for troff, look for . + letter + letter or .\"; this must be done
+ * to disambiguate tar archives' ./file and other trash from real
+ * troff input.
+ */
+ if (*buf == '.') {
+ unsigned char *tp = buf + 1;
+
+ while (isascii(*tp) && isspace(*tp))
+ ++tp; /* skip leading whitespace */
+ if ((isascii(*tp) && (isalnum(*tp) || *tp == '\\') &&
+ isascii(*(tp + 1)) && (isalnum(*(tp + 1)) || *tp == '"'))) {
+ conf->resultBuf = MIME_APPL_TROFF;
+ return 1;
+ }
+ }
+ if ((*buf == 'c' || *buf == 'C') &&
+ isascii(*(buf + 1)) && isspace(*(buf + 1))) {
+ /* Fortran */
+ conf->resultBuf = MIME_TEXT_FORTRAN;
+ return 1;
+ }
+ assert(nbytes-1 < HOWMANY + 1);
+ /* look for tokens - this is expensive! */
+ has_escapes = (memchr(buf, '\033', nbytes) != NULL);
+ Tokenizer tokenizer((char*)buf, nbytes);
+ const Token* token;
+ bool linecomment = false, blockcomment = false;
+ const struct names *p;
+ int typecount[NTYPES];
+/*
+ * Fritz:
+ * Try a little harder on C/C++/Java.
+ */
+ memset(&typecount, 0, sizeof(typecount));
+ typeset = 0;
+ jonly = 0;
+ conly = 0;
+ jconly = 0;
+ objconly = 0;
+ cpponly = 0;
+ tokencount = 0;
+ bool foundClass = false; // mandatory for java
+ // first collect all possible types and count matches
+ // we stop at '>' too, because of "<title>blah</title>" on HTML pages
+ while ((token = tokenizer.nextToken())->length > 0) {
+#ifdef DEBUG_MIMEMAGIC
+ kdDebug(7018) << "KMimeMagic::ascmagic token=" << token << endl;
+#endif
+ if (linecomment && tokenizer.isNewLine())
+ linecomment = false;
+ if (blockcomment && STREQ(token, "*/")) {
+ blockcomment = false;
+ continue;
+ }
+ for (p = names; p->name ; p++) {
+ if (STREQ(token, p->name)) {
+#ifdef DEBUG_MIMEMAGIC
+ kdDebug(7018) << "KMimeMagic::ascmagic token matches ! name=" << p->name << " type=" << p->type << endl;
+#endif
+ tokencount++;
+ typeset |= p->type;
+ if(p->type & (L_C|L_CPP|L_JAVA|L_OBJC)) {
+ if (linecomment || blockcomment) {
+ continue;
+ }
+ else {
+ switch(p->type & (L_C|L_CPP|L_JAVA|L_OBJC))
+ {
+ case L_JAVA:
+ jonly++;
+ break;
+ case L_OBJC:
+ objconly++;
+ break;
+ case L_CPP:
+ cpponly++;
+ break;
+ case (L_CPP|L_JAVA):
+ jconly++;
+ if ( !foundClass && STREQ(token, "class") )
+ foundClass = true;
+ break;
+ case (L_C|L_CPP):
+ conly++;
+ break;
+ default:
+ if (STREQ(token, "//")) linecomment = true;
+ if (STREQ(token, "/*")) blockcomment = true;
+ }
+ }
+ }
+ for (i = 0; i < (int)NTYPES; i++) {
+ if ((1 << i) & p->type) typecount[i]+= p->type & FLAG_STRONG ? 2 : 1;
+ }
+ }
+ }
+ }
+
+ if (typeset & (L_C|L_CPP|L_JAVA|L_OBJC)) {
+ conf->accuracy = 60;
+ if (!(typeset & ~(L_C|L_CPP|L_JAVA|L_OBJC))) {
+#ifdef DEBUG_MIMEMAGIC
+ kdDebug(7018) << "C/C++/Java/ObjC: jonly=" << jonly << " conly=" << conly << " jconly=" << jconly << " objconly=" << objconly << endl;
+#endif
+ if (jonly > 1 && foundClass) {
+ // At least two java-only tokens have matched, including "class"
+ conf->resultBuf = TQString(types[P_JAVA].type);
+ return 1;
+ }
+ if (jconly > 1) {
+ // At least two non-C (only C++ or Java) token have matched.
+ if (typecount[P_JAVA] < typecount[P_CPP])
+ conf->resultBuf = TQString(types[P_CPP].type);
+ else
+ conf->resultBuf = TQString(types[P_JAVA].type);
+ return 1;
+ }
+ if (conly + cpponly > 1) {
+ // Either C or C++.
+ if (cpponly > 0)
+ conf->resultBuf = TQString(types[P_CPP].type);
+ else
+ conf->resultBuf = TQString(types[P_C].type);
+ return 1;
+ }
+ if (objconly > 0) {
+ conf->resultBuf = TQString(types[P_OBJC].type);
+ return 1;
+ }
+ }
+ }
+
+ /* Neither C, C++ or Java (or all of them without able to distinguish):
+ * Simply take the token-class with the highest
+ * matchcount > 0
+ */
+ mostaccurate = -1;
+ maxpct = pctsum = 0.0;
+ for (i = 0; i < (int)NTYPES; i++) {
+ if (typecount[i] > 1) { // one word is not enough, we need at least two
+ pct = (double)typecount[i] / (double)types[i].kwords *
+ (double)types[i].weight;
+ pcts[i] = pct;
+ pctsum += pct;
+ if (pct > maxpct) {
+ maxpct = pct;
+ mostaccurate = i;
+ }
+#ifdef DEBUG_MIMEMAGIC
+ kdDebug(7018) << "" << types[i].type << " has " << typecount[i] << " hits, " << types[i].kwords << " kw, weight " << types[i].weight << ", " << pct << " -> max = " << maxpct << "\n" << endl;
+#endif
+ }
+ }
+ if (mostaccurate >= 0) {
+ if ( mostaccurate != P_JAVA || foundClass ) // 'class' mandatory for java
+ {
+ conf->accuracy = (int)(pcts[mostaccurate] / pctsum * 60);
+#ifdef DEBUG_MIMEMAGIC
+ kdDebug(7018) << "mostaccurate=" << mostaccurate << " pcts=" << pcts[mostaccurate] << " pctsum=" << pctsum << " accuracy=" << conf->accuracy << endl;
+#endif
+ conf->resultBuf = TQString(types[mostaccurate].type);
+ return 1;
+ }
+ }
+
+ switch (is_tar(buf, nbytes)) {
+ case 1:
+ /* V7 tar archive */
+ conf->resultBuf = MIME_APPL_TAR;
+ conf->accuracy = 90;
+ return 1;
+ case 2:
+ /* POSIX tar archive */
+ conf->resultBuf = MIME_APPL_TAR;
+ conf->accuracy = 90;
+ return 1;
+ }
+
+ for (i = 0; i < nbytes; i++) {
+ if (!isascii(*(buf + i)))
+ return 0; /* not all ascii */
+ }
+
+ /* all else fails, but it is ascii... */
+ conf->accuracy = 90;
+ if (has_escapes) {
+ /* text with escape sequences */
+ /* we leave this open for further differentiation later */
+ conf->resultBuf = MIME_TEXT_UNKNOWN;
+ } else {
+ /* plain text */
+ conf->resultBuf = MIME_TEXT_PLAIN;
+ }
+ return 1;
+}
+
+/* Maximal length of a line we consider "reasonable". */
+#define TEXT_MAXLINELEN 300
+
+// This code is taken from the "file" command, where it is licensed
+// in the "beer-ware license" :-)
+// Original author: <joerg@FreeBSD.ORG>
+// Simplified by David Faure to avoid the static array char[256].
+static int textmagic(struct config_rec* conf, unsigned char * buf, int nbytes)
+{
+ int i;
+ unsigned char *cp;
+
+ nbytes--;
+
+ /* First, look whether there are "unreasonable" characters. */
+ for (i = 0, cp = buf; i < nbytes; i++, cp++)
+ if ((*cp < 8) || (*cp>13 && *cp<32 && *cp!=27 ) || (*cp==0x7F))
+ return 0;
+
+ /* Now, look whether the file consists of lines of
+ * "reasonable" length. */
+
+ for (i = 0; i < nbytes;) {
+ cp = (unsigned char *) memchr(buf, '\n', nbytes - i);
+ if (cp == NULL) {
+ /* Don't fail if we hit the end of buffer. */
+ if (i + TEXT_MAXLINELEN >= nbytes)
+ break;
+ else
+ return 0;
+ }
+ if (cp - buf > TEXT_MAXLINELEN)
+ return 0;
+ i += (cp - buf + 1);
+ buf = cp + 1;
+ }
+ conf->resultBuf = MIME_TEXT_PLAIN;
+ return 1;
+}
+
+
+/*
+ * is_tar() -- figure out whether file is a tar archive.
+ *
+ * Stolen (by author of file utility) from the public domain tar program: Public
+ * Domain version written 26 Aug 1985 John Gilmore (ihnp4!hoptoad!gnu).
+ *
+ * @(#)list.c 1.18 9/23/86 Public Domain - gnu $Id: mod_mime_magic.c,v 1.7
+ * 1997/06/24 00:41:02 ikluft Exp ikluft $
+ *
+ * Comments changed and some code/comments reformatted for file command by Ian
+ * Darwin.
+ */
+
+#define isodigit(c) ( ((c) >= '0') && ((c) <= '7') )
+
+/*
+ * Return 0 if the checksum is bad (i.e., probably not a tar archive), 1 for
+ * old UNIX tar file, 2 for Unix Std (POSIX) tar file.
+ */
+
+static int
+is_tar(unsigned char *buf, int nbytes)
+{
+ register union record *header = (union record *) buf;
+ register int i;
+ register long sum,
+ recsum;
+ register char *p;
+
+ if (nbytes < (int)sizeof(union record))
+ return 0;
+
+ recsum = from_oct(8, header->header.chksum);
+
+ sum = 0;
+ p = header->charptr;
+ for (i = sizeof(union record); --i >= 0;) {
+ /*
+ * We can't use unsigned char here because of old compilers,
+ * e.g. V7.
+ */
+ sum += 0xFF & *p++;
+ }
+
+ /* Adjust checksum to count the "chksum" field as blanks. */
+ for (i = sizeof(header->header.chksum); --i >= 0;)
+ sum -= 0xFF & header->header.chksum[i];
+ sum += ' ' * sizeof header->header.chksum;
+
+ if (sum != recsum)
+ return 0; /* Not a tar archive */
+
+ if (0 == strcmp(header->header.magic, TMAGIC))
+ return 2; /* Unix Standard tar archive */
+
+ return 1; /* Old fashioned tar archive */
+}
+
+
+/*
+ * Quick and dirty octal conversion.
+ *
+ * Result is -1 if the field is invalid (all blank, or nonoctal).
+ */
+static long
+from_oct(int digs, char *where)
+{
+ register long value;
+
+ while (isspace(*where)) { /* Skip spaces */
+ where++;
+ if (--digs <= 0)
+ return -1; /* All blank field */
+ }
+ value = 0;
+ while (digs > 0 && isodigit(*where)) { /* Scan til nonoctal */
+ value = (value << 3) | (*where++ - '0');
+ --digs;
+ }
+
+ if (digs > 0 && *where && !isspace(*where))
+ return -1; /* Ended on non-space/nul */
+
+ return value;
+}
+
+KMimeMagic::KMimeMagic()
+{
+ // Magic file detection init
+ TQString mimefile = locate( "mime", "magic" );
+ init( mimefile );
+ // Add snippets from share/config/magic/*
+ TQStringList snippets = TDEGlobal::dirs()->findAllResources( "config", "magic/*.magic", true );
+ for ( TQStringList::Iterator it = snippets.begin() ; it != snippets.end() ; ++it )
+ if ( !mergeConfig( *it ) )
+ kdWarning() << k_funcinfo << "Failed to parse " << *it << endl;
+}
+
+KMimeMagic::KMimeMagic(const TQString & _configfile)
+{
+ init( _configfile );
+}
+
+void KMimeMagic::init( const TQString& _configfile )
+{
+ int result;
+ conf = new config_rec;
+
+ /* set up the magic list (empty) */
+ conf->magic = conf->last = NULL;
+ magicResult = NULL;
+ conf->followLinks = false;
+
+ conf->utimeConf = 0L; // created on demand
+ /* on the first time through we read the magic file */
+ result = apprentice(_configfile);
+ if (result == -1)
+ return;
+#ifdef MIME_MAGIC_DEBUG_TABLE
+ test_table();
+#endif
+}
+
+/*
+ * The destructor.
+ * Free the magic-table and other resources.
+ */
+KMimeMagic::~KMimeMagic()
+{
+ if (conf) {
+ struct magic *p = conf->magic;
+ struct magic *q;
+ while (p) {
+ q = p;
+ p = p->next;
+ free(q);
+ }
+ delete conf->utimeConf;
+ delete conf;
+ }
+ delete magicResult;
+}
+
+bool
+KMimeMagic::mergeConfig(const TQString & _configfile)
+{
+ kdDebug(7018) << k_funcinfo << _configfile << endl;
+ int result;
+
+ if (_configfile.isEmpty())
+ return false;
+ result = apprentice(_configfile);
+ if (result == -1) {
+ return false;
+ }
+#ifdef MIME_MAGIC_DEBUG_TABLE
+ test_table();
+#endif
+ return true;
+}
+
+bool
+KMimeMagic::mergeBufConfig(char * _configbuf)
+{
+ int result;
+
+ if (conf) {
+ result = buff_apprentice(_configbuf);
+ if (result == -1)
+ return false;
+#ifdef MIME_MAGIC_DEBUG_TABLE
+ test_table();
+#endif
+ return true;
+ }
+ return false;
+}
+
+void
+KMimeMagic::setFollowLinks( bool _enable )
+{
+ conf->followLinks = _enable;
+}
+
+KMimeMagicResult *
+KMimeMagic::findBufferType(const TQByteArray &array)
+{
+ unsigned char buf[HOWMANY + 1]; /* one extra for terminating '\0' */
+
+ conf->resultBuf = TQString::null;
+ if ( !magicResult )
+ magicResult = new KMimeMagicResult();
+ magicResult->setInvalid();
+ conf->accuracy = 100;
+
+ int nbytes = array.size();
+
+ if (nbytes > HOWMANY)
+ nbytes = HOWMANY;
+ memcpy(buf, array.data(), nbytes);
+ if (nbytes == 0) {
+ conf->resultBuf = MIME_BINARY_ZEROSIZE;
+ } else {
+ buf[nbytes++] = '\0'; /* null-terminate it */
+ tryit(conf, buf, nbytes);
+ }
+ /* if we have any results, put them in the request structure */
+ magicResult->setMimeType(conf->resultBuf.stripWhiteSpace());
+ magicResult->setAccuracy(conf->accuracy);
+ return magicResult;
+}
+
+static void
+refineResult(KMimeMagicResult *r, const TQString & _filename)
+{
+ TQString tmp = r->mimeType();
+ if (tmp.isEmpty())
+ return;
+ if ( tmp == "text/x-c" || tmp == "text/x-objc" )
+ {
+ if ( _filename.right(2) == ".h" )
+ tmp += "hdr";
+ else
+ tmp += "src";
+ r->setMimeType(tmp);
+ }
+ else
+ if ( tmp == "text/x-c++" )
+ {
+ if ( _filename.endsWith(".h")
+ || _filename.endsWith(".hh")
+ || _filename.endsWith(".H")
+ || !_filename.right(4).contains('.'))
+ tmp += "hdr";
+ else
+ tmp += "src";
+ r->setMimeType(tmp);
+ }
+ else
+ if ( tmp == "application/x-sharedlib" )
+ {
+ if ( _filename.find( ".so" ) == -1 )
+ {
+ tmp = "application/x-executable";
+ r->setMimeType( tmp );
+ }
+ }
+}
+
+KMimeMagicResult *
+KMimeMagic::findBufferFileType( const TQByteArray &data,
+ const TQString &fn)
+{
+ KMimeMagicResult * r = findBufferType( data );
+ refineResult(r, fn);
+ return r;
+}
+
+/*
+ * Find the content-type of the given file.
+ */
+KMimeMagicResult* KMimeMagic::findFileType(const TQString & fn)
+{
+#ifdef DEBUG_MIMEMAGIC
+ kdDebug(7018) << "KMimeMagic::findFileType " << fn << endl;
+#endif
+ conf->resultBuf = TQString::null;
+
+ if ( !magicResult )
+ magicResult = new KMimeMagicResult();
+ magicResult->setInvalid();
+ conf->accuracy = 100;
+
+ if ( !conf->utimeConf )
+ conf->utimeConf = new KMimeMagicUtimeConf();
+
+ /* process it based on the file contents */
+ process(conf, fn );
+
+ /* if we have any results, put them in the request structure */
+ //finishResult();
+ magicResult->setMimeType(conf->resultBuf.stripWhiteSpace());
+ magicResult->setAccuracy(conf->accuracy);
+ refineResult(magicResult, fn);
+ return magicResult;
+}
diff --git a/tdeio/tdeio/kmimemagic.h b/tdeio/tdeio/kmimemagic.h
new file mode 100644
index 000000000..f5430a219
--- /dev/null
+++ b/tdeio/tdeio/kmimemagic.h
@@ -0,0 +1,218 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/*
+ * KMimeMagic is inspired by the code of the
+ * Apache Web Server.
+ *
+ * Rewritten for KDE by Fritz Elfert
+ * fritz@kde.org
+ * Adaptations by Torben Weis <weis@kde.org>
+ * Fixes and documentation by David Faure <faure@kde.org>
+ */
+
+#ifndef KMIMEMAGIC_H
+#define KMIMEMAGIC_H
+
+#include <tqstring.h>
+#include <tdelibs_export.h>
+
+class KMimeMagic; // see below (read this one first)
+
+/**
+ * @deprecated Use KMimeType::findByContent() instead
+ * May be removed in KDE 4.0.
+ * Returned by KMimeMagic @p find...Type methods.
+ *
+ * It contains the mimetype and the encoding of
+ * the file or buffer read.
+ */
+class TDEIO_EXPORT_DEPRECATED KMimeMagicResult
+{
+public:
+ KMimeMagicResult() { m_iAccuracy = 100; }
+ ~KMimeMagicResult() { }
+
+ /**
+ * Retrieve the mimetype (e.g. "text/html") of the file or buffer parsed.
+ */
+ TQString mimeType() const { return m_strMimeType; }
+ /**
+ * Retrieve the accuracy of the matching.
+ */
+ int accuracy() const { return m_iAccuracy; }
+ /**
+ * Returns whether the result is valid (i.e. mimetype not empty).
+ */
+ bool isValid() const { return !m_strMimeType.isEmpty(); }
+
+ /////////////////
+ // Internal functions only
+ /////////////////
+ void setMimeType( const TQString& _mime ) { m_strMimeType = _mime; }
+ void setAccuracy( int _accuracy ) { m_iAccuracy = _accuracy; }
+ void setInvalid() { m_strMimeType = TQString::null; }
+
+protected:
+ TQString m_strMimeType;
+ int m_iAccuracy;
+};
+
+/**
+ * @deprecated Use KMimeType::findByContent() instead
+ * May be removed in KDE 4.0.
+ * Determine auto-magically the type of file,
+ * not only by using its extension, but also by reading its contents.
+ *
+ *
+ * Unless specified otherwise, KMimeMagic uses
+ * $TDEDIR/share/mimelnk/magic for this purpose.
+ *
+ * To make KMimeMagic restore the 'atime' of a file after it opened it,
+ * add its directory in kmimemagicrc like:
+ * [Settings]
+ * atimeDirs=/tmp,/var/tmp,/home/dfaure/tmp
+ * This isn't done by default because it changes the 'ctime'.
+ * See kmimemagic.cpp for a full discussion on this issue.
+ *
+ * The basic usage of KMimeMagic is :
+ * @li Get a pointer to it, using KMimeMagic::self().
+ * @li Use it for any file or buffer you want, using one of the three
+ * @p find...Type() methods.
+ *
+ * The result is contained in the class KMimeMagicResult.
+ */
+class TDEIO_EXPORT_DEPRECATED KMimeMagic
+{
+public:
+ /**
+ * Create a parser and initialize it with the KDE-global data:
+ * the "magic" config file as well as the snippets from share/config/magic.
+ * @since 3.1
+ */
+ KMimeMagic();
+
+ /**
+ * Create a parser and initialize it with the given config file.
+ */
+ KMimeMagic( const TQString & configFile );
+
+ /**
+ * Destroy the parser.
+ */
+ ~KMimeMagic();
+
+ /**
+ * Merge an existing parse table with the data from the
+ * given file.
+ *
+ * @return @p true on success.
+ */
+ bool mergeConfig( const TQString & configFile );
+
+ /**
+ * Merge an existing parse table with the data from the
+ * given buffer.
+ *
+ * @return @p true on success.
+ */
+ bool mergeBufConfig(char *);
+
+ /**
+ * Enable/Disable follow-links.
+ *
+ * (Default is disabled.)
+ */
+ void setFollowLinks( bool _enable );
+
+ /**
+ * Try to find a MimeType for the given file.
+ *
+ * If no special
+ * MimeType is found, the default MimeType is returned.
+ * This function looks at the content of the file.
+ *
+ * @return A pointer to the result object. Do @em not delete the
+ * result object. After another call to KMimeMagic
+ * the returned result object changes its value
+ * since it is reused by KMimeMagic.
+ */
+ KMimeMagicResult* findFileType( const TQString & _filename );
+
+ /**
+ * Same functionality as above, except data is not
+ * read from a file.
+ *
+ * Instead a buffer can be supplied which
+ * is examined.
+ *
+ * @return A pointer to the result object. Do @em not delete the
+ * result object. After another call to KMimeMagic
+ * the returned result object changes its value
+ * since it is reused by KMimeMagic.
+ */
+ KMimeMagicResult* findBufferType( const TQByteArray &p );
+
+ /**
+ * Same functionality as findBufferType() but with
+ * additional capability of distinguishing between
+ * C-headers and C-Source.
+ *
+ * For this purpose this function looks
+ * at the extension of the filename. This means that 'filename'
+ * can be a filename on some FTP server, too.
+ *
+ * @return A pointer to the result object. Do @em not delete the
+ * result object. After another call to KMimeMagic
+ * the returned result object changes its value
+ * since it is reused by KMimeMagic.
+ */
+ KMimeMagicResult * findBufferFileType( const TQByteArray &, const TQString & filename );
+
+ /**
+ * Returns a pointer to the unique KMimeMagic instance in this process.
+ */
+ static KMimeMagic* self();
+
+protected:
+ /**
+ * The result type.
+ */
+ KMimeMagicResult * magicResult;
+
+ static void initStatic();
+ static KMimeMagic* s_pSelf;
+
+private:
+ void init( const TQString& configFile );
+
+ bool bunused;
+ TQString sunused;
+
+ int parse_line(char *line, int *rule, int lineno);
+ int parse(char *, int);
+ int buff_apprentice(char*buff);
+ int apprentice(const TQString &configFile);
+
+ struct config_rec *conf; // this is also our "d pointer"
+ int iunused;
+};
+
+#endif
+
diff --git a/tdeio/tdeio/kmimetype.cpp b/tdeio/tdeio/kmimetype.cpp
new file mode 100644
index 000000000..344be793a
--- /dev/null
+++ b/tdeio/tdeio/kmimetype.cpp
@@ -0,0 +1,1172 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 1999 Waldo Bastian <bastian@kde.org>
+ * David Faure <faure@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+// $Id$
+
+#include <config.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <kprotocolinfo.h>
+#include <tdeio/global.h>
+#include "kmimetype.h"
+#include "kservicetypefactory.h"
+#include "kmimemagic.h"
+#include "kservice.h"
+#include "krun.h"
+#include "kautomount.h"
+#include <kdirnotify_stub.h>
+
+#include <tqstring.h>
+#include <tqfile.h>
+#include <kmessageboxwrapper.h>
+
+#include <dcopclient.h>
+#include <dcopref.h>
+#include <kapplication.h>
+#include <kprocess.h>
+#include <kdebug.h>
+#include <kdesktopfile.h>
+#include <kdirwatch.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <ksimpleconfig.h>
+#include <kstandarddirs.h>
+#include <kurl.h>
+#include <tdesycoca.h>
+#include <kde_file.h>
+
+template class KSharedPtr<KMimeType>;
+template class TQValueList<KMimeType::Ptr>;
+
+KMimeType::Ptr KMimeType::s_pDefaultType = 0L;
+bool KMimeType::s_bChecked = false;
+
+void KMimeType::buildDefaultType()
+{
+ assert ( !s_pDefaultType );
+ // Try to find the default type
+ KServiceType * mime = KServiceTypeFactory::self()->
+ findServiceTypeByName( defaultMimeType() );
+
+ if (mime && mime->isType( KST_KMimeType ))
+ {
+ s_pDefaultType = KMimeType::Ptr((KMimeType *) mime);
+ }
+ else
+ {
+ errorMissingMimeType( defaultMimeType() );
+ KStandardDirs stdDirs;
+ TQString sDefaultMimeType = stdDirs.resourceDirs("mime").first()+defaultMimeType()+".desktop";
+ s_pDefaultType = new KMimeType( sDefaultMimeType, defaultMimeType(),
+ "unknown", "mime", TQStringList() );
+ }
+}
+
+KMimeType::Ptr KMimeType::defaultMimeTypePtr()
+{
+ if ( !s_pDefaultType ) // we need a default type first
+ buildDefaultType();
+ return s_pDefaultType;
+}
+
+// Check for essential mimetypes
+void KMimeType::checkEssentialMimeTypes()
+{
+ if ( s_bChecked ) // already done
+ return;
+ if ( !s_pDefaultType ) // we need a default type first
+ buildDefaultType();
+
+ s_bChecked = true; // must be done before building mimetypes
+
+ // No Mime-Types installed ?
+ // Lets do some rescue here.
+ if ( !KServiceTypeFactory::self()->checkMimeTypes() )
+ {
+ KMessageBoxWrapper::error( 0L, i18n( "No mime types installed." ) );
+ return; // no point in going any further
+ }
+
+ if ( KMimeType::mimeType( "inode/directory" ) == s_pDefaultType )
+ errorMissingMimeType( "inode/directory" );
+ if ( KMimeType::mimeType( "inode/directory-locked" ) == s_pDefaultType )
+ errorMissingMimeType( "inode/directory-locked" );
+ if ( KMimeType::mimeType( "inode/blockdevice" ) == s_pDefaultType )
+ errorMissingMimeType( "inode/blockdevice" );
+ if ( KMimeType::mimeType( "inode/chardevice" ) == s_pDefaultType )
+ errorMissingMimeType( "inode/chardevice" );
+ if ( KMimeType::mimeType( "inode/socket" ) == s_pDefaultType )
+ errorMissingMimeType( "inode/socket" );
+ if ( KMimeType::mimeType( "inode/fifo" ) == s_pDefaultType )
+ errorMissingMimeType( "inode/fifo" );
+ if ( KMimeType::mimeType( "application/x-shellscript" ) == s_pDefaultType )
+ errorMissingMimeType( "application/x-shellscript" );
+ if ( KMimeType::mimeType( "application/x-executable" ) == s_pDefaultType )
+ errorMissingMimeType( "application/x-executable" );
+ if ( KMimeType::mimeType( "application/x-desktop" ) == s_pDefaultType )
+ errorMissingMimeType( "application/x-desktop" );
+}
+
+void KMimeType::errorMissingMimeType( const TQString& _type )
+{
+ TQString tmp = i18n( "Could not find mime type\n%1" ).arg( _type );
+
+ KMessageBoxWrapper::sorry( 0, tmp );
+}
+
+KMimeType::Ptr KMimeType::mimeType( const TQString& _name )
+{
+ KServiceType * mime = KServiceTypeFactory::self()->findServiceTypeByName( _name );
+
+ if ( !mime || !mime->isType( KST_KMimeType ) )
+ {
+ // When building tdesycoca, findServiceTypeByName doesn't create an object
+ // but returns one from a dict.
+ if ( !KSycoca::self()->isBuilding() )
+ delete mime;
+ if ( !s_pDefaultType )
+ buildDefaultType();
+ return s_pDefaultType;
+ }
+
+ // We got a mimetype
+ return KMimeType::Ptr((KMimeType *) mime);
+}
+
+KMimeType::List KMimeType::allMimeTypes()
+{
+ return KServiceTypeFactory::self()->allMimeTypes();
+}
+
+KMimeType::Ptr KMimeType::findByURL( const KURL& _url, mode_t _mode,
+ bool _is_local_file, bool _fast_mode )
+{
+ checkEssentialMimeTypes();
+ TQString path = _url.path();
+
+ if ( !_fast_mode && !_is_local_file && _url.isLocalFile() )
+ _is_local_file = true;
+
+ if ( !_fast_mode && _is_local_file && (_mode == 0 || _mode == (mode_t)-1) )
+ {
+ KDE_struct_stat buff;
+ if ( KDE_stat( TQFile::encodeName(path), &buff ) != -1 )
+ _mode = buff.st_mode;
+ }
+
+ // Look at mode_t first
+ if ( S_ISDIR( _mode ) )
+ {
+ // Special hack for local files. We want to see whether we
+ // are allowed to enter the directory
+ if ( _is_local_file )
+ {
+ if ( access( TQFile::encodeName(path), R_OK ) == -1 )
+ return mimeType( "inode/directory-locked" );
+ }
+ return mimeType( "inode/directory" );
+ }
+ if ( S_ISCHR( _mode ) )
+ return mimeType( "inode/chardevice" );
+ if ( S_ISBLK( _mode ) )
+ return mimeType( "inode/blockdevice" );
+ if ( S_ISFIFO( _mode ) )
+ return mimeType( "inode/fifo" );
+ if ( S_ISSOCK( _mode ) )
+ return mimeType( "inode/socket" );
+ // KMimeMagic can do that better for local files
+ if ( !_is_local_file && S_ISREG( _mode ) && ( _mode & ( S_IXUSR | S_IXGRP | S_IXOTH ) ) )
+ return mimeType( "application/x-executable" );
+
+ TQString fileName ( _url.fileName() );
+
+ static const TQString& slash = TDEGlobal::staticQString("/");
+ if ( ! fileName.isNull() && !path.endsWith( slash ) )
+ {
+ // Try to find it out by looking at the filename
+ KMimeType::Ptr mime = KServiceTypeFactory::self()->findFromPattern( fileName );
+ if ( mime )
+ {
+ // Found something - can we trust it ? (e.g. don't trust *.pl over HTTP, could be anything)
+ if ( _is_local_file || _url.hasSubURL() || // Explicitly trust suburls
+ KProtocolInfo::determineMimetypeFromExtension( _url.protocol() ) )
+ {
+ if ( _is_local_file && !_fast_mode ) {
+ if ( mime->patternsAccuracy()<100 )
+ {
+ KMimeMagicResult* result =
+ KMimeMagic::self()->findFileType( path );
+
+ if ( result && result->isValid() && result->accuracy() > 0 )
+ return mimeType( result->mimeType() );
+ }
+ }
+
+ return mime;
+ }
+ }
+
+ static const TQString& dotdesktop = TDEGlobal::staticQString(".desktop");
+ static const TQString& dotkdelnk = TDEGlobal::staticQString(".kdelnk");
+ static const TQString& dotdirectory = TDEGlobal::staticQString(".directory");
+
+ // Another filename binding, hardcoded, is .desktop:
+ if ( fileName.endsWith( dotdesktop ) )
+ return mimeType( "application/x-desktop" );
+ // Another filename binding, hardcoded, is .kdelnk;
+ // this is preserved for backwards compatibility
+ if ( fileName.endsWith( dotkdelnk ) )
+ return mimeType( "application/x-desktop" );
+ // .directory files are detected as x-desktop by mimemagic
+ // but don't have a Type= entry. Better cheat and say they are text files
+ if ( fileName == dotdirectory )
+ return mimeType( "text/plain" );
+ }
+
+ if ( !_is_local_file || _fast_mode )
+ {
+ TQString def = KProtocolInfo::defaultMimetype( _url );
+ if ( !def.isEmpty() && def != defaultMimeType() )
+ {
+ // The protocol says it always returns a given mimetype (e.g. text/html for "man:")
+ return mimeType( def );
+ }
+ if ( path.endsWith( slash ) || path.isEmpty() )
+ {
+ // We have no filename at all. Maybe the protocol has a setting for
+ // which mimetype this means (e.g. directory).
+ // For HTTP (def==defaultMimeType()) we don't assume anything,
+ // because of redirections (e.g. freshmeat downloads).
+ if ( def.isEmpty() )
+ {
+ // Assume inode/directory, if the protocol supports listing.
+ if ( KProtocolInfo::supportsListing( _url ) )
+ return mimeType( TQString::fromLatin1("inode/directory") );
+ else
+ return defaultMimeTypePtr(); // == 'no idea', e.g. for "data:,foo/"
+ }
+ }
+
+ // No more chances for non local URLs
+ return defaultMimeTypePtr();
+ }
+
+ // Do some magic for local files
+ //kdDebug(7009) << TQString("Mime Type finding for '%1'").arg(path) << endl;
+ KMimeMagicResult* result = KMimeMagic::self()->findFileType( path );
+
+ // If we still did not find it, we must assume the default mime type
+ if ( !result || !result->isValid() )
+ return defaultMimeTypePtr();
+
+ // The mimemagic stuff was successful
+ return mimeType( result->mimeType() );
+}
+
+KMimeType::Ptr KMimeType::findByURL( const KURL& _url, mode_t _mode,
+ bool _is_local_file, bool _fast_mode,
+ bool *accurate)
+{
+ KMimeType::Ptr mime = findByURL(_url, _mode, _is_local_file, _fast_mode);
+ if (accurate) *accurate = !(_fast_mode) || ((mime->patternsAccuracy() == 100) && mime != defaultMimeTypePtr());
+ return mime;
+}
+
+KMimeType::Ptr KMimeType::diagnoseFileName(const TQString &fileName, TQString &pattern)
+{
+ return KServiceTypeFactory::self()->findFromPattern( fileName, &pattern );
+}
+
+KMimeType::Ptr KMimeType::findByPath( const TQString& path, mode_t mode, bool fast_mode )
+{
+ KURL u;
+ u.setPath(path);
+ return findByURL( u, mode, true, fast_mode );
+}
+
+KMimeType::Ptr KMimeType::findByContent( const TQByteArray &data, int *accuracy )
+{
+ KMimeMagicResult *result = KMimeMagic::self()->findBufferType(data);
+ if (accuracy)
+ *accuracy = result->accuracy();
+ return mimeType( result->mimeType() );
+}
+
+KMimeType::Ptr KMimeType::findByFileContent( const TQString &fileName, int *accuracy )
+{
+ KMimeMagicResult *result = KMimeMagic::self()->findFileType(fileName);
+ if (accuracy)
+ *accuracy = result->accuracy();
+ return mimeType( result->mimeType() );
+}
+
+#define GZIP_MAGIC1 0x1f
+#define GZIP_MAGIC2 0x8b
+
+KMimeType::Format KMimeType::findFormatByFileContent( const TQString &fileName )
+{
+ KMimeType::Format result;
+ result.compression = Format::NoCompression;
+ KMimeType::Ptr mime = findByPath(fileName);
+
+ result.text = mime->name().startsWith("text/");
+ TQVariant v = mime->property("X-TDE-text");
+ if (v.isValid())
+ result.text = v.toBool();
+
+ if (mime->name().startsWith("inode/"))
+ return result;
+
+ TQFile f(fileName);
+ if (f.open(IO_ReadOnly))
+ {
+ unsigned char buf[10+1];
+ int l = f.readBlock((char *)buf, 10);
+ if ((l > 2) && (buf[0] == GZIP_MAGIC1) && (buf[1] == GZIP_MAGIC2))
+ result.compression = Format::GZipCompression;
+ }
+ return result;
+}
+
+KMimeType::KMimeType( const TQString & _fullpath, const TQString& _type, const TQString& _icon,
+ const TQString& _comment, const TQStringList& _patterns )
+ : KServiceType( _fullpath, _type, _icon, _comment )
+{
+ m_lstPatterns = _patterns;
+}
+
+KMimeType::KMimeType( const TQString & _fullpath ) : KServiceType( _fullpath )
+{
+ KDesktopFile _cfg( _fullpath, true );
+ init ( &_cfg );
+
+ if ( !isValid() )
+ kdWarning(7009) << "mimetype not valid '" << m_strName << "' (missing entry in the file ?)" << endl;
+}
+
+KMimeType::KMimeType( KDesktopFile *config ) : KServiceType( config )
+{
+ init( config );
+
+ if ( !isValid() )
+ kdWarning(7009) << "mimetype not valid '" << m_strName << "' (missing entry in the file ?)" << endl;
+}
+
+void KMimeType::init( KDesktopFile * config )
+{
+ config->setDesktopGroup();
+ m_lstPatterns = config->readListEntry( "Patterns", ';' );
+
+ // Read the X-TDE-AutoEmbed setting and store it in the properties map
+ TQString XKDEAutoEmbed = TQString::fromLatin1("X-TDE-AutoEmbed");
+ if ( config->hasKey( XKDEAutoEmbed ) )
+ m_mapProps.insert( XKDEAutoEmbed, TQVariant( config->readBoolEntry( XKDEAutoEmbed ), 0 ) );
+
+ TQString XKDEText = TQString::fromLatin1("X-TDE-text");
+ if ( config->hasKey( XKDEText ) )
+ m_mapProps.insert( XKDEText, config->readBoolEntry( XKDEText ) );
+
+ TQString XKDEIsAlso = TQString::fromLatin1("X-TDE-IsAlso");
+ if ( config->hasKey( XKDEIsAlso ) ) {
+ TQString inherits = config->readEntry( XKDEIsAlso );
+ if ( inherits != name() )
+ m_mapProps.insert( XKDEIsAlso, inherits );
+ else
+ kdWarning(7009) << "Error: " << inherits << " inherits from itself!!!!" << endl;
+ }
+
+ TQString XKDEPatternsAccuracy = TQString::fromLatin1("X-TDE-PatternsAccuracy");
+ if ( config->hasKey( XKDEPatternsAccuracy ) )
+ m_mapProps.insert( XKDEPatternsAccuracy, config->readEntry( XKDEPatternsAccuracy ) );
+
+}
+
+KMimeType::KMimeType( TQDataStream& _str, int offset ) : KServiceType( _str, offset )
+{
+ loadInternal( _str ); // load our specific stuff
+}
+
+void KMimeType::load( TQDataStream& _str )
+{
+ KServiceType::load( _str );
+ loadInternal( _str );
+}
+
+void KMimeType::loadInternal( TQDataStream& _str )
+{
+ // kdDebug(7009) << "KMimeType::load( TQDataStream& ) : loading list of patterns" << endl;
+ _str >> m_lstPatterns;
+}
+
+void KMimeType::save( TQDataStream& _str )
+{
+ KServiceType::save( _str );
+ // Warning adding/removing fields here involves a binary incompatible change - update version
+ // number in tdesycoca.h
+ _str << m_lstPatterns;
+}
+
+TQVariant KMimeType::property( const TQString& _name ) const
+{
+ if ( _name == "Patterns" )
+ return TQVariant( m_lstPatterns );
+
+ return KServiceType::property( _name );
+}
+
+TQStringList KMimeType::propertyNames() const
+{
+ TQStringList res = KServiceType::propertyNames();
+ res.append( "Patterns" );
+
+ return res;
+}
+
+KMimeType::~KMimeType()
+{
+}
+
+TQPixmap KMimeType::pixmap( KIcon::Group _group, int _force_size, int _state,
+ TQString * _path ) const
+{
+ KIconLoader *iconLoader=TDEGlobal::iconLoader();
+ TQString iconName=icon( TQString::null, false );
+ if (!iconLoader->extraDesktopThemesAdded())
+ {
+ TQPixmap pixmap=iconLoader->loadIcon( iconName, _group, _force_size, _state, _path, true );
+ if (!pixmap.isNull() ) return pixmap;
+
+ iconLoader->addExtraDesktopThemes();
+ }
+
+ return iconLoader->loadIcon( iconName , _group, _force_size, _state, _path, false );
+}
+
+TQPixmap KMimeType::pixmap( const KURL& _url, KIcon::Group _group, int _force_size,
+ int _state, TQString * _path ) const
+{
+ KIconLoader *iconLoader=TDEGlobal::iconLoader();
+ TQString iconName=icon( _url, _url.isLocalFile() );
+ if (!iconLoader->extraDesktopThemesAdded())
+ {
+ TQPixmap pixmap=iconLoader->loadIcon( iconName, _group, _force_size, _state, _path, true );
+ if (!pixmap.isNull() ) return pixmap;
+
+ iconLoader->addExtraDesktopThemes();
+ }
+
+ return iconLoader->loadIcon( iconName , _group, _force_size, _state, _path, false );
+}
+
+TQPixmap KMimeType::pixmapForURL( const KURL & _url, mode_t _mode, KIcon::Group _group,
+ int _force_size, int _state, TQString * _path )
+{
+ KIconLoader *iconLoader=TDEGlobal::iconLoader();
+ TQString iconName = iconForURL( _url, _mode );
+
+ if (!iconLoader->extraDesktopThemesAdded())
+ {
+ TQPixmap pixmap=iconLoader->loadIcon( iconName, _group, _force_size, _state, _path, true );
+ if (!pixmap.isNull() ) return pixmap;
+
+ iconLoader->addExtraDesktopThemes();
+ }
+
+ return iconLoader->loadIcon( iconName , _group, _force_size, _state, _path, false );
+
+}
+
+TQString KMimeType::iconForURL( const KURL & _url, mode_t _mode )
+{
+ const KMimeType::Ptr mt = findByURL( _url, _mode, _url.isLocalFile(),
+ false /*HACK*/);
+ static const TQString& unknown = TDEGlobal::staticQString("unknown");
+ const TQString mimeTypeIcon = mt->icon( _url, _url.isLocalFile() );
+ TQString i = mimeTypeIcon;
+
+ // if we don't find an icon, maybe we can use the one for the protocol
+ if ( i == unknown || i.isEmpty() || mt == defaultMimeTypePtr()
+ // and for the root of the protocol (e.g. trash:/) the protocol icon has priority over the mimetype icon
+ || _url.path().length() <= 1 )
+ {
+ i = favIconForURL( _url ); // maybe there is a favicon?
+
+ if ( i.isEmpty() )
+ i = KProtocolInfo::icon( _url.protocol() );
+
+ // root of protocol: if we found nothing, revert to mimeTypeIcon (which is usually "folder")
+ if ( _url.path().length() <= 1 && ( i == unknown || i.isEmpty() ) )
+ i = mimeTypeIcon;
+ }
+ return i;
+}
+
+TQString KMimeType::favIconForURL( const KURL& url )
+{
+ // this method will be called quite often, so better not read the config
+ // again and again.
+ static bool useFavIcons = true;
+ static bool check = true;
+ if ( check ) {
+ check = false;
+ TDEConfig *config = TDEGlobal::config();
+ TDEConfigGroupSaver cs( config, "HTML Settings" );
+ useFavIcons = config->readBoolEntry( "EnableFavicon", true );
+ }
+
+ if ( url.isLocalFile() || !url.protocol().startsWith("http")
+ || !useFavIcons )
+ return TQString::null;
+
+ DCOPRef kded( "kded", "favicons" );
+ DCOPReply result = kded.call( "iconForURL(KURL)", url );
+ if ( result.isValid() )
+ return result;
+
+ return TQString::null;
+}
+
+TQString KMimeType::parentMimeType() const
+{
+ TQVariant v = property("X-TDE-IsAlso");
+ return v.toString();
+}
+
+bool KMimeType::is( const TQString& mimeTypeName ) const
+{
+ if ( name() == mimeTypeName )
+ return true;
+ TQString st = parentMimeType();
+ //if (st.isEmpty()) kdDebug(7009)<<"Parent mimetype is empty"<<endl;
+ while ( !st.isEmpty() )
+ {
+ //kdDebug(7009)<<"Checking parent mime type: "<<st<<endl;
+ KMimeType::Ptr ptr = KMimeType::mimeType( st );
+ if (!ptr) return false; //error
+ if ( ptr->name() == mimeTypeName )
+ return true;
+ st = ptr->parentMimeType();
+ }
+ return false;
+}
+
+int KMimeType::patternsAccuracy() const {
+ TQVariant v = property("X-TDE-PatternsAccuracy");
+ if (!v.isValid()) return 100;
+ else
+ return v.toInt();
+}
+
+
+/*******************************************************
+ *
+ * KFolderType
+ *
+ ******************************************************/
+
+TQString KFolderType::icon( const TQString& _url, bool _is_local ) const
+{
+ if ( !_is_local || _url.isEmpty() )
+ return KMimeType::icon( _url, _is_local );
+
+ return KFolderType::icon( KURL(_url), _is_local );
+}
+
+TQString KFolderType::icon( const KURL& _url, bool _is_local ) const
+{
+ if ( !_is_local )
+ return KMimeType::icon( _url, _is_local );
+
+ KURL u( _url );
+ u.addPath( ".directory" );
+
+ TQString icon;
+ // using KStandardDirs as this one checks for path being
+ // a file instead of a directory
+ if ( KStandardDirs::exists( u.path() ) )
+ {
+ KSimpleConfig cfg( u.path(), true );
+ cfg.setDesktopGroup();
+ icon = cfg.readEntry( "Icon" );
+ TQString empty_icon = cfg.readEntry( "EmptyIcon" );
+
+ if ( !empty_icon.isEmpty() )
+ {
+ bool isempty = false;
+ DIR *dp = 0L;
+ struct dirent *ep;
+ dp = opendir( TQFile::encodeName(_url.path()) );
+ if ( dp )
+ {
+ TQValueList<TQCString> entries;
+ // Note that readdir isn't guaranteed to return "." and ".." first (#79826)
+ ep=readdir( dp ); if ( ep ) entries.append( ep->d_name );
+ ep=readdir( dp ); if ( ep ) entries.append( ep->d_name );
+ if ( (ep=readdir( dp )) == 0L ) // third file is NULL entry -> empty directory
+ isempty = true;
+ else {
+ entries.append( ep->d_name );
+ if ( readdir( dp ) == 0 ) { // only three
+ // check if we got "." ".." and ".directory"
+ isempty = entries.find( "." ) != entries.end() &&
+ entries.find( ".." ) != entries.end() &&
+ entries.find( ".directory" ) != entries.end();
+ }
+ }
+ if (!isempty && !strcmp(ep->d_name, ".directory"))
+ isempty = (readdir(dp) == 0L);
+ closedir( dp );
+ }
+
+ if ( isempty )
+ return empty_icon;
+ }
+ }
+
+ if ( icon.isEmpty() )
+ return KMimeType::icon( _url, _is_local );
+
+ if ( icon.startsWith( "./" ) ) {
+ // path is relative with respect to the location
+ // of the .directory file (#73463)
+ KURL v( _url );
+ v.addPath( icon.mid( 2 ) );
+ icon = v.path();
+ }
+
+ return icon;
+}
+
+TQString KFolderType::comment( const TQString& _url, bool _is_local ) const
+{
+ if ( !_is_local || _url.isEmpty() )
+ return KMimeType::comment( _url, _is_local );
+
+ return KFolderType::comment( KURL(_url), _is_local );
+}
+
+TQString KFolderType::comment( const KURL& _url, bool _is_local ) const
+{
+ if ( !_is_local )
+ return KMimeType::comment( _url, _is_local );
+
+ KURL u( _url );
+ u.addPath( ".directory" );
+
+ KDesktopFile cfg( u.path(), true );
+ TQString comment = cfg.readComment();
+ if ( comment.isEmpty() )
+ return KMimeType::comment( _url, _is_local );
+
+ return comment;
+}
+
+/*******************************************************
+ *
+ * KDEDesktopMimeType
+ *
+ ******************************************************/
+
+TQString KDEDesktopMimeType::icon( const TQString& _url, bool _is_local ) const
+{
+ if ( !_is_local || _url.isEmpty() )
+ return KMimeType::icon( _url, _is_local );
+
+ KURL u( _url );
+ return icon( u, _is_local );
+}
+
+TQString KDEDesktopMimeType::icon( const KURL& _url, bool _is_local ) const
+{
+ if ( !_is_local )
+ return KMimeType::icon( _url, _is_local );
+
+ KSimpleConfig cfg( _url.path(), true );
+ cfg.setDesktopGroup();
+ TQString icon = cfg.readEntry( "Icon" );
+ TQString type = cfg.readEntry( "Type" );
+
+ if ( type == "FSDevice" || type == "FSDev") // need to provide FSDev for
+ // backwards compatibility
+ {
+ TQString unmount_icon = cfg.readEntry( "UnmountIcon" );
+ TQString dev = cfg.readEntry( "Dev" );
+ if ( !icon.isEmpty() && !unmount_icon.isEmpty() && !dev.isEmpty() )
+ {
+ TQString mp = TDEIO::findDeviceMountPoint( dev );
+ // Is the device not mounted ?
+ if ( mp.isNull() )
+ return unmount_icon;
+ }
+ } else if ( type == "Link" ) {
+ const TQString emptyIcon = cfg.readEntry( "EmptyIcon" );
+ if ( !emptyIcon.isEmpty() ) {
+ const TQString u = cfg.readPathEntry( "URL" );
+ const KURL url( u );
+ if ( url.protocol() == "trash" ) {
+ // We need to find if the trash is empty, preferrably without using a KIO job.
+ // So instead kio_trash leaves an entry in its config file for us.
+ KSimpleConfig trashConfig( "trashrc", true );
+ trashConfig.setGroup( "Status" );
+ if ( trashConfig.readBoolEntry( "Empty", true ) ) {
+ return emptyIcon;
+ }
+ }
+ }
+ }
+
+ if ( icon.isEmpty() )
+ return KMimeType::icon( _url, _is_local );
+
+ return icon;
+}
+
+TQPixmap KDEDesktopMimeType::pixmap( const KURL& _url, KIcon::Group _group, int _force_size,
+ int _state, TQString * _path ) const
+{
+ TQString _icon = icon( _url, _url.isLocalFile() );
+ TQPixmap pix = TDEGlobal::iconLoader()->loadIcon( _icon, _group,
+ _force_size, _state, _path, false );
+ if ( pix.isNull() )
+ pix = TDEGlobal::iconLoader()->loadIcon( "unknown", _group,
+ _force_size, _state, _path, false );
+ return pix;
+}
+
+TQString KDEDesktopMimeType::comment( const TQString& _url, bool _is_local ) const
+{
+ if ( !_is_local || _url.isEmpty() )
+ return KMimeType::comment( _url, _is_local );
+
+ KURL u( _url );
+ return comment( u, _is_local );
+}
+
+TQString KDEDesktopMimeType::comment( const KURL& _url, bool _is_local ) const
+{
+ if ( !_is_local )
+ return KMimeType::comment( _url, _is_local );
+
+ KDesktopFile cfg( _url.path(), true );
+ TQString comment = cfg.readComment();
+ if ( comment.isEmpty() )
+ return KMimeType::comment( _url, _is_local );
+
+ return comment;
+}
+
+pid_t KDEDesktopMimeType::run( const KURL& u, bool _is_local )
+{
+ // It might be a security problem to run external untrusted desktop
+ // entry files
+ if ( !_is_local )
+ return 0;
+
+ KSimpleConfig cfg( u.path(), true );
+ cfg.setDesktopGroup();
+ TQString type = cfg.readEntry( "Type" );
+ if ( type.isEmpty() )
+ {
+ TQString tmp = i18n("The desktop entry file %1 "
+ "has no Type=... entry.").arg(u.path() );
+ KMessageBoxWrapper::error( 0, tmp);
+ return 0;
+ }
+
+ //kdDebug(7009) << "TYPE = " << type.data() << endl;
+
+ if ( type == "FSDevice" )
+ return runFSDevice( u, cfg );
+ else if ( type == "Application" )
+ return runApplication( u, u.path() );
+ else if ( type == "Link" )
+ {
+ cfg.setDollarExpansion( true ); // for URL=file:$HOME (Simon)
+ return runLink( u, cfg );
+ }
+ else if ( type == "MimeType" )
+ return runMimeType( u, cfg );
+
+
+ TQString tmp = i18n("The desktop entry of type\n%1\nis unknown.").arg( type );
+ KMessageBoxWrapper::error( 0, tmp);
+
+ return 0;
+}
+
+pid_t KDEDesktopMimeType::runFSDevice( const KURL& _url, const KSimpleConfig &cfg )
+{
+ pid_t retval = 0;
+
+ TQString dev = cfg.readEntry( "Dev" );
+
+ if ( dev.isEmpty() )
+ {
+ TQString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.").arg( _url.path() );
+ KMessageBoxWrapper::error( 0, tmp);
+ return retval;
+ }
+
+ TQString mp = TDEIO::findDeviceMountPoint( dev );
+ // Is the device already mounted ?
+ if ( !mp.isNull() )
+ {
+ KURL mpURL;
+ mpURL.setPath( mp );
+ // Open a new window
+ retval = KRun::runURL( mpURL, TQString::fromLatin1("inode/directory") );
+ }
+ else
+ {
+ bool ro = cfg.readBoolEntry( "ReadOnly", false );
+ TQString fstype = cfg.readEntry( "FSType" );
+ if ( fstype == "Default" ) // KDE-1 thing
+ fstype = TQString::null;
+ TQString point = cfg.readEntry( "MountPoint" );
+#ifndef Q_WS_WIN
+ (void) new KAutoMount( ro, fstype, dev, point, _url.path() );
+#endif
+ retval = -1; // we don't want to return 0, but we don't want to return a pid
+ }
+
+ return retval;
+}
+
+pid_t KDEDesktopMimeType::runApplication( const KURL& , const TQString & _serviceFile )
+{
+ KService s( _serviceFile );
+ if ( !s.isValid() )
+ // The error message was already displayed, so we can just quit here
+ return 0;
+
+ KURL::List lst;
+ return KRun::run( s, lst );
+}
+
+pid_t KDEDesktopMimeType::runLink( const KURL& _url, const KSimpleConfig &cfg )
+{
+ TQString u = cfg.readPathEntry( "URL" );
+ if ( u.isEmpty() )
+ {
+ TQString tmp = i18n("The desktop entry file\n%1\nis of type Link but has no URL=... entry.").arg( _url.prettyURL() );
+ KMessageBoxWrapper::error( 0, tmp );
+ return 0;
+ }
+
+ KURL url ( u );
+ KRun* run = new KRun(url);
+
+ // X-TDE-LastOpenedWith holds the service desktop entry name that
+ // was should be preferred for opening this URL if possible.
+ // This is used by the Recent Documents menu for instance.
+ TQString lastOpenedWidth = cfg.readEntry( "X-TDE-LastOpenedWith" );
+ if ( !lastOpenedWidth.isEmpty() )
+ run->setPreferredService( lastOpenedWidth );
+
+ return -1; // we don't want to return 0, but we don't want to return a pid
+}
+
+pid_t KDEDesktopMimeType::runMimeType( const KURL& url , const KSimpleConfig & )
+{
+ // Hmm, can't really use keditfiletype since we might be looking
+ // at the global file, or at a file not in share/mimelnk...
+
+ TQStringList args;
+ args << "openProperties";
+ args << url.path();
+
+ int pid;
+ if ( !TDEApplication::tdeinitExec("kfmclient", args, 0, &pid) )
+ return pid;
+
+ TDEProcess p;
+ p << "kfmclient" << args;
+ p.start(TDEProcess::DontCare);
+ return p.pid();
+}
+
+TQValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::builtinServices( const KURL& _url )
+{
+ TQValueList<Service> result;
+
+ if ( !_url.isLocalFile() )
+ return result;
+
+ KSimpleConfig cfg( _url.path(), true );
+ cfg.setDesktopGroup();
+ TQString type = cfg.readEntry( "Type" );
+
+ if ( type.isEmpty() )
+ return result;
+
+ if ( type == "FSDevice" )
+ {
+ TQString dev = cfg.readEntry( "Dev" );
+ if ( dev.isEmpty() )
+ {
+ TQString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.").arg( _url.path() );
+ KMessageBoxWrapper::error( 0, tmp);
+ }
+ else
+ {
+ TQString mp = TDEIO::findDeviceMountPoint( dev );
+ // not mounted ?
+ if ( mp.isEmpty() )
+ {
+ Service mount;
+ mount.m_strName = i18n("Mount");
+ mount.m_type = ST_MOUNT;
+ result.append( mount );
+ }
+ else
+ {
+ Service unmount;
+#ifdef HAVE_VOLMGT
+ /*
+ * Solaris' volume management can only umount+eject
+ */
+ unmount.m_strName = i18n("Eject");
+#else
+ unmount.m_strName = i18n("Unmount");
+#endif
+ unmount.m_type = ST_UNMOUNT;
+ result.append( unmount );
+ }
+ }
+ }
+
+ return result;
+}
+
+TQValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::userDefinedServices( const TQString& path, bool bLocalFiles )
+{
+ KSimpleConfig cfg( path, true );
+ return userDefinedServices( path, cfg, bLocalFiles );
+}
+
+TQValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::userDefinedServices( const TQString& path, TDEConfig& cfg, bool bLocalFiles )
+{
+ return userDefinedServices( path, cfg, bLocalFiles, KURL::List() );
+}
+
+TQValueList<KDEDesktopMimeType::Service> KDEDesktopMimeType::userDefinedServices( const TQString& path, TDEConfig& cfg, bool bLocalFiles, const KURL::List & file_list )
+{
+ TQValueList<Service> result;
+
+ cfg.setDesktopGroup();
+
+ if ( !cfg.hasKey( "Actions" ) && !cfg.hasKey( "X-TDE-GetActionMenu") )
+ return result;
+
+ if ( cfg.hasKey( "TryExec" ) )
+ {
+ TQString tryexec = cfg.readPathEntry( "TryExec" );
+ TQString exe = KStandardDirs::findExe( tryexec );
+ if (exe.isEmpty()) {
+ return result;
+ }
+ }
+
+ TQStringList keys;
+
+ if( cfg.hasKey( "X-TDE-GetActionMenu" )) {
+ TQString dcopcall = cfg.readEntry( "X-TDE-GetActionMenu" );
+ const TQCString app = TQString(dcopcall.section(' ', 0,0)).utf8();
+
+ TQByteArray dataToSend;
+ TQDataStream dataStream(dataToSend, IO_WriteOnly);
+ dataStream << file_list;
+ TQCString replyType;
+ TQByteArray replyData;
+ TQCString object = TQString(dcopcall.section(' ', 1,-2)).utf8();
+ TQString function = dcopcall.section(' ', -1);
+ if(!function.endsWith("(KURL::List)")) {
+ kdWarning() << "Desktop file " << path << " contains an invalid X-TDE-ShowIfDcopCall - the function must take the exact parameter (KURL::List) and must be specified." << endl;
+ } else {
+ if(kapp->dcopClient()->call( app, object,
+ function.utf8(),
+ dataToSend, replyType, replyData, true, -1)
+ && replyType == "TQStringList" ) {
+
+ TQDataStream dataStreamIn(replyData, IO_ReadOnly);
+ dataStreamIn >> keys;
+ }
+ }
+ }
+
+ keys += cfg.readListEntry( "Actions", ';' ); //the desktop standard defines ";" as separator!
+
+ if ( keys.count() == 0 )
+ return result;
+
+ TQStringList::ConstIterator it = keys.begin();
+ TQStringList::ConstIterator end = keys.end();
+ for ( ; it != end; ++it )
+ {
+ //kdDebug(7009) << "CURRENT KEY = " << (*it) << endl;
+
+ TQString group = *it;
+
+ if (group == "_SEPARATOR_")
+ {
+ Service s;
+ result.append(s);
+ continue;
+ }
+
+ group.prepend( "Desktop Action " );
+
+ bool bInvalidMenu = false;
+
+ if ( cfg.hasGroup( group ) )
+ {
+ cfg.setGroup( group );
+
+ if ( !cfg.hasKey( "Name" ) || !cfg.hasKey( "Exec" ) )
+ bInvalidMenu = true;
+ else
+ {
+ TQString exec = cfg.readPathEntry( "Exec" );
+ if ( bLocalFiles || exec.contains("%U") || exec.contains("%u") )
+ {
+ Service s;
+ s.m_strName = cfg.readEntry( "Name" );
+ s.m_strIcon = cfg.readEntry( "Icon" );
+ s.m_strExec = exec;
+ s.m_type = ST_USER_DEFINED;
+ s.m_display = !cfg.readBoolEntry( "NoDisplay" );
+ result.append( s );
+ }
+ }
+ }
+ else
+ bInvalidMenu = true;
+
+ if ( bInvalidMenu )
+ {
+ TQString tmp = i18n("The desktop entry file\n%1\n has an invalid menu entry\n%2.").arg( path ).arg( *it );
+ KMessageBoxWrapper::error( 0, tmp );
+ }
+ }
+
+ return result;
+}
+
+void KDEDesktopMimeType::executeService( const TQString& _url, KDEDesktopMimeType::Service& _service )
+{
+ KURL u;
+ u.setPath(_url);
+ KURL::List lst;
+ lst.append( u );
+ executeService( lst, _service );
+}
+
+void KDEDesktopMimeType::executeService( const KURL::List& urls, KDEDesktopMimeType::Service& _service )
+{
+ //kdDebug(7009) << "EXECUTING Service " << _service.m_strName << endl;
+
+ if ( _service.m_type == ST_USER_DEFINED )
+ {
+ kdDebug() << "KDEDesktopMimeType::executeService " << _service.m_strName
+ << " first url's path=" << urls.first().path() << " exec=" << _service.m_strExec << endl;
+ KRun::run( _service.m_strExec, urls, _service.m_strName, _service.m_strIcon, _service.m_strIcon );
+ // The action may update the desktop file. Example: eject unmounts (#5129).
+ KDirNotify_stub allDirNotify("*", "KDirNotify*");
+ allDirNotify.FilesChanged( urls );
+ return;
+ }
+ else if ( _service.m_type == ST_MOUNT || _service.m_type == ST_UNMOUNT )
+ {
+ Q_ASSERT( urls.count() == 1 );
+ TQString path = urls.first().path();
+ //kdDebug(7009) << "MOUNT&UNMOUNT" << endl;
+
+ KSimpleConfig cfg( path, true );
+ cfg.setDesktopGroup();
+ TQString dev = cfg.readEntry( "Dev" );
+ if ( dev.isEmpty() )
+ {
+ TQString tmp = i18n("The desktop entry file\n%1\nis of type FSDevice but has no Dev=... entry.").arg( path );
+ KMessageBoxWrapper::error( 0, tmp );
+ return;
+ }
+ TQString mp = TDEIO::findDeviceMountPoint( dev );
+
+ if ( _service.m_type == ST_MOUNT )
+ {
+ // Already mounted? Strange, but who knows ...
+ if ( !mp.isEmpty() )
+ {
+ kdDebug(7009) << "ALREADY Mounted" << endl;
+ return;
+ }
+
+ bool ro = cfg.readBoolEntry( "ReadOnly", false );
+ TQString fstype = cfg.readEntry( "FSType" );
+ if ( fstype == "Default" ) // KDE-1 thing
+ fstype = TQString::null;
+ TQString point = cfg.readEntry( "MountPoint" );
+#ifndef Q_WS_WIN
+ (void)new KAutoMount( ro, fstype, dev, point, path, false );
+#endif
+ }
+ else if ( _service.m_type == ST_UNMOUNT )
+ {
+ // Not mounted? Strange, but who knows ...
+ if ( mp.isEmpty() )
+ return;
+
+#ifndef Q_WS_WIN
+ (void)new KAutoUnmount( mp, path );
+#endif
+ }
+ }
+ else
+ assert( 0 );
+}
+
+const TQString & KMimeType::defaultMimeType()
+{
+ static const TQString & s_strDefaultMimeType =
+ TDEGlobal::staticQString( "application/octet-stream" );
+ return s_strDefaultMimeType;
+}
+
+void KMimeType::virtual_hook( int id, void* data )
+{ KServiceType::virtual_hook( id, data ); }
+
+void KFolderType::virtual_hook( int id, void* data )
+{ KMimeType::virtual_hook( id, data ); }
+
+void KDEDesktopMimeType::virtual_hook( int id, void* data )
+{ KMimeType::virtual_hook( id, data ); }
+
+void KExecMimeType::virtual_hook( int id, void* data )
+{ KMimeType::virtual_hook( id, data ); }
+
+#include "kmimetyperesolver.moc"
+
diff --git a/tdeio/tdeio/kmimetype.h b/tdeio/tdeio/kmimetype.h
new file mode 100644
index 000000000..19a846b46
--- /dev/null
+++ b/tdeio/tdeio/kmimetype.h
@@ -0,0 +1,641 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 1999 Waldo Bastian <bastian@kde.org>
+ * David Faure <faure@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#ifndef __kmimetype_h__
+#define __kmimetype_h__
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <tqstringlist.h>
+#include <tqvaluelist.h>
+#include <tqpixmap.h>
+
+#include <kicontheme.h>
+#include <kurl.h>
+#include <tdesycocatype.h>
+#include <kservicetype.h>
+
+class KSimpleConfig;
+/**
+ * Represent a mime type, like "text/plain", and the data that is associated
+ * with it.
+ *
+ * The starting point you need is often the static methods.
+ *
+ * KMimeType inherits KServiceType because "text/plain" can be used to find
+ * services (apps and components) "which can open text/plain".
+ *
+ * @see KServiceType
+ */
+class TDEIO_EXPORT KMimeType : public KServiceType
+{
+ K_SYCOCATYPE( KST_KMimeType, KServiceType )
+
+public:
+ typedef KSharedPtr<KMimeType> Ptr;
+ typedef TQValueList<Ptr> List;
+public:
+ /**
+ * Constructor.
+ *
+ * You may pass in arguments to create a mimetype with
+ * specific properties.
+ *
+ * @param _fullpath the path to the configuration file (.desktop)
+ * @param _type the mime type itself
+ * @param _icon the name of the icon that represens the mime type
+ * @param _comment a comment describing the mime type
+ * @param _patterns a list of file globs that describes the names (or
+ * extensions) of the files with this mime type
+ */
+ KMimeType( const TQString & _fullpath, const TQString& _type, const TQString& _icon,
+ const TQString& _comment, const TQStringList& _patterns );
+
+ /**
+ * Construct a mimetype and take all information from a config file.
+ * @param _fullpath the path to the configuration file (.desktop)
+ */
+ KMimeType( const TQString & _fullpath );
+
+ /**
+ * Construct a mimetype and take all information from a desktop file.
+ * @param config the desktop configuration file that describes the mime type
+ */
+ KMimeType( KDesktopFile *config );
+
+ /**
+ * @internal Construct a service from a stream.
+ *
+ * The stream must already be positionned at the correct offset
+ */
+ KMimeType( TQDataStream& _str, int offset );
+
+ virtual ~KMimeType();
+
+ /**
+ * Return the filename of the icon associated with the mimetype.
+ *
+ * The arguments are unused, but provided so that KMimeType-derived classes
+ * can use them (e.g. KFolderType uses the URL to return one out of 2 icons)
+ *
+ * @return The path to the icon associated with this MIME type.
+ */
+ virtual TQString icon( const TQString& , bool ) const { return m_strIcon; }
+
+ /**
+ * Return the filename of the icon associated with the mimetype.
+ *
+ * The arguments are unused, but provided so that KMimeType-derived classes
+ * can use them (e.g. KFolderType uses the URL to return one out of 2 icons)
+ *
+ * @return The path to the icon associated with this MIME type.
+ */
+ virtual TQString icon( const KURL& , bool ) const { return m_strIcon; }
+
+ /**
+ * Use this function only if you don't have a special URL
+ * for which you search a pixmap.
+ *
+ * This function is useful to find
+ * out, which icon is usually chosen for a certain mime type. Since
+ * no URL is passed, it is impossible to obey icon hints in desktop
+ * entries for example.
+ * @param group The icon group where the icon is going to be used.
+ * @param force_size Override globallly configured icon size.
+ * Use 0 for the default size
+ * @param state The icon state, one of: @p KIcon::DefaultState,
+ * @p KIcon::ActiveState or @p KIcon::DisabledState.
+ * @param path Output parameter to get the full path. Seldom needed.
+ * Ignored if 0
+ * @return the pixmap of the mime type, can be a default icon if not found
+ */
+ virtual TQPixmap pixmap( KIcon::Group group, int force_size = 0, int state = 0,
+ TQString * path = 0L ) const;
+
+ /**
+ * Find the pixmap for a given file of this mimetype.
+ *
+ * Convenience method that uses icon(), but also locates and
+ * load the pixmap.
+ *
+ * @param _url URL for the file.
+ * @param _group The icon group where the icon is going to be used.
+ * @param _force_size Override globallly configured icon size.
+ * Use 0 for the default size
+ * @param _state The icon state, one of: KIcon::DefaultState,
+ * KIcon::ActiveState or KIcon::DisabledState.
+ * @param _path Output parameter to get the full path. Seldom needed.
+ * Ignored if 0
+ * @return the pixmap of the URL, can be a default icon if not found
+ */
+ virtual TQPixmap pixmap( const KURL& _url, KIcon::Group _group, int _force_size = 0,
+ int _state = 0, TQString * _path = 0L ) const;
+
+ /**
+ * Convenience method to find the pixmap for a URL.
+ *
+ * Call this one when you don't know the mimetype.
+ *
+ * @param _url URL for the file.
+ * @param _mode the mode of the file. The mode may modify the icon
+ * with overlays that show special properties of the
+ * icon. Use 0 for default
+ * @param _group The icon group where the icon is going to be used.
+ * @param _force_size Override globally configured icon size.
+ * Use 0 for the default size
+ * @param _state The icon state, one of: KIcon::DefaultState,
+ * KIcon::ActiveState or KIcon::DisabledState.
+ * @param _path Output parameter to get the full path. Seldom needed.
+ * Ignored if 0
+ * @return the pixmap of the URL, can be a default icon if not found
+ */
+ static TQPixmap pixmapForURL( const KURL & _url, mode_t _mode = 0, KIcon::Group _group = KIcon::Desktop,
+ int _force_size = 0, int _state = 0, TQString * _path = 0L );
+
+
+ /**
+ * The same functionality as pixmapForURL(), but this method returns the name
+ * of the icon to load. You'll have to use KIconLoader to load the pixmap for it.
+ * The advantage of this method is that you can store the result, and then use it
+ * later on for any kind of size.
+ * @param _url URL for the file
+ * @param _mode the mode of the file. The mode may modify the icon
+ * with overlays that show special properties of the
+ * icon. Use 0 for default
+ * @return the name of the icon. The name of a default icon if there is no icon
+ * for the mime type
+ */
+ static TQString iconForURL( const KURL & _url, mode_t _mode = 0 );
+
+ /**
+ * Return the "favicon" (see http://www.favicon.com) for the given @p url,
+ * if available. Does NOT attempt to download the favicon, it only returns
+ * one that is already available.
+ *
+ * If unavailable, returns TQString::null.
+ * @param url the URL of the favicon
+ * @return the name of the favicon, or TQString::null
+ */
+ static TQString favIconForURL( const KURL& url );
+
+ /**
+ * Returns the descriptive comment associated with the MIME type.
+ * @return the descriptive comment associated with the MIME type
+ */
+ TQString comment() const { return m_strComment; }
+
+ /**
+ * Returns the descriptive comment associated with the MIME type.
+ * The arguments are unused, but provided so that KMimeType derived classes
+ * can use them.
+ *
+ * @return The descriptive comment associated with the MIME type, if any.
+ */
+ virtual TQString comment( const TQString&, bool ) const { return m_strComment; }
+
+ /**
+ * Returns the descriptive comment associated with the MIME type.
+ * The arguments are unused, but provided so that KMimeType derived classes
+ * can use them.
+ *
+ * @return The descriptive comment associated with the MIME type, if any.
+ */
+ virtual TQString comment( const KURL&, bool ) const { return m_strComment; }
+
+ /**
+ * Retrieve the list of patterns associated with the MIME Type.
+ * @return a list of file globs that describe the file names
+ * (or, usually, the extensions) of files with this mime type
+ */
+ const TQStringList& patterns() const { return m_lstPatterns; }
+
+ /**
+ * Load the mimetype from a stream.
+ * @param qs the stream to load from
+ */
+ virtual void load( TQDataStream &qs );
+
+ /**
+ * Save the mimetype to a stream.
+ * @param qs the stream to save to
+ */
+ virtual void save( TQDataStream &qs );
+
+ /**
+ * Returns the property with the given @p _name.
+ * @param _name the name of the property
+ * @return the value of the property
+ * @see propertyNames()
+ */
+ virtual TQVariant property( const TQString& _name ) const;
+
+ /**
+ * Retrieves a list of all properties associated with this
+ * KMimeType.
+ * @return a list of all property names
+ * @see property()
+ */
+ virtual TQStringList propertyNames() const;
+
+ /**
+ * Retrieve a pointer to the mime type @p _name or a pointer to the default
+ * mime type "application/octet-stream".
+ *
+ * 0L is @em never returned.
+ *
+ * @em Very @em important: Don't store the result in a KMimeType* !
+ *
+ * @param _name the name of the mime type
+ * @return the pointer to the KMimeType with the given @p _name, or
+ * a pointer to the application/octet-stream KMimeType if
+ * not found
+ * @see KServiceType::serviceType
+ */
+ static Ptr mimeType( const TQString& _name );
+
+ /**
+ * Finds a KMimeType with the given @p _url.
+ * This function looks at mode_t first.
+ * If that does not help it
+ * looks at the extension. This is fine for FTP, FILE, TAR and
+ * friends, but is not for HTTP ( cgi scripts! ). You should use
+ * KRun instead, but this function returns immediately while
+ * KRun is async. If no extension matches, then
+ * the file will be examined if the URL a local file or
+ * "application/octet-stream" is returned otherwise.
+ *
+ * @param _url Is the right most URL with a filesystem protocol. It
+ * is up to you to find out about that if you have a nested
+ * URL. For example
+ * "http://localhost/mist.gz#gzip:/decompress" would have to
+ * pass the "http://..." URL part, while
+ * "file:/tmp/x.tar#tar:/src/test.gz#gzip:/decompress" would
+ * have to pass the "tar:/..." part of the URL, since gzip is
+ * a filter protocol and not a filesystem protocol.
+ * @param _mode the mode of the file (used, for example, to identify
+ * executables)
+ * @param _is_local_file true if the file is local
+ * @param _fast_mode If set to true no disk access is allowed to
+ * find out the mimetype. The result may be suboptimal, but
+ * it is @em fast.
+ * @return A pointer to the matching mimetype. 0L is never returned.
+ * @em Very @em Important: Don't store the result in a KMimeType* !
+ */
+ static Ptr findByURL( const KURL& _url, mode_t _mode = 0,
+ bool _is_local_file = false, bool _fast_mode = false );
+
+ static Ptr findByURL( const KURL& _url, mode_t _mode,
+ bool _is_local_file, bool _fast_mode,
+ bool *accurate);
+ /**
+ * Finds a KMimeType with the given @p _url.
+ * This function looks at mode_t first.
+ * If that does not help it
+ * looks at the extension. This is fine for FTP, FILE, TAR and
+ * friends, but is not for HTTP ( cgi scripts! ). You should use
+ * KRun instead, but this function returns immediately while
+ * KRun is async. If no extension matches, then
+ * the file will be examined if the URL a local file or
+ * "application/octet-stream" is returned otherwise.
+ *
+ * Equivalent to
+ * \code
+ * KURL u;
+ * u.setPath(path);
+ * return findByURL( u, mode, true, fast_mode );
+ * \endcode
+ *
+ * @param path the path to the file
+ * @param mode the mode of the file (used, for example, to identify
+ * executables)
+ * @param fast_mode If set to true no disk access is allowed to
+ * find out the mimetype. The result may be suboptimal, but
+ * it is @em fast.
+ * @return A pointer to the matching mimetype. 0L is never returned.
+ */
+ static Ptr findByPath( const TQString& path, mode_t mode = 0, bool fast_mode = false );
+
+ /**
+ * Tries to find out the MIME type of a data chunk by looking for
+ * certain magic numbers and characteristic strings in it.
+ *
+ * @param data the data to examine
+ * @param accuracy If not a null pointer, *accuracy is set to the
+ * accuracy of the match (which is in the range 0..100)
+ * @return a pointer to the KMimeType. application/octet-stream's KMimeType of the
+ * type can not be found this way.
+ */
+ static Ptr findByContent( const TQByteArray &data, int *accuracy=0 );
+
+ /**
+ * Tries to find out the MIME type of a file by looking for
+ * certain magic numbers and characteristic strings in it.
+ * This function is similar to the previous one. Note that the
+ * file name is not used for determining the file type, it is just
+ * used for loading the file's contents.
+ *
+ * @param fileName the path to the file
+ * @param accuracy If not a null pointer, *accuracy is set to the
+ * accuracy of the match (which is in the range 0..100)
+ * @return a pointer to the KMimeType. application/octet-stream's KMimeType of the
+ * type can not be found this way.
+ */
+ static Ptr findByFileContent( const TQString &fileName, int *accuracy=0 );
+
+ struct Format{
+ bool text : 1;
+ enum { NoCompression=0, GZipCompression } compression : 4;
+ unsigned dummy : 27;
+ };
+
+ /**
+ * Returns whether a file has an internal format that is human readable,
+ * or that would be human readable after decompression.
+ * @since 3.2
+ */
+ static Format findFormatByFileContent( const TQString &fileName );
+
+ /**
+ * Get all the mimetypes.
+ *
+ * Useful for showing the list of
+ * available mimetypes.
+ * More memory consuming than the ones above, don't use unless
+ * really necessary.
+ * @return the list of all existing KMimeTypes
+ */
+ static List allMimeTypes();
+
+ /**
+ * Returns the name of the default mimetype.
+ * Always application/octet-stream, but this method exists
+ * for performance purposes.
+ * @return the name of the default mime type, always
+ * "application/octet-stream"
+ */
+ static const TQString & defaultMimeType();
+
+ /**
+ * Returns the default mimetype.
+ * Always application/octet-stream.
+ * This can be used to check the result of mimeType(name).
+ * @return the "application/octet-stream" mimetype pointer.
+ * @since 3.2
+ */
+ static KMimeType::Ptr defaultMimeTypePtr();
+
+ /**
+ * If this mimetype inherits from ("is also") another mimetype,
+ * return the name of the parent.
+ *
+ * For instance a text/x-log is a special kind of text/plain,
+ * so the definition of text/x-log can say "X-TDE-IsAlso=text/plain".
+ * Or an smb-workgroup is a special kind of inode/directory, etc.
+ * This mechanism can also be used to rename mimetypes and preserve compat.
+ *
+ * Note that this notion doesn't map to the servicetype inheritance mechanism,
+ * since an application that handles the specific type doesn't necessarily handle
+ * the base type. The opposite is true though.
+ *
+ * @return the parent mime type, or TQString::null if not set
+ * @since 3.2
+ */
+ TQString parentMimeType() const;
+
+ /**
+ * Do not use name()=="somename" anymore, to check for a given mimetype.
+ * For mimetype inheritance to work, use is("somename") instead.
+ * Warning, do not use inherits(), that's the servicetype inheritance concept!
+ * @since 3.2
+ */
+ bool is( const TQString& mimeTypeName ) const;
+
+ /**
+ * @internal
+ * Determines the mimetype of file based on it's name and returns the
+ * matching pattern if any.
+ */
+ static KMimeType::Ptr diagnoseFileName(const TQString &file, TQString &pattern);
+
+protected:
+ void loadInternal( TQDataStream& );
+ void init( KDesktopFile * );
+
+ /**
+ * Signal a missing mime type.
+ * @param _type the missinf mime type
+ */
+ static void errorMissingMimeType( const TQString& _type );
+
+ /**
+ * This function makes sure that the default mime type exists.
+ */
+ static void buildDefaultType();
+
+ /**
+ * This function makes sure that vital mime types are installed.
+ */
+ static void checkEssentialMimeTypes();
+ /**
+ * true if check for vital mime types has been done.
+ */
+ static bool s_bChecked;
+
+ TQStringList m_lstPatterns;
+
+ static Ptr s_pDefaultType;
+
+protected:
+ friend class KServiceTypeFactory;
+ int patternsAccuracy() const;
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+};
+
+/**
+ * Folder mime type. Handles locked folders, for instance.
+ * @short Mimetype for a folder (inode/directory)
+ */
+class TDEIO_EXPORT KFolderType : public KMimeType
+{
+ K_SYCOCATYPE( KST_KFolderType, KMimeType )
+
+public:
+// KFolderType( const TQString & _fullpath, const TQString& _type, const TQString& _icon, const TQString& _comment,
+// const TQStringList& _patterns );
+// KFolderType( const TQString & _fullpath ) : KMimeType( _fullpath ) { }
+ /**
+ * Construct a folder mimetype and take all information from a desktop file.
+ * @param config the desktop configuration file that describes the mime type
+ */
+ KFolderType( KDesktopFile *config) : KMimeType( config ) { }
+ /** \internal */
+ KFolderType( TQDataStream& _str, int offset ) : KMimeType( _str, offset ) { }
+
+ virtual TQString icon( const TQString& _url, bool _is_local ) const;
+ virtual TQString icon( const KURL& _url, bool _is_local ) const;
+ virtual TQString comment( const TQString& _url, bool _is_local ) const;
+ virtual TQString comment( const KURL& _url, bool _is_local ) const;
+protected:
+ virtual void virtual_hook( int id, void* data );
+};
+
+/**
+ * Mime type for desktop files.
+ * Handles mount/umount icon, and user-defined properties.
+ * @short Mimetype for a .desktop file
+ */
+class TDEIO_EXPORT KDEDesktopMimeType : public KMimeType
+{
+ K_SYCOCATYPE( KST_KDEDesktopMimeType, KMimeType )
+
+public:
+ enum ServiceType { ST_MOUNT, ST_UNMOUNT, /* ST_PROPERTIES, */ ST_USER_DEFINED };
+
+ /**
+ * Structure representing a service, in the list of services
+ * returned by builtinServices and userDefinedServices
+ */
+ struct Service
+ {
+ Service() { m_display = true; }
+ bool isEmpty() const { return m_strName.isEmpty(); }
+ TQString m_strName;
+ TQString m_strIcon;
+ TQString m_strExec;
+ ServiceType m_type;
+ bool m_display;
+ };
+ // KDEDesktopMimeType( const TQString & _fullpath, const TQString& _type, const TQString& _icon,
+ // const TQString& _comment, const TQStringList& _patterns );
+ // KDEDesktopMimeType( const TQString & _fullpath ) : KMimeType( _fullpath ) { }
+ /**
+ * Construct a desktop mimetype and take all information from a desktop file.
+ * @param config the desktop configuration file that describes the mime type
+ */
+ KDEDesktopMimeType( KDesktopFile *config) : KMimeType( config ) { }
+ /** \internal */
+ KDEDesktopMimeType( TQDataStream& _str, int offset ) : KMimeType( _str, offset ) { }
+
+ virtual TQString icon( const TQString& _url, bool _is_local ) const;
+ virtual TQString icon( const KURL& _url, bool _is_local ) const;
+ virtual TQPixmap pixmap( const KURL& _url, KIcon::Group _group, int _force_size = 0,
+ int _state = 0, TQString * _path = 0L ) const;
+ virtual TQString comment( const TQString& _url, bool _is_local ) const;
+ virtual TQString comment( const KURL& _url, bool _is_local ) const;
+
+ /**
+ * Returns a list of services for the given .desktop file that are handled
+ * by kio itself. Namely mount/unmount for FSDevice files.
+ * @return the list of services
+ */
+ static TQValueList<Service> builtinServices( const KURL& _url );
+ /**
+ * Returns a list of services defined by the user as possible actions
+ * on the given .desktop file. May include empty actions which represent where
+ * visual separators should appear in user-visible representations of those actions,
+ * such as separators in a menu.
+ * @param path the path to the desktop file describing the services
+ * @param bLocalFiles true if those services are to be applied to local files only
+ * (if false, services that don't have %u or %U in the Exec line won't be taken into account).
+ * @return the list of user deviced actions
+ */
+ static TQValueList<Service> userDefinedServices( const TQString& path, bool bLocalFiles );
+
+ /**
+ * Overload of userDefinedServices for speed purposes: it takes a TDEConfig* so that
+ * the caller can check things in the file without having it parsed twice.
+ * @since 3.4
+ */
+ static TQValueList<Service> userDefinedServices( const TQString& path, TDEConfig& config, bool bLocalFiles );
+
+ /**
+ * Overload of userDefinedServices but also allows you to pass a list of urls for this file.
+ * This allows for the menu to be changed depending on the exact files via
+ * the X-TDE-GetActionMenu extension.
+ * @since 3.5
+ */
+ static TQValueList<Service> userDefinedServices( const TQString& path, TDEConfig& config, bool bLocalFiles, const KURL::List & file_list);
+
+ /**
+ * @param path is the path of the desktop entry.
+ * @param service the service to execute
+ * @deprecated, see the other executeService
+ */
+ static void executeService( const TQString& path, KDEDesktopMimeType::Service& service ) KDE_DEPRECATED;
+
+ /**
+ * Execute @p service on the list of @p urls.
+ * @param urls the list of urls
+ * @param service the service to execute
+ */
+ static void executeService( const KURL::List& urls, KDEDesktopMimeType::Service& service );
+
+ /**
+ * Invokes the default action for the desktop entry. If the desktop
+ * entry is not local, then only false is returned. Otherwise we
+ * would create a security problem. Only types Link and Mimetype
+ * could be followed.
+ *
+ * @param _url the url to run
+ * @param _is_local true if the URL is local, false otherwise
+ * @return true on success and false on failure.
+ * @see KRun::runURL
+ */
+ static pid_t run( const KURL& _url, bool _is_local );
+
+protected:
+ virtual TQPixmap pixmap( KIcon::Group group, int force_size = 0, int state = 0,
+ TQString * path = 0L ) const
+ { return KMimeType::pixmap( group, force_size, state, path ); }
+
+ static pid_t runFSDevice( const KURL& _url, const KSimpleConfig &cfg );
+ static pid_t runApplication( const KURL& _url, const TQString & _serviceFile );
+ static pid_t runLink( const KURL& _url, const KSimpleConfig &cfg );
+ static pid_t runMimeType( const KURL& _url, const KSimpleConfig &cfg );
+protected:
+ virtual void virtual_hook( int id, void* data );
+};
+
+/**
+ * The mime type for executable files.
+ * @short MimeType for any executable, like /bin/ls
+ */
+class TDEIO_EXPORT KExecMimeType : public KMimeType
+{
+ K_SYCOCATYPE( KST_KExecMimeType, KMimeType )
+
+public:
+ // KExecMimeType( const TQString & _fullpath, const TQString& _type, const TQString& _icon,
+ // const TQString& _comment, const TQStringList& _patterns );
+ // KExecMimeType( const TQString & _fullpath ) : KMimeType( _fullpath ) { }
+ /**
+ * Construct a executable mimetype and take all information from a desktop file.
+ * @param config the desktop configuration file that describes the mime type
+ */
+ KExecMimeType( KDesktopFile *config) : KMimeType( config ) { }
+ /** \internal */
+ KExecMimeType( TQDataStream& _str, int offset ) : KMimeType( _str, offset ) { }
+protected:
+ virtual void virtual_hook( int id, void* data );
+};
+
+#endif
diff --git a/tdeio/tdeio/kmimetypechooser.cpp b/tdeio/tdeio/kmimetypechooser.cpp
new file mode 100644
index 000000000..51db75fe1
--- /dev/null
+++ b/tdeio/tdeio/kmimetypechooser.cpp
@@ -0,0 +1,298 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001 - 2004 Anders Lund <anders@alweb.dk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kmimetypechooser.h"
+
+#include <tdeconfig.h>
+#include <kiconloader.h>
+#include <klistview.h>
+#include <klocale.h>
+#include <kmimetype.h>
+#include <kprocess.h>
+#include <krun.h>
+#include <tdesycoca.h>
+
+#include <tqlabel.h>
+#include <tqlayout.h>
+#include <tqlineedit.h>
+#include <tqpushbutton.h>
+#include <tqwhatsthis.h>
+
+//BEGIN KMimeTypeChooserPrivate
+class KMimeTypeChooserPrivate
+{
+ public:
+ KListView *lvMimeTypes;
+ TQPushButton *btnEditMimeType;
+
+ TQString defaultgroup;
+ TQStringList groups;
+ int visuals;
+};
+//END
+
+//BEGIN KMimeTypeChooser
+KMimeTypeChooser::KMimeTypeChooser( const TQString &text,
+ const TQStringList &selMimeTypes,
+ const TQString &defaultGroup,
+ const TQStringList &groupsToShow,
+ int visuals,
+ TQWidget *parent,
+ const char *name )
+ : TQVBox( parent, name )
+{
+ d = new KMimeTypeChooserPrivate();
+ d->lvMimeTypes = 0;
+ d->btnEditMimeType = 0;
+ d->defaultgroup = defaultGroup;
+ d->groups = groupsToShow;
+ d->visuals = visuals;
+
+ setSpacing( KDialogBase::spacingHint() );
+
+ if ( !text.isEmpty() )
+ {
+ new TQLabel( text, this );
+ }
+
+ d->lvMimeTypes = new KListView( this );
+
+ d->lvMimeTypes->addColumn( i18n("Mime Type") );
+// d->lvMimeTypes->setColumnWidthMode( 0, TQListView::Manual );
+
+ if ( visuals & Comments )
+ {
+ d->lvMimeTypes->addColumn( i18n("Comment") );
+ d->lvMimeTypes->setColumnWidthMode( 1, TQListView::Manual );
+ }
+ if ( visuals & Patterns )
+ d->lvMimeTypes->addColumn( i18n("Patterns") );
+
+ d->lvMimeTypes->setRootIsDecorated( true );
+
+ loadMimeTypes( selMimeTypes );
+
+ if (visuals & KMimeTypeChooser::EditButton)
+ {
+ TQHBox *btns = new TQHBox( this );
+ ((TQBoxLayout*)btns->layout())->addStretch(1);
+ d->btnEditMimeType = new TQPushButton( i18n("&Edit..."), btns );
+
+ connect( d->btnEditMimeType, TQT_SIGNAL(clicked()), this, TQT_SLOT(editMimeType()) );
+ d->btnEditMimeType->setEnabled( false );
+ connect( d->lvMimeTypes, TQT_SIGNAL( doubleClicked ( TQListViewItem * )),
+ this, TQT_SLOT( editMimeType()));
+ connect( d->lvMimeTypes, TQT_SIGNAL(currentChanged(TQListViewItem*)),
+ this, TQT_SLOT(slotCurrentChanged(TQListViewItem*)) );
+
+ TQWhatsThis::add( d->btnEditMimeType, i18n(
+ "Click this button to display the familiar TDE mime type editor.") );
+ }
+}
+
+KMimeTypeChooser::~KMimeTypeChooser()
+{
+ delete d;
+}
+
+void KMimeTypeChooser::loadMimeTypes( const TQStringList &_selectedMimeTypes )
+{
+ TQStringList selMimeTypes;
+
+ if ( !_selectedMimeTypes.isEmpty() )
+ selMimeTypes = _selectedMimeTypes;
+ else
+ selMimeTypes = mimeTypes();
+
+ d->lvMimeTypes->clear();
+
+ TQMap<TQString,TQListViewItem*> groups;
+ // thanks to tdebase/kcontrol/filetypes/filetypesview
+ KMimeType::List mimetypes = KMimeType::allMimeTypes();
+ TQValueListIterator<KMimeType::Ptr> it(mimetypes.begin());
+
+ TQListViewItem *groupItem;
+ bool agroupisopen = false;
+ TQListViewItem *idefault = 0; //open this, if all other fails
+ TQListViewItem *firstChecked = 0; // make this one visible after the loop
+
+ for (; it != mimetypes.end(); ++it)
+ {
+ TQString mimetype = (*it)->name();
+ int index = mimetype.find("/");
+ TQString maj = mimetype.left(index);
+
+ if ( d->groups.count() && !d->groups.contains( maj ) )
+ continue;
+
+ TQString min = mimetype.right(mimetype.length() - (index+1));
+
+ TQMapIterator<TQString,TQListViewItem*> mit = groups.find( maj );
+ if ( mit == groups.end() )
+ {
+ groupItem = new TQListViewItem( d->lvMimeTypes, maj );
+ groups.insert( maj, groupItem );
+ if ( maj == d->defaultgroup )
+ idefault = groupItem;
+ }
+ else
+ groupItem = mit.data();
+
+ TQCheckListItem *item = new TQCheckListItem( groupItem, min, TQCheckListItem::CheckBox );
+ item->setPixmap( 0, SmallIcon( (*it)->icon(TQString::null,false) ) );
+
+ int cl = 1;
+
+ if ( d->visuals & Comments )
+ {
+ item->setText( cl, (*it)->comment(TQString::null, false) );
+ cl++;
+ }
+
+ if ( d->visuals & Patterns )
+ item->setText( cl, (*it)->patterns().join("; ") );
+
+ if ( selMimeTypes.contains(mimetype) )
+ {
+ item->setOn( true );
+ groupItem->setOpen( true );
+ agroupisopen = true;
+ if ( !firstChecked )
+ firstChecked = item;
+ }
+ }
+
+ if ( firstChecked )
+ d->lvMimeTypes->ensureItemVisible( firstChecked );
+
+ if ( !agroupisopen && idefault )
+ {
+ idefault->setOpen( true );
+ d->lvMimeTypes->ensureItemVisible( idefault );
+ }
+}
+
+void KMimeTypeChooser::editMimeType()
+{
+ if ( !(d->lvMimeTypes->currentItem() && (d->lvMimeTypes->currentItem())->parent()) )
+ return;
+ TQString mt = (d->lvMimeTypes->currentItem()->parent())->text( 0 ) + "/" + (d->lvMimeTypes->currentItem())->text( 0 );
+ // thanks to libkonq/konq_operations.cc
+ connect( KSycoca::self(), TQT_SIGNAL(databaseChanged()),
+ this, TQT_SLOT(slotSycocaDatabaseChanged()) );
+ TQString keditfiletype = TQString::fromLatin1("keditfiletype");
+ KRun::runCommand( keditfiletype
+ + " --parent " + TQString::number( (ulong)topLevelWidget()->winId())
+ + " " + TDEProcess::quote(mt),
+ keditfiletype, keditfiletype /*unused*/);
+}
+
+void KMimeTypeChooser::slotCurrentChanged(TQListViewItem* i)
+{
+ if ( d->btnEditMimeType )
+ d->btnEditMimeType->setEnabled( i->parent() );
+}
+
+void KMimeTypeChooser::slotSycocaDatabaseChanged()
+{
+ if ( KSycoca::self()->isChanged("mime") )
+ loadMimeTypes();
+}
+
+TQStringList KMimeTypeChooser::mimeTypes() const
+{
+ TQStringList l;
+ TQListViewItemIterator it( d->lvMimeTypes );
+ for (; it.current(); ++it)
+ {
+ if ( it.current()->parent() && ((TQCheckListItem*)it.current())->isOn() )
+ l << it.current()->parent()->text(0) + "/" + it.current()->text(0); // FIXME uncecked, should be Ok unless someone changes mimetypes during this!
+ }
+ return l;
+}
+
+TQStringList KMimeTypeChooser::patterns() const
+{
+ TQStringList l;
+ KMimeType::Ptr p;
+ TQString defMT = KMimeType::defaultMimeType();
+ TQListViewItemIterator it( d->lvMimeTypes );
+ for (; it.current(); ++it)
+ {
+ if ( it.current()->parent() && ((TQCheckListItem*)it.current())->isOn() )
+ {
+ p = KMimeType::mimeType( it.current()->parent()->text(0) + "/" + it.current()->text(0) );
+ if ( p->name() != defMT )
+ l += p->patterns();
+ }
+ }
+ return l;
+}
+//END
+
+//BEGIN KMimeTypeChooserDialog
+KMimeTypeChooserDialog::KMimeTypeChooserDialog(
+ const TQString &caption,
+ const TQString& text,
+ const TQStringList &selMimeTypes,
+ const TQString &defaultGroup,
+ const TQStringList &groupsToShow,
+ int visuals,
+ TQWidget *parent, const char *name )
+ : KDialogBase(parent, name, true, caption, Cancel|Ok, Ok)
+{
+ m_chooser = new KMimeTypeChooser( text, selMimeTypes,
+ defaultGroup, groupsToShow, visuals,
+ this, "chooser" );
+ setMainWidget(m_chooser);
+
+ TDEConfigGroup group( TDEGlobal::config(), "KMimeTypeChooserDialog");
+ TQSize defaultSize( 400, 300 );
+ resize( group.readSizeEntry("size", &defaultSize) );
+}
+
+KMimeTypeChooserDialog::KMimeTypeChooserDialog(
+ const TQString &caption,
+ const TQString& text,
+ const TQStringList &selMimeTypes,
+ const TQString &defaultGroup,
+ TQWidget *parent, const char *name )
+ : KDialogBase(parent, name, true, caption, Cancel|Ok, Ok)
+{
+ m_chooser = new KMimeTypeChooser( text, selMimeTypes,
+ defaultGroup, TQStringList(),
+ KMimeTypeChooser::Comments|KMimeTypeChooser::Patterns|KMimeTypeChooser::EditButton,
+ this, "chooser" );
+ setMainWidget(m_chooser);
+
+ TDEConfigGroup group( TDEGlobal::config(), "KMimeTypeChooserDialog");
+ TQSize defaultSize( 400, 300 );
+ resize( group.readSizeEntry("size", &defaultSize) );
+}
+
+
+KMimeTypeChooserDialog::~KMimeTypeChooserDialog()
+{
+ TDEConfigGroup group( TDEGlobal::config(), "KMimeTypeChooserDialog");
+ group.writeEntry("size", size());
+}
+
+//END KMimeTypeChooserDialog
+
+// kate: space-indent on; indent-width 2; replace-tabs on;
+#include "kmimetypechooser.moc"
diff --git a/tdeio/tdeio/kmimetypechooser.h b/tdeio/tdeio/kmimetypechooser.h
new file mode 100644
index 000000000..16ec052f5
--- /dev/null
+++ b/tdeio/tdeio/kmimetypechooser.h
@@ -0,0 +1,180 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001 - 2004 Anders Lund <anders@alweb.dk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _KMIMETYPE_CHOOSER_H_
+#define _KMIMETYPE_CHOOSER_H_
+
+#include <tqvbox.h>
+#include <kdialogbase.h>
+
+
+/**
+ * This widget provides a checkable list of all available mimetypes,
+ * and a list of selected ones, as well as a corresponding list of file
+ * extensions, an optional text and an optional edit button (not working yet).
+ * Mime types is presented in a list view, with name, comment and patterns columns.
+ *
+ * @author Anders Lund (anders at alweb dk), jan 23, 2002
+ */
+class TDEIO_EXPORT KMimeTypeChooser : public TQVBox
+{
+ Q_OBJECT
+
+ public:
+ /**
+ * Buttons and data for display.
+ */
+ enum Visuals {
+ Comments=1, ///< Show the Mimetypes Comment field in a column ("HTML Document").
+ Patterns=2, ///< Show the Mimetypes Patterns field in a column ("*.html;*.htm").
+ EditButton=4 ///< Show the "Edit" button, allowing to edit the selected type.
+ };
+ /**
+ * Create a new KMimeTypeChooser.
+ *
+ * @param text A Text to display above the list
+ * @param selectedMimeTypes A list of mimetype names, theese will be checked
+ * in the list if they exist.
+ * @param visuals A OR'd Visuals enum to decide which data and buttons to display.
+ * @param defaultGroup The group to open when no groups are selected (like
+ * "text"). If not provided, no group is opened. If @p groupsToShow
+ * is provided and defaultGroup is not a member of that, it is ignored.
+ * @param groupsToShow a list of mimetype groups to show. If empty, all
+ * groups are shown.
+ * @param parent The parent widget to use
+ * @param name The internal name of this object
+ */
+ KMimeTypeChooser( const TQString& text=TQString::null,
+ const TQStringList &selectedMimeTypes=0,
+ const TQString &defaultGroup=TQString::null,
+ const TQStringList &groupsToShow=TQStringList(),
+ int visuals=Comments|Patterns|EditButton,
+ TQWidget *parent=0, const char *name=0 );
+ ~KMimeTypeChooser();
+
+ /**
+ * @return a list of all selected selected mimetypes represented by their name.
+ */
+ TQStringList mimeTypes() const;
+ /**
+ * @return a list of the fileame patterns associated with all selected mimetypes.
+ */
+ TQStringList patterns() const;
+
+ public slots:
+ /**
+ * @short edit the current mimetype
+ * Uses KRun to start the KDE mimetype editor for editing the currently
+ * selected mimetype.
+ */
+ void editMimeType();
+
+ private slots:
+ /**
+ * @internal disables the "edit" button for groups
+ */
+ void slotCurrentChanged(TQListViewItem* i);
+
+ /**
+ * @internal called when the sycoca database has changed after
+ * the user edited a mimetype
+ */
+ void slotSycocaDatabaseChanged();
+
+ private:
+ /**
+ * @internal Load mime types into the list view
+ * If @p selected is empty, selectedMimeTypesStringList() is called
+ * to fill it in.
+ */
+ void loadMimeTypes( const TQStringList &selected=TQStringList() );
+
+ class KMimeTypeChooserPrivate *d;
+};
+
+/**
+ * @short A Dialog to choose some mimetypes.
+ * Provides a checkable tree list of mimetypes, with icons and optinally
+ * comments and patterns, and an (optional) button to display the KDE mimetype
+ * editor.
+ *
+ * Here is an example, using the dialog to set the text of two lineedits:
+ *
+ * @code
+ * TQString text = i18n("Select the MimeTypes you want for this file type.");
+ * TQStringList list = TQStringList::split( TQRegExp("\\s*;\\s*"), leMimetypes->text() );
+ * KMimeTypeChooserDialog *d = new KMimeTypeChooserDialog( this, 0,
+ * i18n("Select Mime Types"), text, list, "text" );
+ * if ( d->exec() == KDialogBase::Accepted ) {
+ * leWildcards->setText( d->chooser()->patterns().join(";") );
+ * leMimetypes->setText( d->chooser()->mimeTypes().join(";") );
+ * }
+ * @endcode
+ *
+ * @author Anders Lund (anders at alweb dk) dec 19, 2001
+ */
+class TDEIO_EXPORT KMimeTypeChooserDialog : public KDialogBase
+{
+ public:
+ /**
+ * Create a KMimeTypeChooser dialog.
+ *
+ * @param caption The title of the dialog
+ * @param text A Text to display above the list
+ * @param selectedMimeTypes A list of mimetype names, theese will be
+ * checked in the list if they exist.
+ * patterns will be added to the list view.
+ * @param visuals A OR'd KMimetypeChooser::Visuals enum to decide which data
+ * and buttons to display.
+ * @param defaultGroup The group to open when no groups are selected (like
+ * "text"). If not provided, no group is opened. If @p groupsToShow
+ * is provided and defaultGroup is not a member of that, it is ignored.
+ * @param groupsToShow a list of mimetype groups to show. If empty, all
+ * groups are shown.
+ * @param parent The parent widget to use
+ * @param name The internal name of this object
+ */
+ KMimeTypeChooserDialog( const TQString &caption=TQString::null,
+ const TQString& text=TQString::null,
+ const TQStringList &selectedMimeTypes=TQStringList(),
+ const TQString &defaultGroup=TQString::null,
+ const TQStringList &groupsToShow=TQStringList(),
+ int visuals=KMimeTypeChooser::Comments|KMimeTypeChooser::Patterns|KMimeTypeChooser::EditButton,
+ TQWidget *parent=0, const char *name=0 );
+
+ /**
+ * @overload
+ */
+ KMimeTypeChooserDialog( const TQString &caption,
+ const TQString& text,
+ const TQStringList &selectedMimeTypes,
+ const TQString &defaultGroup,
+ TQWidget *parent=0, const char *name=0 );
+
+ ~KMimeTypeChooserDialog();
+
+ /**
+ * @return a pointer to the KMimeTypeChooser widget
+ */
+ KMimeTypeChooser* chooser() { return m_chooser; }
+
+ private:
+ KMimeTypeChooser *m_chooser;
+};
+#endif // _KMIMETYPE_CHOOSER_H_
+// kate: space-indent on; indent-width 2; replace-tabs on;
diff --git a/tdeio/tdeio/kmimetyperesolver.h b/tdeio/tdeio/kmimetyperesolver.h
new file mode 100644
index 000000000..c8828a0da
--- /dev/null
+++ b/tdeio/tdeio/kmimetyperesolver.h
@@ -0,0 +1,255 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+ Copyright (C) 2000 Rik Hemsley <rik@kde.org>
+ Copyright (C) 2002 Carsten Pfeiffer <pfeiffer@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kmimetyperesolver_h
+#define __kmimetyperesolver_h
+
+#include <tqscrollview.h>
+#include <tqptrlist.h>
+#include <tqtimer.h>
+#include <kdebug.h>
+
+/**
+ * @internal
+ * A baseclass for KMimeTypeResolver, with the interface,
+ * KMimeTypeResolverHelper uses.
+ */
+class TDEIO_EXPORT KMimeTypeResolverBase
+{
+public:
+ virtual void slotViewportAdjusted() = 0;
+ virtual void slotProcessMimeIcons() = 0;
+protected:
+ virtual void virtual_hook( int, void* ) {}
+};
+
+/**
+ * @internal
+ * This class is used by KMimeTypeResolver, because it can't be a QObject
+ * itself. So an object of this class is used to handle signals, slots etc.
+ * and forwards them to the KMimeTypeResolver instance.
+ */
+class TDEIO_EXPORT KMimeTypeResolverHelper : public TQObject
+{
+ Q_OBJECT
+
+public:
+ KMimeTypeResolverHelper( KMimeTypeResolverBase *resolver,
+ TQScrollView *view )
+ : m_resolver( resolver ),
+ m_timer( new TQTimer( this ) )
+ {
+ connect( m_timer, TQT_SIGNAL( timeout() ), TQT_SLOT( slotProcessMimeIcons() ));
+
+ connect( view->horizontalScrollBar(), TQT_SIGNAL( sliderMoved(int) ),
+ TQT_SLOT( slotAdjust() ) );
+ connect( view->verticalScrollBar(), TQT_SIGNAL( sliderMoved(int) ),
+ TQT_SLOT( slotAdjust() ) );
+
+ view->viewport()->installEventFilter( this );
+ }
+
+ void start( int delay, bool singleShot )
+ {
+ m_timer->start( delay, singleShot );
+ }
+
+protected:
+ virtual bool eventFilter( TQObject *o, TQEvent *e )
+ {
+ bool ret = TQObject::eventFilter( o, e );
+
+ if ( e->type() == TQEvent::Resize )
+ m_resolver->slotViewportAdjusted();
+
+ return ret;
+ }
+
+private slots:
+ void slotProcessMimeIcons()
+ {
+ m_resolver->slotProcessMimeIcons();
+ }
+
+ void slotAdjust()
+ {
+ m_resolver->slotViewportAdjusted();
+ }
+
+private:
+ KMimeTypeResolverBase *m_resolver;
+ TQTimer *m_timer;
+};
+
+/**
+ * This class implements the "delayed-mimetype-determination" feature,
+ * for konqueror's directory views (and KFileDialog's :).
+ *
+ * It determines the mimetypes of the icons in the background, but giving
+ * preferrence to the visible icons.
+ *
+ * It is implemented as a template, so that it can work with both QPtrListViewItem
+ * and TQIconViewItem, without requiring hacks such as void * or TQPtrDict lookups.
+ *
+ * Here's what the parent must implement :
+ * @li void mimeTypeDeterminationFinished();
+ * @li TQScrollView * scrollWidget();
+ * @li void determineIcon( IconItem * item ), which should call
+ * @li KFileItem::determineMimeType on the fileItem, and update the icon, etc.
+*/
+template<class IconItem, class Parent>
+class KMimeTypeResolver : public KMimeTypeResolverBase // if only this could be a TQObject....
+{
+public:
+ /**
+ * Creates a new KMimeTypeResolver with the given parent.
+ * @param parent the parent's resolver
+ */
+ KMimeTypeResolver( Parent * parent )
+ : m_parent(parent),
+ m_helper( new KMimeTypeResolverHelper(this, parent->scrollWidget())),
+ m_delayNonVisibleIcons(10)
+ {}
+
+ virtual ~KMimeTypeResolver() {
+ delete m_helper;
+ }
+
+ /**
+ * Start the mimetype-determination. Call this when the listing is completed.
+ * @param delayNonVisibleIcons the delay to use between icons not on screen.
+ * Usually 10, but should be set to 0 when the image preview feature is
+ * activated, because image preview can only start once we know the mimetypes
+ */
+ void start( uint delayNonVisibleIcons = 10 )
+ {
+ m_helper->start( 0, true /* single shot */ );
+ m_delayNonVisibleIcons = delayNonVisibleIcons;
+ }
+
+ /**
+ * The list of items to process. The view is free to
+ * clear it, insert new items into it, remove items, etc.
+ * @return the list of items to process
+ */
+ TQPtrList<IconItem> m_lstPendingMimeIconItems;
+
+ /**
+ * "Connected" to the viewportAdjusted signal of the scrollview
+ */
+ virtual void slotViewportAdjusted();
+
+ /**
+ * "Connected" to the timer
+ */
+ virtual void slotProcessMimeIcons();
+
+private:
+ /**
+ * Find a visible icon and determine its mimetype.
+ * KonqDirPart will call this method repeatedly until it returns 0L
+ * (no more visible icon to process).
+ * @return the file item that was just processed.
+ */
+ IconItem * findVisibleIcon();
+
+ Parent * m_parent;
+ KMimeTypeResolverHelper *m_helper;
+ uint m_delayNonVisibleIcons;
+};
+
+// The main slot
+template<class IconItem, class Parent>
+inline void KMimeTypeResolver<IconItem, Parent>::slotProcessMimeIcons()
+{
+ //kdDebug(1203) << "KMimeTypeResolver::slotProcessMimeIcons() "
+ // << m_lstPendingMimeIconItems.count() << endl;
+ IconItem * item = 0L;
+ int nextDelay = 0;
+
+ if ( m_lstPendingMimeIconItems.count() > 0 )
+ {
+ // We only find mimetypes for icons that are visible. When more
+ // of our viewport is exposed, we'll get a signal and then get
+ // the mimetypes for the newly visible icons. (Rikkus)
+ item = findVisibleIcon();
+ }
+
+ // No more visible items.
+ if (0 == item)
+ {
+ // Do the unvisible ones, then, but with a bigger delay, if so configured
+ if ( m_lstPendingMimeIconItems.count() > 0 )
+ {
+ item = m_lstPendingMimeIconItems.first();
+ nextDelay = m_delayNonVisibleIcons;
+ }
+ else
+ {
+ m_parent->mimeTypeDeterminationFinished();
+ return;
+ }
+ }
+
+ m_parent->determineIcon(item);
+ m_lstPendingMimeIconItems.remove(item);
+ m_helper->start( nextDelay, true /* single shot */ );
+}
+
+template<class IconItem, class Parent>
+inline void KMimeTypeResolver<IconItem, Parent>::slotViewportAdjusted()
+{
+ if (m_lstPendingMimeIconItems.isEmpty()) return;
+ IconItem * item = findVisibleIcon();
+ if (item)
+ {
+ m_parent->determineIcon( item );
+ m_lstPendingMimeIconItems.remove(item);
+ m_helper->start( 0, true /* single shot */ );
+ }
+}
+
+template<class IconItem, class Parent>
+inline IconItem * KMimeTypeResolver<IconItem, Parent>::findVisibleIcon()
+{
+ // Find an icon that's visible and whose mimetype we don't know.
+
+ TQPtrListIterator<IconItem> it(m_lstPendingMimeIconItems);
+ if ( m_lstPendingMimeIconItems.count()<20) // for few items, it's faster to not bother
+ return m_lstPendingMimeIconItems.first();
+
+ TQScrollView * view = m_parent->scrollWidget();
+ TQRect visibleContentsRect
+ (
+ view->viewportToContents(TQPoint(0, 0)),
+ view->viewportToContents
+ (
+ TQPoint(view->visibleWidth(), view->visibleHeight())
+ )
+ );
+
+ for (; it.current(); ++it)
+ if (visibleContentsRect.intersects(it.current()->rect()))
+ return it.current();
+
+ return 0L;
+}
+
+#endif
diff --git a/tdeio/tdeio/knfsshare.cpp b/tdeio/tdeio/knfsshare.cpp
new file mode 100644
index 000000000..b4a3d903a
--- /dev/null
+++ b/tdeio/tdeio/knfsshare.cpp
@@ -0,0 +1,219 @@
+/* This file is part of the KDE project
+ Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <tqdict.h>
+#include <tqfile.h>
+#include <tqtextstream.h>
+
+#include <kdirwatch.h>
+#include <kstaticdeleter.h>
+#include <kdebug.h>
+#include <tdeconfig.h>
+
+#include "knfsshare.h"
+
+class KNFSSharePrivate
+{
+public:
+ KNFSSharePrivate();
+
+ bool readExportsFile();
+ bool findExportsFile();
+
+ TQDict<bool> sharedPaths;
+ TQString exportsFile;
+};
+
+KNFSSharePrivate::KNFSSharePrivate()
+{
+ if (findExportsFile())
+ readExportsFile();
+}
+
+/**
+ * Try to find the nfs config file path
+ * First tries the tdeconfig, then checks
+ * several well-known paths
+ * @return wether an 'exports' file was found.
+ **/
+bool KNFSSharePrivate::findExportsFile() {
+ TDEConfig config("knfsshare");
+ config.setGroup("General");
+ exportsFile = config.readPathEntry("exportsFile");
+
+ if ( TQFile::exists(exportsFile) )
+ return true;
+
+ if ( TQFile::exists("/etc/exports") )
+ exportsFile = "/etc/exports";
+ else {
+ kdDebug(7000) << "KNFSShare: Could not found exports file!" << endl;
+ return false;
+ }
+
+ config.writeEntry("exportsFile",exportsFile);
+ return true;
+}
+
+/**
+ * Reads all paths from the exports file
+ * and fills the sharedPaths dict with the values
+ */
+bool KNFSSharePrivate::readExportsFile() {
+ TQFile f(exportsFile);
+
+ kdDebug(7000) << "KNFSShare::readExportsFile " << exportsFile << endl;
+
+ if (!f.open(IO_ReadOnly)) {
+ kdError() << "KNFSShare: Could not open " << exportsFile << endl;
+ return false;
+ }
+
+
+ sharedPaths.clear();
+
+ TQTextStream s( &f );
+
+ bool continuedLine = false; // is true if the line before ended with a backslash
+ TQString completeLine;
+
+ while ( !s.eof() )
+ {
+ TQString currentLine = s.readLine().stripWhiteSpace();
+
+ if (continuedLine) {
+ completeLine += currentLine;
+ continuedLine = false;
+ }
+ else
+ completeLine = currentLine;
+
+ // is the line continued in the next line ?
+ if ( completeLine[completeLine.length()-1] == '\\' )
+ {
+ continuedLine = true;
+ // remove the ending backslash
+ completeLine.truncate( completeLine.length()-1 );
+ continue;
+ }
+
+ // comments or empty lines
+ if (completeLine.isEmpty() ||
+ '#' == completeLine[0])
+ {
+ continue;
+ }
+
+ TQString path;
+
+ // Handle quotation marks
+ if ( completeLine[0] == '"' ) {
+ int i = completeLine.find('"',1);
+ if (i == -1) {
+ kdError() << "KNFSShare: Parse error: Missing quotation mark: " << completeLine << endl;
+ continue;
+ }
+ path = completeLine.mid(1,i-1);
+
+ } else { // no quotation marks
+ int i = completeLine.find(' ');
+ if (i == -1)
+ i = completeLine.find('\t');
+
+ if (i == -1)
+ path = completeLine;
+ else
+ path = completeLine.left(i);
+
+ }
+
+ kdDebug(7000) << "KNFSShare: Found path: " << path << endl;
+
+ // normalize path
+ if ( path[path.length()-1] != '/' )
+ path += '/';
+
+ bool b = true;
+ sharedPaths.insert(path,&b);
+ }
+
+ f.close();
+
+ return true;
+
+}
+
+KNFSShare::KNFSShare() {
+ d = new KNFSSharePrivate();
+ if (TQFile::exists(d->exportsFile)) {
+ KDirWatch::self()->addFile(d->exportsFile);
+ connect(KDirWatch::self(), TQT_SIGNAL(dirty (const TQString&)),this,
+ TQT_SLOT(slotFileChange(const TQString&)));
+ }
+}
+
+KNFSShare::~KNFSShare() {
+ if (TQFile::exists(d->exportsFile)) {
+ KDirWatch::self()->removeFile(d->exportsFile);
+ }
+ delete d;
+}
+
+
+bool KNFSShare::isDirectoryShared( const TQString & path ) const {
+ TQString fixedPath = path;
+ if ( path[path.length()-1] != '/' )
+ fixedPath += '/';
+
+ return d->sharedPaths.find(fixedPath) != 0;
+}
+
+TQStringList KNFSShare::sharedDirectories() const {
+ TQStringList result;
+ TQDictIterator<bool> it(d->sharedPaths);
+ for( ; it.current(); ++it )
+ result << it.currentKey();
+
+ return result;
+}
+
+TQString KNFSShare::exportsPath() const {
+ return d->exportsFile;
+}
+
+
+
+void KNFSShare::slotFileChange( const TQString & path ) {
+ if (path == d->exportsFile)
+ d->readExportsFile();
+
+ emit changed();
+}
+
+KNFSShare* KNFSShare::_instance = 0L;
+static KStaticDeleter<KNFSShare> ksdNFSShare;
+
+KNFSShare* KNFSShare::instance() {
+ if (! _instance )
+ _instance = ksdNFSShare.setObject(_instance, new KNFSShare());
+
+ return _instance;
+}
+
+#include "knfsshare.moc"
+
diff --git a/tdeio/tdeio/knfsshare.h b/tdeio/tdeio/knfsshare.h
new file mode 100644
index 000000000..64cd28dcf
--- /dev/null
+++ b/tdeio/tdeio/knfsshare.h
@@ -0,0 +1,86 @@
+/* This file is part of the KDE project
+ Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef knfsshare_h
+#define knfsshare_h
+
+#include <tqobject.h>
+
+#include <tdelibs_export.h>
+
+class KNFSSharePrivate;
+
+/**
+ * Similar functionality like KFileShare,
+ * but works only for NFS and do not need
+ * any suid script.
+ * It parses the /etc/exports file to get its information.
+ * Singleton class, call instance() to get an instance.
+ */
+class TDEIO_EXPORT KNFSShare : public TQObject
+{
+Q_OBJECT
+public:
+ /**
+ * Returns the one and only instance of KNFSShare
+ */
+ static KNFSShare* instance();
+
+ /**
+ * Wether or not the given path is shared by NFS.
+ * @param path the path to check if it is shared by NFS.
+ * @return wether the given path is shared by NFS.
+ */
+ bool isDirectoryShared( const TQString & path ) const;
+
+ /**
+ * Returns a list of all directories shared by NFS.
+ * The resulting list is not sorted.
+ * @return a list of all directories shared by NFS.
+ */
+ TQStringList sharedDirectories() const;
+
+ /**
+ * KNFSShare destructor.
+ * Do not call!
+ * The instance is destroyed automatically!
+ */
+ virtual ~KNFSShare();
+
+ /**
+ * Returns the path to the used exports file,
+ * or null if no exports file was found
+ */
+ TQString exportsPath() const;
+
+signals:
+ /**
+ * Emitted when the /etc/exports file has changed
+ */
+ void changed();
+
+private:
+ KNFSShare();
+ static KNFSShare* _instance;
+ KNFSSharePrivate* d;
+
+private slots:
+ void slotFileChange(const TQString&);
+};
+
+#endif
diff --git a/tdeio/tdeio/kprotocolinfo.cpp b/tdeio/tdeio/kprotocolinfo.cpp
new file mode 100644
index 000000000..9523b70cb
--- /dev/null
+++ b/tdeio/tdeio/kprotocolinfo.cpp
@@ -0,0 +1,257 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1999 Torben Weis <weis@kde.org>
+ Copyright (C) 2003 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifdef MAKE_TDECORE_LIB //needed for proper linkage (win32)
+#undef TDEIO_EXPORT
+#define TDEIO_EXPORT KDE_EXPORT
+#endif
+
+#include "kprotocolinfo.h"
+#include "kprotocolinfofactory.h"
+#include "kprotocolmanager.h"
+
+// Most of this class is implemented in tdecore/kprotocolinfo_tdecore.cpp
+// This file only contains a few static class-functions that depend on
+// KProtocolManager
+
+KProtocolInfo* KProtocolInfo::findProtocol(const KURL &url)
+{
+#ifdef MAKE_TDECORE_LIB
+ return 0;
+#else
+ TQString protocol = url.protocol();
+
+ if ( !KProtocolInfo::proxiedBy( protocol ).isEmpty() )
+ {
+ TQString dummy;
+ protocol = KProtocolManager::slaveProtocol(url, dummy);
+ }
+
+ return KProtocolInfoFactory::self()->findProtocol(protocol);
+#endif
+}
+
+
+KProtocolInfo::Type KProtocolInfo::inputType( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return T_NONE;
+
+ return prot->m_inputType;
+}
+
+KProtocolInfo::Type KProtocolInfo::outputType( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return T_NONE;
+
+ return prot->m_outputType;
+}
+
+
+bool KProtocolInfo::isSourceProtocol( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return false;
+
+ return prot->m_isSourceProtocol;
+}
+
+bool KProtocolInfo::isFilterProtocol( const KURL &url )
+{
+ return isFilterProtocol (url.protocol());
+}
+
+bool KProtocolInfo::isFilterProtocol( const TQString &protocol )
+{
+ // We call the findProtocol (const TQString&) to bypass any proxy settings.
+ KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(protocol);
+ if ( !prot )
+ return false;
+
+ return !prot->m_isSourceProtocol;
+}
+
+bool KProtocolInfo::isHelperProtocol( const KURL &url )
+{
+ return isHelperProtocol (url.protocol());
+}
+
+bool KProtocolInfo::isHelperProtocol( const TQString &protocol )
+{
+ // We call the findProtocol (const TQString&) to bypass any proxy settings.
+ KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(protocol);
+ if ( !prot )
+ return false;
+
+ return prot->m_isHelperProtocol;
+}
+
+bool KProtocolInfo::isKnownProtocol( const KURL &url )
+{
+ return isKnownProtocol (url.protocol());
+}
+
+bool KProtocolInfo::isKnownProtocol( const TQString &protocol )
+{
+ // We call the findProtocol (const TQString&) to bypass any proxy settings.
+ KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(protocol);
+ return ( prot != 0);
+}
+
+bool KProtocolInfo::supportsListing( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return false;
+
+ return prot->m_supportsListing;
+}
+
+TQStringList KProtocolInfo::listing( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return TQStringList();
+
+ return prot->m_listing;
+}
+
+bool KProtocolInfo::supportsReading( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return false;
+
+ return prot->m_supportsReading;
+}
+
+bool KProtocolInfo::supportsWriting( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return false;
+
+ return prot->m_supportsWriting;
+}
+
+bool KProtocolInfo::supportsMakeDir( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return false;
+
+ return prot->m_supportsMakeDir;
+}
+
+bool KProtocolInfo::supportsDeleting( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return false;
+
+ return prot->m_supportsDeleting;
+}
+
+bool KProtocolInfo::supportsLinking( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return false;
+
+ return prot->m_supportsLinking;
+}
+
+bool KProtocolInfo::supportsMoving( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return false;
+
+ return prot->m_supportsMoving;
+}
+
+bool KProtocolInfo::canCopyFromFile( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return false;
+
+ return prot->m_canCopyFromFile;
+}
+
+
+bool KProtocolInfo::canCopyToFile( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return false;
+
+ return prot->m_canCopyToFile;
+}
+
+bool KProtocolInfo::canRenameFromFile( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return false;
+
+ return prot->canRenameFromFile();
+}
+
+
+bool KProtocolInfo::canRenameToFile( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return false;
+
+ return prot->canRenameToFile();
+}
+
+bool KProtocolInfo::canDeleteRecursive( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return false;
+
+ return prot->canDeleteRecursive();
+}
+
+KProtocolInfo::FileNameUsedForCopying KProtocolInfo::fileNameUsedForCopying( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return FromURL;
+
+ return prot->fileNameUsedForCopying();
+}
+
+TQString KProtocolInfo::defaultMimetype( const KURL &url )
+{
+ KProtocolInfo::Ptr prot = findProtocol(url);
+ if ( !prot )
+ return TQString::null;
+
+ return prot->m_defaultMimetype;
+}
+
diff --git a/tdeio/tdeio/kprotocolinfo.h b/tdeio/tdeio/kprotocolinfo.h
new file mode 100644
index 000000000..65ed8c7cb
--- /dev/null
+++ b/tdeio/tdeio/kprotocolinfo.h
@@ -0,0 +1,688 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1999 Torben Weis <weis@kde.org>
+ Copyright (C) 2000-2001 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __kprotocolinfo_h__
+#define __kprotocolinfo_h__
+
+#include <tqstring.h>
+#include <tqstringlist.h>
+#include <tqdatastream.h>
+
+#include <kurl.h>
+#include <tdesycocaentry.h>
+#include <tdesycocatype.h>
+
+/**
+ * Information about I/O (Internet, etc.) protocols supported by KDE.
+
+ * This class is useful if you want to know which protocols
+ * KDE supports. In addition you can find out lots of information
+ * about a certain protocol. A KProtocolInfo instance represents a
+ * single protocol. Most of the functionality is provided by the static
+ * methods that scan the *.protocol files of all installed tdeioslaves to get
+ * this information.
+ *
+ * *.protocol files are installed in the "services" resource.
+ *
+ * @author Torben Weis <weis@kde.org>
+ */
+class TDEIO_EXPORT KProtocolInfo : public KSycocaEntry
+{
+ friend class KProtocolInfoFactory;
+ K_SYCOCATYPE( KST_KProtocolInfo, KSycocaEntry )
+
+public:
+ typedef KSharedPtr<KProtocolInfo> Ptr;
+
+public:
+ /**
+ * Read a protocol description file
+ * @param path the path of the description file
+ */
+ KProtocolInfo( const TQString & path); // KDE4: make private and add friend class KProtocolInfoBuildFactory
+ // Then we can get rid of the d pointer
+
+ /**
+ * Returns whether the protocol description file is valid.
+ * @return true if valid, false otherwise
+ */
+ virtual bool isValid() const { return !m_name.isEmpty(); }
+
+ /**
+ * Returns the name of the protocol.
+ *
+ * This corresponds to the "protocol=" field in the protocol description file.
+ *
+ * @return the name of the protocol
+ * @see KURL::protocol()
+ */
+ virtual TQString name() const { return m_name; }
+
+ //
+ // Static functions:
+ //
+
+ /**
+ * Returns list of all known protocols.
+ * @return a list of all known protocols
+ */
+ static TQStringList protocols();
+
+ /**
+ * Returns whether a protocol is installed that is able to handle @p url.
+ *
+ * @param url the url to check
+ * @return true if the protocol is known
+ * @see name()
+ */
+ static bool isKnownProtocol( const KURL &url );
+
+ /**
+ * Same as above except you can supply just the protocol instead of
+ * the whole URL.
+ */
+ static bool isKnownProtocol( const TQString& protocol )
+#ifdef KPROTOCOLINFO_TDECORE
+ KDE_WEAK_SYMBOL
+#endif
+ ;
+
+ /**
+ * Returns the library / executable to open for the protocol @p protocol
+ * Example : "kio_ftp", meaning either the executable "kio_ftp" or
+ * the library "kio_ftp.la" (recommended), whichever is available.
+ *
+ * This corresponds to the "exec=" field in the protocol description file.
+ * @param protocol the protocol to check
+ * @return the executable of library to open, or TQString::null for
+ * unsupported protocols
+ * @see KURL::protocol()
+ */
+ static TQString exec( const TQString& protocol );
+
+ /**
+ * Describes the type of a protocol.
+ */
+ enum Type { T_STREAM, ///< protocol returns a stream
+ T_FILESYSTEM, ///<protocol describes location in a file system
+ T_NONE, ///< no information about the tyope available
+ T_ERROR ///< used to signal an error
+ };
+
+ /**
+ * Returns whether the protocol should be treated as a filesystem
+ * or as a stream when reading from it.
+ *
+ * This corresponds to the "input=" field in the protocol description file.
+ * Valid values for this field are "filesystem", "stream" or "none" (default).
+ *
+ * @param url the url to check
+ * @return the input type of the given @p url
+ */
+ static Type inputType( const KURL &url );
+
+ /**
+ * Returns whether the protocol should be treated as a filesystem
+ * or as a stream when writing to it.
+ *
+ * This corresponds to the "output=" field in the protocol description file.
+ * Valid values for this field are "filesystem", "stream" or "none" (default).
+ *
+ * @param url the url to check
+ * @return the output type of the given @p url
+ */
+ static Type outputType( const KURL &url );
+
+ /**
+ * Returns the list of fields this protocol returns when listing
+ * The current possibilities are
+ * Name, Type, Size, Date, AccessDate, Access, Owner, Group, Link, URL, MimeType
+ * as well as Extra1, Extra2 etc. for extra fields (see extraFields).
+ *
+ * This corresponds to the "listing=" field in the protocol description file.
+ * The supported fields should be separated with ',' in the protocol description file.
+ *
+ * @param url the url to check
+ * @return a list of field names
+ */
+ static TQStringList listing( const KURL &url );
+
+ /**
+ * Definition of an extra field in the UDS entries, returned by a listDir operation.
+ *
+ * The name is the name of the column, translated.
+ *
+ * The type name comes from TQVariant::typeName()
+ * Currently supported types: "TQString", "TQDateTime" (ISO-8601 format)
+ *
+ * @since 3.2
+ */
+ struct ExtraField {
+ ExtraField() {} // for QValueList
+ ExtraField(const TQString& _name, const TQString& _type )
+ : name(_name), type(_type) {
+ }
+ TQString name;
+ TQString type; // KDE4: make it TQVariant::Type
+ };
+ typedef TQValueList<ExtraField > ExtraFieldList;
+ /**
+ * Definition of extra fields in the UDS entries, returned by a listDir operation.
+ *
+ * This corresponds to the "ExtraNames=" and "ExtraTypes=" fields in the protocol description file.
+ * Those two lists should be separated with ',' in the protocol description file.
+ * See ExtraField for details about names and types
+ *
+ * @since 3.2
+ */
+ static ExtraFieldList extraFields( const KURL& url );
+
+ /**
+ * Returns whether the protocol can act as a source protocol.
+ *
+ * A source protocol retrieves data from or stores data to the
+ * location specified by a URL.
+ * A source protocol is the opposite of a filter protocol.
+ *
+ * The "source=" field in the protocol description file determines
+ * whether a protocol is a source protocol or a filter protocol.
+ * @param url the url to check
+ * @return true if the protocol is a source of data (e.g. http), false if the
+ * protocol is a filter (e.g. gzip)
+ */
+ static bool isSourceProtocol( const KURL &url );
+
+ /**
+ * Returns whether the protocol can act as a helper protocol.
+ * A helper protocol invokes an external application and does not return
+ * a file or stream.
+ *
+ * This corresponds to the "helper=" field in the protocol description file.
+ * Valid values for this field are "true" or "false" (default).
+ *
+ * @param url the url to check
+ * @return true if the protocol is a helper protocol (e.g. vnc), false
+ * if not (e.g. http)
+ */
+ static bool isHelperProtocol( const KURL &url );
+
+ /**
+ * Same as above except you can supply just the protocol instead of
+ * the whole URL.
+ */
+ static bool isHelperProtocol( const TQString& protocol )
+#ifdef KPROTOCOLINFO_TDECORE
+ KDE_WEAK_SYMBOL
+#endif
+ ;
+
+ /**
+ * Returns whether the protocol can act as a filter protocol.
+ *
+ * A filter protocol can operate on data that is passed to it
+ * but does not retrieve/store data itself, like gzip.
+ * A filter protocol is the opposite of a source protocol.
+ *
+ * The "source=" field in the protocol description file determines
+ * whether a protocol is a source protocol or a filter protocol.
+ * Valid values for this field are "true" (default) for source protocol or
+ * "false" for filter protocol.
+ *
+ * @param url the url to check
+ * @return true if the protocol is a filter (e.g. gzip), false if the
+ * protocol is a helper or source
+ */
+ static bool isFilterProtocol( const KURL &url );
+
+ /**
+ * Same as above except you can supply just the protocol instead of
+ * the whole URL.
+ */
+ static bool isFilterProtocol( const TQString& protocol )
+#ifdef KPROTOCOLINFO_TDECORE
+ KDE_WEAK_SYMBOL
+#endif
+ ;
+
+ /**
+ * Returns whether the protocol can list files/objects.
+ * If a protocol supports listing it can be browsed in e.g. file-dialogs
+ * and konqueror.
+ *
+ * Whether a protocol supports listing is determined by the "listing="
+ * field in the protocol description file.
+ * If the protocol support listing it should list the fields it provides in
+ * this field. If the protocol does not support listing this field should
+ * remain empty (default.)
+ *
+ * @param url the url to check
+ * @return true if the protocol support listing
+ * @see listing()
+ */
+ static bool supportsListing( const KURL &url );
+
+ /**
+ * Returns whether the protocol can retrieve data from URLs.
+ *
+ * This corresponds to the "reading=" field in the protocol description file.
+ * Valid values for this field are "true" or "false" (default).
+ *
+ * @param url the url to check
+ * @return true if it is possible to read from the URL
+ */
+ static bool supportsReading( const KURL &url );
+
+ /**
+ * Returns whether the protocol can store data to URLs.
+ *
+ * This corresponds to the "writing=" field in the protocol description file.
+ * Valid values for this field are "true" or "false" (default).
+ *
+ * @param url the url to check
+ * @return true if the protocol supports writing
+ */
+ static bool supportsWriting( const KURL &url );
+
+ /**
+ * Returns whether the protocol can create directories/folders.
+ *
+ * This corresponds to the "makedir=" field in the protocol description file.
+ * Valid values for this field are "true" or "false" (default).
+ *
+ * @param url the url to check
+ * @return true if the protocol can create directories
+ */
+ static bool supportsMakeDir( const KURL &url );
+
+ /**
+ * Returns whether the protocol can delete files/objects.
+ *
+ * This corresponds to the "deleting=" field in the protocol description file.
+ * Valid values for this field are "true" or "false" (default).
+ *
+ * @param url the url to check
+ * @return true if the protocol supports deleting
+ */
+ static bool supportsDeleting( const KURL &url );
+
+ /**
+ * Returns whether the protocol can create links between files/objects.
+ *
+ * This corresponds to the "linking=" field in the protocol description file.
+ * Valid values for this field are "true" or "false" (default).
+ *
+ * @param url the url to check
+ * @return true if the protocol supports linking
+ */
+ static bool supportsLinking( const KURL &url );
+
+ /**
+ * Returns whether the protocol can move files/objects between different
+ * locations.
+ *
+ * This corresponds to the "moving=" field in the protocol description file.
+ * Valid values for this field are "true" or "false" (default).
+ *
+ * @param url the url to check
+ * @return true if the protocol supports moving
+ */
+ static bool supportsMoving( const KURL &url );
+
+ /**
+ * Returns whether the protocol can copy files/objects directly from the
+ * filesystem itself. If not, the application will read files from the
+ * filesystem using the file-protocol and pass the data on to the destination
+ * protocol.
+ *
+ * This corresponds to the "copyFromFile=" field in the protocol description file.
+ * Valid values for this field are "true" or "false" (default).
+ *
+ * @param url the url to check
+ * @return true if the protocol can copy files from the local file system
+ */
+ static bool canCopyFromFile( const KURL &url );
+
+ /**
+ * Returns whether the protocol can copy files/objects directly to the
+ * filesystem itself. If not, the application will receive the data from
+ * the source protocol and store it in the filesystem using the
+ * file-protocol.
+ *
+ * This corresponds to the "copyToFile=" field in the protocol description file.
+ * Valid values for this field are "true" or "false" (default).
+ *
+ * @param url the url to check
+ * @return true if the protocol can copy files to the local file system
+ */
+ static bool canCopyToFile( const KURL &url );
+
+ /**
+ * Returns whether the protocol can rename (i.e. move fast) files/objects
+ * directly from the filesystem itself. If not, the application will read
+ * files from the filesystem using the file-protocol and pass the data on
+ * to the destination protocol.
+ *
+ * This corresponds to the "renameFromFile=" field in the protocol description file.
+ * Valid values for this field are "true" or "false" (default).
+ *
+ * @param url the url to check
+ * @return true if the protocol can rename/move files from the local file system
+ * @since 3.4
+ */
+ static bool canRenameFromFile( const KURL &url );
+
+ /**
+ * Returns whether the protocol can rename (i.e. move fast) files/objects
+ * directly to the filesystem itself. If not, the application will receive
+ * the data from the source protocol and store it in the filesystem using the
+ * file-protocol.
+ *
+ * This corresponds to the "renameToFile=" field in the protocol description file.
+ * Valid values for this field are "true" or "false" (default).
+ *
+ * @param url the url to check
+ * @return true if the protocol can rename files to the local file system
+ * @since 3.4
+ */
+ static bool canRenameToFile( const KURL &url );
+
+ /**
+ * Returns whether the protocol can recursively delete directories by itself.
+ * If not (the usual case) then KIO will list the directory and delete files
+ * and empty directories one by one.
+ *
+ * This corresponds to the "deleteRecursive=" field in the protocol description file.
+ * Valid values for this field are "true" or "false" (default).
+ *
+ * @param url the url to check
+ * @return true if the protocol can delete non-empty directories by itself.
+ * @since 3.4
+ */
+ static bool canDeleteRecursive( const KURL &url );
+
+ typedef enum { Name, FromURL } FileNameUsedForCopying;
+
+ /**
+ * This setting defines the strategy to use for generating a filename, when
+ * copying a file or directory to another directory. By default the destination
+ * filename is made out of the filename in the source URL. However if the
+ * ioslave displays names that are different from the filename of the URL
+ * (e.g. kio_fonts shows Arial for arial.ttf, or kio_trash shows foo.txt and
+ * uses some internal URL), using Name means that the display name (UDS_NAME)
+ * will be used to as the filename in the destination directory.
+ *
+ * This corresponds to the "fileNameUsedForCopying=" field in the protocol description file.
+ * Valid values for this field are "Name" or "FromURL" (default).
+ *
+ * @param url the url to check
+ * @return how to generate the filename in the destination directory when copying/moving
+ * @since 3.4
+ */
+ static FileNameUsedForCopying fileNameUsedForCopying( const KURL &url );
+
+ /**
+ * Returns default mimetype for this URL based on the protocol.
+ *
+ * This corresponds to the "defaultMimetype=" field in the protocol description file.
+ *
+ * @param url the url to check
+ * @return the default mime type of the protocol, or null if unknown
+ */
+ static TQString defaultMimetype( const KURL& url );
+
+ /**
+ * Returns the name of the icon, associated with the specified protocol.
+ *
+ * This corresponds to the "Icon=" field in the protocol description file.
+ *
+ * @param protocol the protocol to check
+ * @return the icon of the protocol, or null if unknown
+ */
+ static TQString icon( const TQString& protocol );
+
+ /**
+ * Returns the name of the config file associated with the
+ * specified protocol. This is useful if two similar protocols
+ * need to share a single config file, e.g. http and https.
+ *
+ * This corresponds to the "config=" field in the protocol description file.
+ * The default is the protocol name, see name()
+ *
+ * @param protocol the protocol to check
+ * @return the config file, or null if unknown
+ */
+ static TQString config( const TQString& protocol );
+
+ /**
+ * Returns the soft limit on the number of slaves for this protocol.
+ * This limits the number of slaves used for a single operation, note
+ * that multiple operations may result in a number of instances that
+ * exceeds this soft limit.
+ *
+ * This corresponds to the "maxInstances=" field in the protocol description file.
+ * The default is 1.
+ *
+ * @param protocol the protocol to check
+ * @return the maximum number of slaves, or 1 if unknown
+ */
+ static int maxSlaves( const TQString& protocol );
+
+ /**
+ * Returns whether mimetypes can be determined based on extension for this
+ * protocol. For some protocols, e.g. http, the filename extension in the URL
+ * can not be trusted to truly reflect the file type.
+ *
+ * This corresponds to the "determineMimetypeFromExtension=" field in the protocol description file.
+ * Valid values for this field are "true" (default) or "false".
+ *
+ * @param protocol the protocol to check
+ * @return true if the mime types can be determined by extension
+ */
+ static bool determineMimetypeFromExtension( const TQString &protocol );
+
+ /**
+ * Returns the documentation path for the specified protocol.
+ *
+ * This corresponds to the "DocPath=" field in the protocol description file.
+ *
+ * @param protocol the protocol to check
+ * @return the docpath of the protocol, or null if unknown
+ * @since 3.2
+ */
+ static TQString docPath( const TQString& protocol );
+
+ /**
+ * Returns the protocol class for the specified protocol.
+ *
+ * This corresponds to the "Class=" field in the protocol description file.
+ *
+ * The following classes are defined:
+ * @li ":internet" for common internet protocols
+ * @li ":local" for protocols that access local resources
+ *
+ * Protocol classes always start with a ':' so that they can not be confused with
+ * the protocols themselves.
+ *
+ * @param protocol the protocol to check
+ * @return the class of the protocol, or null if unknown
+ * @since 3.2
+ */
+ static TQString protocolClass( const TQString& protocol );
+
+ /**
+ * Returns whether file previews should be shown for the specified protocol.
+ *
+ * This corresponds to the "ShowPreviews=" field in the protocol description file.
+ *
+ * By default previews are shown if protocolClass is :local.
+ *
+ * @param protocol the protocol to check
+ * @return true if previews should be shown by default, false otherwise
+ * @since 3.2
+ */
+ static bool showFilePreview( const TQString& protocol );
+
+ /**
+ * Returns the suggested URI parsing mode for the KURL parser.
+ *
+ * This corresponds to the "URIMode=" field in the protocol description file.
+ *
+ * The following parsing modes are defined:
+ * @li "url" for a standards compliant URL
+ * @li "rawuri" for a non-conformant URI for which URL parsing would be meaningless
+ * @li "mailto" for a mailto style URI (the path part contains only an email address)
+ *
+ * @param protocol the protocol to check
+ * @return the suggested parsing mode, or KURL::Auto if unspecified
+ *
+ * @since 3.2
+ */
+ static KURL::URIMode uriParseMode( const TQString& protocol );
+
+ /**
+ * Returns the list of capabilities provided by the tdeioslave implementing
+ * this protocol.
+ *
+ * This corresponds to the "Capabilities=" field in the protocol description file.
+ *
+ * The capability names are not defined globally, they are up to each
+ * slave implementation. For example when adding support for a new
+ * special command for mounting, one would add the string "Mount" to the
+ * capabilities list, and applications could check for that string
+ * before sending a special() command that would otherwise do nothing
+ * on older tdeioslave implementations.
+ *
+ * @param protocol the protocol to check
+ * @return the list of capabilities.
+ *
+ * @since 3.3
+ */
+ static TQStringList capabilities( const TQString& protocol );
+
+ /**
+ * Returns the name of the protocol through which the request
+ * will be routed if proxy support is enabled.
+ *
+ * A good example of this is the ftp protocol for which proxy
+ * support is commonly handled by the http protocol.
+ *
+ * This corresponds to the "ProxiedBy=" in the protocol description file.
+ *
+ * @since 3.3
+ */
+ static TQString proxiedBy( const TQString& protocol );
+
+public:
+ // Internal functions:
+ /**
+ * @internal construct a KProtocolInfo from a stream
+ */
+ KProtocolInfo( TQDataStream& _str, int offset);
+
+ virtual ~KProtocolInfo();
+
+ /**
+ * @internal
+ * Load the protocol info from a stream.
+ */
+ virtual void load(TQDataStream& );
+
+ /**
+ * @internal
+ * Save the protocol info to a stream.
+ */
+ virtual void save(TQDataStream& );
+
+ ////////////////////////// DEPRECATED /////////////////////////
+ // The following methods are deprecated:
+
+ /// @deprecated
+ static Type inputType( const TQString& protocol ) KDE_DEPRECATED;
+ /// @deprecated
+ static Type outputType( const TQString& protocol ) KDE_DEPRECATED;
+ /**
+ * @deprecated
+ * Returns the list of fields this protocol returns when listing
+ * The current possibilities are
+ * Name, Type, Size, Date, AccessDate, Access, Owner, Group, Link, URL, MimeType
+ */
+ static TQStringList listing( const TQString& protocol ) KDE_DEPRECATED;
+ /// @deprecated
+ static bool isSourceProtocol( const TQString& protocol ) KDE_DEPRECATED;
+ /// @deprecated
+ static bool supportsListing( const TQString& protocol ) KDE_DEPRECATED;
+ /// @deprecated
+ static bool supportsReading( const TQString& protocol ) KDE_DEPRECATED;
+ /// @deprecated
+ static bool supportsWriting( const TQString& protocol ) KDE_DEPRECATED;
+ /// @deprecated
+ static bool supportsMakeDir( const TQString& protocol ) KDE_DEPRECATED;
+ /// @deprecated
+ static bool supportsDeleting( const TQString& protocol ) KDE_DEPRECATED;
+ /// @deprecated
+ static bool supportsLinking( const TQString& protocol ) KDE_DEPRECATED;
+ /// @deprecated
+ static bool supportsMoving( const TQString& protocol ) KDE_DEPRECATED;
+ /// @deprecated
+ static bool canCopyFromFile( const TQString& protocol ) KDE_DEPRECATED;
+ /// @deprecated
+ static bool canCopyToFile( const TQString& protocol ) KDE_DEPRECATED;
+ /// @deprecated
+ static TQString defaultMimetype( const TQString& protocol) KDE_DEPRECATED;
+ //////////////////////// END DEPRECATED ///////////////////////
+
+protected:
+ TQString m_name;
+ TQString m_exec;
+ Type m_inputType;
+ Type m_outputType;
+ TQStringList m_listing;
+ bool m_isSourceProtocol;
+ bool m_isHelperProtocol;
+ bool m_supportsListing;
+ bool m_supportsReading;
+ bool m_supportsWriting;
+ bool m_supportsMakeDir;
+ bool m_supportsDeleting;
+ bool m_supportsLinking;
+ bool m_supportsMoving;
+ TQString m_defaultMimetype;
+ bool m_determineMimetypeFromExtension;
+ TQString m_icon;
+ bool m_canCopyFromFile;
+ bool m_canCopyToFile;
+ TQString m_config;
+ int m_maxSlaves;
+
+ bool canRenameFromFile() const; // for kprotocolinfo_tdecore
+ bool canRenameToFile() const; // for kprotocolinfo_tdecore
+ bool canDeleteRecursive() const; // for kprotocolinfo_tdecore
+ FileNameUsedForCopying fileNameUsedForCopying() const; // for kprotocolinfo_tdecore
+ static KProtocolInfo* findProtocol(const KURL &url); // for kprotocolinfo_tdecore
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KProtocolInfoPrivate;
+ KProtocolInfoPrivate* d;
+};
+
+TDEIO_EXPORT TQDataStream& operator>>( TQDataStream& s, KProtocolInfo::ExtraField& field );
+TDEIO_EXPORT TQDataStream& operator<<( TQDataStream& s, const KProtocolInfo::ExtraField& field );
+
+#endif
diff --git a/tdeio/tdeio/kprotocolmanager.cpp b/tdeio/tdeio/kprotocolmanager.cpp
new file mode 100644
index 000000000..450b9d107
--- /dev/null
+++ b/tdeio/tdeio/kprotocolmanager.cpp
@@ -0,0 +1,534 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1999 Torben Weis <weis@kde.org>
+ Copyright (C) 2000- Waldo Bastain <bastain@kde.org>
+ Copyright (C) 2000- Dawit Alemayehu <adawit@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <string.h>
+#include <sys/utsname.h>
+
+#include <dcopref.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <tdeconfig.h>
+#include <kstandarddirs.h>
+#include <klibloader.h>
+#include <kstringhandler.h>
+#include <kstaticdeleter.h>
+#include <tdeio/slaveconfig.h>
+#include <tdeio/ioslave_defaults.h>
+#include <tdeio/http_slave_defaults.h>
+
+#include "kprotocolmanager.h"
+
+class
+KProtocolManagerPrivate
+{
+public:
+ KProtocolManagerPrivate();
+
+ ~KProtocolManagerPrivate();
+
+ TDEConfig *config;
+ TDEConfig *http_config;
+ bool init_busy;
+ KURL url;
+ TQString protocol;
+ TQString proxy;
+ TQString modifiers;
+ TQString useragent;
+};
+
+static KProtocolManagerPrivate* d = 0;
+static KStaticDeleter<KProtocolManagerPrivate> kpmpksd;
+
+KProtocolManagerPrivate::KProtocolManagerPrivate()
+ :config(0), http_config(0), init_busy(false)
+{
+ kpmpksd.setObject(d, this);
+}
+
+KProtocolManagerPrivate::~KProtocolManagerPrivate()
+{
+ delete config;
+ delete http_config;
+}
+
+
+// DEFAULT USERAGENT STRING
+#define CFG_DEFAULT_UAGENT(X) \
+TQString("Mozilla/5.0 (compatible; Konqueror/%1.%2%3) KHTML/%4.%5.%6 (like Gecko)") \
+ .arg(TDE_VERSION_MAJOR).arg(TDE_VERSION_MINOR).arg(X).arg(TDE_VERSION_MAJOR).arg(TDE_VERSION_MINOR).arg(TDE_VERSION_RELEASE)
+
+void KProtocolManager::reparseConfiguration()
+{
+ kpmpksd.destructObject();
+
+ // Force the slave config to re-read its config...
+ TDEIO::SlaveConfig::self()->reset ();
+}
+
+TDEConfig *KProtocolManager::config()
+{
+ if (!d)
+ d = new KProtocolManagerPrivate;
+
+ if (!d->config)
+ {
+ d->config = new TDEConfig("tdeioslaverc", true, false);
+ }
+ return d->config;
+}
+
+TDEConfig *KProtocolManager::http_config()
+{
+ if (!d)
+ d = new KProtocolManagerPrivate;
+
+ if (!d->http_config)
+ {
+ d->http_config = new TDEConfig("kio_httprc", false, false);
+ }
+ return d->http_config;
+}
+
+/*=============================== TIMEOUT SETTINGS ==========================*/
+
+int KProtocolManager::readTimeout()
+{
+ TDEConfig *cfg = config();
+ cfg->setGroup( TQString::null );
+ int val = cfg->readNumEntry( "ReadTimeout", DEFAULT_READ_TIMEOUT );
+ return QMAX(MIN_TIMEOUT_VALUE, val);
+}
+
+int KProtocolManager::connectTimeout()
+{
+ TDEConfig *cfg = config();
+ cfg->setGroup( TQString::null );
+ int val = cfg->readNumEntry( "ConnectTimeout", DEFAULT_CONNECT_TIMEOUT );
+ return QMAX(MIN_TIMEOUT_VALUE, val);
+}
+
+int KProtocolManager::proxyConnectTimeout()
+{
+ TDEConfig *cfg = config();
+ cfg->setGroup( TQString::null );
+ int val = cfg->readNumEntry( "ProxyConnectTimeout", DEFAULT_PROXY_CONNECT_TIMEOUT );
+ return QMAX(MIN_TIMEOUT_VALUE, val);
+}
+
+int KProtocolManager::responseTimeout()
+{
+ TDEConfig *cfg = config();
+ cfg->setGroup( TQString::null );
+ int val = cfg->readNumEntry( "ResponseTimeout", DEFAULT_RESPONSE_TIMEOUT );
+ return QMAX(MIN_TIMEOUT_VALUE, val);
+}
+
+/*========================== PROXY SETTINGS =================================*/
+
+bool KProtocolManager::useProxy()
+{
+ return proxyType() != NoProxy;
+}
+
+bool KProtocolManager::useReverseProxy()
+{
+ TDEConfig *cfg = config();
+ cfg->setGroup( "Proxy Settings" );
+ return cfg->readBoolEntry("ReversedException", false);
+}
+
+KProtocolManager::ProxyType KProtocolManager::proxyType()
+{
+ TDEConfig *cfg = config();
+ cfg->setGroup( "Proxy Settings" );
+ return static_cast<ProxyType>(cfg->readNumEntry( "ProxyType" ));
+}
+
+KProtocolManager::ProxyAuthMode KProtocolManager::proxyAuthMode()
+{
+ TDEConfig *cfg = config();
+ cfg->setGroup( "Proxy Settings" );
+ return static_cast<ProxyAuthMode>(cfg->readNumEntry( "AuthMode" ));
+}
+
+/*========================== CACHING =====================================*/
+
+bool KProtocolManager::useCache()
+{
+ TDEConfig *cfg = http_config();
+ return cfg->readBoolEntry( "UseCache", true );
+}
+
+TDEIO::CacheControl KProtocolManager::cacheControl()
+{
+ TDEConfig *cfg = http_config();
+ TQString tmp = cfg->readEntry("cache");
+ if (tmp.isEmpty())
+ return DEFAULT_CACHE_CONTROL;
+ return TDEIO::parseCacheControl(tmp);
+}
+
+TQString KProtocolManager::cacheDir()
+{
+ TDEConfig *cfg = http_config();
+ return cfg->readPathEntry("CacheDir", TDEGlobal::dirs()->saveLocation("cache","http"));
+}
+
+int KProtocolManager::maxCacheAge()
+{
+ TDEConfig *cfg = http_config();
+ return cfg->readNumEntry( "MaxCacheAge", DEFAULT_MAX_CACHE_AGE ); // 14 days
+}
+
+int KProtocolManager::maxCacheSize()
+{
+ TDEConfig *cfg = http_config();
+ return cfg->readNumEntry( "MaxCacheSize", DEFAULT_MAX_CACHE_SIZE ); // 5 MB
+}
+
+TQString KProtocolManager::noProxyForRaw()
+{
+ TDEConfig *cfg = config();
+ cfg->setGroup( "Proxy Settings" );
+
+ return cfg->readEntry( "NoProxyFor" );
+}
+
+TQString KProtocolManager::noProxyFor()
+{
+ TQString noProxy = noProxyForRaw();
+ if (proxyType() == EnvVarProxy)
+ noProxy = TQString::fromLocal8Bit(getenv(noProxy.local8Bit()));
+
+ return noProxy;
+}
+
+TQString KProtocolManager::proxyFor( const TQString& protocol )
+{
+ TQString scheme = protocol.lower();
+
+ if (scheme == "webdav")
+ scheme = "http";
+ else if (scheme == "webdavs")
+ scheme = "https";
+
+ TDEConfig *cfg = config();
+ cfg->setGroup( "Proxy Settings" );
+ return cfg->readEntry( scheme + "Proxy" );
+}
+
+TQString KProtocolManager::proxyForURL( const KURL &url )
+{
+ TQString proxy;
+ ProxyType pt = proxyType();
+
+ switch (pt)
+ {
+ case PACProxy:
+ case WPADProxy:
+ if (!url.host().isEmpty())
+ {
+ KURL u (url);
+ TQString p = u.protocol().lower();
+
+ // webdav is a KDE specific protocol. Look up proxy
+ // information using HTTP instead...
+ if ( p == "webdav" )
+ {
+ p = "http";
+ u.setProtocol( p );
+ }
+ else if ( p == "webdavs" )
+ {
+ p = "https";
+ u.setProtocol( p );
+ }
+
+ if ( p.startsWith("http") || p == "ftp" || p == "gopher" )
+ DCOPRef( "kded", "proxyscout" ).call( "proxyForURL", u ).get( proxy );
+ }
+ break;
+ case EnvVarProxy:
+ proxy = TQString(TQString::fromLocal8Bit(getenv(proxyFor(url.protocol()).local8Bit()))).stripWhiteSpace();
+ break;
+ case ManualProxy:
+ proxy = proxyFor( url.protocol() );
+ break;
+ case NoProxy:
+ default:
+ break;
+ }
+
+ return (proxy.isEmpty() ? TQString::fromLatin1("DIRECT") : proxy);
+}
+
+void KProtocolManager::badProxy( const TQString &proxy )
+{
+ DCOPRef( "kded", "proxyscout" ).send( "blackListProxy", proxy );
+}
+
+/*
+ Domain suffix match. E.g. return true if host is "cuzco.inka.de" and
+ nplist is "inka.de,hadiko.de" or if host is "localhost" and nplist is
+ "localhost".
+*/
+static bool revmatch(const char *host, const char *nplist)
+{
+ if (host == 0)
+ return false;
+
+ const char *hptr = host + strlen( host ) - 1;
+ const char *nptr = nplist + strlen( nplist ) - 1;
+ const char *shptr = hptr;
+
+ while ( nptr >= nplist )
+ {
+ if ( *hptr != *nptr )
+ {
+ hptr = shptr;
+
+ // Try to find another domain or host in the list
+ while(--nptr>=nplist && *nptr!=',' && *nptr!=' ') ;
+
+ // Strip out multiple spaces and commas
+ while(--nptr>=nplist && (*nptr==',' || *nptr==' ')) ;
+ }
+ else
+ {
+ if ( nptr==nplist || nptr[-1]==',' || nptr[-1]==' ')
+ return true;
+ if ( hptr == host ) // e.g. revmatch("bugs.kde.org","mybugs.kde.org")
+ return false;
+
+ hptr--;
+ nptr--;
+ }
+ }
+
+ return false;
+}
+
+TQString KProtocolManager::slaveProtocol(const KURL &url, TQString &proxy)
+{
+ if (url.hasSubURL()) // We don't want the suburl's protocol
+ {
+ KURL::List list = KURL::split(url);
+ KURL::List::Iterator it = list.fromLast();
+ return slaveProtocol(*it, proxy);
+ }
+
+ if (!d)
+ d = new KProtocolManagerPrivate;
+
+ if (d->url == url)
+ {
+ proxy = d->proxy;
+ return d->protocol;
+ }
+
+ if (useProxy())
+ {
+ proxy = proxyForURL(url);
+ if ((proxy != "DIRECT") && (!proxy.isEmpty()))
+ {
+ bool isRevMatch = false;
+ KProtocolManager::ProxyType type = proxyType();
+ bool useRevProxy = ((type == ManualProxy) && useReverseProxy());
+
+ TQString noProxy;
+ // Check no proxy information iff the proxy type is either
+ // ManualProxy or EnvVarProxy
+ if ( (type == ManualProxy) || (type == EnvVarProxy) )
+ noProxy = noProxyFor();
+
+ if (!noProxy.isEmpty())
+ {
+ TQString qhost = url.host().lower();
+ const char *host = qhost.latin1();
+ TQString qno_proxy = noProxy.stripWhiteSpace().lower();
+ const char *no_proxy = qno_proxy.latin1();
+ isRevMatch = revmatch(host, no_proxy);
+
+ // If no match is found and the request url has a port
+ // number, try the combination of "host:port". This allows
+ // users to enter host:port in the No-proxy-For list.
+ if (!isRevMatch && url.port() > 0)
+ {
+ qhost += ':' + TQString::number (url.port());
+ host = qhost.latin1();
+ isRevMatch = revmatch (host, no_proxy);
+ }
+
+ // If the hostname does not contain a dot, check if
+ // <local> is part of noProxy.
+ if (!isRevMatch && host && (strchr(host, '.') == NULL))
+ isRevMatch = revmatch("<local>", no_proxy);
+ }
+
+ if ( (!useRevProxy && !isRevMatch) || (useRevProxy && isRevMatch) )
+ {
+ d->url = proxy;
+ if ( d->url.isValid() )
+ {
+ // The idea behind slave protocols is not applicable to http
+ // and webdav protocols.
+ TQString protocol = url.protocol().lower();
+ if (protocol.startsWith("http") || protocol.startsWith("webdav"))
+ d->protocol = protocol;
+ else
+ {
+ d->protocol = d->url.protocol();
+ kdDebug () << "slaveProtocol: " << d->protocol << endl;
+ }
+
+ d->url = url;
+ d->proxy = proxy;
+ return d->protocol;
+ }
+ }
+ }
+ }
+
+ d->url = url;
+ d->proxy = proxy = TQString::null;
+ d->protocol = url.protocol();
+ return d->protocol;
+}
+
+/*================================= USER-AGENT SETTINGS =====================*/
+
+TQString KProtocolManager::userAgentForHost( const TQString& hostname )
+{
+ TQString sendUserAgent = TDEIO::SlaveConfig::self()->configData("http", hostname.lower(), "SendUserAgent").lower();
+ if (sendUserAgent == "false")
+ return TQString::null;
+
+ TQString useragent = TDEIO::SlaveConfig::self()->configData("http", hostname.lower(), "UserAgent");
+
+ // Return the default user-agent if none is specified
+ // for the requested host.
+ if (useragent.isEmpty())
+ return defaultUserAgent();
+
+ return useragent;
+}
+
+TQString KProtocolManager::defaultUserAgent( )
+{
+ TQString modifiers = TDEIO::SlaveConfig::self()->configData("http", TQString::null, "UserAgentKeys");
+ return defaultUserAgent(modifiers);
+}
+
+TQString KProtocolManager::defaultUserAgent( const TQString &_modifiers )
+{
+ if (!d)
+ d = new KProtocolManagerPrivate;
+
+ TQString modifiers = _modifiers.lower();
+ if (modifiers.isEmpty())
+ modifiers = DEFAULT_USER_AGENT_KEYS;
+
+ if (d->modifiers == modifiers)
+ return d->useragent;
+
+ TQString supp;
+ struct utsname nam;
+ if( uname(&nam) >= 0 )
+ {
+ if( modifiers.contains('o') )
+ {
+ supp += TQString("; %1").arg(nam.sysname);
+ if ( modifiers.contains('v') )
+ supp += TQString(" %1").arg(nam.release);
+ }
+ if( modifiers.contains('p') )
+ {
+ // TODO: determine this value instead of hardcoding it...
+ supp += TQString::fromLatin1("; X11");
+ }
+ if( modifiers.contains('m') )
+ {
+ supp += TQString("; %1").arg(nam.machine);
+ }
+ if( modifiers.contains('l') )
+ {
+ TQStringList languageList = TDEGlobal::locale()->languageList();
+ TQStringList::Iterator it = languageList.find( TQString::fromLatin1("C") );
+ if( it != languageList.end() )
+ {
+ if( languageList.contains( TQString::fromLatin1("en") ) > 0 )
+ languageList.remove( it );
+ else
+ (*it) = TQString::fromLatin1("en");
+ }
+ if( languageList.count() )
+ supp += TQString("; %1").arg(languageList.join(", "));
+ }
+ }
+ d->modifiers = modifiers;
+ d->useragent = CFG_DEFAULT_UAGENT(supp);
+ return d->useragent;
+}
+
+/*==================================== OTHERS ===============================*/
+
+bool KProtocolManager::markPartial()
+{
+ TDEConfig *cfg = config();
+ cfg->setGroup( TQString::null );
+ return cfg->readBoolEntry( "MarkPartial", true );
+}
+
+int KProtocolManager::minimumKeepSize()
+{
+ TDEConfig *cfg = config();
+ cfg->setGroup( TQString::null );
+ return cfg->readNumEntry( "MinimumKeepSize",
+ DEFAULT_MINIMUM_KEEP_SIZE ); // 5000 byte
+}
+
+bool KProtocolManager::autoResume()
+{
+ TDEConfig *cfg = config();
+ cfg->setGroup( TQString::null );
+ return cfg->readBoolEntry( "AutoResume", false );
+}
+
+bool KProtocolManager::persistentConnections()
+{
+ TDEConfig *cfg = config();
+ cfg->setGroup( TQString::null );
+ return cfg->readBoolEntry( "PersistentConnections", true );
+}
+
+bool KProtocolManager::persistentProxyConnection()
+{
+ TDEConfig *cfg = config();
+ cfg->setGroup( TQString::null );
+ return cfg->readBoolEntry( "PersistentProxyConnection", false );
+}
+
+TQString KProtocolManager::proxyConfigScript()
+{
+ TDEConfig *cfg = config();
+ cfg->setGroup( "Proxy Settings" );
+ return cfg->readEntry( "Proxy Config Script" );
+}
diff --git a/tdeio/tdeio/kprotocolmanager.h b/tdeio/tdeio/kprotocolmanager.h
new file mode 100644
index 000000000..ce504a83f
--- /dev/null
+++ b/tdeio/tdeio/kprotocolmanager.h
@@ -0,0 +1,389 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 1999 Torben Weis <weis@kde.org>
+ Copyright (C) 2000- Waldo Bastain <bastain@kde.org>
+ Copyright (C) 2000- Dawit Alemayehu <adawit@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __kprotocolmanager_h__
+#define __kprotocolmanager_h__
+
+#include <tqstringlist.h>
+
+#include <kapplication.h>
+#include <tdeio/global.h>
+
+/** @deprecated Use KProtocolManager::defaultUserAgent() instead. */
+#define DEFAULT_USERAGENT_STRING ""
+
+class TDEConfig;
+
+/**
+ * Provides information about I/O (Internet, etc.) settings chosen/set
+ * by the end user.
+ *
+ * KProtocolManager has a heap of static functions that allows only read
+ * access to KDE's IO related settings. These include proxy, cache, file
+ * transfer resumption, timeout and user-agent related settings.
+ *
+ * The information provided by this class is generic enough to be applicable
+ * to any application that makes use of KDE's IO sub-system. Note that this
+ * mean the proxy, timeout etc. settings are saved in a separate user-specific
+ * config file and not in the config file of the application.
+ *
+ * Original author:
+ * @author Torben Weis <weis@kde.org>
+ *
+ * Revised by:
+ * @author Waldo Bastain <bastain@kde.org>
+ * @author Dawit Alemayehu <adawit@kde.org>
+ * @see KPAC
+ */
+class TDEIO_EXPORT KProtocolManager
+{
+public:
+
+
+/*=========================== USER-AGENT SETTINGS ===========================*/
+
+
+ /**
+ * Returns the default user-agent string.
+ *
+ * @return the default user-agent string
+ */
+ static TQString defaultUserAgent();
+
+ /**
+ * Returns the default user-agent value.
+ *
+ * @param keys can be any of the following:
+ * @li 'o' Show OS
+ * @li 'v' Show OS Version
+ * @li 'p' Show platform
+ * @li 'm' Show machine architecture
+ * @li 'l' Show language
+ * @return the default user-agent value with the given @p keys
+ */
+ static TQString defaultUserAgent(const TQString &keys);
+
+ /**
+ * Returns the userAgent string configured for the
+ * specified host.
+ *
+ * If hostname is not found or is empty (i.e. "" or
+ * TQString::null) this function will return the default
+ * user agent.
+ *
+ * @param hostname name of the host
+ * @return specified userAgent string
+ */
+ static TQString userAgentForHost( const TQString &hostname );
+
+
+/*=========================== TIMEOUT CONFIG ================================*/
+
+
+ /**
+ * Returns the preferred timeout value for reading from
+ * remote connections in seconds.
+ *
+ * @return timeout value for remote connection in secs.
+ */
+ static int readTimeout();
+
+ /**
+ * Returns the preferred timeout value for remote connections
+ * in seconds.
+ *
+ * @return timeout value for remote connection in secs.
+ */
+ static int connectTimeout();
+
+ /**
+ * Returns the preferred timeout value for proxy connections
+ * in seconds.
+ *
+ * @return timeout value for proxy connection in secs.
+ */
+ static int proxyConnectTimeout();
+
+ /**
+ * Returns the preferred response timeout value for
+ * remote connecting in seconds.
+ *
+ * @return timeout value for remote connection in seconds.
+ */
+ static int responseTimeout();
+
+
+/*=============================== PROXY CONFIG ==============================*/
+
+
+ /**
+ * Returns true if the user specified a proxy server to make connections.
+ *
+ * @see slaveProtocol, proxyForURL, proxyFor
+ */
+ static bool useProxy();
+
+ /**
+ * Returns true if the proxy settings should apply to the list
+ * returned by @ref noProxyFor.
+ *
+ * Normally addresses listed in the noProxyFor list are not routed
+ * through a proxy server. However, if this function returns true,
+ * then all addresses listed in the noProxyFor list are to be routed
+ * through a proxy server where as those that are not should bypass it.
+ *
+ * This function as well as @ref noProxyFor only apply when @ref proxyType
+ * is @p ManualProxy.
+ *
+ * @see proxyForURL, proxyFor, slaveProtocol
+ */
+ static bool useReverseProxy();
+
+ /**
+ * Types of proxy configuration
+ * @li NoProxy - No proxy is used
+ * @li ManualProxy - Proxies are manually configured
+ * @li PACProxy - A Proxy configuration URL has been given
+ * @li WPADProxy - A proxy should be automatically discovered
+ * @li EnvVarProxy - Use the proxy values set through environment variables.
+ */
+ enum ProxyType
+ {
+ NoProxy,
+ ManualProxy,
+ PACProxy,
+ WPADProxy,
+ EnvVarProxy
+ };
+
+ /**
+ * Returns the type of proxy configuration that is used.
+ *
+ * @see ProxyType
+ */
+ static ProxyType proxyType();
+
+ /**
+ * Proxy authorization modes.
+ *
+ * @li Prompt - Ask for authorization as needed
+ * @li Automatic - Use auto login as defined in .kionetrc files.
+ *
+ * NOTE: .kionetrc files have the same format as ftp .netrc files.
+ * Please note the use of .kionetrc files is highly discouraged since
+ * password is stored in clear text. For future releases the ability
+ * to store preset password for proxy servers will probably be supported
+ * through KWallet integration.
+ */
+ enum ProxyAuthMode
+ {
+ Prompt,
+ Automatic
+ };
+
+ /**
+ * Returns the way proxy authorization should be handled.
+ *
+ * @see ProxyAuthMode
+ */
+ static ProxyAuthMode proxyAuthMode();
+
+ /**
+ * Returns a comma-separated list of hostnames or partial
+ * host-names that should bypass any proxy settings.
+ *
+ * This function as well as @ref useReverseProxy only apply
+ * when @ref proxyType is @p ManualProxy.
+ *
+ * @see useReverseProxy, proxyFor, proxyForURL, slaveProtocol
+ */
+ static TQString noProxyFor();
+
+ /**
+ * Same as above except the environment variable name
+ * is returned instead of the variable value when
+ * @ref proxyType is @p EnvVarProxy.
+ *
+ * @see noProxyFor
+ * @since 3.5.x
+ */
+ static TQString noProxyForRaw();
+
+ /**
+ * Returns the proxy server address for a given protocol.
+ *
+ * NOTE: This function does not take the @ref useReverseProxy()
+ * settings into account.
+ *
+ * @see useReverseProxy, slaveProtocol
+ * @param protocol the protocol whose proxy info is needed
+ * @returns the proxy server address if one is available,
+ * or TQString::null if not available
+ */
+ static TQString proxyFor( const TQString& protocol );
+
+ /**
+ * Returns the proxy server address for a given URL.
+ *
+ * If @ref proxyType returns Automatic, an external service
+ * called KPAC (a kded module) is used to determine the proxy
+ * server. Otherwise, @ref proxyFor is invoked to determine
+ * whether the URL needs to be routed through a proxy server.
+ *
+ * NOTE: This function does not take the @ref useReverseProxy()
+ * or the @ref noProxyFor() settings into account.
+ *
+ * @see useReverseProxy, slaveProtocol, noProxyFor
+ * @param url the URL whose proxy info is needed
+ * @returns the proxy server address or the text "DIRECT"
+ * if no proxying is needed for the given address.
+ */
+ static TQString proxyForURL( const KURL& url );
+
+ /**
+ * Marks this proxy as bad (down). It will not be used for the
+ * next 30 minutes. (The script may supply an alternate proxy)
+ * @param proxy the proxy to mark as bad (as URL)
+ */
+ static void badProxy( const TQString & proxy );
+
+ /**
+ * Returns the URL of the script for automatic proxy configuration.
+ * @return the proxy configuration script
+ */
+ static TQString proxyConfigScript();
+
+
+/*========================== CACHE CONFIG ===================================*/
+
+
+ /**
+ * Returns true/false to indicate whether a cache
+ * should be used
+ *
+ * @return true to use the cache, false otherwisea
+ */
+ static bool useCache();
+
+ /**
+ * Returns the maximum age in seconds cached files should be
+ * kept before they are deleted as necessary.
+ *
+ * @return the maximum cache age in seconds
+ */
+ static int maxCacheAge();
+
+ /**
+ * Returns the maximum size that can be used for caching.
+ *
+ * By default this function returns the DEFAULT_MAX_CACHE_SIZE
+ * value as defined in http_slave_defaults.h. Not that the
+ * value returned is in bytes, hence a value of 5120 would mean
+ * 5 Kb.
+ *
+ * @return the maximum cache size in bytes
+ */
+ static int maxCacheSize(); // Maximum cache size in Kb.
+
+ /**
+ * The directory which contains the cache files.
+ * @return the directory that contains the cache files
+ */
+ static TQString cacheDir();
+
+ /**
+ * Returns the Cache control directive to be used.
+ * @return the cache control value
+ */
+ static TDEIO::CacheControl cacheControl();
+
+
+/*============================ DOWNLOAD CONFIG ==============================*/
+
+ /**
+ * Returns true if partial downloads should be
+ * automatically resumed.
+ * @return true to resume partial downloads
+ */
+ static bool autoResume();
+
+ /**
+ * Returns true if partial downloads should be marked
+ * with a ".part" extension.
+ * @return true if partial downloads should get an ".part" extension
+ */
+ static bool markPartial();
+
+ /**
+ * Returns the minimum file size for keeping aborted
+ * downloads.
+ *
+ * Any data downloaded that does not meet this minimum
+ * requirement will simply be discarded. The default size
+ * is 5 KB.
+ *
+ * @return the minimum keep size for aborted downloads in bytes
+ */
+ static int minimumKeepSize();
+
+
+ /*============================ NETWORK CONNECTIONS ==========================*/
+ /**
+ * Returns true if proxy connections should be persistent.
+ * @return true if proxy connections should be persistent
+ * @since 3.1
+ */
+ static bool persistentProxyConnection();
+
+ /**
+ * Returns true if connections should be persistent
+ * @return true if the connections should be persistent
+ */
+ static bool persistentConnections();
+
+/*=============================== OTHERS ====================================*/
+
+
+ /**
+ * Force a reload of the general config file of
+ * io-slaves ( tdeioslaverc).
+ */
+ static void reparseConfiguration();
+
+ /**
+ * Return the protocol to use in order to handle the given @p url
+ * It's usually the same, except that FTP, when handled by a proxy,
+ * needs an HTTP ioslave.
+ *
+ * When a proxy is to be used, proxy contains the URL for the proxy.
+ * @param url the url to check
+ * @param proxy the URL of the proxy to use
+ * @return the slave protocol (e.g. 'http'), can be null if unknown
+ */
+ static TQString slaveProtocol(const KURL &url, TQString &proxy);
+
+ /**
+ * @internal
+ * (Shared with SlaveConfig)
+ */
+ static TDEConfig *config();
+private:
+ static TDEConfig *http_config();
+};
+#endif
diff --git a/tdeio/tdeio/kremoteencoding.cpp b/tdeio/tdeio/kremoteencoding.cpp
new file mode 100644
index 000000000..632eeb8b2
--- /dev/null
+++ b/tdeio/tdeio/kremoteencoding.cpp
@@ -0,0 +1,95 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 Thiago Macieira <thiago.macieira@kdemail.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <config.h>
+
+#include <kdebug.h>
+#include <kstringhandler.h>
+#include "kremoteencoding.h"
+
+KRemoteEncoding::KRemoteEncoding(const char *name)
+ : codec(0L), d(0L)
+{
+ setEncoding(name);
+}
+
+KRemoteEncoding::~KRemoteEncoding()
+{
+ // delete d; // not necessary yet
+}
+
+TQString KRemoteEncoding::decode(const TQCString& name) const
+{
+#ifdef CHECK_UTF8
+ if (codec->mibEnum() == 106 && !KStringHandler::isUtf8(name))
+ return TQString::fromLatin1(name);
+#endif
+
+ TQString result = codec->toUnicode(name);
+ if (codec->fromUnicode(result) != name)
+ // fallback in case of decoding failure
+ return TQString::fromLatin1(name);
+
+ return result;
+}
+
+TQCString KRemoteEncoding::encode(const TQString& name) const
+{
+ TQCString result = codec->fromUnicode(name);
+ if (codec->toUnicode(result) != name)
+ return name.latin1();
+
+ return result;
+}
+
+TQCString KRemoteEncoding::encode(const KURL& url) const
+{
+ return encode(url.path());
+}
+
+TQCString KRemoteEncoding::directory(const KURL& url, bool ignore_trailing_slash) const
+{
+ TQString dir = url.directory(true, ignore_trailing_slash);
+
+ return encode(dir);
+}
+
+TQCString KRemoteEncoding::fileName(const KURL& url) const
+{
+ return encode(url.fileName());
+}
+
+void KRemoteEncoding::setEncoding(const char *name)
+{
+ // don't delete codecs
+
+ if (name)
+ codec = TQTextCodec::codecForName(name);
+ else
+ codec = TQTextCodec::codecForMib( 106 ); // fallback to UTF-8
+
+ if (codec == 0L)
+ codec = TQTextCodec::codecForMib(1);
+
+ kdDebug() << k_funcinfo << "setting encoding " << codec->name()
+ << " for name=" << name << endl;
+}
+
+void KRemoteEncoding::virtual_hook(int, void*)
+{
+}
diff --git a/tdeio/tdeio/kremoteencoding.h b/tdeio/tdeio/kremoteencoding.h
new file mode 100644
index 000000000..18dfe1fdb
--- /dev/null
+++ b/tdeio/tdeio/kremoteencoding.h
@@ -0,0 +1,127 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2003 Thiago Macieira <thiago.macieira@kdemail.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KREMOTEENCODING_H
+#define KREMOTEENCODING_H
+
+#include <kurl.h>
+#include <tqstring.h>
+#include <tqcstring.h>
+#include <tqtextcodec.h>
+
+class KRemoteEncodingPrivate;
+/**
+ * Allows encoding and decoding properly remote filenames into Unicode.
+ *
+ * Certain protocols do not specify an appropriate encoding for decoding
+ * their 8-bit data into proper Unicode forms. Therefore, ioslaves should
+ * use this class in order to convert those forms into QStrings before
+ * creating the respective TDEIO::UDSEntry. The same is true for decoding
+ * URLs to its components.
+ *
+ * Each TDEIO::SlaveBase has one object of this kind, even if it is not necessary.
+ * It can be accessed through TDEIO::SlaveBase::remoteEncoding.
+ *
+ * @short A class for handling remote filenames
+ * @author Thiago Macieira <thiago.macieira@kdemail.net>
+ * @since 3.3
+ */
+class TDEIO_EXPORT KRemoteEncoding
+{
+public:
+ /**
+ * Constructor.
+ *
+ * Constructs this object to use the given encoding name.
+ * If @p name is a null pointer, the standard encoding will be used.
+ */
+ explicit KRemoteEncoding(const char *name = 0L);
+
+ /**
+ * Destructor
+ */
+ virtual ~KRemoteEncoding();
+
+ /**
+ * Converts the given full pathname or filename to Unicode.
+ * This function is supposed to work for dirnames, filenames
+ * or a full pathname.
+ */
+ TQString decode(const TQCString& name) const;
+
+ /**
+ * Converts the given name from Unicode.
+ * This function is supposed to work for dirnames, filenames
+ * or a full pathname.
+ */
+ TQCString encode(const TQString& name) const;
+
+ /**
+ * Converts the given URL into its 8-bit components
+ */
+ TQCString encode(const KURL& url) const;
+
+ /**
+ * Converts the given URL into 8-bit form and separate the
+ * dirname from the filename. This is useful for slave functions
+ * like stat or get.
+ *
+ * The dirname is returned with the final slash always stripped
+ */
+ TQCString directory(const KURL& url, bool ignore_trailing_slash = true) const;
+
+ /**
+ * Converts the given URL into 8-bit form and retrieve the filename.
+ */
+ TQCString fileName(const KURL& url) const;
+
+ /**
+ * Returns the encoding being used.
+ */
+ inline const char *encoding() const
+ { return codec->name(); }
+
+ /**
+ * Returns the MIB for the codec being used.
+ */
+ inline int encodingMib() const
+ { return codec->mibEnum(); }
+
+ /**
+ * Sets the encoding being used.
+ * This function does not change the global configuration.
+ *
+ * Pass a null pointer in @p name to revert to the standard
+ * encoding.
+ */
+ void setEncoding(const char* name);
+
+protected:
+ TQTextCodec *codec;
+
+ virtual void virtual_hook(int id, void* data);
+
+private:
+ // copy constructor
+ KRemoteEncoding(const KRemoteEncoding&);
+
+
+ KRemoteEncodingPrivate *d;
+};
+
+#endif
diff --git a/tdeio/tdeio/krun.cpp b/tdeio/tdeio/krun.cpp
new file mode 100644
index 000000000..5810bdda4
--- /dev/null
+++ b/tdeio/tdeio/krun.cpp
@@ -0,0 +1,1574 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Torben Weis <weis@kde.org>
+ Copyright (C) 2006 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "krun.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <typeinfo>
+
+#include <tqwidget.h>
+#include <tqguardedptr.h>
+
+#include "kuserprofile.h"
+#include "kmimetype.h"
+#include "kmimemagic.h"
+#include "tdeio/job.h"
+#include "tdeio/global.h"
+#include "tdeio/scheduler.h"
+#include "tdeio/netaccess.h"
+#include "tdefile/kopenwith.h"
+#include "tdefile/krecentdocument.h"
+
+#include <kdatastream.h>
+#include <kmessageboxwrapper.h>
+#include <kurl.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kprotocolinfo.h>
+#include <kstandarddirs.h>
+#include <kprocess.h>
+#include <dcopclient.h>
+#include <tqfile.h>
+#include <tqfileinfo.h>
+#include <tqtextstream.h>
+#include <tqdatetime.h>
+#include <tqregexp.h>
+#include <kdesktopfile.h>
+#include <kstartupinfo.h>
+#include <kmacroexpander.h>
+#include <kshell.h>
+#include <kde_file.h>
+#include <kstringhandler.h>
+
+#ifdef Q_WS_X11
+#include <twin.h>
+#endif
+
+class KRun::KRunPrivate
+{
+public:
+ KRunPrivate() { m_showingError = false; }
+
+ bool m_showingError;
+ bool m_runExecutables;
+
+ TQString m_preferredService;
+ TQString m_externalBrowser;
+ TQString m_localPath;
+ TQString m_suggestedFileName;
+ TQGuardedPtr <TQWidget> m_window;
+ TQCString m_asn;
+};
+
+pid_t KRun::runURL( const KURL& u, const TQString& _mimetype )
+{
+ return runURL( u, _mimetype, false, true, TQString::null );
+}
+
+pid_t KRun::runURL( const KURL& u, const TQString& _mimetype, bool tempFile )
+{
+ return runURL( u, _mimetype, tempFile, true, TQString::null );
+}
+
+pid_t KRun::runURL( const KURL& u, const TQString& _mimetype, bool tempFile, bool runExecutables )
+{
+ return runURL( u, _mimetype, tempFile, runExecutables, TQString::null );
+}
+
+bool KRun::isExecutableFile( const KURL& url, const TQString &mimetype )
+{
+ if ( !url.isLocalFile() )
+ return false;
+ TQFileInfo file( url.path() );
+ if ( file.isExecutable() ) // Got a prospective file to run
+ {
+ KMimeType::Ptr mimeType = KMimeType::mimeType( mimetype );
+
+ if ( mimeType->is("application/x-executable") || mimeType->is("application/x-executable-script") )
+ return true;
+ }
+ return false;
+}
+
+pid_t KRun::runURL( const KURL& u, const TQString& _mimetype, bool tempFile, bool runExecutables, const TQString& suggestedFileName )
+{
+ return runURL( u, _mimetype, NULL, "", tempFile, runExecutables, suggestedFileName );
+}
+
+// This is called by foundMimeType, since it knows the mimetype of the URL
+pid_t KRun::runURL( const KURL& u, const TQString& _mimetype, TQWidget* window, const TQCString& asn,
+ bool tempFile, bool runExecutables, const TQString& suggestedFileName )
+{
+ bool noRun = false;
+ bool noAuth = false;
+ if ( _mimetype == "inode/directory-locked" )
+ {
+ KMessageBoxWrapper::error( window,
+ i18n("<qt>Unable to enter <b>%1</b>.\nYou do not have access rights to this location.</qt>").arg(u.htmlURL()) );
+ return 0;
+ }
+ else if ( (_mimetype == "application/x-desktop") ||
+ (_mimetype == "media/builtin-mydocuments") ||
+ (_mimetype == "media/builtin-mycomputer") ||
+ (_mimetype == "media/builtin-mynetworkplaces") ||
+ (_mimetype == "media/builtin-printers") ||
+ (_mimetype == "media/builtin-trash") ||
+ (_mimetype == "media/builtin-webbrowser") )
+ {
+ if ( u.isLocalFile() && runExecutables )
+ return KDEDesktopMimeType::run( u, true );
+ }
+ else if ( isExecutableFile(u, _mimetype) )
+ {
+ if ( u.isLocalFile() && runExecutables)
+ {
+ if (kapp->authorize("shell_access"))
+ {
+ TQString path = u.path();
+ shellQuote( path );
+ return (KRun::runCommand(path, TQString::null, TQString::null, window, asn)); // just execute the url as a command
+ // ## TODO implement deleting the file if tempFile==true
+ }
+ else
+ {
+ noAuth = true;
+ }
+ }
+ else if (_mimetype == "application/x-executable")
+ noRun = true;
+ }
+ else if ( isExecutable(_mimetype) )
+ {
+ if (!runExecutables)
+ noRun = true;
+
+ if (!kapp->authorize("shell_access"))
+ noAuth = true;
+ }
+
+ if ( noRun )
+ {
+ KMessageBox::sorry( window,
+ i18n("<qt>The file <b>%1</b> is an executable program. "
+ "For safety it will not be started.</qt>").arg(u.htmlURL()));
+ return 0;
+ }
+ if ( noAuth )
+ {
+ KMessageBoxWrapper::error( window,
+ i18n("<qt>You do not have permission to run <b>%1</b>.</qt>").arg(u.htmlURL()) );
+ return 0;
+ }
+
+ KURL::List lst;
+ lst.append( u );
+
+ static const TQString& app_str = TDEGlobal::staticQString("Application");
+
+ KService::Ptr offer = KServiceTypeProfile::preferredService( _mimetype, app_str );
+
+ if ( !offer )
+ {
+ // Open-with dialog
+ // TODO : pass the mimetype as a parameter, to show it (comment field) in the dialog !
+ // Hmm, in fact KOpenWithDlg::setServiceType already guesses the mimetype from the first URL of the list...
+ return displayOpenWithDialog( lst, tempFile, suggestedFileName );
+ }
+
+ return KRun::run( *offer, lst, window, asn, tempFile, suggestedFileName );
+}
+
+bool KRun::displayOpenWithDialog( const KURL::List& lst )
+{
+ return displayOpenWithDialog( lst, false, TQString::null );
+}
+
+bool KRun::displayOpenWithDialog( const KURL::List& lst, bool tempFiles )
+{
+ return displayOpenWithDialog( lst, tempFiles, TQString::null );
+}
+
+bool KRun::displayOpenWithDialog( const KURL::List& lst, bool tempFiles, const TQString& suggestedFileName )
+{
+ if (kapp && !kapp->authorizeKAction("openwith"))
+ {
+ // TODO: Better message, i18n freeze :-(
+ KMessageBox::sorry(0L, i18n("You are not authorized to open this file."));
+ return false;
+ }
+
+ KOpenWithDlg l( lst, i18n("Open with:"), TQString::null, 0L );
+ if ( l.exec() )
+ {
+ KService::Ptr service = l.service();
+ if ( !!service )
+ return KRun::run( *service, lst, 0 /*window*/, tempFiles, suggestedFileName );
+
+ kdDebug(7010) << "No service set, running " << l.text() << endl;
+ return KRun::run( l.text(), lst, suggestedFileName ); // TODO handle tempFiles
+ }
+ return false;
+}
+
+void KRun::shellQuote( TQString &_str )
+{
+ // Credits to Walter, says Bernd G. :)
+ if (_str.isEmpty()) // Don't create an explicit empty parameter
+ return;
+ TQChar q('\'');
+ _str.replace(q, "'\\''").prepend(q).append(q);
+}
+
+
+class KRunMX1 : public KMacroExpanderBase {
+public:
+ KRunMX1( const KService &_service ) :
+ KMacroExpanderBase( '%' ), hasUrls( false ), hasSpec( false ), service( _service ) {}
+ bool hasUrls:1, hasSpec:1;
+
+protected:
+ virtual int expandEscapedMacro( const TQString &str, uint pos, TQStringList &ret );
+
+private:
+ const KService &service;
+};
+
+int
+KRunMX1::expandEscapedMacro( const TQString &str, uint pos, TQStringList &ret )
+{
+ uint option = str[pos + 1];
+ switch( option ) {
+ case 'c':
+ ret << service.name().replace( '%', "%%" );
+ break;
+ case 'k':
+ ret << service.desktopEntryPath().replace( '%', "%%" );
+ break;
+ case 'i':
+ ret << "-icon" << service.icon().replace( '%', "%%" );
+ break;
+ case 'm':
+ ret << "-miniicon" << service.icon().replace( '%', "%%" );
+ break;
+ case 'u':
+ case 'U':
+ hasUrls = true;
+ /* fallthrough */
+ case 'f':
+ case 'F':
+ case 'n':
+ case 'N':
+ case 'd':
+ case 'D':
+ case 'v':
+ hasSpec = true;
+ /* fallthrough */
+ default:
+ return -2; // subst with same and skip
+ }
+ return 2;
+}
+
+class KRunMX2 : public KMacroExpanderBase {
+public:
+ KRunMX2( const KURL::List &_urls ) :
+ KMacroExpanderBase( '%' ), ignFile( false ), urls( _urls ) {}
+ bool ignFile:1;
+
+protected:
+ virtual int expandEscapedMacro( const TQString &str, uint pos, TQStringList &ret );
+
+private:
+ void subst( int option, const KURL &url, TQStringList &ret );
+
+ const KURL::List &urls;
+};
+
+void
+KRunMX2::subst( int option, const KURL &url, TQStringList &ret )
+{
+ switch( option ) {
+ case 'u':
+ ret << url.pathOrURL();
+ break;
+ case 'd':
+ ret << url.directory();
+ break;
+ case 'f':
+ ret << url.path();
+ break;
+ case 'n':
+ ret << url.fileName();
+ break;
+ case 'v':
+ if (url.isLocalFile() && TQFile::exists( url.path() ) )
+ ret << KDesktopFile( url.path(), true ).readEntry( "Dev" );
+ break;
+ }
+ return;
+}
+
+int
+KRunMX2::expandEscapedMacro( const TQString &str, uint pos, TQStringList &ret )
+{
+ uint option = str[pos + 1];
+ switch( option ) {
+ case 'f':
+ case 'u':
+ case 'n':
+ case 'd':
+ case 'v':
+ if( urls.isEmpty() ) {
+ if (!ignFile)
+ kdDebug() << "KRun::processDesktopExec: No URLs supplied to single-URL service " << str << endl;
+ } else if( urls.count() > 1 )
+ kdWarning() << "KRun::processDesktopExec: " << urls.count() << " URLs supplied to single-URL service " << str << endl;
+ else
+ subst( option, urls.first(), ret );
+ break;
+ case 'F':
+ case 'U':
+ case 'N':
+ case 'D':
+ option += 'a' - 'A';
+ for( KURL::List::ConstIterator it = urls.begin(); it != urls.end(); ++it )
+ subst( option, *it, ret );
+ break;
+ case '%':
+ ret = "%";
+ break;
+ default:
+ return -2; // subst with same and skip
+ }
+ return 2;
+}
+
+// BIC: merge methods below
+TQStringList KRun::processDesktopExec(const KService &_service, const KURL::List& _urls, bool has_shell) {
+ return processDesktopExec( _service, _urls, has_shell, false, TQString::null );
+}
+
+TQStringList KRun::processDesktopExec(const KService &_service, const KURL::List& _urls, bool has_shell /* KDE4: remove */, bool tempFiles)
+{
+ return processDesktopExec( _service, _urls, has_shell, tempFiles, TQString::null );
+}
+
+TQStringList KRun::processDesktopExec(const KService &_service, const KURL::List& _urls, bool has_shell /* KDE4: remove */, bool tempFiles, const TQString& suggestedFileName)
+{
+ TQString exec = _service.exec();
+ TQStringList result;
+ bool appHasTempFileOption;
+
+ KRunMX1 mx1( _service );
+ KRunMX2 mx2( _urls );
+
+ /// compatibility hack -- KDE 4: remove
+ TQRegExp re("^\\s*(?:/bin/)?sh\\s+-c\\s+(.*)$");
+ if (!re.search( exec )) {
+ exec = TQString(re.cap( 1 )).stripWhiteSpace();
+ for (uint pos = 0; pos < exec.length(); ) {
+ TQChar c = exec.unicode()[pos];
+ if (c != '\'' && c != '"')
+ goto synerr; // what else can we do? after normal parsing the substs would be insecure
+ int pos2 = exec.find( c, pos + 1 ) - 1;
+ if (pos2 < 0)
+ goto synerr; // quoting error
+ memcpy( (void *)(exec.unicode() + pos), exec.unicode() + pos + 1, (pos2 - pos) * sizeof(TQChar));
+ pos = pos2;
+ exec.remove( pos, 2 );
+ }
+ }
+
+ if( !mx1.expandMacrosShellQuote( exec ) )
+ goto synerr; // error in shell syntax
+
+ // FIXME: the current way of invoking tdeioexec disables term and su use
+
+ // Check if we need "tempexec" (tdeioexec in fact)
+ appHasTempFileOption = tempFiles && _service.property("X-TDE-HasTempFileOption").toBool();
+ if( tempFiles && !appHasTempFileOption && _urls.size() ) {
+ result << "tdeioexec" << "--tempfiles" << exec;
+ result += _urls.toStringList();
+ if (has_shell)
+ result = KShell::joinArgs( result );
+ return result;
+ }
+
+ // Check if we need tdeioexec
+ if( !mx1.hasUrls ) {
+ for( KURL::List::ConstIterator it = _urls.begin(); it != _urls.end(); ++it )
+ if ( !(*it).isLocalFile() && !KProtocolInfo::isHelperProtocol(*it) ) {
+ // We need to run the app through tdeioexec
+ result << "tdeioexec";
+ if ( tempFiles )
+ result << "--tempfiles";
+ if ( !suggestedFileName.isEmpty() ) {
+ result << "--suggestedfilename";
+ result << suggestedFileName;
+ }
+ result << exec;
+ result += _urls.toStringList();
+ if (has_shell)
+ result = KShell::joinArgs( result );
+ return result;
+ }
+ }
+
+ if ( appHasTempFileOption )
+ exec += " --tempfile";
+
+ // Did the user forget to append something like '%f'?
+ // If so, then assume that '%f' is the right choice => the application
+ // accepts only local files.
+ if( !mx1.hasSpec ) {
+ exec += " %f";
+ mx2.ignFile = true;
+ }
+
+ mx2.expandMacrosShellQuote( exec ); // syntax was already checked, so don't check return value
+
+/*
+ 1 = need_shell, 2 = terminal, 4 = su, 8 = has_shell
+
+ 0 << split(cmd)
+ 1 << "sh" << "-c" << cmd
+ 2 << split(term) << "-e" << split(cmd)
+ 3 << split(term) << "-e" << "sh" << "-c" << cmd
+
+ 4 << "tdesu" << "-u" << user << "-c" << cmd
+ 5 << "tdesu" << "-u" << user << "-c" << ("sh -c " + quote(cmd))
+ 6 << split(term) << "-e" << "su" << user << "-c" << cmd
+ 7 << split(term) << "-e" << "su" << user << "-c" << ("sh -c " + quote(cmd))
+
+ 8 << cmd
+ 9 << cmd
+ a << term << "-e" << cmd
+ b << term << "-e" << ("sh -c " + quote(cmd))
+
+ c << "tdesu" << "-u" << user << "-c" << quote(cmd)
+ d << "tdesu" << "-u" << user << "-c" << quote("sh -c " + quote(cmd))
+ e << term << "-e" << "su" << user << "-c" << quote(cmd)
+ f << term << "-e" << "su" << user << "-c" << quote("sh -c " + quote(cmd))
+
+ "sh -c" is needed in the "su" case, too, as su uses the user's login shell, not sh.
+ this could be optimized with the -s switch of some su versions (e.g., debian linux).
+*/
+
+ if (_service.terminal()) {
+ TDEConfigGroupSaver gs(TDEGlobal::config(), "General");
+ TQString terminal = TDEGlobal::config()->readPathEntry("TerminalApplication", "konsole");
+ if (terminal == "konsole")
+ terminal += " -caption=%c %i %m";
+ terminal += " ";
+ terminal += _service.terminalOptions();
+ if( !mx1.expandMacrosShellQuote( terminal ) ) {
+ kdWarning() << "KRun: syntax error in command `" << terminal << "', service `" << _service.name() << "'" << endl;
+ return TQStringList();
+ }
+ mx2.expandMacrosShellQuote( terminal );
+ if (has_shell)
+ result << terminal;
+ else
+ result = KShell::splitArgs( terminal ); // assuming that the term spec never needs a shell!
+ result << "-e";
+ }
+
+ int err;
+ if (_service.substituteUid()) {
+ if (_service.terminal())
+ result << "su";
+ else
+ result << "tdesu" << "-u";
+ result << _service.username() << "-c";
+ KShell::splitArgs(exec, KShell::AbortOnMeta | KShell::TildeExpand, &err);
+ if (err == KShell::FoundMeta) {
+ shellQuote( exec );
+ exec.prepend( "/bin/sh -c " );
+ } else if (err != KShell::NoError)
+ goto synerr;
+ if (has_shell)
+ shellQuote( exec );
+ result << exec;
+ } else {
+ if (has_shell) {
+ if (_service.terminal()) {
+ KShell::splitArgs(exec, KShell::AbortOnMeta | KShell::TildeExpand, &err);
+ if (err == KShell::FoundMeta) {
+ shellQuote( exec );
+ exec.prepend( "/bin/sh -c " );
+ } else if (err != KShell::NoError)
+ goto synerr;
+ }
+ result << exec;
+ } else {
+ result += KShell::splitArgs(exec, KShell::AbortOnMeta | KShell::TildeExpand, &err);
+ if (err == KShell::FoundMeta)
+ result << "/bin/sh" << "-c" << exec;
+ else if (err != KShell::NoError)
+ goto synerr;
+ }
+ }
+
+ return result;
+
+ synerr:
+ kdWarning() << "KRun: syntax error in command `" << _service.exec() << "', service `" << _service.name() << "'" << endl;
+ return TQStringList();
+}
+
+//static
+TQString KRun::binaryName( const TQString & execLine, bool removePath )
+{
+ // Remove parameters and/or trailing spaces.
+ TQStringList args = KShell::splitArgs( execLine );
+ for (TQStringList::ConstIterator it = args.begin(); it != args.end(); ++it)
+ if (!(*it).contains('='))
+ // Remove path if wanted
+ return removePath ? (*it).mid(TQString(*it).findRev('/') + 1) : *it;
+ return TQString();
+}
+
+static pid_t runCommandInternal( TDEProcess* proc, const KService* service, const TQString& binName,
+ const TQString &execName, const TQString & iconName, TQWidget* window, TQCString asn )
+{
+ if (service && !service->desktopEntryPath().isEmpty()
+ && !KDesktopFile::isAuthorizedDesktopFile( service->desktopEntryPath() ))
+ {
+ kdWarning() << "No authorization to execute " << service->desktopEntryPath() << endl;
+ KMessageBox::sorry(window, i18n("You are not authorized to execute this file."));
+ return 0;
+ }
+ TQString bin = KRun::binaryName( binName, true );
+#ifdef Q_WS_X11 // Startup notification doesn't work with QT/E, service isn't needed without Startup notification
+ bool silent;
+ TQCString wmclass;
+ KStartupInfoId id;
+ bool startup_notify = ( asn != "0" && KRun::checkStartupNotify( binName, service, &silent, &wmclass ));
+ if( startup_notify )
+ {
+ id.initId( asn );
+ id.setupStartupEnv();
+ KStartupInfoData data;
+ data.setHostname();
+ data.setBin( bin );
+ if( !execName.isEmpty())
+ data.setName( execName );
+ else if( service && !service->name().isEmpty())
+ data.setName( service->name());
+ data.setDescription( i18n( "Launching %1" ).arg( data.name()));
+ if( !iconName.isEmpty())
+ data.setIcon( iconName );
+ else if( service && !service->icon().isEmpty())
+ data.setIcon( service->icon());
+ if( !wmclass.isEmpty())
+ data.setWMClass( wmclass );
+ if( silent )
+ data.setSilent( KStartupInfoData::Yes );
+ data.setDesktop( KWin::currentDesktop());
+ if( window )
+ data.setLaunchedBy( window->winId());
+ KStartupInfo::sendStartup( id, data );
+ }
+ pid_t pid = TDEProcessRunner::run( proc, binName, id );
+ if( startup_notify && pid )
+ {
+ KStartupInfoData data;
+ data.addPid( pid );
+ KStartupInfo::sendChange( id, data );
+ KStartupInfo::resetStartupEnv();
+ }
+ return pid;
+#else
+ Q_UNUSED( execName );
+ Q_UNUSED( iconName );
+ return TDEProcessRunner::run( proc, bin );
+#endif
+}
+
+// This code is also used in tdelauncher.
+bool KRun::checkStartupNotify( const TQString& /*binName*/, const KService* service, bool* silent_arg, TQCString* wmclass_arg )
+{
+ bool silent = false;
+ TQCString wmclass;
+ if( service && service->property( "StartupNotify" ).isValid())
+ {
+ silent = !service->property( "StartupNotify" ).toBool();
+ wmclass = service->property( "StartupWMClass" ).toString().latin1();
+ }
+ else if( service && service->property( "X-TDE-StartupNotify" ).isValid())
+ {
+ silent = !service->property( "X-TDE-StartupNotify" ).toBool();
+ wmclass = service->property( "X-TDE-WMClass" ).toString().latin1();
+ }
+ else // non-compliant app
+ {
+ if( service )
+ {
+ if( service->type() == "Application" )
+ wmclass = "0"; // doesn't have .desktop entries needed, start as non-compliant
+ else
+ return false; // no startup notification at all
+ }
+ else
+ {
+#if 0
+ // Create startup notification even for apps for which there shouldn't be any,
+ // just without any visual feedback. This will ensure they'll be positioned on the proper
+ // virtual desktop, and will get user timestamp from the ASN ID.
+ wmclass = "0";
+ silent = true;
+#else // That unfortunately doesn't work, when the launched non-compliant application
+ // launches another one that is compliant and there is any delay inbetween (bnc:#343359)
+ return false;
+#endif
+ }
+ }
+ if( silent_arg != NULL )
+ *silent_arg = silent;
+ if( wmclass_arg != NULL )
+ *wmclass_arg = wmclass;
+ return true;
+}
+
+static pid_t runTempService( const KService& _service, const KURL::List& _urls, TQWidget* window,
+ const TQCString& asn, bool tempFiles, const TQString& suggestedFileName )
+{
+ if (!_urls.isEmpty()) {
+ kdDebug(7010) << "runTempService: first url " << _urls.first().url() << endl;
+ }
+
+ TQStringList args;
+ if ((_urls.count() > 1) && !_service.allowMultipleFiles())
+ {
+ // We need to launch the application N times. That sucks.
+ // We ignore the result for application 2 to N.
+ // For the first file we launch the application in the
+ // usual way. The reported result is based on this
+ // application.
+ KURL::List::ConstIterator it = _urls.begin();
+ while(++it != _urls.end())
+ {
+ KURL::List singleUrl;
+ singleUrl.append(*it);
+ runTempService( _service, singleUrl, window, "", tempFiles, suggestedFileName );
+ }
+ KURL::List singleUrl;
+ singleUrl.append(_urls.first());
+ args = KRun::processDesktopExec(_service, singleUrl, false, tempFiles, suggestedFileName);
+ }
+ else
+ {
+ args = KRun::processDesktopExec(_service, _urls, false, tempFiles, suggestedFileName);
+ }
+ kdDebug(7010) << "runTempService: TDEProcess args=" << args << endl;
+
+ TDEProcess * proc = new TDEProcess;
+ *proc << args;
+
+ if (!_service.path().isEmpty())
+ proc->setWorkingDirectory(_service.path());
+
+ return runCommandInternal( proc, &_service, KRun::binaryName( _service.exec(), false ),
+ _service.name(), _service.icon(), window, asn );
+}
+
+// WARNING: don't call this from processDesktopExec, since tdelauncher uses that too...
+static KURL::List resolveURLs( const KURL::List& _urls, const KService& _service )
+{
+ // Check which protocols the application supports.
+ // This can be a list of actual protocol names, or just KIO for KDE apps.
+ TQStringList supportedProtocols = _service.property("X-TDE-Protocols").toStringList();
+ KRunMX1 mx1( _service );
+ TQString exec = _service.exec();
+ if ( mx1.expandMacrosShellQuote( exec ) && !mx1.hasUrls ) {
+ Q_ASSERT( supportedProtocols.isEmpty() ); // huh? If you support protocols you need %u or %U...
+ } else {
+ if ( supportedProtocols.isEmpty() )
+ {
+ // compat mode: assume KIO if not set and it's a KDE app
+ TQStringList categories = _service.property("Categories").toStringList();
+ if (( categories.find("TDE") != categories.end() ) && ( categories.find("KDE") != categories.end() ))
+ supportedProtocols.append( "KIO" );
+ else { // if no KDE app, be a bit over-generic
+ supportedProtocols.append( "http");
+ supportedProtocols.append( "ftp");
+ }
+ }
+ }
+ kdDebug(7010) << "supportedProtocols:" << supportedProtocols << endl;
+
+ KURL::List urls( _urls );
+ if ( supportedProtocols.find( "KIO" ) == supportedProtocols.end() ) {
+ for( KURL::List::Iterator it = urls.begin(); it != urls.end(); ++it ) {
+ const KURL url = *it;
+ bool supported = url.isLocalFile() || supportedProtocols.find( url.protocol().lower() ) != supportedProtocols.end();
+ kdDebug(7010) << "Looking at url=" << url << " supported=" << supported << endl;
+ if ( !supported && KProtocolInfo::protocolClass(url.protocol()) == ":local" )
+ {
+ // Maybe we can resolve to a local URL?
+ KURL localURL = TDEIO::NetAccess::mostLocalURL( url, 0 );
+ if ( localURL != url ) {
+ *it = localURL;
+ kdDebug(7010) << "Changed to " << localURL << endl;
+ }
+ }
+ }
+ }
+ return urls;
+}
+
+// BIC merge methods below
+pid_t KRun::run( const KService& _service, const KURL::List& _urls )
+{
+ return run( _service, _urls, 0, false, TQString::null );
+}
+
+pid_t KRun::run( const KService& _service, const KURL::List& _urls, bool tempFiles )
+{
+ return run( _service, _urls, 0, tempFiles, TQString::null );
+}
+
+pid_t KRun::run( const KService& _service, const KURL::List& _urls, TQWidget* window, bool tempFiles )
+{
+ return run( _service, _urls, window, "", tempFiles, TQString::null );
+}
+
+pid_t KRun::run( const KService& _service, const KURL::List& _urls, TQWidget* window, const TQCString& asn, bool tempFiles )
+{
+ return run( _service, _urls, window, asn, tempFiles, TQString::null );
+}
+
+pid_t KRun::run( const KService& _service, const KURL::List& _urls, TQWidget* window, bool tempFiles, const TQString& suggestedFileName )
+{
+ return run( _service, _urls, window, "", tempFiles, suggestedFileName );
+}
+
+pid_t KRun::run( const KService& _service, const KURL::List& _urls, TQWidget* window, const TQCString& asn,
+ bool tempFiles, const TQString& suggestedFileName )
+{
+ if (!_service.desktopEntryPath().isEmpty() &&
+ !KDesktopFile::isAuthorizedDesktopFile( _service.desktopEntryPath()))
+ {
+ kdWarning() << "No authorization to execute " << _service.desktopEntryPath() << endl;
+ KMessageBox::sorry(window, i18n("You are not authorized to execute this service."));
+ return 0;
+ }
+
+ if ( !tempFiles )
+ {
+ // Remember we opened those urls, for the "recent documents" menu in kicker
+ KURL::List::ConstIterator it = _urls.begin();
+ for(; it != _urls.end(); ++it) {
+ //kdDebug(7010) << "KRecentDocument::adding " << (*it).url() << endl;
+ KRecentDocument::add( *it, _service.desktopEntryName() );
+ }
+ }
+
+ if ( tempFiles || _service.desktopEntryPath().isEmpty() || !suggestedFileName.isEmpty() )
+ {
+ return runTempService(_service, _urls, window, asn, tempFiles, suggestedFileName);
+ }
+
+ kdDebug(7010) << "KRun::run " << _service.desktopEntryPath() << endl;
+
+ if (!_urls.isEmpty()) {
+ kdDebug(7010) << "First url " << _urls.first().url() << endl;
+ }
+
+ // Resolve urls if needed, depending on what the app supports
+ const KURL::List urls = resolveURLs( _urls, _service );
+
+ TQString error;
+ int pid = 0;
+
+ TQCString myasn = asn;
+ // startServiceByDesktopPath() doesn't take TQWidget*, add it to the startup info now
+ if( window != NULL )
+ {
+ if( myasn.isEmpty())
+ myasn = KStartupInfo::createNewStartupId();
+ if( myasn != "0" )
+ {
+ KStartupInfoId id;
+ id.initId( myasn );
+ KStartupInfoData data;
+ data.setLaunchedBy( window->winId());
+ KStartupInfo::sendChange( id, data );
+ }
+ }
+
+ int i = TDEApplication::startServiceByDesktopPath(
+ _service.desktopEntryPath(), urls.toStringList(), &error, 0L, &pid, myasn
+ );
+
+ if (i != 0)
+ {
+ kdDebug(7010) << error << endl;
+ KMessageBox::sorry( window, error );
+ return 0;
+ }
+
+ kdDebug(7010) << "startServiceByDesktopPath worked fine" << endl;
+ return (pid_t) pid;
+}
+
+
+pid_t KRun::run( const TQString& _exec, const KURL::List& _urls, const TQString& _name,
+ const TQString& _icon, const TQString&, const TQString&)
+{
+ KService::Ptr service = new KService(_name, _exec, _icon);
+
+ return run(*service, _urls);
+}
+
+pid_t KRun::runCommand( TQString cmd )
+{
+ return KRun::runCommand( cmd, TQString::null, TQString::null, NULL, "" );
+}
+
+pid_t KRun::runCommand( const TQString& cmd, const TQString &execName, const TQString & iconName )
+{
+ return KRun::runCommand( cmd, execName, iconName, NULL, "" );
+}
+
+pid_t KRun::runCommand( const TQString& cmd, const TQString &execName, const TQString & iconName,
+ TQWidget* window, const TQCString& asn )
+{
+ kdDebug(7010) << "runCommand " << cmd << "," << execName << endl;
+ TDEProcess * proc = new TDEProcess;
+ proc->setUseShell(true);
+ *proc << cmd;
+ KService::Ptr service = KService::serviceByDesktopName( binaryName( execName, true ) );
+ TQString bin = binaryName( cmd, false );
+ int pos = bin.findRev( '/' );
+ if (pos != -1) {
+ proc->setWorkingDirectory( bin.mid(0, pos) );
+ }
+ return runCommandInternal( proc, service.data(), binaryName( execName, false ), execName, iconName, window, asn );
+}
+
+KRun::KRun( const KURL& url, mode_t mode, bool isLocalFile, bool showProgressInfo )
+ :m_timer(0,"KRun::timer")
+{
+ init (url, 0, "", mode, isLocalFile, showProgressInfo);
+}
+
+KRun::KRun( const KURL& url, TQWidget* window, mode_t mode, bool isLocalFile,
+ bool showProgressInfo )
+ :m_timer(0,"KRun::timer")
+{
+ init (url, window, "", mode, isLocalFile, showProgressInfo);
+}
+
+KRun::KRun( const KURL& url, TQWidget* window, const TQCString& asn, mode_t mode, bool isLocalFile,
+ bool showProgressInfo )
+ :m_timer(0,"KRun::timer")
+{
+ init (url, window, asn, mode, isLocalFile, showProgressInfo);
+}
+
+void KRun::init ( const KURL& url, TQWidget* window, const TQCString& asn, mode_t mode, bool isLocalFile,
+ bool showProgressInfo )
+{
+ m_bFault = false;
+ m_bAutoDelete = true;
+ m_bProgressInfo = showProgressInfo;
+ m_bFinished = false;
+ m_job = 0L;
+ m_strURL = url;
+ m_bScanFile = false;
+ m_bIsDirectory = false;
+ m_bIsLocalFile = isLocalFile;
+ m_mode = mode;
+ d = new KRunPrivate;
+ d->m_runExecutables = true;
+ d->m_window = window;
+ d->m_asn = asn;
+ setEnableExternalBrowser(true);
+
+ // Start the timer. This means we will return to the event
+ // loop and do initialization afterwards.
+ // Reason: We must complete the constructor before we do anything else.
+ m_bInit = true;
+ connect( &m_timer, TQT_SIGNAL( timeout() ), this, TQT_SLOT( slotTimeout() ) );
+ m_timer.start( 0, true );
+ kdDebug(7010) << " new KRun " << this << " " << url.prettyURL() << " timer=" << &m_timer << endl;
+
+ kapp->ref();
+}
+
+void KRun::init()
+{
+ kdDebug(7010) << "INIT called" << endl;
+
+ bool bypassErrorMessage = false;
+
+ if (m_strURL.url().startsWith("$(")) {
+ // check for environment variables and make necessary translations
+ TQString aValue = m_strURL.url();
+ int nDollarPos = aValue.find( '$' );
+
+ while( nDollarPos != -1 && nDollarPos+1 < static_cast<int>(aValue.length())) {
+ // there is at least one $
+ if( (aValue)[nDollarPos+1] == '(' ) {
+ uint nEndPos = nDollarPos+1;
+ // the next character is no $
+ while ( (nEndPos <= aValue.length()) && (aValue[nEndPos]!=')') )
+ nEndPos++;
+ nEndPos++;
+ TQString cmd = aValue.mid( nDollarPos+2, nEndPos-nDollarPos-3 );
+
+ TQString result;
+ FILE *fs = popen(TQFile::encodeName(cmd).data(), "r");
+ if (fs)
+ {
+ {
+ TQTextStream ts(fs, IO_ReadOnly);
+ result = ts.read().stripWhiteSpace();
+ }
+ pclose(fs);
+ }
+ aValue.replace( nDollarPos, nEndPos-nDollarPos, result );
+ } else if( (aValue)[nDollarPos+1] != '$' ) {
+ uint nEndPos = nDollarPos+1;
+ // the next character is no $
+ TQString aVarName;
+ if (aValue[nEndPos]=='{')
+ {
+ while ( (nEndPos <= aValue.length()) && (aValue[nEndPos]!='}') )
+ nEndPos++;
+ nEndPos++;
+ aVarName = aValue.mid( nDollarPos+2, nEndPos-nDollarPos-3 );
+ }
+ else
+ {
+ while ( nEndPos <= aValue.length() && (aValue[nEndPos].isNumber()
+ || aValue[nEndPos].isLetter() || aValue[nEndPos]=='_' ) )
+ nEndPos++;
+ aVarName = aValue.mid( nDollarPos+1, nEndPos-nDollarPos-1 );
+ }
+ const char* pEnv = 0;
+ if (!aVarName.isEmpty())
+ pEnv = getenv( aVarName.ascii() );
+ if( pEnv ) {
+ // !!! Sergey A. Sukiyazov <corwin@micom.don.ru> !!!
+ // A environment variables may contain values in 8bit
+ // locale cpecified encoding or in UTF8 encoding.
+ aValue.replace( nDollarPos, nEndPos-nDollarPos, KStringHandler::from8Bit( pEnv ) );
+ } else
+ aValue.remove( nDollarPos, nEndPos-nDollarPos );
+ } else {
+ // remove one of the dollar signs
+ aValue.remove( nDollarPos, 1 );
+ nDollarPos++;
+ }
+ nDollarPos = aValue.find( '$', nDollarPos );
+ }
+ m_strURL = KURL(aValue);
+ bypassErrorMessage = true;
+ }
+
+ if ( !m_strURL.isValid() )
+ {
+ if (bypassErrorMessage == false) {
+ d->m_showingError = true;
+ KMessageBoxWrapper::error( d->m_window, i18n( "Malformed URL\n%1" ).arg( m_strURL.url() ) );
+ d->m_showingError = false;
+ }
+ m_bFault = true;
+ m_bFinished = true;
+ m_timer.start( 0, true );
+ return;
+ }
+ if ( !kapp->authorizeURLAction( "open", KURL(), m_strURL))
+ {
+ TQString msg = TDEIO::buildErrorString(TDEIO::ERR_ACCESS_DENIED, m_strURL.prettyURL());
+ d->m_showingError = true;
+ KMessageBoxWrapper::error( d->m_window, msg );
+ d->m_showingError = false;
+ m_bFault = true;
+ m_bFinished = true;
+ m_timer.start( 0, true );
+ return;
+ }
+
+ if ( !m_bIsLocalFile && m_strURL.isLocalFile() )
+ m_bIsLocalFile = true;
+
+ TQString exec;
+ if (m_strURL.protocol().startsWith("http"))
+ {
+ exec = d->m_externalBrowser;
+ }
+
+ if ( m_bIsLocalFile )
+ {
+ if ( m_mode == 0 )
+ {
+ KDE_struct_stat buff;
+ if ( KDE_stat( TQFile::encodeName(m_strURL.path()), &buff ) == -1 )
+ {
+ d->m_showingError = true;
+ KMessageBoxWrapper::error( d->m_window, i18n( "<qt>Unable to run the command specified. The file or folder <b>%1</b> does not exist.</qt>" ).arg( m_strURL.htmlURL() ) );
+ d->m_showingError = false;
+ m_bFault = true;
+ m_bFinished = true;
+ m_timer.start( 0, true );
+ return;
+ }
+ m_mode = buff.st_mode;
+ }
+
+ KMimeType::Ptr mime = KMimeType::findByURL( m_strURL, m_mode, m_bIsLocalFile );
+ assert( mime != 0L );
+ kdDebug(7010) << "MIME TYPE is " << mime->name() << endl;
+ foundMimeType( mime->name() );
+ return;
+ }
+ else if ( !exec.isEmpty() || KProtocolInfo::isHelperProtocol( m_strURL ) ) {
+ kdDebug(7010) << "Helper protocol" << endl;
+
+ bool ok = false;
+ KURL::List urls;
+ if (!((m_strURL.protocol().startsWith("http")) && (m_strURL.url() == "http://default.browser")))
+ urls.append( m_strURL );
+ if (exec.isEmpty())
+ {
+ exec = KProtocolInfo::exec( m_strURL.protocol() );
+ if (exec.isEmpty())
+ {
+ foundMimeType(KProtocolInfo::defaultMimetype(m_strURL));
+ return;
+ }
+ run( exec, urls );
+ ok = true;
+ }
+ else if (exec.startsWith("!"))
+ {
+ exec = exec.mid(1); // Literal command
+ exec += " %u";
+ run( exec, urls );
+ ok = true;
+ }
+ else
+ {
+ KService::Ptr service = KService::serviceByStorageId( exec );
+ if (service)
+ {
+ run( *service, urls, d->m_window, d->m_asn );
+ ok = true;
+ }
+ }
+
+ if (ok)
+ {
+ m_bFinished = true;
+ // will emit the error and autodelete this
+ m_timer.start( 0, true );
+ return;
+ }
+ }
+
+ if ((m_strURL.protocol().startsWith("http")) && (m_strURL.url() == "http://default.browser")) {
+ KURL::List urls;
+ run( "kfmclient openProfile webbrowsing", urls );
+ m_bFinished = true;
+ // will emit the error and autodelete this
+ m_timer.start( 0, true );
+ return;
+ }
+
+ // Did we already get the information that it is a directory ?
+ if ( S_ISDIR( m_mode ) )
+ {
+ foundMimeType( "inode/directory" );
+ return;
+ }
+
+ // Let's see whether it is a directory
+
+ if ( !KProtocolInfo::supportsListing( m_strURL ) )
+ {
+ //kdDebug(7010) << "Protocol has no support for listing" << endl;
+ // No support for listing => it can't be a directory (example: http)
+ scanFile();
+ return;
+ }
+
+ kdDebug(7010) << "Testing directory (stating)" << endl;
+
+ // It may be a directory or a file, let's stat
+ TDEIO::StatJob *job = TDEIO::stat( m_strURL, true, 0 /* no details */, m_bProgressInfo );
+ job->setWindow (d->m_window);
+ connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ),
+ this, TQT_SLOT( slotStatResult( TDEIO::Job * ) ) );
+ m_job = job;
+ kdDebug(7010) << " Job " << job << " is about stating " << m_strURL.url() << endl;
+}
+
+KRun::~KRun()
+{
+ kdDebug(7010) << "KRun::~KRun() " << this << endl;
+ m_timer.stop();
+ killJob();
+ kapp->deref();
+ kdDebug(7010) << "KRun::~KRun() done " << this << endl;
+ delete d;
+}
+
+void KRun::scanFile()
+{
+ kdDebug(7010) << "###### KRun::scanFile " << m_strURL.url() << endl;
+ // First, let's check for well-known extensions
+ // Not when there is a query in the URL, in any case.
+ if ( m_strURL.query().isEmpty() )
+ {
+ KMimeType::Ptr mime = KMimeType::findByURL( m_strURL );
+ assert( mime != 0L );
+ if ( mime->name() != "application/octet-stream" || m_bIsLocalFile )
+ {
+ kdDebug(7010) << "Scanfile: MIME TYPE is " << mime->name() << endl;
+ foundMimeType( mime->name() );
+ return;
+ }
+ }
+
+ // No mimetype found, and the URL is not local (or fast mode not allowed).
+ // We need to apply the 'KIO' method, i.e. either asking the server or
+ // getting some data out of the file, to know what mimetype it is.
+
+ if ( !KProtocolInfo::supportsReading( m_strURL ) )
+ {
+ kdError(7010) << "#### NO SUPPORT FOR READING!" << endl;
+ m_bFault = true;
+ m_bFinished = true;
+ m_timer.start( 0, true );
+ return;
+ }
+ kdDebug(7010) << this << " Scanning file " << m_strURL.url() << endl;
+
+ TDEIO::TransferJob *job = TDEIO::get( m_strURL, false /*reload*/, m_bProgressInfo );
+ job->setWindow (d->m_window);
+ connect(job, TQT_SIGNAL( result(TDEIO::Job *)),
+ this, TQT_SLOT( slotScanFinished(TDEIO::Job *)));
+ connect(job, TQT_SIGNAL( mimetype(TDEIO::Job *, const TQString &)),
+ this, TQT_SLOT( slotScanMimeType(TDEIO::Job *, const TQString &)));
+ m_job = job;
+ kdDebug(7010) << " Job " << job << " is about getting from " << m_strURL.url() << endl;
+}
+
+void KRun::slotTimeout()
+{
+ kdDebug(7010) << this << " slotTimeout called" << endl;
+ if ( m_bInit )
+ {
+ m_bInit = false;
+ init();
+ return;
+ }
+
+ if ( m_bFault ) {
+ emit error();
+ }
+ if ( m_bFinished ) {
+ emit finished();
+ }
+ else
+ {
+ if ( m_bScanFile )
+ {
+ m_bScanFile = false;
+ scanFile();
+ return;
+ }
+ else if ( m_bIsDirectory )
+ {
+ m_bIsDirectory = false;
+ foundMimeType( "inode/directory" );
+ return;
+ }
+ }
+
+ if ( m_bAutoDelete )
+ {
+ delete this;
+ return;
+ }
+}
+
+void KRun::slotStatResult( TDEIO::Job * job )
+{
+ m_job = 0L;
+ if (job->error())
+ {
+ d->m_showingError = true;
+ kdError(7010) << this << " ERROR " << job->error() << " " << job->errorString() << endl;
+ job->showErrorDialog();
+ //kdDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us" << endl;
+ d->m_showingError = false;
+
+ m_bFault = true;
+ m_bFinished = true;
+
+ // will emit the error and autodelete this
+ m_timer.start( 0, true );
+
+ } else {
+
+ kdDebug(7010) << "Finished" << endl;
+ if(!dynamic_cast<TDEIO::StatJob*>(job))
+ kdFatal() << "job is a " << typeid(*job).name() << " should be a StatJob" << endl;
+
+ TQString knownMimeType;
+ TDEIO::UDSEntry entry = ((TDEIO::StatJob*)job)->statResult();
+ TDEIO::UDSEntry::ConstIterator it = entry.begin();
+ for( ; it != entry.end(); it++ ) {
+ switch( (*it).m_uds ) {
+ case TDEIO::UDS_FILE_TYPE:
+ if ( S_ISDIR( (mode_t)((*it).m_long) ) )
+ m_bIsDirectory = true; // it's a dir
+ else
+ m_bScanFile = true; // it's a file
+ break;
+ case TDEIO::UDS_MIME_TYPE: // mimetype already known? (e.g. print:/manager)
+ knownMimeType = (*it).m_str;
+ break;
+ case TDEIO::UDS_LOCAL_PATH:
+ d->m_localPath = (*it).m_str;
+ break;
+ default:
+ break;
+ }
+ }
+ if ( !knownMimeType.isEmpty() )
+ {
+ foundMimeType( knownMimeType );
+ m_bFinished = true;
+ }
+
+ // We should have found something
+ assert ( m_bScanFile || m_bIsDirectory );
+
+ // Start the timer. Once we get the timer event this
+ // protocol server is back in the pool and we can reuse it.
+ // This gives better performance than starting a new slave
+ m_timer.start( 0, true );
+ }
+}
+
+void KRun::slotScanMimeType( TDEIO::Job *, const TQString &mimetype )
+{
+ if ( mimetype.isEmpty() )
+ kdWarning(7010) << "KRun::slotScanFinished : MimetypeJob didn't find a mimetype! Probably a tdeioslave bug." << endl;
+ foundMimeType( mimetype );
+ m_job = 0;
+}
+
+void KRun::slotScanFinished( TDEIO::Job *job )
+{
+ m_job = 0;
+ if (job->error())
+ {
+ d->m_showingError = true;
+ kdError(7010) << this << " ERROR (stat) : " << job->error() << " " << job->errorString() << endl;
+ job->showErrorDialog();
+ //kdDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us" << endl;
+ d->m_showingError = false;
+
+ m_bFault = true;
+ m_bFinished = true;
+
+ // will emit the error and autodelete this
+ m_timer.start( 0, true );
+ }
+}
+
+void KRun::foundMimeType( const TQString& type )
+{
+ kdDebug(7010) << "Resulting mime type is " << type << endl;
+
+/*
+ // Automatically unzip stuff
+
+ // Disabled since the new KIO doesn't have filters yet.
+
+ if ( type == "application/x-gzip" ||
+ type == "application/x-bzip" ||
+ type == "application/x-bzip2" )
+ {
+ KURL::List lst = KURL::split( m_strURL );
+ if ( lst.isEmpty() )
+ {
+ TQString tmp = i18n( "Malformed URL" );
+ tmp += "\n";
+ tmp += m_strURL.url();
+ KMessageBoxWrapper::error( 0L, tmp );
+ return;
+ }
+
+ if ( type == "application/x-gzip" )
+ lst.prepend( KURL( "gzip:/decompress" ) );
+ else if ( type == "application/x-bzip" )
+ lst.prepend( KURL( "bzip:/decompress" ) );
+ else if ( type == "application/x-bzip2" )
+ lst.prepend( KURL( "bzip2:/decompress" ) );
+ else if ( type == "application/x-tar" )
+ lst.prepend( KURL( "tar:/" ) );
+
+ // Move the HTML style reference to the leftmost URL
+ KURL::List::Iterator it = lst.begin();
+ ++it;
+ (*lst.begin()).setRef( (*it).ref() );
+ (*it).setRef( TQString::null );
+
+ // Create the new URL
+ m_strURL = KURL::join( lst );
+
+ kdDebug(7010) << "Now trying with " << debugString(m_strURL.url()) << endl;
+
+ killJob();
+
+ // We don't know if this is a file or a directory. Let's test this first.
+ // (For instance a tar.gz is a directory contained inside a file)
+ // It may be a directory or a file, let's stat
+ TDEIO::StatJob *job = TDEIO::stat( m_strURL, m_bProgressInfo );
+ connect( job, TQT_SIGNAL( result( TDEIO::Job * ) ),
+ this, TQT_SLOT( slotStatResult( TDEIO::Job * ) ) );
+ m_job = job;
+
+ return;
+ }
+*/
+ TDEIO::TransferJob *job = ::tqqt_cast<TDEIO::TransferJob *>( m_job );
+ if ( job )
+ {
+ job->putOnHold();
+ TDEIO::Scheduler::publishSlaveOnHold();
+ m_job = 0;
+ }
+
+ Q_ASSERT( !m_bFinished );
+
+ // Suport for preferred service setting, see setPreferredService
+ if ( !d->m_preferredService.isEmpty() ) {
+ kdDebug(7010) << "Attempting to open with preferred service: " << d->m_preferredService << endl;
+ KService::Ptr serv = KService::serviceByDesktopName( d->m_preferredService );
+ if ( serv && serv->hasServiceType( type ) )
+ {
+ KURL::List lst;
+ lst.append( m_strURL );
+ m_bFinished = KRun::run( *serv, lst, d->m_window, d->m_asn );
+ /// Note: the line above means that if that service failed, we'll
+ /// go to runURL to maybe find another service, even though a dialog
+ /// box was displayed. That's good if runURL tries another service,
+ /// but it's not good if it tries the same one :}
+ }
+ }
+
+ // Resolve .desktop files from media:/, remote:/, applications:/ etc.
+ if ( ((type == "application/x-desktop") ||
+ (type == "media/builtin-mydocuments") ||
+ (type == "media/builtin-mycomputer") ||
+ (type == "media/builtin-mynetworkplaces") ||
+ (type == "media/builtin-printers") ||
+ (type == "media/builtin-trash") ||
+ (type == "media/builtin-webbrowser")) /* or inheriting? */ && (!d->m_localPath.isEmpty()) )
+ {
+ m_strURL = KURL();
+ m_strURL.setPath( d->m_localPath );
+ }
+
+ if (!m_bFinished && KRun::runURL( m_strURL, type, d->m_window, d->m_asn, false, d->m_runExecutables, d->m_suggestedFileName )){
+ m_bFinished = true;
+ }
+ else{
+ m_bFinished = true;
+ m_bFault = true;
+ }
+
+ m_timer.start( 0, true );
+}
+
+void KRun::killJob()
+{
+ if ( m_job )
+ {
+ kdDebug(7010) << "KRun::killJob run=" << this << " m_job=" << m_job << endl;
+ m_job->kill();
+ m_job = 0L;
+ }
+}
+
+void KRun::abort()
+{
+ kdDebug(7010) << "KRun::abort " << this << " m_showingError=" << d->m_showingError << endl;
+ killJob();
+ // If we're showing an error message box, the rest will be done
+ // after closing the msgbox -> don't autodelete nor emit signals now.
+ if ( d->m_showingError )
+ return;
+ m_bFault = true;
+ m_bFinished = true;
+ m_bInit = false;
+ m_bScanFile = false;
+
+ // will emit the error and autodelete this
+ m_timer.start( 0, true );
+}
+
+void KRun::setEnableExternalBrowser(bool b)
+{
+ if (b)
+ d->m_externalBrowser = TDEConfigGroup(TDEGlobal::config(), "General").readEntry("BrowserApplication");
+ else
+ d->m_externalBrowser = TQString::null;
+}
+
+void KRun::setPreferredService( const TQString& desktopEntryName )
+{
+ d->m_preferredService = desktopEntryName;
+}
+
+void KRun::setRunExecutables(bool b)
+{
+ d->m_runExecutables = b;
+}
+
+void KRun::setSuggestedFileName( const TQString& fileName )
+{
+ d->m_suggestedFileName = fileName;
+}
+
+bool KRun::isExecutable( const TQString& serviceType )
+{
+ return ( serviceType == "application/x-desktop" ||
+ serviceType == "media/builtin-mydocuments" ||
+ serviceType == "media/builtin-mycomputer" ||
+ serviceType == "media/builtin-mynetworkplaces" ||
+ serviceType == "media/builtin-printers" ||
+ serviceType == "media/builtin-trash" ||
+ serviceType == "media/builtin-webbrowser" ||
+ serviceType == "application/x-executable" ||
+ serviceType == "application/x-msdos-program" ||
+ serviceType == "application/x-shellscript" );
+}
+
+/****************/
+
+pid_t
+TDEProcessRunner::run(TDEProcess * p, const TQString & binName)
+{
+ return (new TDEProcessRunner(p, binName))->pid();
+}
+
+#ifdef Q_WS_X11
+pid_t
+TDEProcessRunner::run(TDEProcess * p, const TQString & binName, const KStartupInfoId& id )
+{
+ return (new TDEProcessRunner(p, binName, id))->pid();
+}
+#endif
+
+TDEProcessRunner::TDEProcessRunner(TDEProcess * p, const TQString & _binName )
+ : TQObject(),
+ process_(p),
+ binName( _binName )
+{
+ TQObject::connect(
+ process_, TQT_SIGNAL(processExited(TDEProcess *)),
+ this, TQT_SLOT(slotProcessExited(TDEProcess *)));
+
+ process_->start();
+ if ( !process_->pid() )
+ slotProcessExited( process_ );
+}
+
+#ifdef Q_WS_X11
+TDEProcessRunner::TDEProcessRunner(TDEProcess * p, const TQString & _binName, const KStartupInfoId& id )
+ : TQObject(),
+ process_(p),
+ binName( _binName ),
+ id_( id )
+{
+ TQObject::connect(
+ process_, TQT_SIGNAL(processExited(TDEProcess *)),
+ this, TQT_SLOT(slotProcessExited(TDEProcess *)));
+
+ process_->start();
+ if ( !process_->pid() )
+ slotProcessExited( process_ );
+}
+#endif
+
+TDEProcessRunner::~TDEProcessRunner()
+{
+ delete process_;
+}
+
+ pid_t
+TDEProcessRunner::pid() const
+{
+ return process_->pid();
+}
+
+ void
+TDEProcessRunner::slotProcessExited(TDEProcess * p)
+{
+ if (p != process_)
+ return; // Eh ?
+
+ kdDebug(7010) << "slotProcessExited " << binName << endl;
+ kdDebug(7010) << "normalExit " << process_->normalExit() << endl;
+ kdDebug(7010) << "exitStatus " << process_->exitStatus() << endl;
+ bool showErr = process_->normalExit()
+ && ( process_->exitStatus() == 127 || process_->exitStatus() == 1 );
+ if ( !binName.isEmpty() && ( showErr || process_->pid() == 0 ) )
+ {
+ // Often we get 1 (zsh, csh) or 127 (ksh, bash) because the binary doesn't exist.
+ // We can't just rely on that, but it's a good hint.
+ // Before assuming its really so, we'll try to find the binName
+ // relatively to current directory, and then in the PATH.
+ if ( !TQFile( binName ).exists() && KStandardDirs::findExe( binName ).isEmpty() )
+ {
+ kapp->ref();
+ KMessageBox::sorry( 0L, i18n("Could not find the program '%1'").arg( binName ) );
+ kapp->deref();
+ }
+ }
+#ifdef Q_WS_X11
+ if( !id_.none())
+ {
+ KStartupInfoData data;
+ data.addPid( pid()); // announce this pid for the startup notification has finished
+ data.setHostname();
+ KStartupInfo::sendFinish( id_, data );
+ }
+#endif
+ deleteLater();
+}
+
+void KRun::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+#include "krun.moc"
diff --git a/tdeio/tdeio/krun.h b/tdeio/tdeio/krun.h
new file mode 100644
index 000000000..03186af85
--- /dev/null
+++ b/tdeio/tdeio/krun.h
@@ -0,0 +1,512 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ Copyright (C) 2006 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __k_run_h__
+#define __k_run_h__
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <tqobject.h>
+#include <tqtimer.h>
+#include <tqstring.h>
+#include <kurl.h>
+#include <kstartupinfo.h>
+
+class TDEProcess;
+class KService;
+namespace TDEIO {
+ class Job;
+ class StatJob;
+}
+
+/**
+ * To open files with their associated applications in KDE, use KRun.
+ *
+ * It can execute any desktop entry, as well as any file, using
+ * the default application or another application "bound" to the file type
+ * (or URL protocol).
+ *
+ * In that example, the mimetype of the file is not known by the application,
+ * so a KRun instance must be created. It will determine the mimetype by itself.
+ * If the mimetype is known, or if you even know the service (application) to
+ * use for this file, use one of the static methods.
+ *
+ * By default KRun uses auto deletion. It causes the KRun instance to delete
+ * itself when the it finished its task. If you allocate the KRun
+ * object on the stack you must disable auto deletion, otherwise it will crash.
+ *
+ * @short Opens files with their associated applications in KDE
+ */
+class TDEIO_EXPORT KRun : public TQObject
+{
+ Q_OBJECT
+public:
+ /**
+ * Create a KRun object to run the preferred application for a file/URL.
+ * KRun will first determine the type of the file, and will then
+ * run the associated application.
+ *
+ * @param url the URL of the file or directory to 'run'
+ *
+ * @param mode The @p st_mode field of <tt>struct stat</tt>. If
+ * you don't know this set it to 0.
+ *
+ * @param isLocalFile
+ * If this parameter is set to @p false then @p url is
+ * examined to find out whether it is a local URL or
+ * not. This flag is just used to improve speed, since the
+ * function KURL::isLocalFile is a bit slow.
+ *
+ * @param showProgressInfo
+ * Whether to show progress information when determining the
+ * type of the file (i.e. when using TDEIO::stat and TDEIO::mimetype)
+ * Before you set this to false to avoid a dialog box, think about
+ * a very slow FTP server...
+ * It is always better to provide progress info in such cases.
+ */
+ KRun( const KURL& url, mode_t mode = 0,
+ bool isLocalFile = false, bool showProgressInfo = true );
+
+ /**
+ * BIC: Combine with the above ctor for KDE 4.0.
+ * @param window
+ * The top-level widget of the app that invoked this object.
+ * It is used to make sure private information like passwords
+ * are properly handled per application.
+ * @param url the URL of the file or directory to 'run'
+ *
+ * @param mode The @p st_mode field of <tt>struct stat</tt>. If
+ * you don't know this set it to 0.
+ *
+ * @param isLocalFile
+ * If this parameter is set to @p false then @p url is
+ * examined to find out whether it is a local URL or
+ * not. This flag is just used to improve speed, since the
+ * function KURL::isLocalFile is a bit slow.
+ *
+ * @param showProgressInfo
+ * Whether to show progress information when determining the
+ * type of the file (i.e. when using TDEIO::stat and TDEIO::mimetype)
+ * Before you set this to false to avoid a dialog box, think about
+ * a very slow FTP server...
+ * It is always better to provide progress info in such cases.
+ */
+ KRun( const KURL& url, TQWidget* window, mode_t mode = 0,
+ bool isLocalFile = false, bool showProgressInfo = true );
+ KRun( const KURL& url, TQWidget* window, const TQCString& asn, mode_t mode = 0,
+ bool isLocalFile = false, bool showProgressInfo = true );
+
+ /**
+ * Destructor. Don't call it yourself, since a KRun object auto-deletes
+ * itself.
+ */
+ virtual ~KRun();
+
+ /**
+ * Abort this KRun. This kills any jobs launched by it,
+ * and leads to deletion if auto-deletion is on.
+ * This is much safer than deleting the KRun (in case it's
+ * currently showing an error dialog box, for instance)
+ */
+ void abort();
+
+ /**
+ * Returns true if the KRun instance has an error.
+ * @return true when an error occurred
+ * @see error()
+ */
+ bool hasError() const { return m_bFault; }
+
+ /**
+ * Returns true if the KRun instance has finished.
+ * @return true if the KRun instance has finished
+ * @see finished()
+ */
+ bool hasFinished() const { return m_bFinished; }
+
+ /**
+ * Checks whether auto delete is activated.
+ * Auto-deletion causes the KRun instance to delete itself
+ * when it finished its task.
+ * By default auto deletion is on.
+ * @return true if auto deletion is on, false otherwise
+ */
+ bool autoDelete() const { return m_bAutoDelete; }
+
+ /**
+ * Enables or disabled auto deletion.
+ * Auto deletion causes the KRun instance to delete itself
+ * when it finished its task. If you allocate the KRun
+ * object on the stack you must disable auto deletion.
+ * By default auto deletion is on.
+ * @param b true to enable auto deletion, false to disable
+ */
+ void setAutoDelete(bool b) { m_bAutoDelete = b; }
+
+ /**
+ * Set the preferred service for opening this URL, after
+ * its mimetype will have been found by KRun. IMPORTANT: the service is
+ * only used if its configuration says it can handle this mimetype.
+ * This is used for instance for the X-TDE-LastOpenedWith key, for
+ * the recent documents list.
+ * @param desktopEntryName the desktopEntryName of the service, e.g. "kate".
+ */
+ void setPreferredService( const TQString& desktopEntryName );
+
+ /**
+ * Sets whether executables, .desktop files or shell scripts should
+ * be run by KRun. This is enabled by default.
+ * @param b whether to run executable files or not.
+ * @see isExecutable()
+ * @since 3.2
+ */
+ void setRunExecutables(bool b);
+
+ /**
+ * Sets whether the external webbrowser setting should be honoured.
+ * This is enabled by default.
+ * This should only be disabled in webbrowser applications.
+ * @param b whether to enable the external browser or not.
+ * @since 3.4
+ */
+ void setEnableExternalBrowser(bool b);
+
+ /**
+ * Sets the file name to use in the case of downloading the file to a tempfile
+ * in order to give to a non-url-aware application. Some apps rely on the extension
+ * to determine the mimetype of the file. Usually the file name comes from the URL,
+ * but in the case of the HTTP Content-Disposition header, we need to override the
+ * file name.
+ * @since 3.5.3
+ */
+ void setSuggestedFileName( const TQString& fileName );
+
+ /**
+ * Open a list of URLs with a certain service (application).
+ *
+ * @param _service the service to run
+ * @param _urls the list of URLs, can be empty (app launched
+ * without argument)
+ * @param window The top-level widget of the app that invoked this object.
+ * @param tempFiles if true and _urls are local files, they will be deleted
+ * when the application exits.
+ * @return the process id, or 0 on error
+ * @since 3.5.2
+ */
+ static pid_t run( const KService& _service, const KURL::List& _urls, TQWidget* window, bool tempFiles = false );
+ static pid_t run( const KService& _service, const KURL::List& _urls, TQWidget* window,
+ const TQCString& asn, bool tempFiles = false );
+ /**
+ * Open a list of URLs with a certain service (application).
+ *
+ * @param _service the service to run
+ * @param _urls the list of URLs, can be empty (app launched
+ * without argument)
+ * @param tempFiles if true and _urls are local files, they will be deleted
+ * when the application exits.
+ * @return the process id, or 0 on error
+ */
+ // BIC merge second overload with first one, using tempFiles=false
+ static pid_t run( const KService& _service, const KURL::List& _urls, bool tempFiles );
+ static pid_t run( const KService& _service, const KURL::List& _urls );
+ /// @since 3.5.3
+ /// @internal
+ static pid_t run( const KService& _service, const KURL::List& _urls, TQWidget* window, bool tempFiles, const TQString& suggestedFileName );
+ static pid_t run( const KService& _service, const KURL::List& _urls, TQWidget* window,
+ const TQCString& asn, bool tempFiles, const TQString& suggestedFileName );
+
+ /**
+ * Open a list of URLs with.
+ *
+ * @param _exec the name of the executable, for example
+ * "/usr/bin/netscape".
+ * @param _urls the list of URLs to open, can be empty (app launched without argument)
+ * @param _name the logical name of the application, for example
+ * "Netscape 4.06".
+ * @param _icon the icon which should be used by the application.
+ * @param _obsolete1 Do not use!
+ * @param _obsolete2 Do not use!
+ * @return the process id, or 0 on error
+ */
+ static pid_t run( const TQString& _exec, const KURL::List& _urls,
+ const TQString& _name = TQString::null,
+ const TQString& _icon = TQString::null,
+ const TQString& _obsolete1 = TQString::null,
+ const TQString& _obsolete2 = TQString::null );
+
+ /**
+ * Open the given URL.
+ *
+ * This function is used after the mime type
+ * is found out. It will search for all services which can handle
+ * the mime type and call run() afterwards.
+ * @param _url the URL to open
+ * @param _mimetype the mime type of the resource
+ * @param tempFile if true and _url is a local file, it will be deleted
+ * when the launched application exits.
+ * @param runExecutables if false then local .desktop files,
+ * executables and shell scripts will not be run.
+ * See also isExecutable().
+ * @return the process id, or 0 on error
+ */
+ // BIC Merge second overload with first one using runExecutables=true, and
+ // merge third overload with first one as well using tempFiles=false and
+ // runExecutables=true
+ static pid_t runURL( const KURL& _url, const TQString& _mimetype, bool tempFile, bool runExecutables);
+ static pid_t runURL( const KURL& _url, const TQString& _mimetype, bool tempFile);
+ static pid_t runURL( const KURL& _url, const TQString& _mimetype );
+ /// @since 3.5.3
+ /// @internal
+ static pid_t runURL( const KURL& _url, const TQString& _mimetype, TQWidget* window, const TQCString& asn, bool tempFile, bool runExecutables, const TQString& suggestedFileName );
+ static pid_t runURL( const KURL& _url, const TQString& _mimetype, bool tempFile, bool runExecutables, const TQString& suggestedFileName );
+
+ /**
+ * Run the given shell command and notifies kicker of the starting
+ * of the application. If the program to be called doesn't exist,
+ * an error box will be displayed.
+ *
+ * Use only when you know the full command line. Otherwise use the other
+ * static methods, or KRun's constructor.
+ *
+ * @p _cmd must be a shell command. You must not append "&"
+ * to it, since the function will do that for you.
+ *
+ * @return PID of running command, 0 if it could not be started, 0 - (PID
+ * of running command) if command was unsafe for map notification.
+ */
+ static pid_t runCommand( TQString cmd );
+
+ /**
+ * Same as the other runCommand(), but it also takes the name of the
+ * binary, to display an error message in case it couldn't find it.
+ *
+ * @param cmd must be a shell command. You must not append "&"
+ * to it, since the function will do that for you.
+ * @param execName the name of the executable
+ * @param icon icon for app starting notification
+ * @return PID of running command, 0 if it could not be started, 0 - (PID
+ * of running command) if command was unsafe for map notification.
+ */
+ static pid_t runCommand( const TQString& cmd, const TQString & execName, const TQString & icon );
+ static pid_t runCommand( const TQString& cmd, const TQString & execName, const TQString & icon,
+ TQWidget* window, const TQCString& asn );
+
+ /**
+ * Display the Open-With dialog for those URLs, and run the chosen application.
+ * @param lst the list of applications to run
+ * @param tempFiles if true and lst are local files, they will be deleted
+ * when the application exits.
+ * @return false if the dialog was canceled
+ */
+ // BIC merge second overload with first one, using tempFiles=false
+ static bool displayOpenWithDialog( const KURL::List& lst, bool tempFiles );
+ static bool displayOpenWithDialog( const KURL::List& lst );
+ /// @since 3.5.3
+ /// @internal
+ static bool displayOpenWithDialog( const KURL::List& lst, bool tempFiles, const TQString& suggestedFileName );
+
+ /**
+ * Quotes a string for the shell.
+ * @param _str the string to quote. The quoted string will be written here
+ */
+ static void shellQuote( TQString &_str );
+
+ /**
+ * Processes a Exec= line as found in .desktop files.
+ * @param _service the service to extract information from.
+ * @param _urls The urls the service should open.
+ * @param has_shell If true, the arguments are going to be fed into a
+ * shell e.g by using system().
+ * If false, the arguments are going to be fed into a exec() kind
+ * call.
+ * If the arguments are intended for an exec() kind of call and
+ * the Exec line contains shell commands then "/bin/sh -c" is added.
+ * @param tempFiles if true and _urls are local files, they will be deleted
+ * when the application exits.
+ * @return a list of arguments suitable for either system() or exec().
+ */
+ static TQStringList processDesktopExec(const KService &_service, const KURL::List &_urls, bool has_shell, bool tempFiles);
+ static TQStringList processDesktopExec(const KService &_service, const KURL::List &_urls, bool has_shell);
+ /// @since 3.5.3
+ /// @internal
+ static TQStringList processDesktopExec(const KService &_service, const KURL::List &_urls, bool has_shell, bool tempFiles, const TQString& suggestedFileName);
+
+ /**
+ * Given a full command line (e.g. the Exec= line from a .desktop file),
+ * extract the name of the binary being run.
+ * @param execLine the full command line
+ * @param removePath if true, remove a (relative or absolute) path. E.g. /usr/bin/ls becomes ls.
+ * @return the name of the binary to run
+ * @since 3.1
+ */
+ static TQString binaryName( const TQString & execLine, bool removePath );
+
+ /**
+ * Returns whether @p serviceType refers to an executable program instead
+ * of a data file.
+ * @since 3.2
+ */
+ static bool isExecutable( const TQString& serviceType );
+
+ /**
+ * Returns wether the @p url of @p mimetype is executable.
+ * To be executable the file must pass the following rules:
+ * -# Must reside on the local filesystem.
+ * -# Must be marked as executable for the user by the filesystem.
+ * -# The mime type must inherit application/x-executable or application/x-executable-script.
+ * To allow a script to run when the above rules are satisfied add the entry
+ * @code
+ * X-TDE-IsAlso=application/x-executable-script
+ * @endcode
+ * to the mimetype's desktop file.
+ * @since 3.3
+ */
+ static bool isExecutableFile( const KURL& url, const TQString &mimetype );
+
+ /**
+ * @internal
+ * @since 3.4
+ */
+ static bool checkStartupNotify( const TQString& binName, const KService* service, bool* silent_arg, TQCString* wmclass_arg );
+
+signals:
+ /**
+ * Emitted when the operation finished.
+ * @see hasFinished()
+ */
+ void finished();
+ /**
+ * Emitted when the operation had an error.
+ * @see hasError()
+ */
+ void error();
+
+protected slots:
+ void slotTimeout();
+ void slotScanFinished( TDEIO::Job * );
+ void slotScanMimeType( TDEIO::Job *, const TQString &type );
+ virtual void slotStatResult( TDEIO::Job * );
+
+protected:
+ virtual void init();
+
+ virtual void scanFile();
+
+ /**
+ * Called if the mimetype has been detected. The function checks
+ * whether the document and appends the gzip protocol to the
+ * URL. Otherwise runURL is called to finish the job.
+ */
+ virtual void foundMimeType( const TQString& _type );
+
+ virtual void killJob();
+
+ KURL m_strURL;
+ bool m_bFault;
+ bool m_bAutoDelete;
+ bool m_bProgressInfo;
+ bool m_bFinished;
+ TDEIO::Job * m_job;
+ TQTimer m_timer;
+
+ /**
+ * Used to indicate that the next action is to scan the file.
+ * This action is invoked from slotTimeout.
+ */
+ bool m_bScanFile;
+ bool m_bIsDirectory;
+
+ /**
+ * USed to indicate that the next action is to initialize.
+ * This action is invoked from slotTimeout
+ */
+ bool m_bInit;
+
+ bool m_bIsLocalFile;
+ mode_t m_mode;
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+
+private:
+ void init (const KURL& url, TQWidget* window, const TQCString& asn, mode_t mode,
+ bool isLocalFile, bool showProgressInfo);
+private:
+ class KRunPrivate;
+ KRunPrivate *d;
+};
+
+#ifndef KDE_NO_COMPAT
+/**
+ * @deprecated. Kept for source compatibility, does nothing nowadays.
+ * Do not use in new source.
+ * KRun can open the openwith dialog directly now.
+ * Use KRun::displayOpenWithDialog() if you were using KOpenWithHandler directly.
+ */
+class TDEIO_EXPORT_DEPRECATED KOpenWithHandler
+{
+public:
+ KOpenWithHandler() {}
+ static bool exists() { return true; }
+};
+#endif
+
+/**
+ * @internal
+ * This class watches a process launched by KRun.
+ * It sends a notification when the process exits (for the taskbar)
+ * and it will show an error message if necessary (e.g. "program not found").
+ */
+class TDEIO_EXPORT TDEProcessRunner : public TQObject
+{
+ Q_OBJECT
+
+ public:
+
+ static pid_t run(TDEProcess *, const TQString & binName);
+#ifdef Q_WS_X11 // We don't have KStartupInfo in Qt/Embedded
+ static pid_t run(TDEProcess *, const TQString & binName, const KStartupInfoId& id );
+#endif
+
+ virtual ~TDEProcessRunner();
+
+ pid_t pid() const;
+
+ protected slots:
+
+ void slotProcessExited(TDEProcess *);
+
+ private:
+
+ TDEProcessRunner(TDEProcess *, const TQString & binName);
+#ifdef Q_WS_X11 // We don't have KStartupInfo in Qt/Embedded
+ TDEProcessRunner(TDEProcess *, const TQString & binName, const KStartupInfoId& id );
+#endif
+ TDEProcessRunner();
+
+ TDEProcess * process_;
+ TQString binName;
+#ifdef Q_WS_X11 // We don't have KStartupInfo in Qt/Embedded
+ KStartupInfoId id_;
+#endif
+};
+
+#endif
diff --git a/tdeio/tdeio/ksambashare.cpp b/tdeio/tdeio/ksambashare.cpp
new file mode 100644
index 000000000..608594ee5
--- /dev/null
+++ b/tdeio/tdeio/ksambashare.cpp
@@ -0,0 +1,239 @@
+/* This file is part of the KDE project
+ Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <tqdict.h>
+#include <tqfile.h>
+#include <tqtextstream.h>
+
+#include <kdirwatch.h>
+#include <kstaticdeleter.h>
+#include <kdebug.h>
+#include <ksimpleconfig.h>
+
+#include "ksambashare.h"
+
+class KSambaSharePrivate
+{
+public:
+ KSambaSharePrivate();
+
+ bool readSmbConf();
+ bool findSmbConf();
+ bool load();
+
+ TQDict<bool> sharedPaths;
+ TQString smbConf;
+};
+
+KSambaSharePrivate::KSambaSharePrivate()
+{
+ load();
+}
+
+
+#define FILESHARECONF "/etc/security/fileshare.conf"
+
+bool KSambaSharePrivate::load() {
+ if (!findSmbConf())
+ return false;
+
+ return readSmbConf();
+}
+
+/**
+ * Try to find the samba config file path
+ * First tries the tdeconfig, then checks
+ * several well-known paths
+ * @return wether a smb.conf was found.
+ **/
+bool KSambaSharePrivate::findSmbConf() {
+ KSimpleConfig config(TQString::fromLatin1(FILESHARECONF),true);
+ smbConf = config.readEntry("SMBCONF");
+
+ if ( TQFile::exists(smbConf) )
+ return true;
+
+ if ( TQFile::exists("/etc/samba/smb.conf") )
+ smbConf = "/etc/samba/smb.conf";
+ else
+ if ( TQFile::exists("/etc/smb.conf") )
+ smbConf = "/etc/smb.conf";
+ else
+ if ( TQFile::exists("/usr/local/samba/lib/smb.conf") )
+ smbConf = "/usr/local/samba/lib/smb.conf";
+ else
+ if ( TQFile::exists("/usr/samba/lib/smb.conf") )
+ smbConf = "/usr/samba/lib/smb.conf";
+ else
+ if ( TQFile::exists("/usr/lib/smb.conf") )
+ smbConf = "/usr/lib/smb.conf";
+ else
+ if ( TQFile::exists("/usr/local/lib/smb.conf") )
+ smbConf = "/usr/local/lib/smb.conf";
+ else {
+ kdDebug(7000) << "KSambaShare: Could not found smb.conf!" << endl;
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * Reads all path= entries from the smb.conf file
+ * and fills the sharedPaths dict with the values
+ */
+bool KSambaSharePrivate::readSmbConf() {
+ TQFile f(smbConf);
+
+ kdDebug(7000) << "KSambaShare::readSmbConf " << smbConf << endl;
+
+ if (!f.open(IO_ReadOnly)) {
+ kdError() << "KSambaShare: Could not open " << smbConf << endl;
+ return false;
+ }
+
+ sharedPaths.clear();
+
+ TQTextStream s(&f);
+
+ bool continuedLine = false; // is true if the line before ended with a backslash
+ TQString completeLine;
+
+ while (!s.eof())
+ {
+ TQString currentLine = s.readLine().stripWhiteSpace();
+
+ if (continuedLine) {
+ completeLine += currentLine;
+ continuedLine = false;
+ }
+ else
+ completeLine = currentLine;
+
+ // is the line continued in the next line ?
+ if ( completeLine[completeLine.length()-1] == '\\' )
+ {
+ continuedLine = true;
+ // remove the ending backslash
+ completeLine.truncate( completeLine.length()-1 );
+ continue;
+ }
+
+ // comments or empty lines
+ if (completeLine.isEmpty() ||
+ '#' == completeLine[0] ||
+ ';' == completeLine[0])
+ {
+ continue;
+ }
+
+ // parameter
+ int i = completeLine.find('=');
+
+ if (i>-1)
+ {
+ TQString name = completeLine.left(i).stripWhiteSpace().lower();
+ TQString value = completeLine.mid(i+1).stripWhiteSpace();
+
+ if (name == TDEGlobal::staticQString("path")) {
+ // Handle quotation marks
+ if ( value[0] == '"' )
+ value.remove(0,1);
+
+ if ( value[value.length()-1] == '"' )
+ value.truncate(value.length()-1);
+
+ // Normalize path
+ if ( value[value.length()-1] != '/' )
+ value += '/';
+
+ bool b = true;
+ sharedPaths.insert(value,&b);
+ kdDebug(7000) << "KSambaShare: Found path: " << value << endl;
+ }
+ }
+ }
+
+ f.close();
+
+ return true;
+
+}
+
+KSambaShare::KSambaShare() {
+ d = new KSambaSharePrivate();
+ if (TQFile::exists(d->smbConf)) {
+ KDirWatch::self()->addFile(d->smbConf);
+ KDirWatch::self()->addFile(FILESHARECONF);
+ connect(KDirWatch::self(), TQT_SIGNAL(dirty (const TQString&)),this,
+ TQT_SLOT(slotFileChange(const TQString&)));
+ }
+}
+
+KSambaShare::~KSambaShare() {
+ if (TQFile::exists(d->smbConf)) {
+ KDirWatch::self()->removeFile(d->smbConf);
+ KDirWatch::self()->removeFile(FILESHARECONF);
+ }
+ delete d;
+}
+
+TQString KSambaShare::smbConfPath() const {
+ return d->smbConf;
+}
+
+bool KSambaShare::isDirectoryShared( const TQString & path ) const {
+ TQString fixedPath = path;
+ if ( path[path.length()-1] != '/' )
+ fixedPath += '/';
+
+ return d->sharedPaths.find(fixedPath) != 0;
+}
+
+TQStringList KSambaShare::sharedDirectories() const {
+ TQStringList result;
+ TQDictIterator<bool> it(d->sharedPaths);
+ for( ; it.current(); ++it )
+ result << it.currentKey();
+
+ return result;
+}
+
+void KSambaShare::slotFileChange( const TQString & path ) {
+ if (path == d->smbConf)
+ d->readSmbConf();
+ else
+ if (path == FILESHARECONF)
+ d->load();
+
+ emit changed();
+}
+
+KSambaShare* KSambaShare::_instance = 0L;
+static KStaticDeleter<KSambaShare> ksdSambaShare;
+
+KSambaShare* KSambaShare::instance() {
+ if (! _instance )
+ _instance = ksdSambaShare.setObject(_instance, new KSambaShare());
+
+ return _instance;
+}
+
+#include "ksambashare.moc"
+
diff --git a/tdeio/tdeio/ksambashare.h b/tdeio/tdeio/ksambashare.h
new file mode 100644
index 000000000..ffd298588
--- /dev/null
+++ b/tdeio/tdeio/ksambashare.h
@@ -0,0 +1,85 @@
+/* This file is part of the KDE project
+ Copyright (c) 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef ksambashare_h
+#define ksambashare_h
+
+#include <tqobject.h>
+
+#include <tdelibs_export.h>
+
+class KSambaSharePrivate;
+
+/**
+ * Similar functionality like KFileShare,
+ * but works only for Samba and do not need
+ * any suid script.
+ * Singleton class, call instance() to get an instance.
+ */
+class TDEIO_EXPORT KSambaShare : public TQObject
+{
+Q_OBJECT
+public:
+ /**
+ * Returns the one and only instance of KSambaShare
+ */
+ static KSambaShare* instance();
+
+ /**
+ * Whether or not the given path is shared by Samba.
+ * @param path the path to check if it is shared by Samba.
+ * @return whether the given path is shared by Samba.
+ */
+ bool isDirectoryShared( const TQString & path ) const;
+
+ /**
+ * Returns a list of all directories shared by Samba.
+ * The resulting list is not sorted.
+ * @return a list of all directories shared by Samba.
+ */
+ TQStringList sharedDirectories() const;
+
+ /**
+ * KSambaShare destructor.
+ * Do not call!
+ * The instance is destroyed automatically!
+ */
+ virtual ~KSambaShare();
+
+ /**
+ * Returns the path to the used smb.conf file
+ * or null if no file was found
+ */
+ TQString smbConfPath() const;
+
+signals:
+ /**
+ * Emitted when the smb.conf file has changed
+ */
+ void changed();
+
+private:
+ KSambaShare();
+ static KSambaShare* _instance;
+ KSambaSharePrivate* d;
+
+private slots:
+ void slotFileChange(const TQString&);
+};
+
+#endif
diff --git a/tdeio/tdeio/kscan.cpp b/tdeio/tdeio/kscan.cpp
new file mode 100644
index 000000000..49ae7c5ab
--- /dev/null
+++ b/tdeio/tdeio/kscan.cpp
@@ -0,0 +1,185 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001 Carsten Pfeiffer <pfeiffer@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <tqfile.h>
+
+#include <klocale.h>
+#include <ktrader.h>
+
+#include "kscan.h"
+
+// static factory method
+KScanDialog * KScanDialog::getScanDialog( TQWidget *parent, const char *name,
+ bool modal )
+{
+ KTrader::OfferList offers = KTrader::self()->query("KScan/KScanDialog");
+ if ( offers.isEmpty() )
+ return 0L;
+
+ KService::Ptr ptr = *(offers.begin());
+ KLibFactory *factory = KLibLoader::self()->factory( TQFile::encodeName(ptr->library()) );
+
+ if ( !factory )
+ return 0;
+
+ TQStringList args;
+ args << TQString::number( (int)modal );
+
+ TQObject *res = factory->create( TQT_TQOBJECT(parent), name, "KScanDialog", args );
+
+ return dynamic_cast<KScanDialog *>( res );
+}
+
+
+KScanDialog::KScanDialog( int dialogFace, int buttonMask,
+ TQWidget *parent, const char *name, bool modal )
+ : KDialogBase( dialogFace, i18n("Acquire Image"), buttonMask, Close,
+ parent, name, modal, true ),
+ m_currentId( 1 )
+{
+}
+
+KScanDialog::~KScanDialog()
+{
+}
+
+bool KScanDialog::setup()
+{
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////
+
+
+// static factory method
+KOCRDialog * KOCRDialog::getOCRDialog( TQWidget *parent, const char *name,
+ bool modal )
+{
+ KTrader::OfferList offers = KTrader::self()->query("KScan/KOCRDialog");
+ if ( offers.isEmpty() )
+ return 0L;
+
+ KService::Ptr ptr = *(offers.begin());
+ KLibFactory *factory = KLibLoader::self()->factory( TQFile::encodeName(ptr->library()) );
+
+ if ( !factory )
+ return 0;
+
+ TQStringList args;
+ args << TQString::number( (int)modal );
+
+ TQObject *res = factory->create( TQT_TQOBJECT(parent), name, "KOCRDialog", args );
+
+ return dynamic_cast<KOCRDialog *>( res );
+}
+
+
+KOCRDialog::KOCRDialog( int dialogFace, int buttonMask,
+ TQWidget *parent, const char *name, bool modal )
+ : KDialogBase( dialogFace, i18n("OCR Image"), buttonMask, Close,
+ parent, name, modal, true ),
+ m_currentId( 1 )
+{
+
+}
+
+KOCRDialog::~KOCRDialog()
+{
+}
+
+
+///////////////////////////////////////////////////////////////////
+
+
+KScanDialogFactory::KScanDialogFactory( TQObject *parent, const char *name )
+ : KLibFactory( parent, name ),
+ m_instance( 0L )
+{
+}
+
+KScanDialogFactory::~KScanDialogFactory()
+{
+ delete m_instance;
+}
+
+TQObject *KScanDialogFactory::createObject( TQObject *parent, const char *name,
+ const char *classname,
+ const TQStringList &args )
+{
+ if ( strcmp( classname, "KScanDialog" ) != 0 )
+ return 0;
+
+ if ( parent && !parent->isWidgetType() )
+ return 0;
+
+ bool modal = false;
+
+ if ( args.count() == 1 )
+ modal = (bool)args[ 0 ].toInt();
+
+ return TQT_TQOBJECT(createDialog( TQT_TQWIDGET( parent ), name, modal ));
+}
+
+
+///////////////////////////////////////////////////////////////////
+
+
+KOCRDialogFactory::KOCRDialogFactory( TQObject *parent, const char *name )
+ : KLibFactory( parent, name ),
+ m_instance( 0L )
+{
+}
+
+KOCRDialogFactory::~KOCRDialogFactory()
+{
+ delete m_instance;
+}
+
+TQObject *KOCRDialogFactory::createObject( TQObject *parent, const char *name,
+ const char *classname,
+ const TQStringList &args )
+{
+ if ( strcmp( classname, "KOCRDialog" ) != 0 )
+ return 0;
+
+ if ( parent && !parent->isWidgetType() )
+ return 0;
+
+ bool modal = false;
+
+ if ( args.count() == 1 )
+ modal = (bool)args[ 0 ].toInt();
+
+ return TQT_TQOBJECT(createDialog( TQT_TQWIDGET( parent ), name, modal ));
+}
+
+void KScanDialog::virtual_hook( int id, void* data )
+{ KDialogBase::virtual_hook( id, data ); }
+
+void KScanDialogFactory::virtual_hook( int id, void* data )
+{ KLibFactory::virtual_hook( id, data ); }
+
+void KOCRDialog::virtual_hook( int id, void* data )
+{ KDialogBase::virtual_hook( id, data ); }
+
+void KOCRDialogFactory::virtual_hook( int id, void* data )
+{ KLibFactory::virtual_hook( id, data ); }
+
+
+#include "kscan.moc"
diff --git a/tdeio/tdeio/kscan.h b/tdeio/tdeio/kscan.h
new file mode 100644
index 000000000..940b8ceaa
--- /dev/null
+++ b/tdeio/tdeio/kscan.h
@@ -0,0 +1,370 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001 Carsten Pfeiffer <pfeiffer@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KSCAN_H
+#define KSCAN_H
+
+#include <kdialogbase.h>
+#include <kinstance.h>
+#include <klibloader.h>
+
+class TQImage;
+
+/**
+ * This is a base class for scanning dialogs. You can derive from this class
+ * and implement your own dialog. An implementation is available in
+ * tdegraphics/libkscan.
+ *
+ * Application developers that wish to add scanning support to their program
+ * can use the static method @p KScanDialog::getScanDialog() to get an instance
+ * of the user's preferred scanning dialog.
+ *
+ * Typical usage looks like this (e.g. in a slotShowScanDialog() method):
+ *
+ * \code
+ * if ( !m_scanDialog ) {
+ * m_scanDialog = KScanDialog::getScanDialog( this, "scandialog" );
+ * if ( !m_scanDialog ) // no scanning support installed?
+ * return;
+ *
+ * connect( m_scanDialog, TQT_SIGNAL( finalImage( const TQImage&, int )),
+ * TQT_SLOT( slotScanned( const TQImage&, int ) ));
+ * }
+ *
+ * if ( m_scanDialog->setup() ) // only if scanner configured/available
+ * m_scanDialog->show();
+ * \endcode
+ *
+ * This will create and show a non-modal scanning dialog. Connect to more
+ * signals if you like.
+ *
+ * If you implement an own scan-dialog, you also have to implement a
+ * KScanDialogFactory.
+ *
+ * @short A baseclass and accessor for Scanning Dialogs
+ * @author Carsten Pfeiffer <pfeiffer@kde.org>
+ */
+class TDEIO_EXPORT KScanDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Creates the user's preferred scanning dialog and returns it,
+ * or 0L if no scan-support
+ * is available. Pass a suitable @p parent widget, if you like. If you
+ * don't you have to 'delete' the returned pointer yourself.
+ * @param parent the QWidget's parent, or 0
+ * @param name the name of the TQObject, can be 0
+ * @param modal if true the dialog is model
+ * @return the KScanDialog, or 0 if the function failed
+ */
+ static KScanDialog * getScanDialog( TQWidget *parent=0L,
+ const char *name=0, bool modal=false );
+ /**
+ * Destructs the scan dialog.
+ */
+ ~KScanDialog();
+
+ /**
+ * Reimplement this if you need to set up some things, before showing the
+ * dialog, e.g. to ask the user for the scanner device to use. If you
+ * return false (e.g. there is no device available or the user aborted
+ * device selection), the dialog will not be shown.
+ *
+ * @return true by default.
+ */
+ virtual bool setup();
+
+protected:
+ /**
+ * Constructs the scan dialog. If you implement an own dialog, you can
+ * customize it with the usual KDialogBase flags.
+ *
+ * @param dialogFace the KDialogBase::DialogType
+ * @param buttonMask a ORed mask of all buttons (see
+ * KDialogBase::ButtonCode)
+ * @param parent the QWidget's parent, or 0
+ * @param name the name of the TQObject, can be 0
+ * @param modal if true the dialog is model
+ * @see KDialogBase
+ */
+ KScanDialog( int dialogFace=Tabbed, int buttonMask = Close|Help,
+ TQWidget *parent=0L, const char *name=0, bool modal=false );
+
+ /**
+ * Returns the current id for an image. You can use that in your subclass
+ * for the signals. The id is used in the signals to let people know
+ * which preview and which text-recognition belongs to which scan.
+ *
+ * @return the current id for the image
+ * @see nextId
+ * @see finalImage
+ * @see preview
+ * @see textRecognized
+ */
+ int id() const { return m_currentId; }
+
+ /**
+ * Returns the id for the next image. You can use that in your subclass
+ * for the signals.
+ *
+ * @return the id for the next image
+ * @see id
+ * @see finalImage
+ * @see preview
+ * @see textRecognized
+ *
+ */
+ int nextId() { return ++m_currentId; }
+
+signals:
+ /**
+ * Informs you that an image has been previewed.
+ * @param img the image
+ * @param id the image's id
+ */
+ void preview( const TQImage &img, int id );
+
+ /**
+ * Informs you that an image has scanned. @p id is the same as in the
+ * @p preview() signal, if this image had been previewed before.
+ *
+ * Note, that those id's may not be properly implemented in the current
+ * libkscan.
+ * @param img the image
+ * @param id the image's id
+ */
+ void finalImage( const TQImage &img, int id );
+
+ /**
+ * Informs you that the image with the id @p id has been run through
+ * text-recognition. The text is in the TQString parameter. In the future,
+ * a compound document, using rich text will be used instead.
+ *
+ * @param text the text that has been recognized
+ * @param id the id of the image
+ */
+ void textRecognized( const TQString &text, int id );
+
+private:
+ int m_currentId;
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KScanDialogPrivate;
+ KScanDialogPrivate *d;
+};
+
+
+/**
+ * A factory for creating a KScanDialog. You need to reimplement
+ * createDialog().
+ * @short Factory for creating KScanDialogs
+ */
+class TDEIO_EXPORT KScanDialogFactory : public KLibFactory
+{
+public:
+ virtual ~KScanDialogFactory();
+
+ /**
+ * Your library should reimplement this method to return your KScanDialog
+ * derived dialog.
+ * @param parent the QWidget's parent, or 0
+ * @param name the name of the TQObject, can be 0
+ * @param modal if true the dialog is model
+ */
+ virtual KScanDialog * createDialog( TQWidget *parent=0, const char *name=0,
+ bool modal=false ) = 0;
+
+protected:
+ /**
+ * Creates a new KScanDialogFactory.
+ * @param parent the QWidget's parent, or 0
+ * @param name the name of the TQObject, can be 0
+ */
+ KScanDialogFactory( TQObject *parent=0, const char *name=0 );
+
+ virtual TQObject* createObject( TQObject* parent = 0, const char* name = 0,
+ const char* classname = TQOBJECT_OBJECT_NAME_STRING,
+ const TQStringList &args = TQStringList() );
+
+
+ /**
+ * Creates a new instance with the given name.
+ * @param instanceName the name of the instance
+ */
+ void setName( const TQCString& instanceName ) {
+ delete m_instance;
+ m_instance = new TDEInstance( instanceName );
+ }
+
+ /**
+ * Returns the instance.
+ * @return the TDEInstance
+ */
+ TDEInstance *instance() const { return m_instance; }
+
+private:
+ TDEInstance *m_instance;
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KScanDialogFactoryPrivate* d;
+};
+
+/**
+ * Base class for OCR Dialogs.
+ */
+class TDEIO_EXPORT KOCRDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Creates the user's preferred OCR dialog and returns it,
+ * or 0L if no OCR-support
+ * is available. Pass a suitable @p parent widget, if you like. If you
+ * don't you have to 'delete' the returned pointer yourself.
+ * @param parent the QWidget's parent, or 0
+ * @param name the name of the TQObject, can be 0
+ * @param modal if true the dialog is model
+ * @return the KOCRDialog, or 0 if the function failed
+ */
+ static KOCRDialog * getOCRDialog( TQWidget *parent=0L,
+ const char *name=0, bool modal=false );
+ ~KOCRDialog();
+
+protected:
+ /**
+ * Constructs the OCR dialog. If you implement an own dialog, you can
+ * customize it with the usual KDialogBase flags.
+ *
+ * @param dialogFace the KDialogBase::DialogType
+ * @param buttonMask a ORed mask of all buttons (see
+ * KDialogBase::ButtonCode)
+ * @param parent the QWidget's parent, or 0
+ * @param name the name of the TQObject, can be 0
+ * @param modal if true the dialog is model
+ */
+ KOCRDialog( int dialogFace=Tabbed, int buttonMask = Close|Help,
+ TQWidget *parent=0L, const char *name=0, bool modal=false );
+
+ /**
+ * Returns the current id for an image. You can use that in your subclass
+ * for the signals. The id is used in the signals to let people know
+ * which text-recognition belongs to which scan.
+ *
+ * @return the current id for the image
+ * @see nextId
+ * @see textRecognized
+ */
+ int id() const { return m_currentId; }
+
+ /**
+ * Returns the id for the next image. You can use that in your subclass
+ * for the signals.
+ *
+ * @return the id for the next image
+ * @see id
+ * @see textRecognized
+ */
+ int nextId() { return ++m_currentId; }
+
+signals:
+ /**
+ * Informs you that the image with the id @p id has been run through
+ * text-recognition. The text is in the TQString parameter. In the future,
+ * a compound document, using rich text will be used instead.
+ *
+ * @param text the text that has been recognized
+ * @param id the id of the image
+ */
+ void textRecognized( const TQString &text, int id );
+
+private:
+ int m_currentId;
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KOCRDialogPrivate;
+ KOCRDialogPrivate *d;
+};
+
+
+/**
+ * A factory for creating a KOCRDialog. You need to reimplement
+ * createDialog().
+ * @short Factory for creating KScanDialogs
+ */
+class TDEIO_EXPORT KOCRDialogFactory : public KLibFactory
+{
+public:
+ virtual ~KOCRDialogFactory();
+
+ /**
+ * Your library should reimplement this method to return your KOCRDialog
+ * derived dialog.
+ * @param parent the QWidget's parent, or 0
+ * @param name the name of the TQObject, can be 0
+ * @param modal if true the dialog is model
+ */
+ virtual KOCRDialog * createDialog( TQWidget *parent=0, const char *name=0,
+ bool modal=false ) = 0;
+
+protected:
+ /**
+ * Creates a new KScanDialogFactory.
+ * @param parent the QWidget's parent, or 0
+ * @param name the name of the TQObject, can be 0
+ */
+ KOCRDialogFactory( TQObject *parent=0, const char *name=0 );
+
+ virtual TQObject* createObject( TQObject* parent = 0, const char* name = 0,
+ const char* className = TQOBJECT_OBJECT_NAME_STRING,
+ const TQStringList &args = TQStringList() );
+
+
+ /**
+ * Creates a new instance with the given name.
+ * @param instanceName the name of the instance
+ */
+ void setName( const TQCString& instanceName ) {
+ delete m_instance;
+ m_instance = new TDEInstance( instanceName );
+ }
+
+ /**
+ * Returns the instance.
+ * @return the TDEInstance
+ */
+ TDEInstance *instance() const { return m_instance; }
+
+private:
+ TDEInstance *m_instance;
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KOCRDialogFactory* d;
+};
+
+
+#endif // KSCAN_H
diff --git a/tdeio/tdeio/kservice.cpp b/tdeio/tdeio/kservice.cpp
new file mode 100644
index 000000000..2a24743ab
--- /dev/null
+++ b/tdeio/tdeio/kservice.cpp
@@ -0,0 +1,934 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 1999 - 2001 Waldo Bastian <bastian@kde.org>
+ * Copyright (C) 1999 David Faure <faure@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+// $Id$
+
+#include <config.h>
+
+#include "kservice.h"
+#include "kservice_p.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stddef.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <tqstring.h>
+#include <tqfile.h>
+#include <tqdir.h>
+#include <tqtl.h>
+
+#include <ksimpleconfig.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kdesktopfile.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <klocale.h>
+#include <tdeconfigbase.h>
+#include <kstandarddirs.h>
+#include <dcopclient.h>
+
+#include "kservicefactory.h"
+#include "kservicetypefactory.h"
+#include "kservicetype.h"
+#include "kuserprofile.h"
+#include "tdesycoca.h"
+
+class KService::KServicePrivate
+{
+public:
+ TQStringList categories;
+ TQString menuId;
+};
+
+KService::KService( const TQString & _name, const TQString &_exec, const TQString &_icon)
+ : KSycocaEntry( TQString::null)
+{
+ d = new KServicePrivate;
+ m_bValid = true;
+ m_bDeleted = false;
+ m_strType = "Application";
+ m_strName = _name;
+ m_strExec = _exec;
+ m_strIcon = _icon;
+ m_bTerminal = false;
+ m_bAllowAsDefault = true;
+ m_initialPreference = 10;
+}
+
+
+KService::KService( const TQString & _fullpath )
+ : KSycocaEntry( _fullpath)
+{
+ KDesktopFile config( _fullpath );
+
+ init(&config);
+}
+
+KService::KService( KDesktopFile *config )
+ : KSycocaEntry( config->fileName())
+{
+ init(config);
+}
+
+void
+KService::init( KDesktopFile *config )
+{
+ d = new KServicePrivate;
+ m_bValid = true;
+
+ bool absPath = !TQDir::isRelativePath(entryPath());
+ bool kde4application = config->fileName().startsWith("/usr/share/applications/kde4/");
+
+ config->setDesktopGroup();
+
+ TQMap<TQString, TQString> entryMap = config->entryMap(config->group());
+
+ entryMap.remove("Encoding"); // reserved as part of Desktop Entry Standard
+ entryMap.remove("Version"); // reserved as part of Desktop Entry Standard
+
+ m_bDeleted = config->readBoolEntry( "Hidden", false );
+ entryMap.remove("Hidden");
+ if (m_bDeleted)
+ {
+ //kdDebug() << "Hidden=true for " << entryPath() << endl;
+ m_bValid = false;
+ return;
+ }
+
+ m_strName = config->readName();
+ entryMap.remove("Name");
+ if ( m_strName.isEmpty() )
+ {
+ if (config->readEntry( "Exec" ).isEmpty())
+ {
+ //kdWarning(7012) << "The desktop entry file " << entryPath()
+ // << " has no Name and no Exec" << endl;
+ m_bValid = false;
+ return;
+ }
+ // Try to make up a name.
+ m_strName = entryPath();
+ int i = m_strName.findRev('/');
+ m_strName = m_strName.mid(i+1);
+ i = m_strName.findRev('.');
+ if (i != -1)
+ m_strName = m_strName.left(i);
+ }
+
+ m_strType = config->readType();
+ entryMap.remove("Type");
+ if ( m_strType.isEmpty() )
+ {
+ /*kdWarning(7012) << "The desktop entry file " << entryPath()
+ << " has no Type=... entry."
+ << " It should be \"Application\" or \"Service\"" << endl;
+ m_bValid = false;
+ return;*/
+ m_strType = "Application";
+ } else if ( m_strType != "Application" && m_strType != "Service" )
+ {
+ kdWarning(7012) << "The desktop entry file " << entryPath()
+ << " has Type=" << m_strType
+ << " instead of \"Application\" or \"Service\"" << endl;
+ m_bValid = false;
+ return;
+ }
+
+ // In case Try Exec is set, check if the application is available
+ if (!config->tryExec()) {
+ //kdDebug(7012) << "tryExec said false for " << entryPath() << endl;
+ m_bDeleted = true;
+ m_bValid = false;
+ return;
+ }
+
+ TQString resource = config->resource();
+
+ if ( (m_strType == "Application") &&
+ (!resource.isEmpty()) &&
+ (resource != "apps") &&
+ !absPath)
+ {
+ kdWarning(7012) << "The desktop entry file " << entryPath()
+ << " has Type=" << m_strType << " but is located under \"" << resource
+ << "\" instead of \"apps\"" << endl;
+ m_bValid = false;
+ return;
+ }
+
+ if ( (m_strType == "Service") &&
+ (!resource.isEmpty()) &&
+ (resource != "services") &&
+ !absPath)
+ {
+ kdWarning(7012) << "The desktop entry file " << entryPath()
+ << " has Type=" << m_strType << " but is located under \"" << resource
+ << "\" instead of \"services\"" << endl;
+ m_bValid = false;
+ return;
+ }
+
+ TQString name = entryPath();
+ int pos = name.findRev('/');
+ if (pos != -1)
+ name = name.mid(pos+1);
+ pos = name.find('.');
+ if (pos != -1)
+ name = name.left(pos);
+
+ m_strExec = config->readPathEntry( "Exec" );
+ if (kde4application && !m_strExec.startsWith("/")) {
+ m_strExec = "XDG_DATA_DIRS=/usr/share XDG_CONFIG_DIRS=/etc/xdg/ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:$PATH "+m_strExec;
+ } else if (config->readBoolEntry("X-TDE-SubstituteUID")) {
+ int space = m_strExec.find(" ");
+ if (space==-1)
+ m_strExec = KStandardDirs::findExe(m_strExec);
+ else {
+ const TQString command = m_strExec.left(space);
+ m_strExec.replace(command,KStandardDirs::findExe(command));
+ }
+ }
+
+ entryMap.remove("Exec");
+
+ m_strIcon = config->readEntry( "Icon", "unknown" );
+ if (kde4application) {
+ if (TQFile::exists("/usr/share/icons/oxygen/22x22/apps/" + m_strIcon + ".png")) {
+ m_strIcon = "/usr/share/icons/oxygen/22x22/apps/" + m_strIcon + ".png";
+ } else if (TQFile::exists("/usr/share/icons/hicolor/22x22/apps/" + m_strIcon + ".png")) {
+ m_strIcon = "/usr/share/icons/hicolor/22x22/apps/" + m_strIcon + ".png";
+ }
+ }
+ entryMap.remove("Icon");
+ m_bTerminal = (config->readBoolEntry( "Terminal" )); // should be a property IMHO
+ entryMap.remove("Terminal");
+ m_strTerminalOptions = config->readEntry( "TerminalOptions" ); // should be a property IMHO
+ entryMap.remove("TerminalOptions");
+ m_strPath = config->readPath();
+ entryMap.remove("Path");
+ m_strComment = config->readComment();
+ entryMap.remove("Comment");
+ m_strGenName = config->readGenericName();
+ if (kde4application) {
+ m_strGenName += " [KDE4]";
+ }
+ entryMap.remove("GenericName");
+ TQString untranslatedGenericName = config->readEntryUntranslated( "GenericName" );
+ if (!untranslatedGenericName.isEmpty())
+ entryMap.insert("UntranslatedGenericName", untranslatedGenericName);
+
+ m_lstKeywords = config->readListEntry("Keywords");
+ entryMap.remove("Keywords");
+ d->categories = config->readListEntry("Categories", ';');
+ entryMap.remove("Categories");
+ m_strLibrary = config->readEntry( "X-TDE-Library" );
+ entryMap.remove("X-TDE-Library");
+ m_strInit = config->readEntry("X-TDE-Init" );
+ entryMap.remove("X-TDE-Init");
+
+ m_lstServiceTypes = config->readListEntry( "ServiceTypes" );
+ entryMap.remove("ServiceTypes");
+ // For compatibility with KDE 1.x
+ if (!kde4application)
+ m_lstServiceTypes += config->readListEntry( "MimeType", ';' );
+ entryMap.remove("MimeType");
+
+ if ( m_strType == "Application" && !m_lstServiceTypes.contains("Application") )
+ // Applications implement the service type "Application" ;-)
+ m_lstServiceTypes += "Application";
+
+ TQString dcopServiceType = config->readEntry("X-DCOP-ServiceType").lower();
+ entryMap.remove("X-DCOP-ServiceType");
+ if (dcopServiceType == "unique")
+ m_DCOPServiceType = DCOP_Unique;
+ else if (dcopServiceType == "multi")
+ m_DCOPServiceType = DCOP_Multi;
+ else if (dcopServiceType == "wait")
+ m_DCOPServiceType = DCOP_Wait;
+ else
+ m_DCOPServiceType = DCOP_None;
+
+ m_strDesktopEntryName = name.lower();
+ if (kde4application)
+ m_strDesktopEntryName = "kde4-" + m_strDesktopEntryName;
+
+ m_bAllowAsDefault = config->readBoolEntry( "AllowDefault", true );
+ entryMap.remove("AllowDefault");
+
+ m_initialPreference = config->readNumEntry( "X-TDE-InitialPreference", 1 );
+ entryMap.remove("X-TDE-InitialPreference");
+ if ( m_initialPreference == 1 )
+ m_initialPreference = config->readNumEntry( "InitialPreference", 1 );
+ entryMap.remove("InitialPreference");
+
+ // Store all additional entries in the property map.
+ // A TQMap<TQString,TQString> would be easier for this but we can't
+ // brake BC, so we have to store it in m_mapProps.
+// tqWarning("Path = %s", entryPath().latin1());
+ TQMap<TQString,TQString>::ConstIterator it = entryMap.begin();
+ for( ; it != entryMap.end();++it)
+ {
+ //tqDebug(" Key = %s Data = %s", it.key().latin1(), it.data().latin1());
+ TQString key = it.key();
+ if (kde4application && key=="OnlyShowIn" && it.data()=="KDE;")
+ key = "NotShowIn";
+ m_mapProps.insert( key, TQVariant( it.data()));
+ }
+}
+
+KService::KService( TQDataStream& _str, int offset ) : KSycocaEntry( _str, offset )
+{
+ d = new KServicePrivate;
+ load( _str );
+}
+
+KService::~KService()
+{
+ //debug("KService::~KService()");
+ delete d;
+}
+
+TQPixmap KService::pixmap( KIcon::Group _group, int _force_size, int _state, TQString * _path ) const
+{
+ KIconLoader *iconLoader=TDEGlobal::iconLoader();
+ if (!iconLoader->extraDesktopThemesAdded())
+ {
+ TQPixmap pixmap=iconLoader->loadIcon( m_strIcon, _group, _force_size, _state, _path, true );
+ if (!pixmap.isNull() ) return pixmap;
+
+ iconLoader->addExtraDesktopThemes();
+ }
+
+ return iconLoader->loadIcon( m_strIcon, _group, _force_size, _state, _path );
+}
+
+void KService::load( TQDataStream& s )
+{
+ // dummies are here because of fields that were removed, to keep bin compat.
+ // Feel free to re-use, but fields for Applications only (not generic services)
+ // should rather be added to application.desktop
+ TQ_INT8 def, term, dummy1, dummy2;
+ TQ_INT8 dst, initpref;
+ TQString dummyStr1, dummyStr2;
+ int dummyI1, dummyI2;
+ TQ_UINT32 dummyUI32;
+
+ // WARNING: IN KDE 3.x THIS NEEDS TO REMAIN COMPATIBLE WITH KDE 2.x!
+ // !! This data structure should remain binary compatible at all times !!
+ // You may add new fields at the end. Make sure to update the version
+ // number in tdesycoca.h
+ s >> m_strType >> m_strName >> m_strExec >> m_strIcon
+ >> term >> m_strTerminalOptions
+ >> m_strPath >> m_strComment >> m_lstServiceTypes >> def >> m_mapProps
+ >> m_strLibrary >> dummyI1 >> dummyI2
+ >> dst
+ >> m_strDesktopEntryName
+ >> dummy1 >> dummyStr1 >> initpref >> dummyStr2 >> dummy2
+ >> m_lstKeywords >> m_strInit >> dummyUI32 >> m_strGenName
+ >> d->categories >> d->menuId;
+
+ m_bAllowAsDefault = def;
+ m_bTerminal = term;
+ m_DCOPServiceType = (DCOPServiceType_t) dst;
+ m_initialPreference = initpref;
+
+ m_bValid = true;
+}
+
+void KService::save( TQDataStream& s )
+{
+ KSycocaEntry::save( s );
+ TQ_INT8 def = m_bAllowAsDefault, initpref = m_initialPreference;
+ TQ_INT8 term = m_bTerminal;
+ TQ_INT8 dst = (TQ_INT8) m_DCOPServiceType;
+ TQ_INT8 dummy1 = 0, dummy2 = 0; // see ::load
+ TQString dummyStr1, dummyStr2;
+ int dummyI1 = 0, dummyI2 = 0;
+ TQ_UINT32 dummyUI32 = 0;
+
+ // WARNING: IN KDE 3.x THIS NEEDS TO REMAIN COMPATIBLE WITH KDE 2.x!
+ // !! This data structure should remain binary compatible at all times !!
+ // You may add new fields at the end. Make sure to update the version
+ // number in tdesycoca.h
+ s << m_strType << m_strName << m_strExec << m_strIcon
+ << term << m_strTerminalOptions
+ << m_strPath << m_strComment << m_lstServiceTypes << def << m_mapProps
+ << m_strLibrary << dummyI1 << dummyI2
+ << dst
+ << m_strDesktopEntryName
+ << dummy1 << dummyStr1 << initpref << dummyStr2 << dummy2
+ << m_lstKeywords << m_strInit << dummyUI32 << m_strGenName
+ << d->categories << d->menuId;
+}
+
+bool KService::hasServiceType( const TQString& _servicetype ) const
+{
+ if (!m_bValid) return false; // safety test
+
+ //kdDebug(7012) << "Testing " << m_strDesktopEntryName << " for " << _servicetype << endl;
+
+ KMimeType::Ptr mimePtr = KMimeType::mimeType( _servicetype );
+ if ( mimePtr && mimePtr == KMimeType::defaultMimeTypePtr() )
+ mimePtr = 0;
+
+ bool isNumber;
+ // For each service type we are associated with, if it doesn't
+ // match then we try its parent service types.
+ TQStringList::ConstIterator it = m_lstServiceTypes.begin();
+ for( ; it != m_lstServiceTypes.end(); ++it )
+ {
+ (*it).toInt(&isNumber);
+ if (isNumber)
+ continue;
+ //kdDebug(7012) << " has " << (*it) << endl;
+ KServiceType::Ptr ptr = KServiceType::serviceType( *it );
+ if ( ptr && ptr->inherits( _servicetype ) )
+ return true;
+
+ // The mimetype inheritance ("is also") works the other way.
+ // e.g. if we're looking for a handler for mimePtr==smb-workgroup
+ // then a handler for inode/directory is ok.
+ if ( mimePtr && mimePtr->is( *it ) )
+ return true;
+ }
+ return false;
+}
+
+int KService::initialPreferenceForMimeType( const TQString& mimeType ) const
+{
+ if (!m_bValid) return 0; // safety test
+
+ bool isNumber;
+
+ // For each service type we are associated with
+ TQStringList::ConstIterator it = m_lstServiceTypes.begin();
+ for( ; it != m_lstServiceTypes.end(); ++it )
+ {
+ (*it).toInt(&isNumber);
+ if (isNumber)
+ continue;
+ //kdDebug(7012) << " has " << (*it) << endl;
+ KServiceType::Ptr ptr = KServiceType::serviceType( *it );
+ if ( !ptr || !ptr->inherits( mimeType ) )
+ continue;
+
+ int initalPreference = m_initialPreference;
+ ++it;
+ if (it != m_lstServiceTypes.end())
+ {
+ int i = (*it).toInt(&isNumber);
+ if (isNumber)
+ initalPreference = i;
+ }
+ return initalPreference;
+ }
+
+ KMimeType::Ptr mimePtr = KMimeType::mimeType( mimeType );
+ if ( mimePtr && mimePtr == KMimeType::defaultMimeTypePtr() )
+ mimePtr = 0;
+
+ // Try its parent service types.
+ it = m_lstServiceTypes.begin();
+ for( ; it != m_lstServiceTypes.end(); ++it )
+ {
+ (*it).toInt(&isNumber);
+ if (isNumber)
+ continue;
+
+ // The mimetype inheritance ("is also") works the other way.
+ // e.g. if we're looking for a handler for mimePtr==smb-workgroup
+ // then a handler for inode/directory is ok.
+ if ( !mimePtr || !mimePtr->is( *it ) )
+ continue;
+
+ int initalPreference = m_initialPreference;
+ ++it;
+ if (it != m_lstServiceTypes.end())
+ {
+ int i = (*it).toInt(&isNumber);
+ if (isNumber)
+ initalPreference = i;
+ }
+ return initalPreference;
+ }
+ return 0;
+}
+
+class KServiceReadProperty : public TDEConfigBase
+{
+public:
+ KServiceReadProperty(const TQString &_key, const TQCString &_value)
+ : key(_key), value(_value) { }
+
+ bool internalHasGroup(const TQCString &) const { /*tqDebug("hasGroup(const TQCString &)");*/ return false; }
+
+ TQStringList groupList() const { return TQStringList(); }
+
+ TQMap<TQString,TQString> entryMap(const TQString &group) const
+ { Q_UNUSED(group); return TQMap<TQString,TQString>(); }
+
+ void reparseConfiguration() { }
+
+ KEntryMap internalEntryMap( const TQString &pGroup) const
+ { Q_UNUSED(pGroup); return KEntryMap(); }
+
+ KEntryMap internalEntryMap() const { return KEntryMap(); }
+
+ void putData(const KEntryKey &_key, const KEntry& _data, bool _checkGroup)
+ { Q_UNUSED(_key); Q_UNUSED(_data); Q_UNUSED(_checkGroup); }
+
+ KEntry lookupData(const KEntryKey &_key) const
+ { Q_UNUSED(_key); KEntry entry; entry.mValue = value; return entry; }
+protected:
+ TQString key;
+ TQCString value;
+};
+
+TQVariant KService::property( const TQString& _name) const
+{
+ return property( _name, TQVariant::Invalid);
+}
+
+// Return a string TQVariant if string isn't null, and invalid variant otherwise
+// (the variant must be invalid if the field isn't in the .desktop file)
+// This allows trader queries like "exist Library" to work.
+static TQVariant makeStringVariant( const TQString& string )
+{
+ // Using isEmpty here would be wrong.
+ // Empty is "specified but empty", null is "not specified" (in the .desktop file)
+ return string.isNull() ? TQVariant() : TQVariant( string );
+}
+
+TQVariant KService::property( const TQString& _name, TQVariant::Type t ) const
+{
+ if ( _name == "Type" )
+ return TQVariant( m_strType ); // can't be null
+ else if ( _name == "Name" )
+ return TQVariant( m_strName ); // can't be null
+ else if ( _name == "Exec" )
+ return makeStringVariant( m_strExec );
+ else if ( _name == "Icon" )
+ return makeStringVariant( m_strIcon );
+ else if ( _name == "Terminal" )
+ return TQVariant( static_cast<int>(m_bTerminal) );
+ else if ( _name == "TerminalOptions" )
+ return makeStringVariant( m_strTerminalOptions );
+ else if ( _name == "Path" )
+ return makeStringVariant( m_strPath );
+ else if ( _name == "Comment" )
+ return makeStringVariant( m_strComment );
+ else if ( _name == "GenericName" )
+ return makeStringVariant( m_strGenName );
+ else if ( _name == "ServiceTypes" )
+ return TQVariant( m_lstServiceTypes );
+ else if ( _name == "AllowAsDefault" )
+ return TQVariant( static_cast<int>(m_bAllowAsDefault) );
+ else if ( _name == "InitialPreference" )
+ return TQVariant( m_initialPreference );
+ else if ( _name == "Library" )
+ return makeStringVariant( m_strLibrary );
+ else if ( _name == "DesktopEntryPath" ) // can't be null
+ return TQVariant( entryPath() );
+ else if ( _name == "DesktopEntryName")
+ return TQVariant( m_strDesktopEntryName ); // can't be null
+ else if ( _name == "Categories")
+ return TQVariant( d->categories );
+ else if ( _name == "Keywords")
+ return TQVariant( m_lstKeywords );
+
+ // Ok we need to convert the property from a TQString to its real type.
+ // Maybe the caller helped us.
+ if (t == TQVariant::Invalid)
+ {
+ // No luck, let's ask KServiceTypeFactory what the type of this property
+ // is supposed to be.
+ t = KServiceTypeFactory::self()->findPropertyTypeByName(_name);
+ if (t == TQVariant::Invalid)
+ {
+ kdDebug(7012) << "Request for unknown property '" << _name << "'\n";
+ return TQVariant(); // Unknown property: Invalid variant.
+ }
+ }
+
+ // Then we use a homebuild class based on TDEConfigBase to convert the TQString.
+ // For some often used property types we do the conversion ourselves.
+ TQMap<TQString,TQVariant>::ConstIterator it = m_mapProps.find( _name );
+ if ( (it == m_mapProps.end()) || (!it.data().isValid()))
+ {
+ //kdDebug(7012) << "Property not found " << _name << endl;
+ return TQVariant(); // No property set.
+ }
+
+ switch(t)
+ {
+ case TQVariant::String:
+ return it.data();
+ case TQVariant::Bool:
+ case TQVariant::Int:
+ {
+ TQString aValue = it.data().toString();
+ int val = 0;
+ if (aValue == "true" || aValue == "on" || aValue == "yes")
+ val = 1;
+ else
+ {
+ bool bOK;
+ val = aValue.toInt( &bOK );
+ if( !bOK )
+ val = 0;
+ }
+ if (t == TQVariant::Bool)
+ {
+ return TQVariant((bool)val, 1);
+ }
+ return TQVariant(val);
+ }
+ default:
+ // All others
+ KServiceReadProperty ksrp(_name, it.data().toString().utf8());
+ return ksrp.readPropertyEntry(_name, t);
+ }
+}
+
+TQStringList KService::propertyNames() const
+{
+ TQStringList res;
+
+ TQMap<TQString,TQVariant>::ConstIterator it = m_mapProps.begin();
+ for( ; it != m_mapProps.end(); ++it )
+ res.append( it.key() );
+
+ res.append( "Type" );
+ res.append( "Name" );
+ res.append( "Comment" );
+ res.append( "GenericName" );
+ res.append( "Icon" );
+ res.append( "Exec" );
+ res.append( "Terminal" );
+ res.append( "TerminalOptions" );
+ res.append( "Path" );
+ res.append( "ServiceTypes" );
+ res.append( "AllowAsDefault" );
+ res.append( "InitialPreference" );
+ res.append( "Library" );
+ res.append( "DesktopEntryPath" );
+ res.append( "DesktopEntryName" );
+ res.append( "Keywords" );
+ res.append( "Categories" );
+
+ return res;
+}
+
+KService::List KService::allServices()
+{
+ return KServiceFactory::self()->allServices();
+}
+
+KService::Ptr KService::serviceByName( const TQString& _name )
+{
+ KService * s = KServiceFactory::self()->findServiceByName( _name );
+ return KService::Ptr( s );
+}
+
+KService::Ptr KService::serviceByDesktopPath( const TQString& _name )
+{
+ KService * s = KServiceFactory::self()->findServiceByDesktopPath( _name );
+ return KService::Ptr( s );
+}
+
+KService::Ptr KService::serviceByDesktopName( const TQString& _name )
+{
+ KService * s = KServiceFactory::self()->findServiceByDesktopName( _name.lower() );
+ if (!s && !_name.startsWith("kde-"))
+ s = KServiceFactory::self()->findServiceByDesktopName( "kde-"+_name.lower() );
+ return KService::Ptr( s );
+}
+
+KService::Ptr KService::serviceByMenuId( const TQString& _name )
+{
+ KService * s = KServiceFactory::self()->findServiceByMenuId( _name );
+ return KService::Ptr( s );
+}
+
+KService::Ptr KService::serviceByStorageId( const TQString& _storageId )
+{
+ KService::Ptr service = KService::serviceByMenuId( _storageId );
+ if (service)
+ return service;
+
+ service = KService::serviceByDesktopPath(_storageId);
+ if (service)
+ return service;
+
+ if (!TQDir::isRelativePath(_storageId) && TQFile::exists(_storageId))
+ return new KService(_storageId);
+
+ TQString tmp = _storageId;
+ tmp = tmp.mid(tmp.findRev('/')+1); // Strip dir
+
+ if (tmp.endsWith(".desktop"))
+ tmp.truncate(tmp.length()-8);
+
+ if (tmp.endsWith(".kdelnk"))
+ tmp.truncate(tmp.length()-7);
+
+ service = KService::serviceByDesktopName(tmp);
+
+ return service;
+}
+
+KService::List KService::allInitServices()
+{
+ return KServiceFactory::self()->allInitServices();
+}
+
+bool KService::substituteUid() const {
+ TQVariant v = property("X-TDE-SubstituteUID", TQVariant::Bool);
+ return v.isValid() && v.toBool();
+}
+
+TQString KService::username() const {
+ // See also KDesktopFile::tryExec()
+ TQString user;
+ TQVariant v = property("X-TDE-Username", TQVariant::String);
+ user = v.isValid() ? v.toString() : TQString::null;
+ if (user.isEmpty())
+ user = ::getenv("ADMIN_ACCOUNT");
+ if (user.isEmpty())
+ user = "root";
+ return user;
+}
+
+bool KService::noDisplay() const {
+ TQMap<TQString,TQVariant>::ConstIterator it = m_mapProps.find( "NoDisplay" );
+ if ( (it != m_mapProps.end()) && (it.data().isValid()))
+ {
+ TQString aValue = it.data().toString().lower();
+ if (aValue == "true" || aValue == "on" || aValue == "yes")
+ return true;
+ }
+
+ it = m_mapProps.find( "OnlyShowIn" );
+ if ( (it != m_mapProps.end()) && (it.data().isValid()))
+ {
+ TQString aValue = it.data().toString();
+ TQStringList aList = TQStringList::split(';', aValue);
+ if ((!aList.contains("TDE")) && (!aList.contains("KDE")))
+ return true;
+ }
+
+ it = m_mapProps.find( "NotShowIn" );
+ if ( (it != m_mapProps.end()) && (it.data().isValid()))
+ {
+ TQString aValue = it.data().toString();
+ TQStringList aList = TQStringList::split(';', aValue);
+ if ((aList.contains("TDE")) || (aList.contains("KDE")))
+ return true;
+ }
+
+ if (!kapp->authorizeControlModule(d->menuId))
+ return true;
+
+ return false;
+}
+
+TQString KService::untranslatedGenericName() const {
+ TQVariant v = property("UntranslatedGenericName", TQVariant::String);
+ return v.isValid() ? v.toString() : TQString::null;
+}
+
+bool KService::SuSEunimportant() const {
+ TQMap<TQString,TQVariant>::ConstIterator it = m_mapProps.find( "X-SuSE-Unimportant" );
+ if ( (it == m_mapProps.end()) || (!it.data().isValid()))
+ {
+ return false;
+ }
+
+ TQString aValue = it.data().toString();
+ if (aValue == "true" || aValue == "on" || aValue == "yes")
+ return true;
+ else
+ return false;
+}
+
+TQString KService::parentApp() const {
+ TQMap<TQString,TQVariant>::ConstIterator it = m_mapProps.find( "X-TDE-ParentApp" );
+ if ( (it == m_mapProps.end()) || (!it.data().isValid()))
+ {
+ return TQString::null;
+ }
+
+ return it.data().toString();
+}
+
+bool KService::allowMultipleFiles() const {
+ // Can we pass multiple files on the command line or do we have to start the application for every single file ?
+ if ( m_strExec.find( "%F" ) != -1 || m_strExec.find( "%U" ) != -1 ||
+ m_strExec.find( "%N" ) != -1 || m_strExec.find( "%D" ) != -1 )
+ return true;
+ else
+ return false;
+}
+
+TQStringList KService::categories() const
+{
+ return d->categories;
+}
+
+TQString KService::menuId() const
+{
+ return d->menuId;
+}
+
+void KService::setMenuId(const TQString &menuId)
+{
+ d->menuId = menuId;
+}
+
+TQString KService::storageId() const
+{
+ if (!d->menuId.isEmpty())
+ return d->menuId;
+ return entryPath();
+}
+
+TQString KService::locateLocal()
+{
+ if (d->menuId.isEmpty() || desktopEntryPath().startsWith(".hidden") ||
+ (TQDir::isRelativePath(desktopEntryPath()) && d->categories.isEmpty()))
+ return KDesktopFile::locateLocal(desktopEntryPath());
+
+ return ::locateLocal("xdgdata-apps", d->menuId);
+}
+
+TQString KService::newServicePath(bool showInMenu, const TQString &suggestedName,
+ TQString *menuId, const TQStringList *reservedMenuIds)
+{
+ TQString base = suggestedName;
+ if (!showInMenu)
+ base.prepend("kde-");
+
+ TQString result;
+ for(int i = 1; true; i++)
+ {
+ if (i == 1)
+ result = base + ".desktop";
+ else
+ result = base + TQString("-%1.desktop").arg(i);
+
+ if (reservedMenuIds && reservedMenuIds->contains(result))
+ continue;
+
+ // Lookup service by menu-id
+ KService::Ptr s = serviceByMenuId(result);
+ if (s)
+ continue;
+
+ if (showInMenu)
+ {
+ if (!locate("xdgdata-apps", result).isEmpty())
+ continue;
+ }
+ else
+ {
+ TQString file = result.mid(4); // Strip "kde-"
+ if (!locate("apps", ".hidden/"+file).isEmpty())
+ continue;
+ }
+
+ break;
+ }
+ if (menuId)
+ *menuId = result;
+
+ if (showInMenu)
+ {
+ return ::locateLocal("xdgdata-apps", result);
+ }
+ else
+ {
+ TQString file = result.mid(4); // Strip "kde-"
+ return ::locateLocal("apps", ".hidden/"+file);
+ }
+}
+
+
+void KService::virtual_hook( int id, void* data )
+{ KSycocaEntry::virtual_hook( id, data ); }
+
+
+void KService::rebuildKSycoca(TQWidget *parent)
+{
+ KServiceProgressDialog dlg(parent, "tdesycoca_progress",
+ i18n("Updating System Configuration"),
+ i18n("Updating system configuration."));
+
+ TQByteArray data;
+ DCOPClient *client = kapp->dcopClient();
+
+ int result = client->callAsync("kded", "tdebuildsycoca", "recreate()",
+ data, TQT_TQOBJECT(&dlg), TQT_SLOT(slotFinished()));
+
+ if (result)
+ {
+ dlg.exec();
+ }
+}
+
+KServiceProgressDialog::KServiceProgressDialog(TQWidget *parent, const char *name,
+ const TQString &caption, const TQString &text)
+ : KProgressDialog(parent, name, caption, text, true)
+{
+ connect(&m_timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotProgress()));
+ progressBar()->setTotalSteps(20);
+ m_timeStep = 700;
+ m_timer.start(m_timeStep);
+ setAutoClose(false);
+}
+
+void
+KServiceProgressDialog::slotProgress()
+{
+ int p = progressBar()->progress();
+ if (p == 18)
+ {
+ progressBar()->reset();
+ progressBar()->setProgress(1);
+ m_timeStep = m_timeStep * 2;
+ m_timer.start(m_timeStep);
+ }
+ else
+ {
+ progressBar()->setProgress(p+1);
+ }
+}
+
+void
+KServiceProgressDialog::slotFinished()
+{
+ progressBar()->setProgress(20);
+ m_timer.stop();
+ TQTimer::singleShot(1000, this, TQT_SLOT(close()));
+}
+
+#include "kservice_p.moc"
diff --git a/tdeio/tdeio/kservice.h b/tdeio/tdeio/kservice.h
new file mode 100644
index 000000000..4db478ba6
--- /dev/null
+++ b/tdeio/tdeio/kservice.h
@@ -0,0 +1,562 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kservices_h__
+#define __kservices_h__
+
+#include <tqstringlist.h>
+#include <tqmap.h>
+#include <tqvariant.h>
+#include <kicontheme.h>
+
+#include "tdesycocaentry.h"
+
+class TQDataStream;
+class KDesktopFile;
+class KService;
+class KBuildSycoca;
+class TQWidget;
+
+/**
+ * Represent a service, i.e. an application bound to one or several mimetypes
+ * (or servicetypes) as written in its desktop entry file.
+ *
+ * A service may be a library, too.
+ * The starting point you need is often the static methods.
+ * Service types are stored as desktop files in the "service" resource..
+ *
+ * @see KServiceType
+ * @see KServiceGroup
+ * @author Torben Weis <weis@kde.org>
+ */
+class TDEIO_EXPORT KService : public KSycocaEntry
+{
+ K_SYCOCATYPE( KST_KService, KSycocaEntry )
+
+ friend class KBuildSycoca;
+
+public:
+ typedef KSharedPtr<KService> Ptr;
+ typedef TQValueList<Ptr> List;
+public:
+ /**
+ * Construct a temporary service with a given name, exec-line and icon.
+ * @param _name the name of the service
+ * @param _exec the executable
+ * @param _icon the name of the icon
+ */
+ KService( const TQString & _name, const TQString &_exec, const TQString &_icon);
+
+ /**
+ * Construct a service and take all information from a config file.
+ *
+ * @param _fullpath Full path to the config file.
+ */
+ explicit KService( const TQString & _fullpath );
+
+ /**
+ * Construct a service and take all information from a desktop file.
+ * @param config the desktop file to read
+ */
+ KService( KDesktopFile *config ); // KDE-4.0: make explicit
+
+ /**
+ * @internal
+ * Construct a service from a stream.
+ * The stream must already be positionned at the correct offset.
+ */
+ KService( TQDataStream& _str, int offset );
+
+ virtual ~KService();
+
+ /**
+ * Returns the type of the service.
+ * @return the type of the service ("Application" or "Service")
+ */
+ virtual TQString type() const { return m_strType; }
+ /**
+ * Returns the name of the service.
+ * @return the name of the service,
+ * or TQString::null if not set
+ */
+ virtual TQString name() const { return m_strName; }
+ /**
+ * Returns the executable.
+ * @return the command that the service executes,
+ * or TQString::null if not set
+ */
+ TQString exec() const { return m_strExec; }
+ /**
+ * Returns the name of the service's library.
+ * @return the name of the library that contains the services
+ * implementation,
+ * or TQString::null if not set
+ */
+ TQString library() const { return m_strLibrary; }
+ /**
+ * Returns the name of the init function to call (KControl modules).
+ * @return the name of the init function to call in this service
+ * during startup of KDE. (KControl modules only),
+ * or TQString::null if not set
+ */
+ TQString init() const { return m_strInit; }
+
+ /**
+ * Returns the name of the icon.
+ * @return the icon associated with the service,
+ * or "unknown" if not set
+ */
+ TQString icon() const { return m_strIcon; }
+ /**
+ * Returns the pixmap that represents the icon.
+ * @return a pixmap for this service (finds and loads icon()),
+ * null if not set
+ * @see icon()
+ */
+ TQPixmap pixmap( KIcon::Group _group, int _force_size = 0, int _state = 0,
+ TQString * _path = 0L ) const;
+ /**
+ * Checks whethe the service should be run in a terminal.
+ * @return true if the service is to be run in a terminal.
+ */
+ bool terminal() const { return m_bTerminal; }
+ /**
+ * Returns any options associated with the terminal the service
+ * runs in, if it requires a terminal.
+ *
+ * The service must be a tty-oriented program.
+ * @return the terminal options,
+ * or TQString::null if not set
+ */
+ TQString terminalOptions() const { return m_strTerminalOptions; }
+ /**
+ * Checks whether the service runs with a different user id.
+ * @return true if the service has to be run under a different uid.
+ * @see username()
+ */
+ bool substituteUid() const;
+ /**
+ * Returns the user name, if the service runs with a
+ * different user id.
+ * @return the username under which the service has to be run,
+ * or TQString::null if not set
+ * @see substututeUid()a
+ */
+ TQString username() const;
+
+ /**
+ * Returns the path to the location where the service desktop entry
+ * is stored.
+ *
+ * This is a relative path if the desktop entry was found in any
+ * of the locations pointed to by $TDEDIRS (e.g. "Internet/kppp.desktop")
+ * It is a full path if the desktop entry originates from another
+ * location.
+ * @return the path of the service's desktop file,
+ * or TQString::null if not set
+ */
+ TQString desktopEntryPath() const { return entryPath(); }
+
+ /**
+ * Returns the filename of the service desktop entry without any
+ * extension. E.g. "kppp"
+ * @return the name of the desktop entry without path or extension,
+ * or TQString::null if not set
+ */
+ TQString desktopEntryName() const { return m_strDesktopEntryName; }
+
+ /**
+ * Returns the menu ID of the service desktop entry.
+ * The menu ID is used to add or remove the entry to a menu.
+ * @return the menu ID
+ * @since 3.2
+ */
+ TQString menuId() const;
+
+ /**
+ * Returns a normalized ID suitable for storing in configuration files.
+ * It will be based on the menu-id when available and otherwise falls
+ * back to desktopEntryPath()
+ * @return the storage ID
+ * @since 3.2
+ */
+ TQString storageId() const;
+
+ /**
+ * Describes the DCOP type of the service.
+ * @li None - This service has no DCOP support
+ * @li Unique - This service provides a unique DCOP service.
+ * The service name is equal to the desktopEntryName.
+ * @li Multi - This service provides a DCOP service which can be run
+ * with multiple instances in parallel. The service name of
+ * an instance is equal to the desktopEntryName + "-" +
+ * the PID of the process.
+ * @li Wait - This service has no DCOP support, the launcher will wait
+ * till it is finished.
+ */
+ enum DCOPServiceType_t { DCOP_None = 0, DCOP_Unique, DCOP_Multi, DCOP_Wait };
+
+ /**
+ * Returns the DCOPServiceType supported by this service.
+ * @return the DCOPServiceType supported by this service
+ */
+ DCOPServiceType_t DCOPServiceType() const { return m_DCOPServiceType; }
+
+ /**
+ * Returns the working directory to run the program in.
+ * @return the working directory to run the program in,
+ * or TQString::null if not set
+ */
+ TQString path() const { return m_strPath; }
+
+ /**
+ * Returns the descriptive comment for the service, if there is one.
+ * @return the descriptive comment for the service, or TQString::null
+ * if not set
+ */
+ TQString comment() const { return m_strComment; }
+
+ /**
+ * Returns the generic name for the service, if there is one
+ * (e.g. "Mail Client").
+ * @return the generic name,
+ * or TQString::null if not set
+ */
+ TQString genericName() const { return m_strGenName; }
+
+ /**
+ * Returns the untranslated (US English) generic name
+ * for the service, if there is one
+ * (e.g. "Mail Client").
+ * @return the generic name,
+ * or TQString::null if not set
+ * @since 3.2
+ */
+ TQString untranslatedGenericName() const;
+
+ /**
+ * Returns a list of descriptive keywords the service, if there are any.
+ * @return the list of keywords
+ */
+ TQStringList keywords() const { return m_lstKeywords; }
+
+ /**
+ * Returns a list of VFolder categories.
+ * @return the list of VFolder categories
+ * @since 3.1
+ */
+ TQStringList categories() const;
+
+ /**
+ * Returns the service types that this service supports.
+ * @return the list of service types that are supported
+ */
+ TQStringList serviceTypes() const { return m_lstServiceTypes; }
+
+ /**
+ * Checks whether the service supports this service type
+ * @param _service The name of the service type you are
+ * interested in determining whether this services supports.
+ *
+ * @return true if the service you specified is supported,
+ * otherwise false.
+ */
+ bool hasServiceType( const TQString& _service ) const;
+
+ /**
+ * Set to true if it is allowed to use this service as the default (main)
+ * action for the files it supports (e.g. Left Click in a file manager, or KRun in general).
+ *
+ * If not, then this service is only available in RMB popups, so it must
+ * be selected explicitely by the user in order to be used.
+ * Note that servicemenus supersede this functionality though, at least in konqueror.
+ *
+ * @return true if the service may be used as the default (main) handler
+ */
+ bool allowAsDefault() const { return m_bAllowAsDefault; }
+
+ /**
+ * Checks whether this service can handle several files as
+ * startup arguments.
+ * @return true if multiple files may be passed to this service at
+ * startup. False if only one file at a time may be passed.
+ */
+ bool allowMultipleFiles() const;
+
+ /**
+ * What preference to associate with this service initially (before
+ * the user has had any chance to define a profile for it).
+ * The bigger the value, the most preferred the service is.
+ * @return the service preference level of the service
+ */
+ int initialPreference() const { return m_initialPreference; }
+
+ /**
+ * What preference to associate with this service initially
+ * for handling the specified mimetype. (before the user has
+ * had any chance to define a profile for it).
+ * The bigger the value, the most preferred the service is.
+ * @return the service preference level of the service for
+ * this mimetype
+ */
+ int initialPreferenceForMimeType( const TQString& mimeType ) const;
+
+ /**
+ * @internal. Allows KServiceType::offers to tweak the initial preference.
+ */
+ void setInitialPreference( int i ) { m_initialPreference = i; }
+
+ /**
+ * Whether the entry should be suppressed in menus.
+ * @return true to suppress this service
+ */
+ bool noDisplay() const;
+ /**
+ * check if the application entry is important
+ */
+ bool SuSEunimportant() const;
+
+ /**
+ * Name of the application this service belongs to.
+ * (Useful for e.g. plugins)
+ * @return the parent application, or TQString::null if not set
+ * @since 3.1
+ */
+ TQString parentApp() const;
+
+ /**
+ * Returns the requested property. Some often used properties
+ * have convenience access functions like exec(),
+ * serviceTypes etc.
+ *
+ * It depends upon the serviceTypes() of this service which
+ * properties a service can have.
+ *
+ * @param _name the name of the property
+ * @return the property, or invalid if not found
+ * @see KServiceType
+ */
+ virtual TQVariant property( const TQString& _name ) const;
+
+ /**
+ * Returns the requested property.
+ *
+ * @param _name the name of the property
+ * @param t the assumed type of the property
+ * @return the property, or invalid if not found
+ * @see KServiceType
+ * @since 3.2
+ */
+ TQVariant property( const TQString& _name, TQVariant::Type t ) const;
+
+ /**
+ * Returns the list of all properties that this service can have.
+ * That means, that some of these properties may be empty.
+ * @return the list of supported properties
+ */
+ virtual TQStringList propertyNames() const;
+
+ /**
+ * Checks whether the service is valid.
+ * @return true if the service is valid (e.g. name is not empty)
+ */
+ bool isValid() const { return m_bValid; }
+
+ /**
+ * Returns a path that can be used for saving changes to this
+ * service
+ * @return path that can be used for saving changes to this service
+ * @since 3.2
+ */
+ TQString locateLocal();
+
+ /**
+ * @internal
+ * Load the service from a stream.
+ */
+ virtual void load( TQDataStream& );
+ /**
+ * @internal
+ * Save the service to a stream.
+ */
+ virtual void save( TQDataStream& );
+ /**
+ * @internal
+ * Set the menu id
+ */
+ void setMenuId(const TQString &menuId);
+ /**
+ * @internal
+ * Sets whether to use a terminal or not
+ */
+ void setTerminal(bool b) { m_bTerminal = b; }
+ /**
+ * @internal
+ * Sets the terminal options to use
+ */
+ void setTerminalOptions(const TQString &options) { m_strTerminalOptions = options; }
+
+ /**
+ * Find a service by name, i.e. the translated Name field. You should
+ * really not use this method, since the name is translated.
+ *
+ * @param _name the name to search
+ * @return a pointer to the requested service or 0 if the service is
+ * unknown.
+ * @em Very @em important: Don't store the result in a KService* !
+ */
+ static Ptr serviceByName( const TQString& _name );
+
+ /**
+ * Find a service based on its path as returned by desktopEntryPath().
+ * It's usually better to use serviceByStorageId() instead.
+ *
+ * @param _path the path of the configuration file
+ * @return a pointer to the requested service or 0 if the service is
+ * unknown.
+ * @em Very @em important: Don't store the result in a KService* !
+ */
+ static Ptr serviceByDesktopPath( const TQString& _path );
+
+ /**
+ * Find a service by the name of its desktop file, not depending on
+ * its actual location (as long as it's under the applnk or service
+ * directories). For instance "konqbrowser" or "kcookiejar". Note that
+ * the ".desktop" extension is implicit.
+ *
+ * This is the recommended method (safe even if the user moves stuff)
+ * but note that it assumes that no two entries have the same filename.
+ *
+ * @param _name the name of the configuration file
+ * @return a pointer to the requested service or 0 if the service is
+ * unknown.
+ * @em Very @em important: Don't store the result in a KService* !
+ */
+ static Ptr serviceByDesktopName( const TQString& _name );
+
+ /**
+ * Find a service by its menu-id
+ *
+ * @param _menuId the menu id of the service
+ * @return a pointer to the requested service or 0 if the service is
+ * unknown.
+ * @em Very @em important: Don't store the result in a KService* !
+ * @since 3.2
+ */
+ static Ptr serviceByMenuId( const TQString& _menuId );
+
+ /**
+ * Find a service by its storage-id or desktop-file path. This
+ * function will try very hard to find a matching service.
+ *
+ * @param _storageId the storage id or desktop-file path of the service
+ * @return a pointer to the requested service or 0 if the service is
+ * unknown.
+ * @em Very @em important: Don't store the result in a KService* !
+ * @since 3.2
+ */
+ static Ptr serviceByStorageId( const TQString& _storageId );
+
+ /**
+ * Returns the whole list of services.
+ *
+ * Useful for being able to
+ * to display them in a list box, for example.
+ * More memory consuming than the ones above, don't use unless
+ * really necessary.
+ * @return the list of all services
+ */
+ static List allServices();
+
+ /**
+ * Returns all services that require initialisation.
+ *
+ * Only needed by "kcminit"
+ * @return the list of all services that need to be initialized
+ */
+ static List allInitServices();
+
+ /**
+ * Returns a path that can be used to create a new KService based
+ * on @p suggestedName.
+ * @param showInMenu true, if the service should be shown in the TDE menu
+ * false, if the service should be hidden from the menu
+ * @param suggestedName name to base the file on, if a service with such
+ * name already exists, a prefix will be added to make it unique.
+ * @param menuId If provided, menuId will be set to the menu id to use for
+ * the KService
+ * @param reservedMenuIds If provided, the path and menu id will be chosen
+ * in such a way that the new menu id does not conflict with any
+ * of the reservedMenuIds
+ * @return The path to use for the new KService.
+ * @since 3.2
+ */
+ static TQString newServicePath(bool showInMenu, const TQString &suggestedName,
+ TQString *menuId = 0,
+ const TQStringList *reservedMenuIds = 0);
+
+
+ /**
+ * Rebuild KSycoca and show a progress dialog while doing so.
+ * @param parent Parent widget for the progress dialog
+ * @since 3.2
+ */
+ static void rebuildKSycoca(TQWidget *parent);
+
+protected:
+
+ void init(KDesktopFile *config);
+
+ TQStringList &accessServiceTypes() { return m_lstServiceTypes; }
+
+
+private:
+ KService( const KService& ); // forbidden
+ KService& operator=(const KService&);
+
+ TQString m_strType;
+ TQString m_strName;
+ TQString m_strExec;
+ TQString m_strIcon;
+ TQString m_strTerminalOptions;
+ TQString m_strPath;
+ TQString m_strComment;
+ TQString m_strLibrary;
+ TQStringList m_lstServiceTypes;
+ bool m_bAllowAsDefault;
+ int m_initialPreference;
+ bool m_bTerminal;
+ //bool m_bSuid;
+ //TQString m_strUsername;
+ TQString m_strDesktopEntryName;
+ //TQString m_docPath;
+ //bool m_bHideFromPanel;
+ DCOPServiceType_t m_DCOPServiceType;
+ TQMap<TQString,TQVariant> m_mapProps;
+ bool m_bValid;
+ TQStringList m_lstKeywords;
+ TQString m_strInit;
+ TQString m_strGenName;
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KServicePrivate;
+ KServicePrivate* d;
+};
+#endif
diff --git a/tdeio/tdeio/kservice_p.h b/tdeio/tdeio/kservice_p.h
new file mode 100644
index 000000000..180ab8fc3
--- /dev/null
+++ b/tdeio/tdeio/kservice_p.h
@@ -0,0 +1,41 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kservices_p_h__
+#define __kservices_p_h__
+
+#include <tqtimer.h>
+
+#include <kprogress.h>
+
+class KServiceProgressDialog : public KProgressDialog
+{
+ Q_OBJECT
+public:
+ KServiceProgressDialog(TQWidget *parent, const char *name,
+ const TQString &caption, const TQString &text);
+public slots:
+ void slotProgress();
+ void slotFinished();
+
+private:
+ TQTimer m_timer;
+ int m_timeStep;
+};
+
+#endif
diff --git a/tdeio/tdeio/kservicefactory.cpp b/tdeio/tdeio/kservicefactory.cpp
new file mode 100644
index 000000000..f4646fa75
--- /dev/null
+++ b/tdeio/tdeio/kservicefactory.cpp
@@ -0,0 +1,291 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 1999 David Faure <faure@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include "kservicefactory.h"
+#include "tdesycoca.h"
+#include "tdesycocatype.h"
+#include "tdesycocadict.h"
+#include "kservice.h"
+
+#include <tqstring.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kstaticdeleter.h>
+
+KServiceFactory::KServiceFactory()
+ : KSycocaFactory( KST_KServiceFactory )
+{
+ m_offerListOffset = 0;
+ m_nameDictOffset = 0;
+ m_relNameDictOffset = 0;
+ m_menuIdDictOffset = 0;
+ if (m_str)
+ {
+ // Read Header
+ TQ_INT32 i;
+ (*m_str) >> i;
+ m_nameDictOffset = i;
+ (*m_str) >> i;
+ m_relNameDictOffset = i;
+ (*m_str) >> i;
+ m_offerListOffset = i;
+ (*m_str) >> i;
+ m_initListOffset = i;
+ (*m_str) >> i;
+ m_menuIdDictOffset = i;
+
+ int saveOffset = m_str->device()->at();
+ // Init index tables
+ m_nameDict = new KSycocaDict(m_str, m_nameDictOffset);
+ // Init index tables
+ m_relNameDict = new KSycocaDict(m_str, m_relNameDictOffset);
+ // Init index tables
+ m_menuIdDict = new KSycocaDict(m_str, m_menuIdDictOffset);
+ saveOffset = m_str->device()->at(saveOffset);
+ }
+ else
+ {
+ // Build new database
+ m_nameDict = new KSycocaDict();
+ m_relNameDict = new KSycocaDict();
+ m_menuIdDict = new KSycocaDict();
+ }
+ _self = this;
+}
+
+KServiceFactory::~KServiceFactory()
+{
+ _self = 0L;
+ delete m_nameDict;
+ delete m_relNameDict;
+ delete m_menuIdDict;
+}
+
+KServiceFactory * KServiceFactory::self()
+{
+ if (!_self) {
+ _self = new KServiceFactory();
+ }
+ return _self;
+}
+
+KService * KServiceFactory::findServiceByName(const TQString &_name)
+{
+ if (!m_sycocaDict) return 0; // Error!
+
+ // Warning : this assumes we're NOT building a database
+ // But since findServiceByName isn't called in that case...
+ // [ see KServiceTypeFactory for how to do it if needed ]
+
+ int offset = m_sycocaDict->find_string( _name );
+ if (!offset) return 0; // Not found
+
+ KService * newService = createEntry(offset);
+
+ // Check whether the dictionary was right.
+ if (newService && (newService->name() != _name))
+ {
+ // No it wasn't...
+ delete newService;
+ newService = 0; // Not found
+ }
+ return newService;
+}
+
+KService * KServiceFactory::findServiceByDesktopName(const TQString &_name)
+{
+ if (!m_nameDict) return 0; // Error!
+
+ // Warning : this assumes we're NOT building a database
+ // But since findServiceByName isn't called in that case...
+ // [ see KServiceTypeFactory for how to do it if needed ]
+
+ int offset = m_nameDict->find_string( _name );
+ if (!offset) return 0; // Not found
+
+ KService * newService = createEntry(offset);
+
+ // Check whether the dictionary was right.
+ if (newService && (newService->desktopEntryName() != _name))
+ {
+ // No it wasn't...
+ delete newService;
+ newService = 0; // Not found
+ }
+ return newService;
+}
+
+KService * KServiceFactory::findServiceByDesktopPath(const TQString &_name)
+{
+ if (!m_relNameDict) return 0; // Error!
+
+ // Warning : this assumes we're NOT building a database
+ // But since findServiceByName isn't called in that case...
+ // [ see KServiceTypeFactory for how to do it if needed ]
+
+ int offset = m_relNameDict->find_string( _name );
+ if (!offset) return 0; // Not found
+
+ KService * newService = createEntry(offset);
+
+ // Check whether the dictionary was right.
+ if (newService && (newService->desktopEntryPath() != _name))
+ {
+ // No it wasn't...
+ delete newService;
+ newService = 0; // Not found
+ }
+ return newService;
+}
+
+KService * KServiceFactory::findServiceByMenuId(const TQString &_menuId)
+{
+ if (!m_menuIdDict) return 0; // Error!
+
+ // Warning : this assumes we're NOT building a database
+ // But since findServiceByMenuId isn't called in that case...
+ // [ see KServiceTypeFactory for how to do it if needed ]
+
+ int offset = m_menuIdDict->find_string( _menuId );
+ if (!offset) return 0; // Not found
+
+ KService * newService = createEntry(offset);
+
+ // Check whether the dictionary was right.
+ if (newService && (newService->menuId() != _menuId))
+ {
+ // No it wasn't...
+ delete newService;
+ newService = 0; // Not found
+ }
+ return newService;
+}
+
+KService* KServiceFactory::createEntry(int offset)
+{
+ KService * newEntry = 0L;
+ KSycocaType type;
+ TQDataStream *str = KSycoca::self()->findEntry(offset, type);
+ switch(type)
+ {
+ case KST_KService:
+ newEntry = new KService(*str, offset);
+ break;
+
+ default:
+ kdError(7011) << TQString(TQString("KServiceFactory: unexpected object entry in KSycoca database (type = %1)").arg((int)type)) << endl;
+ return 0;
+ }
+ if (!newEntry->isValid())
+ {
+ kdError(7011) << "KServiceFactory: corrupt object in KSycoca database!\n" << endl;
+ delete newEntry;
+ newEntry = 0;
+ }
+ return newEntry;
+}
+
+KService::List KServiceFactory::allServices()
+{
+ KService::List result;
+ KSycocaEntry::List list = allEntries();
+ for( KSycocaEntry::List::Iterator it = list.begin();
+ it != list.end();
+ ++it)
+ {
+ KService *newService = dynamic_cast<KService *>((*it).data());
+ if (newService)
+ result.append( KService::Ptr( newService ) );
+ }
+ return result;
+}
+
+KService::List KServiceFactory::allInitServices()
+{
+ KService::List list;
+ if (!m_str) return list;
+
+ // Assume we're NOT building a database
+
+ m_str->device()->at(m_initListOffset);
+ TQ_INT32 entryCount;
+ (*m_str) >> entryCount;
+
+ TQ_INT32 *offsetList = new TQ_INT32[entryCount];
+ for(int i = 0; i < entryCount; i++)
+ {
+ (*m_str) >> offsetList[i];
+ }
+
+ for(int i = 0; i < entryCount; i++)
+ {
+ KService *newEntry = createEntry(offsetList[i]);
+ if (newEntry)
+ {
+ list.append( KService::Ptr( newEntry ) );
+ }
+ }
+ delete [] offsetList;
+ return list;
+}
+
+KService::List KServiceFactory::offers( int serviceTypeOffset )
+{
+ KService::List list;
+
+ TQDataStream *str = m_str;
+ // Jump to the offer list
+ str->device()->at( m_offerListOffset );
+
+ TQ_INT32 aServiceTypeOffset;
+ TQ_INT32 aServiceOffset;
+ // We might want to do a binary search instead of a linear search
+ // since servicetype offsets are sorted. Bah.
+ while (true)
+ {
+ (*str) >> aServiceTypeOffset;
+ if ( aServiceTypeOffset )
+ {
+ (*str) >> aServiceOffset;
+ if ( aServiceTypeOffset == serviceTypeOffset )
+ {
+ // Save stream position !
+ int savedPos = str->device()->at();
+ // Create Service
+ KService * serv = createEntry( aServiceOffset );
+ if (serv)
+ list.append( KService::Ptr( serv ) );
+ // Restore position
+ str->device()->at( savedPos );
+ } else if ( aServiceTypeOffset > (TQ_INT32)serviceTypeOffset )
+ break; // too far
+ }
+ else
+ break; // 0 => end of list
+ }
+ return list;
+}
+
+KServiceFactory *KServiceFactory::_self = 0;
+
+void KServiceFactory::virtual_hook( int id, void* data )
+{ KSycocaFactory::virtual_hook( id, data ); }
+
diff --git a/tdeio/tdeio/kservicefactory.h b/tdeio/tdeio/kservicefactory.h
new file mode 100644
index 000000000..4e6df6534
--- /dev/null
+++ b/tdeio/tdeio/kservicefactory.h
@@ -0,0 +1,113 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kservicefactory_h__
+#define __kservicefactory_h__
+
+#include <tqstringlist.h>
+
+#include "kservice.h"
+#include "tdesycocafactory.h"
+#include <assert.h>
+
+class KSycoca;
+class KSycocaDict;
+
+/**
+ * @internal
+ * A sycoca factory for services (e.g. applications)
+ * It loads the services from parsing directories (e.g. applnk/)
+ * but can also create service from data streams or single config files
+ */
+class TDEIO_EXPORT KServiceFactory : public KSycocaFactory
+{
+ K_SYCOCAFACTORY( KST_KServiceFactory )
+public:
+ /**
+ * Create factory
+ */
+ KServiceFactory();
+ virtual ~KServiceFactory();
+
+ /**
+ * Construct a KService from a config file.
+ */
+ virtual KSycocaEntry *createEntry(const TQString &, const char *)
+ { assert(0); return 0; }
+
+ /**
+ * Find a service (by name, e.g. "Terminal")
+ */
+ KService * findServiceByName( const TQString &_name );
+
+ /**
+ * Find a service (by desktop file name, e.g. "konsole")
+ */
+ KService * findServiceByDesktopName( const TQString &_name );
+
+ /**
+ * Find a service ( by desktop path, e.g. "System/konsole.desktop")
+ */
+ KService * findServiceByDesktopPath( const TQString &_name );
+
+ /**
+ * Find a service ( by menu id, e.g. "tde-konsole.desktop")
+ */
+ KService * findServiceByMenuId( const TQString &_menuId );
+
+ /**
+ * @return the services supporting the given service type
+ */
+ KService::List offers( int serviceTypeOffset );
+
+ /**
+ * @return all services. Very memory consuming, avoid using.
+ */
+ KService::List allServices();
+
+ /**
+ * @return all services which have a "X-TDE-Init" line.
+ */
+ KService::List allInitServices();
+
+ /**
+ * @return the unique service factory, creating it if necessary
+ */
+ static KServiceFactory * self();
+
+protected:
+ virtual KService * createEntry(int offset);
+ int m_offerListOffset;
+ int m_initListOffset;
+ KSycocaDict *m_nameDict;
+ int m_nameDictOffset;
+ KSycocaDict *m_relNameDict;
+ int m_relNameDictOffset;
+ KSycocaDict *m_menuIdDict;
+ int m_menuIdDictOffset;
+
+private:
+ static KServiceFactory *_self;
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KServiceFactoryPrivate* d;
+};
+
+#endif
diff --git a/tdeio/tdeio/kservicegroup.cpp b/tdeio/tdeio/kservicegroup.cpp
new file mode 100644
index 000000000..2e27c99b0
--- /dev/null
+++ b/tdeio/tdeio/kservicegroup.cpp
@@ -0,0 +1,724 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include <tqdir.h>
+
+#include <kiconloader.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <ksortablevaluelist.h>
+
+#include "kservicefactory.h"
+#include "kservicegroupfactory.h"
+#include "kservicegroup.h"
+#include "kservice.h"
+#include "tdesycoca.h"
+
+class KServiceGroup::Private
+{
+public:
+ Private() { m_bNoDisplay = false; m_bShowEmptyMenu = false;m_bShowInlineHeader=false;m_bInlineAlias=false; m_bAllowInline = false; m_inlineValue = 4; m_bShortMenu = false; m_bGeneralDescription = false;}
+ bool m_bNoDisplay;
+ bool m_bShortMenu;
+ bool m_bGeneralDescription;
+ bool m_bShowEmptyMenu;
+ bool m_bShowInlineHeader;
+ bool m_bInlineAlias;
+ bool m_bAllowInline;
+ int m_inlineValue;
+ TQStringList suppressGenericNames;
+ TQString directoryEntryPath;
+ TQStringList sortOrder;
+};
+
+KServiceGroup::KServiceGroup( const TQString & name )
+ : KSycocaEntry(name), m_childCount(-1)
+{
+ d = new KServiceGroup::Private;
+ m_bDeleted = false;
+ m_bDeep = false;
+}
+
+KServiceGroup::KServiceGroup( const TQString &configFile, const TQString & _relpath )
+ : KSycocaEntry(_relpath), m_childCount(-1)
+{
+ d = new KServiceGroup::Private;
+ m_bDeleted = false;
+ m_bDeep = false;
+
+ TQString cfg = configFile;
+ if (cfg.isEmpty())
+ cfg = _relpath+".directory";
+
+ d->directoryEntryPath = cfg;
+
+ KDesktopFile config( cfg, true, "apps" );
+
+ m_strCaption = config.readName();
+ m_strIcon = config.readIcon();
+ m_strComment = config.readComment();
+ m_bDeleted = config.readBoolEntry( "Hidden", false );
+ d->m_bNoDisplay = config.readBoolEntry( "NoDisplay", false );
+ if (d->directoryEntryPath.startsWith(TQDir::homeDirPath()))
+ d->m_bShortMenu = false;
+ else
+ d->m_bShortMenu = config.readBoolEntry( "X-SuSE-AutoShortMenu", false );
+ d->m_bGeneralDescription = config.readBoolEntry( "X-SuSE-GeneralDescription", false );
+ TQStringList tmpList;
+ if (config.hasKey("OnlyShowIn"))
+ {
+ if ((!config.readListEntry("OnlyShowIn", ';').contains("TDE")) && (!config.readListEntry("OnlyShowIn", ';').contains("KDE")))
+ d->m_bNoDisplay = true;
+ }
+ if (config.hasKey("NotShowIn"))
+ {
+ if ((config.readListEntry("NotShowIn", ';').contains("TDE")) || (config.readListEntry("NotShowIn", ';').contains("KDE")))
+ d->m_bNoDisplay = true;
+ }
+
+ m_strBaseGroupName = config.readEntry( "X-TDE-BaseGroup" );
+ d->suppressGenericNames = config.readListEntry( "X-TDE-SuppressGenericNames" );
+ d->sortOrder = config.readListEntry("SortOrder");
+
+ // Fill in defaults.
+ if (m_strCaption.isEmpty())
+ {
+ m_strCaption = _relpath;
+ if (m_strCaption.right(1) == "/")
+ m_strCaption = m_strCaption.left(m_strCaption.length()-1);
+ int i = m_strCaption.findRev('/');
+ if (i > 0)
+ m_strCaption = m_strCaption.mid(i+1);
+ }
+ if (m_strIcon.isEmpty())
+ m_strIcon = "folder";
+}
+
+KServiceGroup::KServiceGroup( TQDataStream& _str, int offset, bool deep ) :
+ KSycocaEntry( _str, offset )
+{
+ d = new KServiceGroup::Private;
+ m_bDeep = deep;
+ load( _str );
+}
+
+KServiceGroup::~KServiceGroup()
+{
+ delete d;
+}
+
+int KServiceGroup::childCount()
+{
+ if (m_childCount == -1)
+ {
+ TDEConfig global("kdeglobals");
+ global.setGroup("KDE");
+ bool showUnimportant = global.readBoolEntry("showUnimportant", true);
+
+ m_childCount = 0;
+
+ for( List::ConstIterator it = m_serviceList.begin();
+ it != m_serviceList.end(); it++)
+ {
+ KSycocaEntry *p = (*it);
+ if (p->isType(KST_KService))
+ {
+ KService *service = static_cast<KService *>(p);
+ if (!service->noDisplay())
+ if ( showUnimportant || !service->SuSEunimportant() )
+ m_childCount++;
+ }
+ else if (p->isType(KST_KServiceGroup))
+ {
+ KServiceGroup *serviceGroup = static_cast<KServiceGroup *>(p);
+ m_childCount += serviceGroup->childCount();
+ }
+ }
+ }
+ return m_childCount;
+}
+
+
+bool KServiceGroup::showInlineHeader() const
+{
+ return d->m_bShowInlineHeader;
+}
+
+bool KServiceGroup::showEmptyMenu() const
+{
+ return d->m_bShowEmptyMenu;
+}
+
+bool KServiceGroup::inlineAlias() const
+{
+ return d->m_bInlineAlias;
+}
+
+void KServiceGroup::setInlineAlias(bool _b)
+{
+ d->m_bInlineAlias = _b;
+}
+
+void KServiceGroup::setShowEmptyMenu(bool _b)
+{
+ d->m_bShowEmptyMenu=_b;
+}
+
+void KServiceGroup::setShowInlineHeader(bool _b)
+{
+ d->m_bShowInlineHeader=_b;
+}
+
+int KServiceGroup::inlineValue() const
+{
+ return d->m_inlineValue;
+}
+
+void KServiceGroup::setInlineValue(int _val)
+{
+ d->m_inlineValue = _val;
+}
+
+bool KServiceGroup::allowInline() const
+{
+ return d->m_bAllowInline;
+}
+
+void KServiceGroup::setAllowInline(bool _b)
+{
+ d->m_bAllowInline = _b;
+}
+
+bool KServiceGroup::noDisplay() const
+{
+ return d->m_bNoDisplay || m_strCaption.startsWith(".");
+}
+
+TQStringList KServiceGroup::suppressGenericNames() const
+{
+ return d->suppressGenericNames;
+}
+
+bool KServiceGroup::SuSEgeneralDescription() const
+{
+ return d->m_bGeneralDescription;
+}
+
+bool KServiceGroup::SuSEshortMenu() const
+{
+ return d->m_bShortMenu;
+}
+
+void KServiceGroup::load( TQDataStream& s )
+{
+ TQStringList groupList;
+ TQ_INT8 noDisplay;
+ TQ_INT8 _showEmptyMenu;
+ TQ_INT8 inlineHeader;
+ TQ_INT8 _inlineAlias;
+ TQ_INT8 _allowInline;
+ s >> m_strCaption >> m_strIcon >>
+ m_strComment >> groupList >> m_strBaseGroupName >> m_childCount >>
+ noDisplay >> d->suppressGenericNames >> d->directoryEntryPath >>
+ d->sortOrder >> _showEmptyMenu >> inlineHeader >> _inlineAlias >>
+ _allowInline >> d->m_bShortMenu >> d->m_bGeneralDescription;
+
+ d->m_bNoDisplay = (noDisplay != 0);
+ d->m_bShowEmptyMenu = ( _showEmptyMenu != 0 );
+ d->m_bShowInlineHeader = ( inlineHeader != 0 );
+ d->m_bInlineAlias = ( _inlineAlias != 0 );
+ d->m_bAllowInline = ( _allowInline != 0 );
+
+ if (m_bDeep)
+ {
+ for(TQStringList::ConstIterator it = groupList.begin();
+ it != groupList.end(); it++)
+ {
+ TQString path = *it;
+ if (path[path.length()-1] == '/')
+ {
+ KServiceGroup *serviceGroup;
+ serviceGroup = KServiceGroupFactory::self()->findGroupByDesktopPath(path, false);
+ if (serviceGroup)
+ m_serviceList.append( SPtr(serviceGroup) );
+ }
+ else
+ {
+ KService *service;
+ service = KServiceFactory::self()->findServiceByDesktopPath(path);
+ if (service)
+ m_serviceList.append( SPtr(service) );
+ }
+ }
+ }
+}
+
+void KServiceGroup::addEntry( KSycocaEntry *entry)
+{
+ m_serviceList.append(entry);
+}
+
+void KServiceGroup::save( TQDataStream& s )
+{
+ KSycocaEntry::save( s );
+
+ TQStringList groupList;
+ for( List::ConstIterator it = m_serviceList.begin();
+ it != m_serviceList.end(); it++)
+ {
+ KSycocaEntry *p = (*it);
+ if (p->isType(KST_KService))
+ {
+ KService *service = static_cast<KService *>(p);
+ groupList.append( service->desktopEntryPath());
+ }
+ else if (p->isType(KST_KServiceGroup))
+ {
+ KServiceGroup *serviceGroup = static_cast<KServiceGroup *>(p);
+ groupList.append( serviceGroup->relPath());
+ }
+ else
+ {
+ //fprintf(stderr, "KServiceGroup: Unexpected object in list!\n");
+ }
+ }
+
+ (void) childCount();
+
+ TQ_INT8 noDisplay = d->m_bNoDisplay ? 1 : 0;
+ TQ_INT8 _showEmptyMenu = d->m_bShowEmptyMenu ? 1 : 0;
+ TQ_INT8 inlineHeader = d->m_bShowInlineHeader ? 1 : 0;
+ TQ_INT8 _inlineAlias = d->m_bInlineAlias ? 1 : 0;
+ TQ_INT8 _allowInline = d->m_bAllowInline ? 1 : 0;
+ s << m_strCaption << m_strIcon <<
+ m_strComment << groupList << m_strBaseGroupName << m_childCount <<
+ noDisplay << d->suppressGenericNames << d->directoryEntryPath <<
+ d->sortOrder <<_showEmptyMenu <<inlineHeader<<_inlineAlias<<_allowInline <<
+ d->m_bShortMenu << d->m_bGeneralDescription;
+}
+
+KServiceGroup::List
+KServiceGroup::entries(bool sort)
+{
+ return entries(sort, true);
+}
+
+KServiceGroup::List
+KServiceGroup::entries(bool sort, bool excludeNoDisplay)
+{
+ return entries(sort, excludeNoDisplay, false);
+}
+
+static void addItem(KServiceGroup::List &sorted, const KSycocaEntry::Ptr &p, bool &addSeparator)
+{
+ if (addSeparator && !sorted.isEmpty())
+ sorted.append(new KServiceSeparator());
+ sorted.append(p);
+ addSeparator = false;
+}
+
+KServiceGroup::List
+KServiceGroup::entries(bool sort, bool excludeNoDisplay, bool allowSeparators, bool sortByGenericName)
+{
+ return SuSEentries(sort, excludeNoDisplay, allowSeparators, sortByGenericName);
+}
+
+KServiceGroup::List
+KServiceGroup::SuSEentries(bool sort, bool excludeNoDisplay, bool allowSeparators, bool sortByGenericName, bool excludeSuSEunimportant)
+{
+ KServiceGroup *group = this;
+
+ // If the entries haven't been loaded yet, we have to reload ourselves
+ // together with the entries. We can't only load the entries afterwards
+ // since the offsets could have been changed if the database has changed.
+
+ if (!m_bDeep) {
+
+ group =
+ KServiceGroupFactory::self()->findGroupByDesktopPath(relPath(), true);
+
+ if (0 == group) // No guarantee that we still exist!
+ return List();
+ }
+
+ if (!sort)
+ return group->m_serviceList;
+
+ // Sort the list alphabetically, according to locale.
+ // Groups come first, then services.
+
+ KSortableValueList<SPtr,TQCString> slist;
+ KSortableValueList<SPtr,TQCString> glist;
+ for (List::ConstIterator it(group->m_serviceList.begin()); it != group->m_serviceList.end(); ++it)
+ {
+ KSycocaEntry *p = (*it);
+// if( !p->isType(KST_KServiceGroup) && !p->isType(KST_KService))
+// continue;
+ bool noDisplay = p->isType(KST_KServiceGroup) ?
+ static_cast<KServiceGroup *>(p)->noDisplay() :
+ static_cast<KService *>(p)->noDisplay();
+ if (excludeNoDisplay && noDisplay)
+ continue;
+ bool SuSEunimportant = p->isType(KST_KService) &&
+ static_cast<KService *>(p)->SuSEunimportant();
+ if (excludeSuSEunimportant && SuSEunimportant)
+ continue;
+
+ // Choose the right list
+ KSortableValueList<SPtr,TQCString> & list = p->isType(KST_KServiceGroup) ? glist : slist;
+ TQString name;
+ if (p->isType(KST_KServiceGroup))
+ name = static_cast<KServiceGroup *>(p)->caption();
+ else if (sortByGenericName)
+ name = static_cast<KService *>(p)->genericName() + " " + p->name();
+ else
+ name = p->name() + " " + static_cast<KService *>(p)->genericName();
+
+ TQCString key( name.length() * 4 + 1 );
+ // strxfrm() crashes on Solaris
+#ifndef USE_SOLARIS
+ // maybe it'd be better to use wcsxfrm() where available
+ size_t ln = strxfrm( key.data(), name.local8Bit().data(), key.size());
+ if( ln != size_t( -1 ))
+ {
+ if( ln >= key.size())
+ { // didn't fit?
+ key.resize( ln + 1 );
+ if( strxfrm( key.data(), name.local8Bit().data(), key.size()) == size_t( -1 ))
+ key = name.local8Bit();
+ }
+ }
+ else
+#endif
+ {
+ key = name.local8Bit();
+ }
+ list.insert(key,SPtr(*it));
+ }
+
+ return group->SuSEsortEntries( slist, glist, excludeNoDisplay, allowSeparators );
+}
+
+KServiceGroup::List
+KServiceGroup::SuSEsortEntries( KSortableValueList<SPtr,TQCString> slist, KSortableValueList<SPtr,TQCString> glist, bool excludeNoDisplay, bool allowSeparators )
+{
+ KServiceGroup *group = this;
+
+ // Now sort
+ slist.sort();
+ glist.sort();
+
+ if (d->sortOrder.isEmpty())
+ {
+ d->sortOrder << ":M";
+ d->sortOrder << ":F";
+ d->sortOrder << ":OIH IL[4]"; //just inline header
+ }
+
+ TQString rp = relPath();
+ if(rp == "/") rp = TQString::null;
+
+ // Iterate through the sort spec list.
+ // If an entry gets mentioned explicitly, we remove it from the sorted list
+ for (TQStringList::ConstIterator it(d->sortOrder.begin()); it != d->sortOrder.end(); ++it)
+ {
+ const TQString &item = *it;
+ if (item.isEmpty()) continue;
+ if (item[0] == '/')
+ {
+ TQString groupPath = rp + item.mid(1) + "/";
+ // Remove entry from sorted list of services.
+ for(KSortableValueList<SPtr,TQCString>::Iterator it2 = glist.begin(); it2 != glist.end(); ++it2)
+ {
+ KServiceGroup *group = (KServiceGroup *)((KSycocaEntry *)((*it2).value()));
+ if (group->relPath() == groupPath)
+ {
+ glist.remove(it2);
+ break;
+ }
+ }
+ }
+ else if (item[0] != ':')
+ {
+ // Remove entry from sorted list of services.
+ // TODO: Remove item from sortOrder-list if not found
+ // TODO: This prevents duplicates
+ for(KSortableValueList<SPtr,TQCString>::Iterator it2 = slist.begin(); it2 != slist.end(); ++it2)
+ {
+ if (!(*it2).value()->isType(KST_KService))
+ continue;
+ KService *service = (KService *)((KSycocaEntry *)((*it2).value()));
+ if (service->menuId() == item)
+ {
+ slist.remove(it2);
+ break;
+ }
+ }
+ }
+ }
+
+ List sorted;
+
+ bool needSeparator = false;
+ // Iterate through the sort spec list.
+ // Add the entries to the list according to the sort spec.
+ for (TQStringList::ConstIterator it(d->sortOrder.begin()); it != d->sortOrder.end(); ++it)
+ {
+ const TQString &item = *it;
+ if (item.isEmpty()) continue;
+ if (item[0] == ':')
+ {
+ // Special condition...
+ if (item == ":S")
+ {
+ if (allowSeparators)
+ needSeparator = true;
+ }
+ else if ( item.contains( ":O" ) )
+ {
+ //todo parse attribute:
+ TQString tmp( item );
+ tmp = tmp.remove(":O");
+ TQStringList optionAttribute = TQStringList::split(" ",tmp);
+ if( optionAttribute.count()==0)
+ optionAttribute.append(tmp);
+ bool showEmptyMenu = false;
+ bool showInline = false;
+ bool showInlineHeader = false;
+ bool showInlineAlias = false;
+ int inlineValue = -1;
+
+ for ( TQStringList::Iterator it3 = optionAttribute.begin(); it3 != optionAttribute.end(); ++it3 )
+ {
+ parseAttribute( *it3, showEmptyMenu, showInline, showInlineHeader, showInlineAlias, inlineValue );
+ }
+ for(KSortableValueList<SPtr,TQCString>::Iterator it2 = glist.begin(); it2 != glist.end(); ++it2)
+ {
+ KServiceGroup *group = (KServiceGroup *)((KSycocaEntry *)(*it2).value());
+ group->setShowEmptyMenu( showEmptyMenu );
+ group->setAllowInline( showInline );
+ group->setShowInlineHeader( showInlineHeader );
+ group->setInlineAlias( showInlineAlias );
+ group->setInlineValue( inlineValue );
+ }
+
+ }
+ else if (item == ":M")
+ {
+ // Add sorted list of sub-menus
+ for(KSortableValueList<SPtr,TQCString>::Iterator it2 = glist.begin(); it2 != glist.end(); ++it2)
+ {
+ addItem(sorted, (*it2).value(), needSeparator);
+ }
+ }
+ else if (item == ":F")
+ {
+ // Add sorted list of services
+ for(KSortableValueList<SPtr,TQCString>::Iterator it2 = slist.begin(); it2 != slist.end(); ++it2)
+ {
+ addItem(sorted, (*it2).value(), needSeparator);
+ }
+ }
+ else if (item == ":A")
+ {
+ // Add sorted lists of services and submenus
+ KSortableValueList<SPtr,TQCString>::Iterator it_s = slist.begin();
+ KSortableValueList<SPtr,TQCString>::Iterator it_g = glist.begin();
+
+ while(true)
+ {
+ if (it_s == slist.end())
+ {
+ if (it_g == glist.end())
+ break; // Done
+
+ // Insert remaining sub-menu
+ addItem(sorted, (*it_g).value(), needSeparator);
+ it_g++;
+ }
+ else if (it_g == glist.end())
+ {
+ // Insert remaining service
+ addItem(sorted, (*it_s).value(), needSeparator);
+ it_s++;
+ }
+ else if ((*it_g).index() < (*it_s).index())
+ {
+ // Insert sub-menu first
+ addItem(sorted, (*it_g).value(), needSeparator);
+ it_g++;
+ }
+ else
+ {
+ // Insert service first
+ addItem(sorted, (*it_s).value(), needSeparator);
+ it_s++;
+ }
+ }
+ }
+ }
+ else if (item[0] == '/')
+ {
+ TQString groupPath = rp + item.mid(1) + "/";
+
+ for (List::ConstIterator it2(group->m_serviceList.begin()); it2 != group->m_serviceList.end(); ++it2)
+ {
+ if (!(*it2)->isType(KST_KServiceGroup))
+ continue;
+ KServiceGroup *group = (KServiceGroup *)((KSycocaEntry *)(*it2));
+ if (group->relPath() == groupPath)
+ {
+ if (!excludeNoDisplay || !group->noDisplay())
+ {
+ const TQString &nextItem = *( ++it );
+ if ( nextItem.startsWith( ":O" ) )
+ {
+ TQString tmp( nextItem );
+ tmp = tmp.remove(":O");
+ TQStringList optionAttribute = TQStringList::split(" ",tmp);
+ if( optionAttribute.count()==0)
+ optionAttribute.append(tmp);
+ bool bShowEmptyMenu = false;
+ bool bShowInline = false;
+ bool bShowInlineHeader = false;
+ bool bShowInlineAlias = false;
+ int inlineValue = -1;
+ for ( TQStringList::Iterator it3 = optionAttribute.begin(); it3 != optionAttribute.end(); ++it3 )
+ {
+ parseAttribute( *it3 , bShowEmptyMenu, bShowInline, bShowInlineHeader, bShowInlineAlias , inlineValue );
+ group->setShowEmptyMenu( bShowEmptyMenu );
+ group->setAllowInline( bShowInline );
+ group->setShowInlineHeader( bShowInlineHeader );
+ group->setInlineAlias( bShowInlineAlias );
+ group->setInlineValue( inlineValue );
+ }
+ }
+ else
+ it--;
+
+ addItem(sorted, (group), needSeparator);
+ }
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (List::ConstIterator it2(group->m_serviceList.begin()); it2 != group->m_serviceList.end(); ++it2)
+ {
+ if (!(*it2)->isType(KST_KService))
+ continue;
+ KService *service = (KService *)((KSycocaEntry *)(*it2));
+ if (service->menuId() == item)
+ {
+ if (!excludeNoDisplay || !service->noDisplay())
+ addItem(sorted, (*it2), needSeparator);
+ break;
+ }
+ }
+ }
+ }
+
+ return sorted;
+}
+
+void KServiceGroup::parseAttribute( const TQString &item , bool &showEmptyMenu, bool &showInline, bool &showInlineHeader, bool & showInlineAlias , int &inlineValue )
+{
+ if( item == "ME") //menu empty
+ showEmptyMenu=true;
+ else if ( item == "NME") //not menu empty
+ showEmptyMenu=false;
+ else if( item == "I") //inline menu !
+ showInline = true;
+ else if ( item == "NI") //not inline menu!
+ showInline = false;
+ else if( item == "IH") //inline header!
+ showInlineHeader= true;
+ else if ( item == "NIH") //not inline header!
+ showInlineHeader = false;
+ else if( item == "IA") //inline alias!
+ showInlineAlias = true;
+ else if ( item == "NIA") //not inline alias!
+ showInlineAlias = false;
+ else if( ( item ).contains( "IL" )) //inline limite!
+ {
+ TQString tmp( item );
+ tmp = tmp.remove( "IL[" );
+ tmp = tmp.remove( "]" );
+ bool ok;
+ int _inlineValue = tmp.toInt(&ok);
+ if ( !ok ) //error
+ _inlineValue = -1;
+ inlineValue = _inlineValue;
+ }
+ else
+ kdDebug()<<" This attribute is not supported :"<<item<<endl;
+}
+
+void KServiceGroup::setLayoutInfo(const TQStringList &layout)
+{
+ d->sortOrder = layout;
+}
+
+TQStringList KServiceGroup::layoutInfo() const
+{
+ return d->sortOrder;
+}
+
+KServiceGroup::Ptr
+KServiceGroup::baseGroup( const TQString & _baseGroupName )
+{
+ return KServiceGroupFactory::self()->findBaseGroup(_baseGroupName, true);
+}
+
+KServiceGroup::Ptr
+KServiceGroup::root()
+{
+ return KServiceGroupFactory::self()->findGroupByDesktopPath("/", true);
+}
+
+KServiceGroup::Ptr
+KServiceGroup::group(const TQString &relPath)
+{
+ if (relPath.isEmpty()) return root();
+ return KServiceGroupFactory::self()->findGroupByDesktopPath(relPath, true);
+}
+
+KServiceGroup::Ptr
+KServiceGroup::childGroup(const TQString &parent)
+{
+ return KServiceGroupFactory::self()->findGroupByDesktopPath("#parent#"+parent, true);
+}
+
+TQString
+KServiceGroup::directoryEntryPath() const
+{
+ return d->directoryEntryPath;
+}
+
+
+void KServiceGroup::virtual_hook( int id, void* data )
+{ KSycocaEntry::virtual_hook( id, data ); }
+
+
+KServiceSeparator::KServiceSeparator( )
+ : KSycocaEntry("separator")
+{
+}
diff --git a/tdeio/tdeio/kservicegroup.h b/tdeio/tdeio/kservicegroup.h
new file mode 100644
index 000000000..f2cd5a09f
--- /dev/null
+++ b/tdeio/tdeio/kservicegroup.h
@@ -0,0 +1,353 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kservicegroup_h__
+#define __kservicegroup_h__
+
+#include <tqptrlist.h>
+#include <tqstring.h>
+#include <tqshared.h>
+#include <tqdatastream.h>
+#include <tqvariant.h>
+
+#include <kdesktopfile.h>
+#include <ksortablevaluelist.h>
+
+#include "tdesycocaentry.h"
+#include "tdesycocatype.h"
+#include "kservice.h"
+
+class KBuildServiceGroupFactory;
+
+/**
+ * KServiceGroup represents a group of service, for example
+ * screensavers.
+ * This class is typically used like this:
+ *
+ * \code
+ * // Lookup screensaver group
+ * KServiceGroup::Ptr group = KServiceGroup::baseGroup("screensavers");
+ * if (!group || !group->isValid()) return;
+ *
+ * KServiceGroup::List list = group->entries();
+ *
+ * // Iterate over all entries in the group
+ * for( KServiceGroup::List::ConstIterator it = list.begin();
+ * it != list.end(); it++)
+ * {
+ * KSycocaEntry *p = (*it);
+ * if (p->isType(KST_KService))
+ * {
+ * KService *s = static_cast<KService *>(p);
+ * printf("Name = %s\n", s->name().latin1());
+ * }
+ * else if (p->isType(KST_KServiceGroup))
+ * {
+ * KServiceGroup *g = static_cast<KServiceGroup *>(p);
+ * // Sub group ...
+ * }
+ * }
+ * \endcode
+ * @short Represents a group of services
+ */
+class TDEIO_EXPORT KServiceGroup : public KSycocaEntry
+{
+ friend class KBuildServiceGroupFactory;
+ K_SYCOCATYPE( KST_KServiceGroup, KSycocaEntry )
+
+public:
+ typedef KSharedPtr<KServiceGroup> Ptr;
+ typedef KSharedPtr<KSycocaEntry> SPtr;
+ typedef TQValueList<SPtr> List;
+public:
+ /**
+ * Construct a dummy servicegroup indexed with @p name.
+ * @param name the name of the service group
+ * @since 3.1
+ */
+ KServiceGroup( const TQString & name );
+
+ /**
+ * Construct a service and take all informations from a config file
+ * @param _fullpath full path to the config file
+ * @param _relpath relative path to the config file
+ */
+ KServiceGroup( const TQString & _fullpath, const TQString & _relpath );
+
+ /**
+ * @internal construct a service from a stream.
+ * The stream must already be positionned at the correct offset
+ */
+ KServiceGroup( TQDataStream& _str, int offset, bool deep );
+
+ virtual ~KServiceGroup();
+
+ /**
+ * Checks whether the entry is valid, returns always true.
+ * @return true
+ */
+ bool isValid() const { return true; }
+
+ /**
+ * Name used for indexing.
+ * @return the service group's name
+ */
+ virtual TQString name() const { return entryPath(); }
+
+ /**
+ * Returns the relative path of the service group.
+ * @return the service group's relative path
+ */
+ virtual TQString relPath() const { return entryPath(); }
+
+ /**
+ * Returns the caption of this group.
+ * @return the caption of this group
+ */
+ TQString caption() const { return m_strCaption; }
+
+ /**
+ * Returns the name of the icon associated with the group.
+ * @return the name of the icon associated with the group,
+ * or TQString::null if not set
+ */
+ TQString icon() const { return m_strIcon; }
+
+ /**
+ * Returns the comment about this service group.
+ * @return the descriptive comment for the group, if there is one,
+ * or TQString::null if not set
+ */
+ TQString comment() const { return m_strComment; }
+
+ /**
+ * Returns the total number of displayable services in this group and
+ * any of its subgroups.
+ * @return the number of child services
+ */
+ int childCount();
+
+ /**
+ * Returns true if the NoDisplay flag was set, i.e. if this
+ * group should be hidden from menus, while still being in tdesycoca.
+ * @return true to hide this service group, false to display it
+ * @since 3.1
+ */
+ bool noDisplay() const;
+
+ /**
+ * Return true if we want to display empty menu entry
+ * @return true to show this service group as menu entry is empty, false to hide it
+ * @since 3.4
+ */
+ bool showEmptyMenu() const;
+ void setShowEmptyMenu( bool b);
+
+ /**
+ * @return true to show an inline header into menu
+ * @since 3.5
+ */
+ bool showInlineHeader() const;
+ void setShowInlineHeader(bool _b);
+
+ /**
+ * @return true to show an inline alias item into menu
+ * @since 3.5
+ */
+ bool inlineAlias() const;
+ void setInlineAlias(bool _b);
+ /**
+ * @return true if we allow to inline menu.
+ * @since 3.5
+ */
+ bool allowInline() const;
+ void setAllowInline(bool _b);
+
+ /**
+ * @return inline limite value
+ * @since 3.5
+ */
+ int inlineValue() const;
+ void setInlineValue(int _val);
+
+
+ /**
+ * Returns a list of untranslated generic names that should be
+ * be supressed when showing this group.
+ * E.g. The group "Games/Arcade" might want to suppress the generic name
+ * "Arcade Game" since it's redundant in this particular context.
+ * @since 3.2
+ */
+ TQStringList suppressGenericNames() const;
+
+ /**
+ * @internal
+ * Sets information related to the layout of services in this group.
+ */
+ void setLayoutInfo(const TQStringList &layout);
+
+ /**
+ * Original API and feature kindly provided by SuSE
+ */
+ bool SuSEshortMenu() const;
+ bool SuSEgeneralDescription() const;
+
+ /**
+ * @internal
+ * Returns information related to the layout of services in this group.
+ */
+ TQStringList layoutInfo() const;
+
+ /**
+ * @internal
+ * Load the service from a stream.
+ */
+ virtual void load( TQDataStream& );
+ /**
+ * @internal
+ * Save the service to a stream.
+ */
+ virtual void save( TQDataStream& );
+
+ /**
+ * List of all Services and ServiceGroups within this
+ * ServiceGroup.
+ * @param sorted true to sort items
+ * @param excludeNoDisplay true to exclude items marked "NoDisplay"
+ * @param allowSeparators true to allow separator items to be included
+ * @param sortByGenericName true to sort GenericName+Name instead of Name+GenericName
+ * @return the list of entries
+ * @since 3.2
+ */
+ List entries(bool sorted, bool excludeNoDisplay, bool allowSeparators, bool sortByGenericName=false);
+ virtual List entries(bool sorted, bool excludeNoDisplay);
+
+ /**
+ * List of all Services and ServiceGroups within this
+ * ServiceGroup.
+ * @param sorted true to sort items
+ * @return the list of entried
+ */
+ virtual List entries(bool sorted = false);
+
+ /*
+ * Original API and feature kindly provided by SuSE
+ */
+ virtual List SuSEentries(bool sort, bool excludeNoDisplay, bool allowSeparators, bool sortByGenericName, bool excludeSuSEunimportant = false);
+ virtual List SuSEsortEntries( KSortableValueList<SPtr,TQCString> slist, KSortableValueList<SPtr,TQCString> glist, bool excludeNoDisplay, bool allowSeparators );
+
+ /**
+ * Returns a non-empty string if the group is a special base group.
+ * By default, "Settings/" is the kcontrol base group ("settings")
+ * and "System/Screensavers/" is the screensavers base group ("screensavers").
+ * This allows moving the groups without breaking those apps.
+ *
+ * The base group is defined by the X-TDE-BaseGroup key
+ * in the .directory file.
+ * @return the base group name, or null if no base group
+ */
+ TQString baseGroupName() const { return m_strBaseGroupName; }
+
+ /**
+ * Returns a path to the .directory file describing this service group.
+ * The path is either absolute or relative to the "apps" resource.
+ * @since 3.2
+ */
+ TQString directoryEntryPath() const;
+
+ /**
+ * Returns the group for the given baseGroupName.
+ * Can return 0L if the directory (or the .directory file) was deleted.
+ * @return the base group with the given name, or 0 if not available.
+ */
+ static Ptr baseGroup( const TQString &baseGroupName );
+
+ /**
+ * Returns the root service group.
+ * @return the root service group
+ */
+ static Ptr root();
+
+ /**
+ * Returns the group with the given relative path.
+ * @param relPath the path of the service group
+ * @return the group with the given relative path name.
+ */
+ static Ptr group(const TQString &relPath);
+
+ /**
+ * Returns the group of services that have X-TDE-ParentApp equal
+ * to @p parent (siblings).
+ * @param parent the name of the service's parent
+ * @return the services group
+ * @since 3.1
+ */
+ static Ptr childGroup(const TQString &parent);
+
+ /**
+ * This function parse attributes into menu
+ * @since 3.5
+ */
+ void parseAttribute( const TQString &item , bool &showEmptyMenu, bool &showInline, bool &showInlineHeader, bool & showInlineAlias ,int &inlineValue );
+
+protected:
+ /**
+ * @internal
+ * Add a service to this group
+ */
+ void addEntry( KSycocaEntry *entry);
+
+ TQString m_strCaption;
+ TQString m_strIcon;
+ TQString m_strComment;
+
+ List m_serviceList;
+ bool m_bDeep;
+ TQString m_strBaseGroupName;
+ int m_childCount;
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class Private;
+ Private* d;
+};
+
+class TDEIO_EXPORT KServiceSeparator : public KSycocaEntry
+{
+ K_SYCOCATYPE( KST_KServiceSeparator, KSycocaEntry )
+
+public:
+ typedef KSharedPtr<KServiceSeparator> Ptr;
+public:
+ /**
+ * Construct a service separator
+ * @since 3.2
+ */
+ KServiceSeparator();
+
+ bool isValid() const { return true; }
+
+ // Dummy
+ virtual TQString name() const { return "separator"; }
+ // Dummy
+ virtual void load( TQDataStream& ) { };
+ // Dummy
+ virtual void save( TQDataStream& ) { };
+};
+
+#endif
diff --git a/tdeio/tdeio/kservicegroupfactory.cpp b/tdeio/tdeio/kservicegroupfactory.cpp
new file mode 100644
index 000000000..56ec0c07f
--- /dev/null
+++ b/tdeio/tdeio/kservicegroupfactory.cpp
@@ -0,0 +1,148 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include "kservicegroupfactory.h"
+#include "tdesycoca.h"
+#include "tdesycocatype.h"
+#include "tdesycocadict.h"
+#include "kservice.h"
+
+#include <tqstring.h>
+
+#include <klocale.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+
+KServiceGroupFactory::KServiceGroupFactory()
+ : KSycocaFactory( KST_KServiceGroupFactory )
+{
+ m_baseGroupDictOffset = 0;
+ if (m_str)
+ {
+ // Read Header
+ TQ_INT32 i;
+ (*m_str) >> i;
+ m_baseGroupDictOffset = i;
+
+ int saveOffset = m_str->device()->at();
+ // Init index tables
+ m_baseGroupDict = new KSycocaDict(m_str, m_baseGroupDictOffset);
+ m_str->device()->at(saveOffset);
+ }
+ else
+ {
+ // Build new database
+ m_baseGroupDict = new KSycocaDict();
+ }
+ _self = this;
+}
+
+KServiceGroupFactory::~KServiceGroupFactory()
+{
+ _self = 0L;
+ delete m_baseGroupDict;
+}
+
+KServiceGroupFactory * KServiceGroupFactory::self()
+{
+ if (!_self)
+ _self = new KServiceGroupFactory();
+ return _self;
+}
+
+KServiceGroup * KServiceGroupFactory::findGroupByDesktopPath(const TQString &_name, bool deep)
+{
+ if (!m_sycocaDict) return 0; // Error!
+
+ // Warning : this assumes we're NOT building a database
+ // But since findServiceByName isn't called in that case...
+ // [ see KServiceTypeFactory for how to do it if needed ]
+
+ int offset = m_sycocaDict->find_string( _name );
+ if (!offset) return 0; // Not found
+
+ KServiceGroup * newGroup = createGroup(offset, deep);
+
+ // Check whether the dictionary was right.
+ if (newGroup && (newGroup->relPath() != _name))
+ {
+ // No it wasn't...
+ delete newGroup;
+ newGroup = 0; // Not found
+ }
+ return newGroup;
+}
+
+KServiceGroup * KServiceGroupFactory::findBaseGroup(const TQString &_baseGroupName, bool deep)
+{
+ if (!m_baseGroupDict) return 0; // Error!
+
+ // Warning : this assumes we're NOT building a database
+ // But since findServiceByName isn't called in that case...
+ // [ see KServiceTypeFactory for how to do it if needed ]
+
+ int offset = m_baseGroupDict->find_string( _baseGroupName );
+ if (!offset) return 0; // Not found
+
+ KServiceGroup * newGroup = createGroup(offset, deep);
+
+ // Check whether the dictionary was right.
+ if (newGroup && (newGroup->baseGroupName() != _baseGroupName))
+ {
+ // No it wasn't...
+ delete newGroup;
+ newGroup = 0; // Not found
+ }
+ return newGroup;
+}
+
+KServiceGroup* KServiceGroupFactory::createGroup(int offset, bool deep)
+{
+ KServiceGroup * newEntry = 0L;
+ KSycocaType type;
+ TQDataStream *str = KSycoca::self()->findEntry(offset, type);
+ switch(type)
+ {
+ case KST_KServiceGroup:
+ newEntry = new KServiceGroup(*str, offset, deep);
+ break;
+
+ default:
+ kdError(7011) << TQString(TQString("KServiceGroupFactory: unexpected object entry in KSycoca database (type = %1)").arg((int)type)) << endl;
+ return 0;
+ }
+ if (!newEntry->isValid())
+ {
+ kdError(7011) << "KServiceGroupFactory: corrupt object in KSycoca database!\n" << endl;
+ delete newEntry;
+ newEntry = 0;
+ }
+ return newEntry;
+}
+
+KServiceGroup* KServiceGroupFactory::createEntry(int offset)
+{
+ return createGroup(offset, true);
+}
+
+KServiceGroupFactory *KServiceGroupFactory::_self = 0;
+
+void KServiceGroupFactory::virtual_hook( int id, void* data )
+{ KSycocaFactory::virtual_hook( id, data ); }
+
diff --git a/tdeio/tdeio/kservicegroupfactory.h b/tdeio/tdeio/kservicegroupfactory.h
new file mode 100644
index 000000000..77bc7c042
--- /dev/null
+++ b/tdeio/tdeio/kservicegroupfactory.h
@@ -0,0 +1,80 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kservicegroupfactory_h__
+#define __kservicegroupfactory_h__
+
+#include <tqstringlist.h>
+
+#include "kservicegroup.h"
+#include "tdesycocafactory.h"
+#include <assert.h>
+
+class KSycoca;
+class KSycocaDict;
+
+/**
+ * @internal
+ * A sycoca factory for service groups (e.g. list of applications)
+ * It loads the services from parsing directories (e.g. applnk/)
+ */
+class TDEIO_EXPORT KServiceGroupFactory : public KSycocaFactory
+{
+ K_SYCOCAFACTORY( KST_KServiceGroupFactory )
+public:
+ /**
+ * Create factory
+ */
+ KServiceGroupFactory();
+ virtual ~KServiceGroupFactory();
+
+ /**
+ * Construct a KServiceGroup from a config file.
+ */
+ virtual KSycocaEntry *createEntry(const TQString &, const char *)
+ { assert(0); return 0; }
+
+ /**
+ * Find a group ( by desktop path, e.g. "Applications/Editors")
+ */
+ KServiceGroup * findGroupByDesktopPath( const TQString &_name, bool deep = true );
+
+ /**
+ * Find a base group by name, e.g. "settings"
+ */
+ KServiceGroup * findBaseGroup( const TQString &_baseGroupName, bool deep = true );
+
+ /**
+ * @return the unique service group factory, creating it if necessary
+ */
+ static KServiceGroupFactory * self();
+protected:
+ KServiceGroup* createGroup(int offset, bool deep);
+ KServiceGroup* createEntry(int offset);
+ KSycocaDict *m_baseGroupDict;
+ int m_baseGroupDictOffset;
+
+private:
+ static KServiceGroupFactory *_self;
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KServiceGroupFactoryPrivate* d;
+};
+
+#endif
diff --git a/tdeio/tdeio/kservicetype.cpp b/tdeio/tdeio/kservicetype.cpp
new file mode 100644
index 000000000..8565029ee
--- /dev/null
+++ b/tdeio/tdeio/kservicetype.cpp
@@ -0,0 +1,366 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 1999 Waldo Bastian <bastian@kde.org>
+ * David Faure <faure@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include "kservice.h"
+#include "tdesycoca.h"
+#include "kservicetype.h"
+#include "kservicetypefactory.h"
+#include "kservicefactory.h"
+#include "kuserprofile.h"
+#include <assert.h>
+#include <kdebug.h>
+#include <kdesktopfile.h>
+
+template TQDataStream& operator>> <TQString, TQVariant>(TQDataStream&, TQMap<TQString, TQVariant>&);
+template TQDataStream& operator<< <TQString, TQVariant>(TQDataStream&, const TQMap<TQString, TQVariant>&);
+
+class KServiceType::KServiceTypePrivate
+{
+public:
+ KServiceTypePrivate() : parentTypeLoaded(false) { }
+
+ KServiceType::Ptr parentType;
+ KService::List services;
+ bool parentTypeLoaded;
+};
+
+KServiceType::KServiceType( const TQString & _fullpath)
+ : KSycocaEntry(_fullpath), d(0)
+{
+ KDesktopFile config( _fullpath );
+
+ init(&config);
+}
+
+KServiceType::KServiceType( KDesktopFile *config )
+ : KSycocaEntry(config->fileName()), d(0)
+{
+ init(config);
+}
+
+void
+KServiceType::init( KDesktopFile *config)
+{
+ // Is it a mimetype ?
+ m_strName = config->readEntry( "MimeType" );
+
+ // Or is it a servicetype ?
+ if ( m_strName.isEmpty() )
+ {
+ m_strName = config->readEntry( "X-TDE-ServiceType" );
+ }
+
+ m_strComment = config->readComment();
+ m_bDeleted = config->readBoolEntry( "Hidden", false );
+ m_strIcon = config->readIcon();
+
+ // We store this as property to preserve BC, we can't change that
+ // because KSycoca needs to remain BC between KDE 2.x and KDE 3.x
+ TQString sDerived = config->readEntry( "X-TDE-Derived" );
+ m_bDerived = !sDerived.isEmpty();
+ if ( m_bDerived )
+ m_mapProps.insert( "X-TDE-Derived", sDerived );
+
+ TQStringList tmpList = config->groupList();
+ TQStringList::Iterator gIt = tmpList.begin();
+
+ for( ; gIt != tmpList.end(); ++gIt )
+ {
+ if ( (*gIt).find( "Property::" ) == 0 )
+ {
+ config->setGroup( *gIt );
+ TQVariant v = config->readPropertyEntry( "Value",
+ TQVariant::nameToType( config->readEntry( "Type" ).ascii() ) );
+ if ( v.isValid() )
+ m_mapProps.insert( (*gIt).mid( 10 ), v );
+ }
+ }
+
+ gIt = tmpList.begin();
+ for( ; gIt != tmpList.end(); ++gIt )
+ {
+ if( (*gIt).find( "PropertyDef::" ) == 0 )
+ {
+ config->setGroup( *gIt );
+ m_mapPropDefs.insert( (*gIt).mid( 13 ),
+ TQVariant::nameToType( config->readEntry( "Type" ).ascii() ) );
+ }
+ }
+
+ m_bValid = !m_strName.isEmpty();
+}
+
+KServiceType::KServiceType( const TQString & _fullpath, const TQString& _type,
+ const TQString& _icon, const TQString& _comment )
+ : KSycocaEntry(_fullpath), d(0)
+{
+ m_strName = _type;
+ m_strIcon = _icon;
+ m_strComment = _comment;
+ m_bValid = !m_strName.isEmpty();
+}
+
+KServiceType::KServiceType( TQDataStream& _str, int offset )
+ : KSycocaEntry( _str, offset ), d(0)
+{
+ load( _str);
+}
+
+void
+KServiceType::load( TQDataStream& _str )
+{
+ TQ_INT8 b;
+ _str >> m_strName >> m_strIcon >> m_strComment >> m_mapProps >> m_mapPropDefs
+ >> b;
+ m_bValid = b;
+ m_bDerived = m_mapProps.contains("X-TDE-Derived");
+}
+
+void
+KServiceType::save( TQDataStream& _str )
+{
+ KSycocaEntry::save( _str );
+ // !! This data structure should remain binary compatible at all times !!
+ // You may add new fields at the end. Make sure to update the version
+ // number in tdesycoca.h
+ _str << m_strName << m_strIcon << m_strComment << m_mapProps << m_mapPropDefs
+ << (TQ_INT8)m_bValid;
+}
+
+KServiceType::~KServiceType()
+{
+ delete d;
+}
+
+TQString KServiceType::parentServiceType() const
+{
+ TQVariant v = property("X-TDE-Derived");
+ return v.toString();
+}
+
+bool KServiceType::inherits( const TQString& servTypeName ) const
+{
+ if ( name() == servTypeName )
+ return true;
+ TQString st = parentServiceType();
+ while ( !st.isEmpty() )
+ {
+ KServiceType::Ptr ptr = KServiceType::serviceType( st );
+ if (!ptr) return false; //error
+ if ( ptr->name() == servTypeName )
+ return true;
+ st = ptr->parentServiceType();
+ }
+ return false;
+}
+
+TQVariant
+KServiceType::property( const TQString& _name ) const
+{
+ TQVariant v;
+
+ if ( _name == "Name" )
+ v = TQVariant( m_strName );
+ else if ( _name == "Icon" )
+ v = TQVariant( m_strIcon );
+ else if ( _name == "Comment" )
+ v = TQVariant( m_strComment );
+ else {
+ TQMap<TQString,TQVariant>::ConstIterator it = m_mapProps.find( _name );
+ if ( it != m_mapProps.end() )
+ v = it.data();
+ }
+
+ return v;
+}
+
+TQStringList
+KServiceType::propertyNames() const
+{
+ TQStringList res;
+
+ TQMap<TQString,TQVariant>::ConstIterator it = m_mapProps.begin();
+ for( ; it != m_mapProps.end(); ++it )
+ res.append( it.key() );
+
+ res.append( "Name" );
+ res.append( "Comment" );
+ res.append( "Icon" );
+
+ return res;
+}
+
+TQVariant::Type
+KServiceType::propertyDef( const TQString& _name ) const
+{
+ TQMap<TQString,TQVariant::Type>::ConstIterator it = m_mapPropDefs.find( _name );
+ if ( it == m_mapPropDefs.end() )
+ return TQVariant::Invalid;
+ return it.data();
+}
+
+TQStringList
+KServiceType::propertyDefNames() const
+{
+ TQStringList l;
+
+ TQMap<TQString,TQVariant::Type>::ConstIterator it = m_mapPropDefs.begin();
+ for( ; it != m_mapPropDefs.end(); ++it )
+ l.append( it.key() );
+
+ return l;
+}
+
+KServiceType::Ptr KServiceType::serviceType( const TQString& _name )
+{
+ KServiceType * p = KServiceTypeFactory::self()->findServiceTypeByName( _name );
+ return KServiceType::Ptr( p );
+}
+
+static void addUnique(KService::List &lst, TQDict<KService> &dict, const KService::List &newLst, bool lowPrio)
+{
+ TQValueListConstIterator<KService::Ptr> it = newLst.begin();
+ for( ; it != newLst.end(); ++it )
+ {
+ KService *service = static_cast<KService*>(*it);
+ if (dict.find(service->desktopEntryPath()))
+ continue;
+ dict.insert(service->desktopEntryPath(), service);
+ lst.append(service);
+ if (lowPrio)
+ service->setInitialPreference( 0 );
+ }
+}
+
+KService::List KServiceType::offers( const TQString& _servicetype )
+{
+ TQDict<KService> dict(53);
+ KService::List lst;
+
+ // Services associated directly with this servicetype (the normal case)
+ KServiceType::Ptr serv = KServiceTypeFactory::self()->findServiceTypeByName( _servicetype );
+ if ( serv )
+ addUnique(lst, dict, KServiceFactory::self()->offers( serv->offset() ), false);
+ else
+ kdWarning(7009) << "KServiceType::offers : servicetype " << _servicetype << " not found" << endl;
+
+ // Find services associated with any mimetype parents. e.g. text/x-java -> text/plain
+ KMimeType::Ptr mime = dynamic_cast<KMimeType*>(static_cast<KServiceType *>(serv));
+ bool isAMimeType = (mime != 0);
+ if (mime)
+ {
+ while(true)
+ {
+ TQString parent = mime->parentMimeType();
+ if (parent.isEmpty())
+ break;
+ mime = dynamic_cast<KMimeType *>(KServiceTypeFactory::self()->findServiceTypeByName( parent ));
+ if (!mime)
+ break;
+
+ addUnique(lst, dict, KServiceFactory::self()->offers( mime->offset() ), false);
+ }
+ }
+ serv = mime = 0;
+
+ //TQValueListIterator<KService::Ptr> it = lst.begin();
+ //for( ; it != lst.end(); ++it )
+ // kdDebug() << (*it).data() << " " << (*it)->name() << endl;
+
+ // Support for all/* is deactivated by KServiceTypeProfile::configurationMode()
+ // (and makes no sense when querying for an "all" servicetype itself
+ // nor for non-mimetypes service types)
+ if ( !KServiceTypeProfile::configurationMode()
+ && isAMimeType
+ && _servicetype.left(4) != "all/" )
+ {
+ // Support for services associated with "all"
+ KServiceType * servAll = KServiceTypeFactory::self()->findServiceTypeByName( "all/all" );
+ if ( servAll )
+ {
+ addUnique(lst, dict, KServiceFactory::self()->offers( servAll->offset() ), true);
+ }
+ else
+ kdWarning(7009) << "KServiceType::offers : servicetype all/all not found" << endl;
+ delete servAll;
+
+ // Support for services associated with "allfiles"
+ if ( _servicetype != "inode/directory" && _servicetype != "inode/directory-locked" )
+ {
+ KServiceType * servAllFiles = KServiceTypeFactory::self()->findServiceTypeByName( "all/allfiles" );
+ if ( servAllFiles )
+ {
+ addUnique(lst, dict, KServiceFactory::self()->offers( servAllFiles->offset() ), true);
+ }
+ else
+ kdWarning(7009) << "KServiceType::offers : servicetype all/allfiles not found" << endl;
+ delete servAllFiles;
+ }
+ }
+
+ return lst;
+}
+
+KServiceType::List KServiceType::allServiceTypes()
+{
+ return KServiceTypeFactory::self()->allServiceTypes();
+}
+
+KServiceType::Ptr KServiceType::parentType()
+{
+ if (d && d->parentTypeLoaded)
+ return d->parentType;
+
+ if (!d)
+ d = new KServiceTypePrivate;
+
+ TQString parentSt = parentServiceType();
+ if (!parentSt.isEmpty())
+ {
+ d->parentType = KServiceTypeFactory::self()->findServiceTypeByName( parentSt );
+ if (!d->parentType)
+ kdWarning(7009) << "'" << desktopEntryPath() << "' specifies undefined mimetype/servicetype '"<< parentSt << "'" << endl;
+ }
+
+ d->parentTypeLoaded = true;
+
+ return d->parentType;
+}
+
+void KServiceType::addService(KService::Ptr service)
+{
+ if (!d)
+ d = new KServiceTypePrivate;
+
+ if (d->services.count() && d->services.last() == service)
+ return;
+
+ d->services.append(service);
+}
+
+KService::List KServiceType::services()
+{
+ if (d)
+ return d->services;
+
+ return KService::List();
+}
+
+void KServiceType::virtual_hook( int id, void* data )
+{ KSycocaEntry::virtual_hook( id, data ); }
diff --git a/tdeio/tdeio/kservicetype.h b/tdeio/tdeio/kservicetype.h
new file mode 100644
index 000000000..b1cad7284
--- /dev/null
+++ b/tdeio/tdeio/kservicetype.h
@@ -0,0 +1,251 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+ 1999 Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kservicetype_h__
+#define __kservicetype_h__
+
+#include "tdesycocaentry.h"
+#include "kservice.h"
+
+#include <tqstring.h>
+#include <tqstringlist.h>
+#include <tqptrlist.h>
+#include <tqmap.h>
+#include <tqshared.h>
+#include <tqdatastream.h>
+#include <tqvariant.h>
+
+#include <ksimpleconfig.h>
+
+/**
+ * A service type is the generic notion for a mimetype, a type of service
+ * instead of a type of file.
+ * For instance, KOfficeFilter is a service type.
+ * It is associated to services according to the user profile (kuserprofile.h).
+ * Service types are stored as desktop files in $TDEHOME/share/servicetypes.
+ * @see KService
+ */
+class TDEIO_EXPORT KServiceType : public KSycocaEntry
+{
+ K_SYCOCATYPE( KST_KServiceType, KSycocaEntry )
+
+public:
+ typedef KSharedPtr<KServiceType> Ptr;
+ typedef TQValueList<Ptr> List;
+public:
+
+ /**
+ * Constructor. You may pass in arguments to create a servicetype with
+ * specific properties.
+ * @param _fullpath the path of the service type's desktop file
+ * @param _name the name of the service type
+ * @param _icon the icon name of the service type (can be null)
+ * @param _comment a comment (can be null)
+ */
+ KServiceType( const TQString & _fullpath, const TQString& _name,
+ const TQString& _icon, const TQString& _comment);
+
+ /**
+ * Construct a service type and take all informations from a config file.
+ * @param _fullpath path of the desktop file, set to "" if calling from
+ * a inherited constructor.
+ */
+ KServiceType( const TQString & _fullpath );
+
+ /**
+ * Construct a service type and take all informations from a deskop file.
+ * @param config the configuration file
+ */
+ KServiceType( KDesktopFile *config);
+
+ /**
+ * @internal construct a service from a stream.
+ * The stream must already be positionned at the correct offset
+ */
+ KServiceType( TQDataStream& _str, int offset );
+
+ virtual ~KServiceType();
+
+ /**
+ * Returns the icon associated with this service type. Some
+ * derived classes offer special functions which take for
+ * example an URL and returns a special icon for this
+ * URL. An example is KMimeType, KFolderType and
+ * others.
+ * @return the name of the icon, can be TQString::null.
+ */
+ TQString icon() const { return m_strIcon; }
+
+ /**
+ * Returns the descriptive comment associated, if any.
+ * @return the comment, or TQString::null
+ */
+ TQString comment() const { return m_strComment; }
+
+ /**
+ * Returns the name of this service type.
+ * @return the name of the service type
+ */
+ TQString name() const { return m_strName; }
+
+ /**
+ * Returns the relative path to the desktop entry file responsible for
+ * this servicetype.
+ * For instance inode/directory.desktop, or kpart.desktop
+ * @return the path of the desktop file
+ */
+ TQString desktopEntryPath() const { return entryPath(); }
+
+ /**
+ * Checks whether this service type inherits another one.
+ * @return true if this service type inherits another one
+ * @see parentServiceType()
+ */
+ bool isDerived() const { return m_bDerived; }
+
+ /**
+ * If this service type inherits from another service type,
+ * return the name of the parent.
+ * @return the parent service type, or TQString:: null if not set
+ * @see isDerived()
+ */
+ TQString parentServiceType() const;
+
+ /**
+ * Checks whether this service type is or inherits from @p servTypeName.
+ * @return true if this servicetype is or inherits from @p servTypeName
+ * @since 3.1
+ */
+ bool inherits( const TQString& servTypeName ) const;
+
+ /**
+ * Returns the requested property. Some often used properties
+ * have convenience access functions like name(),
+ * comment() etc.
+ *
+ * @param _name the name of the property
+ * @return the property, or invalid if not found
+ */
+ virtual TQVariant property( const TQString& _name ) const;
+
+ /**
+ * Returns the list of all properties of this service type.
+ * @return the list of properties
+ */
+ virtual TQStringList propertyNames() const;
+
+ /**
+ * Checks whether the service type is valid.
+ * @return true if the service is valid (e.g. name is not empty)
+ */
+ bool isValid() const { return m_bValid; }
+
+ /**
+ * Returns the type of the property with the given @p _name.
+ *
+ * @param _name the name of the property
+ * @return the property type, or null if not found
+ */
+ virtual TQVariant::Type propertyDef( const TQString& _name ) const;
+
+ virtual TQStringList propertyDefNames() const;
+ virtual const TQMap<TQString,TQVariant::Type>& propertyDefs() const { return m_mapPropDefs; }
+
+ /**
+ * @internal
+ * Save ourselves to the data stream.
+ */
+ virtual void save( TQDataStream& );
+
+ /**
+ * @internal
+ * Load ourselves from the data stream.
+ */
+ virtual void load( TQDataStream& );
+
+ /**
+ * @internal
+ * Pointer to parent serice type
+ */
+ // gcc 2.95.x doesn't understand KServiceType::Ptr here
+ /* KServiceType:: */ Ptr parentType();
+ /**
+ * @internal
+ * Register service that provides this service type
+ */
+ void addService(KService::Ptr service);
+ /**
+ * @internal
+ * List serices that provide this service type
+ */
+ KService::List services();
+
+ /**
+ * Returns a pointer to the servicetype '_name' or 0L if the
+ * service type is unknown.
+ * VERY IMPORTANT : don't store the result in a KServiceType * !
+ * @param _name the name of the service type to search
+ * @return the pointer to the service type, or 0
+ */
+ static Ptr serviceType( const TQString& _name );
+
+ /**
+ * Returns all services supporting the given servicetype name.
+ * This doesn't take care of the user profile.
+ * In fact it is used by KServiceTypeProfile,
+ * which is used by KTrader, and that's the one you should use.
+ * @param _servicetype the name of the service type to search
+ * @return the list of all services of the given type
+ */
+ static KService::List offers( const TQString& _servicetype );
+
+ /**
+ * Returns a list of all the supported servicetypes. Useful for
+ * showing the list of available servicetypes in a listbox,
+ * for example.
+ * More memory consuming than the ones above, don't use unless
+ * really necessary.
+ * @return the list of all services
+ */
+ static List allServiceTypes();
+
+protected:
+ void init( KDesktopFile *config );
+
+protected:
+ TQString m_strName;
+ TQString m_strIcon;
+ TQString m_strComment;
+ TQMap<TQString,TQVariant> m_mapProps;
+ TQMap<TQString,TQVariant::Type> m_mapPropDefs;
+
+ bool m_bValid:1;
+ bool m_bDerived:1;
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KServiceTypePrivate;
+ KServiceTypePrivate* d;
+};
+
+//TQDataStream& operator>>( TQDataStream& _str, KServiceType& s );
+//TQDataStream& operator<<( TQDataStream& _str, KServiceType& s );
+
+#endif
diff --git a/tdeio/tdeio/kservicetypefactory.cpp b/tdeio/tdeio/kservicetypefactory.cpp
new file mode 100644
index 000000000..ecf527384
--- /dev/null
+++ b/tdeio/tdeio/kservicetypefactory.cpp
@@ -0,0 +1,300 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 1999 Waldo Bastian <bastian@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include "kservicetypefactory.h"
+#include "tdesycoca.h"
+#include "tdesycocatype.h"
+#include "tdesycocadict.h"
+#include "kservicetype.h"
+#include "kmimetype.h"
+#include "kuserprofile.h"
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <assert.h>
+#include <kstringhandler.h>
+#include <tqfile.h>
+
+KServiceTypeFactory::KServiceTypeFactory()
+ : KSycocaFactory( KST_KServiceTypeFactory )
+{
+ _self = this;
+ m_fastPatternOffset = 0;
+ m_otherPatternOffset = 0;
+ if (m_str)
+ {
+ // Read Header
+ TQ_INT32 i,n;
+ (*m_str) >> i;
+ m_fastPatternOffset = i;
+ (*m_str) >> i;
+ m_otherPatternOffset = i;
+ (*m_str) >> n;
+
+ if (n > 1024)
+ {
+ KSycoca::flagError();
+ }
+ else
+ {
+ TQString str;
+ for(;n;n--)
+ {
+ KSycocaEntry::read(*m_str, str);
+ (*m_str) >> i;
+ m_propertyTypeDict.insert(str, i);
+ }
+ }
+ }
+}
+
+
+KServiceTypeFactory::~KServiceTypeFactory()
+{
+ _self = 0L;
+ KServiceTypeProfile::clear();
+}
+
+KServiceTypeFactory * KServiceTypeFactory::self()
+{
+ if (!_self)
+ _self = new KServiceTypeFactory();
+ return _self;
+}
+
+KServiceType * KServiceTypeFactory::findServiceTypeByName(const TQString &_name)
+{
+ if (!m_sycocaDict) return 0L; // Error!
+ assert (!KSycoca::self()->isBuilding());
+ int offset = m_sycocaDict->find_string( _name );
+ if (!offset) return 0; // Not found
+ KServiceType * newServiceType = createEntry(offset);
+
+ // Check whether the dictionary was right.
+ if (newServiceType && (newServiceType->name() != _name))
+ {
+ // No it wasn't...
+ delete newServiceType;
+ newServiceType = 0; // Not found
+ }
+ return newServiceType;
+}
+
+TQVariant::Type KServiceTypeFactory::findPropertyTypeByName(const TQString &_name)
+{
+ if (!m_sycocaDict)
+ return TQVariant::Invalid; // Error!
+
+ assert (!KSycoca::self()->isBuilding());
+
+ TQMapConstIterator<TQString,int> it = m_propertyTypeDict.find(_name);
+ if (it != m_propertyTypeDict.end()) {
+ return (TQVariant::Type)it.data();
+ }
+
+ return TQVariant::Invalid;
+}
+
+KMimeType * KServiceTypeFactory::findFromPattern(const TQString &_filename, TQString *match)
+{
+ // Assume we're NOT building a database
+ if (!m_str) return 0;
+
+ // Get stream to the header
+ TQDataStream *str = m_str;
+
+ str->device()->at( m_fastPatternOffset );
+
+ TQ_INT32 nrOfEntries;
+ (*str) >> nrOfEntries;
+ TQ_INT32 entrySize;
+ (*str) >> entrySize;
+
+ TQ_INT32 fastOffset = str->device()->at( );
+
+ TQ_INT32 matchingOffset = 0;
+
+ // Let's go for a binary search in the "fast" pattern index
+ TQ_INT32 left = 0;
+ TQ_INT32 right = nrOfEntries - 1;
+ TQ_INT32 middle;
+ // Extract extension
+ int lastDot = _filename.findRev('.');
+ int ext_len = _filename.length() - lastDot - 1;
+ if (lastDot != -1 && ext_len <= 4) // if no '.', skip the extension lookup
+ {
+ TQString extension = _filename.right( ext_len );
+ extension = extension.leftJustify(4);
+
+ TQString pattern;
+ while (left <= right) {
+ middle = (left + right) / 2;
+ // read pattern at position "middle"
+ str->device()->at( middle * entrySize + fastOffset );
+ KSycocaEntry::read(*str, pattern);
+ int cmp = pattern.compare( extension );
+ if (cmp < 0)
+ left = middle + 1;
+ else if (cmp == 0) // found
+ {
+ (*str) >> matchingOffset;
+ // don't return newServiceType - there may be an "other" pattern that
+ // matches best this file, like *.tar.bz
+ if (match)
+ *match = "*."+pattern.stripWhiteSpace();
+ break; // but get out of the fast patterns
+ }
+ else
+ right = middle - 1;
+ }
+ }
+
+ // Now try the "other" Pattern table
+ if ( m_patterns.isEmpty() ) {
+ str->device()->at( m_otherPatternOffset );
+
+ TQString pattern;
+ TQ_INT32 mimetypeOffset;
+
+ while (true)
+ {
+ KSycocaEntry::read(*str, pattern);
+ if (pattern.isEmpty()) // end of list
+ break;
+ (*str) >> mimetypeOffset;
+ m_patterns.push_back( pattern );
+ m_pattern_offsets.push_back( mimetypeOffset );
+ }
+ }
+
+ assert( m_patterns.size() == m_pattern_offsets.size() );
+
+ TQStringList::const_iterator it = m_patterns.begin();
+ TQStringList::const_iterator end = m_patterns.end();
+ TQValueVector<TQ_INT32>::const_iterator it_offset = m_pattern_offsets.begin();
+
+ for ( ; it != end; ++it, ++it_offset )
+ {
+ if ( KStringHandler::matchFileName( _filename, *it ) )
+ {
+ if ( !matchingOffset || !(*it).endsWith( "*" ) ) // *.html wins over Makefile.*
+ {
+ matchingOffset = *it_offset;
+ if (match)
+ *match = *it;
+ break;
+ }
+ }
+ }
+
+ if ( matchingOffset ) {
+ KServiceType *newServiceType = createEntry( matchingOffset );
+ assert (newServiceType && newServiceType->isType( KST_KMimeType ));
+ return (KMimeType *) newServiceType;
+ }
+ else
+ return 0;
+}
+
+KMimeType::List KServiceTypeFactory::allMimeTypes()
+{
+ KMimeType::List result;
+ KSycocaEntry::List list = allEntries();
+ for( KSycocaEntry::List::Iterator it = list.begin();
+ it != list.end();
+ ++it)
+ {
+ KMimeType *newMimeType = dynamic_cast<KMimeType *>((*it).data());
+ if (newMimeType)
+ result.append( KMimeType::Ptr( newMimeType ) );
+ }
+ return result;
+}
+
+KServiceType::List KServiceTypeFactory::allServiceTypes()
+{
+ KServiceType::List result;
+ KSycocaEntry::List list = allEntries();
+ for( KSycocaEntry::List::Iterator it = list.begin();
+ it != list.end();
+ ++it)
+ {
+#ifndef Q_WS_QWS
+ KServiceType *newServiceType = dynamic_cast<KServiceType *>((*it).data());
+#else //FIXME
+ KServiceType *newServiceType = (KServiceType*)(*it).data();
+#endif
+ if (newServiceType)
+ result.append( KServiceType::Ptr( newServiceType ) );
+ }
+ return result;
+}
+
+bool KServiceTypeFactory::checkMimeTypes()
+{
+ TQDataStream *str = KSycoca::self()->findFactory( factoryId() );
+ if (!str) return false;
+
+ // check if there are mimetypes/servicetypes
+ return (m_beginEntryOffset != m_endEntryOffset);
+}
+
+KServiceType * KServiceTypeFactory::createEntry(int offset)
+{
+ KServiceType *newEntry = 0;
+ KSycocaType type;
+ TQDataStream *str = KSycoca::self()->findEntry(offset, type);
+ if (!str) return 0;
+
+ switch(type)
+ {
+ case KST_KServiceType:
+ newEntry = new KServiceType(*str, offset);
+ break;
+ case KST_KMimeType:
+ newEntry = new KMimeType(*str, offset);
+ break;
+ case KST_KFolderType:
+ newEntry = new KFolderType(*str, offset);
+ break;
+ case KST_KDEDesktopMimeType:
+ newEntry = new KDEDesktopMimeType(*str, offset);
+ break;
+ case KST_KExecMimeType:
+ newEntry = new KExecMimeType(*str, offset);
+ break;
+
+ default:
+ kdError(7011) << TQString(TQString("KServiceTypeFactory: unexpected object entry in KSycoca database (type = %1)").arg((int)type)) << endl;
+ break;
+ }
+ if (newEntry && !newEntry->isValid())
+ {
+ kdError(7011) << "KServiceTypeFactory: corrupt object in KSycoca database!\n" << endl;
+ delete newEntry;
+ newEntry = 0;
+ }
+ return newEntry;
+}
+
+KServiceTypeFactory *KServiceTypeFactory::_self = 0;
+
+void KServiceTypeFactory::virtual_hook( int id, void* data )
+{ KSycocaFactory::virtual_hook( id, data ); }
+
+// vim: ts=3 sw=3 et
diff --git a/tdeio/tdeio/kservicetypefactory.h b/tdeio/tdeio/kservicetypefactory.h
new file mode 100644
index 000000000..e18630b2c
--- /dev/null
+++ b/tdeio/tdeio/kservicetypefactory.h
@@ -0,0 +1,123 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __k_service_type_factory_h__
+#define __k_service_type_factory_h__
+
+#include <assert.h>
+
+#include <tqstringlist.h>
+#include <tqvaluevector.h>
+
+#include "tdesycocafactory.h"
+#include "kmimetype.h"
+
+class KSycoca;
+class KSycocaDict;
+
+class KServiceType;
+class KFolderType;
+class KDEDesktopMimeType;
+class KExecMimeType;
+
+/**
+ * @internal
+ * A sycoca factory for service types (e.g. mimetypes)
+ * It loads the service types from parsing directories (e.g. mimelnk/)
+ * but can also create service types from data streams or single config files
+ */
+class TDEIO_EXPORT KServiceTypeFactory : public KSycocaFactory
+{
+ K_SYCOCAFACTORY( KST_KServiceTypeFactory )
+public:
+ /**
+ * Create factory
+ */
+ KServiceTypeFactory();
+
+ virtual ~KServiceTypeFactory();
+
+ /**
+ * Not meant to be called at this level
+ */
+ virtual KSycocaEntry *createEntry(const TQString &, const char *)
+ { assert(0); return 0; }
+
+ /**
+ * Find a service type in the database file (allocates it)
+ * Overloaded by KBuildServiceTypeFactory to return a memory one.
+ */
+ virtual KServiceType * findServiceTypeByName(const TQString &_name);
+
+ /**
+ * Find a the property type of a named property.
+ */
+ TQVariant::Type findPropertyTypeByName(const TQString &_name);
+
+ /**
+ * Find a mimetype from a filename (using the pattern list)
+ * @param _filename filename to check.
+ * @param match if provided, returns the pattern that matched.
+ */
+ KMimeType * findFromPattern(const TQString &_filename, TQString *match = 0);
+
+ /**
+ * @return all mimetypes
+ * Slow and memory consuming, avoid using
+ */
+ KMimeType::List allMimeTypes();
+
+ /**
+ * @return all servicetypes
+ * Slow and memory consuming, avoid using
+ */
+ KServiceType::List allServiceTypes();
+
+ /**
+ * @return true if at least one mimetype is present
+ * Safety test
+ */
+ bool checkMimeTypes();
+
+ /**
+ * @return the unique servicetype factory, creating it if necessary
+ */
+ static KServiceTypeFactory * self();
+
+protected:
+ virtual KServiceType *createEntry(int offset);
+
+private:
+ static KServiceTypeFactory *_self;
+
+protected:
+ int m_fastPatternOffset;
+ int m_otherPatternOffset;
+ TQMap<TQString,int> m_propertyTypeDict;
+
+private:
+ TQStringList m_patterns;
+ TQValueVector<TQ_INT32> m_pattern_offsets;
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KServiceTypeFactoryPrivate* d;
+};
+
+#endif
diff --git a/tdeio/tdeio/kshellcompletion.cpp b/tdeio/tdeio/kshellcompletion.cpp
new file mode 100644
index 000000000..2fb67a31f
--- /dev/null
+++ b/tdeio/tdeio/kshellcompletion.cpp
@@ -0,0 +1,311 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Smith <dsmith@algonet.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <stdlib.h>
+#include <kdebug.h>
+#include <tqstring.h>
+#include <tqstringlist.h>
+#include <tqregexp.h>
+#include <kcompletion.h>
+
+#include "kshellcompletion.h"
+
+class KShellCompletionPrivate
+{
+};
+
+KShellCompletion::KShellCompletion() : KURLCompletion()
+{
+ m_word_break_char = ' ';
+ m_quote_char1 = '\"';
+ m_quote_char2 = '\'';
+ m_escape_char = '\\';
+}
+
+/*
+ * makeCompletion()
+ *
+ * Entry point for file name completion
+ */
+TQString KShellCompletion::makeCompletion(const TQString &text)
+{
+ // Split text at the last unquoted space
+ //
+ splitText(text, m_text_start, m_text_compl);
+
+ // Remove quotes from the text to be completed
+ //
+ TQString tmp = unquote(m_text_compl);
+ m_text_compl = tmp;
+
+ // Do exe-completion if there was no unquoted space
+ //
+ bool is_exe_completion = true;
+
+ for ( uint i = 0; i < m_text_start.length(); i++ ) {
+ if ( m_text_start[i] != m_word_break_char ) {
+ is_exe_completion = false;
+ break;
+ }
+ }
+
+ Mode mode = (is_exe_completion ? ExeCompletion : FileCompletion );
+
+ setMode(mode);
+
+ // Make completion on the last part of text
+ //
+ return KURLCompletion::makeCompletion( m_text_compl );
+}
+
+/*
+ * postProcessMatch, postProcessMatches
+ *
+ * Called by KCompletion before emitting match() and matches()
+ *
+ * Add add the part of the text that was not completed
+ * Add quotes when needed
+ */
+void KShellCompletion::postProcessMatch( TQString *match ) const
+{
+ //kDebugInfo("KShellCompletion::postProcessMatch() in: '%s'",
+ // match->latin1());
+
+ KURLCompletion::postProcessMatch( match );
+
+ if ( match->isNull() )
+ return;
+
+ if ( match->right(1) == TQChar('/') )
+ quoteText( match, false, true ); // don't quote the trailing '/'
+ else
+ quoteText( match, false, false ); // quote the whole text
+
+ match->prepend( m_text_start );
+
+ //kDebugInfo("KShellCompletion::postProcessMatch() ut: '%s'",
+ // match->latin1());
+}
+
+void KShellCompletion::postProcessMatches( TQStringList *matches ) const
+{
+ KURLCompletion::postProcessMatches( matches );
+
+ for ( TQStringList::Iterator it = matches->begin();
+ it != matches->end(); it++ )
+ {
+ if ( !(*it).isNull() ) {
+ if ( (*it).right(1) == TQChar('/') )
+ quoteText( &(*it), false, true ); // don't quote trailing '/'
+ else
+ quoteText( &(*it), false, false ); // quote the whole text
+
+ (*it).prepend( m_text_start );
+ }
+ }
+}
+
+void KShellCompletion::postProcessMatches( KCompletionMatches *matches ) const
+{
+ KURLCompletion::postProcessMatches( matches );
+
+ for ( KCompletionMatches::Iterator it = matches->begin();
+ it != matches->end(); it++ )
+ {
+ if ( !(*it).value().isNull() ) {
+ if ( (*it).value().right(1) == TQChar('/') )
+ quoteText( &(*it).value(), false, true ); // don't quote trailing '/'
+ else
+ quoteText( &(*it).value(), false, false ); // quote the whole text
+
+ (*it).value().prepend( m_text_start );
+ }
+ }
+}
+
+/*
+ * splitText
+ *
+ * Split text at the last unquoted space
+ *
+ * text_start = [out] text at the left, including the space
+ * text_compl = [out] text at the right
+ */
+void KShellCompletion::splitText(const TQString &text, TQString &text_start,
+ TQString &text_compl) const
+{
+ bool in_quote = false;
+ bool escaped = false;
+ TQChar p_last_quote_char;
+ int last_unquoted_space = -1;
+ int end_space_len = 0;
+
+ for (uint pos = 0; pos < text.length(); pos++) {
+
+ end_space_len = 0;
+
+ if ( escaped ) {
+ escaped = false;
+ }
+ else if ( in_quote && text[pos] == p_last_quote_char ) {
+ in_quote = false;
+ }
+ else if ( !in_quote && text[pos] == m_quote_char1 ) {
+ p_last_quote_char = m_quote_char1;
+ in_quote = true;
+ }
+ else if ( !in_quote && text[pos] == m_quote_char2 ) {
+ p_last_quote_char = m_quote_char2;
+ in_quote = true;
+ }
+ else if ( text[pos] == m_escape_char ) {
+ escaped = true;
+ }
+ else if ( !in_quote && text[pos] == m_word_break_char ) {
+
+ end_space_len = 1;
+
+ while ( pos+1 < text.length() && text[pos+1] == m_word_break_char ) {
+ end_space_len++;
+ pos++;
+ }
+
+ if ( pos+1 == text.length() )
+ break;
+
+ last_unquoted_space = pos;
+ }
+ }
+
+ text_start = text.left( last_unquoted_space + 1 );
+
+ // the last part without trailing blanks
+ text_compl = text.mid( last_unquoted_space + 1 );
+
+// text_compl = text.mid( last_unquoted_space + 1,
+// text.length() - end_space_len - (last_unquoted_space + 1) );
+
+ //kDebugInfo("split right = '%s'", text_compl.latin1());
+}
+
+/*
+ * quoteText()
+ *
+ * Add quotations to 'text' if needed or if 'force' = true
+ * Returns true if quotes were added
+ *
+ * skip_last => ignore the last charachter (we add a space or '/' to all filenames)
+ */
+bool KShellCompletion::quoteText(TQString *text, bool force, bool skip_last) const
+{
+ int pos = 0;
+
+ if ( !force ) {
+ pos = text->find( m_word_break_char );
+ if ( skip_last && (pos == (int)(text->length())-1) ) pos = -1;
+ }
+
+ if ( !force && pos == -1 ) {
+ pos = text->find( m_quote_char1 );
+ if ( skip_last && (pos == (int)(text->length())-1) ) pos = -1;
+ }
+
+ if ( !force && pos == -1 ) {
+ pos = text->find( m_quote_char2 );
+ if ( skip_last && (pos == (int)(text->length())-1) ) pos = -1;
+ }
+
+ if ( !force && pos == -1 ) {
+ pos = text->find( m_escape_char );
+ if ( skip_last && (pos == (int)(text->length())-1) ) pos = -1;
+ }
+
+ if ( force || (pos >= 0) ) {
+
+ // Escape \ in the string
+ text->replace( m_escape_char,
+ TQString( m_escape_char ) + m_escape_char );
+
+ // Escape " in the string
+ text->replace( m_quote_char1,
+ TQString( m_escape_char ) + m_quote_char1 );
+
+ // " at the beginning
+ text->insert( 0, m_quote_char1 );
+
+ // " at the end
+ if ( skip_last )
+ text->insert( text->length()-1, m_quote_char1 );
+ else
+ text->insert( text->length(), m_quote_char1 );
+
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * unquote
+ *
+ * Remove quotes and return the result in a new string
+ *
+ */
+TQString KShellCompletion::unquote(const TQString &text) const
+{
+ bool in_quote = false;
+ bool escaped = false;
+ TQChar p_last_quote_char;
+ TQString result;
+
+ for (uint pos = 0; pos < text.length(); pos++) {
+
+ if ( escaped ) {
+ escaped = false;
+ result.insert( result.length(), text[pos] );
+ }
+ else if ( in_quote && text[pos] == p_last_quote_char ) {
+ in_quote = false;
+ }
+ else if ( !in_quote && text[pos] == m_quote_char1 ) {
+ p_last_quote_char = m_quote_char1;
+ in_quote = true;
+ }
+ else if ( !in_quote && text[pos] == m_quote_char2 ) {
+ p_last_quote_char = m_quote_char2;
+ in_quote = true;
+ }
+ else if ( text[pos] == m_escape_char ) {
+ escaped = true;
+ result.insert( result.length(), text[pos] );
+ }
+ else {
+ result.insert( result.length(), text[pos] );
+ }
+
+ }
+
+ return result;
+}
+
+void KShellCompletion::virtual_hook( int id, void* data )
+{ KURLCompletion::virtual_hook( id, data ); }
+
+#include "kshellcompletion.moc"
+
diff --git a/tdeio/tdeio/kshellcompletion.h b/tdeio/tdeio/kshellcompletion.h
new file mode 100644
index 000000000..fa90c509e
--- /dev/null
+++ b/tdeio/tdeio/kshellcompletion.h
@@ -0,0 +1,85 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Smith <dsmith@algonet.se>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KSHELLCOMPLETION_H
+#define KSHELLCOMPLETION_H
+
+#include <tqstring.h>
+#include <tqstringlist.h>
+
+#include "kurlcompletion.h"
+
+class KShellCompletionPrivate;
+
+/**
+ * This class does shell-like completion of file names.
+ * A string passed to makeCompletion() will be interpreted as a shell
+ * command line. Completion will be done on the last argument on the line.
+ * Returned matches consist of the first arguments (uncompleted) plus the
+ * completed last argument.
+ *
+ * @short Shell-like completion of file names
+ * @author David Smith <dsmith@algonet.se>
+ */
+class TDEIO_EXPORT KShellCompletion : public KURLCompletion
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Constructs a KShellCompletion object.
+ */
+ KShellCompletion();
+
+ /**
+ * Finds completions to the given text.
+ * The first match is returned and emitted in the signal match().
+ * @param text the text to complete
+ * @return the first match, or TQString::null if not found
+ */
+ TQString makeCompletion(const TQString &text);
+
+protected:
+ // Called by KCompletion
+ void postProcessMatch( TQString *match ) const;
+ void postProcessMatches( TQStringList *matches ) const;
+ void postProcessMatches( KCompletionMatches *matches ) const;
+
+private:
+ // Find the part of text that should be completed
+ void splitText(const TQString &text, TQString &text_start, TQString &text_compl) const;
+ // Insert quotes and neseccary escapes
+ bool quoteText(TQString *text, bool force, bool skip_last) const;
+ TQString unquote(const TQString &text) const;
+
+ TQString m_text_start; // part of the text that was not completed
+ TQString m_text_compl; // part of the text that was completed (unchanged)
+
+ TQChar m_word_break_char;
+ TQChar m_quote_char1;
+ TQChar m_quote_char2;
+ TQChar m_escape_char;
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ KShellCompletionPrivate *d;
+};
+
+#endif // KSHELLCOMPLETION_H
diff --git a/tdeio/tdeio/kshred.cpp b/tdeio/tdeio/kshred.cpp
new file mode 100644
index 000000000..f3997bf58
--- /dev/null
+++ b/tdeio/tdeio/kshred.cpp
@@ -0,0 +1,276 @@
+/*--------------------------------------------------------------------------*
+ KShred.h Copyright (c) 2000 MieTerra LLC.
+ Credits: Andreas F. Pour <bugs@mieterra.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "kshred.h"
+#include <time.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <stdlib.h>
+#include <kapplication.h>
+
+// antlarr: KDE 4: Make it const TQString &
+KShred::KShred(TQString fileName)
+{
+ if (fileName.isEmpty())
+ {
+ kdError() << "KShred: missing file name in constructor" << endl;
+ file = 0L;
+ }
+ else
+ {
+ file = new TQFile();
+ file->setName(fileName);
+ if (!file->open(IO_ReadWrite))
+ {
+ kdError() << "KShred: cannot open file '" << fileName.local8Bit().data() << "' for writing\n" << endl;
+ file = 0L;
+ fileSize = 0;
+ }
+ else
+ fileSize = file->size();
+
+ totalBytes = 0;
+ bytesWritten = 0;
+ lastSignalled = 0;
+ tbpc = 0;
+ fspc = 0;
+ }
+}
+
+
+KShred::~KShred()
+{
+ if (file != 0L)
+ delete file;
+}
+
+
+bool
+KShred::fill1s()
+{
+ return fillbyte(0xFF);
+}
+
+
+bool
+KShred::fill0s()
+{
+ return fillbyte(0x0);
+}
+
+
+bool
+KShred::fillbyte(unsigned int byte)
+{
+ if (file == 0L)
+ return false;
+ unsigned char buff[4096];
+ memset((void *) buff, byte, 4096);
+
+ unsigned int n;
+ for (unsigned int todo = fileSize; todo > 0; todo -= n)
+ {
+ n = (todo > 4096 ? 4096 : todo);
+ if (!writeData(buff, n))
+ return false;
+ }
+ if (!flush())
+ return false;
+ return file->at(0);
+}
+
+
+bool
+KShred::fillpattern(unsigned char *data, unsigned int size)
+{
+ if (file == 0L)
+ return false;
+
+ unsigned int n;
+ for (unsigned int todo = fileSize; todo > 0; todo -= n)
+ {
+ n = (todo > size ? size : todo);
+ if (!writeData(data, n))
+ return false;
+ }
+ if (!flush())
+ return false;
+ return file->at(0);
+}
+
+
+bool
+KShred::fillrandom()
+{
+ if (file == 0L)
+ return false;
+
+ long int buff[4096 / sizeof(long int)];
+ unsigned int n;
+
+ for (unsigned int todo = fileSize; todo > 0; todo -= n)
+ {
+ n = (todo > 4096 ? 4096 : todo);
+ // assumes that 4096 is a multipe of sizeof(long int)
+ int limit = (n + sizeof(long int) - 1) / sizeof(long int);
+ for (int i = 0; i < limit; i++)
+ buff[i] = kapp->random();
+
+ if (!writeData((unsigned char *) buff, n))
+ return false;
+ }
+ if (!flush())
+ return false;
+ return file->at(0);
+}
+
+
+// antlarr: KDE 4: Make it const TQString &
+bool
+KShred::shred(TQString fileName)
+{
+ if (fileName.isEmpty())
+ return false;
+
+ KShred shredder(fileName);
+ return shredder.shred();
+}
+
+
+bool
+KShred::writeData(unsigned char *data, unsigned int size)
+{
+ unsigned int ret = 0;
+
+ // write 'data' of size 'size' to the file
+ while ((ret < size) && (file->putch((int) data[ret]) >= 0))
+ ret++;
+
+ if ((totalBytes > 0) && (ret > 0))
+ {
+ if (tbpc == 0)
+ {
+ tbpc = ((unsigned int) (totalBytes / 100)) == 0 ? 1 : totalBytes / 100;
+ fspc = ((unsigned int) (fileSize / 100)) == 0 ? 1 : fileSize / 100;
+ }
+ bytesWritten += ret;
+ unsigned int pc = (unsigned int) (bytesWritten / tbpc);
+ if (pc > lastSignalled)
+ {
+ emit processedSize(fspc * pc);
+ lastSignalled = pc;
+ }
+ }
+ return ret == size;
+}
+
+
+bool
+KShred::flush()
+{
+ if (file == 0L)
+ return false;
+
+ file->flush();
+ return (fsync(file->handle()) == 0);
+}
+
+
+// shred the file, then close and remove it
+//
+// UPDATED: this function now uses 35 passes based on the the article
+// Peter Gutmann, "Secure Deletion of Data from Magnetic and Solid-State
+// Memory", first published in the Sixth USENIX Security Symposium
+// Proceedings, San Jose, CA, July 22-25, 1996 (available online at
+// http://rootprompt.org/article.php3?article=473)
+
+bool
+KShred::shred()
+{
+ unsigned char p[6][3] = {{'\222', '\111', '\044'}, {'\111', '\044', '\222'},
+ {'\044', '\222', '\111'}, {'\155', '\266', '\333'},
+ {'\266', '\333', '\155'}, {'\333', '\155', '\266'}};
+ TQString msg = i18n("Shredding: pass %1 of 35");
+
+ emit processedSize(0);
+
+ // thirty-five times writing the entire file size
+ totalBytes = fileSize * 35;
+ int iteration = 1;
+
+ for (int ctr = 0; ctr < 4; ctr++)
+ if (!fillrandom())
+ return false;
+ else
+ {
+ emit infoMessage(msg.arg(iteration));
+ }
+
+ if (!fillbyte((unsigned int) 0x55)) // '0x55' is 01010101
+ return false;
+ emit infoMessage(msg.arg(iteration));
+
+ if (!fillbyte((unsigned int) 0xAA)) // '0xAA' is 10101010
+ return false;
+ emit infoMessage(msg.arg(iteration));
+
+ for (unsigned int ctr = 0; ctr < 3; ctr++)
+ if (!fillpattern(p[ctr], 3)) // '0x92', '0x49', '0x24'
+ return false;
+ else
+ {
+ emit infoMessage(msg.arg(iteration));
+ }
+
+ for (unsigned int ctr = 0; ctr <= 255 ; ctr += 17)
+ if (!fillbyte(ctr)) // sequence of '0x00', '0x11', ..., '0xFF'
+ return false;
+ else
+ {
+ emit infoMessage(msg.arg(iteration));
+ }
+
+ for (unsigned int ctr = 0; ctr < 6; ctr++)
+ if (!fillpattern(p[ctr], 3)) // '0x92', '0x49', '0x24'
+ return false;
+ else
+ {
+ emit infoMessage(msg.arg(iteration));
+ }
+
+ for (int ctr = 0; ctr < 4; ctr++)
+ if (!fillrandom())
+ return false;
+ else
+ {
+ emit infoMessage(msg.arg(iteration));
+ }
+
+ if (!file->remove())
+ return false;
+ file = 0L;
+ emit processedSize(fileSize);
+ return true;
+}
+
+#include "kshred.moc"
+
diff --git a/tdeio/tdeio/kshred.h b/tdeio/tdeio/kshred.h
new file mode 100644
index 000000000..5fabcaa5b
--- /dev/null
+++ b/tdeio/tdeio/kshred.h
@@ -0,0 +1,156 @@
+/*--------------------------------------------------------------------------*
+ KShred.h Copyright (c) 2000 MieTerra LLC.
+ Credits: Andreas F. Pour <bugs@mieterra.com>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#ifndef kshred_h
+#define kshred_h
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <tqstring.h>
+#include <tqfile.h>
+#include <tqobject.h>
+
+#include <tdeio/global.h>
+
+/**
+ * @deprecated
+ * Erase a file in a way that makes recovery impossible -- well, no guarentee
+ * of that, but at least as difficult as reasonably possible.
+ * For this, KShred write several times over the
+ * existing file, using different patterns, before deleting it.
+ * @author Andreas F. Pour <bugs@mieterra.com>
+ * @author David Faure <faure@kde.org> (integration into KDE and progress signal)
+ */
+class TDEIO_EXPORT_DEPRECATED KShred : public TQObject { // KDE4: remove
+
+ Q_OBJECT
+
+ public:
+
+ /**
+ * Initialize the class using the name of the file to 'shred'.
+ * @param fileName fully qualified name of the file to shred.
+ */
+ KShred(TQString fileName);
+
+ /*
+ * Destructor for the class.
+ */
+ ~KShred();
+
+ /**
+ * Writes all 1's over the entire file and flushes the file buffers.
+ * @return true on success, false on error (invalid filename or write error)
+ */
+
+ bool fill1s();
+ /**
+ * Writes all 0's over the entire file and flushes the file buffers.
+ * @return true on success, false on error (invalid filename or write error)
+ */
+ bool fill0s();
+
+ /**
+ * Writes the specified byte over the entire file and flushes the file buffers.
+ * @param byte the value to write over every byte of the file
+ * @return true on success, false on error (invalid filename or write error)
+ */
+ bool fillbyte(unsigned int byte);
+
+ /**
+ * Writes random bites over the entire file and flushes the file buffers.
+ * @return true on success, false on error (invalid filename or write error)
+ */
+ bool fillrandom();
+
+ /**
+ * Writes the specified byte array over the entire file and flushes the file buffers.
+ * @param pattern the value to write over the entire file
+ * @param size the length of the 'pattern' byte array
+ * @return true on success, false on error (invalid filename or write error)
+ */
+ bool fillpattern(unsigned char *pattern, unsigned int size);
+
+ /**
+ * Shreds a file by writing a series of values over it (uses
+ * #fill0s, then fill1s, then fillrandom, then
+ * fillbyte with 0101..., then fillbyte with 1010....
+ * @return true on success, false on error (invalid filename or write error)
+ */
+ bool shred();
+
+ /**
+ * The simplest method to shred a file.
+ * No need to create an instance of the class.
+ * @param fileName fully qualified name of the file to shred.
+ */
+ static bool shred(TQString fileName);
+
+ signals:
+ /**
+ * Shows progress of the shredding.
+ * @param bytes the number of bytes written to the file
+ */
+ void processedSize(TDEIO::filesize_t bytes);
+
+ /**
+ * Shows a message in the progress dialog
+ * @param message the message to display
+ */
+ void infoMessage(const TQString & message);
+
+ private:
+ /**
+ * @internal write the data to the file
+ */
+ bool writeData(unsigned char *data, unsigned int size);
+
+ /**
+ * @internal flush the data to the file
+ */
+ bool flush();
+
+ /**
+ * @internal structure for the file information
+ */
+ TQFile *file;
+
+ /**
+ * @internal for the size of the file
+ */
+ TDEIO::filesize_t fileSize;
+
+ /**
+ * @internal for keeping track of progress
+ */
+ unsigned int totalBytes;
+ unsigned int bytesWritten;
+ unsigned int lastSignalled;
+ unsigned int tbpc;
+ unsigned int fspc;
+ private:
+ class KShredPrivate* d;
+};
+
+#endif
diff --git a/tdeio/tdeio/ktar.cpp b/tdeio/tdeio/ktar.cpp
new file mode 100644
index 000000000..9bde2873a
--- /dev/null
+++ b/tdeio/tdeio/ktar.cpp
@@ -0,0 +1,980 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+ Copyright (C) 2003 Leo Savernik <l.savernik@aon.at>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+//#include <stdio.h>
+#include <stdlib.h> // strtol
+#include <time.h> // time()
+/*#include <unistd.h>
+#include <grp.h>
+#include <pwd.h>*/
+#include <assert.h>
+
+#include <tqcstring.h>
+#include <tqdir.h>
+#include <tqfile.h>
+#include <kdebug.h>
+#include <kmimetype.h>
+#include <ktempfile.h>
+
+#include <kfilterdev.h>
+#include <kfilterbase.h>
+
+#include "ktar.h"
+#include <kstandarddirs.h>
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// KTar ///////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+class KTar::KTarPrivate
+{
+public:
+ KTarPrivate() : tarEnd( 0 ), tmpFile( 0 ) {}
+ TQStringList dirList;
+ int tarEnd;
+ KTempFile* tmpFile;
+ TQString mimetype;
+ TQCString origFileName;
+
+ bool fillTempFile(const TQString & filename);
+ bool writeBackTempFile( const TQString & filename );
+};
+
+KTar::KTar( const TQString& filename, const TQString & _mimetype )
+ : KArchive( 0 )
+{
+ m_filename = filename;
+ d = new KTarPrivate;
+ TQString mimetype( _mimetype );
+ bool forced = true;
+ if ( mimetype.isEmpty() ) // Find out mimetype manually
+ {
+ if ( TQFile::exists( filename ) )
+ mimetype = KMimeType::findByFileContent( filename )->name();
+ else
+ mimetype = KMimeType::findByPath( filename, 0, true )->name();
+ kdDebug(7041) << "KTar::KTar mimetype = " << mimetype << endl;
+
+ // Don't move to prepareDevice - the other constructor theoretically allows ANY filter
+ if ( mimetype == "application/x-tgz" || mimetype == "application/x-targz" || // the latter is deprecated but might still be around
+ mimetype == "application/x-webarchive" )
+ {
+ // that's a gzipped tar file, so ask for gzip filter
+ mimetype = "application/x-gzip";
+ }
+ else if ( mimetype == "application/x-tbz" ) // that's a bzipped2 tar file, so ask for bz2 filter
+ {
+ mimetype = "application/x-bzip2";
+ }
+ else
+ {
+ // Something else. Check if it's not really gzip though (e.g. for KOffice docs)
+ TQFile file( filename );
+ if ( file.open( IO_ReadOnly ) )
+ {
+ unsigned char firstByte = file.getch();
+ unsigned char secondByte = file.getch();
+ unsigned char thirdByte = file.getch();
+ if ( firstByte == 0037 && secondByte == 0213 )
+ mimetype = "application/x-gzip";
+ else if ( firstByte == 'B' && secondByte == 'Z' && thirdByte == 'h' )
+ mimetype = "application/x-bzip2";
+ else if ( firstByte == 'P' && secondByte == 'K' && thirdByte == 3 )
+ {
+ unsigned char fourthByte = file.getch();
+ if ( fourthByte == 4 )
+ mimetype = "application/x-zip";
+ }
+ else if ( firstByte == 0xfd && secondByte == '7' && thirdByte == 'z' )
+ {
+ unsigned char fourthByte = file.getch();
+ unsigned char fifthByte = file.getch();
+ unsigned char sixthByte = file.getch();
+ if ( fourthByte == 'X' && fifthByte == 'Z' && sixthByte == 0x00 )
+ mimetype = "application/x-xz";
+ }
+ else if ( firstByte == 0x5d && secondByte == 0x00 && thirdByte == 0x00 )
+ {
+ unsigned char fourthByte = file.getch();
+ if ( fourthByte == 0x80 )
+ mimetype = "application/x-lzma";
+ }
+ }
+ file.close();
+ }
+ forced = false;
+ }
+ d->mimetype = mimetype;
+
+ prepareDevice( filename, mimetype, forced );
+}
+
+void KTar::prepareDevice( const TQString & filename,
+ const TQString & mimetype, bool /*forced*/ )
+{
+ if( "application/x-tar" == mimetype )
+ setDevice( TQT_TQIODEVICE(new TQFile( filename )) );
+ else
+ {
+ // The compression filters are very slow with random access.
+ // So instead of applying the filter to the device,
+ // the file is completly extracted instead,
+ // and we work on the extracted tar file.
+ // This improves the extraction speed by the tar ioslave dramatically,
+ // if the archive file contains many files.
+ // This is because the tar ioslave extracts one file after the other and normally
+ // has to walk through the decompression filter each time.
+ // Which is in fact nearly as slow as a complete decompression for each file.
+ d->tmpFile = new KTempFile(locateLocal("tmp", "ktar-"),".tar");
+ kdDebug( 7041 ) << "KTar::prepareDevice creating TempFile: " << d->tmpFile->name() << endl;
+ d->tmpFile->setAutoDelete(true);
+
+ // KTempFile opens the file automatically,
+ // the device must be closed, however, for KArchive.setDevice()
+ TQFile* file = d->tmpFile->file();
+ file->close();
+ setDevice(TQT_TQIODEVICE(file));
+ }
+}
+
+KTar::KTar( TQIODevice * dev )
+ : KArchive( dev )
+{
+ Q_ASSERT( dev );
+ d = new KTarPrivate;
+}
+
+KTar::~KTar()
+{
+ // mjarrett: Closes to prevent ~KArchive from aborting w/o device
+ if( isOpened() )
+ close();
+
+ if (d->tmpFile)
+ delete d->tmpFile; // will delete the device
+ else if ( !m_filename.isEmpty() )
+ delete device(); // we created it ourselves
+
+
+ delete d;
+}
+
+void KTar::setOrigFileName( const TQCString & fileName )
+{
+ if ( !isOpened() || !(mode() & IO_WriteOnly) )
+ {
+ kdWarning(7041) << "KTar::setOrigFileName: File must be opened for writing first.\n";
+ return;
+ }
+ d->origFileName = fileName;
+}
+
+TQ_LONG KTar::readRawHeader(char *buffer) {
+ // Read header
+ TQ_LONG n = device()->readBlock( buffer, 0x200 );
+ if ( n == 0x200 && buffer[0] != 0 ) {
+ // Make sure this is actually a tar header
+ if (strncmp(buffer + 257, "ustar", 5)) {
+ // The magic isn't there (broken/old tars), but maybe a correct checksum?
+ TQCString s;
+
+ int check = 0;
+ for( uint j = 0; j < 0x200; ++j )
+ check += buffer[j];
+
+ // adjust checksum to count the checksum fields as blanks
+ for( uint j = 0; j < 8 /*size of the checksum field including the \0 and the space*/; j++ )
+ check -= buffer[148 + j];
+ check += 8 * ' ';
+
+ s.sprintf("%o", check );
+
+ // only compare those of the 6 checksum digits that mean something,
+ // because the other digits are filled with all sorts of different chars by different tars ...
+ // Some tars right-justify the checksum so it could start in one of three places - we have to check each.
+ if( strncmp( buffer + 148 + 6 - s.length(), s.data(), s.length() )
+ && strncmp( buffer + 148 + 7 - s.length(), s.data(), s.length() )
+ && strncmp( buffer + 148 + 8 - s.length(), s.data(), s.length() ) ) {
+ kdWarning(7041) << "KTar: invalid TAR file. Header is: " << TQCString( buffer+257, 5 ) << endl;
+ return -1;
+ }
+ }/*end if*/
+ } else {
+ // reset to 0 if 0x200 because logical end of archive has been reached
+ if (n == 0x200) n = 0;
+ }/*end if*/
+ return n;
+}
+
+bool KTar::readLonglink(char *buffer,TQCString &longlink) {
+ TQ_LONG n = 0;
+ TQIODevice *dev = device();
+ // read size of longlink from size field in header
+ // size is in bytes including the trailing null (which we ignore)
+ buffer[ 0x88 ] = 0; // was 0x87, but 0x88 fixes BR #26437
+ char *dummy;
+ const char* p = buffer + 0x7c;
+ while( *p == ' ' ) ++p;
+ int size = (int)strtol( p, &dummy, 8 );
+
+ longlink.resize(size);
+ size--; // ignore trailing null
+ dummy = longlink.data();
+ int offset = 0;
+ while (size > 0) {
+ int chunksize = QMIN(size, 0x200);
+ n = dev->readBlock( dummy + offset, chunksize );
+ if (n == -1) return false;
+ size -= chunksize;
+ offset += 0x200;
+ }/*wend*/
+ // jump over the rest
+ int skip = 0x200 - (n % 0x200);
+ if (skip < 0x200) {
+ if (dev->readBlock(buffer,skip) != skip) return false;
+ }
+ return true;
+}
+
+TQ_LONG KTar::readHeader(char *buffer,TQString &name,TQString &symlink) {
+ name.truncate(0);
+ symlink.truncate(0);
+ while (true) {
+ TQ_LONG n = readRawHeader(buffer);
+ if (n != 0x200) return n;
+
+ // is it a longlink?
+ if (strcmp(buffer,"././@LongLink") == 0) {
+ char typeflag = buffer[0x9c];
+ TQCString longlink;
+ readLonglink(buffer,longlink);
+ switch (typeflag) {
+ case 'L': name = TQFile::decodeName(longlink); break;
+ case 'K': symlink = TQFile::decodeName(longlink); break;
+ }/*end switch*/
+ } else {
+ break;
+ }/*end if*/
+ }/*wend*/
+
+ // if not result of longlink, read names directly from the header
+ if (name.isEmpty())
+ // there are names that are exactly 100 bytes long
+ // and neither longlink nor \0 terminated (bug:101472)
+ name = TQFile::decodeName(TQCString(buffer, 101));
+ if (symlink.isEmpty())
+ symlink = TQFile::decodeName(TQCString(buffer + 0x9d, 101));
+
+ return 0x200;
+}
+
+/*
+ * If we have created a temporary file, we have
+ * to decompress the original file now and write
+ * the contents to the temporary file.
+ */
+bool KTar::KTarPrivate::fillTempFile( const TQString & filename) {
+ if ( ! tmpFile )
+ return true;
+
+ kdDebug( 7041 ) <<
+ "KTar::openArchive: filling tmpFile of mimetype '" << mimetype <<
+ "' ... " << endl;
+
+ bool forced = false;
+ if( "application/x-gzip" == mimetype
+ || "application/x-bzip2" == mimetype
+ || "application/x-lzma" == mimetype
+ || "application/x-xz" == mimetype)
+ forced = true;
+
+ TQIODevice *filterDev = KFilterDev::deviceForFile( filename, mimetype, forced );
+
+ if( filterDev ) {
+ TQFile* file = tmpFile->file();
+ file->close();
+ if ( ! file->open( IO_WriteOnly ) )
+ {
+ delete filterDev;
+ return false;
+ }
+ TQByteArray buffer(8*1024);
+ if ( ! filterDev->open( IO_ReadOnly ) )
+ {
+ delete filterDev;
+ return false;
+ }
+ TQ_LONG len = -1;
+ while ( !filterDev->atEnd() && len != 0) {
+ len = filterDev->readBlock(buffer.data(),buffer.size());
+ if ( len < 0 ) { // corrupted archive
+ delete filterDev;
+ return false;
+ }
+ file->writeBlock(buffer.data(),len);
+ }
+ filterDev->close();
+ delete filterDev;
+
+ file->close();
+ if ( ! file->open( IO_ReadOnly ) )
+ return false;
+ }
+ else
+ kdDebug( 7041 ) << "KTar::openArchive: no filterdevice found!" << endl;
+
+ kdDebug( 7041 ) << "KTar::openArchive: filling tmpFile finished." << endl;
+ return true;
+}
+
+bool KTar::openArchive( int mode )
+{
+ kdDebug( 7041 ) << "KTar::openArchive" << endl;
+ if ( !(mode & IO_ReadOnly) )
+ return true;
+
+ if ( !d->fillTempFile( m_filename ) )
+ return false;
+
+ // We'll use the permission and user/group of d->rootDir
+ // for any directory we emulate (see findOrCreate)
+ //struct stat buf;
+ //stat( m_filename, &buf );
+
+ d->dirList.clear();
+ TQIODevice* dev = device();
+
+ if ( !dev )
+ return false;
+
+ // read dir infos
+ char buffer[ 0x200 ];
+ bool ende = false;
+ do
+ {
+ TQString name;
+ TQString symlink;
+
+ // Read header
+ TQ_LONG n = readHeader(buffer,name,symlink);
+ if (n < 0) return false;
+ if (n == 0x200)
+ {
+ bool isdir = false;
+ TQString nm;
+
+ if ( name.right(1) == "/" )
+ {
+ isdir = true;
+ name = name.left( name.length() - 1 );
+ }
+
+ int pos = name.findRev( '/' );
+ if ( pos == -1 )
+ nm = name;
+ else
+ nm = name.mid( pos + 1 );
+
+ // read access
+ buffer[ 0x6b ] = 0;
+ char *dummy;
+ const char* p = buffer + 0x64;
+ while( *p == ' ' ) ++p;
+ int access = (int)strtol( p, &dummy, 8 );
+
+ // read user and group
+ TQString user( buffer + 0x109 );
+ TQString group( buffer + 0x129 );
+
+ // read time
+ buffer[ 0x93 ] = 0;
+ p = buffer + 0x88;
+ while( *p == ' ' ) ++p;
+ int time = (int)strtol( p, &dummy, 8 );
+
+ // read type flag
+ char typeflag = buffer[ 0x9c ];
+ // '0' for files, '1' hard link, '2' symlink, '5' for directory
+ // (and 'L' for longlink filenames, 'K' for longlink symlink targets)
+ // and 'D' for GNU tar extension DUMPDIR
+ if ( typeflag == '5' )
+ isdir = true;
+
+ bool isDumpDir = false;
+ if ( typeflag == 'D' )
+ {
+ isdir = false;
+ isDumpDir = true;
+ }
+ //bool islink = ( typeflag == '1' || typeflag == '2' );
+ //kdDebug(7041) << "typeflag=" << typeflag << " islink=" << islink << endl;
+
+ if (isdir)
+ access |= S_IFDIR; // f*cking broken tar files
+
+ KArchiveEntry* e;
+ if ( isdir )
+ {
+ //kdDebug(7041) << "KTar::openArchive directory " << nm << endl;
+ e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
+ }
+ else
+ {
+ // read size
+ buffer[ 0x88 ] = 0; // was 0x87, but 0x88 fixes BR #26437
+ char *dummy;
+ const char* p = buffer + 0x7c;
+ while( *p == ' ' ) ++p;
+ int size = (int)strtol( p, &dummy, 8 );
+
+ // for isDumpDir we will skip the additional info about that dirs contents
+ if ( isDumpDir )
+ {
+ //kdDebug(7041) << "KTar::openArchive " << nm << " isDumpDir" << endl;
+ e = new KArchiveDirectory( this, nm, access, time, user, group, symlink );
+ }
+ else
+ {
+
+ // Let's hack around hard links. Our classes don't support that, so make them symlinks
+ if ( typeflag == '1' )
+ {
+ kdDebug(7041) << "HARD LINK, setting size to 0 instead of " << size << endl;
+ size = 0; // no contents
+ }
+
+ //kdDebug(7041) << "KTar::openArchive file " << nm << " size=" << size << endl;
+ e = new KArchiveFile( this, nm, access, time, user, group, symlink,
+ dev->at(), size );
+ }
+
+ // Skip contents + align bytes
+ int rest = size % 0x200;
+ int skip = size + (rest ? 0x200 - rest : 0);
+ //kdDebug(7041) << "KTar::openArchive, at()=" << dev->at() << " rest=" << rest << " skipping " << skip << endl;
+ if (! dev->at( dev->at() + skip ) )
+ kdWarning(7041) << "KTar::openArchive skipping " << skip << " failed" << endl;
+ }
+
+ if ( pos == -1 )
+ {
+ if ( nm == "." ) // special case
+ {
+ Q_ASSERT( isdir );
+ if ( isdir )
+ setRootDir( static_cast<KArchiveDirectory *>( e ) );
+ }
+ else
+ rootDir()->addEntry( e );
+ }
+ else
+ {
+ // In some tar files we can find dir/./file => call cleanDirPath
+ TQString path = TQDir::cleanDirPath( name.left( pos ) );
+ // Ensure container directory exists, create otherwise
+ KArchiveDirectory * d = findOrCreate( path );
+ d->addEntry( e );
+ }
+ }
+ else
+ {
+ //tqDebug("Terminating. Read %d bytes, first one is %d", n, buffer[0]);
+ d->tarEnd = dev->at() - n; // Remember end of archive
+ ende = true;
+ }
+ } while( !ende );
+ return true;
+}
+
+/*
+ * Writes back the changes of the temporary file
+ * to the original file.
+ * Must only be called if in IO_WriteOnly mode
+ */
+bool KTar::KTarPrivate::writeBackTempFile( const TQString & filename ) {
+ if ( ! tmpFile )
+ return true;
+
+ kdDebug(7041) << "Write temporary file to compressed file" << endl;
+ kdDebug(7041) << filename << " " << mimetype << endl;
+
+ bool forced = false;
+ if( "application/x-gzip" == mimetype
+ || "application/x-bzip2" == mimetype
+ || "application/x-lzma" == mimetype
+ || "application/x-xz" == mimetype)
+ forced = true;
+
+ TQIODevice *dev = KFilterDev::deviceForFile( filename, mimetype, forced );
+ if( dev ) {
+ TQFile* file = tmpFile->file();
+ file->close();
+ if ( ! file->open(IO_ReadOnly) || ! dev->open(IO_WriteOnly) )
+ {
+ file->close();
+ delete dev;
+ return false;
+ }
+ if ( forced )
+ static_cast<KFilterDev *>(dev)->setOrigFileName( origFileName );
+ TQByteArray buffer(8*1024);
+ TQ_LONG len;
+ while ( ! file->atEnd()) {
+ len = file->readBlock(buffer.data(),buffer.size());
+ dev->writeBlock(buffer.data(),len);
+ }
+ file->close();
+ dev->close();
+ delete dev;
+ }
+
+ kdDebug(7041) << "Write temporary file to compressed file done." << endl;
+ return true;
+}
+
+bool KTar::closeArchive()
+{
+ d->dirList.clear();
+
+ // If we are in write mode and had created
+ // a temporary tar file, we have to write
+ // back the changes to the original file
+ if( mode() == IO_WriteOnly)
+ return d->writeBackTempFile( m_filename );
+
+ return true;
+}
+
+bool KTar::writeDir( const TQString& name, const TQString& user, const TQString& group )
+{
+ mode_t perm = 040755;
+ time_t the_time = time(0);
+ return writeDir(name,user,group,perm,the_time,the_time,the_time);
+#if 0
+ if ( !isOpened() )
+ {
+ kdWarning(7041) << "KTar::writeDir: You must open the tar file before writing to it\n";
+ return false;
+ }
+
+ if ( !(mode() & IO_WriteOnly) )
+ {
+ kdWarning(7041) << "KTar::writeDir: You must open the tar file for writing\n";
+ return false;
+ }
+
+ // In some tar files we can find dir/./ => call cleanDirPath
+ TQString dirName ( TQDir::cleanDirPath( name ) );
+
+ // Need trailing '/'
+ if ( dirName.right(1) != "/" )
+ dirName += "/";
+
+ if ( d->dirList.contains( dirName ) )
+ return true; // already there
+
+ char buffer[ 0x201 ];
+ memset( buffer, 0, 0x200 );
+ if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
+
+ // If more than 100 chars, we need to use the LongLink trick
+ if ( dirName.length() > 99 )
+ {
+ strcpy( buffer, "././@LongLink" );
+ fillBuffer( buffer, " 0", dirName.length()+1, 'L', user.local8Bit(), group.local8Bit() );
+ device()->writeBlock( buffer, 0x200 );
+ strncpy( buffer, TQFile::encodeName(dirName), 0x200 );
+ buffer[0x200] = 0;
+ // write long name
+ device()->writeBlock( buffer, 0x200 );
+ // not even needed to reclear the buffer, tar doesn't do it
+ }
+ else
+ {
+ // Write name
+ strncpy( buffer, TQFile::encodeName(dirName), 0x200 );
+ buffer[0x200] = 0;
+ }
+
+ fillBuffer( buffer, " 40755", 0, 0x35, user.local8Bit(), group.local8Bit());
+
+ // Write header
+ device()->writeBlock( buffer, 0x200 );
+ if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at();
+
+ d->dirList.append( dirName ); // contains trailing slash
+ return true; // TODO if wanted, better error control
+#endif
+}
+
+bool KTar::prepareWriting( const TQString& name, const TQString& user, const TQString& group, uint size )
+{
+ mode_t dflt_perm = 0100644;
+ time_t the_time = time(0);
+ return prepareWriting(name,user,group,size,dflt_perm,
+ the_time,the_time,the_time);
+}
+
+bool KTar::doneWriting( uint size )
+{
+ // Write alignment
+ int rest = size % 0x200;
+ if ( mode() & IO_ReadWrite )
+ d->tarEnd = device()->at() + (rest ? 0x200 - rest : 0); // Record our new end of archive
+ if ( rest )
+ {
+ char buffer[ 0x201 ];
+ for( uint i = 0; i < 0x200; ++i )
+ buffer[i] = 0;
+ TQ_LONG nwritten = device()->writeBlock( buffer, 0x200 - rest );
+ return nwritten == 0x200 - rest;
+ }
+ return true;
+}
+
+/*** Some help from the tar sources
+struct posix_header
+{ byte offset
+ char name[100]; * 0 * 0x0
+ char mode[8]; * 100 * 0x64
+ char uid[8]; * 108 * 0x6c
+ char gid[8]; * 116 * 0x74
+ char size[12]; * 124 * 0x7c
+ char mtime[12]; * 136 * 0x88
+ char chksum[8]; * 148 * 0x94
+ char typeflag; * 156 * 0x9c
+ char linkname[100]; * 157 * 0x9d
+ char magic[6]; * 257 * 0x101
+ char version[2]; * 263 * 0x107
+ char uname[32]; * 265 * 0x109
+ char gname[32]; * 297 * 0x129
+ char devmajor[8]; * 329 * 0x149
+ char devminor[8]; * 337 * ...
+ char prefix[155]; * 345 *
+ * 500 *
+};
+*/
+
+void KTar::fillBuffer( char * buffer,
+ const char * mode, int size, time_t mtime, char typeflag,
+ const char * uname, const char * gname )
+{
+ // mode (as in stat())
+ assert( strlen(mode) == 6 );
+ strcpy( buffer+0x64, mode );
+ buffer[ 0x6a ] = ' ';
+ buffer[ 0x6b ] = '\0';
+
+ // dummy uid
+ strcpy( buffer + 0x6c, " 765 ");
+ // dummy gid
+ strcpy( buffer + 0x74, " 144 ");
+
+ // size
+ TQCString s;
+ s.sprintf("%o", size); // OCT
+ s = s.rightJustify( 11, ' ' );
+ strcpy( buffer + 0x7c, s.data() );
+ buffer[ 0x87 ] = ' '; // space-terminate (no null after)
+
+ // modification time
+ s.sprintf("%lo", static_cast<unsigned long>(mtime) ); // OCT
+ s = s.rightJustify( 11, ' ' );
+ strcpy( buffer + 0x88, s.data() );
+ buffer[ 0x93 ] = ' '; // space-terminate (no null after)
+
+ // spaces, replaced by the check sum later
+ buffer[ 0x94 ] = 0x20;
+ buffer[ 0x95 ] = 0x20;
+ buffer[ 0x96 ] = 0x20;
+ buffer[ 0x97 ] = 0x20;
+ buffer[ 0x98 ] = 0x20;
+ buffer[ 0x99 ] = 0x20;
+
+ /* From the tar sources :
+ Fill in the checksum field. It's formatted differently from the
+ other fields: it has [6] digits, a null, then a space -- rather than
+ digits, a space, then a null. */
+
+ buffer[ 0x9a ] = '\0';
+ buffer[ 0x9b ] = ' ';
+
+ // type flag (dir, file, link)
+ buffer[ 0x9c ] = typeflag;
+
+ // magic + version
+ strcpy( buffer + 0x101, "ustar");
+ strcpy( buffer + 0x107, "00" );
+
+ // user
+ strcpy( buffer + 0x109, uname );
+ // group
+ strcpy( buffer + 0x129, gname );
+
+ // Header check sum
+ int check = 32;
+ for( uint j = 0; j < 0x200; ++j )
+ check += buffer[j];
+ s.sprintf("%o", check ); // OCT
+ s = s.rightJustify( 7, ' ' );
+ strcpy( buffer + 0x94, s.data() );
+}
+
+void KTar::writeLonglink(char *buffer, const TQCString &name, char typeflag,
+ const char *uname, const char *gname) {
+ strcpy( buffer, "././@LongLink" );
+ int namelen = name.length() + 1;
+ fillBuffer( buffer, " 0", namelen, 0, typeflag, uname, gname );
+ device()->writeBlock( buffer, 0x200 );
+ int offset = 0;
+ while (namelen > 0) {
+ int chunksize = QMIN(namelen, 0x200);
+ memcpy(buffer, name.data()+offset, chunksize);
+ // write long name
+ device()->writeBlock( buffer, 0x200 );
+ // not even needed to reclear the buffer, tar doesn't do it
+ namelen -= chunksize;
+ offset += 0x200;
+ }/*wend*/
+}
+
+bool KTar::prepareWriting(const TQString& name, const TQString& user,
+ const TQString& group, uint size, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime) {
+ return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
+}
+
+bool KTar::prepareWriting_impl(const TQString &name, const TQString &user,
+ const TQString &group, uint size, mode_t perm,
+ time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
+ if ( !isOpened() )
+ {
+ kdWarning(7041) << "KTar::prepareWriting: You must open the tar file before writing to it\n";
+ return false;
+ }
+
+ if ( !(mode() & IO_WriteOnly) )
+ {
+ kdWarning(7041) << "KTar::prepareWriting: You must open the tar file for writing\n";
+ return false;
+ }
+
+ // In some tar files we can find dir/./file => call cleanDirPath
+ TQString fileName ( TQDir::cleanDirPath( name ) );
+
+ /*
+ // Create toplevel dirs
+ // Commented out by David since it's not necessary, and if anybody thinks it is,
+ // he needs to implement a findOrCreate equivalent in writeDir.
+ // But as KTar and the "tar" program both handle tar files without
+ // dir entries, there's really no need for that
+ TQString tmp ( fileName );
+ int i = tmp.findRev( '/' );
+ if ( i != -1 )
+ {
+ TQString d = tmp.left( i + 1 ); // contains trailing slash
+ if ( !m_dirList.contains( d ) )
+ {
+ tmp = tmp.mid( i + 1 );
+ writeDir( d, user, group ); // WARNING : this one doesn't create its toplevel dirs
+ }
+ }
+ */
+
+ char buffer[ 0x201 ];
+ memset( buffer, 0, 0x200 );
+ if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
+
+ // provide converted stuff we need lateron
+ TQCString encodedFilename = TQFile::encodeName(fileName);
+ TQCString uname = user.local8Bit();
+ TQCString gname = group.local8Bit();
+
+ // If more than 100 chars, we need to use the LongLink trick
+ if ( fileName.length() > 99 )
+ writeLonglink(buffer,encodedFilename,'L',uname,gname);
+
+ // Write (potentially truncated) name
+ strncpy( buffer, encodedFilename, 99 );
+ buffer[99] = 0;
+ // zero out the rest (except for what gets filled anyways)
+ memset(buffer+0x9d, 0, 0x200 - 0x9d);
+
+ TQCString permstr;
+ permstr.sprintf("%o",perm);
+ permstr = permstr.rightJustify(6, ' ');
+ fillBuffer(buffer, permstr, size, mtime, 0x30, uname, gname);
+
+ // Write header
+ return device()->writeBlock( buffer, 0x200 ) == 0x200;
+}
+
+bool KTar::writeDir(const TQString& name, const TQString& user,
+ const TQString& group, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime) {
+ return KArchive::writeDir(name,user,group,perm,atime,mtime,ctime);
+}
+
+bool KTar::writeDir_impl(const TQString &name, const TQString &user,
+ const TQString &group, mode_t perm,
+ time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
+ if ( !isOpened() )
+ {
+ kdWarning(7041) << "KTar::writeDir: You must open the tar file before writing to it\n";
+ return false;
+ }
+
+ if ( !(mode() & IO_WriteOnly) )
+ {
+ kdWarning(7041) << "KTar::writeDir: You must open the tar file for writing\n";
+ return false;
+ }
+
+ // In some tar files we can find dir/./ => call cleanDirPath
+ TQString dirName ( TQDir::cleanDirPath( name ) );
+
+ // Need trailing '/'
+ if ( dirName.right(1) != "/" )
+ dirName += "/";
+
+ if ( d->dirList.contains( dirName ) )
+ return true; // already there
+
+ char buffer[ 0x201 ];
+ memset( buffer, 0, 0x200 );
+ if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
+
+ // provide converted stuff we need lateron
+ TQCString encodedDirname = TQFile::encodeName(dirName);
+ TQCString uname = user.local8Bit();
+ TQCString gname = group.local8Bit();
+
+ // If more than 100 chars, we need to use the LongLink trick
+ if ( dirName.length() > 99 )
+ writeLonglink(buffer,encodedDirname,'L',uname,gname);
+
+ // Write (potentially truncated) name
+ strncpy( buffer, encodedDirname, 99 );
+ buffer[99] = 0;
+ // zero out the rest (except for what gets filled anyways)
+ memset(buffer+0x9d, 0, 0x200 - 0x9d);
+
+ TQCString permstr;
+ permstr.sprintf("%o",perm);
+ permstr = permstr.rightJustify(6, ' ');
+ fillBuffer( buffer, permstr, 0, mtime, 0x35, uname, gname);
+
+ // Write header
+ device()->writeBlock( buffer, 0x200 );
+ if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at();
+
+ d->dirList.append( dirName ); // contains trailing slash
+ return true; // TODO if wanted, better error control
+}
+
+bool KTar::writeSymLink(const TQString &name, const TQString &target,
+ const TQString &user, const TQString &group,
+ mode_t perm, time_t atime, time_t mtime, time_t ctime) {
+ return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime);
+}
+
+bool KTar::writeSymLink_impl(const TQString &name, const TQString &target,
+ const TQString &user, const TQString &group,
+ mode_t perm, time_t /*atime*/, time_t mtime, time_t /*ctime*/) {
+ if ( !isOpened() )
+ {
+ kdWarning(7041) << "KTar::writeSymLink: You must open the tar file before writing to it\n";
+ return false;
+ }
+
+ if ( !(mode() & IO_WriteOnly) )
+ {
+ kdWarning(7041) << "KTar::writeSymLink: You must open the tar file for writing\n";
+ return false;
+ }
+
+ device()->flush();
+
+ // In some tar files we can find dir/./file => call cleanDirPath
+ TQString fileName ( TQDir::cleanDirPath( name ) );
+
+ char buffer[ 0x201 ];
+ memset( buffer, 0, 0x200 );
+ if ( mode() & IO_ReadWrite ) device()->at(d->tarEnd); // Go to end of archive as might have moved with a read
+
+ // provide converted stuff we need lateron
+ TQCString encodedFilename = TQFile::encodeName(fileName);
+ TQCString encodedTarget = TQFile::encodeName(target);
+ TQCString uname = user.local8Bit();
+ TQCString gname = group.local8Bit();
+
+ // If more than 100 chars, we need to use the LongLink trick
+ if (target.length() > 99)
+ writeLonglink(buffer,encodedTarget,'K',uname,gname);
+ if ( fileName.length() > 99 )
+ writeLonglink(buffer,encodedFilename,'L',uname,gname);
+
+ // Write (potentially truncated) name
+ strncpy( buffer, encodedFilename, 99 );
+ buffer[99] = 0;
+ // Write (potentially truncated) symlink target
+ strncpy(buffer+0x9d, encodedTarget, 99);
+ buffer[0x9d+99] = 0;
+ // zero out the rest
+ memset(buffer+0x9d+100, 0, 0x200 - 100 - 0x9d);
+
+ TQCString permstr;
+ permstr.sprintf("%o",perm);
+ permstr = permstr.rightJustify(6, ' ');
+ fillBuffer(buffer, permstr, 0, mtime, 0x32, uname, gname);
+
+ // Write header
+ bool retval = device()->writeBlock( buffer, 0x200 ) == 0x200;
+ if ( mode() & IO_ReadWrite ) d->tarEnd = device()->at();
+ return retval;
+}
+
+void KTar::virtual_hook( int id, void* data ) {
+ switch (id) {
+ case VIRTUAL_WRITE_SYMLINK: {
+ WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
+ params->retval = writeSymLink_impl(*params->name,*params->target,
+ *params->user,*params->group,params->perm,
+ params->atime,params->mtime,params->ctime);
+ break;
+ }
+ case VIRTUAL_WRITE_DIR: {
+ WriteDirParams *params = reinterpret_cast<WriteDirParams *>(data);
+ params->retval = writeDir_impl(*params->name,*params->user,
+ *params->group,params->perm,
+ params->atime,params->mtime,params->ctime);
+ break;
+ }
+ case VIRTUAL_PREPARE_WRITING: {
+ PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
+ params->retval = prepareWriting_impl(*params->name,*params->user,
+ *params->group,params->size,params->perm,
+ params->atime,params->mtime,params->ctime);
+ break;
+ }
+ default:
+ KArchive::virtual_hook( id, data );
+ }/*end switch*/
+}
+
diff --git a/tdeio/tdeio/ktar.h b/tdeio/tdeio/ktar.h
new file mode 100644
index 000000000..fc238073c
--- /dev/null
+++ b/tdeio/tdeio/ktar.h
@@ -0,0 +1,171 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+ Copyright (C) 2003 Leo Savernik <l.savernik@aon.at>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __ktar_h
+#define __ktar_h
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <tqdatetime.h>
+#include <tqstring.h>
+#include <tqstringlist.h>
+#include <tqdict.h>
+
+#include <karchive.h>
+
+/**
+ * A class for reading / writing (optionally compressed) tar archives.
+ *
+ * KTar allows you to read and write tar archives, including those
+ * that are compressed using gzip, bzip2, or xz.
+ *
+ * @author Torben Weis <weis@kde.org>, David Faure <faure@kde.org>
+ */
+class TDEIO_EXPORT KTar : public KArchive
+{
+public:
+ /**
+ * Creates an instance that operates on the given filename
+ * using the compression filter associated to given mimetype.
+ *
+ * @param filename is a local path (e.g. "/home/weis/myfile.tgz")
+ * @param mimetype "application/x-gzip", "application/x-bzip2",
+ * or "application/x-xz"
+ * Do not use application/x-tgz or similar - you only need to
+ * specify the compression layer ! If the mimetype is omitted, it
+ * will be determined from the filename.
+ */
+ KTar( const TQString& filename, const TQString & mimetype = TQString::null );
+
+ /**
+ * Creates an instance that operates on the given device.
+ * The device can be compressed (KFilterDev) or not (TQFile, etc.).
+ * @warning Do not assume that giving a TQFile here will decompress the file,
+ * in case it's compressed!
+ * @param dev the device to read from. If the source is compressed, the
+ * TQIODevice must take care of decompression
+ */
+ KTar( TQIODevice * dev );
+
+ /**
+ * If the tar ball is still opened, then it will be
+ * closed automatically by the destructor.
+ */
+ virtual ~KTar();
+
+ /**
+ * The name of the tar file, as passed to the constructor
+ * Null if you used the TQIODevice constructor.
+ * @return the name of the file, or TQString::null if unknown
+ */
+ TQString fileName() { return m_filename; } // TODO KDE4 const
+
+ /**
+ * Special function for setting the "original file name" in the gzip header,
+ * when writing a tar.gz file. It appears when using in the "file" command,
+ * for instance. Should only be called if the underlying device is a KFilterDev!
+ * @param fileName the original file name
+ */
+ void setOrigFileName( const TQCString & fileName );
+
+ // TODO(BIC) make virtual. For now it must be implemented by virtual_hook.
+ bool writeSymLink(const TQString &name, const TQString &target,
+ const TQString &user, const TQString &group,
+ mode_t perm, time_t atime, time_t mtime, time_t ctime);
+ virtual bool writeDir( const TQString& name, const TQString& user, const TQString& group );
+ // TODO(BIC) make virtual. For now it must be implemented by virtual_hook.
+ bool writeDir( const TQString& name, const TQString& user, const TQString& group,
+ mode_t perm, time_t atime, time_t mtime, time_t ctime );
+ virtual bool prepareWriting( const TQString& name, const TQString& user, const TQString& group, uint size );
+ // TODO(BIC) make virtual. For now it must be implemented by virtual_hook.
+ bool prepareWriting( const TQString& name, const TQString& user,
+ const TQString& group, uint size, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime );
+ virtual bool doneWriting( uint size );
+
+protected:
+ /**
+ * Opens the archive for reading.
+ * Parses the directory listing of the archive
+ * and creates the KArchiveDirectory/KArchiveFile entries.
+ * @param mode the mode of the file
+ */
+ virtual bool openArchive( int mode );
+ virtual bool closeArchive();
+
+private:
+ /**
+ * @internal
+ */
+ void prepareDevice( const TQString & filename, const TQString & mimetype, bool forced = false );
+
+ /**
+ * @internal
+ * Fills @p buffer for writing a file as required by the tar format
+ * Has to be called LAST, since it does the checksum
+ * (normally, only the name has to be filled in before)
+ * @param mode is expected to be 6 chars long, [uname and gname 31].
+ */
+ void fillBuffer( char * buffer, const char * mode, int size, time_t mtime,
+ char typeflag, const char * uname, const char * gname );
+
+ /**
+ * @internal
+ * Writes an overlong name into a special longlink entry. Call this
+ * if the file name or symlink target (or both) are longer than 99 chars.
+ * @p buffer buffer at least 0x200 bytes big to be used as a write buffer
+ * @p name 8-bit encoded file name to be written
+ * @p typeflag specifying the type of the entry, 'L' for filenames or
+ * 'K' for symlink targets.
+ * @p uname user name
+ * @p gname group name
+ */
+ void writeLonglink(char *buffer, const TQCString &name, char typeflag,
+ const char *uname, const char *gname);
+
+ TQ_LONG readRawHeader(char *buffer);
+ bool readLonglink(char *buffer,TQCString &longlink);
+ TQ_LONG readHeader(char *buffer,TQString &name,TQString &symlink);
+
+ TQString m_filename;
+protected:
+ virtual void virtual_hook( int id, void* data );
+ bool prepareWriting_impl(const TQString& name, const TQString& user,
+ const TQString& group, uint size, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime);
+ bool writeDir_impl(const TQString& name, const TQString& user,
+ const TQString& group, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime );
+ bool writeSymLink_impl(const TQString &name, const TQString &target,
+ const TQString &user, const TQString &group,
+ mode_t perm, time_t atime, time_t mtime, time_t ctime);
+private:
+ class KTarPrivate;
+ KTarPrivate * d;
+};
+
+/**
+ * Old, deprecated naming
+ */
+#define KTarGz KTar
+#define KTarEntry KArchiveEntry
+#define KTarFile KArchiveFile
+#define KTarDirectory KArchiveDirectory
+
+#endif
diff --git a/tdeio/tdeio/ktrader.cpp b/tdeio/tdeio/ktrader.cpp
new file mode 100644
index 000000000..585c6a499
--- /dev/null
+++ b/tdeio/tdeio/ktrader.cpp
@@ -0,0 +1,186 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Torben Weis <weis@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "ktrader.h"
+#include "ktraderparsetree.h"
+
+#include <tqtl.h>
+#include <tqbuffer.h>
+
+#include <kuserprofile.h>
+#include <kstandarddirs.h>
+#include <kstaticdeleter.h>
+#include <kdebug.h>
+
+template class KStaticDeleter<KTrader>;
+
+using namespace TDEIO;
+
+class KTraderSorter
+{
+public:
+ KTraderSorter() { m_pService = 0; };
+ KTraderSorter( const KTraderSorter& s ) : m_userPreference( s.m_userPreference ),
+ m_bAllowAsDefault( s.m_bAllowAsDefault ),
+ m_traderPreference( s.m_traderPreference ), m_pService( s.m_pService ) { }
+ KTraderSorter( const KService::Ptr &_service, double _pref1, int _pref2, bool _default )
+ { m_pService = _service;
+ m_userPreference = _pref2;
+ m_traderPreference = _pref1;
+ m_bAllowAsDefault = _default;
+ }
+
+ KService::Ptr service() const { return m_pService; }
+
+ bool operator< ( const KTraderSorter& ) const;
+
+private:
+ /**
+ * The bigger this number is, the better is this service in
+ * the users opinion.
+ */
+ int m_userPreference;
+ /**
+ * Is it allowed to use this service for default actions.
+ */
+ bool m_bAllowAsDefault;
+
+ /**
+ * The bigger this number is, the better is this service with
+ * respect to the queries preferences expression.
+ */
+ double m_traderPreference;
+
+ KService::Ptr m_pService;
+};
+
+bool KTraderSorter::operator< ( const KTraderSorter& _o ) const
+{
+ if ( _o.m_bAllowAsDefault && !m_bAllowAsDefault )
+ return true;
+ if ( _o.m_userPreference > m_userPreference )
+ return true;
+ if ( _o.m_userPreference < m_userPreference )
+ return false;
+ if ( _o.m_traderPreference > m_traderPreference )
+ return true;
+ return false;
+}
+
+// --------------------------------------------------
+
+KTrader* KTrader::s_self = 0;
+static KStaticDeleter<KTrader> ktradersd;
+
+KTrader* KTrader::self()
+{
+ if ( !s_self )
+ ktradersd.setObject( s_self, new KTrader );
+
+ return s_self;
+}
+
+KTrader::KTrader()
+{
+}
+
+KTrader::~KTrader()
+{
+}
+
+KTrader::OfferList KTrader::query( const TQString& _servicetype, const TQString& _constraint,
+ const TQString& _preferences ) const
+{
+ return query( _servicetype, TQString::null, _constraint, _preferences );
+}
+
+KTrader::OfferList KTrader::query( const TQString& _servicetype, const TQString& _genericServiceType,
+ const TQString& _constraint,
+ const TQString& _preferences ) const
+{
+ // TODO: catch errors here
+ ParseTreeBase::Ptr constr;
+ ParseTreeBase::Ptr prefs;
+
+ if ( !_constraint.isEmpty() )
+ constr = TDEIO::parseConstraints( _constraint );
+
+ if ( !_preferences.isEmpty() )
+ prefs = TDEIO::parsePreferences( _preferences );
+
+ KServiceTypeProfile::OfferList lst;
+ KTrader::OfferList ret;
+
+ // Get all services of this service type.
+ lst = KServiceTypeProfile::offers( _servicetype, _genericServiceType );
+ if ( lst.count() == 0 )
+ return ret;
+
+ if ( !!constr )
+ {
+ // Find all services matching the constraint
+ // and remove the other ones
+ KServiceTypeProfile::OfferList::Iterator it = lst.begin();
+ while( it != lst.end() )
+ {
+ if ( matchConstraint( constr, (*it).service(), lst ) != 1 )
+ it = lst.remove( it );
+ else
+ ++it;
+ }
+ }
+
+ if ( !!prefs )
+ {
+ TQValueList<KTraderSorter> sorter;
+ KServiceTypeProfile::OfferList::Iterator it = lst.begin();
+ for( ; it != lst.end(); ++it )
+ {
+ PreferencesReturn p = matchPreferences( prefs, (*it).service(), lst );
+ if ( p.type == PreferencesReturn::PRT_DOUBLE )
+ sorter.append( KTraderSorter( (*it).service(), p.f, (*it).preference(), (*it).allowAsDefault() ) );
+ }
+ qBubbleSort( sorter );
+
+ TQValueList<KTraderSorter>::Iterator it2 = sorter.begin();
+ for( ; it2 != sorter.end(); ++it2 )
+ ret.prepend( (*it2).service() );
+ }
+ else
+ {
+ KServiceTypeProfile::OfferList::Iterator it = lst.begin();
+ for( ; it != lst.end(); ++it )
+ ret.append( (*it).service() );
+ }
+
+#ifndef NDEBUG
+ TQString query = _servicetype;
+ if ( !_genericServiceType.isEmpty() ) {
+ query += ", ";
+ query += _genericServiceType;
+ }
+ kdDebug(7014) << "query for " << query
+ << " : returning " << ret.count() << " offers" << endl;
+#endif
+ return ret;
+}
+
+void KTrader::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+#include "ktrader.moc"
diff --git a/tdeio/tdeio/ktrader.h b/tdeio/tdeio/ktrader.h
new file mode 100644
index 000000000..c96a64a21
--- /dev/null
+++ b/tdeio/tdeio/ktrader.h
@@ -0,0 +1,295 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Torben Weis <weis@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __ktrader_h__
+#define __ktrader_h__
+
+#include <tqstring.h>
+#include <tqobject.h>
+#include <kservice.h>
+
+/**
+ * A Trader interface, similar to the CORBA Trader.
+ *
+ * Basically, it provides a way for an application to query
+ * all KDE services (that is, applications and components) that match
+ * a specific set of requirements. This allows you to find an
+ * application in real-time without you having to hard-code the name
+ * and/or path of the application.
+ *
+ * \par Examples
+ *
+ * A few examples will make this a lot more clear.
+ *
+ * Say you have an application that will display HTML. In this
+ * example, you don't want to link to tdehtml... and furthermore, you
+ * really don't care if the HTML browser is ours or not, as long as
+ * it works. The way that you formulate your query as well as the way
+ * that you execute the browser depends on whether or not you want the
+ * browser to run stand-alone or embedded.
+ *
+ * If you want the browser to run standalone, then you will limit the
+ * query to search for all services that handle 'text/html' @em and,
+ * furthermore, they must be applications (Type=Application). You
+ * then will use KRun::run() to invoke the application. In "trader-speak",
+ * this looks like this:
+ * \code
+ * KTrader::OfferList offers = KTrader::self()->query("text/html", "Type == 'Application'");
+ * KService::Ptr ptr = offers.first();
+ * KURL::List lst;
+ * lst.append("http://www.kde.org/index.html");
+ * KRun::run(*ptr, lst);
+ * \endcode
+ *
+ * Now, say that you want to list all KParts component that can handle HTML.
+ * \code
+ * KTrader::OfferList offers = KTrader::self()->query("text/html", "KParts/ReadOnlyPart");
+ * \endcode
+ *
+ * If you want to get the preferred KParts component for text/html you could use
+ * KServiceTypeProfile::preferredService("text/html", "KParts/ReadOnlyPart"), although if this is about
+ * loading that component you would rather use KParts::ComponentFactory directly.
+ *
+ *
+ * Please note that when including property names containing arithmetic operators like - or +, then you have
+ * to put brackets around the property name, in order to correctly separate arithmetic operations from
+ * the name. So for example a constraint expression like
+ * X-TDE-Blah < 4
+ * needs to be written as
+ * [X-TDE-Blah] < 4
+ * otherwise it could also be interpreted as
+ * Substract the numeric value of the property "KDE" and "Blah" from the property "X" and make sure it
+ * is less than 4.
+ * Instead of the other meaning, make sure that the numeric value of "X-TDE-Blah" is less than 4.
+ *
+ * See also the formal syntax defined in @ref tradersyntax .
+ *
+ * @short Provides a way to query the KDE infrastructure for specific
+ * applications or components.
+ * @author Torben Weis <weis@kde.org>
+ */
+class TDEIO_EXPORT KTrader : public TQObject
+{
+ Q_OBJECT
+public:
+ /**
+ * A list of services.
+ */
+ typedef TQValueList<KService::Ptr> OfferList;
+ typedef TQValueListIterator<KService::Ptr> OfferListIterator;
+
+ /**
+ * Standard destructor
+ */
+ virtual ~KTrader();
+
+ /**
+ * The main function in the KTrader class.
+ *
+ * It will return a list of services that match your
+ * specifications. The only required parameter is the service
+ * type. This is something like 'text/plain' or 'text/html'. The
+ * constraint parameter is used to limit the possible choices
+ * returned based on the constraints you give it.
+ *
+ * The @p constraint language is rather full. The most common
+ * keywords are AND, OR, NOT, IN, and EXIST, all used in an
+ * almost spoken-word form. An example is:
+ * \code
+ * (Type == 'Service') and (('KParts/ReadOnlyPart' in ServiceTypes) or (exist Exec))
+ * \endcode
+ *
+ * The keys used in the query (Type, ServiceType, Exec) are all
+ * fields found in the .desktop files.
+ *
+ * @param servicetype A service type like 'text/plain', 'text/html', or 'KOfficePlugin'.
+ * @param constraint A constraint to limit the choices returned, TQString::null to
+ * get all services of the given @p servicetype
+ * @param preferences Indicates a particular preference to return, TQString::null to ignore.
+ * Uses an expression in the constraint language that must return
+ * a number
+ *
+ * @return A list of services that satisfy the query
+ * @see http://developer.kde.org/documentation/library/3.5-api/tdelibs-apidocs/tdeio/tdeio/html/tradersyntax.html
+ */
+ virtual OfferList query( const TQString& servicetype,
+ const TQString& constraint = TQString::null,
+ const TQString& preferences = TQString::null) const;
+
+ /**
+ * A variant of query(), that takes two service types as an input.
+ * It is not exactly the same as adding the second service type
+ * in the constraints of the other query call, because this one
+ * takes into account user preferences for this combination of service types.
+ *
+ * Example usage:
+ * To get list of applications that can handle a given mimetype,
+ * set @p servicetype to the mimetype and @p genericServiceType is "Application".
+ * To get list of embeddable components that can handle a given mimetype,
+ * set @p servicetype to the mimetype and @p genericServiceType is "KParts/ReadOnlyPart".
+ *
+ * @param servicetype A service type like 'text/plain', 'text/html', or 'KOfficePlugin'.
+ * @param genericServiceType a basic service type, like 'KParts/ReadOnlyPart' or 'Application'
+ * @param constraint A constraint to limit the choices returned, TQString::null to
+ * get all services of the given @p servicetype
+ * @param preferences Indicates a particular preference to return, TQString::null to ignore.
+ * Uses an expression in the constraint language that must return
+ * a number
+ *
+ * @return A list of services that satisfy the query
+ * @see http://developer.kde.org/documentation/library/kdeqt/tradersyntax.html
+ */
+ OfferList query( const TQString& servicetype, const TQString& genericServiceType,
+ const TQString& constraint /*= TQString::null*/,
+ const TQString& preferences /*= TQString::null*/) const;
+
+ /**
+ * This is a static pointer to a KTrader instance.
+ *
+ * You will need
+ * to use this to access the KTrader functionality since the
+ * constuctors are protected.
+ *
+ * @return Static KTrader instance
+ */
+ static KTrader* self();
+
+protected:
+ /**
+ * @internal
+ */
+ KTrader();
+
+private:
+ static KTrader* s_self;
+protected:
+ virtual void virtual_hook( int id, void* data );
+};
+
+/** @page tradersyntax Trader Syntax
+ *
+ *
+ * @section Literals
+ *
+ * As elementary atoms of the constraint language, KTrader supports
+ * booleans, integers, floats and strings. Boolean literals are
+ * @a TRUE and @a FALSE . Integers can be positive or negative,
+ * i.e. @a 42 and @a -10 are legal values. Floating point
+ * numbers are @a 3.141592535 or @a -999.999 . Scientific notation
+ * like @a 1.5e-2 is not supported. Character literals are delimited
+ * by single quotation marks, e.g. @a 'Bernd' .
+ *
+ *
+ * @section Symbols
+ *
+ * Identifiers in query string are interpreted as property names, which
+ * are listed in the service's <tt>.desktop</tt> file. For example,
+ * <tt>Name</tt> is the name of the service, <tt>ServiceTypes</tt> is a
+ * list of the service types it supports. Note that only properties can
+ * be written as-is which start with an alphabetical character and contain
+ * only alphanumerical characters. Other properties have to be enclosed in
+ * brackets, e.g. <tt>[X-TDE-Init]</tt>. Properties must not contain any
+ * special characters other than <tt>-</tt>.
+ *
+ * Special property names:
+ * - <b>DesktopEntryName</b> stands for the filename of the service
+ * desktop entry without any extension. This can be useful to
+ * exclude some specific services.
+ * - <b>DesktopEntryPath</b> stands for the relative or full path
+ * to the .desktop file, see KService::desktopEntryPath. Mentionned
+ * here for completeness, better not use it (things can be moved
+ * around).
+ * - <b>Library</b> is the property whose value is set by
+ * <tt>X-TDE-Library</tt> in the .desktop file. This renaming
+ * happened to conform to the desktop file standard, but the
+ * property name didn't change.
+ *
+ *
+ * @section Comparison
+ *
+ * Supported comparison operators are:
+ *
+ * - <tt>==</tt>
+ * - <tt>!=</tt>
+ * - <tt>&lt;</tt>
+ * - <tt>&lt;=</tt>
+ * - <tt>&gt;</tt>
+ * - <tt>&gt;=</tt>
+ *
+ *
+ * @section Arithmetic Arithmetic and boolean expressions
+ *
+ * - <tt>+</tt>
+ * - <tt>-</tt>
+ * - <tt>*</tt>
+ * - <tt>/</tt>
+ * - <tt>and</tt>
+ * - <tt>or</tt>
+ * - <tt>not</tt>
+ *
+ * Note that the arithmetic operators are possible for integers and
+ * floating point numbers. <tt>-</tt> is both a unary and binary operator,
+ * <tt>not</tt> is a unary operator.
+ *
+ *
+ * @section Other Other operators
+ *
+ * - <tt>~</tt>
+ * - <tt>in</tt>
+ * - <tt>exist</tt>
+ * - <tt>()</tt>
+ *
+ * The tilde operator stands for a substring match. For example,
+ * <tt>KParts ~ 'KParts/ReadOnlyPart'</tt> is TRUE. The membership
+ * operator <tt>in</tt> tests whether a value is in a list. A list is a
+ * string with semi-colon- or comma-separated entries, depending on the
+ * type. An example for the membership operator is
+ * <tt>'text/plain' in ServiceTypes</tt>.
+ * The <tt>exist</tt> tests whether a certain property is defined in the
+ * <tt>.desktop</tt> file. Subexpressions are written in parentheses.
+ *
+ * Warning, testing the contents of a property only works if the property
+ * is specified. There is not support for default values. If the property
+ * might be missing, and you still want such services to be included, you
+ * have to check for existence before testing it. For instance, to say
+ * that MyProp is a boolean that defaults to true, and that you want the
+ * services that have it set to true, use:
+ * <tt>not exist MyProp or MyProp</tt>
+ * Simply testing for <tt>MyProp</tt> would
+ * exclude the services without the property at all.
+ *
+ *
+ * @section Examples
+ *
+ * The following examples show filters for .desktop files.
+ * <tt>Type</tt>, <tt>ServiceTypes</tt> and <tt>MimeType</tt> are
+ * properties in .desktop files. Be aware that within KTrader MimeType
+ * properties are understood as ServiceTypes ones.
+ *
+ *
+ * - <tt>Type == 'Application'</tt>@n
+ * All services that are applications.
+ * - <tt>'KParts/ReadOnlyPart' in ServiceTypes</tt>@n
+ * All read-only KParts.
+ * - <tt>('KParts/ReadOnlyPart' in ServiceTypes) and ('text/plain' in ServiceTypes)</tt>@n
+ * All read-only KParts that handle the mime type 'text/plain'.
+ *
+ * @author Bernd Gehrmann <a href="mailto:bernd@tdevelop.org">bernd@tdevelop.org</a>
+*/
+
+
+#endif
diff --git a/tdeio/tdeio/ktraderparse.cpp b/tdeio/tdeio/ktraderparse.cpp
new file mode 100644
index 000000000..627791c00
--- /dev/null
+++ b/tdeio/tdeio/ktraderparse.cpp
@@ -0,0 +1,159 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <assert.h>
+#include <stdlib.h>
+
+// TODO: Torben: On error free memory!
+
+extern "C"
+{
+#include "ktraderparse.h"
+}
+
+#include "ktraderparsetree.h"
+#include <kdebug.h>
+
+using namespace TDEIO;
+
+static ParseTreeBase::Ptr *pTree = 0;
+static const char* sCode = 0;
+
+ParseTreeBase::Ptr TDEIO::parseConstraints( const TQString& _constr )
+{
+ TQCString str = _constr.utf8();
+ sCode = str.data();
+ KTraderParse_mainParse( sCode );
+ sCode = 0;
+ assert( pTree );
+ return *pTree;
+}
+
+ParseTreeBase::Ptr TDEIO::parsePreferences( const TQString& _prefs )
+{
+ TQCString str = _prefs.utf8();
+ sCode = str.data();
+ KTraderParse_mainParse( sCode );
+ sCode = 0;
+ assert( pTree );
+ return *pTree;
+}
+
+void KTraderParse_setParseTree( void *_ptr1 )
+{
+ if ( !pTree )
+ pTree = new ParseTreeBase::Ptr; // ### leak; should use KStaticDeleter
+ *pTree = static_cast<ParseTreeBase*>( _ptr1 );
+}
+
+
+void KTraderParse_error( const char* err )
+{
+ kdWarning(7014) << "Parsing '" << sCode << "' gave " << err << endl;
+}
+
+void* KTraderParse_newOR( void *_ptr1, void *_ptr2 )
+{
+ return new ParseTreeOR( (ParseTreeBase*)_ptr1, (ParseTreeBase*)_ptr2 );
+}
+
+void* KTraderParse_newAND( void *_ptr1, void *_ptr2 )
+{
+ return new ParseTreeAND( (ParseTreeBase*)_ptr1, (ParseTreeBase*)_ptr2 );
+}
+
+void* KTraderParse_newCMP( void *_ptr1, void *_ptr2, int _i )
+{
+ return new ParseTreeCMP( (ParseTreeBase*)_ptr1, (ParseTreeBase*)_ptr2, _i );
+}
+
+void* KTraderParse_newIN( void *_ptr1, void *_ptr2 )
+{
+ return new ParseTreeIN( (ParseTreeBase*)_ptr1, (ParseTreeBase*)_ptr2 );
+}
+
+void* KTraderParse_newMATCH( void *_ptr1, void *_ptr2 )
+{
+ return new ParseTreeMATCH( (ParseTreeBase*)_ptr1, (ParseTreeBase*)_ptr2 );
+}
+
+void* KTraderParse_newCALC( void *_ptr1, void *_ptr2, int _i )
+{
+ return new ParseTreeCALC( (ParseTreeBase*)_ptr1, (ParseTreeBase*)_ptr2, _i );
+}
+
+void* KTraderParse_newBRACKETS( void *_ptr1 )
+{
+ return new ParseTreeBRACKETS( (ParseTreeBase*)_ptr1 );
+}
+
+void* KTraderParse_newNOT( void *_ptr1 )
+{
+ return new ParseTreeNOT( (ParseTreeBase*)_ptr1 );
+}
+
+void* KTraderParse_newEXIST( char *_ptr1 )
+{
+ ParseTreeEXIST *t = new ParseTreeEXIST( _ptr1 );
+ free(_ptr1);
+ return t;
+}
+
+void* KTraderParse_newID( char *_ptr1 )
+{
+ ParseTreeID *t = new ParseTreeID( _ptr1 );
+ free(_ptr1);
+ return t;
+}
+
+void* KTraderParse_newSTRING( char *_ptr1 )
+{
+ ParseTreeSTRING *t = new ParseTreeSTRING( _ptr1 );
+ free(_ptr1);
+ return t;
+}
+
+void* KTraderParse_newNUM( int _i )
+{
+ return new ParseTreeNUM( _i );
+}
+
+void* KTraderParse_newFLOAT( float _f )
+{
+ return new ParseTreeDOUBLE( _f );
+}
+
+void* KTraderParse_newBOOL( char _b )
+{
+ return new ParseTreeBOOL( (bool)_b );
+}
+
+void* KTraderParse_newMAX2( char *_id )
+{
+ ParseTreeMAX2 *t = new ParseTreeMAX2( _id );
+ free(_id);
+ return t;
+}
+
+void* KTraderParse_newMIN2( char *_id )
+{
+ ParseTreeMIN2 *t = new ParseTreeMIN2( _id );
+ free(_id);
+ return t;
+}
diff --git a/tdeio/tdeio/ktraderparse.h b/tdeio/tdeio/ktraderparse.h
new file mode 100644
index 000000000..bfeb15fe0
--- /dev/null
+++ b/tdeio/tdeio/ktraderparse.h
@@ -0,0 +1,52 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __parse_h__
+#define __parse_h__
+
+/*
+ * Functions definition for yacc
+ */
+void KTraderParse_mainParse( const char *_code );
+void KTraderParse_setParseTree( void *_ptr1 );
+void KTraderParse_error( const char* err );
+void* KTraderParse_newOR( void *_ptr1, void *_ptr2 );
+void* KTraderParse_newAND( void *_ptr1, void *_ptr2 );
+void* KTraderParse_newCMP( void *_ptr1, void *_ptr2, int _i );
+void* KTraderParse_newIN( void *_ptr1, void *_ptr2 );
+void* KTraderParse_newMATCH( void *_ptr1, void *_ptr2 );
+void* KTraderParse_newCALC( void *_ptr1, void *_ptr2, int _i );
+void* KTraderParse_newBRACKETS( void *_ptr1 );
+void* KTraderParse_newNOT( void *_ptr1 );
+void* KTraderParse_newEXIST( char *_ptr1 );
+void* KTraderParse_newID( char *_ptr1 );
+void* KTraderParse_newSTRING( char *_ptr1 );
+void* KTraderParse_newNUM( int _i );
+void* KTraderParse_newFLOAT( float _f );
+void* KTraderParse_newBOOL( char _b );
+
+void* KTraderParse_newWITH( void *_ptr1 );
+void* KTraderParse_newMAX( void *_ptr1 );
+void* KTraderParse_newMIN( void *_ptr1 );
+void* KTraderParse_newMAX2( char *_id );
+void* KTraderParse_newMIN2( char *_id );
+void* KTraderParse_newFIRST();
+void* KTraderParse_newRANDOM();
+
+#endif
diff --git a/tdeio/tdeio/ktraderparsetree.cpp b/tdeio/tdeio/ktraderparsetree.cpp
new file mode 100644
index 000000000..0a04b7918
--- /dev/null
+++ b/tdeio/tdeio/ktraderparsetree.cpp
@@ -0,0 +1,714 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "ktraderparsetree.h"
+
+namespace TDEIO {
+
+bool ParseTreeOR::eval( ParseContext *_context ) const
+{
+ ParseContext c1( _context );
+ ParseContext c2( _context );
+
+// don't evaluate both expressions but return immediately
+// if the first one of them succeeds. Otherwise queries like
+// ((not exist Blah) or (Blah == 'Foo')) do not work, because
+// the evaluation of the second term ends up in a fatal error
+// (Simon)
+
+ if ( !m_pLeft->eval( &c1 ) )
+ return false;
+
+ if ( c1.type != ParseContext::T_BOOL )
+ return false;
+
+ _context->b = c1.b;
+ _context->type = ParseContext::T_BOOL;
+ if ( c1.b )
+ return true;
+
+ if ( !m_pRight->eval( &c2 ) )
+ return false;
+
+ if ( c2.type != ParseContext::T_BOOL )
+ return false;
+
+ _context->b = ( c1.b || c2.b );
+ _context->type = ParseContext::T_BOOL;
+
+ return true;
+}
+
+bool ParseTreeAND::eval( ParseContext *_context ) const
+{
+ _context->type = ParseContext::T_BOOL;
+
+ ParseContext c1( _context );
+ ParseContext c2( _context );
+ if ( !m_pLeft->eval( &c1 ) )
+ return false;
+ if ( c1.type != ParseContext::T_BOOL )
+ return false;
+ if ( !c1.b )
+ {
+ _context->b = false;
+ return true;
+ }
+
+ if ( !m_pRight->eval( &c2 ) )
+ return false;
+ if ( c2.type != ParseContext::T_BOOL )
+ return false;
+
+ _context->b = ( c1.b && c2.b );
+
+ return true;
+}
+
+bool ParseTreeCALC::eval( ParseContext *_context ) const
+{
+ ParseContext c1( _context );
+ ParseContext c2( _context );
+ if ( !m_pLeft->eval( &c1 ) )
+ return false;
+ if ( !m_pRight->eval( &c2 ) )
+ return false;
+
+ // Bool extension
+ if ( c1.type != ParseContext::T_NUM && c1.type != ParseContext::T_DOUBLE && c1.type != ParseContext::T_BOOL )
+ return false;
+ // Bool extension
+ if ( c2.type != ParseContext::T_NUM && c2.type != ParseContext::T_DOUBLE && c2.type != ParseContext::T_BOOL )
+ return false;
+ // Bool extension
+ if ( c1.type == ParseContext::T_BOOL && c2.type == ParseContext::T_BOOL )
+ return false;
+
+ /**
+ * Make types compatible
+ */
+ if ( c1.type == ParseContext::T_NUM && c2.type == ParseContext::T_DOUBLE )
+ {
+ c1.type = ParseContext::T_DOUBLE;
+ c1.f = (double)c1.i;
+ }
+ else if ( c1.type == ParseContext::T_DOUBLE && c2.type == ParseContext::T_NUM )
+ {
+ c2.type = ParseContext::T_DOUBLE;
+ c2.f = (double)c2.i;
+ }
+ // Bool extension
+ else if ( c1.type == ParseContext::T_BOOL && c2.type == ParseContext::T_NUM )
+ {
+ c1.type = ParseContext::T_NUM;
+ if ( c1.b )
+ c1.i = 1;
+ else
+ c1.i = -1;
+ }
+ // Bool extension
+ else if ( c1.type == ParseContext::T_BOOL && c2.type == ParseContext::T_DOUBLE )
+ {
+ c1.type = ParseContext::T_DOUBLE;
+ if ( c1.b )
+ c1.f = 1.0;
+ else
+ c1.f = -1.0;
+ }
+ // Bool extension
+ else if ( c1.type == ParseContext::T_NUM && c2.type == ParseContext::T_BOOL )
+ {
+ c2.type = ParseContext::T_NUM;
+ if ( c2.b )
+ c2.i = 1;
+ else
+ c2.i = -1;
+ }
+ // Bool extension
+ else if ( c1.type == ParseContext::T_DOUBLE && c2.type == ParseContext::T_BOOL )
+ {
+ c2.type = ParseContext::T_DOUBLE;
+ if ( c2.b )
+ c2.f = 1.0;
+ else
+ c2.f = -1.0;
+ }
+
+ _context->type = c1.type;
+
+ /**
+ * Calculate
+ */
+ switch( m_cmd )
+ {
+ case 1: /* Add */
+ if ( c1.type == ParseContext::T_DOUBLE )
+ {
+ _context->f = ( c1.f + c2.f );
+ return true;
+ }
+ if ( c1.type == ParseContext::T_NUM )
+ {
+ _context->i = ( c1.i + c2.i );
+ return true;
+ }
+ break;
+ case 2: /* Sub */
+ if ( c1.type == ParseContext::T_DOUBLE )
+ {
+ _context->f = ( c1.f - c2.f );
+ return true;
+ }
+ if ( c1.type == ParseContext::T_NUM )
+ {
+ _context->i = ( c1.i - c2.i );
+ return true;
+ }
+ break;
+ case 3: /* Mul */
+ if ( c1.type == ParseContext::T_DOUBLE )
+ {
+ //cout << "Double Mult" << endl;
+ _context->f = ( c1.f * c2.f );
+ return true;
+ }
+ if ( c1.type == ParseContext::T_NUM )
+ {
+ _context->i = ( c1.i * c2.i );
+ return true;
+ }
+ break;
+ case 4: /* Div */
+ if ( c1.type == ParseContext::T_DOUBLE )
+ {
+ _context->f = ( c1.f / c2.f );
+ return true;
+ }
+ if ( c1.type == ParseContext::T_NUM )
+ {
+ _context->i = ( c1.i / c2.i );
+ return true;
+ }
+ break;
+ }
+
+ return false;
+}
+
+bool ParseTreeCMP::eval( ParseContext *_context ) const
+{
+ //cout << "CMP 1 cmd=" << m_cmd << endl;
+ ParseContext c1( _context );
+ ParseContext c2( _context );
+ if ( !m_pLeft->eval( &c1 ) )
+ return false;
+
+ if ( !m_pRight->eval( &c2 ) )
+ return false;
+
+ /**
+ * Make types compatible
+ */
+ if ( c1.type == ParseContext::T_NUM && c2.type == ParseContext::T_DOUBLE )
+ {
+ c1.type = ParseContext::T_DOUBLE;
+ c1.f = (double)c1.i;
+ }
+ else if ( c1.type == ParseContext::T_DOUBLE && c2.type == ParseContext::T_NUM )
+ {
+ c2.type = ParseContext::T_DOUBLE;
+ c2.f = (double)c2.i;
+ }
+
+ /**
+ * Compare
+ */
+ _context->type = ParseContext::T_BOOL;
+
+ switch( m_cmd )
+ {
+ case 1: /* EQ */
+ if ( c1.type != c2.type )
+ {
+ _context->b = false;
+ return true;
+ }
+ if ( c1.type == ParseContext::T_STRING )
+ {
+ _context->b = ( c1.str == c2.str );
+ return true;
+ }
+ if ( c1.type == ParseContext::T_BOOL )
+ {
+ _context->b = ( c1.b == c2.b );
+ return true;
+ }
+ if ( c1.type == ParseContext::T_DOUBLE )
+ {
+ _context->b = ( c1.f == c2.f );
+ return true;
+ }
+ if ( c1.type == ParseContext::T_NUM )
+ {
+ _context->b = ( c1.i == c2.i );
+ return true;
+ }
+ break;
+ case 2: /* NEQ */
+ if ( c1.type != c2.type )
+ {
+ _context->b = true;
+ return true;
+ }
+ if ( c1.type == ParseContext::T_STRING )
+ {
+ _context->b = ( c1.str != c2.str );
+ return true;
+ }
+ if ( c1.type == ParseContext::T_BOOL )
+ {
+ _context->b = ( c1.b != c2.b );
+ return true;
+ }
+ if ( c1.type == ParseContext::T_DOUBLE )
+ {
+ _context->b = ( c1.f != c2.f );
+ return true;
+ }
+ if ( c1.type == ParseContext::T_NUM )
+ {
+ _context->b = ( c1.i != c2.i );
+ return true;
+ }
+ break;
+ case 3: /* GEQ */
+ if ( c1.type != c2.type )
+ {
+ _context->b = false;
+ return true;
+ }
+ if ( c1.type == ParseContext::T_DOUBLE )
+ {
+ _context->b = ( c1.f >= c2.f );
+ return true;
+ }
+ if ( c1.type == ParseContext::T_NUM )
+ {
+ _context->b = ( c1.i >= c2.i );
+ return true;
+ }
+ _context->b = false;
+ return true;
+
+ case 4: /* LEQ */
+ if ( c1.type != c2.type )
+ {
+ _context->b = false;
+ return true;
+ }
+ if ( c1.type == ParseContext::T_DOUBLE )
+ {
+ _context->b = ( c1.f <= c2.f );
+ return true;
+ }
+ if ( c1.type == ParseContext::T_NUM )
+ {
+ _context->b = ( c1.i <= c2.i );
+ return true;
+ }
+ _context->b = false;
+ return true;
+
+ case 5: /* < */
+ if ( c1.type != c2.type )
+ {
+ _context->b = false;
+ return true;
+ }
+ if ( c1.type == ParseContext::T_DOUBLE )
+ {
+ _context->b = ( c1.f < c2.f );
+ return true;
+ }
+ if ( c1.type == ParseContext::T_NUM )
+ {
+ _context->b = ( c1.i < c2.i );
+ return true;
+ }
+ _context->b = false;
+ return true;
+
+ case 6: /* > */
+ if ( c1.type != c2.type )
+ {
+ _context->b = false;
+ return true;
+ }
+ if ( c1.type == ParseContext::T_DOUBLE )
+ {
+ _context->b = ( c1.f > c2.f );
+ return true;
+ }
+ if ( c1.type == ParseContext::T_NUM )
+ {
+ _context->b = ( c1.i > c2.i );
+ return true;
+ }
+ _context->b = false;
+ return true;
+
+ }
+
+ return false;
+}
+
+bool ParseTreeNOT::eval( ParseContext *_context ) const
+{
+ ParseContext c1( _context );
+ if ( !m_pLeft->eval( &c1 ) )
+ return false;
+ if ( c1.type != ParseContext::T_BOOL )
+ return false;
+
+ _context->b = !c1.b;
+ _context->type = ParseContext::T_BOOL;
+
+ return true;
+}
+
+bool ParseTreeEXIST::eval( ParseContext *_context ) const
+{
+ _context->type = ParseContext::T_BOOL;
+
+ TQVariant prop = _context->service->property( m_id );
+ _context->b = prop.isValid();
+
+ return true;
+}
+
+bool ParseTreeMATCH::eval( ParseContext *_context ) const
+{
+ _context->type = ParseContext::T_BOOL;
+
+ ParseContext c1( _context );
+ ParseContext c2( _context );
+ if ( !m_pLeft->eval( &c1 ) )
+ return false;
+ if ( !m_pRight->eval( &c2 ) )
+ return false;
+ if ( c1.type != ParseContext::T_STRING || c2.type != ParseContext::T_STRING )
+ return false;
+
+ _context->b = ( c2.str.find( c1.str ) != -1 );
+
+ return true;
+}
+
+bool ParseTreeIN::eval( ParseContext *_context ) const
+{
+ _context->type = ParseContext::T_BOOL;
+
+ ParseContext c1( _context );
+ ParseContext c2( _context );
+ if ( !m_pLeft->eval( &c1 ) )
+ return false;
+ if ( !m_pRight->eval( &c2 ) )
+ return false;
+
+ if ( (c1.type == ParseContext::T_NUM) &&
+ (c2.type == ParseContext::T_SEQ) &&
+ ((*(c2.seq.begin())).type() == TQVariant::Int)) {
+
+ TQValueList<TQVariant>::ConstIterator it = c2.seq.begin();
+ TQValueList<TQVariant>::ConstIterator end = c2.seq.end();
+ _context->b = false;
+ for (; it != end; it++)
+ if ((*it).type() == TQVariant::Int &&
+ (*it).toInt() == c1.i) {
+ _context->b = true;
+ break;
+ }
+ return true;
+ }
+
+ if ( c1.type == ParseContext::T_DOUBLE &&
+ c2.type == ParseContext::T_SEQ &&
+ (*(c2.seq.begin())).type() == TQVariant::Double) {
+
+ TQValueList<TQVariant>::ConstIterator it = c2.seq.begin();
+ TQValueList<TQVariant>::ConstIterator end = c2.seq.end();
+ _context->b = false;
+ for (; it != end; it++)
+ if ((*it).type() == TQVariant::Double &&
+ (*it).toDouble() == c1.i) {
+ _context->b = true;
+ break;
+ }
+ return true;
+ }
+
+ if ( c1.type == ParseContext::T_STRING && c2.type == ParseContext::T_STR_SEQ )
+ {
+ _context->b = ( c2.strSeq.find( c1.str ) != c2.strSeq.end() );
+ return true;
+ }
+
+ return false;
+}
+
+bool ParseTreeID::eval( ParseContext *_context ) const
+{
+ TQVariant prop = _context->service->property( m_str );
+ if ( !prop.isValid() )
+ return false;
+
+ if ( prop.type() == TQVariant::String )
+ {
+ _context->str = prop.toString();
+ _context->type = ParseContext::T_STRING;
+ return true;
+ }
+
+ if ( prop.type() == TQVariant::Int )
+ {
+ _context->i = prop.toInt();
+ _context->type = ParseContext::T_NUM;
+ return true;
+ }
+
+ if ( prop.type() == TQVariant::Bool )
+ {
+ _context->b = prop.toBool();
+ _context->type = ParseContext::T_BOOL;
+ return true;
+ }
+
+ if ( prop.type() == TQVariant::Double )
+ {
+ _context->f = prop.toDouble();
+ _context->type = ParseContext::T_DOUBLE;
+ return true;
+ }
+
+ if ( prop.type() == TQVariant::List )
+ {
+ _context->seq = prop.toList();
+ _context->type = ParseContext::T_SEQ;
+ return true;
+ }
+
+ if ( prop.type() == TQVariant::StringList )
+ {
+ _context->strSeq = prop.toStringList();
+ _context->type = ParseContext::T_STR_SEQ;
+ return true;
+ }
+
+ // Value has unknown type
+ return false;
+}
+
+bool ParseTreeMIN2::eval( ParseContext *_context ) const
+{
+ _context->type = ParseContext::T_DOUBLE;
+
+ TQVariant prop = _context->service->property( m_strId );
+ if ( !prop.isValid() )
+ return false;
+
+ if ( !_context->initMaxima( m_strId ) )
+ return false;
+
+ TQMap<TQString,PreferencesMaxima>::Iterator it = _context->maxima.find( m_strId );
+ if ( it == _context->maxima.end() )
+ return false;
+
+ if ( prop.type() == TQVariant::Int && it.data().type == PreferencesMaxima::PM_INT )
+ {
+ _context->f = (double)( prop.toInt() - it.data().iMin ) /
+ (double)(it.data().iMax - it.data().iMin ) * (-2.0) + 1.0;
+ return true;
+ }
+ else if ( prop.type() == TQVariant::Double && it.data().type == PreferencesMaxima::PM_DOUBLE )
+ {
+ _context->f = ( prop.toDouble() - it.data().fMin ) / (it.data().fMax - it.data().fMin )
+ * (-2.0) + 1.0;
+ return true;
+ }
+
+ return false;
+}
+
+bool ParseTreeMAX2::eval( ParseContext *_context ) const
+{
+ _context->type = ParseContext::T_DOUBLE;
+
+ TQVariant prop = _context->service->property( m_strId );
+ if ( !prop.isValid() )
+ return false;
+
+ // Create extrema
+ if ( !_context->initMaxima( m_strId ) )
+ return false;
+
+ // Find extrema
+ TQMap<TQString,PreferencesMaxima>::Iterator it = _context->maxima.find( m_strId );
+ if ( it == _context->maxima.end() )
+ return false;
+
+ if ( prop.type() == TQVariant::Int && it.data().type == PreferencesMaxima::PM_INT )
+ {
+ _context->f = (double)( prop.toInt() - it.data().iMin ) /
+ (double)(it.data().iMax - it.data().iMin ) * 2.0 - 1.0;
+ return true;
+ }
+ else if ( prop.type() == TQVariant::Double && it.data().type == PreferencesMaxima::PM_DOUBLE )
+ {
+ _context->f = ( prop.toDouble() - it.data().fMin ) /
+ (it.data().fMax - it.data().fMin ) * 2.0 - 1.0;
+ return true;
+ }
+
+ return false;
+}
+
+int matchConstraint( const ParseTreeBase *_tree, const KService::Ptr &_service,
+ const KServiceTypeProfile::OfferList& _list )
+{
+ // Empty tree matches always
+ if ( !_tree )
+ return 1;
+
+ TQMap<TQString,PreferencesMaxima> maxima;
+ ParseContext c( _service, _list, maxima );
+
+ // Error during evaluation ?
+ if ( !_tree->eval( &c ) )
+ return -1;
+
+ // Did we get a bool ?
+ if ( c.type != ParseContext::T_BOOL )
+ return -1;
+
+ return ( c.b ? 1 : 0 );
+}
+
+PreferencesReturn matchPreferences( const ParseTreeBase *_tree, const KService::Ptr &_service,
+ const KServiceTypeProfile::OfferList& _list )
+{
+ // By default: error
+ PreferencesReturn ret;
+
+ if ( !_tree )
+ return ret;
+
+ TQMap<TQString,PreferencesMaxima> maxima;
+ ParseContext c( _service, _list, maxima );
+
+ if ( !_tree->eval( &c ) )
+ return ret;
+
+ // Did we get a numeric return value ?
+ if ( c.type == ParseContext::T_NUM )
+ {
+ ret.type = PreferencesReturn::PRT_DOUBLE;
+ ret.f = (double)c.i;
+ }
+ else if ( c.type == ParseContext::T_DOUBLE )
+ {
+ ret.type = PreferencesReturn::PRT_DOUBLE;
+ ret.f = c.f;
+ }
+
+ return ret;
+}
+
+bool ParseContext::initMaxima( const TQString& _prop )
+{
+ // Is the property known ?
+ TQVariant prop = service->property( _prop );
+ if ( !prop.isValid() )
+ return false;
+
+ // Numeric ?
+ if ( prop.type() != TQVariant::Int && prop.type() != TQVariant::Double )
+ return false;
+
+ // Did we cache the result ?
+ TQMap<TQString,PreferencesMaxima>::Iterator it = maxima.find( _prop );
+ if ( it != maxima.end() )
+ return ( it.data().type == PreferencesMaxima::PM_DOUBLE ||
+ it.data().type == PreferencesMaxima::PM_INT );
+
+ // Double or Int ?
+ PreferencesMaxima extrema;
+ if ( prop.type() == TQVariant::Int )
+ extrema.type = PreferencesMaxima::PM_INVALID_INT;
+ else
+ extrema.type = PreferencesMaxima::PM_INVALID_DOUBLE;
+
+ // Iterate over all offers
+ KServiceTypeProfile::OfferList::ConstIterator oit = offers.begin();
+ for( ; oit != offers.end(); ++oit )
+ {
+ TQVariant p = (*oit).service()->property( _prop );
+ if ( p.isValid() )
+ {
+ // Determine new maximum/minimum
+ if ( extrema.type == PreferencesMaxima::PM_INVALID_INT )
+ {
+ extrema.type = PreferencesMaxima::PM_INT;
+ extrema.iMin = p.toInt();
+ extrema.iMax = p.toInt();
+ }
+ // Correct existing extrema
+ else if ( extrema.type == PreferencesMaxima::PM_INT )
+ {
+ if ( p.toInt() < extrema.iMin )
+ extrema.iMin = p.toInt();
+ if ( p.toInt() > extrema.iMax )
+ extrema.iMax = p.toInt();
+ }
+ // Determine new maximum/minimum
+ else if ( extrema.type == PreferencesMaxima::PM_INVALID_DOUBLE )
+ {
+ extrema.type = PreferencesMaxima::PM_DOUBLE;
+ extrema.fMin = p.toDouble();
+ extrema.fMax = p.toDouble();
+ }
+ // Correct existing extrema
+ else if ( extrema.type == PreferencesMaxima::PM_DOUBLE )
+ {
+ if ( p.toDouble() < it.data().fMin )
+ extrema.fMin = p.toDouble();
+ if ( p.toDouble() > it.data().fMax )
+ extrema.fMax = p.toDouble();
+ }
+ }
+ }
+
+ // Cache the result
+ maxima.insert( _prop, extrema );
+
+ // Did we succeed ?
+ return ( extrema.type == PreferencesMaxima::PM_DOUBLE ||
+ extrema.type == PreferencesMaxima::PM_INT );
+}
+
+}
diff --git a/tdeio/tdeio/ktraderparsetree.h b/tdeio/tdeio/ktraderparsetree.h
new file mode 100644
index 000000000..a08b61a5a
--- /dev/null
+++ b/tdeio/tdeio/ktraderparsetree.h
@@ -0,0 +1,371 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __parse_tree_h__
+#define __parse_tree_h__
+
+#include <tqstring.h>
+#include <tqstringlist.h>
+#include <tqvaluelist.h>
+#include <tqmap.h>
+#include <tqshared.h>
+
+#include <kservice.h>
+#include <kuserprofile.h>
+
+#include "ktrader.h"
+
+namespace TDEIO {
+
+class ParseTreeBase;
+
+/** \internal */
+struct TDEIO_EXPORT PreferencesReturn
+{
+ enum Type { PRT_DOUBLE, PRT_ERROR };
+
+ PreferencesReturn() { type = PRT_ERROR; }
+
+ PreferencesReturn( const PreferencesReturn& _r )
+ {
+ type = _r.type;
+ f = _r.f;
+ }
+
+ Type type;
+ double f;
+};
+
+
+/**
+ * @internal
+ * @return 0 => Does not match
+ * 1 => Does match
+ * <0 => Error
+ */
+TDEIO_EXPORT int matchConstraint( const ParseTreeBase *_tree, const KService::Ptr &,
+ const KServiceTypeProfile::OfferList& );
+
+/**
+ * @internal
+ * @return 1 on success or <0 on Error
+ */
+TDEIO_EXPORT PreferencesReturn matchPreferences( const ParseTreeBase *_tree, const KService::Ptr &,
+ const KServiceTypeProfile::OfferList& );
+
+/**
+ * @internal
+ */
+struct TDEIO_EXPORT PreferencesMaxima
+{
+ enum Type { PM_ERROR, PM_INVALID_INT, PM_INVALID_DOUBLE, PM_DOUBLE, PM_INT };
+
+ Type type;
+ int iMax;
+ int iMin;
+ double fMax;
+ double fMin;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseContext
+{
+public:
+ /**
+ * This is NOT a copy constructor.
+ */
+ ParseContext( const ParseContext* _ctx ) : service( _ctx->service ), maxima( _ctx->maxima ),
+ offers( _ctx->offers ) {}
+ ParseContext( const KService::Ptr & _service, const KServiceTypeProfile::OfferList& _offers,
+ TQMap<TQString,PreferencesMaxima>& _m )
+ : service( _service ), maxima( _m ), offers( _offers ) {}
+
+ bool initMaxima( const TQString& _prop);
+
+ enum Type { T_STRING = 1, T_DOUBLE = 2, T_NUM = 3, T_BOOL = 4,
+ T_STR_SEQ = 5, T_SEQ = 6 };
+
+ TQString str;
+ int i;
+ double f;
+ bool b;
+ TQValueList<TQVariant> seq;
+ TQStringList strSeq;
+ Type type;
+
+ KService::Ptr service;
+
+ TQMap<TQString,PreferencesMaxima>& maxima;
+ const KServiceTypeProfile::OfferList& offers;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeBase : public KShared
+{
+public:
+ typedef KSharedPtr<ParseTreeBase> Ptr;
+ ParseTreeBase() { }
+
+ virtual bool eval( ParseContext *_context ) const = 0;
+protected:
+ virtual ~ParseTreeBase() { };
+};
+
+TDEIO_EXPORT ParseTreeBase::Ptr parseConstraints( const TQString& _constr );
+TDEIO_EXPORT ParseTreeBase::Ptr parsePreferences( const TQString& _prefs );
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeOR : public ParseTreeBase
+{
+public:
+ ParseTreeOR( ParseTreeBase *_ptr1, ParseTreeBase *_ptr2 ) { m_pLeft = _ptr1; m_pRight = _ptr2; }
+
+ bool eval( ParseContext *_context ) const;
+
+protected:
+ ParseTreeBase::Ptr m_pLeft;
+ ParseTreeBase::Ptr m_pRight;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeAND : public ParseTreeBase
+{
+public:
+ ParseTreeAND( ParseTreeBase *_ptr1, ParseTreeBase *_ptr2 ) { m_pLeft = _ptr1; m_pRight = _ptr2; }
+
+ bool eval( ParseContext *_context ) const;
+
+protected:
+ ParseTreeBase::Ptr m_pLeft;
+ ParseTreeBase::Ptr m_pRight;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeCMP : public ParseTreeBase
+{
+public:
+ ParseTreeCMP( ParseTreeBase *_ptr1, ParseTreeBase *_ptr2, int _i ) { m_pLeft = _ptr1; m_pRight = _ptr2; m_cmd = _i; }
+
+ bool eval( ParseContext *_context ) const;
+
+protected:
+ ParseTreeBase::Ptr m_pLeft;
+ ParseTreeBase::Ptr m_pRight;
+ int m_cmd;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeIN : public ParseTreeBase
+{
+public:
+ ParseTreeIN( ParseTreeBase *_ptr1, ParseTreeBase *_ptr2 ) { m_pLeft = _ptr1; m_pRight = _ptr2; }
+
+ bool eval( ParseContext *_context ) const;
+
+protected:
+ ParseTreeBase::Ptr m_pLeft;
+ ParseTreeBase::Ptr m_pRight;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeMATCH : public ParseTreeBase
+{
+public:
+ ParseTreeMATCH( ParseTreeBase *_ptr1, ParseTreeBase *_ptr2 ) { m_pLeft = _ptr1; m_pRight = _ptr2; }
+
+ bool eval( ParseContext *_context ) const;
+
+protected:
+ ParseTreeBase::Ptr m_pLeft;
+ ParseTreeBase::Ptr m_pRight;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeCALC : public ParseTreeBase
+{
+public:
+ ParseTreeCALC( ParseTreeBase *_ptr1, ParseTreeBase *_ptr2, int _i ) { m_pLeft = _ptr1; m_pRight = _ptr2; m_cmd = _i; }
+
+ bool eval( ParseContext *_context ) const;
+
+protected:
+ ParseTreeBase::Ptr m_pLeft;
+ ParseTreeBase::Ptr m_pRight;
+ int m_cmd;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeBRACKETS : public ParseTreeBase
+{
+public:
+ ParseTreeBRACKETS( ParseTreeBase *_ptr ) { m_pLeft = _ptr; }
+
+ bool eval( ParseContext *_context ) const { return m_pLeft->eval( _context ); }
+
+protected:
+ ParseTreeBase::Ptr m_pLeft;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeNOT : public ParseTreeBase
+{
+public:
+ ParseTreeNOT( ParseTreeBase *_ptr ) { m_pLeft = _ptr; }
+
+ bool eval( ParseContext *_context ) const;
+
+protected:
+ ParseTreeBase::Ptr m_pLeft;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeEXIST : public ParseTreeBase
+{
+public:
+ ParseTreeEXIST( const char *_id ) { m_id = _id; }
+
+ bool eval( ParseContext *_context ) const;
+
+protected:
+ TQString m_id;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeID : public ParseTreeBase
+{
+public:
+ ParseTreeID( const char *arg ) { m_str = arg; }
+
+ bool eval( ParseContext *_context ) const;
+
+protected:
+ TQString m_str;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeSTRING : public ParseTreeBase
+{
+public:
+ ParseTreeSTRING( const char *arg ) { m_str = arg; }
+
+ bool eval( ParseContext *_context ) const { _context->type = ParseContext::T_STRING; _context->str = m_str; return true; }
+
+protected:
+ TQString m_str;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeNUM : public ParseTreeBase
+{
+public:
+ ParseTreeNUM( int arg ) { m_int = arg; }
+
+ bool eval( ParseContext *_context ) const { _context->type = ParseContext::T_NUM; _context->i = m_int; return true; }
+
+protected:
+ int m_int;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeDOUBLE : public ParseTreeBase
+{
+public:
+ ParseTreeDOUBLE( double arg ) { m_double = arg; }
+
+ bool eval( ParseContext *_context ) const { _context->type = ParseContext::T_DOUBLE; _context->f = m_double; return true; }
+
+protected:
+ double m_double;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeBOOL : public ParseTreeBase
+{
+public:
+ ParseTreeBOOL( bool arg ) { m_bool = arg; }
+
+ bool eval( ParseContext *_context ) const { _context->type = ParseContext::T_BOOL; _context->b = m_bool; return true; }
+
+protected:
+ bool m_bool;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeMAX2 : public ParseTreeBase
+{
+public:
+ ParseTreeMAX2( const char *_id ) { m_strId = _id; }
+
+ bool eval( ParseContext *_context ) const;
+
+protected:
+ TQString m_strId;
+};
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT ParseTreeMIN2 : public ParseTreeBase
+{
+public:
+ ParseTreeMIN2( const char *_id ) { m_strId = _id; }
+
+ bool eval( ParseContext *_context ) const;
+
+protected:
+ TQString m_strId;
+};
+
+}
+
+#endif
diff --git a/tdeio/tdeio/kurifilter.cpp b/tdeio/tdeio/kurifilter.cpp
new file mode 100644
index 000000000..5c50f4fa9
--- /dev/null
+++ b/tdeio/tdeio/kurifilter.cpp
@@ -0,0 +1,451 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 2000 Yves Arrouye <yves@realnames.com>
+ * Copyright (C) 2000 Dawit Alemayehu <adawit at kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include <config.h>
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <ktrader.h>
+#include <kmimetype.h>
+#include <klibloader.h>
+#include <kstaticdeleter.h>
+#include <tdeparts/componentfactory.h>
+
+#ifdef HAVE_ELFICON
+#include <tqimage.h>
+#include "tdelficon.h"
+#endif // HAVE_ELFICON
+
+#include "kurifilter.h"
+
+template class TQPtrList<KURIFilterPlugin>;
+
+KURIFilterPlugin::KURIFilterPlugin( TQObject *parent, const char *name, double pri )
+ :TQObject( parent, name )
+{
+ m_strName = TQString::fromLatin1( name );
+ m_dblPriority = pri;
+}
+
+void KURIFilterPlugin::setFilteredURI( KURIFilterData& data, const KURL& uri ) const
+{
+ if ( data.uri() != uri )
+ {
+ data.m_pURI = uri;
+ data.m_bChanged = true;
+ }
+}
+
+class KURIFilterDataPrivate
+{
+public:
+ KURIFilterDataPrivate() {};
+ TQString abs_path;
+ TQString args;
+ TQString typedString;
+};
+
+KURIFilterData::KURIFilterData( const KURIFilterData& data )
+{
+ m_iType = data.m_iType;
+ m_pURI = data.m_pURI;
+ m_strErrMsg = data.m_strErrMsg;
+ m_strIconName = data.m_strIconName;
+ m_bChanged = data.m_bChanged;
+ m_bCheckForExecutables = data.m_bCheckForExecutables;
+ d = new KURIFilterDataPrivate;
+ d->abs_path = data.absolutePath();
+ d->typedString = data.typedString();
+ d->args = data.argsAndOptions();
+}
+
+KURIFilterData::~KURIFilterData()
+{
+ delete d;
+ d = 0;
+}
+
+void KURIFilterData::init( const KURL& url )
+{
+ m_iType = KURIFilterData::UNKNOWN;
+ m_pURI = url;
+ m_strErrMsg = TQString::null;
+ m_strIconName = TQString::null;
+ m_bCheckForExecutables = true;
+ m_bChanged = true;
+ d = new KURIFilterDataPrivate;
+ d->typedString = url.url();
+}
+
+void KURIFilterData::init( const TQString& url )
+{
+ m_iType = KURIFilterData::UNKNOWN;
+ m_pURI = url;
+ m_strErrMsg = TQString::null;
+ m_strIconName = TQString::null;
+ m_bCheckForExecutables = true;
+ m_bChanged = true;
+ d = new KURIFilterDataPrivate;
+ d->typedString = url;
+}
+
+void KURIFilterData::reinit(const KURL &url)
+{
+ delete d;
+ init(url);
+}
+
+void KURIFilterData::reinit(const TQString &url)
+{
+ delete d;
+ init(url);
+}
+
+TQString KURIFilterData::typedString() const
+{
+ return d->typedString;
+}
+
+void KURIFilterData::setCheckForExecutables( bool check )
+{
+ m_bCheckForExecutables = check;
+}
+
+bool KURIFilterData::hasArgsAndOptions() const
+{
+ return !d->args.isEmpty();
+}
+
+bool KURIFilterData::hasAbsolutePath() const
+{
+ return !d->abs_path.isEmpty();
+}
+
+bool KURIFilterData::setAbsolutePath( const TQString& absPath )
+{
+ // Since a malformed URL could possibly be a relative
+ // URL we tag it as a possible local resource...
+ if( (!m_pURI.isValid() || m_pURI.isLocalFile()) )
+ {
+ d->abs_path = absPath;
+ return true;
+ }
+ return false;
+}
+
+TQString KURIFilterData::absolutePath() const
+{
+ return d->abs_path;
+}
+
+TQString KURIFilterData::argsAndOptions() const
+{
+ return d->args;
+}
+
+TQString KURIFilterData::iconName()
+{
+ if( m_bChanged )
+ {
+ m_customIconPixmap = TQPixmap();
+ switch ( m_iType )
+ {
+ case KURIFilterData::LOCAL_FILE:
+ case KURIFilterData::LOCAL_DIR:
+ case KURIFilterData::NET_PROTOCOL:
+ {
+ m_strIconName = KMimeType::iconForURL( m_pURI );
+ break;
+ }
+ case KURIFilterData::EXECUTABLE:
+ {
+ TQString exeName = m_pURI.url();
+ exeName = exeName.mid( exeName.findRev( '/' ) + 1 ); // strip path if given
+ KService::Ptr service = KService::serviceByDesktopName( exeName );
+#ifndef HAVE_ELFICON
+ // Try to find an icon with the same name as the binary (useful for non-tde apps)
+ // FIXME: We should only do this if the binary is in the system path somewhere,
+ // otherwise TDE could end up showing system icons for user binaries
+ if (service && service->icon() != TQString::fromLatin1( "unknown" )) {
+ m_strIconName = service->icon();
+ }
+ else if ( !TDEGlobal::iconLoader()->loadIcon( exeName, KIcon::NoGroup, 16, KIcon::DefaultState, 0, true ).isNull() ) {
+ m_strIconName = exeName;
+ }
+ else {
+ // use default
+ m_strIconName = TQString::fromLatin1("exec");
+ }
+#else // HAVE_ELFICON
+ // Try to find an icon with the same name as the binary (useful for non-tde apps)
+ // FIXME: We should only do this if the binary is in the system path somewhere,
+ // otherwise TDE could end up showing system icons for user binaries
+ if (service && service->icon() != TQString::fromLatin1( "unknown" )) {
+ m_strIconName = service->icon();
+ }
+ else if ( !TDEGlobal::iconLoader()->loadIcon( exeName, KIcon::NoGroup, 16, KIcon::DefaultState, 0, true ).isNull() ) {
+ m_strIconName = exeName;
+ }
+ else {
+ // use default
+ m_strIconName = TQString::fromLatin1("exec");
+ }
+ // Try to load from elf file (if supported)
+ // Check for an embedded icon
+ unsigned int icon_size;
+ libr_icon *icon = NULL;
+ libr_file *handle = NULL;
+ libr_access_t access = LIBR_READ;
+ char libr_can_continue = 1;
+
+ if((handle = libr_open(const_cast<char*>(m_pURI.path().ascii()), access)) == NULL)
+ {
+ kdWarning() << "failed to open file" << m_pURI.path() << endl;
+ libr_can_continue = 0;
+ }
+
+ if (libr_can_continue == 1) {
+ icon_size = 32; // FIXME: Is this a reasonable size request for all possible usages of kurifilter?
+ icon = libr_icon_geticon_bysize(handle, icon_size);
+
+ if (libr_can_continue == 1) {
+ // See if the embedded icon name matches any icon file names already on the system
+ // If it does, use the system icon instead of the embedded one
+ int iconresnamefound = 0;
+ iconentry *entry = NULL;
+ iconlist icons;
+ if(!get_iconlist(handle, &icons))
+ {
+ // Failed to obtain a list of ELF icons
+ kdWarning() << "failed to obtain ELF icon: " << libr_errmsg() << endl;
+
+ // See if there is a system icon we can use
+ TQString sysIconName = elf_get_resource(handle, ".metadata_sysicon");
+ if (!sysIconName.isEmpty()) {
+ if (TDEGlobal::iconLoader()->iconPath(sysIconName.ascii(), 0, true) != "") {
+ m_strIconName = sysIconName;
+ }
+ }
+
+ libr_close(handle);
+ libr_can_continue = 0;
+ }
+ else {
+ while((entry = get_nexticon(&icons, entry)) != NULL)
+ {
+ if(icon == NULL)
+ {
+ // Try loading this icon as fallback
+ icon = libr_icon_geticon_byname(handle, entry->name);
+ }
+ if (TDEGlobal::iconLoader()->iconPath(entry->name, 0, true) != "") {
+ iconresnamefound = 1;
+ m_strIconName = entry->name;
+ break;
+ }
+ }
+ }
+
+ if (libr_can_continue == 1) {
+ if ((iconresnamefound == 0) && (icon)) {
+ // Extract the embedded icon
+ size_t icon_data_length;
+ char* icondata = libr_icon_malloc(icon, &icon_data_length);
+ m_customIconPixmap.loadFromData(static_cast<uchar*>(static_cast<void*>(icondata)), icon_data_length); // EVIL CAST
+ if (icon_size != 0) {
+ TQImage ip = m_customIconPixmap.convertToImage();
+ ip = ip.smoothScale(icon_size, icon_size);
+ m_customIconPixmap.convertFromImage(ip);
+ }
+ free(icondata);
+ libr_icon_close(icon);
+ }
+
+ libr_close(handle);
+ }
+ }
+ }
+#endif // HAVE_ELFICON
+ break;
+ }
+ case KURIFilterData::HELP:
+ {
+ m_strIconName = TQString::fromLatin1("khelpcenter");
+ break;
+ }
+ case KURIFilterData::SHELL:
+ {
+ m_strIconName = TQString::fromLatin1("konsole");
+ break;
+ }
+ case KURIFilterData::ERROR:
+ case KURIFilterData::BLOCKED:
+ {
+ m_strIconName = TQString::fromLatin1("error");
+ break;
+ }
+ default:
+ m_strIconName = TQString::null;
+ break;
+ }
+ m_bChanged = false;
+ }
+ return m_strIconName;
+}
+
+TQPixmap KURIFilterData::customIconPixmap()
+{
+ return m_customIconPixmap;
+}
+
+//******************************************** KURIFilterPlugin **********************************************
+void KURIFilterPlugin::setArguments( KURIFilterData& data, const TQString& args ) const
+{
+ data.d->args = args;
+}
+
+//******************************************** KURIFilter **********************************************
+KURIFilter *KURIFilter::s_self;
+static KStaticDeleter<KURIFilter> kurifiltersd;
+
+KURIFilter *KURIFilter::self()
+{
+ if (!s_self)
+ s_self = kurifiltersd.setObject(s_self, new KURIFilter);
+ return s_self;
+}
+
+KURIFilter::KURIFilter()
+{
+ m_lstPlugins.setAutoDelete(true);
+ loadPlugins();
+}
+
+KURIFilter::~KURIFilter()
+{
+}
+
+bool KURIFilter::filterURI( KURIFilterData& data, const TQStringList& filters )
+{
+ bool filtered = false;
+ KURIFilterPluginList use_plugins;
+
+ // If we have a filter list, only include the once
+ // explicitly specified by it. Otherwise, use all available filters...
+ if( filters.isEmpty() )
+ use_plugins = m_lstPlugins; // Use everything that is loaded...
+ else
+ {
+ //kdDebug() << "Named plugins requested..." << endl;
+ for( TQStringList::ConstIterator lst = filters.begin(); lst != filters.end(); ++lst )
+ {
+ TQPtrListIterator<KURIFilterPlugin> it( m_lstPlugins );
+ for( ; it.current() ; ++it )
+ {
+ if( (*lst) == it.current()->name() )
+ {
+ //kdDebug() << "Will use filter plugin named: " << it.current()->name() << endl;
+ use_plugins.append( it.current() );
+ break; // We already found it ; so lets test the next named filter...
+ }
+ }
+ }
+ }
+
+ TQPtrListIterator<KURIFilterPlugin> it( use_plugins );
+ //kdDebug() << "Using " << use_plugins.count() << " out of the "
+ // << m_lstPlugins.count() << " available plugins" << endl;
+ for (; it.current() && !filtered; ++it)
+ {
+ //kdDebug() << "Using a filter plugin named: " << it.current()->name() << endl;
+ filtered |= it.current()->filterURI( data );
+ }
+ return filtered;
+}
+
+bool KURIFilter::filterURI( KURL& uri, const TQStringList& filters )
+{
+ KURIFilterData data = uri;
+ bool filtered = filterURI( data, filters );
+ if( filtered ) uri = data.uri();
+ return filtered;
+}
+
+bool KURIFilter::filterURI( TQString& uri, const TQStringList& filters )
+{
+ KURIFilterData data = uri;
+ bool filtered = filterURI( data, filters );
+ if( filtered ) uri = data.uri().url();
+ return filtered;
+
+}
+
+KURL KURIFilter::filteredURI( const KURL &uri, const TQStringList& filters )
+{
+ KURIFilterData data = uri;
+ filterURI( data, filters );
+ return data.uri();
+}
+
+TQString KURIFilter::filteredURI( const TQString &uri, const TQStringList& filters )
+{
+ KURIFilterData data = uri;
+ filterURI( data, filters );
+ return data.uri().url();
+}
+
+TQPtrListIterator<KURIFilterPlugin> KURIFilter::pluginsIterator() const
+{
+ return TQPtrListIterator<KURIFilterPlugin>(m_lstPlugins);
+}
+
+TQStringList KURIFilter::pluginNames() const
+{
+ TQStringList list;
+ for(TQPtrListIterator<KURIFilterPlugin> i = pluginsIterator(); *i; ++i)
+ list.append((*i)->name());
+ return list;
+}
+
+void KURIFilter::loadPlugins()
+{
+ KTrader::OfferList offers = KTrader::self()->query( "KURIFilter/Plugin" );
+
+ KTrader::OfferList::ConstIterator it = offers.begin();
+ KTrader::OfferList::ConstIterator end = offers.end();
+
+ for (; it != end; ++it )
+ {
+ KURIFilterPlugin *plugin = KParts::ComponentFactory::createInstanceFromService<KURIFilterPlugin>( *it, 0, (*it)->desktopEntryName().latin1() );
+ if ( plugin )
+ m_lstPlugins.append( plugin );
+ }
+
+ // NOTE: Plugin priority is now determined by
+ // the entry in the .desktop files...
+ // TODO: Config dialog to differentiate "system"
+ // plugins from "user-defined" ones...
+ // m_lstPlugins.sort();
+}
+
+void KURIFilterPlugin::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+#include "kurifilter.moc"
diff --git a/tdeio/tdeio/kurifilter.h b/tdeio/tdeio/kurifilter.h
new file mode 100644
index 000000000..fd7cb9b3c
--- /dev/null
+++ b/tdeio/tdeio/kurifilter.h
@@ -0,0 +1,667 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2000-2001,2003 Dawit Alemayehu <adawit at kde.org>
+ *
+ * Original author
+ * Copyright (C) 2000 Yves Arrouye <yves@realnames.com>
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#ifndef __kurifilter_h__
+#define __kurifilter_h__
+
+#include <tqptrlist.h>
+#include <tqobject.h>
+#include <tqstringlist.h>
+#include <tqpixmap.h>
+
+#include <kurl.h>
+
+#ifdef Q_OS_WIN
+#undef ERROR
+#endif
+
+class KURIFilterPrivate;
+class KURIFilterDataPrivate;
+
+class TDECModule;
+
+/**
+* A basic message object used for exchanging filtering
+* information between the filter plugins and the application
+* requesting the filtering service.
+*
+* Use this object if you require a more detailed information
+* about the URI you want to filter. Any application can create
+* an instance of this class and send it to KURIFilter to
+* have the plugins fill out all possible information about the
+* URI.
+*
+* \b Example
+*
+* \code
+* TQString text = "kde.org";
+* KURIFilterData d = text;
+* bool filtered = KURIFilter::self()->filter( d );
+* cout << "URL: " << text.latin1() << endl
+* << "Filtered URL: " << d.uri().url().latin1() << endl
+* << "URI Type: " << d.uriType() << endl
+* << "Was Filtered: " << filtered << endl;
+* \endcode
+*
+* The above code should yield the following output:
+* \code
+* URI: kde.org
+* Filtered URI: http://kde.org
+* URI Type: 0 <== means NET_PROTOCOL
+* Was Filtered: 1 <== means the URL was successfully filtered
+* \endcode
+*
+* @short A message object for exchanging filtering URI info.
+* @author Dawit Alemayehu <adawit at kde.org>
+*/
+
+class TDEIO_EXPORT KURIFilterData
+{
+friend class KURIFilterPlugin;
+
+public:
+ /**
+ * Describes the type of the URI that was filtered.
+ * Here is a brief description of the types:
+ *
+ * @li NET_PROTOCOL - Any network protocol: http, ftp, nttp, pop3, etc...
+ * @li LOCAL_FILE - A local file whose executable flag is not set
+ * @li LOCAL_DIR - A local directory
+ * @li EXECUTABLE - A local file whose executable flag is set
+ * @li HELP - A man or info page
+ * @li SHELL - A shell executable (ex: echo "Test..." >> ~/testfile)
+ * @li BLOCKED - A URI that should be blocked/filtered (ex: ad filtering)
+ * @li ERROR - An incorrect URI (ex: "~johndoe" when user johndoe
+ * does not exist in that system )
+ * @li UNKNOWN - A URI that is not identified. Default value when
+ * a KURIFilterData is first created.
+ */
+ enum URITypes { NET_PROTOCOL=0, LOCAL_FILE, LOCAL_DIR, EXECUTABLE, HELP, SHELL, BLOCKED, ERROR, UNKNOWN };
+
+ /**
+ * Default constructor.
+ *
+ * Creates a URIFilterData object.
+ */
+ KURIFilterData() { init(); }
+
+ /**
+ * Creates a URIFilterData object from the given URL.
+ *
+ * @param url is the URL to be filtered.
+ */
+ KURIFilterData( const KURL& url ) { init( url); }
+
+ /**
+ * Creates a URIFilterData object from the given string.
+ *
+ * @param url is the string to be filtered.
+ */
+ KURIFilterData( const TQString& url ) { init( url ); }
+
+ /**
+ * Copy constructor.
+ *
+ * Creates a URIFilterData object from another
+ * URI filter data object.
+ *
+ * @param data the uri filter data to be copied.
+ */
+ KURIFilterData( const KURIFilterData& data);
+
+ /**
+ * Destructor.
+ */
+ ~KURIFilterData();
+
+ /**
+ * This method has been deprecated and will always return
+ * true. You should instead use the result from the
+ * KURIFilter::filterURI() calls.
+ *
+ * @deprecated
+ */
+ KDE_DEPRECATED bool hasBeenFiltered() const { return true; }
+
+ /**
+ * Returns the filtered or the original URL.
+ *
+ * This function returns the filtered url if one
+ * of the plugins successfully filtered the original
+ * URL. Otherwise, it returns the original URL.
+ * See hasBeenFiltered() and
+ *
+ * @return the filtered or original url.
+ */
+ KURL uri() const { return m_pURI; }
+
+ /**
+ * Returns an error message.
+ *
+ * This functions returns the error message set
+ * by the plugin whenever the uri type is set to
+ * KURIFilterData::ERROR. Otherwise, it returns
+ * a TQString::null.
+ *
+ * @return the error message or a NULL when there is none.
+ */
+ TQString errorMsg() const { return m_strErrMsg; }
+
+ /**
+ * Returns the URI type.
+ *
+ * This method always returns KURIFilterData::UNKNOWN
+ * if the given URL was not filtered.
+ * @return the type of the URI
+ */
+ URITypes uriType() const { return m_iType; }
+
+ /**
+ * Sets the URL to be filtered.
+ *
+ * Use this function to set the string to be
+ * filtered when you construct an empty filter
+ * object.
+ *
+ * @param url the string to be filtered.
+ */
+ void setData( const TQString& url ) { reinit( url ); }
+
+ /**
+ * Same as above except the argument is a URL.
+ *
+ * Use this function to set the string to be
+ * filtered when you construct an empty filter
+ * object.
+ *
+ * @param url the URL to be filtered.
+ */
+ void setData( const KURL& url ) { reinit( url ); }
+
+ /**
+ * Sets the absolute path to be used whenever the supplied
+ * data is a relative local URL.
+ *
+ * NOTE: This function should only be used for local resources,
+ * i.e. the "file:/" protocol. It is useful for specifying the
+ * absolute path in cases where the actual URL might be relative.
+ * meta object. If deriving the path from a KURL, make sure you
+ * set the argument for this function to the result of calling
+ * path () instead of url ().
+ *
+ * @param abs_path the abolute path to the local resource.
+ * @return true if absolute path is successfully set. Otherwise, false.
+ */
+ bool setAbsolutePath( const TQString& abs_path );
+
+ /**
+ * Returns the absolute path if one has already been set.
+ * @return the absolute path, or TQString::null
+ * @see hasAbsolutePath()
+ */
+ TQString absolutePath() const;
+
+ /**
+ * Checks whether the supplied data had an absolute path.
+ * @return true if the supplied data has an absolute path
+ * @see absolutePath()
+ */
+ bool hasAbsolutePath() const;
+
+ /**
+ * Returns the command line options and arguments for a
+ * local resource when present.
+ *
+ * @return options and arguments when present, otherwise TQString::null
+ */
+ TQString argsAndOptions() const;
+
+ /**
+ * Checks whether the current data is a local resource with
+ * command line options and arguments.
+ * @return true if the current data has command line options and arguments
+ */
+ bool hasArgsAndOptions() const;
+
+ /**
+ * Returns the name of the icon that matches
+ * the current filtered URL.
+ *
+ * NOTE that this function will return a NULL
+ * string by default and when no associated icon
+ * is found.
+ *
+ * @return the name of the icon associated with the resource,
+ * or TQString::null if not found
+ */
+ TQString iconName();
+
+ /**
+ * Returns the current custom icon
+ * The results are valid iff iconName() has
+ * returned TQString::null
+ *
+ * @return a pixmap with the current custom icon,
+ * or a null pixmap if no icon is available
+ */
+ TQPixmap customIconPixmap();
+
+ /**
+ * Check whether the provided uri is executable or not.
+ *
+ * Setting this to false ensures that typing the name of
+ * an executable does not start that application. This is
+ * useful in the location bar of a browser. The default
+ * value is true.
+ *
+ * @since 3.2
+ */
+ void setCheckForExecutables (bool check);
+
+ /**
+ * @return true if the filters should attempt to check whether the
+ * supplied uri is an executable. False otherwise.
+ *
+ * @since 3.2
+ */
+ bool checkForExecutables() const { return m_bCheckForExecutables; }
+
+ /**
+ * @return the string as typed by the user, before any URL processing is done
+ * @since 3.2
+ */
+ TQString typedString() const;
+
+ /**
+ * Overloaded assigenment operator.
+ *
+ * This function allows you to easily assign a KURL
+ * to a KURIFilterData object.
+ *
+ * @return an instance of a KURIFilterData object.
+ */
+ KURIFilterData& operator=( const KURL& url ) { reinit( url ); return *this; }
+
+ /**
+ * Overloaded assigenment operator.
+ *
+ * This function allows you to easily assign a QString
+ * to a KURIFilterData object.
+ *
+ * @return an instance of a KURIFilterData object.
+ */
+ KURIFilterData& operator=( const TQString& url ) { reinit( url ); return *this; }
+
+protected:
+
+ /**
+ * Initializes the KURIFilterData on construction.
+ * @param url the URL to initialize the object with
+ */
+ void init( const KURL& url);
+
+ /**
+ * Initializes the KURIFilterData on construction.
+ * @param url the URL to initialize the object with
+ */
+ void init( const TQString& url = TQString::null );
+
+private:
+
+ // BC hack to avoid leaking KURIFilterDataPrivate objects.
+ // setData() and operator= used to call init() without deleting `d'
+ void reinit(const KURL& url);
+ void reinit(const TQString& url = TQString::null);
+
+ bool m_bCheckForExecutables;
+ bool m_bChanged;
+
+ TQString m_strErrMsg;
+ TQString m_strIconName;
+
+ KURL m_pURI;
+ URITypes m_iType;
+ KURIFilterDataPrivate *d;
+
+ TQPixmap m_customIconPixmap;
+};
+
+
+/**
+ * Base class for URI filter plugins.
+ *
+ * This class applies a single filter to a URI. All plugins designed
+ * to provide URI filtering service should inherit from this abstract
+ * class and provide a concrete implementation.
+ *
+ * All inheriting classes need to implement the pure virtual function
+ * filterURI.
+ *
+ * @short Abstract class for URI filter plugins.
+ */
+class TDEIO_EXPORT KURIFilterPlugin : public TQObject
+{
+ Q_OBJECT
+
+
+public:
+
+ /**
+ * Constructs a filter plugin with a given name and
+ * priority.
+ *
+ * @param parent the parent object, or 0 for no parent
+ * @param name the name of the plugin, or 0 for no name
+ * @param pri the priority of the plugin.
+ */
+ KURIFilterPlugin( TQObject *parent = 0, const char *name = 0, double pri = 1.0 );
+
+ /**
+ * Returns the filter's name.
+ *
+ * @return A string naming the filter.
+ */
+ virtual TQString name() const { return m_strName; }
+
+ /**
+ * Returns the filter's priority.
+ *
+ * Each filter has an assigned priority, a float from 0 to 1. Filters
+ * with the lowest priority are first given a chance to filter a URI.
+ *
+ * @return The priority of the filter.
+ */
+ virtual double priority() const { return m_dblPriority; }
+
+ /**
+ * Filters a URI.
+ *
+ * @param data the URI data to be filtered.
+ * @return A boolean indicating whether the URI has been changed.
+ */
+ virtual bool filterURI( KURIFilterData& data ) const = 0;
+
+ /**
+ * Creates a configuration module for the filter.
+ *
+ * It is the responsibility of the caller to delete the module
+ * once it is not needed anymore.
+ *
+ * @return A configuration module, 0 if the filter isn't configurable.
+ */
+ virtual TDECModule *configModule( TQWidget*, const char* ) const { return 0; }
+
+ /**
+ * Returns the name of the configuration module for the filter.
+ *
+ * @return the name of a configuration module or TQString::null if none.
+ */
+ virtual TQString configName() const { return name(); }
+
+protected:
+
+ /**
+ * Sets the the URL in @p data to @p uri.
+ */
+ void setFilteredURI ( KURIFilterData& data, const KURL& uri ) const;
+
+ /**
+ * Sets the error message in @p data to @p errormsg.
+ */
+ void setErrorMsg ( KURIFilterData& data, const TQString& errmsg ) const {
+ data.m_strErrMsg = errmsg;
+ }
+
+ /**
+ * Sets the URI type in @p data to @p type.
+ */
+ void setURIType ( KURIFilterData& data, KURIFilterData::URITypes type) const {
+ data.m_iType = type;
+ data.m_bChanged = true;
+ }
+
+ /**
+ * Sets the arguments and options string in @p data
+ * to @p args if any were found during filterting.
+ */
+ void setArguments( KURIFilterData& data, const TQString& args ) const;
+
+ TQString m_strName;
+ double m_dblPriority;
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KURIFilterPluginPrivate *d;
+};
+
+
+/**
+ * A list of filter plugins.
+ */
+class TDEIO_EXPORT KURIFilterPluginList : public TQPtrList<KURIFilterPlugin>
+{
+public:
+ virtual int compareItems(Item a, Item b)
+ {
+ double diff = ((KURIFilterPlugin *) a)->priority() - ((KURIFilterPlugin *) b)->priority();
+ return diff < 0 ? -1 : (diff > 0 ? 1 : 0);
+ }
+
+private:
+ KURIFilterPrivate *d;
+
+};
+
+/**
+ * Manages the filtering of URIs.
+ *
+ * The intention of this plugin class is to allow people to extend the
+ * functionality of KURL without modifying it directly. This way KURL will
+ * remain a generic parser capable of parsing any generic URL that adheres
+ * to specifications.
+ *
+ * The KURIFilter class applies a number of filters to a URI and returns the
+ * filtered version whenever possible. The filters are implemented using
+ * plugins to provide easy extensibility of the filtering mechanism. New
+ * filters can be added in the future by simply inheriting from
+ * KURIFilterPlugin and implementing the KURIFilterPlugin::filterURI
+ * method.
+ *
+ * Use of this plugin-manager class is straight forward. Since it is a
+ * singleton object, all you have to do is obtain an instance by doing
+ * @p KURIFilter::self() and use any of the public member functions to
+ * preform the filtering.
+ *
+ * \b Example
+ *
+ * To simply filter a given string:
+ *
+ * \code
+ * bool filtered = KURIFilter::self()->filterURI( "kde.org" );
+ * \endcode
+ *
+ * You can alternatively use a KURL:
+ *
+ * \code
+ * KURL url = "kde.org";
+ * bool filtered = KURIFilter::self()->filterURI( url );
+ * \endcode
+ *
+ * If you have a constant string or a constant URL, simply invoke the
+ * corresponding function to obtain the filtered string or URL instead
+ * of a boolean flag:
+ *
+ * \code
+ * TQString u = KURIFilter::self()->filteredURI( "kde.org" );
+ * \endcode
+ *
+ * You can also restrict the filter(s) to be used by supplying
+ * the name of the filter(s) to use. By defualt all available
+ * filters will be used. To use specific filters, add the names
+ * of the filters you want to use to a TQStringList and invoke
+ * the appropriate filtering function. The examples below show
+ * the use of specific filters. The first one uses a single
+ * filter called kshorturifilter while the second example uses
+ * multiple filters:
+ *
+ * \code
+ * TQString text = "kde.org";
+ * bool filtered = KURIFilter::self()->filterURI( text, "kshorturifilter" );
+ * \endcode
+ *
+ * \code
+ * TQStringList list;
+ * list << "kshorturifilter" << "localdomainfilter";
+ * bool filtered = KURIFilter::self()->filterURI( text, list );
+ * \endcode
+ *
+ * KURIFilter also allows richer data exchange through a simple
+ * meta-object called @p KURIFilterData. Using this meta-object
+ * you can find out more information about the URL you want to
+ * filter. See KURIFilterData for examples and details.
+ *
+ * @short Filters a given URL into its proper format whenever possible.
+ */
+
+class TDEIO_EXPORT KURIFilter
+{
+public:
+ /**
+ * Destructor
+ */
+ ~KURIFilter ();
+
+ /**
+ * Returns an instance of KURIFilter.
+ */
+ static KURIFilter* self();
+
+ /**
+ * Filters the URI given by the object URIFilterData.
+ *
+ * The given URL is filtered based on the specified list of filters.
+ * If the list is empty all available filters would be used.
+ *
+ * @param data object that contains the URI to be filtered.
+ * @param filters specify the list of filters to be used.
+ *
+ * @return a boolean indicating whether the URI has been changed
+ */
+ bool filterURI( KURIFilterData& data, const TQStringList& filters = TQStringList() );
+
+ /**
+ * Filters the URI given by the URL.
+ *
+ * The given URL is filtered based on the specified list of filters.
+ * If the list is empty all available filters would be used.
+ *
+ * @param uri the URI to filter.
+ * @param filters specify the list of filters to be used.
+ *
+ * @return a boolean indicating whether the URI has been changed
+ */
+ bool filterURI( KURL &uri, const TQStringList& filters = TQStringList() );
+
+ /**
+ * Filters a string representing a URI.
+ *
+ * The given URL is filtered based on the specified list of filters.
+ * If the list is empty all available filters would be used.
+ *
+ * @param uri The URI to filter.
+ * @param filters specify the list of filters to be used.
+ *
+ * @return a boolean indicating whether the URI has been changed
+ */
+ bool filterURI( TQString &uri, const TQStringList& filters = TQStringList() );
+
+ /**
+ * Returns the filtered URI.
+ *
+ * The given URL is filtered based on the specified list of filters.
+ * If the list is empty all available filters would be used.
+ *
+ * @param uri The URI to filter.
+ * @param filters specify the list of filters to be used.
+ *
+ * @return the filtered URI or null if it cannot be filtered
+ */
+ KURL filteredURI( const KURL &uri, const TQStringList& filters = TQStringList() );
+
+ /**
+ * Return a filtered string representation of a URI.
+ *
+ * The given URL is filtered based on the specified list of filters.
+ * If the list is empty all available filters would be used.
+ *
+ * @param uri the URI to filter.
+ * @param filters specify the list of filters to be used.
+ *
+ * @return the filtered URI or null if it cannot be filtered
+ */
+ TQString filteredURI( const TQString &uri, const TQStringList& filters = TQStringList() );
+
+ /**
+ * Return an iterator to iterate over all loaded
+ * plugins.
+ *
+ * @return a plugin iterator.
+ */
+ TQPtrListIterator<KURIFilterPlugin> pluginsIterator() const;
+
+ /**
+ * Return a list of the names of all loaded plugins.
+ *
+ * @return a TQStringList of plugin names
+ * @since 3.1
+ */
+ TQStringList pluginNames() const;
+
+protected:
+
+ /**
+ * A protected constructor.
+ *
+ * This constructor creates a KURIFilter and
+ * initializes all plugins it can find by invoking
+ * loadPlugins.
+ */
+ KURIFilter();
+
+ /**
+ * Loads all allowed plugins.
+ *
+ * This function loads all filters that have not
+ * been disbled.
+ */
+ void loadPlugins();
+
+private:
+ static KURIFilter *s_self;
+ KURIFilterPluginList m_lstPlugins;
+ KURIFilterPrivate *d;
+};
+
+#endif
diff --git a/tdeio/tdeio/kurlcompletion.cpp b/tdeio/tdeio/kurlcompletion.cpp
new file mode 100644
index 000000000..22bbe147a
--- /dev/null
+++ b/tdeio/tdeio/kurlcompletion.cpp
@@ -0,0 +1,1604 @@
+/* -*- indent-tabs-mode: t; tab-width: 4; c-basic-offset:4 -*-
+
+ This file is part of the KDE libraries
+ Copyright (C) 2000 David Smith <dsmith@algonet.se>
+ Copyright (C) 2004 Scott Wheeler <wheeler@kde.org>
+
+ This class was inspired by a previous KURLCompletion by
+ Henner Zeller <zeller@think.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <config.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <limits.h>
+
+#include <tqstring.h>
+#include <tqstringlist.h>
+#include <tqvaluelist.h>
+#include <tqregexp.h>
+#include <tqtimer.h>
+#include <tqdir.h>
+#include <tqfile.h>
+#include <tqtextstream.h>
+#include <tqdeepcopy.h>
+#include <tqthread.h>
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kcompletion.h>
+#include <kurl.h>
+#include <tdeio/jobclasses.h>
+#include <tdeio/job.h>
+#include <kprotocolinfo.h>
+#include <tdeconfig.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kde_file.h>
+
+#include <sys/types.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <time.h>
+#include <sys/param.h>
+
+#include "kurlcompletion.h"
+
+static bool expandTilde(TQString &);
+static bool expandEnv(TQString &);
+
+static TQString unescape(const TQString &text);
+
+// Permission mask for files that are executable by
+// user, group or other
+#define MODE_EXE (S_IXUSR | S_IXGRP | S_IXOTH)
+
+// Constants for types of completion
+enum ComplType {CTNone=0, CTEnv, CTUser, CTMan, CTExe, CTFile, CTUrl, CTInfo};
+
+class CompletionThread;
+
+/**
+ * A custom event type that is used to return a list of completion
+ * matches from an asyncrynous lookup.
+ */
+
+class CompletionMatchEvent : public TQCustomEvent
+{
+public:
+ CompletionMatchEvent( CompletionThread *thread ) :
+ TQCustomEvent( uniqueType() ),
+ m_completionThread( thread )
+ {}
+
+ CompletionThread *completionThread() const { return m_completionThread; }
+ static int uniqueType() { return User + 61080; }
+
+private:
+ CompletionThread *m_completionThread;
+};
+
+class CompletionThread : public TQThread
+{
+protected:
+ CompletionThread( KURLCompletion *receiver ) :
+ TQThread(),
+ m_receiver( receiver ),
+ m_terminationRequested( false )
+ {}
+
+public:
+ void requestTermination() { m_terminationRequested = true; }
+ TQDeepCopy<TQStringList> matches() const { return m_matches; }
+
+protected:
+ void addMatch( const TQString &match ) { m_matches.append( match ); }
+ bool terminationRequested() const { return m_terminationRequested; }
+ void done()
+ {
+ if ( !m_terminationRequested )
+ kapp->postEvent( m_receiver, new CompletionMatchEvent( this ) );
+ else
+ delete this;
+ }
+
+private:
+ KURLCompletion *m_receiver;
+ TQStringList m_matches;
+ bool m_terminationRequested;
+};
+
+/**
+ * A simple thread that fetches a list of tilde-completions and returns this
+ * to the caller via a CompletionMatchEvent.
+ */
+
+class UserListThread : public CompletionThread
+{
+public:
+ UserListThread( KURLCompletion *receiver ) :
+ CompletionThread( receiver )
+ {}
+
+protected:
+ virtual void run()
+ {
+ static const TQChar tilde = '~';
+
+ struct passwd *pw;
+ while ( ( pw = ::getpwent() ) && !terminationRequested() )
+ addMatch( tilde + TQString::fromLocal8Bit( pw->pw_name ) );
+
+ ::endpwent();
+
+ addMatch( tilde );
+
+ done();
+ }
+};
+
+class DirectoryListThread : public CompletionThread
+{
+public:
+ DirectoryListThread( KURLCompletion *receiver,
+ const TQStringList &dirList,
+ const TQString &filter,
+ bool onlyExe,
+ bool onlyDir,
+ bool noHidden,
+ bool appendSlashToDir ) :
+ CompletionThread( receiver ),
+ m_dirList( TQDeepCopy<TQStringList>( dirList ) ),
+ m_filter( TQDeepCopy<TQString>( filter ) ),
+ m_onlyExe( onlyExe ),
+ m_onlyDir( onlyDir ),
+ m_noHidden( noHidden ),
+ m_appendSlashToDir( appendSlashToDir )
+ {}
+
+ virtual void run();
+
+private:
+ TQStringList m_dirList;
+ TQString m_filter;
+ bool m_onlyExe;
+ bool m_onlyDir;
+ bool m_noHidden;
+ bool m_appendSlashToDir;
+};
+
+void DirectoryListThread::run()
+{
+ // Thread safety notes:
+ //
+ // There very possibly may be thread safety issues here, but I've done a check
+ // of all of the things that would seem to be problematic. Here are a few
+ // things that I have checked to be safe here (some used indirectly):
+ //
+ // TQDir::currentDirPath(), TQDir::setCurrent(), TQFile::decodeName(), TQFile::encodeName()
+ // TQString::fromLocal8Bit(), TQString::local8Bit(), TQTextCodec::codecForLocale()
+ //
+ // Also see (for POSIX functions):
+ // http://www.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_09.html
+
+ DIR *dir = 0;
+
+ for ( TQStringList::ConstIterator it = m_dirList.begin();
+ it != m_dirList.end() && !terminationRequested();
+ ++it )
+ {
+ // Open the next directory
+
+ if ( !dir ) {
+ dir = ::opendir( TQFile::encodeName( *it ) );
+ if ( ! dir ) {
+ kdDebug() << "Failed to open dir: " << *it << endl;
+ done();
+ return;
+ }
+ }
+
+ // A trick from KIO that helps performance by a little bit:
+ // chdir to the directroy so we won't have to deal with full paths
+ // with stat()
+
+ TQString path = TQDir::currentDirPath();
+ TQDir::setCurrent( *it );
+
+ // Loop through all directory entries
+ // Solaris and IRIX dirent structures do not allocate space for d_name. On
+ // systems that do (HP-UX, Linux, Tru64 UNIX), we overallocate space but
+ // that's ok.
+#ifndef HAVE_READDIR_R
+ struct dirent *dirEntry = 0;
+ while ( !terminationRequested() &&
+ (dirEntry = ::readdir( dir)))
+#else
+#if !defined(MAXPATHLEN) && defined(__GNU__)
+#define MAXPATHLEN UCHAR_MAX
+#endif
+ struct dirent *dirPosition = (struct dirent *) malloc( sizeof( struct dirent ) + MAXPATHLEN + 1 );
+ struct dirent *dirEntry = 0;
+ while ( !terminationRequested() &&
+ ::readdir_r( dir, dirPosition, &dirEntry ) == 0 && dirEntry )
+#endif
+
+ {
+ // Skip hidden files if m_noHidden is true
+
+ if ( dirEntry->d_name[0] == '.' && m_noHidden )
+ continue;
+
+ // Skip "."
+
+ if ( dirEntry->d_name[0] == '.' && dirEntry->d_name[1] == '\0' )
+ continue;
+
+ // Skip ".."
+
+ if ( dirEntry->d_name[0] == '.' && dirEntry->d_name[1] == '.' && dirEntry->d_name[2] == '\0' )
+ continue;
+
+ TQString file = TQFile::decodeName( dirEntry->d_name );
+
+ if ( m_filter.isEmpty() || file.startsWith( m_filter ) ) {
+
+ if ( m_onlyExe || m_onlyDir || m_appendSlashToDir ) {
+ KDE_struct_stat sbuff;
+
+ if ( KDE_stat( dirEntry->d_name, &sbuff ) == 0 ) {
+
+ // Verify executable
+
+ if ( m_onlyExe && ( sbuff.st_mode & MODE_EXE ) == 0 )
+ continue;
+
+ // Verify directory
+
+ if ( m_onlyDir && !S_ISDIR( sbuff.st_mode ) )
+ continue;
+
+ // Add '/' to directories
+
+ if ( m_appendSlashToDir && S_ISDIR( sbuff.st_mode ) )
+ file.append( '/' );
+
+ }
+ else {
+ kdDebug() << "Could not stat file " << file << endl;
+ continue;
+ }
+ }
+
+ addMatch( file );
+ }
+ }
+
+ // chdir to the original directory
+
+ TQDir::setCurrent( path );
+
+ ::closedir( dir );
+ dir = 0;
+#ifdef HAVE_READDIR_R
+ free( dirPosition );
+#endif
+ }
+
+ done();
+}
+
+///////////////////////////////////////////////////////
+///////////////////////////////////////////////////////
+// MyURL - wrapper for KURL with some different functionality
+//
+
+class KURLCompletion::MyURL
+{
+public:
+ MyURL(const TQString &url, const TQString &cwd);
+ MyURL(const MyURL &url);
+ ~MyURL();
+
+ KURL *kurl() const { return m_kurl; }
+
+ TQString protocol() const { return m_kurl->protocol(); }
+ // The directory with a trailing '/'
+ TQString dir() const { return m_kurl->directory(false, false); }
+ TQString file() const { return m_kurl->fileName(false); }
+
+ // The initial, unparsed, url, as a string.
+ TQString url() const { return m_url; }
+
+ // Is the initial string a URL, or just a path (whether absolute or relative)
+ bool isURL() const { return m_isURL; }
+
+ void filter( bool replace_user_dir, bool replace_env );
+
+private:
+ void init(const TQString &url, const TQString &cwd);
+
+ KURL *m_kurl;
+ TQString m_url;
+ bool m_isURL;
+};
+
+KURLCompletion::MyURL::MyURL(const TQString &url, const TQString &cwd)
+{
+ init(url, cwd);
+}
+
+KURLCompletion::MyURL::MyURL(const MyURL &url)
+{
+ m_kurl = new KURL( *(url.m_kurl) );
+ m_url = url.m_url;
+ m_isURL = url.m_isURL;
+}
+
+void KURLCompletion::MyURL::init(const TQString &url, const TQString &cwd)
+{
+ // Save the original text
+ m_url = url;
+
+ // Non-const copy
+ TQString url_copy = url;
+
+ // Special shortcuts for "man:" and "info:"
+ if ( url_copy[0] == '#' ) {
+ if ( url_copy[1] == '#' )
+ url_copy.replace( 0, 2, TQString("info:") );
+ else
+ url_copy.replace( 0, 1, TQString("man:") );
+ }
+
+ // Look for a protocol in 'url'
+ TQRegExp protocol_regex = TQRegExp( "^[^/\\s\\\\]*:" );
+
+ // Assume "file:" or whatever is given by 'cwd' if there is
+ // no protocol. (KURL does this only for absoute paths)
+ if ( protocol_regex.search( url_copy ) == 0 )
+ {
+ m_kurl = new KURL( url_copy );
+ m_isURL = true;
+ }
+ else // relative path or ~ or $something
+ {
+ m_isURL = false;
+ if ( cwd.isEmpty() )
+ {
+ m_kurl = new KURL();
+ if ( !TQDir::isRelativePath(url_copy) || url_copy[0] == '$' || url_copy[0] == '~' )
+ m_kurl->setPath( url_copy );
+ else
+ *m_kurl = url_copy;
+ }
+ else
+ {
+ KURL base = KURL::fromPathOrURL( cwd );
+ base.adjustPath(+1);
+
+ if ( !TQDir::isRelativePath(url_copy) || url_copy[0] == '~' || url_copy[0] == '$' )
+ {
+ m_kurl = new KURL();
+ m_kurl->setPath( url_copy );
+ }
+ else // relative path
+ {
+ //m_kurl = new KURL( base, url_copy );
+ m_kurl = new KURL( base );
+ m_kurl->addPath( url_copy );
+ }
+ }
+ }
+}
+
+KURLCompletion::MyURL::~MyURL()
+{
+ delete m_kurl;
+}
+
+void KURLCompletion::MyURL::filter( bool replace_user_dir, bool replace_env )
+{
+ TQString d = dir() + file();
+ if ( replace_user_dir ) expandTilde( d );
+ if ( replace_env ) expandEnv( d );
+ m_kurl->setPath( d );
+}
+
+///////////////////////////////////////////////////////
+///////////////////////////////////////////////////////
+// KURLCompletionPrivate
+//
+class KURLCompletionPrivate
+{
+public:
+ KURLCompletionPrivate() : url_auto_completion(true),
+ userListThread(0),
+ dirListThread(0) {}
+ ~KURLCompletionPrivate();
+
+ TQValueList<KURL*> list_urls;
+
+ bool onlyLocalProto;
+
+ // urlCompletion() in Auto/Popup mode?
+ bool url_auto_completion;
+
+ // Append '/' to directories in Popup mode?
+ // Doing that stat's all files and is slower
+ bool popup_append_slash;
+
+ // Keep track of currently listed files to avoid reading them again
+ TQString last_path_listed;
+ TQString last_file_listed;
+ TQString last_prepend;
+ int last_compl_type;
+ int last_no_hidden;
+
+ TQString cwd; // "current directory" = base dir for completion
+
+ KURLCompletion::Mode mode; // ExeCompletion, FileCompletion, DirCompletion
+ bool replace_env;
+ bool replace_home;
+ bool complete_url; // if true completing a URL (i.e. 'prepend' is a URL), otherwise a path
+
+ TDEIO::ListJob *list_job; // kio job to list directories
+
+ TQString prepend; // text to prepend to listed items
+ TQString compl_text; // text to pass on to KCompletion
+
+ // Filters for files read with kio
+ bool list_urls_only_exe; // true = only list executables
+ bool list_urls_no_hidden;
+ TQString list_urls_filter; // filter for listed files
+
+ CompletionThread *userListThread;
+ CompletionThread *dirListThread;
+};
+
+KURLCompletionPrivate::~KURLCompletionPrivate()
+{
+ if ( userListThread )
+ userListThread->requestTermination();
+ if ( dirListThread )
+ dirListThread->requestTermination();
+}
+
+///////////////////////////////////////////////////////
+///////////////////////////////////////////////////////
+// KURLCompletion
+//
+
+KURLCompletion::KURLCompletion() : KCompletion()
+{
+ init();
+}
+
+
+KURLCompletion::KURLCompletion( Mode mode ) : KCompletion()
+{
+ init();
+ setMode ( mode );
+}
+
+KURLCompletion::~KURLCompletion()
+{
+ stop();
+ delete d;
+}
+
+
+void KURLCompletion::init()
+{
+ d = new KURLCompletionPrivate;
+
+ d->cwd = TQDir::homeDirPath();
+
+ d->replace_home = true;
+ d->replace_env = true;
+ d->last_no_hidden = false;
+ d->last_compl_type = 0;
+ d->list_job = 0L;
+ d->mode = KURLCompletion::FileCompletion;
+
+ // Read settings
+ TDEConfig *c = TDEGlobal::config();
+ TDEConfigGroupSaver cgs( c, "URLCompletion" );
+
+ d->url_auto_completion = c->readBoolEntry("alwaysAutoComplete", true);
+ d->popup_append_slash = c->readBoolEntry("popupAppendSlash", true);
+ d->onlyLocalProto = c->readBoolEntry("LocalProtocolsOnly", false);
+}
+
+void KURLCompletion::setDir(const TQString &dir)
+{
+ d->cwd = dir;
+}
+
+TQString KURLCompletion::dir() const
+{
+ return d->cwd;
+}
+
+KURLCompletion::Mode KURLCompletion::mode() const
+{
+ return d->mode;
+}
+
+void KURLCompletion::setMode( Mode mode )
+{
+ d->mode = mode;
+}
+
+bool KURLCompletion::replaceEnv() const
+{
+ return d->replace_env;
+}
+
+void KURLCompletion::setReplaceEnv( bool replace )
+{
+ d->replace_env = replace;
+}
+
+bool KURLCompletion::replaceHome() const
+{
+ return d->replace_home;
+}
+
+void KURLCompletion::setReplaceHome( bool replace )
+{
+ d->replace_home = replace;
+}
+
+/*
+ * makeCompletion()
+ *
+ * Entry point for file name completion
+ */
+TQString KURLCompletion::makeCompletion(const TQString &text)
+{
+ //kdDebug() << "KURLCompletion::makeCompletion: " << text << " d->cwd=" << d->cwd << endl;
+
+ MyURL url(text, d->cwd);
+
+ d->compl_text = text;
+
+ // Set d->prepend to the original URL, with the filename [and ref/query] stripped.
+ // This is what gets prepended to the directory-listing matches.
+ int toRemove = url.file().length() - url.kurl()->query().length();
+ if ( url.kurl()->hasRef() )
+ toRemove += url.kurl()->ref().length() + 1;
+ d->prepend = text.left( text.length() - toRemove );
+ d->complete_url = url.isURL();
+
+ TQString match;
+
+ // Environment variables
+ //
+ if ( d->replace_env && envCompletion( url, &match ) )
+ return match;
+
+ // User directories
+ //
+ if ( d->replace_home && userCompletion( url, &match ) )
+ return match;
+
+ // Replace user directories and variables
+ url.filter( d->replace_home, d->replace_env );
+
+ //kdDebug() << "Filtered: proto=" << url.protocol()
+ // << ", dir=" << url.dir()
+ // << ", file=" << url.file()
+ // << ", kurl url=" << *url.kurl() << endl;
+
+ if ( d->mode == ExeCompletion ) {
+ // Executables
+ //
+ if ( exeCompletion( url, &match ) )
+ return match;
+
+ // KRun can run "man:" and "info:" etc. so why not treat them
+ // as executables...
+
+ if ( urlCompletion( url, &match ) )
+ return match;
+ }
+ else if ( d->mode == SystemExeCompletion ) {
+ // Executables
+ //
+ if ( systemexeCompletion( url, &match ) )
+ return match;
+
+ // KRun can run "man:" and "info:" etc. so why not treat them
+ // as executables...
+
+ if ( urlCompletion( url, &match ) )
+ return match;
+ }
+ else {
+ // Local files, directories
+ //
+ if ( fileCompletion( url, &match ) )
+ return match;
+
+ // All other...
+ //
+ if ( urlCompletion( url, &match ) )
+ return match;
+ }
+
+ setListedURL( CTNone );
+ stop();
+
+ return TQString::null;
+}
+
+/*
+ * finished
+ *
+ * Go on and call KCompletion.
+ * Called when all matches have been added
+ */
+TQString KURLCompletion::finished()
+{
+ if ( d->last_compl_type == CTInfo )
+ return KCompletion::makeCompletion( d->compl_text.lower() );
+ else
+ return KCompletion::makeCompletion( d->compl_text );
+}
+
+/*
+ * isRunning
+ *
+ * Return true if either a KIO job or the DirLister
+ * is running
+ */
+bool KURLCompletion::isRunning() const
+{
+ return d->list_job || (d->dirListThread && !d->dirListThread->finished());
+}
+
+/*
+ * stop
+ *
+ * Stop and delete a running KIO job or the DirLister
+ */
+void KURLCompletion::stop()
+{
+ if ( d->list_job ) {
+ d->list_job->kill();
+ d->list_job = 0L;
+ }
+
+ if ( !d->list_urls.isEmpty() ) {
+ TQValueList<KURL*>::Iterator it = d->list_urls.begin();
+ for ( ; it != d->list_urls.end(); it++ )
+ delete (*it);
+ d->list_urls.clear();
+ }
+
+ if ( d->dirListThread ) {
+ d->dirListThread->requestTermination();
+ d->dirListThread = 0;
+ }
+}
+
+/*
+ * Keep track of the last listed directory
+ */
+void KURLCompletion::setListedURL( int complType,
+ const TQString& dir,
+ const TQString& filter,
+ bool no_hidden )
+{
+ d->last_compl_type = complType;
+ d->last_path_listed = dir;
+ d->last_file_listed = filter;
+ d->last_no_hidden = (int)no_hidden;
+ d->last_prepend = d->prepend;
+}
+
+bool KURLCompletion::isListedURL( int complType,
+ const TQString& dir,
+ const TQString& filter,
+ bool no_hidden )
+{
+ return d->last_compl_type == complType
+ && ( d->last_path_listed == dir
+ || (dir.isEmpty() && d->last_path_listed.isEmpty()) )
+ && ( filter.startsWith(d->last_file_listed)
+ || (filter.isEmpty() && d->last_file_listed.isEmpty()) )
+ && d->last_no_hidden == (int)no_hidden
+ && d->last_prepend == d->prepend; // e.g. relative path vs absolute
+}
+
+/*
+ * isAutoCompletion
+ *
+ * Returns true if completion mode is Auto or Popup
+ */
+bool KURLCompletion::isAutoCompletion()
+{
+ return completionMode() == TDEGlobalSettings::CompletionAuto
+ || completionMode() == TDEGlobalSettings::CompletionPopup
+ || completionMode() == TDEGlobalSettings::CompletionMan
+ || completionMode() == TDEGlobalSettings::CompletionPopupAuto;
+}
+//////////////////////////////////////////////////
+//////////////////////////////////////////////////
+// User directories
+//
+
+bool KURLCompletion::userCompletion(const MyURL &url, TQString *match)
+{
+ if ( url.protocol() != "file"
+ || !url.dir().isEmpty()
+ || url.file().at(0) != '~' )
+ return false;
+
+ if ( !isListedURL( CTUser ) ) {
+ stop();
+ clear();
+
+ if ( !d->userListThread ) {
+ d->userListThread = new UserListThread( this );
+ d->userListThread->start();
+
+ // If the thread finishes quickly make sure that the results
+ // are added to the first matching case.
+
+ d->userListThread->wait( 200 );
+ TQStringList l = d->userListThread->matches();
+ addMatches( l );
+ }
+ }
+ *match = finished();
+ return true;
+}
+
+/////////////////////////////////////////////////////
+/////////////////////////////////////////////////////
+// Environment variables
+//
+
+extern char **environ; // Array of environment variables
+
+bool KURLCompletion::envCompletion(const MyURL &url, TQString *match)
+{
+ if ( url.file().at(0) != '$' )
+ return false;
+
+ if ( !isListedURL( CTEnv ) ) {
+ stop();
+ clear();
+
+ char **env = environ;
+
+ TQString dollar = TQString("$");
+
+ TQStringList l;
+
+ while ( *env ) {
+ TQString s = TQString::fromLocal8Bit( *env );
+
+ int pos = s.find('=');
+
+ if ( pos == -1 )
+ pos = s.length();
+
+ if ( pos > 0 )
+ l.append( dollar + s.left(pos) );
+
+ env++;
+ }
+
+ addMatches( l );
+ }
+
+ setListedURL( CTEnv );
+
+ *match = finished();
+ return true;
+}
+
+//////////////////////////////////////////////////
+//////////////////////////////////////////////////
+// Executables
+//
+
+bool KURLCompletion::exeCompletion(const MyURL &url, TQString *match)
+{
+ if ( url.protocol() != "file" )
+ return false;
+
+ TQString dir = url.dir();
+
+ dir = unescape( dir ); // remove escapes
+
+ // Find directories to search for completions, either
+ //
+ // 1. complete path given in url
+ // 2. current directory (d->cwd)
+ // 3. $PATH
+ // 4. no directory at all
+
+ TQStringList dirList;
+
+ if ( !TQDir::isRelativePath(dir) ) {
+ // complete path in url
+ dirList.append( dir );
+ }
+ else if ( !dir.isEmpty() && !d->cwd.isEmpty() ) {
+ // current directory
+ dirList.append( d->cwd + '/' + dir );
+ }
+ else if ( !url.file().isEmpty() ) {
+ // $PATH
+ dirList = TQStringList::split(KPATH_SEPARATOR,
+ TQString::fromLocal8Bit(::getenv("PATH")));
+
+ TQStringList::Iterator it = dirList.begin();
+
+ for ( ; it != dirList.end(); it++ )
+ (*it).append('/');
+ }
+
+ // No hidden files unless the user types "."
+ bool no_hidden_files = url.file().at(0) != '.';
+
+ // List files if needed
+ //
+ if ( !isListedURL( CTExe, dir, url.file(), no_hidden_files ) )
+ {
+ stop();
+ clear();
+
+ setListedURL( CTExe, dir, url.file(), no_hidden_files );
+
+ *match = listDirectories( dirList, url.file(), true, false, no_hidden_files );
+ }
+ else if ( !isRunning() ) {
+ *match = finished();
+ }
+ else {
+ if ( d->dirListThread )
+ setListedURL( CTExe, dir, url.file(), no_hidden_files );
+ *match = TQString::null;
+ }
+
+ return true;
+}
+
+//////////////////////////////////////////////////
+//////////////////////////////////////////////////
+// System Executables
+//
+
+bool KURLCompletion::systemexeCompletion(const MyURL &url, TQString *match)
+{
+ if ( url.protocol() != "file" )
+ return false;
+
+ TQString dir = url.dir();
+
+ dir = unescape( dir ); // remove escapes
+
+ // Find directories to search for completions, either
+ //
+ // 1. complete path given in url
+ // 2. current directory (d->cwd)
+ // 3. $PATH
+ // 4. no directory at all
+
+ TQStringList dirList;
+
+ if ( !url.file().isEmpty() ) {
+ // $PATH
+ dirList = TQStringList::split(KPATH_SEPARATOR,
+ TQString::fromLocal8Bit(::getenv("PATH")));
+
+ TQStringList::Iterator it = dirList.begin();
+
+ for ( ; it != dirList.end(); it++ )
+ (*it).append('/');
+ }
+
+ // No hidden files unless the user types "."
+ bool no_hidden_files = url.file().at(0) != '.';
+
+ // List files if needed
+ //
+ if ( !isListedURL( CTExe, dir, url.file(), no_hidden_files ) )
+ {
+ stop();
+ clear();
+
+ setListedURL( CTExe, dir, url.file(), no_hidden_files );
+
+ *match = listDirectories( dirList, url.file(), true, false, no_hidden_files );
+ }
+ else if ( !isRunning() ) {
+ *match = finished();
+ }
+ else {
+ if ( d->dirListThread )
+ setListedURL( CTExe, dir, url.file(), no_hidden_files );
+ *match = TQString::null;
+ }
+
+ return true;
+}
+
+//////////////////////////////////////////////////
+//////////////////////////////////////////////////
+// Local files
+//
+
+bool KURLCompletion::fileCompletion(const MyURL &url, TQString *match)
+{
+ if ( url.protocol() != "file" )
+ return false;
+
+ TQString dir = url.dir();
+
+ if (url.url()[0] == '.')
+ {
+ if (url.url().length() == 1)
+ {
+ *match =
+ ( completionMode() == TDEGlobalSettings::CompletionMan )? "." : "..";
+ return true;
+ }
+ if (url.url().length() == 2 && url.url()[1]=='.')
+ {
+ *match="..";
+ return true;
+ }
+ }
+
+ //kdDebug() << "fileCompletion " << url.url() << " dir=" << dir << endl;
+
+ dir = unescape( dir ); // remove escapes
+
+ // Find directories to search for completions, either
+ //
+ // 1. complete path given in url
+ // 2. current directory (d->cwd)
+ // 3. no directory at all
+
+ TQStringList dirList;
+
+ if ( !TQDir::isRelativePath(dir) ) {
+ // complete path in url
+ dirList.append( dir );
+ }
+ else if ( !d->cwd.isEmpty() ) {
+ // current directory
+ dirList.append( d->cwd + '/' + dir );
+ }
+
+ // No hidden files unless the user types "."
+ bool no_hidden_files = ( url.file().at(0) != '.' );
+
+ // List files if needed
+ //
+ if ( !isListedURL( CTFile, dir, "", no_hidden_files ) )
+ {
+ stop();
+ clear();
+
+ setListedURL( CTFile, dir, "", no_hidden_files );
+
+ // Append '/' to directories in Popup mode?
+ bool append_slash = ( d->popup_append_slash
+ && (completionMode() == TDEGlobalSettings::CompletionPopup ||
+ completionMode() == TDEGlobalSettings::CompletionPopupAuto ) );
+
+ bool only_dir = ( d->mode == DirCompletion );
+
+ *match = listDirectories( dirList, "", false, only_dir, no_hidden_files,
+ append_slash );
+ }
+ else if ( !isRunning() ) {
+ *match = finished();
+ }
+ else {
+ *match = TQString::null;
+ }
+
+ return true;
+}
+
+//////////////////////////////////////////////////
+//////////////////////////////////////////////////
+// URLs not handled elsewhere...
+//
+
+bool KURLCompletion::urlCompletion(const MyURL &url, TQString *match)
+{
+ //kdDebug() << "urlCompletion: url = " << *url.kurl() << endl;
+ if (d->onlyLocalProto && KProtocolInfo::protocolClass(url.protocol()) != ":local")
+ return false;
+
+ // Use d->cwd as base url in case url is not absolute
+ KURL url_cwd = KURL::fromPathOrURL( d->cwd );
+
+ // Create an URL with the directory to be listed
+ KURL url_dir( url_cwd, url.kurl()->url() );
+
+ // Don't try url completion if
+ // 1. malformed url
+ // 2. protocol that doesn't have listDir()
+ // 3. there is no directory (e.g. "ftp://ftp.kd" shouldn't do anything)
+ // 4. auto or popup completion mode depending on settings
+
+ bool man_or_info = ( url_dir.protocol() == TQString("man")
+ || url_dir.protocol() == TQString("info") );
+
+ if ( !url_dir.isValid()
+ || !KProtocolInfo::supportsListing( url_dir )
+ || ( !man_or_info
+ && ( url_dir.directory(false,false).isEmpty()
+ || ( isAutoCompletion()
+ && !d->url_auto_completion ) ) ) ) {
+ return false;
+ }
+
+ url_dir.setFileName(""); // not really nesseccary, but clear the filename anyway...
+
+ // Remove escapes
+ TQString dir = url_dir.directory( false, false );
+
+ dir = unescape( dir );
+
+ url_dir.setPath( dir );
+
+ // List files if needed
+ //
+ if ( !isListedURL( CTUrl, url_dir.prettyURL(), url.file() ) )
+ {
+ stop();
+ clear();
+
+ setListedURL( CTUrl, url_dir.prettyURL(), "" );
+
+ TQValueList<KURL*> url_list;
+ url_list.append( new KURL( url_dir ) );
+
+ listURLs( url_list, "", false );
+
+ *match = TQString::null;
+ }
+ else if ( !isRunning() ) {
+ *match = finished();
+ }
+ else {
+ *match = TQString::null;
+ }
+
+ return true;
+}
+
+//////////////////////////////////////////////////
+//////////////////////////////////////////////////
+// Directory and URL listing
+//
+
+/*
+ * addMatches
+ *
+ * Called to add matches to KCompletion
+ */
+void KURLCompletion::addMatches( const TQStringList &matches )
+{
+ TQStringList::ConstIterator it = matches.begin();
+ TQStringList::ConstIterator end = matches.end();
+
+ if ( d->complete_url )
+ for ( ; it != end; it++ )
+ addItem( d->prepend + KURL::encode_string(*it));
+ else
+ for ( ; it != end; it++ )
+ addItem( d->prepend + (*it));
+}
+
+/*
+ * listDirectories
+ *
+ * List files starting with 'filter' in the given directories,
+ * either using DirLister or listURLs()
+ *
+ * In either case, addMatches() is called with the listed
+ * files, and eventually finished() when the listing is done
+ *
+ * Returns the match if available, or TQString::null if
+ * DirLister timed out or using kio
+ */
+TQString KURLCompletion::listDirectories(
+ const TQStringList &dirList,
+ const TQString &filter,
+ bool only_exe,
+ bool only_dir,
+ bool no_hidden,
+ bool append_slash_to_dir)
+{
+ assert( !isRunning() );
+
+ if ( !::getenv("KURLCOMPLETION_LOCAL_KIO") ) {
+
+ //kdDebug() << "Listing (listDirectories): " << dirList << " filter=" << filter << " without KIO" << endl;
+
+ // Don't use KIO
+
+ if ( d->dirListThread )
+ d->dirListThread->requestTermination();
+
+ TQStringList dirs;
+
+ for ( TQStringList::ConstIterator it = dirList.begin();
+ it != dirList.end();
+ ++it )
+ {
+ KURL url;
+ url.setPath(*it);
+ if ( kapp->authorizeURLAction( "list", KURL(), url ) )
+ dirs.append( *it );
+ }
+
+ d->dirListThread = new DirectoryListThread( this, dirs, filter, only_exe, only_dir,
+ no_hidden, append_slash_to_dir );
+ d->dirListThread->start();
+ d->dirListThread->wait( 200 );
+ addMatches( d->dirListThread->matches() );
+
+ return finished();
+ }
+ else {
+
+ // Use KIO
+ //kdDebug() << "Listing (listDirectories): " << dirList << " with KIO" << endl;
+
+ TQValueList<KURL*> url_list;
+
+ TQStringList::ConstIterator it = dirList.begin();
+
+ for ( ; it != dirList.end(); it++ )
+ url_list.append( new KURL(*it) );
+
+ listURLs( url_list, filter, only_exe, no_hidden );
+ // Will call addMatches() and finished()
+
+ return TQString::null;
+ }
+}
+
+/*
+ * listURLs
+ *
+ * Use KIO to list the given urls
+ *
+ * addMatches() is called with the listed files
+ * finished() is called when the listing is done
+ */
+void KURLCompletion::listURLs(
+ const TQValueList<KURL *> &urls,
+ const TQString &filter,
+ bool only_exe,
+ bool no_hidden )
+{
+ assert( d->list_urls.isEmpty() );
+ assert( d->list_job == 0L );
+
+ d->list_urls = urls;
+ d->list_urls_filter = filter;
+ d->list_urls_only_exe = only_exe;
+ d->list_urls_no_hidden = no_hidden;
+
+// kdDebug() << "Listing URLs: " << urls[0]->prettyURL() << ",..." << endl;
+
+ // Start it off by calling slotIOFinished
+ //
+ // This will start a new list job as long as there
+ // are urls in d->list_urls
+ //
+ slotIOFinished(0L);
+}
+
+/*
+ * slotEntries
+ *
+ * Receive files listed by KIO and call addMatches()
+ */
+void KURLCompletion::slotEntries(TDEIO::Job*, const TDEIO::UDSEntryList& entries)
+{
+ TQStringList matches;
+
+ TDEIO::UDSEntryListConstIterator it = entries.begin();
+ TDEIO::UDSEntryListConstIterator end = entries.end();
+
+ TQString filter = d->list_urls_filter;
+
+ int filter_len = filter.length();
+
+ // Iterate over all files
+ //
+ for (; it != end; ++it) {
+ TQString name;
+ TQString url;
+ bool is_exe = false;
+ bool is_dir = false;
+
+ TDEIO::UDSEntry e = *it;
+ TDEIO::UDSEntry::ConstIterator it_2 = e.begin();
+
+ for( ; it_2 != e.end(); it_2++ ) {
+ switch ( (*it_2).m_uds ) {
+ case TDEIO::UDS_NAME:
+ name = (*it_2).m_str;
+ break;
+ case TDEIO::UDS_ACCESS:
+ is_exe = ((*it_2).m_long & MODE_EXE) != 0;
+ break;
+ case TDEIO::UDS_FILE_TYPE:
+ is_dir = ((*it_2).m_long & S_IFDIR) != 0;
+ break;
+ case TDEIO::UDS_URL:
+ url = (*it_2).m_str;
+ break;
+ }
+ }
+
+ if (!url.isEmpty()) {
+ // kdDebug() << "KURLCompletion::slotEntries url: " << url << endl;
+ name = KURL(url).fileName();
+ }
+
+ // kdDebug() << "KURLCompletion::slotEntries name: " << name << endl;
+
+ if ( name[0] == '.' &&
+ ( d->list_urls_no_hidden ||
+ name.length() == 1 ||
+ ( name.length() == 2 && name[1] == '.' ) ) )
+ continue;
+
+ if ( d->mode == DirCompletion && !is_dir )
+ continue;
+
+ if ( filter_len == 0 || name.left(filter_len) == filter ) {
+ if ( is_dir )
+ name.append( '/' );
+
+ if ( is_exe || !d->list_urls_only_exe )
+ matches.append( name );
+ }
+ }
+
+ addMatches( matches );
+}
+
+/*
+ * slotIOFinished
+ *
+ * Called when a KIO job is finished.
+ *
+ * Start a new list job if there are still urls in
+ * d->list_urls, otherwise call finished()
+ */
+void KURLCompletion::slotIOFinished( TDEIO::Job * job )
+{
+// kdDebug() << "slotIOFinished() " << endl;
+
+ assert( job == d->list_job );
+
+ if ( d->list_urls.isEmpty() ) {
+
+ d->list_job = 0L;
+
+ finished(); // will call KCompletion::makeCompletion()
+
+ }
+ else {
+
+ KURL *kurl = d->list_urls.first();
+
+ d->list_urls.remove( kurl );
+
+// kdDebug() << "Start KIO: " << kurl->prettyURL() << endl;
+
+ d->list_job = TDEIO::listDir( *kurl, false );
+ d->list_job->addMetaData("no-auth-prompt", "true");
+
+ assert( d->list_job );
+
+ connect( d->list_job,
+ TQT_SIGNAL(result(TDEIO::Job*)),
+ TQT_SLOT(slotIOFinished(TDEIO::Job*)) );
+
+ connect( d->list_job,
+ TQT_SIGNAL( entries( TDEIO::Job*, const TDEIO::UDSEntryList&)),
+ TQT_SLOT( slotEntries( TDEIO::Job*, const TDEIO::UDSEntryList&)) );
+
+ delete kurl;
+ }
+}
+
+///////////////////////////////////////////////////
+///////////////////////////////////////////////////
+
+/*
+ * postProcessMatch, postProcessMatches
+ *
+ * Called by KCompletion before emitting match() and matches()
+ *
+ * Append '/' to directories for file completion. This is
+ * done here to avoid stat()'ing a lot of files
+ */
+void KURLCompletion::postProcessMatch( TQString *match ) const
+{
+// kdDebug() << "KURLCompletion::postProcess: " << *match << endl;
+
+ if ( !match->isEmpty() ) {
+
+ // Add '/' to directories in file completion mode
+ // unless it has already been done
+ if ( d->last_compl_type == CTFile )
+ adjustMatch( *match );
+ }
+}
+
+void KURLCompletion::adjustMatch( TQString& match ) const
+{
+ if ( match.at( match.length()-1 ) != '/' )
+ {
+ TQString copy;
+
+ if ( match.startsWith( TQString("file:") ) )
+ copy = KURL(match).path();
+ else
+ copy = match;
+
+ expandTilde( copy );
+ expandEnv( copy );
+ if ( TQDir::isRelativePath(copy) )
+ copy.prepend( d->cwd + '/' );
+
+// kdDebug() << "postProcess: stating " << copy << endl;
+
+ KDE_struct_stat sbuff;
+
+ TQCString file = TQFile::encodeName( copy );
+
+ if ( KDE_stat( (const char*)file, &sbuff ) == 0 ) {
+ if ( S_ISDIR ( sbuff.st_mode ) )
+ match.append( '/' );
+ }
+ else {
+ kdDebug() << "Could not stat file " << copy << endl;
+ }
+ }
+}
+
+void KURLCompletion::postProcessMatches( TQStringList * matches ) const
+{
+ if ( !matches->isEmpty() && d->last_compl_type == CTFile ) {
+ TQStringList::Iterator it = matches->begin();
+ for (; it != matches->end(); ++it ) {
+ adjustMatch( (*it) );
+ }
+ }
+}
+
+void KURLCompletion::postProcessMatches( KCompletionMatches * matches ) const
+{
+ if ( !matches->isEmpty() && d->last_compl_type == CTFile ) {
+ KCompletionMatches::Iterator it = matches->begin();
+ for (; it != matches->end(); ++it ) {
+ adjustMatch( (*it).value() );
+ }
+ }
+}
+
+void KURLCompletion::customEvent(TQCustomEvent *e)
+{
+ if ( e->type() == CompletionMatchEvent::uniqueType() ) {
+
+ CompletionMatchEvent *event = static_cast<CompletionMatchEvent *>( e );
+
+ event->completionThread()->wait();
+
+ if ( !isListedURL( CTUser ) ) {
+ stop();
+ clear();
+ addMatches( event->completionThread()->matches() );
+ }
+
+ setListedURL( CTUser );
+
+ if ( d->userListThread == event->completionThread() )
+ d->userListThread = 0;
+
+ if ( d->dirListThread == event->completionThread() )
+ d->dirListThread = 0;
+
+ delete event->completionThread();
+ }
+}
+
+// static
+TQString KURLCompletion::replacedPath( const TQString& text, bool replaceHome, bool replaceEnv )
+{
+ if ( text.isEmpty() )
+ return text;
+
+ MyURL url( text, TQString::null ); // no need to replace something of our current cwd
+ if ( !url.kurl()->isLocalFile() )
+ return text;
+
+ url.filter( replaceHome, replaceEnv );
+ return url.dir() + url.file();
+}
+
+
+TQString KURLCompletion::replacedPath( const TQString& text )
+{
+ return replacedPath( text, d->replace_home, d->replace_env );
+}
+
+/////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////
+// Static functions
+
+/*
+ * expandEnv
+ *
+ * Expand environment variables in text. Escaped '$' are ignored.
+ * Return true if expansion was made.
+ */
+static bool expandEnv( TQString &text )
+{
+ // Find all environment variables beginning with '$'
+ //
+ int pos = 0;
+
+ bool expanded = false;
+
+ while ( (pos = text.find('$', pos)) != -1 ) {
+
+ // Skip escaped '$'
+ //
+ if ( text[pos-1] == '\\' ) {
+ pos++;
+ }
+ // Variable found => expand
+ //
+ else {
+ // Find the end of the variable = next '/' or ' '
+ //
+ int pos2 = text.find( ' ', pos+1 );
+ int pos_tmp = text.find( '/', pos+1 );
+
+ if ( pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2) )
+ pos2 = pos_tmp;
+
+ if ( pos2 == -1 )
+ pos2 = text.length();
+
+ // Replace if the variable is terminated by '/' or ' '
+ // and defined
+ //
+ if ( pos2 >= 0 ) {
+ int len = pos2 - pos;
+ TQString key = text.mid( pos+1, len-1);
+ TQString value =
+ TQString::fromLocal8Bit( ::getenv(key.local8Bit()) );
+
+ if ( !value.isEmpty() ) {
+ expanded = true;
+ text.replace( pos, len, value );
+ pos = pos + value.length();
+ }
+ else {
+ pos = pos2;
+ }
+ }
+ }
+ }
+
+ return expanded;
+}
+
+/*
+ * expandTilde
+ *
+ * Replace "~user" with the users home directory
+ * Return true if expansion was made.
+ */
+static bool expandTilde(TQString &text)
+{
+ if ( text[0] != '~' )
+ return false;
+
+ bool expanded = false;
+
+ // Find the end of the user name = next '/' or ' '
+ //
+ int pos2 = text.find( ' ', 1 );
+ int pos_tmp = text.find( '/', 1 );
+
+ if ( pos2 == -1 || (pos_tmp != -1 && pos_tmp < pos2) )
+ pos2 = pos_tmp;
+
+ if ( pos2 == -1 )
+ pos2 = text.length();
+
+ // Replace ~user if the user name is terminated by '/' or ' '
+ //
+ if ( pos2 >= 0 ) {
+
+ TQString user = text.mid( 1, pos2-1 );
+ TQString dir;
+
+ // A single ~ is replaced with $HOME
+ //
+ if ( user.isEmpty() ) {
+ dir = TQDir::homeDirPath();
+ }
+ // ~user is replaced with the dir from passwd
+ //
+ else {
+ struct passwd *pw = ::getpwnam( user.local8Bit() );
+
+ if ( pw )
+ dir = TQFile::decodeName( pw->pw_dir );
+
+ ::endpwent();
+ }
+
+ if ( !dir.isEmpty() ) {
+ expanded = true;
+ text.replace(0, pos2, dir);
+ }
+ }
+
+ return expanded;
+}
+
+/*
+ * unescape
+ *
+ * Remove escapes and return the result in a new string
+ *
+ */
+static TQString unescape(const TQString &text)
+{
+ TQString result;
+
+ for (uint pos = 0; pos < text.length(); pos++)
+ if ( text[pos] != '\\' )
+ result.insert( result.length(), text[pos] );
+
+ return result;
+}
+
+void KURLCompletion::virtual_hook( int id, void* data )
+{ KCompletion::virtual_hook( id, data ); }
+
+#include "kurlcompletion.moc"
+
diff --git a/tdeio/tdeio/kurlcompletion.h b/tdeio/tdeio/kurlcompletion.h
new file mode 100644
index 000000000..30a825d3d
--- /dev/null
+++ b/tdeio/tdeio/kurlcompletion.h
@@ -0,0 +1,236 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Smith <dsmith@algonet.se>
+
+ This class was inspired by a previous KURLCompletion by
+ Henner Zeller <zeller@think.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KURLCOMPLETION_H
+#define KURLCOMPLETION_H
+
+#include <kcompletion.h>
+#include <tdeio/jobclasses.h>
+#include <tqstring.h>
+#include <tqstringlist.h>
+
+class KURL;
+class KURLCompletionPrivate;
+
+/**
+ * This class does completion of URLs including user directories (~user)
+ * and environment variables. Remote URLs are passed to KIO.
+ *
+ * @short Completion of a single URL
+ * @author David Smith <dsmith@algonet.se>
+ */
+class TDEIO_EXPORT KURLCompletion : public KCompletion
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Determines how completion is done.
+ * @li ExeCompletion - executables in $PATH or with full path.
+ * @li FileCompletion - all files with full path or in dir(), URLs
+ * are listed using KIO.
+ * @li DirCompletion - Same as FileCompletion but only returns directories.
+ */
+ enum Mode { ExeCompletion=1, FileCompletion, DirCompletion, SystemExeCompletion };
+
+ /**
+ * Constructs a KURLCompletion object in FileCompletion mode.
+ */
+ KURLCompletion();
+ /**
+ * This overloaded constructor allows you to set the Mode to ExeCompletion
+ * or FileCompletion without using setMode. Default is FileCompletion.
+ */
+ KURLCompletion(Mode);
+ /**
+ * Destructs the KURLCompletion object.
+ */
+ virtual ~KURLCompletion();
+
+ /**
+ * Finds completions to the given text.
+ *
+ * Remote URLs are listed with KIO. For performance reasons, local files
+ * are listed with KIO only if KURLCOMPLETION_LOCAL_KIO is set.
+ * The completion is done asyncronously if KIO is used.
+ *
+ * Returns the first match for user, environment, and local dir completion
+ * and TQString::null for asynchronous completion (KIO or threaded).
+ *
+ * @param text the text to complete
+ * @return the first match, or TQString::null if not found
+ */
+ virtual TQString makeCompletion(const TQString &text); // KDE4: remove return value, it's often null due to threading
+
+ /**
+ * Sets the current directory (used as base for completion).
+ * Default = $HOME.
+ * @param dir the current directory, either as a path or URL
+ */
+ virtual void setDir(const TQString &dir);
+
+ /**
+ * Returns the current directory, as it was given in setDir
+ * @return the current directory (path or URL)
+ */
+ virtual TQString dir() const;
+
+ /**
+ * Check whether asynchronous completion is in progress.
+ * @return true if asynchronous completion is in progress
+ */
+ virtual bool isRunning() const;
+
+ /**
+ * Stops asynchronous completion.
+ */
+ virtual void stop();
+
+ /**
+ * Returns the completion mode: exe or file completion (default FileCompletion).
+ * @return the completion mode
+ */
+ virtual Mode mode() const;
+
+ /**
+ * Changes the completion mode: exe or file completion
+ * @param mode the new completion mode
+ */
+ virtual void setMode( Mode mode );
+
+ /**
+ * Checks whether environment variables are completed and
+ * whether they are replaced internally while finding completions.
+ * Default is enabled.
+ * @return true if environment vvariables will be replaced
+ */
+ virtual bool replaceEnv() const;
+
+ /**
+ * Enables/disables completion and replacement (internally) of
+ * environment variables in URLs. Default is enabled.
+ * @param replace true to replace environment variables
+ */
+ virtual void setReplaceEnv( bool replace );
+
+ /**
+ * Returns whether ~username is completed and whether ~username
+ * is replaced internally with the user's home directory while
+ * finding completions. Default is enabled.
+ * @return true to replace tilde with the home directory
+ */
+ virtual bool replaceHome() const;
+
+ /**
+ * Enables/disables completion of ~username and replacement
+ * (internally) of ~username with the user's home directory.
+ * Default is enabled.
+ * @param replace true to replace tilde with the home directory
+ */
+ virtual void setReplaceHome( bool replace );
+
+ /**
+ * Replaces username and/or environment variables, depending on the
+ * current settings and returns the filtered url. Only works with
+ * local files, i.e. returns back the original string for non-local
+ * urls.
+ * @param text the text to process
+ * @return the path or URL resulting from this operation. If you
+ * want to convert it to a KURL, use KURL::fromPathOrURL.
+ */
+ TQString replacedPath( const TQString& text );
+
+ /**
+ * @internal I'll let ossi add a real one to KShell :)
+ * @since 3.2
+ */
+ static TQString replacedPath( const TQString& text,
+ bool replaceHome, bool replaceEnv = true );
+
+ class MyURL;
+protected:
+ // Called by KCompletion, adds '/' to directories
+ void postProcessMatch( TQString *match ) const;
+ void postProcessMatches( TQStringList *matches ) const;
+ void postProcessMatches( KCompletionMatches* matches ) const;
+
+ virtual void customEvent( TQCustomEvent *e );
+
+protected slots:
+ void slotEntries( TDEIO::Job *, const TDEIO::UDSEntryList& );
+ void slotIOFinished( TDEIO::Job * );
+
+private:
+
+ bool isAutoCompletion();
+
+ bool userCompletion(const MyURL &url, TQString *match);
+ bool envCompletion(const MyURL &url, TQString *match);
+ bool exeCompletion(const MyURL &url, TQString *match);
+ bool systemexeCompletion(const MyURL &url, TQString *match);
+ bool fileCompletion(const MyURL &url, TQString *match);
+ bool urlCompletion(const MyURL &url, TQString *match);
+
+ // List a directory using readdir()
+ void listDir( const TQString& dir,
+ TQStringList *matches,
+ const TQString& filter,
+ bool only_exe,
+ bool no_hidden );
+
+ // List the next dir in m_dirs
+ TQString listDirectories(const TQStringList &,
+ const TQString &,
+ bool only_exe = false,
+ bool only_dir = false,
+ bool no_hidden = false,
+ bool stat_files = true);
+
+ void listURLs( const TQValueList<KURL *> &urls,
+ const TQString &filter = TQString::null,
+ bool only_exe = false,
+ bool no_hidden = false );
+
+ void addMatches( const TQStringList & );
+ TQString finished();
+
+ void init();
+
+ void setListedURL(int compl_type /* enum ComplType */,
+ const TQString& dir = TQString::null,
+ const TQString& filter = TQString::null,
+ bool no_hidden = false );
+
+ bool isListedURL( int compl_type /* enum ComplType */,
+ const TQString& dir = TQString::null,
+ const TQString& filter = TQString::null,
+ bool no_hidden = false );
+
+ void adjustMatch( TQString& match ) const;
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ KURLCompletionPrivate *d;
+};
+
+#endif // KURLCOMPLETION_H
diff --git a/tdeio/tdeio/kurlpixmapprovider.cpp b/tdeio/tdeio/kurlpixmapprovider.cpp
new file mode 100644
index 000000000..caeedf066
--- /dev/null
+++ b/tdeio/tdeio/kurlpixmapprovider.cpp
@@ -0,0 +1,33 @@
+/* This file is part of the KDE libraries
+
+ Copyright (c) 2000 Carsten Pfeiffer <pfeiffer@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "kurlpixmapprovider.h"
+
+TQPixmap KURLPixmapProvider::pixmapFor( const TQString& url, int size ) {
+ KURL u;
+ if ( url.at(0) == '/' )
+ u.setPath( url );
+ else
+ u = url;
+ return KMimeType::pixmapForURL( u, 0, KIcon::Desktop, size );
+ }
+
+void KURLPixmapProvider::virtual_hook( int id, void* data )
+{ KPixmapProvider::virtual_hook( id, data ); }
diff --git a/tdeio/tdeio/kurlpixmapprovider.h b/tdeio/tdeio/kurlpixmapprovider.h
new file mode 100644
index 000000000..82be4bd1b
--- /dev/null
+++ b/tdeio/tdeio/kurlpixmapprovider.h
@@ -0,0 +1,59 @@
+/* This file is part of the KDE libraries
+
+ Copyright (c) 2000 Carsten Pfeiffer <pfeiffer@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KURLPIXMAPPROVIDER_H
+#define KURLPIXMAPPROVIDER_H
+
+#include <kpixmapprovider.h>
+#include <kmimetype.h>
+
+/**
+ * Implementation of KPixmapProvider.
+ *
+ * Uses KMimeType::pixmapForURL() to resolve icons.
+ *
+ * Instatiate this class and supply it to the desired class, e.g.
+ * \code
+ * KHistoryCombo *combo = new KHistoryCombo( this );
+ * combo->setPixmapProvider( new KURLPixmapProvider );
+ * [...]
+ * \endcode
+ *
+ * @short Resolves pixmaps for URLs
+ * @author Carsten Pfeiffer <pfeiffer@kde.org>
+ */
+class TDEIO_EXPORT KURLPixmapProvider : public KPixmapProvider
+{
+public:
+ /**
+ * Returns a pixmap for @p url with size @p size.
+ * Uses KMimeType::pixmapForURL().
+ * @param url the URL to fetch a pixmap for
+ * @param size the size of the pixmap in pixels, or 0 for default.
+ * @return the resulting pixmap
+ * @see KIcon::StdSizes
+ */
+ virtual TQPixmap pixmapFor( const TQString& url, int size = 0 );
+protected:
+ virtual void virtual_hook( int id, void* data );
+};
+
+
+#endif // KURLPIXMAPPROVIDER_H
diff --git a/tdeio/tdeio/kuserprofile.cpp b/tdeio/tdeio/kuserprofile.cpp
new file mode 100644
index 000000000..ebd8b8deb
--- /dev/null
+++ b/tdeio/tdeio/kuserprofile.cpp
@@ -0,0 +1,355 @@
+/* This file is part of the KDE libraries
+ * Copyright (C) 1999 Torben Weis <weis@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include "kuserprofile.h"
+#include "kservice.h"
+#include "kservicetype.h"
+#include "kservicetypefactory.h"
+
+#include <tdeconfig.h>
+#include <kapplication.h>
+#include <kglobal.h>
+#include <kdebug.h>
+#include <kstaticdeleter.h>
+
+#include <tqtl.h>
+
+template class TQPtrList<KServiceTypeProfile>;
+typedef TQPtrList<KServiceTypeProfile> KServiceTypeProfileList;
+
+/*********************************************
+ *
+ * KServiceTypeProfile
+ *
+ *********************************************/
+
+KServiceTypeProfileList* KServiceTypeProfile::s_lstProfiles = 0L;
+static KStaticDeleter< KServiceTypeProfileList > profileDeleter;
+bool KServiceTypeProfile::s_configurationMode = false;
+
+void KServiceTypeProfile::initStatic()
+{
+ if ( s_lstProfiles )
+ return;
+
+ // Make sure that a KServiceTypeFactory gets created.
+ (void) KServiceTypeFactory::self();
+
+ profileDeleter.setObject(s_lstProfiles, new KServiceTypeProfileList);
+ s_lstProfiles->setAutoDelete( true );
+
+ TDEConfig config( "profilerc", true, false);
+
+ static const TQString & defaultGroup = TDEGlobal::staticQString("<default>");
+
+ TQStringList tmpList = config.groupList();
+ for (TQStringList::Iterator aIt = tmpList.begin();
+ aIt != tmpList.end(); ++aIt) {
+ if ( *aIt == defaultGroup )
+ continue;
+
+ config.setGroup( *aIt );
+
+ TQString appId = config.readEntry( "Application" );
+
+ KService::Ptr pService = KService::serviceByStorageId(appId);
+
+ if ( pService ) {
+ TQString application = pService->storageId();
+ TQString type = config.readEntry( "ServiceType" );
+ TQString type2 = config.readEntry( "GenericServiceType" );
+ if (type2.isEmpty()) // compat code
+ type2 = (pService->type() == "Application") ? "Application" : "KParts/ReadOnlyPart";
+ int pref = config.readNumEntry( "Preference" );
+
+ if ( !type.isEmpty() /* && pref >= 0*/ ) // Don't test for pref here. We want those in the list, to mark them as forbidden
+ {
+ KServiceTypeProfile* p =
+ KServiceTypeProfile::serviceTypeProfile( type, type2 );
+
+ if ( !p ) {
+ p = new KServiceTypeProfile( type, type2 );
+ s_lstProfiles->append( p );
+ }
+
+ bool allow = config.readBoolEntry( "AllowAsDefault" );
+ //kdDebug(7014) << "KServiceTypeProfile::initStatic adding service " << application << " to profile for " << type << "," << type2 << " with preference " << pref << endl;
+ p->addService( application, pref, allow );
+ }
+ }
+ }
+}
+
+//static
+void KServiceTypeProfile::clear()
+{
+ // HACK tdesycoca may open the dummy db, in such case the first call to tdesycoca
+ // in initStatic() leads to closing the dummy db and clear() being called
+ // in the middle of it, making s_lstProfiles be NULL
+ if( s_lstProfiles == NULL || s_lstProfiles->count() == 0 )
+ return;
+ profileDeleter.destructObject();
+}
+
+//static
+KServiceTypeProfile::OfferList KServiceTypeProfile::offers( const TQString& _servicetype, const TQString& _genericServiceType )
+{
+ OfferList offers;
+ TQStringList serviceList;
+ //kdDebug(7014) << "KServiceTypeProfile::offers( " << _servicetype << "," << _genericServiceType << " )" << endl;
+
+ // Note that KServiceTypeProfile::offers() calls KServiceType::offers(),
+ // so we _do_ get the new services, that are available but not in the profile.
+ if ( _genericServiceType.isEmpty() )
+ {
+ initStatic();
+ // We want all profiles for servicetype, if we have profiles.
+ // ## Slow loop, if profilerc is big. We should use a map instead?
+ TQPtrListIterator<KServiceTypeProfile> it( *s_lstProfiles );
+ for( ; it.current(); ++it )
+ if ( it.current()->m_strServiceType == _servicetype )
+ {
+ offers += it.current()->offers();
+ }
+ //kdDebug(7014) << "Found profile: " << offers.count() << " offers" << endl;
+ }
+ else
+ {
+ KServiceTypeProfile* profile = serviceTypeProfile( _servicetype, _genericServiceType );
+ if ( profile )
+ {
+ //kdDebug(7014) << "Found profile: " << profile->offers().count() << " offers" << endl;
+ offers += profile->offers();
+ }
+ else
+ {
+ // Try the other way round, order is not like size, it doesn't matter.
+ profile = serviceTypeProfile( _genericServiceType, _servicetype );
+ if ( profile )
+ {
+ //kdDebug(7014) << "Found profile after switching: " << profile->offers().count() << " offers" << endl;
+ offers += profile->offers();
+ }
+ }
+ }
+
+ // Collect services, to make the next loop faster
+ OfferList::Iterator itOffers = offers.begin();
+ for( ; itOffers != offers.end(); ++itOffers )
+ serviceList += (*itOffers).service()->desktopEntryPath(); // this should identify each service uniquely
+ //kdDebug(7014) << "serviceList: " << serviceList.join(",") << endl;
+
+ // Now complete with any other offers that aren't in the profile
+ // This can be because the services have been installed after the profile was written,
+ // but it's also the case for any service that's neither App nor ReadOnlyPart, e.g. RenameDlg/Plugin
+ KService::List list = KServiceType::offers( _servicetype );
+ //kdDebug(7014) << "Using KServiceType::offers, result: " << list.count() << " offers" << endl;
+ TQValueListIterator<KService::Ptr> it = list.begin();
+ for( ; it != list.end(); ++it )
+ {
+ if (_genericServiceType.isEmpty() /*no constraint*/ || (*it)->hasServiceType( _genericServiceType ))
+ {
+ // Check that we don't already have it ;)
+ if ( serviceList.find( (*it)->desktopEntryPath() ) == serviceList.end() )
+ {
+ bool allow = (*it)->allowAsDefault();
+ KServiceOffer o( (*it), (*it)->initialPreferenceForMimeType(_servicetype), allow );
+ offers.append( o );
+ //kdDebug(7014) << "Appending offer " << (*it)->name() << " initial preference=" << (*it)->initialPreference() << " allow-as-default=" << allow << endl;
+ }
+ //else
+ // kdDebug(7014) << "Already having offer " << (*it)->name() << endl;
+ }
+ }
+
+ qBubbleSort( offers );
+
+#if 0
+ // debug code, comment if you wish but don't remove.
+ kdDebug(7014) << "Sorted list:" << endl;
+ OfferList::Iterator itOff = offers.begin();
+ for( ; itOff != offers.end(); ++itOff )
+ kdDebug(7014) << (*itOff).service()->name() << " allow-as-default=" << (*itOff).allowAsDefault() << endl;
+#endif
+
+ //kdDebug(7014) << "Returning " << offers.count() << " offers" << endl;
+ return offers;
+}
+
+KServiceTypeProfile::KServiceTypeProfile( const TQString& _servicetype, const TQString& _genericServiceType )
+{
+ initStatic();
+
+ m_strServiceType = _servicetype;
+ m_strGenericServiceType = _genericServiceType;
+}
+
+KServiceTypeProfile::~KServiceTypeProfile()
+{
+}
+
+void KServiceTypeProfile::addService( const TQString& _service,
+ int _preference, bool _allow_as_default )
+{
+ m_mapServices[ _service ].m_iPreference = _preference;
+ m_mapServices[ _service ].m_bAllowAsDefault = _allow_as_default;
+}
+
+int KServiceTypeProfile::preference( const TQString& _service ) const
+{
+ KService::Ptr service = KService::serviceByName( _service );
+ if (!service)
+ return 0;
+ TQMap<TQString,Service>::ConstIterator it = m_mapServices.find( service->storageId() );
+ if ( it == m_mapServices.end() )
+ return 0;
+
+ return it.data().m_iPreference;
+}
+
+bool KServiceTypeProfile::allowAsDefault( const TQString& _service ) const
+{
+ KService::Ptr service = KService::serviceByName( _service );
+ if (!service)
+ return false;
+
+ // Does the service itself not allow that ?
+ if ( !service->allowAsDefault() )
+ return false;
+
+ // Look what the user says ...
+ TQMap<TQString,Service>::ConstIterator it = m_mapServices.find( service->storageId() );
+ if ( it == m_mapServices.end() )
+ return 0;
+
+ return it.data().m_bAllowAsDefault;
+}
+
+KServiceTypeProfile* KServiceTypeProfile::serviceTypeProfile( const TQString& _servicetype, const TQString& _genericServiceType )
+{
+ initStatic();
+ static const TQString& app_str = TDEGlobal::staticQString("Application");
+
+ const TQString &_genservicetype = ((!_genericServiceType.isEmpty()) ? _genericServiceType : app_str);
+
+ TQPtrListIterator<KServiceTypeProfile> it( *s_lstProfiles );
+ for( ; it.current(); ++it )
+ if (( it.current()->m_strServiceType == _servicetype ) &&
+ ( it.current()->m_strGenericServiceType == _genservicetype))
+ return it.current();
+
+ return 0;
+}
+
+
+KServiceTypeProfile::OfferList KServiceTypeProfile::offers() const
+{
+ OfferList offers;
+
+ kdDebug(7014) << "KServiceTypeProfile::offers serviceType=" << m_strServiceType << " genericServiceType=" << m_strGenericServiceType << endl;
+ KService::List list = KServiceType::offers( m_strServiceType );
+ TQValueListIterator<KService::Ptr> it = list.begin();
+ for( ; it != list.end(); ++it )
+ {
+ //kdDebug(7014) << "KServiceTypeProfile::offers considering " << (*it)->name() << endl;
+ if ( m_strGenericServiceType.isEmpty() || (*it)->hasServiceType( m_strGenericServiceType ) )
+ {
+ // Now look into the profile, to find this service's preference.
+ TQMap<TQString,Service>::ConstIterator it2 = m_mapServices.find( (*it)->storageId() );
+
+ if( it2 != m_mapServices.end() )
+ {
+ //kdDebug(7014) << "found in mapServices pref=" << it2.data().m_iPreference << endl;
+ if ( it2.data().m_iPreference > 0 ) {
+ bool allow = (*it)->allowAsDefault();
+ if ( allow )
+ allow = it2.data().m_bAllowAsDefault;
+ KServiceOffer o( (*it), it2.data().m_iPreference, allow );
+ offers.append( o );
+ }
+ }
+ else
+ {
+ //kdDebug(7014) << "not found in mapServices. Appending." << endl;
+ // We use 0 as the preference to ensure new apps don't take over existing apps (which default to 1)
+ KServiceOffer o( (*it), 0, (*it)->allowAsDefault() );
+ offers.append( o );
+ }
+ }/* else
+ kdDebug(7014) << "Doesn't have " << m_strGenericServiceType << endl;*/
+ }
+
+ qBubbleSort( offers );
+
+ //kdDebug(7014) << "KServiceTypeProfile::offers returning " << offers.count() << " offers" << endl;
+ return offers;
+}
+
+KService::Ptr KServiceTypeProfile::preferredService( const TQString & _serviceType, const TQString & _genericServiceType )
+{
+ OfferList lst = offers( _serviceType, _genericServiceType );
+
+ OfferList::Iterator itOff = lst.begin();
+ // Look for the first one that is allowed as default.
+ // Since the allowed-as-default are first anyway, we only have
+ // to look at the first one to know.
+ if( itOff != lst.end() && (*itOff).allowAsDefault() )
+ return (*itOff).service();
+
+ //kdDebug(7014) << "No offers, or none allowed as default" << endl;
+ return 0L;
+}
+
+/*********************************************
+ *
+ * KServiceOffer
+ *
+ *********************************************/
+
+KServiceOffer::KServiceOffer()
+{
+ m_iPreference = -1;
+}
+
+KServiceOffer::KServiceOffer( const KServiceOffer& _o )
+{
+ m_pService = _o.m_pService;
+ m_iPreference = _o.m_iPreference;
+ m_bAllowAsDefault = _o.m_bAllowAsDefault;
+}
+
+KServiceOffer::KServiceOffer( KService::Ptr _service, int _pref, bool _default )
+{
+ m_pService = _service;
+ m_iPreference = _pref;
+ m_bAllowAsDefault = _default;
+}
+
+
+bool KServiceOffer::operator< ( const KServiceOffer& _o ) const
+{
+ // Put offers allowed as default FIRST.
+ if ( _o.m_bAllowAsDefault && !m_bAllowAsDefault )
+ return false; // _o is default and not 'this'.
+ if ( !_o.m_bAllowAsDefault && m_bAllowAsDefault )
+ return true; // 'this' is default but not _o.
+ // Both offers are allowed or not allowed as default
+ // -> use preferences to sort them
+ // The bigger the better, but we want the better FIRST
+ return _o.m_iPreference < m_iPreference;
+}
diff --git a/tdeio/tdeio/kuserprofile.h b/tdeio/tdeio/kuserprofile.h
new file mode 100644
index 000000000..45b58fe6a
--- /dev/null
+++ b/tdeio/tdeio/kuserprofile.h
@@ -0,0 +1,282 @@
+/* This file is part of the KDE project
+ Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kuserprofile_h__
+#define __kuserprofile_h__
+
+#include <tqmap.h>
+#include <tqstring.h>
+#include <tqptrlist.h>
+#include <tqvaluelist.h>
+
+#include <kservice.h>
+
+/**
+ * This class holds the user-specific preferences of a service
+ * (whether it can be a default offer or not, how big is the preference
+ * for this offer, ...). Basically it is a reference to a
+ * KService, a number that represents the user's preference (bigger
+ * is better) and a flag whether the KService can be used as default.
+ *
+ * @see KService
+ * @short Holds the user's preference of a service.
+ */
+class TDEIO_EXPORT KServiceOffer
+{
+public:
+ /**
+ * Create an invalid service offer.
+ */
+ KServiceOffer();
+
+ /**
+ * Copy constructor.
+ * Shallow copy (the KService will not be copied).
+ */
+ KServiceOffer( const KServiceOffer& );
+
+ /**
+ * Creates a new KServiceOffer.
+ * @param _service a pointer to the KService
+ * @param _pref the user's preference value, must be positive,
+ * bigger is better
+ * @param _default true if the service should be used as
+ * default
+ */
+ KServiceOffer( KService::Ptr _service,
+ int _pref, bool _default );
+
+ /**
+ * A service is bigger that the other when it can be default
+ * (and the other is not) and its preference value it higher.
+ */
+ bool operator< ( const KServiceOffer& ) const;
+ /**
+ * Is it allowed to use this service for default actions
+ * (e.g. Left Click in a file manager, or KRun in general).
+ * @return true if the service is a allowed as default
+ */
+ bool allowAsDefault() const { return m_bAllowAsDefault; }
+ /**
+ * The bigger this number is, the better is this service.
+ * @return the preference number (negative numbers will be
+ * returned by invalid service offers)
+ */
+ int preference() const { return m_iPreference; }
+ /**
+ * The service which this offer is about.
+ * @return the service this offer is about, can be 0
+ * in valid offers or when not set
+ */
+ KService::Ptr service() const { return m_pService; }
+ /**
+ * Check whether the entry is valid. A service is valid if
+ * its preference value is positive.
+ * @return true if the service offer is valid
+ */
+ bool isValid() const { return m_iPreference >= 0; }
+
+private:
+ int m_iPreference;
+ bool m_bAllowAsDefault;
+ KService::Ptr m_pService;
+private:
+ class KServiceOfferPrivate;
+};
+
+/**
+ * KServiceTypeProfile represents the user's preferences for services
+ * of a service type.
+ * It consists of a list of services (service offers) for the service type
+ * that is sorted by the user's preference.
+ * KTrader uses KServiceTypeProfile to sort its results, so usually
+ * you can just use KTrader to find the user's preferred service.
+ *
+ * @see KService
+ * @see KServiceType
+ * @see KServiceOffer
+ * @see KTrader
+ * @short Represents the user's preferences for services of a service type
+ */
+class TDEIO_EXPORT KServiceTypeProfile
+{
+public:
+ typedef TQValueList<KServiceOffer> OfferList;
+
+ ~KServiceTypeProfile();
+
+ /**
+ * @deprecated Remove in KDE 4, unused.
+ * Returns the users preference of the given service.
+ * @param _service the name of the service to check
+ * @return the user's preference number of the given
+ * @p _service, or 0 the service is unknown.
+ */
+ int preference( const TQString& _service ) const;
+
+ /**
+ * @deprecated Remove in KDE 4, unused.
+ * Checks whether the given @p _service can be used as default.
+ * @param _service the name of the service to check
+ * @return true if allowed as default
+ */
+ bool allowAsDefault( const TQString& _service ) const;
+
+ /**
+ * Returns the list of all service offers for the service types
+ * that are represented by this profile.
+ * @return the list of KServiceOffer instances
+ */
+ OfferList offers() const;
+
+ /**
+ * Returns the preferred service for @p _serviceType and @p _genericServiceType
+ * ("Application", type of component, or null).
+ *
+ * @param serviceType the service type (e.g. a MIME type)
+ * @param genericServiceType the generic service type (e.g. "Application" or
+ * "KParts/ReadOnlyPart")
+ * @return the preferred service, or 0 if no service is available
+ */
+ static KService::Ptr preferredService( const TQString & serviceType, const TQString & genericServiceType );
+
+ /**
+ * Returns the profile for the requested service type.
+ * @param servicetype the service type (e.g. a MIME type)
+ * @param genericServiceType the generic service type (e.g. "Application"
+ * or "KParts/ReadOnlyPart"). Can be TQString::null,
+ * then the "Application" generic type will be used
+ * @return the KServiceTypeProfile with the given arguments, or 0 if not found
+ */
+ static KServiceTypeProfile* serviceTypeProfile( const TQString& servicetype, const TQString & genericServiceType = TQString::null );
+
+ /**
+ * Returns the offers associated with a given servicetype, sorted by preference.
+ * This is what KTrader uses to get the list of offers, before applying the
+ * constraints and preferences.
+ *
+ * If @p genericServiceType is specified, a list is returned with
+ * the offers associated with the combination of the two service types.
+ * This is almost like an "foo in ServiceTypes" constraint in the KTrader,
+ * but the difference is that to order the offers, we will look at entries
+ * specifically for those two service types. Typically, this is used for
+ * getting the list of embeddable components that can handle a given mimetype.
+ * In that case, @p servicetype is the mimetype and @p genericServiceType is "KParts/ReadOnlyPart".
+ *
+ * @param servicetype the service type (e.g. a MIME type)
+ * @param genericServiceType the generic service type (e.g. "Application"
+ * or "KParts/ReadOnlyPart"). Can be TQString::null,
+ * then all generic types will be included
+ * @return the list of offers witht he given parameters
+ */
+ static OfferList offers( const TQString& servicetype, const TQString& genericServiceType = TQString::null );
+
+ /**
+ * Returns a list of all KServiceTypeProfiles.
+ * @return a list of all KServiceTypeProfiles
+ */
+ static const TQPtrList<KServiceTypeProfile>& serviceTypeProfiles() { return *s_lstProfiles; }
+
+ /**
+ * Clear all cached information
+ */
+ static void clear();
+
+ /**
+ * This method activates a special mode of KServiceTypeProfile, in which all/all
+ * and all/allfiles are excluded from the results of the queries.
+ * It is meant for the configuration module _only_.
+ * @internal
+ */
+ static void setConfigurationMode() { s_configurationMode = true; }
+
+ /**
+ * This method deactivates the special mode above of KServiceTypeProfile
+ * It is meant for the configuration module _only_.
+ * @internal
+ * @since 3.5.7
+ */
+ static void unsetConfigurationMode() { s_configurationMode = false; }
+
+ /**
+ * @internal
+ */
+ static bool configurationMode() { return s_configurationMode; }
+
+protected:
+ /**
+ * Constructor is called when the user profile is read for the
+ * first time.
+ * @param serviceType the service type (e.g. a MIME type)
+ * @param genericServiceType the generic service type (e.g. "Application"
+ * or "KParts/ReadOnlyPart"). Can be TQString::null,
+ * then the "Application" generic type will be used
+ */
+ KServiceTypeProfile( const TQString& serviceType,
+ const TQString& genericServiceType = TQString::null );
+
+ /**
+ * Add a service to this profile.
+ * @param _service the name of the service
+ * @param _preference the user's preference value, must be positive,
+ * bigger is better
+ * @param _allow_as_default true if the service should be used as
+ * default
+ */
+ void addService( const TQString& _service, int _preference = 1, bool _allow_as_default = true );
+
+private:
+ /**
+ * Represents the users assessment of a special service
+ */
+ struct Service
+ {
+ /**
+ * The bigger this number is, the better is this service.
+ */
+ int m_iPreference;
+ /**
+ * Is it allowed to use this service for default actions.
+ */
+ bool m_bAllowAsDefault;
+ };
+
+ /**
+ * Map of all services for which we have assessments.
+ */
+ TQMap<TQString,Service> m_mapServices;
+
+ /**
+ * ServiceType of this profile.
+ */
+ TQString m_strServiceType;
+
+ /**
+ * Secondary ServiceType of this profile.
+ */
+ TQString m_strGenericServiceType;
+
+ static void initStatic();
+ static TQPtrList<KServiceTypeProfile>* s_lstProfiles;
+ static bool s_configurationMode;
+private:
+ class KServiceTypeProfilePrivate* d;
+};
+
+#endif
diff --git a/tdeio/tdeio/kzip.cpp b/tdeio/tdeio/kzip.cpp
new file mode 100644
index 000000000..85dcb76d1
--- /dev/null
+++ b/tdeio/tdeio/kzip.cpp
@@ -0,0 +1,1460 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+ Copyright (C) 2002 Holger Schroeder <holger-kde@holgis.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/*
+ This class implements a tdeioslave to access ZIP files from KDE.
+ you can use it in IO_ReadOnly or in IO_WriteOnly mode, and it
+ behaves just as expected (i hope ;-) ).
+ It can also be used in IO_ReadWrite mode, in this case one can
+ append files to an existing zip archive. when you append new files, which
+ are not yet in the zip, it works as expected, they are appended at the end.
+ when you append a file, which is already in the file, the reference to the
+ old file is dropped and the new one is added to the zip. but the
+ old data from the file itself is not deleted, it is still in the
+ zipfile. so when you want to have a small and garbagefree zipfile,
+ just read the contents of the appended zipfile and write it to a new one
+ in IO_WriteOnly mode. especially take care of this, when you don't want
+ to leak information of how intermediate versions of files in the zip
+ were looking.
+ For more information on the zip fileformat go to
+ http://www.pkware.com/support/appnote.html .
+
+*/
+
+#include "kzip.h"
+#include "kfilterdev.h"
+#include "klimitediodevice.h"
+#include <kmimetype.h>
+#include <ksavefile.h>
+#include <kdebug.h>
+
+#include <tqasciidict.h>
+#include <tqfile.h>
+#include <tqdir.h>
+#include <tqdatetime.h>
+#include <tqptrlist.h>
+
+#include <zlib.h>
+#include <time.h>
+#include <string.h>
+
+const int max_path_len = 4095; // maximum number of character a path may contain
+
+static void transformToMsDos(const TQDateTime& dt, char* buffer)
+{
+ if ( dt.isValid() )
+ {
+ const TQ_UINT16 time =
+ ( dt.time().hour() << 11 ) // 5 bit hour
+ | ( dt.time().minute() << 5 ) // 6 bit minute
+ | ( dt.time().second() >> 1 ); // 5 bit double seconds
+
+ buffer[0] = char(time);
+ buffer[1] = char(time >> 8);
+
+ const TQ_UINT16 date =
+ ( ( dt.date().year() - 1980 ) << 9 ) // 7 bit year 1980-based
+ | ( dt.date().month() << 5 ) // 4 bit month
+ | ( dt.date().day() ); // 5 bit day
+
+ buffer[2] = char(date);
+ buffer[3] = char(date >> 8);
+ }
+ else // !dt.isValid(), assume 1980-01-01 midnight
+ {
+ buffer[0] = 0;
+ buffer[1] = 0;
+ buffer[2] = 33;
+ buffer[3] = 0;
+ }
+}
+
+static time_t transformFromMsDos(const char* buffer)
+{
+ TQ_UINT16 time = (uchar)buffer[0] | ( (uchar)buffer[1] << 8 );
+ int h = time >> 11;
+ int m = ( time & 0x7ff ) >> 5;
+ int s = ( time & 0x1f ) * 2 ;
+ TQTime qt(h, m, s);
+
+ TQ_UINT16 date = (uchar)buffer[2] | ( (uchar)buffer[3] << 8 );
+ int y = ( date >> 9 ) + 1980;
+ int o = ( date & 0x1ff ) >> 5;
+ int d = ( date & 0x1f );
+ TQDate qd(y, o, d);
+
+ TQDateTime dt( qd, qt );
+ return dt.toTime_t();
+}
+
+// == parsing routines for zip headers
+
+/** all relevant information about parsing file information */
+struct ParseFileInfo {
+ // file related info
+// TQCString name; // filename
+ mode_t perm; // permissions of this file
+ time_t atime; // last access time (UNIX format)
+ time_t mtime; // modification time (UNIX format)
+ time_t ctime; // creation time (UNIX format)
+ int uid; // user id (-1 if not specified)
+ int gid; // group id (-1 if not specified)
+ TQCString guessed_symlink; // guessed symlink target
+ int extralen; // length of extra field
+
+ // parsing related info
+ bool exttimestamp_seen; // true if extended timestamp extra field
+ // has been parsed
+ bool newinfounix_seen; // true if Info-ZIP Unix New extra field has
+ // been parsed
+
+ ParseFileInfo() : perm(0100644), uid(-1), gid(-1), extralen(0),
+ exttimestamp_seen(false), newinfounix_seen(false) {
+ ctime = mtime = atime = time(0);
+ }
+};
+
+/** updates the parse information with the given extended timestamp extra field.
+ * @param buffer start content of buffer known to contain an extended
+ * timestamp extra field (without magic & size)
+ * @param size size of field content (must not count magic and size entries)
+ * @param islocal true if this is a local field, false if central
+ * @param pfi ParseFileInfo object to be updated
+ * @return true if processing was successful
+ */
+static bool parseExtTimestamp(const char *buffer, int size, bool islocal,
+ ParseFileInfo &pfi) {
+ if (size < 1) {
+ kdDebug(7040) << "premature end of extended timestamp (#1)" << endl;
+ return false;
+ }/*end if*/
+ int flags = *buffer; // read flags
+ buffer += 1;
+ size -= 1;
+
+ if (flags & 1) { // contains modification time
+ if (size < 4) {
+ kdDebug(7040) << "premature end of extended timestamp (#2)" << endl;
+ return false;
+ }/*end if*/
+ pfi.mtime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
+ | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
+ buffer += 4;
+ size -= 4;
+ }/*end if*/
+ // central extended field cannot contain more than the modification time
+ // even if other flags are set
+ if (!islocal) {
+ pfi.exttimestamp_seen = true;
+ return true;
+ }/*end if*/
+
+ if (flags & 2) { // contains last access time
+ if (size < 4) {
+ kdDebug(7040) << "premature end of extended timestamp (#3)" << endl;
+ return true;
+ }/*end if*/
+ pfi.atime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
+ | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
+ buffer += 4;
+ size -= 4;
+ }/*end if*/
+
+ if (flags & 4) { // contains creation time
+ if (size < 4) {
+ kdDebug(7040) << "premature end of extended timestamp (#4)" << endl;
+ return true;
+ }/*end if*/
+ pfi.ctime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
+ | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
+ buffer += 4;
+ }/*end if*/
+
+ pfi.exttimestamp_seen = true;
+ return true;
+}
+
+/** updates the parse information with the given Info-ZIP Unix old extra field.
+ * @param buffer start of content of buffer known to contain an Info-ZIP
+ * Unix old extra field (without magic & size)
+ * @param size size of field content (must not count magic and size entries)
+ * @param islocal true if this is a local field, false if central
+ * @param pfi ParseFileInfo object to be updated
+ * @return true if processing was successful
+ */
+static bool parseInfoZipUnixOld(const char *buffer, int size, bool islocal,
+ ParseFileInfo &pfi) {
+ // spec mandates to omit this field if one of the newer fields are available
+ if (pfi.exttimestamp_seen || pfi.newinfounix_seen) return true;
+
+ if (size < 8) {
+ kdDebug(7040) << "premature end of Info-ZIP unix extra field old" << endl;
+ return false;
+ }/*end if*/
+
+ pfi.atime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
+ | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
+ buffer += 4;
+ pfi.mtime = time_t((uchar)buffer[0] | (uchar)buffer[1] << 8
+ | (uchar)buffer[2] << 16 | (uchar)buffer[3] << 24);
+ buffer += 4;
+ if (islocal && size >= 12) {
+ pfi.uid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
+ buffer += 2;
+ pfi.gid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
+ buffer += 2;
+ }/*end if*/
+ return true;
+}
+
+#if 0 // not needed yet
+/** updates the parse information with the given Info-ZIP Unix new extra field.
+ * @param buffer start of content of buffer known to contain an Info-ZIP
+ * Unix new extra field (without magic & size)
+ * @param size size of field content (must not count magic and size entries)
+ * @param islocal true if this is a local field, false if central
+ * @param pfi ParseFileInfo object to be updated
+ * @return true if processing was successful
+ */
+static bool parseInfoZipUnixNew(const char *buffer, int size, bool islocal,
+ ParseFileInfo &pfi) {
+ if (!islocal) { // contains nothing in central field
+ pfi.newinfounix = true;
+ return true;
+ }/*end if*/
+
+ if (size < 4) {
+ kdDebug(7040) << "premature end of Info-ZIP unix extra field new" << endl;
+ return false;
+ }/*end if*/
+
+ pfi.uid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
+ buffer += 2;
+ pfi.gid = (uchar)buffer[0] | (uchar)buffer[1] << 8;
+ buffer += 2;
+
+ pfi.newinfounix = true;
+ return true;
+}
+#endif
+
+/**
+ * parses the extra field
+ * @param buffer start of buffer where the extra field is to be found
+ * @param size size of the extra field
+ * @param islocal true if this is part of a local header, false if of central
+ * @param pfi ParseFileInfo object which to write the results into
+ * @return true if parsing was successful
+ */
+static bool parseExtraField(const char *buffer, int size, bool islocal,
+ ParseFileInfo &pfi) {
+ // extra field in central directory doesn't contain useful data, so we
+ // don't bother parsing it
+ if (!islocal) return true;
+
+ while (size >= 4) { // as long as a potential extra field can be read
+ int magic = (uchar)buffer[0] | (uchar)buffer[1] << 8;
+ buffer += 2;
+ int fieldsize = (uchar)buffer[0] | (uchar)buffer[1] << 8;
+ buffer += 2;
+ size -= 4;
+
+ if (fieldsize > size) {
+ //kdDebug(7040) << "fieldsize: " << fieldsize << " size: " << size << endl;
+ kdDebug(7040) << "premature end of extra fields reached" << endl;
+ break;
+ }/*end if*/
+
+ switch (magic) {
+ case 0x5455: // extended timestamp
+ if (!parseExtTimestamp(buffer, fieldsize, islocal, pfi)) return false;
+ break;
+ case 0x5855: // old Info-ZIP unix extra field
+ if (!parseInfoZipUnixOld(buffer, fieldsize, islocal, pfi)) return false;
+ break;
+#if 0 // not needed yet
+ case 0x7855: // new Info-ZIP unix extra field
+ if (!parseInfoZipUnixNew(buffer, fieldsize, islocal, pfi)) return false;
+ break;
+#endif
+ default:
+ /* ignore everything else */;
+ }/*end switch*/
+
+ buffer += fieldsize;
+ size -= fieldsize;
+ }/*wend*/
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////
+/////////////////////////// KZip ///////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+
+class KZip::KZipPrivate
+{
+public:
+ KZipPrivate()
+ : m_crc( 0 ),
+ m_currentFile( 0L ),
+ m_currentDev( 0L ),
+ m_compression( 8 ),
+ m_extraField( KZip::NoExtraField ),
+ m_offset( 0L ),
+ m_saveFile( 0 ) {}
+
+ unsigned long m_crc; // checksum
+ KZipFileEntry* m_currentFile; // file currently being written
+ TQIODevice* m_currentDev; // filterdev used to write to the above file
+ TQPtrList<KZipFileEntry> m_fileList; // flat list of all files, for the index (saves a recursive method ;)
+ int m_compression;
+ KZip::ExtraField m_extraField;
+ unsigned int m_offset; // holds the offset of the place in the zip,
+ // where new data can be appended. after openarchive it points to 0, when in
+ // writeonly mode, or it points to the beginning of the central directory.
+ // each call to writefile updates this value.
+ KSaveFile* m_saveFile;
+};
+
+KZip::KZip( const TQString& filename )
+ : KArchive( 0L )
+{
+ //kdDebug(7040) << "KZip(filename) reached." << endl;
+ Q_ASSERT( !filename.isEmpty() );
+ m_filename = filename;
+ d = new KZipPrivate;
+ // unusual: this ctor leaves the device set to 0.
+ // This is for the use of KSaveFile, see openArchive.
+ // KDE4: move KSaveFile support to base class, KArchive.
+}
+
+KZip::KZip( TQIODevice * dev )
+ : KArchive( dev )
+{
+ //kdDebug(7040) << "KZip::KZip( TQIODevice * dev) reached." << endl;
+ d = new KZipPrivate;
+}
+
+KZip::~KZip()
+{
+ // mjarrett: Closes to prevent ~KArchive from aborting w/o device
+ //kdDebug(7040) << "~KZip reached." << endl;
+ if( isOpened() )
+ close();
+ if ( !m_filename.isEmpty() ) { // we created the device ourselves
+ if ( d->m_saveFile ) // writing mode
+ delete d->m_saveFile;
+ else // reading mode
+ delete device(); // (the TQFile)
+ }
+ delete d;
+}
+
+bool KZip::openArchive( int mode )
+{
+ //kdDebug(7040) << "openarchive reached." << endl;
+ d->m_fileList.clear();
+
+ switch ( mode ) {
+ case IO_WriteOnly:
+ // The use of KSaveFile can't be done in the ctor (no mode known yet)
+ // Ideally we would reimplement open() and do it there (BIC)
+ if ( !m_filename.isEmpty() ) {
+ kdDebug(7040) << "Writing to a file using KSaveFile" << endl;
+ d->m_saveFile = new KSaveFile( m_filename );
+ if ( d->m_saveFile->status() != 0 ) {
+ kdWarning(7040) << "KSaveFile creation for " << m_filename << " failed, " << strerror( d->m_saveFile->status() ) << endl;
+ delete d->m_saveFile;
+ d->m_saveFile = 0;
+ return false;
+ }
+ Q_ASSERT( d->m_saveFile->file() );
+ setDevice( TQT_TQIODEVICE(d->m_saveFile->file()) );
+ }
+ return true;
+ case IO_ReadOnly:
+ case IO_ReadWrite:
+ {
+ // ReadWrite mode still uses TQFile for now; we'd need to copy to the tempfile, in fact.
+ if ( !m_filename.isEmpty() ) {
+ setDevice( TQT_TQIODEVICE(new TQFile( m_filename )) );
+ if ( !device()->open( mode ) )
+ return false;
+ }
+ break; // continued below
+ }
+ default:
+ kdWarning(7040) << "Unsupported mode " << mode << endl;
+ return false;
+ }
+
+ char buffer[47];
+
+ // Check that it's a valid ZIP file
+ // the above code opened the underlying device already.
+ TQIODevice* dev = device();
+
+ if (!dev) {
+ return false;
+ }
+
+ uint offset = 0; // holds offset, where we read
+ int n;
+
+ // contains information gathered from the local file headers
+ TQAsciiDict<ParseFileInfo> pfi_map(1009, true /*case sensitive */, true /*copy keys*/);
+ pfi_map.setAutoDelete(true);
+
+ // We set a bool for knowing if we are allowed to skip the start of the file
+ bool startOfFile = true;
+
+ for (;;) // repeat until 'end of entries' signature is reached
+ {
+kdDebug(7040) << "loop starts" << endl;
+kdDebug(7040) << "dev->at() now : " << dev->at() << endl;
+ n = dev->readBlock( buffer, 4 );
+
+ if (n < 4)
+ {
+ kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#1)" << endl;
+
+ return false;
+ }
+
+ if ( !memcmp( buffer, "PK\5\6", 4 ) ) // 'end of entries'
+ {
+ kdDebug(7040) << "PK56 found end of archive" << endl;
+ startOfFile = false;
+ break;
+ }
+
+ if ( !memcmp( buffer, "PK\3\4", 4 ) ) // local file header
+ {
+ kdDebug(7040) << "PK34 found local file header" << endl;
+ startOfFile = false;
+ // can this fail ???
+ dev->at( dev->at() + 2 ); // skip 'version needed to extract'
+
+ // read static header stuff
+ n = dev->readBlock( buffer, 24 );
+ if (n < 24) {
+ kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#4)" << endl;
+ return false;
+ }
+
+ int gpf = (uchar)buffer[0]; // "general purpose flag" not "general protection fault" ;-)
+ int compression_mode = (uchar)buffer[2] | (uchar)buffer[3] << 8;
+ time_t mtime = transformFromMsDos( buffer+4 );
+
+ TQ_LONG compr_size = (uchar)buffer[12] | (uchar)buffer[13] << 8
+ | (uchar)buffer[14] << 16 | (uchar)buffer[15] << 24;
+ TQ_LONG uncomp_size = (uchar)buffer[16] | (uchar)buffer[17] << 8
+ | (uchar)buffer[18] << 16 | (uchar)buffer[19] << 24;
+ int namelen = (uchar)buffer[20] | (uchar)buffer[21] << 8;
+ int extralen = (uchar)buffer[22] | (uchar)buffer[23] << 8;
+
+ kdDebug(7040) << "general purpose bit flag: " << gpf << endl;
+ kdDebug(7040) << "compressed size: " << compr_size << endl;
+ kdDebug(7040) << "uncompressed size: " << uncomp_size << endl;
+ kdDebug(7040) << "namelen: " << namelen << endl;
+ kdDebug(7040) << "extralen: " << extralen << endl;
+ kdDebug(7040) << "archive size: " << dev->size() << endl;
+
+ // read filename
+ TQCString filename(namelen + 1);
+ n = dev->readBlock(filename.data(), namelen);
+ if ( n < namelen ) {
+ kdWarning(7040) << "Invalid ZIP file. Name not completely read (#2)" << endl;
+ return false;
+ }
+
+ ParseFileInfo *pfi = new ParseFileInfo();
+ pfi->mtime = mtime;
+ pfi_map.insert(filename.data(), pfi);
+
+ // read and parse the beginning of the extra field,
+ // skip rest of extra field in case it is too long
+ unsigned int extraFieldEnd = dev->at() + extralen;
+ pfi->extralen = extralen;
+ int handledextralen = QMIN(extralen, (int)sizeof buffer);
+
+ kdDebug(7040) << "handledextralen: " << handledextralen << endl;
+
+ n = dev->readBlock(buffer, handledextralen);
+ // no error msg necessary as we deliberately truncate the extra field
+ if (!parseExtraField(buffer, handledextralen, true, *pfi))
+ {
+ kdWarning(7040) << "Invalid ZIP File. Broken ExtraField." << endl;
+ return false;
+ }
+
+ // jump to end of extra field
+ dev->at( extraFieldEnd );
+
+ // we have to take care of the 'general purpose bit flag'.
+ // if bit 3 is set, the header doesn't contain the length of
+ // the file and we look for the signature 'PK\7\8'.
+ if ( gpf & 8 )
+ {
+ // here we have to read through the compressed data to find
+ // the next PKxx
+ kdDebug(7040) << "trying to seek for next PK78" << endl;
+ bool foundSignature = false;
+
+ while (!foundSignature)
+ {
+ n = dev->readBlock( buffer, 1 );
+ if (n < 1)
+ {
+ kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)" << endl;
+ return false;
+ }
+
+ if ( buffer[0] != 'P' )
+ continue;
+
+ n = dev->readBlock( buffer, 3 );
+ if (n < 3)
+ {
+ kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)" << endl;
+ return false;
+ }
+
+ // we have to detect three magic tokens here:
+ // PK34 for the next local header in case there is no data descriptor
+ // PK12 for the central header in case there is no data descriptor
+ // PK78 for the data descriptor in case it is following the compressed data
+
+ if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
+ {
+ foundSignature = true;
+ dev->at( dev->at() + 12 ); // skip the 'data_descriptor'
+ }
+ else if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 )
+ || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) )
+ {
+ foundSignature = true;
+ dev->at( dev->at() - 4 ); // go back 4 bytes, so that the magic bytes can be found...
+ }
+ else if ( buffer[0] == 'P' || buffer[1] == 'P' || buffer[2] == 'P' )
+ {
+ // We have another P character so we must go back a little to check if it is a magic
+ dev->at( dev->at() - 3 );
+ }
+
+ }
+ }
+ else
+ {
+ // here we skip the compressed data and jump to the next header
+ kdDebug(7040) << "general purpose bit flag indicates, that local file header contains valid size" << endl;
+ // check if this could be a symbolic link
+ if (compression_mode == NoCompression
+ && uncomp_size <= max_path_len
+ && uncomp_size > 0) {
+ // read content and store it
+ pfi->guessed_symlink.resize(uncomp_size + 1);
+ kdDebug(7040) << "guessed symlink size: " << uncomp_size << endl;
+ n = dev->readBlock(pfi->guessed_symlink.data(), uncomp_size);
+ if (n < uncomp_size) {
+ kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#5)" << endl;
+ return false;
+ }
+ } else {
+
+ if ( compr_size > (TQ_LONG)dev->size() )
+ {
+ // here we cannot trust the compressed size, so scan through the compressed
+ // data to find the next header
+ bool foundSignature = false;
+
+ while (!foundSignature)
+ {
+ n = dev->readBlock( buffer, 1 );
+ if (n < 1)
+ {
+ kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#2)" << endl;
+ return false;
+ }
+
+ if ( buffer[0] != 'P' )
+ continue;
+
+ n = dev->readBlock( buffer, 3 );
+ if (n < 3)
+ {
+ kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. (#3)" << endl;
+ return false;
+ }
+
+ // we have to detect three magic tokens here:
+ // PK34 for the next local header in case there is no data descriptor
+ // PK12 for the central header in case there is no data descriptor
+ // PK78 for the data descriptor in case it is following the compressed data
+
+ if ( buffer[0] == 'K' && buffer[1] == 7 && buffer[2] == 8 )
+ {
+ foundSignature = true;
+ dev->at( dev->at() + 12 ); // skip the 'data_descriptor'
+ }
+
+ if ( ( buffer[0] == 'K' && buffer[1] == 1 && buffer[2] == 2 )
+ || ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 ) )
+ {
+ foundSignature = true;
+ dev->at( dev->at() - 4 );
+ // go back 4 bytes, so that the magic bytes can be found
+ // in the next cycle...
+ }
+ }
+ }
+ else
+ {
+// kdDebug(7040) << "before interesting dev->at(): " << dev->at() << endl;
+ bool success;
+ success = dev->at( dev->at() + compr_size ); // can this fail ???
+/* kdDebug(7040) << "after interesting dev->at(): " << dev->at() << endl;
+ if ( success )
+ kdDebug(7040) << "dev->at was successful... " << endl;
+ else
+ kdDebug(7040) << "dev->at failed... " << endl;*/
+ }
+
+ }
+
+// not needed any more
+/* // here we calculate the length of the file in the zip
+ // with headers and jump to the next header.
+ uint skip = compr_size + namelen + extralen;
+ offset += 30 + skip;*/
+ }
+ }
+ else if ( !memcmp( buffer, "PK\1\2", 4 ) ) // central block
+ {
+ kdDebug(7040) << "PK12 found central block" << endl;
+ startOfFile = false;
+
+ // so we reached the central header at the end of the zip file
+ // here we get all interesting data out of the central header
+ // of a file
+ offset = dev->at() - 4;
+
+ //set offset for appending new files
+ if ( d->m_offset == 0L ) d->m_offset = offset;
+
+ n = dev->readBlock( buffer + 4, 42 );
+ if (n < 42) {
+ kdWarning(7040) << "Invalid ZIP file, central entry too short" << endl; // not long enough for valid entry
+ return false;
+ }
+
+ //int gpf = (uchar)buffer[9] << 8 | (uchar)buffer[10];
+ //kdDebug() << "general purpose flag=" << gpf << endl;
+ // length of the filename (well, pathname indeed)
+ int namelen = (uchar)buffer[29] << 8 | (uchar)buffer[28];
+ TQCString bufferName( namelen + 1 );
+ n = dev->readBlock( bufferName.data(), namelen );
+ if ( n < namelen )
+ kdWarning(7040) << "Invalid ZIP file. Name not completely read" << endl;
+
+ ParseFileInfo *pfi = pfi_map[bufferName];
+ if (!pfi) { // can that happen?
+ pfi_map.insert(bufferName.data(), pfi = new ParseFileInfo());
+ }
+ TQString name( TQFile::decodeName(bufferName) );
+
+ //kdDebug(7040) << "name: " << name << endl;
+ // only in central header ! see below.
+ // length of extra attributes
+ int extralen = (uchar)buffer[31] << 8 | (uchar)buffer[30];
+ // length of comment for this file
+ int commlen = (uchar)buffer[33] << 8 | (uchar)buffer[32];
+ // compression method of this file
+ int cmethod = (uchar)buffer[11] << 8 | (uchar)buffer[10];
+
+ //kdDebug(7040) << "cmethod: " << cmethod << endl;
+ //kdDebug(7040) << "extralen: " << extralen << endl;
+
+ // uncompressed file size
+ uint ucsize = (uchar)buffer[27] << 24 | (uchar)buffer[26] << 16 |
+ (uchar)buffer[25] << 8 | (uchar)buffer[24];
+ // compressed file size
+ uint csize = (uchar)buffer[23] << 24 | (uchar)buffer[22] << 16 |
+ (uchar)buffer[21] << 8 | (uchar)buffer[20];
+
+ // offset of local header
+ uint localheaderoffset = (uchar)buffer[45] << 24 | (uchar)buffer[44] << 16 |
+ (uchar)buffer[43] << 8 | (uchar)buffer[42];
+
+ // some clever people use different extra field lengths
+ // in the central header and in the local header... funny.
+ // so we need to get the localextralen to calculate the offset
+ // from localheaderstart to dataoffset
+ int localextralen = pfi->extralen; // FIXME: this will not work if
+ // no local header exists
+
+ //kdDebug(7040) << "localextralen: " << localextralen << endl;
+
+ // offset, where the real data for uncompression starts
+ uint dataoffset = localheaderoffset + 30 + localextralen + namelen; //comment only in central header
+
+ //kdDebug(7040) << "esize: " << esize << endl;
+ //kdDebug(7040) << "eoffset: " << eoffset << endl;
+ //kdDebug(7040) << "csize: " << csize << endl;
+
+ int os_madeby = (uchar)buffer[5];
+ bool isdir = false;
+ int access = 0100644;
+
+ if (os_madeby == 3) { // good ole unix
+ access = (uchar)buffer[40] | (uchar)buffer[41] << 8;
+ }
+
+ TQString entryName;
+
+ if ( name.endsWith( "/" ) ) // Entries with a trailing slash are directories
+ {
+ isdir = true;
+ name = name.left( name.length() - 1 );
+ if (os_madeby != 3) access = S_IFDIR | 0755;
+ else Q_ASSERT(access & S_IFDIR);
+ }
+
+ int pos = name.findRev( '/' );
+ if ( pos == -1 )
+ entryName = name;
+ else
+ entryName = name.mid( pos + 1 );
+ Q_ASSERT( !entryName.isEmpty() );
+
+ KArchiveEntry* entry;
+ if ( isdir )
+ {
+ TQString path = TQDir::cleanDirPath( name );
+ KArchiveEntry* ent = rootDir()->entry( path );
+ if ( ent && ent->isDirectory() )
+ {
+ //kdDebug(7040) << "Directory already exists, NOT going to add it again" << endl;
+ entry = 0L;
+ }
+ else
+ {
+ entry = new KArchiveDirectory( this, entryName, access, (int)pfi->mtime, rootDir()->user(), rootDir()->group(), TQString::null );
+ //kdDebug(7040) << "KArchiveDirectory created, entryName= " << entryName << ", name=" << name << endl;
+ }
+ }
+ else
+ {
+ TQString symlink;
+ if (S_ISLNK(access)) {
+ symlink = TQFile::decodeName(pfi->guessed_symlink);
+ }
+ entry = new KZipFileEntry( this, entryName, access, pfi->mtime,
+ rootDir()->user(), rootDir()->group(),
+ symlink, name, dataoffset,
+ ucsize, cmethod, csize );
+ static_cast<KZipFileEntry *>(entry)->setHeaderStart( localheaderoffset );
+ //kdDebug(7040) << "KZipFileEntry created, entryName= " << entryName << ", name=" << name << endl;
+ d->m_fileList.append( static_cast<KZipFileEntry *>( entry ) );
+ }
+
+ if ( entry )
+ {
+ if ( pos == -1 )
+ {
+ rootDir()->addEntry(entry);
+ }
+ else
+ {
+ // In some tar files we can find dir/./file => call cleanDirPath
+ TQString path = TQDir::cleanDirPath( name.left( pos ) );
+ // Ensure container directory exists, create otherwise
+ KArchiveDirectory * tdir = findOrCreate( path );
+ tdir->addEntry(entry);
+ }
+ }
+
+ //calculate offset to next entry
+ offset += 46 + commlen + extralen + namelen;
+ bool b = dev->at(offset);
+ Q_ASSERT( b );
+ if ( !b )
+ return false;
+ }
+ else if ( startOfFile )
+ {
+ // The file does not start with any ZIP header (e.g. self-extractable ZIP files)
+ // Therefore we need to find the first PK\003\004 (local header)
+ kdDebug(7040) << "Try to skip start of file" << endl;
+ startOfFile = false;
+ bool foundSignature = false;
+
+ while (!foundSignature)
+ {
+ n = dev->readBlock( buffer, 1 );
+ if (n < 1)
+ {
+ kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. " << k_funcinfo << endl;
+ return false;
+ }
+
+ if ( buffer[0] != 'P' )
+ continue;
+
+ n = dev->readBlock( buffer, 3 );
+ if (n < 3)
+ {
+ kdWarning(7040) << "Invalid ZIP file. Unexpected end of file. " << k_funcinfo << endl;
+ return false;
+ }
+
+ // We have to detect the magic token for a local header: PK\003\004
+ /*
+ * Note: we do not need to check the other magics, if the ZIP file has no
+ * local header, then it has not any files!
+ */
+ if ( buffer[0] == 'K' && buffer[1] == 3 && buffer[2] == 4 )
+ {
+ foundSignature = true;
+ dev->at( dev->at() - 4 ); // go back 4 bytes, so that the magic bytes can be found...
+ }
+ else if ( buffer[0] == 'P' || buffer[1] == 'P' || buffer[2] == 'P' )
+ {
+ // We have another P character so we must go back a little to check if it is a magic
+ dev->at( dev->at() - 3 );
+ }
+ }
+ }
+ else
+ {
+ kdWarning(7040) << "Invalid ZIP file. Unrecognized header at offset " << offset << endl;
+
+ return false;
+ }
+ }
+ //kdDebug(7040) << "*** done *** " << endl;
+ return true;
+}
+
+bool KZip::closeArchive()
+{
+ if ( ! ( mode() & IO_WriteOnly ) )
+ {
+ //kdDebug(7040) << "closearchive readonly reached." << endl;
+ return true;
+ }
+
+ kdDebug() << k_funcinfo << "device=" << device() << endl;
+ //ReadWrite or WriteOnly
+ //write all central dir file entries
+
+ if ( !device() ) // saving aborted
+ return false;
+
+ // to be written at the end of the file...
+ char buffer[ 22 ]; // first used for 12, then for 22 at the end
+ uLong crc = crc32(0L, Z_NULL, 0);
+
+ TQ_LONG centraldiroffset = device()->at();
+ //kdDebug(7040) << "closearchive: centraldiroffset: " << centraldiroffset << endl;
+ TQ_LONG atbackup = centraldiroffset;
+ TQPtrListIterator<KZipFileEntry> it( d->m_fileList );
+
+ for ( ; it.current() ; ++it )
+ { //set crc and compressed size in each local file header
+ if ( !device()->at( it.current()->headerStart() + 14 ) )
+ return false;
+ //kdDebug(7040) << "closearchive setcrcandcsize: filename: "
+ // << it.current()->path()
+ // << " encoding: "<< it.current()->encoding() << endl;
+
+ uLong mycrc = it.current()->crc32();
+ buffer[0] = char(mycrc); // crc checksum, at headerStart+14
+ buffer[1] = char(mycrc >> 8);
+ buffer[2] = char(mycrc >> 16);
+ buffer[3] = char(mycrc >> 24);
+
+ int mysize1 = it.current()->compressedSize();
+ buffer[4] = char(mysize1); // compressed file size, at headerStart+18
+ buffer[5] = char(mysize1 >> 8);
+ buffer[6] = char(mysize1 >> 16);
+ buffer[7] = char(mysize1 >> 24);
+
+ int myusize = it.current()->size();
+ buffer[8] = char(myusize); // uncompressed file size, at headerStart+22
+ buffer[9] = char(myusize >> 8);
+ buffer[10] = char(myusize >> 16);
+ buffer[11] = char(myusize >> 24);
+
+ if ( device()->writeBlock( buffer, 12 ) != 12 )
+ return false;
+ }
+ device()->at( atbackup );
+
+ for ( it.toFirst(); it.current() ; ++it )
+ {
+ //kdDebug(7040) << "closearchive: filename: " << it.current()->path()
+ // << " encoding: "<< it.current()->encoding() << endl;
+
+ TQCString path = TQFile::encodeName(it.current()->path());
+
+ const int extra_field_len = 9;
+ int bufferSize = extra_field_len + path.length() + 46;
+ char* buffer = new char[ bufferSize ];
+
+ memset(buffer, 0, 46); // zero is a nice default for most header fields
+
+ const char head[] =
+ {
+ 'P', 'K', 1, 2, // central file header signature
+ 0x14, 3, // version made by (3 == UNIX)
+ 0x14, 0 // version needed to extract
+ };
+
+ // I do not know why memcpy is not working here
+ //memcpy(buffer, head, sizeof(head));
+ tqmemmove(buffer, head, sizeof(head));
+
+ buffer[ 10 ] = char(it.current()->encoding()); // compression method
+ buffer[ 11 ] = char(it.current()->encoding() >> 8);
+
+ transformToMsDos( it.current()->datetime(), &buffer[ 12 ] );
+
+ uLong mycrc = it.current()->crc32();
+ buffer[ 16 ] = char(mycrc); // crc checksum
+ buffer[ 17 ] = char(mycrc >> 8);
+ buffer[ 18 ] = char(mycrc >> 16);
+ buffer[ 19 ] = char(mycrc >> 24);
+
+ int mysize1 = it.current()->compressedSize();
+ buffer[ 20 ] = char(mysize1); // compressed file size
+ buffer[ 21 ] = char(mysize1 >> 8);
+ buffer[ 22 ] = char(mysize1 >> 16);
+ buffer[ 23 ] = char(mysize1 >> 24);
+
+ int mysize = it.current()->size();
+ buffer[ 24 ] = char(mysize); // uncompressed file size
+ buffer[ 25 ] = char(mysize >> 8);
+ buffer[ 26 ] = char(mysize >> 16);
+ buffer[ 27 ] = char(mysize >> 24);
+
+ buffer[ 28 ] = char(it.current()->path().length()); // filename length
+ buffer[ 29 ] = char(it.current()->path().length() >> 8);
+
+ buffer[ 30 ] = char(extra_field_len);
+ buffer[ 31 ] = char(extra_field_len >> 8);
+
+ buffer[ 40 ] = char(it.current()->permissions());
+ buffer[ 41 ] = char(it.current()->permissions() >> 8);
+
+ int myhst = it.current()->headerStart();
+ buffer[ 42 ] = char(myhst); //relative offset of local header
+ buffer[ 43 ] = char(myhst >> 8);
+ buffer[ 44 ] = char(myhst >> 16);
+ buffer[ 45 ] = char(myhst >> 24);
+
+ // file name
+ strncpy( buffer + 46, path, path.length() );
+ //kdDebug(7040) << "closearchive length to write: " << bufferSize << endl;
+
+ // extra field
+ char *extfield = buffer + 46 + path.length();
+ extfield[0] = 'U';
+ extfield[1] = 'T';
+ extfield[2] = 5;
+ extfield[3] = 0;
+ extfield[4] = 1 | 2 | 4; // specify flags from local field
+ // (unless I misread the spec)
+ // provide only modification time
+ unsigned long time = (unsigned long)it.current()->date();
+ extfield[5] = char(time);
+ extfield[6] = char(time >> 8);
+ extfield[7] = char(time >> 16);
+ extfield[8] = char(time >> 24);
+
+ crc = crc32(crc, (Bytef *)buffer, bufferSize );
+ bool ok = ( device()->writeBlock( buffer, bufferSize ) == bufferSize );
+ delete[] buffer;
+ if ( !ok )
+ return false;
+ }
+ TQ_LONG centraldirendoffset = device()->at();
+ //kdDebug(7040) << "closearchive: centraldirendoffset: " << centraldirendoffset << endl;
+ //kdDebug(7040) << "closearchive: device()->at(): " << device()->at() << endl;
+
+ //write end of central dir record.
+ buffer[ 0 ] = 'P'; //end of central dir signature
+ buffer[ 1 ] = 'K';
+ buffer[ 2 ] = 5;
+ buffer[ 3 ] = 6;
+
+ buffer[ 4 ] = 0; // number of this disk
+ buffer[ 5 ] = 0;
+
+ buffer[ 6 ] = 0; // number of disk with start of central dir
+ buffer[ 7 ] = 0;
+
+ int count = d->m_fileList.count();
+ //kdDebug(7040) << "number of files (count): " << count << endl;
+
+
+ buffer[ 8 ] = char(count); // total number of entries in central dir of
+ buffer[ 9 ] = char(count >> 8); // this disk
+
+ buffer[ 10 ] = buffer[ 8 ]; // total number of entries in the central dir
+ buffer[ 11 ] = buffer[ 9 ];
+
+ int cdsize = centraldirendoffset - centraldiroffset;
+ buffer[ 12 ] = char(cdsize); // size of the central dir
+ buffer[ 13 ] = char(cdsize >> 8);
+ buffer[ 14 ] = char(cdsize >> 16);
+ buffer[ 15 ] = char(cdsize >> 24);
+
+ //kdDebug(7040) << "end : centraldiroffset: " << centraldiroffset << endl;
+ //kdDebug(7040) << "end : centraldirsize: " << cdsize << endl;
+
+ buffer[ 16 ] = char(centraldiroffset); // central dir offset
+ buffer[ 17 ] = char(centraldiroffset >> 8);
+ buffer[ 18 ] = char(centraldiroffset >> 16);
+ buffer[ 19 ] = char(centraldiroffset >> 24);
+
+ buffer[ 20 ] = 0; //zipfile comment length
+ buffer[ 21 ] = 0;
+
+ if ( device()->writeBlock( buffer, 22 ) != 22 )
+ return false;
+
+ if ( d->m_saveFile ) {
+ d->m_saveFile->close();
+ setDevice( 0 );
+ delete d->m_saveFile;
+ d->m_saveFile = 0;
+ }
+
+ //kdDebug(7040) << __FILE__" reached." << endl;
+ return true;
+}
+
+// Doesn't need to be reimplemented anymore. Remove for KDE-4.0
+bool KZip::writeFile( const TQString& name, const TQString& user, const TQString& group, uint size, const char* data )
+{
+ mode_t mode = 0100644;
+ time_t the_time = time(0);
+ return KArchive::writeFile( name, user, group, size, mode, the_time,
+ the_time, the_time, data );
+}
+
+// Doesn't need to be reimplemented anymore. Remove for KDE-4.0
+bool KZip::writeFile( const TQString& name, const TQString& user,
+ const TQString& group, uint size, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime,
+ const char* data ) {
+ return KArchive::writeFile(name, user, group, size, perm, atime, mtime,
+ ctime, data);
+}
+
+// Doesn't need to be reimplemented anymore. Remove for KDE-4.0
+bool KZip::prepareWriting( const TQString& name, const TQString& user, const TQString& group, uint size )
+{
+ mode_t dflt_perm = 0100644;
+ time_t the_time = time(0);
+ return prepareWriting(name,user,group,size,dflt_perm,
+ the_time,the_time,the_time);
+}
+
+// Doesn't need to be reimplemented anymore. Remove for KDE-4.0
+bool KZip::prepareWriting(const TQString& name, const TQString& user,
+ const TQString& group, uint size, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime) {
+ return KArchive::prepareWriting(name,user,group,size,perm,atime,mtime,ctime);
+}
+
+bool KZip::prepareWriting_impl(const TQString &name, const TQString &user,
+ const TQString &group, uint /*size*/, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime) {
+ //kdDebug(7040) << "prepareWriting reached." << endl;
+ if ( !isOpened() )
+ {
+ tqWarning( "KZip::writeFile: You must open the zip file before writing to it\n");
+ return false;
+ }
+
+ if ( ! ( mode() & IO_WriteOnly ) ) // accept WriteOnly and ReadWrite
+ {
+ tqWarning( "KZip::writeFile: You must open the zip file for writing\n");
+ return false;
+ }
+
+ if ( !device() ) { // aborted
+ //kdWarning(7040) << "prepareWriting_impl: no device" << endl;
+ return false;
+ }
+
+ // set right offset in zip.
+ if ( !device()->at( d->m_offset ) ) {
+ kdWarning(7040) << "prepareWriting_impl: cannot seek in ZIP file. Disk full?" << endl;
+ abort();
+ return false;
+ }
+
+ // delete entries in the filelist with the same filename as the one we want
+ // to save, so that we don�t have duplicate file entries when viewing the zip
+ // with konqi...
+ // CAUTION: the old file itself is still in the zip and won't be removed !!!
+ TQPtrListIterator<KZipFileEntry> it( d->m_fileList );
+
+ //kdDebug(7040) << "filename to write: " << name <<endl;
+ for ( ; it.current() ; ++it )
+ {
+ //kdDebug(7040) << "prepfilename: " << it.current()->path() <<endl;
+ if (name == it.current()->path() )
+ {
+ //kdDebug(7040) << "removing following entry: " << it.current()->path() <<endl;
+ d->m_fileList.remove();
+ }
+
+ }
+ // Find or create parent dir
+ KArchiveDirectory* parentDir = rootDir();
+ TQString fileName( name );
+ int i = name.findRev( '/' );
+ if ( i != -1 )
+ {
+ TQString dir = name.left( i );
+ fileName = name.mid( i + 1 );
+ //kdDebug(7040) << "KZip::prepareWriting ensuring " << dir << " exists. fileName=" << fileName << endl;
+ parentDir = findOrCreate( dir );
+ }
+
+ // construct a KZipFileEntry and add it to list
+ KZipFileEntry * e = new KZipFileEntry( this, fileName, perm, mtime, user, group, TQString::null,
+ name, device()->at() + 30 + name.length(), // start
+ 0 /*size unknown yet*/, d->m_compression, 0 /*csize unknown yet*/ );
+ e->setHeaderStart( device()->at() );
+ //kdDebug(7040) << "wrote file start: " << e->position() << " name: " << name << endl;
+ parentDir->addEntry( e );
+
+ d->m_currentFile = e;
+ d->m_fileList.append( e );
+
+ int extra_field_len = 0;
+ if ( d->m_extraField == ModificationTime )
+ extra_field_len = 17; // value also used in doneWriting()
+
+ // write out zip header
+ TQCString encodedName = TQFile::encodeName(name);
+ int bufferSize = extra_field_len + encodedName.length() + 30;
+ //kdDebug(7040) << "KZip::prepareWriting bufferSize=" << bufferSize << endl;
+ char* buffer = new char[ bufferSize ];
+
+ buffer[ 0 ] = 'P'; //local file header signature
+ buffer[ 1 ] = 'K';
+ buffer[ 2 ] = 3;
+ buffer[ 3 ] = 4;
+
+ buffer[ 4 ] = 0x14; // version needed to extract
+ buffer[ 5 ] = 0;
+
+ buffer[ 6 ] = 0; // general purpose bit flag
+ buffer[ 7 ] = 0;
+
+ buffer[ 8 ] = char(e->encoding()); // compression method
+ buffer[ 9 ] = char(e->encoding() >> 8);
+
+ transformToMsDos( e->datetime(), &buffer[ 10 ] );
+
+ buffer[ 14 ] = 'C'; //dummy crc
+ buffer[ 15 ] = 'R';
+ buffer[ 16 ] = 'C';
+ buffer[ 17 ] = 'q';
+
+ buffer[ 18 ] = 'C'; //compressed file size
+ buffer[ 19 ] = 'S';
+ buffer[ 20 ] = 'I';
+ buffer[ 21 ] = 'Z';
+
+ buffer[ 22 ] = 'U'; //uncompressed file size
+ buffer[ 23 ] = 'S';
+ buffer[ 24 ] = 'I';
+ buffer[ 25 ] = 'Z';
+
+ buffer[ 26 ] = (uchar)(encodedName.length()); //filename length
+ buffer[ 27 ] = (uchar)(encodedName.length() >> 8);
+
+ buffer[ 28 ] = (uchar)(extra_field_len); // extra field length
+ buffer[ 29 ] = (uchar)(extra_field_len >> 8);
+
+ // file name
+ strncpy( buffer + 30, encodedName, encodedName.length() );
+
+ // extra field
+ if ( d->m_extraField == ModificationTime )
+ {
+ char *extfield = buffer + 30 + encodedName.length();
+ // "Extended timestamp" header (0x5455)
+ extfield[0] = 'U';
+ extfield[1] = 'T';
+ extfield[2] = 13; // data size
+ extfield[3] = 0;
+ extfield[4] = 1 | 2 | 4; // contains mtime, atime, ctime
+
+ extfield[5] = char(mtime);
+ extfield[6] = char(mtime >> 8);
+ extfield[7] = char(mtime >> 16);
+ extfield[8] = char(mtime >> 24);
+
+ extfield[9] = char(atime);
+ extfield[10] = char(atime >> 8);
+ extfield[11] = char(atime >> 16);
+ extfield[12] = char(atime >> 24);
+
+ extfield[13] = char(ctime);
+ extfield[14] = char(ctime >> 8);
+ extfield[15] = char(ctime >> 16);
+ extfield[16] = char(ctime >> 24);
+ }
+
+ // Write header
+ bool b = (device()->writeBlock( buffer, bufferSize ) == bufferSize );
+ d->m_crc = 0L;
+ delete[] buffer;
+
+ Q_ASSERT( b );
+ if (!b) {
+ abort();
+ return false;
+ }
+
+ // Prepare device for writing the data
+ // Either device() if no compression, or a KFilterDev to compress
+ if ( d->m_compression == 0 ) {
+ d->m_currentDev = device();
+ return true;
+ }
+
+ d->m_currentDev = KFilterDev::device( device(), "application/x-gzip", false );
+ Q_ASSERT( d->m_currentDev );
+ if ( !d->m_currentDev ) {
+ abort();
+ return false; // ouch
+ }
+ static_cast<KFilterDev *>(d->m_currentDev)->setSkipHeaders(); // Just zlib, not gzip
+
+ b = d->m_currentDev->open( IO_WriteOnly );
+ Q_ASSERT( b );
+ return b;
+}
+
+bool KZip::doneWriting( uint size )
+{
+ if ( d->m_currentFile->encoding() == 8 ) {
+ // Finish
+ (void)d->m_currentDev->writeBlock( 0, 0 );
+ delete d->m_currentDev;
+ }
+ // If 0, d->m_currentDev was device() - don't delete ;)
+ d->m_currentDev = 0L;
+
+ Q_ASSERT( d->m_currentFile );
+ //kdDebug(7040) << "donewriting reached." << endl;
+ //kdDebug(7040) << "filename: " << d->m_currentFile->path() << endl;
+ //kdDebug(7040) << "getpos (at): " << device()->at() << endl;
+ d->m_currentFile->setSize(size);
+ int extra_field_len = 0;
+ if ( d->m_extraField == ModificationTime )
+ extra_field_len = 17; // value also used in doneWriting()
+
+ int csize = device()->at() -
+ d->m_currentFile->headerStart() - 30 -
+ d->m_currentFile->path().length() - extra_field_len;
+ d->m_currentFile->setCompressedSize(csize);
+ //kdDebug(7040) << "usize: " << d->m_currentFile->size() << endl;
+ //kdDebug(7040) << "csize: " << d->m_currentFile->compressedSize() << endl;
+ //kdDebug(7040) << "headerstart: " << d->m_currentFile->headerStart() << endl;
+
+ //kdDebug(7040) << "crc: " << d->m_crc << endl;
+ d->m_currentFile->setCRC32( d->m_crc );
+
+ d->m_currentFile = 0L;
+
+ // update saved offset for appending new files
+ d->m_offset = device()->at();
+ return true;
+}
+
+bool KZip::writeSymLink(const TQString &name, const TQString &target,
+ const TQString &user, const TQString &group,
+ mode_t perm, time_t atime, time_t mtime, time_t ctime) {
+ return KArchive::writeSymLink(name,target,user,group,perm,atime,mtime,ctime);
+}
+
+bool KZip::writeSymLink_impl(const TQString &name, const TQString &target,
+ const TQString &user, const TQString &group,
+ mode_t perm, time_t atime, time_t mtime, time_t ctime) {
+
+ // reassure that symlink flag is set, otherwise strange things happen on
+ // extraction
+ perm |= S_IFLNK;
+ Compression c = compression();
+ setCompression(NoCompression); // link targets are never compressed
+
+ if (!prepareWriting(name, user, group, 0, perm, atime, mtime, ctime)) {
+ kdWarning() << "KZip::writeFile prepareWriting failed" << endl;
+ setCompression(c);
+ return false;
+ }
+
+ TQCString symlink_target = TQFile::encodeName(target);
+ if (!writeData(symlink_target, symlink_target.length())) {
+ kdWarning() << "KZip::writeFile writeData failed" << endl;
+ setCompression(c);
+ return false;
+ }
+
+ if (!doneWriting(symlink_target.length())) {
+ kdWarning() << "KZip::writeFile doneWriting failed" << endl;
+ setCompression(c);
+ return false;
+ }
+
+ setCompression(c);
+ return true;
+}
+
+void KZip::virtual_hook( int id, void* data )
+{
+ switch (id) {
+ case VIRTUAL_WRITE_DATA: {
+ WriteDataParams* params = reinterpret_cast<WriteDataParams *>(data);
+ params->retval = writeData_impl( params->data, params->size );
+ break;
+ }
+ case VIRTUAL_WRITE_SYMLINK: {
+ WriteSymlinkParams *params = reinterpret_cast<WriteSymlinkParams *>(data);
+ params->retval = writeSymLink_impl(*params->name,*params->target,
+ *params->user,*params->group,params->perm,
+ params->atime,params->mtime,params->ctime);
+ break;
+ }
+ case VIRTUAL_PREPARE_WRITING: {
+ PrepareWritingParams *params = reinterpret_cast<PrepareWritingParams *>(data);
+ params->retval = prepareWriting_impl(*params->name,*params->user,
+ *params->group,params->size,params->perm,
+ params->atime,params->mtime,params->ctime);
+ break;
+ }
+ default:
+ KArchive::virtual_hook( id, data );
+ }/*end switch*/
+}
+
+// made virtual using virtual_hook
+bool KZip::writeData(const char * c, uint i)
+{
+ return KArchive::writeData( c, i );
+}
+
+bool KZip::writeData_impl(const char * c, uint i)
+{
+ Q_ASSERT( d->m_currentFile );
+ Q_ASSERT( d->m_currentDev );
+ if (!d->m_currentFile || !d->m_currentDev) {
+ abort();
+ return false;
+ }
+
+ // crc to be calculated over uncompressed stuff...
+ // and they didn't mention it in their docs...
+ d->m_crc = crc32(d->m_crc, (const Bytef *) c , i);
+
+ TQ_LONG written = d->m_currentDev->writeBlock( c, i );
+ //kdDebug(7040) << "KZip::writeData wrote " << i << " bytes." << endl;
+ bool ok = written == (TQ_LONG)i;
+ if ( !ok )
+ abort();
+ return ok;
+}
+
+void KZip::setCompression( Compression c )
+{
+ d->m_compression = ( c == NoCompression ) ? 0 : 8;
+}
+
+KZip::Compression KZip::compression() const
+{
+ return ( d->m_compression == 8 ) ? DeflateCompression : NoCompression;
+}
+
+void KZip::setExtraField( ExtraField ef )
+{
+ d->m_extraField = ef;
+}
+
+KZip::ExtraField KZip::extraField() const
+{
+ return d->m_extraField;
+}
+
+void KZip::abort()
+{
+ if ( d->m_saveFile ) {
+ d->m_saveFile->abort();
+ setDevice( 0 );
+ }
+}
+
+
+///////////////
+
+TQByteArray KZipFileEntry::data() const
+{
+ TQIODevice* dev = device();
+ TQByteArray arr;
+ if ( dev ) {
+ arr = dev->readAll();
+ delete dev;
+ }
+ return arr;
+}
+
+TQIODevice* KZipFileEntry::device() const
+{
+ //kdDebug(7040) << "KZipFileEntry::device creating iodevice limited to pos=" << position() << ", csize=" << compressedSize() << endl;
+ // Limit the reading to the appropriate part of the underlying device (e.g. file)
+ KLimitedIODevice* limitedDev = new KLimitedIODevice( archive()->device(), position(), compressedSize() );
+ if ( encoding() == 0 || compressedSize() == 0 ) // no compression (or even no data)
+ return limitedDev;
+
+ if ( encoding() == 8 )
+ {
+ // On top of that, create a device that uncompresses the zlib data
+ TQIODevice* filterDev = KFilterDev::device( limitedDev, "application/x-gzip" );
+ if ( !filterDev )
+ return 0L; // ouch
+ static_cast<KFilterDev *>(filterDev)->setSkipHeaders(); // Just zlib, not gzip
+ bool b = filterDev->open( IO_ReadOnly );
+ Q_ASSERT( b );
+ return filterDev;
+ }
+
+ kdError() << "This zip file contains files compressed with method "
+ << encoding() <<", this method is currently not supported by KZip,"
+ <<" please use a command-line tool to handle this file." << endl;
+ return 0L;
+}
diff --git a/tdeio/tdeio/kzip.h b/tdeio/tdeio/kzip.h
new file mode 100644
index 000000000..333736e21
--- /dev/null
+++ b/tdeio/tdeio/kzip.h
@@ -0,0 +1,284 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 Holger Schroeder <holger-kde@holgis.net>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __kzip_h
+#define __kzip_h
+
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <tqdatetime.h>
+#include <tqstring.h>
+#include <tqstringlist.h>
+#include <tqdict.h>
+#include <tqvaluelist.h>
+#include <karchive.h>
+
+class KZipFileEntry;
+/**
+ * This class implements a tdeioslave to access zip files from KDE.
+ * You can use it in IO_ReadOnly or in IO_WriteOnly mode, and it
+ * behaves just as expected.
+ * It can also be used in IO_ReadWrite mode, in this case one can
+ * append files to an existing zip archive. When you append new files, which
+ * are not yet in the zip, it works as expected, i.e. the files are appended at the end.
+ * When you append a file, which is already in the file, the reference to the
+ * old file is dropped and the new one is added to the zip - but the
+ * old data from the file itself is not deleted, it is still in the
+ * zipfile. so when you want to have a small and garbage-free zipfile,
+ * just read the contents of the appended zip file and write it to a new one
+ * in IO_WriteOnly mode. This is especially important when you don't want
+ * to leak information of how intermediate versions of files in the zip
+ * were looking.
+ * For more information on the zip fileformat go to
+ * http://www.pkware.com/products/enterprise/white_papers/appnote.html
+ * @short A class for reading/writing zip archives.
+ * @author Holger Schroeder <holger-kde@holgis.net>
+ * @since 3.1
+ */
+class TDEIO_EXPORT KZip : public KArchive
+{
+public:
+ /**
+ * Creates an instance that operates on the given filename.
+ * using the compression filter associated to given mimetype.
+ *
+ * @param filename is a local path (e.g. "/home/holger/myfile.zip")
+ */
+ KZip( const TQString& filename );
+
+ /**
+ * Creates an instance that operates on the given device.
+ * The device can be compressed (KFilterDev) or not (TQFile, etc.).
+ * @warning Do not assume that giving a TQFile here will decompress the file,
+ * in case it's compressed!
+ * @param dev the device to access
+ */
+ KZip( TQIODevice * dev );
+
+ /**
+ * If the zip file is still opened, then it will be
+ * closed automatically by the destructor.
+ */
+ virtual ~KZip();
+
+ /**
+ * The name of the zip file, as passed to the constructor.
+ * Null if you used the TQIODevice constructor.
+ * @return the zip's file name, or null if a TQIODevice is used
+ */
+ TQString fileName() { return m_filename; }
+
+ /**
+ * Describes the contents of the "extra field" for a given file in the Zip archive.
+ */
+ enum ExtraField { NoExtraField = 0, ///< No extra field
+ ModificationTime = 1, ///< Modification time ("extended timestamp" header)
+ DefaultExtraField = 1
+ };
+
+ /**
+ * Call this before writeFile or prepareWriting, to define what the next
+ * file to be written should have in its extra field.
+ * @param ef the type of "extra field"
+ * @see extraField()
+ */
+ void setExtraField( ExtraField ef );
+
+ /**
+ * The current type of "extra field" that will be used for new files.
+ * @return the current type of "extra field"
+ * @see setExtraField()
+ */
+ ExtraField extraField() const;
+
+ /**
+ * Describes the compression type for a given file in the Zip archive.
+ */
+ enum Compression { NoCompression = 0, ///< Uncompressed.
+ DeflateCompression = 1 ///< Deflate compression method.
+ };
+
+
+ /**
+ * Call this before writeFile or prepareWriting, to define whether the next
+ * files to be written should be compressed or not.
+ * @param c the new compression mode
+ * @see compression()
+ */
+ void setCompression( Compression c );
+
+ /**
+ * The current compression mode that will be used for new files.
+ * @return the current compression mode
+ * @see setCompression()
+ */
+ Compression compression() const;
+
+ /**
+ * If an archive is opened for writing then you can add a new file
+ * using this function.
+ * This method takes the whole data at once.
+ * @param name can include subdirs e.g. path/to/the/file
+ * @param user the user owning the file
+ * @param group the group owning the file
+ * @param size the size of the file
+ * @param data a pointer to the data
+ * @return true if successful, false otherwise
+ */
+ virtual bool writeFile( const TQString& name, const TQString& user, const TQString& group, uint size, const char* data ); // BC: remove reimplementation for KDE-4.0
+
+ /**
+ * Alternative method for writing: call prepareWriting(), then feed the data
+ * in small chunks using writeData(), and call doneWriting() when done.
+ * @param name can include subdirs e.g. path/to/the/file
+ * @param user the user owning the file
+ * @param group the group owning the file
+ * @param size unused argument
+ * @return true if successful, false otherwise
+ */
+ virtual bool prepareWriting( const TQString& name, const TQString& user, const TQString& group, uint size );
+
+ // TODO(BIC) make virtual. For now it must be implemented by virtual_hook.
+ bool writeSymLink(const TQString &name, const TQString &target,
+ const TQString &user, const TQString &group,
+ mode_t perm, time_t atime, time_t mtime, time_t ctime);
+ // TODO(BIC) make virtual. For now it must be implemented by virtual_hook.
+ bool prepareWriting( const TQString& name, const TQString& user,
+ const TQString& group, uint size, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime );
+ // TODO(BIC) make virtual. For now it must be implemented by virtual_hook.
+ bool writeFile( const TQString& name, const TQString& user, const TQString& group,
+ uint size, mode_t perm, time_t atime, time_t mtime,
+ time_t ctime, const char* data );
+ /**
+ * Write data to a file that has been created using prepareWriting().
+ * @param data a pointer to the data
+ * @param size the size of the chunk
+ * @return true if successful, false otherwise
+ */
+ bool writeData( const char* data, uint size ); // TODO make virtual
+
+ /**
+ * Write data to a file that has been created using prepareWriting().
+ * @param size the size of the file
+ * @return true if successful, false otherwise
+ */
+ virtual bool doneWriting( uint size );
+
+protected:
+ /**
+ * Opens the archive for reading.
+ * Parses the directory listing of the archive
+ * and creates the KArchiveDirectory/KArchiveFile entries.
+ * @param mode the mode of the file
+ */
+ virtual bool openArchive( int mode );
+ /// Closes the archive
+ virtual bool closeArchive();
+
+ /**
+ * @internal Not needed for zip
+ */
+ virtual bool writeDir( const TQString& name, const TQString& user, const TQString& group) { Q_UNUSED(name); Q_UNUSED(user); Q_UNUSED(group); return true; }
+ // TODO(BIC) uncomment and make virtual for KDE 4.
+// bool writeDir( const TQString& name, const TQString& user, const TQString& group,
+// mode_t perm, time_t atime, time_t mtime, time_t ctime );
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+ /** @internal for virtual_hook */
+ // from KArchive
+ bool writeData_impl( const char* data, uint size );
+ bool prepareWriting_impl(const TQString& name, const TQString& user,
+ const TQString& group, uint size, mode_t perm,
+ time_t atime, time_t mtime, time_t ctime);
+ bool writeSymLink_impl(const TQString &name, const TQString &target,
+ const TQString &user, const TQString &group,
+ mode_t perm, time_t atime, time_t mtime, time_t ctime);
+private:
+ void abort();
+
+private:
+ TQString m_filename;
+ class KZipPrivate;
+ KZipPrivate * d;
+};
+
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT KZipFileEntry : public KArchiveFile
+{
+public:
+ /*KZipFileEntry() : st(-1)
+ {}*/
+ KZipFileEntry( KZip* zip, const TQString& name, int access, int date,
+ const TQString& user, const TQString& group, const TQString& symlink,
+ const TQString& path, TQ_LONG start, TQ_LONG uncompressedSize,
+ int encoding, TQ_LONG compressedSize) :
+ KArchiveFile( zip, name, access, date, user, group, symlink,
+ start, uncompressedSize ),
+ m_crc(0),
+ m_compressedSize(compressedSize),
+ m_headerStart(0),
+ m_encoding(encoding),
+ m_path( path )
+ {}
+ int encoding() const { return m_encoding; }
+ TQ_LONG compressedSize() const { return m_compressedSize; }
+
+ /// Only used when writing
+ void setCompressedSize(TQ_LONG compressedSize) { m_compressedSize = compressedSize; }
+
+ /// Header start: only used when writing
+ void setHeaderStart(TQ_LONG headerstart) { m_headerStart = headerstart; }
+ TQ_LONG headerStart() const {return m_headerStart; }
+
+ /// CRC: only used when writing
+ unsigned long crc32() const { return m_crc; }
+ void setCRC32(unsigned long crc32) { m_crc=crc32; }
+
+ /// Name with complete path - KArchiveFile::name() is the filename only (no path)
+ TQString path() const { return m_path; }
+
+ /**
+ * @return the content of this file.
+ * Call data() with care (only once per file), this data isn't cached.
+ */
+ virtual TQByteArray data() const;
+
+ /**
+ * This method returns a TQIODevice to read the file contents.
+ * This is obviously for reading only.
+ * Note that the ownership of the device is being transferred to the caller,
+ * who will have to delete it.
+ * The returned device auto-opens (in readonly mode), no need to open it.
+ */
+ TQIODevice* device() const; // WARNING, not virtual!
+
+private:
+ unsigned long m_crc;
+ TQ_LONG m_compressedSize;
+ TQ_LONG m_headerStart;
+ int m_encoding;
+ TQString m_path;
+ // KDE4: d pointer or at least some int for future extensions
+};
+
+#endif
diff --git a/tdeio/tdeio/lex.c b/tdeio/tdeio/lex.c
new file mode 100644
index 000000000..99848a2f3
--- /dev/null
+++ b/tdeio/tdeio/lex.c
@@ -0,0 +1,1759 @@
+#define yy_create_buffer kiotrader_create_buffer
+#define yy_delete_buffer kiotrader_delete_buffer
+#define yy_scan_buffer kiotrader_scan_buffer
+#define yy_scan_string kiotrader_scan_string
+#define yy_scan_bytes kiotrader_scan_bytes
+#define yy_flex_debug kiotrader_flex_debug
+#define yy_init_buffer kiotrader_init_buffer
+#define yy_flush_buffer kiotrader_flush_buffer
+#define yy_load_buffer_state kiotrader_load_buffer_state
+#define yy_switch_to_buffer kiotrader_switch_to_buffer
+#define yyin kiotraderin
+#define yyleng kiotraderleng
+#define yylex kiotraderlex
+#define yyout kiotraderout
+#define yyrestart kiotraderrestart
+#define yytext kiotradertext
+#define yywrap kiotraderwrap
+
+#line 20 "lex.c"
+/* A lexical scanner generated by flex */
+
+/* Scanner skeleton version:
+ * $Header$
+ */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+
+#include <stdio.h>
+#include <unistd.h>
+
+
+/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+
+
+#ifdef __cplusplus
+
+#include <stdlib.h>
+
+/* Use prototypes in function declarations. */
+#define YY_USE_PROTOS
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef __TURBOC__
+ #pragma warn -rch
+ #pragma warn -use
+#include <io.h>
+#include <stdlib.h>
+#define YY_USE_CONST
+#define YY_USE_PROTOS
+#endif
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#define YY_BUF_SIZE 16384
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+extern int yyleng;
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+/* The funky do-while in the following #define is used to turn the definition
+ * int a single C statement (which needs a semi-colon terminator). This
+ * avoids problems with code like:
+ *
+ * if ( condition_holds )
+ * yyless( 5 );
+ * else
+ * do_something_else();
+ *
+ * Prior to using the do-while the compiler would get upset at the
+ * "else" because it interpreted the "if" statement as being all
+ * done when it reached the ';' after the yyless() call.
+ */
+
+/* Return all but the first 'n' matched characters back to the input stream. */
+
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ *yy_cp = yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yytext_ptr )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+typedef unsigned int yy_size_t;
+
+
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+ };
+
+static YY_BUFFER_STATE yy_current_buffer = 0;
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ */
+#define YY_CURRENT_BUFFER yy_current_buffer
+
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+
+
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart YY_PROTO(( FILE *input_file ));
+
+void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer ));
+void yy_load_buffer_state YY_PROTO(( void ));
+YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size ));
+void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file ));
+void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer )
+
+YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size ));
+YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *yy_str ));
+YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len ));
+
+static void *yy_flex_alloc YY_PROTO(( yy_size_t ));
+static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t ));
+static void yy_flex_free YY_PROTO(( void * ));
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! yy_current_buffer ) \
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ yy_current_buffer->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! yy_current_buffer ) \
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ yy_current_buffer->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (yy_current_buffer->yy_at_bol)
+
+typedef unsigned char YY_CHAR;
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+typedef int yy_state_type;
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state YY_PROTO(( void ));
+static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));
+static int yy_get_next_buffer YY_PROTO(( void ));
+static void yy_fatal_error YY_PROTO(( yyconst char msg[] ));
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yytext_ptr = yy_bp; \
+ yyleng = (int) (yy_cp - yy_bp); \
+ yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 25
+#define YY_END_OF_BUFFER 26
+static yyconst short int yy_accept[63] =
+ { 0,
+ 0, 0, 26, 24, 23, 23, 24, 24, 14, 14,
+ 24, 19, 3, 14, 4, 22, 22, 22, 24, 22,
+ 22, 22, 22, 22, 22, 23, 2, 0, 17, 18,
+ 20, 0, 19, 5, 1, 6, 22, 22, 22, 0,
+ 22, 22, 10, 22, 22, 22, 9, 22, 22, 0,
+ 21, 8, 22, 12, 13, 7, 22, 15, 22, 16,
+ 11, 0
+ } ;
+
+static yyconst int yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 4, 1, 1, 1, 1, 1, 5, 6,
+ 7, 8, 9, 10, 11, 12, 13, 14, 14, 14,
+ 14, 14, 14, 14, 14, 14, 14, 1, 1, 15,
+ 16, 17, 1, 1, 18, 19, 19, 19, 20, 21,
+ 19, 19, 19, 19, 19, 22, 19, 19, 19, 19,
+ 19, 23, 24, 25, 26, 19, 19, 19, 19, 19,
+ 27, 1, 28, 1, 1, 1, 29, 19, 19, 30,
+
+ 31, 19, 19, 19, 32, 19, 19, 19, 33, 34,
+ 35, 19, 19, 36, 37, 38, 19, 19, 19, 39,
+ 19, 19, 1, 1, 1, 40, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst int yy_meta[41] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 2, 1, 1, 3, 1, 1, 1, 4, 4, 4,
+ 4, 4, 4, 4, 4, 4, 1, 2, 4, 4,
+ 4, 4, 4, 4, 4, 4, 4, 4, 4, 1
+ } ;
+
+static yyconst short int yy_base[67] =
+ { 0,
+ 0, 0, 95, 96, 39, 41, 78, 88, 96, 78,
+ 77, 33, 74, 73, 72, 0, 69, 63, 0, 51,
+ 45, 49, 17, 47, 45, 48, 96, 75, 96, 65,
+ 64, 63, 40, 96, 96, 96, 0, 54, 49, 46,
+ 43, 40, 0, 32, 36, 31, 0, 44, 47, 38,
+ 96, 0, 28, 0, 0, 0, 44, 0, 15, 0,
+ 0, 96, 54, 56, 44, 59
+ } ;
+
+static yyconst short int yy_def[67] =
+ { 0,
+ 62, 1, 62, 62, 62, 62, 62, 63, 62, 62,
+ 62, 62, 62, 62, 62, 64, 64, 64, 65, 64,
+ 64, 64, 64, 64, 64, 62, 62, 63, 62, 62,
+ 62, 62, 62, 62, 62, 62, 64, 64, 64, 66,
+ 64, 64, 64, 64, 64, 64, 64, 64, 64, 66,
+ 62, 64, 64, 64, 64, 64, 64, 64, 64, 64,
+ 64, 0, 62, 62, 62, 62
+ } ;
+
+static yyconst short int yy_nxt[137] =
+ { 0,
+ 4, 5, 6, 7, 8, 9, 9, 9, 9, 9,
+ 10, 11, 9, 12, 13, 14, 15, 16, 16, 16,
+ 17, 16, 16, 16, 18, 16, 19, 4, 20, 16,
+ 21, 22, 23, 24, 25, 16, 16, 16, 16, 9,
+ 26, 26, 26, 26, 32, 44, 33, 40, 45, 26,
+ 26, 32, 61, 33, 28, 28, 28, 28, 37, 37,
+ 50, 50, 50, 60, 59, 51, 58, 57, 56, 55,
+ 54, 53, 52, 51, 49, 48, 31, 31, 30, 29,
+ 47, 46, 43, 42, 41, 39, 38, 36, 35, 34,
+ 31, 30, 29, 27, 62, 3, 62, 62, 62, 62,
+
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62
+ } ;
+
+static yyconst short int yy_chk[137] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 5, 5, 6, 6, 12, 23, 12, 65, 23, 26,
+ 26, 33, 59, 33, 63, 63, 63, 63, 64, 64,
+ 66, 66, 66, 57, 53, 50, 49, 48, 46, 45,
+ 44, 42, 41, 40, 39, 38, 32, 31, 30, 28,
+ 25, 24, 22, 21, 20, 18, 17, 15, 14, 13,
+ 11, 10, 8, 7, 3, 62, 62, 62, 62, 62,
+
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62, 62, 62, 62, 62,
+ 62, 62, 62, 62, 62, 62
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+#line 1 "lex.l"
+#define INITIAL 0
+#line 2 "lex.l"
+#define yylval kiotraderlval
+#define yywrap kiotraderwrap
+
+#include "yacc.h"
+#include <string.h>
+#include <stdlib.h>
+#define YY_NO_UNPUT
+
+char* KTraderParse_putSymbol( char *_name );
+char *KTraderParse_putSymbolInBrackets( char *_name );
+char* KTraderParse_putString( char *_name );
+int yywrap();
+int kiotraderlex(void);
+void KTraderParse_initFlex( const char *_code );
+
+#line 447 "lex.c"
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap YY_PROTO(( void ));
+#else
+extern int yywrap YY_PROTO(( void ));
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+static void yyunput YY_PROTO(( int c, char *buf_ptr ));
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int ));
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen YY_PROTO(( yyconst char * ));
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+#endif
+
+#if YY_STACK_USED
+static int yy_start_stack_ptr = 0;
+static int yy_start_stack_depth = 0;
+static int *yy_start_stack = 0;
+#ifndef YY_NO_PUSH_STATE
+static void yy_push_state YY_PROTO(( int new_state ));
+#endif
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state YY_PROTO(( void ));
+#endif
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state YY_PROTO(( void ));
+#endif
+
+#else
+#define YY_NO_PUSH_STATE 1
+#define YY_NO_POP_STATE 1
+#define YY_NO_TOP_STATE 1
+#endif
+
+#ifdef YY_MALLOC_DECL
+YY_MALLOC_DECL
+#else
+#if __STDC__
+#ifndef __cplusplus
+#include <stdlib.h>
+#endif
+#else
+/* Just try to get by without declaring the routines. This will fail
+ * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int)
+ * or sizeof(void*) != sizeof(int).
+ */
+#endif
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( yy_current_buffer->yy_is_interactive ) \
+ { \
+ int c = '*', n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \
+ && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" );
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL int yylex YY_PROTO(( void ))
+#endif
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+YY_DECL
+ {
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 21 "lex.l"
+
+
+#line 601 "lex.c"
+
+ if ( yy_init )
+ {
+ yy_init = 0;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! yy_start )
+ yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! yy_current_buffer )
+ yy_current_buffer =
+ yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_load_buffer_state();
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yy_start;
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ yy_last_accepting_state = yy_current_state;
+ yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 63 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 96 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = yy_last_accepting_cpos;
+ yy_current_state = yy_last_accepting_state;
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+
+do_action: /* This label is used only to access EOF actions. */
+
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yy_hold_char;
+ yy_cp = yy_last_accepting_cpos;
+ yy_current_state = yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 23 "lex.l"
+{ return EQ; }
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 24 "lex.l"
+{ return NEQ; }
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 25 "lex.l"
+{ return LE; }
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 26 "lex.l"
+{ return GR; }
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 27 "lex.l"
+{ return LEQ; }
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 28 "lex.l"
+{ return GEQ; }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 29 "lex.l"
+{ return NOT; }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 30 "lex.l"
+{ return AND; }
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 31 "lex.l"
+{ return OR; }
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 32 "lex.l"
+{ return TOKEN_IN; }
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 33 "lex.l"
+{ return EXIST; }
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 34 "lex.l"
+{ return MAX; }
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 35 "lex.l"
+{ return MIN; }
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 37 "lex.l"
+{ yylval.name = 0L; return (int)(*yytext); }
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 39 "lex.l"
+{ yylval.valb = 1; return VAL_BOOL; }
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 40 "lex.l"
+{ yylval.valb = 0; return VAL_BOOL; }
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 42 "lex.l"
+{ yylval.name = KTraderParse_putString( yytext ); return VAL_STRING; }
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 44 "lex.l"
+{ yylval.vali = atoi( yytext ); return VAL_NUM; }
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 45 "lex.l"
+{ yylval.vali = atoi( yytext ); return VAL_NUM; }
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 47 "lex.l"
+{ yylval.vald = atof( yytext ); return VAL_FLOAT; }
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 49 "lex.l"
+{ yylval.name = KTraderParse_putSymbolInBrackets( yytext ); return VAL_ID; }
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 51 "lex.l"
+{ yylval.name = KTraderParse_putSymbol( yytext ); return VAL_ID; }
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 53 "lex.l"
+/* eat up whitespace */
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 55 "lex.l"
+{ printf( "Unrecognized character: %s\n", yytext ); }
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 57 "lex.l"
+ECHO;
+ YY_BREAK
+#line 809 "lex.c"
+case YY_STATE_EOF(INITIAL):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between yy_current_buffer and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yy_n_chars = yy_current_buffer->yy_n_chars;
+ yy_current_buffer->yy_input_file = yyin;
+ yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state();
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yy_c_buf_p;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yy_did_buffer_switch_on_eof = 0;
+
+ if ( yywrap() )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p =
+ yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state();
+
+ yy_cp = yy_c_buf_p;
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yy_c_buf_p =
+ &yy_current_buffer->yy_ch_buf[yy_n_chars];
+
+ yy_current_state = yy_get_previous_state();
+
+ yy_cp = yy_c_buf_p;
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+ } /* end of yylex */
+
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+
+static int yy_get_next_buffer()
+ {
+ register char *dest = yy_current_buffer->yy_ch_buf;
+ register char *source = yytext_ptr;
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( yy_current_buffer->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ yy_current_buffer->yy_n_chars = yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ yy_current_buffer->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+#ifdef YY_USES_REJECT
+ YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+#else
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = yy_current_buffer;
+
+ int yy_c_buf_p_offset =
+ (int) (yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yy_flex_realloc( (void *) b->yy_ch_buf,
+ b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = yy_current_buffer->yy_buf_size -
+ number_to_move - 1;
+#endif
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),
+ yy_n_chars, num_to_read );
+
+ yy_current_buffer->yy_n_chars = yy_n_chars;
+ }
+
+ if ( yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart( yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ yy_current_buffer->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ yy_n_chars += number_to_move;
+ yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yytext_ptr = &yy_current_buffer->yy_ch_buf[0];
+
+ return ret_val;
+ }
+
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+static yy_state_type yy_get_previous_state()
+ {
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = yy_start;
+
+ for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ yy_last_accepting_state = yy_current_state;
+ yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 63 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+ }
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+
+#ifdef YY_USE_PROTOS
+static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state )
+#else
+static yy_state_type yy_try_NUL_trans( yy_current_state )
+yy_state_type yy_current_state;
+#endif
+ {
+ register int yy_is_jam;
+ register char *yy_cp = yy_c_buf_p;
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ yy_last_accepting_state = yy_current_state;
+ yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 63 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 62);
+
+ return yy_is_jam ? 0 : yy_current_state;
+ }
+
+
+#ifndef YY_NO_UNPUT
+#ifdef YY_USE_PROTOS
+static void yyunput( int c, register char *yy_bp )
+#else
+static void yyunput( c, yy_bp )
+int c;
+register char *yy_bp;
+#endif
+ {
+ register char *yy_cp = yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yy_hold_char;
+
+ if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = yy_n_chars + 2;
+ register char *dest = &yy_current_buffer->yy_ch_buf[
+ yy_current_buffer->yy_buf_size + 2];
+ register char *source =
+ &yy_current_buffer->yy_ch_buf[number_to_move];
+
+ while ( source > yy_current_buffer->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ yy_current_buffer->yy_n_chars =
+ yy_n_chars = yy_current_buffer->yy_buf_size;
+
+ if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+
+ yytext_ptr = yy_bp;
+ yy_hold_char = *yy_cp;
+ yy_c_buf_p = yy_cp;
+ }
+#endif /* ifndef YY_NO_UNPUT */
+
+
+#ifdef __cplusplus
+static int yyinput()
+#else
+static int input()
+#endif
+ {
+ int c;
+
+ *yy_c_buf_p = yy_hold_char;
+
+ if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+ /* This was really a NUL. */
+ *yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ int offset = yy_c_buf_p - yytext_ptr;
+ ++yy_c_buf_p;
+
+ switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ yyrestart( yyin );
+
+ /* fall through */
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap() )
+ return EOF;
+
+ if ( ! yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p = yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */
+ *yy_c_buf_p = '\0'; /* preserve yytext */
+ yy_hold_char = *++yy_c_buf_p;
+
+
+ return c;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yyrestart( FILE *input_file )
+#else
+void yyrestart( input_file )
+FILE *input_file;
+#endif
+ {
+ if ( ! yy_current_buffer )
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_init_buffer( yy_current_buffer, input_file );
+ yy_load_buffer_state();
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+#else
+void yy_switch_to_buffer( new_buffer )
+YY_BUFFER_STATE new_buffer;
+#endif
+ {
+ if ( yy_current_buffer == new_buffer )
+ return;
+
+ if ( yy_current_buffer )
+ {
+ /* Flush out information for old buffer. */
+ *yy_c_buf_p = yy_hold_char;
+ yy_current_buffer->yy_buf_pos = yy_c_buf_p;
+ yy_current_buffer->yy_n_chars = yy_n_chars;
+ }
+
+ yy_current_buffer = new_buffer;
+ yy_load_buffer_state();
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yy_did_buffer_switch_on_eof = 1;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_load_buffer_state( void )
+#else
+void yy_load_buffer_state()
+#endif
+ {
+ yy_n_chars = yy_current_buffer->yy_n_chars;
+ yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos;
+ yyin = yy_current_buffer->yy_input_file;
+ yy_hold_char = *yy_c_buf_p;
+ }
+
+
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+#else
+YY_BUFFER_STATE yy_create_buffer( file, size )
+FILE *file;
+int size;
+#endif
+ {
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer( b, file );
+
+ return b;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_delete_buffer( YY_BUFFER_STATE b )
+#else
+void yy_delete_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+ {
+ if ( ! b )
+ return;
+
+ if ( b == yy_current_buffer )
+ yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yy_flex_free( (void *) b->yy_ch_buf );
+
+ yy_flex_free( (void *) b );
+ }
+
+
+
+#ifdef YY_USE_PROTOS
+void yy_init_buffer( YY_BUFFER_STATE b, FILE *file )
+#else
+void yy_init_buffer( b, file )
+YY_BUFFER_STATE b;
+FILE *file;
+#endif
+
+
+ {
+ yy_flush_buffer( b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+#if YY_ALWAYS_INTERACTIVE
+ b->yy_is_interactive = 1;
+#else
+#if YY_NEVER_INTERACTIVE
+ b->yy_is_interactive = 0;
+#else
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+#endif
+#endif
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_flush_buffer( YY_BUFFER_STATE b )
+#else
+void yy_flush_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+
+ {
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == yy_current_buffer )
+ yy_load_buffer_state();
+ }
+
+
+#ifndef YY_NO_SCAN_BUFFER
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size )
+#else
+YY_BUFFER_STATE yy_scan_buffer( base, size )
+char *base;
+yy_size_t size;
+#endif
+ {
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer( b );
+
+ return b;
+ }
+#endif
+
+
+#ifndef YY_NO_SCAN_STRING
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_string( yyconst char *yy_str )
+#else
+YY_BUFFER_STATE yy_scan_string( yy_str )
+yyconst char *yy_str;
+#endif
+ {
+ int len;
+ for ( len = 0; yy_str[len]; ++len )
+ ;
+
+ return yy_scan_bytes( yy_str, len );
+ }
+#endif
+
+
+#ifndef YY_NO_SCAN_BYTES
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len )
+#else
+YY_BUFFER_STATE yy_scan_bytes( bytes, len )
+yyconst char *bytes;
+int len;
+#endif
+ {
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = len + 2;
+ buf = (char *) yy_flex_alloc( n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < len; ++i )
+ buf[i] = bytes[i];
+
+ buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer( buf, n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+ }
+#endif
+
+
+#ifndef YY_NO_PUSH_STATE
+#ifdef YY_USE_PROTOS
+static void yy_push_state( int new_state )
+#else
+static void yy_push_state( new_state )
+int new_state;
+#endif
+ {
+ if ( yy_start_stack_ptr >= yy_start_stack_depth )
+ {
+ yy_size_t new_size;
+
+ yy_start_stack_depth += YY_START_STACK_INCR;
+ new_size = yy_start_stack_depth * sizeof( int );
+
+ if ( ! yy_start_stack )
+ yy_start_stack = (int *) yy_flex_alloc( new_size );
+
+ else
+ yy_start_stack = (int *) yy_flex_realloc(
+ (void *) yy_start_stack, new_size );
+
+ if ( ! yy_start_stack )
+ YY_FATAL_ERROR(
+ "out of memory expanding start-condition stack" );
+ }
+
+ yy_start_stack[yy_start_stack_ptr++] = YY_START;
+
+ BEGIN(new_state);
+ }
+#endif
+
+
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state()
+ {
+ if ( --yy_start_stack_ptr < 0 )
+ YY_FATAL_ERROR( "start-condition stack underflow" );
+
+ BEGIN(yy_start_stack[yy_start_stack_ptr]);
+ }
+#endif
+
+
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state()
+ {
+ return yy_start_stack[yy_start_stack_ptr - 1];
+ }
+#endif
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+#ifdef YY_USE_PROTOS
+static void yy_fatal_error( yyconst char msg[] )
+#else
+static void yy_fatal_error( msg )
+char msg[];
+#endif
+ {
+ (void) fprintf( stderr, "[lex] %s\n", msg );
+ exit( YY_EXIT_FAILURE );
+ }
+
+
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ yytext[yyleng] = yy_hold_char; \
+ yy_c_buf_p = yytext + n; \
+ yy_hold_char = *yy_c_buf_p; \
+ *yy_c_buf_p = '\0'; \
+ yyleng = n; \
+ } \
+ while ( 0 )
+
+
+/* Internal utility routines. */
+
+#ifndef yytext_ptr
+#ifdef YY_USE_PROTOS
+static void yy_flex_strncpy( char *s1, yyconst char *s2, int n )
+#else
+static void yy_flex_strncpy( s1, s2, n )
+char *s1;
+yyconst char *s2;
+int n;
+#endif
+ {
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+ }
+#endif
+
+#ifdef YY_NEED_STRLEN
+#ifdef YY_USE_PROTOS
+static int yy_flex_strlen( yyconst char *s )
+#else
+static int yy_flex_strlen( s )
+yyconst char *s;
+#endif
+ {
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+ }
+#endif
+
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_alloc( yy_size_t size )
+#else
+static void *yy_flex_alloc( size )
+yy_size_t size;
+#endif
+ {
+ return (void *) malloc( size );
+ }
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_realloc( void *ptr, yy_size_t size )
+#else
+static void *yy_flex_realloc( ptr, size )
+void *ptr;
+yy_size_t size;
+#endif
+ {
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+ }
+
+#ifdef YY_USE_PROTOS
+static void yy_flex_free( void *ptr )
+#else
+static void yy_flex_free( ptr )
+void *ptr;
+#endif
+ {
+ free( ptr );
+ }
+
+#if YY_MAIN
+int main()
+ {
+ yylex();
+ return 0;
+ }
+#endif
+#line 57 "lex.l"
+
+
+char* KTraderParse_putSymbolInBrackets( char *_name )
+{
+ int l = strlen( _name )-1;
+ char *p = (char *)malloc( l );
+ if (p != NULL)
+ {
+ strncpy( p, _name+1, l-1 );
+ p[l-1] = 0;
+ }
+
+ return p;
+}
+
+char *KTraderParse_putSymbol( char *_name )
+{
+ char *p = (char*)malloc( strlen( _name ) + 1 );
+ if (p != NULL)
+ {
+ strcpy( p, _name );
+ }
+ return p;
+}
+
+char* KTraderParse_putString( char *_str )
+{
+ int l = strlen( _str );
+ char *p = (char*)malloc( l );
+ char *s = _str + 1;
+ char *d = p;
+
+ if (p == NULL)
+ return NULL;
+
+ while ( s != _str + l - 1 )
+ {
+ if ( *s != '\\' )
+ *d++ = *s++;
+ else
+ {
+ s++;
+ if ( s != _str + l - 1 )
+ {
+ if ( *s == '\\' )
+ *d++ = '\\';
+ else if ( *s == 'n' )
+ *d++ = '\n';
+ else if ( *s == 'r' )
+ *d++ = '\r';
+ else if ( *s == 't' )
+ *d++ = '\t';
+ s++;
+ }
+ }
+ }
+ *d = 0;
+ return p;
+}
+
+void KTraderParse_initFlex( const char *_code )
+{
+ yy_switch_to_buffer( yy_scan_string( _code ) );
+}
+
+int yywrap()
+{
+ yy_delete_buffer( YY_CURRENT_BUFFER );
+ return 1;
+}
diff --git a/tdeio/tdeio/lex.l b/tdeio/tdeio/lex.l
new file mode 100644
index 000000000..073a34965
--- /dev/null
+++ b/tdeio/tdeio/lex.l
@@ -0,0 +1,126 @@
+%{
+#define yylval kiotraderlval
+#define yywrap kiotraderwrap
+
+#include "yacc.h"
+#include <string.h>
+#include <stdlib.h>
+#define YY_NO_UNPUT
+
+char* KTraderParse_putSymbol( char *_name );
+char *KTraderParse_putSymbolInBrackets( char *_name );
+char* KTraderParse_putString( char *_name );
+int yywrap();
+int kiotraderlex(void);
+void KTraderParse_initFlex( const char *_code );
+
+%}
+
+DIGIT [0-9]
+
+%%
+
+"==" { return EQ; }
+"!=" { return NEQ; }
+"<" { return LE; }
+">" { return GR; }
+"<=" { return LEQ; }
+">=" { return GEQ; }
+"not" { return NOT; }
+"and" { return AND; }
+"or" { return OR; }
+"in" { return TOKEN_IN; }
+"exist" { return EXIST; }
+"max" { return MAX; }
+"min" { return MIN; }
+
+"~"|"/"|"+"|"-"|"="|"*"|"("|")"|"," { yylval.name = 0L; return (int)(*yytext); }
+
+"TRUE" { yylval.valb = 1; return VAL_BOOL; }
+"FALSE" { yylval.valb = 0; return VAL_BOOL; }
+
+"'"[^']*"'" { yylval.name = KTraderParse_putString( yytext ); return VAL_STRING; }
+
+"-"{DIGIT}+ { yylval.vali = atoi( yytext ); return VAL_NUM; }
+{DIGIT}+ { yylval.vali = atoi( yytext ); return VAL_NUM; }
+
+{DIGIT}*"\."{DIGIT}+ { yylval.vald = atof( yytext ); return VAL_FLOAT; }
+
+\[[a-zA-Z][a-zA-Z0-9\-]*\] { yylval.name = KTraderParse_putSymbolInBrackets( yytext ); return VAL_ID; }
+
+[a-zA-Z][a-zA-Z0-9]* { yylval.name = KTraderParse_putSymbol( yytext ); return VAL_ID; }
+
+[ \t\n]+ /* eat up whitespace */
+
+. { printf( "Unrecognized character: %s\n", yytext ); }
+
+%%
+
+char* KTraderParse_putSymbolInBrackets( char *_name )
+{
+ int l = strlen( _name )-1;
+ char *p = (char *)malloc( l );
+ if (p != NULL)
+ {
+ strncpy( p, _name+1, l-1 );
+ p[l-1] = 0;
+ }
+
+ return p;
+}
+
+char *KTraderParse_putSymbol( char *_name )
+{
+ char *p = (char*)malloc( strlen( _name ) + 1 );
+ if (p != NULL)
+ {
+ strcpy( p, _name );
+ }
+ return p;
+}
+
+char* KTraderParse_putString( char *_str )
+{
+ int l = strlen( _str );
+ char *p = (char*)malloc( l );
+ char *s = _str + 1;
+ char *d = p;
+
+ if (p == NULL)
+ return NULL;
+
+ while ( s != _str + l - 1 )
+ {
+ if ( *s != '\\' )
+ *d++ = *s++;
+ else
+ {
+ s++;
+ if ( s != _str + l - 1 )
+ {
+ if ( *s == '\\' )
+ *d++ = '\\';
+ else if ( *s == 'n' )
+ *d++ = '\n';
+ else if ( *s == 'r' )
+ *d++ = '\r';
+ else if ( *s == 't' )
+ *d++ = '\t';
+ s++;
+ }
+ }
+ }
+ *d = 0;
+ return p;
+}
+
+void KTraderParse_initFlex( const char *_code )
+{
+ yy_switch_to_buffer( yy_scan_string( _code ) );
+}
+
+int yywrap()
+{
+ yy_delete_buffer( YY_CURRENT_BUFFER );
+ return 1;
+}
diff --git a/tdeio/tdeio/metainfojob.cpp b/tdeio/tdeio/metainfojob.cpp
new file mode 100644
index 000000000..d9a31ab0e
--- /dev/null
+++ b/tdeio/tdeio/metainfojob.cpp
@@ -0,0 +1,184 @@
+// -*- c++ -*-
+// vim: ts=4 sw=4 et
+/* This file is part of the KDE libraries
+ Copyright (C) 2002 Rolf Magnus <ramagnus@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation version 2.0.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+
+ $Id$
+*/
+
+#include <kdatastream.h> // Do not remove, needed for correct bool serialization
+#include <tdefileitem.h>
+#include <kdebug.h>
+#include <tdefilemetainfo.h>
+#include <tdeio/kservice.h>
+#include <tdeparts/componentfactory.h>
+
+#include <tqtimer.h>
+
+#include "metainfojob.moc"
+
+using namespace TDEIO;
+
+struct TDEIO::MetaInfoJobPrivate
+{
+ KFileItemList items; // all the items we got
+ KFileItemListIterator* currentItem; // argh! No default constructor
+ bool deleteItems; // Delete the KFileItems when done?
+ bool succeeded; // if the current item is ok
+};
+
+MetaInfoJob::MetaInfoJob(const KFileItemList &items, bool deleteItems)
+ : TDEIO::Job(false /* no GUI */)
+{
+ d = new MetaInfoJobPrivate;
+ d->deleteItems = deleteItems;
+ d->succeeded = false;
+ d->items = items;
+ d->currentItem = new KFileItemListIterator(d->items);
+
+ d->items.setAutoDelete(deleteItems);
+
+ if (d->currentItem->isEmpty())
+ {
+ kdDebug(7007) << "nothing to do for the MetaInfoJob\n";
+ emitResult();
+ return;
+ }
+
+ kdDebug(7007) << "starting MetaInfoJob\n";
+
+ // Return to event loop first, determineNextFile() might delete this;
+ // (no idea what that means, it comes from previewjob)
+ TQTimer::singleShot(0, this, TQT_SLOT(start()));
+}
+
+MetaInfoJob::~MetaInfoJob()
+{
+ delete d->currentItem;
+ delete d;
+}
+
+void MetaInfoJob::start()
+{
+ getMetaInfo();
+}
+
+void MetaInfoJob::removeItem(const KFileItem* item)
+{
+ if (d->currentItem->current() == item)
+ {
+ subjobs.first()->kill();
+ subjobs.removeFirst();
+ determineNextFile();
+ }
+
+ d->items.remove(d->items.find(item));
+}
+
+void MetaInfoJob::determineNextFile()
+{
+ if (d->currentItem->atLast())
+ {
+ kdDebug(7007) << "finished MetaInfoJob\n";
+ emitResult();
+ return;
+ }
+
+ ++(*d->currentItem);
+ d->succeeded = false;
+
+ // does the file item already have the needed info? Then shortcut
+ if (d->currentItem->current()->metaInfo(false).isValid())
+ {
+// kdDebug(7007) << "Is already valid *************************\n";
+ emit gotMetaInfo(d->currentItem->current());
+ determineNextFile();
+ return;
+ }
+
+ getMetaInfo();
+}
+
+void MetaInfoJob::slotResult( TDEIO::Job *job )
+{
+ subjobs.remove(job);
+ Q_ASSERT(subjobs.isEmpty()); // We should have only one job at a time ...
+
+ determineNextFile();
+}
+
+void MetaInfoJob::getMetaInfo()
+{
+ Q_ASSERT(!d->currentItem->isEmpty());
+
+ KURL URL;
+ URL.setProtocol("metainfo");
+ URL.setPath(d->currentItem->current()->url().path());
+
+ TDEIO::TransferJob* job = TDEIO::get(URL, false, false);
+ addSubjob(job);
+
+ connect(job, TQT_SIGNAL(data(TDEIO::Job *, const TQByteArray &)),
+ this, TQT_SLOT(slotMetaInfo(TDEIO::Job *, const TQByteArray &)));
+
+ job->addMetaData("mimeType", d->currentItem->current()->mimetype());
+}
+
+
+void MetaInfoJob::slotMetaInfo(TDEIO::Job*, const TQByteArray &data)
+{
+ KFileMetaInfo info;
+ TQDataStream s(data, IO_ReadOnly);
+
+ s >> info;
+
+ d->currentItem->current()->setMetaInfo(info);
+ emit gotMetaInfo(d->currentItem->current());
+ d->succeeded = true;
+}
+
+TQStringList MetaInfoJob::availablePlugins()
+{
+ TQStringList result;
+ KTrader::OfferList plugins = KTrader::self()->query("KFilePlugin");
+ for (KTrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
+ result.append((*it)->desktopEntryName());
+ return result;
+}
+
+TQStringList MetaInfoJob::supportedMimeTypes()
+{
+ TQStringList result;
+ KTrader::OfferList plugins = KTrader::self()->query("KFilePlugin");
+ for (KTrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
+ result += (*it)->property("MimeTypes").toStringList();
+ return result;
+}
+
+TDEIO_EXPORT MetaInfoJob *TDEIO::fileMetaInfo( const KFileItemList &items)
+{
+ return new MetaInfoJob(items, false);
+}
+
+TDEIO_EXPORT MetaInfoJob *TDEIO::fileMetaInfo( const KURL::List &items)
+{
+ KFileItemList fileItems;
+ for (KURL::List::ConstIterator it = items.begin(); it != items.end(); ++it)
+ fileItems.append(new KFileItem(KFileItem::Unknown, KFileItem::Unknown, *it, true));
+ return new MetaInfoJob(fileItems, true);
+}
+
diff --git a/tdeio/tdeio/metainfojob.h b/tdeio/tdeio/metainfojob.h
new file mode 100644
index 000000000..3a9fab67e
--- /dev/null
+++ b/tdeio/tdeio/metainfojob.h
@@ -0,0 +1,119 @@
+// -*- c++ -*-
+// vim: ts=4 sw=4 et
+/* This file is part of the KDE libraries
+ Copyright (C) 2001 Rolf Magnus <ramagnus@kde.org>
+ parts of this taken from previewjob.h
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation version 2.0.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kio_metainfojob_h__
+#define __kio_metainfojob_h__
+
+#include <tdeio/job.h>
+#include <tdefileitem.h>
+
+namespace TDEIO {
+ /**
+ * MetaInfoJob is a KIO Job to retrieve meta information from files.
+ *
+ * @short KIO Job to retrieve meta information from files.
+ * @since 3.1
+ */
+ class TDEIO_EXPORT MetaInfoJob : public TDEIO::Job
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Creates a new MetaInfoJob.
+ * @param items A list of KFileItems to get the metainfo for
+ * @param deleteItems If true, the finished KFileItems are deleted
+ */
+ MetaInfoJob(const KFileItemList &items, bool deleteItems = false);
+ virtual ~MetaInfoJob();
+
+ /**
+ * Removes an item from metainfo extraction.
+ *
+ * @param item the item that should be removed from the queue
+ */
+ void removeItem( const KFileItem *item );
+
+ /**
+ * Returns a list of all available metainfo plugins. The list
+ * contains the basenames of the plugins' .desktop files (no path,
+ * no .desktop).
+ * @return the list of available meta info plugins
+ */
+ static TQStringList availablePlugins();
+
+ /**
+ * Returns a list of all supported MIME types. The list can
+ * contain entries like text/ * (without the space).
+ * @return the list of MIME types that are supported
+ */
+ static TQStringList supportedMimeTypes();
+
+ signals:
+ /**
+ * Emitted when the meta info for @p item has been successfully
+ * retrieved.
+ * @param item the KFileItem describing the fetched item
+ */
+ void gotMetaInfo( const KFileItem *item );
+ /**
+ * Emitted when metainfo for @p item could not be extracted,
+ * either because a plugin for its MIME type does not
+ * exist, or because something went wrong.
+ * @param item the KFileItem of the file that failed
+ */
+ void failed( const KFileItem *item );
+
+ protected:
+ void getMetaInfo();
+
+ protected slots:
+ virtual void slotResult( TDEIO::Job *job );
+
+ private slots:
+ void start();
+ void slotMetaInfo(TDEIO::Job *, const TQByteArray &);
+
+ private:
+ void determineNextFile();
+// void saveMetaInfo(const TQByteArray info);
+
+ private:
+ struct MetaInfoJobPrivate *d;
+ };
+
+ /**
+ * Retrieves meta information for the given items.
+ *
+ * @param items files to get metainfo for
+ * @return the MetaInfoJob to retrieve the items
+ */
+ TDEIO_EXPORT MetaInfoJob* fileMetaInfo(const KFileItemList& items);
+
+ /**
+ * Retrieves meta information for the given items.
+ *
+ * @param items files to get metainfo for
+ * @return the MetaInfoJob to retrieve the items
+ */
+ TDEIO_EXPORT MetaInfoJob* fileMetaInfo(const KURL::List& items);
+}
+
+#endif
diff --git a/tdeio/tdeio/netaccess.cpp b/tdeio/tdeio/netaccess.cpp
new file mode 100644
index 000000000..89830d88b
--- /dev/null
+++ b/tdeio/tdeio/netaccess.cpp
@@ -0,0 +1,536 @@
+/* $Id$
+
+ This file is part of the KDE libraries
+ Copyright (C) 1997 Torben Weis (weis@kde.org)
+ Copyright (C) 1998 Matthias Ettrich (ettrich@kde.org)
+ Copyright (C) 1999 David Faure (faure@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include <cstring>
+
+#include <tqstring.h>
+#include <tqapplication.h>
+#include <tqfile.h>
+#include <tqmetaobject.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <ktempfile.h>
+#include <kdebug.h>
+#include <kurl.h>
+#include <tdeio/job.h>
+#include <tdeio/scheduler.h>
+
+#include "tdeio/netaccess.h"
+
+using namespace TDEIO;
+
+TQString * NetAccess::lastErrorMsg;
+int NetAccess::lastErrorCode = 0;
+TQStringList* NetAccess::tmpfiles;
+
+bool NetAccess::download(const KURL& u, TQString & target)
+{
+ return NetAccess::download (u, target, 0);
+}
+
+bool NetAccess::download(const KURL& u, TQString & target, TQWidget* window)
+{
+ if (u.isLocalFile()) {
+ // file protocol. We do not need the network
+ target = u.path();
+ bool accessible = checkAccess(target, R_OK);
+ if(!accessible)
+ {
+ if(!lastErrorMsg)
+ lastErrorMsg = new TQString;
+ *lastErrorMsg = i18n("File '%1' is not readable").arg(target);
+ lastErrorCode = ERR_COULD_NOT_READ;
+ }
+ return accessible;
+ }
+
+ if (target.isEmpty())
+ {
+ KTempFile tmpFile;
+ target = tmpFile.name();
+ if (!tmpfiles)
+ tmpfiles = new TQStringList;
+ tmpfiles->append(target);
+ }
+
+ NetAccess kioNet;
+ KURL dest;
+ dest.setPath( target );
+ return kioNet.filecopyInternal( u, dest, -1, true /*overwrite*/,
+ false, window, false /*copy*/);
+}
+
+bool NetAccess::upload(const TQString& src, const KURL& target)
+{
+ return NetAccess::upload(src, target, 0);
+}
+
+bool NetAccess::upload(const TQString& src, const KURL& target, TQWidget* window)
+{
+ if (target.isEmpty())
+ return false;
+
+ // If target is local... well, just copy. This can be useful
+ // when the client code uses a temp file no matter what.
+ // Let's make sure it's not the exact same file though
+ if (target.isLocalFile() && target.path() == src)
+ return true;
+
+ NetAccess kioNet;
+ KURL s;
+ s.setPath(src);
+ return kioNet.filecopyInternal( s, target, -1, true /*overwrite*/,
+ false, window, false /*copy*/ );
+}
+
+bool NetAccess::copy( const KURL & src, const KURL & target )
+{
+ return NetAccess::file_copy( src, target, -1, false /*not overwrite*/, false, 0L );
+}
+
+bool NetAccess::copy( const KURL & src, const KURL & target, TQWidget* window )
+{
+ return NetAccess::file_copy( src, target, -1, false /*not overwrite*/, false, window );
+}
+
+bool NetAccess::file_copy( const KURL& src, const KURL& target, int permissions,
+ bool overwrite, bool resume, TQWidget* window )
+{
+ NetAccess kioNet;
+ return kioNet.filecopyInternal( src, target, permissions, overwrite, resume,
+ window, false /*copy*/ );
+}
+
+
+bool NetAccess::file_move( const KURL& src, const KURL& target, int permissions,
+ bool overwrite, bool resume, TQWidget* window )
+{
+ NetAccess kioNet;
+ return kioNet.filecopyInternal( src, target, permissions, overwrite, resume,
+ window, true /*move*/ );
+}
+
+bool NetAccess::dircopy( const KURL & src, const KURL & target )
+{
+ return NetAccess::dircopy( src, target, 0 );
+}
+
+bool NetAccess::dircopy( const KURL & src, const KURL & target, TQWidget* window )
+{
+ KURL::List srcList;
+ srcList.append( src );
+ return NetAccess::dircopy( srcList, target, window );
+}
+
+bool NetAccess::dircopy( const KURL::List & srcList, const KURL & target, TQWidget* window )
+{
+ NetAccess kioNet;
+ return kioNet.dircopyInternal( srcList, target, window, false /*copy*/ );
+}
+
+bool NetAccess::move( const KURL& src, const KURL& target, TQWidget* window )
+{
+ KURL::List srcList;
+ srcList.append( src );
+ return NetAccess::move( srcList, target, window );
+}
+
+bool NetAccess::move( const KURL::List& srcList, const KURL& target, TQWidget* window )
+{
+ NetAccess kioNet;
+ return kioNet.dircopyInternal( srcList, target, window, true /*move*/ );
+}
+
+bool NetAccess::exists( const KURL & url )
+{
+ return NetAccess::exists( url, false, 0 );
+}
+
+bool NetAccess::exists( const KURL & url, TQWidget* window )
+{
+ return NetAccess::exists( url, false, window );
+}
+
+bool NetAccess::exists( const KURL & url, bool source )
+{
+ return NetAccess::exists( url, source, 0 );
+}
+
+bool NetAccess::exists( const KURL & url, bool source, TQWidget* window )
+{
+ if ( url.isLocalFile() )
+ return TQFile::exists( url.path() );
+ NetAccess kioNet;
+ return kioNet.statInternal( url, 0 /*no details*/, source, window );
+}
+
+bool NetAccess::stat( const KURL & url, TDEIO::UDSEntry & entry )
+{
+ return NetAccess::stat( url, entry, 0 );
+}
+
+bool NetAccess::stat( const KURL & url, TDEIO::UDSEntry & entry, TQWidget* window )
+{
+ NetAccess kioNet;
+ bool ret = kioNet.statInternal( url, 2 /*all details*/, true /*source*/, window );
+ if (ret)
+ entry = kioNet.m_entry;
+ return ret;
+}
+
+KURL NetAccess::mostLocalURL(const KURL & url, TQWidget* window)
+{
+ if ( url.isLocalFile() )
+ {
+ return url;
+ }
+
+ TDEIO::UDSEntry entry;
+ if (!stat(url, entry, window))
+ {
+ return url;
+ }
+
+ TQString path;
+
+ // Extract the local path from the TDEIO::UDSEntry
+ TDEIO::UDSEntry::ConstIterator it = entry.begin();
+ const TDEIO::UDSEntry::ConstIterator end = entry.end();
+ for ( ; it != end; ++it )
+ {
+ if ( (*it).m_uds == TDEIO::UDS_LOCAL_PATH )
+ {
+ path = (*it).m_str;
+ break;
+ }
+ }
+
+ if ( !path.isEmpty() )
+ {
+ KURL new_url;
+ new_url.setPath(path);
+ return new_url;
+ }
+
+ return url;
+}
+
+
+bool NetAccess::del( const KURL & url )
+{
+ return NetAccess::del( url, 0 );
+}
+
+bool NetAccess::del( const KURL & url, TQWidget* window )
+{
+ NetAccess kioNet;
+ return kioNet.delInternal( url, window );
+}
+
+bool NetAccess::mkdir( const KURL & url, int permissions )
+{
+ return NetAccess::mkdir( url, 0, permissions );
+}
+
+bool NetAccess::mkdir( const KURL & url, TQWidget* window, int permissions )
+{
+ NetAccess kioNet;
+ return kioNet.mkdirInternal( url, permissions, window );
+}
+
+TQString NetAccess::fish_execute( const KURL & url, const TQString command, TQWidget* window )
+{
+ NetAccess kioNet;
+ return kioNet.fish_executeInternal( url, command, window );
+}
+
+bool NetAccess::synchronousRun( Job* job, TQWidget* window, TQByteArray* data,
+ KURL* finalURL, TQMap<TQString, TQString>* metaData )
+{
+ NetAccess kioNet;
+ return kioNet.synchronousRunInternal( job, window, data, finalURL, metaData );
+}
+
+TQString NetAccess::mimetype( const KURL& url )
+{
+ NetAccess kioNet;
+ return kioNet.mimetypeInternal( url, 0 );
+}
+
+TQString NetAccess::mimetype( const KURL& url, TQWidget* window )
+{
+ NetAccess kioNet;
+ return kioNet.mimetypeInternal( url, window );
+}
+
+void NetAccess::removeTempFile(const TQString& name)
+{
+ if (!tmpfiles)
+ return;
+ if (tmpfiles->contains(name))
+ {
+ unlink(TQFile::encodeName(name));
+ tmpfiles->remove(name);
+ }
+}
+
+bool NetAccess::filecopyInternal(const KURL& src, const KURL& target, int permissions,
+ bool overwrite, bool resume, TQWidget* window, bool move)
+{
+ bJobOK = true; // success unless further error occurs
+
+ TDEIO::Scheduler::checkSlaveOnHold(true);
+ TDEIO::Job * job = move
+ ? TDEIO::file_move( src, target, permissions, overwrite, resume )
+ : TDEIO::file_copy( src, target, permissions, overwrite, resume );
+ job->setWindow (window);
+ connect( job, TQT_SIGNAL( result (TDEIO::Job *) ),
+ this, TQT_SLOT( slotResult (TDEIO::Job *) ) );
+
+ enter_loop();
+ return bJobOK;
+}
+
+bool NetAccess::dircopyInternal(const KURL::List& src, const KURL& target,
+ TQWidget* window, bool move)
+{
+ bJobOK = true; // success unless further error occurs
+
+ TDEIO::Job * job = move
+ ? TDEIO::move( src, target )
+ : TDEIO::copy( src, target );
+ job->setWindow (window);
+ connect( job, TQT_SIGNAL( result (TDEIO::Job *) ),
+ this, TQT_SLOT( slotResult (TDEIO::Job *) ) );
+
+ enter_loop();
+ return bJobOK;
+}
+
+bool NetAccess::statInternal( const KURL & url, int details, bool source,
+ TQWidget* window )
+{
+ bJobOK = true; // success unless further error occurs
+ TDEIO::StatJob * job = TDEIO::stat( url, !url.isLocalFile() && !url.url().startsWith("beagle:?") );
+ job->setWindow (window);
+ job->setDetails( details );
+ job->setSide( source );
+ connect( job, TQT_SIGNAL( result (TDEIO::Job *) ),
+ this, TQT_SLOT( slotResult (TDEIO::Job *) ) );
+ enter_loop();
+ return bJobOK;
+}
+
+bool NetAccess::delInternal( const KURL & url, TQWidget* window )
+{
+ bJobOK = true; // success unless further error occurs
+ TDEIO::Job * job = TDEIO::del( url );
+ job->setWindow (window);
+ connect( job, TQT_SIGNAL( result (TDEIO::Job *) ),
+ this, TQT_SLOT( slotResult (TDEIO::Job *) ) );
+ enter_loop();
+ return bJobOK;
+}
+
+bool NetAccess::mkdirInternal( const KURL & url, int permissions,
+ TQWidget* window )
+{
+ bJobOK = true; // success unless further error occurs
+ TDEIO::Job * job = TDEIO::mkdir( url, permissions );
+ job->setWindow (window);
+ connect( job, TQT_SIGNAL( result (TDEIO::Job *) ),
+ this, TQT_SLOT( slotResult (TDEIO::Job *) ) );
+ enter_loop();
+ return bJobOK;
+}
+
+TQString NetAccess::mimetypeInternal( const KURL & url, TQWidget* window )
+{
+ bJobOK = true; // success unless further error occurs
+ m_mimetype = TQString::fromLatin1("unknown");
+ TDEIO::Job * job = TDEIO::mimetype( url );
+ job->setWindow (window);
+ connect( job, TQT_SIGNAL( result (TDEIO::Job *) ),
+ this, TQT_SLOT( slotResult (TDEIO::Job *) ) );
+ connect( job, TQT_SIGNAL( mimetype (TDEIO::Job *, const TQString &) ),
+ this, TQT_SLOT( slotMimetype (TDEIO::Job *, const TQString &) ) );
+ enter_loop();
+ return m_mimetype;
+}
+
+void NetAccess::slotMimetype( TDEIO::Job *, const TQString & type )
+{
+ m_mimetype = type;
+}
+
+TQString NetAccess::fish_executeInternal(const KURL & url, const TQString command, TQWidget* window)
+{
+ TQString target, remoteTempFileName, resultData;
+ KURL tempPathUrl;
+ KTempFile tmpFile;
+ tmpFile.setAutoDelete( true );
+
+ if( url.protocol() == "fish" )
+ {
+ // construct remote temp filename
+ tempPathUrl = url;
+ remoteTempFileName = tmpFile.name();
+ // only need the filename KTempFile adds some KDE specific dirs
+ // that probably does not exist on the remote side
+ int pos = remoteTempFileName.findRev('/');
+ remoteTempFileName = "/tmp/fishexec_" + remoteTempFileName.mid(pos + 1);
+ tempPathUrl.setPath( remoteTempFileName );
+ bJobOK = true; // success unless further error occurs
+ TQByteArray packedArgs;
+ TQDataStream stream( packedArgs, IO_WriteOnly );
+
+ stream << int('X') << tempPathUrl << command;
+
+ TDEIO::Job * job = TDEIO::special( tempPathUrl, packedArgs, true );
+ job->setWindow( window );
+ connect( job, TQT_SIGNAL( result (TDEIO::Job *) ),
+ this, TQT_SLOT( slotResult (TDEIO::Job *) ) );
+ enter_loop();
+
+ // since the TDEIO::special does not provide feedback we need to download the result
+ if( NetAccess::download( tempPathUrl, target, window ) )
+ {
+ TQFile resultFile( target );
+
+ if (resultFile.open( IO_ReadOnly ))
+ {
+ TQTextStream ts( &resultFile );
+ ts.setEncoding( TQTextStream::Locale ); // Locale??
+ resultData = ts.read();
+ resultFile.close();
+ NetAccess::del( tempPathUrl, window );
+ }
+ }
+ }
+ else
+ {
+ resultData = i18n( "ERROR: Unknown protocol '%1'" ).arg( url.protocol() );
+ }
+ return resultData;
+}
+
+bool NetAccess::synchronousRunInternal( Job* job, TQWidget* window, TQByteArray* data,
+ KURL* finalURL, TQMap<TQString,TQString>* metaData )
+{
+ job->setWindow( window );
+
+ m_metaData = metaData;
+ if ( m_metaData ) {
+ for ( TQMap<TQString, TQString>::iterator it = m_metaData->begin(); it != m_metaData->end(); ++it ) {
+ job->addMetaData( it.key(), it.data() );
+ }
+ }
+
+ if ( finalURL ) {
+ SimpleJob *sj = dynamic_cast<SimpleJob*>( job );
+ if ( sj ) {
+ m_url = sj->url();
+ }
+ }
+
+ connect( job, TQT_SIGNAL( result (TDEIO::Job *) ),
+ this, TQT_SLOT( slotResult (TDEIO::Job *) ) );
+
+ TQMetaObject *meta = job->metaObject();
+
+ static const char dataSignal[] = "data(TDEIO::Job*,const " TQBYTEARRAY_OBJECT_NAME_STRING "&)";
+ if ( meta->findSignal( dataSignal ) != -1 ) {
+ connect( job, TQT_SIGNAL(data(TDEIO::Job*,const TQByteArray&)),
+ this, TQT_SLOT(slotData(TDEIO::Job*,const TQByteArray&)) );
+ }
+
+ static const char redirSignal[] = "redirection(TDEIO::Job*,const KURL&)";
+ if ( meta->findSignal( redirSignal ) != -1 ) {
+ connect( job, TQT_SIGNAL(redirection(TDEIO::Job*,const KURL&)),
+ this, TQT_SLOT(slotRedirection(TDEIO::Job*, const KURL&)) );
+ }
+
+ enter_loop();
+
+ if ( finalURL )
+ *finalURL = m_url;
+ if ( data )
+ *data = m_data;
+
+ return bJobOK;
+}
+
+// If a troll sees this, he kills me
+void tqt_enter_modal( TQWidget *widget );
+void tqt_leave_modal( TQWidget *widget );
+
+void NetAccess::enter_loop()
+{
+ TQWidget dummy(0,0,(WFlags)(WType_Dialog | WShowModal));
+ dummy.setFocusPolicy( TQ_NoFocus );
+ tqt_enter_modal(&dummy);
+ tqApp->enter_loop();
+ tqt_leave_modal(&dummy);
+}
+
+void NetAccess::slotResult( TDEIO::Job * job )
+{
+ lastErrorCode = job->error();
+ bJobOK = !job->error();
+ if ( !bJobOK )
+ {
+ if ( !lastErrorMsg )
+ lastErrorMsg = new TQString;
+ *lastErrorMsg = job->errorString();
+ }
+ if ( job->isA("TDEIO::StatJob") )
+ m_entry = static_cast<TDEIO::StatJob *>(job)->statResult();
+
+ if ( m_metaData )
+ *m_metaData = job->metaData();
+
+ tqApp->exit_loop();
+}
+
+void NetAccess::slotData( TDEIO::Job*, const TQByteArray& data )
+{
+ if ( data.isEmpty() )
+ return;
+
+ unsigned offset = m_data.size();
+ m_data.resize( offset + data.size() );
+ std::memcpy( m_data.data() + offset, data.data(), data.size() );
+}
+
+void NetAccess::slotRedirection( TDEIO::Job*, const KURL& url )
+{
+ m_url = url;
+}
+
+#include "netaccess.moc"
diff --git a/tdeio/tdeio/netaccess.h b/tdeio/tdeio/netaccess.h
new file mode 100644
index 000000000..356a91738
--- /dev/null
+++ b/tdeio/tdeio/netaccess.h
@@ -0,0 +1,540 @@
+/*
+ This file is part of the KDE libraries
+ Copyright (C) 1997 Torben Weis (weis@kde.org)
+ Copyright (C) 1998 Matthias Ettrich (ettrich@kde.org)
+ Copyright (C) 1999-2004 David Faure (faure@kde.org)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kio_netaccess_h
+#define __kio_netaccess_h
+
+#include <tqobject.h>
+#include <tqstring.h>
+#include <tdeio/global.h>
+
+class TQStringList;
+class TQWidget;
+class KURL;
+template<typename T, typename K> class TQMap;
+
+namespace TDEIO {
+
+ class Job;
+
+ /**
+ * Net Transparency.
+ *
+ * NetAccess allows you to do simple file operation (load, save,
+ * copy, delete...) without working with TDEIO::Job directly.
+ * Whereas a TDEIO::Job is asynchronous, meaning that the
+ * developer has to connect slots for it, TDEIO::NetAccess provides
+ * synchronous downloads and uploads, as well as temporary file
+ * creation and removal. The functions appear to be blocking,
+ * but the Qt event loop continues running while the operations
+ * are handled. This means that the GUI will not freeze.
+ *
+ * This class isn't meant to be used as a class but only as a simple
+ * namespace for static functions, though an instance of the class
+ * is built for internal purposes.
+ *
+ * Port to kio done by David Faure, faure@kde.org
+ *
+ * @short Provides an easy, synchronous interface to KIO file operations.
+ */
+class TDEIO_EXPORT NetAccess : public TQObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Downloads a file from an arbitrary URL (@p src) to a
+ * temporary file on the local filesystem (@p target).
+ *
+ * If the argument
+ * for @p target is an empty string, download will generate a
+ * unique temporary filename in /tmp. Since @p target is a reference
+ * to TQString you can access this filename easily. Download will
+ * return true if the download was successful, otherwise false.
+ *
+ * Special case:
+ * If the URL is of kind file:, then no downloading is
+ * processed but the full filename is returned in @p target.
+ * That means you @em have to take care about the @p target argument.
+ * (This is very easy to do, please see the example below.)
+ *
+ * Download is synchronous. That means you can use it like
+ * this, (assuming @p u is a string which represents a URL and your
+ * application has a loadFile() function):
+ *
+ * \code
+ * TQString tmpFile;
+ * if( TDEIO::NetAccess::download( u, tmpFile, window ) )
+ * {
+ * loadFile( tmpFile );
+ * TDEIO::NetAccess::removeTempFile( tmpFile );
+ * } else {
+ * KMessageBox::error(this, TDEIO::NetAccess::lastErrorString() );
+ * }
+ * \endcode
+ *
+ * Of course, your user interface will still process exposure/repaint
+ * events during the download.
+ *
+ * If the download fails, lastError() and lastErrorString() will be set.
+ *
+ * @param src URL Reference to the file to download.
+ * @param target String containing the final local location of the
+ * file. If you insert an empty string, it will
+ * return a location in a temporary spot. <B>Note:</B>
+ * you are responsible for the removal of this file when
+ * you are finished reading it using removeTempFile.
+ * @param window main window associated with this job. This is used to
+ * automatically cache and discard authentication information
+ * as needed. If NULL, authentication information will be
+ * cached only for a short duration after which the user will
+ * again be prompted for passwords as needed.
+ * @return true if successful, false for failure. Use lastErrorString() to
+ * get the reason it failed.
+ *
+ * @see lastErrorString()
+ * @since 3.2
+ */
+ static bool download(const KURL& src, TQString & target, TQWidget* window);
+
+ /**
+ * @deprecated. Use the function above instead.
+ */
+ static bool download(const KURL& src, TQString & target) KDE_DEPRECATED;
+
+ /**
+ * Removes the specified file if and only if it was created
+ * by TDEIO::NetAccess as a temporary file for a former download.
+ *
+ * Note: This means that if you created your temporary with KTempFile,
+ * use KTempFile::unlink() or KTempFile::setAutoDelete() to have
+ * it removed.
+ *
+ * @param name Path to temporary file to remove. May not be
+ * empty.
+ */
+ static void removeTempFile(const TQString& name);
+
+ /**
+ * Uploads file @p src to URL @p target.
+ *
+ * Both must be specified, unlike download.
+ * Note that this is assumed to be used for saving a file over
+ * the network, so overwriting is set to true. This is not the
+ * case with copy.
+ *
+ * @param src URL Referencing the file to upload.
+ * @param target URL containing the final location of the file.
+ * @param window main window associated with this job. This is used to
+ * automatically cache and discard authentication information
+ * as needed. If NULL, authentication information will be cached
+ * only for a short duration after which the user will again be
+ * prompted for passwords as needed.
+ *
+ * @return true if successful, false for failure
+ * @since 3.2
+ */
+ static bool upload(const TQString& src, const KURL& target, TQWidget* window);
+
+ /**
+ * @deprecated. Use the function above instead.
+ */
+ static bool upload(const TQString& src, const KURL& target) KDE_DEPRECATED;
+
+ /**
+ * Alternative to upload for copying over the network.
+ * Overwrite is false, so this will fail if @p target exists.
+ *
+ * This one takes two URLs and is a direct equivalent
+ * of TDEIO::file_copy (not TDEIO::copy!).
+ * It will be renamed file_copy in KDE4, so better use file_copy.
+ *
+ * @param src URL Referencing the file to upload.
+ * @param target URL containing the final location of the file.
+ * @param window main window associated with this job. This is used to
+ * automatically cache and discard authentication information
+ * as needed. If NULL, authentication information will be cached
+ * only for a short duration after which the user will again be
+ * prompted for passwords as needed.
+ *
+ * @return true if successful, false for failure
+ */
+ static bool copy( const KURL& src, const KURL& target, TQWidget* window );
+ // KDE4: rename to file_copy
+
+ /**
+ * @deprecated. Use the function above instead.
+ */
+ static bool copy( const KURL& src, const KURL& target ) KDE_DEPRECATED;
+ // KDE4: merge with above
+
+ /**
+ * Full-fledged equivalent of TDEIO::file_copy
+ */
+ static bool file_copy( const KURL& src, const KURL& dest, int permissions=-1,
+ bool overwrite=false, bool resume=false, TQWidget* window = 0L );
+
+ /**
+ * Full-fledged equivalent of TDEIO::file_move.
+ * Moves or renames *one file*.
+ * @since 3.2
+ */
+ static bool file_move( const KURL& src, const KURL& target, int permissions=-1,
+ bool overwrite=false, bool resume=false, TQWidget* window = 0L );
+
+
+ /**
+ * Alternative method for copying over the network.
+ * Overwrite is false, so this will fail if @p target exists.
+ *
+ * This one takes two URLs and is a direct equivalent
+ * of TDEIO::copy!.
+ * This means that it can copy files and directories alike
+ * (it should have been named copy()).
+ *
+ * @param src URL Referencing the file to upload.
+ * @param target URL containing the final location of the
+ * file.
+ * @param window main window associated with this job. This is used to
+ * automatically cache and discard authentication information
+ * as needed. If NULL, authentication information will be cached
+ * only for a short duration after which the user will again be
+ * prompted for passwords as needed.
+ * @return true if successful, false for failure
+ */
+ static bool dircopy( const KURL& src, const KURL& target, TQWidget* window );
+
+ /**
+ * @deprecated. Use the function above instead.
+ */
+ static bool dircopy( const KURL& src, const KURL& target ) KDE_DEPRECATED; // KDE4: merge
+
+ /**
+ * Overloaded method, which takes a list of source URLs
+ */
+ static bool dircopy( const KURL::List& src, const KURL& target, TQWidget* window = 0L );
+
+ /**
+ * Full-fledged equivalent of TDEIO::move.
+ * Moves or renames one file or directory.
+ * @since 3.2
+ */
+ static bool move( const KURL& src, const KURL& target, TQWidget* window = 0L );
+
+ /**
+ * Full-fledged equivalent of TDEIO::move.
+ * Moves or renames a list of files or directories.
+ * @since 3.2
+ */
+ static bool move( const KURL::List& src, const KURL& target, TQWidget* window = 0L );
+
+ /**
+ * Tests whether a URL exists.
+ *
+ * @param url the URL we are testing
+ * @param source if true, we want to read from that URL.
+ * If false, we want to write to it.
+ * IMPORTANT: see documentation for TDEIO::stat for more details about this.
+ * @param window main window associated with this job. This is used to
+ * automatically cache and discard authentication information
+ * as needed. If NULL, authentication information will be
+ * cached only for a short duration after which the user will
+ * again be prompted for passwords as needed.
+ * @return true if the URL exists and we can do the operation specified by
+ * @p source, false otherwise
+ * @since 3.2
+ */
+ static bool exists(const KURL& url, bool source, TQWidget* window);
+
+ /**
+ * @deprecated. Use the function above instead.
+ * @since 3.2
+ */
+ static bool exists(const KURL& url, TQWidget* window) KDE_DEPRECATED;
+
+ /**
+ * @deprecated. Use the function above instead.
+ */
+ static bool exists(const KURL& url) KDE_DEPRECATED;
+
+ /**
+ * @deprecated. Use the function above instead.
+ */
+ static bool exists(const KURL& url, bool source) KDE_DEPRECATED; // KDE4: merge
+
+ /**
+ * Tests whether a URL exists and return information on it.
+ *
+ * This is a convenience function for TDEIO::stat
+ * (it saves creating a slot and testing for the job result).
+ *
+ * @param url The URL we are testing.
+ * @param entry The result of the stat. Iterate over the list
+ * of atoms to get hold of name, type, size, etc., or use KFileItem.
+ * @param window main window associated with this job. This is used to
+ * automatically cache and discard authentication information
+ * as needed. If NULL, authentication information will be
+ * cached only for a short duration after which the user will
+ * again be prompted for passwords as needed.
+ * @return true if successful, false for failure
+ */
+ static bool stat(const KURL& url, TDEIO::UDSEntry & entry, TQWidget* window);
+
+ /**
+ * @deprecated. Use the function above instead.
+ */
+ static bool stat(const KURL& url, TDEIO::UDSEntry & entry) KDE_DEPRECATED;
+
+ /**
+ * Tries to map a local URL for the given URL.
+ *
+ * This is a convenience function for TDEIO::stat + parsing the
+ * resulting UDSEntry.
+ *
+ * @param url The URL we are testing.
+ * @param window main window associated with this job. This is used to
+ * automatically cache and discard authentication information
+ * as needed. If NULL, authentication information will be
+ * cached only for a short duration after which the user will
+ * again be prompted for passwords as needed.
+ * @return a local URL corresponding to the same ressource than the
+ * original URL, or the original URL if no local URL can be mapped
+ * @since 3.5
+ */
+ static KURL mostLocalURL(const KURL& url, TQWidget* window);
+
+ /**
+ * Deletes a file or a directory in a synchronous way.
+ *
+ * This is a convenience function for TDEIO::del
+ * (it saves creating a slot and testing for the job result).
+ *
+ * @param url The file or directory to delete.
+ * @param window main window associated with this job. This is used to
+ * automatically cache and discard authentication information
+ * as needed. If NULL, authentication information will be
+ * cached only for a short duration after which the user will
+ * again be prompted for passwords as needed.
+ * @return true on success, false on failure.
+ */
+ static bool del( const KURL & url, TQWidget* window );
+
+ /**
+ * @deprecated. Use the function above instead. Passing NULL as the
+ * additional argument will give the same behaviour, but
+ * you should try to identify a suitable parent widget
+ * if at all possible.
+ */
+ static bool del( const KURL & url ) KDE_DEPRECATED;
+
+ /**
+ * Creates a directory in a synchronous way.
+ *
+ * This is a convenience function for @p TDEIO::mkdir
+ * (it saves creating a slot and testing for the job result).
+ *
+ * @param url The directory to create.
+ * @param window main window associated with this job. This is used to
+ * automatically cache and discard authentication information
+ * as needed. If NULL, authentication information will be
+ * cached only for a short duration after which the user will
+ * again be prompted for passwords as needed.
+ * @param permissions directory permissions.
+ * @return true on success, false on failure.
+ */
+ static bool mkdir( const KURL & url, TQWidget* window, int permissions = -1 );
+
+ /**
+ * @deprecated. Use the function above instead. Passing NULL as the
+ * additional argument will give the same behaviour, but
+ * you should try to identify a suitable parent widget
+ * if at all possible.
+ */
+ static bool mkdir( const KURL & url, int permissions = -1 ) KDE_DEPRECATED;
+
+ /**
+ * Executes a remote process via the fish ioslave in a synchronous way.
+ *
+ * @param url The remote machine where the command should be executed.
+ * e.g. fish://someuser\@somehost:sshport/
+ * some special cases exist.
+ * fish://someuser\@localhost/
+ * will use su instead of ssh to connect and execute the command.
+ * fish://someuser\@localhost:port/
+ * will use ssh to connect and execute the command.
+ * @param command The command to be executed.
+ * @param window main window associated with this job. This is used to
+ * automatically cache and discard authentication information
+ * as needed. If NULL, authentication information will be
+ * cached only for a short duration after which the user will
+ * again be prompted for passwords as needed.
+ * @return The resulting output of the @p command that is executed.
+ */
+ static TQString fish_execute( const KURL & url, const TQString command, TQWidget* window );
+
+ /**
+ * This function executes a job in a synchronous way.
+ * If a job fetches some data, pass a TQByteArray pointer as data parameter to this function
+ * and after the function returns it will contain all the data fetched by this job.
+ *
+ * <code>
+ * TDEIO::Job *job = TDEIO::get( url, false, false );
+ * TQMap<TQString, TQString> metaData;
+ * metaData.insert( "PropagateHttpHeader", "true" );
+ * if ( NetAccess::synchronousRun( job, 0, &data, &url, &metaData ) ) {
+ * TQString responseHeaders = metaData[ "HTTP-Headers" ];
+ * kdDebug()<<"Response header = "<< responseHeaders << endl;
+ * }
+ * </code>
+ *
+ * @param job job which the function will run. Note that after this function
+ * finishes running, job is deleted and you can't access it anymore!
+ * @param window main window associated with this job. This is used to
+ * automatically cache and discard authentication information
+ * as needed. If NULL, authentication information will be
+ * cached only for a short duration after which the user will
+ * again be prompted for passwords as needed.
+ * @param data if passed and relevant to this job then it will contain the data
+ * that was fetched by the job
+ * @param finalURL if passed will contain the final url of this job (it might differ
+ * from the one it was created with if there was a redirection)
+ * @param metaData you can pass a pointer to the map with meta data you wish to
+ * set on the job. After the job finishes this map will hold all the
+ * meta data from the job.
+ *
+ * @return true on success, false on failure.
+ *
+ * @since 3.4
+ */
+ static bool synchronousRun( Job* job, TQWidget* window, TQByteArray* data=0,
+ KURL* finalURL=0, TQMap<TQString,TQString>* metaData=0 );
+
+ /**
+ * @internal
+ * This function is not implemented!?
+ * (only mimetypeInternal)
+ *
+ * Determines the mimetype of a given URL.
+ *
+ * This is a convenience function for TDEIO::mimetype. You
+ * should call this only when really necessary.
+ * KMimeType::findByURL can determine extension a lot faster, but
+ * less reliably for remote files. Only when findByURL() returns
+ * unknown (application/octet-stream) then this one should be
+ * used.
+ *
+ * @param url The URL whose mimetype we are interested in.
+ * @param window main window associated with this job. This is used to
+ * automatically cache and discard authentication information
+ * as needed. If NULL, authentication information will be
+ * cached only for a short duration after which the user will
+ * again be prompted for passwords as needed.
+ * @return The mimetype name.
+ */
+ static TQString mimetype( const KURL & url, TQWidget* window );
+
+ /**
+ * @deprecated. Use the function above instead. Passing NULL as the
+ * additional argument will give the same behaviour, but
+ * you should try to identify a suitable parent widget
+ * if at all possible.
+ */
+ static TQString mimetype( const KURL & url ) KDE_DEPRECATED;
+
+ /**
+ * Returns the error string for the last job, in case it failed.
+ * Note that this is already translated.
+ * @return the last error string, or TQString::null
+ */
+ static TQString lastErrorString() { return lastErrorMsg ? *lastErrorMsg : TQString::null; }
+
+ /**
+ * Returns the error code for the last job, in case it failed.
+ * @return the last error code
+ * @since 3.3
+ */
+ static int lastError() { return lastErrorCode; }
+
+private:
+ /**
+ * Private constructor
+ */
+ NetAccess() : m_metaData(0), d(0) {}
+
+ /**
+ * Private destructor
+ */
+ ~NetAccess() {}
+
+ /**
+ * Internal methods
+ */
+ bool filecopyInternal(const KURL& src, const KURL& target, int permissions,
+ bool overwrite, bool resume, TQWidget* window, bool move);
+ bool dircopyInternal(const KURL::List& src, const KURL& target,
+ TQWidget* window, bool move);
+ bool statInternal(const KURL & url, int details, bool source, TQWidget* window = 0);
+
+ bool delInternal(const KURL & url, TQWidget* window = 0);
+ bool mkdirInternal(const KURL & url, int permissions, TQWidget* window = 0);
+ TQString fish_executeInternal(const KURL & url, const TQString command, TQWidget* window = 0);
+ bool synchronousRunInternal( Job* job, TQWidget* window, TQByteArray* data,
+ KURL* finalURL, TQMap<TQString,TQString>* metaData );
+
+ TQString mimetypeInternal(const KURL & url, TQWidget* window = 0);
+ void enter_loop();
+
+ /**
+ * List of temporary files
+ */
+ static TQStringList* tmpfiles;
+
+ static TQString* lastErrorMsg;
+ static int lastErrorCode;
+
+ friend class I_like_this_class;
+
+private slots:
+ void slotResult( TDEIO::Job * job );
+ void slotMimetype( TDEIO::Job * job, const TQString & type );
+ void slotData( TDEIO::Job*, const TQByteArray& );
+ void slotRedirection( TDEIO::Job*, const KURL& );
+
+private:
+ UDSEntry m_entry;
+ TQString m_mimetype;
+ TQByteArray m_data;
+ KURL m_url;
+ TQMap<TQString, TQString> *m_metaData;
+
+ /**
+ * Whether the download succeeded or not
+ */
+ bool bJobOK;
+
+private:
+ class NetAccessPrivate* d; // not really needed, the ctor is private already.
+};
+
+}
+
+#endif
diff --git a/tdeio/tdeio/observer.cpp b/tdeio/tdeio/observer.cpp
new file mode 100644
index 000000000..5e4e7aa87
--- /dev/null
+++ b/tdeio/tdeio/observer.cpp
@@ -0,0 +1,417 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Matej Koss <koss@miesto.sk>
+ David Faure <faure@kde.org>
+
+ $Id$
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <assert.h>
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <kurl.h>
+
+#include "jobclasses.h"
+#include "observer.h"
+
+#include "uiserver_stub.h"
+
+#include "passdlg.h"
+#include "slavebase.h"
+#include "observer_stub.h"
+#include <kmessagebox.h>
+#include <ksslinfodlg.h>
+#include <ksslcertdlg.h>
+#include <ksslcertificate.h>
+#include <ksslcertchain.h>
+#include <klocale.h>
+
+using namespace TDEIO;
+
+template class TQIntDict<TDEIO::Job>;
+
+Observer * Observer::s_pObserver = 0L;
+
+const int KDEBUG_OBSERVER = 7007; // Should be 7028
+
+Observer::Observer() : DCOPObject("TDEIO::Observer")
+{
+ // Register app as able to receive DCOP messages
+ if (kapp && !kapp->dcopClient()->isAttached())
+ {
+ kapp->dcopClient()->attach();
+ }
+
+ if ( !kapp->dcopClient()->isApplicationRegistered( "tdeio_uiserver" ) )
+ {
+ kdDebug(KDEBUG_OBSERVER) << "Starting tdeio_uiserver" << endl;
+ TQString error;
+ int ret = TDEApplication::startServiceByDesktopPath( "tdeio_uiserver.desktop",
+ TQStringList(), &error );
+ if ( ret > 0 )
+ {
+ kdError() << "Couldn't start tdeio_uiserver from tdeio_uiserver.desktop: " << error << endl;
+ } else
+ kdDebug(KDEBUG_OBSERVER) << "startServiceByDesktopPath returned " << ret << endl;
+
+ }
+ if ( !kapp->dcopClient()->isApplicationRegistered( "tdeio_uiserver" ) )
+ kdDebug(KDEBUG_OBSERVER) << "The application tdeio_uiserver is STILL NOT REGISTERED" << endl;
+ else
+ kdDebug(KDEBUG_OBSERVER) << "tdeio_uiserver registered" << endl;
+
+ m_uiserver = new UIServer_stub( "tdeio_uiserver", "UIServer" );
+}
+
+int Observer::newJob( TDEIO::Job * job, bool showProgress )
+{
+ // Tell the UI Server about this new job, and give it the application id
+ // at the same time
+ int progressId = m_uiserver->newJob( kapp->dcopClient()->appId(), showProgress );
+
+ // Keep the result in a dict
+ m_dctJobs.insert( progressId, job );
+
+ return progressId;
+}
+
+void Observer::jobFinished( int progressId )
+{
+ m_uiserver->jobFinished( progressId );
+ m_dctJobs.remove( progressId );
+}
+
+void Observer::killJob( int progressId )
+{
+ TDEIO::Job * job = m_dctJobs[ progressId ];
+ if (!job)
+ {
+ kdWarning() << "Can't find job to kill ! There is no job with progressId=" << progressId << " in this process" << endl;
+ return;
+ }
+ job->kill( false /* not quietly */ );
+}
+
+MetaData Observer::metadata( int progressId )
+{
+ TDEIO::Job * job = m_dctJobs[ progressId ];
+ assert(job);
+ return job->metaData();
+}
+
+void Observer::slotTotalSize( TDEIO::Job* job, TDEIO::filesize_t size )
+{
+ //kdDebug(KDEBUG_OBSERVER) << "** Observer::slotTotalSize " << job << " " << TDEIO::number(size) << endl;
+ m_uiserver->totalSize64( job->progressId(), size );
+}
+
+void Observer::slotTotalFiles( TDEIO::Job* job, unsigned long files )
+{
+ //kdDebug(KDEBUG_OBSERVER) << "** Observer::slotTotalFiles " << job << " " << files << endl;
+ m_uiserver->totalFiles( job->progressId(), files );
+}
+
+void Observer::slotTotalDirs( TDEIO::Job* job, unsigned long dirs )
+{
+ //kdDebug(KDEBUG_OBSERVER) << "** Observer::slotTotalDirs " << job << " " << dirs << endl;
+ m_uiserver->totalDirs( job->progressId(), dirs );
+}
+
+void Observer::slotProcessedSize( TDEIO::Job* job, TDEIO::filesize_t size )
+{
+ //kdDebug(KDEBUG_OBSERVER) << "** Observer::slotProcessedSize " << job << " " << job->progressId() << " " << TDEIO::number(size) << endl;
+ m_uiserver->processedSize64( job->progressId(), size );
+}
+
+void Observer::slotProcessedFiles( TDEIO::Job* job, unsigned long files )
+{
+ //kdDebug(KDEBUG_OBSERVER) << "** Observer::slotProcessedFiles " << job << " " << files << endl;
+ m_uiserver->processedFiles( job->progressId(), files );
+}
+
+void Observer::slotProcessedDirs( TDEIO::Job* job, unsigned long dirs )
+{
+ //kdDebug(KDEBUG_OBSERVER) << "** Observer::slotProcessedDirs " << job << " " << dirs << endl;
+ m_uiserver->processedDirs( job->progressId(), dirs );
+}
+
+void Observer::slotSpeed( TDEIO::Job* job, unsigned long speed )
+{
+ //kdDebug(KDEBUG_OBSERVER) << "** Observer::slotSpeed " << job << " " << speed << endl;
+ m_uiserver->speed( job->progressId(), speed );
+}
+
+void Observer::slotPercent( TDEIO::Job* job, unsigned long percent )
+{
+ //kdDebug(KDEBUG_OBSERVER) << "** Observer::slotPercent " << job << " " << percent << endl;
+ m_uiserver->percent( job->progressId(), percent );
+}
+
+void Observer::slotInfoMessage( TDEIO::Job* job, const TQString & msg )
+{
+ m_uiserver->infoMessage( job->progressId(), msg );
+}
+
+void Observer::slotCopying( TDEIO::Job* job, const KURL& from, const KURL& to )
+{
+ //kdDebug(KDEBUG_OBSERVER) << "** Observer::slotCopying " << job << " " << from.url() << " " << to.url() << endl;
+ m_uiserver->copying( job->progressId(), from, to );
+}
+
+void Observer::slotMoving( TDEIO::Job* job, const KURL& from, const KURL& to )
+{
+ //kdDebug(KDEBUG_OBSERVER) << "** Observer::slotMoving " << job << " " << from.url() << " " << to.url() << endl;
+ m_uiserver->moving( job->progressId(), from, to );
+}
+
+void Observer::slotDeleting( TDEIO::Job* job, const KURL& url )
+{
+ //kdDebug(KDEBUG_OBSERVER) << "** Observer::slotDeleting " << job << " " << url.url() << endl;
+ m_uiserver->deleting( job->progressId(), url );
+}
+
+void Observer::slotTransferring( TDEIO::Job* job, const KURL& url )
+{
+ //kdDebug(KDEBUG_OBSERVER) << "** Observer::slotTransferring " << job << " " << url.url() << endl;
+ m_uiserver->transferring( job->progressId(), url );
+}
+
+void Observer::slotCreatingDir( TDEIO::Job* job, const KURL& dir )
+{
+ //kdDebug(KDEBUG_OBSERVER) << "** Observer::slotCreatingDir " << job << " " << dir.url() << endl;
+ m_uiserver->creatingDir( job->progressId(), dir );
+}
+
+void Observer::slotCanResume( TDEIO::Job* job, TDEIO::filesize_t offset )
+{
+ //kdDebug(KDEBUG_OBSERVER) << "** Observer::slotCanResume " << job << " " << TDEIO::number(offset) << endl;
+ m_uiserver->canResume64( job->progressId(), offset );
+}
+
+void Observer::stating( TDEIO::Job* job, const KURL& url )
+{
+ m_uiserver->stating( job->progressId(), url );
+}
+
+void Observer::mounting( TDEIO::Job* job, const TQString & dev, const TQString & point )
+{
+ m_uiserver->mounting( job->progressId(), dev, point );
+}
+
+void Observer::unmounting( TDEIO::Job* job, const TQString & point )
+{
+ m_uiserver->unmounting( job->progressId(), point );
+}
+
+bool Observer::openPassDlg( const TQString& prompt, TQString& user,
+ TQString& pass, bool readOnly )
+{
+ AuthInfo info;
+ info.prompt = prompt;
+ info.username = user;
+ info.password = pass;
+ info.readOnly = readOnly;
+ bool result = openPassDlg ( info );
+ if ( result )
+ {
+ user = info.username;
+ pass = info.password;
+ }
+ return result;
+}
+
+bool Observer::openPassDlg( TDEIO::AuthInfo& info )
+{
+ kdDebug(KDEBUG_OBSERVER) << "Observer::openPassDlg: User= " << info.username
+ << ", Message= " << info.prompt << endl;
+ int result = TDEIO::PasswordDialog::getNameAndPassword( info.username, info.password,
+ &info.keepPassword, info.prompt,
+ info.readOnly, info.caption,
+ info.comment, info.commentLabel );
+ if ( result == TQDialog::Accepted )
+ {
+ info.setModified( true );
+ return true;
+ }
+ return false;
+}
+
+int Observer::messageBox( int progressId, int type, const TQString &text,
+ const TQString &caption, const TQString &buttonYes,
+ const TQString &buttonNo )
+{
+ return messageBox( progressId, type, text, caption, buttonYes, buttonNo, TQString::null );
+}
+
+int Observer::messageBox( int progressId, int type, const TQString &text,
+ const TQString &caption, const TQString &buttonYes,
+ const TQString &buttonNo, const TQString &dontAskAgainName )
+{
+ kdDebug() << "Observer::messageBox " << type << " " << text << " - " << caption << endl;
+ int result = -1;
+ TDEConfig *config = new TDEConfig("tdeioslaverc");
+ KMessageBox::setDontShowAskAgainConfig(config);
+
+ switch (type) {
+ case TDEIO::SlaveBase::QuestionYesNo:
+ result = KMessageBox::questionYesNo( 0L, // parent ?
+ text, caption, buttonYes, buttonNo, dontAskAgainName );
+ break;
+ case TDEIO::SlaveBase::WarningYesNo:
+ result = KMessageBox::warningYesNo( 0L, // parent ?
+ text, caption, buttonYes, buttonNo, dontAskAgainName );
+ break;
+ case TDEIO::SlaveBase::WarningContinueCancel:
+ result = KMessageBox::warningContinueCancel( 0L, // parent ?
+ text, caption, buttonYes, dontAskAgainName );
+ break;
+ case TDEIO::SlaveBase::WarningYesNoCancel:
+ result = KMessageBox::warningYesNoCancel( 0L, // parent ?
+ text, caption, buttonYes, buttonNo, dontAskAgainName );
+ break;
+ case TDEIO::SlaveBase::Information:
+ KMessageBox::information( 0L, // parent ?
+ text, caption, dontAskAgainName );
+ result = 1; // whatever
+ break;
+ case TDEIO::SlaveBase::SSLMessageBox:
+ {
+ TQCString observerAppId = caption.utf8(); // hack, see slaveinterface.cpp
+ // Contact the object "TDEIO::Observer" in the application <appId>
+ // Yes, this could be the same application we are, but not necessarily.
+ Observer_stub observer( observerAppId, "TDEIO::Observer" );
+
+ TDEIO::MetaData meta = observer.metadata( progressId );
+ KSSLInfoDlg *kid = new KSSLInfoDlg(meta["ssl_in_use"].upper()=="TRUE", 0L /*parent?*/, 0L, true);
+ KSSLCertificate *x = KSSLCertificate::fromString(meta["ssl_peer_certificate"].local8Bit());
+ if (x) {
+ // Set the chain back onto the certificate
+ TQStringList cl =
+ TQStringList::split(TQString("\n"), meta["ssl_peer_chain"]);
+ TQPtrList<KSSLCertificate> ncl;
+
+ ncl.setAutoDelete(true);
+ for (TQStringList::Iterator it = cl.begin(); it != cl.end(); ++it) {
+ KSSLCertificate *y = KSSLCertificate::fromString((*it).local8Bit());
+ if (y) ncl.append(y);
+ }
+
+ if (ncl.count() > 0)
+ x->chain().setChain(ncl);
+
+ kid->setup( x,
+ meta["ssl_peer_ip"],
+ text, // the URL
+ meta["ssl_cipher"],
+ meta["ssl_cipher_desc"],
+ meta["ssl_cipher_version"],
+ meta["ssl_cipher_used_bits"].toInt(),
+ meta["ssl_cipher_bits"].toInt(),
+ KSSLCertificate::KSSLValidation(meta["ssl_cert_state"].toInt()));
+ kdDebug(7024) << "Showing SSL Info dialog" << endl;
+ kid->exec();
+ delete x;
+ kdDebug(7024) << "SSL Info dialog closed" << endl;
+ } else {
+ KMessageBox::information( 0L, // parent ?
+ i18n("The peer SSL certificate appears to be corrupt."), i18n("SSL") );
+ }
+ // This doesn't have to get deleted. It deletes on it's own.
+ result = 1; // whatever
+ break;
+ }
+ default:
+ kdWarning() << "Observer::messageBox: unknown type " << type << endl;
+ result = 0;
+ break;
+ }
+ KMessageBox::setDontShowAskAgainConfig(0);
+ delete config;
+ return result;
+#if 0
+ TQByteArray data, replyData;
+ TQCString replyType;
+ TQDataStream arg( data, IO_WriteOnly );
+ arg << progressId;
+ arg << type;
+ arg << text;
+ arg << caption;
+ arg << buttonYes;
+ arg << buttonNo;
+ if ( kapp->dcopClient()->call( "tdeio_uiserver", "UIServer", "messageBox(int,int,TQString,TQString,TQString,TQString)", data, replyType, replyData, true )
+ && replyType == "int" )
+ {
+ int result;
+ TQDataStream _reply_stream( replyData, IO_ReadOnly );
+ _reply_stream >> result;
+ kdDebug(KDEBUG_OBSERVER) << "Observer::messageBox got result " << result << endl;
+ return result;
+ }
+ kdDebug(KDEBUG_OBSERVER) << "Observer::messageBox call failed" << endl;
+ return 0;
+#endif
+}
+
+RenameDlg_Result Observer::open_RenameDlg( TDEIO::Job* job,
+ const TQString & caption,
+ const TQString& src, const TQString & dest,
+ RenameDlg_Mode mode, TQString& newDest,
+ TDEIO::filesize_t sizeSrc,
+ TDEIO::filesize_t sizeDest,
+ time_t ctimeSrc,
+ time_t ctimeDest,
+ time_t mtimeSrc,
+ time_t mtimeDest
+ )
+{
+ kdDebug(KDEBUG_OBSERVER) << "Observer::open_RenameDlg job=" << job << endl;
+ if (job)
+ kdDebug(KDEBUG_OBSERVER) << " progressId=" << job->progressId() << endl;
+ // Hide existing dialog box if any
+ if (job && job->progressId())
+ m_uiserver->setJobVisible( job->progressId(), false );
+ // We now do it in process => KDE4: move this code out of Observer (back to job.cpp), so that
+ // opening the rename dialog doesn't start uiserver for nothing if progressId=0 (e.g. F2 in konq)
+ RenameDlg_Result res = TDEIO::open_RenameDlg( caption, src, dest, mode,
+ newDest, sizeSrc, sizeDest,
+ ctimeSrc, ctimeDest, mtimeSrc,
+ mtimeDest );
+ if (job && job->progressId())
+ m_uiserver->setJobVisible( job->progressId(), true );
+ return res;
+}
+
+SkipDlg_Result Observer::open_SkipDlg( TDEIO::Job* job,
+ bool _multi,
+ const TQString& _error_text )
+{
+ kdDebug(KDEBUG_OBSERVER) << "Observer::open_SkipDlg job=" << job << " progressId=" << job->progressId() << endl;
+ // Hide existing dialog box if any
+ if (job && job->progressId())
+ m_uiserver->setJobVisible( job->progressId(), false );
+ // We now do it in process. So this method is a useless wrapper around TDEIO::open_RenameDlg.
+ SkipDlg_Result res = TDEIO::open_SkipDlg( _multi, _error_text );
+ if (job && job->progressId())
+ m_uiserver->setJobVisible( job->progressId(), true );
+ return res;
+}
+
+void Observer::virtual_hook( int id, void* data )
+{ DCOPObject::virtual_hook( id, data ); }
+
+#include "observer.moc"
diff --git a/tdeio/tdeio/observer.h b/tdeio/tdeio/observer.h
new file mode 100644
index 000000000..f018bb399
--- /dev/null
+++ b/tdeio/tdeio/observer.h
@@ -0,0 +1,213 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Matej Koss <koss@miesto.sk>
+ David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __kio_observer_h__
+#define __kio_observer_h__
+
+#include <tqobject.h>
+#include <dcopobject.h>
+#include <tqintdict.h>
+
+#include <tdeio/global.h>
+#include <tdeio/authinfo.h>
+#include "tdeio/job.h"
+#include "tdeio/skipdlg.h"
+#include "tdeio/renamedlg.h"
+
+class UIServer_stub;
+class KURL;
+
+namespace TDEIO {
+ class Job;
+}
+
+/**
+ * Observer for TDEIO::Job progress information.
+ *
+ * This class, of which there is always only one instance,
+ * "observes" what jobs do and forwards this information
+ * to the progress-info server.
+ *
+ * It is a DCOP object so that the UI server can call the
+ * kill method when the user presses Cancel.
+ *
+ * Usually jobs are automatically registered by the
+ * TDEIO::Scheduler, so you do not have to care about that.
+ *
+ * @short Observer for TDEIO::Job progress information
+ * @author David Faure <faure@kde.org>
+ */
+class TDEIO_EXPORT Observer : public TQObject, public DCOPObject {
+
+ K_DCOP
+ Q_OBJECT
+
+public:
+
+ /**
+ * Returns the unique observer object.
+ * @return the observer object
+ */
+ static Observer * self() {
+ if (!s_pObserver) s_pObserver = new Observer;
+ return s_pObserver;
+ }
+
+ /**
+ * Called by the job constructor, to signal its presence to the
+ * UI Server.
+ * @param job the new job
+ * @param showProgress true to show progress, false otherwise
+ * @return the progress ID assigned by the UI Server to the Job.
+ */
+ int newJob( TDEIO::Job * job, bool showProgress );
+
+ /**
+ * Called by the job destructor, to tell the UI Server that
+ * the job ended.
+ * @param progressId the progress ID of the job, as returned by newJob()
+ */
+ void jobFinished( int progressId );
+
+ /**
+ * @deprecated use TDEIO::AutoInfo
+ */
+ bool openPassDlg( const TQString& prompt, TQString& user, TQString& pass,
+ bool readOnly );
+
+ /**
+ * Opens a password dialog.
+ * @param info the authentication information
+ * @return true if successful ("ok" clicked), false otherwise
+ */
+ bool openPassDlg( TDEIO::AuthInfo& info );
+
+ /**
+ * Popup a message box. See TDEIO::SlaveBase.
+ * This doesn't use DCOP anymore, it shows the dialog in the application's process.
+ * Otherwise, other apps would block when trying to communicate with UIServer.
+ * @param progressId the progress ID of the job, as returned by newJob()
+ * @param type the type of the message box
+ * @param text the text to show
+ * @param caption the window caption
+ * @param buttonYes the text of the "Yes" button
+ * @param buttonNo the text of the "No button
+ */
+ static int messageBox( int progressId, int type, const TQString &text, const TQString &caption,
+ const TQString &buttonYes, const TQString &buttonNo );
+
+ /**
+ * Popup a message box. See TDEIO::SlaveBase.
+ * This doesn't use DCOP anymore, it shows the dialog in the application's process.
+ * Otherwise, other apps would block when trying to communicate with UIServer.
+ * @param progressId the progress ID of the job, as returned by newJob()
+ * @param type the type of the message box
+ * @param text the text to show
+ * @param caption the window caption
+ * @param buttonYes the text of the "Yes" button
+ * @param buttonNo the text of the "No button
+ * @param dontAskAgainName A checkbox is added with which further confirmation can be turned off.
+ * The string is used to lookup and store the setting in tdeioslaverc.
+ * @since 3.3
+ */
+ static int messageBox( int progressId, int type, const TQString &text, const TQString &caption,
+ const TQString &buttonYes, const TQString &buttonNo, const TQString &dontAskAgainName );
+
+ /**
+ * @internal
+ * See renamedlg.h
+ */
+ TDEIO::RenameDlg_Result open_RenameDlg( TDEIO::Job * job,
+ const TQString & caption,
+ const TQString& src, const TQString & dest,
+ TDEIO::RenameDlg_Mode mode,
+ TQString& newDest,
+ TDEIO::filesize_t sizeSrc = (TDEIO::filesize_t) -1,
+ TDEIO::filesize_t sizeDest = (TDEIO::filesize_t) -1,
+ time_t ctimeSrc = (time_t) -1,
+ time_t ctimeDest = (time_t) -1,
+ time_t mtimeSrc = (time_t) -1,
+ time_t mtimeDest = (time_t) -1
+ );
+
+ /**
+ * @internal
+ * See skipdlg.h
+ */
+ TDEIO::SkipDlg_Result open_SkipDlg( TDEIO::Job * job,
+ bool multi,
+ const TQString & error_text );
+
+k_dcop:
+ /**
+ * Called by the UI Server (using DCOP) if the user presses cancel.
+ * @param progressId the progress ID of the job, as returned by newJob()
+ */
+ void killJob( int progressId );
+
+ /**
+ * Called by the UI Server (using DCOP) to get all the metadata of the job
+ * @param progressId the progress IDof the job, as returned by newJob()
+ */
+ TDEIO::MetaData metadata( int progressId );
+
+protected:
+
+ static Observer * s_pObserver;
+ Observer();
+ ~Observer() {}
+
+ UIServer_stub * m_uiserver;
+
+ TQIntDict< TDEIO::Job > m_dctJobs;
+
+public slots:
+
+ void slotTotalSize( TDEIO::Job*, TDEIO::filesize_t size );
+ void slotTotalFiles( TDEIO::Job*, unsigned long files );
+ void slotTotalDirs( TDEIO::Job*, unsigned long dirs );
+
+ void slotProcessedSize( TDEIO::Job*, TDEIO::filesize_t size );
+ void slotProcessedFiles( TDEIO::Job*, unsigned long files );
+ void slotProcessedDirs( TDEIO::Job*, unsigned long dirs );
+
+ void slotSpeed( TDEIO::Job*, unsigned long speed );
+ void slotPercent( TDEIO::Job*, unsigned long percent );
+ void slotInfoMessage( TDEIO::Job*, const TQString & msg );
+
+ void slotCopying( TDEIO::Job*, const KURL& from, const KURL& to );
+ void slotMoving( TDEIO::Job*, const KURL& from, const KURL& to );
+ void slotDeleting( TDEIO::Job*, const KURL& url );
+ /// @since 3.1
+ void slotTransferring( TDEIO::Job*, const KURL& url );
+ void slotCreatingDir( TDEIO::Job*, const KURL& dir );
+ // currently unused
+ void slotCanResume( TDEIO::Job*, TDEIO::filesize_t offset );
+
+public:
+ void stating( TDEIO::Job*, const KURL& url );
+ void mounting( TDEIO::Job*, const TQString & dev, const TQString & point );
+ void unmounting( TDEIO::Job*, const TQString & point );
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class ObserverPrivate* d;
+};
+
+// -*- mode: c++; c-basic-offset: 2 -*-
+#endif
diff --git a/tdeio/tdeio/passdlg.cpp b/tdeio/tdeio/passdlg.cpp
new file mode 100644
index 000000000..54bcec3b2
--- /dev/null
+++ b/tdeio/tdeio/passdlg.cpp
@@ -0,0 +1,367 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "passdlg.h"
+
+#include <tqapplication.h>
+#include <tqcheckbox.h>
+#include <tqhbox.h>
+#include <tqlabel.h>
+#include <tqlayout.h>
+#include <tqsimplerichtext.h>
+#include <tqstylesheet.h>
+
+#include <kcombobox.h>
+#include <tdeconfig.h>
+#include <kiconloader.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <kstandarddirs.h>
+
+using namespace TDEIO;
+
+struct PasswordDialog::PasswordDialogPrivate
+{
+ TQGridLayout *layout;
+ TQLineEdit* userEdit;
+ KLineEdit* passEdit;
+ TQLabel* userNameLabel;
+ TQLabel* prompt;
+ TQCheckBox* keepCheckBox;
+ TQMap<TQString,TQString> knownLogins;
+ KComboBox* userEditCombo;
+ TQHBox* userNameHBox;
+
+ bool keep;
+ short unsigned int nRow;
+};
+
+PasswordDialog::PasswordDialog( const TQString& prompt, const TQString& user,
+ bool enableKeep, bool modal, TQWidget* parent,
+ const char* name )
+ :KDialogBase( parent, name, modal, i18n("Password"), Ok|Cancel, Ok, true)
+{
+ init ( prompt, user, enableKeep );
+}
+
+PasswordDialog::~PasswordDialog()
+{
+ delete d;
+}
+
+void PasswordDialog::init( const TQString& prompt, const TQString& user,
+ bool enableKeep )
+{
+ TQWidget *main = makeMainWidget();
+
+ d = new PasswordDialogPrivate;
+ d->keep = false;
+ d->nRow = 0;
+ d->keepCheckBox = 0;
+
+ TDEConfig* cfg = TDEGlobal::config();
+ TDEConfigGroupSaver saver( cfg, "Passwords" );
+
+ d->layout = new TQGridLayout( main, 9, 3, spacingHint(), marginHint());
+ d->layout->addColSpacing(1, 5);
+
+ // Row 0: pixmap prompt
+ TQLabel* lbl;
+ TQPixmap pix( TDEGlobal::iconLoader()->loadIcon( "password", KIcon::NoGroup, KIcon::SizeHuge, 0, 0, true));
+ if ( !pix.isNull() )
+ {
+ lbl = new TQLabel( main );
+ lbl->setPixmap( pix );
+ lbl->setAlignment( Qt::AlignLeft|Qt::AlignVCenter );
+ lbl->setFixedSize( lbl->sizeHint() );
+ d->layout->addWidget( lbl, 0, 0, Qt::AlignLeft );
+ }
+ d->prompt = new TQLabel( main );
+ d->prompt->setAlignment( Qt::AlignLeft|Qt::AlignVCenter|TQt::WordBreak );
+ d->layout->addWidget( d->prompt, 0, 2, Qt::AlignLeft );
+ if ( prompt.isEmpty() )
+ setPrompt( i18n( "You need to supply a username and a password" ) );
+ else
+ setPrompt( prompt );
+
+ // Row 1: Row Spacer
+ d->layout->addRowSpacing( 1, 7 );
+
+ // Row 2-3: Reserved for an additional comment
+
+ // Row 4: Username field
+ d->userNameLabel = new TQLabel( i18n("&Username:"), main );
+ d->userNameLabel->setAlignment( Qt::AlignVCenter | Qt::AlignLeft );
+ d->userNameLabel->setFixedSize( d->userNameLabel->sizeHint() );
+ d->userNameHBox = new TQHBox( main );
+
+ d->userEdit = new KLineEdit( d->userNameHBox );
+ TQSize s = d->userEdit->sizeHint();
+ d->userEdit->setFixedHeight( s.height() );
+ d->userEdit->setMinimumWidth( s.width() );
+ d->userNameLabel->setBuddy( d->userEdit );
+ d->layout->addWidget( d->userNameLabel, 4, 0 );
+ d->layout->addWidget( d->userNameHBox, 4, 2 );
+
+ // Row 5: Row spacer
+ d->layout->addRowSpacing( 5, 4 );
+
+ // Row 6: Password field
+ lbl = new TQLabel( i18n("&Password:"), main );
+ lbl->setAlignment( Qt::AlignVCenter | Qt::AlignLeft );
+ lbl->setFixedSize( lbl->sizeHint() );
+ TQHBox* hbox = new TQHBox( main );
+ d->passEdit = new KLineEdit( hbox );
+ if ( cfg->readEntry("EchoMode", "OneStar") == "NoEcho" )
+ d->passEdit->setEchoMode( TQLineEdit::NoEcho );
+ else
+ d->passEdit->setEchoMode( TQLineEdit::Password );
+ s = d->passEdit->sizeHint();
+ d->passEdit->setFixedHeight( s.height() );
+ d->passEdit->setMinimumWidth( s.width() );
+ lbl->setBuddy( d->passEdit );
+ d->layout->addWidget( lbl, 6, 0 );
+ d->layout->addWidget( hbox, 6, 2 );
+
+ if ( enableKeep )
+ {
+ // Row 7: Add spacer
+ d->layout->addRowSpacing( 7, 4 );
+ // Row 8: Keep Password
+ hbox = new TQHBox( main );
+ d->keepCheckBox = new TQCheckBox( i18n("&Keep password"), hbox );
+ d->keepCheckBox->setFixedSize( d->keepCheckBox->sizeHint() );
+ d->keep = cfg->readBoolEntry("Keep", false );
+ d->keepCheckBox->setChecked( d->keep );
+ connect(d->keepCheckBox, TQT_SIGNAL(toggled( bool )), TQT_SLOT(slotKeep( bool )));
+ d->layout->addWidget( hbox, 8, 2 );
+ }
+
+ // Configure necessary key-bindings and connect necessar slots and signals
+ connect( d->userEdit, TQT_SIGNAL(returnPressed()), d->passEdit, TQT_SLOT(setFocus()) );
+ connect( d->passEdit, TQT_SIGNAL(returnPressed()), TQT_SLOT(slotOk()) );
+
+ if ( !user.isEmpty() )
+ {
+ d->userEdit->setText( user );
+ d->passEdit->setFocus();
+ }
+ else
+ d->userEdit->setFocus();
+
+ d->userEditCombo = 0;
+// setFixedSize( sizeHint() );
+}
+
+TQString PasswordDialog::username() const
+{
+ return d->userEdit->text();
+}
+
+TQString PasswordDialog::password() const
+{
+ return d->passEdit->text();
+}
+
+void PasswordDialog::setKeepPassword( bool b )
+{
+ if ( d->keepCheckBox )
+ d->keepCheckBox->setChecked( b );
+}
+
+bool PasswordDialog::keepPassword() const
+{
+ return d->keep;
+}
+
+static void calculateLabelSize(TQLabel *label)
+{
+ TQString qt_text = label->text();
+
+ int pref_width = 0;
+ int pref_height = 0;
+ // Calculate a proper size for the text.
+ {
+ TQSimpleRichText rt(qt_text, label->font());
+ TQRect d = TDEGlobalSettings::desktopGeometry(label->topLevelWidget());
+
+ pref_width = d.width() / 4;
+ rt.setWidth(pref_width-10);
+ int used_width = rt.widthUsed();
+ pref_height = rt.height();
+ if (used_width <= pref_width)
+ {
+ while(true)
+ {
+ int new_width = (used_width * 9) / 10;
+ rt.setWidth(new_width-10);
+ int new_height = rt.height();
+ if (new_height > pref_height)
+ break;
+ used_width = rt.widthUsed();
+ if (used_width > new_width)
+ break;
+ }
+ pref_width = used_width;
+ }
+ else
+ {
+ if (used_width > (pref_width *2))
+ pref_width = pref_width *2;
+ else
+ pref_width = used_width;
+ }
+ }
+ label->setFixedSize(TQSize(pref_width+10, pref_height));
+}
+
+void PasswordDialog::addCommentLine( const TQString& label,
+ const TQString comment )
+{
+ if (d->nRow > 0)
+ return;
+
+ TQWidget *main = mainWidget();
+
+ TQLabel* lbl = new TQLabel( label, main);
+ lbl->setAlignment( Qt::AlignVCenter|Qt::AlignRight );
+ lbl->setFixedSize( lbl->sizeHint() );
+ d->layout->addWidget( lbl, d->nRow+2, 0, Qt::AlignLeft );
+ lbl = new TQLabel( comment, main);
+ lbl->setAlignment( Qt::AlignVCenter|Qt::AlignLeft|TQt::WordBreak );
+ calculateLabelSize(lbl);
+ d->layout->addWidget( lbl, d->nRow+2, 2, Qt::AlignLeft );
+ d->layout->addRowSpacing( 3, 10 ); // Add a spacer
+ d->nRow++;
+}
+
+void PasswordDialog::slotKeep( bool keep )
+{
+ d->keep = keep;
+}
+
+static TQString qrichtextify( const TQString& text )
+{
+ if ( text.isEmpty() || text[0] == '<' )
+ return text;
+
+ TQStringList lines = TQStringList::split('\n', text);
+ for(TQStringList::Iterator it = lines.begin(); it != lines.end(); ++it)
+ {
+ *it = TQStyleSheet::convertFromPlainText( *it, TQStyleSheetItem::WhiteSpaceNormal );
+ }
+
+ return lines.join(TQString::null);
+}
+
+void PasswordDialog::setPrompt(const TQString& prompt)
+{
+ TQString text = qrichtextify(prompt);
+ d->prompt->setText(text);
+ calculateLabelSize(d->prompt);
+}
+
+void PasswordDialog::setPassword(const TQString &p)
+{
+ d->passEdit->setText(p);
+}
+
+void PasswordDialog::setUserReadOnly( bool readOnly )
+{
+ d->userEdit->setReadOnly( readOnly );
+ if ( readOnly && d->userEdit->hasFocus() )
+ d->passEdit->setFocus();
+}
+
+void PasswordDialog::setKnownLogins( const TQMap<TQString, TQString>& knownLogins )
+{
+ const int nr = knownLogins.count();
+ if ( nr == 0 )
+ return;
+ if ( nr == 1 ) {
+ d->userEdit->setText( knownLogins.begin().key() );
+ setPassword( knownLogins.begin().data() );
+ return;
+ }
+
+ Q_ASSERT( !d->userEdit->isReadOnly() );
+ if ( !d->userEditCombo ) {
+ delete d->userEdit;
+ d->userEditCombo = new KComboBox( true, d->userNameHBox );
+ d->userEdit = d->userEditCombo->lineEdit();
+ TQSize s = d->userEditCombo->sizeHint();
+ d->userEditCombo->setFixedHeight( s.height() );
+ d->userEditCombo->setMinimumWidth( s.width() );
+ d->userNameLabel->setBuddy( d->userEditCombo );
+ d->layout->addWidget( d->userNameHBox, 4, 2 );
+ }
+
+ d->knownLogins = knownLogins;
+ d->userEditCombo->insertStringList( knownLogins.keys() );
+ d->userEditCombo->setFocus();
+
+ connect( d->userEditCombo, TQT_SIGNAL( activated( const TQString& ) ),
+ this, TQT_SLOT( slotActivated( const TQString& ) ) );
+}
+
+void PasswordDialog::slotActivated( const TQString& userName )
+{
+ TQMap<TQString, TQString>::ConstIterator it = d->knownLogins.find( userName );
+ if ( it != d->knownLogins.end() )
+ setPassword( it.data() );
+}
+
+
+int PasswordDialog::getNameAndPassword( TQString& user, TQString& pass, bool* keep,
+ const TQString& prompt, bool readOnly,
+ const TQString& caption,
+ const TQString& comment,
+ const TQString& label )
+{
+ PasswordDialog* dlg;
+ if( keep )
+ dlg = new PasswordDialog( prompt, user, (*keep) );
+ else
+ dlg = new PasswordDialog( prompt, user );
+
+ if ( !caption.isEmpty() )
+ dlg->setPlainCaption( caption );
+ else
+ dlg->setPlainCaption( i18n("Authorization Dialog") );
+
+ if ( !comment.isEmpty() )
+ dlg->addCommentLine( label, comment );
+
+ if ( readOnly )
+ dlg->setUserReadOnly( readOnly );
+
+ int ret = dlg->exec();
+ if ( ret == Accepted )
+ {
+ user = dlg->username();
+ pass = dlg->password();
+ if ( keep ) { (*keep) = dlg->keepPassword(); }
+ }
+ delete dlg;
+ return ret;
+ }
+
+void PasswordDialog::virtual_hook( int id, void* data )
+{ KDialogBase::virtual_hook( id, data ); }
+
+#include "passdlg.moc"
diff --git a/tdeio/tdeio/passdlg.h b/tdeio/tdeio/passdlg.h
new file mode 100644
index 000000000..864023398
--- /dev/null
+++ b/tdeio/tdeio/passdlg.h
@@ -0,0 +1,175 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+ Copyright (C) 2000 Dawit Alemayehu <adawit@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; see the file COPYING.LIB. If
+ not, write to the Free Software Foundation, Inc., 51 Franklin Street,
+ Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kio_pass_dlg_h__
+#define __kio_pass_dlg_h__
+
+#include <kdialogbase.h>
+
+class TQGridLayout;
+
+namespace TDEIO {
+
+/**
+ * A dialog for requesting a login and a password from the end user.
+ *
+ * KIO-Slave authors are encouraged to use SlaveBase::openPassDlg
+ * instead of directly instantiating this dialog.
+ * @short dialog for requesting login and password from the end user
+ */
+class TDEIO_EXPORT PasswordDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Create a password dialog.
+ *
+ * @param prompt instructional text to be shown.
+ * @param user username, if known initially.
+ * @param enableKeep if true, shows checkbox that makes password persistent until KDE is shutdown.
+ * @param modal if true, the dialog will be modal (default:true).
+ * @param parent the parent widget (default:NULL).
+ * @param name the dialog name (default:NULL).
+ */
+ PasswordDialog( const TQString& prompt, const TQString& user,
+ bool enableKeep = false, bool modal=true,
+ TQWidget* parent=0, const char* name=0 );
+
+ /**
+ * Destructor
+ */
+ ~PasswordDialog();
+
+ /**
+ * Sets the prompt to show to the user.
+ * @param prompt instructional text to be shown.
+ */
+ void setPrompt( const TQString& prompt );
+
+ /**
+ * Adds a comment line to the dialog.
+ *
+ * This function allows you to add one additional comment
+ * line to this widget. Calling this function after a
+ * comment has already been added will not have any effect.
+ *
+ * @param label label for comment (ex:"Command:")
+ * @param comment the actual comment text.
+ */
+ void addCommentLine( const TQString& label, const TQString comment );
+
+ /**
+ * Returns the password entered by the user.
+ * @return the password
+ */
+ TQString password() const;
+
+ /**
+ * Returns the username entered by the user.
+ * @return the user name
+ */
+ TQString username() const;
+
+ /**
+ * Determines whether supplied authorization should
+ * persist even after the application has been closed.
+ * @return true to keep the password
+ */
+ bool keepPassword() const;
+
+ /**
+ * Check or uncheck the "keep password" checkbox.
+ * This can be used to check it before showing the dialog, to tell
+ * the user that the password is stored already (e.g. in the wallet).
+ * enableKeep must have been set to true in the constructor.
+ */
+ void setKeepPassword( bool b );
+
+ /**
+ * Sets the username field read-only and sets the
+ * focus to the password field.
+ *
+ * @param readOnly true to set the user field to read-only
+ */
+ void setUserReadOnly( bool readOnly );
+
+ /**
+ * @deprecated. Use setUserReadOnly(bool).
+ */
+ KDE_DEPRECATED void setEnableUserField( bool enable, bool=false ) {
+ setUserReadOnly( !enable );
+ };
+
+ /**
+ * Presets the password.
+ * @param password the password to set
+ * @since 3.1
+ */
+ void setPassword( const TQString& password );
+
+ /**
+ * Presets a number of login+password pairs that the user can choose from.
+ * The passwords can be empty if you simply want to offer usernames to choose from.
+ * This is incompatible with setUserReadOnly(true).
+ * @param knownLogins map of known logins: the keys are usernames, the values are passwords.
+ * @since 3.4
+ */
+ void setKnownLogins( const TQMap<TQString, TQString>& knownLogins );
+
+ /**
+ * A convienence static method for obtaining authorization
+ * information from the end user.
+ *
+ *
+ * @param user username
+ * @param pass password
+ * @param keep pointer to flag that indicates whether to keep password (can be null)
+ * @param prompt text to display to user.
+ * @param readOnly make the username field read-only.
+ * @param caption set the title bar to given text.
+ * @param comment extra comment to display to user.
+ * @param label optinal label for extra comment.
+ *
+ * @return Accepted/Rejected based on the user choice.
+ */
+ static int getNameAndPassword( TQString& user, TQString& pass, bool* keep,
+ const TQString& prompt = TQString::null,
+ bool readOnly = false,
+ const TQString& caption = TQString::null,
+ const TQString& comment = TQString::null,
+ const TQString& label = TQString::null );
+
+private slots:
+ void slotKeep( bool );
+ void slotActivated( const TQString& userName );
+
+private:
+ void init( const TQString&, const TQString&, bool );
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ struct PasswordDialogPrivate;
+ PasswordDialogPrivate* d;
+};
+
+}
+
+#endif
diff --git a/tdeio/tdeio/paste.cpp b/tdeio/tdeio/paste.cpp
new file mode 100644
index 000000000..fd24f9a0d
--- /dev/null
+++ b/tdeio/tdeio/paste.cpp
@@ -0,0 +1,308 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "paste.h"
+#include "pastedialog.h"
+
+#include "tdeio/job.h"
+#include "tdeio/global.h"
+#include "tdeio/netaccess.h"
+#include "tdeio/observer.h"
+#include "tdeio/renamedlg.h"
+#include "tdeio/kprotocolmanager.h"
+
+#include <kurl.h>
+#include <kurldrag.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kinputdialog.h>
+#include <kmessagebox.h>
+#include <kmimetype.h>
+#include <ktempfile.h>
+
+#include <tqapplication.h>
+#include <tqclipboard.h>
+#include <tqdragobject.h>
+#include <tqtextstream.h>
+#include <tqvaluevector.h>
+
+static KURL getNewFileName( const KURL &u, const TQString& text )
+{
+ bool ok;
+ TQString dialogText( text );
+ if ( dialogText.isEmpty() )
+ dialogText = i18n( "Filename for clipboard content:" );
+ TQString file = KInputDialog::getText( TQString::null, dialogText, TQString::null, &ok );
+ if ( !ok )
+ return KURL();
+
+ KURL myurl(u);
+ myurl.addPath( file );
+
+ if (TDEIO::NetAccess::exists(myurl, false, 0))
+ {
+ kdDebug(7007) << "Paste will overwrite file. Prompting..." << endl;
+ TDEIO::RenameDlg_Result res = TDEIO::R_OVERWRITE;
+
+ TQString newPath;
+ // Ask confirmation about resuming previous transfer
+ res = Observer::self()->open_RenameDlg(
+ 0L, i18n("File Already Exists"),
+ u.pathOrURL(),
+ myurl.pathOrURL(),
+ (TDEIO::RenameDlg_Mode) (TDEIO::M_OVERWRITE | TDEIO::M_SINGLE), newPath);
+
+ if ( res == TDEIO::R_RENAME )
+ {
+ myurl = newPath;
+ }
+ else if ( res == TDEIO::R_CANCEL )
+ {
+ return KURL();
+ }
+ }
+
+ return myurl;
+}
+
+// The finaly step: write _data to tempfile and move it to neW_url
+static TDEIO::CopyJob* pasteDataAsyncTo( const KURL& new_url, const TQByteArray& _data )
+{
+ KTempFile tempFile;
+ tempFile.dataStream()->writeRawBytes( _data.data(), _data.size() );
+ tempFile.close();
+
+ KURL orig_url;
+ orig_url.setPath(tempFile.name());
+
+ return TDEIO::move( orig_url, new_url );
+}
+
+#ifndef QT_NO_MIMECLIPBOARD
+static TDEIO::CopyJob* chooseAndPaste( const KURL& u, TQMimeSource* data,
+ const TQValueVector<TQCString>& formats,
+ const TQString& text,
+ TQWidget* widget,
+ bool clipboard )
+{
+ TQStringList formatLabels;
+ for ( uint i = 0; i < formats.size(); ++i ) {
+ const TQCString& fmt = formats[i];
+ KMimeType::Ptr mime = KMimeType::mimeType( fmt );
+ if ( mime != KMimeType::defaultMimeTypePtr() )
+ formatLabels.append( i18n( "%1 (%2)" ).arg( mime->comment() ).arg( fmt.data() ) );
+ else
+ formatLabels.append( fmt );
+ }
+
+ TQString dialogText( text );
+ if ( dialogText.isEmpty() )
+ dialogText = i18n( "Filename for clipboard content:" );
+ TDEIO::PasteDialog dlg( TQString::null, dialogText, TQString::null, formatLabels, widget, clipboard );
+
+ if ( dlg.exec() != KDialogBase::Accepted )
+ return 0;
+
+ if ( clipboard && dlg.clipboardChanged() ) {
+ KMessageBox::sorry( widget,
+ i18n( "The clipboard has changed since you used 'paste': "
+ "the chosen data format is no longer applicable. "
+ "Please copy again what you wanted to paste." ) );
+ return 0;
+ }
+
+ const TQString result = dlg.lineEditText();
+ const TQCString chosenFormat = formats[ dlg.comboItem() ];
+
+ kdDebug() << " result=" << result << " chosenFormat=" << chosenFormat << endl;
+ KURL new_url( u );
+ new_url.addPath( result );
+ // if "data" came from TQClipboard, then it was deleted already - by a nice 0-seconds timer
+ // In that case, get it again. Let's hope the user didn't copy something else meanwhile :/
+ if ( clipboard ) {
+ data = TQApplication::clipboard()->data();
+ }
+ const TQByteArray ba = data->encodedData( chosenFormat );
+ return pasteDataAsyncTo( new_url, ba );
+}
+#endif
+
+// KDE4: remove
+TDEIO_EXPORT bool TDEIO::isClipboardEmpty()
+{
+#ifndef QT_NO_MIMECLIPBOARD
+ TQMimeSource *data = TQApplication::clipboard()->data();
+ if ( data->provides( "text/uri-list" ) && data->encodedData( "text/uri-list" ).size() > 0 )
+ return false;
+#else
+ // Happens with some versions of Qt Embedded... :/
+ // Guess.
+ TQString data = TQApplication::clipboard()->text();
+ if(data.contains("://"))
+ return false;
+#endif
+ return true;
+}
+
+#ifndef QT_NO_MIMECLIPBOARD
+// The main method for dropping
+TDEIO::CopyJob* TDEIO::pasteMimeSource( TQMimeSource* data, const KURL& dest_url,
+ const TQString& dialogText, TQWidget* widget, bool clipboard )
+{
+ TQByteArray ba;
+
+ // Now check for plain text
+ // We don't want to display a mimetype choice for a TQTextDrag, those mimetypes look ugly.
+ TQString text;
+ if ( TQTextDrag::canDecode( data ) && TQTextDrag::decode( data, text ) )
+ {
+ TQTextStream txtStream( ba, IO_WriteOnly );
+ txtStream << text;
+ }
+ else
+ {
+ TQValueVector<TQCString> formats;
+ const char* fmt;
+ for ( int i = 0; ( fmt = data->format( i ) ); ++i ) {
+ if ( qstrcmp( fmt, "application/x-qiconlist" ) == 0 ) // see QIconDrag
+ continue;
+ if ( qstrcmp( fmt, "application/x-kde-cutselection" ) == 0 ) // see KonqDrag
+ continue;
+ if ( strchr( fmt, '/' ) == 0 ) // e.g. TARGETS, MULTIPLE, TIMESTAMP
+ continue;
+ formats.append( fmt );
+ }
+
+ if ( formats.size() == 0 )
+ return 0;
+
+ if ( formats.size() > 1 ) {
+ return chooseAndPaste( dest_url, data, formats, dialogText, widget, clipboard );
+ }
+ ba = data->encodedData( formats.first() );
+ }
+ if ( ba.size() == 0 )
+ {
+ KMessageBox::sorry(0, i18n("The clipboard is empty"));
+ return 0;
+ }
+
+ return pasteDataAsync( dest_url, ba, dialogText );
+}
+#endif
+
+// The main method for pasting
+TDEIO_EXPORT TDEIO::Job *TDEIO::pasteClipboard( const KURL& dest_url, bool move )
+{
+ if ( !dest_url.isValid() ) {
+ KMessageBox::error( 0L, i18n( "Malformed URL\n%1" ).arg( dest_url.url() ) );
+ return 0;
+ }
+
+#ifndef QT_NO_MIMECLIPBOARD
+ TQMimeSource *data = TQApplication::clipboard()->data();
+
+ // First check for URLs.
+ KURL::List urls;
+ if ( KURLDrag::canDecode( data ) && KURLDrag::decode( data, urls ) ) {
+ if ( urls.count() == 0 ) {
+ KMessageBox::error( 0L, i18n("The clipboard is empty"));
+ return 0;
+ }
+
+ TDEIO::Job *res = 0;
+ if ( move )
+ res = TDEIO::move( urls, dest_url );
+ else
+ res = TDEIO::copy( urls, dest_url );
+
+ // If moving, erase the clipboard contents, the original files don't exist anymore
+ if ( move )
+ TQApplication::clipboard()->clear();
+ return res;
+ }
+ return pasteMimeSource( data, dest_url, TQString::null, 0 /*TODO parent widget*/, true /*clipboard*/ );
+#else
+ TQByteArray ba;
+ TQTextStream txtStream( ba, IO_WriteOnly );
+ TQStringList data = TQStringList::split("\n", TQApplication::clipboard()->text());
+ KURL::List urls;
+ KURLDrag::decode(data, urls);
+ TQStringList::Iterator end(data.end());
+ for(TQStringList::Iterator it=data.begin(); it!=end; ++it)
+ txtStream << *it;
+ if ( ba.size() == 0 )
+ {
+ KMessageBox::sorry(0, i18n("The clipboard is empty"));
+ return 0;
+ }
+ return pasteDataAsync( dest_url, ba );
+#endif
+}
+
+
+TDEIO_EXPORT void TDEIO::pasteData( const KURL& u, const TQByteArray& _data )
+{
+ KURL new_url = getNewFileName( u, TQString::null );
+ // We could use TDEIO::put here, but that would require a class
+ // for the slotData call. With NetAccess, we can do a synchronous call.
+
+ if (new_url.isEmpty())
+ return;
+
+ KTempFile tempFile;
+ tempFile.setAutoDelete( true );
+ tempFile.dataStream()->writeRawBytes( _data.data(), _data.size() );
+ tempFile.close();
+
+ (void) TDEIO::NetAccess::upload( tempFile.name(), new_url, 0 );
+}
+
+TDEIO_EXPORT TDEIO::CopyJob* TDEIO::pasteDataAsync( const KURL& u, const TQByteArray& _data )
+{
+ return pasteDataAsync( u, _data, TQString::null );
+}
+
+TDEIO_EXPORT TDEIO::CopyJob* TDEIO::pasteDataAsync( const KURL& u, const TQByteArray& _data, const TQString& text )
+{
+ KURL new_url = getNewFileName( u, text );
+
+ if (new_url.isEmpty())
+ return 0;
+
+ return pasteDataAsyncTo( new_url, _data );
+}
+
+TDEIO_EXPORT TQString TDEIO::pasteActionText()
+{
+ TQMimeSource *data = TQApplication::clipboard()->data();
+ KURL::List urls;
+ if ( KURLDrag::canDecode( data ) && KURLDrag::decode( data, urls ) ) {
+ if ( urls.isEmpty() )
+ return TQString::null; // nothing to paste
+ else if ( urls.first().isLocalFile() )
+ return i18n( "&Paste File", "&Paste %n Files", urls.count() );
+ else
+ return i18n( "&Paste URL", "&Paste %n URLs", urls.count() );
+ } else if ( data->format(0) != 0 ) {
+ return i18n( "&Paste Clipboard Contents" );
+ } else {
+ return TQString::null;
+ }
+}
+
diff --git a/tdeio/tdeio/paste.h b/tdeio/tdeio/paste.h
new file mode 100644
index 000000000..66579831a
--- /dev/null
+++ b/tdeio/tdeio/paste.h
@@ -0,0 +1,125 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000-2005 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kio_paste_h__
+#define __kio_paste_h__
+
+#include <tqstring.h>
+#include <tqmemarray.h>
+#include <kurl.h>
+class TQWidget;
+class TQMimeSource;
+
+// KDE4 TODO pass a parent widget to all methods that will display a message box
+
+namespace TDEIO {
+ class Job;
+ class CopyJob;
+
+ /**
+ * Pastes the content of the clipboard to the given destination URL.
+ * URLs are treated separately (performing a file copy)
+ * from other data (which is saved into a file after asking the user
+ * to choose a filename and the preferred data format)
+ *
+ * @param destURL the URL to receive the data
+ * @param move true to move the data, false to copy
+ * @return the job that handles the operation
+ * @see pasteData()
+ */
+ TDEIO_EXPORT Job *pasteClipboard( const KURL& destURL, bool move = false );
+
+ /**
+ * Pastes the given @p data to the given destination URL.
+ * NOTE: This method is blocking (uses NetAccess for saving the data).
+ * Please consider using pasteDataAsync instead.
+ *
+ * @param destURL the URL of the directory where the data will be pasted.
+ * The filename to use in that directory is prompted by this method.
+ * @param data the data to copy
+ * @see pasteClipboard()
+ */
+ TDEIO_EXPORT void pasteData( const KURL& destURL, const TQByteArray& data );
+
+ /**
+ * Pastes the given @p data to the given destination URL.
+ * Note that this method requires the caller to have chosen the QByteArray
+ * to paste before hand, unlike pasteClipboard and pasteMimeSource.
+ *
+ * @param destURL the URL of the directory where the data will be pasted.
+ * The filename to use in that directory is prompted by this method.
+ * @param data the data to copy
+ * @see pasteClipboard()
+ */
+ TDEIO_EXPORT CopyJob *pasteDataAsync( const KURL& destURL, const TQByteArray& data );
+
+ /**
+ * Pastes the given @p data to the given destination URL.
+ * Note that this method requires the caller to have chosen the QByteArray
+ * to paste before hand, unlike pasteClipboard and pasteMimeSource.
+ *
+ * @param destURL the URL of the directory where the data will be pasted.
+ * The filename to use in that directory is prompted by this method.
+ * @param data the data to copy
+ * @param dialogText the text to show in the dialog
+ * @see pasteClipboard()
+ */
+ TDEIO_EXPORT CopyJob *pasteDataAsync( const KURL& destURL, const TQByteArray& data, const TQString& dialogText ); // KDE4: merge with above
+
+
+ /**
+ * Save the given mimesource @p data to the given destination URL
+ * after offering the user to choose a data format.
+ * This is the method used when handling drops (of anything else than URLs)
+ * onto kdesktop and konqueror.
+ *
+ * @param data the TQMimeSource (e.g. a TQDropEvent)
+ * @param destURL the URL of the directory where the data will be pasted.
+ * The filename to use in that directory is prompted by this method.
+ * @param dialogText the text to show in the dialog
+ * @param widget parent widget to use for dialogs
+ * @param clipboard whether the TQMimeSource comes from TQClipboard. If you
+ * use pasteClipboard for that case, you never have to worry about this parameter.
+ *
+ * @see pasteClipboard()
+ *
+ * @since 3.5
+ */
+ TDEIO_EXPORT CopyJob* pasteMimeSource( TQMimeSource* data, const KURL& destURL,
+ const TQString& dialogText, TQWidget* widget,
+ bool clipboard = false );
+
+ /**
+ * Checks whether the clipboard contains any URLs.
+ * @return true if not
+ * Not used anymore, wrong method name, so it will disappear in KDE4.
+ */
+ TDEIO_EXPORT_DEPRECATED bool isClipboardEmpty();
+
+ /**
+ * Returns the text to use for the Paste action, when the application supports
+ * pasting files, urls, and clipboard data, using pasteClipboard().
+ * @return a string suitable for KAction::setText, or an empty string if pasting
+ * isn't possible right now.
+ *
+ * @since 3.5
+ */
+ TDEIO_EXPORT TQString pasteActionText();
+}
+
+#endif
diff --git a/tdeio/tdeio/pastedialog.cpp b/tdeio/tdeio/pastedialog.cpp
new file mode 100644
index 000000000..0252baff1
--- /dev/null
+++ b/tdeio/tdeio/pastedialog.cpp
@@ -0,0 +1,84 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2005 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "pastedialog.h"
+
+#include <klineedit.h>
+#include <kmimetype.h>
+#include <klocale.h>
+
+#include <tqlayout.h>
+#include <tqlabel.h>
+#include <tqcombobox.h>
+#include <tqapplication.h>
+#include <tqclipboard.h>
+
+TDEIO::PasteDialog::PasteDialog( const TQString &caption, const TQString &label,
+ const TQString &value, const TQStringList& items,
+ TQWidget *parent,
+ bool clipboard )
+ : KDialogBase( parent, 0 /*name*/, true, caption, Ok|Cancel, Ok, true )
+{
+ TQFrame *frame = makeMainWidget();
+ TQVBoxLayout *layout = new TQVBoxLayout( frame, 0, spacingHint() );
+
+ m_label = new TQLabel( label, frame );
+ layout->addWidget( m_label );
+
+ m_lineEdit = new KLineEdit( value, frame );
+ layout->addWidget( m_lineEdit );
+
+ m_lineEdit->setFocus();
+ m_label->setBuddy( m_lineEdit );
+
+ layout->addWidget( new TQLabel( i18n( "Data format:" ), frame ) );
+ m_comboBox = new TQComboBox( frame );
+ m_comboBox->insertStringList( items );
+ layout->addWidget( m_comboBox );
+
+ layout->addStretch();
+
+ //connect( m_lineEdit, TQT_SIGNAL( textChanged( const TQString & ) ),
+ // TQT_SLOT( slotEditTextChanged( const TQString & ) ) );
+ //connect( this, TQT_SIGNAL( user1Clicked() ), m_lineEdit, TQT_SLOT( clear() ) );
+
+ //slotEditTextChanged( value );
+ setMinimumWidth( 350 );
+
+ m_clipboardChanged = false;
+ if ( clipboard )
+ connect( TQApplication::clipboard(), TQT_SIGNAL( dataChanged() ),
+ this, TQT_SLOT( slotClipboardDataChanged() ) );
+}
+
+void TDEIO::PasteDialog::slotClipboardDataChanged()
+{
+ m_clipboardChanged = true;
+}
+
+TQString TDEIO::PasteDialog::lineEditText() const
+{
+ return m_lineEdit->text();
+}
+
+int TDEIO::PasteDialog::comboItem() const
+{
+ return m_comboBox->currentItem();
+}
+
+#include "pastedialog.moc"
diff --git a/tdeio/tdeio/pastedialog.h b/tdeio/tdeio/pastedialog.h
new file mode 100644
index 000000000..2e7bdfda1
--- /dev/null
+++ b/tdeio/tdeio/pastedialog.h
@@ -0,0 +1,64 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2005 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef PASTEDIALOG_H
+#define PASTEDIALOG_H
+
+#include <kdialogbase.h>
+
+class TQComboBox;
+class KLineEdit;
+class TQLabel;
+
+namespace TDEIO {
+
+/**
+ * @internal
+ * Internal class used by paste.h. DO NOT USE.
+ * @since 3.5
+ */
+class PasteDialog : public KDialogBase
+{
+ Q_OBJECT
+public:
+ PasteDialog( const TQString &caption, const TQString &label,
+ const TQString &value, const TQStringList& items,
+ TQWidget *parent, bool clipboard );
+
+ TQString lineEditText() const;
+ int comboItem() const;
+ bool clipboardChanged() const { return m_clipboardChanged; }
+
+private slots:
+ void slotClipboardDataChanged();
+
+private:
+ TQLabel* m_label;
+ KLineEdit* m_lineEdit;
+ TQComboBox* m_comboBox;
+ bool m_clipboardChanged;
+
+ class Private;
+ Private* d;
+};
+
+} // namespace
+
+
+#endif /* PASTEDIALOG_H */
+
diff --git a/tdeio/tdeio/posixacladdons.cpp b/tdeio/tdeio/posixacladdons.cpp
new file mode 100644
index 000000000..bae51592b
--- /dev/null
+++ b/tdeio/tdeio/posixacladdons.cpp
@@ -0,0 +1,236 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Markus Brueffer <markus@brueffer.de> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#include "posixacladdons.h"
+
+#if defined(USE_POSIX_ACL) && !defined(HAVE_NON_POSIX_ACL_EXTENSIONS)
+
+#include <errno.h>
+#include <sys/stat.h>
+
+#include <tqptrlist.h>
+
+class SortedEntryList : public TQPtrList<acl_entry_t>
+{
+protected:
+ int compareItems( TQPtrCollection::Item i1,
+ TQPtrCollection::Item i2 )
+ {
+ acl_entry_t *e1 = static_cast<acl_entry_t*>( i1 );
+ acl_entry_t *e2 = static_cast<acl_entry_t*>( i2 );
+
+ acl_tag_t tag1, tag2;
+ uid_t uid1 = 0, uid2 = 0;
+
+ acl_get_tag_type( *e1, &tag1 );
+ acl_get_tag_type( *e2, &tag2 );
+
+ if ( tag1 == ACL_USER || tag1 == ACL_GROUP )
+ uid1 = *( (uid_t*) acl_get_qualifier( *e1 ) );
+
+ if ( tag2 == ACL_USER || tag2 == ACL_GROUP )
+ uid2 = *( (uid_t*) acl_get_qualifier( *e2 ) );
+
+ if ( tag1 < tag2 )
+ return -1;
+ else if ( tag1 > tag2 )
+ return 1;
+
+ if ( uid1 < uid2 )
+ return -1;
+ else if ( uid1 > uid2 )
+ return 1;
+
+ return 0;
+ }
+};
+
+int acl_cmp(acl_t acl1, acl_t acl2)
+{
+ if ( !acl1 || !acl2 )
+ return -1;
+
+ SortedEntryList entries1, entries2;
+ entries1.setAutoDelete( true );
+ entries2.setAutoDelete( true );
+
+ /* Add ACL entries to vectors */
+ acl_entry_t *entry = new acl_entry_t;
+ int ret = acl_get_entry( acl1, ACL_FIRST_ENTRY, entry );
+ while( ret == 1 ) {
+ entries1.append( entry );
+ entry = new acl_entry_t;
+ ret = acl_get_entry( acl1, ACL_NEXT_ENTRY, entry );
+ }
+ delete entry;
+
+ entry = new acl_entry_t;
+ ret = acl_get_entry( acl2, ACL_FIRST_ENTRY, entry );
+ while ( ret == 1 ) {
+ entries2.append( entry );
+ entry = new acl_entry_t;
+ ret = acl_get_entry( acl2, ACL_NEXT_ENTRY, entry );
+ }
+ delete entry;
+
+ /* If the entry count differs, we are done */
+ if ( entries1.count() != entries2.count() )
+ return 1;
+
+ /* Sort vectors */
+ entries1.sort();
+ entries2.sort();
+
+ /* Compare all entries */
+ acl_permset_t permset1, permset2;
+ acl_tag_t tag1, tag2;
+ uid_t uid1, uid2;
+ acl_entry_t *e1, *e2;
+
+ for ( e1 = entries1.first(), e2 = entries2.first(); e1; e1 = entries1.next(), e2 = entries2.next() ) {
+ /* Compare tag */
+ if ( acl_get_tag_type( *e1, &tag1 ) != 0 ) return 1;
+ if ( acl_get_tag_type( *e2, &tag2 ) != 0 ) return 1;
+ if ( tag1 != tag2 ) return 1;
+
+ /* Compare permissions */
+ if ( acl_get_permset( *e1, &permset1 ) != 0 ) return 1;
+ if ( acl_get_permset( *e2, &permset2 ) != 0 ) return 1;
+ if ( *permset1 != *permset2) return 1;
+
+ /* Compare uid */
+ switch( tag1 ) {
+ case ACL_USER:
+ case ACL_GROUP:
+ uid1 = *( (uid_t*) acl_get_qualifier( *e1 ) );
+ uid2 = *( (uid_t*) acl_get_qualifier( *e2 ) );
+ if ( uid1 != uid2 ) return 1;
+ }
+ }
+
+ return 0;
+}
+
+acl_t acl_from_mode(mode_t mode)
+{
+ acl_t newACL = acl_init( 3 );
+ acl_entry_t entry;
+ acl_permset_t permset;
+ int error = 0;
+
+ /* Add owner entry */
+ if ( ( error = acl_create_entry( &newACL, &entry ) ) == 0 ) {
+ /* Set owner permissions */
+ acl_set_tag_type( entry, ACL_USER_OBJ );
+ acl_get_permset( entry, &permset );
+ acl_clear_perms( permset );
+ if ( mode & S_IRUSR ) acl_add_perm( permset, ACL_READ );
+ if ( mode & S_IWUSR ) acl_add_perm( permset, ACL_WRITE );
+ if ( mode & S_IXUSR ) acl_add_perm( permset, ACL_EXECUTE );
+ acl_set_permset( entry, permset );
+
+ /* Add group entry */
+ if ( ( error = acl_create_entry( &newACL, &entry ) ) == 0 ) {
+ /* Set group permissions */
+ acl_set_tag_type( entry, ACL_GROUP_OBJ );
+ acl_get_permset( entry, &permset );
+ acl_clear_perms( permset );
+ if ( mode & S_IRGRP ) acl_add_perm( permset, ACL_READ );
+ if ( mode & S_IWGRP ) acl_add_perm( permset, ACL_WRITE );
+ if ( mode & S_IXGRP ) acl_add_perm( permset, ACL_EXECUTE );
+ acl_set_permset( entry, permset );
+
+ /* Add other entry */
+ if ( ( error = acl_create_entry( &newACL, &entry ) ) == 0) {
+ /* Set other permissions */
+ acl_set_tag_type( entry, ACL_OTHER );
+ acl_get_permset( entry, &permset );
+ acl_clear_perms( permset );
+ if ( mode & S_IROTH ) acl_add_perm( permset, ACL_READ );
+ if ( mode & S_IWOTH ) acl_add_perm( permset, ACL_WRITE );
+ if ( mode & S_IXOTH ) acl_add_perm( permset, ACL_EXECUTE );
+ acl_set_permset( entry, permset );
+ }
+ }
+ }
+
+ if ( error ) {
+ acl_free ( &newACL );
+ return NULL;
+ }
+
+ return newACL;
+}
+
+int acl_equiv_mode(acl_t acl, mode_t *mode_p)
+{
+ acl_entry_t entry;
+ acl_tag_t tag;
+ acl_permset_t permset;
+ mode_t mode = 0;
+ int notEquiv = 0;
+
+ if ( !acl )
+ return -1;
+
+ int ret = acl_get_entry( acl, ACL_FIRST_ENTRY, &entry );
+ while ( ret == 1 ) {
+ acl_get_tag_type( entry, &tag );
+ acl_get_permset( entry, &permset );
+
+ switch( tag ) {
+ case ACL_USER_OBJ:
+ if ( acl_get_perm( permset, ACL_READ ) ) mode |= S_IRUSR;
+ if ( acl_get_perm( permset, ACL_WRITE ) ) mode |= S_IWUSR;
+ if ( acl_get_perm( permset, ACL_EXECUTE ) ) mode |= S_IXUSR;
+ break;
+
+ case ACL_GROUP_OBJ:
+ if ( acl_get_perm( permset, ACL_READ ) ) mode |= S_IRGRP;
+ if ( acl_get_perm( permset, ACL_WRITE ) ) mode |= S_IWGRP;
+ if ( acl_get_perm( permset, ACL_EXECUTE ) ) mode |= S_IXGRP;
+ break;
+
+ case ACL_OTHER:
+ if ( acl_get_perm( permset, ACL_READ ) ) mode |= S_IROTH;
+ if ( acl_get_perm( permset, ACL_WRITE ) ) mode |= S_IWOTH;
+ if ( acl_get_perm( permset, ACL_EXECUTE ) ) mode |= S_IXOTH;
+ break;
+
+ case ACL_USER:
+ case ACL_GROUP:
+ case ACL_MASK:
+ notEquiv = 1;
+ break;
+
+ default:
+ errno = EINVAL;
+ return -1;
+ }
+
+ ret = acl_get_entry( acl, ACL_NEXT_ENTRY, &entry );
+ }
+
+ if (mode_p)
+ *mode_p = mode;
+
+ return notEquiv;
+}
+
+#endif // USE_POSIX_ACL
diff --git a/tdeio/tdeio/posixacladdons.h b/tdeio/tdeio/posixacladdons.h
new file mode 100644
index 000000000..45c4af245
--- /dev/null
+++ b/tdeio/tdeio/posixacladdons.h
@@ -0,0 +1,47 @@
+/***************************************************************************
+ * Copyright (C) 2005 by Markus Brueffer <markus@brueffer.de> *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU Library General Public License as *
+ * published by the Free Software Foundation; either version 2 of the *
+ * License, or (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
+ ***************************************************************************/
+
+#ifndef __posixacladdons_h__
+#define __posixacladdons_h__
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <tqglobal.h>
+
+#if defined(USE_POSIX_ACL) && !defined(HAVE_NON_POSIX_ACL_EXTENSIIONS)
+
+#ifdef Q_OS_FREEBSD
+#include <sys/types.h>
+#endif
+
+#include <sys/acl.h>
+
+#ifdef Q_OS_FREEBSD
+#define acl_get_perm acl_get_perm_np
+#endif
+
+int acl_cmp(acl_t acl1, acl_t acl2);
+acl_t acl_from_mode(mode_t mode);
+int acl_equiv_mode(acl_t acl, mode_t *mode_p);
+
+#endif // USE_POSIX_ACL
+
+#endif // __posixacladdons_h__
diff --git a/tdeio/tdeio/previewjob.cpp b/tdeio/tdeio/previewjob.cpp
new file mode 100644
index 000000000..539d88bea
--- /dev/null
+++ b/tdeio/tdeio/previewjob.cpp
@@ -0,0 +1,597 @@
+// -*- c++ -*-
+// vim: ts=4 sw=4 et
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+ 2000 Carsten Pfeiffer <pfeiffer@kde.org>
+ 2001 Malte Starostik <malte.starostik@t-online.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "previewjob.h"
+
+#include <sys/stat.h>
+#ifdef __FreeBSD__
+ #include <machine/param.h>
+#endif
+#include <sys/types.h>
+
+#ifdef Q_OS_UNIX
+#include <sys/ipc.h>
+#include <sys/shm.h>
+#endif
+
+#include <tqdir.h>
+#include <tqfile.h>
+#include <tqimage.h>
+#include <tqtimer.h>
+#include <tqregexp.h>
+
+#include <kdatastream.h> // Do not remove, needed for correct bool serialization
+#include <tdefileitem.h>
+#include <kapplication.h>
+#include <ktempfile.h>
+#include <ktrader.h>
+#include <kmdcodec.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+
+#include <tdeio/kservice.h>
+
+#include "previewjob.moc"
+
+namespace TDEIO { struct PreviewItem; }
+using namespace TDEIO;
+
+struct TDEIO::PreviewItem
+{
+ KFileItem *item;
+ KService::Ptr plugin;
+};
+
+struct TDEIO::PreviewJobPrivate
+{
+ enum { STATE_STATORIG, // if the thumbnail exists
+ STATE_GETORIG, // if we create it
+ STATE_CREATETHUMB // thumbnail:/ slave
+ } state;
+ KFileItemList initialItems;
+ const TQStringList *enabledPlugins;
+ // Our todo list :)
+ TQValueList<PreviewItem> items;
+ // The current item
+ PreviewItem currentItem;
+ // The modification time of that URL
+ time_t tOrig;
+ // Path to thumbnail cache for the current size
+ TQString thumbPath;
+ // Original URL of current item in TMS format
+ // (file:///path/to/file instead of file:/path/to/file)
+ TQString origName;
+ // Thumbnail file name for current item
+ TQString thumbName;
+ // Size of thumbnail
+ int width;
+ int height;
+ // Unscaled size of thumbnail (128 or 256 if cache is enabled)
+ int cacheWidth;
+ int cacheHeight;
+ // Whether the thumbnail should be scaled
+ bool bScale;
+ // Whether we should save the thumbnail
+ bool bSave;
+ // If the file to create a thumb for was a temp file, this is its name
+ TQString tempName;
+ // Over that, it's too much
+ unsigned long maximumSize;
+ // the size for the icon overlay
+ int iconSize;
+ // the transparency of the blended mimetype icon
+ int iconAlpha;
+ // Shared memory segment Id. The segment is allocated to a size
+ // of extent x extent x 4 (32 bit image) on first need.
+ int shmid;
+ // And the data area
+ uchar *shmaddr;
+ // Delete the KFileItems when done?
+ bool deleteItems;
+ bool succeeded;
+ // Root of thumbnail cache
+ TQString thumbRoot;
+ bool ignoreMaximumSize;
+ TQTimer startPreviewTimer;
+};
+
+PreviewJob::PreviewJob( const KFileItemList &items, int width, int height,
+ int iconSize, int iconAlpha, bool scale, bool save,
+ const TQStringList *enabledPlugins, bool deleteItems )
+ : TDEIO::Job( false /* no GUI */ )
+{
+ d = new PreviewJobPrivate;
+ d->tOrig = 0;
+ d->shmid = -1;
+ d->shmaddr = 0;
+ d->initialItems = items;
+ d->enabledPlugins = enabledPlugins;
+ d->width = width;
+ d->height = height ? height : width;
+ d->cacheWidth = d->width;
+ d->cacheHeight = d->height;
+ d->iconSize = iconSize;
+ d->iconAlpha = iconAlpha;
+ d->deleteItems = deleteItems;
+ d->bScale = scale;
+ d->bSave = save && scale;
+ d->succeeded = false;
+ d->currentItem.item = 0;
+ d->thumbRoot = TQDir::homeDirPath() + "/.thumbnails/";
+ d->ignoreMaximumSize = false;
+
+ // Return to event loop first, determineNextFile() might delete this;
+ connect(&d->startPreviewTimer, TQT_SIGNAL(timeout()), TQT_SLOT(startPreview()) );
+ d->startPreviewTimer.start(0, true);
+}
+
+PreviewJob::~PreviewJob()
+{
+#ifdef Q_OS_UNIX
+ if (d->shmaddr) {
+ shmdt((char*)d->shmaddr);
+ shmctl(d->shmid, IPC_RMID, 0);
+ }
+#endif
+ delete d;
+}
+
+void PreviewJob::startPreview()
+{
+ // Load the list of plugins to determine which mimetypes are supported
+ KTrader::OfferList plugins = KTrader::self()->query("ThumbCreator");
+ TQMap<TQString, KService::Ptr> mimeMap;
+
+ for (KTrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
+ if (!d->enabledPlugins || d->enabledPlugins->contains((*it)->desktopEntryName()))
+ {
+ TQStringList mimeTypes = (*it)->property("MimeTypes").toStringList();
+ for (TQStringList::ConstIterator mt = mimeTypes.begin(); mt != mimeTypes.end(); ++mt)
+ mimeMap.insert(*mt, *it);
+ }
+
+ // Look for images and store the items in our todo list :)
+ bool bNeedCache = false;
+ for (KFileItemListIterator it(d->initialItems); it.current(); ++it )
+ {
+ PreviewItem item;
+ item.item = it.current();
+ TQMap<TQString, KService::Ptr>::ConstIterator plugin = mimeMap.find(it.current()->mimetype());
+ if (plugin == mimeMap.end()
+ && (it.current()->mimetype() != "application/x-desktop")
+ && (it.current()->mimetype() != "media/builtin-mydocuments")
+ && (it.current()->mimetype() != "media/builtin-mycomputer")
+ && (it.current()->mimetype() != "media/builtin-mynetworkplaces")
+ && (it.current()->mimetype() != "media/builtin-printers")
+ && (it.current()->mimetype() != "media/builtin-trash")
+ && (it.current()->mimetype() != "media/builtin-webbrowser"))
+ {
+ TQString mimeType = it.current()->mimetype();
+ plugin = mimeMap.find(mimeType.replace(TQRegExp("/.*"), "/*"));
+
+ if (plugin == mimeMap.end())
+ {
+ // check mime type inheritance
+ KMimeType::Ptr mimeInfo = KMimeType::mimeType(it.current()->mimetype());
+ TQString parentMimeType = mimeInfo->parentMimeType();
+ while (!parentMimeType.isEmpty())
+ {
+ plugin = mimeMap.find(parentMimeType);
+ if (plugin != mimeMap.end()) break;
+
+ KMimeType::Ptr parentMimeInfo = KMimeType::mimeType(parentMimeType);
+ if (!parentMimeInfo) break;
+
+ parentMimeType = parentMimeInfo->parentMimeType();
+ }
+ }
+
+ if (plugin == mimeMap.end())
+ {
+ // check X-TDE-Text property
+ KMimeType::Ptr mimeInfo = KMimeType::mimeType(it.current()->mimetype());
+ TQVariant textProperty = mimeInfo->property("X-TDE-text");
+ if (textProperty.isValid() && textProperty.type() == TQVariant::Bool)
+ {
+ if (textProperty.toBool())
+ {
+ plugin = mimeMap.find("text/plain");
+ if (plugin == mimeMap.end())
+ {
+ plugin = mimeMap.find( "text/*" );
+ }
+ }
+ }
+ }
+ }
+
+ if (plugin != mimeMap.end())
+ {
+ item.plugin = *plugin;
+ d->items.append(item);
+ if (!bNeedCache && d->bSave &&
+ (it.current()->url().protocol() != "file" ||
+ !it.current()->url().directory( false ).startsWith(d->thumbRoot)) &&
+ (*plugin)->property("CacheThumbnail").toBool())
+ bNeedCache = true;
+ }
+ else
+ {
+ emitFailed(it.current());
+ if (d->deleteItems)
+ delete it.current();
+ }
+ }
+
+ // Read configuration value for the maximum allowed size
+ TDEConfig * config = TDEGlobal::config();
+ TDEConfigGroupSaver cgs( config, "PreviewSettings" );
+ d->maximumSize = config->readNumEntry( "MaximumSize", 1024*1024 /* 1MB */ );
+
+ if (bNeedCache)
+ {
+ if (d->width <= 128 && d->height <= 128) d->cacheWidth = d->cacheHeight = 128;
+ else d->cacheWidth = d->cacheHeight = 256;
+ d->thumbPath = d->thumbRoot + (d->cacheWidth == 128 ? "normal/" : "large/");
+ KStandardDirs::makeDir(d->thumbPath, 0700);
+ }
+ else
+ d->bSave = false;
+ determineNextFile();
+}
+
+void PreviewJob::removeItem( const KFileItem *item )
+{
+ for (TQValueList<PreviewItem>::Iterator it = d->items.begin(); it != d->items.end(); ++it)
+ if ((*it).item == item)
+ {
+ d->items.remove(it);
+ break;
+ }
+
+ if (d->currentItem.item == item)
+ {
+ subjobs.first()->kill();
+ subjobs.removeFirst();
+ determineNextFile();
+ }
+}
+
+void PreviewJob::setIgnoreMaximumSize(bool ignoreSize)
+{
+ d->ignoreMaximumSize = ignoreSize;
+}
+
+void PreviewJob::determineNextFile()
+{
+ if (d->currentItem.item)
+ {
+ if (!d->succeeded)
+ emitFailed();
+ if (d->deleteItems) {
+ delete d->currentItem.item;
+ d->currentItem.item = 0L;
+ }
+ }
+ // No more items ?
+ if ( d->items.isEmpty() )
+ {
+ emitResult();
+ return;
+ }
+ else
+ {
+ // First, stat the orig file
+ d->state = PreviewJobPrivate::STATE_STATORIG;
+ d->currentItem = d->items.first();
+ d->succeeded = false;
+ d->items.remove(d->items.begin());
+ TDEIO::Job *job = TDEIO::stat( d->currentItem.item->url(), false );
+ job->addMetaData( "no-auth-prompt", "true" );
+ addSubjob(job);
+ }
+}
+
+void PreviewJob::slotResult( TDEIO::Job *job )
+{
+ subjobs.remove( job );
+ Q_ASSERT ( subjobs.isEmpty() ); // We should have only one job at a time ...
+ switch ( d->state )
+ {
+ case PreviewJobPrivate::STATE_STATORIG:
+ {
+ if (job->error()) // that's no good news...
+ {
+ // Drop this one and move on to the next one
+ determineNextFile();
+ return;
+ }
+ TDEIO::UDSEntry entry = ((TDEIO::StatJob*)job)->statResult();
+ TDEIO::UDSEntry::ConstIterator it = entry.begin();
+ d->tOrig = 0;
+ int found = 0;
+ for( ; it != entry.end() && found < 2; it++ )
+ {
+ if ( (*it).m_uds == TDEIO::UDS_MODIFICATION_TIME )
+ {
+ d->tOrig = (time_t)((*it).m_long);
+ found++;
+ }
+ else if ( (*it).m_uds == TDEIO::UDS_SIZE )
+ {
+ if ( filesize_t((*it).m_long) > d->maximumSize &&
+ !d->ignoreMaximumSize &&
+ !d->currentItem.plugin->property("IgnoreMaximumSize").toBool() )
+ {
+ determineNextFile();
+ return;
+ }
+ found++;
+ }
+ }
+
+ if ( !d->currentItem.plugin->property( "CacheThumbnail" ).toBool() )
+ {
+ // This preview will not be cached, no need to look for a saved thumbnail
+ // Just create it, and be done
+ getOrCreateThumbnail();
+ return;
+ }
+
+ if ( statResultThumbnail() )
+ return;
+
+ getOrCreateThumbnail();
+ return;
+ }
+ case PreviewJobPrivate::STATE_GETORIG:
+ {
+ if (job->error())
+ {
+ determineNextFile();
+ return;
+ }
+
+ createThumbnail( static_cast<TDEIO::FileCopyJob*>(job)->destURL().path() );
+ return;
+ }
+ case PreviewJobPrivate::STATE_CREATETHUMB:
+ {
+ if (!d->tempName.isEmpty())
+ {
+ TQFile::remove(d->tempName);
+ d->tempName = TQString::null;
+ }
+ determineNextFile();
+ return;
+ }
+ }
+}
+
+bool PreviewJob::statResultThumbnail()
+{
+ if ( d->thumbPath.isEmpty() )
+ return false;
+
+ KURL url = d->currentItem.item->url();
+ // Don't include the password if any
+ url.setPass(TQString::null);
+ // The TMS defines local files as file:///path/to/file instead of KDE's
+ // way (file:/path/to/file)
+#ifdef KURL_TRIPLE_SLASH_FILE_PROT
+ d->origName = url.url();
+#else
+ if (url.protocol() == "file") d->origName = "file://" + url.path();
+ else d->origName = url.url();
+#endif
+
+ KMD5 md5( TQFile::encodeName( d->origName ).data() );
+ d->thumbName = TQFile::encodeName( md5.hexDigest() ) + ".png";
+
+ TQImage thumb;
+ if ( !thumb.load( d->thumbPath + d->thumbName ) ) return false;
+
+ if ( thumb.text( "Thumb::URI", 0 ) != d->origName ||
+ thumb.text( "Thumb::MTime", 0 ).toInt() != d->tOrig ) return false;
+
+ // Found it, use it
+ emitPreview( thumb );
+ d->succeeded = true;
+ determineNextFile();
+ return true;
+}
+
+
+void PreviewJob::getOrCreateThumbnail()
+{
+ // We still need to load the orig file ! (This is getting tedious) :)
+ const KFileItem* item = d->currentItem.item;
+ const TQString localPath = item->localPath();
+ if ( !localPath.isEmpty() )
+ createThumbnail( localPath );
+ else
+ {
+ d->state = PreviewJobPrivate::STATE_GETORIG;
+ KTempFile localFile;
+ KURL localURL;
+ localURL.setPath( d->tempName = localFile.name() );
+ const KURL currentURL = item->url();
+ TDEIO::Job * job = TDEIO::file_copy( currentURL, localURL, -1, true,
+ false, false /* No GUI */ );
+ job->addMetaData("thumbnail","1");
+ addSubjob(job);
+ }
+}
+
+// KDE 4: Make it const TQString &
+void PreviewJob::createThumbnail( TQString pixPath )
+{
+ d->state = PreviewJobPrivate::STATE_CREATETHUMB;
+ KURL thumbURL;
+ thumbURL.setProtocol("thumbnail");
+ thumbURL.setPath(pixPath);
+ TDEIO::TransferJob *job = TDEIO::get(thumbURL, false, false);
+ addSubjob(job);
+ connect(job, TQT_SIGNAL(data(TDEIO::Job *, const TQByteArray &)), TQT_SLOT(slotThumbData(TDEIO::Job *, const TQByteArray &)));
+ bool save = d->bSave && d->currentItem.plugin->property("CacheThumbnail").toBool();
+ job->addMetaData("mimeType", d->currentItem.item->mimetype());
+ job->addMetaData("width", TQString().setNum(save ? d->cacheWidth : d->width));
+ job->addMetaData("height", TQString().setNum(save ? d->cacheHeight : d->height));
+ job->addMetaData("iconSize", TQString().setNum(save ? 64 : d->iconSize));
+ job->addMetaData("iconAlpha", TQString().setNum(d->iconAlpha));
+ job->addMetaData("plugin", d->currentItem.plugin->library());
+#ifdef Q_OS_UNIX
+ if (d->shmid == -1)
+ {
+ if (d->shmaddr) {
+ shmdt((char*)d->shmaddr);
+ shmctl(d->shmid, IPC_RMID, 0);
+ }
+ d->shmid = shmget(IPC_PRIVATE, d->cacheWidth * d->cacheHeight * 4, IPC_CREAT|0600);
+ if (d->shmid != -1)
+ {
+ d->shmaddr = (uchar *)(shmat(d->shmid, 0, SHM_RDONLY));
+ if (d->shmaddr == (uchar *)-1)
+ {
+ shmctl(d->shmid, IPC_RMID, 0);
+ d->shmaddr = 0;
+ d->shmid = -1;
+ }
+ }
+ else
+ d->shmaddr = 0;
+ }
+ if (d->shmid != -1)
+ job->addMetaData("shmid", TQString().setNum(d->shmid));
+#endif
+}
+
+void PreviewJob::slotThumbData(TDEIO::Job *, const TQByteArray &data)
+{
+ bool save = d->bSave &&
+ d->currentItem.plugin->property("CacheThumbnail").toBool() &&
+ (d->currentItem.item->url().protocol() != "file" ||
+ !d->currentItem.item->url().directory( false ).startsWith(d->thumbRoot));
+ TQImage thumb;
+#ifdef Q_OS_UNIX
+ if (d->shmaddr)
+ {
+ TQDataStream str(data, IO_ReadOnly);
+ int width, height, depth;
+ bool alpha;
+ str >> width >> height >> depth >> alpha;
+ thumb = TQImage(d->shmaddr, width, height, depth, 0, 0, TQImage::IgnoreEndian);
+ thumb.setAlphaBuffer(alpha);
+ }
+ else
+#endif
+ thumb.loadFromData(data);
+
+ if (save)
+ {
+ thumb.setText("Thumb::URI", 0, d->origName);
+ thumb.setText("Thumb::MTime", 0, TQString::number(d->tOrig));
+ thumb.setText("Thumb::Size", 0, number(d->currentItem.item->size()));
+ thumb.setText("Thumb::Mimetype", 0, d->currentItem.item->mimetype());
+ thumb.setText("Software", 0, "KDE Thumbnail Generator");
+ KTempFile temp(d->thumbPath + "kde-tmp-", ".png");
+ if (temp.status() == 0) //Only try to write out the thumbnail if we
+ { //actually created the temp file.
+ thumb.save(temp.name(), "PNG");
+ rename(TQFile::encodeName(temp.name()), TQFile::encodeName(d->thumbPath + d->thumbName));
+ }
+ }
+ emitPreview( thumb );
+ d->succeeded = true;
+}
+
+void PreviewJob::emitPreview(const TQImage &thumb)
+{
+ TQPixmap pix;
+ if (thumb.width() > d->width || thumb.height() > d->height)
+ {
+ double imgRatio = (double)thumb.height() / (double)thumb.width();
+ if (imgRatio > (double)d->height / (double)d->width)
+ pix.convertFromImage(
+ thumb.smoothScale((int)TQMAX((double)d->height / imgRatio, 1), d->height));
+ else pix.convertFromImage(
+ thumb.smoothScale(d->width, (int)TQMAX((double)d->width * imgRatio, 1)));
+ }
+ else pix.convertFromImage(thumb);
+ emit gotPreview(d->currentItem.item, pix);
+}
+
+void PreviewJob::emitFailed(const KFileItem *item)
+{
+ if (!item)
+ item = d->currentItem.item;
+ emit failed(item);
+}
+
+TQStringList PreviewJob::availablePlugins()
+{
+ TQStringList result;
+ KTrader::OfferList plugins = KTrader::self()->query("ThumbCreator");
+ for (KTrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
+ if (!result.contains((*it)->desktopEntryName()))
+ result.append((*it)->desktopEntryName());
+ return result;
+}
+
+TQStringList PreviewJob::supportedMimeTypes()
+{
+ TQStringList result;
+ KTrader::OfferList plugins = KTrader::self()->query("ThumbCreator");
+ for (KTrader::OfferList::ConstIterator it = plugins.begin(); it != plugins.end(); ++it)
+ result += (*it)->property("MimeTypes").toStringList();
+ return result;
+}
+
+void PreviewJob::kill( bool quietly )
+{
+ d->startPreviewTimer.stop();
+ Job::kill( quietly );
+}
+
+PreviewJob *TDEIO::filePreview( const KFileItemList &items, int width, int height,
+ int iconSize, int iconAlpha, bool scale, bool save,
+ const TQStringList *enabledPlugins )
+{
+ return new PreviewJob(items, width, height, iconSize, iconAlpha,
+ scale, save, enabledPlugins);
+}
+
+PreviewJob *TDEIO::filePreview( const KURL::List &items, int width, int height,
+ int iconSize, int iconAlpha, bool scale, bool save,
+ const TQStringList *enabledPlugins )
+{
+ KFileItemList fileItems;
+ for (KURL::List::ConstIterator it = items.begin(); it != items.end(); ++it)
+ fileItems.append(new KFileItem(KFileItem::Unknown, KFileItem::Unknown, *it, true));
+ return new PreviewJob(fileItems, width, height, iconSize, iconAlpha,
+ scale, save, enabledPlugins, true);
+}
+
+void PreviewJob::virtual_hook( int id, void* data )
+{ TDEIO::Job::virtual_hook( id, data ); }
+
diff --git a/tdeio/tdeio/previewjob.h b/tdeio/tdeio/previewjob.h
new file mode 100644
index 000000000..9c62859c9
--- /dev/null
+++ b/tdeio/tdeio/previewjob.h
@@ -0,0 +1,182 @@
+// -*- c++ -*-
+// vim: ts=4 sw=4 et
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+ 2000 Carsten Pfeiffer <pfeiffer@kde.org>
+ 2001 Malte Starostik <malte.starostik@t-online.de>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kio_previewjob_h__
+#define __kio_previewjob_h__
+
+#include <tdefileitem.h>
+#include <tdeio/job.h>
+
+class TQPixmap;
+
+namespace TDEIO {
+ /*!
+ * This class catches a preview (thumbnail) for files.
+ * @short KIO Job to get a thumbnail picture
+ */
+ class TDEIO_EXPORT PreviewJob : public TDEIO::Job
+ {
+ Q_OBJECT
+ public:
+ /**
+ * Creates a new PreviewJob.
+ * @param items a list of files to create previews for
+ * @param width the desired width
+ * @param height the desired height, 0 to use the @p width
+ * @param iconSize the size of the mimetype icon to overlay over the
+ * preview or zero to not overlay an icon. This has no effect if the
+ * preview plugin that will be used doesn't use icon overlays.
+ * @param iconAlpha transparency to use for the icon overlay
+ * @param scale if the image is to be scaled to the requested size or
+ * returned in its original size
+ * @param save if the image should be cached for later use
+ * @param enabledPlugins if non-zero, this points to a list containing
+ * the names of the plugins that may be used.
+ * @param deleteItems true to delete the items when done
+ */
+ PreviewJob( const KFileItemList &items, int width, int height,
+ int iconSize, int iconAlpha, bool scale, bool save,
+ const TQStringList *enabledPlugins, bool deleteItems = false );
+ virtual ~PreviewJob();
+
+ /**
+ * Removes an item from preview processing. Use this if you passed
+ * an item to filePreview and want to delete it now.
+ *
+ * @param item the item that should be removed from the preview queue
+ */
+ void removeItem( const KFileItem *item );
+
+ /**
+ * If @p ignoreSize is true, then the preview is always
+ * generated regardless of the settings
+ *
+ * @since KDE 3.4
+ **/
+ void setIgnoreMaximumSize(bool ignoreSize = true);
+
+ /**
+ * Returns a list of all available preview plugins. The list
+ * contains the basenames of the plugins' .desktop files (no path,
+ * no .desktop).
+ * @return the list of plugins
+ */
+ static TQStringList availablePlugins();
+
+ /**
+ * Returns a list of all supported MIME types. The list can
+ * contain entries like text/ * (without the space).
+ * @return the list of mime types
+ */
+ static TQStringList supportedMimeTypes();
+
+ /**
+ * Reimplemented for internal reasons
+ */
+ virtual void kill( bool quietly = true );
+
+ signals:
+ /**
+ * Emitted when a thumbnail picture for @p item has been successfully
+ * retrieved.
+ * @param item the file of the preview
+ * @param preview the preview image
+ */
+ void gotPreview( const KFileItem *item, const TQPixmap &preview );
+ /**
+ * Emitted when a thumbnail for @p item could not be created,
+ * either because a ThumbCreator for its MIME type does not
+ * exist, or because something went wrong.
+ * @param item the file that failed
+ */
+ void failed( const KFileItem *item );
+
+ protected:
+ void getOrCreateThumbnail();
+ bool statResultThumbnail();
+ void createThumbnail( TQString );
+
+ protected slots:
+ virtual void slotResult( TDEIO::Job *job );
+
+ private slots:
+ void startPreview();
+ void slotThumbData(TDEIO::Job *, const TQByteArray &);
+
+ private:
+ void determineNextFile();
+ void emitPreview(const TQImage &thumb);
+ void emitFailed(const KFileItem *item = 0);
+
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ private:
+ struct PreviewJobPrivate *d;
+ };
+
+ /**
+ * Creates a PreviewJob to generate or retrieve a preview image
+ * for the given URL.
+ *
+ * @param items files to get previews for
+ * @param width the maximum width to use
+ * @param height the maximum height to use, if this is 0, the same
+ * value as width is used.
+ * @param iconSize the size of the mimetype icon to overlay over the
+ * preview or zero to not overlay an icon. This has no effect if the
+ * preview plugin that will be used doesn't use icon overlays.
+ * @param iconAlpha transparency to use for the icon overlay
+ * @param scale if the image is to be scaled to the requested size or
+ * returned in its original size
+ * @param save if the image should be cached for later use
+ * @param enabledPlugins if non-zero, this points to a list containing
+ * the names of the plugins that may be used.
+ * @return the new PreviewJob
+ * @see PreviewJob::availablePlugins()
+ */
+ TDEIO_EXPORT PreviewJob *filePreview( const KFileItemList &items, int width, int height = 0, int iconSize = 0, int iconAlpha = 70, bool scale = true, bool save = true, const TQStringList *enabledPlugins = 0 );
+
+ /**
+ * Creates a PreviewJob to generate or retrieve a preview image
+ * for the given URL.
+ *
+ * @param items files to get previews for
+ * @param width the maximum width to use
+ * @param height the maximum height to use, if this is 0, the same
+ * value as width is used.
+ * @param iconSize the size of the mimetype icon to overlay over the
+ * preview or zero to not overlay an icon. This has no effect if the
+ * preview plugin that will be used doesn't use icon overlays.
+ * @param iconAlpha transparency to use for the icon overlay
+ * @param scale if the image is to be scaled to the requested size or
+ * returned in its original size
+ * @param save if the image should be cached for later use
+ * @param enabledPlugins if non-zero, this points to a list containing
+ * the names of the plugins that may be used.
+ * @return the new PreviewJob
+ * @see PreviewJob::availablePlugins()
+ */
+ TDEIO_EXPORT PreviewJob *filePreview( const KURL::List &items, int width, int height = 0, int iconSize = 0, int iconAlpha = 70, bool scale = true, bool save = true, const TQStringList *enabledPlugins = 0 );
+}
+
+#endif
diff --git a/tdeio/tdeio/progressbase.cpp b/tdeio/tdeio/progressbase.cpp
new file mode 100644
index 000000000..146f4182e
--- /dev/null
+++ b/tdeio/tdeio/progressbase.cpp
@@ -0,0 +1,180 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Matej Koss <koss@miesto.sk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "jobclasses.h"
+#include "progressbase.h"
+
+namespace TDEIO {
+
+ProgressBase::ProgressBase( TQWidget *parent )
+ : TQWidget( parent )
+{
+ m_pJob = 0;
+
+ // delete dialog after the job is finished / canceled
+ m_bOnlyClean = false;
+
+ // stop job on close
+ m_bStopOnClose = true;
+}
+
+
+void ProgressBase::setJob( TDEIO::Job *job )
+{
+ // first connect all slots
+ connect( job, TQT_SIGNAL( percent( TDEIO::Job*, unsigned long ) ),
+ TQT_SLOT( slotPercent( TDEIO::Job*, unsigned long ) ) );
+
+ connect( job, TQT_SIGNAL( result( TDEIO::Job* ) ),
+ TQT_SLOT( slotFinished( TDEIO::Job* ) ) );
+
+ connect( job, TQT_SIGNAL( canceled( TDEIO::Job* ) ),
+ TQT_SLOT( slotFinished( TDEIO::Job* ) ) );
+
+ // then assign job
+ m_pJob = job;
+}
+
+
+void ProgressBase::setJob( TDEIO::CopyJob *job )
+{
+ // first connect all slots
+ connect( job, TQT_SIGNAL( totalSize( TDEIO::Job*, TDEIO::filesize_t ) ),
+ TQT_SLOT( slotTotalSize( TDEIO::Job*, TDEIO::filesize_t ) ) );
+ connect( job, TQT_SIGNAL( totalFiles( TDEIO::Job*, unsigned long ) ),
+ TQT_SLOT( slotTotalFiles( TDEIO::Job*, unsigned long ) ) );
+ connect( job, TQT_SIGNAL( totalDirs( TDEIO::Job*, unsigned long ) ),
+ TQT_SLOT( slotTotalDirs( TDEIO::Job*, unsigned long ) ) );
+
+ connect( job, TQT_SIGNAL( processedSize( TDEIO::Job*, TDEIO::filesize_t ) ),
+ TQT_SLOT( slotProcessedSize( TDEIO::Job*, TDEIO::filesize_t ) ) );
+ connect( job, TQT_SIGNAL( processedFiles( TDEIO::Job*, unsigned long ) ),
+ TQT_SLOT( slotProcessedFiles( TDEIO::Job*, unsigned long ) ) );
+ connect( job, TQT_SIGNAL( processedDirs( TDEIO::Job*, unsigned long ) ),
+ TQT_SLOT( slotProcessedDirs( TDEIO::Job*, unsigned long ) ) );
+
+ connect( job, TQT_SIGNAL( speed( TDEIO::Job*, unsigned long ) ),
+ TQT_SLOT( slotSpeed( TDEIO::Job*, unsigned long ) ) );
+ connect( job, TQT_SIGNAL( percent( TDEIO::Job*, unsigned long ) ),
+ TQT_SLOT( slotPercent( TDEIO::Job*, unsigned long ) ) );
+
+ connect( job, TQT_SIGNAL( copying( TDEIO::Job*, const KURL& , const KURL& ) ),
+ TQT_SLOT( slotCopying( TDEIO::Job*, const KURL&, const KURL& ) ) );
+ connect( job, TQT_SIGNAL( moving( TDEIO::Job*, const KURL& , const KURL& ) ),
+ TQT_SLOT( slotMoving( TDEIO::Job*, const KURL&, const KURL& ) ) );
+ connect( job, TQT_SIGNAL( creatingDir( TDEIO::Job*, const KURL& ) ),
+ TQT_SLOT( slotCreatingDir( TDEIO::Job*, const KURL& ) ) );
+
+ connect( job, TQT_SIGNAL( result( TDEIO::Job* ) ),
+ TQT_SLOT( slotFinished( TDEIO::Job* ) ) );
+
+ connect( job, TQT_SIGNAL( canceled( TDEIO::Job* ) ),
+ TQT_SLOT( slotFinished( TDEIO::Job* ) ) );
+
+ // then assign job
+ m_pJob = job;
+}
+
+
+void ProgressBase::setJob( TDEIO::DeleteJob *job )
+{
+ // first connect all slots
+ connect( job, TQT_SIGNAL( totalSize( TDEIO::Job*, TDEIO::filesize_t ) ),
+ TQT_SLOT( slotTotalSize( TDEIO::Job*, TDEIO::filesize_t ) ) );
+ connect( job, TQT_SIGNAL( totalFiles( TDEIO::Job*, unsigned long ) ),
+ TQT_SLOT( slotTotalFiles( TDEIO::Job*, unsigned long ) ) );
+ connect( job, TQT_SIGNAL( totalDirs( TDEIO::Job*, unsigned long ) ),
+ TQT_SLOT( slotTotalDirs( TDEIO::Job*, unsigned long ) ) );
+
+ connect( job, TQT_SIGNAL( processedSize( TDEIO::Job*, TDEIO::filesize_t ) ),
+ TQT_SLOT( slotProcessedSize( TDEIO::Job*, TDEIO::filesize_t ) ) );
+ connect( job, TQT_SIGNAL( processedFiles( TDEIO::Job*, unsigned long ) ),
+ TQT_SLOT( slotProcessedFiles( TDEIO::Job*, unsigned long ) ) );
+ connect( job, TQT_SIGNAL( processedDirs( TDEIO::Job*, unsigned long ) ),
+ TQT_SLOT( slotProcessedDirs( TDEIO::Job*, unsigned long ) ) );
+
+ connect( job, TQT_SIGNAL( speed( TDEIO::Job*, unsigned long ) ),
+ TQT_SLOT( slotSpeed( TDEIO::Job*, unsigned long ) ) );
+ connect( job, TQT_SIGNAL( percent( TDEIO::Job*, unsigned long ) ),
+ TQT_SLOT( slotPercent( TDEIO::Job*, unsigned long ) ) );
+
+ connect( job, TQT_SIGNAL( deleting( TDEIO::Job*, const KURL& ) ),
+ TQT_SLOT( slotDeleting( TDEIO::Job*, const KURL& ) ) );
+
+ connect( job, TQT_SIGNAL( result( TDEIO::Job* ) ),
+ TQT_SLOT( slotFinished( TDEIO::Job* ) ) );
+
+ connect( job, TQT_SIGNAL( canceled( TDEIO::Job* ) ),
+ TQT_SLOT( slotFinished( TDEIO::Job* ) ) );
+
+ // then assign job
+ m_pJob = job;
+}
+
+
+void ProgressBase::closeEvent( TQCloseEvent* ) {
+ // kill job when desired
+ if ( m_bStopOnClose ) {
+ slotStop();
+ } else {
+ // clean or delete dialog
+ if ( m_bOnlyClean ) {
+ slotClean();
+ } else {
+ delete this;
+ }
+ }
+}
+
+void ProgressBase::finished() {
+ // clean or delete dialog
+ if ( m_bOnlyClean ) {
+ slotClean();
+ } else {
+ deleteLater();
+ }
+}
+
+void ProgressBase::slotFinished( TDEIO::Job* ) {
+ finished();
+}
+
+
+void ProgressBase::slotStop() {
+ if ( m_pJob ) {
+ m_pJob->kill(); // this will call slotFinished
+ m_pJob = 0L;
+ } else {
+ slotFinished( 0 ); // here we call it ourselves
+ }
+
+ emit stopped();
+}
+
+
+void ProgressBase::slotClean() {
+ hide();
+}
+
+void ProgressBase::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+} /* namespace */
+
+#include "progressbase.moc"
+
diff --git a/tdeio/tdeio/progressbase.h b/tdeio/tdeio/progressbase.h
new file mode 100644
index 000000000..655edeee5
--- /dev/null
+++ b/tdeio/tdeio/progressbase.h
@@ -0,0 +1,271 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Matej Koss <koss@miesto.sk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef __progressbase_h__
+#define __progressbase_h__
+
+
+#include <tqwidget.h>
+
+#include <tdeio/global.h>
+
+class KURL;
+namespace TDEIO {
+ class Job;
+ class CopyJob;
+ class DeleteJob;
+}
+
+namespace TDEIO
+{
+ enum Progress {
+ DEFAULT = 1,
+ STATUSBAR = 2,
+ LIST = 3
+ };
+
+/**
+* This class does all initialization stuff for progress,
+* like connecting signals to slots.
+* All slots are implemented as pure virtual methods.
+*
+* All custom IO progress dialog should inherit this class.
+* Add your GUI code to the constructor and implemement those virtual
+* methods which you need in order to display progress.
+*
+* E.g. StatusbarProgress only implements slotTotalSize(),
+* slotPercent() and slotSpeed().
+*
+* Custom progress dialog will be used like this :
+* \code
+* // create job
+* CopyJob* job = TDEIO::copy(...);
+* // create a dialog
+* MyCustomProgress *customProgress;
+* customProgress = new MyCustomProgress();
+* // connect progress with job
+* customProgress->setJob( job );
+* ...
+* \endcode
+*
+* There is a special method setStopOnClose() that controls the behavior of
+* the dialog.
+* @short Base class for IO progress dialogs.
+* @author Matej Koss <koss@miesto.sk>
+*/
+class TDEIO_EXPORT ProgressBase : public TQWidget {
+
+ Q_OBJECT
+
+public:
+
+ /**
+ * Creates a new progress dialog.
+ * @param parent the parent of this dialog window, or 0
+ */
+ ProgressBase( TQWidget *parent );
+ ~ProgressBase() {}
+
+ /**
+ * Assign a TDEIO::Job to this progress dialog.
+ * @param job the job to assign
+ */
+ void setJob( TDEIO::Job *job );
+ /**
+ * Assign a TDEIO::Job to this progress dialog.
+ * @param job the job to assign
+ */
+ void setJob( TDEIO::CopyJob *job );
+ /**
+ * Assign a TDEIO::Job to this progress dialog.
+ * @param job the job to assign
+ */
+ void setJob( TDEIO::DeleteJob *job );
+
+ // should we stop the job when the dialog is closed ?
+ void setStopOnClose( bool stopOnClose ) { m_bStopOnClose = stopOnClose; }
+ bool stopOnClose() const { return m_bStopOnClose; }
+
+ // should we delete the dialog or just clean it when the job is finished ?
+ /**
+ * This controls whether the dialog should be deleted or only cleaned when
+ * the TDEIO::Job is finished (or canceled).
+ *
+ * If your dialog is an embedded widget and not a separate window, you should
+ * setOnlyClean(true) in the constructor of your custom dialog.
+ *
+ * @param onlyClean If true the dialog will only call method slotClean.
+ * If false the dialog will be deleted.
+ * @see onlyClean()
+ */
+ void setOnlyClean( bool onlyClean ) { m_bOnlyClean = onlyClean; }
+
+ /**
+ * Checks whether the dialog should be deleted or cleaned.
+ * @return true if the dialog only calls slotClean, false if it will be
+ * deleted
+ * @see setOnlyClean()
+ */
+ bool onlyClean() const { return m_bOnlyClean; }
+
+ /**
+ * Call when the operation finished.
+ * @since 3.1
+ */
+ void finished();
+
+public slots:
+ /**
+ * This method should be called for correct cancellation of IO operation
+ * Connect this to the progress widgets buttons etc.
+ */
+ void slotStop();
+ /**
+ * This method is called when the widget should be cleaned (after job is finished).
+ * redefine this for custom behavior.
+ */
+ virtual void slotClean();
+
+ // progress slots
+ /**
+ * Called to set the total size.
+ * @param job the TDEIO::Job
+ * @param size the total size in bytes
+ */
+ virtual void slotTotalSize( TDEIO::Job* job, TDEIO::filesize_t size ) {
+ Q_UNUSED(job);Q_UNUSED(size);}
+ /**
+ * Called to set the total number of files.
+ * @param job the TDEIO::Job
+ * @param files the number of files
+ */
+ virtual void slotTotalFiles( TDEIO::Job* job, unsigned long files ) {
+ Q_UNUSED(job);Q_UNUSED(files);}
+ /**
+ * Called to set the total number of directories.
+ * @param job the TDEIO::Job
+ * @param dirs the number of directories
+ */
+ virtual void slotTotalDirs( TDEIO::Job* job, unsigned long dirs ) {
+ Q_UNUSED(job);Q_UNUSED(dirs);}
+
+ /**
+ * Called to set the processed size.
+ * @param job the TDEIO::Job
+ * @param bytes the processed size in bytes
+ */
+ virtual void slotProcessedSize( TDEIO::Job* job, TDEIO::filesize_t bytes ) {
+ Q_UNUSED(job);Q_UNUSED(bytes);}
+ /**
+ * Called to set the number of processed files.
+ * @param job the TDEIO::Job
+ * @param files the number of files
+ */
+ virtual void slotProcessedFiles( TDEIO::Job* job, unsigned long files ) {
+ Q_UNUSED(job);Q_UNUSED(files);}
+ /**
+ * Called to set the number of processed directories.
+ * @param job the TDEIO::Job
+ * @param dirs the number of directories
+ */
+ virtual void slotProcessedDirs( TDEIO::Job* job, unsigned long dirs ) {
+ Q_UNUSED(job);Q_UNUSED(dirs);}
+
+ /**
+ * Called to set the speed.
+ * @param job the TDEIO::Job
+ * @param speed the speed in bytes/second
+ */
+ virtual void slotSpeed( TDEIO::Job* job, unsigned long speed ) {
+ Q_UNUSED(job);Q_UNUSED(speed);}
+
+ /**
+ * Called to set the percentage.
+ * @param job the TDEIO::Job
+ * @param percent the percentage
+ */
+ virtual void slotPercent( TDEIO::Job* job, unsigned long percent ) {
+ Q_UNUSED(job);Q_UNUSED(percent);}
+
+ /**
+ * Called when the job is copying.
+ * @param job the TDEIO::Job
+ * @param src the source of the operation
+ * @param dest the destination of the operation
+ */
+ virtual void slotCopying( TDEIO::Job* job, const KURL& src, const KURL& dest ) {
+ Q_UNUSED(job);Q_UNUSED(src);Q_UNUSED(dest);}
+ /**
+ * Called when the job is moving.
+ * @param job the TDEIO::Job
+ * @param src the source of the operation
+ * @param dest the destination of the operation
+ */
+ virtual void slotMoving( TDEIO::Job* job, const KURL& src, const KURL& dest ) {
+ Q_UNUSED(job);Q_UNUSED(src);Q_UNUSED(dest);}
+ /**
+ * Called when the job is deleting.
+ * @param job the TDEIO::Job
+ * @param url the URL to delete
+ */
+ virtual void slotDeleting( TDEIO::Job* job, const KURL& url) {
+ Q_UNUSED(job);Q_UNUSED(url);}
+ /**
+ * Called when the job is creating a directory.
+ * @param job the TDEIO::Job
+ * @param dir the URL of the directory to create
+ */
+ virtual void slotCreatingDir( TDEIO::Job* job, const KURL& dir ) {
+ Q_UNUSED(job);Q_UNUSED(dir);}
+
+ /**
+ * Called when the job is resuming..
+ * @param job the TDEIO::Job
+ * @param from the position to resume from in bytes
+ */
+ virtual void slotCanResume( TDEIO::Job* job, TDEIO::filesize_t from) {
+ Q_UNUSED(job);Q_UNUSED(from);}
+
+signals:
+ /**
+ * Called when the operation stopped.
+ */
+ void stopped();
+
+protected slots:
+ void slotFinished( TDEIO::Job* );
+
+protected:
+
+ virtual void closeEvent( TQCloseEvent * );
+
+ TDEIO::Job* m_pJob;
+
+private:
+ bool m_bOnlyClean;
+ bool m_bStopOnClose;
+
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class ProgressBasePrivate* d;
+};
+
+} /* namespace */
+
+#endif // __progressbase_h__
diff --git a/tdeio/tdeio/renamedlg.cpp b/tdeio/tdeio/renamedlg.cpp
new file mode 100644
index 000000000..d66130993
--- /dev/null
+++ b/tdeio/tdeio/renamedlg.cpp
@@ -0,0 +1,574 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
+ David Faure <faure@kde.org>
+ 2001 Holger Freyther <freyther@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "tdeio/renamedlg.h"
+#include "tdeio/renamedlgplugin.h"
+#include <stdio.h>
+#include <assert.h>
+
+#include <tqfileinfo.h>
+#include <tqlabel.h>
+#include <tqlayout.h>
+#include <tqlineedit.h>
+#include <tqdir.h>
+
+#include <kmessagebox.h>
+#include <kpushbutton.h>
+#include <kapplication.h>
+#include <tdeio/global.h>
+#include <ktrader.h>
+#include <klibloader.h>
+#include <kdialog.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kdebug.h>
+#include <kurl.h>
+#include <kmimetype.h>
+#include <kseparator.h>
+#include <kstringhandler.h>
+#include <kstdguiitem.h>
+#include <kguiitem.h>
+#include <ksqueezedtextlabel.h>
+
+#ifdef Q_WS_X11
+#include <twin.h>
+#endif
+
+using namespace TDEIO;
+
+class RenameDlg::RenameDlgPrivate
+{
+ public:
+ RenameDlgPrivate(){
+ bCancel = 0;
+ bRename = bSkip = bAutoSkip = bOverwrite = bOverwriteAll = 0;
+ bResume = bResumeAll = bSuggestNewName = 0;
+ m_pLineEdit = 0;
+ }
+ KPushButton *bCancel;
+ TQPushButton *bRename;
+ TQPushButton *bSkip;
+ TQPushButton *bAutoSkip;
+ TQPushButton *bOverwrite;
+ TQPushButton *bOverwriteAll;
+ TQPushButton *bResume;
+ TQPushButton *bResumeAll;
+ TQPushButton *bSuggestNewName;
+ TQLineEdit* m_pLineEdit;
+ KURL src;
+ KURL dest;
+ TQString mimeSrc;
+ TQString mimeDest;
+ bool modal;
+ bool plugin;
+};
+
+RenameDlg::RenameDlg(TQWidget *parent, const TQString & _caption,
+ const TQString &_src, const TQString &_dest,
+ RenameDlg_Mode _mode,
+ TDEIO::filesize_t sizeSrc,
+ TDEIO::filesize_t sizeDest,
+ time_t ctimeSrc,
+ time_t ctimeDest,
+ time_t mtimeSrc,
+ time_t mtimeDest,
+ bool _modal)
+ : TQDialog ( parent, "TDEIO::RenameDialog" , _modal )
+{
+ d = new RenameDlgPrivate( );
+ d->modal = _modal;
+#if 0
+ // Set "StaysOnTop", because this dialog is typically used in tdeio_uiserver,
+ // i.e. in a separate process.
+ // ####### This isn't the case anymore - remove?
+#if !defined(Q_WS_QWS) && !defined(Q_WS_WIN) //FIXME(E): Implement for QT Embedded & win32
+ if (d->modal)
+ KWin::setState( winId(), NET::StaysOnTop );
+#endif
+#endif
+
+ d->src = _src;
+ d->dest = _dest;
+ d->plugin = false;
+
+
+ setCaption( _caption );
+
+ d->bCancel = new KPushButton( KStdGuiItem::cancel(), this );
+ connect(d->bCancel, TQT_SIGNAL(clicked()), this, TQT_SLOT(b0Pressed()));
+
+ if ( ! (_mode & M_NORENAME ) ) {
+ d->bRename = new TQPushButton( i18n( "&Rename" ), this );
+ d->bRename->setEnabled(false);
+ d->bSuggestNewName = new TQPushButton( i18n( "Suggest New &Name" ), this );
+ connect(d->bSuggestNewName, TQT_SIGNAL(clicked()), this, TQT_SLOT(b8Pressed()));
+ connect(d->bRename, TQT_SIGNAL(clicked()), this, TQT_SLOT(b1Pressed()));
+ }
+
+ if ( ( _mode & M_MULTI ) && ( _mode & M_SKIP ) ) {
+ d->bSkip = new TQPushButton( i18n( "&Skip" ), this );
+ connect(d->bSkip, TQT_SIGNAL(clicked()), this, TQT_SLOT(b2Pressed()));
+
+ d->bAutoSkip = new TQPushButton( i18n( "&Auto Skip" ), this );
+ connect(d->bAutoSkip, TQT_SIGNAL(clicked()), this, TQT_SLOT(b3Pressed()));
+ }
+
+ if ( _mode & M_OVERWRITE ) {
+ d->bOverwrite = new TQPushButton( i18n( "&Overwrite" ), this );
+ connect(d->bOverwrite, TQT_SIGNAL(clicked()), this, TQT_SLOT(b4Pressed()));
+
+ if ( _mode & M_MULTI ) {
+ d->bOverwriteAll = new TQPushButton( i18n( "O&verwrite All" ), this );
+ connect(d->bOverwriteAll, TQT_SIGNAL(clicked()), this, TQT_SLOT(b5Pressed()));
+ }
+ }
+
+ if ( _mode & M_RESUME ) {
+ d->bResume = new TQPushButton( i18n( "&Resume" ), this );
+ connect(d->bResume, TQT_SIGNAL(clicked()), this, TQT_SLOT(b6Pressed()));
+
+ if ( _mode & M_MULTI )
+ {
+ d->bResumeAll = new TQPushButton( i18n( "R&esume All" ), this );
+ connect(d->bResumeAll, TQT_SIGNAL(clicked()), this, TQT_SLOT(b7Pressed()));
+ }
+ }
+
+ TQVBoxLayout* pLayout = new TQVBoxLayout( this, KDialog::marginHint(),
+ KDialog::spacingHint() );
+ pLayout->addStrut( 360 ); // makes dlg at least that wide
+
+ // User tries to overwrite a file with itself ?
+ if ( _mode & M_OVERWRITE_ITSELF ) {
+ TQLabel *lb = new TQLabel( i18n( "This action would overwrite '%1' with itself.\n"
+ "Please enter a new file name:" ).arg( KStringHandler::csqueeze( d->src.pathOrURL(),100 ) ), this );
+ d->bRename->setText(i18n("C&ontinue"));
+ pLayout->addWidget( lb );
+ }
+ else if ( _mode & M_OVERWRITE ) {
+
+ // Figure out the mimetype and load one plugin
+ // (This is the only mode that is handled by plugins)
+ pluginHandling();
+ KTrader::OfferList plugin_offers;
+ if( d->mimeSrc != KMimeType::defaultMimeType() ){
+ plugin_offers = KTrader::self()->query(d->mimeSrc, "'RenameDlg/Plugin' in ServiceTypes");
+
+ }else if(d->mimeDest != KMimeType::defaultMimeType() ) {
+ plugin_offers = KTrader::self()->query(d->mimeDest, "'RenameDlg/Plugin' in ServiceTypes");
+ }
+ if(!plugin_offers.isEmpty() ){
+ kdDebug(7024) << "Offers" << endl;
+ KTrader::OfferList::ConstIterator it = plugin_offers.begin();
+ KTrader::OfferList::ConstIterator end = plugin_offers.end();
+ for( ; it != end; ++it ){
+ TQString libName = (*it)->library();
+ if( libName.isEmpty() ){
+ kdDebug(7024) << "lib is empty" << endl;
+ continue;
+ }
+ KLibrary *lib = KLibLoader::self()->library(libName.local8Bit() );
+ if(!lib) {
+ continue;
+ }
+ KLibFactory *factory = lib->factory();
+ if(!factory){
+ lib->unload();
+ continue;
+ }
+ TQObject *obj = factory->create( TQT_TQOBJECT(this), (*it)->name().latin1() );
+ if(!obj) {
+ lib->unload();
+ continue;
+ }
+ RenameDlgPlugin *plugin = static_cast<RenameDlgPlugin *>(TQT_TQWIDGET(obj));
+ if(!plugin ){
+ delete obj;
+ continue;
+ }
+ if( plugin->initialize( _mode, _src, _dest, d->mimeSrc,
+ d->mimeDest, sizeSrc, sizeDest,
+ ctimeSrc, ctimeDest,
+ mtimeSrc, mtimeDest ) ) {
+ d->plugin = true;
+ pLayout->addWidget(plugin );
+ kdDebug(7024) << "RenameDlgPlugin" << endl;
+ break;
+ } else {
+ delete obj;
+ }
+ }
+
+ }
+
+ if( !d->plugin ){
+ // No plugin found, build default dialog
+ TQGridLayout * gridLayout = new TQGridLayout( 0L, 9, 2, KDialog::marginHint(),
+ KDialog::spacingHint() );
+ pLayout->addLayout(TQT_TQLAYOUT(gridLayout));
+ gridLayout->setColStretch(0,0);
+ gridLayout->setColStretch(1,10);
+
+ TQString sentence1;
+ if (mtimeDest < mtimeSrc)
+ sentence1 = i18n("An older item named '%1' already exists.");
+ else if (mtimeDest == mtimeSrc)
+ sentence1 = i18n("A similar file named '%1' already exists.");
+ else
+ sentence1 = i18n("A newer item named '%1' already exists.");
+
+ TQLabel * lb1 = new KSqueezedTextLabel( sentence1.arg(d->dest.pathOrURL() ), this );
+ gridLayout->addMultiCellWidget( lb1, 0, 0, 0, 1 ); // takes the complete first line
+
+ lb1 = new TQLabel( this );
+ lb1->setPixmap( KMimeType::pixmapForURL( d->dest ) );
+ gridLayout->addMultiCellWidget( lb1, 1, 3, 0, 0 ); // takes the first column on rows 1-3
+
+ int row = 1;
+ if ( sizeDest != (TDEIO::filesize_t)-1 )
+ {
+ TQLabel * lb = new TQLabel( i18n("size %1").arg( TDEIO::convertSize(sizeDest) ), this );
+ gridLayout->addWidget( lb, row, 1 );
+ row++;
+
+ }
+ if ( ctimeDest != (time_t)-1 )
+ {
+ TQDateTime dctime; dctime.setTime_t( ctimeDest );
+ TQLabel * lb = new TQLabel( i18n("created on %1").arg( TDEGlobal::locale()->formatDateTime(dctime) ), this );
+ gridLayout->addWidget( lb, row, 1 );
+ row++;
+ }
+ if ( mtimeDest != (time_t)-1 )
+ {
+ TQDateTime dmtime; dmtime.setTime_t( mtimeDest );
+ TQLabel * lb = new TQLabel( i18n("modified on %1").arg( TDEGlobal::locale()->formatDateTime(dmtime) ), this );
+ gridLayout->addWidget( lb, row, 1 );
+ row++;
+ }
+
+ if ( !d->src.isEmpty() )
+ {
+ // rows 1 to 3 are the details (size/ctime/mtime), row 4 is empty
+ gridLayout->addRowSpacing( 4, 20 );
+
+ TQLabel * lb2 = new KSqueezedTextLabel( i18n("The source file is '%1'").arg(d->src.pathOrURL()), this );
+ gridLayout->addMultiCellWidget( lb2, 5, 5, 0, 1 ); // takes the complete first line
+
+ lb2 = new TQLabel( this );
+ lb2->setPixmap( KMimeType::pixmapForURL( d->src ) );
+ gridLayout->addMultiCellWidget( lb2, 6, 8, 0, 0 ); // takes the first column on rows 6-8
+
+ row = 6;
+
+ if ( sizeSrc != (TDEIO::filesize_t)-1 )
+ {
+ TQLabel * lb = new TQLabel( i18n("size %1").arg( TDEIO::convertSize(sizeSrc) ), this );
+ gridLayout->addWidget( lb, row, 1 );
+ row++;
+ }
+ if ( ctimeSrc != (time_t)-1 )
+ {
+ TQDateTime dctime; dctime.setTime_t( ctimeSrc );
+ TQLabel * lb = new TQLabel( i18n("created on %1").arg( TDEGlobal::locale()->formatDateTime(dctime) ), this );
+ gridLayout->addWidget( lb, row, 1 );
+ row++;
+ }
+ if ( mtimeSrc != (time_t)-1 )
+ {
+ TQDateTime dmtime; dmtime.setTime_t( mtimeSrc );
+ TQLabel * lb = new TQLabel( i18n("modified on %1").arg( TDEGlobal::locale()->formatDateTime(dmtime) ), this );
+ gridLayout->addWidget( lb, row, 1 );
+ row++;
+ }
+ }
+ }
+ }
+ else
+ {
+ // This is the case where we don't want to allow overwriting, the existing
+ // file must be preserved (e.g. when renaming).
+ TQString sentence1;
+ if (mtimeDest < mtimeSrc)
+ sentence1 = i18n("An older item named '%1' already exists.");
+ else if (mtimeDest == mtimeSrc)
+ sentence1 = i18n("A similar file named '%1' already exists.");
+ else
+ sentence1 = i18n("A newer item named '%1' already exists.");
+
+ TQLabel *lb = new KSqueezedTextLabel( sentence1.arg(d->dest.pathOrURL()), this );
+ pLayout->addWidget(lb);
+ }
+ TQHBoxLayout* layout2 = new TQHBoxLayout();
+ pLayout->addLayout( layout2 );
+
+ d->m_pLineEdit = new TQLineEdit( this );
+ layout2->addWidget( d->m_pLineEdit );
+ TQString fileName = d->dest.fileName();
+ d->m_pLineEdit->setText( TDEIO::decodeFileName( fileName ) );
+ if ( d->bRename || d->bOverwrite )
+ connect(d->m_pLineEdit, TQT_SIGNAL(textChanged(const TQString &)),
+ TQT_SLOT(enableRenameButton(const TQString &)));
+ if ( d->bSuggestNewName )
+ {
+ layout2->addWidget( d->bSuggestNewName );
+ setTabOrder( d->m_pLineEdit, d->bSuggestNewName );
+ }
+
+ KSeparator* separator = new KSeparator( this );
+ pLayout->addWidget( separator );
+
+ TQHBoxLayout* layout = new TQHBoxLayout();
+ pLayout->addLayout( layout );
+
+ layout->addStretch(1);
+
+ if ( d->bRename )
+ {
+ layout->addWidget( d->bRename );
+ setTabOrder( d->bRename, d->bCancel );
+ }
+ if ( d->bSkip )
+ {
+ layout->addWidget( d->bSkip );
+ setTabOrder( d->bSkip, d->bCancel );
+ }
+ if ( d->bAutoSkip )
+ {
+ layout->addWidget( d->bAutoSkip );
+ setTabOrder( d->bAutoSkip, d->bCancel );
+ }
+ if ( d->bOverwrite )
+ {
+ layout->addWidget( d->bOverwrite );
+ setTabOrder( d->bOverwrite, d->bCancel );
+ }
+ if ( d->bOverwriteAll )
+ {
+ layout->addWidget( d->bOverwriteAll );
+ setTabOrder( d->bOverwriteAll, d->bCancel );
+ }
+ if ( d->bResume )
+ {
+ layout->addWidget( d->bResume );
+ setTabOrder( d->bResume, d->bCancel );
+ }
+ if ( d->bResumeAll )
+ {
+ layout->addWidget( d->bResumeAll );
+ setTabOrder( d->bResumeAll, d->bCancel );
+ }
+
+ d->bCancel->setDefault( true );
+ layout->addWidget( d->bCancel );
+
+ resize( sizeHint() );
+}
+
+RenameDlg::~RenameDlg()
+{
+ delete d;
+ // no need to delete Pushbuttons,... qt will do this
+}
+
+void RenameDlg::enableRenameButton(const TQString &newDest)
+{
+ if ( newDest != TDEIO::decodeFileName( d->dest.fileName() ) && !newDest.isEmpty() )
+ {
+ d->bRename->setEnabled( true );
+ d->bRename->setDefault( true );
+ if ( d->bOverwrite )
+ d->bOverwrite->setEnabled( false ); // prevent confusion (#83114)
+ }
+ else
+ {
+ d->bRename->setEnabled( false );
+ if ( d->bOverwrite )
+ d->bOverwrite->setEnabled( true );
+ }
+}
+
+KURL RenameDlg::newDestURL()
+{
+ KURL newDest( d->dest );
+ TQString fileName = d->m_pLineEdit->text();
+ newDest.setFileName( TDEIO::encodeFileName( fileName ) );
+ return newDest;
+}
+
+void RenameDlg::b0Pressed()
+{
+ done( 0 );
+}
+
+// Rename
+void RenameDlg::b1Pressed()
+{
+ if ( d->m_pLineEdit->text().isEmpty() )
+ return;
+
+ KURL u = newDestURL();
+ if ( !u.isValid() )
+ {
+ KMessageBox::error( this, i18n( "Malformed URL\n%1" ).arg( u.url() ) );
+ return;
+ }
+
+ done( 1 );
+}
+
+TQString RenameDlg::suggestName(const KURL& baseURL, const TQString& oldName)
+{
+ TQString dotSuffix, suggestedName;
+ TQString basename = oldName;
+
+ int index = basename.find( '.' );
+ if ( index != -1 ) {
+ dotSuffix = basename.mid( index );
+ basename.truncate( index );
+ }
+
+ int pos = basename.findRev( '_' );
+ if(pos != -1 ){
+ TQString tmp = basename.mid( pos+1 );
+ bool ok;
+ int number = tmp.toInt( &ok );
+ if ( !ok ) {// ok there is no number
+ suggestedName = basename + "1" + dotSuffix;
+ }
+ else {
+ // yes there's already a number behind the _ so increment it by one
+ basename.replace( pos+1, tmp.length(), TQString::number(number+1) );
+ suggestedName = basename + dotSuffix;
+ }
+ }
+ else // no underscore yet
+ suggestedName = basename + "_1" + dotSuffix ;
+
+ // Check if suggested name already exists
+ bool exists = false;
+ // TODO: network transparency. However, using NetAccess from a modal dialog
+ // could be a problem, no? (given that it uses a modal widget itself....)
+ if ( baseURL.isLocalFile() )
+ exists = TQFileInfo( baseURL.path(+1) + suggestedName ).exists();
+
+ if ( !exists )
+ return suggestedName;
+ else // already exists -> recurse
+ return suggestName( baseURL, suggestedName );
+}
+
+// Propose button clicked
+void RenameDlg::b8Pressed()
+{
+ /* no name to play with */
+ if ( d->m_pLineEdit->text().isEmpty() )
+ return;
+
+ KURL destDirectory( d->dest );
+ destDirectory.setPath( destDirectory.directory() );
+ d->m_pLineEdit->setText( suggestName( destDirectory, d->m_pLineEdit->text() ) );
+ return;
+}
+
+void RenameDlg::b2Pressed()
+{
+ done( 2 );
+}
+
+void RenameDlg::b3Pressed()
+{
+ done( 3 );
+}
+
+void RenameDlg::b4Pressed()
+{
+ done( 4 );
+}
+
+void RenameDlg::b5Pressed()
+{
+ done( 5 );
+}
+
+void RenameDlg::b6Pressed()
+{
+ done( 6 );
+}
+
+void RenameDlg::b7Pressed()
+{
+ done( 7 );
+}
+
+static TQString mime( const KURL& src )
+{
+ KMimeType::Ptr type = KMimeType::findByURL( src );
+ //if( type->name() == KMimeType::defaultMimeType() ){ // ok no mimetype
+ // TQString ty = TDEIO::NetAccess::mimetype(d->src );
+ // return ty;
+ return type->name();
+}
+
+/** This will figure out the mimetypes and query for a plugin
+ * Loads it then and asks the plugin if it wants to do the job
+ * We'll take the first available mimetype
+ * The scanning for a mimetype will be done in 2 ways
+ *
+ */
+void RenameDlg::pluginHandling()
+{
+ d->mimeSrc = mime( d->src );
+ d->mimeDest = mime(d->dest );
+
+ kdDebug(7024) << "Source Mimetype: "<< d->mimeSrc << endl;
+ kdDebug(7024) << "Dest Mimetype: "<< d->mimeDest << endl;
+}
+
+
+RenameDlg_Result TDEIO::open_RenameDlg( const TQString & _caption,
+ const TQString & _src, const TQString & _dest,
+ RenameDlg_Mode _mode,
+ TQString& _new,
+ TDEIO::filesize_t sizeSrc,
+ TDEIO::filesize_t sizeDest,
+ time_t ctimeSrc,
+ time_t ctimeDest,
+ time_t mtimeSrc,
+ time_t mtimeDest)
+{
+ Q_ASSERT(kapp);
+
+ RenameDlg dlg( 0L, _caption, _src, _dest, _mode,
+ sizeSrc, sizeDest, ctimeSrc, ctimeDest, mtimeSrc, mtimeDest,
+ true /*modal*/ );
+ int i = dlg.exec();
+ _new = dlg.newDestURL().path();
+
+ return (RenameDlg_Result)i;
+}
+
+#include "renamedlg.moc"
+
+
+
+
+
diff --git a/tdeio/tdeio/renamedlg.h b/tdeio/tdeio/renamedlg.h
new file mode 100644
index 000000000..81f16df94
--- /dev/null
+++ b/tdeio/tdeio/renamedlg.h
@@ -0,0 +1,153 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
+ David Faure <faure@kde.org>
+ 2001 Holger Freyther <freyther@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kio_rename_dlg__
+#define __kio_rename_dlg__
+
+#include <kurl.h>
+#include <tqdialog.h>
+#include <tqstring.h>
+#include <sys/types.h>
+
+#include <tdeio/global.h>
+
+namespace TDEIO {
+
+// KDE4: get rid of M_OVERWRITE_ITSELF, trigger it internally if src==dest
+enum RenameDlg_Mode { M_OVERWRITE = 1, M_OVERWRITE_ITSELF = 2, M_SKIP = 4, M_SINGLE = 8, M_MULTI = 16, M_RESUME = 32, M_NORENAME = 64 };
+
+/**
+ * The result of open_RenameDlg().
+ */
+enum RenameDlg_Result { R_RESUME = 6, R_RESUME_ALL = 7, R_OVERWRITE = 4, R_OVERWRITE_ALL = 5, R_SKIP = 2, R_AUTO_SKIP = 3, R_RENAME = 1, R_CANCEL = 0 };
+
+
+/**
+ * A dialog for the options to rename two files.
+ * @short A dialog for renaming files.
+ * @since 3.1
+ */
+class TDEIO_EXPORT RenameDlg : public TQDialog
+{
+ Q_OBJECT
+public:
+ /**
+ * Construct a "rename" dialog.
+ * @param parent parent widget (often 0)
+ * @param caption the caption for the dialog box
+ * @param src the url to the file/dir we're trying to copy, as it's part of the text message
+ * @param dest the path to destination file/dir, i.e. the one that already exists
+ * @param mode parameters for the dialog (which buttons to show...),
+ * @param sizeSrc size of source file
+ * @param sizeDest size of destination file
+ * @param ctimeSrc creation time of source file
+ * @param ctimeDest creation time of destination file
+ * @param mtimeSrc modification time of source file
+ * @param mtimeDest modification time of destination file
+ * @param modal set to true for a modal dialog
+ * @see RenameDlg_Mode
+ */
+ RenameDlg( TQWidget *parent, const TQString & caption,
+ // KDE4: make those KURLs, and use pathOrURL() internally.
+ const TQString & src, const TQString & dest,
+ RenameDlg_Mode mode,
+ TDEIO::filesize_t sizeSrc = (TDEIO::filesize_t) -1,
+ TDEIO::filesize_t sizeDest = (TDEIO::filesize_t) -1,
+ time_t ctimeSrc = (time_t) -1,
+ time_t ctimeDest = (time_t) -1,
+ time_t mtimeSrc = (time_t) -1,
+ time_t mtimeDest = (time_t) -1,
+ bool modal = false );
+ ~RenameDlg();
+
+ /**
+ * @return the new destination
+ * valid only if RENAME was chosen
+ */
+ KURL newDestURL();
+
+ /**
+ * Given a directory path and a filename (which usually exists already),
+ * this function returns a suggested name for a file that doesn't exist
+ * in that directory. The existence is only checked for local urls though.
+ * The suggested file name is of the form foo_1 foo_2 etc.
+ * @since 3.4
+ */
+ static TQString suggestName(const KURL& baseURL, const TQString& oldName);
+
+public slots:
+ /// KDE4: rename to cancelPressed(), renamePressed() etc.
+ void b0Pressed();
+ void b1Pressed();
+ void b2Pressed();
+ void b3Pressed();
+ void b4Pressed();
+ void b5Pressed();
+ void b6Pressed();
+ void b7Pressed();
+ void b8Pressed();
+
+protected slots:
+ void enableRenameButton(const TQString &);
+private:
+ class RenameDlgPrivate;
+ RenameDlgPrivate *d;
+ void pluginHandling( );
+};
+
+ /**
+ * \addtogroup renamedlg "RenameDlg related Functions"
+ * @{
+ * \relates TDEIO::RenameDlg
+ * Construct a modal, parent-less "rename" dialog, and return
+ * a result code, as well as the new dest. Much easier to use than the
+ * class RenameDlg directly.
+
+ * @param caption the caption for the dialog box
+ * @param src the URL of the file/dir we're trying to copy, as it's part of the text message
+ * @param dest the URL of the destination file/dir, i.e. the one that already exists
+ * @param mode parameters for the dialog (which buttons to show...),
+ * see RenameDlg_Mode
+ * @param newDest the new destination path, valid if R_RENAME was returned.
+ * @param sizeSrc size of source file
+ * @param sizeDest size of destination file
+ * @param ctimeSrc creation time of source file
+ * @param ctimeDest creation time of destination file
+ * @param mtimeSrc modification time of source file
+ * @param mtimeDest modification time of destination file
+ * @return the result
+ */
+TDEIO_EXPORT RenameDlg_Result open_RenameDlg( const TQString & caption,
+ // KDE4: make those KURLs
+ const TQString& src, const TQString & dest,
+ RenameDlg_Mode mode, TQString& newDestPath,
+ TDEIO::filesize_t sizeSrc = (TDEIO::filesize_t) -1,
+ TDEIO::filesize_t sizeDest = (TDEIO::filesize_t) -1,
+ time_t ctimeSrc = (time_t) -1,
+ time_t ctimeDest = (time_t) -1,
+ time_t mtimeSrc = (time_t) -1,
+ time_t mtimeDest = (time_t) -1
+ );
+
+/*! @} */
+
+}
+#endif
diff --git a/tdeio/tdeio/renamedlgplugin.h b/tdeio/tdeio/renamedlgplugin.h
new file mode 100644
index 000000000..18daab017
--- /dev/null
+++ b/tdeio/tdeio/renamedlgplugin.h
@@ -0,0 +1,59 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2001 Holger Freyther <freyther@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef renamedlgplugin_h
+#define renamedlgplugin_h
+
+#include <tdeio/renamedlg.h>
+#include <tqdialog.h>
+#include <sys/types.h>
+#include <tqstring.h>
+#include <tqstringlist.h>
+
+/**
+ * This is the base class for all RenameDlg plugins.
+ * @short Base class for RenameDlg plugins.
+ * @since 3.1
+ */
+class TDEIO_EXPORT RenameDlgPlugin : public TQWidget
+{
+public:
+ /**
+ * This is the c'tor.
+ */
+ RenameDlgPlugin(TQDialog *dialog, const char *name, const TQStringList &/*list*/ = TQStringList() ): TQWidget(dialog, name ) {};
+
+ /**
+ * This function will be called by RenameDlg. The params are infos about the files.
+ * @return If the plugin want's to display it return true, if not return false
+ */
+ virtual bool initialize(TDEIO::RenameDlg_Mode /*mod*/ , const TQString &/*_src*/, const TQString &/*_dest*/,
+ const TQString &/*mimeSrc*/,
+ const TQString &/*mimeDest*/,
+ TDEIO::filesize_t /*sizeSrc*/,
+ TDEIO::filesize_t /*sizeDest*/,
+ time_t /*ctimeSrc*/,
+ time_t /*ctimeDest*/,
+ time_t /*mtimeSrc*/,
+ time_t /*mtimeDest*/ ) {return false;};
+
+};
+
+#endif
+
diff --git a/tdeio/tdeio/scheduler.cpp b/tdeio/tdeio/scheduler.cpp
new file mode 100644
index 000000000..c0aad7d38
--- /dev/null
+++ b/tdeio/tdeio/scheduler.cpp
@@ -0,0 +1,922 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
+ Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "tdeio/sessiondata.h"
+#include "tdeio/slaveconfig.h"
+#include "tdeio/scheduler.h"
+#include "tdeio/authinfo.h"
+#include "tdeio/slave.h"
+#include <tqptrlist.h>
+#include <tqdict.h>
+
+#include <dcopclient.h>
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kprotocolmanager.h>
+#include <kprotocolinfo.h>
+#include <assert.h>
+#include <kstaticdeleter.h>
+#include <tdesu/client.h>
+
+
+// Slaves may be idle for MAX_SLAVE_IDLE time before they are being returned
+// to the system wide slave pool. (3 minutes)
+#define MAX_SLAVE_IDLE (3*60)
+
+using namespace TDEIO;
+
+template class TQDict<TDEIO::Scheduler::ProtocolInfo>;
+
+Scheduler *Scheduler::instance = 0;
+
+class TDEIO::SlaveList: public TQPtrList<Slave>
+{
+ public:
+ SlaveList() { }
+};
+
+//
+// There are two kinds of protocol:
+// (1) The protocol of the url
+// (2) The actual protocol that the io-slave uses.
+//
+// These two often match, but not necasserily. Most notably, they don't
+// match when doing ftp via a proxy.
+// In that case (1) is ftp, but (2) is http.
+//
+// JobData::protocol stores (2) while Job::url().protocol() returns (1).
+// The ProtocolInfoDict is indexed with (2).
+//
+// We schedule slaves based on (2) but tell the slave about (1) via
+// Slave::setProtocol().
+
+class TDEIO::Scheduler::JobData
+{
+public:
+ JobData() : checkOnHold(false) { }
+
+public:
+ TQString protocol;
+ TQString proxy;
+ bool checkOnHold;
+};
+
+class TDEIO::Scheduler::ExtraJobData: public TQPtrDict<TDEIO::Scheduler::JobData>
+{
+public:
+ ExtraJobData() { setAutoDelete(true); }
+};
+
+class TDEIO::Scheduler::ProtocolInfo
+{
+public:
+ ProtocolInfo() : maxSlaves(1), skipCount(0)
+ {
+ joblist.setAutoDelete(false);
+ }
+
+ TQPtrList<SimpleJob> joblist;
+ SlaveList activeSlaves;
+ int maxSlaves;
+ int skipCount;
+ TQString protocol;
+};
+
+class TDEIO::Scheduler::ProtocolInfoDict : public TQDict<TDEIO::Scheduler::ProtocolInfo>
+{
+ public:
+ ProtocolInfoDict() { }
+
+ TDEIO::Scheduler::ProtocolInfo *get( const TQString &protocol);
+};
+
+TDEIO::Scheduler::ProtocolInfo *
+TDEIO::Scheduler::ProtocolInfoDict::get(const TQString &protocol)
+{
+ ProtocolInfo *info = find(protocol);
+ if (!info)
+ {
+ info = new ProtocolInfo;
+ info->protocol = protocol;
+ info->maxSlaves = KProtocolInfo::maxSlaves( protocol );
+
+ insert(protocol, info);
+ }
+ return info;
+}
+
+
+Scheduler::Scheduler()
+ : DCOPObject( "TDEIO::Scheduler" ),
+ TQObject(kapp, "scheduler"),
+ slaveTimer(0, "Scheduler::slaveTimer"),
+ coSlaveTimer(0, "Scheduler::coSlaveTimer"),
+ cleanupTimer(0, "Scheduler::cleanupTimer")
+{
+ checkOnHold = true; // !! Always check with KLauncher for the first request.
+ slaveOnHold = 0;
+ protInfoDict = new ProtocolInfoDict;
+ slaveList = new SlaveList;
+ idleSlaves = new SlaveList;
+ coIdleSlaves = new SlaveList;
+ extraJobData = new ExtraJobData;
+ sessionData = new SessionData;
+ slaveConfig = SlaveConfig::self();
+ connect(&slaveTimer, TQT_SIGNAL(timeout()), TQT_SLOT(startStep()));
+ connect(&coSlaveTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotScheduleCoSlave()));
+ connect(&cleanupTimer, TQT_SIGNAL(timeout()), TQT_SLOT(slotCleanIdleSlaves()));
+ busy = false;
+}
+
+Scheduler::~Scheduler()
+{
+ protInfoDict->setAutoDelete(true);
+ delete protInfoDict; protInfoDict = 0;
+ delete idleSlaves; idleSlaves = 0;
+ delete coIdleSlaves; coIdleSlaves = 0;
+ slaveList->setAutoDelete(true);
+ delete slaveList; slaveList = 0;
+ delete extraJobData; extraJobData = 0;
+ delete sessionData; sessionData = 0;
+ instance = 0;
+}
+
+void
+Scheduler::debug_info()
+{
+}
+
+bool Scheduler::process(const TQCString &fun, const TQByteArray &data, TQCString &replyType, TQByteArray &replyData )
+{
+ if ( fun != "reparseSlaveConfiguration(TQString)" )
+ return DCOPObject::process( fun, data, replyType, replyData );
+
+ slaveConfig = SlaveConfig::self();
+ replyType = "void";
+ TQDataStream stream( data, IO_ReadOnly );
+ TQString proto;
+ stream >> proto;
+
+ kdDebug( 7006 ) << "reparseConfiguration( " << proto << " )" << endl;
+ KProtocolManager::reparseConfiguration();
+ slaveConfig->reset();
+ sessionData->reset();
+ NetRC::self()->reload();
+
+ Slave *slave = slaveList->first();
+ for (; slave; slave = slaveList->next() )
+ if ( slave->slaveProtocol() == proto || proto.isEmpty() )
+ {
+ slave->send( CMD_REPARSECONFIGURATION );
+ slave->resetHost();
+ }
+ return true;
+}
+
+QCStringList Scheduler::functions()
+{
+ QCStringList funcs = DCOPObject::functions();
+ funcs << "void reparseSlaveConfiguration(TQString)";
+ return funcs;
+}
+
+void Scheduler::_doJob(SimpleJob *job) {
+ JobData *jobData = new JobData;
+ jobData->protocol = KProtocolManager::slaveProtocol(job->url(), jobData->proxy);
+// kdDebug(7006) << "Scheduler::_doJob protocol=" << jobData->protocol << endl;
+ if (job->command() == CMD_GET)
+ {
+ jobData->checkOnHold = checkOnHold;
+ checkOnHold = false;
+ }
+ extraJobData->replace(job, jobData);
+ newJobs.append(job);
+ slaveTimer.start(0, true);
+#ifndef NDEBUG
+ if (newJobs.count() > 150)
+ kdDebug() << "WARNING - TDEIO::Scheduler got more than 150 jobs! This shows a misuse in your app (yes, a job is a TQObject)." << endl;
+#endif
+}
+
+void Scheduler::_scheduleJob(SimpleJob *job) {
+ newJobs.removeRef(job);
+ JobData *jobData = extraJobData->find(job);
+ if (!jobData)
+{
+ kdFatal(7006) << "BUG! _ScheduleJob(): No extraJobData for job!" << endl;
+ return;
+}
+ TQString protocol = jobData->protocol;
+// kdDebug(7006) << "Scheduler::_scheduleJob protocol=" << protocol << endl;
+ ProtocolInfo *protInfo = protInfoDict->get(protocol);
+ protInfo->joblist.append(job);
+
+ slaveTimer.start(0, true);
+}
+
+void Scheduler::_cancelJob(SimpleJob *job) {
+// kdDebug(7006) << "Scheduler: canceling job " << job << endl;
+ Slave *slave = job->slave();
+ if ( !slave )
+ {
+ // was not yet running (don't call this on a finished job!)
+ JobData *jobData = extraJobData->find(job);
+ if (!jobData)
+ return; // I said: "Don't call this on a finished job!"
+
+ newJobs.removeRef(job);
+ ProtocolInfo *protInfo = protInfoDict->get(jobData->protocol);
+ protInfo->joblist.removeRef(job);
+
+ // Search all slaves to see if job is in the queue of a coSlave
+ slave = slaveList->first();
+ for(; slave; slave = slaveList->next())
+ {
+ JobList *list = coSlaves.find(slave);
+ if (list && list->removeRef(job))
+ break; // Job was found and removed.
+ // Fall through to kill the slave as well!
+ }
+ if (!slave)
+ {
+ extraJobData->remove(job);
+ return; // Job was not yet running and not in a coSlave queue.
+ }
+ }
+ kdDebug(7006) << "Scheduler: killing slave " << slave->slave_pid() << endl;
+ slave->kill();
+ _jobFinished( job, slave );
+ slotSlaveDied( slave);
+}
+
+void Scheduler::startStep()
+{
+ while(newJobs.count())
+ {
+ (void) startJobDirect();
+ }
+ TQDictIterator<TDEIO::Scheduler::ProtocolInfo> it(*protInfoDict);
+ while(it.current())
+ {
+ if (startJobScheduled(it.current())) return;
+ ++it;
+ }
+}
+
+void Scheduler::setupSlave(TDEIO::Slave *slave, const KURL &url, const TQString &protocol, const TQString &proxy , bool newSlave, const TDEIO::MetaData *config)
+{
+ TQString host = url.host();
+ int port = url.port();
+ TQString user = url.user();
+ TQString passwd = url.pass();
+
+ if ((newSlave) ||
+ (slave->host() != host) ||
+ (slave->port() != port) ||
+ (slave->user() != user) ||
+ (slave->passwd() != passwd))
+ {
+ slaveConfig = SlaveConfig::self();
+
+ MetaData configData = slaveConfig->configData(protocol, host);
+ sessionData->configDataFor( configData, protocol, host );
+
+ configData["UseProxy"] = proxy;
+
+ TQString autoLogin = configData["EnableAutoLogin"].lower();
+ if ( autoLogin == "true" )
+ {
+ NetRC::AutoLogin l;
+ l.login = user;
+ bool usern = (protocol == "ftp");
+ if ( NetRC::self()->lookup( url, l, usern) )
+ {
+ configData["autoLoginUser"] = l.login;
+ configData["autoLoginPass"] = l.password;
+ if ( usern )
+ {
+ TQString macdef;
+ TQMap<TQString, TQStringList>::ConstIterator it = l.macdef.begin();
+ for ( ; it != l.macdef.end(); ++it )
+ macdef += it.key() + '\\' + it.data().join( "\\" ) + '\n';
+ configData["autoLoginMacro"] = macdef;
+ }
+ }
+ }
+ if (config)
+ configData += *config;
+ slave->setConfig(configData);
+ slave->setProtocol(url.protocol());
+ slave->setHost(host, port, user, passwd);
+ }
+}
+
+bool Scheduler::startJobScheduled(ProtocolInfo *protInfo)
+{
+ if (protInfo->joblist.isEmpty())
+ return false;
+
+// kdDebug(7006) << "Scheduling job" << endl;
+ debug_info();
+ bool newSlave = false;
+
+ SimpleJob *job = 0;
+ Slave *slave = 0;
+
+ if (protInfo->skipCount > 2)
+ {
+ bool dummy;
+ // Prevent starvation. We skip the first entry in the queue at most
+ // 2 times in a row. The
+ protInfo->skipCount = 0;
+ job = protInfo->joblist.at(0);
+ slave = findIdleSlave(protInfo, job, dummy );
+ }
+ else
+ {
+ bool exact=false;
+ SimpleJob *firstJob = 0;
+ Slave *firstSlave = 0;
+ for(uint i = 0; (i < protInfo->joblist.count()) && (i < 10); i++)
+ {
+ job = protInfo->joblist.at(i);
+ slave = findIdleSlave(protInfo, job, exact);
+ if (!firstSlave)
+ {
+ firstJob = job;
+ firstSlave = slave;
+ }
+ if (!slave) break;
+ if (exact) break;
+ }
+
+ if (!exact)
+ {
+ slave = firstSlave;
+ job = firstJob;
+ }
+ if (job == firstJob)
+ protInfo->skipCount = 0;
+ else
+ protInfo->skipCount++;
+ }
+
+ if (!slave)
+ {
+ if ( protInfo->maxSlaves > static_cast<int>(protInfo->activeSlaves.count()) )
+ {
+ newSlave = true;
+ slave = createSlave(protInfo, job, job->url());
+ if (!slave)
+ slaveTimer.start(0, true);
+ }
+ }
+
+ if (!slave)
+ {
+// kdDebug(7006) << "No slaves available" << endl;
+// kdDebug(7006) << " -- active: " << protInfo->activeSlaves.count() << endl;
+ return false;
+ }
+
+ protInfo->activeSlaves.append(slave);
+ idleSlaves->removeRef(slave);
+ protInfo->joblist.removeRef(job);
+// kdDebug(7006) << "scheduler: job started " << job << endl;
+
+
+ JobData *jobData = extraJobData->find(job);
+ setupSlave(slave, job->url(), jobData->protocol, jobData->proxy, newSlave);
+ job->start(slave);
+
+ slaveTimer.start(0, true);
+ return true;
+}
+
+bool Scheduler::startJobDirect()
+{
+ debug_info();
+ SimpleJob *job = newJobs.take(0);
+ JobData *jobData = extraJobData->find(job);
+ if (!jobData)
+ {
+ kdFatal(7006) << "BUG! startjobDirect(): No extraJobData for job!"
+ << endl;
+ return false;
+ }
+ TQString protocol = jobData->protocol;
+ ProtocolInfo *protInfo = protInfoDict->get(protocol);
+
+ bool newSlave = false;
+ bool dummy;
+
+ // Look for matching slave
+ Slave *slave = findIdleSlave(protInfo, job, dummy);
+
+ if (!slave)
+ {
+ newSlave = true;
+ slave = createSlave(protInfo, job, job->url());
+ }
+
+ if (!slave)
+ return false;
+
+ idleSlaves->removeRef(slave);
+// kdDebug(7006) << "scheduler: job started " << job << endl;
+
+ setupSlave(slave, job->url(), protocol, jobData->proxy, newSlave);
+ job->start(slave);
+ return true;
+}
+
+static Slave *searchIdleList(SlaveList *idleSlaves, const KURL &url, const TQString &protocol, bool &exact)
+{
+ TQString host = url.host();
+ int port = url.port();
+ TQString user = url.user();
+ exact = true;
+
+ for( Slave *slave = idleSlaves->first();
+ slave;
+ slave = idleSlaves->next())
+ {
+ if ((protocol == slave->slaveProtocol()) &&
+ (host == slave->host()) &&
+ (port == slave->port()) &&
+ (user == slave->user()))
+ return slave;
+ }
+
+ exact = false;
+
+ // Look for slightly matching slave
+ for( Slave *slave = idleSlaves->first();
+ slave;
+ slave = idleSlaves->next())
+ {
+ if (protocol == slave->slaveProtocol())
+ return slave;
+ }
+ return 0;
+}
+
+Slave *Scheduler::findIdleSlave(ProtocolInfo *, SimpleJob *job, bool &exact)
+{
+ Slave *slave = 0;
+ JobData *jobData = extraJobData->find(job);
+ if (!jobData)
+ {
+ kdFatal(7006) << "BUG! findIdleSlave(): No extraJobData for job!" << endl;
+ return 0;
+ }
+ if (jobData->checkOnHold)
+ {
+ slave = Slave::holdSlave(jobData->protocol, job->url());
+ if (slave)
+ return slave;
+ }
+ if (slaveOnHold)
+ {
+ // Make sure that the job wants to do a GET or a POST, and with no offset
+ bool bCanReuse = (job->command() == CMD_GET);
+ TDEIO::TransferJob * tJob = dynamic_cast<TDEIO::TransferJob *>(job);
+ if ( tJob )
+ {
+ bCanReuse = (job->command() == CMD_GET || job->command() == CMD_SPECIAL);
+ if ( bCanReuse )
+ {
+ TDEIO::MetaData outgoing = tJob->outgoingMetaData();
+ TQString resume = (!outgoing.contains("resume")) ? TQString() : outgoing["resume"];
+ kdDebug(7006) << "Resume metadata is '" << resume << "'" << endl;
+ bCanReuse = (resume.isEmpty() || resume == "0");
+ }
+ }
+// kdDebug(7006) << "bCanReuse = " << bCanReuse << endl;
+ if (bCanReuse)
+ {
+ if (job->url() == urlOnHold)
+ {
+ kdDebug(7006) << "HOLD: Reusing held slave for " << urlOnHold.prettyURL() << endl;
+ slave = slaveOnHold;
+ }
+ else
+ {
+ kdDebug(7006) << "HOLD: Discarding held slave (" << urlOnHold.prettyURL() << ")" << endl;
+ slaveOnHold->kill();
+ }
+ slaveOnHold = 0;
+ urlOnHold = KURL();
+ }
+ if (slave)
+ return slave;
+ }
+
+ return searchIdleList(idleSlaves, job->url(), jobData->protocol, exact);
+}
+
+Slave *Scheduler::createSlave(ProtocolInfo *protInfo, SimpleJob *job, const KURL &url)
+{
+ int error;
+ TQString errortext;
+ Slave *slave = Slave::createSlave(protInfo->protocol, url, error, errortext);
+ if (slave)
+ {
+ slaveList->append(slave);
+ idleSlaves->append(slave);
+ connect(slave, TQT_SIGNAL(slaveDied(TDEIO::Slave *)),
+ TQT_SLOT(slotSlaveDied(TDEIO::Slave *)));
+ connect(slave, TQT_SIGNAL(slaveStatus(pid_t,const TQCString &,const TQString &, bool)),
+ TQT_SLOT(slotSlaveStatus(pid_t,const TQCString &, const TQString &, bool)));
+
+ connect(slave,TQT_SIGNAL(authorizationKey(const TQCString&, const TQCString&, bool)),
+ sessionData,TQT_SLOT(slotAuthData(const TQCString&, const TQCString&, bool)));
+ connect(slave,TQT_SIGNAL(delAuthorization(const TQCString&)), sessionData,
+ TQT_SLOT(slotDelAuthData(const TQCString&)));
+ }
+ else
+ {
+ kdError() <<": couldn't create slave : " << errortext << endl;
+ if (job)
+ {
+ protInfo->joblist.removeRef(job);
+ extraJobData->remove(job);
+ job->slotError( error, errortext );
+ }
+ }
+ return slave;
+}
+
+void Scheduler::slotSlaveStatus(pid_t, const TQCString &, const TQString &, bool)
+{
+}
+
+void Scheduler::_jobFinished(SimpleJob *job, Slave *slave)
+{
+ JobData *jobData = extraJobData->take(job);
+ if (!jobData)
+ {
+ kdFatal(7006) << "BUG! _jobFinished(): No extraJobData for job!" << endl;
+ return;
+ }
+ ProtocolInfo *protInfo = protInfoDict->get(jobData->protocol);
+ delete jobData;
+ slave->disconnect(job);
+ protInfo->activeSlaves.removeRef(slave);
+ if (slave->isAlive())
+ {
+ JobList *list = coSlaves.find(slave);
+ if (list)
+ {
+ assert(slave->isConnected());
+ assert(!coIdleSlaves->contains(slave));
+ coIdleSlaves->append(slave);
+ if (!list->isEmpty())
+ coSlaveTimer.start(0, true);
+ return;
+ }
+ else
+ {
+ assert(!slave->isConnected());
+ idleSlaves->append(slave);
+ slave->setIdle();
+ _scheduleCleanup();
+// slave->send( CMD_SLAVE_STATUS );
+ }
+ }
+ if (protInfo->joblist.count())
+ {
+ slaveTimer.start(0, true);
+ }
+}
+
+void Scheduler::slotSlaveDied(TDEIO::Slave *slave)
+{
+ assert(!slave->isAlive());
+ ProtocolInfo *protInfo = protInfoDict->get(slave->slaveProtocol());
+ protInfo->activeSlaves.removeRef(slave);
+ if (slave == slaveOnHold)
+ {
+ slaveOnHold = 0;
+ urlOnHold = KURL();
+ }
+ idleSlaves->removeRef(slave);
+ JobList *list = coSlaves.find(slave);
+ if (list)
+ {
+ // coSlave dies, kill jobs waiting in queue
+ disconnectSlave(slave);
+ }
+
+ if (!slaveList->removeRef(slave))
+ kdDebug(7006) << "Scheduler: BUG!! Slave " << slave << "/" << slave->slave_pid() << " died, but is NOT in slaveList!!!\n" << endl;
+ else
+ slave->deref(); // Delete slave
+}
+
+void Scheduler::slotCleanIdleSlaves()
+{
+ for(Slave *slave = idleSlaves->first();slave;)
+ {
+ if (slave->idleTime() >= MAX_SLAVE_IDLE)
+ {
+ // kdDebug(7006) << "Removing idle slave: " << slave->slaveProtocol() << " " << slave->host() << endl;
+ Slave *removeSlave = slave;
+ slave = idleSlaves->next();
+ idleSlaves->removeRef(removeSlave);
+ slaveList->removeRef(removeSlave);
+ removeSlave->connection()->close();
+ removeSlave->deref();
+ }
+ else
+ {
+ slave = idleSlaves->next();
+ }
+ }
+ _scheduleCleanup();
+}
+
+void Scheduler::_scheduleCleanup()
+{
+ if (idleSlaves->count())
+ {
+ if (!cleanupTimer.isActive())
+ cleanupTimer.start( MAX_SLAVE_IDLE*1000, true );
+ }
+}
+
+void Scheduler::_putSlaveOnHold(TDEIO::SimpleJob *job, const KURL &url)
+{
+ Slave *slave = job->slave();
+ slave->disconnect(job);
+
+ if (slaveOnHold)
+ {
+ slaveOnHold->kill();
+ }
+ slaveOnHold = slave;
+ urlOnHold = url;
+ slaveOnHold->suspend();
+}
+
+void Scheduler::_publishSlaveOnHold()
+{
+ if (!slaveOnHold)
+ return;
+
+ slaveOnHold->hold(urlOnHold);
+}
+
+void Scheduler::_removeSlaveOnHold()
+{
+ if (slaveOnHold)
+ {
+ slaveOnHold->kill();
+ }
+ slaveOnHold = 0;
+ urlOnHold = KURL();
+}
+
+Slave *
+Scheduler::_getConnectedSlave(const KURL &url, const TDEIO::MetaData &config )
+{
+ TQString proxy;
+ TQString protocol = KProtocolManager::slaveProtocol(url, proxy);
+ bool dummy;
+ Slave *slave = searchIdleList(idleSlaves, url, protocol, dummy);
+ if (!slave)
+ {
+ ProtocolInfo *protInfo = protInfoDict->get(protocol);
+ slave = createSlave(protInfo, 0, url);
+ }
+ if (!slave)
+ return 0; // Error
+ idleSlaves->removeRef(slave);
+
+ setupSlave(slave, url, protocol, proxy, true, &config);
+
+ slave->send( CMD_CONNECT );
+ connect(slave, TQT_SIGNAL(connected()),
+ TQT_SLOT(slotSlaveConnected()));
+ connect(slave, TQT_SIGNAL(error(int, const TQString &)),
+ TQT_SLOT(slotSlaveError(int, const TQString &)));
+
+ coSlaves.insert(slave, new TQPtrList<SimpleJob>());
+// kdDebug(7006) << "_getConnectedSlave( " << slave << ")" << endl;
+ return slave;
+}
+
+void
+Scheduler::slotScheduleCoSlave()
+{
+ Slave *nextSlave;
+ slaveConfig = SlaveConfig::self();
+ for(Slave *slave = coIdleSlaves->first();
+ slave;
+ slave = nextSlave)
+ {
+ nextSlave = coIdleSlaves->next();
+ JobList *list = coSlaves.find(slave);
+ assert(list);
+ if (list && !list->isEmpty())
+ {
+ SimpleJob *job = list->take(0);
+ coIdleSlaves->removeRef(slave);
+// kdDebug(7006) << "scheduler: job started " << job << endl;
+
+ assert(!coIdleSlaves->contains(slave));
+
+ KURL url =job->url();
+ TQString host = url.host();
+ int port = url.port();
+
+ if (slave->host() == "<reset>")
+ {
+ TQString user = url.user();
+ TQString passwd = url.pass();
+
+ MetaData configData = slaveConfig->configData(url.protocol(), url.host());
+ slave->setConfig(configData);
+ slave->setProtocol(url.protocol());
+ slave->setHost(host, port, user, passwd);
+ }
+
+ assert(slave->protocol() == url.protocol());
+ assert(slave->host() == host);
+ assert(slave->port() == port);
+ job->start(slave);
+ }
+ }
+}
+
+void
+Scheduler::slotSlaveConnected()
+{
+ Slave *slave = (Slave *)sender();
+// kdDebug(7006) << "slotSlaveConnected( " << slave << ")" << endl;
+ slave->setConnected(true);
+ disconnect(slave, TQT_SIGNAL(connected()),
+ this, TQT_SLOT(slotSlaveConnected()));
+ emit slaveConnected(slave);
+ assert(!coIdleSlaves->contains(slave));
+ coIdleSlaves->append(slave);
+ coSlaveTimer.start(0, true);
+}
+
+void
+Scheduler::slotSlaveError(int errorNr, const TQString &errorMsg)
+{
+ Slave *slave = (Slave *)sender();
+ if (!slave->isConnected() || (coIdleSlaves->find(slave) != -1))
+ {
+ // Only forward to application if slave is idle or still connecting.
+ emit slaveError(slave, errorNr, errorMsg);
+ }
+}
+
+bool
+Scheduler::_assignJobToSlave(TDEIO::Slave *slave, SimpleJob *job)
+{
+// kdDebug(7006) << "_assignJobToSlave( " << job << ", " << slave << ")" << endl;
+ TQString dummy;
+ if ((slave->slaveProtocol() != KProtocolManager::slaveProtocol( job->url(), dummy ))
+ ||
+ (!newJobs.removeRef(job)))
+ {
+ kdDebug(7006) << "_assignJobToSlave(): ERROR, nonmatching or unknown job." << endl;
+ job->kill();
+ return false;
+ }
+
+ JobList *list = coSlaves.find(slave);
+ assert(list);
+ if (!list)
+ {
+ kdDebug(7006) << "_assignJobToSlave(): ERROR, unknown slave." << endl;
+ job->kill();
+ return false;
+ }
+
+ assert(list->contains(job) == 0);
+ list->append(job);
+ coSlaveTimer.start(0, true); // Start job on timer event
+
+ return true;
+}
+
+bool
+Scheduler::_disconnectSlave(TDEIO::Slave *slave)
+{
+// kdDebug(7006) << "_disconnectSlave( " << slave << ")" << endl;
+ JobList *list = coSlaves.take(slave);
+ assert(list);
+ if (!list)
+ return false;
+ // Kill jobs still in queue.
+ while(!list->isEmpty())
+ {
+ Job *job = list->take(0);
+ job->kill();
+ }
+ delete list;
+ coIdleSlaves->removeRef(slave);
+ assert(!coIdleSlaves->contains(slave));
+ disconnect(slave, TQT_SIGNAL(connected()),
+ this, TQT_SLOT(slotSlaveConnected()));
+ disconnect(slave, TQT_SIGNAL(error(int, const TQString &)),
+ this, TQT_SLOT(slotSlaveError(int, const TQString &)));
+ if (slave->isAlive())
+ {
+ idleSlaves->append(slave);
+ slave->send( CMD_DISCONNECT );
+ slave->setIdle();
+ slave->setConnected(false);
+ _scheduleCleanup();
+ }
+ return true;
+}
+
+void
+Scheduler::_checkSlaveOnHold(bool b)
+{
+ checkOnHold = b;
+}
+
+void
+Scheduler::_registerWindow(TQWidget *wid)
+{
+ if (!wid)
+ return;
+
+ TQObject *obj = TQT_TQOBJECT(wid);
+ if (!m_windowList.contains(obj))
+ {
+ // We must store the window Id because by the time
+ // the destroyed signal is emitted we can no longer
+ // access TQWidget::winId() (already destructed)
+ WId windowId = wid->winId();
+ m_windowList.insert(obj, windowId);
+ connect(TQT_TQOBJECT(wid), TQT_SIGNAL(destroyed(TQObject *)),
+ this, TQT_SLOT(slotUnregisterWindow(TQObject*)));
+ TQByteArray params;
+ TQDataStream stream(params, IO_WriteOnly);
+ stream << windowId;
+ if( !kapp->dcopClient()->send( "kded", "kded",
+ "registerWindowId(long int)", params ) )
+ kdDebug(7006) << "Could not register window with kded!" << endl;
+ }
+}
+
+void
+Scheduler::slotUnregisterWindow(TQObject *obj)
+{
+ if (!obj)
+ return;
+
+ TQMap<TQObject *, WId>::Iterator it = m_windowList.find(obj);
+ if (it == m_windowList.end())
+ return;
+ WId windowId = it.data();
+ disconnect( it.key(), TQT_SIGNAL(destroyed(TQObject *)),
+ this, TQT_SLOT(slotUnregisterWindow(TQObject*)));
+ m_windowList.remove( it );
+ if (kapp)
+ {
+ TQByteArray params;
+ TQDataStream stream(params, IO_WriteOnly);
+ stream << windowId;
+ kapp->dcopClient()->send( "kded", "kded",
+ "unregisterWindowId(long int)", params );
+ }
+}
+
+Scheduler* Scheduler::self() {
+ if ( !instance ) {
+ instance = new Scheduler;
+ }
+ return instance;
+}
+
+void Scheduler::virtual_hook( int id, void* data )
+{ DCOPObject::virtual_hook( id, data ); }
+
+
+
+#include "scheduler.moc"
diff --git a/tdeio/tdeio/scheduler.h b/tdeio/tdeio/scheduler.h
new file mode 100644
index 000000000..e55b2293c
--- /dev/null
+++ b/tdeio/tdeio/scheduler.h
@@ -0,0 +1,364 @@
+// -*- c++ -*-
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
+ Waldo Bastian <bastian@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _kio_scheduler_h
+#define _kio_scheduler_h
+
+#include "tdeio/job.h"
+#include "tdeio/jobclasses.h"
+#include <tqtimer.h>
+#include <tqptrdict.h>
+#include <tqmap.h>
+
+#include <dcopobject.h>
+
+namespace TDEIO {
+
+ class Slave;
+ class SlaveList;
+ class SlaveConfig;
+ class SessionData;
+
+ /**
+ * The TDEIO::Scheduler manages io-slaves for the application.
+ * It also queues jobs and assigns the job to a slave when one
+ * becomes available.
+ *
+ * There are 3 possible ways for a job to get a slave:
+ *
+ * <h3>1. Direct</h3>
+ * This is the default. When you create a job the
+ * TDEIO::Scheduler will be notified and will find either an existing
+ * slave that is idle or it will create a new slave for the job.
+ *
+ * Example:
+ * \code
+ * TransferJob *job = TDEIO::get(KURL("http://www.kde.org"));
+ * \endcode
+ *
+ *
+ * <h3>2. Scheduled</h3>
+ * If you create a lot of jobs, you might want not want to have a
+ * slave for each job. If you schedule a job, a maximum number
+ * of slaves will be created. When more jobs arrive, they will be
+ * queued. When a slave is finished with a job, it will be assigned
+ * a job from the queue.
+ *
+ * Example:
+ * \code
+ * TransferJob *job = TDEIO::get(KURL("http://www.kde.org"));
+ * TDEIO::Scheduler::scheduleJob(job);
+ * \endcode
+ *
+ * <h3>3. Connection Oriented</h3>
+ * For some operations it is important that multiple jobs use
+ * the same connection. This can only be ensured if all these jobs
+ * use the same slave.
+ *
+ * You can ask the scheduler to open a slave for connection oriented
+ * operations. You can then use the scheduler to assign jobs to this
+ * slave. The jobs will be queued and the slave will handle these jobs
+ * one after the other.
+ *
+ * Example:
+ * \code
+ * Slave *slave = TDEIO::Scheduler::getConnectedSlave(
+ * KURL("pop3://bastian:password@mail.kde.org"));
+ * TransferJob *job1 = TDEIO::get(
+ * KURL("pop3://bastian:password@mail.kde.org/msg1"));
+ * TDEIO::Scheduler::assignJobToSlave(slave, job1);
+ * TransferJob *job2 = TDEIO::get(
+ * KURL("pop3://bastian:password@mail.kde.org/msg2"));
+ * TDEIO::Scheduler::assignJobToSlave(slave, job2);
+ * TransferJob *job3 = TDEIO::get(
+ * KURL("pop3://bastian:password@mail.kde.org/msg3"));
+ * TDEIO::Scheduler::assignJobToSlave(slave, job3);
+ *
+ * // ... Wait for jobs to finish...
+ *
+ * TDEIO::Scheduler::disconnectSlave(slave);
+ * \endcode
+ *
+ * Note that you need to explicitly disconnect the slave when the
+ * connection goes down, so your error handler should contain:
+ * \code
+ * if (error == TDEIO::ERR_CONNECTION_BROKEN)
+ * TDEIO::Scheduler::disconnectSlave(slave);
+ * \endcode
+ *
+ * @see TDEIO::Slave
+ * @see TDEIO::Job
+ **/
+
+ class TDEIO_EXPORT Scheduler : public TQObject, virtual public DCOPObject {
+ Q_OBJECT
+
+ public:
+ typedef TQPtrList<SimpleJob> JobList;
+
+ // InfoDict needs Info, so we can't declare it private
+ class ProtocolInfo;
+ class JobData;
+
+ ~Scheduler();
+
+ /**
+ * Register @p job with the scheduler.
+ * The default is to create a new slave for the job if no slave
+ * is available. This can be changed by calling scheduleJob.
+ * @param job the job to register
+ */
+ static void doJob(SimpleJob *job)
+ { self()->_doJob(job); }
+
+ /**
+ * Calling ths function makes that @p job gets scheduled for later
+ * execution, if multiple jobs are registered it might wait for
+ * other jobs to finish.
+ * @param job the job to schedule
+ */
+ static void scheduleJob(SimpleJob *job)
+ { self()->_scheduleJob(job); }
+
+ /**
+ * Stop the execution of a job.
+ * @param job the job to cancel
+ */
+ static void cancelJob(SimpleJob *job)
+ { self()->_cancelJob(job); }
+
+ /**
+ * Called when a job is done.
+ * @param job the finished job
+ * @param slave the slave that executed the @p job
+ */
+ static void jobFinished(TDEIO::SimpleJob *job, TDEIO::Slave *slave)
+ { self()->_jobFinished(job, slave); }
+
+ /**
+ * Puts a slave on notice. A next job may reuse this slave if it
+ * requests the same URL.
+ *
+ * A job can be put on hold after it has emit'ed its mimetype.
+ * Based on the mimetype, the program can give control to another
+ * component in the same process which can then resume the job
+ * by simply asking for the same URL again.
+ * @param job the job that should be stopped
+ * @param url the URL that is handled by the @p url
+ */
+ static void putSlaveOnHold(TDEIO::SimpleJob *job, const KURL &url)
+ { self()->_putSlaveOnHold(job, url); }
+
+ /**
+ * Removes any slave that might have been put on hold. If a slave
+ * was put on hold it will be killed.
+ */
+ static void removeSlaveOnHold()
+ { self()->_removeSlaveOnHold(); }
+
+ /**
+ * Send the slave that was put on hold back to KLauncher. This
+ * allows another process to take over the slave and resume the job
+ * that was started.
+ */
+ static void publishSlaveOnHold()
+ { self()->_publishSlaveOnHold(); }
+
+ /**
+ * Requests a slave for use in connection-oriented mode.
+ *
+ * @param url This defines the username,password,host & port to
+ * connect with.
+ * @param config Configuration data for the slave.
+ *
+ * @return A pointer to a connected slave or 0 if an error occurred.
+ * @see assignJobToSlave()
+ * @see disconnectSlave()
+ */
+ static TDEIO::Slave *getConnectedSlave(const KURL &url, const TDEIO::MetaData &config = MetaData() )
+ { return self()->_getConnectedSlave(url, config); }
+
+ /*
+ * Uses @p slave to do @p job.
+ * This function should be called immediately after creating a Job.
+ *
+ * @param slave The slave to use. The slave must have been obtained
+ * with a call to getConnectedSlave and must not
+ * be currently assigned to any other job.
+ * @param job The job to do.
+ *
+ * @return true is successful, false otherwise.
+ *
+ * @see getConnectedSlave()
+ * @see disconnectSlave()
+ * @see slaveConnected()
+ * @see slaveError()
+ */
+ static bool assignJobToSlave(TDEIO::Slave *slave, TDEIO::SimpleJob *job)
+ { return self()->_assignJobToSlave(slave, job); }
+
+ /*
+ * Disconnects @p slave.
+ *
+ * @param slave The slave to disconnect. The slave must have been
+ * obtained with a call to getConnectedSlave
+ * and must not be assigned to any job.
+ *
+ * @return true is successful, false otherwise.
+ *
+ * @see getConnectedSlave
+ * @see assignJobToSlave
+ */
+ static bool disconnectSlave(TDEIO::Slave *slave)
+ { return self()->_disconnectSlave(slave); }
+
+ /**
+ * Send the slave that was put on hold back to KLauncher. This
+ * allows another process to take over the slave and resume the job
+ * the that was started.
+ * Register the mainwindow @p wid with the KIO subsystem
+ * Do not call this, it is called automatically from
+ * void TDEIO::Job::setWindow(TQWidget*).
+ * @param wid the window to register
+ * @since 3.1
+ */
+ static void registerWindow(TQWidget *wid)
+ { self()->_registerWindow(wid); }
+
+ /**
+ * @internal
+ * Unregisters the window registered by registerWindow().
+ */
+ static void unregisterWindow(TQObject *wid)
+ { self()->slotUnregisterWindow(wid); }
+
+ /**
+ * Function to connect signals emitted by the scheduler.
+ *
+ * @see slaveConnected()
+ * @see slaveError()
+ */
+ static bool connect( const char *signal, const TQObject *receiver,
+ const char *member)
+ { return TQObject::connect(self(), signal, receiver, member); }
+
+ static bool connect( const TQObject* sender, const char* signal,
+ const TQObject* receiver, const char* member )
+ { return TQObject::connect(sender, signal, receiver, member); }
+
+ static bool disconnect( const TQObject* sender, const char* signal,
+ const TQObject* receiver, const char* member )
+ { return TQObject::disconnect(sender, signal, receiver, member); }
+
+ bool connect( const TQObject *sender, const char *signal,
+ const char *member )
+ { return TQObject::connect(sender, signal, member); }
+
+ /**
+ * When true, the next job will check whether KLauncher has a slave
+ * on hold that is suitable for the job.
+ * @param b true when KLauncher has a job on hold
+ */
+ static void checkSlaveOnHold(bool b) { self()->_checkSlaveOnHold(b); }
+
+ void debug_info();
+
+ virtual bool process(const TQCString &fun, const TQByteArray &data,
+ TQCString& replyType, TQByteArray &replyData);
+
+ virtual QCStringList functions();
+
+ public slots:
+ void slotSlaveDied(TDEIO::Slave *slave);
+ void slotSlaveStatus(pid_t pid, const TQCString &protocol,
+ const TQString &host, bool connected);
+ signals:
+ void slaveConnected(TDEIO::Slave *slave);
+ void slaveError(TDEIO::Slave *slave, int error, const TQString &errorMsg);
+
+ protected:
+ void setupSlave(TDEIO::Slave *slave, const KURL &url, const TQString &protocol, const TQString &proxy , bool newSlave, const TDEIO::MetaData *config=0);
+ bool startJobScheduled(ProtocolInfo *protInfo);
+ bool startJobDirect();
+ Scheduler();
+
+ protected slots:
+ void startStep();
+ void slotCleanIdleSlaves();
+ void slotSlaveConnected();
+ void slotSlaveError(int error, const TQString &errorMsg);
+ void slotScheduleCoSlave();
+ /// @since 3.1
+ void slotUnregisterWindow(TQObject *);
+
+ private:
+ class ProtocolInfoDict;
+ class ExtraJobData;
+
+ Scheduler(const Scheduler&);
+ static Scheduler *self();
+ static Scheduler *instance;
+ void _doJob(SimpleJob *job);
+ void _scheduleJob(SimpleJob *job);
+ void _cancelJob(SimpleJob *job);
+ void _jobFinished(TDEIO::SimpleJob *job, TDEIO::Slave *slave);
+ void _scheduleCleanup();
+ void _putSlaveOnHold(TDEIO::SimpleJob *job, const KURL &url);
+ void _removeSlaveOnHold();
+ Slave *_getConnectedSlave(const KURL &url, const TDEIO::MetaData &metaData );
+ bool _assignJobToSlave(TDEIO::Slave *slave, TDEIO::SimpleJob *job);
+ bool _disconnectSlave(TDEIO::Slave *slave);
+ void _checkSlaveOnHold(bool b);
+ void _publishSlaveOnHold();
+ void _registerWindow(TQWidget *wid);
+
+ Slave *findIdleSlave(ProtocolInfo *protInfo, SimpleJob *job, bool &exact);
+ Slave *createSlave(ProtocolInfo *protInfo, SimpleJob *job, const KURL &url);
+
+
+ TQTimer slaveTimer;
+ TQTimer coSlaveTimer;
+ TQTimer cleanupTimer;
+ bool busy;
+
+ SlaveList *slaveList;
+ SlaveList *idleSlaves;
+ SlaveList *coIdleSlaves;
+
+ ProtocolInfoDict *protInfoDict;
+ Slave *slaveOnHold;
+ KURL urlOnHold;
+ JobList newJobs;
+
+ TQPtrDict<JobList> coSlaves;
+ ExtraJobData *extraJobData;
+ SlaveConfig *slaveConfig;
+ SessionData *sessionData;
+ bool checkOnHold;
+ TQMap<TQObject *,WId> m_windowList;
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ private:
+ class SchedulerPrivate* d;
+};
+
+}
+#endif
diff --git a/tdeio/tdeio/sessiondata.cpp b/tdeio/tdeio/sessiondata.cpp
new file mode 100644
index 000000000..99c6a26f3
--- /dev/null
+++ b/tdeio/tdeio/sessiondata.cpp
@@ -0,0 +1,311 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 Dawit Alemayehu <adawit@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 (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, or (at your option) any
+ later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 51 Franklin Street,
+ Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#include <tqptrlist.h>
+#include <tqtextcodec.h>
+
+#include <kdebug.h>
+#include <tdeconfig.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kcharsets.h>
+#include <dcopclient.h>
+#include <kprotocolmanager.h>
+#include <kstandarddirs.h>
+
+#include <tdesu/client.h>
+#include <tdeio/slaveconfig.h>
+#include <tdeio/http_slave_defaults.h>
+
+#include "sessiondata.h"
+#include "sessiondata.moc"
+
+namespace TDEIO {
+
+/***************************** SessionData::AuthData ************************/
+struct SessionData::AuthData
+{
+
+public:
+ AuthData() {}
+
+ AuthData(const TQCString& k, const TQCString& g, bool p) {
+ key = k;
+ group = g;
+ persist = p;
+ }
+
+ bool isKeyMatch( const TQCString& val ) const {
+ return (val==key);
+ }
+
+ bool isGroupMatch( const TQCString& val ) const {
+ return (val==group);
+ }
+
+ TQCString key;
+ TQCString group;
+ bool persist;
+};
+
+/************************* SessionData::AuthDataList ****************************/
+class SessionData::AuthDataList : public TQPtrList<SessionData::AuthData>
+{
+public:
+ AuthDataList();
+ ~AuthDataList();
+
+ void addData( SessionData::AuthData* );
+ void removeData( const TQCString& );
+
+ bool pingCacheDaemon();
+ void registerAuthData( SessionData::AuthData* );
+ void unregisterAuthData( SessionData::AuthData* );
+ void purgeCachedData();
+
+private:
+#ifdef Q_OS_UNIX
+ KDEsuClient * m_tdesuClient;
+#endif
+};
+
+SessionData::AuthDataList::AuthDataList()
+{
+#ifdef Q_OS_UNIX
+ m_tdesuClient = new KDEsuClient;
+#endif
+ setAutoDelete(true);
+}
+
+SessionData::AuthDataList::~AuthDataList()
+{
+ purgeCachedData();
+#ifdef Q_OS_UNIX
+ delete m_tdesuClient;
+ m_tdesuClient = 0;
+#endif
+}
+
+void SessionData::AuthDataList::addData( SessionData::AuthData* d )
+{
+ TQPtrListIterator<SessionData::AuthData> it ( *this );
+ for ( ; it.current(); ++it )
+ {
+ if ( it.current()->isKeyMatch( d->key ) )
+ return;
+ }
+ registerAuthData( d );
+ append( d );
+}
+
+void SessionData::AuthDataList::removeData( const TQCString& gkey )
+{
+ TQPtrListIterator<SessionData::AuthData> it( *this );
+ for( ; it.current(); ++it )
+ {
+ if ( it.current()->isGroupMatch(gkey) && pingCacheDaemon() )
+ {
+ unregisterAuthData( it.current() );
+ remove( it.current() );
+ }
+ }
+}
+
+bool SessionData::AuthDataList::pingCacheDaemon()
+{
+#ifdef Q_OS_UNIX
+ Q_ASSERT(m_tdesuClient);
+
+ int success = m_tdesuClient->ping();
+ if( success == -1 )
+ {
+ success = m_tdesuClient->startServer();
+ if( success == -1 )
+ return false;
+ }
+ return true;
+#else
+ return false;
+#endif
+}
+
+void SessionData::AuthDataList::registerAuthData( SessionData::AuthData* d )
+{
+ if( !pingCacheDaemon() )
+ return;
+
+#ifdef Q_OS_UNIX
+ bool ok;
+ TQCString ref_key = d->key + "-refcount";
+ int count = m_tdesuClient->getVar(ref_key).toInt( &ok );
+ if( ok )
+ {
+ TQCString val;
+ val.setNum( count+1 );
+ m_tdesuClient->setVar( ref_key, val, 0, d->group );
+ }
+ else
+ m_tdesuClient->setVar( ref_key, "1", 0, d->group );
+#endif
+}
+
+void SessionData::AuthDataList::unregisterAuthData( SessionData::AuthData* d )
+{
+ if ( !d || d->persist )
+ return;
+
+ bool ok;
+ int count;
+ TQCString ref_key = d->key + "-refcount";
+
+#ifdef Q_OS_UNIX
+ count = m_tdesuClient->getVar( ref_key ).toInt( &ok );
+ if ( ok )
+ {
+ if ( count > 1 )
+ {
+ TQCString val;
+ val.setNum(count-1);
+ m_tdesuClient->setVar( ref_key, val, 0, d->group );
+ }
+ else
+ {
+ m_tdesuClient->delVars(d->key);
+ }
+ }
+#endif
+}
+
+void SessionData::AuthDataList::purgeCachedData()
+{
+ if ( !isEmpty() && pingCacheDaemon() )
+ {
+ TQPtrListIterator<SessionData::AuthData> it( *this );
+ for ( ; it.current(); ++it )
+ unregisterAuthData( it.current() );
+ }
+}
+
+/********************************* SessionData ****************************/
+
+class SessionData::SessionDataPrivate
+{
+public:
+ SessionDataPrivate() {
+ useCookie = true;
+ initDone = false;
+ }
+
+ bool initDone;
+ bool useCookie;
+ TQString charsets;
+ TQString language;
+};
+
+SessionData::SessionData()
+{
+ authData = 0;
+ d = new SessionDataPrivate;
+}
+
+SessionData::~SessionData()
+{
+ delete d;
+ delete authData;
+ d = 0L;
+ authData = 0L;
+}
+
+void SessionData::configDataFor( MetaData &configData, const TQString &proto,
+ const TQString & )
+{
+ if ( (proto.find("http", 0, false) == 0 ) ||
+ (proto.find("webdav", 0, false) == 0) )
+ {
+ if (!d->initDone)
+ reset();
+
+ // These might have already been set so check first
+ // to make sure that we do not trumpt settings sent
+ // by apps or end-user.
+ if ( configData["Cookies"].isEmpty() )
+ configData["Cookies"] = d->useCookie ? "true" : "false";
+ if ( configData["Languages"].isEmpty() )
+ configData["Languages"] = d->language;
+ if ( configData["Charsets"].isEmpty() )
+ configData["Charsets"] = d->charsets;
+ if ( configData["CacheDir"].isEmpty() )
+ configData["CacheDir"] = TDEGlobal::dirs()->saveLocation("cache", "http");
+ if ( configData["UserAgent"].isEmpty() )
+ {
+ configData["UserAgent"] = KProtocolManager::defaultUserAgent();
+ }
+ }
+}
+
+void SessionData::reset()
+{
+ d->initDone = true;
+ // Get Cookie settings...
+ TDEConfig* cfg = new TDEConfig("kcookiejarrc", true, false);
+ cfg->setGroup( "Cookie Policy" );
+ d->useCookie = cfg->readBoolEntry( "Cookies", true );
+ delete cfg;
+
+ static const TQString & english = TDEGlobal::staticQString( "en" );
+
+ // Get language settings...
+ TQStringList languageList = TDEGlobal::locale()->languagesTwoAlpha();
+ TQStringList::Iterator it = languageList.find( TQString::fromLatin1("C") );
+ if ( it != languageList.end() )
+ {
+ if ( languageList.contains( english ) > 0 )
+ languageList.remove( it );
+ else
+ (*it) = english;
+ }
+ if ( !languageList.contains( english ) )
+ languageList.append( english );
+
+ d->language = languageList.join( ", " );
+
+ d->charsets = TQString::fromLatin1(TQTextCodec::codecForLocale()->mimeName()).lower();
+ KProtocolManager::reparseConfiguration();
+}
+
+void SessionData::slotAuthData( const TQCString& key, const TQCString& gkey,
+ bool keep )
+{
+ if (!authData)
+ authData = new AuthDataList;
+ authData->addData( new SessionData::AuthData(key, gkey, keep) );
+}
+
+void SessionData::slotDelAuthData( const TQCString& gkey )
+{
+ if (!authData)
+ return;
+ authData->removeData( gkey );
+}
+
+void SessionData::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+}
diff --git a/tdeio/tdeio/sessiondata.h b/tdeio/tdeio/sessiondata.h
new file mode 100644
index 000000000..a87e63dba
--- /dev/null
+++ b/tdeio/tdeio/sessiondata.h
@@ -0,0 +1,67 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 Dawit Alemayehu <adawit@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 (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, or (at your option) any
+ later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; see the file COPYING.LIB. If not,
+ write to the Free Software Foundation, Inc., 51 Franklin Street,
+ Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __KIO_SESSIONDATA_H
+#define __KIO_SESSIONDATA_H
+
+#include <tqobject.h>
+#include <tdeio/global.h>
+
+namespace TDEIO {
+
+class SlaveConfig;
+
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT SessionData : public TQObject
+{
+ Q_OBJECT
+
+public:
+ SessionData();
+ ~SessionData();
+
+ virtual void configDataFor( TDEIO::MetaData &configData, const TQString &proto,
+ const TQString &host );
+ virtual void reset();
+
+ /// @since 3.1
+ struct AuthData;
+public slots:
+ void slotAuthData( const TQCString&, const TQCString&, bool );
+ void slotDelAuthData( const TQCString& );
+
+private:
+ class AuthDataList;
+ friend class AuthDataList;
+ AuthDataList* authData;
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class SessionDataPrivate;
+ SessionDataPrivate* d;
+};
+
+} // namespace
+
+#endif
diff --git a/tdeio/tdeio/skipdlg.cpp b/tdeio/tdeio/skipdlg.cpp
new file mode 100644
index 000000000..eab245dfc
--- /dev/null
+++ b/tdeio/tdeio/skipdlg.cpp
@@ -0,0 +1,143 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "tdeio/skipdlg.h"
+
+#include <stdio.h>
+#include <assert.h>
+
+#include <tqmessagebox.h>
+#include <tqwidget.h>
+#include <tqlayout.h>
+#include <tqlabel.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kurl.h>
+#include <kpushbutton.h>
+#include <kstdguiitem.h>
+
+#ifdef Q_WS_X11
+#include <twin.h>
+#endif
+
+using namespace TDEIO;
+
+SkipDlg::SkipDlg(TQWidget *parent, bool _multi, const TQString& _error_text, bool _modal ) :
+ KDialog ( parent, "" , _modal )
+{
+ // TODO : port to KDialogBase
+ modal = _modal;
+
+ // Set "StaysOnTop", because this dialog is typically used in tdeio_uiserver,
+ // i.e. in a separate process.
+#ifdef Q_WS_X11 //FIXME(E): Implement for QT Embedded, mac & win32
+ if (modal)
+ KWin::setState( winId(), NET::StaysOnTop );
+#endif
+
+ b0 = b1 = b2 = 0L;
+
+ setCaption( i18n( "Information" ) );
+
+ b0 = new KPushButton( KStdGuiItem::cancel(), this );
+ connect(b0, TQT_SIGNAL(clicked()), this, TQT_SLOT(b0Pressed()));
+
+ if ( _multi )
+ {
+ b1 = new TQPushButton( i18n( "Skip" ), this );
+ connect(b1, TQT_SIGNAL(clicked()), this, TQT_SLOT(b1Pressed()));
+
+ b2 = new TQPushButton( i18n( "Auto Skip" ), this );
+ connect(b2, TQT_SIGNAL(clicked()), this, TQT_SLOT(b2Pressed()));
+ }
+
+ TQVBoxLayout *vlayout = new TQVBoxLayout( this, 10, 0 );
+ // vlayout->addStrut( 360 ); makes dlg at least that wide
+
+ TQLabel * lb = new TQLabel( _error_text, this );
+ lb->setFixedHeight( lb->sizeHint().height() );
+ lb->setMinimumWidth( lb->sizeHint().width() );
+ vlayout->addWidget( lb );
+
+ vlayout->addSpacing( 10 );
+
+ TQHBoxLayout* layout = new TQHBoxLayout();
+ vlayout->addLayout( layout );
+ if ( b0 )
+ {
+ b0->setDefault( true );
+ b0->setFixedSize( b0->sizeHint() );
+ layout->addWidget( b0 );
+ layout->addSpacing( 5 );
+ }
+ if ( b1 )
+ {
+ b1->setFixedSize( b1->sizeHint() );
+ layout->addWidget( b1 );
+ layout->addSpacing( 5 );
+ }
+ if ( b2 )
+ {
+ b2->setFixedSize( b2->sizeHint() );
+ layout->addWidget( b2 );
+ layout->addSpacing( 5 );
+ }
+
+ vlayout->addStretch( 10 );
+ vlayout->activate();
+ resize( sizeHint() );
+}
+
+SkipDlg::~SkipDlg()
+{
+}
+
+void SkipDlg::b0Pressed()
+{
+ if ( modal )
+ done( 0 );
+ else
+ emit result( this, 0 );
+}
+
+void SkipDlg::b1Pressed()
+{
+ if ( modal )
+ done( 1 );
+ else
+ emit result( this, 1 );
+}
+
+void SkipDlg::b2Pressed()
+{
+ if ( modal )
+ done( 2 );
+ else
+ emit result( this, 2 );
+}
+
+SkipDlg_Result TDEIO::open_SkipDlg( bool _multi, const TQString& _error_text )
+{
+ Q_ASSERT(kapp);
+
+ SkipDlg dlg( 0L, _multi, _error_text, true );
+ return (SkipDlg_Result) dlg.exec();
+}
+
+#include "skipdlg.moc"
diff --git a/tdeio/tdeio/skipdlg.h b/tdeio/tdeio/skipdlg.h
new file mode 100644
index 000000000..56252dace
--- /dev/null
+++ b/tdeio/tdeio/skipdlg.h
@@ -0,0 +1,61 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kio_skip_dlg__
+#define __kio_skip_dlg__
+
+#include <tdelibs_export.h>
+#include <kdialog.h>
+
+class TQPushButton;
+class TQWidget;
+
+namespace TDEIO {
+
+ enum SkipDlg_Result { S_SKIP = 1, S_AUTO_SKIP = 2, S_CANCEL = 0 };
+
+ TDEIO_EXPORT SkipDlg_Result open_SkipDlg( bool _multi, const TQString& _error_text = TQString::null );
+
+/**
+ * @internal
+ */
+class TDEIO_EXPORT SkipDlg : public KDialog
+{
+ Q_OBJECT
+public:
+ SkipDlg( TQWidget *parent, bool _multi, const TQString& _error_text, bool _modal = false );
+ ~SkipDlg();
+
+protected:
+ TQPushButton *b0;
+ TQPushButton *b1;
+ TQPushButton *b2;
+
+ bool modal;
+
+public slots:
+ void b0Pressed();
+ void b1Pressed();
+ void b2Pressed();
+
+signals:
+ void result( SkipDlg *_this, int _button );
+};
+
+}
+#endif
diff --git a/tdeio/tdeio/slave.cpp b/tdeio/tdeio/slave.cpp
new file mode 100644
index 000000000..dfd6d6bb3
--- /dev/null
+++ b/tdeio/tdeio/slave.cpp
@@ -0,0 +1,519 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (c) 2000 Waldo Bastian <bastian@kde.org>
+ * 2000 Stephan Kulow <coolo@kde.org>
+ *
+ * $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include <config.h>
+
+#include <time.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <sys/types.h>
+
+#include <tqfile.h>
+#include <tqtimer.h>
+
+#include <dcopclient.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kglobal.h>
+#include <kstandarddirs.h>
+#include <kapplication.h>
+#include <ktempfile.h>
+#include <ksock.h>
+#include <kprocess.h>
+#include <klibloader.h>
+
+#include "tdeio/dataprotocol.h"
+#include "tdeio/slave.h"
+#include "tdeio/kservice.h"
+#include <tdeio/global.h>
+#include <kprotocolmanager.h>
+#include <kprotocolinfo.h>
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif
+
+#ifndef _PATH_TMP
+#define _PATH_TMP "/tmp"
+#endif
+
+using namespace TDEIO;
+
+#define SLAVE_CONNECTION_TIMEOUT_MIN 2
+
+// Without debug info we consider it an error if the slave doesn't connect
+// within 10 seconds.
+// With debug info we give the slave an hour so that developers have a chance
+// to debug their slave.
+#ifdef NDEBUG
+#define SLAVE_CONNECTION_TIMEOUT_MAX 10
+#else
+#define SLAVE_CONNECTION_TIMEOUT_MAX 3600
+#endif
+
+namespace TDEIO {
+
+ /**
+ * @internal
+ */
+ class SlavePrivate {
+ public:
+ bool derived; // true if this instance of Slave is actually an
+ // instance of a derived class.
+
+ SlavePrivate(bool derived) : derived(derived) {}
+ };
+}
+
+void Slave::accept(TDESocket *socket)
+{
+#ifndef Q_WS_WIN
+ slaveconn.init(socket);
+#endif
+ delete serv;
+ serv = 0;
+ slaveconn.connect(this, TQT_SLOT(gotInput()));
+ unlinkSocket();
+}
+
+void Slave::unlinkSocket()
+{
+ if (m_socket.isEmpty()) return;
+ TQCString filename = TQFile::encodeName(m_socket);
+ unlink(filename.data());
+ m_socket = TQString::null;
+}
+
+void Slave::timeout()
+{
+ if (!serv) return;
+ kdDebug(7002) << "slave failed to connect to application pid=" << m_pid << " protocol=" << m_protocol << endl;
+ if (m_pid && (::kill(m_pid, 0) == 0))
+ {
+ int delta_t = (int) difftime(time(0), contact_started);
+ kdDebug(7002) << "slave is slow... pid=" << m_pid << " t=" << delta_t << endl;
+ if (delta_t < SLAVE_CONNECTION_TIMEOUT_MAX)
+ {
+ TQTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, this, TQT_SLOT(timeout()));
+ return;
+ }
+ }
+ kdDebug(7002) << "Houston, we lost our slave, pid=" << m_pid << endl;
+ delete serv;
+ serv = 0;
+ unlinkSocket();
+ dead = true;
+ TQString arg = m_protocol;
+ if (!m_host.isEmpty())
+ arg += "://"+m_host;
+ kdDebug(7002) << "slave died pid = " << m_pid << endl;
+ ref();
+ // Tell the job about the problem.
+ emit error(ERR_SLAVE_DIED, arg);
+ // Tell the scheduler about the problem.
+ emit slaveDied(this);
+ // After the above signal we're dead!!
+ deref();
+}
+
+Slave::Slave(TDEServerSocket *socket, const TQString &protocol, const TQString &socketname)
+ : SlaveInterface(&slaveconn), serv(socket), contacted(false),
+ d(new SlavePrivate(false))
+{
+ m_refCount = 1;
+ m_protocol = protocol;
+ m_slaveProtocol = protocol;
+ m_socket = socketname;
+ dead = false;
+ contact_started = time(0);
+ idle_since = contact_started;
+ m_pid = 0;
+ m_port = 0;
+#ifndef Q_WS_WIN
+ connect(serv, TQT_SIGNAL(accepted( TDESocket* )),
+ TQT_SLOT(accept(TDESocket*) ) );
+#endif
+}
+
+Slave::Slave(bool /*derived*/, TDEServerSocket *socket, const TQString &protocol,
+ const TQString &socketname)
+ : SlaveInterface(&slaveconn), serv(socket), contacted(false),
+ d(new SlavePrivate(true))
+{
+ // FIXME: hmm, duplicating code here from public ctor, no good (LS)
+ m_refCount = 1;
+ m_protocol = protocol;
+ m_slaveProtocol = protocol;
+ m_socket = socketname;
+ dead = false;
+ contact_started = time(0);
+ idle_since = contact_started;
+ m_pid = 0;
+ m_port = 0;
+ if (serv != 0) {
+#ifndef Q_WS_WIN
+ connect(serv, TQT_SIGNAL(accepted( TDESocket* )),
+ TQT_SLOT(accept(TDESocket*) ) );
+#endif
+ }
+}
+
+Slave::~Slave()
+{
+ // kdDebug(7002) << "destructing slave object pid = " << m_pid << endl;
+ if (serv != 0) {
+ delete serv;
+ serv = 0;
+ }
+ unlinkSocket();
+ m_pid = 99999;
+ delete d;
+ d = 0;
+}
+
+void Slave::setProtocol(const TQString & protocol)
+{
+ m_protocol = protocol;
+}
+
+void Slave::setIdle()
+{
+ idle_since = time(0);
+}
+
+time_t Slave::idleTime()
+{
+ return (time_t) difftime(time(0), idle_since);
+}
+
+void Slave::setPID(pid_t pid)
+{
+ m_pid = pid;
+}
+
+void Slave::hold(const KURL &url)
+{
+ if (d->derived) { // TODO: clean up before KDE 4
+ HoldParams params;
+ params.url = &url;
+ virtual_hook(VIRTUAL_HOLD, &params);
+ return;
+ }/*end if*/
+
+ ref();
+ {
+ TQByteArray data;
+ TQDataStream stream( data, IO_WriteOnly );
+ stream << url;
+ slaveconn.send( CMD_SLAVE_HOLD, data );
+ slaveconn.close();
+ dead = true;
+ emit slaveDied(this);
+ }
+ deref();
+ // Call KLauncher::waitForSlave(pid);
+ {
+ DCOPClient *client = kapp->dcopClient();
+ if (!client->isAttached())
+ client->attach();
+
+ TQByteArray params, reply;
+ TQCString replyType;
+ TQDataStream stream(params, IO_WriteOnly);
+ pid_t pid = m_pid;
+ stream << pid;
+
+ TQCString launcher = TDEApplication::launcher();
+ client->call(launcher, launcher, "waitForSlave(pid_t)",
+ params, replyType, reply);
+ }
+}
+
+void Slave::suspend()
+{
+ if (d->derived) { // TODO: clean up before KDE 4
+ virtual_hook(VIRTUAL_SUSPEND, 0);
+ return;
+ }/*end if*/
+
+ slaveconn.suspend();
+}
+
+void Slave::resume()
+{
+ if (d->derived) { // TODO: clean up before KDE 4
+ virtual_hook(VIRTUAL_RESUME, 0);
+ return;
+ }/*end if*/
+
+ slaveconn.resume();
+}
+
+bool Slave::suspended()
+{
+ if (d->derived) { // TODO: clean up before KDE 4
+ SuspendedParams params;
+ virtual_hook(VIRTUAL_SUSPENDED, &params);
+ return params.retval;
+ }/*end if*/
+
+ return slaveconn.suspended();
+}
+
+void Slave::send(int cmd, const TQByteArray &arr) {
+ if (d->derived) { // TODO: clean up before KDE 4
+ SendParams params;
+ params.cmd = cmd;
+ params.arr = &arr;
+ virtual_hook(VIRTUAL_SEND, &params);
+ return;
+ }/*end if*/
+
+ slaveconn.send(cmd, arr);
+}
+
+void Slave::gotInput()
+{
+ ref();
+ if (!dispatch())
+ {
+ slaveconn.close();
+ dead = true;
+ TQString arg = m_protocol;
+ if (!m_host.isEmpty())
+ arg += "://"+m_host;
+ kdDebug(7002) << "slave died pid = " << m_pid << endl;
+ // Tell the job about the problem.
+ emit error(ERR_SLAVE_DIED, arg);
+ // Tell the scheduler about the problem.
+ emit slaveDied(this);
+ }
+ deref();
+ // Here we might be dead!!
+}
+
+void Slave::kill()
+{
+ dead = true; // OO can be such simple.
+ kdDebug(7002) << "killing slave pid=" << m_pid << " (" << m_protocol << "://"
+ << m_host << ")" << endl;
+ if (m_pid)
+ {
+ ::kill(m_pid, SIGTERM);
+ }
+}
+
+void Slave::setHost( const TQString &host, int port,
+ const TQString &user, const TQString &passwd)
+{
+ m_host = host;
+ m_port = port;
+ m_user = user;
+ m_passwd = passwd;
+
+ TQByteArray data;
+ TQDataStream stream( data, IO_WriteOnly );
+ stream << m_host << m_port << m_user << m_passwd;
+ slaveconn.send( CMD_HOST, data );
+}
+
+void Slave::resetHost()
+{
+ m_host = "<reset>";
+}
+
+void Slave::setConfig(const MetaData &config)
+{
+ TQByteArray data;
+ TQDataStream stream( data, IO_WriteOnly );
+ stream << config;
+ slaveconn.send( CMD_CONFIG, data );
+}
+
+Slave* Slave::createSlave( const TQString &protocol, const KURL& url, int& error, TQString& error_text )
+{
+ //kdDebug(7002) << "createSlave '" << protocol << "' for " << url.prettyURL() << endl;
+ // Firstly take into account all special slaves
+ if (protocol == "data")
+ return new DataProtocol();
+
+ DCOPClient *client = kapp->dcopClient();
+ if (!client->isAttached())
+ client->attach();
+
+ TQString prefix = locateLocal("socket", TDEGlobal::instance()->instanceName());
+ KTempFile socketfile(prefix, TQString::fromLatin1(".slave-socket"));
+ if ( socketfile.status() != 0 )
+ {
+ error_text = i18n("Unable to create io-slave: %1").arg(strerror(errno));
+ error = TDEIO::ERR_CANNOT_LAUNCH_PROCESS;
+ return 0;
+ }
+
+#ifdef __CYGWIN__
+ socketfile.close();
+#endif
+
+#ifndef Q_WS_WIN
+ TDEServerSocket *kss = new TDEServerSocket(TQFile::encodeName(socketfile.name()).data());
+
+ Slave *slave = new Slave(kss, protocol, socketfile.name());
+#else
+ Slave *slave = 0;
+#endif
+
+ // WABA: if the dcopserver is running under another uid we don't ask
+ // tdelauncher for a slave, because the slave might have that other uid
+ // as well, which might either be a) undesired or b) make it impossible
+ // for the slave to connect to the application.
+ // In such case we start the slave via TDEProcess.
+ // It's possible to force this by setting the env. variable
+ // TDE_FORK_SLAVES, Clearcase seems to require this.
+ static bool bForkSlaves = !TQCString(getenv("TDE_FORK_SLAVES")).isEmpty();
+
+ if (bForkSlaves || !client->isAttached() || client->isAttachedToForeignServer())
+ {
+ TQString _name = KProtocolInfo::exec(protocol);
+ if (_name.isEmpty())
+ {
+ error_text = i18n("Unknown protocol '%1'.").arg(protocol);
+ error = TDEIO::ERR_CANNOT_LAUNCH_PROCESS;
+ delete slave;
+ return 0;
+ }
+ TQString lib_path = KLibLoader::findLibrary(_name.latin1());
+ if (lib_path.isEmpty())
+ {
+ error_text = i18n("Can not find io-slave for protocol '%1'.").arg(protocol);
+ error = TDEIO::ERR_CANNOT_LAUNCH_PROCESS;
+ return 0;
+ }
+
+ TDEProcess proc;
+
+ proc << locate("exe", "tdeioslave") << lib_path << protocol << "" << socketfile.name();
+ kdDebug(7002) << "tdeioslave" << ", " << lib_path << ", " << protocol << ", " << TQString::null << ", " << socketfile.name() << endl;
+
+ proc.start(TDEProcess::DontCare);
+
+#ifndef Q_WS_WIN
+ slave->setPID(proc.pid());
+ TQTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, TQT_SLOT(timeout()));
+#endif
+ return slave;
+ }
+
+
+ TQByteArray params, reply;
+ TQCString replyType;
+ TQDataStream stream(params, IO_WriteOnly);
+ stream << protocol << url.host() << socketfile.name();
+
+ TQCString launcher = TDEApplication::launcher();
+ if (!client->call(launcher, launcher, "requestSlave(TQString,TQString,TQString)",
+ params, replyType, reply)) {
+ error_text = i18n("Cannot talk to tdelauncher");
+ error = TDEIO::ERR_SLAVE_DEFINED;
+ delete slave;
+ return 0;
+ }
+ TQDataStream stream2(reply, IO_ReadOnly);
+ TQString errorStr;
+ pid_t pid;
+ stream2 >> pid >> errorStr;
+ if (!pid)
+ {
+ error_text = i18n("Unable to create io-slave:\ntdelauncher said: %1").arg(errorStr);
+ error = TDEIO::ERR_CANNOT_LAUNCH_PROCESS;
+ delete slave;
+ return 0;
+ }
+#ifndef Q_WS_WIN
+ slave->setPID(pid);
+ TQTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, TQT_SLOT(timeout()));
+#endif
+ return slave;
+}
+
+Slave* Slave::holdSlave( const TQString &protocol, const KURL& url )
+{
+ //kdDebug(7002) << "holdSlave '" << protocol << "' for " << url.prettyURL() << endl;
+ // Firstly take into account all special slaves
+ if (protocol == "data")
+ return 0;
+
+ DCOPClient *client = kapp->dcopClient();
+ if (!client->isAttached())
+ client->attach();
+
+ TQString prefix = locateLocal("socket", TDEGlobal::instance()->instanceName());
+ KTempFile socketfile(prefix, TQString::fromLatin1(".slave-socket"));
+ if ( socketfile.status() != 0 )
+ return 0;
+
+#ifdef __CYGWIN__
+ socketfile.close();
+ socketfile.unlink();
+#endif
+
+#ifndef Q_WS_WIN
+ TDEServerSocket *kss = new TDEServerSocket(TQFile::encodeName(socketfile.name()).data());
+
+ Slave *slave = new Slave(kss, protocol, socketfile.name());
+#else
+ Slave *slave = 0;
+#endif
+
+ TQByteArray params, reply;
+ TQCString replyType;
+ TQDataStream stream(params, IO_WriteOnly);
+ stream << url << socketfile.name();
+
+ TQCString launcher = TDEApplication::launcher();
+ if (!client->call(launcher, launcher, "requestHoldSlave(KURL,TQString)",
+ params, replyType, reply)) {
+ delete slave;
+ return 0;
+ }
+ TQDataStream stream2(reply, IO_ReadOnly);
+ pid_t pid;
+ stream2 >> pid;
+ if (!pid)
+ {
+ delete slave;
+ return 0;
+ }
+#ifndef Q_WS_WIN
+ slave->setPID(pid);
+ TQTimer::singleShot(1000*SLAVE_CONNECTION_TIMEOUT_MIN, slave, TQT_SLOT(timeout()));
+#endif
+ return slave;
+}
+
+void Slave::virtual_hook( int id, void* data ) {
+ TDEIO::SlaveInterface::virtual_hook( id, data );
+}
+
+#include "slave.moc"
diff --git a/tdeio/tdeio/slave.h b/tdeio/tdeio/slave.h
new file mode 100644
index 000000000..80ae30d3a
--- /dev/null
+++ b/tdeio/tdeio/slave.h
@@ -0,0 +1,270 @@
+// -*- c++ -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (c) 2000 Waldo Bastian <bastian@kde.org>
+ * 2000 Stephan Kulow <coolo@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#ifndef KIO_SLAVE_H
+#define KIO_SLAVE_H
+
+#include <time.h>
+#include <unistd.h>
+
+#include <tqobject.h>
+
+#include <kurl.h>
+
+#include "tdeio/slaveinterface.h"
+#include "tdeio/connection.h"
+
+class TDEServerSocket;
+class TDESocket;
+
+namespace TDEIO {
+
+ /** Attention developers: If you change the implementation of TDEIO::Slave,
+ * do *not* use connection() or slaveconn but the respective TDEIO::Slave
+ * accessor methods. Otherwise classes derived from Slave might break. (LS)
+ */
+ class TDEIO_EXPORT Slave : public TDEIO::SlaveInterface
+ {
+ Q_OBJECT
+
+
+ protected:
+ /**
+ * Use this constructor if you derive your own class from Slave
+ * @p derived must be true in any case
+ * @internal
+ * @since 3.2
+ */
+ Slave(bool derived, TDEServerSocket *unixdomain, const TQString &protocol,
+ const TQString &socketname); // TODO(BIC): Remove in KDE 4
+
+ public:
+ Slave(TDEServerSocket *unixdomain,
+ const TQString &protocol, const TQString &socketname);
+
+ virtual ~Slave();
+
+ void setPID(pid_t);
+
+ int slave_pid() { return m_pid; }
+
+ /**
+ * Force termination
+ */
+ void kill();
+
+ /**
+ * @return true if the slave survived the last mission.
+ */
+ bool isAlive() { return !dead; }
+
+ /**
+ * Set host for url
+ * @param host to connect to.
+ * @param port to connect to.
+ * @param user to login as
+ * @param passwd to login with
+ */
+ void setHost( const TQString &host, int port,
+ const TQString &user, const TQString &passwd); // TODO(BIC): make virtual
+
+ /**
+ * Clear host info.
+ */
+ void resetHost();
+
+ /**
+ * Configure slave
+ */
+ void setConfig(const MetaData &config); // TODO(BIC): make virtual
+
+ /**
+ * The protocol this slave handles.
+ *
+ * @return name of protocol handled by this slave, as seen by the user
+ */
+ TQString protocol() { return m_protocol; }
+
+ void setProtocol(const TQString & protocol);
+ /**
+ * The actual protocol used to handle the request.
+ *
+ * This method will return a different protocol than
+ * the one obtained by using protocol() if a
+ * proxy-server is used for the given protocol. This
+ * usually means that this method will return "http"
+ * when the actuall request was to retrieve a resource
+ * from an "ftp" server by going through a proxy server.
+ *
+ * @return the actual protocol (io-slave) that handled the request
+ */
+ TQString slaveProtocol() { return m_slaveProtocol; }
+
+ /**
+ * @return Host this slave is (was?) connected to
+ */
+ TQString host() { return m_host; }
+
+ /**
+ * @return port this slave is (was?) connected to
+ */
+ int port() { return m_port; }
+
+ /**
+ * @return User this slave is (was?) logged in as
+ */
+ TQString user() { return m_user; }
+
+ /**
+ * @return Passwd used to log in
+ */
+ TQString passwd() { return m_passwd; }
+
+ /**
+ * Creates a new slave.
+ *
+ * @param protocol protocol the slave is for.
+ * @param url URL the slave should operate on.
+ * @param error is the error code on failure and undefined else.
+ * @param error_text is the error text on failure and undefined else.
+ *
+ * @return 0 on failure, or a pointer to a slave otherwise.
+ * @todo What are legal @p protocol values?
+ */
+ static Slave* createSlave( const TQString &protocol, const KURL& url, int& error, TQString& error_text );
+
+ static Slave* holdSlave( const TQString &protocol, const KURL& url );
+
+ // == communication with connected tdeioslave ==
+ // whenever possible prefer these methods over the respective
+ // methods in connection()
+ /**
+ * Suspends the operation of the attached tdeioslave.
+ */
+ void suspend(); // TODO(BIC): make virtual
+ /**
+ * Resumes the operation of the attached tdeioslave.
+ */
+ void resume(); // TODO(BIC): make virtual
+ /**
+ * Tells wether the tdeioslave is suspended.
+ * @return true if the tdeioslave is suspended.
+ * @since 3.2
+ */
+ bool suspended(); // TODO(BIC): make virtual
+ /**
+ * Sends the given command to the tdeioslave.
+ * @param cmd command id
+ * @param data byte array containing data
+ * @since 3.2
+ */
+ void send(int cmd, const TQByteArray &data = TQByteArray());// TODO(BIC): make virtual
+ // == end communication with connected tdeioslave ==
+
+ /**
+ * Puts the tdeioslave associated with @p url at halt.
+ */
+ void hold(const KURL &url); // TODO(BIC): make virtual
+
+ /**
+ * @return The time this slave has been idle.
+ */
+ time_t idleTime();
+
+ /**
+ * Marks this slave as idle.
+ */
+ void setIdle();
+
+ /*
+ * @returns Whether the slave is connected
+ * (Connection oriented slaves only)
+ */
+ bool isConnected() { return contacted; }
+ void setConnected(bool c) { contacted = c; }
+
+ /** @deprecated This method is obsolete, use the accessor methods
+ * within TDEIO::Slave instead. Old code directly accessing connection()
+ * will not be able to access special protocols.
+ */
+ KDE_DEPRECATED Connection *connection() { return &slaveconn; } // TODO(BIC): remove before KDE 4
+
+ void ref() { m_refCount++; }
+ void deref() { m_refCount--; if (!m_refCount) delete this; }
+
+ public slots:
+ void accept(TDESocket *socket);
+ void gotInput();
+ void timeout();
+ signals:
+ void slaveDied(TDEIO::Slave *slave);
+
+ protected:
+ void unlinkSocket();
+
+ private:
+ TQString m_protocol;
+ TQString m_slaveProtocol;
+ TQString m_host;
+ int m_port;
+ TQString m_user;
+ TQString m_passwd;
+ TDEServerSocket *serv;
+ TQString m_socket;
+ pid_t m_pid;
+ bool contacted;
+ bool dead;
+ time_t contact_started;
+ time_t idle_since;
+ TDEIO::Connection slaveconn;
+ int m_refCount;
+ protected:
+ virtual void virtual_hook( int id, void* data );
+ // grant SlaveInterface all IDs < 0x200
+ enum { VIRTUAL_SUSPEND = 0x200, VIRTUAL_RESUME, VIRTUAL_SEND,
+ VIRTUAL_HOLD, VIRTUAL_SUSPENDED,
+ VIRTUAL_SET_HOST, VIRTUAL_SET_CONFIG };
+ struct SendParams {
+ int cmd;
+ const TQByteArray *arr;
+ };
+ struct HoldParams {
+ const KURL *url;
+ };
+ struct SuspendedParams {
+ bool retval;
+ };
+ struct SetHostParams {
+ const TQString *host;
+ int port;
+ const TQString *user;
+ const TQString *passwd;
+ };
+ struct SetConfigParams {
+ const MetaData *config;
+ };
+ private:
+ class SlavePrivate* d;
+ };
+
+}
+
+#endif
diff --git a/tdeio/tdeio/slavebase.cpp b/tdeio/tdeio/slavebase.cpp
new file mode 100644
index 000000000..e979f530b
--- /dev/null
+++ b/tdeio/tdeio/slavebase.cpp
@@ -0,0 +1,1315 @@
+/*
+ *
+ * This file is part of the KDE libraries
+ * Copyright (c) 2000 Waldo Bastian <bastian@kde.org>
+ * Copyright (c) 2000 David Faure <faure@kde.org>
+ * Copyright (c) 2000 Stephan Kulow <coolo@kde.org>
+ *
+ * $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ **/
+
+#include "slavebase.h"
+
+#include <config.h>
+
+#include <sys/time.h>
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h> // Needed on some systems.
+#endif
+
+#include <assert.h>
+#include <kdebug.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#include <time.h>
+
+#include <tqfile.h>
+
+#include <dcopclient.h>
+
+#include <kapplication.h>
+#include <ksock.h>
+#include <kcrash.h>
+#include <tdesu/client.h>
+#include <klocale.h>
+#include <ksocks.h>
+
+#include "kremoteencoding.h"
+
+#include "tdeio/slavebase.h"
+#include "tdeio/connection.h"
+#include "tdeio/ioslave_defaults.h"
+#include "tdeio/slaveinterface.h"
+
+#include "uiserver_stub.h"
+
+#ifndef NDEBUG
+#ifdef HAVE_BACKTRACE
+#include <execinfo.h>
+#endif
+#endif
+
+using namespace TDEIO;
+
+template class TQPtrList<TQValueList<UDSAtom> >;
+typedef TQValueList<TQCString> AuthKeysList;
+typedef TQMap<TQString,TQCString> AuthKeysMap;
+#define KIO_DATA TQByteArray data; TQDataStream stream( data, IO_WriteOnly ); stream
+#define KIO_FILESIZE_T(x) (unsigned long)(x & 0xffffffff) << (unsigned long)(x >> 32)
+
+namespace TDEIO {
+
+class SlaveBaseConfig : public TDEConfigBase
+{
+public:
+ SlaveBaseConfig(SlaveBase *_slave)
+ : slave(_slave) { }
+
+ bool internalHasGroup(const TQCString &) const { tqWarning("hasGroup(const TQCString &)");
+return false; }
+
+ TQStringList groupList() const { return TQStringList(); }
+
+ TQMap<TQString,TQString> entryMap(const TQString &group) const
+ { Q_UNUSED(group); return TQMap<TQString,TQString>(); }
+
+ void reparseConfiguration() { }
+
+ KEntryMap internalEntryMap( const TQString &pGroup) const { Q_UNUSED(pGroup); return KEntryMap(); }
+
+ KEntryMap internalEntryMap() const { return KEntryMap(); }
+
+ void putData(const KEntryKey &_key, const KEntry&_data, bool _checkGroup)
+ { Q_UNUSED(_key); Q_UNUSED(_data); Q_UNUSED(_checkGroup); }
+
+ KEntry lookupData(const KEntryKey &_key) const
+ {
+ KEntry entry;
+ TQString value = slave->metaData(_key.c_key);
+ if (!value.isNull())
+ entry.mValue = value.utf8();
+ return entry;
+ }
+protected:
+ SlaveBase *slave;
+};
+
+
+class SlaveBasePrivate {
+public:
+ TQString slaveid;
+ bool resume:1;
+ bool needSendCanResume:1;
+ bool onHold:1;
+ bool wasKilled:1;
+ MetaData configData;
+ SlaveBaseConfig *config;
+ KURL onHoldUrl;
+
+ struct timeval last_tv;
+ TDEIO::filesize_t totalSize;
+ TDEIO::filesize_t sentListEntries;
+ DCOPClient *dcopClient;
+ KRemoteEncoding *remotefile;
+ time_t timeout;
+ TQByteArray timeoutData;
+};
+
+}
+
+static SlaveBase *globalSlave;
+long SlaveBase::s_seqNr;
+
+static volatile bool slaveWriteError = false;
+
+static const char *s_protocol;
+
+#ifdef Q_OS_UNIX
+static void genericsig_handler(int sigNumber)
+{
+ signal(sigNumber,SIG_IGN);
+ //WABA: Don't do anything that requires malloc, we can deadlock on it since
+ //a SIGTERM signal can come in while we are in malloc/free.
+ //kdDebug()<<"tdeioslave : exiting due to signal "<<sigNumber<<endl;
+ //set the flag which will be checked in dispatchLoop() and which *should* be checked
+ //in lengthy operations in the various slaves
+ if (globalSlave!=0)
+ globalSlave->setKillFlag();
+ signal(SIGALRM,SIG_DFL);
+ alarm(5); //generate an alarm signal in 5 seconds, in this time the slave has to exit
+}
+#endif
+
+//////////////
+
+SlaveBase::SlaveBase( const TQCString &protocol,
+ const TQCString &pool_socket,
+ const TQCString &app_socket )
+ : mProtocol(protocol), m_pConnection(0),
+ mPoolSocket( TQFile::decodeName(pool_socket)),
+ mAppSocket( TQFile::decodeName(app_socket))
+{
+ s_protocol = protocol.data();
+#ifdef Q_OS_UNIX
+ if (!getenv("TDE_DEBUG"))
+ {
+ KCrash::setCrashHandler( sigsegv_handler );
+ signal(SIGILL,&sigsegv_handler);
+ signal(SIGTRAP,&sigsegv_handler);
+ signal(SIGABRT,&sigsegv_handler);
+ signal(SIGBUS,&sigsegv_handler);
+ signal(SIGALRM,&sigsegv_handler);
+ signal(SIGFPE,&sigsegv_handler);
+#ifdef SIGPOLL
+ signal(SIGPOLL, &sigsegv_handler);
+#endif
+#ifdef SIGSYS
+ signal(SIGSYS, &sigsegv_handler);
+#endif
+#ifdef SIGVTALRM
+ signal(SIGVTALRM, &sigsegv_handler);
+#endif
+#ifdef SIGXCPU
+ signal(SIGXCPU, &sigsegv_handler);
+#endif
+#ifdef SIGXFSZ
+ signal(SIGXFSZ, &sigsegv_handler);
+#endif
+ }
+
+ struct sigaction act;
+ act.sa_handler = sigpipe_handler;
+ sigemptyset( &act.sa_mask );
+ act.sa_flags = 0;
+ sigaction( SIGPIPE, &act, 0 );
+
+ signal(SIGINT,&genericsig_handler);
+ signal(SIGQUIT,&genericsig_handler);
+ signal(SIGTERM,&genericsig_handler);
+#endif
+
+ globalSlave=this;
+
+ appconn = new Connection();
+ listEntryCurrentSize = 100;
+ struct timeval tp;
+ gettimeofday(&tp, 0);
+ listEntry_sec = tp.tv_sec;
+ listEntry_usec = tp.tv_usec;
+ mConnectedToApp = true;
+
+ d = new SlaveBasePrivate;
+ // by kahl for netmgr (need a way to identify slaves)
+ d->slaveid = protocol;
+ d->slaveid += TQString::number(getpid());
+ d->resume = false;
+ d->needSendCanResume = false;
+ d->config = new SlaveBaseConfig(this);
+ d->onHold = false;
+ d->wasKilled=false;
+ d->last_tv.tv_sec = 0;
+ d->last_tv.tv_usec = 0;
+// d->processed_size = 0;
+ d->totalSize=0;
+ d->sentListEntries=0;
+ d->timeout = 0;
+ connectSlave(mAppSocket);
+
+ d->dcopClient = 0;
+ d->remotefile = 0;
+}
+
+SlaveBase::~SlaveBase()
+{
+ delete d;
+ s_protocol = "";
+}
+
+DCOPClient *SlaveBase::dcopClient()
+{
+ if (!d->dcopClient)
+ {
+ d->dcopClient = TDEApplication::dcopClient();
+ if (!d->dcopClient->isAttached())
+ d->dcopClient->attach();
+ d->dcopClient->setDaemonMode( true );
+ }
+ return d->dcopClient;
+}
+
+void SlaveBase::dispatchLoop()
+{
+#ifdef Q_OS_UNIX //TODO: WIN32
+ fd_set rfds;
+ int retval;
+
+ while (true)
+ {
+ if (d->timeout && (d->timeout < time(0)))
+ {
+ TQByteArray data = d->timeoutData;
+ d->timeout = 0;
+ d->timeoutData = TQByteArray();
+ special(data);
+ }
+ FD_ZERO(&rfds);
+
+ assert(appconn->inited());
+ int maxfd = appconn->fd_from();
+ FD_SET(appconn->fd_from(), &rfds);
+ if( d->dcopClient )
+ {
+ FD_SET( d->dcopClient->socket(), &rfds );
+ if( d->dcopClient->socket() > maxfd )
+ maxfd = d->dcopClient->socket();
+ }
+
+ if (!d->timeout) // we can wait forever
+ {
+ retval = select( maxfd + 1, &rfds, NULL, NULL, NULL);
+ }
+ else
+ {
+ struct timeval tv;
+ tv.tv_sec = kMax(d->timeout-time(0),(time_t) 1);
+ tv.tv_usec = 0;
+ retval = select( maxfd + 1, &rfds, NULL, NULL, &tv);
+ }
+ if ((retval>0) && FD_ISSET(appconn->fd_from(), &rfds))
+ { // dispatch application messages
+ int cmd;
+ TQByteArray data;
+ if ( appconn->read(&cmd, data) != -1 )
+ {
+ dispatch(cmd, data);
+ }
+ else // some error occurred, perhaps no more application
+ {
+ // When the app exits, should the slave be put back in the pool ?
+ if (mConnectedToApp && !mPoolSocket.isEmpty())
+ {
+ disconnectSlave();
+ mConnectedToApp = false;
+ closeConnection();
+ connectSlave(mPoolSocket);
+ }
+ else
+ {
+ return;
+ }
+ }
+ }
+ if( retval > 0 && d->dcopClient && FD_ISSET( d->dcopClient->socket(), &rfds ))
+ {
+ d->dcopClient->processSocketData( d->dcopClient->socket());
+ }
+ if ((retval<0) && (errno != EINTR))
+ {
+ kdDebug(7019) << "dispatchLoop(): select returned " << retval << " "
+ << (errno==EBADF?"EBADF":errno==EINTR?"EINTR":errno==EINVAL?"EINVAL":errno==ENOMEM?"ENOMEM":"unknown")
+ << " (" << errno << ")" << endl;
+ return;
+ }
+ //I think we get here when we were killed in dispatch() and not in select()
+ if (wasKilled())
+ {
+ kdDebug(7019)<<" dispatchLoop() slave was killed, returning"<<endl;
+ return;
+ }
+ }
+#else
+#error The KIO slave system only works under UNIX
+#endif
+}
+
+void SlaveBase::connectSlave(const TQString& path)
+{
+#ifdef Q_OS_UNIX //TODO: TDESocket not yet available on WIN32
+ appconn->init(new TDESocket(TQFile::encodeName(path).data()));
+ if (!appconn->inited())
+ {
+ kdDebug(7019) << "SlaveBase: failed to connect to " << path << endl;
+ exit();
+ }
+
+ setConnection(appconn);
+#endif
+}
+
+void SlaveBase::disconnectSlave()
+{
+ appconn->close();
+}
+
+void SlaveBase::setMetaData(const TQString &key, const TQString &value)
+{
+ mOutgoingMetaData.replace(key, value);
+}
+
+TQString SlaveBase::metaData(const TQString &key) const
+{
+ if (mIncomingMetaData.contains(key))
+ return mIncomingMetaData[key];
+ if (d->configData.contains(key))
+ return d->configData[key];
+ return TQString::null;
+}
+
+bool SlaveBase::hasMetaData(const TQString &key) const
+{
+ if (mIncomingMetaData.contains(key))
+ return true;
+ if (d->configData.contains(key))
+ return true;
+ return false;
+}
+
+// ### remove the next two methods for KDE4 (they miss the const)
+TQString SlaveBase::metaData(const TQString &key) {
+ return const_cast<const SlaveBase*>(this)->metaData( key );
+}
+bool SlaveBase::hasMetaData(const TQString &key) {
+ return const_cast<const SlaveBase*>(this)->hasMetaData( key );
+}
+
+TDEConfigBase *SlaveBase::config()
+{
+ return d->config;
+}
+
+void SlaveBase::sendMetaData()
+{
+ KIO_DATA << mOutgoingMetaData;
+
+ slaveWriteError = false;
+ m_pConnection->send( INF_META_DATA, data );
+ if (slaveWriteError) exit();
+ mOutgoingMetaData.clear(); // Clear
+}
+
+KRemoteEncoding *SlaveBase::remoteEncoding()
+{
+ if (d->remotefile != 0)
+ return d->remotefile;
+
+ return d->remotefile = new KRemoteEncoding(metaData("Charset").latin1());
+}
+
+void SlaveBase::data( const TQByteArray &data )
+{
+ if (!mOutgoingMetaData.isEmpty())
+ sendMetaData();
+ slaveWriteError = false;
+ m_pConnection->send( MSG_DATA, data );
+ if (slaveWriteError) exit();
+}
+
+void SlaveBase::dataReq( )
+{
+/*
+ if (!mOutgoingMetaData.isEmpty())
+ sendMetaData();
+*/
+ if (d->needSendCanResume)
+ canResume(0);
+ m_pConnection->send( MSG_DATA_REQ );
+}
+
+void SlaveBase::error( int _errid, const TQString &_text )
+{
+ mIncomingMetaData.clear(); // Clear meta data
+ mOutgoingMetaData.clear();
+ KIO_DATA << (TQ_INT32) _errid << _text;
+
+ m_pConnection->send( MSG_ERROR, data );
+ //reset
+ listEntryCurrentSize = 100;
+ d->sentListEntries=0;
+ d->totalSize=0;
+}
+
+void SlaveBase::connected()
+{
+ slaveWriteError = false;
+ m_pConnection->send( MSG_CONNECTED );
+ if (slaveWriteError) exit();
+}
+
+void SlaveBase::finished()
+{
+ mIncomingMetaData.clear(); // Clear meta data
+ if (!mOutgoingMetaData.isEmpty())
+ sendMetaData();
+ m_pConnection->send( MSG_FINISHED );
+
+ // reset
+ listEntryCurrentSize = 100;
+ d->sentListEntries=0;
+ d->totalSize=0;
+}
+
+void SlaveBase::needSubURLData()
+{
+ m_pConnection->send( MSG_NEED_SUBURL_DATA );
+}
+
+void SlaveBase::slaveStatus( const TQString &host, bool connected )
+{
+ pid_t pid = getpid();
+ TQ_INT8 b = connected ? 1 : 0;
+ KIO_DATA << pid << mProtocol << host << b;
+ if (d->onHold)
+ stream << d->onHoldUrl;
+ m_pConnection->send( MSG_SLAVE_STATUS, data );
+}
+
+void SlaveBase::canResume()
+{
+ m_pConnection->send( MSG_CANRESUME );
+}
+
+void SlaveBase::totalSize( TDEIO::filesize_t _bytes )
+{
+ KIO_DATA << KIO_FILESIZE_T(_bytes);
+ slaveWriteError = false;
+ m_pConnection->send( INF_TOTAL_SIZE, data );
+ if (slaveWriteError) exit();
+
+ //this one is usually called before the first item is listed in listDir()
+ struct timeval tp;
+ gettimeofday(&tp, 0);
+ listEntry_sec = tp.tv_sec;
+ listEntry_usec = tp.tv_usec;
+ d->totalSize=_bytes;
+ d->sentListEntries=0;
+}
+
+void SlaveBase::processedSize( TDEIO::filesize_t _bytes )
+{
+ bool emitSignal=false;
+ struct timeval tv;
+ int gettimeofday_res=gettimeofday( &tv, 0L );
+
+ if( _bytes == d->totalSize )
+ emitSignal=true;
+ else if ( gettimeofday_res == 0 ) {
+ time_t msecdiff = 2000;
+ if (d->last_tv.tv_sec) {
+ // Compute difference, in ms
+ msecdiff = 1000 * ( tv.tv_sec - d->last_tv.tv_sec );
+ time_t usecdiff = tv.tv_usec - d->last_tv.tv_usec;
+ if ( usecdiff < 0 ) {
+ msecdiff--;
+ msecdiff += 1000;
+ }
+ msecdiff += usecdiff / 1000;
+ }
+ emitSignal=msecdiff >= 100; // emit size 10 times a second
+ }
+
+ if( emitSignal ) {
+ KIO_DATA << KIO_FILESIZE_T(_bytes);
+ slaveWriteError = false;
+ m_pConnection->send( INF_PROCESSED_SIZE, data );
+ if (slaveWriteError) exit();
+ if ( gettimeofday_res == 0 ) {
+ d->last_tv.tv_sec = tv.tv_sec;
+ d->last_tv.tv_usec = tv.tv_usec;
+ }
+ }
+// d->processed_size = _bytes;
+}
+
+void SlaveBase::processedPercent( float /* percent */ )
+{
+ kdDebug(7019) << "SlaveBase::processedPercent: STUB" << endl;
+}
+
+
+void SlaveBase::speed( unsigned long _bytes_per_second )
+{
+ KIO_DATA << (TQ_UINT32) _bytes_per_second;
+ slaveWriteError = false;
+ m_pConnection->send( INF_SPEED, data );
+ if (slaveWriteError) exit();
+}
+
+void SlaveBase::redirection( const KURL& _url )
+{
+ KIO_DATA << _url;
+ m_pConnection->send( INF_REDIRECTION, data );
+}
+
+void SlaveBase::errorPage()
+{
+ m_pConnection->send( INF_ERROR_PAGE );
+}
+
+static bool isSubCommand(int cmd)
+{
+ return ( (cmd == CMD_REPARSECONFIGURATION) ||
+ (cmd == CMD_META_DATA) ||
+ (cmd == CMD_CONFIG) ||
+ (cmd == CMD_SUBURL) ||
+ (cmd == CMD_SLAVE_STATUS) ||
+ (cmd == CMD_SLAVE_CONNECT) ||
+ (cmd == CMD_SLAVE_HOLD) ||
+ (cmd == CMD_MULTI_GET));
+}
+
+void SlaveBase::mimeType( const TQString &_type)
+{
+ // kdDebug(7019) << "(" << getpid() << ") SlaveBase::mimeType '" << _type << "'" << endl;
+ int cmd;
+ do
+ {
+ // Send the meta-data each time we send the mime-type.
+ if (!mOutgoingMetaData.isEmpty())
+ {
+ // kdDebug(7019) << "(" << getpid() << ") mimeType: emitting meta data" << endl;
+ KIO_DATA << mOutgoingMetaData;
+ m_pConnection->send( INF_META_DATA, data );
+ }
+ KIO_DATA << _type;
+ m_pConnection->send( INF_MIME_TYPE, data );
+ while(true)
+ {
+ cmd = 0;
+ if ( m_pConnection->read( &cmd, data ) == -1 ) {
+ kdDebug(7019) << "SlaveBase: mimetype: read error" << endl;
+ exit();
+ }
+ // kdDebug(7019) << "(" << getpid() << ") Slavebase: mimetype got " << cmd << endl;
+ if ( cmd == CMD_HOST) // Ignore.
+ continue;
+ if ( isSubCommand(cmd) )
+ {
+ dispatch( cmd, data );
+ continue; // Disguised goto
+ }
+ break;
+ }
+ }
+ while (cmd != CMD_NONE);
+ mOutgoingMetaData.clear();
+}
+
+void SlaveBase::exit()
+{
+ this->~SlaveBase();
+ ::exit(255);
+}
+
+void SlaveBase::warning( const TQString &_msg)
+{
+ KIO_DATA << _msg;
+ m_pConnection->send( INF_WARNING, data );
+}
+
+void SlaveBase::infoMessage( const TQString &_msg)
+{
+ KIO_DATA << _msg;
+ m_pConnection->send( INF_INFOMESSAGE, data );
+}
+
+bool SlaveBase::requestNetwork(const TQString& host)
+{
+ KIO_DATA << host << d->slaveid;
+ m_pConnection->send( MSG_NET_REQUEST, data );
+
+ if ( waitForAnswer( INF_NETWORK_STATUS, 0, data ) != -1 )
+ {
+ bool status;
+ TQDataStream stream( data, IO_ReadOnly );
+ stream >> status;
+ return status;
+ } else
+ return false;
+}
+
+void SlaveBase::dropNetwork(const TQString& host)
+{
+ KIO_DATA << host << d->slaveid;
+ m_pConnection->send( MSG_NET_DROP, data );
+}
+
+void SlaveBase::statEntry( const UDSEntry& entry )
+{
+ KIO_DATA << entry;
+ slaveWriteError = false;
+ m_pConnection->send( MSG_STAT_ENTRY, data );
+ if (slaveWriteError) exit();
+}
+
+void SlaveBase::listEntry( const UDSEntry& entry, bool _ready )
+{
+ static struct timeval tp;
+ static const int maximum_updatetime = 300;
+ static const int minimum_updatetime = 100;
+
+ if (!_ready) {
+ pendingListEntries.append(entry);
+
+ if (pendingListEntries.count() > listEntryCurrentSize) {
+ gettimeofday(&tp, 0);
+
+ long diff = ((tp.tv_sec - listEntry_sec) * 1000000 +
+ tp.tv_usec - listEntry_usec) / 1000;
+ if (diff==0) diff=1;
+
+ if (diff > maximum_updatetime) {
+ listEntryCurrentSize = listEntryCurrentSize * 3 / 4;
+ _ready = true;
+ }
+//if we can send all list entries of this dir which have not yet been sent
+//within maximum_updatetime, then make listEntryCurrentSize big enough for all of them
+ else if (((pendingListEntries.count()*maximum_updatetime)/diff) > (d->totalSize-d->sentListEntries))
+ listEntryCurrentSize=d->totalSize-d->sentListEntries+1;
+//if we are below minimum_updatetime, estimate how much we will get within
+//maximum_updatetime
+ else if (diff < minimum_updatetime)
+ listEntryCurrentSize = (pendingListEntries.count() * maximum_updatetime) / diff;
+ else
+ _ready=true;
+ }
+ }
+ if (_ready) { // may happen when we started with !ready
+ listEntries( pendingListEntries );
+ pendingListEntries.clear();
+
+ gettimeofday(&tp, 0);
+ listEntry_sec = tp.tv_sec;
+ listEntry_usec = tp.tv_usec;
+ }
+}
+
+void SlaveBase::listEntries( const UDSEntryList& list )
+{
+ KIO_DATA << (TQ_UINT32)list.count();
+ UDSEntryListConstIterator it = list.begin();
+ UDSEntryListConstIterator end = list.end();
+ for (; it != end; ++it)
+ stream << *it;
+ slaveWriteError = false;
+ m_pConnection->send( MSG_LIST_ENTRIES, data);
+ if (slaveWriteError) exit();
+ d->sentListEntries+=(uint)list.count();
+}
+
+void SlaveBase::sendAuthenticationKey( const TQCString& key,
+ const TQCString& group,
+ bool keepPass )
+{
+ KIO_DATA << key << group << keepPass;
+ m_pConnection->send( MSG_AUTH_KEY, data );
+}
+
+void SlaveBase::delCachedAuthentication( const TQString& key )
+{
+ KIO_DATA << key.utf8() ;
+ m_pConnection->send( MSG_DEL_AUTH_KEY, data );
+}
+
+void SlaveBase::sigsegv_handler(int sig)
+{
+#ifdef Q_OS_UNIX
+ signal(sig,SIG_DFL); // Next one kills
+
+ //Kill us if we deadlock
+ signal(SIGALRM,SIG_DFL);
+ alarm(5); //generate an alarm signal in 5 seconds, in this time the slave has to exit
+
+ // Debug and printf should be avoided because they might
+ // call malloc.. and get in a nice recursive malloc loop
+ char buffer[120];
+ snprintf(buffer, sizeof(buffer), "tdeioslave: ####### CRASH ###### protocol = %s pid = %d signal = %d\n", s_protocol, getpid(), sig);
+ write(2, buffer, strlen(buffer));
+#ifndef NDEBUG
+#ifdef HAVE_BACKTRACE
+ void* trace[256];
+ int n = backtrace(trace, 256);
+ if (n)
+ backtrace_symbols_fd(trace, n, 2);
+#endif
+#endif
+ ::exit(1);
+#endif
+}
+
+void SlaveBase::sigpipe_handler (int)
+{
+ // We ignore a SIGPIPE in slaves.
+ // A SIGPIPE can happen in two cases:
+ // 1) Communication error with application.
+ // 2) Communication error with network.
+ slaveWriteError = true;
+
+ // Don't add anything else here, especially no debug output
+}
+
+void SlaveBase::setHost(TQString const &, int, TQString const &, TQString const &)
+{
+}
+
+void SlaveBase::openConnection(void)
+{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CONNECT)); }
+void SlaveBase::closeConnection(void)
+{ } // No response!
+void SlaveBase::stat(KURL const &)
+{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_STAT)); }
+void SlaveBase::put(KURL const &, int, bool, bool)
+{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_PUT)); }
+void SlaveBase::special(const TQByteArray &)
+{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SPECIAL)); }
+void SlaveBase::listDir(KURL const &)
+{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_LISTDIR)); }
+void SlaveBase::get(KURL const & )
+{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_GET)); }
+void SlaveBase::mimetype(KURL const &url)
+{ get(url); }
+void SlaveBase::rename(KURL const &, KURL const &, bool)
+{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_RENAME)); }
+void SlaveBase::symlink(TQString const &, KURL const &, bool)
+{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SYMLINK)); }
+void SlaveBase::copy(KURL const &, KURL const &, int, bool)
+{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_COPY)); }
+void SlaveBase::del(KURL const &, bool)
+{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_DEL)); }
+void SlaveBase::mkdir(KURL const &, int)
+{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_MKDIR)); }
+void SlaveBase::chmod(KURL const &, int)
+{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CHMOD)); }
+void SlaveBase::setSubURL(KURL const &)
+{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SUBURL)); }
+void SlaveBase::multiGet(const TQByteArray &)
+{ error( ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_MULTI_GET)); }
+
+
+void SlaveBase::slave_status()
+{ slaveStatus( TQString::null, false ); }
+
+void SlaveBase::reparseConfiguration()
+{
+}
+
+void SlaveBase::localURL(const KURL& remoteURL)
+{
+ bool local = remoteURL.isLocalFile();
+ TQ_INT8 islocal;
+ KURL retURL;
+ if (local) {
+ islocal = true;
+ retURL = remoteURL;
+ }
+ else {
+ islocal = false;
+ retURL = remoteURL;
+ }
+ KIO_DATA << islocal << retURL;
+ m_pConnection->send( INF_LOCALURL, data );
+}
+
+bool SlaveBase::dispatch()
+{
+ assert( m_pConnection );
+
+ int cmd;
+ TQByteArray data;
+ if ( m_pConnection->read( &cmd, data ) == -1 )
+ {
+ kdDebug(7019) << "SlaveBase::dispatch() has read error." << endl;
+ return false;
+ }
+
+ dispatch( cmd, data );
+ return true;
+}
+
+bool SlaveBase::openPassDlg( AuthInfo& info )
+{
+ return openPassDlg(info, TQString::null);
+}
+
+bool SlaveBase::openPassDlg( AuthInfo& info, const TQString &errorMsg )
+{
+ TQCString replyType;
+ TQByteArray params;
+ TQByteArray reply;
+ AuthInfo authResult;
+ long windowId = metaData("window-id").toLong();
+ long progressId = metaData("progress-id").toLong();
+ unsigned long userTimestamp = metaData("user-timestamp").toULong();
+
+ kdDebug(7019) << "SlaveBase::openPassDlg window-id=" << windowId << " progress-id=" << progressId << endl;
+
+ (void) dcopClient(); // Make sure to have a dcop client.
+
+ UIServer_stub uiserver( "tdeio_uiserver", "UIServer" );
+ if (progressId)
+ uiserver.setJobVisible( progressId, false );
+
+ TQDataStream stream(params, IO_WriteOnly);
+
+ if (metaData("no-auth-prompt").lower() == "true")
+ stream << info << TQString("<NoAuthPrompt>") << windowId << s_seqNr << userTimestamp;
+ else
+ stream << info << errorMsg << windowId << s_seqNr << userTimestamp;
+
+ bool callOK = d->dcopClient->call( "kded", "kpasswdserver", "queryAuthInfo(TDEIO::AuthInfo, TQString, long int, long int, unsigned long int)",
+ params, replyType, reply );
+
+ if (progressId)
+ uiserver.setJobVisible( progressId, true );
+
+ if (!callOK)
+ {
+ kdWarning(7019) << "Can't communicate with kded_kpasswdserver!" << endl;
+ return false;
+ }
+
+ if ( replyType == "TDEIO::AuthInfo" )
+ {
+ TQDataStream stream2( reply, IO_ReadOnly );
+ stream2 >> authResult >> s_seqNr;
+ }
+ else
+ {
+ kdError(7019) << "DCOP function queryAuthInfo(...) returns "
+ << replyType << ", expected TDEIO::AuthInfo" << endl;
+ return false;
+ }
+
+ if (!authResult.isModified())
+ return false;
+
+ info = authResult;
+
+ kdDebug(7019) << "SlaveBase::openPassDlg: username=" << info.username << endl;
+ kdDebug(7019) << "SlaveBase::openPassDlg: password=[hidden]" << endl;
+
+ return true;
+}
+
+int SlaveBase::messageBox( MessageBoxType type, const TQString &text, const TQString &caption,
+ const TQString &buttonYes, const TQString &buttonNo )
+{
+ return messageBox( text, type, caption, buttonYes, buttonNo, TQString::null );
+}
+
+int SlaveBase::messageBox( const TQString &text, MessageBoxType type, const TQString &caption,
+ const TQString &buttonYes, const TQString &buttonNo, const TQString &dontAskAgainName )
+{
+ kdDebug(7019) << "messageBox " << type << " " << text << " - " << caption << buttonYes << buttonNo << endl;
+ KIO_DATA << (TQ_INT32)type << text << caption << buttonYes << buttonNo << dontAskAgainName;
+ m_pConnection->send( INF_MESSAGEBOX, data );
+ if ( waitForAnswer( CMD_MESSAGEBOXANSWER, 0, data ) != -1 )
+ {
+ TQDataStream stream( data, IO_ReadOnly );
+ int answer;
+ stream >> answer;
+ kdDebug(7019) << "got messagebox answer" << answer << endl;
+ return answer;
+ } else
+ return 0; // communication failure
+}
+
+bool SlaveBase::canResume( TDEIO::filesize_t offset )
+{
+ kdDebug(7019) << "SlaveBase::canResume offset=" << TDEIO::number(offset) << endl;
+ d->needSendCanResume = false;
+ KIO_DATA << KIO_FILESIZE_T(offset);
+ m_pConnection->send( MSG_RESUME, data );
+ if ( offset )
+ {
+ int cmd;
+ if ( waitForAnswer( CMD_RESUMEANSWER, CMD_NONE, data, &cmd ) != -1 )
+ {
+ kdDebug(7019) << "SlaveBase::canResume returning " << (cmd == CMD_RESUMEANSWER) << endl;
+ return cmd == CMD_RESUMEANSWER;
+ } else
+ return false;
+ }
+ else // No resuming possible -> no answer to wait for
+ return true;
+}
+
+
+
+int SlaveBase::waitForAnswer( int expected1, int expected2, TQByteArray & data, int *pCmd )
+{
+ int cmd, result;
+ for (;;)
+ {
+ result = m_pConnection->read( &cmd, data );
+ if ( result == -1 )
+ {
+ kdDebug(7019) << "SlaveBase::waitForAnswer has read error." << endl;
+ return -1;
+ }
+ if ( cmd == expected1 || cmd == expected2 )
+ {
+ if ( pCmd ) *pCmd = cmd;
+ return result;
+ }
+ if ( isSubCommand(cmd) )
+ {
+ dispatch( cmd, data );
+ }
+ else
+ {
+ kdWarning() << "Got cmd " << cmd << " while waiting for an answer!" << endl;
+ }
+ }
+}
+
+
+int SlaveBase::readData( TQByteArray &buffer)
+{
+ int result = waitForAnswer( MSG_DATA, 0, buffer );
+ //kdDebug(7019) << "readData: length = " << result << " " << endl;
+ return result;
+}
+
+void SlaveBase::setTimeoutSpecialCommand(int timeout, const TQByteArray &data)
+{
+ if (timeout > 0)
+ d->timeout = time(0)+(time_t)timeout;
+ else if (timeout == 0)
+ d->timeout = 1; // Immediate timeout
+ else
+ d->timeout = 0; // Canceled
+
+ d->timeoutData = data;
+}
+
+void SlaveBase::dispatch( int command, const TQByteArray &data )
+{
+ TQDataStream stream( data, IO_ReadOnly );
+
+ KURL url;
+ int i;
+
+ switch( command ) {
+ case CMD_HOST: {
+ // Reset s_seqNr, see kpasswdserver/DESIGN
+ s_seqNr = 0;
+ TQString passwd;
+ TQString host, user;
+ stream >> host >> i >> user >> passwd;
+ setHost( host, i, user, passwd );
+ }
+ break;
+ case CMD_CONNECT:
+ openConnection( );
+ break;
+ case CMD_DISCONNECT:
+ closeConnection( );
+ break;
+ case CMD_SLAVE_STATUS:
+ slave_status();
+ break;
+ case CMD_SLAVE_CONNECT:
+ {
+ d->onHold = false;
+ TQString app_socket;
+ TQDataStream stream( data, IO_ReadOnly);
+ stream >> app_socket;
+ appconn->send( MSG_SLAVE_ACK );
+ disconnectSlave();
+ mConnectedToApp = true;
+ connectSlave(app_socket);
+ } break;
+ case CMD_SLAVE_HOLD:
+ {
+ KURL url;
+ TQDataStream stream( data, IO_ReadOnly);
+ stream >> url;
+ d->onHoldUrl = url;
+ d->onHold = true;
+ disconnectSlave();
+ mConnectedToApp = false;
+ // Do not close connection!
+ connectSlave(mPoolSocket);
+ } break;
+ case CMD_REPARSECONFIGURATION:
+ reparseConfiguration();
+ break;
+ case CMD_CONFIG:
+ stream >> d->configData;
+#ifdef Q_OS_UNIX //TODO: not yet available on WIN32
+ KSocks::setConfig(d->config);
+#endif
+ delete d->remotefile;
+ d->remotefile = 0;
+ break;
+ case CMD_GET:
+ {
+ stream >> url;
+ get( url );
+ } break;
+ case CMD_PUT:
+ {
+ int permissions;
+ TQ_INT8 iOverwrite, iResume;
+ stream >> url >> iOverwrite >> iResume >> permissions;
+ bool overwrite = ( iOverwrite != 0 );
+ bool resume = ( iResume != 0 );
+
+ // Remember that we need to send canResume(), TransferJob is expecting
+ // it. Well, in theory this shouldn't be done if resume is true.
+ // (the resume bool is currently unused)
+ d->needSendCanResume = true /* !resume */;
+
+ put( url, permissions, overwrite, resume);
+ } break;
+ case CMD_STAT:
+ stream >> url;
+ stat( url );
+ break;
+ case CMD_MIMETYPE:
+ stream >> url;
+ mimetype( url );
+ break;
+ case CMD_LISTDIR:
+ stream >> url;
+ listDir( url );
+ break;
+ case CMD_MKDIR:
+ stream >> url >> i;
+ mkdir( url, i );
+ break;
+ case CMD_RENAME:
+ {
+ TQ_INT8 iOverwrite;
+ KURL url2;
+ stream >> url >> url2 >> iOverwrite;
+ bool overwrite = (iOverwrite != 0);
+ rename( url, url2, overwrite );
+ } break;
+ case CMD_SYMLINK:
+ {
+ TQ_INT8 iOverwrite;
+ TQString target;
+ stream >> target >> url >> iOverwrite;
+ bool overwrite = (iOverwrite != 0);
+ symlink( target, url, overwrite );
+ } break;
+ case CMD_COPY:
+ {
+ int permissions;
+ TQ_INT8 iOverwrite;
+ KURL url2;
+ stream >> url >> url2 >> permissions >> iOverwrite;
+ bool overwrite = (iOverwrite != 0);
+ copy( url, url2, permissions, overwrite );
+ } break;
+ case CMD_DEL:
+ {
+ TQ_INT8 isFile;
+ stream >> url >> isFile;
+ del( url, isFile != 0);
+ } break;
+ case CMD_CHMOD:
+ stream >> url >> i;
+ chmod( url, i);
+ break;
+ case CMD_SPECIAL:
+ special( data );
+ break;
+ case CMD_META_DATA:
+ //kdDebug(7019) << "(" << getpid() << ") Incoming meta-data..." << endl;
+ stream >> mIncomingMetaData;
+ break;
+ case CMD_SUBURL:
+ stream >> url;
+ setSubURL(url);
+ break;
+ case CMD_NONE:
+ fprintf(stderr, "Got unexpected CMD_NONE!\n");
+ break;
+ case CMD_MULTI_GET:
+ multiGet( data );
+ break;
+ case CMD_LOCALURL:
+ {
+ stream >> url;
+ localURL( url );
+ } break;
+ default:
+ // Some command we don't understand.
+ // Just ignore it, it may come from some future version of KDE.
+ break;
+ }
+}
+
+TQString SlaveBase::createAuthCacheKey( const KURL& url )
+{
+ if( !url.isValid() )
+ return TQString::null;
+
+ // Generate the basic key sequence.
+ TQString key = url.protocol();
+ key += '-';
+ key += url.host();
+ int port = url.port();
+ if( port )
+ {
+ key += ':';
+ key += TQString::number(port);
+ }
+
+ return key;
+}
+
+bool SlaveBase::pingCacheDaemon() const
+{
+#ifdef Q_OS_UNIX
+ // TODO: Ping kded / kpasswdserver
+ KDEsuClient client;
+ int success = client.ping();
+ if( success == -1 )
+ {
+ success = client.startServer();
+ if( success == -1 )
+ {
+ kdDebug(7019) << "Cannot start a new deamon!!" << endl;
+ return false;
+ }
+ kdDebug(7019) << "Sucessfully started new cache deamon!!" << endl;
+ }
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool SlaveBase::checkCachedAuthentication( AuthInfo& info )
+{
+ TQCString replyType;
+ TQByteArray params;
+ TQByteArray reply;
+ AuthInfo authResult;
+ long windowId = metaData("window-id").toLong();
+ unsigned long userTimestamp = metaData("user-timestamp").toULong();
+
+ kdDebug(7019) << "SlaveBase::checkCachedAuthInfo window = " << windowId << " url = " << info.url.url() << endl;
+
+ (void) dcopClient(); // Make sure to have a dcop client.
+
+ TQDataStream stream(params, IO_WriteOnly);
+ stream << info << windowId << userTimestamp;
+
+ if ( !d->dcopClient->call( "kded", "kpasswdserver", "checkAuthInfo(TDEIO::AuthInfo, long int, unsigned long int)",
+ params, replyType, reply ) )
+ {
+ kdWarning(7019) << "Can't communicate with kded_kpasswdserver!" << endl;
+ return false;
+ }
+
+ if ( replyType == "TDEIO::AuthInfo" )
+ {
+ TQDataStream stream2( reply, IO_ReadOnly );
+ stream2 >> authResult;
+ }
+ else
+ {
+ kdError(7019) << "DCOP function checkAuthInfo(...) returns "
+ << replyType << ", expected TDEIO::AuthInfo" << endl;
+ return false;
+ }
+ if (!authResult.isModified())
+ {
+ return false;
+ }
+
+ info = authResult;
+ return true;
+}
+
+bool SlaveBase::cacheAuthentication( const AuthInfo& info )
+{
+ TQByteArray params;
+ long windowId = metaData("window-id").toLong();
+
+ (void) dcopClient(); // Make sure to have a dcop client.
+
+ TQDataStream stream(params, IO_WriteOnly);
+ stream << info << windowId;
+
+ d->dcopClient->send( "kded", "kpasswdserver", "addAuthInfo(TDEIO::AuthInfo, long int)", params );
+
+ return true;
+}
+
+int SlaveBase::connectTimeout()
+{
+ bool ok;
+ TQString tmp = metaData("ConnectTimeout");
+ int result = tmp.toInt(&ok);
+ if (ok)
+ return result;
+ return DEFAULT_CONNECT_TIMEOUT;
+}
+
+int SlaveBase::proxyConnectTimeout()
+{
+ bool ok;
+ TQString tmp = metaData("ProxyConnectTimeout");
+ int result = tmp.toInt(&ok);
+ if (ok)
+ return result;
+ return DEFAULT_PROXY_CONNECT_TIMEOUT;
+}
+
+
+int SlaveBase::responseTimeout()
+{
+ bool ok;
+ TQString tmp = metaData("ResponseTimeout");
+ int result = tmp.toInt(&ok);
+ if (ok)
+ return result;
+ return DEFAULT_RESPONSE_TIMEOUT;
+}
+
+
+int SlaveBase::readTimeout()
+{
+ bool ok;
+ TQString tmp = metaData("ReadTimeout");
+ int result = tmp.toInt(&ok);
+ if (ok)
+ return result;
+ return DEFAULT_READ_TIMEOUT;
+}
+
+bool SlaveBase::wasKilled() const
+{
+ return d->wasKilled;
+}
+
+void SlaveBase::setKillFlag()
+{
+ d->wasKilled=true;
+}
+
+void SlaveBase::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
diff --git a/tdeio/tdeio/slavebase.h b/tdeio/tdeio/slavebase.h
new file mode 100644
index 000000000..1030d94c4
--- /dev/null
+++ b/tdeio/tdeio/slavebase.h
@@ -0,0 +1,847 @@
+/*
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __slavebase_h
+#define __slavebase_h
+
+#include <kurl.h>
+#include <tdeconfigbase.h>
+#include <tdeio/global.h>
+#include <tdeio/authinfo.h>
+
+class DCOPClient;
+class KRemoteEncoding;
+
+namespace TDEIO {
+
+class Connection;
+class SlaveBasePrivate;
+
+/**
+ * There are two classes that specifies the protocol between application (job)
+ * and tdeioslave. SlaveInterface is the class to use on the application end,
+ * SlaveBase is the one to use on the slave end.
+ *
+ * Slave implementations should simply inherit SlaveBase
+ *
+ * A call to foo() results in a call to slotFoo() on the other end.
+ */
+class TDEIO_EXPORT SlaveBase
+{
+public:
+ SlaveBase( const TQCString &protocol, const TQCString &pool_socket, const TQCString &app_socket);
+ virtual ~SlaveBase();
+
+ /**
+ * @internal
+ * Terminate the slave by calling the destructor and then ::exit()
+ */
+ void exit();
+
+ /**
+ * @internal
+ */
+ void dispatchLoop();
+
+ /**
+ * @internal
+ */
+ void setConnection( Connection* connection ) { m_pConnection = connection; }
+ /**
+ * @internal
+ */
+ Connection *connection() const { return m_pConnection; }
+
+
+ ///////////
+ // Message Signals to send to the job
+ ///////////
+
+ /**
+ * Sends data in the slave to the job (i.e. in get).
+ *
+ * To signal end of data, simply send an empty
+ * TQByteArray().
+ *
+ * @param data the data read by the slave
+ */
+ void data( const TQByteArray &data );
+
+ /**
+ * Asks for data from the job.
+ * @see readData
+ */
+ void dataReq( );
+
+ /**
+ * Call to signal an error.
+ * This also finishes the job, no need to call finished.
+ *
+ * If the Error code is TDEIO::ERR_SLAVE_DEFINED then the
+ * _text should contain the complete translated text of
+ * of the error message. This message will be displayed
+ * in an KTextBrowser which allows rich text complete
+ * with hyper links. Email links will call the default
+ * mailer, "exec:/command arg1 arg2" will be forked and
+ * all other links will call the default browser.
+ *
+ * @see TDEIO::Error
+ * @see KTextBrowser
+ * @param _errid the error code from TDEIO::Error
+ * @param _text the rich text error message
+ */
+ void error( int _errid, const TQString &_text );
+
+ /**
+ * Call in openConnection, if you reimplement it, when you're done.
+ */
+ void connected();
+
+ /**
+ * Call to signal successful completion of any command
+ * (besides openConnection and closeConnection)
+ */
+ void finished();
+
+ /**
+ * Call to signal that data from the sub-URL is needed
+ */
+ void needSubURLData();
+
+ /**
+ * Used to report the status of the slave.
+ * @param host the slave is currently connected to. (Should be
+ * empty if not connected)
+ * @param connected Whether an actual network connection exists.
+ **/
+ void slaveStatus(const TQString &host, bool connected);
+
+ /**
+ * Call this from stat() to express details about an object, the
+ * UDSEntry customarily contains the atoms describing file name, size,
+ * mimetype, etc.
+ * @param _entry The UDSEntry containing all of the object attributes.
+ */
+ void statEntry( const UDSEntry& _entry );
+
+ /**
+ * Call this in listDir, each time you have a bunch of entries
+ * to report.
+ * @param _entry The UDSEntry containing all of the object attributes.
+ */
+ void listEntries( const UDSEntryList& _entry );
+
+ /**
+ * Call this at the beginning of put(), to give the size of the existing
+ * partial file, if there is one. The @p offset argument notifies the
+ * other job (the one that gets the data) about the offset to use.
+ * In this case, the boolean returns whether we can indeed resume or not
+ * (we can't if the protocol doing the get() doesn't support setting an offset)
+ */
+ bool canResume( TDEIO::filesize_t offset );
+
+ /*
+ * Call this at the beginning of get(), if the "resume" metadata was set
+ * and resuming is implemented by this protocol.
+ */
+ void canResume();
+
+ ///////////
+ // Info Signals to send to the job
+ ///////////
+
+ /**
+ * Call this in get and copy, to give the total size
+ * of the file
+ * Call in listDir too, when you know the total number of items.
+ */
+ void totalSize( TDEIO::filesize_t _bytes );
+ /**
+ * Call this during get and copy, once in a while,
+ * to give some info about the current state.
+ * Don't emit it in listDir, listEntries speaks for itself.
+ */
+ void processedSize( TDEIO::filesize_t _bytes );
+
+ /**
+ * Only use this if you can't know in advance the size of the
+ * copied data. For example, if you're doing variable bitrate
+ * compression of the source.
+ *
+ * STUB ! Currently unimplemented. Here now for binary compatibility.
+ *
+ * Call this during get and copy, once in a while,
+ * to give some info about the current state.
+ * Don't emit it in listDir, listEntries speaks for itself.
+ */
+ void processedPercent( float percent );
+
+ /**
+ * Call this in get and copy, to give the current transfer
+ * speed, but only if it can't be calculated out of the size you
+ * passed to processedSize (in most cases you don't want to call it)
+ */
+ void speed( unsigned long _bytes_per_second );
+
+ /**
+ * Call this to signal a redirection
+ * The job will take care of going to that url.
+ */
+ void redirection( const KURL &_url );
+
+ /**
+ * Tell that we will only get an error page here.
+ * This means: the data you'll get isn't the data you requested,
+ * but an error page (usually HTML) that describes an error.
+ */
+ void errorPage();
+
+ /**
+ * Call this in mimetype() and in get(), when you know the mimetype.
+ * See mimetype about other ways to implement it.
+ */
+ void mimeType( const TQString &_type );
+
+ /**
+ * Call to signal a warning, to be displayed in a dialog box.
+ */
+ void warning( const TQString &msg );
+
+ /**
+ * Call to signal a message, to be displayed if the application wants to,
+ * for instance in a status bar. Usual examples are "connecting to host xyz", etc.
+ */
+ void infoMessage( const TQString &msg );
+
+ enum MessageBoxType { QuestionYesNo = 1, WarningYesNo = 2, WarningContinueCancel = 3, WarningYesNoCancel = 4, Information = 5, SSLMessageBox = 6 };
+
+ /**
+ * Call this to show a message box from the slave
+ * @param type type of message box: QuestionYesNo, WarningYesNo, WarningContinueCancel...
+ * @param text Message string. May contain newlines.
+ * @param caption Message box title.
+ * @param buttonYes The text for the first button.
+ * The default is i18n("&Yes").
+ * @param buttonNo The text for the second button.
+ * The default is i18n("&No").
+ * Note: for ContinueCancel, buttonYes is the continue button and buttonNo is unused.
+ * and for Information, none is used.
+ * @return a button code, as defined in KMessageBox, or 0 on communication error.
+ */
+ int messageBox( MessageBoxType type, const TQString &text,
+ const TQString &caption = TQString::null,
+ const TQString &buttonYes = TQString::null,
+ const TQString &buttonNo = TQString::null );
+
+ /**
+ * Call this to show a message box from the slave
+ * @param text Message string. May contain newlines.
+ * @param type type of message box: QuestionYesNo, WarningYesNo, WarningContinueCancel...
+ * @param caption Message box title.
+ * @param buttonYes The text for the first button.
+ * The default is i18n("&Yes").
+ * @param buttonNo The text for the second button.
+ * The default is i18n("&No").
+ * Note: for ContinueCancel, buttonYes is the continue button and buttonNo is unused.
+ * and for Information, none is used.
+ * @param dontAskAgainName A checkbox is added with which further confirmation can be turned off.
+ * The string is used to lookup and store the setting in tdeioslaverc.
+ * @return a button code, as defined in KMessageBox, or 0 on communication error.
+ * @since 3.3
+ */
+ int messageBox( const TQString &text, MessageBoxType type,
+ const TQString &caption = TQString::null,
+ const TQString &buttonYes = TQString::null,
+ const TQString &buttonNo = TQString::null,
+ const TQString &dontAskAgainName = TQString::null );
+
+ /**
+ * Sets meta-data to be send to the application before the first
+ * data() or finished() signal.
+ */
+ void setMetaData(const TQString &key, const TQString &value);
+
+ /**
+ * Queries for the existence of a certain config/meta-data entry
+ * send by the application to the slave.
+ * @since 3.2
+ */
+ bool hasMetaData(const TQString &key) const;
+
+ /**
+ * Queries for config/meta-data send by the application to the slave.
+ * @since 3.2
+ */
+ TQString metaData(const TQString &key) const;
+
+ /**
+ * @obsolete kept for binary compatibility
+ * Queries for the existence of a certain config/meta-data entry
+ * send by the application to the slave.
+ */
+ bool hasMetaData(const TQString &key);
+
+ /**
+ * @obsolete kept for binary compatibility
+ * Queries for config/meta-data sent by the application to the slave.
+ */
+ TQString metaData(const TQString &key);
+
+ /**
+ * @internal for ForwardingSlaveBase
+ * Contains all metadata (but no config) sent by the application to the slave.
+ * @since 3.5.2
+ */
+ MetaData allMetaData() const { return mIncomingMetaData; }
+
+ /**
+ * Returns a configuration object to query config/meta-data information
+ * from.
+ *
+ * The application provides the slave with all configuration information
+ * relevant for the current protocol and host.
+ */
+ TDEConfigBase* config();
+
+ /**
+ * Returns an object that can translate remote filenames into proper
+ * Unicode forms. This encoding can be set by the user.
+ *
+ * @since 3.3
+ */
+ KRemoteEncoding* remoteEncoding();
+
+
+ ///////////
+ // Commands sent by the job, the slave has to
+ // override what it wants to implement
+ ///////////
+
+ /**
+ * Set the host
+ * @param host
+ * @param port
+ * @param user
+ * @param pass
+ * Called directly by createSlave, this is why there is no equivalent in
+ * SlaveInterface, unlike the other methods.
+ *
+ * This method is called whenever a change in host, port or user occurs.
+ */
+ virtual void setHost(const TQString& host, int port, const TQString& user, const TQString& pass);
+
+ /**
+ * Prepare slave for streaming operation
+ */
+ virtual void setSubURL(const KURL&url);
+
+ /**
+ * Opens the connection (forced)
+ * When this function gets called the slave is operating in
+ * connection-oriented mode.
+ * When a connection gets lost while the slave operates in
+ * connection oriented mode, the slave should report
+ * ERR_CONNECTION_BROKEN instead of reconnecting. The user is
+ * expected to disconnect the slave in the error handler.
+ */
+ virtual void openConnection();
+
+ /**
+ * Closes the connection (forced)
+ * Called when the application disconnects the slave to close
+ * any open network connections.
+ *
+ * When the slave was operating in connection-oriented mode,
+ * it should reset itself to connectionless (default) mode.
+ */
+ virtual void closeConnection();
+
+ /**
+ * get, aka read.
+ * @param url the full url for this request. Host, port and user of the URL
+ * can be assumed to be the same as in the last setHost() call.
+ * The slave emits the data through data
+ */
+ virtual void get( const KURL& url );
+
+ /**
+ * put, i.e. write data into a file.
+ *
+ * @param url where to write the file
+ * @param permissions may be -1. In this case no special permission mode is set.
+ * @param overwrite if true, any existing file will be overwritten.
+ * If the file indeed already exists, the slave should NOT apply the
+ * permissions change to it.
+ * @param resume currently unused, please ignore.
+ * The support for resuming using .part files is done by calling canResume().
+ *
+ * IMPORTANT: Use the "modified" metadata in order to set the modification time of the file.
+ *
+ * @see canResume()
+ */
+ virtual void put( const KURL& url, int permissions, bool overwrite, bool resume );
+
+ /**
+ * Finds all details for one file or directory.
+ * The information returned is the same as what listDir returns,
+ * but only for one file or directory.
+ */
+ virtual void stat( const KURL& url );
+
+ /**
+ * Finds mimetype for one file or directory.
+ *
+ * This method should either emit 'mimeType' or it
+ * should send a block of data big enough to be able
+ * to determine the mimetype.
+ *
+ * If the slave doesn't reimplement it, a get will
+ * be issued, i.e. the whole file will be downloaded before
+ * determining the mimetype on it - this is obviously not a
+ * good thing in most cases.
+ */
+ virtual void mimetype( const KURL& url );
+
+ /**
+ * Lists the contents of @p url.
+ * The slave should emit ERR_CANNOT_ENTER_DIRECTORY if it doesn't exist,
+ * if we don't have enough permissions, or if it is a file
+ * It should also emit totalFiles as soon as it knows how many
+ * files it will list.
+ */
+ virtual void listDir( const KURL& url );
+
+ /**
+ * Create a directory
+ * @param url path to the directory to create
+ * @param permissions the permissions to set after creating the directory
+ * (-1 if no permissions to be set)
+ * The slave emits ERR_COULD_NOT_MKDIR if failure.
+ */
+ virtual void mkdir( const KURL&url, int permissions );
+
+ /**
+ * Rename @p oldname into @p newname.
+ * If the slave returns an error ERR_UNSUPPORTED_ACTION, the job will
+ * ask for copy + del instead.
+ * @param src where to move the file from
+ * @param dest where to move the file to
+ * @param overwrite if true, any existing file will be overwritten
+ */
+ virtual void rename( const KURL& src, const KURL& dest, bool overwrite );
+
+ /**
+ * Creates a symbolic link named @p dest, pointing to @p target, which
+ * may be a relative or an absolute path.
+ * @param target The string that will become the "target" of the link (can be relative)
+ * @param dest The symlink to create.
+ * @param overwrite whether to automatically overwrite if the dest exists
+ */
+ virtual void symlink( const TQString& target, const KURL& dest, bool overwrite );
+
+ /**
+ * Change permissions on @p path
+ * The slave emits ERR_DOES_NOT_EXIST or ERR_CANNOT_CHMOD
+ */
+ virtual void chmod( const KURL& url, int permissions );
+
+ /**
+ * Copy @p src into @p dest.
+ * If the slave returns an error ERR_UNSUPPORTED_ACTION, the job will
+ * ask for get + put instead.
+ * @param src where to copy the file from (decoded)
+ * @param dest where to copy the file to (decoded)
+ * @param permissions may be -1. In this case no special permission mode is set.
+ * @param overwrite if true, any existing file will be overwritten
+ *
+ */
+ virtual void copy( const KURL &src, const KURL &dest, int permissions, bool overwrite );
+
+ /**
+ * Delete a file or directory.
+ * @param url file/directory to delete
+ * @param isfile if true, a file should be deleted.
+ * if false, a directory should be deleted.
+ */
+ virtual void del( const KURL &url, bool isfile);
+
+ // TODO KDE4: add setLinkDest() or something, to modify symlink targets.
+ // Will be used for kio_file but also kio_remote (#97129)
+
+ /**
+ * Used for any command that is specific to this slave (protocol)
+ * Examples are : HTTP POST, mount and unmount (kio_file)
+ *
+ * @param data packed data; the meaning is completely dependent on the
+ * slave, but usually starts with an int for the command number.
+ * Document your slave's commands, at least in its header file.
+ */
+ virtual void special( const TQByteArray & data );
+
+ /**
+ * Used for multiple get. Currently only used foir HTTP pielining
+ * support.
+ *
+ * @param data packed data; Contains number of URLs to fetch, and for
+ * each URL the URL itself and its associated MetaData.
+ */
+ virtual void multiGet( const TQByteArray & data );
+
+ /**
+ * Called to get the status of the slave. Slave should respond
+ * by calling slaveStatus(...)
+ */
+ virtual void slave_status();
+
+ /**
+ * Called by the scheduler to tell the slave that the configuration
+ * changed (i.e. proxy settings) .
+ */
+ virtual void reparseConfiguration();
+
+ /**
+ * For use with for ForwardingSlaveBase
+ * Returns the local URL of the given remote URL if possible
+ * @since R14.0.0
+ */
+ virtual void localURL( const KURL& remoteURL );
+
+ /**
+ * @return timeout value for connecting to remote host.
+ */
+ int connectTimeout();
+
+ /**
+ * @return timeout value for connecting to proxy in secs.
+ */
+ int proxyConnectTimeout();
+
+ /**
+ * @return timeout value for read from first data from
+ * remote host in seconds.
+ */
+ int responseTimeout();
+
+ /**
+ * @return timeout value for read from subsequent data from
+ * remote host in secs.
+ */
+ int readTimeout();
+
+ /**
+ * This function sets a timeout of @p timeout seconds and calls
+ * special(data) when the timeout occurs as if it was called by the
+ * application.
+ *
+ * A timeout can only occur when the slave is waiting for a command
+ * from the application.
+ *
+ * Specifying a negative timeout cancels a pending timeout.
+ *
+ * Only one timeout at a time is supported, setting a timeout
+ * cancels any pending timeout.
+ * @since 3.1
+ */
+ void setTimeoutSpecialCommand(int timeout, const TQByteArray &data=TQByteArray());
+
+ /**
+ * @internal
+ */
+ static void sigsegv_handler(int);
+ /**
+ * @internal
+ */
+ static void sigpipe_handler(int);
+
+ /////////////////
+ // Dispatching (internal)
+ ////////////////
+
+ /**
+ * @internal
+ */
+ virtual bool dispatch();
+ /**
+ * @internal
+ */
+ virtual void dispatch( int command, const TQByteArray &data );
+
+ /**
+ * Read data send by the job, after a dataReq
+ *
+ * @param buffer buffer where data is stored
+ * @return 0 on end of data,
+ * > 0 bytes read
+ * < 0 error
+ **/
+ int readData( TQByteArray &buffer );
+
+ /**
+ * internal function to be called by the slave.
+ * It collects entries and emits them via listEntries
+ * when enough of them are there or a certain time
+ * frame exceeded (to make sure the app gets some
+ * items in time but not too many items one by one
+ * as this will cause a drastic performance penalty)
+ * @param _entry The UDSEntry containing all of the object attributes.
+ * @param ready set to true after emitting all items. @p _entry is not
+ * used in this case
+ */
+ void listEntry( const UDSEntry& _entry, bool ready);
+
+ /**
+ * internal function to connect a slave to/ disconnect from
+ * either the slave pool or the application
+ */
+ void connectSlave(const TQString& path);
+ void disconnectSlave();
+
+ /**
+ * Prompt the user for Authorization info (login & password).
+ *
+ * Use this function to request authorization information from
+ * the end user. You can also pass an error message which explains
+ * why a previous authorization attempt failed. Here is a very
+ * simple example:
+ *
+ * \code
+ * TDEIO::AuthInfo authInfo;
+ * if ( openPassDlg( authInfo ) )
+ * {
+ * kdDebug() << TQString::fromLatin1("User: ")
+ * << authInfo.username << endl;
+ * kdDebug() << TQString::fromLatin1("Password: ")
+ * << TQString::fromLatin1("Not displayed here!") << endl;
+ * }
+ * \endcode
+ *
+ * You can also preset some values like the username, caption or
+ * comment as follows:
+ *
+ * \code
+ * TDEIO::AuthInfo authInfo;
+ * authInfo.caption= "Acme Password Dialog";
+ * authInfo.username= "Wile E. Coyote";
+ * TQString errorMsg = "You entered an incorrect password.";
+ * if ( openPassDlg( authInfo, errorMsg ) )
+ * {
+ * kdDebug() << TQString::fromLatin1("User: ")
+ * << authInfo.username << endl;
+ * kdDebug() << TQString::fromLatin1("Password: ")
+ * << TQString::fromLatin1("Not displayed here!") << endl;
+ * }
+ * \endcode
+ *
+ * \note You should consider using checkCachedAuthentication() to
+ * see if the password is available in kpasswdserver before calling
+ * this function.
+ *
+ * \note A call to this function can fail and return @p false,
+ * if the UIServer could not be started for whatever reason.
+ *
+ * @see checkCachedAuthentication
+ * @param info See AuthInfo.
+ * @param errorMsg Error message to show
+ * @return @p true if user clicks on "OK", @p false otherwsie.
+ * @since 3.1
+ */
+ bool openPassDlg( TDEIO::AuthInfo& info, const TQString &errorMsg );
+
+ /**
+ * Same as above function except it does not need error message.
+ * BIC: Combine this function with the above for KDE4.
+ */
+ bool openPassDlg( TDEIO::AuthInfo& info );
+
+ /**
+ * Checks for cached authentication based on parameters
+ * given by @p info.
+ *
+ * Use this function to check if any cached password exists
+ * for the URL given by @p info. If @p AuthInfo::realmValue
+ * and/or @p AuthInfo::verifyPath flag is specified, then
+ * they will also be factored in determining the presence
+ * of a cached password. Note that @p Auth::url is a required
+ * parameter when attempting to check for cached authorization
+ * info. Here is a simple example:
+ *
+ * \code
+ * AuthInfo info;
+ * info.url = KURL("http://www.foobar.org/foo/bar");
+ * info.username = "somename";
+ * info.verifyPath = true;
+ * if ( !checkCachedAuthentication( info ) )
+ * {
+ * if ( !openPassDlg(info) )
+ * ....
+ * }
+ * \endcode
+ *
+ * @param info See AuthInfo.
+ * @return @p true if cached Authorization is found, false otherwise.
+ */
+ bool checkCachedAuthentication( AuthInfo& info );
+
+ /**
+ * Explicitly store authentication information. openPassDlg already
+ * stores password information automatically, you only need to call
+ * this function if you want to store authentication information that
+ * is different from the information returned by openPassDlg.
+ */
+ bool cacheAuthentication( const AuthInfo& info );
+
+ /**
+ * @obsolete as of 3.1.
+ * TODO: Remove before KDE 4.0
+ */
+ bool pingCacheDaemon() const;
+
+ /**
+ * @obsolete as of 3.1. Use openPassDlg instead.
+ * TODO: Remove before KDE 4.0
+ * Creates a basic key to be used to cache the password.
+ * @param url the url from which the key is supposed to be generated
+ */
+ TQString createAuthCacheKey( const KURL& url );
+
+ /**
+ * @obsolete as of 3.1. Use openPassDlg instead.
+ * TODO: Remove before KDE 4.0
+ *
+ * Cache authentication information is now stored automatically
+ * by openPassDlg.
+ */
+ void sendAuthenticationKey( const TQCString& gKey, const TQCString& key, bool keep );
+
+ /**
+ * @obsolete as of 3.1. Use openPassDlg instead.
+ * TODO: Remove before KDE 4.0
+ *
+ * Cached authentication information is now session based and
+ * removed automatically when a given session ends, i.e. the
+ * application is closed.
+ */
+ void delCachedAuthentication( const TQString& key );
+
+ /**
+ * @obsolete as of 3.1. Use openPassDlg instead.
+ * TODO: Remove before KDE 4.0
+ */
+ void setMultipleAuthCaching( bool ) {};
+
+ /**
+ * @obsolete as of 3.1. Use openPassDlg instead.
+ * TODO: Remove before KDE 4.0
+ */
+ bool multipleAuthCaching() const { return false; }
+
+ /**
+ * Used by the slave to check if it can connect
+ * to a given host. This should be called where the slave is ready
+ * to do a ::connect() on a socket. For each call to
+ * requestNetwork must exist a matching call to
+ * dropNetwork, or the system will stay online until
+ * KNetMgr gets closed (or the SlaveBase gets destructed)!
+ *
+ * If KNetMgr is not running, then this is a no-op and returns true
+ *
+ * @param host tells the netmgr the host the slave wants to connect
+ * to. As this could also be a proxy, we can't just take
+ * the host currenctly connected to (but that's the default
+ * value)
+ *
+ * @return true in theorie, the host is reachable
+ * false the system is offline and the host is in a remote network.
+ */
+ bool requestNetwork(const TQString& host = TQString::null);
+
+ /**
+ * Used by the slave to withdraw a connection requested by
+ * requestNetwork. This function cancels the last call to
+ * requestNetwork. If a client uses more than one internet
+ * connection, it must use dropNetwork(host) to
+ * stop each request.
+ *
+ * If KNetMgr is not running, then this is a no-op.
+ *
+ * @param host the host passed to requestNetwork
+ *
+ * A slave should call this function every time it disconnect from a host.
+ * */
+ void dropNetwork(const TQString& host = TQString::null);
+
+ /**
+ * Return the dcop client used by this slave.
+ * @since 3.1
+ */
+ DCOPClient *dcopClient();
+
+ /**
+ * Wait for an answer to our request, until we get @p expected1 or @p expected2
+ * @return the result from readData, as well as the cmd in *pCmd if set, and the data in @p data
+ */
+ int waitForAnswer( int expected1, int expected2, TQByteArray & data, int * pCmd = 0 );
+
+ /**
+ * Internal function to transmit meta data to the application.
+ */
+ void sendMetaData();
+
+ /**
+ * Name of the protocol supported by this slave
+ */
+ TQCString mProtocol;
+
+ Connection * m_pConnection;
+
+ MetaData mOutgoingMetaData;
+ MetaData mIncomingMetaData;
+
+ /** If your ioslave was killed by a signal, wasKilled() returns true.
+ Check it regularly in lengthy functions (e.g. in get();) and return
+ as fast as possible from this function if wasKilled() returns true.
+ This will ensure that your slave destructor will be called correctly.
+ @since 3.1
+ */
+ bool wasKilled() const;
+
+ /** Internally used.
+ * @internal
+ * @since 3.1
+ */
+ void setKillFlag();
+
+protected:
+ UDSEntryList pendingListEntries;
+ uint listEntryCurrentSize;
+ long listEntry_sec, listEntry_usec;
+ Connection *appconn;
+ TQString mPoolSocket;
+ TQString mAppSocket;
+ bool mConnectedToApp;
+ static long s_seqNr;
+ virtual void virtual_hook( int id, void* data );
+
+private:
+ SlaveBasePrivate *d;
+};
+
+}
+
+#endif
diff --git a/tdeio/tdeio/slaveconfig.cpp b/tdeio/tdeio/slaveconfig.cpp
new file mode 100644
index 000000000..e81146e76
--- /dev/null
+++ b/tdeio/tdeio/slaveconfig.cpp
@@ -0,0 +1,225 @@
+// -*- c++ -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (c) 2001 Waldo Bastian <bastian@kde.org>
+ *
+ * $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include <assert.h>
+
+#include <tqdict.h>
+
+#include <tdeconfig.h>
+#include <kstaticdeleter.h>
+#include <kprotocolinfo.h>
+#include <kprotocolmanager.h>
+
+#include "slaveconfig.h"
+
+using namespace TDEIO;
+
+namespace TDEIO {
+
+class SlaveConfigProtocol
+{
+public:
+ SlaveConfigProtocol() { host.setAutoDelete(true); }
+ ~SlaveConfigProtocol()
+ {
+ delete configFile;
+ }
+
+public:
+ MetaData global;
+ TQDict<MetaData> host;
+ TDEConfig *configFile;
+};
+
+static void readConfig(TDEConfig *config, const TQString & group, MetaData *metaData)
+{
+ *metaData += config->entryMap(group);
+}
+
+class SlaveConfigPrivate
+{
+ public:
+ void readGlobalConfig();
+ SlaveConfigProtocol *readProtocolConfig(const TQString &_protocol);
+ SlaveConfigProtocol *findProtocolConfig(const TQString &_protocol);
+ void readConfigProtocolHost(const TQString &_protocol, SlaveConfigProtocol *scp, const TQString &host);
+ public:
+ MetaData global;
+ TQDict<SlaveConfigProtocol> protocol;
+};
+
+void SlaveConfigPrivate::readGlobalConfig()
+{
+ global.clear();
+ // Read stuff...
+ TDEConfig *config = KProtocolManager::config();
+ readConfig(TDEGlobal::config(), "Socks", &global); // Socks settings.
+ if ( config )
+ readConfig(config, "<default>", &global);
+}
+
+SlaveConfigProtocol* SlaveConfigPrivate::readProtocolConfig(const TQString &_protocol)
+{
+ SlaveConfigProtocol *scp = protocol.find(_protocol);
+ if (!scp)
+ {
+ TQString filename = KProtocolInfo::config(_protocol);
+ scp = new SlaveConfigProtocol;
+ scp->configFile = new TDEConfig(filename, true, false);
+ protocol.insert(_protocol, scp);
+ }
+ // Read global stuff...
+ readConfig(scp->configFile, "<default>", &(scp->global));
+ return scp;
+}
+
+SlaveConfigProtocol* SlaveConfigPrivate::findProtocolConfig(const TQString &_protocol)
+{
+ SlaveConfigProtocol *scp = protocol.find(_protocol);
+ if (!scp)
+ scp = readProtocolConfig(_protocol);
+ return scp;
+}
+
+void SlaveConfigPrivate::readConfigProtocolHost(const TQString &, SlaveConfigProtocol *scp, const TQString &host)
+{
+ MetaData *metaData = new MetaData;
+ scp->host.replace(host, metaData);
+
+ // Read stuff
+ // Break host into domains
+ TQString domain = host;
+
+ if (!domain.contains('.'))
+ {
+ // Host without domain.
+ if (scp->configFile->hasGroup("<local>"))
+ readConfig(scp->configFile, "<local>", metaData);
+ }
+
+ int pos = 0;
+ do
+ {
+ pos = host.findRev('.', pos-1);
+
+ if (pos < 0)
+ domain = host;
+ else
+ domain = host.mid(pos+1);
+
+ if (scp->configFile->hasGroup(domain))
+ readConfig(scp->configFile, domain.lower(), metaData);
+ }
+ while (pos > 0);
+}
+
+
+SlaveConfig *SlaveConfig::_self = 0;
+static KStaticDeleter<SlaveConfig> slaveconfigsd;
+
+SlaveConfig *SlaveConfig::self()
+{
+ if (!_self)
+ _self = slaveconfigsd.setObject(_self, new SlaveConfig);
+ return _self;
+}
+
+SlaveConfig::SlaveConfig()
+{
+ d = new SlaveConfigPrivate;
+ d->protocol.setAutoDelete(true);
+ d->readGlobalConfig();
+}
+
+SlaveConfig::~SlaveConfig()
+{
+ delete d; d = 0;
+ _self = 0;
+}
+
+void SlaveConfig::setConfigData(const TQString &protocol,
+ const TQString &host,
+ const TQString &key,
+ const TQString &value )
+{
+ MetaData config;
+ config.insert(key, value);
+ setConfigData(protocol, host, config);
+}
+
+void SlaveConfig::setConfigData(const TQString &protocol, const TQString &host, const MetaData &config )
+{
+ if (protocol.isEmpty())
+ d->global += config;
+ else {
+ SlaveConfigProtocol *scp = d->findProtocolConfig(protocol);
+ if (host.isEmpty())
+ {
+ scp->global += config;
+ }
+ else
+ {
+ MetaData *hostConfig = scp->host.find(host);
+ if (!hostConfig)
+ {
+ d->readConfigProtocolHost(protocol, scp, host);
+ hostConfig = scp->host.find(host);
+ assert(hostConfig);
+ }
+ *hostConfig += config;
+ }
+ }
+}
+
+MetaData SlaveConfig::configData(const TQString &protocol, const TQString &host)
+{
+ MetaData config = d->global;
+ SlaveConfigProtocol *scp = d->findProtocolConfig(protocol);
+ config += scp->global;
+ if (host.isEmpty())
+ return config;
+ MetaData *hostConfig = scp->host.find(host);
+ if (!hostConfig)
+ {
+ d->readConfigProtocolHost(protocol, scp, host);
+ emit configNeeded(protocol, host);
+ hostConfig = scp->host.find(host);
+ assert(hostConfig);
+ }
+ config += *hostConfig;
+ return config;
+}
+
+TQString SlaveConfig::configData(const TQString &protocol, const TQString &host, const TQString &key)
+{
+ return configData(protocol, host)[key];
+}
+
+void SlaveConfig::reset()
+{
+ d->protocol.clear();
+ d->readGlobalConfig();
+}
+
+}
+
+#include "slaveconfig.moc"
diff --git a/tdeio/tdeio/slaveconfig.h b/tdeio/tdeio/slaveconfig.h
new file mode 100644
index 000000000..500910062
--- /dev/null
+++ b/tdeio/tdeio/slaveconfig.h
@@ -0,0 +1,106 @@
+// -*- c++ -*-
+/*
+ * This file is part of the KDE libraries
+ * Copyright (c) 2001 Waldo Bastian <bastian@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#ifndef KIO_SLAVE_CONFIG_H
+#define KIO_SLAVE_CONFIG_H
+
+#include <tqobject.h>
+#include <tdeio/global.h>
+
+namespace TDEIO {
+
+ class SlaveConfigPrivate;
+ /**
+ * SlaveConfig
+ *
+ * This class manages the configuration for io-slaves based on protocol
+ * and host. The Scheduler makes use of this class to configure the slave
+ * whenever it has to connect to a new host.
+ *
+ * You only need to use this class if you want to override specific
+ * configuration items of an io-slave when the io-slave is used by
+ * your application.
+ *
+ * Normally io-slaves are being configured by "kio_<protocol>rc"
+ * configuration files. Groups defined in such files are treated as host
+ * or domain specification. Configuration items defined in a group are
+ * only applied when the slave is connecting with a host that matches with
+ * the host and/or domain specified by the group.
+ */
+ class TDEIO_EXPORT SlaveConfig : public TQObject
+ {
+ Q_OBJECT
+ public:
+ static SlaveConfig *self();
+ ~SlaveConfig();
+ /**
+ * Configure slaves of type @p protocol by setting @p key to @p value.
+ * If @p host is specified the configuration only applies when dealing
+ * with @p host.
+ *
+ * Changes made to the slave configuration only apply to slaves
+ * used by the current process.
+ */
+ void setConfigData(const TQString &protocol, const TQString &host, const TQString &key, const TQString &value );
+
+ /**
+ * Configure slaves of type @p protocol with @p config.
+ * If @p host is specified the configuration only applies when dealing
+ * with @p host.
+ *
+ * Changes made to the slave configuration only apply to slaves
+ * used by the current process.
+ */
+ void setConfigData(const TQString &protocol, const TQString &host, const MetaData &config );
+
+ /**
+ * Query slave configuration for slaves of type @p protocol when
+ * dealing with @p host.
+ */
+ MetaData configData(const TQString &protocol, const TQString &host);
+
+ /**
+ * Query a specific configuration key for slaves of type @p protocol when
+ * dealing with @p host.
+ */
+ TQString configData(const TQString &protocol, const TQString &host, const TQString &key);
+
+ /**
+ * Undo any changes made by calls to setConfigData.
+ */
+ void reset();
+ signals:
+ /**
+ * This signal is raised when a slave of type @p protocol deals
+ * with @p host for the first time.
+ *
+ * Your application can use this signal to make some last minute
+ * configuration changes with setConfigData based on the
+ * host.
+ */
+ void configNeeded(const TQString &protocol, const TQString &host);
+ protected:
+ SlaveConfig();
+ static SlaveConfig *_self;
+ SlaveConfigPrivate *d;
+ };
+}
+
+#endif
diff --git a/tdeio/tdeio/slaveinterface.cpp b/tdeio/tdeio/slaveinterface.cpp
new file mode 100644
index 000000000..40b66c47a
--- /dev/null
+++ b/tdeio/tdeio/slaveinterface.cpp
@@ -0,0 +1,550 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "tdeio/slaveinterface.h"
+#include "tdeio/slavebase.h"
+#include "tdeio/connection.h"
+#include <errno.h>
+#include <assert.h>
+#include <kdebug.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <signal.h>
+#include <tdeio/observer.h>
+#include <kapplication.h>
+#include <dcopclient.h>
+#include <time.h>
+#include <tqtimer.h>
+
+using namespace TDEIO;
+
+
+TQDataStream &operator <<(TQDataStream &s, const TDEIO::UDSEntry &e )
+{
+ // On 32-bit platforms we send UDS_SIZE with UDS_SIZE_LARGE in front
+ // of it to carry the 32 msb. We can't send a 64 bit UDS_SIZE because
+ // that would break the compatibility of the wire-protocol with KDE 2.
+ // We do the same on 64-bit platforms in case we run in a mixed 32/64bit
+ // environment.
+
+ TQ_UINT32 size = 0;
+ TDEIO::UDSEntry::ConstIterator it = e.begin();
+ for( ; it != e.end(); ++it )
+ {
+ size++;
+ if ((*it).m_uds == TDEIO::UDS_SIZE)
+ size++;
+ }
+ s << size;
+ it = e.begin();
+ for( ; it != e.end(); ++it )
+ {
+ if ((*it).m_uds == TDEIO::UDS_SIZE)
+ {
+ TDEIO::UDSAtom a;
+ a.m_uds = TDEIO::UDS_SIZE_LARGE;
+ a.m_long = (*it).m_long >> 32;
+ s << a;
+ }
+ s << *it;
+ }
+ return s;
+}
+
+TQDataStream &operator >>(TQDataStream &s, TDEIO::UDSEntry &e )
+{
+ e.clear();
+ TQ_UINT32 size;
+ s >> size;
+
+ // On 32-bit platforms we send UDS_SIZE with UDS_SIZE_LARGE in front
+ // of it to carry the 32 msb. We can't send a 64 bit UDS_SIZE because
+ // that would break the compatibility of the wire-protocol with KDE 2.
+ // We do the same on 64-bit platforms in case we run in a mixed 32/64bit
+ // environment.
+ TQ_LLONG msb = 0;
+ for(TQ_UINT32 i = 0; i < size; i++)
+ {
+ TDEIO::UDSAtom a;
+ s >> a;
+ if (a.m_uds == TDEIO::UDS_SIZE_LARGE)
+ {
+ msb = a.m_long;
+ }
+ else
+ {
+ if (a.m_uds == TDEIO::UDS_SIZE)
+ {
+ if (a.m_long < 0)
+ a.m_long += (TQ_LLONG) 1 << 32;
+ a.m_long += msb << 32;
+ }
+ e.append(a);
+ msb = 0;
+ }
+ }
+ return s;
+}
+
+static const unsigned int max_nums = 8;
+
+class TDEIO::SlaveInterfacePrivate
+{
+public:
+ SlaveInterfacePrivate() {
+ slave_calcs_speed = false;
+ start_time.tv_sec = 0;
+ start_time.tv_usec = 0;
+ last_time = 0;
+ nums = 0;
+ filesize = 0;
+ offset = 0;
+ }
+ bool slave_calcs_speed;
+ struct timeval start_time;
+ uint nums;
+ long times[max_nums];
+ TDEIO::filesize_t sizes[max_nums];
+ size_t last_time;
+ TDEIO::filesize_t filesize, offset;
+
+ TQTimer speed_timer;
+};
+
+//////////////
+
+SlaveInterface::SlaveInterface( Connection * connection )
+{
+ m_pConnection = connection;
+ m_progressId = 0;
+
+ d = new SlaveInterfacePrivate;
+ connect(&d->speed_timer, TQT_SIGNAL(timeout()), TQT_SLOT(calcSpeed()));
+}
+
+SlaveInterface::~SlaveInterface()
+{
+ // Note: no kdDebug() here (scheduler is deleted very late)
+ m_pConnection = 0; // a bit like the "wasDeleted" of TQObject...
+
+ delete d;
+}
+
+static TDEIO::filesize_t readFilesize_t(TQDataStream &stream)
+{
+ TDEIO::filesize_t result;
+ unsigned long ul;
+ stream >> ul;
+ result = ul;
+ if (stream.atEnd())
+ return result;
+ stream >> ul;
+ result += ((TDEIO::filesize_t)ul) << 32;
+ return result;
+}
+
+
+bool SlaveInterface::dispatch()
+{
+ assert( m_pConnection );
+
+ int cmd;
+ TQByteArray data;
+
+ if (m_pConnection->read( &cmd, data ) == -1)
+ return false;
+
+ return dispatch( cmd, data );
+}
+
+void SlaveInterface::calcSpeed()
+{
+ if (d->slave_calcs_speed) {
+ d->speed_timer.stop();
+ return;
+ }
+
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+
+ long diff = ((tv.tv_sec - d->start_time.tv_sec) * 1000000 +
+ tv.tv_usec - d->start_time.tv_usec) / 1000;
+ if (diff - d->last_time >= 900) {
+ d->last_time = diff;
+ if (d->nums == max_nums) {
+ // let's hope gcc can optimize that well enough
+ // otherwise I'd try memcpy :)
+ for (unsigned int i = 1; i < max_nums; ++i) {
+ d->times[i-1] = d->times[i];
+ d->sizes[i-1] = d->sizes[i];
+ }
+ d->nums--;
+ }
+ d->times[d->nums] = diff;
+ d->sizes[d->nums++] = d->filesize - d->offset;
+
+ TDEIO::filesize_t lspeed = 1000 * (d->sizes[d->nums-1] - d->sizes[0]) / (d->times[d->nums-1] - d->times[0]);
+
+// kdDebug() << "proceeed " << (long)d->filesize << " " << diff << " "
+// << long(d->sizes[d->nums-1] - d->sizes[0]) << " "
+// << d->times[d->nums-1] - d->times[0] << " "
+// << long(lspeed) << " " << double(d->filesize) / diff
+// << " " << convertSize(lspeed) << " "
+// << convertSize(long(double(d->filesize) / diff) * 1000) << " "
+// << endl ;
+
+ if (!lspeed) {
+ d->nums = 1;
+ d->times[0] = diff;
+ d->sizes[0] = d->filesize - d->offset;
+ }
+ emit speed(lspeed);
+ }
+}
+
+bool SlaveInterface::dispatch( int _cmd, const TQByteArray &rawdata )
+{
+ //kdDebug(7007) << "dispatch " << _cmd << endl;
+
+ TQDataStream stream( rawdata, IO_ReadOnly );
+
+ TQString str1;
+ TQ_INT32 i;
+ TQ_INT8 b;
+ TQ_UINT32 ul;
+
+ switch( _cmd ) {
+ case MSG_DATA:
+ emit data( rawdata );
+ break;
+ case MSG_DATA_REQ:
+ emit dataReq();
+ break;
+ case MSG_FINISHED:
+ //kdDebug(7007) << "Finished [this = " << this << "]" << endl;
+ d->offset = 0;
+ d->speed_timer.stop();
+ emit finished();
+ break;
+ case MSG_STAT_ENTRY:
+ {
+ UDSEntry entry;
+ stream >> entry;
+ emit statEntry(entry);
+ }
+ break;
+ case MSG_LIST_ENTRIES:
+ {
+ TQ_UINT32 count;
+ stream >> count;
+
+ UDSEntryList list;
+ UDSEntry entry;
+ for (uint i = 0; i < count; i++) {
+ stream >> entry;
+ list.append(entry);
+ }
+ emit listEntries(list);
+
+ }
+ break;
+ case MSG_RESUME: // From the put job
+ {
+ d->offset = readFilesize_t(stream);
+ emit canResume( d->offset );
+ }
+ break;
+ case MSG_CANRESUME: // From the get job
+ d->filesize = d->offset;
+ emit canResume(0); // the arg doesn't matter
+ break;
+ case MSG_ERROR:
+ stream >> i >> str1;
+ kdDebug(7007) << "error " << i << " " << str1 << endl;
+ emit error( i, str1 );
+ break;
+ case MSG_SLAVE_STATUS:
+ {
+ pid_t pid;
+ TQCString protocol;
+ stream >> pid >> protocol >> str1 >> b;
+ emit slaveStatus(pid, protocol, str1, (b != 0));
+ }
+ break;
+ case MSG_CONNECTED:
+ emit connected();
+ break;
+
+ case INF_TOTAL_SIZE:
+ {
+ TDEIO::filesize_t size = readFilesize_t(stream);
+ gettimeofday(&d->start_time, 0);
+ d->last_time = 0;
+ d->filesize = d->offset;
+ d->sizes[0] = d->filesize - d->offset;
+ d->times[0] = 0;
+ d->nums = 1;
+ d->speed_timer.start(1000);
+ d->slave_calcs_speed = false;
+ emit totalSize( size );
+ }
+ break;
+ case INF_PROCESSED_SIZE:
+ {
+ TDEIO::filesize_t size = readFilesize_t(stream);
+ emit processedSize( size );
+ d->filesize = size;
+ }
+ break;
+ case INF_SPEED:
+ stream >> ul;
+ d->slave_calcs_speed = true;
+ d->speed_timer.stop();
+
+ emit speed( ul );
+ break;
+ case INF_GETTING_FILE:
+ break;
+ case INF_ERROR_PAGE:
+ emit errorPage();
+ break;
+ case INF_REDIRECTION:
+ {
+ KURL url;
+ stream >> url;
+
+ emit redirection( url );
+ }
+ break;
+ case INF_MIME_TYPE:
+ stream >> str1;
+
+ emit mimeType( str1 );
+ if (!m_pConnection->suspended())
+ m_pConnection->sendnow( CMD_NONE, TQByteArray() );
+ break;
+ case INF_WARNING:
+ stream >> str1;
+
+ emit warning( str1 );
+ break;
+ case INF_NEED_PASSWD: {
+ AuthInfo info;
+ stream >> info;
+ openPassDlg( info );
+ break;
+ }
+ case INF_MESSAGEBOX: {
+ kdDebug(7007) << "needs a msg box" << endl;
+ TQString text, caption, buttonYes, buttonNo, dontAskAgainName;
+ int type;
+ stream >> type >> text >> caption >> buttonYes >> buttonNo;
+ if (stream.atEnd())
+ messageBox(type, text, caption, buttonYes, buttonNo);
+ else {
+ stream >> dontAskAgainName;
+ messageBox(type, text, caption, buttonYes, buttonNo, dontAskAgainName);
+ }
+ break;
+ }
+ case INF_INFOMESSAGE: {
+ TQString msg;
+ stream >> msg;
+ infoMessage(msg);
+ break;
+ }
+ case INF_META_DATA: {
+ MetaData meta_data;
+ stream >> meta_data;
+ metaData(meta_data);
+ break;
+ }
+ case INF_LOCALURL: {
+ TQ_INT8 islocal;
+ KURL url;
+ stream >> islocal >> url;
+ emit localURL( url, islocal );
+ break;
+ }
+ case MSG_NET_REQUEST: {
+ TQString host;
+ TQString slaveid;
+ stream >> host >> slaveid;
+ requestNetwork(host, slaveid);
+ break;
+ }
+ case MSG_NET_DROP: {
+ TQString host;
+ TQString slaveid;
+ stream >> host >> slaveid;
+ dropNetwork(host, slaveid);
+ break;
+ }
+ case MSG_NEED_SUBURL_DATA: {
+ emit needSubURLData();
+ break;
+ }
+ case MSG_AUTH_KEY: {
+ bool keep;
+ TQCString key, group;
+ stream >> key >> group >> keep;
+ kdDebug(7007) << "Got auth-key: " << key << endl
+ << " group-key: " << group << endl
+ << " keep password: " << keep << endl;
+ emit authorizationKey( key, group, keep );
+ break;
+ }
+ case MSG_DEL_AUTH_KEY: {
+ TQCString key;
+ stream >> key;
+ kdDebug(7007) << "Delete auth-key: " << key << endl;
+ emit delAuthorization( key );
+ }
+ default:
+ kdWarning(7007) << "Slave sends unknown command (" << _cmd << "), dropping slave" << endl;
+ return false;
+ }
+ return true;
+}
+
+void SlaveInterface::setOffset( TDEIO::filesize_t o)
+{
+ d->offset = o;
+}
+
+TDEIO::filesize_t SlaveInterface::offset() const { return d->offset; }
+
+void SlaveInterface::requestNetwork(const TQString &host, const TQString &slaveid)
+{
+ kdDebug(7007) << "requestNetwork " << host << slaveid << endl;
+ TQByteArray packedArgs;
+ TQDataStream stream( packedArgs, IO_WriteOnly );
+ stream << true;
+ m_pConnection->sendnow( INF_NETWORK_STATUS, packedArgs );
+}
+
+void SlaveInterface::dropNetwork(const TQString &host, const TQString &slaveid)
+{
+ kdDebug(7007) << "dropNetwork " << host << slaveid << endl;
+}
+
+void SlaveInterface::sendResumeAnswer( bool resume )
+{
+ kdDebug(7007) << "SlaveInterface::sendResumeAnswer ok for resuming :" << resume << endl;
+ m_pConnection->sendnow( resume ? CMD_RESUMEANSWER : CMD_NONE, TQByteArray() );
+}
+
+void SlaveInterface::openPassDlg( const TQString& prompt, const TQString& user, bool readOnly )
+{
+ AuthInfo info;
+ info.prompt = prompt;
+ info.username = user;
+ info.readOnly = readOnly;
+ openPassDlg( info );
+}
+
+void SlaveInterface::openPassDlg( const TQString& prompt, const TQString& user,
+ const TQString& caption, const TQString& comment,
+ const TQString& label, bool readOnly )
+{
+ AuthInfo info;
+ info.prompt = prompt;
+ info.username = user;
+ info.caption = caption;
+ info.comment = comment;
+ info.commentLabel = label;
+ info.readOnly = readOnly;
+ openPassDlg( info );
+}
+
+void SlaveInterface::openPassDlg( AuthInfo& info )
+{
+ kdDebug(7007) << "SlaveInterface::openPassDlg: "
+ << "User= " << info.username
+ << ", Message= " << info.prompt << endl;
+ bool result = Observer::self()->openPassDlg( info );
+ if ( m_pConnection )
+ {
+ TQByteArray data;
+ TQDataStream stream( data, IO_WriteOnly );
+ if ( result )
+ {
+ stream << info;
+ kdDebug(7007) << "SlaveInterface:::openPassDlg got: "
+ << "User= " << info.username
+ << ", Password= [hidden]" << endl;
+ m_pConnection->sendnow( CMD_USERPASS, data );
+ }
+ else
+ m_pConnection->sendnow( CMD_NONE, data );
+ }
+}
+
+void SlaveInterface::messageBox( int type, const TQString &text, const TQString &_caption,
+ const TQString &buttonYes, const TQString &buttonNo )
+{
+ messageBox( type, text, _caption, buttonYes, buttonNo, TQString::null );
+}
+
+void SlaveInterface::messageBox( int type, const TQString &text, const TQString &_caption,
+ const TQString &buttonYes, const TQString &buttonNo, const TQString &dontAskAgainName )
+{
+ kdDebug(7007) << "messageBox " << type << " " << text << " - " << _caption << " " << dontAskAgainName << endl;
+ TQByteArray packedArgs;
+ TQDataStream stream( packedArgs, IO_WriteOnly );
+
+ TQString caption( _caption );
+ if ( type == TDEIO::SlaveBase::SSLMessageBox )
+ caption = TQString::fromUtf8(kapp->dcopClient()->appId()); // hack, see observer.cpp
+
+ emit needProgressId();
+ kdDebug(7007) << "SlaveInterface::messageBox m_progressId=" << m_progressId << endl;
+ TQGuardedPtr<SlaveInterface> me = this;
+ m_pConnection->suspend();
+ int result = Observer::/*self()->*/messageBox( m_progressId, type, text, caption, buttonYes, buttonNo, dontAskAgainName );
+ if ( me && m_pConnection ) // Don't do anything if deleted meanwhile
+ {
+ m_pConnection->resume();
+ kdDebug(7007) << this << " SlaveInterface result=" << result << endl;
+ stream << result;
+ m_pConnection->sendnow( CMD_MESSAGEBOXANSWER, packedArgs );
+ }
+}
+
+// No longer used.
+// Remove in KDE 4.0
+void SlaveInterface::sigpipe_handler(int)
+{
+ int saved_errno = errno;
+ // Using kdDebug from a signal handler is not a good idea.
+#ifndef NDEBUG
+ char msg[1000];
+ sprintf(msg, "*** SIGPIPE *** (ignored, pid = %ld)\n", (long) getpid());
+ write(2, msg, strlen(msg));
+#endif
+
+ // Do nothing.
+ // dispatch will return false and that will trigger ERR_SLAVE_DIED in slave.cpp
+ errno = saved_errno;
+}
+
+void SlaveInterface::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+#include "slaveinterface.moc"
diff --git a/tdeio/tdeio/slaveinterface.h b/tdeio/tdeio/slaveinterface.h
new file mode 100644
index 000000000..a8992ee65
--- /dev/null
+++ b/tdeio/tdeio/slaveinterface.h
@@ -0,0 +1,290 @@
+/* This file is part of the KDE project
+ Copyright (C) 2000 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __kio_slaveinterface_h
+#define __kio_slaveinterface_h
+
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <tqobject.h>
+
+#include <kurl.h>
+#include <tdeio/global.h>
+#include <tdeio/authinfo.h>
+#include <kdatastream.h>
+
+namespace TDEIO {
+
+class Connection;
+// better there is one ...
+class SlaveInterfacePrivate;
+
+ // Definition of enum Command has been moved to global.h
+
+ /**
+ * Identifiers for KIO informational messages.
+ */
+ enum Info {
+ INF_TOTAL_SIZE = 10,
+ INF_PROCESSED_SIZE = 11,
+ INF_SPEED,
+ INF_REDIRECTION = 20,
+ INF_MIME_TYPE = 21,
+ INF_ERROR_PAGE = 22,
+ INF_WARNING = 23,
+ INF_GETTING_FILE, // Deprecated
+ INF_NEED_PASSWD = 25,
+ INF_INFOMESSAGE,
+ INF_META_DATA,
+ INF_NETWORK_STATUS,
+ INF_MESSAGEBOX,
+ INF_LOCALURL
+ // add new ones here once a release is done, to avoid breaking binary compatibility
+ };
+
+ /**
+ * Identifiers for KIO data messages.
+ */
+ enum Message {
+ MSG_DATA = 100,
+ MSG_DATA_REQ,
+ MSG_ERROR,
+ MSG_CONNECTED,
+ MSG_FINISHED,
+ MSG_STAT_ENTRY,
+ MSG_LIST_ENTRIES,
+ MSG_RENAMED, // unused
+ MSG_RESUME,
+ MSG_SLAVE_STATUS,
+ MSG_SLAVE_ACK,
+ MSG_NET_REQUEST,
+ MSG_NET_DROP,
+ MSG_NEED_SUBURL_DATA,
+ MSG_CANRESUME,
+ MSG_AUTH_KEY, // deprecated.
+ MSG_DEL_AUTH_KEY // deprecated.
+ // add new ones here once a release is done, to avoid breaking binary compatibility
+ };
+
+/**
+ * There are two classes that specifies the protocol between application
+ * (TDEIO::Job) and tdeioslave. SlaveInterface is the class to use on the application
+ * end, SlaveBase is the one to use on the slave end.
+ *
+ * A call to foo() results in a call to slotFoo() on the other end.
+ */
+class TDEIO_EXPORT SlaveInterface : public TQObject
+{
+ Q_OBJECT
+
+public:
+ SlaveInterface( Connection *connection );
+ virtual ~SlaveInterface();
+
+ void setConnection( Connection* connection ) { m_pConnection = connection; }
+ Connection *connection() const { return m_pConnection; }
+
+ void setProgressId( int id ) { m_progressId = id; }
+ int progressId() const { return m_progressId; }
+
+ /** Send our answer to the MSG_RESUME (canResume) request
+ * (to tell the "put" job whether to resume or not)
+ */
+ void sendResumeAnswer( bool resume );
+
+ void setOffset( TDEIO::filesize_t offset );
+ TDEIO::filesize_t offset() const;
+
+signals:
+ ///////////
+ // Messages sent by the slave
+ ///////////
+
+ void data( const TQByteArray & );
+ void dataReq( );
+ void error( int , const TQString & );
+ void connected();
+ void finished();
+ void slaveStatus(pid_t, const TQCString &, const TQString &, bool);
+ void listEntries( const TDEIO::UDSEntryList& );
+ void statEntry( const TDEIO::UDSEntry& );
+ void needSubURLData();
+ void needProgressId();
+
+ void canResume( TDEIO::filesize_t ) ;
+
+ ///////////
+ // Info sent by the slave
+ //////////
+ void metaData( const TDEIO::MetaData & );
+ void totalSize( TDEIO::filesize_t ) ;
+ void processedSize( TDEIO::filesize_t ) ;
+ void redirection( const KURL& ) ;
+ void localURL( const KURL&, bool ) ;
+
+ void speed( unsigned long ) ;
+ void errorPage() ;
+ void mimeType( const TQString & ) ;
+ void warning( const TQString & ) ;
+ void infoMessage( const TQString & ) ;
+ void connectFinished();
+
+ /**
+ * @deprecated. Obsolete as of 3.1. Replaced by kpassword, a kded module.
+ */
+ void authorizationKey( const TQCString&, const TQCString&, bool );
+
+ /**
+ * @deprecated. Obsolete as of 3.1. Replaced by kpassword, a kded module.
+ */
+ void delAuthorization( const TQCString& grpkey );
+
+protected:
+ /////////////////
+ // Dispatching
+ ////////////////
+
+ virtual bool dispatch();
+ virtual bool dispatch( int _cmd, const TQByteArray &data );
+
+ /**
+ * Prompt the user for authrization info (login & password).
+ *
+ * Use this function to request authorization info from the
+ * the end user. For example to open an empty password dialog
+ * using default values:
+ *
+ * \code
+ * TDEIO::AuthInfo authInfo;
+ * bool result = openPassDlg( authInfo );
+ * if ( result )
+ * {
+ * printf( "Username: %s", result.username.latin1() );
+ * printf( "Username: %s", result.username.latin1() );
+ * }
+ * \endcode
+ *
+ * You can also pre-set some values like the username before hand
+ * if it is known as well as the comment and caption to be displayed:
+ *
+ * \code
+ * authInfo.comment= "Enter username and password to access acmeone";
+ * authInfo.caption= "Acme Password Dialog";
+ * authInfo.username= "Wily E. kaiody";
+ * bool result = openPassDlg( authInfo );
+ * if ( result )
+ * {
+ * printf( "Username: %s", result.username.latin1() );
+ * printf( "Username: %s", result.username.latin1() );
+ * }
+ * \endcode
+ *
+ * NOTE: A call to this function can also fail and result
+ * in a return value of @p false, if the UIServer could not
+ * be started for whatever reason.
+ *
+ * @param info See AuthInfo.
+ * @return true if user clicks on "OK", false otherwsie.
+ */
+ void openPassDlg( TDEIO::AuthInfo& info );
+
+ /**
+ * @deprecated. Use openPassDlg( AuthInfo& ) instead.
+ */
+ void openPassDlg( const TQString& prompt, const TQString& user,
+ const TQString& caption, const TQString& comment,
+ const TQString& label, bool readOnly ) KDE_DEPRECATED;
+
+ /**
+ * @deprecated. Use openPassDlg( AuthInfo& ) instead.
+ */
+ void openPassDlg( const TQString& prompt, const TQString& user, bool readOnly ) KDE_DEPRECATED;
+
+ void messageBox( int type, const TQString &text, const TQString &caption,
+ const TQString &buttonYes, const TQString &buttonNo );
+
+ /**
+ * @since 3.3
+ */
+ void messageBox( int type, const TQString &text, const TQString &caption,
+ const TQString &buttonYes, const TQString &buttonNo, const TQString &dontAskAgainName );
+
+ // I need to identify the slaves
+ void requestNetwork( const TQString &, const TQString &);
+ void dropNetwork( const TQString &, const TQString &);
+
+ /**
+ * @internal
+ * KDE 4.0: Remove
+ */
+ static void sigpipe_handler(int);
+
+protected slots:
+ void calcSpeed();
+
+protected:
+ Connection * m_pConnection;
+
+private:
+ int m_progressId;
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ SlaveInterfacePrivate *d;
+};
+
+}
+
+inline TQDataStream &operator >>(TQDataStream &s, TDEIO::UDSAtom &a )
+{
+ TQ_INT32 l;
+ s >> a.m_uds;
+
+ if ( a.m_uds & TDEIO::UDS_LONG ) {
+ s >> l;
+ a.m_long = l;
+ a.m_str = TQString::null;
+ } else if ( a.m_uds & TDEIO::UDS_STRING ) {
+ s >> a.m_str;
+ a.m_long = 0;
+ } else {} // DIE!
+ // assert( 0 );
+
+ return s;
+}
+
+inline TQDataStream &operator <<(TQDataStream &s, const TDEIO::UDSAtom &a )
+{
+ s << a.m_uds;
+
+ if ( a.m_uds & TDEIO::UDS_LONG )
+ s << (TQ_INT32) a.m_long;
+ else if ( a.m_uds & TDEIO::UDS_STRING )
+ s << a.m_str;
+ else {} // DIE!
+ // assert( 0 );
+
+ return s;
+}
+
+TDEIO_EXPORT TQDataStream &operator <<(TQDataStream &s, const TDEIO::UDSEntry &e );
+TDEIO_EXPORT TQDataStream &operator >>(TQDataStream &s, TDEIO::UDSEntry &e );
+
+#endif
diff --git a/tdeio/tdeio/statusbarprogress.cpp b/tdeio/tdeio/statusbarprogress.cpp
new file mode 100644
index 000000000..66517ca03
--- /dev/null
+++ b/tdeio/tdeio/statusbarprogress.cpp
@@ -0,0 +1,166 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Matej Koss <koss@miesto.sk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <tqtooltip.h>
+#include <tqlayout.h>
+#include <tqwidgetstack.h>
+#include <tqpushbutton.h>
+#include <tqlabel.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kdebug.h>
+#include <kprogress.h>
+
+#include "jobclasses.h"
+#include "statusbarprogress.h"
+
+namespace TDEIO {
+
+StatusbarProgress::StatusbarProgress( TQWidget* parent, bool button )
+ : ProgressBase( parent ) {
+
+ m_bShowButton = button;
+
+ // only clean this dialog
+ setOnlyClean(true);
+ // TODO : is this really needed ?
+ setStopOnClose(false);
+
+ int w = fontMetrics().width( " 999.9 kB/s 00:00:01 " ) + 8;
+ box = new TQHBoxLayout( this, 0, 0 );
+
+ m_pButton = new TQPushButton( "X", this );
+ box->addWidget( m_pButton );
+ stack = new TQWidgetStack( this );
+ box->addWidget( stack );
+ connect( m_pButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotStop() ) );
+
+ m_pProgressBar = new KProgress( this );
+ m_pProgressBar->setFrameStyle( TQFrame::Box | TQFrame::Raised );
+ m_pProgressBar->setLineWidth( 1 );
+ m_pProgressBar->setBackgroundMode( TQWidget::PaletteBackground );
+ m_pProgressBar->installEventFilter( this );
+ m_pProgressBar->setMinimumWidth( w );
+ stack->addWidget( m_pProgressBar, 1 );
+
+ m_pLabel = new TQLabel( "", this );
+ m_pLabel->setAlignment( AlignHCenter | AlignVCenter );
+ m_pLabel->installEventFilter( this );
+ m_pLabel->setMinimumWidth( w );
+ stack->addWidget( m_pLabel, 2 );
+ setMinimumSize( sizeHint() );
+
+ mode = None;
+ setMode();
+}
+
+
+void StatusbarProgress::setJob( TDEIO::Job *job )
+{
+ ProgressBase::setJob( job );
+
+ mode = Progress;
+ setMode();
+}
+
+
+void StatusbarProgress::setMode() {
+ switch ( mode ) {
+ case None:
+ if ( m_bShowButton ) {
+ m_pButton->hide();
+ }
+ stack->hide();
+ break;
+
+ case Label:
+ if ( m_bShowButton ) {
+ m_pButton->show();
+ }
+ stack->show();
+ stack->raiseWidget( m_pLabel );
+ break;
+
+ case Progress:
+ if ( m_bShowButton ) {
+ m_pButton->show();
+ }
+ stack->show();
+ stack->raiseWidget( m_pProgressBar );
+ break;
+ }
+}
+
+
+void StatusbarProgress::slotClean() {
+ // we don't want to delete this widget, only clean
+ m_pProgressBar->setValue( 0 );
+ m_pLabel->clear();
+
+ mode = None;
+ setMode();
+}
+
+
+void StatusbarProgress::slotTotalSize( TDEIO::Job*, TDEIO::filesize_t size ) {
+ m_iTotalSize = size; // size is measured in bytes
+}
+
+void StatusbarProgress::slotPercent( TDEIO::Job*, unsigned long percent ) {
+ m_pProgressBar->setValue( percent );
+}
+
+
+void StatusbarProgress::slotSpeed( TDEIO::Job*, unsigned long speed ) {
+ if ( speed == 0 ) { // spped is measured in bytes-per-second
+ m_pLabel->setText( i18n( " Stalled ") );
+ } else {
+ m_pLabel->setText( i18n( " %1/s ").arg( TDEIO::convertSize( speed )) );
+ }
+}
+
+
+bool StatusbarProgress::eventFilter( TQObject *, TQEvent *ev ) {
+ if ( ! m_pJob ) { // don't react when there isn't any job doing IO
+ return true;
+ }
+
+ if ( ev->type() == TQEvent::MouseButtonPress ) {
+ TQMouseEvent *e = (TQMouseEvent*)ev;
+
+ if ( e->button() == Qt::LeftButton ) { // toggle view on left mouse button
+ if ( mode == Label ) {
+ mode = Progress;
+ } else if ( mode == Progress ) {
+ mode = Label;
+ }
+ setMode();
+ return true;
+
+ }
+ }
+
+ return false;
+}
+
+void StatusbarProgress::virtual_hook( int id, void* data )
+{ ProgressBase::virtual_hook( id, data ); }
+
+} /* namespace */
+#include "statusbarprogress.moc"
diff --git a/tdeio/tdeio/statusbarprogress.h b/tdeio/tdeio/statusbarprogress.h
new file mode 100644
index 000000000..d1d591fbe
--- /dev/null
+++ b/tdeio/tdeio/statusbarprogress.h
@@ -0,0 +1,112 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Matej Koss <koss@miesto.sk>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __statusbarprogress_h__
+#define __statusbarprogress_h__
+
+#include "progressbase.h"
+
+class TQWidgetStack;
+class TQBoxLayout;
+class TQPushButton;
+class TQLabel;
+class KProgress;
+
+namespace TDEIO {
+
+class Job;
+
+/**
+* This is a special IO progress widget.
+*
+* Similarly to DefaultProgress,
+* it's purpose is to show a progress of the IO operation.
+*
+* Instead of creating a separate window, this is only a widget that can be
+* easily embedded in a statusbar.
+*
+* Usage of StatusbarProgress is little different.
+* This dialog will be a part of some application.
+* \code
+* // create a dialog
+* StatusbarProgress *statusProgress;
+* statusProgress = new StatusbarProgress( statusBar() );
+* statusBar()->insertWidget( statusProgress, statusProgress->width() , 0 );
+* ...
+* // create job and connect it to the progress
+* CopyJob* job = TDEIO::copy(...);
+* statusProgress->setJob( job );
+* ...
+* \endcode
+*
+* @short IO progress widget for embedding in a statusbar.
+* @author Matej Koss <koss@miesto.sk>
+*/
+class TDEIO_EXPORT StatusbarProgress : public ProgressBase {
+
+ Q_OBJECT
+
+public:
+
+ /**
+ * Creates a new StatusbarProgress.
+ * @param parent the parent of this widget
+ * @param button true to add an abort button. The button will be
+ * connected to ProgressBase::slotStop()
+ */
+ StatusbarProgress( TQWidget* parent, bool button = true );
+ ~StatusbarProgress() {}
+
+ /**
+ * Sets the job to monitor.
+ * @param job the job to monitor
+ */
+ void setJob( TDEIO::Job *job );
+
+public slots:
+ virtual void slotClean();
+ virtual void slotTotalSize( TDEIO::Job* job, TDEIO::filesize_t size );
+ virtual void slotPercent( TDEIO::Job* job, unsigned long percent );
+ virtual void slotSpeed( TDEIO::Job* job, unsigned long speed );
+
+protected:
+ KProgress* m_pProgressBar;
+ TQLabel* m_pLabel;
+ TQPushButton* m_pButton;
+
+ TDEIO::filesize_t m_iTotalSize;
+
+ enum Mode { None, Label, Progress };
+
+ uint mode;
+ bool m_bShowButton;
+
+ void setMode();
+
+ virtual bool eventFilter( TQObject *, TQEvent * );
+ TQBoxLayout *box;
+ TQWidgetStack *stack;
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class StatusbarProgressPrivate* d;
+};
+
+} /* namespace */
+
+#endif // __statusbarprogress_h__
diff --git a/tdeio/tdeio/tcpslavebase.cpp b/tdeio/tdeio/tcpslavebase.cpp
new file mode 100644
index 000000000..2b7df9d7b
--- /dev/null
+++ b/tdeio/tdeio/tcpslavebase.cpp
@@ -0,0 +1,1343 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2000 Alex Zepeda <zipzippy@sonic.net
+ * Copyright (C) 2001-2003 George Staikos <staikos@kde.org>
+ * Copyright (C) 2001 Dawit Alemayehu <adawit@kde.org>
+ *
+ * This file is part of the KDE project
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <time.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <ksocks.h>
+#include <kdebug.h>
+#include <ksslall.h>
+#include <ksslcertdlg.h>
+#include <kmessagebox.h>
+#ifndef Q_WS_WIN //temporary
+#include <kresolver.h>
+#endif
+
+#include <klocale.h>
+#include <dcopclient.h>
+#include <tqcstring.h>
+#include <tqdatastream.h>
+
+#include <kapplication.h>
+
+#include <kprotocolmanager.h>
+#include <kde_file.h>
+
+#include "tdeio/tcpslavebase.h"
+
+using namespace TDEIO;
+
+class TCPSlaveBase::TcpSlaveBasePrivate
+{
+public:
+
+ TcpSlaveBasePrivate() : rblockSz(256), militantSSL(false), userAborted(false) {}
+ ~TcpSlaveBasePrivate() {}
+
+ KSSL *kssl;
+ bool usingTLS;
+ KSSLCertificateCache *cc;
+ TQString host;
+ TQString realHost;
+ TQString ip;
+ DCOPClient *dcc;
+ KSSLPKCS12 *pkcs;
+
+ int status;
+ int timeout;
+ int rblockSz; // Size for reading blocks in readLine()
+ bool block;
+ bool useSSLTunneling;
+ bool needSSLHandShake;
+ bool militantSSL; // If true, we just drop a connection silently
+ // if SSL certificate check fails in any way.
+ bool userAborted;
+ MetaData savedMetaData;
+};
+
+
+TCPSlaveBase::TCPSlaveBase(unsigned short int defaultPort,
+ const TQCString &protocol,
+ const TQCString &poolSocket,
+ const TQCString &appSocket)
+ :SlaveBase (protocol, poolSocket, appSocket),
+ m_iSock(-1),
+ m_iDefaultPort(defaultPort),
+ m_sServiceName(protocol),
+ fp(0)
+{
+ // We have to have two constructors, so don't add anything
+ // else in here. Put it in doConstructorStuff() instead.
+ doConstructorStuff();
+ m_bIsSSL = false;
+}
+
+TCPSlaveBase::TCPSlaveBase(unsigned short int defaultPort,
+ const TQCString &protocol,
+ const TQCString &poolSocket,
+ const TQCString &appSocket,
+ bool useSSL)
+ :SlaveBase (protocol, poolSocket, appSocket),
+ m_iSock(-1),
+ m_bIsSSL(useSSL),
+ m_iDefaultPort(defaultPort),
+ m_sServiceName(protocol),
+ fp(0)
+{
+ doConstructorStuff();
+ if (useSSL)
+ m_bIsSSL = initializeSSL();
+}
+
+// The constructor procedures go here now.
+void TCPSlaveBase::doConstructorStuff()
+{
+ d = new TcpSlaveBasePrivate;
+ d->kssl = 0L;
+ d->ip = "";
+ d->cc = 0L;
+ d->usingTLS = false;
+ d->dcc = 0L;
+ d->pkcs = 0L;
+ d->status = -1;
+ d->timeout = KProtocolManager::connectTimeout();
+ d->block = false;
+ d->useSSLTunneling = false;
+}
+
+TCPSlaveBase::~TCPSlaveBase()
+{
+ cleanSSL();
+ if (d->usingTLS) delete d->kssl;
+ if (d->dcc) delete d->dcc;
+ if (d->pkcs) delete d->pkcs;
+ delete d;
+}
+
+ssize_t TCPSlaveBase::write(const void *data, ssize_t len)
+{
+#ifdef Q_OS_UNIX
+ if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling )
+ {
+ if ( d->needSSLHandShake )
+ (void) doSSLHandShake( true );
+ return d->kssl->write(data, len);
+ }
+ return KSocks::self()->write(m_iSock, data, len);
+#else
+ return 0;
+#endif
+}
+
+ssize_t TCPSlaveBase::read(void *data, ssize_t len)
+{
+#ifdef Q_OS_UNIX
+ if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling )
+ {
+ if ( d->needSSLHandShake )
+ (void) doSSLHandShake( true );
+ return d->kssl->read(data, len);
+ }
+ return KSocks::self()->read(m_iSock, data, len);
+#else
+ return 0;
+#endif
+}
+
+
+void TCPSlaveBase::setBlockSize(int sz)
+{
+ if (sz <= 0)
+ sz = 1;
+
+ d->rblockSz = sz;
+}
+
+
+ssize_t TCPSlaveBase::readLine(char *data, ssize_t len)
+{
+// Optimization:
+// It's small, but it probably results in a gain on very high
+// speed connections. I moved 3 if statements out of the while loop
+// so that the while loop is as small as possible. (GS)
+
+ // let's not segfault!
+ if (!data)
+ return -1;
+
+ char tmpbuf[1024]; // 1kb temporary buffer for peeking
+ *data = 0;
+ ssize_t clen = 0;
+ char *buf = data;
+ int rc = 0;
+
+if ((m_bIsSSL || d->usingTLS) && !d->useSSLTunneling) { // SSL CASE
+ if ( d->needSSLHandShake )
+ (void) doSSLHandShake( true );
+
+ while (clen < len-1) {
+ rc = d->kssl->pending();
+ if (rc > 0) { // Read a chunk
+ int bytes = rc;
+ if (bytes > d->rblockSz)
+ bytes = d->rblockSz;
+
+ rc = d->kssl->peek(tmpbuf, bytes);
+ if (rc <= 0) {
+ // FIXME: this doesn't cover rc == 0 case
+ return -1;
+ }
+
+ bytes = rc; // in case it contains no \n
+ for (int i = 0; i < rc; i++) {
+ if (tmpbuf[i] == '\n') {
+ bytes = i+1;
+ break;
+ }
+ }
+
+ if (bytes+clen >= len) // don't read too much!
+ bytes = len - clen - 1;
+
+ rc = d->kssl->read(buf, bytes);
+ if (rc > 0) {
+ clen += rc;
+ buf += (rc-1);
+ if (*buf++ == '\n')
+ break;
+ } else {
+ // FIXME: different case if rc == 0;
+ return -1;
+ }
+ } else { // Read a byte
+ rc = d->kssl->read(buf, 1);
+ if (rc <= 0) {
+ return -1;
+ // hm rc = 0 then
+ // SSL_read says to call SSL_get_error to see if
+ // this was an error. FIXME
+ } else {
+ clen++;
+ if (*buf++ == '\n')
+ break;
+ }
+ }
+ }
+} else { // NON SSL CASE
+ while (clen < len-1) {
+#ifdef Q_OS_UNIX
+ rc = KSocks::self()->read(m_iSock, buf, 1);
+#else
+ rc = 0;
+#endif
+ if (rc <= 0) {
+ // FIXME: this doesn't cover rc == 0 case
+ return -1;
+ } else {
+ clen++;
+ if (*buf++ == '\n')
+ break;
+ }
+ }
+}
+
+ // Both cases fall through to here
+ *buf = 0;
+return clen;
+}
+
+unsigned short int TCPSlaveBase::port(unsigned short int _p)
+{
+ unsigned short int p = _p;
+
+ if (_p <= 0)
+ {
+ p = m_iDefaultPort;
+ }
+
+ return p;
+}
+
+// This function is simply a wrapper to establish the connection
+// to the server. It's a bit more complicated than ::connect
+// because we first have to check to see if the user specified
+// a port, and if so use it, otherwise we check to see if there
+// is a port specified in /etc/services, and if so use that
+// otherwise as a last resort use the supplied default port.
+bool TCPSlaveBase::connectToHost( const TQString &host,
+ unsigned int _port,
+ bool sendError )
+{
+#ifdef Q_OS_UNIX
+ unsigned short int p;
+ KExtendedSocket ks;
+
+ d->userAborted = false;
+
+ // - leaving SSL - warn before we even connect
+ if (metaData("main_frame_request") == "TRUE" &&
+ metaData("ssl_activate_warnings") == "TRUE" &&
+ metaData("ssl_was_in_use") == "TRUE" &&
+ !m_bIsSSL) {
+ KSSLSettings kss;
+ if (kss.warnOnLeave()) {
+ int result = messageBox( i18n("You are about to leave secure "
+ "mode. Transmissions will no "
+ "longer be encrypted.\nThis "
+ "means that a third party could "
+ "observe your data in transit."),
+ WarningContinueCancel,
+ i18n("Security Information"),
+ i18n("C&ontinue Loading"), TQString::null,
+ "WarnOnLeaveSSLMode" );
+
+ // Move this setting into KSSL instead
+ TDEConfig *config = new TDEConfig("tdeioslaverc");
+ config->setGroup("Notification Messages");
+
+ if (!config->readBoolEntry("WarnOnLeaveSSLMode", true)) {
+ config->deleteEntry("WarnOnLeaveSSLMode");
+ config->sync();
+ kss.setWarnOnLeave(false);
+ kss.save();
+ }
+ delete config;
+
+ if ( result == KMessageBox::Cancel ) {
+ d->userAborted = true;
+ return false;
+ }
+ }
+ }
+
+ d->status = -1;
+ d->host = host;
+ d->needSSLHandShake = m_bIsSSL;
+ p = port(_port);
+ ks.setAddress(host, p);
+ if ( d->timeout > -1 )
+ ks.setTimeout( d->timeout );
+
+ if (ks.connect() < 0)
+ {
+ d->status = ks.status();
+ if ( sendError )
+ {
+ if (d->status == IO_LookupError)
+ error( ERR_UNKNOWN_HOST, host);
+ else if ( d->status != -1 )
+ error( ERR_COULD_NOT_CONNECT, host);
+ }
+ return false;
+ }
+
+ m_iSock = ks.fd();
+
+ // store the IP for later
+ const TDESocketAddress *sa = ks.peerAddress();
+ if (sa)
+ d->ip = sa->nodeName();
+ else
+ d->ip = "";
+
+ ks.release(); // KExtendedSocket no longer applicable
+
+ if ( d->block != ks.blockingMode() )
+ ks.setBlockingMode( d->block );
+
+ m_iPort=p;
+
+ if (m_bIsSSL && !d->useSSLTunneling) {
+ if ( !doSSLHandShake( sendError ) )
+ return false;
+ }
+ else
+ setMetaData("ssl_in_use", "FALSE");
+
+ // Since we want to use stdio on the socket,
+ // we must fdopen it to get a file pointer,
+ // if it fails, close everything up
+ if ((fp = KDE_fdopen(m_iSock, "w+")) == 0) {
+ closeDescriptor();
+ return false;
+ }
+
+ return true;
+#else //!Q_OS_UNIX
+ return false;
+#endif //Q_OS_UNIX
+}
+
+void TCPSlaveBase::closeDescriptor()
+{
+ stopTLS();
+ if (fp) {
+ fclose(fp);
+ fp=0;
+ m_iSock=-1;
+ if (m_bIsSSL)
+ d->kssl->close();
+ }
+ if (m_iSock != -1) {
+ close(m_iSock);
+ m_iSock=-1;
+ }
+ d->ip = "";
+ d->host = "";
+}
+
+bool TCPSlaveBase::initializeSSL()
+{
+ if (m_bIsSSL) {
+ if (KSSL::doesSSLWork()) {
+ d->kssl = new KSSL;
+ return true;
+ }
+ }
+return false;
+}
+
+void TCPSlaveBase::cleanSSL()
+{
+ delete d->cc;
+
+ if (m_bIsSSL) {
+ delete d->kssl;
+ d->kssl = 0;
+ }
+ d->militantSSL = false;
+}
+
+bool TCPSlaveBase::atEnd()
+{
+ return feof(fp);
+}
+
+int TCPSlaveBase::startTLS()
+{
+ if (d->usingTLS || d->useSSLTunneling || m_bIsSSL || !KSSL::doesSSLWork())
+ return false;
+
+ d->kssl = new KSSL(false);
+ if (!d->kssl->TLSInit()) {
+ delete d->kssl;
+ return -1;
+ }
+
+ if ( !d->realHost.isEmpty() )
+ {
+ kdDebug(7029) << "Setting real hostname: " << d->realHost << endl;
+ d->kssl->setPeerHost(d->realHost);
+ } else {
+ kdDebug(7029) << "Setting real hostname: " << d->host << endl;
+ d->kssl->setPeerHost(d->host);
+ }
+
+ if (hasMetaData("ssl_session_id")) {
+ KSSLSession *s = KSSLSession::fromString(metaData("ssl_session_id"));
+ if (s) {
+ d->kssl->setSession(s);
+ delete s;
+ }
+ }
+ certificatePrompt();
+
+ int rc = d->kssl->connect(m_iSock);
+ if (rc < 0) {
+ delete d->kssl;
+ return -2;
+ }
+
+ setMetaData("ssl_session_id", d->kssl->session()->toString());
+
+ d->usingTLS = true;
+ setMetaData("ssl_in_use", "TRUE");
+
+ if (!d->kssl->reusingSession()) {
+ rc = verifyCertificate();
+ if (rc != 1) {
+ setMetaData("ssl_in_use", "FALSE");
+ d->usingTLS = false;
+ delete d->kssl;
+ return -3;
+ }
+ }
+
+ d->savedMetaData = mOutgoingMetaData;
+ return (d->usingTLS ? 1 : 0);
+}
+
+
+void TCPSlaveBase::stopTLS()
+{
+ if (d->usingTLS) {
+ delete d->kssl;
+ d->usingTLS = false;
+ setMetaData("ssl_in_use", "FALSE");
+ }
+}
+
+
+void TCPSlaveBase::setSSLMetaData() {
+ if (!(d->usingTLS || d->useSSLTunneling || m_bIsSSL))
+ return;
+
+ mOutgoingMetaData = d->savedMetaData;
+}
+
+
+bool TCPSlaveBase::canUseTLS()
+{
+ if (m_bIsSSL || d->needSSLHandShake || !KSSL::doesSSLWork())
+ return false;
+
+ KSSLSettings kss;
+ return kss.tlsv1();
+}
+
+
+void TCPSlaveBase::certificatePrompt()
+{
+TQString certname; // the cert to use this session
+bool send = false, prompt = false, save = false, forcePrompt = false;
+KSSLCertificateHome::KSSLAuthAction aa;
+
+ setMetaData("ssl_using_client_cert", "FALSE"); // we change this if needed
+
+ if (metaData("ssl_no_client_cert") == "TRUE") return;
+ forcePrompt = (metaData("ssl_force_cert_prompt") == "TRUE");
+
+ // Delete the old cert since we're certainly done with it now
+ if (d->pkcs) {
+ delete d->pkcs;
+ d->pkcs = NULL;
+ }
+
+ if (!d->kssl) return;
+
+ // Look for a general certificate
+ if (!forcePrompt) {
+ certname = KSSLCertificateHome::getDefaultCertificateName(&aa);
+ switch(aa) {
+ case KSSLCertificateHome::AuthSend:
+ send = true; prompt = false;
+ break;
+ case KSSLCertificateHome::AuthDont:
+ send = false; prompt = false;
+ certname = TQString::null;
+ break;
+ case KSSLCertificateHome::AuthPrompt:
+ send = false; prompt = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ TQString ourHost;
+ if (!d->realHost.isEmpty()) {
+ ourHost = d->realHost;
+ } else {
+ ourHost = d->host;
+ }
+
+ // Look for a certificate on a per-host basis as an override
+ TQString tmpcn = KSSLCertificateHome::getDefaultCertificateName(ourHost, &aa);
+ if (aa != KSSLCertificateHome::AuthNone) { // we must override
+ switch (aa) {
+ case KSSLCertificateHome::AuthSend:
+ send = true;
+ prompt = false;
+ certname = tmpcn;
+ break;
+ case KSSLCertificateHome::AuthDont:
+ send = false;
+ prompt = false;
+ certname = TQString::null;
+ break;
+ case KSSLCertificateHome::AuthPrompt:
+ send = false;
+ prompt = true;
+ certname = tmpcn;
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Finally, we allow the application to override anything.
+ if (hasMetaData("ssl_demand_certificate")) {
+ certname = metaData("ssl_demand_certificate");
+ if (!certname.isEmpty()) {
+ forcePrompt = false;
+ prompt = false;
+ send = true;
+ }
+ }
+
+ if (certname.isEmpty() && !prompt && !forcePrompt) return;
+
+ // Ok, we're supposed to prompt the user....
+ if (prompt || forcePrompt) {
+ TQStringList certs = KSSLCertificateHome::getCertificateList();
+
+ for (TQStringList::Iterator it = certs.begin(); it != certs.end(); ++it) {
+ KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(*it);
+ if (pkcs && (!pkcs->getCertificate() ||
+ !pkcs->getCertificate()->x509V3Extensions().certTypeSSLClient())) {
+ certs.remove(*it);
+ }
+ delete pkcs;
+ }
+
+ if (certs.isEmpty()) return; // we had nothing else, and prompt failed
+
+ if (!d->dcc) {
+ d->dcc = new DCOPClient;
+ d->dcc->attach();
+ if (!d->dcc->isApplicationRegistered("tdeio_uiserver")) {
+ TDEApplication::startServiceByDesktopPath("tdeio_uiserver.desktop",
+ TQStringList() );
+ }
+ }
+
+ TQByteArray data, retval;
+ TQCString rettype;
+ TQDataStream arg(data, IO_WriteOnly);
+ arg << ourHost;
+ arg << certs;
+ arg << metaData("window-id").toInt();
+ bool rc = d->dcc->call("tdeio_uiserver", "UIServer",
+ "showSSLCertDialog(TQString, TQStringList,int)",
+ data, rettype, retval);
+
+ if (rc && rettype == "KSSLCertDlgRet") {
+ TQDataStream retStream(retval, IO_ReadOnly);
+ KSSLCertDlgRet drc;
+ retStream >> drc;
+ if (drc.ok) {
+ send = drc.send;
+ save = drc.save;
+ certname = drc.choice;
+ }
+ }
+ }
+
+ // The user may have said to not send the certificate,
+ // but to save the choice
+ if (!send) {
+ if (save) {
+ KSSLCertificateHome::setDefaultCertificate(certname, ourHost,
+ false, false);
+ }
+ return;
+ }
+
+ // We're almost committed. If we can read the cert, we'll send it now.
+ KSSLPKCS12 *pkcs = KSSLCertificateHome::getCertificateByName(certname);
+ if (!pkcs && KSSLCertificateHome::hasCertificateByName(certname)) { // We need the password
+ TDEIO::AuthInfo ai;
+ bool first = true;
+ do {
+ ai.prompt = i18n("Enter the certificate password:");
+ ai.caption = i18n("SSL Certificate Password");
+ ai.url.setProtocol("kssl");
+ ai.url.setHost(certname);
+ ai.username = certname;
+ ai.keepPassword = true;
+
+ bool showprompt;
+ if (first)
+ showprompt = !checkCachedAuthentication(ai);
+ else
+ showprompt = true;
+ if (showprompt) {
+ if (!openPassDlg(ai, first ? TQString::null :
+ i18n("Unable to open the certificate. Try a new password?")))
+ break;
+ }
+
+ first = false;
+ pkcs = KSSLCertificateHome::getCertificateByName(certname, ai.password);
+ } while (!pkcs);
+
+ }
+
+ // If we could open the certificate, let's send it
+ if (pkcs) {
+ if (!d->kssl->setClientCertificate(pkcs)) {
+ messageBox(Information, i18n("The procedure to set the "
+ "client certificate for the session "
+ "failed."), i18n("SSL"));
+ delete pkcs; // we don't need this anymore
+ pkcs = 0L;
+ } else {
+ kdDebug(7029) << "Client SSL certificate is being used." << endl;
+ setMetaData("ssl_using_client_cert", "TRUE");
+ if (save) {
+ KSSLCertificateHome::setDefaultCertificate(certname, ourHost,
+ true, false);
+ }
+ }
+ d->pkcs = pkcs;
+ }
+}
+
+
+
+bool TCPSlaveBase::usingTLS() const
+{
+ return d->usingTLS;
+}
+
+// ### remove this for KDE4 (misses const):
+bool TCPSlaveBase::usingTLS()
+{
+ return d->usingTLS;
+}
+
+
+// Returns 0 for failed verification, -1 for rejected cert and 1 for ok
+int TCPSlaveBase::verifyCertificate()
+{
+ int rc = 0;
+ bool permacache = false;
+ bool isChild = false;
+ bool _IPmatchesCN = false;
+ int result;
+ bool doAddHost = false;
+ TQString ourHost;
+
+ if (!d->realHost.isEmpty())
+ ourHost = d->realHost;
+ else ourHost = d->host;
+
+ TQString theurl = TQString(m_sServiceName)+"://"+ourHost+":"+TQString::number(m_iPort);
+
+ if (!hasMetaData("ssl_militant") || metaData("ssl_militant") == "FALSE")
+ d->militantSSL = false;
+ else if (metaData("ssl_militant") == "TRUE")
+ d->militantSSL = true;
+
+ if (!d->cc) d->cc = new KSSLCertificateCache;
+
+ KSSLCertificate& pc = d->kssl->peerInfo().getPeerCertificate();
+
+ KSSLCertificate::KSSLValidationList ksvl = pc.validateVerbose(KSSLCertificate::SSLServer);
+
+ _IPmatchesCN = d->kssl->peerInfo().certMatchesAddress();
+ if (!_IPmatchesCN) {
+#ifndef Q_WS_WIN //temporary
+ KNetwork::KResolverResults res = KNetwork::KResolver::resolve(d->kssl->peerInfo().peerHost(), "80", KNetwork::KResolver::CanonName);
+ if (!res.isEmpty()) {
+ TQString old = d->kssl->peerInfo().peerHost();
+ d->kssl->peerInfo().setPeerHost(res[0].canonicalName());
+ _IPmatchesCN = d->kssl->peerInfo().certMatchesAddress();
+ if (!_IPmatchesCN) {
+ d->kssl->peerInfo().setPeerHost(old);
+ }
+ }
+#endif
+ if (!_IPmatchesCN && !d->militantSSL) { // force this if the user wants it
+ if (d->cc->getHostList(pc).contains(ourHost)) {
+ _IPmatchesCN = true;
+ }
+ }
+ }
+
+ if (!_IPmatchesCN) {
+ ksvl << KSSLCertificate::InvalidHost;
+ }
+
+ KSSLCertificate::KSSLValidation ksv = KSSLCertificate::Ok;
+ if (!ksvl.isEmpty())
+ ksv = ksvl.first();
+
+ /* Setting the various bits of meta-info that will be needed. */
+ setMetaData("ssl_cipher", d->kssl->connectionInfo().getCipher());
+ setMetaData("ssl_cipher_desc",
+ d->kssl->connectionInfo().getCipherDescription());
+ setMetaData("ssl_cipher_version",
+ d->kssl->connectionInfo().getCipherVersion());
+ setMetaData("ssl_cipher_used_bits",
+ TQString::number(d->kssl->connectionInfo().getCipherUsedBits()));
+ setMetaData("ssl_cipher_bits",
+ TQString::number(d->kssl->connectionInfo().getCipherBits()));
+ setMetaData("ssl_peer_ip", d->ip);
+ if (!d->realHost.isEmpty()) {
+ setMetaData("ssl_proxied", "true");
+ }
+
+ TQString errorStr;
+ for(KSSLCertificate::KSSLValidationList::ConstIterator it = ksvl.begin();
+ it != ksvl.end(); ++it)
+ {
+ errorStr += TQString::number(*it)+":";
+ }
+ setMetaData("ssl_cert_errors", errorStr);
+ setMetaData("ssl_peer_certificate", pc.toString());
+
+ if (pc.chain().isValid() && pc.chain().depth() > 1) {
+ TQString theChain;
+ TQPtrList<KSSLCertificate> chain = pc.chain().getChain();
+ chain.setAutoDelete(true);
+ for (KSSLCertificate *c = chain.first(); c; c = chain.next()) {
+ theChain += c->toString();
+ theChain += "\n";
+ }
+ setMetaData("ssl_peer_chain", theChain);
+ } else setMetaData("ssl_peer_chain", "");
+
+ setMetaData("ssl_cert_state", TQString::number(ksv));
+
+ if (ksv == KSSLCertificate::Ok) {
+ rc = 1;
+ setMetaData("ssl_action", "accept");
+ }
+
+ kdDebug(7029) << "SSL HTTP frame the parent? " << metaData("main_frame_request") << endl;
+ if (!hasMetaData("main_frame_request") || metaData("main_frame_request") == "TRUE") {
+ // Since we're the parent, we need to teach the child.
+ setMetaData("ssl_parent_ip", d->ip);
+ setMetaData("ssl_parent_cert", pc.toString());
+ // - Read from cache and see if there is a policy for this
+ KSSLCertificateCache::KSSLCertificatePolicy cp =
+ d->cc->getPolicyByCertificate(pc);
+
+ // - validation code
+ if (ksv != KSSLCertificate::Ok) {
+ if (d->militantSSL) {
+ return -1;
+ }
+
+ if (cp == KSSLCertificateCache::Unknown ||
+ cp == KSSLCertificateCache::Ambiguous) {
+ cp = KSSLCertificateCache::Prompt;
+ } else {
+ // A policy was already set so let's honor that.
+ permacache = d->cc->isPermanent(pc);
+ }
+
+ if (!_IPmatchesCN && cp == KSSLCertificateCache::Accept) {
+ cp = KSSLCertificateCache::Prompt;
+// ksv = KSSLCertificate::Ok;
+ }
+
+ // Precondition: cp is one of Reject, Accept or Prompt
+ switch (cp) {
+ case KSSLCertificateCache::Accept:
+ rc = 1;
+ setMetaData("ssl_action", "accept");
+ break;
+ case KSSLCertificateCache::Reject:
+ rc = -1;
+ setMetaData("ssl_action", "reject");
+ break;
+ case KSSLCertificateCache::Prompt:
+ {
+ do {
+ if (ksv == KSSLCertificate::InvalidHost) {
+ TQString msg = i18n("The IP address of the host %1 "
+ "does not match the one the "
+ "certificate was issued to.");
+ result = messageBox( WarningYesNoCancel,
+ msg.arg(ourHost),
+ i18n("Server Authentication"),
+ i18n("&Details"),
+ i18n("Co&ntinue") );
+ } else {
+ TQString msg = i18n("The server certificate failed the "
+ "authenticity test (%1).");
+ result = messageBox( WarningYesNoCancel,
+ msg.arg(ourHost),
+ i18n("Server Authentication"),
+ i18n("&Details"),
+ i18n("Co&ntinue") );
+ }
+
+ if (result == KMessageBox::Yes) {
+ if (!d->dcc) {
+ d->dcc = new DCOPClient;
+ d->dcc->attach();
+ if (!d->dcc->isApplicationRegistered("tdeio_uiserver")) {
+ TDEApplication::startServiceByDesktopPath("tdeio_uiserver.desktop",
+ TQStringList() );
+ }
+
+ }
+ TQByteArray data, ignore;
+ TQCString ignoretype;
+ TQDataStream arg(data, IO_WriteOnly);
+ arg << theurl << mOutgoingMetaData;
+ arg << metaData("window-id").toInt();
+ d->dcc->call("tdeio_uiserver", "UIServer",
+ "showSSLInfoDialog(TQString,TDEIO::MetaData,int)",
+ data, ignoretype, ignore);
+ }
+ } while (result == KMessageBox::Yes);
+
+ if (result == KMessageBox::No) {
+ setMetaData("ssl_action", "accept");
+ rc = 1;
+ cp = KSSLCertificateCache::Accept;
+ doAddHost = true;
+ result = messageBox( WarningYesNo,
+ i18n("Would you like to accept this "
+ "certificate forever without "
+ "being prompted?"),
+ i18n("Server Authentication"),
+ i18n("&Forever"),
+ i18n("&Current Sessions Only"));
+ if (result == KMessageBox::Yes)
+ permacache = true;
+ else
+ permacache = false;
+ } else {
+ setMetaData("ssl_action", "reject");
+ rc = -1;
+ cp = KSSLCertificateCache::Prompt;
+ }
+ break;
+ }
+ default:
+ kdDebug(7029) << "TCPSlaveBase/SSL error in cert code."
+ << "Please report this to kfm-devel@kde.org."
+ << endl;
+ break;
+ }
+ }
+
+
+ // - cache the results
+ d->cc->addCertificate(pc, cp, permacache);
+ if (doAddHost) d->cc->addHost(pc, ourHost);
+ } else { // Child frame
+ // - Read from cache and see if there is a policy for this
+ KSSLCertificateCache::KSSLCertificatePolicy cp =
+ d->cc->getPolicyByCertificate(pc);
+ isChild = true;
+
+ // Check the cert and IP to make sure they're the same
+ // as the parent frame
+ bool certAndIPTheSame = (d->ip == metaData("ssl_parent_ip") &&
+ pc.toString() == metaData("ssl_parent_cert"));
+
+ if (ksv == KSSLCertificate::Ok) {
+ if (certAndIPTheSame) { // success
+ rc = 1;
+ setMetaData("ssl_action", "accept");
+ } else {
+ /*
+ if (d->militantSSL) {
+ return -1;
+ }
+ result = messageBox(WarningYesNo,
+ i18n("The certificate is valid but does not appear to have been assigned to this server. Do you wish to continue loading?"),
+ i18n("Server Authentication"));
+ if (result == KMessageBox::Yes) { // success
+ rc = 1;
+ setMetaData("ssl_action", "accept");
+ } else { // fail
+ rc = -1;
+ setMetaData("ssl_action", "reject");
+ }
+ */
+ setMetaData("ssl_action", "accept");
+ rc = 1; // Let's accept this now. It's bad, but at least the user
+ // will see potential attacks in KDE3 with the pseudo-lock
+ // icon on the toolbar, and can investigate with the RMB
+ }
+ } else {
+ if (d->militantSSL) {
+ return -1;
+ }
+
+ if (cp == KSSLCertificateCache::Accept) {
+ if (certAndIPTheSame) { // success
+ rc = 1;
+ setMetaData("ssl_action", "accept");
+ } else { // fail
+ result = messageBox(WarningYesNo,
+ i18n("You have indicated that you wish to accept this certificate, but it is not issued to the server who is presenting it. Do you wish to continue loading?"),
+ i18n("Server Authentication"));
+ if (result == KMessageBox::Yes) {
+ rc = 1;
+ setMetaData("ssl_action", "accept");
+ d->cc->addHost(pc, ourHost);
+ } else {
+ rc = -1;
+ setMetaData("ssl_action", "reject");
+ }
+ }
+ } else if (cp == KSSLCertificateCache::Reject) { // fail
+ messageBox(Information, i18n("SSL certificate is being rejected as requested. You can disable this in the TDE Control Center."),
+ i18n("Server Authentication"));
+ rc = -1;
+ setMetaData("ssl_action", "reject");
+ } else {
+ do {
+ TQString msg = i18n("The server certificate failed the "
+ "authenticity test (%1).");
+ result = messageBox(WarningYesNoCancel,
+ msg.arg(ourHost),
+ i18n("Server Authentication"),
+ i18n("&Details"),
+ i18n("Co&nnect"));
+ if (result == KMessageBox::Yes) {
+ if (!d->dcc) {
+ d->dcc = new DCOPClient;
+ d->dcc->attach();
+ if (!d->dcc->isApplicationRegistered("tdeio_uiserver")) {
+ TDEApplication::startServiceByDesktopPath("tdeio_uiserver.desktop",
+ TQStringList() );
+ }
+ }
+ TQByteArray data, ignore;
+ TQCString ignoretype;
+ TQDataStream arg(data, IO_WriteOnly);
+ arg << theurl << mOutgoingMetaData;
+ arg << metaData("window-id").toInt();
+ d->dcc->call("tdeio_uiserver", "UIServer",
+ "showSSLInfoDialog(TQString,TDEIO::MetaData,int)",
+ data, ignoretype, ignore);
+ }
+ } while (result == KMessageBox::Yes);
+
+ if (result == KMessageBox::No) {
+ setMetaData("ssl_action", "accept");
+ rc = 1;
+ cp = KSSLCertificateCache::Accept;
+ result = messageBox(WarningYesNo,
+ i18n("Would you like to accept this "
+ "certificate forever without "
+ "being prompted?"),
+ i18n("Server Authentication"),
+ i18n("&Forever"),
+ i18n("&Current Sessions Only"));
+ permacache = (result == KMessageBox::Yes);
+ d->cc->addCertificate(pc, cp, permacache);
+ d->cc->addHost(pc, ourHost);
+ } else {
+ setMetaData("ssl_action", "reject");
+ rc = -1;
+ cp = KSSLCertificateCache::Prompt;
+ d->cc->addCertificate(pc, cp, permacache);
+ }
+ }
+ }
+ }
+
+
+ if (rc == -1) {
+ return rc;
+ }
+
+ if (metaData("ssl_activate_warnings") == "TRUE") {
+ // - entering SSL
+ if (!isChild && metaData("ssl_was_in_use") == "FALSE" &&
+ d->kssl->settings()->warnOnEnter()) {
+ int result;
+ do {
+ result = messageBox( i18n("You are about to "
+ "enter secure mode. "
+ "All transmissions "
+ "will be encrypted "
+ "unless otherwise "
+ "noted.\nThis means "
+ "that no third party "
+ "will be able to "
+ "easily observe your "
+ "data in transit."),
+ WarningYesNo,
+ i18n("Security Information"),
+ i18n("Display SSL "
+ "&Information"),
+ i18n("C&onnect"),
+ "WarnOnEnterSSLMode" );
+ // Move this setting into KSSL instead
+ TDEConfig *config = new TDEConfig("tdeioslaverc");
+ config->setGroup("Notification Messages");
+
+ if (!config->readBoolEntry("WarnOnEnterSSLMode", true)) {
+ config->deleteEntry("WarnOnEnterSSLMode");
+ config->sync();
+ d->kssl->settings()->setWarnOnEnter(false);
+ d->kssl->settings()->save();
+ }
+ delete config;
+
+ if ( result == KMessageBox::Yes )
+ {
+ if (!d->dcc) {
+ d->dcc = new DCOPClient;
+ d->dcc->attach();
+ if (!d->dcc->isApplicationRegistered("tdeio_uiserver")) {
+ TDEApplication::startServiceByDesktopPath("tdeio_uiserver.desktop",
+ TQStringList() );
+ }
+ }
+ TQByteArray data, ignore;
+ TQCString ignoretype;
+ TQDataStream arg(data, IO_WriteOnly);
+ arg << theurl << mOutgoingMetaData;
+ arg << metaData("window-id").toInt();
+ d->dcc->call("tdeio_uiserver", "UIServer",
+ "showSSLInfoDialog(TQString,TDEIO::MetaData,int)",
+ data, ignoretype, ignore);
+ }
+ } while (result != KMessageBox::No);
+ }
+
+ } // if ssl_activate_warnings
+
+
+ kdDebug(7029) << "SSL connection information follows:" << endl
+ << "+-----------------------------------------------" << endl
+ << "| Cipher: " << d->kssl->connectionInfo().getCipher() << endl
+ << "| Description: " << d->kssl->connectionInfo().getCipherDescription() << endl
+ << "| Version: " << d->kssl->connectionInfo().getCipherVersion() << endl
+ << "| Strength: " << d->kssl->connectionInfo().getCipherUsedBits()
+ << " of " << d->kssl->connectionInfo().getCipherBits()
+ << " bits used." << endl
+ << "| PEER:" << endl
+ << "| Subject: " << d->kssl->peerInfo().getPeerCertificate().getSubject() << endl
+ << "| Issuer: " << d->kssl->peerInfo().getPeerCertificate().getIssuer() << endl
+ << "| Validation: " << (int)ksv << endl
+ << "| Certificate matches IP: " << _IPmatchesCN << endl
+ << "+-----------------------------------------------"
+ << endl;
+
+ // sendMetaData(); Do not call this function!!
+ return rc;
+}
+
+
+bool TCPSlaveBase::isConnectionValid()
+{
+ if ( m_iSock == -1 )
+ return false;
+
+ fd_set rdfs;
+ FD_ZERO(&rdfs);
+ FD_SET(m_iSock , &rdfs);
+
+ struct timeval tv;
+ tv.tv_usec = 0;
+ tv.tv_sec = 0;
+ int retval;
+#ifdef Q_OS_UNIX
+ do {
+ retval = KSocks::self()->select(m_iSock+1, &rdfs, NULL, NULL, &tv);
+ if (wasKilled())
+ return false; // Beam us out of here
+ } while ((retval == -1) && (errno == EAGAIN));
+#else
+ retval = -1;
+#endif
+ // retval == -1 ==> Error
+ // retval == 0 ==> Connection Idle
+ // retval >= 1 ==> Connection Active
+ //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: select returned: "
+ // << retval << endl;
+
+ if (retval == -1)
+ return false;
+
+ if (retval == 0)
+ return true;
+
+ // Connection is active, check if it has closed.
+ char buffer[100];
+#ifdef Q_OS_UNIX
+ do {
+ retval = KSocks::self()->recv(m_iSock, buffer, 80, MSG_PEEK);
+
+ } while ((retval == -1) && (errno == EAGAIN));
+#else
+ retval = -1;
+#endif
+ //kdDebug(7029) << "TCPSlaveBase::isConnectionValid: recv returned: "
+ // << retval << endl;
+ if (retval <= 0)
+ return false; // Error or connection closed.
+
+ return true; // Connection still valid.
+}
+
+
+bool TCPSlaveBase::waitForResponse( int t )
+{
+ fd_set rd;
+ struct timeval timeout;
+
+ if ( (m_bIsSSL || d->usingTLS) && !d->useSSLTunneling && d->kssl )
+ if (d->kssl->pending() > 0)
+ return true;
+
+ FD_ZERO(&rd);
+ FD_SET(m_iSock, &rd);
+
+ timeout.tv_usec = 0;
+ timeout.tv_sec = t;
+ time_t startTime;
+
+ int rc;
+ int n = t;
+
+reSelect:
+ startTime = time(NULL);
+#ifdef Q_OS_UNIX
+ rc = KSocks::self()->select(m_iSock+1, &rd, NULL, NULL, &timeout);
+#else
+ rc = -1;
+#endif
+ if (wasKilled())
+ return false; // We're dead.
+
+ if (rc == -1)
+ return false;
+
+ if (FD_ISSET(m_iSock, &rd))
+ return true;
+
+ // Well it returned but it wasn't set. Let's see if it
+ // returned too early (perhaps from an errant signal) and
+ // start over with the remaining time
+ int timeDone = time(NULL) - startTime;
+ if (timeDone < n)
+ {
+ n -= timeDone;
+ timeout.tv_sec = n;
+ goto reSelect;
+ }
+
+ return false; // Timed out!
+}
+
+int TCPSlaveBase::connectResult()
+{
+ return d->status;
+}
+
+void TCPSlaveBase::setBlockConnection( bool b )
+{
+ d->block = b;
+}
+
+void TCPSlaveBase::setConnectTimeout( int t )
+{
+ d->timeout = t;
+}
+
+bool TCPSlaveBase::isSSLTunnelEnabled()
+{
+ return d->useSSLTunneling;
+}
+
+void TCPSlaveBase::setEnableSSLTunnel( bool enable )
+{
+ d->useSSLTunneling = enable;
+}
+
+void TCPSlaveBase::setRealHost( const TQString& realHost )
+{
+ d->realHost = realHost;
+}
+
+bool TCPSlaveBase::doSSLHandShake( bool sendError )
+{
+ kdDebug(7029) << "TCPSlaveBase::doSSLHandShake: " << endl;
+ TQString msgHost = d->host;
+
+ d->kssl->reInitialize();
+
+ if (hasMetaData("ssl_session_id")) {
+ KSSLSession *s = KSSLSession::fromString(metaData("ssl_session_id"));
+ if (s) {
+ d->kssl->setSession(s);
+ delete s;
+ }
+ }
+ certificatePrompt();
+
+ if ( !d->realHost.isEmpty() )
+ {
+ msgHost = d->realHost;
+ }
+
+ kdDebug(7029) << "Setting real hostname: " << msgHost << endl;
+ d->kssl->setPeerHost(msgHost);
+
+ d->status = d->kssl->connect(m_iSock);
+ if (d->status < 0)
+ {
+ closeDescriptor();
+ if ( sendError )
+ error( ERR_COULD_NOT_CONNECT, msgHost);
+ return false;
+ }
+
+ setMetaData("ssl_session_id", d->kssl->session()->toString());
+ setMetaData("ssl_in_use", "TRUE");
+
+ if (!d->kssl->reusingSession()) {
+ int rc = verifyCertificate();
+ if ( rc != 1 ) {
+ d->status = -1;
+ closeDescriptor();
+ if ( sendError )
+ error( ERR_COULD_NOT_CONNECT, msgHost);
+ return false;
+ }
+ }
+
+ d->needSSLHandShake = false;
+
+ d->savedMetaData = mOutgoingMetaData;
+ return true;
+}
+
+
+bool TCPSlaveBase::userAborted() const
+{
+ return d->userAborted;
+}
+
+void TCPSlaveBase::virtual_hook( int id, void* data )
+{ SlaveBase::virtual_hook( id, data ); }
+
diff --git a/tdeio/tdeio/tcpslavebase.h b/tdeio/tdeio/tcpslavebase.h
new file mode 100644
index 000000000..4903dd7ac
--- /dev/null
+++ b/tdeio/tdeio/tcpslavebase.h
@@ -0,0 +1,389 @@
+/*
+ * Copyright (C) 2000 Alex Zepeda <zipzippy@sonic.net>
+ * Copyright (C) 2001 George Staikos <staikos@kde.org>
+ * Copyright (C) 2001 Dawit Alemayehu <adawit@kde.org>
+ *
+ * This file is part of the KDE project
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _TCP_SLAVEBASE_H
+#define _TCP_SLAVEBASE_H
+
+#include <sys/types.h>
+#include <stdio.h>
+
+#include <kextsock.h>
+#include <tdeio/slavebase.h>
+
+
+namespace TDEIO {
+
+/**
+ * There are two classes that specifies the protocol between application (job)
+ * and tdeioslave. SlaveInterface is the class to use on the application end,
+ * SlaveBase is the one to use on the slave end.
+ *
+ * Slave implementations should simply inherit SlaveBase
+ *
+ * A call to foo() results in a call to slotFoo() on the other end.
+ */
+class TDEIO_EXPORT TCPSlaveBase : public SlaveBase
+{
+public:
+ TCPSlaveBase(unsigned short int defaultPort, const TQCString &protocol,
+ const TQCString &poolSocket, const TQCString &appSocket);
+
+ TCPSlaveBase(unsigned short int defaultPort, const TQCString &protocol,
+ const TQCString &poolSocket, const TQCString &appSocket,
+ bool useSSL);
+
+ virtual ~TCPSlaveBase();
+
+protected:
+
+#ifndef KDE_NO_COMPAT
+ /**
+ * @deprecated Due to inconsistency with KDE naming convention.
+ */
+ KDE_DEPRECATED ssize_t Write(const void *data, ssize_t len) { return write( data, len ); }
+
+ /**
+ * @deprecated Due to inconsistency with KDE naming convention.
+ */
+ KDE_DEPRECATED ssize_t Read(void *data, ssize_t len) { return read( data, len ); }
+
+ /**
+ * @deprecated Due to inconsistency with KDE naming convention.
+ */
+ KDE_DEPRECATED ssize_t ReadLine(char *data, ssize_t len) { return readLine( data, len ); }
+
+ /**
+ * @deprecated Due to inconsistency with KDE naming convention.
+ */
+ KDE_DEPRECATED unsigned short int GetPort(unsigned short int p) { return port(p); }
+
+ /**
+ * @deprecated Due to inconsistency with KDE naming convention.
+ */
+ KDE_DEPRECATED bool ConnectToHost( const TQString &host, unsigned int port,
+ bool sendError ) { return connectToHost( host, port, sendError ); }
+
+ /**
+ * @deprecated Due to inconsistency with KDE naming convention.
+ */
+ KDE_DEPRECATED void CloseDescriptor() { closeDescriptor(); }
+
+ /**
+ * @deprecated Due to inconsistency with KDE naming convention.
+ */
+ KDE_DEPRECATED bool AtEOF() { return atEnd(); }
+
+ /**
+ * @deprecated Due to inconsistency with KDE naming convention.
+ */
+ KDE_DEPRECATED bool InitializeSSL() { return initializeSSL(); }
+
+ /**
+ * @deprecated Due to inconsistency with KDE naming convention.
+ */
+ KDE_DEPRECATED void CleanSSL() { cleanSSL(); }
+#endif
+
+ /**
+ * This function acts like standard write function call
+ * except it is also capable of making SSL or SOCKS
+ * connections.
+ *
+ * @param data info to be sent to remote machine
+ * @param len the length of the data to be sent
+ *
+ * @return the actual size of the data that was sent
+ */
+ ssize_t write(const void *data, ssize_t len);
+
+ /**
+ * This function acts like standard read function call
+ * except it is also capable of deciphering SSL data as
+ * well as handling data over SOCKSified connections.
+ *
+ * @param data storage for the info read from server
+ * @param len length of the info to read from the server
+ *
+ * @return the actual size of data that was obtained
+ */
+ ssize_t read(void *data, ssize_t len);
+
+ /**
+ * Same as above except it reads data one line at a time.
+ */
+ ssize_t readLine(char *data, ssize_t len);
+
+ /**
+ * Sets the maximum size of blocks read in during calls to readLine().
+ * This allows a slave to optimize for the protocol which it implements.
+ * Ideally this should be (common_line_length+1) or so.
+ * Making this too large will have adverse effects on performance.
+ * Initial/default value is 256(bytes)
+ */
+ void setBlockSize(int sz);
+
+ /**
+ * Determines the appropriate port to use.
+ *
+ * This functions attempts to discover the appropriate port.
+ *
+ * @param _port the port to try, if it works, it is returned
+ * @return the default port if the given port doesn't work
+ */
+ unsigned short int port(unsigned short int _port);
+
+ /**
+ * Performs the initial TCP connection stuff and/or
+ * SSL handshaking as necessary.
+ *
+ * Please note that unlike its deprecated counterpart, this
+ * function allows you to disable any error message from being
+ * sent back to the calling application! You can then use the
+ * connectResult() function to determine the result of the
+ * request for connection.
+ *
+ * @param host hostname
+ * @param port port number to connect to
+ * @param sendError if true sends error message to calling app.
+ *
+ * @return on succes, true is returned.
+ * on failure, false is returned and an appropriate
+ * error message is send to the application.
+ */
+ bool connectToHost( const TQString &host, unsigned int port,
+ bool sendError = true );
+
+ /**
+ * Are we using SSL?
+ *
+ * @return if so, true is returned.
+ * if not, true isn't returned.
+ * @since 3.2
+ */
+ bool usingSSL() const { return m_bIsSSL; }
+
+ /**
+ * Are we using TLS?
+ *
+ * @return if so, true is returned.
+ * if not, true isn't returned.
+ * @since 3.2
+ */
+ bool usingTLS() const;
+
+ /**
+ * @obsolete kept for binary compatibility
+ * Are we using TLS?
+ *
+ * @return if so, true is returned.
+ * if not, true isn't returned.
+ */
+ bool usingTLS();
+
+ /**
+ * Can we use TLS?
+ *
+ * @return if so, true is returned.
+ * if not, true isn't returned.
+ */
+ bool canUseTLS();
+
+ /**
+ * Start using TLS on the connection.
+ *
+ * @return on success, 1 is returned.
+ * on failure, 0 is returned.
+ * on TLS init failure, -1 is returned.
+ * on connect failure, -2 is returned.
+ * on certificate failure, -3 is returned.
+ */
+ int startTLS();
+
+ /**
+ * Stop using TLS on the connection.
+ */
+ void stopTLS();
+
+ /**
+ * Closes the current file descriptor.
+ *
+ * Call this function to properly close up the socket
+ * since it also takes care to prroperly close the stdio
+ * fstream stuff, as well as sets the socket back to -1
+ */
+ void closeDescriptor();
+
+
+ /**
+ * Returns true when end of data is reached
+ */
+ bool atEnd();
+
+
+ /**
+ * Call this if you use persistent connections and want all the
+ * metadata restored. This is particularly important for SSL
+ * sessions since the app needs to know the state of connection,
+ * certificates, etc.
+ */
+ void setSSLMetaData();
+
+
+ /**
+ * Initializs all SSL variables
+ */
+ bool initializeSSL();
+
+
+ /**
+ * Cleans up all SSL settings.
+ */
+ void cleanSSL();
+
+ /**
+ * Determines whether or not we are still connected
+ * to the remote machine.
+ *
+ * This method may fail to detect a closed SSL connection.
+ *
+ * return @p true if the socket is still active or
+ * false otherwise.
+ */
+ bool isConnectionValid();
+
+ /**
+ * Returns the status of the connection.
+ *
+ * This function allows you to invoke ConnectToHost
+ * with the @p sendError flag set to false so that you
+ * can send the appropriate error message back to the
+ * calling io-slave.
+ *
+ * @return the status code as returned by KExtendedSocket.
+ */
+ int connectResult();
+
+ /**
+ * Wait for some type of activity on the socket
+ * for the period specified by @p t.
+ *
+ * @param t length of time in seconds that we should monitor the
+ * socket before timing out.
+ *
+ * @return true if any activity was seen on the socket before the
+ * timeout value was reached, false otherwise.
+ */
+ bool waitForResponse( int t );
+
+ /**
+ * Sets the mode of the connection to blocking or non-blocking.
+ *
+ * Be sure to call this function before calling connectToHost.
+ * Otherwise, this setting will not have any effect until the next
+ * @p connectToHost.
+ *
+ * @param b true to make the connection a blocking one, false otherwise.
+ */
+ void setBlockConnection( bool b );
+
+ /**
+ * Sets how long to wait for orignally connecting to
+ * the requested before timinig out.
+ *
+ * Be sure to call this function before calling ConnectToHost,
+ * otherwise the setting will not take effect until the next call
+ * to @p ConnectToHost.
+ *
+ * @param t timeout value
+ */
+ void setConnectTimeout( int t );
+
+ /**
+ * Returns true if SSL tunneling is enabled.
+ *
+ * @see setEnableSSlTunnel
+ */
+ bool isSSLTunnelEnabled();
+
+ /**
+ * Set up SSL tunneling mode.
+ *
+ * Calling this function with a @p true argument will allow
+ * you to temprarly ignore the @p m_bIsSSL flag setting and
+ * make a non-SSL connection. It is mostly useful for making
+ * connections to SSL sites through a non-transparent proxy
+ * server (i.e. most proxy servers out there).
+ *
+ * Note that once you have successfully "tunneled" through the
+ * proxy server you must call this function with its argument
+ * set to false to properly connect to the SSL site.
+ *
+ * @param enable if true SSL Tunneling will be enabled
+ */
+ void setEnableSSLTunnel( bool enable );
+
+ /**
+ * Sets up the the real hostname for an SSL connection
+ * that goes through a proxy server.
+ *
+ * This function is essential in making sure that the
+ * real hostname is used for validating certificates from
+ * SSL sites!
+ *
+ * @param realHost the actual host name we are connecting to
+ */
+ void setRealHost( const TQString& realHost );
+
+ // don't use me!
+ void doConstructorStuff();
+
+ // For the certificate verification code
+ int verifyCertificate();
+
+ // For prompting for the certificate to use
+ void certificatePrompt();
+
+ // Did the user abort (as the reason for connectToHost returning false)
+ bool userAborted() const;
+
+protected:
+ int m_iSock;
+ bool m_bIsSSL;
+ unsigned short int m_iPort;
+ unsigned short int m_iDefaultPort;
+ TQCString m_sServiceName;
+ FILE *fp;
+
+private:
+ bool doSSLHandShake( bool sendError );
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class TcpSlaveBasePrivate;
+ TcpSlaveBasePrivate *d;
+};
+
+}
+
+#endif
diff --git a/tdeio/tdeio/tdefilefilter.cpp b/tdeio/tdeio/tdefilefilter.cpp
new file mode 100644
index 000000000..310b86221
--- /dev/null
+++ b/tdeio/tdeio/tdefilefilter.cpp
@@ -0,0 +1,134 @@
+/* This file is part of the KDE libraries
+
+ Copyright (c) 2001,2002 Carsten Pfeiffer <pfeiffer@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <tqregexp.h>
+
+#include <tdefileitem.h>
+#include <kglobal.h>
+
+#include "tdefilefilter.h"
+
+KSimpleFileFilter::KSimpleFileFilter()
+ : m_filterDotFiles( true ),
+ m_filterSpecials( true ),
+ m_modeFilter( 0 )
+{
+ m_nameFilters.setAutoDelete( true );
+}
+
+KSimpleFileFilter::~KSimpleFileFilter()
+{
+}
+
+void KSimpleFileFilter::setFilterDotFiles( bool filter )
+{
+ m_filterDotFiles = filter;
+}
+
+void KSimpleFileFilter::setFilterSpecials( bool filter )
+{
+ m_filterSpecials = filter;
+}
+
+void KSimpleFileFilter::setNameFilters( const TQString& nameFilters )
+{
+ // KDE 3.0 defaults
+ setNameFilters( nameFilters, false, ' ' );
+}
+
+void KSimpleFileFilter::setNameFilters( const TQString& nameFilters,
+ bool caseSensitive,
+ const TQChar& separator )
+{
+ m_nameFilters.clear();
+
+ // Split on white space
+ TQStringList list = TQStringList::split(separator, nameFilters);
+
+ TQStringList::ConstIterator it = list.begin();
+ for ( ; it != list.end(); ++it )
+ m_nameFilters.append(new TQRegExp(*it, caseSensitive, true ));
+}
+
+void KSimpleFileFilter::setMimeFilters( const TQStringList& mimeFilters )
+{
+ m_mimeFilters = mimeFilters;
+}
+
+void KSimpleFileFilter::setModeFilter( mode_t mode )
+{
+ m_modeFilter = mode;
+}
+
+bool KSimpleFileFilter::passesFilter( const KFileItem *item ) const
+{
+ static const TQString& dot = TDEGlobal::staticQString(".");
+ static const TQString& dotdot = TDEGlobal::staticQString("..");
+
+ const TQString& name = item->name();
+
+ if ( m_filterDotFiles && item->isHidden() )
+ return false;
+
+ if ( m_filterSpecials && (name == dot || name == dotdot) )
+ return false;
+
+ if ( m_modeFilter && !(m_modeFilter & item->mode()) )
+ return false;
+
+ if ( !m_mimeFilters.isEmpty() ) {
+ // correct or guessed mimetype -- we don't mind
+ KMimeType::Ptr mime = item->mimeTypePtr();
+ bool ok = false;
+
+ TQStringList::ConstIterator it = m_mimeFilters.begin();
+ for ( ; it != m_mimeFilters.end(); ++it ) {
+ if ( mime->is(*it) ) { // match!
+ ok = true;
+ break;
+ }
+ }
+ if ( !ok )
+ return false;
+ }
+
+ if ( !m_nameFilters.isEmpty() ) {
+ bool ok = false;
+
+ TQPtrListIterator<TQRegExp> it( m_nameFilters );
+ for ( ; it.current(); ++it ) {
+ if ( it.current()->exactMatch( name ) ) { // match!
+ ok = true;
+ break;
+ }
+ }
+ if ( !ok )
+ return false;
+ }
+
+ return true; // passes the filter!
+}
+
+void KFileFilter::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+void KSimpleFileFilter::virtual_hook( int id, void* data )
+{ KFileFilter::virtual_hook( id, data ); }
+
diff --git a/tdeio/tdeio/tdefilefilter.h b/tdeio/tdeio/tdefilefilter.h
new file mode 100644
index 000000000..2891e800e
--- /dev/null
+++ b/tdeio/tdeio/tdefilefilter.h
@@ -0,0 +1,170 @@
+/* This file is part of the KDE libraries
+
+ Copyright (c) 2001,2002 Carsten Pfeiffer <pfeiffer@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef KFILEFILTER_H
+#define KFILEFILTER_H
+
+#include <tqptrlist.h>
+#include <tqstringlist.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <tdelibs_export.h>
+
+class TQRegExp;
+class KFileItem;
+
+/**
+ * A KFileFilter is a simple base class for file filters. Just
+ * reimplement passesFilter().
+ * @short Base class for file filters.
+ */
+class TDEIO_EXPORT KFileFilter
+{
+public:
+ /**
+ * Checks the given @p item.
+ * @param item the item to filter
+ * @return true if the @p item passes the filter, false otherwise
+ */
+ virtual bool passesFilter( const KFileItem *item ) const = 0;
+protected:
+ virtual void virtual_hook( int id, void* data );
+};
+
+/**
+ * A simple file filter that can filter hidden dot files, by name,
+ * by mime type and by mode.
+ * @short A simple file filter.
+ */
+class TDEIO_EXPORT KSimpleFileFilter : public KFileFilter
+{
+public:
+ /**
+ * Creates a new filter. By default, it filters only hidden dot files
+ * and "." and "..".
+ */
+ KSimpleFileFilter();
+ virtual ~KSimpleFileFilter();
+
+ /**
+ * Enable or disable filtering hidden dot files.
+ * This option is enabled by default.
+ * @param filter true to enable filtering dot files, false to
+ * disable
+ * @see filterDotFiles
+ */
+ virtual void setFilterDotFiles( bool filter );
+ /**
+ * Checks whether filtering dot files is enabled.
+ * This option is enabled by default.
+ * @return true if filtering is enabled, false otherwise
+ * @see setFilterDotFiles
+ */
+ bool filterDotFiles() const { return m_filterDotFiles; }
+
+ /**
+ * Filters "." and "..", default is true.
+ * @param filter true to enable, false otherwise
+ */
+ virtual void setFilterSpecials( bool filter );
+ /**
+ * Checks whether it filters "." and "..", default is true.
+ * @return true if enabled, false otherwise
+ */
+ bool filterSpecials() const { return m_filterSpecials; }
+
+ // ### KDE4 make virtual and bool caseSensitive = false
+ /**
+ * Sets a list of regular expressions to filter by name.
+ * The file will only pass if its name matches one of the regular
+ * expressions.
+ * @param nameFilters a list of regular expressions, separated by
+ * the character @p separator
+ * @param caseSensitive if true, matches case sensitive. False
+ * otherwise
+ * @param separator the separator in the @p nameFilter
+ * @since 3.1
+ */
+ void setNameFilters( const TQString& nameFilters, bool caseSensitive,
+ const TQChar& separator = ' ' );
+ /**
+ * Sets a list of regular expressions to filter by name.
+ * The file will only pass if its name matches one of the regular
+ * expressions.
+ * @param nameFilters a list of regular expressions, separated by
+ * space (' ')
+ */
+ virtual void setNameFilters( const TQString& nameFilters );
+
+ /**
+ * Sets a list of mime filters. A file can only pass if its
+ * mime type is contained in this list.
+ * @param mimeFilters the list of mime types
+ * @see setMimeFilter
+ */
+ virtual void setMimeFilters( const TQStringList& mimeFilters );
+ /**
+ * Returns the list of mime types.
+ * @return the list of mime types
+ * @see mimeFilter
+ */
+ TQStringList mimeFilters() const { return m_mimeFilters; }
+
+ /**
+ * Sets the mode filter. If the @p mode is 0, the filter is
+ * disabled.
+ * When enabled, a file will only pass if the files mode
+ * ANDed with @p mode is not zero.
+ * @param mode the new mode. 0 to disable
+ * @see modeFilter
+ */
+ virtual void setModeFilter( mode_t mode );
+ /**
+ * Returns the mode filter, as set by setModeFilter().
+ * @return the mode filter, 0 if disabled
+ * @see setModeFilter
+ */
+ mode_t modeFilter() const { return m_modeFilter; }
+
+ /**
+ * Checks the given @p item.
+ * @param item the item to filter
+ * @return true if the @p item passes the filter, false otherwise
+ */
+ virtual bool passesFilter( const KFileItem *item ) const;
+
+protected:
+ TQPtrList<TQRegExp> m_nameFilters;
+
+private:
+ TQStringList m_mimeFilters;
+ bool m_filterDotFiles :1;
+ bool m_filterSpecials :1;
+ mode_t m_modeFilter;
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KSimpleFileFilterPrivate* d;
+};
+
+#endif // KFILEFILTER_H
diff --git a/tdeio/tdeio/tdefileitem.cpp b/tdeio/tdeio/tdefileitem.cpp
new file mode 100644
index 000000000..bd043504a
--- /dev/null
+++ b/tdeio/tdeio/tdefileitem.cpp
@@ -0,0 +1,1202 @@
+/* This file is part of the KDE project
+ Copyright (C) 1999 David Faure <faure@kde.org>
+ 2001 Carsten Pfeiffer <pfeiffer@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+// $Id$
+
+#include <config.h>
+
+#include <sys/time.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/types.h>
+
+#include <assert.h>
+#include <unistd.h>
+
+#include "tdefileitem.h"
+
+#include <tqdir.h>
+#include <tqfile.h>
+#include <tqmap.h>
+#include <tqstylesheet.h>
+#include <tqimage.h>
+
+#include <kdebug.h>
+#include <tdefilemetainfo.h>
+#include <ksambashare.h>
+#include <knfsshare.h>
+#include <kglobal.h>
+#include <kglobalsettings.h>
+#include <kiconloader.h>
+#include <klargefile.h>
+#include <klocale.h>
+#include <kmimetype.h>
+#include <krun.h>
+
+#ifdef HAVE_ELFICON
+#include "tdelficon.h"
+#endif // HAVE_ELFICON
+
+class KFileItem::KFileItemPrivate {
+ public:
+ TQString iconName;
+};
+
+KFileItem::KFileItem( const TDEIO::UDSEntry& _entry, const KURL& _url,
+ bool _determineMimeTypeOnDemand, bool _urlIsDirectory ) :
+ m_entry( _entry ),
+ m_url( _url ),
+ m_pMimeType( 0 ),
+ m_fileMode( KFileItem::Unknown ),
+ m_permissions( KFileItem::Unknown ),
+ m_bMarked( false ),
+ m_bLink( false ),
+ m_bIsLocalURL( _url.isLocalFile() ),
+ m_bMimeTypeKnown( false ),
+ m_hidden( Auto ),
+ d(0)
+{
+ readUDSEntry( _urlIsDirectory );
+ init( _determineMimeTypeOnDemand );
+}
+
+KFileItem::KFileItem( mode_t _mode, mode_t _permissions, const KURL& _url, bool _determineMimeTypeOnDemand ) :
+ m_entry(), // warning !
+ m_url( _url ),
+ m_strName( _url.fileName() ),
+ m_strText( TDEIO::decodeFileName( m_strName ) ),
+ m_pMimeType( 0 ),
+ m_fileMode ( _mode ),
+ m_permissions( _permissions ),
+ m_bMarked( false ),
+ m_bLink( false ),
+ m_bIsLocalURL( _url.isLocalFile() ),
+ m_bMimeTypeKnown( false ),
+ m_hidden( Auto ),
+ d(0)
+{
+ init( _determineMimeTypeOnDemand );
+}
+
+KFileItem::KFileItem( const KURL &url, const TQString &mimeType, mode_t mode )
+: m_url( url ),
+ m_strName( url.fileName() ),
+ m_strText( TDEIO::decodeFileName( m_strName ) ),
+ m_pMimeType( 0 ),
+ m_fileMode( mode ),
+ m_permissions( KFileItem::Unknown ),
+ m_bMarked( false ),
+ m_bLink( false ),
+ m_bIsLocalURL( url.isLocalFile() ),
+ m_bMimeTypeKnown( !mimeType.isEmpty() ),
+ m_hidden( Auto ),
+ d(0)
+{
+ if (m_bMimeTypeKnown)
+ m_pMimeType = KMimeType::mimeType( mimeType );
+
+ init( false );
+}
+
+KFileItem::KFileItem( const KFileItem & item ) :
+ d(0)
+{
+ assign( item );
+}
+
+KFileItem& KFileItem::operator=( const KFileItem & item )
+{
+ assign( item );
+ return *this;
+}
+
+KFileItem::~KFileItem()
+{
+ delete d;
+}
+
+void KFileItem::init( bool _determineMimeTypeOnDemand )
+{
+ m_access = TQString::null;
+ m_size = (TDEIO::filesize_t) -1;
+ // metaInfo = KFileMetaInfo();
+ for ( int i = 0; i < NumFlags; i++ )
+ m_time[i] = (time_t) -1;
+
+ // determine mode and/or permissions if unknown
+ if ( m_fileMode == KFileItem::Unknown || m_permissions == KFileItem::Unknown )
+ {
+ mode_t mode = 0;
+ if ( m_url.isLocalFile() )
+ {
+ /* directories may not have a slash at the end if
+ * we want to stat() them; it requires that we
+ * change into it .. which may not be allowed
+ * stat("/is/unaccessible") -> rwx------
+ * stat("/is/unaccessible/") -> EPERM H.Z.
+ * This is the reason for the -1
+ */
+ KDE_struct_stat buf;
+ TQCString path = TQFile::encodeName(m_url.path( -1 ));
+ if ( KDE_lstat( path.data(), &buf ) == 0 )
+ {
+ mode = buf.st_mode;
+ if ( S_ISLNK( mode ) )
+ {
+ m_bLink = true;
+ if ( KDE_stat( path.data(), &buf ) == 0 )
+ mode = buf.st_mode;
+ else // link pointing to nowhere (see tdeio/file/file.cc)
+ mode = (S_IFMT-1) | S_IRWXU | S_IRWXG | S_IRWXO;
+ }
+ // While we're at it, store the times
+ m_time[ Modification ] = buf.st_mtime;
+ m_time[ Access ] = buf.st_atime;
+ if ( m_fileMode == KFileItem::Unknown )
+ m_fileMode = mode & S_IFMT; // extract file type
+ if ( m_permissions == KFileItem::Unknown )
+ m_permissions = mode & 07777; // extract permissions
+ }
+ }
+ }
+
+ // determine the mimetype
+ if (!m_pMimeType && !m_url.isEmpty())
+ {
+ bool accurate = false;
+ bool isLocalURL;
+ KURL url = mostLocalURL(isLocalURL);
+
+ m_pMimeType = KMimeType::findByURL( url, m_fileMode, isLocalURL,
+ // use fast mode if not mimetype on demand
+ _determineMimeTypeOnDemand, &accurate );
+ //kdDebug() << "finding mimetype for " << url.url() << " : " << m_pMimeType->name() << endl;
+ // if we didn't use fast mode, or if we got a result, then this is the mimetype
+ // otherwise, determineMimeType will be able to do better.
+ m_bMimeTypeKnown = (!_determineMimeTypeOnDemand) || accurate;
+ }
+}
+
+void KFileItem::readUDSEntry( bool _urlIsDirectory )
+{
+ // extract the mode and the filename from the TDEIO::UDS Entry
+ bool UDS_URL_seen = false;
+
+ if (&m_entry == NULL) return;
+
+ TDEIO::UDSEntry::ConstIterator it = m_entry.begin();
+ for( ; it != m_entry.end(); ++it ) {
+ switch ((*it).m_uds) {
+
+ case TDEIO::UDS_FILE_TYPE:
+ m_fileMode = (mode_t)((*it).m_long);
+ break;
+
+ case TDEIO::UDS_ACCESS:
+ m_permissions = (mode_t)((*it).m_long);
+ break;
+
+ case TDEIO::UDS_USER:
+ m_user = ((*it).m_str);
+ break;
+
+ case TDEIO::UDS_GROUP:
+ m_group = ((*it).m_str);
+ break;
+
+ case TDEIO::UDS_NAME:
+ m_strName = (*it).m_str;
+ m_strText = TDEIO::decodeFileName( m_strName );
+ break;
+
+ case TDEIO::UDS_URL:
+ UDS_URL_seen = true;
+ m_url = KURL((*it).m_str);
+ if ( m_url.isLocalFile() )
+ m_bIsLocalURL = true;
+ break;
+
+ case TDEIO::UDS_MIME_TYPE:
+ m_pMimeType = KMimeType::mimeType((*it).m_str);
+ m_bMimeTypeKnown = true;
+ break;
+
+ case TDEIO::UDS_GUESSED_MIME_TYPE:
+ m_guessedMimeType = (*it).m_str;
+ break;
+
+ case TDEIO::UDS_LINK_DEST:
+ m_bLink = !(*it).m_str.isEmpty(); // we don't store the link dest
+ break;
+
+ case TDEIO::UDS_ICON_NAME:
+ if ( !d )
+ d = new KFileItemPrivate();
+ d->iconName = (*it).m_str;
+ break;
+
+ case TDEIO::UDS_HIDDEN:
+ if ( (*it).m_long )
+ m_hidden = Hidden;
+ else
+ m_hidden = Shown;
+ break;
+ }
+ }
+
+ // avoid creating these QStrings again and again
+ static const TQString& dot = TDEGlobal::staticQString(".");
+ if ( _urlIsDirectory && !UDS_URL_seen && !m_strName.isEmpty() && m_strName != dot )
+ m_url.addPath( m_strName );
+}
+
+void KFileItem::refresh()
+{
+ m_fileMode = KFileItem::Unknown;
+ m_permissions = KFileItem::Unknown;
+ m_pMimeType = 0L;
+ m_user = TQString::null;
+ m_group = TQString::null;
+ m_metaInfo = KFileMetaInfo();
+ m_hidden = Auto;
+
+ // Basically, we can't trust any information we got while listing.
+ // Everything could have changed...
+ // Clearing m_entry makes it possible to detect changes in the size of the file,
+ // the time information, etc.
+ m_entry = TDEIO::UDSEntry();
+ init( false );
+}
+
+void KFileItem::refreshMimeType()
+{
+ m_pMimeType = 0L;
+ init( false ); // Will determine the mimetype
+}
+
+void KFileItem::setURL( const KURL &url )
+{
+ m_url = url;
+ setName( url.fileName() );
+}
+
+void KFileItem::setName( const TQString& name )
+{
+ m_strName = name;
+ m_strText = TDEIO::decodeFileName( m_strName );
+}
+
+TQString KFileItem::linkDest() const
+{
+ if (&m_entry == NULL) return TQString::null;
+
+ // Extract it from the TDEIO::UDSEntry
+ TDEIO::UDSEntry::ConstIterator it = m_entry.begin();
+ for( ; it != m_entry.end(); ++it )
+ if ( (*it).m_uds == TDEIO::UDS_LINK_DEST )
+ return (*it).m_str;
+ // If not in the TDEIO::UDSEntry, or if UDSEntry empty, use readlink() [if local URL]
+ if ( m_bIsLocalURL )
+ {
+ char buf[1000];
+ int n = readlink( TQFile::encodeName(m_url.path( -1 )), buf, sizeof(buf)-1 );
+ if ( n != -1 )
+ {
+ buf[ n ] = 0;
+ return TQFile::decodeName( buf );
+ }
+ }
+ return TQString::null;
+}
+
+TQString KFileItem::localPath() const
+{
+ if ( m_bIsLocalURL )
+ {
+ return m_url.path();
+ }
+ else
+ {
+ if (&m_entry == NULL) return TQString::null;
+
+ // Extract the local path from the TDEIO::UDSEntry
+ TDEIO::UDSEntry::ConstIterator it = m_entry.begin();
+ const TDEIO::UDSEntry::ConstIterator end = m_entry.end();
+ for( ; it != end; ++it )
+ if ( (*it).m_uds == TDEIO::UDS_LOCAL_PATH )
+ return (*it).m_str;
+ }
+
+ return TQString::null;
+}
+
+TDEIO::filesize_t KFileItem::size(bool &exists) const
+{
+ exists = true;
+ if ( m_size != (TDEIO::filesize_t) -1 )
+ return m_size;
+
+ if (&m_entry == NULL) return 0L;
+
+ // Extract it from the TDEIO::UDSEntry
+ TDEIO::UDSEntry::ConstIterator it = m_entry.begin();
+ for( ; it != m_entry.end(); ++it )
+ if ( (*it).m_uds == TDEIO::UDS_SIZE ) {
+ m_size = (*it).m_long;
+ return m_size;
+ }
+ // If not in the TDEIO::UDSEntry, or if UDSEntry empty, use stat() [if local URL]
+ if ( m_bIsLocalURL )
+ {
+ KDE_struct_stat buf;
+ if ( KDE_stat( TQFile::encodeName(m_url.path( -1 )), &buf ) == 0 )
+ return buf.st_size;
+ }
+ exists = false;
+ return 0L;
+}
+
+bool KFileItem::hasExtendedACL() const
+{
+ if (&m_entry == NULL) return false;
+ TDEIO::UDSEntry::ConstIterator it = m_entry.begin();
+ for( ; it != m_entry.end(); it++ )
+ if ( (*it).m_uds == TDEIO::UDS_EXTENDED_ACL ) {
+ return true;
+ }
+ return false;
+}
+
+KACL KFileItem::ACL() const
+{
+ if ( hasExtendedACL() ) {
+ if (&m_entry == NULL) return KACL( m_permissions );
+
+ // Extract it from the TDEIO::UDSEntry
+ TDEIO::UDSEntry::ConstIterator it = m_entry.begin();
+ for( ; it != m_entry.end(); ++it )
+ if ( (*it).m_uds == TDEIO::UDS_ACL_STRING )
+ return KACL((*it).m_str);
+ }
+ // create one from the basic permissions
+ return KACL( m_permissions );
+}
+
+KACL KFileItem::defaultACL() const
+{
+ if (&m_entry == NULL) return KACL();
+
+ // Extract it from the TDEIO::UDSEntry
+ TDEIO::UDSEntry::ConstIterator it = m_entry.begin();
+ for( ; it != m_entry.end(); ++it )
+ if ( (*it).m_uds == TDEIO::UDS_DEFAULT_ACL_STRING )
+ return KACL((*it).m_str);
+ return KACL();
+}
+
+TDEIO::filesize_t KFileItem::size() const
+{
+ bool exists;
+ return size(exists);
+}
+
+time_t KFileItem::time( unsigned int which ) const
+{
+ bool hasTime;
+ return time(which, hasTime);
+}
+time_t KFileItem::time( unsigned int which, bool &hasTime ) const
+{
+ hasTime = true;
+ unsigned int mappedWhich = 0;
+
+ switch( which ) {
+ case TDEIO::UDS_MODIFICATION_TIME:
+ mappedWhich = Modification;
+ break;
+ case TDEIO::UDS_ACCESS_TIME:
+ mappedWhich = Access;
+ break;
+ case TDEIO::UDS_CREATION_TIME:
+ mappedWhich = Creation;
+ break;
+ }
+
+ if ( m_time[mappedWhich] != (time_t) -1 )
+ return m_time[mappedWhich];
+
+ if (&m_entry == NULL) return static_cast<time_t>(0);
+
+ // Extract it from the TDEIO::UDSEntry
+ TDEIO::UDSEntry::ConstIterator it = m_entry.begin();
+ for( ; it != m_entry.end(); ++it )
+ if ( (*it).m_uds == which ) {
+ m_time[mappedWhich] = static_cast<time_t>((*it).m_long);
+ return m_time[mappedWhich];
+ }
+
+ // If not in the TDEIO::UDSEntry, or if UDSEntry empty, use stat() [if local URL]
+ if ( m_bIsLocalURL )
+ {
+ KDE_struct_stat buf;
+ if ( KDE_stat( TQFile::encodeName(m_url.path(-1)), &buf ) == 0 )
+ {
+ if(which == TDEIO::UDS_CREATION_TIME) {
+ // We can't determine creation time for local files
+ hasTime = false;
+ m_time[mappedWhich] = static_cast<time_t>(0);
+ return m_time[mappedWhich];
+ }
+ m_time[mappedWhich] = (which == TDEIO::UDS_MODIFICATION_TIME) ?
+ buf.st_mtime :
+ /* which == TDEIO::UDS_ACCESS_TIME)*/
+ buf.st_atime;
+ return m_time[mappedWhich];
+ }
+ }
+ hasTime = false;
+ return static_cast<time_t>(0);
+}
+
+
+TQString KFileItem::user() const
+{
+ if ( m_user.isEmpty() && m_bIsLocalURL )
+ {
+ KDE_struct_stat buff;
+ if ( KDE_lstat( TQFile::encodeName(m_url.path( -1 )), &buff ) == 0) // get uid/gid of the link, if it's a link
+ {
+ struct passwd *user = getpwuid( buff.st_uid );
+ if ( user != 0L )
+ m_user = TQString::fromLocal8Bit(user->pw_name);
+ }
+ }
+ return m_user;
+}
+
+TQString KFileItem::group() const
+{
+#ifdef Q_OS_UNIX
+ if (m_group.isEmpty() && m_bIsLocalURL )
+ {
+ KDE_struct_stat buff;
+ if ( KDE_lstat( TQFile::encodeName(m_url.path( -1 )), &buff ) == 0) // get uid/gid of the link, if it's a link
+ {
+ struct group *ge = getgrgid( buff.st_gid );
+ if ( ge != 0L ) {
+ m_group = TQString::fromLocal8Bit(ge->gr_name);
+ if (m_group.isEmpty())
+ m_group.sprintf("%d",ge->gr_gid);
+ } else
+ m_group.sprintf("%d",buff.st_gid);
+ }
+ }
+#endif
+ return m_group;
+}
+
+TQString KFileItem::mimetype() const
+{
+ KFileItem * that = const_cast<KFileItem *>(this);
+ return that->determineMimeType()->name();
+}
+
+KMimeType::Ptr KFileItem::determineMimeType()
+{
+ if ( !m_pMimeType || !m_bMimeTypeKnown )
+ {
+ bool isLocalURL;
+ KURL url = mostLocalURL(isLocalURL);
+
+ m_pMimeType = KMimeType::findByURL( url, m_fileMode, isLocalURL );
+ //kdDebug() << "finding mimetype for " << url.url() << " : " << m_pMimeType->name() << endl;
+ m_bMimeTypeKnown = true;
+ }
+
+ return m_pMimeType;
+}
+
+bool KFileItem::isMimeTypeKnown() const
+{
+ // The mimetype isn't known if determineMimeType was never called (on-demand determination)
+ // or if this fileitem has a guessed mimetype (e.g. ftp symlink) - in which case
+ // it always remains "not fully determined"
+ return m_bMimeTypeKnown && m_guessedMimeType.isEmpty();
+}
+
+TQString KFileItem::mimeComment()
+{
+ KMimeType::Ptr mType = determineMimeType();
+
+ bool isLocalURL;
+ KURL url = mostLocalURL(isLocalURL);
+
+ TQString comment = mType->comment( url, isLocalURL );
+ //kdDebug() << "finding comment for " << url.url() << " : " << m_pMimeType->name() << endl;
+ if (!comment.isEmpty())
+ return comment;
+ else
+ return mType->name();
+}
+
+TQString KFileItem::iconName()
+{
+ if (d && (!d->iconName.isEmpty())) return d->iconName;
+
+ bool isLocalURL;
+ KURL url = mostLocalURL(isLocalURL);
+
+ //kdDebug() << "finding icon for " << url.url() << " : " << m_pMimeType->name() << endl;
+ return determineMimeType()->icon(url, isLocalURL);
+}
+
+int KFileItem::overlays() const
+{
+ int _state = 0;
+ if ( m_bLink )
+ _state |= KIcon::LinkOverlay;
+
+ if ( !S_ISDIR( m_fileMode ) // Locked dirs have a special icon, use the overlay for files only
+ && !isReadable())
+ _state |= KIcon::LockOverlay;
+
+ if ( isHidden() )
+ _state |= KIcon::HiddenOverlay;
+
+ if( S_ISDIR( m_fileMode ) && m_bIsLocalURL)
+ {
+ if (KSambaShare::instance()->isDirectoryShared( m_url.path() ) ||
+ KNFSShare::instance()->isDirectoryShared( m_url.path() ))
+ {
+ //kdDebug()<<"KFileShare::isDirectoryShared : "<<m_url.path()<<endl;
+ _state |= KIcon::ShareOverlay;
+ }
+ }
+
+ if ( m_pMimeType->name() == "application/x-gzip" && m_url.fileName().right(3) == ".gz" )
+ _state |= KIcon::ZipOverlay;
+ return _state;
+}
+
+TQPixmap KFileItem::pixmap( int _size, int _state ) const
+{
+ if (d && (!d->iconName.isEmpty()))
+ return DesktopIcon(d->iconName,_size,_state);
+
+ if ( !m_pMimeType )
+ {
+ static const TQString & defaultFolderIcon =
+ TDEGlobal::staticQString(KMimeType::mimeType( "inode/directory" )->KServiceType::icon());
+
+ if ( S_ISDIR( m_fileMode ) )
+ return DesktopIcon( defaultFolderIcon, _size, _state );
+
+ return DesktopIcon( "unknown", _size, _state );
+ }
+
+ _state |= overlays();
+
+ KMimeType::Ptr mime;
+ // Use guessed mimetype if the main one hasn't been determined for sure
+ if ( !m_bMimeTypeKnown && !m_guessedMimeType.isEmpty() )
+ mime = KMimeType::mimeType( m_guessedMimeType );
+ else
+ mime = m_pMimeType;
+
+ // Support for gzipped files: extract mimetype of contained file
+ // See also the relevant code in overlays, which adds the zip overlay.
+ if ( mime->name() == "application/x-gzip" && m_url.fileName().right(3) == ".gz" )
+ {
+ KURL sf;
+ sf.setPath( m_url.path().left( m_url.path().length() - 3 ) );
+ //kdDebug() << "KFileItem::pixmap subFileName=" << subFileName << endl;
+ mime = KMimeType::findByURL( sf, 0, m_bIsLocalURL );
+ }
+
+ bool isLocalURL;
+ KURL url = mostLocalURL(isLocalURL);
+
+ TQPixmap p = mime->pixmap( url, KIcon::Desktop, _size, _state );
+ //kdDebug() << "finding pixmap for " << url.url() << " : " << mime->name() << endl;
+ if (p.isNull())
+ kdWarning() << "Pixmap not found for mimetype " << m_pMimeType->name() << endl;
+
+ if ( mime->name() == "application/x-executable" ) {
+ // At first glance it might seem to be a good idea to
+ // look for .desktop files for this executable before resorting to the embedded icon
+ // in the same fashion as the minicli, but on close examination this is NOT A GOOD IDEA.
+ // Specifically it allows one executable to mimic another purely based on filename,
+ // which could at certain times fool any user regardless of experience level.
+#ifdef HAVE_ELFICON
+ // Check for an embedded icon
+ unsigned int icon_size;
+ libr_icon *icon = NULL;
+ libr_file *handle = NULL;
+ libr_access_t access = LIBR_READ;
+
+ if((handle = libr_open(const_cast<char*>(url.path().ascii()), access)) == NULL)
+ {
+ kdWarning() << "failed to open file" << url.path() << endl;
+ return p;
+ }
+
+ icon_size = _size;
+ icon = libr_icon_geticon_bysize(handle, icon_size);
+
+ // See if the embedded icon name matches any icon file names already on the system
+ // If it does, use the system icon instead of the embedded one
+ int iconresnamefound = 0;
+ iconentry *entry = NULL;
+ iconlist icons;
+ if(!get_iconlist(handle, &icons))
+ {
+ // Failed to obtain a list of ELF icons
+ kdWarning() << "failed to obtain ELF icon: " << libr_errmsg() << endl;
+
+ // See if there is a system icon we can use
+ TQString sysIconName = elf_get_resource(handle, ".metadata_sysicon");
+ if (!sysIconName.isEmpty()) {
+ if (TDEGlobal::iconLoader()->iconPath(sysIconName.ascii(), 0, true) != "") {
+ p = DesktopIcon( sysIconName.ascii(), _size, _state );
+ }
+ }
+
+ libr_close(handle);
+ return p;
+ }
+ else {
+ while((entry = get_nexticon(&icons, entry)) != NULL)
+ {
+ if(icon == NULL)
+ {
+ // Try loading this icon as fallback
+ icon = libr_icon_geticon_byname(handle, entry->name);
+ }
+ if (TDEGlobal::iconLoader()->iconPath(entry->name, 0, true) != "") {
+ iconresnamefound = 1;
+ p = DesktopIcon( entry->name, _size, _state );
+ break;
+ }
+ }
+ }
+
+ if ((iconresnamefound == 0) && (icon)) {
+ // Extract the embedded icon
+ size_t icon_data_length;
+ char* icondata = libr_icon_malloc(icon, &icon_data_length);
+ p.loadFromData(static_cast<uchar*>(static_cast<void*>(icondata)), icon_data_length); // EVIL CAST
+ if (icon_size != 0) {
+ TQImage ip = p.convertToImage();
+ ip = ip.smoothScale(icon_size, icon_size);
+ p.convertFromImage(ip);
+ }
+ free(icondata);
+ libr_icon_close(icon);
+ }
+
+ libr_close(handle);
+#endif // HAVE_ELFICON
+ }
+
+ return p;
+}
+
+bool KFileItem::isReadable() const
+{
+ /*
+ struct passwd * user = getpwuid( geteuid() );
+ bool isMyFile = (TQString::fromLocal8Bit(user->pw_name) == m_user);
+ // This gets ugly for the group....
+ // Maybe we want a static TQString for the user and a static QStringList
+ // for the groups... then we need to handle the deletion properly...
+ */
+
+ if ( m_permissions != KFileItem::Unknown ) {
+ // No read permission at all
+ if ( !(S_IRUSR & m_permissions) && !(S_IRGRP & m_permissions) && !(S_IROTH & m_permissions) )
+ return false;
+
+ // Read permissions for all: save a stat call
+ if ( (S_IRUSR|S_IRGRP|S_IROTH) & m_permissions )
+ return true;
+ }
+
+ // Or if we can't read it [using ::access()] - not network transparent
+ if ( m_bIsLocalURL && ::access( TQFile::encodeName(m_url.path()), R_OK ) == -1 )
+ return false;
+
+ return true;
+}
+
+bool KFileItem::isWritable() const
+{
+ /*
+ struct passwd * user = getpwuid( geteuid() );
+ bool isMyFile = (TQString::fromLocal8Bit(user->pw_name) == m_user);
+ // This gets ugly for the group....
+ // Maybe we want a static TQString for the user and a static QStringList
+ // for the groups... then we need to handle the deletion properly...
+ */
+
+ if ( m_permissions != KFileItem::Unknown ) {
+ // No write permission at all
+ if ( !(S_IWUSR & m_permissions) && !(S_IWGRP & m_permissions) && !(S_IWOTH & m_permissions) )
+ return false;
+ }
+
+ // Or if we can't read it [using ::access()] - not network transparent
+ if ( m_bIsLocalURL && ::access( TQFile::encodeName(m_url.path()), W_OK ) == -1 )
+ return false;
+
+ return true;
+}
+
+bool KFileItem::isHidden() const
+{
+ if ( m_hidden != Auto )
+ return m_hidden == Hidden;
+
+ if ( !m_url.isEmpty() )
+ return m_url.fileName()[0] == '.';
+ else // should never happen
+ return m_strName[0] == '.';
+}
+
+bool KFileItem::isDir() const
+{
+ if ( m_fileMode == KFileItem::Unknown )
+ {
+ kdDebug() << " KFileItem::isDir can't say -> false " << endl;
+ return false; // can't say for sure, so no
+ }
+ return (S_ISDIR(m_fileMode));
+/*
+ if (!S_ISDIR(m_fileMode)) {
+ if (m_url.isLocalFile()) {
+ KMimeType::Ptr ptr=KMimeType::findByURL(m_url,0,true,true);
+ if ((ptr!=0) && (ptr->is("directory/inode"))) return true;
+ }
+ return false
+ } else return true;*/
+}
+
+bool KFileItem::acceptsDrops()
+{
+ // A directory ?
+ if ( S_ISDIR( mode() ) ) {
+ return isWritable();
+ }
+
+ // But only local .desktop files and executables
+ if ( !m_bIsLocalURL )
+ return false;
+
+ if (( mimetype() == "application/x-desktop") ||
+ ( mimetype() == "media/builtin-mydocuments") ||
+ ( mimetype() == "media/builtin-mycomputer") ||
+ ( mimetype() == "media/builtin-mynetworkplaces") ||
+ ( mimetype() == "media/builtin-printers") ||
+ ( mimetype() == "media/builtin-trash") ||
+ ( mimetype() == "media/builtin-webbrowser"))
+ return true;
+
+ // Executable, shell script ... ?
+ if ( ::access( TQFile::encodeName(m_url.path()), X_OK ) == 0 )
+ return true;
+
+ return false;
+}
+
+TQString KFileItem::getStatusBarInfo()
+{
+ TQString text = m_strText;
+
+ if ( m_bLink )
+ {
+ TQString comment = determineMimeType()->comment( m_url, m_bIsLocalURL );
+ TQString tmp;
+ if ( comment.isEmpty() )
+ tmp = i18n ( "Symbolic Link" );
+ else
+ tmp = i18n("%1 (Link)").arg(comment);
+ text += "->";
+ text += linkDest();
+ text += " ";
+ text += tmp;
+ }
+ else if ( S_ISREG( m_fileMode ) )
+ {
+ bool hasSize;
+ TDEIO::filesize_t sizeValue = size(hasSize);
+ if(hasSize)
+ text += TQString(" (%1) ").arg( TDEIO::convertSize( sizeValue ) );
+ text += mimeComment();
+ }
+ else if ( S_ISDIR ( m_fileMode ) )
+ {
+ text += "/ ";
+ text += mimeComment();
+ }
+ else
+ {
+ text += " ";
+ text += mimeComment();
+ }
+ text.replace('\n', " "); // replace any newlines with a space, so the statusbar doesn't get a two-line string which messes the display up, Alex
+ return text;
+}
+
+TQString KFileItem::getToolTipText(int maxcount)
+{
+ // we can return TQString::null if no tool tip should be shown
+ TQString tip;
+ KFileMetaInfo info = metaInfo();
+
+ // the font tags are a workaround for the fact that the tool tip gets
+ // screwed if the color scheme uses white as default text color
+ const char* start = "<tr><td><nobr><font color=\"black\">";
+ const char* mid = "</font></nobr></td><td><nobr><font color=\"black\">";
+ const char* end = "</font></nobr></td></tr>";
+
+ tip = "<table cellspacing=0 cellpadding=0>";
+
+ tip += start + i18n("Name:") + mid + text() + end;
+ tip += start + i18n("Type:") + mid;
+
+ TQString type = TQStyleSheet::escape(mimeComment());
+ if ( m_bLink ) {
+ tip += i18n("Link to %1 (%2)").arg(linkDest(), type) + end;
+ } else
+ tip += type + end;
+
+ if ( !S_ISDIR ( m_fileMode ) ) {
+ bool hasSize;
+ TDEIO::filesize_t sizeValue = size(hasSize);
+ if(hasSize)
+ tip += start + i18n("Size:") + mid +
+ TDEIO::convertSizeWithBytes(sizeValue) + end;
+ }
+ TQString timeStr = timeString( TDEIO::UDS_MODIFICATION_TIME);
+ if(!timeStr.isEmpty())
+ tip += start + i18n("Modified:") + mid +
+ timeStr + end;
+#ifndef Q_WS_WIN //TODO: show win32-specific permissions
+ TQString userStr = user();
+ TQString groupStr = group();
+ if(!userStr.isEmpty() || !groupStr.isEmpty())
+ tip += start + i18n("Owner:") + mid + userStr + " - " + groupStr + end +
+ start + i18n("Permissions:") + mid +
+ parsePermissions(m_permissions) + end;
+#endif
+
+ if (info.isValid() && !info.isEmpty() )
+ {
+ tip += "<tr><td colspan=2><center><s>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</s></center></td></tr>";
+ TQStringList keys = info.preferredKeys();
+
+ // now the rest
+ TQStringList::Iterator it = keys.begin();
+ for (int count = 0; count<maxcount && it!=keys.end() ; ++it)
+ {
+ KFileMetaInfoItem item = info.item( *it );
+ if ( item.isValid() )
+ {
+ TQString s = item.string();
+ if ( ( item.attributes() & KFileMimeTypeInfo::SqueezeText )
+ && s.length() > 50) {
+ s.truncate(47);
+ s.append("...");
+ }
+ if ( !s.isEmpty() )
+ {
+ count++;
+ tip += start +
+ TQStyleSheet::escape( item.translatedKey() ) + ":" +
+ mid +
+ TQStyleSheet::escape( s ) +
+ end;
+ }
+
+ }
+ }
+ }
+ tip += "</table>";
+
+ //kdDebug() << "making this the tool tip rich text:\n";
+ //kdDebug() << tip << endl;
+
+ return tip;
+}
+
+void KFileItem::run()
+{
+ // It might be faster to pass skip that when we know the mimetype,
+ // and just call KRun::runURL. But then we need to use mostLocalURL()
+ // for application/x-desktop files, to be able to execute them.
+ (void) new KRun( m_url, m_fileMode, m_bIsLocalURL );
+}
+
+bool KFileItem::cmp( const KFileItem & item )
+{
+ bool hasSize1,hasSize2,hasTime1,hasTime2;
+ hasSize1 = hasSize2 = hasTime1 = hasTime2 = false;
+ return ( m_strName == item.m_strName
+ && m_bIsLocalURL == item.m_bIsLocalURL
+ && m_fileMode == item.m_fileMode
+ && m_permissions == item.m_permissions
+ && m_user == item.m_user
+ && m_group == item.m_group
+ && m_bLink == item.m_bLink
+ && m_hidden == item.m_hidden
+ && size(hasSize1) == item.size(hasSize2)
+ && hasSize1 == hasSize2
+ && time(TDEIO::UDS_MODIFICATION_TIME, hasTime1) == item.time(TDEIO::UDS_MODIFICATION_TIME, hasTime2)
+ && hasTime1 == hasTime2
+ && (!d || !item.d || d->iconName == item.d->iconName) );
+
+ // Don't compare the mimetypes here. They might not be known, and we don't want to
+ // do the slow operation of determining them here.
+}
+
+void KFileItem::assign( const KFileItem & item )
+{
+ if ( this == &item )
+ return;
+ m_entry = item.m_entry;
+ m_url = item.m_url;
+ m_bIsLocalURL = item.m_bIsLocalURL;
+ m_strName = item.m_strName;
+ m_strText = item.m_strText;
+ m_fileMode = item.m_fileMode;
+ m_permissions = item.m_permissions;
+ m_user = item.m_user;
+ m_group = item.m_group;
+ m_bLink = item.m_bLink;
+ m_pMimeType = item.m_pMimeType;
+ m_strLowerCaseName = item.m_strLowerCaseName;
+ m_bMimeTypeKnown = item.m_bMimeTypeKnown;
+ m_hidden = item.m_hidden;
+ m_guessedMimeType = item.m_guessedMimeType;
+ m_access = item.m_access;
+ m_metaInfo = item.m_metaInfo;
+ for ( int i = 0; i < NumFlags; i++ )
+ m_time[i] = item.m_time[i];
+ m_size = item.m_size;
+ // note: m_extra is NOT copied, as we'd have no control over who is
+ // deleting the data or not.
+
+ // We had a mimetype previously (probably), so we need to re-determine it
+ determineMimeType();
+
+ if ( item.d ) {
+ if ( !d )
+ d = new KFileItemPrivate;
+ d->iconName = item.d->iconName;
+ } else {
+ delete d;
+ d = 0;
+ }
+}
+
+void KFileItem::setUDSEntry( const TDEIO::UDSEntry& _entry, const KURL& _url,
+ bool _determineMimeTypeOnDemand, bool _urlIsDirectory )
+{
+ m_entry = _entry;
+ m_url = _url;
+ m_strName = TQString::null;
+ m_strText = TQString::null;
+ m_user = TQString::null;
+ m_group = TQString::null;
+ m_strLowerCaseName = TQString::null;
+ m_pMimeType = 0;
+ m_fileMode = KFileItem::Unknown;
+ m_permissions = KFileItem::Unknown;
+ m_bMarked = false;
+ m_bLink = false;
+ m_bIsLocalURL = _url.isLocalFile();
+ m_bMimeTypeKnown = false;
+ m_hidden = Auto;
+ m_guessedMimeType = TQString::null;
+ m_metaInfo = KFileMetaInfo();
+
+ if ( d )
+ d->iconName = TQString::null;
+
+ readUDSEntry( _urlIsDirectory );
+ init( _determineMimeTypeOnDemand );
+}
+
+void KFileItem::setFileMode( mode_t m )
+{
+ m_fileMode = m;
+}
+
+void KFileItem::setMimeType( const TQString& mimetype )
+{
+ m_pMimeType = KMimeType::mimeType( mimetype );
+}
+
+void KFileItem::setExtraData( const void *key, void *value )
+{
+ if ( !key )
+ return;
+
+ m_extra.replace( key, value );
+}
+
+const void * KFileItem::extraData( const void *key ) const
+{
+ TQMapConstIterator<const void*,void*> it = m_extra.find( key );
+ if ( it != m_extra.end() )
+ return it.data();
+ return 0L;
+}
+
+void * KFileItem::extraData( const void *key )
+{
+ TQMapIterator<const void*,void*> it = m_extra.find( key );
+ if ( it != m_extra.end() )
+ return it.data();
+ return 0L;
+}
+
+void KFileItem::removeExtraData( const void *key )
+{
+ m_extra.remove( key );
+}
+
+TQString KFileItem::permissionsString() const
+{
+ if (m_access.isNull())
+ m_access = parsePermissions( m_permissions );
+
+ return m_access;
+}
+
+TQString KFileItem::parsePermissions(mode_t perm) const
+{
+ char p[] = "---------- ";
+
+ if (isDir())
+ p[0]='d';
+ else if (isLink())
+ p[0]='l';
+
+ if (perm & TQFileInfo::ReadUser)
+ p[1]='r';
+ if (perm & TQFileInfo::WriteUser)
+ p[2]='w';
+ if ((perm & TQFileInfo::ExeUser) && !(perm & S_ISUID)) p[3]='x';
+ else if ((perm & TQFileInfo::ExeUser) && (perm & S_ISUID)) p[3]='s';
+ else if (!(perm & TQFileInfo::ExeUser) && (perm & S_ISUID)) p[3]='S';
+
+ if (perm & TQFileInfo::ReadGroup)
+ p[4]='r';
+ if (perm & TQFileInfo::WriteGroup)
+ p[5]='w';
+ if ((perm & TQFileInfo::ExeGroup) && !(perm & S_ISGID)) p[6]='x';
+ else if ((perm & TQFileInfo::ExeGroup) && (perm & S_ISGID)) p[6]='s';
+ else if (!(perm & TQFileInfo::ExeGroup) && (perm & S_ISGID)) p[6]='S';
+
+ if (perm & TQFileInfo::ReadOther)
+ p[7]='r';
+ if (perm & TQFileInfo::WriteOther)
+ p[8]='w';
+ if ((perm & TQFileInfo::ExeOther) && !(perm & S_ISVTX)) p[9]='x';
+ else if ((perm & TQFileInfo::ExeOther) && (perm & S_ISVTX)) p[9]='t';
+ else if (!(perm & TQFileInfo::ExeOther) && (perm & S_ISVTX)) p[9]='T';
+
+ if (hasExtendedACL())
+ p[10]='+';
+
+ return TQString::fromLatin1(p);
+}
+
+// check if we need to cache this
+TQString KFileItem::timeString( unsigned int which ) const
+{
+ bool hasTime;
+ time_t time_ = time(which, hasTime);
+ if(!hasTime) return TQString::null;
+
+ TQDateTime t;
+ t.setTime_t( time_);
+ return TDEGlobal::locale()->formatDateTime( t );
+}
+
+void KFileItem::setMetaInfo( const KFileMetaInfo & info )
+{
+ m_metaInfo = info;
+}
+
+const KFileMetaInfo & KFileItem::metaInfo(bool autoget, int) const
+{
+ bool isLocalURL;
+ KURL url = mostLocalURL(isLocalURL);
+
+ if ( autoget && !m_metaInfo.isValid() &&
+ TDEGlobalSettings::showFilePreview(url) )
+ {
+ m_metaInfo = KFileMetaInfo( url, mimetype() );
+ }
+
+ return m_metaInfo;
+}
+
+KURL KFileItem::mostLocalURL(bool &local) const
+{
+ TQString local_path = localPath();
+
+ if ( !local_path.isEmpty() )
+ {
+ local = true;
+ KURL url;
+ url.setPath(local_path);
+ return url;
+ }
+ else
+ {
+ local = m_bIsLocalURL;
+ return m_url;
+ }
+}
+
+void KFileItem::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+TQDataStream & operator<< ( TQDataStream & s, const KFileItem & a )
+{
+ // We don't need to save/restore anything that refresh() invalidates,
+ // since that means we can re-determine those by ourselves.
+ s << a.m_url;
+ s << a.m_strName;
+ s << a.m_strText;
+ return s;
+}
+
+TQDataStream & operator>> ( TQDataStream & s, KFileItem & a )
+{
+ s >> a.m_url;
+ s >> a.m_strName;
+ s >> a.m_strText;
+ a.m_bIsLocalURL = a.m_url.isLocalFile();
+ a.m_bMimeTypeKnown = false;
+ a.refresh();
+ return s;
+}
diff --git a/tdeio/tdeio/tdefileitem.h b/tdeio/tdeio/tdefileitem.h
new file mode 100644
index 000000000..7097d7bc5
--- /dev/null
+++ b/tdeio/tdeio/tdefileitem.h
@@ -0,0 +1,671 @@
+/* This file is part of the KDE project
+ Copyright (C) 1999 David Faure <faure@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __tdefileitem_h__
+#define __tdefileitem_h__
+
+#include <tqstringlist.h>
+#include <sys/stat.h>
+
+#include <tqptrlist.h>
+#include <tdeio/global.h>
+#include <kurl.h>
+#include <kacl.h>
+#include <kmimetype.h>
+#include <tdefilemetainfo.h>
+
+#define KFILEITEM_HAS_ISWRITABLE // only used in libkonq/konq_iconviewwidget.cc, will be removed for 3.4
+
+/**
+ * A KFileItem is a generic class to handle a file, local or remote.
+ * In particular, it makes it easier to handle the result of TDEIO::listDir
+ * (UDSEntry isn't very friendly to use).
+ * It includes many file attributes such as mimetype, icon, text, mode, link...
+ */
+class TDEIO_EXPORT KFileItem
+{
+public:
+ enum { Unknown = (mode_t) - 1 };
+
+ /**
+ * Creates an item representing a file, from a UDSEntry.
+ * This is the preferred constructor when using TDEIO::listDir().
+ *
+ * @param _entry the KIO entry used to get the file, contains info about it
+ * @param _url the file url
+ * @param _determineMimeTypeOnDemand specifies if the mimetype of the given
+ * URL should be determined immediately or on demand
+ * @param _urlIsDirectory specifies if the url is just the directory of the
+ * fileitem and the filename from the UDSEntry should be used.
+ */
+ KFileItem( const TDEIO::UDSEntry& _entry, const KURL& _url,
+ bool _determineMimeTypeOnDemand = false,
+ bool _urlIsDirectory = false );
+
+ /**
+ * Creates an item representing a file, from all the necessary info for it.
+ * @param _mode the file mode (according to stat() (e.g. S_IFDIR...)
+ * Set to KFileItem::Unknown if unknown. For local files, KFileItem will use stat().
+ * @param _permissions the access permissions
+ * If you set both the mode and the permissions, you save a ::stat() for
+ * local files.
+ * Set to KFileItem::Unknown if you don't know the mode or the permission.
+ * @param _url the file url
+ *
+ * @param _determineMimeTypeOnDemand specify if the mimetype of the given URL
+ * should be determined immediately or on demand
+ */
+ KFileItem( mode_t _mode, mode_t _permissions, const KURL& _url,
+ bool _determineMimeTypeOnDemand = false );
+
+ /**
+ * Creates an item representing a file, for which the mimetype is already known.
+ * @param url the file url
+ * @param mimeType the name of the file's mimetype
+ * @param mode the mode (S_IFDIR...)
+ */
+ KFileItem( const KURL &url, const TQString &mimeType, mode_t mode );
+
+ /**
+ * Copy constructor. Note that extra-data set via setExtraData() is not
+ * deeply copied -- just the pointers are copied.
+ */
+ KFileItem( const KFileItem &item );
+
+ /**
+ * Destructs the KFileItem. Extra data set via setExtraData()
+ * is not deleted.
+ */
+ virtual ~KFileItem();
+
+ /**
+ * Throw away and re-read (for local files) all information about the file.
+ * This is called when the _file_ changes.
+ */
+ void refresh();
+
+ /**
+ * Re-reads mimetype information.
+ * This is called when the mimetype database changes.
+ */
+ void refreshMimeType();
+
+ /**
+ * Returns the url of the file.
+ * @return the url of the file
+ */
+ const KURL & url() const { return m_url; }
+
+ /**
+ * Sets the item's URL. Do not call unless you know what you are doing!
+ * (used for example when an item got renamed).
+ * @param url the item's URL
+ */
+ void setURL( const KURL &url );
+
+ /**
+ * Sets the item's name (i.e. the filename).
+ * This is automatically done by setURL, to set the name from the URL's fileName().
+ * This method is provided for some special cases like relative paths as names (KFindPart)
+ * @param name the item's name
+ */
+ void setName( const TQString &name );
+
+ /**
+ * Returns the permissions of the file (stat.st_mode containing only permissions).
+ * @return the permissions of the file
+ */
+ mode_t permissions() const { return m_permissions; }
+
+ /**
+ * Returns the access permissions for the file as a string.
+ * @return the access persmission as string
+ */
+ TQString permissionsString() const;
+
+ /**
+ * Tells if the file has extended access level information ( Posix ACL )
+ * @return true if the file has extend ACL information or false if it hasn't
+ * @since 3.5
+ */
+ bool hasExtendedACL() const;
+
+ /**
+ * Returns the access control list for the file.
+ * @return the access control list as a KACL
+ * @since 3.5
+ */
+ KACL ACL() const;
+
+ /**
+ * Returns the default access control list for the directory.
+ * @return the default access control list as a KACL
+ * @since 3.5
+ */
+ KACL defaultACL() const;
+
+ /**
+ * Returns the file type (stat.st_mode containing only S_IFDIR, S_IFLNK, ...).
+ * @return the file type
+ */
+ mode_t mode() const { return m_fileMode; }
+
+ /**
+ * Returns the owner of the file.
+ * @return the file's owner
+ */
+ TQString user() const;
+
+ /**
+ * Returns the group of the file.
+ * @return the file's group
+ */
+ TQString group() const;
+
+ /**
+ * Returns true if this item represents a link in the UNIX sense of
+ * a link.
+ * @return true if the file is a link
+ */
+ bool isLink() const { return m_bLink; }
+
+ /**
+ * Returns true if this item represents a directory.
+ * @return true if the item is a directory
+ */
+ bool isDir() const;
+
+ /**
+ * Returns true if this item represents a file (and not a a directory)
+ * @return true if the item is a file
+ */
+ bool isFile() const { return !isDir(); }
+
+ /**
+ * Checks whether the file or directory is readable. In some cases
+ * (remote files), we may return true even though it can't be read.
+ * @return true if the file can be read - more precisely,
+ * false if we know for sure it can't
+ */
+ bool isReadable() const;
+
+ /**
+ * Checks whether the file or directory is writable. In some cases
+ * (remote files), we may return true even though it can't be written to.
+ * @return true if the file or directory can be written to - more precisely,
+ * false if we know for sure it can't
+ * @since 3.4
+ */
+ bool isWritable() const;
+
+ /**
+ * Checks whether the file is hidden.
+ * @return true if the file is hidden.
+ */
+ bool isHidden() const;
+
+ /**
+ * Returns the link destination if isLink() == true.
+ * @return the link destination. TQString::null if the item is not a link
+ */
+ TQString linkDest() const;
+
+ /**
+ * Returns the local path if isLocalFile() == true or the KIO item has
+ * a UDS_LOCAL_PATH atom.
+ * @return the item local path, or TQString::null if not known
+ * @since 3.4
+ */
+ TQString localPath() const;
+
+ //FIXME KDE4 deprecate this in favor of size(bool &hasSize)
+ /**
+ * Returns the size of the file, if known.
+ * @return the file size, or 0 if not known
+ */
+ TDEIO::filesize_t size() const;
+
+ /**
+ * Returns the size of the file, if known, and sets @p hasSize to false if not known
+ * @param @hasSize This is set to true if the size is known, and false if not known
+ * @return the file size, or 0 if not known
+ */
+ TDEIO::filesize_t size(bool &hasSize) const;
+
+ //FIXME KDE4 deprecate this in favor of time(unsigned int which, bool &hasSize)
+ /**
+ * Requests the modification, access or creation time, depending on @p which.
+ * @param which UDS_MODIFICATION_TIME, UDS_ACCESS_TIME or UDS_CREATION_TIME
+ * @return the time asked for, (time_t)0 if not available
+ * @see timeString()
+ */
+ time_t time( unsigned int which ) const;
+
+ /**
+ * Requests the modification, access or creation time, depending on @p which.
+ * @param which UDS_MODIFICATION_TIME, UDS_ACCESS_TIME or UDS_CREATION_TIME
+ * @param hasTime This is set to true is the time is known, and false if not known
+ * @return the time asked for, (time_t)0 if not known/available
+ * @see timeString()
+ */
+ time_t time( unsigned int which, bool &hasTime ) const;
+
+ /**
+ * Requests the modification, access or creation time as a string, depending
+ * on @p which.
+ * @param which UDS_MODIFICATION_TIME, UDS_ACCESS_TIME or UDS_CREATION_TIME
+ * @returns a formatted string of the requested time, TQString::null if time is not known
+ * @see time
+ */
+ TQString timeString( unsigned int which = TDEIO::UDS_MODIFICATION_TIME ) const;
+
+ /**
+ * Returns true if the file is a local file.
+ * @return true if the file is local, false otherwise
+ */
+ bool isLocalFile() const { return m_bIsLocalURL; }
+
+ /**
+ * Returns the text of the file item.
+ * It's not exactly the filename since some decoding happens ('%2F'->'/').
+ * @return the text of the file item
+ */
+ const TQString& text() const { return m_strText; }
+
+ /**
+ * Return the name of the file item (without a path).
+ * Similar to text(), but unencoded, i.e. the original name.
+ * @param lowerCase if true, the name will be returned in lower case,
+ * which is useful to speed up sorting by name, case insensitively.
+ * @return the file's name
+ */
+ const TQString& name( bool lowerCase = false ) const {
+ if ( !lowerCase )
+ return m_strName;
+ else
+ if ( m_strLowerCaseName.isNull() )
+ m_strLowerCaseName = m_strName.lower();
+ return m_strLowerCaseName;
+ }
+
+ /**
+ * Returns the mimetype of the file item.
+ * If @p _determineMimeTypeOnDemand was used in the constructor, this will determine
+ * the mimetype first. Equivalent to determineMimeType()->name()
+ * @return the mime type of the file
+ */
+ TQString mimetype() const;
+
+ /**
+ * Returns the mimetype of the file item.
+ * If _determineMimeTypeOnDemand was used in the constructor, this will determine
+ * the mimetype first.
+ * @return the mime type
+ */
+ KMimeType::Ptr determineMimeType();
+
+ /**
+ * Returns the currently known mimetype of the file item.
+ * This will not try to determine the mimetype if unknown.
+ * @return the known mime type
+ */
+ KMimeType::Ptr mimeTypePtr() const { return m_pMimeType; }
+
+ bool isMimeTypeKnown() const;
+ /**
+ * Returns the descriptive comment for this mime type, or
+ * the mime type itself if none is present.
+ * @return the mime type description, or the mime type itself
+ */
+ TQString mimeComment();
+
+ /**
+ * Returns the full path name to the icon that represents
+ * this mime type.
+ * @return iconName the name of the file's icon
+ */
+ TQString iconName();
+
+ /**
+ * Returns a pixmap representing the file.
+ * @param _size Size for the pixmap in pixels. Zero will return the
+ * globally configured default size.
+ * @param _state The state of the icon: KIcon::DefaultState,
+ * KIcon::ActiveState or KIcon::DisabledState.
+ * @return the pixmap
+ */
+ TQPixmap pixmap( int _size, int _state=0 ) const;
+
+ /**
+ * Returns the overlays (bitfield of KIcon::*Overlay flags) that are used
+ * for this item's pixmap. Overlays are used to show for example, whether
+ * a file can be modified.
+ * @return the overlays of the pixmap
+ */
+ int overlays() const;
+
+ /**
+ * Returns the string to be displayed in the statusbar,
+ * e.g. when the mouse is over this item
+ * @return the status bar information
+ */
+ TQString getStatusBarInfo();
+
+ /**
+ * Returns the string to be displayed in the tool tip when the mouse
+ * is over this item. This may load a plugin to determine additional
+ * information specific to the mimetype of the file.
+ *
+ * @param maxcount the maximum number of entries shown
+ * @return the tool tip string
+ */
+ TQString getToolTipText(int maxcount = 6);
+
+ /**
+ * Returns true if files can be dropped over this item.
+ * Contrary to popular belief, not only dirs will return true :)
+ * Executables, .desktop files, will do so as well.
+ * @return true if you can drop files over the item
+ */
+ bool acceptsDrops( );
+
+ /**
+ * Let's "KRun" this file !
+ * (e.g. when file is clicked or double-clicked or return is pressed)
+ */
+ void run();
+
+ /**
+ * Returns the UDS entry. Used by the tree view to access all details
+ * by position.
+ * @return the UDS entry
+ */
+ const TDEIO::UDSEntry & entry() const { return m_entry; }
+
+ /**
+ * Used when updating a directory. marked == seen when refreshing.
+ * @return true if the file item is marked
+ */
+ bool isMarked() const { return m_bMarked; }
+ /**
+ * Marks the item.
+ * @see isMarked()
+ */
+ void mark() { m_bMarked = true; }
+ /**
+ * Unmarks the item.
+ * @see isMarked()
+ */
+ void unmark() { m_bMarked = false; }
+
+ /**
+ * Somewhat like a comparison operator, but more explicit.
+ * @param item the item to compare
+ * @return true if all values are equal
+ */
+ bool cmp( const KFileItem & item );
+
+ /**
+ * This allows to associate some "extra" data to a KFileItem. As one
+ * KFileItem can be used by several objects (often views) which all need
+ * to add some data, you have to use a key to reference your extra data
+ * within the KFileItem.
+ *
+ * That way a KFileItem can hold and provide access to all those views
+ * separately.
+ *
+ * I.e. a KFileIconView that associates a KFileIconViewItem (an item suitable
+ * for use with TQIconView) does
+ *
+ * \code
+ * tdefileItem->setExtraData( this, iconViewItem );
+ * \endcode
+ *
+ * and can later access the iconViewItem by doing
+ *
+ * \code
+ * KFileIconViewItem *iconViewItem = static_cast<KFileIconViewItem*>( tdefileItem->extraData( this ));
+ * \endcode
+ *
+ * This is usually more efficient then having every view associate data to
+ * items by using a separate TQDict or TQMap.
+ *
+ * Note: you have to remove and destroy the data you associated yourself
+ * when you don't need it anymore!
+ *
+ * @param key the key of the extra data
+ * @param value the value of the extra data
+ * @see extraData
+ * @see removeExtraData
+ */
+ virtual void setExtraData( const void *key, void *value );
+
+ /**
+ * Retrieves the extra data with the given @p key.
+ * @param key the key of the extra data
+ * @return the extra data associated to an item with @p key via
+ * setExtraData. 0L if nothing was associated with @p key.
+ * @see extraData
+ */
+ virtual const void * extraData( const void *key ) const;
+
+ /**
+ * Retrieves the extra data with the given @p key.
+ * @param key the key of the extra data
+ * @return the extra data associated to an item with @p key via
+ * setExtraData. 0L if nothing was associated with @p key.
+ * @see extraData
+ */
+ virtual void * extraData( const void *key );
+
+ /**
+ * Removes the extra data associated with an item via @p key.
+ * @param key the key of the extra data to remove
+ */
+ virtual void removeExtraData( const void *key );
+
+ /**
+ * Sets the metainfo of this item to @p info.
+ * @param info the new meta info
+ */
+ void setMetaInfo( const KFileMetaInfo & info );
+
+ /**
+ * Sets the file type (stat.st_mode containing only S_IFDIR, S_IFLNK, ...).
+ * @param m the new file type
+ * @since 3.5.0
+ * @todo Actually explain what this does -- does setting S_IFDIR
+ * mean the file type is set to Directory?
+ */
+ void setFileMode( mode_t m );
+
+ /**
+ * Sets new mimetype for item
+ * @param mimetype the new mimetype
+ * @since 3.5.0
+ */
+ void setMimeType( const TQString& mimetype );
+
+ /**
+ * Returns the metainfo of this item.
+ * @param autoget if true, the metainfo will automatically be created
+ * @param what ignored
+ */
+ const KFileMetaInfo & metaInfo(bool autoget = true,
+ int what = KFileMetaInfo::Fastest) const;
+
+ /**
+ * Somewhat like an assignment operator, but more explicit.
+ * Note: extra-data set with setExtraData() is not copied, so be careful
+ * what you do!
+ *
+ * @param item the item to copy
+ */
+ void assign( const KFileItem & item );
+
+ /**
+ * Reinitialize KFileItem with a new UDSEntry.
+ *
+ * Note: extra-data set with setExtraData() is not changed or deleted, so
+ * be careful what you do!
+ *
+ * KDirListerCache uses it to save new/delete calls by updating existing
+ * items that are otherwise not needed anymore.
+ *
+ * @param entry the UDSEntry to assign to this KFileItem
+ * @param url the file url
+ * @param determineMimeTypeOnDemand specifies if the mimetype of the given
+ * URL should be determined immediately or on demand
+ * @param urlIsDirectory specifies if the url is just the directory of the
+ * fileitem and the filename from the UDSEntry should be used.
+ * @since 3.4.1
+ */
+ void setUDSEntry( const TDEIO::UDSEntry& entry, const KURL& url,
+ bool determineMimeTypeOnDemand = false,
+ bool urlIsDirectory = false );
+
+ /**
+ * Assignment operator, calls assign()
+ */
+ KFileItem& operator=( const KFileItem& );
+
+ /**
+ * Tries to give a local URL for this file item if possible.
+ * The given boolean indicates if the returned url is local or not.
+ */
+ KURL mostLocalURL(bool &local) const;
+
+ /////////////
+
+protected:
+ /**
+ * Computes the text, mode, and mimetype from the UDSEntry
+ * Called by constructor, but can be called again later
+ */
+ void init( bool _determineMimeTypeOnDemand );
+
+ /**
+ * Extracts the data from the UDSEntry member and updates the KFileItem
+ * accordingly.
+ * @since 3.4.1
+ */
+ void readUDSEntry( bool _urlIsDirectory );
+
+ /**
+ * Parses the given permission set and provides it for access()
+ */
+ TQString parsePermissions( mode_t perm ) const;
+
+private:
+ /**
+ * We keep a copy of the UDSEntry since we need it for getStatusBarInfo
+ */
+ TDEIO::UDSEntry m_entry;
+ /**
+ * The url of the file
+ */
+ KURL m_url;
+
+ /**
+ * The text for this item, i.e. the file name without path,
+ */
+ TQString m_strName;
+
+ /**
+ * The text for this item, i.e. the file name without path, decoded
+ * ('%%' becomes '%', '%2F' becomes '/')
+ */
+ TQString m_strText;
+
+ /**
+ * the user and group assigned to the file.
+ */
+ mutable TQString m_user, m_group;
+
+ /**
+ * The filename in lower case (to speed up sorting)
+ */
+ mutable TQString m_strLowerCaseName;
+
+ /**
+ * The mimetype of the file
+ */
+ KMimeType::Ptr m_pMimeType;
+
+ /**
+ * The file mode
+ */
+ mode_t m_fileMode;
+ /**
+ * The permissions
+ */
+ mode_t m_permissions;
+
+ /**
+ * Marked : see mark()
+ */
+ bool m_bMarked:1;
+ /**
+ * Whether the file is a link
+ */
+ bool m_bLink:1;
+ /**
+ * True if local file
+ */
+ bool m_bIsLocalURL:1;
+
+ bool m_bMimeTypeKnown:1;
+
+ // Auto: check leading dot.
+ enum { Auto, Hidden, Shown } m_hidden:3;
+
+ // For special case like link to dirs over FTP
+ TQString m_guessedMimeType;
+ mutable TQString m_access;
+ TQMap<const void*, void*> m_extra;
+ mutable KFileMetaInfo m_metaInfo;
+
+ enum { Modification = 0, Access = 1, Creation = 2, NumFlags = 3 };
+ mutable time_t m_time[3];
+ mutable TDEIO::filesize_t m_size;
+
+protected:
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KFileItemPrivate;
+ KFileItemPrivate * d;
+ TDEIO_EXPORT friend TQDataStream & operator<< ( TQDataStream & s, const KFileItem & a );
+ TDEIO_EXPORT friend TQDataStream & operator>> ( TQDataStream & s, KFileItem & a );
+};
+
+/**
+ * List of KFileItems
+ */
+typedef TQPtrList<KFileItem> KFileItemList;
+
+/**
+ * Iterator for KFileItemList
+ */
+typedef TQPtrListIterator<KFileItem> KFileItemListIterator;
+
+TDEIO_EXPORT TQDataStream & operator<< ( TQDataStream & s, const KFileItem & a );
+TDEIO_EXPORT TQDataStream & operator>> ( TQDataStream & s, KFileItem & a );
+
+
+#endif
diff --git a/tdeio/tdeio/tdefilemetainfo.cpp b/tdeio/tdeio/tdefilemetainfo.cpp
new file mode 100644
index 000000000..4943dea93
--- /dev/null
+++ b/tdeio/tdeio/tdefilemetainfo.cpp
@@ -0,0 +1,1859 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2001-2002 Rolf Magnus <ramagnus@kde.org>
+ * Copyright (C) 2001-2002 Carsten Pfeiffer <pfeiffer@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation version 2.0.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ * $Id$
+ */
+
+#include <assert.h>
+
+#include <tqshared.h>
+#include <tqdict.h>
+
+#include <ktrader.h>
+#include <kstaticdeleter.h>
+#include <tdeparts/componentfactory.h>
+#include <kuserprofile.h>
+#include <kdebug.h>
+#include <kmimetype.h>
+#include <kdatastream.h> // needed for serialization of bool
+#include <klocale.h>
+#include <tdeio/global.h>
+
+#include "tdefilemetainfo.h"
+
+// shared data of a KFileMetaInfoItem
+class KFileMetaInfoItem::Data : public TQShared
+{
+public:
+ Data( const KFileMimeTypeInfo::ItemInfo* mti, const TQString& _key,
+ const TQVariant& _value )
+ : TQShared(),
+ mimeTypeInfo( mti ),
+ key( _key ),
+ value( _value ),
+ dirty( false ),
+ added( false ),
+ removed( false )
+ {}
+
+ // we use this one for the streaming operators
+ Data() : mimeTypeInfo( 0L )
+ {}
+
+ ~Data()
+ {
+ if ( this == null ) // only the null item owns its mimeTypeInfo
+ delete mimeTypeInfo;
+ }
+
+ const KFileMimeTypeInfo::ItemInfo* mimeTypeInfo;
+ // mimeTypeInfo has the key, too, but only for non-variable ones
+ TQString key;
+ TQVariant value;
+ bool dirty :1;
+ bool added :1;
+ bool removed :1;
+
+ static Data* null;
+ static Data* makeNull();
+};
+
+//this is our null data
+KFileMetaInfoItem::Data* KFileMetaInfoItem::Data::null = 0L;
+static KStaticDeleter<KFileMetaInfoItem::Data> sd_KFileMetaInfoItemData;
+
+KFileMetaInfoItem::Data* KFileMetaInfoItem::Data::makeNull()
+{
+ if (!null)
+ {
+ // We deliberately do not reset "null" after it has been destroyed!
+ // Otherwise we will run into problems later in ~KFileMetaInfoItem
+ // where the d-pointer is compared against null.
+
+ KFileMimeTypeInfo::ItemInfo* info = new KFileMimeTypeInfo::ItemInfo();
+ null = new Data(info, TQString::null, TQVariant());
+ sd_KFileMetaInfoItemData.setObject( null );
+ }
+ return null;
+}
+
+KFileMetaInfoItem::KFileMetaInfoItem( const KFileMimeTypeInfo::ItemInfo* mti,
+ const TQString& key, const TQVariant& value )
+ : d( new Data( mti, key, value ) )
+{
+}
+
+KFileMetaInfoItem::KFileMetaInfoItem( const KFileMetaInfoItem& item )
+{
+ // operator= does everything that's necessary
+ d = Data::makeNull();
+ *this = item;
+}
+
+KFileMetaInfoItem::KFileMetaInfoItem()
+{
+ d = Data::makeNull();
+}
+
+KFileMetaInfoItem::~KFileMetaInfoItem()
+{
+ deref();
+}
+
+const KFileMetaInfoItem& KFileMetaInfoItem::operator=
+ (const KFileMetaInfoItem & item )
+{
+ if (d != item.d)
+ {
+ // first deref the old one
+ deref();
+ d = item.d;
+ // and now ref the new one
+ ref();
+ }
+
+ return *this;
+}
+
+bool KFileMetaInfoItem::setValue( const TQVariant& value )
+{
+ // We don't call makeNull here since it isn't necassery, see deref()
+ if ( d == Data::null ) return false;
+
+ if ( ! (d->mimeTypeInfo->attributes() & KFileMimeTypeInfo::Modifiable ) ||
+ ! (value.canCast(d->mimeTypeInfo->type())))
+ {
+ kdDebug(7033) << "setting the value of " << key() << "failed\n";
+ return false;
+ }
+
+// kdDebug(7033) << key() << ".setValue()\n";
+
+ if ( d->value == value )
+ return true;
+
+ d->dirty = true;
+ d->value = value;
+ // If we don't cast (and test for canCast in the above if), TQVariant is
+ // very picky about types (e.g. TQString vs. TQCString or int vs. uint)
+ d->value.cast(d->mimeTypeInfo->type());
+
+ return true;
+}
+
+bool KFileMetaInfoItem::isRemoved() const
+{
+ return d->removed;
+}
+
+TQString KFileMetaInfoItem::key() const
+{
+ return d->key;
+}
+
+TQString KFileMetaInfoItem::translatedKey() const
+{
+ // are we a variable key?
+ if (d->mimeTypeInfo->key().isNull())
+ {
+ // then try if we have luck with i18n()
+ return i18n(d->key.utf8());
+ }
+
+ return d->mimeTypeInfo->translatedKey();
+}
+
+const TQVariant& KFileMetaInfoItem::value() const
+{
+ return d->value;
+}
+
+TQString KFileMetaInfoItem::string( bool mangle ) const
+{
+ return d->mimeTypeInfo->string(d->value, mangle);
+}
+
+TQVariant::Type KFileMetaInfoItem::type() const
+{
+ return d->mimeTypeInfo->type();
+}
+
+uint KFileMetaInfoItem::unit() const
+{
+ return d->mimeTypeInfo->unit();
+}
+
+bool KFileMetaInfoItem::isModified() const
+{
+ return d->dirty;
+}
+
+TQString KFileMetaInfoItem::prefix() const
+{
+ return d->mimeTypeInfo->prefix();
+}
+
+TQString KFileMetaInfoItem::suffix() const
+{
+ return d->mimeTypeInfo->suffix();
+}
+
+uint KFileMetaInfoItem::hint() const
+{
+ return d->mimeTypeInfo->hint();
+}
+
+uint KFileMetaInfoItem::attributes() const
+{
+ return d->mimeTypeInfo->attributes();
+}
+
+bool KFileMetaInfoItem::isEditable() const
+{
+ return d->mimeTypeInfo->attributes() & KFileMimeTypeInfo::Modifiable;
+}
+
+bool KFileMetaInfoItem::isValid() const
+{
+ // We don't call makeNull here since it isn't necassery:
+ // If d is equal to null it means that null is initialized already.
+ // null is 0L when it hasn't been initialized and d is never 0L.
+ return d != Data::null;
+}
+
+void KFileMetaInfoItem::setAdded()
+{
+ d->added = true;
+}
+
+void KFileMetaInfoItem::setRemoved()
+{
+ d->removed = true;
+}
+
+void KFileMetaInfoItem::ref()
+{
+ if (d != Data::null) d->ref();
+}
+
+void KFileMetaInfoItem::deref()
+{
+ // We don't call makeNull here since it isn't necassery:
+ // If d is equal to null it means that null is initialized already.
+ // null is 0L when it hasn't been initialized and d is never 0L.
+ if ((d != Data::null) && d->deref())
+ {
+// kdDebug(7033) << "item " << d->key
+// << " is finally deleted\n";
+ delete d;
+ d = 0;
+ }
+}
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+
+// shared data of a KFileMetaInfo
+class KFileMetaInfo::Data : public TQShared
+{
+public:
+ Data(const KURL& _url, uint _what)
+ : TQShared(),
+ url(_url),
+ what(_what),
+ mimeTypeInfo( 0L )
+ {}
+
+ // wee use this one for the streaming operators
+ Data() {};
+
+ KURL url;
+ uint what;
+ TQMap<TQString, KFileMetaInfoGroup> groups;
+ const KFileMimeTypeInfo* mimeTypeInfo;
+ TQStringList removedGroups;
+
+ static Data* null;
+ static Data* makeNull();
+
+};
+
+KFileMetaInfo::KFileMetaInfo( const TQString& path, const TQString& mimeType,
+ uint what )
+{
+ KURL u;
+
+ u.setPath(path);
+ init(u, mimeType, what);
+}
+
+KFileMetaInfo::KFileMetaInfo( const KURL& url, const TQString& mimeType,
+ uint what )
+{
+ init(url, mimeType, what);
+}
+
+void KFileMetaInfo::init( const KURL& url, const TQString& mimeType,
+ uint what )
+{
+ d = new Data( url, what );
+
+ TQString mT;
+ if (mimeType.isEmpty())
+ mT = KMimeType::findByURL(url)->name();
+ else
+ mT = mimeType;
+
+ // let's "share our property"
+ KFileMetaInfo item(*this);
+
+ //kdDebug() << k_funcinfo << mT << " " << url << endl;
+
+ d->mimeTypeInfo = KFileMetaInfoProvider::self()->mimeTypeInfo( mT, url.protocol() );
+ if ( d->mimeTypeInfo )
+ {
+ //kdDebug(7033) << "Found mimetype info for " << mT /* or protocol*/ << endl;
+ KFilePlugin *p = plugin();
+ Q_ASSERT( p );
+ if ( p && !p->readInfo( item, what) )
+ {
+ deref();
+ d = Data::makeNull();
+ }
+ }
+ else
+ {
+// kdDebug(7033) << "No mimetype info for " << mimeType << endl;
+ deref();
+ d = Data::makeNull();
+ }
+}
+
+KFileMetaInfo::KFileMetaInfo( const KFileMetaInfo& original )
+{
+ // operator= does everything that's necessary
+ d = Data::makeNull();
+ *this = original;
+}
+
+KFileMetaInfo::KFileMetaInfo()
+{
+ d = Data::makeNull();
+}
+
+KFileMetaInfo::~KFileMetaInfo()
+{
+ deref();
+}
+
+TQStringList KFileMetaInfo::supportedGroups() const
+{
+ assert(isValid());
+ return d->mimeTypeInfo->supportedGroups();
+}
+
+TQStringList KFileMetaInfo::supportedKeys() const
+{
+ assert(isValid());
+ return d->mimeTypeInfo->supportedKeys();
+}
+
+TQStringList KFileMetaInfo::groups() const
+{
+ TQStringList list;
+ TQMapConstIterator<TQString, KFileMetaInfoGroup> it = d->groups.begin();
+ for ( ; it != d->groups.end(); ++it )
+ list += (*it).name();
+
+ return list;
+}
+
+TQStringList KFileMetaInfo::editableGroups() const
+{
+ TQStringList list;
+ TQStringList supported = supportedGroups();
+ TQStringList::ConstIterator it = supported.begin();
+ for ( ; it != supported.end(); ++it ) {
+ const KFileMimeTypeInfo::GroupInfo * groupInfo = d->mimeTypeInfo->groupInfo( *it );
+ if ( groupInfo && groupInfo->attributes() &
+ (KFileMimeTypeInfo::Addable | KFileMimeTypeInfo::Removable) )
+ list.append( *it );
+ }
+
+ return list;
+}
+
+TQStringList KFileMetaInfo::preferredGroups() const
+{
+ assert(isValid());
+ TQStringList list = groups();
+ TQStringList newlist;
+ TQStringList preferred = d->mimeTypeInfo->preferredGroups();
+ TQStringList::Iterator pref;
+
+ // move all keys from the preferred groups that are in our list to a new list
+ for ( pref = preferred.begin(); pref != preferred.end(); ++pref )
+ {
+ TQStringList::Iterator group = list.find(*pref);
+ if ( group != list.end() )
+ {
+ newlist.append( *group );
+ list.remove(group);
+ }
+ }
+
+ // now the old list only contains the non-preferred items, so we
+ // add the remaining ones to newlist
+ newlist += list;
+
+ return newlist;
+}
+
+TQStringList KFileMetaInfo::preferredKeys() const
+{
+ TQStringList newlist;
+
+ TQStringList list = preferredGroups();
+ for (TQStringList::Iterator git = list.begin(); git != list.end(); ++git)
+ {
+ newlist += d->groups[*git].preferredKeys();
+ }
+
+ return newlist;
+}
+
+KFileMetaInfoGroup KFileMetaInfo::group(const TQString& key) const
+{
+ TQMapIterator<TQString,KFileMetaInfoGroup> it = d->groups.find( key );
+ if ( it != d->groups.end() )
+ return it.data();
+ else
+ return KFileMetaInfoGroup();
+}
+
+bool KFileMetaInfo::addGroup( const TQString& name )
+{
+ assert(isValid());
+ if ( d->mimeTypeInfo->supportedGroups().contains(name) &&
+ ! d->groups.contains(name) )
+ {
+ KFileMetaInfoGroup group( name, d->mimeTypeInfo );
+
+ // add all the items that can't be added by the user later
+ const KFileMimeTypeInfo::GroupInfo* ginfo = d->mimeTypeInfo->groupInfo(name);
+ Q_ASSERT(ginfo);
+ if (!ginfo) return false;
+
+ TQStringList keys = ginfo->supportedKeys();
+ for (TQStringList::Iterator it = keys.begin(); it != keys.end(); ++it)
+ {
+ const KFileMimeTypeInfo::ItemInfo* iteminfo = ginfo->itemInfo(*it);
+ Q_ASSERT(ginfo);
+ if (!iteminfo) return false;
+
+ if ( !(iteminfo->attributes() & KFileMimeTypeInfo::Addable) &&
+ (iteminfo->attributes() & KFileMimeTypeInfo::Modifiable))
+ {
+ // append it now or never
+ group.appendItem(iteminfo->key(), TQVariant());
+ }
+
+ }
+
+ d->groups.insert(name, group);
+ group.setAdded();
+ return true;
+ }
+
+ return false;
+}
+
+bool KFileMetaInfo::removeGroup( const TQString& name )
+{
+ TQMapIterator<TQString, KFileMetaInfoGroup> it = d->groups.find(name);
+ if ( (it==d->groups.end()) ||
+ !((*it).attributes() & KFileMimeTypeInfo::Removable))
+ return false;
+
+ d->groups.remove(it);
+ d->removedGroups.append(name);
+ return true;
+}
+
+TQStringList KFileMetaInfo::removedGroups()
+{
+ return d->removedGroups;
+}
+
+const KFileMetaInfo& KFileMetaInfo::operator= (const KFileMetaInfo& info )
+{
+ if (d != info.d)
+ {
+ deref();
+ // first deref the old one
+ d = info.d;
+ // and now ref the new one
+ ref();
+ }
+ return *this;
+}
+
+bool KFileMetaInfo::isValid() const
+{
+ // We don't call makeNull here since it isn't necassery, see deref()
+ return d != Data::null;
+}
+
+bool KFileMetaInfo::isEmpty() const
+{
+ for (TQMapIterator<TQString, KFileMetaInfoGroup> it = d->groups.begin();
+ it!=d->groups.end(); ++it)
+ if (!(*it).isEmpty())
+ return false;
+ return true;
+}
+
+bool KFileMetaInfo::applyChanges()
+{
+ return applyChanges( path() );
+}
+
+bool KFileMetaInfo::applyChanges( const TQString& path )
+{
+ bool doit = false;
+
+// kdDebug(7033) << "KFileMetaInfo::applyChanges()\n";
+
+ // look up if we need to write to the file
+ TQMapConstIterator<TQString, KFileMetaInfoGroup> it;
+ for (it = d->groups.begin(); it!=d->groups.end() && !doit; ++it)
+ {
+ if ( (*it).isModified() )
+ doit = true;
+
+ else
+ {
+ TQStringList keys = it.data().keys();
+ for (TQStringList::Iterator it2 = keys.begin(); it2!=keys.end(); ++it2)
+ {
+ if ( (*it)[*it2].isModified() )
+ {
+ doit = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (!doit)
+ {
+ kdDebug(7033) << "Don't need to write, nothing changed\n";
+ return true;
+ }
+
+ KFilePlugin* p = plugin();
+ if (!p) return false;
+
+// kdDebug(7033) << "Ok, trying to write the info\n";
+
+ KURL savedURL = url();
+ d->url = KURL();
+ d->url.setPath( path );
+
+ bool ret = p->writeInfo(*this);
+
+ d->url = savedURL;
+ return ret;
+}
+
+KFilePlugin * KFileMetaInfo::plugin() const
+{
+ assert(isValid());
+ KFileMetaInfoProvider* prov = KFileMetaInfoProvider::self();
+ return prov->plugin( d->mimeTypeInfo->mimeType(), d->url.protocol() );
+}
+
+TQString KFileMetaInfo::mimeType() const
+{
+ assert(isValid());
+ return d->mimeTypeInfo->mimeType();
+}
+
+bool KFileMetaInfo::contains(const TQString& key) const
+{
+ TQStringList glist = groups();
+ for (TQStringList::Iterator it = glist.begin(); it != glist.end(); ++it)
+ {
+ KFileMetaInfoGroup g = d->groups[*it];
+ if (g.contains(key)) return true;
+ }
+ return false;
+}
+
+bool KFileMetaInfo::containsGroup(const TQString& key) const
+{
+ return groups().contains(key);
+}
+
+KFileMetaInfoItem KFileMetaInfo::item( const TQString& key) const
+{
+ TQStringList groups = preferredGroups();
+ for (TQStringList::Iterator it = groups.begin(); it != groups.end(); ++it)
+ {
+ KFileMetaInfoItem i = d->groups[*it][key];
+ if (i.isValid()) return i;
+ }
+ return KFileMetaInfoItem();
+}
+
+KFileMetaInfoItem KFileMetaInfo::item(const KFileMetaInfoItem::Hint hint) const
+{
+ TQStringList groups = preferredGroups();
+ TQStringList::ConstIterator it;
+ for (it = groups.begin(); it != groups.end(); ++it)
+ {
+ KFileMetaInfoItem i = d->groups[*it].item(hint);
+ if (i.isValid()) return i;
+ }
+ return KFileMetaInfoItem();
+}
+
+KFileMetaInfoItem KFileMetaInfo::saveItem( const TQString& key,
+ const TQString& preferredGroup,
+ bool createGroup )
+{
+ assert(isValid());
+ // try the preferred groups first
+ if ( !preferredGroup.isEmpty() ) {
+ TQMapIterator<TQString,KFileMetaInfoGroup> it =
+ d->groups.find( preferredGroup );
+
+ // try to create the preferred group, if necessary
+ if ( it == d->groups.end() && createGroup ) {
+ const KFileMimeTypeInfo::GroupInfo *groupInfo =
+ d->mimeTypeInfo->groupInfo( preferredGroup );
+ if ( groupInfo && groupInfo->supportedKeys().contains( key ) ) {
+ if ( addGroup( preferredGroup ) )
+ it = d->groups.find( preferredGroup );
+ }
+ }
+
+ if ( it != d->groups.end() ) {
+ KFileMetaInfoItem item = it.data().addItem( key );
+ if ( item.isValid() )
+ return item;
+ }
+ }
+
+ TQStringList groups = preferredGroups();
+
+ KFileMetaInfoItem item;
+
+ TQStringList::ConstIterator groupIt = groups.begin();
+ for ( ; groupIt != groups.end(); ++groupIt )
+ {
+ TQMapIterator<TQString,KFileMetaInfoGroup> it = d->groups.find( *groupIt );
+ if ( it != d->groups.end() )
+ {
+ KFileMetaInfoGroup group = it.data();
+ item = findEditableItem( group, key );
+ if ( item.isValid() )
+ return item;
+ }
+ else // not existant -- try to create the group
+ {
+ const KFileMimeTypeInfo::GroupInfo *groupInfo =
+ d->mimeTypeInfo->groupInfo( *groupIt );
+ if ( groupInfo && groupInfo->supportedKeys().contains( key ) )
+ {
+ if ( addGroup( *groupIt ) )
+ {
+ KFileMetaInfoGroup group = d->groups[*groupIt];
+ KFileMetaInfoItem item = group.addItem( key );
+ if ( item.isValid() )
+ return item;
+// else ### add when removeGroup() is implemented :)
+// removeGroup( *groupIt ); // couldn't add item -> remove
+ }
+ }
+ }
+ }
+
+ // finally check for variable items
+
+ return item;
+}
+
+KFileMetaInfoItem KFileMetaInfo::findEditableItem( KFileMetaInfoGroup& group,
+ const TQString& key )
+{
+ assert(isValid());
+ KFileMetaInfoItem item = group.addItem( key );
+ if ( item.isValid() && item.isEditable() )
+ return item;
+
+ if ( (d->mimeTypeInfo->groupInfo( group.name() )->attributes() & KFileMimeTypeInfo::Addable) )
+ return item;
+
+ return KFileMetaInfoItem();
+}
+
+KFileMetaInfoGroup KFileMetaInfo::appendGroup(const TQString& name)
+{
+ assert(isValid());
+ if ( d->mimeTypeInfo->supportedGroups().contains(name) &&
+ ! d->groups.contains(name) )
+ {
+ KFileMetaInfoGroup group( name, d->mimeTypeInfo );
+ d->groups.insert(name, group);
+ return group;
+ }
+
+ else {
+ kdWarning(7033) << "Someone's trying to add a KFileMetaInfoGroup which is not supported or already existing: " << name << endl;
+ return KFileMetaInfoGroup();
+ }
+}
+
+TQString KFileMetaInfo::path() const
+{
+ return d->url.isLocalFile() ? d->url.path() : TQString::null;
+}
+
+KURL KFileMetaInfo::url() const
+{
+ return d->url;
+}
+
+void KFileMetaInfo::ref()
+{
+ if (d != Data::null) d->ref();
+
+}
+
+void KFileMetaInfo::deref()
+{
+ // We don't call makeNull here since it isn't necassery:
+ // If d is equal to null it means that null is initialized already.
+ // null is 0L when it hasn't been initialized and d is never 0L.
+ if ((d != Data::null) && d->deref())
+ {
+// kdDebug(7033) << "metainfo object for " << d->url.path << " is finally deleted\n";
+ delete d;
+ d = 0;
+ }
+
+}
+
+
+KFileMetaInfo::Data* KFileMetaInfo::Data::null = 0L;
+static KStaticDeleter<KFileMetaInfo::Data> sd_KFileMetaInfoData;
+
+KFileMetaInfo::Data* KFileMetaInfo::Data::makeNull()
+{
+ if (!null)
+ // We deliberately do not reset "null" after it has been destroyed!
+ // Otherwise we will run into problems later in ~KFileMetaInfoItem
+ // where the d-pointer is compared against null.
+ null = sd_KFileMetaInfoData.setObject( new KFileMetaInfo::Data(KURL(), 0) );
+ return null;
+}
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+
+KFilePlugin::KFilePlugin( TQObject *parent, const char *name,
+ const TQStringList& /*args*/)
+ : TQObject( parent, name )
+{
+// kdDebug(7033) << "loaded a plugin for " << name << endl;
+}
+
+KFilePlugin::~KFilePlugin()
+{
+// kdDebug(7033) << "unloaded a plugin for " << name() << endl;
+}
+
+KFileMimeTypeInfo * KFilePlugin::addMimeTypeInfo( const TQString& mimeType )
+{
+ return KFileMetaInfoProvider::self()->addMimeTypeInfo( mimeType );
+}
+
+void KFilePlugin::virtual_hook( int, void* )
+{ /*BASE::virtual_hook( id, data );*/ }
+
+
+KFileMimeTypeInfo::GroupInfo* KFilePlugin::addGroupInfo(KFileMimeTypeInfo* info,
+ const TQString& key, const TQString& translatedKey) const
+{
+ return info->addGroupInfo(key, translatedKey);
+}
+
+void KFilePlugin::setAttributes(KFileMimeTypeInfo::GroupInfo* gi, uint attr) const
+{
+ gi->m_attr = attr;
+}
+
+void KFilePlugin::addVariableInfo(KFileMimeTypeInfo::GroupInfo* gi,
+ TQVariant::Type type, uint attr) const
+{
+ gi->addVariableInfo(type, attr);
+}
+
+KFileMimeTypeInfo::ItemInfo* KFilePlugin::addItemInfo(KFileMimeTypeInfo::GroupInfo* gi,
+ const TQString& key,
+ const TQString& translatedKey,
+ TQVariant::Type type)
+{
+ return gi->addItemInfo(key, translatedKey, type);
+}
+
+void KFilePlugin::setAttributes(KFileMimeTypeInfo::ItemInfo* item, uint attr)
+{
+ item->m_attr = attr;
+}
+
+void KFilePlugin::setHint(KFileMimeTypeInfo::ItemInfo* item, uint hint)
+{
+ item->m_hint = hint;
+}
+
+void KFilePlugin::setUnit(KFileMimeTypeInfo::ItemInfo* item, uint unit)
+{
+ item->m_unit = unit;
+ // set prefix and suffix
+ switch (unit)
+ {
+ case KFileMimeTypeInfo::Seconds:
+ item->m_suffix = i18n("s"); break;
+
+ case KFileMimeTypeInfo::MilliSeconds:
+ item->m_suffix = i18n("ms"); break;
+
+ case KFileMimeTypeInfo::BitsPerSecond:
+ item->m_suffix = i18n("bps"); break;
+
+ case KFileMimeTypeInfo::Pixels:
+ item->m_suffix = i18n("pixels"); break;
+
+ case KFileMimeTypeInfo::Inches:
+ item->m_suffix = i18n("in"); break;
+
+ case KFileMimeTypeInfo::Centimeters:
+ item->m_suffix = i18n("cm"); break;
+
+ case KFileMimeTypeInfo::Bytes:
+ item->m_suffix = i18n("B"); break;
+
+ case KFileMimeTypeInfo::KiloBytes:
+ item->m_suffix = i18n("KB"); break;
+
+ case KFileMimeTypeInfo::FramesPerSecond:
+ item->m_suffix = i18n("fps"); break;
+
+ case KFileMimeTypeInfo::DotsPerInch:
+ item->m_suffix = i18n("dpi"); break;
+
+ case KFileMimeTypeInfo::BitsPerPixel:
+ item->m_suffix = i18n("bpp"); break;
+
+ case KFileMimeTypeInfo::Hertz:
+ item->m_suffix = i18n("Hz"); break;
+
+ case KFileMimeTypeInfo::Millimeters:
+ item->m_suffix = i18n("mm");
+ }
+}
+
+void KFilePlugin::setPrefix(KFileMimeTypeInfo::ItemInfo* item, const TQString& prefix)
+{
+ item->m_prefix = prefix;
+}
+
+void KFilePlugin::setSuffix(KFileMimeTypeInfo::ItemInfo* item, const TQString& suffix)
+{
+ item->m_suffix = suffix;
+}
+
+KFileMetaInfoGroup KFilePlugin::appendGroup(KFileMetaInfo& info, const TQString& key)
+{
+ return info.appendGroup(key);
+}
+
+void KFilePlugin::appendItem(KFileMetaInfoGroup& group, const TQString& key, TQVariant value)
+{
+ group.appendItem(key, value);
+}
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+
+
+KFileMetaInfoProvider * KFileMetaInfoProvider::s_self;
+static KStaticDeleter<KFileMetaInfoProvider> sd;
+
+KFileMetaInfoProvider * KFileMetaInfoProvider::self()
+{
+ if ( !s_self )
+ s_self = sd.setObject( s_self, new KFileMetaInfoProvider() );
+
+ return s_self;
+}
+
+KFileMetaInfoProvider::KFileMetaInfoProvider()
+{
+ m_plugins.setAutoDelete( true );
+}
+
+KFileMetaInfoProvider::~KFileMetaInfoProvider()
+{
+ m_plugins.clear();
+ sd.setObject( 0 );
+}
+
+KFilePlugin* KFileMetaInfoProvider::loadPlugin( const TQString& mimeType, const TQString& protocol )
+{
+ //kdDebug() << "loadPlugin: mimeType=" << mimeType << " protocol=" << protocol << endl;
+ // Currently the idea is: either the mimetype is set or the protocol, but not both.
+ // We need PNG fileinfo, and trash: fileinfo, but not "PNG in the trash".
+ TQString queryMimeType, query;
+ if ( !mimeType.isEmpty() ) {
+ query = "(not exist [X-TDE-Protocol])";
+ queryMimeType = mimeType;
+ } else {
+ query = TQString::fromLatin1( "[X-TDE-Protocol] == '%1'" ).arg(protocol);
+ // querying for a protocol: we have no mimetype, so we need to use KFilePlugin as one
+ queryMimeType = "KFilePlugin";
+ // hopefully using KFilePlugin as genericMimeType too isn't a problem
+ }
+ const KTrader::OfferList offers = KTrader::self()->query( queryMimeType, "KFilePlugin", query, TQString::null );
+ if ( offers.isEmpty() )
+ return 0;
+ KService::Ptr service = *(offers.begin());
+ Q_ASSERT( service && service->isValid() );
+ if ( !service || !service->isValid() )
+ return 0;
+
+ KFilePlugin* plugin = KParts::ComponentFactory::createInstanceFromService<KFilePlugin>
+ ( service, TQT_TQOBJECT(this), mimeType.local8Bit() );
+ if (!plugin)
+ kdWarning(7033) << "error loading the plugin from " << service->desktopEntryPath() << endl;
+
+ return plugin;
+}
+
+KFilePlugin* KFileMetaInfoProvider::loadAndRegisterPlugin( const TQString& mimeType, const TQString& protocol )
+{
+ Q_ASSERT( m_pendingMimetypeInfos.isEmpty() );
+ m_pendingMimetypeInfos.clear();
+
+ KFilePlugin* plugin = loadPlugin( mimeType, protocol );
+ if ( !plugin ) {
+ // No plugin found. Remember that, to save time.
+ m_plugins.insert( protocol.isEmpty() ? mimeType : protocol, new CachedPluginInfo );
+ return 0;
+ }
+
+ if ( !protocol.isEmpty() ) {
+ // Protocol-metainfo: only one entry
+ Q_ASSERT( m_pendingMimetypeInfos.count() == 1 );
+ KFileMimeTypeInfo* info = m_pendingMimetypeInfos[ protocol ];
+ Q_ASSERT( info );
+ m_plugins.insert( protocol, new CachedPluginInfo( plugin, info, true ) );
+ } else {
+ // Mimetype-metainfo: the plugin can register itself for multiple mimetypes, remember them all
+ bool first = true;
+ TQDictIterator<KFileMimeTypeInfo> it( m_pendingMimetypeInfos );
+ for( ; it.current(); ++it ) {
+ KFileMimeTypeInfo* info = it.current();
+ m_plugins.insert( it.currentKey(), new CachedPluginInfo( plugin, info, first ) );
+ first = false;
+ }
+ // Hopefully the above includes the mimetype we asked for!
+ if ( m_pendingMimetypeInfos.find( mimeType ) == 0 )
+ kdWarning(7033) << plugin->className() << " was created for " << mimeType << " but doesn't call addMimeTypeInfo for it!" << endl;
+ }
+ m_pendingMimetypeInfos.clear();
+ return plugin;
+}
+
+KFilePlugin * KFileMetaInfoProvider::plugin(const TQString& mimeType)
+{
+ return plugin( mimeType, TQString::null );
+}
+
+KFilePlugin * KFileMetaInfoProvider::plugin(const TQString& mimeType, const TQString& protocol)
+{
+ //kdDebug(7033) << "plugin() : looking for plugin for protocol=" << protocol << " mimeType=" << mimeType << endl;
+
+ if ( !protocol.isEmpty() ) {
+ CachedPluginInfo *cache = m_plugins.find( protocol );
+ if ( cache && cache->plugin ) {
+ return cache->plugin;
+ }
+ if ( !cache ) {
+ KFilePlugin* plugin = loadAndRegisterPlugin( TQString::null, protocol );
+ if ( plugin )
+ return plugin;
+ }
+ }
+
+ CachedPluginInfo *cache = m_plugins.find( mimeType );
+ if ( cache ) {
+ return cache->plugin;
+ }
+
+ KFilePlugin* plugin = loadAndRegisterPlugin( mimeType, TQString::null );
+
+#if 0
+ kdDebug(7033) << "currently loaded plugins:\n";
+
+ TQDictIterator<CachedPluginInfo> it( m_plugins );
+ for( ; it.current(); ++it ) {
+ CachedPluginInfo* cache = it.current();
+ kdDebug(7033)
+ << it.currentKey() // mimetype or protocol
+ << " : " << (cache->plugin ? cache->plugin->className() : "(no plugin)") << endl; // plugin
+ // TODO print cache->mimeTypeInfo
+ }
+#endif
+
+ return plugin;
+}
+
+TQStringList KFileMetaInfoProvider::preferredKeys( const TQString& mimeType ) const
+{
+ KService::Ptr service =
+ KServiceTypeProfile::preferredService( mimeType, "KFilePlugin");
+
+ if ( !service || !service->isValid() )
+ {
+// kdDebug(7033) << "no valid service found\n";
+ return TQStringList();
+ }
+ return service->property("PreferredItems").toStringList();
+}
+
+TQStringList KFileMetaInfoProvider::preferredGroups( const TQString& mimeType ) const
+{
+ KService::Ptr service =
+ KServiceTypeProfile::preferredService( mimeType, "KFilePlugin");
+
+ if ( !service || !service->isValid() )
+ {
+// kdDebug(7033) << "no valid service found\n";
+ return TQStringList();
+ }
+ return service->property("PreferredGroups").toStringList();
+}
+
+const KFileMimeTypeInfo * KFileMetaInfoProvider::mimeTypeInfo( const TQString& mimeType )
+{
+ return mimeTypeInfo( mimeType, TQString::null );
+}
+
+const KFileMimeTypeInfo * KFileMetaInfoProvider::mimeTypeInfo( const TQString& mimeType, const TQString& protocol )
+{
+ //kdDebug(7033) << "mimeTypeInfo() : looking for plugin for protocol=" << protocol << " mimeType=" << mimeType << endl;
+ if ( !protocol.isEmpty() ) {
+ CachedPluginInfo *cache = m_plugins.find( protocol );
+ if ( cache && cache->mimeTypeInfo ) {
+ return cache->mimeTypeInfo;
+ }
+
+ if ( !cache ) {
+ loadAndRegisterPlugin( TQString::null, protocol );
+ cache = m_plugins.find( protocol );
+ if ( cache && cache->mimeTypeInfo ) {
+ return cache->mimeTypeInfo;
+ }
+ }
+ }
+
+ CachedPluginInfo *cache = m_plugins.find( mimeType );
+ if ( cache ) {
+ return cache->mimeTypeInfo;
+ }
+
+ loadAndRegisterPlugin( mimeType, TQString::null );
+ cache = m_plugins.find( mimeType );
+ if ( cache ) {
+ return cache->mimeTypeInfo;
+ }
+ return 0;
+}
+
+KFileMimeTypeInfo * KFileMetaInfoProvider::addMimeTypeInfo(
+ const TQString& mimeType )
+{
+
+ KFileMimeTypeInfo *info = m_pendingMimetypeInfos.find( mimeType );
+ Q_ASSERT( !info );
+ if ( !info )
+ {
+ info = new KFileMimeTypeInfo( mimeType );
+ m_pendingMimetypeInfos.insert( mimeType, info );
+ }
+
+ info->m_preferredKeys = preferredKeys( mimeType );
+ info->m_preferredGroups = preferredGroups( mimeType );
+
+ return info;
+}
+
+TQStringList KFileMetaInfoProvider::supportedMimeTypes() const
+{
+ TQStringList allMimeTypes;
+ TQString tdefilePlugin = "KFilePlugin";
+
+ KTrader::OfferList offers = KTrader::self()->query( "KFilePlugin" );
+ KTrader::OfferListIterator it = offers.begin();
+ for ( ; it != offers.end(); ++it )
+ {
+ const TQStringList mimeTypes = (*it)->serviceTypes();
+ TQStringList::ConstIterator it2 = mimeTypes.begin();
+ for ( ; it2 != mimeTypes.end(); ++it2 )
+ if ( allMimeTypes.find( *it2 ) == allMimeTypes.end() &&
+ *it2 != tdefilePlugin ) // also in serviceTypes()
+ allMimeTypes.append( *it2 );
+ }
+
+ return allMimeTypes;
+}
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+
+
+// shared data of a KFileMetaInfoGroup
+class KFileMetaInfoGroup::Data : public TQShared
+{
+public:
+ Data(const TQString& _name)
+ : TQShared(),
+ name(_name),
+ mimeTypeInfo(0L),
+ dirty( false ),
+ added( false )
+ {}
+
+ // we use this one for the streaming operators
+ Data() : mimeTypeInfo(0L) {}
+ ~Data() {
+ if ( this == null )
+ delete mimeTypeInfo;
+ };
+
+ TQString name;
+ TQMap<TQString, KFileMetaInfoItem> items;
+ const KFileMimeTypeInfo* mimeTypeInfo;
+ TQStringList removedItems;
+ bool dirty :1;
+ bool added :1;
+
+ static Data* null;
+ static Data* makeNull();
+
+};
+
+KFileMetaInfoGroup::KFileMetaInfoGroup( const TQString& name,
+ const KFileMimeTypeInfo* info )
+ : d(new Data( name ) )
+{
+ d->mimeTypeInfo = info;
+}
+
+KFileMetaInfoGroup::KFileMetaInfoGroup( const KFileMetaInfoGroup& original )
+{
+ // operator= does everything that's necessary
+ d = Data::makeNull();
+ *this = original;
+}
+
+KFileMetaInfoGroup::KFileMetaInfoGroup()
+{
+ d = Data::makeNull();
+}
+
+KFileMetaInfoGroup::~KFileMetaInfoGroup()
+{
+ deref();
+}
+
+const KFileMetaInfoGroup& KFileMetaInfoGroup::operator= (const KFileMetaInfoGroup& info )
+{
+ if (d != info.d)
+ {
+ deref();
+ // first deref the old one
+ d = info.d;
+ // and now ref the new one
+ ref();
+ }
+ return *this;
+}
+
+bool KFileMetaInfoGroup::isValid() const
+{
+ // We don't call makeNull here since it isn't necassery, see deref()
+ return d != Data::null;
+}
+
+bool KFileMetaInfoGroup::isEmpty() const
+{
+ return d->items.isEmpty();
+}
+
+TQStringList KFileMetaInfoGroup::preferredKeys() const
+{
+ assert(isValid());
+ TQStringList list = keys();
+ TQStringList newlist;
+ TQStringList preferredKeys = d->mimeTypeInfo->preferredKeys();
+ TQStringList::Iterator pref;
+ TQStringList::Iterator begin = preferredKeys.begin();
+ TQStringList::Iterator end = preferredKeys.end();
+
+ // move all keys from the preferred keys that are in our list to a new list
+ for ( pref = begin; pref!=end; ++pref )
+ {
+ TQStringList::Iterator item = list.find(*pref);
+ if ( item != list.end() )
+ {
+ newlist.append( *item );
+ list.remove(item);
+ }
+ }
+
+ // now the old list only contains the non-preferred items, so we
+ // add the remaining ones to newlist
+ newlist += list;
+
+ return newlist;
+}
+
+TQStringList KFileMetaInfoGroup::keys() const
+{
+ if (d == Data::makeNull())
+ kdWarning(7033) << "attempt to get the keys of "
+ "an invalid metainfo group";
+
+ TQStringList list;
+
+ // make a TQStringList with all available keys
+ TQMapConstIterator<TQString, KFileMetaInfoItem> it;
+ for (it = d->items.begin(); it!=d->items.end(); ++it)
+ {
+ list.append(it.data().key());
+// kdDebug(7033) << "Item " << it.data().key() << endl;
+ }
+ return list;
+}
+
+TQString KFileMetaInfoGroup::translatedName() const
+{
+ assert(isValid());
+ return d->mimeTypeInfo->groupInfo(d->name)->translatedName();
+}
+
+TQStringList KFileMetaInfoGroup::supportedKeys() const
+{
+ assert(isValid());
+ return d->mimeTypeInfo->groupInfo(d->name)->supportedKeys();
+}
+
+bool KFileMetaInfoGroup::supportsVariableKeys() const
+{
+ assert(isValid());
+ return d->mimeTypeInfo->groupInfo(d->name)->supportsVariableKeys();
+}
+
+bool KFileMetaInfoGroup::contains( const TQString& key ) const
+{
+ return d->items.contains(key);
+}
+
+KFileMetaInfoItem KFileMetaInfoGroup::item( const TQString& key) const
+{
+ TQMapIterator<TQString,KFileMetaInfoItem> it = d->items.find( key );
+ if ( it != d->items.end() )
+ return it.data();
+
+ return KFileMetaInfoItem();
+}
+
+KFileMetaInfoItem KFileMetaInfoGroup::item(uint hint) const
+{
+ TQMapIterator<TQString, KFileMetaInfoItem> it;
+
+ for (it = d->items.begin(); it!=d->items.end(); ++it)
+ if (it.data().hint() == hint)
+ return it.data();
+
+ return KFileMetaInfoItem();
+}
+
+TQString KFileMetaInfoGroup::name() const
+{
+ return d->name;
+}
+
+uint KFileMetaInfoGroup::attributes() const
+{
+ assert(isValid());
+ return d->mimeTypeInfo->groupInfo(d->name)->attributes();
+}
+
+void KFileMetaInfoGroup::setAdded()
+{
+ d->added = true;
+}
+
+bool KFileMetaInfoGroup::isModified() const
+{
+ return d->dirty;
+}
+
+void KFileMetaInfoGroup::ref()
+{
+ if (d != Data::null) d->ref();
+
+}
+
+void KFileMetaInfoGroup::deref()
+{
+ // We don't call makeNull here since it isn't necassery:
+ // If d is equal to null it means that null is initialized already.
+ // null is 0L when it hasn't been initialized and d is never 0L.
+ if ((d != Data::null) && d->deref())
+ {
+// kdDebug(7033) << "metainfo group " << d->name
+// << " is finally deleted\n";
+ delete d;
+ d = 0;
+ }
+
+}
+
+KFileMetaInfoItem KFileMetaInfoGroup::addItem( const TQString& key )
+{
+ assert(isValid());
+ TQMapIterator<TQString,KFileMetaInfoItem> it = d->items.find( key );
+ if ( it != d->items.end() )
+ return it.data();
+
+ const KFileMimeTypeInfo::GroupInfo* ginfo = d->mimeTypeInfo->groupInfo(d->name);
+
+ if ( !ginfo ) {
+ Q_ASSERT( ginfo );
+ return KFileMetaInfoItem();
+ }
+
+ const KFileMimeTypeInfo::ItemInfo* info = ginfo->itemInfo(key);
+
+ if ( !info ) {
+ Q_ASSERT( info );
+ return KFileMetaInfoItem();
+ }
+
+ KFileMetaInfoItem item;
+
+ if (info->isVariableItem())
+ item = KFileMetaInfoItem(ginfo->variableItemInfo(), key, TQVariant());
+ else
+ item = KFileMetaInfoItem(info, key, TQVariant());
+
+ d->items.insert(key, item);
+ item.setAdded(); // mark as added
+ d->dirty = true; // mark ourself as dirty, too
+ return item;
+}
+
+bool KFileMetaInfoGroup::removeItem( const TQString& key )
+{
+ if (!isValid())
+ {
+ kdDebug(7033) << "trying to remove an item from an invalid group\n";
+ return false;
+ }
+
+ TQMapIterator<TQString, KFileMetaInfoItem> it = d->items.find(key);
+ if ( it==d->items.end() )
+ {
+ kdDebug(7033) << "trying to remove the non existant item " << key << "\n";
+ return false;
+ }
+
+ if (!((*it).attributes() & KFileMimeTypeInfo::Removable))
+ {
+ kdDebug(7033) << "trying to remove a non removable item\n";
+ return false;
+ }
+
+ (*it).setRemoved();
+ d->items.remove(it);
+ d->removedItems.append(key);
+ d->dirty = true;
+ return true;
+}
+
+TQStringList KFileMetaInfoGroup::removedItems()
+{
+ return d->removedItems;
+}
+
+KFileMetaInfoItem KFileMetaInfoGroup::appendItem(const TQString& key,
+ const TQVariant& value)
+{
+ //KDE4 enforce (value.type() == d->mimeTypeInfo->type())
+ assert(isValid());
+ const KFileMimeTypeInfo::GroupInfo* ginfo = d->mimeTypeInfo->groupInfo(d->name);
+ if ( !ginfo ) {
+ kdWarning() << "Trying to append a Metadata item for a non-existant group:" << d->name << endl;
+ return KFileMetaInfoItem();
+ }
+ const KFileMimeTypeInfo::ItemInfo* info = ginfo->itemInfo(key);
+ if ( !info ) {
+ kdWarning() << "Trying to append a Metadata item for an unknown key (no ItemInfo): " << key << endl;
+ return KFileMetaInfoItem();
+ }
+
+ KFileMetaInfoItem item;
+
+ if (info->key().isNull())
+ item = KFileMetaInfoItem(ginfo->variableItemInfo(), key, value);
+ else
+ item = KFileMetaInfoItem(info, key, value);
+
+ kdDebug(7033) << "KFileMetaInfogroup inserting a " << key << endl;
+
+ d->items.insert(key, item);
+ return item;
+}
+
+KFileMetaInfoGroup::Data* KFileMetaInfoGroup::Data::null = 0L;
+static KStaticDeleter<KFileMetaInfoGroup::Data> sd_KFileMetaInfoGroupData;
+
+KFileMetaInfoGroup::Data* KFileMetaInfoGroup::Data::makeNull()
+{
+ if (!null)
+ {
+ // We deliberately do not reset "null" after it has been destroyed!
+ // Otherwise we will run into problems later in ~KFileMetaInfoItem
+ // where the d-pointer is compared against null.
+ null = new Data(TQString::null);
+ null->mimeTypeInfo = new KFileMimeTypeInfo();
+ sd_KFileMetaInfoGroupData.setObject( null );
+ }
+ return null;
+}
+
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+
+KFileMimeTypeInfo::KFileMimeTypeInfo( const TQString& mimeType )
+ : m_mimeType( mimeType )
+{
+ m_groups.setAutoDelete( true );
+}
+
+KFileMimeTypeInfo::~KFileMimeTypeInfo()
+{
+}
+
+const KFileMimeTypeInfo::GroupInfo * KFileMimeTypeInfo::groupInfo( const TQString& group ) const
+{
+ return m_groups.find( group );
+}
+
+KFileMimeTypeInfo::GroupInfo * KFileMimeTypeInfo::addGroupInfo(
+ const TQString& name, const TQString& translatedName )
+{
+ GroupInfo* group = new GroupInfo( name, translatedName );
+ m_groups.insert(name, group);
+ return group;
+}
+
+TQStringList KFileMimeTypeInfo::supportedGroups() const
+{
+ TQStringList list;
+ TQDictIterator<GroupInfo> it( m_groups );
+ for ( ; it.current(); ++it )
+ list.append( it.current()->name() );
+
+ return list;
+}
+
+TQStringList KFileMimeTypeInfo::translatedGroups() const
+{
+ TQStringList list;
+ TQDictIterator<GroupInfo> it( m_groups );
+ for ( ; it.current(); ++it )
+ list.append( it.current()->translatedName() );
+
+ return list;
+}
+
+TQStringList KFileMimeTypeInfo::supportedKeys() const
+{
+ // not really efficient, but not those are not large lists, probably.
+ // maybe cache the result?
+ TQStringList keys;
+ TQStringList::ConstIterator lit;
+ TQDictIterator<GroupInfo> it( m_groups );
+ for ( ; it.current(); ++it ) { // need to nuke dupes
+ TQStringList list = it.current()->supportedKeys();
+ for ( lit = list.begin(); lit != list.end(); ++lit ) {
+ if ( keys.find( *lit ) == keys.end() )
+ keys.append( *lit );
+ }
+ }
+
+ return keys;
+}
+
+TQValidator * KFileMimeTypeInfo::createValidator(const TQString& group,
+ const TQString& key,
+ TQObject *parent,
+ const char *name) const
+{
+ KFilePlugin* plugin = KFileMetaInfoProvider::self()->plugin(m_mimeType);
+ if (plugin) return plugin->createValidator(mimeType(), group, key,
+ parent, name);
+ return 0;
+}
+
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+
+KFileMimeTypeInfo::GroupInfo::GroupInfo( const TQString& name,
+ const TQString& translatedName )
+ : m_name( name ),
+ m_translatedName( translatedName ),
+ m_attr( 0 ),
+ m_variableItemInfo( 0 )
+
+{
+ m_itemDict.setAutoDelete( true );
+}
+
+KFileMimeTypeInfo::GroupInfo::~GroupInfo()
+{
+ delete m_variableItemInfo;
+}
+
+const KFileMimeTypeInfo::ItemInfo * KFileMimeTypeInfo::GroupInfo::itemInfo( const TQString& key ) const
+{
+ ItemInfo* item = m_itemDict.find( key );
+
+ // if we the item isn't found and variable keys are supported, we need to
+ // return the default variable key iteminfo.
+ if (!item && m_variableItemInfo)
+ {
+ return m_variableItemInfo;
+ }
+ return item;
+}
+
+KFileMimeTypeInfo::ItemInfo* KFileMimeTypeInfo::GroupInfo::addItemInfo(
+ const TQString& key, const TQString& translatedKey,
+ TQVariant::Type type)
+{
+// kdDebug(7034) << key << "(" << translatedKey << ") -> " << TQVariant::typeToName(type) << endl;
+
+ ItemInfo* item = new ItemInfo(key, translatedKey, type);
+ m_supportedKeys.append(key);
+ m_itemDict.insert(key, item);
+ return item;
+}
+
+
+void KFileMimeTypeInfo::GroupInfo::addVariableInfo( TQVariant::Type type,
+ uint attr )
+{
+ // just make sure that it's not already there
+ delete m_variableItemInfo;
+ m_variableItemInfo = new ItemInfo(TQString::null, TQString::null, type);
+ m_variableItemInfo->m_attr = attr;
+}
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+
+TQString KFileMimeTypeInfo::ItemInfo::string(const TQVariant& value, bool mangle) const
+{
+ TQString s;
+
+ switch (value.type())
+ {
+ case TQVariant::Invalid :
+ return "---";
+
+ case TQVariant::Bool :
+ s = value.toBool() ? i18n("Yes") : i18n("No");
+ break;
+
+ case TQVariant::Int :
+ if (unit() == KFileMimeTypeInfo::Seconds)
+ {
+ int seconds = value.toInt() % 60;
+ int minutes = value.toInt() / 60 % 60;
+ int hours = value.toInt() / 3600;
+ s = hours ? TQString().sprintf("%d:%02d:%02d",hours, minutes, seconds)
+ : TQString().sprintf("%02d:%02d", minutes, seconds);
+ return s; // no suffix wanted
+ }
+ else if (unit() == KFileMimeTypeInfo::Bytes)
+ {
+ // convertSize already adds the correct suffix
+ return TDEIO::convertSize(value.toInt());
+ }
+ else if (unit() == KFileMimeTypeInfo::KiloBytes)
+ {
+ // convertSizeFromKB already adds the correct suffix
+ return TDEIO::convertSizeFromKB(value.toInt());
+ }
+ else
+ s = TDEGlobal::locale()->formatNumber( value.toInt() , 0);
+ break;
+
+ case TQVariant::LongLong :
+ s = TDEGlobal::locale()->formatNumber( value.toLongLong(), 0 );
+ break;
+
+ case TQVariant::ULongLong :
+ if ( unit() == KFileMimeTypeInfo::Bytes )
+ return TDEIO::convertSize( value.toULongLong() );
+ else if ( unit() == KFileMimeTypeInfo::KiloBytes )
+ return TDEIO::convertSizeFromKB( value.toULongLong() );
+ else
+ s = TDEGlobal::locale()->formatNumber( value.toULongLong(), 0 );
+ break;
+
+ case TQVariant::UInt :
+ s = TDEGlobal::locale()->formatNumber( value.toUInt() , 0);
+ break;
+
+ case TQVariant::Double :
+ s = TDEGlobal::locale()->formatNumber( value.toDouble(), 3);
+ break;
+
+ case TQVariant::Date :
+ s = TDEGlobal::locale()->formatDate( value.toDate(), true );
+ break;
+
+ case TQVariant::Time :
+ s = TDEGlobal::locale()->formatTime( value.toTime(), true );
+ break;
+
+ case TQVariant::DateTime :
+ s = TDEGlobal::locale()->formatDateTime( value.toDateTime(),
+ true, true );
+ break;
+
+ case TQVariant::Size :
+ s = TQString("%1 x %2").arg(value.toSize().width())
+ .arg(value.toSize().height());
+ break;
+
+ case TQVariant::Point :
+ s = TQString("%1/%2").arg(value.toSize().width())
+ .arg(value.toSize().height());
+ break;
+
+ default:
+ s = value.toString();
+ }
+
+ if (mangle && !s.isNull())
+ {
+ s.prepend(prefix());
+ s.append(" " + suffix());
+ }
+ return s;
+}
+
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+
+
+
+// stream operators
+
+/* serialization of a KFileMetaInfoItem:
+ first a bool that says if the items is valid, and if yes,
+ all the elements of the Data
+*/
+TDEIO_EXPORT TQDataStream& operator <<(TQDataStream& s, const KFileMetaInfoItem& item )
+{
+
+ KFileMetaInfoItem::Data* d = item.d;
+
+ // if the object is invalid, put only a char in the stream
+ bool isValid = item.isValid();
+ s << isValid;
+ // ### what do about mimetypeInfo ?
+ if (isValid)
+ s << d->key
+ << d->value
+ << d->dirty
+ << d->added
+ << d->removed;
+
+ return s;
+}
+
+
+TDEIO_EXPORT TQDataStream& operator >>(TQDataStream& s, KFileMetaInfoItem& item )
+{
+ bool isValid;
+ s >> isValid;
+
+ if (!isValid)
+ {
+ item = KFileMetaInfoItem();
+ return s;
+ }
+
+ // we need a new object for our data
+ item.deref();
+ item.d = new KFileMetaInfoItem::Data();
+
+ // ### what do about mimetypeInfo ?
+ bool dirty, added, removed;
+ s >> item.d->key
+ >> item.d->value
+ >> dirty
+ >> added
+ >> removed;
+ item.d->dirty = dirty;
+ item.d->added = added;
+ item.d->removed = removed;
+
+ return s;
+}
+
+
+// serialization of a KFileMetaInfoGroup
+// we serialize the name of the mimetype here instead of the mimetype info
+// on the other side, we can simply use this to ask the provider for the info
+TDEIO_EXPORT TQDataStream& operator <<(TQDataStream& s, const KFileMetaInfoGroup& group )
+{
+ KFileMetaInfoGroup::Data* d = group.d;
+
+ // if the object is invalid, put only a byte in the stream
+ bool isValid = group.isValid();
+
+ s << isValid;
+ if (isValid)
+ {
+ s << d->name
+ << d->items
+ << d->mimeTypeInfo->mimeType();
+ }
+ return s;
+}
+
+TDEIO_EXPORT TQDataStream& operator >>(TQDataStream& s, KFileMetaInfoGroup& group )
+{
+ TQString mimeType;
+ bool isValid;
+ s >> isValid;
+
+ // if it's invalid, there is not much to do
+ if (!isValid)
+ {
+ group = KFileMetaInfoGroup();
+ return s;
+ }
+
+ // we need a new object for our data
+ group.deref();
+ group.d = new KFileMetaInfoGroup::Data();
+
+ s >> group.d->name
+ >> group.d->items
+ >> mimeType;
+
+ group.d->mimeTypeInfo = KFileMetaInfoProvider::self()->mimeTypeInfo(mimeType);
+
+ // we need to set the item info for the items here
+ TQMapIterator<TQString, KFileMetaInfoItem> it = group.d->items.begin();
+ for ( ; it != group.d->items.end(); ++it)
+ {
+ (*it).d->mimeTypeInfo = group.d->mimeTypeInfo->groupInfo(group.d->name)
+ ->itemInfo((*it).key());
+ }
+
+ return s;
+}
+
+// serialization of a KFileMetaInfo object
+// we serialize the name of the mimetype here instead of the mimetype info
+// on the other side, we can simply use this to ask the provider for the info
+TDEIO_EXPORT TQDataStream& operator <<(TQDataStream& s, const KFileMetaInfo& info )
+{
+ KFileMetaInfo::Data* d = info.d;
+
+ // if the object is invalid, put only a byte that tells this
+ bool isValid = info.isValid();
+
+ s << isValid;
+ if (isValid)
+ {
+ s << d->url
+ << d->what
+ << d->groups
+ << d->mimeTypeInfo->mimeType();
+ }
+ return s;
+}
+
+TDEIO_EXPORT TQDataStream& operator >>(TQDataStream& s, KFileMetaInfo& info )
+{
+ TQString mimeType;
+ bool isValid;
+ s >> isValid;
+
+ // if it's invalid, there is not much to do
+ if (!isValid)
+ {
+ info = KFileMetaInfo();
+ return s;
+ }
+
+ // we need a new object for our data
+ info.deref();
+ info.d = new KFileMetaInfo::Data();
+
+ s >> info.d->url
+ >> info.d->what
+ >> info.d->groups
+ >> mimeType;
+ info.d->mimeTypeInfo = KFileMetaInfoProvider::self()->mimeTypeInfo(mimeType);
+
+ return s;
+}
+
+#include "tdefilemetainfo.moc"
diff --git a/tdeio/tdeio/tdefilemetainfo.h b/tdeio/tdeio/tdefilemetainfo.h
new file mode 100644
index 000000000..8cc3fdbdf
--- /dev/null
+++ b/tdeio/tdeio/tdefilemetainfo.h
@@ -0,0 +1,1738 @@
+/*
+ * This file is part of the KDE libraries
+ * Copyright (C) 2001-2002 Rolf Magnus <ramagnus@kde.org>
+ * Copyright (C) 2001-2002 Carsten Pfeiffer <pfeiffer@kde.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation version 2.0.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef KILEMETAINFO_H
+#define KILEMETAINFO_H
+
+/* Hack for HPUX: Namespace pollution
+ m_unit is a define in <sys/sysmacros.h> */
+#define m_unit outouftheway_m_unit
+
+#include <tqdict.h>
+#include <tqvariant.h>
+#include <tqobject.h>
+#include <tqstring.h>
+#include <kurl.h>
+
+#undef m_unit
+
+class TQValidator;
+class KFilePlugin;
+class KFileMetaInfoGroup;
+
+/**
+ * @brief Represents the capabilities of a KFilePlugin for a given mimetype
+ *
+ * This class provides information about the capabilities that a
+ * KFilePlugin for a given mimetype has. It includes a list of metainfo
+ * groups and items together with their type, a prefix, suffix and some other
+ * information about how to use, display or edit the items.
+ *
+ * @author Rolf Magnus
+ * @author Carsten Pfeiffer
+ */
+class TDEIO_EXPORT KFileMimeTypeInfo
+{
+ // the plugin needs to be a friend because it puts the data into the object,
+ // and it should be the only one allowed to do this.
+ friend class KFilePlugin;
+ friend class KFileMetaInfoProvider;
+
+public:
+ KFileMimeTypeInfo() {}
+
+ /**
+ * This enum is used to specify some attributes that an item can have,
+ * which fit neither in the Hint nor in the Unit enum.
+ */
+ enum Attributes
+ {
+ Addable = 1, ///< The item or group can be added by a user
+ Removable = 2, ///< It can be removed
+ Modifiable = 4, ///< The value can be edited (no meaning for a group)
+ Cumulative = 8, /**< If an application wants to display information
+ for more than one file, it may add up the values
+ for this item (e.g. play time of an mp3 file) */
+ Cummulative = Cumulative, ///< @deprecated Use Cumulative instead
+ Averaged = 16, /**< Similar to Cumulative, but the average should
+ be calculated instead of the sum */
+ MultiLine = 32, /**< This attribute says that a string item is likely
+ to be more than one line long, so for editing, a
+ widget capable for multline text should be used
+ @since 3.1 */
+ SqueezeText = 64 /**< If the text for this item is very long, it
+ should be squeezed to the size of the widget
+ where it's displayed
+ @since 3.1 */
+ };
+
+ /**
+ * This enum is mainly for items that have a special meaning for some
+ * applications.
+ */
+ enum Hint {
+ NoHint = 0, ///< No hint
+ Name = 1, ///< The name or title of the document
+ Author = 2, ///< The one who created the contents of it
+ Description = 3, ///< Description Some information about the document
+ Width = 4, ///< The width in pixels
+ Height = 5, ///< The height in pixels
+ Size = 6, ///< The size in pixels (width and height)
+ Bitrate = 7, ///< For media files
+ Length = 8, ///< The length of the file, also for media files
+ Hidden = 9, ///< The item is usually not shown to the user
+ Thumbnail = 10 ///< The item is a thumbnail picture of the file
+
+ };
+
+ /**
+ * This enum exists so that you can specify units for items, which you
+ * can usually use for integer items, so an application knows how to
+ * display it (e.g. a time in seconds in a hh:mm:ss form). You can either
+ * use one of those units, or if you don't find one that fits, you can
+ * add it yourself using a prefix and/or suffix.
+ */
+ enum Unit {
+ NoUnit = 0, ///< None of the listed units
+ Seconds = 1, ///< The item represents a time in seconds
+ MilliSeconds = 2, ///< The item represents a time in milliseconds
+ BitsPerSecond = 3, ///< A bit rate
+ Pixels = 4, ///< For image dimensions and similar
+ Inches = 5, ///< Sizes
+ Centimeters = 6, ///< Sizes
+ Bytes = 7, ///< Some data/file size in bytes
+ FramesPerSecond = 8, ///< A frame rate @since 3.1
+ DotsPerInch = 9, ///< Resolution in DPI @since 3.1
+ BitsPerPixel = 10, ///< A bit depth @since 3.1
+ Hertz = 11, ///< Sample rates and similar @since 3.1
+ KiloBytes = 12, ///< Some data/file size in kilobytes @since 3.1
+ Millimeters = 13 ///< Sizes @since 3.3
+ };
+
+
+ class ItemInfo;
+
+ /**
+ * @brief Information about a meta information group
+ *
+ * This is the class for one group of items of a KFileMimeTypeInfo.
+ * It contains, among other things, the information about the group's name
+ * and a list of supported items.
+ */
+ class TDEIO_EXPORT GroupInfo
+ {
+
+ friend class KFilePlugin;
+ friend class KFileMimeTypeInfo;
+ public:
+ /**
+ * Use this method to get a list of keys in the specified group that
+ * the plugin knows about. No variable keys.
+ * For a group that doesn't support variable keys, all keys that this
+ * group may have are returned. For a group that does support them, the
+ * non-variable ones are returned. See KFileMetaInfo about variable
+ * keys
+ *
+ * @return the list of keys supported for this mimetype
+ **/
+ TQStringList supportedKeys() const
+ {
+ return m_supportedKeys;
+ }
+
+ /**
+ * Use this method to get the name of the group. This string doesn't
+ * depend on the user's locale settings
+ *
+ * @return the group name
+ */
+ const TQString& name() const
+ {
+ return m_name;
+ }
+
+ /**
+ * Use this method to get the string to display to the user as group
+ * name. This may be different to name() and it returns the
+ * name in the user's language
+ *
+ * @return the translated group name
+ */
+ const TQString& translatedName() const
+ {
+ return m_translatedName;
+ }
+
+ /**
+ * A group object can contain several item objects (of which you can
+ * get the names with supportedKeys() . With this method, you can
+ * get one of those item objects. See ItemInfo
+ *
+ * @return a pointer to the item info. Don't delete this object!
+ */
+ const ItemInfo * itemInfo( const TQString& key ) const;
+
+ /**
+ * Get the attributes of this group (see Attributes)
+ *
+ * @return the attributes
+ */
+ uint attributes() const
+ {
+ return m_attr;
+ }
+
+ /**
+ * @return true if this group supports adding or removing arbitrary
+ * keys, false if not.
+ **/
+ bool supportsVariableKeys() const
+ {
+ return m_variableItemInfo;
+ }
+
+ /**
+ * If the group supports variable keys, you can query their item
+ * info with this method. The main reason for this is that you can
+ * get the type and attributes of variable keys.
+ *
+ * @return a pointer to the item info. Don't delete this object!
+ **/
+ const ItemInfo* variableItemInfo( ) const
+ {
+ return m_variableItemInfo;
+ }
+
+ /** @internal */
+ ~GroupInfo();
+ private:
+ /** @internal */
+ GroupInfo( const TQString& name, const TQString& translatedName);
+
+ /** @internal */
+ KFileMimeTypeInfo::ItemInfo* addItemInfo( const TQString& key,
+ const TQString& translatedKey,
+ TQVariant::Type type);
+
+ /** @internal */
+ void addVariableInfo( TQVariant::Type type, uint attr );
+
+ TQString m_name;
+ TQString m_translatedName;
+ TQStringList m_supportedKeys;
+ uint m_attr;
+ ItemInfo* m_variableItemInfo;
+ TQDict<ItemInfo> m_itemDict;
+
+ };
+
+ /**
+ * This is the class for one item of a KFileMimeTypeInfo.
+ * It contains every information about a KFileMetaInfoItem that this
+ * item has in common for each file of a specific mimetype.
+ **/
+ class TDEIO_EXPORT ItemInfo
+ {
+ friend class KFilePlugin;
+ friend class GroupInfo;
+ public:
+ /** @internal */
+ ItemInfo() {} // ### should be private?
+
+ /**
+ *
+ * This method returns a translated prefix to be displayed before the
+ * value. Think e.g. of the $ in $30
+ *
+ * @return the prefix
+ */
+ const TQString& prefix() const
+ {
+ return m_prefix;
+ }
+
+ /**
+ * This method returns a translated suffix to be displayed after the
+ * value. Think of the kbps in 128kbps
+ *
+ * @return the prefix
+ */
+ const TQString& suffix() const
+ {
+ return m_suffix;
+ }
+
+ /**
+ * The items for a file are stored as a TQVariant and this method
+ * can be used to get the data type of this item.
+ *
+ * @return the TQVariant type
+ */
+ TQVariant::Type type() const
+ {
+ return m_type;
+ }
+
+ /**
+ * Returns the name of the item.
+ * @return the name of the item
+ */
+ const TQString& key() const
+ {
+ return m_key;
+ }
+
+ /**
+ * Returns a string for the specified @p value, if possible. If not,
+ * TQString::null is returned. This can be used by programs if they want
+ * to display a sum or an average of some item for a list of files.
+ *
+ * @param value the value to convert
+ * @param mangle if true, the string will already contain prefix and
+ * suffix
+ * @return the converted string, or TQString::null if not possible
+ * @since 3.1
+ */
+ TQString string( const TQVariant& value, bool mangle = true ) const;
+
+ /**
+ * Is this item the variable item?
+ *
+ * @return true if it is, false if not
+ */
+ bool isVariableItem() const
+ {
+ // every valid item is supposed to have a non-null key
+ return key().isNull();
+ }
+
+ /**
+ * Returns a translation of the key for displaying to the user. If the
+ * plugin provides translation to the key, it's also in the user's
+ * language.
+ * @return the translated key
+ */
+ const TQString& translatedKey() const
+ {
+ return m_translatedKey;
+ }
+
+ /**
+ * Return the attributes of the item. See
+ * KFileMimeTypeInfo::Attributes.
+ * @return the attributes
+ */
+ uint attributes() const
+ {
+ return m_attr;
+ }
+
+ /**
+ * Return the hints for the item. See
+ * KFileMimeTypeInfo::Hint
+ * @return the hint
+ */
+ uint hint() const
+ {
+ return m_hint;
+ }
+
+ /**
+ * Return the unit of the item. See
+ * KFileMimeTypeInfo::Unit
+ * @return the unit
+ */
+ uint unit() const
+ {
+ return m_unit;
+ }
+
+ private:
+ /** @internal */
+ ItemInfo(const TQString& key, const TQString& translatedKey,
+ TQVariant::Type type)
+ : m_key(key), m_translatedKey(translatedKey),
+ m_type(type),
+ m_attr(0), m_unit(NoUnit), m_hint(NoHint),
+ m_prefix(TQString::null), m_suffix(TQString::null)
+ {}
+
+ TQString m_key;
+ TQString m_translatedKey;
+ TQVariant::Type m_type;
+ uint m_attr;
+ uint m_unit;
+ uint m_hint;
+ TQString m_prefix;
+ TQString m_suffix;
+ };
+
+ // ### could it be made private? Would this be BC?
+ ~KFileMimeTypeInfo();
+
+ /**
+ * Creates a validator for this item. Make sure to supply a proper
+ * @p parent argument or delete the validator yourself.
+ *
+ * @param group the group of the item
+ * @param key the key of the item
+ * @param parent the parent of the TQObject, or 0 for a parent-less object
+ * @param name the name of the TQObject, can be 0
+ * @return the validator. You are responsible for deleting it. 0 if
+ * creation failed
+ */
+ TQValidator * createValidator(const TQString& group, const TQString& key,
+ TQObject *parent = 0, const char *name = 0) const;
+
+ /**
+ * Returns the list of all groups that the plugin for this mimetype
+ * supports.
+ *
+ * @return the list of groups
+ */
+ TQStringList supportedGroups() const;
+
+ /**
+ * Same as the above function, but returns the strings to display to the
+ * user.
+ *
+ * @return the list of groups
+ */
+ TQStringList translatedGroups() const;
+
+ /**
+ * This returns the list of groups in the preferred order that's specified
+ * in the .desktop file.
+ *
+ * @return the list of groups
+ */
+ TQStringList preferredGroups() const
+ {
+ return m_preferredGroups;
+ }
+
+ /**
+ * Returns the mimetype to which this info belongs.
+ *
+ * @return the mimetype of this info
+ */
+ TQString mimeType() const {return m_mimeType;}
+
+ /**
+ * Get the group info for a specific group.
+ *
+ * @param group the group whose group info should be retrieved
+ * @return a pointer to the info. 0 if it does not
+ * exist. Don't delete this object!
+ */
+ const GroupInfo * groupInfo( const TQString& group ) const;
+
+ // always returning stringlists which the user has to iterate and use them
+ // to look up the real items sounds strange to me. I think we should add
+ // our own iterators some time (somewhere in the future ;)
+
+ /**
+ * Return a list of all supported keys without looking for a specific
+ * group
+ *
+ * @return the list of keys
+ */
+ TQStringList supportedKeys() const;
+
+ /**
+ * Return a list of all supported keys in preference order
+ *
+ * @return the list of keys
+ */
+ TQStringList preferredKeys() const
+ {
+ return m_preferredKeys;
+ }
+
+ // ### shouldn't this be private? BC?
+ GroupInfo * addGroupInfo( const TQString& name,
+ const TQString& translatedName);
+
+ TQString m_translatedName;
+ TQStringList m_supportedKeys;
+ uint m_attr;
+ // bool m_supportsVariableKeys : 1;
+ TQDict<ItemInfo> m_itemDict;
+
+// ### this should be made private instead, but this would be BIC
+protected:
+ /** @internal */
+ KFileMimeTypeInfo( const TQString& mimeType );
+
+ TQDict<GroupInfo> m_groups;
+ TQString m_mimeType;
+ TQStringList m_preferredKeys; // same as KFileMetaInfoProvider::preferredKeys()
+ TQStringList m_preferredGroups; // same as KFileMetaInfoProvider::preferredKeys()
+};
+
+
+/**
+ * @brief A meta information item about a file
+ *
+ * This is one item of the meta information about a file (see
+ * KFileMetaInfo).
+ */
+class TDEIO_EXPORT KFileMetaInfoItem
+{
+public:
+ class Data;
+ typedef KFileMimeTypeInfo::Hint Hint;
+ typedef KFileMimeTypeInfo::Unit Unit;
+ typedef KFileMimeTypeInfo::Attributes Attributes;
+
+ /**
+ * @internal
+ * You usually don't need to use this constructor yourself. Let
+ * KFileMetaInfo do it for you.
+ **/
+ // ### hmm, then it should be private
+ KFileMetaInfoItem( const KFileMimeTypeInfo::ItemInfo* mti,
+ const TQString& key, const TQVariant& value);
+
+ /**
+ * Copy constructor
+ **/
+ KFileMetaInfoItem( const KFileMetaInfoItem & item );
+
+ /**
+ * The assignment operator, so you can do:
+ * @code
+ * KFileMetaInfoItem item = info.item("Title");
+ * @endcode
+ *
+ * This will create a shared copy of the object. The actual data
+ * is automatically deleted if all copies go out of scope
+ **/
+ const KFileMetaInfoItem& operator= (const KFileMetaInfoItem & item );
+
+ /**
+ * Default constructor. This creates an "invalid" item
+ */
+ KFileMetaInfoItem();
+
+ ~KFileMetaInfoItem();
+
+ /**
+ * Returns the key of the item.
+ *
+ * @return the key of this item
+ */
+ TQString key() const;
+
+ /**
+ * Returns a translation of the key for displaying to the user. If the
+ * plugin provides translation to the key, it's also in the user's language
+ *
+ * @return the translated key
+ */
+ TQString translatedKey() const;
+
+ /**
+ * Returns the value of the item.
+ *
+ * @return the value of the item.
+ */
+ const TQVariant& value() const;
+
+ /**
+ * Returns a string containing the value, if possible. If not,
+ * TQString::null is returned.
+ *
+ * @param mangle if true, the string will already contain prefix and
+ * suffix
+ * @return the value string, or TQString::null if not possible
+ */
+ TQString string( bool mangle = true ) const;
+
+ /**
+ * Changes the value of the item.
+ *
+ * @param value the new value
+ * @return true if successful, false otherwise
+ */
+ bool setValue( const TQVariant& value );
+
+ /**
+ * Return the type of the item.
+ *
+ * @return the type of the item
+ */
+ TQVariant::Type type() const;
+
+ /**
+ * You can query if the application can edit the item and write it back to
+ * the file with this method.
+ *
+ * @note This doesn't ensure that you have write access to the file and
+ * that enough space is available.
+ *
+ * @return true if the item's value can be changed, false if not
+ */
+ bool isEditable() const;
+
+ /**
+ * If you remove an item, it is only marked for removal for the file. On
+ * the next KFileMetaInfo::applyChanges() , it will be removed from
+ * the file. With this method, you can ask if the item is marked for
+ * removal.
+ *
+ * @return true if the item was removed, false if not
+ */
+ bool isRemoved() const;
+
+ /**
+ * If you change an item, it is marked as "dirty". On the next
+ * KFileMetaInfo::applyChanges() , the change will be written to the
+ * file. With this method, you can ask if this item is dirty.
+ *
+ * @return true if the item contains changes that have not yet been written
+ * back into the file. Removing or adding an item counts as such a change
+ */
+ bool isModified() const;
+
+ /**
+ * This method returns a translated prefix to be displayed before the
+ * value. Think e.g. of the $ in $30
+ *
+ * @return the prefix
+ */
+ TQString prefix() const;
+
+ /**
+ * This method returns a translated suffix to be displayed after the
+ * value. Think of the kbps in 128kbps
+ *
+ * @return the suffix
+ */
+ TQString suffix() const;
+
+ /**
+ * Returns the hint for this item. See KFileMimeTypeInfo::Hint.
+ *
+ * @return the hint
+ **/
+ uint hint() const;
+
+ /**
+ * Returns the unit for this item. See KFileMimeTypeInfo::Unit.
+ *
+ * @return the unit
+ * @since 3.2
+ **/
+ uint unit() const;
+
+ /**
+ * Returns the attributes for this item. See
+ * KFileMimeTypeInfo::Attributes.
+ *
+ * @return the attributes
+ **/
+ uint attributes() const;
+
+ /**
+ * Return true if the item is valid, i.e. if it contains data, false
+ * if it's invalid (created with the default constructor and not been
+ * assigned anything), or if KFileMetaInfoGroup::item() didn't find
+ * your requested item).
+ *
+ * @return true if valid, false if invalid
+ */
+ bool isValid() const;
+
+ TDEIO_EXPORT friend TQDataStream& operator >>(TQDataStream& s, KFileMetaInfoItem& );
+ TDEIO_EXPORT friend TQDataStream& operator >>(TQDataStream& s, KFileMetaInfoGroup& );
+ TDEIO_EXPORT friend TQDataStream& operator <<(TQDataStream& s, const KFileMetaInfoItem& );
+ friend class KFileMetaInfoGroup;
+
+protected:
+ void setAdded();
+ void setRemoved();
+
+ void ref();
+ void deref();
+
+ Data *d;
+};
+
+/**
+ * @brief A group of meta information items about a file
+ *
+ * This is one group of meta information items about a file (see
+ * KFileMetaInfo).
+ */
+class TDEIO_EXPORT KFileMetaInfoGroup
+{
+ friend class KFilePlugin;
+ friend class KFileMetaInfo;
+ TDEIO_EXPORT friend TQDataStream& operator >>(TQDataStream& s, KFileMetaInfoGroup& );
+ TDEIO_EXPORT friend TQDataStream& operator <<(TQDataStream& s, const KFileMetaInfoGroup& );
+
+public:
+ class Data;
+ /**
+ * @internal
+ * You usually don't need to use this constructor yourself. Let
+ * KFileMetaInfo do it for you.
+ **/
+ // ### hmm, then it should be private
+ KFileMetaInfoGroup( const TQString& name, const KFileMimeTypeInfo* info );
+
+ /**
+ * Copy constructor
+ **/
+ KFileMetaInfoGroup( const KFileMetaInfoGroup& original );
+
+ /**
+ * The assignment operator, so you can do:
+ * @code
+ * KFileMetaInfoGroup group = info.group("Technical");
+ * @endcode
+ *
+ * This will create a shared copy of the object. The actual data
+ * is automatically deleted if all copies go out of scope
+ **/
+ const KFileMetaInfoGroup& operator= (const KFileMetaInfoGroup& info );
+
+ /**
+ * Default constructor. This creates an "invalid" item
+ *
+ * @since 3.1
+ */
+ KFileMetaInfoGroup();
+
+ ~KFileMetaInfoGroup();
+
+ /**
+ * Returns true if the item is valid, i.e. if it contains data, false
+ * if it's invalid (created with the default constructor and not been
+ * assigned anything), or if KFileMetaInfoGroup::item() didn't find
+ * your requested item).
+ *
+ * @return true if valid, false if invalid
+ */
+ bool isValid() const;
+
+ /**
+ * Returns false if the object contains data, true if it's empty. An
+ * empty group is a group with no items (amazing, isn't it?).
+ *
+ * @return true if empty, false otherwise
+ */
+ bool isEmpty() const;
+
+ /**
+ * Returns true if an item as added or removed from the group.
+ *
+ * @return true if an item was added or removed from the group, otherwise
+ * false.
+ *
+ * @since 3.1
+ */
+ bool isModified() const;
+
+ /**
+ * Operator for convenience. It does the same as item(),
+ * but you cannot specify a group to search in
+ */
+ KFileMetaInfoItem operator[]( const TQString& key ) const
+ { return item( key ); }
+
+ /**
+ * This method searches for the specified item.
+ *
+ * @param key the key of the item to search
+ * @return the specified item if found, an invalid item, if not
+ **/
+ KFileMetaInfoItem item( const TQString& key ) const;
+
+ /**
+ * Returns the item with the given @p hint.
+ *
+ * @param hint the hint of the item
+ * @return the item with the specified @p hint
+ **/
+ KFileMetaInfoItem item( uint hint ) const;
+
+ /**
+ * Convenience function. Returns the value of the specified key.
+ * It does the same as item(key).value().
+ *
+ * @param key the key of the item to search
+ * @return the value with the given key
+ */
+ const TQVariant value( const TQString& key ) const
+ {
+ const KFileMetaInfoItem &i = item( key );
+ return i.value();
+ }
+
+ /**
+ * Use this method to get a list of keys in the specified group that
+ * the plugin knows about. No variable keys.
+ * For a group that doesn't support variable keys, all keys that this
+ * group may have are returned. For a group that does support them, the
+ * non-variable ones are returned. See KFileMetaInfo about variable
+ * keys
+ *
+ * @return the list of keys supported for this mimetype
+ **/
+ TQStringList supportedKeys() const;
+
+ /**
+ * Returns true if this group supports adding or removing arbitrary
+ * keys, false if not.
+ *
+ * @return true is variable keys are supported, false otherwise
+ **/
+ bool supportsVariableKeys() const;
+
+ /**
+ * Checks whether an item with the given @p key exists.
+ *
+ * @return true if an item for this @p key exists.
+ */
+ bool contains( const TQString& key ) const;
+
+ /**
+ * Returns a list of all keys.
+ *
+ * @return a list of all keys in the order they were inserted.
+ **/
+ TQStringList keys() const;
+
+ /**
+ * Returns a list of all keys in preference order.
+ *
+ * @return a list of all keys in preference order.
+ **/
+ TQStringList preferredKeys() const;
+
+ /**
+ * @return the list of possible types that the value for the specified key
+ * can be. You can use this to determine the possible types for new
+ * keys before you add them.
+ *
+ **/
+ // ### do we really want to support that?
+ // let's not waste time on thinking about it. Let's just kick it for now
+ // and add it in 4.0 if needed ;)
+// const TQMemArray<TQVariant::Type>& types( const TQString& key ) const;
+
+ /**
+ * Add an item to the info. This is only possible if the specified @p key
+ * is in the supportedKeys list and not yet defined or if
+ * the group supports variable keys.
+ *
+ * @param key the key of the item
+ * @return the KFileMetaInfoItem for the given @p key
+ **/
+ KFileMetaInfoItem addItem( const TQString& key );
+
+ /**
+ * Remove this item from the meta info of the file. You cannot query
+ * KFileMetaInfo for a removed object, but you can query for a list of
+ * removed items with removedItems() if you need to.
+ * If you re-add it, its value will be cleared.
+ *
+ * @param key the key of the removed item
+ * @return true if successful, false otherwise
+ */
+ bool removeItem(const TQString& key);
+
+ /**
+ * Returns a list of all removed items.
+ *
+ * @return a list of all removed items
+ */
+ TQStringList removedItems();
+
+ /**
+ * The name of this group.
+ *
+ * @return the name of this group
+ */
+ TQString name() const;
+
+ /**
+ * The translated name of this group.
+ *
+ * @return the translated name of this group
+ *
+ * @since 3.2
+ */
+ TQString translatedName() const;
+
+ /**
+ * Returns the attributes of this item.
+ *
+ * @return the attributes
+ */
+ uint attributes() const;
+
+protected:
+ void setAdded();
+ KFileMetaInfoItem appendItem( const TQString& key, const TQVariant& value);
+
+ Data* d;
+ void ref();
+ void deref();
+
+};
+
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+
+
+/**
+ * @brief Meta Information about a file
+ *
+ * This is the class for objects that hold meta information about a file.
+ * The information is kept in form of a system of key/value pairs. See also
+ * KFileMetaInfoItem.
+ * This information is retrieved from the file through a plugin system, and
+ * this class is the main interface to it.
+ * If you want to write your own plugin, have a look at KFilePlugin.
+ * There are basically two different kinds of meta information: Fixed ones
+ * that the plugin knows about (e.g. an mp3 id3v1 tag has a well defined
+ * fixed list of fields), and variable keys that exist in mimetypes that
+ * support their own key/value system (comments in png files are of this type).
+ * Almost every file has fixed keys, but some also have variable keys.
+ *
+ * The groups and the What enum are not yet supported, but already added to
+ * the interface so that adding support doesn't break compatibility.
+ */
+class TDEIO_EXPORT KFileMetaInfo
+{
+public:
+ typedef KFileMimeTypeInfo::Hint Hint;
+ typedef KFileMimeTypeInfo::Unit Unit;
+ typedef KFileMimeTypeInfo::Attributes Attributes;
+ class Data;
+
+ /**
+ * This is used to specify what a KFileMetaInfo object should read, so
+ * you can specify if you want to read "expensive" items or not.
+ */
+ enum What
+ {
+ Fastest = 0x1, /**< do the fastest possible read and omit all items
+ that might need a significantly longer time
+ than the others */
+ DontCare = 0x2, ///< let the plugin decide what to read
+
+ TechnicalInfo = 0x4, /**< extract technical details about the file, like
+ e.g. play time, resolution or a compressioni
+ type */
+ ContentInfo = 0x8, /**< read information about the content of the file,
+ like comments or id3 tags */
+ ExtenedAttr = 0x10, /**< read filesystem based extended attributes if
+ they are supported for the filesystem */
+ Thumbnail = 0x20, /**< only read the file's thumbnail, if it contains
+ one */
+ Preferred = 0x40, ///< get at least the preferred items
+ Everything = 0xffff ///< read everything, even if it might take a while
+
+ };
+
+ /**
+ * The constructor.
+ *
+ * creating a KFileMetaInfo item through this will autoload the plugin
+ * belonging to the mimetype and try to get meta information about
+ * the specified file.
+ *
+ * If no info is available, you'll get an empty (not invalid) object.
+ * You can test for it with the isEmpty() method.
+ *
+ * @param path The file name. This must be the path to a local file.
+ * @param mimeType The name of the file's mimetype. If ommited, the
+ * mimetype is autodetected
+ * @param what one or more of the What enum values. It gives some
+ * hint to the plugin what information is desired. The plugin
+ * may still return more items.
+ *
+ * @note This version will @b only work for @b local (file:/) files.
+ *
+ **/
+ KFileMetaInfo( const TQString& path,
+ const TQString& mimeType = TQString::null,
+ uint what = Fastest);
+
+ /**
+ * Another constructor
+ *
+ * Similar to the above, but takes a URL so that meta-data may be retrieved
+ * over other protocols (ftp, etc.)
+ *
+ **/
+ KFileMetaInfo( const KURL& url,
+ const TQString& mimeType = TQString::null,
+ uint what = Fastest);
+
+ /**
+ * Default constructor. This will create an invalid object (see
+ * isValid().
+ **/
+ KFileMetaInfo();
+
+ /**
+ * Copy constructor. This creates a copy of the original object, but
+ * that copy will point to the same data, so if you change the original,
+ * the copy will be changed, too. After all, they are referring to the same
+ * file.
+ **/
+ KFileMetaInfo( const KFileMetaInfo& original);
+
+ ~KFileMetaInfo();
+
+ /**
+ * The assignment operator, so you can do e.g.:
+ * @code
+ * KFileMetaInfo info;
+ * if (something) info = KFileMetaInfo("/the/file");
+ * @endcode
+ *
+ * This will create a shared copy of the object. The actual data
+ * is automatically deleted if all copies go out of scope.
+ **/
+ const KFileMetaInfo& operator= (const KFileMetaInfo& info );
+
+
+ /**
+ * Returns a list of all groups.
+ *
+ * @return the keys of the groups that the file has.
+ */
+ TQStringList groups() const;
+
+ /**
+ * Returns a list of all supported groups.
+ *
+ * @return the supported keys of the groups that the file has.
+ */
+ TQStringList supportedGroups() const;
+
+ /**
+ * Returns a list of the preferred groups.
+ *
+ * @return the keys of the preferred groups that the file has.
+ */
+ TQStringList preferredGroups() const;
+
+ /**
+ * Returns a list of all preferred keys.
+ *
+ * @return a list of all preferred keys.
+ */
+ TQStringList preferredKeys() const;
+
+ /**
+ * Returns a list of supported keys.
+ *
+ * @return a list of supported keys
+ */
+ TQStringList supportedKeys() const;
+
+ /**
+ * Returns the list of groups that you can add or remove from the file.
+ *
+ * @return the groups can be added or removed
+ */
+ TQStringList editableGroups() const;
+
+ // I'd like to keep those for lookup without group, at least the hint
+ // version
+ /**
+ * Returns the KFileMetaInfoItem with the given @p key.
+ *
+ * @param key the key of the item
+ * @return the item. Invalid if there is no item with the given @p key.
+ */
+ KFileMetaInfoItem item(const TQString& key) const;
+ /**
+ * Returns the KFileMetaInfoItem with the given @p hint.
+ *
+ * @param hint the hint of the item
+ * @return the item. Invalid if there is no item with the given @p hint.
+ */
+ KFileMetaInfoItem item(const KFileMetaInfoItem::Hint hint) const;
+
+ /**
+ * Saves the item with the given @p key.
+ *
+ * @param key the key of the item
+ * @param preferredGroup the preferred group, or TQString::null
+ * @param createGroup true to create the group if necessary
+ * @return the saved item
+ */
+ KFileMetaInfoItem saveItem( const TQString& key,
+ const TQString& preferredGroup = TQString::null,
+ bool createGroup = true );
+
+ /**
+ * Returns the KFileMetaInfoGroup with the given @p key.
+ *
+ * @param key the key of the item
+ * @return the group. Invalid if there is no group with the given @p key.
+ */
+ KFileMetaInfoGroup group(const TQString& key) const;
+
+ /**
+ * Returns the KFileMetaInfoGroup with the given @p key.
+ *
+ * @param key the key of the item
+ * @return the group. Invalid if there is no group with the given @p key.
+ */
+ KFileMetaInfoGroup operator[] (const TQString& key) const
+ {
+ return group(key);
+ }
+
+ /**
+ * Try to add the specified group. This will only succeed if it is
+ * in the list of editableGroups().
+ *
+ * @note that all non-variable items that belong to this group are
+ * automatically added as empty item.
+ *
+ * @param name the name of the group to add
+ * @return true if successful, false if not
+ */
+ bool addGroup( const TQString& name );
+
+ /**
+ * Remove the specified group. This will only succeed if it is
+ * in the list of editableGroups(). Beware that this also
+ * removes all the items in that group, so always ask the user
+ * before removing it!
+ *
+ * @param name the name of the group to remove
+ * @return true if successful, false if not
+ */
+ bool removeGroup( const TQString& name );
+
+ /**
+ * Returns a list of removed groups.
+ *
+ * @return a list of removed groups.
+ */
+ TQStringList removedGroups();
+
+ /**
+ * This method writes all pending changes of the meta info back to the file.
+ * If any items are marked as removed, they are really removed from the
+ * list. The info object as well as all items are updated.
+ *
+ * @return true if successful, false if not
+ */
+ bool applyChanges();
+
+ /**
+ * This method writes all pending changes of the meta info to the file @p path.
+ * If any items are marked as removed, they are really removed from the
+ * list. The info object as well as all items are updated.
+ *
+ * @return true if successful, false if not
+ */
+ bool applyChanges(const TQString& path);
+
+ /**
+ * Checks whether an item with the given @p key exists.
+ *
+ * @param key the key to check
+ * @return whether an item for this @p key exists.
+ */
+ bool contains( const TQString& key ) const;
+
+ /**
+ * Checks whether a group with the given @p key exists.
+ *
+ * @param key the key to check
+ * @return whether a group with this name exists.
+ */
+ bool containsGroup( const TQString& key ) const;
+
+ /**
+ * Returns the value with the given @p key.
+ *
+ * @param key the key to retrieve
+ * @return the value. Invalid if it does not exist
+ */
+ const TQVariant value( const TQString& key ) const
+ {
+ return item(key).value();
+ }
+
+
+ /**
+ * Returns true if the item is valid, i.e. if actually represents the info
+ * about a file, false if the object is uninitialized.
+ *
+ * @return true if valid, false otherwise
+ */
+ bool isValid() const;
+
+ /**
+ * Returns false if the object contains data, true if it's empty. You'll
+ * get an empty object if no plugin for the file could be found.
+ *
+ * @return true if empty, false otherwise
+ */
+ bool isEmpty() const;
+
+ /**
+ * Returns the mime type of file.
+ *
+ * @return the file's mime type
+ */
+ TQString mimeType() const;
+
+ /**
+ * Returns the path of file - or TQString::null if file is non-local
+ *
+ * @return the file's path - or TQString::null if file is non-local
+ */
+ TQString path() const;
+
+ /**
+ * Returns the url of file
+ *
+ * @return the file's url
+ */
+ KURL url() const;
+
+ TDEIO_EXPORT friend TQDataStream& operator >>(TQDataStream& s, KFileMetaInfo& );
+ TDEIO_EXPORT friend TQDataStream& operator <<(TQDataStream& s, const KFileMetaInfo& );
+ friend class KFilePlugin;
+
+protected:
+ KFileMetaInfoGroup appendGroup(const TQString& name);
+
+ /**
+ * @return a pointer to the plugin that belogs to this object's mimetype.
+ * It will be auto-loaded if it's currently not loaded
+ **/
+ KFilePlugin * plugin() const;
+
+ void ref();
+ void deref();
+
+ Data* d;
+
+private:
+ KFileMetaInfoItem findEditableItem( KFileMetaInfoGroup& group,
+ const TQString& key );
+
+ void init( const KURL& url,
+ const TQString& mimeType = TQString::null,
+ uint what = Fastest);
+};
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+
+
+/**
+ * @brief Base class for a meta information plugin
+ *
+ * Meta information plugins are used to extract useful information from files
+ * of a given type. These plugins are used in Konqueror's file properties
+ * dialog, for example.
+ *
+ * If you want to write your own plugin, you need to derive from this class.
+ *
+ * In the constructor of your class, you need to call addMimeTypeInfo() to tell
+ * the KFile framework which mimetype(s) your plugin supports. For each
+ * mimetype, use the addGroupInfo() and addItemInfo() methods to declare the
+ * meta information items the plugin calculates and to group them accordingly.
+ * For groups, use setAttributes() to customize your group (see
+ * KFileMimeTypeInfo::Attributes). For items, use setAttributes() to define the
+ * behaviour of the item; use setHint() to define the meaning of the item; use
+ * setUnit() to define the Unit, such as KFileMimeTypeInfo::Seconds or
+ * KFileMimeTypeInfo::KiloBytes. In short, the constructor defines the data
+ * structure of the meta information supported by your plugin.
+ *
+ * Example:
+ * @code
+ * FooPlugin::FooPlugin(TQObject *parent, const char *name,
+ * const TQStringList &args)
+ * : KFilePlugin(parent, name, args)
+ * {
+ * KFileMimeTypeInfo* info = addMimeTypeInfo( "application/x-foo" );
+ *
+ * // our new group
+ * KFileMimeTypeInfo::GroupInfo* group = 0L;
+ * group = addGroupInfo(info, "FooInfo", i18n("Foo Information"));
+ *
+ * KFileMimeTypeInfo::ItemInfo* item;
+ *
+ * // our new items in the group
+ * item = addItemInfo(group, "Items", i18n("Items"), TQVariant::Int);
+ * item = addItemInfo(group, "Size", i18n("Size"), TQVariant::Int);
+ * setUnit(item, KFileMimeTypeInfo::KiloBytes);
+ *
+ * // strings are possible, too:
+ * //addItemInfo(group, "Document Type", i18n("Document type"), TQVariant::String);
+ * }
+ * @endcode
+ *
+ * Some meta information items are likely to be available in several different
+ * file formats, such as @c "Author", @c "Title" (for documents), and
+ * @c "Length" (for multimedia files). Be sure to use the naming scheme from
+ * existing plugins for your meta information items if possible. If, for
+ * example, the meta information of a group of files is shown in a table view,
+ * this will allow two files to share the same column (say "Length") even if
+ * they are of a different file type.
+ *
+ * You must overwrite the readInfo() method. In this method you need to extract
+ * the meta information of the given file. You can use a third-party library to
+ * achieve this task. This might be the best way for binary files, since a
+ * change in the file format is likely to be supported by subsequent releases
+ * of that library. Alternatively, for text-based file formats, you can use
+ * TQTextStream to parse the file. For simple file formats, TQRegExp can be of
+ * great help, too.
+ *
+ * After you extracted the relevant information, use appendGroup() and
+ * appendItem() to fill the meta information data structure (as defined in the
+ * constructor) with values. Note that you can leave out groups or items
+ * which are not appropriate for a particular file.
+ *
+ * Example:
+ * @code
+ * bool FooPlugin::readInfo( KFileMetaInfo& info, uint what)
+ * {
+ * int numItems = 0;
+ * int size = 0;
+ *
+ * // do your calculations here, e.g. using a third-party
+ * // library or by writing an own parser using e.g. QTextStream
+ *
+ * // calculate numItems and size ...
+ *
+ * // note: use the same key strings as in the constructor
+ * KFileMetaInfoGroup group = appendGroup(info, "FooInfo");
+ *
+ * appendItem(group, "Items", numItems);
+ * appendItem(group, "Size", size);
+ *
+ * return true;
+ * }
+ * @endcode
+ *
+ * If you want to define mutable meta information items, you need to overwrite
+ * the writeInfo() method. In this method, you can use third-party library
+ * (appropriate mostly for binary files, see above) or TQTextStream to write the
+ * information back to the file. If you use TQTextStream, be sure to write all
+ * file contents back.
+ *
+ * For some items, it might be that not all possible values are allowed. You
+ * can overwrite the createValidator() method to define constraints for a meta
+ * information item. For example, the @c "Year" field for an MP3 file could
+ * reject values outside the range 1500 - 2050 (at least for now). The
+ * validator is used to check values before the writeInfo() method is called so
+ * that writeInfo() is only provided correct values.
+ *
+ * In your plugin, you need to create a factory for the KFilePlugin
+ *
+ * Example:
+ * @code
+ * typedef KGenericFactory<FooPlugin> FooFactory;
+ * K_EXPORT_COMPONENT_FACTORY(tdefile_foo, FooFactory("tdefile_foo"));
+ * @endcode
+ *
+ * To make your plugin available within KDE, you also need to provide a
+ * @c .desktop file which describes your plugin. The required fields in the
+ * file are:
+ *
+ * - @c Type: must be @c "Service"
+ * - @c Name: the name of the plugin
+ * - @c ServiceTypes: must contain @c "KFilePlugin"
+ * - @c X-TDE-Library: the name of the library containing the KFile plugin
+ * - @c MimeType: the mimetype(s) which are supported by the plugin
+ * - @c PreferredGroups: a comma-separated list of the most important groups.
+ * This list defines the order in which the meta information groups should be
+ * displayed
+ * - @c PreferredItems: a comma-separated list of the most important items.
+ * This list defines the order in which the meta information items should be
+ * displayed
+ *
+ * Example:
+ * @code
+ * [Desktop Entry]
+ * Encoding=UTF-8
+ * Type=Service
+ * Name=Foo Info
+ * ServiceTypes=KFilePlugin
+ * X-TDE-Library=tdefile_foo
+ * MimeType=application/x-foo
+ * PreferredGroups=FooInfo
+ * PreferredItems=Items,Size
+ * @endcode
+ **/
+class TDEIO_EXPORT KFilePlugin : public TQObject
+{
+ Q_OBJECT
+
+public:
+ /**
+ * Creates a new KFilePlugin instance. You need to implement a constructor
+ * with the same argument list as this is required by KGenericFactory
+ *
+ * @param parent the parent of the TQObject, can be @c 0
+ * @param name the name of the TQObject, can be @c 0
+ * @param args currently ignored
+ *
+ * @see addMimeTypeInfo()
+ * @see addGroupInfo()
+ * @see addItemInfo()
+ * @see TQObject()
+ **/
+ KFilePlugin( TQObject *parent, const char *name,
+ const TQStringList& args );
+
+ /**
+ * Destructor
+ */
+ virtual ~KFilePlugin();
+
+ /**
+ * Read the info from the file in this method and insert it into the
+ * provided KFileMetaInfo object. You can get the path to the file with
+ * KFileMetaInfo::path(). Use appendGroup() and appendItem() to fill
+ * @p info with the extracted values
+ *
+ * @param info the information will be written here
+ * @param what defines what to read, see KFileMetaInfo::What
+ * @return @c true if successful, @c false if it failed
+ *
+ * @see writeInfo()
+ **/
+ virtual bool readInfo( KFileMetaInfo& info,
+ uint what = KFileMetaInfo::Fastest ) = 0;
+
+ /**
+ * Similar to the readInfo() but for writing the info back to the file.
+ * If you don't have any writable keys, don't implement this method
+ *
+ * @param info the information that will be written
+ * @return @c true if successful, @c false if it failed
+ **/
+ virtual bool writeInfo( const KFileMetaInfo& info ) const
+ {
+ Q_UNUSED(info);
+ return true;
+ }
+
+ /**
+ * This method should create an appropriate validator for the specified
+ * item if it's editable or return a null pointer if not. If you don't have
+ * any editable items, you don't need to implement this method.
+ *
+ * If you you don't need any validation, e.g. you accept any input, you can
+ * simply return @c 0L, or not reimplement this method at all.
+ *
+ * @param mimeType the mime type
+ * @param group the group name of the validator item
+ * @param key the key name of the validator item
+ * @param parent the TQObject parent, can be @c 0
+ * @param name the name of the TQObject, can be @c 0
+ **/
+ virtual TQValidator* createValidator( const TQString& mimeType,
+ const TQString& group,
+ const TQString& key,
+ TQObject* parent,
+ const char* name) const
+ {
+ Q_UNUSED(mimeType); Q_UNUSED(group);Q_UNUSED(key);
+ Q_UNUSED(parent);Q_UNUSED(name);
+ return 0;
+ }
+
+protected:
+
+ /**
+ * Call this from within your constructor to tell the KFile framework what
+ * mimetypes your plugin supports.
+ *
+ * @param mimeType a string containing the mimetype, e.g. @c "text/html"
+ * @return a KFileMimeTypeInfo object, to be used with addGroupInfo()
+ **/
+ KFileMimeTypeInfo * addMimeTypeInfo( const TQString& mimeType );
+ // ### do we need this, if it only calls the provider?
+ // IMHO the Plugin shouldn't call its provider.
+ // DF: yes we need this. A plugin can create more than one mimetypeinfo.
+ // What sucks though, is to let plugins do that in their ctor.
+ // Would be much simpler to have a virtual init method for that,
+ // so that the provider can set up stuff with the plugin pointer first!
+
+ /**
+ * Creates a meta information group for KFileMimeTypeInfo object returned
+ * by addMimeTypeInfo().
+ *
+ * @param info the object returned by addMimeTypeInfo()
+ * @param key a unique string identifiing this group. For simplicity it is
+ * recommended to use the same string as for the translatedKey
+ * parameter
+ * @param translatedKey the translated version of the key string for
+ * displaying in user interfaces. Use i18n() to translate the string
+ * @return a GroupInfo object. Pass this object to addItemInfo to add meta
+ * information attributed to this group.
+ *
+ * @see setAttributes()
+ * @see addItemInfo()
+ **/
+ KFileMimeTypeInfo::GroupInfo* addGroupInfo(KFileMimeTypeInfo* info,
+ const TQString& key, const TQString& translatedKey) const;
+
+ /**
+ * Sets attributes of the GroupInfo object returned by addGroupInfo().
+ *
+ * @param gi the object returned by addGroupInfo()
+ * @param attr the attributes for this group; these are values of type
+ * KFileMimeTypeInfo::Attributes, or'ed together
+ **/
+ void setAttributes(KFileMimeTypeInfo::GroupInfo* gi, uint attr) const;
+
+ void addVariableInfo(KFileMimeTypeInfo::GroupInfo* gi, TQVariant::Type type,
+ uint attr) const;
+
+ /**
+ * Adds a meta information item to a GroupInfo object as returned by
+ * addGroupInfo().
+ *
+ * @param gi the GroupInfo object to add a new item to
+ * @param key a unique string to identify this item. For simplicity it is
+ * recommended to use the same string as for the translatedKey
+ * parameter
+ * @param translatedKey the translated version of the key string for
+ * displaying in user interfaces. Use i18n() to translate the string
+ * @param type the type of the meta information item, e.g. TQVariant::Int
+ * or TQVariant::String.
+ * @return an ItemInfo object. Pass this object to setAttributes()
+ **/
+ KFileMimeTypeInfo::ItemInfo* addItemInfo(KFileMimeTypeInfo::GroupInfo* gi,
+ const TQString& key,
+ const TQString& translatedKey,
+ TQVariant::Type type);
+
+ /**
+ * Sets some attributes for a meta information item. The attributes
+ * describe if the item is mutable, how it should be computed for a list of
+ * files, and how it should be displayed
+ *
+ * @param item the ItemInfo object as returned by addItemInfo()
+ * @param attr the attributes for this item; these are values of type
+ * KFileMimeTypeInfo::Attributes, or'ed together
+ **/
+ void setAttributes(KFileMimeTypeInfo::ItemInfo* item, uint attr);
+
+ /**
+ * Defines the meaning of the meta information item. Some applications make
+ * use of this information, so be sure to check KFileMimeTypeInfo::Hint to
+ * see if an item's meaning is in the list.
+ *
+ * @param item the ItemInfo object as returned by addItemInfo()
+ * @param hint the item's meaning. See KFileMimeTypeInfo::Hint for a list
+ * of available meanings
+ **/
+ void setHint(KFileMimeTypeInfo::ItemInfo* item, uint hint);
+
+ /**
+ * Sets the unit used in the meta information item. This unit is used to
+ * format the value and to make large values human-readable. For example,
+ * if the item's unit is KFileMimeTypeInfo::Seconds and the value is 276,
+ * it will be displayed as 4:36.
+ *
+ * @param item the ItemInfo object as returned by addItemInfo()
+ * @param unit the item's unit. See KFileMimeTypeInfo::Unit for a list of
+ * available units
+ **/
+ void setUnit(KFileMimeTypeInfo::ItemInfo* item, uint unit);
+
+ /**
+ * Sets a prefix string which is displayed before the item's value. Use
+ * this string if no predefined unit fits the item's unit. Be sure to
+ * translate the string with i18n()
+ *
+ * @param item the ItemInfo object as returned by addItemInfo()
+ * @param prefix the prefix string to display
+ **/
+ void setPrefix(KFileMimeTypeInfo::ItemInfo* item, const TQString& prefix);
+
+ /**
+ * Sets a suffix string which is displayed before the item's value. Use
+ * this string if no predefined unit fits the item's unit. Be sure to
+ * translate the string with i18n()
+ *
+ * @param item the ItemInfo object as returned by addItemInfo()
+ * @param suffix the suffix string to display
+ **/
+ void setSuffix(KFileMimeTypeInfo::ItemInfo* item, const TQString& suffix);
+
+ /**
+ * Call this method from within readInfo() to indicate that you wish to
+ * fill meta information items of the group identified by @p key with
+ * values.
+ *
+ * @param info the KFileMetaInfo object. Use the parameter of the
+ * readInfo() method
+ * @param key the key string to identify the group. Use the string that you
+ * defined in your class' constructor
+ * @return a KFileMetaInfoGroup object, to be used in appendItem()
+ **/
+ KFileMetaInfoGroup appendGroup(KFileMetaInfo& info, const TQString& key);
+
+ /**
+ * Call this method from within readInfo() to fill the meta information item
+ * identified by @p key with a @p value
+ *
+ * @param group the KFileMetaInfoGroup object, as returned by appendGroup()
+ * @param key the key string to identify the item.
+ * @param value the value of the meta information item
+ **/
+ void appendItem(KFileMetaInfoGroup& group, const TQString& key, TQVariant value);
+
+ TQStringList m_preferredKeys;
+ TQStringList m_preferredGroups;
+
+protected:
+ /**
+ * Helper method to allow binary compatible extensions when needing
+ * "new virtual methods"
+ *
+ * @param id the identifier of the new "virtual" method
+ * @param data any parameter data the new "virtual" method needs
+ */
+ virtual void virtual_hook( int id, void* data );
+private:
+ class KFilePluginPrivate;
+ KFilePluginPrivate *d;
+};
+
+///////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////
+
+
+/**
+ * @internal
+ * Synchronous access to metadata of a local file. Usually, you don't want
+ * to use this class for getting metainfo from a file. Use KFileMetaInfo
+ * directly. However, if you want to find out if a specific mimetype is
+ * supported and which groups and items are provided for it, you can ask
+ * the KFileMetainfoProvider for it.
+ **/
+class TDEIO_EXPORT KFileMetaInfoProvider: private QObject
+{
+ friend class KFilePlugin;
+
+ Q_OBJECT
+public:
+ virtual ~KFileMetaInfoProvider();
+
+ static KFileMetaInfoProvider * self();
+
+ /**
+ * @return a pointer to the plugin that belongs to the specified mimetype,
+ * which means also load the plugin if it's not in memory
+ */
+ KFilePlugin * plugin( const TQString& mimeType ); // KDE4: merge with method below
+
+ /**
+ * @return a pointer to the plugin that belongs to the specified mimetype,
+ * for the given protocol.
+ * This loads the plugin if it's not in memory yet.
+ */
+ KFilePlugin * plugin( const TQString& mimeType, const TQString& protocol );
+
+ const KFileMimeTypeInfo * mimeTypeInfo( const TQString& mimeType ); // KDE4: merge with below
+ const KFileMimeTypeInfo * mimeTypeInfo( const TQString& mimeType, const TQString& protocol );
+
+ TQStringList preferredKeys( const TQString& mimeType ) const;
+ TQStringList preferredGroups( const TQString& mimeType ) const;
+
+ /// @since 3.1
+ TQStringList supportedMimeTypes() const;
+
+protected: // ## should be private, right?
+ KFileMetaInfoProvider();
+
+private:
+
+ // Data structure:
+ // Mimetype or Protocol -> { Plugin and MimeTypeInfo }
+ // The {} struct is CachedPluginInfo
+ struct CachedPluginInfo
+ {
+ CachedPluginInfo() : plugin( 0 ), mimeTypeInfo( 0 ), ownsPlugin( false ) {}
+ CachedPluginInfo( KFilePlugin* p, KFileMimeTypeInfo* i, bool owns )
+ : plugin( p ), mimeTypeInfo( i ), ownsPlugin( owns ) {}
+ // auto-delete behavior
+ ~CachedPluginInfo() {
+ if ( ownsPlugin ) delete plugin;
+ delete mimeTypeInfo;
+ }
+
+ // If plugin and mimeTypeInfo are 0, means that no plugin is available.
+ KFilePlugin* plugin;
+ KFileMimeTypeInfo* mimeTypeInfo;
+ // The problem here is that plugin can be shared in multiple instances,
+ // so the memory management isn't easy. KDE4 solution: use KSharedPtr?
+ // For now we flag one copy of the KFilePlugin pointer as being "owned".
+ bool ownsPlugin;
+ };
+
+ // The key is either a mimetype or a protocol. Those things don't look the same
+ // so there's no need for two QDicts.
+ TQDict<CachedPluginInfo> m_plugins;
+
+ // This data is aggregated during the creation of a plugin,
+ // before being moved to the appropriate CachedPluginInfo(s)
+ // At any other time than during the loading of a plugin, this dict is EMPTY.
+ // Same key as in m_plugins: mimetype or protocol
+ TQDict<KFileMimeTypeInfo> m_pendingMimetypeInfos;
+
+private:
+ static KFileMetaInfoProvider * s_self;
+
+ KFilePlugin* loadPlugin( const TQString& mimeType, const TQString& protocol );
+ KFilePlugin* loadAndRegisterPlugin( const TQString& mimeType, const TQString& protocol );
+ KFileMimeTypeInfo * addMimeTypeInfo( const TQString& mimeType );
+
+ class KFileMetaInfoProviderPrivate;
+ KFileMetaInfoProviderPrivate *d;
+
+};
+
+TDEIO_EXPORT TQDataStream& operator <<(TQDataStream& s, const KFileMetaInfoItem& );
+TDEIO_EXPORT TQDataStream& operator >>(TQDataStream& s, KFileMetaInfoItem& );
+
+TDEIO_EXPORT TQDataStream& operator <<(TQDataStream& s, const KFileMetaInfoGroup& );
+TDEIO_EXPORT TQDataStream& operator >>(TQDataStream& s, KFileMetaInfoGroup& );
+
+TDEIO_EXPORT TQDataStream& operator <<(TQDataStream& s, const KFileMetaInfo& );
+TDEIO_EXPORT TQDataStream& operator >>(TQDataStream& s, KFileMetaInfo& );
+
+
+#endif // KILEMETAINFO_H
diff --git a/tdeio/tdeio/tdefileshare.cpp b/tdeio/tdeio/tdefileshare.cpp
new file mode 100644
index 000000000..fe6e0bd15
--- /dev/null
+++ b/tdeio/tdeio/tdefileshare.cpp
@@ -0,0 +1,346 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 David Faure <david@mandrakesoft.com>
+ Copyright (c) 2001 Laurent Montel <lmontel@mandrakesoft.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "tdefileshare.h"
+#include <tqdir.h>
+#include <tqfile.h>
+#include <tqregexp.h>
+#include <kprocess.h>
+#include <kprocio.h>
+#include <klocale.h>
+#include <kstaticdeleter.h>
+#include <kstandarddirs.h>
+#include <kdebug.h>
+#include <kdirwatch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <kdirnotify_stub.h>
+#include <ksimpleconfig.h>
+#include <kuser.h>
+
+KFileShare::Authorization KFileShare::s_authorization = NotInitialized;
+//TQStringList* KFileShare::s_shareList = 0L;
+//static KStaticDeleter<TQStringList> sdShareList;
+TQMap<TQString,TQString>* KFileShare::s_shareMap = 0L;
+static KStaticDeleter<TQMap<TQString,TQString> > sdShareMap;
+
+KFileShare::ShareMode KFileShare::s_shareMode;
+bool KFileShare::s_sambaEnabled;
+bool KFileShare::s_nfsEnabled;
+bool KFileShare::s_restricted;
+TQString KFileShare::s_fileShareGroup;
+bool KFileShare::s_sharingEnabled;
+
+
+#define FILESHARECONF "/etc/security/fileshare.conf"
+
+KFileSharePrivate::KFileSharePrivate()
+{
+ KDirWatch::self()->addFile(FILESHARECONF);
+ connect(KDirWatch::self(), TQT_SIGNAL(dirty (const TQString&)),this,
+ TQT_SLOT(slotFileChange(const TQString &)));
+ connect(KDirWatch::self(), TQT_SIGNAL(created(const TQString&)),this,
+ TQT_SLOT(slotFileChange(const TQString &)));
+ connect(KDirWatch::self(), TQT_SIGNAL(deleted(const TQString&)),this,
+ TQT_SLOT(slotFileChange(const TQString &)));
+}
+
+KFileSharePrivate::~KFileSharePrivate()
+{
+ KDirWatch::self()->removeFile(FILESHARECONF);
+}
+
+KFileSharePrivate *KFileSharePrivate::_self=0L;
+
+static KStaticDeleter<KFileSharePrivate> kstFileShare;
+
+KFileSharePrivate* KFileSharePrivate::self()
+{
+ if (!_self)
+ _self = kstFileShare.setObject(_self, new KFileSharePrivate());
+ return _self;
+}
+
+void KFileSharePrivate::slotFileChange(const TQString &file)
+{
+ if(file==FILESHARECONF) {
+ KFileShare::readConfig();
+ KFileShare::readShareList();
+ }
+}
+
+void KFileShare::readConfig() // static
+{
+ // Create KFileSharePrivate instance
+ KFileSharePrivate::self();
+ KSimpleConfig config(TQString::fromLatin1(FILESHARECONF),true);
+
+ s_sharingEnabled = config.readEntry("FILESHARING", "yes") == "yes";
+ s_restricted = config.readEntry("RESTRICT", "yes") == "yes";
+ s_fileShareGroup = config.readEntry("FILESHAREGROUP", "fileshare");
+
+
+ if (!s_sharingEnabled)
+ s_authorization = UserNotAllowed;
+ else
+ if (!s_restricted )
+ s_authorization = Authorized;
+ else {
+ // check if current user is in fileshare group
+ KUserGroup shareGroup(s_fileShareGroup);
+ if (shareGroup.users().findIndex(KUser()) > -1 )
+ s_authorization = Authorized;
+ else
+ s_authorization = UserNotAllowed;
+ }
+
+ if (config.readEntry("SHARINGMODE", "simple") == "simple")
+ s_shareMode = Simple;
+ else
+ s_shareMode = Advanced;
+
+
+ s_sambaEnabled = config.readEntry("SAMBA", "yes") == "yes";
+ s_nfsEnabled = config.readEntry("NFS", "yes") == "yes";
+}
+
+KFileShare::ShareMode KFileShare::shareMode() {
+ if ( s_authorization == NotInitialized )
+ readConfig();
+
+ return s_shareMode;
+}
+
+bool KFileShare::sharingEnabled() {
+ if ( s_authorization == NotInitialized )
+ readConfig();
+
+ return s_sharingEnabled;
+}
+
+bool KFileShare::isRestricted() {
+ if ( s_authorization == NotInitialized )
+ readConfig();
+
+ return s_restricted;
+}
+
+TQString KFileShare::fileShareGroup() {
+ if ( s_authorization == NotInitialized )
+ readConfig();
+
+ return s_fileShareGroup;
+}
+
+
+bool KFileShare::sambaEnabled() {
+ if ( s_authorization == NotInitialized )
+ readConfig();
+
+ return s_sambaEnabled;
+}
+
+bool KFileShare::nfsEnabled() {
+ if ( s_authorization == NotInitialized )
+ readConfig();
+
+ return s_nfsEnabled;
+}
+
+
+void KFileShare::readShareList()
+{
+ KFileSharePrivate::self();
+ if ( !s_shareMap )
+ sdShareMap.setObject( s_shareMap, new TQMap<TQString,TQString> );
+ else
+ s_shareMap->clear();
+
+ // /usr/sbin on Mandrake, $PATH allows flexibility for other distributions
+ TQString exe = findExe( "filesharelist" );
+ if (exe.isEmpty()) {
+ s_authorization = ErrorNotFound;
+ return;
+ }
+ KProcIO proc;
+ proc << exe;
+ if ( !proc.start( TDEProcess::Block ) ) {
+ kdError() << "Can't run " << exe << endl;
+ s_authorization = ErrorNotFound;
+ return;
+ }
+
+ // Reading code shamelessly stolen from khostname.cpp ;)
+ TQString line;
+ TQString options;
+ TQString path;
+ int length;
+ TQRegExp rx_line("([^\\s]+)\\s+(.*)");
+ do {
+ length = proc.readln(line, true);
+ if ( length > 0 )
+ {
+ if ( line[length-1] != '/' )
+ line += '/';
+ if( rx_line.search( line ) != -1 ) {
+ options = rx_line.cap(1);
+ path = rx_line.cap(2);
+ (*s_shareMap)[path] = options;
+ }
+ kdDebug(7000) << "Shared dir:" << line << endl;
+ }
+ } while (length > -1);
+}
+
+
+int KFileShare::isDirectoryShared( const TQString& _path )
+{
+ int ret(0);
+
+ if ( ! s_shareMap )
+ readShareList();
+
+ TQString path( _path );
+ if ( path[path.length()-1] != '/' )
+ path += '/';
+ //return s_shareList && s_shareList->contains( path );
+ if( (*s_shareMap).contains(path) && !((*s_shareMap)[path].isEmpty()) ) {
+ ret+=1;
+ if( (*s_shareMap)[path].find("readwrite") != -1 )
+ ret+=2;
+ }
+
+ return ret;
+}
+
+KFileShare::Authorization KFileShare::authorization()
+{
+ // The app should do this on startup, but if it doesn't, let's do here.
+ if ( s_authorization == NotInitialized )
+ readConfig();
+ return s_authorization;
+}
+
+TQString KFileShare::findExe( const char* exeName )
+{
+ // /usr/sbin on Mandrake, $PATH allows flexibility for other distributions
+ TQString path = TQString::fromLocal8Bit(getenv("PATH")) + TQString::fromLatin1(":/usr/sbin");
+ TQString exe = KStandardDirs::findExe( exeName, path );
+ if (exe.isEmpty())
+ kdError() << exeName << " not found in " << path << endl;
+ return exe;
+}
+
+bool KFileShare::setShared( const TQString& path, bool shared )
+{
+ return SuSEsetShared( path, shared, false );
+}
+
+bool KFileShare::SuSEsetShared( const TQString& path, bool shared, bool rw )
+{
+ if (! KFileShare::sharingEnabled() ||
+ KFileShare::shareMode() == Advanced)
+ return false;
+
+ TQString exe = KFileShare::findExe( "fileshareset" );
+ if (exe.isEmpty())
+ return false;
+
+ // we want to share, so we kick it first - just to be sure
+ TDEProcess proc;
+ proc << exe;
+ proc << "--remove";
+ proc << path;
+ proc.start( TDEProcess::Block );
+ proc.clearArguments();
+
+ proc << exe;
+ if( rw )
+ proc << "--rw";
+ if ( shared )
+ proc << "--add";
+ else
+ proc << "--remove";
+ proc << path;
+ proc.start( TDEProcess::Block ); // should be ok, the perl script terminates fast
+ bool ok = proc.normalExit() && (proc.exitStatus() == 0);
+ kdDebug(7000) << "KFileSharePropsPlugin::setShared normalExit="
+ << proc.normalExit() << endl;
+ kdDebug(7000) << "KFileSharePropsPlugin::setShared exitStatus="
+ << proc.exitStatus() << endl;
+ if ( proc.normalExit() ) {
+ switch( proc.exitStatus() ) {
+ case 1:
+ // User is not authorized
+ break;
+ case 3:
+ // Called script with --add, but path was already shared before.
+ // Result is nevertheless what the client wanted, so
+ // this is alright.
+ ok = true;
+ break;
+ case 4:
+ // Invalid mount point
+ break;
+ case 5:
+ // Called script with --remove, but path was not shared before.
+ // Result is nevertheless what the client wanted, so
+ // this is alright.
+ ok = true;
+ break;
+ case 6:
+ // There is no export method
+ break;
+ case 7:
+ // file sharing is disabled
+ break;
+ case 8:
+ // advanced sharing is enabled
+ break;
+ case 255:
+ // Abitrary error
+ break;
+ }
+ }
+
+ return ok;
+}
+
+bool KFileShare::sambaActive()
+{
+ // rcsmb is not executable by users, try ourselves
+ int status = system( "/sbin/checkproc -p /var/run/samba/smbd.pid /usr/sbin/smbd" );
+ return status != -1 && WIFEXITED( status ) && WEXITSTATUS( status ) == 0;
+}
+
+bool KFileShare::nfsActive()
+{
+ // rcnfsserver is not executable by users, try ourselves
+ int status = system( "/sbin/checkproc /usr/sbin/rpc.mountd" );
+ if( status != -1 && WIFEXITED( status ) && WEXITSTATUS( status ) == 0 )
+ {
+ status = system( "/sbin/checkproc -n nfsd" );
+ if( status != -1 && WIFEXITED( status ) && WEXITSTATUS( status ) == 0 )
+ return true;
+ }
+ return false;
+}
+
+#include "tdefileshare.moc"
diff --git a/tdeio/tdeio/tdefileshare.h b/tdeio/tdeio/tdefileshare.h
new file mode 100644
index 000000000..e18749972
--- /dev/null
+++ b/tdeio/tdeio/tdefileshare.h
@@ -0,0 +1,165 @@
+/* This file is part of the KDE project
+ Copyright (c) 2001 David Faure <david@mandrakesoft.com>
+ Copyright (c) 2001 Laurent Montel <lmontel@mandrakesoft.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License version 2 as published by the Free Software Foundation.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef tdefileshare_h
+#define tdefileshare_h
+#include <tqobject.h>
+
+#include <tdelibs_export.h>
+
+class KDirWatch;
+
+/**
+ * @internal
+ * Do not use, ever.
+ */
+class KFileSharePrivate : public TQObject
+{
+ Q_OBJECT
+
+public:
+ KFileSharePrivate();
+ ~KFileSharePrivate();
+ KDirWatch* m_watchFile;
+ static KFileSharePrivate *self();
+ static KFileSharePrivate *_self;
+protected slots: // this is why this class needs to be in the .h
+ void slotFileChange(const TQString &);
+};
+
+/**
+ * Common functionality for the file sharing
+ * (communication with the backend)
+ * @since 3.1
+ */
+class TDEIO_EXPORT KFileShare
+{
+
+
+public:
+ /**
+ * Reads the file share configuration file
+ */
+ static void readConfig();
+
+ /**
+ * Reads the list of shared folders
+ */
+ static void readShareList();
+
+
+ /**
+ * Call this to know if a directory is currently shared
+ */
+ static int isDirectoryShared( const TQString& path );
+
+ enum Authorization { NotInitialized, ErrorNotFound, Authorized, UserNotAllowed };
+ /**
+ * Call this to know if the current user is authorized to share directories
+ */
+ static Authorization authorization();
+
+ static TQString findExe( const char* exeName );
+
+ /**
+ * Uses a suid perl script to share the given path
+ * with NFS and Samba
+ * @param path the path to share
+ * @param shared whether the path should be shared or not
+ * @returns whether the perl script was successful
+ */
+ static bool setShared( const TQString& path, bool shared );
+
+ /*
+ * SuSE only enhancement for now
+ */
+ static bool SuSEsetShared( const TQString& path, bool shared, bool ro );
+
+ /**
+ * The used share mode.
+ * Simple means that the simple sharing dialog is used and
+ * users can share only folders from there HOME folder.
+ * Advanced means that the advanced sharing dialog is used and
+ * users can share any folder.
+ */
+ enum ShareMode { Simple, Advanced };
+
+ /**
+ * Returns whether sharing is enabled
+ * If this is false, file sharing is disabled and
+ * nobody can share files.
+ */
+ static bool sharingEnabled();
+
+ /**
+ * Returns whether file sharing is restricted.
+ * If it is not restricted every user can shar files.
+ * If it is restricted only users in the configured
+ * file share group can share files.
+ */
+ static bool isRestricted();
+
+ /**
+ * Returns the group that is used for file sharing.
+ * That is, all users in that group are allowed to
+ * share files if file sharing is restricted.
+ */
+ static TQString fileShareGroup();
+
+ /**
+ * Returns the configured share mode
+ */
+ static ShareMode shareMode();
+
+ /**
+ * Returns whether Samba is enabled
+ */
+ static bool sambaEnabled();
+
+ /**
+ * Returns whether NFS is enabled
+ */
+ static bool nfsEnabled();
+
+ /**
+ * Returns whether Samba is active (service is running)
+ * @internal
+ */
+ static bool sambaActive();
+
+ /**
+ * Returns whether NFS is active (service is running)
+ * @internal
+ */
+ static bool nfsActive();
+
+private:
+ static Authorization s_authorization;
+// static TQStringList* s_shareList;
+ static TQMap<TQString,TQString>* s_shareMap;
+ static ShareMode s_shareMode;
+ static bool s_sambaEnabled;
+ static bool s_nfsEnabled;
+ static bool s_restricted;
+ static TQString s_fileShareGroup;
+ static bool s_sharingEnabled;
+
+};
+
+#endif
diff --git a/tdeio/tdeio/tdelficon.cpp b/tdeio/tdeio/tdelficon.cpp
new file mode 100644
index 000000000..49ceffd24
--- /dev/null
+++ b/tdeio/tdeio/tdelficon.cpp
@@ -0,0 +1,89 @@
+#include "tdelficon.h"
+
+#include <cstring>
+
+/*
+ * Obtain an existing icon resource list
+ */
+int get_iconlist(libr_file *file_handle, iconlist *icons)
+{
+ if(icons == NULL)
+ {
+ /* Need to be able to return SOMETHING */
+ return false;
+ }
+ /* Obtain the icon resource list */
+ icons->buffer = libr_malloc(file_handle, ICON_SECTION, &(icons->size));
+ if(icons->buffer == NULL)
+ return false;
+ return true;
+}
+
+/*
+ * Get the next entry in an icon resource list
+ */
+iconentry *get_nexticon(iconlist *icons, iconentry *last_entry)
+{
+ size_t i;
+
+ /* The icon list is needed both for the data buffer and for a call-specific iconentry instance */
+ if(icons == NULL)
+ return NULL;
+ /* If this is the first call (last_entry == NULL) then return the first entry */
+ if(last_entry == NULL)
+ icons->entry.offset = sizeof(uint32_t)+sizeof(UUID);
+ else
+ icons->entry.offset += icons->entry.entry_size;
+ /* Check to see if we've run out of entries */
+ if(icons->entry.offset >= icons->size)
+ return NULL;
+ i = icons->entry.offset;
+ memcpy(&(icons->entry.entry_size), &(icons->buffer[i]), sizeof(uint32_t));
+ i += sizeof(uint32_t);
+ icons->entry.type = (libr_icontype_t)icons->buffer[i];
+ i += sizeof(unsigned char);
+ switch(icons->entry.type)
+ {
+ case LIBR_SVG:
+ icons->entry.icon_size = 0;
+ icons->entry.name = &(icons->buffer[i]);
+ break;
+ case LIBR_PNG:
+ memcpy(&(icons->entry.icon_size), &(icons->buffer[i]), sizeof(uint32_t));
+ i += sizeof(uint32_t);
+ icons->entry.name = &(icons->buffer[i]);
+ break;
+ default:
+ /* Invalid entry type */
+ return NULL;
+ }
+ return &(icons->entry);
+}
+
+TQString elf_get_resource(libr_file *handle, char *section_name)
+{
+ size_t buffer_size = 0;
+ char *buffer = NULL;
+ TQString result;
+
+ /* Get the resource from the ELF binary */
+ if(!libr_size(handle, section_name, &buffer_size))
+ {
+// kdWarning() << "failed to obtain ELF resource size: " << libr_errmsg() << endl;
+ return result;
+ }
+ /* Get the resource from the ELF file */
+ buffer = (char *) malloc(buffer_size+1);
+ buffer[buffer_size] = 0;
+ if(!libr_read(handle, section_name, buffer))
+ {
+// kdWarning() << "failed to obtain ELF resource: " << libr_errmsg() << endl;
+ goto fail;
+ }
+ result = buffer;
+
+fail:
+ free(buffer);
+
+ return result;
+}
diff --git a/tdeio/tdeio/tdelficon.h b/tdeio/tdeio/tdelficon.h
new file mode 100644
index 000000000..7a962bdaa
--- /dev/null
+++ b/tdeio/tdeio/tdelficon.h
@@ -0,0 +1,54 @@
+#include <alloca.h>
+#include <stdint.h>
+#include <cstdlib>
+
+#include <tqdict.h>
+#include <tqvalidator.h>
+#include <tqcstring.h>
+#include <tqfile.h>
+#include <tqdatetime.h>
+
+extern "C" {
+ #include <libr-icons.h>
+
+ // BEGIN HACK
+ // libr does not export these structures and defines,
+ // but we need access to them to make the UI behave sanely
+ // Keep them in sync with libr and all should be OK
+
+ // Valid for libr version 0.6.0
+ // See libr detection code in ConfigureChecks.cmake
+
+ typedef uint32_t ID8;
+ typedef uint16_t ID4;
+ typedef struct {uint64_t p:48;} __attribute__((__packed__)) ID12;
+
+ typedef struct {
+ ID8 g1;
+ ID4 g2;
+ ID4 g3;
+ ID4 g4;
+ ID12 g5;
+ } __attribute__((__packed__)) UUID;
+
+ typedef struct {
+ char *name;
+ size_t offset;
+ size_t entry_size;
+ libr_icontype_t type;
+ unsigned int icon_size;
+ } iconentry;
+
+ typedef struct{
+ size_t size;
+ char *buffer;
+ iconentry entry;
+ } iconlist;
+
+ #define ICON_SECTION ".icon"
+ // END HACK
+}
+
+int get_iconlist(libr_file *file_handle, iconlist *icons);
+iconentry *get_nexticon(iconlist *icons, iconentry *last_entry);
+TQString elf_get_resource(libr_file *handle, char *section_name); \ No newline at end of file
diff --git a/tdeio/tdeio/thumbcreator.h b/tdeio/tdeio/thumbcreator.h
new file mode 100644
index 000000000..756dd0cf9
--- /dev/null
+++ b/tdeio/tdeio/thumbcreator.h
@@ -0,0 +1,124 @@
+/* This file is part of the KDE libraries
+ Copyright (C) 2000 Malte Starostik <malte@kde.org>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef _THUMBCREATOR_H_
+#define _THUMBCREATOR_H_
+
+#include <tqstring.h>
+
+class TQString;
+class TQImage;
+/**
+ * This is the baseclass for "thumbnail-plugins" in KDE. Using the class
+ * TDEIO::PreviewJob allows you to generate small images (thumbnails)
+ * for any kind of file, where a "ThumbCreator" is available. Have a look
+ * at tdebase/tdeioslave/thumbnail/ for existing ThumbCreators.
+ *
+ * What you need to do to create and register a ThumbCreator:
+ * @li Inherit from this class and reimplement the create() method to
+ * generate a thumbnail for the given file-path.
+ * @li Provide a factory method in your implementation file to instantiate
+ * your plugin, e.g.:
+ * \code
+ * extern "C"
+ * {
+ * ThumbCreator *new_creator()
+ * {
+ * return new YourThumbCreator();
+ * }
+ * };
+ * \endcode
+ *
+ * Compile your ThumbCreator as a module. The contents of Makefile.am
+ * need to look like this:
+ * \code
+ * INCLUDES = $(all_includes)
+ * kde_module_LTLIBRARIES = yourthumbcreator.la
+ * yourthumbcreator_la_SOURCES = yourthumbcreator.cpp
+ * yourthumbcreator_la_LIBADD = $(LIB_KIO)
+ * yourthumbcreator_la_LDFLAGS = $(all_libraries) -module $(KDE_PLUGIN)
+ * kde_services_DATA = yourthumbcreator.desktop
+ * \endcode
+ *
+ * @li Create a file yourthumbcreator.desktop with the following contents:
+ * \code
+ * [Desktop Entry]
+ * Encoding=UTF-8
+ * Type=Service
+ * Name=Name of the type of files your ThumbCreator supports
+ * ServiceTypes=ThumbCreator
+ * MimeTypes=application/x-somemimetype
+ * CacheThumbnail=true
+ * X-TDE-Library=yourthumbcreator
+ * \endcode
+ *
+ * You can supply a comma-separated list of mimetypes to the MimeTypes entry,
+ * naming all mimetypes your ThumbCreator supports. You can also use simple
+ * wildcards, like (where you see [slash], put a /)
+ * \code
+ * text[slash]* or image[slash]*.
+ * \endcode
+ *
+ * If your plugin is rather inexpensive (e.g. like the text preview ThumbCreator),
+ * you can set CacheThumbnail=false to prevent your thumbnails from being cached
+ * on disk.
+ *
+ * @short Baseclass for thumbnail-generating plugins.
+ */
+class ThumbCreator
+{
+public:
+ /**
+ * The flags of this plugin.
+ * @see flags()
+ */
+ enum Flags { None = 0, DrawFrame = 1, BlendIcon = 2 };
+ virtual ~ThumbCreator() {}
+
+ /**
+ * Creates a thumbnail
+ * Note that the width and height parameters should not be used
+ * for scaling. Only plugins that create an image "from scratch",
+ * like the TextCreator should directly use the specified size.
+ * If the resulting preview is larger than width x height, it will be
+ * scaled down.
+ *
+ * @param path the (always local) file path to create a preview for
+ * @param width maximum width for the preview
+ * @param height maximum height for the preview
+ * @param img this image will contain the preview on success
+ *
+ * @return true if preview generation succeeded
+ */
+ virtual bool create(const TQString &path, int width, int height, TQImage &img) = 0;
+
+ /**
+ * The flags of this plugin:
+ * @li None nothing special
+ * @li DrawFrame a frame should be painted around the preview
+ * @li BlendIcon the mimetype icon should be blended over the preview
+ *
+ * @return flags for this plugin
+ */
+ virtual Flags flags() const { return None; }
+};
+
+typedef ThumbCreator *(*newCreator)();
+
+#endif
diff --git a/tdeio/tdeio/yacc.c b/tdeio/tdeio/yacc.c
new file mode 100644
index 000000000..a6fa63428
--- /dev/null
+++ b/tdeio/tdeio/yacc.c
@@ -0,0 +1,1522 @@
+/* A Bison parser, made by GNU Bison 2.0. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Written by Richard Stallman by simplifying the original so called
+ ``semantic'' parser. */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+ infringing on user name space. This should be done even for local
+ variables, as they might otherwise be expanded by user macros.
+ There are some unavoidable exceptions within include files to
+ define necessary library symbols; they are noted "INFRINGES ON
+ USER NAME SPACE" below. */
+
+/* Identify Bison output. */
+#define YYBISON 1
+
+/* Skeleton name. */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers. */
+#define YYPURE 0
+
+/* Using locations. */
+#define YYLSP_NEEDED 0
+
+/* Substitute the variable and function names. */
+#define yyparse kiotraderparse
+#define yylex kiotraderlex
+#define yyerror kiotradererror
+#define yylval kiotraderlval
+#define yychar kiotraderchar
+#define yydebug kiotraderdebug
+#define yynerrs kiotradernerrs
+
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ NOT = 258,
+ EQ = 259,
+ NEQ = 260,
+ LEQ = 261,
+ GEQ = 262,
+ LE = 263,
+ GR = 264,
+ OR = 265,
+ AND = 266,
+ TOKEN_IN = 267,
+ EXIST = 268,
+ MAX = 269,
+ MIN = 270,
+ VAL_BOOL = 271,
+ VAL_STRING = 272,
+ VAL_ID = 273,
+ VAL_NUM = 274,
+ VAL_FLOAT = 275
+ };
+#endif
+#define NOT 258
+#define EQ 259
+#define NEQ 260
+#define LEQ 261
+#define GEQ 262
+#define LE 263
+#define GR 264
+#define OR 265
+#define AND 266
+#define TOKEN_IN 267
+#define EXIST 268
+#define MAX 269
+#define MIN 270
+#define VAL_BOOL 271
+#define VAL_STRING 272
+#define VAL_ID 273
+#define VAL_NUM 274
+#define VAL_FLOAT 275
+
+
+
+
+/* Copy the first part of user declarations. */
+#line 1 "yacc.y"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "ktraderparse.h"
+
+void yyerror(const char *s);
+int yylex();
+void KTraderParse_initFlex( const char *s );
+
+
+
+/* Enabling traces. */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+
+/* Enabling verbose error messages. */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 13 "yacc.y"
+typedef union YYSTYPE {
+ char valb;
+ int vali;
+ double vald;
+ char *name;
+ void *ptr;
+} YYSTYPE;
+/* Line 190 of yacc.c. */
+#line 143 "yacc.tab.c"
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+
+
+/* Copy the second part of user declarations. */
+
+
+/* Line 213 of yacc.c. */
+#line 155 "yacc.tab.c"
+
+#if ! defined (yyoverflow) || YYERROR_VERBOSE
+
+# ifndef YYFREE
+# define YYFREE free
+# endif
+# ifndef YYMALLOC
+# define YYMALLOC malloc
+# endif
+
+/* The parser invokes alloca or malloc; define the necessary symbols. */
+
+# ifdef YYSTACK_USE_ALLOCA
+# if YYSTACK_USE_ALLOCA
+# ifdef __GNUC__
+# define YYSTACK_ALLOC __builtin_alloca
+# else
+# define YYSTACK_ALLOC alloca
+# endif
+# endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+ /* Pacify GCC's `empty if-body' warning. */
+# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+# else
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# endif
+# define YYSTACK_ALLOC YYMALLOC
+# define YYSTACK_FREE YYFREE
+# endif
+#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */
+
+
+#if (! defined (yyoverflow) \
+ && (! defined (__cplusplus) \
+ || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member. */
+union yyalloc
+{
+ short int yyss;
+ YYSTYPE yyvs;
+ };
+
+/* The size of the maximum gap between one aligned stack and the next. */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+ N elements. */
+# define YYSTACK_BYTES(N) \
+ ((N) * (sizeof (short int) + sizeof (YYSTYPE)) \
+ + YYSTACK_GAP_MAXIMUM)
+
+/* Copy COUNT objects from FROM to TO. The source and destination do
+ not overlap. */
+# ifndef YYCOPY
+# if defined (__GNUC__) && 1 < __GNUC__
+# define YYCOPY(To, From, Count) \
+ __builtin_memcpy (To, From, (Count) * sizeof (*(From)))
+# else
+# define YYCOPY(To, From, Count) \
+ do \
+ { \
+ register YYSIZE_T yyi; \
+ for (yyi = 0; yyi < (Count); yyi++) \
+ (To)[yyi] = (From)[yyi]; \
+ } \
+ while (0)
+# endif
+# endif
+
+/* Relocate STACK from its old location to the new one. The
+ local variables YYSIZE and YYSTACKSIZE give the old and new number of
+ elements in the stack, and YYPTR gives the new location of the
+ stack. Advance YYPTR to a properly aligned location for the next
+ stack. */
+# define YYSTACK_RELOCATE(Stack) \
+ do \
+ { \
+ YYSIZE_T yynewbytes; \
+ YYCOPY (&yyptr->Stack, Stack, yysize); \
+ Stack = &yyptr->Stack; \
+ yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+ yyptr += yynewbytes / sizeof (*yyptr); \
+ } \
+ while (0)
+
+#endif
+
+#if defined (__STDC__) || defined (__cplusplus)
+ typedef signed char yysigned_char;
+#else
+ typedef short int yysigned_char;
+#endif
+
+/* YYFINAL -- State number of the termination state. */
+#define YYFINAL 27
+/* YYLAST -- Last index in YYTABLE. */
+#define YYLAST 55
+
+/* YYNTOKENS -- Number of terminals. */
+#define YYNTOKENS 28
+/* YYNNTS -- Number of nonterminals. */
+#define YYNNTS 12
+/* YYNRULES -- Number of rules. */
+#define YYNRULES 36
+/* YYNRULES -- Number of states. */
+#define YYNSTATES 57
+
+/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */
+#define YYUNDEFTOK 2
+#define YYMAXUTOK 275
+
+#define YYTRANSLATE(YYX) \
+ ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */
+static const unsigned char yytranslate[] =
+{
+ 0, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 26, 27, 24, 22, 2, 23, 2, 25, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 21, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 1, 2, 3, 4,
+ 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20
+};
+
+#if YYDEBUG
+/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in
+ YYRHS. */
+static const unsigned char yyprhs[] =
+{
+ 0, 0, 3, 4, 6, 8, 12, 14, 18, 20,
+ 24, 28, 32, 36, 40, 44, 46, 50, 52, 56,
+ 58, 62, 66, 68, 72, 76, 78, 81, 83, 87,
+ 90, 92, 94, 96, 98, 100, 103
+};
+
+/* YYRHS -- A `-1'-separated list of the rules' RHS. */
+static const yysigned_char yyrhs[] =
+{
+ 29, 0, -1, -1, 30, -1, 31, -1, 32, 10,
+ 31, -1, 32, -1, 33, 11, 32, -1, 33, -1,
+ 34, 4, 34, -1, 34, 5, 34, -1, 34, 7,
+ 34, -1, 34, 6, 34, -1, 34, 8, 34, -1,
+ 34, 9, 34, -1, 34, -1, 35, 12, 18, -1,
+ 35, -1, 36, 21, 36, -1, 36, -1, 36, 22,
+ 37, -1, 36, 23, 37, -1, 37, -1, 37, 24,
+ 38, -1, 37, 25, 38, -1, 38, -1, 3, 39,
+ -1, 39, -1, 26, 31, 27, -1, 13, 18, -1,
+ 18, -1, 19, -1, 20, -1, 17, -1, 16, -1,
+ 14, 18, -1, 15, 18, -1
+};
+
+/* YYRLINE[YYN] -- source line where rule number YYN was defined. */
+static const unsigned char yyrline[] =
+{
+ 0, 56, 56, 57, 60, 63, 64, 67, 68, 71,
+ 72, 73, 74, 75, 76, 77, 80, 81, 84, 85,
+ 88, 89, 90, 93, 94, 95, 98, 99, 102, 103,
+ 104, 105, 106, 107, 108, 109, 110
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE
+/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+ First, the terminals, then, starting at YYNTOKENS, nonterminals. */
+static const char *const yytname[] =
+{
+ "$end", "error", "$undefined", "NOT", "EQ", "NEQ", "LEQ", "GEQ", "LE",
+ "GR", "OR", "AND", "TOKEN_IN", "EXIST", "MAX", "MIN", "VAL_BOOL",
+ "VAL_STRING", "VAL_ID", "VAL_NUM", "VAL_FLOAT", "'~'", "'+'", "'-'",
+ "'*'", "'/'", "'('", "')'", "$accept", "constraint", "bool", "bool_or",
+ "bool_and", "bool_compare", "expr_in", "expr_twiddle", "expr", "term",
+ "factor_non", "factor", 0
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to
+ token YYLEX-NUM. */
+static const unsigned short int yytoknum[] =
+{
+ 0, 256, 257, 258, 259, 260, 261, 262, 263, 264,
+ 265, 266, 267, 268, 269, 270, 271, 272, 273, 274,
+ 275, 126, 43, 45, 42, 47, 40, 41
+};
+# endif
+
+/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */
+static const unsigned char yyr1[] =
+{
+ 0, 28, 29, 29, 30, 31, 31, 32, 32, 33,
+ 33, 33, 33, 33, 33, 33, 34, 34, 35, 35,
+ 36, 36, 36, 37, 37, 37, 38, 38, 39, 39,
+ 39, 39, 39, 39, 39, 39, 39
+};
+
+/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */
+static const unsigned char yyr2[] =
+{
+ 0, 2, 0, 1, 1, 3, 1, 3, 1, 3,
+ 3, 3, 3, 3, 3, 1, 3, 1, 3, 1,
+ 3, 3, 1, 3, 3, 1, 2, 1, 3, 2,
+ 1, 1, 1, 1, 1, 2, 2
+};
+
+/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state
+ STATE-NUM when YYTABLE doesn't specify something else to do. Zero
+ means the default is an error. */
+static const unsigned char yydefact[] =
+{
+ 2, 0, 0, 0, 0, 34, 33, 30, 31, 32,
+ 0, 0, 3, 4, 6, 8, 15, 17, 19, 22,
+ 25, 27, 26, 29, 35, 36, 0, 1, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 28, 5, 7, 9, 10, 12, 11, 13,
+ 14, 16, 18, 20, 21, 23, 24
+};
+
+/* YYDEFGOTO[NTERM-NUM]. */
+static const yysigned_char yydefgoto[] =
+{
+ -1, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+ 20, 21
+};
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+ STATE-NUM. */
+#define YYPACT_NINF -17
+static const yysigned_char yypact[] =
+{
+ -3, 11, 0, 18, 28, -17, -17, -17, -17, -17,
+ -3, 47, -17, -17, 38, 39, -2, 37, -1, -16,
+ -17, -17, -17, -17, -17, -17, 24, -17, -3, -3,
+ -3, -3, -3, -3, -3, -3, 34, -3, -3, -3,
+ -3, -3, -17, -17, -17, -17, -17, -17, -17, -17,
+ -17, -17, 10, -16, -16, -17, -17
+};
+
+/* YYPGOTO[NTERM-NUM]. */
+static const yysigned_char yypgoto[] =
+{
+ -17, -17, -17, -9, 25, -17, 8, -17, 16, -4,
+ 4, 54
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If
+ positive, shift that token. If negative, reduce the rule which
+ number is the opposite. If zero, do what YYDEFACT says.
+ If YYTABLE_NINF, syntax error. */
+#define YYTABLE_NINF -1
+static const unsigned char yytable[] =
+{
+ 1, 26, 30, 31, 32, 33, 34, 35, 40, 41,
+ 2, 3, 4, 5, 6, 7, 8, 9, 23, 43,
+ 37, 38, 39, 10, 2, 3, 4, 5, 6, 7,
+ 8, 9, 38, 39, 53, 54, 24, 10, 45, 46,
+ 47, 48, 49, 50, 55, 56, 25, 27, 28, 36,
+ 29, 42, 51, 52, 44, 22
+};
+
+static const unsigned char yycheck[] =
+{
+ 3, 10, 4, 5, 6, 7, 8, 9, 24, 25,
+ 13, 14, 15, 16, 17, 18, 19, 20, 18, 28,
+ 21, 22, 23, 26, 13, 14, 15, 16, 17, 18,
+ 19, 20, 22, 23, 38, 39, 18, 26, 30, 31,
+ 32, 33, 34, 35, 40, 41, 18, 0, 10, 12,
+ 11, 27, 18, 37, 29, 1
+};
+
+/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+ symbol of state STATE-NUM. */
+static const unsigned char yystos[] =
+{
+ 0, 3, 13, 14, 15, 16, 17, 18, 19, 20,
+ 26, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ 38, 39, 39, 18, 18, 18, 31, 0, 10, 11,
+ 4, 5, 6, 7, 8, 9, 12, 21, 22, 23,
+ 24, 25, 27, 31, 32, 34, 34, 34, 34, 34,
+ 34, 18, 36, 37, 37, 38, 38
+};
+
+#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__)
+# define YYSIZE_T __SIZE_TYPE__
+#endif
+#if ! defined (YYSIZE_T) && defined (size_t)
+# define YYSIZE_T size_t
+#endif
+#if ! defined (YYSIZE_T)
+# if defined (__STDC__) || defined (__cplusplus)
+# include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+# define YYSIZE_T size_t
+# endif
+#endif
+#if ! defined (YYSIZE_T)
+# define YYSIZE_T unsigned int
+#endif
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY (-2)
+#define YYEOF 0
+
+#define YYACCEPT goto yyacceptlab
+#define YYABORT goto yyabortlab
+#define YYERROR goto yyerrorlab
+
+
+/* Like YYERROR except do call yyerror. This remains here temporarily
+ to ease the transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+
+#define YYFAIL goto yyerrlab
+
+#define YYRECOVERING() (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { \
+ yychar = (Token); \
+ yylval = (Value); \
+ yytoken = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { \
+ yyerror ("syntax error: cannot back up");\
+ YYERROR; \
+ } \
+while (0)
+
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+
+/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N].
+ If N is 0, then set CURRENT to the empty location which ends
+ the previous symbol: RHS[0] (always defined). */
+
+#define YYRHSLOC(Rhs, K) ((Rhs)[K])
+#ifndef YYLLOC_DEFAULT
+# define YYLLOC_DEFAULT(Current, Rhs, N) \
+ do \
+ if (N) \
+ { \
+ (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \
+ (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \
+ (Current).last_line = YYRHSLOC (Rhs, N).last_line; \
+ (Current).last_column = YYRHSLOC (Rhs, N).last_column; \
+ } \
+ else \
+ { \
+ (Current).first_line = (Current).last_line = \
+ YYRHSLOC (Rhs, 0).last_line; \
+ (Current).first_column = (Current).last_column = \
+ YYRHSLOC (Rhs, 0).last_column; \
+ } \
+ while (0)
+#endif
+
+
+/* YY_LOCATION_PRINT -- Print the location on the stream.
+ This macro was not mandated originally: define only if we know
+ we won't break user code: when these are the locations we know. */
+
+#ifndef YY_LOCATION_PRINT
+# if YYLTYPE_IS_TRIVIAL
+# define YY_LOCATION_PRINT(File, Loc) \
+ fprintf (File, "%d.%d-%d.%d", \
+ (Loc).first_line, (Loc).first_column, \
+ (Loc).last_line, (Loc).last_column)
+# else
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+# endif
+#endif
+
+
+/* YYLEX -- calling `yylex' with the right arguments. */
+
+#ifdef YYLEX_PARAM
+# define YYLEX yylex (YYLEX_PARAM)
+#else
+# define YYLEX yylex ()
+#endif
+
+/* Enable debugging if requested. */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+# include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+# define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args) \
+do { \
+ if (yydebug) \
+ YYFPRINTF Args; \
+} while (0)
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \
+do { \
+ if (yydebug) \
+ { \
+ YYFPRINTF (stderr, "%s ", Title); \
+ yysymprint (stderr, \
+ Type, Value); \
+ YYFPRINTF (stderr, "\n"); \
+ } \
+} while (0)
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included). |
+`------------------------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_stack_print (short int *bottom, short int *top)
+#else
+static void
+yy_stack_print (bottom, top)
+ short int *bottom;
+ short int *top;
+#endif
+{
+ YYFPRINTF (stderr, "Stack now");
+ for (/* Nothing. */; bottom <= top; ++bottom)
+ YYFPRINTF (stderr, " %d", *bottom);
+ YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top) \
+do { \
+ if (yydebug) \
+ yy_stack_print ((Bottom), (Top)); \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced. |
+`------------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yy_reduce_print (int yyrule)
+#else
+static void
+yy_reduce_print (yyrule)
+ int yyrule;
+#endif
+{
+ int yyi;
+ unsigned int yylno = yyrline[yyrule];
+ YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ",
+ yyrule - 1, yylno);
+ /* Print the symbols being reduced, and their result. */
+ for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++)
+ YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]);
+ YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]);
+}
+
+# define YY_REDUCE_PRINT(Rule) \
+do { \
+ if (yydebug) \
+ yy_reduce_print (Rule); \
+} while (0)
+
+/* Nonzero means print parse trace. It is left uninitialized so that
+ multiple parsers can coexist. */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks. */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+ if the built-in stack extension method is used).
+
+ Do not make this value too large; the results are undefined if
+ SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH)
+ evaluated with infinite-precision integer arithmetic. */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+# if defined (__GLIBC__) && defined (_STRING_H)
+# define yystrlen strlen
+# else
+/* Return the length of YYSTR. */
+static YYSIZE_T
+# if defined (__STDC__) || defined (__cplusplus)
+yystrlen (const char *yystr)
+# else
+yystrlen (yystr)
+ const char *yystr;
+# endif
+{
+ register const char *yys = yystr;
+
+ while (*yys++ != '\0')
+ continue;
+
+ return yys - yystr - 1;
+}
+# endif
+# endif
+
+# ifndef yystpcpy
+# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE)
+# define yystpcpy stpcpy
+# else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+ YYDEST. */
+static char *
+# if defined (__STDC__) || defined (__cplusplus)
+yystpcpy (char *yydest, const char *yysrc)
+# else
+yystpcpy (yydest, yysrc)
+ char *yydest;
+ const char *yysrc;
+# endif
+{
+ register char *yyd = yydest;
+ register const char *yys = yysrc;
+
+ while ((*yyd++ = *yys++) != '\0')
+ continue;
+
+ return yyd - 1;
+}
+# endif
+# endif
+
+#endif /* !YYERROR_VERBOSE */
+
+
+
+#if YYDEBUG
+/*--------------------------------.
+| Print this symbol on YYOUTPUT. |
+`--------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yysymprint (yyoutput, yytype, yyvaluep)
+ FILE *yyoutput;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+
+ if (yytype < YYNTOKENS)
+ YYFPRINTF (yyoutput, "token %s (", yytname[yytype]);
+ else
+ YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]);
+
+
+# ifdef YYPRINT
+ if (yytype < YYNTOKENS)
+ YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+ switch (yytype)
+ {
+ default:
+ break;
+ }
+ YYFPRINTF (yyoutput, ")");
+}
+
+#endif /* ! YYDEBUG */
+/*-----------------------------------------------.
+| Release the memory associated to this symbol. |
+`-----------------------------------------------*/
+
+#if defined (__STDC__) || defined (__cplusplus)
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+#else
+static void
+yydestruct (yymsg, yytype, yyvaluep)
+ const char *yymsg;
+ int yytype;
+ YYSTYPE *yyvaluep;
+#endif
+{
+ /* Pacify ``unused variable'' warnings. */
+ (void) yyvaluep;
+
+ if (!yymsg)
+ yymsg = "Deleting";
+ YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+ switch (yytype)
+ {
+
+ default:
+ break;
+ }
+}
+
+
+/* Prevent warnings from -Wmissing-prototypes. */
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM);
+# else
+int yyparse ();
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void);
+#else
+int yyparse ();
+#endif
+#endif /* ! YYPARSE_PARAM */
+
+
+
+/* The look-ahead symbol. */
+int yychar;
+
+/* The semantic value of the look-ahead symbol. */
+YYSTYPE yylval;
+
+/* Number of syntax errors so far. */
+int yynerrs;
+
+
+
+/*----------.
+| yyparse. |
+`----------*/
+
+#ifdef YYPARSE_PARAM
+# if defined (__STDC__) || defined (__cplusplus)
+int yyparse (void *YYPARSE_PARAM)
+# else
+int yyparse (YYPARSE_PARAM)
+ void *YYPARSE_PARAM;
+# endif
+#else /* ! YYPARSE_PARAM */
+#if defined (__STDC__) || defined (__cplusplus)
+int
+yyparse (void)
+#else
+int
+yyparse ()
+
+#endif
+#endif
+{
+
+ register int yystate;
+ register int yyn;
+ int yyresult;
+ /* Number of tokens to shift before error messages enabled. */
+ int yyerrstatus;
+ /* Look-ahead token as an internal (translated) token number. */
+ int yytoken = 0;
+
+ /* Three stacks and their tools:
+ `yyss': related to states,
+ `yyvs': related to semantic values,
+ `yyls': related to locations.
+
+ Refer to the stacks thru separate pointers, to allow yyoverflow
+ to reallocate them elsewhere. */
+
+ /* The state stack. */
+ short int yyssa[YYINITDEPTH];
+ short int *yyss = yyssa;
+ register short int *yyssp;
+
+ /* The semantic value stack. */
+ YYSTYPE yyvsa[YYINITDEPTH];
+ YYSTYPE *yyvs = yyvsa;
+ register YYSTYPE *yyvsp;
+
+
+
+#define YYPOPSTACK (yyvsp--, yyssp--)
+
+ YYSIZE_T yystacksize = YYINITDEPTH;
+
+ /* The variables used to return semantic value and location from the
+ action routines. */
+ YYSTYPE yyval;
+
+
+ /* When reducing, the number of symbols on the RHS of the reduced
+ rule. */
+ int yylen;
+
+ YYDPRINTF ((stderr, "Starting parse\n"));
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss;
+ yyvsp = yyvs;
+
+
+ yyvsp[0] = yylval;
+
+ goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate. |
+`------------------------------------------------------------*/
+ yynewstate:
+ /* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks.
+ */
+ yyssp++;
+
+ yysetstate:
+ *yyssp = yystate;
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ {
+ /* Get the current used size of the three stacks, in elements. */
+ YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ {
+ /* Give user a chance to reallocate the stack. Use copies of
+ these so that the &'s don't force the real ones into
+ memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short int *yyss1 = yyss;
+
+
+ /* Each stack pointer address is followed by the size of the
+ data in use in that stack, in bytes. This used to be a
+ conditional around just the two extra args, but that might
+ be undefined if yyoverflow is a macro. */
+ yyoverflow ("parser stack overflow",
+ &yyss1, yysize * sizeof (*yyssp),
+ &yyvs1, yysize * sizeof (*yyvsp),
+
+ &yystacksize);
+
+ yyss = yyss1;
+ yyvs = yyvs1;
+ }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+ goto yyoverflowlab;
+# else
+ /* Extend the stack our own way. */
+ if (YYMAXDEPTH <= yystacksize)
+ goto yyoverflowlab;
+ yystacksize *= 2;
+ if (YYMAXDEPTH < yystacksize)
+ yystacksize = YYMAXDEPTH;
+
+ {
+ short int *yyss1 = yyss;
+ union yyalloc *yyptr =
+ (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+ if (! yyptr)
+ goto yyoverflowlab;
+ YYSTACK_RELOCATE (yyss);
+ YYSTACK_RELOCATE (yyvs);
+
+# undef YYSTACK_RELOCATE
+ if (yyss1 != yyssa)
+ YYSTACK_FREE (yyss1);
+ }
+# endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + yysize - 1;
+ yyvsp = yyvs + yysize - 1;
+
+
+ YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+ (unsigned long int) yystacksize));
+
+ if (yyss + yystacksize - 1 <= yyssp)
+ YYABORT;
+ }
+
+ YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+ goto yybackup;
+
+/*-----------.
+| yybackup. |
+`-----------*/
+yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a look-ahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to look-ahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYPACT_NINF)
+ goto yydefault;
+
+ /* Not known => get a look-ahead token if don't already have one. */
+
+ /* YYCHAR is either YYEMPTY or YYEOF or a valid look-ahead symbol. */
+ if (yychar == YYEMPTY)
+ {
+ YYDPRINTF ((stderr, "Reading a token: "));
+ yychar = YYLEX;
+ }
+
+ if (yychar <= YYEOF)
+ {
+ yychar = yytoken = YYEOF;
+ YYDPRINTF ((stderr, "Now at end of input.\n"));
+ }
+ else
+ {
+ yytoken = YYTRANSLATE (yychar);
+ YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+ }
+
+ /* If the proper action on seeing token YYTOKEN is to reduce or to
+ detect an error, take that action. */
+ yyn += yytoken;
+ if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+ goto yydefault;
+ yyn = yytable[yyn];
+ if (yyn <= 0)
+ {
+ if (yyn == 0 || yyn == YYTABLE_NINF)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the look-ahead token. */
+ YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+
+
+ /* Count tokens shifted since error; after three, turn off error
+ status. */
+ if (yyerrstatus)
+ yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state. |
+`-----------------------------------------------------------*/
+yydefault:
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+ goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction. |
+`-----------------------------*/
+yyreduce:
+ /* yyn is the number of a rule to reduce with. */
+ yylen = yyr2[yyn];
+
+ /* If YYLEN is nonzero, implement the default value of the action:
+ `$$ = $1'.
+
+ Otherwise, the following line sets YYVAL to garbage.
+ This behavior is undocumented and Bison
+ users should not rely upon it. Assigning to YYVAL
+ unconditionally makes the parser a bit smaller, and it avoids a
+ GCC warning that YYVAL may be used uninitialized. */
+ yyval = yyvsp[1-yylen];
+
+
+ YY_REDUCE_PRINT (yyn);
+ switch (yyn)
+ {
+ case 2:
+#line 56 "yacc.y"
+ { KTraderParse_setParseTree( 0L ); ;}
+ break;
+
+ case 3:
+#line 57 "yacc.y"
+ { KTraderParse_setParseTree( (yyvsp[0].ptr) ); ;}
+ break;
+
+ case 4:
+#line 60 "yacc.y"
+ { (yyval.ptr) = (yyvsp[0].ptr); ;}
+ break;
+
+ case 5:
+#line 63 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newOR( (yyvsp[-2].ptr), (yyvsp[0].ptr) ); ;}
+ break;
+
+ case 6:
+#line 64 "yacc.y"
+ { (yyval.ptr) = (yyvsp[0].ptr); ;}
+ break;
+
+ case 7:
+#line 67 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newAND( (yyvsp[-2].ptr), (yyvsp[0].ptr) ); ;}
+ break;
+
+ case 8:
+#line 68 "yacc.y"
+ { (yyval.ptr) = (yyvsp[0].ptr); ;}
+ break;
+
+ case 9:
+#line 71 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newCMP( (yyvsp[-2].ptr), (yyvsp[0].ptr), 1 ); ;}
+ break;
+
+ case 10:
+#line 72 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newCMP( (yyvsp[-2].ptr), (yyvsp[0].ptr), 2 ); ;}
+ break;
+
+ case 11:
+#line 73 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newCMP( (yyvsp[-2].ptr), (yyvsp[0].ptr), 3 ); ;}
+ break;
+
+ case 12:
+#line 74 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newCMP( (yyvsp[-2].ptr), (yyvsp[0].ptr), 4 ); ;}
+ break;
+
+ case 13:
+#line 75 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newCMP( (yyvsp[-2].ptr), (yyvsp[0].ptr), 5 ); ;}
+ break;
+
+ case 14:
+#line 76 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newCMP( (yyvsp[-2].ptr), (yyvsp[0].ptr), 6 ); ;}
+ break;
+
+ case 15:
+#line 77 "yacc.y"
+ { (yyval.ptr) = (yyvsp[0].ptr); ;}
+ break;
+
+ case 16:
+#line 80 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newIN( (yyvsp[-2].ptr), KTraderParse_newID( (yyvsp[0].name) ) ); ;}
+ break;
+
+ case 17:
+#line 81 "yacc.y"
+ { (yyval.ptr) = (yyvsp[0].ptr); ;}
+ break;
+
+ case 18:
+#line 84 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newMATCH( (yyvsp[-2].ptr), (yyvsp[0].ptr) ); ;}
+ break;
+
+ case 19:
+#line 85 "yacc.y"
+ { (yyval.ptr) = (yyvsp[0].ptr); ;}
+ break;
+
+ case 20:
+#line 88 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newCALC( (yyvsp[-2].ptr), (yyvsp[0].ptr), 1 ); ;}
+ break;
+
+ case 21:
+#line 89 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newCALC( (yyvsp[-2].ptr), (yyvsp[0].ptr), 2 ); ;}
+ break;
+
+ case 22:
+#line 90 "yacc.y"
+ { (yyval.ptr) = (yyvsp[0].ptr); ;}
+ break;
+
+ case 23:
+#line 93 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newCALC( (yyvsp[-2].ptr), (yyvsp[0].ptr), 3 ); ;}
+ break;
+
+ case 24:
+#line 94 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newCALC( (yyvsp[-2].ptr), (yyvsp[0].ptr), 4 ); ;}
+ break;
+
+ case 25:
+#line 95 "yacc.y"
+ { (yyval.ptr) = (yyvsp[0].ptr); ;}
+ break;
+
+ case 26:
+#line 98 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newNOT( (yyvsp[0].ptr) ); ;}
+ break;
+
+ case 27:
+#line 99 "yacc.y"
+ { (yyval.ptr) = (yyvsp[0].ptr); ;}
+ break;
+
+ case 28:
+#line 102 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newBRACKETS( (yyvsp[-1].ptr) ); ;}
+ break;
+
+ case 29:
+#line 103 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newEXIST( (yyvsp[0].name) ); ;}
+ break;
+
+ case 30:
+#line 104 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newID( (yyvsp[0].name) ); ;}
+ break;
+
+ case 31:
+#line 105 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newNUM( (yyvsp[0].vali) ); ;}
+ break;
+
+ case 32:
+#line 106 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newFLOAT( (yyvsp[0].vald) ); ;}
+ break;
+
+ case 33:
+#line 107 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newSTRING( (yyvsp[0].name) ); ;}
+ break;
+
+ case 34:
+#line 108 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newBOOL( (yyvsp[0].valb) ); ;}
+ break;
+
+ case 35:
+#line 109 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newMAX2( (yyvsp[0].name) ); ;}
+ break;
+
+ case 36:
+#line 110 "yacc.y"
+ { (yyval.ptr) = KTraderParse_newMIN2( (yyvsp[0].name) ); ;}
+ break;
+
+
+ }
+
+/* Line 1037 of yacc.c. */
+#line 1281 "yacc.tab.c"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+
+
+ YY_STACK_PRINT (yyss, yyssp);
+
+ *++yyvsp = yyval;
+
+
+ /* Now `shift' the result of the reduction. Determine what state
+ that goes to, based on the state we popped back to and the rule
+ number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+ if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTOKENS];
+
+ goto yynewstate;
+
+
+/*------------------------------------.
+| yyerrlab -- here on detecting error |
+`------------------------------------*/
+yyerrlab:
+ /* If not already recovering from an error, report this error. */
+ if (!yyerrstatus)
+ {
+ ++yynerrs;
+#if YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (YYPACT_NINF < yyn && yyn < YYLAST)
+ {
+ YYSIZE_T yysize = 0;
+ int yytype = YYTRANSLATE (yychar);
+ const char* yyprefix;
+ char *yymsg;
+ int yyx;
+
+ /* Start YYX at -YYN if negative to avoid negative indexes in
+ YYCHECK. */
+ int yyxbegin = yyn < 0 ? -yyn : 0;
+
+ /* Stay within bounds of both yycheck and yytname. */
+ int yychecklim = YYLAST - yyn;
+ int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+ int yycount = 0;
+
+ yyprefix = ", expecting ";
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]);
+ yycount += 1;
+ if (yycount == 5)
+ {
+ yysize = 0;
+ break;
+ }
+ }
+ yysize += (sizeof ("syntax error, unexpected ")
+ + yystrlen (yytname[yytype]));
+ yymsg = (char *) YYSTACK_ALLOC (yysize);
+ if (yymsg != 0)
+ {
+ char *yyp = yystpcpy (yymsg, "syntax error, unexpected ");
+ yyp = yystpcpy (yyp, yytname[yytype]);
+
+ if (yycount < 5)
+ {
+ yyprefix = ", expecting ";
+ for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+ if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR)
+ {
+ yyp = yystpcpy (yyp, yyprefix);
+ yyp = yystpcpy (yyp, yytname[yyx]);
+ yyprefix = " or ";
+ }
+ }
+ yyerror (yymsg);
+ YYSTACK_FREE (yymsg);
+ }
+ else
+ yyerror ("syntax error; also virtual memory exhausted");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror ("syntax error");
+ }
+
+
+
+ if (yyerrstatus == 3)
+ {
+ /* If just tried and failed to reuse look-ahead token after an
+ error, discard it. */
+
+ if (yychar <= YYEOF)
+ {
+ /* If at end of input, pop the error token,
+ then the rest of the stack, then return failure. */
+ if (yychar == YYEOF)
+ for (;;)
+ {
+
+ YYPOPSTACK;
+ if (yyssp == yyss)
+ YYABORT;
+ yydestruct ("Error: popping",
+ yystos[*yyssp], yyvsp);
+ }
+ }
+ else
+ {
+ yydestruct ("Error: discarding", yytoken, &yylval);
+ yychar = YYEMPTY;
+ }
+ }
+
+ /* Else will try to reuse look-ahead token after shifting the error
+ token. */
+ goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR. |
+`---------------------------------------------------*/
+yyerrorlab:
+
+#ifdef __GNUC__
+ /* Pacify GCC when the user code never invokes YYERROR and the label
+ yyerrorlab therefore never appears in user code. */
+ if (0)
+ goto yyerrorlab;
+#endif
+
+yyvsp -= yylen;
+ yyssp -= yylen;
+ yystate = *yyssp;
+ goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR. |
+`-------------------------------------------------------------*/
+yyerrlab1:
+ yyerrstatus = 3; /* Each real token shifted decrements this. */
+
+ for (;;)
+ {
+ yyn = yypact[yystate];
+ if (yyn != YYPACT_NINF)
+ {
+ yyn += YYTERROR;
+ if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+ {
+ yyn = yytable[yyn];
+ if (0 < yyn)
+ break;
+ }
+ }
+
+ /* Pop the current state because it cannot handle the error token. */
+ if (yyssp == yyss)
+ YYABORT;
+
+
+ yydestruct ("Error: popping", yystos[yystate], yyvsp);
+ YYPOPSTACK;
+ yystate = *yyssp;
+ YY_STACK_PRINT (yyss, yyssp);
+ }
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ *++yyvsp = yylval;
+
+
+ /* Shift the error token. */
+ YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+ yystate = yyn;
+ goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here. |
+`-------------------------------------*/
+yyacceptlab:
+ yyresult = 0;
+ goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here. |
+`-----------------------------------*/
+yyabortlab:
+ yydestruct ("Error: discarding lookahead",
+ yytoken, &yylval);
+ yychar = YYEMPTY;
+ yyresult = 1;
+ goto yyreturn;
+
+#ifndef yyoverflow
+/*----------------------------------------------.
+| yyoverflowlab -- parser overflow comes here. |
+`----------------------------------------------*/
+yyoverflowlab:
+ yyerror ("parser stack overflow");
+ yyresult = 2;
+ /* Fall through. */
+#endif
+
+yyreturn:
+#ifndef yyoverflow
+ if (yyss != yyssa)
+ YYSTACK_FREE (yyss);
+#endif
+ return yyresult;
+}
+
+
+#line 115 "yacc.y"
+
+
+void yyerror ( const char *s ) /* Called by yyparse on error */
+{
+ KTraderParse_error( s );
+}
+
+void KTraderParse_mainParse( const char *_code )
+{
+ KTraderParse_initFlex( _code );
+ yyparse();
+}
+
diff --git a/tdeio/tdeio/yacc.h b/tdeio/tdeio/yacc.h
new file mode 100644
index 000000000..ca84fb025
--- /dev/null
+++ b/tdeio/tdeio/yacc.h
@@ -0,0 +1,93 @@
+/* A Bison parser, made by GNU Bison 1.875. */
+
+/* Skeleton parser for Yacc-like parsing with Bison,
+ Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002 Free Software Foundation, Inc.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+/* Tokens. */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+ /* Put the tokens into the symbol table, so that GDB and other debuggers
+ know about them. */
+ enum yytokentype {
+ NOT = 258,
+ EQ = 259,
+ NEQ = 260,
+ LEQ = 261,
+ GEQ = 262,
+ LE = 263,
+ GR = 264,
+ OR = 265,
+ AND = 266,
+ TOKEN_IN = 267,
+ EXIST = 268,
+ MAX = 269,
+ MIN = 270,
+ VAL_BOOL = 271,
+ VAL_STRING = 272,
+ VAL_ID = 273,
+ VAL_NUM = 274,
+ VAL_FLOAT = 275
+ };
+#endif
+#define NOT 258
+#define EQ 259
+#define NEQ 260
+#define LEQ 261
+#define GEQ 262
+#define LE 263
+#define GR 264
+#define OR 265
+#define AND 266
+#define TOKEN_IN 267
+#define EXIST 268
+#define MAX 269
+#define MIN 270
+#define VAL_BOOL 271
+#define VAL_STRING 272
+#define VAL_ID 273
+#define VAL_NUM 274
+#define VAL_FLOAT 275
+
+
+
+
+#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED)
+#line 13 "yacc.y"
+typedef union YYSTYPE {
+ char valb;
+ int vali;
+ double vald;
+ char *name;
+ void *ptr;
+} YYSTYPE;
+/* Line 1240 of yacc.c. */
+#line 84 "yacc.tab.h"
+# define yystype YYSTYPE /* obsolescent; will be withdrawn */
+# define YYSTYPE_IS_DECLARED 1
+# define YYSTYPE_IS_TRIVIAL 1
+#endif
+
+extern YYSTYPE kiotraderlval;
+
+
+
diff --git a/tdeio/tdeio/yacc.y b/tdeio/tdeio/yacc.y
new file mode 100644
index 000000000..1b86f4737
--- /dev/null
+++ b/tdeio/tdeio/yacc.y
@@ -0,0 +1,126 @@
+%{
+#include <stdlib.h>
+#include <stdio.h>
+#include "ktraderparse.h"
+
+void yyerror(const char *s);
+int yylex();
+void KTraderParse_initFlex( const char *s );
+
+%}
+
+%union
+{
+ char valb;
+ int vali;
+ double vald;
+ char *name;
+ void *ptr;
+}
+
+%token NOT
+%token EQ
+%token NEQ
+%token LEQ
+%token GEQ
+%token LE
+%token GR
+%token OR
+%token AND
+%token TOKEN_IN
+%token EXIST
+%token MAX
+%token MIN
+
+%token <valb> VAL_BOOL
+%token <name> VAL_STRING
+%token <name> VAL_ID
+%token <vali> VAL_NUM
+%token <vald> VAL_FLOAT
+
+%type <ptr> bool
+%type <ptr> bool_or
+%type <ptr> bool_and
+%type <ptr> bool_compare
+%type <ptr> expr_in
+%type <ptr> expr_twiddle
+%type <ptr> expr
+%type <ptr> term
+%type <ptr> factor_non
+%type <ptr> factor
+
+/* Grammar follows */
+
+%%
+
+constraint: /* empty */ { KTraderParse_setParseTree( 0L ); }
+ | bool { KTraderParse_setParseTree( $<ptr>1 ); }
+;
+
+bool: bool_or { $$ = $<ptr>1; }
+;
+
+bool_or: bool_and OR bool_or { $$ = KTraderParse_newOR( $<ptr>1, $<ptr>3 ); }
+ | bool_and { $$ = $<ptr>1; }
+;
+
+bool_and: bool_compare AND bool_and { $$ = KTraderParse_newAND( $<ptr>1, $<ptr>3 ); }
+ | bool_compare { $$ = $<ptr>1; }
+;
+
+bool_compare: expr_in EQ expr_in { $$ = KTraderParse_newCMP( $<ptr>1, $<ptr>3, 1 ); }
+ | expr_in NEQ expr_in { $$ = KTraderParse_newCMP( $<ptr>1, $<ptr>3, 2 ); }
+ | expr_in GEQ expr_in { $$ = KTraderParse_newCMP( $<ptr>1, $<ptr>3, 3 ); }
+ | expr_in LEQ expr_in { $$ = KTraderParse_newCMP( $<ptr>1, $<ptr>3, 4 ); }
+ | expr_in LE expr_in { $$ = KTraderParse_newCMP( $<ptr>1, $<ptr>3, 5 ); }
+ | expr_in GR expr_in { $$ = KTraderParse_newCMP( $<ptr>1, $<ptr>3, 6 ); }
+ | expr_in { $$ = $<ptr>1; }
+;
+
+expr_in: expr_twiddle TOKEN_IN VAL_ID { $$ = KTraderParse_newIN( $<ptr>1, KTraderParse_newID( $<name>3 ) ); }
+ | expr_twiddle { $$ = $<ptr>1; }
+;
+
+expr_twiddle: expr '~' expr { $$ = KTraderParse_newMATCH( $<ptr>1, $<ptr>3 ); }
+ | expr { $$ = $<ptr>1; }
+;
+
+expr: expr '+' term { $$ = KTraderParse_newCALC( $<ptr>1, $<ptr>3, 1 ); }
+ | expr '-' term { $$ = KTraderParse_newCALC( $<ptr>1, $<ptr>3, 2 ); }
+ | term { $$ = $<ptr>1; }
+;
+
+term: term '*' factor_non { $$ = KTraderParse_newCALC( $<ptr>1, $<ptr>3, 3 ); }
+ | term '/' factor_non { $$ = KTraderParse_newCALC( $<ptr>1, $<ptr>3, 4 ); }
+ | factor_non { $$ = $<ptr>1; }
+;
+
+factor_non: NOT factor { $$ = KTraderParse_newNOT( $<ptr>2 ); }
+ | factor { $$ = $<ptr>1; }
+;
+
+factor: '(' bool_or ')' { $$ = KTraderParse_newBRACKETS( $<ptr>2 ); }
+ | EXIST VAL_ID { $$ = KTraderParse_newEXIST( $<name>2 ); }
+ | VAL_ID { $$ = KTraderParse_newID( $<name>1 ); }
+ | VAL_NUM { $$ = KTraderParse_newNUM( $<vali>1 ); }
+ | VAL_FLOAT { $$ = KTraderParse_newFLOAT( $<vald>1 ); }
+ | VAL_STRING { $$ = KTraderParse_newSTRING( $<name>1 ); }
+ | VAL_BOOL { $$ = KTraderParse_newBOOL( $<valb>1 ); }
+ | MAX VAL_ID { $$ = KTraderParse_newMAX2( $<name>2 ); }
+ | MIN VAL_ID { $$ = KTraderParse_newMIN2( $<name>2 ); }
+;
+
+/* End of grammar */
+
+%%
+
+void yyerror ( const char *s ) /* Called by yyparse on error */
+{
+ KTraderParse_error( s );
+}
+
+void KTraderParse_mainParse( const char *_code )
+{
+ KTraderParse_initFlex( _code );
+ yyparse();
+}