diff options
Diffstat (limited to 'tdeio/tdeio')
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, ¤tTag ); + 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, ¤tTag ); + 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, ¤tTag ); + 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, ¤tTag ); + 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, ¤tTag ); + 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, ¤tTag ); + 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, ¤tTag ); + 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,¶ms); + 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,¶ms); + 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,¶ms); + 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 = ⌖ + params.user = &user; + params.group = &group; + params.perm = perm; + params.atime = atime; + params.mtime = mtime; + params.ctime = ctime; + virtual_hook(VIRTUAL_WRITE_SYMLINK,¶ms); + 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, ¶ms ); + 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><</tt> + * - <tt><=</tt> + * - <tt>></tt> + * - <tt>>=</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, ¶ms); + 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, ¶ms); + 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, ¶ms); + 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> </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(); +} |